feat(input): 完成阶段 1 输入采集与规范化
This commit is contained in:
@@ -185,6 +185,8 @@ public:
|
||||
// 绘制入口。实现应只在 dirty/show 条件满足时写屏,并在完成后清理 dirty。
|
||||
virtual void draw() = 0;
|
||||
// 事件入口。返回 true 表示事件已消费;若事件造成视觉变化,应设置 eventVisualChanged 并请求重绘。
|
||||
// 阶段 1 兼容过渡:EX_MOUSE 永久保留在此入口;EX_KEY 暂时仍按旧路径下发以保持行为,
|
||||
// 计划在阶段 3 KeyboardRouter 一次性换轨后停止下发。派生控件不要再新增对 EX_KEY 的依赖。
|
||||
virtual bool handleEvent(const ExMessage& msg) = 0;
|
||||
// 轻量清理接口:仅用于“没有机会收到本次 WM_MOUSEMOVE”的兄弟分支,
|
||||
// 清掉 hover / tooltip / 临时按下态等鼠标瞬时状态。
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*******************************************************************************
|
||||
* StellarX native keyboard, text, IME, and activation event model.
|
||||
* Stage 1 only captures, normalizes, queues, and observes these events.
|
||||
* Focus, routing, and control behavior are intentionally out of scope.
|
||||
******************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
namespace StellarX
|
||||
{
|
||||
enum class KeyAction : std::uint8_t
|
||||
{
|
||||
Down,
|
||||
Up
|
||||
};
|
||||
|
||||
// Snapshot modifiers in WndProc; delayed consumers must not reread global state.
|
||||
struct KeyModifiers
|
||||
{
|
||||
bool leftControl = false;
|
||||
bool rightControl = false;
|
||||
bool leftShift = false;
|
||||
bool rightShift = false;
|
||||
bool leftAlt = false;
|
||||
bool rightAlt = false;
|
||||
|
||||
bool control() const noexcept { return leftControl || rightControl; }
|
||||
bool shift() const noexcept { return leftShift || rightShift; }
|
||||
bool alt() const noexcept { return leftAlt || rightAlt; }
|
||||
};
|
||||
|
||||
struct KeyEvent
|
||||
{
|
||||
KeyAction action = KeyAction::Down;
|
||||
|
||||
UINT virtualKey = 0; // Raw WndProc VK; VK_PROCESSKEY is never rewritten.
|
||||
UINT physicalVirtualKey = 0; // Left/right-normalized modifier VK.
|
||||
UINT scanCode = 0;
|
||||
|
||||
std::uint16_t repeatCount = 0;
|
||||
bool extended = false;
|
||||
bool wasDown = false;
|
||||
bool altContext = false; // Raw lParam bit 29; modifiers.alt() is the semantic snapshot.
|
||||
bool systemKey = false;
|
||||
|
||||
// Snapshot of the standard IME lifecycle axis at capture time.
|
||||
// It never overrides the per-key ownership expressed by VK_PROCESSKEY.
|
||||
bool imeComposingAtCapture = false;
|
||||
|
||||
KeyModifiers modifiers{};
|
||||
|
||||
bool isAutoRepeat() const noexcept
|
||||
{
|
||||
return action == KeyAction::Down && wasDown;
|
||||
}
|
||||
|
||||
bool isImeOwnedAtCapture() const noexcept
|
||||
{
|
||||
// Per-key ownership and IME lifecycle are intentionally independent axes.
|
||||
// A normal key captured during a standard composition remains a normal key;
|
||||
// routing may suppress it because of the lifecycle snapshot, but must not
|
||||
// rewrite its ownership.
|
||||
return virtualKey == VK_PROCESSKEY;
|
||||
}
|
||||
};
|
||||
|
||||
enum class TextSource : std::uint8_t
|
||||
{
|
||||
ImeResult,
|
||||
UnicodeChar,
|
||||
UnicodeScalar,
|
||||
AnsiCharFallback,
|
||||
Clipboard
|
||||
};
|
||||
|
||||
struct TextInputEvent
|
||||
{
|
||||
std::wstring text;
|
||||
TextSource source = TextSource::UnicodeChar;
|
||||
std::uint64_t compositionId = 0; // Zero for non-IME input.
|
||||
};
|
||||
|
||||
enum class CompositionStage : std::uint8_t
|
||||
{
|
||||
Start,
|
||||
Update,
|
||||
Commit,
|
||||
Cancel,
|
||||
End
|
||||
};
|
||||
|
||||
struct CompositionEvent
|
||||
{
|
||||
CompositionStage stage = CompositionStage::Start;
|
||||
std::uint64_t compositionId = 0;
|
||||
std::wstring compositionText;
|
||||
std::uint32_t caretOffset = 0;
|
||||
};
|
||||
|
||||
struct WindowActivationEvent
|
||||
{
|
||||
bool keyboardFocusActive = false;
|
||||
HWND otherWindow = nullptr;
|
||||
};
|
||||
|
||||
using NativeInputPayload = std::variant<
|
||||
KeyEvent,
|
||||
TextInputEvent,
|
||||
CompositionEvent,
|
||||
WindowActivationEvent>;
|
||||
|
||||
struct NativeInputEvent
|
||||
{
|
||||
std::uint64_t sequence = 0;
|
||||
DWORD messageTime = 0;
|
||||
HWND hwnd = nullptr;
|
||||
NativeInputPayload payload{};
|
||||
};
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "InputEvents.h"
|
||||
#include "SxLog.h"
|
||||
#include "Control.h"
|
||||
#include"Canvas.h"
|
||||
|
||||
@@ -17,14 +17,26 @@
|
||||
|
||||
#include "Control.h"
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <windows.h>
|
||||
|
||||
class Window;
|
||||
|
||||
namespace StellarX::Internal
|
||||
{
|
||||
class NativeInputContext;
|
||||
struct NativeInputRuntimeSnapshot;
|
||||
NativeInputRuntimeSnapshot inspectWindowNativeInputForTesting(const ::Window& window) noexcept;
|
||||
}
|
||||
|
||||
class Window
|
||||
{
|
||||
friend class Dialog;
|
||||
friend StellarX::Internal::NativeInputRuntimeSnapshot
|
||||
StellarX::Internal::inspectWindowNativeInputForTesting(const Window& window) noexcept;
|
||||
|
||||
// —— 尺寸状态 ——(绘制尺寸与待应用尺寸分离;收口时一次性更新)
|
||||
int width; // 当前有效宽(已应用到画布/控件的客户区宽)
|
||||
@@ -46,7 +58,14 @@ class Window
|
||||
// EasyX 的 WndProc 与宿主事件循环分属不同线程;只有 WndProc 完成 WM_NCDESTROY
|
||||
// 转发后才发布 false,避免宿主提前析构仍在执行 WndProcThunk 的 Window 对象。
|
||||
std::atomic<bool> nativeWindowAlive{ false };
|
||||
// 阶段 1 只建立采集、规范化与跨线程队列;宿主安全点只观测排空,不做业务路由。
|
||||
std::unique_ptr<StellarX::Internal::NativeInputContext> nativeInputContext;
|
||||
bool nativeInputFailureReported = false;
|
||||
std::uint64_t nativeInputRejectedTextReported = 0;
|
||||
std::uint64_t nativeInputImeQueryFailuresReported = 0;
|
||||
std::uint64_t nativeInputDiscardedFragmentsReported = 0;
|
||||
bool hasLiveNativeWindow() const noexcept;
|
||||
bool drainNativeInputAtSafePoint();
|
||||
static LRESULT CALLBACK WndProcThunk(HWND h, UINT m, WPARAM w, LPARAM l); // 静态过程分发到 this
|
||||
|
||||
// —— 绘制相关 ——(是否使用合成双缓冲、窗口标题、背景等)
|
||||
|
||||
Reference in New Issue
Block a user