Re: Generic nested menus implementation
- To: mathgroup at smc.vnet.net
- Subject: [mg100658] Re: Generic nested menus implementation
- From: Albert Retey <awnl at gmx-topmail.de>
- Date: Wed, 10 Jun 2009 17:10:15 -0400 (EDT)
- References: <h0nums$bfl$1@smc.vnet.net>
Hi,
I have played around with your initial idea also, and appended is what I
came up with...
hth,
albert
(* START OF CODE *)
(* just a little helper *)
ClearAll@toLabel;
toLabel[s_String] := s;
toLabel[s_String -> l_List] := s;
toLabel[s_String :> _] := s;
(* the heart of the code: recursive definition of the action menues *)
ClearAll@createNextLevel;
SetAttributes[createNextLevel, HoldFirst];
Options[createNextLevel] = Flatten[{
Options[ActionMenu], "AppearanceWrapper" -> Row,
"DefaultLabels" -> {}
}];
createNextLevel[{levels_, stack_}, label_String, action_,
opts : OptionsPattern[]] := (
Do[levels[i] =., {i, Length[stack] + 1,
Max[DownValues[levels][[All, 1, 1, 1]]]}];
action[stack]
);
createNextLevel[{levels_, stack_}, label_String :> myaction_, action_,
opts : OptionsPattern[]] := (
Do[levels[i] =., {i, Length[stack] + 1,
Max[DownValues[levels][[All, 1, 1, 1]]]}];
myaction
);
createNextLevel[{levels_, stack_}, label_String -> items_List,
action_, opts : OptionsPattern[]] := With[{
defaultlabel = Function[
Which[
Length[#] >= Length[stack] + 1, #[[Length[stack] + 1]],
Length[#] > 0, Last[#],
True, "Choose"
]
][OptionValue["DefaultLabels"]]
},
Do[levels[i] =., {i, Length[stack] + 1,
Max[DownValues[levels][[All, 1, 1, 1]]]}];
levels[Length[stack]] = With[{i = Length[stack] + 1},
ActionMenu[
Dynamic[
If[Length[stack] >= i, stack[[i]], defaultlabel]
],
Map[
toLabel[#] :> (
stack = Append[Take[stack, i - 1], toLabel[#]];
createNextLevel[{levels, stack}, #, action, opts];
) &,
items
],
FilterRules[Flatten@{opts}, Options[ActionMenu]]
]
]
];
(* main function to call *)
ClearAll@nestedActionMenu
Options[nestedActionMenu] = Flatten[{
"AppearanceWrapper" -> Row, "DefaultLabels" -> {},
Options[ActionMenu]
}];
nestedActionMenu[items_List, action_: None, opts : OptionsPattern[]] :=
With[{
defaultlabel =
Function[If[Length[#] > 0, #[[1]], "Choose"]][
OptionValue["DefaultLabels"]]
},
DynamicModule[{
levels, stack = {}
},
levels[0] = ActionMenu[
Dynamic[If[Length[stack] > 0, stack[[1]], defaultlabel]],
Map[
toLabel[#] :> (
stack = {toLabel[#]};
createNextLevel[{levels, stack}, #, action, opts]) &,
items
],
FilterRules[Flatten@{opts}, Options[ActionMenu]]
];
Column[{
Dynamic[OptionValue["AppearanceWrapper"][
Table[
levels[i], {i, 0, Max[DownValues[levels][[All, 1, 1, 1]]]}]
]]
}]
]
];
(* END OF CODE *)
(* simple example usage, with the default there are no actions! *)
nestedActionMenu[{
"Africa" -> {
"Algeria" -> {"Algiers", "Oran"},
"Angola" -> {"Luanda", "Huambo"}
},
"North America" -> {
"United States" -> {"New York", "Washington"},
"Canada" -> {"Toronto", "Montreal"}
}
}
]
(* example with actions, they can be given globaly or locally for \
each entry *)
Deploy@nestedActionMenu[{
"Africa" -> {
"Algeria" -> {"Algiers", "Oran"},
"Angola" -> {"Luanda", "Huambo"}
},
"North America" -> {
"United States" -> {"New York", "Washington"},
"Canada" -> {"Toronto",
"Montreal" :> Print["Monteral is special!"]}
}
},
Print,
"DefaultLabels" -> {"Continent", "Country", "City"}
]
(*
somewhat more involved example,menu definition is created \
programmatically from CountryData and CityData, watch out,this will \
not work well when ContryData and CityData are not yet initialized \
and will take a while to finish
*)
menuDefinition = Map[
Function[cont, cont -> Map[
# -> CountryData[#, "LargestCities"][[All, 1]] &,
CountryData[cont]
]
],
DeleteCases[CountryData["Continents"], "Antarctica"]
];
(* example for a more interesting global action *)
cityinfo[{continent___, country_, city_}] := CreateDialog[{
Grid[{
{Style[city, "Section"],
Show[CountryData[country, "Flag"], ImageSize -> 50]},
{continent, country},
{"Population", CityData[city, "Population"]},
{"Elevation", CityData[city, "Elevation"]}
}, Alignment -> {{Left, Right}, Automatic}], DefaultButton[]
}]
(* check that the global action works: *)
cityinfo[{"Europe", "Switzerland", "Zurich"}]
(*
now we are ready to install the nested action menu in a "menu bar", \
we use the option "AppearanceWrapper" for further formatting...
*)
SetOptions[EvaluationNotebook[],
DockedCells -> {
Cell[BoxData[ToBoxes[Button["City Information",
CreateDialog[{ExpressionCell[Panel[
nestedActionMenu[
menuDefinition, (DialogReturn[]; cityinfo[#]) &,
Appearance -> None,
"AppearanceWrapper" ->
Function[
Grid[{#}, Alignment -> Left, Dividers -> All,
FrameStyle -> LightGray, ItemSize -> {10, 1}]],
"DefaultLabels" -> {"Continent", "Country", "City"}
],
ImageMargins -> 0, FrameMargins -> 0
],
CellMargins -> {{0, 0}, {0, 0}}
]},
WindowMargins -> {
{MousePosition["ScreenAbsolute"][[1]],
Automatic}, {Automatic,
MousePosition["ScreenAbsolute"][[2]] - 40}},
WindowFrame -> "Frameless",
CellMargins -> 0,
CellFrameMargins -> 0,
WindowSize -> FitAll
],
Appearance -> None
]
]],
"DockedCell"
]
}
]