mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
7 Commits
roller/nod
...
41-x-y
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b91d3d33fa | ||
|
|
de2ebae944 | ||
|
|
6dcbf464f4 | ||
|
|
3a2b7d3720 | ||
|
|
f86822163f | ||
|
|
e600ad9dac | ||
|
|
f3f3e113ab |
@@ -354,6 +354,11 @@ Affects the default output directory of [v8.setHeapSnapshotNearHeapLimit](https:
|
||||
|
||||
Disable exposition of [Navigator API][] on the global scope from Node.js.
|
||||
|
||||
### `--experimental-transform-types`
|
||||
|
||||
Enables the [transformation](https://nodejs.org/api/typescript.html#type-stripping)
|
||||
of TypeScript-only syntax into JavaScript code.
|
||||
|
||||
## Chromium Flags
|
||||
|
||||
There isn't a documented list of all Chromium switches, but there are a few ways to find them.
|
||||
|
||||
@@ -30,7 +30,7 @@ The `dialog` module has the following methods:
|
||||
* `openFile` - Allow files to be selected.
|
||||
* `openDirectory` - Allow directories to be selected.
|
||||
* `multiSelections` - Allow multiple paths to be selected.
|
||||
* `showHiddenFiles` - Show hidden files in dialog.
|
||||
* `showHiddenFiles` _macOS_ _Windows_ _Deprecated_ - Show hidden files in dialog. Deprecated on Linux.
|
||||
* `createDirectory` _macOS_ - Allow creating new directories from dialog.
|
||||
* `promptToCreate` _Windows_ - Prompt for creation if the file path entered
|
||||
in the dialog does not exist. This does not actually create the file at
|
||||
@@ -102,7 +102,7 @@ dialog.showOpenDialogSync(mainWindow, {
|
||||
* `openFile` - Allow files to be selected.
|
||||
* `openDirectory` - Allow directories to be selected.
|
||||
* `multiSelections` - Allow multiple paths to be selected.
|
||||
* `showHiddenFiles` - Show hidden files in dialog.
|
||||
* `showHiddenFiles` _macOS_ _Windows_ _Deprecated_ - Show hidden files in dialog. Deprecated on Linux.
|
||||
* `createDirectory` _macOS_ - Allow creating new directories from dialog.
|
||||
* `promptToCreate` _Windows_ - Prompt for creation if the file path entered
|
||||
in the dialog does not exist. This does not actually create the file at
|
||||
@@ -185,7 +185,7 @@ dialog.showOpenDialog(mainWindow, {
|
||||
* `showsTagField` boolean (optional) _macOS_ - Show the tags input box,
|
||||
defaults to `true`.
|
||||
* `properties` string[] (optional)
|
||||
* `showHiddenFiles` - Show hidden files in dialog.
|
||||
* `showHiddenFiles` _macOS_ _Windows_ _Deprecated_ - Show hidden files in dialog. Deprecated on Linux.
|
||||
* `createDirectory` _macOS_ - Allow creating new directories from dialog.
|
||||
* `treatPackageAsDirectory` _macOS_ - Treat packages, such as `.app` folders,
|
||||
as a directory instead of a file.
|
||||
@@ -215,7 +215,7 @@ The `filters` specifies an array of file types that can be displayed, see
|
||||
displayed in front of the filename text field.
|
||||
* `showsTagField` boolean (optional) _macOS_ - Show the tags input box, defaults to `true`.
|
||||
* `properties` string[] (optional)
|
||||
* `showHiddenFiles` - Show hidden files in dialog.
|
||||
* `showHiddenFiles` _macOS_ _Windows_ _Deprecated_ - Show hidden files in dialog. Deprecated on Linux.
|
||||
* `createDirectory` _macOS_ - Allow creating new directories from dialog.
|
||||
* `treatPackageAsDirectory` _macOS_ - Treat packages, such as `.app` folders,
|
||||
as a directory instead of a file.
|
||||
|
||||
@@ -41,6 +41,12 @@ your preload script and expose it using the [contextBridge](https://www.electron
|
||||
Debug symbols for MacOS (dSYM) now use xz compression in order to handle larger file sizes. `dsym.zip` files are now
|
||||
`dsym.tar.xz` files. End users using debug symbols may need to update their zip utilities.
|
||||
|
||||
### Deprecated: `showHiddenFiles` in Dialogs on Linux
|
||||
|
||||
This property will still be honored on macOS and Windows, but support on Linux
|
||||
will be removed in Electron 42. GTK intends for this to be a user choice rather
|
||||
than an app choice and has removed the API to do this programmatically.
|
||||
|
||||
## Planned Breaking API Changes (39.0)
|
||||
|
||||
### Deprecated: `--host-rules` command line switch
|
||||
|
||||
@@ -50,6 +50,8 @@ filenames = {
|
||||
"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/linux_frame_layout.cc",
|
||||
"shell/browser/ui/views/linux_frame_layout.h",
|
||||
"shell/common/application_info_linux.cc",
|
||||
"shell/common/language_util_linux.cc",
|
||||
"shell/common/node_bindings_linux.cc",
|
||||
|
||||
@@ -124,7 +124,7 @@ async function main () {
|
||||
// Get the merge base with the target branch
|
||||
let mergeBase;
|
||||
try {
|
||||
mergeBase = execSync(`git merge-base ${currentBranch} origin/${targetBranch}`, {
|
||||
mergeBase = execSync(`git merge-base HEAD origin/${targetBranch}`, {
|
||||
cwd: ELECTRON_DIR,
|
||||
encoding: 'utf8'
|
||||
}).trim();
|
||||
|
||||
@@ -368,6 +368,9 @@ def upload_io_to_github(release, filename, filepath, version):
|
||||
for c in iter(lambda: upload_process.stdout.read(1), b""):
|
||||
sys.stdout.buffer.write(c)
|
||||
sys.stdout.flush()
|
||||
upload_process.wait()
|
||||
if upload_process.returncode != 0:
|
||||
sys.exit(upload_process.returncode)
|
||||
|
||||
if "GITHUB_OUTPUT" in os.environ:
|
||||
output_path = os.environ["GITHUB_OUTPUT"]
|
||||
|
||||
@@ -192,8 +192,7 @@ void NativeWindow::InitFromOptions(const gin_helper::Dictionary& options) {
|
||||
if (bool val; options.Get(options::kMovable, &val))
|
||||
SetMovable(val);
|
||||
|
||||
if (bool val; options.Get(options::kHasShadow, &val))
|
||||
SetHasShadow(val);
|
||||
SetHasShadow(options.ValueOrDefault(options::kHasShadow, true));
|
||||
|
||||
if (double val; options.Get(options::kOpacity, &val))
|
||||
SetOpacity(val);
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
#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/opaque_frame_view.h"
|
||||
#include "shell/common/platform_util.h"
|
||||
@@ -262,6 +263,8 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
|
||||
gfx::Rect bounds{0, 0, width, height};
|
||||
widget_size_ = bounds.size();
|
||||
|
||||
has_shadow_ = options.ValueOrDefault(options::kHasShadow, true);
|
||||
|
||||
widget()->AddObserver(this);
|
||||
|
||||
using InitParams = views::Widget::InitParams;
|
||||
@@ -418,7 +421,7 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
|
||||
}
|
||||
|
||||
gfx::Size size = bounds.size();
|
||||
if (has_frame() && use_content_size_)
|
||||
if ((has_frame() || has_client_frame()) && use_content_size_)
|
||||
size = ContentBoundsToWindowBounds(gfx::Rect(size)).size();
|
||||
|
||||
widget()->CenterWindow(size);
|
||||
@@ -1275,18 +1278,27 @@ void NativeWindowViews::SetBackgroundColor(SkColor background_color) {
|
||||
DeleteObject((HBRUSH)previous_brush);
|
||||
InvalidateRect(GetAcceleratedWidget(), nullptr, 1);
|
||||
#endif
|
||||
widget()->GetCompositor()->SetBackgroundColor(background_color);
|
||||
SkColor compositor_color = background_color;
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
// Widget background needs to stay transparent for CSD shadow regions.
|
||||
LinuxFrameLayout* frame_layout = GetLinuxFrameLayout();
|
||||
const bool uses_csd =
|
||||
frame_layout && frame_layout->SupportsClientFrameShadow();
|
||||
if (transparent() || uses_csd)
|
||||
compositor_color = SK_ColorTRANSPARENT;
|
||||
#endif
|
||||
widget()->GetCompositor()->SetBackgroundColor(compositor_color);
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetHasShadow(bool has_shadow) {
|
||||
has_shadow_ = has_shadow;
|
||||
wm::SetShadowElevation(GetNativeWindow(),
|
||||
has_shadow ? wm::kShadowElevationInactiveWindow
|
||||
: wm::kShadowElevationNone);
|
||||
}
|
||||
|
||||
bool NativeWindowViews::HasShadow() const {
|
||||
return GetNativeWindow()->GetProperty(wm::kShadowElevationKey) !=
|
||||
wm::kShadowElevationNone;
|
||||
return has_shadow_;
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetOpacity(const double opacity) {
|
||||
@@ -1688,7 +1700,7 @@ gfx::Rect NativeWindowViews::WidgetToLogicalBounds(
|
||||
|
||||
gfx::Rect NativeWindowViews::ContentBoundsToWindowBounds(
|
||||
const gfx::Rect& bounds) const {
|
||||
if (!has_frame())
|
||||
if (!has_frame() && !has_client_frame())
|
||||
return bounds;
|
||||
|
||||
gfx::Rect window_bounds(bounds);
|
||||
@@ -1715,7 +1727,7 @@ gfx::Rect NativeWindowViews::ContentBoundsToWindowBounds(
|
||||
|
||||
gfx::Rect NativeWindowViews::WindowBoundsToContentBounds(
|
||||
const gfx::Rect& bounds) const {
|
||||
if (!has_frame())
|
||||
if (!has_frame() && !has_client_frame())
|
||||
return bounds;
|
||||
|
||||
gfx::Rect content_bounds(bounds);
|
||||
@@ -1920,15 +1932,10 @@ std::unique_ptr<views::FrameView> NativeWindowViews::CreateFrameView(
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
electron::ClientFrameViewLinux* NativeWindowViews::GetClientFrameViewLinux() {
|
||||
// Check to make sure this window's non-client frame view is a
|
||||
// ClientFrameViewLinux. If either has_frame() or has_client_frame()
|
||||
// are false, it will be an OpaqueFrameView or NativeFrameView instead.
|
||||
// See NativeWindowViews::CreateFrameView.
|
||||
if (!has_frame() || !has_client_frame())
|
||||
return {};
|
||||
return static_cast<ClientFrameViewLinux*>(
|
||||
LinuxFrameLayout* NativeWindowViews::GetLinuxFrameLayout() {
|
||||
auto* view = views::AsViewClass<FramelessView>(
|
||||
widget()->non_client_view()->frame_view());
|
||||
return view ? view->GetLinuxFrameLayout() : nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace electron {
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
class ClientFrameViewLinux;
|
||||
class GlobalMenuBarX11;
|
||||
class LinuxFrameLayout;
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(SUPPORTS_OZONE_X11)
|
||||
@@ -198,9 +199,7 @@ class NativeWindowViews : public NativeWindow,
|
||||
SkColor overlay_symbol_color() const { return overlay_symbol_color_; }
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
// returns the ClientFrameViewLinux iff that is our FrameView type,
|
||||
// nullptr otherwise.
|
||||
ClientFrameViewLinux* GetClientFrameViewLinux();
|
||||
LinuxFrameLayout* GetLinuxFrameLayout();
|
||||
#endif
|
||||
|
||||
private:
|
||||
@@ -367,6 +366,7 @@ class NativeWindowViews : public NativeWindow,
|
||||
bool maximizable_ = true;
|
||||
bool minimizable_ = true;
|
||||
bool fullscreenable_ = true;
|
||||
bool has_shadow_ = true;
|
||||
gfx::Size widget_size_;
|
||||
double opacity_ = 1.0;
|
||||
bool widget_destroyed_ = false;
|
||||
|
||||
@@ -371,7 +371,7 @@ void HandleToastActivation(const std::wstring& invoked_args,
|
||||
|
||||
int action_index = -1;
|
||||
if (!action_index_str.empty()) {
|
||||
action_index = std::stoi(action_index_str);
|
||||
base::StringToInt(base::WideToUTF8(action_index_str), &action_index);
|
||||
}
|
||||
|
||||
std::string reply_text;
|
||||
|
||||
@@ -35,6 +35,41 @@ int ScopedDisableResize::disable_resize_ = 0;
|
||||
|
||||
typedef void (*MouseDownImpl)(id, SEL, NSEvent*);
|
||||
|
||||
// Work around an Apple bug where the visual tab picker's
|
||||
// grid animation creates NSLayoutConstraints against nil layout anchors,
|
||||
// crashing in NSVisualTabPickerShadowTileView. This happens when a new tabbed
|
||||
// window is created while the tab picker is open — the "+" tile (and possibly
|
||||
// others) have broken internal state. Rather than patching individual tile
|
||||
// animation methods, short-circuit the entire grid animation by swizzling
|
||||
// NSVisualTabPickerGridView's -startGridAnimation:completionHandler: to
|
||||
// immediately invoke the completion handler without running the animation.
|
||||
typedef void (*StartGridAnimationIMP)(id, SEL, id, id);
|
||||
static StartGridAnimationIMP g_orig_startGridAnimation = nullptr;
|
||||
|
||||
static void Patched_startGridAnimation(id self,
|
||||
SEL _cmd,
|
||||
id animation,
|
||||
void (^completionHandler)(void)) {
|
||||
if (completionHandler)
|
||||
completionHandler();
|
||||
}
|
||||
|
||||
static void SwizzleTabPickerGridAnimation() {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
Class cls = NSClassFromString(@"NSVisualTabPickerGridView");
|
||||
if (!cls)
|
||||
return;
|
||||
SEL sel = @selector(startGridAnimation:completionHandler:);
|
||||
Method method = class_getInstanceMethod(cls, sel);
|
||||
if (!method)
|
||||
return;
|
||||
g_orig_startGridAnimation =
|
||||
(StartGridAnimationIMP)method_getImplementation(method);
|
||||
method_setImplementation(method, (IMP)Patched_startGridAnimation);
|
||||
});
|
||||
}
|
||||
|
||||
namespace {
|
||||
MouseDownImpl g_nsthemeframe_mousedown;
|
||||
MouseDownImpl g_nsnextstepframe_mousedown;
|
||||
@@ -125,6 +160,7 @@ void SwizzleSwipeWithEvent(NSView* view, SEL swiz_selector) {
|
||||
|
||||
- (id)initWithShell:(electron::NativeWindowMac*)shell
|
||||
styleMask:(NSUInteger)styleMask {
|
||||
SwizzleTabPickerGridAnimation();
|
||||
if ((self = [super initWithContentRect:ui::kWindowSizeDeterminedLater
|
||||
styleMask:styleMask
|
||||
backing:NSBackingStoreBuffered
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#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/client_frame_view_linux.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"
|
||||
@@ -85,11 +85,11 @@ gfx::Insets ElectronDesktopWindowTreeHostLinux::CalculateInsetsInDIP(
|
||||
return gfx::Insets();
|
||||
}
|
||||
|
||||
auto* const view = native_window_view_->GetClientFrameViewLinux();
|
||||
if (!view)
|
||||
auto* const layout = native_window_view_->GetLinuxFrameLayout();
|
||||
if (!layout)
|
||||
return {};
|
||||
|
||||
gfx::Insets insets = view->RestoredFrameBorderInsets();
|
||||
gfx::Insets insets = layout->RestoredFrameBorderInsets();
|
||||
return insets;
|
||||
}
|
||||
|
||||
@@ -117,14 +117,14 @@ void ElectronDesktopWindowTreeHostLinux::OnWindowStateChanged(
|
||||
|
||||
void ElectronDesktopWindowTreeHostLinux::OnWindowTiledStateChanged(
|
||||
ui::WindowTiledEdges new_tiled_edges) {
|
||||
if (auto* const view = native_window_view_->GetClientFrameViewLinux()) {
|
||||
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;
|
||||
view->set_tiled(tiled && !maximized);
|
||||
layout->set_tiled(tiled && !maximized);
|
||||
}
|
||||
UpdateFrameHints();
|
||||
ScheduleRelayout();
|
||||
@@ -180,15 +180,14 @@ void ElectronDesktopWindowTreeHostLinux::OnDeviceScaleFactorChanged() {
|
||||
|
||||
void ElectronDesktopWindowTreeHostLinux::UpdateFrameHints() {
|
||||
if (base::FeatureList::IsEnabled(features::kWaylandWindowDecorations)) {
|
||||
auto* const view = native_window_view_->GetClientFrameViewLinux();
|
||||
if (!view)
|
||||
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 =
|
||||
view->GetWidget()->GetWindowBoundsInScreen().size();
|
||||
const gfx::Size widget_size = GetWidget()->GetWindowBoundsInScreen().size();
|
||||
|
||||
if (SupportsClientFrameShadow()) {
|
||||
auto insets = CalculateInsetsInDIP(window_state);
|
||||
@@ -196,7 +195,7 @@ void ElectronDesktopWindowTreeHostLinux::UpdateFrameHints() {
|
||||
window->SetInputRegion(std::nullopt);
|
||||
} else {
|
||||
gfx::Rect input_bounds(widget_size);
|
||||
input_bounds.Inset(insets - view->GetInputInsets());
|
||||
input_bounds.Inset(insets - layout->GetInputInsets());
|
||||
input_bounds = gfx::ScaleToEnclosingRect(input_bounds, scale);
|
||||
window->SetInputRegion(
|
||||
std::optional<std::vector<gfx::Rect>>({input_bounds}));
|
||||
@@ -210,8 +209,8 @@ void ElectronDesktopWindowTreeHostLinux::UpdateFrameHints() {
|
||||
// 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 = view->GetRoundedWindowContentBounds();
|
||||
gfx::RectF rectf(view->GetWindowContentBounds());
|
||||
SkRRect rrect = layout->GetRoundedWindowContentBounds();
|
||||
gfx::RectF rectf(layout->GetWindowContentBounds());
|
||||
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
|
||||
@@ -245,7 +244,8 @@ void ElectronDesktopWindowTreeHostLinux::UpdateFrameHints() {
|
||||
|
||||
auto translucent_top_area_rect = SkIRect::MakeXYWH(
|
||||
rect.x(), rect.y(), rect.width(),
|
||||
std::ceil(view->GetTranslucentTopAreaHeight() * scale - rect.y()));
|
||||
std::ceil(layout->GetTranslucentTopAreaHeight() * scale -
|
||||
rect.y()));
|
||||
region.op(translucent_top_area_rect, SkRegion::kDifference_Op);
|
||||
|
||||
// Convert the region to a list of rectangles.
|
||||
@@ -256,7 +256,7 @@ void ElectronDesktopWindowTreeHostLinux::UpdateFrameHints() {
|
||||
// The entire window except for the translucent top is opaque.
|
||||
gfx::Rect opaque_region_dip(widget_size);
|
||||
gfx::Insets insets;
|
||||
insets.set_top(view->GetTranslucentTopAreaHeight());
|
||||
insets.set_top(layout->GetTranslucentTopAreaHeight());
|
||||
opaque_region_dip.Inset(insets);
|
||||
opaque_region.push_back(
|
||||
gfx::ScaleToEnclosingRect(opaque_region_dip, scale));
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
namespace electron {
|
||||
|
||||
class ClientFrameViewLinux;
|
||||
class NativeWindowViews;
|
||||
|
||||
class ElectronDesktopWindowTreeHostLinux
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
#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 "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"
|
||||
@@ -20,7 +20,6 @@
|
||||
#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
|
||||
@@ -39,9 +38,6 @@ namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
// These values should be the same as Chromium uses.
|
||||
constexpr int kResizeBorder = 10;
|
||||
|
||||
ui::NavButtonProvider::ButtonState ButtonStateToNavButtonProviderState(
|
||||
views::Button::ButtonState state) {
|
||||
switch (state) {
|
||||
@@ -116,6 +112,7 @@ ClientFrameViewLinux::~ClientFrameViewLinux() {
|
||||
void ClientFrameViewLinux::Init(NativeWindowViews* window,
|
||||
views::Widget* frame) {
|
||||
FramelessView::Init(window, frame);
|
||||
linux_frame_layout_ = std::make_unique<LinuxCSDFrameLayout>(window);
|
||||
|
||||
// Unretained() is safe because the subscription is saved into an instance
|
||||
// member and thus will be cancelled upon the instance's destruction.
|
||||
@@ -123,11 +120,6 @@ void ClientFrameViewLinux::Init(NativeWindowViews* window,
|
||||
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_) {
|
||||
@@ -143,50 +135,11 @@ void ClientFrameViewLinux::Init(NativeWindowViews* window,
|
||||
}
|
||||
|
||||
gfx::Insets ClientFrameViewLinux::RestoredFrameBorderInsets() const {
|
||||
gfx::Insets insets = GetFrameProvider()->GetFrameThicknessDip();
|
||||
const gfx::Insets input = GetInputInsets();
|
||||
|
||||
auto expand_if_visible = [](int side_thickness, int min_band) {
|
||||
return side_thickness > 0 ? std::max(side_thickness, min_band) : 0;
|
||||
};
|
||||
|
||||
gfx::Insets merged;
|
||||
merged.set_top(expand_if_visible(insets.top(), input.top()));
|
||||
merged.set_left(expand_if_visible(insets.left(), input.left()));
|
||||
merged.set_bottom(expand_if_visible(insets.bottom(), input.bottom()));
|
||||
merged.set_right(expand_if_visible(insets.right(), input.right()));
|
||||
|
||||
return base::i18n::IsRTL() ? gfx::Insets::TLBR(merged.top(), merged.right(),
|
||||
merged.bottom(), merged.left())
|
||||
: merged;
|
||||
return linux_frame_layout_->RestoredFrameBorderInsets();
|
||||
}
|
||||
|
||||
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(RestoredFrameBorderInsets());
|
||||
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;
|
||||
LinuxFrameLayout* ClientFrameViewLinux::GetLinuxFrameLayout() const {
|
||||
return linux_frame_layout_.get();
|
||||
}
|
||||
|
||||
void ClientFrameViewLinux::OnNativeThemeUpdated(
|
||||
@@ -245,11 +198,6 @@ int ClientFrameViewLinux::NonClientHitTest(const gfx::Point& point) {
|
||||
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.
|
||||
@@ -287,11 +235,8 @@ void ClientFrameViewLinux::Layout(PassKey) {
|
||||
}
|
||||
|
||||
void ClientFrameViewLinux::OnPaint(gfx::Canvas* canvas) {
|
||||
if (!frame_->IsFullscreen()) {
|
||||
GetFrameProvider()->PaintWindowFrame(
|
||||
canvas, GetLocalBounds(), GetTitlebarBounds().bottom(),
|
||||
ShouldPaintAsActive(), GetInputInsets());
|
||||
}
|
||||
linux_frame_layout_->PaintWindowFrame(
|
||||
canvas, GetLocalBounds(), GetTitlebarBounds(), ShouldPaintAsActive());
|
||||
}
|
||||
|
||||
void ClientFrameViewLinux::PaintAsActiveChanged() {
|
||||
@@ -322,7 +267,7 @@ void ClientFrameViewLinux::UpdateThemeValues() {
|
||||
}
|
||||
|
||||
theme_values_.window_border_radius =
|
||||
GetFrameProvider()->GetTopCornerRadiusDip();
|
||||
linux_frame_layout_->GetFrameProvider()->GetTopCornerRadiusDip();
|
||||
|
||||
gtk::GtkStyleContextGet(headerbar_context, "min-height",
|
||||
&theme_values_.titlebar_min_height, nullptr);
|
||||
@@ -479,10 +424,6 @@ views::View* ClientFrameViewLinux::TargetForRect(views::View* root,
|
||||
return views::FrameView::TargetForRect(root, rect);
|
||||
}
|
||||
|
||||
int ClientFrameViewLinux::GetTranslucentTopAreaHeight() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
BEGIN_METADATA(ClientFrameViewLinux) END_METADATA
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -13,13 +13,12 @@
|
||||
#include "base/memory/raw_ptr_exclusion.h"
|
||||
#include "base/scoped_observation.h"
|
||||
#include "shell/browser/ui/views/frameless_view.h"
|
||||
#include "third_party/skia/include/core/SkRRect.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/linux/window_frame_provider.h"
|
||||
#include "ui/native_theme/native_theme.h"
|
||||
#include "ui/native_theme/native_theme_observer.h"
|
||||
#include "ui/views/controls/button/image_button.h"
|
||||
@@ -44,15 +43,7 @@ class ClientFrameViewLinux : public FramelessView,
|
||||
|
||||
// FramelessView:
|
||||
gfx::Insets RestoredFrameBorderInsets() const override;
|
||||
|
||||
gfx::Insets GetInputInsets() const;
|
||||
gfx::Rect GetWindowContentBounds() const;
|
||||
SkRRect GetRoundedWindowContentBounds() const;
|
||||
int GetTranslucentTopAreaHeight() const;
|
||||
|
||||
// Returns whether the frame is in a tiled state.
|
||||
bool tiled() const { return tiled_; }
|
||||
void set_tiled(bool tiled) { tiled_ = tiled; }
|
||||
LinuxFrameLayout* GetLinuxFrameLayout() const override;
|
||||
|
||||
protected:
|
||||
// ui::NativeThemeObserver:
|
||||
@@ -80,8 +71,6 @@ class ClientFrameViewLinux : public FramelessView,
|
||||
// Overridden from views::ViewTargeterDelegate
|
||||
views::View* TargetForRect(views::View* root, const gfx::Rect& rect) override;
|
||||
|
||||
ui::WindowFrameProvider* GetFrameProvider() const;
|
||||
|
||||
private:
|
||||
static constexpr int kNavButtonCount = 4;
|
||||
|
||||
@@ -123,6 +112,8 @@ class ClientFrameViewLinux : public FramelessView,
|
||||
gfx::Insets GetTitlebarContentInsets() const;
|
||||
gfx::Rect GetTitlebarContentBounds() const;
|
||||
|
||||
std::unique_ptr<LinuxFrameLayout> linux_frame_layout_;
|
||||
|
||||
raw_ptr<ui::NativeTheme> theme_;
|
||||
ThemeValues theme_values_;
|
||||
|
||||
@@ -134,16 +125,12 @@ class ClientFrameViewLinux : public FramelessView,
|
||||
std::vector<views::FrameButton> leading_frame_buttons_;
|
||||
std::vector<views::FrameButton> trailing_frame_buttons_;
|
||||
|
||||
bool host_supports_client_frame_shadow_ = false;
|
||||
|
||||
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_;
|
||||
|
||||
bool tiled_ = false;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -122,6 +122,13 @@ gfx::Size FramelessView::GetMaximumSize() const {
|
||||
return gfx::Size();
|
||||
return window_->GetMaximumSize();
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
LinuxFrameLayout* FramelessView::GetLinuxFrameLayout() const {
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
BEGIN_METADATA(FramelessView)
|
||||
END_METADATA
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
#include "ui/gfx/geometry/insets.h"
|
||||
#include "ui/views/window/non_client_view.h"
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
#include "shell/browser/ui/views/linux_frame_layout.h"
|
||||
#endif
|
||||
|
||||
namespace views {
|
||||
class Widget;
|
||||
}
|
||||
@@ -42,6 +46,10 @@ class FramelessView : public views::FrameView {
|
||||
// bounds of the view, used for CSD and resize targets on some platforms.
|
||||
virtual gfx::Insets RestoredFrameBorderInsets() const;
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
virtual LinuxFrameLayout* GetLinuxFrameLayout() const;
|
||||
#endif
|
||||
|
||||
NativeWindowViews* window() const { return window_; }
|
||||
views::Widget* frame() const { return frame_; }
|
||||
|
||||
|
||||
171
shell/browser/ui/views/linux_frame_layout.cc
Normal file
171
shell/browser/ui/views/linux_frame_layout.cc
Normal file
@@ -0,0 +1,171 @@
|
||||
// Copyright (c) 2025 Mitchell Cohen.
|
||||
// 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/linux_frame_layout.h"
|
||||
#include "base/i18n/rtl.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "shell/browser/ui/electron_desktop_window_tree_host_linux.h"
|
||||
#include "ui/gfx/canvas.h"
|
||||
#include "ui/gfx/geometry/insets.h"
|
||||
#include "ui/gfx/geometry/skia_conversions.h"
|
||||
#include "ui/linux/linux_ui.h"
|
||||
#include "ui/native_theme/native_theme.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
// This should match Chromium's value.
|
||||
constexpr int kResizeBorder = 10;
|
||||
// This should match FramelessView's inside resize band.
|
||||
constexpr int kResizeInsideBoundsSize = 5;
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
std::unique_ptr<LinuxFrameLayout> LinuxFrameLayout::Create(
|
||||
NativeWindowViews* window,
|
||||
bool wants_shadow) {
|
||||
if (x11_util::IsX11() || window->IsTranslucent() || !wants_shadow) {
|
||||
return std::make_unique<LinuxUndecoratedFrameLayout>(window);
|
||||
} else {
|
||||
return std::make_unique<LinuxCSDFrameLayout>(window);
|
||||
}
|
||||
}
|
||||
|
||||
LinuxCSDFrameLayout::LinuxCSDFrameLayout(NativeWindowViews* window)
|
||||
: window_(window) {
|
||||
host_supports_client_frame_shadow_ = SupportsClientFrameShadow();
|
||||
}
|
||||
|
||||
bool LinuxCSDFrameLayout::tiled() const {
|
||||
return tiled_;
|
||||
}
|
||||
|
||||
void LinuxCSDFrameLayout::set_tiled(bool tiled) {
|
||||
tiled_ = tiled;
|
||||
}
|
||||
|
||||
gfx::Insets LinuxCSDFrameLayout::RestoredFrameBorderInsets() const {
|
||||
gfx::Insets insets = GetFrameProvider()->GetFrameThicknessDip();
|
||||
const gfx::Insets input = GetInputInsets();
|
||||
|
||||
auto expand_if_visible = [](int side_thickness, int min_band) {
|
||||
return side_thickness > 0 ? std::max(side_thickness, min_band) : 0;
|
||||
};
|
||||
|
||||
gfx::Insets merged;
|
||||
merged.set_top(expand_if_visible(insets.top(), input.top()));
|
||||
merged.set_left(expand_if_visible(insets.left(), input.left()));
|
||||
merged.set_bottom(expand_if_visible(insets.bottom(), input.bottom()));
|
||||
merged.set_right(expand_if_visible(insets.right(), input.right()));
|
||||
|
||||
return base::i18n::IsRTL() ? gfx::Insets::TLBR(merged.top(), merged.right(),
|
||||
merged.bottom(), merged.left())
|
||||
: merged;
|
||||
}
|
||||
|
||||
gfx::Insets LinuxCSDFrameLayout::GetInputInsets() const {
|
||||
bool showing_shadow = host_supports_client_frame_shadow_ &&
|
||||
!window_->IsMaximized() && !window_->IsFullscreen();
|
||||
return gfx::Insets(showing_shadow ? kResizeBorder : 0);
|
||||
}
|
||||
|
||||
bool LinuxCSDFrameLayout::SupportsClientFrameShadow() const {
|
||||
auto* tree_host = static_cast<ElectronDesktopWindowTreeHostLinux*>(
|
||||
ElectronDesktopWindowTreeHostLinux::GetHostForWidget(
|
||||
window_->GetAcceleratedWidget()));
|
||||
return tree_host->SupportsClientFrameShadow();
|
||||
}
|
||||
|
||||
void LinuxCSDFrameLayout::PaintWindowFrame(gfx::Canvas* canvas,
|
||||
gfx::Rect local_bounds,
|
||||
gfx::Rect titlebar_bounds,
|
||||
bool active) {
|
||||
GetFrameProvider()->PaintWindowFrame(
|
||||
canvas, local_bounds, titlebar_bounds.bottom(), active, GetInputInsets());
|
||||
}
|
||||
|
||||
gfx::Rect LinuxCSDFrameLayout::GetWindowContentBounds() const {
|
||||
gfx::Rect content_bounds = window_->widget()->GetWindowBoundsInScreen();
|
||||
content_bounds.Inset(RestoredFrameBorderInsets());
|
||||
return content_bounds;
|
||||
}
|
||||
|
||||
SkRRect LinuxCSDFrameLayout::GetRoundedWindowContentBounds() const {
|
||||
SkRect rect = gfx::RectToSkRect(GetWindowContentBounds());
|
||||
SkRRect rrect;
|
||||
|
||||
if (!window_->IsMaximized()) {
|
||||
float radius = GetFrameProvider()->GetTopCornerRadiusDip();
|
||||
SkPoint round_point{radius, radius};
|
||||
SkPoint radii[] = {round_point, round_point, {}, {}};
|
||||
rrect.setRectRadii(rect, radii);
|
||||
} else {
|
||||
rrect.setRect(rect);
|
||||
}
|
||||
|
||||
return rrect;
|
||||
}
|
||||
|
||||
int LinuxCSDFrameLayout::GetTranslucentTopAreaHeight() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ui::WindowFrameProvider* LinuxCSDFrameLayout::GetFrameProvider() const {
|
||||
return ui::LinuxUiTheme::GetForProfile(nullptr)->GetWindowFrameProvider(
|
||||
!host_supports_client_frame_shadow_, tiled(), window_->IsMaximized());
|
||||
}
|
||||
|
||||
LinuxUndecoratedFrameLayout::LinuxUndecoratedFrameLayout(
|
||||
NativeWindowViews* window)
|
||||
: window_(window) {}
|
||||
|
||||
gfx::Insets LinuxUndecoratedFrameLayout::RestoredFrameBorderInsets() const {
|
||||
return gfx::Insets();
|
||||
}
|
||||
|
||||
gfx::Insets LinuxUndecoratedFrameLayout::GetInputInsets() const {
|
||||
return gfx::Insets(kResizeInsideBoundsSize);
|
||||
}
|
||||
|
||||
bool LinuxUndecoratedFrameLayout::SupportsClientFrameShadow() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LinuxUndecoratedFrameLayout::tiled() const {
|
||||
return tiled_;
|
||||
}
|
||||
|
||||
void LinuxUndecoratedFrameLayout::set_tiled(bool tiled) {
|
||||
tiled_ = tiled;
|
||||
}
|
||||
|
||||
void LinuxUndecoratedFrameLayout::PaintWindowFrame(gfx::Canvas* canvas,
|
||||
gfx::Rect local_bounds,
|
||||
gfx::Rect titlebar_bounds,
|
||||
bool active) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
gfx::Rect LinuxUndecoratedFrameLayout::GetWindowContentBounds() const {
|
||||
// With no transparent insets, widget bounds and logical bounds match.
|
||||
return window_->widget()->GetWindowBoundsInScreen();
|
||||
}
|
||||
|
||||
SkRRect LinuxUndecoratedFrameLayout::GetRoundedWindowContentBounds() const {
|
||||
SkRRect rrect;
|
||||
rrect.setRect(gfx::RectToSkRect(GetWindowContentBounds()));
|
||||
return rrect;
|
||||
}
|
||||
|
||||
int LinuxUndecoratedFrameLayout::GetTranslucentTopAreaHeight() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ui::WindowFrameProvider* LinuxUndecoratedFrameLayout::GetFrameProvider() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
117
shell/browser/ui/views/linux_frame_layout.h
Normal file
117
shell/browser/ui/views/linux_frame_layout.h
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright (c) 2025 Mitchell Cohen.
|
||||
// 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_LINUX_FRAME_LAYOUT_H_
|
||||
#define ELECTRON_SHELL_BROWSER_UI_VIEWS_LINUX_FRAME_LAYOUT_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/i18n/rtl.h"
|
||||
#include "shell/browser/linux/x11_util.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "shell/browser/ui/electron_desktop_window_tree_host_linux.h"
|
||||
#include "third_party/skia/include/core/SkRRect.h"
|
||||
#include "ui/base/ozone_buildflags.h"
|
||||
#include "ui/gfx/canvas.h"
|
||||
#include "ui/gfx/geometry/insets.h"
|
||||
#include "ui/linux/linux_ui.h"
|
||||
#include "ui/linux/window_frame_provider.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
class NativeWindowViews;
|
||||
|
||||
// Shared helper for CSD layout and frame painting on Linux (shadows, resize
|
||||
// regions, titlebars, etc.). Also helps views determine insets and perform
|
||||
// bounds conversions between widget and logical coordinates.
|
||||
class LinuxFrameLayout {
|
||||
public:
|
||||
virtual ~LinuxFrameLayout() = default;
|
||||
|
||||
static std::unique_ptr<LinuxFrameLayout> Create(NativeWindowViews* window,
|
||||
bool wants_shadow);
|
||||
|
||||
// Insets from the transparent widget border to the opaque part of the window
|
||||
virtual gfx::Insets RestoredFrameBorderInsets() const = 0;
|
||||
// Insets for parts of the surface that should be counted for user input
|
||||
virtual gfx::Insets GetInputInsets() const = 0;
|
||||
|
||||
virtual bool SupportsClientFrameShadow() const = 0;
|
||||
|
||||
virtual bool tiled() const = 0;
|
||||
virtual void set_tiled(bool tiled) = 0;
|
||||
|
||||
virtual void PaintWindowFrame(gfx::Canvas* canvas,
|
||||
gfx::Rect local_bounds,
|
||||
gfx::Rect titlebar_bounds,
|
||||
bool active) = 0;
|
||||
|
||||
// The logical bounds of the window
|
||||
virtual gfx::Rect GetWindowContentBounds() const = 0;
|
||||
// The logical bounds as a rounded rect with corner radii applied
|
||||
virtual SkRRect GetRoundedWindowContentBounds() const = 0;
|
||||
|
||||
virtual int GetTranslucentTopAreaHeight() const = 0;
|
||||
|
||||
virtual ui::WindowFrameProvider* GetFrameProvider() const = 0;
|
||||
};
|
||||
|
||||
// Client-side decoration (CSD) Linux frame layout implementation.
|
||||
class LinuxCSDFrameLayout : public LinuxFrameLayout {
|
||||
public:
|
||||
explicit LinuxCSDFrameLayout(NativeWindowViews* window);
|
||||
~LinuxCSDFrameLayout() override = default;
|
||||
|
||||
gfx::Insets RestoredFrameBorderInsets() const override;
|
||||
gfx::Insets GetInputInsets() const override;
|
||||
bool SupportsClientFrameShadow() const override;
|
||||
bool tiled() const override;
|
||||
void set_tiled(bool tiled) override;
|
||||
void PaintWindowFrame(gfx::Canvas* canvas,
|
||||
gfx::Rect local_bounds,
|
||||
gfx::Rect titlebar_bounds,
|
||||
bool active) override;
|
||||
gfx::Rect GetWindowContentBounds() const override;
|
||||
SkRRect GetRoundedWindowContentBounds() const override;
|
||||
int GetTranslucentTopAreaHeight() const override;
|
||||
ui::WindowFrameProvider* GetFrameProvider() const override;
|
||||
|
||||
private:
|
||||
raw_ptr<NativeWindowViews> window_;
|
||||
bool tiled_ = false;
|
||||
bool host_supports_client_frame_shadow_ = false;
|
||||
};
|
||||
|
||||
// No-decoration Linux frame layout implementation.
|
||||
//
|
||||
// Intended for cases where we do not allocate a transparent inset area around
|
||||
// the window (e.g. X11 / server-side decorations, or when insets are disabled).
|
||||
// All inset math returns 0 and frame painting is skipped.
|
||||
class LinuxUndecoratedFrameLayout : public LinuxFrameLayout {
|
||||
public:
|
||||
explicit LinuxUndecoratedFrameLayout(NativeWindowViews* window);
|
||||
~LinuxUndecoratedFrameLayout() override = default;
|
||||
|
||||
gfx::Insets RestoredFrameBorderInsets() const override;
|
||||
gfx::Insets GetInputInsets() const override;
|
||||
bool SupportsClientFrameShadow() const override;
|
||||
bool tiled() const override;
|
||||
void set_tiled(bool tiled) override;
|
||||
void PaintWindowFrame(gfx::Canvas* canvas,
|
||||
gfx::Rect local_bounds,
|
||||
gfx::Rect titlebar_bounds,
|
||||
bool active) override;
|
||||
gfx::Rect GetWindowContentBounds() const override;
|
||||
SkRRect GetRoundedWindowContentBounds() const override;
|
||||
int GetTranslucentTopAreaHeight() const override;
|
||||
ui::WindowFrameProvider* GetFrameProvider() const override;
|
||||
|
||||
private:
|
||||
raw_ptr<NativeWindowViews> window_;
|
||||
bool tiled_ = false;
|
||||
};
|
||||
} // namespace electron
|
||||
|
||||
#endif // ELECTRON_SHELL_BROWSER_UI_VIEWS_LINUX_FRAME_LAYOUT_H_
|
||||
@@ -60,6 +60,13 @@ OpaqueFrameView::~OpaqueFrameView() = default;
|
||||
|
||||
void OpaqueFrameView::Init(NativeWindowViews* window, views::Widget* frame) {
|
||||
FramelessView::Init(window, frame);
|
||||
linux_frame_layout_ = LinuxFrameLayout::Create(window, window->HasShadow());
|
||||
|
||||
// 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)));
|
||||
|
||||
if (!window->IsWindowControlsOverlayEnabled())
|
||||
return;
|
||||
@@ -88,16 +95,12 @@ void OpaqueFrameView::Init(NativeWindowViews* window, views::Widget* frame) {
|
||||
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);
|
||||
auto insets = RestoredFrameBorderInsets();
|
||||
return ResizingBorderHitTestImpl(
|
||||
point, insets.IsEmpty() ? linux_frame_layout_->GetInputInsets() : insets);
|
||||
}
|
||||
|
||||
void OpaqueFrameView::InvalidateCaptionButtons() {
|
||||
@@ -108,40 +111,28 @@ void OpaqueFrameView::InvalidateCaptionButtons() {
|
||||
}
|
||||
|
||||
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()));
|
||||
gfx::Rect client_bounds = bounds();
|
||||
if (!frame_->IsFullscreen()) {
|
||||
client_bounds.Inset(FrameBorderInsets(false));
|
||||
}
|
||||
|
||||
return FramelessView::GetBoundsForClientView();
|
||||
return client_bounds;
|
||||
}
|
||||
|
||||
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);
|
||||
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 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;
|
||||
int frame_component = ResizingBorderHitTest(point);
|
||||
if (frame_component != HTNOWHERE)
|
||||
return frame_component;
|
||||
|
||||
if (window()->IsWindowControlsOverlayEnabled()) {
|
||||
if (HitTestCaptionButton(close_button_, point))
|
||||
return HTCLOSE;
|
||||
if (HitTestCaptionButton(restore_button_, point))
|
||||
@@ -185,41 +176,53 @@ void OpaqueFrameView::Layout(PassKey) {
|
||||
|
||||
// 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;
|
||||
gfx::Rect client_bounds = GetBoundsForClientView();
|
||||
available_space_leading_x_ = client_bounds.x() + top_area_padding.leading;
|
||||
available_space_trailing_x_ =
|
||||
client_bounds.right() - top_area_padding.trailing;
|
||||
minimum_size_for_buttons_ =
|
||||
available_space_leading_x_ + width() - available_space_trailing_x_;
|
||||
(available_space_leading_x_ - client_bounds.x()) +
|
||||
(client_bounds.right() - 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;
|
||||
int container_x =
|
||||
placed_trailing_button_ ? available_space_trailing_x_ : client_bounds.x();
|
||||
caption_button_placeholder_container_->SetBounds(
|
||||
container_x, insets.top(), minimum_size_for_buttons_ - insets.width(),
|
||||
height - insets.top());
|
||||
|
||||
container_x, client_bounds.y(), minimum_size_for_buttons_, height);
|
||||
LayoutWindowControlsOverlay();
|
||||
}
|
||||
|
||||
void OpaqueFrameView::OnPaint(gfx::Canvas* canvas) {
|
||||
if (!window()->IsWindowControlsOverlayEnabled())
|
||||
if (frame()->IsFullscreen())
|
||||
return;
|
||||
|
||||
if (frame()->IsFullscreen())
|
||||
// Titlebar height must be at least the frame border insets to avoid
|
||||
// a negative height calculation in the GTK frame provider. We add 1 to
|
||||
// ensure it's always positive even when insets are 0.
|
||||
int top_area_height = RestoredFrameBorderInsets().top() + 1;
|
||||
|
||||
linux_frame_layout_->PaintWindowFrame(
|
||||
canvas, GetLocalBounds(), gfx::Rect(0, 0, width(), top_area_height),
|
||||
ShouldPaintAsActive());
|
||||
|
||||
if (!window()->IsWindowControlsOverlayEnabled())
|
||||
return;
|
||||
|
||||
UpdateFrameCaptionButtons();
|
||||
}
|
||||
|
||||
void OpaqueFrameView::PaintAsActiveChanged() {
|
||||
if (!window()->IsWindowControlsOverlayEnabled())
|
||||
return;
|
||||
if (window()->IsWindowControlsOverlayEnabled()) {
|
||||
UpdateCaptionButtonPlaceholderContainerBackground();
|
||||
UpdateFrameCaptionButtons();
|
||||
}
|
||||
|
||||
UpdateCaptionButtonPlaceholderContainerBackground();
|
||||
UpdateFrameCaptionButtons();
|
||||
InvalidateLayout();
|
||||
SchedulePaint();
|
||||
}
|
||||
|
||||
void OpaqueFrameView::UpdateFrameCaptionButtons() {
|
||||
@@ -284,7 +287,8 @@ void OpaqueFrameView::LayoutWindowControlsOverlay() {
|
||||
: caption_button_placeholder_container_->size().height() + 1;
|
||||
}
|
||||
int overlay_width = caption_button_placeholder_container_->size().width();
|
||||
int bounding_rect_width = width() - overlay_width;
|
||||
gfx::Rect client_bounds = GetBoundsForClientView();
|
||||
int bounding_rect_width = client_bounds.width() - overlay_width;
|
||||
auto bounding_rect =
|
||||
GetMirroredRect(gfx::Rect(0, 0, bounding_rect_width, overlay_height));
|
||||
|
||||
@@ -317,8 +321,9 @@ views::Button* OpaqueFrameView::CreateButton(
|
||||
}
|
||||
|
||||
gfx::Insets OpaqueFrameView::FrameBorderInsets(bool restored) const {
|
||||
return !restored && IsFrameCondensed() ? gfx::Insets()
|
||||
: RestoredFrameBorderInsets();
|
||||
return !restored && IsFrameCondensed()
|
||||
? gfx::Insets()
|
||||
: linux_frame_layout_->RestoredFrameBorderInsets();
|
||||
}
|
||||
|
||||
int OpaqueFrameView::FrameTopBorderThickness(bool restored) const {
|
||||
@@ -331,8 +336,7 @@ int OpaqueFrameView::FrameTopBorderThickness(bool restored) const {
|
||||
OpaqueFrameView::TopAreaPadding OpaqueFrameView::GetTopAreaPadding(
|
||||
bool has_leading_buttons,
|
||||
bool has_trailing_buttons) const {
|
||||
const auto padding = FrameBorderInsets(false);
|
||||
return TopAreaPadding{padding.left(), padding.right()};
|
||||
return {};
|
||||
}
|
||||
|
||||
bool OpaqueFrameView::IsFrameCondensed() const {
|
||||
@@ -340,7 +344,11 @@ bool OpaqueFrameView::IsFrameCondensed() const {
|
||||
}
|
||||
|
||||
gfx::Insets OpaqueFrameView::RestoredFrameBorderInsets() const {
|
||||
return {};
|
||||
return linux_frame_layout_->RestoredFrameBorderInsets();
|
||||
}
|
||||
|
||||
LinuxFrameLayout* OpaqueFrameView::GetLinuxFrameLayout() const {
|
||||
return linux_frame_layout_.get();
|
||||
}
|
||||
|
||||
gfx::Insets OpaqueFrameView::RestoredFrameEdgeInsets() const {
|
||||
@@ -378,7 +386,7 @@ int OpaqueFrameView::DefaultCaptionButtonY(bool restored) const {
|
||||
// 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()
|
||||
? 0
|
||||
: OpaqueBrowserFrameViewLayout::kFrameShadowThickness;
|
||||
}
|
||||
|
||||
@@ -462,7 +470,8 @@ void OpaqueFrameView::HideButton(views::FrameButton button_id) {
|
||||
void OpaqueFrameView::SetBoundsForButton(views::FrameButton button_id,
|
||||
views::Button* button,
|
||||
ButtonAlignment alignment) {
|
||||
const int caption_y = CaptionButtonY(button_id, false);
|
||||
gfx::Rect client_bounds = GetBoundsForClientView();
|
||||
const int caption_y = CaptionButtonY(button_id, false) + client_bounds.y();
|
||||
|
||||
// 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
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "chrome/browser/ui/view_ids.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/linux/window_button_order_observer.h"
|
||||
#include "ui/views/controls/button/button.h"
|
||||
@@ -40,6 +41,7 @@ class OpaqueFrameView : public FramelessView {
|
||||
int ResizingBorderHitTest(const gfx::Point& point) override;
|
||||
void InvalidateCaptionButtons() override;
|
||||
gfx::Insets RestoredFrameBorderInsets() const override;
|
||||
LinuxFrameLayout* GetLinuxFrameLayout() const override;
|
||||
|
||||
// views::FrameView:
|
||||
gfx::Rect GetBoundsForClientView() const override;
|
||||
@@ -163,6 +165,8 @@ class OpaqueFrameView : public FramelessView {
|
||||
bool leading_spacing,
|
||||
bool is_leading_button) const;
|
||||
|
||||
std::unique_ptr<LinuxFrameLayout> linux_frame_layout_;
|
||||
|
||||
// Window controls.
|
||||
raw_ptr<views::Button> minimize_button_;
|
||||
raw_ptr<views::Button> maximize_button_;
|
||||
|
||||
@@ -413,6 +413,7 @@ bool IsAllowedOption(const std::string_view option) {
|
||||
"--inspect-port",
|
||||
"--inspect-publish-uid",
|
||||
"--experimental-network-inspection",
|
||||
"--experimental-transform-types",
|
||||
});
|
||||
|
||||
// This should be aligned with what's possible to set via the process object.
|
||||
|
||||
7
spec/fixtures/type-stripping/basic.ts
vendored
Normal file
7
spec/fixtures/type-stripping/basic.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import { app } from 'electron/main';
|
||||
|
||||
const logMessage = (message: string): void => console.log(message);
|
||||
|
||||
logMessage('running');
|
||||
|
||||
app.exit(0);
|
||||
9
spec/fixtures/type-stripping/transform-types-node.ts
vendored
Normal file
9
spec/fixtures/type-stripping/transform-types-node.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
enum Test {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
}
|
||||
|
||||
console.log(Test.A);
|
||||
|
||||
process.exit(0);
|
||||
11
spec/fixtures/type-stripping/transform-types.ts
vendored
Normal file
11
spec/fixtures/type-stripping/transform-types.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { app } from 'electron/main';
|
||||
|
||||
enum Test {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
}
|
||||
|
||||
console.log(Test.A);
|
||||
|
||||
app.exit(0);
|
||||
@@ -1030,4 +1030,26 @@ describe('node feature', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('type stripping', () => {
|
||||
it('strips TypeScript types automatically in the main process', async () => {
|
||||
const child = childProcess.spawn(process.execPath, [path.join(fixtures, 'type-stripping', 'basic.ts')]);
|
||||
const [code] = await once(child, 'exit');
|
||||
expect(code).to.equal(0);
|
||||
});
|
||||
|
||||
it('will not transform TypeScript types without --experimental-transform-types', async () => {
|
||||
const child = childProcess.spawn(process.execPath, [path.join(fixtures, 'type-stripping', 'transform-types-node.ts')], {
|
||||
env: { ELECTRON_RUN_AS_NODE: 'true' }
|
||||
});
|
||||
const [code] = await once(child, 'exit');
|
||||
expect(code).to.not.equal(0);
|
||||
});
|
||||
|
||||
it('transforms TypeScript types with --experimental-transform-types', async () => {
|
||||
const child = childProcess.spawn(process.execPath, ['--experimental-transform-types', path.join(fixtures, 'type-stripping', 'transform-types.ts')]);
|
||||
const [code] = await once(child, 'exit');
|
||||
expect(code).to.equal(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user