Fwt
Overview
Fan Widget Toolkit or FWT provides a toolkit for building both desktop and HTML5 browser based applications:
- Widgets are reusable UI components such as buttons, text fields, dialogs, etc
- Graphics for 2D rendering
- Eventing for user input: keyboard, mouse, and focus eventing
- UI layout
The fwt defines the basic low level infrastructure for applications. Flux is built on top of the fwt to provide a more advanced application framework for desktop apps.
Widgets
The fwt::Widget
class is the root of the widget class hierarchy. Widgets represent UI components such as buttons or text fields.
Widget Tree
Widgets are structured as a tree. Typically the root of a visible widget tree is a fwt::Window
on the screen. Any widget is a potential container for other widgets - although typically only Panes contain children. The following methods are used to work with the widget tree:
Widget.parent
: get the parent of a mounted widgetWidget.window
: get the window ancestorWidget.children
: list the children widgetsWidget.each
: iterate the children widgetsWidget.add
: add a child widgetWidget.remove
: remove a child widget
A widget may only be mounted under one parent. If you attempt to add a widget to multiple parents an exception is thrown.
Panes
Widgets come in two flavors: panes and controls. Panes
are widgets which are designed to be containers for other widgets. Panes are responsible for laying out their children. Widgets which don't subclass from Pane are most often leaf widgets which provide some control such as a button or text field.
Custom Widgets
Most often you will use the predefined widgets in the toolkit. However you can create your own widgets too. Typically you will subclass fwt::Pane
to create a new container widget or fwt::Canvas
for a new control. Panes are responsible for deciding how to layout their children. Controls will typically define custom painting and eventing.
Painting
Canvas
widgets may override the onPaint
method to provide custom painting. Painting is done via the gfx::Graphics
API:
- draw geometries
- fill geometries
- draw text
- draw images
- coordinate system transformations
- clipping
A Graphics instance maintains state for how it renders:
gfx::Pen
defines how geometries are drawngfx::Brush
defines how geometries and text are drawn and filledgfx::Font
defines how text is renderedgfx::Image
models an image - typically loaded from a png, gif, or jpeg file- current clipping region
- current transform to apply to the coordinate system
You can use the push
and pop
methods to create a stack of Graphics instances and their associated state. A typical pattern is:
g.push try { g.translate(...) ... } finally { g.pop }
The following is a simple widget which paints itself as a red box with a blue outline:
using gfx using fwt class RedBox : Canvas { override Void onPaint(Graphics g) { w := size.w h := size.h g.brush = Color.red g.fillRect(0, 0, w, h) g.brush = Color.blue g.drawRect(0, 0, w-1, h-1) } Void main() { Window { InsetPane { content = RedBox() }, }.open } }
Layout
Every widget plays a role in how the UI is laid out. Leaf widgets define a preferred size by overriding the prefSize
method. The prefSize
method takes a gfx::Hints
which contains an optional width and height constraint.
Panes which contain children implement a layout strategy by overriding the onLayout
method. This callback is used to set the bounds of all the children widgets. Often panes will also override prefSize
to compute the containers preferred size from the children.
Eventing
All widgets support a set of eventing callbacks which by convention start with the "on" prefix. Widget events are declared as fields of type fwt::EventListeners
which maintain a list of callback functions. Event callbacks take a single Event
argument. Refer to the fandoc of each event to see how the Event fields are used.
The following illustrates a simple text field with some event handlers:
class EventTest { Void modifyCallback(Event event) { echo(event) } Void main() { text := Text { onFocus.add |Event e| { echo(e) } onAction.add |,| { echo("onAction!") } onModify.add(&modifyCallback) } Window { GridPane { text, }, }.open } }
The example illustrates creating callbacks using both closures and the curry operator. The onFocus
event handler is defined as a closure with an Event
argument. The onAction
event handler is also a closure, but with no arguments (remember that you can use functions which take fewer arguments). The onModify
event handler shows how you might use the curry operator to define a callback function.
Commands
A common technique in user interfaces to separate the UI elements from the command logic. For example you might have both a menu item and a toolbar button for a "Save" command. The fwt::Command
class is used to manage this design pattern.
Commands are responsible for the text, icon, accelerator, and how a command is executed. Commands may optionally handle undo/redo. Often the visual elements of a command are stored in a localization props file. For example to create a localized "Save" command:
// locale/en.props save.name=Save save.icon=fan:/sys/pod/icons/x16/save.png save.accelerator=Ctrl+S // create Command instance save := Command.makeLocale(Pod.find("flux"), "save") |,| { echo("save!") }
You can create UI widgets from commands:
menu.addCommand(save) toolBar.addCommand(save)
Widgets which are mapped to a command are said to be registered. Registration occurs automatically when setting a widget's command
field. If you create your own custom widgets with command support you should follow the pattern used by Button
and MenuItem
. Once a widget is registered with a command, it tracks the command's state. For example enabling or disabling the command will automatically enable/disable as its registered widgets. For toggle commands its widgets automatically track the command's select state.