refactor: rename the atom directory to shell

This commit is contained in:
Samuel Attard 2019-06-19 13:43:10 -07:00 committed by Samuel Attard
parent 4575a4aae3
commit d7f07e8a80
631 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,18 @@
// Created by Mathias Leppich on 03/02/14.
// Copyright (c) 2014 Bit Bar. All rights reserved.
// Copyright (c) 2017 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_UI_COCOA_NSCOLOR_HEX_H_
#define ATOM_BROWSER_UI_COCOA_NSCOLOR_HEX_H_
#import <Cocoa/Cocoa.h>
@interface NSColor (Hex)
- (NSString*)hexadecimalValue;
- (NSString*)RGBAValue;
+ (NSColor*)colorWithHexColorString:(NSString*)hex;
@end
#endif // ATOM_BROWSER_UI_COCOA_NSCOLOR_HEX_H_

View file

@ -0,0 +1,77 @@
// Created by Mathias Leppich on 03/02/14.
// Copyright (c) 2014 Bit Bar. All rights reserved.
// Copyright (c) 2017 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/ui/cocoa/NSColor+Hex.h"
@implementation NSColor (Hex)
- (NSString*)RGBAValue {
double redFloatValue, greenFloatValue, blueFloatValue, alphaFloatValue;
NSColor* convertedColor =
[self colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
if (convertedColor) {
[convertedColor getRed:&redFloatValue
green:&greenFloatValue
blue:&blueFloatValue
alpha:&alphaFloatValue];
int redIntValue = redFloatValue * 255.99999f;
int greenIntValue = greenFloatValue * 255.99999f;
int blueIntValue = blueFloatValue * 255.99999f;
int alphaIntValue = alphaFloatValue * 255.99999f;
return
[NSString stringWithFormat:@"%02x%02x%02x%02x", redIntValue,
greenIntValue, blueIntValue, alphaIntValue];
}
return nil;
}
- (NSString*)hexadecimalValue {
double redFloatValue, greenFloatValue, blueFloatValue;
NSColor* convertedColor =
[self colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
if (convertedColor) {
[convertedColor getRed:&redFloatValue
green:&greenFloatValue
blue:&blueFloatValue
alpha:NULL];
int redIntValue = redFloatValue * 255.99999f;
int greenIntValue = greenFloatValue * 255.99999f;
int blueIntValue = blueFloatValue * 255.99999f;
return [NSString stringWithFormat:@"#%02x%02x%02x", redIntValue,
greenIntValue, blueIntValue];
}
return nil;
}
+ (NSColor*)colorWithHexColorString:(NSString*)inColorString {
unsigned colorCode = 0;
if (inColorString) {
NSScanner* scanner = [NSScanner scannerWithString:inColorString];
(void)[scanner scanHexInt:&colorCode]; // ignore error
}
unsigned char redByte = (unsigned char)(colorCode >> 16);
unsigned char greenByte = (unsigned char)(colorCode >> 8);
unsigned char blueByte = (unsigned char)(colorCode); // masks off high bits
return [NSColor colorWithCalibratedRed:(CGFloat)redByte / 0xff
green:(CGFloat)greenByte / 0xff
blue:(CGFloat)blueByte / 0xff
alpha:1.0];
}
@end

View file

@ -0,0 +1,17 @@
// Created by Kent Karlsson on 3/11/16.
// Copyright (c) 2016 Bit Bar. All rights reserved.
// Copyright (c) 2017 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_UI_COCOA_NSSTRING_ANSI_H_
#define ATOM_BROWSER_UI_COCOA_NSSTRING_ANSI_H_
#import <Foundation/Foundation.h>
@interface NSString (ANSI)
- (BOOL)containsANSICodes;
- (NSMutableAttributedString*)attributedStringParsingANSICodes;
@end
#endif // ATOM_BROWSER_UI_COCOA_NSSTRING_ANSI_H_

View file

@ -0,0 +1,166 @@
// Created by Kent Karlsson on 3/11/16.
// Copyright (c) 2016 Bit Bar. All rights reserved.
// Copyright (c) 2017 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/ui/cocoa/NSColor+Hex.h"
#include "atom/browser/ui/cocoa/NSString+ANSI.h"
#include "base/mac/scoped_nsobject.h"
@implementation NSMutableDictionary (ANSI)
- (NSMutableDictionary*)modifyAttributesForANSICodes:(NSString*)codes {
BOOL bold = NO;
NSFont* font = self[NSFontAttributeName];
NSArray* codeArray = [codes componentsSeparatedByString:@";"];
for (NSString* codeString in codeArray) {
int code = codeString.intValue;
switch (code) {
case 0:
[self removeAllObjects];
// remove italic and bold from font here
if (font)
self[NSFontAttributeName] = font;
break;
case 1:
case 22:
bold = (code == 1);
break;
// case 3: italic
// case 23: italic off
// case 4: underlined
// case 24: underlined off
case 30:
self[NSForegroundColorAttributeName] =
[NSColor colorWithHexColorString:bold ? @"7f7f7f" : @"000000"];
break;
case 31:
self[NSForegroundColorAttributeName] =
[NSColor colorWithHexColorString:bold ? @"cd0000" : @"ff0000"];
break;
case 32:
self[NSForegroundColorAttributeName] =
[NSColor colorWithHexColorString:bold ? @"00cd00" : @"00ff00"];
break;
case 33:
self[NSForegroundColorAttributeName] =
[NSColor colorWithHexColorString:bold ? @"cdcd00" : @"ffff00"];
break;
case 34:
self[NSForegroundColorAttributeName] =
[NSColor colorWithHexColorString:bold ? @"0000ee" : @"5c5cff"];
break;
case 35:
self[NSForegroundColorAttributeName] =
[NSColor colorWithHexColorString:bold ? @"cd00cd" : @"ff00ff"];
break;
case 36:
self[NSForegroundColorAttributeName] =
[NSColor colorWithHexColorString:bold ? @"00cdcd" : @"00ffff"];
break;
case 37:
self[NSForegroundColorAttributeName] =
[NSColor colorWithHexColorString:bold ? @"e5e5e5" : @"ffffff"];
break;
case 39:
[self removeObjectForKey:NSForegroundColorAttributeName];
break;
case 40:
self[NSBackgroundColorAttributeName] =
[NSColor colorWithHexColorString:@"7f7f7f"];
break;
case 41:
self[NSBackgroundColorAttributeName] =
[NSColor colorWithHexColorString:@"cd0000"];
break;
case 42:
self[NSBackgroundColorAttributeName] =
[NSColor colorWithHexColorString:@"00cd00"];
break;
case 43:
self[NSBackgroundColorAttributeName] =
[NSColor colorWithHexColorString:@"cdcd00"];
break;
case 44:
self[NSBackgroundColorAttributeName] =
[NSColor colorWithHexColorString:@"0000ee"];
break;
case 45:
self[NSBackgroundColorAttributeName] =
[NSColor colorWithHexColorString:@"cd00cd"];
break;
case 46:
self[NSBackgroundColorAttributeName] =
[NSColor colorWithHexColorString:@"00cdcd"];
break;
case 47:
self[NSBackgroundColorAttributeName] =
[NSColor colorWithHexColorString:@"e5e5e5"];
break;
case 49:
[self removeObjectForKey:NSBackgroundColorAttributeName];
break;
default:
break;
}
}
return self;
}
@end
@implementation NSString (ANSI)
- (BOOL)containsANSICodes {
return [self rangeOfString:@"\033["].location != NSNotFound;
}
- (NSMutableAttributedString*)attributedStringParsingANSICodes {
NSMutableAttributedString* result = [[NSMutableAttributedString alloc] init];
base::scoped_nsobject<NSMutableDictionary> attributes(
[[NSMutableDictionary alloc] init]);
NSArray* parts = [self componentsSeparatedByString:@"\033["];
[result appendAttributedString:[[[NSAttributedString alloc]
initWithString:parts.firstObject
attributes:nil] autorelease]];
for (NSString* part in
[parts subarrayWithRange:NSMakeRange(1, parts.count - 1)]) {
if (part.length == 0)
continue;
NSArray* sequence = [part componentsSeparatedByString:@"m"];
NSString* text = sequence.lastObject;
if (sequence.count < 2) {
[result
appendAttributedString:[[[NSAttributedString alloc]
initWithString:text
attributes:attributes] autorelease]];
} else if (sequence.count >= 2) {
text = [[sequence subarrayWithRange:NSMakeRange(1, sequence.count - 1)]
componentsJoinedByString:@"m"];
[attributes modifyAttributesForANSICodes:sequence[0]];
[result
appendAttributedString:[[[NSAttributedString alloc]
initWithString:text
attributes:attributes] autorelease]];
}
}
return result;
}
@end

View file

@ -0,0 +1,43 @@
// Copyright (c) 2017 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_UI_COCOA_ATOM_BUNDLE_MOVER_H_
#define ATOM_BROWSER_UI_COCOA_ATOM_BUNDLE_MOVER_H_
#include <string>
#include "native_mate/persistent_dictionary.h"
namespace atom {
namespace ui {
namespace cocoa {
class AtomBundleMover {
public:
static bool Move(mate::Arguments* args);
static bool IsCurrentAppInApplicationsFolder();
private:
static bool IsInApplicationsFolder(NSString* bundlePath);
static NSString* ContainingDiskImageDevice(NSString* bundlePath);
static void Relaunch(NSString* destinationPath);
static NSString* ShellQuotedString(NSString* string);
static bool CopyBundle(NSString* srcPath, NSString* dstPath);
static bool AuthorizedInstall(NSString* srcPath,
NSString* dstPath,
bool* canceled);
static bool IsApplicationAtPathRunning(NSString* bundlePath);
static bool DeleteOrTrash(NSString* path);
static bool Trash(NSString* path);
};
} // namespace cocoa
} // namespace ui
} // namespace atom
#endif // ATOM_BROWSER_UI_COCOA_ATOM_BUNDLE_MOVER_H_

View file

@ -0,0 +1,425 @@
// Copyright (c) 2017 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#import "atom/browser/ui/cocoa/atom_bundle_mover.h"
#include <string>
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
#import <Security/Security.h>
#import <dlfcn.h>
#import <sys/mount.h>
#import <sys/param.h>
#import "atom/browser/browser.h"
namespace atom {
namespace ui {
namespace cocoa {
bool AtomBundleMover::Move(mate::Arguments* args) {
// Path of the current bundle
NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
// Skip if the application is already in the Applications folder
if (IsInApplicationsFolder(bundlePath))
return true;
NSFileManager* fileManager = [NSFileManager defaultManager];
NSString* diskImageDevice = ContainingDiskImageDevice(bundlePath);
NSString* applicationsDirectory = [[NSSearchPathForDirectoriesInDomains(
NSApplicationDirectory, NSLocalDomainMask, true) lastObject]
stringByResolvingSymlinksInPath];
NSString* bundleName = [bundlePath lastPathComponent];
NSString* destinationPath =
[applicationsDirectory stringByAppendingPathComponent:bundleName];
// Check if we can write to the applications directory
// and then make sure that if the app already exists we can overwrite it
bool needAuthorization =
![fileManager isWritableFileAtPath:applicationsDirectory] |
([fileManager fileExistsAtPath:destinationPath] &&
![fileManager isWritableFileAtPath:destinationPath]);
// Activate app -- work-around for focus issues related to "scary file from
// internet" OS dialog.
if (![NSApp isActive]) {
[NSApp activateIgnoringOtherApps:true];
}
// Move to applications folder
if (needAuthorization) {
bool authorizationCanceled;
if (!AuthorizedInstall(bundlePath, destinationPath,
&authorizationCanceled)) {
if (authorizationCanceled) {
// User rejected the authorization request
args->ThrowError("User rejected the authorization request");
return false;
} else {
args->ThrowError(
"Failed to copy to applications directory even with authorization");
return false;
}
}
} else {
// If a copy already exists in the Applications folder, put it in the Trash
if ([fileManager fileExistsAtPath:destinationPath]) {
// But first, make sure that it's not running
if (IsApplicationAtPathRunning(destinationPath)) {
// Give the running app focus and terminate myself
[[NSTask
launchedTaskWithLaunchPath:@"/usr/bin/open"
arguments:[NSArray
arrayWithObject:destinationPath]]
waitUntilExit];
atom::Browser::Get()->Quit();
return true;
} else {
if (!Trash([applicationsDirectory
stringByAppendingPathComponent:bundleName])) {
args->ThrowError("Failed to delete existing application");
return false;
}
}
}
if (!CopyBundle(bundlePath, destinationPath)) {
args->ThrowError(
"Failed to copy current bundle to the applications folder");
return false;
}
}
// Trash the original app. It's okay if this fails.
// NOTE: This final delete does not work if the source bundle is in a network
// mounted volume.
// Calling rm or file manager's delete method doesn't work either. It's
// unlikely to happen but it'd be great if someone could fix this.
if (diskImageDevice == nil && !DeleteOrTrash(bundlePath)) {
// Could not delete original but we just don't care
}
// Relaunch.
Relaunch(destinationPath);
// Launched from within a disk image? -- unmount (if no files are open after 5
// seconds, otherwise leave it mounted).
if (diskImageDevice) {
NSString* script = [NSString
stringWithFormat:@"(/bin/sleep 5 && /usr/bin/hdiutil detach %@) &",
ShellQuotedString(diskImageDevice)];
[NSTask launchedTaskWithLaunchPath:@"/bin/sh"
arguments:[NSArray arrayWithObjects:@"-c", script,
nil]];
}
atom::Browser::Get()->Quit();
return true;
}
bool AtomBundleMover::IsCurrentAppInApplicationsFolder() {
return IsInApplicationsFolder([[NSBundle mainBundle] bundlePath]);
}
bool AtomBundleMover::IsInApplicationsFolder(NSString* bundlePath) {
// Check all the normal Application directories
NSArray* applicationDirs = NSSearchPathForDirectoriesInDomains(
NSApplicationDirectory, NSAllDomainsMask, true);
for (NSString* appDir in applicationDirs) {
if ([bundlePath hasPrefix:appDir])
return true;
}
// Also, handle the case that the user has some other Application directory
// (perhaps on a separate data partition).
if ([[bundlePath pathComponents] containsObject:@"Applications"])
return true;
return false;
}
NSString* AtomBundleMover::ContainingDiskImageDevice(NSString* bundlePath) {
NSString* containingPath = [bundlePath stringByDeletingLastPathComponent];
struct statfs fs;
if (statfs([containingPath fileSystemRepresentation], &fs) ||
(fs.f_flags & MNT_ROOTFS))
return nil;
NSString* device = [[NSFileManager defaultManager]
stringWithFileSystemRepresentation:fs.f_mntfromname
length:strlen(fs.f_mntfromname)];
NSTask* hdiutil = [[[NSTask alloc] init] autorelease];
[hdiutil setLaunchPath:@"/usr/bin/hdiutil"];
[hdiutil setArguments:[NSArray arrayWithObjects:@"info", @"-plist", nil]];
[hdiutil setStandardOutput:[NSPipe pipe]];
[hdiutil launch];
[hdiutil waitUntilExit];
NSData* data =
[[[hdiutil standardOutput] fileHandleForReading] readDataToEndOfFile];
NSDictionary* info =
[NSPropertyListSerialization propertyListWithData:data
options:NSPropertyListImmutable
format:NULL
error:NULL];
if (![info isKindOfClass:[NSDictionary class]])
return nil;
NSArray* images = (NSArray*)[info objectForKey:@"images"];
if (![images isKindOfClass:[NSArray class]])
return nil;
for (NSDictionary* image in images) {
if (![image isKindOfClass:[NSDictionary class]])
return nil;
id systemEntities = [image objectForKey:@"system-entities"];
if (![systemEntities isKindOfClass:[NSArray class]])
return nil;
for (NSDictionary* systemEntity in systemEntities) {
if (![systemEntity isKindOfClass:[NSDictionary class]])
return nil;
NSString* devEntry = [systemEntity objectForKey:@"dev-entry"];
if (![devEntry isKindOfClass:[NSString class]])
return nil;
if ([devEntry isEqualToString:device])
return device;
}
}
return nil;
}
bool AtomBundleMover::AuthorizedInstall(NSString* srcPath,
NSString* dstPath,
bool* canceled) {
if (canceled)
*canceled = false;
// Make sure that the destination path is an app bundle. We're essentially
// running 'sudo rm -rf' so we really don't want to screw this up.
if (![[dstPath pathExtension] isEqualToString:@"app"])
return false;
// Do some more checks
if ([[dstPath stringByTrimmingCharactersInSet:[NSCharacterSet
whitespaceCharacterSet]]
length] == 0)
return false;
if ([[srcPath stringByTrimmingCharactersInSet:[NSCharacterSet
whitespaceCharacterSet]]
length] == 0)
return false;
int pid, status;
AuthorizationRef myAuthorizationRef;
// Get the authorization
OSStatus err =
AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
kAuthorizationFlagDefaults, &myAuthorizationRef);
if (err != errAuthorizationSuccess)
return false;
AuthorizationItem myItems = {kAuthorizationRightExecute, 0, NULL, 0};
AuthorizationRights myRights = {1, &myItems};
AuthorizationFlags myFlags = (AuthorizationFlags)(
kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights |
kAuthorizationFlagPreAuthorize);
err = AuthorizationCopyRights(myAuthorizationRef, &myRights, NULL, myFlags,
NULL);
if (err != errAuthorizationSuccess) {
if (err == errAuthorizationCanceled && canceled)
*canceled = true;
goto fail;
}
static OSStatus (*security_AuthorizationExecuteWithPrivileges)(
AuthorizationRef authorization, const char* pathToTool,
AuthorizationFlags options, char* const* arguments,
FILE** communicationsPipe) = NULL;
if (!security_AuthorizationExecuteWithPrivileges) {
// On 10.7, AuthorizationExecuteWithPrivileges is deprecated. We want to
// still use it since there's no good alternative (without requiring code
// signing). We'll look up the function through dyld and fail if it is no
// longer accessible. If Apple removes the function entirely this will fail
// gracefully. If they keep the function and throw some sort of exception,
// this won't fail gracefully, but that's a risk we'll have to take for now.
security_AuthorizationExecuteWithPrivileges = (OSStatus(*)(
AuthorizationRef, const char*, AuthorizationFlags, char* const*,
FILE**))dlsym(RTLD_DEFAULT, "AuthorizationExecuteWithPrivileges");
}
if (!security_AuthorizationExecuteWithPrivileges)
goto fail;
// Delete the destination
{
char rf[] = "-rf";
char* args[] = {rf, (char*)[dstPath fileSystemRepresentation], NULL};
err = security_AuthorizationExecuteWithPrivileges(
myAuthorizationRef, "/bin/rm", kAuthorizationFlagDefaults, args, NULL);
if (err != errAuthorizationSuccess)
goto fail;
// Wait until it's done
pid = wait(&status);
if (pid == -1 || !WIFEXITED(status))
goto fail; // We don't care about exit status as the destination most
// likely does not exist
}
// Copy
{
char pR[] = "-pR";
char* args[] = {pR, (char*)[srcPath fileSystemRepresentation],
(char*)[dstPath fileSystemRepresentation], NULL};
err = security_AuthorizationExecuteWithPrivileges(
myAuthorizationRef, "/bin/cp", kAuthorizationFlagDefaults, args, NULL);
if (err != errAuthorizationSuccess)
goto fail;
// Wait until it's done
pid = wait(&status);
if (pid == -1 || !WIFEXITED(status) || WEXITSTATUS(status))
goto fail;
}
AuthorizationFree(myAuthorizationRef, kAuthorizationFlagDefaults);
return true;
fail:
AuthorizationFree(myAuthorizationRef, kAuthorizationFlagDefaults);
return false;
}
bool AtomBundleMover::CopyBundle(NSString* srcPath, NSString* dstPath) {
NSFileManager* fileManager = [NSFileManager defaultManager];
NSError* error = nil;
if ([fileManager copyItemAtPath:srcPath toPath:dstPath error:&error]) {
return true;
} else {
return false;
}
}
NSString* AtomBundleMover::ShellQuotedString(NSString* string) {
return [NSString
stringWithFormat:@"'%@'",
[string stringByReplacingOccurrencesOfString:@"'"
withString:@"'\\''"]];
}
void AtomBundleMover::Relaunch(NSString* destinationPath) {
// The shell script waits until the original app process terminates.
// This is done so that the relaunched app opens as the front-most app.
int pid = [[NSProcessInfo processInfo] processIdentifier];
// Command run just before running open /final/path
NSString* preOpenCmd = @"";
NSString* quotedDestinationPath = ShellQuotedString(destinationPath);
// Before we launch the new app, clear xattr:com.apple.quarantine to avoid
// duplicate "scary file from the internet" dialog.
preOpenCmd = [NSString
stringWithFormat:@"/usr/bin/xattr -d -r com.apple.quarantine %@",
quotedDestinationPath];
NSString* script =
[NSString stringWithFormat:
@"(while /bin/kill -0 %d >&/dev/null; do /bin/sleep 0.1; "
@"done; %@; /usr/bin/open %@) &",
pid, preOpenCmd, quotedDestinationPath];
[NSTask
launchedTaskWithLaunchPath:@"/bin/sh"
arguments:[NSArray arrayWithObjects:@"-c", script, nil]];
}
bool AtomBundleMover::IsApplicationAtPathRunning(NSString* bundlePath) {
bundlePath = [bundlePath stringByStandardizingPath];
for (NSRunningApplication* runningApplication in
[[NSWorkspace sharedWorkspace] runningApplications]) {
NSString* runningAppBundlePath =
[[[runningApplication bundleURL] path] stringByStandardizingPath];
if ([runningAppBundlePath isEqualToString:bundlePath]) {
return true;
}
}
return false;
}
bool AtomBundleMover::Trash(NSString* path) {
bool result = false;
if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_8) {
result = [[NSFileManager defaultManager]
trashItemAtURL:[NSURL fileURLWithPath:path]
resultingItemURL:NULL
error:NULL];
}
if (!result) {
result = [[NSWorkspace sharedWorkspace]
performFileOperation:NSWorkspaceRecycleOperation
source:[path stringByDeletingLastPathComponent]
destination:@""
files:[NSArray arrayWithObject:[path lastPathComponent]]
tag:NULL];
}
// As a last resort try trashing with AppleScript.
// This allows us to trash the app in macOS Sierra even when the app is
// running inside an app translocation image.
if (!result) {
auto* code = R"str(
set theFile to POSIX file "%@"
tell application "Finder"
move theFile to trash
end tell
)str";
NSAppleScript* appleScript = [[[NSAppleScript alloc]
initWithSource:[NSString stringWithFormat:@(code), path]] autorelease];
NSDictionary* errorDict = nil;
NSAppleEventDescriptor* scriptResult =
[appleScript executeAndReturnError:&errorDict];
result = (scriptResult != nil);
}
return result;
}
bool AtomBundleMover::DeleteOrTrash(NSString* path) {
NSError* error;
if ([[NSFileManager defaultManager] removeItemAtPath:path error:&error]) {
return true;
} else {
return Trash(path);
}
}
} // namespace cocoa
} // namespace ui
} // namespace atom

View file

@ -0,0 +1,47 @@
// Copyright (c) 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-CHROMIUM file.
#ifndef ATOM_BROWSER_UI_COCOA_BRY_INSPECTABLE_WEB_CONTENTS_VIEW_H_
#define ATOM_BROWSER_UI_COCOA_BRY_INSPECTABLE_WEB_CONTENTS_VIEW_H_
#import <AppKit/AppKit.h>
#include "base/mac/scoped_nsobject.h"
#include "chrome/browser/devtools/devtools_contents_resizing_strategy.h"
#include "ui/base/cocoa/base_view.h"
namespace atom {
class InspectableWebContentsViewMac;
}
using atom::InspectableWebContentsViewMac;
@interface AtomInspectableWebContentsView : BaseView <NSWindowDelegate> {
@private
atom::InspectableWebContentsViewMac* inspectableWebContentsView_;
base::scoped_nsobject<NSView> fake_view_;
base::scoped_nsobject<NSWindow> devtools_window_;
BOOL devtools_visible_;
BOOL devtools_docked_;
BOOL devtools_is_first_responder_;
DevToolsContentsResizingStrategy strategy_;
}
- (instancetype)initWithInspectableWebContentsViewMac:
(InspectableWebContentsViewMac*)view;
- (void)removeObservers;
- (void)notifyDevToolsFocused;
- (void)setDevToolsVisible:(BOOL)visible activate:(BOOL)activate;
- (BOOL)isDevToolsVisible;
- (BOOL)isDevToolsFocused;
- (void)setIsDocked:(BOOL)docked activate:(BOOL)activate;
- (void)setContentsResizingStrategy:
(const DevToolsContentsResizingStrategy&)strategy;
- (void)setTitle:(NSString*)title;
@end
#endif // ATOM_BROWSER_UI_COCOA_BRY_INSPECTABLE_WEB_CONTENTS_VIEW_H_

View file

@ -0,0 +1,269 @@
// Copyright (c) 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-CHROMIUM file.
#include "atom/browser/ui/cocoa/atom_inspectable_web_contents_view.h"
#include "atom/browser/ui/cocoa/event_dispatching_window.h"
#include "atom/browser/ui/inspectable_web_contents_impl.h"
#include "atom/browser/ui/inspectable_web_contents_view_delegate.h"
#include "atom/browser/ui/inspectable_web_contents_view_mac.h"
#include "content/public/browser/render_widget_host_view.h"
#include "ui/gfx/mac/scoped_cocoa_disable_screen_updates.h"
@implementation AtomInspectableWebContentsView
- (instancetype)initWithInspectableWebContentsViewMac:
(InspectableWebContentsViewMac*)view {
self = [super init];
if (!self)
return nil;
inspectableWebContentsView_ = view;
devtools_visible_ = NO;
devtools_docked_ = NO;
devtools_is_first_responder_ = NO;
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(viewDidBecomeFirstResponder:)
name:kViewDidBecomeFirstResponder
object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(parentWindowBecameMain:)
name:NSWindowDidBecomeMainNotification
object:nil];
if (inspectableWebContentsView_->inspectable_web_contents()->IsGuest()) {
fake_view_.reset([[NSView alloc] init]);
[fake_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[self addSubview:fake_view_];
} else {
auto* contents = inspectableWebContentsView_->inspectable_web_contents()
->GetWebContents();
auto* contentsView = contents->GetNativeView().GetNativeNSView();
[contentsView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[self addSubview:contentsView];
}
// See https://code.google.com/p/chromium/issues/detail?id=348490.
[self setWantsLayer:YES];
return self;
}
- (void)removeObservers {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize {
[self adjustSubviews];
}
- (IBAction)showDevTools:(id)sender {
inspectableWebContentsView_->inspectable_web_contents()->ShowDevTools(true);
}
- (void)notifyDevToolsFocused {
if (inspectableWebContentsView_->GetDelegate())
inspectableWebContentsView_->GetDelegate()->DevToolsFocused();
}
- (void)setDevToolsVisible:(BOOL)visible activate:(BOOL)activate {
if (visible == devtools_visible_)
return;
auto* inspectable_web_contents =
inspectableWebContentsView_->inspectable_web_contents();
auto* devToolsWebContents =
inspectable_web_contents->GetDevToolsWebContents();
auto* devToolsView = devToolsWebContents->GetNativeView().GetNativeNSView();
devtools_visible_ = visible;
if (devtools_docked_) {
if (visible) {
// Place the devToolsView under contentsView, notice that we didn't set
// sizes for them until the setContentsResizingStrategy message.
[self addSubview:devToolsView positioned:NSWindowBelow relativeTo:nil];
[self adjustSubviews];
// Focus on web view.
devToolsWebContents->RestoreFocus();
} else {
gfx::ScopedCocoaDisableScreenUpdates disabler;
[devToolsView removeFromSuperview];
[self adjustSubviews];
}
} else {
if (visible) {
if (activate) {
[devtools_window_ makeKeyAndOrderFront:nil];
} else {
[devtools_window_ orderBack:nil];
}
} else {
[devtools_window_ setDelegate:nil];
[devtools_window_ close];
devtools_window_.reset();
}
}
}
- (BOOL)isDevToolsVisible {
return devtools_visible_;
}
- (BOOL)isDevToolsFocused {
if (devtools_docked_) {
return [[self window] isKeyWindow] && devtools_is_first_responder_;
} else {
return [devtools_window_ isKeyWindow];
}
}
- (void)setIsDocked:(BOOL)docked activate:(BOOL)activate {
// Revert to no-devtools state.
[self setDevToolsVisible:NO activate:NO];
// Switch to new state.
devtools_docked_ = docked;
if (!docked) {
auto* inspectable_web_contents =
inspectableWebContentsView_->inspectable_web_contents();
auto* devToolsWebContents =
inspectable_web_contents->GetDevToolsWebContents();
auto devToolsView = devToolsWebContents->GetNativeView().GetNativeNSView();
auto styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
NSMiniaturizableWindowMask | NSWindowStyleMaskResizable |
NSTexturedBackgroundWindowMask |
NSWindowStyleMaskUnifiedTitleAndToolbar;
devtools_window_.reset([[EventDispatchingWindow alloc]
initWithContentRect:NSMakeRect(0, 0, 800, 600)
styleMask:styleMask
backing:NSBackingStoreBuffered
defer:YES]);
[devtools_window_ setDelegate:self];
[devtools_window_ setFrameAutosaveName:@"electron.devtools"];
[devtools_window_ setTitle:@"Developer Tools"];
[devtools_window_ setReleasedWhenClosed:NO];
[devtools_window_ setAutorecalculatesContentBorderThickness:NO
forEdge:NSMaxYEdge];
[devtools_window_ setContentBorderThickness:24 forEdge:NSMaxYEdge];
NSView* contentView = [devtools_window_ contentView];
devToolsView.frame = contentView.bounds;
devToolsView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
[contentView addSubview:devToolsView];
}
[self setDevToolsVisible:YES activate:activate];
}
- (void)setContentsResizingStrategy:
(const DevToolsContentsResizingStrategy&)strategy {
strategy_.CopyFrom(strategy);
[self adjustSubviews];
}
- (void)adjustSubviews {
if (![[self subviews] count])
return;
if (![self isDevToolsVisible] || devtools_window_) {
DCHECK_EQ(1u, [[self subviews] count]);
NSView* contents = [[self subviews] objectAtIndex:0];
[contents setFrame:[self bounds]];
return;
}
NSView* devToolsView = [[self subviews] objectAtIndex:0];
NSView* contentsView = [[self subviews] objectAtIndex:1];
DCHECK_EQ(2u, [[self subviews] count]);
gfx::Rect new_devtools_bounds;
gfx::Rect new_contents_bounds;
ApplyDevToolsContentsResizingStrategy(
strategy_, gfx::Size(NSSizeToCGSize([self bounds].size)),
&new_devtools_bounds, &new_contents_bounds);
[devToolsView setFrame:[self flipRectToNSRect:new_devtools_bounds]];
[contentsView setFrame:[self flipRectToNSRect:new_contents_bounds]];
}
- (void)setTitle:(NSString*)title {
[devtools_window_ setTitle:title];
}
- (void)viewDidBecomeFirstResponder:(NSNotification*)notification {
auto* inspectable_web_contents =
inspectableWebContentsView_->inspectable_web_contents();
if (!inspectable_web_contents || inspectable_web_contents->IsGuest())
return;
auto* webContents = inspectable_web_contents->GetWebContents();
auto* webContentsView = webContents->GetNativeView().GetNativeNSView();
NSView* view = [notification object];
if ([[webContentsView subviews] containsObject:view]) {
devtools_is_first_responder_ = NO;
return;
}
auto* devToolsWebContents =
inspectable_web_contents->GetDevToolsWebContents();
if (!devToolsWebContents)
return;
auto devToolsView = devToolsWebContents->GetNativeView().GetNativeNSView();
if ([[devToolsView subviews] containsObject:view]) {
devtools_is_first_responder_ = YES;
[self notifyDevToolsFocused];
}
}
- (void)parentWindowBecameMain:(NSNotification*)notification {
NSWindow* parentWindow = [notification object];
if ([self window] == parentWindow && devtools_docked_ &&
devtools_is_first_responder_)
[self notifyDevToolsFocused];
}
#pragma mark - NSWindowDelegate
- (void)windowWillClose:(NSNotification*)notification {
inspectableWebContentsView_->inspectable_web_contents()->CloseDevTools();
}
- (void)windowDidBecomeMain:(NSNotification*)notification {
content::WebContents* web_contents =
inspectableWebContentsView_->inspectable_web_contents()
->GetDevToolsWebContents();
if (!web_contents)
return;
web_contents->RestoreFocus();
content::RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView();
if (rwhv)
rwhv->SetActive(true);
[self notifyDevToolsFocused];
}
- (void)windowDidResignMain:(NSNotification*)notification {
content::WebContents* web_contents =
inspectableWebContentsView_->inspectable_web_contents()
->GetDevToolsWebContents();
if (!web_contents)
return;
web_contents->StoreFocus();
content::RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView();
if (rwhv)
rwhv->SetActive(false);
}
@end

View file

@ -0,0 +1,62 @@
// Copyright (c) 2013 GitHub, Inc.
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_UI_COCOA_ATOM_MENU_CONTROLLER_H_
#define ATOM_BROWSER_UI_COCOA_ATOM_MENU_CONTROLLER_H_
#import <Cocoa/Cocoa.h>
#include "base/callback.h"
#include "base/mac/scoped_nsobject.h"
#include "base/strings/string16.h"
namespace atom {
class AtomMenuModel;
}
// A controller for the cross-platform menu model. The menu that's created
// has the tag and represented object set for each menu item. The object is a
// NSValue holding a pointer to the model for that level of the menu (to
// allow for hierarchical menus). The tag is the index into that model for
// that particular item. It is important that the model outlives this object
// as it only maintains weak references.
@interface AtomMenuController : NSObject <NSMenuDelegate> {
@protected
atom::AtomMenuModel* model_; // weak
base::scoped_nsobject<NSMenu> menu_;
BOOL isMenuOpen_;
BOOL useDefaultAccelerator_;
base::Callback<void()> closeCallback;
}
@property(nonatomic, assign) atom::AtomMenuModel* model;
// Builds a NSMenu from the pre-built model (must not be nil). Changes made
// to the contents of the model after calling this will not be noticed.
- (id)initWithModel:(atom::AtomMenuModel*)model useDefaultAccelerator:(BOOL)use;
- (void)setCloseCallback:(const base::Callback<void()>&)callback;
// Populate current NSMenu with |model|.
- (void)populateWithModel:(atom::AtomMenuModel*)model;
// Programmatically close the constructed menu.
- (void)cancel;
// Access to the constructed menu if the complex initializer was used. If the
// default initializer was used, then this will create the menu on first call.
- (NSMenu*)menu;
// Whether the menu is currently open.
- (BOOL)isMenuOpen;
// NSMenuDelegate methods this class implements. Subclasses should call super
// if extending the behavior.
- (void)menuWillOpen:(NSMenu*)menu;
- (void)menuDidClose:(NSMenu*)menu;
@end
#endif // ATOM_BROWSER_UI_COCOA_ATOM_MENU_CONTROLLER_H_

View file

@ -0,0 +1,384 @@
// Copyright (c) 2013 GitHub, Inc.
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#import "atom/browser/ui/cocoa/atom_menu_controller.h"
#include "atom/browser/mac/atom_application.h"
#include "atom/browser/ui/atom_menu_model.h"
#include "base/logging.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/accelerators/platform_accelerator_cocoa.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "ui/events/cocoa/cocoa_event_utils.h"
#include "ui/gfx/image/image.h"
#include "ui/strings/grit/ui_strings.h"
using content::BrowserThread;
namespace {
struct Role {
SEL selector;
const char* role;
};
Role kRolesMap[] = {
{@selector(orderFrontStandardAboutPanel:), "about"},
{@selector(hide:), "hide"},
{@selector(hideOtherApplications:), "hideothers"},
{@selector(unhideAllApplications:), "unhide"},
{@selector(arrangeInFront:), "front"},
{@selector(undo:), "undo"},
{@selector(redo:), "redo"},
{@selector(cut:), "cut"},
{@selector(copy:), "copy"},
{@selector(paste:), "paste"},
{@selector(delete:), "delete"},
{@selector(pasteAndMatchStyle:), "pasteandmatchstyle"},
{@selector(selectAll:), "selectall"},
{@selector(startSpeaking:), "startspeaking"},
{@selector(stopSpeaking:), "stopspeaking"},
{@selector(performMiniaturize:), "minimize"},
{@selector(performClose:), "close"},
{@selector(performZoom:), "zoom"},
{@selector(terminate:), "quit"},
// ↓ is intentionally not `toggleFullScreen`. The macOS full screen menu
// item behaves weird. If we use `toggleFullScreen`, then the menu item will
// use the default label, and not take the one provided.
{@selector(toggleFullScreenMode:), "togglefullscreen"},
{@selector(toggleTabBar:), "toggletabbar"},
{@selector(selectNextTab:), "selectnexttab"},
{@selector(selectPreviousTab:), "selectprevioustab"},
{@selector(mergeAllWindows:), "mergeallwindows"},
{@selector(moveTabToNewWindow:), "movetabtonewwindow"},
{@selector(clearRecentDocuments:), "clearrecentdocuments"},
};
// Called when adding a submenu to the menu and checks if the submenu, via its
// |model|, has visible child items.
bool MenuHasVisibleItems(const atom::AtomMenuModel* model) {
int count = model->GetItemCount();
for (int index = 0; index < count; index++) {
if (model->IsVisibleAt(index))
return true;
}
return false;
}
// Called when an empty submenu is created. This inserts a menu item labeled
// "(empty)" into the submenu. Matches Windows behavior.
NSMenu* MakeEmptySubmenu() {
base::scoped_nsobject<NSMenu> submenu([[NSMenu alloc] initWithTitle:@""]);
NSString* empty_menu_title =
l10n_util::GetNSString(IDS_APP_MENU_EMPTY_SUBMENU);
[submenu addItemWithTitle:empty_menu_title action:NULL keyEquivalent:@""];
[[submenu itemAtIndex:0] setEnabled:NO];
return submenu.autorelease();
}
} // namespace
// Menu item is located for ease of removing it from the parent owner
static base::scoped_nsobject<NSMenuItem> recentDocumentsMenuItem_;
// Submenu retained to be swapped back to |recentDocumentsMenuItem_|
static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
@implementation AtomMenuController
@synthesize model = model_;
- (id)initWithModel:(atom::AtomMenuModel*)model
useDefaultAccelerator:(BOOL)use {
if ((self = [super init])) {
model_ = model;
isMenuOpen_ = NO;
useDefaultAccelerator_ = use;
[self menu];
}
return self;
}
- (void)dealloc {
[menu_ setDelegate:nil];
// Close the menu if it is still open. This could happen if a tab gets closed
// while its context menu is still open.
[self cancel];
model_ = nil;
[super dealloc];
}
- (void)setCloseCallback:(const base::Callback<void()>&)callback {
closeCallback = callback;
}
- (void)populateWithModel:(atom::AtomMenuModel*)model {
if (!menu_)
return;
if (!recentDocumentsMenuItem_) {
// Locate & retain the recent documents menu item
recentDocumentsMenuItem_.reset(
[[[[[NSApp mainMenu] itemWithTitle:@"Electron"] submenu]
itemWithTitle:@"Open Recent"] retain]);
}
model_ = model;
[menu_ removeAllItems];
const int count = model->GetItemCount();
for (int index = 0; index < count; index++) {
if (model->GetTypeAt(index) == atom::AtomMenuModel::TYPE_SEPARATOR)
[self addSeparatorToMenu:menu_ atIndex:index];
else
[self addItemToMenu:menu_ atIndex:index fromModel:model];
}
}
- (void)cancel {
if (isMenuOpen_) {
[menu_ cancelTracking];
isMenuOpen_ = NO;
model_->MenuWillClose();
if (!closeCallback.is_null()) {
base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, closeCallback);
}
}
}
// Creates a NSMenu from the given model. If the model has submenus, this can
// be invoked recursively.
- (NSMenu*)menuFromModel:(atom::AtomMenuModel*)model {
NSMenu* menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
const int count = model->GetItemCount();
for (int index = 0; index < count; index++) {
if (model->GetTypeAt(index) == atom::AtomMenuModel::TYPE_SEPARATOR)
[self addSeparatorToMenu:menu atIndex:index];
else
[self addItemToMenu:menu atIndex:index fromModel:model];
}
return menu;
}
// Adds a separator item at the given index. As the separator doesn't need
// anything from the model, this method doesn't need the model index as the
// other method below does.
- (void)addSeparatorToMenu:(NSMenu*)menu atIndex:(int)index {
NSMenuItem* separator = [NSMenuItem separatorItem];
[menu insertItem:separator atIndex:index];
}
// Empties the source menu items to the destination.
- (void)moveMenuItems:(NSMenu*)source to:(NSMenu*)destination {
const NSInteger count = [source numberOfItems];
for (NSInteger index = 0; index < count; index++) {
NSMenuItem* removedItem = [[[source itemAtIndex:0] retain] autorelease];
[source removeItemAtIndex:0];
[destination addItem:removedItem];
}
}
// Replaces the item's submenu instance with the singleton recent documents
// menu. Previously replaced menu items will be recovered.
- (void)replaceSubmenuShowingRecentDocuments:(NSMenuItem*)item {
NSMenu* recentDocumentsMenu =
[[[recentDocumentsMenuItem_ submenu] retain] autorelease];
// Remove menu items in recent documents back to swap menu
[self moveMenuItems:recentDocumentsMenu to:recentDocumentsMenuSwap_];
// Swap back the submenu
[recentDocumentsMenuItem_ setSubmenu:recentDocumentsMenuSwap_];
// Retain the item's submenu for a future recovery
recentDocumentsMenuSwap_.reset([[item submenu] retain]);
// Repopulate with items from the submenu to be replaced
[self moveMenuItems:recentDocumentsMenuSwap_ to:recentDocumentsMenu];
// Update the submenu's title
[recentDocumentsMenu setTitle:[recentDocumentsMenuSwap_ title]];
// Replace submenu
[item setSubmenu:recentDocumentsMenu];
// Remember the new menu item that carries the recent documents menu
recentDocumentsMenuItem_.reset([item retain]);
}
// Adds an item or a hierarchical menu to the item at the |index|,
// associated with the entry in the model identified by |modelIndex|.
- (void)addItemToMenu:(NSMenu*)menu
atIndex:(NSInteger)index
fromModel:(atom::AtomMenuModel*)model {
base::string16 label16 = model->GetLabelAt(index);
NSString* label = l10n_util::FixUpWindowsStyleLabel(label16);
base::scoped_nsobject<NSMenuItem> item([[NSMenuItem alloc]
initWithTitle:label
action:@selector(itemSelected:)
keyEquivalent:@""]);
// If the menu item has an icon, set it.
gfx::Image icon;
if (model->GetIconAt(index, &icon) && !icon.IsEmpty())
[item setImage:icon.ToNSImage()];
base::string16 role = model->GetRoleAt(index);
atom::AtomMenuModel::ItemType type = model->GetTypeAt(index);
if (role == base::ASCIIToUTF16("services")) {
base::string16 title = base::ASCIIToUTF16("Services");
NSString* label = l10n_util::FixUpWindowsStyleLabel(title);
[item setTarget:nil];
[item setAction:nil];
NSMenu* submenu = [[NSMenu alloc] initWithTitle:label];
[item setSubmenu:submenu];
[NSApp setServicesMenu:submenu];
} else if (type == atom::AtomMenuModel::TYPE_SUBMENU &&
model->IsVisibleAt(index)) {
// We need to specifically check that the submenu top-level item has been
// enabled as it's not validated by validateUserInterfaceItem
if (!model->IsEnabledAt(index))
[item setEnabled:NO];
// Recursively build a submenu from the sub-model at this index.
[item setTarget:nil];
[item setAction:nil];
atom::AtomMenuModel* submenuModel =
static_cast<atom::AtomMenuModel*>(model->GetSubmenuModelAt(index));
NSMenu* submenu = MenuHasVisibleItems(submenuModel)
? [self menuFromModel:submenuModel]
: MakeEmptySubmenu();
[submenu setTitle:[item title]];
[item setSubmenu:submenu];
// Set submenu's role.
if ((role == base::ASCIIToUTF16("window") ||
role == base::ASCIIToUTF16("windowmenu")) &&
[submenu numberOfItems])
[NSApp setWindowsMenu:submenu];
else if (role == base::ASCIIToUTF16("help"))
[NSApp setHelpMenu:submenu];
else if (role == base::ASCIIToUTF16("recentdocuments"))
[self replaceSubmenuShowingRecentDocuments:item];
} else {
// The MenuModel works on indexes so we can't just set the command id as the
// tag like we do in other menus. Also set the represented object to be
// the model so hierarchical menus check the correct index in the correct
// model. Setting the target to |self| allows this class to participate
// in validation of the menu items.
[item setTag:index];
NSValue* modelObject = [NSValue valueWithPointer:model];
[item setRepresentedObject:modelObject]; // Retains |modelObject|.
ui::Accelerator accelerator;
if (model->GetAcceleratorAtWithParams(index, useDefaultAccelerator_,
&accelerator)) {
NSString* key_equivalent;
NSUInteger modifier_mask;
GetKeyEquivalentAndModifierMaskFromAccelerator(
accelerator, &key_equivalent, &modifier_mask);
[item setKeyEquivalent:key_equivalent];
[item setKeyEquivalentModifierMask:modifier_mask];
}
if (@available(macOS 10.13, *)) {
[(id)item
setAllowsKeyEquivalentWhenHidden:(model->WorksWhenHiddenAt(index))];
}
// Set menu item's role.
[item setTarget:self];
if (!role.empty()) {
for (const Role& pair : kRolesMap) {
if (role == base::ASCIIToUTF16(pair.role)) {
[item setTarget:nil];
[item setAction:pair.selector];
break;
}
}
}
}
[menu insertItem:item atIndex:index];
}
// Called before the menu is to be displayed to update the state (enabled,
// radio, etc) of each item in the menu.
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
SEL action = [item action];
if (action != @selector(itemSelected:))
return NO;
NSInteger modelIndex = [item tag];
atom::AtomMenuModel* model = static_cast<atom::AtomMenuModel*>(
[[(id)item representedObject] pointerValue]);
DCHECK(model);
if (model) {
BOOL checked = model->IsItemCheckedAt(modelIndex);
DCHECK([(id)item isKindOfClass:[NSMenuItem class]]);
[(id)item setState:(checked ? NSOnState : NSOffState)];
[(id)item setHidden:(!model->IsVisibleAt(modelIndex))];
return model->IsEnabledAt(modelIndex);
}
return NO;
}
// Called when the user chooses a particular menu item. |sender| is the menu
// item chosen.
- (void)itemSelected:(id)sender {
NSInteger modelIndex = [sender tag];
atom::AtomMenuModel* model = static_cast<atom::AtomMenuModel*>(
[[sender representedObject] pointerValue]);
DCHECK(model);
if (model) {
NSEvent* event = [NSApp currentEvent];
model->ActivatedAt(modelIndex,
ui::EventFlagsFromModifiers([event modifierFlags]));
}
}
- (NSMenu*)menu {
if (menu_)
return menu_.get();
menu_.reset([[NSMenu alloc] initWithTitle:@""]);
[menu_ setDelegate:self];
if (model_)
[self populateWithModel:model_];
return menu_.get();
}
- (BOOL)isMenuOpen {
return isMenuOpen_;
}
- (void)menuWillOpen:(NSMenu*)menu {
isMenuOpen_ = YES;
model_->MenuWillShow();
}
- (void)menuDidClose:(NSMenu*)menu {
if (isMenuOpen_) {
isMenuOpen_ = NO;
model_->MenuWillClose();
// Post async task so that itemSelected runs before the close callback
// deletes the controller from the map which deallocates it
if (!closeCallback.is_null()) {
base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, closeCallback);
}
}
}
@end

View file

@ -0,0 +1,35 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_UI_COCOA_ATOM_NATIVE_WIDGET_MAC_H_
#define ATOM_BROWSER_UI_COCOA_ATOM_NATIVE_WIDGET_MAC_H_
#include "ui/views/widget/native_widget_mac.h"
namespace atom {
class NativeWindowMac;
class AtomNativeWidgetMac : public views::NativeWidgetMac {
public:
AtomNativeWidgetMac(NativeWindowMac* shell,
NSUInteger style_mask,
views::internal::NativeWidgetDelegate* delegate);
~AtomNativeWidgetMac() override;
protected:
// NativeWidgetMac:
NativeWidgetMacNSWindow* CreateNSWindow(
const remote_cocoa::mojom::CreateWindowParams* params) override;
private:
NativeWindowMac* shell_;
NSUInteger style_mask_;
DISALLOW_COPY_AND_ASSIGN(AtomNativeWidgetMac);
};
} // namespace atom
#endif // ATOM_BROWSER_UI_COCOA_ATOM_NATIVE_WIDGET_MAC_H_

View file

@ -0,0 +1,27 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/ui/cocoa/atom_native_widget_mac.h"
#include "atom/browser/ui/cocoa/atom_ns_window.h"
namespace atom {
AtomNativeWidgetMac::AtomNativeWidgetMac(
NativeWindowMac* shell,
NSUInteger style_mask,
views::internal::NativeWidgetDelegate* delegate)
: views::NativeWidgetMac(delegate),
shell_(shell),
style_mask_(style_mask) {}
AtomNativeWidgetMac::~AtomNativeWidgetMac() {}
NativeWidgetMacNSWindow* AtomNativeWidgetMac::CreateNSWindow(
const remote_cocoa::mojom::CreateWindowParams* params) {
return [[[AtomNSWindow alloc] initWithShell:shell_
styleMask:style_mask_] autorelease];
}
} // namespace atom

View file

@ -0,0 +1,47 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_UI_COCOA_ATOM_NS_WINDOW_H_
#define ATOM_BROWSER_UI_COCOA_ATOM_NS_WINDOW_H_
#include "atom/browser/ui/cocoa/event_dispatching_window.h"
#include "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
#include "ui/views/widget/native_widget_mac.h"
namespace atom {
class NativeWindowMac;
// Prevents window from resizing during the scope.
class ScopedDisableResize {
public:
ScopedDisableResize() { disable_resize_ = true; }
~ScopedDisableResize() { disable_resize_ = false; }
static bool IsResizeDisabled() { return disable_resize_; }
private:
static bool disable_resize_;
};
} // namespace atom
@interface AtomNSWindow : NativeWidgetMacNSWindow {
@private
atom::NativeWindowMac* shell_;
}
@property BOOL acceptsFirstMouse;
@property BOOL enableLargerThanScreen;
@property BOOL disableAutoHideCursor;
@property BOOL disableKeyOrMainWindow;
@property(nonatomic, retain) NSView* vibrantView;
- (id)initWithShell:(atom::NativeWindowMac*)shell
styleMask:(NSUInteger)styleMask;
- (atom::NativeWindowMac*)shell;
- (id)accessibilityFocusedUIElement;
- (NSRect)originalContentRectForFrameRect:(NSRect)frameRect;
- (void)toggleFullScreenMode:(id)sender;
@end
#endif // ATOM_BROWSER_UI_COCOA_ATOM_NS_WINDOW_H_

View file

@ -0,0 +1,191 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/ui/cocoa/atom_ns_window.h"
#include "atom/browser/native_window_mac.h"
#include "atom/browser/ui/cocoa/atom_preview_item.h"
#include "atom/browser/ui/cocoa/atom_touch_bar.h"
#include "atom/browser/ui/cocoa/root_view_mac.h"
#include "base/strings/sys_string_conversions.h"
#include "ui/base/cocoa/window_size_constants.h"
namespace atom {
bool ScopedDisableResize::disable_resize_ = false;
} // namespace atom
@implementation AtomNSWindow
@synthesize acceptsFirstMouse;
@synthesize enableLargerThanScreen;
@synthesize disableAutoHideCursor;
@synthesize disableKeyOrMainWindow;
@synthesize vibrantView;
- (id)initWithShell:(atom::NativeWindowMac*)shell
styleMask:(NSUInteger)styleMask {
if ((self = [super initWithContentRect:ui::kWindowSizeDeterminedLater
styleMask:styleMask
backing:NSBackingStoreBuffered
defer:YES])) {
shell_ = shell;
}
return self;
}
- (atom::NativeWindowMac*)shell {
return shell_;
}
- (id)accessibilityFocusedUIElement {
views::Widget* widget = shell_->widget();
id superFocus = [super accessibilityFocusedUIElement];
if (!widget || shell_->IsFocused())
return superFocus;
return nil;
}
- (NSRect)originalContentRectForFrameRect:(NSRect)frameRect {
return [super contentRectForFrameRect:frameRect];
}
- (NSTouchBar*)makeTouchBar API_AVAILABLE(macosx(10.12.2)) {
if (shell_->touch_bar())
return [shell_->touch_bar() makeTouchBar];
else
return nil;
}
// NSWindow overrides.
- (void)swipeWithEvent:(NSEvent*)event {
if (event.deltaY == 1.0) {
shell_->NotifyWindowSwipe("up");
} else if (event.deltaX == -1.0) {
shell_->NotifyWindowSwipe("right");
} else if (event.deltaY == -1.0) {
shell_->NotifyWindowSwipe("down");
} else if (event.deltaX == 1.0) {
shell_->NotifyWindowSwipe("left");
}
}
- (NSRect)contentRectForFrameRect:(NSRect)frameRect {
if (shell_->has_frame())
return [super contentRectForFrameRect:frameRect];
else
return frameRect;
}
- (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen*)screen {
// Resizing is disabled.
if (atom::ScopedDisableResize::IsResizeDisabled())
return [self frame];
// Enable the window to be larger than screen.
if ([self enableLargerThanScreen])
return frameRect;
else
return [super constrainFrameRect:frameRect toScreen:screen];
}
- (void)setFrame:(NSRect)windowFrame display:(BOOL)displayViews {
// constrainFrameRect is not called on hidden windows so disable adjusting
// the frame directly when resize is disabled
if (!atom::ScopedDisableResize::IsResizeDisabled())
[super setFrame:windowFrame display:displayViews];
}
- (id)accessibilityAttributeValue:(NSString*)attribute {
if ([attribute isEqual:NSAccessibilityTitleAttribute])
return base::SysUTF8ToNSString(shell_->GetTitle());
if ([attribute isEqual:NSAccessibilityEnabledAttribute])
return [NSNumber numberWithBool:YES];
if (![attribute isEqualToString:@"AXChildren"])
return [super accessibilityAttributeValue:attribute];
// Filter out objects that aren't the title bar buttons. This has the effect
// of removing the window title, which VoiceOver already sees.
// * when VoiceOver is disabled, this causes Cmd+C to be used for TTS but
// still leaves the buttons available in the accessibility tree.
// * when VoiceOver is enabled, the full accessibility tree is used.
// Without removing the title and with VO disabled, the TTS would always read
// the window title instead of using Cmd+C to get the selected text.
NSPredicate* predicate = [NSPredicate
predicateWithFormat:@"(self isKindOfClass: %@) OR (self.className == %@)",
[NSButtonCell class], @"RenderWidgetHostViewCocoa"];
NSArray* children = [super accessibilityAttributeValue:attribute];
return [children filteredArrayUsingPredicate:predicate];
}
- (BOOL)canBecomeMainWindow {
return !self.disableKeyOrMainWindow;
}
- (BOOL)canBecomeKeyWindow {
return !self.disableKeyOrMainWindow;
}
- (NSView*)frameView {
return [[self contentView] superview];
}
// Quicklook methods
- (BOOL)acceptsPreviewPanelControl:(QLPreviewPanel*)panel {
return YES;
}
- (void)beginPreviewPanelControl:(QLPreviewPanel*)panel {
panel.delegate = [self delegate];
panel.dataSource = static_cast<id<QLPreviewPanelDataSource>>([self delegate]);
}
- (void)endPreviewPanelControl:(QLPreviewPanel*)panel {
panel.delegate = nil;
panel.dataSource = nil;
}
// Custom window button methods
- (BOOL)windowShouldClose:(id)sender {
return YES;
}
- (void)performClose:(id)sender {
if (shell_->title_bar_style() ==
atom::NativeWindowMac::TitleBarStyle::CUSTOM_BUTTONS_ON_HOVER) {
[[self delegate] windowShouldClose:self];
} else if (shell_->IsSimpleFullScreen()) {
if ([[self delegate] respondsToSelector:@selector(windowShouldClose:)]) {
if (![[self delegate] windowShouldClose:self])
return;
} else if ([self respondsToSelector:@selector(windowShouldClose:)]) {
if (![self windowShouldClose:self])
return;
}
[self close];
} else {
[super performClose:sender];
}
}
- (void)toggleFullScreenMode:(id)sender {
if (shell_->simple_fullscreen())
shell_->SetSimpleFullScreen(!shell_->IsSimpleFullScreen());
else
[super toggleFullScreen:sender];
}
- (void)performMiniaturize:(id)sender {
if (shell_->title_bar_style() ==
atom::NativeWindowMac::TitleBarStyle::CUSTOM_BUTTONS_ON_HOVER)
[self miniaturize:self];
else
[super performMiniaturize:sender];
}
@end

View file

@ -0,0 +1,27 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_UI_COCOA_ATOM_NS_WINDOW_DELEGATE_H_
#define ATOM_BROWSER_UI_COCOA_ATOM_NS_WINDOW_DELEGATE_H_
#include <Quartz/Quartz.h>
#include "components/remote_cocoa/app_shim/views_nswindow_delegate.h"
namespace atom {
class NativeWindowMac;
}
@interface AtomNSWindowDelegate
: ViewsNSWindowDelegate <NSTouchBarDelegate, QLPreviewPanelDataSource> {
@private
atom::NativeWindowMac* shell_;
bool is_zooming_;
int level_;
bool is_resizable_;
}
- (id)initWithShell:(atom::NativeWindowMac*)shell;
@end
#endif // ATOM_BROWSER_UI_COCOA_ATOM_NS_WINDOW_DELEGATE_H_

View file

@ -0,0 +1,306 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/ui/cocoa/atom_ns_window_delegate.h"
#include <algorithm>
#include "atom/browser/browser.h"
#include "atom/browser/native_window_mac.h"
#include "atom/browser/ui/cocoa/atom_preview_item.h"
#include "atom/browser/ui/cocoa/atom_touch_bar.h"
#include "base/mac/mac_util.h"
#include "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h"
#include "ui/views/cocoa/native_widget_mac_ns_window_host.h"
#include "ui/views/widget/native_widget_mac.h"
using TitleBarStyle = atom::NativeWindowMac::TitleBarStyle;
@implementation AtomNSWindowDelegate
- (id)initWithShell:(atom::NativeWindowMac*)shell {
// The views library assumes the window delegate must be an instance of
// ViewsNSWindowDelegate, since we don't have a way to override the creation
// of NSWindowDelegate, we have to dynamically replace the window delegate
// on the fly.
// TODO(zcbenz): Add interface in NativeWidgetMac to allow overriding creating
// window delegate.
auto* bridge_host = views::NativeWidgetMacNSWindowHost::GetFromNativeWindow(
shell->GetNativeWindow());
auto* bridged_view = bridge_host->bridge_impl();
if ((self = [super initWithBridgedNativeWidget:bridged_view])) {
shell_ = shell;
is_zooming_ = false;
level_ = [shell_->GetNativeWindow().GetNativeNSWindow() level];
}
return self;
}
#pragma mark - NSWindowDelegate
- (void)windowDidChangeOcclusionState:(NSNotification*)notification {
// notification.object is the window that changed its state.
// It's safe to use self.window instead if you don't assign one delegate to
// many windows
NSWindow* window = notification.object;
// check occlusion binary flag
if (window.occlusionState & NSWindowOcclusionStateVisible) {
// The app is visible
shell_->NotifyWindowShow();
} else {
// The app is not visible
shell_->NotifyWindowHide();
}
}
// Called when the user clicks the zoom button or selects it from the Window
// menu to determine the "standard size" of the window.
- (NSRect)windowWillUseStandardFrame:(NSWindow*)window
defaultFrame:(NSRect)frame {
if (!shell_->zoom_to_page_width())
return frame;
// If the shift key is down, maximize.
if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask)
return frame;
// Get preferred width from observers. Usually the page width.
int preferred_width = 0;
shell_->NotifyWindowRequestPreferredWith(&preferred_width);
// Never shrink from the current size on zoom.
NSRect window_frame = [window frame];
CGFloat zoomed_width =
std::max(static_cast<CGFloat>(preferred_width), NSWidth(window_frame));
// |frame| determines our maximum extents. We need to set the origin of the
// frame -- and only move it left if necessary.
if (window_frame.origin.x + zoomed_width > NSMaxX(frame))
frame.origin.x = NSMaxX(frame) - zoomed_width;
else
frame.origin.x = window_frame.origin.x;
// Set the width. Don't touch y or height.
frame.size.width = zoomed_width;
return frame;
}
- (void)windowDidBecomeMain:(NSNotification*)notification {
shell_->NotifyWindowFocus();
}
- (void)windowDidResignMain:(NSNotification*)notification {
shell_->NotifyWindowBlur();
}
- (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize {
NSSize newSize = frameSize;
double aspectRatio = shell_->GetAspectRatio();
if (aspectRatio > 0.0) {
gfx::Size windowSize = shell_->GetSize();
gfx::Size contentSize = shell_->GetContentSize();
gfx::Size extraSize = shell_->GetAspectRatioExtraSize();
double extraWidthPlusFrame =
windowSize.width() - contentSize.width() + extraSize.width();
double extraHeightPlusFrame =
windowSize.height() - contentSize.height() + extraSize.height();
newSize.width =
roundf((frameSize.height - extraHeightPlusFrame) * aspectRatio +
extraWidthPlusFrame);
newSize.height =
roundf((newSize.width - extraWidthPlusFrame) / aspectRatio +
extraHeightPlusFrame);
}
{
bool prevent_default = false;
gfx::Rect new_bounds(gfx::Point(sender.frame.origin), gfx::Size(newSize));
shell_->NotifyWindowWillResize(new_bounds, &prevent_default);
if (prevent_default) {
return sender.frame.size;
}
}
return newSize;
}
- (void)windowDidResize:(NSNotification*)notification {
[super windowDidResize:notification];
shell_->NotifyWindowResize();
}
- (void)windowDidMove:(NSNotification*)notification {
[super windowDidMove:notification];
// TODO(zcbenz): Remove the alias after figuring out a proper
// way to dispatch move.
shell_->NotifyWindowMove();
shell_->NotifyWindowMoved();
}
- (void)windowWillMiniaturize:(NSNotification*)notification {
NSWindow* window = shell_->GetNativeWindow().GetNativeNSWindow();
// store the current status window level to be restored in
// windowDidDeminiaturize
level_ = [window level];
[window setLevel:NSNormalWindowLevel];
}
- (void)windowDidMiniaturize:(NSNotification*)notification {
[super windowDidMiniaturize:notification];
shell_->NotifyWindowMinimize();
}
- (void)windowDidDeminiaturize:(NSNotification*)notification {
[super windowDidDeminiaturize:notification];
[shell_->GetNativeWindow().GetNativeNSWindow() setLevel:level_];
shell_->NotifyWindowRestore();
}
- (BOOL)windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame {
is_zooming_ = true;
return YES;
}
- (void)windowDidEndLiveResize:(NSNotification*)notification {
if (is_zooming_) {
if (shell_->IsMaximized())
shell_->NotifyWindowMaximize();
else
shell_->NotifyWindowUnmaximize();
is_zooming_ = false;
}
}
- (void)windowWillEnterFullScreen:(NSNotification*)notification {
// Setting resizable to true before entering fullscreen
is_resizable_ = shell_->IsResizable();
shell_->SetResizable(true);
// Hide the native toolbar before entering fullscreen, so there is no visual
// artifacts.
if (shell_->title_bar_style() == TitleBarStyle::HIDDEN_INSET) {
NSWindow* window = shell_->GetNativeWindow().GetNativeNSWindow();
[window setToolbar:nil];
}
}
- (void)windowDidEnterFullScreen:(NSNotification*)notification {
shell_->NotifyWindowEnterFullScreen();
// For frameless window we don't show set title for normal mode since the
// titlebar is expected to be empty, but after entering fullscreen mode we
// have to set one, because title bar is visible here.
NSWindow* window = shell_->GetNativeWindow().GetNativeNSWindow();
if ((shell_->transparent() || !shell_->has_frame()) &&
// FIXME(zcbenz): Showing titlebar for hiddenInset window is weird under
// fullscreen mode.
// Show title if fullscreen_window_title flag is set
(shell_->title_bar_style() != TitleBarStyle::HIDDEN_INSET ||
shell_->fullscreen_window_title())) {
[window setTitleVisibility:NSWindowTitleVisible];
}
// Restore the native toolbar immediately after entering fullscreen, if we
// do this before leaving fullscreen, traffic light buttons will be jumping.
if (shell_->title_bar_style() == TitleBarStyle::HIDDEN_INSET) {
base::scoped_nsobject<NSToolbar> toolbar(
[[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]);
[toolbar setShowsBaselineSeparator:NO];
[window setToolbar:toolbar];
// Set window style to hide the toolbar, otherwise the toolbar will show
// in fullscreen mode.
[window setTitlebarAppearsTransparent:NO];
shell_->SetStyleMask(true, NSWindowStyleMaskFullSizeContentView);
}
}
- (void)windowWillExitFullScreen:(NSNotification*)notification {
// Restore the titlebar visibility.
NSWindow* window = shell_->GetNativeWindow().GetNativeNSWindow();
if ((shell_->transparent() || !shell_->has_frame()) &&
(shell_->title_bar_style() != TitleBarStyle::HIDDEN_INSET ||
shell_->fullscreen_window_title())) {
[window setTitleVisibility:NSWindowTitleHidden];
}
// Turn off the style for toolbar.
if (shell_->title_bar_style() == TitleBarStyle::HIDDEN_INSET) {
shell_->SetStyleMask(false, NSWindowStyleMaskFullSizeContentView);
[window setTitlebarAppearsTransparent:YES];
}
}
- (void)windowDidExitFullScreen:(NSNotification*)notification {
shell_->SetResizable(is_resizable_);
shell_->NotifyWindowLeaveFullScreen();
}
- (void)windowWillClose:(NSNotification*)notification {
shell_->NotifyWindowClosed();
// Clears the delegate when window is going to be closed, since EL Capitan it
// is possible that the methods of delegate would get called after the window
// has been closed.
auto* bridge_host = views::NativeWidgetMacNSWindowHost::GetFromNativeWindow(
shell_->GetNativeWindow());
auto* bridged_view = bridge_host->bridge_impl();
bridged_view->OnWindowWillClose();
}
- (BOOL)windowShouldClose:(id)window {
shell_->NotifyWindowCloseButtonClicked();
return NO;
}
- (NSRect)window:(NSWindow*)window
willPositionSheet:(NSWindow*)sheet
usingRect:(NSRect)rect {
NSView* view = window.contentView;
rect.origin.x = shell_->GetSheetOffsetX();
rect.origin.y = view.frame.size.height - shell_->GetSheetOffsetY();
return rect;
}
- (void)windowWillBeginSheet:(NSNotification*)notification {
shell_->NotifyWindowSheetBegin();
}
- (void)windowDidEndSheet:(NSNotification*)notification {
shell_->NotifyWindowSheetEnd();
}
- (IBAction)newWindowForTab:(id)sender {
shell_->NotifyNewWindowForTab();
atom::Browser::Get()->NewWindowForTab();
}
#pragma mark - NSTouchBarDelegate
- (NSTouchBarItem*)touchBar:(NSTouchBar*)touchBar
makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier
API_AVAILABLE(macosx(10.12.2)) {
if (touchBar && shell_->touch_bar())
return [shell_->touch_bar() makeItemForIdentifier:identifier];
else
return nil;
}
#pragma mark - QLPreviewPanelDataSource
- (NSInteger)numberOfPreviewItemsInPreviewPanel:(QLPreviewPanel*)panel {
return 1;
}
- (id<QLPreviewItem>)previewPanel:(QLPreviewPanel*)panel
previewItemAtIndex:(NSInteger)index {
return shell_->preview_item();
}
@end

View file

@ -0,0 +1,17 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_UI_COCOA_ATOM_PREVIEW_ITEM_H_
#define ATOM_BROWSER_UI_COCOA_ATOM_PREVIEW_ITEM_H_
#import <Cocoa/Cocoa.h>
#import <Quartz/Quartz.h>
@interface AtomPreviewItem : NSObject <QLPreviewItem>
@property(nonatomic, retain) NSURL* previewItemURL;
@property(nonatomic, retain) NSString* previewItemTitle;
- (id)initWithURL:(NSURL*)url title:(NSString*)title;
@end
#endif // ATOM_BROWSER_UI_COCOA_ATOM_PREVIEW_ITEM_H_

View file

@ -0,0 +1,21 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/ui/cocoa/atom_preview_item.h"
@implementation AtomPreviewItem
@synthesize previewItemURL;
@synthesize previewItemTitle;
- (id)initWithURL:(NSURL*)url title:(NSString*)title {
self = [super init];
if (self) {
self.previewItemURL = url;
self.previewItemTitle = title;
}
return self;
}
@end

View file

@ -0,0 +1,101 @@
// Copyright (c) 2017 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_UI_COCOA_ATOM_TOUCH_BAR_H_
#define ATOM_BROWSER_UI_COCOA_ATOM_TOUCH_BAR_H_
#import <Cocoa/Cocoa.h>
#include <map>
#include <string>
#include <vector>
#include "atom/browser/native_window.h"
#include "atom/browser/ui/cocoa/touch_bar_forward_declarations.h"
#include "base/mac/scoped_nsobject.h"
#include "native_mate/constructor.h"
#include "native_mate/persistent_dictionary.h"
@interface AtomTouchBar : NSObject <NSScrubberDelegate,
NSScrubberDataSource,
NSScrubberFlowLayoutDelegate> {
@protected
std::vector<mate::PersistentDictionary> ordered_settings_;
std::map<std::string, mate::PersistentDictionary> settings_;
id<NSTouchBarDelegate> delegate_;
atom::NativeWindow* window_;
}
- (id)initWithDelegate:(id<NSTouchBarDelegate>)delegate
window:(atom::NativeWindow*)window
settings:(const std::vector<mate::PersistentDictionary>&)settings;
- (NSTouchBar*)makeTouchBar API_AVAILABLE(macosx(10.12.2));
- (NSTouchBar*)touchBarFromItemIdentifiers:(NSMutableArray*)items
API_AVAILABLE(macosx(10.12.2));
- (NSMutableArray*)identifiersFromSettings:
(const std::vector<mate::PersistentDictionary>&)settings;
- (void)refreshTouchBarItem:(NSTouchBar*)touchBar
id:(const std::string&)item_id
API_AVAILABLE(macosx(10.12.2));
- (void)addNonDefaultTouchBarItems:
(const std::vector<mate::PersistentDictionary>&)items;
- (void)setEscapeTouchBarItem:(const mate::PersistentDictionary&)item
forTouchBar:(NSTouchBar*)touchBar
API_AVAILABLE(macosx(10.12.2));
- (NSString*)idFromIdentifier:(NSString*)identifier
withPrefix:(NSString*)prefix;
- (NSTouchBarItemIdentifier)identifierFromID:(const std::string&)item_id
type:(const std::string&)typere;
- (bool)hasItemWithID:(const std::string&)item_id;
- (NSColor*)colorFromHexColorString:(const std::string&)colorString;
// Selector actions
- (void)buttonAction:(id)sender;
- (void)colorPickerAction:(id)sender API_AVAILABLE(macosx(10.12.2));
- (void)sliderAction:(id)sender API_AVAILABLE(macosx(10.12.2));
// Helpers to create touch bar items
- (NSTouchBarItem*)makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier
API_AVAILABLE(macosx(10.12.2));
- (NSTouchBarItem*)makeButtonForID:(NSString*)id
withIdentifier:(NSString*)identifier
API_AVAILABLE(macosx(10.12.2));
- (NSTouchBarItem*)makeLabelForID:(NSString*)id
withIdentifier:(NSString*)identifier
API_AVAILABLE(macosx(10.12.2));
- (NSTouchBarItem*)makeColorPickerForID:(NSString*)id
withIdentifier:(NSString*)identifier
API_AVAILABLE(macosx(10.12.2));
- (NSTouchBarItem*)makeSliderForID:(NSString*)id
withIdentifier:(NSString*)identifier
API_AVAILABLE(macosx(10.12.2));
- (NSTouchBarItem*)makePopoverForID:(NSString*)id
withIdentifier:(NSString*)identifier
API_AVAILABLE(macosx(10.12.2));
- (NSTouchBarItem*)makeGroupForID:(NSString*)id
withIdentifier:(NSString*)identifier
API_AVAILABLE(macosx(10.12.2));
// Helpers to update touch bar items
- (void)updateButton:(NSCustomTouchBarItem*)item
withSettings:(const mate::PersistentDictionary&)settings
API_AVAILABLE(macosx(10.12.2));
- (void)updateLabel:(NSCustomTouchBarItem*)item
withSettings:(const mate::PersistentDictionary&)settings
API_AVAILABLE(macosx(10.12.2));
- (void)updateColorPicker:(NSColorPickerTouchBarItem*)item
withSettings:(const mate::PersistentDictionary&)settings
API_AVAILABLE(macosx(10.12.2));
- (void)updateSlider:(NSSliderTouchBarItem*)item
withSettings:(const mate::PersistentDictionary&)settings
API_AVAILABLE(macosx(10.12.2));
- (void)updatePopover:(NSPopoverTouchBarItem*)item
withSettings:(const mate::PersistentDictionary&)settings
API_AVAILABLE(macosx(10.12.2));
@end
#endif // ATOM_BROWSER_UI_COCOA_ATOM_TOUCH_BAR_H_

View file

@ -0,0 +1,820 @@
// Copyright (c) 2017 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#import "atom/browser/ui/cocoa/atom_touch_bar.h"
#include <string>
#include <vector>
#include "atom/common/color_util.h"
#include "atom/common/native_mate_converters/image_converter.h"
#include "base/strings/sys_string_conversions.h"
#include "skia/ext/skia_utils_mac.h"
#include "ui/gfx/image/image.h"
@implementation AtomTouchBar
static NSTouchBarItemIdentifier ButtonIdentifier =
@"com.electron.touchbar.button.";
static NSTouchBarItemIdentifier ColorPickerIdentifier =
@"com.electron.touchbar.colorpicker.";
static NSTouchBarItemIdentifier GroupIdentifier =
@"com.electron.touchbar.group.";
static NSTouchBarItemIdentifier LabelIdentifier =
@"com.electron.touchbar.label.";
static NSTouchBarItemIdentifier PopoverIdentifier =
@"com.electron.touchbar.popover.";
static NSTouchBarItemIdentifier SliderIdentifier =
@"com.electron.touchbar.slider.";
static NSTouchBarItemIdentifier SegmentedControlIdentifier =
@"com.electron.touchbar.segmentedcontrol.";
static NSTouchBarItemIdentifier ScrubberIdentifier =
@"com.electron.touchbar.scrubber.";
static NSString* const TextScrubberItemIdentifier = @"scrubber.text.item";
static NSString* const ImageScrubberItemIdentifier = @"scrubber.image.item";
- (id)initWithDelegate:(id<NSTouchBarDelegate>)delegate
window:(atom::NativeWindow*)window
settings:
(const std::vector<mate::PersistentDictionary>&)settings {
if ((self = [super init])) {
delegate_ = delegate;
window_ = window;
ordered_settings_ = settings;
}
return self;
}
- (NSTouchBar*)makeTouchBar {
NSMutableArray* identifiers =
[self identifiersFromSettings:ordered_settings_];
return [self touchBarFromItemIdentifiers:identifiers];
}
- (NSTouchBar*)touchBarFromItemIdentifiers:(NSMutableArray*)items {
base::scoped_nsobject<NSTouchBar> bar(
[[NSClassFromString(@"NSTouchBar") alloc] init]);
[bar setDelegate:delegate_];
[bar setDefaultItemIdentifiers:items];
return bar.autorelease();
}
- (NSMutableArray*)identifiersFromSettings:
(const std::vector<mate::PersistentDictionary>&)dicts {
NSMutableArray* identifiers = [NSMutableArray array];
if (@available(macOS 10.12.2, *)) {
for (const auto& item : dicts) {
std::string type;
std::string item_id;
if (item.Get("type", &type) && item.Get("id", &item_id)) {
NSTouchBarItemIdentifier identifier = nil;
if (type == "spacer") {
std::string size;
item.Get("size", &size);
if (size == "large") {
identifier = NSTouchBarItemIdentifierFixedSpaceLarge;
} else if (size == "flexible") {
identifier = NSTouchBarItemIdentifierFlexibleSpace;
} else {
identifier = NSTouchBarItemIdentifierFixedSpaceSmall;
}
} else {
identifier = [self identifierFromID:item_id type:type];
}
if (identifier) {
settings_[item_id] = item;
[identifiers addObject:identifier];
}
}
}
[identifiers addObject:NSTouchBarItemIdentifierOtherItemsProxy];
}
return identifiers;
}
- (NSTouchBarItem*)makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier {
NSString* item_id = nil;
if ([identifier hasPrefix:ButtonIdentifier]) {
item_id = [self idFromIdentifier:identifier withPrefix:ButtonIdentifier];
return [self makeButtonForID:item_id withIdentifier:identifier];
} else if ([identifier hasPrefix:LabelIdentifier]) {
item_id = [self idFromIdentifier:identifier withPrefix:LabelIdentifier];
return [self makeLabelForID:item_id withIdentifier:identifier];
} else if ([identifier hasPrefix:ColorPickerIdentifier]) {
item_id = [self idFromIdentifier:identifier
withPrefix:ColorPickerIdentifier];
return [self makeColorPickerForID:item_id withIdentifier:identifier];
} else if ([identifier hasPrefix:SliderIdentifier]) {
item_id = [self idFromIdentifier:identifier withPrefix:SliderIdentifier];
return [self makeSliderForID:item_id withIdentifier:identifier];
} else if ([identifier hasPrefix:PopoverIdentifier]) {
item_id = [self idFromIdentifier:identifier withPrefix:PopoverIdentifier];
return [self makePopoverForID:item_id withIdentifier:identifier];
} else if ([identifier hasPrefix:GroupIdentifier]) {
item_id = [self idFromIdentifier:identifier withPrefix:GroupIdentifier];
return [self makeGroupForID:item_id withIdentifier:identifier];
} else if ([identifier hasPrefix:SegmentedControlIdentifier]) {
item_id = [self idFromIdentifier:identifier
withPrefix:SegmentedControlIdentifier];
return [self makeSegmentedControlForID:item_id withIdentifier:identifier];
} else if ([identifier hasPrefix:ScrubberIdentifier]) {
item_id = [self idFromIdentifier:identifier withPrefix:ScrubberIdentifier];
return [self makeScrubberForID:item_id withIdentifier:identifier];
}
return nil;
}
- (void)refreshTouchBarItem:(NSTouchBar*)touchBar
id:(NSTouchBarItemIdentifier)identifier
withType:(const std::string&)item_type
withSettings:(const mate::PersistentDictionary&)settings
API_AVAILABLE(macosx(10.12.2)) {
NSTouchBarItem* item = [touchBar itemForIdentifier:identifier];
if (!item)
return;
if (item_type == "button") {
[self updateButton:(NSCustomTouchBarItem*)item withSettings:settings];
} else if (item_type == "label") {
[self updateLabel:(NSCustomTouchBarItem*)item withSettings:settings];
} else if (item_type == "colorpicker") {
[self updateColorPicker:(NSColorPickerTouchBarItem*)item
withSettings:settings];
} else if (item_type == "slider") {
[self updateSlider:(NSSliderTouchBarItem*)item withSettings:settings];
} else if (item_type == "popover") {
[self updatePopover:(NSPopoverTouchBarItem*)item withSettings:settings];
} else if (item_type == "segmented_control") {
[self updateSegmentedControl:(NSCustomTouchBarItem*)item
withSettings:settings];
} else if (item_type == "scrubber") {
[self updateScrubber:(NSCustomTouchBarItem*)item withSettings:settings];
} else if (item_type == "group") {
[self updateGroup:(NSGroupTouchBarItem*)item withSettings:settings];
}
}
- (void)addNonDefaultTouchBarItems:
(const std::vector<mate::PersistentDictionary>&)items {
[self identifiersFromSettings:items];
}
- (void)setEscapeTouchBarItem:(const mate::PersistentDictionary&)item
forTouchBar:(NSTouchBar*)touchBar {
if (![touchBar
respondsToSelector:@selector(escapeKeyReplacementItemIdentifier)])
return;
std::string type;
std::string item_id;
NSTouchBarItemIdentifier identifier = nil;
if (item.Get("type", &type) && item.Get("id", &item_id)) {
identifier = [self identifierFromID:item_id type:type];
}
if (identifier) {
[self addNonDefaultTouchBarItems:{item}];
touchBar.escapeKeyReplacementItemIdentifier = identifier;
} else {
touchBar.escapeKeyReplacementItemIdentifier = nil;
}
}
- (void)refreshTouchBarItem:(NSTouchBar*)touchBar
id:(const std::string&)item_id {
if (![self hasItemWithID:item_id])
return;
mate::PersistentDictionary settings = settings_[item_id];
std::string item_type;
settings.Get("type", &item_type);
auto identifier = [self identifierFromID:item_id type:item_type];
if (!identifier)
return;
std::vector<mate::Dictionary> parents;
settings.Get("_parents", &parents);
for (auto& parent : parents) {
std::string parent_type;
std::string parent_id;
if (!parent.Get("type", &parent_type) || !parent.Get("id", &parent_id))
continue;
auto parentIdentifier = [self identifierFromID:parent_id type:parent_type];
if (!parentIdentifier)
continue;
if (parent_type == "popover") {
NSPopoverTouchBarItem* popoverItem =
[touchBar itemForIdentifier:parentIdentifier];
[self refreshTouchBarItem:popoverItem.popoverTouchBar
id:identifier
withType:item_type
withSettings:settings];
} else if (parent_type == "group") {
NSGroupTouchBarItem* groupItem =
[touchBar itemForIdentifier:parentIdentifier];
[self refreshTouchBarItem:groupItem.groupTouchBar
id:identifier
withType:item_type
withSettings:settings];
}
}
[self refreshTouchBarItem:touchBar
id:identifier
withType:item_type
withSettings:settings];
}
- (void)buttonAction:(id)sender {
NSString* item_id =
[NSString stringWithFormat:@"%ld", ((NSButton*)sender).tag];
window_->NotifyTouchBarItemInteraction([item_id UTF8String],
base::DictionaryValue());
}
- (void)colorPickerAction:(id)sender {
NSString* identifier = ((NSColorPickerTouchBarItem*)sender).identifier;
NSString* item_id = [self idFromIdentifier:identifier
withPrefix:ColorPickerIdentifier];
NSColor* color = ((NSColorPickerTouchBarItem*)sender).color;
std::string hex_color = atom::ToRGBHex(skia::NSDeviceColorToSkColor(color));
base::DictionaryValue details;
details.SetString("color", hex_color);
window_->NotifyTouchBarItemInteraction([item_id UTF8String], details);
}
- (void)sliderAction:(id)sender {
NSString* identifier = ((NSSliderTouchBarItem*)sender).identifier;
NSString* item_id = [self idFromIdentifier:identifier
withPrefix:SliderIdentifier];
base::DictionaryValue details;
details.SetInteger("value",
[((NSSliderTouchBarItem*)sender).slider intValue]);
window_->NotifyTouchBarItemInteraction([item_id UTF8String], details);
}
- (NSString*)idFromIdentifier:(NSString*)identifier
withPrefix:(NSString*)prefix {
return [identifier substringFromIndex:[prefix length]];
}
- (void)segmentedControlAction:(id)sender {
NSString* item_id =
[NSString stringWithFormat:@"%ld", ((NSSegmentedControl*)sender).tag];
base::DictionaryValue details;
details.SetInteger("selectedIndex",
((NSSegmentedControl*)sender).selectedSegment);
details.SetBoolean(
"isSelected",
[((NSSegmentedControl*)sender)
isSelectedForSegment:((NSSegmentedControl*)sender).selectedSegment]);
window_->NotifyTouchBarItemInteraction([item_id UTF8String], details);
}
- (void)scrubber:(NSScrubber*)scrubber
didSelectItemAtIndex:(NSInteger)selectedIndex
API_AVAILABLE(macosx(10.12.2)) {
base::DictionaryValue details;
details.SetInteger("selectedIndex", selectedIndex);
details.SetString("type", "select");
window_->NotifyTouchBarItemInteraction([scrubber.identifier UTF8String],
details);
}
- (void)scrubber:(NSScrubber*)scrubber
didHighlightItemAtIndex:(NSInteger)highlightedIndex
API_AVAILABLE(macosx(10.12.2)) {
base::DictionaryValue details;
details.SetInteger("highlightedIndex", highlightedIndex);
details.SetString("type", "highlight");
window_->NotifyTouchBarItemInteraction([scrubber.identifier UTF8String],
details);
}
- (NSTouchBarItemIdentifier)identifierFromID:(const std::string&)item_id
type:(const std::string&)type {
NSTouchBarItemIdentifier base_identifier = nil;
if (type == "button")
base_identifier = ButtonIdentifier;
else if (type == "label")
base_identifier = LabelIdentifier;
else if (type == "colorpicker")
base_identifier = ColorPickerIdentifier;
else if (type == "slider")
base_identifier = SliderIdentifier;
else if (type == "popover")
base_identifier = PopoverIdentifier;
else if (type == "group")
base_identifier = GroupIdentifier;
else if (type == "segmented_control")
base_identifier = SegmentedControlIdentifier;
else if (type == "scrubber")
base_identifier = ScrubberIdentifier;
if (base_identifier)
return [NSString stringWithFormat:@"%@%s", base_identifier, item_id.data()];
else
return nil;
}
- (bool)hasItemWithID:(const std::string&)item_id {
return settings_.find(item_id) != settings_.end();
}
- (NSColor*)colorFromHexColorString:(const std::string&)colorString {
SkColor color = atom::ParseHexColor(colorString);
return skia::SkColorToDeviceNSColor(color);
}
- (NSTouchBarItem*)makeButtonForID:(NSString*)id
withIdentifier:(NSString*)identifier {
std::string s_id([id UTF8String]);
if (![self hasItemWithID:s_id])
return nil;
mate::PersistentDictionary settings = settings_[s_id];
base::scoped_nsobject<NSCustomTouchBarItem> item([[NSClassFromString(
@"NSCustomTouchBarItem") alloc] initWithIdentifier:identifier]);
NSButton* button = [NSButton buttonWithTitle:@""
target:self
action:@selector(buttonAction:)];
button.tag = [id floatValue];
[item setView:button];
[self updateButton:item withSettings:settings];
return item.autorelease();
}
- (void)updateButton:(NSCustomTouchBarItem*)item
withSettings:(const mate::PersistentDictionary&)settings {
NSButton* button = (NSButton*)item.view;
std::string backgroundColor;
if (settings.Get("backgroundColor", &backgroundColor)) {
button.bezelColor = [self colorFromHexColorString:backgroundColor];
}
std::string label;
settings.Get("label", &label);
button.title = base::SysUTF8ToNSString(label);
gfx::Image image;
if (settings.Get("icon", &image)) {
button.image = image.AsNSImage();
std::string iconPosition;
settings.Get("iconPosition", &iconPosition);
if (iconPosition == "left") {
button.imagePosition = NSImageLeft;
} else if (iconPosition == "right") {
button.imagePosition = NSImageRight;
} else {
button.imagePosition = NSImageOverlaps;
}
}
}
- (NSTouchBarItem*)makeLabelForID:(NSString*)id
withIdentifier:(NSString*)identifier {
std::string s_id([id UTF8String]);
if (![self hasItemWithID:s_id])
return nil;
mate::PersistentDictionary settings = settings_[s_id];
base::scoped_nsobject<NSCustomTouchBarItem> item([[NSClassFromString(
@"NSCustomTouchBarItem") alloc] initWithIdentifier:identifier]);
[item setView:[NSTextField labelWithString:@""]];
[self updateLabel:item withSettings:settings];
return item.autorelease();
}
- (void)updateLabel:(NSCustomTouchBarItem*)item
withSettings:(const mate::PersistentDictionary&)settings {
NSTextField* text_field = (NSTextField*)item.view;
std::string label;
settings.Get("label", &label);
text_field.stringValue = base::SysUTF8ToNSString(label);
std::string textColor;
if (settings.Get("textColor", &textColor) && !textColor.empty()) {
text_field.textColor = [self colorFromHexColorString:textColor];
} else {
text_field.textColor = nil;
}
}
- (NSTouchBarItem*)makeColorPickerForID:(NSString*)id
withIdentifier:(NSString*)identifier {
std::string s_id([id UTF8String]);
if (![self hasItemWithID:s_id])
return nil;
mate::PersistentDictionary settings = settings_[s_id];
base::scoped_nsobject<NSColorPickerTouchBarItem> item([[NSClassFromString(
@"NSColorPickerTouchBarItem") alloc] initWithIdentifier:identifier]);
[item setTarget:self];
[item setAction:@selector(colorPickerAction:)];
[self updateColorPicker:item withSettings:settings];
return item.autorelease();
}
- (void)updateColorPicker:(NSColorPickerTouchBarItem*)item
withSettings:(const mate::PersistentDictionary&)settings {
std::vector<std::string> colors;
if (settings.Get("availableColors", &colors) && !colors.empty()) {
NSColorList* color_list =
[[[NSColorList alloc] initWithName:@""] autorelease];
for (size_t i = 0; i < colors.size(); ++i) {
[color_list insertColor:[self colorFromHexColorString:colors[i]]
key:base::SysUTF8ToNSString(colors[i])
atIndex:i];
}
item.colorList = color_list;
}
std::string selectedColor;
if (settings.Get("selectedColor", &selectedColor)) {
item.color = [self colorFromHexColorString:selectedColor];
}
}
- (NSTouchBarItem*)makeSliderForID:(NSString*)id
withIdentifier:(NSString*)identifier {
std::string s_id([id UTF8String]);
if (![self hasItemWithID:s_id])
return nil;
mate::PersistentDictionary settings = settings_[s_id];
base::scoped_nsobject<NSSliderTouchBarItem> item([[NSClassFromString(
@"NSSliderTouchBarItem") alloc] initWithIdentifier:identifier]);
[item setTarget:self];
[item setAction:@selector(sliderAction:)];
[self updateSlider:item withSettings:settings];
return item.autorelease();
}
- (void)updateSlider:(NSSliderTouchBarItem*)item
withSettings:(const mate::PersistentDictionary&)settings {
std::string label;
settings.Get("label", &label);
item.label = base::SysUTF8ToNSString(label);
int maxValue = 100;
int minValue = 0;
int value = 50;
settings.Get("minValue", &minValue);
settings.Get("maxValue", &maxValue);
settings.Get("value", &value);
item.slider.minValue = minValue;
item.slider.maxValue = maxValue;
item.slider.doubleValue = value;
}
- (NSTouchBarItem*)makePopoverForID:(NSString*)id
withIdentifier:(NSString*)identifier {
std::string s_id([id UTF8String]);
if (![self hasItemWithID:s_id])
return nil;
mate::PersistentDictionary settings = settings_[s_id];
base::scoped_nsobject<NSPopoverTouchBarItem> item([[NSClassFromString(
@"NSPopoverTouchBarItem") alloc] initWithIdentifier:identifier]);
[self updatePopover:item withSettings:settings];
return item.autorelease();
}
- (void)updatePopover:(NSPopoverTouchBarItem*)item
withSettings:(const mate::PersistentDictionary&)settings {
std::string label;
settings.Get("label", &label);
item.collapsedRepresentationLabel = base::SysUTF8ToNSString(label);
gfx::Image image;
if (settings.Get("icon", &image)) {
item.collapsedRepresentationImage = image.AsNSImage();
}
bool showCloseButton = true;
settings.Get("showCloseButton", &showCloseButton);
item.showsCloseButton = showCloseButton;
mate::PersistentDictionary child;
std::vector<mate::PersistentDictionary> items;
if (settings.Get("child", &child) && child.Get("ordereredItems", &items)) {
item.popoverTouchBar =
[self touchBarFromItemIdentifiers:[self identifiersFromSettings:items]];
}
}
- (NSTouchBarItem*)makeGroupForID:(NSString*)id
withIdentifier:(NSString*)identifier {
std::string s_id([id UTF8String]);
if (![self hasItemWithID:s_id])
return nil;
mate::PersistentDictionary settings = settings_[s_id];
mate::PersistentDictionary child;
if (!settings.Get("child", &child))
return nil;
std::vector<mate::PersistentDictionary> items;
if (!child.Get("ordereredItems", &items))
return nil;
NSMutableArray* generatedItems = [NSMutableArray array];
NSMutableArray* identifiers = [self identifiersFromSettings:items];
for (NSUInteger i = 0; i < [identifiers count]; ++i) {
if ([identifiers objectAtIndex:i] !=
NSTouchBarItemIdentifierOtherItemsProxy) {
NSTouchBarItem* generatedItem =
[self makeItemForIdentifier:[identifiers objectAtIndex:i]];
if (generatedItem) {
[generatedItems addObject:generatedItem];
}
}
}
return [NSClassFromString(@"NSGroupTouchBarItem")
groupItemWithIdentifier:identifier
items:generatedItems];
}
- (void)updateGroup:(NSGroupTouchBarItem*)item
withSettings:(const mate::PersistentDictionary&)settings
API_AVAILABLE(macosx(10.12.2)) {
mate::PersistentDictionary child;
if (!settings.Get("child", &child))
return;
std::vector<mate::PersistentDictionary> items;
if (!child.Get("ordereredItems", &items))
return;
item.groupTouchBar =
[self touchBarFromItemIdentifiers:[self identifiersFromSettings:items]];
}
- (NSTouchBarItem*)makeSegmentedControlForID:(NSString*)id
withIdentifier:(NSString*)identifier
API_AVAILABLE(macosx(10.12.2)) {
std::string s_id([id UTF8String]);
if (![self hasItemWithID:s_id])
return nil;
mate::PersistentDictionary settings = settings_[s_id];
base::scoped_nsobject<NSCustomTouchBarItem> item([[NSClassFromString(
@"NSCustomTouchBarItem") alloc] initWithIdentifier:identifier]);
NSSegmentedControl* control = [NSSegmentedControl
segmentedControlWithLabels:[NSMutableArray array]
trackingMode:NSSegmentSwitchTrackingSelectOne
target:self
action:@selector(segmentedControlAction:)];
control.tag = [id floatValue];
[item setView:control];
[self updateSegmentedControl:item withSettings:settings];
return item.autorelease();
}
- (void)updateSegmentedControl:(NSCustomTouchBarItem*)item
withSettings:(const mate::PersistentDictionary&)settings
API_AVAILABLE(macosx(10.12.2)) {
NSSegmentedControl* control = item.view;
std::string segmentStyle;
settings.Get("segmentStyle", &segmentStyle);
if (segmentStyle == "rounded")
control.segmentStyle = NSSegmentStyleRounded;
else if (segmentStyle == "textured-rounded")
control.segmentStyle = NSSegmentStyleTexturedRounded;
else if (segmentStyle == "round-rect")
control.segmentStyle = NSSegmentStyleRoundRect;
else if (segmentStyle == "textured-square")
control.segmentStyle = NSSegmentStyleTexturedSquare;
else if (segmentStyle == "capsule")
control.segmentStyle = NSSegmentStyleCapsule;
else if (segmentStyle == "small-square")
control.segmentStyle = NSSegmentStyleSmallSquare;
else if (segmentStyle == "separated")
control.segmentStyle = NSSegmentStyleSeparated;
else
control.segmentStyle = NSSegmentStyleAutomatic;
std::string segmentMode;
settings.Get("mode", &segmentMode);
if (segmentMode == "multiple")
control.trackingMode = NSSegmentSwitchTrackingSelectAny;
else if (segmentMode == "buttons")
control.trackingMode = NSSegmentSwitchTrackingMomentary;
else
control.trackingMode = NSSegmentSwitchTrackingSelectOne;
std::vector<mate::Dictionary> segments;
settings.Get("segments", &segments);
control.segmentCount = segments.size();
for (size_t i = 0; i < segments.size(); ++i) {
std::string label;
gfx::Image image;
bool enabled = true;
segments[i].Get("enabled", &enabled);
if (segments[i].Get("label", &label)) {
[control setLabel:base::SysUTF8ToNSString(label) forSegment:i];
} else {
[control setLabel:@"" forSegment:i];
}
if (segments[i].Get("icon", &image)) {
[control setImage:image.AsNSImage() forSegment:i];
[control setImageScaling:NSImageScaleProportionallyUpOrDown forSegment:i];
} else {
[control setImage:nil forSegment:i];
}
[control setEnabled:enabled forSegment:i];
}
int selectedIndex = 0;
settings.Get("selectedIndex", &selectedIndex);
if (selectedIndex >= 0 && selectedIndex < control.segmentCount)
control.selectedSegment = selectedIndex;
}
- (NSTouchBarItem*)makeScrubberForID:(NSString*)id
withIdentifier:(NSString*)identifier
API_AVAILABLE(macosx(10.12.2)) {
std::string s_id([id UTF8String]);
if (![self hasItemWithID:s_id])
return nil;
mate::PersistentDictionary settings = settings_[s_id];
base::scoped_nsobject<NSCustomTouchBarItem> item([[NSClassFromString(
@"NSCustomTouchBarItem") alloc] initWithIdentifier:identifier]);
NSScrubber* scrubber = [[[NSClassFromString(@"NSScrubber") alloc]
initWithFrame:NSZeroRect] autorelease];
[scrubber registerClass:NSClassFromString(@"NSScrubberTextItemView")
forItemIdentifier:TextScrubberItemIdentifier];
[scrubber registerClass:NSClassFromString(@"NSScrubberImageItemView")
forItemIdentifier:ImageScrubberItemIdentifier];
scrubber.delegate = self;
scrubber.dataSource = self;
scrubber.identifier = id;
[item setView:scrubber];
[self updateScrubber:item withSettings:settings];
return item.autorelease();
}
- (void)updateScrubber:(NSCustomTouchBarItem*)item
withSettings:(const mate::PersistentDictionary&)settings
API_AVAILABLE(macosx(10.12.2)) {
NSScrubber* scrubber = item.view;
bool showsArrowButtons = false;
settings.Get("showArrowButtons", &showsArrowButtons);
scrubber.showsArrowButtons = showsArrowButtons;
std::string selectedStyle;
std::string overlayStyle;
settings.Get("selectedStyle", &selectedStyle);
settings.Get("overlayStyle", &overlayStyle);
if (selectedStyle == "outline") {
scrubber.selectionBackgroundStyle =
[NSClassFromString(@"NSScrubberSelectionStyle") outlineOverlayStyle];
} else if (selectedStyle == "background") {
scrubber.selectionBackgroundStyle =
[NSClassFromString(@"NSScrubberSelectionStyle") roundedBackgroundStyle];
} else {
scrubber.selectionBackgroundStyle = nil;
}
if (overlayStyle == "outline") {
scrubber.selectionOverlayStyle =
[NSClassFromString(@"NSScrubberSelectionStyle") outlineOverlayStyle];
} else if (overlayStyle == "background") {
scrubber.selectionOverlayStyle =
[NSClassFromString(@"NSScrubberSelectionStyle") roundedBackgroundStyle];
} else {
scrubber.selectionOverlayStyle = nil;
}
std::string mode;
settings.Get("mode", &mode);
if (mode == "fixed") {
scrubber.mode = NSScrubberModeFixed;
} else {
scrubber.mode = NSScrubberModeFree;
}
bool continuous = true;
settings.Get("continuous", &continuous);
scrubber.continuous = continuous;
[scrubber reloadData];
}
- (NSInteger)numberOfItemsForScrubber:(NSScrubber*)scrubber
API_AVAILABLE(macosx(10.12.2)) {
std::string s_id([[scrubber identifier] UTF8String]);
if (![self hasItemWithID:s_id])
return 0;
mate::PersistentDictionary settings = settings_[s_id];
std::vector<mate::PersistentDictionary> items;
settings.Get("items", &items);
return items.size();
}
- (NSScrubberItemView*)scrubber:(NSScrubber*)scrubber
viewForItemAtIndex:(NSInteger)index
API_AVAILABLE(macosx(10.12.2)) {
std::string s_id([[scrubber identifier] UTF8String]);
if (![self hasItemWithID:s_id])
return nil;
mate::PersistentDictionary settings = settings_[s_id];
std::vector<mate::PersistentDictionary> items;
if (!settings.Get("items", &items))
return nil;
if (index >= static_cast<NSInteger>(items.size()))
return nil;
mate::PersistentDictionary item = items[index];
NSScrubberItemView* itemView;
std::string title;
if (item.Get("label", &title)) {
NSScrubberTextItemView* view =
[scrubber makeItemWithIdentifier:TextScrubberItemIdentifier owner:self];
view.title = base::SysUTF8ToNSString(title);
itemView = view;
} else {
NSScrubberImageItemView* view =
[scrubber makeItemWithIdentifier:ImageScrubberItemIdentifier
owner:self];
gfx::Image image;
if (item.Get("icon", &image)) {
view.image = image.AsNSImage();
}
itemView = view;
}
return itemView;
}
- (NSSize)scrubber:(NSScrubber*)scrubber
layout:(NSScrubberFlowLayout*)layout
sizeForItemAtIndex:(NSInteger)itemIndex API_AVAILABLE(macosx(10.12.2)) {
NSInteger width = 50;
NSInteger height = 30;
NSInteger margin = 15;
NSSize defaultSize = NSMakeSize(width, height);
std::string s_id([[scrubber identifier] UTF8String]);
if (![self hasItemWithID:s_id])
return defaultSize;
mate::PersistentDictionary settings = settings_[s_id];
std::vector<mate::PersistentDictionary> items;
if (!settings.Get("items", &items))
return defaultSize;
if (itemIndex >= static_cast<NSInteger>(items.size()))
return defaultSize;
mate::PersistentDictionary item = items[itemIndex];
std::string title;
if (item.Get("label", &title)) {
NSSize size = NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX);
NSRect textRect = [base::SysUTF8ToNSString(title)
boundingRectWithSize:size
options:NSStringDrawingUsesLineFragmentOrigin |
NSStringDrawingUsesFontLeading
attributes:@{
NSFontAttributeName : [NSFont systemFontOfSize:0]
}];
width = textRect.size.width + margin;
} else {
gfx::Image image;
if (item.Get("icon", &image)) {
width = image.AsNSImage().size.width;
}
}
return NSMakeSize(width, height);
}
@end

View file

@ -0,0 +1,21 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/ui/cocoa/delayed_native_view_host.h"
namespace atom {
DelayedNativeViewHost::DelayedNativeViewHost(gfx::NativeView native_view)
: native_view_(native_view) {}
DelayedNativeViewHost::~DelayedNativeViewHost() {}
void DelayedNativeViewHost::ViewHierarchyChanged(
const views::ViewHierarchyChangedDetails& details) {
NativeViewHost::ViewHierarchyChanged(details);
if (details.is_add && GetWidget())
Attach(native_view_);
}
} // namespace atom

View file

@ -0,0 +1,31 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_UI_COCOA_DELAYED_NATIVE_VIEW_HOST_H_
#define ATOM_BROWSER_UI_COCOA_DELAYED_NATIVE_VIEW_HOST_H_
#include "ui/views/controls/native/native_view_host.h"
namespace atom {
// Automatically attach the native view after the NativeViewHost is attached to
// a widget. (Attaching it directly would cause crash.)
class DelayedNativeViewHost : public views::NativeViewHost {
public:
explicit DelayedNativeViewHost(gfx::NativeView native_view);
~DelayedNativeViewHost() override;
// views::View:
void ViewHierarchyChanged(
const views::ViewHierarchyChangedDetails& details) override;
private:
gfx::NativeView native_view_;
DISALLOW_COPY_AND_ASSIGN(DelayedNativeViewHost);
};
} // namespace atom
#endif // ATOM_BROWSER_UI_COCOA_DELAYED_NATIVE_VIEW_HOST_H_

View file

@ -0,0 +1,19 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_UI_COCOA_EVENT_DISPATCHING_WINDOW_H_
#define ATOM_BROWSER_UI_COCOA_EVENT_DISPATCHING_WINDOW_H_
#import "ui/base/cocoa/underlay_opengl_hosting_window.h"
@interface EventDispatchingWindow : UnderlayOpenGLHostingWindow {
@private
BOOL redispatchingEvent_;
}
- (void)redispatchKeyEvent:(NSEvent*)event;
@end
#endif // ATOM_BROWSER_UI_COCOA_EVENT_DISPATCHING_WINDOW_H_

View file

@ -0,0 +1,34 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/ui/cocoa/event_dispatching_window.h"
@implementation EventDispatchingWindow
- (void)sendEvent:(NSEvent*)event {
if (!redispatchingEvent_)
[super sendEvent:event];
}
- (BOOL)performKeyEquivalent:(NSEvent*)event {
if (redispatchingEvent_)
return NO;
else
return [super performKeyEquivalent:event];
}
- (void)redispatchKeyEvent:(NSEvent*)event {
NSEventType eventType = [event type];
if (eventType != NSKeyDown && eventType != NSKeyUp &&
eventType != NSFlagsChanged) {
return;
}
// Redispatch the event.
redispatchingEvent_ = YES;
[NSApp sendEvent:event];
redispatchingEvent_ = NO;
}
@end

View file

@ -0,0 +1,33 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_UI_COCOA_ROOT_VIEW_MAC_H_
#define ATOM_BROWSER_UI_COCOA_ROOT_VIEW_MAC_H_
#include "ui/views/view.h"
namespace atom {
class NativeWindow;
class RootViewMac : public views::View {
public:
explicit RootViewMac(NativeWindow* window);
~RootViewMac() override;
// views::View:
void Layout() override;
gfx::Size GetMinimumSize() const override;
gfx::Size GetMaximumSize() const override;
private:
// Parent window, weak ref.
NativeWindow* window_;
DISALLOW_COPY_AND_ASSIGN(RootViewMac);
};
} // namespace atom
#endif // ATOM_BROWSER_UI_COCOA_ROOT_VIEW_MAC_H_

View file

@ -0,0 +1,32 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/ui/cocoa/root_view_mac.h"
#include "atom/browser/native_window.h"
namespace atom {
RootViewMac::RootViewMac(NativeWindow* window) : window_(window) {
set_owned_by_client();
}
RootViewMac::~RootViewMac() {}
void RootViewMac::Layout() {
if (!window_->content_view()) // Not ready yet.
return;
window_->content_view()->SetBoundsRect(gfx::Rect(gfx::Point(), size()));
}
gfx::Size RootViewMac::GetMinimumSize() const {
return window_->GetMinimumSize();
}
gfx::Size RootViewMac::GetMaximumSize() const {
return window_->GetMaximumSize();
}
} // namespace atom

View file

@ -0,0 +1,267 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_UI_COCOA_TOUCH_BAR_FORWARD_DECLARATIONS_H_
#define ATOM_BROWSER_UI_COCOA_TOUCH_BAR_FORWARD_DECLARATIONS_H_
// Once Chrome no longer supports OSX 10.12.0, this file can be deleted.
#import <Foundation/Foundation.h>
#if !defined(MAC_OS_X_VERSION_10_12_1)
#pragma clang assume_nonnull begin
@class NSTouchBar, NSTouchBarItem;
@class NSScrubber, NSScrubberItemView, NSScrubberArrangedView,
NSScrubberTextItemView, NSScrubberImageItemView, NSScrubberSelectionStyle;
@protocol NSTouchBarDelegate
, NSScrubberDelegate, NSScrubberDataSource, NSScrubberFlowLayoutDelegate,
NSScrubberFlowLayout;
typedef float NSTouchBarItemPriority;
static const NSTouchBarItemPriority NSTouchBarItemPriorityHigh = 1000;
static const NSTouchBarItemPriority NSTouchBarItemPriorityNormal = 0;
static const NSTouchBarItemPriority NSTouchBarItemPriorityLow = -1000;
enum NSScrubberMode { NSScrubberModeFixed = 0, NSScrubberModeFree };
typedef NSString* NSTouchBarItemIdentifier;
typedef NSString* NSTouchBarCustomizationIdentifier;
static const NSTouchBarItemIdentifier NSTouchBarItemIdentifierFixedSpaceSmall =
@"NSTouchBarItemIdentifierFixedSpaceSmall";
static const NSTouchBarItemIdentifier NSTouchBarItemIdentifierFixedSpaceLarge =
@"NSTouchBarItemIdentifierFixedSpaceLarge";
static const NSTouchBarItemIdentifier NSTouchBarItemIdentifierFlexibleSpace =
@"NSTouchBarItemIdentifierFlexibleSpace";
static const NSTouchBarItemIdentifier NSTouchBarItemIdentifierOtherItemsProxy =
@"NSTouchBarItemIdentifierOtherItemsProxy";
@interface NSTouchBar : NSObject <NSCoding>
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder*)aDecoder
NS_DESIGNATED_INITIALIZER;
@property(copy, nullable)
NSTouchBarCustomizationIdentifier customizationIdentifier;
@property(copy) NSArray* customizationAllowedItemIdentifiers;
@property(copy) NSArray* customizationRequiredItemIdentifiers;
@property(copy) NSArray* defaultItemIdentifiers;
@property(copy, readonly) NSArray* itemIdentifiers;
@property(copy, nullable) NSTouchBarItemIdentifier principalItemIdentifier;
@property(copy, nullable)
NSTouchBarItemIdentifier escapeKeyReplacementItemIdentifier;
@property(copy) NSSet* templateItems;
@property(nullable, weak) id<NSTouchBarDelegate> delegate;
- (nullable __kindof NSTouchBarItem*)itemForIdentifier:
(NSTouchBarItemIdentifier)identifier;
@property(readonly, getter=isVisible) BOOL visible;
@end
@interface NSTouchBarItem : NSObject <NSCoding>
- (instancetype)initWithIdentifier:(NSTouchBarItemIdentifier)identifier
NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder*)coder
NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@property(readonly, copy) NSTouchBarItemIdentifier identifier;
@property NSTouchBarItemPriority visibilityPriority;
@property(readonly, nullable) NSView* view;
@property(readonly, nullable) NSViewController* viewController;
@property(readwrite, copy) NSString* customizationLabel;
@property(readonly, getter=isVisible) BOOL visible;
@end
@interface NSGroupTouchBarItem : NSTouchBarItem
+ (NSGroupTouchBarItem*)groupItemWithIdentifier:
(NSTouchBarItemIdentifier)identifier
items:(NSArray*)items;
@property(strong) NSTouchBar* groupTouchBar;
@property(readwrite, copy, null_resettable) NSString* customizationLabel;
@end
@interface NSCustomTouchBarItem : NSTouchBarItem
@property(readwrite, strong) __kindof NSView* view;
@property(readwrite, strong, nullable)
__kindof NSViewController* viewController;
@property(readwrite, copy, null_resettable) NSString* customizationLabel;
@end
@interface NSColorPickerTouchBarItem : NSTouchBarItem
@property SEL action;
@property(weak) id target;
@property(copy) NSColor* color;
@property(strong) NSColorList* colorList;
@end
@interface NSPopoverTouchBarItem : NSTouchBarItem
@property BOOL showsCloseButton;
@property(strong) NSImage* collapsedRepresentationImage;
@property(strong) NSString* collapsedRepresentationLabel;
@property(strong) NSTouchBar* popoverTouchBar;
@end
@interface NSSliderTouchBarItem : NSTouchBarItem
@property SEL action;
@property(weak) id target;
@property(copy) NSString* label;
@property(strong) NSSlider* slider;
@end
@interface NSScrubber : NSView
@property(weak) id<NSScrubberDelegate> delegate;
@property(weak) id<NSScrubberDataSource> dataSource;
@property NSScrubberMode mode;
@property BOOL showsArrowButtons;
@property(getter=isContinuous) BOOL continuous;
@property(strong, nullable) NSScrubberSelectionStyle* selectionBackgroundStyle;
@property(strong, nullable) NSScrubberSelectionStyle* selectionOverlayStyle;
- (void)registerClass:(Class)itemViewClass
forItemIdentifier:(NSString*)itemIdentifier;
- (__kindof NSScrubberItemView*)makeItemWithIdentifier:(NSString*)itemIdentifier
owner:(id)owner;
- (void)reloadData;
@end
@interface NSScrubberFlowLayout : NSObject
@end
@interface NSScrubberSelectionStyle : NSObject <NSCoding>
@property(class, strong, readonly)
NSScrubberSelectionStyle* outlineOverlayStyle;
@property(class, strong, readonly)
NSScrubberSelectionStyle* roundedBackgroundStyle;
@end
@interface NSScrubberArrangedView : NSView
@end
@interface NSScrubberItemView : NSScrubberArrangedView
@end
@interface NSScrubberTextItemView : NSScrubberItemView
@property(copy) NSString* title;
@end
@interface NSScrubberImageItemView : NSScrubberItemView
@property(copy) NSImage* image;
@end
@interface NSWindow (TouchBarSDK)
@property(strong, readwrite, nullable) NSTouchBar* touchBar;
@end
@interface NSButton (TouchBarSDK)
@property(copy) NSColor* bezelColor;
+ (instancetype)buttonWithTitle:(NSString*)title
target:(id)target
action:(SEL)action;
@end
@interface NSTextField (TouchBarSDK)
+ (instancetype)labelWithString:(NSString*)stringValue;
@end
@interface NSSegmentedControl (TouchBarSDK)
+ (instancetype)segmentedControlWithLabels:(NSArray*)labels
trackingMode:(NSSegmentSwitchTracking)trackingMode
target:(id)target
action:(SEL)action;
@end
@protocol NSTouchBarDelegate <NSObject>
@optional
- (nullable NSTouchBarItem*)touchBar:(NSTouchBar*)touchBar
makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier;
@end
@protocol NSScrubberDelegate <NSObject>
- (void)scrubber:(NSScrubber*)scrubber
didHighlightItemAtIndex:(NSInteger)highlightedIndex;
- (void)scrubber:(NSScrubber*)scrubber
didSelectItemAtIndex:(NSInteger)selectedIndex;
@end
@protocol NSScrubberDataSource <NSObject>
- (NSInteger)numberOfItemsForScrubber:(NSScrubber*)scrubber;
- (__kindof NSScrubberItemView*)scrubber:(NSScrubber*)scrubber
viewForItemAtIndex:(NSInteger)index;
@end
@protocol NSScrubberFlowLayoutDelegate <NSObject>
- (NSSize)scrubber:(NSScrubber*)scrubber
layout:(NSScrubberFlowLayout*)layout
sizeForItemAtIndex:(NSInteger)itemIndex;
@end
#pragma clang assume_nonnull end
#elif MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12_1
// When compiling against the 10.12.1 SDK or later, just provide forward
// declarations to suppress the partial availability warnings.
@class NSCustomTouchBarItem;
@class NSGroupTouchBarItem;
@class NSTouchBar;
@protocol NSTouchBarDelegate;
@class NSTouchBarItem;
@interface NSWindow (TouchBarSDK)
@property(strong, readonly) NSTouchBar* touchBar API_AVAILABLE(macosx(10.12.2));
@end
#endif // MAC_OS_X_VERSION_10_12_1
#endif // ATOM_BROWSER_UI_COCOA_TOUCH_BAR_FORWARD_DECLARATIONS_H_

View file

@ -0,0 +1,30 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_UI_COCOA_VIEWS_DELEGATE_MAC_H_
#define ATOM_BROWSER_UI_COCOA_VIEWS_DELEGATE_MAC_H_
#include "ui/views/views_delegate.h"
namespace atom {
class ViewsDelegateMac : public views::ViewsDelegate {
public:
ViewsDelegateMac();
~ViewsDelegateMac() override;
// ViewsDelegate:
void OnBeforeWidgetInit(
views::Widget::InitParams* params,
views::internal::NativeWidgetDelegate* delegate) override;
ui::ContextFactory* GetContextFactory() override;
ui::ContextFactoryPrivate* GetContextFactoryPrivate() override;
private:
DISALLOW_COPY_AND_ASSIGN(ViewsDelegateMac);
};
} // namespace atom
#endif // ATOM_BROWSER_UI_COCOA_VIEWS_DELEGATE_MAC_H_

View file

@ -0,0 +1,43 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/ui/cocoa/views_delegate_mac.h"
#include "content/public/browser/context_factory.h"
#include "ui/views/widget/native_widget_mac.h"
namespace atom {
ViewsDelegateMac::ViewsDelegateMac() {}
ViewsDelegateMac::~ViewsDelegateMac() {}
void ViewsDelegateMac::OnBeforeWidgetInit(
views::Widget::InitParams* params,
views::internal::NativeWidgetDelegate* delegate) {
// If we already have a native_widget, we don't have to try to come
// up with one.
if (params->native_widget)
return;
if (!native_widget_factory().is_null()) {
params->native_widget = native_widget_factory().Run(*params, delegate);
if (params->native_widget)
return;
}
// Setting null here causes Widget to create the default NativeWidget
// implementation.
params->native_widget = nullptr;
}
ui::ContextFactory* ViewsDelegateMac::GetContextFactory() {
return content::GetContextFactory();
}
ui::ContextFactoryPrivate* ViewsDelegateMac::GetContextFactoryPrivate() {
return content::GetContextFactoryPrivate();
}
} // namespace atom