a8e6d104f2
Panes can now be selected and moved around. Panes can be resized from corners or edges. Improved hit detection for panes. Mouse left click now selects and moves panes. Use middle mouse or hold shift + left mouse to pan/move camera. More progress on timeline, but currently not functional so currently disabled atm. Multiple layout animations can be selected and played at once. Goes to the highest amount of frames. Start to impliment a parts manager. Will allow editing external layout and animation data, and saving back properly.
260 lines
9.6 KiB
C#
260 lines
9.6 KiB
C#
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;
|
|
|
|
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,
|
|
};
|
|
|
|
/* imgList.Images.Add("AnimationGroup", Properties.Resources.AnimationGroup);
|
|
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;
|
|
// NodeTree.Anchor = AnchorStyles.Bottom | AnchorStyles.Top | AnchorStyles.Left;
|
|
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);
|
|
|
|
margin = NodeTree.Width;*/
|
|
}
|
|
|
|
private void PopulateTree()
|
|
{
|
|
if (ActiveAnimation == null || NodeTree == null || !DisplayKeys) return;
|
|
|
|
NodeTree.Nodes.Clear();
|
|
foreach (var group in ActiveAnimation.AnimGroups)
|
|
{
|
|
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);
|
|
|
|
}
|
|
}
|
|
}
|