mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
e18db64991
The only known use-case (nested scrolling) now works without special treatment of the foremost handler. Signed-off-by: Elias Naur <mail@eliasnaur.com>
397 lines
10 KiB
Go
397 lines
10 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
package pointer
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"gioui.org/f32"
|
|
"gioui.org/internal/ops"
|
|
"gioui.org/io/event"
|
|
"gioui.org/io/key"
|
|
"gioui.org/op"
|
|
)
|
|
|
|
// Event is a pointer event.
|
|
type Event struct {
|
|
Kind Kind
|
|
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 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
|
|
// Buttons are the set of pressed mouse buttons for this event.
|
|
Buttons Buttons
|
|
// Position is the coordinates of the event in the local coordinate
|
|
// system of the receiving tag. The transformation from global window
|
|
// coordinates to local coordinates is performed by the inverse of
|
|
// the effective transformation of the tag.
|
|
Position f32.Point
|
|
// Scroll is the scroll amount, if any.
|
|
Scroll f32.Point
|
|
// Modifiers is the set of active modifiers when
|
|
// the mouse button was pressed.
|
|
Modifiers key.Modifiers
|
|
}
|
|
|
|
// PassOp sets the pass-through mode. InputOps added while the pass-through
|
|
// mode is set don't block events to siblings.
|
|
type PassOp struct {
|
|
}
|
|
|
|
// PassStack represents a PassOp on the pass stack.
|
|
type PassStack struct {
|
|
ops *ops.Ops
|
|
id ops.StackID
|
|
macroID uint32
|
|
}
|
|
|
|
// Filter matches every [Event] that target the Tag and whose kind is
|
|
// included in Kinds. Note that only tags specified in [event.Op] can
|
|
// be targeted by pointer events.
|
|
type Filter struct {
|
|
Target event.Tag
|
|
// Kinds is a bitwise-or of event types to match.
|
|
Kinds Kind
|
|
// ScrollX and ScrollY constrain the range of scrolling events delivered
|
|
// to Target. Specifically, any Event e delivered to Tag will satisfy
|
|
//
|
|
// ScrollX.Min <= e.Scroll.X <= ScrollX.Max (horizontal axis)
|
|
// ScrollY.Min <= e.Scroll.Y <= ScrollY.Max (vertical axis)
|
|
ScrollX ScrollRange
|
|
ScrollY ScrollRange
|
|
}
|
|
|
|
// ScrollRange describes the range of scrolling distances in an
|
|
// axis.
|
|
type ScrollRange struct {
|
|
Min, Max int
|
|
}
|
|
|
|
// GrabCmd requests a pointer grab on the pointer identified by ID.
|
|
type GrabCmd struct {
|
|
Tag event.Tag
|
|
ID ID
|
|
}
|
|
|
|
type ID uint16
|
|
|
|
// Kind of an Event.
|
|
type Kind uint
|
|
|
|
// Priority of an Event.
|
|
type Priority uint8
|
|
|
|
// Source of an Event.
|
|
type Source uint8
|
|
|
|
// Buttons is a set of mouse buttons
|
|
type Buttons uint8
|
|
|
|
// Cursor denotes a pre-defined cursor shape. Its Add method adds an
|
|
// operation that sets the cursor shape for the current clip area.
|
|
type Cursor byte
|
|
|
|
// The cursors correspond to CSS pointer naming.
|
|
const (
|
|
// CursorDefault is the default cursor.
|
|
CursorDefault Cursor = iota
|
|
// CursorNone hides the cursor. To show it again, use any other cursor.
|
|
CursorNone
|
|
// CursorText is for selecting and inserting text.
|
|
CursorText
|
|
// CursorVerticalText is for selecting and inserting vertical text.
|
|
CursorVerticalText
|
|
// CursorPointer is for a link.
|
|
// Usually displayed as a pointing hand.
|
|
CursorPointer
|
|
// CursorCrosshair is for a precise location.
|
|
CursorCrosshair
|
|
// CursorAllScroll is for indicating scrolling in all directions.
|
|
// Usually displayed as arrows to all four directions.
|
|
CursorAllScroll
|
|
// CursorColResize is for vertical resize.
|
|
// Usually displayed as a vertical bar with arrows pointing east and west.
|
|
CursorColResize
|
|
// CursorRowResize is for horizontal resize.
|
|
// Usually displayed as a horizontal bar with arrows pointing north and south.
|
|
CursorRowResize
|
|
// CursorGrab is for content that can be grabbed (dragged to be moved).
|
|
// Usually displayed as an open hand.
|
|
CursorGrab
|
|
// CursorGrabbing is for content that is being grabbed (dragged to be moved).
|
|
// Usually displayed as a closed hand.
|
|
CursorGrabbing
|
|
// CursorNotAllowed is shown when the request action cannot be carried out.
|
|
// Usually displayed as a circle with a line through.
|
|
CursorNotAllowed
|
|
// CursorWait is shown when the program is busy and user cannot interact.
|
|
// Usually displayed as a hourglass or the system equivalent.
|
|
CursorWait
|
|
// CursorProgress is shown when the program is busy, but the user can still interact.
|
|
// Usually displayed as a default cursor with a hourglass.
|
|
CursorProgress
|
|
// CursorNorthWestResize is for top-left corner resizing.
|
|
// Usually displayed as an arrow towards north-west.
|
|
CursorNorthWestResize
|
|
// CursorNorthEastResize is for top-right corner resizing.
|
|
// Usually displayed as an arrow towards north-east.
|
|
CursorNorthEastResize
|
|
// CursorSouthWestResize is for bottom-left corner resizing.
|
|
// Usually displayed as an arrow towards south-west.
|
|
CursorSouthWestResize
|
|
// CursorSouthEastResize is for bottom-right corner resizing.
|
|
// Usually displayed as an arrow towards south-east.
|
|
CursorSouthEastResize
|
|
// CursorNorthSouth is for top-bottom resizing.
|
|
// Usually displayed as a bi-directional arrow towards north-south.
|
|
CursorNorthSouthResize
|
|
// CursorEastWestResize is for left-right resizing.
|
|
// Usually displayed as a bi-directional arrow towards east-west.
|
|
CursorEastWestResize
|
|
// CursorWestResize is for left resizing.
|
|
// Usually displayed as an arrow towards west.
|
|
CursorWestResize
|
|
// CursorEastResize is for right resizing.
|
|
// Usually displayed as an arrow towards east.
|
|
CursorEastResize
|
|
// CursorNorthResize is for top resizing.
|
|
// Usually displayed as an arrow towards north.
|
|
CursorNorthResize
|
|
// CursorSouthResize is for bottom resizing.
|
|
// Usually displayed as an arrow towards south.
|
|
CursorSouthResize
|
|
// CursorNorthEastSouthWestResize is for top-right to bottom-left diagonal resizing.
|
|
// Usually displayed as a double ended arrow on the corresponding diagonal.
|
|
CursorNorthEastSouthWestResize
|
|
// CursorNorthWestSouthEastResize is for top-left to bottom-right diagonal resizing.
|
|
// Usually displayed as a double ended arrow on the corresponding diagonal.
|
|
CursorNorthWestSouthEastResize
|
|
)
|
|
|
|
const (
|
|
// A Cancel event is generated when the current gesture is
|
|
// interrupted by other handlers or the system.
|
|
Cancel Kind = 1 << iota
|
|
// Press of a pointer.
|
|
Press
|
|
// Release of a pointer.
|
|
Release
|
|
// Move of a pointer.
|
|
Move
|
|
// Drag of a pointer.
|
|
Drag
|
|
// Pointer enters an area watching for pointer input
|
|
Enter
|
|
// Pointer leaves an area watching for pointer input
|
|
Leave
|
|
// Scroll of a pointer.
|
|
Scroll
|
|
)
|
|
|
|
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
|
|
// Grabbed is used for matching sets of size 1.
|
|
Grabbed
|
|
)
|
|
|
|
const (
|
|
// ButtonPrimary is the primary button, usually the left button for a
|
|
// right-handed user.
|
|
ButtonPrimary Buttons = 1 << iota
|
|
// ButtonSecondary is the secondary button, usually the right button for a
|
|
// right-handed user.
|
|
ButtonSecondary
|
|
// ButtonTertiary is the tertiary button, usually the middle button.
|
|
ButtonTertiary
|
|
)
|
|
|
|
func (s ScrollRange) Union(s2 ScrollRange) ScrollRange {
|
|
return ScrollRange{
|
|
Min: min(s.Min, s2.Min),
|
|
Max: max(s.Max, s2.Max),
|
|
}
|
|
}
|
|
|
|
// Push the current pass mode to the pass stack and set the pass mode.
|
|
func (p PassOp) Push(o *op.Ops) PassStack {
|
|
id, mid := ops.PushOp(&o.Internal, ops.PassStack)
|
|
data := ops.Write(&o.Internal, ops.TypePassLen)
|
|
data[0] = byte(ops.TypePass)
|
|
return PassStack{ops: &o.Internal, id: id, macroID: mid}
|
|
}
|
|
|
|
func (p PassStack) Pop() {
|
|
ops.PopOp(p.ops, ops.PassStack, p.id, p.macroID)
|
|
data := ops.Write(p.ops, ops.TypePopPassLen)
|
|
data[0] = byte(ops.TypePopPass)
|
|
}
|
|
|
|
func (op Cursor) Add(o *op.Ops) {
|
|
data := ops.Write(&o.Internal, ops.TypeCursorLen)
|
|
data[0] = byte(ops.TypeCursor)
|
|
data[1] = byte(op)
|
|
}
|
|
|
|
func (t Kind) String() string {
|
|
if t == Cancel {
|
|
return "Cancel"
|
|
}
|
|
var buf strings.Builder
|
|
for tt := Kind(1); tt > 0; tt <<= 1 {
|
|
if t&tt > 0 {
|
|
if buf.Len() > 0 {
|
|
buf.WriteByte('|')
|
|
}
|
|
buf.WriteString((t & tt).string())
|
|
}
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
func (t Kind) string() string {
|
|
switch t {
|
|
case Press:
|
|
return "Press"
|
|
case Release:
|
|
return "Release"
|
|
case Cancel:
|
|
return "Cancel"
|
|
case Move:
|
|
return "Move"
|
|
case Drag:
|
|
return "Drag"
|
|
case Enter:
|
|
return "Enter"
|
|
case Leave:
|
|
return "Leave"
|
|
case Scroll:
|
|
return "Scroll"
|
|
default:
|
|
panic("unknown Type")
|
|
}
|
|
}
|
|
|
|
func (p Priority) String() string {
|
|
switch p {
|
|
case Shared:
|
|
return "Shared"
|
|
case Grabbed:
|
|
return "Grabbed"
|
|
default:
|
|
panic("unknown priority")
|
|
}
|
|
}
|
|
|
|
func (s Source) String() string {
|
|
switch s {
|
|
case Mouse:
|
|
return "Mouse"
|
|
case Touch:
|
|
return "Touch"
|
|
default:
|
|
panic("unknown source")
|
|
}
|
|
}
|
|
|
|
// Contain reports whether the set b contains
|
|
// all of the buttons.
|
|
func (b Buttons) Contain(buttons Buttons) bool {
|
|
return b&buttons == buttons
|
|
}
|
|
|
|
func (b Buttons) String() string {
|
|
var strs []string
|
|
if b.Contain(ButtonPrimary) {
|
|
strs = append(strs, "ButtonPrimary")
|
|
}
|
|
if b.Contain(ButtonSecondary) {
|
|
strs = append(strs, "ButtonSecondary")
|
|
}
|
|
if b.Contain(ButtonTertiary) {
|
|
strs = append(strs, "ButtonTertiary")
|
|
}
|
|
return strings.Join(strs, "|")
|
|
}
|
|
|
|
func (c Cursor) String() string {
|
|
switch c {
|
|
case CursorDefault:
|
|
return "Default"
|
|
case CursorNone:
|
|
return "None"
|
|
case CursorText:
|
|
return "Text"
|
|
case CursorVerticalText:
|
|
return "VerticalText"
|
|
case CursorPointer:
|
|
return "Pointer"
|
|
case CursorCrosshair:
|
|
return "Crosshair"
|
|
case CursorAllScroll:
|
|
return "AllScroll"
|
|
case CursorColResize:
|
|
return "ColResize"
|
|
case CursorRowResize:
|
|
return "RowResize"
|
|
case CursorGrab:
|
|
return "Grab"
|
|
case CursorGrabbing:
|
|
return "Grabbing"
|
|
case CursorNotAllowed:
|
|
return "NotAllowed"
|
|
case CursorWait:
|
|
return "Wait"
|
|
case CursorProgress:
|
|
return "Progress"
|
|
case CursorNorthWestResize:
|
|
return "NorthWestResize"
|
|
case CursorNorthEastResize:
|
|
return "NorthEastResize"
|
|
case CursorSouthWestResize:
|
|
return "SouthWestResize"
|
|
case CursorSouthEastResize:
|
|
return "SouthEastResize"
|
|
case CursorNorthSouthResize:
|
|
return "NorthSouthResize"
|
|
case CursorEastWestResize:
|
|
return "EastWestResize"
|
|
case CursorWestResize:
|
|
return "WestResize"
|
|
case CursorEastResize:
|
|
return "EastResize"
|
|
case CursorNorthResize:
|
|
return "NorthResize"
|
|
case CursorSouthResize:
|
|
return "SouthResize"
|
|
case CursorNorthEastSouthWestResize:
|
|
return "NorthEastSouthWestResize"
|
|
case CursorNorthWestSouthEastResize:
|
|
return "NorthWestSouthEastResize"
|
|
default:
|
|
panic("unknown Type")
|
|
}
|
|
}
|
|
|
|
func (Event) ImplementsEvent() {}
|
|
|
|
func (GrabCmd) ImplementsCommand() {}
|
|
|
|
func (Filter) ImplementsFilter() {}
|