From 47cf4e7bfdb6593fc9fe30335b436f4869da65e4 Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Mon, 24 Mar 2025 12:08:24 +0100 Subject: [PATCH] fix: Wayland resizing border (#46155) * fix: Wayland resizing border Closes https://github.com/electron/electron/issues/44543 Refs CL:5180720 Fixes an issue where the resizing border didn't work as expected on Wayland windows. * fix: border insets when fullscreen --- ...electron_desktop_window_tree_host_linux.cc | 27 ++++++++---- .../electron_desktop_window_tree_host_linux.h | 2 + .../ui/views/client_frame_view_linux.cc | 43 ++++++++++--------- .../ui/views/client_frame_view_linux.h | 14 +++--- 4 files changed, 50 insertions(+), 36 deletions(-) diff --git a/shell/browser/ui/electron_desktop_window_tree_host_linux.cc b/shell/browser/ui/electron_desktop_window_tree_host_linux.cc index 7f79c7223ef8..eb8d61f7f6af 100644 --- a/shell/browser/ui/electron_desktop_window_tree_host_linux.cc +++ b/shell/browser/ui/electron_desktop_window_tree_host_linux.cc @@ -47,22 +47,28 @@ void ElectronDesktopWindowTreeHostLinux::OnWidgetInitDone() { UpdateFrameHints(); } +bool ElectronDesktopWindowTreeHostLinux::IsShowingFrame() const { + return !native_window_view_->IsFullscreen() && + !native_window_view_->IsMaximized() && + !native_window_view_->IsMinimized(); +} + gfx::Insets ElectronDesktopWindowTreeHostLinux::CalculateInsetsInDIP( ui::PlatformWindowState window_state) const { // If we are not showing frame, the insets should be zero. - if (native_window_view_->IsFullscreen()) { - return {}; + if (!IsShowingFrame()) { + return gfx::Insets(); } if (!native_window_view_->has_frame() || !native_window_view_->has_client_frame()) { - return {}; + return gfx::Insets(); } auto* view = static_cast( native_window_view_->widget()->non_client_view()->frame_view()); - gfx::Insets insets = view->GetBorderDecorationInsets(); + gfx::Insets insets = view->RestoredMirroredFrameBorderInsets(); if (base::i18n::IsRTL()) insets.set_left_right(insets.right(), insets.left()); return insets; @@ -99,9 +105,14 @@ void ElectronDesktopWindowTreeHostLinux::OnWindowTiledStateChanged( // of view. if (native_window_view_->has_frame() && native_window_view_->has_client_frame()) { - static_cast( - native_window_view_->widget()->non_client_view()->frame_view()) - ->set_tiled_edges(new_tiled_edges); + ClientFrameViewLinux* frame = static_cast( + native_window_view_->widget()->non_client_view()->frame_view()); + + bool maximized = new_tiled_edges.top && new_tiled_edges.left && + new_tiled_edges.bottom && new_tiled_edges.right; + bool tiled = new_tiled_edges.top || new_tiled_edges.left || + new_tiled_edges.bottom || new_tiled_edges.right; + frame->set_tiled(tiled && !maximized); } UpdateFrameHints(); } @@ -181,7 +192,7 @@ void ElectronDesktopWindowTreeHostLinux::UpdateFrameHints() { if (ui::OzonePlatform::GetInstance()->IsWindowCompositingSupported()) { // Set the opaque region. std::vector opaque_region; - if (!native_window_view_->IsFullscreen()) { + if (!IsShowingFrame()) { // The opaque region is a list of rectangles that contain only fully // opaque pixels of the window. We need to convert the clipping // rounded-rect into this format. diff --git a/shell/browser/ui/electron_desktop_window_tree_host_linux.h b/shell/browser/ui/electron_desktop_window_tree_host_linux.h index d1ee9a33a8b3..f1455aa0553d 100644 --- a/shell/browser/ui/electron_desktop_window_tree_host_linux.h +++ b/shell/browser/ui/electron_desktop_window_tree_host_linux.h @@ -65,6 +65,8 @@ class ElectronDesktopWindowTreeHostLinux private: void UpdateWindowState(ui::PlatformWindowState new_state); + bool IsShowingFrame() const; + raw_ptr native_window_view_; // weak ref base::ScopedObservation diff --git a/shell/browser/ui/views/client_frame_view_linux.cc b/shell/browser/ui/views/client_frame_view_linux.cc index a0e29dc385bb..d41a7d8d8fc7 100644 --- a/shell/browser/ui/views/client_frame_view_linux.cc +++ b/shell/browser/ui/views/client_frame_view_linux.cc @@ -39,7 +39,7 @@ namespace electron { namespace { // These values should be the same as Chromium uses. -constexpr int kResizeOutsideBorderSize = 10; +constexpr int kResizeBorder = 10; constexpr int kResizeInsideBoundsSize = 5; ui::NavButtonProvider::ButtonState ButtonStateToNavButtonProviderState( @@ -142,25 +142,28 @@ void ClientFrameViewLinux::Init(NativeWindowViews* window, UpdateThemeValues(); } -gfx::Insets ClientFrameViewLinux::GetBorderDecorationInsets() const { - const auto insets = GetFrameProvider()->GetFrameThicknessDip(); +gfx::Insets ClientFrameViewLinux::RestoredMirroredFrameBorderInsets() const { + auto border = RestoredFrameBorderInsets(); + return base::i18n::IsRTL() ? gfx::Insets::TLBR(border.top(), border.right(), + border.bottom(), border.left()) + : border; +} - // We shouldn't draw frame decorations for the tiled edges. - // See https://wayland.app/protocols/xdg-shell#xdg_toplevel:enum:state - const auto& edges = tiled_edges(); - return gfx::Insets::TLBR( - edges.top ? 0 : insets.top(), edges.left ? 0 : insets.left(), - edges.bottom ? 0 : insets.bottom(), edges.right ? 0 : insets.right()); +gfx::Insets ClientFrameViewLinux::RestoredFrameBorderInsets() const { + gfx::Insets insets = GetFrameProvider()->GetFrameThicknessDip(); + insets.SetToMax(GetInputInsets()); + return insets; } gfx::Insets ClientFrameViewLinux::GetInputInsets() const { - return gfx::Insets{ - host_supports_client_frame_shadow_ ? -kResizeOutsideBorderSize : 0}; + bool showing_shadow = host_supports_client_frame_shadow_ && + !frame_->IsMaximized() && !frame_->IsFullscreen(); + return gfx::Insets(showing_shadow ? kResizeBorder : 0); } gfx::Rect ClientFrameViewLinux::GetWindowContentBounds() const { gfx::Rect content_bounds = bounds(); - content_bounds.Inset(GetBorderDecorationInsets()); + content_bounds.Inset(RestoredMirroredFrameBorderInsets()); return content_bounds; } @@ -194,15 +197,15 @@ void ClientFrameViewLinux::OnWindowButtonOrderingChange() { } int ClientFrameViewLinux::ResizingBorderHitTest(const gfx::Point& point) { - return ResizingBorderHitTestImpl( - point, - GetBorderDecorationInsets() + gfx::Insets(kResizeInsideBoundsSize)); + return ResizingBorderHitTestImpl(point, + RestoredMirroredFrameBorderInsets() + + gfx::Insets(kResizeInsideBoundsSize)); } gfx::Rect ClientFrameViewLinux::GetBoundsForClientView() const { gfx::Rect client_bounds = bounds(); if (!frame_->IsFullscreen()) { - client_bounds.Inset(GetBorderDecorationInsets()); + client_bounds.Inset(RestoredMirroredFrameBorderInsets()); client_bounds.Inset( gfx::Insets::TLBR(GetTitlebarBounds().height(), 0, 0, 0)); } @@ -239,10 +242,8 @@ int ClientFrameViewLinux::NonClientHitTest(const gfx::Point& point) { } ui::WindowFrameProvider* ClientFrameViewLinux::GetFrameProvider() const { - const bool tiled = tiled_edges().top || tiled_edges().left || - tiled_edges().bottom || tiled_edges().right; return ui::LinuxUiTheme::GetForProfile(nullptr)->GetWindowFrameProvider( - !host_supports_client_frame_shadow_, tiled, frame_->IsMaximized()); + !host_supports_client_frame_shadow_, tiled(), frame_->IsMaximized()); } void ClientFrameViewLinux::GetWindowMask(const gfx::Size& size, @@ -465,7 +466,7 @@ gfx::Rect ClientFrameViewLinux::GetTitlebarBounds() const { std::max(font_height, theme_values_.titlebar_min_height) + GetTitlebarContentInsets().height(); - gfx::Insets decoration_insets = GetBorderDecorationInsets(); + gfx::Insets decoration_insets = RestoredMirroredFrameBorderInsets(); // We add the inset height here, so the .Inset() that follows won't reduce it // to be too small. @@ -486,7 +487,7 @@ gfx::Rect ClientFrameViewLinux::GetTitlebarContentBounds() const { } gfx::Size ClientFrameViewLinux::SizeWithDecorations(gfx::Size size) const { - gfx::Insets decoration_insets = GetBorderDecorationInsets(); + gfx::Insets decoration_insets = RestoredMirroredFrameBorderInsets(); size.Enlarge(0, GetTitlebarBounds().height()); size.Enlarge(decoration_insets.width(), decoration_insets.height()); diff --git a/shell/browser/ui/views/client_frame_view_linux.h b/shell/browser/ui/views/client_frame_view_linux.h index c05632de8639..9bb03a2472ba 100644 --- a/shell/browser/ui/views/client_frame_view_linux.h +++ b/shell/browser/ui/views/client_frame_view_linux.h @@ -43,16 +43,16 @@ class ClientFrameViewLinux : public FramelessView, void Init(NativeWindowViews* window, views::Widget* frame) override; // These are here for ElectronDesktopWindowTreeHostLinux to use. - gfx::Insets GetBorderDecorationInsets() const; + gfx::Insets RestoredMirroredFrameBorderInsets() const; + gfx::Insets RestoredFrameBorderInsets() const; gfx::Insets GetInputInsets() const; gfx::Rect GetWindowContentBounds() const; SkRRect GetRoundedWindowContentBounds() const; int GetTranslucentTopAreaHeight() const; - // Returns which edges of the frame are tiled. - const ui::WindowTiledEdges& tiled_edges() const { return tiled_edges_; } - void set_tiled_edges(ui::WindowTiledEdges tiled_edges) { - tiled_edges_ = tiled_edges; - } + + // Returns whether the frame is in a tiled state. + bool tiled() const { return tiled_; } + void set_tiled(bool tiled) { tiled_ = tiled; } protected: // ui::NativeThemeObserver: @@ -149,7 +149,7 @@ class ClientFrameViewLinux : public FramelessView, base::CallbackListSubscription paint_as_active_changed_subscription_; - ui::WindowTiledEdges tiled_edges_; + bool tiled_ = false; }; } // namespace electron