pointer.go raw

   1  // SPDX-License-Identifier: Unlicense OR MIT
   2  
   3  package pointer
   4  
   5  import (
   6  	"encoding/binary"
   7  	"fmt"
   8  	"image"
   9  	"strings"
  10  	"time"
  11  
  12  	"github.com/p9c/p9/pkg/gel/gio/f32"
  13  	"github.com/p9c/p9/pkg/gel/gio/internal/opconst"
  14  	"github.com/p9c/p9/pkg/gel/gio/io/event"
  15  	"github.com/p9c/p9/pkg/gel/gio/io/key"
  16  	"github.com/p9c/p9/pkg/gel/gio/op"
  17  )
  18  
  19  // Event is a pointer event.
  20  type Event struct {
  21  	Type   Type
  22  	Source Source
  23  	// PointerID is the id for the pointer and can be used
  24  	// to track a particular pointer from Press to
  25  	// Release or Cancel.
  26  	PointerID ID
  27  	// Priority is the priority of the receiving handler
  28  	// for this event.
  29  	Priority Priority
  30  	// Time is when the event was received. The
  31  	// timestamp is relative to an undefined base.
  32  	Time time.Duration
  33  	// Buttons are the set of pressed mouse buttons for this event.
  34  	Buttons Buttons
  35  	// Position is the position of the event, relative to
  36  	// the current transformation, as set by op.TransformOp.
  37  	Position f32.Point
  38  	// Scroll is the scroll amount, if any.
  39  	Scroll f32.Point
  40  	// Modifiers is the set of active modifiers when
  41  	// the mouse button was pressed.
  42  	Modifiers key.Modifiers
  43  }
  44  
  45  // AreaOp updates the hit area to the intersection of the current
  46  // hit area and the area. The area is transformed before applying
  47  // it.
  48  type AreaOp struct {
  49  	kind areaKind
  50  	rect image.Rectangle
  51  }
  52  
  53  // CursorNameOp sets the cursor for the current area.
  54  type CursorNameOp struct {
  55  	Name CursorName
  56  }
  57  
  58  // InputOp declares an input handler ready for pointer
  59  // events.
  60  type InputOp struct {
  61  	Tag event.Tag
  62  	// Grab, if set, request that the handler get
  63  	// Grabbed priority.
  64  	Grab bool
  65  	// Types is a bitwise-or of event types to receive.
  66  	Types Type
  67  	// ScrollBounds describe the maximum scrollable distances in both
  68  	// axes. Specifically, any Event e delivered to Tag will satisfy
  69  	//
  70  	// ScrollBounds.Min.X <= e.Scroll.X <= ScrollBounds.Max.X (horizontal axis)
  71  	// ScrollBounds.Min.Y <= e.Scroll.Y <= ScrollBounds.Max.Y (vertical axis)
  72  	ScrollBounds image.Rectangle
  73  }
  74  
  75  // PassOp sets the pass-through mode.
  76  type PassOp struct {
  77  	Pass bool
  78  }
  79  
  80  type ID uint16
  81  
  82  // Type of an Event.
  83  type Type uint8
  84  
  85  // Priority of an Event.
  86  type Priority uint8
  87  
  88  // Source of an Event.
  89  type Source uint8
  90  
  91  // Buttons is a set of mouse buttons
  92  type Buttons uint8
  93  
  94  // CursorName is the name of a cursor.
  95  type CursorName string
  96  
  97  // Must match app/internal/input.areaKind
  98  type areaKind uint8
  99  
 100  const (
 101  	// CursorDefault is the default cursor.
 102  	CursorDefault CursorName = ""
 103  	// CursorText is the cursor for text.
 104  	CursorText CursorName = "text"
 105  	// CursorPointer is the cursor for a link.
 106  	CursorPointer CursorName = "pointer"
 107  	// CursorCrossHair is the cursor for precise location.
 108  	CursorCrossHair CursorName = "crosshair"
 109  	// CursorColResize is the cursor for vertical resize.
 110  	CursorColResize CursorName = "col-resize"
 111  	// CursorRowResize is the cursor for horizontal resize.
 112  	CursorRowResize CursorName = "row-resize"
 113  	// CursorGrab is the cursor for moving object in any direction.
 114  	CursorGrab CursorName = "grab"
 115  	// CursorNone hides the cursor. To show it again, use any other cursor.
 116  	CursorNone CursorName = "none"
 117  )
 118  
 119  const (
 120  	// A Cancel event is generated when the current gesture is
 121  	// interrupted by other handlers or the system.
 122  	Cancel Type = (1 << iota) >> 1
 123  	// Press of a pointer.
 124  	Press
 125  	// Release of a pointer.
 126  	Release
 127  	// Move of a pointer.
 128  	Move
 129  	// Drag of a pointer.
 130  	Drag
 131  	// Pointer enters an area watching for pointer input
 132  	Enter
 133  	// Pointer leaves an area watching for pointer input
 134  	Leave
 135  	// Scroll of a pointer.
 136  	Scroll
 137  )
 138  
 139  const (
 140  	// Mouse generated event.
 141  	Mouse Source = iota
 142  	// Touch generated event.
 143  	Touch
 144  )
 145  
 146  const (
 147  	// Shared priority is for handlers that
 148  	// are part of a matching set larger than 1.
 149  	Shared Priority = iota
 150  	// Foremost priority is like Shared, but the
 151  	// handler is the foremost of the matching set.
 152  	Foremost
 153  	// Grabbed is used for matching sets of size 1.
 154  	Grabbed
 155  )
 156  
 157  const (
 158  	// ButtonPrimary is the primary button, usually the left button for a
 159  	// right-handed user.
 160  	ButtonPrimary Buttons = 1 << iota
 161  	// ButtonSecondary is the secondary button, usually the right button for a
 162  	// right-handed user.
 163  	ButtonSecondary
 164  	// ButtonTertiary is the tertiary button, usually the middle button.
 165  	ButtonTertiary
 166  )
 167  
 168  const (
 169  	areaRect areaKind = iota
 170  	areaEllipse
 171  )
 172  
 173  // Rect constructs a rectangular hit area.
 174  func Rect(size image.Rectangle) AreaOp {
 175  	return AreaOp{
 176  		kind: areaRect,
 177  		rect: size,
 178  	}
 179  }
 180  
 181  // Ellipse constructs an ellipsoid hit area.
 182  func Ellipse(size image.Rectangle) AreaOp {
 183  	return AreaOp{
 184  		kind: areaEllipse,
 185  		rect: size,
 186  	}
 187  }
 188  
 189  func (op AreaOp) Add(o *op.Ops) {
 190  	data := o.Write(opconst.TypeAreaLen)
 191  	data[0] = byte(opconst.TypeArea)
 192  	data[1] = byte(op.kind)
 193  	bo := binary.LittleEndian
 194  	bo.PutUint32(data[2:], uint32(op.rect.Min.X))
 195  	bo.PutUint32(data[6:], uint32(op.rect.Min.Y))
 196  	bo.PutUint32(data[10:], uint32(op.rect.Max.X))
 197  	bo.PutUint32(data[14:], uint32(op.rect.Max.Y))
 198  }
 199  
 200  func (op CursorNameOp) Add(o *op.Ops) {
 201  	data := o.Write1(opconst.TypeCursorLen, op.Name)
 202  	data[0] = byte(opconst.TypeCursor)
 203  }
 204  
 205  // Add panics if the scroll range does not contain zero.
 206  func (op InputOp) Add(o *op.Ops) {
 207  	if op.Tag == nil {
 208  		panic("Tag must be non-nil")
 209  	}
 210  	if b := op.ScrollBounds; b.Min.X > 0 || b.Max.X < 0 || b.Min.Y > 0 || b.Max.Y < 0 {
 211  		panic(fmt.Errorf("invalid scroll range value %v", b))
 212  	}
 213  	data := o.Write1(opconst.TypePointerInputLen, op.Tag)
 214  	data[0] = byte(opconst.TypePointerInput)
 215  	if op.Grab {
 216  		data[1] = 1
 217  	}
 218  	data[2] = byte(op.Types)
 219  	bo := binary.LittleEndian
 220  	bo.PutUint32(data[3:], uint32(op.ScrollBounds.Min.X))
 221  	bo.PutUint32(data[7:], uint32(op.ScrollBounds.Min.Y))
 222  	bo.PutUint32(data[11:], uint32(op.ScrollBounds.Max.X))
 223  	bo.PutUint32(data[15:], uint32(op.ScrollBounds.Max.Y))
 224  }
 225  
 226  func (op PassOp) Add(o *op.Ops) {
 227  	data := o.Write(opconst.TypePassLen)
 228  	data[0] = byte(opconst.TypePass)
 229  	if op.Pass {
 230  		data[1] = 1
 231  	}
 232  }
 233  
 234  func (t Type) String() string {
 235  	switch t {
 236  	case Press:
 237  		return "Press"
 238  	case Release:
 239  		return "Release"
 240  	case Cancel:
 241  		return "Cancel"
 242  	case Move:
 243  		return "Move"
 244  	case Drag:
 245  		return "Drag"
 246  	case Enter:
 247  		return "Enter"
 248  	case Leave:
 249  		return "Leave"
 250  	case Scroll:
 251  		return "Scroll"
 252  	default:
 253  		panic("unknown Type")
 254  	}
 255  }
 256  
 257  func (p Priority) String() string {
 258  	switch p {
 259  	case Shared:
 260  		return "Shared"
 261  	case Foremost:
 262  		return "Foremost"
 263  	case Grabbed:
 264  		return "Grabbed"
 265  	default:
 266  		panic("unknown priority")
 267  	}
 268  }
 269  
 270  func (s Source) String() string {
 271  	switch s {
 272  	case Mouse:
 273  		return "Mouse"
 274  	case Touch:
 275  		return "Touch"
 276  	default:
 277  		panic("unknown source")
 278  	}
 279  }
 280  
 281  // Contain reports whether the set b contains
 282  // all of the buttons.
 283  func (b Buttons) Contain(buttons Buttons) bool {
 284  	return b&buttons == buttons
 285  }
 286  
 287  func (b Buttons) String() string {
 288  	var strs []string
 289  	if b.Contain(ButtonPrimary) {
 290  		strs = append(strs, "ButtonPrimary")
 291  	}
 292  	if b.Contain(ButtonSecondary) {
 293  		strs = append(strs, "ButtonSecondary")
 294  	}
 295  	if b.Contain(ButtonTertiary) {
 296  		strs = append(strs, "ButtonTertiary")
 297  	}
 298  	return strings.Join(strs, "|")
 299  }
 300  
 301  func (c CursorName) String() string {
 302  	if c == CursorDefault {
 303  		return "default"
 304  	}
 305  	return string(c)
 306  }
 307  
 308  func (Event) ImplementsEvent() {}
 309