Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions irr/include/IGUIElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,17 @@ class IGUIElement : virtual public IReferenceCounted, public IEventReceiver
return Children;
}

/// Recursively visit all descendants.
/// @param visitor callable with an IGUIElement*, returns whether to recurse into a child.
template<class F>
void visitDescendants(const F &visitor)
{
for (auto *child : Children) {
if (visitor(child))
child->visitDescendants(visitor);
}
}

//! Finds the first element with the given id.
/** \param id: Id to search for.
\param recursive: Set this to true, if also children of this
Expand Down
12 changes: 12 additions & 0 deletions irr/src/CGUITabControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
#include "CGUITabControl.h"

#include "CGUIButton.h"
#include "IEventReceiver.h"
#include "IGUISkin.h"
#include "IGUIEnvironment.h"
#include "IGUIFont.h"
#include "IVideoDriver.h"
#include "Keycodes.h"
#include "rect.h"
#include "os.h"

Expand Down Expand Up @@ -374,6 +376,16 @@ bool CGUITabControl::OnEvent(const SEvent &event)
break;
}
break;
case EET_GAMEPAD_BUTTON_EVENT:
switch (event.GamepadButtonEvent.Button) {
case GamepadButton::LEFT_SHOULDER:
break;
case GamepadButton::RIGHT_SHOULDER:
break;
default:
break;
}
break;
default:
break;
}
Expand Down
55 changes: 37 additions & 18 deletions src/gui/guiButton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "guiButton.h"


#include "IEventReceiver.h"
#include "Keycodes.h"
#include "client/guiscalingfilter.h"
#include "IGUISkin.h"
#include "IGUIEnvironment.h"
Expand Down Expand Up @@ -115,6 +117,29 @@ bool GUIButton::getSpriteLoop(EGUI_BUTTON_STATE state) const
return ButtonSprites[(u32)state].Loop;
}

void GUIButton::startPress()
{
if (!IsPushButton)
setPressed(true);
else
setPressed(!Pressed);
}

void GUIButton::stopPress()
{
if (!IsPushButton)
setPressed(false);

if (Parent) {
SEvent newEvent;
newEvent.EventType = EET_GUI_EVENT;
newEvent.GUIEvent.Caller = this;
newEvent.GUIEvent.Element = 0;
newEvent.GUIEvent.EventType = EGET_BUTTON_CLICKED;
Parent->OnEvent(newEvent);
}
}

//! called if an event happened.
bool GUIButton::OnEvent(const SEvent& event)
{
Expand All @@ -127,11 +152,7 @@ bool GUIButton::OnEvent(const SEvent& event)
if (event.KeyInput.PressedDown &&
(event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE))
{
if (!IsPushButton)
setPressed(true);
else
setPressed(!Pressed);

startPress();
return true;
}
if (Pressed && !IsPushButton && event.KeyInput.PressedDown && event.KeyInput.Key == KEY_ESCAPE)
Expand All @@ -143,22 +164,20 @@ bool GUIButton::OnEvent(const SEvent& event)
if (!event.KeyInput.PressedDown && Pressed &&
(event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE))
{

if (!IsPushButton)
setPressed(false);

if (Parent)
{
if (Parent) {
ClickShiftState = event.KeyInput.Shift;
ClickControlState = event.KeyInput.Control;

SEvent newEvent;
newEvent.EventType = EET_GUI_EVENT;
newEvent.GUIEvent.Caller = this;
newEvent.GUIEvent.Element = 0;
newEvent.GUIEvent.EventType = EGET_BUTTON_CLICKED;
Parent->OnEvent(newEvent);
}
stopPress();
return true;
}
break;
case EET_GAMEPAD_BUTTON_EVENT:
if (event.GamepadButtonEvent.Button == GamepadButton::SOUTH) {
if (event.GamepadButtonEvent.PressedDown)
startPress();
else
stopPress();
return true;
}
break;
Expand Down
4 changes: 4 additions & 0 deletions src/gui/guiButton.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ class GUIButton : public gui::IGUIButton
const wchar_t *tooltiptext=L"");

protected:

void startPress();
void stopPress();

void drawSprite(gui::EGUI_BUTTON_STATE state, u32 startTime, const core::position2di& center);
gui::EGUI_BUTTON_IMAGE_STATE getImageState(bool pressed) const;

Expand Down
183 changes: 162 additions & 21 deletions src/gui/guiFormSpecMenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
#include <limits>
#include "guiFormSpecMenu.h"
#include "EGUIElementTypes.h"
#include "IEventReceiver.h"
#include "Keycodes.h"
#include "environment.h"
#include "irrlichttypes.h"
#include "itemdef.h"
#include "gamedef.h"
#include "client/keycode.h"
Expand Down Expand Up @@ -36,6 +40,7 @@
#include "util/screenshot.h"
#include "util/string.h" // for parseColorString()
#include "irrlicht_changes/static_text.h"
#include "irrlicht_changes/printing.h"
#include "guiAnimatedImage.h"
#include "guiBackgroundImage.h"
#include "guiBox.h"
Expand Down Expand Up @@ -4334,6 +4339,21 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)

// Handle keyboard and touch input to show/hide focus outline
switch (event.EventType) {
case EET_GAMEPAD_BUTTON_EVENT:
switch (event.GamepadButtonEvent.Button) {
case GamepadButton::DPAD_RIGHT:
case GamepadButton::DPAD_LEFT:
case GamepadButton::DPAD_UP:
case GamepadButton::DPAD_DOWN:
if (event.GamepadButtonEvent.PressedDown) {
m_show_focus = true;
m_last_focused = nullptr;
}
break;
default:
break;
}
break;
case EET_KEY_INPUT_EVENT:
if (event.KeyInput.PressedDown && event.KeyInput.Key == KEY_TAB &&
!event.KeyInput.Control) {
Expand Down Expand Up @@ -4447,35 +4467,156 @@ void GUIFormSpecMenu::trySubmitClose()
}
}

bool GUIFormSpecMenu::switchTab(bool next)
{
// Try to find a tab control among our elements
for (const FieldSpec &s : m_fields) {
if (s.ftype != f_TabHeader)
continue;

IGUIElement *element = getElementFromId(s.fid, true);
if (!element || element->getType() != gui::EGUIET_TAB_CONTROL)
continue;

gui::IGUITabControl *tabs = static_cast<gui::IGUITabControl *>(element);
s32 num_tabs = tabs->getTabCount();
if (num_tabs <= 1)
continue;

s32 active = tabs->getActiveTab();
active = (active + (next ? 1 : -1) + num_tabs) % num_tabs;
tabs->setActiveTab(active);
return true; // handled
}
return false;
}

void GUIFormSpecMenu::switchFocus(s32 v2s32::* axis, s32 v2s32::* ortho_axis, s8 sign)
{
const auto *focus = Environment->getFocus();
v2s32 focus_minp, focus_maxp;
if (focus) {
const auto focus_rect = focus->getAbsoluteClippingRect();
focus_minp = focus_rect.UpperLeftCorner;
focus_maxp = focus_rect.LowerRightCorner;
} else {
focus_minp.*axis = sign * (1 << 30);
focus_maxp.*axis = sign * (1 << 30);
focus_minp.*ortho_axis = S32_MIN;
focus_maxp.*ortho_axis = S32_MAX;
}

struct Candidate
{
IGUIElement *element = nullptr;
s32 axis_offset = 0;
s32 ortho_offset = 0;

bool proper() const
{
return ortho_offset == 0 && axis_offset >= 0;
}

bool beats(Candidate other) const
{
if ((element == nullptr) != (other.element == nullptr))
return element != nullptr;
// One candidate is in the same row, the other is not
if (proper() != other.proper())
return proper();
if (proper() && other.proper())
return axis_offset < other.axis_offset;
if ((ortho_offset > 0) != (other.ortho_offset > 0))
return ortho_offset > 0;
if (ortho_offset == other.ortho_offset)
return axis_offset < other.axis_offset;
return ortho_offset < other.ortho_offset; // wrap around
}
};

Candidate best_candidate;

visitDescendants([&](IGUIElement *elem) -> bool {
if (elem == focus)
return false;
if (elem->isTabStop() && elem->isEnabled() && elem->isVisible()) {
const auto rect = elem->getAbsoluteClippingRect();
const auto minp = rect.UpperLeftCorner;
const auto maxp = rect.LowerRightCorner;

Candidate candidate;
candidate.element = elem;

if ((candidate.ortho_offset = maxp.*ortho_axis - focus_minp.*ortho_axis) < 0) {
} else if ((candidate.ortho_offset = minp.*ortho_axis - focus_maxp.*ortho_axis) > 0) {
} else {
candidate.ortho_offset = 0;
}
candidate.ortho_offset *= sign;

if (sign > 0) {
candidate.axis_offset = minp.*axis - focus_maxp.*axis;
} else {
candidate.axis_offset = focus_minp.*axis - maxp.*axis;
}
if (candidate.beats(best_candidate)) {
best_candidate = candidate;
return false;
}
}
return true;
});

if (best_candidate.element)
Environment->setFocus(best_candidate.element);
}

bool GUIFormSpecMenu::OnEvent(const SEvent& event)
{
if (event.EventType == EET_GAMEPAD_BUTTON_EVENT) {
const auto &gp_btn_event = event.GamepadButtonEvent;
if (gp_btn_event.PressedDown) {
switch (gp_btn_event.Button) {
// Tab control navigation
case GamepadButton::LEFT_SHOULDER:
if (switchTab(false))
return true;
break;
case GamepadButton::RIGHT_SHOULDER:
if (switchTab(true))
return true;
break;
case GamepadButton::DPAD_RIGHT:
switchFocus(&v2s32::X, &v2s32::Y, 1);
return true;
case GamepadButton::DPAD_LEFT:
switchFocus(&v2s32::X, &v2s32::Y, -1);
return true;
case GamepadButton::DPAD_DOWN:
switchFocus(&v2s32::Y, &v2s32::X, 1);
return true;
case GamepadButton::DPAD_UP:
switchFocus(&v2s32::Y, &v2s32::X, -1);
return true;
case GamepadButton::EAST:
tryClose();
return true;
default:
break;
}
}
}

if (event.EventType==EET_KEY_INPUT_EVENT) {
KeyPress kp(event.KeyInput);
// Ctrl (+ Shift) + Tab: Select the (previous or) next tab of a TabControl instance.
bool shift = event.KeyInput.Shift;
bool ctrl = event.KeyInput.Control;
if (event.KeyInput.PressedDown && (event.KeyInput.Key == KEY_TAB && ctrl)) {
// Try to find a tab control among our elements
for (const FieldSpec &s : m_fields) {
if (s.ftype != f_TabHeader)
continue;

IGUIElement *element = getElementFromId(s.fid, true);
if (!element || element->getType() != gui::EGUIET_TAB_CONTROL)
continue;

gui::IGUITabControl *tabs = static_cast<gui::IGUITabControl *>(element);
s32 num_tabs = tabs->getTabCount();
if (num_tabs <= 1)
continue;

s32 active = tabs->getActiveTab();
// Shift: Previous tab, No shift: Next tab
active = (active + (shift ? -1 : 1) + num_tabs) % num_tabs;
tabs->setActiveTab(active);
return true; // handled
}
if (event.KeyInput.PressedDown && (event.KeyInput.Key == KEY_TAB && ctrl) &&
switchTab(!shift)) {
return true; // switched tab
}

if (event.KeyInput.PressedDown && (
(kp == EscapeKey) ||
((m_client != NULL) && (keySettingHasMatch("keymap_inventory", kp))))) {
Expand Down
8 changes: 8 additions & 0 deletions src/gui/guiFormSpecMenu.h
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,14 @@ class GUIFormSpecMenu : public GUIModalMenu
static double getImgsize(v2u32 avail_screensize, double screen_dpi, double gui_scaling);

protected:

/// Try to switch to a neighboring tab (right neighbor if next = true, left neighbor otherwise).
/// @return if the operation succeeded.
bool switchTab(bool next);

/// Try to switch focus in the given direction
void switchFocus(s32 v2s32::* axis, s32 v2s32::* ortho_axis, s8 sign);

bool remapClickOutside(const SEvent &event) override;

v2s32 getBasePos() const
Expand Down
Loading