-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add editor modes and a basic ui showing the current one
- Loading branch information
Showing
5 changed files
with
205 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { ContainerModule } from "inversify"; | ||
import { TYPES, configureCommand } from "sprotty"; | ||
import { ChangeEditorModeCommand, EditorModeController } from "./editorModeController"; | ||
import { EditorModeSwitchUi } from "./modeSwitchUi"; | ||
import { EDITOR_TYPES } from "../../utils"; | ||
|
||
export const editorModeModule = new ContainerModule((bind, unbind, isBound, rebind) => { | ||
const context = { bind, unbind, isBound, rebind }; | ||
|
||
bind(EditorModeController).toSelf().inSingletonScope(); | ||
bind(EditorModeSwitchUi).toSelf().inSingletonScope(); | ||
bind(TYPES.IUIExtension).toService(EditorModeSwitchUi); | ||
bind(EDITOR_TYPES.DefaultUIElement).toService(EditorModeSwitchUi); | ||
|
||
configureCommand(context, ChangeEditorModeCommand); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import { inject, injectable } from "inversify"; | ||
import { Command, CommandExecutionContext, CommandReturn, TYPES } from "sprotty"; | ||
import { Action } from "sprotty-protocol"; | ||
import { DfdNodeImpl } from "../dfdElements/nodes"; | ||
|
||
export type EditorMode = "edit" | "validation" | "readonly"; | ||
|
||
/** | ||
* Holds the current editor mode in a central place. | ||
* Used to get the current mode in places where it is used. | ||
* | ||
* Changes to the mode should be done using the ChangeEditorModeCommand | ||
* and not directly on this class when done interactively | ||
* for undo/redo support and actions that are done to the model | ||
* when the mode changes. | ||
*/ | ||
@injectable() | ||
export class EditorModeController { | ||
private mode: EditorMode = "edit"; | ||
private modeChangeCallbacks: ((mode: EditorMode) => void)[] = []; | ||
|
||
getCurrentMode(): EditorMode { | ||
return this.mode; | ||
} | ||
|
||
setMode(mode: EditorMode) { | ||
this.mode = mode; | ||
|
||
this.modeChangeCallbacks.forEach((callback) => callback(mode)); | ||
} | ||
|
||
onModeChange(callback: (mode: EditorMode) => void) { | ||
this.modeChangeCallbacks.push(callback); | ||
} | ||
} | ||
|
||
export interface ChangeEditorModeAction extends Action { | ||
kind: typeof ChangeEditorModeAction.KIND; | ||
newMode: EditorMode; | ||
} | ||
export namespace ChangeEditorModeAction { | ||
export const KIND = "changeEditorMode"; | ||
|
||
export function create(newMode: EditorMode): ChangeEditorModeAction { | ||
return { | ||
kind: KIND, | ||
newMode, | ||
}; | ||
} | ||
} | ||
|
||
export class ChangeEditorModeCommand extends Command { | ||
static readonly KIND = ChangeEditorModeAction.KIND; | ||
|
||
private oldMode?: EditorMode; | ||
|
||
@inject(EditorModeController) | ||
private readonly controller?: EditorModeController; | ||
|
||
constructor(@inject(TYPES.Action) private action: ChangeEditorModeAction) { | ||
super(); | ||
} | ||
|
||
execute(context: CommandExecutionContext): CommandReturn { | ||
if (!this.controller) throw new Error("Missing injects"); | ||
|
||
this.oldMode = this.controller.getCurrentMode(); | ||
this.controller.setMode(this.action.newMode); | ||
this.postModeSwitch(context); | ||
|
||
return context.root; | ||
} | ||
|
||
private postModeSwitch(context: CommandExecutionContext): void { | ||
if (this.oldMode === "validation" && this.action.newMode === "edit") { | ||
// Remove validation errors when enabling editing | ||
context.root.index.all().forEach((element) => { | ||
if (element instanceof DfdNodeImpl) { | ||
element.validationResult = undefined; | ||
} | ||
}); | ||
} | ||
} | ||
|
||
undo(context: CommandExecutionContext): CommandReturn { | ||
if (!this.controller) throw new Error("Missing injects"); | ||
|
||
if (!this.oldMode) { | ||
// This should never happen because execute() is called before undo() is called. | ||
throw new Error("No old mode to restore"); | ||
} | ||
this.controller.setMode(this.oldMode); | ||
|
||
return context.root; | ||
} | ||
|
||
redo(context: CommandExecutionContext): CommandReturn { | ||
return this.execute(context); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
.editor-mode-switcher { | ||
/* Position the switcher in the top left corner */ | ||
top: 40px; | ||
padding: 8px; | ||
left: 40px; | ||
|
||
/* Make text non-selectable */ | ||
-webkit-user-select: none; /* Safari only supports user select using the -webkit prefix */ | ||
user-select: none; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { AbstractUIExtension, ActionDispatcher, TYPES } from "sprotty"; | ||
import { ChangeEditorModeAction, EditorMode, EditorModeController } from "./editorModeController"; | ||
import { inject, injectable } from "inversify"; | ||
|
||
import "./modeSwitchUi.css"; | ||
|
||
/** | ||
* UI that shows the current editor mode (unless it is edit mode) | ||
* with details about the mode. | ||
* For validation mode the user can also choose to enable editing | ||
* and switch the editor to edit mode. | ||
*/ | ||
@injectable() | ||
export class EditorModeSwitchUi extends AbstractUIExtension { | ||
static readonly ID = "editor-mode-switcher"; | ||
|
||
constructor( | ||
@inject(EditorModeController) | ||
private readonly editorModeController: EditorModeController, | ||
@inject(TYPES.IActionDispatcher) | ||
private readonly actionDispatcher: ActionDispatcher, | ||
) { | ||
super(); | ||
} | ||
|
||
id(): string { | ||
return EditorModeSwitchUi.ID; | ||
} | ||
containerClass(): string { | ||
return this.id(); | ||
} | ||
|
||
protected initializeContents(containerElement: HTMLElement): void { | ||
containerElement.classList.add("ui-float"); | ||
this.editorModeController.onModeChange((mode) => this.reRender(mode)); | ||
// Only for testing, TODO: remove when mode is loaded from the model | ||
this.editorModeController.setMode("validation"); | ||
} | ||
|
||
private reRender(mode: EditorMode): void { | ||
this.containerElement.innerHTML = ""; | ||
switch (mode) { | ||
case "edit": | ||
this.containerElement.style.visibility = "hidden"; | ||
break; | ||
case "readonly": | ||
this.containerElement.style.visibility = "visible"; | ||
this.renderReadonlyMode(); | ||
break; | ||
case "validation": | ||
this.containerElement.style.visibility = "visible"; | ||
this.renderValidationMode(); | ||
break; | ||
default: | ||
throw new Error(`Unknown editor mode: ${mode}`); | ||
} | ||
} | ||
|
||
private renderValidationMode(): void { | ||
this.containerElement.innerHTML = ` | ||
Currently validation errors from the analysis.</br> | ||
Enabling editing will remove the validation errors.</br> | ||
<button id="enableEditingButton">Enable editing</button> | ||
`; | ||
const enableEditingButton = this.containerElement.querySelector("#enableEditingButton"); | ||
enableEditingButton?.addEventListener("click", () => { | ||
this.actionDispatcher.dispatch(ChangeEditorModeAction.create("edit")); | ||
}); | ||
} | ||
|
||
private renderReadonlyMode(): void { | ||
this.containerElement.innerHTML = ` | ||
This diagram was generated from a palladio model.</br> | ||
Model is readonly. | ||
`; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters