13
$\begingroup$

For example, when an image is displayed in the frontend, the image suggestion toolbar, which is an AttachedCell, pops up:

AttachedImage2D

It can can be found here, among other attached toolbars:

FileNameJoin@{$InstallationDirectory,
              "SystemFiles", "FrontEnd", "SystemResources", "AttachedImage2D.nb"}

I want to create a custom attached cell that appears whenever I hover over a graphic. The following works for some extent, but I want to generalize it.

old = Plot[{Sin[x], Sin[2 x], Sin[3 x]}, {x, 0, 2 Pi}];

DynamicModule[{g = old, c},
 EventHandler[Dynamic@g, {
   "MouseEntered" :> (c = AttachCell[EvaluationBox[],
       DynamicModule[{}, 
        ButtonBar[{"Black" :> (g = Show[g, Background -> Black]), 
          "White" :> (g = Show[g, Background -> White]), 
          "Reset" :> (g = old)}], InheritScope -> True],
       RemovalConditions -> {"OutsideMouseClick", "ParentChanged"}])
   }]]

AttachedCell toolbar in work

  1. How to write the code for the attached cell generally, without knowing what the parent cell/expression will be?
  2. How to set the attached cell to appear whenever a particular expression (e.g. Graphics) appears in the frontend? I rather not specify Format definitions for Graphics objects.
  3. How to make the attached cell only appear when mouse hovers over the expression and disappear when mouse leaves the cells? "MouseExited" :> NotebookDelete@c is no good, as it exits when the mouse leaves g but is still over c, which is unwanted.
$\endgroup$
4
$\begingroup$

TLDR: I am afraid that too many pieces of this puzzle are inaccessible in the FrontEnd to make custom toolbars the way they are done by WRI.

Edit: my concerns were confirmed in comments below this post.


Ok, to make it more than a pessimistic comment here is what I know:

How to write the code for the attached cell generally, without knowing what the parent cell/expression will be?

Surprisingly it is not a big problem, your toolbar would look like this:

attachToolbar[box_] := AttachCell[
  box,
  DynamicModule[{bgInit},
     ButtonBar[{
       "Black" :> (CurrentValue[box, Background] = Black), 
       "White" :> (CurrentValue[box, Background] = White), 
       "Reset" :> (CurrentValue[box, Background] = bgInit)
       }],
   Initialization :> ( bgInit = CurrentValue[box, Background]; )
   ], 
   RemovalConditions -> {"OutsideMouseClick", "ParentChanged"}
]

You can even make those actions performed FE-side. With more complex operations you would need to NotebookRead -> modify in kernel -> NotebookWrite the content but still possible.

What's an unexpected issue here is that is though to obtain an appropriate BoxObject to work with. All we have is EvaluationBox/ParentBox.

You can ignore that issue and attach cell to the parent cell. Can be good enough but already worse what WRI is doing. But wait, you'd have to have a way to find a way to target the desired box in the cell. SelectionMove does not give much hope in descending within the cell box structure. Nor does NotebookFind in searching within box structure. You could try to attach BoxID and follow How to set focus of a dialog window? but it has several pitfalls. There's ridiculous solution for some cases, like Graphics, you'd have to inject Dynamic to each created Graphics expression, something like:

Graphics[{
  Dynamic[Print@ParentBox@EvaluationBox[]; {}, DestroyAfterEvaluation -> True], 
  Disk[]}
]

Click on the printed BoxObject. How and when to store/use this BoxObject is another story but I hope it already is crazy enough not to do this.

How to set the attached cell to appear whenever a particular expression (e.g. Graphics) appears in the frontend? I rather not specify Format definitions for Graphics objects.

I don't know the mechanism behind e.g. images toolbar and I am afraid we don't have access to that. You could attach CellEventActions to Output cells and monitor FrontEnd`UndocumentedGetSelectionPacket on mouse click but that cumbersome way is only part of a solution you'd have to implement.

How to make the attached cell only appear when mouse hovers over the expression and disappear when mouse leaves the cells? "MouseExited" :> NotebookDelete@c is no good, as it exits when the mouse leaves g but is still over c, which is unwanted.

I don't know how you could to this with 'native' toolbars but I think we've already established that we won't have those. Otherwise the idea is simple, both the parent and the attached cell should have EventHandlers attached, and you should keep track of where the mouse is with MouseEntered/Exited. If it isn't in any of them on exit then delete the attached cell.

$\endgroup$
4
  • $\begingroup$ Any idea how to modify/set up custom TagBoxes, because I was able to get a minimal way to activate the image toolbar: Cell[BoxData[GraphicsBox[ TagBox[RasterBox[{}], BoxForm`ImageTag[]], ImageSizeRaw -> {150., 150.} ]], "Output"] but the issue is I don't know how to inspect what that ImageTag is doing. Just for curiosity sake, "ImageEditMode" -> False can also be set on the RasterBox to turn off the toolbar $\endgroup$
    – b3m2a1
    Nov 30 '21 at 0:00
  • 1
    $\begingroup$ @b3m2a1, the only thing magical about BoxForm`ImageTag is that the FrontEnd was instructed to look for it. Nothing more, nothing less. The list of identifiers which summon AttachCell's is not end-user extensible. $\endgroup$
    – ihojnicki
    Nov 30 '21 at 1:31
  • $\begingroup$ @ihojnicki what about in a fragile undocumented way? Is there some config file we can edit to call up custom attachements or are those baked in at compile time? $\endgroup$
    – b3m2a1
    Nov 30 '21 at 1:34
  • 4
    $\begingroup$ @b3m2a1, not even in a fragile, undocumented way. Each one has some amount of custom native code backing it. Maybe at some point it could be brought top-level, but it might not be infinitely flexible. $\endgroup$
    – ihojnicki
    Nov 30 '21 at 2:05

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.