IME支持

This commit is contained in:
daiqingshuang
2025-05-12 18:26:37 +08:00
parent d6487aef83
commit 96189c8a1a
18 changed files with 725 additions and 146 deletions

View File

@@ -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: '^".*'

View File

@@ -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)
]
);

View File

@@ -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 ()

View File

@@ -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 };

View 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);
}

View 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_;
};

View File

@@ -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_{};

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;
};

View File

@@ -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,

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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_;
};

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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_;
};