Documentation
¶
Overview ¶
Package appkit embeds real AppKit controls — NSButton, NSTextField, NSSecureTextField, NSSlider and their kin — inside a host's own NSView, from pure Go with CGO_ENABLED=0.
It exists to be the NATIVE counterpart to a pixel-drawn widget toolkit. A toolkit that paints its own buttons and text fields into a framebuffer is portable and fast, but there is a small set of controls the operating system must own: a secure text field the window server fills without the process ever seeing the keystrokes, the system colour and font panels, a control that carries the platform's exact focus-ring, drag and accessibility behaviour. For those, a drawn imitation is not merely less faithful — it cannot be correct. This package hands the host a live AppKit object it can place where the toolkit laid out a region, so the two compose: the toolkit does the layout, AppKit does the control.
The shape of the API ¶
A Control wraps one native control. The host creates it with a constructor (NewButton, NewSecureTextField, …), positions it with Control.SetFrame in the coordinate system of the superview it is added to with Control.AddTo, reads and writes its value (Control.StringValue, Control.SetBool, …), and is told when the person changes it (Control.OnAction, Control.OnChange). Every method must be called on the process main thread — where AppKit permits control creation and mutation, and where a windowing host already runs its layout and event loop. The action and change handlers are called back on that same thread.
It binds through go-macos/objc ¶
Every native call is an Objective-C message send over github.com/go-macos/objc (itself over github.com/ebitengine/purego), so the package links with no cgo and cross-compiles like any other Go code. Off macOS every constructor reports ErrUnsupported rather than being absent, so a consumer builds and runs on every platform and finds out at run time — with one clean error — that this platform has no AppKit to embed.
It needs a running application and a host view ¶
A control is only useful once it is a subview of a view that is on screen and whose application is running an event loop — otherwise it draws nowhere and no action ever fires. This package does not create the application, the window or the event loop; those belong to whoever owns the process (a windowing library such as github.com/go-widgets/window). A control created before there is an application is still a valid object; it simply shows and reacts to nothing until it is placed and the loop runs.
Index ¶
- Variables
- type Control
- func New(spec Spec) (*Control, error)
- func NewButton(title string) (*Control, error)
- func NewCheckbox(title string) (*Control, error)
- func NewColorWell() (*Control, error)
- func NewComboBox(items []string) (*Control, error)
- func NewDatePicker() (*Control, error)
- func NewLabel(text string) (*Control, error)
- func NewLinkButton(text string) (*Control, error)
- func NewPopUpButton(items []string) (*Control, error)
- func NewProgressIndicator(min, max float64) (*Control, error)
- func NewRadioButton(title string) (*Control, error)
- func NewSearchField(text string) (*Control, error)
- func NewSecureTextField(text string) (*Control, error)
- func NewSegmentedControl(items []string) (*Control, error)
- func NewSlider(min, max, value float64) (*Control, error)
- func NewSpinner() (*Control, error)
- func NewStepper(min, max, value float64) (*Control, error)
- func NewSwitch() (*Control, error)
- func NewTableView(items []string) (*Control, error)
- func NewTextField(text string) (*Control, error)
- func NewTextView(text string) (*Control, error)
- func (c *Control) AddTo(parent objc.ID) error
- func (c *Control) Bool() bool
- func (c *Control) Close()
- func (c *Control) Double() float64
- func (c *Control) Kind() Kind
- func (c *Control) OnAction(fn func())
- func (c *Control) OnChange(fn func())
- func (c *Control) Remove() error
- func (c *Control) SetBool(on bool) error
- func (c *Control) SetDouble(v float64) error
- func (c *Control) SetFrame(x, y, w, h float64) error
- func (c *Control) SetHidden(hidden bool) error
- func (c *Control) SetImage(png []byte) error
- func (c *Control) SetImageOnly(only bool) error
- func (c *Control) SetItems(items []string) error
- func (c *Control) SetMenu(items []MenuItem) error
- func (c *Control) SetStringValue(s string) error
- func (c *Control) StringValue() string
- type Kind
- type MenuItem
- type Spec
Constants ¶
This section is empty.
Variables ¶
var ErrClosed = errors.New("appkit: control is closed")
ErrClosed is returned when a method is called on a control that has already been closed. A closed control's native object is gone; touching it would message freed memory.
var ErrUnsupported = errors.New("appkit: native controls are only available on macOS")
ErrUnsupported is returned by every constructor away from macOS: the controls this package embeds are AppKit's, and there is no AppKit to embed them in.
Functions ¶
This section is empty.
Types ¶
type Control ¶
type Control struct {
// contains filtered or unexported fields
}
Control is one live native AppKit control. Its zero value is not usable; get one from a constructor.
Every method must be called on the process main thread — the thread that runs the AppKit event loop — because AppKit permits control creation and mutation only there. A host such as github.com/go-widgets/window drives its layout and event handling on that thread already, which is where it places and updates these controls. Calling from another goroutine is a bug this package does not guard against; the action and change handlers, in turn, are invoked on the main thread because that is where AppKit delivers them.
func New ¶
New builds a control from a full Spec. The constructors below are thin wrappers over it and are what most callers want.
func NewCheckbox ¶
NewCheckbox makes a labelled checkbox.
func NewColorWell ¶ added in v0.2.0
NewColorWell makes a colour well. Its value is Control.StringValue as a #RRGGBB hex string; Control.OnAction (and Control.OnChange) fire when the colour changes.
func NewComboBox ¶ added in v0.2.0
NewComboBox makes an editable combo box with the given drop-down items. The typed-or-picked text is Control.StringValue; Control.OnChange fires on a keystroke, Control.OnAction on Return or a pick.
func NewDatePicker ¶ added in v0.2.0
NewDatePicker makes a date picker. Its value is Control.StringValue as an ISO-8601 YYYY-MM-DD string; Control.OnAction fires when the date changes.
func NewLinkButton ¶ added in v0.2.0
NewLinkButton makes a hyperlink-styled button with the given title. Its title is Control.StringValue; Control.OnAction fires when it is clicked.
func NewPopUpButton ¶
NewPopUpButton makes a pop-up list of the given items.
func NewProgressIndicator ¶ added in v0.2.0
NewProgressIndicator makes a determinate progress bar over [min,max], starting at min. Its value is read and written with Control.Double; it has no action.
func NewRadioButton ¶
NewRadioButton makes a radio button. Give a set of them the same superview and they behave as one group.
func NewSearchField ¶ added in v0.2.0
NewSearchField makes a search field with the given initial text. Its text is Control.StringValue; Control.OnChange fires on every keystroke and Control.OnAction on Return.
func NewSecureTextField ¶
NewSecureTextField makes a secure (bulleted) text field. This is the control a drawn toolkit cannot substitute for a password box: the window server fills it without the process seeing the keystrokes.
func NewSegmentedControl ¶ added in v0.2.0
NewSegmentedControl makes a segmented control with the given segment labels. The selected segment's label is Control.StringValue; Control.OnAction fires when the selection changes.
func NewSpinner ¶ added in v0.2.0
NewSpinner makes an indeterminate spinning progress indicator. Control.SetBool(true) starts its animation and (false) stops it.
func NewStepper ¶ added in v0.2.0
NewStepper makes a stepper over [min,max] starting at value. Its value is Control.Double; Control.OnAction fires on each step.
func NewTableView ¶ added in v0.2.0
NewTableView makes a list of items: an NSTableView of one text column inside an NSScrollView, with the system's scrolling, keyboard navigation and accessibility.
The chosen row is Control.Double as a zero-based index, -1 when none is; OnChange fires when it moves. Control.SetItems replaces the rows.
func NewTextField ¶
NewTextField makes an editable text field with the given initial text.
func NewTextView ¶ added in v0.2.0
NewTextView makes a multi-line, editable text view with the given initial text. Its text is Control.StringValue; Control.OnChange fires as it is edited.
func (*Control) AddTo ¶
AddTo makes the control a subview of parent. Adding it to a view that is on screen is what makes it appear; a control is never visible on its own.
func (*Control) Bool ¶
Bool reads the on/off state of a Checkbox or Switch. On a closed control it returns false.
func (*Control) Close ¶
func (c *Control) Close()
Close removes the control from its superview and releases the native object. After Close the control is inert: mutating methods return ErrClosed and readers return their zero value. Closing twice is safe.
func (*Control) OnAction ¶
func (c *Control) OnAction(fn func())
OnAction registers the handler called when the control fires its primary action: a button clicked, a checkbox or switch toggled, a pop-up selection made, or editing ended in a text field. The handler runs on the main thread. Passing nil clears it.
func (*Control) OnChange ¶
func (c *Control) OnChange(fn func())
OnChange registers the handler called as a control's value changes continuously: every keystroke in a text field, every step of a dragged slider. The handler runs on the main thread. Passing nil clears it.
func (*Control) Remove ¶
Remove takes the control out of its superview without closing it, so it can be added elsewhere. To dispose of it for good, use Control.Close.
func (*Control) SetFrame ¶
SetFrame positions and sizes the control in the coordinate system of the view it is (or will be) added to. The host converts from its own layout space; a control's frame means nothing until it has a superview.
func (*Control) SetHidden ¶
SetHidden shows or hides the control in place, keeping its frame and its place in the view tree.
func (*Control) SetImage ¶ added in v0.4.0
SetImage puts a picture on a control -- a button, in practice -- from PNG (or any other format NSImage reads) bytes.
A toolbar is icons. Transmission's is eleven of them and not one word, and it is legible at a glance because a picture of a pause sign is read faster than the word "pause". A button that can only carry a title cannot make one.
Passing no bytes removes the image and leaves the title.
func (*Control) SetImageOnly ¶ added in v0.4.0
SetImageOnly says the control shows its picture and not its title.
The title stays SET even so: it is what a screen reader announces and what the tooltip shows, so an icon-only button that dropped its title would be a button nobody using assistive technology could name.
func (*Control) SetItems ¶ added in v0.2.0
SetItems replaces the rows of a TableView (or the entries of a PopUpButton or ComboBox), and reloads it.
A list whose contents are fixed at creation is not a list anybody needs: the queue this was built for gains and loses entries while the window is open.
func (*Control) SetMenu ¶ added in v0.3.0
SetMenu gives a control the menu it shows on a right-click (or a Control click, or the trackpad's secondary gesture -- the system decides, which is the point of asking it).
Buttons along the bottom of a window are a dialogue's shape: a fixed row of verbs that must all fit, all the time, whether or not any of them applies to what is selected. A context menu is the other shape -- the verbs that apply to THIS row, where the row is, named in full rather than abbreviated to fit.
Passing no items removes the menu.
func (*Control) SetStringValue ¶
SetStringValue replaces a text control's contents (Label, TextField, SecureTextField), or a pop-up's selected title.
func (*Control) StringValue ¶
StringValue reads a text control's contents, or a pop-up's selected title. On a closed control it returns "".
type Kind ¶
type Kind int
Kind is which native control a Control wraps. It is fixed at construction: AppKit has no single control that becomes a button or a slider after the fact, so neither does this package.
const ( // Button is an NSButton with a push-button style — a momentary control that // fires its action when clicked. Button Kind = iota // Label is a non-editable, non-selectable NSTextField: text the host places // but the person cannot change. It has no action. Label // TextField is an editable NSTextField. Its action fires when editing ends // (Return, or focus leaving the field); OnChange fires on every keystroke. TextField // SecureTextField is an NSSecureTextField: an editable field whose glyphs // are bullets and whose contents the window server fills without this // process seeing the keystrokes. This is the control that cannot be drawn. SecureTextField // Checkbox is an NSButton with a switch (checkbox) style: an on/off control // with a label. Its state is [Control.Bool]. Checkbox // RadioButton is an NSButton with a radio style. AppKit groups radio buttons // that share a superview and action, so that selecting one clears its // siblings; give a group of RadioButtons the same parent to get that. RadioButton // Switch is an NSSwitch, the sliding on/off control introduced in macOS // 10.15. Its state is [Control.Bool]. Switch // Slider is an NSSlider over a [Spec.Min],[Spec.Max] range. Its value is // [Control.Double]; OnChange fires as it is dragged. Slider // PopUpButton is an NSPopUpButton: a pull-down list of [Spec.Items]. The // selected title is [Control.StringValue]; its action fires on selection. PopUpButton // ProgressIndicator is a determinate NSProgressIndicator (a bar). Its value // is [Control.Double], clamped to the [Spec.Min],[Spec.Max] range fixed at // construction. It is read-only: it has no action, because a progress bar // reports, it is not operated. ProgressIndicator // Spinner is an indeterminate, spinning NSProgressIndicator. It carries no // value; [Control.SetBool] starts (true) and stops (false) its animation. Spinner // Stepper is an NSStepper over [Spec.Min],[Spec.Max] starting at // [Spec.Value]. Its value is [Control.Double]; its action fires on each step. Stepper // SearchField is an NSSearchField: an editable field styled for search. Its // text is [Control.StringValue]; OnChange fires on every keystroke and its // action fires on Return. SearchField // ComboBox is an editable NSComboBox: a text field with a drop-down list of // [Spec.Items]. The typed-or-picked text is [Control.StringValue]; OnChange // fires on a keystroke, its action on Return or a pick. ComboBox // SegmentedControl is an NSSegmentedControl over [Spec.Items]. The selected // segment's label is [Control.StringValue] (the binding maps label to index); // its action fires when the selection changes. SegmentedControl // TextView is a multi-line, editable NSTextView (inside an NSScrollView). Its // text is [Control.StringValue]; OnChange fires as it is edited. It has no // action. TextView // LinkButton is an NSButton styled as a hyperlink. Its title is // [Control.StringValue]; its action fires when it is clicked, which is where // a host opens the link. LinkButton // DatePicker is an NSDatePicker. Its value is [Control.StringValue] as an // ISO-8601 YYYY-MM-DD date string (the binding parses and formats at the // boundary); its action fires when the date changes. DatePicker // ColorWell is an NSColorWell. Its value is [Control.StringValue] as a // #RRGGBB hex string (the binding converts to and from NSColor); its action // fires when the colour changes. ColorWell // TableView is an NSTableView of one text column inside an NSScrollView: // a list of [Spec.Items] a person picks a row from, with the system's own // scrolling, keyboard navigation and accessibility. // // The selected row is [Control.Double] as a zero-based index, and -1 when // nothing is selected; OnChange fires when the selection moves. The rows // themselves are replaced with [Control.SetItems], because a list whose // contents never change is not a list anybody needs. // // It is the one kind here that needs a DATA SOURCE rather than a value: // AppKit asks how many rows there are and what is in each, so this package // answers those two questions from the items it was given. TableView )
type MenuItem ¶ added in v0.3.0
type MenuItem struct {
Title string
// OnPick runs when the item is chosen. Nil makes the item inert, which is
// how a menu shows a verb that does not apply right now rather than hiding
// it and moving everything else.
OnPick func()
}
MenuItem is one line of a control's context menu.
An empty Title is a separator: a menu is a list of verbs with rules between the groups, and giving the rule its own type would make every caller name something that has no name.
type Spec ¶
type Spec struct {
Kind Kind
// Title is the label of a Button, Checkbox or RadioButton, the initial text
// of a Label, TextField, SecureTextField, SearchField or TextView, and the
// title of a LinkButton. It is ignored for the valueless controls (Switch,
// Slider, PopUpButton, and the rest).
Title string
// Items are the entries of a PopUpButton or ComboBox drop-down, or the
// segments of a SegmentedControl, in order. A PopUpButton or SegmentedControl
// with no items is refused: neither can be operated empty. A ComboBox may be
// empty — it is still a typeable text field.
Items []string
// Min, Max and Value are the bounds and initial position of a Slider or
// Stepper, and the bounds of a ProgressIndicator (whose initial value is
// Min). Min must be < Max. Value is clamped into the range. Ignored
// otherwise.
Min, Max, Value float64
}
Spec describes a control to build. Most callers reach for a constructor (NewButton, NewSlider, …) rather than filling this in by hand; it is exported so a host that maps a laid-out region to a control kind at run time can build one from data.