Skip to content

Commit 4377eb7

Browse files
Merge pull request #3 from Developer-Mike/better-commands
Add more canvas commands
2 parents 166739a + 6870219 commit 4377eb7

File tree

5 files changed

+190
-1
lines changed

5 files changed

+190
-1
lines changed

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222
- Enable the plugin in Settings -> Community plugins -> Installed plugins
2323

2424
## Features
25+
All features can be enabled/disabled in the settings.
26+
2527
- Create groups independently of the nodes ([Updated card menu](#updated-canvas-card-menu))
28+
- More [canvas commands](#canvas-commands)
2629
- (Flowchart) [Node shapes](#node-shapes)
2730
- Terminal shape
2831
- Process shape
@@ -63,6 +66,16 @@
6366
<img src="./assets/control-menu.png" alt="New canvas control menu"/>
6467
</details>
6568

69+
## Canvas Commands
70+
- `Advanced Canvas: Create text node`
71+
- Create a new text node
72+
- `Advanced Canvas: Clone node up/down/left/right`
73+
- Clone the selected node in the direction of the arrow keys
74+
- The cloned node will have the same dimensions and color as the original node
75+
- `Advanced Canvas: Expand node up/down/left/right`
76+
- Expand the selected node in the direction of the arrow keys
77+
- Default hotkey: `Alt` + `Arrow keys`
78+
6679
## Node Shapes
6780
<details>
6881
<summary>Flowchart Example</summary>

src/@types/Canvas.d.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { TFile } from "obsidian"
22

33
export interface Canvas {
44
view: CanvasView
5+
config: CanvasConfig
56

67
data: CanvasData
78
getData(): CanvasData
@@ -32,6 +33,7 @@ export interface Canvas {
3233

3334
getEdgesForNode(node: CanvasNode): CanvasEdge[]
3435

36+
createTextNode(options: TextNodeOptions): CanvasNode
3537
createGroupNode(options: GroupNodeOptions): CanvasNode
3638
createFileNode(options: FileNodeOptions): CanvasNode
3739
removeNode(node: CanvasNode): void
@@ -55,7 +57,11 @@ export interface Canvas {
5557
lockedX: number
5658
lockedY: number
5759
lockedZoom: number
58-
setNodeUnknownData(node: CanvasNode, key: string, value: any): void
60+
setNodeUnknownData(node: CanvasNode, key: keyof CanvasNodeUnknownData, value: any): void
61+
}
62+
63+
export interface CanvasConfig {
64+
defaultTextNodeDimensions: Size
5965
}
6066

6167
export interface CanvasView {
@@ -88,6 +94,10 @@ export interface NodeOptions {
8894
focus?: boolean
8995
}
9096

97+
export interface TextNodeOptions extends NodeOptions {
98+
text?: string
99+
}
100+
91101
export interface GroupNodeOptions extends NodeOptions {
92102
label?: string
93103
}
@@ -108,6 +118,9 @@ export interface CanvasNode {
108118
canvas: Canvas
109119
nodeEl: HTMLElement
110120
bbox: BBox
121+
getBBox(): BBox
122+
text?: string
123+
color: string
111124
unknownData: CanvasNodeUnknownData
112125
}
113126

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { Canvas } from "src/@types/Canvas"
2+
import AdvancedCanvasPlugin from "src/main"
3+
import * as CanvasHelper from "src/utils/canvas-helper"
4+
5+
type Direction = 'up' | 'down' | 'left' | 'right'
6+
const DRIECTIONS = ['up', 'down', 'left', 'right'] as Direction[]
7+
const DIRECTION_KEYS = {
8+
up: 'ArrowUp',
9+
down: 'ArrowDown',
10+
left: 'ArrowLeft',
11+
right: 'ArrowRight'
12+
}
13+
14+
export default class CommandsCanvasExtension {
15+
plugin: AdvancedCanvasPlugin
16+
17+
constructor(plugin: AdvancedCanvasPlugin) {
18+
this.plugin = plugin
19+
20+
if (!this.plugin.settingsManager.getSetting('commandsFeatureEnabled')) return
21+
22+
this.plugin.addCommand({
23+
id: 'create-text-node',
24+
name: 'Create text node',
25+
checkCallback: CanvasHelper.canvasCommand(
26+
this.plugin,
27+
(canvas: Canvas) => !canvas.readonly,
28+
(canvas: Canvas) => this.createTextNode(canvas)
29+
)
30+
})
31+
32+
for (const direction of DRIECTIONS) {
33+
this.plugin.addCommand({
34+
id: `clone-node-${direction}`,
35+
name: `Clone node ${direction}`,
36+
checkCallback: CanvasHelper.canvasCommand(
37+
this.plugin,
38+
(canvas: Canvas) => !canvas.readonly && canvas.selection.size === 1 && canvas.selection.values().next().value?.getData().type === 'text',
39+
(canvas: Canvas) => this.cloneNode(canvas, direction)
40+
)
41+
})
42+
43+
this.plugin.addCommand({
44+
id: `expand-node-${direction}`,
45+
name: `Expand node ${direction}`,
46+
hotkeys: [
47+
{
48+
modifiers: ['Alt'],
49+
key: DIRECTION_KEYS[direction]
50+
}
51+
],
52+
checkCallback: CanvasHelper.canvasCommand(
53+
this.plugin,
54+
(canvas: Canvas) => !canvas.readonly && canvas.selection.size === 1,
55+
(canvas: Canvas) => this.expandNode(canvas, direction)
56+
)
57+
})
58+
}
59+
}
60+
61+
private createTextNode(canvas: Canvas) {
62+
const size = canvas.config.defaultTextNodeDimensions
63+
const pos = CanvasHelper.getCenterCoordinates(canvas, size)
64+
65+
canvas.createTextNode({ pos: pos, size: size })
66+
}
67+
68+
private cloneNode(canvas: Canvas, cloneDirection: Direction) {
69+
const sourceNode = canvas.selection.values().next().value
70+
if (!sourceNode) return
71+
const sourceNodeData = sourceNode.getData()
72+
73+
const nodeMargin = this.plugin.settingsManager.getSetting('cloneNodeMargin')
74+
const offset = {
75+
x: (sourceNode.width + nodeMargin) * (cloneDirection === 'left' ? -1 : (cloneDirection === 'right' ? 1 : 0)),
76+
y: (sourceNode.height + nodeMargin) * (cloneDirection === 'up' ? -1 : (cloneDirection === 'down' ? 1 : 0))
77+
}
78+
79+
const clonedNode = canvas.createTextNode({
80+
pos: {
81+
x: sourceNode.x + offset.x,
82+
y: sourceNode.y + offset.y
83+
},
84+
size: {
85+
width: sourceNode.width,
86+
height: sourceNode.height
87+
}
88+
})
89+
90+
clonedNode.color = sourceNode.color
91+
canvas.setNodeUnknownData(clonedNode, 'shape', sourceNodeData.shape)
92+
93+
if (this.plugin.settingsManager.getSetting('zoomToClonedNode'))
94+
canvas.zoomToBbox(clonedNode.getBBox())
95+
}
96+
97+
private expandNode(canvas: Canvas, expandDirection: Direction) {
98+
const node = canvas.selection.values().next().value
99+
if (!node) return
100+
101+
const expandNodeStepSize = this.plugin.settingsManager.getSetting('expandNodeStepSize')
102+
const expand = {
103+
x: expandDirection === 'left' ? -1 : (expandDirection === 'right' ? 1 : 0),
104+
y: expandDirection === 'up' ? -1 : (expandDirection === 'down' ? 1 : 0)
105+
}
106+
107+
node.setData({
108+
...node.getData(),
109+
width: node.width + expand.x * expandNodeStepSize,
110+
height: node.height + expand.y * expandNodeStepSize
111+
})
112+
}
113+
}

src/main.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ import CanvasEventEmitter from './events/canvas-event-emitter'
99
import { Canvas } from './@types/Canvas'
1010
import ReadonlyCanvasExtension from './canvas-extensions/readonly-canvas-extension'
1111
import EncapsulateCanvasExtension from './canvas-extensions/encapsulate-canvas-extension'
12+
import CommandsCanvasExtension from './canvas-extensions/commands-canvas-extension'
1213

1314
const CANVAS_EXTENSIONS: any[] = [
1415
NodeDataTaggerCanvasExtension,
1516
InteractionTaggerCanvasExtension,
17+
CommandsCanvasExtension,
1618
ReadonlyCanvasExtension,
1719
GroupCanvasExtension,
1820
EncapsulateCanvasExtension,

src/settings.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ const SLIDE_SIZE_OPTIONS: { [key: string]: string } = {
99
export interface AdvancedCanvasPluginSettings {
1010
shapesFeatureEnabled: boolean
1111

12+
commandsFeatureEnabled: boolean
13+
zoomToClonedNode: boolean
14+
cloneNodeMargin: number
15+
expandNodeStepSize: number
16+
1217
betterReadonlyEnabled: boolean
1318
disableNodePopup: boolean
1419
disableZoom: boolean
@@ -27,6 +32,11 @@ export interface AdvancedCanvasPluginSettings {
2732
export const DEFAULT_SETTINGS: Partial<AdvancedCanvasPluginSettings> = {
2833
shapesFeatureEnabled: true,
2934

35+
commandsFeatureEnabled: true,
36+
zoomToClonedNode: true,
37+
cloneNodeMargin: 25,
38+
expandNodeStepSize: 25,
39+
3040
betterReadonlyEnabled: true,
3141
disableNodePopup: false,
3242
disableZoom: false,
@@ -99,6 +109,44 @@ export class AdvancedCanvasPluginSettingTab extends PluginSettingTab {
99109
.onChange(async (value) => await this.settingsManager.setSetting({ shapesFeatureEnabled: value }))
100110
)
101111

112+
new Setting(containerEl)
113+
.setHeading()
114+
.setName("Extended Commands")
115+
.setDesc("Add more commands to the canvas.")
116+
.addToggle((toggle) =>
117+
toggle
118+
.setTooltip("Requires a reload to take effect.")
119+
.setValue(this.settingsManager.getSetting('commandsFeatureEnabled'))
120+
.onChange(async (value) => await this.settingsManager.setSetting({ commandsFeatureEnabled: value }))
121+
)
122+
123+
new Setting(containerEl)
124+
.setName("Zoom to cloned node")
125+
.setDesc("When enabled, the canvas will zoom to the cloned node.")
126+
.addToggle((toggle) =>
127+
toggle
128+
.setValue(this.settingsManager.getSetting('zoomToClonedNode'))
129+
.onChange(async (value) => await this.settingsManager.setSetting({ zoomToClonedNode: value }))
130+
)
131+
132+
new Setting(containerEl)
133+
.setName("Clone node margin")
134+
.setDesc("The margin between the cloned node and the source node.")
135+
.addText((text) =>
136+
text
137+
.setValue(this.settingsManager.getSetting('cloneNodeMargin').toString())
138+
.onChange(async (value) => await this.settingsManager.setSetting({ cloneNodeMargin: parseInt(value) }))
139+
)
140+
141+
new Setting(containerEl)
142+
.setName("Expand node step size")
143+
.setDesc("The step size for expanding the node.")
144+
.addText((text) =>
145+
text
146+
.setValue(this.settingsManager.getSetting('expandNodeStepSize').toString())
147+
.onChange(async (value) => await this.settingsManager.setSetting({ expandNodeStepSize: parseInt(value) }))
148+
)
149+
102150
new Setting(containerEl)
103151
.setHeading()
104152
.setName("Better readonly")

0 commit comments

Comments
 (0)