From 3a79faaa65b7a353cadc383eddfd81cdb62852bb Mon Sep 17 00:00:00 2001 From: relikd Date: Sun, 5 Aug 2018 23:55:07 +0200 Subject: [PATCH] RSS icon drawing --- baRSS.xcodeproj/project.pbxproj | 6 ++ baRSS/AppDelegate.m | 7 +- baRSS/Base.lproj/Main.xib | 22 +++--- baRSS/DrawImage.h | 45 ++++++++++++ baRSS/DrawImage.m | 122 ++++++++++++++++++++++++++++++++ baRSS/NewsController.m | 32 ++++----- 6 files changed, 204 insertions(+), 30 deletions(-) create mode 100644 baRSS/DrawImage.h create mode 100644 baRSS/DrawImage.m diff --git a/baRSS.xcodeproj/project.pbxproj b/baRSS.xcodeproj/project.pbxproj index 49a7898..fe07b64 100644 --- a/baRSS.xcodeproj/project.pbxproj +++ b/baRSS.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 1968E0AE14B8E8A90E194980 /* PyHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 1968EF7567E06D2A5BB3481A /* PyHandler.m */; }; + 54209E942117325100F3B5EF /* DrawImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 54209E932117325100F3B5EF /* DrawImage.m */; }; 544B011A2114B41200386E5C /* ModalSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = 544B01192114B41200386E5C /* ModalSheet.m */; }; 544B011D2114EE9100386E5C /* AppHook.m in Sources */ = {isa = PBXBuildFile; fileRef = 544B011C2114EE9100386E5C /* AppHook.m */; }; 544FBD4521064AEB008A260C /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 544FBD4421064AEB008A260C /* Python.framework */; }; @@ -25,6 +26,8 @@ /* Begin PBXFileReference section */ 1968E7919BAA36F042FCB717 /* PyHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyHandler.h; sourceTree = ""; }; 1968EF7567E06D2A5BB3481A /* PyHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PyHandler.m; sourceTree = ""; }; + 54209E922117325100F3B5EF /* DrawImage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DrawImage.h; sourceTree = ""; }; + 54209E932117325100F3B5EF /* DrawImage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DrawImage.m; sourceTree = ""; }; 544B01182114B41200386E5C /* ModalSheet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ModalSheet.h; sourceTree = ""; }; 544B01192114B41200386E5C /* ModalSheet.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ModalSheet.m; sourceTree = ""; }; 544B011B2114EE9100386E5C /* AppHook.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppHook.h; sourceTree = ""; }; @@ -108,6 +111,8 @@ 54ACC29721061FBA0020715F /* Preferences.m */, 544B01182114B41200386E5C /* ModalSheet.h */, 544B01192114B41200386E5C /* ModalSheet.m */, + 54209E922117325100F3B5EF /* DrawImage.h */, + 54209E932117325100F3B5EF /* DrawImage.m */, 54ACC28521061B3C0020715F /* Assets.xcassets */, 54ACC28721061B3C0020715F /* Main.xib */, 54ACC28A21061B3C0020715F /* Info.plist */, @@ -205,6 +210,7 @@ 544B011A2114B41200386E5C /* ModalSheet.m in Sources */, 54ACC29821061FBA0020715F /* Preferences.m in Sources */, 1968E0AE14B8E8A90E194980 /* PyHandler.m in Sources */, + 54209E942117325100F3B5EF /* DrawImage.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/baRSS/AppDelegate.m b/baRSS/AppDelegate.m index 9657914..26cf493 100644 --- a/baRSS/AppDelegate.m +++ b/baRSS/AppDelegate.m @@ -22,6 +22,7 @@ #import "AppDelegate.h" #import "PyHandler.h" +#import "DrawImage.h" @interface AppDelegate () @property (strong) NSStatusItem *statusItem; @@ -34,9 +35,11 @@ self.statusItem = [NSStatusBar.systemStatusBar statusItemWithLength:NSVariableStatusItemLength]; self.statusItem.title = @"me"; self.statusItem.menu = self.statusMenu; - printf("will init\n"); + self.statusItem.highlightMode = YES; + self.statusItem.image = [[RSSIcon templateIcon:16 tint:nil] image]; + self.statusItem.image.template = YES; [PyHandler prepare]; - printf("done\n"); + printf("up and running\n"); } - (void)applicationWillTerminate:(NSNotification *)aNotification { diff --git a/baRSS/Base.lproj/Main.xib b/baRSS/Base.lproj/Main.xib index ed30530..335bf8c 100644 --- a/baRSS/Base.lproj/Main.xib +++ b/baRSS/Base.lproj/Main.xib @@ -308,21 +308,21 @@ - + - + - + @@ -336,16 +336,16 @@ - + - + - + @@ -360,11 +360,11 @@ - + - + @@ -379,7 +379,7 @@ - + @@ -392,11 +392,11 @@ - + - + diff --git a/baRSS/DrawImage.h b/baRSS/DrawImage.h new file mode 100644 index 0000000..f2475d8 --- /dev/null +++ b/baRSS/DrawImage.h @@ -0,0 +1,45 @@ +// +// The MIT License (MIT) +// Copyright (c) 2018 Oleg Geier +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#import + +@interface RSSIcon : NSObject +@property (strong) NSColor *barsColor; +@property (strong) NSColor *squareColor; +@property (strong) NSColor *squareGradientColor; +@property (assign) NSSize size; +@property (assign) bool isTemplate; + ++ (instancetype)iconWithSize:(NSSize)size; ++ (instancetype)templateIcon:(CGFloat)size tint:(nullable NSColor*)color; +- (instancetype)autoGradient; + ++ (NSColor*)rssOrange; +- (NSImage*)image; +@end + + +IB_DESIGNABLE +@interface DrawSeparator : NSView +@end + + diff --git a/baRSS/DrawImage.m b/baRSS/DrawImage.m new file mode 100644 index 0000000..1ccb3a7 --- /dev/null +++ b/baRSS/DrawImage.m @@ -0,0 +1,122 @@ +// +// The MIT License (MIT) +// Copyright (c) 2018 Oleg Geier +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#import "DrawImage.h" + +@implementation RSSIcon + ++ (NSColor*)rssOrange { + return [NSColor colorWithCalibratedRed:0.984 green:0.639 blue:0.227 alpha:1.0]; +} + ++ (instancetype)iconWithSize:(NSSize)size { + RSSIcon *icon = [[super alloc] init]; + icon.size = size; + icon.barsColor = [NSColor whiteColor]; + icon.squareColor = [RSSIcon rssOrange]; + return icon; +} + ++ (instancetype)templateIcon:(CGFloat)s tint:(NSColor*)color { + RSSIcon *icon = [[super alloc] init]; + icon.size = NSMakeSize(s, s); + icon.squareColor = (color ? color : [NSColor blackColor]); + icon.isTemplate = YES; + return icon; +} + +- (instancetype)autoGradient { + const CGFloat h = self.squareColor.hueComponent; + const CGFloat s = self.squareColor.saturationComponent; + const CGFloat b = self.squareColor.brightnessComponent; + const CGFloat a = self.squareColor.alphaComponent; + static const CGFloat impact = 0.3; + self.squareGradientColor = [NSColor colorWithHue:h saturation:(s - impact < 0 ? 0 : s - impact) brightness:b alpha:a]; + self.squareColor = [NSColor colorWithHue:h saturation:(s + impact > 1 ? 1 : s + impact) brightness:b alpha:a]; + return self; +} + +- (NSImage*)image { + return [NSImage imageWithSize:self.size flipped:NO drawingHandler:^BOOL(NSRect rect) { + CGFloat s = (self.size.height < self.size.width ? self.size.height : self.size.width); + CGFloat corner = s * 0.2; + + CGMutablePathRef square = CGPathCreateMutable(); // the brackground + CGPathAddRoundedRect(square, NULL, rect, corner, corner); + + CGMutablePathRef bars = CGPathCreateMutable(); // the rss bars + CGAffineTransform at = CGAffineTransformMake(0.75, 0, 0, 0.75, s * 0.15, s * 0.15); // scale 0.75, translate 0.15 + // circle + CGPathAddEllipseInRect(bars, &at, CGRectMake(0, 0, s * 0.25, s * 0.25)); + // 1st bar + CGPathMoveToPoint(bars, &at, 0, s * 0.65); + CGPathAddArc(bars, &at, 0, 0, s * 0.65, M_PI_2, 0, YES); + CGPathAddLineToPoint(bars, &at, s * 0.45, 0); + CGPathAddArc(bars, &at, 0, 0, s * 0.45, 0, M_PI_2, NO); + CGPathCloseSubpath(bars); + // 2nd bar + CGPathMoveToPoint(bars, &at, 0, s); + CGPathAddArc(bars, &at, 0, 0, s, M_PI_2, 0, YES); + CGPathAddLineToPoint(bars, &at, s * 0.8, 0); + CGPathAddArc(bars, &at, 0, 0, s * 0.8, 0, M_PI_2, NO); + CGPathCloseSubpath(bars); + + CGContextRef c = [[NSGraphicsContext currentContext] CGContext]; + CGContextSetFillColorWithColor(c, [self.squareColor CGColor]); + CGContextAddPath(c, square); + if (!self.isTemplate) { + if (self.squareGradientColor) { + CGContextClip(c); + const void* cgColors[] = { + [self.squareColor CGColor], + [self.squareGradientColor CGColor], + [self.squareColor CGColor] + }; + CFArrayRef colors = CFArrayCreate(NULL, cgColors, 3, NULL); + CGGradientRef gradient = CGGradientCreateWithColors(NULL, colors, NULL); + CGContextDrawLinearGradient(c, gradient, CGPointMake(0, s), CGPointMake(s, 0), 0); + CGGradientRelease(gradient); + CFRelease(colors); + } + CGContextFillPath(c); + CGContextSetFillColorWithColor(c, [self.barsColor CGColor]); + } + CGContextAddPath(c, bars); + CGContextEOFillPath(c); + + CGPathRelease(square); + CGPathRelease(bars); + return YES; + }]; +} + +@end + + + +@implementation DrawSeparator +- (void)drawRect:(NSRect)dirtyRect { + NSGradient *grdnt = [[NSGradient alloc] initWithStartingColor:[NSColor darkGrayColor] endingColor:[[NSColor darkGrayColor] colorWithAlphaComponent:0.0]]; + NSBezierPath *rounded = [NSBezierPath bezierPathWithRoundedRect:NSMakeRect(1, self.bounds.size.height/2.0-1, self.bounds.size.width-2, 2) xRadius:1 yRadius:1]; + [grdnt drawInBezierPath:rounded angle:0]; +} +@end diff --git a/baRSS/NewsController.m b/baRSS/NewsController.m index 54e21f4..afedf2a 100644 --- a/baRSS/NewsController.m +++ b/baRSS/NewsController.m @@ -25,6 +25,7 @@ #import "Preferences.h" #import "DBv1+CoreDataModel.h" #import "ModalSheet.h" +#import "DrawImage.h" @interface NewsController () @property (weak) IBOutlet Preferences *preferencesWindow; @@ -49,6 +50,7 @@ static NSString *dragNodeType = @"baRSS-feed-drag"; - (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item { FeedConfig *f = [(NSTreeNode*)item representedObject]; + bool isFeed = (f.type == 1); bool isSeperator = (f.type == 2); bool isRefreshColumn = [tableColumn.identifier isEqualToString:@"RefreshColumn"]; @@ -57,15 +59,24 @@ static NSString *dragNodeType = @"baRSS-feed-drag"; NSTableCellView *cellView = [self.outlineView makeViewWithIdentifier:cellIdent owner:nil]; if (isRefreshColumn) { - cellView.textField.stringValue = (f.type != 1 ? @"" : [ModalFeedEdit stringForRefreshNum:f.refreshNum unit:f.refreshUnit]); + cellView.textField.stringValue = (!isFeed ? @"" : [ModalFeedEdit stringForRefreshNum:f.refreshNum unit:f.refreshUnit]); } else if (isSeperator) { return cellView; // the refresh cell is already skipped with the above if condition } else { cellView.textField.objectValue = f.name; - cellView.imageView.image = (f.type == 0 ? [NSImage imageNamed:NSImageNameFolder] : nil); - // TODO: load icon or show default rss icon + if (f.type == 0) { + cellView.imageView.image = [NSImage imageNamed:NSImageNameFolder]; + } else { + // TODO: load icon + static NSImage *defaultRSSIcon; + if (!defaultRSSIcon) + defaultRSSIcon = [[[RSSIcon iconWithSize:cellView.imageView.frame.size] autoGradient] image]; + + cellView.imageView.image = defaultRSSIcon; + } } - cellView.textField.textColor = (f.refreshNum == 0 ? [NSColor disabledControlTextColor] : [NSColor controlTextColor]); + if (isFeed) // also for refresh column + cellView.textField.textColor = (f.refreshNum == 0 ? [NSColor disabledControlTextColor] : [NSColor controlTextColor]); return cellView; } @@ -322,16 +333,3 @@ static NSString *dragNodeType = @"baRSS-feed-drag"; } @end - -#pragma mark - Drawings on Screen - -@interface DrawSeparator : NSView @end - -@implementation DrawSeparator -- (void)drawRect:(NSRect)dirtyRect { - [super drawRect:dirtyRect]; - NSGradient *grdnt = [[NSGradient alloc] initWithStartingColor:[NSColor darkGrayColor] endingColor:[[NSColor darkGrayColor] colorWithAlphaComponent:0.0]]; - NSBezierPath *rounded = [NSBezierPath bezierPathWithRoundedRect:NSMakeRect(1, self.bounds.size.height/2.0-1, self.bounds.size.width-2, 2) xRadius:1 yRadius:1]; - [grdnt drawInBezierPath:rounded angle:0]; -} -@end