From b3dde417a0c0c3814ab64f3dee8478d1dec99963 Mon Sep 17 00:00:00 2001 From: sbosse Date: Mon, 21 Jul 2025 23:41:42 +0200 Subject: [PATCH] Mon 21 Jul 22:43:21 CEST 2025 --- js/ui/mxgraph/src/js/layout/mxStackLayout.js | 515 +++++++++++++++++++ 1 file changed, 515 insertions(+) create mode 100644 js/ui/mxgraph/src/js/layout/mxStackLayout.js diff --git a/js/ui/mxgraph/src/js/layout/mxStackLayout.js b/js/ui/mxgraph/src/js/layout/mxStackLayout.js new file mode 100644 index 0000000..ed46a5b --- /dev/null +++ b/js/ui/mxgraph/src/js/layout/mxStackLayout.js @@ -0,0 +1,515 @@ +/** + * Copyright (c) 2006-2015, JGraph Ltd + * Copyright (c) 2006-2015, Gaudenz Alder + */ +/** + * Class: mxStackLayout + * + * Extends to create a horizontal or vertical stack of the + * child vertices. The children do not need to be connected for this layout + * to work. + * + * Example: + * + * (code) + * var layout = new mxStackLayout(graph, true); + * layout.execute(graph.getDefaultParent()); + * (end) + * + * Constructor: mxStackLayout + * + * Constructs a new stack layout layout for the specified graph, + * spacing, orientation and offset. + */ +function mxStackLayout(graph, horizontal, spacing, x0, y0, border) +{ + mxGraphLayout.call(this, graph); + this.horizontal = (horizontal != null) ? horizontal : true; + this.spacing = (spacing != null) ? spacing : 0; + this.x0 = (x0 != null) ? x0 : 0; + this.y0 = (y0 != null) ? y0 : 0; + this.border = (border != null) ? border : 0; +}; + +/** + * Extends mxGraphLayout. + */ +mxStackLayout.prototype = new mxGraphLayout(); +mxStackLayout.prototype.constructor = mxStackLayout; + +/** + * Variable: horizontal + * + * Specifies the orientation of the layout. Default is true. + */ +mxStackLayout.prototype.horizontal = null; + +/** + * Variable: spacing + * + * Specifies the spacing between the cells. Default is 0. + */ +mxStackLayout.prototype.spacing = null; + +/** + * Variable: x0 + * + * Specifies the horizontal origin of the layout. Default is 0. + */ +mxStackLayout.prototype.x0 = null; + +/** + * Variable: y0 + * + * Specifies the vertical origin of the layout. Default is 0. + */ +mxStackLayout.prototype.y0 = null; + +/** + * Variable: border + * + * Border to be added if fill is true. Default is 0. + */ +mxStackLayout.prototype.border = 0; + +/** + * Variable: marginTop + * + * Top margin for the child area. Default is 0. + */ +mxStackLayout.prototype.marginTop = 0; + +/** + * Variable: marginLeft + * + * Top margin for the child area. Default is 0. + */ +mxStackLayout.prototype.marginLeft = 0; + +/** + * Variable: marginRight + * + * Top margin for the child area. Default is 0. + */ +mxStackLayout.prototype.marginRight = 0; + +/** + * Variable: marginBottom + * + * Top margin for the child area. Default is 0. + */ +mxStackLayout.prototype.marginBottom = 0; + +/** + * Variable: keepFirstLocation + * + * Boolean indicating if the location of the first cell should be + * kept, that is, it will not be moved to x0 or y0. + */ +mxStackLayout.prototype.keepFirstLocation = false; + +/** + * Variable: fill + * + * Boolean indicating if dimension should be changed to fill out the parent + * cell. Default is false. + */ +mxStackLayout.prototype.fill = false; + +/** + * Variable: resizeParent + * + * If the parent should be resized to match the width/height of the + * stack. Default is false. + */ +mxStackLayout.prototype.resizeParent = false; + +/** + * Variable: resizeParentMax + * + * Use maximum of existing value and new value for resize of parent. + * Default is false. + */ +mxStackLayout.prototype.resizeParentMax = false; + +/** + * Variable: resizeLast + * + * If the last element should be resized to fill out the parent. Default is + * false. If is true then this is ignored. + */ +mxStackLayout.prototype.resizeLast = false; + +/** + * Variable: wrap + * + * Value at which a new column or row should be created. Default is null. + */ +mxStackLayout.prototype.wrap = null; + +/** + * Variable: borderCollapse + * + * If the strokeWidth should be ignored. Default is true. + */ +mxStackLayout.prototype.borderCollapse = true; + +/** + * Function: isHorizontal + * + * Returns . + */ +mxStackLayout.prototype.isHorizontal = function() +{ + return this.horizontal; +}; + +/** + * Function: moveCell + * + * Implements . + */ +mxStackLayout.prototype.moveCell = function(cell, x, y) +{ + var model = this.graph.getModel(); + var parent = model.getParent(cell); + var horizontal = this.isHorizontal(); + + if (cell != null && parent != null) + { + var i = 0; + var last = 0; + var childCount = model.getChildCount(parent); + var value = (horizontal) ? x : y; + var pstate = this.graph.getView().getState(parent); + + if (pstate != null) + { + value -= (horizontal) ? pstate.x : pstate.y; + } + + value /= this.graph.view.scale; + + for (i = 0; i < childCount; i++) + { + var child = model.getChildAt(parent, i); + + if (child != cell) + { + var bounds = model.getGeometry(child); + + if (bounds != null) + { + var tmp = (horizontal) ? + bounds.x + bounds.width / 2 : + bounds.y + bounds.height / 2; + + if (last <= value && tmp > value) + { + break; + } + + last = tmp; + } + } + } + + // Changes child order in parent + var idx = parent.getIndex(cell); + idx = Math.max(0, i - ((i > idx) ? 1 : 0)); + + model.add(parent, cell, idx); + } +}; + +/** + * Function: getParentSize + * + * Returns the size for the parent container or the size of the graph + * container if the parent is a layer or the root of the model. + */ +mxStackLayout.prototype.getParentSize = function(parent) +{ + var model = this.graph.getModel(); + var pgeo = model.getGeometry(parent); + + // Handles special case where the parent is either a layer with no + // geometry or the current root of the view in which case the size + // of the graph's container will be used. + if (this.graph.container != null && ((pgeo == null && + model.isLayer(parent)) || parent == this.graph.getView().currentRoot)) + { + var width = this.graph.container.offsetWidth - 1; + var height = this.graph.container.offsetHeight - 1; + pgeo = new mxRectangle(0, 0, width, height); + } + + return pgeo; +}; + +/** + * Function: execute + * + * Implements . + * + * Only children where returns false are taken into + * account. + */ +mxStackLayout.prototype.execute = function(parent) +{ + if (parent != null) + { + var pgeo = this.getParentSize(parent); + var horizontal = this.isHorizontal(); + var model = this.graph.getModel(); + var fillValue = null; + + if (pgeo != null) + { + fillValue = (horizontal) ? pgeo.height - this.marginTop - this.marginBottom : + pgeo.width - this.marginLeft - this.marginRight; + } + + fillValue -= 2 * this.spacing + 2 * this.border; + var x0 = this.x0 + this.border + this.marginLeft; + var y0 = this.y0 + this.border + this.marginTop; + + // Handles swimlane start size + if (this.graph.isSwimlane(parent)) + { + // Uses computed style to get latest + var style = this.graph.getCellStyle(parent); + var start = mxUtils.getNumber(style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_STARTSIZE); + var horz = mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, true) == 1; + + if (pgeo != null) + { + if (horz) + { + start = Math.min(start, pgeo.height); + } + else + { + start = Math.min(start, pgeo.width); + } + } + + if (horizontal == horz) + { + fillValue -= start; + } + + if (horz) + { + y0 += start; + } + else + { + x0 += start; + } + } + + model.beginUpdate(); + try + { + var tmp = 0; + var last = null; + var lastValue = 0; + var lastChild = null; + var childCount = model.getChildCount(parent); + + for (var i = 0; i < childCount; i++) + { + var child = model.getChildAt(parent, i); + + if (!this.isVertexIgnored(child) && this.isVertexMovable(child)) + { + var geo = model.getGeometry(child); + + if (geo != null) + { + geo = geo.clone(); + + if (this.wrap != null && last != null) + { + if ((horizontal && last.x + last.width + + geo.width + 2 * this.spacing > this.wrap) || + (!horizontal && last.y + last.height + + geo.height + 2 * this.spacing > this.wrap)) + { + last = null; + + if (horizontal) + { + y0 += tmp + this.spacing; + } + else + { + x0 += tmp + this.spacing; + } + + tmp = 0; + } + } + + tmp = Math.max(tmp, (horizontal) ? geo.height : geo.width); + var sw = 0; + + if (!this.borderCollapse) + { + var childStyle = this.graph.getCellStyle(child); + sw = mxUtils.getNumber(childStyle, mxConstants.STYLE_STROKEWIDTH, 1); + } + + if (last != null) + { + if (horizontal) + { + geo.x = lastValue + this.spacing + Math.floor(sw / 2); + } + else + { + geo.y = lastValue + this.spacing + Math.floor(sw / 2); + } + } + else if (!this.keepFirstLocation) + { + if (horizontal) + { + geo.x = x0; + } + else + { + geo.y = y0; + } + } + + if (horizontal) + { + geo.y = y0; + } + else + { + geo.x = x0; + } + + if (this.fill && fillValue != null) + { + if (horizontal) + { + geo.height = fillValue; + } + else + { + geo.width = fillValue; + } + } + + this.setChildGeometry(child, geo); + lastChild = child; + last = geo; + + if (horizontal) + { + lastValue = last.x + last.width + Math.floor(sw / 2); + } + else + { + lastValue = last.y + last.height + Math.floor(sw / 2); + } + } + } + } + + if (this.resizeParent && pgeo != null && last != null && !this.graph.isCellCollapsed(parent)) + { + this.updateParentGeometry(parent, pgeo, last); + } + else if (this.resizeLast && pgeo != null && last != null && lastChild != null) + { + if (horizontal) + { + last.width = pgeo.width - last.x - this.spacing - this.marginRight - this.marginLeft; + } + else + { + last.height = pgeo.height - last.y - this.spacing - this.marginBottom; + } + + this.setChildGeometry(lastChild, last); + } + } + finally + { + model.endUpdate(); + } + } +}; + +/** + * Function: execute + * + * Implements . + * + * Only children where returns false are taken into + * account. + */ +mxStackLayout.prototype.setChildGeometry = function(child, geo) +{ + var geo2 = this.graph.getCellGeometry(child); + + if (geo2 == null || geo.x != geo2.x || geo.y != geo2.y || + geo.width != geo2.width || geo.height != geo2.height) + { + this.graph.getModel().setGeometry(child, geo); + } +}; + +/** + * Function: execute + * + * Implements . + * + * Only children where returns false are taken into + * account. + */ +mxStackLayout.prototype.updateParentGeometry = function(parent, pgeo, last) +{ + var horizontal = this.isHorizontal(); + var model = this.graph.getModel(); + + var pgeo2 = pgeo.clone(); + + if (horizontal) + { + var tmp = last.x + last.width + this.spacing + this.marginRight; + + if (this.resizeParentMax) + { + pgeo2.width = Math.max(pgeo2.width, tmp); + } + else + { + pgeo2.width = tmp; + } + } + else + { + var tmp = last.y + last.height + this.spacing + this.marginBottom; + + if (this.resizeParentMax) + { + pgeo2.height = Math.max(pgeo2.height, tmp); + } + else + { + pgeo2.height = tmp; + } + } + + if (pgeo.x != pgeo2.x || pgeo.y != pgeo2.y || + pgeo.width != pgeo2.width || pgeo.height != pgeo2.height) + { + model.setGeometry(parent, pgeo2); + } +};