From f393f51d2db2fbbc6c1c2b360e014818292eadc6 Mon Sep 17 00:00:00 2001 From: Mike Kucera Date: Wed, 8 Nov 2023 09:30:57 -0500 Subject: [PATCH] Back-port 3.26.2: Clear style cache for connected edges when 'display' property changed. Refs #3070 See also: Back-port: Connected edge not rendered after hiding and showing node with display: none #3184 --- debug/tests.js | 90 +++++++++++++++++++++++++++++++++++++++++ src/style/apply.js | 16 ++++++-- src/style/properties.js | 2 +- 3 files changed, 103 insertions(+), 5 deletions(-) diff --git a/debug/tests.js b/debug/tests.js index c2b689fc11..3707fa13fb 100644 --- a/debug/tests.js +++ b/debug/tests.js @@ -796,5 +796,95 @@ cy.nodes().removeStyle(); } }); + + test({ + name: "display:none", + displayName: "display: none", + description: "Apply display:none or display:element to nodes. Check that edge visibility works as expected.", + // bug: https://github.com/cytoscape/cytoscape.js/issues/3070 + + setup: function(){ + cy.scratch('prevEles', cy.elements().jsons()); + cy.scratch('prevStyle', cy.style().json()); + cy.elements().remove(); + + cy.style() + .resetToDefault() + .selector('node') + .style({ + 'background-fit': 'cover', + 'background-color': '#8B5050', + 'border-color': '#000', + 'border-width': 3, + 'border-opacity': 0.5 + }) + .selector('edge') + .style({ + 'width': 1, + 'line-color': '#ffaaaa', + 'curve-style': 'bezier', + 'target-arrow-shape': 'vee' + }) + .selector('#bird').style({ 'background-image': 'https://live.staticflickr.com/7272/7633179468_3e19e45a0c_b.jpg' }) + .selector('#cat').style({ 'background-image': 'https://live.staticflickr.com/1261/1413379559_412a540d29_b.jpg' }) + .selector('#ladybug').style({ 'background-image': 'https://live.staticflickr.com/3063/2751740612_af11fb090b_b.jpg' }) + .selector('#aphid').style({ 'background-image': 'https://live.staticflickr.com/8316/8003798443_32d01257c8_b.jpg' }) + .selector('#buggy').style({ width: 2, "line-color": "#ff0000", }); + + cy.add( [ + { group: 'nodes', data: { id: 'root' } }, + { group: 'nodes', data: { id: 'cat', parent: 'root' } }, + { group: 'nodes', data: { id: 'bird', parent: 'root' } }, + { group: 'nodes', data: { id: 'ladybug', parent: 'root' } }, + { group: 'nodes', data: { id: 'aphid', parent: 'root' } }, + { group: 'edges', data: { source: 'bird', target: 'cat' } }, + { group: 'edges', data: { source: 'aphid', target: 'cat' } }, + { group: 'edges', data: { source: 'bird', target: 'ladybug' } }, + { group: 'edges', data: { source: 'ladybug', target: 'aphid', id: 'buggy' } } + ] ); + + cy.layout({ name: 'grid' }).run(); + cy.fit(cy.elements(), 120); + + const buttonIDs = []; + + cy.nodes().forEach(node=> { + var id = node.data().id; + var button = document.createElement('button'); + button.id = 'button_' + id; + buttonIDs.push(button.id); + button.innerText = 'hide ' + id; + button.onclick = () => { + var display = 'element'; + var text = 'hide'; + if (node.style('display') === 'element') { + display = 'none'; + text = 'show'; + } + node.style('display', display); + button.innerText = text + ' ' +id; + }; + button.style.position = 'relative'; + document.body.append(button); + }); + + cy.scratch('buttonIDs', buttonIDs); + }, + + teardown: function(){ + const buttonIDs = cy.scratch('buttonIDs'); + buttonIDs.forEach(id => document.getElementById(id).remove()); + cy.removeScratch('buttonIDs'); + + cy.elements().remove(); + cy.style().resetToDefault(); + const prevEles = cy.scratch('prevEles'); + const prevStyle = cy.scratch('prevStyle'); + cy.removeScratch('prevEles'); + cy.removeScratch('prevStyle'); + cy.add(prevEles); + cy.style(prevStyle); + } + }); })(); diff --git a/src/style/apply.js b/src/style/apply.js index b731f5b4a2..d232de621d 100644 --- a/src/style/apply.js +++ b/src/style/apply.js @@ -822,9 +822,7 @@ styfn.checkBoundsTrigger = function( ele, name, fromValue, toValue ){ // then dirty the pll edge bb cache as well if( // only for beziers -- so performance of other edges isn't affected prop.triggersBoundsOfParallelBeziers - && ( ( name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier') ) - || ( name === 'display' && (fromValue === 'none' || toValue === 'none') ) - ) + && ( name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier') ) ){ ele.parallelEdges().forEach(pllEdge => { if( pllEdge.isBundledBezier() ){ @@ -832,7 +830,17 @@ styfn.checkBoundsTrigger = function( ele, name, fromValue, toValue ){ } }); } - } ); + + if( + prop.triggersBoundsOfConnectedEdges + && ( name === 'display' && (fromValue === 'none' || toValue === 'none') ) + ){ + ele.connectedEdges().forEach(edge => { + edge.dirtyBoundingBoxCache(); + }); + } + + }); }; styfn.checkTriggers = function( ele, name, fromValue, toValue ){ diff --git a/src/style/properties.js b/src/style/properties.js index 3804852bfd..377ac355ec 100644 --- a/src/style/properties.js +++ b/src/style/properties.js @@ -234,7 +234,7 @@ const styfn = {}; ]; let visibility = [ - { name: 'display', type: t.display, triggersZOrder: diff.any, triggersBounds: diff.any, triggersBoundsOfParallelBeziers: true }, + { name: 'display', type: t.display, triggersZOrder: diff.any, triggersBounds: diff.any, triggersBoundsOfConnectedEdges: true }, { name: 'visibility', type: t.visibility, triggersZOrder: diff.any }, { name: 'opacity', type: t.zeroOneNumber, triggersZOrder: diff.zeroNonZero }, { name: 'text-opacity', type: t.zeroOneNumber },