Skip to content

Commit

Permalink
feat: enable windows control overlay on Windows (electron#29600)
Browse files Browse the repository at this point in the history
* rebase "feat: enable windows control overlay on Windows"

* correct compilation error

* fix linting errors

* modify includes and build file

* change `hidden` option to `overlay`

* add patch to fix visual layout

* add button background color parameter

* add button text color parameter

* modify `overlay` in docs and modify button hover/press transition color

* change `text` to `symbol`

* remove todo and fix `text` replacement

* add new titleBarOverlay property and remove titleBarStyle `overlay`

* update browser and frameless window docs

* remove chromium patches

* chore: update patches

* change button hover color, update trailing `_`, update test file

* add dchecks, update title bar drawing checks, update test file

* modify for mac and linux builds

* update docs with overlayColor and overlaySymbolColor

* add corner and side hit test info

* modify docs and copyright info

* modify `titlebar_overlay_` as boolean or object

* move `title_bar_style_ to `NativeWindow`

* update docs with boolean and object titlebar_overlay_

* add `IsEmpty` checks

* move get options for boolean and object checks

* fix linting error

* disable `use_lld` for macos

* Update docs/api/frameless-window.md

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

* Update docs/api/frameless-window.md

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

* Update docs/api/frameless-window.md

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

* Apply docs suggestions from code review

Co-authored-by: Jeremy Rose <jeremya@chromium.org>

* modify `true` option description `titleBarOverlay`

* ci: cleanup keychain after tests on arm64 mac (electron#30472)

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Jeremy Rose <jeremya@chromium.org>
  • Loading branch information
4 people authored and BlackHole1 committed Aug 27, 2021
1 parent 9de8c33 commit 5260355
Show file tree
Hide file tree
Showing 26 changed files with 974 additions and 72 deletions.
3 changes: 3 additions & 0 deletions chromium_src/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,11 @@ static_library("chrome") {
"//chrome/browser/extensions/global_shortcut_listener_win.h",
"//chrome/browser/icon_loader_win.cc",
"//chrome/browser/media/webrtc/window_icon_util_win.cc",
"//chrome/browser/ui/frame/window_frame_util.h",
"//chrome/browser/ui/view_ids.h",
"//chrome/browser/win/chrome_process_finder.cc",
"//chrome/browser/win/chrome_process_finder.h",
"//chrome/browser/win/titlebar_config.h",
"//chrome/child/v8_crashpad_support_win.cc",
"//chrome/child/v8_crashpad_support_win.h",
]
Expand Down
20 changes: 8 additions & 12 deletions docs/api/browser-window.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,16 +213,13 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
* `followWindow` - The backdrop should automatically appear active when the window is active, and inactive when it is not. This is the default.
* `active` - The backdrop should always appear active.
* `inactive` - The backdrop should always appear inactive.
* `titleBarStyle` String (optional) - The style of window title bar.
* `titleBarStyle` String (optional) _macOS_ _Windows_ - The style of window title bar.
Default is `default`. Possible values are:
* `default` - Results in the standard gray opaque Mac title
bar.
* `hidden` - Results in a hidden title bar and a full size content window, yet
the title bar still has the standard window controls ("traffic lights") in
the top left.
* `hiddenInset` - Results in a hidden title bar with an alternative look
* `default` - Results in the standard title bar for macOS or Windows respectively.
* `hidden` - Results in a hidden title bar and a full size content window. On macOS, the window still has the standard window controls (“traffic lights”) in the top left. On Windows, when combined with `titleBarOverlay: true` it will activate the Window Controls Overlay (see `titleBarOverlay` for more information), otherwise no window controls will be shown.
* `hiddenInset` - Only on macOS, results in a hidden title bar with an alternative look
where the traffic light buttons are slightly more inset from the window edge.
* `customButtonsOnHover` - Results in a hidden title bar and a full size
* `customButtonsOnHover` - Only on macOS, results in a hidden title bar and a full size
content window, the traffic light buttons will display when being hovered
over in the top left of the window. **Note:** This option is currently
experimental.
Expand Down Expand Up @@ -392,10 +389,9 @@ 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` Boolean (optional) - On macOS, when using a frameless window in conjunction with
`win.setWindowButtonVisibility(true)` or using a `titleBarStyle` so that the traffic lights are visible,
this property enables the Window Controls Overlay [JavaScript APIs][overlay-javascript-apis] and
[CSS Environment Variables][overlay-css-env-vars]. 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.

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
32 changes: 24 additions & 8 deletions docs/api/frameless-window.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,26 @@ const win = new BrowserWindow({ width: 800, height: 600, frame: false })
win.show()
```

### Alternatives on macOS
### Alternatives

There's an alternative way to specify a chromeless window.
There's an alternative way to specify a chromeless window on macOS and Windows.
Instead of setting `frame` to `false` which disables both the titlebar and window controls,
you may want to have the title bar hidden and your content extend to the full window size,
yet still preserve the window controls ("traffic lights") for standard window actions.
yet still preserve the window controls ("traffic lights" on macOS) for standard window actions.
You can do so by specifying the `titleBarStyle` option:

#### `hidden`

Results in a hidden title bar and a full size content window, yet the title bar still has the standard window controls (“traffic lights”) in the top left.
Results in a hidden title bar and a full size content window. On macOS, the title bar still has the standard window controls (“traffic lights”) in the top left.

```javascript
const { BrowserWindow } = require('electron')
const win = new BrowserWindow({ titleBarStyle: 'hidden' })
win.show()
```

### Alternatives on macOS

#### `hiddenInset`

Results in a hidden title bar with an alternative look where the traffic light buttons are slightly more inset from the window edge.
Expand Down Expand Up @@ -63,19 +65,33 @@ win.show()

## Windows Control Overlay

On macOS, when using a frameless window in conjuction with `win.setWindowButtonVisibility(true)` or using one of the `titleBarStyle`s described above so
that the traffic lights are visible, you can access the Window Controls Overlay [JavaScript APIs][overlay-javascript-apis] and
[CSS Environment Variables][overlay-css-env-vars] by setting the `titleBarOverlay` option to true:
When using a frameless window in conjuction with `win.setWindowButtonVisibility(true)` on macOS, using one of the `titleBarStyle`s as described above so
that the traffic lights are visible, or using `titleBarStyle: hidden` on Windows, you can access the Window Controls Overlay [JavaScript APIs][overlay-javascript-apis] and
[CSS Environment Variables][overlay-css-env-vars] by setting the `titleBarOverlay` option to true. Specifying `true` will result in an overlay with default system colors.

On Windows, you can also specify the color of the overlay and its symbols by setting `titleBarOverlay` to an object with the options `color` and `symbolColor`. If an option is not specified, the color will default to its system color for the window control buttons:

```javascript
const { BrowserWindow } = require('electron')
const win = new BrowserWindow({
titleBarStyle: 'hiddenInset',
titleBarStyle: 'hidden',
titleBarOverlay: true
})
win.show()
```

```javascript
const { BrowserWindow } = require('electron')
const win = new BrowserWindow({
titleBarStyle: 'hidden',
titleBarOverlay: {
color: '#2f3241',
symbolColor: '#74b1be'
}
})
win.show()
```

## Transparent window

By setting the `transparent` option to `true`, you can also make the frameless
Expand Down
14 changes: 14 additions & 0 deletions electron_strings.grdp
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<grit-part>
<!-- Windows Caption Buttons -->
<message name="IDS_APP_ACCNAME_CLOSE" desc="The accessible name for the Close button.">
Close
</message>
<message name="IDS_APP_ACCNAME_MINIMIZE" desc="The accessible name for the Minimize button.">
Minimize
</message>
<message name="IDS_APP_ACCNAME_MAXIMIZE" desc="The accessible name for the Maximize button.">
Maximize
</message>
<message name="IDS_APP_ACCNAME_RESTORE" desc="The accessible name for the Restore button.">
Restore
</message>

<!-- Printing Service -->
<message name="IDS_UTILITY_PROCESS_PRINTING_SERVICE_NAME" desc="The name of the utility process used for printing conversions.">
Printing Service
Expand Down
4 changes: 4 additions & 0 deletions filenames.gni
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ filenames = {
"shell/browser/ui/views/electron_views_delegate_win.cc",
"shell/browser/ui/views/win_frame_view.cc",
"shell/browser/ui/views/win_frame_view.h",
"shell/browser/ui/views/win_caption_button.cc",
"shell/browser/ui/views/win_caption_button.h",
"shell/browser/ui/views/win_caption_button_container.cc",
"shell/browser/ui/views/win_caption_button_container.h",
"shell/browser/ui/win/dialog_thread.cc",
"shell/browser/ui/win/dialog_thread.h",
"shell/browser/ui/win/electron_desktop_native_widget_aura.cc",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": {
"@electron/docs-parser": "^0.12.1",
"@electron/typescript-definitions": "^8.9.4",
"@electron/typescript-definitions": "^8.9.5",
"@octokit/auth-app": "^2.10.0",
"@octokit/rest": "^18.0.3",
"@primer/octicons": "^10.0.0",
Expand Down
1 change: 1 addition & 0 deletions patches/chromium/.patches
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,4 @@ hack_to_allow_gclient_sync_with_host_os_mac_on_linux_in_ci.patch
don_t_run_pcscan_notifythreadcreated_if_pcscan_is_disabled.patch
add_gin_wrappable_crash_key.patch
logging_win32_only_create_a_console_if_logging_to_stderr.patch
disable_use_lld_for_macos.patch
21 changes: 21 additions & 0 deletions patches/chromium/disable_use_lld_for_macos.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: mlaurencin <mlaurencin@electronjs.org>
Date: Fri, 6 Aug 2021 15:29:48 -0700
Subject: disable use_lld for macOS

This patch disables use_lld on macOS, in order to prevent a linking bug
that occurs when building for arm64.

diff --git a/build/config/compiler/compiler.gni b/build/config/compiler/compiler.gni
index 4a714ab0a5cdafded76bba553205f94599c27a30..d2723dd224a8afd2b6dd99ec11e9e71b7f76fe4a 100644
--- a/build/config/compiler/compiler.gni
+++ b/build/config/compiler/compiler.gni
@@ -201,7 +201,7 @@ declare_args() {
# In late bring-up on macOS (see docs/mac_lld.md), and not functional at all for
# iOS. The default linker everywhere else.
use_lld =
- is_clang && (!is_apple || (target_os == "mac" && chrome_pgo_phase != 1))
+ is_clang && !is_apple
}

declare_args() {
42 changes: 41 additions & 1 deletion shell/browser/native_window.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,34 @@
#include "ui/display/win/screen_win.h"
#endif

namespace gin {

template <>
struct Converter<electron::NativeWindow::TitleBarStyle> {
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
electron::NativeWindow::TitleBarStyle* out) {
using TitleBarStyle = electron::NativeWindow::TitleBarStyle;
std::string title_bar_style;
if (!ConvertFromV8(isolate, val, &title_bar_style))
return false;
if (title_bar_style == "hidden") {
*out = TitleBarStyle::kHidden;
#if defined(OS_MAC)
} else if (title_bar_style == "hiddenInset") {
*out = TitleBarStyle::kHiddenInset;
} else if (title_bar_style == "customButtonsOnHover") {
*out = TitleBarStyle::kCustomButtonsOnHover;
#endif
} else {
return false;
}
return true;
}
};

} // namespace gin

namespace electron {

namespace {
Expand Down Expand Up @@ -54,7 +82,19 @@ NativeWindow::NativeWindow(const gin_helper::Dictionary& options,
options.Get(options::kFrame, &has_frame_);
options.Get(options::kTransparent, &transparent_);
options.Get(options::kEnableLargerThanScreen, &enable_larger_than_screen_);
options.Get(options::ktitleBarOverlay, &titlebar_overlay_);
options.Get(options::kTitleBarStyle, &title_bar_style_);

v8::Local<v8::Value> titlebar_overlay;
if (options.Get(options::ktitleBarOverlay, &titlebar_overlay)) {
if (titlebar_overlay->IsBoolean()) {
options.Get(options::ktitleBarOverlay, &titlebar_overlay_);
} else if (titlebar_overlay->IsObject()) {
titlebar_overlay_ = true;
#if !defined(OS_WIN)
DCHECK(false);
#endif
}
}

if (parent)
options.Get("modal", &is_modal_);
Expand Down
12 changes: 12 additions & 0 deletions shell/browser/native_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,14 @@ class NativeWindow : public base::SupportsUserData,
views::Widget* widget() const { return widget_.get(); }
views::View* content_view() const { return content_view_; }

enum class TitleBarStyle {
kNormal,
kHidden,
kHiddenInset,
kCustomButtonsOnHover,
};
TitleBarStyle title_bar_style() const { return title_bar_style_; }

bool has_frame() const { return has_frame_; }
void set_has_frame(bool has_frame) { has_frame_ = has_frame; }

Expand Down Expand Up @@ -347,8 +355,12 @@ class NativeWindow : public base::SupportsUserData,
[&browser_view](NativeBrowserView* n) { return (n == browser_view); });
}

// The boolean parsing of the "titleBarOverlay" option
bool titlebar_overlay_ = false;

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

private:
std::unique_ptr<views::Widget> widget_;

Expand Down
11 changes: 0 additions & 11 deletions shell/browser/native_window_mac.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,14 +183,6 @@ class NativeWindowMac : public NativeWindow,
kInactive,
};

enum class TitleBarStyle {
kNormal,
kHidden,
kHiddenInset,
kCustomButtonsOnHover,
};
TitleBarStyle title_bar_style() const { return title_bar_style_; }

ElectronPreviewItem* preview_item() const { return preview_item_.get(); }
ElectronTouchBar* touch_bar() const { return touch_bar_.get(); }
bool zoom_to_page_width() const { return zoom_to_page_width_; }
Expand Down Expand Up @@ -248,9 +240,6 @@ class NativeWindowMac : public NativeWindow,
// The presentation options before entering kiosk mode.
NSApplicationPresentationOptions kiosk_options_;

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

// The "visualEffectState" option.
VisualEffectState visual_effect_state_ = VisualEffectState::kFollowWindow;

Expand Down
23 changes: 0 additions & 23 deletions shell/browser/native_window_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -165,28 +165,6 @@ - (void)drawRect:(NSRect)dirtyRect {

namespace gin {

template <>
struct Converter<electron::NativeWindowMac::TitleBarStyle> {
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
electron::NativeWindowMac::TitleBarStyle* out) {
using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle;
std::string title_bar_style;
if (!ConvertFromV8(isolate, val, &title_bar_style))
return false;
if (title_bar_style == "hidden") {
*out = TitleBarStyle::kHidden;
} else if (title_bar_style == "hiddenInset") {
*out = TitleBarStyle::kHiddenInset;
} else if (title_bar_style == "customButtonsOnHover") {
*out = TitleBarStyle::kCustomButtonsOnHover;
} else {
return false;
}
return true;
}
};

template <>
struct Converter<electron::NativeWindowMac::VisualEffectState> {
static bool FromV8(v8::Isolate* isolate,
Expand Down Expand Up @@ -276,7 +254,6 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) {

bool resizable = true;
options.Get(options::kResizable, &resizable);
options.Get(options::kTitleBarStyle, &title_bar_style_);
options.Get(options::kZoomToPageWidth, &zoom_to_page_width_);
options.Get(options::kSimpleFullScreen, &always_simple_fullscreen_);
options.GetOptional(options::kTrafficLightPosition, &traffic_light_position_);
Expand Down
33 changes: 33 additions & 0 deletions shell/browser/native_window_views.cc
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,14 @@

#elif defined(OS_WIN)
#include "base/win/win_util.h"
#include "extensions/common/image_util.h"
#include "shell/browser/ui/views/win_frame_view.h"
#include "shell/browser/ui/win/electron_desktop_native_widget_aura.h"
#include "skia/ext/skia_utils_win.h"
#include "ui/base/win/shell.h"
#include "ui/display/screen.h"
#include "ui/display/win/screen_win.h"
#include "ui/gfx/color_utils.h"
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#endif

Expand Down Expand Up @@ -165,6 +167,37 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
options.Get("thickFrame", &thick_frame_);
if (transparent())
thick_frame_ = false;

overlay_button_color_ = color_utils::GetSysSkColor(COLOR_BTNFACE);
overlay_symbol_color_ = color_utils::GetSysSkColor(COLOR_BTNTEXT);

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);
options.Get(options::ktitleBarOverlay, &titlebar_overlay_obj);

std::string overlay_color_string;
if (titlebar_overlay_obj.Get(options::kOverlayButtonColor,
&overlay_color_string)) {
bool success = extensions::image_util::ParseCssColorString(
overlay_color_string, &overlay_button_color_);
DCHECK(success);
}

std::string overlay_symbol_color_string;
if (titlebar_overlay_obj.Get(options::kOverlaySymbolColor,
&overlay_symbol_color_string)) {
bool success = extensions::image_util::ParseCssColorString(
overlay_symbol_color_string, &overlay_symbol_color_);
DCHECK(success);
}
}

if (title_bar_style_ != TitleBarStyle::kNormal)
set_has_frame(false);
#endif

if (enable_larger_than_screen())
Expand Down

0 comments on commit 5260355

Please sign in to comment.