Skip to content

Commit 12f6d8c

Browse files
nameless911claude
andcommitted
Keep the suggestion window on one side and draw it upside down above
The suggestion window grows and shrinks while typing, and it was flipped above the preedit only when the current size did not fit below. Near the bottom of the screen it jumped up and down on every key stroke. Decide the side with the size of a full suggestion window (3 rows) and, when it is above the preedit, draw it upside down: the best candidate is next to the preedit and the footer is at the top. Conversion windows are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent cad1d35 commit 12f6d8c

3 files changed

Lines changed: 84 additions & 16 deletions

File tree

‎src/renderer/win32/candidate_window.cc‎

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ void CandidateWindow::HandleMouseEvent(UINT nFlags, const CPoint& point,
329329
const commands::CandidateWindow::Candidate& candidate =
330330
candidate_window_->candidate(i);
331331

332-
const CRect rect = ToCRect(table_layout_->GetRowRect(i));
332+
const CRect rect = ToCRect(FlipIfUpsideDown(table_layout_->GetRowRect(i)));
333333
if (rect.PtInRect(point)) {
334334
commands::SessionCommand command;
335335
if (close_candidatewindow) {
@@ -636,7 +636,8 @@ Rect CandidateWindow::GetSelectionRectInScreenCord() const {
636636
focused_array_index < candidate_window_->candidate_size()) {
637637
(void)candidate_window_->candidate(focused_array_index);
638638

639-
CRect rect = ToCRect(table_layout_->GetRowRect(focused_array_index));
639+
CRect rect = ToCRect(
640+
FlipIfUpsideDown(table_layout_->GetRowRect(focused_array_index)));
640641
ClientToScreen(&rect);
641642
return Rect(rect.left, rect.top, rect.Width(), rect.Height());
642643
}
@@ -654,7 +655,27 @@ Rect CandidateWindow::GetFirstRowInClientCord() const {
654655
DCHECK(table_layout_->IsLayoutFrozen()) << "Table layout is not frozen.";
655656
DCHECK_GT(table_layout_->number_of_rows(), 0)
656657
<< "number of rows should be positive";
657-
return table_layout_->GetRowRect(0);
658+
return FlipIfUpsideDown(table_layout_->GetRowRect(0));
659+
}
660+
661+
void CandidateWindow::SetUpsideDown(bool upside_down) {
662+
if (upside_down_ == upside_down) {
663+
return;
664+
}
665+
upside_down_ = upside_down;
666+
if (m_hWnd != nullptr) {
667+
Invalidate();
668+
}
669+
}
670+
671+
Rect CandidateWindow::FlipIfUpsideDown(Rect rect) const {
672+
if (!upside_down_) {
673+
return rect;
674+
}
675+
// Mirror vertically within the window so that the first row comes to the
676+
// bottom and the footer comes to the top.
677+
rect.origin.y = table_layout_->GetTotalSize().height - rect.Bottom();
678+
return rect;
658679
}
659680

660681
void CandidateWindow::DrawCells(HDC dc) {
@@ -676,15 +697,17 @@ void CandidateWindow::DrawCells(HDC dc) {
676697
candidate_window_->candidate(i);
677698
const std::wstring display_string =
678699
GetDisplayStringByColumn(candidate, column_type);
679-
const Rect text_rect = table_layout_->GetCellRect(i, column_type);
700+
const Rect text_rect =
701+
FlipIfUpsideDown(table_layout_->GetCellRect(i, column_type));
680702
display_list.push_back(TextRenderingInfo(display_string, text_rect));
681703
}
682704
text_renderer_->RenderTextList(dc, display_list, font_type);
683705
}
684706
}
685707

686708
void CandidateWindow::DrawVScrollBar(HDC dc) {
687-
const Rect& vscroll_rect = table_layout_->GetVScrollBarRect();
709+
const Rect vscroll_rect =
710+
FlipIfUpsideDown(table_layout_->GetVScrollBarRect());
688711

689712
if (!vscroll_rect.IsRectEmpty() && candidate_window_->candidate_size() > 0) {
690713
const int begin_index = candidate_window_->candidate(0).index();
@@ -697,8 +720,9 @@ void CandidateWindow::DrawVScrollBar(HDC dc) {
697720
FillSolidRect(dc, &background_crect,
698721
ToColorRef(style_.scrollbar_background_color()));
699722

700-
const mozc::Rect& indicator_rect = table_layout_->GetVScrollIndicatorRect(
701-
begin_index, end_index, candidates_total);
723+
const mozc::Rect indicator_rect =
724+
FlipIfUpsideDown(table_layout_->GetVScrollIndicatorRect(
725+
begin_index, end_index, candidates_total));
702726

703727
const CRect indicator_crect = ToCRect(indicator_rect);
704728
FillSolidRect(dc, &indicator_crect,
@@ -708,7 +732,8 @@ void CandidateWindow::DrawVScrollBar(HDC dc) {
708732

709733
void CandidateWindow::DrawShortcutBackground(HDC dc) {
710734
if (table_layout_->number_of_columns() > 0) {
711-
Rect shortcut_colmun_rect = table_layout_->GetColumnRect(0);
735+
Rect shortcut_colmun_rect =
736+
FlipIfUpsideDown(table_layout_->GetColumnRect(0));
712737
if (!shortcut_colmun_rect.IsRectEmpty()) {
713738
// Due to the mismatch of the implementation of the TableLayout class
714739
// and the design requiement, we have to *fix* the width and origin
@@ -727,27 +752,32 @@ void CandidateWindow::DrawShortcutBackground(HDC dc) {
727752
}
728753

729754
void CandidateWindow::DrawFooter(HDC dc) {
730-
const Rect& footer_rect = table_layout_->GetFooterRect();
755+
const Rect footer_rect = FlipIfUpsideDown(table_layout_->GetFooterRect());
731756
if (!candidate_window_->has_footer() || footer_rect.IsRectEmpty()) {
732757
return;
733758
}
734759

735760
const int footer_separator_height = style_.footer_border_colors_size();
736761

737762
// DC pen is available in Windows 2000 and later.
763+
// The separator faces the rows: at the top of the footer normally, and at
764+
// the bottom of the footer when the window is upside down.
738765
{
739766
wil::unique_select_object prev_pen =
740767
wil::SelectObject(dc, static_cast<HPEN>(::GetStockObject(DC_PEN)));
741-
for (size_t i = 0, y = footer_rect.Top(); i < footer_separator_height;
742-
y++, i++) {
768+
for (int i = 0; i < footer_separator_height; ++i) {
769+
const int y =
770+
upside_down_ ? footer_rect.Bottom() - 1 - i : footer_rect.Top() + i;
743771
::SetDCPenColor(dc, ToColorRef(style_.footer_border_colors(i)));
744772
::MoveToEx(dc, footer_rect.Left(), y, nullptr);
745773
::LineTo(dc, footer_rect.Right(), y);
746774
}
747775
}
748776

749777
const Rect footer_content_rect(
750-
footer_rect.Left(), footer_rect.Top() + footer_separator_height,
778+
footer_rect.Left(),
779+
upside_down_ ? footer_rect.Top()
780+
: footer_rect.Top() + footer_separator_height,
751781
footer_rect.Width(), footer_rect.Height() - footer_separator_height);
752782

753783
// Draw gradient rect in the footer area
@@ -843,8 +873,8 @@ void CandidateWindow::DrawSelectedRect(HDC dc) {
843873
focused_array_index < candidate_window_->candidate_size()) {
844874
(void)candidate_window_->candidate(focused_array_index);
845875

846-
const CRect selected_rect =
847-
ToCRect(table_layout_->GetRowRect(focused_array_index));
876+
const CRect selected_rect = ToCRect(
877+
FlipIfUpsideDown(table_layout_->GetRowRect(focused_array_index)));
848878
FillSolidRect(dc, &selected_rect,
849879
ToColorRef(style_.focused_background_color()));
850880

@@ -859,7 +889,7 @@ void CandidateWindow::DrawInformationIcon(HDC dc) {
859889
const double scale_factor = GetDPIScalingFactor(dpi_);
860890
for (size_t i = 0; i < candidate_window_->candidate_size(); ++i) {
861891
if (candidate_window_->candidate(i).has_information_id()) {
862-
CRect rect = ToCRect(table_layout_->GetRowRect(i));
892+
CRect rect = ToCRect(FlipIfUpsideDown(table_layout_->GetRowRect(i)));
863893
rect.left = rect.right - (6.0 * scale_factor);
864894
rect.right = rect.right - (2.0 * scale_factor);
865895
rect.top += (2.0 * scale_factor);

‎src/renderer/win32/candidate_window.h‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,17 @@ class CandidateWindow : public ATL::CWindowImpl<CandidateWindow, ATL::CWindow,
117117
Rect GetCandidateColumnInClientCord() const;
118118
Rect GetFirstRowInClientCord() const;
119119

120+
// When |upside_down| is true, the rows are drawn from the bottom (the first
121+
// candidate at the bottom) and the footer is drawn at the top. This is used
122+
// when the window is placed above the preedit.
123+
void SetUpsideDown(bool upside_down);
124+
120125
private:
121126
void DoPaint(HDC dc);
122127

128+
// Converts a rect from |table_layout_| according to |upside_down_|.
129+
Rect FlipIfUpsideDown(Rect rect) const;
130+
123131
void DrawCells(HDC dc);
124132
void DrawVScrollBar(HDC dc);
125133
void DrawShortcutBackground(HDC dc);
@@ -206,6 +214,7 @@ class CandidateWindow : public ATL::CWindowImpl<CandidateWindow, ATL::CWindow,
206214
int indicator_width_;
207215
bool metrics_changed_;
208216
bool mouse_moving_;
217+
bool upside_down_ = false;
209218
RendererStyle style_;
210219
};
211220

‎src/renderer/win32/window_manager.cc‎

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,10 +281,39 @@ void WindowManager::UpdateLayout(const commands::RendererCommand& command) {
281281
if (!vertical) {
282282
new_target_point.y = preedit_rect.Bottom();
283283
}
284+
// The suggestion window grows and shrinks while typing, and WindowUtil
285+
// flips it above the preedit only when it does not fit below. To keep it
286+
// from jumping up and down, decide the side with the size of a full
287+
// suggestion window instead of the current one.
288+
const bool is_suggestion =
289+
!vertical && candidate_window.category() == commands::SUGGESTION;
290+
Size placement_size = main_window_size;
291+
if (is_suggestion) {
292+
constexpr int kMaxSuggestionRows = 3;
293+
const int rows = candidate_window.candidate_size();
294+
if (rows < kMaxSuggestionRows) {
295+
placement_size.height += (kMaxSuggestionRows - rows) *
296+
main_window_->GetFirstRowInClientCord().Height();
297+
}
298+
}
284299
main_window_rect =
285300
WindowUtil::GetWindowRectForMainWindowFromTargetPointAndPreedit(
286-
new_target_point, preedit_rect, main_window_size,
301+
new_target_point, preedit_rect, placement_size,
287302
main_window_zero_point, working_area, vertical);
303+
304+
// When the suggestion window is above the preedit, anchor its bottom to
305+
// the preedit and draw it upside down so that the best candidate is the
306+
// closest to the preedit.
307+
bool upside_down = false;
308+
if (is_suggestion) {
309+
upside_down = main_window_rect.Top() < preedit_rect.Top();
310+
if (upside_down) {
311+
main_window_rect.origin.y =
312+
main_window_rect.Bottom() - main_window_size.height;
313+
}
314+
main_window_rect.size.height = main_window_size.height;
315+
}
316+
main_window_->SetUpsideDown(upside_down);
288317
}
289318

290319
const DWORD set_windows_pos_flags = SWP_NOACTIVATE | SWP_SHOWWINDOW;

0 commit comments

Comments
 (0)