mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2024-11-29 10:22:53 +08:00
1117 lines
47 KiB
C#
1117 lines
47 KiB
C#
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
using System.Text;
|
|||
|
|
|||
|
using System.Drawing;
|
|||
|
using System.Windows.Forms;
|
|||
|
using System.Collections;
|
|||
|
/*
|
|||
|
MIT License
|
|||
|
|
|||
|
Copyright (c) 2021 DebugST@crystal_lz
|
|||
|
|
|||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|||
|
of this software and associated documentation files (the "Software"), to deal
|
|||
|
in the Software without restriction, including without limitation the rights
|
|||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
|
copies of the Software, and to permit persons to whom the Software is
|
|||
|
furnished to do so, subject to the following conditions:
|
|||
|
|
|||
|
The above copyright notice and this permission notice shall be included in all
|
|||
|
copies or substantial portions of the Software.
|
|||
|
|
|||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||
|
SOFTWARE.
|
|||
|
*/
|
|||
|
/*
|
|||
|
* create: 2021-12-08
|
|||
|
* modify: 2021-03-02
|
|||
|
* Author: Crystal_lz
|
|||
|
* blog: http://st233.com
|
|||
|
* Gitee: https://gitee.com/DebugST
|
|||
|
* Github: https://github.com/DebugST
|
|||
|
*/
|
|||
|
namespace ST.Library.UI.NodeEditor
|
|||
|
{
|
|||
|
public abstract class STNode
|
|||
|
{
|
|||
|
private STNodeEditor _Owner;
|
|||
|
/// <summary>
|
|||
|
/// Get the current Node owner
|
|||
|
/// </summary>
|
|||
|
public STNodeEditor Owner {
|
|||
|
get { return _Owner; }
|
|||
|
internal set {
|
|||
|
if (value == _Owner) return;
|
|||
|
if (_Owner != null) {
|
|||
|
foreach (STNodeOption op in this._InputOptions.ToArray()) op.DisConnectionAll();
|
|||
|
foreach (STNodeOption op in this._OutputOptions.ToArray()) op.DisConnectionAll();
|
|||
|
}
|
|||
|
_Owner = value;
|
|||
|
if (!this._AutoSize) this.SetOptionsLocation();
|
|||
|
this.BuildSize(true, true, false);
|
|||
|
this.OnOwnerChanged();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private bool _IsSelected;
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets whether Node is selected
|
|||
|
/// </summary>
|
|||
|
public bool IsSelected {
|
|||
|
get { return _IsSelected; }
|
|||
|
set {
|
|||
|
if (value == _IsSelected) return;
|
|||
|
_IsSelected = value;
|
|||
|
this.Invalidate();
|
|||
|
this.OnSelectedChanged();
|
|||
|
if (this._Owner != null) this._Owner.OnSelectedChanged(EventArgs.Empty);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private bool _IsActive;
|
|||
|
/// <summary>
|
|||
|
/// Gets if Node is active
|
|||
|
/// </summary>
|
|||
|
public bool IsActive {
|
|||
|
get { return _IsActive; }
|
|||
|
internal set {
|
|||
|
if (value == _IsActive) return;
|
|||
|
_IsActive = value;
|
|||
|
this.OnActiveChanged();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private Color _TitleColor;
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets the title background color
|
|||
|
/// </summary>
|
|||
|
public Color TitleColor {
|
|||
|
get { return _TitleColor; }
|
|||
|
protected set {
|
|||
|
_TitleColor = value;
|
|||
|
this.Invalidate(new Rectangle(0, 0, this._Width, this._TitleHeight));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private Color _MarkColor;
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets the background color of the marker information
|
|||
|
/// </summary>
|
|||
|
public Color MarkColor {
|
|||
|
get { return _MarkColor; }
|
|||
|
protected set {
|
|||
|
_MarkColor = value;
|
|||
|
this.Invalidate(this._MarkRectangle);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private Color _ForeColor = Color.White;
|
|||
|
/// <summary>
|
|||
|
/// Get or set the current Node foreground color
|
|||
|
/// </summary>
|
|||
|
public Color ForeColor {
|
|||
|
get { return _ForeColor; }
|
|||
|
protected set {
|
|||
|
_ForeColor = value;
|
|||
|
this.Invalidate();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private Color _BackColor;
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets the current Node background color
|
|||
|
/// </summary>
|
|||
|
public Color BackColor {
|
|||
|
get { return _BackColor; }
|
|||
|
protected set {
|
|||
|
_BackColor = value;
|
|||
|
this.Invalidate();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private string _Title;
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets the Node title
|
|||
|
/// </summary>
|
|||
|
public string Title {
|
|||
|
get { return _Title; }
|
|||
|
protected set {
|
|||
|
_Title = value;
|
|||
|
if (this._AutoSize) this.BuildSize(true, true, true);
|
|||
|
//this.Invalidate(this.TitleRectangle);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private string _Mark;
|
|||
|
/// <summary>
|
|||
|
/// Get or set Node tag information
|
|||
|
/// </summary>
|
|||
|
public string Mark {
|
|||
|
get { return _Mark; }
|
|||
|
set {
|
|||
|
_Mark = value;
|
|||
|
if (value == null)
|
|||
|
_MarkLines = null;
|
|||
|
else
|
|||
|
_MarkLines = (from s in value.Split('\n') select s.Trim()).ToArray();
|
|||
|
this.Invalidate(new Rectangle(-5, -5, this._MarkRectangle.Width + 10, this._MarkRectangle.Height + 10));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private string[] _MarkLines;//Store line data separately without splitting it every time you draw
|
|||
|
/// <summary>
|
|||
|
/// Get Node tag information row data
|
|||
|
/// </summary>
|
|||
|
public string[] MarkLines {
|
|||
|
get { return _MarkLines; }
|
|||
|
}
|
|||
|
|
|||
|
private int _Left;
|
|||
|
/// <summary>
|
|||
|
/// Get or set the left coordinate of Node
|
|||
|
/// </summary>
|
|||
|
public int Left {
|
|||
|
get { return _Left; }
|
|||
|
set {
|
|||
|
if (this._LockLocation || value == _Left) return;
|
|||
|
_Left = value;
|
|||
|
this.SetOptionsLocation();
|
|||
|
this.BuildSize(false, true, false);
|
|||
|
this.OnMove(EventArgs.Empty);
|
|||
|
if (this._Owner != null) {
|
|||
|
this._Owner.BuildLinePath();
|
|||
|
this._Owner.BuildBounds();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private int _Top;
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets the coordinates on the top of the Node
|
|||
|
/// </summary>
|
|||
|
public int Top {
|
|||
|
get { return _Top; }
|
|||
|
set {
|
|||
|
if (this._LockLocation || value == _Top) return;
|
|||
|
_Top = value;
|
|||
|
this.SetOptionsLocation();
|
|||
|
this.BuildSize(false, true, false);
|
|||
|
this.OnMove(EventArgs.Empty);
|
|||
|
if (this._Owner != null) {
|
|||
|
this._Owner.BuildLinePath();
|
|||
|
this._Owner.BuildBounds();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private int _Width = 100;
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets the Node width. This value cannot be set when AutoSize is set
|
|||
|
/// </summary>
|
|||
|
public int Width {
|
|||
|
get { return _Width; }
|
|||
|
protected set {
|
|||
|
if (value < 50) return;
|
|||
|
if (this._AutoSize || value == _Width) return;
|
|||
|
_Width = value;
|
|||
|
this.SetOptionsLocation();
|
|||
|
this.BuildSize(false, true, false);
|
|||
|
this.OnResize(EventArgs.Empty);
|
|||
|
if (this._Owner != null) {
|
|||
|
this._Owner.BuildLinePath();
|
|||
|
this._Owner.BuildBounds();
|
|||
|
}
|
|||
|
this.Invalidate();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private int _Height = 40;
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets the height of Node. This value cannot be set when AutoSize is set
|
|||
|
/// </summary>
|
|||
|
public int Height {
|
|||
|
get { return _Height; }
|
|||
|
protected set {
|
|||
|
if (value < 40) return;
|
|||
|
if (this._AutoSize || value == _Height) return;
|
|||
|
_Height = value;
|
|||
|
this.SetOptionsLocation();
|
|||
|
this.BuildSize(false, true, false);
|
|||
|
this.OnResize(EventArgs.Empty);
|
|||
|
if (this._Owner != null) {
|
|||
|
this._Owner.BuildLinePath();
|
|||
|
this._Owner.BuildBounds();
|
|||
|
}
|
|||
|
this.Invalidate();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private int _ItemHeight = 20;
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets the height of each option of Node
|
|||
|
/// </summary>
|
|||
|
public int ItemHeight {
|
|||
|
get { return _ItemHeight; }
|
|||
|
protected set {
|
|||
|
if (value < 16) value = 16;
|
|||
|
if (value > 200) value = 200;
|
|||
|
if (value == _ItemHeight) return;
|
|||
|
_ItemHeight = value;
|
|||
|
if (this._AutoSize) {
|
|||
|
this.BuildSize(true, false, true);
|
|||
|
} else {
|
|||
|
this.SetOptionsLocation();
|
|||
|
if (this._Owner != null) this._Owner.Invalidate();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private bool _AutoSize = true;
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets whether Node automatically calculates width and height
|
|||
|
/// </summary>
|
|||
|
public bool AutoSize {
|
|||
|
get { return _AutoSize; }
|
|||
|
protected set { _AutoSize = value; }
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Get the coordinates of the right edge of Node
|
|||
|
/// </summary>
|
|||
|
public int Right {
|
|||
|
get { return _Left + _Width; }
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Get the coordinates below the Node
|
|||
|
/// </summary>
|
|||
|
public int Bottom {
|
|||
|
get { return _Top + _Height; }
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Get the Node rectangle area
|
|||
|
/// </summary>
|
|||
|
public Rectangle Rectangle {
|
|||
|
get {
|
|||
|
return new Rectangle(this._Left, this._Top, this._Width, this._Height);
|
|||
|
}
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Get the Node title rectangle area
|
|||
|
/// </summary>
|
|||
|
public Rectangle TitleRectangle {
|
|||
|
get {
|
|||
|
return new Rectangle(this._Left, this._Top, this._Width, this._TitleHeight);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private Rectangle _MarkRectangle;
|
|||
|
/// <summary>
|
|||
|
/// Get the Node marked rectangular area
|
|||
|
/// </summary>
|
|||
|
public Rectangle MarkRectangle {
|
|||
|
get { return _MarkRectangle; }
|
|||
|
}
|
|||
|
|
|||
|
private int _TitleHeight = 20;
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets the height of the Node title
|
|||
|
/// </summary>
|
|||
|
public int TitleHeight {
|
|||
|
get { return _TitleHeight; }
|
|||
|
protected set { _TitleHeight = value; }
|
|||
|
}
|
|||
|
|
|||
|
private STNodeOptionCollection _InputOptions;
|
|||
|
/// <summary>
|
|||
|
/// Get the set of input options
|
|||
|
/// </summary>
|
|||
|
protected internal STNodeOptionCollection InputOptions {
|
|||
|
get { return _InputOptions; }
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Get the number of input options set
|
|||
|
/// </summary>
|
|||
|
public int InputOptionsCount { get { return _InputOptions.Count; } }
|
|||
|
|
|||
|
private STNodeOptionCollection _OutputOptions;
|
|||
|
/// <summary>
|
|||
|
/// Get output options
|
|||
|
/// </summary>
|
|||
|
protected internal STNodeOptionCollection OutputOptions {
|
|||
|
get { return _OutputOptions; }
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Get the number of output options
|
|||
|
/// </summary>
|
|||
|
public int OutputOptionsCount { get { return _OutputOptions.Count; } }
|
|||
|
|
|||
|
private STNodeControlCollection _Controls;
|
|||
|
/// <summary>
|
|||
|
/// Get the set of controls contained in Node
|
|||
|
/// </summary>
|
|||
|
protected STNodeControlCollection Controls {
|
|||
|
get { return _Controls; }
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Get the number of control sets contained in Node
|
|||
|
/// </summary>
|
|||
|
public int ControlsCount { get { return _Controls.Count; } }
|
|||
|
/// <summary>
|
|||
|
/// Get the Node coordinate position
|
|||
|
/// </summary>
|
|||
|
public Point Location {
|
|||
|
get { return new Point(this._Left, this._Top); }
|
|||
|
set {
|
|||
|
this.Left = value.X;
|
|||
|
this.Top = value.Y;
|
|||
|
}
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Get the Node size
|
|||
|
/// </summary>
|
|||
|
public Size Size {
|
|||
|
get { return new Size(this._Width, this._Height); }
|
|||
|
set {
|
|||
|
this.Width = value.Width;
|
|||
|
this.Height = value.Height;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private Font _Font;
|
|||
|
/// <summary>
|
|||
|
/// Get or set the Node font
|
|||
|
/// </summary>
|
|||
|
protected Font Font {
|
|||
|
get { return _Font; }
|
|||
|
set {
|
|||
|
if (value == _Font) return;
|
|||
|
this._Font.Dispose();
|
|||
|
_Font = value;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private bool _LockOption;
|
|||
|
/// <summary>
|
|||
|
/// Get or set whether to lock the Option option and not accept connections after locking
|
|||
|
/// </summary>
|
|||
|
public bool LockOption {
|
|||
|
get { return _LockOption; }
|
|||
|
set {
|
|||
|
_LockOption = value;
|
|||
|
this.Invalidate(new Rectangle(0, 0, this._Width, this._TitleHeight));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private bool _LockLocation;
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets whether to lock the Node position and cannot move after it is locked
|
|||
|
/// </summary>
|
|||
|
public bool LockLocation {
|
|||
|
get { return _LockLocation; }
|
|||
|
set {
|
|||
|
_LockLocation = value;
|
|||
|
this.Invalidate(new Rectangle(0, 0, this._Width, this._TitleHeight));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private ContextMenuStrip _ContextMenuStrip;
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets the current Node context menu
|
|||
|
/// </summary>
|
|||
|
public ContextMenuStrip ContextMenuStrip {
|
|||
|
get { return _ContextMenuStrip; }
|
|||
|
set { _ContextMenuStrip = value; }
|
|||
|
}
|
|||
|
|
|||
|
private object _Tag;
|
|||
|
/// <summary>
|
|||
|
/// Get or set user-defined saved data
|
|||
|
/// </summary>
|
|||
|
public object Tag {
|
|||
|
get { return _Tag; }
|
|||
|
set { _Tag = value; }
|
|||
|
}
|
|||
|
|
|||
|
private Guid _Guid;
|
|||
|
/// <summary>
|
|||
|
/// Get the global unique ID
|
|||
|
/// </summary>
|
|||
|
public Guid Guid {
|
|||
|
get { return _Guid; }
|
|||
|
}
|
|||
|
|
|||
|
private bool _LetGetOptions = false;
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets whether to allow external access to STNodeOption
|
|||
|
/// </summary>
|
|||
|
public bool LetGetOptions {
|
|||
|
get { return _LetGetOptions; }
|
|||
|
protected set { _LetGetOptions = value; }
|
|||
|
}
|
|||
|
|
|||
|
private static Point m_static_pt_init = new Point(10, 10);
|
|||
|
|
|||
|
public STNode() {
|
|||
|
this._Title = "Untitled";
|
|||
|
this._MarkRectangle.Height = this._Height;
|
|||
|
this._Left = this._MarkRectangle.X = m_static_pt_init.X;
|
|||
|
this._Top = m_static_pt_init.Y;
|
|||
|
this._MarkRectangle.Y = this._Top - 30;
|
|||
|
this._InputOptions = new STNodeOptionCollection(this, true);
|
|||
|
this._OutputOptions = new STNodeOptionCollection(this, false);
|
|||
|
this._Controls = new STNodeControlCollection(this);
|
|||
|
this._BackColor = Color.FromArgb(200, 64, 64, 64);
|
|||
|
this._TitleColor = Color.FromArgb(200, Color.DodgerBlue);
|
|||
|
this._MarkColor = Color.FromArgb(200, Color.Brown);
|
|||
|
this._Font = new Font("courier new", 8.25f);
|
|||
|
|
|||
|
m_sf = new StringFormat();
|
|||
|
m_sf.Alignment = StringAlignment.Near;
|
|||
|
m_sf.LineAlignment = StringAlignment.Center;
|
|||
|
m_sf.FormatFlags = StringFormatFlags.NoWrap;
|
|||
|
m_sf.SetTabStops(0, new float[] { 40 });
|
|||
|
m_static_pt_init.X += 10;
|
|||
|
m_static_pt_init.Y += 10;
|
|||
|
this._Guid = Guid.NewGuid();
|
|||
|
this.OnCreate();
|
|||
|
}
|
|||
|
|
|||
|
//private int m_nItemHeight = 30;
|
|||
|
protected StringFormat m_sf;
|
|||
|
/// <summary>
|
|||
|
/// Active controls in the current Node
|
|||
|
/// </summary>
|
|||
|
protected STNodeControl m_ctrl_active;
|
|||
|
/// <summary>
|
|||
|
/// The hover control in the current Node
|
|||
|
/// </summary>
|
|||
|
protected STNodeControl m_ctrl_hover;
|
|||
|
/// <summary>
|
|||
|
/// The control under the mouse click in the current Node
|
|||
|
/// </summary>
|
|||
|
protected STNodeControl m_ctrl_down;
|
|||
|
|
|||
|
protected internal void BuildSize(bool bBuildNode, bool bBuildMark, bool bRedraw) {
|
|||
|
if (this._Owner == null) return;
|
|||
|
using (Graphics g = this._Owner.CreateGraphics()) {
|
|||
|
if (this._AutoSize && bBuildNode) {
|
|||
|
Size sz = this.GetDefaultNodeSize(g);
|
|||
|
if (this._Width != sz.Width || this._Height != sz.Height) {
|
|||
|
this._Width = sz.Width;
|
|||
|
this._Height = sz.Height;
|
|||
|
this.SetOptionsLocation();
|
|||
|
this.OnResize(EventArgs.Empty);
|
|||
|
}
|
|||
|
}
|
|||
|
if (bBuildMark && !string.IsNullOrEmpty(this._Mark)) {
|
|||
|
this._MarkRectangle = this.OnBuildMarkRectangle(g);
|
|||
|
}
|
|||
|
}
|
|||
|
if (bRedraw) this._Owner.Invalidate();
|
|||
|
}
|
|||
|
|
|||
|
internal Dictionary<string, byte[]> OnSaveNode() {
|
|||
|
Dictionary<string, byte[]> dic = new Dictionary<string, byte[]>();
|
|||
|
dic.Add("Guid", this._Guid.ToByteArray());
|
|||
|
dic.Add("Left", BitConverter.GetBytes(this._Left));
|
|||
|
dic.Add("Top", BitConverter.GetBytes(this._Top));
|
|||
|
dic.Add("Width", BitConverter.GetBytes(this._Width));
|
|||
|
dic.Add("Height", BitConverter.GetBytes(this._Height));
|
|||
|
dic.Add("AutoSize", new byte[] { (byte)(this._AutoSize ? 1 : 0) });
|
|||
|
if (this._Mark != null) dic.Add("Mark", Encoding.UTF8.GetBytes(this._Mark));
|
|||
|
dic.Add("LockOption", new byte[] { (byte)(this._LockLocation ? 1 : 0) });
|
|||
|
dic.Add("LockLocation", new byte[] { (byte)(this._LockLocation ? 1 : 0) });
|
|||
|
Type t = this.GetType();
|
|||
|
foreach (var p in t.GetProperties()) {
|
|||
|
var attrs = p.GetCustomAttributes(true);
|
|||
|
foreach (var a in attrs) {
|
|||
|
if (!(a is STNodePropertyAttribute)) continue;
|
|||
|
var attr = a as STNodePropertyAttribute;
|
|||
|
object obj = Activator.CreateInstance(attr.DescriptorType);
|
|||
|
if (!(obj is STNodePropertyDescriptor))
|
|||
|
throw new InvalidOperationException("[STNodePropertyAttribute.Type] parameter value must be the type of [STNodePropertyDescriptor] or its subclass");
|
|||
|
var desc = (STNodePropertyDescriptor)Activator.CreateInstance(attr.DescriptorType);
|
|||
|
desc.Node = this;
|
|||
|
desc.PropertyInfo = p;
|
|||
|
byte[] byData = desc.GetBytesFromValue();
|
|||
|
if (byData == null) continue;
|
|||
|
dic.Add(p.Name, byData);
|
|||
|
}
|
|||
|
}
|
|||
|
this.OnSaveNode(dic);
|
|||
|
return dic;
|
|||
|
}
|
|||
|
|
|||
|
internal byte[] GetSaveData() {
|
|||
|
List<byte> lst = new List<byte>();
|
|||
|
Type t = this.GetType();
|
|||
|
byte[] byData = Encoding.UTF8.GetBytes(t.Module.Name + "|" + t.FullName);
|
|||
|
lst.Add((byte)byData.Length);
|
|||
|
lst.AddRange(byData);
|
|||
|
byData = Encoding.UTF8.GetBytes(t.GUID.ToString());
|
|||
|
lst.Add((byte)byData.Length);
|
|||
|
lst.AddRange(byData);
|
|||
|
|
|||
|
var dic = this.OnSaveNode();
|
|||
|
if (dic != null) {
|
|||
|
foreach (var v in dic) {
|
|||
|
byData = Encoding.UTF8.GetBytes(v.Key);
|
|||
|
lst.AddRange(BitConverter.GetBytes(byData.Length));
|
|||
|
lst.AddRange(byData);
|
|||
|
lst.AddRange(BitConverter.GetBytes(v.Value.Length));
|
|||
|
lst.AddRange(v.Value);
|
|||
|
}
|
|||
|
}
|
|||
|
return lst.ToArray();
|
|||
|
}
|
|||
|
|
|||
|
#region protected
|
|||
|
/// <summary>
|
|||
|
/// Occurs when Node is constructed
|
|||
|
/// </summary>
|
|||
|
protected virtual void OnCreate() { }
|
|||
|
/// <summary>
|
|||
|
/// Draw the entire Node
|
|||
|
/// </summary>
|
|||
|
/// <param name="dt">Drawing tool</param>
|
|||
|
protected internal virtual void OnDrawNode(DrawingTools dt) {
|
|||
|
dt.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
|
|||
|
//Fill background
|
|||
|
if (this._BackColor.A != 0) {
|
|||
|
dt.SolidBrush.Color = this._BackColor;
|
|||
|
dt.Graphics.FillRectangle(dt.SolidBrush, this._Left, this._Top + this._TitleHeight, this._Width, this.Height - this._TitleHeight);
|
|||
|
}
|
|||
|
this.OnDrawTitle(dt);
|
|||
|
this.OnDrawBody(dt);
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Draw the Node header section
|
|||
|
/// </summary>
|
|||
|
/// <param name="dt">Drawing tool</param>
|
|||
|
protected virtual void OnDrawTitle(DrawingTools dt) {
|
|||
|
m_sf.Alignment = StringAlignment.Center;
|
|||
|
m_sf.LineAlignment = StringAlignment.Center;
|
|||
|
Graphics g = dt.Graphics;
|
|||
|
SolidBrush brush = dt.SolidBrush;
|
|||
|
if (this._TitleColor.A != 0) {
|
|||
|
brush.Color = this._TitleColor;
|
|||
|
g.FillRectangle(brush, this.TitleRectangle);
|
|||
|
}
|
|||
|
if (this._LockOption) {
|
|||
|
//dt.Pen.Color = this.ForeColor;
|
|||
|
brush.Color = this._ForeColor;
|
|||
|
int n = this._Top + this._TitleHeight / 2 - 5;
|
|||
|
g.FillRectangle(dt.SolidBrush, this._Left + 4, n + 0, 2, 4);
|
|||
|
g.FillRectangle(dt.SolidBrush, this._Left + 6, n + 0, 2, 2);
|
|||
|
g.FillRectangle(dt.SolidBrush, this._Left + 8, n + 0, 2, 4);
|
|||
|
g.FillRectangle(dt.SolidBrush, this._Left + 3, n + 4, 8, 6);
|
|||
|
//g.DrawLine(dt.Pen, this._Left + 6, n + 5, this._Left + 6, n + 7);
|
|||
|
//g.DrawRectangle(dt.Pen, this._Left + 3, n + 0, 6, 3);
|
|||
|
//g.DrawRectangle(dt.Pen, this._Left + 2, n + 3, 8, 6);
|
|||
|
//g.DrawLine(dt.Pen, this._Left + 6, n + 5, this._Left + 6, n + 7);
|
|||
|
|
|||
|
}
|
|||
|
if (this._LockLocation) {
|
|||
|
//dt.Pen.Color = this.ForeColor;
|
|||
|
brush.Color = this._ForeColor;
|
|||
|
int n = this._Top + this._TitleHeight / 2 - 5;
|
|||
|
g.FillRectangle(brush, this.Right - 9, n, 4, 4);
|
|||
|
g.FillRectangle(brush, this.Right - 11, n + 4, 8, 2);
|
|||
|
g.FillRectangle(brush, this.Right - 8, n + 6, 2, 4);
|
|||
|
//g.DrawLine(dt.Pen, this.Right - 10, n + 6, this.Right - 4, n + 6);
|
|||
|
//g.DrawLine(dt.Pen, this.Right - 10, n, this.Right - 4, n);
|
|||
|
//g.DrawLine(dt.Pen, this.Right - 11, n + 6, this.Right - 3, n + 6);
|
|||
|
//g.DrawLine(dt.Pen, this.Right - 7, n + 7, this.Right - 7, n + 9);
|
|||
|
}
|
|||
|
if (!string.IsNullOrEmpty(this._Title) && this._ForeColor.A != 0) {
|
|||
|
brush.Color = this._ForeColor;
|
|||
|
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
|
|||
|
g.DrawString(this._Title, this._Font, brush, this.TitleRectangle, m_sf);
|
|||
|
}
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Draw the Node body part to remove the title part
|
|||
|
/// </summary>
|
|||
|
/// <param name="dt">Drawing tool</param>
|
|||
|
protected virtual void OnDrawBody(DrawingTools dt) {
|
|||
|
SolidBrush brush = dt.SolidBrush;
|
|||
|
foreach (STNodeOption op in this._InputOptions) {
|
|||
|
if (op == STNodeOption.Empty) continue;
|
|||
|
this.OnDrawOptionDot(dt, op);
|
|||
|
this.OnDrawOptionText(dt, op);
|
|||
|
}
|
|||
|
foreach (STNodeOption op in this._OutputOptions) {
|
|||
|
if (op == STNodeOption.Empty) continue;
|
|||
|
this.OnDrawOptionDot(dt, op);
|
|||
|
this.OnDrawOptionText(dt, op);
|
|||
|
}
|
|||
|
if (this._Controls.Count != 0) { //Draw child controls
|
|||
|
// Align the origin of the coordinates with the node
|
|||
|
//dt.Graphics.ResetTransform();
|
|||
|
dt.Graphics.TranslateTransform(this._Left, this._Top + this._TitleHeight);
|
|||
|
Point pt = Point.Empty; //The current amount of offset needed
|
|||
|
Point pt_last = Point.Empty; //The coordinates of the last control relative to the node
|
|||
|
foreach (STNodeControl v in this._Controls) {
|
|||
|
if (!v.Visable) continue;
|
|||
|
pt.X = v.Left - pt_last.X;
|
|||
|
pt.Y = v.Top - pt_last.Y;
|
|||
|
pt_last = v.Location;
|
|||
|
dt.Graphics.TranslateTransform(pt.X, pt.Y); //Move the origin coordinates to the control position
|
|||
|
dt.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
|
|||
|
v.OnPaint(dt);
|
|||
|
}
|
|||
|
//dt.Graphics.TranslateTransform(-pt_last.X, -pt_last.Y); restore coordinates
|
|||
|
dt.Graphics.TranslateTransform(-this._Left - pt_last.X, -this._Top - this._TitleHeight - pt_last.Y);
|
|||
|
//dt.Graphics.
|
|||
|
}
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Draw marker information
|
|||
|
/// </summary>
|
|||
|
/// <param name="dt">Drawing tool</param>
|
|||
|
protected internal virtual void OnDrawMark(DrawingTools dt) {
|
|||
|
if (string.IsNullOrEmpty(this._Mark)) return;
|
|||
|
Graphics g = dt.Graphics;
|
|||
|
SolidBrush brush = dt.SolidBrush;
|
|||
|
m_sf.LineAlignment = StringAlignment.Center;
|
|||
|
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
|
|||
|
brush.Color = this._MarkColor;
|
|||
|
g.FillRectangle(brush, this._MarkRectangle); //fill background color
|
|||
|
|
|||
|
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; //Determine the size required for text drawing
|
|||
|
var sz = g.MeasureString(this.Mark, this.Font, this._MarkRectangle.Width);
|
|||
|
brush.Color = this._ForeColor;
|
|||
|
if (sz.Height > this._ItemHeight || sz.Width > this._MarkRectangle.Width) { //If it exceeds the drawing area, draw the part
|
|||
|
Rectangle rect = new Rectangle(this._MarkRectangle.Left + 2, this._MarkRectangle.Top + 2, this._MarkRectangle.Width - 20, 16);
|
|||
|
m_sf.Alignment = StringAlignment.Near;
|
|||
|
g.DrawString(this._MarkLines[0], this._Font, brush, rect, m_sf);
|
|||
|
m_sf.Alignment = StringAlignment.Far;
|
|||
|
rect.Width = this._MarkRectangle.Width - 5;
|
|||
|
g.DrawString("+", this._Font, brush, rect, m_sf); // + means beyond the drawing area
|
|||
|
} else {
|
|||
|
m_sf.Alignment = StringAlignment.Near;
|
|||
|
g.DrawString(this._MarkLines[0].Trim(), this._Font, brush, this._MarkRectangle, m_sf);
|
|||
|
}
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// The point where the option line is drawn
|
|||
|
/// </summary>
|
|||
|
/// <param name="dt">Drawing tool</param>
|
|||
|
/// <param name="op">Specified options</param>
|
|||
|
protected virtual void OnDrawOptionDot(DrawingTools dt, STNodeOption op) {
|
|||
|
Graphics g = dt.Graphics;
|
|||
|
Pen pen = dt.Pen;
|
|||
|
SolidBrush brush = dt.SolidBrush;
|
|||
|
var t = typeof(object);
|
|||
|
if (op.DotColor != Color.Transparent) //Set the color
|
|||
|
brush.Color = op.DotColor;
|
|||
|
else {
|
|||
|
if (op.DataType == t)
|
|||
|
pen.Color = this.Owner.UnknownTypeColor;
|
|||
|
else
|
|||
|
brush.Color = this.Owner.TypeColor.ContainsKey(op.DataType) ? this.Owner.TypeColor[op.DataType] : this.Owner.UnknownTypeColor;
|
|||
|
}
|
|||
|
if (op.IsSingle) { //Single connection circle
|
|||
|
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
|
|||
|
if (op.DataType == t) { //unknown type to draw otherwise fill
|
|||
|
g.DrawEllipse(pen, op.DotRectangle.X, op.DotRectangle.Y, op.DotRectangle.Width - 1, op.DotRectangle.Height - 1);
|
|||
|
} else
|
|||
|
g.FillEllipse(brush, op.DotRectangle);
|
|||
|
} else { //Multiple connection rectangles
|
|||
|
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
|
|||
|
if (op.DataType == t) {
|
|||
|
g.DrawRectangle(pen, op.DotRectangle.X, op.DotRectangle.Y, op.DotRectangle.Width - 1, op.DotRectangle.Height - 1);
|
|||
|
} else
|
|||
|
g.FillRectangle(brush, op.DotRectangle);
|
|||
|
}
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Text for drawing options
|
|||
|
/// </summary>
|
|||
|
/// <param name="dt">Drawing tool</param>
|
|||
|
/// <param name="op">Specified options</param>
|
|||
|
protected virtual void OnDrawOptionText(DrawingTools dt, STNodeOption op) {
|
|||
|
Graphics g = dt.Graphics;
|
|||
|
SolidBrush brush = dt.SolidBrush;
|
|||
|
if (op.IsInput) {
|
|||
|
m_sf.Alignment = StringAlignment.Near;
|
|||
|
} else {
|
|||
|
m_sf.Alignment = StringAlignment.Far;
|
|||
|
}
|
|||
|
brush.Color = op.TextColor;
|
|||
|
g.DrawString(op.Text, this.Font, brush, op.TextRectangle, m_sf);
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Occurs when calculating the position of the Option connection point
|
|||
|
/// </summary>
|
|||
|
/// <param name="op">Option to be calculated</param>
|
|||
|
/// <param name="pt">Automatically calculated position</param>
|
|||
|
/// <param name="nIndex">Index of the current Option</param>
|
|||
|
/// <returns>New location</returns>
|
|||
|
protected virtual Point OnSetOptionDotLocation(STNodeOption op, Point pt, int nIndex) {
|
|||
|
return pt;
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Occurs when evaluating the Option text area
|
|||
|
/// </summary>
|
|||
|
/// <param name="op">Option to be calculated</param>
|
|||
|
/// <param name="rect">Automatically calculated area</param>
|
|||
|
/// <param name="nIndex">Index of the current Option</param>
|
|||
|
/// <returns>New area</returns>
|
|||
|
protected virtual Rectangle OnSetOptionTextRectangle(STNodeOption op, Rectangle rect, int nIndex) {
|
|||
|
return rect;
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Get the default size required by the current STNode
|
|||
|
/// The returned size does not limit the drawing area and can still be drawn outside of this area
|
|||
|
/// But it will not be accepted by STNodeEditor and trigger the corresponding event
|
|||
|
/// </summary>
|
|||
|
/// <param name="g">Drawing panel</param>
|
|||
|
/// <returns>Calculated size</returns>
|
|||
|
protected virtual Size GetDefaultNodeSize(Graphics g) {
|
|||
|
int nInputHeight = 0, nOutputHeight = 0;
|
|||
|
foreach (STNodeOption op in this._InputOptions) nInputHeight += this._ItemHeight;
|
|||
|
foreach (STNodeOption op in this._OutputOptions) nOutputHeight += this._ItemHeight;
|
|||
|
int nHeight = this._TitleHeight + (nInputHeight > nOutputHeight ? nInputHeight : nOutputHeight);
|
|||
|
|
|||
|
SizeF szf_input = SizeF.Empty, szf_output = SizeF.Empty;
|
|||
|
foreach (STNodeOption v in this._InputOptions) {
|
|||
|
if (string.IsNullOrEmpty(v.Text)) continue;
|
|||
|
SizeF szf = g.MeasureString(v.Text, this._Font);
|
|||
|
if (szf.Width > szf_input.Width) szf_input = szf;
|
|||
|
}
|
|||
|
foreach (STNodeOption v in this._OutputOptions) {
|
|||
|
if (string.IsNullOrEmpty(v.Text)) continue;
|
|||
|
SizeF szf = g.MeasureString(v.Text, this._Font);
|
|||
|
if (szf.Width > szf_output.Width) szf_output = szf;
|
|||
|
}
|
|||
|
int nWidth = (int)(szf_input.Width + szf_output.Width + 25);
|
|||
|
if (!string.IsNullOrEmpty(this.Title)) szf_input = g.MeasureString(this.Title, this.Font);
|
|||
|
if (szf_input.Width + 30 > nWidth) nWidth = (int)szf_input.Width + 30;
|
|||
|
return new Size(nWidth, nHeight);
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Calculate the rectangular area required by the current Mark
|
|||
|
/// The returned size does not limit the drawing area and can still be drawn outside of this area
|
|||
|
/// But it will not be accepted by STNodeEditor and trigger the corresponding event
|
|||
|
/// </summary>
|
|||
|
/// <param name="g">Drawing panel</param>
|
|||
|
/// <returns>Calculated area</returns>
|
|||
|
protected virtual Rectangle OnBuildMarkRectangle(Graphics g) {
|
|||
|
//if (string.IsNullOrEmpty(this._Mark)) return Rectangle.Empty;
|
|||
|
return new Rectangle(this._Left, this._Top - 30, this._Width, 20);
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// What data does this Node need to save additionally when it needs to be saved?
|
|||
|
/// Note: When saving, it will not be serialized and restored. When only re-creating this Node through the empty parameter constructor
|
|||
|
/// Then call OnLoadNode() to restore the saved data
|
|||
|
/// </summary>
|
|||
|
/// <param name="dic">Data to be saved</param>
|
|||
|
protected virtual void OnSaveNode(Dictionary<string, byte[]> dic) { }
|
|||
|
/// <summary>
|
|||
|
/// When restoring the node, the data returned by OnSaveNode() will be re-introduced to this function
|
|||
|
/// </summary>
|
|||
|
/// <param name="dic">Save data</param>
|
|||
|
protected internal virtual void OnLoadNode(Dictionary<string, byte[]> dic) {
|
|||
|
if (dic.ContainsKey("AutoSize")) this._AutoSize = dic["AutoSize"][0] == 1;
|
|||
|
if (dic.ContainsKey("LockOption")) this._LockOption = dic["LockOption"][0] == 1;
|
|||
|
if (dic.ContainsKey("LockLocation")) this._LockLocation = dic["LockLocation"][0] == 1;
|
|||
|
if (dic.ContainsKey("Guid")) this._Guid = new Guid(dic["Guid"]);
|
|||
|
if (dic.ContainsKey("Left")) this._Left = BitConverter.ToInt32(dic["Left"], 0);
|
|||
|
if (dic.ContainsKey("Top")) this._Top = BitConverter.ToInt32(dic["Top"], 0);
|
|||
|
if (dic.ContainsKey("Width") && !this._AutoSize) this._Width = BitConverter.ToInt32(dic["Width"], 0);
|
|||
|
if (dic.ContainsKey("Height") && !this._AutoSize) this._Height = BitConverter.ToInt32(dic["Height"], 0);
|
|||
|
if (dic.ContainsKey("Mark")) this.Mark = Encoding.UTF8.GetString(dic["Mark"]);
|
|||
|
Type t = this.GetType();
|
|||
|
foreach (var p in t.GetProperties()) {
|
|||
|
var attrs = p.GetCustomAttributes(true);
|
|||
|
foreach (var a in attrs) {
|
|||
|
if (!(a is STNodePropertyAttribute)) continue;
|
|||
|
var attr = a as STNodePropertyAttribute;
|
|||
|
object obj = Activator.CreateInstance(attr.DescriptorType);
|
|||
|
if (!(obj is STNodePropertyDescriptor))
|
|||
|
throw new InvalidOperationException("[STNodePropertyAttribute.Type] parameter value must be the type of [STNodePropertyDescriptor] or its subclass");
|
|||
|
var desc = (STNodePropertyDescriptor)Activator.CreateInstance(attr.DescriptorType);
|
|||
|
desc.Node = this;
|
|||
|
desc.PropertyInfo = p;
|
|||
|
try {
|
|||
|
if (dic.ContainsKey(p.Name)) desc.SetValue(dic[p.Name]);
|
|||
|
} catch (Exception ex) {
|
|||
|
string strErr = "The value of attribute [" + this.Title + "." + p.Name + "] cannot be restored by overriding [STNodePropertyAttribute.GetBytesFromValue(), STNodePropertyAttribute.GetValueFromBytes(byte[])] to ensure preservation and The binary data is correct when loading";
|
|||
|
Exception e = ex;
|
|||
|
while (e != null) {
|
|||
|
strErr += "\r\n----\r\n[" + e.GetType().Name + "] -> " + e.Message;
|
|||
|
e = e.InnerException;
|
|||
|
}
|
|||
|
throw new InvalidOperationException(strErr, ex);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Occurs when the editor has loaded all nodes
|
|||
|
/// </summary>
|
|||
|
protected internal virtual void OnEditorLoadCompleted() { }
|
|||
|
/// <summary>
|
|||
|
/// Set the text information of Option
|
|||
|
/// </summary>
|
|||
|
/// <param name="op">Target Option</param>
|
|||
|
/// <param name="strText">text</param>
|
|||
|
/// <returns>Successful</returns>
|
|||
|
protected bool SetOptionText(STNodeOption op, string strText) {
|
|||
|
if (op.Owner != this) return false;
|
|||
|
op.Text = strText;
|
|||
|
return true;
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Set the color of the Option text information
|
|||
|
/// </summary>
|
|||
|
/// <param name="op">Target Option</param>
|
|||
|
/// <param name="clr">color</param>
|
|||
|
/// <returns>Successful</returns>
|
|||
|
protected bool SetOptionTextColor(STNodeOption op, Color clr) {
|
|||
|
if (op.Owner != this) return false;
|
|||
|
op.TextColor = clr;
|
|||
|
return true;
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Set the color of the Option connection point
|
|||
|
/// </summary>
|
|||
|
/// <param name="op">Target Option</param>
|
|||
|
/// <param name="clr">color</param>
|
|||
|
/// <returns>Successful</returns>
|
|||
|
protected bool SetOptionDotColor(STNodeOption op, Color clr) {
|
|||
|
if (op.Owner != this) return false;
|
|||
|
op.DotColor = clr;
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
//[event]===========================[event]==============================[event]============================[event]
|
|||
|
|
|||
|
protected internal virtual void OnGotFocus(EventArgs e) { }
|
|||
|
|
|||
|
protected internal virtual void OnLostFocus(EventArgs e) { }
|
|||
|
|
|||
|
protected internal virtual void OnMouseEnter(EventArgs e) { }
|
|||
|
|
|||
|
protected internal virtual void OnMouseDown(MouseEventArgs e) {
|
|||
|
Point pt = e.Location;
|
|||
|
pt.Y -= this._TitleHeight;
|
|||
|
for (int i = this._Controls.Count - 1; i >= 0; i--) {
|
|||
|
var c = this._Controls[i];
|
|||
|
if (c.DisplayRectangle.Contains(pt)) {
|
|||
|
if (!c.Enabled) return;
|
|||
|
if (!c.Visable) continue;
|
|||
|
c.OnMouseDown(new MouseEventArgs(e.Button, e.Clicks, e.X - c.Left, pt.Y - c.Top, e.Delta));
|
|||
|
m_ctrl_down = c;
|
|||
|
if (m_ctrl_active != c) {
|
|||
|
c.OnGotFocus(EventArgs.Empty);
|
|||
|
if (m_ctrl_active != null) m_ctrl_active.OnLostFocus(EventArgs.Empty);
|
|||
|
m_ctrl_active = c;
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
if (m_ctrl_active != null) m_ctrl_active.OnLostFocus(EventArgs.Empty);
|
|||
|
m_ctrl_active = null;
|
|||
|
}
|
|||
|
|
|||
|
protected internal virtual void OnMouseMove(MouseEventArgs e) {
|
|||
|
Point pt = e.Location;
|
|||
|
pt.Y -= this._TitleHeight;
|
|||
|
if (m_ctrl_down != null) {
|
|||
|
if (m_ctrl_down.Enabled && m_ctrl_down.Visable)
|
|||
|
m_ctrl_down.OnMouseMove(new MouseEventArgs(e.Button, e.Clicks, e.X - m_ctrl_down.Left, pt.Y - m_ctrl_down.Top, e.Delta));
|
|||
|
return;
|
|||
|
}
|
|||
|
for (int i = this._Controls.Count - 1; i >= 0; i--) {
|
|||
|
var c = this._Controls[i];
|
|||
|
if (c.DisplayRectangle.Contains(pt)) {
|
|||
|
if (m_ctrl_hover != this._Controls[i]) {
|
|||
|
c.OnMouseEnter(EventArgs.Empty);
|
|||
|
if (m_ctrl_hover != null) m_ctrl_hover.OnMouseLeave(EventArgs.Empty);
|
|||
|
m_ctrl_hover = c;
|
|||
|
}
|
|||
|
m_ctrl_hover.OnMouseMove(new MouseEventArgs(e.Button, e.Clicks, e.X - c.Left, pt.Y - c.Top, e.Delta));
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
if (m_ctrl_hover != null) m_ctrl_hover.OnMouseLeave(EventArgs.Empty);
|
|||
|
m_ctrl_hover = null;
|
|||
|
}
|
|||
|
|
|||
|
protected internal virtual void OnMouseUp(MouseEventArgs e) {
|
|||
|
Point pt = e.Location;
|
|||
|
pt.Y -= this._TitleHeight;
|
|||
|
if (m_ctrl_down != null && m_ctrl_down.Enabled && m_ctrl_down.Visable) {
|
|||
|
m_ctrl_down.OnMouseUp(new MouseEventArgs(e.Button, e.Clicks, e.X - m_ctrl_down.Left, pt.Y - m_ctrl_down.Top, e.Delta));
|
|||
|
}
|
|||
|
//if (m_ctrl_active != null) {
|
|||
|
// m_ctrl_active.OnMouseUp(new MouseEventArgs(e.Button, e.Clicks,
|
|||
|
// e.X - m_ctrl_active.Left, pt.Y - m_ctrl_active.Top, e.Delta));
|
|||
|
//}
|
|||
|
m_ctrl_down = null;
|
|||
|
}
|
|||
|
|
|||
|
protected internal virtual void OnMouseLeave(EventArgs e) {
|
|||
|
if (m_ctrl_hover != null && m_ctrl_hover.Enabled && m_ctrl_hover.Visable) m_ctrl_hover.OnMouseLeave(e);
|
|||
|
m_ctrl_hover = null;
|
|||
|
}
|
|||
|
|
|||
|
protected internal virtual void OnMouseClick(MouseEventArgs e) {
|
|||
|
Point pt = e.Location;
|
|||
|
pt.Y -= this._TitleHeight;
|
|||
|
if (m_ctrl_active != null && m_ctrl_active.Enabled && m_ctrl_active.Visable)
|
|||
|
m_ctrl_active.OnMouseClick(new MouseEventArgs(e.Button, e.Clicks, e.X - m_ctrl_active.Left, pt.Y - m_ctrl_active.Top, e.Delta));
|
|||
|
}
|
|||
|
|
|||
|
protected internal virtual void OnMouseWheel(MouseEventArgs e) {
|
|||
|
Point pt = e.Location;
|
|||
|
pt.Y -= this._TitleHeight;
|
|||
|
if (m_ctrl_hover != null && m_ctrl_active.Enabled && m_ctrl_hover.Visable) {
|
|||
|
m_ctrl_hover.OnMouseWheel(new MouseEventArgs(e.Button, e.Clicks, e.X - m_ctrl_hover.Left, pt.Y - m_ctrl_hover.Top, e.Delta));
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
protected internal virtual void OnMouseHWheel(MouseEventArgs e) {
|
|||
|
if (m_ctrl_hover != null && m_ctrl_active.Enabled && m_ctrl_hover.Visable) {
|
|||
|
m_ctrl_hover.OnMouseHWheel(e);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
protected internal virtual void OnKeyDown(KeyEventArgs e) {
|
|||
|
if (m_ctrl_active != null && m_ctrl_active.Enabled && m_ctrl_active.Visable) m_ctrl_active.OnKeyDown(e);
|
|||
|
}
|
|||
|
protected internal virtual void OnKeyUp(KeyEventArgs e) {
|
|||
|
if (m_ctrl_active != null && m_ctrl_active.Enabled && m_ctrl_active.Visable) m_ctrl_active.OnKeyUp(e);
|
|||
|
}
|
|||
|
protected internal virtual void OnKeyPress(KeyPressEventArgs e) {
|
|||
|
if (m_ctrl_active != null && m_ctrl_active.Enabled && m_ctrl_active.Visable) m_ctrl_active.OnKeyPress(e);
|
|||
|
}
|
|||
|
|
|||
|
protected virtual void OnMove(EventArgs e) { /*this.SetOptionLocation();*/ }
|
|||
|
protected virtual void OnResize(EventArgs e) { /*this.SetOptionLocation();*/ }
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Occurs when the owner changes
|
|||
|
/// </summary>
|
|||
|
protected virtual void OnOwnerChanged() { }
|
|||
|
/// <summary>
|
|||
|
/// Occurs when the selected state changes
|
|||
|
/// </summary>
|
|||
|
protected virtual void OnSelectedChanged() { }
|
|||
|
/// <summary>
|
|||
|
/// Occurs when the activity state changes
|
|||
|
/// </summary>
|
|||
|
protected virtual void OnActiveChanged() { }
|
|||
|
|
|||
|
#endregion protected
|
|||
|
/// <summary>
|
|||
|
/// Calculate the position of each Option
|
|||
|
/// </summary>
|
|||
|
protected virtual void SetOptionsLocation() {
|
|||
|
int nIndex = 0;
|
|||
|
Rectangle rect = new Rectangle(this.Left + 10, this._Top + this._TitleHeight, this._Width - 20, this._ItemHeight);
|
|||
|
foreach (STNodeOption op in this._InputOptions) {
|
|||
|
if (op != STNodeOption.Empty) {
|
|||
|
Point pt = this.OnSetOptionDotLocation(op, new Point(this.Left - op.DotSize / 2, rect.Y + (rect.Height - op.DotSize) / 2), nIndex);
|
|||
|
op.TextRectangle = this.OnSetOptionTextRectangle(op, rect, nIndex);
|
|||
|
op.DotLeft = pt.X;
|
|||
|
op.DotTop = pt.Y;
|
|||
|
}
|
|||
|
rect.Y += this._ItemHeight;
|
|||
|
nIndex++;
|
|||
|
}
|
|||
|
rect.Y = this._Top + this._TitleHeight;
|
|||
|
m_sf.Alignment = StringAlignment.Far;
|
|||
|
foreach (STNodeOption op in this._OutputOptions) {
|
|||
|
if (op != STNodeOption.Empty) {
|
|||
|
Point pt = this.OnSetOptionDotLocation(op, new Point(this._Left + this._Width - op.DotSize / 2, rect.Y + (rect.Height - op.DotSize) / 2), nIndex);
|
|||
|
op.TextRectangle = this.OnSetOptionTextRectangle(op, rect, nIndex);
|
|||
|
op.DotLeft = pt.X;
|
|||
|
op.DotTop = pt.Y;
|
|||
|
}
|
|||
|
rect.Y += this._ItemHeight;
|
|||
|
nIndex++;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Redraw Node
|
|||
|
/// </summary>
|
|||
|
public void Invalidate() {
|
|||
|
if (this._Owner != null) {
|
|||
|
this._Owner.Invalidate(this._Owner.CanvasToControl(new Rectangle(this._Left - 5, this._Top - 5, this._Width + 10, this._Height + 10)));
|
|||
|
}
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Redraw the specified area of Node
|
|||
|
/// </summary>
|
|||
|
/// <param name="rect">Node specified area</param>
|
|||
|
public void Invalidate(Rectangle rect) {
|
|||
|
rect.X += this._Left;
|
|||
|
rect.Y += this._Top;
|
|||
|
if (this._Owner != null) {
|
|||
|
rect = this._Owner.CanvasToControl(rect);
|
|||
|
rect.Width += 1; rect.Height += 1;//Coordinate system conversion may cause progress loss plus one more pixel
|
|||
|
this._Owner.Invalidate(rect);
|
|||
|
}
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Get the input Option collection contained in this Node
|
|||
|
/// </summary>
|
|||
|
/// <returns>Option array</returns>
|
|||
|
public STNodeOption[] GetInputOptions() {
|
|||
|
if (!this._LetGetOptions) return null;
|
|||
|
STNodeOption[] ops = new STNodeOption[this._InputOptions.Count];
|
|||
|
for (int i = 0; i < this._InputOptions.Count; i++) ops[i] = this._InputOptions[i];
|
|||
|
return ops;
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Get the output Option set contained in this Node
|
|||
|
/// </summary>
|
|||
|
/// <returns>Option array</returns>
|
|||
|
public STNodeOption[] GetOutputOptions() {
|
|||
|
if (!this._LetGetOptions) return null;
|
|||
|
STNodeOption[] ops = new STNodeOption[this._OutputOptions.Count];
|
|||
|
for (int i = 0; i < this._OutputOptions.Count; i++) ops[i] = this._OutputOptions[i];
|
|||
|
return ops;
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Set the selected state of Node
|
|||
|
/// </summary>
|
|||
|
/// <param name="bSelected">Selected</param>
|
|||
|
/// <param name="bRedraw">Whether to redraw</param>
|
|||
|
public void SetSelected(bool bSelected, bool bRedraw) {
|
|||
|
if (this._IsSelected == bSelected) return;
|
|||
|
this._IsSelected = bSelected;
|
|||
|
if (this._Owner != null) {
|
|||
|
if (bSelected)
|
|||
|
this._Owner.AddSelectedNode(this);
|
|||
|
else
|
|||
|
this._Owner.RemoveSelectedNode(this);
|
|||
|
}
|
|||
|
if (bRedraw) this.Invalidate();
|
|||
|
this.OnSelectedChanged();
|
|||
|
if (this._Owner != null) this._Owner.OnSelectedChanged(EventArgs.Empty);
|
|||
|
}
|
|||
|
public IAsyncResult BeginInvoke(Delegate method) { return this.BeginInvoke(method, null); }
|
|||
|
public IAsyncResult BeginInvoke(Delegate method, params object[] args) {
|
|||
|
if (this._Owner == null) return null;
|
|||
|
return this._Owner.BeginInvoke(method, args);
|
|||
|
}
|
|||
|
public object Invoke(Delegate method) { return this.Invoke(method, null); }
|
|||
|
public object Invoke(Delegate method, params object[] args) {
|
|||
|
if (this._Owner == null) return null;
|
|||
|
return this._Owner.Invoke(method, args);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|