aboutsummaryrefslogtreecommitdiff
path: root/src/gui/modalMenu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/modalMenu.cpp')
-rw-r--r--src/gui/modalMenu.cpp261
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;
}