From bb5daa2e90623691a5259700b9d388d2d21f46d3 Mon Sep 17 00:00:00 2001 From: Ruben Beltran del Rio Date: Thu, 4 Sep 2025 11:11:37 +0200 Subject: Rename to Liquid Glass Bezel, add preferences --- Liquid Glass Bezel/BBLiquidGlassBezelInterface.h | 9 + Liquid Glass Bezel/BBLiquidGlassBezelInterface.m | 103 ++++++++++ Liquid Glass Bezel/BBLiquidGlassBezelInterface.xib | 102 ++++++++++ Liquid Glass Bezel/BBLiquidGlassBezelPrefs 2.h | 6 + Liquid Glass Bezel/BBLiquidGlassBezelPrefs 2.m | 10 + Liquid Glass Bezel/BBLiquidGlassBezelPrefs.h | 10 + Liquid Glass Bezel/BBLiquidGlassBezelPrefs.m | 13 ++ Liquid Glass Bezel/BBLiquidGlassBezelPrefs.xib | 56 ++++++ Liquid Glass Bezel/BBObjectCell.h | 4 + Liquid Glass Bezel/BBObjectCell.m | 211 +++++++++++++++++++++ Liquid Glass Bezel/BackgroundView.swift | 88 +++++++++ .../LiquidGlassBezel-Bridging-Header.h | 2 + ...iquidGlassBezelCollectingSearchObjectView.swift | 38 ++++ .../LiquidGlassBezelObjectCell.swift | 164 ++++++++++++++++ .../LiquidGlassBezelSearchObjectView.swift | 6 + Liquid Glass Bezel/en.lproj/InfoPlist.strings | 2 + 16 files changed, 824 insertions(+) create mode 100644 Liquid Glass Bezel/BBLiquidGlassBezelInterface.h create mode 100644 Liquid Glass Bezel/BBLiquidGlassBezelInterface.m create mode 100644 Liquid Glass Bezel/BBLiquidGlassBezelInterface.xib create mode 100644 Liquid Glass Bezel/BBLiquidGlassBezelPrefs 2.h create mode 100644 Liquid Glass Bezel/BBLiquidGlassBezelPrefs 2.m create mode 100644 Liquid Glass Bezel/BBLiquidGlassBezelPrefs.h create mode 100644 Liquid Glass Bezel/BBLiquidGlassBezelPrefs.m create mode 100644 Liquid Glass Bezel/BBLiquidGlassBezelPrefs.xib create mode 100644 Liquid Glass Bezel/BBObjectCell.h create mode 100644 Liquid Glass Bezel/BBObjectCell.m create mode 100644 Liquid Glass Bezel/BackgroundView.swift create mode 100644 Liquid Glass Bezel/LiquidGlassBezel-Bridging-Header.h create mode 100644 Liquid Glass Bezel/LiquidGlassBezelCollectingSearchObjectView.swift create mode 100644 Liquid Glass Bezel/LiquidGlassBezelObjectCell.swift create mode 100644 Liquid Glass Bezel/LiquidGlassBezelSearchObjectView.swift create mode 100644 Liquid Glass Bezel/en.lproj/InfoPlist.strings (limited to 'Liquid Glass Bezel') diff --git a/Liquid Glass Bezel/BBLiquidGlassBezelInterface.h b/Liquid Glass Bezel/BBLiquidGlassBezelInterface.h new file mode 100644 index 0000000..d4671fd --- /dev/null +++ b/Liquid Glass Bezel/BBLiquidGlassBezelInterface.h @@ -0,0 +1,9 @@ +@interface BBLiquidGlassBezelInterface : QSResizingInterfaceController { + NSRect standardRect; +} + +@end + +@interface NSWindow (QSBCInterfaceController) +- (NSTimeInterval)animationResizeTime:(NSRect)newWindowFrame; +@end diff --git a/Liquid Glass Bezel/BBLiquidGlassBezelInterface.m b/Liquid Glass Bezel/BBLiquidGlassBezelInterface.m new file mode 100644 index 0000000..335eacd --- /dev/null +++ b/Liquid Glass Bezel/BBLiquidGlassBezelInterface.m @@ -0,0 +1,103 @@ +#import "BBLiquidGlassBezelInterface.h" + +@interface BBLiquidGlassBezelInterface () { + CGRect initialRect; +} +@end + +@implementation BBLiquidGlassBezelInterface + +- (id)init { + return [self initWithWindowNibName:@"BBLiquidGlassBezelInterface"]; +} + +- (void)windowDidLoad { + initialRect = centerRectInRect([[self window] frame], [[NSScreen mainScreen] frame]); + + [super windowDidLoad]; + + QSWindow *window = (QSWindow *)[self window]; + [window setLevel:NSPopUpMenuWindowLevel]; + [window setBackgroundColor:[NSColor clearColor]]; + [window setOpaque:NO]; + + [window setHideOffset:NSMakePoint(0, 0)]; + [window setShowOffset:NSMakePoint(0, 0)]; + + [window setMovableByWindowBackground:NO]; + [window setFastShow:YES]; + [window setHasShadow:YES]; + + [window setFrame:standardRect display:YES]; + + NSArray *theControls = [NSArray arrayWithObjects:dSelector, aSelector, iSelector, nil]; + for(QSSearchObjectView *theControl in theControls) { + QSObjectCell *theCell = [theControl cell]; + + [theControl setPreferredEdge:NSMinYEdge]; + [(QSWindow *)[(theControl)->resultController window] setHideOffset:NSMakePoint(0, NSMinY([iSelector frame]))]; + [(QSWindow *)[(theControl)->resultController window] setShowOffset:NSMakePoint(0, NSMinY([dSelector frame]))]; + + [theCell setBackgroundColor:[NSColor clearColor]]; + [theCell setFont:[NSFont systemFontOfSize:11 weight:NSFontWeightLight]]; + [theCell setCellRadiusFactor:32]; + + [theCell setHighlightColor:[NSColor colorWithRed:1 green:1 blue:1 alpha:.15]]; + [theCell setTextColor:[NSColor textColor]]; + [theCell setShowDetails:NO]; + [theCell setState:NSControlStateValueOn]; + [theCell setIconSize:QSSize128]; + [theCell setImagePosition:NSImageAbove]; + } + + [self contractWindow:nil]; +} + +- (NSSize) maxIconSize { + return QSSize32; +} + +- (void)showMainWindow:(id)sender { + [[self window] setFrame:[self rectForState:[self expanded]] display:YES]; + if ([[self window] isVisible]) [[self window] pulse:self]; + [super showMainWindow:sender]; + // Does this need to be here? + [[[self window] contentView] setNeedsDisplay:YES]; +} + +- (void)expandWindow:(id)sender { + if (![self expanded]) + [[self window] setFrame:[self rectForState:YES] display:YES animate:YES]; + [super expandWindow:sender]; +} + +- (void)contractWindow:(id)sender { + if ([self expanded]) + [[self window] setFrame:[self rectForState:NO] display:YES animate:YES]; + [super contractWindow:sender]; +} + +- (NSRect)rectForState:(BOOL)shouldExpand { + NSRect newRect = initialRect; + NSRect screenRect = [[NSScreen mainScreen] frame]; + + if (!shouldExpand) { + newRect.size.width -= 192; + } + + return NSOffsetRect(centerRectInRect(newRect, screenRect), 0, (NSHeight(screenRect) / 5)); +} + +- (NSRect)window:(NSWindow *)window willPositionSheet:(NSWindow *)sheet usingRect:(NSRect)rect { + return NSOffsetRect(NSInsetRect(rect, 8, 0), 0, 0); +} + +- (NSTimeInterval)animationResizeTime:(NSRect)newWindowFrame { + return 0.01; +} + +- (IBAction)customize:(id)sender{ + [[NSClassFromString(@"QSPreferencesController") sharedInstance] showPaneWithIdentifier:@"BBLiquidGlassBezelPrefs"]; +} + +@end diff --git a/Liquid Glass Bezel/BBLiquidGlassBezelInterface.xib b/Liquid Glass Bezel/BBLiquidGlassBezelInterface.xib new file mode 100644 index 0000000..db6f979 --- /dev/null +++ b/Liquid Glass Bezel/BBLiquidGlassBezelInterface.xib @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Liquid Glass Bezel/BBLiquidGlassBezelPrefs 2.h b/Liquid Glass Bezel/BBLiquidGlassBezelPrefs 2.h new file mode 100644 index 0000000..36c5177 --- /dev/null +++ b/Liquid Glass Bezel/BBLiquidGlassBezelPrefs 2.h @@ -0,0 +1,6 @@ +#import +NS_ASSUME_NONNULL_BEGIN +@interface BBLiquidGlassBezelPrefs : QSPreferencePane { +} +@end +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/Liquid Glass Bezel/BBLiquidGlassBezelPrefs 2.m b/Liquid Glass Bezel/BBLiquidGlassBezelPrefs 2.m new file mode 100644 index 0000000..4e57c7a --- /dev/null +++ b/Liquid Glass Bezel/BBLiquidGlassBezelPrefs 2.m @@ -0,0 +1,10 @@ +// +// BBLiquidGlassBezelPrefs.h +// Liquid Glass Bezel +// +// Created by Ruben Beltran del Rio on 2025-09-04. +// Copyright © 2025 BRNBW. All rights reserved. +// +#import "BBLiquidGlassBezelPrefs.h" +@implementation BBLiquidGlassBezelPrefs +@end \ No newline at end of file diff --git a/Liquid Glass Bezel/BBLiquidGlassBezelPrefs.h b/Liquid Glass Bezel/BBLiquidGlassBezelPrefs.h new file mode 100644 index 0000000..2ce6bb6 --- /dev/null +++ b/Liquid Glass Bezel/BBLiquidGlassBezelPrefs.h @@ -0,0 +1,10 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface BBLiquidGlassBezelPrefs : QSPreferencePane { + +} +@end + +NS_ASSUME_NONNULL_END diff --git a/Liquid Glass Bezel/BBLiquidGlassBezelPrefs.m b/Liquid Glass Bezel/BBLiquidGlassBezelPrefs.m new file mode 100644 index 0000000..1deb432 --- /dev/null +++ b/Liquid Glass Bezel/BBLiquidGlassBezelPrefs.m @@ -0,0 +1,13 @@ +// +// BBLiquidGlassBezelPrefs.h +// Liquid Glass Bezel +// +// Created by Ruben Beltran del Rio on 2025-09-04. +// Copyright © 2025 BRNBW. All rights reserved. +// + +#import "BBLiquidGlassBezelPrefs.h" + +@implementation BBLiquidGlassBezelPrefs + +@end diff --git a/Liquid Glass Bezel/BBLiquidGlassBezelPrefs.xib b/Liquid Glass Bezel/BBLiquidGlassBezelPrefs.xib new file mode 100644 index 0000000..41e51fd --- /dev/null +++ b/Liquid Glass Bezel/BBLiquidGlassBezelPrefs.xib @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Liquid Glass Bezel/BBObjectCell.h b/Liquid Glass Bezel/BBObjectCell.h new file mode 100644 index 0000000..3fa67c6 --- /dev/null +++ b/Liquid Glass Bezel/BBObjectCell.h @@ -0,0 +1,4 @@ +#import + +@interface BBObjectCell : QSObjectCell +@end diff --git a/Liquid Glass Bezel/BBObjectCell.m b/Liquid Glass Bezel/BBObjectCell.m new file mode 100644 index 0000000..7bf1b3c --- /dev/null +++ b/Liquid Glass Bezel/BBObjectCell.m @@ -0,0 +1,211 @@ +#import "BBObjectCell.h" + +@implementation BBObjectCell + +- (NSCellImagePosition)preferredImagePosition { + return NSImageAbove; +} + +- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { + BOOL isFirstResponder = [[controlView window] firstResponder] == controlView && ![controlView isKindOfClass:[NSTableView class]]; + BOOL dropTarget = ([self isHighlighted] && ([self highlightsBy] & NSChangeBackgroundCellMask) && ![self isBezeled]); + + NSColor *fillColor; + NSColor *strokeColor = [NSColor clearColor]; + + if (isFirstResponder) { + fillColor = [NSColor selectedTextBackgroundColor]; + } else { + fillColor = [NSColor clearColor]; + } + + if (dropTarget) { + fillColor = [NSColor controlAccentColor]; + } + + [fillColor setFill]; + [strokeColor setStroke]; + + NSBezierPath *roundRect = [NSBezierPath bezierPath]; + [roundRect appendBezierPathWithRoundedRectangle:cellFrame withRadius:NSHeight(cellFrame)/cellRadiusFactor]; + [roundRect fill]; + + [self drawInteriorWithFrame:[self drawingRectForBounds:cellFrame] inView:controlView]; +} + +- (NSRect)titleRectForBounds:(NSRect)theRect +{ + NSRect rect = theRect; + rect = NSOffsetRect(rect, 0, -4); + return [super titleRectForBounds: rect]; +} + +- (void)drawTextForObject:(QSObject *)drawObject withFrame:(NSRect)cellFrame inView:(NSView *)controlView { + if ([self imagePosition] == NSImageOnly) return; + + NSString *abbrString = nil; + if ([controlView respondsToSelector:@selector(matchedString)]) + abbrString = [(QSSearchObjectView *)controlView matchedString]; + + NSString *nameString = nil; + NSIndexSet *hitMask = nil; + + id ranker = [drawObject ranker]; + if (ranker && abbrString) + nameString = [ranker matchedStringForAbbreviation:abbrString hitmask:&hitMask inContext:nil]; + + if (!nameString) + nameString = [drawObject displayName]; + + BOOL rankedStringIsName = [nameString isEqualToString:[drawObject displayName]] || nameString == nil; + if (!nameString) { + // fall back to the identifier if no reasonable name can be found + nameString = [drawObject identifier]; + } + if (!nameString) { + // Couldn't find anything sensible to use for the name, fallback to avoid a crash + nameString = @"Unknown"; + } + + BOOL useAlternateColor = [controlView isKindOfClass:[NSTableView class]] && [(NSTableView *)controlView isRowSelected:[(NSTableView *)controlView rowAtPoint:cellFrame.origin]]; + NSColor *mainColor = (textColor ? textColor : (useAlternateColor ? [NSColor alternateSelectedControlTextColor] : [NSColor controlTextColor])); + NSColor *fadedColor = [mainColor colorWithAlphaComponent:0.50]; + NSRect textDrawRect = [self titleRectForBounds:cellFrame]; + + NSMutableAttributedString *titleString = [[[NSMutableAttributedString alloc] initWithString:nameString] autorelease]; + [titleString setAttributes:rankedStringIsName ? nameAttributes : detailsAttributes range:NSMakeRange(0, [titleString length])]; + + // Bring out the matched letters + if (abbrString && ![abbrString hasPrefix:@"QSActionMnemonic"]) { + [titleString addAttribute:NSForegroundColorAttributeName value:rankedStringIsName ? fadedColor : [fadedColor colorWithAlphaComponent:0.8] range:NSMakeRange(0, [titleString length])]; + + NSUInteger i = 0; + NSUInteger j = 0; + NSUInteger hits[[titleString length]]; + NSUInteger count = [hitMask getIndexes:(NSUInteger *)&hits maxCount:[titleString length] inIndexRange:nil]; + NSDictionary *attributes = @{ + NSForegroundColorAttributeName: rankedStringIsName ? mainColor : fadedColor + }; + for(i = 0; i NSControl.ImagePosition { + return .imageAbove + } + + override func draw(withFrame cellFrame: NSRect, in controlView: NSView?) { + if let controlView { + + let isFirstResponder = controlView.window?.firstResponder == controlView && !(controlView is NSTableView) + let dropTarget = isHighlighted && ((highlightsBy.rawValue & NSCell.StyleMask.changeBackgroundCellMask.rawValue) != 0) && !isBezeled + + var fillColor: NSColor? + let strokeColor = NSColor.clear + + if isFirstResponder { + fillColor = self.highlightColor() + } else { + fillColor = self.backgroundColor + } + + if dropTarget { + fillColor = NSColor(red: 0.77, green: 0.91, blue: 0.96, alpha: 1) + } + + fillColor?.setFill() + strokeColor.setStroke() + + let roundRect = NSBezierPath() + roundRect.append(withRoundedRectangle: cellFrame, withRadius: cellRadiusFactor()) + roundRect.fill() + + drawInterior(withFrame: drawingRect(forBounds: cellFrame), in: controlView) + } + } + + override func titleRect(forBounds _rect: NSRect) -> NSRect { + var rect = _rect + rect = NSOffsetRect(rect, 0, -4) + return super.titleRect(forBounds: rect) + } + + override func drawText(for drawObject: QSObject!, withFrame cellFrame: NSRect, in controlView: NSView!) { + if imagePosition == .imageOnly { + return + } + + var abbrString: String? = nil + + if controlView.responds(to: #selector(QSSearchObjectView.matchedString)) { + abbrString = (controlView as! QSSearchObjectView).matchedString() + } + + var nameString: String? = nil + var hitMask: NSIndexSet? = nil + + let ranker = drawObject.ranker() + if ranker != nil && abbrString != nil { + nameString = ranker?.matchedString(forAbbreviation: abbrString, hitmask: &hitMask, inContext: nil) + } + + if (nameString == nil) { + nameString = drawObject.displayName() + } + + let rankedStringIsName = nameString == drawObject.displayName() || nameString == nil + + if nameString == nil { + nameString = drawObject.identifier() ?? "Unknown" + } + + let useAlternateColor = controlView is NSTableView && (controlView as! NSTableView).isRowSelected((controlView as! NSTableView).row(at: cellFrame.origin)) + let mainColor = textColor() ?? (useAlternateColor ? .alternateSelectedControlTextColor : .controlTextColor) + let fadedColor = mainColor.withAlphaComponent(0.50) + let textDrawRect = titleRect(forBounds: cellFrame) + + + let titleString = NSMutableAttributedString(string: nameString!) + let nameAttributes = (value(forKey: "nameAttributes") as? [NSAttributedString.Key : Any]) + let detailsAttributes = (value(forKey: "detailsAttributes") as? [NSAttributedString.Key : Any]) + titleString.setAttributes(rankedStringIsName ? nameAttributes : detailsAttributes, range: NSMakeRange(0, titleString.length)) + + if abbrString != nil && abbrString!.hasPrefix("QSActionMnemonic") { + titleString.addAttribute(.foregroundColor, value: rankedStringIsName ? fadedColor : fadedColor.withAlphaComponent(0.8), range: NSMakeRange(0, titleString.length)) + + var hits: Int = 0 + let count = hitMask?.getIndexes(&hits, maxCount: titleString.length, inIndexRange: nil) + for i in 0..<(count ?? 0) { + for j in 1.. { + } + } + } +} + + NSUInteger i = 0; + NSUInteger j = 0; + NSUInteger hits[[titleString length]]; + NSUInteger count = [hitMask getIndexes:(NSUInteger *)&hits maxCount:[titleString length] inIndexRange:nil]; + NSDictionary *attributes = @{ + NSForegroundColorAttributeName: rankedStringIsName ? mainColor : fadedColor + }; + for(i = 0; i