Cell Tray Widgets

Jason Abernathy, WRI

(RowDefault[, (Spacer1), , (Spacer1), ])  #WolframTechConf

ABSTRACT

The cell tray widget interface allows developers to produce high quality cell-level user interface tools that are managed by the front end. Widgets are organised into “trays” placed near the cell bracket.

An overview of the interface will be presented, along with a deep dive into widget properties, lifetime management, and an example of widget construction.

The Problems

User interface elements (want to) fight for space at the cell bracket:

Instantiation can be tricky

when to attach? Initialization?

how to handle transient elements?

The Solution

Cell Tray Widget Interface

An interface for managing, creating, and positioning widgets.

small, light-weight UI elements

placement managed by the front end

widgets instantiated manually or by the front end

Widgets

What does a widget look like?

CellPrint@Cell["Gear Icon",CellTrayWidgets->\[LeftAssociation]"GearMenu"->\[LeftAssociation]\[RightAssociation]\[RightAssociation],"Output"]

CellPrint@Cell["Cell Tag Editor",CellTrayWidgets->\[LeftAssociation]"CellTagsWidget"->\[LeftAssociation]\[RightAssociation]\[RightAssociation],"Output"]

CellPrint@Cell["SCU (Self-contained usage) Callout",CellTrayWidgets->\[LeftAssociation]"SCUWidget"->\[LeftAssociation]\[RightAssociation]\[RightAssociation],"Output"]

CellPrint@Cell["All of the above",CellTrayWidgets->\[LeftAssociation]"GearMenu"->\[LeftAssociation]\[RightAssociation],"CellTagsWidget"->\[LeftAssociation]\[RightAssociation],"SCUWidget"->\[LeftAssociation]\[RightAssociation]\[RightAssociation],"Output"]

Widgets

are Cells

have names

have properties

properties modify behaviour

key/value pairs in an association

<|"prop1" -> "val1",
   "prop2" -> "val2"|>

Using Widgets

Two “workflows”

Automatic instantiation

the front end creates and destroys widgets based on conditions

targets cell style (eg: “Input” cells)

Manual instantiation

user/developer code attaches widgets

targets cell objects

Automatic instantiation

Two key elements:

new cell-level option “CellTrayWidgets”, and

a "Condition" widget property.

Automatic instantiation ...

CellTrayWidgets accepts an association where

keys are widget names, and

values are properties.

Cell["Gear Menu",
  CellTrayWidgets -> \[LeftAssociation]
"GearMenu"->\[LeftAssociation]
"prop1"->"val1",
"prop2"->"val2"
\[RightAssociation]
\[RightAssociation]
]//CellPrint

Automatic instantiation ...

The "Condition" property controls when the widget is present

widget not present unless True (default)

Cell["Gear Menu",
CellTrayWidgets->\[LeftAssociation]
"GearMenu"->\[LeftAssociation]"Condition":>True\[RightAssociation]
\[RightAssociation]
]//CellPrint

Automatic instantiation ...

Association inheritance is useful.

For example, always show the gear menu in “MyStyle” cells

Cell[StyleData["MyStyle"],
CellTrayWidgets->\[LeftAssociation]
"GearMenu"->\[LeftAssociation]
"Condition":>True,
"prop1" -> "val1"
\[RightAssociation]
\[RightAssociation]]

but turn it off in presentations:

Cell[StyleData["MyStyle","Presentation",
StyleDefinitions->StyleData["MyStyle"]],
CellTrayWidgets->\[LeftAssociation]
"GearMenu"->\[LeftAssociation]
"Condition":>False
(*Inherited*)
(*"prop1" -> "val1"*)
\[RightAssociation]
\[RightAssociation]]

Automatic instantiation ...

"Condition" can be dynamic.

x = True;
Toggler[Dynamic[x]]

Cell["Gear Menu",
CellTrayWidgets -> \[LeftAssociation]
"GearMenu"->\[LeftAssociation]"Condition":>Dynamic[x]\[RightAssociation]
\[RightAssociation]
]//CellPrint

Manual instantiation

Targets cell objects.

Key elements:

widget attachment via function evaluation, and

removal via RemovalConditions argument.

Manual instantiation ...

AddCellTrayWidget[args___]:=
FE`Evaluate@FEPrivate`AddCellTrayWidget[args];

AddCellTrayWidget[target_CellObject, widget_, args___];

widget can be a widget name

AddCellTrayWidget[
 EvaluationCell[],"GearMenu"
];

widget can specify properties

AddCellTrayWidget[
EvaluationCell[],
"GearMenu"->\[LeftAssociation]"prop1"->"val1"\[RightAssociation]
];

Manual instantiation ...

Upon success, the function returns a handle to the widget.

widget=AddCellTrayWidget[
EvaluationCell[],"GearMenu"
]

The handle can be used to manipulate or dismiss the widget.

CurrentValue[widget,Background]=Orange

widget//NotebookDelete

Manual instantiation ...

Automatically dismissing a manually instantiated widget.

AddCellTrayWidget[
EvaluationCell[],"GearMenu",
RemovalConditions->{"MouseClickOutside"}
];

(same option values as AttachCell)

Manual instantiation ...

AddCellTrayWidget is idempotent by default.

AddCellTrayWidget[
EvaluationCell[],"GearMenu"
];

“AllowDuplicate” overrides this behaviour.

AddCellTrayWidget[
EvaluationCell[],"GearMenu",
"AllowDuplicate"->True
];

Widget Content

Three different methods for defining widget content:

Notebooks containing widgets,

widget registration, and

“Content” property value.

Widget Notebooks

Notebooks inside “PrivatePathsWidgets” directories are scanned for widgets.

AbsoluteCurrentValue[EvaluationNotebook[],"PrivatePathsWidgets"]

Widgets look like this:

Cell[
"content",
CellTags->{"name"}
]

"Open CellTrayWidgets.nb"

Widget Registration

Define widget content for lifetime of front end session.

RegisterWidget[args___]:=
  FE`Evaluate@FEPrivate`RegisterWidget[args];

RegisterWidget["FaceWidget"->"☺"]

AddCellTrayWidget[
EvaluationCell[],"FaceWidget"
];

overrides PrivatePathsWidgets

“Content” Property

Specify widget content on a per-instance basis.

AddCellTrayWidget[
EvaluationCell[],
"FaceWidget"->\[LeftAssociation]"Content"->"\[FreakedSmiley]"\[RightAssociation]
];

Cell["Just a face",
CellTrayWidgets -> \[LeftAssociation]
"FaceWidget"->\[LeftAssociation]"Content"->"\[FreakedSmiley]"\[RightAssociation]
\[RightAssociation]
]//CellPrint

Other Widget Properties

“Type” controls when the widget is present.

Cell["Widget types",
CellTrayWidgets -> \[LeftAssociation]
"GearMenu"->\[LeftAssociation]"Type"->"Focus"\[RightAssociation],
"FaceWidget"->\[LeftAssociation]"Type" -> Automatic\[RightAssociation]
\[RightAssociation]
]//CellPrint

“Focus” widgets are present when the cell has focus.

Other Widget Properties ...

“SortingValue” controls priority order of widgets.

(lower is closer to the bracket)

Cell["Sorting example",
CellTrayWidgets -> \[LeftAssociation]
"GearMenu"->\[LeftAssociation]"SortingValue"->2\[RightAssociation],
"FaceWidget"->\[LeftAssociation]"SortingValue"->1\[RightAssociation]
\[RightAssociation]
]//CellPrint

Automatic is given a lower priority

Developing Widgets

Tips

Minimized / active states

keep the minimized state small

For controls in the widget:

EvaluationCell[] is the widget,

ParentCell@EvaluationCell[] is the cell with the tray, and

use RuleDelayed (:>) for properties that are meant to be evaluated by the widget.

Example

A widget that shows the latest cell modification time.

We’ll need:

an appearance for the minimized state, and

the modification time.

Example ...

An appearance for the Minimized state.

min = Graphics[{
Thickness[Scaled[0.1]],GrayLevel[0.5],
Circle[],
Line[{Scaled[{0.5,0.5}],Scaled[{0.5,0.75}]}](*hour hand*),
Line[{Scaled[{0.5,0.5}],Scaled[{0.65,0.5}]}](*minute hand*),
Disk[Scaled[{0.5,0.3}],Scaled[0.05]](*an ellipsis*),
Disk[Scaled[{0.3,0.3}],Scaled[0.05]],
Disk[Scaled[{0.7,0.3}],Scaled[0.05]]
},ImageSize->16,ImagePadding->1]

Example ...

CellChangeTimes contains a list (potentially sublists) containing cell modification times.

{(Subscript[t, 1]), (Subscript[t, 2]), {(Subscript[t, 3,i]), (Subscript[t, 3,f])}, ...}

The following code snippet will grab the latest time

ReplaceRepeated[
CurrentValue[ParentCell[EvaluationCell[]],CellChangeTimes],
{___,last_} :> last
]

and this will transform it to a human-readable format

Replace[
,
{absTime_?NumberQ :> Style[DateString[absTime],Small],
_:>min}]

Example ...

Handle the mouse over with a PaneSelector.

PaneSelector[{
True->Dynamic[],
False->min},
Dynamic[CurrentValue[{"MouseOver","Box"}]],
min (*default appearance*),
ImageSize->Automatic (*resize*),
BaselinePosition->Bottom]

Example ...

Putting it all together.

With[{content=ToBoxes@},
Cell["Edit me",
CellTrayWidgets-><|
"EditTime"-><|"Content":>content|>,
"GearMenu" -> <||>
|>]]//CellPrint

The End

Thank you

Questions?