feat: add GraphDataModel.batchUpdate method (#213)

The method already exists in the `Graph` class. It needs to be made
available in `GraphDataModel` as well, to simplify the transaction
syntax.
Start using it in some places to simplify the syntax and also use
`Graph.batchUpdate`.
development
Thomas Bouffard 2023-07-06 07:02:20 +02:00 committed by GitHub
parent 0c7a68bcc1
commit 992f3af63d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 63 additions and 100 deletions

View File

@ -329,8 +329,7 @@ export const setCellStyles = (
value: any value: any
) => { ) => {
if (cells.length > 0) { if (cells.length > 0) {
model.beginUpdate(); model.batchUpdate(() => {
try {
for (let i = 0; i < cells.length; i += 1) { for (let i = 0; i < cells.length; i += 1) {
const cell = cells[i]; const cell = cells[i];
@ -341,9 +340,7 @@ export const setCellStyles = (
model.setStyle(cell, style); model.setStyle(cell, style);
} }
} }
} finally { });
model.endUpdate();
}
} }
}; };
@ -377,8 +374,7 @@ export const setCellStyleFlags = (
value: boolean value: boolean
) => { ) => {
if (cells.length > 0) { if (cells.length > 0) {
model.beginUpdate(); model.batchUpdate(() => {
try {
for (let i = 0; i < cells.length; i += 1) { for (let i = 0; i < cells.length; i += 1) {
const cell = cells[i]; const cell = cells[i];
@ -387,9 +383,7 @@ export const setCellStyleFlags = (
model.setStyle(cell, style); model.setStyle(cell, style);
} }
} }
} finally { });
model.endUpdate();
}
} }
}; };
@ -397,7 +391,7 @@ export const setCellStyleFlags = (
* Sets or removes the given key from the specified style and returns the * Sets or removes the given key from the specified style and returns the
* new style. If value is null then the flag is toggled. * new style. If value is null then the flag is toggled.
* *
* @param style String of the form [(stylename|key=value);]. * @param style The style of the Cell.
* @param key Key of the style to be changed. * @param key Key of the style to be changed.
* @param flag Integer for the bit to be changed. * @param flag Integer for the bit to be changed.
* @param value Optional boolean value for the given flag. * @param value Optional boolean value for the given flag.

View File

@ -484,20 +484,14 @@ class Graph extends EventSource {
getContainsValidationErrorsResource = () => this.containsValidationErrorsResource; getContainsValidationErrorsResource = () => this.containsValidationErrorsResource;
/** /**
* Shortcut for updating the model in a transaction. * Updates the model in a transaction.
* *
* @param fn the update to be performed in the transaction. * @param fn the update to be performed in the transaction.
* *
* @see {@link GraphDataModel.beginUpdate} * @see {@link GraphDataModel.batchUpdate}
* @see {@link GraphDataModel.endUpdate}
*/ */
batchUpdate(fn: () => void) { batchUpdate(fn: () => void) {
this.getDataModel().beginUpdate(); this.getDataModel().batchUpdate(fn);
try {
fn();
} finally {
this.getDataModel().endUpdate();
}
} }
/** /**

View File

@ -956,6 +956,31 @@ export class GraphDataModel extends EventSource {
this.endUpdate(); this.endUpdate();
} }
/**
* Updates the model in a transaction.
* This is a shortcut to the usage of {@link beginUpdate} and the {@link endUpdate} methods.
*
* ```javascript
* const model = graph.getDataModel();
* const parent = graph.getDefaultParent();
* const index = model.getChildCount(parent);
* model.batchUpdate(() => {
* model.add(parent, v1, index);
* model.add(parent, v2, index+1);
* });
* ```
*
* @param fn the update to be performed in the transaction.
*/
batchUpdate(fn: () => void) {
this.beginUpdate();
try {
fn();
} finally {
this.endUpdate();
}
}
/** /**
* Increments the {@link updateLevel} by one. The event notification * Increments the {@link updateLevel} by one. The event notification
* is queued until {@link updateLevel} reaches 0 by use of * is queued until {@link updateLevel} reaches 0 by use of
@ -971,9 +996,9 @@ export class GraphDataModel extends EventSource {
* and {@link endUpdate} calls as shown here: * and {@link endUpdate} calls as shown here:
* *
* ```javascript * ```javascript
* var model = graph.getDataModel(); * const model = graph.getDataModel();
* var parent = graph.getDefaultParent(); * const parent = graph.getDefaultParent();
* var index = model.getChildCount(parent); * const index = model.getChildCount(parent);
* model.beginUpdate(); * model.beginUpdate();
* try * try
* { * {

View File

@ -1727,11 +1727,9 @@ class EdgeHandler {
isClone: boolean, isClone: boolean,
me: InternalMouseEvent me: InternalMouseEvent
) { ) {
const model = this.graph.getDataModel();
const parent = edge.getParent(); const parent = edge.getParent();
model.beginUpdate(); this.graph.batchUpdate(() => {
try {
let constraint = this.constraintHandler.currentConstraint; let constraint = this.constraintHandler.currentConstraint;
if (constraint == null) { if (constraint == null) {
@ -1739,9 +1737,7 @@ class EdgeHandler {
} }
this.graph.connectCell(edge, terminal, isSource, constraint); this.graph.connectCell(edge, terminal, isSource, constraint);
} finally { });
model.endUpdate();
}
return edge; return edge;
} }
@ -1752,8 +1748,7 @@ class EdgeHandler {
changeTerminalPoint(edge: Cell, point: Point, isSource: boolean, clone: boolean) { changeTerminalPoint(edge: Cell, point: Point, isSource: boolean, clone: boolean) {
const model = this.graph.getDataModel(); const model = this.graph.getDataModel();
model.beginUpdate(); model.batchUpdate(() => {
try {
if (clone) { if (clone) {
const parent = edge.getParent(); const parent = edge.getParent();
const terminal = edge.getTerminal(!isSource); const terminal = edge.getTerminal(!isSource);
@ -1770,9 +1765,7 @@ class EdgeHandler {
model.setGeometry(edge, geo); model.setGeometry(edge, geo);
this.graph.connectCell(edge, null, isSource, new ConnectionConstraint(null)); this.graph.connectCell(edge, null, isSource, new ConnectionConstraint(null));
} }
} finally { });
model.endUpdate();
}
return edge; return edge;
} }
@ -1782,8 +1775,7 @@ class EdgeHandler {
*/ */
changePoints(edge: Cell, points: Point[], clone: boolean) { changePoints(edge: Cell, points: Point[], clone: boolean) {
const model = this.graph.getDataModel(); const model = this.graph.getDataModel();
model.beginUpdate(); model.batchUpdate(() => {
try {
if (clone) { if (clone) {
const parent = edge.getParent(); const parent = edge.getParent();
const source = edge.getTerminal(true); const source = edge.getTerminal(true);
@ -1802,9 +1794,7 @@ class EdgeHandler {
model.setGeometry(edge, geo); model.setGeometry(edge, geo);
} }
} finally { });
model.endUpdate();
}
return edge; return edge;
} }
@ -2194,7 +2184,7 @@ class EdgeHandler {
this.destroyBends(this.virtualBends); this.destroyBends(this.virtualBends);
this.virtualBends = this.createVirtualBends(); this.virtualBends = this.createVirtualBends();
} }
if (this.customHandles) { if (this.customHandles) {
this.destroyBends(this.customHandles); this.destroyBends(this.customHandles);
this.customHandles = this.createCustomHandles(); this.customHandles = this.createCustomHandles();
@ -2272,7 +2262,7 @@ class EdgeHandler {
this.virtualBends = []; this.virtualBends = [];
} }
if (this.customHandles){ if (this.customHandles) {
this.destroyBends(this.customHandles); this.destroyBends(this.customHandles);
this.customHandles = []; this.customHandles = [];
} }

View File

@ -86,13 +86,9 @@ class CircleLayout extends GraphLayout {
* Implements {@link GraphLayout#execute}. * Implements {@link GraphLayout#execute}.
*/ */
execute(parent: Cell) { execute(parent: Cell) {
const model = this.graph.getDataModel();
// Moves the vertices to build a circle. Makes sure the // Moves the vertices to build a circle. Makes sure the
// radius is large enough for the vertices to not // radius is large enough for the vertices to not
// overlap // overlap
model.beginUpdate();
this.graph.batchUpdate(() => { this.graph.batchUpdate(() => {
// Gets all vertices inside the parent and finds // Gets all vertices inside the parent and finds
// the maximum dimension of the largest vertex // the maximum dimension of the largest vertex

View File

@ -80,16 +80,11 @@ class CompositeLayout extends GraphLayout {
* single transaction. * single transaction.
*/ */
execute(parent: Cell): void { execute(parent: Cell): void {
const model = this.graph.getDataModel(); this.graph.batchUpdate(() => {
model.beginUpdate();
try {
for (let i = 0; i < this.layouts.length; i += 1) { for (let i = 0; i < this.layouts.length; i += 1) {
this.layouts[i].execute.apply(this.layouts[i], [parent]); this.layouts[i].execute.apply(this.layouts[i], [parent]);
} }
} finally { });
model.endUpdate();
}
} }
} }

View File

@ -75,13 +75,10 @@ class EdgeLabelLayout extends GraphLayout {
* @param e edges * @param e edges
*/ */
placeLabels(v: CellState[], e: CellState[]): void { placeLabels(v: CellState[], e: CellState[]): void {
const model = this.graph.getDataModel();
// Moves the vertices to build a circle. Makes sure the // Moves the vertices to build a circle. Makes sure the
// radius is large enough for the vertices to not // radius is large enough for the vertices to not
// overlap // overlap
model.beginUpdate(); this.graph.batchUpdate(() => {
try {
for (let i = 0; i < e.length; i += 1) { for (let i = 0; i < e.length; i += 1) {
const edge = e[i]; const edge = e[i];
@ -95,9 +92,7 @@ class EdgeLabelLayout extends GraphLayout {
} }
} }
} }
} finally { });
model.endUpdate();
}
} }
/** /**

View File

@ -170,7 +170,6 @@ class MxFastOrganicLayout extends GraphLayout {
* given parent where <isVertexIgnored> returns false. * given parent where <isVertexIgnored> returns false.
*/ */
execute(parent: Cell) { execute(parent: Cell) {
const model = this.graph.getDataModel();
this.vertexArray = []; this.vertexArray = [];
let cells = this.graph.getChildVertices(parent); let cells = this.graph.getChildVertices(parent);
@ -230,8 +229,7 @@ class MxFastOrganicLayout extends GraphLayout {
// Moves cell location back to top-left from center locations used in // Moves cell location back to top-left from center locations used in
// algorithm, resetting the edge points is part of the transaction // algorithm, resetting the edge points is part of the transaction
model.beginUpdate(); this.graph.batchUpdate(() => {
try {
for (let i = 0; i < n; i += 1) { for (let i = 0; i < n; i += 1) {
this.dispX[i] = 0; this.dispX[i] = 0;
this.dispY[i] = 0; this.dispY[i] = 0;
@ -341,9 +339,7 @@ class MxFastOrganicLayout extends GraphLayout {
} }
this.graph.moveCells(this.vertexArray, dx, dy); this.graph.moveCells(this.vertexArray, dx, dy);
} finally { });
model.endUpdate();
}
} }
/** /**

View File

@ -183,7 +183,6 @@ class HierarchicalLayout extends GraphLayout {
*/ */
execute(parent: Cell, roots: Cell[] | Cell | null = null): void { execute(parent: Cell, roots: Cell[] | Cell | null = null): void {
this.parent = parent; this.parent = parent;
const { model } = this.graph;
this.edgesCache = new Dictionary(); this.edgesCache = new Dictionary();
this.edgeSourceTermCache = new Dictionary(); this.edgeSourceTermCache = new Dictionary();
this.edgesTargetTermCache = new Dictionary(); this.edgesTargetTermCache = new Dictionary();
@ -234,8 +233,8 @@ class HierarchicalLayout extends GraphLayout {
this.roots = rootsCopy; this.roots = rootsCopy;
} }
model.beginUpdate(); const { model } = this.graph;
try { model.batchUpdate(() => {
this.run(parent); this.run(parent);
if (this.resizeParent && !parent.isCollapsed()) { if (this.resizeParent && !parent.isCollapsed()) {
@ -253,9 +252,7 @@ class HierarchicalLayout extends GraphLayout {
model.setGeometry(parent, geo); model.setGeometry(parent, geo);
} }
} }
} finally { });
model.endUpdate();
}
} }
/** /**

View File

@ -361,8 +361,7 @@ class LayoutManager extends EventSource {
// Invokes the layouts while removing duplicates // Invokes the layouts while removing duplicates
const model = this.getGraph().getDataModel(); const model = this.getGraph().getDataModel();
model.beginUpdate(); model.batchUpdate(() => {
try {
let last = null; let last = null;
for (const cell of cells) { for (const cell of cells) {
@ -373,9 +372,7 @@ class LayoutManager extends EventSource {
} }
this.fireEvent(new EventObject(InternalEvent.LAYOUT_CELLS, { cells })); this.fireEvent(new EventObject(InternalEvent.LAYOUT_CELLS, { cells }));
} finally { });
model.endUpdate();
}
} }
} }

View File

@ -86,8 +86,7 @@ class ParallelEdgeLayout extends GraphLayout {
execute(parent: Cell, cells: Cell[] | null = null): void { execute(parent: Cell, cells: Cell[] | null = null): void {
const lookup = this.findParallels(parent, cells); const lookup = this.findParallels(parent, cells);
this.graph.model.beginUpdate(); this.graph.batchUpdate(() => {
try {
for (const i in lookup) { for (const i in lookup) {
const parallels = lookup[i]; const parallels = lookup[i];
@ -95,9 +94,7 @@ class ParallelEdgeLayout extends GraphLayout {
this.layout(parallels); this.layout(parallels);
} }
} }
} finally { });
this.graph.model.endUpdate();
}
} }
/** /**

View File

@ -207,18 +207,13 @@ class SwimlaneManager extends EventSource {
*/ */
cellsAdded(cells: Cell[]) { cellsAdded(cells: Cell[]) {
if (cells.length > 0) { if (cells.length > 0) {
const model = this.graph.getDataModel(); this.graph.batchUpdate(() => {
model.beginUpdate();
try {
for (const cell of cells) { for (const cell of cells) {
if (!this.isSwimlaneIgnored(cell)) { if (!this.isSwimlaneIgnored(cell)) {
this.swimlaneAdded(cell); this.swimlaneAdded(cell);
} }
} }
} finally { });
model.endUpdate();
}
} }
} }
@ -260,10 +255,7 @@ class SwimlaneManager extends EventSource {
*/ */
cellsResized(cells: Cell[]) { cellsResized(cells: Cell[]) {
if (cells.length > 0) { if (cells.length > 0) {
const model = this.getGraph().getDataModel(); this.graph.batchUpdate(() => {
model.beginUpdate();
try {
// Finds the top-level swimlanes and adds offsets // Finds the top-level swimlanes and adds offsets
for (const cell of cells) { for (const cell of cells) {
if (!this.isSwimlaneIgnored(cell)) { if (!this.isSwimlaneIgnored(cell)) {
@ -291,9 +283,7 @@ class SwimlaneManager extends EventSource {
} }
} }
} }
} finally { });
model.endUpdate();
}
} }
} }
@ -307,8 +297,7 @@ class SwimlaneManager extends EventSource {
resizeSwimlane(swimlane: Cell, w: number, h: number, parentHorizontal: boolean) { resizeSwimlane(swimlane: Cell, w: number, h: number, parentHorizontal: boolean) {
const model = this.graph.getDataModel(); const model = this.graph.getDataModel();
model.beginUpdate(); model.batchUpdate(() => {
try {
const horizontal = this.isCellHorizontal(swimlane); const horizontal = this.isCellHorizontal(swimlane);
if (!this.isSwimlaneIgnored(swimlane)) { if (!this.isSwimlaneIgnored(swimlane)) {
@ -344,9 +333,7 @@ class SwimlaneManager extends EventSource {
const child = swimlane.getChildAt(i); const child = swimlane.getChildAt(i);
this.resizeSwimlane(child, w, h, horizontal); this.resizeSwimlane(child, w, h, horizontal);
} }
} finally { });
model.endUpdate();
}
} }
/** /**