Web editor refactoring and some calendar occurrence summary stuff.

This commit is contained in:
Burak Kaan Köse
2026-03-07 15:37:02 +01:00
parent 09f1cee3a5
commit d1f8163d72
17 changed files with 2649 additions and 1068 deletions
@@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Microsoft.UI;
using Microsoft.UI.Xaml.Media;
using Windows.UI;
namespace Wino.Mail.Controls;
public interface IEditorCommandTarget
{
EditorState CurrentState { get; }
EditorCapabilities Capabilities { get; }
event EventHandler<EditorState>? StateChanged;
event EventHandler<EditorCapabilities>? CapabilitiesChanged;
Task ExecuteCommandAsync(EditorCommand command);
}
public interface IEditorCommandControl
{
IEditorCommandTarget? CommandTarget { get; set; }
void AttachCommandTarget(IEditorCommandTarget? target);
void DetachCommandTarget();
}
public enum EditorCommandKind
{
ToggleBold,
ToggleItalic,
ToggleUnderline,
ToggleStrikethrough,
ToggleOrderedList,
ToggleUnorderedList,
Indent,
Outdent,
SetAlignment,
SetFontFamily,
SetFontSize,
SetParagraphStyle,
SetTextColor,
SetHighlightColor,
SetLineHeight,
InsertImage,
InsertLink,
RemoveLink,
InsertEmoji,
InsertTable,
ToggleBuiltInToolbar,
ToggleTheme,
ToggleSpellCheck
}
public enum EditorTextAlignment
{
Left,
Center,
Right,
Justify
}
public sealed record class EditorCommand(EditorCommandKind Kind, object? Value = null)
{
public static EditorCommand ToggleBold() => new(EditorCommandKind.ToggleBold);
public static EditorCommand ToggleItalic() => new(EditorCommandKind.ToggleItalic);
public static EditorCommand ToggleUnderline() => new(EditorCommandKind.ToggleUnderline);
public static EditorCommand ToggleStrikethrough() => new(EditorCommandKind.ToggleStrikethrough);
public static EditorCommand ToggleOrderedList() => new(EditorCommandKind.ToggleOrderedList);
public static EditorCommand ToggleUnorderedList() => new(EditorCommandKind.ToggleUnorderedList);
public static EditorCommand Indent() => new(EditorCommandKind.Indent);
public static EditorCommand Outdent() => new(EditorCommandKind.Outdent);
public static EditorCommand SetAlignment(EditorTextAlignment alignment) => new(EditorCommandKind.SetAlignment, alignment);
public static EditorCommand SetFontFamily(string fontFamily) => new(EditorCommandKind.SetFontFamily, fontFamily);
public static EditorCommand SetFontSize(int fontSize) => new(EditorCommandKind.SetFontSize, fontSize);
public static EditorCommand SetParagraphStyle(string tagName) => new(EditorCommandKind.SetParagraphStyle, tagName);
public static EditorCommand SetTextColor(string color) => new(EditorCommandKind.SetTextColor, color);
public static EditorCommand SetHighlightColor(string color) => new(EditorCommandKind.SetHighlightColor, color);
public static EditorCommand SetLineHeight(string lineHeight) => new(EditorCommandKind.SetLineHeight, lineHeight);
public static EditorCommand InsertImage() => new(EditorCommandKind.InsertImage);
public static EditorCommand InsertEmoji() => new(EditorCommandKind.InsertEmoji);
public static EditorCommand InsertLink(EditorLinkCommandArgs args) => new(EditorCommandKind.InsertLink, args);
public static EditorCommand RemoveLink() => new(EditorCommandKind.RemoveLink);
public static EditorCommand InsertTable(EditorTableCommandArgs args) => new(EditorCommandKind.InsertTable, args);
public static EditorCommand ToggleBuiltInToolbar(bool isVisible) => new(EditorCommandKind.ToggleBuiltInToolbar, isVisible);
public static EditorCommand ToggleTheme(bool isDarkMode) => new(EditorCommandKind.ToggleTheme, isDarkMode);
public static EditorCommand ToggleSpellCheck(bool isEnabled) => new(EditorCommandKind.ToggleSpellCheck, isEnabled);
}
public sealed record class EditorLinkCommandArgs(
[property: JsonPropertyName("url")] string Url,
[property: JsonPropertyName("text")] string? Text = null,
[property: JsonPropertyName("openInNewWindow")] bool OpenInNewWindow = true);
public sealed record class EditorTableCommandArgs(
[property: JsonPropertyName("rows")] int Rows,
[property: JsonPropertyName("columns")] int Columns);
public sealed record class EditorColorOption(string Name, string Value)
{
public SolidColorBrush Brush => new(ParseColor(Value));
private static Color ParseColor(string? value)
{
if (string.IsNullOrWhiteSpace(value))
{
return Colors.Transparent;
}
var hex = value.Trim().TrimStart('#');
if (hex.Length == 6)
{
hex = $"FF{hex}";
}
if (hex.Length != 8 || !uint.TryParse(hex, System.Globalization.NumberStyles.HexNumber, null, out var argb))
{
return Colors.Transparent;
}
return Color.FromArgb(
(byte)((argb >> 24) & 0xFF),
(byte)((argb >> 16) & 0xFF),
(byte)((argb >> 8) & 0xFF),
(byte)(argb & 0xFF));
}
}
public sealed record class EditorParagraphStyleOption(string Name, string Tag);
public sealed record class EditorCapabilities
{
public IReadOnlyList<string> Fonts { get; init; } = Array.Empty<string>();
public IReadOnlyList<int> FontSizes { get; init; } = Array.Empty<int>();
public IReadOnlyList<EditorColorOption> TextColors { get; init; } = Array.Empty<EditorColorOption>();
public IReadOnlyList<EditorColorOption> HighlightColors { get; init; } = Array.Empty<EditorColorOption>();
public IReadOnlyList<EditorParagraphStyleOption> ParagraphStyles { get; init; } = Array.Empty<EditorParagraphStyleOption>();
public IReadOnlyList<string> LineHeights { get; init; } = Array.Empty<string>();
public IReadOnlyList<EditorTextAlignment> Alignments { get; init; } = Array.Empty<EditorTextAlignment>();
}
public sealed record class EditorState
{
public bool IsBold { get; init; }
public bool IsItalic { get; init; }
public bool IsUnderline { get; init; }
public bool IsStrikethrough { get; init; }
public bool IsOrderedList { get; init; }
public bool IsUnorderedList { get; init; }
public bool CanIndent { get; init; } = true;
public bool CanOutdent { get; init; }
public bool HasSelection { get; init; }
public bool IsDarkMode { get; init; }
public bool IsBuiltInToolbarVisible { get; init; }
public bool IsSpellCheckEnabled { get; init; } = true;
public EditorTextAlignment Alignment { get; init; } = EditorTextAlignment.Left;
public string? FontFamily { get; init; }
public int? FontSize { get; init; }
public string? ParagraphStyle { get; init; }
public string? TextColor { get; init; }
public string? HighlightColor { get; init; }
public string? LineHeight { get; init; }
public string? LinkUrl { get; init; }
public string? SelectedText { get; init; }
}
@@ -0,0 +1,259 @@
<UserControl
x:Class="Wino.Mail.Controls.EditorTabbedCommandBarControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Wino.Mail.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mail="using:Wino.Mail.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:toolkit="using:CommunityToolkit.WinUI.Controls"
x:Name="Root"
mc:Ignorable="d">
<UserControl.Resources>
<Style x:Key="CompactComboBoxStyle" TargetType="ComboBox">
<Setter Property="MinWidth" Value="88" />
<Setter Property="MaxWidth" Value="136" />
</Style>
<Style x:Key="CompactPickerContainerStyle" TargetType="AppBarElementContainer">
<Setter Property="MinWidth" Value="0" />
<Setter Property="Margin" Value="0" />
</Style>
<DataTemplate x:Key="ColorOptionTemplate" x:DataType="mail:EditorColorOption">
<StackPanel Orientation="Horizontal" Spacing="8">
<Grid Width="14" Height="14">
<Rectangle
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>
<toolkit:TabbedCommandBar.Resources>
<SolidColorBrush x:Key="TabContentContentBorderBackground" Color="Transparent" />
<SolidColorBrush x:Key="TabContentContentBorderBorderBrush" Color="Transparent" />
<Thickness x:Key="TabContentBorderBorderThickness">0</Thickness>
</toolkit:TabbedCommandBar.Resources>
<toolkit:TabbedCommandBar.PaneCustomContent>
<ContentPresenter Content="{x:Bind PaneCustomContent, Mode=OneWay}" />
</toolkit:TabbedCommandBar.PaneCustomContent>
<toolkit:TabbedCommandBar.MenuItems>
<toolkit:TabbedCommandBarItem DefaultLabelPosition="Collapsed" Header="Format">
<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.Icon>
<PathIcon Data="{StaticResource ItalicPathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<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.Icon>
<PathIcon Data="{StaticResource StrikePathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarSeparator />
<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.Icon>
<PathIcon Data="{StaticResource OrderedListPathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<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.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>
</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>
</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>
</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>
</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>
</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>
</AppBarElementContainer>
</toolkit:TabbedCommandBarItem>
<toolkit:TabbedCommandBarItem DefaultLabelPosition="Collapsed" Header="Insert">
<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.Icon>
<PathIcon Data="{StaticResource EmojiPathIcon}" />
</AppBarButton.Icon>
</AppBarButton>
<AppBarButton x:Name="LinkButton" Click="LinkButton_Click" Label="Link" ToolTipService.ToolTip="Insert or edit link">
<AppBarButton.Icon>
<PathIcon Data="{StaticResource AddLinkPathIcon}" />
</AppBarButton.Icon>
</AppBarButton>
<AppBarButton
x:Name="RemoveLinkButton"
Click="RemoveLinkButton_Click"
Label="Remove link"
ToolTipService.ToolTip="Remove link"
Visibility="Collapsed">
<AppBarButton.Icon>
<SymbolIcon Symbol="Remove" />
</AppBarButton.Icon>
</AppBarButton>
<AppBarButton x:Name="TableButton" Click="TableButton_Click" Label="Table" ToolTipService.ToolTip="Insert table">
<AppBarButton.Icon>
<FontIcon Glyph="&#xE14C;" />
</AppBarButton.Icon>
</AppBarButton>
<AppBarElementContainer ToolTipService.ToolTip="Insert actions">
<ContentPresenter Content="{x:Bind InsertCustomContent, Mode=OneWay}" />
</AppBarElementContainer>
</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.Icon>
<PathIcon Data="{StaticResource WebviewToolBarPathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarToggleButton x:Name="SpellCheckButton" Click="SpellCheckButton_Click" Label="Spell check" ToolTipService.ToolTip="Toggle spell check">
<AppBarToggleButton.Icon>
<FontIcon Glyph="&#xEA53;" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarElementContainer ToolTipService.ToolTip="Composer options">
<ContentPresenter Content="{x:Bind OptionsCustomContent, Mode=OneWay}" />
</AppBarElementContainer>
</toolkit:TabbedCommandBarItem>
</toolkit:TabbedCommandBar.MenuItems>
</toolkit:TabbedCommandBar>
</UserControl>
@@ -0,0 +1,418 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
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));
public static readonly DependencyProperty PaneCustomContentProperty = DependencyProperty.Register(
nameof(PaneCustomContent),
typeof(object),
typeof(EditorTabbedCommandBarControl),
new PropertyMetadata(null));
public static readonly DependencyProperty InsertCustomContentProperty = DependencyProperty.Register(
nameof(InsertCustomContent),
typeof(object),
typeof(EditorTabbedCommandBarControl),
new PropertyMetadata(null));
public static readonly DependencyProperty OptionsCustomContentProperty = DependencyProperty.Register(
nameof(OptionsCustomContent),
typeof(object),
typeof(EditorTabbedCommandBarControl),
new PropertyMetadata(null));
private bool _isApplyingState;
private IEditorCommandTarget? _subscribedTarget;
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 EditorTabbedCommandBarControl()
{
InitializeComponent();
Loaded += OnLoaded;
Unloaded += OnUnloaded;
}
public void AttachCommandTarget(IEditorCommandTarget? target)
{
if (_subscribedTarget == target)
{
return;
}
if (_subscribedTarget != null)
{
_subscribedTarget.StateChanged -= CommandTarget_StateChanged;
_subscribedTarget.CapabilitiesChanged -= CommandTarget_CapabilitiesChanged;
}
_subscribedTarget = target;
if (_subscribedTarget != null)
{
_subscribedTarget.StateChanged += CommandTarget_StateChanged;
_subscribedTarget.CapabilitiesChanged += CommandTarget_CapabilitiesChanged;
ApplyCapabilities(_subscribedTarget.Capabilities);
ApplyState(_subscribedTarget.CurrentState);
}
}
public void DetachCommandTarget()
{
if (_subscribedTarget == null)
{
return;
}
_subscribedTarget.StateChanged -= CommandTarget_StateChanged;
_subscribedTarget.CapabilitiesChanged -= CommandTarget_CapabilitiesChanged;
_subscribedTarget = null;
}
private static void OnCommandTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (EditorTabbedCommandBarControl)d;
control.AttachCommandTarget((IEditorCommandTarget?)e.NewValue);
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
AttachCommandTarget(CommandTarget);
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
DetachCommandTarget();
}
private void CommandTarget_StateChanged(object? sender, EditorState e)
{
ApplyState(e);
}
private void CommandTarget_CapabilitiesChanged(object? sender, EditorCapabilities e)
{
ApplyCapabilities(e);
}
private void ApplyCapabilities(EditorCapabilities capabilities)
{
FontFamilyComboBox.ItemsSource = capabilities.Fonts;
FontSizeComboBox.ItemsSource = capabilities.FontSizes;
AlignmentComboBox.ItemsSource = capabilities.Alignments;
ParagraphStyleComboBox.ItemsSource = capabilities.ParagraphStyles;
TextColorComboBox.ItemsSource = capabilities.TextColors;
HighlightColorComboBox.ItemsSource = capabilities.HighlightColors;
LineHeightComboBox.ItemsSource = capabilities.LineHeights;
}
private void ApplyState(EditorState state)
{
_isApplyingState = true;
BoldButton.IsChecked = state.IsBold;
ItalicButton.IsChecked = state.IsItalic;
UnderlineButton.IsChecked = state.IsUnderline;
StrikeButton.IsChecked = state.IsStrikethrough;
BulletListButton.IsChecked = state.IsUnorderedList;
OrderedListButton.IsChecked = state.IsOrderedList;
IndentButton.IsEnabled = state.CanIndent;
OutdentButton.IsEnabled = state.CanOutdent;
RemoveLinkButton.IsEnabled = !string.IsNullOrWhiteSpace(state.LinkUrl);
RemoveLinkButton.Visibility = RemoveLinkButton.IsEnabled ? Visibility.Visible : Visibility.Collapsed;
BuiltInToolbarButton.IsChecked = state.IsBuiltInToolbarVisible;
SpellCheckButton.IsChecked = state.IsSpellCheckEnabled;
AlignmentComboBox.SelectedItem = state.Alignment;
FontFamilyComboBox.SelectedItem = MatchStringItem(FontFamilyComboBox.ItemsSource, state.FontFamily);
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);
_isApplyingState = false;
}
private static object? MatchStringItem(object? itemsSource, string? value)
{
if (itemsSource is IEnumerable<string> strings)
{
return strings.FirstOrDefault(item => string.Equals(item, value, StringComparison.OrdinalIgnoreCase));
}
return null;
}
private static object? MatchValueItem<T>(object? itemsSource, T? value) where T : struct
{
if (!value.HasValue || itemsSource is not IEnumerable<T> values)
{
return null;
}
foreach (var item in values)
{
if (EqualityComparer<T>.Default.Equals(item, value.Value))
{
return item;
}
}
return null;
}
private object? MatchParagraphItem(string? tag)
{
if (ParagraphStyleComboBox.ItemsSource is not IEnumerable<EditorParagraphStyleOption> styles)
{
return null;
}
return styles.FirstOrDefault(item => string.Equals(item.Tag, tag, StringComparison.OrdinalIgnoreCase));
}
private static object? MatchColorItem(object? itemsSource, string? value)
{
if (itemsSource is not IEnumerable<EditorColorOption> colors)
{
return null;
}
return colors.FirstOrDefault(item => string.Equals(item.Value, value ?? string.Empty, StringComparison.OrdinalIgnoreCase));
}
private async Task ExecuteAsync(EditorCommand command)
{
if (_isApplyingState || CommandTarget == null)
{
return;
}
await CommandTarget.ExecuteCommandAsync(command);
}
private async void BoldButton_Click(object sender, RoutedEventArgs e) => await ExecuteAsync(EditorCommand.ToggleBold());
private async void ItalicButton_Click(object sender, RoutedEventArgs e) => await ExecuteAsync(EditorCommand.ToggleItalic());
private async void UnderlineButton_Click(object sender, RoutedEventArgs e) => await ExecuteAsync(EditorCommand.ToggleUnderline());
private async void StrikeButton_Click(object sender, RoutedEventArgs e) => await ExecuteAsync(EditorCommand.ToggleStrikethrough());
private async void BulletListButton_Click(object sender, RoutedEventArgs e) => await ExecuteAsync(EditorCommand.ToggleUnorderedList());
private async void OrderedListButton_Click(object sender, RoutedEventArgs e) => await ExecuteAsync(EditorCommand.ToggleOrderedList());
private async void IndentButton_Click(object sender, RoutedEventArgs e) => await ExecuteAsync(EditorCommand.Indent());
private async void OutdentButton_Click(object sender, RoutedEventArgs e) => await ExecuteAsync(EditorCommand.Outdent());
private async void ImageButton_Click(object sender, RoutedEventArgs e) => await ExecuteAsync(EditorCommand.InsertImage());
private async void EmojiButton_Click(object sender, RoutedEventArgs e) => await ExecuteAsync(EditorCommand.InsertEmoji());
private async void RemoveLinkButton_Click(object sender, RoutedEventArgs e) => await ExecuteAsync(EditorCommand.RemoveLink());
private async void AlignmentComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_isApplyingState || AlignmentComboBox.SelectedItem is not EditorTextAlignment alignment)
{
return;
}
await ExecuteAsync(EditorCommand.SetAlignment(alignment));
}
private async void FontFamilyComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_isApplyingState || FontFamilyComboBox.SelectedItem is not string fontFamily || string.IsNullOrWhiteSpace(fontFamily))
{
return;
}
await ExecuteAsync(EditorCommand.SetFontFamily(fontFamily));
}
private async void FontSizeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_isApplyingState || FontSizeComboBox.SelectedItem is not int fontSize)
{
return;
}
await ExecuteAsync(EditorCommand.SetFontSize(fontSize));
}
private async void ParagraphStyleComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_isApplyingState || ParagraphStyleComboBox.SelectedItem is not EditorParagraphStyleOption paragraphStyle)
{
return;
}
await ExecuteAsync(EditorCommand.SetParagraphStyle(paragraphStyle.Tag));
}
private async void TextColorComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_isApplyingState || TextColorComboBox.SelectedItem is not EditorColorOption color)
{
return;
}
await ExecuteAsync(EditorCommand.SetTextColor(color.Value));
}
private async void HighlightColorComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_isApplyingState || HighlightColorComboBox.SelectedItem is not EditorColorOption color)
{
return;
}
await ExecuteAsync(EditorCommand.SetHighlightColor(color.Value));
}
private async void LineHeightComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_isApplyingState || LineHeightComboBox.SelectedItem is not string lineHeight || string.IsNullOrWhiteSpace(lineHeight))
{
return;
}
await ExecuteAsync(EditorCommand.SetLineHeight(lineHeight));
}
private async void BuiltInToolbarButton_Click(object sender, RoutedEventArgs e)
{
await ExecuteAsync(EditorCommand.ToggleBuiltInToolbar(BuiltInToolbarButton.IsChecked == true));
}
private async void SpellCheckButton_Click(object sender, RoutedEventArgs e)
{
await ExecuteAsync(EditorCommand.ToggleSpellCheck(SpellCheckButton.IsChecked == true));
}
private async void LinkButton_Click(object sender, RoutedEventArgs e)
{
if (CommandTarget == null)
{
return;
}
var currentState = CommandTarget.CurrentState;
var urlTextBox = new TextBox
{
Header = "URL",
Text = currentState.LinkUrl ?? string.Empty,
PlaceholderText = "https://example.com"
};
var textTextBox = new TextBox
{
Header = "Text",
Text = currentState.SelectedText ?? string.Empty,
PlaceholderText = "Link text"
};
var openInNewWindow = new CheckBox
{
Content = "Open in new window",
IsChecked = true
};
var dialog = new ContentDialog
{
XamlRoot = XamlRoot,
Title = "Insert link",
PrimaryButtonText = "Apply",
CloseButtonText = "Cancel",
DefaultButton = ContentDialogButton.Primary,
Content = new StackPanel
{
Spacing = 12,
Children =
{
urlTextBox,
textTextBox,
openInNewWindow
}
}
};
if (await dialog.ShowAsync() == ContentDialogResult.Primary && !string.IsNullOrWhiteSpace(urlTextBox.Text))
{
await ExecuteAsync(EditorCommand.InsertLink(new EditorLinkCommandArgs(urlTextBox.Text.Trim(), textTextBox.Text.Trim(), openInNewWindow.IsChecked == true)));
}
}
private async void TableButton_Click(object sender, RoutedEventArgs e)
{
var rowsBox = new NumberBox
{
Header = "Rows",
Minimum = 1,
Maximum = 10,
SmallChange = 1,
Value = 2
};
var columnsBox = new NumberBox
{
Header = "Columns",
Minimum = 1,
Maximum = 10,
SmallChange = 1,
Value = 2
};
var dialog = new ContentDialog
{
XamlRoot = XamlRoot,
Title = "Insert table",
PrimaryButtonText = "Insert",
CloseButtonText = "Cancel",
DefaultButton = ContentDialogButton.Primary,
Content = new StackPanel
{
Spacing = 12,
Children =
{
rowsBox,
columnsBox
}
}
};
if (await dialog.ShowAsync() == ContentDialogResult.Primary)
{
await ExecuteAsync(EditorCommand.InsertTable(new EditorTableCommandArgs((int)Math.Max(1, rowsBox.Value), (int)Math.Max(1, columnsBox.Value))));
}
}
}
File diff suppressed because it is too large Load Diff