Decouple project-map from map

This commit is contained in:
ziajka 2018-11-05 14:21:53 +01:00
parent 48dcfcbf1c
commit 1d1cf149bb
11 changed files with 149 additions and 90 deletions

View File

@ -10,7 +10,7 @@ import { LayersManager } from './managers/layers-manager';
import { MapChangeDetectorRef } from './services/map-change-detector-ref';
import { GraphLayout } from './widgets/graph-layout';
import { LinksWidget } from './widgets/links';
import { NodesWidget, NodeEvent } from './widgets/nodes';
import { NodesWidget } from './widgets/nodes';
import { DrawingsWidget } from './widgets/drawings';
import { DrawingLineWidget } from './widgets/drawing-line';
import { SelectionTool } from './tools/selection-tool';
@ -24,6 +24,7 @@ import { ImageDrawingWidget } from './widgets/drawings/image-drawing';
import { RectDrawingWidget } from './widgets/drawings/rect-drawing';
import { TextDrawingWidget } from './widgets/drawings/text-drawing';
import { LineDrawingWidget } from './widgets/drawings/line-drawing';
import { Context } from './models/context';
@NgModule({
imports: [
@ -55,7 +56,8 @@ import { LineDrawingWidget } from './widgets/drawings/line-drawing';
ImageDrawingWidget,
LineDrawingWidget,
RectDrawingWidget,
TextDrawingWidget
TextDrawingWidget,
Context
],
exports: [MapComponent]
})

View File

@ -13,6 +13,9 @@ import { Drawing } from "../../models/drawing";
import { Symbol } from '../../../models/symbol';
import { NodeEvent, NodesWidget } from '../../widgets/nodes';
import { Subscription } from 'rxjs';
import { InterfaceLabelWidget } from '../../widgets/interface-label';
import { SelectionTool } from '../../tools/selection-tool';
import { MovingTool } from '../../tools/moving-tool';
@Component({
@ -34,7 +37,8 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
private d3: D3;
private parentNativeElement: any;
private svg: Selection<SVGSVGElement, any, null, undefined>;
private graphContext: Context;
private isReady = false;
private onNodeDraggingSubscription: Subscription;
@ -43,9 +47,13 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
};
constructor(
private context: Context,
protected element: ElementRef,
protected d3Service: D3Service,
protected nodesWidget: NodesWidget,
protected interfaceLabelWidget: InterfaceLabelWidget,
protected selectionToolWidget: SelectionTool,
protected movingToolWidget: MovingTool,
public graphLayout: GraphLayout
) {
this.d3 = d3Service.getD3();
@ -56,6 +64,28 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
@Input('show-interface-labels')
set showInterfaceLabels(value) {
this.settings.show_interface_labels = value;
this.interfaceLabelWidget.setEnabled(value);
if (this.isReady) {
this.redraw();
}
}
@Input('moving-tool')
set movingTool(value) {
this.movingToolWidget.setEnabled(value);
if(this.isReady) {
this.redraw();
}
}
@Input('selection-tool')
set selectionTool(value) {
this.selectionToolWidget.setEnabled(value);
if(this.isReady) {
this.redraw();
}
}
ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
@ -88,22 +118,17 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
}
ngOnInit() {
const d3 = this.d3;
if (this.parentNativeElement !== null) {
this.createGraph(this.parentNativeElement);
}
this.context.size = this.getSize();
}
public createGraph(domElement: HTMLElement) {
const rootElement = this.d3.select(domElement);
this.svg = rootElement.select<SVGSVGElement>('svg');
this.graphContext = new Context(true);
this.graphContext.size = this.getSize();
this.graphLayout.connect(this.svg, this.graphContext);
this.graphLayout.connect(this.svg, this.context);
this.onNodeDraggingSubscription = this.graphLayout.getNodesWidget().onNodeDragging.subscribe((eventNode: NodeEvent) => {
const linksWidget = this.graphLayout.getLinksWidget();
@ -115,7 +140,9 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
});
});
this.graphLayout.draw(this.svg, this.graphContext);
this.graphLayout.draw(this.svg, this.context);
this.isReady = true;
}
public getSize(): Size {
@ -132,7 +159,7 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
private changeLayout() {
if (this.parentNativeElement != null) {
this.graphContext.size = this.getSize();
this.context.size = this.getSize();
}
this.graphLayout.setNodes(this.nodes);
@ -174,7 +201,7 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
}
public redraw() {
this.graphLayout.draw(this.svg, this.graphContext);
this.graphLayout.draw(this.svg, this.context);
}
public reload() {

View File

@ -1,5 +1,6 @@
import { Size } from "./size";
import { Point } from "./point";
import { Injectable } from "@angular/core";
export class Transformation {
constructor(
@ -9,12 +10,13 @@ export class Transformation {
) {}
}
@Injectable()
export class Context {
public transformation: Transformation;
public size: Size;
constructor(public centerZeroZeroPoint = true) {
public centerZeroZeroPoint = true;
constructor() {
this.size = new Size(0, 0);
this.transformation = new Transformation(0, 0, 1);
}

View File

@ -9,32 +9,49 @@ import { Context} from "../models/context";
@Injectable()
export class MovingTool {
private selection: SVGSelection;
private context: Context;
// private selection: SVGSelection;
private zoom: ZoomBehavior<SVGSVGElement, any>;
constructor() {
private enabled = false;
private needsDeactivate = false;
private needsActivate = false;
constructor(
private context: Context
) {
this.zoom = zoom<SVGSVGElement, any>()
.scaleExtent([1 / 2, 8]);
}
public connect(selection: SVGSelection, context: Context) {
this.selection = selection;
this.context = context;
public setEnabled(enabled) {
if (this.enabled != enabled) {
if (enabled) {
this.needsActivate = true;
}
else {
this.needsDeactivate = true;
}
}
this.enabled = enabled;
}
public draw(selection: SVGSelection, context: Context) {
this.selection = selection;
this.context = context;
if(this.needsActivate) {
this.activate(selection);
this.needsActivate = false;
}
if(this.needsDeactivate) {
this.deactivate(selection);
this.needsDeactivate = false;
}
}
public activate() {
private activate(selection: SVGSelection) {
const self = this;
const onZoom = function(this: SVGSVGElement) {
const canvas = self.selection.select<SVGGElement>("g.canvas");
const canvas = selection.select<SVGGElement>("g.canvas");
const e: D3ZoomEvent<SVGSVGElement, any> = event;
canvas.attr(
'transform',
@ -51,12 +68,12 @@ export class MovingTool {
};
this.zoom.on('zoom', onZoom);
this.selection.call(this.zoom);
selection.call(this.zoom);
}
public deactivate() {
private deactivate(selection: SVGSelection) {
// d3.js preserves event `mousedown.zoom` and blocks selection
this.selection.on('mousedown.zoom', null);
selection.on('mousedown.zoom', null);
this.zoom.on('zoom', null);
}
}

View File

@ -11,26 +11,33 @@ import { Rectangle } from "../models/rectangle";
export class SelectionTool {
static readonly SELECTABLE_CLASS = '.selectable';
public rectangleSelected: Subject<Rectangle>;
public rectangleSelected = new Subject<Rectangle>();
private selection: SVGSelection;
private path;
private context: Context;
private enabled = false;
private needsDeactivate = false;
private needsActivate = false;
public constructor() {
this.rectangleSelected = new Subject<Rectangle>();
public constructor(
private context: Context
) {}
public setEnabled(enabled) {
if (this.enabled != enabled) {
if (enabled) {
this.needsActivate = true;
}
else {
this.needsDeactivate = true;
}
}
this.enabled = enabled;
}
public connect(selection: SVGSelection, context: Context) {
this.selection = selection;
this.context = context;
}
public activate() {
private activate(selection) {
const self = this;
this.selection.on("mousedown", function() {
selection.on("mousedown", function() {
const subject = select(window);
const parent = this.parentElement;
@ -38,7 +45,7 @@ export class SelectionTool {
self.startSelection(start);
// clear selection
self.selection
selection
.selectAll(SelectionTool.SELECTABLE_CLASS)
.classed("selected", false);
@ -56,8 +63,8 @@ export class SelectionTool {
});
}
public deactivate() {
this.selection.on('mousedown', null);
private deactivate(selection) {
selection.on('mousedown', null);
}
public draw(selection: SVGSelection, context: Context) {
@ -72,7 +79,15 @@ export class SelectionTool {
.attr("class", "selection")
.attr("visibility", "hidden");
}
this.selection = selection;
if(this.needsActivate) {
this.activate(selection);
this.needsActivate = false;
}
if(this.needsDeactivate) {
this.deactivate(selection);
this.needsDeactivate = false;
}
}
private startSelection(start) {

View File

@ -60,20 +60,12 @@ export class GraphLayout implements Widget {
return this.drawingLineTool;
}
public getMovingTool() {
return this.movingTool;
}
public getSelectionTool() {
return this.selectionTool;
}
connect(view: SVGSelection, context: Context) {
this.drawingLineTool.connect(view, context);
this.selectionTool.connect(view, context);
this.movingTool.connect(view, context);
this.selectionTool.activate();
}
draw(view: SVGSelection, context: Context) {

View File

@ -1,4 +1,5 @@
import { Injectable } from "@angular/core";
import { Widget } from "./widget";
import { SVGSelection } from "../models/types";
import { Link } from "../../models/link";

View File

@ -15,8 +15,8 @@ export class NodeSelectInterfaceComponent implements OnInit {
@ViewChild(MatMenuTrigger) contextMenu: MatMenuTrigger;
private topPosition;
private leftPosition;
protected topPosition;
protected leftPosition;
public node: Node;
constructor(

View File

@ -28,7 +28,10 @@ export class ProjectMapShortcutsComponent implements OnInit, OnDestroy {
) { }
ngOnInit() {
this.deleteHotkey = new Hotkey('del', this.onDeleteHandler);
const self = this;
this.deleteHotkey = new Hotkey('del', (event: KeyboardEvent) => {
return self.onDeleteHandler(event);
});
this.hotkeysService.add(this.deleteHotkey);
}

View File

@ -7,6 +7,8 @@
[width]="project.scene_width"
[height]="project.scene_height"
[show-interface-labels]="project.show_interface_labels"
[selection-tool]="tools.selection"
[moving-tool]="tools.moving"
(onNodeDragged)="onNodeDragged($event)"
></app-map>
@ -53,7 +55,7 @@
</mat-toolbar-row>
<mat-toolbar-row>
<button mat-icon-button [color]="movingMode ? 'primary': 'basic'" (click)="toggleMovingMode()">
<button mat-icon-button [color]="tools.moving ? 'primary': 'basic'" (click)="toggleMovingMode()">
<mat-icon>zoom_out_map</mat-icon>
</button>
</mat-toolbar-row>

View File

@ -48,8 +48,13 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
private ws: Subject<any>;
private drawLineMode = false;
private movingMode = false;
private readonly = false;
protected tools = {
'selection': true,
'moving': false
};
private inReadOnlyMode = false;
protected selectionManager: SelectionManager;
@ -189,13 +194,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
}
setUpMapCallbacks(project: Project) {
if (this.readonly) {
this.mapChild.graphLayout.getSelectionTool().deactivate();
}
this.mapChild.graphLayout.getNodesWidget().setDraggingEnabled(!this.readonly);
const onContextMenu = this.mapChild.graphLayout.getNodesWidget().onContextMenu.subscribe((eventNode: NodeEvent) => {
this.nodeContextMenu.open(eventNode.node, eventNode.event.clientY, eventNode.event.clientX);
});
@ -217,9 +217,6 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
this.mapChild.graphLayout.getSelectionTool().rectangleSelected)
);
this.mapChild.graphLayout
.getLinksWidget().getLinkWidget().getInterfaceLabelWidget().setEnabled(this.project.show_interface_labels);
this.mapChild.reload();
}
@ -244,6 +241,27 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
});
}
public set readonly(value) {
this.inReadOnlyMode = value;
if (value) {
this.tools.selection = false;
}
else {
this.tools.selection = true;
}
}
public get readonly() {
return this.inReadOnlyMode;
}
public toggleMovingMode() {
this.tools.moving = !this.tools.moving;
if (!this.readonly) {
this.tools.selection = !this.tools.moving;
}
}
public toggleDrawLineMode() {
this.drawLineMode = !this.drawLineMode;
if (!this.drawLineMode) {
@ -251,22 +269,6 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
}
}
public toggleMovingMode() {
this.movingMode = !this.movingMode;
if (this.movingMode) {
if (!this.readonly) {
this.mapChild.graphLayout.getSelectionTool().deactivate();
}
this.mapChild.graphLayout.getMovingTool().activate();
} else {
this.mapChild.graphLayout.getMovingTool().deactivate();
if (!this.readonly) {
this.mapChild.graphLayout.getSelectionTool().activate();
}
}
}
public onChooseInterface(event) {
const node: Node = event.node;
const port: Port = event.port;
@ -294,10 +296,6 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
public toggleShowInterfaceLabels(enabled: boolean) {
this.project.show_interface_labels = enabled;
this.mapChild.graphLayout.getLinksWidget().getLinkWidget().getInterfaceLabelWidget()
.setEnabled(this.project.show_interface_labels);
this.mapChild.reload();
}
public ngOnDestroy() {