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
This commit is contained in:
Shelley Vohr 2025-03-24 12:08:24 +01:00 committed by GitHub
parent b8150f33db
commit 47cf4e7bfd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 50 additions and 36 deletions

View file

@ -47,22 +47,28 @@ void ElectronDesktopWindowTreeHostLinux::OnWidgetInitDone() {
UpdateFrameHints(); UpdateFrameHints();
} }
bool ElectronDesktopWindowTreeHostLinux::IsShowingFrame() const {
return !native_window_view_->IsFullscreen() &&
!native_window_view_->IsMaximized() &&
!native_window_view_->IsMinimized();
}
gfx::Insets ElectronDesktopWindowTreeHostLinux::CalculateInsetsInDIP( gfx::Insets ElectronDesktopWindowTreeHostLinux::CalculateInsetsInDIP(
ui::PlatformWindowState window_state) const { ui::PlatformWindowState window_state) const {
// If we are not showing frame, the insets should be zero. // If we are not showing frame, the insets should be zero.
if (native_window_view_->IsFullscreen()) { if (!IsShowingFrame()) {
return {}; return gfx::Insets();
} }
if (!native_window_view_->has_frame() || if (!native_window_view_->has_frame() ||
!native_window_view_->has_client_frame()) { !native_window_view_->has_client_frame()) {
return {}; return gfx::Insets();
} }
auto* view = static_cast<ClientFrameViewLinux*>( auto* view = static_cast<ClientFrameViewLinux*>(
native_window_view_->widget()->non_client_view()->frame_view()); native_window_view_->widget()->non_client_view()->frame_view());
gfx::Insets insets = view->GetBorderDecorationInsets(); gfx::Insets insets = view->RestoredMirroredFrameBorderInsets();
if (base::i18n::IsRTL()) if (base::i18n::IsRTL())
insets.set_left_right(insets.right(), insets.left()); insets.set_left_right(insets.right(), insets.left());
return insets; return insets;
@ -99,9 +105,14 @@ void ElectronDesktopWindowTreeHostLinux::OnWindowTiledStateChanged(
// of view. // of view.
if (native_window_view_->has_frame() && if (native_window_view_->has_frame() &&
native_window_view_->has_client_frame()) { native_window_view_->has_client_frame()) {
static_cast<ClientFrameViewLinux*>( ClientFrameViewLinux* frame = static_cast<ClientFrameViewLinux*>(
native_window_view_->widget()->non_client_view()->frame_view()) native_window_view_->widget()->non_client_view()->frame_view());
->set_tiled_edges(new_tiled_edges);
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(); UpdateFrameHints();
} }
@ -181,7 +192,7 @@ void ElectronDesktopWindowTreeHostLinux::UpdateFrameHints() {
if (ui::OzonePlatform::GetInstance()->IsWindowCompositingSupported()) { if (ui::OzonePlatform::GetInstance()->IsWindowCompositingSupported()) {
// Set the opaque region. // Set the opaque region.
std::vector<gfx::Rect> opaque_region; std::vector<gfx::Rect> opaque_region;
if (!native_window_view_->IsFullscreen()) { if (!IsShowingFrame()) {
// The opaque region is a list of rectangles that contain only fully // The opaque region is a list of rectangles that contain only fully
// opaque pixels of the window. We need to convert the clipping // opaque pixels of the window. We need to convert the clipping
// rounded-rect into this format. // rounded-rect into this format.

View file

@ -65,6 +65,8 @@ class ElectronDesktopWindowTreeHostLinux
private: private:
void UpdateWindowState(ui::PlatformWindowState new_state); void UpdateWindowState(ui::PlatformWindowState new_state);
bool IsShowingFrame() const;
raw_ptr<NativeWindowViews> native_window_view_; // weak ref raw_ptr<NativeWindowViews> native_window_view_; // weak ref
base::ScopedObservation<ui::NativeTheme, ui::NativeThemeObserver> base::ScopedObservation<ui::NativeTheme, ui::NativeThemeObserver>

View file

@ -39,7 +39,7 @@ namespace electron {
namespace { namespace {
// These values should be the same as Chromium uses. // These values should be the same as Chromium uses.
constexpr int kResizeOutsideBorderSize = 10; constexpr int kResizeBorder = 10;
constexpr int kResizeInsideBoundsSize = 5; constexpr int kResizeInsideBoundsSize = 5;
ui::NavButtonProvider::ButtonState ButtonStateToNavButtonProviderState( ui::NavButtonProvider::ButtonState ButtonStateToNavButtonProviderState(
@ -142,25 +142,28 @@ void ClientFrameViewLinux::Init(NativeWindowViews* window,
UpdateThemeValues(); UpdateThemeValues();
} }
gfx::Insets ClientFrameViewLinux::GetBorderDecorationInsets() const { gfx::Insets ClientFrameViewLinux::RestoredMirroredFrameBorderInsets() const {
const auto insets = GetFrameProvider()->GetFrameThicknessDip(); 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. gfx::Insets ClientFrameViewLinux::RestoredFrameBorderInsets() const {
// See https://wayland.app/protocols/xdg-shell#xdg_toplevel:enum:state gfx::Insets insets = GetFrameProvider()->GetFrameThicknessDip();
const auto& edges = tiled_edges(); insets.SetToMax(GetInputInsets());
return gfx::Insets::TLBR( return insets;
edges.top ? 0 : insets.top(), edges.left ? 0 : insets.left(),
edges.bottom ? 0 : insets.bottom(), edges.right ? 0 : insets.right());
} }
gfx::Insets ClientFrameViewLinux::GetInputInsets() const { gfx::Insets ClientFrameViewLinux::GetInputInsets() const {
return gfx::Insets{ bool showing_shadow = host_supports_client_frame_shadow_ &&
host_supports_client_frame_shadow_ ? -kResizeOutsideBorderSize : 0}; !frame_->IsMaximized() && !frame_->IsFullscreen();
return gfx::Insets(showing_shadow ? kResizeBorder : 0);
} }
gfx::Rect ClientFrameViewLinux::GetWindowContentBounds() const { gfx::Rect ClientFrameViewLinux::GetWindowContentBounds() const {
gfx::Rect content_bounds = bounds(); gfx::Rect content_bounds = bounds();
content_bounds.Inset(GetBorderDecorationInsets()); content_bounds.Inset(RestoredMirroredFrameBorderInsets());
return content_bounds; return content_bounds;
} }
@ -194,15 +197,15 @@ void ClientFrameViewLinux::OnWindowButtonOrderingChange() {
} }
int ClientFrameViewLinux::ResizingBorderHitTest(const gfx::Point& point) { int ClientFrameViewLinux::ResizingBorderHitTest(const gfx::Point& point) {
return ResizingBorderHitTestImpl( return ResizingBorderHitTestImpl(point,
point, RestoredMirroredFrameBorderInsets() +
GetBorderDecorationInsets() + gfx::Insets(kResizeInsideBoundsSize)); gfx::Insets(kResizeInsideBoundsSize));
} }
gfx::Rect ClientFrameViewLinux::GetBoundsForClientView() const { gfx::Rect ClientFrameViewLinux::GetBoundsForClientView() const {
gfx::Rect client_bounds = bounds(); gfx::Rect client_bounds = bounds();
if (!frame_->IsFullscreen()) { if (!frame_->IsFullscreen()) {
client_bounds.Inset(GetBorderDecorationInsets()); client_bounds.Inset(RestoredMirroredFrameBorderInsets());
client_bounds.Inset( client_bounds.Inset(
gfx::Insets::TLBR(GetTitlebarBounds().height(), 0, 0, 0)); gfx::Insets::TLBR(GetTitlebarBounds().height(), 0, 0, 0));
} }
@ -239,10 +242,8 @@ int ClientFrameViewLinux::NonClientHitTest(const gfx::Point& point) {
} }
ui::WindowFrameProvider* ClientFrameViewLinux::GetFrameProvider() const { 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( 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, 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) + std::max(font_height, theme_values_.titlebar_min_height) +
GetTitlebarContentInsets().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 // We add the inset height here, so the .Inset() that follows won't reduce it
// to be too small. // to be too small.
@ -486,7 +487,7 @@ gfx::Rect ClientFrameViewLinux::GetTitlebarContentBounds() const {
} }
gfx::Size ClientFrameViewLinux::SizeWithDecorations(gfx::Size size) 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(0, GetTitlebarBounds().height());
size.Enlarge(decoration_insets.width(), decoration_insets.height()); size.Enlarge(decoration_insets.width(), decoration_insets.height());

View file

@ -43,16 +43,16 @@ class ClientFrameViewLinux : public FramelessView,
void Init(NativeWindowViews* window, views::Widget* frame) override; void Init(NativeWindowViews* window, views::Widget* frame) override;
// These are here for ElectronDesktopWindowTreeHostLinux to use. // These are here for ElectronDesktopWindowTreeHostLinux to use.
gfx::Insets GetBorderDecorationInsets() const; gfx::Insets RestoredMirroredFrameBorderInsets() const;
gfx::Insets RestoredFrameBorderInsets() const;
gfx::Insets GetInputInsets() const; gfx::Insets GetInputInsets() const;
gfx::Rect GetWindowContentBounds() const; gfx::Rect GetWindowContentBounds() const;
SkRRect GetRoundedWindowContentBounds() const; SkRRect GetRoundedWindowContentBounds() const;
int GetTranslucentTopAreaHeight() const; int GetTranslucentTopAreaHeight() const;
// Returns which edges of the frame are tiled.
const ui::WindowTiledEdges& tiled_edges() const { return tiled_edges_; } // Returns whether the frame is in a tiled state.
void set_tiled_edges(ui::WindowTiledEdges tiled_edges) { bool tiled() const { return tiled_; }
tiled_edges_ = tiled_edges; void set_tiled(bool tiled) { tiled_ = tiled; }
}
protected: protected:
// ui::NativeThemeObserver: // ui::NativeThemeObserver:
@ -149,7 +149,7 @@ class ClientFrameViewLinux : public FramelessView,
base::CallbackListSubscription paint_as_active_changed_subscription_; base::CallbackListSubscription paint_as_active_changed_subscription_;
ui::WindowTiledEdges tiled_edges_; bool tiled_ = false;
}; };
} // namespace electron } // namespace electron