import { SocketGraphicsItem } from "./socketgraphicsitem" ; import { GraphicsItem , MouseDownEvent , MouseMoveEvent , MouseUpEvent , MouseOverEvent } from "./graphicsitem" ; import { SceneView , Vector2 , Rect } from "./view" ; import { Color } from "../designer/color" ; import { NodeGraphicsItem } from "./nodegraphicsitem" ; import { IPropertyHolder , Property , StringProperty , BoolProperty , PropertyGroup } from "../designer/properties" ; import { MoveItemsAction } from "../actions/moveItemsaction" ; import { UndoStack } from "../undostack" ; import { ResizeFrameAction } from "../actions/resizeframeaction" ; enum XResizeDir { None , Left , Right } enum YResizeDir { None , Top , Bottom } enum DragMode { None , HandleTop , Resize } export class FrameRegion { rect : Rect = null ; dragMode : DragMode = DragMode . None ; xResizeDir : XResizeDir = XResizeDir . None ; yResizeDir : YResizeDir = YResizeDir . None ; cursor : string = null ; } export class FrameGraphicsItem extends GraphicsItem implements IPropertyHolder { title : string ; description : string ; showTitle : boolean ; view : SceneView ; color : Color ; hit : boolean ; dragged : boolean ; dragStartPos : Vector2 ; dragStartRect : Rect ; xResize : XResizeDir ; yResize : YResizeDir ; dragMode : DragMode ; handleSize : number ; resizeHandleSize : number ; nodes : NodeGraphicsItem [ ] ; titleProp : StringProperty ; showTitleProp : BoolProperty ; descrProp : StringProperty ; public constructor ( view ) { super ( ) ; this . title = "Frame" ; this . description = "" ; this . showTitle = true ; this . view = view ; this . color = new Color ( 0.1 , 0 , 0.2 ) ; this . hit = false ; this . dragged = true ; this . xResize = XResizeDir . None ; this . yResize = YResizeDir . None ; this . dragMode = DragMode . None ; this . handleSize = 30 ; this . resizeHandleSize = 20 ; this . setSize ( 500 , 300 ) ; this . nodes = [ ] ; this . titleProp = new StringProperty ( "title" , "Title" , "Frame" ) ; this . showTitleProp = new BoolProperty ( "showtitle" , "Show Title" , true ) ; this . descrProp = new StringProperty ( "description" , "Description" , "" , true ) ; this . properties . push ( this . titleProp ) ; this . properties . push ( this . showTitleProp ) ; this . properties . push ( this . descrProp ) ; } propertyGroups : PropertyGroup [ ] ; properties : Property [ ] = [ ] ; setProperty ( name , value ) { if ( name == "title" ) { this . setTitle ( value ) ; } else if ( name == "showtitle" ) { this . setShowTitle ( value ) ; } else if ( name == "description" ) { this . setDescription ( value ) ; } } setSize ( w , h ) { this . width = w ; this . height = h ; } setTitle ( text ) { this . title = text ; this . titleProp . setValue ( text ) ; } setShowTitle ( shouldShow ) { this . showTitle = shouldShow ; this . showTitleProp . setValue ( shouldShow ) ; } setDescription ( text ) { this . description = text ; this . descrProp . setValue ( text ) ; } private buildColor ( color , alpha ) { const col = "rgba(" + color . r * 255 + "," + color . g * 255 + "," + color . b * 255 + "," + alpha + ")" ; return col ; } public setPos ( x , y ) { const diff = new Vector2 ( x - this . x , y - this . y ) ; super . setPos ( x , y ) ; } public setFrameRect ( rect ) { this . x = rect . x ; this . y = rect . y ; this . width = rect . width ; this . height = rect . height ; } draw ( ctx , renderData = null ) { ctx . beginPath ( ) ; ctx . lineWidth = 1 ; ctx . strokeStyle = this . buildColor ( this . color , 1 ) ; this . roundRect ( ctx , this . x , this . y , this . width , this . height , 1 ) ; ctx . stroke ( ) ; const handleSize = this . handleSize ; ctx . beginPath ( ) ; ctx . lineWidth = 1 ; ctx . fillStyle = this . buildColor ( this . color , 0.5 ) ; this . roundRect ( ctx , this . x , this . y , this . width , handleSize , 1 ) ; ctx . fill ( ) ; ctx . beginPath ( ) ; ctx . lineWidth = 1 ; ctx . strokeStyle = this . buildColor ( this . color , 0.8 ) ; this . roundRect ( ctx , this . x , this . y , this . width , handleSize , 1 ) ; ctx . stroke ( ) ; ctx . beginPath ( ) ; ctx . lineWidth = 2 ; ctx . fillStyle = this . buildColor ( this . color , 0.2 ) ; this . roundRect ( ctx , this . x , this . y + handleSize , this . width , this . height - handleSize , 1 ) ; ctx . fill ( ) ; if ( this . showTitle == true ) { ctx . beginPath ( ) ; const fontSize = 18 ; ctx . save ( ) ; ctx . setTransform ( 1 , 0 , 0 , 1 , this . view . offset . x , this . view . offset . y ) ; ctx . font = "30px 'Open Sans'" ; ctx . fillStyle = "rgb(240, 240, 240)" ; const textX = this . x ; const textY = this . y ; ctx . fillText ( this . title , textX * this . view . zoomFactor , ( textY - 5 ) * this . view . zoomFactor ) ; ctx . restore ( ) ; } } public isPointInside ( px , py ) { const regions = this . getFrameRegions ( ) ; for ( const region of regions ) { if ( region . rect . isPointInside ( px , py ) ) { return true ; } } if ( px >= this . x && px <= this . x + this . width && py >= this . y && py <= this . y + this . handleSize ) return true ; return false ; } public mouseDown ( evt ) { this . hit = true ; this . dragged = false ; const px = evt . globalX ; const py = evt . globalY ; let hitRegion = null ; const regions = this . getFrameRegions ( ) ; for ( const region of regions ) { if ( region . rect . isPointInside ( px , py ) ) { this . dragMode = region . dragMode ; this . xResize = region . xResizeDir ; this . yResize = region . yResizeDir ; this . dragStartRect = this . getRect ( ) ; hitRegion = region ; break ; } } if ( hitRegion == null ) { if ( px >= this . x && px <= this . x + this . width && py >= this . y && py <= this . y + this . handleSize ) { this . dragMode = DragMode . HandleTop ; this . xResize = XResizeDir . None ; this . yResize = YResizeDir . None ; this . dragStartPos = new Vector2 ( this . x , this . y ) ; if ( ! evt . altKey ) this . nodes = this . getHoveredNodes ( ) ; this . view . canvas . style . cursor = "grabbing" ; } } } public mouseOver ( evt ) { const px = evt . globalX ; const py = evt . globalY ; const hitRegion = null ; const regions = this . getFrameRegions ( ) ; for ( const region of regions ) { if ( region . rect . isPointInside ( px , py ) ) { this . view . canvas . style . cursor = region . cursor ; return ; } } if ( hitRegion == null ) { if ( px >= this . x && px <= this . x + this . width && py >= this . y && py <= this . y + this . handleSize ) { this . view . canvas . style . cursor = "grab" ; } } } getHoveredNodes ( ) { const nodes = [ ] ; for ( const node of this . scene . nodes ) { if ( node . left >= this . left && node . right <= this . right && node . top >= this . top && node . bottom <= this . bottom ) { nodes . push ( node ) ; } } return nodes ; } public mouseMove ( evt ) { if ( this . hit ) { if ( this . dragMode == DragMode . HandleTop ) { this . move ( evt . deltaX , evt . deltaY ) ; for ( const node of this . nodes ) { node . move ( evt . deltaX , evt . deltaY ) ; } } if ( this . dragMode == DragMode . Resize ) { if ( this . xResize == XResizeDir . Left ) { this . left += evt . deltaX ; this . width -= evt . deltaX ; } if ( this . xResize == XResizeDir . Right ) { this . width += evt . deltaX ; } if ( this . yResize == YResizeDir . Top ) { this . top += evt . deltaY ; this . height -= evt . deltaY ; } if ( this . yResize == YResizeDir . Bottom ) { this . height += evt . deltaY ; } this . height = Math . max ( this . height , this . handleSize + this . resizeHandleSize ) ; this . width = Math . max ( this . width , this . resizeHandleSize * 2 ) ; } this . dragged = true ; } } public mouseUp ( evt ) { if ( this . dragged ) { if ( this . dragMode == DragMode . HandleTop ) { const newPos = new Vector2 ( this . x , this . y ) ; const items = [ this ] ; const oldPosList = [ this . dragStartPos . clone ( ) ] ; const newPosList = [ newPos ] ; const diff = new Vector2 ( this . dragStartPos . x - newPos . x , this . dragStartPos . y - newPos . y ) ; for ( const node of this . nodes ) { items . push ( node ) ; const itemNewPos = new Vector2 ( node . left , node . top ) ; const itemOldPos = Vector2 . add ( itemNewPos , diff ) ; newPosList . push ( itemNewPos ) ; oldPosList . push ( itemOldPos ) ; } const action = new MoveItemsAction ( items , oldPosList , newPosList ) ; UndoStack . current . push ( action ) ; } else if ( this . dragMode == DragMode . Resize ) { const action = new ResizeFrameAction ( this , this . dragStartRect . clone ( ) , this . getRect ( ) . clone ( ) ) ; UndoStack . current . push ( action ) ; } } this . hit = false ; this . dragMode = DragMode . None ; this . nodes = [ ] ; this . view . canvas . style . cursor = "default" ; } getFrameRegions ( ) { const regions = [ ] ; const frameRect = this . getRect ( ) ; let rect = null ; let region = null ; region = new FrameRegion ( ) ; rect = new Rect ( ) ; rect . x = frameRect . right - this . resizeHandleSize ; rect . y = frameRect . bottom - this . resizeHandleSize ; rect . width = this . resizeHandleSize ; rect . height = this . resizeHandleSize ; region . rect = rect ; region . dragMode = DragMode . Resize ; region . xResizeDir = XResizeDir . Right ; region . yResizeDir = YResizeDir . Bottom ; region . cursor = "nwse-resize" ; regions . push ( region ) ; region = new FrameRegion ( ) ; rect = new Rect ( ) ; rect . x = frameRect . left ; rect . y = frameRect . bottom - this . resizeHandleSize ; rect . width = this . resizeHandleSize ; rect . height = this . resizeHandleSize ; region . rect = rect ; region . dragMode = DragMode . Resize ; region . xResizeDir = XResizeDir . Left ; region . yResizeDir = YResizeDir . Bottom ; region . cursor = "nesw-resize" ; regions . push ( region ) ; region = new FrameRegion ( ) ; rect = new Rect ( ) ; rect . x = frameRect . left ; rect . y = frameRect . top ; rect . width = this . resizeHandleSize ; rect . height = this . resizeHandleSize ; region . rect = rect ; region . dragMode = DragMode . Resize ; region . xResizeDir = XResizeDir . Left ; region . yResizeDir = YResizeDir . Top ; region . cursor = "nw-resize" ; regions . push ( region ) ; region = new FrameRegion ( ) ; rect = new Rect ( ) ; rect . x = frameRect . right - this . resizeHandleSize ; rect . y = frameRect . top ; rect . width = this . resizeHandleSize ; rect . height = this . resizeHandleSize ; region . rect = rect ; region . dragMode = DragMode . Resize ; region . xResizeDir = XResizeDir . Right ; region . yResizeDir = YResizeDir . Top ; region . cursor = "ne-resize" ; regions . push ( region ) ; region = new FrameRegion ( ) ; rect = new Rect ( ) ; rect . x = frameRect . left ; rect . y = frameRect . top ; rect . width = this . resizeHandleSize ; rect . height = frameRect . height ; region . rect = rect ; region . dragMode = DragMode . Resize ; region . xResizeDir = XResizeDir . Left ; region . yResizeDir = YResizeDir . None ; region . cursor = "w-resize" ; regions . push ( region ) ; region = new FrameRegion ( ) ; rect = new Rect ( ) ; rect . x = frameRect . right - this . resizeHandleSize ; rect . y = frameRect . top ; rect . width = this . resizeHandleSize ; rect . height = frameRect . height ; region . rect = rect ; region . dragMode = DragMode . Resize ; region . xResizeDir = XResizeDir . Right ; region . yResizeDir = YResizeDir . None ; region . cursor = "e-resize" ; regions . push ( region ) ; region = new FrameRegion ( ) ; rect = new Rect ( ) ; rect . x = frameRect . left ; rect . y = frameRect . top ; rect . width = frameRect . width ; rect . height = this . resizeHandleSize * 0.5 ; region . rect = rect ; region . dragMode = DragMode . Resize ; region . xResizeDir = XResizeDir . None ; region . yResizeDir = YResizeDir . Top ; region . cursor = "n-resize" ; regions . push ( region ) ; region = new FrameRegion ( ) ; rect = new Rect ( ) ; rect . x = frameRect . left ; rect . y = frameRect . bottom - this . resizeHandleSize ; rect . width = frameRect . width ; rect . height = this . resizeHandleSize ; region . rect = rect ; region . dragMode = DragMode . Resize ; region . xResizeDir = XResizeDir . None ; region . yResizeDir = YResizeDir . Bottom ; region . cursor = "s-resize" ; regions . push ( region ) ; return regions ; } }