Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b194a1427d | ||
|
|
ff34781fea | ||
|
|
4edd4448ae | ||
|
|
33f907228b | ||
|
|
673e0d3d48 | ||
|
|
b3fdadb9f4 | ||
|
|
9fc513254f | ||
|
|
881b9db02c | ||
|
|
3a14c90f37 | ||
|
|
96884474ac | ||
|
|
82ae18c8a5 | ||
|
|
6eddb57651 | ||
|
|
67d17599b5 | ||
|
|
3507fd8e27 | ||
|
|
ca417f35b6 | ||
|
|
6e5326f913 | ||
|
|
1589b23aa9 | ||
|
|
e0cd04b882 | ||
|
|
6b4c38ec21 | ||
|
|
e7208ae2ab | ||
|
|
508377a823 | ||
|
|
2185eb76fb |
@@ -108,6 +108,13 @@ 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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
3. Each article menu item shows a summary tooltip (if the server provides one).
|
||||||
|
By default, the tooltip is limited to 2000 characters.
|
||||||
|
You can change the limit with this command:
|
||||||
|
```
|
||||||
|
defaults write de.relikd.baRSS tooltipCharacterLimit -int 500
|
||||||
|
```
|
||||||
|
|
||||||
3. Limit the number of displayed articles per feed menu.
|
3. Limit the number of displayed articles per feed menu.
|
||||||
**Note:** displayed unread count may be different than the unread items inside. 'Open all unread' will open hidden items too.
|
**Note:** displayed unread count may be different than the unread items inside. 'Open all unread' will open hidden items too.
|
||||||
```
|
```
|
||||||
|
|||||||
8
baRSS/Artwork/icon-appearance-article.svg
Normal file
8
baRSS/Artwork/icon-appearance-article.svg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<rect y="14" width="16" height="1"/>
|
||||||
|
<rect y="10" width="16" height="1"/>
|
||||||
|
<rect x="9" y="6" width="7" height="1"/>
|
||||||
|
<rect x="9" y="2" width="7" height="1"/>
|
||||||
|
<rect x="1" y="1" width="7" height="7"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 313 B |
7
baRSS/Artwork/icon-appearance-group.svg
Normal file
7
baRSS/Artwork/icon-appearance-group.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<g fill="none" stroke="#000">
|
||||||
|
<path d="M3,13.5c-1.5,0-2.5-1-2.5-2.5V3.5c0-1.5.5-2,2-2h1.5c1.5,0,1.5,1,3,1h6c1.5,0,2.5,1,2.5,2.5v6c0,1.5-1,2.5-2.5,2.5H3Z"/>
|
||||||
|
<path d="M1.5,5h13Z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 294 B |
11
baRSS/Artwork/icon-appearance-main-menu.svg
Normal file
11
baRSS/Artwork/icon-appearance-main-menu.svg
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<!-- menu -->
|
||||||
|
<rect x="0" y="0" width="16" height="3"/>
|
||||||
|
<rect x="5" y="4" width="9" height="12"/>
|
||||||
|
<rect x="6" y="3" width="7" height="12" fill="#aaa"/>
|
||||||
|
<!-- entries -->
|
||||||
|
<rect x="6" y="12" width="6" height="1"/>
|
||||||
|
<rect x="6" y="9" width="6" height="1"/>
|
||||||
|
<rect x="6" y="6" width="6" height="1"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 415 B |
7
baRSS/Artwork/icon-regex.svg
Normal file
7
baRSS/Artwork/icon-regex.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
|
<path d="M18,19c-14,21-13,43,0,62l-7,4C-4,63-4,35,12,14l6,5Z"/>
|
||||||
|
<circle cx="31" cy="67" r="7"/>
|
||||||
|
<path d="M65,28l11-4,2,6-11,4,7,9-5,4-7-9-7,9-5-4,7-9-11-4,2-6,11,4v-11h6v11Z"/>
|
||||||
|
<path d="M82,81c14-21,13-43,0-62l7-5c16,22,15,50,0,71l-7-4Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 356 B |
7
baRSS/Artwork/icon-rss-plain-paused.svg
Normal file
7
baRSS/Artwork/icon-rss-plain-paused.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
|
<circle cx="13" cy="87" r="13"/>
|
||||||
|
<path d="M0,35q65,0,65,65h-20q0,-45,-45,-45z"/>
|
||||||
|
<rect x="60" y="0" width="15" height="50"/>
|
||||||
|
<rect x="85" y="0" width="15" height="50"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 281 B |
6
baRSS/Artwork/icon-rss-plain.svg
Normal file
6
baRSS/Artwork/icon-rss-plain.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
|
<circle cx="13" cy="87" r="13"/>
|
||||||
|
<path d="M0,35q65,0,65,65h-20q0,-45,-45,-45z"/>
|
||||||
|
<path d="M0,0q100,0,100,100h-20q0,-80,-80,-80z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 242 B |
@@ -22,12 +22,16 @@ static NSString* const auxiliaryAppURL = @"https://github.com/relikd/URL-Scheme-
|
|||||||
|
|
||||||
/// Default RSS icon (with border, with gradient, orange)
|
/// Default RSS icon (with border, with gradient, orange)
|
||||||
static NSImageName const RSSImageDefaultRSSIcon = @"RSSImageDefaultRSSIcon";
|
static NSImageName const RSSImageDefaultRSSIcon = @"RSSImageDefaultRSSIcon";
|
||||||
/// Settings, global icon (menu bar, black)
|
/// Settings, global statusbar icon (rss icon with neighbor icons)
|
||||||
static NSImageName const RSSImageSettingsGlobal = @"RSSImageSettingsGlobal";
|
static NSImageName const RSSImageSettingsGlobalIcon = @"RSSImageSettingsGlobalIcon";
|
||||||
|
/// Settings, global menu icon (menu bar, black)
|
||||||
|
static NSImageName const RSSImageSettingsGlobalMenu = @"RSSImageSettingsGlobalMenu";
|
||||||
/// Settings, group icon (folder, black)
|
/// Settings, group icon (folder, black)
|
||||||
static NSImageName const RSSImageSettingsGroup = @"RSSImageSettingsGroup";
|
static NSImageName const RSSImageSettingsGroup = @"RSSImageSettingsGroup";
|
||||||
/// Settings, feed icon (RSS, no border, no gradient, black)
|
/// Settings, feed icon (RSS, no border, no gradient, black)
|
||||||
static NSImageName const RSSImageSettingsFeed = @"RSSImageSettingsFeed";
|
static NSImageName const RSSImageSettingsFeed = @"RSSImageSettingsFeed";
|
||||||
|
/// Settings, article icon (RSS surrounded by text lines)
|
||||||
|
static NSImageName const RSSImageSettingsArticle = @"RSSImageSettingsArticle";
|
||||||
/// Menu bar, bar icon (RSS, with border, no gradient, orange)
|
/// Menu bar, bar icon (RSS, with border, no gradient, orange)
|
||||||
static NSImageName const RSSImageMenuBarIconActive = @"RSSImageMenuBarIconActive";
|
static NSImageName const RSSImageMenuBarIconActive = @"RSSImageMenuBarIconActive";
|
||||||
/// Menu bar, bar icon (RSS, with border, no gradient, paused, orange)
|
/// Menu bar, bar icon (RSS, with border, no gradient, paused, orange)
|
||||||
|
|||||||
@@ -63,7 +63,14 @@
|
|||||||
item.state = (self.unread && UserPrefsBool(Pref_feedUnreadIndicator) ? NSControlStateValueOn : NSControlStateValueOff);
|
item.state = (self.unread && UserPrefsBool(Pref_feedUnreadIndicator) ? NSControlStateValueOn : NSControlStateValueOff);
|
||||||
item.onStateImage = [NSImage imageNamed:RSSImageMenuItemUnread];
|
item.onStateImage = [NSImage imageNamed:RSSImageMenuItemUnread];
|
||||||
item.accessibilityLabel = (self.unread ? NSLocalizedString(@"article: unread", @"accessibility label, feed menu item") : NSLocalizedString(@"article: read", @"accessibility label, feed menu item"));
|
item.accessibilityLabel = (self.unread ? NSLocalizedString(@"article: unread", @"accessibility label, feed menu item") : NSLocalizedString(@"article: read", @"accessibility label, feed menu item"));
|
||||||
item.toolTip = (self.abstract ? self.abstract : self.body); // fall back to body (html)
|
// truncate tooltip
|
||||||
|
NSUInteger limit = UserPrefsUInt(Pref_tooltipCharacterLimit);
|
||||||
|
if (limit > 0) {
|
||||||
|
NSString *tooltip = (self.abstract ? self.abstract : self.body); // fall back to body (html)
|
||||||
|
if (tooltip.length > limit)
|
||||||
|
tooltip = [[tooltip substringToIndex:limit] stringByAppendingString:@"…\n[…]"];
|
||||||
|
item.toolTip = tooltip;
|
||||||
|
}
|
||||||
item.representedObject = self.objectID;
|
item.representedObject = self.objectID;
|
||||||
item.target = [self class];
|
item.target = [self class];
|
||||||
item.action = @selector(didClickOnMenuItem:);
|
item.action = @selector(didClickOnMenuItem:);
|
||||||
|
|||||||
@@ -23,126 +23,18 @@ static inline const CGFloat ShorterSide(NSSize s) {
|
|||||||
return (s.width < s.height ? s.width : s.height);
|
return (s.width < s.height ? s.width : s.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform @c CGAffineTransform with custom rotation point
|
/// Flip coordinate system
|
||||||
// CGAffineTransform RotateAroundPoint(CGAffineTransform at, CGFloat angle, CGFloat x, CGFloat y) {
|
//static void FlipCoordinateSystem(CGContextRef c, CGFloat height) {
|
||||||
// at = CGAffineTransformTranslate(at, x, y);
|
// CGContextTranslateCTM(c, 0, height);
|
||||||
// at = CGAffineTransformRotate(at, angle);
|
// CGContextScaleCTM(c, 1, -1);
|
||||||
// return CGAffineTransformTranslate(at, -x, -y);
|
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
/// Scale and translate context to the center with respect to the new scale. If @c width @c != @c length align top left.
|
||||||
#pragma mark - CGPath Component Generators
|
static void SetContentScale(CGContextRef c, CGSize size, CGFloat scale) {
|
||||||
|
const CGFloat s = ShorterSide(size);
|
||||||
|
CGFloat offset = s * (1 - scale) / 2;
|
||||||
/// Add circle with @c radius
|
CGContextTranslateCTM(c, offset, size.height - s + offset); // top left alignment
|
||||||
static inline void PathAddCircle(CGMutablePathRef path, CGFloat radius) {
|
CGContextScaleCTM(c, scale, scale);
|
||||||
CGPathAddArc(path, NULL, radius, radius, radius, 0, M_PI * 2, YES);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add ring with @c radius and @c innerRadius
|
|
||||||
static inline void PathAddRing(CGMutablePathRef path, CGFloat radius, CGFloat innerRadius) {
|
|
||||||
CGPathAddArc(path, NULL, radius, radius, radius, 0, M_PI * 2, YES);
|
|
||||||
CGPathAddArc(path, NULL, radius, radius, innerRadius, 0, M_PI * -2, YES);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a single RSS icon radio wave
|
|
||||||
static inline void PathAddRSSArc(CGMutablePathRef path, CGFloat radius, CGFloat thickness) {
|
|
||||||
CGPathMoveToPoint(path, NULL, 0, radius + thickness);
|
|
||||||
CGPathAddArc(path, NULL, 0, 0, radius + thickness, M_PI_2, 0, YES);
|
|
||||||
CGPathAddLineToPoint(path, NULL, radius, 0);
|
|
||||||
CGPathAddArc(path, NULL, 0, 0, radius, 0, M_PI_2, NO);
|
|
||||||
CGPathCloseSubpath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add two vertical bars representing a pause icon
|
|
||||||
static inline void PathAddPauseIcon(CGMutablePathRef path, CGAffineTransform at, CGFloat size, CGFloat thickness) {
|
|
||||||
const CGFloat off = (size - 2 * thickness) / 4;
|
|
||||||
CGPathAddRect(path, &at, CGRectMake(off, 0, thickness, size));
|
|
||||||
CGPathAddRect(path, &at, CGRectMake(size/2 + off, 0, thickness, size));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add X icon by applying a rotational affine transform and drawing a plus sign
|
|
||||||
// void PathAddXIcon(CGMutablePathRef path, CGAffineTransform at, CGFloat size, CGFloat thickness) {
|
|
||||||
// at = RotateAroundPoint(at, M_PI_4, size/2, size/2);
|
|
||||||
// const CGFloat p = size * 0.5 - thickness / 2;
|
|
||||||
// CGPathAddRect(path, &at, CGRectMake(0, p, size, thickness));
|
|
||||||
// CGPathAddRect(path, &at, CGRectMake(p, 0, thickness, p));
|
|
||||||
// CGPathAddRect(path, &at, CGRectMake(p, p + thickness, thickness, p));
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - Full Icon Path Generators
|
|
||||||
|
|
||||||
|
|
||||||
/// Create @c CGPath for global icon; a menu bar and an open menu below
|
|
||||||
static inline void AddGlobalIconPath(CGContextRef c, CGFloat size) {
|
|
||||||
CGMutablePathRef menu = CGPathCreateMutable();
|
|
||||||
CGPathAddRect(menu, NULL, CGRectMake(0, 0.8 * size, size, 0.2 * size));
|
|
||||||
CGPathAddRect(menu, NULL, CGRectMake(0.3 * size, 0, 0.55 * size, 0.75 * size));
|
|
||||||
CGPathAddRect(menu, NULL, CGRectMake(0.35 * size, 0.05 * size, 0.45 * size, 0.75 * size));
|
|
||||||
|
|
||||||
CGFloat entryHeight = 0.1 * size; // 0.075
|
|
||||||
for (int i = 0; i < 3; i++) { // 4
|
|
||||||
//CGPathAddRect(menu, NULL, CGRectMake(0.37 * size, (2 * i + 1) * entryHeight, 0.42 * size, entryHeight)); // uncomment path above
|
|
||||||
CGPathAddRect(menu, NULL, CGRectMake(0.35 * size, (2 * i + 1.5) * entryHeight, 0.4 * size, entryHeight * 0.8));
|
|
||||||
}
|
|
||||||
CGContextAddPath(c, menu);
|
|
||||||
CGPathRelease(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create @c CGPath for group icon; a folder symbol
|
|
||||||
static inline void AddGroupIconPath(CGContextRef c, CGFloat size, BOOL showBackground) {
|
|
||||||
const CGFloat r1 = size * 0.05; // corners
|
|
||||||
const CGFloat r2 = size * 0.08; // upper part, name tag
|
|
||||||
const CGFloat r3 = size * 0.15; // lower part, corners inside
|
|
||||||
const CGFloat posTop = 0.85 * size;
|
|
||||||
const CGFloat posMiddle = 0.6 * size - r3;
|
|
||||||
const CGFloat posBottom = 0.15 * size + r1;
|
|
||||||
const CGFloat posNameTag = 0.3 * size;
|
|
||||||
|
|
||||||
CGMutablePathRef upper = CGPathCreateMutable();
|
|
||||||
CGPathMoveToPoint(upper, NULL, 0, 0.5 * size);
|
|
||||||
CGPathAddLineToPoint(upper, NULL, 0, posTop - r1);
|
|
||||||
CGPathAddArc(upper, NULL, r1, posTop - r1, r1, M_PI, M_PI_2, YES);
|
|
||||||
CGPathAddArc(upper, NULL, posNameTag, posTop - r2, r2, M_PI_2, M_PI_4, YES);
|
|
||||||
CGPathAddArc(upper, NULL, posNameTag + 1.85 * r2, posTop, r2, M_PI + M_PI_4, -M_PI_2, NO);
|
|
||||||
CGPathAddArc(upper, NULL, size - r1, posTop - r1 - r2, r1, M_PI_2, 0, YES);
|
|
||||||
CGPathAddArc(upper, NULL, size - r1, posBottom, r1, 0, -M_PI_2, YES);
|
|
||||||
CGPathAddArc(upper, NULL, r1, posBottom, r1, -M_PI_2, M_PI, YES);
|
|
||||||
CGPathCloseSubpath(upper);
|
|
||||||
|
|
||||||
CGMutablePathRef lower = CGPathCreateMutable();
|
|
||||||
CGPathAddArc(lower, NULL, r3, posMiddle, r3, M_PI, M_PI_2, YES);
|
|
||||||
CGPathAddArc(lower, NULL, size - r3, posMiddle, r3, M_PI_2, 0, YES);
|
|
||||||
CGPathAddArc(lower, NULL, size - r1, posBottom, r1, 0, -M_PI_2, YES);
|
|
||||||
CGPathAddArc(lower, NULL, r1, posBottom, r1, -M_PI_2, M_PI, YES);
|
|
||||||
CGPathCloseSubpath(lower);
|
|
||||||
|
|
||||||
CGContextAddPath(c, upper);
|
|
||||||
if (showBackground)
|
|
||||||
CGContextEOFillPath(c);
|
|
||||||
CGContextAddPath(c, lower);
|
|
||||||
CGPathRelease(upper);
|
|
||||||
CGPathRelease(lower);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Create @c CGPath for RSS icon; a circle in the lower left bottom and two radio waves going outwards.
|
|
||||||
@param connection If @c NO, draw only one radio wave and a pause icon in the upper right
|
|
||||||
*/
|
|
||||||
static inline void AddRSSIconPath(CGContextRef c, CGFloat size, BOOL connection) {
|
|
||||||
CGMutablePathRef bars = CGPathCreateMutable(); // the rss bars
|
|
||||||
PathAddCircle(bars, size * 0.125);
|
|
||||||
PathAddRSSArc(bars, size * 0.45, size * 0.2);
|
|
||||||
if (connection) {
|
|
||||||
PathAddRSSArc(bars, size * 0.8, size * 0.2);
|
|
||||||
} else {
|
|
||||||
CGAffineTransform at = CGAffineTransformMake(0.5, 0, 0, 0.5, size/2, size/2);
|
|
||||||
PathAddPauseIcon(bars, at, size, size * 0.3);
|
|
||||||
//PathAddXIcon(bars, at, size, size * 0.3);
|
|
||||||
}
|
|
||||||
CGContextAddPath(c, bars);
|
|
||||||
CGPathRelease(bars);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -168,130 +60,214 @@ static void DrawGradient(CGContextRef c, CGFloat size, NSColor *color) {
|
|||||||
CFArrayRef colors = CFArrayCreate(NULL, cgColors, 3, NULL);
|
CFArrayRef colors = CFArrayCreate(NULL, cgColors, 3, NULL);
|
||||||
CGGradientRef gradient = CGGradientCreateWithColors(NULL, colors, NULL);
|
CGGradientRef gradient = CGGradientCreateWithColors(NULL, colors, NULL);
|
||||||
|
|
||||||
CGContextDrawLinearGradient(c, gradient, CGPointMake(0, size), CGPointMake(size, 0), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
|
CGContextDrawLinearGradient(c, gradient, CGPointMake(0, 0), CGPointMake(size, size), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
|
||||||
CGGradientRelease(gradient);
|
CGGradientRelease(gradient);
|
||||||
CFRelease(colors);
|
CFRelease(colors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - CGContext Drawing & Manipulation
|
#pragma mark - RSS Icon (rounded corners)
|
||||||
|
|
||||||
|
|
||||||
/// Flip coordinate system
|
/**
|
||||||
static void FlipCoordinateSystem(CGContextRef c, CGFloat height) {
|
Create @c CGPath for RSS icon; a circle in the lower left bottom and two radio waves going outwards.
|
||||||
CGContextTranslateCTM(c, 0, height);
|
@param connection If @c NO, draw only one radio wave and a pause icon in the upper right
|
||||||
CGContextScaleCTM(c, 1, -1);
|
*/
|
||||||
}
|
static inline void AddRSSIconPath(CGContextRef c, CGFloat size, BOOL connection) {
|
||||||
|
svgCircle(c, size/100, 13, 87, 13, NO);
|
||||||
/// Scale and translate context to the center with respect to the new scale. If @c width @c != @c length align top left.
|
svgPath(c, size/100, "M0,35q65,0,65,65h-20q0,-45,-45,-45z");
|
||||||
static void SetContentScale(CGContextRef c, CGSize size, CGFloat scale) {
|
if (connection) {
|
||||||
const CGFloat s = ShorterSide(size);
|
svgPath(c, size/100, "M0,0q100,0,100,100h-20q0,-80,-80,-80z");
|
||||||
CGFloat offset = s * (1 - scale) / 2;
|
} else {
|
||||||
CGContextTranslateCTM(c, offset, size.height - s + offset); // top left alignment
|
// pause icon
|
||||||
CGContextScaleCTM(c, scale, scale);
|
svgRect(c, size/100, CGRectMake(60, 0, 15, 50));
|
||||||
}
|
svgRect(c, size/100, CGRectMake(85, 0, 15, 50));
|
||||||
|
|
||||||
/// Helper method; set drawing color, add rounded background and prepare content scale
|
|
||||||
static void DrawRoundedFrame(CGContextRef c, CGRect r, CGColorRef color, BOOL background, CGFloat corner, CGFloat defaultScale, CGFloat scaling) {
|
|
||||||
CGContextSetFillColorWithColor(c, color);
|
|
||||||
CGContextSetStrokeColorWithColor(c, color);
|
|
||||||
CGFloat contentScale = defaultScale;
|
|
||||||
if (background) {
|
|
||||||
svgAddRect(c, 1, r, ShorterSide(r.size) * corner/2);
|
|
||||||
if (scaling != 0.0)
|
|
||||||
contentScale *= scaling;
|
|
||||||
}
|
}
|
||||||
SetContentScale(c, r.size, contentScale);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Draw monochrome RSS icon with rounded corners
|
||||||
#pragma mark - Easy Icon Drawing Methods
|
static void RoundedRSS_Monochrome(CGRect r, BOOL connection) {
|
||||||
|
|
||||||
|
|
||||||
/// Draw global icon (menu bar)
|
|
||||||
static void DrawGlobalIcon(CGRect r, CGColorRef color, BOOL background) {
|
|
||||||
CGContextRef c = NSGraphicsContext.currentContext.CGContext;
|
|
||||||
DrawRoundedFrame(c, r, color, background, 0.4, 1.0, 0.7);
|
|
||||||
AddGlobalIconPath(c, ShorterSide(r.size));
|
|
||||||
CGContextEOFillPath(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draw group icon (folder)
|
|
||||||
static void DrawGroupIcon(CGRect r, CGColorRef color, BOOL background) {
|
|
||||||
CGContextRef c = NSGraphicsContext.currentContext.CGContext;
|
|
||||||
const CGFloat s = ShorterSide(r.size);
|
|
||||||
const CGFloat l = s * 0.08; // line width
|
|
||||||
DrawRoundedFrame(c, r, color, background, 0.4, 1.0 - (l / s), 0.85);
|
|
||||||
CGContextSetLineWidth(c, l * (background ? 0.5 : 1.0));
|
|
||||||
AddGroupIconPath(c, s, background);
|
|
||||||
CGContextStrokePath(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draw RSS icon (flat without gradient)
|
|
||||||
static void DrawRSSIcon(CGRect r, CGColorRef color, BOOL background, BOOL connection) {
|
|
||||||
CGContextRef c = NSGraphicsContext.currentContext.CGContext;
|
|
||||||
DrawRoundedFrame(c, r, color, background, 0.4, 1.0, 0.7);
|
|
||||||
AddRSSIconPath(c, ShorterSide(r.size), connection);
|
|
||||||
CGContextEOFillPath(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draw RSS icon (with orange gradient, corner @c 0.4, white radio waves)
|
|
||||||
static void DrawRSSGradientIcon(CGRect r, NSColor *color) {
|
|
||||||
const CGFloat size = ShorterSide(r.size);
|
const CGFloat size = ShorterSide(r.size);
|
||||||
CGContextRef c = NSGraphicsContext.currentContext.CGContext;
|
CGContextRef c = NSGraphicsContext.currentContext.CGContext;
|
||||||
DrawRoundedFrame(c, r, NSColor.whiteColor.CGColor, YES, 0.4, 1.0, 0.7);
|
CGContextSetFillColorWithColor(c, [NSColor menuBarIconColor].CGColor);
|
||||||
|
// background rounded rect
|
||||||
|
svgRoundedRect(c, 1, r, size * 0.4/2);
|
||||||
|
// RSS icon
|
||||||
|
SetContentScale(c, r.size, 11/16.0);
|
||||||
|
AddRSSIconPath(c, size, connection);
|
||||||
|
CGContextEOFillPath(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw RSS icon with orange gradient background
|
||||||
|
static void RoundedRSS_Gradient(CGRect r, NSColor *color) {
|
||||||
|
const CGFloat size = ShorterSide(r.size);
|
||||||
|
CGContextRef c = NSGraphicsContext.currentContext.CGContext;
|
||||||
|
CGContextSetFillColorWithColor(c, NSColor.whiteColor.CGColor);
|
||||||
|
// background rounded rect
|
||||||
|
svgRoundedRect(c, 1, r, size * 0.4/2);
|
||||||
// Gradient
|
// Gradient
|
||||||
CGContextSaveGState(c);
|
CGContextSaveGState(c);
|
||||||
CGContextClip(c);
|
CGContextClip(c);
|
||||||
DrawGradient(c, size, color);
|
DrawGradient(c, size, color);
|
||||||
CGContextRestoreGState(c);
|
CGContextRestoreGState(c);
|
||||||
// Bars
|
// RSS icon
|
||||||
|
SetContentScale(c, r.size, 11/16.0);
|
||||||
AddRSSIconPath(c, size, YES);
|
AddRSSIconPath(c, size, YES);
|
||||||
CGContextEOFillPath(c);
|
CGContextEOFillPath(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark - Appearance Settings
|
||||||
|
|
||||||
|
|
||||||
|
/// Draw icon representing global `status bar icon` (rounded RSS icon with neighbor items)
|
||||||
|
static void Appearance_MenuBarIcon(CGRect r) {
|
||||||
|
const CGFloat size = ShorterSide(r.size);
|
||||||
|
CGContextRef c = NSGraphicsContext.currentContext.CGContext;
|
||||||
|
CGContextSetFillColorWithColor(c, [NSColor controlTextColor].CGColor);
|
||||||
|
|
||||||
|
// menu bar
|
||||||
|
CGContextSetAlpha(c, .23);
|
||||||
|
const CGFloat barHeightInset = round(size*.06);
|
||||||
|
svgRect(c, 1, CGRectInset(r, 0, barHeightInset));
|
||||||
|
CGContextFillPath(c);
|
||||||
|
|
||||||
|
const CGFloat offset = round(size*.75);
|
||||||
|
const CGFloat iconInset = round(size*.2);
|
||||||
|
const CGFloat iconCorner = size*.12;
|
||||||
|
CGContextSetAlpha(c, .66);
|
||||||
|
|
||||||
|
// left neighbor
|
||||||
|
CGContextTranslateCTM(c, -offset, 0);
|
||||||
|
svgRoundedRect(c, 1, CGRectInset(r, iconInset, iconInset), iconCorner);
|
||||||
|
CGContextFillPath(c);
|
||||||
|
|
||||||
|
// right neighbor
|
||||||
|
CGContextTranslateCTM(c, +2*offset, 0);
|
||||||
|
svgRoundedRect(c, 1, CGRectInset(r, iconInset, iconInset), iconCorner);
|
||||||
|
CGContextFillPath(c);
|
||||||
|
|
||||||
|
// main icon
|
||||||
|
CGContextSetAlpha(c, 1);
|
||||||
|
CGContextTranslateCTM(c, -offset, 0);
|
||||||
|
svgRoundedRect(c, 1, CGRectInset(r, iconInset, iconInset), iconCorner);
|
||||||
|
SetContentScale(c, r.size, 7/16.0);
|
||||||
|
AddRSSIconPath(c, size, YES);
|
||||||
|
CGContextEOFillPath(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw icon representing `Main Menu` (menu bar)
|
||||||
|
static void Appearance_MainMenu(CGRect r) {
|
||||||
|
const CGFloat size = ShorterSide(r.size);
|
||||||
|
CGContextRef c = NSGraphicsContext.currentContext.CGContext;
|
||||||
|
CGContextSetFillColorWithColor(c, [NSColor controlTextColor].CGColor);
|
||||||
|
// menu
|
||||||
|
svgRect(c, size/16, CGRectMake(0, 0, 16, 3));
|
||||||
|
svgRect(c, size/16, CGRectMake(5, 4, 9, 12));
|
||||||
|
svgRect(c, size/16, CGRectMake(6, 3, 7, 12));
|
||||||
|
// entries
|
||||||
|
svgRect(c, size/16, CGRectMake(6, 12, 6, 1));
|
||||||
|
svgRect(c, size/16, CGRectMake(6, 9, 6, 1));
|
||||||
|
svgRect(c, size/16, CGRectMake(6, 6, 6, 1));
|
||||||
|
CGContextEOFillPath(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw icon representing `FeedGroup` (folder)
|
||||||
|
static void Appearance_Group(CGRect r, BOOL withLine) {
|
||||||
|
const CGFloat size = ShorterSide(r.size);
|
||||||
|
CGContextRef c = NSGraphicsContext.currentContext.CGContext;
|
||||||
|
// folder path
|
||||||
|
svgPath(c, size/16, "M3,13.5c-1.5,0-2.5-1-2.5-2.5V3.5c0-1.5.5-2,2-2h1.5c1.5,0,1.5,1,3,1h6c1.5,0,2.5,1,2.5,2.5v6c0,1.5-1,2.5-2.5,2.5H3Z");
|
||||||
|
// line
|
||||||
|
if (withLine) {
|
||||||
|
svgPath(c, size/16, "M1.5,5h13Z");
|
||||||
|
}
|
||||||
|
CGContextSetLineWidth(c, size * 1/16);
|
||||||
|
CGContextSetStrokeColorWithColor(c, [NSColor controlTextColor].CGColor);
|
||||||
|
CGContextStrokePath(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw icon representing `Feed` (group + RSS)
|
||||||
|
static void Appearance_Feed(CGRect r) {
|
||||||
|
const CGFloat size = ShorterSide(r.size);
|
||||||
|
CGContextRef c = NSGraphicsContext.currentContext.CGContext;
|
||||||
|
CGContextSetFillColorWithColor(c, [NSColor controlTextColor].CGColor);
|
||||||
|
// folder
|
||||||
|
Appearance_Group(r, NO);
|
||||||
|
// rss icon
|
||||||
|
SetContentScale(c, r.size, 7/16.0);
|
||||||
|
AddRSSIconPath(c, size, YES);
|
||||||
|
CGContextFillPath(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw icon representing `Article` (RSS inside text document)
|
||||||
|
static void Appearance_Article(CGRect r) {
|
||||||
|
const CGFloat size = ShorterSide(r.size);
|
||||||
|
CGContextRef c = NSGraphicsContext.currentContext.CGContext;
|
||||||
|
CGContextSetFillColorWithColor(c, [NSColor controlTextColor].CGColor);
|
||||||
|
// text lines
|
||||||
|
svgRect(c, size/16, CGRectMake(0, 14, 16, 1));
|
||||||
|
svgRect(c, size/16, CGRectMake(0, 10, 16, 1));
|
||||||
|
svgRect(c, size/16, CGRectMake(9, 6, 7, 1));
|
||||||
|
svgRect(c, size/16, CGRectMake(9, 2, 7, 1));
|
||||||
|
// picture
|
||||||
|
//svgRect(c, size/16, CGRectMake(1, 1, 7, 7));
|
||||||
|
// RSS icon
|
||||||
|
CGContextTranslateCTM(c, size/16 * 1, size/16 * 1); // same offset as picture
|
||||||
|
CGContextScaleCTM(c, 7/16.0, 7/16.0); // same size as picture
|
||||||
|
AddRSSIconPath(c, size, YES);
|
||||||
|
CGContextEOFillPath(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark - Other Icons
|
||||||
|
|
||||||
|
|
||||||
/// Draw unread icon (blue dot for unread menu item)
|
/// Draw unread icon (blue dot for unread menu item)
|
||||||
static void DrawUnreadIcon(CGRect r, NSColor *color) {
|
static void DrawUnreadIcon(CGRect r, NSColor *color) {
|
||||||
CGFloat size = ShorterSide(r.size) / 2.0;
|
const CGFloat radius = ShorterSide(r.size) / 2.0;
|
||||||
CGContextRef c = NSGraphicsContext.currentContext.CGContext;
|
CGContextRef c = NSGraphicsContext.currentContext.CGContext;
|
||||||
CGMutablePathRef path = CGPathCreateMutable();
|
CGMutablePathRef path = CGPathCreateMutable();
|
||||||
SetContentScale(c, r.size, 0.7);
|
SetContentScale(c, r.size, 0.7);
|
||||||
CGContextTranslateCTM(c, 0, size * -0.15); // align with baseline of menu item text
|
CGContextTranslateCTM(c, 0, radius * -0.15); // align with baseline of menu item text
|
||||||
|
|
||||||
|
// outer ring (opaque)
|
||||||
CGContextSetFillColorWithColor(c, color.CGColor);
|
CGContextSetFillColorWithColor(c, color.CGColor);
|
||||||
PathAddRing(path, size, size * 0.7);
|
CGPathAddArc(path, NULL, radius, radius, radius, 0, M_PI * 2, YES);
|
||||||
|
CGPathAddArc(path, NULL, radius, radius, radius*.7, 0, M_PI * -2, YES);
|
||||||
CGContextAddPath(c, path);
|
CGContextAddPath(c, path);
|
||||||
CGContextEOFillPath(c);
|
CGContextEOFillPath(c);
|
||||||
|
|
||||||
|
// inner circle (translucent)
|
||||||
CGContextSetFillColorWithColor(c, [color colorWithAlphaComponent:0.5].CGColor);
|
CGContextSetFillColorWithColor(c, [color colorWithAlphaComponent:0.5].CGColor);
|
||||||
PathAddCircle(path, size);
|
CGPathAddArc(path, NULL, radius, radius, radius, 0, M_PI * 2, YES);
|
||||||
CGContextAddPath(c, path);
|
CGContextAddPath(c, path);
|
||||||
CGContextFillPath(c);
|
CGContextFillPath(c);
|
||||||
CGPathRelease(path);
|
CGPathRelease(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw "(.*)" as vector path
|
/// Draw `(.*)` as vector path
|
||||||
static void DrawRegexIcon(CGRect r) {
|
static void DrawRegexIcon(CGRect r) {
|
||||||
const CGFloat size = ShorterSide(r.size);
|
const CGFloat size = ShorterSide(r.size);
|
||||||
CGContextRef c = NSGraphicsContext.currentContext.CGContext;
|
CGContextRef c = NSGraphicsContext.currentContext.CGContext;
|
||||||
|
|
||||||
svgAddRect(c, 1, r, .2 * size);
|
// background
|
||||||
CGContextSetFillColorWithColor(c, NSColor.redColor.CGColor);
|
CGContextSetFillColorWithColor(c, NSColor.redColor.CGColor);
|
||||||
|
svgRoundedRect(c, 1, r, size * 0.4/2);
|
||||||
CGContextFillPath(c);
|
CGContextFillPath(c);
|
||||||
|
|
||||||
// SVG files use bottom-left corner coordinate system. Quartz uses top-left.
|
// foreground
|
||||||
FlipCoordinateSystem(c, r.size.height);
|
|
||||||
SetContentScale(c, r.size, 0.8);
|
|
||||||
// "("
|
|
||||||
svgAddPath(c, size/1000, "m184 187c-140 205-134 432-1 622l-66 44c-159-221-151-499 0-708z");
|
|
||||||
// "."
|
|
||||||
svgAddCircle(c, size/1000, 315, 675, 70, NO);
|
|
||||||
// "*"
|
|
||||||
svgAddPath(c, size/1000, "m652 277 107-35 21 63-109 36 68 92-54 39-68-93-66 91-52-41 67-88-109-37 21-63 108 37v-113h66v112z");
|
|
||||||
// ")"
|
|
||||||
svgAddPath(c, size/1000, "m816 813c140-205 134-430 1-621l66-45c159 221 151 499 0 708z");
|
|
||||||
|
|
||||||
CGContextSetFillColorWithColor(c, NSColor.whiteColor.CGColor);
|
CGContextSetFillColorWithColor(c, NSColor.whiteColor.CGColor);
|
||||||
|
SetContentScale(c, r.size, 25/32.0);
|
||||||
|
// "("
|
||||||
|
svgPath(c, size/100, "M18,19c-14,21-13,43,0,62l-7,4C-4,63-4,35,12,14l6,5Z");
|
||||||
|
// "."
|
||||||
|
svgCircle(c, size/100, 31, 67, 7, NO);
|
||||||
|
// "*"
|
||||||
|
svgPath(c, size/100, "M65,28l11-4,2,6-11,4,7,9-5,4-7-9-7,9-5-4,7-9-11-4,2-6,11,4v-11h6v11Z");
|
||||||
|
// ")"
|
||||||
|
svgPath(c, size/100, "M82,81c14-21,13-43,0-62l7-5c16,22,15,50,0,71l-7-4Z");
|
||||||
CGContextFillPath(c);
|
CGContextFillPath(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,19 +277,25 @@ static void DrawRegexIcon(CGRect r) {
|
|||||||
|
|
||||||
/// Add single image to @c ImageNamed cache and set accessibility description
|
/// Add single image to @c ImageNamed cache and set accessibility description
|
||||||
static void Register(CGFloat size, NSImageName name, NSString *description, BOOL (^draw)(NSRect r)) {
|
static void Register(CGFloat size, NSImageName name, NSString *description, BOOL (^draw)(NSRect r)) {
|
||||||
NSImage *img = [NSImage imageWithSize: NSMakeSize(size, size) flipped:NO drawingHandler:draw];
|
NSImage *img = [NSImage imageWithSize: NSMakeSize(size, size) flipped:YES drawingHandler:draw];
|
||||||
img.accessibilityDescription = description;
|
img.accessibilityDescription = description;
|
||||||
img.name = name;
|
img.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register all icons that require custom drawing in @c ImageNamed cache
|
/// Register all icons that require custom drawing in @c ImageNamed cache
|
||||||
void RegisterImageViewNames(void) {
|
void RegisterImageViewNames(void) {
|
||||||
Register(16, RSSImageDefaultRSSIcon, NSLocalizedString(@"RSS icon", nil), ^(NSRect r) { DrawRSSGradientIcon(r, [NSColor rssOrange]); return YES; });
|
// Default feed icon (fallback icon if no favicon found)
|
||||||
Register(16, RSSImageSettingsGlobal, NSLocalizedString(@"Global settings", nil), ^(NSRect r) { DrawGlobalIcon(r, [NSColor controlTextColor].CGColor, NO); return YES; });
|
Register(16, RSSImageDefaultRSSIcon, NSLocalizedString(@"Default feed icon", nil), ^(NSRect r) { RoundedRSS_Gradient(r, [NSColor rssOrange]); return YES; });
|
||||||
Register(16, RSSImageSettingsGroup, NSLocalizedString(@"Group settings", nil), ^(NSRect r) { DrawGroupIcon(r, [NSColor controlTextColor].CGColor, NO); return YES; });
|
// Menu bar icon
|
||||||
Register(16, RSSImageSettingsFeed, NSLocalizedString(@"Feed settings", nil), ^(NSRect r) { DrawRSSIcon(r, [NSColor controlTextColor].CGColor, NO, YES); return YES; });
|
Register(16, RSSImageMenuBarIconActive, NSLocalizedString(@"Menu bar icon", nil), ^(NSRect r) { RoundedRSS_Monochrome(r, YES); return YES; });
|
||||||
Register(16, RSSImageMenuBarIconActive, NSLocalizedString(@"RSS menu bar icon", nil), ^(NSRect r) { DrawRSSIcon(r, [NSColor menuBarIconColor].CGColor, YES, YES); return YES; });
|
Register(16, RSSImageMenuBarIconPaused, NSLocalizedString(@"Menu bar icon, paused", nil), ^(NSRect r) { RoundedRSS_Monochrome(r, NO); return YES; });
|
||||||
Register(16, RSSImageMenuBarIconPaused, NSLocalizedString(@"RSS menu bar icon, paused", nil), ^(NSRect r) { DrawRSSIcon(r, [NSColor menuBarIconColor].CGColor, YES, NO); return YES; });
|
// Appearance settings
|
||||||
Register(14, RSSImageMenuItemUnread, NSLocalizedString(@"Unread icon", nil), ^(NSRect r) { DrawUnreadIcon(r, [NSColor unreadIndicatorColor]); return YES; });
|
Register(16, RSSImageSettingsGlobalIcon, NSLocalizedString(@"Global settings, menu bar icon", nil), ^(NSRect r) { Appearance_MenuBarIcon(r); return YES; });
|
||||||
|
Register(16, RSSImageSettingsGlobalMenu, NSLocalizedString(@"Global settings, main menu", nil), ^(NSRect r) { Appearance_MainMenu(r); return YES; });
|
||||||
|
Register(16, RSSImageSettingsGroup, NSLocalizedString(@"Group settings", nil), ^(NSRect r) { Appearance_Group(r, YES); return YES; });
|
||||||
|
Register(16, RSSImageSettingsFeed, NSLocalizedString(@"Feed settings", nil), ^(NSRect r) { Appearance_Feed(r); return YES; });
|
||||||
|
Register(16, RSSImageSettingsArticle, NSLocalizedString(@"Article settings", nil), ^(NSRect r) { Appearance_Article(r); return YES; });
|
||||||
|
// Other settings
|
||||||
|
Register(14, RSSImageMenuItemUnread, NSLocalizedString(@"Unread indicator", nil), ^(NSRect r) { DrawUnreadIcon(r, [NSColor unreadIndicatorColor]); return YES; });
|
||||||
Register(32, RSSImageRegexIcon, NSLocalizedString(@"Regex icon", nil), ^(NSRect r) { DrawRegexIcon(r); return YES; });
|
Register(32, RSSImageRegexIcon, NSLocalizedString(@"Regex icon", nil), ^(NSRect r) { DrawRegexIcon(r); return YES; });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
@import Cocoa;
|
@import Cocoa;
|
||||||
|
|
||||||
void svgAddPath(CGContextRef context, CGFloat scale, const char * path);
|
void svgPath(CGContextRef context, CGFloat scale, const char * path);
|
||||||
void svgAddCircle(CGContextRef context, CGFloat scale, CGFloat x, CGFloat y, CGFloat radius, bool clockwise);
|
void svgCircle(CGContextRef context, CGFloat scale, CGFloat x, CGFloat y, CGFloat radius, bool clockwise);
|
||||||
void svgAddRect(CGContextRef context, CGFloat scale, CGRect rect, CGFloat cornerRadius);
|
void svgRoundedRect(CGContextRef context, CGFloat scale, CGRect rect, CGFloat cornerRadius);
|
||||||
|
void svgRect(CGContextRef context, CGFloat scale, CGRect rect);
|
||||||
|
|||||||
@@ -64,10 +64,16 @@ static void finishOp(CGMutablePathRef path, struct SVGState *state) {
|
|||||||
state->y = state->num[1];
|
state->y = state->num[1];
|
||||||
CGPathAddLineToPoint(path, NULL, state->x * state->scale, state->y * state->scale);
|
CGPathAddLineToPoint(path, NULL, state->x * state->scale, state->y * state->scale);
|
||||||
|
|
||||||
|
} else if (op == 'Q' && state->iNum == 4) {
|
||||||
|
state->x = state->num[2];
|
||||||
|
state->y = state->num[3];
|
||||||
|
CGPathAddQuadCurveToPoint(path, NULL, state->num[0] * state->scale, state->num[1] * state->scale, state->x * state->scale, state->y * state->scale);
|
||||||
|
|
||||||
} else if (op == 'C' && state->iNum == 6) {
|
} else if (op == 'C' && state->iNum == 6) {
|
||||||
state->x = state->num[4];
|
state->x = state->num[4];
|
||||||
state->y = state->num[5];
|
state->y = state->num[5];
|
||||||
CGPathAddCurveToPoint(path, NULL, state->num[0] * state->scale, state->num[1] * state->scale, state->num[2] * state->scale, state->num[3] * state->scale, state->x * state->scale, state->y * state->scale);
|
CGPathAddCurveToPoint(path, NULL, state->num[0] * state->scale, state->num[1] * state->scale, state->num[2] * state->scale, state->num[3] * state->scale, state->x * state->scale, state->y * state->scale);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
NSLog(@"Unsupported SVG operation %c %d", state->op, state->iNum);
|
NSLog(@"Unsupported SVG operation %c %d", state->op, state->iNum);
|
||||||
}
|
}
|
||||||
@@ -124,6 +130,8 @@ static void tinySVG_parse(const char * code, CGFloat scale, CGMutablePathRef pat
|
|||||||
finishOp(path, &state);
|
finishOp(path, &state);
|
||||||
} else if (state.iNum == 2 && strchr("MmLl", state.op) != NULL) {
|
} else if (state.iNum == 2 && strchr("MmLl", state.op) != NULL) {
|
||||||
finishOp(path, &state);
|
finishOp(path, &state);
|
||||||
|
} else if (state.iNum == 4 && strchr("Qq", state.op) != NULL) {
|
||||||
|
finishOp(path, &state);
|
||||||
} else if (state.iNum == 6 && strchr("Cc", state.op) != NULL) {
|
} else if (state.iNum == 6 && strchr("Cc", state.op) != NULL) {
|
||||||
finishOp(path, &state);
|
finishOp(path, &state);
|
||||||
}
|
}
|
||||||
@@ -138,11 +146,17 @@ static void tinySVG_parse(const char * code, CGFloat scale, CGMutablePathRef pat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper method to scale `rect` according to svg size.
|
||||||
|
static inline CGRect scaledRect(CGRect rect, CGFloat scale) {
|
||||||
|
if (scale == 1.0) { return rect; }
|
||||||
|
return CGRectMake(rect.origin.x * scale, rect.origin.y * scale, rect.size.width * scale, rect.size.height * scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# pragma mark - External API
|
# pragma mark - External API
|
||||||
|
|
||||||
/// calls @c tinySVG_path and handles @c CGPath creation and release.
|
/// calls @c tinySVG_path and handles @c CGPath creation and release.
|
||||||
void svgAddPath(CGContextRef context, CGFloat scale, const char * code) {
|
void svgPath(CGContextRef context, CGFloat scale, const char * code) {
|
||||||
CGMutablePathRef path = CGPathCreateMutable();
|
CGMutablePathRef path = CGPathCreateMutable();
|
||||||
tinySVG_parse(code, scale, path);
|
tinySVG_parse(code, scale, path);
|
||||||
CGContextAddPath(context, path);
|
CGContextAddPath(context, path);
|
||||||
@@ -150,22 +164,24 @@ void svgAddPath(CGContextRef context, CGFloat scale, const char * code) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// calls @c CGPathAddArc with full circle
|
/// calls @c CGPathAddArc with full circle
|
||||||
void svgAddCircle(CGContextRef context, CGFloat scale, CGFloat x, CGFloat y, CGFloat radius, bool clockwise) {
|
void svgCircle(CGContextRef context, CGFloat scale, CGFloat x, CGFloat y, CGFloat radius, bool clockwise) {
|
||||||
|
// No `CGContextAddArc` because that doesnt work well with overlapping counter-clockwise
|
||||||
CGMutablePathRef tmp = CGPathCreateMutable();
|
CGMutablePathRef tmp = CGPathCreateMutable();
|
||||||
CGPathAddArc(tmp, NULL, x * scale, y * scale, radius * scale, 0, M_PI * 2, clockwise);
|
CGPathAddArc(tmp, NULL, x * scale, y * scale, radius * scale, 0, M_PI * 2, clockwise);
|
||||||
CGContextAddPath(context, tmp);
|
CGContextAddPath(context, tmp);
|
||||||
CGPathRelease(tmp);
|
CGPathRelease(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls @c CGContextAddRect or @c CGPathAddRoundedRect (optional).
|
/// Calls @c CGPathAddRoundedRect
|
||||||
/// @param cornerRadius Use @c <=0 for no corners. Use half of @c min(w,h) for a full circle.
|
/// @param cornerRadius Use half of @c min(w,h) for a full circle.
|
||||||
void svgAddRect(CGContextRef context, CGFloat scale, CGRect rect, CGFloat cornerRadius) {
|
void svgRoundedRect(CGContextRef context, CGFloat scale, CGRect rect, CGFloat cornerRadius) {
|
||||||
if (cornerRadius > 0) {
|
|
||||||
CGMutablePathRef tmp = CGPathCreateMutable();
|
CGMutablePathRef tmp = CGPathCreateMutable();
|
||||||
CGPathAddRoundedRect(tmp, NULL, rect, cornerRadius * scale, cornerRadius * scale);
|
CGPathAddRoundedRect(tmp, NULL, scaledRect(rect, scale), cornerRadius * scale, cornerRadius * scale);
|
||||||
CGContextAddPath(context, tmp);
|
CGContextAddPath(context, tmp);
|
||||||
CGPathRelease(tmp);
|
CGPathRelease(tmp);
|
||||||
} else {
|
}
|
||||||
CGContextAddRect(context, rect);
|
|
||||||
}
|
/// Calls @c CGContextAddRect
|
||||||
|
void svgRect(CGContextRef context, CGFloat scale, CGRect rect) {
|
||||||
|
CGContextAddRect(context, scaledRect(rect, scale));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
// ------ Hidden preferences ------ only modifiable via `defaults write de.relikd.baRSS {KEY}` ------
|
// ------ Hidden preferences ------ only modifiable via `defaults write de.relikd.baRSS {KEY}` ------
|
||||||
/** default: @c 10 */ static NSString* const Pref_openFewLinksLimit = @"openFewLinksLimit";
|
/** default: @c 10 */ static NSString* const Pref_openFewLinksLimit = @"openFewLinksLimit";
|
||||||
/** default: @c 60 */ static NSString* const Pref_shortArticleNamesLimit = @"shortArticleNamesLimit";
|
/** default: @c 60 */ static NSString* const Pref_shortArticleNamesLimit = @"shortArticleNamesLimit";
|
||||||
|
/** default: @c 2k */ static NSString* const Pref_tooltipCharacterLimit = @"tooltipCharacterLimit";
|
||||||
/** default: @c 40 */ static NSString* const Pref_articlesInMenuLimit = @"articlesInMenuLimit";
|
/** default: @c 40 */ static NSString* const Pref_articlesInMenuLimit = @"articlesInMenuLimit";
|
||||||
/** default: @c nil */ static NSString* const Pref_colorStatusIconTint = @"colorStatusIconTint";
|
/** default: @c nil */ static NSString* const Pref_colorStatusIconTint = @"colorStatusIconTint";
|
||||||
/** default: @c nil */ static NSString* const Pref_colorUnreadIndicator = @"colorUnreadIndicator";
|
/** default: @c nil */ static NSString* const Pref_colorUnreadIndicator = @"colorUnreadIndicator";
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ void UserPrefsInit(void) {
|
|||||||
// Display limits & truncation ( defaults write de.relikd.baRSS {KEY} -int 10 )
|
// Display limits & truncation ( defaults write de.relikd.baRSS {KEY} -int 10 )
|
||||||
[defs setObject:[NSNumber numberWithUnsignedInteger:10] forKey:Pref_openFewLinksLimit];
|
[defs setObject:[NSNumber numberWithUnsignedInteger:10] forKey:Pref_openFewLinksLimit];
|
||||||
[defs setObject:[NSNumber numberWithUnsignedInteger:60] forKey:Pref_shortArticleNamesLimit];
|
[defs setObject:[NSNumber numberWithUnsignedInteger:60] forKey:Pref_shortArticleNamesLimit];
|
||||||
|
[defs setObject:[NSNumber numberWithUnsignedInteger:2000] forKey:Pref_tooltipCharacterLimit];
|
||||||
[defs setObject:[NSNumber numberWithUnsignedInteger:40] forKey:Pref_articlesInMenuLimit];
|
[defs setObject:[NSNumber numberWithUnsignedInteger:40] forKey:Pref_articlesInMenuLimit];
|
||||||
[defs setObject:[NSNumber numberWithUnsignedInteger:1] forKey:Pref_prefSelectedTab]; // feed tab
|
[defs setObject:[NSNumber numberWithUnsignedInteger:1] forKey:Pref_prefSelectedTab]; // feed tab
|
||||||
[[NSUserDefaults standardUserDefaults] registerDefaults:defs];
|
[[NSUserDefaults standardUserDefaults] registerDefaults:defs];
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
- (instancetype)init {
|
- (instancetype)init {
|
||||||
self = [super initWithFrame:NSMakeRect(0, 0, 320, 327)];
|
self = [super initWithFrame:NSMakeRect(0, 0, 320, 327)];
|
||||||
// Insert matrix header (icons above checkbox matrix)
|
// Insert matrix header (icons above checkbox matrix)
|
||||||
ColumnIcon(self, X__, RSSImageSettingsGlobal);
|
ColumnIcon(self, X__, RSSImageSettingsGlobalMenu);
|
||||||
ColumnIcon(self, _X_, RSSImageSettingsGroup);
|
ColumnIcon(self, _X_, RSSImageSettingsGroup);
|
||||||
ColumnIcon(self, __X, RSSImageSettingsFeed);
|
ColumnIcon(self, __X, RSSImageSettingsFeed);
|
||||||
// Generate checkbox matrix
|
// Generate checkbox matrix
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
c2:nil c2tt:nil
|
c2:nil c2tt:nil
|
||||||
c3:nil c3tt:nil];
|
c3:nil c3tt:nil];
|
||||||
|
|
||||||
[self entry:NSLocalizedString(@"Toggle “Show Hidden Articles”", nil)
|
[self entry:NSLocalizedString(@"Toggle “Show hidden articles”", nil)
|
||||||
help:NSLocalizedString(@"Show button in main menu to quickly toggle whether hidden articles should be shown. See option “Show only unread”.", nil)
|
help:NSLocalizedString(@"Show button in main menu to quickly toggle whether hidden articles should be shown. See option “Show only unread”.", nil)
|
||||||
tip:nil
|
tip:nil
|
||||||
c1:Pref_globalToggleHidden c1tt:NSLocalizedString(@"in main menu", nil)
|
c1:Pref_globalToggleHidden c1tt:NSLocalizedString(@"in main menu", nil)
|
||||||
@@ -132,7 +132,7 @@ static inline NSButton* Checkbox(id this, CGFloat x, CGFloat y, NSString *key) {
|
|||||||
if (pref3) [Checkbox(self, __X + 2, y + 2, pref3) tooltip:ttip3].accessibilityLabel = [label stringByAppendingString:@" (feed)"];
|
if (pref3) [Checkbox(self, __X + 2, y + 2, pref3) tooltip:ttip3].accessibilityLabel = [label stringByAppendingString:@" (feed)"];
|
||||||
if (extraTip != nil) {
|
if (extraTip != nil) {
|
||||||
label = [label stringByAppendingString:@" *"];
|
label = [label stringByAppendingString:@" *"];
|
||||||
ttip = [ttip stringByAppendingFormat:@"\n\nTip: %@", extraTip];
|
ttip = [ttip stringByAppendingFormat:@"\n\n* Tip: %@", extraTip];
|
||||||
}
|
}
|
||||||
return [[[[NSView label:label] placeIn:self x:PAD_WIN + 3 * colWidth yTop:y] sizeToRight:PAD_WIN] tooltip:ttip];
|
return [[[[NSView label:label] placeIn:self x:PAD_WIN + 3 * colWidth yTop:y] sizeToRight:PAD_WIN] tooltip:ttip];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,14 +176,14 @@
|
|||||||
|
|
||||||
- (void)insertMainMenuHeader:(NSMenu*)menu {
|
- (void)insertMainMenuHeader:(NSMenu*)menu {
|
||||||
// 'Pause Updates' item
|
// 'Pause Updates' item
|
||||||
NSMenuItem *pause = [menu addItemWithTitle:NSLocalizedString(@"Pause Updates", nil) action:@selector(pauseUpdates) keyEquivalent:@""];
|
NSMenuItem *pause = [menu addItemWithTitle:NSLocalizedString(@"Pause updates", nil) action:@selector(pauseUpdates) keyEquivalent:@""];
|
||||||
pause.target = self;
|
pause.target = self;
|
||||||
if ([UpdateScheduler isPaused])
|
if ([UpdateScheduler isPaused])
|
||||||
pause.title = NSLocalizedString(@"Resume Updates", nil);
|
pause.title = NSLocalizedString(@"Resume updates", nil);
|
||||||
|
|
||||||
// 'show hidden articles' item
|
// 'show hidden articles' item
|
||||||
if (UserPrefsBool(Pref_globalToggleHidden)) {
|
if (UserPrefsBool(Pref_globalToggleHidden)) {
|
||||||
NSMenuItem *toggleHidden = [menu addItemWithTitle:NSLocalizedString(@"Show Hidden Articles", nil) action:@selector(toggleHiddenArticles) keyEquivalent:@"h"];
|
NSMenuItem *toggleHidden = [menu addItemWithTitle:NSLocalizedString(@"Show hidden articles", nil) action:@selector(toggleHiddenArticles) keyEquivalent:@"h"];
|
||||||
toggleHidden.target = self;
|
toggleHidden.target = self;
|
||||||
toggleHidden.enabled = !self.holdingOptKey && (UserPrefsBool(Pref_groupUnreadOnly) || UserPrefsBool(Pref_feedUnreadOnly));
|
toggleHidden.enabled = !self.holdingOptKey && (UserPrefsBool(Pref_groupUnreadOnly) || UserPrefsBool(Pref_feedUnreadOnly));
|
||||||
[toggleHidden setState:self.barMenu.showHidden ? NSControlStateValueOn : NSControlStateValueOff];
|
[toggleHidden setState:self.barMenu.showHidden ? NSControlStateValueOn : NSControlStateValueOff];
|
||||||
|
|||||||
Reference in New Issue
Block a user