Type MenuScroll

type MenuScroll struct {
	// Offset is how far the content has moved up, in pixels.
	Offset float32
	// BarID, when set, makes the scrollbar a hit of that identity so the thumb can
	// be dragged (see Drag). The rows' hits then end where the bar starts.
	BarID ui.ID

	// measured says a paint has found max, so Offset can be held to it; before
	// that an Offset the caller set stands.

	// view is the height of the room the rows show in; items are the rows with
	// an identity, as the last paint laid them out in content coordinates.

	// TabCloses makes Tab close the list and continue through the page, as a
	// dropdown or select does. A context menu leaves it false, because Base UI
	// keeps Tab inside a context menu.
	TabCloses bool
	// followed is the keyboard focus the list last scrolled to.

	// barTop and thumb place the thumb of the last paint; grab is where in the
	// thumb a drag holds it.

}

MenuScroll is the caller-owned scroll position of a menu or command list whose items do not all fit its bounds, as `overflow-y-auto` is the browser's. Keep one per open list, hand its address to the list's options, and feed wheel gestures that happen over the list to Wheel:

var scroll components.MenuScroll // reset to its zero value when the list opens
...
menu := components.BeginDropdownMenu(canvas, theme, bounds, components.DropdownMenuOptions{Scroll: &scroll})
... AddItem ...
menu.End() // paints the scrollbar
...
config.OnScroll = func(event ui.ScrollEvent) ui.EventResult {
	if menuOpen && pointInRect(event.X, event.Y, menuBounds) {
		return ui.EventResult{Handled: true, Repaint: scroll.Wheel(event)}
	}
	...
}

Rows that are scrolled out declare no hit, so Tab and the pointer cannot reach them; the arrow keys can, through Navigate, and a row the keyboard focuses is scrolled into view by the next paint. Set BarID to make the scrollbar draggable.

The list measures its content as it paints, so Offset is clamped to what the last paint found. Set Offset to zero when the items change.

Methods

By#

func (scroll *MenuScroll) By(step float32) bool

By scrolls by step pixels (positive reveals what is below) and reports whether the position changed.

Drag#

func (scroll *MenuScroll) Drag(event ui.PointerEvent) bool

Drag drags the thumb of a list whose BarID is set: feed it the pointer events whose Target is BarID, and, while Dragging, every pointer event. A press on the thumb holds it where it was grabbed; a press on the track elsewhere centers the thumb on the pointer. It reports whether the position changed, to repaint.

Dragging#

func (scroll *MenuScroll) Dragging() bool

Dragging reports whether the thumb is held, so pointer events that follow it out of the scrollbar still belong to Drag.

Match#

func (scroll *MenuScroll) Match(prefix string, from ui.ID) (ui.ID, bool)

Match returns the first enabled row whose label starts with prefix, ignoring case, searching after from and wrapping once, and scrolls it into view. It is Base UI's menu and select typeahead: the caller owns the typed text and when a session starts or expires (Base UI resets it after 500ms), then hands each printable key to Match. Passing the item the keyboard or selection started on as from starts at the next row, as a new session does; passing the previous match continues after it, which is how a repeated first letter cycles through same-initial rows. It reports false for an empty prefix or when nothing among the last paint's rows matches.

Max#

func (scroll *MenuScroll) Max() float32

Max is how far the list can scroll: how much of its content the last paint did not fit. It is zero until the list has painted, and for a list that fits.

Navigate#

func (scroll *MenuScroll) Navigate(key ui.Key, from ui.ID, loop bool) (ui.ID, bool)

Navigate is the arrow-key movement of a list: Up and Down step to the previous and next enabled item, Home and End go to the first and last, PageUp and PageDown move a viewport's height. It returns the identity of the item the key leads to from the item from (the zero identity when none has the keyboard) and scrolls it into view; ask the runtime to focus it:

if id, ok := scroll.Navigate(event.Key, event.Target, true); ok {
	return ui.EventResult{Handled: true, Repaint: true, Focus: id, FocusVisible: true}
}

loop makes Down past the last item arrive at the first, and Up past the first at the last, as a menu does; a list that does not loop stays on its edge. It reports false for any other key, and for a list without an enabled item.

Reveal#

func (scroll *MenuScroll) Reveal(id ui.ID) bool

Reveal scrolls the least that brings the item with that identity fully into view, as the last paint laid the list out, and reports whether the position changed. The first item scrolls to the top, so a group label above it shows, and the last to the end. An identity the list did not paint changes nothing.

Wheel#

func (scroll *MenuScroll) Wheel(event ui.ScrollEvent) bool

Wheel scrolls by one scroll gesture and reports whether the position changed. A notch of a wheel (a gesture that is not Precise) moves at least 24 pixels.