From 16e1cf3d82ba732d380f624fe966975b7f1c7868 Mon Sep 17 00:00:00 2001 From: fxliang Date: Thu, 5 Feb 2026 22:03:00 +0800 Subject: [PATCH 01/25] refactor(WeaselUI): reuse codes for alignment, mark metrics, pager metrics --- WeaselUI/HorizontalLayout.cpp | 79 ++------------- WeaselUI/StandardLayout.cpp | 147 +++++++++++++++++++++++++++ WeaselUI/StandardLayout.h | 27 +++++ WeaselUI/VHorizontalLayout.cpp | 175 +++++---------------------------- WeaselUI/VerticalLayout.cpp | 70 ++----------- 5 files changed, 214 insertions(+), 284 deletions(-) diff --git a/WeaselUI/HorizontalLayout.cpp b/WeaselUI/HorizontalLayout.cpp index 99d8e64cb..17a31cb18 100644 --- a/WeaselUI/HorizontalLayout.cpp +++ b/WeaselUI/HorizontalLayout.cpp @@ -7,44 +7,12 @@ void HorizontalLayout::DoLayout(CDCHandle dc, PDWR pDWR) { CSize size; int width = offsetX + real_margin_x, height = offsetY + real_margin_y; int w = offsetX + real_margin_x; + MarkMetrics mark = ComputeMarkMetrics(pDWR); + int base_offset = mark.base_offset; - /* calc mark_text sizes */ - if ((_style.hilited_mark_color & 0xff000000)) { - CSize sg; - if (candidates_count) { - if (_style.mark_text.empty()) - GetTextSizeDW(L"|", 1, pDWR->pTextFormat, pDWR, &sg); - else - GetTextSizeDW(_style.mark_text, _style.mark_text.length(), - pDWR->pTextFormat, pDWR, &sg); - } - - mark_width = sg.cx; - mark_height = sg.cy; - if (_style.mark_text.empty()) { - mark_width = mark_height / 7; - if (_style.linespacing && _style.baseline) - mark_width = - (int)((float)mark_width / ((float)_style.linespacing / 100.0f)); - mark_width = max(mark_width, 6); - } - mark_gap = (_style.mark_text.empty()) ? mark_width - : mark_width + _style.hilite_spacing; - } - int base_offset = ((_style.hilited_mark_color & 0xff000000)) ? mark_gap : 0; - - // calc page indicator - CSize pgszl, pgszr; - if (!IsInlinePreedit()) { - GetTextSizeDW(pre, pre.length(), pDWR->pPreeditTextFormat, pDWR, &pgszl); - GetTextSizeDW(next, next.length(), pDWR->pPreeditTextFormat, pDWR, &pgszr); - } - bool page_en = (_style.prevpage_color & 0xff000000) && - (_style.nextpage_color & 0xff000000); - int pgw = page_en ? (pgszl.cx + pgszr.cx + _style.hilite_spacing + - _style.hilite_padding_x * 2) - : 0; - int pgh = page_en ? max(pgszl.cy, pgszr.cy) : 0; + PagerMetrics pager = ComputePagerMetrics(pDWR); + int pgw = pager.pgw; + int pgh = pager.pgh; /* Preedit */ if (!IsInlinePreedit() && !_context.preedit.str.empty()) { @@ -172,28 +140,7 @@ void HorizontalLayout::DoLayout(CDCHandle dc, PDWR pDWR) { _candidateCommentRects[i].right, mintop_of_rows[row_of_candidate[i]] + height_of_rows[row_of_candidate[i]]); - int ol = 0, ot = 0, oc = 0; - if (_style.align_type == UIStyle::ALIGN_CENTER) { - ol = (height_of_rows[row_of_candidate[i]] - - _candidateLabelRects[i].Height()) / - 2; - ot = (height_of_rows[row_of_candidate[i]] - - _candidateTextRects[i].Height()) / - 2; - oc = (height_of_rows[row_of_candidate[i]] - - _candidateCommentRects[i].Height()) / - 2; - } else if (_style.align_type == UIStyle::ALIGN_BOTTOM) { - ol = (height_of_rows[row_of_candidate[i]] - - _candidateLabelRects[i].Height()); - ot = (height_of_rows[row_of_candidate[i]] - - _candidateTextRects[i].Height()); - oc = (height_of_rows[row_of_candidate[i]] - - _candidateCommentRects[i].Height()); - } - _candidateLabelRects[i].OffsetRect(0, ol); - _candidateTextRects[i].OffsetRect(0, ot); - _candidateCommentRects[i].OffsetRect(0, oc); + ReAdjustAlignment(height_of_rows[row_of_candidate[i]], i); } height = mintop_of_rows[row_cnt] + height_of_rows[row_cnt] - offsetY; width = max(width, max_width_of_rows); @@ -224,19 +171,7 @@ void HorizontalLayout::DoLayout(CDCHandle dc, PDWR pDWR) { _contentRect.SetRect(0, 0, _contentSize.cx, _contentSize.cy); // calc page indicator - if (page_en && candidates_count && !_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); - } - } + PlacePagerHorizontal(pager, _contentSize.cx, _contentSize.cy); // prepare temp rect _bgRect for roundinfo calculation CopyRect(_bgRect, _contentRect); diff --git a/WeaselUI/StandardLayout.cpp b/WeaselUI/StandardLayout.cpp index 1855ec4fa..8d8674488 100644 --- a/WeaselUI/StandardLayout.cpp +++ b/WeaselUI/StandardLayout.cpp @@ -3,6 +3,153 @@ using namespace weasel; +StandardLayout::MarkMetrics StandardLayout::ComputeMarkMetrics(PDWR pDWR) { + MarkMetrics m; + if ((_style.hilited_mark_color & 0xff000000)) { + CSize sg; + if (candidates_count) { + if (_style.mark_text.empty()) + GetTextSizeDW(L"|", 1, pDWR->pTextFormat, pDWR, &sg); + else + GetTextSizeDW(_style.mark_text, _style.mark_text.length(), + pDWR->pTextFormat, pDWR, &sg); + } + + m.mark_width = sg.cx; + m.mark_height = sg.cy; + if (_style.mark_text.empty()) { + if (_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT) { + m.mark_height = m.mark_width / 7; + if (_style.linespacing && _style.baseline) + m.mark_height = (int)((float)m.mark_height / + ((float)_style.linespacing / 100.0f)); + m.mark_height = max(m.mark_height, 6); + } else { + m.mark_width = m.mark_height / 7; + if (_style.linespacing && _style.baseline) + m.mark_width = + (int)((float)m.mark_width / ((float)_style.linespacing / 100.0f)); + m.mark_width = max(m.mark_width, 6); + } + } + const int base = (_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT) + ? m.mark_height + : m.mark_width; + m.mark_gap = + (_style.mark_text.empty()) + ? base + : base + (_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT + ? _style.hilite_spacing + : _style.hilite_spacing); + m.base_offset = m.mark_gap; + } + + // keep member values in sync for downstream code + mark_width = m.mark_width; + mark_height = m.mark_height; + mark_gap = m.mark_gap; + return m; +} + +StandardLayout::PagerMetrics StandardLayout::ComputePagerMetrics(PDWR pDWR) { + PagerMetrics pager; + if (!IsInlinePreedit()) { + GetTextSizeDW(pre, pre.length(), pDWR->pPreeditTextFormat, pDWR, + &pager.pgszl); + GetTextSizeDW(next, next.length(), pDWR->pPreeditTextFormat, pDWR, + &pager.pgszr); + } + pager.page_en = (_style.prevpage_color & 0xff000000) && + (_style.nextpage_color & 0xff000000); + if (_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT) { + pager.pgh = pager.page_en + ? pager.pgszl.cy + pager.pgszr.cy + _style.hilite_spacing + + _style.hilite_padding_y * 2 + : 0; + pager.pgw = pager.page_en ? max(pager.pgszl.cx, pager.pgszr.cx) : 0; + } else { + pager.pgw = pager.page_en + ? (pager.pgszl.cx + pager.pgszr.cx + _style.hilite_spacing + + _style.hilite_padding_x * 2) + : 0; + pager.pgh = pager.page_en ? max(pager.pgszl.cy, pager.pgszr.cy) : 0; + } + return pager; +} + +void StandardLayout::PlacePagerHorizontal(const PagerMetrics& pager, + int contentWidth, + int contentHeight) { + if (!pager.page_en || !candidates_count || _style.inline_preedit) + return; + int _prex = contentWidth - offsetX - real_margin_x + _style.hilite_padding_x - + pager.pgw; + int _prey = (_preeditRect.top + _preeditRect.bottom) / 2 - pager.pgszl.cy / 2; + _prePageRect.SetRect(_prex, _prey, _prex + pager.pgszl.cx, + _prey + pager.pgszl.cy); + _nextPageRect.SetRect( + _prePageRect.right + _style.hilite_spacing, _prey, + _prePageRect.right + _style.hilite_spacing + pager.pgszr.cx, + _prey + pager.pgszr.cy); + if (ShouldDisplayStatusIcon()) { + _prePageRect.OffsetRect(-STATUS_ICON_SIZE, 0); + _nextPageRect.OffsetRect(-STATUS_ICON_SIZE, 0); + } +} + +void StandardLayout::PlacePagerVerticalText(const PagerMetrics& pager, + int contentWidth, + int contentHeight) { + if (!pager.page_en || !candidates_count || _style.inline_preedit) + return; + int _prey = contentHeight - offsetY - real_margin_y + + _style.hilite_padding_y - pager.pgh; + int _prex = (_preeditRect.left + _preeditRect.right) / 2 - pager.pgszl.cx / 2; + _prePageRect.SetRect(_prex, _prey, _prex + pager.pgszl.cx, + _prey + pager.pgszl.cy); + _nextPageRect.SetRect( + _prex, _prePageRect.bottom + _style.hilite_spacing, + _prex + pager.pgszr.cx, + _prePageRect.bottom + _style.hilite_spacing + pager.pgszr.cy); + if (ShouldDisplayStatusIcon()) { + _prePageRect.OffsetRect(0, -STATUS_ICON_SIZE); + _nextPageRect.OffsetRect(0, -STATUS_ICON_SIZE); + } +} + +void StandardLayout::ReAdjustAlignment(size_t limit, + int index, + bool alignToEnd, + bool endIsEnd) { + int ol = 0, ot = 0, oc = 0; + bool isVerticalText = (_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT); + size_t textSpan = isVerticalText ? _candidateTextRects[index].Width() + : _candidateTextRects[index].Height(); + size_t labelSpan = isVerticalText ? _candidateLabelRects[index].Width() + : _candidateLabelRects[index].Height(); + size_t commentSpan = isVerticalText ? _candidateCommentRects[index].Width() + : _candidateCommentRects[index].Height(); + + const bool alignCenter = _style.align_type == UIStyle::ALIGN_CENTER; + const bool alignEnd = + ((endIsEnd && _style.align_type == UIStyle::ALIGN_BOTTOM) || alignToEnd); + + if (alignCenter) { + ol = static_cast((limit - labelSpan) / 2); + ot = static_cast((limit - textSpan) / 2); + oc = static_cast((limit - commentSpan) / 2); + } else if (alignEnd) { + ol = static_cast(limit - labelSpan); + ot = static_cast(limit - textSpan); + oc = static_cast(limit - commentSpan); + } + + int factor = isVerticalText ? 1 : 0; + _candidateLabelRects[index].OffsetRect(ol * factor, ol * (1 - factor)); + _candidateTextRects[index].OffsetRect(ot * factor, ot * (1 - factor)); + _candidateCommentRects[index].OffsetRect(oc * factor, oc * (1 - factor)); +} + std::wstring StandardLayout::GetLabelText(const std::vector& labels, int id, const wchar_t* format) const { diff --git a/WeaselUI/StandardLayout.h b/WeaselUI/StandardLayout.h index 23eef3590..cdc23ae2d 100644 --- a/WeaselUI/StandardLayout.h +++ b/WeaselUI/StandardLayout.h @@ -59,6 +59,33 @@ class StandardLayout : public Layout { protected: /* Utility functions */ + struct MarkMetrics { + int mark_width = 0; + int mark_height = 0; + int mark_gap = 0; + int base_offset = 0; + }; + + struct PagerMetrics { + CSize pgszl; + CSize pgszr; + int pgw = 0; + int pgh = 0; + bool page_en = false; + }; + + MarkMetrics ComputeMarkMetrics(PDWR pDWR); + PagerMetrics ComputePagerMetrics(PDWR pDWR); + void PlacePagerHorizontal(const PagerMetrics& pager, + int contentWidth, + int contentHeight); + void PlacePagerVerticalText(const PagerMetrics& pager, + int contentWidth, + int contentHeight); + void ReAdjustAlignment(size_t limit, + int index, + bool alignToEnd = false, + bool endIsEnd = true); CSize GetPreeditSize(CDCHandle dc, const weasel::Text& text, ComPtr pTextFormat = NULL, diff --git a/WeaselUI/VHorizontalLayout.cpp b/WeaselUI/VHorizontalLayout.cpp index 1f571131e..60b031fe3 100644 --- a/WeaselUI/VHorizontalLayout.cpp +++ b/WeaselUI/VHorizontalLayout.cpp @@ -13,43 +13,12 @@ void VHorizontalLayout::DoLayout(CDCHandle dc, PDWR pDWR) { CSize size; int height = offsetY, width = offsetX + real_margin_x; int h = offsetY + real_margin_y; + MarkMetrics mark = ComputeMarkMetrics(pDWR); + int base_offset = mark.base_offset; - if ((_style.hilited_mark_color & 0xff000000)) { - CSize sg; - if (candidates_count) { - if (_style.mark_text.empty()) - GetTextSizeDW(L"|", 1, pDWR->pTextFormat, pDWR, &sg); - else - GetTextSizeDW(_style.mark_text, _style.mark_text.length(), - pDWR->pTextFormat, pDWR, &sg); - } - - mark_width = sg.cx; - mark_height = sg.cy; - if (_style.mark_text.empty()) { - mark_height = mark_width / 7; - if (_style.linespacing && _style.baseline) - mark_height = - (int)((float)mark_height / ((float)_style.linespacing / 100.0f)); - mark_height = max(mark_height, 6); - } - mark_gap = (_style.mark_text.empty()) ? mark_height - : mark_height + _style.hilite_spacing; - } - int base_offset = ((_style.hilited_mark_color & 0xff000000)) ? mark_gap : 0; - - // calc page indicator - CSize pgszl, pgszr; - if (!IsInlinePreedit()) { - GetTextSizeDW(pre, pre.length(), pDWR->pPreeditTextFormat, pDWR, &pgszl); - GetTextSizeDW(next, next.length(), pDWR->pPreeditTextFormat, pDWR, &pgszr); - } - bool page_en = (_style.prevpage_color & 0xff000000) && - (_style.nextpage_color & 0xff000000); - int pgh = page_en ? pgszl.cy + pgszr.cy + _style.hilite_spacing + - _style.hilite_padding_y * 2 - : 0; - int pgw = page_en ? max(pgszl.cx, pgszr.cx) : 0; + PagerMetrics pager = ComputePagerMetrics(pDWR); + int pgw = pager.pgw; + int pgh = pager.pgh; /* Preedit */ if (!IsInlinePreedit() && !_context.preedit.str.empty()) { @@ -139,27 +108,14 @@ void VHorizontalLayout::DoLayout(CDCHandle dc, PDWR pDWR) { // reposition candidates if (candidates_count) { for (auto i = 0; i < candidates_count && i < MAX_CANDIDATES_COUNT; ++i) { - int ol = 0, ot = 0, oc = 0; - if (_style.align_type == UIStyle::ALIGN_CENTER) { - ol = (wids[i] - _candidateLabelRects[i].Width()) / 2; - ot = (wids[i] - _candidateTextRects[i].Width()) / 2; - oc = (wids[i] - _candidateCommentRects[i].Width()) / 2; - } else if ((_style.align_type == UIStyle::ALIGN_BOTTOM) && - _style.vertical_text_left_to_right) { - ol = (wids[i] - _candidateLabelRects[i].Width()); - ot = (wids[i] - _candidateTextRects[i].Width()); - oc = (wids[i] - _candidateCommentRects[i].Width()); - } else if ((_style.align_type == UIStyle::ALIGN_TOP) && - (!_style.vertical_text_left_to_right)) { - ol = (wids[i] - _candidateLabelRects[i].Width()); - ot = (wids[i] - _candidateTextRects[i].Width()); - oc = (wids[i] - _candidateCommentRects[i].Width()); - } - // offset rects - _candidateLabelRects[i].OffsetRect(ol, 0); - _candidateTextRects[i].OffsetRect(ot, 0); + bool alignToEnd = (_style.align_type == UIStyle::ALIGN_BOTTOM && + _style.vertical_text_left_to_right) || + (_style.align_type == UIStyle::ALIGN_TOP && + !_style.vertical_text_left_to_right); + bool endIsEnd = _style.vertical_text_left_to_right; + ReAdjustAlignment(static_cast(wids[i]), i, alignToEnd, endIsEnd); _candidateCommentRects[i].OffsetRect( - oc, max_content_height + _style.hilite_spacing); + 0, max_content_height + _style.hilite_spacing); // define _candidateRects _candidateRects[i].left = min(_candidateLabelRects[i].left, _candidateTextRects[i].left); @@ -230,19 +186,7 @@ void VHorizontalLayout::DoLayout(CDCHandle dc, PDWR pDWR) { _contentSize.SetSize(width + offsetX, height + offsetY); // calc page indicator - if (page_en && candidates_count && !_style.inline_preedit) { - int _prey = _contentSize.cy - offsetY - real_margin_y + - _style.hilite_padding_y - pgh; - int _prex = (_preeditRect.left + _preeditRect.right) / 2 - pgszl.cx / 2; - _prePageRect.SetRect(_prex, _prey, _prex + pgszl.cx, _prey + pgszl.cy); - _nextPageRect.SetRect( - _prex, _prePageRect.bottom + _style.hilite_spacing, _prex + pgszr.cx, - _prePageRect.bottom + _style.hilite_spacing + pgszr.cy); - if (ShouldDisplayStatusIcon()) { - _prePageRect.OffsetRect(0, -STATUS_ICON_SIZE); - _nextPageRect.OffsetRect(0, -STATUS_ICON_SIZE); - } - } + PlacePagerVerticalText(pager, _contentSize.cx, _contentSize.cy); _contentRect.SetRect(0, 0, _contentSize.cx, _contentSize.cy); // background rect prepare for Hemispherical calculation @@ -259,43 +203,12 @@ void VHorizontalLayout::DoLayoutWithWrap(CDCHandle dc, PDWR pDWR) { CSize size; int height = offsetY, width = offsetX + real_margin_x; int h = offsetY + real_margin_y; + MarkMetrics mark = ComputeMarkMetrics(pDWR); + int base_offset = mark.base_offset; - if ((_style.hilited_mark_color & 0xff000000)) { - CSize sg; - if (candidates_count) { - if (_style.mark_text.empty()) - GetTextSizeDW(L"|", 1, pDWR->pTextFormat, pDWR, &sg); - else - GetTextSizeDW(_style.mark_text, _style.mark_text.length(), - pDWR->pTextFormat, pDWR, &sg); - } - - mark_width = sg.cx; - mark_height = sg.cy; - if (_style.mark_text.empty()) { - mark_height = mark_width / 7; - if (_style.linespacing && _style.baseline) - mark_height = - (int)((float)mark_height / ((float)_style.linespacing / 100.0f)); - mark_height = max(mark_height, 6); - } - mark_gap = (_style.mark_text.empty()) ? mark_height - : mark_height + _style.hilite_spacing; - } - int base_offset = ((_style.hilited_mark_color & 0xff000000)) ? mark_gap : 0; - - // calc page indicator - CSize pgszl, pgszr; - if (!IsInlinePreedit()) { - GetTextSizeDW(pre, pre.length(), pDWR->pPreeditTextFormat, pDWR, &pgszl); - GetTextSizeDW(next, next.length(), pDWR->pPreeditTextFormat, pDWR, &pgszr); - } - bool page_en = (_style.prevpage_color & 0xff000000) && - (_style.nextpage_color & 0xff000000); - int pgh = page_en ? pgszl.cy + pgszr.cy + _style.hilite_spacing + - _style.hilite_padding_y * 2 - : 0; - int pgw = page_en ? max(pgszl.cx, pgszr.cx) : 0; + PagerMetrics pager = ComputePagerMetrics(pDWR); + int pgw = pager.pgw; + int pgh = pager.pgh; /* Preedit */ if (!IsInlinePreedit() && !_context.preedit.str.empty()) { @@ -410,37 +323,13 @@ void VHorizontalLayout::DoLayoutWithWrap(CDCHandle dc, PDWR pDWR) { minleft_of_cols[col_of_candidate[i]] + width_of_cols[col_of_candidate[i]], _candidateCommentRects[i].bottom); - int ol = 0, ot = 0, oc = 0; - if (_style.align_type == UIStyle::ALIGN_CENTER) { - ol = (width_of_cols[col_of_candidate[i]] - - _candidateLabelRects[i].Width()) / - 2; - ot = (width_of_cols[col_of_candidate[i]] - - _candidateTextRects[i].Width()) / - 2; - oc = (width_of_cols[col_of_candidate[i]] - - _candidateCommentRects[i].Width()) / - 2; - } else if ((_style.align_type == UIStyle::ALIGN_BOTTOM) && - _style.vertical_text_left_to_right) { - ol = (width_of_cols[col_of_candidate[i]] - - _candidateLabelRects[i].Width()); - ot = (width_of_cols[col_of_candidate[i]] - - _candidateTextRects[i].Width()); - oc = (width_of_cols[col_of_candidate[i]] - - _candidateCommentRects[i].Width()); - } else if ((_style.align_type == UIStyle::ALIGN_TOP) && - (!_style.vertical_text_left_to_right)) { - ol = (width_of_cols[col_of_candidate[i]] - - _candidateLabelRects[i].Width()); - ot = (width_of_cols[col_of_candidate[i]] - - _candidateTextRects[i].Width()); - oc = (width_of_cols[col_of_candidate[i]] - - _candidateCommentRects[i].Width()); - } - _candidateLabelRects[i].OffsetRect(ol, 0); - _candidateTextRects[i].OffsetRect(ot, 0); - _candidateCommentRects[i].OffsetRect(oc, 0); + bool alignToEnd = (_style.align_type == UIStyle::ALIGN_BOTTOM && + _style.vertical_text_left_to_right) || + (_style.align_type == UIStyle::ALIGN_TOP && + !_style.vertical_text_left_to_right); + bool endIsEnd = _style.vertical_text_left_to_right; + ReAdjustAlignment(static_cast(width_of_cols[col_of_candidate[i]]), + i, alignToEnd, endIsEnd); if ((i < candidates_count - 1 && col_of_candidate[i] < col_of_candidate[i + 1]) || (i == candidates_count - 1)) @@ -516,19 +405,7 @@ void VHorizontalLayout::DoLayoutWithWrap(CDCHandle dc, PDWR pDWR) { _contentRect.SetRect(0, 0, _contentSize.cx, _contentSize.cy); // calc page indicator - if (page_en && candidates_count && !_style.inline_preedit) { - int _prey = _contentSize.cy - offsetY - real_margin_y + - _style.hilite_padding_y - pgh; - int _prex = (_preeditRect.left + _preeditRect.right) / 2 - pgszl.cx / 2; - _prePageRect.SetRect(_prex, _prey, _prex + pgszl.cx, _prey + pgszl.cy); - _nextPageRect.SetRect( - _prex, _prePageRect.bottom + _style.hilite_spacing, _prex + pgszr.cx, - _prePageRect.bottom + _style.hilite_spacing + pgszr.cy); - if (ShouldDisplayStatusIcon()) { - _prePageRect.OffsetRect(0, -STATUS_ICON_SIZE); - _nextPageRect.OffsetRect(0, -STATUS_ICON_SIZE); - } - } + PlacePagerVerticalText(pager, _contentSize.cx, _contentSize.cy); // prepare temp rect _bgRect for roundinfo calculation CopyRect(_bgRect, _contentRect); diff --git a/WeaselUI/VerticalLayout.cpp b/WeaselUI/VerticalLayout.cpp index fee38f6c3..e6f7c4fd3 100644 --- a/WeaselUI/VerticalLayout.cpp +++ b/WeaselUI/VerticalLayout.cpp @@ -6,43 +6,12 @@ using namespace weasel; void weasel::VerticalLayout::DoLayout(CDCHandle dc, PDWR pDWR) { const int space = _style.hilite_spacing; int width = 0, height = real_margin_y; + MarkMetrics mark = ComputeMarkMetrics(pDWR); + int base_offset = mark.base_offset; - if ((_style.hilited_mark_color & 0xff000000)) { - CSize sg; - if (candidates_count) { - if (_style.mark_text.empty()) - GetTextSizeDW(L"|", 1, pDWR->pTextFormat, pDWR, &sg); - else - GetTextSizeDW(_style.mark_text, _style.mark_text.length(), - pDWR->pTextFormat, pDWR, &sg); - } - - mark_width = sg.cx; - mark_height = sg.cy; - if (_style.mark_text.empty()) { - mark_width = mark_height / 7; - if (_style.linespacing && _style.baseline) - mark_width = - (int)((float)mark_width / ((float)_style.linespacing / 100.0f)); - mark_width = max(mark_width, 6); - } - mark_gap = (_style.mark_text.empty()) ? mark_width - : mark_width + _style.hilite_spacing; - } - int base_offset = ((_style.hilited_mark_color & 0xff000000)) ? mark_gap : 0; - - // calc page indicator - CSize pgszl, pgszr; - if (!IsInlinePreedit()) { - GetTextSizeDW(pre, pre.length(), pDWR->pPreeditTextFormat, pDWR, &pgszl); - GetTextSizeDW(next, next.length(), pDWR->pPreeditTextFormat, pDWR, &pgszr); - } - bool page_en = (_style.prevpage_color & 0xff000000) && - (_style.nextpage_color & 0xff000000); - int pgw = page_en ? pgszl.cx + pgszr.cx + _style.hilite_spacing + - _style.hilite_padding_x * 2 - : 0; - int pgh = page_en ? max(pgszl.cy, pgszr.cy) : 0; + PagerMetrics pager = ComputePagerMetrics(pDWR); + int pgw = pager.pgw; + int pgh = pager.pgh; /* preedit and auxiliary rectangle calc start */ CSize size; @@ -129,20 +98,7 @@ void weasel::VerticalLayout::DoLayout(CDCHandle dc, PDWR pDWR) { comment_width += size.cx * cmtFontValid; max_comment_width = max(max_comment_width, comment_width); } - int ol = 0, ot = 0, oc = 0; - if (_style.align_type == UIStyle::ALIGN_CENTER) { - ol = (max_height_curren_candidate - _candidateLabelRects[i].Height()) / 2; - ot = (max_height_curren_candidate - _candidateTextRects[i].Height()) / 2; - oc = (max_height_curren_candidate - _candidateCommentRects[i].Height()) / - 2; - } else if (_style.align_type == UIStyle::ALIGN_BOTTOM) { - ol = (max_height_curren_candidate - _candidateLabelRects[i].Height()); - ot = (max_height_curren_candidate - _candidateTextRects[i].Height()); - oc = (max_height_curren_candidate - _candidateCommentRects[i].Height()); - } - _candidateLabelRects[i].OffsetRect(0, ol); - _candidateTextRects[i].OffsetRect(0, ot); - _candidateCommentRects[i].OffsetRect(0, oc); + ReAdjustAlignment(static_cast(max_height_curren_candidate), i); int hlTop = _candidateTextRects[i].top; int hlBot = _candidateTextRects[i].bottom; @@ -214,19 +170,7 @@ void weasel::VerticalLayout::DoLayout(CDCHandle dc, PDWR pDWR) { _highlightRect = _candidateRects[id]; // calc page indicator - if (page_en && candidates_count && !_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); - } - } + PlacePagerHorizontal(pager, _contentSize.cx, _contentSize.cy); // calc roundings start _contentRect.SetRect(0, 0, _contentSize.cx, _contentSize.cy); // background rect prepare for Hemispherical calculation From 2d03032551f4acb8ebe15319650698021eb817a4 Mon Sep 17 00:00:00 2001 From: fxliang Date: Fri, 6 Feb 2026 16:28:18 +0800 Subject: [PATCH 02/25] perf(WeaselUI): Optimized the performance of DirectWriteResources - cache text format - static const some const values - improve ws_split without regex - optimize _MatchWordsOutLowerCaseTrim1st with const regex reference input - remove useless swap for vectors --- WeaselUI/DirectWriteResources.cpp | 83 ++++++++++++++++++++----------- include/WeaselUI.h | 2 + 2 files changed, 56 insertions(+), 29 deletions(-) diff --git a/WeaselUI/DirectWriteResources.cpp b/WeaselUI/DirectWriteResources.cpp index 07d3481e6..eaae6aab6 100644 --- a/WeaselUI/DirectWriteResources.cpp +++ b/WeaselUI/DirectWriteResources.cpp @@ -5,9 +5,22 @@ #include using namespace weasel; -#define STYLEORWEIGHT (L":[^:]*[^a-f0-9:]+[^:]*") -vector ws_split(const wstring& in, const wstring& delim) { +static vector ws_split(const wstring& in, const wstring& delim) { + // Optimization for simple character delimiters to avoid regex overhead + if (delim.find_first_of(L"\\^$.|?*+()[]{}") == wstring::npos && + delim.length() > 0) { + vector result; + size_t start = 0; + size_t end = in.find(delim); + while (end != wstring::npos) { + result.push_back(in.substr(start, end - start)); + start = end + delim.length(); + end = in.find(delim, start); + } + result.push_back(in.substr(start)); + return result; + } std::wregex re{delim}; return vector{ std::wsregex_token_iterator(in.begin(), in.end(), re, -1), @@ -28,11 +41,11 @@ DirectWriteResources::DirectWriteResources(weasel::UIStyle& style, pTextFormat(NULL), pLabelTextFormat(NULL), pCommentTextFormat(NULL) { - D2D1_TEXT_ANTIALIAS_MODE mode = + // prepare d2d1 resources create factory + static const D2D1_TEXT_ANTIALIAS_MODE mode = _style.antialias_mode <= 3 ? (D2D1_TEXT_ANTIALIAS_MODE)(_style.antialias_mode) - : D2D1_TEXT_ANTIALIAS_MODE_FORCE_DWORD; // prepare d2d1 resources - // create factory + : D2D1_TEXT_ANTIALIAS_MODE_FORCE_DWORD; HR(::D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, pD2d1Factory.ReleaseAndGetAddressOf())); // create IDWriteFactory @@ -40,9 +53,9 @@ DirectWriteResources::DirectWriteResources(weasel::UIStyle& style, DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast(pDWFactory.ReleaseAndGetAddressOf()))); /* ID2D1HwndRenderTarget */ - const D2D1_PIXEL_FORMAT format = D2D1::PixelFormat( + static const D2D1_PIXEL_FORMAT format = D2D1::PixelFormat( DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED); - const D2D1_RENDER_TARGET_PROPERTIES properties = + static const D2D1_RENDER_TARGET_PROPERTIES properties = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, format); HR(pD2d1Factory->CreateDCRenderTarget(&properties, &pRenderTarget)); pRenderTarget->SetTextAntialiasMode(mode); @@ -57,7 +70,9 @@ DirectWriteResources::DirectWriteResources(weasel::UIStyle& style, InitResources(style, dpi); } -DirectWriteResources::~DirectWriteResources() {} +DirectWriteResources::~DirectWriteResources() { + _textFormatCache.clear(); +} HRESULT DirectWriteResources::InitResources(const wstring& label_font_face, const int& label_font_point, @@ -67,26 +82,26 @@ HRESULT DirectWriteResources::InitResources(const wstring& label_font_face, const int& comment_font_point, const bool& vertical_text) { // prepare d2d1 resources - DWRITE_WORD_WRAPPING wrapping = + const DWRITE_WORD_WRAPPING wrapping = ((_style.max_width == 0 && _style.layout_type != UIStyle::LAYOUT_VERTICAL_TEXT) || (_style.max_height == 0 && _style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT)) ? DWRITE_WORD_WRAPPING_NO_WRAP : DWRITE_WORD_WRAPPING_WHOLE_WORD; - DWRITE_WORD_WRAPPING wrapping_preedit = + const DWRITE_WORD_WRAPPING wrapping_preedit = ((_style.max_width == 0 && _style.layout_type != UIStyle::LAYOUT_VERTICAL_TEXT) || (_style.max_height == 0 && _style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT)) ? DWRITE_WORD_WRAPPING_NO_WRAP : DWRITE_WORD_WRAPPING_CHARACTER; - DWRITE_FLOW_DIRECTION flow = _style.vertical_text_left_to_right - ? DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT - : DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT; + const DWRITE_FLOW_DIRECTION flow = _style.vertical_text_left_to_right + ? DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT + : DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT; // set main font a invalid font name, to make every font range customizable - const wstring _mainFontFace = L"_InvalidFontName_"; + static const wstring _mainFontFace = L"_InvalidFontName_"; DWRITE_FONT_WEIGHT fontWeight = DWRITE_FONT_WEIGHT_NORMAL; DWRITE_FONT_STYLE fontStyle = DWRITE_FONT_STYLE_NORMAL; // convert percentage to float @@ -98,14 +113,24 @@ HRESULT DirectWriteResources::InitResources(const wstring& label_font_face, auto init_font = [&](const wstring& fontface, int fontpoint, ComPtr& _pTextFormat, DWRITE_WORD_WRAPPING wrap) { + const wstring key = fontface + L"|" + std::to_wstring(fontpoint) + L"|" + + (vertical_text ? L"1" : L"0") + L"|" + + std::to_wstring((int)wrap) + L"|" + + std::to_wstring(_style.linespacing) + L"|" + + std::to_wstring(_style.baseline); + if (_textFormatCache.find(key) != _textFormatCache.end()) { + _pTextFormat = _textFormatCache[key]; + return; + } vector fontFaceStrVector; // text font text format set up fontFaceStrVector = ws_split(fontface, L","); // setup weight and style by the first unit of fontface setting string _ParseFontFace(fontface, fontWeight, fontStyle); + static const std::wregex styleOrWeightRegex(L":[^:]*[^a-f0-9:]+[^:]*", + std::wregex::icase); fontFaceStrVector[0] = - std::regex_replace(fontFaceStrVector[0], - std::wregex(STYLEORWEIGHT, std::wregex::icase), L""); + std::regex_replace(fontFaceStrVector[0], styleOrWeightRegex, L""); // create text format with invalid font point will 'FAILED', no HR pDWFactory->CreateTextFormat(_mainFontFace.c_str(), NULL, fontWeight, fontStyle, DWRITE_FONT_STRETCH_NORMAL, @@ -123,14 +148,14 @@ HRESULT DirectWriteResources::InitResources(const wstring& label_font_face, HR(_pTextFormat->SetParagraphAlignment( DWRITE_PARAGRAPH_ALIGNMENT_CENTER)); - HR(_pTextFormat->SetWordWrapping(wrapping)); + HR(_pTextFormat->SetWordWrapping(wrap)); _SetFontFallback(_pTextFormat, fontFaceStrVector); if (_style.linespacing && _style.baseline) _pTextFormat->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_UNIFORM, fontpoint * linespacing, fontpoint * baseline); + _textFormatCache[key] = _pTextFormat; } - decltype(fontFaceStrVector)().swap(fontFaceStrVector); }; init_font(font_face, font_point, pTextFormat, wrapping); init_font(font_face, font_point, pPreeditTextFormat, wrapping_preedit); @@ -157,14 +182,14 @@ void weasel::DirectWriteResources::SetDpi(const UINT& dpi) { dpiScaleFontPoint = dpi / 72.0f; dpiScaleLayout = dpi / 96.0f; + _textFormatCache.clear(); InitResources(_style); } static wstring _MatchWordsOutLowerCaseTrim1st(const wstring& wstr, - const wstring& pat) { + const std::wregex& pattern) { wstring mat = L""; std::wsmatch mc; - std::wregex pattern(pat, std::wregex::icase); wstring::const_iterator iter = wstr.cbegin(); wstring::const_iterator end = wstr.cend(); while (regex_search(iter, end, mc, pattern)) { @@ -183,11 +208,12 @@ static wstring _MatchWordsOutLowerCaseTrim1st(const wstring& wstr, void DirectWriteResources::_ParseFontFace(const wstring& fontFaceStr, DWRITE_FONT_WEIGHT& fontWeight, DWRITE_FONT_STYLE& fontStyle) { - const wstring patWeight( + static const std::wregex patWeight( L"(:thin|:extra_light|:ultra_light|:light|:semi_light|:medium|:demi_bold|" L":semi_bold|:bold|:extra_bold|:ultra_bold|:black|:heavy|:extra_black|:" - L"ultra_black)"); - const std::map _mapWeight = { + L"ultra_black)", + std::wregex::icase); + static const std::map _mapWeight = { {L"thin", DWRITE_FONT_WEIGHT_THIN}, {L"extra_light", DWRITE_FONT_WEIGHT_EXTRA_LIGHT}, {L"ultra_light", DWRITE_FONT_WEIGHT_ULTRA_LIGHT}, @@ -204,18 +230,19 @@ void DirectWriteResources::_ParseFontFace(const wstring& fontFaceStr, {L"extra_black", DWRITE_FONT_WEIGHT_EXTRA_BLACK}, {L"normal", DWRITE_FONT_WEIGHT_NORMAL}, {L"ultra_black", DWRITE_FONT_WEIGHT_ULTRA_BLACK}}; - wstring weight = _MatchWordsOutLowerCaseTrim1st(fontFaceStr, patWeight); + const wstring weight = _MatchWordsOutLowerCaseTrim1st(fontFaceStr, patWeight); auto it = _mapWeight.find(weight); fontWeight = (it != _mapWeight.end()) ? it->second : DWRITE_FONT_WEIGHT_NORMAL; - const wstring patStyle(L"(:italic|:oblique|:normal)"); - const std::map _mapStyle = { + static const std::wregex patStyle(L"(:italic|:oblique|:normal)", + std::wregex::icase); + static const std::map _mapStyle = { {L"italic", DWRITE_FONT_STYLE_ITALIC}, {L"oblique", DWRITE_FONT_STYLE_OBLIQUE}, {L"normal", DWRITE_FONT_STYLE_NORMAL}, }; - wstring style = _MatchWordsOutLowerCaseTrim1st(fontFaceStr, patStyle); + const wstring style = _MatchWordsOutLowerCaseTrim1st(fontFaceStr, patStyle); auto it2 = _mapStyle.find(style); fontStyle = (it2 != _mapStyle.end()) ? it2->second : DWRITE_FONT_STYLE_NORMAL; } @@ -267,12 +294,10 @@ void DirectWriteResources::_SetFontFallback( DWRITE_UNICODE_RANGE range = {first, last}; const WCHAR* familys = {_fontFaceWstr.c_str()}; HR(pFontFallbackBuilder->AddMapping(&range, 1, &familys, 1)); - decltype(fallbackFontsVector)().swap(fallbackFontsVector); } // add system defalt font fallback HR(pFontFallbackBuilder->AddMappings(pSysFallback.Get())); HR(pFontFallbackBuilder->CreateFontFallback( pFontFallback.ReleaseAndGetAddressOf())); HR(textFormat->SetFontFallback(pFontFallback.Get())); - decltype(fallbackFontsVector)().swap(fallbackFontsVector); } diff --git a/include/WeaselUI.h b/include/WeaselUI.h index 77bb3b4b2..951da6656 100755 --- a/include/WeaselUI.h +++ b/include/WeaselUI.h @@ -8,6 +8,7 @@ #include #include #include +#include #include namespace weasel { @@ -164,5 +165,6 @@ class DirectWriteResources { DWRITE_FONT_STYLE& fontStyle); void _SetFontFallback(ComPtr& pTextFormat, const std::vector& fontVector); + std::map> _textFormatCache; }; } // namespace weasel From 29011628d60197c3aa0884505ebfaf65abd44221 Mon Sep 17 00:00:00 2001 From: fxliang Date: Fri, 6 Feb 2026 17:59:14 +0800 Subject: [PATCH 03/25] perf(WeaselUI): GdiplusBlur with openmp --- WeaselUI/GdiplusBlur.cpp | 55 ++++++++++++++++++++------------------- WeaselUI/WeaselUI.vcxproj | 8 ++++++ WeaselUI/xmake.lua | 1 + 3 files changed, 37 insertions(+), 27 deletions(-) diff --git a/WeaselUI/GdiplusBlur.cpp b/WeaselUI/GdiplusBlur.cpp index 23c4d3cbf..23d117f4f 100644 --- a/WeaselUI/GdiplusBlur.cpp +++ b/WeaselUI/GdiplusBlur.cpp @@ -1,5 +1,11 @@ #include "stdafx.h" #include "GdiplusBlur.h" +#include // For std::unique_ptr +#ifdef _OPENMP +#include // For OpenMP parallelization +#endif +#include // For BYTE, LONGLONG +#include // For Gdiplus::Bitmap namespace weasel { /* start image gauss blur functions from @@ -31,6 +37,8 @@ void boxBlurH_4(BYTE* scl, int bpp, int stride) { float iarr = (float)(1. / ((LONGLONG)r + r + 1)); +#pragma omp \ + parallel for if (h > 64) // Parallelize for images taller than 64 pixels for (int i = 0; i < h; ++i) { int ti1 = i * stride; int ti2 = i * stride + 1; @@ -150,6 +158,8 @@ void boxBlurT_4(BYTE* scl, int bpp, int stride) { float iarr = (float)(1.0f / (r + r + 1.0f)); +#pragma omp \ + parallel for if (w > 64) // Parallelize for images wider than 64 pixels for (int i = 0; i < w; ++i) { int ti1 = i * bpp; int ti2 = i * bpp + 1; @@ -295,26 +305,25 @@ void gaussBlur_4(BYTE* scl, } void DoGaussianBlur(Gdiplus::Bitmap* img, float radiusX, float radiusY) { - if (img == 0 || (radiusX == 0.0f && radiusY == 0.0f)) + if (!img || (radiusX <= 0.0f && radiusY <= 0.0f)) return; const int w = img->GetWidth(); const int h = img->GetHeight(); - if (radiusX > w / 2) { - radiusX = (float)(w / 2); - } - - if (radiusY > h / 2) { - radiusY = (float)(h / 2); - } + // Clamp radii to prevent excessive computation + if (radiusX > w / 2.0f) + radiusX = w / 2.0f; + if (radiusY > h / 2.0f) + radiusY = h / 2.0f; - Gdiplus::Bitmap* temp = new Gdiplus::Bitmap(img->GetWidth(), img->GetHeight(), - img->GetPixelFormat()); + // Use unique_ptr for automatic memory management + std::unique_ptr temp( + new Gdiplus::Bitmap(w, h, img->GetPixelFormat())); Gdiplus::BitmapData bitmapData1; Gdiplus::BitmapData bitmapData2; - Gdiplus::Rect rect(0, 0, img->GetWidth(), img->GetHeight()); + Gdiplus::Rect rect(0, 0, w, h); if (Gdiplus::Ok == img->LockBits( @@ -324,10 +333,10 @@ void DoGaussianBlur(Gdiplus::Bitmap* img, float radiusX, float radiusY) { temp->LockBits( &rect, Gdiplus::ImageLockModeRead | Gdiplus::ImageLockModeWrite, temp->GetPixelFormat(), &bitmapData2)) { - BYTE* src = (BYTE*)bitmapData1.Scan0; - BYTE* dst = (BYTE*)bitmapData2.Scan0; + BYTE* src = static_cast(bitmapData1.Scan0); + BYTE* dst = static_cast(bitmapData2.Scan0); - const int bpp = 4; + constexpr int bpp = 4; const int stride = bitmapData1.Stride; gaussBlur_4(src, dst, w, h, radiusX, radiusY, bpp, stride); @@ -335,26 +344,18 @@ void DoGaussianBlur(Gdiplus::Bitmap* img, float radiusX, float radiusY) { img->UnlockBits(&bitmapData1); temp->UnlockBits(&bitmapData2); } - - delete temp; + // temp is automatically deleted here } void DoGaussianBlurPower(Gdiplus::Bitmap* img, float radiusX, float radiusY, int nPower) { - Gdiplus::Bitmap* pBitmap = - img->Clone(0, 0, img->GetWidth(), img->GetHeight(), PixelFormat32bppARGB); - DoGaussianBlur(pBitmap, radiusX, radiusY); - Gdiplus::Graphics g(pBitmap); - for (int i = 0; i < 8; ++i) { - g.DrawImage(pBitmap, 0, 0); - if ((1 << i) & nPower) { - Gdiplus::Graphics g(img); - g.DrawImage(pBitmap, 0, 0); - } + // Optimized: Directly apply Gaussian blur multiple times on the original + // image Avoids cloning and GDI+ drawing overhead + for (int i = 0; i < nPower; ++i) { + DoGaussianBlur(img, radiusX, radiusY); } - delete pBitmap; } /* end image gauss blur functions from https://github.com/kenjinote/DropShadow/ */ diff --git a/WeaselUI/WeaselUI.vcxproj b/WeaselUI/WeaselUI.vcxproj index 216bce16e..7b3bafe34 100644 --- a/WeaselUI/WeaselUI.vcxproj +++ b/WeaselUI/WeaselUI.vcxproj @@ -159,6 +159,7 @@ EditAndContinue /Zc:threadSafeInit- %(AdditionalOptions) stdcpp17 + GenerateParallelCode @@ -175,6 +176,7 @@ EditAndContinue /Zc:threadSafeInit- %(AdditionalOptions) stdcpp17 + GenerateParallelCode @@ -191,6 +193,7 @@ /Zc:threadSafeInit- %(AdditionalOptions) stdcpp17 true + GenerateParallelCode @@ -207,6 +210,7 @@ /Zc:threadSafeInit- %(AdditionalOptions) stdcpp17 true + GenerateParallelCode @@ -226,6 +230,7 @@ ProgramDatabase /Zc:threadSafeInit- %(AdditionalOptions) stdcpp17 + GenerateParallelCode @@ -243,6 +248,7 @@ ProgramDatabase /Zc:threadSafeInit- %(AdditionalOptions) stdcpp17 + GenerateParallelCode @@ -262,6 +268,7 @@ /Zc:threadSafeInit- %(AdditionalOptions) stdcpp17 true + GenerateParallelCode @@ -279,6 +286,7 @@ /Zc:threadSafeInit- %(AdditionalOptions) stdcpp17 true + GenerateParallelCode diff --git a/WeaselUI/xmake.lua b/WeaselUI/xmake.lua index 420befc2a..0852e69b2 100644 --- a/WeaselUI/xmake.lua +++ b/WeaselUI/xmake.lua @@ -1,4 +1,5 @@ target("WeaselUI") set_kind("static") add_files("./*.cpp") + add_cxflags("/openmp") -- Enable OpenMP for parallel processing From 209370363ba5c61a423aed22e6e5edf993347a2b Mon Sep 17 00:00:00 2001 From: fxliang Date: Sat, 7 Feb 2026 23:07:37 +0800 Subject: [PATCH 04/25] refactor(WeaselUI): use smart prt --- WeaselUI/WeaselPanel.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index 71100c4a7..65293f6f9 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "WeaselPanel.h" +#include #include #include #include @@ -958,8 +959,8 @@ void WeaselPanel::DoPaint(CDCHandle dc) { CRect auxrc = m_layout->GetAuxiliaryRect(); CRect preeditrc = m_layout->GetPreeditRect(); if (m_istorepos) { - CRect* rects = new CRect[m_candidateCount]; - int* btmys = new int[m_candidateCount]; + std::unique_ptr rects(new CRect[m_candidateCount]); + std::unique_ptr btmys(new int[m_candidateCount]); for (auto i = 0; i < m_candidateCount && i < MAX_CANDIDATES_COUNT; ++i) { rects[i] = m_layout->GetCandidateRect(i); btmys[i] = rects[i].bottom; @@ -989,8 +990,6 @@ void WeaselPanel::DoPaint(CDCHandle dc) { DPI_SCALE(m_style.candidate_spacing)) - rects[i].bottom; } - delete[] rects; - delete[] btmys; } // background and candidates back, hilite back drawing start if ((!m_ctx.empty() && !m_style.inline_preedit) || From 5835b1824fe2b50f2c662de61ca16647db60619d Mon Sep 17 00:00:00 2001 From: fxliang Date: Sat, 7 Feb 2026 23:47:52 +0800 Subject: [PATCH 05/25] perf(WeaselUI): reuse Gdiplus::Graphics --- WeaselUI/WeaselPanel.cpp | 41 ++++++++++++++++++---------------------- WeaselUI/WeaselPanel.h | 14 ++++++++------ 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index 65293f6f9..2ad5516cc 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -484,18 +484,14 @@ LRESULT WeaselPanel::OnMouseLeave(UINT uMsg, return 0; } -void WeaselPanel::_HighlightText(CDCHandle& dc, +void WeaselPanel::_HighlightText(Gdiplus::Graphics& g_back, const CRect& rc, const COLORREF& color, const COLORREF& shadowColor, const int& radius, - const BackType& type = BackType::TEXT, - const IsToRoundStruct& rd = IsToRoundStruct(), - const COLORREF& bordercolor = TRANS_COLOR) { - // Graphics obj with SmoothingMode - Gdiplus::Graphics g_back(dc); - g_back.SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeHighQuality); - + const BackType& type, + const IsToRoundStruct& rd, + const COLORREF& bordercolor) { // blur buffer int blurMarginX = m_layout->offsetX; int blurMarginY = m_layout->offsetY; @@ -684,7 +680,7 @@ bool WeaselPanel::_DrawPreedit(const Text& text, // draw hilited back color, back only bool WeaselPanel::_DrawPreeditBack(const Text& text, - CDCHandle dc, + Gdiplus::Graphics& g_back, const CRect& rc) { bool drawn = false; std::wstring const& t = text.str; @@ -731,7 +727,7 @@ bool WeaselPanel::_DrawPreeditBack(const Text& text, std::swap(rd.IsTopLeftNeedToRound, rd.IsBottomLeftNeedToRound); std::swap(rd.IsTopRightNeedToRound, rd.IsBottomRightNeedToRound); } - _HighlightText(dc, rc_hi, m_style.hilited_back_color, + _HighlightText(g_back, rc_hi, m_style.hilited_back_color, m_style.hilited_shadow_color, DPI_SCALE(m_style.round_corner), BackType::TEXT, rd); } @@ -741,7 +737,7 @@ bool WeaselPanel::_DrawPreeditBack(const Text& text, return drawn; } -bool WeaselPanel::_DrawCandidates(CDCHandle& dc, bool back) { +bool WeaselPanel::_DrawCandidates(Gdiplus::Graphics& g_back, bool back) { bool drawn = false; const std::vector& candidates(m_ctx.cinfo.candies); const std::vector& comments(m_ctx.cinfo.comments); @@ -773,7 +769,7 @@ bool WeaselPanel::_DrawCandidates(CDCHandle& dc, bool back) { } rect.InflateRect(DPI_SCALE(m_style.hilite_padding_x), DPI_SCALE(m_style.hilite_padding_y)); - _HighlightText(dc, rect, 0x00000000, m_style.candidate_shadow_color, + _HighlightText(g_back, rect, 0x00000000, m_style.candidate_shadow_color, DPI_SCALE(m_style.round_corner), bkType, rd); drawn = true; } @@ -792,7 +788,7 @@ bool WeaselPanel::_DrawCandidates(CDCHandle& dc, bool back) { } rect.InflateRect(DPI_SCALE(m_style.hilite_padding_x), DPI_SCALE(m_style.hilite_padding_y)); - _HighlightText(dc, rect, m_style.candidate_back_color, 0x00000000, + _HighlightText(g_back, rect, m_style.candidate_back_color, 0x00000000, DPI_SCALE(m_style.round_corner), bkType, rd, m_style.candidate_border_color); drawn = true; @@ -808,7 +804,7 @@ bool WeaselPanel::_DrawCandidates(CDCHandle& dc, bool back) { } rect.InflateRect(DPI_SCALE(m_style.hilite_padding_x), DPI_SCALE(m_style.hilite_padding_y)); - _HighlightText(dc, rect, + _HighlightText(g_back, rect, HALF_ALPHA_COLOR(m_style.hilited_candidate_back_color), HALF_ALPHA_COLOR(m_style.hilited_candidate_shadow_color), DPI_SCALE(m_style.round_corner), bkType, rd, @@ -825,7 +821,7 @@ bool WeaselPanel::_DrawCandidates(CDCHandle& dc, bool back) { } rect.InflateRect(DPI_SCALE(m_style.hilite_padding_x), DPI_SCALE(m_style.hilite_padding_y)); - _HighlightText(dc, rect, m_style.hilited_candidate_back_color, + _HighlightText(g_back, rect, m_style.hilited_candidate_back_color, markSt ? m_style.hilited_candidate_shadow_color : 0, DPI_SCALE(m_style.round_corner), bkType, rd, m_style.hilited_candidate_border_color); @@ -842,9 +838,6 @@ bool WeaselPanel::_DrawCandidates(CDCHandle& dc, bool back) { width = static_cast(width * bar_scale_); height = static_cast(height * bar_scale_); } - Gdiplus::Graphics g_back(dc); - g_back.SetSmoothingMode( - Gdiplus::SmoothingMode::SmoothingModeHighQuality); Gdiplus::Color mark_color = GDPCOLOR_FROM_COLORREF(m_style.hilited_mark_color); Gdiplus::SolidBrush mk_brush(mark_color); @@ -992,25 +985,27 @@ void WeaselPanel::DoPaint(CDCHandle dc) { } } // background and candidates back, hilite back drawing start + Gdiplus::Graphics g_back(memDC); + g_back.SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeHighQuality); if ((!m_ctx.empty() && !m_style.inline_preedit) || (m_style.inline_preedit && (m_candidateCount || !m_ctx.aux.empty()))) { CRect backrc = m_layout->GetContentRect(); - _HighlightText(memDC, backrc, m_style.back_color, m_style.shadow_color, + _HighlightText(g_back, backrc, m_style.back_color, m_style.shadow_color, DPI_SCALE(m_style.round_corner_ex), BackType::BACKGROUND, IsToRoundStruct(), m_style.border_color); } if (!m_ctx.aux.str.empty()) { if (m_istorepos) auxrc.OffsetRect(0, m_offsety_aux); - drawn |= _DrawPreeditBack(m_ctx.aux, memDC, auxrc); + drawn |= _DrawPreeditBack(m_ctx.aux, g_back, auxrc); } if (!m_layout->IsInlinePreedit() && !m_ctx.preedit.str.empty()) { if (m_istorepos) preeditrc.OffsetRect(0, m_offsety_preedit); - drawn |= _DrawPreeditBack(m_ctx.preedit, memDC, preeditrc); + drawn |= _DrawPreeditBack(m_ctx.preedit, g_back, preeditrc); } if (m_candidateCount) - drawn |= _DrawCandidates(memDC, true); + drawn |= _DrawCandidates(g_back, true); // background and candidates back, hilite back drawing end // begin texts drawing, if pRenderTarget failed, force to reinit @@ -1028,7 +1023,7 @@ void WeaselPanel::DoPaint(CDCHandle dc) { drawn |= _DrawPreedit(m_ctx.preedit, memDC, preeditrc); // draw candidates string if (m_candidateCount) - drawn |= _DrawCandidates(memDC); + drawn |= _DrawCandidates(g_back); if (FAILED(pDWR->pRenderTarget->EndDraw())) { _InitFontRes(true); Refresh(); diff --git a/WeaselUI/WeaselPanel.h b/WeaselUI/WeaselPanel.h index 18d7713f3..76f709dd5 100644 --- a/WeaselUI/WeaselPanel.h +++ b/WeaselUI/WeaselPanel.h @@ -85,16 +85,18 @@ class WeaselPanel void _ResizeWindow(); void _RepositionWindow(const bool& adj = false); bool _DrawPreedit(const Text& text, CDCHandle dc, const CRect& rc); - bool _DrawPreeditBack(const Text& text, CDCHandle dc, const CRect& rc); - bool _DrawCandidates(CDCHandle& dc, bool back = false); - void _HighlightText(CDCHandle& dc, + bool _DrawPreeditBack(const Text& text, + Gdiplus::Graphics& g_back, + const CRect& rc); + bool _DrawCandidates(Gdiplus::Graphics& g_back, bool back = false); + void _HighlightText(Gdiplus::Graphics& g_back, const CRect& rc, const COLORREF& color, const COLORREF& shadowColor, const int& radius, - const BackType& type, - const IsToRoundStruct& rd, - const COLORREF& bordercolor); + const BackType& type = BackType::TEXT, + const IsToRoundStruct& rd = IsToRoundStruct(), + const COLORREF& bordercolor = 0); void _TextOut(const CRect& rc, const std::wstring& psz, const size_t& cch, From 2ec04981225fe7d55b552f58286ab538eb585951 Mon Sep 17 00:00:00 2001 From: fxliang Date: Sun, 8 Feb 2026 00:18:40 +0800 Subject: [PATCH 06/25] perf(WeaselUI): cache DPI_SCALE style params --- WeaselUI/WeaselPanel.cpp | 201 ++++++++++++++++++++------------------- WeaselUI/WeaselPanel.h | 16 ++++ 2 files changed, 119 insertions(+), 98 deletions(-) diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index 2ad5516cc..8bdf9bfb6 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -1,4 +1,4 @@ -#include "stdafx.h" +#include "stdafx.h" #include "WeaselPanel.h" #include @@ -151,8 +151,7 @@ void WeaselPanel::Refresh() { (m_ctx.empty() && should_show_icon)); // show schema menu status: schema_id == L".default" bool show_schema_menu = m_status.schema_id == L".default"; - bool margin_negative = - (DPI_SCALE(m_style.margin_x) < 0 || DPI_SCALE(m_style.margin_y) < 0); + bool margin_negative = m_style.margin_x < 0 || m_style.margin_y < 0; // when to hide_cadidates? // 1. margin_negative, and not in show tips mode( ascii switching / half-full // switching / simp-trad switching / error tips), and not in schema menu @@ -197,6 +196,21 @@ void WeaselPanel::_InitFontRes(bool forced) { m_ostyle = m_style; dpi = dpiX; dpiScaleLayout = (float)dpi / 96.0f; + + // Update cached style configuration + m_cachedStyle.margin_x = DPI_SCALE(m_style.margin_x); + m_cachedStyle.margin_y = DPI_SCALE(m_style.margin_y); + m_cachedStyle.hilite_padding_x = DPI_SCALE(m_style.hilite_padding_x); + m_cachedStyle.hilite_padding_y = DPI_SCALE(m_style.hilite_padding_y); + m_cachedStyle.shadow_radius = DPI_SCALE(m_style.shadow_radius); + m_cachedStyle.shadow_offset_x = DPI_SCALE(m_style.shadow_offset_x); + m_cachedStyle.shadow_offset_y = DPI_SCALE(m_style.shadow_offset_y); + m_cachedStyle.round_corner = DPI_SCALE(m_style.round_corner); + m_cachedStyle.round_corner_ex = DPI_SCALE(m_style.round_corner_ex); + m_cachedStyle.border = DPI_SCALE(m_style.border); + m_cachedStyle.hilite_spacing = DPI_SCALE(m_style.hilite_spacing); + m_cachedStyle.candidate_spacing = DPI_SCALE(m_style.candidate_spacing); + m_cachedStyle.spacing = DPI_SCALE(m_style.spacing); } static HBITMAP CopyDCToBitmap(HDC hDC, LPRECT lpRect) { @@ -284,8 +298,8 @@ LRESULT WeaselPanel::OnLeftClickedUp(UINT uMsg, CRect rect = m_layout->GetCandidateRect((int)m_ctx.cinfo.highlighted); if (m_istorepos) rect.OffsetRect(0, m_offsetys[m_ctx.cinfo.highlighted]); - rect.InflateRect(DPI_SCALE(m_style.hilite_padding_x), - DPI_SCALE(m_style.hilite_padding_y)); + rect.InflateRect(m_cachedStyle.hilite_padding_x, + m_cachedStyle.hilite_padding_y); if (rect.PtInRect(point)) { size_t i = m_ctx.cinfo.highlighted; if (_UICallback) { @@ -319,36 +333,33 @@ LRESULT WeaselPanel::OnLeftClickedDown(UINT uMsg, CRect recth = m_layout->GetCandidateRect((int)m_ctx.cinfo.highlighted); if (m_istorepos) recth.OffsetRect(0, m_offsetys[m_ctx.cinfo.highlighted]); - recth.InflateRect(DPI_SCALE(m_style.hilite_padding_x), - DPI_SCALE(m_style.hilite_padding_y)); + recth.InflateRect(m_cachedStyle.hilite_padding_x, + m_cachedStyle.hilite_padding_y); // capture widow if (recth.PtInRect(point)) _CaptureRect(recth); else { // if shadow_color transparent, decrease the capture rectangle size if (COLORTRANSPARENT(m_style.shadow_color) && - DPI_SCALE(m_style.shadow_radius) != 0) { + m_style.shadow_radius != 0) { CRect crc(rcw); - int shadow_gap = (DPI_SCALE(m_style.shadow_offset_x) == 0 && - DPI_SCALE(m_style.shadow_offset_y) == 0) - ? 2 * DPI_SCALE(m_style.shadow_radius) - : DPI_SCALE(m_style.shadow_radius) + - DPI_SCALE(m_style.shadow_radius) / 2; - int ofx = DPI_SCALE(m_style.hilite_padding_x) + - abs(DPI_SCALE(m_style.shadow_offset_x)) + - shadow_gap > - abs(DPI_SCALE(m_style.margin_x)) - ? DPI_SCALE(m_style.hilite_padding_x) + - abs(DPI_SCALE(m_style.shadow_offset_x)) + - shadow_gap - abs(DPI_SCALE(m_style.margin_x)) + int shadow_gap = + (!m_style.shadow_offset_x && !m_style.shadow_offset_y) + ? 2 * m_cachedStyle.shadow_radius + : m_cachedStyle.shadow_radius + m_cachedStyle.shadow_radius / 2; + int ofx = m_cachedStyle.hilite_padding_x + + abs(m_cachedStyle.shadow_offset_x) + shadow_gap > + abs(m_cachedStyle.margin_x) + ? m_cachedStyle.hilite_padding_x + + abs(m_cachedStyle.shadow_offset_x) + shadow_gap - + abs(m_cachedStyle.margin_x) : 0; - int ofy = DPI_SCALE(m_style.hilite_padding_y) + - abs(DPI_SCALE(m_style.shadow_offset_y)) + - shadow_gap > - abs(DPI_SCALE(m_style.margin_y)) - ? DPI_SCALE(m_style.hilite_padding_y) + - abs(DPI_SCALE(m_style.shadow_offset_y)) + - shadow_gap - abs(DPI_SCALE(m_style.margin_y)) + int ofy = m_cachedStyle.hilite_padding_y + + abs(m_cachedStyle.shadow_offset_y) + shadow_gap > + abs(m_cachedStyle.margin_y) + ? m_cachedStyle.hilite_padding_y + + abs(m_cachedStyle.shadow_offset_y) + shadow_gap - + abs(m_cachedStyle.margin_y) : 0; crc.DeflateRect(m_layout->offsetX - ofx, m_layout->offsetY - ofy); _CaptureRect(crc); @@ -393,8 +404,8 @@ LRESULT WeaselPanel::OnLeftClickedDown(UINT uMsg, CRect rect = m_layout->GetCandidateRect((int)i); if (m_istorepos) rect.OffsetRect(0, m_offsetys[i]); - rect.InflateRect(DPI_SCALE(m_style.hilite_padding_x), - DPI_SCALE(m_style.hilite_padding_y)); + rect.InflateRect(m_cachedStyle.hilite_padding_x, + m_cachedStyle.hilite_padding_y); if (rect.PtInRect(point)) { bar_scale_ = 0.8f; // modify highlighted @@ -453,8 +464,8 @@ LRESULT WeaselPanel::OnMouseMove(UINT uMsg, CRect rect = m_layout->GetCandidateRect((int)i); if (m_istorepos) rect.OffsetRect(0, m_offsetys[i]); - rect.InflateRect(DPI_SCALE(m_style.hilite_padding_x), - DPI_SCALE(m_style.hilite_padding_y)); + rect.InflateRect(m_cachedStyle.hilite_padding_x, + m_cachedStyle.hilite_padding_y); if (rect.PtInRect(point)) { if (i != m_ctx.cinfo.highlighted) { if (m_style.hover_type == UIStyle::HoverType::HILITE) { @@ -501,8 +512,8 @@ void WeaselPanel::_HighlightText(Gdiplus::Graphics& g_back, NOT_FULLSCREENLAYOUT(m_style)) hiliteBackPath = new GraphicsRoundRectPath( rc, - DPI_SCALE(m_style.round_corner_ex) - - (DPI_SCALE(m_style.border) % 2 ? DPI_SCALE(m_style.border) / 2 : 0), + m_cachedStyle.round_corner_ex - + (m_cachedStyle.border % 2 ? m_cachedStyle.border / 2 : 0), rd.IsTopLeftNeedToRound, rd.IsTopRightNeedToRound, rd.IsBottomRightNeedToRound, rd.IsBottomLeftNeedToRound); else // background or current candidate background not out of window @@ -510,12 +521,12 @@ void WeaselPanel::_HighlightText(Gdiplus::Graphics& g_back, hiliteBackPath = new GraphicsRoundRectPath(rc, radius); // 必须shadow_color都是非完全透明色才做绘制, 全屏状态不绘制阴影保证响应速度 - if (DPI_SCALE(m_style.shadow_radius) && COLORNOTTRANSPARENT(shadowColor) && + if (m_style.shadow_radius && COLORNOTTRANSPARENT(shadowColor) && NOT_FULLSCREENLAYOUT(m_style)) { - CRect rect(blurMarginX + DPI_SCALE(m_style.shadow_offset_x), - blurMarginY + DPI_SCALE(m_style.shadow_offset_y), - rc.Width() + blurMarginX + DPI_SCALE(m_style.shadow_offset_x), - rc.Height() + blurMarginY + DPI_SCALE(m_style.shadow_offset_y)); + CRect rect(blurMarginX + m_cachedStyle.shadow_offset_x, + blurMarginY + m_cachedStyle.shadow_offset_y, + rc.Width() + blurMarginX + m_cachedStyle.shadow_offset_x, + rc.Height() + blurMarginY + m_cachedStyle.shadow_offset_y); BYTE r = GetRValue(shadowColor); BYTE g = GetGValue(shadowColor); BYTE b = GetBValue(shadowColor); @@ -529,17 +540,16 @@ void WeaselPanel::_HighlightText(Gdiplus::Graphics& g_back, Gdiplus::Graphics g_shadow(pBitmapDropShadow); g_shadow.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality); // dropshadow, draw a roundrectangle to blur - if (DPI_SCALE(m_style.shadow_offset_x) != 0 || - DPI_SCALE(m_style.shadow_offset_y) != 0) { + if (m_style.shadow_offset_x != 0 || m_style.shadow_offset_y != 0) { GraphicsRoundRectPath shadow_path(rect, radius); Gdiplus::SolidBrush shadow_brush(shadow_color); g_shadow.FillPath(&shadow_brush, &shadow_path); } // round shadow, draw multilines as base round line else { - int step = alpha / DPI_SCALE(m_style.shadow_radius) / 2; + int step = alpha / m_cachedStyle.shadow_radius / 2; Gdiplus::Pen pen_shadow(shadow_color, (Gdiplus::REAL)1); - for (int i = 0; i < DPI_SCALE(m_style.shadow_radius); i++) { + for (int i = 0; i < m_cachedStyle.shadow_radius; i++) { GraphicsRoundRectPath round_path(rect, radius + 1 + i); g_shadow.DrawPath(&pen_shadow, &round_path); shadow_color = Gdiplus::Color::MakeARGB(alpha - i * step, r, g, b); @@ -547,8 +557,8 @@ void WeaselPanel::_HighlightText(Gdiplus::Graphics& g_back, rect.InflateRect(1, 1); } } - DoGaussianBlur(pBitmapDropShadow, (float)DPI_SCALE(m_style.shadow_radius), - (float)DPI_SCALE(m_style.shadow_radius)); + DoGaussianBlur(pBitmapDropShadow, (float)m_cachedStyle.shadow_radius, + (float)m_cachedStyle.shadow_radius); g_back.DrawImage(pBitmapDropShadow, rc.left - blurMarginX, rc.top - blurMarginY); @@ -565,13 +575,12 @@ void WeaselPanel::_HighlightText(Gdiplus::Graphics& g_back, g_back.FillPath(&back_brush, hiliteBackPath); } // draw border, for bordercolor not transparent and border valid - if (COLORNOTTRANSPARENT(bordercolor) && DPI_SCALE(m_style.border) > 0) { + if (COLORNOTTRANSPARENT(bordercolor) && m_style.border > 0) { Gdiplus::Color border_color = GDPCOLOR_FROM_COLORREF(bordercolor); - Gdiplus::Pen gPenBorder(border_color, - (Gdiplus::REAL)DPI_SCALE(m_style.border)); + Gdiplus::Pen gPenBorder(border_color, (Gdiplus::REAL)m_cachedStyle.border); // candidate window border if (type == BackType::BACKGROUND) { - GraphicsRoundRectPath bgPath(rc, DPI_SCALE(m_style.round_corner_ex)); + GraphicsRoundRectPath bgPath(rc, m_cachedStyle.round_corner_ex); g_back.DrawPath(&gPenBorder, &bgPath); } else if (type != BackType::TEXT) // hilited_candidate_border / candidate_border @@ -615,9 +624,9 @@ bool WeaselPanel::_DrawPreedit(const Text& text, _TextOut(rc_before, str_before.c_str(), str_before.length(), m_style.text_color, txtFormat); if (m_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT) - y += beforeSz.cy + DPI_SCALE(m_style.hilite_spacing); + y += beforeSz.cy + m_cachedStyle.hilite_spacing; else - x += beforeSz.cx + DPI_SCALE(m_style.hilite_spacing); + x += beforeSz.cx + m_cachedStyle.hilite_spacing; } { // zzz[yyy] @@ -632,9 +641,9 @@ bool WeaselPanel::_DrawPreedit(const Text& text, _TextOut(rc_hi, str_highlight.c_str(), str_highlight.length(), m_style.hilited_text_color, txtFormat); if (m_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT) - y += rc_hi.Height() + DPI_SCALE(m_style.hilite_spacing); + y += rc_hi.Height() + m_cachedStyle.hilite_spacing; else - x += rc_hi.Width() + DPI_SCALE(m_style.hilite_spacing); + x += rc_hi.Width() + m_cachedStyle.hilite_spacing; } if (range.end < static_cast(t.length())) { // zzz[yyy]xxx @@ -698,9 +707,9 @@ bool WeaselPanel::_DrawPreeditBack(const Text& text, if (range.start > 0) { if (m_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT) - y += beforeSz.cy + DPI_SCALE(m_style.hilite_spacing); + y += beforeSz.cy + m_cachedStyle.hilite_spacing; else - x += beforeSz.cx + DPI_SCALE(m_style.hilite_spacing); + x += beforeSz.cx + m_cachedStyle.hilite_spacing; } { CRect rc_hi; @@ -720,16 +729,16 @@ bool WeaselPanel::_DrawPreeditBack(const Text& text, rc_hi.InflateRect((STATUS_ICON_SIZE - hilitedSz.cx) / 2, 0); } - rc_hi.InflateRect(DPI_SCALE(m_style.hilite_padding_x), - DPI_SCALE(m_style.hilite_padding_y)); + rc_hi.InflateRect(m_cachedStyle.hilite_padding_x, + m_cachedStyle.hilite_padding_y); IsToRoundStruct rd = m_layout->GetTextRoundInfo(); if (m_istorepos) { std::swap(rd.IsTopLeftNeedToRound, rd.IsBottomLeftNeedToRound); std::swap(rd.IsTopRightNeedToRound, rd.IsBottomRightNeedToRound); } _HighlightText(g_back, rc_hi, m_style.hilited_back_color, - m_style.hilited_shadow_color, - DPI_SCALE(m_style.round_corner), BackType::TEXT, rd); + m_style.hilited_shadow_color, m_cachedStyle.round_corner, + BackType::TEXT, rd); } } drawn = true; @@ -767,10 +776,10 @@ bool WeaselPanel::_DrawCandidates(Gdiplus::Graphics& g_back, bool back) { rect.OffsetRect(0, m_offsetys[i]); ReconfigRoundInfo(rd, i, m_candidateCount); } - rect.InflateRect(DPI_SCALE(m_style.hilite_padding_x), - DPI_SCALE(m_style.hilite_padding_y)); + rect.InflateRect(m_cachedStyle.hilite_padding_x, + m_cachedStyle.hilite_padding_y); _HighlightText(g_back, rect, 0x00000000, m_style.candidate_shadow_color, - DPI_SCALE(m_style.round_corner), bkType, rd); + m_cachedStyle.round_corner, bkType, rd); drawn = true; } } @@ -786,10 +795,10 @@ bool WeaselPanel::_DrawCandidates(Gdiplus::Graphics& g_back, bool back) { rect.OffsetRect(0, m_offsetys[i]); ReconfigRoundInfo(rd, i, m_candidateCount); } - rect.InflateRect(DPI_SCALE(m_style.hilite_padding_x), - DPI_SCALE(m_style.hilite_padding_y)); + rect.InflateRect(m_cachedStyle.hilite_padding_x, + m_cachedStyle.hilite_padding_y); _HighlightText(g_back, rect, m_style.candidate_back_color, 0x00000000, - DPI_SCALE(m_style.round_corner), bkType, rd, + m_cachedStyle.round_corner, bkType, rd, m_style.candidate_border_color); drawn = true; } @@ -802,12 +811,12 @@ bool WeaselPanel::_DrawCandidates(Gdiplus::Graphics& g_back, bool back) { rect.OffsetRect(0, m_offsetys[m_hoverIndex]); ReconfigRoundInfo(rd, m_hoverIndex, m_candidateCount); } - rect.InflateRect(DPI_SCALE(m_style.hilite_padding_x), - DPI_SCALE(m_style.hilite_padding_y)); + rect.InflateRect(m_cachedStyle.hilite_padding_x, + m_cachedStyle.hilite_padding_y); _HighlightText(g_back, rect, HALF_ALPHA_COLOR(m_style.hilited_candidate_back_color), HALF_ALPHA_COLOR(m_style.hilited_candidate_shadow_color), - DPI_SCALE(m_style.round_corner), bkType, rd, + m_cachedStyle.round_corner, bkType, rd, HALF_ALPHA_COLOR(m_style.hilited_candidate_border_color)); } // draw highlighted background and shadow @@ -819,19 +828,18 @@ bool WeaselPanel::_DrawCandidates(Gdiplus::Graphics& g_back, bool back) { rect.OffsetRect(0, m_offsetys[m_ctx.cinfo.highlighted]); ReconfigRoundInfo(rd, m_ctx.cinfo.highlighted, m_candidateCount); } - rect.InflateRect(DPI_SCALE(m_style.hilite_padding_x), - DPI_SCALE(m_style.hilite_padding_y)); + rect.InflateRect(m_cachedStyle.hilite_padding_x, + m_cachedStyle.hilite_padding_y); _HighlightText(g_back, rect, m_style.hilited_candidate_back_color, markSt ? m_style.hilited_candidate_shadow_color : 0, - DPI_SCALE(m_style.round_corner), bkType, rd, + m_cachedStyle.round_corner, bkType, rd, m_style.hilited_candidate_border_color); if (m_style.mark_text.empty() && COLORNOTTRANSPARENT(m_style.hilited_mark_color)) { - int height = - min(rect.Height() - DPI_SCALE(m_style.hilite_padding_y) * 2, - rect.Height() - DPI_SCALE(m_style.round_corner) * 2); - int width = min(rect.Width() - DPI_SCALE(m_style.hilite_padding_x) * 2, - rect.Width() - DPI_SCALE(m_style.round_corner) * 2); + int height = min(rect.Height() - m_cachedStyle.hilite_padding_y * 2, + rect.Height() - m_cachedStyle.round_corner * 2); + int width = min(rect.Width() - m_cachedStyle.hilite_padding_x * 2, + rect.Width() - m_cachedStyle.round_corner * 2); width = min(width, static_cast(rect.Width() * 0.618)); height = min(height, static_cast(rect.Height() * 0.618)); if (bar_scale_ != 1.0f) { @@ -908,8 +916,8 @@ bool WeaselPanel::_DrawCandidates(Gdiplus::Graphics& g_back, bool back) { CRect rc = m_layout->GetHighlightRect(); if (m_istorepos) rc.OffsetRect(0, m_offsetys[m_ctx.cinfo.highlighted]); - rc.InflateRect(DPI_SCALE(m_style.hilite_padding_x), - DPI_SCALE(m_style.hilite_padding_y)); + rc.InflateRect(m_cachedStyle.hilite_padding_x, + m_cachedStyle.hilite_padding_y); int vgap = m_layout->mark_height ? (rc.Height() - m_layout->mark_height) / 2 : 0; @@ -917,17 +925,15 @@ bool WeaselPanel::_DrawCandidates(Gdiplus::Graphics& g_back, bool back) { m_layout->mark_width ? (rc.Width() - m_layout->mark_width) / 2 : 0; CRect hlRc; if (m_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT) - hlRc = CRect(rc.left + hgap, - rc.top + DPI_SCALE(m_style.hilite_padding_y), - rc.left + hgap + m_layout->mark_width, - rc.top + DPI_SCALE(m_style.hilite_padding_y) + - m_layout->mark_height); + hlRc = CRect( + rc.left + hgap, rc.top + m_cachedStyle.hilite_padding_y, + rc.left + hgap + m_layout->mark_width, + rc.top + m_cachedStyle.hilite_padding_y + m_layout->mark_height); else - hlRc = CRect(rc.left + DPI_SCALE(m_style.hilite_padding_x), - rc.top + vgap, - rc.left + DPI_SCALE(m_style.hilite_padding_x) + - m_layout->mark_width, - rc.bottom - vgap); + hlRc = CRect( + rc.left + m_cachedStyle.hilite_padding_x, rc.top + vgap, + rc.left + m_cachedStyle.hilite_padding_x + m_layout->mark_width, + rc.bottom - vgap); _TextOut(hlRc, m_style.mark_text.c_str(), m_style.mark_text.length(), m_style.hilited_mark_color, pDWR->pTextFormat.Get()); } @@ -980,7 +986,7 @@ void WeaselPanel::DoPaint(CDCHandle dc) { btmys[m_candidateCount - i - 1] - base_gap - rects[i].bottom; else m_offsetys[i] = (rects[i - 1].top + m_offsetys[i - 1] - - DPI_SCALE(m_style.candidate_spacing)) - + m_cachedStyle.candidate_spacing) - rects[i].bottom; } } @@ -991,7 +997,7 @@ void WeaselPanel::DoPaint(CDCHandle dc) { (m_style.inline_preedit && (m_candidateCount || !m_ctx.aux.empty()))) { CRect backrc = m_layout->GetContentRect(); _HighlightText(g_back, backrc, m_style.back_color, m_style.shadow_color, - DPI_SCALE(m_style.round_corner_ex), BackType::BACKGROUND, + m_cachedStyle.round_corner_ex, BackType::BACKGROUND, IsToRoundStruct(), m_style.border_color); } if (!m_ctx.aux.str.empty()) { @@ -1194,13 +1200,13 @@ void WeaselPanel::_RepositionWindow(const bool& adj) { rcWorkArea.bottom -= height; int x = m_inputPos.left; int y = m_inputPos.bottom; - if (DPI_SCALE(m_style.shadow_radius)) { - x -= (DPI_SCALE(m_style.shadow_offset_x) >= 0 || + if (m_style.shadow_radius) { + x -= (m_cachedStyle.shadow_offset_x >= 0 || COLORTRANSPARENT(m_style.shadow_color)) ? m_layout->offsetX : (m_layout->offsetX / 2); if (adj) - y -= (DPI_SCALE(m_style.shadow_offset_y) > 0 || + y -= (m_cachedStyle.shadow_offset_y > 0 || COLORTRANSPARENT(m_style.shadow_color)) ? m_layout->offsetY : (m_layout->offsetY / 2); @@ -1209,7 +1215,7 @@ void WeaselPanel::_RepositionWindow(const bool& adj) { if (m_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT && !m_style.vertical_text_left_to_right) { x += m_layout->offsetX - width; - if (DPI_SCALE(m_style.shadow_offset_x) < 0) + if (m_cachedStyle.shadow_offset_x < 0) x += m_layout->offsetX; } if (adj) @@ -1223,13 +1229,12 @@ void WeaselPanel::_RepositionWindow(const bool& adj) { if (!m_sticky) m_sticky = true; y = m_inputPos.top - height - 6; // over workarea bottom - if (DPI_SCALE(m_style.shadow_radius) && - DPI_SCALE(m_style.shadow_offset_y) > 0) - y -= DPI_SCALE(m_style.shadow_offset_y); + if (m_style.shadow_radius && m_style.shadow_offset_y > 0) + y -= m_cachedStyle.shadow_offset_y; m_istorepos = (m_style.vertical_auto_reverse && m_style.layout_type == UIStyle::LAYOUT_VERTICAL); - if (DPI_SCALE(m_style.shadow_radius) > 0) - y += (DPI_SCALE(m_style.shadow_offset_y) < 0 || + if (m_style.shadow_radius > 0) + y += (m_cachedStyle.shadow_offset_y < 0 || COLORTRANSPARENT(m_style.shadow_color)) ? m_layout->offsetY : (m_layout->offsetY / 2); diff --git a/WeaselUI/WeaselPanel.h b/WeaselUI/WeaselPanel.h index 76f709dd5..31d3a487c 100644 --- a/WeaselUI/WeaselPanel.h +++ b/WeaselUI/WeaselPanel.h @@ -150,4 +150,20 @@ class WeaselPanel int m_hoverIndex = -1; HMONITOR m_hMonitor = NULL; bool m_redraw_by_monitor_change = false; + + struct CachedStyle { + int margin_x; + int margin_y; + int hilite_padding_x; + int hilite_padding_y; + int shadow_radius; + int shadow_offset_x; + int shadow_offset_y; + int round_corner; + int round_corner_ex; + int border; + int hilite_spacing; + int candidate_spacing; + int spacing; + } m_cachedStyle; }; From 0206aac1aaf84fd1ec462309abf18b5e75fe6cb4 Mon Sep 17 00:00:00 2001 From: fxliang Date: Sun, 8 Feb 2026 00:28:52 +0800 Subject: [PATCH 07/25] fix(WeaselUI): fix clipboard issue --- WeaselUI/WeaselPanel.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index 8bdf9bfb6..638019bb5 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -231,12 +231,10 @@ static HBITMAP CopyDCToBitmap(HDC hDC, LPRECT lpRect) { hMemDC = CreateCompatibleDC(hDC); hBitmap = CreateCompatibleBitmap(hDC, nWidth, nHeight); hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap); - StretchBlt(hMemDC, 0, 0, nWidth, nHeight, hDC, nX, nY, nWidth, nHeight, - SRCCOPY); + BitBlt(hMemDC, 0, 0, nWidth, nHeight, hDC, nX, nY, SRCCOPY); hBitmap = (HBITMAP)SelectObject(hMemDC, hOldBitmap); DeleteDC(hMemDC); - DeleteObject(hOldBitmap); return hBitmap; } @@ -248,13 +246,15 @@ void WeaselPanel::_CaptureRect(CRect& rect) { rect.OffsetRect(WindowPosAtScreen); // capture input window if (OpenClipboard()) { - HBITMAP bmp = CopyDCToBitmap(ScreenDC, LPRECT(rect)); EmptyClipboard(); - SetClipboardData(CF_BITMAP, bmp); + HBITMAP bmp = CopyDCToBitmap(ScreenDC, LPRECT(rect)); + if (bmp) { + if (!SetClipboardData(CF_BITMAP, bmp)) + DeleteObject(bmp); + } CloseClipboard(); - DeleteObject(bmp); } - ReleaseDC(ScreenDC); + ::ReleaseDC(NULL, ScreenDC); } LRESULT WeaselPanel::OnMouseActivate(UINT uMsg, From f79bfbba738c8b6a83713da4d8eaef771576f0ba Mon Sep 17 00:00:00 2001 From: fxliang Date: Sun, 8 Feb 2026 22:37:51 +0800 Subject: [PATCH 08/25] perf(WeaselUI): _WeaselPanel::_DrawCandidates --- WeaselUI/WeaselPanel.cpp | 75 +++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 44 deletions(-) diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index 638019bb5..499436e9f 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -765,74 +765,61 @@ bool WeaselPanel::_DrawCandidates(Gdiplus::Graphics& g_back, bool back) { CRect rect; // draw back color and shadow color, with gdi+ if (back) { + std::vector cachedRects(m_candidateCount); + std::vector cachedRounds(m_candidateCount); + + for (int i = 0; i < m_candidateCount; ++i) { + cachedRects[i] = m_layout->GetCandidateRect(i); + cachedRounds[i] = m_layout->GetRoundInfo(i); + if (m_istorepos) { + cachedRects[i].OffsetRect(0, m_offsetys[i]); + ReconfigRoundInfo(cachedRounds[i], i, m_candidateCount); + } + cachedRects[i].InflateRect(m_cachedStyle.hilite_padding_x, + m_cachedStyle.hilite_padding_y); + } + // if candidate_shadow_color not transparent, draw candidate shadow first if (COLORNOTTRANSPARENT(m_style.candidate_shadow_color)) { - for (auto i = 0; i < m_candidateCount && i < MAX_CANDIDATES_COUNT; ++i) { + for (int i = 0; i < m_candidateCount; ++i) { if (i == m_ctx.cinfo.highlighted || i == m_hoverIndex) continue; // draw non hilited candidates only - rect = m_layout->GetCandidateRect((int)i); - IsToRoundStruct rd = m_layout->GetRoundInfo(i); - if (m_istorepos) { - rect.OffsetRect(0, m_offsetys[i]); - ReconfigRoundInfo(rd, i, m_candidateCount); - } - rect.InflateRect(m_cachedStyle.hilite_padding_x, - m_cachedStyle.hilite_padding_y); - _HighlightText(g_back, rect, 0x00000000, m_style.candidate_shadow_color, - m_cachedStyle.round_corner, bkType, rd); + _HighlightText(g_back, cachedRects[i], 0x00000000, + m_style.candidate_shadow_color, + m_cachedStyle.round_corner, bkType, cachedRounds[i]); drawn = true; } } // draw non highlighted candidates, without shadow if ((COLORNOTTRANSPARENT(m_style.candidate_back_color) || COLORNOTTRANSPARENT(m_style.candidate_border_color))) { - for (auto i = 0; i < m_candidateCount && i < MAX_CANDIDATES_COUNT; ++i) { + for (int i = 0; i < m_candidateCount; ++i) { if (i == m_ctx.cinfo.highlighted || i == m_hoverIndex) continue; - rect = m_layout->GetCandidateRect((int)i); - IsToRoundStruct rd = m_layout->GetRoundInfo(i); - if (m_istorepos) { - rect.OffsetRect(0, m_offsetys[i]); - ReconfigRoundInfo(rd, i, m_candidateCount); - } - rect.InflateRect(m_cachedStyle.hilite_padding_x, - m_cachedStyle.hilite_padding_y); - _HighlightText(g_back, rect, m_style.candidate_back_color, 0x00000000, - m_cachedStyle.round_corner, bkType, rd, - m_style.candidate_border_color); + _HighlightText(g_back, cachedRects[i], m_style.candidate_back_color, + 0x00000000, m_cachedStyle.round_corner, bkType, + cachedRounds[i], m_style.candidate_border_color); drawn = true; } } // draw semi-hilite background and shadow - if (m_hoverIndex >= 0) { - rect = m_layout->GetCandidateRect(m_hoverIndex); - IsToRoundStruct rd = m_layout->GetRoundInfo(m_hoverIndex); - if (m_istorepos) { - rect.OffsetRect(0, m_offsetys[m_hoverIndex]); - ReconfigRoundInfo(rd, m_hoverIndex, m_candidateCount); - } - rect.InflateRect(m_cachedStyle.hilite_padding_x, - m_cachedStyle.hilite_padding_y); - _HighlightText(g_back, rect, + if (m_hoverIndex >= 0 && m_hoverIndex < m_candidateCount) { + _HighlightText(g_back, cachedRects[m_hoverIndex], HALF_ALPHA_COLOR(m_style.hilited_candidate_back_color), HALF_ALPHA_COLOR(m_style.hilited_candidate_shadow_color), - m_cachedStyle.round_corner, bkType, rd, + m_cachedStyle.round_corner, bkType, + cachedRounds[m_hoverIndex], HALF_ALPHA_COLOR(m_style.hilited_candidate_border_color)); } // draw highlighted background and shadow - { - rect = m_layout->GetHighlightRect(); + if (m_ctx.cinfo.highlighted < m_candidateCount) { + const auto& rect = cachedRects[m_ctx.cinfo.highlighted]; bool markSt = bar_scale_ == 1.0 || (!m_style.mark_text.empty()); - IsToRoundStruct rd = m_layout->GetRoundInfo(m_ctx.cinfo.highlighted); - if (m_istorepos) { - rect.OffsetRect(0, m_offsetys[m_ctx.cinfo.highlighted]); - ReconfigRoundInfo(rd, m_ctx.cinfo.highlighted, m_candidateCount); - } - rect.InflateRect(m_cachedStyle.hilite_padding_x, - m_cachedStyle.hilite_padding_y); + _HighlightText(g_back, rect, m_style.hilited_candidate_back_color, markSt ? m_style.hilited_candidate_shadow_color : 0, - m_cachedStyle.round_corner, bkType, rd, + m_cachedStyle.round_corner, bkType, + cachedRounds[m_ctx.cinfo.highlighted], m_style.hilited_candidate_border_color); if (m_style.mark_text.empty() && COLORNOTTRANSPARENT(m_style.hilited_mark_color)) { From 4354753b50fc42b99f6f4987b4d19d68905e9697 Mon Sep 17 00:00:00 2001 From: fxliang Date: Sun, 8 Feb 2026 23:05:20 +0800 Subject: [PATCH 09/25] refactor(WeaselUI): simplify WeaselPanel::DoPaint --- WeaselUI/WeaselPanel.cpp | 135 +++++++++++++++++++++------------------ WeaselUI/WeaselPanel.h | 2 + 2 files changed, 75 insertions(+), 62 deletions(-) diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index 499436e9f..2a047e0b0 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -135,7 +135,8 @@ void WeaselPanel::_CreateLayout() { void WeaselPanel::Refresh() { bool should_show_icon = (m_status.ascii_mode || !m_status.composing || !m_ctx.aux.empty()); - m_candidateCount = (BYTE)m_ctx.cinfo.candies.size(); + m_candidateCount = + min((BYTE)m_ctx.cinfo.candies.size(), (BYTE)MAX_CANDIDATES_COUNT); // When the candidate window changes from having content to having no content, // reset the sticky state if (m_lastCandidateCount > 0 && m_candidateCount == 0) { @@ -929,6 +930,73 @@ bool WeaselPanel::_DrawCandidates(Gdiplus::Graphics& g_back, bool back) { return drawn; } +bool WeaselPanel::_DrawStatusIcon(CDCHandle memDC) { + // status icon (I guess Metro IME stole my idea :) + if (m_layout->ShouldDisplayStatusIcon()) { + // decide if custom schema zhung icon to show + LoadIconNecessary(m_current_zhung_icon, m_style.current_zhung_icon, + m_iconEnabled, IDI_ZH); + LoadIconNecessary(m_current_ascii_icon, m_style.current_ascii_icon, + m_iconAlpha, IDI_EN); + LoadIconNecessary(m_current_half_icon, m_style.current_half_icon, + m_iconHalf, IDI_HALF_SHAPE); + LoadIconNecessary(m_current_full_icon, m_style.current_full_icon, + m_iconFull, IDI_FULL_SHAPE); + CRect iconRect(m_layout->GetStatusIconRect()); + if (m_istorepos && !m_ctx.aux.str.empty()) + iconRect.OffsetRect(0, m_offsety_aux); + else if (m_istorepos && !m_layout->IsInlinePreedit() && + !m_ctx.preedit.str.empty()) + iconRect.OffsetRect(0, m_offsety_preedit); + + CIcon& icon(m_status.disabled ? m_iconDisabled + : m_status.ascii_mode + ? m_iconAlpha + : (m_status.type == SCHEMA + ? m_iconEnabled + : (m_status.full_shape ? m_iconFull : m_iconHalf))); + memDC.DrawIconEx(iconRect.left, iconRect.top, icon, 0, 0); + return true; + } + return false; +} + +void WeaselPanel::_UpdateOffsets(const CRect& auxrc, const CRect& preeditrc) { + if (m_istorepos) { + std::unique_ptr rects(new CRect[m_candidateCount]); + std::unique_ptr btmys(new int[m_candidateCount]); + for (auto i = 0; i < m_candidateCount && i < MAX_CANDIDATES_COUNT; ++i) { + rects[i] = m_layout->GetCandidateRect(i); + btmys[i] = rects[i].bottom; + } + if (m_candidateCount) { + if (!m_layout->IsInlinePreedit() && !m_ctx.preedit.str.empty()) + m_offsety_preedit = + rects[m_candidateCount - 1].bottom - preeditrc.bottom; + if (!m_ctx.aux.str.empty()) + m_offsety_aux = rects[m_candidateCount - 1].bottom - auxrc.bottom; + } else { + m_offsety_preedit = 0; + m_offsety_aux = 0; + } + int base_gap = 0; + if (!m_ctx.aux.str.empty()) + base_gap = auxrc.Height() + m_style.spacing; + else if (!m_layout->IsInlinePreedit() && !m_ctx.preedit.str.empty()) + base_gap = preeditrc.Height() + m_style.spacing; + + for (auto i = 0; i < m_candidateCount && i < MAX_CANDIDATES_COUNT; ++i) { + if (i == 0) + m_offsetys[i] = + btmys[m_candidateCount - i - 1] - base_gap - rects[i].bottom; + else + m_offsetys[i] = (rects[i - 1].top + m_offsetys[i - 1] - + m_cachedStyle.candidate_spacing) - + rects[i].bottom; + } + } +} + // draw client area void WeaselPanel::DoPaint(CDCHandle dc) { // turn off WS_EX_TRANSPARENT, for better resp performance @@ -940,43 +1008,12 @@ void WeaselPanel::DoPaint(CDCHandle dc) { HBITMAP memBitmap = ::CreateCompatibleBitmap(hdc, rcw.Width(), rcw.Height()); ::SelectObject(memDC, memBitmap); ReleaseDC(hdc); + bool drawn = false; if (!hide_candidates) { CRect auxrc = m_layout->GetAuxiliaryRect(); CRect preeditrc = m_layout->GetPreeditRect(); - if (m_istorepos) { - std::unique_ptr rects(new CRect[m_candidateCount]); - std::unique_ptr btmys(new int[m_candidateCount]); - for (auto i = 0; i < m_candidateCount && i < MAX_CANDIDATES_COUNT; ++i) { - rects[i] = m_layout->GetCandidateRect(i); - btmys[i] = rects[i].bottom; - } - if (m_candidateCount) { - if (!m_layout->IsInlinePreedit() && !m_ctx.preedit.str.empty()) - m_offsety_preedit = - rects[m_candidateCount - 1].bottom - preeditrc.bottom; - if (!m_ctx.aux.str.empty()) - m_offsety_aux = rects[m_candidateCount - 1].bottom - auxrc.bottom; - } else { - m_offsety_preedit = 0; - m_offsety_aux = 0; - } - int base_gap = 0; - if (!m_ctx.aux.str.empty()) - base_gap = auxrc.Height() + m_style.spacing; - else if (!m_layout->IsInlinePreedit() && !m_ctx.preedit.str.empty()) - base_gap = preeditrc.Height() + m_style.spacing; - - for (auto i = 0; i < m_candidateCount && i < MAX_CANDIDATES_COUNT; ++i) { - if (i == 0) - m_offsetys[i] = - btmys[m_candidateCount - i - 1] - base_gap - rects[i].bottom; - else - m_offsetys[i] = (rects[i - 1].top + m_offsetys[i - 1] - - m_cachedStyle.candidate_spacing) - - rects[i].bottom; - } - } + _UpdateOffsets(auxrc, preeditrc); // background and candidates back, hilite back drawing start Gdiplus::Graphics g_back(memDC); g_back.SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeHighQuality); @@ -1023,34 +1060,8 @@ void WeaselPanel::DoPaint(CDCHandle dc) { } // end texts drawing - // status icon (I guess Metro IME stole my idea :) - if (m_layout->ShouldDisplayStatusIcon()) { - // decide if custom schema zhung icon to show - LoadIconNecessary(m_current_zhung_icon, m_style.current_zhung_icon, - m_iconEnabled, IDI_ZH); - LoadIconNecessary(m_current_ascii_icon, m_style.current_ascii_icon, - m_iconAlpha, IDI_EN); - LoadIconNecessary(m_current_half_icon, m_style.current_half_icon, - m_iconHalf, IDI_HALF_SHAPE); - LoadIconNecessary(m_current_full_icon, m_style.current_full_icon, - m_iconFull, IDI_FULL_SHAPE); - CRect iconRect(m_layout->GetStatusIconRect()); - if (m_istorepos && !m_ctx.aux.str.empty()) - iconRect.OffsetRect(0, m_offsety_aux); - else if (m_istorepos && !m_layout->IsInlinePreedit() && - !m_ctx.preedit.str.empty()) - iconRect.OffsetRect(0, m_offsety_preedit); - - CIcon& icon( - m_status.disabled ? m_iconDisabled - : m_status.ascii_mode - ? m_iconAlpha - : (m_status.type == SCHEMA - ? m_iconEnabled - : (m_status.full_shape ? m_iconFull : m_iconHalf))); - memDC.DrawIconEx(iconRect.left, iconRect.top, icon, 0, 0); - drawn = true; - } + drawn |= _DrawStatusIcon(memDC); + /* Nothing drawn, hide candidate window */ if (!drawn) ShowWindow(SW_HIDE); diff --git a/WeaselUI/WeaselPanel.h b/WeaselUI/WeaselPanel.h index 31d3a487c..c811df6b2 100644 --- a/WeaselUI/WeaselPanel.h +++ b/WeaselUI/WeaselPanel.h @@ -89,6 +89,8 @@ class WeaselPanel Gdiplus::Graphics& g_back, const CRect& rc); bool _DrawCandidates(Gdiplus::Graphics& g_back, bool back = false); + bool _DrawStatusIcon(CDCHandle memDC); + void _UpdateOffsets(const CRect& auxrc, const CRect& preeditrc); void _HighlightText(Gdiplus::Graphics& g_back, const CRect& rc, const COLORREF& color, From 8edd37990b86b19a53dc1758405fbc787b7efd8c Mon Sep 17 00:00:00 2001 From: fxliang Date: Sun, 8 Feb 2026 23:16:03 +0800 Subject: [PATCH 10/25] refactor(WeaselUI): add _UpdateWindowVisibility for Refresh --- WeaselUI/WeaselPanel.cpp | 9 +++++++-- WeaselUI/WeaselPanel.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index 2a047e0b0..06ebfbc52 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -131,8 +131,7 @@ void WeaselPanel::_CreateLayout() { m_layout = layout; } -// 更新界面 -void WeaselPanel::Refresh() { +bool WeaselPanel::_UpdateWindowVisibility() { bool should_show_icon = (m_status.ascii_mode || !m_status.composing || !m_ctx.aux.empty()); m_candidateCount = @@ -161,6 +160,12 @@ void WeaselPanel::Refresh() { (m_style.inline_preedit && m_candidateCount == 0) && !show_tips; hide_candidates = inline_no_candidates || (margin_negative && !show_tips && !show_schema_menu); + return inline_no_candidates; +} + +// 更新界面 +void WeaselPanel::Refresh() { + bool inline_no_candidates = _UpdateWindowVisibility(); // only RedrawWindow if no need to hide candidates window, or // inline_no_candidates diff --git a/WeaselUI/WeaselPanel.h b/WeaselUI/WeaselPanel.h index c811df6b2..4f00a722a 100644 --- a/WeaselUI/WeaselPanel.h +++ b/WeaselUI/WeaselPanel.h @@ -91,6 +91,7 @@ class WeaselPanel bool _DrawCandidates(Gdiplus::Graphics& g_back, bool back = false); bool _DrawStatusIcon(CDCHandle memDC); void _UpdateOffsets(const CRect& auxrc, const CRect& preeditrc); + bool _UpdateWindowVisibility(); void _HighlightText(Gdiplus::Graphics& g_back, const CRect& rc, const COLORREF& color, From 822a7944794fc3358064cc3221e77f65894d82a7 Mon Sep 17 00:00:00 2001 From: fxliang Date: Sun, 8 Feb 2026 23:21:17 +0800 Subject: [PATCH 11/25] refactor(WeaselUI): return if not need to update, in WeaselPanel::_InitFontRes --- WeaselUI/WeaselPanel.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index 06ebfbc52..3c004fddc 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -190,15 +190,19 @@ void WeaselPanel::_InitFontRes(bool forced) { UINT dpiX = 96, dpiY = 96; if (hMonitor) GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + + if (!forced && (pDWR != NULL) && !(m_ostyle != m_style) && (dpiX == dpi)) { + return; + } + // prepare d2d1 resources // if style changed, or dpi changed, or pDWR NULL, re-initialize directwrite // resources - if (forced || (pDWR == NULL) || (m_ostyle != m_style) || (dpiX != dpi)) { - pDWR.reset(); - pDWR = std::make_shared(m_style, dpiX); - pDWR->pRenderTarget->SetTextAntialiasMode( - (D2D1_TEXT_ANTIALIAS_MODE)m_style.antialias_mode); - } + pDWR.reset(); + pDWR = std::make_shared(m_style, dpiX); + pDWR->pRenderTarget->SetTextAntialiasMode( + (D2D1_TEXT_ANTIALIAS_MODE)m_style.antialias_mode); + m_ostyle = m_style; dpi = dpiX; dpiScaleLayout = (float)dpi / 96.0f; From 1a7a91d4967733a61142e8f39e097d90a0d02ad3 Mon Sep 17 00:00:00 2001 From: fxliang Date: Sun, 8 Feb 2026 23:33:02 +0800 Subject: [PATCH 12/25] refactor(WeaselUI): DirectWriteResources::SetBrushColor with init, WeaselPanel::_OffsetRectIfIsToRepos --- WeaselUI/WeaselPanel.cpp | 63 +++++++++++++++------------------------- WeaselUI/WeaselPanel.h | 1 + include/WeaselUI.h | 14 +++++---- 3 files changed, 34 insertions(+), 44 deletions(-) diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index 3c004fddc..c683a2b4b 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -50,6 +50,11 @@ static inline void ReconfigRoundInfo(IsToRoundStruct& rd, } } +void WeaselPanel::_OffsetRectIfIsToRepos(CRect& rect, int offsety) { + if (m_istorepos) + rect.OffsetRect(0, offsety); +} + WeaselPanel::WeaselPanel(weasel::UI& ui) : m_layout(NULL), m_ctx(ui.ctx()), @@ -306,8 +311,7 @@ LRESULT WeaselPanel::OnLeftClickedUp(UINT uMsg, { // select by click CRect rect = m_layout->GetCandidateRect((int)m_ctx.cinfo.highlighted); - if (m_istorepos) - rect.OffsetRect(0, m_offsetys[m_ctx.cinfo.highlighted]); + _OffsetRectIfIsToRepos(rect, m_offsetys[m_ctx.cinfo.highlighted]); rect.InflateRect(m_cachedStyle.hilite_padding_x, m_cachedStyle.hilite_padding_y); if (rect.PtInRect(point)) { @@ -341,8 +345,7 @@ LRESULT WeaselPanel::OnLeftClickedDown(UINT uMsg, // capture if (m_style.click_to_capture) { CRect recth = m_layout->GetCandidateRect((int)m_ctx.cinfo.highlighted); - if (m_istorepos) - recth.OffsetRect(0, m_offsetys[m_ctx.cinfo.highlighted]); + _OffsetRectIfIsToRepos(recth, m_offsetys[m_ctx.cinfo.highlighted]); recth.InflateRect(m_cachedStyle.hilite_padding_x, m_cachedStyle.hilite_padding_y); // capture widow @@ -385,8 +388,7 @@ LRESULT WeaselPanel::OnLeftClickedDown(UINT uMsg, // click prepage if (m_ctx.cinfo.currentPage != 0) { CRect prc = m_layout->GetPrepageRect(); - if (m_istorepos) - prc.OffsetRect(0, m_offsety_preedit); + _OffsetRectIfIsToRepos(prc, m_offsety_preedit); if (prc.PtInRect(point)) { bool nextPage = false; if (_UICallback) @@ -398,8 +400,7 @@ LRESULT WeaselPanel::OnLeftClickedDown(UINT uMsg, // click nextpage if (!m_ctx.cinfo.is_last_page) { CRect prc = m_layout->GetNextpageRect(); - if (m_istorepos) - prc.OffsetRect(0, m_offsety_preedit); + _OffsetRectIfIsToRepos(prc, m_offsety_preedit); if (prc.PtInRect(point)) { bool nextPage = true; if (_UICallback) @@ -412,8 +413,7 @@ LRESULT WeaselPanel::OnLeftClickedDown(UINT uMsg, // select by click relative actions for (size_t i = 0; i < m_candidateCount && i < MAX_CANDIDATES_COUNT; ++i) { CRect rect = m_layout->GetCandidateRect((int)i); - if (m_istorepos) - rect.OffsetRect(0, m_offsetys[i]); + _OffsetRectIfIsToRepos(rect, m_offsetys[i]); rect.InflateRect(m_cachedStyle.hilite_padding_x, m_cachedStyle.hilite_padding_y); if (rect.PtInRect(point)) { @@ -472,8 +472,7 @@ LRESULT WeaselPanel::OnMouseMove(UINT uMsg, for (size_t i = 0; i < m_candidateCount && i < MAX_CANDIDATES_COUNT; ++i) { CRect rect = m_layout->GetCandidateRect((int)i); - if (m_istorepos) - rect.OffsetRect(0, m_offsetys[i]); + _OffsetRectIfIsToRepos(rect, m_offsetys[i]); rect.InflateRect(m_cachedStyle.hilite_padding_x, m_cachedStyle.hilite_padding_y); if (rect.PtInRect(point)) { @@ -680,16 +679,14 @@ bool WeaselPanel::_DrawPreedit(const Text& text, // clickable color / disabled color int color = m_ctx.cinfo.currentPage ? m_style.prevpage_color : m_style.text_color; - if (m_istorepos) - prc.OffsetRect(0, m_offsety_preedit); + _OffsetRectIfIsToRepos(prc, m_offsety_preedit); _TextOut(prc, pre.c_str(), pre.length(), color, txtFormat); CRect nrc = m_layout->GetNextpageRect(); // clickable color / disabled color color = m_ctx.cinfo.is_last_page ? m_style.text_color : m_style.nextpage_color; - if (m_istorepos) - nrc.OffsetRect(0, m_offsety_preedit); + _OffsetRectIfIsToRepos(nrc, m_offsety_preedit); _TextOut(nrc, next.c_str(), next.length(), color, txtFormat); } drawn = true; @@ -881,8 +878,7 @@ bool WeaselPanel::_DrawCandidates(Gdiplus::Graphics& g_back, bool back) { labels, (int)i, m_style.label_text_format.c_str()); if (!label.empty()) { rect = m_layout->GetCandidateLabelRect((int)i); - if (m_istorepos) - rect.OffsetRect(0, m_offsetys[i]); + _OffsetRectIfIsToRepos(rect, m_offsetys[i]); _TextOut(rect, label.c_str(), label.length(), label_text_color, labeltxtFormat.Get()); } @@ -890,8 +886,7 @@ bool WeaselPanel::_DrawCandidates(Gdiplus::Graphics& g_back, bool back) { std::wstring text = candidates.at(i).str; if (!text.empty()) { rect = m_layout->GetCandidateTextRect((int)i); - if (m_istorepos) - rect.OffsetRect(0, m_offsetys[i]); + _OffsetRectIfIsToRepos(rect, m_offsetys[i]); _TextOut(rect, text.c_str(), text.length(), candidate_text_color, txtFormat.Get()); } @@ -899,8 +894,7 @@ bool WeaselPanel::_DrawCandidates(Gdiplus::Graphics& g_back, bool back) { std::wstring comment = comments.at(i).str; if (!comment.empty() && COLORNOTTRANSPARENT(comment_text_color)) { rect = m_layout->GetCandidateCommentRect((int)i); - if (m_istorepos) - rect.OffsetRect(0, m_offsetys[i]); + _OffsetRectIfIsToRepos(rect, m_offsetys[i]); _TextOut(rect, comment.c_str(), comment.length(), comment_text_color, commenttxtFormat.Get()); } @@ -911,8 +905,7 @@ bool WeaselPanel::_DrawCandidates(Gdiplus::Graphics& g_back, bool back) { if (!m_style.mark_text.empty() && COLORNOTTRANSPARENT(m_style.hilited_mark_color)) { CRect rc = m_layout->GetHighlightRect(); - if (m_istorepos) - rc.OffsetRect(0, m_offsetys[m_ctx.cinfo.highlighted]); + _OffsetRectIfIsToRepos(rc, m_offsetys[m_ctx.cinfo.highlighted]); rc.InflateRect(m_cachedStyle.hilite_padding_x, m_cachedStyle.hilite_padding_y); int vgap = m_layout->mark_height @@ -952,11 +945,10 @@ bool WeaselPanel::_DrawStatusIcon(CDCHandle memDC) { LoadIconNecessary(m_current_full_icon, m_style.current_full_icon, m_iconFull, IDI_FULL_SHAPE); CRect iconRect(m_layout->GetStatusIconRect()); - if (m_istorepos && !m_ctx.aux.str.empty()) - iconRect.OffsetRect(0, m_offsety_aux); - else if (m_istorepos && !m_layout->IsInlinePreedit() && - !m_ctx.preedit.str.empty()) - iconRect.OffsetRect(0, m_offsety_preedit); + if (!m_ctx.aux.str.empty()) + _OffsetRectIfIsToRepos(iconRect, m_offsety_aux); + else if (!m_layout->IsInlinePreedit() && !m_ctx.preedit.str.empty()) + _OffsetRectIfIsToRepos(iconRect, m_offsety_preedit); CIcon& icon(m_status.disabled ? m_iconDisabled : m_status.ascii_mode @@ -1034,13 +1026,11 @@ void WeaselPanel::DoPaint(CDCHandle dc) { IsToRoundStruct(), m_style.border_color); } if (!m_ctx.aux.str.empty()) { - if (m_istorepos) - auxrc.OffsetRect(0, m_offsety_aux); + _OffsetRectIfIsToRepos(auxrc, m_offsety_aux); drawn |= _DrawPreeditBack(m_ctx.aux, g_back, auxrc); } if (!m_layout->IsInlinePreedit() && !m_ctx.preedit.str.empty()) { - if (m_istorepos) - preeditrc.OffsetRect(0, m_offsety_preedit); + _OffsetRectIfIsToRepos(preeditrc, m_offsety_preedit); drawn |= _DrawPreeditBack(m_ctx.preedit, g_back, preeditrc); } if (m_candidateCount) @@ -1265,12 +1255,7 @@ void WeaselPanel::_TextOut(const CRect& rc, float g = (float)(GetGValue(inColor)) / 255.0f; float b = (float)(GetBValue(inColor)) / 255.0f; float alpha = (float)((inColor >> 24) & 255) / 255.0f; - HRESULT hr = S_OK; - if (pDWR->pBrush == NULL) { - HR(pDWR->CreateBrush(D2D1::ColorF(r, g, b, alpha))); - } else - pDWR->SetBrushColor(D2D1::ColorF(r, g, b, alpha)); - + HR(pDWR->SetBrushColor(D2D1::ColorF(r, g, b, alpha))); HR(pDWR->CreateTextLayout(psz.c_str(), (int)cch, pTextFormat, (float)rc.Width(), (float)rc.Height())); if (m_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT) { diff --git a/WeaselUI/WeaselPanel.h b/WeaselUI/WeaselPanel.h index 4f00a722a..9626dd323 100644 --- a/WeaselUI/WeaselPanel.h +++ b/WeaselUI/WeaselPanel.h @@ -107,6 +107,7 @@ class WeaselPanel IDWriteTextFormat1* const pTextFormat = NULL); void _LayerUpdate(const CRect& rc, CDCHandle dc); + void _OffsetRectIfIsToRepos(CRect& rect, int offsety); weasel::Layout* m_layout; weasel::Context& m_ctx; diff --git a/include/WeaselUI.h b/include/WeaselUI.h index 951da6656..c366013bd 100755 --- a/include/WeaselUI.h +++ b/include/WeaselUI.h @@ -139,12 +139,16 @@ class DirectWriteResources { pRenderTarget->DrawTextLayout(point, pTextLayout.Get(), pBrush.Get(), D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT); } - HRESULT CreateBrush(const D2D1_COLOR_F& color) { - return pRenderTarget->CreateSolidColorBrush( - color, pBrush.ReleaseAndGetAddressOf()); - } void ResetLayout() { pTextLayout.Reset(); } - void SetBrushColor(const D2D1_COLOR_F& color) { pBrush->SetColor(color); } + HRESULT SetBrushColor(const D2D1_COLOR_F& color) { + if (pBrush) { + pBrush->SetColor(color); + return S_OK; + } else { + return pRenderTarget->CreateSolidColorBrush( + color, pBrush.ReleaseAndGetAddressOf()); + } + } void SetDpi(const UINT& dpi); float dpiScaleFontPoint, dpiScaleLayout; From 805f1c450034a8ae6e77c1094deb15f6479d1448 Mon Sep 17 00:00:00 2001 From: fxliang Date: Sun, 8 Feb 2026 23:35:22 +0800 Subject: [PATCH 13/25] fix(WeaselUI): memory leak risk in WeaselPanel::_HighlightText --- WeaselUI/WeaselPanel.cpp | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index c683a2b4b..58573ecad 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -529,7 +529,7 @@ void WeaselPanel::_HighlightText(Gdiplus::Graphics& g_back, // background hiliteBackPath = new GraphicsRoundRectPath(rc, radius); - // 必须shadow_color都是非完全透明色才做绘制, 全屏状态不绘制阴影保证响应速度 + // 必须shadow_color都是非完全透明色才做绘制 if (m_style.shadow_radius && COLORNOTTRANSPARENT(shadowColor) && NOT_FULLSCREENLAYOUT(m_style)) { CRect rect(blurMarginX + m_cachedStyle.shadow_offset_x, @@ -541,12 +541,12 @@ void WeaselPanel::_HighlightText(Gdiplus::Graphics& g_back, BYTE b = GetBValue(shadowColor); BYTE alpha = (BYTE)((shadowColor >> 24) & 255); Gdiplus::Color shadow_color = Gdiplus::Color::MakeARGB(alpha, r, g, b); - static Gdiplus::Bitmap* pBitmapDropShadow; - pBitmapDropShadow = new Gdiplus::Bitmap((INT)rc.Width() + blurMarginX * 2, - (INT)rc.Height() + blurMarginY * 2, - PixelFormat32bppPARGB); + std::unique_ptr pBitmapDropShadow = + std::make_unique((INT)rc.Width() + blurMarginX * 2, + (INT)rc.Height() + blurMarginY * 2, + PixelFormat32bppPARGB); - Gdiplus::Graphics g_shadow(pBitmapDropShadow); + Gdiplus::Graphics g_shadow(pBitmapDropShadow.get()); g_shadow.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality); // dropshadow, draw a roundrectangle to blur if (m_style.shadow_offset_x != 0 || m_style.shadow_offset_y != 0) { @@ -566,15 +566,11 @@ void WeaselPanel::_HighlightText(Gdiplus::Graphics& g_back, rect.InflateRect(1, 1); } } - DoGaussianBlur(pBitmapDropShadow, (float)m_cachedStyle.shadow_radius, + DoGaussianBlur(pBitmapDropShadow.get(), (float)m_cachedStyle.shadow_radius, (float)m_cachedStyle.shadow_radius); - g_back.DrawImage(pBitmapDropShadow, rc.left - blurMarginX, + g_back.DrawImage(pBitmapDropShadow.get(), rc.left - blurMarginX, rc.top - blurMarginY); - - // free memory - delete pBitmapDropShadow; - pBitmapDropShadow = NULL; } // 必须back_color非完全透明才绘制 From f45dec9c6763aedc0bc618e406cfddf73d34f8c3 Mon Sep 17 00:00:00 2001 From: fxliang Date: Mon, 9 Feb 2026 11:30:29 +0800 Subject: [PATCH 14/25] refactor(WeaselUI): avoid direct2d and directwrite factory rebuild --- WeaselUI/DirectWriteResources.cpp | 100 +++++++++++++----------------- WeaselUI/FullScreenLayout.cpp | 8 +-- WeaselUI/WeaselPanel.cpp | 38 ++++++++---- include/WeaselUI.h | 14 ++--- 4 files changed, 79 insertions(+), 81 deletions(-) diff --git a/WeaselUI/DirectWriteResources.cpp b/WeaselUI/DirectWriteResources.cpp index eaae6aab6..a25c9a8f5 100644 --- a/WeaselUI/DirectWriteResources.cpp +++ b/WeaselUI/DirectWriteResources.cpp @@ -27,11 +27,9 @@ static vector ws_split(const wstring& in, const wstring& delim) { std::wsregex_token_iterator()}; } -DirectWriteResources::DirectWriteResources(weasel::UIStyle& style, - UINT dpi = 96) - : _style(style), - dpiScaleFontPoint(0), - dpiScaleLayout(0), +DirectWriteResources::DirectWriteResources() + : dpiScaleFontPoint(1.0f), + dpiScaleLayout(1.0f), pD2d1Factory(NULL), pDWFactory(NULL), pRenderTarget(NULL), @@ -42,61 +40,60 @@ DirectWriteResources::DirectWriteResources(weasel::UIStyle& style, pLabelTextFormat(NULL), pCommentTextFormat(NULL) { // prepare d2d1 resources create factory - static const D2D1_TEXT_ANTIALIAS_MODE mode = - _style.antialias_mode <= 3 - ? (D2D1_TEXT_ANTIALIAS_MODE)(_style.antialias_mode) - : D2D1_TEXT_ANTIALIAS_MODE_FORCE_DWORD; HR(::D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, pD2d1Factory.ReleaseAndGetAddressOf())); // create IDWriteFactory HR(DWriteCreateFactory( DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast(pDWFactory.ReleaseAndGetAddressOf()))); - /* ID2D1HwndRenderTarget */ +} + +DirectWriteResources::~DirectWriteResources() { + _textFormatCache.clear(); +} + +HRESULT DirectWriteResources::EnsureRenderTarget(int antialiasMode) { + if (pRenderTarget) + return S_OK; + static const D2D1_PIXEL_FORMAT format = D2D1::PixelFormat( DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED); static const D2D1_RENDER_TARGET_PROPERTIES properties = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, format); HR(pD2d1Factory->CreateDCRenderTarget(&properties, &pRenderTarget)); - pRenderTarget->SetTextAntialiasMode(mode); + pRenderTarget->SetTextAntialiasMode((D2D1_TEXT_ANTIALIAS_MODE)antialiasMode); pRenderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); HR(pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), pBrush.ReleaseAndGetAddressOf())); - // get the dpi information - dpiScaleFontPoint = dpiScaleLayout = (float)dpi; - dpiScaleFontPoint /= 72.0f; - dpiScaleLayout /= 96.0f; - - InitResources(style, dpi); + return S_OK; } -DirectWriteResources::~DirectWriteResources() { - _textFormatCache.clear(); +void DirectWriteResources::ResetRenderTarget() { + pRenderTarget.Reset(); + pBrush.Reset(); } -HRESULT DirectWriteResources::InitResources(const wstring& label_font_face, +HRESULT DirectWriteResources::InitResources(const UIStyle& style, const int& label_font_point, - const wstring& font_face, const int& font_point, - const wstring& comment_font_face, - const int& comment_font_point, - const bool& vertical_text) { + const int& comment_font_point) { // prepare d2d1 resources + const bool vertical_text = style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT; const DWRITE_WORD_WRAPPING wrapping = - ((_style.max_width == 0 && - _style.layout_type != UIStyle::LAYOUT_VERTICAL_TEXT) || - (_style.max_height == 0 && - _style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT)) + ((style.max_width == 0 && + style.layout_type != UIStyle::LAYOUT_VERTICAL_TEXT) || + (style.max_height == 0 && + style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT)) ? DWRITE_WORD_WRAPPING_NO_WRAP : DWRITE_WORD_WRAPPING_WHOLE_WORD; const DWRITE_WORD_WRAPPING wrapping_preedit = - ((_style.max_width == 0 && - _style.layout_type != UIStyle::LAYOUT_VERTICAL_TEXT) || - (_style.max_height == 0 && - _style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT)) + ((style.max_width == 0 && + style.layout_type != UIStyle::LAYOUT_VERTICAL_TEXT) || + (style.max_height == 0 && + style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT)) ? DWRITE_WORD_WRAPPING_NO_WRAP : DWRITE_WORD_WRAPPING_CHARACTER; - const DWRITE_FLOW_DIRECTION flow = _style.vertical_text_left_to_right + const DWRITE_FLOW_DIRECTION flow = style.vertical_text_left_to_right ? DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT : DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT; @@ -105,9 +102,9 @@ HRESULT DirectWriteResources::InitResources(const wstring& label_font_face, DWRITE_FONT_WEIGHT fontWeight = DWRITE_FONT_WEIGHT_NORMAL; DWRITE_FONT_STYLE fontStyle = DWRITE_FONT_STYLE_NORMAL; // convert percentage to float - float linespacing = dpiScaleFontPoint * ((float)_style.linespacing / 100.0f); - float baseline = dpiScaleFontPoint * ((float)_style.baseline / 100.0f); - if (_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT) + float linespacing = dpiScaleFontPoint * ((float)style.linespacing / 100.0f); + float baseline = dpiScaleFontPoint * ((float)style.baseline / 100.0f); + if (style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT) baseline = linespacing / 2; auto init_font = [&](const wstring& fontface, int fontpoint, @@ -116,8 +113,9 @@ HRESULT DirectWriteResources::InitResources(const wstring& label_font_face, const wstring key = fontface + L"|" + std::to_wstring(fontpoint) + L"|" + (vertical_text ? L"1" : L"0") + L"|" + std::to_wstring((int)wrap) + L"|" + - std::to_wstring(_style.linespacing) + L"|" + - std::to_wstring(_style.baseline); + std::to_wstring(style.linespacing) + L"|" + + std::to_wstring(style.baseline) + L"|" + + std::to_wstring(dpiScaleFontPoint); if (_textFormatCache.find(key) != _textFormatCache.end()) { _pTextFormat = _textFormatCache[key]; return; @@ -150,40 +148,30 @@ HRESULT DirectWriteResources::InitResources(const wstring& label_font_face, DWRITE_PARAGRAPH_ALIGNMENT_CENTER)); HR(_pTextFormat->SetWordWrapping(wrap)); _SetFontFallback(_pTextFormat, fontFaceStrVector); - if (_style.linespacing && _style.baseline) + if (style.linespacing && style.baseline) _pTextFormat->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_UNIFORM, fontpoint * linespacing, fontpoint * baseline); _textFormatCache[key] = _pTextFormat; } }; - init_font(font_face, font_point, pTextFormat, wrapping); - init_font(font_face, font_point, pPreeditTextFormat, wrapping_preedit); - init_font(label_font_face, label_font_point, pLabelTextFormat, wrapping); - init_font(comment_font_face, comment_font_point, pCommentTextFormat, + init_font(style.font_face, font_point, pTextFormat, wrapping); + init_font(style.font_face, font_point, pPreeditTextFormat, wrapping_preedit); + init_font(style.label_font_face, label_font_point, pLabelTextFormat, + wrapping); + init_font(style.comment_font_face, comment_font_point, pCommentTextFormat, wrapping); return S_OK; } HRESULT DirectWriteResources::InitResources(const UIStyle& style, const UINT& dpi = 96) { - _style = style; if (dpi) { dpiScaleFontPoint = dpi / 72.0f; dpiScaleLayout = dpi / 96.0f; } - return InitResources(style.label_font_face, style.label_font_point, - style.font_face, style.font_point, - style.comment_font_face, style.comment_font_point, - style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT); -} - -void weasel::DirectWriteResources::SetDpi(const UINT& dpi) { - dpiScaleFontPoint = dpi / 72.0f; - dpiScaleLayout = dpi / 96.0f; - - _textFormatCache.clear(); - InitResources(_style); + return InitResources(style, style.label_font_point, style.font_point, + style.comment_font_point); } static wstring _MatchWordsOutLowerCaseTrim1st(const wstring& wstr, diff --git a/WeaselUI/FullScreenLayout.cpp b/WeaselUI/FullScreenLayout.cpp index 96f10cf9b..907a10dc9 100644 --- a/WeaselUI/FullScreenLayout.cpp +++ b/WeaselUI/FullScreenLayout.cpp @@ -119,9 +119,7 @@ bool FullScreenLayout::AdjustFontPoint(CDCHandle dc, fontPoint += step; fontPointLabel += step; fontPointComment += step; - pDWR->InitResources(_style.label_font_face, fontPointLabel, - _style.font_face, fontPoint, _style.comment_font_face, - fontPointComment); + pDWR->InitResources(_style, fontPointLabel, fontPoint, fontPointComment); return true; } else if (sz.cx <= (workArea.Width() - offsetX * 2) * 31 / 32 && sz.cy <= (workArea.Height() - offsetY * 2) * 31 / 32) { @@ -131,9 +129,7 @@ bool FullScreenLayout::AdjustFontPoint(CDCHandle dc, fontPoint += step; fontPointLabel += step; fontPointComment += step; - pDWR->InitResources(_style.label_font_face, fontPointLabel, - _style.font_face, fontPoint, _style.comment_font_face, - fontPointComment); + pDWR->InitResources(_style, fontPointLabel, fontPoint, fontPointComment); return true; } diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index 58573ecad..e139a79f4 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -196,17 +196,24 @@ void WeaselPanel::_InitFontRes(bool forced) { if (hMonitor) GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); - if (!forced && (pDWR != NULL) && !(m_ostyle != m_style) && (dpiX == dpi)) { + bool styleChanged = (m_ostyle != m_style); + bool dpiChanged = (dpiX != dpi); + + if (!forced && (pDWR != NULL) && !styleChanged && !dpiChanged) { return; } // prepare d2d1 resources - // if style changed, or dpi changed, or pDWR NULL, re-initialize directwrite - // resources - pDWR.reset(); - pDWR = std::make_shared(m_style, dpiX); - pDWR->pRenderTarget->SetTextAntialiasMode( - (D2D1_TEXT_ANTIALIAS_MODE)m_style.antialias_mode); + // if style changed, or dpi changed, re-initialize directwrite resources + bool needInit = false; + if (!pDWR) { + pDWR = std::make_shared(); + needInit = true; + } + if (needInit || styleChanged || dpiChanged) { + pDWR->InitResources(m_style, dpiX); + } + pDWR->EnsureRenderTarget(m_style.antialias_mode); m_ostyle = m_style; dpi = dpiX; @@ -1035,9 +1042,18 @@ void WeaselPanel::DoPaint(CDCHandle dc) { // begin texts drawing, if pRenderTarget failed, force to reinit // directwrite resources - if (FAILED(pDWR->pRenderTarget->BindDC(memDC, &rcw))) { - _InitFontRes(true); - pDWR->pRenderTarget->BindDC(memDC, &rcw); + if (FAILED(pDWR->EnsureRenderTarget(m_style.antialias_mode)) || + FAILED(pDWR->pRenderTarget->BindDC(memDC, &rcw))) { + pDWR->ResetRenderTarget(); + if (SUCCEEDED(pDWR->EnsureRenderTarget(m_style.antialias_mode))) + if (FAILED(pDWR->pRenderTarget->BindDC(memDC, &rcw))) { + ::MessageBoxW(NULL, + L"Failed to bind DC to render target after reset.", + L"Error", MB_ICONERROR); + // bad luck, still failed after reset, avoid infinite loop + throw std::runtime_error( + "Failed to bind DC to render target after reset."); + } } pDWR->pRenderTarget->BeginDraw(); // draw auxiliary string @@ -1050,7 +1066,7 @@ void WeaselPanel::DoPaint(CDCHandle dc) { if (m_candidateCount) drawn |= _DrawCandidates(g_back); if (FAILED(pDWR->pRenderTarget->EndDraw())) { - _InitFontRes(true); + pDWR->ResetRenderTarget(); Refresh(); } // end texts drawing diff --git a/include/WeaselUI.h b/include/WeaselUI.h index c366013bd..dd0782877 100755 --- a/include/WeaselUI.h +++ b/include/WeaselUI.h @@ -96,18 +96,18 @@ class UI { class DirectWriteResources { public: - DirectWriteResources(weasel::UIStyle& style, UINT dpi); + DirectWriteResources(); ~DirectWriteResources(); - HRESULT InitResources(const std::wstring& label_font_face, + HRESULT InitResources(const UIStyle& style, const int& label_font_point, - const std::wstring& font_face, const int& font_point, - const std::wstring& comment_font_face, - const int& comment_font_point, - const bool& vertical_text = false); + const int& comment_font_point); HRESULT InitResources(const UIStyle& style, const UINT& dpi); + HRESULT EnsureRenderTarget(int antialiasMode); + void ResetRenderTarget(); + HRESULT CreateTextLayout(const std::wstring& text, const int& nCount, IDWriteTextFormat1* const txtFormat, @@ -149,7 +149,6 @@ class DirectWriteResources { color, pBrush.ReleaseAndGetAddressOf()); } } - void SetDpi(const UINT& dpi); float dpiScaleFontPoint, dpiScaleLayout; ComPtr pD2d1Factory; @@ -163,7 +162,6 @@ class DirectWriteResources { ComPtr pBrush; private: - UIStyle& _style; void _ParseFontFace(const std::wstring& fontFaceStr, DWRITE_FONT_WEIGHT& fontWeight, DWRITE_FONT_STYLE& fontStyle); From 75b205a4fb4125435d6f766e2f280bf8d261be74 Mon Sep 17 00:00:00 2001 From: fxliang Date: Mon, 9 Feb 2026 14:38:35 +0800 Subject: [PATCH 15/25] refactor(WeaselUI): DirectWriteResources owned by UI --- WeaselUI/FullScreenLayout.cpp | 5 +++-- WeaselUI/FullScreenLayout.h | 6 +++--- WeaselUI/HorizontalLayout.cpp | 2 +- WeaselUI/HorizontalLayout.h | 4 ++-- WeaselUI/Layout.cpp | 2 +- WeaselUI/Layout.h | 6 +++--- WeaselUI/StandardLayout.cpp | 10 ++++++---- WeaselUI/StandardLayout.h | 12 ++++++------ WeaselUI/VHorizontalLayout.cpp | 5 +++-- WeaselUI/VHorizontalLayout.h | 6 +++--- WeaselUI/VerticalLayout.cpp | 3 ++- WeaselUI/VerticalLayout.h | 4 ++-- WeaselUI/WeaselPanel.cpp | 11 +++++------ WeaselUI/WeaselPanel.h | 3 ++- WeaselUI/WeaselUI.cpp | 12 ++++++++++++ include/WeaselUI.h | 13 +++---------- 16 files changed, 57 insertions(+), 47 deletions(-) diff --git a/WeaselUI/FullScreenLayout.cpp b/WeaselUI/FullScreenLayout.cpp index 907a10dc9..446ee1cf1 100644 --- a/WeaselUI/FullScreenLayout.cpp +++ b/WeaselUI/FullScreenLayout.cpp @@ -3,7 +3,8 @@ using namespace weasel; -void weasel::FullScreenLayout::DoLayout(CDCHandle dc, PDWR pDWR) { +void weasel::FullScreenLayout::DoLayout(CDCHandle dc, + DirectWriteResources* pDWR) { if (_context.empty()) { int width = 0, height = 0; UpdateStatusIconLayout(&width, &height); @@ -87,7 +88,7 @@ void weasel::FullScreenLayout::DoLayout(CDCHandle dc, PDWR pDWR) { bool FullScreenLayout::AdjustFontPoint(CDCHandle dc, const CRect& workArea, int& step, - PDWR pDWR) { + DirectWriteResources* pDWR) { if (_context.empty() || step == 0) return false; { diff --git a/WeaselUI/FullScreenLayout.h b/WeaselUI/FullScreenLayout.h index 27c43d5c1..31890eb5f 100644 --- a/WeaselUI/FullScreenLayout.h +++ b/WeaselUI/FullScreenLayout.h @@ -10,19 +10,19 @@ class FullScreenLayout : public StandardLayout { const Status& status, const CRect& inputPos, Layout* layout, - PDWR pDWR) + DirectWriteResources* pDWR) : StandardLayout(style, context, status, pDWR), mr_inputPos(inputPos), m_layout(layout) {} virtual ~FullScreenLayout() { delete m_layout; } - virtual void DoLayout(CDCHandle dc, PDWR pDWR = NULL); + virtual void DoLayout(CDCHandle dc, DirectWriteResources* pDWR = NULL); private: bool AdjustFontPoint(CDCHandle dc, const CRect& workArea, int& step, - PDWR pDWR = NULL); + DirectWriteResources* pDWR = NULL); const CRect& mr_inputPos; Layout* m_layout; diff --git a/WeaselUI/HorizontalLayout.cpp b/WeaselUI/HorizontalLayout.cpp index 17a31cb18..145c7ffdb 100644 --- a/WeaselUI/HorizontalLayout.cpp +++ b/WeaselUI/HorizontalLayout.cpp @@ -3,7 +3,7 @@ using namespace weasel; -void HorizontalLayout::DoLayout(CDCHandle dc, PDWR pDWR) { +void HorizontalLayout::DoLayout(CDCHandle dc, DirectWriteResources* pDWR) { CSize size; int width = offsetX + real_margin_x, height = offsetY + real_margin_y; int w = offsetX + real_margin_x; diff --git a/WeaselUI/HorizontalLayout.h b/WeaselUI/HorizontalLayout.h index 11eb31dc4..67a99593f 100644 --- a/WeaselUI/HorizontalLayout.h +++ b/WeaselUI/HorizontalLayout.h @@ -8,8 +8,8 @@ class HorizontalLayout : public StandardLayout { HorizontalLayout(const UIStyle& style, const Context& context, const Status& status, - PDWR pDWR) + DirectWriteResources* pDWR) : StandardLayout(style, context, status, pDWR) {} - virtual void DoLayout(CDCHandle dc, PDWR pDWR = NULL); + virtual void DoLayout(CDCHandle dc, DirectWriteResources* pDWR = NULL); }; }; // namespace weasel diff --git a/WeaselUI/Layout.cpp b/WeaselUI/Layout.cpp index 60b60b4d7..125d92910 100644 --- a/WeaselUI/Layout.cpp +++ b/WeaselUI/Layout.cpp @@ -5,7 +5,7 @@ using namespace weasel; Layout::Layout(const UIStyle& style, const Context& context, const Status& status, - PDWR pDWR) + DirectWriteResources* pDWR) : _style(style), _context(context), _status(status), diff --git a/WeaselUI/Layout.h b/WeaselUI/Layout.h index d96ad8e80..c0bcbf56e 100644 --- a/WeaselUI/Layout.h +++ b/WeaselUI/Layout.h @@ -67,9 +67,9 @@ class Layout { Layout(const UIStyle& style, const Context& context, const Status& status, - PDWR pDWR); + DirectWriteResources* pDWR); - virtual void DoLayout(CDCHandle dc, PDWR pDWR = NULL) = 0; + virtual void DoLayout(CDCHandle dc, DirectWriteResources* pDWR = NULL) = 0; /* All points in this class is based on the content area */ /* The top-left corner of the content area is always (0, 0) */ virtual CSize GetContentSize() const = 0; @@ -99,7 +99,7 @@ class Layout { virtual void GetTextSizeDW(const std::wstring& text, size_t nCount, ComPtr& pTextFormat, - PDWR pDWR, + DirectWriteResources* pDWR, LPSIZE lpSize) const = 0; int offsetX = 0; diff --git a/WeaselUI/StandardLayout.cpp b/WeaselUI/StandardLayout.cpp index 8d8674488..0414a1bf5 100644 --- a/WeaselUI/StandardLayout.cpp +++ b/WeaselUI/StandardLayout.cpp @@ -3,7 +3,8 @@ using namespace weasel; -StandardLayout::MarkMetrics StandardLayout::ComputeMarkMetrics(PDWR pDWR) { +StandardLayout::MarkMetrics StandardLayout::ComputeMarkMetrics( + DirectWriteResources* pDWR) { MarkMetrics m; if ((_style.hilited_mark_color & 0xff000000)) { CSize sg; @@ -51,7 +52,8 @@ StandardLayout::MarkMetrics StandardLayout::ComputeMarkMetrics(PDWR pDWR) { return m; } -StandardLayout::PagerMetrics StandardLayout::ComputePagerMetrics(PDWR pDWR) { +StandardLayout::PagerMetrics StandardLayout::ComputePagerMetrics( + DirectWriteResources* pDWR) { PagerMetrics pager; if (!IsInlinePreedit()) { GetTextSizeDW(pre, pre.length(), pDWR->pPreeditTextFormat, pDWR, @@ -162,7 +164,7 @@ void weasel::StandardLayout::GetTextSizeDW( const std::wstring& text, size_t nCount, ComPtr& pTextFormat, - PDWR pDWR, + DirectWriteResources* pDWR, LPSIZE lpSize) const { D2D1_SIZE_F sz; HRESULT hr = S_OK; @@ -231,7 +233,7 @@ void weasel::StandardLayout::GetTextSizeDW( CSize StandardLayout::GetPreeditSize(CDCHandle dc, const weasel::Text& text, ComPtr pTextFormat, - PDWR pDWR) { + DirectWriteResources* pDWR) { const std::wstring& preedit = text.str; const std::vector& attrs = text.attributes; CSize size(0, 0); diff --git a/WeaselUI/StandardLayout.h b/WeaselUI/StandardLayout.h index cdc23ae2d..3514bea8c 100644 --- a/WeaselUI/StandardLayout.h +++ b/WeaselUI/StandardLayout.h @@ -15,12 +15,12 @@ class StandardLayout : public Layout { StandardLayout(const UIStyle& style, const Context& context, const Status& status, - PDWR pDWR) + DirectWriteResources* pDWR) : Layout(style, context, status, pDWR) {} /* Layout */ - virtual void DoLayout(CDCHandle dc, PDWR pDWR = NULL) = 0; + virtual void DoLayout(CDCHandle dc, DirectWriteResources* pDWR = NULL) = 0; virtual CSize GetContentSize() const { return _contentSize; } virtual CRect GetPreeditRect() const { return _preeditRect; } virtual CRect GetAuxiliaryRect() const { return _auxiliaryRect; } @@ -54,7 +54,7 @@ class StandardLayout : public Layout { void GetTextSizeDW(const std::wstring& text, size_t nCount, ComPtr& pTextFormat, - PDWR pDWR, + DirectWriteResources* pDWR, LPSIZE lpSize) const; protected: @@ -74,8 +74,8 @@ class StandardLayout : public Layout { bool page_en = false; }; - MarkMetrics ComputeMarkMetrics(PDWR pDWR); - PagerMetrics ComputePagerMetrics(PDWR pDWR); + MarkMetrics ComputeMarkMetrics(DirectWriteResources* pDWR); + PagerMetrics ComputePagerMetrics(DirectWriteResources* pDWR); void PlacePagerHorizontal(const PagerMetrics& pager, int contentWidth, int contentHeight); @@ -89,7 +89,7 @@ class StandardLayout : public Layout { CSize GetPreeditSize(CDCHandle dc, const weasel::Text& text, ComPtr pTextFormat = NULL, - PDWR pDWR = NULL); + DirectWriteResources* pDWR = NULL); bool _IsHighlightOverCandidateWindow(CRect& rc, CDCHandle& dc); void _PrepareRoundInfo(CDCHandle& dc); diff --git a/WeaselUI/VHorizontalLayout.cpp b/WeaselUI/VHorizontalLayout.cpp index 60b031fe3..c7019d508 100644 --- a/WeaselUI/VHorizontalLayout.cpp +++ b/WeaselUI/VHorizontalLayout.cpp @@ -4,7 +4,7 @@ using namespace weasel; -void VHorizontalLayout::DoLayout(CDCHandle dc, PDWR pDWR) { +void VHorizontalLayout::DoLayout(CDCHandle dc, DirectWriteResources* pDWR) { if (_style.vertical_text_with_wrap) { DoLayoutWithWrap(dc, pDWR); return; @@ -199,7 +199,8 @@ void VHorizontalLayout::DoLayout(CDCHandle dc, PDWR pDWR) { _contentRect.DeflateRect(offsetX, offsetY); } -void VHorizontalLayout::DoLayoutWithWrap(CDCHandle dc, PDWR pDWR) { +void VHorizontalLayout::DoLayoutWithWrap(CDCHandle dc, + DirectWriteResources* pDWR) { CSize size; int height = offsetY, width = offsetX + real_margin_x; int h = offsetY + real_margin_y; diff --git a/WeaselUI/VHorizontalLayout.h b/WeaselUI/VHorizontalLayout.h index 66937e8cc..4a5e83aad 100644 --- a/WeaselUI/VHorizontalLayout.h +++ b/WeaselUI/VHorizontalLayout.h @@ -8,11 +8,11 @@ class VHorizontalLayout : public StandardLayout { VHorizontalLayout(const UIStyle& style, const Context& context, const Status& status, - PDWR pDWR) + DirectWriteResources* pDWR) : StandardLayout(style, context, status, pDWR) {} - virtual void DoLayout(CDCHandle dc, PDWR pDWR = NULL); + virtual void DoLayout(CDCHandle dc, DirectWriteResources* pDWR = NULL); private: - void DoLayoutWithWrap(CDCHandle dc, PDWR pDWR = NULL); + void DoLayoutWithWrap(CDCHandle dc, DirectWriteResources* pDWR = NULL); }; }; // namespace weasel diff --git a/WeaselUI/VerticalLayout.cpp b/WeaselUI/VerticalLayout.cpp index e6f7c4fd3..7eb5fee23 100644 --- a/WeaselUI/VerticalLayout.cpp +++ b/WeaselUI/VerticalLayout.cpp @@ -3,7 +3,8 @@ using namespace weasel; -void weasel::VerticalLayout::DoLayout(CDCHandle dc, PDWR pDWR) { +void weasel::VerticalLayout::DoLayout(CDCHandle dc, + DirectWriteResources* pDWR) { const int space = _style.hilite_spacing; int width = 0, height = real_margin_y; MarkMetrics mark = ComputeMarkMetrics(pDWR); diff --git a/WeaselUI/VerticalLayout.h b/WeaselUI/VerticalLayout.h index e4f85ee25..7f1c1d17e 100644 --- a/WeaselUI/VerticalLayout.h +++ b/WeaselUI/VerticalLayout.h @@ -8,8 +8,8 @@ class VerticalLayout : public StandardLayout { VerticalLayout(const UIStyle& style, const Context& context, const Status& status, - PDWR pDWR) + DirectWriteResources* pDWR) : StandardLayout(style, context, status, pDWR) {} - virtual void DoLayout(CDCHandle dc, PDWR pDWR = NULL); + virtual void DoLayout(CDCHandle dc, DirectWriteResources* pDWR = NULL); }; }; // namespace weasel diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index e139a79f4..32dee9505 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -205,12 +205,7 @@ void WeaselPanel::_InitFontRes(bool forced) { // prepare d2d1 resources // if style changed, or dpi changed, re-initialize directwrite resources - bool needInit = false; - if (!pDWR) { - pDWR = std::make_shared(); - needInit = true; - } - if (needInit || styleChanged || dpiChanged) { + if (styleChanged || dpiChanged) { pDWR->InitResources(m_style, dpiX); } pDWR->EnsureRenderTarget(m_style.antialias_mode); @@ -1297,3 +1292,7 @@ void WeaselPanel::_TextOut(const CRect& rc, #endif } } + +DirectWriteResources* WeaselPanel::GetDWR() const { + return pDWR; +} diff --git a/WeaselUI/WeaselPanel.h b/WeaselUI/WeaselPanel.h index 9626dd323..e666648dd 100644 --- a/WeaselUI/WeaselPanel.h +++ b/WeaselUI/WeaselPanel.h @@ -65,6 +65,7 @@ class WeaselPanel void DoPaint(CDCHandle dc); bool GetIsReposition() { return m_istorepos; } void RedrawWindow(); + DirectWriteResources* GetDWR() const; static VOID CALLBACK OnTimer(_In_ HWND hwnd, _In_ UINT uMsg, @@ -146,7 +147,7 @@ class WeaselPanel bool hide_candidates; bool m_sticky; // for multi font_face & font_point - PDWR pDWR; + DirectWriteResources* pDWR; std::function& _UICallback; float bar_scale_ = 1.0; diff --git a/WeaselUI/WeaselUI.cpp b/WeaselUI/WeaselUI.cpp index 53a9b92fe..386960554 100644 --- a/WeaselUI/WeaselUI.cpp +++ b/WeaselUI/WeaselUI.cpp @@ -177,3 +177,15 @@ void UI::Update(const Context& ctx, const Status& status) { } Refresh(); } + +UI::~UI() { + if (pimpl_) + Destroy(true); +} + +DirectWriteResources* UI::pdwr() { + if (!pDWR) { + pDWR = std::make_unique(); + } + return pDWR.get(); +} diff --git a/include/WeaselUI.h b/include/WeaselUI.h index dd0782877..bdda13c78 100755 --- a/include/WeaselUI.h +++ b/include/WeaselUI.h @@ -22,7 +22,6 @@ class DirectWriteResources; template using an = std::shared_ptr; -using PDWR = an; // // 输入法界面接口类 // @@ -30,13 +29,7 @@ class UI { public: UI() : pimpl_(0), in_server_(false) {} - virtual ~UI() { - if (pimpl_) - Destroy(true); - if (pDWR) { - pDWR.reset(); - } - } + virtual ~UI(); // 创建输入法界面 bool Create(HWND parent); @@ -65,7 +58,7 @@ class UI { Status& status() { return status_; } UIStyle& style() { return style_; } UIStyle& ostyle() { return ostyle_; } - PDWR pdwr() { return pDWR; } + DirectWriteResources* pdwr(); bool GetIsReposition(); bool& InServer() { return in_server_; } @@ -82,7 +75,7 @@ class UI { private: UIImpl* pimpl_; - PDWR pDWR; + std::unique_ptr pDWR; Context ctx_; Context octx_; From 191844910e19019d16c0305370ae12d7e91cbdf0 Mon Sep 17 00:00:00 2001 From: fxliang Date: Mon, 9 Feb 2026 14:47:54 +0800 Subject: [PATCH 16/25] refactor(WeaselUI): alias UICallback, and rename for styling --- WeaselUI/WeaselPanel.cpp | 26 +++++++++++++------------- WeaselUI/WeaselPanel.h | 3 +-- include/WeaselUI.h | 18 ++++++------------ 3 files changed, 20 insertions(+), 27 deletions(-) diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index 32dee9505..e15f9332f 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -71,7 +71,7 @@ WeaselPanel::WeaselPanel(weasel::UI& ui) dpi(96), hide_candidates(false), pDWR(ui.pdwr()), - _UICallback(ui.uiCallback()), + ui_callback_(ui.uiCallback()), _m_gdiplusToken(0) { m_iconDisabled.LoadIconW(IDI_RELOAD, STATUS_ICON_SIZE, STATUS_ICON_SIZE, LR_DEFAULTCOLOR); @@ -287,9 +287,9 @@ LRESULT WeaselPanel::OnMouseWheel(UINT uMsg, LPARAM lParam, BOOL& bHandled) { int delta = GET_WHEEL_DELTA_WPARAM(wParam); - if (_UICallback && delta != 0) { + if (ui_callback_ && delta != 0) { bool nextpage = delta < 0; - _UICallback(NULL, NULL, NULL, &nextpage); + ui_callback_(NULL, NULL, NULL, &nextpage); } bHandled = true; return 0; @@ -318,9 +318,9 @@ LRESULT WeaselPanel::OnLeftClickedUp(UINT uMsg, m_cachedStyle.hilite_padding_y); if (rect.PtInRect(point)) { size_t i = m_ctx.cinfo.highlighted; - if (_UICallback) { + if (ui_callback_) { m_mouse_entry = false; - _UICallback(&i, NULL, NULL, NULL); + ui_callback_(&i, NULL, NULL, NULL); if (!m_status.composing) DestroyWindow(); } @@ -393,8 +393,8 @@ LRESULT WeaselPanel::OnLeftClickedDown(UINT uMsg, _OffsetRectIfIsToRepos(prc, m_offsety_preedit); if (prc.PtInRect(point)) { bool nextPage = false; - if (_UICallback) - _UICallback(NULL, NULL, &nextPage, NULL); + if (ui_callback_) + ui_callback_(NULL, NULL, &nextPage, NULL); bHandled = true; return 0; } @@ -405,8 +405,8 @@ LRESULT WeaselPanel::OnLeftClickedDown(UINT uMsg, _OffsetRectIfIsToRepos(prc, m_offsety_preedit); if (prc.PtInRect(point)) { bool nextPage = true; - if (_UICallback) - _UICallback(NULL, NULL, &nextPage, NULL); + if (ui_callback_) + ui_callback_(NULL, NULL, &nextPage, NULL); bHandled = true; return 0; } @@ -422,8 +422,8 @@ LRESULT WeaselPanel::OnLeftClickedDown(UINT uMsg, bar_scale_ = 0.8f; // modify highlighted if (i != m_ctx.cinfo.highlighted) { - if (_UICallback) - _UICallback(NULL, &i, NULL, NULL); + if (ui_callback_) + ui_callback_(NULL, &i, NULL, NULL); } else { RedrawWindow(); } @@ -480,8 +480,8 @@ LRESULT WeaselPanel::OnMouseMove(UINT uMsg, if (rect.PtInRect(point)) { if (i != m_ctx.cinfo.highlighted) { if (m_style.hover_type == UIStyle::HoverType::HILITE) { - if (_UICallback) - _UICallback(NULL, &i, NULL, NULL); + if (ui_callback_) + ui_callback_(NULL, &i, NULL, NULL); } else if (m_hoverIndex != i) { m_hoverIndex = static_cast(i); InvalidateRect(&rcw, true); diff --git a/WeaselUI/WeaselPanel.h b/WeaselUI/WeaselPanel.h index e666648dd..d24c90681 100644 --- a/WeaselUI/WeaselPanel.h +++ b/WeaselUI/WeaselPanel.h @@ -148,8 +148,7 @@ class WeaselPanel bool m_sticky; // for multi font_face & font_point DirectWriteResources* pDWR; - std::function& - _UICallback; + UICallback& ui_callback_; float bar_scale_ = 1.0; float dpiScaleLayout = 1.0f; int m_hoverIndex = -1; diff --git a/include/WeaselUI.h b/include/WeaselUI.h index bdda13c78..13dd80c7b 100755 --- a/include/WeaselUI.h +++ b/include/WeaselUI.h @@ -22,6 +22,9 @@ class DirectWriteResources; template using an = std::shared_ptr; +using UICallback = + std::function; + // // 输入法界面接口类 // @@ -62,16 +65,8 @@ class UI { bool GetIsReposition(); bool& InServer() { return in_server_; } - std::function& - uiCallback() { - return _UICallback; - } - void SetUICallBack( - std::function< - void(size_t* const, size_t* const, bool* const, bool* const)> const& - func) { - _UICallback = func; - } + UICallback& uiCallback() { return ui_callback_; } + void SetUICallBack(UICallback const& func) { ui_callback_ = func; } private: UIImpl* pimpl_; @@ -83,8 +78,7 @@ class UI { UIStyle style_; UIStyle ostyle_; bool in_server_; - std::function - _UICallback; + UICallback ui_callback_; }; class DirectWriteResources { From fabcdeebc50720a422e3ecabc53660a64d9cc3fb Mon Sep 17 00:00:00 2001 From: fxliang Date: Mon, 9 Feb 2026 15:44:58 +0800 Subject: [PATCH 17/25] refactor(WeaselUI): refactor layouts implements, DirectWriteResources embedded --- WeaselUI/FullScreenLayout.cpp | 34 +++++++++---------- WeaselUI/FullScreenLayout.h | 7 ++-- WeaselUI/HorizontalLayout.cpp | 16 ++++----- WeaselUI/HorizontalLayout.h | 2 +- WeaselUI/Layout.cpp | 3 +- WeaselUI/Layout.h | 5 +-- WeaselUI/StandardLayout.cpp | 62 +++++++++++++++------------------- WeaselUI/StandardLayout.h | 10 +++--- WeaselUI/VHorizontalLayout.cpp | 35 ++++++++++--------- WeaselUI/VHorizontalLayout.h | 4 +-- WeaselUI/VerticalLayout.cpp | 17 +++++----- WeaselUI/VerticalLayout.h | 2 +- WeaselUI/WeaselPanel.cpp | 2 +- 13 files changed, 93 insertions(+), 106 deletions(-) diff --git a/WeaselUI/FullScreenLayout.cpp b/WeaselUI/FullScreenLayout.cpp index 446ee1cf1..de85392ec 100644 --- a/WeaselUI/FullScreenLayout.cpp +++ b/WeaselUI/FullScreenLayout.cpp @@ -3,8 +3,7 @@ using namespace weasel; -void weasel::FullScreenLayout::DoLayout(CDCHandle dc, - DirectWriteResources* pDWR) { +void weasel::FullScreenLayout::DoLayout(CDCHandle dc) { if (_context.empty()) { int width = 0, height = 0; UpdateStatusIconLayout(&width, &height); @@ -24,15 +23,15 @@ void weasel::FullScreenLayout::DoLayout(CDCHandle dc, int step = 32; do { - m_layout->DoLayout(dc, pDWR); + m_layout->DoLayout(dc); if ((_style.hilited_mark_color & 0xff000000)) { CSize sg; if (candidates_count) { if (_style.mark_text.empty()) - GetTextSizeDW(L"|", 1, pDWR->pTextFormat, pDWR, &sg); + GetTextSizeDW(L"|", 1, pDWR_->pTextFormat, &sg); else GetTextSizeDW(_style.mark_text, _style.mark_text.length(), - pDWR->pTextFormat, pDWR, &sg); + pDWR_->pTextFormat, &sg); } mark_width = sg.cx; mark_height = sg.cy; @@ -47,7 +46,7 @@ void weasel::FullScreenLayout::DoLayout(CDCHandle dc, ? mark_width : mark_width + _style.hilite_spacing; } - } while (AdjustFontPoint(dc, workArea, step, pDWR)); + } while (AdjustFontPoint(dc, workArea, step)); int offsetx = (workArea.Width() - m_layout->GetContentSize().cx) / 2; int offsety = (workArea.Height() - m_layout->GetContentSize().cy) / 2; @@ -87,8 +86,7 @@ void weasel::FullScreenLayout::DoLayout(CDCHandle dc, bool FullScreenLayout::AdjustFontPoint(CDCHandle dc, const CRect& workArea, - int& step, - DirectWriteResources* pDWR) { + int& step) { if (_context.empty() || step == 0) return false; { @@ -96,19 +94,19 @@ bool FullScreenLayout::AdjustFontPoint(CDCHandle dc, int fontPoint; int fontPointComment; - if (pDWR->pLabelTextFormat != NULL) - fontPointLabel = (int)(pDWR->pLabelTextFormat->GetFontSize() / - pDWR->dpiScaleFontPoint); + if (pDWR_->pLabelTextFormat != NULL) + fontPointLabel = (int)(pDWR_->pLabelTextFormat->GetFontSize() / + pDWR_->dpiScaleFontPoint); else fontPointLabel = 0; - if (pDWR->pTextFormat != NULL) + if (pDWR_->pTextFormat != NULL) fontPoint = - (int)(pDWR->pTextFormat->GetFontSize() / pDWR->dpiScaleFontPoint); + (int)(pDWR_->pTextFormat->GetFontSize() / pDWR_->dpiScaleFontPoint); else fontPoint = 0; - if (pDWR->pCommentTextFormat != NULL) - fontPointComment = (int)(pDWR->pCommentTextFormat->GetFontSize() / - pDWR->dpiScaleFontPoint); + if (pDWR_->pCommentTextFormat != NULL) + fontPointComment = (int)(pDWR_->pCommentTextFormat->GetFontSize() / + pDWR_->dpiScaleFontPoint); else fontPointComment = 0; CSize sz = m_layout->GetContentSize(); @@ -120,7 +118,7 @@ bool FullScreenLayout::AdjustFontPoint(CDCHandle dc, fontPoint += step; fontPointLabel += step; fontPointComment += step; - pDWR->InitResources(_style, fontPointLabel, fontPoint, fontPointComment); + pDWR_->InitResources(_style, fontPointLabel, fontPoint, fontPointComment); return true; } else if (sz.cx <= (workArea.Width() - offsetX * 2) * 31 / 32 && sz.cy <= (workArea.Height() - offsetY * 2) * 31 / 32) { @@ -130,7 +128,7 @@ bool FullScreenLayout::AdjustFontPoint(CDCHandle dc, fontPoint += step; fontPointLabel += step; fontPointComment += step; - pDWR->InitResources(_style, fontPointLabel, fontPoint, fontPointComment); + pDWR_->InitResources(_style, fontPointLabel, fontPoint, fontPointComment); return true; } diff --git a/WeaselUI/FullScreenLayout.h b/WeaselUI/FullScreenLayout.h index 31890eb5f..0a1eea96e 100644 --- a/WeaselUI/FullScreenLayout.h +++ b/WeaselUI/FullScreenLayout.h @@ -16,13 +16,10 @@ class FullScreenLayout : public StandardLayout { m_layout(layout) {} virtual ~FullScreenLayout() { delete m_layout; } - virtual void DoLayout(CDCHandle dc, DirectWriteResources* pDWR = NULL); + virtual void DoLayout(CDCHandle dc); private: - bool AdjustFontPoint(CDCHandle dc, - const CRect& workArea, - int& step, - DirectWriteResources* pDWR = NULL); + bool AdjustFontPoint(CDCHandle dc, const CRect& workArea, int& step); const CRect& mr_inputPos; Layout* m_layout; diff --git a/WeaselUI/HorizontalLayout.cpp b/WeaselUI/HorizontalLayout.cpp index 145c7ffdb..42e2ca1d4 100644 --- a/WeaselUI/HorizontalLayout.cpp +++ b/WeaselUI/HorizontalLayout.cpp @@ -3,20 +3,20 @@ using namespace weasel; -void HorizontalLayout::DoLayout(CDCHandle dc, DirectWriteResources* pDWR) { +void HorizontalLayout::DoLayout(CDCHandle dc) { CSize size; int width = offsetX + real_margin_x, height = offsetY + real_margin_y; int w = offsetX + real_margin_x; - MarkMetrics mark = ComputeMarkMetrics(pDWR); + MarkMetrics mark = ComputeMarkMetrics(); int base_offset = mark.base_offset; - PagerMetrics pager = ComputePagerMetrics(pDWR); + PagerMetrics pager = ComputePagerMetrics(); int pgw = pager.pgw; int pgh = pager.pgh; /* Preedit */ if (!IsInlinePreedit() && !_context.preedit.str.empty()) { - size = GetPreeditSize(dc, _context.preedit, pDWR->pPreeditTextFormat, pDWR); + size = GetPreeditSize(dc, _context.preedit, pDWR_->pPreeditTextFormat); int szx = pgw, szy = max(size.cy, pgh); // icon size higher then preedit text int yoffset = (STATUS_ICON_SIZE >= szy && ShouldDisplayStatusIcon()) @@ -32,7 +32,7 @@ void HorizontalLayout::DoLayout(CDCHandle dc, DirectWriteResources* pDWR) { /* Auxiliary */ if (!_context.aux.str.empty()) { - size = GetPreeditSize(dc, _context.aux, pDWR->pPreeditTextFormat, pDWR); + size = GetPreeditSize(dc, _context.aux, pDWR_->pPreeditTextFormat); // icon size higher then auxiliary text int yoffset = (STATUS_ICON_SIZE >= size.cy && ShouldDisplayStatusIcon()) ? (STATUS_ICON_SIZE - size.cy) / 2 @@ -60,7 +60,7 @@ void HorizontalLayout::DoLayout(CDCHandle dc, DirectWriteResources* pDWR) { /* Label */ std::wstring label = GetLabelText(labels, i, _style.label_text_format.c_str()); - GetTextSizeDW(label, label.length(), pDWR->pLabelTextFormat, pDWR, &size); + GetTextSizeDW(label, label.length(), pDWR_->pLabelTextFormat, &size); _candidateLabelRects[i].SetRect(w, height, w + size.cx * labelFontValid, height + size.cy); w += size.cx * labelFontValid; @@ -69,7 +69,7 @@ void HorizontalLayout::DoLayout(CDCHandle dc, DirectWriteResources* pDWR) { /* Text */ w += _style.hilite_spacing; const std::wstring& text = candidates.at(i).str; - GetTextSizeDW(text, text.length(), pDWR->pTextFormat, pDWR, &size); + GetTextSizeDW(text, text.length(), pDWR_->pTextFormat, &size); _candidateTextRects[i].SetRect(w, height, w + size.cx * textFontValid, height + size.cy); w += size.cx * textFontValid; @@ -81,7 +81,7 @@ void HorizontalLayout::DoLayout(CDCHandle dc, DirectWriteResources* pDWR) { (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, + GetTextSizeDW(comment, comment.length(), pDWR_->pCommentTextFormat, &size); w += _style.hilite_spacing; _candidateCommentRects[i].SetRect(w, height, w + size.cx * cmtFontValid, diff --git a/WeaselUI/HorizontalLayout.h b/WeaselUI/HorizontalLayout.h index 67a99593f..0576d609a 100644 --- a/WeaselUI/HorizontalLayout.h +++ b/WeaselUI/HorizontalLayout.h @@ -10,6 +10,6 @@ class HorizontalLayout : public StandardLayout { const Status& status, DirectWriteResources* pDWR) : StandardLayout(style, context, status, pDWR) {} - virtual void DoLayout(CDCHandle dc, DirectWriteResources* pDWR = NULL); + virtual void DoLayout(CDCHandle dc); }; }; // namespace weasel diff --git a/WeaselUI/Layout.cpp b/WeaselUI/Layout.cpp index 125d92910..3126c171f 100644 --- a/WeaselUI/Layout.cpp +++ b/WeaselUI/Layout.cpp @@ -16,7 +16,8 @@ Layout::Layout(const UIStyle& style, candidates_count((int)candidates.size()), labelFontValid(!!(_style.label_font_point > 0)), textFontValid(!!(_style.font_point > 0)), - cmtFontValid(!!(_style.comment_font_point > 0)) { + cmtFontValid(!!(_style.comment_font_point > 0)), + pDWR_(pDWR) { if (pDWR) { float scale = pDWR->dpiScaleLayout; _style.min_width = (int)(_style.min_width * scale); diff --git a/WeaselUI/Layout.h b/WeaselUI/Layout.h index c0bcbf56e..655e9e2be 100644 --- a/WeaselUI/Layout.h +++ b/WeaselUI/Layout.h @@ -69,7 +69,7 @@ class Layout { const Status& status, DirectWriteResources* pDWR); - virtual void DoLayout(CDCHandle dc, DirectWriteResources* pDWR = NULL) = 0; + virtual void DoLayout(CDCHandle dc) = 0; /* All points in this class is based on the content area */ /* The top-left corner of the content area is always (0, 0) */ virtual CSize GetContentSize() const = 0; @@ -99,7 +99,6 @@ class Layout { virtual void GetTextSizeDW(const std::wstring& text, size_t nCount, ComPtr& pTextFormat, - DirectWriteResources* pDWR, LPSIZE lpSize) const = 0; int offsetX = 0; @@ -122,5 +121,7 @@ class Layout { const int labelFontValid; const int textFontValid; const int cmtFontValid; + + DirectWriteResources* pDWR_ = nullptr; }; }; // namespace weasel diff --git a/WeaselUI/StandardLayout.cpp b/WeaselUI/StandardLayout.cpp index 0414a1bf5..c86b1ccff 100644 --- a/WeaselUI/StandardLayout.cpp +++ b/WeaselUI/StandardLayout.cpp @@ -3,17 +3,16 @@ using namespace weasel; -StandardLayout::MarkMetrics StandardLayout::ComputeMarkMetrics( - DirectWriteResources* pDWR) { +StandardLayout::MarkMetrics StandardLayout::ComputeMarkMetrics() { MarkMetrics m; if ((_style.hilited_mark_color & 0xff000000)) { CSize sg; if (candidates_count) { if (_style.mark_text.empty()) - GetTextSizeDW(L"|", 1, pDWR->pTextFormat, pDWR, &sg); + GetTextSizeDW(L"|", 1, pDWR_->pTextFormat, &sg); else GetTextSizeDW(_style.mark_text, _style.mark_text.length(), - pDWR->pTextFormat, pDWR, &sg); + pDWR_->pTextFormat, &sg); } m.mark_width = sg.cx; @@ -52,14 +51,11 @@ StandardLayout::MarkMetrics StandardLayout::ComputeMarkMetrics( return m; } -StandardLayout::PagerMetrics StandardLayout::ComputePagerMetrics( - DirectWriteResources* pDWR) { +StandardLayout::PagerMetrics StandardLayout::ComputePagerMetrics() { PagerMetrics pager; if (!IsInlinePreedit()) { - GetTextSizeDW(pre, pre.length(), pDWR->pPreeditTextFormat, pDWR, - &pager.pgszl); - GetTextSizeDW(next, next.length(), pDWR->pPreeditTextFormat, pDWR, - &pager.pgszr); + GetTextSizeDW(pre, pre.length(), pDWR_->pPreeditTextFormat, &pager.pgszl); + GetTextSizeDW(next, next.length(), pDWR_->pPreeditTextFormat, &pager.pgszr); } pager.page_en = (_style.prevpage_color & 0xff000000) && (_style.nextpage_color & 0xff000000); @@ -164,7 +160,6 @@ void weasel::StandardLayout::GetTextSizeDW( const std::wstring& text, size_t nCount, ComPtr& pTextFormat, - DirectWriteResources* pDWR, LPSIZE lpSize) const { D2D1_SIZE_F sz; HRESULT hr = S_OK; @@ -176,22 +171,23 @@ void weasel::StandardLayout::GetTextSizeDW( } // 创建文本布局 if (_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT) - HR(pDWR->CreateTextLayout(text.c_str(), (int)nCount, pTextFormat.Get(), - 0.0f, (float)_style.max_height)); + HR(pDWR_->CreateTextLayout(text.c_str(), (int)nCount, pTextFormat.Get(), + 0.0f, (float)_style.max_height)); else - HR(pDWR->CreateTextLayout(text.c_str(), (int)nCount, pTextFormat.Get(), - (float)_style.max_width, 0)); + HR(pDWR_->CreateTextLayout(text.c_str(), (int)nCount, pTextFormat.Get(), + (float)_style.max_width, 0)); if (_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT) { DWRITE_FLOW_DIRECTION flow = _style.vertical_text_left_to_right ? DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT : DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT; - HR(pDWR->SetLayoutReadingDirection(DWRITE_READING_DIRECTION_TOP_TO_BOTTOM)); - HR(pDWR->SetLayoutFlowDirection(flow)); + HR(pDWR_->SetLayoutReadingDirection( + DWRITE_READING_DIRECTION_TOP_TO_BOTTOM)); + HR(pDWR_->SetLayoutFlowDirection(flow)); } // 获取文本尺寸 DWRITE_TEXT_METRICS textMetrics; - HR(pDWR->GetLayoutMetrics(&textMetrics)); + HR(pDWR_->GetLayoutMetrics(&textMetrics)); sz = D2D1::SizeF(ceil(textMetrics.widthIncludingTrailingWhitespace), ceil(textMetrics.height)); @@ -202,22 +198,23 @@ void weasel::StandardLayout::GetTextSizeDW( auto max_width = _style.max_width == 0 ? textMetrics.widthIncludingTrailingWhitespace : _style.max_width; - HR(pDWR->CreateTextLayout(text.c_str(), (int)nCount, pTextFormat.Get(), - max_width, textMetrics.height)); + HR(pDWR_->CreateTextLayout(text.c_str(), (int)nCount, pTextFormat.Get(), + max_width, textMetrics.height)); } else { auto max_height = _style.max_height == 0 ? textMetrics.height : _style.max_height; - HR(pDWR->CreateTextLayout(text.c_str(), (int)nCount, pTextFormat.Get(), - textMetrics.widthIncludingTrailingWhitespace, - max_height)); + HR(pDWR_->CreateTextLayout(text.c_str(), (int)nCount, pTextFormat.Get(), + textMetrics.widthIncludingTrailingWhitespace, + max_height)); } if (_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT) { - HR(pDWR->SetLayoutReadingDirection(DWRITE_READING_DIRECTION_TOP_TO_BOTTOM)); - HR(pDWR->SetLayoutFlowDirection(DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT)); + HR(pDWR_->SetLayoutReadingDirection( + DWRITE_READING_DIRECTION_TOP_TO_BOTTOM)); + HR(pDWR_->SetLayoutFlowDirection(DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT)); } DWRITE_OVERHANG_METRICS overhangMetrics; - HR(pDWR->GetLayoutOverhangMetrics(&overhangMetrics)); + HR(pDWR_->GetLayoutOverhangMetrics(&overhangMetrics)); { if (overhangMetrics.left > 0) lpSize->cx += (LONG)(overhangMetrics.left + 1); @@ -232,8 +229,7 @@ void weasel::StandardLayout::GetTextSizeDW( CSize StandardLayout::GetPreeditSize(CDCHandle dc, const weasel::Text& text, - ComPtr pTextFormat, - DirectWriteResources* pDWR) { + ComPtr pTextFormat) { const std::wstring& preedit = text.str; const std::vector& attrs = text.attributes; CSize size(0, 0); @@ -246,12 +242,10 @@ CSize StandardLayout::GetPreeditSize(CDCHandle dc, std::wstring before_str = preedit.substr(0, _range.start); std::wstring hilited_str = preedit.substr(_range.start, _range.end); std::wstring after_str = preedit.substr(_range.end); - GetTextSizeDW(before_str, before_str.length(), pTextFormat, pDWR, - &_beforesz); - GetTextSizeDW(hilited_str, hilited_str.length(), pTextFormat, pDWR, + GetTextSizeDW(before_str, before_str.length(), pTextFormat, &_beforesz); + GetTextSizeDW(hilited_str, hilited_str.length(), pTextFormat, &_hilitedsz); - GetTextSizeDW(after_str, after_str.length(), pTextFormat, pDWR, - &_aftersz); + GetTextSizeDW(after_str, after_str.length(), pTextFormat, &_aftersz); auto width_max = 0, height_max = 0; if (_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT) { width_max = max(width_max, _beforesz.cx); @@ -273,7 +267,7 @@ CSize StandardLayout::GetPreeditSize(CDCHandle dc, size.cx = width_max; size.cy = height_max; } else - GetTextSizeDW(preedit, preedit.length(), pTextFormat, pDWR, &size); + GetTextSizeDW(preedit, preedit.length(), pTextFormat, &size); } return size; } diff --git a/WeaselUI/StandardLayout.h b/WeaselUI/StandardLayout.h index 3514bea8c..6b8e387ab 100644 --- a/WeaselUI/StandardLayout.h +++ b/WeaselUI/StandardLayout.h @@ -20,7 +20,7 @@ class StandardLayout : public Layout { /* Layout */ - virtual void DoLayout(CDCHandle dc, DirectWriteResources* pDWR = NULL) = 0; + virtual void DoLayout(CDCHandle dc) = 0; virtual CSize GetContentSize() const { return _contentSize; } virtual CRect GetPreeditRect() const { return _preeditRect; } virtual CRect GetAuxiliaryRect() const { return _auxiliaryRect; } @@ -54,7 +54,6 @@ class StandardLayout : public Layout { void GetTextSizeDW(const std::wstring& text, size_t nCount, ComPtr& pTextFormat, - DirectWriteResources* pDWR, LPSIZE lpSize) const; protected: @@ -74,8 +73,8 @@ class StandardLayout : public Layout { bool page_en = false; }; - MarkMetrics ComputeMarkMetrics(DirectWriteResources* pDWR); - PagerMetrics ComputePagerMetrics(DirectWriteResources* pDWR); + MarkMetrics ComputeMarkMetrics(); + PagerMetrics ComputePagerMetrics(); void PlacePagerHorizontal(const PagerMetrics& pager, int contentWidth, int contentHeight); @@ -88,8 +87,7 @@ class StandardLayout : public Layout { bool endIsEnd = true); CSize GetPreeditSize(CDCHandle dc, const weasel::Text& text, - ComPtr pTextFormat = NULL, - DirectWriteResources* pDWR = NULL); + ComPtr pTextFormat = NULL); bool _IsHighlightOverCandidateWindow(CRect& rc, CDCHandle& dc); void _PrepareRoundInfo(CDCHandle& dc); diff --git a/WeaselUI/VHorizontalLayout.cpp b/WeaselUI/VHorizontalLayout.cpp index c7019d508..6bd051e33 100644 --- a/WeaselUI/VHorizontalLayout.cpp +++ b/WeaselUI/VHorizontalLayout.cpp @@ -4,25 +4,25 @@ using namespace weasel; -void VHorizontalLayout::DoLayout(CDCHandle dc, DirectWriteResources* pDWR) { +void VHorizontalLayout::DoLayout(CDCHandle dc) { if (_style.vertical_text_with_wrap) { - DoLayoutWithWrap(dc, pDWR); + DoLayoutWithWrap(dc); return; } CSize size; int height = offsetY, width = offsetX + real_margin_x; int h = offsetY + real_margin_y; - MarkMetrics mark = ComputeMarkMetrics(pDWR); + MarkMetrics mark = ComputeMarkMetrics(); int base_offset = mark.base_offset; - PagerMetrics pager = ComputePagerMetrics(pDWR); + PagerMetrics pager = ComputePagerMetrics(); int pgw = pager.pgw; int pgh = pager.pgh; /* Preedit */ if (!IsInlinePreedit() && !_context.preedit.str.empty()) { - size = GetPreeditSize(dc, _context.preedit, pDWR->pPreeditTextFormat, pDWR); + size = GetPreeditSize(dc, _context.preedit, pDWR_->pPreeditTextFormat); int szx = max(size.cx, pgw), szy = pgh; // icon size wider then preedit text int xoffset = (STATUS_ICON_SIZE >= szx && ShouldDisplayStatusIcon()) @@ -38,7 +38,7 @@ void VHorizontalLayout::DoLayout(CDCHandle dc, DirectWriteResources* pDWR) { /* Auxiliary */ if (!_context.aux.str.empty()) { - size = GetPreeditSize(dc, _context.aux, pDWR->pPreeditTextFormat, pDWR); + size = GetPreeditSize(dc, _context.aux, pDWR_->pPreeditTextFormat); // icon size wider then preedit text int xoffset = (STATUS_ICON_SIZE >= size.cx && ShouldDisplayStatusIcon()) ? (STATUS_ICON_SIZE - size.cx) / 2 @@ -59,7 +59,7 @@ void VHorizontalLayout::DoLayout(CDCHandle dc, DirectWriteResources* pDWR) { /* Label */ std::wstring label = GetLabelText(labels, i, _style.label_text_format.c_str()); - GetTextSizeDW(label, label.length(), pDWR->pLabelTextFormat, pDWR, &size); + GetTextSizeDW(label, label.length(), pDWR_->pLabelTextFormat, &size); _candidateLabelRects[i].SetRect(w, h, w + size.cx * labelFontValid, h + size.cy); h += size.cy * labelFontValid; @@ -69,7 +69,7 @@ void VHorizontalLayout::DoLayout(CDCHandle dc, DirectWriteResources* pDWR) { /* Text */ h += _style.hilite_spacing * labelFontValid; const std::wstring& text = candidates.at(i).str; - GetTextSizeDW(text, text.length(), pDWR->pTextFormat, pDWR, &size); + GetTextSizeDW(text, text.length(), pDWR_->pTextFormat, &size); _candidateTextRects[i].SetRect(w, h, w + size.cx * textFontValid, h + size.cy); h += size.cy * textFontValid; @@ -83,7 +83,7 @@ void VHorizontalLayout::DoLayout(CDCHandle dc, DirectWriteResources* pDWR) { if (!comments.at(i).str.empty() && cmtFontValid && cmtFontNotTrans) { h += _style.hilite_spacing; const std::wstring& comment = comments.at(i).str; - GetTextSizeDW(comment, comment.length(), pDWR->pCommentTextFormat, pDWR, + GetTextSizeDW(comment, comment.length(), pDWR_->pCommentTextFormat, &size); _candidateCommentRects[i].SetRect(w, 0, w + size.cx * cmtFontValid, size.cy * cmtFontValid); @@ -199,21 +199,20 @@ void VHorizontalLayout::DoLayout(CDCHandle dc, DirectWriteResources* pDWR) { _contentRect.DeflateRect(offsetX, offsetY); } -void VHorizontalLayout::DoLayoutWithWrap(CDCHandle dc, - DirectWriteResources* pDWR) { +void VHorizontalLayout::DoLayoutWithWrap(CDCHandle dc) { CSize size; int height = offsetY, width = offsetX + real_margin_x; int h = offsetY + real_margin_y; - MarkMetrics mark = ComputeMarkMetrics(pDWR); + MarkMetrics mark = ComputeMarkMetrics(); int base_offset = mark.base_offset; - PagerMetrics pager = ComputePagerMetrics(pDWR); + PagerMetrics pager = ComputePagerMetrics(); int pgw = pager.pgw; int pgh = pager.pgh; /* Preedit */ if (!IsInlinePreedit() && !_context.preedit.str.empty()) { - size = GetPreeditSize(dc, _context.preedit, pDWR->pPreeditTextFormat, pDWR); + size = GetPreeditSize(dc, _context.preedit, pDWR_->pPreeditTextFormat); size_t szx = max(size.cx, pgw), szy = pgh; // icon size wider then preedit text int xoffset = ((size_t)STATUS_ICON_SIZE >= szx && ShouldDisplayStatusIcon()) @@ -229,7 +228,7 @@ void VHorizontalLayout::DoLayoutWithWrap(CDCHandle dc, } /* Auxiliary */ if (!_context.aux.str.empty()) { - size = GetPreeditSize(dc, _context.aux, pDWR->pPreeditTextFormat, pDWR); + size = GetPreeditSize(dc, _context.aux, pDWR_->pPreeditTextFormat); // icon size wider then auxiliary text int xoffset = (STATUS_ICON_SIZE >= size.cx && ShouldDisplayStatusIcon()) ? (STATUS_ICON_SIZE - size.cx) / 2 @@ -256,7 +255,7 @@ void VHorizontalLayout::DoLayoutWithWrap(CDCHandle dc, /* Label */ std::wstring label = GetLabelText(labels, i, _style.label_text_format.c_str()); - GetTextSizeDW(label, label.length(), pDWR->pLabelTextFormat, pDWR, &size); + GetTextSizeDW(label, label.length(), pDWR_->pLabelTextFormat, &size); _candidateLabelRects[i].SetRect(width, h, width + size.cx, h + size.cy * labelFontValid); h += size.cy * labelFontValid; @@ -265,7 +264,7 @@ void VHorizontalLayout::DoLayoutWithWrap(CDCHandle dc, /* Text */ h += _style.hilite_spacing; const std::wstring& text = candidates.at(i).str; - GetTextSizeDW(text, text.length(), pDWR->pTextFormat, pDWR, &size); + GetTextSizeDW(text, text.length(), pDWR_->pTextFormat, &size); _candidateTextRects[i].SetRect(width, h, width + size.cx, h + size.cy * textFontValid); h += size.cy * textFontValid; @@ -277,7 +276,7 @@ void VHorizontalLayout::DoLayoutWithWrap(CDCHandle dc, (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, + GetTextSizeDW(comment, comment.length(), pDWR_->pCommentTextFormat, &size); h += _style.hilite_spacing; _candidateCommentRects[i].SetRect(width, h, width + size.cx, diff --git a/WeaselUI/VHorizontalLayout.h b/WeaselUI/VHorizontalLayout.h index 4a5e83aad..bfdd2b957 100644 --- a/WeaselUI/VHorizontalLayout.h +++ b/WeaselUI/VHorizontalLayout.h @@ -10,9 +10,9 @@ class VHorizontalLayout : public StandardLayout { const Status& status, DirectWriteResources* pDWR) : StandardLayout(style, context, status, pDWR) {} - virtual void DoLayout(CDCHandle dc, DirectWriteResources* pDWR = NULL); + virtual void DoLayout(CDCHandle dc); private: - void DoLayoutWithWrap(CDCHandle dc, DirectWriteResources* pDWR = NULL); + void DoLayoutWithWrap(CDCHandle dc); }; }; // namespace weasel diff --git a/WeaselUI/VerticalLayout.cpp b/WeaselUI/VerticalLayout.cpp index 7eb5fee23..8a759a7a2 100644 --- a/WeaselUI/VerticalLayout.cpp +++ b/WeaselUI/VerticalLayout.cpp @@ -3,14 +3,13 @@ using namespace weasel; -void weasel::VerticalLayout::DoLayout(CDCHandle dc, - DirectWriteResources* pDWR) { +void weasel::VerticalLayout::DoLayout(CDCHandle dc) { const int space = _style.hilite_spacing; int width = 0, height = real_margin_y; - MarkMetrics mark = ComputeMarkMetrics(pDWR); + MarkMetrics mark = ComputeMarkMetrics(); int base_offset = mark.base_offset; - PagerMetrics pager = ComputePagerMetrics(pDWR); + PagerMetrics pager = ComputePagerMetrics(); int pgw = pager.pgw; int pgh = pager.pgh; @@ -18,7 +17,7 @@ void weasel::VerticalLayout::DoLayout(CDCHandle dc, CSize size; /* Preedit */ if (!IsInlinePreedit() && !_context.preedit.str.empty()) { - size = GetPreeditSize(dc, _context.preedit, pDWR->pPreeditTextFormat, pDWR); + size = GetPreeditSize(dc, _context.preedit, pDWR_->pPreeditTextFormat); int szx = pgw, szy = max(size.cy, pgh); // icon size higher then preedit text int yoffset = (STATUS_ICON_SIZE >= szy && ShouldDisplayStatusIcon()) @@ -35,7 +34,7 @@ void weasel::VerticalLayout::DoLayout(CDCHandle dc, /* Auxiliary */ if (!_context.aux.str.empty()) { - size = GetPreeditSize(dc, _context.aux, pDWR->pPreeditTextFormat, pDWR); + size = GetPreeditSize(dc, _context.aux, pDWR_->pPreeditTextFormat); // icon size higher then auxiliary text int yoffset = (STATUS_ICON_SIZE >= size.cy && ShouldDisplayStatusIcon()) ? (STATUS_ICON_SIZE - size.cy) / 2 @@ -61,7 +60,7 @@ void weasel::VerticalLayout::DoLayout(CDCHandle dc, /* Label */ std::wstring label = GetLabelText(labels, i, _style.label_text_format.c_str()); - GetTextSizeDW(label, label.length(), pDWR->pLabelTextFormat, pDWR, &size); + GetTextSizeDW(label, label.length(), pDWR_->pLabelTextFormat, &size); _candidateLabelRects[i].SetRect(w, height, w + size.cx * labelFontValid, height + size.cy); _candidateLabelRects[i].OffsetRect(offsetX, offsetY); @@ -71,7 +70,7 @@ void weasel::VerticalLayout::DoLayout(CDCHandle dc, /* Text */ const std::wstring& text = candidates.at(i).str; - GetTextSizeDW(text, text.length(), pDWR->pTextFormat, pDWR, &size); + GetTextSizeDW(text, text.length(), pDWR_->pTextFormat, &size); _candidateTextRects[i].SetRect(w, height, w + size.cx * textFontValid, height + size.cy); _candidateTextRects[i].OffsetRect(offsetX, offsetY); @@ -89,7 +88,7 @@ void weasel::VerticalLayout::DoLayout(CDCHandle dc, comment_shift_width = max(comment_shift_width, w); const std::wstring& comment = comments.at(i).str; - GetTextSizeDW(comment, comment.length(), pDWR->pCommentTextFormat, pDWR, + GetTextSizeDW(comment, comment.length(), pDWR_->pCommentTextFormat, &size); _candidateCommentRects[i].SetRect(0, height, size.cx * cmtFontValid, height + size.cy); diff --git a/WeaselUI/VerticalLayout.h b/WeaselUI/VerticalLayout.h index 7f1c1d17e..6cb47ca5a 100644 --- a/WeaselUI/VerticalLayout.h +++ b/WeaselUI/VerticalLayout.h @@ -10,6 +10,6 @@ class VerticalLayout : public StandardLayout { const Status& status, DirectWriteResources* pDWR) : StandardLayout(style, context, status, pDWR) {} - virtual void DoLayout(CDCHandle dc, DirectWriteResources* pDWR = NULL); + virtual void DoLayout(CDCHandle dc); }; }; // namespace weasel diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index e15f9332f..e2b45483c 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -179,7 +179,7 @@ void WeaselPanel::Refresh() { _CreateLayout(); CDCHandle dc = GetDC(); - m_layout->DoLayout(dc, pDWR); + m_layout->DoLayout(dc); ReleaseDC(dc); _ResizeWindow(); _RepositionWindow(); From d443e0fbc873e2e84a0b12fa84199200214f2702 Mon Sep 17 00:00:00 2001 From: fxliang Date: Mon, 9 Feb 2026 16:22:33 +0800 Subject: [PATCH 18/25] perf(WeaselUI): fast return when no need to relayout and redraw --- WeaselUI/WeaselPanel.cpp | 52 ++++++++++++++++++++++++++++++---------- WeaselUI/WeaselPanel.h | 2 +- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index e2b45483c..93ad22dc6 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -175,7 +175,29 @@ void WeaselPanel::Refresh() { // only RedrawWindow if no need to hide candidates window, or // inline_no_candidates if (!hide_candidates || inline_no_candidates) { - _InitFontRes(); + HMONITOR hMonitor = MonitorFromRect(m_inputPos, MONITOR_DEFAULTTONEAREST); + UINT dpiX = 96, dpiY = 96; + if (hMonitor) + GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + + bool context_changed = (m_ctx != m_octx); + bool style_changed = (m_ostyle != m_style); + bool dpi_changed = (dpi != dpiX); + bool layout_missing = (m_layout == NULL); + + // If nothing changed, return + if (!layout_missing && !context_changed && !style_changed && !dpi_changed) { + if (!IS_FULLSCREENLAYOUT(m_style)) + return; + // For fullscreen layout, we might need to check if work area changed + // (even if dpi same) + if (hMonitor == m_hMonitor) + return; + } + + _InitFontRes(false, dpiX); + // Recreate layout if any state changed (passed the guard above) + // Layout depends on Context (counts), Style, DPI, and Monitor. _CreateLayout(); CDCHandle dc = GetDC(); @@ -183,29 +205,35 @@ void WeaselPanel::Refresh() { ReleaseDC(dc); _ResizeWindow(); _RepositionWindow(); - if (m_ctx != m_octx) { + + RedrawWindow(); + + if (context_changed) { m_octx = m_ctx; - RedrawWindow(); } } } -void WeaselPanel::_InitFontRes(bool forced) { - HMONITOR hMonitor = MonitorFromRect(m_inputPos, MONITOR_DEFAULTTONEAREST); - UINT dpiX = 96, dpiY = 96; - if (hMonitor) - GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); +void WeaselPanel::_InitFontRes(bool forced, UINT dpiX) { + if (dpiX == 0) { + HMONITOR hMonitor = MonitorFromRect(m_inputPos, MONITOR_DEFAULTTONEAREST); + dpiX = 96; + UINT dpiY = 96; + if (hMonitor) + GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + } bool styleChanged = (m_ostyle != m_style); bool dpiChanged = (dpiX != dpi); - if (!forced && (pDWR != NULL) && !styleChanged && !dpiChanged) { + if (!forced && (pDWR != NULL) && !styleChanged && !dpiChanged && + pDWR->pTextFormat != NULL) { return; } - // prepare d2d1 resources - // if style changed, or dpi changed, re-initialize directwrite resources - if (styleChanged || dpiChanged) { + // if style changed, or dpi changed, or pTextFormat null re-initialize + // directwrite resources + if (styleChanged || dpiChanged || !pDWR->pTextFormat) { pDWR->InitResources(m_style, dpiX); } pDWR->EnsureRenderTarget(m_style.antialias_mode); diff --git a/WeaselUI/WeaselPanel.h b/WeaselUI/WeaselPanel.h index d24c90681..60404b077 100644 --- a/WeaselUI/WeaselPanel.h +++ b/WeaselUI/WeaselPanel.h @@ -79,7 +79,7 @@ class WeaselPanel int DPI_SCALE(T t) { return (int)(t * dpiScaleLayout); } - void _InitFontRes(bool forced = false); + void _InitFontRes(bool forced = false, UINT dpiX = 0); void _CaptureRect(CRect& rect); bool m_mouse_entry = false; void _CreateLayout(); From cc151ee818f7bd9d6da27184cadce8ed8b6a4595 Mon Sep 17 00:00:00 2001 From: fxliang Date: Mon, 9 Feb 2026 17:04:34 +0800 Subject: [PATCH 19/25] perf(WeaselUI): reduce hMonitor getting in _RepositionWindow --- WeaselUI/WeaselPanel.cpp | 8 +++++--- WeaselUI/WeaselPanel.h | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index 93ad22dc6..f5cc1005b 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -204,7 +204,7 @@ void WeaselPanel::Refresh() { m_layout->DoLayout(dc); ReleaseDC(dc); _ResizeWindow(); - _RepositionWindow(); + _RepositionWindow(false, hMonitor); RedrawWindow(); @@ -1208,10 +1208,12 @@ void WeaselPanel::MoveTo(RECT const& rc) { } } -void WeaselPanel::_RepositionWindow(const bool& adj) { +void WeaselPanel::_RepositionWindow(const bool& adj, HMONITOR hMonitor) { RECT rcWorkArea; memset(&rcWorkArea, 0, sizeof(rcWorkArea)); - HMONITOR hMonitor = MonitorFromRect(m_inputPos, MONITOR_DEFAULTTONEAREST); + if (hMonitor == NULL) + hMonitor = MonitorFromRect(m_inputPos, MONITOR_DEFAULTTONEAREST); + if (hMonitor) { MONITORINFO info; info.cbSize = sizeof(MONITORINFO); diff --git a/WeaselUI/WeaselPanel.h b/WeaselUI/WeaselPanel.h index 60404b077..35e1af7a3 100644 --- a/WeaselUI/WeaselPanel.h +++ b/WeaselUI/WeaselPanel.h @@ -84,7 +84,7 @@ class WeaselPanel bool m_mouse_entry = false; void _CreateLayout(); void _ResizeWindow(); - void _RepositionWindow(const bool& adj = false); + void _RepositionWindow(const bool& adj = false, HMONITOR hMonitor = NULL); bool _DrawPreedit(const Text& text, CDCHandle dc, const CRect& rc); bool _DrawPreeditBack(const Text& text, Gdiplus::Graphics& g_back, From 0dd417236b8c446ad623322ef827959917978feb Mon Sep 17 00:00:00 2001 From: fxliang Date: Mon, 9 Feb 2026 17:13:02 +0800 Subject: [PATCH 20/25] fix(WeaselUI): hover is triggered when mouse not moved --- WeaselUI/WeaselPanel.cpp | 7 +++++++ WeaselUI/WeaselPanel.h | 1 + 2 files changed, 8 insertions(+) diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index f5cc1005b..a4c16ba32 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -500,6 +500,13 @@ LRESULT WeaselPanel::OnMouseMove(UINT uMsg, point.x = GET_X_LPARAM(lParam); point.y = GET_Y_LPARAM(lParam); + // Ignore if mouse screen position not changed + CPoint ptScreen = point; + ClientToScreen(&ptScreen); + if (ptScreen == m_lastMousePos) + return 0; + m_lastMousePos = ptScreen; + for (size_t i = 0; i < m_candidateCount && i < MAX_CANDIDATES_COUNT; ++i) { CRect rect = m_layout->GetCandidateRect((int)i); _OffsetRectIfIsToRepos(rect, m_offsetys[i]); diff --git a/WeaselUI/WeaselPanel.h b/WeaselUI/WeaselPanel.h index 35e1af7a3..e1dfb0886 100644 --- a/WeaselUI/WeaselPanel.h +++ b/WeaselUI/WeaselPanel.h @@ -82,6 +82,7 @@ class WeaselPanel void _InitFontRes(bool forced = false, UINT dpiX = 0); void _CaptureRect(CRect& rect); bool m_mouse_entry = false; + CPoint m_lastMousePos = {-1, -1}; void _CreateLayout(); void _ResizeWindow(); void _RepositionWindow(const bool& adj = false, HMONITOR hMonitor = NULL); From 45e9db0160fa8a2f98041a0f6207b619c3a9d01f Mon Sep 17 00:00:00 2001 From: fxliang Date: Wed, 11 Feb 2026 17:57:44 +0800 Subject: [PATCH 21/25] fix(WeaselUI): mark metrics of fullscreenlayout --- WeaselUI/FullScreenLayout.cpp | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/WeaselUI/FullScreenLayout.cpp b/WeaselUI/FullScreenLayout.cpp index de85392ec..c702fcf0a 100644 --- a/WeaselUI/FullScreenLayout.cpp +++ b/WeaselUI/FullScreenLayout.cpp @@ -24,30 +24,11 @@ void weasel::FullScreenLayout::DoLayout(CDCHandle dc) { int step = 32; do { m_layout->DoLayout(dc); - if ((_style.hilited_mark_color & 0xff000000)) { - CSize sg; - if (candidates_count) { - if (_style.mark_text.empty()) - GetTextSizeDW(L"|", 1, pDWR_->pTextFormat, &sg); - else - GetTextSizeDW(_style.mark_text, _style.mark_text.length(), - pDWR_->pTextFormat, &sg); - } - mark_width = sg.cx; - mark_height = sg.cy; - if (_style.mark_text.empty()) { - mark_width = mark_height / 7; - if (_style.linespacing && _style.baseline) - mark_width = - (int)((float)mark_width / ((float)_style.linespacing / 100.0f)); - mark_width = max(mark_width, 6); - } - mark_gap = (_style.mark_text.empty()) - ? mark_width - : mark_width + _style.hilite_spacing; - } } while (AdjustFontPoint(dc, workArea, step)); + mark_height = m_layout->mark_height; + mark_width = m_layout->mark_width; + mark_gap = m_layout->mark_gap; int offsetx = (workArea.Width() - m_layout->GetContentSize().cx) / 2; int offsety = (workArea.Height() - m_layout->GetContentSize().cy) / 2; _preeditRect = m_layout->GetPreeditRect(); From 827a4924b2576248c0f0f4e001fc2034bf043262 Mon Sep 17 00:00:00 2001 From: fxliang Date: Thu, 12 Feb 2026 19:41:14 +0800 Subject: [PATCH 22/25] fix cacheStyle.spacing --- WeaselUI/WeaselPanel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index a4c16ba32..0531a044a 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -1015,9 +1015,9 @@ void WeaselPanel::_UpdateOffsets(const CRect& auxrc, const CRect& preeditrc) { } int base_gap = 0; if (!m_ctx.aux.str.empty()) - base_gap = auxrc.Height() + m_style.spacing; + base_gap = auxrc.Height() + m_cachedStyle.spacing; else if (!m_layout->IsInlinePreedit() && !m_ctx.preedit.str.empty()) - base_gap = preeditrc.Height() + m_style.spacing; + base_gap = preeditrc.Height() + m_cachedStyle.spacing; for (auto i = 0; i < m_candidateCount && i < MAX_CANDIDATES_COUNT; ++i) { if (i == 0) From daecc1b7393a5a8b16cb2da24cdd34d7e9ff4612 Mon Sep 17 00:00:00 2001 From: fxliang Date: Thu, 12 Feb 2026 21:49:44 +0800 Subject: [PATCH 23/25] chore: fix WeaselPanel.cpp bomb issue --- WeaselUI/WeaselPanel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index 0531a044a..139b843be 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -1,4 +1,4 @@ -#include "stdafx.h" +#include "stdafx.h" #include "WeaselPanel.h" #include From aef28b72708ccf444b8ed29d3fd8bbb7b019986c Mon Sep 17 00:00:00 2001 From: fxliang Date: Thu, 12 Feb 2026 21:53:02 +0800 Subject: [PATCH 24/25] refactor(WeaselUI): clear text format cache when style/dpi changed --- WeaselUI/WeaselPanel.cpp | 1 + include/WeaselUI.h | 1 + 2 files changed, 2 insertions(+) diff --git a/WeaselUI/WeaselPanel.cpp b/WeaselUI/WeaselPanel.cpp index 139b843be..00af6e8bf 100644 --- a/WeaselUI/WeaselPanel.cpp +++ b/WeaselUI/WeaselPanel.cpp @@ -234,6 +234,7 @@ void WeaselPanel::_InitFontRes(bool forced, UINT dpiX) { // if style changed, or dpi changed, or pTextFormat null re-initialize // directwrite resources if (styleChanged || dpiChanged || !pDWR->pTextFormat) { + pDWR->ClearTextFormatCache(); pDWR->InitResources(m_style, dpiX); } pDWR->EnsureRenderTarget(m_style.antialias_mode); diff --git a/include/WeaselUI.h b/include/WeaselUI.h index 13dd80c7b..f62d06f64 100755 --- a/include/WeaselUI.h +++ b/include/WeaselUI.h @@ -136,6 +136,7 @@ class DirectWriteResources { color, pBrush.ReleaseAndGetAddressOf()); } } + void ClearTextFormatCache() { _textFormatCache.clear(); } float dpiScaleFontPoint, dpiScaleLayout; ComPtr pD2d1Factory; From d1fa2c9cc8cdc4164c19317dbc4004d38f7dd865 Mon Sep 17 00:00:00 2001 From: fxliang Date: Thu, 12 Feb 2026 22:38:35 +0800 Subject: [PATCH 25/25] fix: antialias_mode can't be update at runtime --- WeaselUI/DirectWriteResources.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/WeaselUI/DirectWriteResources.cpp b/WeaselUI/DirectWriteResources.cpp index a25c9a8f5..4d9f38601 100644 --- a/WeaselUI/DirectWriteResources.cpp +++ b/WeaselUI/DirectWriteResources.cpp @@ -53,8 +53,13 @@ DirectWriteResources::~DirectWriteResources() { } HRESULT DirectWriteResources::EnsureRenderTarget(int antialiasMode) { - if (pRenderTarget) + if (pRenderTarget) { + if (pRenderTarget->GetTextAntialiasMode() != + (D2D1_TEXT_ANTIALIAS_MODE)antialiasMode) + pRenderTarget->SetTextAntialiasMode( + (D2D1_TEXT_ANTIALIAS_MODE)antialiasMode); return S_OK; + } static const D2D1_PIXEL_FORMAT format = D2D1::PixelFormat( DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED);