@@ -18,7 +18,6 @@ import {
1818 Action ,
1919 ChangeBoundsOperation ,
2020 ElementAndBounds ,
21- ElementMove ,
2221 IActionDispatcher ,
2322 IActionHandler ,
2423 ICommand ,
@@ -27,24 +26,40 @@ import {
2726 MoveViewportAction ,
2827 Point ,
2928 TYPES ,
30- isBoundsAware
29+ isBoundsAware ,
30+ type Bounds ,
31+ type ElementMove ,
32+ type GModelElement
3133} from '@eclipse-glsp/sprotty' ;
3234import { inject , injectable , optional , postConstruct } from 'inversify' ;
3335import { DebouncedFunc , debounce } from 'lodash' ;
3436import { EditorContextService } from '../../base/editor-context-service' ;
3537import { IFeedbackActionDispatcher } from '../../base/feedback/feedback-action-dispatcher' ;
3638import { FeedbackEmitter } from '../../base/feedback/feedback-emitter' ;
37- import { SelectableBoundsAware , getElements , isSelectableAndBoundsAware } from '../../utils/gmodel-util' ;
39+ import {
40+ SelectableBoundsAware ,
41+ getElements ,
42+ isNonRoutableSelectedMovableBoundsAware ,
43+ isNotUndefined ,
44+ type MoveableElement
45+ } from '../../utils/gmodel-util' ;
3846import { isValidMove } from '../../utils/layout-utils' ;
3947import { outsideOfViewport } from '../../utils/viewpoint-util' ;
4048import { IMovementRestrictor } from '../change-bounds/movement-restrictor' ;
49+ import type { IChangeBoundsManager } from '../tools/change-bounds/change-bounds-manager' ;
50+ import { TrackedElementResize , type ChangeBoundsTracker } from '../tools/change-bounds/change-bounds-tracker' ;
51+ import { GResizeHandle } from './model' ;
4152import { MoveElementRelativeAction } from './move-element-action' ;
4253
4354/**
4455 * Action handler for moving elements.
4556 */
4657@injectable ( )
4758export class MoveElementHandler implements IActionHandler {
59+ @inject ( TYPES . IChangeBoundsManager )
60+ protected readonly changeBoundsManager : IChangeBoundsManager ;
61+ protected tracker : ChangeBoundsTracker ;
62+
4863 @inject ( EditorContextService )
4964 protected editorContextService : EditorContextService ;
5065
@@ -68,10 +83,12 @@ export class MoveElementHandler implements IActionHandler {
6883 @postConstruct ( )
6984 protected init ( ) : void {
7085 this . moveFeedback = this . feedbackDispatcher . createEmitter ( ) ;
86+ this . tracker = this . changeBoundsManager . createTracker ( ) ;
7187 }
7288
7389 handle ( action : Action ) : void | Action | ICommand {
74- if ( MoveElementRelativeAction . is ( action ) ) {
90+ if ( MoveElementRelativeAction . is ( action ) && action . elementIds . length > 0 ) {
91+ this . tracker . startTracking ( this . editorContextService . modelRoot ) ;
7592 this . handleMoveElement ( action ) ;
7693 }
7794 }
@@ -84,7 +101,7 @@ export class MoveElementHandler implements IActionHandler {
84101
85102 const viewportActions : Action [ ] = [ ] ;
86103 const elementMoves : ElementMove [ ] = [ ] ;
87- const elements = getElements ( viewport . index , action . elementIds , isSelectableAndBoundsAware ) ;
104+ const elements = getElements ( viewport . index , action . elementIds , this . isValidMoveable ) ;
88105 for ( const element of elements ) {
89106 const newPosition = this . getTargetBounds ( element , action ) ;
90107 elementMoves . push ( {
@@ -103,12 +120,41 @@ export class MoveElementHandler implements IActionHandler {
103120 viewportActions . push ( MoveViewportAction . create ( { moveX : action . moveX , moveY : action . moveY } ) ) ;
104121 }
105122 }
106-
107123 this . dispatcher . dispatchAll ( viewportActions ) ;
108- const moveAction = MoveAction . create ( elementMoves , { animate : false } ) ;
109- this . moveFeedback . add ( moveAction ) . submit ( ) ;
124+ this . moveFeedback . add ( this . createMoveAction ( elementMoves ) ) ;
125+
126+ const newBounds = elementMoves . map ( this . toElementAndBounds . bind ( this ) ) . filter ( isNotUndefined ) ;
127+ const wraps = this . tracker . wrap (
128+ elements . map ( element => {
129+ const bounds = newBounds . find ( b => b . elementId === element . id ) ! ;
130+ const toBounds : Bounds = {
131+ ...element . bounds ,
132+ ...bounds . newSize ,
133+ ...bounds . newPosition
134+ } ;
135+ return {
136+ element : element ,
137+ fromBounds : element . bounds ,
138+ toBounds
139+ } ;
140+ } ) ,
141+ {
142+ validate : true
143+ }
144+ ) ;
145+
146+ this . moveFeedback . add ( TrackedElementResize . createFeedbackActions ( Object . values ( wraps ?? { } ) ) ) ;
147+ this . moveFeedback . submit ( ) ;
148+
149+ if ( Object . keys ( wraps ) . length > 0 ) {
150+ newBounds . push (
151+ ...Object . values ( wraps )
152+ . filter ( resize => ! action . elementIds . includes ( resize . element . id ) )
153+ . map ( TrackedElementResize . toElementAndBounds )
154+ ) ;
155+ }
110156
111- this . scheduleChangeBounds ( this . toElementAndBounds ( elementMoves ) ) ;
157+ this . scheduleChangeBounds ( newBounds ) ;
112158 }
113159
114160 protected getTargetBounds ( element : SelectableBoundsAware , action : MoveElementRelativeAction ) : Point {
@@ -129,28 +175,35 @@ export class MoveElementHandler implements IActionHandler {
129175 this . moveFeedback . dispose ( ) ;
130176 this . dispatcher . dispatchAll ( [ ChangeBoundsOperation . create ( elementAndBounds ) ] ) ;
131177 this . debouncedChangeBounds = undefined ;
178+ this . tracker . dispose ( ) ;
132179 } , 300 ) ;
133180 this . debouncedChangeBounds ( ) ;
134181 }
135182
136- protected toElementAndBounds ( elementMoves : ElementMove [ ] ) : ElementAndBounds [ ] {
137- const elementBounds : ElementAndBounds [ ] = [ ] ;
138- for ( const elementMove of elementMoves ) {
139- const element = this . editorContextService . modelRoot . index . getById ( elementMove . elementId ) ;
140- if ( element && isBoundsAware ( element ) ) {
141- elementBounds . push ( {
142- elementId : elementMove . elementId ,
143- newSize : {
144- height : element . bounds . height ,
145- width : element . bounds . width
146- } ,
147- newPosition : {
148- x : elementMove . toPosition . x ,
149- y : elementMove . toPosition . y
150- }
151- } ) ;
152- }
183+ protected createMoveAction ( moves : ElementMove [ ] ) : Action {
184+ return MoveAction . create ( moves , { animate : false } ) ;
185+ }
186+
187+ protected isValidMoveable ( element ?: GModelElement ) : element is MoveableElement & SelectableBoundsAware {
188+ return ! ! element && isNonRoutableSelectedMovableBoundsAware ( element ) && ! ( element instanceof GResizeHandle ) ;
189+ }
190+
191+ protected toElementAndBounds ( elementMove : ElementMove ) : ElementAndBounds | undefined {
192+ const element = this . editorContextService . modelRoot . index . getById ( elementMove . elementId ) ;
193+ if ( element && isBoundsAware ( element ) ) {
194+ return {
195+ elementId : elementMove . elementId ,
196+ newSize : {
197+ height : element . bounds . height ,
198+ width : element . bounds . width
199+ } ,
200+ newPosition : {
201+ x : elementMove . toPosition . x ,
202+ y : elementMove . toPosition . y
203+ }
204+ } ;
153205 }
154- return elementBounds ;
206+
207+ return undefined ;
155208 }
156209}
0 commit comments