Windows GUI Library
The klyn.gui.windows package provides Klyn's native widget layer for desktop
applications. It is built around typed widgets, layout managers, multicast events, and KSS
themes so application code stays explicit and statically typed.
Only klyn is imported by default. GUI applications must import the widget package
and, when needed, the layout, event, and binding packages explicitly.
import klyn.gui.windows
import klyn.gui.windows.layouts
import klyn.gui.event
import klyn.binding
The main classes are MainWindow, Container, Label,
TextBox, Button, ComboBox, and the layout managers in
klyn.gui.windows.layouts.
A MainWindow owns a centralWidget. Add widgets to containers and let
a layout manager compute bounds. This keeps resize behavior in one place and avoids manual
coordinate code in ordinary forms.
import klyn.gui.windows
import klyn.gui.windows.layouts
win = MainWindow("Hello")
win.size = (420, 240)
root = win.centralWidget
root.layout = VBoxLayout(spacing=12)
root.padding = [24, 24, 24, 24]
title = Label("Welcome to Klyn")
root.add(title)
field = TextBox()
field.placeholder = "Your name"
root.add(field)
button = Button("Continue")
root.add(button)
win.centerIn(null)
win.show()
win.run()
Layouts currently include VBoxLayout, HBoxLayout,
GridLayout, FormLayout, FlowLayout, and
DockLayout. For form-oriented screens, FormLayout is usually a better
starting point than absolute positioning.
GUI events are multicast. Use += to subscribe and -= to unsubscribe.
A single event can have several handlers, so a widget can notify both application logic and
instrumentation code without replacing the previous callback. See
Signals vs Events for the precise split between
observable state and rich UI actions.
import klyn.gui.windows
import klyn.gui.event
class Controller:
public onSave(action as ActionEvent) as Void:
print("save requested")
controller = Controller()
button = Button("Save")
button.clicked += controller::onSave
button.clicked += lambda(e: ActionEvent): print(e.source)
Method references use object::method. Lambdas can capture values from the
surrounding scope and should keep an explicit event parameter type when the event payload
matters.
Core widgets expose the common GUI event set through klyn.gui.event:
clicked, pressed, released,
keyPressed, keyReleased, mousePressed,
mouseReleased, mouseMoved, resized,
closed, and paintRequested. Use these for user actions and lifecycle
notifications; keep ordinary mutable state as signal properties.
Observable widget state is declared as a signal property. The notification object is
intentionally not exposed as a separate member; use Binding.watch,
Binding.oneWay, or Binding.twoWay with member references.
import klyn.binding
import klyn.gui.windows
slider = Slider(value=50, minimum=0, maximum=100)
spin = Spinner(value=50, minimum=0, maximum=100)
Binding.twoWay(slider::value, spin::value)
Binding.watch(slider::value, lambda(value: Object): print(value))
Use event clicked as ActionEvent for rich UI actions. Use
signal property value as Int for state that should participate in data binding.
The dedicated Signals vs Events page explains when to
choose each tool.
Use Timer for periodic GUI work such as blinking indicators, clocks, previews, or
lightweight polling. Timer callbacks are dispatched by the native event loop, not by an
arbitrary worker thread.
import klyn.gui.windows
import klyn.gui.event
canvas = MyCanvas()
timer = Timer(1000)
timer.timeout += lambda(e: ActionEvent): canvas.repaint()
timer.start()
Stop timers when they are no longer needed. samples/gui/ClockSample.kn uses this
pattern to repaint an analog clock once per second.
Text fields expose editing state through TextBox. Password fields use
echoMode, and buttons can be marked as the default action for Enter-driven forms.
The native backend also supports Tab and Shift+Tab focus traversal.
import klyn.gui.windows
import klyn.gui.windows.layouts
import klyn.gui.event
public class LoginWindow extends MainWindow:
private login as TextBox
private password as TextBox
private status as Label
public LoginWindow():
super("Login")
this.size = (420, 260)
root = this.centralWidget
root.layout = FormLayout(rowSpacing=12, columnSpacing=16)
root.padding = [24, 24, 24, 24]
this.login = TextBox()
this.login.placeholder = "login"
root.add(Label("Login"))
root.add(this.login)
this.password = TextBox()
this.password.echoMode = TextBox.ECHO_PASSWORD
this.password.placeholder = "password"
root.add(Label("Password"))
root.add(this.password)
submit = Button("Connect")
submit.isDefaultButton = true
submit.clicked += this::connect
root.add(Label(""))
root.add(submit)
this.status = Label("")
root.add(Label(""))
root.add(this.status)
private connect(action as ActionEvent) as Void:
if this.login.text.trim() == "":
this.status.text = "Login is required"
return
this.status.text = "Connected"
win = LoginWindow()
win.centerIn(null)
win.show()
win.run()
The default widget appearance is defined with KSS, a small CSS-like styling layer. Built-in
themes live under KLYN_HOME/lib/klyn/gui/windows/themes as
light.kss and dark.kss. The default mode is system, so
Klyn chooses the light or dark theme from the operating-system appearance when it can.
import klyn.gui.windows
import klyn.gui.windows.themes
ThemeManager.setAppearanceMode(ThemeManager.MODE_DARK)
win = MainWindow("Themed")
win.appearanceMode = ThemeManager.MODE_SYSTEM
KSS currently covers the common surface properties used by the widget set: foreground color,
background color, margins, padding, font family, font size, borders, corner radius, placeholder
color, selection color, caret color, and slider track fill color. Slider thumbs use the
resolved accent-color, while the active track segment can be overridden with
track-fill-color. Prefer the built-in theme files for default widget styling;
application-specific colors should live in application code or a dedicated app theme. See GUI Themes and KSS for mode control, custom stylesheets, and the supported KSS subset.
Window.capture() renders the window client area to a BMP file through Klyn's own
painter pipeline. It is useful for documentation, visual regression checks, and tuning layouts
without relying on an external screenshot tool.
win = MainWindow("Preview")
win.size = (640, 420)
win.capture("preview.bmp")
Captures do not include operating-system window decorations. They show the same widget tree that Klyn paints inside the client area.
Open samples/gui/Calculator.kn for a larger GUI program using layouts, buttons,
focus feedback, and event handlers. Continue with Signals vs Events for the event/binding model, GUI Themes and KSS for appearance control, FormLayout for label/field forms, Slider Widget for scale input, then Canvas and
Custom Painting when you need a widget that draws its own pixels.