diff options
Diffstat (limited to 'src/gui/modalMenu.cpp')
-rw-r--r-- | src/gui/modalMenu.cpp | 261 |
1 files changed, 182 insertions, 79 deletions
diff --git a/src/gui/modalMenu.cpp b/src/gui/modalMenu.cpp index a6fe7ebaf..0d3fb55f0 100644 --- a/src/gui/modalMenu.cpp +++ b/src/gui/modalMenu.cpp @@ -28,14 +28,16 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "touchscreengui.h" #endif -GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, - IMenuManager *menumgr) : +// clang-format off +GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, + s32 id, IMenuManager *menumgr, bool remap_dbl_click) : IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, core::rect<s32>(0, 0, 100, 100)), #ifdef __ANDROID__ m_jni_field_name(""), #endif - m_menumgr(menumgr) + m_menumgr(menumgr), + m_remap_dbl_click(remap_dbl_click) { m_gui_scale = g_settings->getFloat("gui_scaling"); #ifdef __ANDROID__ @@ -45,6 +47,12 @@ GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment *env, gui::IGUIElement *parent, setVisible(true); Environment->setFocus(this); m_menumgr->createdMenu(this); + + m_doubleclickdetect[0].time = 0; + m_doubleclickdetect[1].time = 0; + + m_doubleclickdetect[0].pos = v2s32(0, 0); + m_doubleclickdetect[1].pos = v2s32(0, 0); } // clang-format on @@ -112,6 +120,127 @@ void GUIModalMenu::removeChildren() } } +// clang-format off +bool GUIModalMenu::DoubleClickDetection(const SEvent &event) +{ + /* The following code is for capturing double-clicks of the mouse button + * and translating the double-click into an EET_KEY_INPUT_EVENT event + * -- which closes the form -- under some circumstances. + * + * There have been many github issues reporting this as a bug even though it + * was an intended feature. For this reason, remapping the double-click as + * an ESC must be explicitly set when creating this class via the + * /p remap_dbl_click parameter of the constructor. + */ + + if (!m_remap_dbl_click) + return false; + + if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { + m_doubleclickdetect[0].pos = m_doubleclickdetect[1].pos; + m_doubleclickdetect[0].time = m_doubleclickdetect[1].time; + + m_doubleclickdetect[1].pos = m_pointer; + m_doubleclickdetect[1].time = porting::getTimeMs(); + } else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) { + u64 delta = porting::getDeltaMs( + m_doubleclickdetect[0].time, porting::getTimeMs()); + if (delta > 400) + return false; + + double squaredistance = m_doubleclickdetect[0].pos. + getDistanceFromSQ(m_doubleclickdetect[1].pos); + + if (squaredistance > (30 * 30)) { + 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; +} +// clang-format on + +static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent) +{ + while (tocheck) { + if (tocheck == parent) { + return true; + } + tocheck = tocheck->getParent(); + } + return false; +} + +#ifdef __ANDROID__ + +bool GUIModalMenu::simulateMouseEvent( + gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event) +{ + SEvent mouse_event{}; // value-initialized, not unitialized + mouse_event.EventType = EET_MOUSE_INPUT_EVENT; + mouse_event.MouseInput.X = m_pointer.X; + mouse_event.MouseInput.Y = m_pointer.Y; + switch (touch_event) { + case ETIE_PRESSED_DOWN: + mouse_event.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN; + mouse_event.MouseInput.ButtonStates = EMBSM_LEFT; + break; + case ETIE_MOVED: + mouse_event.MouseInput.Event = EMIE_MOUSE_MOVED; + mouse_event.MouseInput.ButtonStates = EMBSM_LEFT; + break; + case ETIE_LEFT_UP: + mouse_event.MouseInput.Event = EMIE_LMOUSE_LEFT_UP; + mouse_event.MouseInput.ButtonStates = 0; + break; + default: + return false; + } + if (preprocessEvent(mouse_event)) + return true; + if (!target) + return false; + return target->OnEvent(mouse_event); +} + +void GUIModalMenu::enter(gui::IGUIElement *hovered) +{ + sanity_check(!m_hovered); + m_hovered.grab(hovered); + SEvent gui_event{}; + gui_event.EventType = EET_GUI_EVENT; + gui_event.GUIEvent.Caller = m_hovered.get(); + gui_event.GUIEvent.EventType = EGET_ELEMENT_HOVERED; + gui_event.GUIEvent.Element = gui_event.GUIEvent.Caller; + m_hovered->OnEvent(gui_event); +} + +void GUIModalMenu::leave() +{ + if (!m_hovered) + return; + SEvent gui_event{}; + gui_event.EventType = EET_GUI_EVENT; + gui_event.GUIEvent.Caller = m_hovered.get(); + gui_event.GUIEvent.EventType = EGET_ELEMENT_LEFT; + m_hovered->OnEvent(gui_event); + m_hovered.reset(); +} + +#endif + bool GUIModalMenu::preprocessEvent(const SEvent &event) { #ifdef __ANDROID__ @@ -159,92 +288,66 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) } if (event.EventType == EET_TOUCH_INPUT_EVENT) { - SEvent translated; - memset(&translated, 0, sizeof(SEvent)); - translated.EventType = EET_MOUSE_INPUT_EVENT; - gui::IGUIElement *root = Environment->getRootGUIElement(); - - if (!root) { - errorstream << "GUIModalMenu::preprocessEvent" - << " unable to get root element" << std::endl; - return false; - } - gui::IGUIElement *hovered = - root->getElementFromPoint(core::position2d<s32>( - event.TouchInput.X, event.TouchInput.Y)); + irr_ptr<GUIModalMenu> holder; + holder.grab(this); // keep this alive until return (it might be dropped downstream [?]) - translated.MouseInput.X = event.TouchInput.X; - translated.MouseInput.Y = event.TouchInput.Y; - translated.MouseInput.Control = false; - - if (event.TouchInput.touchedCount == 1) { - switch (event.TouchInput.Event) { - case ETIE_PRESSED_DOWN: + switch ((int)event.TouchInput.touchedCount) { + case 1: { + if (event.TouchInput.Event == ETIE_PRESSED_DOWN || event.TouchInput.Event == ETIE_MOVED) m_pointer = v2s32(event.TouchInput.X, event.TouchInput.Y); - translated.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN; - translated.MouseInput.ButtonStates = EMBSM_LEFT; + if (event.TouchInput.Event == ETIE_PRESSED_DOWN) m_down_pos = m_pointer; - break; - case ETIE_MOVED: - m_pointer = v2s32(event.TouchInput.X, event.TouchInput.Y); - translated.MouseInput.Event = EMIE_MOUSE_MOVED; - translated.MouseInput.ButtonStates = EMBSM_LEFT; - break; - case ETIE_LEFT_UP: - translated.MouseInput.Event = EMIE_LMOUSE_LEFT_UP; - translated.MouseInput.ButtonStates = 0; - hovered = root->getElementFromPoint(m_down_pos); - // we don't have a valid pointer element use last - // known pointer pos - translated.MouseInput.X = m_pointer.X; - translated.MouseInput.Y = m_pointer.Y; - - // reset down pos - m_down_pos = v2s32(0, 0); - break; - default: - break; + gui::IGUIElement *hovered = Environment->getRootGUIElement()->getElementFromPoint(core::position2d<s32>(m_pointer)); + if (event.TouchInput.Event == ETIE_PRESSED_DOWN) + Environment->setFocus(hovered); + if (m_hovered != hovered) { + leave(); + enter(hovered); } - } else if ((event.TouchInput.touchedCount == 2) && - (event.TouchInput.Event == ETIE_PRESSED_DOWN)) { - hovered = root->getElementFromPoint(m_down_pos); - - translated.MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN; - translated.MouseInput.ButtonStates = EMBSM_LEFT | EMBSM_RIGHT; - translated.MouseInput.X = m_pointer.X; - translated.MouseInput.Y = m_pointer.Y; - if (hovered) - hovered->OnEvent(translated); - - translated.MouseInput.Event = EMIE_RMOUSE_LEFT_UP; - translated.MouseInput.ButtonStates = EMBSM_LEFT; - - if (hovered) - hovered->OnEvent(translated); - - return true; - } else { - // ignore unhandled 2 touch events (accidental moving for example) + gui::IGUIElement *focused = Environment->getFocus(); + bool ret = simulateMouseEvent(focused, event.TouchInput.Event); + if (!ret && m_hovered != focused) + ret = simulateMouseEvent(m_hovered.get(), event.TouchInput.Event); + if (event.TouchInput.Event == ETIE_LEFT_UP) + leave(); + return ret; + } + case 2: { + if (event.TouchInput.Event != ETIE_PRESSED_DOWN) + return true; // ignore + auto focused = Environment->getFocus(); + if (!focused) + return true; + SEvent rclick_event{}; + rclick_event.EventType = EET_MOUSE_INPUT_EVENT; + rclick_event.MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN; + rclick_event.MouseInput.ButtonStates = EMBSM_LEFT | EMBSM_RIGHT; + rclick_event.MouseInput.X = m_pointer.X; + rclick_event.MouseInput.Y = m_pointer.Y; + focused->OnEvent(rclick_event); + rclick_event.MouseInput.Event = EMIE_RMOUSE_LEFT_UP; + rclick_event.MouseInput.ButtonStates = EMBSM_LEFT; + focused->OnEvent(rclick_event); return true; } - - // check if translated event needs to be preprocessed again - if (preprocessEvent(translated)) + default: // ignored return true; - - if (hovered) { - grab(); - bool retval = hovered->OnEvent(translated); - - if (event.TouchInput.Event == ETIE_LEFT_UP) - // reset pointer - m_pointer = v2s32(0, 0); - - drop(); - return retval; } } #endif + + if (event.EventType == EET_MOUSE_INPUT_EVENT) { + s32 x = event.MouseInput.X; + s32 y = event.MouseInput.Y; + gui::IGUIElement *hovered = + Environment->getRootGUIElement()->getElementFromPoint( + core::position2d<s32>(x, y)); + if (!isChild(hovered, this)) { + if (DoubleClickDetection(event)) { + return true; + } + } + } return false; } |