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

478 lines
15 KiB
C#

using System;
using System.Drawing;
using System.Windows.Forms;
using JD.Kernel.Structs;
using JD.Maths.Utils;
using JD.ScreenWrappers;
using JD.Sequencers.Controls;
using JD.Sequencers.Windowlesses;
namespace JD.Sequencers.Times
{
using RangeF = Range<float>;
/// <summary>
/// Provides base behaviors for any sequencer control (TrackView, Timeline, Zoom, Optional scroll bars, etc)
/// </summary>
public abstract class SequencerControlBase : WindowlessRootControl
{
#region Variables
private Timeline m_timeline;
private HScrollBar m_horizontalScrollbar;
private VScrollBar m_verticalScrollbar;
private TrackView m_trackView;
private Cursor m_previousCursor;
private Point m_lastMouseMoveLocation;
private bool m_suspendScrollbarEvents;
private bool m_isTrackViewVisible = true;
#endregion
#region Constructor
protected SequencerControlBase()
{
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
m_previousCursor = DefaultCursor;
}
protected void Init()
{
RootContainer.BoundsChanged += RootContainer_BoundsChanged;
}
#endregion
#region Properties
public bool IsTrackViewVisible
{
get { return m_isTrackViewVisible; }
set { m_isTrackViewVisible = value; }
}
public TrackView TrackView
{
get
{
if (m_trackView == null)
{
m_trackView = new TrackView(this, Timeline);
m_trackView.ScrollMaximumChanged += m_trackView_ScrollMaximumChanged;
m_trackView.ScrollValueChanged += m_trackView_ScrollValueChanged;
RootContainer.Controls.Add(m_trackView);
}
m_trackView.IsVisible = m_isTrackViewVisible;
return m_trackView;
}
}
private Timeline Timeline
{
get
{
if (m_timeline == null)
{
m_timeline = CreateTimeline();
m_timeline.CurrentTimeChanged += m_timeline_CurrentTimeChanged;
m_timeline.TimeRangeChanged += m_timeline_TimeRangeChanged;
m_timeline.PixelsPerTimeChanged += m_timeline_PixelsPerTimeChanged;
m_timeline.TimeOffsetChanged += m_timeline_TimeOffsetChanged;
m_timeline.MouseWheel += m_timeline_MouseWheel;
}
return m_timeline;
}
}
private HScrollBar HorizontalScrollbar
{
get
{
if (m_horizontalScrollbar == null)
{
m_horizontalScrollbar = CreateHorizontalScrollbar();
if (m_horizontalScrollbar != null)
{
m_horizontalScrollbar.ValueChanged += m_horizontalScrollbar_ValueChanged;
}
}
return m_horizontalScrollbar;
}
}
private VScrollBar VerticalScrollbar
{
get
{
if (m_verticalScrollbar == null)
{
m_verticalScrollbar = CreateVerticalScrollbar();
if (m_verticalScrollbar != null)
{
m_verticalScrollbar.ValueChanged += m_verticalScrollbar_ValueChanged;
}
}
return m_verticalScrollbar;
}
}
#endregion
#region Methods
protected abstract Timeline CreateTimeline();
protected abstract HScrollBar CreateHorizontalScrollbar();
protected abstract VScrollBar CreateVerticalScrollbar();
public void Zoom(float factor)
{
float zoomFactor = factor > 0.0f ? -0.1f : 0.1f;
RangeF currentRange = Timeline.VisibleTimeRange;
currentRange = currentRange.Inflate(currentRange.Width() * zoomFactor);
if (currentRange.Width() > 0.1f)
{
Timeline.VisibleTimeRange = currentRange;
}
}
public void SelectTrack(Track track)
{
int index = TrackView.GetTrackIndex(track);
if (index >= 0)
{
TrackView.ScrollValue = index <= TrackView.ScrollMaximum ? index : TrackView.ScrollMaximum;
TrackView.ActiveTrack = track;
UpdateScrollbars();
}
}
protected virtual void UpdateBoundsRootContainer()
{
Rectangle bounds = RootContainer.Bounds;
Timeline.Bounds = new Rectangle(bounds.Left, bounds.Top, bounds.Width, Timeline.Height);
if (TrackView.IsVisible)
{
TrackView.Bounds = new Rectangle(bounds.Left, Timeline.Bounds.Bottom, bounds.Width, bounds.Height - Timeline.Height);
}
UpdateScrollbars();
}
protected virtual void UpdateHorizontalScrollbar()
{
double rangeWidth = Timeline.TimeRange.Width() * Timeline.PixelsPerTime;
double displayWidth = Timeline.Width - 2 * Timeline.Margin;
if (System.Math.Abs(displayWidth - rangeWidth) > 1e-3)
{
m_suspendScrollbarEvents = true;
HorizontalScrollbar.Minimum = (int)System.Math.Round(Timeline.TimeRange.Min * Timeline.PixelsPerTime) - Timeline.Margin;
HorizontalScrollbar.Maximum = (int)System.Math.Round((Timeline.TimeRange.Max + Timeline.MajorInterval) * Timeline.PixelsPerTime - Timeline.Width + Timeline.Margin);
m_suspendScrollbarEvents = false;
HorizontalScrollbar.LargeChange = (int)System.Math.Round(Timeline.MajorInterval * Timeline.PixelsPerTime);
HorizontalScrollbar.SmallChange = (int)System.Math.Round(Timeline.MinorInterval * Timeline.PixelsPerTime);
if (Timeline.TimeOffset > HorizontalScrollbar.Maximum / Timeline.PixelsPerTime)
{
HorizontalScrollbar.Value = HorizontalScrollbar.Maximum;
}
else if (Timeline.TimeOffset < HorizontalScrollbar.Minimum / Timeline.PixelsPerTime)
{
HorizontalScrollbar.Value = HorizontalScrollbar.Minimum;
}
else
{
HorizontalScrollbar.Value = (int)System.Math.Round(Timeline.TimeOffset * Timeline.PixelsPerTime);
}
}
else
{
m_suspendScrollbarEvents = true;
HorizontalScrollbar.Maximum = HorizontalScrollbar.Minimum = 0;
m_suspendScrollbarEvents = false;
}
}
protected virtual void UpdateVerticalScrollbar()
{
if (TrackView.ScrollMaximum <= 0)
{
VerticalScrollbar.Value = 0;
VerticalScrollbar.Visible = false;
}
else
{
VerticalScrollbar.Visible = true;
m_suspendScrollbarEvents = true;
VerticalScrollbar.Minimum = 0;
VerticalScrollbar.Maximum = System.Math.Max(TrackView.ScrollMaximum + VerticalScrollbar.LargeChange - 1, TrackView.ScrollValue);
m_suspendScrollbarEvents = false;
VerticalScrollbar.Value = TrackView.ScrollValue;
}
}
private void UpdateScrollbars()
{
if (m_suspendScrollbarEvents)
{
return;
}
if (HorizontalScrollbar != null)
{
int valueBefore = HorizontalScrollbar.Value;
UpdateHorizontalScrollbar();
HorizontalScrollbar.Invalidate();
if (HorizontalScrollbar.Value != valueBefore)
{
Timeline.Invalidate();
TrackView.Invalidate();
}
}
if (VerticalScrollbar != null)
{
int valueBefore = VerticalScrollbar.Value;
UpdateVerticalScrollbar();
VerticalScrollbar.Invalidate();
if (VerticalScrollbar.Value != valueBefore)
{
Timeline.Invalidate();
TrackView.Invalidate();
}
}
}
#endregion
#region Event Handlers
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
Focus();
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button == MouseButtons.Middle)
{
m_previousCursor = Cursor;
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
if (e.Button == MouseButtons.Middle)
{
Cursor = m_previousCursor;
}
}
protected override void OnMouseWheel(MouseEventArgs e)
{
base.OnMouseWheel(e);
if (Bounds.Contains(e.Location))
HandleZoomOnMouseWheel(e);
}
private void m_timeline_MouseWheel(object sender, MouseEventArgs e)
{
if (Bounds.Contains(e.Location))
HandleZoomOnMouseWheel(e);
}
protected virtual void HandleZoomOnMouseWheel(MouseEventArgs e)
{
if ((ModifierKeys & Keys.Control) == Keys.Control)
{
Zoom(e.Delta);
}
else
{
if (m_suspendScrollbarEvents || e.Delta == 0)
{
return;
}
if (e.Delta > 0 && TrackView.ScrollValue > 0)
{
TrackView.ScrollValue--;
}
if (e.Delta < 1 && TrackView.ScrollValue < TrackView.ScrollMaximum)
{
TrackView.ScrollValue++;
}
UpdateScrollbars();
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
Point delta = new Point(
e.Location.X - m_lastMouseMoveLocation.X,
e.Location.Y - m_lastMouseMoveLocation.Y
);
m_lastMouseMoveLocation = e.Location;
if (e.Button == MouseButtons.Middle)
{
Cursor = PanCursor(true);
PanHorizontal(delta);
PanVertical(delta);
int lastLocationX = m_lastMouseMoveLocation.X;
int lastLocationY = m_lastMouseMoveLocation.Y;
GuiUtil.WrapAround(this, ref lastLocationX, ref lastLocationY);
m_lastMouseMoveLocation = new Point(lastLocationX, lastLocationY);
}
}
private void PanVertical(Point delta)
{
// Scroll according to an average track size, ...
//int trackSize = TrackView.Tracks.Where(track => !track.Fixed).Select(track => track.Height).FirstOrDefault();
int trackSize = 16;
if (trackSize > 0 && VerticalScrollbar != null && VerticalScrollbar.Visible)
{
double scroll = delta.Y/(double) trackSize;
double appliedScroll = System.Math.Round(delta.Y/(double) trackSize);
int initialValue = VerticalScrollbar.Value;
int newValue = (initialValue - (int) appliedScroll).Clamp(VerticalScrollbar.Minimum, VerticalScrollbar.Maximum - VerticalScrollbar.LargeChange + 1);
VerticalScrollbar.Value = newValue;
// Set last point according to what was already consumed by the event.
int unconsumedDelta = (int) ((scroll - appliedScroll)*trackSize);
m_lastMouseMoveLocation.Y -= unconsumedDelta;
}
}
private void PanHorizontal(Point delta)
{
float deltaInTime = delta.X / Timeline.PixelsPerTime;
float oldOffset = Timeline.TimeOffset;
float newOffset = oldOffset - deltaInTime;
if (oldOffset != newOffset)
{
Cursor = PanCursor(oldOffset < newOffset);
Timeline.TimeOffset = newOffset;
}
}
protected virtual Cursor PanCursor(bool west)
{
return west ? Cursors.PanWest : Cursors.PanEast;
}
private void m_timeline_CurrentTimeChanged(object sender, EventArgs e)
{
UpdateScrollbars();
}
private void m_timeline_TimeRangeChanged(object sender, EventArgs e)
{
UpdateScrollbars();
}
private void m_timeline_PixelsPerTimeChanged(object sender, EventArgs e)
{
UpdateScrollbars();
}
private void m_timeline_TimeOffsetChanged(object sender, EventArgs e)
{
UpdateScrollbars();
}
private void m_trackView_ScrollMaximumChanged(object sender, EventArgs e)
{
UpdateScrollbars();
}
void m_trackView_ScrollValueChanged(object sender, EventArgs e)
{
UpdateScrollbars();
}
private void m_horizontalScrollbar_ValueChanged(object sender, EventArgs e)
{
if (m_suspendScrollbarEvents)
{
return;
}
var currentOffset = Timeline.TimeOffset;
var offset = HorizontalScrollbar.Value / Timeline.PixelsPerTime;
if (currentOffset != offset)
{
Timeline.TimeOffset = offset;
Invalidate();
}
OnScrolled(EventArgs.Empty);
}
private void m_verticalScrollbar_ValueChanged(object sender, EventArgs e)
{
if (m_suspendScrollbarEvents)
{
return;
}
TrackView.ScrollValue = VerticalScrollbar.Value;
OnScrolled(EventArgs.Empty);
}
private void RootContainer_BoundsChanged(object sender, EventArgs e)
{
UpdateBoundsRootContainer();
}
#endregion
#region Events
public event EventHandler<EventArgs> Scrolled;
protected virtual void OnScrolled(EventArgs e)
{
Scrolled?.Invoke(this, e);
}
#endregion
}
}