diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index f76559e4fca0c..1176f4fd01317 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -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 diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index c851ff24b9287..e6de0637ec287 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -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 } diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index 1d41a357c3efd..4c97507097841 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -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; } @@ -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; diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index e39fe6024e238..7574bb6cb52a6 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -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) { @@ -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; diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 0c52856aad38c..e82af94f43467 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -179,9 +179,8 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, v8::Local 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; diff --git a/shell/browser/ui/cocoa/window_buttons_proxy.h b/shell/browser/ui/cocoa/window_buttons_proxy.h index 521c5cf110e88..17633ab516a1c 100644 --- a/shell/browser/ui/cocoa/window_buttons_proxy.h +++ b/shell/browser/ui/cocoa/window_buttons_proxy.h @@ -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_; @@ -49,6 +51,10 @@ // Set left-top margin of the window buttons.. - (void)setMargin:(const absl::optional&)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; diff --git a/shell/browser/ui/cocoa/window_buttons_proxy.mm b/shell/browser/ui/cocoa/window_buttons_proxy.mm index 9e675e9dffb36..e93eb289cb313 100644 --- a/shell/browser/ui/cocoa/window_buttons_proxy.mm +++ b/shell/browser/ui/cocoa/window_buttons_proxy.mm @@ -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; } @@ -86,6 +88,17 @@ - (void)setMargin:(const absl::optional&)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()); } @@ -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]]; @@ -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)); @@ -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)); diff --git a/shell/browser/ui/views/win_caption_button.cc b/shell/browser/ui/views/win_caption_button.cc index ec202a227481e..d12f761f9deb4 100644 --- a/shell/browser/ui/views/win_caption_button.cc +++ b/shell/browser/ui/views/win_caption_button.cc @@ -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 #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" @@ -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) { @@ -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 diff --git a/shell/browser/ui/views/win_caption_button.h b/shell/browser/ui/views/win_caption_button.h index f57918538ec95..596432131ab95 100644 --- a/shell/browser/ui/views/win_caption_button.h +++ b/shell/browser/ui/views/win_caption_button.h @@ -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" @@ -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. @@ -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 diff --git a/shell/browser/ui/views/win_caption_button_container.cc b/shell/browser/ui/views/win_caption_button_container.cc index aba141e722512..9995c9741279c 100644 --- a/shell/browser/ui/views/win_caption_button_container.cc +++ b/shell/browser/ui/views/win_caption_button_container.cc @@ -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 @@ -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); diff --git a/shell/browser/ui/views/win_caption_button_container.h b/shell/browser/ui/views/win_caption_button_container.h index ed36a106b3cf9..1adca287d288d 100644 --- a/shell/browser/ui/views/win_caption_button_container.h +++ b/shell/browser/ui/views/win_caption_button_container.h @@ -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_ @@ -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; diff --git a/shell/browser/ui/views/win_frame_view.cc b/shell/browser/ui/views/win_frame_view.cc index 9d8a460b830e4..9ddea873c704c 100644 --- a/shell/browser/ui/views/win_frame_view.cc +++ b/shell/browser/ui/views/win_frame_view.cc @@ -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. @@ -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 = diff --git a/shell/browser/ui/views/win_frame_view.h b/shell/browser/ui/views/win_frame_view.h index c0311a38f11f8..53501adf6357a 100644 --- a/shell/browser/ui/views/win_frame_view.h +++ b/shell/browser/ui/views/win_frame_view.h @@ -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 diff --git a/shell/common/options_switches.cc b/shell/common/options_switches.cc index 3738126a297c0..065632376e359 100644 --- a/shell/common/options_switches.cc +++ b/shell/common/options_switches.cc @@ -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"; diff --git a/shell/common/options_switches.h b/shell/common/options_switches.h index d69912e5db64a..fd897ab35766f 100644 --- a/shell/common/options_switches.h +++ b/shell/common/options_switches.h @@ -60,6 +60,7 @@ extern const char kRoundedCorners[]; extern const char ktitleBarOverlay[]; extern const char kOverlayButtonColor[]; extern const char kOverlaySymbolColor[]; +extern const char kOverlayHeight[]; // WebPreferences. extern const char kZoomFactor[]; diff --git a/spec-main/api-browser-window-spec.ts b/spec-main/api-browser-window-spec.ts index 58c3cd209e340..d7ec5e806ed4d 100644 --- a/spec-main/api-browser-window-spec.ts +++ b/spec-main/api-browser-window-spec.ts @@ -2021,6 +2021,56 @@ describe('BrowserWindow module', () => { }); }); + ifdescribe(process.platform === 'win32' || (process.platform === 'darwin' && semver.gte(os.release(), '14.0.0')))('"titleBarOverlay" option', () => { + const testWindowsOverlayHeight = async (size: any) => { + const w = new BrowserWindow({ + show: false, + width: 400, + height: 400, + titleBarStyle: 'hidden', + webPreferences: { + nodeIntegration: true, + contextIsolation: false + }, + titleBarOverlay: { + height: size + } + }); + const overlayHTML = path.join(__dirname, 'fixtures', 'pages', 'overlay.html'); + if (process.platform === 'darwin') { + await w.loadFile(overlayHTML); + } else { + const overlayReady = emittedOnce(ipcMain, 'geometrychange'); + await w.loadFile(overlayHTML); + await overlayReady; + } + const overlayEnabled = await w.webContents.executeJavaScript('navigator.windowControlsOverlay.visible'); + expect(overlayEnabled).to.be.true('overlayEnabled'); + const overlayRectPreMax = await w.webContents.executeJavaScript('getJSOverlayProperties()'); + await w.maximize(); + const max = await w.isMaximized(); + expect(max).to.equal(true); + const overlayRectPostMax = await w.webContents.executeJavaScript('getJSOverlayProperties()'); + + expect(overlayRectPreMax.y).to.equal(0); + if (process.platform === 'darwin') { + expect(overlayRectPreMax.x).to.be.greaterThan(0); + } else { + expect(overlayRectPreMax.x).to.equal(0); + } + expect(overlayRectPreMax.width).to.be.greaterThan(0); + + expect(overlayRectPreMax.height).to.equal(size); + // Confirm that maximization only affected the height of the buttons and not the title bar + expect(overlayRectPostMax.height).to.equal(size); + }; + afterEach(closeAllWindows); + afterEach(() => { ipcMain.removeAllListeners('geometrychange'); }); + it('sets Window Control Overlay with title bar height of 40', async () => { + await testWindowsOverlayHeight(40); + }); + }); + ifdescribe(process.platform === 'darwin')('"enableLargerThanScreen" option', () => { afterEach(closeAllWindows); it('can move the window out of screen', () => {