Skip to content

Commit 5408329

Browse files
committed
refactor(spx-gui): move quick-config panels and improve monitor node sync
1 parent 219e55c commit 5408329

File tree

16 files changed

+439
-497
lines changed

16 files changed

+439
-497
lines changed

spx-gui/src/components/editor/common/viewer/SpriteNode.vue

Lines changed: 60 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts" setup>
2-
import { computed, onMounted, ref, watch, watchEffect } from 'vue'
2+
import { computed, onMounted, ref, watchEffect } from 'vue'
33
import type { Stage } from 'konva/lib/Stage'
44
import type { Shape } from 'konva/lib/Shape'
55
import type { KonvaEventObject } from 'konva/lib/Node'
@@ -10,9 +10,11 @@ import type { Size } from '@/models/common'
1010
import { normalizeDegree, round, useAsyncComputedLegacy } from '@/utils/utils'
1111
import { useFileImg } from '@/utils/file'
1212
import { cancelBubble, getNodeId } from './common'
13+
import type { SpriteLocalConfig } from './quick-config/utils'
1314
1415
const props = defineProps<{
1516
sprite: Sprite
17+
localConfig: SpriteLocalConfig | null
1618
selected: boolean
1719
project: Project
1820
mapSize: Size
@@ -28,12 +30,6 @@ const emit = defineEmits<{
2830
selected: []
2931
dragMove: [notifyCameraScroll: CameraScrollNotifyFn]
3032
dragEnd: []
31-
updatePos: [{ x: number; y: number }]
32-
updatePosEnd: [{ x: number; y: number }]
33-
updateHeading: [{ heading: number; leftRight?: LeftRight }]
34-
updateHeadingEnd: [{ heading: number; leftRight?: LeftRight }]
35-
updateSize: [{ size: number }]
36-
updateSizeEnd: [{ size: number }]
3733
}>()
3834
3935
const nodeRef = ref<KonvaNodeInstance<Image>>()
@@ -62,75 +58,86 @@ onMounted(() => {
6258
}
6359
})
6460
65-
function updateSprite({
66-
oldSize,
67-
size,
68-
oldHeading,
69-
heading,
61+
function updateLocalConfig({
7062
oldX,
7163
x,
7264
oldY,
7365
y,
74-
isEnd = false
66+
oldSize,
67+
size,
68+
oldHeading,
69+
heading
7570
}: {
76-
oldSize: number
77-
size: number
78-
oldHeading: number
79-
heading: number
8071
oldX: number
8172
x: number
8273
oldY: number
8374
y: number
84-
isEnd?: boolean
75+
oldSize: number
76+
size: number
77+
oldHeading: number
78+
heading: number
8579
}) {
86-
if (oldSize !== size) {
87-
if (isEnd) {
88-
emit('updateSizeEnd', { size })
89-
} else {
90-
emit('updateSize', { size })
91-
}
80+
const spriteLocalConfig = props.localConfig
81+
if (spriteLocalConfig == null) return
82+
if (size !== oldSize) {
83+
spriteLocalConfig.setSize(size)
9284
return
9385
}
94-
if (oldHeading !== heading && props.sprite.rotationStyle !== RotationStyle.None) {
95-
let leftRight: LeftRight | undefined = undefined
96-
if (props.sprite.rotationStyle === RotationStyle.LeftRight) {
97-
leftRight = headingToLeftRight(heading)
98-
}
99-
if (isEnd) {
100-
emit('updateHeadingEnd', { heading, leftRight })
101-
} else {
102-
emit('updateHeading', { heading, leftRight })
103-
}
86+
if (heading !== oldHeading && spriteLocalConfig.rotationStyle === RotationStyle.Normal) {
87+
spriteLocalConfig.setHeading(heading)
10488
return
10589
}
106-
if (oldX !== x || oldY !== y) {
107-
if (isEnd) {
108-
emit('updatePosEnd', { x, y })
109-
} else {
110-
emit('updatePos', { x, y })
111-
}
90+
if (x !== oldX || y !== oldY) {
91+
spriteLocalConfig.setX(x)
92+
spriteLocalConfig.setY(y)
11293
}
11394
}
114-
115-
const notifyUpdateSprite = (node: Shape | Stage, isEnd = false) => {
116-
if (!props.selected) return
95+
function updateLocalConfigByShape(node: Shape | Stage) {
96+
if (!props.selected || props.localConfig == null) return
11797
const { x, y } = toPosition(node)
118-
updateSprite({
119-
oldSize: props.sprite.size,
120-
size: toSize(node),
121-
oldHeading: props.sprite.heading,
122-
heading: toHeading(node),
98+
updateLocalConfig({
12399
oldX: props.sprite.x,
124100
x,
125101
oldY: props.sprite.y,
126102
y,
127-
isEnd
103+
oldSize: props.sprite.size,
104+
size: toSize(node),
105+
oldHeading: props.sprite.heading,
106+
heading: toHeading(node)
107+
})
108+
}
109+
110+
function syncLocalConfig({ size, x, y, heading }: { size: number; x: number; y: number; heading: number }) {
111+
const spriteLocalConfig = props.localConfig
112+
if (spriteLocalConfig == null) return
113+
if (size != null && props.sprite.size !== size) {
114+
spriteLocalConfig.setSize(size, false)
115+
spriteLocalConfig.syncSize()
116+
return
117+
}
118+
if (props.sprite.heading !== heading) {
119+
spriteLocalConfig.setHeading(heading, false)
120+
spriteLocalConfig.syncHeading()
121+
return
122+
}
123+
if (props.sprite.x !== x || props.sprite.y !== y) {
124+
spriteLocalConfig.setX(x, false)
125+
spriteLocalConfig.setX(x, false)
126+
spriteLocalConfig.syncPos()
127+
}
128+
}
129+
function syncLocalConfigByShape(node: Shape | Stage) {
130+
syncLocalConfig({
131+
size: toSize(node),
132+
x: toPosition(node).x,
133+
y: toPosition(node).y,
134+
heading: toHeading(node)
128135
})
129136
}
130137
131138
function handleDragMove(e: KonvaEventObject<unknown>) {
132139
cancelBubble(e)
133-
notifyUpdateSprite(e.target)
140+
updateLocalConfigByShape(e.target)
134141
emit('dragMove', (delta) => {
135142
// Adjust position if camera scrolled during dragging to keep the sprite visually unmoved
136143
e.target.x(e.target.x() - delta.x)
@@ -140,12 +147,12 @@ function handleDragMove(e: KonvaEventObject<unknown>) {
140147
141148
function handleDragEnd(e: KonvaEventObject<unknown>) {
142149
cancelBubble(e)
143-
notifyUpdateSprite(e.target, true)
150+
syncLocalConfigByShape(e.target)
144151
emit('dragEnd')
145152
}
146153
147154
function handleTransformed(e: KonvaEventObject<unknown>) {
148-
notifyUpdateSprite(e.target, true)
155+
syncLocalConfigByShape(e.target)
149156
}
150157
151158
const config = computed<ImageConfig>(() => {
@@ -177,27 +184,6 @@ const config = computed<ImageConfig>(() => {
177184
return config
178185
})
179186
180-
// After Sprite changes, updateXXX events also need to be triggered
181-
watch(config, () => {
182-
const node = nodeRef.value?.getNode()
183-
if (node != null) {
184-
const { x, y, heading, size } = props.sprite
185-
const { x: oldX, y: oldY } = toPosition(node)
186-
const oldHeading = toHeading(node)
187-
const oldSize = toSize(node)
188-
updateSprite({
189-
oldSize,
190-
size,
191-
oldHeading,
192-
heading,
193-
oldX,
194-
x,
195-
oldY,
196-
y
197-
})
198-
}
199-
})
200-
201187
function toPosition(node: Shape | Stage) {
202188
const { mapSize } = props
203189
const x = round(node.x() - mapSize.width / 2)
@@ -228,7 +214,7 @@ function handleClick() {
228214
:config="config"
229215
@dragmove="handleDragMove"
230216
@dragend="handleDragEnd"
231-
@transform="notifyUpdateSprite($event.target)"
217+
@transform="updateLocalConfigByShape($event.target)"
232218
@transformend="handleTransformed"
233219
@click="handleClick"
234220
/>

spx-gui/src/components/editor/common/viewer/quick-config/SpriteQuickConfig.vue

Lines changed: 11 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,27 @@
22
import { inject, watch } from 'vue'
33
44
import DefaultConfigPanel from './sprite/DefaultConfigPanel.vue'
5-
import { RotationStyle, type Sprite } from '@/models/sprite'
5+
import { RotationStyle } from '@/models/sprite'
66
import type { Project } from '@/models/project'
7-
import SizeConfigPanel from './sprite/SizeConfigPanel.vue'
7+
import SizeConfigPanel from './common/SizeConfigPanel.vue'
88
import HeadingConfigPanel from './sprite/HeadingConfigPanel.vue'
9-
import PositionConfigPanel from './sprite/PositionConfigPanel.vue'
9+
import PositionConfigPanel from './common/PositionConfigPanel.vue'
1010
import { configTypeInjectionKey, updateConfigTypesInjectionKey } from './QuickConfigWrapper.vue'
11+
import type { SpriteLocalConfig } from './utils'
1112
1213
const props = defineProps<{
13-
sprite: Sprite
14+
localConfig: SpriteLocalConfig
1415
project: Project
15-
size?: number
16-
heading?: number
17-
x?: number
18-
y?: number
19-
}>()
20-
21-
defineEmits<{
22-
'update:size': [{ size: number }]
23-
'update:heading': [{ heading: number }]
24-
'update:pos': [{ x: number; y: number }]
2516
}>()
2617
2718
const configType = inject(configTypeInjectionKey)
2819
const update = inject(updateConfigTypesInjectionKey)
2920
3021
watch(
31-
() => props.sprite.rotationStyle,
22+
() => props.localConfig.rotationStyle,
3223
() => {
3324
// If the selected sprite's rotationStyle is LeftRight, it needs to be restored to default immediately
34-
if (props.sprite.rotationStyle === RotationStyle.LeftRight) {
25+
if (props.localConfig.rotationStyle === RotationStyle.LeftRight) {
3526
update?.(['default'])
3627
}
3728
}
@@ -40,26 +31,10 @@ watch(
4031

4132
<template>
4233
<template v-if="configType != null">
43-
<SizeConfigPanel
44-
v-if="configType === 'size' && size != null"
45-
:sprite="sprite"
46-
:size="size"
47-
@update:size="$emit('update:size', $event)"
48-
/>
49-
<HeadingConfigPanel
50-
v-else-if="configType === 'rotate' && heading != null"
51-
:sprite="sprite"
52-
:heading="heading"
53-
@update:heading="$emit('update:heading', $event)"
54-
/>
55-
<PositionConfigPanel
56-
v-else-if="configType === 'pos' && x != null && y != null"
57-
:sprite="sprite"
58-
:x="x"
59-
:y="y"
60-
@update:pos="$emit('update:pos', $event)"
61-
/>
62-
<DefaultConfigPanel v-else-if="configType === 'default'" :sprite="sprite" :project="project" />
34+
<SizeConfigPanel v-if="configType === 'size'" name="sprite" :local-config="localConfig" />
35+
<HeadingConfigPanel v-else-if="configType === 'rotate'" :local-config="localConfig" />
36+
<PositionConfigPanel v-else-if="configType === 'pos'" name="sprite" :local-config="localConfig" />
37+
<DefaultConfigPanel v-else-if="configType === 'default'" :local-config="localConfig" :project="project" />
6338
</template>
6439
</template>
6540

spx-gui/src/components/editor/common/viewer/quick-config/WidgetQuickConfig.vue

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,24 @@ import { inject } from 'vue'
33
44
import type { Project } from '@/models/project'
55
import { configTypeInjectionKey } from './QuickConfigWrapper.vue'
6-
import type { Widget } from '@/models/widget'
76
import DefaultConfigPanel from './widget/DefaultConfigPanel.vue'
8-
import SizeConfigPanel from './widget/SizeConfigPanel.vue'
9-
import PositionConfigPanel from './widget/PositionConfigPanel.vue'
7+
import SizeConfigPanel from './common/SizeConfigPanel.vue'
8+
import PositionConfigPanel from './common/PositionConfigPanel.vue'
9+
import type { WidgetLocalConfig } from './utils'
1010
1111
defineProps<{
12-
widget: Widget
12+
localConfig: WidgetLocalConfig
1313
project: Project
14-
size?: number
15-
x?: number
16-
y?: number
17-
}>()
18-
19-
defineEmits<{
20-
'update:size': [{ size: number }]
21-
'update:pos': [{ x: number; y: number }]
2214
}>()
2315
2416
const configType = inject(configTypeInjectionKey)
2517
</script>
2618

2719
<template>
2820
<template v-if="configType != null">
29-
<SizeConfigPanel
30-
v-if="configType === 'size' && size != null"
31-
:widget="widget"
32-
:size="size"
33-
@update:size="$emit('update:size', $event)"
34-
/>
35-
<PositionConfigPanel
36-
v-else-if="configType === 'pos' && x != null && y != null"
37-
:widget="widget"
38-
:x="x"
39-
:y="y"
40-
@update:pos="$emit('update:pos', $event)"
41-
/>
42-
<DefaultConfigPanel v-else-if="configType === 'default'" :widget="widget" :project="project" />
21+
<SizeConfigPanel v-if="configType === 'size'" name="monitor" :local-config="localConfig" />
22+
<PositionConfigPanel v-else-if="configType === 'pos'" name="monitor" :local-config="localConfig" />
23+
<DefaultConfigPanel v-else-if="configType === 'default'" :local-config="localConfig" :project="project" />
4324
</template>
4425
</template>
4526

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<script lang="ts" setup>
2+
import { UINumberInput } from '@/components/ui'
3+
import ConfigPanel from '../common/ConfigPanel.vue'
4+
import type { LocalConfig } from '../utils'
5+
6+
const props = defineProps<{
7+
name: 'sprite' | 'monitor'
8+
localConfig: LocalConfig
9+
}>()
10+
11+
function handleUpdateX(x: number) {
12+
props.localConfig.setX(x)
13+
props.localConfig.syncSize()
14+
}
15+
function handleUpdateY(y: number) {
16+
props.localConfig.setY(y)
17+
props.localConfig.syncSize()
18+
}
19+
</script>
20+
21+
<template>
22+
<ConfigPanel>
23+
<div class="position-config-wrapper">
24+
<UINumberInput
25+
v-radar="{ name: 'X position input', desc: `Input to set ${name} X position` }"
26+
:value="localConfig.x"
27+
@update:value="handleUpdateX($event ?? 0)"
28+
>
29+
<template #prefix>X</template>
30+
</UINumberInput>
31+
<UINumberInput
32+
v-radar="{ name: 'Y position input', desc: `Input to set ${name} Y position` }"
33+
:value="localConfig.y"
34+
@update:value="handleUpdateY($event ?? 0)"
35+
>
36+
<template #prefix>Y</template>
37+
</UINumberInput>
38+
</div>
39+
</ConfigPanel>
40+
</template>
41+
42+
<style lang="scss" scoped>
43+
.position-config-wrapper {
44+
display: flex;
45+
gap: 4px;
46+
width: 158px;
47+
}
48+
</style>

0 commit comments

Comments
 (0)