diff --git a/RimeWithWeasel/RimeWithWeasel.cpp b/RimeWithWeasel/RimeWithWeasel.cpp index c4d3a5e22..4502f6b7c 100644 --- a/RimeWithWeasel/RimeWithWeasel.cpp +++ b/RimeWithWeasel/RimeWithWeasel.cpp @@ -1,4 +1,4 @@ -#include "stdafx.h" +#include "stdafx.h" #include #include #include @@ -268,6 +268,9 @@ BOOL RimeWithWeaselHandler::ProcessKeyEvent(KeyEvent keyEvent, << ", mask = " << keyEvent.mask << ", ipc_id = " << ipc_id; if (m_disabled) return FALSE; + BOOL grid_handled = FALSE; + if (_HandleGridKeyEvent(keyEvent, ipc_id, eat, grid_handled)) + return grid_handled; RimeSessionId session_id = to_session_id(ipc_id); Bool handled = rime_api->process_key(session_id, keyEvent.keycode, expand_ibus_modifier(keyEvent.mask)); @@ -472,6 +475,161 @@ void RimeWithWeaselHandler::_GetCandidateInfo(CandidateInfo& cinfo, cinfo.is_last_page = ctx.menu.is_last_page; } +void RimeWithWeaselHandler::_GetCandidateInfo(CandidateInfo& cinfo, + RimeSessionId session_id, + SessionStatus& session_status) { + RIME_STRUCT(RimeContext, ctx); + if (!rime_api->get_context(session_id, &ctx)) + return; + + if (!session_status.style.grid_layout || !ctx.menu.num_candidates) { + _GetCandidateInfo(cinfo, ctx); + rime_api->free_context(&ctx); + return; + } + + const int columns = session_status.style.grid_columns; + const int visible_rows = session_status.style.grid_visible_rows; + const int visible_count = columns * visible_rows; + const int highlighted = ctx.menu.page_no * ctx.menu.page_size + + ctx.menu.highlighted_candidate_index; + if (session_status.grid_row_offset < 0) + session_status.grid_row_offset = 0; + if (session_status.grid_window_row_offset < 0) + session_status.grid_window_row_offset = 0; + if (highlighted < session_status.grid_window_row_offset * columns || + highlighted >= + (session_status.grid_window_row_offset + visible_rows) * columns) { + session_status.grid_window_row_offset = max(0, highlighted / columns); + session_status.grid_row_offset = session_status.grid_window_row_offset; + } + const int start = session_status.grid_window_row_offset * columns; + + RimeCandidateListIterator it = {0}; + if (!rime_api->candidate_list_from_index(session_id, &it, start)) { + _GetCandidateInfo(cinfo, ctx); + rime_api->free_context(&ctx); + return; + } + + int count = 0; + while (count < visible_count && rime_api->candidate_list_next(&it)) { + Text candidate; + candidate.str = escape_string(u8tow(it.candidate.text)); + cinfo.candies.push_back(candidate); + + Text comment; + if (it.candidate.comment) + comment.str = escape_string(u8tow(it.candidate.comment)); + cinfo.comments.push_back(comment); + + Text label; + label.str = std::to_wstring((count % columns) + 1); + cinfo.labels.push_back(label); + ++count; + } + rime_api->candidate_list_end(&it); + + cinfo.highlighted = highlighted >= start && highlighted < start + count + ? highlighted - start + : 0; + cinfo.currentPage = session_status.grid_window_row_offset; + cinfo.is_last_page = count < visible_count; + rime_api->free_context(&ctx); +} + +bool RimeWithWeaselHandler::_HandleGridKeyEvent(KeyEvent keyEvent, + WeaselSessionId ipc_id, + EatLine eat, + BOOL& handled) { + if ((keyEvent.mask & ibus::Modifier::RELEASE_MASK) || !ipc_id) + return false; + SessionStatus& session_status = get_session_status(ipc_id); + if (!session_status.style.grid_layout) + return false; + + RimeSessionId session_id = session_status.session_id; + RIME_STRUCT(RimeContext, ctx); + if (!rime_api->get_context(session_id, &ctx)) + return false; + const bool has_candidates = ctx.menu.num_candidates > 0; + rime_api->free_context(&ctx); + if (!has_candidates) { + session_status.grid_row_offset = 0; + session_status.grid_window_row_offset = 0; + return false; + } + + const int columns = session_status.style.grid_columns; + const int visible_rows = session_status.style.grid_visible_rows; + const bool no_command_modifier = + (keyEvent.mask & + (ibus::Modifier::CONTROL_MASK | ibus::Modifier::ALT_MASK | + ibus::Modifier::META_MASK | ibus::Modifier::SUPER_MASK | + ibus::Modifier::HYPER_MASK)) == 0; + const bool plain = + no_command_modifier && !(keyEvent.mask & ibus::Modifier::SHIFT_MASK); + + if (no_command_modifier && + (keyEvent.keycode == '+' || keyEvent.keycode == '=' || + keyEvent.keycode == ibus::KP_Add)) { + const int next_row_offset = session_status.grid_row_offset + 1; + RimeCandidateListIterator it = {0}; + bool has_next_row = rime_api->candidate_list_from_index( + session_id, &it, next_row_offset * columns) && + rime_api->candidate_list_next(&it); + rime_api->candidate_list_end(&it); + if (has_next_row) { + session_status.grid_row_offset = next_row_offset; + if (session_status.grid_row_offset >= + session_status.grid_window_row_offset + visible_rows) { + session_status.grid_window_row_offset = + session_status.grid_row_offset - visible_rows + 1; + } + } + rime_api->highlight_candidate(session_id, + session_status.grid_row_offset * columns); + _Respond(ipc_id, eat); + _UpdateUI(ipc_id); + m_active_session = ipc_id; + handled = TRUE; + return true; + } + + if (plain && + (keyEvent.keycode == '-' || keyEvent.keycode == ibus::KP_Subtract)) { + if (session_status.grid_row_offset > 0) + --session_status.grid_row_offset; + if (session_status.grid_row_offset < session_status.grid_window_row_offset) + session_status.grid_window_row_offset = session_status.grid_row_offset; + rime_api->highlight_candidate(session_id, + session_status.grid_row_offset * columns); + _Respond(ipc_id, eat); + _UpdateUI(ipc_id); + m_active_session = ipc_id; + handled = TRUE; + return true; + } + + int digit = 0; + if (keyEvent.keycode >= '1' && keyEvent.keycode <= '9') + digit = keyEvent.keycode - '0'; + else if (keyEvent.keycode >= ibus::KP_1 && keyEvent.keycode <= ibus::KP_9) + digit = keyEvent.keycode - ibus::KP_0; + + if (plain && digit >= 1 && digit <= columns) { + const size_t index = + (size_t)(session_status.grid_row_offset * columns + digit - 1); + handled = (BOOL)rime_api->select_candidate(session_id, index); + _Respond(ipc_id, eat); + _UpdateUI(ipc_id); + m_active_session = ipc_id; + return true; + } + + return false; +} + void RimeWithWeaselHandler::StartMaintenance() { m_session_status_map.clear(); Finalize(); @@ -789,7 +947,7 @@ bool RimeWithWeaselHandler::_Respond(WeaselSessionId ipc_id, EatLine eat) { bool has_candidates = ctx.menu.num_candidates > 0; CandidateInfo cinfo; if (has_candidates) { - _GetCandidateInfo(cinfo, ctx); + _GetCandidateInfo(cinfo, session_id, session_status); } if (is_composing) { const auto& preedit = ctx.composition.preedit; @@ -1282,6 +1440,19 @@ static void _UpdateUIStyle(RimeConfig* config, UI* ui, bool initialize) { _RimeGetIntStr(config, "style/layout/spacing", style.spacing, 0, 0, _abs); _RimeGetIntStr(config, "style/layout/candidate_spacing", style.candidate_spacing, 0, 0, _abs); + _RimeGetBool(config, "style/layout/grid", false, style.grid_layout); + _RimeGetIntStr(config, "style/layout/grid_columns", style.grid_columns, 0, 0, + _abs); + if (style.grid_columns <= 0) + style.grid_columns = UIStyle::DEFAULT_GRID_COLUMNS; + _RimeGetIntStr(config, "style/layout/grid_visible_rows", + style.grid_visible_rows, 0, 0, _abs); + if (style.grid_visible_rows <= 0) + style.grid_visible_rows = UIStyle::DEFAULT_GRID_VISIBLE_ROWS; + _RimeGetIntStr(config, "style/layout/grid_cell_width", style.grid_cell_width, + 0, 0, _abs); + _RimeGetIntStr(config, "style/layout/grid_cell_height", + style.grid_cell_height, 0, 0, _abs); _RimeGetIntStr(config, "style/layout/hilite_spacing", style.hilite_spacing, 0, 0, _abs); _RimeGetIntStr(config, "style/layout/hilite_padding_x", @@ -1492,7 +1663,8 @@ void RimeWithWeaselHandler::_GetContext(Context& weasel_context, } if (ctx.menu.num_candidates) { CandidateInfo& cinfo(weasel_context.cinfo); - _GetCandidateInfo(cinfo, ctx); + SessionStatus& session_status = get_session_status(m_active_session); + _GetCandidateInfo(cinfo, session_id, session_status); } rime_api->free_context(&ctx); } diff --git a/WeaselUI/HorizontalLayout.cpp b/WeaselUI/HorizontalLayout.cpp index 99d8e64cb..fb1616334 100644 --- a/WeaselUI/HorizontalLayout.cpp +++ b/WeaselUI/HorizontalLayout.cpp @@ -75,6 +75,127 @@ void HorizontalLayout::DoLayout(CDCHandle dc, PDWR pDWR) { width = max(width, real_margin_x * 2 + size.cx); } + if (_style.grid_layout && candidates_count) { + int columns = min(max(_style.grid_columns, 1), MAX_CANDIDATES_COUNT); + int rows = (candidates_count + columns - 1) / columns; + int cell_width = _style.grid_cell_width; + int cell_height = _style.grid_cell_height; + + for (auto i = 0; i < candidates_count && i < MAX_CANDIDATES_COUNT; ++i) { + int natural_width = 0; + int natural_height = 0; + + std::wstring label = + GetLabelText(labels, i, _style.label_text_format.c_str()); + GetTextSizeDW(label, label.length(), pDWR->pLabelTextFormat, pDWR, &size); + _candidateLabelRects[i].SetRect(0, 0, size.cx * labelFontValid, size.cy); + natural_width += size.cx * labelFontValid; + natural_height = max(natural_height, size.cy); + + const std::wstring& text = candidates.at(i).str; + GetTextSizeDW(text, text.length(), pDWR->pTextFormat, pDWR, &size); + _candidateTextRects[i].SetRect(0, 0, size.cx * textFontValid, size.cy); + natural_width += (size.cx + _style.hilite_spacing) * textFontValid; + natural_height = max(natural_height, size.cy); + + bool cmtFontNotTrans = + (i == id && (_style.hilited_comment_text_color & 0xff000000)) || + (i != id && (_style.comment_text_color & 0xff000000)); + if (!comments.at(i).str.empty() && cmtFontValid && cmtFontNotTrans) { + const std::wstring& comment = comments.at(i).str; + GetTextSizeDW(comment, comment.length(), pDWR->pCommentTextFormat, pDWR, + &size); + _candidateCommentRects[i].SetRect(0, 0, size.cx * cmtFontValid, + size.cy); + natural_width += (size.cx + _style.hilite_spacing) * cmtFontValid; + natural_height = max(natural_height, size.cy); + } else { + _candidateCommentRects[i].SetRect(0, 0, 0, natural_height); + } + + if (i == id) + natural_width += base_offset; + cell_width = max(cell_width, natural_width); + cell_height = max(cell_height, natural_height); + } + + cell_width = max(cell_width, 1); + cell_height = max(cell_height, 1); + + for (auto i = 0; i < candidates_count && i < MAX_CANDIDATES_COUNT; ++i) { + int col = i % columns; + int row = i / columns; + int cell_left = offsetX + real_margin_x + + col * (cell_width + _style.candidate_spacing); + int cell_top = height + row * (cell_height + _style.candidate_spacing); + int x = cell_left + (i == id ? base_offset : 0); + + _candidateLabelRects[i].OffsetRect(x, cell_top); + x = _candidateLabelRects[i].right + _style.hilite_spacing; + _candidateTextRects[i].OffsetRect(x, cell_top); + x = _candidateTextRects[i].right; + if (!_candidateCommentRects[i].IsRectEmpty()) { + x += _style.hilite_spacing; + _candidateCommentRects[i].OffsetRect(x, cell_top); + } else { + _candidateCommentRects[i].OffsetRect(x, cell_top); + } + + int ol = 0, ot = 0, oc = 0; + if (_style.align_type == UIStyle::ALIGN_CENTER) { + ol = (cell_height - _candidateLabelRects[i].Height()) / 2; + ot = (cell_height - _candidateTextRects[i].Height()) / 2; + oc = (cell_height - _candidateCommentRects[i].Height()) / 2; + } else if (_style.align_type == UIStyle::ALIGN_BOTTOM) { + ol = cell_height - _candidateLabelRects[i].Height(); + ot = cell_height - _candidateTextRects[i].Height(); + oc = cell_height - _candidateCommentRects[i].Height(); + } + _candidateLabelRects[i].OffsetRect(0, ol); + _candidateTextRects[i].OffsetRect(0, ot); + _candidateCommentRects[i].OffsetRect(0, oc); + + _candidateRects[i].SetRect(cell_left, cell_top, cell_left + cell_width, + cell_top + cell_height); + } + + width = max(width, offsetX + real_margin_x + columns * cell_width + + max(0, columns - 1) * _style.candidate_spacing); + height += rows * cell_height + max(0, rows - 1) * _style.candidate_spacing - + offsetY; + width += real_margin_x; + height += real_margin_y; + + width = max(width, _style.min_width); + height = max(height, _style.min_height); + + _highlightRect = _candidateRects[id]; + UpdateStatusIconLayout(&width, &height); + _contentSize.SetSize(width + offsetX, height + 2 * offsetY); + _contentRect.SetRect(0, 0, _contentSize.cx, _contentSize.cy); + + if (page_en && !_style.inline_preedit) { + int _prex = _contentSize.cx - offsetX - real_margin_x + + _style.hilite_padding_x - pgw; + int _prey = (_preeditRect.top + _preeditRect.bottom) / 2 - pgszl.cy / 2; + _prePageRect.SetRect(_prex, _prey, _prex + pgszl.cx, _prey + pgszl.cy); + _nextPageRect.SetRect( + _prePageRect.right + _style.hilite_spacing, _prey, + _prePageRect.right + _style.hilite_spacing + pgszr.cx, + _prey + pgszr.cy); + if (ShouldDisplayStatusIcon()) { + _prePageRect.OffsetRect(-STATUS_ICON_SIZE, 0); + _nextPageRect.OffsetRect(-STATUS_ICON_SIZE, 0); + } + } + + CopyRect(_bgRect, _contentRect); + _bgRect.DeflateRect(offsetX + 1, offsetY + 1); + _PrepareRoundInfo(dc); + _contentRect.DeflateRect(offsetX, offsetY); + return; + } + int row_cnt = 0; int max_width_of_rows = 0; int height_of_rows[MAX_CANDIDATES_COUNT] = {0}; // height of every row diff --git a/WeaselUI/Layout.cpp b/WeaselUI/Layout.cpp index 60b60b4d7..c43e32f83 100644 --- a/WeaselUI/Layout.cpp +++ b/WeaselUI/Layout.cpp @@ -28,6 +28,8 @@ Layout::Layout(const UIStyle& style, _style.margin_y = (int)(_style.margin_y * scale); _style.spacing = (int)(_style.spacing * scale); _style.candidate_spacing = (int)(_style.candidate_spacing * scale); + _style.grid_cell_width = (int)(_style.grid_cell_width * scale); + _style.grid_cell_height = (int)(_style.grid_cell_height * scale); _style.hilite_spacing = (int)(_style.hilite_spacing * scale); _style.hilite_padding_x = (int)(_style.hilite_padding_x * scale); _style.hilite_padding_y = (int)(_style.hilite_padding_y * scale); diff --git a/include/RimeWithWeasel.h b/include/RimeWithWeasel.h index ab20d8d76..141bb781e 100644 --- a/include/RimeWithWeasel.h +++ b/include/RimeWithWeasel.h @@ -23,13 +23,20 @@ typedef std::map AppOptionsByAppName; struct SessionStatus { - SessionStatus() : style(weasel::UIStyle()), __synced(false), session_id(0) { + SessionStatus() + : style(weasel::UIStyle()), + __synced(false), + session_id(0), + grid_row_offset(0), + grid_window_row_offset(0) { RIME_STRUCT(RimeStatus, status); } weasel::UIStyle style; RimeStatus status; bool __synced; RimeSessionId session_id; + int grid_row_offset; + int grid_window_row_offset; }; typedef std::map SessionStatusMap; typedef DWORD WeaselSessionId; @@ -77,6 +84,13 @@ class RimeWithWeaselHandler : public weasel::RequestHandler { bool _Respond(WeaselSessionId ipc_id, EatLine eat); void _ReadClientInfo(WeaselSessionId ipc_id, LPWSTR buffer); void _GetCandidateInfo(weasel::CandidateInfo& cinfo, RimeContext& ctx); + void _GetCandidateInfo(weasel::CandidateInfo& cinfo, + RimeSessionId session_id, + SessionStatus& session_status); + bool _HandleGridKeyEvent(weasel::KeyEvent keyEvent, + WeaselSessionId ipc_id, + EatLine eat, + BOOL& handled); void _GetStatus(weasel::Status& stat, WeaselSessionId ipc_id, weasel::Context& ctx); diff --git a/include/WeaselIPCData.h b/include/WeaselIPCData.h index 09f22f91c..b276cda2d 100644 --- a/include/WeaselIPCData.h +++ b/include/WeaselIPCData.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include @@ -193,6 +193,9 @@ struct Config { }; struct UIStyle { + static constexpr int DEFAULT_GRID_COLUMNS = 5; + static constexpr int DEFAULT_GRID_VISIBLE_ROWS = 5; + enum AntiAliasMode { DEFAULT = 0, CLEARTYPE = 1, @@ -255,6 +258,11 @@ struct UIStyle { int margin_y; int spacing; int candidate_spacing; + bool grid_layout; + int grid_columns; + int grid_visible_rows; + int grid_cell_width; + int grid_cell_height; int hilite_spacing; int hilite_padding_x; int hilite_padding_y; @@ -328,6 +336,11 @@ struct UIStyle { margin_y(0), spacing(0), candidate_spacing(0), + grid_layout(false), + grid_columns(DEFAULT_GRID_COLUMNS), + grid_visible_rows(DEFAULT_GRID_VISIBLE_ROWS), + grid_cell_width(0), + grid_cell_height(0), hilite_spacing(0), hilite_padding_x(0), hilite_padding_y(0), @@ -390,6 +403,10 @@ struct UIStyle { border != st.border || margin_x != st.margin_x || margin_y != st.margin_y || spacing != st.spacing || candidate_spacing != st.candidate_spacing || + grid_layout != st.grid_layout || grid_columns != st.grid_columns || + grid_visible_rows != st.grid_visible_rows || + grid_cell_width != st.grid_cell_width || + grid_cell_height != st.grid_cell_height || hilite_spacing != st.hilite_spacing || hilite_padding_x != st.hilite_padding_x || hilite_padding_y != st.hilite_padding_y || @@ -464,6 +481,11 @@ void serialize(Archive& ar, weasel::UIStyle& s, const unsigned int version) { ar & s.margin_y; ar & s.spacing; ar & s.candidate_spacing; + ar & s.grid_layout; + ar & s.grid_columns; + ar & s.grid_visible_rows; + ar & s.grid_cell_width; + ar & s.grid_cell_height; ar & s.hilite_spacing; ar & s.hilite_padding_x; ar & s.hilite_padding_y;