Skip to content

Commit

Permalink
add experimental MouseDrag event
Browse files Browse the repository at this point in the history
An example is included.
  • Loading branch information
quasilyte committed Aug 2, 2024
1 parent d638ebd commit 5287a1e
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 5 deletions.
98 changes: 98 additions & 0 deletions _examples/mousedrag/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//go:build example

package main

import (
"fmt"
"log"

"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
input "github.com/quasilyte/ebitengine-input"
)

const (
ActionUnknown input.Action = iota
ActionDrag
ActionClick
)

func main() {
ebiten.SetWindowSize(640, 480)

if err := ebiten.RunGame(newExampleGame()); err != nil {
log.Fatal(err)
}
}

type exampleGame struct {
started bool

pos input.Vec
fallbackPos input.Vec

numDrags int
numClicks int

inputHandler *input.Handler
inputSystem input.System
}

func newExampleGame() *exampleGame {
g := &exampleGame{}

g.inputSystem.Init(input.SystemConfig{
DevicesEnabled: input.AnyDevice,
})

return g
}

func (g *exampleGame) Layout(_, _ int) (int, int) {
return 640, 480
}

func (g *exampleGame) Draw(screen *ebiten.Image) {
ebitenutil.DebugPrint(screen,
fmt.Sprintf("use drag to move the star\nnum drags: %d\nnum clicks: %d", g.numDrags, g.numClicks))
ebitenutil.DebugPrintAt(screen, "*", int(g.pos.X), int(g.pos.Y))
}

func (g *exampleGame) Update() error {
g.inputSystem.Update()

if !g.started {
g.Init()
g.started = true
}

if g.inputHandler.ActionIsJustPressed(ActionClick) {
g.numClicks++
}

if info, ok := g.inputHandler.JustPressedActionInfo(ActionDrag); ok {
// Start dragging.
g.numDrags++
g.fallbackPos = info.StartPos
} else if info, ok := g.inputHandler.PressedActionInfo(ActionDrag); ok {
// Continue dragging.
g.pos = info.Pos
} else {
// Not being dragged.
g.pos = g.fallbackPos
}

return nil
}

func (g *exampleGame) Init() {
g.pos = input.Vec{X: 200, Y: 200}
g.fallbackPos = g.pos

keymap := input.Keymap{
ActionDrag: {input.KeyMouseLeftDrag},
ActionClick: {input.KeyMouseLeft},
}

g.inputHandler = g.inputSystem.NewHandler(0, keymap)
}
13 changes: 11 additions & 2 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func (h *Handler) EmitEvent(e SimulatedAction) {
// This function is useful for "press any key" contexts where you
// don't care which key was used to trigger the event.
//
// This method does respects simulated inputs.
// This method does respect simulated inputs.
//
// This method does not support gamepad pseudo-keys like KeyGamepadLStickUp.
func (h *Handler) AnyKeyJustPressed() bool {
Expand Down Expand Up @@ -474,6 +474,8 @@ func (h *Handler) keyIsJustPressed(k Key) bool {
return false
case keyTouchDrag:
return h.sys.touchJustHadDrag
case keyMouseDrag:
return h.sys.mouseJustHadDrag
case keyGamepad:
return h.gamepadKeyIsJustPressed(k)
case keyGamepadLeftStick:
Expand Down Expand Up @@ -523,8 +525,11 @@ func (h *Handler) keyIsJustPressed(k Key) bool {

func (h *Handler) getKeyStartPos(k Key) Vec {
var result Vec
if k.kind == keyTouchDrag {
switch k.kind {
case keyTouchDrag:
result = h.sys.touchStartPos
case keyMouseDrag:
result = h.sys.mouseStartPos
}
return result
}
Expand All @@ -538,6 +543,8 @@ func (h *Handler) getKeyPos(k Key) Vec {
result = h.sys.touchTapPos
case keyTouchDrag:
result = h.sys.touchDragPos
case keyMouseDrag:
result = h.sys.mouseDragPos
case keyWheel, keyWheelWithCtrl, keyWheelWithShift, keyWheelWithCtrlShift:
result = h.sys.wheel
case keyGamepadStickMotion:
Expand Down Expand Up @@ -580,6 +587,8 @@ func (h *Handler) keyIsPressed(k Key) bool {
return false
case keyTouchDrag:
return h.sys.touchHasDrag
case keyMouseDrag:
return h.sys.mouseHasDrag
case keyGamepad:
return h.gamepadKeyIsPressed(k)
case keyGamepadLeftStick:
Expand Down
2 changes: 2 additions & 0 deletions internal_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const (
keyMouseWithCtrl
keyMouseWithShift
keyMouseWithCtrlShift
keyMouseDrag
keyTouch
keyTouchDrag
keyWheel
Expand Down Expand Up @@ -83,6 +84,7 @@ var keyKindFlagTable = [256]keyKindFlag{
keyMouseWithCtrl: keyFlagHasPos,
keyMouseWithShift: keyFlagHasPos,
keyMouseWithCtrlShift: keyFlagHasPos,
keyMouseDrag: keyFlagHasPos,
keyTouch: keyFlagHasPos,
keyWheel: keyFlagHasPos,
keyWheelWithCtrl: keyFlagHasPos,
Expand Down
1 change: 1 addition & 0 deletions internal_key_list.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions key.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ var (
KeyMouseMiddle = Key{code: int(ebiten.MouseButtonMiddle), kind: keyMouse, name: "mouse_middle_button"}
KeyMouseBack = Key{code: int(ebiten.MouseButton3), kind: keyMouse, name: "mouse_back_button"}
KeyMouseForward = Key{code: int(ebiten.MouseButton4), kind: keyMouse, name: "mouse_forward_button"}

// A special event that is triggered if the left mouse button is being pressed
// and the cursor is moved. This is useful for UI interfaces to detect drag-and-drop triggers.
KeyMouseLeftDrag = Key{code: int(ebiten.MouseButtonLeft), kind: keyMouseDrag, name: "mouse_left_drag"}
)

// Touch keys.
Expand Down
42 changes: 39 additions & 3 deletions system.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,15 @@ type System struct {
touchStartPos Vec
touchTime float64

mouseEnabled bool
cursorPos Vec
wheel Vec
mouseEnabled bool
mouseHasDrag bool // For "drag" event
mouseDragging bool // For "drag" event
mouseJustHadDrag bool // For "drag" event
mousePressed bool // For "drag" event
mouseStartPos Vec // For "drag" event
mouseDragPos Vec // For "drag" event
cursorPos Vec
wheel Vec
}

// SystemConfig configures the input system.
Expand Down Expand Up @@ -170,6 +176,36 @@ func (sys *System) UpdateWithDelta(delta float64) {
if sys.mouseEnabled {
x, y := ebiten.CursorPosition()
sys.cursorPos = Vec{X: float64(x), Y: float64(y)}

// We copy a lot from the touch-style drag gesture.
// This is not mandatory as getting a cursor pos is much easier on PC.
// But I do value the consistency and easier cross-platform coding,
// so let's try to make them behave as close to each other as feasible.
sys.mouseHasDrag = false
sys.mouseJustHadDrag = false
if inpututil.IsMouseButtonJustReleased(ebiten.MouseButtonLeft) {
sys.mouseDragging = false
sys.mousePressed = false
}
if sys.mousePressed {
if sys.mouseDragging {
sys.mouseHasDrag = true
sys.mouseDragPos = sys.cursorPos
} else {

Check failure on line 194 in system.go

View workflow job for this annotation

GitHub Actions / Build

elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic)
// Mouse pointer is more precise than a finger gesture,
// therefore we can have a lower threshold here.
if vecDistance(sys.mouseStartPos, sys.cursorPos) > 3 {
sys.mouseDragging = true
sys.mouseJustHadDrag = true
sys.mouseHasDrag = true
sys.mouseDragPos = sys.cursorPos
}
}
}
if !sys.mousePressed && inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
sys.mouseStartPos = sys.cursorPos
sys.mousePressed = true
}
}

if sys.mouseEnabled || sys.touchEnabled {
Expand Down

0 comments on commit 5287a1e

Please sign in to comment.