Skip to content

Commit

Permalink
fix: window maximizing with Mica (electron#45456)
Browse files Browse the repository at this point in the history
* fix: window maximizing with Mica

* Fix rounded corners after restore
  • Loading branch information
codebytere authored Feb 7, 2025
1 parent 96460be commit 9199d5c
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 57 deletions.
90 changes: 55 additions & 35 deletions shell/browser/native_window_views.cc
Original file line number Diff line number Diff line change
Expand Up @@ -642,8 +642,22 @@ void NativeWindowViews::SetEnabledInternal(bool enable) {
#endif
}

#if BUILDFLAG(IS_LINUX)
void NativeWindowViews::Maximize() {
#if BUILDFLAG(IS_WIN)
if (IsTranslucent()) {
// If a window is translucent but not transparent on Windows,
// that means it must have a backgroundMaterial set.
if (!transparent())
SetRoundedCorners(false);
restore_bounds_ = GetBounds();
auto display = display::Screen::GetScreen()->GetDisplayNearestWindow(
GetNativeWindow());
SetBounds(display.work_area(), false);
NotifyWindowMaximize();
return;
}
#endif

if (IsVisible()) {
widget()->Maximize();
} else {
Expand All @@ -652,44 +666,46 @@ void NativeWindowViews::Maximize() {
NotifyWindowShow();
}
}
#endif

void NativeWindowViews::Unmaximize() {
if (IsMaximized()) {
if (!IsMaximized())
return;

#if BUILDFLAG(IS_WIN)
if (IsTranslucent()) {
SetBounds(restore_bounds_, false);
NotifyWindowUnmaximize();
if (transparent()) {
SetBounds(restore_bounds_, false);
NotifyWindowUnmaximize();
UpdateThickFrame();
return;
} else {
SetRoundedCorners(true);
}
return;
}
#endif

widget()->Restore();
widget()->Restore();

#if BUILDFLAG(IS_WIN)
UpdateThickFrame();
UpdateThickFrame();
#endif
}
}

bool NativeWindowViews::IsMaximized() const {
if (widget()->IsMaximized()) {
if (widget()->IsMaximized())
return true;
} else {

#if BUILDFLAG(IS_WIN)
if (transparent() && !IsMinimized()) {
// Compare the size of the window with the size of the display
auto display = display::Screen::GetScreen()->GetDisplayNearestWindow(
GetNativeWindow());
// Maximized if the window is the same dimensions and placement as the
// display
return GetBounds() == display.work_area();
}
if (IsTranslucent() && !IsMinimized()) {
// If the window is the same dimensions and placement as the
// display, we consider it maximized.
auto display = display::Screen::GetScreen()->GetDisplayNearestWindow(
GetNativeWindow());
return GetBounds() == display.work_area();
}
#endif

return false;
}
return false;
}

void NativeWindowViews::Minimize() {
Expand All @@ -702,10 +718,14 @@ void NativeWindowViews::Minimize() {

void NativeWindowViews::Restore() {
#if BUILDFLAG(IS_WIN)
if (IsMaximized() && transparent()) {
if (IsMaximized() && IsTranslucent()) {
SetBounds(restore_bounds_, false);
NotifyWindowRestore();
UpdateThickFrame();
if (transparent()) {
UpdateThickFrame();
} else {
SetRoundedCorners(true);
}
return;
}
#endif
Expand Down Expand Up @@ -845,7 +865,7 @@ gfx::Size NativeWindowViews::GetContentSize() const {

gfx::Rect NativeWindowViews::GetNormalBounds() const {
#if BUILDFLAG(IS_WIN)
if (IsMaximized() && transparent())
if (IsMaximized() && IsTranslucent())
return restore_bounds_;
#endif
return widget()->GetRestoredBounds();
Expand All @@ -860,8 +880,8 @@ void NativeWindowViews::SetContentSizeConstraints(
if (!thick_frame_)
return;
#endif
// widget_delegate() is only available after Init() is called, we make use of
// this to determine whether native widget has initialized.
// widget_delegate() is only available after Init() is called, we make use
// of this to determine whether native widget has initialized.
if (widget() && widget()->widget_delegate())
widget()->OnSizeConstraintsChanged();
if (resizable_)
Expand Down Expand Up @@ -1639,9 +1659,9 @@ void NativeWindowViews::UpdateThickFrame() {
return;

if (IsMaximized() && !transparent()) {
// For maximized window add thick frame always, otherwise it will be removed
// in HWNDMessageHandler::SizeConstraintsChanged() which will result in
// maximized window bounds change.
// For maximized window add thick frame always, otherwise it will be
// removed in HWNDMessageHandler::SizeConstraintsChanged() which will
// result in maximized window bounds change.
FlipWindowStyle(GetAcceleratedWidget(), true, WS_THICKFRAME);
} else if (has_frame()) {
FlipWindowStyle(GetAcceleratedWidget(), resizable_, WS_THICKFRAME);
Expand Down Expand Up @@ -1679,10 +1699,10 @@ void NativeWindowViews::OnWidgetBoundsChanged(views::Widget* changed_widget,
// WidgetObserver::OnWidgetBoundsChanged is being called from
// Widget::OnNativeWidgetMove() and not Widget::OnNativeWidgetSizeChanged.
// |GetWindowBoundsInScreen| has a ~1 pixel margin
// of error because it converts from floats to integers between calculations,
// so if we check existing bounds directly against the new bounds without
// accounting for that we'll have constant false positives when the window is
// moving but the user hasn't changed its size at all.
// of error because it converts from floats to integers between
// calculations, so if we check existing bounds directly against the new
// bounds without accounting for that we'll have constant false positives
// when the window is moving but the user hasn't changed its size at all.
auto isWithinOnePixel = [](gfx::Size old_size, gfx::Size new_size) -> bool {
return base::IsApproximatelyEqual(old_size.width(), new_size.width(), 1) &&
base::IsApproximatelyEqual(old_size.height(), new_size.height(), 1);
Expand Down Expand Up @@ -1807,8 +1827,8 @@ void NativeWindowViews::OnMouseEvent(ui::MouseEvent* event) {
ui::mojom::WindowShowState NativeWindowViews::GetRestoredState() {
if (IsMaximized()) {
#if BUILDFLAG(IS_WIN)
// Only restore Maximized state when window is NOT transparent style
if (!transparent()) {
// Restore maximized state for windows that are not translucent.
if (!IsTranslucent()) {
return ui::mojom::WindowShowState::kMaximized;
}
#else
Expand Down
4 changes: 1 addition & 3 deletions shell/browser/native_window_views.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,9 @@ class NativeWindowViews : public NativeWindow,
void OnWidgetMove() override;
#if BUILDFLAG(IS_WIN)
bool ExecuteWindowsCommand(int command_id) override;
#endif

#if BUILDFLAG(IS_WIN)
void HandleSizeEvent(WPARAM w_param, LPARAM l_param);
void ResetWindowControls();
void SetRoundedCorners(bool rounded);
void SetForwardMouseMessages(bool forward);
static LRESULT CALLBACK SubclassProc(HWND hwnd,
UINT msg,
Expand Down
38 changes: 19 additions & 19 deletions shell/browser/native_window_views_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "base/win/atl.h" // Must be before UIAutomationCore.h
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
#include "content/public/browser/browser_accessibility_state.h"
#include "shell/browser/browser.h"
#include "shell/browser/native_window_views.h"
Expand Down Expand Up @@ -225,25 +226,6 @@ bool IsScreenReaderActive() {
std::set<NativeWindowViews*> NativeWindowViews::forwarding_windows_;
HHOOK NativeWindowViews::mouse_hook_ = nullptr;

void NativeWindowViews::Maximize() {
// Only use Maximize() when window is NOT transparent style
if (!transparent()) {
if (IsVisible()) {
widget()->Maximize();
} else {
widget()->native_widget_private()->Show(
ui::mojom::WindowShowState::kMaximized, gfx::Rect());
NotifyWindowShow();
}
} else {
restore_bounds_ = GetBounds();
auto display = display::Screen::GetScreen()->GetDisplayNearestWindow(
GetNativeWindow());
SetBounds(display.work_area(), false);
NotifyWindowMaximize();
}
}

bool NativeWindowViews::ExecuteWindowsCommand(int command_id) {
const auto command_name = AppCommandToString(command_id);
NotifyWindowExecuteAppCommand(command_name);
Expand Down Expand Up @@ -535,6 +517,24 @@ void NativeWindowViews::ResetWindowControls() {
}
}

// Windows with |backgroundMaterial| expand to the same dimensions and
// placement as the display to approximate maximization - unless we remove
// rounded corners there will be a gap between the window and the display
// at the corners noticable to users.
void NativeWindowViews::SetRoundedCorners(bool rounded) {
// DWMWA_WINDOW_CORNER_PREFERENCE is supported after Windows 11 Build 22000.
if (base::win::GetVersion() < base::win::Version::WIN11)
return;

DWM_WINDOW_CORNER_PREFERENCE round_pref =
rounded ? DWMWCP_ROUND : DWMWCP_DONOTROUND;
HRESULT result = DwmSetWindowAttribute(GetAcceleratedWidget(),
DWMWA_WINDOW_CORNER_PREFERENCE,
&round_pref, sizeof(round_pref));
if (FAILED(result))
LOG(WARNING) << "Failed to set rounded corners to " << rounded;
}

void NativeWindowViews::SetForwardMouseMessages(bool forward) {
if (forward && !forwarding_mouse_messages_) {
forwarding_mouse_messages_ = true;
Expand Down

0 comments on commit 9199d5c

Please sign in to comment.