Custom colors via defaults plist
This commit is contained in:
@@ -24,6 +24,7 @@ and this project does adhere to [Semantic Versioning](https://semver.org/spec/v2
|
||||
- *Settings, General*: [Auxiliary application](https://github.com/relikd/URL-Scheme-Defaults) for changing default feed reader
|
||||
- *UI:* Accessibility hints for most UI elements
|
||||
- *UI*: Show welcome message upon first usage (empty db)
|
||||
- *UI*: Custom colors via user defaults plist (bar icon tint & unread indicator)
|
||||
- Welcome message also adds Github releases feed
|
||||
- Config URL scheme `barss:` with `open/preferences`, `config/fixcache`, and `backup/show`
|
||||
|
||||
|
||||
37
README.md
37
README.md
@@ -95,25 +95,36 @@ If you prefer the optimized release version go to `Product > Archive`.
|
||||
Hidden options
|
||||
--------------
|
||||
|
||||
1) When holding down the option key, the menu will show an item to open only a few unread items at a time.
|
||||
This listing contains of options that have no UI that can be configured.
|
||||
Most likely, you wouldn't ever stumble upon these if not reading this chapter.
|
||||
**Note:** To reset an option run `defaults delete de.relikd.baRSS {KEY}`, where `{KEY}` is an option from below.
|
||||
|
||||
|
||||
1. If you hold down the option key and click on an article item, you can mark a single item (un-)read.
|
||||
|
||||
2. When holding down the option key, the menu will show an item to open only a few unread items at a time.
|
||||
This number can be changed with the following Terminal command (default: 10):
|
||||
```
|
||||
defaults write de.relikd.baRSS openFewLinksLimit -int 10
|
||||
```
|
||||
|
||||
```defaults write de.relikd.baRSS openFewLinksLimit -int 10```
|
||||
|
||||
|
||||
2) If you hold down the option key and click on an article item, you can mark a single item (un-)read.
|
||||
|
||||
|
||||
3) In preferences you can choose to show 'Short article names'. This will limit the number of displayed characters to 60 (default).
|
||||
3. In preferences you can choose to show 'Short article names'. This will limit the number of displayed characters to 60 (default).
|
||||
With this Terminal command you can customize this limit:
|
||||
```
|
||||
defaults write de.relikd.baRSS shortArticleNamesLimit -int 50
|
||||
```
|
||||
|
||||
```defaults write de.relikd.baRSS shortArticleNamesLimit -int 50```
|
||||
|
||||
|
||||
4) Limit the number of displayed articles per feed menu.
|
||||
4. Limit the number of displayed articles per feed menu.
|
||||
**Note:** displayed unread count may be different than the unread items inside ('Open unread' will open hidden items too).
|
||||
```
|
||||
defaults write de.relikd.baRSS articlesInMenuLimit -int 40
|
||||
```
|
||||
|
||||
```defaults write de.relikd.baRSS articlesInMenuLimit -int 40```
|
||||
5. You can change the appearance of colors throughout the application. E.g., The tint color of the menu bar icon and the color of the blue dot of unread articles.
|
||||
```
|
||||
defaults write de.relikd.baRSS colorStatusIconTint -string "#37F"
|
||||
defaults write de.relikd.baRSS colorUnreadTickMark -string "#FBA33A"
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -22,14 +22,6 @@
|
||||
|
||||
@import Cocoa;
|
||||
|
||||
@interface NSColor (RandomColor)
|
||||
/// just for testing purposes
|
||||
+ (NSColor*)randomColor;
|
||||
/// RGB color with (251, 163, 58)
|
||||
+ (NSColor*)rssOrange;
|
||||
@end
|
||||
|
||||
|
||||
/// Draw separator line in @c NSOutlineView
|
||||
IB_DESIGNABLE
|
||||
@interface DrawSeparator : NSView
|
||||
|
||||
@@ -22,20 +22,7 @@
|
||||
|
||||
#import "DrawImage.h"
|
||||
#import "Constants.h"
|
||||
|
||||
@implementation NSColor (RandomColor)
|
||||
/// @return Color with random R, G, B values for testing purposes
|
||||
+ (NSColor*)randomColor {
|
||||
return [NSColor colorWithRed:(arc4random()%50+20)/100.0
|
||||
green:(arc4random()%50+20)/100.0
|
||||
blue:(arc4random()%50+20)/100.0
|
||||
alpha:1];
|
||||
}
|
||||
/// @return Orange color that is typically used for RSS
|
||||
+ (NSColor*)rssOrange {
|
||||
return [NSColor colorWithCalibratedRed:251/255.0 green:163/255.0 blue:58/255.0 alpha:1.0];
|
||||
}
|
||||
@end
|
||||
#import "UserPrefs.h"
|
||||
|
||||
|
||||
@implementation DrawSeparator
|
||||
@@ -278,14 +265,14 @@ static void DrawRSSIcon(CGRect r, CGColorRef color, BOOL background, BOOL connec
|
||||
}
|
||||
|
||||
/// Draw RSS icon (with orange gradient, corner @c 0.4, white radio waves)
|
||||
static void DrawRSSGradientIcon(CGRect r) {
|
||||
static void DrawRSSGradientIcon(CGRect r, NSColor *color) {
|
||||
const CGFloat size = ShorterSide(r.size);
|
||||
CGContextRef c = NSGraphicsContext.currentContext.CGContext;
|
||||
DrawRoundedFrame(c, r, NSColor.whiteColor.CGColor, YES, 0.4, 1.0, 0.7);
|
||||
// Gradient
|
||||
CGContextSaveGState(c);
|
||||
CGContextClip(c);
|
||||
DrawGradient(c, size, [NSColor rssOrange]);
|
||||
DrawGradient(c, size, color);
|
||||
CGContextRestoreGState(c);
|
||||
// Bars
|
||||
AddRSSIconPath(c, size, YES);
|
||||
@@ -297,7 +284,8 @@ static void DrawUnreadIcon(CGRect r, NSColor *color) {
|
||||
CGFloat size = ShorterSide(r.size) / 2.0;
|
||||
CGContextRef c = NSGraphicsContext.currentContext.CGContext;
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
SetContentScale(c, r.size, 0.8);
|
||||
SetContentScale(c, r.size, 0.7);
|
||||
CGContextTranslateCTM(c, 0, size * -0.15); // align with baseline of menu item text
|
||||
|
||||
CGContextSetFillColorWithColor(c, color.CGColor);
|
||||
PathAddRing(path, size, size * 0.7);
|
||||
@@ -325,12 +313,17 @@ static void Register(CGFloat size, NSImageName name, NSString *description, BOOL
|
||||
/// Register all icons that require custom drawing in @c ImageNamed cache
|
||||
void RegisterImageViewNames(void) {
|
||||
const CGColorRef black = [NSColor controlTextColor].CGColor;
|
||||
Register(16, RSSImageDefaultRSSIcon, NSLocalizedString(@"RSS icon", nil), ^(NSRect r) { DrawRSSGradientIcon(r); return YES; });
|
||||
NSColor* const orange = [NSColor colorWithCalibratedRed:251/255.f green:163/255.f blue:58/255.f alpha:1.f]; // #FBA33A
|
||||
|
||||
Register(16, RSSImageDefaultRSSIcon, NSLocalizedString(@"RSS icon", nil), ^(NSRect r) { DrawRSSGradientIcon(r, orange); return YES; });
|
||||
Register(16, RSSImageSettingsGlobal, NSLocalizedString(@"Global settings", nil), ^(NSRect r) { DrawGlobalIcon(r, black, NO); return YES; });
|
||||
Register(16, RSSImageSettingsGroup, NSLocalizedString(@"Group settings", nil), ^(NSRect r) { DrawGroupIcon(r, black, NO); return YES; });
|
||||
Register(16, RSSImageSettingsFeed, NSLocalizedString(@"Feed settings", nil), ^(NSRect r) { DrawRSSIcon(r, black, NO, YES); return YES; });
|
||||
Register(16, RSSImageMenuBarIconActive, NSLocalizedString(@"RSS menu bar icon", nil), ^(NSRect r) { DrawRSSIcon(r, [NSColor rssOrange].CGColor, YES, YES); return YES; });
|
||||
Register(16, RSSImageMenuBarIconPaused, NSLocalizedString(@"RSS menu bar icon, paused", nil), ^(NSRect r) { DrawRSSIcon(r, [NSColor rssOrange].CGColor, YES, NO); return YES; });
|
||||
Register(12, RSSImageMenuItemUnread, NSLocalizedString(@"Unread icon", nil), ^(NSRect r) { DrawUnreadIcon(r, [NSColor systemBlueColor]); return YES; });
|
||||
// TODO: user selected color for rss bar icon & unread dot
|
||||
|
||||
NSColor *c1 = [UserPrefs defaultColor:orange forKey:@"colorStatusIconTint"];
|
||||
NSColor *c2 = [UserPrefs defaultColor:[NSColor systemBlueColor] forKey:@"colorUnreadTickMark"];
|
||||
|
||||
Register(16, RSSImageMenuBarIconActive, NSLocalizedString(@"RSS menu bar icon", nil), ^(NSRect r) { DrawRSSIcon(r, c1.CGColor, YES, YES); return YES; });
|
||||
Register(16, RSSImageMenuBarIconPaused, NSLocalizedString(@"RSS menu bar icon, paused", nil), ^(NSRect r) { DrawRSSIcon(r, c1.CGColor, YES, NO); return YES; });
|
||||
Register(14, RSSImageMenuItemUnread, NSLocalizedString(@"Unread icon", nil), ^(NSRect r) { DrawUnreadIcon(r, c2); return YES; });
|
||||
}
|
||||
|
||||
@@ -33,9 +33,10 @@
|
||||
+ (BOOL)openURLsWithPreferredBrowser:(NSArray<NSURL*>*)urls;
|
||||
|
||||
// Hidden Plist Properties
|
||||
+ (NSUInteger)openFewLinksLimit; // Change with: 'defaults write de.relikd.baRSS openFewLinksLimit -int 10'
|
||||
+ (NSUInteger)shortArticleNamesLimit; // Change with: 'defaults write de.relikd.baRSS shortArticleNamesLimit -int 50'
|
||||
+ (NSUInteger)articlesInMenuLimit; // Change with: 'defaults write de.relikd.baRSS articlesInMenuLimit -int 40'
|
||||
+ (NSUInteger)openFewLinksLimit; // Change with: defaults write de.relikd.baRSS openFewLinksLimit -int 10
|
||||
+ (NSUInteger)shortArticleNamesLimit; // Change with: defaults write de.relikd.baRSS shortArticleNamesLimit -int 50
|
||||
+ (NSUInteger)articlesInMenuLimit; // Change with: defaults write de.relikd.baRSS articlesInMenuLimit -int 40
|
||||
+ (NSColor*)defaultColor:(NSColor*)defaultColor forKey:(NSString*)key; // Change with: defaults write de.relikd.baRSS {KEY} -string "#FBA33A"
|
||||
|
||||
// Application Info Plist
|
||||
+ (NSString*)appName;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#import "UserPrefs.h"
|
||||
#import "StoreCoordinator.h"
|
||||
#import "NSString+Ext.h"
|
||||
|
||||
@implementation UserPrefs
|
||||
|
||||
@@ -84,6 +85,18 @@
|
||||
/// Default: @c 40
|
||||
+ (NSUInteger)articlesInMenuLimit { return [self defaultUInt:40 forKey:@"articlesInMenuLimit"]; }
|
||||
|
||||
/// @return Returns @c defaultColor if defaults value couldn't be parsed or wasn't modified by user.
|
||||
+ (NSColor*)defaultColor:(NSColor*)defaultColor forKey:(NSString*)key {
|
||||
NSString *colorStr = [[NSUserDefaults standardUserDefaults] stringForKey:key];
|
||||
if (colorStr) {
|
||||
NSColor *color = [colorStr hexColor];
|
||||
if (color) return color;
|
||||
NSLog(@"Error reading defaults '%@'. Hex color '%@' is invalid. It should be of the form #RBG or #RRGGBB.", key, colorStr);
|
||||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
|
||||
}
|
||||
return defaultColor;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Application Info Plist
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>13723</string>
|
||||
<string>13931</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.news</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
||||
@@ -28,8 +28,9 @@
|
||||
@interface NSError (Ext)
|
||||
// Generators
|
||||
+ (instancetype)statusCode:(NSInteger)code reason:(nullable NSString*)reason;
|
||||
+ (instancetype)canceledByUser;
|
||||
+ (instancetype)feedURLNotFound:(NSURL*)url;
|
||||
+ (instancetype)canceledByUser;
|
||||
//+ (instancetype)formattingError:(NSString*)description;
|
||||
// User notification
|
||||
- (BOOL)inCaseLog:(nullable const char*)title;
|
||||
- (BOOL)inCasePresent:(NSApplication*)app;
|
||||
|
||||
@@ -115,17 +115,23 @@ static const char* CodeDescription(NSInteger code) {
|
||||
return [self errorWithDomain:NSURLErrorDomain code:errCode userInfo:info];
|
||||
}
|
||||
|
||||
/// Generate @c NSError for user canceled operation. With title "Operation canceled.".
|
||||
+ (instancetype)canceledByUser {
|
||||
NSDictionary *info = @{ NSLocalizedDescriptionKey: NSLocalizedString(@"Operation canceled.", nil) };
|
||||
return [self errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:info];
|
||||
}
|
||||
|
||||
/// Generate @c NSError for webpages that don't contain feed urls.
|
||||
+ (instancetype)feedURLNotFound:(NSURL*)url {
|
||||
return RSXMLMakeErrorWrongParser(RSXMLErrorExpectingFeed, RSXMLErrorExpectingHTML, url);
|
||||
}
|
||||
|
||||
/// Generate @c NSError for user canceled operation. With title "Operation was canceled."
|
||||
+ (instancetype)canceledByUser {
|
||||
return [NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil];
|
||||
}
|
||||
|
||||
/*// Generate @c NSError for invalid or malformed input. With title "The value is invalid."
|
||||
+ (instancetype)formattingError:(NSString*)description {
|
||||
NSDictionary *info = nil;
|
||||
if (description) info = @{ NSLocalizedRecoverySuggestionErrorKey: description };
|
||||
return [NSError errorWithDomain:NSCocoaErrorDomain code:NSFormattingError userInfo:info];
|
||||
}*/
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// | MARK: - User notification
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
@@ -22,7 +22,11 @@
|
||||
|
||||
@import Cocoa;
|
||||
|
||||
@interface NSString (Ext)
|
||||
@interface NSString (PlainHTML)
|
||||
+ (NSString*)plainTextFromHTMLData:(NSData*)data;
|
||||
- (nonnull NSString*)htmlToPlainText;
|
||||
@end
|
||||
|
||||
@interface NSString (HexColor)
|
||||
- (nullable NSColor*)hexColor;
|
||||
@end
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
#import "NSString+Ext.h"
|
||||
|
||||
@implementation NSString (Ext)
|
||||
@implementation NSString (PlainHTML)
|
||||
|
||||
/// Init string with @c NSUTF8StringEncoding and call @c htmlToPlainText
|
||||
+ (NSString*)plainTextFromHTMLData:(NSData*)data {
|
||||
@@ -112,3 +112,51 @@ static inline BOOL CLOSE(NSString *tag, NSString *match) {
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation NSString (HexColor)
|
||||
|
||||
/**
|
||||
Color from hex string with format: @c #[0x|0X]([A]RGB|[AA]RRGGBB)
|
||||
|
||||
@return @c nil if string is not properly formatted.
|
||||
*/
|
||||
- (nullable NSColor*)hexColor {
|
||||
if ([self characterAtIndex:0] != '#') // must start with '#'
|
||||
return nil;
|
||||
|
||||
NSScanner *scanner = [NSScanner scannerWithString:self];
|
||||
scanner.scanLocation = 1;
|
||||
unsigned int value;
|
||||
if (![scanner scanHexInt:&value])
|
||||
return nil;
|
||||
|
||||
NSUInteger len = scanner.scanLocation - 1; // -'#'
|
||||
if (len > 1 && ([self characterAtIndex:2] == 'x' || [self characterAtIndex:3] == 'X'))
|
||||
len -= 2; // ignore '0x'RRGGBB
|
||||
|
||||
unsigned int r = 0, g = 0, b = 0, a = 255;
|
||||
switch (len) {
|
||||
case 4: // #ARGB
|
||||
// ignore alpha for now
|
||||
// a = (value >> 8) & 0xF0; a = a | (a >> 4);
|
||||
case 3: // #RGB
|
||||
r = (value >> 4) & 0xF0; r = r | (r >> 4);
|
||||
g = (value) & 0xF0; g = g | (g >> 4);
|
||||
b = (value) & 0x0F; b = b | (b << 4);
|
||||
break;
|
||||
case 8: // #AARRGGBB
|
||||
// a = (value >> 24) & 0xFF;
|
||||
case 6: // #RRGGBB
|
||||
r = (value >> 16) & 0xFF;
|
||||
g = (value >> 8) & 0xFF;
|
||||
b = (value) & 0xFF;
|
||||
break;
|
||||
default:
|
||||
return nil;
|
||||
}
|
||||
return [NSColor colorWithCalibratedRed:r/255.f green:g/255.f blue:b/255.f alpha:a/255.f];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user