mirror of
https://github.com/storytold/photocraft.git
synced 2026-11-13 21:52:54 +00:00
Merge storytold/photocraft main into the Fill/Stroke popover branch
Clean merge of 4 upstream commits; scorecard unchanged (12 of 183). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TYsCL5E2GM6hUabP9dmYAv
This commit is contained in:
7 files changed
+47
-6
No files matched your search
@@ -76,8 +76,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. macOS
|
||||
@@ -646,7 +645,14 @@ fn main() -> eframe::Result {
|
||||
// Caps Lock state (X11/Windows/macOS; `None` on native Wayland): the canvas shows the
|
||||
// precise crosshair for painting tools while it is toggled on (#1758).
|
||||
app.services.caps_lock = caps_lock::service(cc);
|
||||
app.services.screen_pick = screen_color::service(cc, app.services.is_wayland);
|
||||
// Pick through the portal on a Wayland desktop even when the window went through
|
||||
// Xwayland (a pen, or the X11 display-server preference): Xwayland's root window has
|
||||
// no desktop pixels to read (#8977).
|
||||
#[cfg(target_os = "linux")]
|
||||
let portal = screen_color::uses_portal(native_session);
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let portal = false;
|
||||
app.services.screen_pick = screen_color::service(cc, portal);
|
||||
// Tablet pressure/tilt/eraser (winit drops them): the macOS monitor and the X11 reader
|
||||
// write into the stylus feed. The monitor goes in here, not before the event loop:
|
||||
// AppKit's shared application only exists once winit created it (#759).
|
||||
|
||||
@@ -38,6 +38,12 @@ pub fn service(cc: &eframe::CreationContext<'_>, wayland: bool) -> Option<Servic
|
||||
Pending { receiver: rx, cancelled }
|
||||
}))
|
||||
}
|
||||
/// Whether to pick through the desktop portal: on a Wayland desktop, whichever display server the
|
||||
/// window opened on. Xwayland serves only its own windows, so reading its root window fails (#8977).
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn uses_portal(desktop: crate::linux_libs::DisplaySession) -> bool {
|
||||
desktop == crate::linux_libs::DisplaySession::Wayland
|
||||
}
|
||||
#[derive(Clone, Copy)]
|
||||
#[cfg_attr(not(any(target_os = "linux", target_os = "macos", target_os = "windows", test)), allow(dead_code))]
|
||||
struct Placement {
|
||||
@@ -480,6 +486,15 @@ mod tests {
|
||||
let offset = (y * first.width + x) * 4;
|
||||
assert_eq!(&first.rgba[offset..offset + 4], &[255, 128, 64, 255]);
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn a_wayland_desktop_picks_through_the_portal_even_with_an_xwayland_window() {
|
||||
use crate::linux_libs::DisplaySession;
|
||||
// A pen or the X11 display-server preference opens the window through Xwayland, but the
|
||||
// session is still Wayland: the X11 grab fails there with BadMatch (#8977).
|
||||
assert!(uses_portal(DisplaySession::Wayland));
|
||||
assert!(!uses_portal(DisplaySession::X11));
|
||||
}
|
||||
#[test]
|
||||
fn screen_capture_matches_negative_and_hidpi_monitor_geometry() {
|
||||
let m = [Placement { index: 2, x: -3840, y: 0, width: 3840, height: 2160 }];
|
||||
|
||||
@@ -730,7 +730,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]);
|
||||
}
|
||||
|
||||
@@ -210,7 +210,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()) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -41,6 +41,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;
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user