Skip to content

Commit 0d88566

Browse files
Merge branch 'beta'
2 parents 0a02825 + 1ffd00f commit 0d88566

File tree

4 files changed

+124
-21
lines changed

4 files changed

+124
-21
lines changed

src/@types/Canvas.d.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,8 @@ export interface CanvasElement {
170170
initialize(): void
171171
setColor(color: string): void
172172

173+
getBBox(): BBox
174+
173175
getData(): CanvasNodeData | CanvasEdgeData
174176
setData(data: CanvasNodeData | CanvasEdgeData): void
175177
}
@@ -188,6 +190,8 @@ export interface CanvasNodeData {
188190
collapsedData?: CanvasData
189191

190192
isStartNode?: boolean
193+
sideRatio?: number
194+
191195
edgesToNodeFromPortal?: { [key: string]: CanvasEdgeData[] }
192196

193197
// Portal node
@@ -207,7 +211,6 @@ export interface CanvasNodeData {
207211

208212
export interface CanvasNode extends CanvasElement {
209213
nodeEl: HTMLElement
210-
getBBox(): BBox
211214

212215
labelEl?: HTMLElement
213216
file?: TFile

src/canvas-extensions/presentation-canvas-extension.ts

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Notice } from 'obsidian'
2-
import { Canvas, CanvasEdge, CanvasNode, Position, Size } from 'src/@types/Canvas'
1+
import { Menu, Notice } from 'obsidian'
2+
import { BBox, Canvas, CanvasEdge, CanvasElement, CanvasNode, Position, Size } from 'src/@types/Canvas'
33
import AdvancedCanvasPlugin from 'src/main'
44
import { CanvasEvent } from 'src/events/events'
55
import * as CanvasHelper from "src/utils/canvas-helper"
@@ -19,6 +19,23 @@ export default class PresentationCanvasExtension {
1919

2020
if (!this.plugin.settings.getSetting('presentationFeatureEnabled')) return
2121

22+
/* Add wrap in slide option to context menu */
23+
this.plugin.registerEvent(this.plugin.app.workspace.on(
24+
'canvas:selection-menu',
25+
(menu: Menu, canvas: Canvas) => {
26+
menu.addItem((item) =>
27+
item
28+
.setTitle('Wrap in slide')
29+
.setIcon('gallery-vertical')
30+
.onClick(() => this.addSlide(canvas, undefined,
31+
BBoxHelper.enlargeBBox(BBoxHelper.combineBBoxes(
32+
[...canvas.selection.values()].map((element: CanvasElement) => element.getBBox())
33+
), this.plugin.settings.getSetting('wrapInSlidePadding'))
34+
))
35+
)
36+
}
37+
))
38+
2239
this.plugin.addCommand({
2340
id: 'create-new-slide',
2441
name: 'Create new slide',
@@ -69,6 +86,11 @@ export default class PresentationCanvasExtension {
6986
CanvasEvent.PopupMenuCreated,
7087
(canvas: Canvas) => this.onPopupMenuCreated(canvas)
7188
))
89+
90+
this.plugin.registerEvent(this.plugin.app.workspace.on(
91+
CanvasEvent.NodeMoved,
92+
(canvas: Canvas, node: CanvasNode) => this.onNodeMoved(canvas, node)
93+
))
7294
}
7395

7496
onCanvasChanged(canvas: Canvas): void {
@@ -81,7 +103,7 @@ export default class PresentationCanvasExtension {
81103
label: 'Drag to add slide',
82104
icon: 'gallery-vertical'
83105
},
84-
() => this.getSlideSize(),
106+
() => this.getDefaultSlideSize(),
85107
(canvas: Canvas, pos: Position) => this.addSlide(canvas, pos)
86108
)
87109
)
@@ -105,6 +127,28 @@ export default class PresentationCanvasExtension {
105127
})
106128
)
107129
}
130+
131+
private onNodeMoved(_canvas: Canvas, node: CanvasNode) {
132+
const nodeData = node.getData()
133+
if (!nodeData.sideRatio) return
134+
135+
const nodeBBox = node.getBBox()
136+
const nodeSize = {
137+
width: nodeBBox.maxX - nodeBBox.minX,
138+
height: nodeBBox.maxY - nodeBBox.minY
139+
}
140+
const nodeAspectRatio = nodeSize.width / nodeSize.height
141+
142+
if (nodeAspectRatio < nodeData.sideRatio)
143+
nodeSize.width = nodeSize.height * nodeData.sideRatio
144+
else nodeSize.height = nodeSize.width / nodeData.sideRatio
145+
146+
node.setData({
147+
...nodeData,
148+
width: nodeSize.width,
149+
height: nodeSize.height
150+
})
151+
}
108152

109153
private getStartNode(canvas: Canvas): CanvasNode|undefined {
110154
for (const [_, node] of canvas.nodes) {
@@ -123,26 +167,56 @@ export default class PresentationCanvasExtension {
123167
if (node !== startNode) node.setData({ ...node.getData(), isStartNode: true })
124168
}
125169

126-
private getSlideSize(): Size {
170+
private getDefaultSlideSize(): Size {
127171
const slideSizeString = this.plugin.settings.getSetting('defaultSlideSize')
128172
const slideSizeArray = slideSizeString.split('x').map((value: string) => parseInt(value))
129173
return { width: slideSizeArray[0], height: slideSizeArray[1] }
130174
}
131175

132-
private addSlide(canvas: Canvas, pos?: Position) {
133-
if (!pos) pos = CanvasHelper.getCenterCoordinates(canvas, this.getSlideSize())
176+
private getSlideAspectRatio(): number {
177+
const slideSize = this.getDefaultSlideSize()
178+
return slideSize.width / slideSize.height
179+
}
134180

181+
private addSlide(canvas: Canvas, pos?: Position, bbox?: BBox) {
135182
const isStartNode = this.getStartNode(canvas) == null
136-
const nodeSize = this.getSlideSize()
183+
const slideSize = this.getDefaultSlideSize()
184+
const slideAspectRatio = this.getSlideAspectRatio()
185+
186+
if (bbox) {
187+
const bboxWidth = bbox.maxX - bbox.minX
188+
const bboxHeight = bbox.maxY - bbox.minY
189+
190+
// Make sure the nodes fit inside the bounding box while keeping the aspect ratio
191+
if (bboxWidth / bboxHeight > slideAspectRatio) {
192+
slideSize.width = bboxWidth
193+
slideSize.height = bboxWidth / slideAspectRatio
194+
} else {
195+
slideSize.height = bboxHeight
196+
slideSize.width = bboxHeight * slideAspectRatio
197+
}
198+
199+
pos = {
200+
x: bbox.minX,
201+
y: bbox.minY
202+
}
203+
}
204+
205+
// If no position is provided, use the center of the canvas
206+
if (!pos) pos = CanvasHelper.getCenterCoordinates(canvas, this.getDefaultSlideSize())
137207

138208
const groupNode = canvas.createGroupNode({
139209
pos: pos,
140-
size: nodeSize,
210+
size: slideSize,
141211
label: isStartNode ? START_SLIDE_NAME : DEFAULT_SLIDE_NAME,
142212
focus: false,
143213
})
144214

145-
if (isStartNode) groupNode.setData({ ...groupNode.getData(), isStartNode: true })
215+
groupNode.setData({
216+
...groupNode.getData(),
217+
sideRatio: slideAspectRatio,
218+
isStartNode: isStartNode ? true : undefined,
219+
})
146220
}
147221

148222
private async animateNodeTransition(canvas: Canvas, fromNode: CanvasNode|undefined, toNode: CanvasNode) {

src/settings.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ const NODE_TYPES_ON_DOUBLE_CLICK = {
66
'file': 'File'
77
}
88

9-
const SLIDE_SIZE_OPTIONS = {
9+
/*const SLIDE_SIZE_OPTIONS = {
1010
'1200x675': '16:9',
1111
'1350x900': '3:2',
12-
}
12+
}*/
1313

1414
export interface AdvancedCanvasPluginSettings {
1515
nodeTypeOnDoubleClick: string
@@ -42,6 +42,7 @@ export interface AdvancedCanvasPluginSettings {
4242

4343
presentationFeatureEnabled: boolean
4444
defaultSlideSize: string
45+
wrapInSlidePadding: number
4546
useArrowKeysToChangeSlides: boolean
4647
zoomToSlideWithoutPadding: boolean
4748
slideTransitionAnimationDuration: number
@@ -70,8 +71,8 @@ export const DEFAULT_SETTINGS: Partial<AdvancedCanvasPluginSettings> = {
7071

7172
commandsFeatureEnabled: true,
7273
zoomToClonedNode: true,
73-
cloneNodeMargin: 25,
74-
expandNodeStepSize: 25,
74+
cloneNodeMargin: 20,
75+
expandNodeStepSize: 20,
7576

7677
betterReadonlyEnabled: true,
7778
disableNodePopup: false,
@@ -84,7 +85,8 @@ export const DEFAULT_SETTINGS: Partial<AdvancedCanvasPluginSettings> = {
8485
stickersFeatureEnabled: true,
8586

8687
presentationFeatureEnabled: true,
87-
defaultSlideSize: Object.keys(SLIDE_SIZE_OPTIONS).first(),
88+
defaultSlideSize: '1200x675',
89+
wrapInSlidePadding: 20,
8890
useArrowKeysToChangeSlides: true,
8991
zoomToSlideWithoutPadding: true,
9092
slideTransitionAnimationDuration: 0.5,
@@ -346,13 +348,21 @@ export class AdvancedCanvasPluginSettingTab extends PluginSettingTab {
346348

347349
new Setting(containerEl)
348350
.setName("Default slide ratio")
349-
.setDesc("The default ratio of the slide.")
350-
.addDropdown((dropdown) =>
351-
dropdown
352-
.addOptions(SLIDE_SIZE_OPTIONS)
351+
.setDesc("The default ratio of the slide. For example, 16:9 is 1200x675 and 3:2 is 1350x900.")
352+
.addText((text) =>
353+
text
353354
.setValue(this.settingsManager.getSetting('defaultSlideSize'))
354-
.onChange(async (value) => await this.settingsManager.setSetting({ defaultSlideSize: value }))
355-
)
355+
.onChange(async (value) => await this.settingsManager.setSetting({ defaultSlideSize: value.replace(' ', '') }))
356+
)
357+
358+
new Setting(containerEl)
359+
.setName("Wrap in slide padding")
360+
.setDesc("The padding of the slide when wrapping the canvas in a slide.")
361+
.addText((text) =>
362+
text
363+
.setValue(this.settingsManager.getSetting('wrapInSlidePadding').toString())
364+
.onChange(async (value) => await this.settingsManager.setSetting({ wrapInSlidePadding: parseInt(value) }))
365+
)
356366

357367
new Setting(containerEl)
358368
.setName("Use arrow keys to change slides")

src/utils/bbox-helper.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
import { BBox, CanvasNodeData, Position, Side } from "src/@types/Canvas"
22

3+
export function combineBBoxes(bboxes: BBox[]): BBox {
4+
let minX = Infinity
5+
let minY = Infinity
6+
let maxX = -Infinity
7+
let maxY = -Infinity
8+
9+
for (let bbox of bboxes) {
10+
minX = Math.min(minX, bbox.minX)
11+
minY = Math.min(minY, bbox.minY)
12+
maxX = Math.max(maxX, bbox.maxX)
13+
maxY = Math.max(maxY, bbox.maxY)
14+
}
15+
16+
return { minX, minY, maxX, maxY }
17+
}
18+
319
export function scaleBBox(bbox: BBox, scale: number): BBox {
420
let diffX = (scale - 1) * (bbox.maxX - bbox.minX)
521
let diffY = (scale - 1) * (bbox.maxY - bbox.minY)

0 commit comments

Comments
 (0)