fix(tray): rebuild Linux tray icon as monochrome multi-size pixmaps (#362)
The previous tray-light.png / tray-dark.png were 128x128 RGBA glyphs with a black radial background. GNOME's StatusNotifierItem path expects monochrome silhouettes around 22px base size and was rendering ours as the "three dots" fallback. Regenerate from icons/icon-symbolic.svg using Inkscape with a corrected square viewBox (149 1 944 944, centred on the path bbox) and emit four sizes per theme (22/32/44/64). decode_tray_icons() now returns Vec<Icon> so ksni can offer the panel the best match for its bar height and HiDPI. cc
No files matched your search
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg"
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="50 60 1100 1100"
|
viewBox="149 1 944 944"
|
||||||
width="1100"
|
width="944"
|
||||||
height="1100"
|
height="944"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
stroke="currentColor">
|
stroke="currentColor">
|
||||||
<g transform="translate(4.763702,7.069182)">
|
<g transform="translate(4.763702,7.069182)">
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 598 B |
|
After Width: | Height: | Size: 851 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 602 B |
|
After Width: | Height: | Size: 838 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 11 KiB |
@@ -22,8 +22,18 @@ use ksni::{
|
|||||||
};
|
};
|
||||||
use tauri::{AppHandle, Emitter, Manager};
|
use tauri::{AppHandle, Emitter, Manager};
|
||||||
|
|
||||||
const TRAY_ICON_DARK_PNG: &[u8] = include_bytes!("../icons/tray-dark.png");
|
// Multiple pixmap sizes per StatusNotifierItem spec — panels pick the best
|
||||||
const TRAY_ICON_LIGHT_PNG: &[u8] = include_bytes!("../icons/tray-light.png");
|
// match for their bar height (22 = base, 44/64 = HiDPI). All are monochromatic
|
||||||
|
// silhouettes of the qbz glyph: black on transparent for light panels, white
|
||||||
|
// for dark panels. Generated from icons/icon-symbolic.svg via Inkscape.
|
||||||
|
const TRAY_ICON_LIGHT_22: &[u8] = include_bytes!("../icons/tray-light-22.png");
|
||||||
|
const TRAY_ICON_LIGHT_32: &[u8] = include_bytes!("../icons/tray-light-32.png");
|
||||||
|
const TRAY_ICON_LIGHT_44: &[u8] = include_bytes!("../icons/tray-light-44.png");
|
||||||
|
const TRAY_ICON_LIGHT_64: &[u8] = include_bytes!("../icons/tray-light-64.png");
|
||||||
|
const TRAY_ICON_DARK_22: &[u8] = include_bytes!("../icons/tray-dark-22.png");
|
||||||
|
const TRAY_ICON_DARK_32: &[u8] = include_bytes!("../icons/tray-dark-32.png");
|
||||||
|
const TRAY_ICON_DARK_44: &[u8] = include_bytes!("../icons/tray-dark-44.png");
|
||||||
|
const TRAY_ICON_DARK_64: &[u8] = include_bytes!("../icons/tray-dark-64.png");
|
||||||
|
|
||||||
fn is_flatpak() -> bool {
|
fn is_flatpak() -> bool {
|
||||||
std::env::var("FLATPAK_ID").is_ok() || std::path::Path::new("/.flatpak-info").exists()
|
std::env::var("FLATPAK_ID").is_ok() || std::path::Path::new("/.flatpak-info").exists()
|
||||||
@@ -66,8 +76,7 @@ fn prefer_dark_tray() -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convert an embedded RGBA PNG to the ARGB32 big-endian layout ksni expects.
|
/// Convert an embedded RGBA PNG to the ARGB32 big-endian layout ksni expects.
|
||||||
fn decode_tray_icon() -> Result<Icon, String> {
|
fn decode_pixmap(bytes: &[u8]) -> Result<Icon, String> {
|
||||||
let bytes = if prefer_dark_tray() { TRAY_ICON_DARK_PNG } else { TRAY_ICON_LIGHT_PNG };
|
|
||||||
let img = image::load_from_memory(bytes)
|
let img = image::load_from_memory(bytes)
|
||||||
.map_err(|e| format!("decode tray icon: {e}"))?;
|
.map_err(|e| format!("decode tray icon: {e}"))?;
|
||||||
let (width, height) = img.dimensions();
|
let (width, height) = img.dimensions();
|
||||||
@@ -85,9 +94,21 @@ fn decode_tray_icon() -> Result<Icon, String> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decode all monochromatic pixmap sizes for the active theme. Panels pick the
|
||||||
|
/// best size from the list; supplying 22/32/44/64 covers standard SNI bar
|
||||||
|
/// heights and HiDPI variants.
|
||||||
|
fn decode_tray_icons() -> Result<Vec<Icon>, String> {
|
||||||
|
let sources: [&[u8]; 4] = if prefer_dark_tray() {
|
||||||
|
[TRAY_ICON_DARK_22, TRAY_ICON_DARK_32, TRAY_ICON_DARK_44, TRAY_ICON_DARK_64]
|
||||||
|
} else {
|
||||||
|
[TRAY_ICON_LIGHT_22, TRAY_ICON_LIGHT_32, TRAY_ICON_LIGHT_44, TRAY_ICON_LIGHT_64]
|
||||||
|
};
|
||||||
|
sources.iter().map(|b| decode_pixmap(b)).collect()
|
||||||
|
}
|
||||||
|
|
||||||
struct QbzTray {
|
struct QbzTray {
|
||||||
app: AppHandle,
|
app: AppHandle,
|
||||||
icon: Icon,
|
icons: Vec<Icon>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl QbzTray {
|
impl QbzTray {
|
||||||
@@ -132,7 +153,7 @@ impl Tray for QbzTray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn icon_pixmap(&self) -> Vec<Icon> {
|
fn icon_pixmap(&self) -> Vec<Icon> {
|
||||||
vec![self.icon.clone()]
|
self.icons.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tool_tip(&self) -> ToolTip {
|
fn tool_tip(&self) -> ToolTip {
|
||||||
@@ -206,10 +227,10 @@ impl Tray for QbzTray {
|
|||||||
pub fn init(app: &AppHandle) -> Result<(), Box<dyn std::error::Error>> {
|
pub fn init(app: &AppHandle) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
log::info!("Initializing ksni tray (Linux, SNI primary-activate enabled)");
|
log::info!("Initializing ksni tray (Linux, SNI primary-activate enabled)");
|
||||||
|
|
||||||
let icon = decode_tray_icon()?;
|
let icons = decode_tray_icons()?;
|
||||||
let tray = QbzTray {
|
let tray = QbzTray {
|
||||||
app: app.clone(),
|
app: app.clone(),
|
||||||
icon,
|
icons,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Flatpak requires disabling the well-known DBus name because the sandbox
|
// Flatpak requires disabling the well-known DBus name because the sandbox
|
||||||
|
|||||||