UNPKG

devexpress-diagram

Version:

DevExpress Diagram Control

234 lines (217 loc) 11.6 kB
import { Style, StrokeStyle } from "../Model/Style"; import { RectanglePrimitive } from "./Primitives/RectaglePrimitive"; import { SvgPrimitive } from "./Primitives/Primitive"; import { PathPrimitiveMoveToCommand, PathPrimitiveLineToCommand, PathPrimitive, PathPrimitiveCommand } from "./Primitives/PathPrimitive"; import { PatternPrimitive } from "./Primitives/PatternPrimitive"; import { ClipPathPrimitive } from "./Primitives/ClipPathPrimitive"; import { CanvasManagerBase } from "./CanvasManagerBase"; import { Rectangle } from "@devexpress/utils/lib/geometry/rectangle"; import { Offsets } from "@devexpress/utils/lib/geometry/offsets"; import { Size } from "@devexpress/utils/lib/geometry/size"; import { Point } from "@devexpress/utils/lib/geometry/point"; import { IModelSizeListener, IModelChangesListener } from "../Model/ModelManipulator"; import { ItemChange } from "../Model/ModelChange"; import { IViewChangesListener, AutoZoomMode } from "../Settings"; import { ICanvasViewListener } from "./CanvasViewManager"; import { RenderUtils } from "./Utils"; import { DOMManipulator } from "./DOMManipulator"; import { ColorUtils } from "@devexpress/utils/lib/utils/color"; import { UnitConverter } from "@devexpress/utils/lib/class/unit-converter"; const GRID_PAGES_LINEWIDTH = 2; export class CanvasPageManager extends CanvasManagerBase implements IModelSizeListener, IModelChangesListener, ICanvasViewListener, IViewChangesListener { private backgroundContainer: SVGElement; private gridRectElement: SVGElement; private gridPatternElement: SVGElement; private pagesGridClipPathElement: SVGElement; private pagesGridPatternElement: SVGElement; private pageSize: Size; private gridSize: number; private gridVisible: boolean; private modelSize: Size; private simpleView: boolean; private pageColor: number; private canvasViewOffset: Point; private snapPoint: Point = new Point(0, 0); readonly gridPatternId = RenderUtils.generateSvgElementId("gridPattern"); readonly pagesGridPatternId = RenderUtils.generateSvgElementId("pagesGridPattern"); readonly pagesGridClipId = RenderUtils.generateSvgElementId("pagesGridClip"); constructor(parent: SVGElement, settings: ICanvasPageManagerSettings, dom: DOMManipulator) { super(settings.zoomLevel, dom); this.pageColor = settings.pageColor; this.modelSize = settings.modelSize; this.simpleView = settings.simpleView; this.gridSize = settings.gridSize; this.gridVisible = settings.gridVisible; this.pageSize = settings.pageLandscape ? new Size(settings.pageSize.height, settings.pageSize.width) : settings.pageSize; this.canvasViewOffset = new Point(0, 0); this.initContainers(parent); } private initContainers(parent: SVGElement) { this.backgroundContainer = parent; } public redraw() { this.redrawPage(this.pageColor); this.redrawGrid(); } private redrawPage(color: number) { const style = new Style(); style["fill"] = ColorUtils.colorToHash(color); this.getOrCreateElement("page-bg", new RectanglePrimitive(0, 0, "100%", "100%", style, "page"), this.backgroundContainer); this.createTextFloodFilter("page-bg-textflood-filter", this.backgroundContainer, color); } private redrawGrid() { this.updateGridElements(); this.updatePagesGridElements(); } private getGridRectElement(primitive: SvgPrimitive<SVGElement>) { if(this.gridRectElement === undefined) this.gridRectElement = this.createPrimitiveElement(primitive, this.backgroundContainer); return this.gridRectElement; } private getGridPatternElement(primitive: SvgPrimitive<SVGElement>) { if(this.gridPatternElement === undefined) this.gridPatternElement = this.createPrimitiveElement(primitive, this.backgroundContainer); return this.gridPatternElement; } private updateGridElements() { const gridRectPrimitive = new RectanglePrimitive("0", "0", "100%", "100%", null, "grid", null, element => { element.style.setProperty("fill", RenderUtils.getUrlPathById(this.gridPatternId)); }); const rectEl = this.getGridRectElement(gridRectPrimitive); if(!this.gridVisible) rectEl.style.display = "none"; else { rectEl.style.display = ""; this.changePrimitiveElement(gridRectPrimitive, rectEl); const absGridSize = UnitConverter.twipsToPixelsF(this.gridSize) * this.actualZoom; const sizes = [0, 1, 2, 3, 4].map(i => Math.round(absGridSize * i)); const outerPathCommands = [ new PathPrimitiveMoveToCommand(sizes[4].toString(), "0"), new PathPrimitiveLineToCommand(sizes[4].toString(), sizes[4].toString()), new PathPrimitiveLineToCommand("0", sizes[4].toString()) ]; const innerPathCommands = []; for(let i = 1; i <= 3; i++) { innerPathCommands.push(new PathPrimitiveMoveToCommand(sizes[i].toString(), "0")); innerPathCommands.push(new PathPrimitiveLineToCommand(sizes[i].toString(), sizes[4].toString())); } for(let i = 1; i <= 3; i++) { innerPathCommands.push(new PathPrimitiveMoveToCommand("0", sizes[i].toString())); innerPathCommands.push(new PathPrimitiveLineToCommand(sizes[4].toString(), sizes[i].toString())); } const commonSize = absGridSize * 4; const canvasViewOffset = this.simpleView ? this.canvasViewOffset : Point.zero(); const gridPatternPrimitive = new PatternPrimitive(this.gridPatternId, [ this.createGridPathPrimitive(outerPathCommands, "grid-outer-line"), this.createGridPathPrimitive(innerPathCommands, "grid-inner-line") ], this.createGridPatternPrimitivePosition(canvasViewOffset.x, this.snapPoint.x, commonSize), this.createGridPatternPrimitivePosition(canvasViewOffset.y, this.snapPoint.y, commonSize), commonSize.toString(), commonSize.toString()); this.changePrimitiveElement(gridPatternPrimitive, this.getGridPatternElement(gridPatternPrimitive)); } } private createGridPatternPrimitivePosition(offset: number, coord: number, commonSize: number) : string { return (((offset + coord * this.actualZoom) % commonSize - commonSize) % commonSize).toString(); } private createGridPathPrimitive(commands: PathPrimitiveCommand[], className: string) : PathPrimitive { return new PathPrimitive(commands, StrokeStyle.default1pxNegativeOffsetInstance, className); } private getPagesGridRectElement(primitive: SvgPrimitive<SVGElement>) { return this.getOrCreateElement("grid-pages-rect", primitive, this.backgroundContainer); } private getPagesGridClipPathElement(primitive: SvgPrimitive<SVGElement>) { if(this.pagesGridClipPathElement === undefined) this.pagesGridClipPathElement = this.createPrimitiveElement(primitive, this.backgroundContainer); return this.pagesGridClipPathElement; } private getPagesGridPatternElement(primitive: SvgPrimitive<SVGElement>) { if(this.pagesGridPatternElement === undefined) this.pagesGridPatternElement = this.createPrimitiveElement(primitive, this.backgroundContainer); return this.pagesGridPatternElement; } private updatePagesGridElements() { const pageAbsSize = this.getAbsoluteSize(this.pageSize); const rectPrimitive = new RectanglePrimitive("0", "0", "100%", "100%", null, "grid-page", this.pagesGridClipId, element => { element.style.setProperty("fill", RenderUtils.getUrlPathById(this.pagesGridPatternId)); element.style.setProperty("display", this.simpleView ? "none" : ""); } ); this.getPagesGridRectElement(rectPrimitive); if(!this.simpleView) { const modelSize = this.modelSize.clone().multiply(this.actualZoom, this.actualZoom); const pageGridPathCommands = [ new PathPrimitiveMoveToCommand((pageAbsSize.width - GRID_PAGES_LINEWIDTH / 2).toString(), "0"), new PathPrimitiveLineToCommand( (pageAbsSize.width - GRID_PAGES_LINEWIDTH / 2).toString(), (pageAbsSize.height - GRID_PAGES_LINEWIDTH / 2).toString() ), new PathPrimitiveLineToCommand("0", (pageAbsSize.height - GRID_PAGES_LINEWIDTH / 2).toString()) ]; const pagesGridPatternPrimitive = new PatternPrimitive(this.pagesGridPatternId, [ new PathPrimitive(pageGridPathCommands, null, "pages-grid-line") ], 0, 0, pageAbsSize.width.toString(), pageAbsSize.height.toString()); this.changePrimitiveElement(pagesGridPatternPrimitive, this.getPagesGridPatternElement(pagesGridPatternPrimitive)); const pagesGridClipPathPrimitive = new ClipPathPrimitive(this.pagesGridClipId, [ new RectanglePrimitive(0, 0, (UnitConverter.twipsToPixelsF(modelSize.width) - GRID_PAGES_LINEWIDTH * 2).toString(), (UnitConverter.twipsToPixelsF(modelSize.height) - GRID_PAGES_LINEWIDTH * 2).toString()) ]); this.changePrimitiveElement(pagesGridClipPathPrimitive, this.getPagesGridClipPathElement(pagesGridClipPathPrimitive)); } } notifyModelSizeChanged(size: Size, offset?: Offsets) { this.modelSize = size.clone(); this.redraw(); } notifyModelRectangleChanged(rectangle: Rectangle) { } notifySnapPointPositionChanged(point: Point) { this.snapPoint = point.clone().applyConverter(UnitConverter.twipsToPixelsF); this.redrawGrid(); } notifyPageColorChanged(color: number) { this.pageColor = color; this.redrawPage(this.pageColor); } notifyModelChanged(changes: ItemChange[]) { } notifyPageSizeChanged(pageSize: Size, pageLandscape: boolean) { this.pageSize = pageLandscape ? new Size(pageSize.height, pageSize.width) : pageSize.clone(); this.redraw(); } notifyActualZoomChanged(actualZoom: number) { this.actualZoom = actualZoom; this.redraw(); } notifyViewAdjusted(canvasViewOffset: Point) { if(!this.canvasViewOffset.equals(canvasViewOffset)) { this.canvasViewOffset = canvasViewOffset; if(this.simpleView) this.redraw(); } } notifyViewChanged(simpleView: boolean) { this.simpleView = simpleView; this.redraw(); } notifyGridChanged(showGrid: boolean, gridSize: number) { this.gridVisible = showGrid; this.gridSize = gridSize; this.redraw(); } } export interface ICanvasPageManagerSettings { modelSize: Size; rectangle: Rectangle; zoomLevel: number; simpleView: boolean; autoZoom: AutoZoomMode; readOnly: boolean; contextMenuEnabled: boolean; pageColor: number; pageLandscape: boolean; pageSize: Size; gridVisible: boolean; gridSize: number; }