Skip to content

Commit

Permalink
basic layout choice menu
Browse files Browse the repository at this point in the history
  • Loading branch information
Kr0nox committed Jan 5, 2025
1 parent fb24e23 commit 7e10d0e
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 14 deletions.
7 changes: 5 additions & 2 deletions src/common/commandPalette.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { injectable } from "inversify";
import { inject, injectable } from "inversify";
import { ICommandPaletteActionProvider, LabeledAction, SModelRootImpl, CommitModelAction } from "sprotty";
import { Point } from "sprotty-protocol";
import { LoadDiagramAction } from "../features/serialize/load";
Expand All @@ -10,12 +10,15 @@ import { LayoutModelAction } from "../features/autoLayout/command";
import "@vscode/codicons/dist/codicon.css";
import "sprotty/css/command-palette.css";
import "./commandPalette.css";
import { SettingsManager } from "./settingsMenu";

/**
* Provides possible actions for the command palette.
*/
@injectable()
export class ServerCommandPaletteActionProvider implements ICommandPaletteActionProvider {
constructor(@inject(SettingsManager) protected readonly settings: SettingsManager) {}

async getActions(
root: Readonly<SModelRootImpl>,
_text: string,
Expand All @@ -31,7 +34,7 @@ export class ServerCommandPaletteActionProvider implements ICommandPaletteAction
new LabeledAction("Load diagram from JSON", [LoadDiagramAction.create(), commitAction], "go-to-file"),
new LabeledAction("Load default diagram", [LoadDefaultDiagramAction.create(), commitAction], "clear-all"),
new LabeledAction(
"Layout diagram",
"Layout diagram (Method: " + this.settings.layoutMethod + ")",
[LayoutModelAction.create(), commitAction, fitToScreenAction],
"layout",
),
Expand Down
6 changes: 6 additions & 0 deletions src/common/di.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { DiagramModificationCommandStack } from "./customCommandStack";

import "./commonStyling.css";
import { LightDarkSwitch } from "./lightDarkSwitch";
import { SettingsManager, SettingsUI } from "./settingsMenu";

export const commonModule = new ContainerModule((bind, unbind, isBound, rebind) => {
bind(ServerCommandPaletteActionProvider).toSelf().inSingletonScope();
Expand All @@ -34,6 +35,11 @@ export const commonModule = new ContainerModule((bind, unbind, isBound, rebind)
bind(TYPES.IUIExtension).toService(HelpUI);
bind(EDITOR_TYPES.DefaultUIElement).toService(HelpUI);

bind(SettingsManager).toSelf().inSingletonScope();
bind(SettingsUI).toSelf().inSingletonScope();
bind(TYPES.IUIExtension).toService(SettingsUI);
bind(EDITOR_TYPES.DefaultUIElement).toService(SettingsUI);

bind(LightDarkSwitch).toSelf().inSingletonScope();
bind(TYPES.IUIExtension).toService(LightDarkSwitch);
bind(EDITOR_TYPES.DefaultUIElement).toService(LightDarkSwitch);
Expand Down
2 changes: 1 addition & 1 deletion src/common/lightDarkSwitch.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
div.light-dark-switch {
left: 20px;
left: 242px;
bottom: 70px;
padding: 10px 10px;
}
Expand Down
30 changes: 30 additions & 0 deletions src/common/settingsMenu.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
div.settings-ui {
left: 20px;
bottom: 70px;
padding: 10px 10px;
min-width: 190px;
}

#settings-ui-accordion-label .accordion-button::before {
content: "";
background-image: url("@fortawesome/fontawesome-free/svgs/solid/gear.svg");
display: inline-block;
filter: invert(var(--dark-mode));
height: 16px;
width: 16px;
background-size: 16px 16px;
vertical-align: text-top;
}

#settings-content {
display: grid;
}

#settings-content > label {
grid-column-start: 1;
}

#settings-content > input,
#settings-content > select {
grid-column-start: 2;
}
70 changes: 70 additions & 0 deletions src/common/settingsMenu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { AbstractUIExtension } from "sprotty";
import { inject, injectable } from "inversify";

import "./settingsMenu.css";

@injectable()
export class SettingsManager {
public layoutMethod: LayoutMethod = LayoutMethod.LINES;
}

@injectable()
export class SettingsUI extends AbstractUIExtension {
static readonly ID = "settings-ui";

constructor(@inject(SettingsManager) protected readonly settings: SettingsManager) {
super();
}

id(): string {
return SettingsUI.ID;
}

containerClass(): string {
return SettingsUI.ID;
}

protected initializeContents(containerElement: HTMLElement): void {
containerElement.classList.add("ui-float");
containerElement.innerHTML = `
<input type="checkbox" id="accordion-state-settings" class="accordion-state" hidden>
<label id="settings-ui-accordion-label" for="accordion-state-settings">
<div class="accordion-button flip-arrow">
Settings
</div>
</label>
<div class="accordion-content">
<div id="settings-content">
<label for="setting-layout-option">Layout Method:</label>
<select name="setting-layout-option" id="setting-layout-option">
<option value="${LayoutMethod.LINES}">Lines</option>
<option value="${LayoutMethod.WRAPPING}">Wrapping Lines</option>
<option value="${LayoutMethod.CIRCLES}">Circles</option>
</select>
</div>
</div>
`;

// Set `settings-enabled` class on body element when keyboard shortcut overview is open.
const checkbox = containerElement.querySelector("#accordion-state-settings") as HTMLInputElement;
const bodyElement = document.querySelector("body") as HTMLBodyElement;
checkbox.addEventListener("change", () => {
if (checkbox.checked) {
bodyElement.classList.add("settings-enabled");
} else {
bodyElement.classList.remove("settings-enabled");
}
});

const layoutOptionSelect = containerElement.querySelector("#setting-layout-option") as HTMLSelectElement;
layoutOptionSelect.addEventListener("change", () => {
this.settings.layoutMethod = layoutOptionSelect.value as LayoutMethod;
});
}
}

export enum LayoutMethod {
LINES = "Lines",
WRAPPING = "Wrapping Lines",
CIRCLES = "Circles",
}
53 changes: 42 additions & 11 deletions src/features/autoLayout/layouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,58 @@ import {
import { SChildElementImpl, SShapeElementImpl, isBoundsAware } from "sprotty";
import { SShapeElement, SGraph, SModelIndex } from "sprotty-protocol";
import { ElkShape, LayoutOptions } from "elkjs";
import { LayoutMethod, SettingsManager } from "../../common/settingsMenu";

export class DfdLayoutConfigurator extends DefaultLayoutConfigurator {
constructor(@inject(SettingsManager) protected readonly settings: SettingsManager) {
super();
}

protected override graphOptions(_sgraph: SGraph, _index: SModelIndex): LayoutOptions {
// Elk settings. See https://eclipse.dev/elk/reference.html for available options.
return {
"org.eclipse.elk.algorithm": "org.eclipse.elk.layered",
"org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers": "30.0",
"org.eclipse.elk.layered.spacing.edgeNodeBetweenLayers": "20.0",
"org.eclipse.elk.port.borderOffset": "14.0",
// Do not do micro layout for nodes, which includes the node dimensions etc.
// These are all automatically determined by our dfd node views
"org.eclipse.elk.omitNodeMicroLayout": "true",
// Balanced graph > straight edges
"org.eclipse.elk.layered.nodePlacement.favorStraightEdges": "false",
};
[LayoutMethod.LINES]: {
"org.eclipse.elk.algorithm": "org.eclipse.elk.layered",
"org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers": "30.0",
"org.eclipse.elk.layered.spacing.edgeNodeBetweenLayers": "20.0",
"org.eclipse.elk.port.borderOffset": "14.0",
// Do not do micro layout for nodes, which includes the node dimensions etc.
// These are all automatically determined by our dfd node views
"org.eclipse.elk.omitNodeMicroLayout": "true",
// Balanced graph > straight edges
"org.eclipse.elk.layered.nodePlacement.favorStraightEdges": "false",
},
[LayoutMethod.WRAPPING]: {
"org.eclipse.elk.algorithm": "org.eclipse.elk.layered",
"org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers": "10.0", //Save more space between layers (long names might break this!)
"org.eclipse.elk.layered.spacing.edgeNodeBetweenLayers": "5.0", //Save more space between layers (long names might break this!)
"org.eclipse.elk.edgeRouting": "ORTHOGONAL", //Edges should be routed orthogonal to each another
"org.eclipse.elk.layered.layering.strategy": "COFFMAN_GRAHAM",
"org.eclipse.elk.layered.compaction.postCompaction.strategy": "LEFT_RIGHT_CONSTRAINT_LOCKING", //Compact the resulting graph horizontally
"org.eclipse.elk.layered.wrapping.strategy": "MULTI_EDGE", //Allow wrapping of multiple edges
"org.eclipse.elk.layered.wrapping.correctionFactor": "2.0", //Allow the wrapping to occur earlier
// Do not do micro layout for nodes, which includes the node dimensions etc.
// These are all automatically determined by our dfd node views
"org.eclipse.elk.omitNodeMicroLayout": "true",
},
[LayoutMethod.CIRCLES]: {
"org.eclipse.elk.algorithm": "org.eclipse.elk.stress",
"org.eclipse.elk.force.repulsion": "5.0",
"org.eclipse.elk.force.iterations": "100", //Reduce iterations for faster formatting, did not notice differences with more iterations
"org.eclipse.elk.force.repulsivePower": "1", //Edges should repel vertices as well
"org.eclipse.elk.port.borderOffset": "14.0",
// Do not do micro layout for nodes, which includes the node dimensions etc.
// These are all automatically determined by our dfd node views
"org.eclipse.elk.omitNodeMicroLayout": "true",
// Balanced graph > straight edges
},
}[this.settings.layoutMethod];
}
}

export const elkFactory = () =>
new ElkConstructor({
algorithms: ["layered"],
algorithms: ["layered", "stress"],
});

/**
Expand Down

0 comments on commit 7e10d0e

Please sign in to comment.