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 AddDialogImpl(ImGuiDialog* dialog);
|
||||
void RemoveDialogImpl(ImGuiDialog* dialog);
|
||||
|
||||
void ClearInput();
|
||||
void OnKey(KeyEvent& e, bool is_down);
|
||||
static int MouseEventButtonToImGui(const MouseEvent& e);
|
||||
void UpdateMousePosition(float x, float y);
|
||||
void SwitchToPhysicalMouseAndUpdateMousePosition(const MouseEvent& e);
|
||||
|
||||
|
|
@ -108,6 +112,15 @@ class ImGuiDrawer : public WindowInputListener, public UIDrawer {
|
|||
// detaching the presenter.
|
||||
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 it's TouchEvent::kPointerIDNone, the ImGui mouse is controlled by the
|
||||
// mouse.
|
||||
|
|
|
|||
|
|
@ -311,6 +311,14 @@ class Window {
|
|||
// the cursor instantly.
|
||||
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; }
|
||||
// May be applied in a delayed way or dropped at all, HasFocus will not
|
||||
// necessarily be true immediately.
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ class SDLWindow final : public Window {
|
|||
|
||||
static void HandleSDLEvent(const SDL_Event& event);
|
||||
|
||||
void SetTextInputActive(bool active) override;
|
||||
|
||||
protected:
|
||||
uint32_t GetLatestDpiImpl() const override;
|
||||
bool OpenImpl() override;
|
||||
|
|
@ -51,6 +53,8 @@ class SDLWindow final : public Window {
|
|||
WindowDestructionReceiver& destruction_receiver);
|
||||
void HandleMouseWheel(const SDL_MouseWheelEvent& event,
|
||||
WindowDestructionReceiver& destruction_receiver);
|
||||
void HandleTextInput(const SDL_TextInputEvent& event,
|
||||
WindowDestructionReceiver& destruction_receiver);
|
||||
void RevealAutoHiddenCursor();
|
||||
void SetCursorAutoHideTimer();
|
||||
void RemoveCursorAutoHideTimer();
|
||||
|
|
|
|||
|
|
@ -377,7 +377,9 @@ class KeyboardInputDialog : public XamDialog {
|
|||
}
|
||||
if (ImGui::InputText("##body", text_buffer_.data(), text_buffer_.size(),
|
||||
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;
|
||||
ImGui::CloseCurrentPopup();
|
||||
Close();
|
||||
|
|
@ -390,7 +392,7 @@ class KeyboardInputDialog : public XamDialog {
|
|||
}
|
||||
}
|
||||
if (ImGui::Button("OK")) {
|
||||
text_ = std::string(text_buffer_.data(), text_buffer_.size());
|
||||
text_ = std::string(text_buffer_.data());
|
||||
cancelled_ = false;
|
||||
ImGui::CloseCurrentPopup();
|
||||
Close();
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include <rex/ui/imgui_drawer.h>
|
||||
#include <rex/ui/ui_event.h>
|
||||
#include <rex/ui/window.h>
|
||||
#include <rex/ui/windowed_app_context.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
|
|
@ -44,11 +45,11 @@ ImGuiDrawer::~ImGuiDrawer() {
|
|||
SetPresenter(nullptr);
|
||||
if (!dialogs_.empty()) {
|
||||
window_->RemoveInputListener(this);
|
||||
if (internal_state_) {
|
||||
ImGui::SetCurrentContext(internal_state_);
|
||||
if (touch_pointer_id_ == TouchEvent::kPointerIDNone && ImGui::IsAnyMouseDown()) {
|
||||
if (touch_pointer_id_ == TouchEvent::kPointerIDNone && mouse_buttons_down_) {
|
||||
window_->ReleaseMouse();
|
||||
}
|
||||
if (text_input_active_) {
|
||||
window_->SetTextInputActive(false);
|
||||
}
|
||||
}
|
||||
if (internal_state_) {
|
||||
|
|
@ -59,6 +60,20 @@ ImGuiDrawer::~ImGuiDrawer() {
|
|||
|
||||
void ImGuiDrawer::AddDialog(ImGuiDialog* 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.
|
||||
if (std::find(dialogs_.cbegin(), dialogs_.cend(), dialog) != dialogs_.cend()) {
|
||||
return;
|
||||
|
|
@ -78,6 +93,15 @@ void ImGuiDrawer::AddDialog(ImGuiDialog* dialog) {
|
|||
|
||||
void ImGuiDrawer::RemoveDialog(ImGuiDialog* 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);
|
||||
if (it == dialogs_.cend()) {
|
||||
return;
|
||||
|
|
@ -265,9 +289,19 @@ std::optional<ImGuiKey> ImGuiDrawer::VirtualKeyToImGuiKey(VirtualKey vkey) {
|
|||
{ui::VirtualKey::kF11, ImGuiKey_F11},
|
||||
{ui::VirtualKey::kF12, ImGuiKey_F12},
|
||||
// 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::kControl, ImGuiKey_LeftCtrl},
|
||||
{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::kNumLock, ImGuiKey_NumLock},
|
||||
{ui::VirtualKey::kScroll, ImGuiKey_ScrollLock},
|
||||
|
|
@ -402,7 +436,15 @@ void ImGuiDrawer::Draw(UIDrawContext& ui_draw_context) {
|
|||
|
||||
if (reset_mouse_position_after_next_frame_) {
|
||||
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
|
||||
|
|
@ -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) {
|
||||
SwitchToPhysicalMouseAndUpdateMousePosition(e);
|
||||
auto& io = GetIO();
|
||||
int button = -1;
|
||||
switch (e.button()) {
|
||||
case rex::ui::MouseEvent::Button::kLeft: {
|
||||
button = 0;
|
||||
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()) {
|
||||
int button = MouseEventButtonToImGui(e);
|
||||
if (button >= 0) {
|
||||
if (!(mouse_buttons_down_ & (UINT32_C(1) << button))) {
|
||||
if (!mouse_buttons_down_) {
|
||||
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) {
|
||||
SwitchToPhysicalMouseAndUpdateMousePosition(e);
|
||||
auto& io = GetIO();
|
||||
int button = -1;
|
||||
switch (e.button()) {
|
||||
case rex::ui::MouseEvent::Button::kLeft: {
|
||||
button = 0;
|
||||
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]) {
|
||||
io.MouseDown[button] = false;
|
||||
if (!ImGui::IsAnyMouseDown()) {
|
||||
int button = MouseEventButtonToImGui(e);
|
||||
if (button >= 0) {
|
||||
if (mouse_buttons_down_ & (UINT32_C(1) << button)) {
|
||||
mouse_buttons_down_ &= ~(UINT32_C(1) << button);
|
||||
if (!mouse_buttons_down_) {
|
||||
window_->ReleaseMouse();
|
||||
}
|
||||
}
|
||||
io.AddMouseButtonEvent(button, false);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiDrawer::OnMouseWheel(MouseEvent& e) {
|
||||
SwitchToPhysicalMouseAndUpdateMousePosition(e);
|
||||
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) {
|
||||
|
|
@ -550,8 +592,13 @@ void ImGuiDrawer::OnTouchEvent(TouchEvent& e) {
|
|||
// The latest pointer needs to be controlling the ImGui mouse.
|
||||
if (touch_pointer_id_ == TouchEvent::kPointerIDNone) {
|
||||
// Switching from the mouse to touch input.
|
||||
if (ImGui::IsAnyMouseDown()) {
|
||||
std::memset(io.MouseDown, 0, sizeof(io.MouseDown));
|
||||
if (mouse_buttons_down_) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -563,27 +610,32 @@ void ImGuiDrawer::OnTouchEvent(TouchEvent& e) {
|
|||
}
|
||||
UpdateMousePosition(e.x(), e.y());
|
||||
if (action == TouchEvent::Action::kUp || action == TouchEvent::Action::kCancel) {
|
||||
io.MouseDown[0] = false;
|
||||
io.AddMouseButtonEvent(0, false);
|
||||
touch_pointer_id_ = TouchEvent::kPointerIDNone;
|
||||
// Make sure that after a touch, the ImGui mouse isn't hovering over
|
||||
// anything.
|
||||
reset_mouse_position_after_next_frame_ = true;
|
||||
} else {
|
||||
io.MouseDown[0] = true;
|
||||
io.AddMouseButtonEvent(0, true);
|
||||
reset_mouse_position_after_next_frame_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiDrawer::ClearInput() {
|
||||
auto& io = GetIO();
|
||||
if (touch_pointer_id_ == TouchEvent::kPointerIDNone && ImGui::IsAnyMouseDown()) {
|
||||
if (touch_pointer_id_ == TouchEvent::kPointerIDNone && mouse_buttons_down_) {
|
||||
window_->ReleaseMouse();
|
||||
}
|
||||
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
|
||||
std::memset(io.MouseDown, 0, sizeof(io.MouseDown));
|
||||
mouse_buttons_down_ = 0;
|
||||
io.ClearEventsQueue();
|
||||
io.ClearInputKeys();
|
||||
io.ClearInputMouse();
|
||||
touch_pointer_id_ = TouchEvent::kPointerIDNone;
|
||||
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) {
|
||||
|
|
@ -594,35 +646,47 @@ void ImGuiDrawer::OnKey(KeyEvent& e, bool is_down) {
|
|||
}
|
||||
switch (virtual_key) {
|
||||
case VirtualKey::kShift:
|
||||
io.KeyShift = is_down;
|
||||
case VirtualKey::kLShift:
|
||||
case VirtualKey::kRShift:
|
||||
io.AddKeyEvent(ImGuiMod_Shift, is_down);
|
||||
break;
|
||||
case VirtualKey::kControl:
|
||||
io.KeyCtrl = is_down;
|
||||
case VirtualKey::kLControl:
|
||||
case VirtualKey::kRControl:
|
||||
io.AddKeyEvent(ImGuiMod_Ctrl, is_down);
|
||||
break;
|
||||
case VirtualKey::kMenu:
|
||||
// FIXME(Triang3l): Doesn't work in xenia-ui-window-demo.
|
||||
io.KeyAlt = is_down;
|
||||
case VirtualKey::kLMenu:
|
||||
case VirtualKey::kRMenu:
|
||||
io.AddKeyEvent(ImGuiMod_Alt, is_down);
|
||||
break;
|
||||
case VirtualKey::kLWin:
|
||||
io.KeySuper = is_down;
|
||||
case VirtualKey::kRWin:
|
||||
io.AddKeyEvent(ImGuiMod_Super, is_down);
|
||||
break;
|
||||
default:
|
||||
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) {
|
||||
auto& io = GetIO();
|
||||
float physical_to_logical = float(window_->GetMediumDpi()) / float(window_->GetDpi());
|
||||
io.MousePos.x = x * physical_to_logical;
|
||||
io.MousePos.y = y * physical_to_logical;
|
||||
io.AddMousePosEvent(x * physical_to_logical, y * physical_to_logical);
|
||||
}
|
||||
|
||||
void ImGuiDrawer::SwitchToPhysicalMouseAndUpdateMousePosition(const MouseEvent& e) {
|
||||
if (touch_pointer_id_ != TouchEvent::kPointerIDNone) {
|
||||
touch_pointer_id_ = TouchEvent::kPointerIDNone;
|
||||
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
|
||||
// buttons as well as mouse capture have been released when switching to
|
||||
// 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:
|
||||
window_id = event.key.windowID;
|
||||
break;
|
||||
case SDL_EVENT_TEXT_INPUT:
|
||||
window_id = event.text.windowID;
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
window_id = event.button.windowID;
|
||||
|
|
@ -589,6 +592,9 @@ void SDLWindow::HandleEvent(const SDL_Event& event) {
|
|||
case SDL_EVENT_KEY_UP:
|
||||
HandleKey(event.key, false, destruction_receiver);
|
||||
break;
|
||||
case SDL_EVENT_TEXT_INPUT:
|
||||
HandleTextInput(event.text, destruction_receiver);
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
HandleMouseButton(event.button, true, destruction_receiver);
|
||||
break;
|
||||
|
|
@ -662,6 +668,68 @@ void SDLWindow::HandleMouseMotion(const SDL_MouseMotionEvent& event,
|
|||
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,
|
||||
WindowDestructionReceiver& destruction_receiver) {
|
||||
RevealAutoHiddenCursor();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue