# Built-in Views
- See also the technical views reference.
These are designed to be composable enough to build any reasonable UI.
If a complex view is common enough, it will become a built-in composite.
Otherwise, the community can publish compostes and functions as needed.
# A note on backing refs
All properties have a ref equivalent, where something
is backed by ref $something
:
class View {
size: Size
$size: Ref<Size>
children: View[]
$children: Ref<View[]>
// etc ...
}
The refs back the properties, so v.prop === v.$prop.val
always. See the refs guide.
To avoid verbosity, backing refs are omitted from the rest of the documentation.
# Layout types
To help views lay themselves out, views may override two methods:
-
adjust()
allows a view to set its own size -
layout()
allows a view to reposition and resize its children
This results in three types of views:
For a deeper dive into understanding layouts, see the layout technical guide.
# Generic
Generic views can be malleable or axiomic depending on how they’re used.
# View
The base class.
Neither axiomic nor malleable, since it doesn’t implement layout
or adjust
.
Its size is whatever you tell it to be, either via size
or $size
(see backing refs).
class View {
panel: Panel | null
parent: View | null
children: View[]
point: Point
size: Size
canFocus: boolean // default false
canMouse: boolean // default false
visible: boolean
autofocus: boolean
hovered: boolean
pressed: boolean
selected: boolean
alpha: number // 0-1
background: number // default 0 i.e. 0x00000000
panelOffset: Point
mouse: Point
onPanelFocus?(): void
onPanelBlur?(): void
onMouseDown?(button: number): void
onMouseMove?(pos: Point): void
onMouseUp?(): void
onMouseEnter?(): void
onMouseExit?(): void
onWheel?(x: number, y: number): void
onFocus?(): void
onBlur?(): void
onKeyDown?(key: string): void
onKeyUp?(key: string): void
onKeyPress?(key: string): boolean
adjust?(): void
layout?(): void
forceLayoutTree(): void
adopted?(parent: View): void
presented?(panel: Panel): void
draw(ctx: DrawingContext): void {
this.drawBackground(ctx, this.background)
}
protected onChildResized(child: View) {
this.adjust?.()
this.layout?.()
}
protected drawBackground(ctx: DrawingContext, bg: number) {
ctx.fillRect(0, 0, this.size.w, this.size.h, bg)
}
focus() { this.panel?.focusView(this) }
needsRedraw() { this.panel?.needsRedraw(this) }
get firstChild(): View | undefined
get lastChild(): View | undefined
addChild(child: View, i = this.children.length): void
removeChild(child: View): void
remove(): void
}
# Grid
Malleable when cols === Infinity
, otherwise axiomic.
Lays out children in columns,
wrapping at cols
,
or edge if flow
is set.
Properly handles differently sized children.
class Grid extends View {
cols = Infinity
flow = false
xgap = 0
ygap = 0
}
# Spaced
Axiomic in one direction, malleable in the other.
Spaces its children apart equally.
A least favorite child. A necessary evil. An unfortunate fact of life. Almost deprecated.
class Spaced extends View{dir:'x'|'y'='x'}
class SpacedX extends Spaced{}
class SpacedY extends Spaced{}
# Malleable
Malleable views can reposition and resize their children, but don’t have an inherent size.
They rely on a parent view to set their own size.
These are an app’s outer layers, starting from the root panel view and working inwards.
# Margin
Malleable.
Resizes and repositions its child to match its own size and position, except padding.
Use this to add a border around a malleables like splits or more margins.
The border can be transparent padding or colored.
class Margin extends View {
// shortcut for up = down = left = right = n
padding: number
// each defaults to 0
up: number
down: number
left: number
right: number
paddingColor: number // defaults to transparent black
}
# Center
Malleable.
Literally just centers its content on both axes.
No customization. Put inside Paned if you need alignment to an axis.
class Center extends View { /*...*/ }
# Paned
Malleable.
Like Split, except it “vacuums-shrinks” towards a given side.
The side being vacuumed must be axiomic in the given direction.
After layout, both sides will be resized to fit available area.
class Paned extends View {
gap = 0
dir: 'x' | 'y'
vacuum: 'a' | 'b'
}
Convenience subclasses:
class PanedXA extends Paned { dir='x'; align='a' }
class PanedXB extends Paned { dir='x'; align='b' }
class PanedYA extends Paned { dir='y'; align='a' }
class PanedYB extends Paned { dir='y'; align='b' }
# Scroll
Malleable.
Allows scrolling its content which presumably outgrows it.
Doesn’t resize its content.
(May have more than one child, but only deals with the first one.)
Currently there’s no way to auto-hide/show scrollbars in relation to mouse events.
class Scroll extends View {
scrollBy: number
showh: boolean // default true
showv: boolean // default true
get content() { return this.firstChild! }
scrollVisible(inner: View): void
}
# Split
Malleable.
Lays out two children side by side in a given dir
.
Resizable by pos
, which is relative to stick
side.
Negative values imply coming from the non-stick
side.
class Split extends View {
dividerColorHovered = 0xffffff11
dividerColorPressed = 0x1177ffcc
pos = 20
min = 10
max = -10
dir: 'x' | 'y'
stick: 'a' | 'b'
}
Convenience subclasses:
class SplitXA extends Split { dir='x'; stick='a' }
class SplitXB extends Split { dir='x'; stick='b' }
class SplitYA extends Split { dir='y'; stick='a' }
class SplitYB extends Split { dir='y'; stick='b' }
# Axiomic
Axomioc views are views that know their own size based on their contents and children.
These are an app’s inner layers, starting at the depths and building towards the root.
# Border
Axiomic.
Shrinks itself to fit around its child, except padding.
Use this to add a border around axiomics like buttons or labels.
The border can be transparent padding or colored.
class Border extends Margin {
// shortcut for up = down = left = right = n
padding: number
// each defaults to 0
up: number
down: number
left: number
right: number
paddingColor: number // defaults to transparent black
}
# Button
Axiomic.
Has no content.
Give it a Label or Image child,
or use a button
composite.
Use this to build clickable things, like text buttons, checkboxes, or scrollbar handles.
class Button extends Border {
// reasonable defaults
hoverBackground = 0xffffff22
pressBackground = 0xffffff11
selectedBackground = 0xffffff33
onClick?(button: number): void
}
# Group
Axiomic.
Shrinks to fit multiple children,
laying them out in a given dir
with optional gap
.
Aligns children to
start (a
),
middle (m
),
end (z
)
or grows them to match the biggest (+
).
class Group extends View {
gap: number
dir: 'x' | 'y'
align: 'a' | 'm' | 'z' | '+'
}
Convenience subclasses:
class GroupX extends Group { dir='x' }
class GroupY extends Group { dir='y' }
class GroupXA extends Group { dir='x'; align='a' }
class GroupXZ extends Group { dir='x'; align='z' }
class GroupYA extends Group { dir='y'; align='a' }
class GroupYZ extends Group { dir='y'; align='z' }
# Image
Axiomic.
Draws an image on screen.
Shrinks itself around the image.
Often wrapped with Border or Button.
class Image extends View {
bitmap: Bitmap | null
}
# Label
Axiomic.
Draws text on screen.
Shrinks itself around the text.
Often wrapped with Border or Button.
Allows multiline strings.
Not editable; use Textbox for that.
class Label extends View {
color = 0xffffffff
font: Font
text = ''
}
# Textbox
Axiomic believe it or not.
Not quite deprecated, but still very experimental and likely to become easier to use.
Shrinks itself around its content. Wrap in Scroll for stereotypical editable text area.
Use model
to get/set/edit text or respond to text events.
Has a primitive form of syntax highlighting for some reason, but no selection yet? Ha.
class Textbox extends View {
model = new TextModel()
font: Font
cursorColor = 0x0000ff99
textColor = 0xffffffff
editable = true
xgap = 0
ygap = 0
onEnter?(): void
}