Effective Use of Wolfram Language and Notebooks for User Interface Design

Noah Hardwicke,
Wolfram Research, Inc.

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

Overview

Our general UI design process

How Wolfram tech enhances this process

Notebooks as development and deployment environments

Prototyping in Wolfram Language

Efficient design iteration

Other useful WL and Notebook features for UI design

From Conception to Deployment

The usual UI creation process, having identified the UI’s aims and required functionality:

Iterative loops are key to any non-trivial project

Wolfram tech has allowed us to iterate more quickly and efficiently

Notebooks: Develop and Deploy in the Same Environment

The vast majority of our interfaces are WL expressions, deployed in or as notebooks.

Palettes, dialogs, even the Welcome Screen are Notebooks.

UI elements like:

toolbars,

FrontEndResource["NotebookToolbarExpressions","ToolbarCell"]//RawBoxes

entities,

{,,}

output size limiters,

Range[10^6]

etc., etc. are all formatted Wolfram Language Box expressions.

Hence, if we implement a prototype UI, we can manipulate and permute it in real time.

Examples from: New Assistance Features

Project notebook...

Don’t overlook the organisational benefits of Notebooks!
Keep notes, images, code, assets, etc. all in one document.

Content Suggestion Raft: First Prototype

First, let’s make a quick prototype of the raft itself.

Make use of:

Constructs like Column and Framed to lay out the interface.

DynamicModule and EventHandler to highlight items.

Button and NotebookWrite to insert suggestions.

Define an item as a framed label with a dynamic frame colour:

item[label_,i_, Dynamic[sel_]]:=EventHandler[

Framed[
label,
FrameStyle->Dynamic[If[sel===i,,]],
],

{"MouseEntered":>(sel=i)}]

Make a column of items, as per the mockup:

mock=DynamicModule[{sel=1},
Framed[

Column@MapIndexed[

Button[
item[#1,#2[[1]],Dynamic[sel]],

NotebookWrite[EvaluationNotebook[],MakeBoxes[#1]];
NotebookDelete[EvaluationCell[]],

]&,

{{x,0,1},{x,0,2π},{x,-π,π}}],

]]

Test It!

We want to attach the raft to the insertion point.

New in 14.1, AttachCell can attach to selections:

Button["Attach Mock",
AttachCell[
(* Attachment point: *)
NotebookSelection[],

(* Attachment: *)
ExpressionCell[mock,],

(* Alignment: *)
{Center,Bottom},Offset[{-5,-12},0],{Left,Top},

(* Options: *)
RemovalConditions->"MouseClickOutside"]]

Let’s try it:

Plot[Sin[x],]

Labeled[Framed[#,RoundingRadius->5],"Sine Wave"]&@Plot[Sin[x],]

Refine

Issue: the raft can get clipped by the window edge.

Solution: make the raft full-width and center-aligned, using AttachCell’s “WindowCentered” position specification.

Alter the enclosing Frame, making it full-width:

mock=DynamicModule[{sel=1},
Framed[

Column@MapIndexed[

Button[
item[#1,#2[[1]],Dynamic[sel]],

NotebookWrite[EvaluationNotebook[],MakeBoxes[#1]];
NotebookDelete[EvaluationCell[]],

]&,

{{x,0,1},{x,0,2π},{x,-π,π}}],

,
ImageSize->{Full,Automatic},
ImageMargins->{{25,25},{0,0}}]]

Align the raft to the window center:

Button["Attach Mock",
AttachCell[
(* Attachment point: *)
NotebookSelection[],

(* Attachment: *)
ExpressionCell[mock,],

(* Alignment: *)
{"WindowCenter",Bottom},Offset[{0,-12},0],{Center,Top},

(* Options: *)
RemovalConditions->"MouseClickOutside"]]

Now try our clipped example again:

Labeled[Framed[#,RoundingRadius->5],"Sine Wave"]&@Plot[Sin[x],]

Content Suggestion Raft

With a very quick and basic WL prototype, we could immediately test our design, identify an issue, and iterate in real time to find a solution.

...with a bit more work, we can incorporate other features of the mockup such as:

Input field

Loading indicator

With this more complete prototype, we can quickly play around with the code to optimise the UX

More Advanced Prototype

Create some carets:

{loadingCaret,loadingCaretRightwards,caret,caretRightwards} =

Make a column of items, adding an input field this time:

popupMock =

The main difference between our simple prototype and this more advanced one relates to managing display states of attached cells:

stateManager[{loading_, done_}] :=

As before, we call AttachCell to attach the prototype to the selection:

Button["Attach Mock",
AttachCell[
(* Attachment point: *)
NotebookSelection[],

(* Attachment: *)
ExpressionCell[stateManager[{loadingCaret, caret}],],

(* Alignment: *)
{Center, Bottom},Offset[{0,-1}, 0],{Center, Top},

(* Options: *)
RemovalConditions -> {}]]

Iteration is Still Easy!

Despite the increased complication of the prototype, much of the structure remained the same.

Hence, it’s still easy to modify in real time. Let’s try different caret positions:

Button["Attach Mock",
AttachCell[
(* Attachment point: *)
NotebookSelection[],

(* Attachment: *)
ExpressionCell[stateManager[{loadingCaret, caret}],],

(* Alignment: *)
{Center, Bottom},Offset[{0,-1}, 0],{Center, Top},

(* Options: *)
RemovalConditions -> {}]]

Use your Tools Efficiently

We used Illustrator to draw out our first idea:

We used WL to implement a quick prototype, assessed the effectiveness of the design in situ, and iterated accordingly.

Now we can revert back to Illustrator and rapidly mock up aesthetic changes:

Other Useful WL and Notebook Features

Import SVG assets directly

Copy and paste from Illustrator...

icon=ResourceFunction["SVGImport"][""]

This is now a Graphics expression which can manipulate in the usual ways:

icon=Append[icon,ImagePadding->{{0,1},{1,0}}]/.(ImageSize->{w_,h_}):>(ImageSize->{w,h}+{1,1})

Other Useful WL and Notebook Features

Make use of Cell options and TaggingRules

Let’s store our icon in the Notebook’s tagging rules for kernel-independent access:

CurrentValue[EvaluationNotebook[],{TaggingRules,"Assistant","Icon1"}]=icon;

AbsoluteCurrentValue[{TaggingRules,"Assistant","Icon1"}]

Now we can assess the icon in situ:

Plot[Sin[x],x,0,2π]

SetOptions[PreviousCell[],Initialization:>
AttachCell[
EvaluationCell[],

ExpressionCell[
Dynamic@AbsoluteCurrentValue[{TaggingRules,"Assistant","Icon1"}],
],

{Left,Center},Offset[{-5,0},0],{Right,Center}]]

...and because we referred to the Tagging Rule dynamically, I can now easily change the icon:

CurrentValue[EvaluationNotebook[],{TaggingRules,"Assistant","Icon1"}]=Labeled[icon,"Help"];

Summary

Many Front End interfaces are top-level WL expressions.

If we implement a prototype UI, we can manipulate and permute it quickly, enabling efficient and iterative refinement of its design.

With only a small number of key functions, you can quickly implement sophisticated prototypes. We made particular use of:
AttachCell, DynamicModule, Dynamic, EventHandler, Button, Column, Framed, NotebookWrite, TaggingRules, CurrentValue, AbsoluteCurrentValue.

Notebooks are powerful: organise, design, develop and deploy all in one document.