Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions WAYWindow/WAYWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,15 @@
//// Returns the titlebar view of the window, which you can add arbitrary subviews to.
@property (strong,readonly) IBOutlet NSView *titleBarView;

/// If set to YES, the title of the window is vertically centered. Default: YES.
@property (nonatomic) IBInspectable BOOL verticallyCenterTitle;

/// If set to YES, the standard window button will be vertically centered. Default: YES.
@property (nonatomic) IBInspectable BOOL centerTrafficLightButtons;

/// If set to YES, the traffic light buttons are displayed in vertical orientation. Default: NO.
@property (nonatomic) IBInspectable BOOL verticalTrafficLightButtons;

/// Defines the left margin of the standard window buttons. Defaut: OS X default value.
@property (nonatomic) IBInspectable CGFloat trafficLightButtonsLeftMargin;

Expand All @@ -50,6 +56,9 @@
/// If set to YES, the title of the window will be hidden. Default: YES.
@property (nonatomic) IBInspectable BOOL hidesTitle;

/// If set to YES, the title of the window is shown. Default: NO.
@property (nonatomic) IBInspectable BOOL showsTitle;

/// Replaces the window's content view with an instance of NSVisualEffectView and applies the Vibrant Dark look. Transfers all subviews to the new content view.
- (void) setContentViewAppearanceVibrantDark;

Expand Down
210 changes: 153 additions & 57 deletions WAYWindow/WAYWindow.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
//

#import "WAYWindow.h"
#import <Carbon/Carbon.h>

/**
Convenience methods to make NSPointerArray act like a stack of selectors. It would be a subclass, but NSPointerArray
Expand Down Expand Up @@ -199,41 +200,58 @@ - (void) orderFront:(id)sender {
#pragma mark - Public

- (NSView *) titleBarView {
return [_standardButtons[0] superview];
return [_standardButtons[0] superview];
}

- (void) setCenterTrafficLightButtons:(BOOL)centerTrafficLightButtons {
_centerTrafficLightButtons = centerTrafficLightButtons;
[self _setNeedsLayout];
_centerTrafficLightButtons = centerTrafficLightButtons;
[self _setNeedsLayout];
}

- (void) setTitleBarHeight:(CGFloat)titleBarHeight {
- (void) setVerticalTrafficLightButtons:(BOOL)verticalTrafficLightButtons {
_verticalTrafficLightButtons = verticalTrafficLightButtons;
[self _setNeedsLayout];
}

titleBarHeight = MAX(titleBarHeight,[[self class] defaultTitleBarHeight]);
CGFloat delta = titleBarHeight - _titleBarHeight;
_titleBarHeight = titleBarHeight;

if (_dummyTitlebarAccessoryViewController) {
[self removeTitlebarAccessoryViewControllerAtIndex:0];
}

NSView *view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 10, titleBarHeight-[WAYWindow defaultTitleBarHeight])];
_dummyTitlebarAccessoryViewController = [NSTitlebarAccessoryViewController new];
_dummyTitlebarAccessoryViewController.view = view;
_dummyTitlebarAccessoryViewController.fullScreenMinHeight = titleBarHeight;
[self addTitlebarAccessoryViewController:_dummyTitlebarAccessoryViewController];

NSRect frame = self.frame;
frame.size.height += delta;
frame.origin.y -= delta;

[self _setNeedsLayout];
[self setFrame:frame display:NO]; // NO is important.
- (void) setVerticallyCenterTitle:(BOOL)verticallyCenterTitle {
_verticallyCenterTitle = verticallyCenterTitle;
[self _setNeedsLayout];
}

- (void) setTitleBarHeight:(CGFloat)titleBarHeight {

titleBarHeight = MAX(titleBarHeight,[[self class] defaultTitleBarHeight]);
CGFloat delta = titleBarHeight - _titleBarHeight;
_titleBarHeight = titleBarHeight;

if (_dummyTitlebarAccessoryViewController) {
[self removeTitlebarAccessoryViewControllerAtIndex:0];
}

NSView *view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 10, titleBarHeight-[WAYWindow defaultTitleBarHeight])];
_dummyTitlebarAccessoryViewController = [NSTitlebarAccessoryViewController new];
_dummyTitlebarAccessoryViewController.view = view;
_dummyTitlebarAccessoryViewController.fullScreenMinHeight = titleBarHeight;
[self addTitlebarAccessoryViewController:_dummyTitlebarAccessoryViewController];

NSRect frame = self.frame;
frame.size.height += delta;
frame.origin.y -= delta;

[self _setNeedsLayout];
[self setFrame:frame display:NO]; // NO is important.
}

- (void) setHidesTitle:(BOOL)hidesTitle {
_hidesTitle = hidesTitle;
[self setTitleVisibility:hidesTitle ? NSWindowTitleHidden : NSWindowTitleVisible];
_hidesTitle = hidesTitle;
_showsTitle = !hidesTitle;
[self setTitleVisibility:hidesTitle ? NSWindowTitleHidden : NSWindowTitleVisible];
}

- (void) setShowsTitle:(BOOL)showsTitle {
_showsTitle = showsTitle;
_hidesTitle = !showsTitle;
[self setTitleVisibility:showsTitle ? NSWindowTitleVisible : NSWindowTitleHidden];
}

- (void) setContentViewAppearanceVibrantDark {
Expand Down Expand Up @@ -305,40 +323,118 @@ - (NSView *) replaceSubview:(NSView *)aView withViewOfClass:(Class)newViewClass
#pragma mark - Private

- (void) _setUp {
_delegateProxy = [[WAYWindowDelegateProxy alloc] init];
_delegateProxy.firstDelegate = self;
super.delegate = _delegateProxy;

_standardButtons = @[[self standardWindowButton:NSWindowCloseButton],
[self standardWindowButton:NSWindowMiniaturizeButton],
[self standardWindowButton:NSWindowZoomButton]];
_centerTrafficLightButtons = YES;

NSButton *closeButton = [self standardWindowButton:NSWindowCloseButton];
kWAYWindowDefaultTrafficLightButtonsLeftMargin = NSMinX(closeButton.frame);
kWAYWindowDefaultTrafficLightButtonsTopMargin = NSHeight(closeButton.superview.frame)-NSMaxY(closeButton.frame);

self.styleMask |= NSFullSizeContentViewWindowMask;
_trafficLightButtonsLeftMargin = kWAYWindowDefaultTrafficLightButtonsLeftMargin;
_trafficLightButtonsTopMargin = kWAYWindowDefaultTrafficLightButtonsTopMargin;

self.hidesTitle = YES;

[super setDelegate:self];
[self _setNeedsLayout];
_delegateProxy = [[WAYWindowDelegateProxy alloc] init];
_delegateProxy.firstDelegate = self;
super.delegate = _delegateProxy;

_standardButtons = @[[self standardWindowButton:NSWindowCloseButton],
[self standardWindowButton:NSWindowMiniaturizeButton],
[self standardWindowButton:NSWindowZoomButton]];
_centerTrafficLightButtons = YES;

NSButton *closeButton = [self standardWindowButton:NSWindowCloseButton];
kWAYWindowDefaultTrafficLightButtonsLeftMargin = NSMinX(closeButton.frame);
kWAYWindowDefaultTrafficLightButtonsTopMargin = NSHeight(closeButton.superview.frame)-NSMaxY(closeButton.frame);

self.styleMask |= NSFullSizeContentViewWindowMask;
_trafficLightButtonsLeftMargin = kWAYWindowDefaultTrafficLightButtonsLeftMargin;
_trafficLightButtonsTopMargin = kWAYWindowDefaultTrafficLightButtonsTopMargin;

self.hidesTitle = YES;
self.showsTitle = NO;
self.verticallyCenterTitle = YES;

[super setDelegate:self];
[self _setNeedsLayout];
}

- (void) _setNeedsLayout {
[_standardButtons enumerateObjectsUsingBlock:^(NSButton *standardButton, NSUInteger idx, BOOL *stop) {
NSRect frame = standardButton.frame;
if (_centerTrafficLightButtons)
frame.origin.y = NSHeight(standardButton.superview.frame)/2-NSHeight(standardButton.frame)/2;
else
frame.origin.y = NSHeight(standardButton.superview.frame)-NSHeight(standardButton.frame)-_trafficLightButtonsTopMargin;

frame.origin.x = _trafficLightButtonsLeftMargin +idx*(NSWidth(frame) + 6);
[standardButton setFrame:frame];
}];
[_standardButtons enumerateObjectsUsingBlock:^(NSButton *standardButton, NSUInteger idx, BOOL *stop) {
NSRect frame = standardButton.frame;
CGFloat trafficLightSeparation = 5.0; // db: modified from the original
if (_verticalTrafficLightButtons)
{
if (_centerTrafficLightButtons)
{
CGFloat trafficLightsHeight = 3.0*NSHeight(frame) + 2.0*trafficLightSeparation;
CGFloat offset = (_titleBarHeight - trafficLightsHeight)/2.0;
frame.origin.y = _titleBarHeight - NSHeight(frame) - idx*(NSHeight(frame) + trafficLightSeparation) - offset;
}
else
{
frame.origin.y = (_titleBarHeight - NSHeight(frame) - _trafficLightButtonsTopMargin) - idx*(NSHeight(frame) + trafficLightSeparation);
}

frame.origin.x = _trafficLightButtonsLeftMargin;
[standardButton setFrame:frame];
}
else
{
if (_centerTrafficLightButtons)
frame.origin.y = NSHeight(standardButton.superview.frame)/2-NSHeight(standardButton.frame)/2;
else
frame.origin.y = NSHeight(standardButton.superview.frame)-NSHeight(standardButton.frame)-_trafficLightButtonsTopMargin;

frame.origin.x = _trafficLightButtonsLeftMargin +idx*(NSWidth(frame) + 6.0);
[standardButton setFrame:frame];
}
}];

// Vertically center window title if necessary.
if (self.verticallyCenterTitle) {
[self layoutVerticallyCenterTitle];
}
}

// Position the title so it is centered vertically in the parent's view. This is done by traversing
// the view hierarchy and vertically centering the title name, title edit state, document icon, and
// autosave button according to class names. Naturally, centering won't work if class names change.
- (void) layoutVerticallyCenterTitle
{
NSRect drawingRect = self.titleBarView.frame;
if (self.showsTitle)
{
NSRect titleTextRect;
NSDictionary *titleTextStyles = nil;
[self getTitleFrame:&titleTextRect textAttributes:&titleTextStyles forWindow:self];
titleTextRect.origin.y = floor(NSMidY(drawingRect) - (NSHeight(titleTextRect) / 2.f) + 1);
for (NSView * view in self.titleBarView.subviews)
{
if ([[view className] isEqualToString:[NSTextField className]] || [[view className] isEqualToString:@"NSThemeDocumentButton"] || [[view className] isEqualToString:@"NSThemeAutosaveButton"])
{
[view setFrameOrigin:NSMakePoint(view.frame.origin.x, titleTextRect.origin.y)];
}
}
}
}

- (void) getNativeTitleTextInfo:(out HIThemeTextInfo *)titleTextInfo
{
BOOL drawsAsMainWindow = (self.isMainWindow && [NSApplication sharedApplication].isActive);
titleTextInfo->version = 1;
titleTextInfo->state = drawsAsMainWindow ? kThemeStateActive : kThemeStateUnavailableInactive;
titleTextInfo->fontID = kThemeWindowTitleFont;
titleTextInfo->horizontalFlushness = kHIThemeTextHorizontalFlushCenter;
titleTextInfo->verticalFlushness = kHIThemeTextVerticalFlushCenter;
titleTextInfo->options = kHIThemeTextBoxOptionEngraved;
titleTextInfo->truncationPosition = kHIThemeTextTruncationDefault;
titleTextInfo->truncationMaxLines = 1;
}

- (void) getTitleFrame:(out NSRect *)frame textAttributes:(out NSDictionary **)attributes forWindow:(in WAYWindow *)window
{
NSSize titleSize;
NSRect titleTextRect;

HIThemeTextInfo titleTextInfo;
[self getNativeTitleTextInfo:&titleTextInfo];
HIThemeGetTextDimensions((__bridge CFTypeRef)(window.title), 0, &titleTextInfo, &titleSize.width, &titleSize.height, NULL);

titleTextRect.size = titleSize;

if (frame) {
*frame = titleTextRect;
}
}

#pragma mark - NSWindow Delegate
Expand Down
2 changes: 1 addition & 1 deletion WAYWindowDemo/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[_window setVibrantDarkAppearance];
[_window setContentViewAppearanceVibrantDark];
[_window.titleBarView addSubview:_titlebar];
[_window.titleBarView addSubview:_titlebar];
}

- (void)applicationWillTerminate:(NSNotification *)aNotification {
Expand Down