-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add DocumentMode groundwork with MaskMode and Q toggle #4001
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
f122517
268ec73
49807ad
a97d49d
3fde8bb
5b1ca12
b14dbd9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,7 +19,7 @@ use crate::messages::portfolio::document::overlays::grid_overlays::{grid_overlay | |
| use crate::messages::portfolio::document::overlays::utility_types::{OverlaysType, OverlaysVisibilitySettings, Pivot}; | ||
| use crate::messages::portfolio::document::properties_panel::properties_panel_message_handler::PropertiesPanelMessageContext; | ||
| use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier}; | ||
| use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis, PTZ}; | ||
| use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, DocumentMode, FlipAxis, PTZ}; | ||
| use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, InputConnector, NodeTemplate}; | ||
| use crate::messages::portfolio::utility_types::{PanelType, PersistentData}; | ||
| use crate::messages::prelude::*; | ||
|
|
@@ -116,6 +116,16 @@ pub struct DocumentMessageHandler { | |
| /// The name of the document, which is displayed in the tab and title bar of the editor. | ||
| #[serde(skip)] | ||
| pub name: String, | ||
| /// The current editor-only mode for the active document. | ||
| #[serde(skip)] | ||
| pub document_mode: DocumentMode, | ||
| /// The NodeId of the mask group layer when in MaskMode, or None otherwise. | ||
| #[serde(skip)] | ||
| pub mask_group_id: Option<NodeId>, | ||
| /// The layers that are targets for the current mask operation. | ||
| /// When entering MaskMode, this stores the selected layers so the mask can be applied to them on exit. | ||
| #[serde(skip)] | ||
| pub mask_target_layers: Vec<LayerNodeIdentifier>, | ||
| /// The path of the to the document file. | ||
| #[serde(skip)] | ||
| pub(crate) path: Option<PathBuf>, | ||
|
|
@@ -174,6 +184,9 @@ impl Default for DocumentMessageHandler { | |
| // Fields omitted from the saved document format | ||
| // ============================================= | ||
| name: DEFAULT_DOCUMENT_NAME.to_string(), | ||
| document_mode: DocumentMode::default(), | ||
| mask_group_id: None, | ||
| mask_target_layers: Vec::new(), | ||
| path: None, | ||
| breadcrumb_network_path: Vec::new(), | ||
| selection_network_path: Vec::new(), | ||
|
|
@@ -1115,6 +1128,85 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes | |
| self.render_mode = render_mode; | ||
| responses.add_front(NodeGraphMessage::RunDocumentGraph); | ||
| } | ||
| DocumentMessage::ToggleDocumentMode => { | ||
| self.document_mode = match self.document_mode { | ||
| DocumentMode::MaskMode => DocumentMode::DesignMode, | ||
| _ => DocumentMode::MaskMode, | ||
| }; | ||
| responses.add(PortfolioMessage::UpdateDocumentWidgets); | ||
| } | ||
| DocumentMessage::SetDocumentMode { document_mode } => { | ||
| if self.document_mode != document_mode { | ||
| self.document_mode = document_mode; | ||
| responses.add(PortfolioMessage::UpdateDocumentWidgets); | ||
| } | ||
| } | ||
| DocumentMessage::EnterMaskMode => { | ||
| if self.document_mode == DocumentMode::MaskMode { | ||
| return; | ||
| } | ||
|
|
||
| self.document_mode = DocumentMode::MaskMode; | ||
|
|
||
| // Store the currently selected layers as mask targets | ||
| self.mask_target_layers = self.network_interface.selected_nodes().selected_layers(self.network_interface.document_metadata()).collect(); | ||
|
|
||
| // Create a new group layer for the mask | ||
| let mask_group_id = NodeId::new(); | ||
| self.mask_group_id = Some(mask_group_id); | ||
|
|
||
| responses.add(DocumentMessage::AddTransaction); | ||
|
|
||
| // Create the mask group layer as an empty custom layer container | ||
| graph_modification_utils::new_custom(mask_group_id, Vec::new(), self.new_layer_parent(true), responses); | ||
|
|
||
| // Set the mask group opacity to 50% so the user can see artwork beneath | ||
| responses.add(GraphOperationMessage::OpacitySet { | ||
| layer: LayerNodeIdentifier::new_unchecked(mask_group_id), | ||
| opacity: 0.5, | ||
| }); | ||
|
|
||
| responses.add(DocumentMessage::EndTransaction); | ||
| responses.add(PortfolioMessage::UpdateDocumentWidgets); | ||
| } | ||
krVatsal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| DocumentMessage::ExitMaskMode { discard } => { | ||
| if self.document_mode != DocumentMode::MaskMode { | ||
| return; | ||
| } | ||
|
|
||
| self.document_mode = DocumentMode::DesignMode; | ||
|
|
||
| if discard { | ||
| // Delete the mask group without applying it | ||
| if let Some(mask_group_id) = self.mask_group_id { | ||
| responses.add(DocumentMessage::AddTransaction); | ||
| responses.add(NodeGraphMessage::DeleteNodes { | ||
| node_ids: vec![mask_group_id], | ||
| delete_children: true, | ||
| }); | ||
| responses.add(DocumentMessage::EndTransaction); | ||
| } | ||
| } else { | ||
| // Rasterize the mask group and apply it to target layers | ||
| if let Some(mask_group_id) = self.mask_group_id { | ||
| responses.add(GraphOperationMessage::ApplyMaskStencil { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Applying mask on Prompt for AI agents |
||
| layers: self.mask_target_layers.clone(), | ||
| mask_image: graphene_std::raster::Image::new(1, 1, graphene_std::Color::WHITE), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Mask apply path discards authored mask content by using a placeholder 1x1 white image and then deleting the mask group. Prompt for AI agents |
||
| }); | ||
| responses.add(NodeGraphMessage::DeleteNodes { | ||
| node_ids: vec![mask_group_id], | ||
| delete_children: true, | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| // Clear mask state | ||
| self.mask_group_id = None; | ||
| self.mask_target_layers.clear(); | ||
|
|
||
| responses.add(PortfolioMessage::UpdateDocumentWidgets); | ||
| } | ||
| DocumentMessage::DrawMarchingAntsOverlay { context: _ } => {} | ||
| DocumentMessage::AddTransaction => { | ||
| // Reverse order since they are added to the front | ||
| responses.add_front(DocumentMessage::CommitTransaction); | ||
|
|
@@ -1470,6 +1562,8 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes | |
| SaveDocument, | ||
| SelectAllLayers, | ||
| SetSnapping, | ||
| ToggleDocumentMode, | ||
| SetDocumentMode, | ||
| ToggleGridVisibility, | ||
| ToggleOverlaysVisibility, | ||
| ToggleSnapping, | ||
|
|
@@ -2084,6 +2178,13 @@ impl DocumentMessageHandler { | |
|
|
||
| /// Finds the parent folder which, based on the current selections, should be the container of any newly added layers. | ||
| pub fn new_layer_parent(&self, include_self: bool) -> LayerNodeIdentifier { | ||
| // If we're in MaskMode and have a mask group, all new layers should go into the mask group | ||
| if self.document_mode == DocumentMode::MaskMode | ||
| && let Some(mask_group_id) = self.mask_group_id | ||
| { | ||
| return LayerNodeIdentifier::new_unchecked(mask_group_id); | ||
| } | ||
|
|
||
| let Some(selected_nodes) = self.network_interface.selected_nodes_in_nested_network(&self.selection_network_path) else { | ||
| warn!("No selected nodes found in new_layer_parent. Defaulting to ROOT_PARENT."); | ||
| return LayerNodeIdentifier::ROOT_PARENT; | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: Mask group creation can self-parent because parent resolution happens after entering MaskMode and storing the same mask group ID used for the new layer.
Prompt for AI agents