gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/io/pointer/pointer.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package pointer
     4  
     5  import (
     6  	"strings"
     7  	"time"
     8  
     9  	"gioui.org/f32"
    10  	"gioui.org/internal/ops"
    11  	"gioui.org/io/event"
    12  	"gioui.org/io/key"
    13  	"gioui.org/op"
    14  )
    15  
    16  // Event is a pointer event.
    17  type Event struct {
    18  	Kind   Kind
    19  	Source Source
    20  	// PointerID is the id for the pointer and can be used
    21  	// to track a particular pointer from Press to
    22  	// Release or Cancel.
    23  	PointerID ID
    24  	// Priority is the priority of the receiving handler
    25  	// for this event.
    26  	Priority Priority
    27  	// Time is when the event was received. The
    28  	// timestamp is relative to an undefined base.
    29  	Time time.Duration
    30  	// Buttons are the set of pressed mouse buttons for this event.
    31  	Buttons Buttons
    32  	// Position is the coordinates of the event in the local coordinate
    33  	// system of the receiving tag. The transformation from global window
    34  	// coordinates to local coordinates is performed by the inverse of
    35  	// the effective transformation of the tag.
    36  	Position f32.Point
    37  	// Scroll is the scroll amount, if any.
    38  	Scroll f32.Point
    39  	// Modifiers is the set of active modifiers when
    40  	// the mouse button was pressed.
    41  	Modifiers key.Modifiers
    42  }
    43  
    44  // PassOp sets the pass-through mode. InputOps added while the pass-through
    45  // mode is set don't block events to siblings.
    46  type PassOp struct {
    47  }
    48  
    49  // PassStack represents a PassOp on the pass stack.
    50  type PassStack struct {
    51  	ops     *ops.Ops
    52  	id      ops.StackID
    53  	macroID uint32
    54  }
    55  
    56  // Filter matches every [Event] that target the Tag and whose kind is
    57  // included in Kinds. Note that only tags specified in [event.Op] can
    58  // be targeted by pointer events.
    59  type Filter struct {
    60  	Target event.Tag
    61  	// Kinds is a bitwise-or of event types to match.
    62  	Kinds Kind
    63  	// ScrollX and ScrollY constrain the range of scrolling events delivered
    64  	// to Target. Specifically, any Event e delivered to Tag will satisfy
    65  	//
    66  	// ScrollX.Min <= e.Scroll.X <= ScrollX.Max (horizontal axis)
    67  	// ScrollY.Min <= e.Scroll.Y <= ScrollY.Max (vertical axis)
    68  	ScrollX ScrollRange
    69  	ScrollY ScrollRange
    70  }
    71  
    72  // ScrollRange describes the range of scrolling distances in an
    73  // axis.
    74  type ScrollRange struct {
    75  	Min, Max int
    76  }
    77  
    78  // GrabCmd requests a pointer grab on the pointer identified by ID.
    79  type GrabCmd struct {
    80  	Tag event.Tag
    81  	ID  ID
    82  }
    83  
    84  type ID uint16
    85  
    86  // Kind of an Event.
    87  type Kind uint
    88  
    89  // Priority of an Event.
    90  type Priority uint8
    91  
    92  // Source of an Event.
    93  type Source uint8
    94  
    95  // Buttons is a set of mouse buttons
    96  type Buttons uint8
    97  
    98  // Cursor denotes a pre-defined cursor shape. Its Add method adds an
    99  // operation that sets the cursor shape for the current clip area.
   100  type Cursor byte
   101  
   102  // The cursors correspond to CSS pointer naming.
   103  const (
   104  	// CursorDefault is the default cursor.
   105  	CursorDefault Cursor = iota
   106  	// CursorNone hides the cursor. To show it again, use any other cursor.
   107  	CursorNone
   108  	// CursorText is for selecting and inserting text.
   109  	CursorText
   110  	// CursorVerticalText is for selecting and inserting vertical text.
   111  	CursorVerticalText
   112  	// CursorPointer is for a link.
   113  	// Usually displayed as a pointing hand.
   114  	CursorPointer
   115  	// CursorCrosshair is for a precise location.
   116  	CursorCrosshair
   117  	// CursorAllScroll is for indicating scrolling in all directions.
   118  	// Usually displayed as arrows to all four directions.
   119  	CursorAllScroll
   120  	// CursorColResize is for vertical resize.
   121  	// Usually displayed as a vertical bar with arrows pointing east and west.
   122  	CursorColResize
   123  	// CursorRowResize is for horizontal resize.
   124  	// Usually displayed as a horizontal bar with arrows pointing north and south.
   125  	CursorRowResize
   126  	// CursorGrab is for content that can be grabbed (dragged to be moved).
   127  	// Usually displayed as an open hand.
   128  	CursorGrab
   129  	// CursorGrabbing is for content that is being grabbed (dragged to be moved).
   130  	// Usually displayed as a closed hand.
   131  	CursorGrabbing
   132  	// CursorNotAllowed is shown when the request action cannot be carried out.
   133  	// Usually displayed as a circle with a line through.
   134  	CursorNotAllowed
   135  	// CursorWait is shown when the program is busy and user cannot interact.
   136  	// Usually displayed as a hourglass or the system equivalent.
   137  	CursorWait
   138  	// CursorProgress is shown when the program is busy, but the user can still interact.
   139  	// Usually displayed as a default cursor with a hourglass.
   140  	CursorProgress
   141  	// CursorNorthWestResize is for top-left corner resizing.
   142  	// Usually displayed as an arrow towards north-west.
   143  	CursorNorthWestResize
   144  	// CursorNorthEastResize is for top-right corner resizing.
   145  	// Usually displayed as an arrow towards north-east.
   146  	CursorNorthEastResize
   147  	// CursorSouthWestResize is for bottom-left corner resizing.
   148  	// Usually displayed as an arrow towards south-west.
   149  	CursorSouthWestResize
   150  	// CursorSouthEastResize is for bottom-right corner resizing.
   151  	// Usually displayed as an arrow towards south-east.
   152  	CursorSouthEastResize
   153  	// CursorNorthSouth is for top-bottom resizing.
   154  	// Usually displayed as a bi-directional arrow towards north-south.
   155  	CursorNorthSouthResize
   156  	// CursorEastWestResize is for left-right resizing.
   157  	// Usually displayed as a bi-directional arrow towards east-west.
   158  	CursorEastWestResize
   159  	// CursorWestResize is for left resizing.
   160  	// Usually displayed as an arrow towards west.
   161  	CursorWestResize
   162  	// CursorEastResize is for right resizing.
   163  	// Usually displayed as an arrow towards east.
   164  	CursorEastResize
   165  	// CursorNorthResize is for top resizing.
   166  	// Usually displayed as an arrow towards north.
   167  	CursorNorthResize
   168  	// CursorSouthResize is for bottom resizing.
   169  	// Usually displayed as an arrow towards south.
   170  	CursorSouthResize
   171  	// CursorNorthEastSouthWestResize is for top-right to bottom-left diagonal resizing.
   172  	// Usually displayed as a double ended arrow on the corresponding diagonal.
   173  	CursorNorthEastSouthWestResize
   174  	// CursorNorthWestSouthEastResize is for top-left to bottom-right diagonal resizing.
   175  	// Usually displayed as a double ended arrow on the corresponding diagonal.
   176  	CursorNorthWestSouthEastResize
   177  )
   178  
   179  const (
   180  	// A Cancel event is generated when the current gesture is
   181  	// interrupted by other handlers or the system.
   182  	Cancel Kind = 1 << iota
   183  	// Press of a pointer.
   184  	Press
   185  	// Release of a pointer.
   186  	Release
   187  	// Move of a pointer.
   188  	Move
   189  	// Drag of a pointer.
   190  	Drag
   191  	// Pointer enters an area watching for pointer input
   192  	Enter
   193  	// Pointer leaves an area watching for pointer input
   194  	Leave
   195  	// Scroll of a pointer.
   196  	Scroll
   197  )
   198  
   199  const (
   200  	// Mouse generated event.
   201  	Mouse Source = iota
   202  	// Touch generated event.
   203  	Touch
   204  )
   205  
   206  const (
   207  	// Shared priority is for handlers that
   208  	// are part of a matching set larger than 1.
   209  	Shared Priority = iota
   210  	// Foremost priority is like Shared, but the
   211  	// handler is the foremost of the matching set.
   212  	Foremost
   213  	// Grabbed is used for matching sets of size 1.
   214  	Grabbed
   215  )
   216  
   217  const (
   218  	// ButtonPrimary is the primary button, usually the left button for a
   219  	// right-handed user.
   220  	ButtonPrimary Buttons = 1 << iota
   221  	// ButtonSecondary is the secondary button, usually the right button for a
   222  	// right-handed user.
   223  	ButtonSecondary
   224  	// ButtonTertiary is the tertiary button, usually the middle button.
   225  	ButtonTertiary
   226  )
   227  
   228  func (s ScrollRange) Union(s2 ScrollRange) ScrollRange {
   229  	return ScrollRange{
   230  		Min: min(s.Min, s2.Min),
   231  		Max: max(s.Max, s2.Max),
   232  	}
   233  }
   234  
   235  // Push the current pass mode to the pass stack and set the pass mode.
   236  func (p PassOp) Push(o *op.Ops) PassStack {
   237  	id, mid := ops.PushOp(&o.Internal, ops.PassStack)
   238  	data := ops.Write(&o.Internal, ops.TypePassLen)
   239  	data[0] = byte(ops.TypePass)
   240  	return PassStack{ops: &o.Internal, id: id, macroID: mid}
   241  }
   242  
   243  func (p PassStack) Pop() {
   244  	ops.PopOp(p.ops, ops.PassStack, p.id, p.macroID)
   245  	data := ops.Write(p.ops, ops.TypePopPassLen)
   246  	data[0] = byte(ops.TypePopPass)
   247  }
   248  
   249  func (op Cursor) Add(o *op.Ops) {
   250  	data := ops.Write(&o.Internal, ops.TypeCursorLen)
   251  	data[0] = byte(ops.TypeCursor)
   252  	data[1] = byte(op)
   253  }
   254  
   255  func (t Kind) String() string {
   256  	if t == Cancel {
   257  		return "Cancel"
   258  	}
   259  	var buf strings.Builder
   260  	for tt := Kind(1); tt > 0; tt <<= 1 {
   261  		if t&tt > 0 {
   262  			if buf.Len() > 0 {
   263  				buf.WriteByte('|')
   264  			}
   265  			buf.WriteString((t & tt).string())
   266  		}
   267  	}
   268  	return buf.String()
   269  }
   270  
   271  func (t Kind) string() string {
   272  	switch t {
   273  	case Press:
   274  		return "Press"
   275  	case Release:
   276  		return "Release"
   277  	case Cancel:
   278  		return "Cancel"
   279  	case Move:
   280  		return "Move"
   281  	case Drag:
   282  		return "Drag"
   283  	case Enter:
   284  		return "Enter"
   285  	case Leave:
   286  		return "Leave"
   287  	case Scroll:
   288  		return "Scroll"
   289  	default:
   290  		panic("unknown Type")
   291  	}
   292  }
   293  
   294  func (p Priority) String() string {
   295  	switch p {
   296  	case Shared:
   297  		return "Shared"
   298  	case Foremost:
   299  		return "Foremost"
   300  	case Grabbed:
   301  		return "Grabbed"
   302  	default:
   303  		panic("unknown priority")
   304  	}
   305  }
   306  
   307  func (s Source) String() string {
   308  	switch s {
   309  	case Mouse:
   310  		return "Mouse"
   311  	case Touch:
   312  		return "Touch"
   313  	default:
   314  		panic("unknown source")
   315  	}
   316  }
   317  
   318  // Contain reports whether the set b contains
   319  // all of the buttons.
   320  func (b Buttons) Contain(buttons Buttons) bool {
   321  	return b&buttons == buttons
   322  }
   323  
   324  func (b Buttons) String() string {
   325  	var strs []string
   326  	if b.Contain(ButtonPrimary) {
   327  		strs = append(strs, "ButtonPrimary")
   328  	}
   329  	if b.Contain(ButtonSecondary) {
   330  		strs = append(strs, "ButtonSecondary")
   331  	}
   332  	if b.Contain(ButtonTertiary) {
   333  		strs = append(strs, "ButtonTertiary")
   334  	}
   335  	return strings.Join(strs, "|")
   336  }
   337  
   338  func (c Cursor) String() string {
   339  	switch c {
   340  	case CursorDefault:
   341  		return "Default"
   342  	case CursorNone:
   343  		return "None"
   344  	case CursorText:
   345  		return "Text"
   346  	case CursorVerticalText:
   347  		return "VerticalText"
   348  	case CursorPointer:
   349  		return "Pointer"
   350  	case CursorCrosshair:
   351  		return "Crosshair"
   352  	case CursorAllScroll:
   353  		return "AllScroll"
   354  	case CursorColResize:
   355  		return "ColResize"
   356  	case CursorRowResize:
   357  		return "RowResize"
   358  	case CursorGrab:
   359  		return "Grab"
   360  	case CursorGrabbing:
   361  		return "Grabbing"
   362  	case CursorNotAllowed:
   363  		return "NotAllowed"
   364  	case CursorWait:
   365  		return "Wait"
   366  	case CursorProgress:
   367  		return "Progress"
   368  	case CursorNorthWestResize:
   369  		return "NorthWestResize"
   370  	case CursorNorthEastResize:
   371  		return "NorthEastResize"
   372  	case CursorSouthWestResize:
   373  		return "SouthWestResize"
   374  	case CursorSouthEastResize:
   375  		return "SouthEastResize"
   376  	case CursorNorthSouthResize:
   377  		return "NorthSouthResize"
   378  	case CursorEastWestResize:
   379  		return "EastWestResize"
   380  	case CursorWestResize:
   381  		return "WestResize"
   382  	case CursorEastResize:
   383  		return "EastResize"
   384  	case CursorNorthResize:
   385  		return "NorthResize"
   386  	case CursorSouthResize:
   387  		return "SouthResize"
   388  	case CursorNorthEastSouthWestResize:
   389  		return "NorthEastSouthWestResize"
   390  	case CursorNorthWestSouthEastResize:
   391  		return "NorthWestSouthEastResize"
   392  	default:
   393  		panic("unknown Type")
   394  	}
   395  }
   396  
   397  func (Event) ImplementsEvent() {}
   398  
   399  func (GrabCmd) ImplementsCommand() {}
   400  
   401  func (Filter) ImplementsFilter() {}