This chapter covers the application runtime, the widget and view object model, the display root, windows and workspaces, layouting and painting, input delivery, layouts, buttons, displays, editors and selectors, collections, menus, icons and textual content, and styles and renderers.
A Forms application constructs its visual hierarchy, shows its windows, and runs the event loop. The inherited event loop also supports I/O, timers, and asynchronous operations.
Application is the runtime root. Construct it before forms, windows, or controls. It is not a Widget. The application provides the primary Screen and its WindowManager, dispatches platform events, and owns the shared scaling, style, font, and input-method services.
The C++ class hierarchy and the visual hierarchy describe different relationships. The following diagram shows inheritance only. It does not describe which objects display or contain other objects. In particular, Window derives from Form; it is not a direct child of View.
The visual hierarchy describes how Forms objects appear together. Each level follows the same non-owning host pattern: Application provides the Screen, a WindowManager attaches Window objects, a Form attaches one content Control, and a Control attaches child controls, including layouts. A Workspace applies the same pattern inside an ordinary control: its window manager presents several windows within the workspace bounds.
Attaching a window or control does not transfer ownership. The code that creates an object keeps it alive while the hierarchy uses it. A window or control removes itself from its parent when it is destroyed.
The following example creates a visual hierarchy with a window, its content layout, and a control displayed by that layout.
Application provides the Forms runtime. It is not a Widget. Construct one before any widget, show the visual hierarchy, then enter the inherited event loop with Pt::System::Application::run().
The application owns its platform implementation, graphics backend, and primary Screen. It does not own the windows or controls that use those services. Widgets remove themselves from their visual parent when they are destroyed. Each widget receives an ID while the application exists; findWidget() resolves that ID only while the widget remains alive.
Platform events enter the inherited event loop and are routed through the Forms event dispatcher to their target widget or the primary screen. The same dispatcher handles layout, invalidation, repainting, scaling, and window changes. It permits the visual hierarchy to coalesce deferred work. Applications normally request changes through widget APIs. Use processEvent() only when deliberately supplying Forms events.
The application provides shared styles, style options, input methods, fonts, and scaling. Use setStyle() and setStyleOptions() to change the global theme. Those methods reset shared renderer facets and invalidate registered widgets. Widgets rebuild in onInvalidate(). setScaleFactor() sends a rescale event through the primary screen. Configure shared services before showing the user interface when possible. impl() and graphicsBackend() support Forms backend implementations; ordinary applications use the higher-level Forms APIs.
Every visual Forms object is a Widget. A widget has a non-owning parent relationship and becomes connected when its parent hierarchy reaches a Screen. Connection is independent of visibility. It supplies logical coordinates, scaling, repaint and state-change requests, and input delivery. Attaching a control does not transfer ownership: the caller keeps every attached object alive until it is detached.
Screen and WindowManager are widgets but not views. Form and Control are the usual View implementations. A view is the host and paint-surface boundary for controls. When a control joins a form that is shown on a screen, the screen, scaling, coordinates, and paint surface become available for the cycle in Layouting and Painting. Removing it reverses that relationship.
Derive custom visual content from Control. Derive from View only when a custom content host needs different paint-surface or coordinate behavior. WindowManager and WindowFrame support platform and embedded-window implementations; applications normally use the window manager provided by a Screen or Workspace.
Every visual Forms object is a Widget. Application is not. A widget supplies identity, screen connection, parent relationships, geometry, visibility, enabled state, scaling, coordinate conversion, repaint requests, event dispatch, and pointer capture. Derive ordinary visual content from Control. Derive directly from Widget only for a runtime or host base with its own parent coordinate system; derived classes must implement onToParent() and onFromParent().
Construct widgets only after an Application exists. The application gives each widget an ID, registers it for event routing, and removes that entry during destruction. A name is optional metadata, not an identifier. Parent and screen pointers are non-owning runtime relationships. A widget is connected when its parent hierarchy reaches a screen, independent of its visibility. Containers attach and detach widgets through their public APIs; callers keep attached widgets alive.
Geometry uses logical coordinates. position() is in parent coordinates, while size() and bounds() are local and bounds() starts at the local origin. toParent(), fromParent(), toGlobal(), and fromGlobal() convert between these systems. The default global conversion walks the parent chain. Coordinate hooks must agree with hit testing, painting, and input delivery.
show(), enable(), activate(), move(), and resize() are requests. A parent, frame, or backend confirms them by sending Forms events, which then update the observable state. Names, size limits, cursor overrides, and the next responder change local state immediately. invalidate() queues coalesced deferred work; repaint() asks to redraw a local dirty region.
A widget is a Responder. Unhandled input continues to the next responder, with mouse and touch coordinates converted locally.
Peers are non-hierarchical, bidirectional associations. They do not establish ownership, parenting, or screen connection. A widget can also provide a Cursor while it is under the pointer; clearing the override restores the default cursor.
A View is a Widget that attaches controls to a PaintSurface. It establishes the surface and coordinate mapping for those controls and forwards their repaint, layout, visibility, geometry, and activation requests. Form and Control are the usual view implementations. Screen and WindowManager are widgets but not views.
The view owns its surface adapter, not the PaintSurface assigned through setSurface(). The adapter presents the view's local size, scaling, and offset within the borrowed surface. When a control is initialized, the view gives it that surface with an offset derived from the control's position; release clears the control's surface. Form and Control extend this mechanism to their content and child controls.
toControl() and fromControl() convert between view-local and attached control coordinates. Their default mapping uses the control position. View implementations align child move and resize requests to the active scaling before sending the corresponding Forms event. Override the protected mapping and forwarding hooks only when a custom host has a different coordinate system, surface arrangement, or request policy.
View is an implementation boundary rather than the usual application base class. Derive visible application content from Control. Derive from View only when a custom content host needs different paint-surface or coordinate behavior. A custom view must arrange a complete attach, initialize, release, and detach lifecycle for every hosted control and must not retain a surface supplied by another view beyond that lifecycle.
A Form attaches one content Control and is the point where the content hierarchy joins a window or another form host. It is the layout root of that tree. It delegates measuring and layout to that content, forwards view requests, and does not own the control. Use setContent() to replace the content; keep the control alive until it is detached. A form is often a Window, but other hosts can derive from Form.
Attachment connects the complete tree to the screen, paint surface, responder chain, and form. Detachment reverses those links. Replacing the content detaches the old root before attaching the new one. The content root may itself contain an arbitrary control tree. These links are non-owning, so destruction order remains the caller's responsibility.
relayout() queues a layout event only while the form is connected. Pending layout requests are combined before the form measures its content and then assigns it the available bounds. Controls request layout from their parent rather than laying out an enclosing form directly. Custom form hosts override the protected measuring and layout hooks when their content needs a different allocation rule.
The form is the focus root for its content tree. It delivers focus changes as FocusEvent objects and handles Tab traversal, shortcuts, and Alt mnemonics before ordinary keyboard input reaches the focused control.
A Control is the building block of a Forms user interface. Derive from it to implement custom content. It can be the content of a Form or the child of another view, and it can attach child controls of its own. Attachment is non-owning: a parent stores control pointers but does not destroy its children.
A control's parent is a View, and its direct children are controls. add() first detaches a child from any old parent; remove() detaches it without destroying it. When the tree joins a connected form, connection, form association, responder chain, scaling, and paint surface propagate through all descendants. Removing a control reverses those runtime relationships. Keep every attached control alive until it has been removed or its parent is gone.
The parent paints children in stacking order and hit-tests them from front to back. raise() moves a direct child to the front. Controls forward repaint and relayout requests to their parent, which converts the requested local region to its own coordinates. A control measures under the supplied SizePolicy and caches the preferred size until content, policy, limits, or scaling invalidate it. Its layout hook assigns geometry to its direct children.
The default measurement returns an empty size and the default layout does not assign child geometry. A derived container measures its content in onMeasure() and places each child with move() and resize() in onLayout(). Call relayout() after a content change that affects size or geometry and invalidate() after a visual-only change.
Once associated with a form, a control may take part in focus traversal, action keys, shortcuts, and mnemonics. Override onMeasure(), onLayout(), onPaint(), and the protected input hooks as required; use state-changing public APIs so changes travel through the parent hierarchy.
A Screen is the runtime root for the top-level windows on one display. Application provides the primary screen and connects it to the platform. The screen tracks attached Window objects, routes input to the widget below the pointer, and requests drawing of changed regions. Applications normally use the screen through Application::screen() rather than creating one directly.
A screen provides the top-level WindowManager for its display. Showing an unparented Window attaches it through that manager. The manager and screen use non-owning relationships to the windows, so the application must keep every attached window alive and detach it before destruction. Use a Workspace when an application needs a window manager within a control instead of another platform display.
Screen coordinates are global Forms coordinates. The screen hit-tests windows and their content to choose the input target. When that target changes, setPointer() delivers a leave event to the old widget before an enter event is delivered to the new one. Pointer capture and transient popups can override normal hit-test routing through Application.
Repaint requests from the hierarchy reach the screen as dirty rectangles. The screen combines pending rectangles and schedules one paint pass, then passes the resulting update to the platform implementation. Screen is a backend extension point; applications should use its window manager and should not call its protected event and platform hooks.
Forms presents windows in two places: on a platform display, and inside a workspace. In both cases a Window is a Form attached to a WindowManager. The manager does not own the window. This is the same non-owning host pattern as Application with a Screen and Form with a content Control.
A top-level window uses the primary Screen and a platform frame. A Workspace nests that windowing model inside a Control: ordinary background content plus a stack of windows in the control's bounds. It is not a second screen. A Popup is still a window, used for a short interaction tied to an opener, not a third model.
A window joins a manager when it is shown, unless the application already selected a parent manager. Showing connects it to the screen and frame. Closing detaches it and does not destroy it. Geometry, visibility, and activation are requests confirmed by events, as with other widgets.
A Window is a Form shown by a WindowManager. Show it after attaching its content with setContent(). The window translates form requests for geometry, visibility, activation, repainting, and input into operations on the platform window. Its content remains owned by the caller and must outlive its attachment.
Construct a window, configure its content, title, icon, geometry, and presentation state, and then call show(). An unparented window is added to the primary screen's window manager when it is shown. Use setParent() only to select another manager, for example the manager of a workspace. The manager supplies a frame that connects the window to the screen and applies pending configuration when that connection is made.
move() and resize() request normal-window geometry. autoSize() measures the content with a SizePolicy, while autoCenter() asks the current frame to choose a position in its manager's available area. A manual move or resize returns the window to the normal state and can end the corresponding automatic behavior. setAbove() controls stacking within the current manager; it is not an operating-system-wide always-on-top guarantee.
WindowType selects the normal or borderless frame. WindowState is normal, minimized, or maximized. Visibility, enabled state, and activation are requested from the frame and confirmed by ShowEvent, MoveEvent, ResizeEvent, WindowStateEvent, ActivateEvent, and CloseEvent. A window accepts pointer, touch, scroll, and keyboard input only while it is visible and enabled. close() requests a close transition. Once processed, the window is hidden, detached, marked closed, and emits closed(); it is not destroyed. showModal() runs a nested event loop and temporarily disables the other top-level windows, so reserve it for intentionally synchronous dialogs.
A WindowManager is a Widget that attaches Window objects it does not own. Screen provides the manager for top-level platform windows, while Workspace uses a derived manager to present windows in its own bounds. Applications select a manager through a screen or workspace and attach windows through Window; they do not derive from this class.
Backend implementations derive from WindowManager and implement the attachment and lifecycle hooks that create, initialize, release, and detach each internal WindowFrame. Window controls that lifecycle: it calls onAttach(), initializes the frame and then calls onInit(). During detachment it releases the frame, calls onRelease() and onDetach(), and finally deletes the frame. The returned frame is therefore owned by the window; a manager must not delete it.
The default onInit() and onRelease() implementations connect and disconnect the frame and its window from the current screen. Derived managers must call the corresponding base implementation. The protected hooks are for backend implementations and are not an application customization API.
A Workspace nests the windowing model inside a Control. It has ordinary background content and an embedded window stack. addWindow() attaches a window it does not own. Visible windows paint in stack order. The workspace hit-tests the frontmost window first, activates a window on a pointer or touch press, and sends keyboard and scroll input to its active window.
Visual content updates in one cycle: invalidate, layouting, then painting. Call Widget::invalidate() after a visual-only change. It coalesces and later calls onInvalidate() so the widget can rebuild derived visual state. Control::onInvalidate() also requests relayout. Call Control::relayout() after a content or policy change that affects size or geometry.
Layouting has two passes, not two pipelines. Measure is inside-out: Control::measure() / onMeasure() compute a preferred size under a SizePolicy and cache it until content, policy, limits, or scaling invalidate it. Layout is outside-in: onLayout() assigns child geometry in the available rectangle with move() and resize(). A layout event then requests a repaint.
Painting is not synchronous. Widget::repaint() queues a local dirty region. A PaintEvent carries that rectangle down visible content after the window frame makes a PaintSurface available. A View adapts that surface. onPaint() draws in local coordinates.
PaintSurface, PaintContext, and Painter are one mechanism. A PaintSurface is the Forms render target; a view borrows it and does not own it. A PaintContext is the active session on that surface and the argument to onPaint(). A Painter binds to the surface or the context and issues drawing commands. It extends Gfx with pixmap drawing. A Pixmap is an off-screen PaintSurface used as a back buffer or as cached image content. Use Drawing Graphics and Text for pens, brushes, text, and paths.
A styled control does not invent a second cycle. onInvalidate() binds the Styler. Layouting calls named measure then layout methods. Painting calls named render methods. Styles and Renderers describes that implementation.
GraphicsBackend and IPixmapImpl are platform integration. Ordinary applications do not call them.
The following example implements the three update hooks on a custom control. onMeasure() returns the preferred size for the control. onLayout() places a child in the rectangle assigned by the parent. onPaint() constructs a Painter on the supplied PaintContext and fills the dirty rectangle. The painter does not own the context or the surface.
Platform input enters the Application event loop and is targeted into the connected visual hierarchy. Pointer, touch, and scroll input are directed by hit testing, while keyboard input is directed by focus. The Screen or a workspace finds the widget below the pointer. These are two targeting models, not two event loops. Every Widget is a Responder. A responder handles an event by returning true; returning false continues delivery through the responder chain. Mouse and touch positions are converted to each responder's local coordinates before its handler runs.
Hit testing selects the frontmost eligible widget below the pointer. Widget::setCapture() temporarily overrides hit testing and directs pointer input to the capturing widget until it releases capture. Forms sends a LeaveEvent before an EnterEvent when the pointer target changes. A MouseEvent reports movement and button state, a TouchEvent reports an individual touch and pressure, and a ScrollEvent reports the scroll axis and delta.
A Form is the focus root for its content tree. A focused control receives key input, subject to the form's interaction rules: an active control is considered first, then registered shortcuts, Alt mnemonics, and Tab focus traversal, followed by the focused control and responder chain. A control can take part in that focus set; pointer and touch presses on a control typically request focus. A KeyEvent carries a Key, press or release action, and optional Unicode text. Controls register their focus policy, action key, shortcut, and mnemonic through their public APIs.
InputMethod is the extension point for platform or custom text entry. It begins a session for a control, retains only a non-owning receiver, and sends KeyEvent objects to that receiver. Ordinary controls use the method installed on the application, which always provides a default method; derive an input method only to implement a text-entry interface.
A Responder is the unit of input delivery. Widget is the usual responder. Handle an event by returning true; return false to continue to the next responder. Mouse and touch positions are converted to the current responder's local coordinates before the handler runs.
Applications derive visual content from Control, not from Responder. A custom responder must implement onNextResponder(), onToGlobal(), and onFromGlobal() and must not create a cycle in the chain.
An input method begins and finishes a session for a Control, may provide an active window, and sends KeyEvent objects to its current receiver. The caller does not own the receiver. Derive from this class only to implement a text-entry interface; ordinary controls use the method installed on Application.
A layout control derives the geometry of its children from a placement policy. It remains in the visual hierarchy: a form's content is often a layout, and a layout can nest other layouts. Attachment is non-owning. addItem() records the layout-specific placement and attaches the child; removeItem() detaches it. Control::add() attaches without that policy. Keep each child alive until it is detached.
Layouting follows the cycle in Layouting and Painting. Measure runs inside-out under a SizePolicy. Layout runs outside-in and assigns child geometry with move() and resize(). A layout implements those passes for its policy. Control::padding() is the inner inset of the layout. A child's Control::margin() is the outer gap around that child. Invisible children are skipped.
Choose a layout by the arrangement it produces.
A flow places children on one axis. Docking consumes edges in add order and gives leftover space to Fill. A grid uses uniform cells and wrapping. A table assigns children to row and column tracks that can prefer, fix, or fill size. A stack shows one child at a time. A canvas uses stored positions. A scroll layout offsets children when content exceeds the viewport; it is not a scroll view with bars.
Layout attributes refine placement without being layout controls. Spacing is the four-sided inset used as a control's margin or padding. Direction is the axis of a flow. Alignment places content in a rectangle. Adjustment aligns along one axis, typically a line of text.
Each child keeps the position and size given to addItem(). The canvas adds its padding to those positions. The preferred size is the bounding box of the stored rectangles plus padding.
Children are placed in add order. Left and Right take preferred width and the remaining height. Top and Bottom take preferred height and the remaining width. Each dock shrinks the leftover rectangle. Fill children share that leftover.
Direction::Left and Direction::Right place children on a horizontal axis. Direction::Top and Direction::Bottom place them on a vertical axis. Each child keeps its preferred size on the flow axis and stretches on the cross axis. setCenter() packs the group in the remaining space. setReverse() visits children in reverse order along the same direction.
Cell size is the largest preferred child size. Vertical fills columns then wraps to the next row. Horizontal fills rows then wraps to the next column. The wrap count is the number of cells on that axis; 0 fits as many as the available size allows. Items keep their preferred size and are centered in their cells.
Added children are hidden until setCurrent() selects an index. The current child fills the padded bounds minus its margin. The preferred size is the largest preferred child size. current() is NoIndex until a child is selected.
addItem() places a child in a cell. setColumn() and setRow() set each track to Preferred, Fixed, or Fill. Preferred tracks use the largest preferred child on that track. Fixed tracks use the given size. Fill tracks share leftover space after preferred and fixed tracks. Measure resolves preferred and fixed tracks first so Fill cells and nested layouts see the resulting track size. Layout assigns each visible cell the track rectangle minus its margin.
Children are measured with setContentMode() and placed at their preferred size. scrollX() and scrollY() move the content within the layout bounds. Offsets are clamped to the scrollable range. enableScrolling() selects which axes accept scroll input. This layout does not draw scroll bars.
Use a button when the user should run a command or turn a choice on or off. A completed gesture runs a press, release, and cancel protocol and ends in clicked().
Button is the shared interaction control. It is not a look. It holds the caption and hover, and is the type PushButton and CheckBox share.
PushButton is a classic button: a caption on a clickable face, sometimes with an icon beside the text. Clicking it runs a command. As a toggle it stays visually pressed to show that a mode is on.
CheckBox is a clickable choice with a caption beside a checkable area. The area shows whether the choice is on or off.
Appearance follows the application style. On invalidate the control binds a family renderer through a styler. Widget-local brushes, pens, colors, fonts, and setRenderer() overlay that style without replacing it.
A Button holds a caption and hover, and runs a press, release, and cancel protocol that ends in clicked(). It is a base class for all button-like controls. Derive PushButton for a command or CheckBox for a boolean choice.
setText() sets the caption and records a mnemonic from an ampersand. Pointer, touch, mnemonic, shortcut, and the action key all run the protocol. A release outside the widget, or a scroll during a press, cancels.
Connect clicked() to handle a completed gesture. Override onPressed(), onReleased(), and onCanceled() in a subclass to implement the command or choice.
A PushButton is a Button that presents a command. A completed click emits clicked() and, unless it is a toggle, returns to the unpressed look. setToggle() keeps the pressed state after release so the button can show an on/off command such as Bold.
setIcon() places a picture next to the caption. setLayout() chooses the Direction of icon and text. setFlat() drops the framed face.
The button owns a ButtonStyler. On invalidate it calls Styler::bind(). Appearance getters and setters read and write widget-local options. setRenderer() assigns a ButtonRenderer until it is cleared. Measure, layout, and paint call typed methods on the styler. ButtonState is the snapshot passed to paint layers.
A CheckBox is a Button whose value is Unspecified, Checked, or Unchecked. setState() assigns the value. isChecked() is true only for Checked. A completed click toggles Checked and Unchecked, then emits clicked(). Unspecified becomes Checked on that click.
The box owns a CheckBoxStyler. On invalidate it calls Styler::bind(). Appearance getters and setters overlay the application style. setRenderer() assigns a CheckBoxRenderer. CheckBoxState is the snapshot passed to paint layers.
Use a display when the user should read information without editing it.
Label is a caption or an icon in a layout. It has no chrome by default, so it reads as text or a picture on the parent surface. Alignment places it in its bounds.
Panel is a surface that groups other controls. An optional fill and frame set it apart from the parent, like a banner or a well.
ProgressBar shows how far a task has come. A track fills as the value moves through its range.
Label and Panel share the panel appearance family. ProgressBar has its own. On invalidate the control binds a family renderer through a styler. Widget-local options overlay the application style without replacing it.
A Label presents a caption or a picture. It does not edit text. setText() sets the caption and clears any icon. setIcon() shows an icon instead of text. setAlignment() places the content in the label bounds.
setBackground() and setContour() enable a fill or frame. setBackground(false) and setFrame(false) turn them off without replacing the style. Whether the fill or frame is on is a widget setting, not a style option.
The label owns a PanelStyler and shares the panel appearance family with Panel. On invalidate it binds that styler. Measure, layout, and paint call typed methods on it. TextBlock lays out wrapped text. Icon supplies pictures. Neither is a widget.
A Panel presents one content control with an optional background, frame, and icon. setContent() attaches the child without taking ownership. Keep the child alive until it is detached. content() returns the attached control, or 0.
setBackground() and setContour() enable a fill or frame. setBackground(false) and setFrame(false) turn them off. Whether the fill or frame is on is a widget setting, not a style option. setIcon() paints a picture aligned with the given Alignment.
The panel owns a PanelStyler and shares that appearance family with Label. On invalidate it binds the styler. Measure, layout, and paint call typed methods on it. The content control is laid out in the inner rectangle after the frame.
A ProgressBar maps an integer value between minimum() and maximum() to a ratio from 0 to 1. setRange() sets the bounds. setValue() clamps the value and emits valueChanged(). progress() returns the ratio used to fill the track. reset() returns to the minimum.
The bar owns a ProgressBarStyler. On invalidate it binds that styler. Appearance getters and setters overlay the application style. setRenderer() assigns a ProgressBarRenderer. progressBarState() is the snapshot passed to paint layers.
Use an editor or selector when the user should enter or pick a value.
LineEdit is a single-line text field. An entry shows the text, a caret, and an optional selection. Placeholder text can appear when the field is empty.
SpinBox is an integer in a range. An entry shows the number. Up and down controls step it. A style may merge those controls with the entry or place them beside it.
ComboBox is a value chosen from a list, or typed when the box is editable. An entry shows the current text. A button opens a popup list.
Slider is a value chosen along a range by dragging. A track shows the range. A handle shows the current value.
LineEditor is not a control. It stores the text, caret, and scroll of one line. LineEdit, SpinBox, and ComboBox use it.
Appearance follows the application style. On invalidate the control binds a family renderer through a styler. Widget-local brushes, pens, colors, fonts, and setRenderer() overlay that style without replacing it.
A LineEdit presents one line of text for the user to enter or change. setText() assigns the string and emits textEdited(). setPlaceholderText() is shown while the field is empty and does not have focus. setEchoMode() chooses whether the entered text, a mask character, or nothing is shown. text() remains the entered string.
setEditable(false) keeps the text from being changed and does not take focus. setAccepted(true) completes an edit: Return emits returnPressed(), and losing focus after a change emits editingFinished().
The field owns a LineEditStyler. On invalidate it calls Styler::bind(). Appearance getters and setters overlay the application style. setRenderer() assigns a LineEditRenderer until it is cleared. Measure, layout, and paint call typed methods on the styler. lineEditState() is the snapshot passed to paint layers.
A LineEditor stores the text, caret, and scroll. It is not a widget.
A SpinBox presents an integer between minimum() and maximum(). setRange() sets the bounds. setValue() clamps the value, updates the entry, and emits valueEdited(). Up and down controls step the value. setEditable(false) keeps the number from being typed and does not take focus. setAccepted(true) completes an edit: Return emits returnPressed(), and losing focus after a change emits editingFinished().
Override toText() and toValue() to change how the integer is shown and parsed. onInput() accepts partial text while the user types. onStep() applies an up or down step.
The box owns a SpinBoxStyler. On invalidate it calls Styler::bind(). Appearance getters and setters overlay the application style. setRenderer() assigns a SpinBoxRenderer until it is cleared. Measure, layout, and paint call typed methods on the styler.
A LineEditor stores the entry text and caret. It is not a widget. SpinBoxButton is an integrated subpart, not a control the application places.
A ComboBox presents a current text in an entry and a button that opens a popup list. Use addItem() to add a caller-owned ListBoxItem. Choosing an item emits the selected() signal and sets the text. Use setText() to assign the string and emit textChanged(). User typing emits textEdited() when the box is editable.
Calling setEditable(false) keeps the text from being typed, allowing the user to still pick from the list. Calling setAccepted(true) completes an edit: pressing Return emits returnPressed(), and losing focus after a change emits editingFinished(). Calling setMaxHeight() limits the popup height, and calling setScrollBars() shows scroll bars on the list.
The box owns a ComboBoxStyler. On invalidate, it calls Styler::bind(). Appearance getters and setters overlay the application style. Use setRenderer() to assign a ComboBoxRenderer until it is cleared. Measure, layout, and paint operations call typed methods on the styler.
An internal LineEditor stores the entry text and caret, but is not a widget. The popup list is a Popup that hosts a ListBox.
A Slider maps an integer between minimum() and maximum() to a handle on a track. setRange() sets the bounds. setPosition() clamps the value and emits positionChanged() when it changes. Dragging the handle updates the position.
The slider owns a SliderStyler. On invalidate it calls Styler::bind(). Appearance getters and setters overlay the application style. setRenderer() assigns a SliderRenderer until it is cleared. Measure, layout, and paint call typed methods on the styler. sliderState() is the snapshot passed to paint layers.
Track and handle are borrowable renderer primitives. The slider still orchestrates measure, layout, and paint through the styler.
Use a collection when the user should browse a list, switch among pages, or scroll content that does not fit.
ListBox is a scrollable list of caller-owned items. Items show text, sometimes with an icon. A selected item is highlighted. Optional scroll bars appear when the list does not fit.
ListBoxItem is an item in a list. The application creates it and adds it to a ListBox. ComboBox uses the same type. An item shows text and an optional icon. Selection highlights the item.
TabView presents several pages. A row of tabs shows the titles. One tab is current. Choosing a tab shows that page.
ScrollBar is a position in a range. A track shows the range. A handle shows the current position. Decrease and increase controls step it. A style may place those controls at the ends of the track.
ScrollView is a viewport over larger content. Optional scroll bars move the content.
TabViewItem is not a control. TabView creates tab labels from addTab titles. The view owns the titles, the current tab, tab geometry, and tab input. There is no tab-bar widget. ListBoxLayout is an integrated subpart. Applications do not construct it.
List and tab chrome are separate from repeated-element visuals. ListBoxRenderer paints the list. ListItemRenderer paints an item. TabViewRenderer paints the view chrome and each tab. Item and tab methods take rectangles, text, icons, and item or tab state. They do not take the item widget, a model object, or the tab collection.
A collection that only needs pane or frame chrome can use the panel appearance family. A dedicated container renderer is for chrome that differs from a panel.
ScrollView has no appearance family of its own. Optional bars are ScrollBar objects. Offset of oversized content is a ScrollLayout.
Appearance follows the application style. On invalidate the control binds a family renderer through a styler. Widget-local brushes, pens, colors, fonts, and setRenderer() overlay that style without replacing it.
A ListBox presents caller-owned ListBoxItem objects. Use addItem() to add an item. Keep each item alive until removeItem() detaches it. A completed click on a selectable item toggles ListBoxItem::isSelected(). selected() emits that item. selectedItems() returns the items that are currently selected. Calling setScrollBars() shows scroll bars when the list does not fit.
The list owns a ListBoxStyler. On invalidate it calls Styler::bind(). Appearance getters and setters overlay the application style. Use setRenderer() to assign a ListBoxRenderer until it is cleared. Measure, layout, and paint operations call typed methods on the styler.
Items are hosted in a ScrollView.
A TabView presents several caller-owned content controls, one at a time. Use addTab() to add a control with a title. Keep each control alive until removeTab() detaches it. setCurrent() shows that page. Choosing a tab shows its page. setText() changes a tab title. The view owns the titles, the current tab, tab geometry, and tab input. There is no separate tab-bar widget.
The view owns a TabViewStyler. On invalidate it calls Styler::bind(). Appearance getters and setters overlay the application style. Use setRenderer() to assign a TabViewRenderer until it is cleared. Measure, layout, and paint operations call typed methods on the styler.
Pages live in a StackLayout.
A ScrollBar maps a value between minimumPosition() and maximumPosition() to a handle on a track. setRange() sets the bounds. setPosition() clamps the value. scroll() clamps the value and emits changed(). Decrease and increase controls step by setStepping(). The constructor sets Orientation.
The bar owns a ScrollBarStyler. On invalidate it calls Styler::bind(). Appearance getters and setters overlay the application style. setRenderer() assigns a ScrollBarRenderer until it is cleared. Measure, layout, and paint call typed methods on the styler. scrollBarState() is the snapshot passed to paint layers.
Track, handle, and decrease and increase controls are renderer primitives. The bar still orchestrates measure, layout, and paint through the styler.
A ScrollView presents one caller-owned content control that may be larger than the view. Use setContent() to attach the control. Keep it alive until another call replaces it. Calling setScrollBars() shows horizontal and vertical ScrollBar objects when the content does not fit. scrollX() and scrollY() move the content. setContentMode() chooses how the content is measured.
The view has no family renderer. It hosts a ScrollLayout and optional ScrollBar objects. Offset of oversized content is a ScrollLayout.
Use menus to group related commands when the user chooses an action from a compact list. A MenuBar is a horizontal strip of top-level entries. Choosing an entry opens its associated Menu. A Menu is a transient popup containing a vertical list of command items and submenu items.
MenuItem represents a command that the user can choose. It shows text, an optional icon, and an optional separator before the item. MenuSubItem and MenuMenuItem represent entries that open a nested Menu. A MenuBarItem is a top-level submenu entry for a MenuBar.
Applications create menu items and nested menus, add the items to a Menu or MenuBar, and keep them alive until they are removed or disassociated. MenuSubItem and MenuMenuItem associate a nested Menu without taking ownership. Activating a command item emits its MenuItemBase::triggered() signal. Popup provides the transient window behavior used by Menu and can also present other short interactions.
Menu containers and items use the application style for their default appearance. Menu, MenuBar, and MenuItemBase provide local brush, pen, color, and font overrides when an application needs a specific appearance.
Use a Menu for commands chosen from a vertical popup list, including a context menu or the menu opened by a MenuBarItem. Add MenuItem objects for commands and MenuSubItem objects for nested menus. The menu does not own its items; remove an item before destroying it.
A menu derives its default background and contour from the application style during invalidation. setBackground() and setContour() supply local overrides for this menu. A nested menu is opened and positioned by its associated submenu item.
Menu and MenuBar use this interface to register submenu items, track their parent item, and coordinate opening, closing, and cancellation. Applications use a concrete menu container instead of deriving from this type.
Use a MenuBar to present a stable row of top-level command categories. Add MenuBarItem objects and associate each item with a Menu. The bar opens the selected menu below its item and closes the previously active menu. It does not own its items; remove an item before destroying it.
A menu bar derives its default background and contour from the application style during invalidation. setBackground() and setContour() supply local overrides for this menu bar.
A menu item displays text, an optional icon, an optional separator, and a shortcut when one is registered. A pointer or touch release over the item, or a matching shortcut, emits triggered(). A highlighted item is the item currently under the pointer.
The item derives its default drawing attributes from the application style. Its background, contour, text color, and font setters provide local overrides. MenuItem, MenuSubItem, MenuMenuItem, and MenuBarItem use this common behavior. Use a MenuItem for a command that does not open a nested menu. Add it to a Menu and connect triggered() to perform the command. Its text, icon, separator, and local appearance overrides are inherited from MenuItemBase. MenuMenuItem provides the same nested-menu association and keyboard delegation as MenuSubItem while using the base item presentation. setMenu() associates a nested menu without taking ownership.
Use a MenuSubItem when choosing an entry should reveal another Menu. setMenu() associates a nested menu without taking ownership. Keep that menu alive while the item uses it. The containing Menu or MenuBar sets the parent menu while the item is attached.
Use a MenuBarItem to give a MenuBar a labeled top-level category. It inherits the nested-menu association and opening behavior of MenuSubItem. Add it to a MenuBar after assigning its menu.
A Popup is a Window shown for a short interaction such as a menu. setAnchor() relates it to the widget that opened it for popup input handling. The anchor does not position or size the popup. Move, size, raise, and show it explicitly.
Controls use Icon and TextBlock to supply visual content. These types are not widgets and do not appear in the visual hierarchy. Derive custom visual content from Control, not from an icon or text block.
An Icon associates one picture with images at one or more logical sizes. A control requests the image that fits the area it is about to display. Add images directly for the usual case. Implement IconProvider only when images must come from another source.
A TextBlock converts a string into positioned TextLine objects. Controls such as Label use it to lay out text. A custom control configures the available width and alignment, calls TextBlock::layout(), and uses the resulting lines when it measures or paints wrapped text.
An Icon is not a Widget. It supplies images to controls, which request an image for a layout area with getImage(). Label, PushButton, Panel, and list items accept an Icon through setIcon().
A default icon uses a built-in provider. Icon(IconProvider&) attaches a caller-owned provider that must outlive the icon. Copies share the same implementation until addImage() or clear() changes one copy.
addImage() registers an image or file path at a logical size. Passing only an image uses its pixel size as the key. The logical size may differ from the pixel size. Images registered by path load as PNG on first use through Application::loadImage(). A missing path produces an empty image.
getImage() returns the registered image for a requested area. The built-in provider selects the largest image that fits entirely in that area. An empty icon produces an empty image. minimumSize() and maximumSize() return the smallest and largest registered keys.
Derive from IconProvider only to provide a custom image source. Ordinary code uses the built-in provider created by a default Icon. A custom provider implements every pure virtual function. Its getImage() function selects and, when needed, loads an image for the requested size.
An Icon constructed with a provider does not own it. Keep the provider alive while an icon uses it. Destroying a provider detaches its remaining icons. Calling Icon::getImage() on a detached icon is invalid.
A TextBlock is not a Widget and does not own a painter or control. Label and LineEditor use it. A custom control sets the maximum width, adjustment, and line spacing, calls layout(), then iterates the lines to measure or paint the text.
layout() uses the painter's font metrics to wrap a string into TextLine objects. It replaces the previous lines and updates the block size. Wrapping is word-based: whitespace separates words, and explicit newlines are not hard breaks. Adjustment::Left, Adjustment::Right, and Adjustment::Center align each line within the maximum width. Adjustment::Justify is treated as left. The default adjustment is Center.
position() is the block origin and each line position is relative to it. size() returns the laid-out width and height. Line spacing adds extra space between consecutive lines.
A Forms application has a Style and StyleOptions. The style is the look of the controls. The options are shared colors, pens, brushes, and the font. Application owns both and starts with PlatinumStyle.
Use Application::setStyle() or Application::setStyleOptions() to change the global theme. Those methods reset shared renderer facets and invalidate widgets. Widgets rebuild in onInvalidate(). A widget can overlay local options or assign a custom renderer without replacing the style.
StyleOptions stores tokens that every style can honor: colors, pens, brushes, and the font. Look-specific metrics stay in the derived style or renderer. Default construction is an empty overlay. StyleOptions::defaults() fills the built-in tokens. The application options are const. Whether a fill or frame is on is a widget setting, not a style option. Use Panel::setBackground(false) to turn a fill off.
A Renderer implements layouting and painting for one control family. Each method names the layer it measures, lays out, or paints, such as ButtonRenderer::measureFrame() or ButtonRenderer::renderChrome(). There is no unqualified measure(), layout(), or render(). Renderer methods take prepared rectangles, sizes, scalars, enums, typed visual state, and text or pixmap values. They do not take the widget, a model object, a collection, or a temporary layout object. A small parameter struct is appropriate only when the same prepared group is reused across methods.
Named measure methods run inside-out by layer: content, then frame. Named layout methods run outside-in: the frame method returns the inner content rectangle, then content partitions it. Named render methods paint those prepared rectangles. Only the full-control background or chrome method takes the widget bounds. The widget owns geometry and calls the named methods in order. The renderer does not store or mutate widget geometry. Layouting and Painting describes the cycle.
A layer method may paint the layer as a whole. Derived renderers that use a platform theme API override that layer method. Derived renderers that draw parts override the part methods. The base layer method paints the parts in order.
A part is a public primitive when another control can reuse it without the original widget's layout or hit-testing. Parts that a style may merge with surrounding chrome are integrated subparts. SliderRenderer track and handle are public primitives. SpinBoxRenderer step controls are integrated subparts.
Typed visual state matches the layer. Container state carries enabled, focused, and container highlight. Item, tab, or cell state carries selected, current, checked, or pressed. Visual state is not passed as mutable brush, pen, or font out-parameters.
Family renderers provide a text painter whose font and text color already follow the style. A widget does not construct a Painter for that themed text. Style::Facet::onReset() is the point at which a renderer reads StyleOptions and stores drawing state. Render methods use that prepared state plus the visual-state snapshot.
renderBackground() paints the whole control. An inner fill uses a layer name such as entry background. PlatinumStyle backgrounds are empty unless a local BackgroundOption is set.
Styler binds a control to the current style. Call Styler::bind() from onInvalidate() after the base implementation. Layouting and painting call typed methods on the derived styler. Derive a Style to install a different look. Derive a Renderer to draw a control family. Derive a Styler only when adding a new control family.
A Style stores one renderer facet per dynamic type. Use set() to register or replace a facet. Use get() to retrieve it. Use reset() to apply StyleOptions to every registered facet. generation() changes when the set of facets changes.
Facets are reference counted. set() takes a reference. Pass a facet with a reference count of 0. Use Renderer::create() to allocate such an instance.
Use Application::setStyle() to install a complete style. The application starts with PlatinumStyle. Derive Renderer, or another Facet, to add drawing for a control family.
Shared facets receive global options only through reset(). Look- specific metrics stay in the derived renderer, not on Style.
A StyleOptions object contains named appearance options, such as the application background, text colors, accent color, contour pen, and default font. A Style uses these values when it prepares the renderers that paint controls.
Application owns the global style options. It creates them from defaults() and uses them with the default PlatinumStyle. To change the application theme, create an options object, change the values that the application requires, and pass it to Application::setStyleOptions(). The application resets its renderers and invalidates its widgets so that the new appearance is applied.
The default constructor creates an empty options object. An empty object is useful as a local overlay for a control. A styler binds this overlay to the application options. A local option overrides the option of the same type in the application options, while an option that is not present locally continues to use the application value. Application owns the live global instance, constructed from defaults(). Application::styleOptions() returns that object as const.
findLocal() searches only this object and returns 0 when the option is not stored locally. find() searches this object and then its bound parent. After bind(), get() returns a local or inherited token and throws std::logic_error when the requested option is not available. set() adds or replaces a local option, and reset() removes a local option.
StyleOptions stores tokens that every style can honor. Look-specific metrics stay in the derived style or renderer.
FontOption supports complete and partial font overrides. A local option can change only the font size, weight, or slant while inheriting the font family and all remaining attributes from the application font.
generation() changes when local options or their binding change. Stylers use the generation to detect that a renderer must prepare its drawing state again.
A Styler connects a control family to the current Style and StyleOptions. Widgets own a derived styler. Applications do not construct Styler. Derive from it only to add a styler for a new control family.
Use bind() to associate the styler with a style and the application options. It returns true when the effective renderer or the effective options changed. Call bind() from onInvalidate() after the base implementation.
When the overlay contains no local options, bind() uses the shared renderer from the style and does not reset it. When the overlay contains local options, it uses a private clone and resets that clone with the overlay. A renderer assigned through the derived setRenderer() remains until it is cleared. Passing a null renderer falls back to the current style on the next bind().
Styler does not provide a public renderer accessor. Measure, layout, and paint use typed methods on the derived styler.
A Renderer is a Style::Facet that can clone itself. Shared instances remain in Style. Use reset() to apply application options. Use create() to allocate a new instance with a reference count of 0. FacetPtr and Style::set() take their own references.
Named measure, layout, and render methods belong on derived renderer types. Use Facet::onReset() to read style options and store drawing state for those methods.
A renderer may store a Painter. That painter keeps its brush, pen, font, clip, and composition after begin(). onReset() assigns the default attributes. A render method that changes them assigns the defaults again before it returns.
Derive a renderer for a control family and register it on a Style.