Skip to content

Commit 1142d12

Browse files
committed
no-bug: Make macOS app icon changes available from settings
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
1 parent 18c15ac commit 1142d12

14 files changed

Lines changed: 249 additions & 8 deletions

File tree

locales/en-US/browser/browser/preferences/zen-preferences.ftl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ zen-look-and-feel-compact-view-top-toolbar =
4848
zen-look-and-feel-compact-toolbar-flash-popup =
4949
.label = Briefly make the toolbar popup when switching or opening new tabs in compact mode
5050
51+
zen-look-and-feel-app-icon-header = App Icon
52+
zen-look-and-feel-app-icon-description = Change the icon used by the running app on macOS
53+
zen-look-and-feel-app-icon-label = Variant
54+
zen-look-and-feel-app-icon-default =
55+
.label = Default
56+
zen-look-and-feel-app-icon-alternate =
57+
.label = Alternative
58+
5159
pane-zen-tabs-title = Tab Management
5260
category-zen-workspaces =
5361
.tooltiptext = { pane-zen-tabs-title }
@@ -357,4 +365,4 @@ zen-devtools-toggle-dom-shortcut = Toggle DOM
357365
zen-devtools-toggle-accessibility-shortcut = Toggle Accessibility
358366
zen-close-all-unpinned-tabs-shortcut = Close All Unpinned Tabs
359367
zen-new-unsynced-window-shortcut = New Blank Window
360-
zen-duplicate-tab-shortcut = Duplicate Tab
368+
zen-duplicate-tab-shortcut = Duplicate Tab

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
},
88
"type": "module",
99
"scripts": {
10-
"build": "surfer build",
10+
"build": "npm run sync:l10n:en-US && surfer build",
1111
"build:ui": "surfer build --ui",
1212
"start": "cd engine && python3 ./mach run --noprofile",
13-
"import": "npm run ffprefs && npm run import:dumps && surfer import",
13+
"import": "npm run ffprefs && npm run import:dumps && npm run sync:l10n:en-US && surfer import",
1414
"import:dumps": "python3 scripts/update_service_dumps.py",
1515
"export": "surfer export",
1616
"init": "npm run download && npm run import && npm run bootstrap",
@@ -21,6 +21,7 @@
2121
"sync:raw": "surfer update",
2222
"sync:rc": "python3 scripts/update_ff.py --rc",
2323
"sync:l10n": "python3 scripts/update_ff.py --just-l10n",
24+
"sync:l10n:en-US": "python3 scripts/update_en_US_packs.py",
2425
"lint": "cd engine && ./mach lint zen",
2526
"lint:fix": "npm run lint -- --fix",
2627
"reset-ff": "surfer reset",

src/browser/components/preferences/zen-settings.js

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,11 @@ var gZenMarketplaceManager = {
644644

645645
const kZenExtendedSidebar = "zen.view.sidebar-expanded";
646646
const kZenSingleToolbar = "zen.view.use-single-toolbar";
647+
const kZenMacOSAppIconVariant = "zen.widget.macos.app-icon-variant";
648+
const kZenMacOSAppIconPreviewSrcByVariant = {
649+
default: "chrome://branding/content/icon48.png",
650+
alternate: "chrome://browser/content/zen-images/app-icons/alternate.png",
651+
};
647652

648653
var gZenLooksAndFeel = {
649654
init() {
@@ -652,18 +657,24 @@ var gZenLooksAndFeel = {
652657
}
653658
this.__hasInitialized = true;
654659
gZenMarketplaceManager.init();
655-
for (const pref of [kZenExtendedSidebar, kZenSingleToolbar]) {
660+
for (const pref of [kZenExtendedSidebar, kZenSingleToolbar, kZenMacOSAppIconVariant]) {
656661
Services.prefs.addObserver(pref, this);
657662
}
658663
window.addEventListener("unload", () => {
659-
for (const pref of [kZenExtendedSidebar, kZenSingleToolbar]) {
664+
for (const pref of [kZenExtendedSidebar, kZenSingleToolbar, kZenMacOSAppIconVariant]) {
660665
Services.prefs.removeObserver(pref, this);
661666
}
662667
});
663668
this.applySidebarLayout();
669+
this.applyMacOSAppIconPreview();
664670
},
665671

666-
observe() {
672+
observe(_subject, _topic, data) {
673+
if (data == kZenMacOSAppIconVariant) {
674+
this.applyMacOSAppIconPreview();
675+
return;
676+
}
677+
667678
this.applySidebarLayout();
668679
},
669680

@@ -708,6 +719,20 @@ var gZenLooksAndFeel = {
708719
});
709720
}
710721
},
722+
723+
applyMacOSAppIconPreview() {
724+
const preview = document.getElementById("zenLooksAndFeelMacOSAppIconPreview");
725+
if (!preview) {
726+
return;
727+
}
728+
729+
const variant = Services.prefs.getStringPref(kZenMacOSAppIconVariant, "default");
730+
preview.setAttribute(
731+
"src",
732+
kZenMacOSAppIconPreviewSrcByVariant[variant] ??
733+
kZenMacOSAppIconPreviewSrcByVariant.default
734+
);
735+
},
711736
};
712737

713738
var gZenWorkspacesSettings = {
@@ -1135,6 +1160,11 @@ Preferences.addAll([
11351160
type: "bool",
11361161
default: true,
11371162
},
1163+
{
1164+
id: "zen.widget.macos.app-icon-variant",
1165+
type: "string",
1166+
default: "default",
1167+
},
11381168
{
11391169
id: "zen.workspaces.hide-default-container-indicator",
11401170
type: "bool",

src/browser/components/preferences/zenLooksAndFeel.inc.xhtml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,31 @@
6161
data-l10n-id="zen-look-and-feel-compact-toolbar-flash-popup"
6262
preference="zen.view.compact.toolbar-flash-popup"/>
6363
</vbox>
64+
65+
#ifdef XP_MACOSX
66+
<label><html:h2 data-l10n-id="zen-look-and-feel-app-icon-header"/></label>
67+
<description class="description-deemphasized" data-l10n-id="zen-look-and-feel-app-icon-description" />
68+
69+
<hbox id="zenMacOSAppIconSetting" align="center">
70+
<html:div class="zenAppIconPreviewFrame">
71+
<html:img id="zenLooksAndFeelMacOSAppIconPreview"
72+
src="chrome://branding/content/icon48.png" />
73+
</html:div>
74+
75+
<vbox flex="1">
76+
<label data-l10n-id="zen-look-and-feel-app-icon-label"/>
77+
<menulist id="zenLooksAndFeelMacOSAppIconVariant"
78+
preference="zen.widget.macos.app-icon-variant">
79+
<menupopup>
80+
<menuitem data-l10n-id="zen-look-and-feel-app-icon-default"
81+
value="default"/>
82+
<menuitem data-l10n-id="zen-look-and-feel-app-icon-alternate"
83+
value="alternate"/>
84+
</menupopup>
85+
</menulist>
86+
</vbox>
87+
</hbox>
88+
#endif
6489
</groupbox>
6590

6691
<hbox id="zenGlanceCategory"

src/browser/themes/shared/preferences/zen-preferences.css

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,31 @@ groupbox h2 {
9797
margin-top: 15px;
9898
}
9999

100+
#zenMacOSAppIconSetting {
101+
align-items: center;
102+
gap: 16px;
103+
margin-block: 10px 14px;
104+
}
105+
106+
.zenAppIconPreviewFrame {
107+
align-items: center;
108+
background: color-mix(in srgb, var(--zen-colors-tertiary) 70%, transparent);
109+
border: 1px solid var(--zen-colors-border);
110+
border-radius: 18px;
111+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
112+
display: flex;
113+
height: 80px;
114+
justify-content: center;
115+
min-width: 80px;
116+
width: 80px;
117+
}
118+
119+
#zenLooksAndFeelMacOSAppIconPreview {
120+
border-radius: 18px;
121+
height: 64px;
122+
width: 64px;
123+
}
124+
100125
.zen-compact-mode-styles-browser-wrapper {
101126
width: 55%;
102127
height: 60px;

src/zen/@types/lib.gecko.xpcom.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19519,6 +19519,7 @@ declare global {
1951919519
): void;
1952019520
canShare(): boolean;
1952119521
playHapticFeedback(): void;
19522+
setMacOSAppIconData(dataURL: string): void;
1952219523
}
1952319524

1952419525
// https://searchfox.org/mozilla-central/source/zen/drag-and-drop/nsIZenDragAndDrop.idl

src/zen/common/modules/ZenStartup.mjs

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,23 @@ import checkForZenUpdates, {
66
createWindowUpdateAnimation,
77
} from "chrome://browser/content/ZenUpdates.mjs";
88

9+
const ZEN_MACOS_APP_ICON_VARIANT_PREF = "zen.widget.macos.app-icon-variant";
10+
const ZEN_MACOS_APP_ICON_URLS = {
11+
alternate: "chrome://browser/content/zen-images/app-icons/alternate.png",
12+
};
13+
914
class ZenStartup {
1015
#watermarkIgnoreElements = ["zen-toast-container"];
1116
#hasInitializedLayout = false;
17+
#macOSAppIconDataURLs = new Map();
1218

1319
isReady = false;
1420
promiseInitialized = new Promise(resolve => {
1521
this.promiseInitializedResolve = resolve;
1622
});
1723

1824
init() {
25+
this.#initMacOSAppIcon();
1926
this.openWatermark();
2027
this.#zenInitBrowserLayout();
2128
}
@@ -69,15 +76,84 @@ class ZenStartup {
6976
}
7077
}
7178

72-
observe(aSubject, aTopic) {
79+
observe(aSubject, aTopic, aData) {
7380
// This nsIObserver method allows us to defer initialization until after
7481
// this window has finished painting and starting up.
7582
if (aTopic == "browser-delayed-startup-finished" && aSubject == window) {
7683
Services.obs.removeObserver(this, "browser-delayed-startup-finished");
7784
this.delayedStartupFinished();
85+
return;
86+
}
87+
88+
if (aTopic == "nsPref:changed") {
89+
if (aData == ZEN_MACOS_APP_ICON_VARIANT_PREF) {
90+
void this.#applyMacOSAppIconVariant();
91+
}
7892
}
7993
}
8094

95+
#initMacOSAppIcon() {
96+
if (Services.appinfo.OS != "Darwin") {
97+
return;
98+
}
99+
100+
Services.prefs.addObserver(ZEN_MACOS_APP_ICON_VARIANT_PREF, this);
101+
window.addEventListener(
102+
"unload",
103+
() => {
104+
Services.prefs.removeObserver(ZEN_MACOS_APP_ICON_VARIANT_PREF, this);
105+
},
106+
{ once: true }
107+
);
108+
109+
void this.#applyMacOSAppIconVariant();
110+
}
111+
112+
async #applyMacOSAppIconVariant() {
113+
const variant = Services.prefs.getStringPref(
114+
ZEN_MACOS_APP_ICON_VARIANT_PREF,
115+
"default"
116+
);
117+
118+
if (variant == "default") {
119+
Services.zen.setMacOSAppIconData("");
120+
return;
121+
}
122+
123+
const dataUrl = await this.#getMacOSAppIconDataUrl(variant);
124+
Services.zen.setMacOSAppIconData(dataUrl);
125+
}
126+
127+
async #getMacOSAppIconDataUrl(variant) {
128+
const iconUrl = ZEN_MACOS_APP_ICON_URLS[variant];
129+
if (!iconUrl) {
130+
return "";
131+
}
132+
133+
let dataUrlPromise = this.#macOSAppIconDataURLs.get(variant);
134+
if (!dataUrlPromise) {
135+
dataUrlPromise = this.#readImageAsDataUrl(iconUrl).catch(error => {
136+
console.error("ZenStartup: failed to load macOS app icon variant", error);
137+
this.#macOSAppIconDataURLs.delete(variant);
138+
return "";
139+
});
140+
this.#macOSAppIconDataURLs.set(variant, dataUrlPromise);
141+
}
142+
143+
return dataUrlPromise;
144+
}
145+
146+
async #readImageAsDataUrl(url) {
147+
const response = await fetch(url);
148+
const blob = await response.blob();
149+
return await new Promise((resolve, reject) => {
150+
const reader = new FileReader();
151+
reader.addEventListener("loadend", () => resolve(reader.result || ""));
152+
reader.addEventListener("error", () => reject(reader.error));
153+
reader.readAsDataURL(blob);
154+
});
155+
}
156+
81157
delayedStartupFinished() {
82158
gZenWorkspaces.promiseInitialized.then(async () => {
83159
await delayedStartupPromise;
48.3 KB
Loading

src/zen/images/jar.inc.mn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
content/browser/zen-images/layouts/collapsed.png (../../zen/images/layouts/collapsed.png)
66
content/browser/zen-images/layouts/multiple-toolbar.png (../../zen/images/layouts/multiple-toolbar.png)
77
content/browser/zen-images/layouts/single-toolbar.png (../../zen/images/layouts/single-toolbar.png)
8+
content/browser/zen-images/app-icons/alternate.png (../../zen/images/app-icons/alternate.png)
89
content/browser/zen-images/grain-bg.png (../../zen/images/grain-bg.png)
910
content/browser/zen-images/note-indicator.svg (../../zen/images/note-indicator.svg)
1011

src/zen/toolkit/common/ZenCommonUtils.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ ZenCommonUtils::PlayHapticFeedback() {
6969
return PlayHapticFeedbackInternal();
7070
}
7171

72+
NS_IMETHODIMP
73+
ZenCommonUtils::SetMacOSAppIconData(const nsACString& aDataUrl) {
74+
return SetMacOSAppIconDataInternal(aDataUrl);
75+
}
76+
7277
NS_IMETHODIMP
7378
ZenCommonUtils::CanShare(bool* canShare) {
7479
auto aWindow = GetMostRecentWindow();

0 commit comments

Comments
 (0)