Fix alingment issues when dealing with inlines

This commit is contained in:
Tyrrrz
2026-02-24 21:05:03 +02:00
parent 12d98e9ab0
commit 5f8054e2f0
3 changed files with 98 additions and 99 deletions

View File

@@ -3,7 +3,6 @@ using System.Globalization;
using System.Linq; using System.Linq;
using Avalonia.Controls.Documents; using Avalonia.Controls.Documents;
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using DiscordChatExporter.Gui.Utils.Extensions; using DiscordChatExporter.Gui.Utils.Extensions;
using DiscordChatExporter.Gui.Views.Controls; using DiscordChatExporter.Gui.Views.Controls;
@@ -34,7 +33,10 @@ public class MarkdownToInlinesConverter : IValueConverter
{ {
case LiteralInline literal: case LiteralInline literal:
{ {
var run = new Run(literal.Content.ToString()); var run = new Run(literal.Content.ToString())
{
BaselineAlignment = BaselineAlignment.Center,
};
if (fontWeight is not null) if (fontWeight is not null)
run.FontWeight = fontWeight.Value; run.FontWeight = fontWeight.Value;
@@ -83,17 +85,7 @@ public class MarkdownToInlinesConverter : IValueConverter
case LinkInline link: case LinkInline link:
{ {
inlines.Add( inlines.Add(new HyperLink { Text = link.GetInnerText(), Url = link.Url });
new InlineUIContainer(
new HyperLink
{
Text = link.GetInnerText(),
Url = link.Url,
VerticalAlignment = VerticalAlignment.Bottom,
}
)
);
break; break;
} }
@@ -149,13 +141,18 @@ public class MarkdownToInlinesConverter : IValueConverter
isFirst = false; isFirst = false;
var prefix = list.IsOrdered ? $"{itemOrder++}. " : $"{list.BulletType} "; var prefix = list.IsOrdered ? $"{itemOrder++}. " : $"{list.BulletType} ";
inlines.Add(new Run(prefix));
inlines.Add(
new Run(prefix) { BaselineAlignment = BaselineAlignment.Center }
);
foreach (var subBlock in listItem.OfType<ParagraphBlock>()) foreach (var subBlock in listItem.OfType<ParagraphBlock>())
{ {
if (subBlock is { Inline: not null } p) if (subBlock is { Inline: not null })
foreach (var markdownInline in p.Inline) {
foreach (var markdownInline in subBlock.Inline)
ProcessInline(inlines, markdownInline); ProcessInline(inlines, markdownInline);
}
} }
} }

View File

@@ -1,12 +1,11 @@
using System.Linq; using System.Linq;
using Markdig.Syntax.Inlines; using Markdig.Syntax.Inlines;
using MarkdownInline = Markdig.Syntax.Inlines.Inline;
namespace DiscordChatExporter.Gui.Utils.Extensions; namespace DiscordChatExporter.Gui.Utils.Extensions;
internal static class MarkdigExtensions internal static class MarkdigExtensions
{ {
extension(MarkdownInline inline) extension(Inline inline)
{ {
public string GetInnerText() => public string GetInnerText() =>
inline switch inline switch

View File

@@ -1,54 +1,54 @@
<UserControl <UserControl
Loaded="UserControl_OnLoaded"
x:Class="DiscordChatExporter.Gui.Views.Components.DashboardView" x:Class="DiscordChatExporter.Gui.Views.Components.DashboardView"
x:DataType="components:DashboardViewModel"
x:Name="UserControl"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:asyncImageLoader="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia" xmlns:asyncImageLoader="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia"
xmlns:components="clr-namespace:DiscordChatExporter.Gui.ViewModels.Components" xmlns:components="clr-namespace:DiscordChatExporter.Gui.ViewModels.Components"
xmlns:converters="clr-namespace:DiscordChatExporter.Gui.Converters" xmlns:converters="clr-namespace:DiscordChatExporter.Gui.Converters"
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:materialStyles="clr-namespace:Material.Styles.Controls;assembly=Material.Styles" xmlns:materialStyles="clr-namespace:Material.Styles.Controls;assembly=Material.Styles"
x:Name="UserControl" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
x:DataType="components:DashboardViewModel"
Loaded="UserControl_OnLoaded">
<DockPanel> <DockPanel>
<!-- Header --> <!-- Header -->
<StackPanel <StackPanel
Background="{DynamicResource MaterialDarkBackgroundBrush}" Background="{DynamicResource MaterialDarkBackgroundBrush}"
DockPanel.Dock="Top" DockPanel.Dock="Top"
Orientation="Vertical"> Orientation="Vertical">
<Grid Margin="12,12,8,12" ColumnDefinitions="*,Auto"> <Grid ColumnDefinitions="*,Auto" Margin="12,12,8,12">
<materialStyles:Card Grid.Column="0"> <materialStyles:Card Grid.Column="0">
<!-- Token --> <!-- Token -->
<TextBox <TextBox
x:Name="TokenValueTextBox"
FontSize="16" FontSize="16"
PasswordChar="*" PasswordChar="*"
RevealPassword="{Binding $self.IsFocused}" RevealPassword="{Binding $self.IsFocused}"
Text="{Binding Token}" Text="{Binding Token}"
Theme="{DynamicResource SoloTextBox}" Theme="{DynamicResource SoloTextBox}"
Watermark="{Binding LocalizationManager.TokenWatermark}"> Watermark="{Binding LocalizationManager.TokenWatermark}"
x:Name="TokenValueTextBox">
<TextBox.InnerLeftContent> <TextBox.InnerLeftContent>
<materialIcons:MaterialIcon <materialIcons:MaterialIcon
Grid.Column="0"
Width="24"
Height="24"
Margin="4,0,8,0"
Foreground="{DynamicResource PrimaryHueMidBrush}" Foreground="{DynamicResource PrimaryHueMidBrush}"
Kind="Key" /> Grid.Column="0"
Height="24"
Kind="Key"
Margin="4,0,8,0"
Width="24" />
</TextBox.InnerLeftContent> </TextBox.InnerLeftContent>
<TextBox.InnerRightContent> <TextBox.InnerRightContent>
<Button <Button
Command="{Binding PullGuildsCommand}"
Grid.Column="2" Grid.Column="2"
IsDefault="True"
Margin="8,0,0,0" Margin="8,0,0,0"
Padding="4" Padding="4"
Command="{Binding PullGuildsCommand}"
IsDefault="True"
Theme="{DynamicResource MaterialFlatButton}" Theme="{DynamicResource MaterialFlatButton}"
ToolTip.Tip="{Binding LocalizationManager.PullGuildsTooltip}"> ToolTip.Tip="{Binding LocalizationManager.PullGuildsTooltip}">
<materialIcons:MaterialIcon <materialIcons:MaterialIcon
Width="24"
Height="24" Height="24"
Kind="ArrowRight" /> Kind="ArrowRight"
Width="24" />
</Button> </Button>
</TextBox.InnerRightContent> </TextBox.InnerRightContent>
</TextBox> </TextBox>
@@ -56,25 +56,25 @@
<!-- Settings button --> <!-- Settings button -->
<Button <Button
Command="{Binding ShowSettingsCommand}"
Foreground="{DynamicResource MaterialDarkForegroundBrush}"
Grid.Column="1" Grid.Column="1"
Margin="8,0,0,0" Margin="8,0,0,0"
Padding="8" Padding="8"
VerticalAlignment="Center"
Command="{Binding ShowSettingsCommand}"
Foreground="{DynamicResource MaterialDarkForegroundBrush}"
Theme="{DynamicResource MaterialFlatButton}" Theme="{DynamicResource MaterialFlatButton}"
ToolTip.Tip="{Binding LocalizationManager.SettingsTooltip}"> ToolTip.Tip="{Binding LocalizationManager.SettingsTooltip}"
VerticalAlignment="Center">
<materialIcons:MaterialIcon <materialIcons:MaterialIcon
Width="24"
Height="24" Height="24"
Kind="Settings" /> Kind="Settings"
Width="24" />
</Button> </Button>
</Grid> </Grid>
<!-- Progress --> <!-- Progress -->
<ProgressBar <ProgressBar
Height="2"
Background="Transparent" Background="Transparent"
Height="2"
IsIndeterminate="{Binding IsProgressIndeterminate}" IsIndeterminate="{Binding IsProgressIndeterminate}"
Value="{Binding Progress.Current.Fraction, Mode=OneWay}" /> Value="{Binding Progress.Current.Fraction, Mode=OneWay}" />
</StackPanel> </StackPanel>
@@ -95,16 +95,16 @@
<Grid ColumnDefinitions="Auto,*" IsVisible="{Binding !!AvailableGuilds.Count}"> <Grid ColumnDefinitions="Auto,*" IsVisible="{Binding !!AvailableGuilds.Count}">
<!-- Guilds --> <!-- Guilds -->
<Border <Border
Grid.Column="0"
BorderBrush="{DynamicResource MaterialDividerBrush}" BorderBrush="{DynamicResource MaterialDividerBrush}"
BorderThickness="0,0,1,0"> BorderThickness="0,0,1,0"
Grid.Column="0">
<ListBox <ListBox
x:Name="AvailableGuildsListBox"
ItemsSource="{Binding AvailableGuilds}" ItemsSource="{Binding AvailableGuilds}"
ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden"
SelectedItem="{Binding SelectedGuild}" SelectedItem="{Binding SelectedGuild}"
SelectionChanged="AvailableGuildsListBox_OnSelectionChanged" SelectionChanged="AvailableGuildsListBox_OnSelectionChanged"
SelectionMode="Single"> SelectionMode="Single"
x:Name="AvailableGuildsListBox">
<ListBox.Styles> <ListBox.Styles>
<Style Selector="ListBox"> <Style Selector="ListBox">
<Style Selector="^ ListBoxItem"> <Style Selector="^ ListBoxItem">
@@ -118,16 +118,16 @@
<Panel Background="Transparent" ToolTip.Tip="{Binding Name}"> <Panel Background="Transparent" ToolTip.Tip="{Binding Name}">
<!-- Guild icon placeholder --> <!-- Guild icon placeholder -->
<Ellipse <Ellipse
Width="48" Fill="{DynamicResource MaterialDividerBrush}"
Height="48" Height="48"
Margin="12" Margin="12"
Fill="{DynamicResource MaterialDividerBrush}" /> Width="48" />
<!-- Guild icon --> <!-- Guild icon -->
<Ellipse <Ellipse
Width="48"
Height="48" Height="48"
Margin="12"> Margin="12"
Width="48">
<Ellipse.Fill> <Ellipse.Fill>
<ImageBrush asyncImageLoader:ImageBrushLoader.Source="{Binding IconUrl}" /> <ImageBrush asyncImageLoader:ImageBrushLoader.Source="{Binding IconUrl}" />
</Ellipse.Fill> </Ellipse.Fill>
@@ -141,12 +141,12 @@
<!-- Channels --> <!-- Channels -->
<Border Grid.Column="1"> <Border Grid.Column="1">
<TreeView <TreeView
x:Name="AvailableChannelsTreeView"
ItemsSource="{Binding AvailableChannels}" ItemsSource="{Binding AvailableChannels}"
SelectedItems="{Binding SelectedChannels}" SelectedItems="{Binding SelectedChannels}"
SelectionChanged="AvailableChannelsTreeView_OnSelectionChanged" SelectionChanged="AvailableChannelsTreeView_OnSelectionChanged"
SelectionMode="Multiple" SelectionMode="Multiple"
TextSearch.Text="Name"> TextSearch.Text="Name"
x:Name="AvailableChannelsTreeView">
<TreeView.Styles> <TreeView.Styles>
<Style Selector="TreeView"> <Style Selector="TreeView">
<Style Selector="^ TreeViewItem"> <Style Selector="^ TreeViewItem">
@@ -178,10 +178,10 @@
<!-- Channel icon --> <!-- Channel icon -->
<materialIcons:MaterialIcon <materialIcons:MaterialIcon
Grid.Column="0"
Margin="0,0,4,0"
Classes.voice="{Binding Channel.IsVoice}" Classes.voice="{Binding Channel.IsVoice}"
IsVisible="{Binding !Channel.IsCategory}"> Grid.Column="0"
IsVisible="{Binding !Channel.IsCategory}"
Margin="0,0,4,0">
<materialIcons:MaterialIcon.Styles> <materialIcons:MaterialIcon.Styles>
<Style Selector="materialIcons|MaterialIcon"> <Style Selector="materialIcons|MaterialIcon">
<Setter Property="Kind" Value="Pound" /> <Setter Property="Kind" Value="Pound" />
@@ -195,19 +195,19 @@
<!-- Channel name --> <!-- Channel name -->
<TextBlock <TextBlock
FontSize="14"
Grid.Column="1" Grid.Column="1"
Margin="0,12" Margin="0,12"
FontSize="14"
Text="{Binding Channel.Name, Mode=OneWay}" /> Text="{Binding Channel.Name, Mode=OneWay}" />
<!-- Checkmark --> <!-- Checkmark -->
<materialIcons:MaterialIcon <materialIcons:MaterialIcon
Grid.Column="2" Grid.Column="2"
Width="24"
Height="24" Height="24"
Margin="16,0"
IsVisible="{Binding $parent[TreeViewItem].IsSelected}" IsVisible="{Binding $parent[TreeViewItem].IsSelected}"
Kind="Check" /> Kind="Check"
Margin="16,0"
Width="24" />
</Grid> </Grid>
</TreeDataTemplate> </TreeDataTemplate>
</TreeView.ItemTemplate> </TreeView.ItemTemplate>
@@ -219,86 +219,89 @@
<Panel IsVisible="{Binding !AvailableGuilds.Count}"> <Panel IsVisible="{Binding !AvailableGuilds.Count}">
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto"> <ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel Margin="32,16" Spacing="0"> <StackPanel Margin="32,16" Spacing="0">
<!-- User token --> <!-- User token -->
<TextBlock> <TextBlock>
<InlineUIContainer> <InlineUIContainer>
<materialIcons:MaterialIcon <materialIcons:MaterialIcon
Width="18"
Height="18"
Margin="0,-2,0,0"
Foreground="{DynamicResource PrimaryHueMidBrush}" Foreground="{DynamicResource PrimaryHueMidBrush}"
Kind="Account" /> Height="18"
Kind="Account"
Width="18" />
</InlineUIContainer> </InlineUIContainer>
<Run Text=" " /> <Run BaselineAlignment="Center" Text="" />
<Run <Run
BaselineAlignment="Center"
FontSize="16" FontSize="16"
FontWeight="SemiBold" FontWeight="SemiBold"
Text="{Binding LocalizationManager.TokenPersonalHeader}" /> Text="{Binding LocalizationManager.TokenPersonalHeader}" />
</TextBlock> </TextBlock>
<TextBlock Inlines="{Binding LocalizationManager.TokenPersonalTosWarning, Converter={x:Static converters:MarkdownToInlinesConverter.Instance}}" <TextBlock
FontSize="14" FontSize="14"
FontWeight="Light" FontWeight="Light"
LineHeight="23" Inlines="{Binding LocalizationManager.TokenPersonalTosWarning, Converter={x:Static converters:MarkdownToInlinesConverter.Instance}}"
TextWrapping="Wrap" /> LineHeight="23"
TextWrapping="Wrap" />
<TextBlock Inlines="{Binding LocalizationManager.TokenPersonalInstructions, Converter={x:Static converters:MarkdownToInlinesConverter.Instance}}" <TextBlock
FontSize="14" FontSize="14"
FontWeight="Light" FontWeight="Light"
LineHeight="23" Inlines="{Binding LocalizationManager.TokenPersonalInstructions, Converter={x:Static converters:MarkdownToInlinesConverter.Instance}}"
TextWrapping="Wrap" /> LineHeight="23"
TextWrapping="Wrap" />
<!-- Bot token --> <!-- Bot token -->
<TextBlock Margin="0,12,0,0"> <TextBlock Margin="0,12,0,0">
<InlineUIContainer> <InlineUIContainer>
<materialIcons:MaterialIcon <materialIcons:MaterialIcon
Width="18"
Height="18"
Margin="0,-2,0,0"
Foreground="{DynamicResource PrimaryHueMidBrush}" Foreground="{DynamicResource PrimaryHueMidBrush}"
Kind="Robot" /> Height="18"
Kind="Robot"
Width="18" />
</InlineUIContainer> </InlineUIContainer>
<Run Text=" " /> <Run BaselineAlignment="Center" Text="" />
<Run <Run
BaselineAlignment="Center"
FontSize="16" FontSize="16"
FontWeight="SemiBold" FontWeight="SemiBold"
Text="{Binding LocalizationManager.TokenBotHeader}" /> Text="{Binding LocalizationManager.TokenBotHeader}" />
</TextBlock> </TextBlock>
<TextBlock Inlines="{Binding LocalizationManager.TokenBotInstructions, Converter={x:Static converters:MarkdownToInlinesConverter.Instance}}" <TextBlock
FontSize="14" FontSize="14"
FontWeight="Light" FontWeight="Light"
LineHeight="23" Inlines="{Binding LocalizationManager.TokenBotInstructions, Converter={x:Static converters:MarkdownToInlinesConverter.Instance}}"
TextWrapping="Wrap" /> LineHeight="23"
TextWrapping="Wrap" />
<TextBlock Margin="0,12,0,0" <TextBlock
FontSize="14" FontSize="14"
FontWeight="Light" FontWeight="Light"
LineHeight="23" Inlines="{Binding LocalizationManager.TokenHelpText, Converter={x:Static converters:MarkdownToInlinesConverter.Instance}}"
TextWrapping="Wrap" LineHeight="23"
Inlines="{Binding LocalizationManager.TokenHelpText, Converter={x:Static converters:MarkdownToInlinesConverter.Instance}}" /> Margin="0,12,0,0"
TextWrapping="Wrap" />
</StackPanel> </StackPanel>
</ScrollViewer> </ScrollViewer>
</Panel> </Panel>
<!-- Export button --> <!-- Export button -->
<Button <Button
Width="56"
Height="56"
Margin="32,24"
Padding="0"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Background="{DynamicResource MaterialSecondaryMidBrush}" Background="{DynamicResource MaterialSecondaryMidBrush}"
Command="{Binding ExportCommand}" Command="{Binding ExportCommand}"
Foreground="{DynamicResource MaterialSecondaryMidForegroundBrush}" Foreground="{DynamicResource MaterialSecondaryMidForegroundBrush}"
Height="56"
HorizontalAlignment="Right"
IsVisible="{Binding $self.IsEffectivelyEnabled}" IsVisible="{Binding $self.IsEffectivelyEnabled}"
Theme="{DynamicResource MaterialIconButton}"> Margin="32,24"
Padding="0"
Theme="{DynamicResource MaterialIconButton}"
VerticalAlignment="Bottom"
Width="56">
<materialIcons:MaterialIcon <materialIcons:MaterialIcon
Width="32"
Height="32" Height="32"
Kind="Download" /> Kind="Download"
Width="32" />
</Button> </Button>
</Panel> </Panel>
</DockPanel> </DockPanel>