ViewBox
ViewBox[expr_, displayView_, opts___]is a low-level box used to alter expr visibly with custom HTML/JS elements in StandardForm, while keeping the input form of expr behind.
Most decorations generated by TemplateBox, graphics functions Graphics, Graphics3D, InputButton and etc are ViewBox-es.
- the expression is not editable by a user and is fully replaced in the editor by the provided
displayView - the inner
expris revealed on the next evaluation
RGBColor, DateObject and InterpretationBox (together with EditorView) and many more use this structure to display a human-readable form of the content behind.
displayView must be frontend symbol (i.e., executed by WLJS Interpreter) and must be defined in advanceOptions
"Event"
Manually enables events generation. Using the provided event object identifier (_String), it fires various events based on its state. You can attach a normal EventHandler to the given id
uid = CreateUUID[];
EventHandler[uid, {pattern_String :> handler_, ..}]
ViewBox[.., "Event"->uid]The following patterns are available to be attached to
"Mounted"fires once a widget is visible. A unique identifier is provided as a payload in a form of association"Destroy"fires once it was removed
Once mounted, it provides a unique ID as a string from the key Instance in the "Mounted" event, which can be used in the constructor FrontInstanceReference. You can also apply ViewBox`InnerExpression and ViewBox`OuterExpression.
Examples
This method is much faster than InterpretationBox or Interpretation, since it does not spawn EditorView inside for displaying regular boxes.
Replacing expression with custom DOM element
You can define your own style for cell boxes. The visible DOM element in the editor is provided as element property of env object:
.js
core.MyView = async (args, env) => {
env.element.style.background = "red";
env.element.style.width = "2em";
env.element.style.height = "1em";
}myBox /: MakeBoxes[myBox[expr_], StandardForm] := ViewBox[myBox[expr], MyView]and then try
myBox[1/2]This is basically how RGBColor, DateObject are implemented
Mutability
In general it is possible to update the expression underneath indirectly. For this reason, there are multiple way of accessing this feature
From Wolfram Kernel
Please see methods below
ViewBox`InnerExpression
Used to replace a covered expression in a ViewBox with a given string.
It must be evaluated in the context of an instance of ViewBox, use FrontInstanceReference and FrontSubmit in order to apply this to a specific box
Returns the current content if no string is specified as an argument. Use it with FrontInstanceReference and FrontFetch.
For example, let's make a dummy view-box that covers a simple string with a disk
dummy /: MakeBoxes[_dummy, StandardForm] := With[{uid = CreateUUID[]},
EventHandler[uid, {
"Mounted" -> Function[res, ref = res["Instance"]]
}];
ViewBox["Covered", Graphics[Disk[]], "Event"->uid]
]
dummy[]Now our element has reported itself to the ref global symbol. Let's fetch this covered expression
FrontFetch[ViewBox`InnerExpression[], FrontInstanceReference[ref]]"\"Covered\""Now let's change it
FrontSubmit[ViewBox`InnerExpression["1+1"], FrontInstanceReference[ref]]Now if you evaluate our disk, you will see
2ViewBox`OuterExpression
Used to replace or update the content string within the box (including the box itself)
This will destroy an instance of the ViewBox once applied.
Example
ref = FrontInstanceReference[];
Plot[x, {x,0,1}, Epilog->{ref}]and then we can destroy it and replace with our text
FrontSubmit[ViewBox`OuterExpression["Hello World"], ref] From JavaScript function
The displayView expression must be a frontend symbol
Frontend symbols
Overview of frontend symbols / function
Decorating symbols
An advanced guide on how alter or enchance the displayed expression of a symbol
core.displayView = async (args, env) => {
env.global.EditorWidget
}this object contains a property to update the content
EditorWidget.applyChanges('"new content"')Example
Let's create a special object that will act like a checkbox
CheckObject /: MakeBoxes[CheckObject[state_:(True | False)], StandardForm] := With[{},
ViewBox[CheckObject[state], CheckBoxDecorator[]]
]now JS part
.js
core.CheckBoxDecorator = async (args, env) => {
let state = false;
//check the raw data from the viewbox to determine the state
if (env.global.EditorWidget.getDoc() == 'CheckObject[True]') state = true;
//make a rectangle
env.element.style.width = "1em";
env.element.style.height = "1em";
const update = (s) => env.element.style.background = s ? 'red' : 'blue';
//color it depending on state
update(state);
//logic for updates when a user click on it
env.element.addEventListener("click", () => {
state = !state;
update(state);
const stringState = state ? 'True' : 'False';
env.global.EditorWidget.applyChanges('CheckObject['+stringState+']');
});
}Let's test it!
CheckObject[True]then try to click on it, copy and paste it
whatever you do, it will keep its state synced. No communication with WL Kernel happens, everything is running within the code-editor in the browser.
Or even cooler
Table[CheckObject[True], {i, 3}, {j, 3}] // MatrixForm