UNPKG

@kieler/klighd-core

Version:

Core KLighD diagram visualization with Sprotty

228 lines (178 loc) 7.6 kB
/* * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient * * http://rtsys.informatik.uni-kiel.de/kieler * * Copyright 2021-2024 by * + Kiel University * + Department of Computer Science * + Real-Time and Embedded Systems Group * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0. * * SPDX-License-Identifier: EPL-2.0 */ import { inject, injectable, postConstruct } from 'inversify' import { IActionDispatcher, ICommand, TYPES } from 'sprotty' import { Action } from 'sprotty-protocol' import { Registry } from './base/registry' import { ResetPreferencesAction, SetPreferencesAction } from './options/actions' import { Preference, TransformationOptionType } from './options/option-models' import { Connection, NotificationType, PersistenceStorage, ServiceTypes } from './services' /** * Indicates whether or not a text selection should select the corresponding diagram part. */ export class ShouldSelectDiagramOption implements Preference { static readonly ID: string = 'diagram.shouldSelectDiagram' static readonly NAME: string = 'Text Selects Diagram' readonly id: string = ShouldSelectDiagramOption.ID readonly name: string = ShouldSelectDiagramOption.NAME readonly type: TransformationOptionType = TransformationOptionType.CHECK readonly initialValue: boolean = false currentValue = false notifyServer = true } /** * Indicates whether or nat a selection in the diagram should also highlight the corresponding text. */ export class ShouldSelectTextOption implements Preference { static readonly ID: string = 'diagram.shouldSelectText' static readonly NAME: string = 'Diagram Selects Text' readonly id: string = ShouldSelectTextOption.ID readonly name: string = ShouldSelectTextOption.NAME readonly type: TransformationOptionType = TransformationOptionType.CHECK readonly initialValue: boolean = true currentValue = true notifyServer = true } /** * Instructs the server if the diagram should be sent incrementally in pieces. */ export class IncrementalDiagramGeneratorOption implements Preference { static readonly ID: string = 'diagram.incrementalDiagramGenerator' static readonly NAME: string = 'Incremental Diagram Generator' readonly id: string = IncrementalDiagramGeneratorOption.ID readonly name: string = IncrementalDiagramGeneratorOption.NAME readonly type: TransformationOptionType = TransformationOptionType.CHECK readonly initialValue: boolean = false currentValue = false notifyServer = true debug = true } /** * Switch between client-only and server-only layout. */ export class ClientLayoutOption implements Preference { static readonly ID: string = 'diagram.clientLayout' static readonly NAME: string = 'Client Layout' readonly description: string | undefined = 'Switch between client-only and server-only layout.' readonly id: string = ClientLayoutOption.ID readonly name: string = ClientLayoutOption.NAME readonly type: TransformationOptionType = TransformationOptionType.CHECK readonly initialValue: boolean = false currentValue = false notifyServer = true debug = true } export interface PreferenceType { readonly ID: string readonly NAME: string new (): Preference } /** * {@link Registry} that stores user preferences which change the behavior of the diagram view. * * This registry should store options or preferences that are not provided by the Synthesis as LayoutOptions but that also * should be send to the server. * In contrast to RenderOptions they are cannot be solely handled by the client. */ @injectable() export class PreferencesRegistry extends Registry { private _preferences: Map<string, Preference> = new Map() @inject(ServiceTypes.Connection) private connection: Connection @inject(ServiceTypes.PersistenceStorage) private storage: PersistenceStorage @inject(TYPES.IActionDispatcher) private dispatcher: IActionDispatcher constructor() { super() // Add available preferences this.register(ShouldSelectDiagramOption) this.register(ShouldSelectTextOption) this.register(IncrementalDiagramGeneratorOption) this.register(ClientLayoutOption) } @postConstruct() init(): void { this.storage.onClear(this.handleClear.bind(this)) this.storage .getItem<Record<string, unknown>>('preference') .then((data) => { if (data) this.loadPersistedData(data) }) .then(() => { // Wait until values are loaded before notifying. this.notifyListeners() // Notify the server about initial preferences. this.notifyServer() }) } /** * Restores options that where previously persisted in storage. * Since preferences are not provided by the server, they have to be retrieved from storage. */ private loadPersistedData(data: Record<string, unknown>) { for (const entry of Object.entries(data)) { const option = this._preferences.get(entry[0]) if (option) { // eslint-disable-next-line prefer-destructuring option.currentValue = entry[1] } } } register(Option: PreferenceType): void { this._preferences.set(Option.ID, new Option()) } handle(action: Action): void | Action | ICommand { if (SetPreferencesAction.isThisAction(action)) { // Update storage values this.storage.setItem<Record<string, boolean>>('preference', (prev) => { const obj: Record<string, boolean> = prev ?? {} for (const option of action.options) { obj[option.id] = option.value // Update local value from storage const localPreference = this._preferences.get(option.id) if (localPreference) { localPreference.currentValue = option.value } } return obj }) this.notifyListeners() this.notifyServer() } else if (ResetPreferencesAction.isThisAction(action)) { this._preferences.forEach((option) => { option.currentValue = option.initialValue }) this.notifyListeners() } } /** Notifies the server about changed preferences that are supported by the client. */ private notifyServer() { this.connection.onReady().then(async () => { const obj = { 'diagram.shouldSelectDiagram': this.getValue(ShouldSelectDiagramOption), 'diagram.shouldSelectText': this.getValue(ShouldSelectTextOption), 'diagram.incrementalDiagramGenerator': this.getValue(IncrementalDiagramGeneratorOption), 'diagram.clientLayout': this.getValue(ClientLayoutOption), } this.connection.sendNotification(NotificationType.SetPreferences, obj) }) } getValue(option: PreferenceType): any | undefined { return this._preferences.get(option.ID)?.currentValue } /** Reset all stored options when the storage gets cleared from outside. */ private handleClear() { this.dispatcher.dispatch(ResetPreferencesAction.create()) } }