github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/io/pointer/pointer.go (about)

     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/cybriq/giocore/f32"
    13  	"github.com/cybriq/giocore/internal/opconst"
    14  	"github.com/cybriq/giocore/io/event"
    15  	"github.com/cybriq/giocore/io/key"
    16  	"github.com/cybriq/giocore/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() {}