From f2b5c35fa292c9c84eda85f7b03b08427305b786 Mon Sep 17 00:00:00 2001 From: grorp Date: Wed, 17 Apr 2024 15:59:52 +0200 Subject: [PATCH] TouchScreenGUI dehardcoding refactor (#14472) --- src/client/clientlauncher.cpp | 4 - src/client/game.cpp | 4 +- src/gui/touchscreengui.cpp | 1004 +++++++++++++-------------------- src/gui/touchscreengui.h | 178 +++--- 4 files changed, 491 insertions(+), 699 deletions(-) diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index 56beba927..5ea089fea 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -206,10 +206,6 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args) if (!m_rendering_engine->run() || *kill) break; - if (g_settings->getBool("enable_touch")) { - g_touchscreengui = new TouchScreenGUI(m_rendering_engine->get_raw_device(), receiver); - } - the_game( kill, input, diff --git a/src/client/game.cpp b/src/client/game.cpp index d89b6639c..4bd3d593d 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1555,8 +1555,8 @@ bool Game::initGui() gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(), -1, chat_backend, client, &g_menumgr); - if (g_touchscreengui) - g_touchscreengui->init(texture_src); + if (g_settings->getBool("enable_touch")) + g_touchscreengui = new TouchScreenGUI(device, texture_src); return true; } diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchscreengui.cpp index 007420b08..d35d6f18d 100644 --- a/src/gui/touchscreengui.cpp +++ b/src/gui/touchscreengui.cpp @@ -36,23 +36,144 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include -using namespace irr::core; - TouchScreenGUI *g_touchscreengui; -const std::string button_image_names[] = { +static const char *button_image_names[] = { "jump_btn.png", "down.png", "zoom.png", - "aux1_btn.png" -}; + "aux1_btn.png", + "gear_icon.png", + "rare_controls.png", + + "fly_btn.png", + "noclip_btn.png", + "fast_btn.png", + "debug_btn.png", + "camera_btn.png", + "rangeview_btn.png", + "minimap_btn.png", + "", + + "chat_btn.png", + "inventory_btn.png", + "drop_btn.png", + "exit_btn.png", -const std::string joystick_image_names[] = { "joystick_off.png", "joystick_bg.png", - "joystick_center.png" + "joystick_center.png", }; +static void load_button_texture(IGUIImage *gui_button, const std::string &path, + const recti &button_rect, ISimpleTextureSource *tsrc, video::IVideoDriver *driver) +{ + video::ITexture *texture = guiScalingImageButton(driver, + tsrc->getTexture(path), button_rect.getWidth(), + button_rect.getHeight()); + gui_button->setImage(texture); + gui_button->setScaleImage(true); +} + +void button_info::emitAction(bool action, video::IVideoDriver *driver, + IEventReceiver *receiver, ISimpleTextureSource *tsrc) +{ + if (keycode == KEY_UNKNOWN) + return; + + SEvent translated{}; + translated.EventType = EET_KEY_INPUT_EVENT; + translated.KeyInput.Key = keycode; + translated.KeyInput.Control = false; + translated.KeyInput.Shift = false; + translated.KeyInput.Char = 0; + + if (action) { + translated.KeyInput.PressedDown = true; + receiver->OnEvent(translated); + + if (toggleable == button_info::FIRST_TEXTURE) { + toggleable = button_info::SECOND_TEXTURE; + load_button_texture(gui_button.get(), toggle_textures[1], + gui_button->getRelativePosition(), + tsrc, driver); + } else if (toggleable == button_info::SECOND_TEXTURE) { + toggleable = button_info::FIRST_TEXTURE; + load_button_texture(gui_button.get(), toggle_textures[0], + gui_button->getRelativePosition(), + tsrc, driver); + } + } else { + translated.KeyInput.PressedDown = false; + receiver->OnEvent(translated); + } +} + +static bool buttons_handlePress(std::vector &buttons, size_t pointer_id, IGUIElement *element, + video::IVideoDriver *driver, IEventReceiver *receiver, ISimpleTextureSource *tsrc) +{ + if (!element) + return false; + + for (button_info &btn : buttons) { + if (btn.gui_button.get() == element) { + assert(std::find(btn.pointer_ids.begin(), btn.pointer_ids.end(), pointer_id) == btn.pointer_ids.end()); + btn.pointer_ids.push_back(pointer_id); + + if (btn.pointer_ids.size() > 1) + return true; + + btn.emitAction(true, driver, receiver, tsrc); + btn.repeat_counter = -BUTTON_REPEAT_DELAY; + return true; + } + } + + return false; +} + + +static bool buttons_handleRelease(std::vector &buttons, size_t pointer_id, + video::IVideoDriver *driver, IEventReceiver *receiver, ISimpleTextureSource *tsrc) +{ + for (button_info &btn : buttons) { + auto it = std::find(btn.pointer_ids.begin(), btn.pointer_ids.end(), pointer_id); + if (it != btn.pointer_ids.end()) { + btn.pointer_ids.erase(it); + + if (!btn.pointer_ids.empty()) + return true; + + btn.emitAction(false, driver, receiver, tsrc); + return true; + } + } + + return false; +} + +static bool buttons_step(std::vector &buttons, float dtime, + video::IVideoDriver *driver, IEventReceiver *receiver, ISimpleTextureSource *tsrc) +{ + bool has_pointers = false; + + for (button_info &btn : buttons) { + if (btn.pointer_ids.empty()) + continue; + has_pointers = true; + + btn.repeat_counter += dtime; + if (btn.repeat_counter < BUTTON_REPEAT_INTERVAL) + continue; + + btn.emitAction(false, driver, receiver, tsrc); + btn.emitAction(true, driver, receiver, tsrc); + btn.repeat_counter = 0.0f; + } + + return has_pointers; +} + static EKEY_CODE id_to_keycode(touch_gui_button_id id) { EKEY_CODE code; @@ -65,7 +186,7 @@ static EKEY_CODE id_to_keycode(touch_gui_button_id id) case jump_id: key = "jump"; break; - case crunch_id: + case sneak_id: key = "sneak"; break; case zoom_id: @@ -122,93 +243,28 @@ static EKEY_CODE id_to_keycode(touch_gui_button_id id) return code; } -static void load_button_texture(const button_info *btn, const std::string &path, - const rect &button_rect, ISimpleTextureSource *tsrc, video::IVideoDriver *driver) -{ - u32 tid; - video::ITexture *texture = guiScalingImageButton(driver, - tsrc->getTexture(path, &tid), button_rect.getWidth(), - button_rect.getHeight()); - if (texture) { - btn->gui_button->setUseAlphaChannel(true); - if (g_settings->getBool("gui_scaling_filter")) { - rect txr_rect = rect(0, 0, button_rect.getWidth(), button_rect.getHeight()); - btn->gui_button->setImage(texture, txr_rect); - btn->gui_button->setPressedImage(texture, txr_rect); - btn->gui_button->setScaleImage(false); - } else { - btn->gui_button->setImage(texture); - btn->gui_button->setPressedImage(texture); - btn->gui_button->setScaleImage(true); - } - btn->gui_button->setDrawBorder(false); - btn->gui_button->setText(L""); - } -} - -AutoHideButtonBar::AutoHideButtonBar(IrrlichtDevice *device, - IEventReceiver *receiver) : +AutoHideButtonBar::AutoHideButtonBar(IrrlichtDevice *device, ISimpleTextureSource *tsrc, + touch_gui_button_id starter_id, const std::string &starter_img, + recti starter_rect, autohide_button_bar_dir dir) : m_driver(device->getVideoDriver()), m_guienv(device->getGUIEnvironment()), - m_receiver(receiver) + m_receiver(device->getEventReceiver()), + m_texturesource(tsrc) { -} + m_upper_left = starter_rect.UpperLeftCorner; + m_lower_right = starter_rect.LowerRightCorner; -void AutoHideButtonBar::init(ISimpleTextureSource *tsrc, - const std::string &starter_img, int button_id, const v2s32 &UpperLeft, - const v2s32 &LowerRight, autohide_button_bar_dir dir, float timeout) -{ - m_texturesource = tsrc; - - m_upper_left = UpperLeft; - m_lower_right = LowerRight; - - rect starter_rect = rect(UpperLeft.X, UpperLeft.Y, LowerRight.X, LowerRight.Y); - - IGUIButton *starter_gui_button = m_guienv->addButton(starter_rect, nullptr, - button_id, L"", nullptr); - - m_starter.gui_button = starter_gui_button; - m_starter.gui_button->grab(); - m_starter.repeat_counter = -1.0f; - m_starter.keycode = KEY_OEM_8; // use invalid keycode as it's not relevant - m_starter.immediate_release = true; - m_starter.ids.clear(); - - load_button_texture(&m_starter, starter_img, starter_rect, + IGUIImage *starter_gui_button = m_guienv->addImage(starter_rect, nullptr, + starter_id); + load_button_texture(starter_gui_button, starter_img, starter_rect, m_texturesource, m_driver); + m_starter = grab_gui_element(starter_gui_button); m_dir = dir; - m_timeout_value = timeout; - - m_initialized = true; } -AutoHideButtonBar::~AutoHideButtonBar() +void AutoHideButtonBar::addButton(touch_gui_button_id id, const std::string &image) { - if (m_starter.gui_button) { - m_starter.gui_button->setVisible(false); - m_starter.gui_button->drop(); - m_starter.gui_button = nullptr; - } - - for (auto &button : m_buttons) { - if (button->gui_button) { - button->gui_button->drop(); - button->gui_button = nullptr; - } - } -} - -void AutoHideButtonBar::addButton(touch_gui_button_id button_id, const wchar_t *caption, - const std::string &btn_image) -{ - - if (!m_initialized) { - errorstream << "AutoHideButtonBar::addButton not yet initialized!" << std::endl; - return; - } - int button_size = 0; if (m_dir == AHBB_Dir_Top_Bottom || m_dir == AHBB_Dir_Bottom_Top) @@ -216,7 +272,7 @@ void AutoHideButtonBar::addButton(touch_gui_button_id button_id, const wchar_t * else button_size = m_lower_right.Y - m_upper_left.Y; - irr::core::rect current_button; + recti current_button; if (m_dir == AHBB_Dir_Right_Left || m_dir == AHBB_Dir_Left_Right) { int x_start = 0; @@ -232,7 +288,7 @@ void AutoHideButtonBar::addButton(touch_gui_button_id button_id, const wchar_t * x_start = x_end - button_size; } - current_button = rect(x_start, m_upper_left.Y, x_end, m_lower_right.Y); + current_button = recti(x_start, m_upper_left.Y, x_end, m_lower_right.Y); } else { double y_start = 0; double y_end = 0; @@ -247,371 +303,238 @@ void AutoHideButtonBar::addButton(touch_gui_button_id button_id, const wchar_t * y_start = y_end - button_size; } - current_button = rect(m_upper_left.X, y_start, m_lower_right.Y, y_end); + current_button = recti(m_upper_left.X, y_start, m_lower_right.Y, y_end); } - IGUIButton *btn_gui_button = m_guienv->addButton(current_button, nullptr, button_id, - caption, nullptr); - - std::shared_ptr btn(new button_info); - btn->gui_button = btn_gui_button; - btn->gui_button->grab(); - btn->gui_button->setVisible(false); - btn->gui_button->setEnabled(false); - btn->repeat_counter = -1.0f; - btn->keycode = id_to_keycode(button_id); - btn->immediate_release = true; - btn->ids.clear(); - - load_button_texture(btn.get(), btn_image, current_button, m_texturesource, m_driver); + IGUIImage *btn_gui_button = m_guienv->addImage(current_button, nullptr, id); + btn_gui_button->setVisible(false); + btn_gui_button->setEnabled(false); + load_button_texture(btn_gui_button, image, current_button, m_texturesource, m_driver); + button_info btn{}; + btn.keycode = id_to_keycode(id); + btn.gui_button = grab_gui_element(btn_gui_button); m_buttons.push_back(btn); } -void AutoHideButtonBar::addToggleButton(touch_gui_button_id button_id, const wchar_t *caption, - const std::string &btn_image_1, const std::string &btn_image_2) +void AutoHideButtonBar::addToggleButton(touch_gui_button_id id, + const std::string &image_1, const std::string &image_2) { - addButton(button_id, caption, btn_image_1); - std::shared_ptr btn = m_buttons.back(); - btn->toggleable = button_info::FIRST_TEXTURE; - btn->textures.push_back(btn_image_1); - btn->textures.push_back(btn_image_2); + addButton(id, image_1); + button_info &btn = m_buttons.back(); + btn.toggleable = button_info::FIRST_TEXTURE; + btn.toggle_textures[0] = image_1; + btn.toggle_textures[1] = image_2; } -bool AutoHideButtonBar::isButton(const SEvent &event) +bool AutoHideButtonBar::handlePress(size_t pointer_id, IGUIElement *element) { - IGUIElement *rootguielement = m_guienv->getRootGUIElement(); - - if (rootguielement == nullptr) - return false; - - gui::IGUIElement *element = rootguielement->getElementFromPoint( - v2s32(event.TouchInput.X, event.TouchInput.Y)); - - if (element == nullptr) - return false; - if (m_active) { - // check for all buttons in vector - for (const auto &button : m_buttons) { - if (button->gui_button == element) { - SEvent translated{}; - translated.EventType = irr::EET_KEY_INPUT_EVENT; - translated.KeyInput.Key = button->keycode; - translated.KeyInput.Control = false; - translated.KeyInput.Shift = false; - translated.KeyInput.Char = 0; - - // add this event - translated.KeyInput.PressedDown = true; - m_receiver->OnEvent(translated); - - // remove this event - translated.KeyInput.PressedDown = false; - m_receiver->OnEvent(translated); - - button->ids.push_back(event.TouchInput.ID); - - m_timeout = 0.0f; - - if (button->toggleable == button_info::FIRST_TEXTURE) { - button->toggleable = button_info::SECOND_TEXTURE; - load_button_texture(button.get(), button->textures[1], - button->gui_button->getRelativePosition(), - m_texturesource, m_driver); - } else if (button->toggleable == button_info::SECOND_TEXTURE) { - button->toggleable = button_info::FIRST_TEXTURE; - load_button_texture(button.get(), button->textures[0], - button->gui_button->getRelativePosition(), - m_texturesource, m_driver); - } - - return true; - } - } - } else { - // check for starter button only - if (element == m_starter.gui_button) { - m_starter.ids.push_back(event.TouchInput.ID); - m_starter.gui_button->setVisible(false); - m_starter.gui_button->setEnabled(false); - m_active = true; - m_timeout = 0.0f; - - for (const auto &button : m_buttons) { - button->gui_button->setVisible(true); - button->gui_button->setEnabled(true); - } - - return true; - } + return buttons_handlePress(m_buttons, pointer_id, element, m_driver, + m_receiver, m_texturesource); + } + if (m_starter.get() == element) { + activate(); + return true; } return false; } +bool AutoHideButtonBar::handleRelease(size_t pointer_id) +{ + return buttons_handleRelease(m_buttons, pointer_id, m_driver, + m_receiver, m_texturesource); +} + void AutoHideButtonBar::step(float dtime) { - if (m_active) { - m_timeout += dtime; + // Since buttons can stay pressed after the buttonbar is deactivated, + // we call the step function even if the buttonbar is inactive. + bool has_pointers = buttons_step(m_buttons, dtime, m_driver, m_receiver, + m_texturesource); - if (m_timeout > m_timeout_value) - deactivate(); + if (m_active) { + if (!has_pointers) { + m_timeout += dtime; + if (m_timeout > BUTTONBAR_HIDE_DELAY) + deactivate(); + } else { + m_timeout = 0.0f; + } } + +} + +void AutoHideButtonBar::updateVisibility() { + bool starter_visible = m_visible && !m_active; + bool inner_visible = m_visible && m_active; + + m_starter->setVisible(starter_visible); + m_starter->setEnabled(starter_visible); + + for (auto &button : m_buttons) { + button.gui_button->setVisible(inner_visible); + button.gui_button->setEnabled(inner_visible); + } +} + +void AutoHideButtonBar::activate() +{ + m_active = true; + m_timeout = 0.0f; + updateVisibility(); } void AutoHideButtonBar::deactivate() { - if (m_visible) { - m_starter.gui_button->setVisible(true); - m_starter.gui_button->setEnabled(true); - } m_active = false; - - for (const auto &button : m_buttons) { - button->gui_button->setVisible(false); - button->gui_button->setEnabled(false); - } -} - -void AutoHideButtonBar::hide() -{ - m_visible = false; - m_starter.gui_button->setVisible(false); - m_starter.gui_button->setEnabled(false); - - for (const auto &button : m_buttons) { - button->gui_button->setVisible(false); - button->gui_button->setEnabled(false); - } + updateVisibility(); } void AutoHideButtonBar::show() { m_visible = true; - - if (m_active) { - for (const auto &button : m_buttons) { - button->gui_button->setVisible(true); - button->gui_button->setEnabled(true); - } - } else { - m_starter.gui_button->setVisible(true); - m_starter.gui_button->setEnabled(true); - } + updateVisibility(); } -TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver *receiver): +void AutoHideButtonBar::hide() +{ + m_visible = false; + updateVisibility(); +} + +TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, ISimpleTextureSource *tsrc): m_device(device), m_guienv(device->getGUIEnvironment()), - m_receiver(receiver), - m_settings_bar(device, receiver), - m_rare_controls_bar(device, receiver) + m_receiver(device->getEventReceiver()), + m_texturesource(tsrc) { - for (auto &button : m_buttons) { - button.gui_button = nullptr; - button.repeat_counter = -1.0f; - button.repeat_delay = BUTTON_REPEAT_DELAY; - } - m_touchscreen_threshold = g_settings->getU16("touchscreen_threshold"); m_long_tap_delay = g_settings->getU16("touch_long_tap_delay"); m_fixed_joystick = g_settings->getBool("fixed_virtual_joystick"); m_joystick_triggers_aux1 = g_settings->getBool("virtual_joystick_triggers_aux1"); m_screensize = m_device->getVideoDriver()->getScreenSize(); - button_size = MYMIN(m_screensize.Y / 4.5f, + m_button_size = MYMIN(m_screensize.Y / 4.5f, RenderingEngine::getDisplayDensity() * 65.0f * g_settings->getFloat("hud_scaling")); -} - -void TouchScreenGUI::initButton(touch_gui_button_id id, const rect &button_rect, - const std::wstring &caption, bool immediate_release, float repeat_delay) -{ - IGUIButton *btn_gui_button = m_guienv->addButton(button_rect, nullptr, id, caption.c_str()); - - button_info *btn = &m_buttons[id]; - btn->gui_button = btn_gui_button; - btn->gui_button->grab(); - btn->repeat_counter = -1.0f; - btn->repeat_delay = repeat_delay; - btn->keycode = id_to_keycode(id); - btn->immediate_release = immediate_release; - btn->ids.clear(); - - load_button_texture(btn, button_image_names[id], button_rect, - m_texturesource, m_device->getVideoDriver()); -} - -std::shared_ptr TouchScreenGUI::initJoystickButton(touch_gui_button_id id, - const rect &button_rect, int texture_id, bool visible) -{ - IGUIButton *btn_gui_button = m_guienv->addButton(button_rect, nullptr, id, L"O"); - - std::shared_ptr btn(new button_info); - btn->gui_button = btn_gui_button; - btn->gui_button->setVisible(visible); - btn->gui_button->grab(); - btn->ids.clear(); - - load_button_texture(btn.get(), joystick_image_names[texture_id], button_rect, - m_texturesource, m_device->getVideoDriver()); - - return btn; -} - -void TouchScreenGUI::init(ISimpleTextureSource *tsrc) -{ - assert(tsrc); - - m_visible = true; - m_texturesource = tsrc; // Initialize joystick display "button". // Joystick is placed on the bottom left of screen. if (m_fixed_joystick) { - m_joystick_btn_off = initJoystickButton(joystick_off_id, - rect(button_size, - m_screensize.Y - button_size * 4, - button_size * 4, - m_screensize.Y - button_size), 0); + m_joystick_btn_off = grab_gui_element(makeJoystickButton(joystick_off_id, + recti(m_button_size, + m_screensize.Y - m_button_size * 4, + m_button_size * 4, + m_screensize.Y - m_button_size), true)); } else { - m_joystick_btn_off = initJoystickButton(joystick_off_id, - rect(button_size, - m_screensize.Y - button_size * 3, - button_size * 3, - m_screensize.Y - button_size), 0); + m_joystick_btn_off = grab_gui_element(makeJoystickButton(joystick_off_id, + recti(m_button_size, + m_screensize.Y - m_button_size * 3, + m_button_size * 3, + m_screensize.Y - m_button_size), true)); } - m_joystick_btn_bg = initJoystickButton(joystick_bg_id, - rect(button_size, - m_screensize.Y - button_size * 4, - button_size * 4, - m_screensize.Y - button_size), - 1, false); + m_joystick_btn_bg = grab_gui_element(makeJoystickButton(joystick_bg_id, + recti(m_button_size, + m_screensize.Y - m_button_size * 4, + m_button_size * 4, + m_screensize.Y - m_button_size), false)); - m_joystick_btn_center = initJoystickButton(joystick_center_id, - rect(0, 0, button_size, button_size), 2, false); + m_joystick_btn_center = grab_gui_element(makeJoystickButton(joystick_center_id, + recti(0, 0, m_button_size, m_button_size), false)); // init jump button - initButton(jump_id, - rect(m_screensize.X - 1.75f * button_size, - m_screensize.Y - button_size, - m_screensize.X - 0.25f * button_size, - m_screensize.Y), - L"x", false); + addButton(jump_id, button_image_names[jump_id], + recti(m_screensize.X - 1.75f * m_button_size, + m_screensize.Y - m_button_size, + m_screensize.X - 0.25f * m_button_size, + m_screensize.Y)); - // init crunch button - initButton(crunch_id, - rect(m_screensize.X - 3.25f * button_size, - m_screensize.Y - button_size, - m_screensize.X - 1.75f * button_size, - m_screensize.Y), - L"H", false); + // init sneak button + addButton(sneak_id, button_image_names[sneak_id], + recti(m_screensize.X - 3.25f * m_button_size, + m_screensize.Y - m_button_size, + m_screensize.X - 1.75f * m_button_size, + m_screensize.Y)); // init zoom button - initButton(zoom_id, - rect(m_screensize.X - 1.25f * button_size, - m_screensize.Y - 4 * button_size, - m_screensize.X - 0.25f * button_size, - m_screensize.Y - 3 * button_size), - L"z", false); + addButton(zoom_id, button_image_names[zoom_id], + recti(m_screensize.X - 1.25f * m_button_size, + m_screensize.Y - 4 * m_button_size, + m_screensize.X - 0.25f * m_button_size, + m_screensize.Y - 3 * m_button_size)); // init aux1 button if (!m_joystick_triggers_aux1) - initButton(aux1_id, - rect(m_screensize.X - 1.25f * button_size, - m_screensize.Y - 2.5f * button_size, - m_screensize.X - 0.25f * button_size, - m_screensize.Y - 1.5f * button_size), - L"spc1", false); + addButton(aux1_id, button_image_names[aux1_id], + recti(m_screensize.X - 1.25f * m_button_size, + m_screensize.Y - 2.5f * m_button_size, + m_screensize.X - 0.25f * m_button_size, + m_screensize.Y - 1.5f * m_button_size)); - m_settings_bar.init(m_texturesource, "gear_icon.png", settings_starter_id, - v2s32(m_screensize.X - 1.25f * button_size, - m_screensize.Y - (SETTINGS_BAR_Y_OFFSET + 1.0f) * button_size - + 0.5f * button_size), - v2s32(m_screensize.X - 0.25f * button_size, - m_screensize.Y - SETTINGS_BAR_Y_OFFSET * button_size - + 0.5f * button_size), - AHBB_Dir_Right_Left, 3.0f); + AutoHideButtonBar &settings_bar = m_buttonbars.emplace_back(m_device, m_texturesource, + settings_starter_id, button_image_names[settings_starter_id], + recti(m_screensize.X - 1.25f * m_button_size, + m_screensize.Y - (SETTINGS_BAR_Y_OFFSET + 1.0f) * m_button_size + + 0.5f * m_button_size, + m_screensize.X - 0.25f * m_button_size, + m_screensize.Y - SETTINGS_BAR_Y_OFFSET * m_button_size + + 0.5f * m_button_size), + AHBB_Dir_Right_Left); - const static std::map settings_bar_buttons { - {fly_id, "fly"}, - {noclip_id, "noclip"}, - {fast_id, "fast"}, - {debug_id, "debug"}, - {camera_id, "camera"}, - {range_id, "rangeview"}, - {minimap_id, "minimap"}, + const static touch_gui_button_id settings_bar_buttons[] { + fly_id, noclip_id, fast_id, debug_id, camera_id, range_id, minimap_id, }; - for (const auto &pair : settings_bar_buttons) { - if (id_to_keycode(pair.first) == KEY_UNKNOWN) + for (auto id : settings_bar_buttons) { + if (id_to_keycode(id) == KEY_UNKNOWN) continue; - - std::wstring wide = utf8_to_wide(pair.second); - m_settings_bar.addButton(pair.first, wide.c_str(), - pair.second + "_btn.png"); + settings_bar.addButton(id, button_image_names[id]); } // Chat is shown by default, so chat_hide_btn.png is shown first. - m_settings_bar.addToggleButton(toggle_chat_id, L"togglechat", + settings_bar.addToggleButton(toggle_chat_id, "chat_hide_btn.png", "chat_show_btn.png"); - m_rare_controls_bar.init(m_texturesource, "rare_controls.png", - rare_controls_starter_id, - v2s32(0.25f * button_size, - m_screensize.Y - (RARE_CONTROLS_BAR_Y_OFFSET + 1.0f) * button_size - + 0.5f * button_size), - v2s32(0.75f * button_size, - m_screensize.Y - RARE_CONTROLS_BAR_Y_OFFSET * button_size - + 0.5f * button_size), - AHBB_Dir_Left_Right, 2.0f); + AutoHideButtonBar &rare_controls_bar = m_buttonbars.emplace_back(m_device, m_texturesource, + rare_controls_starter_id, button_image_names[rare_controls_starter_id], + recti(0.25f * m_button_size, + m_screensize.Y - (RARE_CONTROLS_BAR_Y_OFFSET + 1.0f) * m_button_size + + 0.5f * m_button_size, + 0.75f * m_button_size, + m_screensize.Y - RARE_CONTROLS_BAR_Y_OFFSET * m_button_size + + 0.5f * m_button_size), + AHBB_Dir_Left_Right); - const static std::map rare_controls_bar_buttons { - {chat_id, "chat"}, - {inventory_id, "inventory"}, - {drop_id, "drop"}, - {exit_id, "exit"}, + const static touch_gui_button_id rare_controls_bar_buttons[] { + chat_id, inventory_id, drop_id, exit_id, }; - for (const auto &pair : rare_controls_bar_buttons) { - if (id_to_keycode(pair.first) == KEY_UNKNOWN) + for (auto id : rare_controls_bar_buttons) { + if (id_to_keycode(id) == KEY_UNKNOWN) continue; - - std::wstring wide = utf8_to_wide(pair.second); - m_rare_controls_bar.addButton(pair.first, wide.c_str(), - pair.second + "_btn.png"); + rare_controls_bar.addButton(id, button_image_names[id]); } - - m_initialized = true; } -touch_gui_button_id TouchScreenGUI::getButtonID(s32 x, s32 y) +void TouchScreenGUI::addButton(touch_gui_button_id id, const std::string &image, const recti &rect) { - IGUIElement *rootguielement = m_guienv->getRootGUIElement(); + IGUIImage *btn_gui_button = m_guienv->addImage(rect, nullptr, id); + load_button_texture(btn_gui_button, image, rect, + m_texturesource, m_device->getVideoDriver()); - if (rootguielement != nullptr) { - gui::IGUIElement *element = - rootguielement->getElementFromPoint(v2s32(x, y)); - - if (element) - for (unsigned int i = 0; i < after_last_element_id; i++) - if (element == m_buttons[i].gui_button) - return (touch_gui_button_id) i; - } - - return after_last_element_id; + button_info &btn = m_buttons.emplace_back(); + btn.keycode = id_to_keycode(id); + btn.gui_button = grab_gui_element(btn_gui_button); } -touch_gui_button_id TouchScreenGUI::getButtonID(size_t eventID) +IGUIImage *TouchScreenGUI::makeJoystickButton(touch_gui_button_id id, + const recti &button_rect, bool visible) { - for (unsigned int i = 0; i < after_last_element_id; i++) { - button_info *btn = &m_buttons[i]; + IGUIImage *btn_gui_button = m_guienv->addImage(button_rect, nullptr, id); + btn_gui_button->setVisible(visible); + load_button_texture(btn_gui_button, button_image_names[id], button_rect, + m_texturesource, m_device->getVideoDriver()); - auto id = std::find(btn->ids.begin(), btn->ids.end(), eventID); - - if (id != btn->ids.end()) - return (touch_gui_button_id) i; - } - - return after_last_element_id; + return btn_gui_button; } bool TouchScreenGUI::isHotbarButton(const SEvent &event) @@ -636,56 +559,25 @@ std::optional TouchScreenGUI::getHotbarSelection() return selection; } -void TouchScreenGUI::handleButtonEvent(touch_gui_button_id button, - size_t eventID, bool action) +void TouchScreenGUI::handleReleaseEvent(size_t pointer_id) { - button_info *btn = &m_buttons[button]; - SEvent translated{}; - translated.EventType = irr::EET_KEY_INPUT_EVENT; - translated.KeyInput.Key = btn->keycode; - translated.KeyInput.Control = false; - translated.KeyInput.Shift = false; - translated.KeyInput.Char = 0; + // By the way: Android reuses pointer IDs, so m_pointer_pos[pointer_id] + // will be overwritten soon anyway. + m_pointer_downpos.erase(pointer_id); + m_pointer_pos.erase(pointer_id); - // add this event - if (action) { - assert(std::find(btn->ids.begin(), btn->ids.end(), eventID) == btn->ids.end()); + // handle buttons + if (buttons_handleRelease(m_buttons, pointer_id, m_device->getVideoDriver(), + m_receiver, m_texturesource)) + return; - btn->ids.push_back(eventID); - - if (btn->ids.size() > 1) + // handle buttonbars + for (AutoHideButtonBar &bar : m_buttonbars) { + if (bar.handleRelease(pointer_id)) return; - - btn->repeat_counter = 0.0f; - translated.KeyInput.PressedDown = true; - translated.KeyInput.Key = btn->keycode; - m_receiver->OnEvent(translated); } - // remove event - if (!action || btn->immediate_release) { - auto pos = std::find(btn->ids.begin(), btn->ids.end(), eventID); - // has to be in touch list - assert(pos != btn->ids.end()); - btn->ids.erase(pos); - - if (!btn->ids.empty()) - return; - - translated.KeyInput.PressedDown = false; - btn->repeat_counter = -1.0f; - m_receiver->OnEvent(translated); - } -} - -void TouchScreenGUI::handleReleaseEvent(size_t evt_id) -{ - touch_gui_button_id button = getButtonID(evt_id); - - if (button != after_last_element_id) { - // handle button events - handleButtonEvent(button, evt_id, false); - } else if (m_has_move_id && evt_id == m_move_id) { + if (m_has_move_id && pointer_id == m_move_id) { // handle the point used for moving view m_has_move_id = false; @@ -700,7 +592,7 @@ void TouchScreenGUI::handleReleaseEvent(size_t evt_id) } // handle joystick - else if (m_has_joystick_id && evt_id == m_joystick_id) { + else if (m_has_joystick_id && pointer_id == m_joystick_id) { m_has_joystick_id = false; // reset joystick @@ -709,25 +601,17 @@ void TouchScreenGUI::handleReleaseEvent(size_t evt_id) m_joystick_status_aux1 = false; applyJoystickStatus(); - m_joystick_btn_off->gui_button->setVisible(true); - m_joystick_btn_bg->gui_button->setVisible(false); - m_joystick_btn_center->gui_button->setVisible(false); + m_joystick_btn_off->setVisible(true); + m_joystick_btn_bg->setVisible(false); + m_joystick_btn_center->setVisible(false); } else { infostream << "TouchScreenGUI::translateEvent released unknown button: " - << evt_id << std::endl; + << pointer_id << std::endl; } - - // By the way: Android reuses pointer IDs, so m_pointer_pos[evt_id] - // will be overwritten soon anyway. - m_pointer_downpos.erase(evt_id); - m_pointer_pos.erase(evt_id); } void TouchScreenGUI::translateEvent(const SEvent &event) { - if (!m_initialized) - return; - if (!m_visible) { infostream << "TouchScreenGUI::translateEvent got event but is not visible!" << std::endl; @@ -737,7 +621,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event) if (event.EventType != EET_TOUCH_INPUT_EVENT) return; - const s32 half_button_size = button_size / 2.0f; + const s32 half_button_size = m_button_size / 2.0f; const s32 fixed_joystick_range_sq = half_button_size * half_button_size * 3 * 3; const s32 X = event.TouchInput.X; const s32 Y = event.TouchInput.Y; @@ -747,71 +631,80 @@ void TouchScreenGUI::translateEvent(const SEvent &event) const v2s32 dir_fixed = touch_pos - fixed_joystick_center; if (event.TouchInput.Event == ETIE_PRESSED_DOWN) { - size_t eventID = event.TouchInput.ID; + size_t pointer_id = event.TouchInput.ID; + m_pointer_downpos[pointer_id] = touch_pos; + m_pointer_pos[pointer_id] = touch_pos; - touch_gui_button_id button = getButtonID(X, Y); + IGUIElement *element = m_guienv->getRootGUIElement()->getElementFromPoint(touch_pos); - // handle button events - if (button != after_last_element_id) { - handleButtonEvent(button, eventID, true); - m_settings_bar.deactivate(); - m_rare_controls_bar.deactivate(); - } else if (isHotbarButton(event)) { - m_settings_bar.deactivate(); - m_rare_controls_bar.deactivate(); - // already handled in isHotbarButton() - } else if (m_settings_bar.isButton(event)) { - m_rare_controls_bar.deactivate(); - // already handled in isSettingsBarButton() - } else if (m_rare_controls_bar.isButton(event)) { - m_settings_bar.deactivate(); - // already handled in isSettingsBarButton() - } else { - // handle non button events - if (m_settings_bar.active() || m_rare_controls_bar.active()) { - m_settings_bar.deactivate(); - m_rare_controls_bar.deactivate(); + // handle buttons + if (buttons_handlePress(m_buttons, pointer_id, element, + m_device->getVideoDriver(), m_receiver, m_texturesource)) { + for (AutoHideButtonBar &bar : m_buttonbars) + bar.deactivate(); + return; + } + + // handle buttonbars + for (AutoHideButtonBar &bar : m_buttonbars) { + if (bar.handlePress(pointer_id, element)) { + for (AutoHideButtonBar &other : m_buttonbars) + if (other != bar) + other.deactivate(); return; } - - // Select joystick when joystick tapped (fixed joystick position) or - // when left 1/3 of screen dragged (free joystick position) - if ((m_fixed_joystick && dir_fixed.getLengthSQ() <= fixed_joystick_range_sq) || - (!m_fixed_joystick && X < m_screensize.X / 3.0f)) { - // If we don't already have a starting point for joystick, make this the one. - if (!m_has_joystick_id) { - m_has_joystick_id = true; - m_joystick_id = event.TouchInput.ID; - m_joystick_has_really_moved = false; - - m_joystick_btn_off->gui_button->setVisible(false); - m_joystick_btn_bg->gui_button->setVisible(true); - m_joystick_btn_center->gui_button->setVisible(true); - - // If it's a fixed joystick, don't move the joystick "button". - if (!m_fixed_joystick) - m_joystick_btn_bg->gui_button->setRelativePosition( - touch_pos - half_button_size * 3); - - m_joystick_btn_center->gui_button->setRelativePosition( - touch_pos - half_button_size); - } - } else { - // If we don't already have a moving point, make this the moving one. - if (!m_has_move_id) { - m_has_move_id = true; - m_move_id = event.TouchInput.ID; - m_move_has_really_moved = false; - m_move_downtime = porting::getTimeMs(); - m_move_pos = touch_pos; - // DON'T reset m_tap_state here, otherwise many short taps - // will be ignored if you tap very fast. - } - } } - m_pointer_downpos[event.TouchInput.ID] = touch_pos; - m_pointer_pos[event.TouchInput.ID] = touch_pos; + // handle hotbar + if (isHotbarButton(event)) { + // already handled in isHotbarButton() + for (AutoHideButtonBar &bar : m_buttonbars) + bar.deactivate(); + return; + } + + // handle non button events + for (AutoHideButtonBar &bar : m_buttonbars) { + if (bar.isActive()) { + bar.deactivate(); + return; + } + } + + // Select joystick when joystick tapped (fixed joystick position) or + // when left 1/3 of screen dragged (free joystick position) + if ((m_fixed_joystick && dir_fixed.getLengthSQ() <= fixed_joystick_range_sq) || + (!m_fixed_joystick && X < m_screensize.X / 3.0f)) { + // If we don't already have a starting point for joystick, make this the one. + if (!m_has_joystick_id) { + m_has_joystick_id = true; + m_joystick_id = pointer_id; + m_joystick_has_really_moved = false; + + m_joystick_btn_off->setVisible(false); + m_joystick_btn_bg->setVisible(true); + m_joystick_btn_center->setVisible(true); + + // If it's a fixed joystick, don't move the joystick "button". + if (!m_fixed_joystick) + m_joystick_btn_bg->setRelativePosition( + touch_pos - half_button_size * 3); + + m_joystick_btn_center->setRelativePosition( + touch_pos - half_button_size); + } + } else { + // If we don't already have a moving point, make this the moving one. + if (!m_has_move_id) { + m_has_move_id = true; + m_move_id = pointer_id; + m_move_has_really_moved = false; + m_move_downtime = porting::getTimeMs(); + m_move_pos = touch_pos; + // DON'T reset m_tap_state here, otherwise many short taps + // will be ignored if you tap very fast. + } + } } else if (event.TouchInput.Event == ETIE_LEFT_UP) { verbosestream << "Up event for pointerid: " << event.TouchInput.ID << std::endl; @@ -861,74 +754,36 @@ void TouchScreenGUI::translateEvent(const SEvent &event) if (distance <= m_touchscreen_threshold) { m_joystick_speed = 0.0f; } else { - m_joystick_speed = distance / button_size; + m_joystick_speed = distance / m_button_size; if (m_joystick_speed > 1.0f) m_joystick_speed = 1.0f; } m_joystick_status_aux1 = distance > (half_button_size * 3); - if (distance > button_size) { + if (distance > m_button_size) { // move joystick "button" - v2s32 new_offset = dir * button_size / distance - half_button_size; + v2s32 new_offset = dir * m_button_size / distance - half_button_size; if (m_fixed_joystick) - m_joystick_btn_center->gui_button->setRelativePosition( + m_joystick_btn_center->setRelativePosition( fixed_joystick_center + new_offset); else - m_joystick_btn_center->gui_button->setRelativePosition( + m_joystick_btn_center->setRelativePosition( free_joystick_center + new_offset); } else { - m_joystick_btn_center->gui_button->setRelativePosition( + m_joystick_btn_center->setRelativePosition( touch_pos - half_button_size); } } } - - if (!m_has_move_id && !m_has_joystick_id) - handleChangedButton(event); } } -void TouchScreenGUI::handleChangedButton(const SEvent &event) -{ - for (unsigned int i = 0; i < after_last_element_id; i++) { - if (m_buttons[i].ids.empty()) - continue; - - for (auto iter = m_buttons[i].ids.begin(); iter != m_buttons[i].ids.end(); ++iter) { - if (event.TouchInput.ID == *iter) { - auto current_button_id = getButtonID(event.TouchInput.X, event.TouchInput.Y); - - if (current_button_id == i) - continue; - - // remove old button - handleButtonEvent((touch_gui_button_id) i, *iter, false); - - if (current_button_id == after_last_element_id) - return; - - handleButtonEvent((touch_gui_button_id) current_button_id, *iter, true); - return; - } - } - } - - int current_button_id = getButtonID(event.TouchInput.X, event.TouchInput.Y); - - if (current_button_id == after_last_element_id) - return; - - button_info *btn = &m_buttons[current_button_id]; - if (std::find(btn->ids.begin(), btn->ids.end(), event.TouchInput.ID) == btn->ids.end()) - handleButtonEvent((touch_gui_button_id) current_button_id, event.TouchInput.ID, true); -} - void TouchScreenGUI::applyJoystickStatus() { if (m_joystick_triggers_aux1) { SEvent translated{}; - translated.EventType = irr::EET_KEY_INPUT_EVENT; + translated.EventType = EET_KEY_INPUT_EVENT; translated.KeyInput.Key = id_to_keycode(aux1_id); translated.KeyInput.PressedDown = false; m_receiver->OnEvent(translated); @@ -940,58 +795,12 @@ void TouchScreenGUI::applyJoystickStatus() } } -TouchScreenGUI::~TouchScreenGUI() -{ - if (!m_initialized) - return; - - for (auto &button : m_buttons) { - if (button.gui_button) { - button.gui_button->drop(); - button.gui_button = nullptr; - } - } - - if (m_joystick_btn_off->gui_button) { - m_joystick_btn_off->gui_button->drop(); - m_joystick_btn_off->gui_button = nullptr; - } - - if (m_joystick_btn_bg->gui_button) { - m_joystick_btn_bg->gui_button->drop(); - m_joystick_btn_bg->gui_button = nullptr; - } - - if (m_joystick_btn_center->gui_button) { - m_joystick_btn_center->gui_button->drop(); - m_joystick_btn_center->gui_button = nullptr; - } -} - void TouchScreenGUI::step(float dtime) { - if (!m_initialized) - return; - // simulate keyboard repeats - for (auto &button : m_buttons) { - if (!button.ids.empty()) { - button.repeat_counter += dtime; - - if (button.repeat_counter < button.repeat_delay) - continue; - - button.repeat_counter = 0.0f; - SEvent translated {}; - translated.EventType = irr::EET_KEY_INPUT_EVENT; - translated.KeyInput.Key = button.keycode; - translated.KeyInput.PressedDown = false; - m_receiver->OnEvent(translated); - - translated.KeyInput.PressedDown = true; - m_receiver->OnEvent(translated); - } - } + buttons_step(m_buttons, dtime, m_device->getVideoDriver(), m_receiver, m_texturesource); + for (AutoHideButtonBar &bar : m_buttonbars) + bar.step(dtime); // joystick applyJoystickStatus(); @@ -1019,9 +828,6 @@ void TouchScreenGUI::step(float dtime) ->getSceneCollisionManager() ->getRayFromScreenCoordinates(pointer_pos); } - - m_settings_bar.step(dtime); - m_rare_controls_bar.step(dtime); } void TouchScreenGUI::resetHotbarRects() @@ -1029,35 +835,31 @@ void TouchScreenGUI::resetHotbarRects() m_hotbar_rects.clear(); } -void TouchScreenGUI::registerHotbarRect(u16 index, const rect &rect) +void TouchScreenGUI::registerHotbarRect(u16 index, const recti &rect) { m_hotbar_rects[index] = rect; } void TouchScreenGUI::setVisible(bool visible) { - if (!m_initialized) - return; - m_visible = visible; for (auto &button : m_buttons) { if (button.gui_button) button.gui_button->setVisible(visible); } - if (m_joystick_btn_off->gui_button) - m_joystick_btn_off->gui_button->setVisible(visible); + if (m_joystick_btn_off) + m_joystick_btn_off->setVisible(visible); // clear all active buttons if (!visible) { while (!m_pointer_pos.empty()) handleReleaseEvent(m_pointer_pos.begin()->first); - - m_settings_bar.hide(); - m_rare_controls_bar.hide(); + for (AutoHideButtonBar &bar : m_buttonbars) + bar.hide(); } else { - m_settings_bar.show(); - m_rare_controls_bar.show(); + for (AutoHideButtonBar &bar : m_buttonbars) + bar.show(); } } @@ -1071,7 +873,7 @@ void TouchScreenGUI::hide() void TouchScreenGUI::show() { - if (m_visible) + if (m_visible) return; setVisible(true); @@ -1180,7 +982,7 @@ void TouchScreenGUI::applyContextControls(const TouchInteractionMode &mode) m_place_pressed = true; } else if (!target_place_pressed && m_place_pressed) { - emitMouseEvent(irr::EMIE_RMOUSE_LEFT_UP); + emitMouseEvent(EMIE_RMOUSE_LEFT_UP); m_place_pressed = false; } } diff --git a/src/gui/touchscreengui.h b/src/gui/touchscreengui.h index 2ee881b93..014cb6fef 100644 --- a/src/gui/touchscreengui.h +++ b/src/gui/touchscreengui.h @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes.h" #include -#include +#include #include #include @@ -38,6 +38,29 @@ using namespace irr; using namespace irr::core; using namespace irr::gui; + +// We cannot use irr_ptr for Irrlicht GUI elements we own. +// Option 1: Pass IGUIElement* returned by IGUIEnvironment::add* into irr_ptr +// constructor. +// -> We steal the reference owned by IGUIEnvironment and drop it later, +// causing the IGUIElement to be deleted while IGUIEnvironment still +// references it. +// Option 2: Pass IGUIElement* returned by IGUIEnvironment::add* into irr_ptr::grab. +// -> We add another reference and drop it later, but since IGUIEnvironment +// still references the IGUIElement, it is never deleted. +// To make IGUIEnvironment drop its reference to the IGUIElement, we have to call +// IGUIElement::remove, so that's what we'll do. +template +std::shared_ptr grab_gui_element(T *element) +{ + static_assert(std::is_base_of_v, + "grab_gui_element only works for IGUIElement"); + return std::shared_ptr(element, [](T *e) { + e->remove(); + }); +} + + enum class TapState { None, @@ -45,15 +68,16 @@ enum class TapState LongTap, }; -typedef enum +enum touch_gui_button_id { jump_id = 0, - crunch_id, + sneak_id, zoom_id, aux1_id, - after_last_element_id, settings_starter_id, rare_controls_starter_id, + + // usually in the "settings bar" fly_id, noclip_id, fast_id, @@ -62,24 +86,30 @@ typedef enum range_id, minimap_id, toggle_chat_id, + + // usually in the "rare controls bar" chat_id, inventory_id, drop_id, exit_id, + + // the joystick joystick_off_id, joystick_bg_id, - joystick_center_id -} touch_gui_button_id; + joystick_center_id, +}; -typedef enum +enum autohide_button_bar_dir { AHBB_Dir_Top_Bottom, AHBB_Dir_Bottom_Top, AHBB_Dir_Left_Right, AHBB_Dir_Right_Left -} autohide_button_bar_dir; +}; -#define BUTTON_REPEAT_DELAY 0.2f +#define BUTTON_REPEAT_DELAY 0.5f +#define BUTTON_REPEAT_INTERVAL 0.333f +#define BUTTONBAR_HIDE_DELAY 3.0f #define SETTINGS_BAR_Y_OFFSET 5 #define RARE_CONTROLS_BAR_Y_OFFSET 5 @@ -88,96 +118,79 @@ typedef enum // If you tap faster than this value, the simulated clicks are of course shorter. #define SIMULATED_CLICK_DURATION_MS 50 -extern const std::string button_image_names[]; -extern const std::string joystick_image_names[]; - struct button_info { float repeat_counter; - float repeat_delay; EKEY_CODE keycode; - std::vector ids; - IGUIButton *gui_button = nullptr; - bool immediate_release; + std::vector pointer_ids; + std::shared_ptr gui_button = nullptr; enum { NOT_TOGGLEABLE, FIRST_TEXTURE, SECOND_TEXTURE } toggleable = NOT_TOGGLEABLE; - std::vector textures; + std::string toggle_textures[2]; + + void emitAction(bool action, video::IVideoDriver *driver, + IEventReceiver *receiver, ISimpleTextureSource *tsrc); }; class AutoHideButtonBar { public: - AutoHideButtonBar(IrrlichtDevice *device, IEventReceiver *receiver); + AutoHideButtonBar(IrrlichtDevice *device, ISimpleTextureSource *tsrc, + touch_gui_button_id starter_id, const std::string &starter_image, + recti starter_rect, autohide_button_bar_dir dir); - void init(ISimpleTextureSource *tsrc, const std::string &starter_img, int button_id, - const v2s32 &UpperLeft, const v2s32 &LowerRight, - autohide_button_bar_dir dir, float timeout); + void addButton(touch_gui_button_id id, const std::string &image); + void addToggleButton(touch_gui_button_id id, + const std::string &image_1, const std::string &image_2); - ~AutoHideButtonBar(); + bool handlePress(size_t pointer_id, IGUIElement *element); + bool handleRelease(size_t pointer_id); - // add button to be shown - void addButton(touch_gui_button_id id, const wchar_t *caption, - const std::string &btn_image); - - // add toggle button to be shown - void addToggleButton(touch_gui_button_id id, const wchar_t *caption, - const std::string &btn_image_1, const std::string &btn_image_2); - - // detect button bar button events - bool isButton(const SEvent &event); - - // step handler void step(float dtime); - // return whether the button bar is active - bool active() { return m_active; } - - // deactivate the button bar + void activate(); void deactivate(); + bool isActive() { return m_active; } - // hide the whole button bar + void show(); void hide(); - // unhide the button bar - void show(); + bool operator==(const AutoHideButtonBar &other) + { return m_starter.get() == other.m_starter.get(); } + bool operator!=(const AutoHideButtonBar &other) + { return m_starter.get() != other.m_starter.get(); } private: + irr::video::IVideoDriver *m_driver = nullptr; + IGUIEnvironment *m_guienv = nullptr; + IEventReceiver *m_receiver = nullptr; ISimpleTextureSource *m_texturesource = nullptr; - irr::video::IVideoDriver *m_driver; - IGUIEnvironment *m_guienv; - IEventReceiver *m_receiver; - button_info m_starter; - std::vector> m_buttons; + std::shared_ptr m_starter; + std::vector m_buttons; v2s32 m_upper_left; v2s32 m_lower_right; - // show button bar bool m_active = false; bool m_visible = true; - - // button bar timeout float m_timeout = 0.0f; - float m_timeout_value = 3.0f; - bool m_initialized = false; autohide_button_bar_dir m_dir = AHBB_Dir_Right_Left; + + void updateVisibility(); }; class TouchScreenGUI { public: - TouchScreenGUI(IrrlichtDevice *device, IEventReceiver *receiver); - ~TouchScreenGUI(); + TouchScreenGUI(IrrlichtDevice *device, ISimpleTextureSource *tsrc); void translateEvent(const SEvent &event); void applyContextControls(const TouchInteractionMode &mode); - void init(ISimpleTextureSource *tsrc); - double getYawChange() { double res = m_camera_yaw_change; @@ -212,22 +225,21 @@ public: void show(); void resetHotbarRects(); - void registerHotbarRect(u16 index, const rect &rect); + void registerHotbarRect(u16 index, const recti &rect); std::optional getHotbarSelection(); private: - bool m_initialized = false; - IrrlichtDevice *m_device; - IGUIEnvironment *m_guienv; - IEventReceiver *m_receiver; - ISimpleTextureSource *m_texturesource; + IrrlichtDevice *m_device = nullptr; + IGUIEnvironment *m_guienv = nullptr; + IEventReceiver *m_receiver = nullptr; + ISimpleTextureSource *m_texturesource = nullptr; v2u32 m_screensize; - s32 button_size; + s32 m_button_size; double m_touchscreen_threshold; u16 m_long_tap_delay; - bool m_visible; // is the whole touch screen gui visible + bool m_visible = true; // is the whole touch screen gui visible - std::unordered_map> m_hotbar_rects; + std::unordered_map m_hotbar_rects; std::optional m_hotbar_selection = std::nullopt; // value in degree @@ -257,39 +269,25 @@ private: bool m_fixed_joystick = false; bool m_joystick_triggers_aux1 = false; bool m_draw_crosshair = false; - std::shared_ptr m_joystick_btn_off = nullptr; - std::shared_ptr m_joystick_btn_bg = nullptr; - std::shared_ptr m_joystick_btn_center = nullptr; + std::shared_ptr m_joystick_btn_off; + std::shared_ptr m_joystick_btn_bg; + std::shared_ptr m_joystick_btn_center; - button_info m_buttons[after_last_element_id]; - - // gui button detection - touch_gui_button_id getButtonID(s32 x, s32 y); - - // gui button by eventID - touch_gui_button_id getButtonID(size_t eventID); - - // check if a button has changed - void handleChangedButton(const SEvent &event); + std::vector m_buttons; // initialize a button - void initButton(touch_gui_button_id id, const rect &button_rect, - const std::wstring &caption, bool immediate_release, - float repeat_delay = BUTTON_REPEAT_DELAY); + void addButton(touch_gui_button_id id, const std::string &image, + const recti &rect); // initialize a joystick button - std::shared_ptr initJoystickButton(touch_gui_button_id id, - const rect &button_rect, int texture_id, - bool visible = true); - - // handle a button event - void handleButtonEvent(touch_gui_button_id bID, size_t eventID, bool action); + IGUIImage *makeJoystickButton(touch_gui_button_id id, + const recti &rect, bool visible); // handle pressing hotbar items bool isHotbarButton(const SEvent &event); // handle release event - void handleReleaseEvent(size_t evt_id); + void handleReleaseEvent(size_t pointer_id); // apply joystick status void applyJoystickStatus(); @@ -299,11 +297,7 @@ private: // map to store the IDs and positions of currently pressed pointers std::unordered_map m_pointer_pos; - // settings bar - AutoHideButtonBar m_settings_bar; - - // rare controls bar - AutoHideButtonBar m_rare_controls_bar; + std::vector m_buttonbars; v2s32 getPointerPos(); void emitMouseEvent(EMOUSE_INPUT_EVENT type);