The OpenD Programming Language

arsd.minigui

minigui is a smallish GUI widget library, aiming to be on par with at least HTML4 forms and a few other expected gui components. It uses native controls on Windows and does its own thing on Linux (Mac is not currently supported but may be later, and should use native controls) to keep size down. The Linux appearance is similar to Windows 95 and avoids using images to maintain network efficiency on remote X connections, though you can customize that.

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
CustomPasswordEdit
class CustomPasswordEdit

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

CustomTextEdit
class CustomTextEdit
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

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.

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
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.

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

TextEdit
class TextEdit
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.

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)

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

addDataControllerWidget
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.

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)

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.

getOpenFileName
void getOpenFileName(void delegate(string) onOK, string prefilledName, string[] filters, void delegate() onCancel, string initialDirectory)
getSaveFileName
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.

objectInspectionWindow
ObjectInspectionWindow objectInspectionWindow(T t)

Observes and allows inspection of an object via automatic gui

Interfaces

ReflectableProperties
interface ReflectableProperties
Undocumented in source.

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.

Detailed Description

minigui's only required dependencies are arsd.simpledisplay and arsd.color, 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
<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.

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

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

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.

    // * A widget must now opt in to receiving keyboard focus, rather than opting out.

  • 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.