IME支持
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
# Generated from CLion C/C++ Code Style settings
|
||||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: LLVM
|
||||
@@ -8,14 +7,12 @@ AlignConsecutiveDeclarations: true
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowShortBlocksOnASingleLine: true
|
||||
AllowShortCaseLabelsOnASingleLine: false # 禁止 case 标签与语句在同一行
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortReturnStatementsOnASingleLine: WithoutReturnValue # **只允许无返回值的 return 语句在单行**
|
||||
AlwaysBreakAfterDefinitionReturnType: false
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: false
|
||||
BraceWrapping:
|
||||
BinPackParameters: false
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
@@ -33,14 +30,13 @@ BraceWrapping:
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializers: AfterColon
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
ColumnLimit: 120
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ContinuationIndentWidth: 8
|
||||
Cpp11BracedListStyle: false
|
||||
IncludeCategories:
|
||||
IncludeCategories:
|
||||
- Regex: '^<.*'
|
||||
Priority: 1
|
||||
- Regex: '^".*'
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "widget/panel_widget/mbox.h"
|
||||
|
||||
#include "utf8.h"
|
||||
#include "widget/leaf_widget/meditable_text_box.h"
|
||||
#include "widget/panel_widget/moverlay.h"
|
||||
|
||||
void test_color() {
|
||||
@@ -96,7 +97,11 @@ int main(int argc, char* argv[]) {
|
||||
.warp_text(true)
|
||||
)
|
||||
]
|
||||
]
|
||||
],
|
||||
|
||||
mslot(mv_box)
|
||||
.horizontal_alignment(horizontal_alignment_t::stretch)
|
||||
+ mnew(meditable_text_box)
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
@@ -32,3 +32,7 @@ endif ()
|
||||
# 添加编译shader的自定义命令
|
||||
add_mirage_shader_directory(${CMAKE_CURRENT_SOURCE_DIR}/src/shaders)
|
||||
add_shader_dependencies(${PROJECT_NAME})
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC imm32)
|
||||
endif ()
|
||||
|
||||
@@ -324,6 +324,11 @@ text_layout_t font_manager::layout_text(
|
||||
// --- 处理文本末尾的最后一行 ---
|
||||
finish_line();
|
||||
|
||||
// --- 如果没有内容, 则设置高度为主字体的高度 ---
|
||||
if (total_height == 0.f && primary_font) {
|
||||
total_height = primary_font->get_metrics().line_height * line_spacing;
|
||||
}
|
||||
|
||||
// --- 设置最终布局尺寸 ---
|
||||
layout.total_size = { total_width, total_height };
|
||||
|
||||
|
||||
53
src/mirage_render/src/ime/ime.cpp
Normal file
53
src/mirage_render/src/ime/ime.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "ime.h"
|
||||
|
||||
void ime::process_char(char32_t c) {
|
||||
for (const auto& processor: ime_processors_) {
|
||||
if (const auto p = processor.lock()) {
|
||||
p->process_ime_char(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ime::process_backspace() {
|
||||
for (const auto& processor: ime_processors_) {
|
||||
if (const auto p = processor.lock()) {
|
||||
p->process_ime_backspace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ime::process_text(const std::u32string& text) {
|
||||
for (const auto& processor: ime_processors_) {
|
||||
if (const auto p = processor.lock()) {
|
||||
p->process_ime_text(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ime::process_clear() {
|
||||
for (const auto& processor: ime_processors_) {
|
||||
if (const auto p = processor.lock()) {
|
||||
p->process_ime_clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Eigen::Vector2i> ime::get_cursor_pos() const {
|
||||
for (const auto& processor: ime_processors_) {
|
||||
if (const auto p = processor.lock()) {
|
||||
if (auto pos = p->get_ime_cursor_pos()) { return pos; }
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void ime::register_processor(const std::weak_ptr<ime_processor>& processor) {
|
||||
ime_processors_.push_back(processor);
|
||||
}
|
||||
|
||||
void ime::unregister_processor(std::weak_ptr<ime_processor> processor) {
|
||||
auto pred = [&processor](const std::weak_ptr<ime_processor>& p) {
|
||||
return p.lock() == processor.lock();
|
||||
};
|
||||
std::erase_if(ime_processors_, pred);
|
||||
}
|
||||
37
src/mirage_render/src/ime/ime.h
Normal file
37
src/mirage_render/src/ime/ime.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <Eigen/Eigen>
|
||||
|
||||
#include "misc/delegates.h"
|
||||
|
||||
class ime_processor {
|
||||
public:
|
||||
virtual ~ime_processor() = default;
|
||||
|
||||
virtual void process_ime_char(char32_t c) = 0;
|
||||
virtual void process_ime_backspace() = 0;
|
||||
virtual void process_ime_text(const std::u32string& text) = 0;
|
||||
virtual void process_ime_clear() = 0;
|
||||
virtual std::optional<Eigen::Vector2i> get_ime_cursor_pos() const{ return std::nullopt; }
|
||||
};
|
||||
|
||||
class ime {
|
||||
public:
|
||||
static auto& get() {
|
||||
static ime instance;
|
||||
return instance;
|
||||
}
|
||||
void process_char(char32_t c);
|
||||
void process_backspace();
|
||||
void process_text(const std::u32string& text);
|
||||
void process_clear();
|
||||
std::optional<Eigen::Vector2i> get_cursor_pos() const;
|
||||
|
||||
bool has_processors() const { return !ime_processors_.empty(); }
|
||||
void register_processor(const std::weak_ptr<ime_processor>& processor);
|
||||
void unregister_processor(std::weak_ptr<ime_processor> processor);
|
||||
private:
|
||||
ime() = default;
|
||||
|
||||
std::vector<std::weak_ptr<ime_processor>> ime_processors_;
|
||||
};
|
||||
@@ -37,6 +37,11 @@ public:
|
||||
*/
|
||||
bool is_visible() const;
|
||||
|
||||
/**
|
||||
* @brief 请求重新绘制
|
||||
*/
|
||||
void request_redraw() { on_request_redraw.broadcast(); }
|
||||
|
||||
//-------------- 窗口位置和大小 --------------
|
||||
|
||||
/**
|
||||
@@ -228,6 +233,7 @@ public:
|
||||
|
||||
multicast_delegate<const Eigen::Vector2i&> on_move_delegate;
|
||||
multicast_delegate<const Eigen::Vector2i&> on_resize_delegate;
|
||||
multicast_delegate<> on_request_redraw;
|
||||
multicast_delegate<platform_window*> on_close_delegate;
|
||||
|
||||
multicast_delegate<const Eigen::Vector2f&> on_mouse_move_delegate;
|
||||
@@ -236,6 +242,8 @@ public:
|
||||
multicast_delegate<const Eigen::Vector2f&, mouse_button> on_mouse_button_dbl_delegate;
|
||||
multicast_delegate<const Eigen::Vector2f&, wheel_event> on_mouse_wheel_delegate;
|
||||
multicast_delegate<> on_mouse_leave_delegate;
|
||||
|
||||
inline static std::vector<platform_window*> windows;
|
||||
private:
|
||||
/** 原生窗口句柄 */
|
||||
void* window_handle_{};
|
||||
|
||||
@@ -5,124 +5,16 @@
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
#include "utf8.h"
|
||||
#include "ime/ime.h"
|
||||
#include "windows_window_event.h"
|
||||
|
||||
#define WINDOW_HANDLE static_cast<HWND>(window_handle_)
|
||||
|
||||
bool mouse_tracking_ = FALSE;
|
||||
std::vector<platform_window*> windows;
|
||||
|
||||
platform_window* get_window_from_hwnd(const HWND hwnd) {
|
||||
for (const auto& window: windows) { if (window->get_window_handle() == hwnd) { return window; } }
|
||||
return nullptr;
|
||||
}
|
||||
windows_window_event_handler event_handler;
|
||||
|
||||
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||
bool processed = false;
|
||||
// 窗口关闭事件
|
||||
if (uMsg == WM_CLOSE) {
|
||||
std::erase_if(windows,
|
||||
[hwnd](platform_window* window) {
|
||||
if (window->get_window_handle() == hwnd) {
|
||||
window->close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
processed = true;
|
||||
}
|
||||
|
||||
// 窗口销毁事件
|
||||
if (uMsg == WM_DESTROY) {
|
||||
PostQuitMessage(0);
|
||||
processed = true;
|
||||
}
|
||||
|
||||
// 窗口移动事件
|
||||
if (uMsg == WM_MOVE) {
|
||||
if (const auto window = get_window_from_hwnd(hwnd)) {
|
||||
window->on_move(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
||||
}
|
||||
processed = true;
|
||||
}
|
||||
|
||||
// 窗口大小改变事件
|
||||
if (uMsg == WM_SIZE) {
|
||||
if (const auto window = get_window_from_hwnd(hwnd)) {
|
||||
window->on_resize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
||||
}
|
||||
processed = true;
|
||||
}
|
||||
|
||||
// 鼠标移动事件
|
||||
if (uMsg == WM_MOUSEMOVE) {
|
||||
if (const auto window = get_window_from_hwnd(hwnd)) {
|
||||
const int x = GET_X_LPARAM(lParam);
|
||||
const int y = GET_Y_LPARAM(lParam);
|
||||
const Eigen::Vector2f pos(static_cast<float>(x), static_cast<float>(y));
|
||||
window->handle_mouse_move(pos);
|
||||
}
|
||||
if (!mouse_tracking_) {
|
||||
TRACKMOUSEEVENT tme;
|
||||
tme.cbSize = sizeof(TRACKMOUSEEVENT);
|
||||
tme.dwFlags = TME_LEAVE;
|
||||
tme.hwndTrack = hwnd;
|
||||
tme.dwHoverTime = HOVER_DEFAULT;
|
||||
TrackMouseEvent(&tme);
|
||||
mouse_tracking_ = TRUE;
|
||||
}
|
||||
processed = true;
|
||||
}
|
||||
|
||||
// 鼠标按键事件
|
||||
const auto lmr_button = uMsg >= WM_LBUTTONDOWN && uMsg <= WM_MBUTTONDBLCLK;
|
||||
const auto x_button = uMsg >= WM_XBUTTONDOWN && uMsg <= WM_XBUTTONDBLCLK;
|
||||
if (lmr_button || x_button) {
|
||||
if (const auto window = get_window_from_hwnd(hwnd)) {
|
||||
const int x = GET_X_LPARAM(lParam);
|
||||
const int y = GET_Y_LPARAM(lParam);
|
||||
const Eigen::Vector2f pos(static_cast<float>(x), static_cast<float>(y));
|
||||
const auto action = platform_event_to_mouse_action(uMsg, wParam);
|
||||
const auto button = platform_event_to_mouse_button(uMsg, wParam);
|
||||
if (action == mouse_action::press)
|
||||
window->handle_mouse_button_down(pos, button);
|
||||
else if (action == mouse_action::release)
|
||||
window->handle_mouse_button_up(pos, button);
|
||||
|
||||
// 对于双击事件,先处理按下事件,然后处理双击事件
|
||||
if (action == mouse_action::dbl_click)
|
||||
{
|
||||
window->handle_mouse_button_down(pos, button);
|
||||
window->handle_mouse_dbl_click(pos, button);
|
||||
}
|
||||
}
|
||||
processed = true;
|
||||
}
|
||||
|
||||
// 鼠标滚轮事件
|
||||
if (uMsg == WM_MOUSEWHEEL || uMsg == WM_MOUSEHWHEEL) {
|
||||
if (const auto window = get_window_from_hwnd(hwnd)) {
|
||||
const int x = GET_X_LPARAM(lParam);
|
||||
const int y = GET_Y_LPARAM(lParam);
|
||||
POINT screen_point = { x, y };
|
||||
ScreenToClient(hwnd, &screen_point);
|
||||
|
||||
// 现在 screen_point.x 和 screen_point.y 包含相对于窗口客户区的坐标
|
||||
const Eigen::Vector2f client_pos(static_cast<float>(screen_point.x), static_cast<float>(screen_point.y));
|
||||
|
||||
// 计算滚轮事件的增量
|
||||
const auto delta = platform_event_to_wheel_event(uMsg, wParam, lParam);
|
||||
window->handle_mouse_wheel(client_pos, delta);
|
||||
}
|
||||
processed = true;
|
||||
}
|
||||
|
||||
if (uMsg == WM_MOUSELEAVE) {
|
||||
mouse_tracking_ = FALSE;
|
||||
if (const auto window = get_window_from_hwnd(hwnd)) { window->handle_mouse_leave(); }
|
||||
processed = true;
|
||||
}
|
||||
|
||||
if (processed) { return 0; }
|
||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
return event_handler.handle_event(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
platform_window::platform_window(int32_t in_width, int32_t in_height, const wchar_t* in_title) {
|
||||
@@ -137,8 +29,7 @@ platform_window::platform_window(int32_t in_width, int32_t in_height, const wcha
|
||||
|
||||
RECT rect = { 0, 0, in_width, in_height };
|
||||
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
|
||||
|
||||
window_handle_ = static_cast<void*>(CreateWindowW(
|
||||
auto hwnd = CreateWindowW(
|
||||
L"mirage_window_class",
|
||||
in_title,
|
||||
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
||||
@@ -150,7 +41,8 @@ platform_window::platform_window(int32_t in_width, int32_t in_height, const wcha
|
||||
nullptr,
|
||||
GetModuleHandleW(nullptr),
|
||||
nullptr
|
||||
));
|
||||
);
|
||||
window_handle_ = static_cast<void*>(hwnd);
|
||||
|
||||
if (!window_handle_) {
|
||||
std::cerr << "Failed to create window" << std::endl;
|
||||
|
||||
@@ -0,0 +1,409 @@
|
||||
#include "windows_window_event.h"
|
||||
|
||||
#include <windowsx.h>
|
||||
#include <utf8.h>
|
||||
|
||||
#include "ime/ime.h"
|
||||
#include "platform_window/platform_window.h"
|
||||
|
||||
// 默认处理过程宏,用于未处理的消息传递给系统默认处理
|
||||
#define DEFAULT_PROCESS DefWindowProc(hwnd, msg, w_param, l_param)
|
||||
|
||||
// 鼠标追踪状态映射表,记录每个窗口是否正在追踪鼠标
|
||||
std::map<HWND, bool> mouse_tracking_;
|
||||
|
||||
/**
|
||||
* @brief 根据窗口句柄查找对应的窗口对象
|
||||
* @param hwnd Windows窗口句柄
|
||||
* @return 对应的平台窗口对象指针,未找到则返回nullptr
|
||||
*/
|
||||
platform_window* get_window_from_hwnd(const HWND hwnd) {
|
||||
for (const auto& window: platform_window::windows) {
|
||||
if (window->get_window_handle() == hwnd) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
namespace window_events {
|
||||
/**
|
||||
* @brief 处理窗口关闭消息
|
||||
* @param hwnd 窗口句柄
|
||||
* @param msg 消息类型
|
||||
* @param w_param 消息参数
|
||||
* @param l_param 消息参数
|
||||
* @return 处理结果
|
||||
*/
|
||||
LRESULT handle_window_close(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
|
||||
// 创建谓词函数用于查找并关闭对应窗口
|
||||
auto pred = [hwnd](platform_window* window) {
|
||||
if (window->get_window_handle() == hwnd) {
|
||||
window->close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// 从窗口列表中删除已关闭的窗口
|
||||
std::erase_if(platform_window::windows, pred);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理窗口销毁消息
|
||||
* @param hwnd 窗口句柄
|
||||
* @param msg 消息类型
|
||||
* @param w_param 消息参数
|
||||
* @param l_param 消息参数
|
||||
* @return 处理结果
|
||||
*/
|
||||
LRESULT handle_window_destroy(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
|
||||
// 发送退出消息,结束消息循环
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理窗口移动消息
|
||||
* @param hwnd 窗口句柄
|
||||
* @param msg 消息类型
|
||||
* @param w_param 消息参数
|
||||
* @param l_param 消息参数,包含窗口的新位置
|
||||
* @return 处理结果
|
||||
*/
|
||||
LRESULT handle_window_move(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
|
||||
if (const auto window = get_window_from_hwnd(hwnd)) {
|
||||
// 从l_param中提取x和y坐标并通知窗口
|
||||
window->on_move(GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理窗口大小变化消息
|
||||
* @param hwnd 窗口句柄
|
||||
* @param msg 消息类型
|
||||
* @param w_param 消息参数
|
||||
* @param l_param 消息参数,包含窗口的新大小
|
||||
* @return 处理结果
|
||||
*/
|
||||
LRESULT handle_window_size(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
|
||||
if (const auto window = get_window_from_hwnd(hwnd)) {
|
||||
// 从l_param中提取宽度和高度并通知窗口
|
||||
window->on_resize(GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理窗口获得焦点消息
|
||||
* @param hwnd 窗口句柄
|
||||
* @param msg 消息类型
|
||||
* @param w_param 消息参数
|
||||
* @param l_param 消息参数
|
||||
* @return 处理结果
|
||||
*/
|
||||
LRESULT handle_window_set_focus(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
|
||||
// 创建并显示光标
|
||||
CreateCaret(hwnd, nullptr, 2, 20); // 宽度2像素,高度20像素
|
||||
ShowCaret(hwnd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理窗口失去焦点消息
|
||||
* @param hwnd 窗口句柄
|
||||
* @param msg 消息类型
|
||||
* @param w_param 消息参数
|
||||
* @param l_param 消息参数
|
||||
* @return 处理结果
|
||||
*/
|
||||
LRESULT handle_window_kill_focus(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
|
||||
// 隐藏并销毁光标
|
||||
HideCaret(hwnd);
|
||||
DestroyCaret();
|
||||
return 0;
|
||||
}
|
||||
} // namespace window_events
|
||||
|
||||
namespace mouse_events {
|
||||
/**
|
||||
* @brief 处理鼠标移动消息
|
||||
* @param hwnd 窗口句柄
|
||||
* @param msg 消息类型
|
||||
* @param w_param 消息参数
|
||||
* @param l_param 消息参数,包含鼠标位置
|
||||
* @return 处理结果
|
||||
*/
|
||||
LRESULT handle_window_mouse_move(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
|
||||
if (const auto window = get_window_from_hwnd(hwnd)) {
|
||||
// 从l_param中提取鼠标坐标
|
||||
const int x = GET_X_LPARAM(l_param);
|
||||
const int y = GET_Y_LPARAM(l_param);
|
||||
// 转换为浮点坐标并通知窗口
|
||||
const Eigen::Vector2f pos(static_cast<float>(x), static_cast<float>(y));
|
||||
window->handle_mouse_move(pos);
|
||||
}
|
||||
|
||||
// 如果当前窗口未在追踪鼠标,则开始追踪
|
||||
if (!mouse_tracking_[hwnd]) {
|
||||
TRACKMOUSEEVENT tme;
|
||||
tme.cbSize = sizeof(TRACKMOUSEEVENT);
|
||||
tme.dwFlags = TME_LEAVE;
|
||||
tme.hwndTrack = hwnd;
|
||||
tme.dwHoverTime = HOVER_DEFAULT;
|
||||
TrackMouseEvent(&tme);
|
||||
mouse_tracking_[hwnd] = TRUE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理鼠标离开窗口消息
|
||||
* @param hwnd 窗口句柄
|
||||
* @param msg 消息类型
|
||||
* @param w_param 消息参数
|
||||
* @param l_param 消息参数
|
||||
* @return 处理结果
|
||||
*/
|
||||
LRESULT handle_window_mouse_leave(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
|
||||
// 重置鼠标追踪状态
|
||||
mouse_tracking_[hwnd] = FALSE;
|
||||
if (const auto window = get_window_from_hwnd(hwnd)) {
|
||||
window->handle_mouse_leave();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理鼠标按钮消息(点击、释放、双击)
|
||||
* @param hwnd 窗口句柄
|
||||
* @param msg 消息类型
|
||||
* @param w_param 消息参数
|
||||
* @param l_param 消息参数,包含鼠标位置
|
||||
* @return 处理结果
|
||||
*/
|
||||
LRESULT handle_window_mouse_button(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
|
||||
if (const auto window = get_window_from_hwnd(hwnd)) {
|
||||
// 从l_param中提取鼠标坐标
|
||||
const int x = GET_X_LPARAM(l_param);
|
||||
const int y = GET_Y_LPARAM(l_param);
|
||||
const Eigen::Vector2f pos(static_cast<float>(x), static_cast<float>(y));
|
||||
|
||||
// 将Windows消息转换为应用程序定义的鼠标动作和按钮
|
||||
const auto action = platform_event_to_mouse_action(msg, w_param);
|
||||
const auto button = platform_event_to_mouse_button(msg, w_param);
|
||||
|
||||
// 根据动作类型分发事件
|
||||
if (action == mouse_action::press)
|
||||
window->handle_mouse_button_down(pos, button);
|
||||
else if (action == mouse_action::release)
|
||||
window->handle_mouse_button_up(pos, button);
|
||||
|
||||
// 对于双击事件,先处理按下事件,然后处理双击事件
|
||||
if (action == mouse_action::dbl_click) {
|
||||
window->handle_mouse_button_down(pos, button);
|
||||
window->handle_mouse_dbl_click(pos, button);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理鼠标滚轮消息
|
||||
* @param hwnd 窗口句柄
|
||||
* @param msg 消息类型
|
||||
* @param w_param 消息参数
|
||||
* @param l_param 消息参数,包含鼠标位置(屏幕坐标)
|
||||
* @return 处理结果
|
||||
*/
|
||||
LRESULT handle_window_mouse_wheel(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
|
||||
if (const auto window = get_window_from_hwnd(hwnd)) {
|
||||
// 从l_param中提取屏幕坐标
|
||||
const int x = GET_X_LPARAM(l_param);
|
||||
const int y = GET_Y_LPARAM(l_param);
|
||||
|
||||
// 将屏幕坐标转换为窗口客户区坐标
|
||||
POINT screen_point = { x, y };
|
||||
ScreenToClient(hwnd, &screen_point);
|
||||
|
||||
// 将坐标转换为浮点值
|
||||
const Eigen::Vector2f client_pos(static_cast<float>(screen_point.x),
|
||||
static_cast<float>(screen_point.y));
|
||||
|
||||
// 计算滚轮事件的增量并通知窗口
|
||||
const auto delta = platform_event_to_wheel_event(msg, w_param, l_param);
|
||||
window->handle_mouse_wheel(client_pos, delta);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace mouse_events
|
||||
|
||||
namespace ime_events {
|
||||
/**
|
||||
* @brief 处理输入法字符消息
|
||||
* @param hwnd 窗口句柄
|
||||
* @param msg 消息类型
|
||||
* @param w_param 消息参数,包含输入的字符
|
||||
* @param l_param 消息参数
|
||||
* @return 处理结果
|
||||
*/
|
||||
LRESULT handle_window_ime_char(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
|
||||
const auto input_char = static_cast<wchar_t>(w_param);
|
||||
if (input_char >= 32) {
|
||||
// 处理普通字符输入
|
||||
ime::get().process_char(input_char);
|
||||
}
|
||||
else if (input_char == VK_BACK) {
|
||||
// 处理退格键
|
||||
ime::get().process_backspace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理输入法组合消息(用于中文输入过程)
|
||||
* @param hwnd 窗口句柄
|
||||
* @param msg 消息类型
|
||||
* @param w_param 消息参数
|
||||
* @param l_param 消息参数
|
||||
* @return 处理结果
|
||||
*/
|
||||
LRESULT handle_window_ime_composition(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
|
||||
// 只处理组合字符串消息
|
||||
if (!(l_param & GCS_COMPSTR))
|
||||
return DEFAULT_PROCESS;
|
||||
|
||||
// 获取输入法上下文
|
||||
const auto himc = ImmGetContext(hwnd);
|
||||
if (!himc)
|
||||
return DEFAULT_PROCESS;
|
||||
|
||||
// 获取组合字符串的大小
|
||||
const auto dw_size = ImmGetCompositionString(himc, GCS_COMPSTR, nullptr, 0);
|
||||
if (dw_size <= 0)
|
||||
return DEFAULT_PROCESS;
|
||||
|
||||
// 分配缓冲区并获取组合字符串
|
||||
if (auto lpsz_result = static_cast<LPWSTR>(malloc(dw_size + sizeof(WCHAR)))) {
|
||||
ImmGetCompositionString(himc, GCS_COMPSTR, lpsz_result, dw_size);
|
||||
lpsz_result[dw_size / sizeof(WCHAR)] = 0;
|
||||
|
||||
// 将UTF-16字符串转换为UTF-8,再转换为UTF-32
|
||||
const auto& temp_utf8 = utf8::utf16to8(std::u16string((char16_t*)lpsz_result));
|
||||
const auto& utf32 = utf8::utf8to32(temp_utf8);
|
||||
ime::get().process_text(utf32);
|
||||
|
||||
// 设置IME窗口位置,跟随光标
|
||||
auto pos = ime::get().get_cursor_pos();
|
||||
if (!pos)
|
||||
pos = Eigen::Vector2i{ 0, 0 };
|
||||
|
||||
COMPOSITIONFORM cf;
|
||||
cf.dwStyle = CFS_POINT;
|
||||
cf.ptCurrentPos.x = pos->x();
|
||||
cf.ptCurrentPos.y = pos->y();
|
||||
ImmSetCompositionWindow(himc, &cf);
|
||||
|
||||
free(lpsz_result);
|
||||
}
|
||||
ImmReleaseContext(hwnd, himc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理输入法结束组合消息
|
||||
* @param hwnd 窗口句柄
|
||||
* @param msg 消息类型
|
||||
* @param w_param 消息参数
|
||||
* @param l_param 消息参数
|
||||
* @return 处理结果
|
||||
*/
|
||||
LRESULT handle_window_ime_end_composition(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
|
||||
// 清除IME状态
|
||||
ime::get().process_clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理输入法开始组合消息
|
||||
* @param hwnd 窗口句柄
|
||||
* @param msg 消息类型
|
||||
* @param w_param 消息参数
|
||||
* @param l_param 消息参数
|
||||
* @return 处理结果
|
||||
*/
|
||||
LRESULT handle_window_ime_start_composition(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
|
||||
// 如果没有注册IME处理器,则使用默认处理
|
||||
if (!ime::get().has_processors())
|
||||
return DEFAULT_PROCESS;
|
||||
|
||||
// 获取输入法上下文
|
||||
HIMC himc = ImmGetContext(hwnd);
|
||||
if (!himc)
|
||||
return DEFAULT_PROCESS;
|
||||
|
||||
// 获取光标位置,如果没有则默认为(0,0)
|
||||
auto pos = ime::get().get_cursor_pos();
|
||||
if (!pos)
|
||||
pos = Eigen::Vector2i{ 0, 0 };
|
||||
|
||||
// 设置组合窗口位置(跟随光标)
|
||||
COMPOSITIONFORM cf;
|
||||
cf.dwStyle = CFS_POINT;
|
||||
cf.ptCurrentPos.x = pos->x();
|
||||
cf.ptCurrentPos.y = pos->y();
|
||||
ImmSetCompositionWindow(himc, &cf);
|
||||
|
||||
// 设置组合窗口字体
|
||||
LOGFONT lf;
|
||||
GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &lf);
|
||||
ImmSetCompositionFont(himc, &lf);
|
||||
|
||||
ImmReleaseContext(hwnd, himc);
|
||||
|
||||
// 对于WM_IME_SETCONTEXT,如果要显示系统的输入法UI
|
||||
if (msg == WM_IME_SETCONTEXT && w_param == TRUE) {
|
||||
// 保持系统默认值以显示输入法UI
|
||||
l_param |= ISC_SHOWUICOMPOSITIONWINDOW;
|
||||
return DEFAULT_PROCESS;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace ime_events
|
||||
|
||||
#undef DEFAULT_PROCESS
|
||||
|
||||
windows_window_event_handler::windows_window_event_handler() {
|
||||
// 注册基础窗口事件处理器
|
||||
register_event_handler(WM_CLOSE, window_events::handle_window_close);
|
||||
register_event_handler(WM_DESTROY, window_events::handle_window_destroy);
|
||||
register_event_handler(WM_MOVE, window_events::handle_window_move);
|
||||
register_event_handler(WM_SIZE, window_events::handle_window_size);
|
||||
register_event_handler(WM_SETFOCUS, window_events::handle_window_set_focus);
|
||||
register_event_handler(WM_KILLFOCUS, window_events::handle_window_kill_focus);
|
||||
|
||||
// 注册鼠标事件处理器
|
||||
register_event_handler(WM_MOUSEMOVE, mouse_events::handle_window_mouse_move);
|
||||
register_event_handler(WM_MOUSELEAVE, mouse_events::handle_window_mouse_leave);
|
||||
register_predicate_handler([](UINT msg){
|
||||
const auto lmr_button = msg >= WM_LBUTTONDOWN && msg <= WM_MBUTTONDBLCLK;
|
||||
const auto x_button = msg >= WM_XBUTTONDOWN && msg <= WM_XBUTTONDBLCLK;
|
||||
return lmr_button || x_button;
|
||||
}, mouse_events::handle_window_mouse_button);
|
||||
register_predicate_handler([](UINT msg){
|
||||
// 检测所有鼠标滚轮事件
|
||||
return msg == WM_MOUSEWHEEL || msg == WM_MOUSEHWHEEL;
|
||||
}, mouse_events::handle_window_mouse_wheel);
|
||||
|
||||
// 注册IME事件处理器
|
||||
register_event_handler(WM_IME_COMPOSITION, ime_events::handle_window_ime_composition);
|
||||
register_event_handler(WM_IME_ENDCOMPOSITION, ime_events::handle_window_ime_end_composition);
|
||||
register_predicate_handler([](UINT msg) {
|
||||
return msg == WM_IME_STARTCOMPOSITION || msg == WM_IME_SETCONTEXT;
|
||||
}, ime_events::handle_window_ime_start_composition);
|
||||
register_predicate_handler([](UINT msg) {
|
||||
return msg == WM_IME_CHAR || msg == WM_CHAR;
|
||||
}, ime_events::handle_window_ime_char);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <windows.h>
|
||||
|
||||
class windows_window_event_handler {
|
||||
public:
|
||||
using event_handler_t = std::function<LRESULT(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param)>;
|
||||
|
||||
windows_window_event_handler();
|
||||
|
||||
void register_event_handler(UINT msg, const event_handler_t& handler) {
|
||||
handlers[msg] = handler;
|
||||
}
|
||||
|
||||
void register_predicate_handler(const std::function<bool(UINT msg)>& predicate, const event_handler_t& handler) {
|
||||
predicate_handlers.push_back({predicate, handler});
|
||||
}
|
||||
|
||||
LRESULT handle_event(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
|
||||
const auto it = handlers.find(msg);
|
||||
if (it != handlers.end()) { return it->second(hwnd, msg, w_param, l_param); }
|
||||
for (const auto& [predicate, handler]: predicate_handlers) {
|
||||
if (predicate(msg)) { return handler(hwnd, msg, w_param, l_param); }
|
||||
}
|
||||
return DefWindowProc(hwnd, msg, w_param, l_param);
|
||||
}
|
||||
private:
|
||||
std::map<UINT, event_handler_t> handlers;
|
||||
std::vector<std::pair<std::function<bool(UINT msg)>, event_handler_t>> predicate_handlers;
|
||||
};
|
||||
@@ -13,7 +13,7 @@
|
||||
bool windows_mirage_render_context::init() {
|
||||
try {
|
||||
// 定义支持的特性级别(从高到低排序)
|
||||
D3D_FEATURE_LEVEL feature_levels[] = {
|
||||
constexpr D3D_FEATURE_LEVEL feature_levels[] = {
|
||||
D3D_FEATURE_LEVEL_11_1,
|
||||
D3D_FEATURE_LEVEL_11_0,
|
||||
D3D_FEATURE_LEVEL_10_1,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "geometry/margin.h"
|
||||
#include "widget/panel_widget/mbox.h"
|
||||
#include "window/mwindow.h"
|
||||
|
||||
mbutton::mbutton() {
|
||||
slot_.h_alignment(horizontal_alignment_t::center);
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
#include "meditable_text_box.h"
|
||||
|
||||
#include "font/font_system.h"
|
||||
|
||||
void meditable_text_box::init() {
|
||||
mleaf_widget<editable_text_box_args>::init();
|
||||
set_visibility(visibility_t::visible);
|
||||
}
|
||||
|
||||
void meditable_text_box::setup_widget(const editable_text_box_args& in_args) {
|
||||
text_ = in_args.text();
|
||||
font_ = in_args.font();
|
||||
font_size_ = in_args.font_size();
|
||||
line_spacing_ = in_args.line_spacing();
|
||||
warp_text_ = in_args.warp_text();
|
||||
update_no_wrap_size();
|
||||
}
|
||||
|
||||
void meditable_text_box::on_paint(mirage_paint_context& in_context) {
|
||||
// 绘制底
|
||||
in_context.drawer().make_rounded_rect(
|
||||
{0, 0},
|
||||
in_context.geo().get_local_size(),
|
||||
in_context.geo(),
|
||||
draw_effect::none,
|
||||
{ { 0.1, 0.1, 0.1, 1 } },
|
||||
{5}
|
||||
);
|
||||
// 绘制文本
|
||||
in_context.drawer().make_text(
|
||||
layout_,
|
||||
{0, 0},
|
||||
in_context.geo().get_local_size(),
|
||||
in_context.geo()
|
||||
);
|
||||
}
|
||||
|
||||
auto meditable_text_box::compute_desired_size(float in_layout_scale_multiplier) const -> Eigen::Vector2f {
|
||||
return no_warp_size_.cwiseMax(layout_.total_size);
|
||||
}
|
||||
|
||||
void meditable_text_box::arrange_children(const geometry_t& in_allotted_geometry) {
|
||||
mleaf_widget<editable_text_box_args>::arrange_children(in_allotted_geometry);
|
||||
if (warp_text_) {
|
||||
const auto current_width = in_allotted_geometry.get_local_size().x();
|
||||
if (current_width != layout_.total_size.x()) {
|
||||
layout_ = font_manager::instance().layout_text(text_, font_, font_size_, current_width, line_spacing_);
|
||||
invalidate(invalidate_reason::layout);
|
||||
}
|
||||
} else {
|
||||
layout_ = font_manager::instance().layout_text(text_, font_, font_size_, 0, line_spacing_);
|
||||
invalidate(invalidate_reason::layout);
|
||||
}
|
||||
}
|
||||
|
||||
hit_test_handle meditable_text_box::on_mouse_button_down(const Eigen::Vector2f& in_position, mouse_button in_button) {
|
||||
std::shared_ptr<meditable_text_box> p = std::static_pointer_cast<meditable_text_box>(shared_from_this());
|
||||
ime::get().register_processor(p);
|
||||
return hit_test_handle::handled();
|
||||
}
|
||||
|
||||
void meditable_text_box::update_no_wrap_size() {
|
||||
no_warp_size_ = font_manager::instance().layout_text(text_, font_, font_size_, 0, line_spacing_).total_size;
|
||||
invalidate(invalidate_reason::layout);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
#include "ime/ime.h"
|
||||
#include "widget/mleaf_widget.h"
|
||||
|
||||
class font_face_interface;
|
||||
|
||||
struct editable_text_box_args {
|
||||
WARG(std::u32string, text, {})
|
||||
WARG(std::shared_ptr<font_face_interface>, font, {})
|
||||
WARG(float, font_size, 24.f)
|
||||
WARG(float, line_spacing, 1.2f)
|
||||
WARG(bool, warp_text, false)
|
||||
};
|
||||
|
||||
class meditable_text_box : public mleaf_widget<editable_text_box_args>, public ime_processor {
|
||||
public:
|
||||
virtual void init() override;
|
||||
virtual void setup_widget(const editable_text_box_args& in_args) override;
|
||||
virtual void on_paint(mirage_paint_context& in_context) override;
|
||||
virtual auto compute_desired_size(float in_layout_scale_multiplier) const -> Eigen::Vector2f override;
|
||||
virtual void arrange_children(const geometry_t& in_allotted_geometry) override;
|
||||
|
||||
virtual hit_test_handle on_mouse_button_down(const Eigen::Vector2f& in_position, mouse_button in_button) override;
|
||||
|
||||
const auto& get_text() const { return text_; }
|
||||
|
||||
virtual void process_ime_char(char32_t c) override {
|
||||
text_ += c;
|
||||
}
|
||||
virtual void process_ime_backspace() override {
|
||||
if (!text_.empty()) {
|
||||
text_.pop_back();
|
||||
update_no_wrap_size();
|
||||
}
|
||||
}
|
||||
virtual void process_ime_text(const std::u32string& text) override {
|
||||
text_ += text;
|
||||
update_no_wrap_size();
|
||||
}
|
||||
virtual void process_ime_clear() override {
|
||||
update_no_wrap_size();
|
||||
}
|
||||
virtual std::optional<Eigen::Vector2i> get_ime_cursor_pos() const override {
|
||||
return (geometry_.get_window_position() + no_warp_size_).cast<int32_t>();
|
||||
}
|
||||
|
||||
void update_no_wrap_size();
|
||||
private:
|
||||
std::u32string text_;
|
||||
text_layout_t layout_{};
|
||||
Eigen::Vector2f no_warp_size_{};
|
||||
float font_size_ = .0f;
|
||||
float line_spacing_ = 1.f;
|
||||
bool warp_text_ = false;
|
||||
std::shared_ptr<font_face_interface> font_;
|
||||
};
|
||||
@@ -45,5 +45,8 @@ void mtext_block::arrange_children(const geometry_t& in_allotted_geometry) {
|
||||
|
||||
void mtext_block::update_no_wrap_size() {
|
||||
no_warp_size_ = font_manager::instance().layout_text(text_, font_, font_size_, 0, line_spacing_).total_size;
|
||||
// 如果没有任何内容, 将大小设置为0
|
||||
if (no_warp_size_.x() == 0)
|
||||
no_warp_size_.y() = 0;
|
||||
invalidate(invalidate_reason::layout);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ struct mwindow_make_shared_enabler : public mwindow {
|
||||
// mwindow 类实现
|
||||
//==============================================================================
|
||||
|
||||
auto mwindow::get_platform_handle() -> void* {
|
||||
return pimpl_->get_platform_window()->get_window_handle();
|
||||
}
|
||||
|
||||
std::shared_ptr<mwindow> mwindow::create(const Eigen::Vector2i& in_size, const std::wstring& in_title) {
|
||||
auto window = std::make_shared<mwindow_make_shared_enabler>(in_size, in_title);
|
||||
windows_.push_back(window);
|
||||
@@ -109,19 +113,19 @@ Eigen::Vector2f mwindow::compute_desired_size(float scale_multiplier) const {
|
||||
}
|
||||
|
||||
void mwindow::arrange_children(const geometry_t& allotted_geometry) {
|
||||
auto content = get_content();
|
||||
if (content) {
|
||||
auto child_geo = allotted_geometry.make_child(
|
||||
{0, 0},
|
||||
get_platform_window()->get_window_frame_size().cast<float>()
|
||||
);
|
||||
content->set_geometry(child_geo);
|
||||
}
|
||||
const auto content = get_content();
|
||||
if (!content)
|
||||
return;
|
||||
|
||||
const auto child_geo = allotted_geometry.make_child(
|
||||
{0, 0},
|
||||
get_platform_window()->get_window_frame_size().cast<float>()
|
||||
);
|
||||
content->set_geometry(child_geo);
|
||||
}
|
||||
|
||||
void mwindow::on_paint(mirage_paint_context& context) {
|
||||
auto content = get_content();
|
||||
if (content) {
|
||||
if (const auto content = get_content()) {
|
||||
content->on_paint(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,6 +171,12 @@ public:
|
||||
*/
|
||||
[[nodiscard]] auto get_window() -> mwindow* override { return this; }
|
||||
|
||||
/**
|
||||
* @brief 获取窗口平台指针
|
||||
* @return
|
||||
*/
|
||||
[[nodiscard]] auto get_platform_handle() -> void*;
|
||||
|
||||
//-------------- 事件通知 --------------
|
||||
/** 窗口关闭事件委托 */
|
||||
multicast_delegate<mwindow*> on_close_delegate;
|
||||
|
||||
@@ -46,6 +46,7 @@ public:
|
||||
|
||||
// 设置窗口事件回调
|
||||
platform_window_->on_resize_delegate.add_raw(this, &impl::on_resize);
|
||||
platform_window_->on_request_redraw.add_raw(this, &impl::on_request_redraw);
|
||||
platform_window_->on_mouse_move_delegate.add_raw(this, &impl::process_mouse_move);
|
||||
platform_window_->on_mouse_button_down_delegate.add_raw(this, &impl::process_mouse_button_down);
|
||||
platform_window_->on_mouse_button_up_delegate.add_raw(this, &impl::process_mouse_button_up);
|
||||
@@ -55,9 +56,8 @@ public:
|
||||
|
||||
// 创建窗口渲染状态
|
||||
const auto& window_frame_size = platform_window_->get_window_frame_size();
|
||||
window_state_ = mirage_render_context::get()->create_window_state(
|
||||
window_frame_size,
|
||||
platform_window_->get_window_handle());
|
||||
window_state_ = mirage_render_context::get()->create_window_state(window_frame_size,
|
||||
platform_window_->get_window_handle());
|
||||
|
||||
// 初始化渲染上下文
|
||||
paint_context_.init(window_frame_size);
|
||||
@@ -183,7 +183,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<mwidget_interface> get_content() const {
|
||||
[[nodiscard]] std::shared_ptr<mwidget_interface> get_content() const {
|
||||
return content_widget_;
|
||||
}
|
||||
|
||||
@@ -287,6 +287,10 @@ public:
|
||||
rebuild_swapchain();
|
||||
}
|
||||
|
||||
void on_request_redraw() {
|
||||
owner_->invalidate(invalidate_reason::paint);
|
||||
}
|
||||
|
||||
void process_mouse_move(const Eigen::Vector2f& window_pos) {
|
||||
// 更新最后鼠标位置
|
||||
mouse_.position = window_pos;
|
||||
@@ -439,12 +443,12 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
mwindow* owner_;
|
||||
std::unique_ptr<platform_window> platform_window_;
|
||||
mwindow* owner_;
|
||||
std::unique_ptr<platform_window> platform_window_;
|
||||
std::unique_ptr<mirage_window_state> window_state_;
|
||||
std::shared_ptr<mwidget_interface> content_widget_;
|
||||
mirage_paint_context paint_context_;
|
||||
render_state render_state_;
|
||||
mouse_state mouse_;
|
||||
std::shared_ptr<mwidget_interface> content_widget_;
|
||||
mirage_paint_context paint_context_;
|
||||
render_state render_state_;
|
||||
mouse_state mouse_;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user