feat: enable windows control overlay on Windows (#29600)
* 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 (#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>
This commit is contained in:
parent
42936b07fe
commit
41646d1168
26 changed files with 974 additions and 72 deletions
|
@ -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 {
|
||||
|
@ -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_);
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
@ -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_;
|
||||
|
||||
|
|
|
@ -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_; }
|
||||
|
@ -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;
|
||||
|
||||
|
|
|
@ -165,28 +165,6 @@
|
|||
|
||||
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,
|
||||
|
@ -276,7 +254,6 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
|||
|
||||
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_);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -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())
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#if defined(OS_WIN)
|
||||
#include "base/win/scoped_gdi_object.h"
|
||||
#include "shell/browser/ui/win/taskbar_host.h"
|
||||
|
||||
#endif
|
||||
|
||||
namespace views {
|
||||
|
@ -174,6 +175,15 @@ class NativeWindowViews : public NativeWindow,
|
|||
TaskbarHost& taskbar_host() { return taskbar_host_; }
|
||||
#endif
|
||||
|
||||
#if defined(OS_WIN)
|
||||
bool IsWindowControlsOverlayEnabled() const {
|
||||
return (title_bar_style_ == NativeWindowViews::TitleBarStyle::kHidden) &&
|
||||
titlebar_overlay_;
|
||||
}
|
||||
SkColor overlay_button_color() const { return overlay_button_color_; }
|
||||
SkColor overlay_symbol_color() const { return overlay_symbol_color_; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
// views::WidgetObserver:
|
||||
void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
|
||||
|
@ -292,6 +302,11 @@ class NativeWindowViews : public NativeWindow,
|
|||
|
||||
// Whether the window is currently being moved.
|
||||
bool is_moving_ = false;
|
||||
|
||||
// The color to use as the theme and symbol colors respectively for Window
|
||||
// Controls Overlay if enabled on Windows.
|
||||
SkColor overlay_button_color_;
|
||||
SkColor overlay_symbol_color_;
|
||||
#endif
|
||||
|
||||
// Handles unhandled keyboard messages coming back from the renderer process.
|
||||
|
|
|
@ -85,17 +85,17 @@ int FramelessView::NonClientHitTest(const gfx::Point& cursor) {
|
|||
return HTCAPTION;
|
||||
}
|
||||
|
||||
// Support resizing frameless window by dragging the border.
|
||||
int frame_component = ResizingBorderHitTest(cursor);
|
||||
if (frame_component != HTNOWHERE)
|
||||
return frame_component;
|
||||
|
||||
// Check for possible draggable region in the client area for the frameless
|
||||
// window.
|
||||
SkRegion* draggable_region = window_->draggable_region();
|
||||
if (draggable_region && draggable_region->contains(cursor.x(), cursor.y()))
|
||||
return HTCAPTION;
|
||||
|
||||
// Support resizing frameless window by dragging the border.
|
||||
int frame_component = ResizingBorderHitTest(cursor);
|
||||
if (frame_component != HTNOWHERE)
|
||||
return frame_component;
|
||||
|
||||
return HTCLIENT;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,8 @@ class FramelessView : public views::NonClientFrameView {
|
|||
NativeWindowViews* window_ = nullptr;
|
||||
views::Widget* frame_ = nullptr;
|
||||
|
||||
friend class NativeWindowsViews;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(FramelessView);
|
||||
};
|
||||
|
|
220
shell/browser/ui/views/win_caption_button.cc
Normal file
220
shell/browser/ui/views/win_caption_button.cc
Normal file
|
@ -0,0 +1,220 @@
|
|||
// Copyright (c) 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#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"
|
||||
#include "ui/base/metadata/metadata_impl_macros.h"
|
||||
#include "ui/base/theme_provider.h"
|
||||
#include "ui/gfx/animation/tween.h"
|
||||
#include "ui/gfx/color_utils.h"
|
||||
#include "ui/gfx/geometry/rect_conversions.h"
|
||||
#include "ui/gfx/scoped_canvas.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
WinCaptionButton::WinCaptionButton(PressedCallback callback,
|
||||
WinFrameView* frame_view,
|
||||
ViewID button_type,
|
||||
const std::u16string& accessible_name)
|
||||
: views::Button(std::move(callback)),
|
||||
frame_view_(frame_view),
|
||||
button_type_(button_type) {
|
||||
SetAnimateOnStateChange(true);
|
||||
// Not focusable by default, only for accessibility.
|
||||
SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
|
||||
SetAccessibleName(accessible_name);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void WinCaptionButton::OnPaintBackground(gfx::Canvas* canvas) {
|
||||
// Paint the background of the button (the semi-transparent rectangle that
|
||||
// appears when you hover or press the button).
|
||||
|
||||
const SkColor bg_color = frame_view_->window()->overlay_button_color();
|
||||
const SkAlpha theme_alpha = SkColorGetA(bg_color);
|
||||
|
||||
gfx::Rect bounds = GetContentsBounds();
|
||||
bounds.Inset(0, 0, 0, 0);
|
||||
|
||||
canvas->FillRect(bounds, SkColorSetA(bg_color, theme_alpha));
|
||||
|
||||
SkColor base_color;
|
||||
SkAlpha hovered_alpha, pressed_alpha;
|
||||
if (button_type_ == VIEW_ID_CLOSE_BUTTON) {
|
||||
base_color = SkColorSetRGB(0xE8, 0x11, 0x23);
|
||||
hovered_alpha = SK_AlphaOPAQUE;
|
||||
pressed_alpha = 0x98;
|
||||
} else {
|
||||
// Match the native buttons.
|
||||
base_color = frame_view_->GetReadableFeatureColor(bg_color);
|
||||
hovered_alpha = 0x1A;
|
||||
pressed_alpha = 0x33;
|
||||
|
||||
if (theme_alpha > 0) {
|
||||
// Theme buttons have slightly increased opacity to make them stand out
|
||||
// against a visually-busy frame image.
|
||||
constexpr float kAlphaScale = 1.3f;
|
||||
hovered_alpha = base::ClampRound<SkAlpha>(hovered_alpha * kAlphaScale);
|
||||
pressed_alpha = base::ClampRound<SkAlpha>(pressed_alpha * kAlphaScale);
|
||||
}
|
||||
}
|
||||
|
||||
SkAlpha alpha;
|
||||
if (GetState() == STATE_PRESSED)
|
||||
alpha = pressed_alpha;
|
||||
else
|
||||
alpha = gfx::Tween::IntValueBetween(hover_animation().GetCurrentValue(),
|
||||
SK_AlphaTRANSPARENT, hovered_alpha);
|
||||
canvas->FillRect(bounds, SkColorSetA(base_color, alpha));
|
||||
}
|
||||
|
||||
void WinCaptionButton::PaintButtonContents(gfx::Canvas* canvas) {
|
||||
PaintSymbol(canvas);
|
||||
}
|
||||
|
||||
int WinCaptionButton::GetBetweenButtonSpacing() const {
|
||||
const int display_order_index = GetButtonDisplayOrderIndex();
|
||||
return display_order_index == 0
|
||||
? 0
|
||||
: WindowFrameUtil::kWindows10GlassCaptionButtonVisualSpacing;
|
||||
}
|
||||
|
||||
int WinCaptionButton::GetButtonDisplayOrderIndex() const {
|
||||
int button_display_order = 0;
|
||||
switch (button_type_) {
|
||||
case VIEW_ID_MINIMIZE_BUTTON:
|
||||
button_display_order = 0;
|
||||
break;
|
||||
case VIEW_ID_MAXIMIZE_BUTTON:
|
||||
case VIEW_ID_RESTORE_BUTTON:
|
||||
button_display_order = 1;
|
||||
break;
|
||||
case VIEW_ID_CLOSE_BUTTON:
|
||||
button_display_order = 2;
|
||||
break;
|
||||
default:
|
||||
NOTREACHED();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Reverse the ordering if we're in RTL mode
|
||||
if (base::i18n::IsRTL())
|
||||
button_display_order = 2 - button_display_order;
|
||||
|
||||
return button_display_order;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Canvas::DrawRect's stroke can bleed out of |rect|'s bounds, so this draws a
|
||||
// rectangle inset such that the result is constrained to |rect|'s size.
|
||||
void DrawRect(gfx::Canvas* canvas,
|
||||
const gfx::Rect& rect,
|
||||
const cc::PaintFlags& flags) {
|
||||
gfx::RectF rect_f(rect);
|
||||
float stroke_half_width = flags.getStrokeWidth() / 2;
|
||||
rect_f.Inset(stroke_half_width, stroke_half_width);
|
||||
canvas->DrawRect(rect_f, flags);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void WinCaptionButton::PaintSymbol(gfx::Canvas* canvas) {
|
||||
SkColor symbol_color = frame_view_->window()->overlay_symbol_color();
|
||||
|
||||
if (button_type_ == VIEW_ID_CLOSE_BUTTON &&
|
||||
hover_animation().is_animating()) {
|
||||
symbol_color = gfx::Tween::ColorValueBetween(
|
||||
hover_animation().GetCurrentValue(), symbol_color, SK_ColorWHITE);
|
||||
} else if (button_type_ == VIEW_ID_CLOSE_BUTTON &&
|
||||
(GetState() == STATE_HOVERED || GetState() == STATE_PRESSED)) {
|
||||
symbol_color = SK_ColorWHITE;
|
||||
}
|
||||
|
||||
gfx::ScopedCanvas scoped_canvas(canvas);
|
||||
const float scale = canvas->UndoDeviceScaleFactor();
|
||||
|
||||
const int symbol_size_pixels = std::round(10 * scale);
|
||||
gfx::RectF bounds_rect(GetContentsBounds());
|
||||
bounds_rect.Scale(scale);
|
||||
gfx::Rect symbol_rect(gfx::ToEnclosingRect(bounds_rect));
|
||||
symbol_rect.ClampToCenteredSize(
|
||||
gfx::Size(symbol_size_pixels, symbol_size_pixels));
|
||||
|
||||
cc::PaintFlags flags;
|
||||
flags.setAntiAlias(false);
|
||||
flags.setColor(symbol_color);
|
||||
flags.setStyle(cc::PaintFlags::kStroke_Style);
|
||||
// Stroke width jumps up a pixel every time we reach a new integral scale.
|
||||
const int stroke_width = std::floor(scale);
|
||||
flags.setStrokeWidth(stroke_width);
|
||||
|
||||
switch (button_type_) {
|
||||
case VIEW_ID_MINIMIZE_BUTTON: {
|
||||
const int y = symbol_rect.CenterPoint().y();
|
||||
const gfx::Point p1 = gfx::Point(symbol_rect.x(), y);
|
||||
const gfx::Point p2 = gfx::Point(symbol_rect.right(), y);
|
||||
canvas->DrawLine(p1, p2, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
case VIEW_ID_MAXIMIZE_BUTTON:
|
||||
DrawRect(canvas, symbol_rect, flags);
|
||||
return;
|
||||
|
||||
case VIEW_ID_RESTORE_BUTTON: {
|
||||
// Bottom left ("in front") square.
|
||||
const int separation = std::floor(2 * scale);
|
||||
symbol_rect.Inset(0, separation, separation, 0);
|
||||
DrawRect(canvas, symbol_rect, flags);
|
||||
|
||||
// Top right ("behind") square.
|
||||
canvas->ClipRect(symbol_rect, SkClipOp::kDifference);
|
||||
symbol_rect.Offset(separation, -separation);
|
||||
DrawRect(canvas, symbol_rect, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
case VIEW_ID_CLOSE_BUTTON: {
|
||||
flags.setAntiAlias(true);
|
||||
// The close button's X is surrounded by a "halo" of transparent pixels.
|
||||
// When the X is white, the transparent pixels need to be a bit brighter
|
||||
// to be visible.
|
||||
const float stroke_halo =
|
||||
stroke_width * (symbol_color == SK_ColorWHITE ? 0.1f : 0.05f);
|
||||
flags.setStrokeWidth(stroke_width + stroke_halo);
|
||||
|
||||
// TODO(bsep): This sometimes draws misaligned at fractional device scales
|
||||
// because the button's origin isn't necessarily aligned to pixels.
|
||||
canvas->ClipRect(symbol_rect);
|
||||
SkPath path;
|
||||
path.moveTo(symbol_rect.x(), symbol_rect.y());
|
||||
path.lineTo(symbol_rect.right(), symbol_rect.bottom());
|
||||
path.moveTo(symbol_rect.right(), symbol_rect.y());
|
||||
path.lineTo(symbol_rect.x(), symbol_rect.bottom());
|
||||
canvas->DrawPath(path, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
NOTREACHED();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} // namespace electron
|
54
shell/browser/ui/views/win_caption_button.h
Normal file
54
shell/browser/ui/views/win_caption_button.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_BROWSER_UI_VIEWS_WIN_CAPTION_BUTTON_H_
|
||||
#define SHELL_BROWSER_UI_VIEWS_WIN_CAPTION_BUTTON_H_
|
||||
|
||||
#include "chrome/browser/ui/view_ids.h"
|
||||
#include "ui/base/metadata/metadata_header_macros.h"
|
||||
#include "ui/gfx/canvas.h"
|
||||
#include "ui/views/controls/button/button.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
class WinFrameView;
|
||||
|
||||
class WinCaptionButton : public views::Button {
|
||||
public:
|
||||
WinCaptionButton(PressedCallback callback,
|
||||
WinFrameView* frame_view,
|
||||
ViewID button_type,
|
||||
const std::u16string& accessible_name);
|
||||
WinCaptionButton(const WinCaptionButton&) = delete;
|
||||
WinCaptionButton& operator=(const WinCaptionButton&) = delete;
|
||||
|
||||
// // views::Button:
|
||||
gfx::Size CalculatePreferredSize() const override;
|
||||
void OnPaintBackground(gfx::Canvas* canvas) override;
|
||||
void PaintButtonContents(gfx::Canvas* canvas) override;
|
||||
|
||||
// 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.
|
||||
int GetBetweenButtonSpacing() const;
|
||||
|
||||
// Returns the order in which this button will be displayed (with 0 being
|
||||
// drawn farthest to the left, and larger indices being drawn to the right of
|
||||
// smaller indices).
|
||||
int GetButtonDisplayOrderIndex() const;
|
||||
|
||||
// The base color to use for the button symbols and background blending. Uses
|
||||
// the more readable of black and white.
|
||||
SkColor GetBaseColor() const;
|
||||
|
||||
// Paints the minimize/maximize/restore/close icon for the button.
|
||||
void PaintSymbol(gfx::Canvas* canvas);
|
||||
|
||||
WinFrameView* frame_view_;
|
||||
ViewID button_type_;
|
||||
};
|
||||
} // namespace electron
|
||||
|
||||
#endif // SHELL_BROWSER_UI_VIEWS_WIN_CAPTION_BUTTON_H_
|
143
shell/browser/ui/views/win_caption_button_container.cc
Normal file
143
shell/browser/ui/views/win_caption_button_container.cc
Normal file
|
@ -0,0 +1,143 @@
|
|||
// Copyright 2020 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/ui/views/win_caption_button_container.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "shell/browser/ui/views/win_caption_button.h"
|
||||
#include "shell/browser/ui/views/win_frame_view.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/strings/grit/ui_strings.h"
|
||||
#include "ui/views/layout/flex_layout.h"
|
||||
#include "ui/views/view_class_properties.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
std::unique_ptr<WinCaptionButton> CreateCaptionButton(
|
||||
views::Button::PressedCallback callback,
|
||||
WinFrameView* frame_view,
|
||||
ViewID button_type,
|
||||
int accessible_name_resource_id) {
|
||||
return std::make_unique<WinCaptionButton>(
|
||||
std::move(callback), frame_view, button_type,
|
||||
l10n_util::GetStringUTF16(accessible_name_resource_id));
|
||||
}
|
||||
|
||||
bool HitTestCaptionButton(WinCaptionButton* button, const gfx::Point& point) {
|
||||
return button && button->GetVisible() && button->bounds().Contains(point);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
WinCaptionButtonContainer::WinCaptionButtonContainer(WinFrameView* frame_view)
|
||||
: frame_view_(frame_view),
|
||||
minimize_button_(AddChildView(CreateCaptionButton(
|
||||
base::BindRepeating(&views::Widget::Minimize,
|
||||
base::Unretained(frame_view_->frame())),
|
||||
frame_view_,
|
||||
VIEW_ID_MINIMIZE_BUTTON,
|
||||
IDS_APP_ACCNAME_MINIMIZE))),
|
||||
maximize_button_(AddChildView(CreateCaptionButton(
|
||||
base::BindRepeating(&views::Widget::Maximize,
|
||||
base::Unretained(frame_view_->frame())),
|
||||
frame_view_,
|
||||
VIEW_ID_MAXIMIZE_BUTTON,
|
||||
IDS_APP_ACCNAME_MAXIMIZE))),
|
||||
restore_button_(AddChildView(CreateCaptionButton(
|
||||
base::BindRepeating(&views::Widget::Restore,
|
||||
base::Unretained(frame_view_->frame())),
|
||||
frame_view_,
|
||||
VIEW_ID_RESTORE_BUTTON,
|
||||
IDS_APP_ACCNAME_RESTORE))),
|
||||
close_button_(AddChildView(CreateCaptionButton(
|
||||
base::BindRepeating(&views::Widget::CloseWithReason,
|
||||
base::Unretained(frame_view_->frame()),
|
||||
views::Widget::ClosedReason::kCloseButtonClicked),
|
||||
frame_view_,
|
||||
VIEW_ID_CLOSE_BUTTON,
|
||||
IDS_APP_ACCNAME_CLOSE))) {
|
||||
// Layout is horizontal, with buttons placed at the trailing end of the view.
|
||||
// This allows the container to expand to become a faux titlebar/drag handle.
|
||||
auto* const layout = SetLayoutManager(std::make_unique<views::FlexLayout>());
|
||||
layout->SetOrientation(views::LayoutOrientation::kHorizontal)
|
||||
.SetMainAxisAlignment(views::LayoutAlignment::kEnd)
|
||||
.SetCrossAxisAlignment(views::LayoutAlignment::kStart)
|
||||
.SetDefault(
|
||||
views::kFlexBehaviorKey,
|
||||
views::FlexSpecification(views::LayoutOrientation::kHorizontal,
|
||||
views::MinimumFlexSizeRule::kPreferred,
|
||||
views::MaximumFlexSizeRule::kPreferred,
|
||||
/* adjust_width_for_height */ false,
|
||||
views::MinimumFlexSizeRule::kScaleToZero));
|
||||
}
|
||||
|
||||
WinCaptionButtonContainer::~WinCaptionButtonContainer() {}
|
||||
|
||||
int WinCaptionButtonContainer::NonClientHitTest(const gfx::Point& point) const {
|
||||
DCHECK(HitTestPoint(point))
|
||||
<< "should only be called with a point inside this view's bounds";
|
||||
if (HitTestCaptionButton(minimize_button_, point)) {
|
||||
return HTMINBUTTON;
|
||||
}
|
||||
if (HitTestCaptionButton(maximize_button_, point)) {
|
||||
return HTMAXBUTTON;
|
||||
}
|
||||
if (HitTestCaptionButton(restore_button_, point)) {
|
||||
return HTMAXBUTTON;
|
||||
}
|
||||
if (HitTestCaptionButton(close_button_, point)) {
|
||||
return HTCLOSE;
|
||||
}
|
||||
return HTCAPTION;
|
||||
}
|
||||
|
||||
void WinCaptionButtonContainer::ResetWindowControls() {
|
||||
minimize_button_->SetState(views::Button::STATE_NORMAL);
|
||||
maximize_button_->SetState(views::Button::STATE_NORMAL);
|
||||
restore_button_->SetState(views::Button::STATE_NORMAL);
|
||||
close_button_->SetState(views::Button::STATE_NORMAL);
|
||||
InvalidateLayout();
|
||||
}
|
||||
|
||||
void WinCaptionButtonContainer::AddedToWidget() {
|
||||
views::Widget* const widget = GetWidget();
|
||||
|
||||
DCHECK(!widget_observation_.IsObserving());
|
||||
widget_observation_.Observe(widget);
|
||||
|
||||
UpdateButtons();
|
||||
|
||||
if (frame_view_->window()->IsWindowControlsOverlayEnabled()) {
|
||||
SetPaintToLayer();
|
||||
}
|
||||
}
|
||||
|
||||
void WinCaptionButtonContainer::RemovedFromWidget() {
|
||||
DCHECK(widget_observation_.IsObserving());
|
||||
widget_observation_.Reset();
|
||||
}
|
||||
|
||||
void WinCaptionButtonContainer::OnWidgetBoundsChanged(
|
||||
views::Widget* widget,
|
||||
const gfx::Rect& new_bounds) {
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
void WinCaptionButtonContainer::UpdateButtons() {
|
||||
const bool is_maximized = frame_view_->frame()->IsMaximized();
|
||||
restore_button_->SetVisible(is_maximized);
|
||||
maximize_button_->SetVisible(!is_maximized);
|
||||
|
||||
// In touch mode, windows cannot be taken out of fullscreen or tiled mode, so
|
||||
// the maximize/restore button should be disabled.
|
||||
const bool is_touch = ui::TouchUiController::Get()->touch_ui();
|
||||
restore_button_->SetEnabled(!is_touch);
|
||||
maximize_button_->SetEnabled(!is_touch);
|
||||
InvalidateLayout();
|
||||
}
|
||||
} // namespace electron
|
70
shell/browser/ui/views/win_caption_button_container.h
Normal file
70
shell/browser/ui/views/win_caption_button_container.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2020 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_BROWSER_UI_VIEWS_WIN_CAPTION_BUTTON_CONTAINER_H_
|
||||
#define SHELL_BROWSER_UI_VIEWS_WIN_CAPTION_BUTTON_CONTAINER_H_
|
||||
|
||||
#include "base/scoped_observation.h"
|
||||
#include "ui/base/metadata/metadata_header_macros.h"
|
||||
#include "ui/base/pointer/touch_ui_controller.h"
|
||||
#include "ui/views/controls/button/button.h"
|
||||
#include "ui/views/view.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
#include "ui/views/widget/widget_observer.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
class WinFrameView;
|
||||
class WinCaptionButton;
|
||||
|
||||
// Provides a container for Windows 10 caption buttons that can be moved between
|
||||
// frame and browser window as needed. When extended horizontally, becomes a
|
||||
// grab bar for moving the window.
|
||||
class WinCaptionButtonContainer : public views::View,
|
||||
public views::WidgetObserver {
|
||||
public:
|
||||
explicit WinCaptionButtonContainer(WinFrameView* frame_view);
|
||||
~WinCaptionButtonContainer() override;
|
||||
|
||||
// Tests to see if the specified |point| (which is expressed in this view's
|
||||
// coordinates and which must be within this view's bounds) is within one of
|
||||
// the caption buttons. Returns one of HitTestCompat enum defined in
|
||||
// ui/base/hit_test.h, HTCAPTION if the area hit would be part of the window's
|
||||
// drag handle, and HTNOWHERE otherwise.
|
||||
// See also ClientView::NonClientHitTest.
|
||||
int NonClientHitTest(const gfx::Point& point) const;
|
||||
|
||||
private:
|
||||
// views::View:
|
||||
void AddedToWidget() override;
|
||||
void RemovedFromWidget() override;
|
||||
|
||||
// views::WidgetObserver:
|
||||
void OnWidgetBoundsChanged(views::Widget* widget,
|
||||
const gfx::Rect& new_bounds) override;
|
||||
|
||||
void ResetWindowControls();
|
||||
|
||||
// Sets caption button visibility and enabled state based on window state.
|
||||
// Only one of maximize or restore button should ever be visible at the same
|
||||
// time, and both are disabled in tablet UI mode.
|
||||
void UpdateButtons();
|
||||
|
||||
WinFrameView* const frame_view_;
|
||||
WinCaptionButton* const minimize_button_;
|
||||
WinCaptionButton* const maximize_button_;
|
||||
WinCaptionButton* const restore_button_;
|
||||
WinCaptionButton* const close_button_;
|
||||
|
||||
base::ScopedObservation<views::Widget, views::WidgetObserver>
|
||||
widget_observation_{this};
|
||||
|
||||
base::CallbackListSubscription subscription_ =
|
||||
ui::TouchUiController::Get()->RegisterCallback(
|
||||
base::BindRepeating(&WinCaptionButtonContainer::UpdateButtons,
|
||||
base::Unretained(this)));
|
||||
};
|
||||
} // namespace electron
|
||||
|
||||
#endif // SHELL_BROWSER_UI_VIEWS_WIN_CAPTION_BUTTON_CONTAINER_H_
|
|
@ -1,11 +1,24 @@
|
|||
// Copyright (c) 2014 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// Portions of this file are sourced from
|
||||
// chrome/browser/ui/views/frame/glass_browser_frame_view.cc,
|
||||
// Copyright (c) 2012 The Chromium Authors,
|
||||
// which is governed by a BSD-style license
|
||||
|
||||
#include "shell/browser/ui/views/win_frame_view.h"
|
||||
|
||||
#include <dwmapi.h>
|
||||
#include <memory>
|
||||
|
||||
#include "base/win/windows_version.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "shell/browser/ui/views/win_caption_button_container.h"
|
||||
#include "ui/base/win/hwnd_metrics.h"
|
||||
#include "ui/display/win/dpi.h"
|
||||
#include "ui/display/win/screen_win.h"
|
||||
#include "ui/gfx/geometry/dip_util.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
#include "ui/views/win/hwnd_util.h"
|
||||
|
||||
|
@ -17,6 +30,30 @@ WinFrameView::WinFrameView() = default;
|
|||
|
||||
WinFrameView::~WinFrameView() = default;
|
||||
|
||||
void WinFrameView::Init(NativeWindowViews* window, views::Widget* frame) {
|
||||
window_ = window;
|
||||
frame_ = frame;
|
||||
|
||||
if (window->IsWindowControlsOverlayEnabled()) {
|
||||
caption_button_container_ =
|
||||
AddChildView(std::make_unique<WinCaptionButtonContainer>(this));
|
||||
} else {
|
||||
caption_button_container_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SkColor WinFrameView::GetReadableFeatureColor(SkColor background_color) {
|
||||
// color_utils::GetColorWithMaxContrast()/IsDark() aren't used here because
|
||||
// they switch based on the Chrome light/dark endpoints, while we want to use
|
||||
// the system native behavior below.
|
||||
const auto windows_luma = [](SkColor c) {
|
||||
return 0.25f * SkColorGetR(c) + 0.625f * SkColorGetG(c) +
|
||||
0.125f * SkColorGetB(c);
|
||||
};
|
||||
return windows_luma(background_color) <= 128.0f ? SK_ColorWHITE
|
||||
: SK_ColorBLACK;
|
||||
}
|
||||
|
||||
gfx::Rect WinFrameView::GetWindowBoundsForClientBounds(
|
||||
const gfx::Rect& client_bounds) const {
|
||||
return views::GetWindowBoundsForClientBounds(
|
||||
|
@ -24,15 +61,194 @@ gfx::Rect WinFrameView::GetWindowBoundsForClientBounds(
|
|||
client_bounds);
|
||||
}
|
||||
|
||||
int WinFrameView::FrameBorderThickness() const {
|
||||
return (IsMaximized() || frame()->IsFullscreen())
|
||||
? 0
|
||||
: display::win::ScreenWin::GetSystemMetricsInDIP(SM_CXSIZEFRAME);
|
||||
}
|
||||
|
||||
int WinFrameView::NonClientHitTest(const gfx::Point& point) {
|
||||
if (window_->has_frame())
|
||||
return frame_->client_view()->NonClientHitTest(point);
|
||||
else
|
||||
return FramelessView::NonClientHitTest(point);
|
||||
|
||||
if (ShouldCustomDrawSystemTitlebar()) {
|
||||
// See if the point is within any of the window controls.
|
||||
if (caption_button_container_) {
|
||||
gfx::Point local_point = point;
|
||||
|
||||
ConvertPointToTarget(parent(), caption_button_container_, &local_point);
|
||||
if (caption_button_container_->HitTestPoint(local_point)) {
|
||||
const int hit_test_result =
|
||||
caption_button_container_->NonClientHitTest(local_point);
|
||||
if (hit_test_result != HTNOWHERE)
|
||||
return hit_test_result;
|
||||
}
|
||||
}
|
||||
|
||||
// On Windows 8+, the caption buttons are almost butted up to the top right
|
||||
// corner of the window. This code ensures the mouse isn't set to a size
|
||||
// cursor while hovering over the caption buttons, thus giving the incorrect
|
||||
// impression that the user can resize the window.
|
||||
if (base::win::GetVersion() >= base::win::Version::WIN8) {
|
||||
RECT button_bounds = {0};
|
||||
if (SUCCEEDED(DwmGetWindowAttribute(
|
||||
views::HWNDForWidget(frame()), DWMWA_CAPTION_BUTTON_BOUNDS,
|
||||
&button_bounds, sizeof(button_bounds)))) {
|
||||
gfx::RectF button_bounds_in_dips = gfx::ConvertRectToDips(
|
||||
gfx::Rect(button_bounds), display::win::GetDPIScale());
|
||||
// TODO(crbug.com/1131681): GetMirroredRect() requires an integer rect,
|
||||
// but the size in DIPs may not be an integer with a fractional device
|
||||
// scale factor. If we want to keep using integers, the choice to use
|
||||
// ToFlooredRectDeprecated() seems to be doing the wrong thing given the
|
||||
// comment below about insetting 1 DIP instead of 1 physical pixel. We
|
||||
// should probably use ToEnclosedRect() and then we could have inset 1
|
||||
// physical pixel here.
|
||||
gfx::Rect buttons = GetMirroredRect(
|
||||
gfx::ToFlooredRectDeprecated(button_bounds_in_dips));
|
||||
|
||||
// There is a small one-pixel strip right above the caption buttons in
|
||||
// which the resize border "peeks" through.
|
||||
constexpr int kCaptionButtonTopInset = 1;
|
||||
// The sizing region at the window edge above the caption buttons is
|
||||
// 1 px regardless of scale factor. If we inset by 1 before converting
|
||||
// to DIPs, the precision loss might eliminate this region entirely. The
|
||||
// best we can do is to inset after conversion. This guarantees we'll
|
||||
// show the resize cursor when resizing is possible. The cost of which
|
||||
// is also maybe showing it over the portion of the DIP that isn't the
|
||||
// outermost pixel.
|
||||
buttons.Inset(0, kCaptionButtonTopInset, 0, 0);
|
||||
if (buttons.Contains(point))
|
||||
return HTNOWHERE;
|
||||
}
|
||||
}
|
||||
|
||||
int top_border_thickness = FrameTopBorderThickness(false);
|
||||
// At the window corners the resize area is not actually bigger, but the 16
|
||||
// pixels at the end of the top and bottom edges trigger diagonal resizing.
|
||||
constexpr int kResizeCornerWidth = 16;
|
||||
int window_component = GetHTComponentForFrame(
|
||||
point, gfx::Insets(top_border_thickness, 0, 0, 0), top_border_thickness,
|
||||
kResizeCornerWidth - FrameBorderThickness(),
|
||||
frame()->widget_delegate()->CanResize());
|
||||
if (window_component != HTNOWHERE)
|
||||
return window_component;
|
||||
}
|
||||
|
||||
// Use the parent class's hittest last
|
||||
return FramelessView::NonClientHitTest(point);
|
||||
}
|
||||
|
||||
const char* WinFrameView::GetClassName() const {
|
||||
return kViewClassName;
|
||||
}
|
||||
|
||||
bool WinFrameView::IsMaximized() const {
|
||||
return frame()->IsMaximized();
|
||||
}
|
||||
|
||||
bool WinFrameView::ShouldCustomDrawSystemTitlebar() const {
|
||||
return window()->IsWindowControlsOverlayEnabled();
|
||||
}
|
||||
|
||||
void WinFrameView::Layout() {
|
||||
LayoutCaptionButtons();
|
||||
if (window()->IsWindowControlsOverlayEnabled()) {
|
||||
LayoutWindowControlsOverlay();
|
||||
}
|
||||
NonClientFrameView::Layout();
|
||||
}
|
||||
|
||||
int WinFrameView::FrameTopBorderThickness(bool restored) const {
|
||||
// Mouse and touch locations are floored but GetSystemMetricsInDIP is rounded,
|
||||
// so we need to floor instead or else the difference will cause the hittest
|
||||
// to fail when it ought to succeed.
|
||||
return std::floor(
|
||||
FrameTopBorderThicknessPx(restored) /
|
||||
display::win::ScreenWin::GetScaleFactorForHWND(HWNDForView(this)));
|
||||
}
|
||||
|
||||
int WinFrameView::FrameTopBorderThicknessPx(bool restored) const {
|
||||
// Distinct from FrameBorderThickness() because we can't inset the top
|
||||
// border, otherwise Windows will give us a standard titlebar.
|
||||
// For maximized windows this is not true, and the top border must be
|
||||
// inset in order to avoid overlapping the monitor above.
|
||||
|
||||
// See comments in BrowserDesktopWindowTreeHostWin::GetClientAreaInsets().
|
||||
const bool needs_no_border =
|
||||
(ShouldCustomDrawSystemTitlebar() && frame()->IsMaximized()) ||
|
||||
frame()->IsFullscreen();
|
||||
if (needs_no_border && !restored)
|
||||
return 0;
|
||||
|
||||
// Note that this method assumes an equal resize handle thickness on all
|
||||
// sides of the window.
|
||||
// TODO(dfried): Consider having it return a gfx::Insets object instead.
|
||||
return ui::GetFrameThickness(
|
||||
MonitorFromWindow(HWNDForView(this), MONITOR_DEFAULTTONEAREST));
|
||||
}
|
||||
|
||||
int WinFrameView::TitlebarMaximizedVisualHeight() const {
|
||||
int maximized_height =
|
||||
display::win::ScreenWin::GetSystemMetricsInDIP(SM_CYCAPTION);
|
||||
return maximized_height;
|
||||
}
|
||||
|
||||
int WinFrameView::TitlebarHeight(bool restored) const {
|
||||
if (frame()->IsFullscreen() && !restored)
|
||||
return 0;
|
||||
|
||||
return TitlebarMaximizedVisualHeight() + FrameTopBorderThickness(false);
|
||||
}
|
||||
|
||||
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.
|
||||
// Unfortunately we can't represent either of those at hidpi without using
|
||||
// non-integral dips, so we return the closest reasonable values instead.
|
||||
if (IsMaximized())
|
||||
return FrameTopBorderThickness(false);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void WinFrameView::LayoutCaptionButtons() {
|
||||
if (!caption_button_container_)
|
||||
return;
|
||||
|
||||
// Non-custom system titlebar already contains caption buttons.
|
||||
if (!ShouldCustomDrawSystemTitlebar()) {
|
||||
caption_button_container_->SetVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// 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.
|
||||
caption_button_container_->SetBounds(width() - preferred_size.width(),
|
||||
WindowTopY(), preferred_size.width() - 1,
|
||||
height);
|
||||
}
|
||||
|
||||
void WinFrameView::LayoutWindowControlsOverlay() {
|
||||
int overlay_height = caption_button_container_->size().height();
|
||||
int overlay_width = caption_button_container_->size().width();
|
||||
int bounding_rect_width = width() - overlay_width;
|
||||
auto bounding_rect =
|
||||
GetMirroredRect(gfx::Rect(0, 0, bounding_rect_width, overlay_height));
|
||||
|
||||
window()->SetWindowControlsOverlayRect(bounding_rect);
|
||||
window()->NotifyLayoutWindowControlsOverlay();
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
// Copyright (c) 2014 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// Portions of this file are sourced from
|
||||
// chrome/browser/ui/views/frame/glass_browser_frame_view.h,
|
||||
// Copyright (c) 2012 The Chromium Authors,
|
||||
// which is governed by a BSD-style license
|
||||
|
||||
#ifndef SHELL_BROWSER_UI_VIEWS_WIN_FRAME_VIEW_H_
|
||||
#define SHELL_BROWSER_UI_VIEWS_WIN_FRAME_VIEW_H_
|
||||
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "shell/browser/ui/views/frameless_view.h"
|
||||
#include "shell/browser/ui/views/win_caption_button.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
|
@ -15,6 +22,14 @@ class WinFrameView : public FramelessView {
|
|||
WinFrameView();
|
||||
~WinFrameView() override;
|
||||
|
||||
void Init(NativeWindowViews* window, views::Widget* frame) override;
|
||||
|
||||
// Alpha to use for features in the titlebar (the window title and caption
|
||||
// buttons) when the window is inactive. They are opaque when active.
|
||||
static constexpr SkAlpha kInactiveTitlebarFeatureAlpha = 0x66;
|
||||
|
||||
SkColor GetReadableFeatureColor(SkColor background_color);
|
||||
|
||||
// views::NonClientFrameView:
|
||||
gfx::Rect GetWindowBoundsForClientBounds(
|
||||
const gfx::Rect& client_bounds) const override;
|
||||
|
@ -23,7 +38,50 @@ class WinFrameView : public FramelessView {
|
|||
// views::View:
|
||||
const char* GetClassName() const override;
|
||||
|
||||
NativeWindowViews* window() const { return window_; }
|
||||
views::Widget* frame() const { return frame_; }
|
||||
|
||||
bool IsMaximized() const;
|
||||
|
||||
bool ShouldCustomDrawSystemTitlebar() const;
|
||||
|
||||
// Visual height of the titlebar when the window is maximized (i.e. excluding
|
||||
// the area above the top of the screen).
|
||||
int TitlebarMaximizedVisualHeight() const;
|
||||
|
||||
protected:
|
||||
// views::View:
|
||||
void Layout() override;
|
||||
|
||||
private:
|
||||
friend class WinCaptionButtonContainer;
|
||||
|
||||
int FrameBorderThickness() const;
|
||||
|
||||
// Returns the thickness of the window border for the top edge of the frame,
|
||||
// which is sometimes different than FrameBorderThickness(). Does not include
|
||||
// the titlebar/tabstrip area. If |restored| is true, this is calculated as if
|
||||
// the window was restored, regardless of its current state.
|
||||
int FrameTopBorderThickness(bool restored) const;
|
||||
int FrameTopBorderThicknessPx(bool restored) const;
|
||||
|
||||
// Returns the height of the titlebar for popups or other browser types that
|
||||
// don't have tabs.
|
||||
int TitlebarHeight(bool restored) 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
|
||||
// the window to leave room for the visual border that Windows draws.
|
||||
int WindowTopY() const;
|
||||
|
||||
void LayoutCaptionButtons();
|
||||
void LayoutWindowControlsOverlay();
|
||||
|
||||
// The container holding the caption buttons (minimize, maximize, close, etc.)
|
||||
// May be null if the caption button container is destroyed before the frame
|
||||
// view. Always check for validity before using!
|
||||
WinCaptionButtonContainer* caption_button_container_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(WinFrameView);
|
||||
};
|
||||
|
||||
|
|
|
@ -31,6 +31,11 @@ const char kFullscreen[] = "fullscreen";
|
|||
const char kTrafficLightPosition[] = "trafficLightPosition";
|
||||
const char kRoundedCorners[] = "roundedCorners";
|
||||
|
||||
// The color to use as the theme and symbol colors respectively for Window
|
||||
// Controls Overlay if enabled on Windows.
|
||||
const char kOverlayButtonColor[] = "color";
|
||||
const char kOverlaySymbolColor[] = "symbolColor";
|
||||
|
||||
// Whether the window should show in taskbar.
|
||||
const char kSkipTaskbar[] = "skipTaskbar";
|
||||
|
||||
|
|
|
@ -58,6 +58,8 @@ extern const char kVisualEffectState[];
|
|||
extern const char kTrafficLightPosition[];
|
||||
extern const char kRoundedCorners[];
|
||||
extern const char ktitleBarOverlay[];
|
||||
extern const char kOverlayButtonColor[];
|
||||
extern const char kOverlaySymbolColor[];
|
||||
|
||||
// WebPreferences.
|
||||
extern const char kZoomFactor[];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue