mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05: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>
508 lines
17 KiB
C++
508 lines
17 KiB
C++
// Copyright (c) 2021 Ryan Gonzalez.
|
|
// Use of this source code is governed by the MIT license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "shell/browser/ui/views/client_frame_view_linux.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "base/strings/utf_string_conversions.h"
|
|
#include "cc/paint/paint_filter.h"
|
|
#include "cc/paint/paint_flags.h"
|
|
#include "shell/browser/native_window_views.h"
|
|
#include "shell/browser/ui/electron_desktop_window_tree_host_linux.h"
|
|
#include "shell/browser/ui/views/frameless_view.h"
|
|
#include "ui/base/hit_test.h"
|
|
#include "ui/base/l10n/l10n_util.h"
|
|
#include "ui/base/metadata/metadata_impl_macros.h"
|
|
#include "ui/base/models/image_model.h"
|
|
#include "ui/gfx/canvas.h"
|
|
#include "ui/gfx/font_list.h"
|
|
#include "ui/gfx/geometry/insets.h"
|
|
#include "ui/gfx/geometry/rect.h"
|
|
#include "ui/gfx/geometry/skia_conversions.h"
|
|
#include "ui/gfx/text_constants.h"
|
|
#include "ui/gtk/gtk_compat.h" // nogncheck
|
|
#include "ui/gtk/gtk_util.h" // nogncheck
|
|
#include "ui/linux/linux_ui.h"
|
|
#include "ui/linux/nav_button_provider.h"
|
|
#include "ui/native_theme/native_theme.h"
|
|
#include "ui/strings/grit/ui_strings.h"
|
|
#include "ui/views/controls/button/image_button.h"
|
|
#include "ui/views/style/typography.h"
|
|
#include "ui/views/widget/widget.h"
|
|
#include "ui/views/window/frame_buttons.h"
|
|
#include "ui/views/window/window_button_order_provider.h"
|
|
|
|
namespace electron {
|
|
|
|
namespace {
|
|
|
|
// These values should be the same as Chromium uses.
|
|
constexpr int kResizeBorder = 10;
|
|
constexpr int kResizeInsideBoundsSize = 5;
|
|
|
|
ui::NavButtonProvider::ButtonState ButtonStateToNavButtonProviderState(
|
|
views::Button::ButtonState state) {
|
|
switch (state) {
|
|
case views::Button::STATE_NORMAL:
|
|
return ui::NavButtonProvider::ButtonState::kNormal;
|
|
case views::Button::STATE_HOVERED:
|
|
return ui::NavButtonProvider::ButtonState::kHovered;
|
|
case views::Button::STATE_PRESSED:
|
|
return ui::NavButtonProvider::ButtonState::kPressed;
|
|
case views::Button::STATE_DISABLED:
|
|
return ui::NavButtonProvider::ButtonState::kDisabled;
|
|
|
|
case views::Button::STATE_COUNT:
|
|
default:
|
|
NOTREACHED();
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
ClientFrameViewLinux::ClientFrameViewLinux()
|
|
: theme_(ui::NativeTheme::GetInstanceForNativeUi()),
|
|
nav_button_provider_(
|
|
ui::LinuxUiTheme::GetForProfile(nullptr)->CreateNavButtonProvider()),
|
|
nav_buttons_{
|
|
NavButton{ui::NavButtonProvider::FrameButtonDisplayType::kClose,
|
|
views::FrameButton::kClose, &views::Widget::Close,
|
|
IDS_APP_ACCNAME_CLOSE, HTCLOSE},
|
|
NavButton{ui::NavButtonProvider::FrameButtonDisplayType::kMaximize,
|
|
views::FrameButton::kMaximize, &views::Widget::Maximize,
|
|
IDS_APP_ACCNAME_MAXIMIZE, HTMAXBUTTON},
|
|
NavButton{ui::NavButtonProvider::FrameButtonDisplayType::kRestore,
|
|
views::FrameButton::kMaximize, &views::Widget::Restore,
|
|
IDS_APP_ACCNAME_RESTORE, HTMAXBUTTON},
|
|
NavButton{ui::NavButtonProvider::FrameButtonDisplayType::kMinimize,
|
|
views::FrameButton::kMinimize, &views::Widget::Minimize,
|
|
IDS_APP_ACCNAME_MINIMIZE, HTMINBUTTON},
|
|
},
|
|
trailing_frame_buttons_{views::FrameButton::kMinimize,
|
|
views::FrameButton::kMaximize,
|
|
views::FrameButton::kClose} {
|
|
for (auto& button : nav_buttons_) {
|
|
auto image_button = std::make_unique<views::ImageButton>();
|
|
image_button->SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE);
|
|
image_button->SetAccessibleName(
|
|
l10n_util::GetStringUTF16(button.accessibility_id));
|
|
button.button = AddChildView(std::move(image_button));
|
|
}
|
|
|
|
auto title = std::make_unique<views::Label>();
|
|
title->SetSubpixelRenderingEnabled(false);
|
|
title->SetAutoColorReadabilityEnabled(false);
|
|
title->SetHorizontalAlignment(gfx::ALIGN_CENTER);
|
|
title->SetVerticalAlignment(gfx::ALIGN_MIDDLE);
|
|
title->SetTextStyle(views::style::STYLE_TAB_ACTIVE);
|
|
title_ = AddChildView(std::move(title));
|
|
|
|
native_theme_observer_.Observe(theme_);
|
|
|
|
if (auto* ui = ui::LinuxUi::instance()) {
|
|
ui->AddWindowButtonOrderObserver(this);
|
|
OnWindowButtonOrderingChange();
|
|
}
|
|
}
|
|
|
|
ClientFrameViewLinux::~ClientFrameViewLinux() {
|
|
if (auto* ui = ui::LinuxUi::instance())
|
|
ui->RemoveWindowButtonOrderObserver(this);
|
|
theme_->RemoveObserver(this);
|
|
}
|
|
|
|
void ClientFrameViewLinux::Init(NativeWindowViews* window,
|
|
views::Widget* frame) {
|
|
FramelessView::Init(window, frame);
|
|
|
|
// 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(
|
|
&ClientFrameViewLinux::PaintAsActiveChanged, base::Unretained(this)));
|
|
|
|
auto* tree_host = static_cast<ElectronDesktopWindowTreeHostLinux*>(
|
|
ElectronDesktopWindowTreeHostLinux::GetHostForWidget(
|
|
window->GetAcceleratedWidget()));
|
|
host_supports_client_frame_shadow_ = tree_host->SupportsClientFrameShadow();
|
|
|
|
UpdateWindowTitle();
|
|
|
|
for (auto& button : nav_buttons_) {
|
|
// Unretained() is safe because the buttons are added as children to, and
|
|
// thus owned by, this view. Thus, the buttons themselves will be destroyed
|
|
// when this view is destroyed, and the frame's life must never outlive the
|
|
// view.
|
|
button.button->SetCallback(
|
|
base::BindRepeating(button.callback, base::Unretained(frame)));
|
|
}
|
|
|
|
UpdateThemeValues();
|
|
}
|
|
|
|
gfx::Insets ClientFrameViewLinux::RestoredMirroredFrameBorderInsets() const {
|
|
auto border = RestoredFrameBorderInsets();
|
|
return base::i18n::IsRTL() ? gfx::Insets::TLBR(border.top(), border.right(),
|
|
border.bottom(), border.left())
|
|
: border;
|
|
}
|
|
|
|
gfx::Insets ClientFrameViewLinux::RestoredFrameBorderInsets() const {
|
|
gfx::Insets insets = GetFrameProvider()->GetFrameThicknessDip();
|
|
insets.SetToMax(GetInputInsets());
|
|
return insets;
|
|
}
|
|
|
|
gfx::Insets ClientFrameViewLinux::GetInputInsets() const {
|
|
bool showing_shadow = host_supports_client_frame_shadow_ &&
|
|
!frame_->IsMaximized() && !frame_->IsFullscreen();
|
|
return gfx::Insets(showing_shadow ? kResizeBorder : 0);
|
|
}
|
|
|
|
gfx::Rect ClientFrameViewLinux::GetWindowContentBounds() const {
|
|
gfx::Rect content_bounds = bounds();
|
|
content_bounds.Inset(RestoredMirroredFrameBorderInsets());
|
|
return content_bounds;
|
|
}
|
|
|
|
SkRRect ClientFrameViewLinux::GetRoundedWindowContentBounds() const {
|
|
SkRect rect = gfx::RectToSkRect(GetWindowContentBounds());
|
|
SkRRect rrect;
|
|
|
|
if (!frame_->IsMaximized()) {
|
|
SkPoint round_point{theme_values_.window_border_radius,
|
|
theme_values_.window_border_radius};
|
|
SkPoint radii[] = {round_point, round_point, {}, {}};
|
|
rrect.setRectRadii(rect, radii);
|
|
} else {
|
|
rrect.setRect(rect);
|
|
}
|
|
|
|
return rrect;
|
|
}
|
|
|
|
void ClientFrameViewLinux::OnNativeThemeUpdated(
|
|
ui::NativeTheme* observed_theme) {
|
|
UpdateThemeValues();
|
|
}
|
|
|
|
void ClientFrameViewLinux::OnWindowButtonOrderingChange() {
|
|
auto* provider = views::WindowButtonOrderProvider::GetInstance();
|
|
leading_frame_buttons_ = provider->leading_buttons();
|
|
trailing_frame_buttons_ = provider->trailing_buttons();
|
|
|
|
InvalidateLayout();
|
|
}
|
|
|
|
int ClientFrameViewLinux::ResizingBorderHitTest(const gfx::Point& point) {
|
|
return ResizingBorderHitTestImpl(point,
|
|
RestoredMirroredFrameBorderInsets() +
|
|
gfx::Insets(kResizeInsideBoundsSize));
|
|
}
|
|
|
|
gfx::Rect ClientFrameViewLinux::GetBoundsForClientView() const {
|
|
gfx::Rect client_bounds = bounds();
|
|
if (!frame_->IsFullscreen()) {
|
|
client_bounds.Inset(RestoredMirroredFrameBorderInsets());
|
|
client_bounds.Inset(
|
|
gfx::Insets::TLBR(GetTitlebarBounds().height(), 0, 0, 0));
|
|
}
|
|
return client_bounds;
|
|
}
|
|
|
|
gfx::Rect ClientFrameViewLinux::GetWindowBoundsForClientBounds(
|
|
const gfx::Rect& client_bounds) const {
|
|
gfx::Insets insets = bounds().InsetsFrom(GetBoundsForClientView());
|
|
return gfx::Rect(std::max(0, client_bounds.x() - insets.left()),
|
|
std::max(0, client_bounds.y() - insets.top()),
|
|
client_bounds.width() + insets.width(),
|
|
client_bounds.height() + insets.height());
|
|
}
|
|
|
|
int ClientFrameViewLinux::NonClientHitTest(const gfx::Point& point) {
|
|
int component = ResizingBorderHitTest(point);
|
|
if (component != HTNOWHERE) {
|
|
return component;
|
|
}
|
|
|
|
for (auto& button : nav_buttons_) {
|
|
if (button.button->GetVisible() &&
|
|
button.button->GetMirroredBounds().Contains(point)) {
|
|
return button.hit_test_id;
|
|
}
|
|
}
|
|
|
|
if (GetTitlebarBounds().Contains(point)) {
|
|
return HTCAPTION;
|
|
}
|
|
|
|
return FramelessView::NonClientHitTest(point);
|
|
}
|
|
|
|
ui::WindowFrameProvider* ClientFrameViewLinux::GetFrameProvider() const {
|
|
return ui::LinuxUiTheme::GetForProfile(nullptr)->GetWindowFrameProvider(
|
|
!host_supports_client_frame_shadow_, tiled(), frame_->IsMaximized());
|
|
}
|
|
|
|
void ClientFrameViewLinux::GetWindowMask(const gfx::Size& size,
|
|
SkPath* window_mask) {
|
|
// Nothing to do here, as transparency is used for decorations, not masks.
|
|
}
|
|
|
|
void ClientFrameViewLinux::UpdateWindowTitle() {
|
|
title_->SetText(base::UTF8ToUTF16(window_->GetTitle()));
|
|
}
|
|
|
|
void ClientFrameViewLinux::SizeConstraintsChanged() {
|
|
InvalidateLayout();
|
|
}
|
|
|
|
gfx::Size ClientFrameViewLinux::CalculatePreferredSize(
|
|
const views::SizeBounds& available_size) const {
|
|
return SizeWithDecorations(
|
|
FramelessView::CalculatePreferredSize(available_size));
|
|
}
|
|
|
|
gfx::Size ClientFrameViewLinux::GetMinimumSize() const {
|
|
return SizeWithDecorations(FramelessView::GetMinimumSize());
|
|
}
|
|
|
|
gfx::Size ClientFrameViewLinux::GetMaximumSize() const {
|
|
return SizeWithDecorations(FramelessView::GetMaximumSize());
|
|
}
|
|
|
|
void ClientFrameViewLinux::Layout(PassKey) {
|
|
LayoutSuperclass<FramelessView>(this);
|
|
|
|
if (frame_->IsFullscreen()) {
|
|
// Just hide everything and return.
|
|
for (NavButton& button : nav_buttons_) {
|
|
button.button->SetVisible(false);
|
|
}
|
|
|
|
title_->SetVisible(false);
|
|
return;
|
|
}
|
|
|
|
UpdateButtonImages();
|
|
LayoutButtons();
|
|
|
|
gfx::Rect title_bounds(GetTitlebarContentBounds());
|
|
title_bounds.Inset(theme_values_.title_padding);
|
|
|
|
title_->SetVisible(true);
|
|
title_->SetBoundsRect(title_bounds);
|
|
}
|
|
|
|
void ClientFrameViewLinux::OnPaint(gfx::Canvas* canvas) {
|
|
if (!frame_->IsFullscreen()) {
|
|
GetFrameProvider()->PaintWindowFrame(
|
|
canvas, GetLocalBounds(), GetTitlebarBounds().bottom(),
|
|
ShouldPaintAsActive(), GetInputInsets());
|
|
}
|
|
}
|
|
|
|
void ClientFrameViewLinux::PaintAsActiveChanged() {
|
|
UpdateThemeValues();
|
|
}
|
|
|
|
void ClientFrameViewLinux::UpdateThemeValues() {
|
|
gtk::GtkCssContext window_context =
|
|
gtk::AppendCssNodeToStyleContext({}, "window.background.csd");
|
|
gtk::GtkCssContext headerbar_context = gtk::AppendCssNodeToStyleContext(
|
|
{}, "headerbar.default-decoration.titlebar");
|
|
gtk::GtkCssContext title_context =
|
|
gtk::AppendCssNodeToStyleContext(headerbar_context, "label.title");
|
|
gtk::GtkCssContext button_context = gtk::AppendCssNodeToStyleContext(
|
|
headerbar_context, "button.image-button");
|
|
|
|
gtk_style_context_set_parent(headerbar_context, window_context);
|
|
gtk_style_context_set_parent(title_context, headerbar_context);
|
|
gtk_style_context_set_parent(button_context, headerbar_context);
|
|
|
|
// ShouldPaintAsActive asks the widget, so assume active if the widget is not
|
|
// set yet.
|
|
if (GetWidget() != nullptr && !ShouldPaintAsActive()) {
|
|
gtk_style_context_set_state(window_context, GTK_STATE_FLAG_BACKDROP);
|
|
gtk_style_context_set_state(headerbar_context, GTK_STATE_FLAG_BACKDROP);
|
|
gtk_style_context_set_state(title_context, GTK_STATE_FLAG_BACKDROP);
|
|
gtk_style_context_set_state(button_context, GTK_STATE_FLAG_BACKDROP);
|
|
}
|
|
|
|
theme_values_.window_border_radius =
|
|
GetFrameProvider()->GetTopCornerRadiusDip();
|
|
|
|
gtk::GtkStyleContextGet(headerbar_context, "min-height",
|
|
&theme_values_.titlebar_min_height, nullptr);
|
|
theme_values_.titlebar_padding =
|
|
gtk::GtkStyleContextGetPadding(headerbar_context);
|
|
|
|
theme_values_.title_color = gtk::GtkStyleContextGetColor(title_context);
|
|
theme_values_.title_padding = gtk::GtkStyleContextGetPadding(title_context);
|
|
|
|
gtk::GtkStyleContextGet(button_context, "min-height",
|
|
&theme_values_.button_min_size, nullptr);
|
|
theme_values_.button_padding = gtk::GtkStyleContextGetPadding(button_context);
|
|
|
|
title_->SetEnabledColor(theme_values_.title_color);
|
|
|
|
InvalidateLayout();
|
|
SchedulePaint();
|
|
}
|
|
|
|
ui::NavButtonProvider::FrameButtonDisplayType
|
|
ClientFrameViewLinux::GetButtonTypeToSkip() const {
|
|
return frame_->IsMaximized()
|
|
? ui::NavButtonProvider::FrameButtonDisplayType::kMaximize
|
|
: ui::NavButtonProvider::FrameButtonDisplayType::kRestore;
|
|
}
|
|
|
|
void ClientFrameViewLinux::UpdateButtonImages() {
|
|
nav_button_provider_->RedrawImages(theme_values_.button_min_size,
|
|
frame_->IsMaximized(),
|
|
ShouldPaintAsActive());
|
|
|
|
ui::NavButtonProvider::FrameButtonDisplayType skip_type =
|
|
GetButtonTypeToSkip();
|
|
|
|
for (NavButton& button : nav_buttons_) {
|
|
if (button.type == skip_type) {
|
|
continue;
|
|
}
|
|
|
|
for (size_t state_id = 0; state_id < views::Button::STATE_COUNT;
|
|
state_id++) {
|
|
views::Button::ButtonState state =
|
|
static_cast<views::Button::ButtonState>(state_id);
|
|
button.button->SetImageModel(
|
|
state, ui::ImageModel::FromImageSkia(nav_button_provider_->GetImage(
|
|
button.type, ButtonStateToNavButtonProviderState(state))));
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClientFrameViewLinux::LayoutButtons() {
|
|
for (NavButton& button : nav_buttons_) {
|
|
button.button->SetVisible(false);
|
|
}
|
|
|
|
gfx::Rect remaining_content_bounds = GetTitlebarContentBounds();
|
|
LayoutButtonsOnSide(ButtonSide::kLeading, &remaining_content_bounds);
|
|
LayoutButtonsOnSide(ButtonSide::kTrailing, &remaining_content_bounds);
|
|
}
|
|
|
|
void ClientFrameViewLinux::LayoutButtonsOnSide(
|
|
ButtonSide side,
|
|
gfx::Rect* remaining_content_bounds) {
|
|
ui::NavButtonProvider::FrameButtonDisplayType skip_type =
|
|
GetButtonTypeToSkip();
|
|
|
|
std::vector<views::FrameButton> frame_buttons;
|
|
|
|
switch (side) {
|
|
case ButtonSide::kLeading:
|
|
frame_buttons = leading_frame_buttons_;
|
|
break;
|
|
case ButtonSide::kTrailing:
|
|
frame_buttons = trailing_frame_buttons_;
|
|
// We always lay buttons out going from the edge towards the center, but
|
|
// they are given to us as left-to-right, so reverse them.
|
|
std::ranges::reverse(frame_buttons);
|
|
break;
|
|
default:
|
|
NOTREACHED();
|
|
}
|
|
|
|
for (views::FrameButton frame_button : frame_buttons) {
|
|
auto button =
|
|
std::ranges::find_if(nav_buttons_, [&](const NavButton& test) {
|
|
return test.type != skip_type && test.frame_button == frame_button;
|
|
});
|
|
CHECK(button != nav_buttons_.end())
|
|
<< "Failed to find frame button: " << static_cast<int>(frame_button);
|
|
|
|
if (button->type == skip_type) {
|
|
continue;
|
|
}
|
|
|
|
button->button->SetVisible(true);
|
|
|
|
int button_width = theme_values_.button_min_size;
|
|
int next_button_offset =
|
|
button_width + nav_button_provider_->GetInterNavButtonSpacing();
|
|
|
|
int x_position = 0;
|
|
gfx::Insets inset_after_placement;
|
|
|
|
switch (side) {
|
|
case ButtonSide::kLeading:
|
|
x_position = remaining_content_bounds->x();
|
|
inset_after_placement.set_left(next_button_offset);
|
|
break;
|
|
case ButtonSide::kTrailing:
|
|
x_position = remaining_content_bounds->right() - button_width;
|
|
inset_after_placement.set_right(next_button_offset);
|
|
break;
|
|
default:
|
|
NOTREACHED();
|
|
}
|
|
|
|
button->button->SetBounds(x_position, remaining_content_bounds->y(),
|
|
button_width, remaining_content_bounds->height());
|
|
remaining_content_bounds->Inset(inset_after_placement);
|
|
}
|
|
}
|
|
|
|
gfx::Rect ClientFrameViewLinux::GetTitlebarBounds() const {
|
|
if (frame_->IsFullscreen()) {
|
|
return {};
|
|
}
|
|
|
|
int font_height = gfx::FontList().GetHeight();
|
|
int titlebar_height =
|
|
std::max(font_height, theme_values_.titlebar_min_height) +
|
|
GetTitlebarContentInsets().height();
|
|
|
|
gfx::Insets decoration_insets = RestoredMirroredFrameBorderInsets();
|
|
|
|
// We add the inset height here, so the .Inset() that follows won't reduce it
|
|
// to be too small.
|
|
gfx::Rect titlebar(width(), titlebar_height + decoration_insets.height());
|
|
titlebar.Inset(decoration_insets);
|
|
return titlebar;
|
|
}
|
|
|
|
gfx::Insets ClientFrameViewLinux::GetTitlebarContentInsets() const {
|
|
return theme_values_.titlebar_padding +
|
|
nav_button_provider_->GetTopAreaSpacing();
|
|
}
|
|
|
|
gfx::Rect ClientFrameViewLinux::GetTitlebarContentBounds() const {
|
|
gfx::Rect titlebar(GetTitlebarBounds());
|
|
titlebar.Inset(GetTitlebarContentInsets());
|
|
return titlebar;
|
|
}
|
|
|
|
gfx::Size ClientFrameViewLinux::SizeWithDecorations(gfx::Size size) const {
|
|
gfx::Insets decoration_insets = RestoredMirroredFrameBorderInsets();
|
|
|
|
size.Enlarge(0, GetTitlebarBounds().height());
|
|
size.Enlarge(decoration_insets.width(), decoration_insets.height());
|
|
return size;
|
|
}
|
|
|
|
views::View* ClientFrameViewLinux::TargetForRect(views::View* root,
|
|
const gfx::Rect& rect) {
|
|
return views::FrameView::TargetForRect(root, rect);
|
|
}
|
|
|
|
int ClientFrameViewLinux::GetTranslucentTopAreaHeight() const {
|
|
return 0;
|
|
}
|
|
|
|
BEGIN_METADATA(ClientFrameViewLinux) END_METADATA
|
|
|
|
} // namespace electron
|