aboutsummaryrefslogtreecommitdiff
path: root/Liquid Glass Bezel
diff options
context:
space:
mode:
authorRuben Beltran del Rio <git@r.bdr.sh>2025-09-04 11:11:37 +0200
committerRuben Beltran del Rio <git@r.bdr.sh>2025-09-04 11:11:37 +0200
commitbb5daa2e90623691a5259700b9d388d2d21f46d3 (patch)
treef5629e81bedc6cdd35ee9a79628997c5e14ffa7c /Liquid Glass Bezel
parent3dfeb86c5c3b72acc59919c7a198fcf76a54c35c (diff)
Rename to Liquid Glass Bezel, add preferences
Diffstat (limited to 'Liquid Glass Bezel')
-rw-r--r--Liquid Glass Bezel/BBLiquidGlassBezelInterface.h9
-rw-r--r--Liquid Glass Bezel/BBLiquidGlassBezelInterface.m103
-rw-r--r--Liquid Glass Bezel/BBLiquidGlassBezelInterface.xib102
-rw-r--r--Liquid Glass Bezel/BBLiquidGlassBezelPrefs 2.h6
-rw-r--r--Liquid Glass Bezel/BBLiquidGlassBezelPrefs 2.m10
-rw-r--r--Liquid Glass Bezel/BBLiquidGlassBezelPrefs.h10
-rw-r--r--Liquid Glass Bezel/BBLiquidGlassBezelPrefs.m13
-rw-r--r--Liquid Glass Bezel/BBLiquidGlassBezelPrefs.xib56
-rw-r--r--Liquid Glass Bezel/BBObjectCell.h4
-rw-r--r--Liquid Glass Bezel/BBObjectCell.m211
-rw-r--r--Liquid Glass Bezel/BackgroundView.swift88
-rw-r--r--Liquid Glass Bezel/LiquidGlassBezel-Bridging-Header.h2
-rw-r--r--Liquid Glass Bezel/LiquidGlassBezelCollectingSearchObjectView.swift38
-rw-r--r--Liquid Glass Bezel/LiquidGlassBezelObjectCell.swift164
-rw-r--r--Liquid Glass Bezel/LiquidGlassBezelSearchObjectView.swift6
-rw-r--r--Liquid Glass Bezel/en.lproj/InfoPlist.strings2
16 files changed, 824 insertions, 0 deletions
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="24123.1" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24123.1"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="BBLiquidGlassBezelInterface">
+ <connections>
+ <outlet property="aSelector" destination="69" id="79"/>
+ <outlet property="dSelector" destination="68" id="75"/>
+ <outlet property="iSelector" destination="70" id="80"/>
+ <outlet property="menuButton" destination="209" id="210"/>
+ <outlet property="progressIndicator" destination="192" id="211"/>
+ <outlet property="window" destination="9" id="78"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <window title="Bezel Interface" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="9" userLabel="mainWindow" customClass="QSBorderlessWindow">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" nonactivatingPanel="YES"/>
+ <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+ <rect key="contentRect" x="530" y="653" width="608" height="200"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1512" height="948"/>
+ <value key="minSize" type="size" width="440" height="200"/>
+ <view key="contentView" id="6" customClass="BackgroundView" customModule="Liquid_Glass_Bezel" customModuleProvider="target">
+ <rect key="frame" x="0.0" y="0.0" width="608" height="200"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <customView translatesAutoresizingMaskIntoConstraints="NO" id="68" customClass="LiquidGlassBezelCollectingSearchObjectView" customModule="Liquid_Glass_Bezel" customModuleProvider="target">
+ <rect key="frame" x="8" y="8" width="184" height="184"/>
+ <constraints>
+ <constraint firstAttribute="height" constant="184" id="SwQ-fC-3aw"/>
+ <constraint firstAttribute="width" constant="184" id="Zol-hr-3Bk"/>
+ </constraints>
+ <connections>
+ <outlet property="nextKeyView" destination="69" id="105"/>
+ </connections>
+ </customView>
+ <customView translatesAutoresizingMaskIntoConstraints="NO" id="69" customClass="LiquidGlassBezelSearchObjectView" customModule="Liquid_Glass_Bezel" customModuleProvider="target">
+ <rect key="frame" x="200" y="8" width="184" height="184"/>
+ <constraints>
+ <constraint firstAttribute="width" constant="184" id="O3w-M3-b05"/>
+ <constraint firstAttribute="height" constant="184" id="XPL-72-l7r"/>
+ </constraints>
+ <connections>
+ <outlet property="nextKeyView" destination="70" id="106"/>
+ </connections>
+ </customView>
+ <customView translatesAutoresizingMaskIntoConstraints="NO" id="70" customClass="LiquidGlassBezelCollectingSearchObjectView" customModule="Liquid_Glass_Bezel" customModuleProvider="target">
+ <rect key="frame" x="392" y="8" width="184" height="184"/>
+ <constraints>
+ <constraint firstAttribute="width" constant="184" id="Jbn-VW-sSJ"/>
+ <constraint firstAttribute="height" constant="184" id="X3X-S6-KrY"/>
+ </constraints>
+ <connections>
+ <outlet property="nextKeyView" destination="68" id="104"/>
+ </connections>
+ </customView>
+ <progressIndicator horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="192">
+ <rect key="frame" x="584" y="8" width="16" height="16"/>
+ <constraints>
+ <constraint firstAttribute="width" constant="16" id="Py4-hV-7HG"/>
+ </constraints>
+ </progressIndicator>
+ <button translatesAutoresizingMaskIntoConstraints="NO" id="209" customClass="QSMenuButton">
+ <rect key="frame" x="584" y="173.5" width="16" height="21"/>
+ <buttonCell key="cell" type="bevel" bezelStyle="rounded" image="arrowtriangle.down.square.fill" catalog="system" imagePosition="only" alignment="center" inset="2" id="216">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ <color key="contentTintColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
+ <constraints>
+ <constraint firstAttribute="height" constant="16" id="cvg-xh-bvD"/>
+ <constraint firstAttribute="width" constant="16" id="w12-Bf-x2f"/>
+ </constraints>
+ </button>
+ </subviews>
+ <constraints>
+ <constraint firstItem="209" firstAttribute="top" secondItem="6" secondAttribute="top" constant="8" id="1uj-pU-uJB"/>
+ <constraint firstItem="70" firstAttribute="leading" secondItem="69" secondAttribute="trailing" constant="8" id="3iK-bw-ky1"/>
+ <constraint firstAttribute="bottom" secondItem="192" secondAttribute="bottom" constant="8" id="ASY-5E-0bJ"/>
+ <constraint firstAttribute="trailing" secondItem="192" secondAttribute="trailing" constant="8" id="S36-iH-Apn"/>
+ <constraint firstItem="69" firstAttribute="leading" secondItem="68" secondAttribute="trailing" constant="8" id="VOZ-kp-hVQ"/>
+ <constraint firstAttribute="trailing" secondItem="209" secondAttribute="trailing" constant="8" id="ZBS-80-x88"/>
+ <constraint firstItem="69" firstAttribute="top" secondItem="6" secondAttribute="top" constant="8" id="Zec-BE-M9X"/>
+ <constraint firstItem="70" firstAttribute="top" secondItem="6" secondAttribute="top" constant="8" id="jQN-Vd-Qm0"/>
+ <constraint firstItem="68" firstAttribute="leading" secondItem="6" secondAttribute="leading" constant="8" id="m72-ls-RGc"/>
+ <constraint firstItem="68" firstAttribute="top" secondItem="6" secondAttribute="top" constant="8" id="msf-m2-n1U"/>
+ </constraints>
+ </view>
+ <connections>
+ <outlet property="delegate" destination="-2" id="81"/>
+ <outlet property="initialFirstResponder" destination="68" id="111"/>
+ </connections>
+ <point key="canvasLocation" x="76" y="-42"/>
+ </window>
+ </objects>
+ <resources>
+ <image name="arrowtriangle.down.square.fill" catalog="system" width="15" height="14"/>
+ </resources>
+</document>
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 <QSInterface/QSInterface.h>
+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 <QSInterface/QSInterface.h>
+
+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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="24123.1" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24123.1"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="BBLiquidGlassBezelPrefs">
+ <connections>
+ <outlet property="_window" destination="QvC-M9-y7g" id="cN0-9e-T8O"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" deferred="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
+ <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+ <rect key="contentRect" x="196" y="240" width="389" height="273"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1512" height="948"/>
+ <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
+ <rect key="frame" x="0.0" y="0.0" width="389" height="273"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <subviews>
+ <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="m0T-dG-aXE">
+ <rect key="frame" x="12" y="237" width="125" height="16"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <textFieldCell key="cell" lineBreakMode="clipping" title="Choose Glass Style:" id="bXE-zS-DgD">
+ <font key="font" usesAppearanceFont="YES"/>
+ <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5P5-iQ-pZN">
+ <rect key="frame" x="144" y="231" width="120" height="25"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <popUpButtonCell key="cell" type="push" title="Regular" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="bjs-2K-21R" id="aqc-Gf-EiA">
+ <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="message"/>
+ <menu key="menu" id="6kp-fp-sI3">
+ <items>
+ <menuItem title="Regular" state="on" id="bjs-2K-21R"/>
+ <menuItem title="Clear" tag="1" id="iVE-75-QeO"/>
+ </items>
+ </menu>
+ </popUpButtonCell>
+ <connections>
+ <binding destination="od0-QH-X0x" name="selectedTag" keyPath="values.QSLiquidGlassBezelGlassStyle" id="8w3-oM-wU3"/>
+ </connections>
+ </popUpButton>
+ </subviews>
+ </view>
+ <point key="canvasLocation" x="94.5" y="148.5"/>
+ </window>
+ <userDefaultsController representsSharedInstance="YES" id="od0-QH-X0x"/>
+ </objects>
+</document>
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 <QSInterface/QSInterface.h>
+
+@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<count; i += j) {
+ for (j = 1; i+j<count && hits[i+j-1] +1 == hits[i+j]; j++);
+ [titleString addAttributes:attributes range:NSMakeRange(hits[i], j)];
+ }
+ } else {
+ [titleString addAttribute:NSBaselineOffsetAttributeName value:[NSNumber numberWithDouble:-1.0] range:NSMakeRange(0, [titleString length])];
+ }
+//
+// // Ranked string and nameString aren't the same. Show 'nameString ⟷ rankedString' in the UI
+// if (!rankedStringIsName && [drawObject displayName].length) {
+// [titleString addAttribute:NSFontAttributeName value:detailsFont range:NSMakeRange(0,[titleString length])];
+// NSMutableAttributedString *attributedNameString = [[NSMutableAttributedString alloc] initWithString:[drawObject displayName]];
+// [attributedNameString setAttributes:nameAttributes range:NSMakeRange(0, [[drawObject displayName] length])];
+//
+// [attributedNameString appendAttributedString:[[[NSAttributedString alloc] initWithString:@" ⟷ " attributes:rankedNameAttributes] autorelease]];
+// // the replaceCharacters... method inserts the new string into the receiver at the start of the work (range.location and range.length are 0)
+// [titleString replaceCharactersInRange:NSMakeRange(0,0) withAttributedString:attributedNameString];
+// [attributedNameString release];
+// }
+
+ if (showDetails) {
+ NSString *detailsString = [drawObject details];
+
+ NSRange returnRange = [detailsString rangeOfString:@"\n"];
+ if (returnRange.location != NSNotFound) {
+ detailsString = [detailsString substringToIndex:returnRange.location];
+ }
+
+ detailsAttributes = [detailsAttributes mutableCopy];
+ [detailsAttributes setValue:[NSColor grayColor] forKey:NSForegroundColorAttributeName];
+
+ if (detailsString && detailsString.length && ![detailsString isEqualToString:nameString]) {
+ [titleString appendAttributedString:[[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"\n%@",detailsString] attributes:detailsAttributes] autorelease]];
+ }
+ }
+
+ NSRect centerRect = rectFromSize([titleString size]);
+ centerRect.size.width = NSWidth(textDrawRect);
+ centerRect.size.height = MIN(NSHeight(textDrawRect), centerRect.size.height);
+ [titleString drawInRect:centerRectInRect(centerRect, textDrawRect)];
+}
+
+- (void)drawSearchPlaceholderWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
+ NSString *defaultText = NSLocalizedStringWithDefaultValue(@"Type to search", nil, [NSBundle mainBundle], @"Type to search", @"Hint that appears in the first pane of the QS interface when it's empty.");
+ NSSize textSize = [defaultText sizeWithAttributes:nameAttributes];
+ NSRect textRect = centerRectInRect(rectFromSize(textSize), cellFrame);
+ BOOL isFirstResponder = [[controlView window] firstResponder] == controlView && ![controlView isKindOfClass:[NSTableView class]];
+
+ if (isFirstResponder && [controlView isKindOfClass:[QSSearchObjectView class]]) {
+ NSImage *find = [NSImage imageWithSystemSymbolName:@"magnifyingglass.circle.fill" accessibilityDescription:nil];
+
+
+ [find setSize:QSSize16];
+ NSRect findImageRect = expelRectFromRectOnEdge(centerRectInRect(rectFromSize([find size]), cellFrame), textRect, NSRectEdgeMinX, -2);
+
+
+ NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
+ [graphicsContext saveGraphicsState];
+ CGContextRef context = [graphicsContext CGContext];
+ CGContextBeginTransparencyLayerWithRect(context, findImageRect, nil);
+ CGContextSetBlendMode(context, kCGBlendModeNormal);
+ [find drawInRect:findImageRect fromRect:rectFromSize([find size]) operation:NSCompositingOperationSourceOver fraction:1];
+ CGContextSetBlendMode(context, kCGBlendModeSourceIn);
+ CGContextSetFillColorWithColor(context, [[NSColor textColor] CGColor]);
+ CGContextFillRect(context, findImageRect);
+ CGContextEndTransparencyLayer(context);
+
+ [defaultText drawInRect:textRect withAttributes:nameAttributes];
+ }
+}
+
+- (void)drawIconForObject:(QSObject *)object withFrame:(NSRect)cellFrame inView:(NSView *)controlView {
+ NSImage *image = [object icon];
+ NSString *iconName = nil;
+ NSImageSymbolConfiguration *iconConfig = nil;
+ if ([[image name] isEqualToString: @"defaultAction"]) {
+ iconName = @"gearshape.circle";
+ }
+ else if ([[image name] isEqualToString: @"Find"]) {
+ iconName = @"magnifyingglass";
+ }
+ else if ([[image name] isEqualToString: @"Object"]) {
+ iconName = @"cube.transparent";
+ iconConfig = [NSImageSymbolConfiguration configurationWithHierarchicalColor:[NSColor controlAccentColor]];
+ }
+ else if ([[image name] isEqualToString: @"ContactAddress"]) {
+ iconName = @"mappin.circle";
+ }
+ else if ([[image name] isEqualToString: @"ContactEmail"]) {
+ iconName = @"envelope.circle";
+ }
+ else if ([[image name] isEqualToString: @"ContactPhone"]) {
+ iconName = @"phone.circle";
+ }
+ else if ([[image name] isEqualToString: @"DefaultBookmarkIcon"]) {
+ iconName = @"link.circle";
+ }
+ else if ([[image name] isEqualToString: @"Catalog"]) {
+ iconName = @"sparkles.rectangle.stack";
+ }
+ else if ([[image name] isEqualToString: @"Triggers"]) {
+ iconName = @"bolt.fill";
+ }
+
+
+ if (iconName != nil) {
+ NSImage *newIcon = [NSImage imageWithSystemSymbolName:iconName accessibilityDescription:[image name]];
+
+ if (iconConfig == nil) {
+ NSArray *colors = [NSArray arrayWithObjects:[NSColor textColor], [NSColor controlAccentColor], nil];
+ iconConfig = [NSImageSymbolConfiguration configurationWithPaletteColors:colors];
+ }
+
+ QSObject *newObject = [QSObject objectWithName:[object name]];
+ [newObject setIcon:[newIcon imageWithSymbolConfiguration:iconConfig]];
+ [newObject setIconLoaded:[object iconLoaded]];
+ return [super drawIconForObject: newObject withFrame:cellFrame inView:controlView];
+ }
+
+ [super drawIconForObject: object withFrame:cellFrame inView:controlView];
+}
+
+@end
diff --git a/Liquid Glass Bezel/BackgroundView.swift b/Liquid Glass Bezel/BackgroundView.swift
new file mode 100644
index 0000000..e5f4c86
--- /dev/null
+++ b/Liquid Glass Bezel/BackgroundView.swift
@@ -0,0 +1,88 @@
+import Foundation
+import Cocoa
+
+
+class BackgroundView: QSBezelBackgroundView {
+
+ private var glassView: NSGlassEffectView?
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ configureParentView()
+ setupGlassEffect()
+ }
+
+ override init(frame frameRect: NSRect) {
+ super.init(frame: frameRect)
+ configureParentView()
+ setupGlassEffect()
+ }
+
+ required init?(coder: NSCoder) {
+ super.init(coder: coder)
+ configureParentView()
+ setupGlassEffect()
+ }
+
+ private func configureParentView() {
+ // Make the parent background much more transparent so glass effect shows through
+ setColor(NSColor.clear) // Try completely transparent first
+ setIsGlass(NSNumber(value: false)) // Disable the parent's glass rendering
+
+ // Alternative: if clear doesn't work, try a very transparent background
+ // setColor(NSColor.black.withAlphaComponent(0.1))
+ }
+
+ private func setupGlassEffect() {
+ let glassView = NSGlassEffectView(frame: bounds)
+ glassView.autoresizingMask = [.width, .height]
+ glassView.cornerRadius = 12.0
+ glassView.style = .clear
+
+ // Create a content view for the glass effect
+ let contentView = NSView(frame: glassView.bounds)
+ contentView.autoresizingMask = [.width, .height]
+ glassView.contentView = contentView
+
+ // Add subtle tint based on appearance
+ updateGlassTint()
+
+ // Insert the glass view at the bottom of the view hierarchy
+ addSubview(glassView, positioned: .below, relativeTo: nil)
+ self.glassView = glassView
+ }
+
+ private func updateGlassTint() {
+ guard let glassView = glassView else { return }
+
+ let darkMode = effectiveAppearance.name == .darkAqua
+ if darkMode {
+ // Very subtle tint for dark mode to maintain translucency
+ glassView.tintColor = NSColor.black.withAlphaComponent(0.05)
+ } else {
+ // Even lighter tint for light mode
+ glassView.tintColor = NSColor.white.withAlphaComponent(0.02)
+ }
+ }
+
+ override func viewDidMoveToWindow() {
+ super.viewDidMoveToWindow()
+
+ // Ensure the window is configured for transparency
+ if let window = self.window {
+ window.backgroundColor = NSColor.clear
+ window.isOpaque = false
+ }
+ }
+
+ override func draw(_ rect: NSRect) {
+ // Don't call super.draw(rect) to avoid the parent's opaque background
+ // The glass effect will handle the visual appearance
+ return
+ }
+
+ override func viewDidChangeEffectiveAppearance() {
+ super.viewDidChangeEffectiveAppearance()
+ updateGlassTint()
+ }
+}
diff --git a/Liquid Glass Bezel/LiquidGlassBezel-Bridging-Header.h b/Liquid Glass Bezel/LiquidGlassBezel-Bridging-Header.h
new file mode 100644
index 0000000..f216cf4
--- /dev/null
+++ b/Liquid Glass Bezel/LiquidGlassBezel-Bridging-Header.h
@@ -0,0 +1,2 @@
+#import "Quicksilver.pch"
+#import "BBObjectCell.h"
diff --git a/Liquid Glass Bezel/LiquidGlassBezelCollectingSearchObjectView.swift b/Liquid Glass Bezel/LiquidGlassBezelCollectingSearchObjectView.swift
new file mode 100644
index 0000000..a7c8a90
--- /dev/null
+++ b/Liquid Glass Bezel/LiquidGlassBezelCollectingSearchObjectView.swift
@@ -0,0 +1,38 @@
+class LiquidGlassBezelCollectingSearchObjectView: QSCollectingSearchObjectView {
+ override static var cellClass : AnyClass? {
+ get { return BBObjectCell.self }
+ set { super.cellClass = newValue }
+ }
+
+ override func draw(_ rect: NSRect) {
+ var frame = frame
+
+ // These properties aren't available to Swift, so they need to be accessed in this way.
+ let collection = (value(forKey: "collection") as! NSMutableArray)
+ let collecting = (value(forKey: "collecting") as! Bool)
+
+ let count = collection.count
+ if self.currentEditor() == nil && count != 0 {
+ frame.origin = NSZeroPoint
+ cell?.draw(withFrame: frame, in: self)
+ let iconSize = collectionSpace() != 0 ? collectionSpace() : 16
+ let opacity = collecting ? 1.0 : 0.75
+ let totalWidth = iconSize + 2
+ for i in 0..<count {
+ let object = collection.object(at: i) as! QSObject
+ var icon = object.icon()
+ let size = NSMakeSize(16, 16)
+ icon?.size = size
+ let inRect = NSMakeRect(
+ frame.size.width - totalWidth * CGFloat(count - i),
+ frame.origin.y + 2,
+ iconSize,
+ iconSize
+ )
+ icon?.draw(in: inRect, from: rectFromSize(size), operation: .sourceOver, fraction: opacity)
+ }
+ } else {
+ super.draw(rect)
+ }
+ }
+}
diff --git a/Liquid Glass Bezel/LiquidGlassBezelObjectCell.swift b/Liquid Glass Bezel/LiquidGlassBezelObjectCell.swift
new file mode 100644
index 0000000..dfffce6
--- /dev/null
+++ b/Liquid Glass Bezel/LiquidGlassBezelObjectCell.swift
@@ -0,0 +1,164 @@
+/*
+import Foundation
+
+class LiquidGlassBezelObjectCell: QSObjectCell {
+ override func preferredImagePosition() -> 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<count; i += j) {
+ for (j = 1; i+j<count && hits[i+j-1] +1 == hits[i+j]; j++);
+ [titleString addAttributes:attributes range:NSMakeRange(hits[i], j)];
+ }
+ } else {
+ [titleString addAttribute:NSBaselineOffsetAttributeName value:[NSNumber numberWithDouble:-1.0] range:NSMakeRange(0, [titleString length])];
+ }
+
+ if (showDetails) {
+ NSString *detailsString = [drawObject details];
+
+ NSRange returnRange = [detailsString rangeOfString:@"\n"];
+ if (returnRange.location != NSNotFound) {
+ detailsString = [detailsString substringToIndex:returnRange.location];
+ }
+
+ detailsAttributes = [detailsAttributes mutableCopy];
+ [detailsAttributes setValue:[NSColor grayColor] forKey:NSForegroundColorAttributeName];
+
+ if (detailsString && detailsString.length && ![detailsString isEqualToString:nameString]) {
+ [titleString appendAttributedString:[[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"\n%@",detailsString] attributes:detailsAttributes] autorelease]];
+ }
+ }
+
+ NSRect centerRect = rectFromSize([titleString size]);
+ centerRect.size.width = NSWidth(textDrawRect);
+ centerRect.size.height = MIN(NSHeight(textDrawRect), centerRect.size.height);
+ [titleString drawInRect:centerRectInRect(centerRect, textDrawRect)];
+}
+
+- (void)drawSearchPlaceholderWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
+ NSString *defaultText = NSLocalizedStringWithDefaultValue(@"Type to search", nil, [NSBundle mainBundle], @"Type to search", @"Hint that appears in the first pane of the QS interface when it's empty.");
+ NSSize textSize = [defaultText sizeWithAttributes:nameAttributes];
+ NSRect textRect = centerRectInRect(rectFromSize(textSize), cellFrame);
+ BOOL isFirstResponder = [[controlView window] firstResponder] == controlView && ![controlView isKindOfClass:[NSTableView class]];
+
+ if (isFirstResponder && [controlView isKindOfClass:[QSSearchObjectView class]]) {
+ NSImage *find = [NSImage imageWithSystemSymbolName:@"magnifyingglass.circle.fill" accessibilityDescription:nil];
+
+
+ [find setSize:QSSize16];
+ NSRect findImageRect = expelRectFromRectOnEdge(centerRectInRect(rectFromSize([find size]), cellFrame), textRect, NSRectEdgeMinX, -2);
+
+
+ NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
+ [graphicsContext saveGraphicsState];
+ CGContextRef context = [graphicsContext CGContext];
+ CGContextBeginTransparencyLayerWithRect(context, findImageRect, nil);
+ CGContextSetBlendMode(context, kCGBlendModeNormal);
+ [find drawInRect:findImageRect fromRect:rectFromSize([find size]) operation:NSCompositingOperationSourceOver fraction:1];
+ CGContextSetBlendMode(context, kCGBlendModeSourceIn);
+ CGContextSetFillColorWithColor(context, [[NSColor textColor] CGColor]);
+ CGContextFillRect(context, findImageRect);
+ CGContextEndTransparencyLayer(context);
+
+ [defaultText drawInRect:textRect withAttributes:nameAttributes];
+ }
+}
+
+@end
+*/
diff --git a/Liquid Glass Bezel/LiquidGlassBezelSearchObjectView.swift b/Liquid Glass Bezel/LiquidGlassBezelSearchObjectView.swift
new file mode 100644
index 0000000..825b2d3
--- /dev/null
+++ b/Liquid Glass Bezel/LiquidGlassBezelSearchObjectView.swift
@@ -0,0 +1,6 @@
+class LiquidGlassBezelSearchObjectView: QSSearchObjectView {
+ override static var cellClass : AnyClass? {
+ get { return BBObjectCell.self }
+ set { super.cellClass = newValue }
+ }
+}
diff --git a/Liquid Glass Bezel/en.lproj/InfoPlist.strings b/Liquid Glass Bezel/en.lproj/InfoPlist.strings
new file mode 100644
index 0000000..477b28f
--- /dev/null
+++ b/Liquid Glass Bezel/en.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+