mirror of
https://github.com/minetest/minetest.git
synced 2025-02-17 10:23:47 +01:00
Close formspecs with a single tap outside (#14605)
This commit is contained in:
@ -4740,6 +4740,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||||||
list_selected->changeItem(m_selected_item->i, stack_from);
|
list_selected->changeItem(m_selected_item->i, stack_from);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool absorb_event = false;
|
||||||
|
|
||||||
// Possibly send inventory action to server
|
// Possibly send inventory action to server
|
||||||
if (move_amount > 0) {
|
if (move_amount > 0) {
|
||||||
// Send IAction::Move
|
// Send IAction::Move
|
||||||
@ -4882,6 +4884,10 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||||||
a->from_i = m_selected_item->i;
|
a->from_i = m_selected_item->i;
|
||||||
m_invmgr->inventoryAction(a);
|
m_invmgr->inventoryAction(a);
|
||||||
|
|
||||||
|
// Formspecs usually close when you click outside them, we absorb
|
||||||
|
// the event to prevent that. See GUIModalMenu::remapClickOutside.
|
||||||
|
absorb_event = true;
|
||||||
|
|
||||||
} else if (craft_amount > 0) {
|
} else if (craft_amount > 0) {
|
||||||
assert(s.isValid());
|
assert(s.isValid());
|
||||||
|
|
||||||
@ -4911,6 +4917,9 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||||||
m_selected_dragging = false;
|
m_selected_dragging = false;
|
||||||
}
|
}
|
||||||
m_old_pointer = m_pointer;
|
m_old_pointer = m_pointer;
|
||||||
|
|
||||||
|
if (absorb_event)
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.EventType == EET_GUI_EVENT) {
|
if (event.EventType == EET_GUI_EVENT) {
|
||||||
|
@ -25,19 +25,39 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "client/renderingengine.h"
|
#include "client/renderingengine.h"
|
||||||
#include "modalMenu.h"
|
#include "modalMenu.h"
|
||||||
#include "gettext.h"
|
#include "gettext.h"
|
||||||
|
#include "gui/guiInventoryList.h"
|
||||||
#include "porting.h"
|
#include "porting.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "touchscreengui.h"
|
#include "touchscreengui.h"
|
||||||
|
|
||||||
|
PointerAction PointerAction::fromEvent(const SEvent &event) {
|
||||||
|
switch (event.EventType) {
|
||||||
|
case EET_MOUSE_INPUT_EVENT:
|
||||||
|
return {v2s32(event.MouseInput.X, event.MouseInput.Y), porting::getTimeMs()};
|
||||||
|
case EET_TOUCH_INPUT_EVENT:
|
||||||
|
return {v2s32(event.TouchInput.X, event.TouchInput.Y), porting::getTimeMs()};
|
||||||
|
default:
|
||||||
|
FATAL_ERROR("SEvent given to PointerAction::fromEvent has wrong EventType");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PointerAction::isRelated(PointerAction previous) {
|
||||||
|
u64 time_delta = porting::getDeltaMs(previous.time, time);
|
||||||
|
v2s32 pos_delta = pos - previous.pos;
|
||||||
|
f32 distance_sq = (f32)pos_delta.X * pos_delta.X + (f32)pos_delta.Y * pos_delta.Y;
|
||||||
|
|
||||||
|
return time_delta < 400 && distance_sq < (30.0f * 30.0f);
|
||||||
|
}
|
||||||
|
|
||||||
GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
|
GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
|
||||||
s32 id, IMenuManager *menumgr, bool remap_dbl_click) :
|
s32 id, IMenuManager *menumgr, bool remap_click_outside) :
|
||||||
IGUIElement(gui::EGUIET_ELEMENT, env, parent, id,
|
IGUIElement(gui::EGUIET_ELEMENT, env, parent, id,
|
||||||
core::rect<s32>(0, 0, 100, 100)),
|
core::rect<s32>(0, 0, 100, 100)),
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
m_jni_field_name(""),
|
m_jni_field_name(""),
|
||||||
#endif
|
#endif
|
||||||
m_menumgr(menumgr),
|
m_menumgr(menumgr),
|
||||||
m_remap_dbl_click(remap_dbl_click)
|
m_remap_click_outside(remap_click_outside)
|
||||||
{
|
{
|
||||||
m_gui_scale = std::max(g_settings->getFloat("gui_scaling"), 0.5f);
|
m_gui_scale = std::max(g_settings->getFloat("gui_scaling"), 0.5f);
|
||||||
const float screen_dpi_scale = RenderingEngine::getDisplayDensity();
|
const float screen_dpi_scale = RenderingEngine::getDisplayDensity();
|
||||||
@ -50,9 +70,6 @@ GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
|
|||||||
|
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
m_menumgr->createdMenu(this);
|
m_menumgr->createdMenu(this);
|
||||||
|
|
||||||
m_last_touch.time = 0;
|
|
||||||
m_last_touch.pos = v2s32(0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GUIModalMenu::~GUIModalMenu()
|
GUIModalMenu::~GUIModalMenu()
|
||||||
@ -115,42 +132,53 @@ static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GUIModalMenu::remapDoubleClick(const SEvent &event)
|
bool GUIModalMenu::remapClickOutside(const SEvent &event)
|
||||||
{
|
{
|
||||||
/* The following code is for capturing double-clicks of the mouse button
|
if (!m_remap_click_outside || event.EventType != EET_MOUSE_INPUT_EVENT ||
|
||||||
* and translating the double-click into an EET_KEY_INPUT_EVENT event
|
(event.MouseInput.Event != EMIE_LMOUSE_PRESSED_DOWN &&
|
||||||
* -- which closes the form -- under some circumstances.
|
event.MouseInput.Event != EMIE_LMOUSE_LEFT_UP))
|
||||||
*
|
|
||||||
* 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;
|
return false;
|
||||||
|
|
||||||
if (event.EventType != EET_MOUSE_INPUT_EVENT ||
|
// The formspec must only be closed if both the EMIE_LMOUSE_PRESSED_DOWN and
|
||||||
event.MouseInput.Event != EMIE_LMOUSE_DOUBLE_CLICK)
|
// the EMIE_LMOUSE_LEFT_UP event haven't been absorbed by something else.
|
||||||
return false;
|
|
||||||
|
PointerAction last = m_last_click_outside;
|
||||||
|
m_last_click_outside = {}; // always reset
|
||||||
|
PointerAction current = PointerAction::fromEvent(event);
|
||||||
|
|
||||||
// Only exit if the double-click happened outside the menu.
|
|
||||||
gui::IGUIElement *hovered =
|
gui::IGUIElement *hovered =
|
||||||
Environment->getRootGUIElement()->getElementFromPoint(m_pointer);
|
Environment->getRootGUIElement()->getElementFromPoint(current.pos);
|
||||||
if (isChild(hovered, this))
|
if (isChild(hovered, this))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Translate double-click to escape.
|
// Dropping items is also done by tapping outside the formspec. If an item
|
||||||
SEvent translated{};
|
// is selected, make sure it is dropped without closing the formspec.
|
||||||
translated.EventType = EET_KEY_INPUT_EVENT;
|
// We have to explicitly restrict this to GUIInventoryList because other
|
||||||
translated.KeyInput.Key = KEY_ESCAPE;
|
// GUI elements like text fields like to absorb events for no reason.
|
||||||
translated.KeyInput.Control = false;
|
GUIInventoryList *focused = dynamic_cast<GUIInventoryList *>(Environment->getFocus());
|
||||||
translated.KeyInput.Shift = false;
|
if (focused && focused->OnEvent(event))
|
||||||
translated.KeyInput.PressedDown = true;
|
// Return true since the event was handled, even if it wasn't handled by us.
|
||||||
translated.KeyInput.Char = 0;
|
return true;
|
||||||
OnEvent(translated);
|
|
||||||
|
|
||||||
return true;
|
if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
|
||||||
|
m_last_click_outside = current;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP &&
|
||||||
|
current.isRelated(last)) {
|
||||||
|
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);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GUIModalMenu::simulateMouseEvent(ETOUCH_INPUT_EVENT touch_event, bool second_try)
|
bool GUIModalMenu::simulateMouseEvent(ETOUCH_INPUT_EVENT touch_event, bool second_try)
|
||||||
@ -319,20 +347,12 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
|
|||||||
|
|
||||||
// Detect double-taps and convert them into double-click events.
|
// Detect double-taps and convert them into double-click events.
|
||||||
if (event.TouchInput.Event == ETIE_PRESSED_DOWN) {
|
if (event.TouchInput.Event == ETIE_PRESSED_DOWN) {
|
||||||
u64 time_now = porting::getTimeMs();
|
PointerAction current = PointerAction::fromEvent(event);
|
||||||
u64 time_delta = porting::getDeltaMs(m_last_touch.time, time_now);
|
if (current.isRelated(m_last_touch)) {
|
||||||
|
|
||||||
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.
|
// ETIE_COUNT is used for double-tap events.
|
||||||
simulateMouseEvent(ETIE_COUNT);
|
simulateMouseEvent(ETIE_COUNT);
|
||||||
}
|
}
|
||||||
|
m_last_touch = current;
|
||||||
m_last_touch.time = time_now;
|
|
||||||
m_last_touch.pos = m_pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -361,7 +381,7 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
|
|||||||
m_touch_hovered.reset();
|
m_touch_hovered.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remapDoubleClick(event))
|
if (remapClickOutside(event))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,14 @@ enum class PointerType {
|
|||||||
Touch,
|
Touch,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PointerAction {
|
||||||
|
v2s32 pos;
|
||||||
|
u64 time; // ms
|
||||||
|
|
||||||
|
static PointerAction fromEvent(const SEvent &event);
|
||||||
|
bool isRelated(PointerAction other);
|
||||||
|
};
|
||||||
|
|
||||||
class GUIModalMenu;
|
class GUIModalMenu;
|
||||||
|
|
||||||
class IMenuManager
|
class IMenuManager
|
||||||
@ -94,14 +102,14 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
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 click outside the formspec to ESC. This is so that, for
|
||||||
* that, for example, Android users can double-tap to close a formspec.
|
* example, touchscreen users can close formspecs.
|
||||||
*
|
* The default for this setting is true. Currently, it's set to false for
|
||||||
* This value can (currently) only be set by the class constructor
|
* the mainmenu to prevent Minetest from closing unexpectedly.
|
||||||
* and the default value for the setting is true.
|
|
||||||
*/
|
*/
|
||||||
bool m_remap_dbl_click;
|
bool m_remap_click_outside;
|
||||||
bool remapDoubleClick(const SEvent &event);
|
bool remapClickOutside(const SEvent &event);
|
||||||
|
PointerAction m_last_click_outside{};
|
||||||
|
|
||||||
// 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
|
||||||
@ -111,13 +119,11 @@ private:
|
|||||||
|
|
||||||
irr_ptr<gui::IGUIElement> m_touch_hovered;
|
irr_ptr<gui::IGUIElement> m_touch_hovered;
|
||||||
|
|
||||||
|
// Converts touches into clicks.
|
||||||
bool simulateMouseEvent(ETOUCH_INPUT_EVENT touch_event, bool second_try=false);
|
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.
|
// Used to detect double-taps and convert them into double-click events.
|
||||||
struct {
|
PointerAction m_last_touch{};
|
||||||
v2s32 pos;
|
|
||||||
s64 time;
|
|
||||||
} m_last_touch;
|
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user