I want to interactively manipulate the contents of a GridBox using keyboard commands. To approach this I need to control selection with something like SelectionMove but I cannot figure out the right parameters. How can I programmatically select individual elements and groups of elements within a GridBox?
- 271,378
- 34
- 587
- 1,371
2 Answers
Finally got this working. It's a huge pain to find the current element in Mathematica, as we all know so that was basically what I needed to figure out how to find. But I did! And here's how I'm finding that:
Get["https://raw.githubusercontent.com/b3m2a1/mathematica-tools/master/FENaming.wl"]
getCurrentPosition[tag_] :=
Module[
{
nextObj =
Replace[
BoxObject[
FENamed[_, tag <> "_gridElement"],
{FE`BoxOffset -> FE`BoxParent[3]}
],
Except[_BoxObject] :>
EvaluationBox[]
],
prevObj,
body,
inds
},
prevObj =
Replace[
FrontEndExecute@
FrontEnd`ObjectChildren@
FrontEndExecute@FrontEnd`ParentBox[nextObj],
{
{___, left_, nextObj, ___} :> left,
{nextObj, ___, last_} :> last,
_ -> None
}
];
If[prevObj === None,
body = NotebookRead[prevObj];
inds =
FirstCase[nextObj,
TagBox[__,
BoxID -> (s_String?(StringStartsQ[tag <> "_Position"]))] :>
ToExpression@
StringSplit[StringSplit[s, "_Position", 2][[-1]], ","],
{-1, -1},
6
];
inds = inds - {0, 1},
body = NotebookRead[prevObj];
inds =
FirstCase[body,
TagBox[__,
BoxID -> (s_String?(StringStartsQ[tag <> "_Position"]))] :>
ToExpression@
StringSplit[StringSplit[s, "_Position", 2][[-1]], ","],
{-1, -1},
6
]
];
inds
];
Then a very simple mover within a tagged grid and updated element wrapper:
moveAcrossTags[tag_, {dx_, dy_}] :=
With[{pos1 = getCurrentPosition[tag]},
NotebookLocate@
FENamed[_,
tag <> "_Position" <>
StringRiffle[
Map[ToString, pos1 + {dx, dy}],
","
]
]
]
makeNamedGridElements[gridEls_, tag_String] :=
MapIndexed[
EventHandler[
FENamed[
FENamed[#,
tag <> "_Position" <> StringRiffle[Map[ToString, #2], ","]],
tag <> "_gridElement"],
{
"RightArrowKeyDown" :> moveAcrossTags["realGrid", {0, 1}],
"LeftArrowKeyDown" :> moveAcrossTags["realGrid", {0, -1}],
"DownArrowKeyDown" :> moveAcrossTags["realGrid", {1, 0}],
"UpArrowKeyDown" :> moveAcrossTags["realGrid", {-1, 0}]
}
] &,
gridEls,
{2}
]
Finally:
realGrid =
Grid[
makeNamedGridElements[#, "realGrid"] &@
Partition[
Thread@
Graphics[
Thread[{RandomColor[25], Disk[]}],
ImageSize -> 25
],
5]
]
Original
Here's the start of a solution using the FENamed structure I defined here. We just attach a findable tag to each thing via the BoxID parameter, then we automatically get a clean way to find an element:
makeNamedGridElements[gridEls_, tag_String] :=
MapIndexed[
FENamed[#, tag <> Map[ToString, #2]] &,
gridEls,
{2}
]
realGrid =
Grid@makeNamedGridElements[#, "realGrid_"] &@
Partition[
Thread@
Graphics[
Thread[{RandomColor[25], Disk[]}],
ImageSize -> 25
],
5]
Once we have the current element it's easy to increment to the next, but finding the current one is admittedly beyond my ken right now.
-
This is quite elaborate and it will take some time to understand how this works. I am not sure I will be able to apply it to what I had in mind, but I appreciate your work on this! – Mr.Wizard Feb 25 '19 at 10:23
-
@Mr.Wizard no worries if you can’t. My idea was simply to provide a tag to each grid element (via
BoxID) and then use some undocumented front-end functions to find those elements (these things can also replace them, set/getOptionson them and much much more). – b3m2a1 Feb 25 '19 at 10:25 -
Is
FENaming.wlsomething you previously produced? I do not recall its appearance. – Mr.Wizard Feb 25 '19 at 10:29 -
1@Mr.Wizard the core code has existed since I wrote that answer, but I just recently cleaned it up and made it into a proper package so that I could load it in this answe. – b3m2a1 Feb 25 '19 at 10:30
Perhaps something crude like this could work:
nb = EvaluationNotebook[];
GridBox[{{"a", "b", "c"}, {"d", "e", "f"}}] // DisplayForm
(* select col 2, row 2 -> 5 *)
SelectionMove[nb, Previous, Cell, 2];
SelectionMove[nb, Before, CellContents]
Do[SelectionMove[nb, Next, Character, 2], 5]
SelectionMove[nb, All, Word, 1]
- 31,425
- 8
- 90
- 281
-
This seems to only work with single character grid elements. If I use
{"a", "b00", "c111"}for the first row I get a different selection. – Mr.Wizard Feb 25 '19 at 10:18 -
1@Mr.Wizard After looking at it again, I don't think this approach is tenable. – M.R. Feb 27 '19 at 23:21



BoxID. – b3m2a1 Feb 23 '19 at 06:17