Improve the filename format input field's appearance and validation
This commit is contained in:
parent
b6872a2b9e
commit
e58ac7d2a6
224
OF DL.Gui/Controls/FileNameFormatOverlayTextBlock.cs
Normal file
224
OF DL.Gui/Controls/FileNameFormatOverlayTextBlock.cs
Normal file
@ -0,0 +1,224 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -140,7 +140,8 @@ public partial class ConfigFieldViewModel : ViewModelBase
|
||||
public bool IsTimeoutField =>
|
||||
string.Equals(PropertyName, nameof(Config.Timeout), StringComparison.Ordinal);
|
||||
|
||||
public bool IsRegularTextInput => IsTextInput && !IsIgnoredUsersListField && !IsCreatorConfigsField;
|
||||
public bool IsRegularTextInput =>
|
||||
IsTextInput && !IsIgnoredUsersListField && !IsCreatorConfigsField && !IsFileNameFormatField;
|
||||
|
||||
public bool HasHelpText => !string.IsNullOrWhiteSpace(HelpText);
|
||||
|
||||
@ -167,6 +168,8 @@ public partial class ConfigFieldViewModel : ViewModelBase
|
||||
[ObservableProperty]
|
||||
private string _textValue = string.Empty;
|
||||
|
||||
private bool _isNormalizingFileNameFormatInput;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyCanExecuteChangedFor(nameof(InsertSelectedFileNameVariableCommand))]
|
||||
private string? _selectedFileNameVariable;
|
||||
@ -364,6 +367,18 @@ public partial class ConfigFieldViewModel : ViewModelBase
|
||||
|
||||
partial void OnTextValueChanged(string value)
|
||||
{
|
||||
if (IsFileNameFormatField && !_isNormalizingFileNameFormatInput)
|
||||
{
|
||||
string trimmedValue = value.Trim();
|
||||
if (!string.Equals(value, trimmedValue, StringComparison.Ordinal))
|
||||
{
|
||||
_isNormalizingFileNameFormatInput = true;
|
||||
TextValue = trimmedValue;
|
||||
_isNormalizingFileNameFormatInput = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Store actual value if not the privacy placeholder
|
||||
if (value != "[Hidden for Privacy]")
|
||||
{
|
||||
|
||||
@ -18,12 +18,16 @@ public partial class CreatorConfigModalViewModel : ViewModelBase
|
||||
|
||||
private readonly Action<bool> _onClose;
|
||||
private readonly Func<bool> _isUsernameDuplicate;
|
||||
private bool _isNormalizingUsername;
|
||||
private bool _isNormalizingFileNameFormat;
|
||||
|
||||
[ObservableProperty] private bool _isOpen;
|
||||
[ObservableProperty] private bool _isEditMode;
|
||||
[ObservableProperty] private string _originalUsername = string.Empty;
|
||||
[ObservableProperty] private string _username = string.Empty;
|
||||
[ObservableProperty] private string _usernameError = string.Empty;
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(HasUsernameError))]
|
||||
private string _usernameError = string.Empty;
|
||||
[ObservableProperty] private string _paidPostFileNameFormat = string.Empty;
|
||||
[ObservableProperty] private string _postFileNameFormat = string.Empty;
|
||||
[ObservableProperty] private string _paidMessageFileNameFormat = string.Empty;
|
||||
@ -32,10 +36,37 @@ public partial class CreatorConfigModalViewModel : ViewModelBase
|
||||
[ObservableProperty] private string _selectedPostVariable = string.Empty;
|
||||
[ObservableProperty] private string _selectedPaidMessageVariable = string.Empty;
|
||||
[ObservableProperty] private string _selectedMessageVariable = string.Empty;
|
||||
[ObservableProperty] private string _unknownPaidPostVariablesMessage = string.Empty;
|
||||
[ObservableProperty] private string _unknownPostVariablesMessage = string.Empty;
|
||||
[ObservableProperty] private string _unknownPaidMessageVariablesMessage = string.Empty;
|
||||
[ObservableProperty] private string _unknownMessageVariablesMessage = string.Empty;
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(HasUnknownPaidPostVariables))]
|
||||
private string _unknownPaidPostVariablesMessage = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(HasUnknownPostVariables))]
|
||||
private string _unknownPostVariablesMessage = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(HasUnknownPaidMessageVariables))]
|
||||
private string _unknownPaidMessageVariablesMessage = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(HasUnknownMessageVariables))]
|
||||
private string _unknownMessageVariablesMessage = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(HasPaidPostFileNameFormatError))]
|
||||
private string _paidPostFileNameFormatError = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(HasPostFileNameFormatError))]
|
||||
private string _postFileNameFormatError = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(HasPaidMessageFileNameFormatError))]
|
||||
private string _paidMessageFileNameFormatError = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(HasMessageFileNameFormatError))]
|
||||
private string _messageFileNameFormatError = string.Empty;
|
||||
|
||||
public ObservableCollection<string> AvailableUsers { get; }
|
||||
public ObservableCollection<string> PaidPostVariables { get; } = [];
|
||||
@ -52,6 +83,10 @@ public partial class CreatorConfigModalViewModel : ViewModelBase
|
||||
public bool HasUnknownPostVariables => !string.IsNullOrWhiteSpace(UnknownPostVariablesMessage);
|
||||
public bool HasUnknownPaidMessageVariables => !string.IsNullOrWhiteSpace(UnknownPaidMessageVariablesMessage);
|
||||
public bool HasUnknownMessageVariables => !string.IsNullOrWhiteSpace(UnknownMessageVariablesMessage);
|
||||
public bool HasPaidPostFileNameFormatError => !string.IsNullOrWhiteSpace(PaidPostFileNameFormatError);
|
||||
public bool HasPostFileNameFormatError => !string.IsNullOrWhiteSpace(PostFileNameFormatError);
|
||||
public bool HasPaidMessageFileNameFormatError => !string.IsNullOrWhiteSpace(PaidMessageFileNameFormatError);
|
||||
public bool HasMessageFileNameFormatError => !string.IsNullOrWhiteSpace(MessageFileNameFormatError);
|
||||
|
||||
public string DialogTitle => IsEditMode ? "Edit Creator Config" : "Add Creator Config";
|
||||
|
||||
@ -85,11 +120,11 @@ public partial class CreatorConfigModalViewModel : ViewModelBase
|
||||
IsEditMode = false;
|
||||
OriginalUsername = string.Empty;
|
||||
Username = string.Empty;
|
||||
UsernameError = string.Empty;
|
||||
PaidPostFileNameFormat = string.Empty;
|
||||
PostFileNameFormat = string.Empty;
|
||||
PaidMessageFileNameFormat = string.Empty;
|
||||
MessageFileNameFormat = string.Empty;
|
||||
ClearValidationErrors();
|
||||
ClearAllPreviews();
|
||||
Log.Information("About to set IsOpen = true");
|
||||
IsOpen = true;
|
||||
@ -101,11 +136,11 @@ public partial class CreatorConfigModalViewModel : ViewModelBase
|
||||
IsEditMode = true;
|
||||
OriginalUsername = username;
|
||||
Username = username;
|
||||
UsernameError = string.Empty;
|
||||
PaidPostFileNameFormat = config.PaidPostFileNameFormat ?? string.Empty;
|
||||
PostFileNameFormat = config.PostFileNameFormat ?? string.Empty;
|
||||
PaidMessageFileNameFormat = config.PaidMessageFileNameFormat ?? string.Empty;
|
||||
MessageFileNameFormat = config.MessageFileNameFormat ?? string.Empty;
|
||||
ClearValidationErrors();
|
||||
UpdateAllPreviews();
|
||||
IsOpen = true;
|
||||
}
|
||||
@ -184,32 +219,148 @@ public partial class CreatorConfigModalViewModel : ViewModelBase
|
||||
Log.Information("*** IsOpen property changed to: {Value} ***", value);
|
||||
}
|
||||
|
||||
partial void OnPaidPostFileNameFormatChanged(string value) => UpdatePaidPostPreview();
|
||||
partial void OnPostFileNameFormatChanged(string value) => UpdatePostPreview();
|
||||
partial void OnPaidMessageFileNameFormatChanged(string value) => UpdatePaidMessagePreview();
|
||||
partial void OnMessageFileNameFormatChanged(string value) => UpdateMessagePreview();
|
||||
partial void OnUsernameChanged(string value)
|
||||
{
|
||||
if (_isNormalizingUsername)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string trimmed = value.Trim();
|
||||
if (!string.Equals(value, trimmed, StringComparison.Ordinal))
|
||||
{
|
||||
_isNormalizingUsername = true;
|
||||
Username = trimmed;
|
||||
_isNormalizingUsername = false;
|
||||
}
|
||||
|
||||
UsernameError = string.Empty;
|
||||
}
|
||||
|
||||
partial void OnPaidPostFileNameFormatChanged(string value) =>
|
||||
NormalizeFileNameFormat(
|
||||
value,
|
||||
trimmed => PaidPostFileNameFormat = trimmed,
|
||||
() => PaidPostFileNameFormatError = string.Empty,
|
||||
UpdatePaidPostPreview);
|
||||
|
||||
partial void OnPostFileNameFormatChanged(string value) =>
|
||||
NormalizeFileNameFormat(
|
||||
value,
|
||||
trimmed => PostFileNameFormat = trimmed,
|
||||
() => PostFileNameFormatError = string.Empty,
|
||||
UpdatePostPreview);
|
||||
|
||||
partial void OnPaidMessageFileNameFormatChanged(string value) =>
|
||||
NormalizeFileNameFormat(
|
||||
value,
|
||||
trimmed => PaidMessageFileNameFormat = trimmed,
|
||||
() => PaidMessageFileNameFormatError = string.Empty,
|
||||
UpdatePaidMessagePreview);
|
||||
|
||||
partial void OnMessageFileNameFormatChanged(string value) =>
|
||||
NormalizeFileNameFormat(
|
||||
value,
|
||||
trimmed => MessageFileNameFormat = trimmed,
|
||||
() => MessageFileNameFormatError = string.Empty,
|
||||
UpdateMessagePreview);
|
||||
|
||||
private bool Validate()
|
||||
{
|
||||
UsernameError = string.Empty;
|
||||
ClearValidationErrors();
|
||||
TrimInputValues();
|
||||
|
||||
bool isValid = true;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Username))
|
||||
{
|
||||
UsernameError = "Username is required.";
|
||||
return false;
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
string trimmedUsername = Username.Trim();
|
||||
if (!IsEditMode || trimmedUsername != OriginalUsername)
|
||||
if (isValid && (!IsEditMode || trimmedUsername != OriginalUsername))
|
||||
{
|
||||
if (_isUsernameDuplicate())
|
||||
{
|
||||
UsernameError = "A config for this username already exists.";
|
||||
return false;
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
ValidateFileNameFormatUniqueness(PaidPostFileNameFormat, message => PaidPostFileNameFormatError = message,
|
||||
ref isValid);
|
||||
ValidateFileNameFormatUniqueness(PostFileNameFormat, message => PostFileNameFormatError = message,
|
||||
ref isValid);
|
||||
ValidateFileNameFormatUniqueness(PaidMessageFileNameFormat, message => PaidMessageFileNameFormatError = message,
|
||||
ref isValid);
|
||||
ValidateFileNameFormatUniqueness(MessageFileNameFormat, message => MessageFileNameFormatError = message,
|
||||
ref isValid);
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private void ClearValidationErrors()
|
||||
{
|
||||
UsernameError = string.Empty;
|
||||
PaidPostFileNameFormatError = string.Empty;
|
||||
PostFileNameFormatError = string.Empty;
|
||||
PaidMessageFileNameFormatError = string.Empty;
|
||||
MessageFileNameFormatError = string.Empty;
|
||||
}
|
||||
|
||||
private void TrimInputValues()
|
||||
{
|
||||
Username = Username.Trim();
|
||||
PaidPostFileNameFormat = PaidPostFileNameFormat.Trim();
|
||||
PostFileNameFormat = PostFileNameFormat.Trim();
|
||||
PaidMessageFileNameFormat = PaidMessageFileNameFormat.Trim();
|
||||
MessageFileNameFormat = MessageFileNameFormat.Trim();
|
||||
}
|
||||
|
||||
private static void ValidateFileNameFormatUniqueness(
|
||||
string format,
|
||||
Action<string> setError,
|
||||
ref bool isValid)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(format))
|
||||
{
|
||||
setError(string.Empty);
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasUniqueToken = format.Contains("{mediaId}", StringComparison.OrdinalIgnoreCase) ||
|
||||
format.Contains("{filename}", StringComparison.OrdinalIgnoreCase);
|
||||
if (hasUniqueToken)
|
||||
{
|
||||
setError(string.Empty);
|
||||
return;
|
||||
}
|
||||
|
||||
setError("Format must include {mediaId} or {filename} to avoid file collisions.");
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
private void NormalizeFileNameFormat(
|
||||
string value,
|
||||
Action<string> setValue,
|
||||
Action clearError,
|
||||
Action updatePreview)
|
||||
{
|
||||
if (!_isNormalizingFileNameFormat)
|
||||
{
|
||||
string trimmedValue = value.Trim();
|
||||
if (!string.Equals(value, trimmedValue, StringComparison.Ordinal))
|
||||
{
|
||||
_isNormalizingFileNameFormat = true;
|
||||
setValue(trimmedValue);
|
||||
_isNormalizingFileNameFormat = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
clearError();
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
private void ClearAllPreviews()
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="using:OF_DL.Gui.Controls"
|
||||
xmlns:vm="using:OF_DL.Gui.ViewModels"
|
||||
xmlns:views="using:OF_DL.Gui.Views"
|
||||
x:Class="OF_DL.Gui.Views.MainWindow"
|
||||
@ -38,6 +39,10 @@
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="TextBox.fileNameOverlayInput:not(:empty)">
|
||||
<Setter Property="Foreground" Value="Transparent" />
|
||||
</Style>
|
||||
</Window.Styles>
|
||||
|
||||
<Grid RowDefinitions="Auto,Auto,*">
|
||||
@ -646,6 +651,26 @@
|
||||
<StackPanel
|
||||
IsVisible="{Binding IsFileNameFormatField}"
|
||||
Spacing="6">
|
||||
<Grid ClipToBounds="True">
|
||||
<TextBox
|
||||
x:Name="ConfigFileNameFormatTextBox"
|
||||
Classes="fileNameOverlayInput"
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding TextValue}"
|
||||
Padding="8,6"
|
||||
CaretBrush="{DynamicResource TextPrimaryBrush}" />
|
||||
<controls:FileNameFormatOverlayTextBlock
|
||||
IsHitTestVisible="False"
|
||||
Margin="{Binding #ConfigFileNameFormatTextBox.Padding}"
|
||||
VerticalAlignment="Center"
|
||||
Segments="{Binding FileNameFormatSegments}"
|
||||
SourceTextBox="{Binding #ConfigFileNameFormatTextBox}"
|
||||
FontFamily="{Binding #ConfigFileNameFormatTextBox.FontFamily}"
|
||||
FontSize="{Binding #ConfigFileNameFormatTextBox.FontSize}"
|
||||
FontWeight="{Binding #ConfigFileNameFormatTextBox.FontWeight}"
|
||||
FontStyle="{Binding #ConfigFileNameFormatTextBox.FontStyle}" />
|
||||
</Grid>
|
||||
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<ComboBox Grid.Column="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
@ -658,32 +683,6 @@
|
||||
Command="{Binding InsertSelectedFileNameVariableCommand}" />
|
||||
</Grid>
|
||||
|
||||
<Border Padding="8"
|
||||
Background="{DynamicResource PreviewBackgroundBrush}"
|
||||
BorderBrush="{DynamicResource PreviewBorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8">
|
||||
<ItemsControl
|
||||
ItemsSource="{Binding FileNameFormatSegments}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel
|
||||
Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate
|
||||
x:DataType="vm:FileNameFormatSegmentViewModel">
|
||||
<TextBlock
|
||||
Text="{Binding Text}"
|
||||
Foreground="{Binding Foreground}"
|
||||
FontFamily="Consolas"
|
||||
TextWrapping="Wrap" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Border>
|
||||
|
||||
<TextBlock
|
||||
IsVisible="{Binding HasUnknownFileNameVariables}"
|
||||
Foreground="{DynamicResource ErrorTextBrush}"
|
||||
@ -992,8 +991,23 @@
|
||||
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock FontWeight="SemiBold" Foreground="{DynamicResource TextPrimaryBrush}" Text="Paid Post Filename Format" />
|
||||
<TextBox Text="{Binding PaidPostFileNameFormat}"
|
||||
Watermark="Optional: override global paid post format" />
|
||||
<Grid ClipToBounds="True">
|
||||
<TextBox x:Name="PaidPostFileNameFormatTextBox"
|
||||
Classes="fileNameOverlayInput"
|
||||
Text="{Binding PaidPostFileNameFormat}"
|
||||
Watermark="Optional: override global paid post format"
|
||||
Padding="8,6"
|
||||
CaretBrush="{DynamicResource TextPrimaryBrush}" />
|
||||
<controls:FileNameFormatOverlayTextBlock IsHitTestVisible="False"
|
||||
Margin="{Binding #PaidPostFileNameFormatTextBox.Padding}"
|
||||
VerticalAlignment="Center"
|
||||
Segments="{Binding PaidPostSegments}"
|
||||
SourceTextBox="{Binding #PaidPostFileNameFormatTextBox}"
|
||||
FontFamily="{Binding #PaidPostFileNameFormatTextBox.FontFamily}"
|
||||
FontSize="{Binding #PaidPostFileNameFormatTextBox.FontSize}"
|
||||
FontWeight="{Binding #PaidPostFileNameFormatTextBox.FontWeight}"
|
||||
FontStyle="{Binding #PaidPostFileNameFormatTextBox.FontStyle}" />
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<ComboBox Grid.Column="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
@ -1005,37 +1019,35 @@
|
||||
Content="Insert"
|
||||
Command="{Binding InsertPaidPostVariableCommand}" />
|
||||
</Grid>
|
||||
<Border Padding="8"
|
||||
Background="{DynamicResource PreviewBackgroundBrush}"
|
||||
BorderBrush="{DynamicResource PreviewBorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8">
|
||||
<ItemsControl ItemsSource="{Binding PaidPostSegments}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:FileNameFormatSegmentViewModel">
|
||||
<TextBlock Text="{Binding Text}"
|
||||
Foreground="{Binding Foreground}"
|
||||
FontFamily="Consolas"
|
||||
TextWrapping="Wrap" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Border>
|
||||
<TextBlock IsVisible="{Binding HasUnknownPaidPostVariables}"
|
||||
Foreground="{DynamicResource ErrorTextBrush}"
|
||||
Text="{Binding UnknownPaidPostVariablesMessage}"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock IsVisible="{Binding HasPaidPostFileNameFormatError}"
|
||||
Foreground="{DynamicResource ErrorTextBrush}"
|
||||
Text="{Binding PaidPostFileNameFormatError}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock FontWeight="SemiBold" Foreground="{DynamicResource TextPrimaryBrush}" Text="Post Filename Format" />
|
||||
<TextBox Text="{Binding PostFileNameFormat}"
|
||||
Watermark="Optional: override global post format" />
|
||||
<Grid ClipToBounds="True">
|
||||
<TextBox x:Name="PostFileNameFormatTextBox"
|
||||
Classes="fileNameOverlayInput"
|
||||
Text="{Binding PostFileNameFormat}"
|
||||
Watermark="Optional: override global post format"
|
||||
Padding="8,6"
|
||||
CaretBrush="{DynamicResource TextPrimaryBrush}" />
|
||||
<controls:FileNameFormatOverlayTextBlock IsHitTestVisible="False"
|
||||
Margin="{Binding #PostFileNameFormatTextBox.Padding}"
|
||||
VerticalAlignment="Center"
|
||||
Segments="{Binding PostSegments}"
|
||||
SourceTextBox="{Binding #PostFileNameFormatTextBox}"
|
||||
FontFamily="{Binding #PostFileNameFormatTextBox.FontFamily}"
|
||||
FontSize="{Binding #PostFileNameFormatTextBox.FontSize}"
|
||||
FontWeight="{Binding #PostFileNameFormatTextBox.FontWeight}"
|
||||
FontStyle="{Binding #PostFileNameFormatTextBox.FontStyle}" />
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<ComboBox Grid.Column="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
@ -1047,37 +1059,35 @@
|
||||
Content="Insert"
|
||||
Command="{Binding InsertPostVariableCommand}" />
|
||||
</Grid>
|
||||
<Border Padding="8"
|
||||
Background="{DynamicResource PreviewBackgroundBrush}"
|
||||
BorderBrush="{DynamicResource PreviewBorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8">
|
||||
<ItemsControl ItemsSource="{Binding PostSegments}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:FileNameFormatSegmentViewModel">
|
||||
<TextBlock Text="{Binding Text}"
|
||||
Foreground="{Binding Foreground}"
|
||||
FontFamily="Consolas"
|
||||
TextWrapping="Wrap" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Border>
|
||||
<TextBlock IsVisible="{Binding HasUnknownPostVariables}"
|
||||
Foreground="{DynamicResource ErrorTextBrush}"
|
||||
Text="{Binding UnknownPostVariablesMessage}"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock IsVisible="{Binding HasPostFileNameFormatError}"
|
||||
Foreground="{DynamicResource ErrorTextBrush}"
|
||||
Text="{Binding PostFileNameFormatError}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock FontWeight="SemiBold" Foreground="{DynamicResource TextPrimaryBrush}" Text="Paid Message Filename Format" />
|
||||
<TextBox Text="{Binding PaidMessageFileNameFormat}"
|
||||
Watermark="Optional: override global paid message format" />
|
||||
<Grid ClipToBounds="True">
|
||||
<TextBox x:Name="PaidMessageFileNameFormatTextBox"
|
||||
Classes="fileNameOverlayInput"
|
||||
Text="{Binding PaidMessageFileNameFormat}"
|
||||
Watermark="Optional: override global paid message format"
|
||||
Padding="8,6"
|
||||
CaretBrush="{DynamicResource TextPrimaryBrush}" />
|
||||
<controls:FileNameFormatOverlayTextBlock IsHitTestVisible="False"
|
||||
Margin="{Binding #PaidMessageFileNameFormatTextBox.Padding}"
|
||||
VerticalAlignment="Center"
|
||||
Segments="{Binding PaidMessageSegments}"
|
||||
SourceTextBox="{Binding #PaidMessageFileNameFormatTextBox}"
|
||||
FontFamily="{Binding #PaidMessageFileNameFormatTextBox.FontFamily}"
|
||||
FontSize="{Binding #PaidMessageFileNameFormatTextBox.FontSize}"
|
||||
FontWeight="{Binding #PaidMessageFileNameFormatTextBox.FontWeight}"
|
||||
FontStyle="{Binding #PaidMessageFileNameFormatTextBox.FontStyle}" />
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<ComboBox Grid.Column="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
@ -1089,37 +1099,35 @@
|
||||
Content="Insert"
|
||||
Command="{Binding InsertPaidMessageVariableCommand}" />
|
||||
</Grid>
|
||||
<Border Padding="8"
|
||||
Background="{DynamicResource PreviewBackgroundBrush}"
|
||||
BorderBrush="{DynamicResource PreviewBorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8">
|
||||
<ItemsControl ItemsSource="{Binding PaidMessageSegments}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:FileNameFormatSegmentViewModel">
|
||||
<TextBlock Text="{Binding Text}"
|
||||
Foreground="{Binding Foreground}"
|
||||
FontFamily="Consolas"
|
||||
TextWrapping="Wrap" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Border>
|
||||
<TextBlock IsVisible="{Binding HasUnknownPaidMessageVariables}"
|
||||
Foreground="{DynamicResource ErrorTextBrush}"
|
||||
Text="{Binding UnknownPaidMessageVariablesMessage}"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock IsVisible="{Binding HasPaidMessageFileNameFormatError}"
|
||||
Foreground="{DynamicResource ErrorTextBrush}"
|
||||
Text="{Binding PaidMessageFileNameFormatError}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock FontWeight="SemiBold" Foreground="{DynamicResource TextPrimaryBrush}" Text="Message Filename Format" />
|
||||
<TextBox Text="{Binding MessageFileNameFormat}"
|
||||
Watermark="Optional: override global message format" />
|
||||
<Grid ClipToBounds="True">
|
||||
<TextBox x:Name="MessageFileNameFormatTextBox"
|
||||
Classes="fileNameOverlayInput"
|
||||
Text="{Binding MessageFileNameFormat}"
|
||||
Watermark="Optional: override global message format"
|
||||
Padding="8,6"
|
||||
CaretBrush="{DynamicResource TextPrimaryBrush}" />
|
||||
<controls:FileNameFormatOverlayTextBlock IsHitTestVisible="False"
|
||||
Margin="{Binding #MessageFileNameFormatTextBox.Padding}"
|
||||
VerticalAlignment="Center"
|
||||
Segments="{Binding MessageSegments}"
|
||||
SourceTextBox="{Binding #MessageFileNameFormatTextBox}"
|
||||
FontFamily="{Binding #MessageFileNameFormatTextBox.FontFamily}"
|
||||
FontSize="{Binding #MessageFileNameFormatTextBox.FontSize}"
|
||||
FontWeight="{Binding #MessageFileNameFormatTextBox.FontWeight}"
|
||||
FontStyle="{Binding #MessageFileNameFormatTextBox.FontStyle}" />
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<ComboBox Grid.Column="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
@ -1131,31 +1139,14 @@
|
||||
Content="Insert"
|
||||
Command="{Binding InsertMessageVariableCommand}" />
|
||||
</Grid>
|
||||
<Border Padding="8"
|
||||
Background="{DynamicResource PreviewBackgroundBrush}"
|
||||
BorderBrush="{DynamicResource PreviewBorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8">
|
||||
<ItemsControl ItemsSource="{Binding MessageSegments}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:FileNameFormatSegmentViewModel">
|
||||
<TextBlock Text="{Binding Text}"
|
||||
Foreground="{Binding Foreground}"
|
||||
FontFamily="Consolas"
|
||||
TextWrapping="Wrap" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Border>
|
||||
<TextBlock IsVisible="{Binding HasUnknownMessageVariables}"
|
||||
Foreground="{DynamicResource ErrorTextBrush}"
|
||||
Text="{Binding UnknownMessageVariablesMessage}"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock IsVisible="{Binding HasMessageFileNameFormatError}"
|
||||
Foreground="{DynamicResource ErrorTextBrush}"
|
||||
Text="{Binding MessageFileNameFormatError}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Spacing="10" HorizontalAlignment="Right">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user