collective

Browse / github.com/storytold/photocraft / patch

Extra windows share the main window's app id, so Wayland docks and window rules see them (#6697)

Extra windows share the main window's app id, so Wayland docks and window rules see them (#6697) Partly addresses #6697 (Hyprland: let PhotoCraft's windows float, tile and move between workspaces). ## Problem The main window sets a Wayland app id (`ai.storyteller.photocraft`), but the extra OS windows are built from `egui::ViewportBuilder::default()` and have none: - document windows (Window › New Window, `canvas::tabs::extra_windows`) - Camera Raw (`dialogs::camera_raw_ui::controls`) - the X11/Windows screen color picker (`dialogs::screen_picker`) On Hyprland (0.56.2, Omarchy), `hyprctl clients` showed them with an **empty class**, so no window rule can target them, and they tile lik

Status
open
Score
0.00
Adopters
0 other operators
Author
TaylorBurke (imported from pull request #9894, open)
Base
dacecbfea1d5ca3270b44d09ac35e3d195618cb0
Size
+24 −5 in 6 files
Review
pending
Shared
2026-10-28
Id
b17bbefb9c1cdf705a9172a6

How it went

No other operator has reported using it yet.

Diff

raw · apps/photocraft/src/main.rs crates/ui-egui/src/canvas/tabs.rs crates/ui-egui/src/dialogs/camera_raw_ui/controls.rs crates/ui-egui/src/dialogs/screen_picker.rs crates/ui-egui/src/lib.rs crates/ui-egui/src/os_window.rs

diff --git a/apps/photocraft/src/main.rs b/apps/photocraft/src/main.rs--- a/apps/photocraft/src/main.rs+++ b/apps/photocraft/src/main.rs@@ -75,8 +75,7 @@ mod window_frame; use photocraft_engine::Session; use photocraft_ui_egui::PhotocraftApp; -/// Matches the `.desktop` file and hicolor icon name, so Wayland docks pick up the icon.-const APP_ID: &str = "ai.storyteller.photocraft";+use photocraft_ui_egui::os_window::APP_ID;  /// Windows and Linux: no OS title bar; the app's top bar is the title bar, with its own caption /// buttons and edge resizing (`photocraft_ui_egui::titlebar`), as Photoshop does on Windows. macOSdiff --git a/crates/ui-egui/src/canvas/tabs.rs b/crates/ui-egui/src/canvas/tabs.rs--- a/crates/ui-egui/src/canvas/tabs.rs+++ b/crates/ui-egui/src/canvas/tabs.rs@@ -704,7 +704,7 @@ pub fn extra_windows(app: &mut PhotocraftApp, ctx: &egui::Context) {         let Some(st) = app.session.documents().get(w.document) else { continue };         let title = format!("{} — window {}", st.doc.name, w.id);         let vid = egui::ViewportId::from_hash_of(("docwin", w.id));-        let mut builder = egui::ViewportBuilder::default().with_title(title).with_inner_size([800.0, 600.0]);+        let mut builder = crate::os_window::builder().with_title(title).with_inner_size([800.0, 600.0]);         if let Some([x, y]) = w.position {             builder = builder.with_position([x, y]);         }diff --git a/crates/ui-egui/src/dialogs/camera_raw_ui/controls.rs b/crates/ui-egui/src/dialogs/camera_raw_ui/controls.rs--- a/crates/ui-egui/src/dialogs/camera_raw_ui/controls.rs+++ b/crates/ui-egui/src/dialogs/camera_raw_ui/controls.rs@@ -197,7 +197,7 @@ pub fn show(app: &mut PhotocraftApp, ctx: &egui::Context) {         ui.painter().rect_filled(r, 0.0, Tokens::get(ui.ctx()).scrim);     });     // A window of its own (title bar, moved and resized like any window), like Adobe Camera Raw.-    let builder = egui::ViewportBuilder::default().with_title(window_title).with_inner_size([1360.0, 900.0]).with_min_inner_size([900.0, 600.0]);+    let builder = crate::os_window::builder().with_title(window_title).with_inner_size([1360.0, 900.0]).with_min_inner_size([900.0, 600.0]);     let mut close = false;     ctx.show_viewport_immediate(egui::ViewportId::from_hash_of("camera-raw-window"), builder, |ui, class| {         if ui.ctx().input(|i| i.viewport().close_requested()) {diff --git a/crates/ui-egui/src/dialogs/screen_picker.rs b/crates/ui-egui/src/dialogs/screen_picker.rs--- a/crates/ui-egui/src/dialogs/screen_picker.rs+++ b/crates/ui-egui/src/dialogs/screen_picker.rs@@ -261,7 +261,7 @@ pub fn cancel(ctx: &Context) {  /// The picker window: hidden until its snapshot has been painted (#3210). fn viewport_builder(ctx: &Context, image: &ScreenImage) -> egui::ViewportBuilder {-    let mut builder = egui::ViewportBuilder::default()+    let mut builder = crate::os_window::builder()         .with_title(tl!("PhotoCraft — Screen Color Picker"))         .with_decorations(false)         .with_resizable(false)diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs--- a/crates/ui-egui/src/lib.rs+++ b/crates/ui-egui/src/lib.rs@@ -30,6 +30,7 @@ pub mod menu_catalog; pub mod menus; pub mod monitor_status; pub mod notices;+pub mod os_window; pub mod panels; pub mod preset_files_ui; pub mod proxy;diff --git a/crates/ui-egui/src/os_window.rs b/crates/ui-egui/src/os_window.rsnew file mode 100644--- /dev/null+++ b/crates/ui-egui/src/os_window.rs@@ -0,0 +1,19 @@+//! Extra OS windows (document windows, Camera Raw, the screen color picker) share the main+//! window's app id, so docks show the PhotoCraft icon on them and Wayland compositors' window+//! rules can match them (Hyprland's `class:`, #6697). Without one their app id is empty.++/// Matches the `.desktop` file and hicolor icon name, so Wayland docks pick up the icon.+pub const APP_ID: &str = "ai.storyteller.photocraft";++/// A builder for an extra OS window, carrying [`APP_ID`].+pub fn builder() -> egui::ViewportBuilder {+    egui::ViewportBuilder::default().with_app_id(APP_ID)+}++#[cfg(test)]+mod tests {+    #[test]+    fn extra_windows_carry_the_app_id() {+        assert_eq!(super::builder().app_id.as_deref(), Some("ai.storyteller.photocraft"));+    }+}