6
$\begingroup$

Using Style with the option ContextMenu or AttachedCell it is possible to create context menus in Mathematica user interfaces. Both of these are undocumented, but shown in other answers on this site, see e.g. here or here and actually work well since several versions. But I recently detected that both do not work as expected when used in CDF Player or CDF Player Pro (I have tested this in version 10.4.1 and would actually need this to work in that version, but the problem persists with newer versions). The easiest way to test this is using the menu entry File -> CDF Preview.

The ContextMenu option seems to be ignored in CDF Player and the Attached cell is not closed when selecting an entry as it does in Mathematica (most probably because NotebookDelete does not work in the players).

Does anyone know if there is some "magic" option setting that will make either of them work in the Player versions or another workaround which would allow to define (right click) context menus so that they work in CDF Player and/or CDF Player Pro?

Here is some example code to show what it is that I am trying to get working:

Using the ContextMenu option (prefered):

 Style[
   Panel@Pane["Pane with context menu", {200, 200}],
   ContextMenu -> {
     MenuItem["One", KernelExecute@MessageDialog@"One",
       MenuEvaluator -> Automatic, Method -> "Queued"],
     MenuItem["Two", KernelExecute@MessageDialog@"Two",
       MenuEvaluator -> Automatic, Method -> "Queued"]
   }
 ]

or using attached cells:

EventHandler[
  Panel@Pane["Pane with context menu", {200, 200}],
  {
    {"MouseClicked", 2} :> MathLink`CallFrontEnd[
      FrontEnd`AttachCell[
        EvaluationBox[],
        Cell[BoxData@ToBoxes@Grid[{{
              Button["One", 
                NotebookDelete@EvaluationCell[];MessageDialog@"One",
                Appearance -> "Palette", Alignment -> Left
              ]
            }, {
              Button["Two", 
                NotebookDelete@EvaluationCell[] MessageDialog@"Two",
                Appearance -> "Palette", Alignment -> Left
              ]
            }},
            Spacings -> {0, 0}, BaseStyle -> "Panel"
        ]],
        {Automatic, {Center, Top}},
        {Left, Top},
        "ClosingActions" -> {"OutsideMouseClick", "EvaluatorQuit"}
      ]
    ]
  }
]
$\endgroup$
8
  • 1
    $\begingroup$ Albert why have you explicitly added the System` context? e.g System`ContextMenu? If the explicit context is needed the reason it is not working is probably because the System context gets stripped off. I have encountered this before and had to edit the file in a text editor to add it back, e.g. for System`MenuConfiguration This will work for a *.nb but may lead to problems with *.cdf $\endgroup$ Commented Jan 6, 2019 at 22:24
  • $\begingroup$ @MikeHoneychurch: it is there for no specific reason, I think I just copied that part from somewhere. I just tried the same code without the System context and it doesn't make a difference (works in Mathematica but does not work in Player). But as it is not necessary I have changed it in the example code... $\endgroup$ Commented Jan 7, 2019 at 17:13
  • 1
    $\begingroup$ I would recommend to avoid using CDF $\endgroup$
    – M.R.
    Commented Jan 9, 2019 at 1:38
  • 1
    $\begingroup$ @M.R.: unfortunately that is not an option (actually no CDF is involved, the code is encoded packages running in Player Pro) . There are 30+ users for this, using Mathematica instead of Player Pro for them would mean to pay for 30+ license upgrades (I would then rather not use a context menu at all)... $\endgroup$ Commented Jan 9, 2019 at 17:44
  • $\begingroup$ @AlbertRetey does PacletManager work in PlayerPro? $\endgroup$
    – Kuba
    Commented Jan 14, 2019 at 9:35

1 Answer 1

6
+500
$\begingroup$

Here's a budget solution:

WithBudgetContextMenu[expr_, actions : {__RuleDelayed}] := 
 DynamicModule[{layers, activeLayer, hideMenu, showMenu, alignment},
  showMenu[] := (layers = {1, 2}; activeLayer = 2; 
    alignment = MousePosition["EventHandlerScaled"]);
  hideMenu[] := (layers = {1}; activeLayer = 1);
  hideMenu[]; 
  EventHandler[#, {"MouseClicked", 1} :> hideMenu[], 
       PassEventsDown -> True] &@
     EventHandler[#, {"MouseClicked", 2} :> showMenu[]] &@
   Overlay[{expr, 
     Grid[actions /. 
       RuleDelayed[lbl_, 
         action_] :> {Button[lbl, action;, Appearance -> "Palette", 
          Alignment -> Left, 
          ImageSize -> CurrentValue["DefaultButtonSize"]]}, 
      Spacings -> {0, 0}, BaseStyle -> "Panel"]}, Dynamic@layers, 
    Dynamic@activeLayer,
    Alignment -> Dynamic[2 (alignment - .5)]]]

Works even in a FreePlayer:

WithBudgetContextMenu[
  Panel @ Pane["Pane with context menu", {500, 500}]
, {"One" :> MessageDialog@"One", "Two" :> MessageDialog@"Two"}
] // CDFDeploy["test.cdf", #] &

enter image description here

Here is another variation with a more fine grained control:

WithBudgetContextMenu2[panel_, Dynamic@layerController_] := 
 DynamicModule[{layers = {1}, activeLayer = 1, actionsMenu},
  EventHandler[#, {"MouseClicked", 1} :> layerController["Hide"], 
     PassEventsDown -> True] &@
   Overlay[
    {panel,
     Dynamic@actionsMenu
     },
    Dynamic@layers,
    Dynamic@activeLayer,
    Alignment -> {Center, Center}
    ],
  Initialization :> (
    layerController["Show"] := Function[actions,
      actionsMenu = Grid[
        List /@ BudgetContextMenu2MenuItem /@ actions
        , Spacings -> {0, 0}, BaseStyle -> "Panel"];
      layers = {1, 2}; activeLayer = 2
      ];
    layerController["Hide"] := (layers = {1}; activeLayer = 1);
    )
  ]

BudgetContextMenu2MenuItem[lbl_ :> action_] := 
 Button[lbl, action, Appearance -> "Palette", Alignment -> Left, 
  ImageSize -> CurrentValue["DefaultButtonSize"]]

BudgetContextMenu2EventHandler[expr_, Dynamic@menuToggler_, 
  actions : {__RuleDelayed}] := 
 EventHandler[
  expr, {"MouseClicked", 2} :> (menuToggler["Show"]@actions)]

DynamicModule[{menu},
  WithBudgetContextMenu2[
   Panel[#, ImageSize -> {200, 200}] &@Column[{
      BudgetContextMenu2EventHandler[
       Framed["Pane with context menu"], 
       Dynamic@menu, {"One" :> MessageDialog@"One"}
       ],
      BudgetContextMenu2EventHandler[
       Framed["Another Pane with context menu"], 
       Dynamic@menu, {"Two" :> MessageDialog@"Two"}
       ]

      }],
   Dynamic@menu
   ],
  SaveDefinitions -> True
  ] // CDFDeploy["test.cdf", #] &
$\endgroup$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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