6
$\begingroup$

As a purely academic exercise can I make a box structure that'll toggle between two views when clicked and that doesn't ever depend on Dynamic or DynamicBox?

$\endgroup$
6
$\begingroup$

We'll do this with the FEPrivate`* functions and a whole bunch of TemplateBox tricks. First we start off with a box that'll toggle between two views based on a test:

FESelectorBox[
   data_List,
   test_,
   expr1_,
   expr2_
   ] :=
  With[{t = test[#3]},
   TemplateBox[
    Join[
     data, 
     {
      RowBox@{"{", data[[1]], ",", data[[2]], ",", ToBoxes@data[[3]], 
        "}"}, 
      ToBoxes@test, ToBoxes@expr1, ToBoxes@expr2}
     ],
    "FESelector",
    DisplayFunction ->
     (
      TemplateBox[
        data,
        "FESelectorInterior",
        DisplayFunction -> (
          FEPrivate`If[
           FEPrivate`SameQ[t, True],
           expr1,
           expr2
           ]
          )
        ] &
      ),
    InterpretationFunction ->
     (
      RowBox@{
         "FESelector", "[",
         #4, ", ", #5, ", ", #6, ", ", #7,
         "]"
         } &
      )
    ]
   ];

Next we create a nice wrapper for that box in the normal way:

MakeBoxes[
  FESelector[
   data_List,
   test_,
   expr1_,
   expr2_
   ], 
  StandardForm
  ] :=
 FESelectorBox[
  {ToBoxes@data[[1]], ToBoxes@data[[2]], data[[3]]},
  test,
  expr1,
  expr2
  ]
Options[FESelector] = {
   "DisplayFunctions" ->
    {
     True -> (# &),
     False -> (#2 &),
     Indeterminate -> (#2 &)
     },
   "DisplayTest" -> ({##}[[-1]] &)
   };
FESelector[
  data : {true_, false_, testDat_}, 
  ops : OptionsPattern[]
  ] :=
 Module[
  {
   test = OptionValue["DisplayTest"], 
   funcs = OptionValue["DisplayFunctions"]
   },
  If[! OptionQ@funcs,
   funcs = OptionValue[FESelector, {}, "DisplayFunctions"]
   ];
  FESelector[
   data, 
   test,
   Lookup[funcs, True, (# &) ],
   Lookup[funcs, False, (#2 &) ]
   ]
  ]

Then we hijack ``EventHandlerto execute switching code of someCurrentValuethat ourTemplateBox` will depend on but in such a way as to never call into the kernel:

FECurrentValueToggle[
  {a_, b_},
  target : Except[_String | _List] : FrontEnd`EvaluationNotebook[],
  key_
  ] :=
 FESelector[
  {a, b, key},
  "DisplayTest" -> (FrontEnd`CurrentValue[target, #] &),
  "DisplayFunctions" -> {
    True -> (
      TagBox[
        #,
        EventHandlerTag[
         {
          "MouseClicked" ->
           FEPrivate`FrontEndExecute@
            FrontEnd`SetValue[
             FEPrivate`Set[
              FrontEnd`CurrentValue[target, #3],
               False
              ]
             ],
          Evaluator -> None,
          PassEventsDown -> False,
          PassEventsUp -> True
          }
         ]
        ] &
      ),
    False -> (
      TagBox[
        #2,
        EventHandlerTag[
         {
          "MouseClicked" ->
           FEPrivate`FrontEndExecute@
            FrontEnd`SetValue[
             FEPrivate`Set[
              FrontEnd`CurrentValue[target, #3],
              True
              ]
             ],
          Evaluator -> None,
          PassEventsDown -> False,
          PassEventsUp -> True
          }
         ]
        ] &
      )
    }
  ]

And finally we have what we wanted:

toggle =
 FECurrentValueToggle[
  {
   Graphics[Disk[]],
    Graphics3D[Sphere[]]
   }, 
  {TaggingRules, "test"}
  ]

enter image description here

And this retains a reproducible InputForm:

toggle // InputForm

FESelector[{Graphics[Disk[{0, 0}]], Graphics3D[Sphere[{0, 0, 0}]], 
  {TaggingRules, "test"}}, FrontEnd`CurrentValue[FrontEnd`EvaluationNotebook[], 
   #1] & , 
 TagBox[#1, EventHandlerTag[{"MouseClicked" -> FEPrivate`FrontEndExecute[
       FrontEnd`SetValue[FEPrivate`Set[FrontEnd`CurrentValue[
          FrontEnd`EvaluationNotebook[], #3], False]]], Evaluator -> None, 
     PassEventsDown -> False, PassEventsUp -> True}]] & , 
 TagBox[#2, EventHandlerTag[{"MouseClicked" -> FEPrivate`FrontEndExecute[
       FrontEnd`SetValue[FEPrivate`Set[FrontEnd`CurrentValue[
          FrontEnd`EvaluationNotebook[], #3], True]]], Evaluator -> None, 
     PassEventsDown -> False, PassEventsUp -> True}]] & ] 
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.