mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-01-22 04:18:08 +00:00
Merge pull request #530 from GNS3/Fit-in-view-option
Option to fit in view
This commit is contained in:
commit
2d588c411d
@ -33,6 +33,7 @@ import { ToolsService } from '../../../services/tools.service';
|
||||
import { TextEditorComponent } from '../text-editor/text-editor.component';
|
||||
import { MapScaleService } from '../../../services/mapScale.service';
|
||||
import { Project } from '../../../models/project';
|
||||
import { MapSettingsService } from '../../../services/mapsettings.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-d3-map',
|
||||
@ -75,7 +76,8 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy {
|
||||
protected movingToolWidget: MovingTool,
|
||||
public graphLayout: GraphLayout,
|
||||
private toolsService: ToolsService,
|
||||
private mapScaleService: MapScaleService
|
||||
private mapScaleService: MapScaleService,
|
||||
private mapSettingsService: MapSettingsService
|
||||
) {
|
||||
this.parentNativeElement = element.nativeElement;
|
||||
}
|
||||
@ -193,6 +195,7 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.graphLayout.draw(this.svg, this.context);
|
||||
this.textEditor.activateTextEditingForDrawings();
|
||||
this.textEditor.activateTextEditingForNodeLabels();
|
||||
this.mapSettingsService.mapRenderedEmitter.emit(true);
|
||||
}
|
||||
|
||||
@HostListener('window:resize', ['$event'])
|
||||
|
@ -47,6 +47,10 @@
|
||||
<mat-icon>developer_board</mat-icon>
|
||||
<span>Go to servers</span>
|
||||
</button>
|
||||
<button mat-menu-item (click)="fitInView()">
|
||||
<mat-icon>fullscreen</mat-icon>
|
||||
<span>Fit in view</span>
|
||||
</button>
|
||||
<button mat-menu-item (click)="addNewProject()">
|
||||
<mat-icon>add</mat-icon>
|
||||
<span>Add new blank project</span>
|
||||
|
@ -53,7 +53,7 @@ import { MapLinkNodeToLinkNodeConverter } from '../../cartography/converters/map
|
||||
import { ProjectMapMenuComponent } from './project-map-menu/project-map-menu.component';
|
||||
import { ToasterService } from '../../services/toaster.service';
|
||||
import { ImportProjectDialogComponent } from '../projects/import-project-dialog/import-project-dialog.component';
|
||||
import { MatDialog, MatBottomSheet } from '@angular/material';
|
||||
import { MatDialog, MatBottomSheet, mixinColor } from '@angular/material';
|
||||
import { AddBlankProjectDialogComponent } from '../projects/add-blank-project-dialog/add-blank-project-dialog.component';
|
||||
import { SaveProjectDialogComponent } from '../projects/save-project-dialog/save-project-dialog.component';
|
||||
import { MapNodesDataSource, MapLinksDataSource, MapDrawingsDataSource, MapSymbolsDataSource, Indexed } from '../../cartography/datasources/map-datasource';
|
||||
@ -95,6 +95,9 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
|
||||
protected settings: Settings;
|
||||
private inReadOnlyMode = false;
|
||||
private scrollX: number = 0;
|
||||
private scrollY: number = 0;
|
||||
private scrollEnabled: boolean = false;
|
||||
|
||||
@ViewChild(ContextMenuComponent, {static: false}) contextMenu: ContextMenuComponent;
|
||||
@ViewChild(D3MapComponent, {static: false}) mapChild: D3MapComponent;
|
||||
@ -200,6 +203,12 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.subscriptions.push(routeSub);
|
||||
|
||||
this.subscriptions.push(
|
||||
this.mapSettingsService.mapRenderedEmitter.subscribe((value: boolean) => {
|
||||
if (this.scrollEnabled) this.centerCanvas();
|
||||
})
|
||||
);
|
||||
|
||||
this.subscriptions.push(
|
||||
this.drawingsDataSource.changes.subscribe((drawings: Drawing[]) => {
|
||||
this.drawings = drawings;
|
||||
@ -414,6 +423,133 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
public fitInView() {
|
||||
this.drawings.forEach(drawing => {
|
||||
let splittedSvg = drawing.svg.split("\"");
|
||||
let height: number = parseInt(splittedSvg[1], 10);
|
||||
let width: number = parseInt(splittedSvg[3], 10);
|
||||
|
||||
drawing.element = {
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
});
|
||||
|
||||
if ((this.nodes.length === 0) && (this.drawings.length === 0)) { return };
|
||||
let minX : number, maxX : number, minY: number, maxY : number;
|
||||
|
||||
let borderedNodes: BorderedNode[] = [];
|
||||
this.nodes.forEach(n => {
|
||||
let borderedNode: BorderedNode = new BorderedNode();
|
||||
borderedNode.node = n;
|
||||
borderedNode.top = n.y;
|
||||
borderedNode.left = n.x;
|
||||
borderedNode.bottom = n.y + n.height;
|
||||
borderedNode.right = n.x + n.width;
|
||||
|
||||
if ((n.y + n.label.y) < borderedNode.top) {
|
||||
borderedNode.top = n.y + n.label.y;
|
||||
};
|
||||
|
||||
if ((n.x + n.label.x) < borderedNode.left) {
|
||||
borderedNode.left = n.x + n.label.x;
|
||||
};
|
||||
|
||||
if ((n.y + n.label.y) > borderedNode.bottom) {
|
||||
borderedNode.bottom = n.y + n.label.y;
|
||||
};
|
||||
|
||||
if ((n.x + n.label.x) > borderedNode.right) {
|
||||
borderedNode.right = n.x + n.label.x;
|
||||
};
|
||||
|
||||
borderedNodes.push(borderedNode);
|
||||
});
|
||||
|
||||
let nodeMinX = borderedNodes.sort((n,m) => n.left - m.left)[0];
|
||||
let nodeMaxX = borderedNodes.sort((n,m) => n.right - m.right)[borderedNodes.length - 1];
|
||||
let nodeMinY = borderedNodes.sort((n,m) => n.top - m.top)[0];
|
||||
let nodeMaxY = borderedNodes.sort((n,m) => n.bottom - m.bottom)[borderedNodes.length - 1];
|
||||
|
||||
let borderedDrawings: BorderedDrawing[] = [];
|
||||
this.drawings.forEach(n => {
|
||||
let borderedDrawing: BorderedDrawing = new BorderedDrawing();
|
||||
borderedDrawing.drawing = n;
|
||||
borderedDrawing.top = n.y;
|
||||
borderedDrawing.left = n.x;
|
||||
borderedDrawing.bottom = n.y + n.element.height;
|
||||
borderedDrawing.right = n.x + n.element.width;
|
||||
|
||||
borderedDrawings.push(borderedDrawing);
|
||||
});
|
||||
|
||||
let drawingMinX = borderedDrawings.sort((n,m) => n.left - m.left)[0];
|
||||
let drawingMaxX = borderedDrawings.sort((n,m) => n.right - m.right)[borderedDrawings.length - 1];
|
||||
let drawingMinY = borderedDrawings.sort((n,m) => n.top - m.top)[0];
|
||||
let drawingMaxY = borderedDrawings.sort((n,m) => n.bottom - m.bottom)[borderedDrawings.length - 1];
|
||||
|
||||
if (nodeMinX.left < drawingMinX.left) {
|
||||
minX = nodeMinX.left;
|
||||
} else {
|
||||
minX = drawingMinX.left;
|
||||
}
|
||||
|
||||
if (nodeMaxX.right > drawingMaxX.right) {
|
||||
maxX = nodeMaxX.right;
|
||||
} else {
|
||||
maxX = drawingMaxX.right;
|
||||
}
|
||||
|
||||
if (nodeMinY.top < drawingMinY.top) {
|
||||
minY = nodeMinY.top;
|
||||
} else {
|
||||
minY = drawingMinY.top;
|
||||
}
|
||||
|
||||
if (nodeMaxY.bottom > drawingMaxY.bottom) {
|
||||
maxY = nodeMaxY.bottom;
|
||||
} else {
|
||||
maxY = drawingMaxY.bottom;
|
||||
}
|
||||
|
||||
let margin: number = 20;
|
||||
minX = minX - margin;
|
||||
maxX = maxX + margin;
|
||||
minY = minY - margin;
|
||||
maxY = maxY + margin;
|
||||
|
||||
let windowWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
|
||||
let windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
|
||||
let widthOfAreaToShow = maxX - minX;
|
||||
let heightOfAreaToShow = maxY - minY;
|
||||
let widthToSceneWidthRatio = widthOfAreaToShow / windowWidth;
|
||||
let heightToSceneHeightRatio = heightOfAreaToShow / windowHeight;
|
||||
|
||||
let scale = 1 / Math.max(widthToSceneWidthRatio, heightToSceneHeightRatio);
|
||||
|
||||
if (scale !== this.mapScaleService.currentScale) {
|
||||
this.mapScaleService.setScale(scale);
|
||||
this.project.scene_width = this.project.scene_width * scale;
|
||||
this.project.scene_height = this.project.scene_height * scale;
|
||||
if (heightToSceneHeightRatio < widthOfAreaToShow) {
|
||||
this.scrollX = (minX * scale) - ((windowWidth - widthOfAreaToShow*scale)/2) + this.project.scene_width/2;
|
||||
this.scrollY = (minY * scale) + this.project.scene_height/2;
|
||||
} else {
|
||||
this.scrollX = (minX * scale) + this.project.scene_width/2;
|
||||
this.scrollY = (minY * scale) - ((windowHeight - heightOfAreaToShow*scale)/2) + this.project.scene_height/2;
|
||||
}
|
||||
} else {
|
||||
this.scrollX = (minX * scale) + this.project.scene_width/2;
|
||||
this.scrollY = (minY * scale) + this.project.scene_height/2;
|
||||
}
|
||||
this.scrollEnabled = true;
|
||||
}
|
||||
|
||||
public centerCanvas() {
|
||||
window.scrollTo(this.scrollX, this.scrollY);
|
||||
this.scrollEnabled = false;
|
||||
}
|
||||
|
||||
public centerView() {
|
||||
if (this.project) {
|
||||
let scrollX: number = (this.project.scene_width - document.documentElement.clientWidth) > 0 ? (this.project.scene_width - document.documentElement.clientWidth)/2 : 0;
|
||||
@ -650,3 +786,19 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
this.subscriptions.forEach((subscription: Subscription) => subscription.unsubscribe());
|
||||
}
|
||||
}
|
||||
|
||||
export class BorderedNode {
|
||||
top: number;
|
||||
left: number;
|
||||
bottom: number;
|
||||
right: number;
|
||||
node: Node;
|
||||
}
|
||||
|
||||
export class BorderedDrawing {
|
||||
top: number;
|
||||
left: number;
|
||||
bottom: number;
|
||||
right: number;
|
||||
drawing: Drawing;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Injectable, EventEmitter } from "@angular/core";
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
@ -8,6 +8,7 @@ export class MapSettingsService {
|
||||
public isLogConsoleVisible: boolean = false;
|
||||
public isLayerNumberVisible: boolean = false;
|
||||
public interfaceLabels: Map<string, boolean> = new Map<string, boolean>();
|
||||
public mapRenderedEmitter = new EventEmitter<boolean>();
|
||||
|
||||
constructor() {
|
||||
this.isLayerNumberVisible = localStorage.getItem('layersVisibility') === 'true' ? true : false;
|
||||
|
Loading…
Reference in New Issue
Block a user