From 37424fca16c03d4479157545d6fadaf3c4c1d07e Mon Sep 17 00:00:00 2001 From: Fuminobu TAKEYAMA Date: Fri, 15 Mar 2024 22:36:06 +0900 Subject: [PATCH] Fix #823: Support virtual coordinate in mozc_renderer to fix its window position on HiDPI displays (#876) Qt6 applications use virtual coordinates. Since IBus uses the device-pixel native coordinate system to represent a position of a pre-edit area, mozc_renderer converts it to the virtual coordinate. PiperOrigin-RevId: 616104261 --- src/renderer/qt/qt_window_manager.cc | 60 +++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/src/renderer/qt/qt_window_manager.cc b/src/renderer/qt/qt_window_manager.cc index e684da463..e5dee1d15 100644 --- a/src/renderer/qt/qt_window_manager.cc +++ b/src/renderer/qt/qt_window_manager.cc @@ -339,13 +339,69 @@ void FillCandidates(const commands::Candidates &candidates, const int width = kColumn0Width + max_width1 + max_width2 + kColumn3Width; table->resize(width, total_height); } + +class VirtualRect { + public: + static VirtualRect FromNativeRect(const Rect &native_rect) { + for (const QScreen *screen : QGuiApplication::screens()) { + const Rect rect = TranslateToVirtual(screen, native_rect); + const QRect screen_rect = screen->geometry(); + + // Use top left to locate a screen + if (screen_rect.contains(rect.Left(), rect.Top())) { + return VirtualRect(rect, mozc::renderer::GetRect(screen->geometry())); + } + } + + // fall back to primary screen + // TODO: Return the nearest monitor rect instead. + // See GetMonitorRect + const QScreen *screen = QGuiApplication::primaryScreen(); + const Rect rect = TranslateToVirtual(screen, native_rect); + return VirtualRect(rect, mozc::renderer::GetRect(screen->geometry())); + } + + Rect GetRect() const { return rect_; } + + Rect GetMonitorRect() const { return monitor_rect_; } + + private: + VirtualRect(const Rect &rect, const Rect &monitor_rect) + : rect_(rect), monitor_rect_(monitor_rect) {} + + static Rect TranslateToVirtual(const QScreen *screen, + const Rect &native_rect) { + const double device_pixel_ratio = screen->devicePixelRatio(); + // screen_left, screen_top have the same value in both virtual and native + // coordinate + const int screen_left = screen->geometry().x(); + const int screen_top = screen->geometry().y(); + const int dx = native_rect.Left() - screen_left; + const int dy = native_rect.Top() - screen_top; + const int vx = + static_cast(std::floor(dx / device_pixel_ratio)) + screen_left; + const int vy = + static_cast(std::floor(dy / device_pixel_ratio)) + screen_top; + + return Rect(vx, vy, native_rect.Width() / device_pixel_ratio, + native_rect.Height() / device_pixel_ratio); + } + + Rect rect_; + Rect monitor_rect_; +}; } // namespace Point QtWindowManager::GetWindowPosition( const commands::RendererCommand &command, const Size &win_size) { - const Rect preedit_rect = GetRect(command.preedit_rectangle()); + const Rect native_preedit_rect = GetRect(command.preedit_rectangle()); + // Qt6 applications use virtual coordinates. Since IBus uses the device-pixel + // native coordinate system, we need to translate a received rect to virtual. + const VirtualRect virtual_rect = + VirtualRect::FromNativeRect(native_preedit_rect); + const Rect preedit_rect = virtual_rect.GetRect(); const Point win_pos = Point(preedit_rect.Left(), preedit_rect.Bottom()); - const Rect monitor_rect = GetMonitorRect(win_pos.x, win_pos.y); + const Rect monitor_rect = virtual_rect.GetMonitorRect(); const Point offset_to_column1(kColumn0Width, 0); const Rect adjusted_win_geometry =