Browse Source

Refactoring ine a bit.

Beoran 6 days ago
parent
commit
0eb5634830
1 changed files with 340 additions and 0 deletions
  1. 340 0
      pkg/inez/ine.go

+ 340 - 0
pkg/inez/ine.go

@@ -0,0 +1,340 @@
+// package ine, named after St. Ine of Wessex is an input to event
+// converter for ebitengine.
+// This package adapts Ebitengine's input to be event based in stead
+// of state based.
+// We reuse image.Point for coordinate values.
+package ine
+
+import "image"
+
+import "github.com/hajimehoshi/ebiten/v2"
+import "github.com/hajimehoshi/ebiten/v2/inpututil"
+import "github.com/hajimehoshi/ebiten/v2/exp/textinput"
+
+// Point is image.Point reused
+type Point = image.Point
+
+// Kind is the kind of an event or event reply.
+type Kind int
+
+const (
+	None         Kind = iota // No event.
+	PadAttach                // Attach: gamepad attached.
+	PadDetach                // Detach: gamepad detached.
+	PadPress                 // Press; gamepad button pressed.
+	PadHold                  // Hold; gamepad button held.
+	PadRelease               // Release: gamepad button released.
+	PadMove                  // Move gamepad axes moved.
+	KeyPress                 // Tap: keyboard key tapped
+	KeyHold                  // Push: keyboard key long push
+	KeyRelease               // Type: keyboard key released
+	TouchPress               // Touch: touch start
+	TouchHold                // Rub: touch continued
+	TouchRelease             // Retract: touch released
+	MousePress               // Click: mouse button down.
+	MouseHold                // Drag: mouse moved with button down.
+	MouseRelease             // Loosen: mouse button up.
+	MouseMove                // Move: mouse moved with or without button down.
+	MouseWheel               // Wheel: mouse wheel events.
+	InputText                // Text: IME text input event.
+	ExtraCustom              // Custom is for user events. Set the event.Custom field if needed.
+	LastEvent                // Last event index.
+)
+
+type CustomEvent interface {
+	String() string
+	Data() any
+}
+
+func (k Kind) Is(other Kind) bool {
+	return k == other
+}
+
+// String is the string repesentation of the event kind.
+func (k Kind) String() string {
+	switch k {
+	case PadAttach:
+		return "PadAttach"
+	case PadDetach:
+		return "PadDetach"
+	case PadPress:
+		return "PadPress"
+	case PadHold:
+		return "PadHold"
+	case PadRelease:
+		return "PadRelease"
+	case PadMove:
+		return "PadMove"
+	case KeyPress:
+		return "KeyPress"
+	case KeyHold:
+		return "KeyHold"
+	case KeyRelease:
+		return "KeyRelease"
+	case TouchPress:
+		return "TouchPress"
+	case TouchHold:
+		return "TouchHold"
+	case TouchRelease:
+		return "TouchRelease"
+	case MousePress:
+		return "MousePress"
+	case MouseHold:
+		return "MouseHold"
+	case MouseRelease:
+		return "MouseRelease"
+	case MouseMove:
+		return "MouseMove"
+	case MouseWheel:
+		return "MouseWheel"
+	case InputText:
+		return "InputText"
+	case ExtraCustom:
+		return "ExtraCustom"
+	default:
+		return "Unknown"
+	}
+}
+
+type Button = ebiten.GamepadButton
+type Key = ebiten.Key
+
+type MouseEvent struct {
+	Point
+	Delta    Point
+	Wheel    Point
+	Duration int
+}
+
+type TouchEvent struct {
+	Point
+	ID       int
+	Delta    Point
+	Wheel    Point
+	Duration int
+}
+
+type PadEvent struct {
+	ID       int
+	Axes     []float64
+	Button   Button
+	Duration int
+}
+
+type KeyEvent struct {
+	Key      Key
+	Duration int
+}
+
+type InputEvent struct {
+	ID   int
+	Text string
+}
+
+// Event contains the kind and data for an event.
+// Not all fields will be filled in depending on the Kind
+type Event struct {
+	// Kind is the event kind of the event
+	Kind
+
+	// Key is used for key events
+	Key KeyEvent
+
+	// Pad is used for gamepad events
+	Pad PadEvent
+
+	// Touch is used for touch events
+	Touch TouchEvent
+
+	// Mouse is used for mouse events
+	Mouse MouseEvent
+
+	// Input is for input events.
+	Input InputEvent
+
+	// Custom is used for custom events
+	Custom CustomEvent
+}
+
+// Field is an input field for IME text entry.
+type Field struct {
+	textinput.Field
+	Point
+}
+
+type State struct {
+	gamepads  []ebiten.GamepadID
+	connected []ebiten.GamepadID
+	cx, cy    int
+	// Text input fields in use
+	Fields []*Field
+
+	// Handler handles the event. It is acallback so it can fill an array or use a channel, etc.
+	Handler func(Event) bool
+}
+
+func (s *State) SetHandler(h func(Event) bool) {
+	s.Handler = h
+}
+
+func (s *State) AddField(x, y int) *Field {
+	field := &Field{Point: image.Pt(x, y)}
+	s.Fields = append(s.Fields, field)
+	return field
+}
+
+func (s State) Emit(k Kind, e Event) bool {
+	e.Kind = k
+	if s.Handler != nil {
+		return s.Handler(e)
+	}
+	return false
+}
+
+func (s State) EmitMouse(k Kind, me MouseEvent) bool {
+	return s.Emit(k, Event{Mouse: me})
+}
+
+func (s State) EmitPad(k Kind, pe PadEvent) bool {
+	return s.Emit(k, Event{Pad: pe})
+}
+
+func (s State) EmitKey(k Kind, ke KeyEvent) bool {
+	return s.Emit(k, Event{Key: ke})
+}
+
+func (s State) EmitTouch(k Kind, te TouchEvent) bool {
+	return s.Emit(k, Event{Touch: te})
+}
+
+func (s State) EmitInput(k Kind, ie InputEvent) bool {
+	return s.Emit(k, Event{Input: ie})
+}
+
+func (s State) EmitCustom(k Kind, ce CustomEvent) bool {
+	return s.Emit(k, Event{Custom: ce})
+}
+
+func (s *State) Update() {
+	for _, gid := range s.gamepads {
+		if inpututil.IsGamepadJustDisconnected(gid) {
+			s.EmitPad(PadDetach, PadEvent{ID: int(gid)})
+		}
+	}
+
+	s.connected = inpututil.AppendJustConnectedGamepadIDs(nil)
+	for _, gid := range s.connected {
+		s.EmitPad(PadAttach, PadEvent{ID: int(gid)})
+	}
+
+	s.gamepads = ebiten.AppendGamepadIDs(s.gamepads)
+	for _, gid := range s.gamepads {
+		buttons := inpututil.AppendJustPressedGamepadButtons(gid, nil)
+		for _, button := range buttons {
+			s.EmitPad(PadPress, PadEvent{ID: int(gid), Button: button})
+		}
+
+		buttons = inpututil.AppendPressedGamepadButtons(gid, nil)
+		for _, button := range buttons {
+			dur := inpututil.GamepadButtonPressDuration(gid, button)
+			s.EmitPad(PadHold, PadEvent{ID: int(gid), Button: button, Duration: dur})
+		}
+
+		buttons = inpututil.AppendJustReleasedGamepadButtons(gid, nil)
+		for _, button := range buttons {
+			s.EmitPad(PadRelease, PadEvent{ID: int(gid), Button: button})
+		}
+
+		count := ebiten.GamepadAxisCount(gid)
+		axes := make([]float64, count)
+		moved := false
+		for axis := 0; axis < count; axis++ {
+			value := ebiten.GamepadAxisValue(gid, axis)
+			axes[axis] = value
+			moved = moved || ((value > 0.1) || (value < -0.1))
+		}
+		if (len(axes) > 0) && moved {
+			s.EmitPad(PadMove, PadEvent{ID: int(gid), Axes: axes})
+		}
+	}
+
+	keys := inpututil.AppendJustPressedKeys(nil)
+	for _, key := range keys {
+		s.EmitKey(KeyPress, KeyEvent{Key: key})
+	}
+
+	keys = inpututil.AppendPressedKeys(nil)
+	for _, key := range keys {
+		dur := inpututil.KeyPressDuration(key)
+		s.EmitKey(KeyHold, KeyEvent{Key: key, Duration: dur})
+	}
+
+	keys = inpututil.AppendJustReleasedKeys(nil)
+	for _, key := range keys {
+		s.EmitKey(KeyRelease, KeyEvent{Key: key})
+	}
+
+	for id, field := range s.Fields {
+		if field.IsFocused() {
+			handled, _ := field.HandleInput(field.X, field.Y)
+			if handled {
+				s.EmitInput(InputText, InputEvent{ID: id, Text: field.Text()})
+			}
+		}
+	}
+
+	touches := inpututil.AppendJustPressedTouchIDs(nil)
+	for _, touch := range touches {
+		x, y := ebiten.TouchPosition(touch)
+		s.EmitTouch(TouchPress, TouchEvent{ID: int(touch), Point: image.Pt(x, y)})
+	}
+
+	touches = ebiten.AppendTouchIDs(nil)
+	for _, touch := range touches {
+		x, y := ebiten.TouchPosition(touch)
+		px, py := inpututil.TouchPositionInPreviousTick(touch)
+		dx, dy := x-px, y-py
+		dur := inpututil.TouchPressDuration(touch)
+		s.EmitTouch(TouchHold, TouchEvent{ID: int(touch),
+			Point:    image.Pt(x, y),
+			Delta:    image.Pt(dx, dy),
+			Duration: dur,
+		})
+	}
+
+	touches = inpututil.AppendJustReleasedTouchIDs(nil)
+	for _, touch := range touches {
+		x, y := ebiten.TouchPosition(touch)
+		s.EmitTouch(TouchRelease, TouchEvent{ID: int(touch), Point: image.Pt(x, y)})
+	}
+
+	x, y := ebiten.CursorPosition()
+	dx, dy := x-s.cx, y-s.cy
+	at := image.Pt(x, y)
+	delta := image.Pt(dx, dy)
+
+	for mb := ebiten.MouseButton(0); mb < ebiten.MouseButtonMax; mb++ {
+		if inpututil.IsMouseButtonJustPressed(mb) {
+			s.EmitMouse(MousePress, MouseEvent{Point: at, Delta: delta})
+		}
+		if ebiten.IsMouseButtonPressed(mb) {
+			dur := inpututil.MouseButtonPressDuration(mb)
+			s.EmitMouse(MouseHold, MouseEvent{Point: at, Delta: delta, Duration: dur})
+		}
+		if inpututil.IsMouseButtonJustReleased(mb) {
+			s.EmitMouse(MouseRelease, MouseEvent{Point: at, Delta: delta})
+		}
+	}
+	if dx != 0 || dy != 0 {
+		s.EmitMouse(MouseMove, MouseEvent{Point: at, Delta: delta})
+	}
+	s.cx = x
+	s.cy = y
+
+	wx, wy := ebiten.Wheel()
+	if wx != 0 || wy != 0 {
+		wheel := image.Pt(int(wx), int(wy))
+		s.EmitMouse(MouseWheel, MouseEvent{Point: at, Delta: delta, Wheel: wheel})
+	}
+
+}