@leafer-in/editor 1.0.0-rc.6 → 1.0.0-rc.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,279 @@
1
+ import { IRect, IAround, IEventListenerId, IBoundsData, IRectInputData, IPointData, IKeyEvent, IGroup, IBox } from '@leafer-ui/interface'
2
+ import { Group, DragEvent, PointerEvent, Box } from '@leafer-ui/core'
3
+
4
+ import { IEditBox, IEditor, IDirection8, IEditPoint, IEditPointType } from '@leafer-in/interface'
5
+
6
+ import { updateCursor, updateMoveCursor } from '../editor/cursor'
7
+ import { EditorEvent } from '../event/EditorEvent'
8
+ import { EditPoint } from './EditPoint'
9
+ import { EditDataHelper } from '../helper/EditDataHelper'
10
+
11
+
12
+ const fourDirection = ['top', 'right', 'bottom', 'left']
13
+
14
+ export class EditBox extends Group implements IEditBox {
15
+
16
+ public editor: IEditor
17
+ public dragging: boolean
18
+
19
+ public rect: IBox = new Box({ name: 'rect', hitFill: 'all', hitStroke: 'none', strokeAlign: 'center', hitRadius: 5 }) // target rect
20
+ public circle: IEditPoint = new EditPoint({ name: 'circle', strokeAlign: 'outside', around: 'center', cursor: 'crosshair', hitRadius: 5 }) // rotate point
21
+
22
+ public buttons: IGroup = new Group({ around: 'center', hitSelf: false })
23
+
24
+ public resizePoints: IEditPoint[] = [] // topLeft, top, topRight, right, bottomRight, bottom, bottomLeft, left
25
+ public rotatePoints: IEditPoint[] = [] // topLeft, top, topRight, right, bottomRight, bottom, bottomLeft, left
26
+ public resizeLines: IEditPoint[] = [] // top, right, bottom, left
27
+
28
+ // fliped
29
+ public get flipped(): boolean { return this.flippedX || this.flippedY }
30
+ public get flippedX(): boolean { return this.scaleX < 0 }
31
+ public get flippedY(): boolean { return this.scaleY < 0 }
32
+ public get flippedOne(): boolean { return this.scaleX * this.scaleY < 0 }
33
+
34
+ public enterPoint: IEditPoint
35
+
36
+ protected __eventIds: IEventListenerId[] = []
37
+
38
+ constructor(editor: IEditor) {
39
+ super()
40
+ this.editor = editor
41
+ this.visible = false
42
+ this.create()
43
+ this.__listenEvents()
44
+ }
45
+
46
+ public create() {
47
+ let rotatePoint: IEditPoint, resizeLine: IEditPoint, resizePoint: IEditPoint
48
+ const { resizePoints, rotatePoints, resizeLines, rect, circle, buttons } = this
49
+ const arounds: IAround[] = [{ x: 1, y: 1 }, { x: 0.5, y: 1 }, { x: 0, y: 1 }, { x: 0, y: 0.5 }, { x: 0, y: 0 }, { x: 0.5, y: 0 }, { x: 1, y: 0 }, { x: 1, y: 0.5 }]
50
+
51
+ for (let i = 0; i < 8; i++) {
52
+ rotatePoint = new EditPoint({ around: arounds[i], width: 15, height: 15, hitFill: "all" })
53
+ rotatePoints.push(rotatePoint)
54
+ this.listenPointEvents(rotatePoint, 'rotate', i)
55
+
56
+ if (i % 2) {
57
+ resizeLine = new EditPoint({ name: 'resize-line', around: 'center', width: 10, height: 10, hitFill: "all" })
58
+ resizeLines.push(resizeLine)
59
+ this.listenPointEvents(resizeLine, 'resize', i)
60
+ }
61
+
62
+ resizePoint = new EditPoint({ name: 'resize-point', around: 'center', strokeAlign: 'outside', hitRadius: 5 })
63
+ resizePoints.push(resizePoint)
64
+ this.listenPointEvents(resizePoint, 'resize', i)
65
+ }
66
+
67
+ buttons.add(circle)
68
+ this.listenPointEvents(circle, 'rotate', 2)
69
+
70
+ this.addMany(...rotatePoints, rect, buttons, ...resizeLines, ...resizePoints)
71
+ }
72
+
73
+ // update
74
+
75
+ public update(bounds: IBoundsData): void {
76
+ const { config, list } = this.editor
77
+ const { width, height } = bounds
78
+ const { rect, circle, resizePoints, rotatePoints, resizeLines } = this
79
+ const { middlePoint, resizeable, rotateable, stroke, strokeWidth } = config
80
+
81
+ const points = this.getDirection8Points(bounds)
82
+ const pointsStyle = this.getPointsStyle()
83
+ const middlePointsStyle = this.getMiddlePointsStyle()
84
+
85
+ this.visible = list[0] && !list[0].locked // check locked
86
+
87
+ let point: IPointData, style: IRectInputData, rotateP: IRect, resizeP: IRect, resizeL: IRect
88
+
89
+ for (let i = 0; i < 8; i++) {
90
+
91
+ point = points[i], style = this.getPointStyle((i % 2) ? middlePointsStyle[((i - 1) / 2) % middlePointsStyle.length] : pointsStyle[(i / 2) % pointsStyle.length])
92
+ resizeP = resizePoints[i], rotateP = rotatePoints[i], resizeL = resizeLines[Math.floor(i / 2)]
93
+
94
+ resizeP.set(style)
95
+ resizeP.set(point), rotateP.set(point), resizeL.set(point)
96
+
97
+ // visible
98
+ resizeP.visible = resizeL.visible = resizeable || rotateable
99
+ rotateP.visible = rotateable && resizeable
100
+
101
+ if (i % 2) { // top, right, bottom, left
102
+
103
+ resizeP.visible = rotateP.visible = !!middlePoint
104
+
105
+ if (((i + 1) / 2) % 2) { // top, bottom
106
+ resizeL.width = width
107
+ if (resizeP.width > width - 30) resizeP.visible = false
108
+ } else {
109
+ resizeL.height = height
110
+ resizeP.rotation = 90
111
+ if (resizeP.width > height - 30) resizeP.visible = false
112
+ }
113
+ } else {
114
+ resizeP.rotation = (i / 2) * 90
115
+ }
116
+
117
+ }
118
+
119
+ // rotate
120
+ circle.visible = rotateable && !!config.rotatePoint
121
+ circle.set(this.getPointStyle(config.rotatePoint || pointsStyle[0]))
122
+
123
+ // rect
124
+ rect.set({ stroke, strokeWidth, ...(config.rect || {}) })
125
+ rect.set({ ...bounds, visible: true })
126
+
127
+ // buttons
128
+ this.layoutButtons()
129
+ }
130
+
131
+ protected layoutButtons(): void {
132
+ const { buttons, resizePoints } = this
133
+ const { buttonsDirection, buttonsFixed, buttonsMargin, middlePoint } = this.editor.config
134
+
135
+ const { flippedX, flippedY } = this
136
+ let index = fourDirection.indexOf(buttonsDirection)
137
+ if ((index % 2 && flippedX) || ((index + 1) % 2 && flippedY)) {
138
+ if (buttonsFixed) index = (index + 2) % 4 // flip x / y
139
+ }
140
+ const direction = buttonsFixed ? EditDataHelper.getRotateDirection(index, this.flippedOne ? this.rotation : -this.rotation, 4) : index
141
+
142
+ const point = resizePoints[direction * 2 + 1] // 4 map 8 direction
143
+ const useX = direction % 2 // left / right
144
+ const sign = (!direction || direction === 3) ? -1 : 1 // top / left = -1
145
+
146
+ const useWidth = index % 2 // left / right origin direction
147
+ const margin = (buttonsMargin + (useWidth ? ((middlePoint ? point.width : 0) + buttons.boxBounds.width) : ((middlePoint ? point.height : 0) + buttons.boxBounds.height)) / 2) * sign
148
+
149
+ if (useX) {
150
+ buttons.x = point.x + margin
151
+ buttons.y = point.y
152
+ } else {
153
+ buttons.x = point.x
154
+ buttons.y = point.y + margin
155
+ }
156
+
157
+ if (buttonsFixed) {
158
+ buttons.rotation = (direction - index) * 90
159
+ buttons.scaleX = flippedX ? -1 : 1
160
+ buttons.scaleY = flippedY ? -1 : 1
161
+ }
162
+
163
+ }
164
+
165
+ public getPointStyle(userStyle?: IRectInputData): IRectInputData {
166
+ const { stroke, strokeWidth, pointFill, pointSize, pointRadius } = this.editor.config
167
+ const defaultStyle = { fill: pointFill, stroke, strokeWidth, width: pointSize, height: pointSize, cornerRadius: pointRadius }
168
+ return userStyle ? Object.assign(defaultStyle, userStyle) : defaultStyle
169
+ }
170
+
171
+ public getPointsStyle(): IRectInputData[] {
172
+ const { point } = this.editor.config
173
+ return point instanceof Array ? point : [point]
174
+ }
175
+
176
+ public getMiddlePointsStyle(): IRectInputData[] {
177
+ const { middlePoint } = this.editor.config
178
+ return middlePoint instanceof Array ? middlePoint : (middlePoint ? [middlePoint] : this.getPointsStyle())
179
+ }
180
+
181
+ public getDirection8Points(bounds: IBoundsData): IPointData[] {
182
+ const { x, y, width, height } = bounds
183
+ return [ // topLeft, top, topRight, right, bottomRight, bottom, bottomLeft, left
184
+ { x, y },
185
+ { x: x + width / 2, y },
186
+ { x: x + width, y },
187
+ { x: x + width, y: y + height / 2 },
188
+ { x: x + width, y: y + height },
189
+ { x: x + width / 2, y: y + height },
190
+ { x, y: y + height },
191
+ { x, y: y + height / 2 }
192
+ ]
193
+ }
194
+
195
+ // drag
196
+
197
+ protected onDragStart(e: DragEvent): void {
198
+ this.dragging = true
199
+ if (e.target.name === 'rect') this.editor.opacity = this.editor.config.hideOnMove ? 0 : 1 // move
200
+ }
201
+
202
+ protected onDragEnd(e: DragEvent): void {
203
+ this.dragging = false
204
+ if (e.target.name === 'rect') this.editor.opacity = 1 // move
205
+ }
206
+
207
+ protected onDrag(e: DragEvent): void {
208
+ const { editor } = this
209
+ const point = e.current as IEditPoint
210
+ if (point.pointType === 'rotate' || e.metaKey || e.ctrlKey || !editor.config.resizeable) {
211
+ if (editor.config.rotateable) editor.onRotate(e)
212
+ } else {
213
+ editor.onScale(e)
214
+ }
215
+ }
216
+
217
+ public onArrow(e: IKeyEvent): void {
218
+ if (this.editor.hasTarget) {
219
+ const move = { x: 0, y: 0 }
220
+ const distance = e.shiftKey ? 10 : 1
221
+ switch (e.code) {
222
+ case 'ArrowDown':
223
+ move.y = distance
224
+ break
225
+ case 'ArrowUp':
226
+ move.y = -distance
227
+ break
228
+ case 'ArrowLeft':
229
+ move.x = -distance
230
+ break
231
+ case 'ArrowRight':
232
+ move.x = distance
233
+ }
234
+ if (move.x || move.y) this.editor.move(move.x, move.y)
235
+ }
236
+ }
237
+
238
+ protected onDoubleClick(): void {
239
+ const { editor } = this
240
+ if (editor.single && editor.element.isBranch) {
241
+ //list[0].hitChildren = true
242
+ }
243
+ }
244
+
245
+ public listenPointEvents(point: IEditPoint, type: IEditPointType, direction: IDirection8): void {
246
+ const { editor } = this
247
+ point.direction = direction
248
+ point.pointType = type
249
+ point.on_(DragEvent.START, this.onDragStart, this)
250
+ point.on_(DragEvent.DRAG, this.onDrag, this)
251
+ point.on_(DragEvent.END, this.onDragEnd, this)
252
+ point.on_(PointerEvent.LEAVE, () => this.enterPoint = null)
253
+ if (point.name !== 'circle') point.on_(PointerEvent.ENTER, (e) => { this.enterPoint = point, updateCursor(editor, e) })
254
+ }
255
+
256
+ protected __listenEvents(): void {
257
+ const { rect, editor } = this
258
+ this.__eventIds = [
259
+ editor.on_(EditorEvent.SELECT, () => { this.visible = editor.hasTarget }),
260
+ rect.on_(DragEvent.START, this.onDragStart, this),
261
+ rect.on_(DragEvent.DRAG, editor.onMove, editor),
262
+ rect.on_(DragEvent.END, this.onDragEnd, this),
263
+ rect.on_(PointerEvent.ENTER, () => updateMoveCursor(editor)),
264
+ rect.on_(PointerEvent.DOUBLE_CLICK, this.onDoubleClick, this)
265
+ ]
266
+ }
267
+
268
+ protected __removeListenEvents(): void {
269
+ this.off_(this.__eventIds)
270
+ this.__eventIds.length = 0
271
+ }
272
+
273
+ public destroy(): void {
274
+ this.editor = null
275
+ this.__removeListenEvents()
276
+ super.destroy()
277
+ }
278
+
279
+ }
@@ -0,0 +1,9 @@
1
+ import { Box } from '@leafer-ui/core'
2
+
3
+ import { IDirection8, IEditPoint, IEditPointType } from '@leafer-in/interface'
4
+
5
+
6
+ export class EditPoint extends Box implements IEditPoint {
7
+ public direction: IDirection8
8
+ public pointType: IEditPointType
9
+ }
@@ -0,0 +1,234 @@
1
+ import { IBounds, ILeaf, ILeafList, IUI, IEventListenerId } from '@leafer-ui/interface'
2
+ import { Bounds, PointerEvent, DragEvent, MoveEvent, LeafList, Group, ZoomEvent } from '@leafer-ui/core'
3
+
4
+ import { IEditSelect, IEditor, ISelectArea, IStroker } from '@leafer-in/interface'
5
+
6
+ import { Stroker } from './Stroker'
7
+ import { SelectArea } from './SelectArea'
8
+ import { EditSelectHelper } from '../helper/EditSelectHelper'
9
+ import { EditorEvent } from '../event/EditorEvent'
10
+
11
+
12
+ const { findOne } = EditSelectHelper
13
+
14
+ export class EditSelect extends Group implements IEditSelect {
15
+
16
+ public editor: IEditor
17
+
18
+ public get dragging(): boolean { return !!this.originList }
19
+ public get running(): boolean { return this.editor.hittable && this.editor.config.selector }
20
+ public get isMoveMode(): boolean { return this.app && this.app.interaction.moveMode }
21
+
22
+ public hoverStroker: IStroker = new Stroker()
23
+ public targetStroker: IStroker = new Stroker()
24
+
25
+ public bounds: IBounds = new Bounds()
26
+ public selectArea: ISelectArea = new SelectArea()
27
+
28
+ protected originList: ILeafList
29
+ protected lastDownLeaf: IUI
30
+
31
+ protected __eventIds: IEventListenerId[] = []
32
+
33
+
34
+ constructor(editor: IEditor) {
35
+ super()
36
+ this.editor = editor
37
+ this.addMany(this.targetStroker, this.hoverStroker, this.selectArea)
38
+ this.__listenEvents()
39
+ }
40
+
41
+ // hover / select
42
+
43
+ protected onHover(): void {
44
+ const { editor } = this
45
+ if (this.running && !this.dragging && !editor.dragging) {
46
+ const { stroke, strokeWidth, hover } = editor.config
47
+ this.hoverStroker.setTarget(hover ? this.editor.hoverTarget : null, { stroke, strokeWidth })
48
+ } else {
49
+ this.hoverStroker.target = null
50
+ }
51
+ }
52
+
53
+ protected onSelect(): void {
54
+ if (this.running) {
55
+ const { config, list } = this.editor
56
+ const { stroke, strokeWidth } = config
57
+ this.targetStroker.setTarget(list, { stroke, strokeWidth: Math.max(1, strokeWidth / 2) })
58
+ this.hoverStroker.target = null
59
+ }
60
+ }
61
+
62
+ public update(): void {
63
+ if (this.running) this.targetStroker.forceUpdate()
64
+ }
65
+
66
+ // move / down
67
+
68
+ protected onPointerMove(e: PointerEvent): void {
69
+ if (this.running && !this.isMoveMode) {
70
+ const find = e.shiftKey ? this.findDeepOne(e) : findOne(e.path)
71
+ this.editor.hoverTarget = this.editor.hasItem(find) ? null : find
72
+ } if (this.isMoveMode) {
73
+ this.editor.hoverTarget = null // move.dragEmpty
74
+ }
75
+ }
76
+
77
+ protected onBeforeDown(e: PointerEvent): void {
78
+ if (this.running && !this.isMoveMode && !e.middle) {
79
+ const find = this.lastDownLeaf = findOne(e.path)
80
+
81
+ if (find) {
82
+
83
+ if (e.shiftKey) {
84
+ this.editor.shiftItem(find)
85
+ } else {
86
+ this.editor.target = find
87
+ }
88
+
89
+ // change down data
90
+ this.editor.updateLayout()
91
+ find.leafer.interaction.updateDownData()
92
+
93
+ } else if (this.allow(e.target)) {
94
+
95
+ if (!e.shiftKey) this.editor.target = null
96
+
97
+ }
98
+ }
99
+ }
100
+
101
+ protected onTap(e: PointerEvent): void {
102
+ if (this.running && e.shiftKey && !e.middle && !this.lastDownLeaf) {
103
+ const find = this.findDeepOne(e)
104
+ if (find) this.editor.shiftItem(find)
105
+ } else if (this.isMoveMode) {
106
+ this.editor.target = null // move.dragEmpty
107
+ }
108
+
109
+ this.lastDownLeaf = null
110
+ }
111
+
112
+ // drag
113
+
114
+ protected onDragStart(e: DragEvent): void {
115
+ if (this.running && this.allowDrag(e)) {
116
+ const { editor } = this
117
+ const { stroke, strokeWidth, area } = editor.config
118
+ const { x, y } = e.getInner(this)
119
+
120
+ this.bounds.set(x, y)
121
+
122
+ this.selectArea.setStyle({ visible: true, stroke, strokeWidth, x, y }, area)
123
+ this.selectArea.setBounds(this.bounds.get())
124
+
125
+ this.originList = editor.leafList.clone()
126
+ }
127
+ }
128
+
129
+ protected onDrag(e: DragEvent): void {
130
+ if (this.editor.dragging) {
131
+ this.onDragEnd()
132
+ return
133
+ }
134
+
135
+ if (this.dragging) {
136
+ const { editor } = this
137
+ const total = e.getInnerTotal(this)
138
+
139
+ const dragBounds = this.bounds.clone().unsign()
140
+ const list = new LeafList(editor.app.find(EditSelectHelper.findBounds, dragBounds))
141
+
142
+ this.bounds.width = total.x
143
+ this.bounds.height = total.y
144
+
145
+ this.selectArea.setBounds(dragBounds.get())
146
+
147
+ if (list.length) {
148
+
149
+ const selectList: ILeaf[] = []
150
+
151
+ this.originList.forEach(item => { if (!list.has(item)) selectList.push(item) })
152
+ list.forEach(item => { if (!this.originList.has(item)) selectList.push(item) })
153
+
154
+ editor.target = selectList as IUI[]
155
+
156
+ } else {
157
+
158
+ editor.target = this.originList
159
+ if (editor.leafList.length) editor.update()
160
+
161
+ }
162
+ }
163
+ }
164
+
165
+ protected onDragEnd(): void {
166
+ if (this.dragging) this.originList = null, this.selectArea.visible = false
167
+ }
168
+
169
+ protected onAutoMove(e: MoveEvent): void {
170
+ if (this.dragging) {
171
+ const { x, y } = e.getLocalMove(this)
172
+ this.bounds.x += x
173
+ this.bounds.y += y
174
+ }
175
+ }
176
+
177
+ // helper
178
+
179
+ protected allow(target: ILeaf): boolean {
180
+ return target.leafer !== this.editor.leafer
181
+ }
182
+
183
+ protected allowDrag(e: DragEvent) {
184
+ if (this.editor.config.boxSelect && !e.target.draggable) {
185
+ return (!this.editor.hasTarget && this.allow(e.target)) || (e.shiftKey && !findOne(e.path))
186
+ } else {
187
+ return false
188
+ }
189
+ }
190
+
191
+ protected findDeepOne(e: PointerEvent): IUI {
192
+ const options = { exclude: new LeafList(this.editor.editBox.rect) }
193
+ return findOne(e.target.leafer.interaction.findPath(e, options)) as IUI
194
+ }
195
+
196
+ protected __listenEvents(): void {
197
+ const { editor } = this
198
+ editor.waitLeafer(() => {
199
+
200
+ const { app } = editor
201
+ app.selector.proxy = editor
202
+
203
+ this.__eventIds = [
204
+ editor.on_(EditorEvent.HOVER, this.onHover, this),
205
+ editor.on_(EditorEvent.SELECT, this.onSelect, this),
206
+
207
+ app.on_(PointerEvent.MOVE, this.onPointerMove, this),
208
+ app.on_(PointerEvent.BEFORE_DOWN, this.onBeforeDown, this),
209
+ app.on_(PointerEvent.TAP, this.onTap, this),
210
+
211
+ app.on_(DragEvent.START, this.onDragStart, this),
212
+ app.on_(DragEvent.DRAG, this.onDrag, this),
213
+ app.on_(DragEvent.END, this.onDragEnd, this),
214
+
215
+ app.on_(MoveEvent.MOVE, this.onAutoMove, this),
216
+ app.on_([ZoomEvent.ZOOM, MoveEvent.MOVE], () => { this.editor.hoverTarget = null }),
217
+ ]
218
+
219
+ })
220
+ }
221
+
222
+ protected __removeListenEvents(): void {
223
+ if (this.__eventIds) {
224
+ this.off_(this.__eventIds)
225
+ this.__eventIds.length = 0
226
+ }
227
+ }
228
+
229
+ public destroy(): void {
230
+ this.editor = this.originList = this.lastDownLeaf = null
231
+ this.__removeListenEvents()
232
+ super.destroy()
233
+ }
234
+ }
@@ -0,0 +1,30 @@
1
+ import { IBoundsData, IGroupInputData, IRect, IRectInputData } from '@leafer-ui/interface'
2
+ import { Group, Rect } from '@leafer-ui/core'
3
+
4
+ import { ISelectArea } from '@leafer-in/interface'
5
+
6
+
7
+ export class SelectArea extends Group implements ISelectArea {
8
+
9
+ protected strokeArea: IRect = new Rect({ strokeAlign: 'center' })
10
+ protected fillArea: IRect = new Rect()
11
+
12
+ constructor(data?: IGroupInputData) {
13
+ super(data)
14
+ this.visible = this.hittable = false
15
+ this.addMany(this.fillArea, this.strokeArea)
16
+ }
17
+
18
+ public setStyle(style: IRectInputData, userStyle?: IRectInputData): void {
19
+ const { visible, stroke, strokeWidth } = style
20
+ this.visible = visible
21
+ this.strokeArea.reset({ stroke, strokeWidth, ...(userStyle || {}) })
22
+ this.fillArea.reset({ visible: userStyle ? false : true, fill: stroke, opacity: 0.2 })
23
+ }
24
+
25
+ public setBounds(bounds: IBoundsData): void {
26
+ this.strokeArea.set(bounds)
27
+ this.fillArea.set(bounds)
28
+ }
29
+
30
+ }
@@ -0,0 +1,89 @@
1
+ import { IUI, ILeaferCanvas, IRenderOptions, IRectInputData } from '@leafer-ui/interface'
2
+ import { Paint, UI, MatrixHelper } from '@leafer-ui/core'
3
+
4
+ import { IStroker } from '@leafer-in/interface'
5
+
6
+ import { targetAttr } from '../decorator/data'
7
+
8
+
9
+ const matrix = MatrixHelper.get()
10
+ const { abs } = Math
11
+ const { copy, scale } = MatrixHelper
12
+
13
+ export class Stroker extends UI implements IStroker {
14
+
15
+ @targetAttr(onTarget)
16
+ public target: IUI | IUI[]
17
+
18
+ public list: IUI[] = []
19
+
20
+ constructor() {
21
+ super()
22
+ this.hittable = false
23
+ this.strokeAlign = 'center'
24
+ }
25
+
26
+ public setTarget(target: IUI | IUI[], style: IRectInputData): void {
27
+ const { stroke, strokeWidth } = style
28
+ this.set({ stroke, strokeWidth })
29
+ this.target = target
30
+ }
31
+
32
+ public __draw(canvas: ILeaferCanvas, options: IRenderOptions): void {
33
+ const { list } = this
34
+ if (list.length) {
35
+
36
+ let leaf: IUI
37
+ const { stroke, strokeWidth } = this.__
38
+ const { bounds } = options
39
+
40
+ for (let i = 0; i < list.length; i++) {
41
+ leaf = list[i]
42
+ if (bounds && bounds.hit(leaf.__world, options.matrix)) {
43
+
44
+ let drewPath: boolean
45
+
46
+ if (leaf.__.editSize === 'scale') {
47
+ const aScaleX = abs(leaf.__world.scaleX), aScaleY = abs(leaf.__world.scaleY)
48
+ if (aScaleX !== aScaleY) { // need no scale stroke
49
+ copy(matrix, leaf.__world)
50
+ scale(matrix, 1 / aScaleX, 1 / aScaleY)
51
+
52
+ canvas.setWorld(matrix, options.matrix)
53
+ canvas.beginPath()
54
+ this.__.strokeWidth = strokeWidth
55
+
56
+ const { x, y, width, height } = leaf.__layout.boxBounds
57
+ canvas.rect(x * aScaleX, y * aScaleY, width * aScaleX, height * aScaleY)
58
+
59
+ drewPath = true
60
+ }
61
+ }
62
+
63
+ if (!drewPath) {
64
+ canvas.setWorld(leaf.__world, options.matrix)
65
+ canvas.beginPath()
66
+ leaf.__.__pathForRender ? leaf.__drawRenderPath(canvas) : leaf.__drawPathByBox(canvas)
67
+ this.__.strokeWidth = strokeWidth / abs(leaf.__world.scaleX)
68
+ }
69
+
70
+ typeof stroke === 'string' ? Paint.stroke(stroke, this, canvas, options) : Paint.strokes(stroke, this, canvas, options)
71
+ }
72
+ }
73
+
74
+ this.__.strokeWidth = strokeWidth
75
+ }
76
+ }
77
+
78
+ public destroy(): void {
79
+ this.target = null
80
+ super.destroy()
81
+ }
82
+
83
+ }
84
+
85
+ function onTarget(stroker: Stroker): void {
86
+ const value = stroker.target
87
+ stroker.list = value ? (value instanceof Array ? value : [value]) : []
88
+ stroker.forceUpdate()
89
+ }
@@ -0,0 +1,58 @@
1
+ import { ICursorType, IUIEvent } from '@leafer-ui/interface'
2
+ import { IDirection8, IEditor } from '@leafer-in/interface'
3
+
4
+ import { EditDataHelper } from '../helper/EditDataHelper'
5
+
6
+
7
+ const { topLeft, top, topRight, right, bottomRight, bottom, bottomLeft, left } = IDirection8
8
+
9
+ export function updateCursor(editor: IEditor, e: IUIEvent): void {
10
+ const { editBox } = editor, point = editBox.enterPoint
11
+ if (!point || !editor.hasTarget || !editBox.visible) return
12
+
13
+ let { rotation } = editBox
14
+ let { resizeCursor, rotateCursor, resizeable, rotateable } = editor.config
15
+ const { direction, pointType } = point
16
+
17
+ editBox.enterPoint = point
18
+ const isResizePoint = pointType === 'resize'
19
+
20
+ if (isResizePoint && rotateable && (e.metaKey || e.ctrlKey || !resizeable)) resizeCursor = rotateCursor
21
+
22
+ if (editBox.flipped) {
23
+ const { flippedX, flippedY } = editBox
24
+ mirrorCursors(resizeCursor = [...resizeCursor], flippedX, flippedY)
25
+ mirrorCursors(rotateCursor = [...rotateCursor], flippedY, flippedX)
26
+ if (editBox.flippedOne) rotation = -rotation
27
+ }
28
+
29
+ const index = EditDataHelper.getRotateDirection(direction, rotation)
30
+ point.cursor = isResizePoint ? resizeCursor[index] : rotateCursor[index]
31
+ }
32
+
33
+ export function updateMoveCursor(editor: IEditor): void {
34
+ editor.editBox.rect.cursor = editor.config.moveCursor
35
+ }
36
+
37
+
38
+ export function mirrorCursors(mirror: ICursorType[], mirrorX: boolean, mirrorY: boolean): void {
39
+ if (mirrorX) {
40
+ const topCursor = mirror[top], topLeftCursor = mirror[topLeft], topRightCursor = mirror[topRight]
41
+ mirror[top] = mirror[bottom]
42
+ mirror[topLeft] = mirror[bottomLeft]
43
+ mirror[topRight] = mirror[bottomRight]
44
+ mirror[bottom] = topCursor
45
+ mirror[bottomLeft] = topLeftCursor
46
+ mirror[bottomRight] = topRightCursor
47
+ }
48
+
49
+ if (mirrorY) {
50
+ const leftCursor = mirror[left], topLeftCursor = mirror[topLeft], bottomLeftCursor = mirror[bottomLeft]
51
+ mirror[left] = mirror[right]
52
+ mirror[topLeft] = mirror[topRight]
53
+ mirror[bottomLeft] = mirror[bottomRight]
54
+ mirror[right] = leftCursor
55
+ mirror[topRight] = topLeftCursor
56
+ mirror[bottomRight] = bottomLeftCursor
57
+ }
58
+ }