UI input: fix dialog click/type failures (ImGui event queue + SDL text input)
This commit is contained in:
parent
ef55119407
commit
700e7108c3
6 changed files with 221 additions and 62 deletions
|
|
@ -77,8 +77,12 @@ class ImGuiDrawer : public WindowInputListener, public UIDrawer {
|
||||||
|
|
||||||
void RenderDrawLists(ImDrawData* data, UIDrawContext& ui_draw_context);
|
void RenderDrawLists(ImDrawData* data, UIDrawContext& ui_draw_context);
|
||||||
|
|
||||||
|
void AddDialogImpl(ImGuiDialog* dialog);
|
||||||
|
void RemoveDialogImpl(ImGuiDialog* dialog);
|
||||||
|
|
||||||
void ClearInput();
|
void ClearInput();
|
||||||
void OnKey(KeyEvent& e, bool is_down);
|
void OnKey(KeyEvent& e, bool is_down);
|
||||||
|
static int MouseEventButtonToImGui(const MouseEvent& e);
|
||||||
void UpdateMousePosition(float x, float y);
|
void UpdateMousePosition(float x, float y);
|
||||||
void SwitchToPhysicalMouseAndUpdateMousePosition(const MouseEvent& e);
|
void SwitchToPhysicalMouseAndUpdateMousePosition(const MouseEvent& e);
|
||||||
|
|
||||||
|
|
@ -108,6 +112,15 @@ class ImGuiDrawer : public WindowInputListener, public UIDrawer {
|
||||||
// detaching the presenter.
|
// detaching the presenter.
|
||||||
std::unique_ptr<ImmediateTexture> font_texture_;
|
std::unique_ptr<ImmediateTexture> font_texture_;
|
||||||
|
|
||||||
|
// Bit mask of ImGui mouse buttons the drawer has seen pressed, used for
|
||||||
|
// window mouse capture bookkeeping (io.MouseDown can't be used - input is
|
||||||
|
// queued via the ImGui event API and only reflected at NewFrame).
|
||||||
|
uint32_t mouse_buttons_down_ = 0;
|
||||||
|
|
||||||
|
// Whether the platform window has been asked to activate text input
|
||||||
|
// (character event delivery / IME) because a text widget is active.
|
||||||
|
bool text_input_active_ = false;
|
||||||
|
|
||||||
// If there's an active pointer, the ImGui mouse is controlled by this touch.
|
// If there's an active pointer, the ImGui mouse is controlled by this touch.
|
||||||
// If it's TouchEvent::kPointerIDNone, the ImGui mouse is controlled by the
|
// If it's TouchEvent::kPointerIDNone, the ImGui mouse is controlled by the
|
||||||
// mouse.
|
// mouse.
|
||||||
|
|
|
||||||
|
|
@ -311,6 +311,14 @@ class Window {
|
||||||
// the cursor instantly.
|
// the cursor instantly.
|
||||||
void SetCursorVisibility(CursorVisibility new_cursor_visibility);
|
void SetCursorVisibility(CursorVisibility new_cursor_visibility);
|
||||||
|
|
||||||
|
// Enables or disables platform text input (character event delivery / IME)
|
||||||
|
// while a text widget is active. Characters are delivered to input listeners
|
||||||
|
// via OnKeyChar. On some platforms character events are always delivered
|
||||||
|
// (Win32 WM_CHAR, GTK) and this is a no-op; on SDL, character events are
|
||||||
|
// only delivered while text input is started, so this must be kept in sync
|
||||||
|
// by whoever hosts text widgets (done by ImGuiDrawer).
|
||||||
|
virtual void SetTextInputActive(bool /*active*/) {}
|
||||||
|
|
||||||
bool HasFocus() const { return HasActualState() ? has_focus_ : false; }
|
bool HasFocus() const { return HasActualState() ? has_focus_ : false; }
|
||||||
// May be applied in a delayed way or dropped at all, HasFocus will not
|
// May be applied in a delayed way or dropped at all, HasFocus will not
|
||||||
// necessarily be true immediately.
|
// necessarily be true immediately.
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ class SDLWindow final : public Window {
|
||||||
|
|
||||||
static void HandleSDLEvent(const SDL_Event& event);
|
static void HandleSDLEvent(const SDL_Event& event);
|
||||||
|
|
||||||
|
void SetTextInputActive(bool active) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint32_t GetLatestDpiImpl() const override;
|
uint32_t GetLatestDpiImpl() const override;
|
||||||
bool OpenImpl() override;
|
bool OpenImpl() override;
|
||||||
|
|
@ -51,6 +53,8 @@ class SDLWindow final : public Window {
|
||||||
WindowDestructionReceiver& destruction_receiver);
|
WindowDestructionReceiver& destruction_receiver);
|
||||||
void HandleMouseWheel(const SDL_MouseWheelEvent& event,
|
void HandleMouseWheel(const SDL_MouseWheelEvent& event,
|
||||||
WindowDestructionReceiver& destruction_receiver);
|
WindowDestructionReceiver& destruction_receiver);
|
||||||
|
void HandleTextInput(const SDL_TextInputEvent& event,
|
||||||
|
WindowDestructionReceiver& destruction_receiver);
|
||||||
void RevealAutoHiddenCursor();
|
void RevealAutoHiddenCursor();
|
||||||
void SetCursorAutoHideTimer();
|
void SetCursorAutoHideTimer();
|
||||||
void RemoveCursorAutoHideTimer();
|
void RemoveCursorAutoHideTimer();
|
||||||
|
|
|
||||||
|
|
@ -377,7 +377,9 @@ class KeyboardInputDialog : public XamDialog {
|
||||||
}
|
}
|
||||||
if (ImGui::InputText("##body", text_buffer_.data(), text_buffer_.size(),
|
if (ImGui::InputText("##body", text_buffer_.data(), text_buffer_.size(),
|
||||||
ImGuiInputTextFlags_EnterReturnsTrue)) {
|
ImGuiInputTextFlags_EnterReturnsTrue)) {
|
||||||
text_ = std::string(text_buffer_.data(), text_buffer_.size());
|
// Note: constructing from the pointer only - the buffer is
|
||||||
|
// NUL-padded to max_length, and (data, size) would keep the padding.
|
||||||
|
text_ = std::string(text_buffer_.data());
|
||||||
cancelled_ = false;
|
cancelled_ = false;
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
Close();
|
Close();
|
||||||
|
|
@ -390,7 +392,7 @@ class KeyboardInputDialog : public XamDialog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ImGui::Button("OK")) {
|
if (ImGui::Button("OK")) {
|
||||||
text_ = std::string(text_buffer_.data(), text_buffer_.size());
|
text_ = std::string(text_buffer_.data());
|
||||||
cancelled_ = false;
|
cancelled_ = false;
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
Close();
|
Close();
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
#include <rex/ui/imgui_drawer.h>
|
#include <rex/ui/imgui_drawer.h>
|
||||||
#include <rex/ui/ui_event.h>
|
#include <rex/ui/ui_event.h>
|
||||||
#include <rex/ui/window.h>
|
#include <rex/ui/window.h>
|
||||||
|
#include <rex/ui/windowed_app_context.h>
|
||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
|
||||||
|
|
@ -44,11 +45,11 @@ ImGuiDrawer::~ImGuiDrawer() {
|
||||||
SetPresenter(nullptr);
|
SetPresenter(nullptr);
|
||||||
if (!dialogs_.empty()) {
|
if (!dialogs_.empty()) {
|
||||||
window_->RemoveInputListener(this);
|
window_->RemoveInputListener(this);
|
||||||
if (internal_state_) {
|
if (touch_pointer_id_ == TouchEvent::kPointerIDNone && mouse_buttons_down_) {
|
||||||
ImGui::SetCurrentContext(internal_state_);
|
window_->ReleaseMouse();
|
||||||
if (touch_pointer_id_ == TouchEvent::kPointerIDNone && ImGui::IsAnyMouseDown()) {
|
}
|
||||||
window_->ReleaseMouse();
|
if (text_input_active_) {
|
||||||
}
|
window_->SetTextInputActive(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (internal_state_) {
|
if (internal_state_) {
|
||||||
|
|
@ -59,6 +60,20 @@ ImGuiDrawer::~ImGuiDrawer() {
|
||||||
|
|
||||||
void ImGuiDrawer::AddDialog(ImGuiDialog* dialog) {
|
void ImGuiDrawer::AddDialog(ImGuiDialog* dialog) {
|
||||||
assert_not_null(dialog);
|
assert_not_null(dialog);
|
||||||
|
// Dialogs may be constructed on kernel/guest threads (XamShow*UI dispatch);
|
||||||
|
// the dialog list, the window input listener list and the presenter UI
|
||||||
|
// drawer list are all UI-thread state, so marshal to the UI thread. If the
|
||||||
|
// UI loop is already gone (shutdown), skip - the dialog will never be drawn
|
||||||
|
// and the dispatch path deletes it.
|
||||||
|
WindowedAppContext& app_context = window_->app_context();
|
||||||
|
if (!app_context.IsInUIThread()) {
|
||||||
|
app_context.CallInUIThreadSynchronous([this, dialog]() { AddDialogImpl(dialog); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AddDialogImpl(dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiDrawer::AddDialogImpl(ImGuiDialog* dialog) {
|
||||||
// Check if already added.
|
// Check if already added.
|
||||||
if (std::find(dialogs_.cbegin(), dialogs_.cend(), dialog) != dialogs_.cend()) {
|
if (std::find(dialogs_.cbegin(), dialogs_.cend(), dialog) != dialogs_.cend()) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -78,6 +93,15 @@ void ImGuiDrawer::AddDialog(ImGuiDialog* dialog) {
|
||||||
|
|
||||||
void ImGuiDrawer::RemoveDialog(ImGuiDialog* dialog) {
|
void ImGuiDrawer::RemoveDialog(ImGuiDialog* dialog) {
|
||||||
assert_not_null(dialog);
|
assert_not_null(dialog);
|
||||||
|
WindowedAppContext& app_context = window_->app_context();
|
||||||
|
if (!app_context.IsInUIThread()) {
|
||||||
|
app_context.CallInUIThreadSynchronous([this, dialog]() { RemoveDialogImpl(dialog); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RemoveDialogImpl(dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiDrawer::RemoveDialogImpl(ImGuiDialog* dialog) {
|
||||||
auto it = std::find(dialogs_.cbegin(), dialogs_.cend(), dialog);
|
auto it = std::find(dialogs_.cbegin(), dialogs_.cend(), dialog);
|
||||||
if (it == dialogs_.cend()) {
|
if (it == dialogs_.cend()) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -265,9 +289,19 @@ std::optional<ImGuiKey> ImGuiDrawer::VirtualKeyToImGuiKey(VirtualKey vkey) {
|
||||||
{ui::VirtualKey::kF11, ImGuiKey_F11},
|
{ui::VirtualKey::kF11, ImGuiKey_F11},
|
||||||
{ui::VirtualKey::kF12, ImGuiKey_F12},
|
{ui::VirtualKey::kF12, ImGuiKey_F12},
|
||||||
// Modifiers
|
// Modifiers
|
||||||
|
// Win32 sends the generic VKs (WM_KEYDOWN gives VK_SHIFT etc.), SDL and
|
||||||
|
// GTK send the sided ones - both must be mapped.
|
||||||
{ui::VirtualKey::kShift, ImGuiKey_LeftShift},
|
{ui::VirtualKey::kShift, ImGuiKey_LeftShift},
|
||||||
{ui::VirtualKey::kControl, ImGuiKey_LeftCtrl},
|
{ui::VirtualKey::kControl, ImGuiKey_LeftCtrl},
|
||||||
{ui::VirtualKey::kMenu, ImGuiKey_LeftAlt},
|
{ui::VirtualKey::kMenu, ImGuiKey_LeftAlt},
|
||||||
|
{ui::VirtualKey::kLShift, ImGuiKey_LeftShift},
|
||||||
|
{ui::VirtualKey::kRShift, ImGuiKey_RightShift},
|
||||||
|
{ui::VirtualKey::kLControl, ImGuiKey_LeftCtrl},
|
||||||
|
{ui::VirtualKey::kRControl, ImGuiKey_RightCtrl},
|
||||||
|
{ui::VirtualKey::kLMenu, ImGuiKey_LeftAlt},
|
||||||
|
{ui::VirtualKey::kRMenu, ImGuiKey_RightAlt},
|
||||||
|
{ui::VirtualKey::kLWin, ImGuiKey_LeftSuper},
|
||||||
|
{ui::VirtualKey::kRWin, ImGuiKey_RightSuper},
|
||||||
{ui::VirtualKey::kCapital, ImGuiKey_CapsLock},
|
{ui::VirtualKey::kCapital, ImGuiKey_CapsLock},
|
||||||
{ui::VirtualKey::kNumLock, ImGuiKey_NumLock},
|
{ui::VirtualKey::kNumLock, ImGuiKey_NumLock},
|
||||||
{ui::VirtualKey::kScroll, ImGuiKey_ScrollLock},
|
{ui::VirtualKey::kScroll, ImGuiKey_ScrollLock},
|
||||||
|
|
@ -402,7 +436,15 @@ void ImGuiDrawer::Draw(UIDrawContext& ui_draw_context) {
|
||||||
|
|
||||||
if (reset_mouse_position_after_next_frame_) {
|
if (reset_mouse_position_after_next_frame_) {
|
||||||
reset_mouse_position_after_next_frame_ = false;
|
reset_mouse_position_after_next_frame_ = false;
|
||||||
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
|
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the platform text input state in sync with whether a text widget is
|
||||||
|
// active (SDL only delivers character events while text input is started).
|
||||||
|
bool want_text_input = io.WantTextInput && !dialogs_.empty();
|
||||||
|
if (want_text_input != text_input_active_) {
|
||||||
|
text_input_active_ = want_text_input;
|
||||||
|
window_->SetTextInputActive(want_text_input);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detaching is deferred if the last dialog is removed during drawing, perform
|
// Detaching is deferred if the last dialog is removed during drawing, perform
|
||||||
|
|
@ -476,30 +518,40 @@ void ImGuiDrawer::OnKeyChar(KeyEvent& e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ImGuiDrawer::MouseEventButtonToImGui(const MouseEvent& e) {
|
||||||
|
switch (e.button()) {
|
||||||
|
case rex::ui::MouseEvent::Button::kLeft:
|
||||||
|
return 0;
|
||||||
|
case rex::ui::MouseEvent::Button::kRight:
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
// Ignored.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ImGuiDrawer::OnMouseDown(MouseEvent& e) {
|
void ImGuiDrawer::OnMouseDown(MouseEvent& e) {
|
||||||
SwitchToPhysicalMouseAndUpdateMousePosition(e);
|
SwitchToPhysicalMouseAndUpdateMousePosition(e);
|
||||||
auto& io = GetIO();
|
auto& io = GetIO();
|
||||||
int button = -1;
|
int button = MouseEventButtonToImGui(e);
|
||||||
switch (e.button()) {
|
if (button >= 0) {
|
||||||
case rex::ui::MouseEvent::Button::kLeft: {
|
if (!(mouse_buttons_down_ & (UINT32_C(1) << button))) {
|
||||||
button = 0;
|
if (!mouse_buttons_down_) {
|
||||||
break;
|
|
||||||
}
|
|
||||||
case rex::ui::MouseEvent::Button::kRight: {
|
|
||||||
button = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
// Ignored.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (button >= 0 && button < std::size(io.MouseDown)) {
|
|
||||||
if (!io.MouseDown[button]) {
|
|
||||||
if (!ImGui::IsAnyMouseDown()) {
|
|
||||||
window_->CaptureMouse();
|
window_->CaptureMouse();
|
||||||
}
|
}
|
||||||
io.MouseDown[button] = true;
|
mouse_buttons_down_ |= UINT32_C(1) << button;
|
||||||
|
}
|
||||||
|
// Queue rather than write io.MouseDown directly: the event queue keeps the
|
||||||
|
// position-then-button ordering and spreads a same-frame press+release
|
||||||
|
// over multiple NewFrames, so clicks register at the position they
|
||||||
|
// actually happened at and fast clicks aren't lost when the UI frame rate
|
||||||
|
// lags behind input.
|
||||||
|
io.AddMouseButtonEvent(button, true);
|
||||||
|
if (io.WantCaptureMouse) {
|
||||||
|
// Keep presses over the UI out of lower-Z listeners (game input,
|
||||||
|
// keybinds). Releases are deliberately never eaten so lower listeners
|
||||||
|
// can't be left with a stuck button.
|
||||||
|
e.set_handled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -511,35 +563,25 @@ void ImGuiDrawer::OnMouseMove(MouseEvent& e) {
|
||||||
void ImGuiDrawer::OnMouseUp(MouseEvent& e) {
|
void ImGuiDrawer::OnMouseUp(MouseEvent& e) {
|
||||||
SwitchToPhysicalMouseAndUpdateMousePosition(e);
|
SwitchToPhysicalMouseAndUpdateMousePosition(e);
|
||||||
auto& io = GetIO();
|
auto& io = GetIO();
|
||||||
int button = -1;
|
int button = MouseEventButtonToImGui(e);
|
||||||
switch (e.button()) {
|
if (button >= 0) {
|
||||||
case rex::ui::MouseEvent::Button::kLeft: {
|
if (mouse_buttons_down_ & (UINT32_C(1) << button)) {
|
||||||
button = 0;
|
mouse_buttons_down_ &= ~(UINT32_C(1) << button);
|
||||||
break;
|
if (!mouse_buttons_down_) {
|
||||||
}
|
|
||||||
case rex::ui::MouseEvent::Button::kRight: {
|
|
||||||
button = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
// Ignored.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (button >= 0 && button < std::size(io.MouseDown)) {
|
|
||||||
if (io.MouseDown[button]) {
|
|
||||||
io.MouseDown[button] = false;
|
|
||||||
if (!ImGui::IsAnyMouseDown()) {
|
|
||||||
window_->ReleaseMouse();
|
window_->ReleaseMouse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
io.AddMouseButtonEvent(button, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiDrawer::OnMouseWheel(MouseEvent& e) {
|
void ImGuiDrawer::OnMouseWheel(MouseEvent& e) {
|
||||||
SwitchToPhysicalMouseAndUpdateMousePosition(e);
|
SwitchToPhysicalMouseAndUpdateMousePosition(e);
|
||||||
auto& io = GetIO();
|
auto& io = GetIO();
|
||||||
io.MouseWheel += float(e.scroll_y()) / float(MouseEvent::kScrollPerDetent);
|
io.AddMouseWheelEvent(0.0f, float(e.scroll_y()) / float(MouseEvent::kScrollPerDetent));
|
||||||
|
if (io.WantCaptureMouse) {
|
||||||
|
e.set_handled(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiDrawer::OnTouchEvent(TouchEvent& e) {
|
void ImGuiDrawer::OnTouchEvent(TouchEvent& e) {
|
||||||
|
|
@ -550,8 +592,13 @@ void ImGuiDrawer::OnTouchEvent(TouchEvent& e) {
|
||||||
// The latest pointer needs to be controlling the ImGui mouse.
|
// The latest pointer needs to be controlling the ImGui mouse.
|
||||||
if (touch_pointer_id_ == TouchEvent::kPointerIDNone) {
|
if (touch_pointer_id_ == TouchEvent::kPointerIDNone) {
|
||||||
// Switching from the mouse to touch input.
|
// Switching from the mouse to touch input.
|
||||||
if (ImGui::IsAnyMouseDown()) {
|
if (mouse_buttons_down_) {
|
||||||
std::memset(io.MouseDown, 0, sizeof(io.MouseDown));
|
for (int button = 0; button < 32; ++button) {
|
||||||
|
if (mouse_buttons_down_ & (UINT32_C(1) << button)) {
|
||||||
|
io.AddMouseButtonEvent(button, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mouse_buttons_down_ = 0;
|
||||||
window_->ReleaseMouse();
|
window_->ReleaseMouse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -563,27 +610,32 @@ void ImGuiDrawer::OnTouchEvent(TouchEvent& e) {
|
||||||
}
|
}
|
||||||
UpdateMousePosition(e.x(), e.y());
|
UpdateMousePosition(e.x(), e.y());
|
||||||
if (action == TouchEvent::Action::kUp || action == TouchEvent::Action::kCancel) {
|
if (action == TouchEvent::Action::kUp || action == TouchEvent::Action::kCancel) {
|
||||||
io.MouseDown[0] = false;
|
io.AddMouseButtonEvent(0, false);
|
||||||
touch_pointer_id_ = TouchEvent::kPointerIDNone;
|
touch_pointer_id_ = TouchEvent::kPointerIDNone;
|
||||||
// Make sure that after a touch, the ImGui mouse isn't hovering over
|
// Make sure that after a touch, the ImGui mouse isn't hovering over
|
||||||
// anything.
|
// anything.
|
||||||
reset_mouse_position_after_next_frame_ = true;
|
reset_mouse_position_after_next_frame_ = true;
|
||||||
} else {
|
} else {
|
||||||
io.MouseDown[0] = true;
|
io.AddMouseButtonEvent(0, true);
|
||||||
reset_mouse_position_after_next_frame_ = false;
|
reset_mouse_position_after_next_frame_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiDrawer::ClearInput() {
|
void ImGuiDrawer::ClearInput() {
|
||||||
auto& io = GetIO();
|
auto& io = GetIO();
|
||||||
if (touch_pointer_id_ == TouchEvent::kPointerIDNone && ImGui::IsAnyMouseDown()) {
|
if (touch_pointer_id_ == TouchEvent::kPointerIDNone && mouse_buttons_down_) {
|
||||||
window_->ReleaseMouse();
|
window_->ReleaseMouse();
|
||||||
}
|
}
|
||||||
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
|
mouse_buttons_down_ = 0;
|
||||||
std::memset(io.MouseDown, 0, sizeof(io.MouseDown));
|
io.ClearEventsQueue();
|
||||||
io.ClearInputKeys();
|
io.ClearInputKeys();
|
||||||
|
io.ClearInputMouse();
|
||||||
touch_pointer_id_ = TouchEvent::kPointerIDNone;
|
touch_pointer_id_ = TouchEvent::kPointerIDNone;
|
||||||
reset_mouse_position_after_next_frame_ = false;
|
reset_mouse_position_after_next_frame_ = false;
|
||||||
|
if (text_input_active_) {
|
||||||
|
text_input_active_ = false;
|
||||||
|
window_->SetTextInputActive(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiDrawer::OnKey(KeyEvent& e, bool is_down) {
|
void ImGuiDrawer::OnKey(KeyEvent& e, bool is_down) {
|
||||||
|
|
@ -594,35 +646,47 @@ void ImGuiDrawer::OnKey(KeyEvent& e, bool is_down) {
|
||||||
}
|
}
|
||||||
switch (virtual_key) {
|
switch (virtual_key) {
|
||||||
case VirtualKey::kShift:
|
case VirtualKey::kShift:
|
||||||
io.KeyShift = is_down;
|
case VirtualKey::kLShift:
|
||||||
|
case VirtualKey::kRShift:
|
||||||
|
io.AddKeyEvent(ImGuiMod_Shift, is_down);
|
||||||
break;
|
break;
|
||||||
case VirtualKey::kControl:
|
case VirtualKey::kControl:
|
||||||
io.KeyCtrl = is_down;
|
case VirtualKey::kLControl:
|
||||||
|
case VirtualKey::kRControl:
|
||||||
|
io.AddKeyEvent(ImGuiMod_Ctrl, is_down);
|
||||||
break;
|
break;
|
||||||
case VirtualKey::kMenu:
|
case VirtualKey::kMenu:
|
||||||
// FIXME(Triang3l): Doesn't work in xenia-ui-window-demo.
|
case VirtualKey::kLMenu:
|
||||||
io.KeyAlt = is_down;
|
case VirtualKey::kRMenu:
|
||||||
|
io.AddKeyEvent(ImGuiMod_Alt, is_down);
|
||||||
break;
|
break;
|
||||||
case VirtualKey::kLWin:
|
case VirtualKey::kLWin:
|
||||||
io.KeySuper = is_down;
|
case VirtualKey::kRWin:
|
||||||
|
io.AddKeyEvent(ImGuiMod_Super, is_down);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// While a text field is active, keep key presses away from app keybinds and
|
||||||
|
// the game (typing a name must not toggle overlays or drive MnK input).
|
||||||
|
// Releases always propagate so lower listeners can't get stuck keys.
|
||||||
|
if (is_down && io.WantTextInput) {
|
||||||
|
e.set_handled(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiDrawer::UpdateMousePosition(float x, float y) {
|
void ImGuiDrawer::UpdateMousePosition(float x, float y) {
|
||||||
auto& io = GetIO();
|
auto& io = GetIO();
|
||||||
float physical_to_logical = float(window_->GetMediumDpi()) / float(window_->GetDpi());
|
float physical_to_logical = float(window_->GetMediumDpi()) / float(window_->GetDpi());
|
||||||
io.MousePos.x = x * physical_to_logical;
|
io.AddMousePosEvent(x * physical_to_logical, y * physical_to_logical);
|
||||||
io.MousePos.y = y * physical_to_logical;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiDrawer::SwitchToPhysicalMouseAndUpdateMousePosition(const MouseEvent& e) {
|
void ImGuiDrawer::SwitchToPhysicalMouseAndUpdateMousePosition(const MouseEvent& e) {
|
||||||
if (touch_pointer_id_ != TouchEvent::kPointerIDNone) {
|
if (touch_pointer_id_ != TouchEvent::kPointerIDNone) {
|
||||||
touch_pointer_id_ = TouchEvent::kPointerIDNone;
|
touch_pointer_id_ = TouchEvent::kPointerIDNone;
|
||||||
auto& io = GetIO();
|
auto& io = GetIO();
|
||||||
std::memset(io.MouseDown, 0, sizeof(io.MouseDown));
|
// Release the ImGui touch-driven button.
|
||||||
|
io.AddMouseButtonEvent(0, false);
|
||||||
// Nothing needs to be done regarding CaptureMouse and ReleaseMouse - all
|
// Nothing needs to be done regarding CaptureMouse and ReleaseMouse - all
|
||||||
// buttons as well as mouse capture have been released when switching to
|
// buttons as well as mouse capture have been released when switching to
|
||||||
// touch input, the mouse is never captured during touch input, and now
|
// touch input, the mouse is never captured during touch input, and now
|
||||||
|
|
|
||||||
|
|
@ -376,6 +376,9 @@ void SDLWindow::HandleSDLEvent(const SDL_Event& event) {
|
||||||
case SDL_EVENT_KEY_UP:
|
case SDL_EVENT_KEY_UP:
|
||||||
window_id = event.key.windowID;
|
window_id = event.key.windowID;
|
||||||
break;
|
break;
|
||||||
|
case SDL_EVENT_TEXT_INPUT:
|
||||||
|
window_id = event.text.windowID;
|
||||||
|
break;
|
||||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||||
window_id = event.button.windowID;
|
window_id = event.button.windowID;
|
||||||
|
|
@ -589,6 +592,9 @@ void SDLWindow::HandleEvent(const SDL_Event& event) {
|
||||||
case SDL_EVENT_KEY_UP:
|
case SDL_EVENT_KEY_UP:
|
||||||
HandleKey(event.key, false, destruction_receiver);
|
HandleKey(event.key, false, destruction_receiver);
|
||||||
break;
|
break;
|
||||||
|
case SDL_EVENT_TEXT_INPUT:
|
||||||
|
HandleTextInput(event.text, destruction_receiver);
|
||||||
|
break;
|
||||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||||
HandleMouseButton(event.button, true, destruction_receiver);
|
HandleMouseButton(event.button, true, destruction_receiver);
|
||||||
break;
|
break;
|
||||||
|
|
@ -662,6 +668,68 @@ void SDLWindow::HandleMouseMotion(const SDL_MouseMotionEvent& event,
|
||||||
OnMouseMove(e, destruction_receiver);
|
OnMouseMove(e, destruction_receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SDLWindow::HandleTextInput(const SDL_TextInputEvent& event,
|
||||||
|
WindowDestructionReceiver& destruction_receiver) {
|
||||||
|
if (!event.text) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SDL_Keymod mod = SDL_GetModState();
|
||||||
|
const uint8_t* s = reinterpret_cast<const uint8_t*>(event.text);
|
||||||
|
while (*s) {
|
||||||
|
// Decode one UTF-8 codepoint.
|
||||||
|
uint32_t codepoint = 0;
|
||||||
|
uint32_t continuation_count = 0;
|
||||||
|
uint8_t lead = *s++;
|
||||||
|
if (lead < 0x80) {
|
||||||
|
codepoint = lead;
|
||||||
|
} else if ((lead & 0xE0) == 0xC0) {
|
||||||
|
codepoint = lead & 0x1F;
|
||||||
|
continuation_count = 1;
|
||||||
|
} else if ((lead & 0xF0) == 0xE0) {
|
||||||
|
codepoint = lead & 0x0F;
|
||||||
|
continuation_count = 2;
|
||||||
|
} else if ((lead & 0xF8) == 0xF0) {
|
||||||
|
codepoint = lead & 0x07;
|
||||||
|
continuation_count = 3;
|
||||||
|
} else {
|
||||||
|
// Stray continuation or invalid lead byte - skip.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bool valid = true;
|
||||||
|
for (uint32_t i = 0; i < continuation_count; ++i) {
|
||||||
|
uint8_t continuation = *s;
|
||||||
|
if ((continuation & 0xC0) != 0x80) {
|
||||||
|
valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
codepoint = (codepoint << 6) | (continuation & 0x3F);
|
||||||
|
++s;
|
||||||
|
}
|
||||||
|
if (!valid || !codepoint) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Mirrors the Win32 WM_CHAR path - the character is carried in the
|
||||||
|
// KeyEvent's virtual key field.
|
||||||
|
KeyEvent e(this, VirtualKey(codepoint), 1, false, (mod & SDL_KMOD_SHIFT) != 0,
|
||||||
|
(mod & SDL_KMOD_CTRL) != 0, (mod & SDL_KMOD_ALT) != 0, (mod & SDL_KMOD_GUI) != 0);
|
||||||
|
OnKeyChar(e, destruction_receiver);
|
||||||
|
if (destruction_receiver.IsWindowDestroyedOrClosed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDLWindow::SetTextInputActive(bool active) {
|
||||||
|
if (!window_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (active) {
|
||||||
|
SDL_StartTextInput(window_);
|
||||||
|
} else {
|
||||||
|
SDL_StopTextInput(window_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SDLWindow::HandleMouseWheel(const SDL_MouseWheelEvent& event,
|
void SDLWindow::HandleMouseWheel(const SDL_MouseWheelEvent& event,
|
||||||
WindowDestructionReceiver& destruction_receiver) {
|
WindowDestructionReceiver& destruction_receiver) {
|
||||||
RevealAutoHiddenCursor();
|
RevealAutoHiddenCursor();
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue