@@ -18,7 +18,6 @@ import {
1818 Action ,
1919 ChangeBoundsOperation ,
2020 ElementAndBounds ,
21- ElementMove ,
2221 IActionDispatcher ,
2322 IActionHandler ,
2423 ICommand ,
@@ -27,24 +26,42 @@ 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.
56+ *
57+ * Examples: nudging with arrow keys
4558 */
4659@injectable ( )
4760export class MoveElementHandler implements IActionHandler {
61+ @inject ( TYPES . IChangeBoundsManager )
62+ protected readonly changeBoundsManager : IChangeBoundsManager ;
63+ protected tracker : ChangeBoundsTracker ;
64+
4865 @inject ( EditorContextService )
4966 protected editorContextService : EditorContextService ;
5067
@@ -68,10 +85,12 @@ export class MoveElementHandler implements IActionHandler {
6885 @postConstruct ( )
6986 protected init ( ) : void {
7087 this . moveFeedback = this . feedbackDispatcher . createEmitter ( ) ;
88+ this . tracker = this . changeBoundsManager . createTracker ( ) ;
7189 }
7290
7391 handle ( action : Action ) : void | Action | ICommand {
74- if ( MoveElementRelativeAction . is ( action ) ) {
92+ if ( MoveElementRelativeAction . is ( action ) && action . elementIds . length > 0 ) {
93+ this . tracker . startTracking ( this . editorContextService . modelRoot ) ;
7594 this . handleMoveElement ( action ) ;
7695 }
7796 }
@@ -84,7 +103,7 @@ export class MoveElementHandler implements IActionHandler {
84103
85104 const viewportActions : Action [ ] = [ ] ;
86105 const elementMoves : ElementMove [ ] = [ ] ;
87- const elements = getElements ( viewport . index , action . elementIds , isSelectableAndBoundsAware ) ;
106+ const elements = getElements ( viewport . index , action . elementIds , this . isValidMoveable ) ;
88107 for ( const element of elements ) {
89108 const newPosition = this . getTargetBounds ( element , action ) ;
90109 elementMoves . push ( {
@@ -103,12 +122,41 @@ export class MoveElementHandler implements IActionHandler {
103122 viewportActions . push ( MoveViewportAction . create ( { moveX : action . moveX , moveY : action . moveY } ) ) ;
104123 }
105124 }
106-
107125 this . dispatcher . dispatchAll ( viewportActions ) ;
108- const moveAction = MoveAction . create ( elementMoves , { animate : false } ) ;
109- this . moveFeedback . add ( moveAction ) . submit ( ) ;
126+ this . moveFeedback . add ( this . createMoveAction ( elementMoves ) ) ;
127+
128+ const newBounds = elementMoves . map ( this . toElementAndBounds . bind ( this ) ) . filter ( isNotUndefined ) ;
129+ const wraps = this . tracker . wrap (
130+ elements . map ( element => {
131+ const bounds = newBounds . find ( b => b . elementId === element . id ) ! ;
132+ const toBounds : Bounds = {
133+ ...element . bounds ,
134+ ...bounds . newSize ,
135+ ...bounds . newPosition
136+ } ;
137+ return {
138+ element : element ,
139+ fromBounds : element . bounds ,
140+ toBounds
141+ } ;
142+ } ) ,
143+ {
144+ validate : true
145+ }
146+ ) ;
147+
148+ this . moveFeedback . add ( TrackedElementResize . createFeedbackActions ( Object . values ( wraps ?? { } ) ) ) ;
149+ this . moveFeedback . submit ( ) ;
150+
151+ if ( Object . keys ( wraps ) . length > 0 ) {
152+ newBounds . push (
153+ ...Object . values ( wraps )
154+ . filter ( resize => ! action . elementIds . includes ( resize . element . id ) )
155+ . map ( TrackedElementResize . toElementAndBounds )
156+ ) ;
157+ }
110158
111- this . scheduleChangeBounds ( this . toElementAndBounds ( elementMoves ) ) ;
159+ this . scheduleChangeBounds ( newBounds ) ;
112160 }
113161
114162 protected getTargetBounds ( element : SelectableBoundsAware , action : MoveElementRelativeAction ) : Point {
@@ -129,28 +177,35 @@ export class MoveElementHandler implements IActionHandler {
129177 this . moveFeedback . dispose ( ) ;
130178 this . dispatcher . dispatchAll ( [ ChangeBoundsOperation . create ( elementAndBounds ) ] ) ;
131179 this . debouncedChangeBounds = undefined ;
180+ this . tracker . dispose ( ) ;
132181 } , 300 ) ;
133182 this . debouncedChangeBounds ( ) ;
134183 }
135184
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- }
185+ protected createMoveAction ( moves : ElementMove [ ] ) : Action {
186+ return MoveAction . create ( moves , { animate : false } ) ;
187+ }
188+
189+ protected isValidMoveable ( element ?: GModelElement ) : element is MoveableElement & SelectableBoundsAware {
190+ return ! ! element && isNonRoutableSelectedMovableBoundsAware ( element ) && ! ( element instanceof GResizeHandle ) ;
191+ }
192+
193+ protected toElementAndBounds ( elementMove : ElementMove ) : ElementAndBounds | undefined {
194+ const element = this . editorContextService . modelRoot . index . getById ( elementMove . elementId ) ;
195+ if ( element && isBoundsAware ( element ) ) {
196+ return {
197+ elementId : elementMove . elementId ,
198+ newSize : {
199+ height : element . bounds . height ,
200+ width : element . bounds . width
201+ } ,
202+ newPosition : {
203+ x : elementMove . toPosition . x ,
204+ y : elementMove . toPosition . y
205+ }
206+ } ;
153207 }
154- return elementBounds ;
208+
209+ return undefined ;
155210 }
156211}
0 commit comments