@@ -42,6 +42,7 @@ use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
4242use graphene_std:: vector:: misc:: dvec2_to_point;
4343use graphene_std:: vector:: style:: RenderMode ;
4444use kurbo:: { Affine , BezPath , Line , PathSeg } ;
45+ use std:: collections:: HashSet ;
4546use std:: path:: PathBuf ;
4647use std:: sync:: Arc ;
4748use std:: time:: Duration ;
@@ -84,8 +85,8 @@ pub struct DocumentMessageHandler {
8485 //
8586 // Contains the NodeNetwork and acts an an interface to manipulate the NodeNetwork with custom setters in order to keep NetworkMetadata in sync
8687 pub network_interface : NodeNetworkInterface ,
87- /// List of the [`LayerNodeIdentifier`]s that are currently collapsed by the user in the Layers panel.
88- /// Collapsed means that the expansion arrow isn't set to show the children of these layers.
88+ /// Tracks which layer instances are collapsed in the Layers panel, keyed by instance path .
89+ # [ serde ( deserialize_with = "deserialize_collapsed_layers" , default ) ]
8990 pub collapsed : CollapsedLayers ,
9091 /// The full Git commit hash of the Graphite repository that was used to build the editor.
9192 /// We save this to provide a hint about which version of the editor was used to create the document.
@@ -317,7 +318,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
317318 DocumentMessage :: ClearLayersPanel => {
318319 // Send an empty layer list
319320 if layers_panel_open {
320- let layer_structure = Self :: default ( ) . build_layer_structure ( LayerNodeIdentifier :: ROOT_PARENT ) ;
321+ let layer_structure = Self :: default ( ) . build_layer_structure ( ) ;
321322 responses. add ( FrontendMessage :: UpdateDocumentLayerStructure { layer_structure } ) ;
322323 }
323324
@@ -380,7 +381,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
380381 DocumentMessage :: DocumentStructureChanged => {
381382 if layers_panel_open {
382383 self . network_interface . load_structure ( ) ;
383- let layer_structure = self . build_layer_structure ( LayerNodeIdentifier :: ROOT_PARENT ) ;
384+ let layer_structure = self . build_layer_structure ( ) ;
384385
385386 self . update_layers_panel_control_bar_widgets ( layers_panel_open, responses) ;
386387 self . update_layers_panel_bottom_bar_widgets ( layers_panel_open, responses) ;
@@ -1167,25 +1168,27 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
11671168 responses. add ( OverlaysMessage :: Draw ) ;
11681169 responses. add ( PortfolioMessage :: UpdateOpenDocumentsList ) ;
11691170 }
1170- DocumentMessage :: ToggleLayerExpansion { id, recursive } => {
1171- let layer = LayerNodeIdentifier :: new ( id, & self . network_interface ) ;
1172- let metadata = self . metadata ( ) ;
1173-
1174- let is_collapsed = self . collapsed . 0 . contains ( & layer) ;
1171+ DocumentMessage :: ToggleLayerExpansion { instance_path, recursive } => {
1172+ let is_collapsed = self . collapsed . 0 . contains ( & instance_path) ;
11751173
11761174 if is_collapsed {
11771175 if recursive {
1178- let children : HashSet < _ > = layer . descendants ( metadata ) . collect ( ) ;
1179- self . collapsed . 0 . retain ( |collapsed_layer | !children . contains ( collapsed_layer ) && collapsed_layer != & layer ) ;
1176+ // Remove this path and all descendant paths (paths that start with this one)
1177+ self . collapsed . 0 . retain ( |path | !path . starts_with ( & instance_path ) ) ;
11801178 } else {
1181- self . collapsed . 0 . retain ( |collapsed_layer| collapsed_layer != & layer ) ;
1179+ self . collapsed . 0 . retain ( |path| * path != instance_path ) ;
11821180 }
11831181 } else {
11841182 if recursive {
1185- let children_to_add: Vec < _ > = layer. descendants ( metadata) . filter ( |child| !self . collapsed . 0 . contains ( child) ) . collect ( ) ;
1186- self . collapsed . 0 . extend ( children_to_add) ;
1183+ // Collapse all expanded descendant instances by collecting their paths from the structure tree
1184+ let descendant_paths = self . collect_descendant_instance_paths ( & instance_path) ;
1185+ for path in descendant_paths {
1186+ if !self . collapsed . 0 . contains ( & path) {
1187+ self . collapsed . 0 . push ( path) ;
1188+ }
1189+ }
11871190 }
1188- self . collapsed . 0 . push ( layer ) ;
1191+ self . collapsed . 0 . push ( instance_path ) ;
11891192 }
11901193
11911194 responses. add ( NodeGraphMessage :: SendGraph ) ;
@@ -1740,22 +1743,218 @@ impl DocumentMessageHandler {
17401743 Ok ( document_message_handler)
17411744 }
17421745
1743- /// Recursively builds the layer structure tree for a folder.
1744- fn build_layer_structure ( & self , folder : LayerNodeIdentifier ) -> Vec < LayerStructureEntry > {
1745- folder
1746- . children ( self . metadata ( ) )
1747- . map ( |layer_node| {
1748- let children = if layer_node. has_children ( self . metadata ( ) ) && !self . collapsed . 0 . contains ( & layer_node) {
1749- self . build_layer_structure ( layer_node)
1750- } else {
1751- Vec :: new ( )
1752- } ;
1753- LayerStructureEntry {
1754- layer_id : layer_node. to_node ( ) ,
1755- children,
1746+ /// Builds the layer structure tree by traversing the node graph directly.
1747+ /// Unlike the canonical `structure` field of [`DocumentMetadata`] (which stores single-parent relationships), this allows
1748+ /// the same layer to appear under multiple parents when the graph feeds the same child content into separate parent layers.
1749+ fn build_layer_structure ( & self ) -> Vec < LayerStructureEntry > {
1750+ let network = & self . network_interface ;
1751+
1752+ let Some ( root_node) = network. root_node ( & [ ] ) else { return Vec :: new ( ) } ;
1753+ let Some ( first_root_layer_id) = network
1754+ . upstream_flow_back_from_nodes ( vec ! [ root_node. node_id] , & [ ] , FlowType :: PrimaryFlow )
1755+ . find ( |node_id| network. is_layer ( node_id, & [ ] ) )
1756+ else {
1757+ return Vec :: new ( ) ;
1758+ } ;
1759+
1760+ let selected_layers: HashSet < NodeId > = network. selected_nodes ( ) . selected_layers ( self . metadata ( ) ) . map ( LayerNodeIdentifier :: to_node) . collect ( ) ;
1761+
1762+ let ancestors = HashSet :: new ( ) ;
1763+ let instance_path = Vec :: new ( ) ;
1764+ let mut root_entries = Vec :: new ( ) ;
1765+
1766+ // The first root layer is the topmost entry
1767+ root_entries. push ( self . build_layer_entry ( first_root_layer_id, & ancestors, & selected_layers, & instance_path) ) ;
1768+
1769+ // Layers in the primary flow (input[0] chain) from the first root layer are root-level siblings
1770+ let mut root_ancestors = HashSet :: new ( ) ;
1771+ root_ancestors. insert ( first_root_layer_id) ;
1772+
1773+ for sibling_id in network. upstream_flow_back_from_nodes ( vec ! [ first_root_layer_id] , & [ ] , FlowType :: PrimaryFlow ) . skip ( 1 ) {
1774+ if network. is_layer ( & sibling_id, & [ ] ) && !root_ancestors. contains ( & sibling_id) {
1775+ root_entries. push ( self . build_layer_entry ( sibling_id, & root_ancestors, & selected_layers, & instance_path) ) ;
1776+ }
1777+ }
1778+
1779+ root_entries
1780+ }
1781+
1782+ /// Builds a single `LayerStructureEntry` for the given layer, including its `children_present` flag,
1783+ /// `descendant_selected` flag, and (if expanded) its children collected from the graph.
1784+ fn build_layer_entry ( & self , layer_id : NodeId , ancestors : & HashSet < NodeId > , selected_layers : & HashSet < NodeId > , parent_instance_path : & [ NodeId ] ) -> LayerStructureEntry {
1785+ let mut instance_path = parent_instance_path. to_vec ( ) ;
1786+ instance_path. push ( layer_id) ;
1787+
1788+ let mut child_ancestors = ancestors. clone ( ) ;
1789+ child_ancestors. insert ( layer_id) ;
1790+
1791+ let children_present = self . has_layer_children_in_graph ( layer_id, & child_ancestors) ;
1792+
1793+ let collapsed = self . collapsed . 0 . contains ( & instance_path) ;
1794+
1795+ let children = if children_present && !collapsed {
1796+ self . collect_layer_children ( layer_id, & child_ancestors, selected_layers, & instance_path)
1797+ } else {
1798+ Vec :: new ( )
1799+ } ;
1800+
1801+ // Compute whether any descendant is selected (checking expanded children and, if collapsed, via graph traversal)
1802+ let descendant_selected = if !children. is_empty ( ) {
1803+ children. iter ( ) . any ( |child| child. descendant_selected || selected_layers. contains ( & child. layer_id ) )
1804+ } else if children_present {
1805+ // Layer is collapsed but has children, so check via graph traversal
1806+ self . has_selected_descendant_in_graph ( layer_id, & child_ancestors, selected_layers)
1807+ } else {
1808+ false
1809+ } ;
1810+
1811+ LayerStructureEntry {
1812+ layer_id,
1813+ children,
1814+ children_present,
1815+ descendant_selected,
1816+ }
1817+ }
1818+
1819+ /// Checks whether a layer has any child layers reachable via horizontal flow in the graph.
1820+ fn has_layer_children_in_graph ( & self , layer_id : NodeId , child_ancestors : & HashSet < NodeId > ) -> bool {
1821+ let network = & self . network_interface ;
1822+
1823+ network
1824+ . upstream_flow_back_from_nodes ( vec ! [ layer_id] , & [ ] , FlowType :: HorizontalFlow )
1825+ . skip ( 1 )
1826+ . any ( |id| network. is_layer ( & id, & [ ] ) && !child_ancestors. contains ( & id) )
1827+ }
1828+
1829+ /// Checks whether any descendant layer in the graph (via horizontal + primary flow) is selected.
1830+ /// Used when a layer is collapsed to determine if the ancestor-of-selected indicator should show.
1831+ fn has_selected_descendant_in_graph ( & self , layer_id : NodeId , ancestors : & HashSet < NodeId > , selected_layers : & HashSet < NodeId > ) -> bool {
1832+ let network = & self . network_interface ;
1833+
1834+ // Find child layers via horizontal flow
1835+ let mut stack: Vec < NodeId > = network
1836+ . upstream_flow_back_from_nodes ( vec ! [ layer_id] , & [ ] , FlowType :: HorizontalFlow )
1837+ . skip ( 1 )
1838+ . filter ( |node_id| network. is_layer ( node_id, & [ ] ) && !ancestors. contains ( node_id) )
1839+ . collect ( ) ;
1840+
1841+ let mut visited = ancestors. clone ( ) ;
1842+
1843+ // Iteratively explore all descendant layers via a depth-first traversal
1844+ while let Some ( current_id) = stack. pop ( ) {
1845+ // Skip already-visited layers to avoid infinite loops from graph cycles
1846+ if !visited. insert ( current_id) {
1847+ continue ;
1848+ }
1849+
1850+ // Found a selected descendant, the ancestor indicator should be shown
1851+ if selected_layers. contains ( & current_id) {
1852+ return true ;
1853+ }
1854+
1855+ // Check this layer's children via horizontal flow
1856+ for node_id in network. upstream_flow_back_from_nodes ( vec ! [ current_id] , & [ ] , FlowType :: HorizontalFlow ) . skip ( 1 ) {
1857+ if network. is_layer ( & node_id, & [ ] ) && !visited. contains ( & node_id) {
1858+ stack. push ( node_id) ;
17561859 }
1757- } )
1758- . collect ( )
1860+ }
1861+
1862+ // Check stacked siblings via primary flow
1863+ for node_id in network. upstream_flow_back_from_nodes ( vec ! [ current_id] , & [ ] , FlowType :: PrimaryFlow ) . skip ( 1 ) {
1864+ if network. is_layer ( & node_id, & [ ] ) && !visited. contains ( & node_id) {
1865+ stack. push ( node_id) ;
1866+ }
1867+ }
1868+ }
1869+
1870+ false
1871+ }
1872+
1873+ /// Collects the child entries for a given layer by traversing its horizontal and primary flows.
1874+ /// The horizontal flow (a layer's secondary input chain) finds nested content layers, and the
1875+ /// primary flow from those (their stack's top output) finds stacked siblings at the same depth.
1876+ /// `ancestors` contains layer IDs in the current path from root, used for cycle prevention.
1877+ fn collect_layer_children ( & self , layer_id : NodeId , ancestors : & HashSet < NodeId > , selected_layers : & HashSet < NodeId > , instance_path : & [ NodeId ] ) -> Vec < LayerStructureEntry > {
1878+ let network = & self . network_interface ;
1879+
1880+ // Find the first nested layer via horizontal flow (content inside this layer)
1881+ let Some ( nested_id) = network
1882+ . upstream_flow_back_from_nodes ( vec ! [ layer_id] , & [ ] , FlowType :: HorizontalFlow )
1883+ . skip ( 1 )
1884+ . find ( |id| network. is_layer ( id, & [ ] ) )
1885+ else {
1886+ return Vec :: new ( ) ;
1887+ } ;
1888+
1889+ // Cycle detected, this layer is already an ancestor in the current branch
1890+ if ancestors. contains ( & nested_id) {
1891+ return Vec :: new ( ) ;
1892+ }
1893+
1894+ // The nested layer is the first child at this depth level
1895+ let mut children = vec ! [ self . build_layer_entry( nested_id, ancestors, selected_layers, instance_path) ] ;
1896+
1897+ // Primary flow from the nested layer finds stacked siblings (more children of this layer)
1898+ for sibling_id in network. upstream_flow_back_from_nodes ( vec ! [ nested_id] , & [ ] , FlowType :: PrimaryFlow ) . skip ( 1 ) {
1899+ if network. is_layer ( & sibling_id, & [ ] ) && !ancestors. contains ( & sibling_id) {
1900+ children. push ( self . build_layer_entry ( sibling_id, ancestors, selected_layers, instance_path) ) ;
1901+ }
1902+ }
1903+
1904+ children
1905+ }
1906+
1907+ /// Collects instance paths for all descendant layers of the given instance path by traversing the graph.
1908+ /// Used for recursive collapse to find all expandable descendants.
1909+ fn collect_descendant_instance_paths ( & self , instance_path : & [ NodeId ] ) -> Vec < Vec < NodeId > > {
1910+ let Some ( & layer_id) = instance_path. last ( ) else { return Vec :: new ( ) } ;
1911+ let network = & self . network_interface ;
1912+
1913+ let mut paths = Vec :: new ( ) ;
1914+ let mut stack: Vec < ( NodeId , Vec < NodeId > ) > = Vec :: new ( ) ;
1915+
1916+ // Seed with child layers via horizontal flow
1917+ for node_id in network. upstream_flow_back_from_nodes ( vec ! [ layer_id] , & [ ] , FlowType :: HorizontalFlow ) . skip ( 1 ) {
1918+ if network. is_layer ( & node_id, & [ ] ) {
1919+ let mut child_path = instance_path. to_vec ( ) ;
1920+ child_path. push ( node_id) ;
1921+ stack. push ( ( node_id, child_path) ) ;
1922+ }
1923+ }
1924+
1925+ let mut visited = HashSet :: new ( ) ;
1926+
1927+ // Depth-first traversal collecting all unique descendant instance paths
1928+ while let Some ( ( current_id, current_path) ) = stack. pop ( ) {
1929+ // Skip paths we've already visited to prevent cycles
1930+ if !visited. insert ( current_path. clone ( ) ) {
1931+ continue ;
1932+ }
1933+
1934+ // Record this descendant's instance path for collapsing
1935+ paths. push ( current_path. clone ( ) ) ;
1936+
1937+ // Add nested content layers found via horizontal flow
1938+ for node_id in network. upstream_flow_back_from_nodes ( vec ! [ current_id] , & [ ] , FlowType :: HorizontalFlow ) . skip ( 1 ) {
1939+ if network. is_layer ( & node_id, & [ ] ) {
1940+ let mut child_path = current_path. clone ( ) ;
1941+ child_path. push ( node_id) ;
1942+ stack. push ( ( node_id, child_path) ) ;
1943+ }
1944+ }
1945+
1946+ // Add stacked sibling layers found via primary flow
1947+ for node_id in network. upstream_flow_back_from_nodes ( vec ! [ current_id] , & [ ] , FlowType :: PrimaryFlow ) . skip ( 1 ) {
1948+ if network. is_layer ( & node_id, & [ ] ) {
1949+ // Siblings share the same parent path (everything up to the last element of current_path)
1950+ let mut sibling_path = current_path[ ..current_path. len ( ) - 1 ] . to_vec ( ) ;
1951+ sibling_path. push ( node_id) ;
1952+ stack. push ( ( node_id, sibling_path) ) ;
1953+ }
1954+ }
1955+ }
1956+
1957+ paths
17591958 }
17601959
17611960 pub fn undo_with_history ( & mut self , viewport : & ViewportMessageHandler , responses : & mut VecDeque < Message > ) {
@@ -3221,6 +3420,16 @@ impl Iterator for ClickXRayIter<'_> {
32213420 }
32223421}
32233422
3423+ /// Deserializes `CollapsedLayers` with backwards compatibility for the old format
3424+ /// (flat list of layer node IDs) by consuming the entire value first, then attempting
3425+ /// to interpret it as the new format. Falls back to an empty default for old documents.
3426+ fn deserialize_collapsed_layers < ' de , D : serde:: Deserializer < ' de > > ( deserializer : D ) -> Result < CollapsedLayers , D :: Error > {
3427+ use serde:: Deserialize ;
3428+ // Buffer the entire value to avoid leaving the deserializer in a bad state on type mismatch
3429+ let value = serde_json:: Value :: deserialize ( deserializer) ?;
3430+ Ok ( serde_json:: from_value ( value) . unwrap_or_default ( ) )
3431+ }
3432+
32243433#[ cfg( test) ]
32253434mod document_message_handler_tests {
32263435 use super :: * ;
0 commit comments