Skip to content

Commit 2974aba

Browse files
committed
Use source logo raster for tray icon
1 parent ab64db2 commit 2974aba

4 files changed

Lines changed: 109 additions & 113 deletions

File tree

Cargo.lock

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/desktop/src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ localityd = { path = "../../../crates/localityd" }
1919
notify = "8"
2020
serde = { version = "1.0", features = ["derive"] }
2121
serde_json = "1.0"
22-
tauri = { version = "2", features = ["macos-private-api", "tray-icon"] }
22+
tauri = { version = "2", features = ["macos-private-api", "tray-icon", "image-png"] }
2323
tauri-plugin-dialog = "2"
2424
tauri-plugin-process = "2"
2525
tauri-plugin-updater = "2"
1.32 KB
Loading

apps/desktop/src-tauri/src/main.rs

Lines changed: 72 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -4208,108 +4208,63 @@ fn tray_icon_should_use_template() -> bool {
42084208
cfg!(target_os = "macos")
42094209
}
42104210

4211-
fn draw_short_logo_mark(rgba: &mut [u8], size: usize, color: [u8; 4], halo: bool) {
4212-
let top = if halo {
4213-
[
4214-
(18.0, 1.8),
4215-
(32.6, 8.8),
4216-
(32.9, 13.4),
4217-
(21.0, 19.3),
4218-
(15.0, 19.3),
4219-
(3.1, 13.4),
4220-
(3.4, 8.8),
4221-
]
4222-
} else {
4223-
[
4224-
(18.0, 3.1),
4225-
(30.9, 9.2),
4226-
(31.1, 12.3),
4227-
(20.4, 17.5),
4228-
(15.6, 17.5),
4229-
(4.9, 12.3),
4230-
(5.1, 9.2),
4231-
]
4232-
};
4233-
let bottom = if halo {
4234-
[
4235-
(14.0, 18.8),
4236-
(22.0, 18.8),
4237-
(33.0, 25.5),
4238-
(32.4, 29.4),
4239-
(21.0, 34.4),
4240-
(15.0, 34.4),
4241-
(3.6, 29.4),
4242-
(3.0, 25.5),
4243-
]
4244-
} else {
4245-
[
4246-
(14.8, 20.3),
4247-
(21.2, 20.3),
4248-
(31.0, 26.1),
4249-
(30.5, 28.6),
4250-
(20.4, 32.7),
4251-
(15.6, 32.7),
4252-
(5.5, 28.6),
4253-
(5.0, 26.1),
4254-
]
4255-
};
4256-
4257-
draw_polygon(rgba, size, &top, color);
4258-
draw_polygon(rgba, size, &bottom, color);
4259-
4260-
let dot_radius = if halo { 2.8 } else { 1.65 };
4261-
for center in [
4262-
(4.6, 15.4),
4263-
(4.6, 18.5),
4264-
(4.6, 21.6),
4265-
(31.4, 15.4),
4266-
(31.4, 18.5),
4267-
(31.4, 21.6),
4268-
] {
4269-
draw_disc(rgba, size, center, dot_radius, color);
4270-
}
4271-
}
4211+
const TRAY_LOGO_MARK_PNG: &[u8] = include_bytes!("../assets/tray-locality-mark.png");
42724212

4273-
fn draw_polygon(rgba: &mut [u8], size: usize, points: &[(f64, f64)], color: [u8; 4]) {
4274-
for y in 0..size {
4275-
for x in 0..size {
4276-
let px = x as f64 + 0.5;
4277-
let py = y as f64 + 0.5;
4278-
let inside = point_in_polygon((px, py), points);
4279-
let distance = points
4280-
.iter()
4281-
.zip(points.iter().cycle().skip(1))
4282-
.take(points.len())
4283-
.map(|(start, end)| distance_to_segment((px, py), *start, *end))
4284-
.fold(f64::INFINITY, f64::min);
4285-
let alpha = if inside {
4286-
(distance + 0.6).clamp(0.0, 1.0)
4287-
} else {
4288-
(0.6 - distance).clamp(0.0, 1.0)
4289-
};
4290-
if alpha > 0.0 {
4291-
blend_pixel(rgba, size, x, y, color, alpha);
4213+
fn draw_short_logo_mark(rgba: &mut [u8], size: usize, color: [u8; 4], halo: bool) {
4214+
let mark = tray_logo_mark_image();
4215+
if halo {
4216+
for y_offset in -1..=1 {
4217+
for x_offset in -1..=1 {
4218+
if x_offset != 0 || y_offset != 0 {
4219+
draw_image_alpha_mask(rgba, size, mark, x_offset, y_offset, color);
4220+
}
42924221
}
42934222
}
42944223
}
4224+
draw_image_alpha_mask(rgba, size, mark, 0, 0, color);
4225+
}
4226+
4227+
fn tray_logo_mark_image() -> &'static Image<'static> {
4228+
static TRAY_LOGO_MARK: OnceLock<Image<'static>> = OnceLock::new();
4229+
TRAY_LOGO_MARK.get_or_init(|| {
4230+
Image::from_bytes(TRAY_LOGO_MARK_PNG).expect("embedded tray logo PNG should decode")
4231+
})
42954232
}
42964233

4297-
fn point_in_polygon(point: (f64, f64), points: &[(f64, f64)]) -> bool {
4298-
let mut inside = false;
4299-
let mut previous = points[points.len() - 1];
4300-
for current in points {
4301-
let crosses = (current.1 > point.1) != (previous.1 > point.1);
4302-
if crosses {
4303-
let x_intersection = (previous.0 - current.0) * (point.1 - current.1)
4304-
/ (previous.1 - current.1)
4305-
+ current.0;
4306-
if point.0 < x_intersection {
4307-
inside = !inside;
4234+
fn draw_image_alpha_mask(
4235+
rgba: &mut [u8],
4236+
size: usize,
4237+
image: &Image<'_>,
4238+
x_offset: i32,
4239+
y_offset: i32,
4240+
color: [u8; 4],
4241+
) {
4242+
let image_width = image.width() as usize;
4243+
let image_height = image.height() as usize;
4244+
let image_rgba = image.rgba();
4245+
for y in 0..image_height {
4246+
let target_y = y as i32 + y_offset;
4247+
if !(0..size as i32).contains(&target_y) {
4248+
continue;
4249+
}
4250+
for x in 0..image_width {
4251+
let target_x = x as i32 + x_offset;
4252+
if !(0..size as i32).contains(&target_x) {
4253+
continue;
4254+
}
4255+
let alpha = image_rgba[(y * image_width + x) * 4 + 3] as f64 / 255.0;
4256+
if alpha > 0.0 {
4257+
blend_pixel(
4258+
rgba,
4259+
size,
4260+
target_x as usize,
4261+
target_y as usize,
4262+
color,
4263+
alpha,
4264+
);
43084265
}
43094266
}
4310-
previous = *current;
43114267
}
4312-
inside
43134268
}
43144269

43154270
fn draw_disc(rgba: &mut [u8], size: usize, center: (f64, f64), radius: f64, color: [u8; 4]) {
@@ -4326,21 +4281,6 @@ fn draw_disc(rgba: &mut [u8], size: usize, center: (f64, f64), radius: f64, colo
43264281
}
43274282
}
43284283

4329-
fn distance_to_segment(point: (f64, f64), start: (f64, f64), end: (f64, f64)) -> f64 {
4330-
let vx = end.0 - start.0;
4331-
let vy = end.1 - start.1;
4332-
let wx = point.0 - start.0;
4333-
let wy = point.1 - start.1;
4334-
let length_squared = vx * vx + vy * vy;
4335-
if length_squared == 0.0 {
4336-
return ((point.0 - start.0).powi(2) + (point.1 - start.1).powi(2)).sqrt();
4337-
}
4338-
4339-
let t = ((wx * vx + wy * vy) / length_squared).clamp(0.0, 1.0);
4340-
let closest = (start.0 + t * vx, start.1 + t * vy);
4341-
((point.0 - closest.0).powi(2) + (point.1 - closest.1).powi(2)).sqrt()
4342-
}
4343-
43444284
fn blend_pixel(rgba: &mut [u8], size: usize, x: usize, y: usize, color: [u8; 4], coverage: f64) {
43454285
let idx = (y * size + x) * 4;
43464286
let src_alpha = (color[3] as f64 / 255.0) * coverage;
@@ -12412,20 +12352,40 @@ mod tests {
1241212352
.chunks_exact(4)
1241312353
.any(|pixel| { pixel[0] < 40 && pixel[1] < 50 && pixel[2] < 70 && pixel[3] > 200 })
1241412354
);
12415-
for (x, y) in [(4, 15), (4, 18), (4, 21), (32, 15), (32, 18), (32, 21)] {
12355+
for (x, y) in [(5, 15), (5, 18), (5, 21), (30, 15), (30, 18), (30, 21)] {
1241612356
let pixel = tray_icon_pixel(&ready, x, y);
1241712357
assert!(
1241812358
pixel[0] < 40 && pixel[1] < 50 && pixel[2] < 70 && pixel[3] > 200,
1241912359
"expected short-logo side dot at ({x}, {y}), got {pixel:?}"
1242012360
);
1242112361
}
12422-
for (x, y) in [(18, 6), (18, 27)] {
12362+
for (x, y) in [(3, 15), (33, 15)] {
1242312363
let pixel = tray_icon_pixel(&ready, x, y);
1242412364
assert!(
12425-
pixel[0] < 40 && pixel[1] < 50 && pixel[2] < 70 && pixel[3] > 200,
12426-
"expected short-logo stacked body at ({x}, {y}), got {pixel:?}"
12365+
pixel[3] < 80,
12366+
"expected side dot to stay inside the logo cap span at ({x}, {y}), got {pixel:?}"
12367+
);
12368+
}
12369+
for (x, y) in [(18, 4), (6, 11), (29, 11)] {
12370+
let pixel = tray_icon_pixel(&ready, x, y);
12371+
assert!(
12372+
pixel[0] < 40 && pixel[1] < 50 && pixel[2] < 70 && pixel[3] > 100,
12373+
"expected short-logo top outline at ({x}, {y}), got {pixel:?}"
1242712374
);
1242812375
}
12376+
let bottom_body = tray_icon_pixel(&ready, 18, 27);
12377+
assert!(
12378+
bottom_body[0] < 40
12379+
&& bottom_body[1] < 50
12380+
&& bottom_body[2] < 70
12381+
&& bottom_body[3] > 200,
12382+
"expected short-logo bottom body, got {bottom_body:?}"
12383+
);
12384+
let top_center = tray_icon_pixel(&ready, 18, 9);
12385+
assert!(
12386+
top_center[3] < 80,
12387+
"expected short-logo top interior to stay transparent, got {top_center:?}"
12388+
);
1242912389
if tray_icon_should_use_template() {
1243012390
let ready_opaque = tray_icon_opaque_pixel_count(&ready);
1243112391
assert!(

0 commit comments

Comments
 (0)