Desktop: show platform specific window buttons (#3078)

Implement app window button functionality and set platform
This commit is contained in:
Timon
2025-08-25 14:15:31 +00:00
committed by GitHub
parent 508606cdba
commit c5991c6f61
10 changed files with 91 additions and 28 deletions
+26 -5
View File
@@ -2,7 +2,7 @@ use crate::CustomEvent;
use crate::cef::WindowSize; use crate::cef::WindowSize;
use crate::consts::{APP_NAME, CEF_MESSAGE_LOOP_MAX_ITERATIONS}; use crate::consts::{APP_NAME, CEF_MESSAGE_LOOP_MAX_ITERATIONS};
use crate::render::GraphicsState; use crate::render::GraphicsState;
use graphite_desktop_wrapper::messages::{DesktopFrontendMessage, DesktopWrapperMessage}; use graphite_desktop_wrapper::messages::{DesktopFrontendMessage, DesktopWrapperMessage, Platform};
use graphite_desktop_wrapper::{DesktopWrapper, NodeGraphExecutionResult, WgpuContext, serialize_frontend_messages}; use graphite_desktop_wrapper::{DesktopWrapper, NodeGraphExecutionResult, WgpuContext, serialize_frontend_messages};
use rfd::AsyncFileDialog; use rfd::AsyncFileDialog;
@@ -152,6 +152,15 @@ impl WinitApp {
graphics_state.set_overlays_scene(scene); graphics_state.set_overlays_scene(scene);
} }
} }
DesktopFrontendMessage::UpdateWindowState { maximized, minimized } => {
if let Some(window) = &self.window {
window.set_maximized(maximized);
window.set_minimized(minimized);
}
}
DesktopFrontendMessage::CloseWindow => {
let _ = self.event_loop_proxy.send_event(CustomEvent::CloseWindow);
}
} }
} }
@@ -223,9 +232,17 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
tracing::info!("Winit window created and ready"); tracing::info!("Winit window created and ready");
self.desktop_wrapper.init(self.wgpu_context.clone()); self.desktop_wrapper.init(self.wgpu_context.clone());
#[cfg(target_os = "windows")]
let platform = Platform::Windows;
#[cfg(target_os = "macos")]
let platform = Platform::Mac;
#[cfg(target_os = "linux")]
let platform = Platform::Linux;
self.dispatch_desktop_wrapper_message(DesktopWrapperMessage::UpdatePlatform(platform));
} }
fn user_event(&mut self, _: &ActiveEventLoop, event: CustomEvent) { fn user_event(&mut self, event_loop: &ActiveEventLoop, event: CustomEvent) {
match event { match event {
CustomEvent::WebCommunicationInitialized => { CustomEvent::WebCommunicationInitialized => {
self.web_communication_initialized = true; self.web_communication_initialized = true;
@@ -268,6 +285,12 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
self.cef_schedule = Some(instant); self.cef_schedule = Some(instant);
} }
} }
CustomEvent::CloseWindow => {
// TODO: Implement graceful shutdown
tracing::info!("Exiting main event loop");
event_loop.exit();
}
} }
} }
@@ -276,14 +299,12 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
match event { match event {
WindowEvent::CloseRequested => { WindowEvent::CloseRequested => {
tracing::info!("The close button was pressed; stopping"); let _ = self.event_loop_proxy.send_event(CustomEvent::CloseWindow);
event_loop.exit();
} }
WindowEvent::Resized(PhysicalSize { width, height }) => { WindowEvent::Resized(PhysicalSize { width, height }) => {
let _ = self.window_size_sender.send(WindowSize::new(width as usize, height as usize)); let _ = self.window_size_sender.send(WindowSize::new(width as usize, height as usize));
self.cef_context.notify_of_resize(); self.cef_context.notify_of_resize();
} }
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
let Some(ref mut graphics_state) = self.graphics_state else { return }; let Some(ref mut graphics_state) = self.graphics_state else { return };
// Only rerender once we have a new ui texture to display // Only rerender once we have a new ui texture to display
+1
View File
@@ -24,6 +24,7 @@ pub(crate) enum CustomEvent {
WebCommunicationInitialized, WebCommunicationInitialized,
DesktopWrapperMessage(DesktopWrapperMessage), DesktopWrapperMessage(DesktopWrapperMessage),
NodeGraphExecutionResult(NodeGraphExecutionResult), NodeGraphExecutionResult(NodeGraphExecutionResult),
CloseWindow,
} }
fn main() { fn main() {
@@ -1,6 +1,9 @@
use graphene_std::Color; use graphene_std::Color;
use graphene_std::raster::Image; use graphene_std::raster::Image;
use graphite_editor::messages::prelude::{DocumentMessage, PortfolioMessage}; use graphite_editor::messages::app_window::app_window_message_handler::AppWindowPlatform;
use graphite_editor::messages::prelude::{AppWindowMessage, DocumentMessage, PortfolioMessage};
use crate::messages::Platform;
use super::DesktopWrapperMessageDispatcher; use super::DesktopWrapperMessageDispatcher;
use super::messages::{DesktopFrontendMessage, DesktopWrapperMessage, EditorMessage, OpenFileDialogContext, SaveFileDialogContext}; use super::messages::{DesktopFrontendMessage, DesktopWrapperMessage, EditorMessage, OpenFileDialogContext, SaveFileDialogContext};
@@ -106,5 +109,14 @@ pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMess
dispatcher.queue_editor_message(message.into()); dispatcher.queue_editor_message(message.into());
} }
DesktopWrapperMessage::PollNodeGraphEvaluation => dispatcher.poll_node_graph_evaluation(), DesktopWrapperMessage::PollNodeGraphEvaluation => dispatcher.poll_node_graph_evaluation(),
DesktopWrapperMessage::UpdatePlatform(platform) => {
let platform = match platform {
Platform::Windows => AppWindowPlatform::Windows,
Platform::Mac => AppWindowPlatform::Mac,
Platform::Linux => AppWindowPlatform::Linux,
};
let message = AppWindowMessage::AppWindowUpdatePlatform { platform };
dispatcher.queue_editor_message(message.into());
}
} }
} }
@@ -64,6 +64,15 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD
FrontendMessage::TriggerVisitLink { url } => { FrontendMessage::TriggerVisitLink { url } => {
dispatcher.respond(DesktopFrontendMessage::OpenUrl(url)); dispatcher.respond(DesktopFrontendMessage::OpenUrl(url));
} }
FrontendMessage::UpdateWindowState { maximized, minimized } => {
dispatcher.respond(DesktopFrontendMessage::UpdateWindowState { maximized, minimized });
// Forward this to update the ui
return Some(message);
}
FrontendMessage::CloseWindow => {
dispatcher.respond(DesktopFrontendMessage::CloseWindow);
}
m => return Some(m), m => return Some(m),
} }
None None
+12
View File
@@ -30,6 +30,11 @@ pub enum DesktopFrontendMessage {
height: f32, height: f32,
}, },
UpdateOverlays(vello::Scene), UpdateOverlays(vello::Scene),
UpdateWindowState {
maximized: bool,
minimized: bool,
},
CloseWindow,
} }
pub struct FileFilter { pub struct FileFilter {
@@ -47,6 +52,7 @@ pub enum DesktopWrapperMessage {
ImportSvg { path: PathBuf, content: Vec<u8> }, ImportSvg { path: PathBuf, content: Vec<u8> },
ImportImage { path: PathBuf, content: Vec<u8> }, ImportImage { path: PathBuf, content: Vec<u8> },
PollNodeGraphEvaluation, PollNodeGraphEvaluation,
UpdatePlatform(Platform),
} }
pub enum OpenFileDialogContext { pub enum OpenFileDialogContext {
@@ -58,3 +64,9 @@ pub enum SaveFileDialogContext {
Document { document_id: DocumentId, content: Vec<u8> }, Document { document_id: DocumentId, content: Vec<u8> },
File { content: Vec<u8> }, File { content: Vec<u8> },
} }
pub enum Platform {
Windows,
Mac,
Linux,
}
@@ -1,9 +1,12 @@
use crate::messages::prelude::*; use crate::messages::prelude::*;
use super::app_window_message_handler::AppWindowPlatform;
#[impl_message(Message, AppWindow)] #[impl_message(Message, AppWindow)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum AppWindowMessage { pub enum AppWindowMessage {
AppWindowMinimize, AppWindowMinimize,
AppWindowMaximize, AppWindowMaximize,
AppWindowUpdatePlatform { platform: AppWindowPlatform },
AppWindowClose, AppWindowClose,
} }
@@ -6,34 +6,34 @@ use graphite_proc_macros::{ExtractField, message_handler_data};
pub struct AppWindowMessageHandler { pub struct AppWindowMessageHandler {
platform: AppWindowPlatform, platform: AppWindowPlatform,
maximized: bool, maximized: bool,
viewport_hole_punch_active: bool, minimized: bool,
} }
#[message_handler_data] #[message_handler_data]
impl MessageHandler<AppWindowMessage, ()> for AppWindowMessageHandler { impl MessageHandler<AppWindowMessage, ()> for AppWindowMessageHandler {
fn process_message(&mut self, message: AppWindowMessage, responses: &mut std::collections::VecDeque<Message>, _: ()) { fn process_message(&mut self, message: AppWindowMessage, responses: &mut std::collections::VecDeque<Message>, _: ()) {
match message { match message {
AppWindowMessage::AppWindowMinimize => {
self.platform = if self.platform == AppWindowPlatform::Mac {
AppWindowPlatform::Windows
} else {
AppWindowPlatform::Mac
};
responses.add(FrontendMessage::UpdatePlatform { platform: self.platform });
}
AppWindowMessage::AppWindowMaximize => { AppWindowMessage::AppWindowMaximize => {
self.maximized = !self.maximized; self.maximized = !self.maximized;
responses.add(FrontendMessage::UpdateMaximized { maximized: self.maximized }); responses.add(FrontendMessage::UpdateWindowState {
maximized: self.maximized,
self.viewport_hole_punch_active = !self.viewport_hole_punch_active; minimized: self.minimized,
responses.add(FrontendMessage::UpdateViewportHolePunch {
active: self.viewport_hole_punch_active,
}); });
} }
AppWindowMessage::AppWindowClose => { AppWindowMessage::AppWindowMinimize => {
self.platform = AppWindowPlatform::Web; self.minimized = !self.minimized;
responses.add(FrontendMessage::UpdateWindowState {
maximized: self.maximized,
minimized: self.minimized,
});
}
AppWindowMessage::AppWindowUpdatePlatform { platform } => {
self.platform = platform;
responses.add(FrontendMessage::UpdatePlatform { platform: self.platform }); responses.add(FrontendMessage::UpdatePlatform { platform: self.platform });
} }
AppWindowMessage::AppWindowClose => {
responses.add(FrontendMessage::CloseWindow);
}
} }
} }
@@ -329,9 +329,11 @@ pub enum FrontendMessage {
UpdatePlatform { UpdatePlatform {
platform: AppWindowPlatform, platform: AppWindowPlatform,
}, },
UpdateMaximized { UpdateWindowState {
maximized: bool, maximized: bool,
minimized: bool,
}, },
CloseWindow,
UpdateViewportHolePunch { UpdateViewportHolePunch {
active: bool, active: bool,
}, },
+5 -2
View File
@@ -356,10 +356,13 @@ export class UpdatePlatform extends JsMessage {
readonly platform!: AppWindowPlatform; readonly platform!: AppWindowPlatform;
} }
export class UpdateMaximized extends JsMessage { export class UpdateWindowState extends JsMessage {
readonly maximized!: boolean; readonly maximized!: boolean;
readonly minimized!: boolean;
} }
export class CloseWindow extends JsMessage {}
export class UpdateViewportHolePunch extends JsMessage { export class UpdateViewportHolePunch extends JsMessage {
readonly active!: boolean; readonly active!: boolean;
} }
@@ -1741,7 +1744,7 @@ export const messageMakers: Record<string, MessageMaker> = {
UpdateLayersPanelControlBarLeftLayout, UpdateLayersPanelControlBarLeftLayout,
UpdateLayersPanelControlBarRightLayout, UpdateLayersPanelControlBarRightLayout,
UpdateLayerWidths, UpdateLayerWidths,
UpdateMaximized, UpdateWindowState,
UpdateMenuBarLayout, UpdateMenuBarLayout,
UpdateMouseCursor, UpdateMouseCursor,
UpdateNodeGraphControlBarLayout, UpdateNodeGraphControlBarLayout,
+3 -3
View File
@@ -3,7 +3,7 @@
import { writable } from "svelte/store"; import { writable } from "svelte/store";
import { type Editor } from "@graphite/editor"; import { type Editor } from "@graphite/editor";
import { type AppWindowPlatform, UpdatePlatform, UpdateMaximized, UpdateViewportHolePunch } from "@graphite/messages"; import { type AppWindowPlatform, UpdatePlatform, UpdateViewportHolePunch, UpdateWindowState } from "@graphite/messages";
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function createAppWindowState(editor: Editor) { export function createAppWindowState(editor: Editor) {
@@ -20,9 +20,9 @@ export function createAppWindowState(editor: Editor) {
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(UpdateMaximized, (maximized) => { editor.subscriptions.subscribeJsMessage(UpdateWindowState, (updateWindowState) => {
update((state) => { update((state) => {
state.maximized = maximized.maximized; state.maximized = updateWindowState.maximized;
return state; return state;
}); });
}); });