feat: enable Windows Control Overlay on Linux (#42682)
* feat: enable Windows Control Overlay on Linux Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> * docs: update documentation for Linux WCO Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> * fix: initial symbol painting Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> * test: enable WCO tests for Linux Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> * fix: add missing Layer include Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> * chore: fix gn-check failure Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> * fix: enable BrowserWindow.setTitleBarOverlay on Linux Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> * test: fix test for maximize event on Linux Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> * fix: geometry updating on BrowserWindow.setTitleBarOverlay Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> * fix: crash when invalid titleBarStyle set Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> * chore: clean up ordering and comments Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> * Update docs/api/structures/base-window-options.md Co-authored-by: Erick Zhao <erick@hotmail.ca> Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> * feat: enable customizing symbolColor Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> * docs: correct symbolColor reference Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> * chore: update patches Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com> * chore: remove Chrome-specific padding Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
This commit is contained in:
parent
89d09922f7
commit
342ef8e7e1
24 changed files with 1026 additions and 107 deletions
|
@ -40,6 +40,8 @@
|
|||
#include "shell/browser/ui/views/win_frame_view.h"
|
||||
#include "shell/browser/ui/win/taskbar_host.h"
|
||||
#include "ui/base/win/shell.h"
|
||||
#elif BUILDFLAG(IS_LINUX)
|
||||
#include "shell/browser/ui/views/opaque_frame_view.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
|
@ -1041,11 +1043,13 @@ void BaseWindow::SetAppDetails(const gin_helper::Dictionary& options) {
|
|||
relaunch_command, relaunch_display_name,
|
||||
window_->GetAcceleratedWidget());
|
||||
}
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
|
||||
void BaseWindow::SetTitleBarOverlay(const gin_helper::Dictionary& options,
|
||||
gin_helper::Arguments* args) {
|
||||
// Ensure WCO is already enabled on this window
|
||||
if (!window_->titlebar_overlay_enabled()) {
|
||||
if (!window_->IsWindowControlsOverlayEnabled()) {
|
||||
args->ThrowError("Titlebar overlay is not enabled");
|
||||
return;
|
||||
}
|
||||
|
@ -1090,13 +1094,18 @@ void BaseWindow::SetTitleBarOverlay(const gin_helper::Dictionary& options,
|
|||
updated = true;
|
||||
}
|
||||
|
||||
// If anything was updated, invalidate the layout and schedule a paint of the
|
||||
// window's frame view
|
||||
if (updated) {
|
||||
auto* frame_view = static_cast<WinFrameView*>(
|
||||
window->widget()->non_client_view()->frame_view());
|
||||
frame_view->InvalidateCaptionButtons();
|
||||
}
|
||||
if (!updated)
|
||||
return;
|
||||
|
||||
// If anything was updated, ensure the overlay is repainted.
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
auto* frame_view = static_cast<WinFrameView*>(
|
||||
window->widget()->non_client_view()->frame_view());
|
||||
#else
|
||||
auto* frame_view = static_cast<OpaqueFrameView*>(
|
||||
window->widget()->non_client_view()->frame_view());
|
||||
#endif
|
||||
frame_view->InvalidateCaptionButtons();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1286,6 +1295,8 @@ void BaseWindow::BuildPrototype(v8::Isolate* isolate,
|
|||
.SetMethod("setThumbnailClip", &BaseWindow::SetThumbnailClip)
|
||||
.SetMethod("setThumbnailToolTip", &BaseWindow::SetThumbnailToolTip)
|
||||
.SetMethod("setAppDetails", &BaseWindow::SetAppDetails)
|
||||
#endif
|
||||
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
|
||||
.SetMethod("setTitleBarOverlay", &BaseWindow::SetTitleBarOverlay)
|
||||
#endif
|
||||
.SetProperty("id", &BaseWindow::GetID);
|
||||
|
|
|
@ -241,6 +241,9 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
|||
bool SetThumbnailClip(const gfx::Rect& region);
|
||||
bool SetThumbnailToolTip(const std::string& tooltip);
|
||||
void SetAppDetails(const gin_helper::Dictionary& options);
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
|
||||
void SetTitleBarOverlay(const gin_helper::Dictionary& options,
|
||||
gin_helper::Arguments* args);
|
||||
#endif
|
||||
|
|
|
@ -122,10 +122,6 @@ NativeWindow::NativeWindow(const gin_helper::Dictionary& options,
|
|||
int height;
|
||||
if (titlebar_overlay_dict.Get(options::kOverlayHeight, &height))
|
||||
titlebar_overlay_height_ = height;
|
||||
|
||||
#if !(BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC))
|
||||
DCHECK(false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -372,12 +372,23 @@ class NativeWindow : public base::SupportsUserData,
|
|||
kHiddenInset,
|
||||
kCustomButtonsOnHover,
|
||||
};
|
||||
|
||||
TitleBarStyle title_bar_style() const { return title_bar_style_; }
|
||||
|
||||
bool IsWindowControlsOverlayEnabled() const {
|
||||
bool valid_titlebar_style = title_bar_style() == TitleBarStyle::kHidden
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
||
|
||||
title_bar_style() == TitleBarStyle::kHiddenInset
|
||||
#endif
|
||||
;
|
||||
return valid_titlebar_style && titlebar_overlay_;
|
||||
}
|
||||
|
||||
int titlebar_overlay_height() const { return titlebar_overlay_height_; }
|
||||
void set_titlebar_overlay_height(int height) {
|
||||
titlebar_overlay_height_ = height;
|
||||
}
|
||||
bool titlebar_overlay_enabled() const { return titlebar_overlay_; }
|
||||
|
||||
bool has_frame() const { return has_frame_; }
|
||||
void set_has_frame(bool has_frame) { has_frame_ = has_frame; }
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/desktop_media_id.h"
|
||||
#include "content/public/common/color_parser.h"
|
||||
#include "shell/browser/api/electron_api_web_contents.h"
|
||||
#include "shell/browser/ui/inspectable_web_contents.h"
|
||||
#include "shell/browser/ui/inspectable_web_contents_view.h"
|
||||
|
@ -59,6 +60,7 @@
|
|||
#include "shell/browser/ui/views/client_frame_view_linux.h"
|
||||
#include "shell/browser/ui/views/frameless_view.h"
|
||||
#include "shell/browser/ui/views/native_frame_view.h"
|
||||
#include "shell/browser/ui/views/opaque_frame_view.h"
|
||||
#include "shell/common/platform_util.h"
|
||||
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
|
||||
#include "ui/views/window/native_frame_view.h"
|
||||
|
@ -76,7 +78,6 @@
|
|||
#elif BUILDFLAG(IS_WIN)
|
||||
#include "base/win/win_util.h"
|
||||
#include "base/win/windows_version.h"
|
||||
#include "content/public/common/color_parser.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"
|
||||
|
@ -219,6 +220,7 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
|
|||
|
||||
overlay_button_color_ = color_utils::GetSysSkColor(COLOR_BTNFACE);
|
||||
overlay_symbol_color_ = color_utils::GetSysSkColor(COLOR_BTNTEXT);
|
||||
#endif
|
||||
|
||||
v8::Local<v8::Value> titlebar_overlay;
|
||||
if (options.Get(options::ktitleBarOverlay, &titlebar_overlay) &&
|
||||
|
@ -244,9 +246,11 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
|
|||
}
|
||||
}
|
||||
|
||||
if (title_bar_style_ != TitleBarStyle::kNormal)
|
||||
// |hidden| is the only non-default titleBarStyle valid on Windows and Linux.
|
||||
if (title_bar_style_ == TitleBarStyle::kHidden)
|
||||
set_has_frame(false);
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
// If the taskbar is re-created after we start up, we have to rebuild all of
|
||||
// our buttons.
|
||||
taskbar_created_message_ = RegisterWindowMessage(TEXT("TaskbarCreated"));
|
||||
|
@ -1703,11 +1707,15 @@ NativeWindowViews::CreateNonClientFrameView(views::Widget* widget) {
|
|||
if (has_frame() && !has_client_frame()) {
|
||||
return std::make_unique<NativeFrameView>(this, widget);
|
||||
} else {
|
||||
auto frame_view = has_frame() && has_client_frame()
|
||||
? std::make_unique<ClientFrameViewLinux>()
|
||||
: std::make_unique<FramelessView>();
|
||||
frame_view->Init(this, widget);
|
||||
return frame_view;
|
||||
if (has_frame() && has_client_frame()) {
|
||||
auto frame_view = std::make_unique<ClientFrameViewLinux>();
|
||||
frame_view->Init(this, widget);
|
||||
return frame_view;
|
||||
} else {
|
||||
auto frame_view = std::make_unique<OpaqueFrameView>();
|
||||
frame_view->Init(this, widget);
|
||||
return frame_view;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -167,13 +167,9 @@ class NativeWindowViews : public NativeWindow,
|
|||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
TaskbarHost& taskbar_host() { return taskbar_host_; }
|
||||
void UpdateThickFrame();
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
bool IsWindowControlsOverlayEnabled() const {
|
||||
return (title_bar_style_ == NativeWindowViews::TitleBarStyle::kHidden) &&
|
||||
titlebar_overlay_;
|
||||
}
|
||||
SkColor overlay_button_color() const { return overlay_button_color_; }
|
||||
void set_overlay_button_color(SkColor color) {
|
||||
overlay_button_color_ = color;
|
||||
|
@ -183,9 +179,6 @@ class NativeWindowViews : public NativeWindow,
|
|||
overlay_symbol_color_ = color;
|
||||
}
|
||||
|
||||
void UpdateThickFrame();
|
||||
#endif
|
||||
|
||||
private:
|
||||
// views::WidgetObserver:
|
||||
void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
|
||||
|
@ -264,6 +257,10 @@ class NativeWindowViews : public NativeWindow,
|
|||
std::unique_ptr<EventDisabler> event_disabler_;
|
||||
#endif
|
||||
|
||||
// The color to use as the theme and symbol colors respectively for WCO.
|
||||
SkColor overlay_button_color_ = SkColor();
|
||||
SkColor overlay_symbol_color_ = SkColor();
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
|
||||
ui::WindowShowState last_window_state_;
|
||||
|
@ -307,11 +304,6 @@ class NativeWindowViews : public NativeWindow,
|
|||
|
||||
std::optional<gfx::Rect> pending_bounds_change_;
|
||||
|
||||
// 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_;
|
||||
|
||||
// The message ID of the "TaskbarCreated" message, sent to us when we need to
|
||||
// reset our thumbar buttons.
|
||||
UINT taskbar_created_message_ = 0;
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2024 Microsoft GmbH.
|
||||
// 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/caption_button_placeholder_container.h"
|
||||
|
||||
#include "ui/base/metadata/metadata_impl_macros.h"
|
||||
#include "ui/gfx/canvas.h"
|
||||
#include "ui/gfx/scoped_canvas.h"
|
||||
#include "ui/views/view.h"
|
||||
|
||||
CaptionButtonPlaceholderContainer::CaptionButtonPlaceholderContainer() {
|
||||
SetPaintToLayer();
|
||||
}
|
||||
|
||||
CaptionButtonPlaceholderContainer::~CaptionButtonPlaceholderContainer() =
|
||||
default;
|
||||
|
||||
BEGIN_METADATA(CaptionButtonPlaceholderContainer)
|
||||
END_METADATA
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright 2024 Microsoft GmbH.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_BROWSER_UI_VIEWS_CAPTION_BUTTON_PLACEHOLDER_CONTAINER_H_
|
||||
#define ELECTRON_SHELL_BROWSER_UI_VIEWS_CAPTION_BUTTON_PLACEHOLDER_CONTAINER_H_
|
||||
|
||||
#include "ui/base/metadata/metadata_header_macros.h"
|
||||
#include "ui/views/view.h"
|
||||
|
||||
// A placeholder container for control buttons with window controls
|
||||
// overlay display override. Does not interact with the buttons. It is just
|
||||
// used to indicate that this is non-client-area.
|
||||
class CaptionButtonPlaceholderContainer : public views::View {
|
||||
METADATA_HEADER(CaptionButtonPlaceholderContainer, views::View)
|
||||
|
||||
public:
|
||||
CaptionButtonPlaceholderContainer();
|
||||
CaptionButtonPlaceholderContainer(const CaptionButtonPlaceholderContainer&) =
|
||||
delete;
|
||||
CaptionButtonPlaceholderContainer& operator=(
|
||||
const CaptionButtonPlaceholderContainer&) = delete;
|
||||
~CaptionButtonPlaceholderContainer() override;
|
||||
};
|
||||
|
||||
#endif // ELECTRON_SHELL_BROWSER_UI_VIEWS_CAPTION_BUTTON_PLACEHOLDER_CONTAINER_H_
|
|
@ -95,6 +95,8 @@ void FramelessView::ResetWindowControls() {}
|
|||
|
||||
void FramelessView::UpdateWindowIcon() {}
|
||||
|
||||
void FramelessView::InvalidateCaptionButtons() {}
|
||||
|
||||
void FramelessView::UpdateWindowTitle() {}
|
||||
|
||||
void FramelessView::SizeConstraintsChanged() {}
|
||||
|
|
|
@ -33,6 +33,13 @@ class FramelessView : public views::NonClientFrameView {
|
|||
// Returns whether the |point| is on frameless window's resizing border.
|
||||
virtual int ResizingBorderHitTest(const gfx::Point& point);
|
||||
|
||||
// Tells the NonClientView to invalidate caption buttons
|
||||
// and forces a re-layout and re-paint.
|
||||
virtual void InvalidateCaptionButtons();
|
||||
|
||||
NativeWindowViews* window() const { return window_; }
|
||||
views::Widget* frame() const { return frame_; }
|
||||
|
||||
protected:
|
||||
// Helper function for subclasses to implement ResizingBorderHitTest with a
|
||||
// custom resize inset.
|
||||
|
|
552
shell/browser/ui/views/opaque_frame_view.cc
Normal file
552
shell/browser/ui/views/opaque_frame_view.cc
Normal file
|
@ -0,0 +1,552 @@
|
|||
// Copyright (c) 2024 Microsoft GmbH.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/ui/views/opaque_frame_view.h"
|
||||
|
||||
#include "base/containers/adapters.h"
|
||||
#include "base/i18n/rtl.h"
|
||||
#include "chrome/grit/generated_resources.h"
|
||||
#include "components/strings/grit/components_strings.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "shell/browser/ui/inspectable_web_contents_view.h"
|
||||
#include "shell/browser/ui/views/caption_button_placeholder_container.h"
|
||||
#include "ui/aura/window.h"
|
||||
#include "ui/base/hit_test.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/base/metadata/metadata_impl_macros.h"
|
||||
#include "ui/compositor/layer.h"
|
||||
#include "ui/linux/linux_ui.h"
|
||||
#include "ui/views/background.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
#include "ui/views/widget/widget_delegate.h"
|
||||
#include "ui/views/window/frame_caption_button.h"
|
||||
#include "ui/views/window/vector_icons/vector_icons.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
// These values should be the same as Chromium uses.
|
||||
constexpr int kCaptionButtonHeight = 18;
|
||||
|
||||
bool HitTestCaptionButton(views::Button* button, const gfx::Point& point) {
|
||||
return button && button->GetVisible() &&
|
||||
button->GetMirroredBounds().Contains(point);
|
||||
}
|
||||
|
||||
// The frame has a 2 px 3D edge along the top. This is overridable by
|
||||
// subclasses, so RestoredFrameEdgeInsets() should be used instead of using this
|
||||
// constant directly.
|
||||
const int kTopFrameEdgeThickness = 2;
|
||||
|
||||
// The frame has a 1 px 3D edge along the side. This is overridable by
|
||||
// subclasses, so RestoredFrameEdgeInsets() should be used instead of using this
|
||||
// constant directly.
|
||||
const int kSideFrameEdgeThickness = 1;
|
||||
|
||||
// The minimum vertical padding between the bottom of the caption buttons and
|
||||
// the top of the content shadow.
|
||||
const int kCaptionButtonBottomPadding = 3;
|
||||
|
||||
} // namespace
|
||||
|
||||
// The content edge images have a shadow built into them.
|
||||
const int OpaqueFrameView::kContentEdgeShadowThickness = 2;
|
||||
|
||||
OpaqueFrameView::OpaqueFrameView() = default;
|
||||
OpaqueFrameView::~OpaqueFrameView() = default;
|
||||
|
||||
void OpaqueFrameView::Init(NativeWindowViews* window, views::Widget* frame) {
|
||||
FramelessView::Init(window, frame);
|
||||
|
||||
if (!window->IsWindowControlsOverlayEnabled())
|
||||
return;
|
||||
|
||||
caption_button_placeholder_container_ =
|
||||
AddChildView(std::make_unique<CaptionButtonPlaceholderContainer>());
|
||||
|
||||
minimize_button_ = CreateButton(
|
||||
VIEW_ID_MINIMIZE_BUTTON, IDS_ACCNAME_MINIMIZE,
|
||||
views::CAPTION_BUTTON_ICON_MINIMIZE, HTMINBUTTON,
|
||||
views::kWindowControlMinimizeIcon,
|
||||
base::BindRepeating(&views::Widget::Minimize, base::Unretained(frame)));
|
||||
maximize_button_ = CreateButton(
|
||||
VIEW_ID_MAXIMIZE_BUTTON, IDS_ACCNAME_MAXIMIZE,
|
||||
views::CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE, HTMAXBUTTON,
|
||||
views::kWindowControlMaximizeIcon,
|
||||
base::BindRepeating(&views::Widget::Maximize, base::Unretained(frame)));
|
||||
restore_button_ = CreateButton(
|
||||
VIEW_ID_RESTORE_BUTTON, IDS_ACCNAME_RESTORE,
|
||||
views::CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE, HTMAXBUTTON,
|
||||
views::kWindowControlRestoreIcon,
|
||||
base::BindRepeating(&views::Widget::Restore, base::Unretained(frame)));
|
||||
close_button_ = CreateButton(
|
||||
VIEW_ID_CLOSE_BUTTON, IDS_ACCNAME_CLOSE, views::CAPTION_BUTTON_ICON_CLOSE,
|
||||
HTMAXBUTTON, views::kWindowControlCloseIcon,
|
||||
base::BindRepeating(&views::Widget::CloseWithReason,
|
||||
base::Unretained(frame),
|
||||
views::Widget::ClosedReason::kCloseButtonClicked));
|
||||
|
||||
// Unretained() is safe because the subscription is saved into an instance
|
||||
// member and thus will be cancelled upon the instance's destruction.
|
||||
paint_as_active_changed_subscription_ =
|
||||
frame->RegisterPaintAsActiveChangedCallback(base::BindRepeating(
|
||||
&OpaqueFrameView::PaintAsActiveChanged, base::Unretained(this)));
|
||||
}
|
||||
|
||||
int OpaqueFrameView::ResizingBorderHitTest(const gfx::Point& point) {
|
||||
return FramelessView::ResizingBorderHitTest(point);
|
||||
}
|
||||
|
||||
void OpaqueFrameView::InvalidateCaptionButtons() {
|
||||
UpdateCaptionButtonPlaceholderContainerBackground();
|
||||
UpdateFrameCaptionButtons();
|
||||
LayoutWindowControlsOverlay();
|
||||
InvalidateLayout();
|
||||
}
|
||||
|
||||
gfx::Rect OpaqueFrameView::GetBoundsForClientView() const {
|
||||
if (window()->IsWindowControlsOverlayEnabled()) {
|
||||
auto border_thickness = FrameBorderInsets(false);
|
||||
int top_height = border_thickness.top();
|
||||
return gfx::Rect(
|
||||
border_thickness.left(), top_height,
|
||||
std::max(0, width() - border_thickness.width()),
|
||||
std::max(0, height() - top_height - border_thickness.bottom()));
|
||||
}
|
||||
|
||||
return FramelessView::GetBoundsForClientView();
|
||||
}
|
||||
|
||||
gfx::Rect OpaqueFrameView::GetWindowBoundsForClientBounds(
|
||||
const gfx::Rect& client_bounds) const {
|
||||
if (window()->IsWindowControlsOverlayEnabled()) {
|
||||
int top_height = NonClientTopHeight(false);
|
||||
auto border_insets = FrameBorderInsets(false);
|
||||
return gfx::Rect(
|
||||
std::max(0, client_bounds.x() - border_insets.left()),
|
||||
std::max(0, client_bounds.y() - top_height),
|
||||
client_bounds.width() + border_insets.width(),
|
||||
client_bounds.height() + top_height + border_insets.bottom());
|
||||
}
|
||||
|
||||
return FramelessView::GetWindowBoundsForClientBounds(client_bounds);
|
||||
}
|
||||
|
||||
int OpaqueFrameView::NonClientHitTest(const gfx::Point& point) {
|
||||
if (window()->IsWindowControlsOverlayEnabled()) {
|
||||
if (HitTestCaptionButton(close_button_, point))
|
||||
return HTCLOSE;
|
||||
if (HitTestCaptionButton(restore_button_, point))
|
||||
return HTMAXBUTTON;
|
||||
if (HitTestCaptionButton(maximize_button_, point))
|
||||
return HTMAXBUTTON;
|
||||
if (HitTestCaptionButton(minimize_button_, point))
|
||||
return HTMINBUTTON;
|
||||
|
||||
if (caption_button_placeholder_container_->GetMirroredBounds().Contains(
|
||||
point)) {
|
||||
return HTCAPTION;
|
||||
}
|
||||
}
|
||||
|
||||
return FramelessView::NonClientHitTest(point);
|
||||
}
|
||||
|
||||
void OpaqueFrameView::ResetWindowControls() {
|
||||
NonClientFrameView::ResetWindowControls();
|
||||
|
||||
if (restore_button_)
|
||||
restore_button_->SetState(views::Button::STATE_NORMAL);
|
||||
if (minimize_button_)
|
||||
minimize_button_->SetState(views::Button::STATE_NORMAL);
|
||||
if (maximize_button_)
|
||||
maximize_button_->SetState(views::Button::STATE_NORMAL);
|
||||
// The close button isn't affected by this constraint.
|
||||
}
|
||||
|
||||
views::View* OpaqueFrameView::TargetForRect(views::View* root,
|
||||
const gfx::Rect& rect) {
|
||||
return views::NonClientFrameView::TargetForRect(root, rect);
|
||||
}
|
||||
|
||||
void OpaqueFrameView::Layout(PassKey) {
|
||||
LayoutSuperclass<FramelessView>(this);
|
||||
|
||||
if (!window()->IsWindowControlsOverlayEnabled())
|
||||
return;
|
||||
|
||||
// Reset all our data so that everything is invisible.
|
||||
TopAreaPadding top_area_padding = GetTopAreaPadding();
|
||||
available_space_leading_x_ = top_area_padding.leading;
|
||||
available_space_trailing_x_ = width() - top_area_padding.trailing;
|
||||
minimum_size_for_buttons_ =
|
||||
available_space_leading_x_ + width() - available_space_trailing_x_;
|
||||
placed_leading_button_ = false;
|
||||
placed_trailing_button_ = false;
|
||||
|
||||
LayoutWindowControls();
|
||||
|
||||
int height = NonClientTopHeight(false);
|
||||
auto insets = FrameBorderInsets(false);
|
||||
int container_x = placed_trailing_button_ ? available_space_trailing_x_ : 0;
|
||||
caption_button_placeholder_container_->SetBounds(
|
||||
container_x, insets.top(), minimum_size_for_buttons_ - insets.width(),
|
||||
height - insets.top());
|
||||
|
||||
LayoutWindowControlsOverlay();
|
||||
}
|
||||
|
||||
void OpaqueFrameView::OnPaint(gfx::Canvas* canvas) {
|
||||
if (!window()->IsWindowControlsOverlayEnabled())
|
||||
return;
|
||||
|
||||
if (frame()->IsFullscreen())
|
||||
return;
|
||||
|
||||
UpdateFrameCaptionButtons();
|
||||
}
|
||||
|
||||
void OpaqueFrameView::PaintAsActiveChanged() {
|
||||
if (!window()->IsWindowControlsOverlayEnabled())
|
||||
return;
|
||||
|
||||
UpdateCaptionButtonPlaceholderContainerBackground();
|
||||
UpdateFrameCaptionButtons();
|
||||
}
|
||||
|
||||
void OpaqueFrameView::UpdateFrameCaptionButtons() {
|
||||
const bool active = ShouldPaintAsActive();
|
||||
const SkColor symbol_color = window()->overlay_symbol_color();
|
||||
const SkColor background_color = window()->overlay_button_color();
|
||||
SkColor frame_color =
|
||||
background_color == SkColor() ? GetFrameColor() : background_color;
|
||||
|
||||
for (views::Button* button :
|
||||
{minimize_button_, maximize_button_, restore_button_, close_button_}) {
|
||||
DCHECK_EQ(std::string(views::FrameCaptionButton::kViewClassName),
|
||||
button->GetClassName());
|
||||
views::FrameCaptionButton* frame_caption_button =
|
||||
static_cast<views::FrameCaptionButton*>(button);
|
||||
frame_caption_button->SetPaintAsActive(active);
|
||||
frame_caption_button->SetButtonColor(symbol_color);
|
||||
frame_caption_button->SetBackgroundColor(frame_color);
|
||||
}
|
||||
}
|
||||
|
||||
void OpaqueFrameView::UpdateCaptionButtonPlaceholderContainerBackground() {
|
||||
if (caption_button_placeholder_container_) {
|
||||
const SkColor obc = window()->overlay_button_color();
|
||||
const SkColor bg_color = obc == SkColor() ? GetFrameColor() : obc;
|
||||
caption_button_placeholder_container_->SetBackground(
|
||||
views::CreateSolidBackground(bg_color));
|
||||
}
|
||||
}
|
||||
|
||||
void OpaqueFrameView::LayoutWindowControls() {
|
||||
// Keep a list of all buttons that we don't show.
|
||||
std::vector<views::FrameButton> buttons_not_shown;
|
||||
buttons_not_shown.push_back(views::FrameButton::kMaximize);
|
||||
buttons_not_shown.push_back(views::FrameButton::kMinimize);
|
||||
buttons_not_shown.push_back(views::FrameButton::kClose);
|
||||
|
||||
for (const auto& button : leading_buttons_) {
|
||||
ConfigureButton(button, ALIGN_LEADING);
|
||||
std::erase(buttons_not_shown, button);
|
||||
}
|
||||
|
||||
for (const auto& button : base::Reversed(trailing_buttons_)) {
|
||||
ConfigureButton(button, ALIGN_TRAILING);
|
||||
std::erase(buttons_not_shown, button);
|
||||
}
|
||||
|
||||
for (const auto& button_id : buttons_not_shown)
|
||||
HideButton(button_id);
|
||||
}
|
||||
|
||||
void OpaqueFrameView::LayoutWindowControlsOverlay() {
|
||||
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 =
|
||||
window()->IsMaximized()
|
||||
? caption_button_placeholder_container_->size().height()
|
||||
: caption_button_placeholder_container_->size().height() + 1;
|
||||
}
|
||||
int overlay_width = caption_button_placeholder_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();
|
||||
}
|
||||
|
||||
views::Button* OpaqueFrameView::CreateButton(
|
||||
ViewID view_id,
|
||||
int accessibility_string_id,
|
||||
views::CaptionButtonIcon icon_type,
|
||||
int ht_component,
|
||||
const gfx::VectorIcon& icon_image,
|
||||
views::Button::PressedCallback callback) {
|
||||
views::FrameCaptionButton* button = new views::FrameCaptionButton(
|
||||
views::Button::PressedCallback(), icon_type, ht_component);
|
||||
button->SetImage(button->GetIcon(), views::FrameCaptionButton::Animate::kNo,
|
||||
icon_image);
|
||||
|
||||
button->SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
|
||||
button->SetCallback(std::move(callback));
|
||||
button->SetAccessibleName(l10n_util::GetStringUTF16(accessibility_string_id));
|
||||
button->SetID(view_id);
|
||||
AddChildView(button);
|
||||
|
||||
button->SetPaintToLayer();
|
||||
button->layer()->SetFillsBoundsOpaquely(false);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
gfx::Insets OpaqueFrameView::FrameBorderInsets(bool restored) const {
|
||||
return !restored && IsFrameCondensed() ? gfx::Insets()
|
||||
: RestoredFrameBorderInsets();
|
||||
}
|
||||
|
||||
int OpaqueFrameView::FrameTopBorderThickness(bool restored) const {
|
||||
int thickness = FrameBorderInsets(restored).top();
|
||||
if ((restored || !IsFrameCondensed()) && thickness > 0)
|
||||
thickness += NonClientExtraTopThickness();
|
||||
return thickness;
|
||||
}
|
||||
|
||||
OpaqueFrameView::TopAreaPadding OpaqueFrameView::GetTopAreaPadding(
|
||||
bool has_leading_buttons,
|
||||
bool has_trailing_buttons) const {
|
||||
const auto padding = FrameBorderInsets(false);
|
||||
return TopAreaPadding{padding.left(), padding.right()};
|
||||
}
|
||||
|
||||
bool OpaqueFrameView::IsFrameCondensed() const {
|
||||
return frame()->IsMaximized() || frame()->IsFullscreen();
|
||||
}
|
||||
|
||||
gfx::Insets OpaqueFrameView::RestoredFrameBorderInsets() const {
|
||||
return gfx::Insets();
|
||||
}
|
||||
|
||||
gfx::Insets OpaqueFrameView::RestoredFrameEdgeInsets() const {
|
||||
return gfx::Insets::TLBR(kTopFrameEdgeThickness, kSideFrameEdgeThickness,
|
||||
kSideFrameEdgeThickness, kSideFrameEdgeThickness);
|
||||
}
|
||||
|
||||
int OpaqueFrameView::NonClientExtraTopThickness() const {
|
||||
return kNonClientExtraTopThickness;
|
||||
}
|
||||
|
||||
int OpaqueFrameView::NonClientTopHeight(bool restored) const {
|
||||
// Adding 2px of vertical padding puts at least 1 px of space on the top and
|
||||
// bottom of the element.
|
||||
constexpr int kVerticalPadding = 2;
|
||||
const int icon_height = GetIconSize() + kVerticalPadding;
|
||||
const int caption_button_height = DefaultCaptionButtonY(restored) +
|
||||
kCaptionButtonHeight +
|
||||
kCaptionButtonBottomPadding;
|
||||
|
||||
int custom_height = window()->titlebar_overlay_height();
|
||||
return custom_height ? custom_height
|
||||
: std::max(icon_height, caption_button_height) +
|
||||
kContentEdgeShadowThickness;
|
||||
}
|
||||
|
||||
int OpaqueFrameView::CaptionButtonY(views::FrameButton button_id,
|
||||
bool restored) const {
|
||||
return DefaultCaptionButtonY(restored);
|
||||
}
|
||||
|
||||
int OpaqueFrameView::DefaultCaptionButtonY(bool restored) const {
|
||||
// Maximized buttons start at window top, since the window has no border. This
|
||||
// offset is for the image (the actual clickable bounds extend all the way to
|
||||
// the top to take Fitts' Law into account).
|
||||
const bool start_at_top_of_frame = !restored && IsFrameCondensed();
|
||||
return start_at_top_of_frame
|
||||
? FrameBorderInsets(false).top()
|
||||
: views::NonClientFrameView::kFrameShadowThickness;
|
||||
}
|
||||
|
||||
gfx::Insets OpaqueFrameView::FrameEdgeInsets(bool restored) const {
|
||||
return RestoredFrameEdgeInsets();
|
||||
}
|
||||
|
||||
int OpaqueFrameView::GetIconSize() const {
|
||||
// The icon never shrinks below 16 px on a side.
|
||||
const int kIconMinimumSize = 16;
|
||||
return std::max(gfx::FontList().GetHeight(), kIconMinimumSize);
|
||||
}
|
||||
|
||||
OpaqueFrameView::TopAreaPadding OpaqueFrameView::GetTopAreaPadding() const {
|
||||
return GetTopAreaPadding(!leading_buttons_.empty(),
|
||||
!trailing_buttons_.empty());
|
||||
}
|
||||
|
||||
SkColor OpaqueFrameView::GetFrameColor() const {
|
||||
return GetColorProvider()->GetColor(
|
||||
ShouldPaintAsActive() ? ui::kColorFrameActive : ui::kColorFrameInactive);
|
||||
}
|
||||
|
||||
void OpaqueFrameView::ConfigureButton(views::FrameButton button_id,
|
||||
ButtonAlignment alignment) {
|
||||
switch (button_id) {
|
||||
case views::FrameButton::kMinimize: {
|
||||
bool can_minimize = true; // delegate_->CanMinimize();
|
||||
if (can_minimize) {
|
||||
minimize_button_->SetVisible(true);
|
||||
SetBoundsForButton(button_id, minimize_button_, alignment);
|
||||
} else {
|
||||
HideButton(button_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case views::FrameButton::kMaximize: {
|
||||
bool can_maximize = true; // delegate_->CanMaximize();
|
||||
if (can_maximize) {
|
||||
// When the window is restored, we show a maximized button; otherwise,
|
||||
// we show a restore button.
|
||||
bool is_restored = !window()->IsMaximized() && !window()->IsMinimized();
|
||||
views::Button* invisible_button =
|
||||
is_restored ? restore_button_ : maximize_button_;
|
||||
invisible_button->SetVisible(false);
|
||||
|
||||
views::Button* visible_button =
|
||||
is_restored ? maximize_button_ : restore_button_;
|
||||
visible_button->SetVisible(true);
|
||||
SetBoundsForButton(button_id, visible_button, alignment);
|
||||
} else {
|
||||
HideButton(button_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case views::FrameButton::kClose: {
|
||||
close_button_->SetVisible(true);
|
||||
SetBoundsForButton(button_id, close_button_, alignment);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpaqueFrameView::HideButton(views::FrameButton button_id) {
|
||||
switch (button_id) {
|
||||
case views::FrameButton::kMinimize:
|
||||
minimize_button_->SetVisible(false);
|
||||
break;
|
||||
case views::FrameButton::kMaximize:
|
||||
restore_button_->SetVisible(false);
|
||||
maximize_button_->SetVisible(false);
|
||||
break;
|
||||
case views::FrameButton::kClose:
|
||||
close_button_->SetVisible(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OpaqueFrameView::SetBoundsForButton(views::FrameButton button_id,
|
||||
views::Button* button,
|
||||
ButtonAlignment alignment) {
|
||||
const int caption_y = CaptionButtonY(button_id, false);
|
||||
|
||||
// There should always be the same number of non-shadow pixels visible to the
|
||||
// side of the caption buttons. In maximized mode we extend buttons to the
|
||||
// screen top and the rightmost button to the screen right (or leftmost button
|
||||
// to the screen left, for left-aligned buttons) to obey Fitts' Law.
|
||||
const bool is_frame_condensed = IsFrameCondensed();
|
||||
|
||||
const int button_width = views::GetCaptionButtonWidth();
|
||||
|
||||
gfx::Size button_size = button->GetPreferredSize();
|
||||
DCHECK_EQ(std::string(views::FrameCaptionButton::kViewClassName),
|
||||
button->GetClassName());
|
||||
const int caption_button_center_size =
|
||||
button_width - 2 * views::kCaptionButtonInkDropDefaultCornerRadius;
|
||||
const int height = GetTopAreaHeight() - FrameEdgeInsets(false).top();
|
||||
const int corner_radius =
|
||||
std::clamp((height - caption_button_center_size) / 2, 0,
|
||||
views::kCaptionButtonInkDropDefaultCornerRadius);
|
||||
button_size = gfx::Size(button_width, height);
|
||||
button->SetPreferredSize(button_size);
|
||||
static_cast<views::FrameCaptionButton*>(button)->SetInkDropCornerRadius(
|
||||
corner_radius);
|
||||
|
||||
TopAreaPadding top_area_padding = GetTopAreaPadding();
|
||||
|
||||
switch (alignment) {
|
||||
case ALIGN_LEADING: {
|
||||
int extra_width = top_area_padding.leading;
|
||||
int button_start_spacing =
|
||||
GetWindowCaptionSpacing(button_id, true, !placed_leading_button_);
|
||||
|
||||
available_space_leading_x_ += button_start_spacing;
|
||||
minimum_size_for_buttons_ += button_start_spacing;
|
||||
|
||||
bool top_spacing_clickable = is_frame_condensed;
|
||||
bool start_spacing_clickable =
|
||||
is_frame_condensed && !placed_leading_button_;
|
||||
button->SetBounds(
|
||||
available_space_leading_x_ - (start_spacing_clickable
|
||||
? button_start_spacing + extra_width
|
||||
: 0),
|
||||
top_spacing_clickable ? 0 : caption_y,
|
||||
button_size.width() + (start_spacing_clickable
|
||||
? button_start_spacing + extra_width
|
||||
: 0),
|
||||
button_size.height() + (top_spacing_clickable ? caption_y : 0));
|
||||
|
||||
int button_end_spacing =
|
||||
GetWindowCaptionSpacing(button_id, false, !placed_leading_button_);
|
||||
available_space_leading_x_ += button_size.width() + button_end_spacing;
|
||||
minimum_size_for_buttons_ += button_size.width() + button_end_spacing;
|
||||
placed_leading_button_ = true;
|
||||
break;
|
||||
}
|
||||
case ALIGN_TRAILING: {
|
||||
int extra_width = top_area_padding.trailing;
|
||||
int button_start_spacing =
|
||||
GetWindowCaptionSpacing(button_id, true, !placed_trailing_button_);
|
||||
|
||||
available_space_trailing_x_ -= button_start_spacing;
|
||||
minimum_size_for_buttons_ += button_start_spacing;
|
||||
|
||||
bool top_spacing_clickable = is_frame_condensed;
|
||||
bool start_spacing_clickable =
|
||||
is_frame_condensed && !placed_trailing_button_;
|
||||
button->SetBounds(
|
||||
available_space_trailing_x_ - button_size.width(),
|
||||
top_spacing_clickable ? 0 : caption_y,
|
||||
button_size.width() + (start_spacing_clickable
|
||||
? button_start_spacing + extra_width
|
||||
: 0),
|
||||
button_size.height() + (top_spacing_clickable ? caption_y : 0));
|
||||
|
||||
int button_end_spacing =
|
||||
GetWindowCaptionSpacing(button_id, false, !placed_trailing_button_);
|
||||
available_space_trailing_x_ -= button_size.width() + button_end_spacing;
|
||||
minimum_size_for_buttons_ += button_size.width() + button_end_spacing;
|
||||
placed_trailing_button_ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int OpaqueFrameView::GetTopAreaHeight() const {
|
||||
int top_height = NonClientTopHeight(false);
|
||||
return top_height;
|
||||
}
|
||||
|
||||
int OpaqueFrameView::GetWindowCaptionSpacing(views::FrameButton button_id,
|
||||
bool leading_spacing,
|
||||
bool is_leading_button) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
BEGIN_METADATA(OpaqueFrameView)
|
||||
END_METADATA
|
||||
|
||||
} // namespace electron
|
205
shell/browser/ui/views/opaque_frame_view.h
Normal file
205
shell/browser/ui/views/opaque_frame_view.h
Normal file
|
@ -0,0 +1,205 @@
|
|||
// Copyright 2024 Microsoft GmbH.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_BROWSER_UI_VIEWS_OPAQUE_FRAME_VIEW_H_
|
||||
#define ELECTRON_SHELL_BROWSER_UI_VIEWS_OPAQUE_FRAME_VIEW_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/scoped_observation.h"
|
||||
#include "chrome/browser/ui/view_ids.h"
|
||||
#include "shell/browser/ui/views/frameless_view.h"
|
||||
#include "ui/base/metadata/metadata_header_macros.h"
|
||||
#include "ui/gfx/font_list.h"
|
||||
#include "ui/linux/nav_button_provider.h"
|
||||
#include "ui/linux/window_button_order_observer.h"
|
||||
#include "ui/views/controls/button/button.h"
|
||||
#include "ui/views/window/caption_button_types.h"
|
||||
#include "ui/views/window/frame_buttons.h"
|
||||
#include "ui/views/window/non_client_view.h"
|
||||
|
||||
class CaptionButtonPlaceholderContainer;
|
||||
|
||||
namespace electron {
|
||||
|
||||
class OpaqueFrameView : public FramelessView {
|
||||
METADATA_HEADER(OpaqueFrameView, FramelessView)
|
||||
|
||||
public:
|
||||
// Constants used by OpaqueBrowserFrameView as well.
|
||||
static const int kContentEdgeShadowThickness;
|
||||
|
||||
static constexpr int kNonClientExtraTopThickness = 1;
|
||||
|
||||
OpaqueFrameView();
|
||||
~OpaqueFrameView() override;
|
||||
|
||||
// FramelessView:
|
||||
void Init(NativeWindowViews* window, views::Widget* frame) override;
|
||||
int ResizingBorderHitTest(const gfx::Point& point) override;
|
||||
void InvalidateCaptionButtons() override;
|
||||
|
||||
// views::NonClientFrameView:
|
||||
gfx::Rect GetBoundsForClientView() const override;
|
||||
gfx::Rect GetWindowBoundsForClientBounds(
|
||||
const gfx::Rect& client_bounds) const override;
|
||||
int NonClientHitTest(const gfx::Point& point) override;
|
||||
void ResetWindowControls() override;
|
||||
views::View* TargetForRect(views::View* root, const gfx::Rect& rect) override;
|
||||
|
||||
// views::View:
|
||||
void Layout(PassKey) override;
|
||||
void OnPaint(gfx::Canvas* canvas) override;
|
||||
|
||||
private:
|
||||
enum ButtonAlignment { ALIGN_LEADING, ALIGN_TRAILING };
|
||||
|
||||
struct TopAreaPadding {
|
||||
int leading;
|
||||
int trailing;
|
||||
};
|
||||
|
||||
void PaintAsActiveChanged();
|
||||
|
||||
void UpdateCaptionButtonPlaceholderContainerBackground();
|
||||
void UpdateFrameCaptionButtons();
|
||||
void LayoutWindowControls();
|
||||
void LayoutWindowControlsOverlay();
|
||||
|
||||
// Creates and returns an ImageButton with |this| as its listener.
|
||||
// Memory is owned by the caller.
|
||||
views::Button* CreateButton(ViewID view_id,
|
||||
int accessibility_string_id,
|
||||
views::CaptionButtonIcon icon_type,
|
||||
int ht_component,
|
||||
const gfx::VectorIcon& icon_image,
|
||||
views::Button::PressedCallback callback);
|
||||
|
||||
/** Layout-Related Utility Functions **/
|
||||
|
||||
// Returns the insets from the native window edge to the client view.
|
||||
// This does not include any client edge. If |restored| is true, this
|
||||
// is calculated as if the window was restored, regardless of its
|
||||
// current node_data.
|
||||
gfx::Insets FrameBorderInsets(bool restored) const;
|
||||
|
||||
// Returns the thickness of the border that makes up the window frame edge
|
||||
// along the top of the frame. If |restored| is true, this acts as if the
|
||||
// window is restored regardless of the actual mode.
|
||||
int FrameTopBorderThickness(bool restored) const;
|
||||
|
||||
// Returns the spacing between the edge of the browser window and the first
|
||||
// frame buttons.
|
||||
TopAreaPadding GetTopAreaPadding(bool has_leading_buttons,
|
||||
bool has_trailing_buttons) const;
|
||||
|
||||
// Determines whether the top frame is condensed vertically, as when the
|
||||
// window is maximized. If true, the top frame is just the height of a tab,
|
||||
// rather than having extra vertical space above the tabs.
|
||||
bool IsFrameCondensed() const;
|
||||
|
||||
// The insets from the native window edge to the client view when the window
|
||||
// is restored. This goes all the way to the web contents on the left, right,
|
||||
// and bottom edges.
|
||||
gfx::Insets RestoredFrameBorderInsets() const;
|
||||
|
||||
// The insets from the native window edge to the flat portion of the
|
||||
// window border. That is, this function returns the "3D portion" of the
|
||||
// border when the window is restored. The returned insets will not be larger
|
||||
// than RestoredFrameBorderInsets().
|
||||
gfx::Insets RestoredFrameEdgeInsets() const;
|
||||
|
||||
// Additional vertical padding between tabs and the top edge of the window
|
||||
// when the window is restored.
|
||||
int NonClientExtraTopThickness() const;
|
||||
|
||||
// Returns the height of the entire nonclient top border, from the edge of the
|
||||
// window to the top of the tabs. If |restored| is true, this is calculated as
|
||||
// if the window was restored, regardless of its current state.
|
||||
int NonClientTopHeight(bool restored) const;
|
||||
|
||||
// Returns the y-coordinate of button |button_id|. If |restored| is true,
|
||||
// acts as if the window is restored regardless of the real mode.
|
||||
int CaptionButtonY(views::FrameButton button_id, bool restored) const;
|
||||
|
||||
// Returns the y-coordinate of the caption button when native frame buttons
|
||||
// are disabled. If |restored| is true, acts as if the window is restored
|
||||
// regardless of the real mode.
|
||||
int DefaultCaptionButtonY(bool restored) const;
|
||||
|
||||
// Returns the insets from the native window edge to the flat portion of the
|
||||
// window border. That is, this function returns the "3D portion" of the
|
||||
// border. If |restored| is true, acts as if the window is restored
|
||||
// regardless of the real mode.
|
||||
gfx::Insets FrameEdgeInsets(bool restored) const;
|
||||
|
||||
// Returns the size of the window icon. This can be platform dependent
|
||||
// because of differences in fonts.
|
||||
int GetIconSize() const;
|
||||
|
||||
// Returns the spacing between the edge of the browser window and the first
|
||||
// frame buttons.
|
||||
TopAreaPadding GetTopAreaPadding() const;
|
||||
|
||||
// Returns the color of the frame.
|
||||
SkColor GetFrameColor() const;
|
||||
|
||||
// Initializes the button with |button_id| to be aligned according to
|
||||
// |alignment|.
|
||||
void ConfigureButton(views::FrameButton button_id, ButtonAlignment alignment);
|
||||
|
||||
// Sets the visibility of all buttons associated with |button_id| to false.
|
||||
void HideButton(views::FrameButton button_id);
|
||||
|
||||
// Adds a window caption button to either the leading or trailing side.
|
||||
void SetBoundsForButton(views::FrameButton button_id,
|
||||
views::Button* button,
|
||||
ButtonAlignment alignment);
|
||||
|
||||
// Computes the height of the top area of the frame.
|
||||
int GetTopAreaHeight() const;
|
||||
|
||||
// Returns the margin around button |button_id|. If |leading_spacing| is
|
||||
// true, returns the left margin (in RTL), otherwise returns the right margin
|
||||
// (in RTL). Extra margin may be added if |is_leading_button| is true.
|
||||
int GetWindowCaptionSpacing(views::FrameButton button_id,
|
||||
bool leading_spacing,
|
||||
bool is_leading_button) const;
|
||||
|
||||
// Window controls.
|
||||
raw_ptr<views::Button> minimize_button_;
|
||||
raw_ptr<views::Button> maximize_button_;
|
||||
raw_ptr<views::Button> restore_button_;
|
||||
raw_ptr<views::Button> close_button_;
|
||||
|
||||
// The leading and trailing x positions of the empty space available for
|
||||
// laying out titlebar elements.
|
||||
int available_space_leading_x_ = 0;
|
||||
int available_space_trailing_x_ = 0;
|
||||
|
||||
// Whether any of the window control buttons were packed on the leading or
|
||||
// trailing sides. This state is only valid while layout is being performed.
|
||||
bool placed_leading_button_ = false;
|
||||
bool placed_trailing_button_ = false;
|
||||
|
||||
// The size of the window buttons. This does not count labels or other
|
||||
// elements that should be counted in a minimal frame.
|
||||
int minimum_size_for_buttons_ = 0;
|
||||
|
||||
std::vector<views::FrameButton> leading_buttons_;
|
||||
std::vector<views::FrameButton> trailing_buttons_{
|
||||
views::FrameButton::kMinimize, views::FrameButton::kMaximize,
|
||||
views::FrameButton::kClose};
|
||||
|
||||
base::CallbackListSubscription paint_as_active_changed_subscription_;
|
||||
|
||||
// PlaceholderContainer beneath the controls button for WCO.
|
||||
raw_ptr<CaptionButtonPlaceholderContainer>
|
||||
caption_button_placeholder_container_;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // ELECTRON_SHELL_BROWSER_UI_VIEWS_OPAQUE_FRAME_VIEW_H_
|
|
@ -37,8 +37,6 @@ void WinFrameView::Init(NativeWindowViews* window, views::Widget* frame) {
|
|||
if (window->IsWindowControlsOverlayEnabled()) {
|
||||
caption_button_container_ =
|
||||
AddChildView(std::make_unique<WinCaptionButtonContainer>(this));
|
||||
} else {
|
||||
caption_button_container_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,24 +80,23 @@ views::View* WinFrameView::TargetForRect(views::View* root,
|
|||
// Custom system titlebar returns non HTCLIENT value, however event should
|
||||
// be handled by the view, not by the system, because there are no system
|
||||
// buttons underneath.
|
||||
if (!ShouldCustomDrawSystemTitlebar()) {
|
||||
if (!window()->IsWindowControlsOverlayEnabled())
|
||||
return this;
|
||||
}
|
||||
|
||||
auto local_point = rect.origin();
|
||||
ConvertPointToTarget(parent(), caption_button_container_, &local_point);
|
||||
if (!caption_button_container_->HitTestPoint(local_point)) {
|
||||
if (!caption_button_container_->HitTestPoint(local_point))
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
return NonClientFrameView::TargetForRect(root, rect);
|
||||
}
|
||||
|
||||
int WinFrameView::NonClientHitTest(const gfx::Point& point) {
|
||||
if (window_->has_frame())
|
||||
if (window()->has_frame())
|
||||
return frame_->client_view()->NonClientHitTest(point);
|
||||
|
||||
if (ShouldCustomDrawSystemTitlebar()) {
|
||||
if (window()->IsWindowControlsOverlayEnabled()) {
|
||||
// See if the point is within any of the window controls.
|
||||
if (caption_button_container_) {
|
||||
gfx::Point local_point = point;
|
||||
|
@ -168,10 +165,6 @@ bool WinFrameView::IsMaximized() const {
|
|||
return frame()->IsMaximized();
|
||||
}
|
||||
|
||||
bool WinFrameView::ShouldCustomDrawSystemTitlebar() const {
|
||||
return window()->IsWindowControlsOverlayEnabled();
|
||||
}
|
||||
|
||||
void WinFrameView::Layout(PassKey) {
|
||||
LayoutCaptionButtons();
|
||||
if (window()->IsWindowControlsOverlayEnabled()) {
|
||||
|
@ -197,7 +190,7 @@ int WinFrameView::FrameTopBorderThicknessPx(bool restored) const {
|
|||
|
||||
// See comments in BrowserDesktopWindowTreeHostWin::GetClientAreaInsets().
|
||||
const bool needs_no_border =
|
||||
(ShouldCustomDrawSystemTitlebar() && frame()->IsMaximized()) ||
|
||||
(window()->IsWindowControlsOverlayEnabled() && frame()->IsMaximized()) ||
|
||||
frame()->IsFullscreen();
|
||||
if (needs_no_border && !restored)
|
||||
return 0;
|
||||
|
@ -245,7 +238,7 @@ void WinFrameView::LayoutCaptionButtons() {
|
|||
return;
|
||||
|
||||
// Non-custom system titlebar already contains caption buttons.
|
||||
if (!ShouldCustomDrawSystemTitlebar()) {
|
||||
if (!window()->IsWindowControlsOverlayEnabled()) {
|
||||
caption_button_container_->SetVisible(false);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ class WinFrameView : public FramelessView {
|
|||
~WinFrameView() override;
|
||||
|
||||
void Init(NativeWindowViews* window, views::Widget* frame) override;
|
||||
void InvalidateCaptionButtons() 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.
|
||||
|
@ -33,24 +34,17 @@ class WinFrameView : public FramelessView {
|
|||
|
||||
SkColor GetReadableFeatureColor(SkColor background_color);
|
||||
|
||||
// Tells the NonClientView to invalidate the WinFrameView's caption buttons.
|
||||
void InvalidateCaptionButtons();
|
||||
|
||||
// views::NonClientFrameView:
|
||||
gfx::Rect GetWindowBoundsForClientBounds(
|
||||
const gfx::Rect& client_bounds) const override;
|
||||
int NonClientHitTest(const gfx::Point& point) override;
|
||||
|
||||
NativeWindowViews* window() const { return window_; }
|
||||
views::Widget* frame() const { return frame_; }
|
||||
WinCaptionButtonContainer* caption_button_container() {
|
||||
return caption_button_container_;
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -89,7 +83,7 @@ class WinFrameView : public FramelessView {
|
|||
// 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!
|
||||
raw_ptr<WinCaptionButtonContainer> caption_button_container_;
|
||||
raw_ptr<WinCaptionButtonContainer> caption_button_container_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue