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 <samuel.r.attard@gmail.com>

* review: return after throwing errors

* review: don't need thrower anymore now that we have args

Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
This commit is contained in:
Alfred Xing 2020-08-23 14:39:29 -07:00 committed by GitHub
parent 13751c815e
commit a23c66e4e1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 106 additions and 13 deletions

View file

@ -217,9 +217,11 @@ Sets the `image` associated with this tray icon when pressed on macOS.
Sets the hover text for this tray icon. Sets the hover text for this tray icon.
#### `tray.setTitle(title)` _macOS_ #### `tray.setTitle(title[, options])` _macOS_
* `title` String * `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). Sets the title displayed next to the tray icon in the status bar (Support ANSI colors).

View file

@ -212,11 +212,33 @@ void Tray::SetToolTip(const std::string& tool_tip) {
tray_icon_->SetToolTip(tool_tip); tray_icon_->SetToolTip(tool_tip);
} }
void Tray::SetTitle(const std::string& title) { void Tray::SetTitle(const std::string& title,
const base::Optional<gin_helper::Dictionary>& options,
gin::Arguments* args) {
if (!CheckAlive()) if (!CheckAlive())
return; return;
#if defined(OS_MAC) #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 #endif
} }

View file

@ -86,7 +86,9 @@ class Tray : public gin::Wrappable<Tray>,
void SetImage(gin::Handle<NativeImage> image); void SetImage(gin::Handle<NativeImage> image);
void SetPressedImage(gin::Handle<NativeImage> image); void SetPressedImage(gin::Handle<NativeImage> image);
void SetToolTip(const std::string& tool_tip); void SetToolTip(const std::string& tool_tip);
void SetTitle(const std::string& title); void SetTitle(const std::string& title,
const base::Optional<gin_helper::Dictionary>& options,
gin::Arguments* args);
std::string GetTitle(); std::string GetTitle();
void SetIgnoreDoubleClickEvents(bool ignore); void SetIgnoreDoubleClickEvents(bool ignore);
bool GetIgnoreDoubleClickEvents(); bool GetIgnoreDoubleClickEvents();

View file

@ -45,8 +45,13 @@ class TrayIcon {
virtual void SetIgnoreDoubleClickEvents(bool ignore) = 0; virtual void SetIgnoreDoubleClickEvents(bool ignore) = 0;
virtual bool GetIgnoreDoubleClickEvents() = 0; virtual bool GetIgnoreDoubleClickEvents() = 0;
struct TitleOptions {
std::string font_type;
};
// Set/Get title displayed next to status icon in the status bar. // 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; virtual std::string GetTitle() = 0;
#endif #endif

View file

@ -25,7 +25,7 @@ class TrayIconCocoa : public TrayIcon {
void SetImage(const gfx::Image& image) override; void SetImage(const gfx::Image& image) override;
void SetPressedImage(const gfx::Image& image) override; void SetPressedImage(const gfx::Image& image) override;
void SetToolTip(const std::string& tool_tip) 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; std::string GetTitle() override;
void SetIgnoreDoubleClickEvents(bool ignore) override; void SetIgnoreDoubleClickEvents(bool ignore) override;
bool GetIgnoreDoubleClickEvents() override; bool GetIgnoreDoubleClickEvents() override;

View file

@ -103,14 +103,43 @@
return ignoreDoubleClickEvents_; 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]) { if ([title containsANSICodes]) {
[[statusItem_ button] attributed_title = [title attributedStringParsingANSICodes];
setAttributedTitle:[title attributedStringParsingANSICodes]];
} else {
[[statusItem_ button] setTitle:title];
} }
// 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. // Fix icon margins.
if (title.length > 0) { if (title.length > 0) {
[[statusItem_ button] setImagePosition:NSImageLeft]; [[statusItem_ button] setImagePosition:NSImageLeft];
@ -306,8 +335,10 @@ void TrayIconCocoa::SetToolTip(const std::string& tool_tip) {
[status_item_view_ setToolTip:base::SysUTF8ToNSString(tool_tip)]; [status_item_view_ setToolTip:base::SysUTF8ToNSString(tool_tip)];
} }
void TrayIconCocoa::SetTitle(const std::string& title) { void TrayIconCocoa::SetTitle(const std::string& title,
[status_item_view_ setTitle:base::SysUTF8ToNSString(title)]; const TitleOptions& options) {
[status_item_view_ setTitle:base::SysUTF8ToNSString(title)
font_type:base::SysUTF8ToNSString(options.font_type)];
} }
std::string TrayIconCocoa::GetTitle() { std::string TrayIconCocoa::GetTitle() {

View file

@ -159,5 +159,36 @@ describe('tray module', () => {
expect(newTitle).to.equal(title); 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'/);
});
}); });
}); });