mirror of
https://github.com/electron/electron.git
synced 2026-05-02 03:00:22 -04:00
refactor: replace ClientFrameViewLinux with NativeFrameViewLinux (#51160)
Replace ClientFrameViewLinux with electron::NativeFrameViewLinux, a thin wrapper over views::NativeFrameViewLinux. The wrapper provides Electron integration, such as draggable region support in NonClientHitTest, and adapting to Electron's sizing conventions. ElectronDesktopWindowTreeHostLinux and NativeWindowViews now use FrameViewLinux to query frame geometry and update window states in addition to LinuxFrameLayout. Assisted-By: Claude Opus 4.6, Claude Code
This commit is contained in:
@@ -48,8 +48,8 @@ filenames = {
|
||||
"shell/browser/ui/views/opaque_frame_view.h",
|
||||
"shell/browser/ui/views/caption_button_placeholder_container.cc",
|
||||
"shell/browser/ui/views/caption_button_placeholder_container.h",
|
||||
"shell/browser/ui/views/client_frame_view_linux.cc",
|
||||
"shell/browser/ui/views/client_frame_view_linux.h",
|
||||
"shell/browser/ui/views/native_frame_view_linux.cc",
|
||||
"shell/browser/ui/views/native_frame_view_linux.h",
|
||||
"shell/browser/ui/views/linux_frame_layout.cc",
|
||||
"shell/browser/ui/views/linux_frame_layout.h",
|
||||
"shell/common/application_info_linux.cc",
|
||||
@@ -440,8 +440,6 @@ filenames = {
|
||||
"shell/browser/microtasks_runner.h",
|
||||
"shell/browser/native_window.cc",
|
||||
"shell/browser/native_window.h",
|
||||
"shell/browser/native_window_features.cc",
|
||||
"shell/browser/native_window_features.h",
|
||||
"shell/browser/native_window_observer.h",
|
||||
"shell/browser/net/asar/asar_file_validator.cc",
|
||||
"shell/browser/net/asar/asar_file_validator.h",
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "shell/browser/background_throttling_source.h"
|
||||
#include "shell/browser/browser.h"
|
||||
#include "shell/browser/draggable_region_provider.h"
|
||||
#include "shell/browser/native_window_features.h"
|
||||
#include "shell/browser/ui/drag_util.h"
|
||||
#include "shell/browser/window_list.h"
|
||||
#include "shell/common/color_util.h"
|
||||
@@ -29,6 +28,7 @@
|
||||
|
||||
#if !BUILDFLAG(IS_MAC)
|
||||
#include "shell/browser/ui/views/frameless_view.h"
|
||||
#include "ui/views/view_utils.h"
|
||||
#endif
|
||||
|
||||
#if defined(USE_OZONE)
|
||||
@@ -649,11 +649,13 @@ int NativeWindow::NonClientHitTest(const gfx::Point& point) {
|
||||
#if !BUILDFLAG(IS_MAC)
|
||||
// We need to ensure we account for resizing borders on Windows and Linux.
|
||||
if ((!has_frame() || has_client_frame()) && IsResizable()) {
|
||||
auto* frame =
|
||||
static_cast<FramelessView*>(widget()->non_client_view()->frame_view());
|
||||
int border_hit = frame->ResizingBorderHitTest(point);
|
||||
if (border_hit != HTNOWHERE)
|
||||
return border_hit;
|
||||
auto* frame = views::AsViewClass<FramelessView>(
|
||||
widget()->non_client_view()->frame_view());
|
||||
if (frame) {
|
||||
int border_hit = frame->ResizingBorderHitTest(point);
|
||||
if (border_hit != HTNOWHERE)
|
||||
return border_hit;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -780,7 +782,6 @@ bool NativeWindow::PlatformHasClientFrame() {
|
||||
// Ozone X11 likes to prefer custom frames,
|
||||
// but we don't need them unless on Wayland.
|
||||
static const bool has_client_frame =
|
||||
base::FeatureList::IsEnabled(features::kWaylandWindowDecorations) &&
|
||||
!ui::OzonePlatform::GetInstance()
|
||||
->GetPlatformRuntimeProperties()
|
||||
.supports_server_side_window_decorations;
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright (c) 2022 Slack Technologies, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/native_window_features.h"
|
||||
|
||||
namespace features {
|
||||
BASE_FEATURE(kWaylandWindowDecorations,
|
||||
"WaylandWindowDecorations",
|
||||
base::FEATURE_ENABLED_BY_DEFAULT);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// Copyright (c) 2022 Slack Technologies, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_BROWSER_NATIVE_WINDOW_FEATURES_H_
|
||||
#define ELECTRON_SHELL_BROWSER_NATIVE_WINDOW_FEATURES_H_
|
||||
|
||||
#include "base/feature_list.h"
|
||||
|
||||
namespace features {
|
||||
extern const base::Feature kWaylandWindowDecorations;
|
||||
}
|
||||
|
||||
#endif // ELECTRON_SHELL_BROWSER_NATIVE_WINDOW_FEATURES_H_
|
||||
@@ -64,12 +64,15 @@
|
||||
#include "shell/browser/linux/unity_service.h"
|
||||
#include "shell/browser/linux/x11_util.h"
|
||||
#include "shell/browser/ui/electron_desktop_window_tree_host_linux.h"
|
||||
#include "shell/browser/ui/views/client_frame_view_linux.h"
|
||||
#include "shell/browser/ui/views/linux_frame_layout.h"
|
||||
#include "shell/browser/ui/views/native_frame_view.h"
|
||||
#include "shell/browser/ui/views/native_frame_view_linux.h"
|
||||
#include "shell/browser/ui/views/opaque_frame_view.h"
|
||||
#include "shell/common/platform_util.h"
|
||||
#include "ui/linux/linux_ui.h"
|
||||
#include "ui/linux/linux_ui_factory.h"
|
||||
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
|
||||
#include "ui/views/window/frame_view_linux.h"
|
||||
#include "ui/views/window/native_frame_view.h"
|
||||
|
||||
#if BUILDFLAG(SUPPORTS_OZONE_X11)
|
||||
@@ -508,9 +511,10 @@ void NativeWindowViews::SetTitleBarOverlay(
|
||||
|
||||
// If anything was updated, ensure the overlay is repainted.
|
||||
if (updated) {
|
||||
auto* frame_view =
|
||||
static_cast<FramelessView*>(widget()->non_client_view()->frame_view());
|
||||
frame_view->InvalidateCaptionButtons();
|
||||
auto* fv = widget()->non_client_view()->frame_view();
|
||||
if (auto* frameless = views::AsViewClass<FramelessView>(fv)) {
|
||||
frameless->InvalidateCaptionButtons();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1283,9 +1287,11 @@ void NativeWindowViews::SetBackgroundColor(SkColor background_color) {
|
||||
SkColor compositor_color = background_color;
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
// Widget background needs to stay transparent for CSD shadow regions.
|
||||
auto* fvl = GetFrameViewLinux();
|
||||
LinuxFrameLayout* frame_layout = GetLinuxFrameLayout();
|
||||
const bool uses_csd =
|
||||
frame_layout && frame_layout->SupportsClientFrameShadow();
|
||||
(fvl && fvl->ShouldDrawRestoredFrameShadow()) ||
|
||||
(frame_layout && frame_layout->SupportsClientFrameShadow());
|
||||
if (transparent() || uses_csd)
|
||||
compositor_color = SK_ColorTRANSPARENT;
|
||||
#endif
|
||||
@@ -1510,9 +1516,13 @@ gfx::Insets NativeWindowViews::GetRestoredFrameBorderInsets() const {
|
||||
if (!frame_view)
|
||||
return gfx::Insets();
|
||||
|
||||
if (auto* frameless = views::AsViewClass<FramelessView>(frame_view)) {
|
||||
if (auto* frameless = views::AsViewClass<FramelessView>(frame_view))
|
||||
return frameless->RestoredFrameBorderInsets();
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
if (auto* fvl = views::AsViewClass<views::FrameViewLinux>(frame_view))
|
||||
return fvl->GetRestoredFrameBorderInsets();
|
||||
#endif
|
||||
|
||||
return gfx::Insets();
|
||||
}
|
||||
@@ -1925,8 +1935,21 @@ std::unique_ptr<views::FrameView> NativeWindowViews::CreateFrameView(
|
||||
if (!has_frame())
|
||||
return std::make_unique<OpaqueFrameView>(this, widget);
|
||||
|
||||
if (has_client_frame())
|
||||
return std::make_unique<ClientFrameViewLinux>(this, widget);
|
||||
if (has_client_frame()) {
|
||||
auto* linux_ui_theme = ui::LinuxUiTheme::GetForProfile(nullptr);
|
||||
auto getter = base::BindRepeating(
|
||||
[](ui::LinuxUiTheme* theme, bool tiled,
|
||||
bool maximized) -> ui::WindowFrameProvider* {
|
||||
return theme->GetWindowFrameProvider(ui::FrameType::kDefault,
|
||||
/*solid_frame=*/false, tiled,
|
||||
maximized);
|
||||
},
|
||||
base::Unretained(linux_ui_theme));
|
||||
auto nav_button_provider =
|
||||
linux_ui_theme->CreateNavButtonProvider(ui::FrameType::kDefault);
|
||||
return std::make_unique<NativeFrameViewLinux>(
|
||||
this, widget, std::move(nav_button_provider), std::move(getter));
|
||||
}
|
||||
|
||||
return std::make_unique<NativeFrameView>(this, widget);
|
||||
#endif
|
||||
@@ -1940,6 +1963,13 @@ LinuxFrameLayout* NativeWindowViews::GetLinuxFrameLayout() {
|
||||
auto* view = views::AsViewClass<FramelessView>(ncv->frame_view());
|
||||
return view ? view->GetLinuxFrameLayout() : nullptr;
|
||||
}
|
||||
|
||||
views::FrameViewLinux* NativeWindowViews::GetFrameViewLinux() const {
|
||||
auto* ncv = widget()->non_client_view();
|
||||
if (!ncv)
|
||||
return nullptr;
|
||||
return views::AsViewClass<views::FrameViewLinux>(ncv->frame_view());
|
||||
}
|
||||
#endif
|
||||
|
||||
void NativeWindowViews::OnWidgetMove() {
|
||||
|
||||
@@ -30,10 +30,16 @@ namespace gin {
|
||||
class Arguments;
|
||||
} // namespace gin
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
namespace views {
|
||||
class FrameViewLinux;
|
||||
} // namespace views
|
||||
#endif
|
||||
|
||||
namespace electron {
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
class ClientFrameViewLinux;
|
||||
class NativeFrameViewLinux;
|
||||
class GlobalMenuBarX11;
|
||||
class LinuxFrameLayout;
|
||||
#endif
|
||||
@@ -202,6 +208,7 @@ class NativeWindowViews : public NativeWindow,
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
LinuxFrameLayout* GetLinuxFrameLayout();
|
||||
views::FrameViewLinux* GetFrameViewLinux() const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
@@ -10,19 +10,15 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/feature_list.h"
|
||||
#include "base/i18n/rtl.h"
|
||||
#include "shell/browser/api/electron_api_web_contents.h"
|
||||
#include "shell/browser/linux/x11_util.h"
|
||||
#include "shell/browser/native_window_features.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "shell/browser/ui/views/linux_frame_layout.h"
|
||||
#include "third_party/skia/include/core/SkRegion.h"
|
||||
#include "ui/aura/window_delegate.h"
|
||||
#include "ui/base/hit_test.h"
|
||||
#include "ui/display/screen.h"
|
||||
#include "ui/gfx/geometry/rect.h"
|
||||
#include "ui/gfx/geometry/skia_conversions.h"
|
||||
#include "ui/linux/linux_ui.h"
|
||||
#include "ui/ozone/public/ozone_platform.h"
|
||||
#include "ui/platform_window/extensions/wayland_extension.h"
|
||||
@@ -30,6 +26,8 @@
|
||||
#include "ui/platform_window/platform_window_init_properties.h"
|
||||
#include "ui/views/widget/desktop_aura/desktop_window_tree_host.h"
|
||||
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h"
|
||||
#include "ui/views/window/frame_view_linux.h"
|
||||
#include "ui/views/window/frame_view_utils_linux.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
@@ -50,6 +48,12 @@ bool ElectronDesktopWindowTreeHostLinux::SupportsClientFrameShadow() const {
|
||||
|
||||
void ElectronDesktopWindowTreeHostLinux::OnWidgetInitDone() {
|
||||
views::DesktopWindowTreeHostLinux::OnWidgetInitDone();
|
||||
|
||||
// SetSupportsClientFrameShadow must happen after widget init when
|
||||
// platform_window is available.
|
||||
if (auto* fvl = native_window_view_->GetFrameViewLinux())
|
||||
fvl->SetSupportsClientFrameShadow(SupportsClientFrameShadow());
|
||||
|
||||
UpdateFrameHints();
|
||||
}
|
||||
|
||||
@@ -78,19 +82,22 @@ void ElectronDesktopWindowTreeHostLinux::Show(
|
||||
DesktopWindowTreeHostLinux::SetWindowIcons(saved_window_icon_, {});
|
||||
}
|
||||
|
||||
gfx::Insets ElectronDesktopWindowTreeHostLinux::GetRestoredFrameBorderInsets()
|
||||
const {
|
||||
if (auto* fvl = native_window_view_->GetFrameViewLinux())
|
||||
return fvl->GetRestoredFrameBorderInsets();
|
||||
|
||||
auto* const layout = native_window_view_->GetLinuxFrameLayout();
|
||||
return layout ? layout->RestoredFrameBorderInsets() : gfx::Insets();
|
||||
}
|
||||
|
||||
gfx::Insets ElectronDesktopWindowTreeHostLinux::CalculateInsetsInDIP(
|
||||
ui::PlatformWindowState window_state) const {
|
||||
// If we are not showing frame, the insets should be zero.
|
||||
if (!IsShowingFrame(window_state)) {
|
||||
if (!IsShowingFrame(window_state))
|
||||
return gfx::Insets();
|
||||
}
|
||||
|
||||
auto* const layout = native_window_view_->GetLinuxFrameLayout();
|
||||
if (!layout)
|
||||
return {};
|
||||
|
||||
gfx::Insets insets = layout->RestoredFrameBorderInsets();
|
||||
return insets;
|
||||
return GetRestoredFrameBorderInsets();
|
||||
}
|
||||
|
||||
// Electron treats min/max constraints as the logical window size, but Chromium
|
||||
@@ -101,11 +108,8 @@ std::optional<gfx::Size>
|
||||
ElectronDesktopWindowTreeHostLinux::GetMinimumSizeForWindow() const {
|
||||
auto min_size = views::DesktopWindowTreeHostLinux::GetMinimumSizeForWindow();
|
||||
if (min_size.has_value()) {
|
||||
auto* const layout = native_window_view_->GetLinuxFrameLayout();
|
||||
if (layout) {
|
||||
gfx::Insets insets = layout->RestoredFrameBorderInsets();
|
||||
min_size->Enlarge(insets.width(), insets.height());
|
||||
}
|
||||
gfx::Insets insets = GetRestoredFrameBorderInsets();
|
||||
min_size->Enlarge(insets.width(), insets.height());
|
||||
}
|
||||
return min_size;
|
||||
}
|
||||
@@ -114,15 +118,12 @@ std::optional<gfx::Size>
|
||||
ElectronDesktopWindowTreeHostLinux::GetMaximumSizeForWindow() const {
|
||||
auto max_size = views::DesktopWindowTreeHostLinux::GetMaximumSizeForWindow();
|
||||
if (max_size.has_value()) {
|
||||
auto* const layout = native_window_view_->GetLinuxFrameLayout();
|
||||
if (layout) {
|
||||
gfx::Insets insets = layout->RestoredFrameBorderInsets();
|
||||
// 0 means no constraint, so don't inflate.
|
||||
if (max_size->width() > 0)
|
||||
max_size->set_width(max_size->width() + insets.width());
|
||||
if (max_size->height() > 0)
|
||||
max_size->set_height(max_size->height() + insets.height());
|
||||
}
|
||||
gfx::Insets insets = GetRestoredFrameBorderInsets();
|
||||
// 0 means no constraint, so don't inflate.
|
||||
if (max_size->width() > 0)
|
||||
max_size->set_width(max_size->width() + insets.width());
|
||||
if (max_size->height() > 0)
|
||||
max_size->set_height(max_size->height() + insets.height());
|
||||
}
|
||||
return max_size;
|
||||
}
|
||||
@@ -151,15 +152,17 @@ void ElectronDesktopWindowTreeHostLinux::OnWindowStateChanged(
|
||||
|
||||
void ElectronDesktopWindowTreeHostLinux::OnWindowTiledStateChanged(
|
||||
ui::WindowTiledEdges new_tiled_edges) {
|
||||
if (auto* layout = native_window_view_->GetLinuxFrameLayout()) {
|
||||
// GNOME on Ubuntu reports all edges as tiled
|
||||
// even if the window is only half-tiled so do not trust individual edge
|
||||
// values.
|
||||
bool maximized = native_window_view_->IsMaximized();
|
||||
bool tiled = new_tiled_edges.top || new_tiled_edges.left ||
|
||||
new_tiled_edges.bottom || new_tiled_edges.right;
|
||||
layout->set_tiled(tiled && !maximized);
|
||||
}
|
||||
// GNOME on Ubuntu reports all edges as tiled even if the window is only
|
||||
// half-tiled, so do not trust individual edge values.
|
||||
bool maximized = native_window_view_->IsMaximized();
|
||||
bool tiled = new_tiled_edges.top || new_tiled_edges.left ||
|
||||
new_tiled_edges.bottom || new_tiled_edges.right;
|
||||
bool is_tiled = tiled && !maximized;
|
||||
|
||||
if (auto* fvl = native_window_view_->GetFrameViewLinux())
|
||||
fvl->SetTiled(is_tiled);
|
||||
else if (auto* layout = native_window_view_->GetLinuxFrameLayout())
|
||||
layout->set_tiled(is_tiled);
|
||||
UpdateFrameHints();
|
||||
ScheduleRelayout();
|
||||
if (GetWidget()->non_client_view()) {
|
||||
@@ -213,95 +216,61 @@ void ElectronDesktopWindowTreeHostLinux::OnDeviceScaleFactorChanged() {
|
||||
}
|
||||
|
||||
void ElectronDesktopWindowTreeHostLinux::UpdateFrameHints() {
|
||||
if (base::FeatureList::IsEnabled(features::kWaylandWindowDecorations)) {
|
||||
auto* const layout = native_window_view_->GetLinuxFrameLayout();
|
||||
if (!layout)
|
||||
return;
|
||||
|
||||
ui::PlatformWindow* window = platform_window();
|
||||
auto window_state = window->GetPlatformWindowState();
|
||||
float scale = device_scale_factor();
|
||||
const gfx::Size widget_size = GetWidget()->GetWindowBoundsInScreen().size();
|
||||
|
||||
if (SupportsClientFrameShadow()) {
|
||||
auto insets = CalculateInsetsInDIP(window_state);
|
||||
if (insets.IsEmpty()) {
|
||||
window->SetInputRegion(std::nullopt);
|
||||
} else {
|
||||
gfx::Rect input_bounds(widget_size);
|
||||
input_bounds.Inset(insets - layout->GetInputInsets());
|
||||
input_bounds = gfx::ScaleToEnclosingRect(input_bounds, scale);
|
||||
window->SetInputRegion(
|
||||
std::optional<std::vector<gfx::Rect>>({input_bounds}));
|
||||
}
|
||||
if (native_window_view_->GetFrameViewLinux()) {
|
||||
views::DesktopWindowTreeHostLinux::UpdateFrameHints();
|
||||
// Clear the opaque region for translucent windows.
|
||||
if (native_window_view_->IsTranslucent() &&
|
||||
views::Widget::IsWindowCompositingSupported()) {
|
||||
platform_window()->SetOpaqueRegion(std::vector<gfx::Rect>{});
|
||||
}
|
||||
|
||||
if (ui::OzonePlatform::GetInstance()->IsWindowCompositingSupported()) {
|
||||
// Set the opaque region.
|
||||
std::vector<gfx::Rect> opaque_region;
|
||||
if (native_window_view_->IsTranslucent()) {
|
||||
// Leave opaque_region empty.
|
||||
} else if (IsShowingFrame(window_state)) {
|
||||
// The opaque region is a list of rectangles that contain only fully
|
||||
// opaque pixels of the window. We need to convert the clipping
|
||||
// rounded-rect into this format.
|
||||
SkRRect rrect = layout->GetRoundedWindowBounds();
|
||||
gfx::RectF rectf(layout->GetWindowBounds());
|
||||
rectf.Scale(scale);
|
||||
// It is acceptable to omit some pixels that are opaque, but the region
|
||||
// must not include any translucent pixels. Therefore, we must
|
||||
// conservatively scale to the enclosed rectangle.
|
||||
gfx::Rect rect = gfx::ToEnclosedRect(rectf);
|
||||
|
||||
// Create the initial region from the clipping rectangle without rounded
|
||||
// corners.
|
||||
SkRegion region(gfx::RectToSkIRect(rect));
|
||||
|
||||
// Now subtract out the small rectangles that cover the corners.
|
||||
struct {
|
||||
SkRRect::Corner corner;
|
||||
bool left;
|
||||
bool upper;
|
||||
} kCorners[] = {
|
||||
{SkRRect::kUpperLeft_Corner, true, true},
|
||||
{SkRRect::kUpperRight_Corner, false, true},
|
||||
{SkRRect::kLowerLeft_Corner, true, false},
|
||||
{SkRRect::kLowerRight_Corner, false, false},
|
||||
};
|
||||
for (const auto& corner : kCorners) {
|
||||
auto radii = rrect.radii(corner.corner);
|
||||
auto rx = std::ceil(scale * radii.x());
|
||||
auto ry = std::ceil(scale * radii.y());
|
||||
auto corner_rect = SkIRect::MakeXYWH(
|
||||
corner.left ? rect.x() : rect.right() - rx,
|
||||
corner.upper ? rect.y() : rect.bottom() - ry, rx, ry);
|
||||
region.op(corner_rect, SkRegion::kDifference_Op);
|
||||
}
|
||||
|
||||
auto translucent_top_area_rect = SkIRect::MakeXYWH(
|
||||
rect.x(), rect.y(), rect.width(),
|
||||
std::ceil(layout->GetTranslucentTopAreaHeight() * scale -
|
||||
rect.y()));
|
||||
region.op(translucent_top_area_rect, SkRegion::kDifference_Op);
|
||||
|
||||
// Convert the region to a list of rectangles.
|
||||
for (SkRegion::Iterator i(region); !i.done(); i.next()) {
|
||||
opaque_region.push_back(gfx::SkIRectToRect(i.rect()));
|
||||
}
|
||||
} else {
|
||||
// The entire window except for the translucent top is opaque.
|
||||
gfx::Rect opaque_region_dip(widget_size);
|
||||
gfx::Insets insets;
|
||||
insets.set_top(layout->GetTranslucentTopAreaHeight());
|
||||
opaque_region_dip.Inset(insets);
|
||||
opaque_region.push_back(
|
||||
gfx::ScaleToEnclosingRect(opaque_region_dip, scale));
|
||||
}
|
||||
window->SetOpaqueRegion(opaque_region);
|
||||
}
|
||||
|
||||
SizeConstraintsChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
auto* layout = native_window_view_->GetLinuxFrameLayout();
|
||||
if (!layout)
|
||||
return;
|
||||
|
||||
ui::PlatformWindow* window = platform_window();
|
||||
auto window_state = window->GetPlatformWindowState();
|
||||
float scale = device_scale_factor();
|
||||
const gfx::Size widget_size = GetWidget()->GetWindowBoundsInScreen().size();
|
||||
|
||||
if (SupportsClientFrameShadow()) {
|
||||
auto insets = CalculateInsetsInDIP(window_state);
|
||||
if (insets.IsEmpty()) {
|
||||
window->SetInputRegion(std::nullopt);
|
||||
} else {
|
||||
gfx::Rect input_bounds(widget_size);
|
||||
input_bounds.Inset(insets - layout->GetInputInsets());
|
||||
input_bounds = gfx::ScaleToEnclosingRect(input_bounds, scale);
|
||||
window->SetInputRegion(
|
||||
std::optional<std::vector<gfx::Rect>>({input_bounds}));
|
||||
}
|
||||
}
|
||||
|
||||
if (ui::OzonePlatform::GetInstance()->IsWindowCompositingSupported()) {
|
||||
// Set the opaque region.
|
||||
std::vector<gfx::Rect> opaque_region;
|
||||
if (native_window_view_->IsTranslucent()) {
|
||||
// Leave opaque_region empty.
|
||||
} else if (IsShowingFrame(window_state)) {
|
||||
opaque_region = views::GetRestoredOpaqueRegion(
|
||||
layout->GetRoundedWindowBounds(), scale,
|
||||
layout->GetTranslucentTopAreaHeight());
|
||||
} else {
|
||||
// The entire window except for the translucent top is opaque.
|
||||
gfx::Rect opaque_region_dip(widget_size);
|
||||
gfx::Insets insets;
|
||||
insets.set_top(layout->GetTranslucentTopAreaHeight());
|
||||
opaque_region_dip.Inset(insets);
|
||||
opaque_region.push_back(
|
||||
gfx::ScaleToEnclosingRect(opaque_region_dip, scale));
|
||||
}
|
||||
window->SetOpaqueRegion(opaque_region);
|
||||
}
|
||||
|
||||
SizeConstraintsChanged();
|
||||
}
|
||||
|
||||
void ElectronDesktopWindowTreeHostLinux::DispatchEvent(ui::Event* event) {
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "ui/gfx/image/image_skia.h"
|
||||
#include "ui/linux/device_scale_factor_observer.h"
|
||||
#include "ui/linux/linux_ui.h"
|
||||
#include "ui/native_theme/native_theme_observer.h"
|
||||
#include "ui/platform_window/platform_window.h"
|
||||
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h"
|
||||
|
||||
@@ -78,12 +77,13 @@ class ElectronDesktopWindowTreeHostLinux
|
||||
|
||||
bool IsShowingFrame(ui::PlatformWindowState window_state) const;
|
||||
|
||||
// Returns restored frame border insets regardless of current widget state.
|
||||
gfx::Insets GetRestoredFrameBorderInsets() const;
|
||||
|
||||
gfx::ImageSkia saved_window_icon_;
|
||||
|
||||
raw_ptr<NativeWindowViews> native_window_view_; // weak ref
|
||||
|
||||
base::ScopedObservation<ui::NativeTheme, ui::NativeThemeObserver>
|
||||
theme_observation_{this};
|
||||
base::ScopedObservation<ui::LinuxUi, ui::DeviceScaleFactorObserver>
|
||||
scale_observation_{this};
|
||||
ui::PlatformWindowState window_state_ = ui::PlatformWindowState::kUnknown;
|
||||
|
||||
@@ -1,455 +0,0 @@
|
||||
// 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/views/frameless_view.h"
|
||||
#include "shell/browser/ui/views/linux_frame_layout.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/base/ui_base_types.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/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/accessibility/view_accessibility.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 {
|
||||
|
||||
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(NativeWindowViews* window,
|
||||
views::Widget* frame)
|
||||
: FramelessView{window, frame},
|
||||
theme_{ui::NativeTheme::GetInstanceForNativeUi()},
|
||||
nav_button_provider_(
|
||||
ui::LinuxUiTheme::GetForProfile(nullptr)->CreateNavButtonProvider(
|
||||
ui::FrameType::kDefault)),
|
||||
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->GetViewAccessibility().SetName(
|
||||
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();
|
||||
}
|
||||
linux_frame_layout_ = std::make_unique<LinuxCSDNativeFrameLayout>(window);
|
||||
|
||||
// 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)));
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
ClientFrameViewLinux::~ClientFrameViewLinux() {
|
||||
if (auto* ui = ui::LinuxUi::instance())
|
||||
ui->RemoveWindowButtonOrderObserver(this);
|
||||
theme_->RemoveObserver(this);
|
||||
}
|
||||
|
||||
gfx::Insets ClientFrameViewLinux::RestoredFrameBorderInsets() const {
|
||||
return linux_frame_layout_->RestoredFrameBorderInsets();
|
||||
}
|
||||
|
||||
LinuxFrameLayout* ClientFrameViewLinux::GetLinuxFrameLayout() const {
|
||||
return linux_frame_layout_.get();
|
||||
}
|
||||
|
||||
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, linux_frame_layout_->GetResizeBorderInsets());
|
||||
}
|
||||
|
||||
gfx::Rect ClientFrameViewLinux::GetBoundsForClientView() const {
|
||||
gfx::Rect client_bounds = bounds();
|
||||
if (!frame_->IsFullscreen()) {
|
||||
client_bounds.Inset(linux_frame_layout_->FrameBorderInsets(false));
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame_->IsMaximized()) {
|
||||
// Some GTK themes (Breeze) still render shadow/border assets when
|
||||
// maximized, and we don't need a border when maximized anyway. Chromium
|
||||
// switches on this too: OpaqueBrowserFrameView::PaintMaximizedFrameBorder.
|
||||
PaintMaximizedFrameBorder(canvas);
|
||||
} else {
|
||||
PaintRestoredFrameBorder(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
void ClientFrameViewLinux::PaintRestoredFrameBorder(gfx::Canvas* canvas) {
|
||||
if (auto* frame_provider = linux_frame_layout_->GetFrameProvider()) {
|
||||
frame_provider->PaintWindowFrame(
|
||||
canvas, GetLocalBounds(), GetTitlebarBounds().bottom(),
|
||||
ShouldPaintAsActive(), linux_frame_layout_->GetInputInsets());
|
||||
}
|
||||
}
|
||||
|
||||
void ClientFrameViewLinux::PaintMaximizedFrameBorder(gfx::Canvas* canvas) {
|
||||
ui::NativeTheme::FrameTopAreaExtraParams frame_top_area;
|
||||
frame_top_area.use_custom_frame = true;
|
||||
frame_top_area.is_active = ShouldPaintAsActive();
|
||||
frame_top_area.default_background_color = SK_ColorTRANSPARENT;
|
||||
ui::NativeTheme::ExtraParams params(frame_top_area);
|
||||
GetNativeTheme()->Paint(
|
||||
canvas->sk_canvas(), GetColorProvider(), ui::NativeTheme::kFrameTopArea,
|
||||
ui::NativeTheme::kNormal,
|
||||
gfx::Rect(0, 0, width(), GetTitlebarBounds().bottom()), params);
|
||||
}
|
||||
|
||||
void ClientFrameViewLinux::PaintAsActiveChanged() {
|
||||
UpdateThemeValues();
|
||||
}
|
||||
|
||||
void ClientFrameViewLinux::UpdateThemeValues() {
|
||||
gtk::GtkCssContext window_context =
|
||||
gtk::AppendCssNodeToStyleContext({}, "window.background.csd");
|
||||
gtk::GtkCssContext headerbar_context = gtk::AppendCssNodeToStyleContext(
|
||||
window_context, "headerbar.default-decoration.titlebar");
|
||||
gtk::GtkCssContext title_context =
|
||||
gtk::AppendCssNodeToStyleContext(headerbar_context, "label.title");
|
||||
// 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);
|
||||
}
|
||||
|
||||
theme_values_.window_border_radius =
|
||||
linux_frame_layout_->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);
|
||||
|
||||
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() {
|
||||
int top_area_height = theme_values_.titlebar_min_height +
|
||||
theme_values_.titlebar_padding.height();
|
||||
nav_button_provider_->RedrawImages(top_area_height, 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);
|
||||
|
||||
// CSS min-size/height/width is not enough to determine the actual size of
|
||||
// the buttons, so we sample the rendered image. See Chromium's
|
||||
// BrowserFrameViewLinuxNative::MaybeUpdateCachedFrameButtonImages.
|
||||
int button_width =
|
||||
nav_button_provider_
|
||||
->GetImage(button->type,
|
||||
ui::NavButtonProvider::ButtonState::kNormal)
|
||||
.width();
|
||||
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 = linux_frame_layout_->FrameBorderInsets(false);
|
||||
|
||||
// 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;
|
||||
}
|
||||
views::View* ClientFrameViewLinux::TargetForRect(views::View* root,
|
||||
const gfx::Rect& rect) {
|
||||
return views::FrameView::TargetForRect(root, rect);
|
||||
}
|
||||
|
||||
BEGIN_METADATA(ClientFrameViewLinux) END_METADATA
|
||||
|
||||
} // namespace electron
|
||||
@@ -1,135 +0,0 @@
|
||||
// Copyright (c) 2021 Ryan Gonzalez.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_BROWSER_UI_VIEWS_CLIENT_FRAME_VIEW_LINUX_H_
|
||||
#define ELECTRON_SHELL_BROWSER_UI_VIEWS_CLIENT_FRAME_VIEW_LINUX_H_
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/raw_ptr_exclusion.h"
|
||||
#include "base/scoped_observation.h"
|
||||
#include "shell/browser/ui/views/frameless_view.h"
|
||||
#include "shell/browser/ui/views/linux_frame_layout.h"
|
||||
#include "ui/base/metadata/metadata_header_macros.h"
|
||||
#include "ui/base/ui_base_types.h"
|
||||
#include "ui/linux/linux_ui.h"
|
||||
#include "ui/linux/nav_button_provider.h"
|
||||
#include "ui/linux/window_button_order_observer.h"
|
||||
#include "ui/native_theme/native_theme.h"
|
||||
#include "ui/native_theme/native_theme_observer.h"
|
||||
#include "ui/views/controls/button/image_button.h"
|
||||
#include "ui/views/controls/label.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
#include "ui/views/window/frame_buttons.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
class NativeWindowViews;
|
||||
|
||||
class ClientFrameViewLinux : public FramelessView,
|
||||
private ui::NativeThemeObserver,
|
||||
private ui::WindowButtonOrderObserver {
|
||||
METADATA_HEADER(ClientFrameViewLinux, FramelessView)
|
||||
|
||||
public:
|
||||
ClientFrameViewLinux(NativeWindowViews* window, views::Widget* frame);
|
||||
~ClientFrameViewLinux() override;
|
||||
|
||||
// FramelessView:
|
||||
gfx::Insets RestoredFrameBorderInsets() const override;
|
||||
LinuxFrameLayout* GetLinuxFrameLayout() const override;
|
||||
|
||||
protected:
|
||||
// ui::NativeThemeObserver:
|
||||
void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override;
|
||||
|
||||
// views::WindowButtonOrderObserver:
|
||||
void OnWindowButtonOrderingChange() override;
|
||||
|
||||
// Overridden from FramelessView:
|
||||
int ResizingBorderHitTest(const gfx::Point& point) override;
|
||||
|
||||
// Overridden from views::FrameView:
|
||||
gfx::Rect GetBoundsForClientView() const override;
|
||||
gfx::Rect GetWindowBoundsForClientBounds(
|
||||
const gfx::Rect& client_bounds) const override;
|
||||
int NonClientHitTest(const gfx::Point& point) override;
|
||||
void GetWindowMask(const gfx::Size& size, SkPath* window_mask) override;
|
||||
void UpdateWindowTitle() override;
|
||||
void SizeConstraintsChanged() override;
|
||||
|
||||
// Overridden from View:
|
||||
void Layout(PassKey) override;
|
||||
void OnPaint(gfx::Canvas* canvas) override;
|
||||
|
||||
// Overridden from views::ViewTargeterDelegate
|
||||
views::View* TargetForRect(views::View* root, const gfx::Rect& rect) override;
|
||||
|
||||
private:
|
||||
static constexpr int kNavButtonCount = 4;
|
||||
|
||||
struct NavButton {
|
||||
ui::NavButtonProvider::FrameButtonDisplayType type;
|
||||
views::FrameButton frame_button;
|
||||
void (views::Widget::*callback)();
|
||||
int accessibility_id;
|
||||
int hit_test_id;
|
||||
raw_ptr<views::ImageButton> button = {};
|
||||
};
|
||||
|
||||
struct ThemeValues {
|
||||
float window_border_radius;
|
||||
|
||||
int titlebar_min_height;
|
||||
gfx::Insets titlebar_padding;
|
||||
|
||||
SkColor title_color;
|
||||
gfx::Insets title_padding;
|
||||
};
|
||||
|
||||
void PaintAsActiveChanged();
|
||||
void PaintRestoredFrameBorder(gfx::Canvas* canvas);
|
||||
void PaintMaximizedFrameBorder(gfx::Canvas* canvas);
|
||||
|
||||
void UpdateThemeValues();
|
||||
|
||||
enum class ButtonSide { kLeading, kTrailing };
|
||||
|
||||
ui::NavButtonProvider::FrameButtonDisplayType GetButtonTypeToSkip() const;
|
||||
void UpdateButtonImages();
|
||||
void LayoutButtons();
|
||||
void LayoutButtonsOnSide(ButtonSide side,
|
||||
gfx::Rect* remaining_content_bounds);
|
||||
|
||||
gfx::Rect GetTitlebarBounds() const;
|
||||
gfx::Insets GetTitlebarContentInsets() const;
|
||||
gfx::Rect GetTitlebarContentBounds() const;
|
||||
|
||||
std::unique_ptr<LinuxCSDNativeFrameLayout> linux_frame_layout_;
|
||||
|
||||
raw_ptr<ui::NativeTheme> theme_;
|
||||
ThemeValues theme_values_;
|
||||
|
||||
raw_ptr<views::Label> title_;
|
||||
|
||||
std::unique_ptr<ui::NavButtonProvider> nav_button_provider_;
|
||||
std::array<NavButton, kNavButtonCount> nav_buttons_;
|
||||
|
||||
std::vector<views::FrameButton> leading_frame_buttons_;
|
||||
std::vector<views::FrameButton> trailing_frame_buttons_;
|
||||
|
||||
base::ScopedObservation<ui::NativeTheme, ui::NativeThemeObserver>
|
||||
native_theme_observer_{this};
|
||||
base::ScopedObservation<ui::LinuxUi, ui::WindowButtonOrderObserver>
|
||||
window_button_order_observer_{this};
|
||||
|
||||
base::CallbackListSubscription paint_as_active_changed_subscription_;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // ELECTRON_SHELL_BROWSER_UI_VIEWS_CLIENT_FRAME_VIEW_LINUX_H_
|
||||
52
shell/browser/ui/views/native_frame_view_linux.cc
Normal file
52
shell/browser/ui/views/native_frame_view_linux.cc
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2026 Athul Iddya.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/ui/views/native_frame_view_linux.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "ui/base/hit_test.h"
|
||||
#include "ui/base/metadata/metadata_impl_macros.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
NativeFrameViewLinux::NativeFrameViewLinux(
|
||||
NativeWindowViews* window,
|
||||
views::Widget* widget,
|
||||
std::unique_ptr<ui::NavButtonProvider> nav_button_provider,
|
||||
views::NativeFrameViewLayoutLinux::FrameProviderGetter getter)
|
||||
: views::NativeFrameViewLinux(widget,
|
||||
std::move(nav_button_provider),
|
||||
std::move(getter)),
|
||||
window_(window) {
|
||||
InitViews();
|
||||
}
|
||||
|
||||
NativeFrameViewLinux::~NativeFrameViewLinux() = default;
|
||||
|
||||
int NativeFrameViewLinux::NonClientHitTest(const gfx::Point& point) {
|
||||
int result = views::NativeFrameViewLinux::NonClientHitTest(point);
|
||||
if (result != HTCLIENT)
|
||||
return result;
|
||||
|
||||
int contents_hit_test = window_->NonClientHitTest(point);
|
||||
if (contents_hit_test != HTNOWHERE)
|
||||
return contents_hit_test;
|
||||
|
||||
return HTCLIENT;
|
||||
}
|
||||
|
||||
gfx::Size NativeFrameViewLinux::GetMinimumSize() const {
|
||||
return window_->GetMinimumSize();
|
||||
}
|
||||
|
||||
gfx::Size NativeFrameViewLinux::GetMaximumSize() const {
|
||||
return window_->GetMaximumSize();
|
||||
}
|
||||
|
||||
BEGIN_METADATA(NativeFrameViewLinux)
|
||||
END_METADATA
|
||||
|
||||
} // namespace electron
|
||||
44
shell/browser/ui/views/native_frame_view_linux.h
Normal file
44
shell/browser/ui/views/native_frame_view_linux.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2026 Athul Iddya.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_BROWSER_UI_VIEWS_NATIVE_FRAME_VIEW_LINUX_H_
|
||||
#define ELECTRON_SHELL_BROWSER_UI_VIEWS_NATIVE_FRAME_VIEW_LINUX_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "ui/base/metadata/metadata_header_macros.h"
|
||||
#include "ui/linux/nav_button_provider.h"
|
||||
#include "ui/views/window/native_frame_view_layout_linux.h"
|
||||
#include "ui/views/window/native_frame_view_linux.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
class NativeWindowViews;
|
||||
|
||||
// Extends views::NativeFrameViewLinux for Electron integration, such as
|
||||
// app-defined draggable regions and frame sizing.
|
||||
class NativeFrameViewLinux : public views::NativeFrameViewLinux {
|
||||
METADATA_HEADER(NativeFrameViewLinux, views::NativeFrameViewLinux)
|
||||
|
||||
public:
|
||||
NativeFrameViewLinux(
|
||||
NativeWindowViews* window,
|
||||
views::Widget* widget,
|
||||
std::unique_ptr<ui::NavButtonProvider> nav_button_provider,
|
||||
views::NativeFrameViewLayoutLinux::FrameProviderGetter getter);
|
||||
~NativeFrameViewLinux() override;
|
||||
|
||||
// views::FrameViewLinux:
|
||||
int NonClientHitTest(const gfx::Point& point) override;
|
||||
gfx::Size GetMinimumSize() const override;
|
||||
gfx::Size GetMaximumSize() const override;
|
||||
|
||||
private:
|
||||
raw_ptr<NativeWindowViews> window_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // ELECTRON_SHELL_BROWSER_UI_VIEWS_NATIVE_FRAME_VIEW_LINUX_H_
|
||||
Reference in New Issue
Block a user