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; } } } }