Edit
Once again b3m2a1 provided more detailed information about FE feature so I have to retract my claims it is not worth an effort to create a popup on cursor position.
Main
The following answer is closely related to Live code templates I'm playing with.
The linked topic uses AttachedCell
but here we will use more documented, notebook with Frameless WindowFrame. I will also not care about preserving definitions or edge casesoutline two alternative methods, if you want take a look at the source in theattached cell vs frameless palette. (the linked topic. uses the former one but without attaching on cursor position)
Run code from the last sectionMake sure you saved your work, run testPopup[]
this is a rough implementation and hit Ctrl+1 to interact with menu.
It will be in the center of the notebook. I will explain why in a secsecond approach uses an undocumented AttachedCell.
Cursor position issue
I willThis one is not implement 'show on a cursor position' popupposition because I think it is not worth the effort. I will tell you why and show the direction in case you are stubborn enough.
FrontEndExecute@FrontEnd`UndocumentedGetSelectionPacket@InputNotebook[]
with itstough "VirtualSelectionRectangle"
it can be used to aquire a little info about cursor position. As I will show now, it:) this one is not enough with respect to other tools we need to use.:
testPopup["Cell"]
+ Ctrl+1
We can either use AttachedCell
(undocumented) or a WindowFrame -> Frameless
palette for our popup.
Cell vs. Palette
According to what we know about an AttachedCell
133523. Its position canPalette is not be specifiedclipped by absolute or scaled coordinatesparent notebook so we don't have to care about that. AndIt matters when the offset it supportsselection is only in {-1,1} direction. Not precise enoughclose to use itthe bottom/right edge too.
WindowFrame->"Frameless"
palette could Palette is documented
AttachedCell can be used and if we want to align it with thedirectly put on cursor we needposition, thanks to know parent notebook's WindowMarginsUndocumentedGetSelectionPacket
(easy). But coordinates returned fromIt is not so easy with Palette because UndocumentedGetSelectionPacketUGSP
dodoes not take into account DockedCells
,on DockedCells and additionaly Rasterize[#, "BoundingBox"]&
will not help with this as it misses specific margins/frameswe need to do a little WindowMargins arithmetic.
Cursor position
FrontEndExecute@FrontEnd`UndocumentedGetSelectionPacket@InputNotebook[]
At this point I decided I don't care. And we didn't even discussed that you need to do many of those calculations Kernel side. And you need to be flexible in case the selection
its "VirtualSelectionRectangle"
value is closea cursor position with respect to the bottom.right left edge, then the layout should rise instead of dropthe very bottom docked cell (or nb menu).
testPopup[]testPopup[type:"Cell"|"Palette":"Cell"]:=CreateDocument[{}
, WindowTitle -> type <> " popup menu"
, StyleDefinitions -> Notebook[{
Cell[StyleData[StyleDefinitions -> "Default.nb"]],
Cell[StyleData["Notebook"], Cell[StyleData["Notebook"]
, NotebookEventActions -> {{"MenuCommand", "InsertNewGraphic"}:>openPopup[]>openPopup[type]} (*Ctrl+1*)
]
}]
]];
items = {
<|"lbl"->"item 1","action":>NotebookWrite[InputNotebook[], "item1"],"shortkey" -> "m"|>,
<|"lbl"->"item 2","action":>NotebookWrite[InputNotebook[], "item2"],"shortkey" -> "k"|>,
<|"lbl"->"item 2","action":>NotebookWrite[InputNotebook[], "item3"],"shortkey" -> "l"|>
};
openPopup[]openPopup["Palette"]:= NotebookPut @ CenterToParent @ Notebook[
List @ Cell[BoxData @ ToBoxes @ popupMenu[]popupMenu["Palette"]
, CellFrameMargins->{{0, 0}, {0, 0}}
, CellMargins->{{0, 0}, {0, 0}}
]
, StyleDefinitions->"Dialog.nb"
, WindowClickSelect->False
, WindowFloating->True
, WindowFrame->"Frameless"
, WindowSize->All
, Background->LightBlue
, WindowElements->{}
, WindowFrameElements->{}
];
popupMenu[]openPopup["Cell"] := Module[{sel,fromTop, fromLeft}
, Catch[
sel = FrontEndExecute@FrontEnd`UndocumentedGetSelectionPacket@
InputNotebook[]
; If[
MatchQ[sel, KeyValuePattern[{"CellSelectionType" -> "ContentSelection", "SelectionType" -> "CursorRight"}]]
, {fromLeft,fromTop} = Lookup[sel, "VirtualSelectionRectangle", {{0,0},{0,0}}][[All, 1]]
, Beep[];Throw@$Canceled
]
; MathLink`CallFrontEnd[
FrontEnd`AttachCell[
EvaluationNotebook[]
, Cell @ BoxData @ ToBoxes @ popupMenu["Cell"]
, {Offset[{-fromLeft,-fromTop},0], {Left,Top}}
, {Left,Top}
, "ClosingActions"->{ "OutsideMouseClick" }
]
]
]
];
popupMenu[type_]:=DynamicModule[
{item=1,thisObject, parentObject, menuClose, items = items, itemsLength = Length@items}
, Table[With[{i=i},
EventHandler[#, "MouseEntered":>(item = i)]& @ Button[
Style[
StringTemplate["`lbl` (`shortkey`)"]@items[[i]], Bold, FontColor-> Dynamic@If[item==i,Darker@Red,Darker@Green]]
, items[[i,"action"]]; menuClose[]
, FrameMargins->15
, ImageMargins->0
, Appearance->FrontEndResource["FEExpressions","GrayNinePatchAppearance"]
]]
, {i, itemsLength}
]//Column[#,Spacings->0]&
, Initialization:>(
If[
type === "Palette"
,
thisObject = EvaluationNotebook[];
parentObject menuClose[]:=NotebookClose @ thisObject
,
thisObject = InputNotebook[];EvaluationCell[];
menuClose[]:=NotebookClose=NotebookDelete @ thisObject;thisObject
];
parentObject = InputNotebook[];
CurrentValue[parentObject, NotebookEventActions] = {
"UpArrowKeyDown":>(item=Mod[item-1,itemsLength,1])
, "DownArrowKeyDown":>(item=Mod[item+1,itemsLength,1])
, "EscapeKeyDown":>menuClose[]
, "ReturnKeyDown" :> (items[[item, 2]]; menuClose[])
, "KeyDown" :> If[
MemberQ[items[[;;, "shortkey"]], CurrentValue["EventKey"]]
, (#action& /@ Select[items, #shortkey === CurrentValue["EventKey"]&]);menuClose[]
]
, "MouseClicked" :> menuClose[]
}
)
, Deinitialization:>(
CurrentValue[parentObject,NotebookEventActions]=Inherited
)
]
CenterToParent::usage = "CenterToParent[DialogInput[...]] etc, will make the dialog centered with respect to the parent notebook";
CenterToParent // Attributes = {HoldFirst};
CenterToParent[ dialog_[whatever__, opts : OptionsPattern[]] ] := With[
{apc = AbsoluteCurrentValue}
, With[
{ parentCenter = Transpose @ {
apc[WindowMargins][[;; , 1]] + .5 apc[ WindowSize]
, {Automatic, Automatic}
}
}
, dialog[
whatever
, NotebookDynamicExpression :> Refresh[
SetOptions[
EvaluationNotebook[]
, WindowMargins -> (parentCenter - Transpose[{.5 apc[WindowSize], {0, 0}}])
, NotebookDynamicExpression -> None
]
, None
]
, opts
]
]
];