Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement client-only layout. #195

Merged
merged 8 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/klighd-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@
"dependencies": {
"@kieler/klighd-interactive": "^0.5.0",
"@types/file-saver": "^2.0.7",
"elkjs": "^0.8.2",
"feather-icons": "^4.29.1",
"file-saver": "^2.0.5",
"inversify": "^6.0.2",
"snabbdom": "^3.5.1",
"sprotty": "^1.3.0",
"sprotty-elk": "^1.3.0",
"sprotty-protocol": "^1.3.0"
},
"devDependencies": {
Expand Down
21 changes: 19 additions & 2 deletions packages/klighd-core/src/di.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
*/

import { interactiveModule } from '@kieler/klighd-interactive/lib/interactive-module'
import ElkConstructor from 'elkjs/lib/elk.bundled'
import { Container, ContainerModule, interfaces } from 'inversify'
import {
boundsModule,
configureActionHandler,
configureModelElement,
ConsoleLogger,
Expand All @@ -44,6 +46,7 @@ import {
viewportModule,
ViewRegistry,
} from 'sprotty'
import { ElkFactory, ElkLayoutEngine, elkLayoutModule, ILayoutConfigurator } from 'sprotty-elk'
import actionModule from './actions/actions-module'
// import bookmarkModule from './bookmarks/bookmark-module';
import { DISymbol } from './di.symbols'
Expand All @@ -53,6 +56,7 @@ import { KlighdHoverMouseListener } from './hover/hover'
import { PopupModelProvider } from './hover/popup-provider'
import { KlighdMouseTool } from './klighd-mouse-tool'
import { KlighdSvgExporter } from './klighd-svg-exporter'
import { KielerLayoutConfigurator } from './layout-config'
import { KlighdModelViewer } from './model-viewer'
import { ResetPreferencesAction, SetPreferencesAction } from './options/actions'
import { optionsModule } from './options/options-module'
Expand All @@ -74,6 +78,16 @@ const kGraphDiagramModule = new ContainerModule(
bind(TYPES.ModelSource).to(KlighdDiagramServer).inSingletonScope()
rebind(TYPES.ILogger).to(ConsoleLogger).inSingletonScope()
rebind(TYPES.LogLevel).toConstantValue(LogLevel.warn)

// required binding for elkjs to work
bind(TYPES.IModelLayoutEngine).toService(ElkLayoutEngine)

// Our own layout configurator that just copies the element's poperties as the layout options.
bind(KielerLayoutConfigurator).toSelf().inSingletonScope()
rebind(ILayoutConfigurator).to(KielerLayoutConfigurator).inSingletonScope()
const elkFactory: ElkFactory = () => new ElkConstructor({ algorithms: ['layered'] }) // See elkjs documentation
bind(ElkFactory).toConstantValue(elkFactory)

rebind(TYPES.CommandStackOptions).toConstantValue({
// Override the default animation speed to be 500 ms, as the default value is too quick.
defaultDuration: 500,
Expand Down Expand Up @@ -126,6 +140,8 @@ export default function createContainer(widgetId: string): Container {
const container = new Container()
container.load(
defaultModule,
boundsModule,
elkLayoutModule,
selectModule,
interactiveModule,
viewportModule,
Expand All @@ -144,8 +160,9 @@ export default function createContainer(widgetId: string): Container {
)
// FIXME: bookmarkModule is currently broken due to wrong usage of Sprotty commands. action handling needs to be reimplemented for this to work.
overrideViewerOptions(container, {
needsClientLayout: false,
needsServerLayout: true,
// These are ignored ignored and overwritten by the current needsClientLayout preference during model request.
needsClientLayout: false, // client layout = micro layout (Sprotty/Sprotty+KLighD)
needsServerLayout: true, // server layout = macro layout (ELK/elkjs). false here to not forward it to the Java server (the model source), but keep and handle it directly on the diagram server proxy manually
baseDiv: widgetId,
hiddenDiv: `${widgetId}_hidden`,
// TODO: We should be able to completely deactivate Sprotty's zoom limits to not limit top down layout.
Expand Down
56 changes: 55 additions & 1 deletion packages/klighd-core/src/diagram-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,18 @@ import {
import {
Action,
ActionMessage,
ComputedBoundsAction,
BringToFrontAction,
findElement,
generateRequestId,
IModelLayoutEngine,
RequestModelAction,
GetViewportAction,
RequestPopupModelAction,
SelectAction,
SetModelAction,
SetPopupModelAction,
SModelRoot,
UpdateModelAction,
ViewportResult,
} from 'sprotty-protocol'
Expand All @@ -79,7 +84,7 @@ import {
import { RequestKlighdPopupModelAction } from './hover/hover'
import { PopupModelProvider } from './hover/popup-provider'
import { RenderOptionsRegistry, ResizeToFit } from './options/render-options-registry'
import { IncrementalDiagramGeneratorOption, PreferencesRegistry } from './preferences-registry'
import { ClientLayoutOption, IncrementalDiagramGeneratorOption, PreferencesRegistry } from './preferences-registry'
import { Connection, ServiceTypes, SessionStorage } from './services'
import { SetSynthesisAction } from './syntheses/actions'
import { UpdateDepthMapModelAction } from './update/update-depthmap-model'
Expand All @@ -106,6 +111,8 @@ export class KlighdDiagramServer extends DiagramServerProxy {

@inject(DISymbol.BookmarkRegistry) @optional() private bookmarkRegistry: BookmarkRegistry

@inject(TYPES.IModelLayoutEngine) @optional() protected layoutEngine?: IModelLayoutEngine

constructor(@inject(ServiceTypes.Connection) connection: Connection) {
super()
this._connection = connection
Expand Down Expand Up @@ -301,6 +308,53 @@ export class KlighdDiagramServer extends DiagramServerProxy {
return false
}

// Super class behavior, except taking the needsClientLayout preference into account instead of the client option.
override handleRequestModel(action: RequestModelAction): boolean {
const needsClientLayout = !!this.preferencesRegistry.getValue(ClientLayoutOption)
const needsServerLayout = !needsClientLayout

const newOptions = {
needsClientLayout,
needsServerLayout,
...action.options,
}
const newAction = {
...action,
options: newOptions,
}
this.forwardToServer(newAction)
return false
}

// Behavior adapted from the super class and modified to the behavior of the DiagramServer to allow this proxy to the Java diagram server to still be able to perform the layout locally.
handleComputedBounds(action: ComputedBoundsAction): boolean {
if (!this.preferencesRegistry.getValue(ClientLayoutOption)) {
return false
}
const root = this.currentRoot
this.computedBoundsApplicator.apply(root, action)
this.doSubmitModel(root, root.type === this.lastSubmittedModelType, action)
return false
}

// Behavior taken from the DiagramServer to allow this proxy to the Java diagram server to still be able to perform the layout locally.
private async doSubmitModel(newRoot: SModelRoot, update: boolean, cause?: Action): Promise<void> {
if (this.layoutEngine) {
newRoot = await this.layoutEngine.layout(newRoot)
}
const modelType = newRoot.type
if (cause && cause.kind === RequestModelAction.KIND) {
const { requestId } = cause as RequestModelAction
const response = SetModelAction.create(newRoot, requestId)
this.actionDispatcher.dispatch(response)
} else if (update && modelType === this.lastSubmittedModelType) {
this.actionDispatcher.dispatch(UpdateModelAction.create(newRoot))
} else {
this.actionDispatcher.dispatch(SetModelAction.create(newRoot))
}
this.lastSubmittedModelType = modelType
}

handleRequestDiagramPiece(action: RequestDiagramPieceAction): void {
this.forwardToServer(action)
}
Expand Down
41 changes: 41 additions & 0 deletions packages/klighd-core/src/layout-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* KIELER - Kiel Integrated Environment for Layout Eclipse RichClient
*
* http://rtsys.informatik.uni-kiel.de/kieler
*
* Copyright 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 { LayoutOptions } from 'elkjs'
import { DefaultLayoutConfigurator } from 'sprotty-elk'
import { SModelElement, SModelIndex } from 'sprotty-protocol'

/**
* This layout configurator copies all layout options from the KGraph element's properties.
*/
export class KielerLayoutConfigurator extends DefaultLayoutConfigurator {
override apply(element: SModelElement, _index: SModelIndex): LayoutOptions | undefined {
// Only apply to elements with properties.
if ((element as any).properties === undefined) {
return undefined
}
const properties = (element as any).properties as Record<string, unknown>

// map properties to layout options and stringify values
const layoutOptions: LayoutOptions = {}
Object.entries(properties).forEach(([key, value]) => {
layoutOptions[key] = JSON.stringify(value)
})

return layoutOptions
}
}
20 changes: 20 additions & 0 deletions packages/klighd-core/src/options/general-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
import { inject, injectable, postConstruct } from 'inversify'
import { VNode } from 'snabbdom'
import { html } from 'sprotty' // eslint-disable-line @typescript-eslint/no-unused-vars
import { RefreshDiagramAction } from '@kieler/klighd-interactive/lib/actions'
import { DISymbol } from '../di.symbols'
import { FeatherIcon } from '../feather-icons-snabbdom/feather-icons-snabbdom'
import {
ClientLayoutOption,
IncrementalDiagramGeneratorOption,
PreferencesRegistry,
ShouldSelectDiagramOption,
Expand Down Expand Up @@ -123,6 +125,16 @@ export class GeneralPanel extends SidebarPanel {
) : (
''
)}
{(this.renderOptionsRegistry.getValue(DebugOptions) as boolean) ? (
<CheckOption
id={ClientLayoutOption.ID}
name={ClientLayoutOption.NAME}
value={this.preferencesRegistry.getValue(ClientLayoutOption)}
onChange={this.handlePreferenceChange.bind(this, ClientLayoutOption.ID)}
/>
) : (
''
)}
</div>
</div>
)
Expand All @@ -134,6 +146,14 @@ export class GeneralPanel extends SidebarPanel {

private handlePreferenceChange(key: string, newValue: any) {
this.actionDispatcher.dispatch(SetPreferencesAction.create([{ id: key, value: newValue }]))
if (key === ClientLayoutOption.ID) {
this.actionDispatcher.dispatch(
RefreshDiagramAction.create({
needsClientLayout: newValue,
needsServerLayout: !newValue,
})
)
}
}

get icon(): VNode {
Expand Down
4 changes: 1 addition & 3 deletions packages/klighd-core/src/options/render-options-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* http://rtsys.informatik.uni-kiel.de/kieler
*
* Copyright 2021-2022 by
* Copyright 2021-2024 by
* + Kiel University
* + Department of Computer Science
* + Real-Time and Embedded Systems Group
Expand Down Expand Up @@ -509,8 +509,6 @@ export class DebugOptions implements RenderOption {
readonly description = 'Whether debug options should be shown.'

currentValue = false

debug = true
Eddykasp marked this conversation as resolved.
Show resolved Hide resolved
}

export interface RenderOptionType {
Expand Down
29 changes: 28 additions & 1 deletion packages/klighd-core/src/preferences-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* http://rtsys.informatik.uni-kiel.de/kieler
*
* Copyright 2021 by
* Copyright 2021-2024 by
* + Kiel University
* + Department of Computer Science
* + Real-Time and Embedded Systems Group
Expand Down Expand Up @@ -87,6 +87,31 @@ export class IncrementalDiagramGeneratorOption implements Preference {
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
Expand Down Expand Up @@ -116,6 +141,7 @@ export class PreferencesRegistry extends Registry {
this.register(ShouldSelectDiagramOption)
this.register(ShouldSelectTextOption)
this.register(IncrementalDiagramGeneratorOption)
this.register(ClientLayoutOption)
}

@postConstruct()
Expand Down Expand Up @@ -184,6 +210,7 @@ export class PreferencesRegistry extends Registry {
'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)
})
Expand Down
5 changes: 4 additions & 1 deletion packages/klighd-core/src/skgraph-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import { KEdge, KGraphData, KNode, SKGraphElement } from '@kieler/klighd-interactive/lib/constraint-classes'
import {
boundsFeature,
layoutContainerFeature,
moveFeature,
popupFeature,
RectangularPort,
Expand All @@ -40,6 +41,8 @@ export class SKNode extends KNode {
hasFeature(feature: symbol): boolean {
return (
feature === selectFeature ||
feature === boundsFeature ||
feature === layoutContainerFeature ||
(feature === moveFeature &&
(this.parent as SKNode).properties &&
((this.parent as SKNode).properties['org.eclipse.elk.interactiveLayout'] as boolean)) ||
Expand All @@ -61,7 +64,7 @@ export class SKPort extends RectangularPort implements SKGraphElement {
areNonChildAreaChildrenRendered = false

hasFeature(feature: symbol): boolean {
return feature === selectFeature || feature === popupFeature
return feature === selectFeature || feature === boundsFeature || feature === popupFeature
}

properties: Record<string, unknown>
Expand Down
Loading
Loading