Imagine that you have a PopupMenu's list that requires a lot of scrolling through the selections. Is there a way to create a control that will only display a subsection of the complete list?
Asked
Active
Viewed 930 times
24
Kuba
- 136,707
- 13
- 279
- 740
Mike Honeychurch
- 37,541
- 3
- 85
- 158
-
How closely related is that: Autocomplete InputField? – Kuba Dec 20 '15 at 04:34
-
1@Kuba closely related. In this present case the usage requirement was minimise the choices in a large popup but the autocomplete input field looks like an alternative solution to the same, or similar, problem. The reason why the solution you have linked is not useful to me is that is inserts a column of menu choices into your interface whereas a popup is overlayed on top. Therefore the popup does not disrupt your interface design layout. It would be nice if the popup could automatically open when you type in the input field – Mike Honeychurch Dec 20 '15 at 04:57
-
@kuba please see my edit – Mike Honeychurch Dec 20 '15 at 05:05
-
I've seen, thanks for clarification. :) – Kuba Dec 20 '15 at 05:19
2 Answers
24
I have finished a project where widgets like this have been useful so I thought I would share. By combining InputField and PopupMenu a searchable popup selection table can be created.
searchPopup[Dynamic[selection_], list_List] :=
DynamicModule[{x = ""},
Grid[{{
InputField[Dynamic[x], String,
Appearance -> "Frameless",
ContinuousAction -> True,
FieldHint -> "Search",
FieldSize -> 10],
Spacer[5],
Dynamic[
PopupMenu[Dynamic[selection,(selection = #; x = #) &],
Cases[list,
z_String /; StringMatchQ[z, x ~~ ___, IgnoreCase -> True]],
Null,
Opener[True, Appearance -> Small]],
TrackedSymbols :> {x}]
}},
Alignment -> {{Right, Left}, Center},
Background -> White,
Frame -> True,
FrameStyle -> GrayLevel[0.8]]
]
list = {"Ajax", "admixes", "Acrux", "Alex", "affix", "admixtures",
"Alexei", "affixed", "admixing", "ambidextrously", "admixed",
"admix", "affixing", "ambidextrous", "Alexandrians", "Alexandria",
"Anaxagoras", "Alexandrian", "annexation", "Alexandra",
"admixture", "Alexis", "Alexanders", "annex", "affixes",
"ambidexterity", "Alexander"};
selection = "";
searchPopup[Dynamic[selection], list]
Edit
To address @kuba's comment by way of example in the GIF we have a toy interface. Normally you would not want your interface elements moving around all over the place. Therefore using popup menu is a good way to achieve this search menu objective.
Mike Honeychurch
- 37,541
- 3
- 85
- 158
-
This
Popupcode is indeed very useful. I have aManipulatewhich has twoPopupMenus with a very long list. But I am having trouble incorporating yoursearchPopup[Dynamic[selection], list]into theManipulate. Could you guide me in using it? – Tugrul Temel Nov 25 '21 at 23:10
16
Based on undocumented FrontEnd`AttachCell:
And Experimental`Autocompletebut this can be replaced with Mike's approach.
Need to work on style, automatic scrolling and some edge cases, but already works quite well. Feedback appreciated.
Column[{
searchPopup2[Dynamic[selection], list],
Graphics@Disk[]
}]
Code
searchPopup2[Dynamic[var_], list_] :=
DynamicModule[{thisBox, currentList = {}, autocompleteF,
attachedCell, dynamicFunction, whatToDo, lastLength, cPos = 1,
itemWrapper, attachTo, dropMenu, menu},
EventHandler[
InputField[Dynamic[First@List@var, dynamicFunction], String,
FieldHint -> "Search",
ContinuousAction ->
True], {"MouseClicked" :> (If[ Length@currentList > 0, attachedCell = attachTo[thisBox, menu[currentList]]]),
"DownArrowKeyDown" :> (cPos =
Mod[cPos + 1, Length@currentList, 1]),
"UpArrowKeyDown" :> (cPos =
Mod[cPos - 1, Length@currentList, 1]), "ReturnKeyDown" :> (
lastLength = Length@currentList;
currentList = autocompleteF[var];
If[Length[currentList] > 0, var = currentList[[cPos]];
FinishDynamic[]]; dropMenu[])}, PassEventsDown -> True],
SynchronousInitialization -> False,
Initialization :> (thisBox = EvaluationBox[];
autocompleteF = Autocomplete[list];
currentList = autocompleteF@var;
dynamicFunction = (var = #;
lastLength = Length@currentList;
currentList = autocompleteF[var];
whatToDo[]) &;
whatToDo[] :=
Which[lastLength != 0 && Length[currentList] == 0, dropMenu[],
Or[lastLength == 0 && Length[currentList] > 0,
And[lastLength != 0 && Length[currentList] != 0,
Not@MatchQ[attachedCell, _CellObject]]],
attachedCell = attachTo[thisBox, menu[currentList]]];
attachTo[parentbox_, what_] :=
MathLink`CallFrontEnd[
FrontEnd`AttachCell[parentbox,
ToBoxes[ExpressionCell[what, StripOnInput -> True,
Background -> White, CellFrameColor -> GrayLevel@.8,
CellFrameMargins -> 0, CellFrame -> 2]], {Automatic, {Left,
Bottom}}, {Left, Top},
"ClosingActions" -> {"ParentChanged", "EvaluatorQuit",
"OutsideMouseClick"}]];
SetAttributes[menu, HoldFirst];
menu[currentList_] :=
EventHandler[
Pane[Dynamic[
Column[MapIndexed[itemWrapper, currentList], Spacings -> 0],
TrackedSymbols :> {currentList}], {200, {All, 300}},
AppearanceElements -> None, Scrollbars -> {False, True}],
"MouseExited" :> (dropMenu[];)];
itemWrapper[item_, {pos_}] :=
MouseAppearance[#, "LinkHand"] &@
EventHandler[
Framed[item, ImageSize -> {Full, Automatic},
FrameStyle -> None,
Background ->
Dynamic[If[pos == cPos, CurrentValue@"PanelBackground",
White]]], {"MouseClicked" :> (pos == cPos;
var = item;
dropMenu[];
lastLength = Length@currentList;
currentList = autocompleteF[var];),
"MouseEntered" :> (cPos = pos)}, PassEventsDown -> True];
dropMenu[] := (
If[MatchQ[attachedCell, _CellObject],
NotebookDelete[attachedCell];
attachedCell =.
])
)]
-
I did not know about
FrontEnd\AttachCell`. That could be useful. Did you learn about it by spelunking something? Do you know of anyway to have a branched pulldown or popup menu. For example, instead of using the second argument of dynamic to populate a menu in another popup could the second menu appear next to the first menu and be populated based on the selection in the first menu? We see this on web pages. If you know how to do this i can ask this as a question and you can post the answer. – Mike Honeychurch Dec 20 '15 at 05:48 -
@MikeHoneychurch could you link where I can take a look at such functionality? I will answer when take a closer look. All I know about
AttachCellis posted in the topic I've linked on top. p.s. have to go now, it's quite late(early) :P Will be in touch. – Kuba Dec 20 '15 at 05:51 -
I am finishing now too. I'll se if I can find a pull down menu that shows the functionality. – Mike Honeychurch Dec 20 '15 at 05:54
-
very nice, but I think there is a typo which made my mathematica complain: there is a double ';;' after
EvaluationBox[];;in theInitializationwhich makes Mathematica complain and the widget not work. – Albert Retey Dec 20 '15 at 10:23 -
-
@ Kuba - Good work with AttachCell! Theres a couple features that I'm working on adding to your code. For example I noticed that clicking inside the blank search field results in a scrollable list where all elements are not in alphabetical order. In fact, even when a list is restricted to a smaller subset by typing in a few characters, the resulting list is not quite in alphabetical order. It is in "an" order but it doesn't seem obvious to me what that order is and how to make it alphabetical. Simply changing the order of the elements in list does not seem to fix it. Any ideas? – B flat Dec 23 '15 at 17:41
-
@michael can't test now but it propably depends if the initial lost is sorted or not, here it wasn't. – Kuba Dec 23 '15 at 19:42
-
Yes. I assumed as much but it doesn't seem to match up even with a sorted list. – B flat Dec 23 '15 at 20:05


