rework to Azaion.Suite

This commit is contained in:
Alex Bezdieniezhnykh
2024-11-21 13:41:32 +02:00
parent 2cf69f4e4e
commit 5a592e9dbf
76 changed files with 1739 additions and 882 deletions
@@ -0,0 +1,49 @@
<DataGrid x:Class="Azaion.Common.Controls.AnnotationClasses"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Background="Black"
RowBackground="#252525"
Foreground="White"
RowHeaderWidth="0"
Padding="2 0 0 0"
AutoGenerateColumns="False"
SelectionMode="Single"
CellStyle="{DynamicResource DataGridCellStyle1}"
IsReadOnly="True"
CanUserResizeRows="False"
CanUserResizeColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn
Width="50"
Header="Клавіша"
CanUserSort="False">
<DataGridTemplateColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="#252525"></Setter>
</Style>
</DataGridTemplateColumn.HeaderStyle>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border Background="{Binding Path=ColorBrush}">
<TextBlock Text="{Binding Path=ClassNumber}"></TextBlock>
</Border>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn
Width="*"
Header="Назва"
Binding="{Binding Path=Name}"
CanUserSort="False">
<DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="#252525"></Setter>
</Style>
</DataGridTextColumn.HeaderStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
@@ -0,0 +1,9 @@
namespace Azaion.Common.Controls;
public partial class AnnotationClasses
{
public AnnotationClasses()
{
InitializeComponent();
}
}
+122
View File
@@ -0,0 +1,122 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using Azaion.Common.DTO;
using Label = System.Windows.Controls.Label;
namespace Azaion.Common.Controls;
public class AnnotationControl : Border
{
private readonly Action<object, MouseButtonEventArgs> _resizeStart;
private const double RESIZE_RECT_SIZE = 9;
private readonly Grid _grid;
private readonly TextBlock _classNameLabel;
private readonly Label _probabilityLabel;
public TimeSpan? Time { get; set; }
private AnnotationClass _annotationClass = null!;
public AnnotationClass AnnotationClass
{
get => _annotationClass;
set
{
_grid.Background = value.ColorBrush;
_probabilityLabel.Background = value.ColorBrush;
_classNameLabel.Text = value.Name;
_annotationClass = value;
}
}
private readonly Rectangle _selectionFrame;
private bool _isSelected;
public bool IsSelected
{
get => _isSelected;
set
{
_selectionFrame.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
_isSelected = value;
}
}
public AnnotationControl(AnnotationClass annotationClass, TimeSpan? time, Action<object, MouseButtonEventArgs> resizeStart, double? probability = null)
{
Time = time;
_resizeStart = resizeStart;
_classNameLabel = new TextBlock
{
Text = annotationClass.Name,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Top,
Margin = new Thickness(0, 15, 0, 0),
FontSize = 14,
Cursor = Cursors.SizeAll
};
_probabilityLabel = new Label
{
Content = probability.HasValue ? $"{probability.Value:F0}%" : string.Empty,
HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Top,
Margin = new Thickness(0, -32, 0, 0),
FontSize = 16,
Visibility = Visibility.Visible
};
_selectionFrame = new Rectangle
{
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch,
Stroke = new SolidColorBrush(Colors.Black),
StrokeThickness = 2,
Visibility = Visibility.Collapsed
};
_grid = new Grid
{
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch,
Children =
{
_selectionFrame,
_classNameLabel,
AddRect("rLT", HorizontalAlignment.Left, VerticalAlignment.Top, Cursors.SizeNWSE),
AddRect("rCT", HorizontalAlignment.Center, VerticalAlignment.Top, Cursors.SizeNS),
AddRect("rRT", HorizontalAlignment.Right, VerticalAlignment.Top, Cursors.SizeNESW),
AddRect("rLC", HorizontalAlignment.Left, VerticalAlignment.Center, Cursors.SizeWE),
AddRect("rRC", HorizontalAlignment.Right, VerticalAlignment.Center, Cursors.SizeWE),
AddRect("rLB", HorizontalAlignment.Left, VerticalAlignment.Bottom, Cursors.SizeNESW),
AddRect("rCB", HorizontalAlignment.Center, VerticalAlignment.Bottom, Cursors.SizeNS),
AddRect("rRB", HorizontalAlignment.Right, VerticalAlignment.Bottom, Cursors.SizeNWSE)
}
};
if (probability.HasValue)
_grid.Children.Add(_probabilityLabel);
Child = _grid;
Cursor = Cursors.SizeAll;
AnnotationClass = annotationClass;
}
//small corners
private Rectangle AddRect(string name, HorizontalAlignment ha, VerticalAlignment va, Cursor crs)
{
var rect = new Rectangle() // small rectangles at the corners and sides
{
HorizontalAlignment = ha,
VerticalAlignment = va,
Width = RESIZE_RECT_SIZE,
Height = RESIZE_RECT_SIZE,
Stroke = new SolidColorBrush(Color.FromArgb(230, 40, 40, 40)), // small rectangles color
Fill = new SolidColorBrush(Color.FromArgb(1, 255, 255, 255)),
Cursor = crs,
Name = name,
};
rect.MouseDown += (sender, args) => _resizeStart(sender, args);
return rect;
}
public CanvasLabel Info => new(AnnotationClass.Id, Canvas.GetLeft(this), Canvas.GetTop(this), Width, Height);
}
+363
View File
@@ -0,0 +1,363 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using Azaion.Annotator.DTO;
using Azaion.Common.DTO;
using MediatR;
using Color = System.Windows.Media.Color;
using Rectangle = System.Windows.Shapes.Rectangle;
namespace Azaion.Common.Controls;
public class CanvasEditor : Canvas
{
private Point _lastPos;
public SelectionState SelectionState { get; set; } = SelectionState.None;
private readonly Rectangle _newAnnotationRect;
private Point _newAnnotationStartPos;
private readonly Line _horizontalLine;
private readonly Line _verticalLine;
private readonly TextBlock _classNameHint;
private Rectangle _curRec = new();
private AnnotationControl _curAnn = null!;
private const int MIN_SIZE = 20;
private readonly TimeSpan _viewThreshold = TimeSpan.FromMilliseconds(400);
public IMediator Mediator { get; set; } = null!;
public static readonly DependencyProperty GetTimeFuncProp =
DependencyProperty.Register(
nameof(GetTimeFunc),
typeof(Func<TimeSpan?>),
typeof(CanvasEditor),
new PropertyMetadata(null));
public Func<TimeSpan?> GetTimeFunc
{
get => (Func<TimeSpan?>)GetValue(GetTimeFuncProp);
set => SetValue(GetTimeFuncProp, value);
}
private AnnotationClass _currentAnnClass = null!;
public AnnotationClass CurrentAnnClass
{
get => _currentAnnClass;
set
{
_verticalLine.Stroke = value.ColorBrush;
_verticalLine.Fill = value.ColorBrush;
_horizontalLine.Stroke = value.ColorBrush;
_horizontalLine.Fill = value.ColorBrush;
_classNameHint.Text = value.Name;
_newAnnotationRect.Stroke = value.ColorBrush;
_newAnnotationRect.Fill = value.ColorBrush;
_currentAnnClass = value;
}
}
public readonly List<AnnotationControl> CurrentAnns = new();
public CanvasEditor()
{
_horizontalLine = new Line
{
HorizontalAlignment = HorizontalAlignment.Stretch,
Stroke = new SolidColorBrush(Colors.Blue),
Fill = new SolidColorBrush(Colors.Blue),
StrokeDashArray = [5],
StrokeThickness = 2
};
_verticalLine = new Line
{
VerticalAlignment = VerticalAlignment.Stretch,
Stroke = new SolidColorBrush(Colors.Blue),
Fill = new SolidColorBrush(Colors.Blue),
StrokeDashArray = [5],
StrokeThickness = 2
};
_classNameHint = new TextBlock
{
Text = CurrentAnnClass?.Name ?? "asd",
Foreground = new SolidColorBrush(Colors.Black),
Cursor = Cursors.Arrow,
FontSize = 16,
FontWeight = FontWeights.Bold
};
_newAnnotationRect = new Rectangle
{
Name = "selector",
Height = 0,
Width = 0,
Stroke = new SolidColorBrush(Colors.Gray),
Fill = new SolidColorBrush(Color.FromArgb(128, 128, 128, 128)),
};
KeyDown += (_, args) =>
{
Console.WriteLine($"pressed {args.Key}");
};
MouseDown += CanvasMouseDown;
MouseMove += CanvasMouseMove;
MouseUp += CanvasMouseUp;
SizeChanged += CanvasResized;
Cursor = Cursors.Cross;
Children.Add(_newAnnotationRect);
Children.Add(_horizontalLine);
Children.Add(_verticalLine);
Children.Add(_classNameHint);
Loaded += Init;
}
private void Init(object sender, RoutedEventArgs e)
{
_horizontalLine.X1 = 0;
_horizontalLine.X2 = ActualWidth;
_verticalLine.Y1 = 0;
_verticalLine.Y2 = ActualHeight;
}
private void CanvasMouseDown(object sender, MouseButtonEventArgs e)
{
ClearSelections();
NewAnnotationStart(sender, e);
}
private void CanvasMouseMove(object sender, MouseEventArgs e)
{
var pos = e.GetPosition(this);
_horizontalLine.Y1 = _horizontalLine.Y2 = pos.Y;
_verticalLine.X1 = _verticalLine.X2 = pos.X;
SetLeft(_classNameHint, pos.X + 10);
SetTop(_classNameHint, pos.Y - 30);
if (e.LeftButton != MouseButtonState.Pressed)
return;
if (SelectionState == SelectionState.NewAnnCreating)
NewAnnotationCreatingMove(sender, e);
if (SelectionState == SelectionState.AnnResizing)
AnnotationResizeMove(sender, e);
if (SelectionState == SelectionState.AnnMoving)
AnnotationPositionMove(sender, e);
}
private void CanvasMouseUp(object sender, MouseButtonEventArgs e)
{
if (SelectionState == SelectionState.NewAnnCreating)
CreateAnnotation(e.GetPosition(this));
SelectionState = SelectionState.None;
e.Handled = true;
}
private void CanvasResized(object sender, SizeChangedEventArgs e)
{
_horizontalLine.X2 = e.NewSize.Width;
_verticalLine.Y2 = e.NewSize.Height;
}
#region Annotation Resizing & Moving
private void AnnotationResizeStart(object sender, MouseEventArgs e)
{
SelectionState = SelectionState.AnnResizing;
_lastPos = e.GetPosition(this);
_curRec = (Rectangle)sender;
_curAnn = (AnnotationControl)((Grid)_curRec.Parent).Parent;
e.Handled = true;
}
private void AnnotationResizeMove(object sender, MouseEventArgs e)
{
if (SelectionState != SelectionState.AnnResizing)
return;
var currentPos = e.GetPosition(this);
var x = GetLeft(_curAnn);
var y = GetTop(_curAnn);
var offsetX = currentPos.X - _lastPos.X;
var offsetY = currentPos.Y - _lastPos.Y;
switch (_curRec.HorizontalAlignment, _curRec.VerticalAlignment)
{
case (HorizontalAlignment.Left, VerticalAlignment.Top):
_curAnn.Width = Math.Max(MIN_SIZE, _curAnn.Width - offsetX);
_curAnn.Height = Math.Max(MIN_SIZE, _curAnn.Height - offsetY);
SetLeft(_curAnn, x + offsetX);
SetTop(_curAnn, y + offsetY);
break;
case (HorizontalAlignment.Center, VerticalAlignment.Top):
_curAnn.Height = Math.Max(MIN_SIZE, _curAnn.Height - offsetY);
SetTop(_curAnn, y + offsetY);
break;
case (HorizontalAlignment.Right, VerticalAlignment.Top):
_curAnn.Width = Math.Max(MIN_SIZE, _curAnn.Width + offsetX);
_curAnn.Height = Math.Max(MIN_SIZE, _curAnn.Height - offsetY);
SetTop(_curAnn, y + offsetY);
break;
case (HorizontalAlignment.Left, VerticalAlignment.Center):
_curAnn.Width = Math.Max(MIN_SIZE, _curAnn.Width - offsetX);
SetLeft(_curAnn, x + offsetX);
break;
case (HorizontalAlignment.Right, VerticalAlignment.Center):
_curAnn.Width = Math.Max(MIN_SIZE, _curAnn.Width + offsetX);
break;
case (HorizontalAlignment.Left, VerticalAlignment.Bottom):
_curAnn.Width = Math.Max(MIN_SIZE, _curAnn.Width - offsetX);
_curAnn.Height = Math.Max(MIN_SIZE, _curAnn.Height + offsetY);
SetLeft(_curAnn, x + offsetX);
break;
case (HorizontalAlignment.Center, VerticalAlignment.Bottom):
_curAnn.Height = Math.Max(MIN_SIZE, _curAnn.Height + offsetY);
break;
case (HorizontalAlignment.Right, VerticalAlignment.Bottom):
_curAnn.Width = Math.Max(MIN_SIZE, _curAnn.Width + offsetX);
_curAnn.Height = Math.Max(MIN_SIZE, _curAnn.Height + offsetY);
break;
}
_lastPos = currentPos;
}
private void AnnotationPositionStart(object sender, MouseEventArgs e)
{
_lastPos = e.GetPosition(this);
_curAnn = (AnnotationControl)sender;
if (!Keyboard.IsKeyDown(Key.LeftCtrl) && !Keyboard.IsKeyDown(Key.RightCtrl))
ClearSelections();
_curAnn.IsSelected = true;
SelectionState = SelectionState.AnnMoving;
e.Handled = true;
}
private void AnnotationPositionMove(object sender, MouseEventArgs e)
{
if (SelectionState != SelectionState.AnnMoving)
return;
var currentPos = e.GetPosition(this);
var offsetX = currentPos.X - _lastPos.X;
var offsetY = currentPos.Y - _lastPos.Y;
SetLeft(_curAnn, GetLeft(_curAnn) + offsetX);
SetTop(_curAnn, GetTop(_curAnn) + offsetY);
_lastPos = currentPos;
e.Handled = true;
}
#endregion
#region NewAnnotation
private void NewAnnotationStart(object sender, MouseButtonEventArgs e)
{
_newAnnotationStartPos = e.GetPosition(this);
SetLeft(_newAnnotationRect, _newAnnotationStartPos.X);
SetTop(_newAnnotationRect, _newAnnotationStartPos.Y);
_newAnnotationRect.MouseMove += NewAnnotationCreatingMove;
SelectionState = SelectionState.NewAnnCreating;
}
private void NewAnnotationCreatingMove(object sender, MouseEventArgs e)
{
if (SelectionState != SelectionState.NewAnnCreating)
return;
var currentPos = e.GetPosition(this);
var diff = currentPos - _newAnnotationStartPos;
_newAnnotationRect.Height = Math.Abs(diff.Y);
_newAnnotationRect.Width = Math.Abs(diff.X);
if (diff.X < 0)
SetLeft(_newAnnotationRect, currentPos.X);
if (diff.Y < 0)
SetTop(_newAnnotationRect, currentPos.Y);
}
private void CreateAnnotation(Point endPos)
{
_newAnnotationRect.Width = 0;
_newAnnotationRect.Height = 0;
var width = Math.Abs(endPos.X - _newAnnotationStartPos.X);
var height = Math.Abs(endPos.Y - _newAnnotationStartPos.Y);
if (width < MIN_SIZE || height < MIN_SIZE)
return;
var time = GetTimeFunc();
CreateAnnotation(CurrentAnnClass, time, new CanvasLabel
{
Width = width,
Height = height,
X = Math.Min(endPos.X, _newAnnotationStartPos.X),
Y = Math.Min(endPos.Y, _newAnnotationStartPos.Y)
});
}
public AnnotationControl CreateAnnotation(AnnotationClass annClass, TimeSpan? time, CanvasLabel canvasLabel)
{
var annotationControl = new AnnotationControl(annClass, time, AnnotationResizeStart, canvasLabel.Probability)
{
Width = canvasLabel.Width,
Height = canvasLabel.Height
};
annotationControl.MouseDown += AnnotationPositionStart;
SetLeft(annotationControl, canvasLabel.X );
SetTop(annotationControl, canvasLabel.Y);
Children.Add(annotationControl);
CurrentAnns.Add(annotationControl);
_newAnnotationRect.Fill = new SolidColorBrush(annClass.Color);
return annotationControl;
}
#endregion
private void RemoveAnnotations(IEnumerable<AnnotationControl> listToRemove)
{
foreach (var ann in listToRemove)
{
Children.Remove(ann);
CurrentAnns.Remove(ann);
}
}
public void RemoveAllAnns()
{
foreach (var ann in CurrentAnns)
Children.Remove(ann);
CurrentAnns.Clear();
}
public void RemoveSelectedAnns() => RemoveAnnotations(CurrentAnns.Where(x => x.IsSelected).ToList());
private void ClearSelections()
{
foreach (var ann in CurrentAnns)
ann.IsSelected = false;
}
public void ClearExpiredAnnotations(TimeSpan time)
{
var expiredAnns = CurrentAnns.Where(x =>
x.Time.HasValue &&
Math.Abs((time - x.Time.Value).TotalMilliseconds) > _viewThreshold.TotalMilliseconds)
.ToList();
RemoveAnnotations(expiredAnns);
}
}
@@ -0,0 +1,37 @@
using System.Windows.Controls;
using System.Windows.Input;
namespace Azaion.Annotator.Controls
{
public class UpdatableProgressBar : ProgressBar
{
public delegate void ValueChange(double oldValue, double newValue);
public new event ValueChange? ValueChanged;
public UpdatableProgressBar() : base()
{
MouseDown += OnMouseDown;
MouseMove += OnMouseMove;
}
private double SetProgressBarValue(double mousePos)
{
Value = Minimum;
var pbValue = mousePos / ActualWidth * Maximum;
ValueChanged?.Invoke(Value, pbValue);
return pbValue;
}
private void OnMouseDown(object sender, MouseButtonEventArgs e)
{
Value = SetProgressBarValue(e.GetPosition(this).X);
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
Value = SetProgressBarValue(e.GetPosition(this).X);
}
}
}