-
Notifications
You must be signed in to change notification settings - Fork 0
Thinlet
Thinlet is a lightweight, pure-Java GUI toolkit for JNode that builds UIs declaratively via XML markup and renders them directly onto an AWT Container using custom painting.
Thinlet (gui/src/thinlet/thinlet/Thinlet.java) is a third-party lightweight GUI toolkit (www.thinlet.com) integrated into JNode for desktop GUI development. Unlike AWT or Swing which use heavyweight/lightweight peer systems, Thinlet renders all widgets directly using AWT Graphics primitives on a single Container. UIs are constructed from XML markup rather than imperative Java code, enabling a clean separation between UI description and application logic.
Key characteristics:
-
Single component: Extends AWT
Container, embedding as one component inside aFrame -
Declarative UI: Widget trees defined in XML, parsed at runtime via
parse()method -
Custom rendering: All widgets painted manually via
Graphicscalls — no native peers -
Event dispatch: Central
processEvent()routes AWT events to widget action handlers -
Layout engine: Grid-bag-inspired layout with
weightx/weightydistribution
| Class/File | Role |
|---|---|
Thinlet.java |
Core toolkit: widget tree management, event dispatch, layout engine, custom painting |
ThinTest.java |
Demo/test class showing XML markup parsing and component embedding |
The Thinlet class (~3000 lines) is the central component. Key subsystems:
-
Widget Tree Model: Widgets are stored as
Objectreferences in a flat internal model. Each widget holds typed properties viagetXXX(component, name, default)/setXXX(component, name, value)pairs backed by aHashtable. The:comppseudo-property links children;:nextprovides sibling traversal. -
Layout Engine:
doLayout()implements type-specific layout logic for all widget classes:-
desktop— centers dialogs, fills other children -
panel/dialog— grid layout withgap,columns,weightx/weighty -
combobox— field + dropdown button -
textfield/passwordfield— editable text input -
textarea— multiline text with word-wrap -
tabbedpane— tab layout with placement (top/bottom/left/right/stacked) -
list/table/tree— scrollable item lists -
menubar— horizontal menu bar -
spinbox— numeric field with increment buttons -
splitpane— resizable divider split
-
-
Painting System:
paint(Graphics)traverses the widget tree, rendering each widget type usingGraphicsprimitives:-
paintRect()— border + background rendering with gradient fill -
paintArrow()— directional arrows for scrollbars/combos -
paintScroll()— scrollpane with horizontal/vertical scrollbar knob positioning -
paintField()— text field with caret and selection highlighting
-
-
Event Handling:
processEvent(AWTEvent)intercepts mouse, keyboard, focus, and component events:- Mouse events dispatched via
findComponent()hit-testing andhandleMouseEvent() - Keyboard events via
processKeyEvent() - Focus traversal via Tab/Shift-Tab
- Timer thread (
run()) handles repeat events for scrollbar/spinbox and tooltip delay
- Mouse events dispatched via
-
Widget Classes: Thinlet supports these widget types internally (classname comparison against string constants):
-
button,togglebutton,link,checkbox -
combobox(with:combolistpseudo-widget) -
textfield,passwordfield,textarea label-
panel,dialog desktop-
list,table,tree -
menubar(with:popuppseudo-widget for menus) tabbedpanespinboxsliderprogressbarsplitpaneseparator-
bean(embedding arbitrary AWTComponent)
-
-
Internationalization:
ResourceBundlesupport withlangResourcestatic default and per-instanceresourcebundlefor locale-specific text.
Thinlet is embedded by extending Thinlet and calling add(parse(...)) with XML markup:
public class ThinTest extends Thinlet {
private static final String MARKUP =
"<panel gap=\"4\" top=\"4\" left=\"4\">" +
"<button text=\"Button Test\"/>" +
"<textarea text=\"TextArea\" wrap=\"true\" columns=\"30\" rows=\"10\" />" +
"</panel>";
public ThinTest() throws Exception {
add(parse(new ByteArrayInputStream(MARKUP.getBytes())));
}
public static void main(String[] args) throws Exception {
Frame f = new Frame();
ThinTest test = new ThinTest();
f.add(test, BorderLayout.CENTER);
f.setVisible(true);
}
}The parse() method reads XML and constructs the widget tree. Each XML tag maps to a widget class. Attributes set widget properties (text, selected, enabled, etc.). The action attribute names a method to invoke on the owning object when the widget is activated.
Actions are dispatched via reflection. When a widget triggers an action (e.g., button click, item selection):
private boolean invoke(Object component, Object item, String name) {
// Finds method named `name` on the associated handler object
// Invokes via reflection with the item as argument
}The handler object is obtained from the widget's prototype property or defaults to the Thinlet instance itself.
Thinlet uses a 9-color scheme set via setColors():
- Background, text, textbackground, border, disable, hover, press, focus, select
- Default gradient button rendering via cached
hgradient/vgradientimages
The panel/dialog layout (getGrid()) computes:
- Column/row preferred sizes from child preferred sizes
-
weightx/weightydistribution for remaining space -
colspan/rowspanspanning for multi-cell children
- GUI-AWT — Parent GUI hub covering video drivers, AWT, input handling
- AWT-Peer-Implementation — AWT peer implementation using Swing components (alternative GUI approach)
- Video-Driver-Architecture — Surface and FrameBuffer abstractions underlying GUI rendering
- Font-Rendering — TrueType and BDF font rendering used by GUI components