mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
* chore: bump chromium in DEPS to 142.0.7432.2 * chore: bump chromium in DEPS to 142.0.7434.1 * chore: bump chromium in DEPS to 142.0.7436.1 * chore: bump chromium in DEPS to 142.0.7438.1 * chore: bump chromium in DEPS to 142.0.7440.1 * chore: bump chromium in DEPS to 142.0.7442.1 * chore: bump chromium in DEPS to 142.0.7444.1 * chore: bump chromium in DEPS to 142.0.7444.6 * chore: bump chromium in DEPS to 142.0.7444.3 * 6973697: Use type tags for data stored in V8 internal fields https://chromium-review.googlesource.com/c/chromium/src/+/6973697 * chore: update patches * chore: update filenames.libcxx.gni * fix: parse macOS SDK version across line break https://chromium-review.googlesource.com/c/chromium/src/+/6980166 (cherry picked from commit2bcbb33de0) * fix: replace v8::Object::SetPrototype() usage https://chromium-review.googlesource.com/c/v8/v8/+/6983465 https://github.com/nodejs/node/pull/55453 (cherry picked from commitc31b9ed5ac) * fix: replace additional usages of SetPrototype https://chromium-review.googlesource.com/c/v8/v8/+/6983465 (cherry picked from commit bf151e9d28520c7dd74cba62240acbcaaab5433d) * fixup! fix: replace additional usages of SetPrototype https://chromium-review.googlesource.com/c/v8/v8/+/6983465 (cherry picked from commit f4434755b82b098e4d83d42bab26f183b6824f99) * build: use macos 15 minimum https://chromium-review.googlesource.com/c/chromium/src/+/6980166 (cherry picked from commit 4d2b5d7b2cf9a1786cdb1a77bf73e4ad0d3e45d1) * fixup! build: use macos 15 minimum https://chromium-review.googlesource.com/c/chromium/src/+/6980166 (cherry picked from commit 94bb41a66330dcaf6b92c80cfefd72759405793d) * ci: ignore missing dir for strip_universal_deep (cherry picked from commit 634963f171bc5f6050151f76973e7ffbab0e00cf) * chore: update patches * chore: update patches * chore: bump chromium in DEPS to 142.0.7444.23 * fix: disable C++ modules in electron_lib builds https://chromium-review.googlesource.com/c/chromium/src/+/6950738 (cherry picked from commit6207c79aec) * Revert "build: use macos 15 minimum" This reverts commit2fc12d6acc. Initially this change was made to test if it fixes libcxx compilation issues. As that's now resolved by disabling libcxx modules, this can be reverted. (cherry picked from commitad52007d5b) * fix: js2c compilation failure https://chromium-review.googlesource.com/c/chromium/src/+/6950738 See patch description explaining MacOS 26 SDK headers incompatibility. (cherry picked from commit39e2470875) * fix: disable C++ modules in libnode builds (cherry picked from commitfd0a7b61a1) * fixup! fix: replace v8::Object::SetPrototype() usage https://chromium-review.googlesource.com/c/v8/v8/+/6983465 https://github.com/nodejs/node/pull/55453 (cherry picked from commit2f52159b71) * build: switch to macos-15 runner build/mac/find_sdk.py now requires macOS 15 SDK as a minimum version. The macos 15 runners default to an Xcode using the 15 SDK and removes older versions. (cherry picked from commite368703f24) * chore: update patches * fix: partially revert is_headless_mode removal https://chromium-review.googlesource.com/c/chromium/src/+/6955633 This patch should likely be reworked. For now, this partially reverts the removal of a required class property to restore behavior. (cherry picked from commitaff3bf9a24) * 6938086: Rename native_widget_types.h -> native_ui_types.h | https://chromium-review.googlesource.com/c/chromium/src/+/6938086 (cherry picked from commitc95ac7bf2b) * 6973697: Use type tags for data stored in V8 internal fields https://chromium-review.googlesource.com/c/chromium/src/+/6973697 * fixup! fix: check new forced colors enum value https://chromium-review.googlesource.com/c/chromium/src/+/6944403 (cherry picked from commit0829c74b2f) * fix: check new forced colors enum value https://chromium-review.googlesource.com/c/chromium/src/+/6944403 (cherry picked from commitd585879807) * feat: add new memory-eviction exit reason https://chromium-review.googlesource.com/c/chromium/src/+/6991933 (cherry picked from commit6e63197a22) * fix: views::NonClientFrameView -> views::FrameView https://chromium-review.googlesource.com/c/chromium/src/+/7005027 https://chromium-review.googlesource.com/c/chromium/src/+/6966937 (cherry picked from commit1e86b6ddfb) * fix: migrate NetworkConditions -> MatchedNetworkConditions https://chromium-review.googlesource.com/c/chromium/src/+/6827307 (cherry picked from commit97100ac168) * fix: provide DeviceEmulationCacheBehavior param https://chromium-review.googlesource.com/c/chromium/src/+/6965238 (cherry picked from commitf9a08c5384) * fix: add missing image_skia include https://chromium-review.googlesource.com/c/chromium/src/+/6986762 (cherry picked from commitdd5eaf03fd) * fixup! fix: add missing image_skia include https://chromium-review.googlesource.com/c/chromium/src/+/6986762 (cherry picked from commit249c4d4de1) * 6948286: [wasm-imported-strings] Drop feature flag https://chromium-review.googlesource.com/c/v8/v8/+/6948286 * fix: disable protocol handler DCHECK https://chromium-review.googlesource.com/c/chromium/src/+/6727594 Ignore the extension custom protocol handler registry DCHECK until we invest in supporting it. Replacing this DCHECK seems harmless and will unblock the roll. (cherry picked from commit019d3f0b09) * 6986762: Remove some includes of //ui/gfx/image/image_skia.h https: //chromium-review.googlesource.com/c/chromium/src/+/6986762 * fixup! fix: migrate NetworkConditions -> MatchedNetworkConditions https://chromium-review.googlesource.com/c/chromium/src/+/6827307 (cherry picked from commita8f67f1ac3) * fixup: 6986762: Remove some includes of //ui/gfx/image/image_skia.h s * fix: replace deprecated usage of SetPrototype https://chromium-review.googlesource.com/c/v8/v8/+/6983465 (cherry picked from commit5435d87b40) * chore: restore electron embedder data tag patch Co-Authored-By: Sam Maddock <sam@samuelmaddock.com> * chore: update patches --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org> Co-authored-by: Samuel Maddock <smaddock@slack-corp.com> Co-authored-by: Alice Zhao <alicelovescake@anthropic.com> Co-authored-by: Sam Maddock <sam@samuelmaddock.com>
562 lines
21 KiB
C++
562 lines
21 KiB
C++
// 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/browser/ui/views/frame/opaque_browser_frame_view_layout.h" // nogncheck
|
|
#include "chrome/grit/generated_resources.h"
|
|
#include "components/strings/grit/components_strings.h"
|
|
#include "shell/browser/native_window_views.h"
|
|
#include "shell/browser/ui/views/caption_button_placeholder_container.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/gfx/font_list.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()) {
|
|
// Ensure support for resizing frameless window with border drag.
|
|
int frame_component = ResizingBorderHitTest(point);
|
|
if (frame_component != HTNOWHERE)
|
|
return frame_component;
|
|
|
|
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() {
|
|
FrameView::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::FrameView::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);
|
|
|
|
// We do not want to show the buttons in fullscreen mode.
|
|
if (!frame()->IsFullscreen()) {
|
|
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) {
|
|
auto button = std::make_unique<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);
|
|
|
|
button->SetPaintToLayer();
|
|
button->layer()->SetFillsBoundsOpaquely(false);
|
|
|
|
return AddChildView(std::move(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 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()
|
|
: OpaqueBrowserFrameViewLayout::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: {
|
|
if (window()->IsMinimizable()) {
|
|
minimize_button_->SetVisible(true);
|
|
SetBoundsForButton(button_id, minimize_button_, alignment);
|
|
} else {
|
|
HideButton(button_id);
|
|
}
|
|
break;
|
|
}
|
|
case views::FrameButton::kMaximize: {
|
|
if (window()->IsMaximizable()) {
|
|
// 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: {
|
|
if (window()->IsClosable()) {
|
|
close_button_->SetVisible(true);
|
|
SetBoundsForButton(button_id, close_button_, alignment);
|
|
} else {
|
|
HideButton(button_id);
|
|
}
|
|
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
|