@@ -34,7 +34,8 @@ define(function (require, exports, module) {
3434 MainViewManager = require ( "view/MainViewManager" ) ,
3535 KeyEvent = require ( "utils/KeyEvent" ) ;
3636
37- let _popUps = [ ] , addPopupInProgress = false ;
37+ let _popUps = [ ] , addPopupInProgress = false ,
38+ currentEventPopups = [ ] ;
3839
3940 /**
4041 * Add Esc key handling for a popup DOM element.
@@ -66,6 +67,26 @@ define(function (require, exports, module) {
6667 addPopupInProgress = false ;
6768 }
6869
70+ function handleSelectionEvents ( $popUp , options = { } ) {
71+ const { keyboardEventHandler, enableSearchFilter} = options ;
72+ currentEventPopups . push ( {
73+ $popUp,
74+ keyboardEventHandler,
75+ enableSearchFilter
76+ } ) ;
77+ if ( currentEventPopups . length > 1 ) {
78+ console . error ( `${ currentEventPopups . length } popups are visible while handling keyboard events!` ,
79+ "Possible popup event handler leak. Only 1 popup event handler is expected at this time." ) ;
80+ }
81+ if ( enableSearchFilter && ! $popUp . find ( ".sticky-li-top" ) . length ) {
82+ $popUp . prepend (
83+ `<li class="sticky-li-top forced-hidden">
84+ <a class='stylesheet-link'><i class="fa fa-search" aria-hidden="true"></i>
85+ <span class="searchTextSpan"></span></a>
86+ </li>` ) ;
87+ }
88+ }
89+
6990 /**
7091 * Remove Esc key handling for a pop-up. Removes the pop-up from the DOM
7192 * if the pop-up is currently visible and was not originally attached.
@@ -89,6 +110,11 @@ define(function (require, exports, module) {
89110 MainViewManager . focusActivePane ( ) ;
90111 }
91112
113+ let handlerIndex = currentEventPopups . findIndex ( item => item . $popUp . is ( $popUp ) ) ;
114+ if ( handlerIndex >= 0 ) {
115+ currentEventPopups . splice ( handlerIndex , 1 ) ;
116+ searchStr = "" ;
117+ }
92118 // check index after removeHandler is done processing to protect
93119 // against recursive calls
94120 let index = _popUps . indexOf ( $popUp [ 0 ] ) ;
@@ -126,14 +152,122 @@ define(function (require, exports, module) {
126152 }
127153
128154 removePopUp ( $popUp ) ;
155+ return true ;
129156 }
130157
131158 break ;
132159 }
133160 }
134161 }
135162
163+ let searchStr = "" ;
164+ /**
165+ * hides all elements in popup that doesn't match the given search string, also shows the search bar in popup
166+ * @param $popup
167+ * @param searchString
168+ */
169+ function _filterDropdown ( $popup , searchString ) {
170+ searchStr = searchString ;
171+ const $stickyLi = $popup . find ( 'li.sticky-li-top' ) ;
172+ if ( ! $stickyLi . length ) {
173+ console . error ( "Search filter element not found! Please call" +
174+ " PopUpManager.handleSelectionEvents with enableSearchFilter option." ) ;
175+ return ;
176+ }
177+ if ( searchString ) {
178+ $stickyLi . removeClass ( "forced-hidden" ) ;
179+ } else {
180+ $stickyLi . addClass ( "forced-hidden" ) ;
181+ }
182+
183+ $popup . find ( 'li' ) . each ( function ( index , li ) {
184+ if ( index === 0 ) {
185+ // this is the top search box itself
186+ return ;
187+ }
188+ const $li = $ ( li ) ;
189+ if ( ! $li . text ( ) . toLowerCase ( ) . includes ( searchString . toLowerCase ( ) ) ) {
190+ $li . addClass ( "forced-hidden" ) ;
191+ } else {
192+ $li . removeClass ( "forced-hidden" ) ;
193+ }
194+ } ) ;
195+
196+ if ( searchString ) {
197+ $stickyLi . removeClass ( 'forced-hidden' ) ;
198+ $stickyLi . find ( '.searchTextSpan' ) . text ( searchString ) ;
199+ } else {
200+ $stickyLi . addClass ( 'forced-hidden' ) ;
201+ }
202+ }
203+
204+ function _processSelectionEvent ( event ) {
205+ const { $popUp, keyboardEventHandler} = currentEventPopups [ currentEventPopups . length - 1 ] ;
206+ if ( ! $popUp || ! $popUp . is ( ":visible" ) ) {
207+ return false ;
208+ }
209+ if ( keyboardEventHandler ) {
210+ const processed = keyboardEventHandler ( event ) ;
211+ if ( processed ) {
212+ return true ;
213+ }
214+ }
215+ var keyHandled = false ;
216+
217+ switch ( event . keyCode ) {
218+ case KeyEvent . DOM_VK_UP :
219+ //selectNextItem(-1);
220+ keyHandled = true ;
221+ break ;
222+ case KeyEvent . DOM_VK_DOWN :
223+ //selectNextItem(+1);
224+ keyHandled = true ;
225+ break ;
226+ case KeyEvent . DOM_VK_ENTER :
227+ case KeyEvent . DOM_VK_RETURN :
228+ // if ($dropdownItem) {
229+ // $dropdownItem.trigger("click");
230+ // }
231+ keyHandled = true ;
232+ break ;
233+ }
234+
235+ if ( keyHandled ) {
236+ event . stopImmediatePropagation ( ) ;
237+ event . preventDefault ( ) ;
238+ return keyHandled ;
239+ } else if ( ( event . ctrlKey || event . metaKey ) && event . key === 'v' ) {
240+ Phoenix . app . clipboardReadText ( ) . then ( text => {
241+ searchStr += text ;
242+ _filterDropdown ( $popUp , searchStr ) ;
243+ } ) ;
244+ keyHandled = true ;
245+ } else if ( event . key . length === 1 ) {
246+ searchStr += event . key ;
247+ keyHandled = true ;
248+ } else if ( event . key === 'Backspace' ) {
249+ // Remove the last character when Backspace is pressed
250+ searchStr = searchStr . slice ( 0 , - 1 ) ;
251+ keyHandled = true ;
252+ } else {
253+ // bubble up, not for us to handle
254+ return false ;
255+ }
256+ _filterDropdown ( $popUp , searchStr ) ;
257+
258+ if ( keyHandled ) {
259+ event . stopImmediatePropagation ( ) ;
260+ event . preventDefault ( ) ;
261+ }
262+ return keyHandled ;
263+ }
264+
136265 function _keydownCaptureListener ( keyEvent ) {
266+ if ( currentEventPopups . length ) {
267+ if ( _processSelectionEvent ( keyEvent ) ) {
268+ return true ;
269+ }
270+ }
137271 // Escape key or Alt key (Windows-only)
138272 if ( keyEvent . keyCode !== KeyEvent . DOM_VK_ESCAPE &&
139273 ! ( keyEvent . keyCode === KeyEvent . DOM_VK_ALT && brackets . platform === "win" ) ) {
@@ -145,7 +279,7 @@ define(function (require, exports, module) {
145279 return ;
146280 }
147281
148- removeCurrentPopUp ( keyEvent ) ;
282+ return removeCurrentPopUp ( keyEvent ) ;
149283 }
150284
151285 /**
@@ -197,6 +331,7 @@ define(function (require, exports, module) {
197331 EventDispatcher . makeEventDispatcher ( exports ) ;
198332
199333 exports . addPopUp = addPopUp ;
334+ exports . handleSelectionEvents = handleSelectionEvents ;
200335 exports . removePopUp = removePopUp ;
201336 exports . closeAllPopups = closeAllPopups ;
202337 exports . listenToContextMenu = listenToContextMenu ;
0 commit comments