From a23c66e4e1d1ac8ff3f8829632da09183bcc9429 Mon Sep 17 00:00:00 2001 From: Alfred Xing Date: Sun, 23 Aug 2020 14:39:29 -0700 Subject: [PATCH] feat: allow monospaced font styles to be specified for macOS tray titles (#25059) * feat: add optional font type to macOS tray title * test: add tests for tray font type * docs: update API reference for Tray setTitle * review: change API to use an options object * review: fix string enum in docs Co-authored-by: Samuel Attard * review: return after throwing errors * review: don't need thrower anymore now that we have args Co-authored-by: Samuel Attard --- docs/api/tray.md | 4 ++- shell/browser/api/electron_api_tray.cc | 26 +++++++++++++-- shell/browser/api/electron_api_tray.h | 4 ++- shell/browser/ui/tray_icon.h | 7 +++- shell/browser/ui/tray_icon_cocoa.h | 2 +- shell/browser/ui/tray_icon_cocoa.mm | 45 ++++++++++++++++++++++---- spec-main/api-tray-spec.ts | 31 ++++++++++++++++++ 7 files changed, 106 insertions(+), 13 deletions(-) diff --git a/docs/api/tray.md b/docs/api/tray.md index 98e6d5ac8977..ce63c3e7d578 100644 --- a/docs/api/tray.md +++ b/docs/api/tray.md @@ -217,9 +217,11 @@ Sets the `image` associated with this tray icon when pressed on macOS. Sets the hover text for this tray icon. -#### `tray.setTitle(title)` _macOS_ +#### `tray.setTitle(title[, options])` _macOS_ * `title` String +* `options` Object (optional) + * `fontType` String (optional) - The font family variant to display, can be `monospaced` or `monospacedDigit`. `monospaced` is available in macOS 10.15+ and `monospacedDigit` is available in macOS 10.11+. When left blank, the title uses the default system font. Sets the title displayed next to the tray icon in the status bar (Support ANSI colors). diff --git a/shell/browser/api/electron_api_tray.cc b/shell/browser/api/electron_api_tray.cc index a12508ba4c89..3bb63fa623e1 100644 --- a/shell/browser/api/electron_api_tray.cc +++ b/shell/browser/api/electron_api_tray.cc @@ -212,11 +212,33 @@ void Tray::SetToolTip(const std::string& tool_tip) { tray_icon_->SetToolTip(tool_tip); } -void Tray::SetTitle(const std::string& title) { +void Tray::SetTitle(const std::string& title, + const base::Optional& options, + gin::Arguments* args) { if (!CheckAlive()) return; #if defined(OS_MAC) - tray_icon_->SetTitle(title); + TrayIcon::TitleOptions title_options; + if (options) { + if (options->Get("fontType", &title_options.font_type)) { + // Validate the font type if it's passed in + if (title_options.font_type != "monospaced" && + title_options.font_type != "monospacedDigit") { + args->ThrowTypeError( + "fontType must be one of 'monospaced' or 'monospacedDigit'"); + return; + } + } else if (options->Has("fontType")) { + args->ThrowTypeError( + "fontType must be one of 'monospaced' or 'monospacedDigit'"); + return; + } + } else if (args->Length() >= 2) { + args->ThrowTypeError("setTitle options must be an object"); + return; + } + + tray_icon_->SetTitle(title, title_options); #endif } diff --git a/shell/browser/api/electron_api_tray.h b/shell/browser/api/electron_api_tray.h index b7132f9468c5..1bfacb2fa577 100644 --- a/shell/browser/api/electron_api_tray.h +++ b/shell/browser/api/electron_api_tray.h @@ -86,7 +86,9 @@ class Tray : public gin::Wrappable, void SetImage(gin::Handle image); void SetPressedImage(gin::Handle image); void SetToolTip(const std::string& tool_tip); - void SetTitle(const std::string& title); + void SetTitle(const std::string& title, + const base::Optional& options, + gin::Arguments* args); std::string GetTitle(); void SetIgnoreDoubleClickEvents(bool ignore); bool GetIgnoreDoubleClickEvents(); diff --git a/shell/browser/ui/tray_icon.h b/shell/browser/ui/tray_icon.h index 2a2167688715..947dfc91f564 100644 --- a/shell/browser/ui/tray_icon.h +++ b/shell/browser/ui/tray_icon.h @@ -45,8 +45,13 @@ class TrayIcon { virtual void SetIgnoreDoubleClickEvents(bool ignore) = 0; virtual bool GetIgnoreDoubleClickEvents() = 0; + struct TitleOptions { + std::string font_type; + }; + // Set/Get title displayed next to status icon in the status bar. - virtual void SetTitle(const std::string& title) = 0; + virtual void SetTitle(const std::string& title, + const TitleOptions& options) = 0; virtual std::string GetTitle() = 0; #endif diff --git a/shell/browser/ui/tray_icon_cocoa.h b/shell/browser/ui/tray_icon_cocoa.h index 03989390e6a0..5afe8277c9f7 100644 --- a/shell/browser/ui/tray_icon_cocoa.h +++ b/shell/browser/ui/tray_icon_cocoa.h @@ -25,7 +25,7 @@ class TrayIconCocoa : public TrayIcon { void SetImage(const gfx::Image& image) override; void SetPressedImage(const gfx::Image& image) override; void SetToolTip(const std::string& tool_tip) override; - void SetTitle(const std::string& title) override; + void SetTitle(const std::string& title, const TitleOptions& options) override; std::string GetTitle() override; void SetIgnoreDoubleClickEvents(bool ignore) override; bool GetIgnoreDoubleClickEvents() override; diff --git a/shell/browser/ui/tray_icon_cocoa.mm b/shell/browser/ui/tray_icon_cocoa.mm index 2290459cf6f7..589620efc622 100644 --- a/shell/browser/ui/tray_icon_cocoa.mm +++ b/shell/browser/ui/tray_icon_cocoa.mm @@ -103,14 +103,43 @@ return ignoreDoubleClickEvents_; } -- (void)setTitle:(NSString*)title { +- (void)setTitle:(NSString*)title font_type:(NSString*)font_type { + NSMutableAttributedString* attributed_title = + [[NSMutableAttributedString alloc] initWithString:title]; + if ([title containsANSICodes]) { - [[statusItem_ button] - setAttributedTitle:[title attributedStringParsingANSICodes]]; - } else { - [[statusItem_ button] setTitle:title]; + attributed_title = [title attributedStringParsingANSICodes]; } + // Change font type, if specified + CGFloat existing_size = [[[statusItem_ button] font] pointSize]; + if ([font_type isEqualToString:@"monospaced"]) { + if (@available(macOS 10.15, *)) { + NSDictionary* attributes = @{ + NSFontAttributeName : + [NSFont monospacedSystemFontOfSize:existing_size + weight:NSFontWeightRegular] + }; + [attributed_title + setAttributes:attributes + range:NSMakeRange(0, [attributed_title length])]; + } + } else if ([font_type isEqualToString:@"monospacedDigit"]) { + if (@available(macOS 10.11, *)) { + NSDictionary* attributes = @{ + NSFontAttributeName : + [NSFont monospacedDigitSystemFontOfSize:existing_size + weight:NSFontWeightRegular] + }; + [attributed_title + setAttributes:attributes + range:NSMakeRange(0, [attributed_title length])]; + } + } + + // Set title + [[statusItem_ button] setAttributedTitle:attributed_title]; + // Fix icon margins. if (title.length > 0) { [[statusItem_ button] setImagePosition:NSImageLeft]; @@ -306,8 +335,10 @@ void TrayIconCocoa::SetToolTip(const std::string& tool_tip) { [status_item_view_ setToolTip:base::SysUTF8ToNSString(tool_tip)]; } -void TrayIconCocoa::SetTitle(const std::string& title) { - [status_item_view_ setTitle:base::SysUTF8ToNSString(title)]; +void TrayIconCocoa::SetTitle(const std::string& title, + const TitleOptions& options) { + [status_item_view_ setTitle:base::SysUTF8ToNSString(title) + font_type:base::SysUTF8ToNSString(options.font_type)]; } std::string TrayIconCocoa::GetTitle() { diff --git a/spec-main/api-tray-spec.ts b/spec-main/api-tray-spec.ts index 45d65e32f2d0..de158360ab75 100644 --- a/spec-main/api-tray-spec.ts +++ b/spec-main/api-tray-spec.ts @@ -159,5 +159,36 @@ describe('tray module', () => { expect(newTitle).to.equal(title); }); + + it('can have an options object passed in', () => { + expect(() => { + tray.setTitle('Hello World!', {}); + }).to.not.throw(); + }); + + it('throws when the options parameter is not an object', () => { + expect(() => { + tray.setTitle('Hello World!', 'test' as any); + }).to.throw(/setTitle options must be an object/); + }); + + it('can have a font type option set', () => { + expect(() => { + tray.setTitle('Hello World!', { fontType: 'monospaced' }); + tray.setTitle('Hello World!', { fontType: 'monospacedDigit' }); + }).to.not.throw(); + }); + + it('throws when the font type is specified but is not a string', () => { + expect(() => { + tray.setTitle('Hello World!', { fontType: 5.4 as any }); + }).to.throw(/fontType must be one of 'monospaced' or 'monospacedDigit'/); + }); + + it('throws on invalid font types', () => { + expect(() => { + tray.setTitle('Hello World!', { fontType: 'blep' as any }); + }).to.throw(/fontType must be one of 'monospaced' or 'monospacedDigit'/); + }); }); });