diff --git a/AideDeJeu/AideDeJeu.Android/AideDeJeu.Android.csproj b/AideDeJeu/AideDeJeu.Android/AideDeJeu.Android.csproj index 7d23565a..0bf2caa0 100644 --- a/AideDeJeu/AideDeJeu.Android/AideDeJeu.Android.csproj +++ b/AideDeJeu/AideDeJeu.Android/AideDeJeu.Android.csproj @@ -58,11 +58,11 @@ - + 1.60.0 - - 0.0.0 + + 1.60.0 3.1.0.583944 diff --git a/AideDeJeu/AideDeJeu.UWP/AideDeJeu.UWP.csproj b/AideDeJeu/AideDeJeu.UWP/AideDeJeu.UWP.csproj index 7da7bbd6..eab109de 100644 --- a/AideDeJeu/AideDeJeu.UWP/AideDeJeu.UWP.csproj +++ b/AideDeJeu/AideDeJeu.UWP/AideDeJeu.UWP.csproj @@ -175,13 +175,13 @@ + + 1.60.0 + 1.60.0 - - 0.0.0 - 3.1.0.583944 diff --git a/AideDeJeu/AideDeJeu.iOS/AideDeJeu.iOS.csproj b/AideDeJeu/AideDeJeu.iOS/AideDeJeu.iOS.csproj index 2243de4d..9ae78abb 100644 --- a/AideDeJeu/AideDeJeu.iOS/AideDeJeu.iOS.csproj +++ b/AideDeJeu/AideDeJeu.iOS/AideDeJeu.iOS.csproj @@ -111,11 +111,11 @@ - + 1.60.0 - - 0.0.0 + + 1.60.0 3.1.0.583944 diff --git a/AideDeJeu/AideDeJeu/AideDeJeu.csproj b/AideDeJeu/AideDeJeu/AideDeJeu.csproj index eb916f8e..d7ea06a8 100644 --- a/AideDeJeu/AideDeJeu/AideDeJeu.csproj +++ b/AideDeJeu/AideDeJeu/AideDeJeu.csproj @@ -27,8 +27,8 @@ + - @@ -49,6 +49,7 @@ + diff --git a/AideDeJeu/AideDeJeu/MarkdownView/Extensions/GithubExtensions.cs b/AideDeJeu/AideDeJeu/MarkdownView/Extensions/GithubExtensions.cs new file mode 100644 index 00000000..794571db --- /dev/null +++ b/AideDeJeu/AideDeJeu/MarkdownView/Extensions/GithubExtensions.cs @@ -0,0 +1,30 @@ +namespace Xam.Forms.Markdown +{ + using System.Text.RegularExpressions; + + /// + /// A set o helper extensions for parsing Github common urls. + /// + public static class GithubExtensions + { + private static readonly Regex GithubRepoRegex = new Regex("http(s)?:\\/\\/github.com\\/([a-zA-Z0-9_-]+)\\/([a-zA-Z0-9_-]+)\\/((blob|tree)\\/([a-zA-Z0-9_-]+))?"); + + private const string GithubReadmeUrl = "https://raw.githubusercontent.com/{0}/{1}/{2}/README.md"; + + public static bool TryExtractGithubRawMarkdownUrl(string url, out string readmeUrl) + { + var match = GithubRepoRegex.Match(url); + if(match.Success) + { + var user = match.Groups[2].Value; + var repo = match.Groups[3].Value; + var branch = match.Groups.Count > 6 ? match.Groups[6].Value : "master"; + readmeUrl = string.Format(GithubReadmeUrl, user, repo, branch); + return true; + } + + readmeUrl = null; + return false; + } + } +} diff --git a/AideDeJeu/AideDeJeu/MarkdownView/Extensions/ImageExtensions.cs b/AideDeJeu/AideDeJeu/MarkdownView/Extensions/ImageExtensions.cs new file mode 100644 index 00000000..54c28f42 --- /dev/null +++ b/AideDeJeu/AideDeJeu/MarkdownView/Extensions/ImageExtensions.cs @@ -0,0 +1,52 @@ +namespace Xam.Forms.Markdown.Extensions +{ + using System; + using System.IO; + using System.Net; + using SkiaSharp; + using Xamarin.Forms; + using System.Diagnostics; + + public static class ImageExtensions + { + public static void RenderSvg(this Image view, string uri) + { + try + { + var req = (HttpWebRequest)WebRequest.Create(uri); + + var svg = new SkiaSharp.Extended.Svg.SKSvg(); + req.BeginGetResponse((ar) => + { + var res = (ar.AsyncState as HttpWebRequest).EndGetResponse(ar) as HttpWebResponse; + using (var stream = res.GetResponseStream()) + { + if (stream != null) + { + var picture = svg.Load(stream); + + using (var image = SKImage.FromPicture(picture, picture.CullRect.Size.ToSizeI())) + using (var data = image.Encode(SKEncodedImageFormat.Jpeg, 80)) + { + var ms = new MemoryStream(); + + if (data != null && !data.IsEmpty) + { + data.SaveTo(ms); + ms.Seek(0, SeekOrigin.Begin); + ms.Position = 0; + view.Source = ImageSource.FromStream(() => ms); + } + } + } + } + }, req); + + } + catch (Exception ex) + { + Debug.WriteLine($"Failed to render svg: {ex}"); + } + } + } +} diff --git a/AideDeJeu/AideDeJeu/MarkdownView/MarkdownStyle.cs b/AideDeJeu/AideDeJeu/MarkdownView/MarkdownStyle.cs new file mode 100644 index 00000000..4c609070 --- /dev/null +++ b/AideDeJeu/AideDeJeu/MarkdownView/MarkdownStyle.cs @@ -0,0 +1,21 @@ +namespace Xam.Forms.Markdown +{ + using Xamarin.Forms; + + public class MarkdownStyle + { + public FontAttributes Attributes { get; set; } = FontAttributes.None; + + public float FontSize { get; set; } = 12; + + public Color ForegroundColor { get; set; } = Color.Black; + + public Color BackgroundColor { get; set; } = Color.Transparent; + + public Color BorderColor { get; set; } + + public float BorderSize { get; set; } + + public string FontFamily { get; set; } + } +} diff --git a/AideDeJeu/AideDeJeu/MarkdownView/MarkdownTheme.cs b/AideDeJeu/AideDeJeu/MarkdownView/MarkdownTheme.cs new file mode 100644 index 00000000..244a5ca4 --- /dev/null +++ b/AideDeJeu/AideDeJeu/MarkdownView/MarkdownTheme.cs @@ -0,0 +1,204 @@ + +namespace Xam.Forms.Markdown +{ + using Xamarin.Forms; + + public class MarkdownTheme + { + public MarkdownTheme() + { + this.Paragraph = new MarkdownStyle + { + Attributes = FontAttributes.None, + BackgroundColor = Color.White, + FontSize = 12, + }; + + this.Heading1 = new MarkdownStyle + { + Attributes = FontAttributes.Bold, + BorderSize = 1, + FontSize = 26, + }; + + this.Heading2 = new MarkdownStyle + { + Attributes = FontAttributes.Bold, + BorderSize = 1, + FontSize = 22, + }; + + this.Heading3 = new MarkdownStyle + { + Attributes = FontAttributes.Bold, + FontSize = 20, + }; + + this.Heading4 = new MarkdownStyle + { + Attributes = FontAttributes.Bold, + FontSize = 18, + }; + + this.Heading5 = new MarkdownStyle + { + Attributes = FontAttributes.Bold, + FontSize = 16, + }; + + this.Heading6 = new MarkdownStyle + { + Attributes = FontAttributes.Bold, + FontSize = 14, + }; + + this.Link = new MarkdownStyle + { + Attributes = FontAttributes.None, + FontSize = 12, + }; + + this.Code = new MarkdownStyle + { + Attributes = FontAttributes.None, + FontSize = 12, + }; + + this.Quote = new MarkdownStyle + { + Attributes = FontAttributes.None, + BorderSize = 4, + FontSize = 12, + BackgroundColor = Color.Gray.MultiplyAlpha(.1), + }; + + this.Separator = new MarkdownStyle + { + BorderSize = 2, + }; + + this.TableHeader = new MarkdownStyle + { + Attributes = FontAttributes.Bold, + FontSize = 12, + BorderSize = 1, + ForegroundColor = Color.White, + BackgroundColor = Color.Silver, + }; + + // Platform specific properties + switch (Device.RuntimePlatform) + { + case Device.iOS: + Code.FontFamily = "Courier"; + break; + + case Device.Android: + Code.FontFamily = "monospace"; + break; + } + } + + public Color BackgroundColor { get; set; } + + public MarkdownStyle Paragraph { get; set; } + + public MarkdownStyle Heading1 { get; set; } + + public MarkdownStyle Heading2 { get; set; } + + public MarkdownStyle Heading3 { get; set; } + + public MarkdownStyle Heading4 { get; set; } + + public MarkdownStyle Heading5 { get; set; } + + public MarkdownStyle Heading6 { get; set; } + + public MarkdownStyle Quote { get; set; } + + public MarkdownStyle Separator { get; set; } + + public MarkdownStyle Link { get; set; } + + public MarkdownStyle Code { get; set; } + + public MarkdownStyle TableHeader { get; set; } + + public float Margin { get; set; } = 10; + } + + public class LightMarkdownTheme : MarkdownTheme + { + public LightMarkdownTheme() + { + this.BackgroundColor = DefaultBackgroundColor; + this.Paragraph.ForegroundColor = DefaultTextColor; + this.Heading1.ForegroundColor = DefaultTextColor; + this.Heading1.BorderColor = DefaultSeparatorColor; + this.Heading2.ForegroundColor = DefaultTextColor; + this.Heading2.BorderColor = DefaultSeparatorColor; + this.Heading3.ForegroundColor = DefaultTextColor; + this.Heading4.ForegroundColor = DefaultTextColor; + this.Heading5.ForegroundColor = DefaultTextColor; + this.Heading6.ForegroundColor = DefaultTextColor; + this.Link.ForegroundColor = DefaultAccentColor; + this.Code.ForegroundColor = DefaultTextColor; + this.Code.BackgroundColor = DefaultCodeBackground; + this.Quote.ForegroundColor = DefaultQuoteTextColor; + this.Quote.BorderColor = DefaultQuoteBorderColor; + this.Separator.BorderColor = DefaultSeparatorColor; + } + + public static readonly Color DefaultBackgroundColor = Color.FromHex("#ffffff"); + + public static readonly Color DefaultAccentColor = Color.FromHex("#0366d6"); + + public static readonly Color DefaultTextColor = Color.FromHex("#24292e"); + + public static readonly Color DefaultCodeBackground = Color.FromHex("#f6f8fa"); + + public static readonly Color DefaultSeparatorColor = Color.FromHex("#eaecef"); + + public static readonly Color DefaultQuoteTextColor = Color.FromHex("#6a737d"); + + public static readonly Color DefaultQuoteBorderColor = Color.FromHex("#dfe2e5"); + } + + public class DarkMarkdownTheme : MarkdownTheme + { + public DarkMarkdownTheme() + { + this.BackgroundColor = DefaultBackgroundColor; + this.Paragraph.ForegroundColor = DefaultTextColor; + this.Heading1.ForegroundColor = DefaultTextColor; + this.Heading1.BorderColor = DefaultSeparatorColor; + this.Heading2.ForegroundColor = DefaultTextColor; + this.Heading2.BorderColor = DefaultSeparatorColor; + this.Heading3.ForegroundColor = DefaultTextColor; + this.Heading4.ForegroundColor = DefaultTextColor; + this.Heading5.ForegroundColor = DefaultTextColor; + this.Heading6.ForegroundColor = DefaultTextColor; + this.Link.ForegroundColor = DefaultAccentColor; + this.Code.ForegroundColor = DefaultTextColor; + this.Code.BackgroundColor = DefaultCodeBackground; + this.Quote.ForegroundColor = DefaultQuoteTextColor; + this.Quote.BorderColor = DefaultQuoteBorderColor; + this.Separator.BorderColor = DefaultSeparatorColor; + } + + public static readonly Color DefaultBackgroundColor = Color.FromHex("#2b303b"); + + public static readonly Color DefaultAccentColor = Color.FromHex("#d08770"); + + public static readonly Color DefaultTextColor = Color.FromHex("#eff1f5"); + + public static readonly Color DefaultCodeBackground = Color.FromHex("#4f5b66"); + + public static readonly Color DefaultSeparatorColor = Color.FromHex("#65737e"); + + public static readonly Color DefaultQuoteTextColor = Color.FromHex("#a7adba"); + + public static readonly Color DefaultQuoteBorderColor = Color.FromHex("#a7adba"); + } +} diff --git a/AideDeJeu/AideDeJeu/MarkdownView/MarkdownView.cs b/AideDeJeu/AideDeJeu/MarkdownView/MarkdownView.cs new file mode 100644 index 00000000..12dbda1f --- /dev/null +++ b/AideDeJeu/AideDeJeu/MarkdownView/MarkdownView.cs @@ -0,0 +1,570 @@ +namespace Xam.Forms.Markdown +{ + using System.Linq; + using Markdig.Syntax; + using Markdig.Syntax.Inlines; + using Xamarin.Forms; + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using Extensions; + using Markdig; + + public class MarkdownView : ContentView + { + public Action NavigateToLink { get; set; } = (s) => Device.OpenUri(new Uri(s)); + + public static MarkdownTheme Global = new LightMarkdownTheme(); + + public string Markdown + { + get { return (string)GetValue(MarkdownProperty); } + set { SetValue(MarkdownProperty, value); } + } + + public static readonly BindableProperty MarkdownProperty = BindableProperty.Create(nameof(Markdown), typeof(string), typeof(MarkdownView), null, propertyChanged: OnMarkdownChanged); + + public string RelativeUrlHost + { + get { return (string)GetValue(RelativeUrlHostProperty); } + set { SetValue(RelativeUrlHostProperty, value); } + } + + public static readonly BindableProperty RelativeUrlHostProperty = BindableProperty.Create(nameof(RelativeUrlHost), typeof(string), typeof(MarkdownView), null, propertyChanged: OnMarkdownChanged); + + public MarkdownTheme Theme + { + get { return (MarkdownTheme)GetValue(ThemeProperty); } + set { SetValue(ThemeProperty, value); } + } + + public static readonly BindableProperty ThemeProperty = BindableProperty.Create(nameof(Theme), typeof(MarkdownTheme), typeof(MarkdownView), Global, propertyChanged: OnMarkdownChanged); + + private bool isQuoted; + + private List queuedViews = new List(); + + static void OnMarkdownChanged(BindableObject bindable, object oldValue, object newValue) + { + var view = bindable as MarkdownView; + view.RenderMarkdown(); + } + + private StackLayout stack; + + private List> links = new List>(); + + private void RenderMarkdown() + { + stack = new StackLayout() + { + Spacing = this.Theme.Margin, + }; + + this.Padding = this.Theme.Margin; + + this.BackgroundColor = this.Theme.BackgroundColor; + + if(!string.IsNullOrEmpty(this.Markdown)) + { + var pipeline = new Markdig.MarkdownPipelineBuilder().UsePipeTables().Build(); + var parsed = Markdig.Markdown.Parse(this.Markdown, pipeline); + this.Render(parsed.AsEnumerable()); + } + + this.Content = stack; + + } + + private void Render(IEnumerable blocks) + { + foreach (var block in blocks) + { + this.Render(block); + } + } + + private void AttachLinks(View view) + { + if (links.Any()) + { + var blockLinks = links; + view.GestureRecognizers.Add(new TapGestureRecognizer + { + Command = new Command(async () => + { + try + { + if (blockLinks.Count > 1) + { + var result = await Application.Current.MainPage.DisplayActionSheet("Open link", "Cancel", null, blockLinks.Select(x => x.Key).ToArray()); + var link = blockLinks.FirstOrDefault(x => x.Key == result); + NavigateToLink(link.Value); + } + else + { + NavigateToLink(blockLinks.First().Value); + } + } + catch (Exception) { } + }), + }); + + links = new List>(); + } + } + + #region Rendering blocks + + private void Render(Block block) + { + switch (block) + { + case HeadingBlock heading: + Render(heading); + break; + + case ParagraphBlock paragraph: + Render(paragraph); + break; + + case QuoteBlock quote: + Render(quote); + break; + + case CodeBlock code: + Render(code); + break; + + case ListBlock list: + Render(list); + break; + + case ThematicBreakBlock thematicBreak: + Render(thematicBreak); + break; + + case HtmlBlock html: + Render(html); + break; + + case Markdig.Extensions.Tables.Table table: + Render(table); + break; + + default: + Debug.WriteLine($"Can't render {block.GetType()} blocks."); + break; + } + + if(queuedViews.Any()) + { + foreach (var view in queuedViews) + { + this.stack.Children.Add(view); + } + queuedViews.Clear(); + } + } + + private int listScope; + + private void Render(ThematicBreakBlock block) + { + var style = this.Theme.Separator; + + if (style.BorderSize > 0) + { + stack.Children.Add(new BoxView + { + HeightRequest = style.BorderSize, + BackgroundColor = style.BorderColor, + }); + } + } + + private void Render(ListBlock block) + { + listScope++; + + for (int i = 0; i < block.Count(); i++) + { + var item = block.ElementAt(i); + + if (item is ListItemBlock itemBlock) + { + this.Render(block, i + 1, itemBlock); + } + } + + listScope--; + } + + private void Render(ListBlock parent, int index, ListItemBlock block) + { + var initialStack = this.stack; + + this.stack = new StackLayout() + { + Spacing = this.Theme.Margin, + }; + + this.Render(block.AsEnumerable()); + + var horizontalStack = new StackLayout + { + Orientation = StackOrientation.Horizontal, + Margin = new Thickness(listScope * this.Theme.Margin, 0, 0, 0), + }; + + View bullet; + + if (parent.IsOrdered) + { + bullet = new Label + { + Text = $"{index}.", + FontSize = this.Theme.Paragraph.FontSize, + TextColor = this.Theme.Paragraph.ForegroundColor, + VerticalOptions = LayoutOptions.Start, + HorizontalOptions = LayoutOptions.End, + }; + } + else + { + bullet = new BoxView + { + WidthRequest = 4, + HeightRequest = 4, + Margin = new Thickness(0, 6, 0, 0), + BackgroundColor = this.Theme.Paragraph.ForegroundColor, + VerticalOptions = LayoutOptions.Start, + HorizontalOptions = LayoutOptions.Center, + }; + } + + horizontalStack.Children.Add(bullet); + + + horizontalStack.Children.Add(this.stack); + initialStack.Children.Add(horizontalStack); + + this.stack = initialStack; + } + + private void Render(HeadingBlock block) + { + MarkdownStyle style; + + switch (block.Level) + { + case 1: + style = this.Theme.Heading1; + break; + case 2: + style = this.Theme.Heading2; + break; + case 3: + style = this.Theme.Heading3; + break; + case 4: + style = this.Theme.Heading4; + break; + case 5: + style = this.Theme.Heading5; + break; + default: + style = this.Theme.Heading6; + break; + } + + var foregroundColor = isQuoted ? this.Theme.Quote.ForegroundColor : style.ForegroundColor; + + var label = new Label + { + FormattedText = CreateFormatted(block.Inline, style.FontFamily, style.Attributes, foregroundColor, style.BackgroundColor, style.FontSize), + }; + + AttachLinks(label); + + if (style.BorderSize > 0) + { + var headingStack = new StackLayout(); + headingStack.Children.Add(label); + headingStack.Children.Add(new BoxView + { + HeightRequest = style.BorderSize, + BackgroundColor = style.BorderColor, + }); + stack.Children.Add(headingStack); + } + else + { + stack.Children.Add(label); + } + } + + private void Render(ParagraphBlock block) + { + var style = this.Theme.Paragraph; + var foregroundColor = isQuoted ? this.Theme.Quote.ForegroundColor : style.ForegroundColor; + var label = new Label + { + FormattedText = CreateFormatted(block.Inline, style.FontFamily, style.Attributes, foregroundColor, style.BackgroundColor, style.FontSize), + }; + AttachLinks(label); + this.stack.Children.Add(label); + } + + private void Render(HtmlBlock block) + { + // ? + } + + private void Render(QuoteBlock block) + { + var initialIsQuoted = this.isQuoted; + var initialStack = this.stack; + + this.isQuoted = true; + this.stack = new StackLayout() + { + Spacing = this.Theme.Margin, + }; + + var style = this.Theme.Quote; + + if (style.BorderSize > 0) + { + var horizontalStack = new StackLayout() + { + Orientation = StackOrientation.Horizontal, + BackgroundColor = this.Theme.Quote.BackgroundColor, + }; + + horizontalStack.Children.Add(new BoxView() + { + WidthRequest = style.BorderSize, + BackgroundColor = style.BorderColor, + }); + + horizontalStack.Children.Add(this.stack); + initialStack.Children.Add(horizontalStack); + } + else + { + stack.BackgroundColor = this.Theme.Quote.BackgroundColor; + initialStack.Children.Add(this.stack); + } + + this.Render(block.AsEnumerable()); + + this.isQuoted = initialIsQuoted; + this.stack = initialStack; + } + + private void Render(CodeBlock block) + { + var style = this.Theme.Code; + var label = new Label + { + TextColor = style.ForegroundColor, + FontAttributes = style.Attributes, + FontFamily = style.FontFamily, + FontSize = style.FontSize, + Text = string.Join(Environment.NewLine, block.Lines), + }; + stack.Children.Add(new Frame() + { + CornerRadius = 3, + HasShadow = false, + Padding = this.Theme.Margin, + BackgroundColor = style.BackgroundColor, + Content = label + }); + } + + private void Render(Markdig.Extensions.Tables.Table tableBlock) + { + var scroll = new ScrollView() { HorizontalScrollBarVisibility = ScrollBarVisibility.Default, Orientation = ScrollOrientation.Horizontal }; + var grid = new Grid() { HorizontalOptions = LayoutOptions.Start, Margin = 0, Padding = 1, BackgroundColor = Theme.TableHeader.BackgroundColor, RowSpacing = 1, ColumnSpacing = 1 }; + + int top = 0; + int maxColumns = 0; + foreach (Markdig.Extensions.Tables.TableRow row in tableBlock) + { + int left = 0; + foreach(Markdig.Extensions.Tables.TableCell cell in row) + { + foreach (var blockpar in cell) + { + var par = blockpar as Markdig.Syntax.ParagraphBlock; + var style = row.IsHeader ? Theme.TableHeader : Theme.Paragraph; + var frame = new Frame + { + BackgroundColor = style.BackgroundColor, + Margin = 1, + Padding = 5, + }; + var label = new Label + { + FormattedText = CreateFormatted(par.Inline, style.FontFamily, style.Attributes, style.ForegroundColor, style.BackgroundColor, style.FontSize), + HorizontalOptions = LayoutOptions.CenterAndExpand, + BackgroundColor = style.BackgroundColor, + VerticalTextAlignment = TextAlignment.Center, + HorizontalTextAlignment = TextAlignment.Center, + }; + frame.Content = label; + AttachLinks(label); + grid.Children.Add(frame, left, top); + } + + left++; + maxColumns = Math.Max(maxColumns, left); + } + grid.RowDefinitions.Add(new RowDefinition { Height= GridLength.Auto }); + top++; + } + for (int i = 0; i < maxColumns; i++) + { + grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); + } + double? gridHeight = null; + grid.SizeChanged += (object sender, EventArgs e) => + { + if (gridHeight == null) + { + gridHeight = grid.HeightRequest = grid.Height; + } + else + { + grid.HeightRequest = gridHeight.Value; + } + }; + + //grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); + //grid.RowDefinitions.Add(new RowDefinition { Height= new GridLength(1, GridUnitType.Star) }); + stack.Children.Add(scroll); + scroll.Content = grid; + //grid.WidthRequest = 1000; + scroll.ForceLayout(); + this.UpdateChildrenLayout(); + } + + + private FormattedString CreateFormatted(ContainerInline inlines, string family, FontAttributes attributes, Color foregroundColor, Color backgroundColor, float size) + { + var fs = new FormattedString(); + + foreach (var inline in inlines) + { + var spans = CreateSpans(inline, family, attributes, foregroundColor, backgroundColor, size); + if (spans != null) + { + foreach (var span in spans) + { + fs.Spans.Add(span); + } + } + } + + return fs; + } + + private Span[] CreateSpans(Inline inline, string family, FontAttributes attributes, Color foregroundColor, Color backgroundColor, float size) + { + switch (inline) + { + case LiteralInline literal: + return new[] + { + new Span + { + Text = literal.Content.Text.Substring(literal.Content.Start, literal.Content.Length), + FontAttributes = attributes, + ForegroundColor = foregroundColor, + BackgroundColor = backgroundColor, + FontSize = size, + FontFamily = family, + } + }; + + case EmphasisInline emphasis: + var childAttributes = attributes | (emphasis.IsDouble ? FontAttributes.Bold : FontAttributes.Italic); + return emphasis.SelectMany(x => CreateSpans(x, family, childAttributes, foregroundColor, backgroundColor, size)).ToArray(); + + case LineBreakInline breakline: + return new[] { new Span { Text = "\n" } }; + + case LinkInline link: + + var url = link.Url; + + if (!(url.StartsWith("http://") || url.StartsWith("https://"))) + { + url = $"{this.RelativeUrlHost?.TrimEnd('/')}/{url.TrimStart('/')}"; + } + + if(link.IsImage) + { + var image = new Image(); + + if(Path.GetExtension(url) == ".svg") + { + image.RenderSvg(url); + } + else + { + image.Source = url; + } + + queuedViews.Add(image); + return new Span[0]; + } + else + { + var spans = link.SelectMany(x => CreateSpans(x, this.Theme.Link.FontFamily ?? family, this.Theme.Link.Attributes, this.Theme.Link.ForegroundColor, this.Theme.Link.BackgroundColor, size)).ToArray(); + links.Add(new KeyValuePair(string.Join("",spans.Select(x => x.Text)), url)); + return spans; + } + + case CodeInline code: + return new[] + { + new Span() + { + Text="\u2002", + FontSize = size, + FontFamily = this.Theme.Code.FontFamily, + ForegroundColor = this.Theme.Code.ForegroundColor, + BackgroundColor = this.Theme.Code.BackgroundColor + }, + new Span + { + Text = code.Content, + FontAttributes = this.Theme.Code.Attributes, + FontSize = size, + FontFamily = this.Theme.Code.FontFamily, + ForegroundColor = this.Theme.Code.ForegroundColor, + BackgroundColor = this.Theme.Code.BackgroundColor + }, + new Span() + { + Text="\u2002", + FontSize = size, + FontFamily = this.Theme.Code.FontFamily, + ForegroundColor = this.Theme.Code.ForegroundColor, + BackgroundColor = this.Theme.Code.BackgroundColor + }, + }; + + default: + Debug.WriteLine($"Can't render {inline.GetType()} inlines."); + return null; + } + } + + #endregion + } +} diff --git a/AideDeJeu/AideDeJeu/Views/MonsterDetailPage.xaml b/AideDeJeu/AideDeJeu/Views/MonsterDetailPage.xaml index c6a8ec1a..cbca83c4 100644 --- a/AideDeJeu/AideDeJeu/Views/MonsterDetailPage.xaml +++ b/AideDeJeu/AideDeJeu/Views/MonsterDetailPage.xaml @@ -3,7 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:tools="clr-namespace:AideDeJeu.Tools" xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms" - xmlns:mdview="clr-namespace:Xam.Forms.Markdown;assembly=Xam.Forms.MarkdownView" + xmlns:mdview="clr-namespace:Xam.Forms.Markdown" x:Class="AideDeJeu.Views.MonsterDetailPage" Title="{Binding Title}"> diff --git a/AideDeJeu/AideDeJeu/Views/SpellDetailPage.xaml b/AideDeJeu/AideDeJeu/Views/SpellDetailPage.xaml index 45a808dc..9b5c2dfb 100644 --- a/AideDeJeu/AideDeJeu/Views/SpellDetailPage.xaml +++ b/AideDeJeu/AideDeJeu/Views/SpellDetailPage.xaml @@ -2,7 +2,7 @@