MyGo
Input

Guides

Input

The pointer, the keyboard focus, shortcuts, input methods, dropped files, and widgets that take every key.

Elements report what the user did to them since the last frame, as you build them: ask, and handle it where the element is built.

card := ui.Column(c).Padding(12).Radius(8).Focusable()
if card.Hovered() {
	card.Background(t.SurfaceHover)
}
if card.DoubleClicked() {
	app.open(item)
}

The pointer

Hovered, Pressed, Clicked, DoubleClicked, RightClicked, Dragged (how far the pointer moved since the last frame while pressing the element) and PointerPosition. ClickModifiers returns the modifier keys held for the last click, as Shift for a Shift-click. PassThrough lets the pointer through to what is below.

Elements that take the pointer give it to the innermost under it: a button in a clickable row takes its own clicks.

While the pointer presses an element, the elements it was over as the press began, as the row around a button, stay Hovered as long as it is over them, as in CSS, and the others hover no more: dragging over other elements does not light them up. A button that shows while its row is hovered stays as it is pressed, to take its click:

row := ui.Row(c).Padding(8)
hovered := row.Hovered()
row.Children(func() {
	ui.Text(c, item.Title).Grow(1)
	if hovered && ui.Button(c, "Remove").Clicked() {
		removed = item.ID
	}
})

The keyboard focus

Focusable elements take the focus when clicked, and Tab and Shift+Tab move it between them, with a focus ring when it moves by keyboard. AutoFocus gives an element the focus when it appears, such as the first field of a dialog, and Focus keeps it there while you call it; Focused, FocusVisible and FocusWithin report it. Enter and Space press a focused button or link, and Space toggles a focused check box, switch, toggle or radio button.

Focus groups

The controls of a toolbar, a radio group, a segmented control or a tab list are one stop of Tab, as they are natively: Tab moves the focus into the group, to the control that had it last (or the radio button or tab chosen), and on out of it, while the arrows move it among them, Home and End to the first and last. FocusGroup does it for a container of your own, with the arrows ui.Horizontal, ui.Vertical or both; a slider or a text input inside keeps the arrows it takes.

ui.Row(c).Gap(4).FocusGroup(ui.Horizontal).Children(func() {
	for _, tool := range tools {
		if ui.Button(c, tool.Name).Clicked() {
			app.tool = tool
		}
	}
})

Shortcuts

c.Shortcut(ui.Cmd, ui.KeyS) reports a key pressed with exactly those modifiers anywhere in the window, and Element.Shortcut only while the element or one inside it has the focus, which comes first. A focused button or link keeps Enter and Space, and a toggle Space, so c.Shortcut(0, ui.KeyEnter) presses a dialog's default button wherever else the focus is.

if c.Shortcut(ui.Cmd, ui.KeyS) {
	app.save()
}

ui.Cmd is Command on macOS and Ctrl elsewhere. ui.KeyBack and ui.KeyForward are the back and forward buttons of a mouse and the keys of keyboards that have them, which a router takes. Shortcuts of menus still work, and the Edit menu's roles (cut, copy, paste, select all, undo, redo) act on the focused text input.

A focused text input takes the editing keys of the platform first: on macOS, Option and Command with the arrows and Backspace, and Control with A, E, B, F, N, P, D, H and K, as in other Mac apps; elsewhere, it leaves Alt and the arrows, which go back and forward, and the function keys.

Input methods

Text inputs take text composed with input methods, which see the text around the caret: macOS's press and hold replaces the letter it accents, Japanese input methods convert typed text again, and others predict from what comes before.

Dropped files

DroppedFiles returns the paths of the files dropped on the element from Finder, Explorer or a file manager, and FileDragOver reports files dragged over it, to show it would take them. The window takes files only over such elements, or anywhere when it has OnFileDrop listeners, which get the files no element takes, with where they were dropped:

zone := ui.Column(c).Size(260, 80).Border(1, t.Border)
if files := zone.DroppedFiles(); files != nil {
	app.files = files
}
if zone.FileDragOver() {
	zone.Border(2, t.Accent)
}

Values dragged within the window are drag and drop.

Every key, as it comes

Widgets that take every key themselves, as the terminal does, get their input as it comes with HandleInput, before the next frame: keys pressed and released, text typed and composed while they have the focus, the edit commands of the menus, and the pointer pressed on them, moving over them or scrolling over them. The function reports whether it took the event; one it leaves goes on to shortcuts, Tab, context menus and scroll containers as usual, and keys that the window or an element around the focus handles with Shortcut go there first. TextCaret turns on the system's input methods for such an element while it has the focus, composing at the caret it gives. c.ReadClipboard, c.WriteClipboard and c.OpenURL copy, paste and open links for them:

ui.Box(c).Fill().Focusable().HandleInput(func(ev ui.InputEvent) bool {
	switch ev.Kind {
	case ui.InputKeyDown:
		return app.key(ev.Mods, ev.Key)
	case ui.InputText:
		app.insert(ev.Text)
		return true
	}
	return false
}).TextCaret(app.caretRect())

See also