Skip to content

Thinlet

opencode-agent[bot] edited this page May 11, 2026 · 1 revision

Thinlet GUI Framework

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.

Overview

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 a Frame
  • Declarative UI: Widget trees defined in XML, parsed at runtime via parse() method
  • Custom rendering: All widgets painted manually via Graphics calls — no native peers
  • Event dispatch: Central processEvent() routes AWT events to widget action handlers
  • Layout engine: Grid-bag-inspired layout with weightx/weighty distribution

Key Components

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

Thinlet.java Architecture

The Thinlet class (~3000 lines) is the central component. Key subsystems:

  1. Widget Tree Model: Widgets are stored as Object references in a flat internal model. Each widget holds typed properties via getXXX(component, name, default) / setXXX(component, name, value) pairs backed by a Hashtable. The :comp pseudo-property links children; :next provides sibling traversal.

  2. Layout Engine: doLayout() implements type-specific layout logic for all widget classes:

    • desktop — centers dialogs, fills other children
    • panel/dialog — grid layout with gap, 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
  3. Painting System: paint(Graphics) traverses the widget tree, rendering each widget type using Graphics primitives:

    • 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
  4. Event Handling: processEvent(AWTEvent) intercepts mouse, keyboard, focus, and component events:

    • Mouse events dispatched via findComponent() hit-testing and handleMouseEvent()
    • Keyboard events via processKeyEvent()
    • Focus traversal via Tab/Shift-Tab
    • Timer thread (run()) handles repeat events for scrollbar/spinbox and tooltip delay
  5. Widget Classes: Thinlet supports these widget types internally (classname comparison against string constants):

    • button, togglebutton, link, checkbox
    • combobox (with :combolist pseudo-widget)
    • textfield, passwordfield, textarea
    • label
    • panel, dialog
    • desktop
    • list, table, tree
    • menubar (with :popup pseudo-widget for menus)
    • tabbedpane
    • spinbox
    • slider
    • progressbar
    • splitpane
    • separator
    • bean (embedding arbitrary AWT Component)
  6. Internationalization: ResourceBundle support with langResource static default and per-instance resourcebundle for locale-specific text.

How It Works

Embedding Thinlet

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);
    }
}

XML Parsing

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.

Action Handler Pattern

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.

Color Scheme

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/vgradient images

Grid Layout Mechanics

The panel/dialog layout (getGrid()) computes:

  • Column/row preferred sizes from child preferred sizes
  • weightx/weighty distribution for remaining space
  • colspan/rowspan spanning for multi-cell children

Related Pages

Clone this wiki locally