784 lines
28 KiB
C#
784 lines
28 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Linq;
|
|
using System.Windows.Forms;
|
|
using JD.Application.View;
|
|
using JD.Application.View.Misc;
|
|
using JD.Application.View.Timeline;
|
|
using JD.Application.View.WindowLess;
|
|
using JD.Audio.Annotations;
|
|
using JD.Core.Collections;
|
|
using JD.Kernel.Interfaces;
|
|
using JD.Kernel.StaticHelpers;
|
|
using JD.Kernel.Structs;
|
|
using JD.Sequencers.Times;
|
|
using JD.Sequencers.Windowlesses;
|
|
using JD.Timeline.Model;
|
|
using JD.Timeline.Model.Clips;
|
|
using JD.Timeline.View.Actions.Macros;
|
|
using JD.Timeline.View.GDI;
|
|
using TeaBox;
|
|
using JD.Serialization;
|
|
using TeaBox.Core.Source.Gui;
|
|
using Actor = JD.Application.Plugin.World.Actor;
|
|
using Brush = System.Drawing.Brush;
|
|
using Color = System.Drawing.Color;
|
|
using FontFamily = System.Drawing.FontFamily;
|
|
using LinearGradientBrush = System.Drawing.Drawing2D.LinearGradientBrush;
|
|
using Pen = System.Drawing.Pen;
|
|
|
|
namespace JD.Timeline.View.ClipView.Widgets
|
|
{
|
|
/// <summary>
|
|
/// Visual representation of a clip in the ClipView
|
|
/// </summary>
|
|
public class ClipWidget : LocalizedTrackItem, IResizable, ICloneable<WindowlessControl>
|
|
{
|
|
#region Constants
|
|
|
|
protected const int IconPadding = 2;
|
|
private static Font DefaultFont = new Font(FontFamily.GenericSansSerif, 7);
|
|
|
|
protected internal static readonly ClipStyle MissingStyle = new ClipStyle { BackgroundColor = Color.Red, Icon = Properties.Resources.Icon_20x20_imagemissing };
|
|
protected internal static readonly ClipStyle CollisionStyle = new ClipStyle { BackgroundColor = Color.FromArgb(255, 222, 0), Icon = Properties.Resources.Icon_20x20_warning };
|
|
protected internal static readonly ClipStyle UnresolvedStyle = new ClipStyle { BackgroundColor = Color.FromArgb(255, 132, 0), Icon = Properties.Resources.Icon_20x20_noactor };
|
|
protected internal static readonly Dictionary<string, ClipStyle> StylePerType = new Dictionary<string, ClipStyle>();
|
|
|
|
#endregion
|
|
|
|
#region Variables
|
|
|
|
private readonly Clip _model;
|
|
private readonly Image _image;
|
|
private readonly LinearBrush _backgroundBrush = new LinearBrush();
|
|
|
|
/// <summary>
|
|
/// Clips are always drawn of a minimum size of the icon size, we still keep the real width corresponding
|
|
/// to clip duration.
|
|
/// </summary>
|
|
private int _durationWidth;
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
static ClipWidget()
|
|
{
|
|
var materialGraphicColor = Color.FromArgb(25, 122, 135);
|
|
RegisterStyle<ColorClip>(Properties.Resources.Icon_20x20_color, Color.FromArgb(121, 75, 123));
|
|
RegisterStyle<ActorEnableClip>(Properties.Resources.Icon_20x20_actorEnable, Color.FromArgb(147, 96, 96), "Actor Activation");
|
|
RegisterStyle<AlphaClip>(Properties.Resources.Icon_20x20_alpha, Color.FromArgb(147, 147, 147));
|
|
RegisterStyle<AlphaClip>("in", Properties.Resources.Icon_20x20_alpha_in, Color.FromArgb(147, 147, 147));
|
|
RegisterStyle<AlphaClip>("out", Properties.Resources.Icon_20x20_alpha_out, Color.FromArgb(147, 147, 147));
|
|
RegisterStyle<FXClip>(Properties.Resources.Icon_20x20_fx, Color.FromArgb(255, 72, 0));
|
|
RegisterStyle<MarkerClip>(Properties.Resources.Icon_20x20_marker, Color.FromArgb(102, 209, 240));
|
|
RegisterStyle<MaterialGraphicAlphaThresholdClip>(Properties.Resources.Icon_20x20_alpha_threshold, materialGraphicColor, "AlphaThredshold");
|
|
RegisterStyle<MaterialGraphicDiffuseAlphaClip>(Properties.Resources.Icon_20x20_alpha, materialGraphicColor, "DiffuseAlpha");
|
|
RegisterStyle<MaterialGraphicDiffuseAlphaClip>("in", Properties.Resources.Icon_20x20_alpha_in, materialGraphicColor, "DiffuseAlpha");
|
|
RegisterStyle<MaterialGraphicDiffuseAlphaClip>("out", Properties.Resources.Icon_20x20_alpha_out, materialGraphicColor, "DiffuseAlpha");
|
|
RegisterStyle<MaterialGraphicDiffuseColorClip>(Properties.Resources.Icon_20x20_color, materialGraphicColor, "DiffuseColor");
|
|
RegisterStyle<MaterialGraphicEnableLayerClip>(Properties.Resources.Icon_20x20_trigger, materialGraphicColor, "EnableLayer");
|
|
RegisterStyle<MaterialGraphicUVAnimRotationClip>(Properties.Resources.Icon_20x20_rotate, materialGraphicColor, "UVAnimRotation");
|
|
RegisterStyle<MaterialGraphicUVRotationClip>(Properties.Resources.Icon_20x20_rotate, materialGraphicColor, "UVRotation");
|
|
RegisterStyle<MaterialGraphicUVScaleClip>(Properties.Resources.Icon_20x20_scale, materialGraphicColor, "UVScale");
|
|
RegisterStyle<MaterialGraphicUVScrollClip>(Properties.Resources.Icon_20x20_translation, materialGraphicColor, "UVScroll");
|
|
RegisterStyle<MaterialGraphicUVTranslationClip>(Properties.Resources.Icon_20x20_translation, materialGraphicColor, "UVTranslation");
|
|
RegisterStyle<MotionClip>(Properties.Resources.Icon_24x24_move, Color.FromArgb(220, 220, 220), "Move");
|
|
RegisterStyle<PictogramClip>(Properties.Resources.Icon_24x24_picto, Color.FromArgb(150, 170, 190), "Pictogram");
|
|
RegisterStyle<CameraFeedClip>(Properties.Resources.Icon_20x20_camera, Color.FromArgb(255, 153, 204), "CameraFeed");
|
|
RegisterStyle<PivotClip>(Properties.Resources.Icon_20x20_pivot, Color.FromArgb(255, 0, 126));
|
|
RegisterStyle<SizeClip>(Properties.Resources.Icon_20x20_scale, Color.FromArgb(255, 178, 0));
|
|
RegisterStyle<ProportionClip>(Properties.Resources.Icon_20x20_scale, Color.FromArgb(174, 208, 0));
|
|
RegisterStyle<Proportion3DClip>(Properties.Resources.Icon_20x20_scale, Color.FromArgb(174, 208, 0));
|
|
RegisterStyle<TranslationClip>(Properties.Resources.Icon_20x20_translation, Color.FromArgb(39, 100, 183));
|
|
RegisterStyle<RotationClip>(Properties.Resources.Icon_20x20_rotate, Color.FromArgb(39, 183, 46));
|
|
RegisterStyle<GameplayEventClip>(Properties.Resources.Icon_20x20_trigger, Color.FromArgb(0, 137, 195), "Gameplay Event");
|
|
RegisterStyle<HideUserInterfaceClip>(Properties.Resources.Icon_20x20_hideUI, Color.FromArgb(87, 124, 114), "Hide user interface");
|
|
RegisterStyle<AnimationClip>(Properties.Resources.Icon_20x20_animation, Color.FromArgb(205, 0, 0), "Animation");
|
|
RegisterStyle<SoundSetClip>(Properties.Resources.Icon_20x20_sound, Color.FromArgb(56, 103, 0));
|
|
RegisterStyle<SoundwichClip>(Properties.Resources.Icon_20x20_sound, Color.FromArgb(75, 38, 175), "Soundwich");
|
|
RegisterStyle<SoundwichClipWithId>(Properties.Resources.Icon_20x20_sound, Color.FromArgb(75, 38, 175), "SoundwichWithId");
|
|
RegisterStyle<SpawnActorClip>(Properties.Resources.Icon_20x20_spawn, Color.FromArgb(134, 184, 0), "Spawner");
|
|
RegisterStyle<TapeLauncherClip>(Properties.Resources.Icon_20x20_tape, Color.FromArgb(74, 106, 137));
|
|
RegisterStyle<TapeReferenceClip>(Properties.Resources.Icon_20x20_tape, Color.FromArgb(117, 124, 55));
|
|
RegisterStyle<TextClip>(Properties.Resources.Icon_20x20_text, Color.FromArgb(161, 121, 200));
|
|
RegisterStyle<TextAreaSizeClip>(Properties.Resources.Icon_20x20_text, Color.FromArgb(161, 121, 200), "TextArea ReSize Clip");
|
|
RegisterStyle<GoldEffectClip>(Properties.Resources.Icon_20x20_fx, Color.FromArgb(223, 84, 60), "Gold move effect");
|
|
RegisterStyle<ScoreClip>(Properties.Resources.TransparentBackground, Color.FromArgb(223, 84, 60), "Score Clip");
|
|
RegisterStyle<MoveExampleClip>(Properties.Resources.TransparentBackground, Color.FromArgb(223, 84, 60), "Move Example Clip");
|
|
RegisterStyle<KaraokeClip>(Properties.Resources.Icon_20x20_fx, Color.FromArgb(255, 222, 0), "Karaoke clip");
|
|
RegisterStyle<SlotClip>(Properties.Resources.Icon_16x16_slot, Color.FromArgb(147, 147, 147), "Mashup Signature");
|
|
RegisterStyle<VideoClip>(Properties.Resources.Icon_16_16_plug, Color.FromArgb(147, 147, 147), "Mashup Video");
|
|
RegisterStyle<CommunityDancerClip>(Properties.Resources.Icon_20x20_motion, Color.FromArgb(220, 220, 220), "Community clip");
|
|
RegisterStyle<VibrationClip>(Properties.Resources.Icon_24x24_move, Color.FromArgb(220, 220, 220), "Vibration");
|
|
}
|
|
|
|
public ClipWidget(Clip model)
|
|
{
|
|
Throw.IfNull("@event", "event");
|
|
|
|
_model = model;
|
|
_model.PropertyChanged += ModelChanged;
|
|
_image = Icon;
|
|
if (_model.Curves.Any())
|
|
{
|
|
Controls.Add(new CurveKeysWidget(this));
|
|
}
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
_model.PropertyChanged -= ModelChanged;
|
|
_backgroundBrush.Dispose();
|
|
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
public Clip Model
|
|
{
|
|
get { return _model; }
|
|
}
|
|
|
|
public virtual bool Finite
|
|
{
|
|
get
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public override float Duration
|
|
{
|
|
get
|
|
{
|
|
return Timeline.TimeFromFrameDuration(Model.Duration);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start position for the text.
|
|
/// </summary>
|
|
protected int TextLeft
|
|
{
|
|
get { return IconPadding * 2 + _image.Width; }
|
|
}
|
|
|
|
protected Rectangle ImageBounds
|
|
{
|
|
get
|
|
{
|
|
return new Rectangle(new Point(Bounds.Left + IconPadding, Bounds.Top + (Bounds.Height - _image.Height) / 2 + 1), _image.Size);
|
|
}
|
|
}
|
|
|
|
public new TapeTrackWidget Track
|
|
{
|
|
get { return (TapeTrackWidget)base.Track; }
|
|
}
|
|
|
|
public override string ToolTipText
|
|
{
|
|
get
|
|
{
|
|
var timeline = Timeline;
|
|
// Can happen when clips are removed from the track view. ...
|
|
if (timeline == null)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
var collision = Model.CollisionsWith;
|
|
if (collision != null)
|
|
{
|
|
return collision.ToString();
|
|
}
|
|
|
|
var beatText = Beat.ToTimeString(timeline.TimeFromTimeUnit(Time));
|
|
var durationText = Beat.ToTimeString(Model.TimeDuration());
|
|
|
|
return $"Time: {beatText} (beat {Model.StartBeat:0.00})\nDuration: {durationText} ({Model.BeatDuration:0.00} beats)";
|
|
}
|
|
}
|
|
|
|
protected virtual int MinimumWidth
|
|
{
|
|
get { return IconPadding + _image.Width; }
|
|
}
|
|
|
|
public override bool SupportAutoLoop
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
public virtual ClipStyle Style
|
|
{
|
|
get
|
|
{
|
|
if (Model.CollisionsWithOther)
|
|
{
|
|
return CollisionStyle;
|
|
}
|
|
if (!Model.Tape.IsOffline && Model.IsUnresolved())
|
|
{
|
|
return UnresolvedStyle;
|
|
}
|
|
|
|
else if (Model is AlphaClip)
|
|
{
|
|
var alphaClip = Model as AlphaClip;
|
|
bool? fadesToOpaque = AlphaClip.FadesToOpaque(alphaClip.Curve);
|
|
if (fadesToOpaque.HasValue)
|
|
{
|
|
return typeof(AlphaClip).GetStyle(fadesToOpaque.Value ? "in" : "out");
|
|
}
|
|
}
|
|
return Model.GetType().GetStyle();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
public virtual void Update()
|
|
{
|
|
Time = Timeline.TimeFromFrame(Model.StartFrame);
|
|
SetupBounds();
|
|
}
|
|
|
|
protected override void SetupBounds()
|
|
{
|
|
if (Timeline != null)
|
|
{
|
|
int left = Timeline.PixelFromFrame(Model.StartFrame);
|
|
int right = Timeline.PixelFromFrame(Model.StopFrame);
|
|
_durationWidth = right - left;
|
|
int width = right - left;
|
|
|
|
// When zoomed out, try to paint the clip with the available space on right side of the clip.
|
|
if (_durationWidth < MinimumWidth)
|
|
{
|
|
int maxRight = NextClipWidgetOnTrack == null ? int.MaxValue : NextClipWidgetOnTrack.Bounds.X;
|
|
width = new Range<int>(_durationWidth, maxRight - Bounds.Left - 2).Clamp(MinimumWidth);
|
|
}
|
|
|
|
Bounds = new Rectangle(left, Track.Bounds.Top, width, Track.Height);
|
|
}
|
|
}
|
|
|
|
public ClipWidget NextClipWidgetOnTrack
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update internal model when the track item is moved.
|
|
/// </summary>
|
|
protected override void OnTimeChanged(System.EventArgs e)
|
|
{
|
|
base.OnTimeChanged(e);
|
|
Model.StartFrame = (int)Math.Round(Timeline.FrameFromTime(Time));
|
|
}
|
|
|
|
public override void Snap_Self(float originalDelta, ref float snappedDelta, ref float diff)
|
|
{
|
|
float snapTime = DraggingEdges == Edge.Right ? EndTime : Time;
|
|
float newValue;
|
|
|
|
if (SnappingUnit == SnappingUnit.None)
|
|
{
|
|
// Snap to the frame.
|
|
newValue = Timeline.TimeFromFrame((float)Math.Round(Timeline.FrameFromTime(snapTime + snappedDelta)));
|
|
}
|
|
else
|
|
{
|
|
newValue = Timeline.Wave().UnitConverter.Snap(new BeatUnit(snapTime + snappedDelta), SnappingUnit).Value;
|
|
}
|
|
|
|
snappedDelta = newValue - snapTime;
|
|
}
|
|
|
|
protected virtual SnappingUnit SnappingUnit
|
|
{
|
|
get { return EditionOptions.CurrentSnappingUnit; }
|
|
}
|
|
|
|
public override void Move(float delta)
|
|
{
|
|
Time += delta;
|
|
}
|
|
|
|
public virtual bool IsResizeAllowed
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
public virtual void Resize(Edge resizeEdge, float delta)
|
|
{
|
|
float diff = 0;
|
|
Snap_Self(delta, ref delta, ref diff);
|
|
|
|
int frameDelta = (int)Math.Round(Timeline.FrameFromTime(delta));
|
|
int newDuration = Model.Duration;
|
|
|
|
if (resizeEdge == Edge.Left)
|
|
{
|
|
newDuration = Math.Max(1, Model.Duration - frameDelta);
|
|
Model.StartFrame -= newDuration - Model.Duration;
|
|
}
|
|
else if (resizeEdge == Edge.Right)
|
|
{
|
|
newDuration = Math.Max(1, Model.Duration + frameDelta);
|
|
}
|
|
|
|
Model.Resize(newDuration, resizeCurves: WaveControl.IsStretchKeyPress);
|
|
}
|
|
|
|
public virtual void ResizeByValue(Edge resizeEdge, float value)
|
|
{
|
|
int frame = (int)Math.Round(Timeline.FrameFromTime(value));
|
|
int newDuration = Model.Duration;
|
|
if (resizeEdge == Edge.Left)
|
|
{
|
|
newDuration = Math.Max(1, Model.StopFrame - frame);
|
|
Model.StartFrame = frame;
|
|
}
|
|
else if (resizeEdge == Edge.Right)
|
|
{
|
|
newDuration = frame - Model.StartFrame;
|
|
}
|
|
Model.Resize(newDuration, WaveControl.IsStretchKeyPress);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when the clip is added via a context menu/shortcut.
|
|
/// </summary>
|
|
public virtual void OnInserted()
|
|
{
|
|
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Painting
|
|
|
|
protected Brush BackgroundBrush(Rectangle bounds)
|
|
{
|
|
return BackgroundBrush(bounds, BaseColor);
|
|
}
|
|
|
|
protected Brush BackgroundBrush(Rectangle bounds, Color color)
|
|
{
|
|
_backgroundBrush.Bounds = bounds;
|
|
_backgroundBrush.Color1 = color.Add(10);
|
|
_backgroundBrush.Color2 = color.Add(-10);
|
|
return _backgroundBrush.Brush;
|
|
}
|
|
|
|
protected virtual Pen EdgePen
|
|
{
|
|
get { return StyleSheet.Pens[Selected ? Color.White : BaseColor.Add(-30)]; }
|
|
}
|
|
|
|
protected Color BaseColor
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
protected Rectangle DurationBounds
|
|
{
|
|
get
|
|
{
|
|
var bounds = Bounds;
|
|
bounds.Width = _durationWidth;
|
|
return bounds;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// RegisterStyle and associates a style to a clip type
|
|
/// </summary>
|
|
/// <typeparam name="T">A Clip class</typeparam>
|
|
/// <param name="styleId">an Id used if you want to associate multiple styles to a clip type</param>
|
|
/// <param name="icon">an Icon</param>
|
|
/// <param name="color">a Color</param>
|
|
/// <param name="displayName">a display name</param>
|
|
public static void RegisterStyle<T>(string styleId, Bitmap icon, Color color, string displayName = "")
|
|
where T : Clip
|
|
{
|
|
var style = new ClipStyle { BackgroundColor = color, Icon = icon, DisplayName = displayName.NonEmpty(typeof(T).Name) };
|
|
StylePerType.Add(typeof(T).Name + styleId, style);
|
|
}
|
|
|
|
public static void RegisterStyle<T>(Bitmap icon, Color color, string displayName = "")
|
|
where T : Clip
|
|
{
|
|
RegisterStyle<T>("", icon, color, displayName);
|
|
}
|
|
|
|
protected override void OnPaint(PaintEventArgs e)
|
|
{
|
|
SetupBounds();
|
|
|
|
var graphics = e.Graphics;
|
|
|
|
BaseColor = BackgroundColor;
|
|
if (Selected)
|
|
{
|
|
BaseColor = BaseColor.Add(100);
|
|
}
|
|
|
|
FillBackground(graphics, Bounds);
|
|
DrawClipType(Model.Type, graphics);
|
|
DrawEdge(graphics, Bounds);
|
|
|
|
OnPaintChildControls(e);
|
|
|
|
if (!Model.IsActive)
|
|
{
|
|
DrawAsInactive(graphics);
|
|
}
|
|
}
|
|
|
|
protected void DrawAsInactive(Graphics graphics)
|
|
{
|
|
var bounds = Bounds;
|
|
if (Selected)
|
|
{
|
|
bounds.Height -= 2;
|
|
bounds.Width -= 2;
|
|
bounds.X++;
|
|
bounds.Y++;
|
|
}
|
|
|
|
if (bounds.Width > 0 && bounds.Height > 0)
|
|
{
|
|
var color = Color.Black.WithAlpha(200);
|
|
var brush = new LinearBrush { Color1 = color, Color2 = color, Bounds = bounds };
|
|
graphics.FillRectangle(brush.Brush, bounds);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the string as displayed in the editor for the event type.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public string DisplayName
|
|
{
|
|
get { return Style.DisplayName; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Icon associated with the event type
|
|
/// </summary>
|
|
public Bitmap Icon
|
|
{
|
|
get { return Style.Icon; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Icon associated with the event type when not selected.
|
|
/// </summary>
|
|
public Bitmap FadeIcon
|
|
{
|
|
get { return Style.FadeIcon; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Color associated with the event type when selected.
|
|
/// </summary>
|
|
public Color BackgroundColor
|
|
{
|
|
get { return Style.BackgroundColor; }
|
|
}
|
|
|
|
protected virtual void DrawClipType(System.Type clipType, Graphics graphics)
|
|
{
|
|
var image = Selected ? this.Icon : FadeIcon;
|
|
var imageBounds = ImageBounds;
|
|
|
|
int right = Bounds.Right;
|
|
if (imageBounds.Right > right)
|
|
{
|
|
imageBounds.Width -= imageBounds.Right - right;
|
|
}
|
|
if (image != null)
|
|
{
|
|
graphics.DrawImageUnscaledAndClipped(image, imageBounds);
|
|
}
|
|
}
|
|
|
|
protected void DrawClipTypeSmall(System.Type clipType, Graphics graphics)
|
|
{
|
|
DrawClipTypes(new[] { clipType }, graphics);
|
|
}
|
|
|
|
protected void DrawClipTypes(IEnumerable<System.Type> clipTypes, Graphics graphics)
|
|
{
|
|
const int smallIconSize = 12;
|
|
int left = Bounds.Left + IconPadding;
|
|
int right = Bounds.Right;
|
|
|
|
foreach (var clipType in clipTypes)
|
|
{
|
|
var bounds = new Rectangle(left, Bounds.Top + IconPadding, smallIconSize, smallIconSize);
|
|
if (bounds.Right > right)
|
|
{
|
|
bounds.Width -= bounds.Right - right;
|
|
}
|
|
|
|
if (bounds.Width > 0)
|
|
{
|
|
graphics.DrawImage(clipType.GetStyle().Icon, bounds);
|
|
left += smallIconSize + 1;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected bool DrawActorImages(IEnumerable<Image> actorsImage, Graphics graphics)
|
|
{
|
|
const int minimumHeightToDrawActorImage = 24;
|
|
const int offset = 6;
|
|
|
|
int position = Bounds.Left + IconPadding;
|
|
bool draw = false;
|
|
|
|
if (Height >= minimumHeightToDrawActorImage)
|
|
{
|
|
foreach (var image in actorsImage)
|
|
{
|
|
if (image != null && image != Actor.DefaultImage)
|
|
{
|
|
int size = Height - IconPadding * 2;
|
|
var imageBounds = new Rectangle(position, Bounds.Top + IconPadding + offset, size, size - offset);
|
|
|
|
int right = Bounds.Right;
|
|
if (imageBounds.Right > right)
|
|
{
|
|
imageBounds.Width -= imageBounds.Right - right;
|
|
}
|
|
|
|
if (imageBounds.Width > 0 && imageBounds.Height > 0)
|
|
{
|
|
graphics.DrawImage(image, imageBounds);
|
|
}
|
|
|
|
position += imageBounds.Width;
|
|
draw = true;
|
|
}
|
|
}
|
|
}
|
|
return draw;
|
|
}
|
|
|
|
protected void DrawEdge(Graphics graphics, Rectangle bounds)
|
|
{
|
|
bounds.Height--; // The pen draws outside the frame otherwise.
|
|
bounds.Width--; // The pen draws outside the frame otherwise.
|
|
if (bounds.Width > 0)
|
|
{
|
|
if (IsCurrentControl)
|
|
{
|
|
ControlPaint.DrawFocusRectangle(graphics, bounds);
|
|
}
|
|
else
|
|
{
|
|
graphics.DrawRectangle(EdgePen, bounds);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void DrawRectangle(Graphics graphics, Rectangle bounds, Pen pen)
|
|
{
|
|
int penWidth = (int)pen.Width;
|
|
bounds.Height -= penWidth; // The pen draws outside the frame otherwise.
|
|
bounds.Width -= penWidth; // The pen draws outside the frame otherwise.
|
|
bounds.Offset(penWidth / 2, penWidth / 2);
|
|
if (bounds.Width > 0)
|
|
{
|
|
graphics.DrawRectangle(pen, bounds);
|
|
}
|
|
}
|
|
|
|
protected virtual void FillBackground(Graphics graphics, Rectangle bounds)
|
|
{
|
|
FillBackground(graphics, bounds, BaseColor);
|
|
}
|
|
|
|
protected virtual void FillBackground(Graphics graphics, Rectangle bounds, Color color)
|
|
{
|
|
if (bounds.Width <= 0)
|
|
return;
|
|
if (Finite)
|
|
{
|
|
graphics.FillRectangle(BackgroundBrush(bounds, color), bounds);
|
|
}
|
|
else
|
|
{
|
|
var brush = new LinearGradientBrush(bounds, color, color.Add(150).WithAlpha(100), LinearGradientMode.Horizontal);
|
|
graphics.FillRectangle(brush, bounds);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Paints a label in form : "type:value" with the label in a different color.
|
|
/// </summary>
|
|
protected void DrawText(Graphics graphics, string label, string value, int line = -1, Color? textColor = null)
|
|
{
|
|
const int lineCount = 2;
|
|
|
|
var rect = Bounds;
|
|
var iconWidth = TextLeft;
|
|
rect.Width -= iconWidth;
|
|
rect.X += iconWidth;
|
|
|
|
if (line >= 0)
|
|
{
|
|
rect.Height /= lineCount;
|
|
rect.Y += line * rect.Height;
|
|
}
|
|
|
|
if (rect.Width <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var baseColor = Selected ? Color.FromArgb(60, 60, 60) : textColor ?? Color.White;
|
|
if (!Model.IsActive)
|
|
{
|
|
baseColor = baseColor.Add(-200);
|
|
}
|
|
var brush = StyleSheet.Brushes[baseColor];
|
|
graphics.DrawString(label, DefaultFont, brush, rect);
|
|
|
|
int width = (int)graphics.MeasureString(label, DefaultFont, rect.Width).Width;
|
|
rect.Width -= width;
|
|
rect.X += width;
|
|
|
|
if (rect.Width <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
graphics.DrawString(value, DefaultFont, brush, rect, StyleSheet.EllipsisStringFormat);
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return "Clip : " + Model;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Handlers
|
|
|
|
protected virtual void ModelChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
|
{
|
|
Invalidate();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Clone on drag
|
|
|
|
public WindowlessControl Clone()
|
|
{
|
|
var clone = _model.Clone();
|
|
_model.Tape.Clips.Add(clone);
|
|
return Parent.Controls.OfType<ClipWidget>().Single(clip => clip.Model == clone);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Nested
|
|
|
|
/// <summary>
|
|
/// Responsible of painting the keys (they can even be outside the clip ...)
|
|
/// </summary>
|
|
private class CurveKeysWidget : WindowlessControl
|
|
{
|
|
private static readonly Image CurveKeyImage = Properties.Resources.Icon_16x16_curveKey;
|
|
private readonly ClipWidget _parent;
|
|
|
|
public CurveKeysWidget(ClipWidget parent)
|
|
{
|
|
_parent = parent;
|
|
_parent.BoundsChanged += Parent_BoundsChanged;
|
|
_parent.Model.Curves.ItemChanged += Curves_ItemChanged;
|
|
UpdateBounds();
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
_parent.Model.Curves.ItemChanged += Curves_ItemChanged;
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
public override string ToolTipText
|
|
{
|
|
get { return _parent.ToolTipText; }
|
|
}
|
|
|
|
private void UpdateBounds()
|
|
{
|
|
if (_parent.Timeline != null)
|
|
{
|
|
int start = _parent.Model.StartFrame;
|
|
int stop = _parent.Model.Curves.SelectMany(curve => curve.Keys).Select(key => (int)key.Frame + start).DefaultIfEmpty(start).Max();
|
|
|
|
int imageWidth = CurveKeyImage.Width;
|
|
int left = _parent.Timeline.PixelFromFrame(start) - imageWidth;
|
|
int right = _parent.Timeline.PixelFromFrame(stop) + imageWidth;
|
|
Bounds = Rectangle.FromLTRB(left, _parent.Bounds.Top, right, _parent.Bounds.Bottom);
|
|
}
|
|
}
|
|
|
|
protected override void OnPaint(PaintEventArgs e)
|
|
{
|
|
base.OnPaint(e);
|
|
|
|
const int minimumWidth = 30;
|
|
if (_parent.Width < minimumWidth)
|
|
{
|
|
return;
|
|
}
|
|
|
|
float halfWidth = CurveKeyImage.Size.Width / 2f;
|
|
float y = Bounds.Bottom - CurveKeyImage.Height;
|
|
|
|
var timeline = _parent.Timeline;
|
|
var startFrame = _parent.Model.StartFrame;
|
|
|
|
foreach (var frame in _parent.Model.Curves.SelectMany(curve => curve.Keys).Select(key => key.Frame).Distinct())
|
|
{
|
|
float x = timeline.PixelFromFrame(frame + startFrame) - halfWidth;
|
|
e.Graphics.DrawImage(CurveKeyImage, x, y);
|
|
}
|
|
}
|
|
|
|
private void Curves_ItemChanged(object sender, ItemChangedEventArgs<BezierCurveFloat> args)
|
|
{
|
|
UpdateBounds();
|
|
}
|
|
|
|
void Parent_BoundsChanged(object sender, EventArgs e)
|
|
{
|
|
UpdateBounds();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|