aboutsummaryrefslogtreecommitdiff
path: root/Liquid Glass Bezel/BBObjectCell.m
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/BBObjectCell.m
parent3dfeb86c5c3b72acc59919c7a198fcf76a54c35c (diff)
Rename to Liquid Glass Bezel, add preferences
Diffstat (limited to 'Liquid Glass Bezel/BBObjectCell.m')
-rw-r--r--Liquid Glass Bezel/BBObjectCell.m211
1 files changed, 211 insertions, 0 deletions
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