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();
}
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<ClientFrameViewLinux*>(
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<ClientFrameViewLinux*>(
native_window_view_->widget()->non_client_view()->frame_view())
->set_tiled_edges(new_tiled_edges);
ClientFrameViewLinux* frame = static_cast<ClientFrameViewLinux*>(
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<gfx::Rect> 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.

View file

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

View file

@ -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());

View file

@ -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