forked from sim0n00ps/OF-DL
225 lines
6.4 KiB
C#
225 lines
6.4 KiB
C#
using System.Collections;
|
|
using System.Collections.Specialized;
|
|
using System.Linq;
|
|
using Avalonia;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Controls.Documents;
|
|
using Avalonia.Controls.Primitives;
|
|
using Avalonia.Media;
|
|
using Avalonia.VisualTree;
|
|
using OF_DL.Gui.ViewModels;
|
|
|
|
namespace OF_DL.Gui.Controls;
|
|
|
|
public class FileNameFormatOverlayTextBlock : TextBlock
|
|
{
|
|
public static readonly StyledProperty<IEnumerable<FileNameFormatSegmentViewModel>?> SegmentsProperty =
|
|
AvaloniaProperty.Register<FileNameFormatOverlayTextBlock, IEnumerable<FileNameFormatSegmentViewModel>?>(nameof(Segments));
|
|
|
|
public static readonly StyledProperty<TextBox?> SourceTextBoxProperty =
|
|
AvaloniaProperty.Register<FileNameFormatOverlayTextBlock, TextBox?>(nameof(SourceTextBox));
|
|
|
|
private INotifyCollectionChanged? _segmentsCollection;
|
|
private TextBox? _attachedTextBox;
|
|
private ScrollViewer? _attachedScrollViewer;
|
|
|
|
static FileNameFormatOverlayTextBlock()
|
|
{
|
|
SegmentsProperty.Changed.AddClassHandler<FileNameFormatOverlayTextBlock>(OnSegmentsChanged);
|
|
SourceTextBoxProperty.Changed.AddClassHandler<FileNameFormatOverlayTextBlock>(OnSourceTextBoxChanged);
|
|
}
|
|
|
|
public IEnumerable<FileNameFormatSegmentViewModel>? Segments
|
|
{
|
|
get => GetValue(SegmentsProperty);
|
|
set => SetValue(SegmentsProperty, value);
|
|
}
|
|
|
|
public TextBox? SourceTextBox
|
|
{
|
|
get => GetValue(SourceTextBoxProperty);
|
|
set => SetValue(SourceTextBoxProperty, value);
|
|
}
|
|
|
|
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
|
{
|
|
base.OnAttachedToVisualTree(e);
|
|
DetachSegmentsCollection();
|
|
AttachSegmentsCollection(Segments);
|
|
AttachSourceTextBox(SourceTextBox);
|
|
RebuildInlines();
|
|
}
|
|
|
|
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
|
{
|
|
DetachSourceTextBox();
|
|
DetachSegmentsCollection();
|
|
base.OnDetachedFromVisualTree(e);
|
|
}
|
|
|
|
private static void OnSegmentsChanged(
|
|
FileNameFormatOverlayTextBlock sender,
|
|
AvaloniaPropertyChangedEventArgs e)
|
|
{
|
|
sender.DetachSegmentsCollection();
|
|
sender.AttachSegmentsCollection(e.NewValue as IEnumerable<FileNameFormatSegmentViewModel>);
|
|
sender.RebuildInlines();
|
|
}
|
|
|
|
private static void OnSourceTextBoxChanged(
|
|
FileNameFormatOverlayTextBlock sender,
|
|
AvaloniaPropertyChangedEventArgs e)
|
|
{
|
|
sender.AttachSourceTextBox(e.NewValue as TextBox);
|
|
}
|
|
|
|
private void AttachSegmentsCollection(IEnumerable<FileNameFormatSegmentViewModel>? segments)
|
|
{
|
|
_segmentsCollection = segments as INotifyCollectionChanged;
|
|
if (_segmentsCollection is not null)
|
|
{
|
|
_segmentsCollection.CollectionChanged += OnSegmentsCollectionChanged;
|
|
}
|
|
}
|
|
|
|
private void DetachSegmentsCollection()
|
|
{
|
|
if (_segmentsCollection is null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_segmentsCollection.CollectionChanged -= OnSegmentsCollectionChanged;
|
|
_segmentsCollection = null;
|
|
}
|
|
|
|
private void OnSegmentsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
|
{
|
|
RebuildInlines();
|
|
}
|
|
|
|
private void AttachSourceTextBox(TextBox? textBox)
|
|
{
|
|
if (ReferenceEquals(_attachedTextBox, textBox))
|
|
{
|
|
UpdateOverlayOffset();
|
|
return;
|
|
}
|
|
|
|
DetachSourceTextBox();
|
|
_attachedTextBox = textBox;
|
|
if (_attachedTextBox is null)
|
|
{
|
|
UpdateOverlayOffset();
|
|
return;
|
|
}
|
|
|
|
_attachedTextBox.TemplateApplied += OnSourceTextBoxTemplateApplied;
|
|
AttachScrollViewer(FindSourceScrollViewer(_attachedTextBox));
|
|
UpdateOverlayOffset();
|
|
}
|
|
|
|
private void DetachSourceTextBox()
|
|
{
|
|
if (_attachedTextBox is not null)
|
|
{
|
|
_attachedTextBox.TemplateApplied -= OnSourceTextBoxTemplateApplied;
|
|
}
|
|
|
|
if (_attachedScrollViewer is not null)
|
|
{
|
|
_attachedScrollViewer.ScrollChanged -= OnSourceScrollChanged;
|
|
_attachedScrollViewer = null;
|
|
}
|
|
|
|
_attachedTextBox = null;
|
|
UpdateOverlayOffset();
|
|
}
|
|
|
|
private void OnSourceTextBoxTemplateApplied(object? sender, TemplateAppliedEventArgs e)
|
|
{
|
|
ScrollViewer? scrollViewer = e.NameScope.Find<ScrollViewer>("PART_ScrollViewer");
|
|
AttachScrollViewer(scrollViewer ?? FindSourceScrollViewer(_attachedTextBox));
|
|
UpdateOverlayOffset();
|
|
}
|
|
|
|
private void OnSourceScrollChanged(object? sender, ScrollChangedEventArgs e)
|
|
{
|
|
UpdateOverlayOffset();
|
|
}
|
|
|
|
private void AttachScrollViewer(ScrollViewer? scrollViewer)
|
|
{
|
|
if (ReferenceEquals(_attachedScrollViewer, scrollViewer))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (_attachedScrollViewer is not null)
|
|
{
|
|
_attachedScrollViewer.ScrollChanged -= OnSourceScrollChanged;
|
|
}
|
|
|
|
_attachedScrollViewer = scrollViewer;
|
|
if (_attachedScrollViewer is not null)
|
|
{
|
|
_attachedScrollViewer.ScrollChanged += OnSourceScrollChanged;
|
|
}
|
|
}
|
|
|
|
private void UpdateOverlayOffset()
|
|
{
|
|
if (_attachedScrollViewer is null)
|
|
{
|
|
RenderTransform = null;
|
|
return;
|
|
}
|
|
|
|
RenderTransform = new TranslateTransform(-_attachedScrollViewer.Offset.X, -_attachedScrollViewer.Offset.Y);
|
|
}
|
|
|
|
private static ScrollViewer? FindSourceScrollViewer(TextBox? textBox)
|
|
{
|
|
if (textBox is null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return textBox.GetVisualDescendants().OfType<ScrollViewer>().FirstOrDefault();
|
|
}
|
|
|
|
private void RebuildInlines()
|
|
{
|
|
if (Inlines is null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Inlines.Clear();
|
|
if (Segments is null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (FileNameFormatSegmentViewModel segment in Segments)
|
|
{
|
|
Run run = new()
|
|
{
|
|
Text = segment.Text ?? string.Empty,
|
|
Foreground = ParseForegroundBrush(segment.Foreground)
|
|
};
|
|
Inlines.Add(run);
|
|
}
|
|
}
|
|
|
|
private IBrush ParseForegroundBrush(string? value)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(value) && Color.TryParse(value, out Color color))
|
|
{
|
|
return new SolidColorBrush(color);
|
|
}
|
|
|
|
return Foreground ?? Brushes.Transparent;
|
|
}
|
|
}
|