UNPKG

devexpress-diagram

Version:

DevExpress Diagram Control

354 lines (330 loc) 17.7 kB
import { IReadOnlyChangesListener, DiagramSettings } from "../../Settings"; import { ConnectionTargetVisualizer } from "./ConnectionTargetVisualizer"; import { ContainerTargetVisualizer } from "./ContainerTargetVisualizer"; import { ExtensionLinesVisualizer, ExtensionLineType } from "./ExtensionLinesVisualizer"; import { IVisualizersListener, IEventManager } from "../EventManager"; import { DiagramModel } from "../../Model/Model"; import { Selection } from "../../Selection/Selection"; import { DiagramMouseEvent, DiagramEvent, MouseEventElementType, DiagramFocusEvent, IMouseOperationsListener } from "../Event"; import { DiagramItem, ItemKey } from "../../Model/DiagramItem"; import { ConnectionPointInfo, ConnectionPointsVisualizer } from "./ConnectionPointsVisualizer"; import { Shape } from "../../Model/Shapes/Shape"; import { EventDispatcher } from "../../Utils"; import { Rectangle } from "@devexpress/utils/lib/geometry/rectangle"; import { Segment } from "@devexpress/utils/lib/geometry/segment"; import { Point } from "@devexpress/utils/lib/geometry/point"; import { CanvasSelectionManager } from "../../Render/CanvasSelectionManager"; import { ModelUtils } from "../../Model/ModelUtils"; import { ResizeInfoVisualizer } from "./ResizeInfoVisualizer"; import { SelectionRectVisualizer } from "./SelectionRectVisualizer"; import { BatchUpdatableObject } from "@devexpress/utils/lib/class/batch-updatable"; import { DiagramLocalizationService } from "../../LocalizationService"; export class VisualizerManager extends BatchUpdatableObject implements IReadOnlyChangesListener, IMouseOperationsListener, IVisualizerManager { protected connectionPointsVisualizer: ConnectionPointsVisualizer; protected connectionTargetVisualizer: ConnectionTargetVisualizer; protected containerTargetVisualizer: ContainerTargetVisualizer; protected extensionLinesVisualizer: ExtensionLinesVisualizer; protected resizeInfoVisualizer: ResizeInfoVisualizer; protected selectionRectangleVisualizer: SelectionRectVisualizer; onVisualizersUpdate: EventDispatcher<IVisualizersListener> = new EventDispatcher(); constructor( protected selection: Selection, protected model: DiagramModel, protected eventManager: IEventManager, protected settings: DiagramSettings, protected readOnly: boolean = settings.readOnly) { super(); this.connectionPointsVisualizer = new ConnectionPointsVisualizer(this.onVisualizersUpdate); this.connectionPointsVisualizer = new ConnectionPointsVisualizer(this.onVisualizersUpdate); this.connectionTargetVisualizer = new ConnectionTargetVisualizer(this.onVisualizersUpdate); this.containerTargetVisualizer = new ContainerTargetVisualizer(this.onVisualizersUpdate); this.extensionLinesVisualizer = new ExtensionLinesVisualizer(this.onVisualizersUpdate); this.resizeInfoVisualizer = new ResizeInfoVisualizer(this.onVisualizersUpdate); this.selectionRectangleVisualizer = new SelectionRectVisualizer(this.onVisualizersUpdate); } initialize(model: DiagramModel) { this.model = model; } onMouseDown(evt: DiagramMouseEvent) { } onMouseUp(evt: DiagramMouseEvent) { } onMouseEnter(evt: DiagramEvent) { } onMouseLeave(evt: DiagramEvent) { this.resetConnectionPoints(); this.resetConnectionTarget(); this.resetExtensionLines(); this.resetContainerTarget(); this.resetResizeInfo(); this.resetSelectionRectangle(); } onBlur(evt: DiagramFocusEvent) { } onFocus(evt: DiagramEvent) { } updateConnections(item: DiagramItem, type: MouseEventElementType, value: any) { let pointIndex = -1; if(value && type === MouseEventElementType.ShapeConnectionPoint) pointIndex = parseInt(value); const preventShowOutside = item && ((!item.allowResizeHorizontally && !item.allowResizeVertically) || item.isLocked); this.setConnectionPoints(item, type, pointIndex, preventShowOutside); } setConnectionPoints(item: DiagramItem, type: MouseEventElementType, pointIndex: number, preventShowOutside?: boolean) { if(!this.eventManager.isFocused()) return; if(item && (type === MouseEventElementType.Shape || type === MouseEventElementType.ShapeResizeBox || type === MouseEventElementType.ShapeConnectionPoint) && item !== undefined) { const key = item.key; const isSelected = this.selection.hasKey(key); const points = item.getConnectionPoints(); this.connectionPointsVisualizer.setPoints(key, points.map(pt => new ConnectionPointInfo(pt, item.getConnectionPointSide(pt))), pointIndex, isSelected && !preventShowOutside && item.rectangle ); } else this.connectionPointsVisualizer.reset(); } setConnectionPointIndex(index: number) { this.connectionPointsVisualizer.setPointIndex(index); } updateConnectionPoints() { const item = this.model.findItem(this.connectionPointsVisualizer.getKey()); if(item !== undefined) this.connectionPointsVisualizer.update(); else this.connectionPointsVisualizer.reset(); } resetConnectionPoints() { this.connectionPointsVisualizer.reset(); } setConnectionTarget(item: DiagramItem, type: MouseEventElementType) { if(item && (type === MouseEventElementType.Shape || type === MouseEventElementType.ShapeConnectionPoint)) this.connectionTargetVisualizer.setTargetRect(item.key, item.rectangle, item.strokeWidth); else this.connectionTargetVisualizer.reset(); } resetConnectionTarget() { this.connectionTargetVisualizer.reset(); } setContainerTarget(item: DiagramItem, type: MouseEventElementType) { if(item && !item.isLocked && (type === MouseEventElementType.Shape) && item.enableChildren) this.containerTargetVisualizer.setTargetRect(item.key, item.rectangle, item.strokeWidth); else this.containerTargetVisualizer.reset(); } resetContainerTarget() { this.containerTargetVisualizer.reset(); } setExtensionLines(items: DiagramItem[]) { if(!this.eventManager.isFocused()) return; this.extensionLinesVisualizer.reset(); const rect = ModelUtils.createRectangle(items.filter(item => item)); this.addPageExtensionLines(rect); this.model.items.forEach(item => { if(items.indexOf(item) > -1) return; if(item instanceof Shape) this.addShapeExtensionLines(item, rect); }); } protected addPageExtensionLines(rect: Rectangle) { const horPages = Math.round(this.model.size.width / this.model.pageWidth); const verPages = Math.round(this.model.size.height / this.model.pageHeight); for(let i = 0; i < horPages; i++) for(let j = 0; j < verPages; j++) { const center = new Point( i * this.model.pageWidth + this.model.pageWidth / 2, j * this.model.pageHeight + this.model.pageHeight / 2 ); if(Math.abs(rect.center.x - center.x) < this.settings.gridSize / 2) { const segment = new Segment(new Point(rect.center.x, 0), new Point(rect.center.x, this.model.size.height)); this.extensionLinesVisualizer.addSegment(ExtensionLineType.HorizontalCenterToPageCenter, segment, ""); } if(Math.abs(rect.center.y - center.y) < this.settings.gridSize / 2) { const segment = new Segment(new Point(0, rect.center.y), new Point(this.model.size.width, rect.center.y)); this.extensionLinesVisualizer.addSegment(ExtensionLineType.VerticalCenterToPageCenter, segment, ""); } if(Math.abs(rect.x - center.x) < this.settings.gridSize / 2) { const segment = new Segment(new Point(rect.x, 0), new Point(rect.x, this.model.size.height)); this.extensionLinesVisualizer.addSegment(ExtensionLineType.LeftToPageCenter, segment, ""); } if(Math.abs(rect.y - center.y) < this.settings.gridSize / 2) { const segment = new Segment(new Point(0, rect.y), new Point(this.model.size.width, rect.y)); this.extensionLinesVisualizer.addSegment(ExtensionLineType.TopToPageCenter, segment, ""); } if(Math.abs(rect.right - center.x) < this.settings.gridSize / 2) { const segment = new Segment(new Point(rect.right, 0), new Point(rect.right, this.model.size.height)); this.extensionLinesVisualizer.addSegment(ExtensionLineType.RightToPageCenter, segment, ""); } if(Math.abs(rect.bottom - center.y) < this.settings.gridSize / 2) { const segment = new Segment(new Point(0, rect.bottom), new Point(this.model.size.width, rect.bottom)); this.extensionLinesVisualizer.addSegment(ExtensionLineType.BottomToPageCenter, segment, ""); } } } protected addShapeExtensionLines(shape: Shape, rect: Rectangle) { const sRect = shape.rectangle; const lwCor = shape.strokeWidth - CanvasSelectionManager.extensionLineWidth; let showDistance = true; let x1nc; let y1nc; let x2nc; let y2nc; let x1; let y1; let x2; let y2; if(rect.right < sRect.x) { x1nc = rect.right; x2nc = sRect.x; x1 = x1nc + lwCor + CanvasSelectionManager.extensionLineOffset; x2 = x2nc - CanvasSelectionManager.extensionLineOffset; } else if(rect.x > sRect.right) { x1nc = rect.x; x2nc = sRect.right; x1 = x1nc - CanvasSelectionManager.extensionLineOffset; x2 = x2nc + lwCor + CanvasSelectionManager.extensionLineOffset; } if(rect.bottom < sRect.y) { y1nc = rect.bottom; y2nc = sRect.y; y1 = y1nc + lwCor + CanvasSelectionManager.extensionLineOffset; y2 = y2nc - CanvasSelectionManager.extensionLineOffset; } else if(rect.y > sRect.bottom) { y1nc = rect.y; y2nc = sRect.bottom; y1 = y1nc - CanvasSelectionManager.extensionLineOffset; y2 = y2nc + lwCor + CanvasSelectionManager.extensionLineOffset; } const eps = this.settings.gridSize / 2; if(x1 !== undefined && x2 !== undefined) { const distanceText = this.getViewUnitText(Math.abs(x1nc - x2nc)); if(Math.abs(rect.center.y - sRect.center.y) < eps) { const segment = new Segment(new Point(x1, rect.center.y), new Point(x2, rect.center.y)); this.extensionLinesVisualizer.addSegment( x1 > x2 ? ExtensionLineType.VerticalCenterAfter : ExtensionLineType.VerticalCenterBefore, segment, showDistance ? distanceText : "" ); showDistance = false; } if(rect.y === sRect.y) { const segment = new Segment(new Point(x1, rect.y), new Point(x2, sRect.y)); this.extensionLinesVisualizer.addSegment( x1 > x2 ? ExtensionLineType.TopToTopAfter : ExtensionLineType.TopToTopBefore, segment, showDistance ? distanceText : "" ); } if(rect.bottom === sRect.bottom) { const segment = new Segment(new Point(x1, rect.bottom + lwCor), new Point(x2, sRect.bottom + lwCor)); this.extensionLinesVisualizer.addSegment( x1 > x2 ? ExtensionLineType.BottomToBottomAfter : ExtensionLineType.BottomToBottomBefore, segment, showDistance ? distanceText : "" ); } if(rect.y === sRect.bottom) { const segment = new Segment(new Point(x1, rect.y), new Point(x2, sRect.bottom + lwCor)); this.extensionLinesVisualizer.addSegment( x1 > x2 ? ExtensionLineType.TopToBottomAfter : ExtensionLineType.TopToBottomBefore, segment, showDistance ? distanceText : "" ); } if(rect.bottom === sRect.y) { const segment = new Segment(new Point(x1, rect.bottom + lwCor), new Point(x2, sRect.y)); this.extensionLinesVisualizer.addSegment( x1 > x2 ? ExtensionLineType.BottomToTopAfter : ExtensionLineType.BottomToTopBefore, segment, showDistance ? distanceText : "" ); } } if(y1 !== undefined && y2 !== undefined) { const distanceText = this.getViewUnitText(Math.abs(y1nc - y2nc)); if(Math.abs(rect.center.x - sRect.center.x) < eps) { const segment = new Segment(new Point(rect.center.x, y1), new Point(rect.center.x, y2)); this.extensionLinesVisualizer.addSegment( y1 > y2 ? ExtensionLineType.HorizontalCenterBelow : ExtensionLineType.HorizontalCenterAbove, segment, showDistance ? distanceText : "" ); showDistance = false; } if(rect.x === sRect.x) { const segment = new Segment(new Point(rect.x, y1), new Point(sRect.x, y2)); this.extensionLinesVisualizer.addSegment( y1 > y2 ? ExtensionLineType.LeftToLeftBelow : ExtensionLineType.LeftToLeftAbove, segment, showDistance ? distanceText : "" ); } if(rect.right === sRect.right) { const segment = new Segment(new Point(rect.right + lwCor, y1), new Point(sRect.right + lwCor, y2)); this.extensionLinesVisualizer.addSegment( y1 > y2 ? ExtensionLineType.RightToRightBelow : ExtensionLineType.RightToRightAbove, segment, showDistance ? distanceText : "" ); } if(rect.x === sRect.right) { const segment = new Segment(new Point(rect.x, y1), new Point(sRect.right + lwCor, y2)); this.extensionLinesVisualizer.addSegment( y1 > y2 ? ExtensionLineType.LeftToRightBelow : ExtensionLineType.LeftToRightAbove, segment, showDistance ? distanceText : "" ); } if(rect.right === sRect.x) { const segment = new Segment(new Point(rect.right + lwCor, y1), new Point(sRect.x, y2)); this.extensionLinesVisualizer.addSegment( y1 > y2 ? ExtensionLineType.RightToLeftBelow : ExtensionLineType.RightToLeftAbove, segment, showDistance ? distanceText : "" ); } } } resetExtensionLines() { this.extensionLinesVisualizer.reset(); } setResizeInfo(items: DiagramItem[]) { const rect = ModelUtils.createRectangle(items); const point = new Point(rect.center.x, rect.bottom + CanvasSelectionManager.resizeInfoOffset); const text = this.getViewUnitText(rect.width) + " x " + this.getViewUnitText(rect.height); this.resizeInfoVisualizer.set(point, text); } resetResizeInfo() { this.resizeInfoVisualizer.reset(); } setSelectionRectangle(rect: Rectangle) { this.selectionRectangleVisualizer.setRectangle(rect); } resetSelectionRectangle() { this.selectionRectangleVisualizer.reset(); } getViewUnitText(value: number) { return ModelUtils.getUnitText(this.settings.viewUnits, DiagramLocalizationService.unitItems, DiagramLocalizationService.formatUnit, value); } notifyReadOnlyChanged(readOnly: boolean) { this.readOnly = readOnly; if(this.readOnly) { this.resetConnectionPoints(); this.resetConnectionTarget(); this.resetExtensionLines(); this.resetContainerTarget(); this.resetResizeInfo(); this.resetSelectionRectangle(); } } notifyDragStart(itemKeys: ItemKey[]) { } notifyDragEnd(itemKeys: ItemKey[]) { } notifyDragScrollStart() {} notifyDragScrollEnd() {} onUpdateUnlocked(occurredEvents: number) { } } export interface IVisualizerManager { resetConnectionTarget(); resetConnectionPoints(); resetExtensionLines(); resetContainerTarget(); resetResizeInfo(); resetSelectionRectangle(); setConnectionTarget(item: DiagramItem, type: MouseEventElementType); setConnectionPoints(item: DiagramItem, type: MouseEventElementType, pointIndex: number, preventShowOutside?: boolean); setConnectionPointIndex(index: number); setExtensionLines(items: DiagramItem[]); setContainerTarget(item: DiagramItem, type: MouseEventElementType); setResizeInfo(items: DiagramItem[]); setSelectionRectangle(rect: Rectangle); updateConnections(item: DiagramItem, type: MouseEventElementType, value: any); }