mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 14:28:11 +08:00
Desktop: show platform specific window buttons (#3078)
Implement app window button functionality and set platform
This commit is contained in:
+26
-5
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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,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;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user