JD2022-TU1/main/tools/framework/JD.Sequencers/Times/TimeCursor.cs

265 lines
7.4 KiB
C#

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using JD.Kernel.Structs;
using JD.Sequencers.Controls;
namespace JD.Sequencers.Times
{
/// <summary>
/// The cursor that represents the "current time".
/// </summary>
public class TimeCursor : Guide
{
#region Inner Types
private class TimeScrollButton : Controls.Button
{
#region Variables
private readonly ScrollButton m_type;
#endregion
#region Constructor
public TimeScrollButton(ScrollButton type)
{
m_type = type;
}
#endregion
#region Properties
public ScrollButton Type
{
get { return m_type; }
}
#endregion
#region Methods
protected override void OnPaintSelf(PaintEventArgs e)
{
base.OnPaintSelf(e);
if (Bounds.Width > 0 || Bounds.Height > 0)
{
ControlPaint.DrawScrollButton(e.Graphics, Bounds, Type, ButtonState.Flat);
if (IsPressed)
{
using (Brush b = new SolidBrush(System.Drawing.Color.FromArgb(128, System.Drawing.Color.Lavender)))
{
e.Graphics.FillRectangle(b, Bounds);
}
}
}
}
#endregion
}
#endregion
#region Constants
internal const int TotalWidth = 60;
private const int ButtonWidth = 12;
private const int DraggingPartWidth = TotalWidth - 2 * ButtonWidth;
#endregion
#region Variables
private readonly TimeScrollButton m_leftButton = new TimeScrollButton(ScrollButton.Left);
private readonly TimeScrollButton m_rightButton = new TimeScrollButton(ScrollButton.Right);
private bool m_isDragging;
private bool m_showIntervalSubdivisionInText;
private int m_adjustTextPositionY;
#endregion
#region Constructor
public TimeCursor(Timeline timeline)
: base(timeline)
{
Width = DraggingPartWidth;
Selectable = true;
Controls.Add(m_leftButton);
Controls.Add(m_rightButton);
m_leftButton.Click += button_Click;
m_rightButton.Click += button_Click;
}
#endregion
#region Properties
/// <summary>
/// The amount of time units to add/substract when clicking on the arrows.
/// </summary>
private float Step
{
get { return Timeline.MinorInterval; }
}
public Rectangle BoundsWithButtons
{
get { return new Rectangle(m_leftButton.Bounds.X, m_leftButton.Bounds.Y, TotalWidth, Height); }
}
public int AdjustTextPositionY
{
get { return m_adjustTextPositionY; }
set
{
m_adjustTextPositionY = value;
Invalidate();
}
}
public bool ShowIntervalSubdivisionInText
{
get { return m_showIntervalSubdivisionInText; }
set { m_showIntervalSubdivisionInText = value; }
}
#endregion
#region Methods
#region Painting
protected override void OnPaintSelf(PaintEventArgs e)
{
UpdateBounds();
base.OnPaintSelf(e);
System.Drawing.Color baseColor = m_isDragging ? System.Drawing.Color.FromArgb(160, 230, 230, 230) : System.Drawing.Color.FromArgb(160, 50, 50, 50);
using (Brush b = new LinearGradientBrush(Bounds, Modify(baseColor, 30), System.Drawing.Color.FromArgb(baseColor.A - 80, Modify(baseColor, -30)), LinearGradientMode.Vertical))
{
e.Graphics.FillRectangle(b, Bounds);
}
Text = GetText();
DrawText(e.Graphics, Text, (int)System.Math.Floor(Location.X + (Width / 2.0)));
e.Graphics.DrawRectangle(Pens.Gray, Bounds);
}
public static System.Drawing.Color Modify(System.Drawing.Color source, int delta)
{
var colorRange = new Range<int>(0, 255);
Func<int, int> modifyChannel = x => colorRange.Clamp(x + delta);
return System.Drawing.Color.FromArgb(source.A, modifyChannel(source.R), modifyChannel(source.G), modifyChannel(source.B));
}
private string GetText()
{
return Timeline.TimeToText(Time, Timeline.MinorInterval, m_showIntervalSubdivisionInText);
}
private void DrawText(Graphics g, string text, int xPos)
{
SizeF size = g.MeasureString(text, Timeline.Font);
int x = (int)System.Math.Floor(xPos - (size.Width / 2.0));
int y = (int)System.Math.Floor((Location.Y + Height / 2.0) - (size.Height / 2.0));
g.DrawString(text, Timeline.Font, m_isDragging ? Brushes.Black : Brushes.DeepSkyBlue, new Point(x, y + m_adjustTextPositionY));
}
#endregion
#region Bounds
protected override void OnTimeChanged(EventArgs e)
{
base.OnTimeChanged(e);
Invalidate();
}
protected override void OnBoundsChanged(EventArgs e)
{
base.OnBoundsChanged(e);
UpdateBounds();
}
private void UpdateBounds()
{
m_leftButton.Bounds = new Rectangle(Bounds.X - ButtonWidth, Bounds.Y, ButtonWidth, Height);
m_rightButton.Bounds = new Rectangle(Bounds.Right, Bounds.Y, ButtonWidth, Height);
}
#endregion
#region Interaction
public virtual void GotoPreviousFrame()
{
int mul = (int)System.Math.Round(Time / Step) - 1;
Time = mul * Step;
}
public virtual void GotoNextFrame()
{
int mul = (int)System.Math.Round(Time / Step) + 1;
Time = mul * Step;
}
protected internal override void OnMouseDown(MouseEventArgs e)
{
if (IsMoveAllowed)
{
m_isDragging = true;
Invalidate();
}
}
protected internal override void OnMouseUp(MouseEventArgs e)
{
if (m_isDragging)
{
m_isDragging = false;
Invalidate();
}
}
void button_Click(object sender, EventArgs e)
{
TimeScrollButton button = (TimeScrollButton)sender;
switch (button.Type)
{
case ScrollButton.Left:
GotoPreviousFrame();
break;
case ScrollButton.Right:
GotoNextFrame();
break;
default:
throw new NotImplementedException();
}
// Recalculate the guide's position now
Location = new Point(Timeline.TimeToPixel(Time) - Width / 2, Timeline.Location.Y + Timeline.BorderWidth);
Cursor.Position = RootControl.PointToScreen(new Point(button.Bounds.X + button.Bounds.Width / 2,
button.Bounds.Y + button.Bounds.Height / 2));
}
#endregion
#endregion
}
}