From 15305011dcc5fe691a7a663fc3a8e691b957f39d Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Fri, 7 Nov 2025 22:18:24 +0100 Subject: [PATCH] moved gestures to application --- pyWebLayout/io/gesture.py | 124 ------------- tests/io_tests/io/__init__.py | 1 - tests/io_tests/io/test_gesture.py | 287 ------------------------------ 3 files changed, 412 deletions(-) delete mode 100644 pyWebLayout/io/gesture.py delete mode 100644 tests/io_tests/io/__init__.py delete mode 100644 tests/io_tests/io/test_gesture.py diff --git a/pyWebLayout/io/gesture.py b/pyWebLayout/io/gesture.py deleted file mode 100644 index 6371cda..0000000 --- a/pyWebLayout/io/gesture.py +++ /dev/null @@ -1,124 +0,0 @@ -""" -Gesture event types for touch input. - -This module defines touch gestures that can be received from a HAL (Hardware Abstraction Layer) -or touch input system, and the response format for actions to be performed. -""" - -from __future__ import annotations -from enum import Enum -from dataclasses import dataclass -from typing import Optional, Dict, Any - - -class GestureType(Enum): - """Touch gesture types from HAL""" - TAP = "tap" # Single finger tap - LONG_PRESS = "long_press" # Hold for 500ms+ - SWIPE_LEFT = "swipe_left" # Swipe left (page forward) - SWIPE_RIGHT = "swipe_right" # Swipe right (page back) - SWIPE_UP = "swipe_up" # Swipe up (scroll down) - SWIPE_DOWN = "swipe_down" # Swipe down (scroll up) - PINCH_IN = "pinch_in" # Pinch fingers together (zoom out) - PINCH_OUT = "pinch_out" # Spread fingers apart (zoom in) - DRAG_START = "drag_start" # Start dragging/selection - DRAG_MOVE = "drag_move" # Continue dragging - DRAG_END = "drag_end" # End dragging/selection - - -@dataclass -class TouchEvent: - """ - Touch event from HAL. - - Represents a single touch gesture with its coordinates and metadata. - """ - gesture: GestureType - x: int # Primary touch point X coordinate - y: int # Primary touch point Y coordinate - x2: Optional[int] = None # Secondary point X (for pinch/drag) - y2: Optional[int] = None # Secondary point Y (for pinch/drag) - timestamp_ms: float = 0 # Timestamp in milliseconds - - @classmethod - def from_hal(cls, hal_data: dict) -> 'TouchEvent': - """ - Parse a touch event from HAL format. - - Args: - hal_data: Dictionary with gesture data from HAL - Expected keys: 'gesture', 'x', 'y', optionally 'x2', 'y2', 'timestamp' - - Returns: - TouchEvent instance - - Example: - >>> event = TouchEvent.from_hal({ - ... 'gesture': 'tap', - ... 'x': 450, - ... 'y': 320 - ... }) - """ - return cls( - gesture=GestureType(hal_data['gesture']), - x=hal_data['x'], - y=hal_data['y'], - x2=hal_data.get('x2'), - y2=hal_data.get('y2'), - timestamp_ms=hal_data.get('timestamp', 0) - ) - - def to_dict(self) -> dict: - """Convert to dictionary for serialization""" - return { - 'gesture': self.gesture.value, - 'x': self.x, - 'y': self.y, - 'x2': self.x2, - 'y2': self.y2, - 'timestamp_ms': self.timestamp_ms - } - - -@dataclass -class GestureResponse: - """ - Response from handling a gesture. - - This encapsulates the action that should be performed by the UI - in response to a gesture, keeping all business logic in the library. - """ - action: str # Action type: "navigate", "define", "select", "zoom", "page_turn", "none", etc. - data: Dict[str, Any] # Action-specific data - - def to_dict(self) -> dict: - """ - Convert to dictionary for Flask JSON response. - - Returns: - Dictionary with action and data - """ - return { - 'action': self.action, - 'data': self.data - } - - -# Action type constants for clarity -class ActionType: - """Constants for gesture response action types""" - NONE = "none" - PAGE_TURN = "page_turn" - NAVIGATE = "navigate" - DEFINE = "define" - SELECT = "select" - ZOOM = "zoom" - BOOK_LOADED = "book_loaded" - WORD_SELECTED = "word_selected" - SHOW_MENU = "show_menu" - SELECTION_START = "selection_start" - SELECTION_UPDATE = "selection_update" - SELECTION_COMPLETE = "selection_complete" - AT_START = "at_start" - AT_END = "at_end" - ERROR = "error" diff --git a/tests/io_tests/io/__init__.py b/tests/io_tests/io/__init__.py deleted file mode 100644 index 56b4aba..0000000 --- a/tests/io_tests/io/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for I/O functionality.""" diff --git a/tests/io_tests/io/test_gesture.py b/tests/io_tests/io/test_gesture.py deleted file mode 100644 index a6064a9..0000000 --- a/tests/io_tests/io/test_gesture.py +++ /dev/null @@ -1,287 +0,0 @@ -""" -Unit tests for gesture event system. - -Tests TouchEvent, GestureType, GestureResponse, and HAL integration. -""" - -import unittest -from pyWebLayout.io.gesture import ( - GestureType, - TouchEvent, - GestureResponse, - ActionType -) - - -class TestGestureType(unittest.TestCase): - """Test GestureType enum""" - - def test_gesture_types_exist(self): - """Test all gesture types are defined""" - self.assertEqual(GestureType.TAP.value, "tap") - self.assertEqual(GestureType.LONG_PRESS.value, "long_press") - self.assertEqual(GestureType.SWIPE_LEFT.value, "swipe_left") - self.assertEqual(GestureType.SWIPE_RIGHT.value, "swipe_right") - self.assertEqual(GestureType.SWIPE_UP.value, "swipe_up") - self.assertEqual(GestureType.SWIPE_DOWN.value, "swipe_down") - self.assertEqual(GestureType.PINCH_IN.value, "pinch_in") - self.assertEqual(GestureType.PINCH_OUT.value, "pinch_out") - self.assertEqual(GestureType.DRAG_START.value, "drag_start") - self.assertEqual(GestureType.DRAG_MOVE.value, "drag_move") - self.assertEqual(GestureType.DRAG_END.value, "drag_end") - - -class TestTouchEvent(unittest.TestCase): - """Test TouchEvent dataclass""" - - def test_init_basic(self): - """Test basic TouchEvent creation""" - event = TouchEvent( - gesture=GestureType.TAP, - x=450, - y=320 - ) - - self.assertEqual(event.gesture, GestureType.TAP) - self.assertEqual(event.x, 450) - self.assertEqual(event.y, 320) - self.assertIsNone(event.x2) - self.assertIsNone(event.y2) - self.assertEqual(event.timestamp_ms, 0) - - def test_init_with_secondary_point(self): - """Test TouchEvent with secondary point (pinch/drag)""" - event = TouchEvent( - gesture=GestureType.PINCH_OUT, - x=400, - y=300, - x2=450, - y2=350, - timestamp_ms=12345.678 - ) - - self.assertEqual(event.gesture, GestureType.PINCH_OUT) - self.assertEqual(event.x, 400) - self.assertEqual(event.y, 300) - self.assertEqual(event.x2, 450) - self.assertEqual(event.y2, 350) - self.assertEqual(event.timestamp_ms, 12345.678) - - def test_from_hal_basic(self): - """Test parsing TouchEvent from HAL format""" - hal_data = { - 'gesture': 'tap', - 'x': 450, - 'y': 320 - } - - event = TouchEvent.from_hal(hal_data) - - self.assertEqual(event.gesture, GestureType.TAP) - self.assertEqual(event.x, 450) - self.assertEqual(event.y, 320) - - def test_from_hal_complete(self): - """Test parsing TouchEvent with all fields from HAL""" - hal_data = { - 'gesture': 'pinch_out', - 'x': 400, - 'y': 300, - 'x2': 450, - 'y2': 350, - 'timestamp': 12345.678 - } - - event = TouchEvent.from_hal(hal_data) - - self.assertEqual(event.gesture, GestureType.PINCH_OUT) - self.assertEqual(event.x, 400) - self.assertEqual(event.y, 300) - self.assertEqual(event.x2, 450) - self.assertEqual(event.y2, 350) - self.assertEqual(event.timestamp_ms, 12345.678) - - def test_to_dict(self): - """Test TouchEvent serialization""" - event = TouchEvent( - gesture=GestureType.SWIPE_LEFT, - x=600, - y=400, - timestamp_ms=12345.0 - ) - - d = event.to_dict() - - self.assertEqual(d['gesture'], 'swipe_left') - self.assertEqual(d['x'], 600) - self.assertEqual(d['y'], 400) - self.assertIsNone(d['x2']) - self.assertIsNone(d['y2']) - self.assertEqual(d['timestamp_ms'], 12345.0) - - -class TestGestureResponse(unittest.TestCase): - """Test GestureResponse dataclass""" - - def test_init(self): - """Test GestureResponse creation""" - response = GestureResponse( - action="page_turn", - data={"direction": "forward", "progress": 0.42} - ) - - self.assertEqual(response.action, "page_turn") - self.assertEqual(response.data['direction'], "forward") - self.assertEqual(response.data['progress'], 0.42) - - def test_to_dict(self): - """Test GestureResponse serialization""" - response = GestureResponse( - action="define", - data={"word": "ephemeral", "bounds": (100, 200, 50, 20)} - ) - - d = response.to_dict() - - self.assertEqual(d['action'], "define") - self.assertEqual(d['data']['word'], "ephemeral") - self.assertEqual(d['data']['bounds'], (100, 200, 50, 20)) - - def test_to_dict_empty_data(self): - """Test GestureResponse with empty data""" - response = GestureResponse(action="none", data={}) - - d = response.to_dict() - - self.assertEqual(d['action'], "none") - self.assertEqual(d['data'], {}) - - -class TestActionType(unittest.TestCase): - """Test ActionType constants""" - - def test_action_types_defined(self): - """Test all action type constants are defined""" - self.assertEqual(ActionType.NONE, "none") - self.assertEqual(ActionType.PAGE_TURN, "page_turn") - self.assertEqual(ActionType.NAVIGATE, "navigate") - self.assertEqual(ActionType.DEFINE, "define") - self.assertEqual(ActionType.SELECT, "select") - self.assertEqual(ActionType.ZOOM, "zoom") - self.assertEqual(ActionType.BOOK_LOADED, "book_loaded") - self.assertEqual(ActionType.WORD_SELECTED, "word_selected") - self.assertEqual(ActionType.SHOW_MENU, "show_menu") - self.assertEqual(ActionType.SELECTION_START, "selection_start") - self.assertEqual(ActionType.SELECTION_UPDATE, "selection_update") - self.assertEqual(ActionType.SELECTION_COMPLETE, "selection_complete") - self.assertEqual(ActionType.AT_START, "at_start") - self.assertEqual(ActionType.AT_END, "at_end") - self.assertEqual(ActionType.ERROR, "error") - - -class TestHALIntegration(unittest.TestCase): - """Test HAL integration scenarios""" - - def test_hal_tap_flow(self): - """Test complete HAL tap event flow""" - # Simulate HAL sending tap event - hal_data = { - 'gesture': 'tap', - 'x': 450, - 'y': 320, - 'timestamp': 1234567890.123 - } - - # Parse event - event = TouchEvent.from_hal(hal_data) - - # Verify event - self.assertEqual(event.gesture, GestureType.TAP) - self.assertEqual(event.x, 450) - self.assertEqual(event.y, 320) - - # Simulate business logic response - response = GestureResponse( - action=ActionType.WORD_SELECTED, - data={"word": "hello", "bounds": (440, 310, 50, 20)} - ) - - # Serialize for Flask - response_dict = response.to_dict() - - self.assertEqual(response_dict['action'], "word_selected") - self.assertEqual(response_dict['data']['word'], "hello") - - def test_hal_pinch_flow(self): - """Test complete HAL pinch event flow""" - # Simulate HAL sending pinch event with two touch points - hal_data = { - 'gesture': 'pinch_out', - 'x': 400, - 'y': 500, - 'x2': 500, - 'y2': 500, - 'timestamp': 1234567891.456 - } - - event = TouchEvent.from_hal(hal_data) - - self.assertEqual(event.gesture, GestureType.PINCH_OUT) - self.assertEqual(event.x, 400) - self.assertEqual(event.x2, 500) - - def test_hal_swipe_flow(self): - """Test complete HAL swipe event flow""" - hal_data = { - 'gesture': 'swipe_left', - 'x': 600, - 'y': 400 - } - - event = TouchEvent.from_hal(hal_data) - - self.assertEqual(event.gesture, GestureType.SWIPE_LEFT) - - # Expected response - response = GestureResponse( - action=ActionType.PAGE_TURN, - data={"direction": "forward", "progress": 0.25} - ) - - self.assertEqual(response.action, "page_turn") - - def test_hal_drag_selection_flow(self): - """Test complete drag selection flow""" - # Drag start - start_data = { - 'gesture': 'drag_start', - 'x': 100, - 'y': 200 - } - - start_event = TouchEvent.from_hal(start_data) - self.assertEqual(start_event.gesture, GestureType.DRAG_START) - - # Drag move - move_data = { - 'gesture': 'drag_move', - 'x': 300, - 'y': 250 - } - - move_event = TouchEvent.from_hal(move_data) - self.assertEqual(move_event.gesture, GestureType.DRAG_MOVE) - - # Drag end - end_data = { - 'gesture': 'drag_end', - 'x': 500, - 'y': 300 - } - - end_event = TouchEvent.from_hal(end_data) - self.assertEqual(end_event.gesture, GestureType.DRAG_END) - - -if __name__ == '__main__': - unittest.main()