arsd.minigui

Public Imports

arsd.simpledisplay
public import arsd.simpledisplay;
Undocumented in source.

Members

Aliases

DefaultTheme
alias DefaultTheme = DefaultLightTheme

This is your entry point to create your own visual theme for custom widgets.

EventHandler
alias EventHandler = void delegate(Widget handlerAttachedTo, Event event)
LabeledLineEdit
alias LabeledLineEdit = Labeled!LineEdit

A line edit box with an associated label.

LabeledPasswordEdit
alias LabeledPasswordEdit = Labeled!PasswordEdit

A labeled password edit.

Rectangle
alias Rectangle = arsd.color.Rectangle

Convenience import to override the Windows GDI Rectangle function (you can still use it through fully-qualified imports)

scriptable
alias scriptable = arsd_jsvar_compatible

Methods marked with this are available from scripts if added to the arsd.script engine.

Classes

Action
class Action

An Action represents some kind of user action they can trigger through menu options, toolbars, hotkeys, and similar mechanisms. The text label, icon, and handlers are centrally held here instead of repeated in each UI element.

ArrowButton
class ArrowButton
AutomaticDialog
class AutomaticDialog(T)

This is the implementation for dialog. None of its details are guaranteed stable and may change at any time; the stable interface is just the dialog function at this time.

BaseVisualTheme
class BaseVisualTheme

Interface to a custom visual theme which is able to access and use style hint properties, draw stylistic elements, and even completely override existing class' paint methods (though I'd note that can be a lot harder than it may seem due to the various little details of state you need to reflect visually, so that should be your last result!)

BlurEvent
class BlurEvent
Button
class Button

Creates a push button with unbounded size. When it is clicked, it emits a triggered event.

ChangeEvent
class ChangeEvent(T)

Single-value widgets (that is, ones with a programming interface that just expose a value that the user has control over) should emit this after their value changes.

ChangeEventBase
class ChangeEventBase

You should generally use a ChangeEvent!Type instead of this directly. See ChangeEvent for more information.

CharEvent
class CharEvent

Indicates that a character has been typed by the user. Normally dispatched to the currently focused widget.

Checkbox
class Checkbox

A basic checked or not checked box with an attached label.

ClickEvent
class ClickEvent

Indicates that the user has worked with the mouse over your widget. For available properties, see MouseEventBase.

ClosedEvent
class ClosedEvent

ClosingEvent is fired when a user is attempting to close a window. You can preventDefault to cancel the close.

ClosingEvent
class ClosingEvent

ClosingEvent is fired when a user is attempting to close a window. You can preventDefault to cancel the close.

CollapsableSidebar
class CollapsableSidebar

A collapsable sidebar is a container that shows if its assigned width is greater than its minimum and otherwise shows as a button.

ComboBox
class ComboBox

A combination of free entry with a list below it.

ComboboxBase
class ComboboxBase
CommandButton
class CommandButton

A button with a consistent size, suitable for user commands like OK and CANCEL.

CommandEvent
class CommandEvent

Command Events are used with a widget wants to issue a higher-level, yet loosely coupled command do its parents and other interested listeners, for example, "scroll up".

CommandEventWithArgs
class CommandEventWithArgs(Args...)

A CommandEvent is typically actually an instance of these to hold the strongly-typed arguments.

ContainerWidget
class ContainerWidget

A widget specifically designed to hold other widgets.

CustomLineEdit
class CustomLineEdit

A LineEdit is an editor of a single line of text, comparable to a HTML <input type="text" />.

CustomPasswordEdit
class CustomPasswordEdit

A LineEdit that displays * in place of the actual characters.

CustomTextEdit
class CustomTextEdit

A TextEdit is a multi-line plain text editor, comparable to a HTML <textarea>.

DataControllerWidget
class DataControllerWidget(T)

The data controller widget is created by reflecting over the given data type. You can use ControlledBy as a UDA on a struct or just let it create things automatically.

DetailsView
class DetailsView

A custom widget similar to the HTML5 <details> tag.

Dialog
class Dialog

A dialog is a transient window that intends to get information from the user before being dismissed.

DoubleClickEvent
class DoubleClickEvent

Indicates that the user has worked with the mouse over your widget. For available properties, see MouseEventBase.

DpiChangedEvent
class DpiChangedEvent
DropDownSelection
class DropDownSelection

A drop-down list where the user must select one of the given options. Like <select> in HTML.

EditableTextWidget
class EditableTextWidget

Contains the implementation of text editing and shared basic api. You should construct one of the child classes instead, like TextEdit, LineEdit, or PasswordEdit.

Event
class Event

Represents an event that is currently being processed.

Fieldset
class Fieldset

Creates the fieldset (also known as a group box) with the given label. A fieldset is generally used a container for mutually exclusive Radioboxs.

FileDialogDelegate
class FileDialogDelegate

It is possible to override or customize the file dialog in some cases. These members provide those hooks: you do fileDialogDelegate = new YourSubclassOf_FileDialogDelegate; and you can do your own thing.

FixedPosition
class FixedPosition

FixedPosition is like StaticPosition, but its coordinates are always relative to the viewport, meaning they do not scroll with the parent content.

FocusEvent
class FocusEvent
FocusInEvent
class FocusInEvent
FocusOutEvent
class FocusOutEvent

FocusInEvent is a FocusEvent that propagates, while FocusOutEvent is a BlurEvent that propagates.

FreeEntrySelection
class FreeEntrySelection

A text box with a drop down arrow listing selections. The user can choose from the list, or type their own.

GenericListViewItem
class GenericListViewItem

A scrollable viewer for an array of widgets. The widgets inside a list item can be whatever you want, and you can have any number of total items you want because only the visible widgets need to actually exist and load their data at a time, giving constantly predictable performance.

GenericListViewWidget
class GenericListViewWidget

A scrollable viewer for an array of widgets. The widgets inside a list item can be whatever you want, and you can have any number of total items you want because only the visible widgets need to actually exist and load their data at a time, giving constantly predictable performance.

GridLayout
class GridLayout
HeaderClickedEvent
class HeaderClickedEvent

This is emitted by the TableView when a user clicks on a column header.

HorizontalLayout
class HorizontalLayout

Stacks the widgets horizontally, taking all the available height for each child.

HorizontalRule
class HorizontalRule

Draws a line

HorizontalSlider
class HorizontalSlider
HorizontalSpacer
class HorizontalSpacer

Adds empty space to a layout.

ImageBox
class ImageBox
IndefiniteProgressBar
class IndefiniteProgressBar

Displays an in-progress indicator without known values

InlineBlockLayout
class InlineBlockLayout

Makes all children minimum width and height, placing them down left to right, top to bottom.

KeyDownEvent
class KeyDownEvent

Indicates that the user has pressed a key on the keyboard, or if they've been holding it long enough to repeat (key down events are sent both on the initial press then repeated by the OS on its own time.) For available properties, see KeyEventBase.

KeyEventBase
class KeyEventBase

Contains shared properties for KeyDownEvents and KeyUpEvents.

KeyUpEvent
class KeyUpEvent

Indicates that the user has released a key on the keyboard. For available properties, see KeyEventBase.

Labeled
class Labeled(T)
Layout
class Layout
LineEdit
class LineEdit

A LineEdit is an editor of a single line of text, comparable to a HTML <input type="text" />.

ListWidget
class ListWidget

A list widget contains a list of strings that the user can examine and select.

MainWindow
class MainWindow

A MainWindow is a window that includes turnkey support for a menu bar, tool bar, and status bar automatically positioned around a client area where you put your widgets.

Menu
class Menu
MenuBar
class MenuBar

You can make one of thse yourself but it is generally easer to use MainWindow.setMenuAndToolbarFromAnnotatedCode.

MenuItem
class MenuItem

A MenuItem belongs to a Menu - use Menu.addItem to add one - and calls an Action when it is clicked.

MessageBox
class MessageBox
MouseActivatedWidget
class MouseActivatedWidget

A "mouse activiated widget" is really just an abstract variant of button.

MouseDownEvent
class MouseDownEvent
MouseEnterEvent
class MouseEnterEvent

Indicates that the user has worked with the mouse over your widget. For available properties, see MouseEventBase.

MouseEventBase
class MouseEventBase

Contains shared properties for various mouse events;

MouseLeaveEvent
class MouseLeaveEvent
MouseMoveEvent
class MouseMoveEvent
MouseOutEvent
class MouseOutEvent
MouseOverEvent
class MouseOverEvent

Indicates that the user has worked with the mouse over your widget. For available properties, see MouseEventBase.

MouseTrackingWidget
class MouseTrackingWidget

A mouse tracking widget is one that follows the mouse when dragged inside it.

MouseUpEvent
class MouseUpEvent

Indicates that the user has worked with the mouse over your widget. For available properties, see MouseEventBase.

NestedChildWindowWidget
class NestedChildWindowWidget

A helper to make widgets out of other native windows.

OpenGlWidget
class OpenGlWidget

Nests an opengl capable window inside this window as a widget.

PageWidget
class PageWidget

A page widget is basically a tab widget with hidden tabs. It is also sometimes called a "StackWidget".

PasswordEdit
class PasswordEdit

A LineEdit that displays * in place of the actual characters.

ProgressBar
class ProgressBar

A progress bar with a known endpoint and completion amount

Radiobox
class Radiobox

Creates a radio button with an associated label. These are usually put inside a Fieldset.

ResizeEvent
class ResizeEvent
ScrollEvent
class ScrollEvent
ScrollMessageWidget
class ScrollMessageWidget

A widget that takes your widget, puts scroll bars around it, and sends messages to it when the user scrolls. Unlike ScrollableWidget, it makes no effort to automatically scroll or clip its child widgets - it just sends the messages.

ScrollableContainerWidget
class ScrollableContainerWidget

A widget meant to contain other widgets that may need to scroll.

ScrollableWidget
class ScrollableWidget

A widget that tries (with, at best, limited success) to offer scrolling that is transparent to the inner.

ScrollbarBase
class ScrollbarBase
Slider
class Slider

A slider, also known as a trackbar control, is commonly used in applications like volume controls where you want the user to select a value between a min and a max without needing a specific value or otherwise precise input.

StateChanged
class StateChanged(alias field)

Event fired when an Observeable variable changes. You will want to add an event listener referencing the field like widget.addEventListener((scope StateChanged!(Whatever.field) ev) { });

StaticLayout
class StaticLayout

Bypasses automatic layout for its children, using manual positioning and sizing only. While you need to manually position them, you must ensure they are inside the StaticLayout's bounding box to avoid undefined behavior.

StaticPosition
class StaticPosition

Bypasses automatic positioning when being laid out. It is your responsibility to make room for this widget in the parent layout.

StatusBar
class StatusBar

Status bars appear at the bottom of a MainWindow. They are made out of Parts, with a width and content.

TabMessageWidget
class TabMessageWidget

A TabMessageWidget is a clickable row of tabs followed by a content area, very similar to the TabWidget. The difference is the TabMessageWidget only sends messages, whereas the TabWidget will automatically change pages of child widgets.

TabWidget
class TabWidget

A tab widget is a set of clickable tab buttons followed by a content area.

TabWidgetPage
class TabWidgetPage
TableView
class TableView

A TableView is a widget made to display a table of data strings.

TextDisplay
class TextDisplay

A read-only text display. It is based on the editable widget base, but does not allow user edits and displays it on the direct background instead of on an editable background.

TextEdit
class TextEdit

A TextEdit is a multi-line plain text editor, comparable to a HTML <textarea>.

TextLabel
class TextLabel
ToolBar
class ToolBar

Toolbars are lists of buttons (typically icons) that appear under the menu. Each button ought to correspond to a menu item, represented by Action objects.

ToolButton
class ToolButton

An implementation helper for ToolBar. Generally, you shouldn't create these yourself and instead just pass Actions to ToolBar's constructor and let it create the buttons for you.

VerticalLayout
class VerticalLayout

Stacks the widgets vertically, taking all the available width for each child.

VerticalRule
class VerticalRule

Draws a line

VerticalSlider
class VerticalSlider
VerticalSpacer
class VerticalSpacer

Adds empty space to a layout.

VisualTheme
class VisualTheme(CRTP)

This is your entry point to create your own visual theme for custom widgets.

Widget
class Widget

The Widget is the base class for minigui's functionality, ranging from UI components like checkboxes or text displays to abstract groupings of other widgets like a layout container or a html <div>. You will likely want to use pre-made widgets as well as creating your own.

WidgetContainer
class WidgetContainer

EXPERIMENTAL

Window
class Window

Enums

ArrowDirection
enum ArrowDirection
EventType
enum EventType

The purpose of this enum was to give a compile-time checked version of various standard event strings.

GenericIcons
enum GenericIcons
MessageBoxButton
enum MessageBoxButton

Identifies the button the user pressed on a message box.

MessageBoxIcon
enum MessageBoxIcon
MessageBoxStyle
enum MessageBoxStyle
ScrollBarShowPolicy
enum ScrollBarShowPolicy

For ScrollableWidget, determines when to show the scroll bar to the user. NEVER USED

Functions

ControlledBy
auto ControlledBy(Args args)

User-defined attribute you can add to struct members contrlled by addDataControllerWidget or dialog to tell which widget you want created for them.

addDataControllerWidget
DataControllerWidget!T addDataControllerWidget(Widget parent, T t, Widget redrawOnChange)
DataControllerWidget!T addDataControllerWidget(Widget parent, T* t, Widget redrawOnChange)

Intended for UFCS action like window.addDataControllerWidget(new MyObject());

addWhenTriggered
void addWhenTriggered(Widget w, void delegate() dg)

Convenience function to add a triggered event listener.

consumesCommand
EventListener consumesCommand(WidgetType w, void delegate(Args) handler)

Declares that the given widget consumes a command identified by the CommandString AND containing Args. Your handler is called with the arguments, then the event's propagation is stopped, so it will not be seen by the consumer's parents.

createContextMenuFromAnnotatedCode
Menu createContextMenuFromAnnotatedCode(Widget w, T t)

Context menus can have @hotkey, @label, @tip, @separator, and @icon

createContextMenuFromAnnotatedCode
Menu createContextMenuFromAnnotatedCode(TWidget w)

Context menus can have @hotkey, @label, @tip, @separator, and @icon

createWin32Window
void createWin32Window(Widget p, const(wchar)[] className, string windowText, DWORD style, DWORD extStyle)

Calls MS Windows' CreateWindowExW function to create a native backing for the given widget. It will create needed mappings, window procedure hooks, and other private member variables needed to tie it into the rest of minigui's expectations.

dialog
void dialog(void delegate(T) onOK, void delegate() onCancel, string title)
void dialog(T initialData, void delegate(T) onOK, void delegate() onCancel, string title)
void dialog(Window parent, void delegate(T) onOK, void delegate() onCancel, string title)
void dialog(T initialData, Window parent, void delegate(T) onOK, void delegate() onCancel, string title)
void dialog(Window parent, T initialData, void delegate(T) onOK, void delegate() onCancel, string title)

Creates a dialog based on a data structure.

emitCommand
void emitCommand(WidgetType w, Args args)

Emits a command to the sender widget's parents with the given CommandString and args. You have no way of knowing if it was ever actually consumed due to the loose coupling. Instead, the consumer may broadcast a state update back toward you.

fileDialogDelegate
FileDialogDelegate fileDialogDelegate()
void fileDialogDelegate(FileDialogDelegate replacement)

It is possible to override or customize the file dialog in some cases. These members provide those hooks: you do fileDialogDelegate = new YourSubclassOf_FileDialogDelegate; and you can do your own thing.

getOpenFileName
void getOpenFileName(Window owner, void delegate(string) onOK, string prefilledName, string[] filters, void delegate() onCancel, string initialDirectory)
void getOpenFileName(void delegate(string) onOK, string prefilledName, string[] filters, void delegate() onCancel, string initialDirectory)
getSaveFileName
void getSaveFileName(Window owner, void delegate(string) onOK, string prefilledName, string[] filters, void delegate() onCancel, string initialDirectory)
void getSaveFileName(void delegate(string) onOK, string prefilledName, string[] filters, void delegate() onCancel, string initialDirectory)

Gets a file name for an open or save operation, calling your onOK function when the user has selected one. This function may or may not block depending on the operating system, you MUST assume it will complete asynchronously.

messageBox
MessageBoxButton messageBox(string title, string message, MessageBoxStyle style, MessageBoxIcon icon)
int messageBox(string message, MessageBoxStyle style, MessageBoxIcon icon)

Displays a modal message box, blocking until the user dismisses it. These global ones are discouraged in favor of the same methods on Window, which give better user experience since the message box is tied the parent window instead of acting independently.

messageBox
MessageBoxButton messageBox(Window originator, string title, string message, MessageBoxStyle style, MessageBoxIcon icon)
int messageBox(Window originator, string message, MessageBoxStyle style, MessageBoxIcon icon)
objectInspectionWindow
ObjectInspectionWindow objectInspectionWindow(T t)

Observes and allows inspection of an object via automatic gui

Interfaces

ReflectableProperties
interface ReflectableProperties
Undocumented in source.

Manifest constants

context_menu
enum context_menu;

Group: generating_from_code

Mixin templates

Beautiful95Theme
mixintemplate Beautiful95Theme()
DefaultDarkTheme
mixintemplate DefaultDarkTheme()
DefaultLightTheme
mixintemplate DefaultLightTheme()

This is your entry point to create your own visual theme for custom widgets.

Emits
mixintemplate Emits(EventType)
mixintemplate Emits(string eventString)

This lets you statically verify you send the events you claim you send and gives you a hook to document them.

Margin
mixintemplate Margin(string code)

Convenience mixin for overriding all four sides of margin or padding in a Widget with the same code. It mixes in the given string as the return value of the four overridden methods.

Observable
mixintemplate Observable(T, string name)

Observable varables can be added to widgets and when they are changed, it fires off a StateChanged event so you can react to it.

Padding
mixintemplate Padding(string code)

Convenience mixin for overriding all four sides of margin or padding in a Widget with the same code. It mixes in the given string as the return value of the four overridden methods.

Structs

ControlledBy_
struct ControlledBy_(T, Args...)

Implementation detail of the ControlledBy UDA.

EventListener
struct EventListener

This is an opaque type you can use to disconnect an event handler when you're no longer interested.

FileName
struct FileName(alias storage = previousFileReferenced, string[] filters = null, FileDialogType type = FileDialogType.Automatic)

Used in automatic menu functions to indicate that the user should be able to browse for a file.

ImageLabel
struct ImageLabel
StyleInformation
struct StyleInformation

Get this through Widget.getComputedStyle. It provides access to the Widget.Style style hints and Widget layout hints, possibly modified through the VisualTheme, through a unifed interface.

WidgetBackground
struct WidgetBackground

Structure to represent a collection of background hints. New features can be added here, so make sure you use the provided constructors and factories for maximum compatibility.

WidgetPainter
struct WidgetPainter

Encapsulates the simpledisplay ScreenPainter for use on a Widget, with VisualTheme and invalidated area awareness.

accelerator
struct accelerator

Program-wide keyboard shortcut to trigger the action

hotkey
struct hotkey

Group: generating_from_code

icon
struct icon

Group: generating_from_code

label
struct label

Group: generating_from_code

menu
struct menu

tells which menu the action will be on

separator
struct separator

This item in the menu will be preceded by a separator line

tip
struct tip

Group: generating_from_code

toolbar
struct toolbar

Describes which toolbar section the action appears on

Templates

Container
template Container(CArgs...)

This is a helper for addDataControllerWidget. You can use it as a UDA on the type. See http://dpldocs.info/this-week-in-d/Blog.Posted_2020_11_02.html for more information.

Variables

previousFileReferenced
string previousFileReferenced;

The default string FileName refers to to store the last file referenced. You can use this if you like, or provide a different variable to FileName in your function.

Detailed Description

Conceptual Overviews

A gui application is made out of widgets laid out in windows that display information and respond to events from the user. They also typically have actions available in menus, and you might also want to customize the appearance. How do we do these things with minigui? Let's break it down into several categories.

Code structure

You will typically want to create the ui, prepare event handlers, then run an event loop. The event loop drives the program, calling your methods to respond to user activity.

import arsd.minigui;

void main() {
	// first, create a window, the (optional) string here is its title
	auto window = new MainWindow("Hello, World!");

	// lay out some widgets inside the window to create the ui
	auto name = new LabeledLineEdit("What is your name?", window);
	auto button = new Button("Say Hello", window);

	// prepare event handlers
	button.addEventListener(EventType.triggered, () {
		window.messageBox("Hello, " ~ name.content ~ "!");
	});

	// show the window and run the event loop until this window is closed
	window.loop();
}

To compile, run opend hello.d, then run the generated hello program.

While the specifics will change, nearly all minigui applications will roughly follow this pattern.

There are two other ways to run event loops: arsd.simpledisplay.EventLoop.get.run(); and arsd.core.getThisThreadEventLoop().run();. They all call the same underlying functions, but have different exit conditions - the EventLoop.get.run() keeps running until all top-level windows are closed, and getThisThreadEventLoop().run keeps running until all "tasks are resolved"; it is more abstract, supporting more than just windows.

You may call this if you don't have a single main window.

Even a basic minigui window can benefit from these if you don't have a single main window:

import arsd.minigui;

void main() {
	// create a struct to hold gathered info
	struct Hello { string name; }
	// let minigui create a dialog box to get that
	// info from the user. If you have a main window,
	// you'd pass that here, but it is not required
	dialog((Hello info) {
		// inline handler of the "OK" button
		messageBox("Hello, " ~ info.name);
	});

	// since there is no main window to loop on,
	// we instead call the event loop singleton ourselves
	EventLoop.get.run;
}

This is also useful when your programs lives as a notification area (aka systray) icon instead of as a window. But let's not get too far ahead of ourselves!

How to lay out widgets

To better understand the details of layout algorithms and see more available included classes, see Layout.

Default layouts

minigui windows default to a flexible vertical layout, where widgets are added, from top to bottom on the window, in the same order of you creating them, then they are sized according to layout hints on the widget itself to fill the available space. This gives a reasonably usable setup but you'll probably want to customize it.

minigui's default VerticalLayout and HorizontalLayout are roughly based on css flexbox with wrap turned off.

Generally speaking, there are two ways to customize layouts: either subclass the widget and change its hints, or wrap it in another layout widget. You can also create your own layout classes and do it all yourself, but that's fairly complicated. Wrapping existing widgets in other layout widgets is usually the easiest way to make things work.

minigui widgets are not supposed to overlap, but can contain children, and are always rectangular. Children are laid out as rectangles inside the parent's rectangular area.

For example, to display two widgets side-by-side, you can wrap them in a HorizontalLayout:

import arsd.minigui;
void main() {
	auto window = new MainWindow();

	// make the layout a child of our window
	auto hl = new HorizontalLayout(window);

	// then make the widgets children of the layout
	auto leftButton = new Button("Left", hl);
	auto rightButton = new Button("Right", hl);

	window.loop();
}

A HorizontalLayout works just like the default VerticalLayout, except in the other direction. These two buttons will take up all the available vertical space, then split available horizontal space equally.

Nesting layouts

Nesting layouts lets you carve up the rectangle in different ways.

This example shows one way you can partition your window into a header and sidebar. Here, the header and sidebar have a fixed width, while the rest of the content sizes with the window.

It might be a new way of thinking about window layout to do things this way - perhaps GridLayout more matches your style of thought - but the concept here is to partition the window into sub-boxes with a particular size, then partition those boxes into further boxes.

The example window has a header across the top

So to make the header, start with a child layout that has a max height. It will use that space from the top, then the remaining children will split the remaining area, meaning you can think of is as just being another box you can split again. Keep splitting until you have the look you desire.

import arsd.minigui;

// This helper class is just to help make the layout boxes visible.
// think of it like a <div style="background-color: whatever;"></div> in HTML.
class ColorWidget : Widget {
	this(Color color, Widget parent) {
		this.color = color;
		super(parent);
	}
	Color color;
	class Style : Widget.Style {
		override WidgetBackground background() { return WidgetBackground(color); }
	}
	mixin OverrideStyle!Style;
}

void main() {
	auto window = new Window;

	// the key is to give it a max height. This is one way to do it:
	auto header = new class HorizontalLayout {
		this() { super(window); }
		override int maxHeight() { return 50; }
	};
	// this next line is a shortcut way of doing it too, but it only works
	// for HorizontalLayout and VerticalLayout, and is less explicit, so it
	// is good to know how to make a new class like above anyway.
	// auto header = new HorizontalLayout(50, window);

	auto bar = new HorizontalLayout(window);

	// or since this is so common, VerticalLayout and HorizontalLayout both
	// can just take an argument in their constructor for max width/height respectively

	// (could have tone this above too, but I wanted to demo both techniques)
	auto left = new VerticalLayout(100, bar);

	// and this is the main section's container. A plain Widget instance is good enough here.
	auto container = new Widget(bar);

	// and these just add color to the containers we made above for the screenshot.
	// in a real application, you can just add your actual controls instead of these.
	auto headerColorBox = new ColorWidget(Color.teal, header);
	auto leftColorBox = new ColorWidget(Color.green, left);
	auto rightColorBox = new ColorWidget(Color.purple, container);

	window.loop();
}
Special layouts

TabWidget can show pages of layouts as tabs.

See ScrollableWidget but be warned that it is weird. You might want to consider something like GenericListViewWidget instead.

Other common layout classes

HorizontalLayout, VerticalLayout, InlineBlockLayout, GridLayout

How to respond to widget events

To better understanding the underlying event system, see Event.

Each widget emits its own events, which propagate up through their parents until they reach their top-level window.

How to do overall ui - title, icons, menus, toolbar, hotkeys, statuses, etc.

We started this series with a MainWindow, but only added widgets to it. MainWindows also support menus and toolbars with various keyboard shortcuts. You can construct these menus by constructing classes and calling methods, but minigui also lets you just write functions in a command object and it does the rest!

See MainWindow.setMenuAndToolbarFromAnnotatedCode for an example.

Note that toggleable menu or toolbar items are not yet implemented, but on the todolist. Submenus and disabled items are also not supported at this time and not currently on the work list (but if you need it, let me know and MAYBE we can work something out. Emphasis on maybe).

The automatic dialog box logic is also available for you to invoke on demand with dialog and the data setting logic can be used with a child widget inside an existing window addDataControllerWidget, which also has annotation-based layout capabilities.

All windows also have titles. You can change this at any time with the window.title = "string"; property.

Windows also have icons, which can be set with the window.icon property. It takes a arsd.color.MemoryImage object, which is an in-memory bitmap. arsd.image can load common file formats into these objects, or you can make one yourself. The default icon on Windows is the icon of your exe, which you can set through a resource file. (FIXME: explain how to do this easily.)

The MainWindow also provides a status bar across the bottom. These aren't so common in new applications, but I love them - on my own computer, I even have a global status bar for my whole desktop! I suggest you use it: a status bar is a consistent place to put information and notifications that will never overlap other content.

A status bar has parts, and the parts have content. The first part's content is assumed to change frequently; the default mouse over event will set it to Widget.statusTip, a public string you can assign to any widget you want at any time.

Other parts can be added by you and are under your control. You add them with:

window.statusBar.parts ~= StatusBar.Part(optional_size, optional_units);

The size can be in a variety of units and what you get with mixes can get complicated. The rule is: explicit pixel sizes are used first. Then, proportional sizes are applied to the remaining space. Then, finally, if there is any space left, any items without an explicit size split them equally.

You may prefer to set them all at once, with:

window.statusBar.parts.setSizes(1, 1, 1);

This makes a three-part status bar, each with the same size - they all take the same proportion of the total size. Negative numbers here will use auto-scaled pixels.

You should call this right after creating your MainWindow as part of your setup code.

Once you make parts, you can explicitly change their content with window.statusBar.parts[index].content = "some string";

I'm thinking about making the other parts do other things by default too, but if I do change it, I'll try not to break any explicitly set things you do anyway.

If you really don't want a status bar on your main window, you can remove it with window.statusBar = null; Make sure you don't try to use it again, or your program will likely crash!

Status bars, at this time, cannot hold non-text content, but I do want to change that. They also cannot have event listeners at this time, but again, that is likely to change. I have something in mind where they can hold clickable messages with a history and maybe icons, but haven't implemented any of that yet. Right now, they're just a (still very useful!) display area.

How to do custom styles

Minigui's custom widgets support styling parameters on the level of individual widgets, or application-wide with VisualThemes.

These don't apply to non-custom widgets! They will use the operating system's native theme unless the documentation for that specific class says otherwise.

At this time, custom widgets gain capability in styling, but lose capability in terms of keeping all the right integrated details of the user experience and availability to accessibility and other automation tools. Evaluate if the benefit is worth the costs before making your decision.

I'd like to erase more and more of these gaps, but no promises as to when - or even if - that will ever actually happen.

See Widget.Style for more information.

Selection of categorized widgets

And more. See members until I write up more of this later and also be aware of the package arsd.minigui_addons.

If none of these do what you need, you'll want to write your own. More on that in the following section.

custom widgets - how to write your own

See Widget.

If you override Widget.recomputeChildLayout, don't forget to call registerMovement() at the top of it, then call recomputeChildLayout of all its children too!

If you need a nested OS level window, see NestedChildWindowWidget. Use Widget.scaleWithDpi to convert logical pixels to physical pixels, as required.

See Widget.OverrideStyle, Widget.paintContent, Widget.dynamicState for some useful starting points.

You may also want to provide layout and style hints by overriding things like Widget.flexBasisWidth, Widget.flexBasisHeight, Widget.minHeight, yada, yada, yada.

You might make a compound widget out of other widgets. Widget.encapsulatedChildren can help hide this from the outside world (though is not necessary and might hurt some debugging!)

Compile your application with the -debug switch and press F12 in your window to open a web-browser-inspired debug window. It sucks right now and doesn't do a lot, but is sometimes better than nothing.
Timers and animations

The Timer class is available and you can call widget.redraw(); to trigger a redraw from a timer handler.

I generally don't like animations in my programs, so it hasn't been a priority for me to do more than this. I also hate uis that move outside of explicit user action, so minigui kinda supports this but I'd rather you didn't. I kinda wanna do something like requestAnimationFrame or something but haven't yet so it is just the Timer class.

Clipboard integrations, drag and drop

GUI application users tend to expect integration with their system, so clipboard support is basically a must, and drag and drop is nice to offer too. The functions for these are provided in arsd.simpledisplay, which is public imported from minigui, and thus available to you here too.

I'd like to think of some better abstractions to make this more automagic, but you must do it yourself when implementing your custom widgets right now.

See: draggable, DropHandler, setClipboardText, setClipboardImage, getClipboardText, getClipboardImage, setPrimarySelection, and others from simpledisplay.

Context menus

Override Widget.contextMenu in your subclass.

Coming later

Among the unfinished features: unified selections, translateable strings, external integrations.

Running minigui programs

Note the environment variable ARSD_SCALING_FACTOR on Linux can set multi-monitor scaling factors. I should also read it from a root window property so it easier to do with migrations... maybe a default theme selector from there too.

Building minigui programs

minigui's only required dependencies are arsd.simpledisplay, arsd.color, and arsd.textlayouter, on which it is built. simpledisplay provides the low-level interfaces and minigui builds the concept of widgets inside the windows on top of it.

Its #1 goal is to be useful without being large and complicated like GTK and Qt. It isn't hugely concerned with appearance - on Windows, it just uses the native controls and native theme, and on Linux, it keeps it simple and I may change that at any time, though after May 2021, you can customize some things with css-inspired Widget.Style classes. (On Windows, if you compile with -version=custom_widgets, you can use the custom implementation there too, but... you shouldn't.)

The event model is similar to what you use in the browser with Javascript and the layout engine tries to automatically fit things in, similar to a css flexbox.

FOR BEST RESULTS: be sure to link with the appropriate subsystem command -L/SUBSYSTEM:WINDOWS and -L/entry:mainCRTStartup`. If using ldc instead of dmd, use -L/entry:wmainCRTStartup instead of mainCRTStartup; note the "w".

Otherwise you'll get a console and possibly other visual bugs. But if you do use the subsystem:windows, note that Phobos' writeln will crash the program!

HTML To Classes

HTML CodeMinigui Class
<input type="text">LineEdit
<input type="password">PasswordEdit
<textarea>TextEdit
<select>DropDownSelection
<input type="checkbox">Checkbox
<input type="radio">Radiobox
<button>Button

Stretchiness: The default is 4. You can use larger numbers for things that should consume a lot of space, and lower numbers for ones that are better at smaller sizes.

Overlapped input

COMING EVENTUALLY: minigui will include a little bit of I/O functionality that just works with the event loop. If you want to get fancy, I suggest spinning up another thread and posting events back and forth.

Add ons

See the minigui_addons directory in the arsd repo for some add on widgets you can import separately too.

XML definitions

If you use arsd.minigui_xml, you can create widget trees from XML at runtime.

Scriptability

minigui is compatible with arsd.script. If you see @scriptable on a method in this documentation, it means you can call it from the script language.

Tip: to allow easy creation of widget trees from script, import arsd.minigui_xml and make arsd.minigui_xml.makeWidgetFromString available to your script:

import arsd.minigui_xml;
import arsd.script;

var globals = var.emptyObject;
globals.makeWidgetFromString = &makeWidgetFromString;

// this now works
interpret(`var window = makeWidgetFromString("<MainWindow />");`, globals);

More to come.

My UI Guidelines

Note that the Linux custom widgets generally aim to be efficient on remote X network connections.

In a perfect world, you'd achieve all the following goals:

  • All operations are present in the menu
  • The operations the user wants at the moment are right where they want them
  • All operations can be scripted
  • The UI does not move any elements without explicit user action
  • All numbers can be seen and typed in if wanted, even if the ui usually hides them

Future Directions

I want to do some newer ideas that might not be easy to keep working fully on Windows, like adding a menu search feature and scrollbar custom marks and typing in numbers. I might make them a default part of the widget with custom, and let you provide them through a menu or something elsewhere.

Examples

This hello world sample will have an oversized button, but that's ok, you see your first window!

import arsd.minigui;

void main() {
	auto window = new MainWindow();

	// note the parent widget is almost always passed as the last argument to a constructor
	auto hello = new TextLabel("Hello, world!", TextAlignment.Center, window);
	auto button = new Button("Close", window);
	button.addWhenTriggered({
		window.close();
	});

	window.loop();
}

Meta

History

Minigui had mostly additive changes or bug fixes since its inception until May 2021.

In May 2021 (dub v10.0), minigui got an overhaul. If it was versioned independently, I'd tag this as version 2.0.

Among the changes:

  • The event model changed to prefer strongly-typed events, though the Javascript string style ones still work, using properties off them is deprecated. It will still compile and function, but you should change the handler to use the classes in its argument list. I adapted my code to use the new model in just a few minutes, so it shouldn't too hard.

    See Event for details.

  • A DoubleClickEvent was added. Previously, you'd get two rapidly repeated click events. Now, you get one click event followed by a double click event. If you must recreate the old way exactly, you can listen for a DoubleClickEvent, set a flag upon receiving one, then send yourself a synthetic ClickEvent on the next MouseUpEvent, but your program might be better served just working with MouseDownEvents instead.

    See DoubleClickEvent for details.

  • Styling hints were added, and the few that existed before have been moved to a new helper class. Deprecated forwarders exist for the (few) old properties to help you transition. Note that most of these only affect a custom_events build, which is the default on Linux, but opt in only on Windows.

    See Widget.Style for details.

  • Widgets now draw their keyboard focus by default instead of opt in. You may wish to set tabStop = false; if it wasn't supposed to receive it.
  • Most Widget constructors no longer have a default parent argument. You must pass the parent to almost all widgets, or in rare cases, an explict null, but more often than not, you need the parent so the default argument was not very useful at best and misleading to a crash at worst.
  • LabeledLineEdit changed its default layout to vertical instead of horizontal. You can restore the old behavior by passing a TextAlignment argument to the constructor.
  • Several conversions of public fields to properties, deprecated, or made private. It is unlikely this will affect you, but the compiler will tell you if it does.
  • Various non-breaking additions.