mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-01-18 18:56:26 +00:00
Decouple project-map from map
This commit is contained in:
parent
48dcfcbf1c
commit
1d1cf149bb
@ -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]
|
||||
})
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { Widget } from "./widget";
|
||||
import { SVGSelection } from "../models/types";
|
||||
import { Link } from "../../models/link";
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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() {
|
||||
|
Loading…
Reference in New Issue
Block a user