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 @@