gh-15005: Improve feel of theme colors (gh-15004)

This commit is contained in:
mr. m
2026-08-17 23:28:23 +02:00
committed by GitHub
parent 67acee0791
commit 9f2771aff6
7 changed files with 69 additions and 45 deletions

View File

@@ -106,3 +106,6 @@
- name: browser.referrals.enabled
value: false
locked: true
- name: browser.shell.customIcon.enabled
value: false # For now?

View File

@@ -21,7 +21,7 @@
value: 100
- name: zen.workspaces.switch-animation-duration
value: 200
value: 250
- name: zen.workspaces.wrap-around-navigation
value: true

View File

@@ -494,39 +494,49 @@ class nsZenMediaController {
window.addEventListener("TabClose", onTabDiscardedOrClosed);
window.addEventListener("TabBrowserDiscarded", onTabDiscardedOrClosed);
window.addEventListener("DOMAudioPlaybackStarted", event => {
const browser = event.target;
// The card is created right away (while the controller is fresh)
// but only shown if the media is still playing a moment later, so
// short sounds (e.g. notification pings) never reveal it.
setTimeout(() => {
const card = this.#cardForBrowser(browser);
if (!card || card.isSharing) {
return;
}
if (card.controller.isPlaying) {
card.refreshVisibility();
} else if (card.element.hidden) {
// The sound was over before ever being shown (e.g. a
// notification ping): drop the card instead of keeping a ghost
// around.
card.destroy();
}
}, 1000);
this.activateMediaControls(
browser.browsingContext.mediaController,
browser
);
});
window.addEventListener("DOMAudioPlaybackStopped", () => {
for (const card of this.#cards.values()) {
card.updateMuteState();
window.addEventListener("TabAttrModified", event => {
if (!event.detail.changed.includes("soundplaying")) {
return;
}
const tab = event.target;
if (!tab.hasAttribute("soundplaying")) {
this.onAudioPlaybackStopped();
return;
}
this.onAudioPlaybackStarted(tab.linkedBrowser);
});
}
onAudioPlaybackStarted(browser) {
// The card is created right away (while the controller is fresh) but
// only shown if the media is still playing a moment later, so short
// sounds (e.g. notification pings) never reveal it.
setTimeout(() => {
const card = this.#cardForBrowser(browser);
if (!card || card.isSharing) {
return;
}
if (card.controller.isPlaying) {
card.refreshVisibility();
} else if (card.element.hidden) {
// The sound was over before ever being shown (e.g. a notification
// ping): drop the card instead of keeping a ghost around.
card.destroy();
}
}, 1000);
this.activateMediaControls(
browser.browsingContext?.mediaController,
browser
);
}
onAudioPlaybackStopped() {
for (const card of this.#cards.values()) {
card.updateMuteState();
}
}
get frontCard() {
return this.#orderedCards.find(card => !card.element.hidden) ?? null;
}
@@ -660,7 +670,7 @@ class nsZenMediaController {
this.#cards.set(key, card);
if (controller) {
// Media cards start hidden and appear via the delayed check in
// DOMAudioPlaybackStarted or on tab switch, like the original bar;
// onAudioPlaybackStarted or on tab switch, like the original bar;
// sharing cards show right away.
card.element.hidden = true;
} else {

View File

@@ -138,6 +138,9 @@
width: 100%;
padding: 4px 6px;
border-radius: var(--border-radius-medium);
@media (-moz-platform: macos) {
border-radius: calc(var(--border-radius-medium) - 2px);
}
outline: var(--zen-sidebar-notification-outline);
outline-offset: -1.5px;
box-shadow: var(--zen-sidebar-notification-shadow);

View File

@@ -542,22 +542,22 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature {
const centerX = rect.width / 2;
const centerY = rect.height / 2;
const radius = (rect.width - padding) / 2;
const distance = Math.sqrt((x - centerX) ** 2 + (y - centerY) ** 2);
const distance = Math.sqrt((x - centerX) ** 2 + (y - centerY) ** 2) - 0.2;
let angle = Math.atan2(y - centerY, x - centerX);
angle = (angle * 180) / Math.PI; // Convert to degrees
angle = (angle * 180) / Math.PI;
if (angle < 0) {
angle += 360; // Normalize to [0, 360)
angle += 360;
}
const normalizedDistance = 1 - Math.min(distance / radius, 1); // Normalize distance to [0, 1]
let hue = (angle / 360) * 360; // Normalize angle to [0, 360)
let saturation = normalizedDistance * 100; // stays high even in center
const normalizedDistance = 1 - Math.min(distance / radius, 1);
let hue = (angle / 360) * 360;
let saturation = normalizedDistance * 100;
if (type !== EXPLICIT_LIGHTNESS_TYPE) {
saturation = 90 + (1 - normalizedDistance) * 10;
// Set the current lightness to how far we are from the center of the circle
// For example, moving the dot outside will have higher lightness, while moving it inside will have lower lightness
this.#currentLightness = Math.round((1 - normalizedDistance) * 100);
}
let lightness = this.#currentLightness; // Fixed lightness for simplicity
let lightness = this.#currentLightness;
if (type === EXPLICIT_BLACKWHITE_TYPE) {
// We can only get grayscales from white to black
saturation = 0;
@@ -1259,10 +1259,10 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature {
let colorToBlendOpacity;
if (this.isMica) {
colorToBlend = this.isDarkMode ? [0, 0, 0] : [255, 255, 255];
colorToBlendOpacity = 0.12;
colorToBlendOpacity = 0.3;
} else if (AppConstants.platform === "macosx") {
colorToBlend = [255, 255, 255];
colorToBlendOpacity = 0.18;
colorToBlendOpacity = 0.6;
}
if (colorToBlend) {
const blendedAlpha = Math.min(
@@ -1495,9 +1495,17 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature {
return color;
}
getToolbarColor(isDarkMode = false) {
getToolbarColor(isDarkMode = false, accentColor = undefined) {
const opacity = 0.8;
let baseColor = isDarkMode ? [255, 255, 255, opacity] : [0, 0, 0, opacity]; // Default toolbar
if (accentColor && this.canBeTransparent) {
// Blend a bit with the accent color to make it more visible
baseColor = this.blendColors(
accentColor,
baseColor.slice(0, 3),
10
).concat(opacity);
}
return baseColor;
}
@@ -1763,7 +1771,7 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature {
docElement.style.setProperty("--zen-primary-color", primaryColor);
// Set `--toolbox-textcolor` to have a contrast with the primary color
let textColor = this.getToolbarColor(isDarkMode);
let textColor = this.getToolbarColor(isDarkMode, dominantColor);
docElement.style.setProperty(
"--toolbox-textcolor",
`rgba(${textColor[0]}, ${textColor[1]}, ${textColor[2]}, ${textColor[3]})`
@@ -2009,7 +2017,7 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature {
grain: theme.texture ?? 0,
isDarkMode,
isExplicitMode,
toolbarColor: this.getToolbarColor(isDarkMode),
toolbarColor: this.getToolbarColor(isDarkMode, dominantColor),
primaryColor: this.getAccentColorForUI(dominantColor, isDarkMode),
};
this.currentOpacity = previousOpacity;

View File

@@ -321,7 +321,7 @@ zen-workspace {
position: absolute;
height: 100%;
overflow: hidden;
color: color-mix(in srgb, var(--toolbox-textcolor) 95%, var(--zen-primary-color));
color: var(--toolbox-textcolor);
--tab-background-color-selected: color-mix(in srgb, light-dark(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.18)) 95%, var(--zen-primary-color)) !important;
--tab-box-shadow-selected: 0 0.8px 1.5px 0px light-dark(rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0.05)) !important;

View File

@@ -30,7 +30,7 @@ async function playVideoIn(tab) {
}
);
// Wait for the browser to actually consider the tab "playing" — this is
// what drives DOMAudioPlaybackStarted into the media controller.
// what drives the soundplaying TabAttrModified into the media controller.
await BrowserTestUtils.waitForCondition(
() => tab.soundPlaying,
"tab reports soundplaying"