Skip to content

Commit

Permalink
feat: add WCO height option (#33061)
Browse files Browse the repository at this point in the history
* feat: add WCO height option (#31222)

* feat: add WCO height option

* add docs and mac functionality

* add macOS functionality and height lowerbound

* Update docs/api/browser-window.md

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>

* update macOS functionality

* add chromium related notes

* add test and fix pixel under button bug and fix typo

* revert changes to docs/api/frameless-window.md

* modify `useCustomHeight` calls

* update `useCustomHeight` and `getCurrentMargin`

* modify margin calculation

* fix minimum custom height on macOS

* Update window_buttons_proxy.mm

* fix specified traffic light positions

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>

* Update win_caption_button.h

* Update win_caption_button_container.h

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
  • Loading branch information
mlaurencin and jkleinsc committed Feb 28, 2022
1 parent 8608b78 commit 079ec9d
Show file tree
Hide file tree
Showing 16 changed files with 194 additions and 28 deletions.
3 changes: 2 additions & 1 deletion docs/api/browser-window.md
Expand Up @@ -391,9 +391,10 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
contain the layout of the document—without requiring scrolling. Enabling
this will cause the `preferred-size-changed` event to be emitted on the
`WebContents` when the preferred size changes. Default is `false`.
* `titleBarOverlay` Object | Boolean (optional) - When using a frameless window in conjunction with `win.setWindowButtonVisibility(true)` on macOS or using a `titleBarStyle` so that the standard window controls ("traffic lights" on macOS) are visible, this property enables the Window Controls Overlay [JavaScript APIs][overlay-javascript-apis] and [CSS Environment Variables][overlay-css-env-vars]. Specifying `true` will result in an overlay with default system colors. Default is `false`.
* `titleBarOverlay` Object | Boolean (optional) - When using a frameless window in conjuction with `win.setWindowButtonVisibility(true)` on macOS or using a `titleBarStyle` so that the standard window controls ("traffic lights" on macOS) are visible, this property enables the Window Controls Overlay [JavaScript APIs][overlay-javascript-apis] and [CSS Environment Variables][overlay-css-env-vars]. Specifying `true` will result in an overlay with default system colors. Default is `false`.
* `color` String (optional) _Windows_ - The CSS color of the Window Controls Overlay when enabled. Default is the system color.
* `symbolColor` String (optional) _Windows_ - The CSS color of the symbols on the Window Controls Overlay when enabled. Default is the system color.
* `height` Integer (optional) _macOS_ _Windows_ - The height of the title bar and Window Controls Overlay in pixels. Default is system height.

When setting minimum or maximum window size with `minWidth`/`maxWidth`/
`minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from
Expand Down
10 changes: 9 additions & 1 deletion shell/browser/native_window.cc
Expand Up @@ -90,7 +90,15 @@ NativeWindow::NativeWindow(const gin_helper::Dictionary& options,
options.Get(options::ktitleBarOverlay, &titlebar_overlay_);
} else if (titlebar_overlay->IsObject()) {
titlebar_overlay_ = true;
#if !defined(OS_WIN)

gin_helper::Dictionary titlebar_overlay =
gin::Dictionary::CreateEmpty(options.isolate());
options.Get(options::ktitleBarOverlay, &titlebar_overlay);
int height;
if (titlebar_overlay.Get(options::kOverlayHeight, &height))
titlebar_overlay_height_ = height;

#if !(defined(OS_WIN) || defined(OS_MAC))
DCHECK(false);
#endif
}
Expand Down
5 changes: 5 additions & 0 deletions shell/browser/native_window.h
Expand Up @@ -323,6 +323,7 @@ class NativeWindow : public base::SupportsUserData,
kCustomButtonsOnHover,
};
TitleBarStyle title_bar_style() const { return title_bar_style_; }
int titlebar_overlay_height() const { return titlebar_overlay_height_; }

bool has_frame() const { return has_frame_; }
void set_has_frame(bool has_frame) { has_frame_ = has_frame; }
Expand Down Expand Up @@ -358,6 +359,10 @@ class NativeWindow : public base::SupportsUserData,
// The boolean parsing of the "titleBarOverlay" option
bool titlebar_overlay_ = false;

// The custom height parsed from the "height" option in a Object
// "titleBarOverlay"
int titlebar_overlay_height_ = 0;

// The "titleBarStyle" option.
TitleBarStyle title_bar_style_ = TitleBarStyle::kNormal;

Expand Down
8 changes: 7 additions & 1 deletion shell/browser/native_window_mac.mm
Expand Up @@ -369,6 +369,7 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) {
InternalSetWindowButtonVisibility(false);
} else {
buttons_proxy_.reset([[WindowButtonsProxy alloc] initWithWindow:window_]);
[buttons_proxy_ setHeight:titlebar_overlay_height()];
if (traffic_light_position_) {
[buttons_proxy_ setMargin:*traffic_light_position_];
} else if (title_bar_style_ == TitleBarStyle::kHiddenInset) {
Expand Down Expand Up @@ -1839,7 +1840,12 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) {
NSRect buttons = [buttons_proxy_ getButtonsContainerBounds];
gfx::Rect overlay;
overlay.set_width(GetContentSize().width() - NSWidth(buttons));
overlay.set_height(NSHeight(buttons));
if ([buttons_proxy_ useCustomHeight]) {
overlay.set_height(titlebar_overlay_height());
} else {
overlay.set_height(NSHeight(buttons));
}

if (!base::i18n::IsRTL())
overlay.set_x(NSMaxX(buttons));
return overlay;
Expand Down
3 changes: 1 addition & 2 deletions shell/browser/native_window_views.cc
Expand Up @@ -179,9 +179,8 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
v8::Local<v8::Value> titlebar_overlay;
if (options.Get(options::ktitleBarOverlay, &titlebar_overlay) &&
titlebar_overlay->IsObject()) {
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
gin_helper::Dictionary titlebar_overlay_obj =
gin::Dictionary::CreateEmpty(isolate);
gin::Dictionary::CreateEmpty(options.isolate());
options.Get(options::ktitleBarOverlay, &titlebar_overlay_obj);

std::string overlay_color_string;
Expand Down
6 changes: 6 additions & 0 deletions shell/browser/ui/cocoa/window_buttons_proxy.h
Expand Up @@ -30,6 +30,8 @@
gfx::Point margin_;
// The default left-top margin.
gfx::Point default_margin_;
// Current height of the title bar container.
float height_;

// Track mouse moves above window buttons.
BOOL show_on_hover_;
Expand All @@ -49,6 +51,10 @@
// Set left-top margin of the window buttons..
- (void)setMargin:(const absl::optional<gfx::Point>&)margin;

// Set height of button container
- (void)setHeight:(const float)height;
- (BOOL)useCustomHeight;

// Return the bounds of all 3 buttons, with margin on all sides.
- (NSRect)getButtonsContainerBounds;

Expand Down
36 changes: 32 additions & 4 deletions shell/browser/ui/cocoa/window_buttons_proxy.mm
Expand Up @@ -36,6 +36,8 @@ - (id)initWithWindow:(NSWindow*)window {

// Remember the default margin.
margin_ = default_margin_ = [self getCurrentMargin];
// Custom height will be used if set larger than default
height_ = 0;

return self;
}
Expand Down Expand Up @@ -86,6 +88,17 @@ - (void)setMargin:(const absl::optional<gfx::Point>&)margin {
[self redraw];
}

- (void)setHeight:(const float)height {
height_ = height;
[self redraw];
}

- (BOOL)useCustomHeight {
NSView* left = [self leftButton];
float button_height = NSHeight(left.frame);
return height_ > button_height + 2 * default_margin_.y();
}

- (NSRect)getButtonsContainerBounds {
return NSInsetRect([self getButtonsBounds], -margin_.x(), -margin_.y());
}
Expand All @@ -111,14 +124,18 @@ - (void)redraw {

NSRect cbounds = titleBarContainer.frame;
cbounds.size.height = button_height + 2 * margin_.y();
// Custom height must be larger than the button height to use
if ([self useCustomHeight]) {
cbounds.size.height = height_;
}
cbounds.origin.y = NSHeight(window_.frame) - NSHeight(cbounds);
[titleBarContainer setFrame:cbounds];

[left setFrameOrigin:NSMakePoint(start, margin_.y())];
[left setFrameOrigin:NSMakePoint(start, [self getCurrentMargin].y())];
start += button_width + padding;
[middle setFrameOrigin:NSMakePoint(start, margin_.y())];
[middle setFrameOrigin:NSMakePoint(start, [self getCurrentMargin].y())];
start += button_width + padding;
[right setFrameOrigin:NSMakePoint(start, margin_.y())];
[right setFrameOrigin:NSMakePoint(start, [self getCurrentMargin].y())];

if (hover_view_)
[hover_view_ setFrame:[self getButtonsBounds]];
Expand Down Expand Up @@ -167,6 +184,7 @@ - (void)updateButtonsVisibility {
- (NSRect)getButtonsBounds {
NSView* left = [self leftButton];
NSView* right = [self rightButton];

return NSMakeRect(NSMinX(left.frame), NSMinY(left.frame),
NSMaxX(right.frame) - NSMinX(left.frame),
NSHeight(left.frame));
Expand All @@ -182,7 +200,17 @@ - (NSRect)getButtonsBounds {
NSView* left = [self leftButton];
NSView* right = [self rightButton];

result.set_y((NSHeight(titleBarContainer.frame) - NSHeight(left.frame)) / 2);
if (height_ != 0) {
result.set_y((height_ - NSHeight(left.frame)) / 2);

// Do not center buttons if height and button position specified
if (margin_.y() != default_margin_.y())
result.set_y(height_ - NSHeight(left.frame) - margin_.y());

} else {
result.set_y((NSHeight(titleBarContainer.frame) - NSHeight(left.frame)) /
2);
}

if (base::i18n::IsRTL())
result.set_x(NSWidth(window_.frame) - NSMaxX(right.frame));
Expand Down
22 changes: 18 additions & 4 deletions shell/browser/ui/views/win_caption_button.cc
Expand Up @@ -2,13 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Modified from chrome/browser/ui/views/frame/windows_10_caption_button.cc

#include "shell/browser/ui/views/win_caption_button.h"

#include <utility>

#include "base/i18n/rtl.h"
#include "base/numerics/safe_conversions.h"
#include "chrome/browser/ui/frame/window_frame_util.h"
#include "chrome/grit/theme_resources.h"
#include "shell/browser/ui/views/win_frame_view.h"
#include "shell/common/color_util.h"
Expand Down Expand Up @@ -37,9 +38,8 @@ WinCaptionButton::WinCaptionButton(PressedCallback callback,
gfx::Size WinCaptionButton::CalculatePreferredSize() const {
// TODO(bsep): The sizes in this function are for 1x device scale and don't
// match Windows button sizes at hidpi.
int height = WindowFrameUtil::kWindows10GlassCaptionButtonHeightRestored;
int base_width = WindowFrameUtil::kWindows10GlassCaptionButtonWidth;
return gfx::Size(base_width + GetBetweenButtonSpacing(), height);

return gfx::Size(base_width_ + GetBetweenButtonSpacing(), height_);
}

void WinCaptionButton::OnPaintBackground(gfx::Canvas* canvas) {
Expand Down Expand Up @@ -88,6 +88,20 @@ void WinCaptionButton::PaintButtonContents(gfx::Canvas* canvas) {
PaintSymbol(canvas);
}

gfx::Size WinCaptionButton::GetSize() const {
return gfx::Size(base_width_, height_);
}

void WinCaptionButton::SetSize(gfx::Size size) {
int width = size.width();
int height = size.height();

if (width > 0)
base_width_ = width;
if (height > 0)
height_ = height;
}

int WinCaptionButton::GetBetweenButtonSpacing() const {
const int display_order_index = GetButtonDisplayOrderIndex();
return display_order_index == 0
Expand Down
11 changes: 10 additions & 1 deletion shell/browser/ui/views/win_caption_button.h
Expand Up @@ -2,9 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Modified from chrome/browser/ui/views/frame/windows_10_caption_button.h

#ifndef SHELL_BROWSER_UI_VIEWS_WIN_CAPTION_BUTTON_H_
#define SHELL_BROWSER_UI_VIEWS_WIN_CAPTION_BUTTON_H_

#include "chrome/browser/ui/frame/window_frame_util.h"
#include "chrome/browser/ui/view_ids.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/gfx/canvas.h"
Expand All @@ -28,7 +31,10 @@ class WinCaptionButton : public views::Button {
void OnPaintBackground(gfx::Canvas* canvas) override;
void PaintButtonContents(gfx::Canvas* canvas) override;

// private:
gfx::Size GetSize() const;
void SetSize(gfx::Size size);

private:
// Returns the amount we should visually reserve on the left (right in RTL)
// for spacing between buttons. We do this instead of repositioning the
// buttons to avoid the sliver of deadspace that would result.
Expand All @@ -48,6 +54,9 @@ class WinCaptionButton : public views::Button {

WinFrameView* frame_view_;
ViewID button_type_;

int base_width_ = WindowFrameUtil::kWindows10GlassCaptionButtonWidth;
int height_ = WindowFrameUtil::kWindows10GlassCaptionButtonHeightRestored;
};
} // namespace electron

Expand Down
15 changes: 15 additions & 0 deletions shell/browser/ui/views/win_caption_button_container.cc
Expand Up @@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Modified from
// chrome/browser/ui/views/frame/glass_browser_caption_button_container.cc

#include "shell/browser/ui/views/win_caption_button_container.h"

#include <memory>
Expand Down Expand Up @@ -96,6 +99,18 @@ int WinCaptionButtonContainer::NonClientHitTest(const gfx::Point& point) const {
return HTCAPTION;
}

gfx::Size WinCaptionButtonContainer::GetButtonSize() const {
// Close button size is set the same as all the buttons
return close_button_->GetSize();
}

void WinCaptionButtonContainer::SetButtonSize(gfx::Size size) {
minimize_button_->SetSize(size);
maximize_button_->SetSize(size);
restore_button_->SetSize(size);
close_button_->SetSize(size);
}

void WinCaptionButtonContainer::ResetWindowControls() {
minimize_button_->SetState(views::Button::STATE_NORMAL);
maximize_button_->SetState(views::Button::STATE_NORMAL);
Expand Down
6 changes: 6 additions & 0 deletions shell/browser/ui/views/win_caption_button_container.h
Expand Up @@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Modified from
// chrome/browser/ui/views/frame/glass_browser_caption_button_container.h

#ifndef SHELL_BROWSER_UI_VIEWS_WIN_CAPTION_BUTTON_CONTAINER_H_
#define SHELL_BROWSER_UI_VIEWS_WIN_CAPTION_BUTTON_CONTAINER_H_

Expand Down Expand Up @@ -35,6 +38,9 @@ class WinCaptionButtonContainer : public views::View,
// See also ClientView::NonClientHitTest.
int NonClientHitTest(const gfx::Point& point) const;

gfx::Size GetButtonSize() const;
void SetButtonSize(gfx::Size size);

private:
// views::View:
void AddedToWidget() override;
Expand Down
41 changes: 28 additions & 13 deletions shell/browser/ui/views/win_frame_view.cc
Expand Up @@ -193,13 +193,20 @@ int WinFrameView::TitlebarMaximizedVisualHeight() const {
return maximized_height;
}

int WinFrameView::TitlebarHeight(bool restored) const {
if (frame()->IsFullscreen() && !restored)
// NOTE(@mlaurencin): Usage of IsWebUITabStrip simplified out from Chromium
int WinFrameView::TitlebarHeight(int custom_height) const {
if (frame()->IsFullscreen() && !IsMaximized())
return 0;

return TitlebarMaximizedVisualHeight() + FrameTopBorderThickness(false);
int height = TitlebarMaximizedVisualHeight() +
FrameTopBorderThickness(false) - WindowTopY();
if (custom_height > TitlebarMaximizedVisualHeight())
height = custom_height - WindowTopY();

return height;
}

// NOTE(@mlaurencin): Usage of IsWebUITabStrip simplified out from Chromium
int WinFrameView::WindowTopY() const {
// The window top is SM_CYSIZEFRAME pixels when maximized (see the comment in
// FrameTopBorderThickness()) and floor(system dsf) pixels when restored.
Expand All @@ -222,27 +229,35 @@ void WinFrameView::LayoutCaptionButtons() {
}

caption_button_container_->SetVisible(true);

const gfx::Size preferred_size =
caption_button_container_->GetPreferredSize();
int height = preferred_size.height();

height = IsMaximized() ? TitlebarMaximizedVisualHeight()
: TitlebarHeight(false) - WindowTopY();
int custom_height = window()->titlebar_overlay_height();
int height = TitlebarHeight(custom_height);

// TODO(mlaurencin): This -1 creates a 1 pixel gap between the right
// edge of the overlay and the edge of the window, allowing for this edge
// portion to return the correct hit test and be manually resized properly.
// Alternatives can be explored, but the differences in view structures
// between Electron and Chromium may result in this as the best option.
// TODO(mlaurencin): This -1 creates a 1 pixel margin between the right
// edge of the button container and the edge of the window, allowing for this
// edge portion to return the correct hit test and be manually resized
// properly. Alternatives can be explored, but the differences in view
// structures between Electron and Chromium may result in this as the best
// option.
int variable_width =
IsMaximized() ? preferred_size.width() : preferred_size.width() - 1;
caption_button_container_->SetBounds(width() - preferred_size.width(),
WindowTopY(), variable_width, height);

// Needed for heights larger than default
caption_button_container_->SetButtonSize(gfx::Size(0, height));
}

void WinFrameView::LayoutWindowControlsOverlay() {
int overlay_height = caption_button_container_->size().height();
int overlay_height = window()->titlebar_overlay_height();
if (overlay_height == 0) {
// Accounting for the 1 pixel margin at the top of the button container
overlay_height = IsMaximized()
? caption_button_container_->size().height()
: caption_button_container_->size().height() + 1;
}
int overlay_width = caption_button_container_->size().width();
int bounding_rect_width = width() - overlay_width;
auto bounding_rect =
Expand Down
2 changes: 1 addition & 1 deletion shell/browser/ui/views/win_frame_view.h
Expand Up @@ -67,7 +67,7 @@ class WinFrameView : public FramelessView {

// Returns the height of the titlebar for popups or other browser types that
// don't have tabs.
int TitlebarHeight(bool restored) const;
int TitlebarHeight(int custom_height) const;

// Returns the y coordinate for the top of the frame, which in maximized mode
// is the top of the screen and in restored mode is 1 pixel below the top of
Expand Down
3 changes: 3 additions & 0 deletions shell/common/options_switches.cc
Expand Up @@ -36,6 +36,9 @@ const char kRoundedCorners[] = "roundedCorners";
const char kOverlayButtonColor[] = "color";
const char kOverlaySymbolColor[] = "symbolColor";

// The custom height for Window Controls Overlay.
const char kOverlayHeight[] = "height";

// Whether the window should show in taskbar.
const char kSkipTaskbar[] = "skipTaskbar";

Expand Down

0 comments on commit 079ec9d

Please sign in to comment.