// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using FlaxEngine; using FlaxEngine.GUI; namespace FlaxEditor.Surface { /// /// Base class for Visject Surface components like nodes or comments. Used to unify various logic parts across different surface elements. /// /// [HideInEditor] public abstract class SurfaceControl : ContainerControl { /// /// The mouse position in local control space. Updates by auto. /// protected Float2 _mousePosition = Float2.Minimum; /// /// The is selected flag for element. /// protected bool _isSelected; /// /// The context. /// public readonly VisjectSurfaceContext Context; /// /// The surface. /// public readonly VisjectSurface Surface; /// /// Gets a value indicating whether this control is selected. /// public bool IsSelected { get => _isSelected; internal set { _isSelected = value; OnSelectionChanged(); } } /// /// Gets the mouse position (in local control space). /// public Float2 MousePosition => _mousePosition; /// /// Initializes a new instance of the class. /// /// The context. /// The initial width. /// The initial height. protected SurfaceControl(VisjectSurfaceContext context, float width, float height) : base(0, 0, width, height) { ClipChildren = false; Surface = context.Surface; Context = context; } /// /// Initializes a new instance of the class. /// /// The surface. /// The initial bounds. protected SurfaceControl(VisjectSurface surface, ref Rectangle bounds) : base(bounds) { ClipChildren = false; Surface = surface; } /// /// Determines whether this element can be selected by the mouse at the specified location. /// /// The mouse location (in surface space). /// true if this instance can be selected by mouse at the specified location; otherwise, false. public abstract bool CanSelect(ref Float2 location); /// /// Determines whether selection rectangle is intersecting with the surface control area that can be selected. /// /// The selection rectangle (in surface space, not the control space). /// true if the selection rectangle is intersecting with the selectable parts of the control ; otherwise, false. public virtual bool IsSelectionIntersecting(ref Rectangle selectionRect) { return Bounds.Intersects(ref selectionRect); } /// /// Called when surface can edit state gets changed. Can be used to enable/disable UI elements that are surface data editing. /// /// True if can edit surface data, otherwise false. public virtual void OnSurfaceCanEditChanged(bool canEdit) { } /// /// Called when editor is showing secondary context menu. Can be used to inject custom options for node logic. /// /// The menu. /// The show location (in control space). public virtual void OnShowSecondaryContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu menu, Float2 location) { } /// /// Called after changes /// protected virtual void OnSelectionChanged() { } /// /// Called when control gets loaded and added to surface. /// public virtual void OnLoaded(SurfaceNodeActions action) { } /// /// Called when surface gets loaded and nodes boxes are connected. /// public virtual void OnSurfaceLoaded(SurfaceNodeActions action) { // Snap bounds (with ceil) when using grid snapping if (Surface != null && Surface.GridSnappingEnabled) { var bounds = Bounds; bounds.Location = Surface.SnapToGrid(bounds.Location, false); bounds.Size = Surface.SnapToGrid(bounds.Size, true); Bounds = bounds; } UpdateRectangles(); } /// /// Called after adding the control to the surface after user spawn (eg. add comment, add new node, etc.). /// public virtual void OnSpawned(SurfaceNodeActions action) { } /// /// Called on removing the control from the surface after user delete/cut operation (eg. delete comment, cut node, etc.). /// public virtual void OnDeleted(SurfaceNodeActions action) { Dispose(); } /// /// Updates the cached rectangles on control size change. /// protected abstract void UpdateRectangles(); /// public override void OnMouseEnter(Float2 location) { _mousePosition = location; base.OnMouseEnter(location); } /// public override void OnMouseMove(Float2 location) { _mousePosition = location; base.OnMouseMove(location); } /// public override void OnMouseLeave() { _mousePosition = Float2.Minimum; base.OnMouseLeave(); } /// protected override void SetScaleInternal(ref Float2 scale) { base.SetScaleInternal(ref scale); UpdateRectangles(); } /// protected override void OnSizeChanged() { base.OnSizeChanged(); UpdateRectangles(); } } }