diff --git a/integration.txt b/integration.txt index 72ad6e2c..cbc184da 100644 --- a/integration.txt +++ b/integration.txt @@ -26,9 +26,13 @@ Turbo Badger will handle input capture, focus, etc. automatically. List of input triggers (root is your root TBWidget): - root->InvokePointer[Down/Up/Move] - Mouse or touch input. - root->InvokeWheel - Mouse wheel input - root->InvokeKey - Keyboard input + root->InvokePointer[Down/Up/Move/Cancel] - Mouse or touch input + root->InvokeTouch[Down/Up/Move/Cancel] - Touch input (touch indices > 0). + root->InvokeWheel - Mouse wheel input + root->InvokeKey - Keyboard input + +Additionally, there's common event types that can be invoked by root->InvokeEvent +like f.ex: EVENT_TYPE_SHORTCUT, EVENT_TYPE_CONTEXT_MENU, EVENT_TYPE_FILE_DROP... Animations, Layout and Painting ------------------------------- diff --git a/src/tb/tb_debug.cpp b/src/tb/tb_debug.cpp index c9007568..380bce06 100644 --- a/src/tb/tb_debug.cpp +++ b/src/tb/tb_debug.cpp @@ -186,6 +186,10 @@ class DebugSettingsWindow : public TBWindow, public TBWidgetListener case EVENT_TYPE_POINTER_DOWN: return "POINTER_DOWN"; case EVENT_TYPE_POINTER_UP: return "POINTER_UP"; case EVENT_TYPE_POINTER_MOVE: return "POINTER_MOVE"; + case EVENT_TYPE_TOUCH_DOWN: return "TOUCH_DOWN"; + case EVENT_TYPE_TOUCH_UP: return "TOUCH_UP"; + case EVENT_TYPE_TOUCH_MOVE: return "TOUCH_MOVE"; + case EVENT_TYPE_TOUCH_CANCEL: return "TOUCH_CANCEL"; case EVENT_TYPE_WHEEL: return "WHEEL"; case EVENT_TYPE_CHANGED: return "CHANGED"; case EVENT_TYPE_KEY_DOWN: return "KEY_DOWN"; diff --git a/src/tb/tb_widgets.cpp b/src/tb/tb_widgets.cpp index e3be4158..d2b2c600 100644 --- a/src/tb/tb_widgets.cpp +++ b/src/tb/tb_widgets.cpp @@ -32,6 +32,27 @@ bool TBWidget::update_widget_states = true; bool TBWidget::update_skin_states = true; bool TBWidget::show_focus_state = false; +static TBHashTableAutoDeleteOf s_touch_info; + +TBWidget::TOUCH_INFO *TBWidget::GetTouchInfo(uint32 id) +{ + return s_touch_info.Get(id); +} + +static TBWidget::TOUCH_INFO *NewTouchInfo(uint32 id) +{ + assert(!s_touch_info.Get(id)); + TBWidget::TOUCH_INFO *ti = new TBWidget::TOUCH_INFO; + memset(ti, 0, sizeof(TBWidget::TOUCH_INFO)); + s_touch_info.Add(id, ti); + return ti; +} + +static void DeleteTouchInfo(uint32 id) +{ + s_touch_info.Delete(id); +} + // == TBLongClickTimer ================================================================== /** One shot timer for long click event */ @@ -84,6 +105,7 @@ TBWidget::~TBWidget() assert(!m_parent); ///< A widget must be removed from parent before deleted m_packed.is_dying = true; + // Unreference from pointer capture if (this == hovered_widget) hovered_widget = nullptr; if (this == captured_widget) @@ -91,6 +113,16 @@ TBWidget::~TBWidget() if (this == focused_widget) focused_widget = nullptr; + // Unreference from touch info + TBHashTableIteratorOf it(&s_touch_info); + while (TOUCH_INFO *ti = it.GetNextContent()) + { + if (this == ti->hovered_widget) + ti->hovered_widget = nullptr; + if (this == ti->captured_widget) + ti->captured_widget = nullptr; + } + TBWidgetListener::InvokeWidgetDelete(this); DeleteAllChildren(); @@ -1374,8 +1406,8 @@ void TBWidget::MaybeInvokeLongClickOrContextMenu(bool touch) void TBWidget::InvokePointerMove(int x, int y, MODIFIER_KEYS modifierkeys, bool touch) { SetHoveredWidget(GetWidgetAt(x, y, true), touch); - TBWidget *target = captured_widget ? captured_widget : hovered_widget; + TBWidget *target = captured_widget ? captured_widget : hovered_widget; if (target) { target->ConvertFromRoot(x, y); @@ -1446,6 +1478,97 @@ void TBWidget::HandlePanningOnMove(int x, int y) } } +void TBWidget::InvokePointerCancel() +{ + if (captured_widget) + captured_widget->ReleaseCapture(); +} + +bool TBWidget::InvokeTouchDown(int x, int y, uint32 id, int click_count, MODIFIER_KEYS modifierkeys) +{ + if (id == 0) + return InvokePointerDown(x, y, click_count, modifierkeys, true); + + TOUCH_INFO *ti = NewTouchInfo(id); + if (!ti) + return false; + + if (!ti->captured_widget) + ti->captured_widget = GetWidgetAt(x, y, true); + if (ti->captured_widget && !ti->captured_widget->GetState(WIDGET_STATE_DISABLED)) + ti->hovered_widget = ti->captured_widget; + + if (ti->captured_widget) + { + ti->captured_widget->ConvertFromRoot(x, y); + ti->move_widget_x = ti->down_widget_x = x; + ti->move_widget_y = ti->down_widget_y = y; + TBWidgetEvent ev(EVENT_TYPE_TOUCH_DOWN, x, y, true, modifierkeys); + ev.count = click_count; + ev.ref_id = id; + ti->captured_widget->InvokeEvent(ev); + return true; + } + return false; +} + +bool TBWidget::InvokeTouchUp(int x, int y, uint32 id, MODIFIER_KEYS modifierkeys) +{ + if (id == 0) + return InvokePointerUp(x, y, modifierkeys, true); + TOUCH_INFO *ti = GetTouchInfo(id); + if (ti && ti->captured_widget) + { + ti->captured_widget->ConvertFromRoot(x, y); + TBWidgetEvent ev(EVENT_TYPE_TOUCH_UP, x, y, true, modifierkeys); + ev.ref_id = id; + ti->captured_widget->InvokeEvent(ev); + DeleteTouchInfo(id); + return true; + } + return false; +} + +void TBWidget::InvokeTouchMove(int x, int y, uint32 id, MODIFIER_KEYS modifierkeys) +{ + if (id == 0) + return InvokePointerMove(x, y, modifierkeys, true); + + TOUCH_INFO *ti = GetTouchInfo(id); + if (!ti) + return; + + ti->hovered_widget = GetWidgetAt(x, y, true); + if (ti->captured_widget) + { + ti->captured_widget->ConvertFromRoot(x, y); + ti->move_widget_x = x; + ti->move_widget_y = y; + TBWidgetEvent ev(EVENT_TYPE_TOUCH_MOVE, x, y, true, modifierkeys); + ev.ref_id = id; + if (ti->captured_widget->InvokeEvent(ev)) + return; + } +} + +void TBWidget::InvokeTouchCancel(uint32 id) +{ + if (id == 0) + return InvokePointerCancel(); + + TOUCH_INFO *ti = GetTouchInfo(id); + if (ti) + { + if (ti->captured_widget) + { + TBWidgetEvent ev(EVENT_TYPE_TOUCH_CANCEL, 0, 0, true); + ev.ref_id = id; + ti->captured_widget->InvokeEvent(ev); + } + DeleteTouchInfo(id); + } +} + bool TBWidget::InvokeWheel(int x, int y, int delta_x, int delta_y, MODIFIER_KEYS modifierkeys) { SetHoveredWidget(GetWidgetAt(x, y, true), true); diff --git a/src/tb/tb_widgets.h b/src/tb/tb_widgets.h index 92b6089b..aeb172bb 100644 --- a/src/tb/tb_widgets.h +++ b/src/tb/tb_widgets.h @@ -55,6 +55,10 @@ enum EVENT_TYPE { EVENT_TYPE_POINTER_DOWN, EVENT_TYPE_POINTER_UP, EVENT_TYPE_POINTER_MOVE, + EVENT_TYPE_TOUCH_DOWN, + EVENT_TYPE_TOUCH_UP, + EVENT_TYPE_TOUCH_MOVE, + EVENT_TYPE_TOUCH_CANCEL, EVENT_TYPE_WHEEL, /** Invoked after changing text in a TBTextField, changing selected item @@ -139,6 +143,10 @@ class TBWidgetEvent : public TBTypedObject bool IsPointerEvent() const { return type == EVENT_TYPE_POINTER_DOWN || type == EVENT_TYPE_POINTER_UP || type == EVENT_TYPE_POINTER_MOVE; } + bool IsTouchEvent() const { return type == EVENT_TYPE_TOUCH_DOWN || + type == EVENT_TYPE_TOUCH_UP || + type == EVENT_TYPE_TOUCH_MOVE || + type == EVENT_TYPE_TOUCH_CANCEL; } bool IsKeyEvent() const { return type == EVENT_TYPE_KEY_DOWN || type == EVENT_TYPE_KEY_UP; } }; @@ -929,6 +937,17 @@ class TBWidget : public TBTypedObject, public TBLinkOf bool InvokePointerDown(int x, int y, int click_count, MODIFIER_KEYS modifierkeys, bool touch); bool InvokePointerUp(int x, int y, MODIFIER_KEYS modifierkeys, bool touch); void InvokePointerMove(int x, int y, MODIFIER_KEYS modifierkeys, bool touch); + void InvokePointerCancel(); + + /** Invoke touch events with ref_id set as the given id. + Note: For id 0, it will invoke EVENT_TYPE_POINTER_DOWN (with touch flag set to true), and EVENT_TYPE_TOUCH_DOWN + for other id > 0. This results in normal interaction for first finger, and optional handling of other + simultaneous interaction. GetTouchInfo(id) can be used to get additional interaction info. */ + bool InvokeTouchDown(int x, int y, uint32 id, int click_count, MODIFIER_KEYS modifierkeys); + bool InvokeTouchUp(int x, int y, uint32 id, MODIFIER_KEYS modifierkeys); + void InvokeTouchMove(int x, int y, uint32 id, MODIFIER_KEYS modifierkeys); + void InvokeTouchCancel(uint32 id); + bool InvokeWheel(int x, int y, int delta_x, int delta_y, MODIFIER_KEYS modifierkeys); /** Invoke the EVENT_TYPE_KEY_DOWN and EVENT_TYPE_KEY_UP events on the currently focused widget. @@ -1019,9 +1038,9 @@ class TBWidget : public TBTypedObject, public TBLinkOf #endif // TB_RUNTIME_DEBUG_INFO // TBWidget related globals - static TBWidget *hovered_widget; ///< The currently hovered widget, or nullptr. - static TBWidget *captured_widget; ///< The currently captured widget, or nullptr. - static TBWidget *focused_widget; ///< The currently focused widget, or nullptr. + static TBWidget *hovered_widget; ///< The currently hovered widget, or nullptr. + static TBWidget *captured_widget; ///< The currently captured widget, or nullptr. + static TBWidget *focused_widget; ///< The currently focused widget, or nullptr. static int pointer_down_widget_x; ///< Pointer x position on down event, relative to the captured widget. static int pointer_down_widget_y; ///< Pointer y position on down event, relative to the captured widget. static int pointer_move_widget_x; ///< Pointer x position on last pointer event, relative to the captured widget (if any) or hovered widget. @@ -1030,6 +1049,16 @@ class TBWidget : public TBTypedObject, public TBLinkOf static bool update_widget_states; ///< true if something has called InvalidateStates() and it still hasn't been updated. static bool update_skin_states; ///< true if something has called InvalidateStates() and skin still hasn't been updated. static bool show_focus_state; ///< true if the focused state should be painted automatically. + struct TOUCH_INFO { + TBWidget *hovered_widget; ///< The currently hovered widget, or nullptr. + TBWidget *captured_widget; ///< The currently captured widget, or nullptr. + int down_widget_x; ///< Touch x position on down event, relative to the captured widget. + int down_widget_y; ///< Touch y position on down event, relative to the captured widget. + int move_widget_x; ///< Touch x position on last touch event, relative to the captured widget. + int move_widget_y; ///< Touch y position on last touch event, relative to the captured widget. + }; + /** Return TOUCH_INFO for the given id, or nullptr if no touch is active for that id. */ + static TOUCH_INFO *GetTouchInfo(uint32 id); private: /** Return this widget or the nearest parent that is scrollable in the given axis, or nullptr if there is none. */