@@ -31,7 +31,6 @@ define(function (require, exports, module) {
3131 Commands = require ( "command/Commands" ) ,
3232 CommandManager = require ( "command/CommandManager" ) ,
3333 Menus = require ( "command/Menus" ) ,
34- MainViewManager = require ( "view/MainViewManager" ) ,
3534 FileSystem = require ( "filesystem/FileSystem" ) ,
3635 AppInit = require ( "utils/AppInit" ) ,
3736 KeyEvent = require ( "utils/KeyEvent" ) ,
@@ -47,16 +46,11 @@ define(function (require, exports, module) {
4746 ExtensionInterface . registerExtensionInterface ( RECENT_PROJECTS_INTERFACE , exports ) ;
4847 const RECENT_PROJECT_STATE = "recentProjects" ;
4948
50- /** @const {string} Recent Projects commands ID */
51- let TOGGLE_DROPDOWN = "recentProjects.toggle" ;
52-
5349 /** @const {number} Maximum number of displayed recent projects */
5450 var MAX_PROJECTS = 20 ;
5551
5652 /** @type {$.Element } jQuery elements used for the dropdown menu */
57- var $dropdownItem ,
58- $dropdown ,
59- $links ;
53+ let $dropdown ;
6054
6155 /**
6256 * Get the stored list of recent projects, fixing up paths as appropriate.
@@ -109,156 +103,28 @@ define(function (require, exports, module) {
109103 PreferencesManager . setViewState ( RECENT_PROJECT_STATE , newProjects ) ;
110104 }
111105
112- /**
113- * Selects the next or previous item in the list
114- * @param {number } direction +1 for next, -1 for prev
115- */
116- function selectNextItem ( direction ) {
117- let $links = $dropdown . find ( "a:visible" ) ,
118- index = $dropdownItem ? $links . index ( $dropdownItem ) : ( direction > 0 ? - 1 : 0 ) ,
119- $newItem = $links . eq ( ( index + direction ) % $links . length ) ;
120-
121- if ( searchStr && $links . length === 1 ) {
122- // no search result, only the top search field visible
123- return ;
124- }
125- if ( $newItem . parent ( ) . hasClass ( "sticky-li-top" ) ) {
126- if ( index === - 1 ) {
127- index = 0 ;
128- }
129- $newItem = $links . eq ( ( index + direction ) % $links . length ) ;
130- }
131- if ( $dropdownItem ) {
132- $dropdownItem . removeClass ( "selected" ) ;
133- }
134- $newItem . addClass ( "selected" ) ;
135-
136- $dropdownItem = $newItem ;
137- }
138-
139- let searchStr = "" ;
140- /**
141- * hides all elements in popup that doesn't match the given search string, also shows the search bar in popup
142- * @param searchString
143- */
144- function filterDropdown ( searchString ) {
145- searchStr = searchString ;
146- const $stickyLi = $dropdown . find ( 'li.sticky-li-top' ) ;
147- if ( searchString ) {
148- $stickyLi . removeClass ( "forced-hidden" ) ;
149- } else {
150- $stickyLi . addClass ( "forced-hidden" ) ;
151- }
152-
153- $dropdown . find ( 'li' ) . each ( function ( index , li ) {
154- if ( index === 0 ) {
155- // this is the top search box itself
156- return ;
157- }
158- const $li = $ ( li ) ;
159- if ( ! $li . text ( ) . toLowerCase ( ) . includes ( searchString . toLowerCase ( ) ) ) {
160- $li . addClass ( "forced-hidden" ) ;
161- } else {
162- $li . removeClass ( "forced-hidden" ) ;
163- }
164- } ) ;
165-
166- if ( searchString ) {
167- $stickyLi . removeClass ( 'forced-hidden' ) ;
168- $stickyLi . find ( '.searchTextSpan' ) . text ( searchString ) ;
169- } else {
170- $stickyLi . addClass ( 'forced-hidden' ) ;
171- }
172- }
173-
174- /**
175- * Deletes the selected item and
176- * move the focus to next item in list.
177- *
178- * @return {boolean } TRUE if project is removed
179- */
180- function removeSelectedItem ( e ) {
181- var recentProjects = getRecentProjects ( ) ,
182- $cacheItem = $dropdownItem ,
183- index = recentProjects . indexOf ( $cacheItem . data ( "path" ) ) ;
184-
185- // When focus is not on project item
186- if ( index === - 1 ) {
187- return false ;
188- }
189-
190- // remove project
191- recentProjects . splice ( index , 1 ) ;
192- PreferencesManager . setViewState ( RECENT_PROJECT_STATE , recentProjects ) ;
193-
194- if ( recentProjects . length === 1 ) {
195- $dropdown . find ( ".divider" ) . remove ( ) ;
196- }
197- selectNextItem ( + 1 ) ;
198- $cacheItem . closest ( "li" ) . remove ( ) ;
199- return true ;
200- }
201-
202106 /**
203107 * Handles the Key Down events
204108 * @param {KeyboardEvent } event
109+ * @param $popUp
205110 * @return {boolean } True if the key was handled
206111 */
207- function keydownHook ( event ) {
208- var keyHandled = false ;
209-
210- switch ( event . keyCode ) {
211- case KeyEvent . DOM_VK_UP :
212- selectNextItem ( - 1 ) ;
213- keyHandled = true ;
214- break ;
215- case KeyEvent . DOM_VK_DOWN :
216- selectNextItem ( + 1 ) ;
217- keyHandled = true ;
218- break ;
219- case KeyEvent . DOM_VK_ENTER :
220- case KeyEvent . DOM_VK_RETURN :
221- if ( $dropdownItem ) {
222- $dropdownItem . trigger ( "click" ) ;
223- }
224- keyHandled = true ;
225- break ;
226- case KeyEvent . DOM_VK_DELETE :
227- if ( $dropdownItem ) {
228- removeSelectedItem ( event ) ;
229- }
230- keyHandled = true ;
231- break ;
232- }
233-
234- if ( keyHandled ) {
235- event . stopImmediatePropagation ( ) ;
236- event . preventDefault ( ) ;
237- return keyHandled ;
238- } else if ( ( event . ctrlKey || event . metaKey ) && event . key === 'v' ) {
239- Phoenix . app . clipboardReadText ( ) . then ( text => {
240- searchStr += text ;
241- filterDropdown ( searchStr ) ;
242- } ) ;
243- keyHandled = true ;
244- } else if ( event . key . length === 1 ) {
245- searchStr += event . key ;
246- keyHandled = true ;
247- } else if ( event . key === 'Backspace' ) {
248- // Remove the last character when Backspace is pressed
249- searchStr = searchStr . slice ( 0 , - 1 ) ;
250- keyHandled = true ;
251- } else {
252- // bubble up, not for us to handle
253- return false ;
254- }
255- filterDropdown ( searchStr ) ;
112+ function _handlePopupKeyEvents ( event , $popUp ) {
113+ if ( event . keyCode === KeyEvent . DOM_VK_DELETE ) {
114+ event . stopPropagation ( ) ;
115+ const $selectedItem = $popUp . find ( ".selected" ) ;
116+ if ( $selectedItem . length && $selectedItem . data ( "path" ) ) {
117+ // Remove the project from the preferences.
118+ removeFromRecentProject ( $selectedItem . data ( "path" ) ) ;
119+ PopUpManager . selectNextItem ( + 1 , $popUp ) ;
120+ $selectedItem . closest ( "li" ) . remove ( ) ;
256121
257- if ( keyHandled ) {
258- event . stopImmediatePropagation ( ) ;
259- event . preventDefault ( ) ;
122+ if ( getRecentProjects ( ) . length === 1 ) {
123+ $dropdown . find ( ".divider" ) . remove ( ) ;
124+ }
125+ }
126+ return true ;
260127 }
261- return keyHandled ;
262128 }
263129
264130
@@ -272,7 +138,6 @@ define(function (require, exports, module) {
272138 if ( $dropdown ) {
273139 PopUpManager . removePopUp ( $dropdown ) ;
274140 }
275- searchStr = "" ;
276141 }
277142
278143 /**
@@ -284,9 +149,6 @@ define(function (require, exports, module) {
284149 $ ( "#project-files-container" ) . off ( "scroll" , closeDropdown ) ;
285150 $ ( "#titlebar .nav" ) . off ( "click" , closeDropdown ) ;
286151 $dropdown = null ;
287-
288- $ ( window ) . off ( "keydown" , keydownHook ) ;
289- searchStr = "" ;
290152 }
291153
292154 function openProjectWithPath ( fullPath ) {
@@ -345,19 +207,6 @@ define(function (require, exports, module) {
345207 CommandManager . execute ( Commands . FILE_DOWNLOAD_PROJECT ) ;
346208 }
347209
348- } )
349- . on ( "mouseenter" , "a" , function ( ) {
350- if ( $dropdownItem ) {
351- $dropdownItem . removeClass ( "selected" ) ;
352- }
353- $dropdownItem = $ ( this ) . addClass ( "selected" ) ;
354- } )
355- . on ( "mouseleave" , "a" , function ( ) {
356- var $link = $ ( this ) . removeClass ( "selected" ) ;
357-
358- if ( $link . get ( 0 ) === $dropdownItem . get ( 0 ) ) {
359- $dropdownItem = null ;
360- }
361210 } ) ;
362211 }
363212
@@ -428,6 +277,10 @@ define(function (require, exports, module) {
428277 . appendTo ( $ ( "body" ) ) ;
429278
430279 PopUpManager . addPopUp ( $dropdown , cleanupDropdown , true , { closeCurrentPopups : true } ) ;
280+ PopUpManager . handleSelectionEvents ( $dropdown , {
281+ enableSearchFilter : true ,
282+ keyboardEventHandler : _handlePopupKeyEvents
283+ } ) ;
431284
432285 // TODO: should use capture, otherwise clicking on the menus doesn't close it. More fallout
433286 // from the fact that we can't use the Boostrap (1.4) dropdowns.
@@ -448,38 +301,8 @@ define(function (require, exports, module) {
448301 $ ( "#titlebar .nav" ) . on ( "click" , closeDropdown ) ;
449302
450303 _handleListEvents ( ) ;
451- $ ( window ) . on ( "keydown" , keydownHook ) ;
452- }
453-
454-
455- /**
456- * Show or hide the recent projects dropdown from the toogle command.
457- */
458- function handleKeyEvent ( ) {
459- if ( ! $dropdown ) {
460- if ( ! SidebarView . isVisible ( ) ) {
461- SidebarView . show ( ) ;
462- }
463-
464- $ ( "#project-dropdown-toggle" ) . trigger ( "click" ) ;
465-
466- $dropdown . focus ( ) ;
467- $links = $dropdown . find ( "a" ) ;
468- // By default, select the most recent project (which is at the top of the list underneath Open Folder),
469- // but if there are none, select Open Folder instead.
470- $dropdownItem = $links . eq ( $links . length > 1 ? 1 : 0 ) ;
471- $dropdownItem . addClass ( "selected" ) ;
472-
473- // If focusing the dropdown caused a modal bar to close, we need to refocus the dropdown
474- window . setTimeout ( function ( ) {
475- $dropdown . focus ( ) ;
476- } , 0 ) ;
477- }
478304 }
479305
480- // Register command handlers
481- CommandManager . register ( Strings . CMD_TOGGLE_RECENT_PROJECTS , TOGGLE_DROPDOWN , handleKeyEvent ) ;
482-
483306 // Initialize extension
484307 AppInit . appReady ( function ( ) {
485308 PreferencesManager . stateManager . definePreference ( RECENT_PROJECT_STATE , 'array' , [ ] )
0 commit comments