Touchscreen: Recognize double-taps as double-clicks (#14187)

This commit is contained in:
grorp 2024-01-05 00:39:40 +01:00 committed by GitHub
parent e17455cb22
commit 05a53cd330
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 77 additions and 74 deletions

@ -51,11 +51,8 @@ GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
setVisible(true); setVisible(true);
m_menumgr->createdMenu(this); m_menumgr->createdMenu(this);
m_doubleclickdetect[0].time = 0; m_last_touch.time = 0;
m_doubleclickdetect[1].time = 0; m_last_touch.pos = v2s32(0, 0);
m_doubleclickdetect[0].pos = v2s32(0, 0);
m_doubleclickdetect[1].pos = v2s32(0, 0);
} }
GUIModalMenu::~GUIModalMenu() GUIModalMenu::~GUIModalMenu()
@ -109,7 +106,18 @@ void GUIModalMenu::quitMenu()
#endif #endif
} }
bool GUIModalMenu::DoubleClickDetection(const SEvent &event) static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent)
{
while (tocheck) {
if (tocheck == parent) {
return true;
}
tocheck = tocheck->getParent();
}
return false;
}
bool GUIModalMenu::remapDoubleClick(const SEvent &event)
{ {
/* The following code is for capturing double-clicks of the mouse button /* The following code is for capturing double-clicks of the mouse button
* and translating the double-click into an EET_KEY_INPUT_EVENT event * and translating the double-click into an EET_KEY_INPUT_EVENT event
@ -124,55 +132,37 @@ bool GUIModalMenu::DoubleClickDetection(const SEvent &event)
if (!m_remap_dbl_click) if (!m_remap_dbl_click)
return false; return false;
if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { if (event.EventType != EET_MOUSE_INPUT_EVENT ||
m_doubleclickdetect[0].pos = m_doubleclickdetect[1].pos; event.MouseInput.Event != EMIE_LMOUSE_DOUBLE_CLICK)
m_doubleclickdetect[0].time = m_doubleclickdetect[1].time; return false;
m_doubleclickdetect[1].pos = m_pointer; // Only exit if the double-click happened outside the menu.
m_doubleclickdetect[1].time = porting::getTimeMs(); gui::IGUIElement *hovered =
} else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) { Environment->getRootGUIElement()->getElementFromPoint(m_pointer);
u64 delta = porting::getDeltaMs( if (isChild(hovered, this))
m_doubleclickdetect[0].time, porting::getTimeMs()); return false;
if (delta > 400)
return false;
double squaredistance = m_doubleclickdetect[0].pos. // Translate double-click to escape.
getDistanceFromSQ(m_doubleclickdetect[1].pos); SEvent translated{};
translated.EventType = EET_KEY_INPUT_EVENT;
translated.KeyInput.Key = KEY_ESCAPE;
translated.KeyInput.Control = false;
translated.KeyInput.Shift = false;
translated.KeyInput.PressedDown = true;
translated.KeyInput.Char = 0;
OnEvent(translated);
if (squaredistance > (30 * 30)) { return true;
return false;
}
SEvent translated{};
// translate doubleclick to escape
translated.EventType = EET_KEY_INPUT_EVENT;
translated.KeyInput.Key = KEY_ESCAPE;
translated.KeyInput.Control = false;
translated.KeyInput.Shift = false;
translated.KeyInput.PressedDown = true;
translated.KeyInput.Char = 0;
OnEvent(translated);
return true;
}
return false;
} }
static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent) bool GUIModalMenu::simulateMouseEvent(ETOUCH_INPUT_EVENT touch_event, bool second_try)
{ {
while (tocheck) { IGUIElement *target;
if (tocheck == parent) { if (!second_try)
return true; target = Environment->getFocus();
} else
tocheck = tocheck->getParent(); target = m_touch_hovered.get();
}
return false;
}
bool GUIModalMenu::simulateMouseEvent(
gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event)
{
SEvent mouse_event{}; // value-initialized, not unitialized SEvent mouse_event{}; // value-initialized, not unitialized
mouse_event.EventType = EET_MOUSE_INPUT_EVENT; mouse_event.EventType = EET_MOUSE_INPUT_EVENT;
mouse_event.MouseInput.X = m_pointer.X; mouse_event.MouseInput.X = m_pointer.X;
@ -190,6 +180,11 @@ bool GUIModalMenu::simulateMouseEvent(
mouse_event.MouseInput.Event = EMIE_LMOUSE_LEFT_UP; mouse_event.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
mouse_event.MouseInput.ButtonStates = 0; mouse_event.MouseInput.ButtonStates = 0;
break; break;
case ETIE_COUNT:
// ETIE_COUNT is used for double-tap events.
mouse_event.MouseInput.Event = EMIE_LMOUSE_DOUBLE_CLICK;
mouse_event.MouseInput.ButtonStates = EMBSM_LEFT;
break;
default: default:
return false; return false;
} }
@ -209,6 +204,9 @@ bool GUIModalMenu::simulateMouseEvent(
} while (false); } while (false);
m_simulated_mouse = false; m_simulated_mouse = false;
if (!retval && !second_try)
return simulateMouseEvent(touch_event, true);
return retval; return retval;
} }
@ -293,12 +291,28 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
leave(); leave();
enter(hovered); enter(hovered);
} }
gui::IGUIElement *focused = Environment->getFocus(); bool ret = simulateMouseEvent(event.TouchInput.Event);
bool ret = simulateMouseEvent(focused, event.TouchInput.Event);
if (!ret && m_touch_hovered != focused)
ret = simulateMouseEvent(m_touch_hovered.get(), event.TouchInput.Event);
if (event.TouchInput.Event == ETIE_LEFT_UP) if (event.TouchInput.Event == ETIE_LEFT_UP)
leave(); leave();
// Detect double-taps and convert them into double-click events.
if (event.TouchInput.Event == ETIE_PRESSED_DOWN) {
u64 time_now = porting::getTimeMs();
u64 time_delta = porting::getDeltaMs(m_last_touch.time, time_now);
v2s32 pos_delta = m_pointer - m_last_touch.pos;
f32 distance_sq = (f32)pos_delta.X * pos_delta.X +
(f32)pos_delta.Y * pos_delta.Y;
if (time_delta < 400 && distance_sq < (30 * 30)) {
// ETIE_COUNT is used for double-tap events.
simulateMouseEvent(ETIE_COUNT);
}
m_last_touch.time = time_now;
m_last_touch.pos = m_pointer;
}
return ret; return ret;
} else if (event.TouchInput.touchedCount == 2) { } else if (event.TouchInput.touchedCount == 2) {
if (event.TouchInput.Event != ETIE_LEFT_UP) if (event.TouchInput.Event != ETIE_LEFT_UP)
@ -325,13 +339,8 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
m_touch_hovered.reset(); m_touch_hovered.reset();
} }
gui::IGUIElement *hovered = if (remapDoubleClick(event))
Environment->getRootGUIElement()->getElementFromPoint(m_pointer); return true;
if (!isChild(hovered, this)) {
if (DoubleClickDetection(event)) {
return true;
}
}
} }
return false; return false;

@ -69,13 +69,6 @@ protected:
virtual std::wstring getLabelByID(s32 id) = 0; virtual std::wstring getLabelByID(s32 id) = 0;
virtual std::string getNameByID(s32 id) = 0; virtual std::string getNameByID(s32 id) = 0;
/**
* check if event is part of a double click
* @param event event to evaluate
* @return true/false if a doubleclick was detected
*/
bool DoubleClickDetection(const SEvent &event);
// Stores the last known pointer type. // Stores the last known pointer type.
PointerType m_pointer_type = PointerType::Mouse; PointerType m_pointer_type = PointerType::Mouse;
// Stores the last known pointer position. // Stores the last known pointer position.
@ -97,13 +90,6 @@ protected:
bool m_simulated_mouse = false; bool m_simulated_mouse = false;
private: private:
struct clickpos
{
v2s32 pos;
s64 time;
};
clickpos m_doubleclickdetect[2];
IMenuManager *m_menumgr; IMenuManager *m_menumgr;
/* If true, remap a double-click (or double-tap) action to ESC. This is so /* If true, remap a double-click (or double-tap) action to ESC. This is so
* that, for example, Android users can double-tap to close a formspec. * that, for example, Android users can double-tap to close a formspec.
@ -112,6 +98,8 @@ private:
* and the default value for the setting is true. * and the default value for the setting is true.
*/ */
bool m_remap_dbl_click; bool m_remap_dbl_click;
bool remapDoubleClick(const SEvent &event);
// This might be necessary to expose to the implementation if it // This might be necessary to expose to the implementation if it
// wants to launch other menus // wants to launch other menus
bool m_allow_focus_removal = false; bool m_allow_focus_removal = false;
@ -120,7 +108,13 @@ private:
irr_ptr<gui::IGUIElement> m_touch_hovered; irr_ptr<gui::IGUIElement> m_touch_hovered;
bool simulateMouseEvent(gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event); bool simulateMouseEvent(ETOUCH_INPUT_EVENT touch_event, bool second_try=false);
void enter(gui::IGUIElement *element); void enter(gui::IGUIElement *element);
void leave(); void leave();
// Used to detect double-taps and convert them into double-click events.
struct {
v2s32 pos;
s64 time;
} m_last_touch;
}; };