Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support both mouse and touch input in GUIs in a single binary #14146

Merged
merged 7 commits into from
Dec 27, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 15 additions & 24 deletions src/gui/guiFormSpecMenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3656,18 +3656,13 @@ void GUIFormSpecMenu::drawMenu()
NULL, m_client, IT_ROT_HOVERED);
}

// On touchscreens, m_pointer is set by GUIModalMenu::preprocessEvent instead.
#ifndef HAVE_TOUCHSCREENGUI
m_pointer = RenderingEngine::get_raw_device()->getCursorControl()->getPosition();
#endif

/*
Draw fields/buttons tooltips and update the mouse cursor
*/
gui::IGUIElement *hovered =
Environment->getRootGUIElement()->getElementFromPoint(m_pointer);

#ifndef HAVE_TOUCHSCREENGUI
#ifndef __ANDROID__
grorp marked this conversation as resolved.
Show resolved Hide resolved
gui::ICursorControl *cursor_control = RenderingEngine::get_raw_device()->
getCursorControl();
gui::ECURSOR_ICON current_cursor_icon = cursor_control->getActiveIcon();
Expand Down Expand Up @@ -3715,7 +3710,7 @@ void GUIFormSpecMenu::drawMenu()
m_tooltips[field.fname].bgcolor);
}

#ifndef HAVE_TOUCHSCREENGUI
#ifndef __ANDROID__
if (field.ftype != f_HyperText && // Handled directly in guiHyperText
current_cursor_icon != field.fcursor_icon)
cursor_control->setActiveIcon(field.fcursor_icon);
Expand All @@ -3730,7 +3725,7 @@ void GUIFormSpecMenu::drawMenu()

if (!hovered_element_found) {
// no element is hovered
#ifndef HAVE_TOUCHSCREENGUI
#ifndef __ANDROID__
if (current_cursor_icon != ECI_NORMAL)
cursor_control->setActiveIcon(ECI_NORMAL);
#endif
Expand Down Expand Up @@ -3764,16 +3759,13 @@ void GUIFormSpecMenu::showTooltip(const std::wstring &text,
v2u32 screenSize = Environment->getVideoDriver()->getScreenSize();
int tooltip_offset_x = m_btn_height;
int tooltip_offset_y = m_btn_height;
#ifdef HAVE_TOUCHSCREENGUI
tooltip_offset_x *= 3;
tooltip_offset_y = 0;
if (m_pointer.X > (s32)screenSize.X / 2)
tooltip_offset_x = -(tooltip_offset_x + tooltip_width);

// Hide tooltip after ETIE_LEFT_UP
if (m_pointer.X == 0)
return;
grorp marked this conversation as resolved.
Show resolved Hide resolved
#endif
if (m_pointer_type == PointerType::Touch) {
tooltip_offset_x *= 3;
tooltip_offset_y = 0;
if (m_pointer.X > (s32)screenSize.X / 2)
tooltip_offset_x = -(tooltip_offset_x + tooltip_width);
}

// Calculate and set the tooltip position
s32 tooltip_x = m_pointer.X + tooltip_offset_x;
Expand Down Expand Up @@ -4070,6 +4062,11 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode)

bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
{
// This must be done first so that GUIModalMenu can set m_pointer_type
// correctly.
if (GUIModalMenu::preprocessEvent(event))
return true;

// The IGUITabControl renders visually using the skin's selected
// font, which we override for the duration of form drawing,
// but computes tab hotspots based on how it would have rendered
Expand Down Expand Up @@ -4147,7 +4144,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
return handled;
}

return GUIModalMenu::preprocessEvent(event);
return false;
}

void GUIFormSpecMenu::tryClose()
Expand Down Expand Up @@ -4326,14 +4323,12 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
}
}

#ifdef HAVE_TOUCHSCREENGUI
// The second touch (see GUIModalMenu::preprocessEvent() function)
ButtonEventType touch = BET_OTHER;
if (event.EventType == EET_TOUCH_INPUT_EVENT) {
if (event.TouchInput.Event == ETIE_LEFT_UP)
touch = BET_RIGHT;
}
#endif

// Set this number to a positive value to generate a move action
// from m_selected_item to s.
Expand Down Expand Up @@ -4678,7 +4673,6 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
break;
}

#ifdef HAVE_TOUCHSCREENGUI
if (touch == BET_RIGHT && m_selected_item && !m_left_dragging) {
if (!s.isValid()) {
// Not a valid slot
Expand All @@ -4698,7 +4692,6 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
}
}
}
#endif

// Update left-dragged slots
if (m_left_dragging && m_left_drag_stacks.size() > 1) {
Expand Down Expand Up @@ -5067,10 +5060,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
}
}

#ifdef HAVE_TOUCHSCREENGUI
if (m_second_touch)
return true; // Stop propagating the event
#endif

return Parent ? Parent->OnEvent(event) : false;
}
Expand Down
4 changes: 2 additions & 2 deletions src/gui/guiHyperText.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1052,7 +1052,7 @@ void GUIHyperText::checkHover(s32 X, s32 Y)
}
}

#ifndef HAVE_TOUCHSCREENGUI
#ifndef __ANDROID__
if (m_drawer.m_hovertag)
RenderingEngine::get_raw_device()->getCursorControl()->setActiveIcon(
gui::ECI_HAND);
Expand All @@ -1075,7 +1075,7 @@ bool GUIHyperText::OnEvent(const SEvent &event)
if (event.EventType == EET_GUI_EVENT &&
event.GUIEvent.EventType == EGET_ELEMENT_LEFT) {
m_drawer.m_hovertag = nullptr;
#ifndef HAVE_TOUCHSCREENGUI
#ifndef __ANDROID__
gui::ICursorControl *cursor_control =
RenderingEngine::get_raw_device()->getCursorControl();
if (cursor_control->isVisible())
Expand Down
6 changes: 3 additions & 3 deletions src/gui/guiInventoryList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,10 @@ void GUIInventoryList::draw()

// Add hovering tooltip
bool show_tooltip = !item.empty() && hovering && !selected_item;
#ifdef HAVE_TOUCHSCREENGUI
// Make it possible to see item tooltips on touchscreens
show_tooltip |= hovering && selected && m_fs_menu->getSelectedAmount() != 0;
#endif
if (m_fs_menu->getPointerType() == PointerType::Touch) {
show_tooltip |= hovering && selected && m_fs_menu->getSelectedAmount() != 0;
}
if (show_tooltip) {
std::string tooltip = orig_item.getDescription(client->idef());
if (m_fs_menu->doTooltipAppendItemname())
Expand Down
76 changes: 45 additions & 31 deletions src/gui/modalMenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/

#include <cstdlib>
#include <IEventReceiver.h>
#include "client/renderingengine.h"
#include "modalMenu.h"
#include "gettext.h"
Expand Down Expand Up @@ -103,7 +104,7 @@ void GUIModalMenu::quitMenu()
m_menumgr->deletingMenu(this);
this->remove();
#ifdef HAVE_TOUCHSCREENGUI
if (g_touchscreengui && m_touchscreen_visible)
if (g_touchscreengui)
g_touchscreengui->show();
#endif
}
Expand Down Expand Up @@ -169,8 +170,6 @@ static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent)
return false;
}

#ifdef HAVE_TOUCHSCREENGUI

bool GUIModalMenu::simulateMouseEvent(
gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event)
{
Expand All @@ -194,41 +193,51 @@ bool GUIModalMenu::simulateMouseEvent(
default:
return false;
}
if (preprocessEvent(mouse_event))
return true;
if (!target)
return false;
return target->OnEvent(mouse_event);

bool retval;
m_simulated_mouse = true;
do {
if (preprocessEvent(mouse_event)) {
retval = true;
break;
}
if (!target) {
retval = false;
break;
}
retval = target->OnEvent(mouse_event);
} while (false);
m_simulated_mouse = false;

return retval;
}

void GUIModalMenu::enter(gui::IGUIElement *hovered)
{
if (!hovered)
return;
sanity_check(!m_hovered);
m_hovered.grab(hovered);
sanity_check(!m_touch_hovered);
m_touch_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.Caller = m_touch_hovered.get();
gui_event.GUIEvent.EventType = irr::gui::EGET_ELEMENT_HOVERED;
grorp marked this conversation as resolved.
Show resolved Hide resolved
gui_event.GUIEvent.Element = gui_event.GUIEvent.Caller;
m_hovered->OnEvent(gui_event);
m_touch_hovered->OnEvent(gui_event);
}

void GUIModalMenu::leave()
{
if (!m_hovered)
if (!m_touch_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();
gui_event.GUIEvent.Caller = m_touch_hovered.get();
gui_event.GUIEvent.EventType = irr::gui::EGET_ELEMENT_LEFT;
m_touch_hovered->OnEvent(gui_event);
m_touch_hovered.reset();
}

#endif

bool GUIModalMenu::preprocessEvent(const SEvent &event)
{
#ifdef __ANDROID__
Expand Down Expand Up @@ -268,25 +277,26 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
}
#endif

#ifdef HAVE_TOUCHSCREENGUI
// Convert touch events into mouse events.
if (event.EventType == EET_TOUCH_INPUT_EVENT) {
irr_ptr<GUIModalMenu> holder;
holder.grab(this); // keep this alive until return (it might be dropped downstream [?])

if (event.TouchInput.touchedCount == 1) {
if (event.TouchInput.Event == ETIE_PRESSED_DOWN || event.TouchInput.Event == ETIE_MOVED)
m_pointer = v2s32(event.TouchInput.X, event.TouchInput.Y);
m_pointer_type = PointerType::Touch;
m_pointer = v2s32(event.TouchInput.X, event.TouchInput.Y);

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) {
if (m_touch_hovered != hovered) {
leave();
enter(hovered);
}
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 (!ret && m_touch_hovered != focused)
ret = simulateMouseEvent(m_touch_hovered.get(), event.TouchInput.Event);
if (event.TouchInput.Event == ETIE_LEFT_UP)
leave();
return ret;
Expand All @@ -306,20 +316,24 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
return true;
}
}
#endif

if (event.EventType == EET_MOUSE_INPUT_EVENT) {
s32 x = event.MouseInput.X;
s32 y = event.MouseInput.Y;
if (!m_simulated_mouse) {
grorp marked this conversation as resolved.
Show resolved Hide resolved
// Only set the pointer type to mouse if this is a real mouse event.
m_pointer_type = PointerType::Mouse;
m_pointer = v2s32(event.MouseInput.X, event.MouseInput.Y);
m_touch_hovered.reset();
}

gui::IGUIElement *hovered =
Environment->getRootGUIElement()->getElementFromPoint(
core::position2d<s32>(x, y));
Environment->getRootGUIElement()->getElementFromPoint(m_pointer);
if (!isChild(hovered, this)) {
if (DoubleClickDetection(event)) {
return true;
}
}
}

return false;
}

Expand Down
24 changes: 18 additions & 6 deletions src/gui/modalMenu.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irr_ptr.h"
#include "util/string.h"

enum class PointerType {
Mouse,
Touch,
};

class GUIModalMenu;

class IMenuManager
Expand Down Expand Up @@ -58,6 +63,8 @@ class GUIModalMenu : public gui::IGUIElement
bool hasAndroidUIInput();
#endif

PointerType getPointerType() { return m_pointer_type; };

protected:
virtual std::wstring getLabelByID(s32 id) = 0;
virtual std::string getNameByID(s32 id) = 0;
Expand All @@ -69,18 +76,25 @@ class GUIModalMenu : public gui::IGUIElement
*/
bool DoubleClickDetection(const SEvent &event);

// Stores the last known pointer type.
PointerType m_pointer_type;
grorp marked this conversation as resolved.
Show resolved Hide resolved
// Stores the last known pointer position.
// If the last input event was a mouse event, it's the cursor position.
// If the last input event was a touch event, it's the finger position.
v2s32 m_pointer;
v2s32 m_old_pointer; // Mouse position after previous mouse event

v2u32 m_screensize_old;
float m_gui_scale;
#ifdef __ANDROID__
std::string m_jni_field_name;
#endif
#ifdef HAVE_TOUCHSCREENGUI

// This is set to true if the menu is currently processing a second-touch event.
bool m_second_touch = false;
bool m_touchscreen_visible = true;
#endif
// This is set to true if the menu is currently processing a mouse event
// that was synthesized by the menu itself from a touch event.
bool m_simulated_mouse = false;

private:
struct clickpos
Expand All @@ -102,11 +116,9 @@ class GUIModalMenu : public gui::IGUIElement
// wants to launch other menus
bool m_allow_focus_removal = false;

#ifdef HAVE_TOUCHSCREENGUI
grorp marked this conversation as resolved.
Show resolved Hide resolved
irr_ptr<gui::IGUIElement> m_hovered;
irr_ptr<gui::IGUIElement> m_touch_hovered;

bool simulateMouseEvent(gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event);
void enter(gui::IGUIElement *element);
void leave();
#endif
};