2019-10-05 17:25:28 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Windows.Forms;
|
|
|
|
|
using Toolbox.Library.Animations;
|
|
|
|
|
|
|
|
|
|
namespace Toolbox.Library.Forms
|
|
|
|
|
{
|
|
|
|
|
public class KeyedAnimTimeline : TimeLine
|
|
|
|
|
{
|
|
|
|
|
public EventHandler OnNodeSelected;
|
|
|
|
|
|
2019-10-19 21:36:16 +00:00
|
|
|
|
public string GroupTarget = "";
|
2019-10-05 17:25:28 +00:00
|
|
|
|
public bool DisplayKeys = true;
|
|
|
|
|
|
|
|
|
|
private STAnimation activeAnimation;
|
|
|
|
|
public STAnimation ActiveAnimation
|
|
|
|
|
{
|
|
|
|
|
get { return activeAnimation; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
activeAnimation = value;
|
|
|
|
|
PopulateTree();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected static int lineHeight = TextRenderer.MeasureText("§", font).Height;
|
|
|
|
|
|
|
|
|
|
protected int scrollY = 0;
|
|
|
|
|
|
|
|
|
|
private int trackCount = 0;
|
|
|
|
|
|
|
|
|
|
private TreeView NodeTree;
|
|
|
|
|
private Splitter splitter;
|
|
|
|
|
|
|
|
|
|
private ImageList imgList = new ImageList();
|
|
|
|
|
|
|
|
|
|
public KeyedAnimTimeline()
|
|
|
|
|
{
|
|
|
|
|
imgList = new ImageList()
|
|
|
|
|
{
|
|
|
|
|
ImageSize = new Size(24, 24),
|
|
|
|
|
ColorDepth = ColorDepth.Depth32Bit,
|
|
|
|
|
};
|
2019-10-19 21:36:16 +00:00
|
|
|
|
}
|
2019-10-05 17:25:28 +00:00
|
|
|
|
|
2019-10-19 21:36:16 +00:00
|
|
|
|
public void ReloadEditorView()
|
|
|
|
|
{
|
|
|
|
|
imgList.Images.Add("AnimationGroup", Properties.Resources.AnimationGroup);
|
2019-10-05 17:25:28 +00:00
|
|
|
|
imgList.Images.Add("AnimationTrack", Properties.Resources.AnimationTrack);
|
|
|
|
|
imgList.Images.Add("AnimationTrackR", Properties.Resources.AnimationTrackR);
|
|
|
|
|
imgList.Images.Add("AnimationTrackG", Properties.Resources.AnimationTrackG);
|
|
|
|
|
imgList.Images.Add("AnimationTrackB", Properties.Resources.AnimationTrackB);
|
|
|
|
|
imgList.Images.Add("AnimationTrackA", Properties.Resources.AnimationTrackA);
|
|
|
|
|
imgList.Images.Add("AnimationTrackX", Properties.Resources.AnimationTrackX);
|
|
|
|
|
imgList.Images.Add("AnimationTrackY", Properties.Resources.AnimationTrackY);
|
|
|
|
|
imgList.Images.Add("AnimationTrackZ", Properties.Resources.AnimationTrackZ);
|
|
|
|
|
imgList.Images.Add("AnimationTrackW", Properties.Resources.AnimationTrackW);
|
|
|
|
|
|
|
|
|
|
NodeTree = new TreeView();
|
|
|
|
|
NodeTree.ImageList = imgList;
|
|
|
|
|
NodeTree.Dock = DockStyle.Left;
|
2019-10-19 21:36:16 +00:00
|
|
|
|
// NodeTree.Anchor = AnchorStyles.Bottom | AnchorStyles.Top | AnchorStyles.Left;
|
2019-10-05 17:25:28 +00:00
|
|
|
|
NodeTree.Height = this.Height;
|
|
|
|
|
NodeTree.Width = 300;
|
|
|
|
|
NodeTree.BackColor = FormThemes.BaseTheme.FormBackColor;
|
|
|
|
|
NodeTree.ForeColor = FormThemes.BaseTheme.FormForeColor;
|
|
|
|
|
NodeTree.AfterSelect += NodeTree_OnAfterNodeSelect;
|
|
|
|
|
NodeTree.BeforeExpand += NodeTree_OnExpand;
|
|
|
|
|
NodeTree.AfterExpand += NodeTree_OnExpand;
|
|
|
|
|
NodeTree.BeforeCollapse += NodeTree_OnExpand;
|
|
|
|
|
NodeTree.AfterCollapse += NodeTree_OnExpand;
|
|
|
|
|
|
|
|
|
|
Controls.Add(NodeTree);
|
|
|
|
|
splitter = new Splitter();
|
|
|
|
|
splitter.Dock = DockStyle.Left;
|
|
|
|
|
splitter.LocationChanged += SpitterLocationChanged;
|
|
|
|
|
Controls.Add(splitter);
|
|
|
|
|
|
2019-10-19 21:36:16 +00:00
|
|
|
|
margin = NodeTree.Width;
|
2019-10-05 17:25:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void PopulateTree()
|
|
|
|
|
{
|
|
|
|
|
if (ActiveAnimation == null || NodeTree == null || !DisplayKeys) return;
|
|
|
|
|
|
|
|
|
|
NodeTree.Nodes.Clear();
|
|
|
|
|
foreach (var group in ActiveAnimation.AnimGroups)
|
|
|
|
|
{
|
2019-10-19 21:36:16 +00:00
|
|
|
|
if (GroupTarget != string.Empty && group.Name != GroupTarget)
|
|
|
|
|
continue;
|
|
|
|
|
|
2019-10-05 17:25:28 +00:00
|
|
|
|
if (group.Category != string.Empty)
|
|
|
|
|
{
|
|
|
|
|
var categoryNode = AddCategoryNode(NodeTree.Nodes, group.Category);
|
|
|
|
|
AddGroup(categoryNode.Nodes, group);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
AddGroup(NodeTree.Nodes, group);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private TreeNode AddCategoryNode(TreeNodeCollection nodes, string name)
|
|
|
|
|
{
|
|
|
|
|
foreach (TreeNode node in nodes)
|
|
|
|
|
if (node.Text == name) return node;
|
|
|
|
|
|
|
|
|
|
var newNode = new TreeNode(name);
|
|
|
|
|
NodeTree.Nodes.Add(newNode);
|
|
|
|
|
return newNode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void AddGroup(TreeNodeCollection nodes, STAnimGroup group)
|
|
|
|
|
{
|
|
|
|
|
TreeNode groupNode = new TreeNode(group.Name)
|
|
|
|
|
{ Tag = group, ImageKey = "AnimationGroup", };
|
|
|
|
|
nodes.Add(groupNode);
|
|
|
|
|
|
|
|
|
|
foreach (var subGroup in group.SubAnimGroups)
|
|
|
|
|
AddGroup(groupNode.Nodes, subGroup);
|
|
|
|
|
|
|
|
|
|
foreach (var track in group.GetTracks())
|
|
|
|
|
{
|
|
|
|
|
if (!track.HasKeys)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
string imageKey = "AnimationTrack";
|
|
|
|
|
if (track.Name.EndsWith("R")) imageKey = "AnimationTrackR";
|
|
|
|
|
if (track.Name.EndsWith("G")) imageKey = "AnimationTrackG";
|
|
|
|
|
if (track.Name.EndsWith("B")) imageKey = "AnimationTrackB";
|
|
|
|
|
if (track.Name.EndsWith("A")) imageKey = "AnimationTrackA";
|
|
|
|
|
if (track.Name.EndsWith("X")) imageKey = "AnimationTrackX";
|
|
|
|
|
if (track.Name.EndsWith("Y")) imageKey = "AnimationTrackY";
|
|
|
|
|
if (track.Name.EndsWith("Z")) imageKey = "AnimationTrackZ";
|
|
|
|
|
if (track.Name.EndsWith("W")) imageKey = "AnimationTrackW";
|
|
|
|
|
|
|
|
|
|
TreeNode trackNode = new TreeNode(track.Name)
|
|
|
|
|
{ Tag = track, ImageKey = imageKey, SelectedImageKey = imageKey, };
|
|
|
|
|
groupNode.Nodes.Add(trackNode);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void NodeTree_OnExpand(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void NodeTree_OnAfterNodeSelect(object sender, TreeViewEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
OnNodeSelected?.Invoke(sender, e);
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SpitterLocationChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
margin = splitter.Location.X;
|
|
|
|
|
this.Invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnPaint(PaintEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (!DisplayKeys)
|
|
|
|
|
{
|
|
|
|
|
base.OnPaint(e);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
e.Graphics.FillRectangle(brush3, new Rectangle(0, 0, margin, Height));
|
|
|
|
|
|
|
|
|
|
e.Graphics.SetClip(new Rectangle(0, barHeight, Width, Height - barHeight));
|
|
|
|
|
|
|
|
|
|
bool v = false;
|
|
|
|
|
int y = -scrollY - 10;
|
|
|
|
|
trackCount = 0;
|
|
|
|
|
|
|
|
|
|
if (ActiveAnimation != null)
|
|
|
|
|
{
|
|
|
|
|
foreach (var node in TreeViewExtensions.Collect(NodeTree.Nodes))
|
|
|
|
|
{
|
|
|
|
|
if (node.Parent != null && node.Tag is STAnimationTrack && node.Parent.IsVisible && node.Parent.IsExpanded)
|
|
|
|
|
{
|
|
|
|
|
var track = node.Tag as STAnimationTrack;
|
|
|
|
|
if (!track.HasKeys)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i < track.KeyFrames.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
int l = Math.Max(-20, (int)((
|
|
|
|
|
track.KeyFrames[i - 1].Frame
|
|
|
|
|
- frameLeft) * (Width - 40 - margin) / (frameRight - frameLeft)));
|
|
|
|
|
int r = (int)((
|
|
|
|
|
track.KeyFrames[i].Frame
|
|
|
|
|
- frameLeft) * (Width - 40 - margin) / (frameRight - frameLeft));
|
|
|
|
|
|
|
|
|
|
if (v = !v)
|
|
|
|
|
{
|
|
|
|
|
var keyColor = new SolidBrush(Color.Yellow);
|
|
|
|
|
switch (node.ImageKey)
|
|
|
|
|
{
|
|
|
|
|
case "AnimationTrackR":
|
|
|
|
|
case "AnimationTrackX":
|
|
|
|
|
keyColor = new SolidBrush(Color.Red);
|
|
|
|
|
break;
|
|
|
|
|
case "AnimationTrackG":
|
|
|
|
|
case "AnimationTrackY":
|
|
|
|
|
keyColor = new SolidBrush(Color.Green);
|
|
|
|
|
break;
|
|
|
|
|
case "AnimationTrackB":
|
|
|
|
|
case "AnimationTrackZ":
|
|
|
|
|
keyColor = new SolidBrush(Color.Blue);
|
|
|
|
|
break;
|
|
|
|
|
case "AnimationTrackA":
|
|
|
|
|
case "AnimationTrackW":
|
|
|
|
|
keyColor = new SolidBrush(Color.Gray);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (node.IsSelected)
|
|
|
|
|
keyColor = new SolidBrush(Color.White);
|
|
|
|
|
|
|
|
|
|
e.Graphics.FillEllipse(keyColor, new Rectangle(l + margin + 20, barHeight + y, 5, lineHeight));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (node.IsVisible)
|
|
|
|
|
y += NodeTree.ItemHeight;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
base.OnPaint(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnMouseWheel(MouseEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (e.X < margin)
|
|
|
|
|
{
|
|
|
|
|
scrollY = Math.Max(Math.Min(trackCount * lineHeight + barHeight - Height, scrollY - e.Delta / 2), 0);
|
|
|
|
|
Refresh();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
base.OnMouseWheel(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnResize(EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
scrollY = Math.Max(Math.Min(trackCount * lineHeight + barHeight - Height, scrollY), 0);
|
|
|
|
|
base.OnResize(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void InitializeComponent()
|
|
|
|
|
{
|
|
|
|
|
this.SuspendLayout();
|
|
|
|
|
//
|
|
|
|
|
// KeyedAnimTimeline
|
|
|
|
|
//
|
|
|
|
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
|
|
|
|
this.Name = "KeyedAnimTimeline";
|
|
|
|
|
this.ResumeLayout(false);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|