diff --git a/components/MarkdownTextBlock/src/HtmlWriter.cs b/components/MarkdownTextBlock/src/HtmlWriter.cs index 1b2557992..40c4b932c 100644 --- a/components/MarkdownTextBlock/src/HtmlWriter.cs +++ b/components/MarkdownTextBlock/src/HtmlWriter.cs @@ -49,6 +49,7 @@ public static void WriteHtml(WinUIRenderer renderer, HtmlNodeCollection nodes) else { var myHyperlink = new MyHyperlink(node, renderer.Config.BaseUrl); + myHyperlink.TextElement.Foreground = renderer.Config.Themes.LinkForeground; myHyperlink.ClickEvent += (sender, e) => { var uri = sender.NavigateUri; diff --git a/components/MarkdownTextBlock/src/MarkdownTextBlock.Properties.cs b/components/MarkdownTextBlock/src/MarkdownTextBlock.Properties.cs index 8dae4cad1..06ad510cc 100644 --- a/components/MarkdownTextBlock/src/MarkdownTextBlock.Properties.cs +++ b/components/MarkdownTextBlock/src/MarkdownTextBlock.Properties.cs @@ -108,6 +108,23 @@ public partial class MarkdownTextBlock typeof(MarkdownTextBlock), new PropertyMetadata(null)); + /// + /// Identifies the dependency property. + /// + private static readonly DependencyProperty IsTextSelectionEnabledProperty = DependencyProperty.Register( + nameof(IsTextSelectionEnabled), + typeof(bool), + typeof(MarkdownTextBlock), + new PropertyMetadata(false, OnIsTextSelectionEnabledChanged)); + + private static void OnIsTextSelectionEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is MarkdownTextBlock mtb && mtb._document != null) + { + mtb._document.RichTextBlock.IsTextSelectionEnabled = (bool)e.NewValue; + } + } + public MarkdownConfig Config { get => (MarkdownConfig)GetValue(ConfigProperty); @@ -203,4 +220,13 @@ public MarkdownDocument? MarkdownDocument get => (MarkdownDocument)GetValue(MarkdownDocumentProperty); private set => SetValue(MarkdownDocumentProperty, value); } + + /// + /// Gets or sets a value indicating whether text selection is enabled. + /// + public bool IsTextSelectionEnabled + { + get => (bool)GetValue(IsTextSelectionEnabledProperty); + set => SetValue(IsTextSelectionEnabledProperty, value); + } } diff --git a/components/MarkdownTextBlock/src/MarkdownThemes.cs b/components/MarkdownTextBlock/src/MarkdownThemes.cs index d29ec9bf5..ad0317cfd 100644 --- a/components/MarkdownTextBlock/src/MarkdownThemes.cs +++ b/components/MarkdownTextBlock/src/MarkdownThemes.cs @@ -34,9 +34,14 @@ public sealed class MarkdownThemes : DependencyObject public double H6FontSize { get; set; } = 12; - public Brush HeadingForeground { get; set; } = Extensions.GetAccentColorBrush(); + public Brush H1Foreground { get; set; } = (Brush)Application.Current.Resources["TextFillColorPrimaryBrush"]; + public Brush H2Foreground { get; set; } = (Brush)Application.Current.Resources["TextFillColorPrimaryBrush"]; + public Brush H3Foreground { get; set; } = (Brush)Application.Current.Resources["TextFillColorPrimaryBrush"]; + public Brush H4Foreground { get; set; } = (Brush)Application.Current.Resources["TextFillColorPrimaryBrush"]; + public Brush H5Foreground { get; set; } = (Brush)Application.Current.Resources["TextFillColorPrimaryBrush"]; + public Brush H6Foreground { get; set; } = (Brush)Application.Current.Resources["TextFillColorPrimaryBrush"]; - public FontWeight H1FontWeight { get; set; } = FontWeights.Bold; + public FontWeight H1FontWeight { get; set; } = FontWeights.SemiBold; public FontWeight H2FontWeight { get; set; } = FontWeights.Normal; @@ -48,10 +53,10 @@ public sealed class MarkdownThemes : DependencyObject public FontWeight H6FontWeight { get; set; } = FontWeights.Normal; - public Thickness H1Margin { get; set; } = new(left: 0, top: 14, right: 0, bottom: 0); - public Thickness H2Margin { get; set; } = new(left: 0, top: 14, right: 0, bottom: 0); - public Thickness H3Margin { get; set; } = new(left: 0, top: 14, right: 0, bottom: 0); - public Thickness H4Margin { get; set; } = new(left: 0, top: 14, right: 0, bottom: 0); + public Thickness H1Margin { get; set; } = new(left: 0, top: 16, right: 0, bottom: 0); + public Thickness H2Margin { get; set; } = new(left: 0, top: 16, right: 0, bottom: 0); + public Thickness H3Margin { get; set; } = new(left: 0, top: 16, right: 0, bottom: 0); + public Thickness H4Margin { get; set; } = new(left: 0, top: 16, right: 0, bottom: 0); public Thickness H5Margin { get; set; } = new(left: 0, top: 8, right: 0, bottom: 0); public Thickness H6Margin { get; set; } = new(left: 0, top: 8, right: 0, bottom: 0); @@ -73,4 +78,54 @@ public sealed class MarkdownThemes : DependencyObject public double InlineCodeFontSize { get; set; } = 10; public FontWeight InlineCodeFontWeight { get; set; } = FontWeights.Normal; + + // Legacy parity properties (new) + // Code block styling + public Brush CodeBlockBackground { get; set; } = (Brush)Application.Current.Resources["ExpanderHeaderBackground"]; + public Brush CodeBlockBorderBrush { get; set; } = new SolidColorBrush(Colors.Gray); + public Thickness CodeBlockBorderThickness { get; set; } = new Thickness(1); + public Thickness CodeBlockPadding { get; set; } = new Thickness(8); + public Thickness CodeBlockMargin { get; set; } = new Thickness(0, 8, 0, 8); + public FontFamily CodeBlockFontFamily { get; set; } = new FontFamily("Consolas"); + public Brush CodeBlockForeground { get; set; } = (Brush)Application.Current.Resources["TextFillColorPrimaryBrush"]; + public CornerRadius CodeBlockCornerRadius { get; set; } = new CornerRadius(4); + + // Horizontal rule + public Brush HorizontalRuleBrush { get; set; } = new SolidColorBrush(Colors.Gray); + public double HorizontalRuleThickness { get; set; } = 1.0; + public Thickness HorizontalRuleMargin { get; set; } = new Thickness(0, 12, 0, 12); + + // Link styling + public Brush LinkForeground { get; set; } = (Brush)Application.Current.Resources["AccentTextFillColorPrimaryBrush"] ?? new SolidColorBrush(Colors.DodgerBlue); + + // Paragraph / list + public Thickness ParagraphMargin { get; set; } = new Thickness(0, 8, 0, 8); + public double ParagraphLineHeight { get; set; } = 0; // 0 = default + public double ListBulletSpacing { get; set; } = 4; // spaces after bullet + public double ListGutterWidth { get; set; } = 30; // indent delta per level + public Thickness ListMargin { get; set; } = new Thickness(0, 4, 0, 4); + + // Quote styling + public Brush QuoteBackground { get; set; } = new SolidColorBrush(Colors.Transparent); + public Brush QuoteBorderBrush { get; set; } = new SolidColorBrush(Colors.Gray); + public Thickness QuoteBorderThickness { get; set; } = new Thickness(4, 0, 0, 0); + public Brush QuoteForeground { get; set; } = (Brush)Application.Current.Resources["TextFillColorPrimaryBrush"]; + public Thickness QuoteMargin { get; set; } = new Thickness(0, 4, 0, 4); + public Thickness QuotePadding { get; set; } = new Thickness(4); + public CornerRadius QuoteCornerRadius { get; set; } = new CornerRadius(4); + + // Image styling + public double ImageMaxWidth { get; set; } = 0; // 0 = no constraint + public double ImageMaxHeight { get; set; } = 0; + public Stretch ImageStretch { get; set; } = Stretch.Uniform; + + // Table styling + public Brush TableBorderBrush { get; set; } = new SolidColorBrush(Colors.Gray); + public double TableBorderThickness { get; set; } = 1; + public Thickness TableCellPadding { get; set; } = new Thickness(4); + public Thickness TableMargin { get; set; } = new Thickness(0, 10, 0, 10); + + // YAML / not currently used - placeholders for parity + public Brush YamlBorderBrush { get; set; } = new SolidColorBrush(Colors.Gray); + public Thickness YamlBorderThickness { get; set; } = new Thickness(1); } diff --git a/components/MarkdownTextBlock/src/Renderers/ObjectRenderers/Inlines/LinkInlineRenderer.cs b/components/MarkdownTextBlock/src/Renderers/ObjectRenderers/Inlines/LinkInlineRenderer.cs index 08b290ea1..c514c45de 100644 --- a/components/MarkdownTextBlock/src/Renderers/ObjectRenderers/Inlines/LinkInlineRenderer.cs +++ b/components/MarkdownTextBlock/src/Renderers/ObjectRenderers/Inlines/LinkInlineRenderer.cs @@ -43,11 +43,14 @@ protected override void Write(WinUIRenderer renderer, LinkInline link) // Optionally restore later; not needed unless reused. } }; + // Apply link foreground to nested RichTextBlock content + // (Handled in MyHyperlinkButton initialization via MarkdownConfig.Default for now) renderer.Push(myHyperlinkButton); } else { var hyperlink = new MyHyperlink(link, renderer.Config.BaseUrl); + hyperlink.TextElement.Foreground = renderer.Config.Themes.LinkForeground; hyperlink.ClickEvent += (sender, e) => { var uri = sender.NavigateUri; diff --git a/components/MarkdownTextBlock/src/Renderers/ObjectRenderers/QuoteBlockRenderer.cs b/components/MarkdownTextBlock/src/Renderers/ObjectRenderers/QuoteBlockRenderer.cs index 139db86d9..bdc1b3210 100644 --- a/components/MarkdownTextBlock/src/Renderers/ObjectRenderers/QuoteBlockRenderer.cs +++ b/components/MarkdownTextBlock/src/Renderers/ObjectRenderers/QuoteBlockRenderer.cs @@ -14,7 +14,7 @@ protected override void Write(WinUIRenderer renderer, QuoteBlock obj) if (renderer == null) throw new ArgumentNullException(nameof(renderer)); if (obj == null) throw new ArgumentNullException(nameof(obj)); - var quote = new MyQuote(obj); + var quote = new MyQuote(obj, renderer.Config.Themes); renderer.Push(quote); renderer.WriteChildren(obj); diff --git a/components/MarkdownTextBlock/src/TextElements/MyCodeBlock.cs b/components/MarkdownTextBlock/src/TextElements/MyCodeBlock.cs index c60109e0d..f7502b9e3 100644 --- a/components/MarkdownTextBlock/src/TextElements/MyCodeBlock.cs +++ b/components/MarkdownTextBlock/src/TextElements/MyCodeBlock.cs @@ -24,11 +24,15 @@ public MyCodeBlock(CodeBlock codeBlock, MarkdownConfig config) _paragraph = new Paragraph(); var container = new InlineUIContainer(); var border = new Border(); - border.Background = (Brush)Application.Current.Resources["ExpanderHeaderBackground"]; - border.Padding = _config.Themes.Padding; - border.Margin = _config.Themes.InternalMargin; - border.CornerRadius = _config.Themes.CornerRadius; + border.Background = _config.Themes.CodeBlockBackground; + border.BorderBrush = _config.Themes.CodeBlockBorderBrush; + border.BorderThickness = _config.Themes.CodeBlockBorderThickness; + border.Padding = _config.Themes.CodeBlockPadding; + border.Margin = _config.Themes.CodeBlockMargin; + border.CornerRadius = _config.Themes.CodeBlockCornerRadius; var richTextBlock = new RichTextBlock(); + richTextBlock.FontFamily = _config.Themes.CodeBlockFontFamily; + richTextBlock.Foreground = _config.Themes.CodeBlockForeground; #if false if (codeBlock is FencedCodeBlock fencedCodeBlock) diff --git a/components/MarkdownTextBlock/src/TextElements/MyHeading.cs b/components/MarkdownTextBlock/src/TextElements/MyHeading.cs index 43ceae327..ab8a85392 100644 --- a/components/MarkdownTextBlock/src/TextElements/MyHeading.cs +++ b/components/MarkdownTextBlock/src/TextElements/MyHeading.cs @@ -60,7 +60,15 @@ private void SetHProperties(int level) 5 => _config.Themes.H5FontSize, _ => _config.Themes.H6FontSize, }; - _paragraph.Foreground = _config.Themes.HeadingForeground; + _paragraph.Foreground = level switch + { + 1 => _config.Themes.H1Foreground, + 2 => _config.Themes.H2Foreground, + 3 => _config.Themes.H3Foreground, + 4 => _config.Themes.H4Foreground, + 5 => _config.Themes.H5Foreground, + _ => _config.Themes.H6Foreground, + }; _paragraph.FontWeight = level switch { 1 => _config.Themes.H1FontWeight, diff --git a/components/MarkdownTextBlock/src/TextElements/MyHyperlink.cs b/components/MarkdownTextBlock/src/TextElements/MyHyperlink.cs index bb4feea3d..fdbb9ad6d 100644 --- a/components/MarkdownTextBlock/src/TextElements/MyHyperlink.cs +++ b/components/MarkdownTextBlock/src/TextElements/MyHyperlink.cs @@ -41,6 +41,7 @@ public MyHyperlink(LinkInline linkInline, string? baseUrl) _hyperlink = new Hyperlink() { NavigateUri = Extensions.GetUri(url, baseUrl), + Foreground = MarkdownConfig.Default.Themes.LinkForeground }; } @@ -52,6 +53,7 @@ public MyHyperlink(HtmlNode htmlNode, string? baseUrl) _hyperlink = new Hyperlink() { NavigateUri = Extensions.GetUri(url, baseUrl), + Foreground = MarkdownConfig.Default.Themes.LinkForeground }; } diff --git a/components/MarkdownTextBlock/src/TextElements/MyHyperlinkButton.cs b/components/MarkdownTextBlock/src/TextElements/MyHyperlinkButton.cs index 927083ac4..594d6fd66 100644 --- a/components/MarkdownTextBlock/src/TextElements/MyHyperlinkButton.cs +++ b/components/MarkdownTextBlock/src/TextElements/MyHyperlinkButton.cs @@ -65,7 +65,8 @@ private MyHyperlinkButton(string? url, string? baseUrl, HtmlNode? htmlNode, Link _flowDoc = new MyFlowDocument(_linkInline!); } _inlineUIContainer.Child = _hyperLinkButton; - _hyperLinkButton.Content = _flowDoc.RichTextBlock; + _flowDoc.RichTextBlock.Foreground = MarkdownConfig.Default.Themes.LinkForeground; + _hyperLinkButton.Content = _flowDoc.RichTextBlock; } public void AddChild(IAddChild child) diff --git a/components/MarkdownTextBlock/src/TextElements/MyImage.cs b/components/MarkdownTextBlock/src/TextElements/MyImage.cs index 4a2e35df8..64b94fd84 100644 --- a/components/MarkdownTextBlock/src/TextElements/MyImage.cs +++ b/components/MarkdownTextBlock/src/TextElements/MyImage.cs @@ -150,6 +150,18 @@ private async void LoadImage(object sender, RoutedEventArgs e) { _image.Height = _precedentHeight; } + + // Apply theme constraints if provided + var themes = MarkdownConfig.Default.Themes; + if (themes.ImageMaxWidth > 0) + { + _image.MaxWidth = themes.ImageMaxWidth; + } + if (themes.ImageMaxHeight > 0) + { + _image.MaxHeight = themes.ImageMaxHeight; + } + _image.Stretch = themes.ImageStretch; } catch (Exception) { } } diff --git a/components/MarkdownTextBlock/src/TextElements/MyParagraph.cs b/components/MarkdownTextBlock/src/TextElements/MyParagraph.cs index 25ef22618..65ffbf5a0 100644 --- a/components/MarkdownTextBlock/src/TextElements/MyParagraph.cs +++ b/components/MarkdownTextBlock/src/TextElements/MyParagraph.cs @@ -26,10 +26,15 @@ public MyParagraph(ParagraphBlock paragraphBlock, WinUIRenderer renderer) // Lists are plain Paragraph_s, one per item. // This is so that you can select across list items. - Thickness margin = new Thickness(0, 8, 0, 8); // renderer.Config.Themes.BlockMargin; + var themes = renderer.Config.Themes; + Thickness margin = themes.ParagraphMargin; int bulletCount = renderer.GetListBulletCount(); - margin.Left += 30 * bulletCount; + margin.Left += themes.ListGutterWidth * bulletCount; _paragraph.Margin = margin; + if (themes.ParagraphLineHeight > 0) + { + _paragraph.LineHeight = themes.ParagraphLineHeight; + } if (bulletCount != 0) { @@ -37,7 +42,7 @@ public MyParagraph(ParagraphBlock paragraphBlock, WinUIRenderer renderer) Run bulletRun = new Run { Text = bullet + "\t" }; _paragraph.Inlines.Add(bulletRun); - _paragraph.TextIndent = -30; + _paragraph.TextIndent = -themes.ListGutterWidth; } } diff --git a/components/MarkdownTextBlock/src/TextElements/MyQuote.cs b/components/MarkdownTextBlock/src/TextElements/MyQuote.cs index 077a22eb7..6633c5bdb 100644 --- a/components/MarkdownTextBlock/src/TextElements/MyQuote.cs +++ b/components/MarkdownTextBlock/src/TextElements/MyQuote.cs @@ -11,15 +11,17 @@ internal class MyQuote : IAddChild private Paragraph _paragraph; private MyFlowDocument _flowDocument; private QuoteBlock _quoteBlock; + private MarkdownThemes _themes; public TextElement TextElement { get => _paragraph; } - public MyQuote(QuoteBlock quoteBlock) + public MyQuote(QuoteBlock quoteBlock, MarkdownThemes themes) { _quoteBlock = quoteBlock; + _themes = themes; _paragraph = new Paragraph(); _flowDocument = new MyFlowDocument(quoteBlock); @@ -30,20 +32,24 @@ public MyQuote(QuoteBlock quoteBlock) grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Auto) }); var bar = new Grid(); - bar.Width = 4; - bar.Background = new SolidColorBrush(Colors.Gray); + var borderThickness = _themes.QuoteBorderThickness.Left > 0 ? _themes.QuoteBorderThickness.Left : 4; + bar.Width = borderThickness; + bar.Background = _themes.QuoteBorderBrush ?? new SolidColorBrush(Colors.Gray); bar.SetValue(Grid.ColumnProperty, 0); bar.VerticalAlignment = VerticalAlignment.Stretch; bar.Margin = new Thickness(0, 0, 4, 0); grid.Children.Add(bar); - var rightGrid = new Grid(); - rightGrid.Padding = new Thickness(4); + var rightGrid = new Grid(); + rightGrid.Padding = _themes.QuotePadding; + rightGrid.Background = _themes.QuoteBackground; + rightGrid.CornerRadius = _themes.QuoteCornerRadius; rightGrid.Children.Add(_flowDocument.RichTextBlock); + _flowDocument.RichTextBlock.Foreground = _themes.QuoteForeground; rightGrid.SetValue(Grid.ColumnProperty, 1); grid.Children.Add(rightGrid); - grid.Margin = new Thickness(0, 2, 0, 2); + grid.Margin = _themes.QuoteMargin; inlineUIContainer.Child = grid; diff --git a/components/MarkdownTextBlock/src/TextElements/MyTable.cs b/components/MarkdownTextBlock/src/TextElements/MyTable.cs index 423b9fbc8..2cd341cb5 100644 --- a/components/MarkdownTextBlock/src/TextElements/MyTable.cs +++ b/components/MarkdownTextBlock/src/TextElements/MyTable.cs @@ -29,10 +29,11 @@ public MyTable(Table table, MarkdownThemes themes) ( column, table.Count, - borderThickness: 1, - themes.BorderBrush, + borderThickness: themes.TableBorderThickness, + themes.TableBorderBrush ?? themes.BorderBrush, themes.TableHeadingBackground, - themes.CornerRadius + themes.CornerRadius, + themes.TableMargin ); var inlineUIContainer = new InlineUIContainer(); diff --git a/components/MarkdownTextBlock/src/TextElements/MyTableCell.cs b/components/MarkdownTextBlock/src/TextElements/MyTableCell.cs index a9ec2634a..1374b7afd 100644 --- a/components/MarkdownTextBlock/src/TextElements/MyTableCell.cs +++ b/components/MarkdownTextBlock/src/TextElements/MyTableCell.cs @@ -66,7 +66,8 @@ public MyTableCell(TableCell tableCell, TextAlignment textAlignment, bool isHead _ => HorizontalAlignment.Left, }; - _container.Padding = new Thickness(4); + // Use themed table cell padding if available + _container.Padding = MarkdownConfig.Default.Themes.TableCellPadding; if (_isHeader) { _flowDocument.RichTextBlock.FontWeight = FontWeights.Bold; diff --git a/components/MarkdownTextBlock/src/TextElements/MyTableUIElement.cs b/components/MarkdownTextBlock/src/TextElements/MyTableUIElement.cs index 585cf304a..687a9398c 100644 --- a/components/MarkdownTextBlock/src/TextElements/MyTableUIElement.cs +++ b/components/MarkdownTextBlock/src/TextElements/MyTableUIElement.cs @@ -18,13 +18,12 @@ internal partial class MyTableUIElement : Panel private double[]? _columnWidths; private double[]? _rowHeights; - public MyTableUIElement(int columnCount, int rowCount, double borderThickness, Brush borderBrush, Brush headingBrush, CornerRadius cornerRadius) + public MyTableUIElement(int columnCount, int rowCount, double borderThickness, Brush borderBrush, Brush headingBrush, CornerRadius cornerRadius, Thickness tableMargin) { _columnCount = columnCount; _rowCount = rowCount; _borderThickness = borderThickness; - - Margin = new Thickness(left: 0, top: 10, right: 0, bottom: 10); + Margin = tableMargin; Children.Add(new Border { diff --git a/components/MarkdownTextBlock/src/TextElements/MyThematicBreak.cs b/components/MarkdownTextBlock/src/TextElements/MyThematicBreak.cs index 152b16fb1..771ef37c2 100644 --- a/components/MarkdownTextBlock/src/TextElements/MyThematicBreak.cs +++ b/components/MarkdownTextBlock/src/TextElements/MyThematicBreak.cs @@ -26,9 +26,10 @@ public MyThematicBreak(ThematicBreakBlock thematicBreakBlock, MarkdownThemes the Line line = new Line { Stretch = Stretch.Fill, - Stroke = themes.BorderBrush, + Stroke = themes.HorizontalRuleBrush ?? themes.BorderBrush, X2 = 1, - Margin = new Thickness(0, 12, 0, 12) + StrokeThickness = themes.HorizontalRuleThickness, + Margin = themes.HorizontalRuleMargin }; inlineUIContainer.Child = line; _paragraph.Inlines.Add(inlineUIContainer);