diff --git a/.clang-format1 b/.clang-format similarity index 81% rename from .clang-format1 rename to .clang-format index 2ffaf98..4d90349 100644 --- a/.clang-format1 +++ b/.clang-format @@ -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: '^".*' diff --git a/example/src/main.cpp b/example/src/main.cpp index 1df662b..04eae67 100644 --- a/example/src/main.cpp +++ b/example/src/main.cpp @@ -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) ] ); diff --git a/src/mirage_render/CMakeLists.txt b/src/mirage_render/CMakeLists.txt index 4492f91..afbb196 100644 --- a/src/mirage_render/CMakeLists.txt +++ b/src/mirage_render/CMakeLists.txt @@ -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 () diff --git a/src/mirage_render/src/font/font_system.cpp b/src/mirage_render/src/font/font_system.cpp index 67525a1..f5b08f0 100644 --- a/src/mirage_render/src/font/font_system.cpp +++ b/src/mirage_render/src/font/font_system.cpp @@ -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 }; diff --git a/src/mirage_render/src/ime/ime.cpp b/src/mirage_render/src/ime/ime.cpp new file mode 100644 index 0000000..a3ca73b --- /dev/null +++ b/src/mirage_render/src/ime/ime.cpp @@ -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 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& processor) { + ime_processors_.push_back(processor); +} + +void ime::unregister_processor(std::weak_ptr processor) { + auto pred = [&processor](const std::weak_ptr& p) { + return p.lock() == processor.lock(); + }; + std::erase_if(ime_processors_, pred); +} diff --git a/src/mirage_render/src/ime/ime.h b/src/mirage_render/src/ime/ime.h new file mode 100644 index 0000000..76278d2 --- /dev/null +++ b/src/mirage_render/src/ime/ime.h @@ -0,0 +1,37 @@ +#pragma once +#include +#include + +#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 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 get_cursor_pos() const; + + bool has_processors() const { return !ime_processors_.empty(); } + void register_processor(const std::weak_ptr& processor); + void unregister_processor(std::weak_ptr processor); +private: + ime() = default; + + std::vector> ime_processors_; +}; diff --git a/src/mirage_render/src/platform_window/platform_window.h b/src/mirage_render/src/platform_window/platform_window.h index 2ed200a..34c73c2 100644 --- a/src/mirage_render/src/platform_window/platform_window.h +++ b/src/mirage_render/src/platform_window/platform_window.h @@ -37,6 +37,11 @@ public: */ bool is_visible() const; + /** + * @brief 请求重新绘制 + */ + void request_redraw() { on_request_redraw.broadcast(); } + //-------------- 窗口位置和大小 -------------- /** @@ -228,6 +233,7 @@ public: multicast_delegate on_move_delegate; multicast_delegate on_resize_delegate; + multicast_delegate<> on_request_redraw; multicast_delegate on_close_delegate; multicast_delegate on_mouse_move_delegate; @@ -236,6 +242,8 @@ public: multicast_delegate on_mouse_button_dbl_delegate; multicast_delegate on_mouse_wheel_delegate; multicast_delegate<> on_mouse_leave_delegate; + + inline static std::vector windows; private: /** 原生窗口句柄 */ void* window_handle_{}; diff --git a/src/mirage_render/src/platform_window/windows/windows_window.cpp b/src/mirage_render/src/platform_window/windows/windows_window.cpp index a3ab8a3..326de8d 100644 --- a/src/mirage_render/src/platform_window/windows/windows_window.cpp +++ b/src/mirage_render/src/platform_window/windows/windows_window.cpp @@ -5,124 +5,16 @@ #include #include +#include "utf8.h" +#include "ime/ime.h" +#include "windows_window_event.h" + #define WINDOW_HANDLE static_cast(window_handle_) -bool mouse_tracking_ = FALSE; -std::vector 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(x), static_cast(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(x), static_cast(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(screen_point.x), static_cast(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(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(hwnd); if (!window_handle_) { std::cerr << "Failed to create window" << std::endl; diff --git a/src/mirage_render/src/platform_window/windows/windows_window_event.cpp b/src/mirage_render/src/platform_window/windows/windows_window_event.cpp new file mode 100644 index 0000000..69dc2ad --- /dev/null +++ b/src/mirage_render/src/platform_window/windows/windows_window_event.cpp @@ -0,0 +1,409 @@ +#include "windows_window_event.h" + +#include +#include + +#include "ime/ime.h" +#include "platform_window/platform_window.h" + +// 默认处理过程宏,用于未处理的消息传递给系统默认处理 +#define DEFAULT_PROCESS DefWindowProc(hwnd, msg, w_param, l_param) + +// 鼠标追踪状态映射表,记录每个窗口是否正在追踪鼠标 +std::map 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(x), static_cast(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(x), static_cast(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(screen_point.x), + static_cast(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(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(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); +} diff --git a/src/mirage_render/src/platform_window/windows/windows_window_event.h b/src/mirage_render/src/platform_window/windows/windows_window_event.h new file mode 100644 index 0000000..bbd8ce8 --- /dev/null +++ b/src/mirage_render/src/platform_window/windows/windows_window_event.h @@ -0,0 +1,31 @@ +#pragma once +#include +#include +#include + +class windows_window_event_handler { +public: + using event_handler_t = std::function; + + 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& 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 handlers; + std::vector, event_handler_t>> predicate_handlers; +}; diff --git a/src/mirage_render/src/render/windows/windows_render_context.cpp b/src/mirage_render/src/render/windows/windows_render_context.cpp index 31f2d21..30de340 100644 --- a/src/mirage_render/src/render/windows/windows_render_context.cpp +++ b/src/mirage_render/src/render/windows/windows_render_context.cpp @@ -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, diff --git a/src/mirage_widget/src/widget/compound_widget/mbutton.cpp b/src/mirage_widget/src/widget/compound_widget/mbutton.cpp index c52d10f..f14800f 100644 --- a/src/mirage_widget/src/widget/compound_widget/mbutton.cpp +++ b/src/mirage_widget/src/widget/compound_widget/mbutton.cpp @@ -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); diff --git a/src/mirage_widget/src/widget/leaf_widget/meditable_text_box.cpp b/src/mirage_widget/src/widget/leaf_widget/meditable_text_box.cpp new file mode 100644 index 0000000..2a57606 --- /dev/null +++ b/src/mirage_widget/src/widget/leaf_widget/meditable_text_box.cpp @@ -0,0 +1,65 @@ +#include "meditable_text_box.h" + +#include "font/font_system.h" + +void meditable_text_box::init() { + mleaf_widget::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::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 p = std::static_pointer_cast(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); +} diff --git a/src/mirage_widget/src/widget/leaf_widget/meditable_text_box.h b/src/mirage_widget/src/widget/leaf_widget/meditable_text_box.h new file mode 100644 index 0000000..18b817d --- /dev/null +++ b/src/mirage_widget/src/widget/leaf_widget/meditable_text_box.h @@ -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, {}) + WARG(float, font_size, 24.f) + WARG(float, line_spacing, 1.2f) + WARG(bool, warp_text, false) +}; + +class meditable_text_box : public mleaf_widget, 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 get_ime_cursor_pos() const override { + return (geometry_.get_window_position() + no_warp_size_).cast(); + } + + 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_; +}; diff --git a/src/mirage_widget/src/widget/leaf_widget/mtext_block.cpp b/src/mirage_widget/src/widget/leaf_widget/mtext_block.cpp index f55001a..1d51b97 100644 --- a/src/mirage_widget/src/widget/leaf_widget/mtext_block.cpp +++ b/src/mirage_widget/src/widget/leaf_widget/mtext_block.cpp @@ -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); } diff --git a/src/mirage_widget/src/window/mwindow.cpp b/src/mirage_widget/src/window/mwindow.cpp index aa138dc..ce4931a 100644 --- a/src/mirage_widget/src/window/mwindow.cpp +++ b/src/mirage_widget/src/window/mwindow.cpp @@ -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::create(const Eigen::Vector2i& in_size, const std::wstring& in_title) { auto window = std::make_shared(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() - ); - 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() + ); + 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); } } diff --git a/src/mirage_widget/src/window/mwindow.h b/src/mirage_widget/src/window/mwindow.h index 5219c91..838686e 100644 --- a/src/mirage_widget/src/window/mwindow.h +++ b/src/mirage_widget/src/window/mwindow.h @@ -171,6 +171,12 @@ public: */ [[nodiscard]] auto get_window() -> mwindow* override { return this; } + /** + * @brief 获取窗口平台指针 + * @return + */ + [[nodiscard]] auto get_platform_handle() -> void*; + //-------------- 事件通知 -------------- /** 窗口关闭事件委托 */ multicast_delegate on_close_delegate; diff --git a/src/mirage_widget/src/window/mwindow_impl.cpp b/src/mirage_widget/src/window/mwindow_impl.cpp index 5d76599..47bdb66 100644 --- a/src/mirage_widget/src/window/mwindow_impl.cpp +++ b/src/mirage_widget/src/window/mwindow_impl.cpp @@ -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 get_content() const { + [[nodiscard]] std::shared_ptr 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_; + mwindow* owner_; + std::unique_ptr platform_window_; std::unique_ptr window_state_; - std::shared_ptr content_widget_; - mirage_paint_context paint_context_; - render_state render_state_; - mouse_state mouse_; + std::shared_ptr content_widget_; + mirage_paint_context paint_context_; + render_state render_state_; + mouse_state mouse_; };