diff --git a/ui/doc.go b/ui/doc.go index 5d9a1aa2..65a7c36e 100644 --- a/ui/doc.go +++ b/ui/doc.go @@ -24,7 +24,7 @@ Drawing a colored square: import "gioui.org/ui/paint" var w app.Window - var ops ui.Ops + ops := new(ui.Ops) ... ops.Reset() paint.ColorOp{Color: ...}.Add(ops) @@ -38,7 +38,7 @@ mutable state stack and execution flow can be controlled with macros. The StackOp saves the current state to the state stack and restores it later: - var ops ui.Ops + ops := new(ui.Ops) var stack ui.StackOp // Save the current state, in particular the transform. stack.Push(ops) @@ -50,7 +50,7 @@ The StackOp saves the current state to the state stack and restores it later: The MacroOp records a list of operations to be executed later: - var ops ui.Ops + ops := new(ui.Ops) var macro ui.MacroOp macro.Record() // Record operations by adding them. diff --git a/ui/input/input.go b/ui/input/input.go index b6f9deb0..9d7185cc 100644 --- a/ui/input/input.go +++ b/ui/input/input.go @@ -23,7 +23,7 @@ The following example marks a handler ready for key input: import gioui.org/ui/input import gioui.org/ui/key - var ops ui.Ops + ops := new(ui.Ops) var h *Handler = ... key.HandlerOp{Key: h}.Add(ops) diff --git a/ui/key/key.go b/ui/key/key.go index a23c6cb5..1ce30c28 100644 --- a/ui/key/key.go +++ b/ui/key/key.go @@ -1,7 +1,8 @@ // SPDX-License-Identifier: Unlicense OR MIT /* -Package key implements key and text input handling. +Package key implements key and text events and +operations. The HandlerOp operations is used for declaring key input handlers. Use the Queue interface from package @@ -28,7 +29,7 @@ type HandlerOp struct { // be hidden. type HideInputOp struct{} -// FocusEvent is sent when a handler gains or looses +// FocusEvent is sent when a handler gains or loses // focus. type FocusEvent struct { Focus bool diff --git a/ui/pointer/doc.go b/ui/pointer/doc.go new file mode 100644 index 00000000..d658188c --- /dev/null +++ b/ui/pointer/doc.go @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +/* +Package pointer implements pointer events and operations. +A pointer is either a mouse controlled cursor or a touch +object such as a finger. + +The HandlerOp operation is used to declare a handler ready +for pointer events. Use a Queue from package input to +receive events. + +Areas + +The area operations are used for specifying the area where +subsequent HandlerOps are active. + +For example, to set up a rectangular hit area: + + var ops ui.Ops + var h *Handler = ... + + r := image.Rectangle{...} + pointer.RectAreaOp{Rect: r}.Add(ops) + pointer.HandlerOp{Key: h}.Add(ops) + +Note that areas compound: the effective area of multiple area +operations is the intersection of the areas. + +Matching events + +StackOp operations and input handlers form an implicit tree. +Each stack operation is a node, and each input handler is associated +with the most recent node. + +For example: + + ops := new(ui.Ops) + var stack ui.StackOp + var h1, h2 *Handler + + stack.Push(ops) + pointer.HandlerOp{Key: h1}.Add(Ops) + stack.Pop() + + stack.Push(ops) + pointer.HandlerOp{Key: h2}.Add(ops) + stack.Pop() + +implies a tree of two inner nodes, each with one pointer handler. + +When determining which handlers match an Event, only handlers whose +areas contain the event position are considered. The matching +proceeds as follows. + +First, the foremost matching handler is included. If the handler +has pass-through enabled, this step is repeated. + +Then, all matching handlers from the current node and all parent +nodes are included. + +In the example above, all events will go to h2 only even though both +handlers have the same area (the entire screen). + +Pass-through + +The PassOp operations controls the pass-through setting. A handler's +pass-through setting is recorded along with the HandlerOp. + +Pass-through handlers are useful for overlay widgets such as a hidden +side drawer. When the user touches the side, both the (transparent) +drawer handle and the interface below should receive pointer events. + +Disambiguation + +When more than one handler matches a pointer event, the input queue +follows a set of rules for distributing the event. + +As long as the pointer has not received a Press event, all +matching handlers receive all events. + +When a pointer is pressed, the set of matching handlers is +recorded. The set is not updated according to the pointer position +and hit areas. Rather, handlers stay in the matching set until they +no longer appear in a HandlerOp or when another handler in the set +grabs the pointer. + +A handler can exclude all other handler from its matching sets +by setting the Grab flag in its HandlerOp. The Grab flag is sticky +and stays in effect until the handler no longer appears in any +matching sets. + +The losing handlers are notified by a Cancel event. + +For multiple grabbing handlers, the foremost handler wins. + +Priorities + +Handlers know their position in a matching set of a pointer through +event priorities. The Shared and Foremost are for matching sets with +multiple handlers; the Grabbed priority indicate exclusive access. + +Priorities are useful for deferred gesture matching. + +Consider a scrollable list of clickable elements. When the user touches an +element, it is unknown whether the gesture is a click on the element +or a drag (scroll) of the list. While the click handler might light up +the element in anticipation of a click, the scrolling handler does not +scroll on finger movements with lower than Grabbed priority. + +Should the user release the finger, the click handler registers a click. + +However, if the finger moves beyond a threshold, the scrolling handler +determines that the gesture is a drag and sets its Grab flag. The +click handler receives a Cancel (removing the highlight) and further +movements for the scroll handler has priority Grabbed, scrolling the +list. +*/ +package pointer diff --git a/ui/pointer/pointer.go b/ui/pointer/pointer.go index 62766513..04645cf1 100644 --- a/ui/pointer/pointer.go +++ b/ui/pointer/pointer.go @@ -13,22 +13,45 @@ import ( "gioui.org/ui/internal/opconst" ) +// Event is a pointer event. type Event struct { - Type Type - Source Source + Type Type + Source Source + // PointerID is the id for the pointer and can be used + // to track a particular pointer from Press to + // Release or Cancel. PointerID ID - Priority Priority - Time time.Duration - Hit bool - Position f32.Point - Scroll f32.Point + // Priority is the priority of the receiving handler + // for this event. + Priority Priority + // Time is when the event was received. The + // timestamp is relative to an undefined base. + Time time.Duration + // Hit is set when the event was within the registered + // area for the handler. Hit can be false when a pointer + // was pressed within the hit area, and then dragged + // outside it. + Hit bool + // Position is the position of the event, relative to + // the current transformation, as set by ui.TransformOp. + Position f32.Point + // Scroll is the scroll amount, if any. + Scroll f32.Point } +// RectAreaOp updates the hit area to the intersection +// of the current hit area with a rectangular area. type RectAreaOp struct { + // Rect defines the rectangle. The current transform + // is applied to it. Rect image.Rectangle } +// EllipseAreaOp updates the hit area to the intersection +// of the current hit area with an elliptical area. type EllipseAreaOp struct { + // Rect is the bounds for the ellipse. The current transform + // is applied to the rectangle. Rect image.Rectangle } @@ -38,40 +61,61 @@ type areaOp struct { rect image.Rectangle } +// HandlerOp declares an input handler ready for pointer +// events. type HandlerOp struct { - Key input.Key + Key input.Key + // Grab, if set, request that the handler get + // Grabbed priority. Grab bool } -// PassOp change the current event pass-through -// setting. +// PassOp sets the pass-through mode. type PassOp struct { Pass bool } type ID uint16 + +// Type of an Event. type Type uint8 + +// Priority of an Event. type Priority uint8 + +// Source of an Event. type Source uint8 // Must match input.areaKind type areaKind uint8 const ( + // Cancel is sent when the current gesture is interrupted + // by other handlers or the system. Cancel Type = iota + // Press of a pointer. Press + // Release of a pointer. Release + // Move of a pointer. Move ) const ( + // Mouse generated event. Mouse Source = iota + // Touch generated event. Touch ) const ( + // Shared priority is for handlers that + // are part of a matching set larger than 1. Shared Priority = iota + // Foremost is like Shared, but the handler is + // the foremost in the matching set. Foremost + // Grabbed is used for matching sets of size 1. Grabbed )