#import static NSString *const kElectrobunVibrancyViewIdentifier = @"ElectrobunVibrancyView"; static NSString *const kElectrobunNativeDragViewIdentifier = @"ElectrobunNativeDragView"; static NSString *const kElectrobunWindowBorderViewIdentifier = @"ElectrobunWindowBorderView"; static CGFloat gWindowCornerRadius = 15.0; @interface ElectrobunWindowBorderView : NSView @property(nonatomic) CGFloat cornerRadius; @end @implementation ElectrobunWindowBorderView @synthesize cornerRadius = _cornerRadius; - (instancetype)initWithFrame:(NSRect)frameRect { self = [super initWithFrame:frameRect]; if (self) { _cornerRadius = gWindowCornerRadius; } return self; } - (BOOL)isOpaque { return NO; } - (BOOL)acceptsFirstMouse:(NSEvent *)event { (void)event; return NO; } - (NSView *)hitTest:(NSPoint)point { (void)point; return nil; } - (void)setFrame:(NSRect)frame { [super setFrame:frame]; [self setNeedsDisplay:YES]; } - (void)drawRect:(NSRect)dirtyRect { (void)dirtyRect; NSRect bounds = [self bounds]; CGFloat radius = MAX(0.0, self.cornerRadius); NSBezierPath *borderPath = [NSBezierPath bezierPathWithRoundedRect:NSInsetRect(bounds, 0.5, 0.5) xRadius:radius yRadius:radius]; if (@available(macOS 10.14, *)) { NSAppearance *appearance = [self effectiveAppearance]; BOOL isDark = [[appearance bestMatchFromAppearancesWithNames:@[ NSAppearanceNameAqua, NSAppearanceNameDarkAqua ]] isEqualToString:NSAppearanceNameDarkAqua]; if (isDark) { [[NSColor colorWithWhite:1.0 alpha:0.18] setStroke]; } else { [[NSColor colorWithWhite:1.0 alpha:0.72] setStroke]; } } else { [[NSColor colorWithWhite:1.0 alpha:0.72] setStroke]; } borderPath.lineWidth = 1.0; [borderPath stroke]; } @end @interface ElectrobunNativeDragView : NSView @end @implementation ElectrobunNativeDragView - (BOOL)isOpaque { return NO; } - (void)drawRect:(NSRect)dirtyRect { (void)dirtyRect; } - (void)mouseDown:(NSEvent *)event { NSWindow *window = [self window]; if (window != nil && event != nil) { [window performWindowDragWithEvent:event]; } } @end static NSVisualEffectView *findVibrancyView(NSView *contentView) { for (NSView *subview in [contentView subviews]) { if ([subview isKindOfClass:[NSVisualEffectView class]] && [[subview identifier] isEqualToString:kElectrobunVibrancyViewIdentifier]) { return (NSVisualEffectView *)subview; } } return nil; } static ElectrobunNativeDragView *findNativeDragView(NSView *contentView) { for (NSView *subview in [contentView subviews]) { if ([subview isKindOfClass:[ElectrobunNativeDragView class]] && [[subview identifier] isEqualToString:kElectrobunNativeDragViewIdentifier]) { return (ElectrobunNativeDragView *)subview; } } return nil; } static ElectrobunWindowBorderView *findWindowBorderView(NSView *contentView) { for (NSView *subview in [contentView subviews]) { if ([subview isKindOfClass:[ElectrobunWindowBorderView class]] && [[subview identifier] isEqualToString:kElectrobunWindowBorderViewIdentifier]) { return (ElectrobunWindowBorderView *)subview; } } return nil; } static void applyWindowCornerRadius(NSWindow *window, CGFloat radius) { gWindowCornerRadius = MAX(0.0, radius); NSView *contentView = [window contentView]; if (contentView == nil) { return; } contentView.wantsLayer = YES; contentView.layer.cornerRadius = gWindowCornerRadius; contentView.layer.masksToBounds = YES; NSVisualEffectView *effectView = findVibrancyView(contentView); if (effectView != nil) { effectView.wantsLayer = YES; effectView.layer.cornerRadius = gWindowCornerRadius; effectView.layer.masksToBounds = YES; } ElectrobunWindowBorderView *borderView = findWindowBorderView(contentView); if (borderView != nil) { borderView.cornerRadius = gWindowCornerRadius; [borderView setNeedsDisplay:YES]; } } static void ensureWindowBorder(NSWindow *window) { NSView *contentView = [window contentView]; if (contentView == nil) { return; } ElectrobunWindowBorderView *borderView = findWindowBorderView(contentView); if (borderView == nil) { borderView = [[ElectrobunWindowBorderView alloc] initWithFrame:[contentView bounds]]; [borderView setIdentifier:kElectrobunWindowBorderViewIdentifier]; [borderView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; } borderView.cornerRadius = gWindowCornerRadius; [borderView setFrame:[contentView bounds]]; if ([borderView superview] == nil) { [contentView addSubview:borderView positioned:NSWindowAbove relativeTo:nil]; } else { [borderView removeFromSuperview]; [contentView addSubview:borderView positioned:NSWindowAbove relativeTo:nil]; } [borderView setNeedsDisplay:YES]; } extern "C" bool enableWindowVibrancy(void *windowPtr, double cornerRadius) { if (windowPtr == nullptr) { return false; } __block BOOL success = NO; dispatch_sync(dispatch_get_main_queue(), ^{ NSWindow *window = (__bridge NSWindow *)windowPtr; if (![window isKindOfClass:[NSWindow class]]) { return; } applyWindowCornerRadius(window, (CGFloat)cornerRadius); [window setOpaque:NO]; [window setBackgroundColor:[NSColor clearColor]]; [window setTitlebarAppearsTransparent:YES]; [window setHasShadow:YES]; NSView *contentView = [window contentView]; if (contentView == nil) { return; } NSVisualEffectView *effectView = findVibrancyView(contentView); if (effectView == nil) { effectView = [[NSVisualEffectView alloc] initWithFrame:[contentView bounds]]; [effectView setIdentifier:kElectrobunVibrancyViewIdentifier]; [effectView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; } if (@available(macOS 10.14, *)) { [effectView setMaterial:NSVisualEffectMaterialUnderWindowBackground]; } else { [effectView setMaterial:NSVisualEffectMaterialSidebar]; } [effectView setBlendingMode:NSVisualEffectBlendingModeBehindWindow]; [effectView setState:NSVisualEffectStateActive]; if ([effectView superview] == nil) { NSView *relativeView = [[contentView subviews] firstObject]; if (relativeView != nil) { [contentView addSubview:effectView positioned:NSWindowBelow relativeTo:relativeView]; } else { [contentView addSubview:effectView]; } } ensureWindowBorder(window); applyWindowCornerRadius(window, gWindowCornerRadius); [window invalidateShadow]; success = YES; }); return success; } extern "C" bool setWindowCornerRadius(void *windowPtr, double cornerRadius) { if (windowPtr == nullptr) { return false; } __block BOOL success = NO; dispatch_sync(dispatch_get_main_queue(), ^{ NSWindow *window = (__bridge NSWindow *)windowPtr; if (![window isKindOfClass:[NSWindow class]]) { return; } applyWindowCornerRadius(window, (CGFloat)cornerRadius); ensureWindowBorder(window); [window invalidateShadow]; success = YES; }); return success; } extern "C" bool ensureWindowShadow(void *windowPtr) { if (windowPtr == nullptr) { return false; } __block BOOL success = NO; dispatch_sync(dispatch_get_main_queue(), ^{ NSWindow *window = (__bridge NSWindow *)windowPtr; if (![window isKindOfClass:[NSWindow class]]) { return; } [window setHasShadow:YES]; [window invalidateShadow]; ensureWindowBorder(window); applyWindowCornerRadius(window, gWindowCornerRadius); success = YES; }); return success; } extern "C" bool setWindowTrafficLightsPosition(void *windowPtr, double x, double yFromTop) { if (windowPtr == nullptr) { return false; } __block BOOL success = NO; dispatch_sync(dispatch_get_main_queue(), ^{ NSWindow *window = (__bridge NSWindow *)windowPtr; if (![window isKindOfClass:[NSWindow class]]) { return; } NSButton *closeButton = [window standardWindowButton:NSWindowCloseButton]; NSButton *minimizeButton = [window standardWindowButton:NSWindowMiniaturizeButton]; NSButton *zoomButton = [window standardWindowButton:NSWindowZoomButton]; if (closeButton == nil || minimizeButton == nil || zoomButton == nil) { return; } NSView *buttonContainer = [closeButton superview]; if (buttonContainer == nil) { return; } CGFloat spacing = NSMinX(minimizeButton.frame) - NSMinX(closeButton.frame); if (spacing <= 0) { spacing = closeButton.frame.size.width + 6.0; } BOOL flipped = [buttonContainer isFlipped]; CGFloat targetY = yFromTop; if (!flipped) { targetY = buttonContainer.frame.size.height - yFromTop - closeButton.frame.size.height; } targetY = MAX(0.0, targetY); CGFloat currentX = x; NSArray *buttons = @[ closeButton, minimizeButton, zoomButton ]; for (NSButton *button in buttons) { [button setFrameOrigin:NSMakePoint(currentX, targetY)]; currentX += spacing; } [buttonContainer setNeedsLayout:YES]; [buttonContainer layoutSubtreeIfNeeded]; [window invalidateShadow]; success = YES; }); return success; } extern "C" bool setNativeWindowDragRegion(void *windowPtr, double x, double height) { if (windowPtr == nullptr) { return false; } __block BOOL success = NO; dispatch_sync(dispatch_get_main_queue(), ^{ NSWindow *window = (__bridge NSWindow *)windowPtr; if (![window isKindOfClass:[NSWindow class]]) { return; } NSView *contentView = [window contentView]; if (contentView == nil) { return; } CGFloat dragX = MAX(0.0, x); CGFloat dragHeight = MAX(0.0, height); CGFloat dragWidth = MAX(0.0, contentView.bounds.size.width - dragX); if (dragHeight <= 0.0 || dragWidth <= 0.0) { return; } BOOL flipped = [contentView isFlipped]; CGFloat dragY = flipped ? 0.0 : contentView.bounds.size.height - dragHeight; dragY = MAX(0.0, dragY); ElectrobunNativeDragView *dragView = findNativeDragView(contentView); if (dragView == nil) { dragView = [[ElectrobunNativeDragView alloc] initWithFrame:NSZeroRect]; [dragView setIdentifier:kElectrobunNativeDragViewIdentifier]; } [dragView setFrame:NSMakeRect(dragX, dragY, dragWidth, dragHeight)]; [dragView setAutoresizingMask:NSViewWidthSizable]; if ([dragView superview] == nil) { [contentView addSubview:dragView positioned:NSWindowAbove relativeTo:nil]; } success = YES; }); return success; }