2019-03-30 15:18:15 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
using System.Data;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Windows.Forms;
|
|
|
|
|
|
2019-07-16 21:35:21 +00:00
|
|
|
|
namespace Toolbox.Library.Forms
|
2019-03-30 15:18:15 +00:00
|
|
|
|
{
|
|
|
|
|
public partial class TimeLine : UserControl
|
|
|
|
|
{
|
|
|
|
|
public TimeLine()
|
|
|
|
|
{
|
|
|
|
|
InitializeComponent();
|
2019-03-31 18:28:00 +00:00
|
|
|
|
timer.Interval = 10;
|
|
|
|
|
timer.Tick += Timer_Tick;
|
2019-03-30 15:18:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 22:23:41 +00:00
|
|
|
|
public void Play() { }
|
|
|
|
|
public void Stop() { }
|
|
|
|
|
|
|
|
|
|
protected int margin = 0;
|
|
|
|
|
|
2019-12-20 00:51:57 +00:00
|
|
|
|
private int startTime = 0;
|
|
|
|
|
public int StartTime
|
|
|
|
|
{
|
2020-03-10 23:46:25 +00:00
|
|
|
|
set {
|
|
|
|
|
if (value == FrameCount)
|
|
|
|
|
startTime = value - 1;
|
|
|
|
|
else
|
|
|
|
|
startTime = value;
|
|
|
|
|
}
|
2019-12-20 00:51:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-30 15:18:15 +00:00
|
|
|
|
public event EventHandler FrameChanged;
|
|
|
|
|
|
2019-03-31 18:28:00 +00:00
|
|
|
|
private Timer timer = new Timer();
|
|
|
|
|
|
|
|
|
|
public bool Locked { get; private set; } = false;
|
|
|
|
|
|
|
|
|
|
private void ResolveCollision()
|
|
|
|
|
{
|
2019-12-20 00:51:57 +00:00
|
|
|
|
if (frameLeft < startTime)
|
2019-03-31 18:28:00 +00:00
|
|
|
|
{
|
2019-12-20 00:51:57 +00:00
|
|
|
|
frameRight += startTime - frameLeft;
|
|
|
|
|
frameLeft = startTime;
|
2019-03-31 18:28:00 +00:00
|
|
|
|
}
|
|
|
|
|
else if (frameRight > lastFrame)
|
|
|
|
|
{
|
|
|
|
|
frameLeft += lastFrame - frameRight;
|
|
|
|
|
frameRight = lastFrame;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ResolveFitting()
|
|
|
|
|
{
|
2019-12-20 00:51:57 +00:00
|
|
|
|
if (frameLeft < startTime)
|
2019-03-31 18:28:00 +00:00
|
|
|
|
{
|
2019-12-20 00:51:57 +00:00
|
|
|
|
frameRight += startTime - frameLeft;
|
2019-03-31 18:28:00 +00:00
|
|
|
|
if (frameRight > lastFrame)
|
|
|
|
|
frameRight = lastFrame;
|
2019-12-20 00:51:57 +00:00
|
|
|
|
frameLeft = startTime;
|
2019-03-31 18:28:00 +00:00
|
|
|
|
}
|
|
|
|
|
else if (frameRight > lastFrame)
|
|
|
|
|
{
|
|
|
|
|
frameLeft += lastFrame - frameRight;
|
2019-12-20 00:51:57 +00:00
|
|
|
|
if (frameLeft < startTime)
|
|
|
|
|
frameLeft = startTime;
|
2019-03-31 18:28:00 +00:00
|
|
|
|
frameRight = lastFrame;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-28 21:27:48 +00:00
|
|
|
|
public float CurrentFrame
|
2019-03-30 15:18:15 +00:00
|
|
|
|
{
|
2019-03-31 22:27:12 +00:00
|
|
|
|
get { return currentFrame; }
|
2019-03-30 15:18:15 +00:00
|
|
|
|
set
|
|
|
|
|
{
|
2019-03-31 18:28:00 +00:00
|
|
|
|
if (FollowCurrentFrame && !(Focused && MouseButtons == MouseButtons.Right))
|
2019-03-30 15:18:15 +00:00
|
|
|
|
{
|
|
|
|
|
double delta = value - (frameRight + frameLeft) * 0.5;
|
|
|
|
|
frameLeft += delta;
|
|
|
|
|
frameRight += delta;
|
|
|
|
|
|
2019-03-31 18:28:00 +00:00
|
|
|
|
ResolveCollision();
|
2019-03-30 15:18:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentFrame = value;
|
|
|
|
|
Refresh();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-28 21:27:48 +00:00
|
|
|
|
public float FrameCount
|
2019-03-30 15:18:15 +00:00
|
|
|
|
{
|
2019-03-31 22:27:12 +00:00
|
|
|
|
get { return lastFrame + 1; }
|
2019-03-30 15:18:15 +00:00
|
|
|
|
set
|
|
|
|
|
{
|
2020-03-10 23:46:25 +00:00
|
|
|
|
if (value == startTime)
|
|
|
|
|
lastFrame = value + 1;
|
|
|
|
|
else
|
|
|
|
|
lastFrame = value;
|
2019-03-30 15:18:15 +00:00
|
|
|
|
|
|
|
|
|
if (value == 1)
|
|
|
|
|
{
|
2019-12-20 00:51:57 +00:00
|
|
|
|
frameLeft = startTime;
|
2019-03-30 15:18:15 +00:00
|
|
|
|
frameRight = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-03-31 18:28:00 +00:00
|
|
|
|
ResolveFitting();
|
2019-03-30 15:18:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 18:28:00 +00:00
|
|
|
|
if (currentFrame > lastFrame)
|
|
|
|
|
currentFrame = lastFrame;
|
|
|
|
|
|
2019-03-30 15:18:15 +00:00
|
|
|
|
Refresh();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool FollowCurrentFrame = true;
|
|
|
|
|
|
2019-09-28 21:27:48 +00:00
|
|
|
|
protected float currentFrame = 0;
|
|
|
|
|
protected float lastFrame = 1000;
|
2019-03-30 15:18:15 +00:00
|
|
|
|
|
2019-03-31 22:23:41 +00:00
|
|
|
|
protected double frameLeft = 0;
|
|
|
|
|
protected double frameRight = 200;
|
2019-03-30 15:18:15 +00:00
|
|
|
|
|
2019-03-31 22:23:41 +00:00
|
|
|
|
protected Point lastMousePos;
|
2019-03-30 15:18:15 +00:00
|
|
|
|
|
|
|
|
|
|
2019-10-20 00:47:25 +00:00
|
|
|
|
protected static Brush brush1 = new SolidBrush(FormThemes.BaseTheme.TimelineNumberColor);
|
|
|
|
|
protected static Brush brush2 = new SolidBrush(FormThemes.BaseTheme.TimelineOverlayColor);
|
|
|
|
|
protected static Brush brush3 = new SolidBrush(FormThemes.BaseTheme.TimelineBackColor);
|
2019-03-31 22:23:41 +00:00
|
|
|
|
protected static Brush brush4 = new SolidBrush(Color.FromArgb(90, 90, 90));
|
|
|
|
|
protected static Brush brush5 = new SolidBrush(Color.FromArgb(150, 150, 150));
|
2019-03-30 15:18:15 +00:00
|
|
|
|
|
2019-10-20 00:47:25 +00:00
|
|
|
|
protected static Pen pen1 = new Pen(new SolidBrush(FormThemes.BaseTheme.TimelineLineColor), 2);
|
|
|
|
|
protected static Pen pen2 = new Pen(new SolidBrush(FormThemes.BaseTheme.TimelineLine2Color), 2);
|
2019-03-31 22:23:41 +00:00
|
|
|
|
protected static Pen pen3 = new Pen(new SolidBrush(Color.FromArgb(150, 150, 150)), 2);
|
2019-03-31 18:28:00 +00:00
|
|
|
|
|
2019-03-31 22:23:41 +00:00
|
|
|
|
protected static Font font = new Font(new FontFamily("arial"), 10);
|
|
|
|
|
|
|
|
|
|
protected static int barHeight = TextRenderer.MeasureText("0", font).Height;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected override void OnPaintBackground(PaintEventArgs e)
|
2019-03-30 15:18:15 +00:00
|
|
|
|
{
|
|
|
|
|
base.OnPaint(e);
|
2019-03-31 22:23:41 +00:00
|
|
|
|
|
|
|
|
|
e.Graphics.SetClip(new Rectangle(margin, 0, Width - margin, Height));
|
|
|
|
|
|
|
|
|
|
double currentFrameX = 20 + margin + (currentFrame - frameLeft) * (Width - 40 - margin) / (frameRight - frameLeft);
|
2019-03-30 15:18:15 +00:00
|
|
|
|
|
|
|
|
|
e.Graphics.FillRectangle(brush2, new Rectangle(0, 0, Width, Height));
|
2019-03-31 22:23:41 +00:00
|
|
|
|
e.Graphics.FillRectangle(brush3, new Rectangle(0, 0, Width, barHeight));
|
2019-03-30 15:18:15 +00:00
|
|
|
|
|
|
|
|
|
double step = 50 * (frameRight - frameLeft) / Width;
|
|
|
|
|
if (step > 10)
|
|
|
|
|
step = Math.Round(step / 10.0) * 10;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
step = Math.Round(Math.Max(1, step));
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-10 23:46:25 +00:00
|
|
|
|
if (frameRight != frameLeft)
|
2019-03-30 15:18:15 +00:00
|
|
|
|
{
|
2019-03-31 18:28:00 +00:00
|
|
|
|
double max;
|
|
|
|
|
if (frameRight < lastFrame)
|
|
|
|
|
max = Math.Min(frameRight + step, lastFrame);
|
|
|
|
|
else
|
|
|
|
|
max = frameRight - step;
|
|
|
|
|
|
|
|
|
|
for (double frame = Math.Floor(frameLeft / step) * step; frame <= max; frame += step)
|
|
|
|
|
{
|
2019-03-31 22:23:41 +00:00
|
|
|
|
double frameX = 20 + margin + (frame - frameLeft) * (Width - 40 - margin) / (frameRight - frameLeft);
|
2019-03-30 15:18:15 +00:00
|
|
|
|
|
2019-03-31 22:23:41 +00:00
|
|
|
|
e.Graphics.DrawLine(pen1, new Point((int)frameX, barHeight), new Point((int)frameX, Height));
|
2019-03-30 15:18:15 +00:00
|
|
|
|
|
2019-03-31 18:28:00 +00:00
|
|
|
|
e.Graphics.DrawString("" + frame, font, brush4, new Point((int)frameX - TextRenderer.MeasureText("" + frame, font).Width / 2, 0));
|
|
|
|
|
}
|
2019-03-30 15:18:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 18:28:00 +00:00
|
|
|
|
if (frameRight == lastFrame)
|
|
|
|
|
{
|
|
|
|
|
//draw last frame regardless of the steps
|
|
|
|
|
double x = Width - 20;
|
|
|
|
|
|
2019-03-31 22:23:41 +00:00
|
|
|
|
e.Graphics.DrawLine(pen1, new Point((int)x, barHeight), new Point((int)x, Height));
|
2019-03-31 18:28:00 +00:00
|
|
|
|
|
|
|
|
|
e.Graphics.DrawString("" + lastFrame, font, brush4, new Point((int)x - TextRenderer.MeasureText("" + lastFrame, font).Width / 2, 0));
|
|
|
|
|
}
|
2019-03-31 22:23:41 +00:00
|
|
|
|
/*
|
|
|
|
|
int lastY = value((int)(Math.Round(- 20+margin * (frameRight - frameLeft) / (Width - 40-margin) + frameLeft) + lastFrame + 1) % (lastFrame + 1));
|
|
|
|
|
for (int x = 5; x < Width+10; x+=5)
|
|
|
|
|
{
|
|
|
|
|
e.Graphics.DrawLine(pen3, x - 5, lastY, x, lastY =
|
|
|
|
|
value((int)(Math.Round((x - 20+margin) * (frameRight - frameLeft) / (Width - 40-margin) + frameLeft) + lastFrame + 1) % (lastFrame + 1))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnPaint(PaintEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
e.Graphics.SetClip(new Rectangle(margin, 0, Width - margin, Height));
|
2019-03-30 15:18:15 +00:00
|
|
|
|
|
2019-03-31 22:23:41 +00:00
|
|
|
|
double currentFrameX = 20 + margin + (currentFrame - frameLeft) * (Width - 40 - margin) / (frameRight - frameLeft);
|
2019-09-28 21:27:48 +00:00
|
|
|
|
|
2019-03-31 22:23:41 +00:00
|
|
|
|
base.OnPaint(e);
|
2020-03-10 23:46:25 +00:00
|
|
|
|
if (frameLeft != frameRight)
|
|
|
|
|
e.Graphics.DrawLine(pen2, new Point((int)currentFrameX, barHeight), new Point((int)currentFrameX, Height));
|
2019-03-31 18:28:00 +00:00
|
|
|
|
|
|
|
|
|
e.Graphics.DrawString("" + currentFrame, font, brush1, new Point((int)currentFrameX - TextRenderer.MeasureText("" + currentFrame, font).Width / 2, 0));
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 22:23:41 +00:00
|
|
|
|
private int value(int frame)
|
|
|
|
|
{
|
|
|
|
|
return frame + 30;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 18:28:00 +00:00
|
|
|
|
private void Timer_Tick(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
double step;
|
2019-03-31 22:23:41 +00:00
|
|
|
|
if (lastMousePos.X < 20 + margin)
|
2019-03-31 18:28:00 +00:00
|
|
|
|
{
|
2019-03-31 22:23:41 +00:00
|
|
|
|
step = (20 + margin - lastMousePos.X) * (frameRight - frameLeft) / Width;
|
2019-03-31 18:28:00 +00:00
|
|
|
|
frameLeft -= step;
|
|
|
|
|
frameRight -= step;
|
|
|
|
|
|
|
|
|
|
#region resolve collsions
|
2019-12-20 00:51:57 +00:00
|
|
|
|
if (frameLeft < startTime)
|
2019-03-31 18:28:00 +00:00
|
|
|
|
{
|
|
|
|
|
frameRight -= frameLeft;
|
2019-12-20 00:51:57 +00:00
|
|
|
|
frameLeft = startTime;
|
2019-03-31 18:28:00 +00:00
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
step = (lastMousePos.X - Width + 20) * (frameRight - frameLeft) / Width;
|
|
|
|
|
frameLeft += step;
|
|
|
|
|
frameRight += step;
|
|
|
|
|
|
|
|
|
|
#region resolve collsions
|
|
|
|
|
if (frameRight > lastFrame)
|
|
|
|
|
{
|
|
|
|
|
frameLeft += lastFrame - frameRight;
|
|
|
|
|
frameRight = lastFrame;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-20 00:51:57 +00:00
|
|
|
|
currentFrame = Math.Min(Math.Max(startTime, (int)Math.Round(((lastMousePos.X - 20 - margin) * (frameRight - frameLeft) / (Width - 40 - margin) + frameLeft))), lastFrame);
|
2019-03-31 18:28:00 +00:00
|
|
|
|
FrameChanged?.Invoke(this, new EventArgs());
|
|
|
|
|
Refresh();
|
2019-03-30 15:18:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnMouseMove(MouseEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
base.OnMouseMove(e);
|
2019-12-20 00:51:57 +00:00
|
|
|
|
if (lastFrame == startTime)
|
2019-03-31 18:28:00 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2019-03-30 15:18:15 +00:00
|
|
|
|
if (e.Button == MouseButtons.Left)
|
|
|
|
|
{
|
2019-03-30 16:26:38 +00:00
|
|
|
|
|
2019-03-31 22:23:41 +00:00
|
|
|
|
timer.Enabled = (e.X < 20 + margin || e.X > Width - 20);
|
2019-03-31 18:28:00 +00:00
|
|
|
|
Locked = true;
|
2019-12-20 00:51:57 +00:00
|
|
|
|
currentFrame = Math.Min(Math.Max(startTime, (int)Math.Round(((e.X - 20 - margin) * (frameRight - frameLeft) / (Width - 40 - margin) + frameLeft))), lastFrame);
|
2019-03-31 18:28:00 +00:00
|
|
|
|
FrameChanged?.Invoke(this, new EventArgs());
|
2019-03-30 15:18:15 +00:00
|
|
|
|
Refresh();
|
|
|
|
|
}
|
|
|
|
|
else if (e.Button == MouseButtons.Right)
|
|
|
|
|
{
|
2019-03-31 22:23:41 +00:00
|
|
|
|
double delta = (e.X - lastMousePos.X) * (frameRight - frameLeft) / (Width - 40 - margin);
|
2019-03-30 15:18:15 +00:00
|
|
|
|
frameLeft -= delta;
|
|
|
|
|
frameRight -= delta;
|
|
|
|
|
|
2019-03-30 16:26:38 +00:00
|
|
|
|
ResolveCollision();
|
2019-03-31 18:28:00 +00:00
|
|
|
|
|
2019-03-30 15:18:15 +00:00
|
|
|
|
Refresh();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lastMousePos = e.Location;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 18:28:00 +00:00
|
|
|
|
protected override void OnMouseUp(MouseEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
timer.Stop();
|
|
|
|
|
Locked = false;
|
|
|
|
|
base.OnMouseUp(e);
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-30 15:18:15 +00:00
|
|
|
|
protected override void OnMouseWheel(MouseEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
base.OnMouseWheel(e);
|
2019-12-20 00:51:57 +00:00
|
|
|
|
if (lastFrame == startTime)
|
2019-03-31 18:28:00 +00:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (frameRight - frameLeft <= 2 && e.Delta > 0)
|
|
|
|
|
return;
|
2019-03-30 15:18:15 +00:00
|
|
|
|
|
|
|
|
|
double delta = 1 + Math.Min(Math.Max(-0.5, -e.Delta * 0.00390625), 0.5);
|
|
|
|
|
|
2019-12-20 00:51:57 +00:00
|
|
|
|
double frameOrigin = Math.Min(Math.Max(startTime, ((e.X - 20 - margin) * (frameRight - frameLeft) / (Width - 40 - margin) + frameLeft)), lastFrame);
|
2019-03-30 15:18:15 +00:00
|
|
|
|
|
|
|
|
|
frameLeft = Math.Min(-1, (frameLeft - frameOrigin)) * delta + frameOrigin;
|
|
|
|
|
frameRight = Math.Max(1, (frameRight - frameOrigin)) * delta + frameOrigin;
|
|
|
|
|
|
2019-03-31 18:28:00 +00:00
|
|
|
|
ResolveFitting();
|
2019-03-30 16:26:38 +00:00
|
|
|
|
|
|
|
|
|
Refresh();
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 18:28:00 +00:00
|
|
|
|
protected override void OnResize(EventArgs e)
|
2019-03-30 16:26:38 +00:00
|
|
|
|
{
|
2019-03-31 18:28:00 +00:00
|
|
|
|
Refresh();
|
|
|
|
|
base.OnResize(e);
|
2019-03-30 15:18:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override CreateParams CreateParams
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
CreateParams cp = base.CreateParams;
|
|
|
|
|
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
|
|
|
|
|
return cp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|