From MozillaWiki
Jump to: navigation, search

The Thunderhead "Book"

As I've been hacking on Thunderhead (aka "Th") I have various things I need to write down, but I haven't decided on a doc format for Th, so just going to record the stuff here.

Component System Overview

Th is a GUI toolkit. Its components are built on top of a small OO library called JSTraits, which in turn is inspired by John Resig's post on JS OO libraries, which in turn was inspired by... you get the idea.

Any Th component must extend th.Component.

Box Model

Th components use the CSS box model for rendering, not for layout. That is, Th components have a margin, a border, padding, and content. Important: note that the margin is where the rendering and layout models conflate. The CSS layout mechanism collapses adjacent margins.

Th doesn't (consistently) collapse component margins because in Th, layout is pluggable--so layout managers can decide on an individual basis want to do with margin.

All of Th's default layout managers do not collapse margins.


The "insets" of a component is the size of the border and the padding of a component along each size; it is of this shape:

{ top: 10, right: 10, bottom: 10, left: 10 }

Core Technics


If you take a normal webpage and normal dom controls you just can press tab and switch the focus between two controls. Also, there can only one control per page has the focus at once. This fails when you start using a HTML5 Canvas. So, you have to "fake" these behaviours and make sure the focus is managed in a good way. Also someone should take care, that the current focused control on a canvas gets the key events the user is typing on the keyboard. FocusManager does *all* this for you. Ah, yes: you get copy/paste/cut also for free :)

FocusManager is not bind to only one scene. This offers you the possibility to have one FocusManager that controlls the controls on two different scenes. In some cases this makes tab moving easier. In most cases you will have one FocusManager per scene. Basicly, you just add the components that should be in one FocusManager - FocusManager doesn't care where they are / on which scene. By default, the tab-row (<< don't know the word for that...) is given by the row you add your components to a FocusManager.

The FocusManager is based on two dom-inputs, that have to be added to the page by hand at the moment. Future versions of the FocusManager will do this on their own.

When a component has received the focus its pseudoClass is set to "active". When it loses the focus again, the pseudoClass is removed.

Component's Functions:

these are functions, a component can have. If it has this, the FocusManager will call it at the given time.

  • performCopy(): called when the user wants to copy some text. Should return a string with the text to put into the clipboard
  • performPaste(text): called when the user wants to paste some text. Text is the text within clipboard. Component should add this text in some way.
  • performCut(): called when the user wans to cut some text. Should return a string like in performCopy(), but should also remove this text within the component


  • subscribe(component): adds a component to the FocusManager.
  • unsubscribe(component): removes a component from the FocusManager
  • focus(component): focus a given component. This *has* to be subscribed to the FocusManager


  • "keydown", "keypress", "keyup": forward the key events to the focused component
  • "focus:received": fired after a component gained focus; reciever is the focused component
  • "focus:lost": fired after a component lost focus; reciever is the former focused component

Component Notes


Right now isn't much of a button, but does know how to request a proper preferred size and draw itself as an image. In addition to the standard CSS background properties, Button also supports three other properties:

  • -th-top-image
  • -th-middle-image
  • -th-bottom-image

Button supports an "orientation" property that determines whether these three images are drawn vertically or horizontally.


A simple one-line text input (like the html <input></input>). Notice, you will have to subscribe the Input to a FocusManager to make it work. You can bind in new key actions via the KeyHelpers trait. The text of the input can be read via the variable "text". If you want to change the text, make *sure* to use the "setText(text)" function.

Useful functions:

  • selectAll()
  • setSelection(selStart, selEnd)
  • getSelectedText()
  • setText()


  • text:changed: fired after the input's text is changed. e = { text: input.text }


Sometimes you want to highlight something within a label by changing the color, font-weight, font-size of a certain text area. HtmlLabel is your choice! It offers you the easy way to do this. To keep things simple only a subset of the complete html-tags are allowed to style the text:

  • : makes the text bold
  • : same like
  • : makes the text italic
  • : makes the text underline

beside these normal html-like tags there is also a special, not html-like tag for changing the font's color:

  • <#hexcod></#hexcod>: changes the color of the text to the given hexcode

If you did something wrong with the html-tags you will see an error on the command line, which tells you where the parser failed.

Be careful: HtmlLabel looks the same like a normal Label within CSS!


Your basic scrollbar, comes in two varieties:

  • HorizontalScrollbar
  • VerticalScrollbar

These two are subclasses of Scrollbar. Scrollbar contains all of the scrolling logic; the subclasses exist primarily to facilitate CSS styling.

The Scrollbar has several properties that control its behavior:

  • min: the minimum value of the scrollbar (0 by default)
  • max: the maximum value of the scrollbar (100 by default)
  • value: the current value of the scrollbar (0 by default)
  • increment: the amount by which value is changed by clicking on the buttons
  • extent: the size of the scrollbar handle as a percentage of the total scrollbar size (0.1 by default); this property is set automatically in the Scrollbar's layout method

No special events are fired when the value is changed (we probably should fire a value changed event). Scrollbar does support a "scrollable" property; this should be set to a component that will be scrolled. Such components should also implement a getScrollInfo() method which returns an object of this shape:

{ scrollTop: 0, scrollHeight: 1000, scrollLeft: 0, scrollWidth: 500 }

If the component is scrollable in only one dimension, only those properties corresponding to that dimension need be included (e.g., just "scrollTop" and "scrollHeight").


Provides scrolling services to one component. Can contain a vertical scrollbar or a horizontal one, or both. These are automatically installed whenever the component inside the ScrollPane returns a preferred size that is larger than the ScrollPane in either dimension.

The vertical scrollbar can have little nibs that makes it draggable. In this case, dragging it changes the preferred size of the ScrollPane and causes the parent component to redo the layout. You can turn on the drag nibs by passing a "splitter" parameter to the ScrollPane, as in:

var sp = new th.ScrollPane({ splitter: true });


<scrollpane splitter="true"></scrollpane>

The CSS overflow-x and overflow-y properties control whether scrollbars are hidden in a given dimension; supported values are "hidden" and anything else. :-)


List accepts an array of anything as its contents; only the first dimension of the array will be used. If the items in the array have a "text" property, that will be used to represent that item in the list. If not, the result of toString() will be used (unless the item is a string).

This behavior can be customized by overriding the getItemText() method in the List, which takes an item as a parameter and returns a string.

There are other mechanisms to customize the rendering; I'll get to documenting those later.


Just as with List, you can override getItemText() in the HorizontalTree to customize how the items in the tree are converted to strings for the lists inside the HorizontalTree.


The editor is a template class that ought to be extended (subclassed if you will) in order to achieve whatever effect you want. A guide to using the editor can be found here.


A Th container uses a layout manager to work out where to put its children components and how big to make them.

Some layout managers rely on the components themselves to provide hints as to how to size them. These hints are provided by the following three methods:

  • getMinimumSize
  • getPreferredSize
  • getMaximumSize

Layout managers may use none or more of these methods to work out how to size the components in a container. These methods should return an object of this shape:

{ height: 100, width: 100 }

Default implementations of these methods are provided to every component; they are as follows:

  • getPreferredSize: returns left + right insets for width and top + bottom insets for height
  • getMinimumSize: returns getPreferredSize
  • getMaximumSize: returns getPreferredSize