зеркало из https://github.com/electron/electron.git
feat: allow restoring macOS BrowserWindow state across relaunches
This commit is contained in:
Родитель
b684a98267
Коммит
ec539d6f27
|
@ -41,6 +41,7 @@
|
|||
* `skipTaskbar` boolean (optional) _macOS_ _Windows_ - Whether to show the window in taskbar.
|
||||
Default is `false`.
|
||||
* `hiddenInMissionControl` boolean (optional) _macOS_ - Whether window should be hidden when the user toggles into mission control.
|
||||
* `restoreOnRelaunch` boolean (optional) _macOS_ - Whether the window should launch with full fidelity across app restarts.
|
||||
* `kiosk` boolean (optional) - Whether the window is in kiosk mode. Default is `false`.
|
||||
* `title` string (optional) - Default window title. Default is `"Electron"`. If the HTML tag `<title>` is defined in the HTML file loaded by `loadURL()`, this property will be ignored.
|
||||
* `icon` ([NativeImage](../native-image.md) | string) (optional) - The window icon. On Windows it is
|
||||
|
|
|
@ -28,11 +28,12 @@ void ElectronBrowserMainParts::PreCreateMainMessageLoop() {
|
|||
|
||||
PreCreateMainMessageLoopCommon();
|
||||
|
||||
// Prevent Cocoa from turning command-line arguments into
|
||||
// |-application:openFiles:|, since we already handle them directly.
|
||||
[[NSUserDefaults standardUserDefaults]
|
||||
setObject:@"NO"
|
||||
forKey:@"NSTreatUnknownArgumentsAsOpen"];
|
||||
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
|
||||
// Prevent Cocoa from turning command-line arguments into -[NSApplication
|
||||
// application:openFile:], because they are handled directly. @"NO" looks
|
||||
// like a mistake, but the value really is supposed to be a string.
|
||||
[defaults setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"];
|
||||
[defaults setBool:NO forKey:@"NSWindowRestoresWorkspaceAtLaunch"];
|
||||
|
||||
if (!device::GeolocationSystemPermissionManager::GetInstance()) {
|
||||
device::GeolocationSystemPermissionManager::SetInstance(
|
||||
|
|
|
@ -164,6 +164,8 @@ class NativeWindowMac : public NativeWindow,
|
|||
// Detach window from parent without destroying it.
|
||||
void DetachChildren() override;
|
||||
|
||||
void OnWindowStateRestorationDataChanged(const std::vector<uint8_t>& data);
|
||||
|
||||
void NotifyWindowWillEnterFullScreen();
|
||||
void NotifyWindowWillLeaveFullScreen();
|
||||
|
||||
|
@ -286,6 +288,8 @@ class NativeWindowMac : public NativeWindow,
|
|||
|
||||
bool user_set_bounds_maximized_ = false;
|
||||
|
||||
std::vector<uint8_t> state_restoration_data_;
|
||||
|
||||
// Simple (pre-Lion) Fullscreen Settings
|
||||
bool always_simple_fullscreen_ = false;
|
||||
bool is_simple_fullscreen_ = false;
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
#include "ui/gfx/skia_util.h"
|
||||
#include "ui/gl/gpu_switching_manager.h"
|
||||
#include "ui/views/background.h"
|
||||
#include "ui/views/cocoa/native_widget_mac_ns_window_host.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
#include "ui/views/window/native_frame_view_mac.h"
|
||||
|
||||
|
@ -261,6 +260,18 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
|||
window_delegate_ = [[ElectronNSWindowDelegate alloc] initWithShell:this];
|
||||
[window_ setDelegate:window_delegate_];
|
||||
|
||||
if (options.Get(options::kRestoreOnRestart, &restore) && !restore) {
|
||||
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
|
||||
NSData* restore_ns_data = [defaults objectForKey:@"state_restoration_data"];
|
||||
if (restore_ns_data != nil) {
|
||||
NSKeyedUnarchiver* decoder =
|
||||
[[NSKeyedUnarchiver alloc] initForReadingFromData:restore_ns_data
|
||||
error:nil];
|
||||
[window_ restoreStateWithCoder:decoder];
|
||||
state_restoration_data_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Only use native parent window for non-modal windows.
|
||||
if (parent && !is_modal()) {
|
||||
SetParentWindow(parent);
|
||||
|
@ -355,7 +366,14 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
|||
original_level_ = [window_ level];
|
||||
}
|
||||
|
||||
NativeWindowMac::~NativeWindowMac() = default;
|
||||
NativeWindowMac::~NativeWindowMac() {
|
||||
if (!state_restoration_data_.empty()) {
|
||||
NSData* data = [NSData dataWithBytes:state_restoration_data_.data()
|
||||
length:state_restoration_data_.size()];
|
||||
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
|
||||
[defaults setObject:data forKey:@"state_restoration_data"];
|
||||
}
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetContentView(views::View* view) {
|
||||
views::View* root_view = GetContentsView();
|
||||
|
@ -1668,6 +1686,12 @@ void NativeWindowMac::NotifyWindowLeaveFullScreen() {
|
|||
[window_ setTitlebarAppearsTransparent:YES];
|
||||
}
|
||||
|
||||
void NativeWindowMac::OnWindowStateRestorationDataChanged(
|
||||
const std::vector<uint8_t>& data) {
|
||||
LOG(INFO) << "OnWindowStateRestorationDataChanged";
|
||||
state_restoration_data_ = data;
|
||||
}
|
||||
|
||||
void NativeWindowMac::NotifyWindowWillEnterFullScreen() {
|
||||
UpdateVibrancyRadii(true);
|
||||
}
|
||||
|
|
|
@ -29,9 +29,11 @@ class ScopedDisableResize {
|
|||
|
||||
} // namespace electron
|
||||
|
||||
@interface ElectronNSWindow : NativeWidgetMacNSWindow {
|
||||
@interface ElectronNSWindow
|
||||
: NativeWidgetMacNSWindow <NSKeyedArchiverDelegate> {
|
||||
@private
|
||||
raw_ptr<electron::NativeWindowMac> shell_;
|
||||
BOOL _willUpdateRestorableState;
|
||||
}
|
||||
@property BOOL acceptsFirstMouse;
|
||||
@property BOOL enableLargerThanScreen;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "shell/browser/ui/cocoa/electron_ns_window.h"
|
||||
|
||||
#include "base/mac/mac_util.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "shell/browser/native_window_mac.h"
|
||||
#include "shell/browser/ui/cocoa/electron_preview_item.h"
|
||||
|
@ -23,6 +24,7 @@ int ScopedDisableResize::disable_resize_ = 0;
|
|||
@interface NSWindow (PrivateAPI)
|
||||
- (NSImage*)_cornerMask;
|
||||
- (int64_t)_resizeDirectionForMouseLocation:(CGPoint)location;
|
||||
- (BOOL)_isConsideredOpenForPersistentState;
|
||||
@end
|
||||
|
||||
// See components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
|
||||
|
@ -186,8 +188,64 @@ void SwizzleSwipeWithEvent(NSView* view, SEL swiz_selector) {
|
|||
return nil;
|
||||
}
|
||||
|
||||
// Called when the window is the delegate of the archiver passed to
|
||||
// |-encodeRestorableStateWithCoder:|, below. It prevents the archiver from
|
||||
// trying to encode the window or an NSView, say, to represent the first
|
||||
// responder. When AppKit calls |-encodeRestorableStateWithCoder:|, it
|
||||
// accomplishes the same thing by passing a custom coder.
|
||||
- (id)archiver:(NSKeyedArchiver*)archiver willEncodeObject:(id)object {
|
||||
if (object == self)
|
||||
return nil;
|
||||
if ([object isKindOfClass:[NSView class]])
|
||||
return nil;
|
||||
return object;
|
||||
}
|
||||
|
||||
- (void)saveRestorableState {
|
||||
if (![self _isConsideredOpenForPersistentState])
|
||||
return;
|
||||
|
||||
// On macOS 12+, create restorable state archives with secure encoding.
|
||||
NSKeyedArchiver* encoder = [[NSKeyedArchiver alloc]
|
||||
initRequiringSecureCoding:base::mac::MacOSMajorVersion() >= 12];
|
||||
encoder.delegate = self;
|
||||
[self encodeRestorableStateWithCoder:encoder];
|
||||
[encoder finishEncoding];
|
||||
NSData* restorableStateData = encoder.encodedData;
|
||||
|
||||
auto* bytes = static_cast<uint8_t const*>(restorableStateData.bytes);
|
||||
shell_->OnWindowStateRestorationDataChanged(
|
||||
std::vector<uint8_t>(bytes, bytes + restorableStateData.length));
|
||||
|
||||
_willUpdateRestorableState = NO;
|
||||
}
|
||||
|
||||
// AppKit calls -invalidateRestorableState when a property of the window which
|
||||
// affects its restorable state changes.
|
||||
- (void)invalidateRestorableState {
|
||||
[super invalidateRestorableState];
|
||||
|
||||
if ([self _isConsideredOpenForPersistentState]) {
|
||||
if (_willUpdateRestorableState)
|
||||
return;
|
||||
_willUpdateRestorableState = YES;
|
||||
[self performSelectorOnMainThread:@selector(saveRestorableState)
|
||||
withObject:nil
|
||||
waitUntilDone:NO
|
||||
modes:@[ NSDefaultRunLoopMode ]];
|
||||
} else if (_willUpdateRestorableState) {
|
||||
_willUpdateRestorableState = NO;
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self];
|
||||
}
|
||||
}
|
||||
|
||||
// NSWindow overrides.
|
||||
|
||||
- (void)dealloc {
|
||||
_willUpdateRestorableState = YES;
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self];
|
||||
}
|
||||
|
||||
- (void)rotateWithEvent:(NSEvent*)event {
|
||||
shell_->NotifyWindowRotateGesture(event.rotation);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ const char kMovable[] = "movable";
|
|||
const char kMinimizable[] = "minimizable";
|
||||
const char kMaximizable[] = "maximizable";
|
||||
const char kFullScreenable[] = "fullscreenable";
|
||||
const char kRestoreOnRestart[] = "restoreOnRestart";
|
||||
const char kClosable[] = "closable";
|
||||
const char kFullscreen[] = "fullscreen";
|
||||
const char kTrafficLightPosition[] = "trafficLightPosition";
|
||||
|
|
|
@ -29,6 +29,7 @@ extern const char kMovable[];
|
|||
extern const char kMinimizable[];
|
||||
extern const char kMaximizable[];
|
||||
extern const char kFullScreenable[];
|
||||
extern const char kRestoreOnRestart[];
|
||||
extern const char kClosable[];
|
||||
extern const char kHiddenInMissionControl[];
|
||||
extern const char kFullscreen[];
|
||||
|
|
Загрузка…
Ссылка в новой задаче