Refactoring the html editor toolbar.

This commit is contained in:
Burak Kaan Köse
2026-03-07 23:33:25 +01:00
parent ebc35c3de8
commit 1da34080d1
19 changed files with 754 additions and 280 deletions
+91 -3
View File
@@ -1,4 +1,5 @@
using System;
using System.Globalization;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
@@ -97,16 +98,33 @@ public sealed record class EditorTableCommandArgs(
public sealed record class EditorColorOption(string Name, string Value)
{
public SolidColorBrush Brush => new(ParseColor(Value));
public SolidColorBrush Brush => new(ParseColorValue(Value));
private static Color ParseColor(string? value)
public static Color ParseColorValue(string? value)
{
if (string.IsNullOrWhiteSpace(value))
{
return Colors.Transparent;
}
var hex = value.Trim().TrimStart('#');
var normalizedValue = value.Trim();
if (string.Equals(normalizedValue, "transparent", StringComparison.OrdinalIgnoreCase))
{
return Colors.Transparent;
}
if (TryParseRgbColor(normalizedValue, out var rgbColor))
{
return rgbColor;
}
if (TryParseNamedColor(normalizedValue, out var namedColor))
{
return namedColor;
}
var hex = normalizedValue.TrimStart('#');
if (hex.Length == 6)
{
hex = $"FF{hex}";
@@ -123,6 +141,76 @@ public sealed record class EditorColorOption(string Name, string Value)
(byte)((argb >> 8) & 0xFF),
(byte)(argb & 0xFF));
}
private static bool TryParseRgbColor(string value, out Color color)
{
color = Colors.Transparent;
var isRgba = value.StartsWith("rgba(", StringComparison.OrdinalIgnoreCase);
var isRgb = value.StartsWith("rgb(", StringComparison.OrdinalIgnoreCase);
if (!isRgb && !isRgba)
{
return false;
}
var startIndex = value.IndexOf('(');
var endIndex = value.LastIndexOf(')');
if (startIndex < 0 || endIndex <= startIndex)
{
return false;
}
var segments = value[(startIndex + 1)..endIndex]
.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
if ((isRgb && segments.Length != 3) || (isRgba && segments.Length != 4))
{
return false;
}
if (!byte.TryParse(segments[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var red) ||
!byte.TryParse(segments[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var green) ||
!byte.TryParse(segments[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out var blue))
{
return false;
}
byte alpha = 255;
if (isRgba)
{
if (!double.TryParse(segments[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var alphaValue))
{
return false;
}
alpha = alphaValue <= 1d
? (byte)Math.Clamp(Math.Round(alphaValue * 255d), 0d, 255d)
: (byte)Math.Clamp(Math.Round(alphaValue), 0d, 255d);
}
color = Color.FromArgb(alpha, red, green, blue);
return true;
}
private static bool TryParseNamedColor(string value, out Color color)
{
color = value.ToLowerInvariant() switch
{
"black" => Colors.Black,
"white" => Colors.White,
"gray" or "grey" => Colors.Gray,
"red" => Colors.Red,
"orange" => Colors.Orange,
"yellow" => Colors.Yellow,
"green" => Colors.Green,
"blue" => Colors.Blue,
"purple" => Colors.Purple,
"pink" => Colors.Pink,
_ => Colors.Transparent
};
return !color.Equals(Colors.Transparent) || string.Equals(value, "transparent", StringComparison.OrdinalIgnoreCase);
}
}
public sealed record class EditorParagraphStyleOption(string Name, string Tag);
@@ -11,29 +11,47 @@
mc:Ignorable="d">
<UserControl.Resources>
<Style x:Key="CompactComboBoxStyle" TargetType="ComboBox">
<Setter Property="MinWidth" Value="88" />
<Setter Property="MaxWidth" Value="136" />
<Style
x:Key="CompactComboBoxStyle"
BasedOn="{StaticResource DefaultComboBoxStyle}"
TargetType="ComboBox">
<Setter Property="MinWidth" Value="72" />
<Setter Property="MaxWidth" Value="132" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Padding" Value="4,0,0,0" />
</Style>
<Style x:Key="CompactPickerContainerStyle" TargetType="AppBarElementContainer">
<Setter Property="MinWidth" Value="0" />
<Setter Property="Margin" Value="0" />
<Setter Property="Margin" Value="0,8,0,0" />
</Style>
<Style x:Key="CompactPickerBorderStyle" TargetType="Border">
<!--<Setter Property="Background" Value="{ThemeResource ControlFillColorSecondaryBrush}" />-->
<Setter Property="BorderBrush" Value="{ThemeResource ControlStrokeColorDefaultBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="{StaticResource ControlCornerRadius}" />
<Setter Property="Padding" Value="0" />
<Setter Property="MinHeight" Value="36" />
<Setter Property="Margin" Value="4,0" />
</Style>
<DataTemplate x:Key="ColorOptionTemplate" x:DataType="mail:EditorColorOption">
<StackPanel Orientation="Horizontal" Spacing="8">
<Grid Width="14" Height="14">
<Rectangle
Fill="{x:Bind Brush}"
RadiusX="3"
RadiusY="3"
Fill="{x:Bind Brush}"
Stroke="{ThemeResource TextFillColorSecondaryBrush}"
StrokeThickness="1" />
</Grid>
<TextBlock VerticalAlignment="Center" Text="{x:Bind Name}" />
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<toolkit:TabbedCommandBar>
@@ -41,6 +59,12 @@
<SolidColorBrush x:Key="TabContentContentBorderBackground" Color="Transparent" />
<SolidColorBrush x:Key="TabContentContentBorderBorderBrush" Color="Transparent" />
<Thickness x:Key="TabContentBorderBorderThickness">0</Thickness>
<Style BasedOn="{StaticResource DefaultAppBarToggleButtonStyle}" TargetType="AppBarToggleButton">
<Setter Property="Width" Value="50" />
</Style>
<Style BasedOn="{StaticResource DefaultAppBarButtonStyle}" TargetType="AppBarButton">
<Setter Property="Width" Value="50" />
</Style>
</toolkit:TabbedCommandBar.Resources>
<toolkit:TabbedCommandBar.PaneCustomContent>
@@ -49,25 +73,41 @@
<toolkit:TabbedCommandBar.MenuItems>
<toolkit:TabbedCommandBarItem DefaultLabelPosition="Collapsed" Header="Format">
<AppBarToggleButton x:Name="BoldButton" Click="BoldButton_Click" Label="Bold" ToolTipService.ToolTip="Bold (Ctrl+B)">
<AppBarToggleButton
x:Name="BoldButton"
Click="BoldButton_Click"
Label="Bold"
ToolTipService.ToolTip="Bold (Ctrl+B)">
<AppBarToggleButton.Icon>
<PathIcon Data="{StaticResource BoldPathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarToggleButton x:Name="ItalicButton" Click="ItalicButton_Click" Label="Italic" ToolTipService.ToolTip="Italic (Ctrl+I)">
<AppBarToggleButton
x:Name="ItalicButton"
Click="ItalicButton_Click"
Label="Italic"
ToolTipService.ToolTip="Italic (Ctrl+I)">
<AppBarToggleButton.Icon>
<PathIcon Data="{StaticResource ItalicPathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarToggleButton x:Name="UnderlineButton" Click="UnderlineButton_Click" Label="Underline" ToolTipService.ToolTip="Underline (Ctrl+U)">
<AppBarToggleButton
x:Name="UnderlineButton"
Click="UnderlineButton_Click"
Label="Underline"
ToolTipService.ToolTip="Underline (Ctrl+U)">
<AppBarToggleButton.Icon>
<PathIcon Data="{StaticResource UnderlinePathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarToggleButton x:Name="StrikeButton" Click="StrikeButton_Click" Label="Strikethrough" ToolTipService.ToolTip="Strikethrough">
<AppBarToggleButton
x:Name="StrikeButton"
Click="StrikeButton_Click"
Label="Strikethrough"
ToolTipService.ToolTip="Strikethrough">
<AppBarToggleButton.Icon>
<PathIcon Data="{StaticResource StrikePathIcon}" />
</AppBarToggleButton.Icon>
@@ -75,139 +115,254 @@
<AppBarSeparator />
<AppBarToggleButton x:Name="BulletListButton" Click="BulletListButton_Click" Label="Bullets" ToolTipService.ToolTip="Bulleted list">
<AppBarToggleButton
x:Name="BulletListButton"
Click="BulletListButton_Click"
Label="Bullets"
ToolTipService.ToolTip="Bulleted list">
<AppBarToggleButton.Icon>
<PathIcon Data="{StaticResource BulletedListPathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarToggleButton x:Name="OrderedListButton" Click="OrderedListButton_Click" Label="Numbered list" ToolTipService.ToolTip="Numbered list">
<AppBarToggleButton
x:Name="OrderedListButton"
Click="OrderedListButton_Click"
Label="Numbered list"
ToolTipService.ToolTip="Numbered list">
<AppBarToggleButton.Icon>
<PathIcon Data="{StaticResource OrderedListPathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarButton x:Name="OutdentButton" Click="OutdentButton_Click" Label="Outdent" ToolTipService.ToolTip="Outdent">
<AppBarButton
x:Name="OutdentButton"
Click="OutdentButton_Click"
Label="Outdent"
ToolTipService.ToolTip="Outdent">
<AppBarButton.Icon>
<PathIcon Data="{StaticResource DecreaseIndentPathIcon}" />
</AppBarButton.Icon>
</AppBarButton>
<AppBarButton x:Name="IndentButton" Click="IndentButton_Click" Label="Indent" ToolTipService.ToolTip="Indent">
<AppBarButton
x:Name="IndentButton"
Click="IndentButton_Click"
Label="Indent"
ToolTipService.ToolTip="Indent">
<AppBarButton.Icon>
<PathIcon Data="{StaticResource IncreaseIndentPathIcon}" />
</AppBarButton.Icon>
</AppBarButton>
<AppBarElementContainer Style="{StaticResource CompactPickerContainerStyle}" ToolTipService.ToolTip="Text alignment">
<StackPanel Orientation="Horizontal" Spacing="6">
<PathIcon
Width="14"
Height="14"
VerticalAlignment="Center"
Data="{StaticResource AlignLeftPathIcon}" />
<ComboBox
x:Name="AlignmentComboBox"
Style="{StaticResource CompactComboBoxStyle}"
MinWidth="108"
SelectionChanged="AlignmentComboBox_SelectionChanged" />
</StackPanel>
<Border Style="{StaticResource CompactPickerBorderStyle}">
<Grid ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<PathIcon
Width="14"
Height="14"
Margin="10,0,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="{StaticResource AlignLeftPathIcon}"
Foreground="{ThemeResource TextFillColorPrimaryBrush}" />
<ComboBox
x:Name="AlignmentComboBox"
Grid.Column="1"
MinWidth="94"
SelectionChanged="AlignmentComboBox_SelectionChanged"
Style="{StaticResource CompactComboBoxStyle}" />
</Grid>
</Border>
</AppBarElementContainer>
<AppBarElementContainer Style="{StaticResource CompactPickerContainerStyle}" ToolTipService.ToolTip="Font family">
<StackPanel Orientation="Horizontal" Spacing="6">
<FontIcon VerticalAlignment="Center" FontSize="14" Glyph="&#xE8D2;" />
<ComboBox
x:Name="FontFamilyComboBox"
Style="{StaticResource CompactComboBoxStyle}"
MinWidth="120"
PlaceholderText="Font"
SelectionChanged="FontFamilyComboBox_SelectionChanged" />
</StackPanel>
</AppBarElementContainer>
<AppBarElementContainer Style="{StaticResource CompactPickerContainerStyle}" ToolTipService.ToolTip="Font size">
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock VerticalAlignment="Center" FontWeight="SemiBold" Text="12" />
<ComboBox
x:Name="FontSizeComboBox"
Style="{StaticResource CompactComboBoxStyle}"
MinWidth="80"
PlaceholderText="Size"
SelectionChanged="FontSizeComboBox_SelectionChanged" />
</StackPanel>
<Border Style="{StaticResource CompactPickerBorderStyle}">
<Grid ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<FontIcon
Margin="10,0,0,0"
VerticalAlignment="Center"
FontSize="14"
Glyph="&#xE8D2;" />
<ComboBox
x:Name="FontFamilyComboBox"
Grid.Column="1"
MinWidth="110"
PlaceholderText="Font"
SelectionChanged="FontFamilyComboBox_SelectionChanged"
Style="{StaticResource CompactComboBoxStyle}" />
<ComboBox
x:Name="FontSizeComboBox"
Grid.Column="2"
MinWidth="72"
HorizontalContentAlignment="Center"
PlaceholderText="Size"
SelectionChanged="FontSizeComboBox_SelectionChanged"
Style="{StaticResource CompactComboBoxStyle}" />
</Grid>
</Border>
</AppBarElementContainer>
<AppBarElementContainer Style="{StaticResource CompactPickerContainerStyle}" ToolTipService.ToolTip="Paragraph style">
<StackPanel Orientation="Horizontal" Spacing="6">
<PathIcon
Width="14"
Height="14"
VerticalAlignment="Center"
Data="{StaticResource ParagraphPathIcon}" />
<ComboBox
x:Name="ParagraphStyleComboBox"
Style="{StaticResource CompactComboBoxStyle}"
MinWidth="110"
DisplayMemberPath="Name"
PlaceholderText="Paragraph"
SelectionChanged="ParagraphStyleComboBox_SelectionChanged" />
</StackPanel>
<Border Style="{StaticResource CompactPickerBorderStyle}">
<Grid ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<PathIcon
Width="14"
Height="14"
Margin="10,0,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="{StaticResource ParagraphPathIcon}"
Foreground="{ThemeResource TextFillColorPrimaryBrush}" />
<ComboBox
x:Name="ParagraphStyleComboBox"
Grid.Column="1"
MinWidth="104"
DisplayMemberPath="Name"
PlaceholderText="Paragraph"
SelectionChanged="ParagraphStyleComboBox_SelectionChanged"
Style="{StaticResource CompactComboBoxStyle}" />
</Grid>
</Border>
</AppBarElementContainer>
<AppBarElementContainer Style="{StaticResource CompactPickerContainerStyle}" ToolTipService.ToolTip="Text color">
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock VerticalAlignment="Center" FontWeight="SemiBold" Text="A" />
<ComboBox
x:Name="TextColorComboBox"
Style="{StaticResource CompactComboBoxStyle}"
MinWidth="116"
ItemTemplate="{StaticResource ColorOptionTemplate}"
PlaceholderText="Text"
SelectionChanged="TextColorComboBox_SelectionChanged" />
</StackPanel>
<Border Style="{StaticResource CompactPickerBorderStyle}">
<Grid ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid
Width="18"
Height="18"
Margin="8,0,0,0"
VerticalAlignment="Center">
<Rectangle
Fill="{x:Bind SelectedTextColorBrush, Mode=OneWay}"
RadiusX="4"
RadiusY="4"
Stroke="{ThemeResource TextFillColorSecondaryBrush}"
StrokeThickness="1" />
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="11"
FontWeight="SemiBold"
Text="A" />
</Grid>
<ComboBox
x:Name="TextColorComboBox"
Grid.Column="1"
MinWidth="104"
ItemTemplate="{StaticResource ColorOptionTemplate}"
PlaceholderText="Text"
SelectionChanged="TextColorComboBox_SelectionChanged"
Style="{StaticResource CompactComboBoxStyle}" />
</Grid>
</Border>
</AppBarElementContainer>
<AppBarElementContainer Style="{StaticResource CompactPickerContainerStyle}" ToolTipService.ToolTip="Highlight color">
<StackPanel Orientation="Horizontal" Spacing="6">
<FontIcon VerticalAlignment="Center" FontSize="14" Glyph="&#xE7C3;" />
<ComboBox
x:Name="HighlightColorComboBox"
Style="{StaticResource CompactComboBoxStyle}"
MinWidth="122"
ItemTemplate="{StaticResource ColorOptionTemplate}"
PlaceholderText="Highlight"
SelectionChanged="HighlightColorComboBox_SelectionChanged" />
</StackPanel>
<Border Style="{StaticResource CompactPickerBorderStyle}">
<Grid ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid
Width="18"
Height="18"
Margin="8,0,0,0"
VerticalAlignment="Center">
<Rectangle
Fill="{x:Bind SelectedHighlightColorBrush, Mode=OneWay}"
RadiusX="4"
RadiusY="4"
Stroke="{ThemeResource TextFillColorSecondaryBrush}"
StrokeThickness="1" />
<Rectangle
Width="10"
Height="2"
Margin="0,0,0,3"
VerticalAlignment="Bottom"
Fill="{ThemeResource TextFillColorPrimaryBrush}" />
</Grid>
<ComboBox
x:Name="HighlightColorComboBox"
Grid.Column="1"
MinWidth="108"
ItemTemplate="{StaticResource ColorOptionTemplate}"
PlaceholderText="Highlight"
SelectionChanged="HighlightColorComboBox_SelectionChanged"
Style="{StaticResource CompactComboBoxStyle}" />
</Grid>
</Border>
</AppBarElementContainer>
<AppBarElementContainer Style="{StaticResource CompactPickerContainerStyle}" ToolTipService.ToolTip="Line height">
<StackPanel Orientation="Horizontal" Spacing="6">
<FontIcon VerticalAlignment="Center" FontSize="14" Glyph="&#xE8C8;" />
<ComboBox
x:Name="LineHeightComboBox"
Style="{StaticResource CompactComboBoxStyle}"
MinWidth="86"
PlaceholderText="Line"
SelectionChanged="LineHeightComboBox_SelectionChanged" />
</StackPanel>
<Border Style="{StaticResource CompactPickerBorderStyle}">
<Grid ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<FontIcon
Margin="9,0,0,0"
VerticalAlignment="Center"
FontSize="14"
Glyph="&#xE8C8;" />
<ComboBox
x:Name="LineHeightComboBox"
Grid.Column="1"
MinWidth="72"
PlaceholderText="Line"
SelectionChanged="LineHeightComboBox_SelectionChanged"
Style="{StaticResource CompactComboBoxStyle}" />
</Grid>
</Border>
</AppBarElementContainer>
</toolkit:TabbedCommandBarItem>
<toolkit:TabbedCommandBarItem DefaultLabelPosition="Collapsed" Header="Insert">
<AppBarButton x:Name="ImageButton" Click="ImageButton_Click" Label="Image" ToolTipService.ToolTip="Insert image">
<AppBarButton
x:Name="ImageButton"
Click="ImageButton_Click"
Label="Image"
ToolTipService.ToolTip="Insert image">
<AppBarButton.Icon>
<PathIcon Data="{StaticResource AddPhotoPathIcon}" />
</AppBarButton.Icon>
</AppBarButton>
<AppBarButton x:Name="EmojiButton" Click="EmojiButton_Click" Label="Emoji" ToolTipService.ToolTip="Insert emoji">
<AppBarButton
x:Name="EmojiButton"
Click="EmojiButton_Click"
Label="Emoji"
ToolTipService.ToolTip="Insert emoji">
<AppBarButton.Icon>
<PathIcon Data="{StaticResource EmojiPathIcon}" />
</AppBarButton.Icon>
</AppBarButton>
<AppBarButton x:Name="LinkButton" Click="LinkButton_Click" Label="Link" ToolTipService.ToolTip="Insert or edit link">
<AppBarButton
x:Name="LinkButton"
Click="LinkButton_Click"
Label="Link"
ToolTipService.ToolTip="Insert or edit link">
<AppBarButton.Icon>
<PathIcon Data="{StaticResource AddLinkPathIcon}" />
</AppBarButton.Icon>
@@ -224,7 +379,11 @@
</AppBarButton.Icon>
</AppBarButton>
<AppBarButton x:Name="TableButton" Click="TableButton_Click" Label="Table" ToolTipService.ToolTip="Insert table">
<AppBarButton
x:Name="TableButton"
Click="TableButton_Click"
Label="Table"
ToolTipService.ToolTip="Insert table">
<AppBarButton.Icon>
<FontIcon Glyph="&#xE14C;" />
</AppBarButton.Icon>
@@ -236,13 +395,21 @@
</toolkit:TabbedCommandBarItem>
<toolkit:TabbedCommandBarItem DefaultLabelPosition="Collapsed" Header="Options">
<AppBarToggleButton x:Name="BuiltInToolbarButton" Click="BuiltInToolbarButton_Click" Label="Web toolbar" ToolTipService.ToolTip="Toggle built-in web toolbar">
<AppBarToggleButton
x:Name="BuiltInToolbarButton"
Click="BuiltInToolbarButton_Click"
Label="Web toolbar"
ToolTipService.ToolTip="Toggle built-in web toolbar">
<AppBarToggleButton.Icon>
<PathIcon Data="{StaticResource WebviewToolBarPathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarToggleButton x:Name="SpellCheckButton" Click="SpellCheckButton_Click" Label="Spell check" ToolTipService.ToolTip="Toggle spell check">
<AppBarToggleButton
x:Name="SpellCheckButton"
Click="SpellCheckButton_Click"
Label="Spell check"
ToolTipService.ToolTip="Toggle spell check">
<AppBarToggleButton.Icon>
<FontIcon Glyph="&#xEA53;" />
</AppBarToggleButton.Icon>
@@ -2,63 +2,42 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.WinUI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Windows.UI;
namespace Wino.Mail.Controls;
public sealed partial class EditorTabbedCommandBarControl : UserControl, IEditorCommandControl
{
public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register(
nameof(CommandTarget),
typeof(IEditorCommandTarget),
typeof(EditorTabbedCommandBarControl),
new PropertyMetadata(null, OnCommandTargetChanged));
[GeneratedDependencyProperty]
public partial IEditorCommandTarget? CommandTarget { get; set; }
public static readonly DependencyProperty PaneCustomContentProperty = DependencyProperty.Register(
nameof(PaneCustomContent),
typeof(object),
typeof(EditorTabbedCommandBarControl),
new PropertyMetadata(null));
[GeneratedDependencyProperty]
public partial object? PaneCustomContent { get; set; }
public static readonly DependencyProperty InsertCustomContentProperty = DependencyProperty.Register(
nameof(InsertCustomContent),
typeof(object),
typeof(EditorTabbedCommandBarControl),
new PropertyMetadata(null));
[GeneratedDependencyProperty]
public partial object? InsertCustomContent { get; set; }
public static readonly DependencyProperty OptionsCustomContentProperty = DependencyProperty.Register(
nameof(OptionsCustomContent),
typeof(object),
typeof(EditorTabbedCommandBarControl),
new PropertyMetadata(null));
[GeneratedDependencyProperty]
public partial object? OptionsCustomContent { get; set; }
[GeneratedDependencyProperty]
public partial EditorColorOption? SelectedTextColorOption { get; set; }
[GeneratedDependencyProperty]
public partial EditorColorOption? SelectedHighlightColorOption { get; set; }
private bool _isApplyingState;
private IEditorCommandTarget? _subscribedTarget;
private static readonly SolidColorBrush TransparentBrush = new(EditorColorOption.ParseColorValue(null));
private IReadOnlyList<EditorColorOption> _textColorOptions = Array.Empty<EditorColorOption>();
private IReadOnlyList<EditorColorOption> _highlightColorOptions = Array.Empty<EditorColorOption>();
public IEditorCommandTarget? CommandTarget
{
get => (IEditorCommandTarget?)GetValue(CommandTargetProperty);
set => SetValue(CommandTargetProperty, value);
}
public object? PaneCustomContent
{
get => GetValue(PaneCustomContentProperty);
set => SetValue(PaneCustomContentProperty, value);
}
public object? InsertCustomContent
{
get => GetValue(InsertCustomContentProperty);
set => SetValue(InsertCustomContentProperty, value);
}
public object? OptionsCustomContent
{
get => GetValue(OptionsCustomContentProperty);
set => SetValue(OptionsCustomContentProperty, value);
}
public Brush SelectedTextColorBrush => SelectedTextColorOption?.Brush ?? TransparentBrush;
public Brush SelectedHighlightColorBrush => SelectedHighlightColorOption?.Brush ?? TransparentBrush;
public EditorTabbedCommandBarControl()
{
@@ -104,12 +83,15 @@ public sealed partial class EditorTabbedCommandBarControl : UserControl, IEditor
_subscribedTarget = null;
}
private static void OnCommandTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
partial void OnCommandTargetChanged(IEditorCommandTarget? newValue)
{
var control = (EditorTabbedCommandBarControl)d;
control.AttachCommandTarget((IEditorCommandTarget?)e.NewValue);
AttachCommandTarget(newValue);
}
partial void OnSelectedTextColorOptionChanged(EditorColorOption? newValue) => Bindings.Update();
partial void OnSelectedHighlightColorOptionChanged(EditorColorOption? newValue) => Bindings.Update();
private void OnLoaded(object sender, RoutedEventArgs e)
{
AttachCommandTarget(CommandTarget);
@@ -136,8 +118,10 @@ public sealed partial class EditorTabbedCommandBarControl : UserControl, IEditor
FontSizeComboBox.ItemsSource = capabilities.FontSizes;
AlignmentComboBox.ItemsSource = capabilities.Alignments;
ParagraphStyleComboBox.ItemsSource = capabilities.ParagraphStyles;
TextColorComboBox.ItemsSource = capabilities.TextColors;
HighlightColorComboBox.ItemsSource = capabilities.HighlightColors;
_textColorOptions = capabilities.TextColors;
_highlightColorOptions = capabilities.HighlightColors;
TextColorComboBox.ItemsSource = _textColorOptions;
HighlightColorComboBox.ItemsSource = _highlightColorOptions;
LineHeightComboBox.ItemsSource = capabilities.LineHeights;
}
@@ -163,8 +147,10 @@ public sealed partial class EditorTabbedCommandBarControl : UserControl, IEditor
FontSizeComboBox.SelectedItem = MatchValueItem<int>(FontSizeComboBox.ItemsSource, state.FontSize);
LineHeightComboBox.SelectedItem = MatchStringItem(LineHeightComboBox.ItemsSource, state.LineHeight);
ParagraphStyleComboBox.SelectedItem = MatchParagraphItem(state.ParagraphStyle);
TextColorComboBox.SelectedItem = MatchColorItem(TextColorComboBox.ItemsSource, state.TextColor);
HighlightColorComboBox.SelectedItem = MatchColorItem(HighlightColorComboBox.ItemsSource, state.HighlightColor);
SelectedTextColorOption = ResolveColorOption(_textColorOptions, state.TextColor);
SelectedHighlightColorOption = ResolveColorOption(_highlightColorOptions, state.HighlightColor);
TextColorComboBox.SelectedItem = SelectedTextColorOption;
HighlightColorComboBox.SelectedItem = SelectedHighlightColorOption;
_isApplyingState = false;
}
@@ -207,14 +193,54 @@ public sealed partial class EditorTabbedCommandBarControl : UserControl, IEditor
return styles.FirstOrDefault(item => string.Equals(item.Tag, tag, StringComparison.OrdinalIgnoreCase));
}
private static object? MatchColorItem(object? itemsSource, string? value)
private static EditorColorOption? MatchColorItem(IEnumerable<EditorColorOption> colors, string? value)
{
if (itemsSource is not IEnumerable<EditorColorOption> colors)
var normalizedValue = value ?? string.Empty;
var matchedByValue = colors.FirstOrDefault(item => string.Equals(item.Value, normalizedValue, StringComparison.OrdinalIgnoreCase));
if (matchedByValue != null)
{
return null;
return matchedByValue;
}
return colors.FirstOrDefault(item => string.Equals(item.Value, value ?? string.Empty, StringComparison.OrdinalIgnoreCase));
var targetColor = EditorColorOption.ParseColorValue(value);
return colors.FirstOrDefault(item => item.Brush.Color.Equals(targetColor));
}
private static EditorColorOption? ResolveColorOption(IEnumerable<EditorColorOption> colors, string? value)
{
var colorOptions = colors.ToList();
var matchedColor = MatchColorItem(colors, value);
if (matchedColor != null)
{
return matchedColor;
}
if (string.IsNullOrWhiteSpace(value))
{
return colorOptions.FirstOrDefault(item => string.IsNullOrWhiteSpace(item.Value));
}
var targetColor = EditorColorOption.ParseColorValue(value);
var selectableColors = colorOptions
.Where(item => !string.IsNullOrWhiteSpace(item.Value))
.ToList();
if (selectableColors.Count == 0)
{
return colorOptions.FirstOrDefault();
}
return selectableColors
.OrderBy(item => GetColorDistance(item.Brush.Color, targetColor))
.First();
}
private static int GetColorDistance(Color left, Color right)
{
var redDiff = left.R - right.R;
var greenDiff = left.G - right.G;
var blueDiff = left.B - right.B;
return (redDiff * redDiff) + (greenDiff * greenDiff) + (blueDiff * blueDiff);
}
private async Task ExecuteAsync(EditorCommand command)
@@ -281,22 +307,26 @@ public sealed partial class EditorTabbedCommandBarControl : UserControl, IEditor
private async void TextColorComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_isApplyingState || TextColorComboBox.SelectedItem is not EditorColorOption color)
SelectedTextColorOption = TextColorComboBox.SelectedItem as EditorColorOption;
if (_isApplyingState || SelectedTextColorOption == null)
{
return;
}
await ExecuteAsync(EditorCommand.SetTextColor(color.Value));
await ExecuteAsync(EditorCommand.SetTextColor(SelectedTextColorOption.Value));
}
private async void HighlightColorComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_isApplyingState || HighlightColorComboBox.SelectedItem is not EditorColorOption color)
SelectedHighlightColorOption = HighlightColorComboBox.SelectedItem as EditorColorOption;
if (_isApplyingState || SelectedHighlightColorOption == null)
{
return;
}
await ExecuteAsync(EditorCommand.SetHighlightColor(color.Value));
await ExecuteAsync(EditorCommand.SetHighlightColor(SelectedHighlightColorOption.Value));
}
private async void LineHeightComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
@@ -412,6 +442,7 @@ public sealed partial class EditorTabbedCommandBarControl : UserControl, IEditor
await ExecuteAsync(EditorCommand.InsertTable(new EditorTableCommandArgs((int)Math.Max(1, rowsBox.Value), (int)Math.Max(1, columnsBox.Value))));
}
}
}
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
@@ -392,7 +393,9 @@ public sealed partial class WebViewEditorControl : Control, IDisposable, IEditor
JsonSerializer.Serialize(_preferencesService.ComposerFont, BasicTypesJsonContext.Default.String),
JsonSerializer.Serialize(_preferencesService.ComposerFontSize, BasicTypesJsonContext.Default.Int32),
JsonSerializer.Serialize(_preferencesService.ReaderFont, BasicTypesJsonContext.Default.String),
JsonSerializer.Serialize(_preferencesService.ReaderFontSize, BasicTypesJsonContext.Default.Int32));
JsonSerializer.Serialize(_preferencesService.ReaderFontSize, BasicTypesJsonContext.Default.Int32),
JsonSerializer.Serialize(DefaultTextColors.Select(option => option.Value).Where(value => !string.IsNullOrWhiteSpace(value)).ToList(), BasicTypesJsonContext.Default.ListString),
JsonSerializer.Serialize(DefaultHighlightColors.Select(option => option.Value).Where(value => !string.IsNullOrWhiteSpace(value)).ToList(), BasicTypesJsonContext.Default.ListString));
UpdateCapabilities(BuildCapabilities(fonts));
_editorReadyTask.TrySetResult(true);