using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using JD.Collections;
using JD.Collections.Extensions;
using JD.Sequencers.Times;
using JD.Sequencers.Windowlesses;
using TeaBox;
using TeaBox.Core.Source.Gui;
namespace JD.Application.View.WindowLess
{
///
/// Paints a hint over other controls when dragging on same position.
///
public class SnapPainter
{
private const int MaxDrawCount = 6;
private const int SnapCircleWidth = 8;
private Rectangle _lastPaintRegion;
///
/// Snaps painter paints dragging line and snaps, this invalidates the region accordingly.
///
public void Invalidate(TrackView trackView)
{
var area = JoinRectangle(trackView.Selection.Where(control => control.IsDragging)
.Select(control => control.Bounds).Concat(_lastPaintRegion));
if (!area.IsEmpty)
{
area.Y = 0;
area.Height = trackView.Parent.Height;
area.Inflate(SnapCircleWidth, 0);
trackView.Invalidate(area);
_lastPaintRegion = Rectangle.Empty;
}
}
private Rectangle JoinRectangle(IEnumerable rectangles)
{
return rectangles.Aggregate(Rectangle.Empty, DrawingExtensions.Union);
}
public void PaintSnaps(TrackView trackView, Graphics graphics)
{
var allItems = trackView.GetTracks().SelectMany(track => track.Children).OfType().ToArray();
// Start/End time of items being dragged.
Set draggedTimes = allItems.SelectMany(DraggingEdges).ToSet();
if (draggedTimes.IsEmpty())
{
return;
}
var min = draggedTimes.Min();
var max = draggedTimes.Max();
if (draggedTimes.Count >= MaxDrawCount)
{
draggedTimes = new Set { min, max };
}
foreach (var time in draggedTimes)
{
DrawDragLine(graphics, trackView, time);
}
// Paint a special snap on the dragged item, which is the one that is used for the snap value.
var currentItem = allItems.FirstOrDefault(item => item.IsCurrentControl && item.IsDragging);
if (currentItem != null)
{
DrawDragLine(graphics, trackView, currentItem.Time);
PaintSnapHint(graphics, currentItem.Timeline.TimeToPixel(currentItem.Time), trackView.Timeline.Height);
}
// Times on which another non dragged item is located.
Set collisionTimes = allItems.Where(item => item.DraggingEdges == Edge.None).SelectMany(Edges).Where(draggedTimes.Contains).ToSet();
foreach (var item in allItems)
{
PaintSnapHint(graphics, item, item.Time, collisionTimes);
PaintSnapHint(graphics, item, item.EndTime, collisionTimes);
}
_lastPaintRegion = Rectangle.FromLTRB(trackView.Timeline.TimeToPixel(min), 0, trackView.Timeline.TimeToPixel(max), trackView.Height);
}
private static void DrawDragLine(Graphics graphics, TrackView trackView, float time)
{
using (var pen = new Pen(Color.DodgerBlue))
{
int position = trackView.Timeline.TimeToPixel(time);
graphics.DrawLine(pen, position, 0, position, trackView.Parent.Height);
}
}
private static void PaintSnapHint(Graphics graphics, LocalizedTrackItem item, float time, Set hotTimes)
{
if (hotTimes.Contains(time))
{
PaintSnapHint(graphics, item.Timeline.TimeToPixel(time), item.Bounds.Top + item.Bounds.Height / 2);
}
}
private static void PaintSnapHint(Graphics graphics, int x, int y)
{
const int halfCircleWidth = SnapCircleWidth / 2;
var circleBounds = new Rectangle(x - halfCircleWidth, y - halfCircleWidth, SnapCircleWidth, SnapCircleWidth);
graphics.FillEllipse(Brushes.WhiteSmoke, circleBounds);
graphics.DrawEllipse(Pens.DodgerBlue, circleBounds);
}
private static IEnumerable Edges(LocalizedTrackItem clip)
{
yield return clip.Time;
yield return clip.EndTime;
}
private static IEnumerable DraggingEdges(LocalizedTrackItem clip)
{
if ((clip.DraggingEdges & Edge.Left) != 0)
{
yield return clip.Time;
}
if ((clip.DraggingEdges & Edge.Right) != 0)
{
yield return clip.EndTime;
}
}
}
}