JD2022-TU1/main/tools/framework/JD.Timeline/Dance/DanceEditorClipWidget.cs

252 lines
7.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using JD.Application.View.Timeline;
using JD.Audio.Annotations;
using JD.Collections.Extensions;
using JD.Maths.Utils;
using JD.VideoPlayer.Project;
using TeaBox;
using JD.Serialization;
namespace JD.Timeline.View.ClipView.Widgets
{
public class DanceEditorClipWidget : ClipWidget
{
#region Variables
private List<ToolbarItem> _toolbarItems = new List<ToolbarItem>();
protected static readonly Color SelectionBorderColor = Color.FromArgb(255, 200, 0);
protected static readonly Color SelectionFillColor = Color.FromArgb(100, SelectionBorderColor);
protected static readonly Pen SelectionBorderPen = new Pen(SelectionBorderColor, 2);
protected static readonly Pen PlayingBorderPen = new Pen(Color.Firebrick, 2);
protected static readonly Color WarningFillColor = Color.FromArgb(255, 255, 0);
#endregion
#region Constructor
public DanceEditorClipWidget(Clip model)
: base(model)
{
}
#endregion
#region Properties
public List<ToolbarItem> ToolbarItems
{
get { return _toolbarItems; }
set { _toolbarItems = value; }
}
/// <summary>
/// Whether the timeline cursors is currently over the clip.
/// </summary>
protected bool IsPlaying
{
get
{
var currentBeat = Timeline.BeatFromTimeUnit(Timeline.TimeCursor.Time).Value;
return currentBeat.IsBetween(Model.StartBeat, Model.StopBeat);
}
}
#endregion
#region Methods
/// <summary>
/// Default painting for Dance Editor clips
/// </summary>
protected override void OnPaint(PaintEventArgs e)
{
SetupBounds();
var graphics = e.Graphics;
if (Model.CollisionsWithOther)
{
DrawClipWithWarning(graphics);
return;
}
FillBackground(graphics, Bounds, BackgroundColor);
DrawClipType(Model.Type, graphics);
if (Selected)
{
FillBackground(graphics, Bounds, SelectionFillColor);
DrawRectangle(graphics, Bounds, SelectionBorderPen);
}
else
{
DrawEdge(graphics, Bounds);
}
OnPaintChildControls(e);
}
/// <summary>
/// Method used to draw clip when there is a warning
/// </summary>
protected void DrawClipWithWarning(Graphics graphics, int additionalOffsetX = 0, int additionalOffsetY = 0)
{
var image = Properties.Resources.Icon_16x16_warning;
var imageBounds = new Rectangle(Bounds.X + 2 + additionalOffsetX, Bounds.Top + 2 + additionalOffsetY, image.Width, image.Height); ;
int right = Bounds.Right;
if (imageBounds.Right > right)
{
imageBounds.Width -= imageBounds.Right - right;
}
if (image != null)
{
graphics.DrawImageUnscaledAndClipped(image, imageBounds);
}
DrawNotificationIcons(graphics);
}
/// <summary>
/// Drawing of notification icons
/// </summary>
protected void DrawNotificationIcons(Graphics graphics)
{
foreach (var icon in ToolbarItems.Where(x => x.IsVisible()))
{
var bounds = GetBounds(icon);
if (bounds.Width > 0)
{
graphics.DrawImage(icon.Bitmap(), bounds);
}
}
}
/// <summary>
/// Returns the bounds of the notification icon. Used for drawing and mouse interaction.
/// </summary>
protected Rectangle GetBounds(ToolbarItem icon)
{
var icons = ToolbarItems.Where(x => x.IsVisible()).ToList();
while (icons.Count > 0 && icons.Sum(x => x.Width) > Bounds.Width)
{
var iconToRemove = icons.MaxElement(x => x.Priority);
if (iconToRemove != null)
{
icons.Remove(iconToRemove);
}
}
if (!icons.Contains(icon))
return new Rectangle(0,0,0,0);
int left = Bounds.Right;
foreach (var notificationIcon in icons)
{
left -= notificationIcon.Width;
if (notificationIcon == icon)
break;
}
return new Rectangle(left, Bounds.Top, icon.Width, icon.Height);
}
/// <summary>
/// Handles click on notification icons
/// </summary>
public override void OnMouseClick(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
foreach (var notificationIcon in ToolbarItems)
{
if (GetBounds(notificationIcon).Contains(e.Location))
{
ToolbarItem icon = notificationIcon;
if (icon.OnClick != null)
{
TeaBox.ObjectModel.Application.Main.Dispatcher.DispatchAsync(() => icon.OnClick(),forceAsync: true);
}
}
}
}
base.OnMouseClick(e);
}
#endregion
#region Video Preview
private float _lastPaintBeat;
protected void PaintPictogram(Graphics graphics)
{
var destBounds = Bounds;
destBounds.Height -= 10;
destBounds.Y += 5;
var currentBeat = Timeline.BeatFromTimeUnit(Timeline.TimeCursor.Time).Value;
var isPlaying = IsPlaying;
var wasPlaying = _lastPaintBeat.IsBetween(Model.StartBeat, Model.StopBeat);
// While playing, refresh the cursor when moving over the clip.
if (Math.Abs(_lastPaintBeat - currentBeat) > 0.001 && (isPlaying || wasPlaying))
{
Invalidate();
}
_lastPaintBeat = currentBeat;
var beatToPaint = GetBeatToPaint(currentBeat);
Model.Tape.VideoSpriteSheet().Paint(graphics, new BeatUnit(beatToPaint), destBounds);
}
protected virtual float GetBeatToPaint(float currentBeat)
{
if (currentBeat.IsBetween(Model.StartBeat, Model.StopBeat) && !IsDragging)
{
return currentBeat;
}
return Model.StartBeat;
}
#endregion
}
#region NotificationIcon
/// <summary>
/// Represents a notification icon on widget's top-right corner
/// </summary>
public class ToolbarItem
{
private Func<Bitmap> _bitmap;
public Func<Bitmap> Bitmap
{
get { return _bitmap; }
set
{
_bitmap = value;
Width = value().Width;
Height = value().Height;
}
}
public int Width { get; private set; }
public int Height { get; private set; }
public Func<bool> IsVisible = () => true;
public System.Action OnClick = () => { };
public uint Priority { get; set; }
}
#endregion
}