135 lines
No EOL
4 KiB
C#
135 lines
No EOL
4 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// Paints a hint over other controls when dragging on same position.
|
|
/// </summary>
|
|
public class SnapPainter
|
|
{
|
|
private const int MaxDrawCount = 6;
|
|
private const int SnapCircleWidth = 8;
|
|
private Rectangle _lastPaintRegion;
|
|
|
|
/// <summary>
|
|
/// Snaps painter paints dragging line and snaps, this invalidates the region accordingly.
|
|
/// </summary>
|
|
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<Rectangle> rectangles)
|
|
{
|
|
return rectangles.Aggregate(Rectangle.Empty, DrawingExtensions.Union);
|
|
}
|
|
|
|
public void PaintSnaps(TrackView trackView, Graphics graphics)
|
|
{
|
|
var allItems = trackView.GetTracks().SelectMany(track => track.Children).OfType<LocalizedTrackItem>().ToArray();
|
|
|
|
// Start/End time of items being dragged.
|
|
Set<float> draggedTimes = allItems.SelectMany(DraggingEdges).ToSet();
|
|
if (draggedTimes.IsEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
var min = draggedTimes.Min();
|
|
var max = draggedTimes.Max();
|
|
|
|
if (draggedTimes.Count >= MaxDrawCount)
|
|
{
|
|
draggedTimes = new Set<float> { 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<float> 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<float> 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<float> Edges(LocalizedTrackItem clip)
|
|
{
|
|
yield return clip.Time;
|
|
yield return clip.EndTime;
|
|
}
|
|
|
|
private static IEnumerable<float> DraggingEdges(LocalizedTrackItem clip)
|
|
{
|
|
if ((clip.DraggingEdges & Edge.Left) != 0)
|
|
{
|
|
yield return clip.Time;
|
|
}
|
|
|
|
if ((clip.DraggingEdges & Edge.Right) != 0)
|
|
{
|
|
yield return clip.EndTime;
|
|
}
|
|
}
|
|
}
|
|
} |