Refactor MarkdownView into a more generalized RichTextView API

This commit is contained in:
Alex Corrado 2012-09-18 17:23:44 -04:00
Родитель d5f31ff473
Коммит c2f98712d0
14 изменённых файлов: 797 добавлений и 624 удалений

Просмотреть файл

@ -113,7 +113,7 @@
<Compile Include="Xwt.GtkBackend\ExpanderBackend.cs" />
<Compile Include="Xwt.GtkBackend\LinkLabelBackend.cs" />
<Compile Include="Xwt.GtkBackend\SpinnerBackend.cs" />
<Compile Include="Xwt.GtkBackend\MarkdownViewBackend.cs" />
<Compile Include="Xwt.GtkBackend\RichTextViewBackend.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>

Просмотреть файл

@ -99,7 +99,7 @@ namespace Xwt.GtkBackend
WidgetRegistry.RegisterBackend (typeof(Xwt.LinkLabel), typeof (LinkLabelBackend));
WidgetRegistry.RegisterBackend (typeof(Xwt.Placement), typeof (BoxBackend));
WidgetRegistry.RegisterBackend (typeof(Xwt.Spinner), typeof (SpinnerBackend));
WidgetRegistry.RegisterBackend (typeof(Xwt.MarkdownView), typeof (MarkdownViewBackend));
WidgetRegistry.RegisterBackend (typeof(Xwt.RichTextView), typeof (RichTextViewBackend));
WidgetRegistry.RegisterBackend (typeof(Xwt.Expander), typeof (ExpanderBackend));
}

Просмотреть файл

@ -1,236 +0,0 @@
//
// MarkdownViewBackend.cs
//
// Author:
// Jérémie Laval <jeremie.laval@xamarin.com>
//
// Copyright (c) 2012 Xamarin, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;
using Xwt;
using Xwt.Backends;
using Xwt.Engine;
namespace Xwt.GtkBackend
{
public class MarkdownViewBackend : WidgetBackend, IMarkdownViewBackend
{
const string NewLine = "\n";
Gtk.TextTagTable table;
List<KeyValuePair<Gtk.TextChildAnchor, Gtk.Widget>> links = new List<KeyValuePair<Gtk.TextChildAnchor, Gtk.Widget>> ();
bool NavigateToUrlEnabled {
get; set;
}
public MarkdownViewBackend ()
{
Widget = new Gtk.TextView ();
Widget.Show ();
Widget.Editable = false;
Widget.WrapMode = Gtk.WrapMode.Word;
InitTagTable ();
}
void InitTagTable ()
{
table = new Gtk.TextTagTable ();
table.Add (new Gtk.TextTag ("bold") {
Weight = Pango.Weight.Bold
});
table.Add (new Gtk.TextTag ("italic") {
Style = Pango.Style.Italic
});
table.Add (new Gtk.TextTag ("tt") {
Family = "Monospace"
});
table.Add (new Gtk.TextTag ("li") {
LeftMargin = 14
});
table.Add (new Gtk.TextTag ("h1") {
Weight = Pango.Weight.Bold,
Scale = Pango.Scale.XXLarge
});
table.Add (new Gtk.TextTag ("h2") {
Weight = Pango.Weight.Bold,
Scale = Pango.Scale.XLarge
});
table.Add (new Gtk.TextTag ("h3") {
Weight = Pango.Weight.Bold,
Scale = Pango.Scale.Large
});
table.Add (new Gtk.TextTag ("h4") {
Scale = Pango.Scale.Large
});
table.Add (new Gtk.TextTag ("pre") {
Family = "Monospace",
Indent = 14
});
}
protected new Gtk.TextView Widget {
get {
return (Gtk.TextView)base.Widget;
}
set {
base.Widget = value;
}
}
public override void EnableEvent (object eventId)
{
base.EnableEvent (eventId);
if (eventId is MarkdownViewEvent) {
switch ((MarkdownViewEvent) eventId) {
case MarkdownViewEvent.NavigateToUrl:
NavigateToUrlEnabled = true;
break;
}
}
}
public override void DisableEvent (object eventId)
{
base.DisableEvent (eventId);
if (eventId is MarkdownViewEvent) {
switch ((MarkdownViewEvent) eventId) {
case MarkdownViewEvent.NavigateToUrl:
NavigateToUrlEnabled = false;
break;
}
}
}
public object CreateBuffer ()
{
return new Gtk.TextBuffer (table);
}
public void EmitText (object buffer, string text)
{
var b = ((Gtk.TextBuffer)buffer);
var iter = b.EndIter;
b.Insert (ref iter, text);
}
public void EmitStyledText (object buffer, string text, MarkdownInlineStyle style)
{
var b = ((Gtk.TextBuffer)buffer);
var iter = b.EndIter;
var tagName = string.Empty;
switch (style) {
case MarkdownInlineStyle.Bold:
tagName = "bold";
break;
case MarkdownInlineStyle.Italic:
tagName = "italic";
break;
case MarkdownInlineStyle.Monospace:
tagName = "tt";
break;
}
if (string.IsNullOrEmpty (tagName))
EmitText (buffer, text);
else
b.InsertWithTagsByName (ref iter, text, tagName);
}
public void EmitHeader (object buffer, string title, int level)
{
var b = ((Gtk.TextBuffer)buffer);
var iter = b.EndIter;
b.Insert (ref iter, NewLine);
b.InsertWithTagsByName (ref iter, title, "h" + level);
b.Insert (ref iter, NewLine);
}
public void EmitStartParagraph (object buffer)
{
EmitText (buffer, NewLine);
}
public void EmitEndParagraph (object buffer)
{
EmitText (buffer, NewLine);
}
public void EmitOpenList (object buffer)
{
EmitText (buffer, NewLine);
}
public void EmitOpenBullet (object buffer)
{
var b = (Gtk.TextBuffer)buffer;
var iter = b.EndIter;
b.InsertWithTagsByName (ref iter, "• ", "li");
}
public void EmitCloseBullet (object buffer)
{
EmitText (buffer, NewLine);
}
public void EmitCloseList (object buffer)
{
}
public void EmitLink (object buffer, string href, string text)
{
var b = (Gtk.TextBuffer)buffer;
var iter = b.EndIter;
var anchor = b.CreateChildAnchor (ref iter);
var link = new LinkLabel (text) { Uri = new Uri (href, UriKind.RelativeOrAbsolute) };
link.NavigateToUrl += (sender, e) => {
if (NavigateToUrlEnabled) {
((IMarkdownViewEventSink) EventSink).OnNavigateToUrl (e.Uri);
e.SetHandled ();
}
};
var gtkWidget = (Gtk.Widget) WidgetRegistry.GetNativeWidget (link);
links.Add (new KeyValuePair<Gtk.TextChildAnchor, Gtk.Widget> (anchor, gtkWidget));
}
public void EmitCodeBlock (object buffer, string code)
{
var b = ((Gtk.TextBuffer)buffer);
var iter = b.EndIter;
b.Insert (ref iter, NewLine);
b.InsertWithTagsByName (ref iter, code, "pre");
}
public void EmitHorizontalRuler (object buffer)
{
}
public void SetBuffer (object buffer)
{
Widget.Buffer = (Gtk.TextBuffer)buffer;
foreach (var l in links)
Widget.AddChildAtAnchor (l.Value, l.Key);
links.Clear ();
}
}
}

Просмотреть файл

@ -0,0 +1,270 @@
//
// RichTextViewBackend.cs
//
// Author:
// Jérémie Laval <jeremie.laval@xamarin.com>
//
// Copyright (c) 2012 Xamarin, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;
using Xwt;
using Xwt.Backends;
using Xwt.Engine;
namespace Xwt.GtkBackend
{
public class RichTextViewBackend : WidgetBackend, IRichTextViewBackend
{
Gtk.TextTagTable table;
LinkLabel [] links;
bool NavigateToUrlEnabled {
get; set;
}
public RichTextViewBackend ()
{
Widget = new Gtk.TextView ();
Widget.Show ();
Widget.Editable = false;
Widget.WrapMode = Gtk.WrapMode.Word;
InitTagTable ();
}
void InitTagTable ()
{
table = new Gtk.TextTagTable ();
table.Add (new Gtk.TextTag ("bold") {
Weight = Pango.Weight.Bold
});
table.Add (new Gtk.TextTag ("italic") {
Style = Pango.Style.Italic
});
table.Add (new Gtk.TextTag ("tt") {
Family = "Monospace"
});
table.Add (new Gtk.TextTag ("li") {
LeftMargin = 14
});
table.Add (new Gtk.TextTag ("h1") {
Weight = Pango.Weight.Bold,
Scale = Pango.Scale.XXLarge
});
table.Add (new Gtk.TextTag ("h2") {
Weight = Pango.Weight.Bold,
Scale = Pango.Scale.XLarge
});
table.Add (new Gtk.TextTag ("h3") {
Weight = Pango.Weight.Bold,
Scale = Pango.Scale.Large
});
table.Add (new Gtk.TextTag ("h4") {
Scale = Pango.Scale.Large
});
table.Add (new Gtk.TextTag ("pre") {
Family = "Monospace",
Indent = 14
});
}
protected new Gtk.TextView Widget {
get {
return (Gtk.TextView)base.Widget;
}
set {
base.Widget = value;
}
}
public override void EnableEvent (object eventId)
{
base.EnableEvent (eventId);
if (eventId is RichTextViewEvent) {
switch ((RichTextViewEvent) eventId) {
case RichTextViewEvent.NavigateToUrl:
NavigateToUrlEnabled = true;
break;
}
}
}
public override void DisableEvent (object eventId)
{
base.DisableEvent (eventId);
if (eventId is RichTextViewEvent) {
switch ((RichTextViewEvent) eventId) {
case RichTextViewEvent.NavigateToUrl:
NavigateToUrlEnabled = false;
break;
}
}
}
public IRichTextBuffer CreateBuffer ()
{
return new RichTextBuffer (table);
}
public void SetBuffer (IRichTextBuffer buffer)
{
var buf = buffer as RichTextBuffer;
if (buf == null)
throw new ArgumentException ("Passed buffer is of incorrect type", "buffer");
if (links != null) {
foreach (var link in links)
link.NavigateToUrl -= HandleNavigateToUrl;
}
Widget.Buffer = buf;
links = new LinkLabel [buf.Links.Count];
for (var i = 0; i < links.Length; i++) {
var link = buf.Links [i];
var label = new LinkLabel (link.Text)
{
Uri = link.Href
};
label.NavigateToUrl += HandleNavigateToUrl;
Widget.AddChildAtAnchor ((Gtk.Widget) WidgetRegistry.GetNativeWidget (label), link.Anchor);
links [i] = label;
}
}
void HandleNavigateToUrl (object sender, NavigateToUrlEventArgs e)
{
if (NavigateToUrlEnabled) {
((IRichTextViewEventSink) EventSink).OnNavigateToUrl (e.Uri);
e.SetHandled ();
}
}
struct Link {
public string Text;
public Uri Href;
public Gtk.TextChildAnchor Anchor;
}
class RichTextBuffer : Gtk.TextBuffer, IRichTextBuffer
{
const string NewLine = "\n";
public List<Link> Links {
get; private set;
}
public RichTextBuffer (Gtk.TextTagTable table) : base (table)
{
Links = new List<Link> ();
}
public void EmitText (string text)
{
var iter = EndIter;
Insert (ref iter, text);
}
public void EmitStyledText (string text, RichTextInlineStyle style)
{
var iter = EndIter;
var tagName = string.Empty;
switch (style) {
case RichTextInlineStyle.Bold:
tagName = "bold";
break;
case RichTextInlineStyle.Italic:
tagName = "italic";
break;
case RichTextInlineStyle.Monospace:
tagName = "tt";
break;
}
if (string.IsNullOrEmpty (tagName))
EmitText (text);
else
InsertWithTagsByName (ref iter, text, tagName);
}
public void EmitHeader (string title, int level)
{
var iter = EndIter;
Insert (ref iter, NewLine);
InsertWithTagsByName (ref iter, title, "h" + level);
Insert (ref iter, NewLine);
}
public void EmitStartParagraph ()
{
EmitText (NewLine);
}
public void EmitEndParagraph ()
{
EmitText (NewLine);
}
public void EmitOpenList ()
{
EmitText (NewLine);
}
public void EmitOpenBullet ()
{
var iter = EndIter;
InsertWithTagsByName (ref iter, "• ", "li");
}
public void EmitCloseBullet ()
{
EmitText (NewLine);
}
public void EmitCloseList ()
{
}
public void EmitLink (string href, string text)
{
var iter = EndIter;
var anchor = CreateChildAnchor (ref iter);
Links.Add (new Link ()
{
Text = text,
Href = new Uri (href, UriKind.RelativeOrAbsolute),
Anchor = anchor
});
}
public void EmitCodeBlock (string code)
{
var iter = EndIter;
Insert (ref iter, NewLine);
InsertWithTagsByName (ref iter, code, "pre");
}
public void EmitHorizontalRuler ()
{
//FIXME
}
}
}
}

2
Xwt.WPF/Xwt.WPF.csproj Normal file → Executable file
Просмотреть файл

@ -65,7 +65,6 @@
<Compile Include="Xwt.WPFBackend\ComboBoxTextEntryBackend.cs" />
<Compile Include="Xwt.WPFBackend\ExRichTextBox.cs" />
<Compile Include="Xwt.WPFBackend\LinkLabelBackend.cs" />
<Compile Include="Xwt.WPFBackend\MarkdownViewBackend.cs" />
<Compile Include="Xwt.WPFBackend\PopoverBackend.cs" />
<Compile Include="Xwt.WPFBackend\ProgressBarBackend.cs" />
<Compile Include="Xwt.WPFBackend\CustomWidgetBackend.cs" />
@ -133,6 +132,7 @@
<Compile Include="Xwt.WPFBackend\NotebookBackend.cs" />
<Compile Include="Xwt.WPFBackend\ScrollAdjustmentBackend.cs" />
<Compile Include="Xwt.WPFBackend\CustomScrollViewPort.cs" />
<Compile Include="Xwt.WPFBackend\RichTextViewBackend.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>

Просмотреть файл

@ -1,220 +0,0 @@
//
// MarkdownViewBackend.cs
//
// Author:
// Alan McGovern <alan@xamarin.com>
//
// Copyright (c) 2012 Xamarin, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Markup;
using Xwt.Backends;
using Xwt.WPFBackend.Utilities;
using System.Windows.Data;
using System.Windows.Navigation;
namespace Xwt.WPFBackend
{
class MarkdownViewBackend
: WidgetBackend, IMarkdownViewBackend
{
int FontSize = 16;
int HeaderIncrement = 8;
StringBuilder Builder {
get; set;
}
public new IMarkdownViewEventSink EventSink {
get { return (IMarkdownViewEventSink) base.EventSink; }
}
public new ExRichTextBox Widget
{
get { return (ExRichTextBox) base.Widget; }
set { base.Widget = value; }
}
XmlWriter Writer {
get; set;
}
public MarkdownViewBackend ()
{
Widget = new ExRichTextBox ();
}
public override void EnableEvent (object eventId)
{
base.EnableEvent (eventId);
if (eventId is MarkdownViewEvent) {
switch ((MarkdownViewEvent) eventId) {
case MarkdownViewEvent.NavigateToUrl:
Widget.AddHandler (Hyperlink.RequestNavigateEvent, new RequestNavigateEventHandler (HyperlinkNavigated), true);
break;
}
}
}
public override void DisableEvent (object eventId)
{
base.DisableEvent (eventId);
if (eventId is MarkdownViewEvent) {
switch ((MarkdownViewEvent) eventId) {
case MarkdownViewEvent.NavigateToUrl:
Widget.RemoveHandler (Hyperlink.RequestNavigateEvent, new RequestNavigateEventHandler (HyperlinkNavigated));
break;
}
}
}
void HyperlinkNavigated (object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
{
Xwt.Engine.Toolkit.Invoke (() => {
EventSink.OnNavigateToUrl (e.Uri);
e.Handled = true;
});
}
object IMarkdownViewBackend.CreateBuffer ()
{
Builder = new StringBuilder ();
Writer = XmlWriter.Create (Builder, new XmlWriterSettings () { OmitXmlDeclaration = true, NewLineOnAttributes = true, Indent = true, IndentChars = "\t" });
Writer.WriteStartElement ("FlowDocument", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
return Writer;
}
void IMarkdownViewBackend.EmitText (object buffer, string text)
{
if (!string.IsNullOrEmpty (text))
Writer.WriteElementString ("Run", text);
}
void IMarkdownViewBackend.EmitHeader (object buffer, string title, int level)
{
((IMarkdownViewBackend) this).EmitStartParagraph (buffer);
Writer.WriteStartElement ("Run");
Writer.WriteAttributeString ("FontSize", (FontSize + HeaderIncrement * level).ToString ());
Writer.WriteString (title);
Writer.WriteEndElement ();
((IMarkdownViewBackend) this).EmitEndParagraph (buffer);
}
void IMarkdownViewBackend.EmitOpenList (object buffer)
{
Writer.WriteStartElement ("List");
}
void IMarkdownViewBackend.EmitOpenBullet (object buffer)
{
Writer.WriteStartElement ("ListItem");
((IMarkdownViewBackend) this).EmitStartParagraph (buffer);
}
void IMarkdownViewBackend.EmitCloseBullet (object buffer)
{
Writer.WriteEndElement ();
Writer.WriteEndElement ();
}
void IMarkdownViewBackend.EmitCloseList (object buffer)
{
// Close the list
Writer.WriteEndElement ();
}
void IMarkdownViewBackend.EmitLink (object buffer, string href, string text)
{
Writer.WriteStartElement ("Hyperlink");
Writer.WriteAttributeString ("NavigateUri", href);
((IMarkdownViewBackend) this).EmitText (buffer, text);
Writer.WriteEndElement ();
}
void IMarkdownViewBackend.EmitCodeBlock (object buffer, string code)
{
((IMarkdownViewBackend) this).EmitStartParagraph (buffer);
Writer.WriteAttributeString ("xml", "space", null, "preserve");
Writer.WriteAttributeString ("TextIndent", "0");
Writer.WriteAttributeString ("Margin", "50,0,0,0");
Writer.WriteAttributeString ("FontFamily", "GlobalMonospace.CompositeFont");
Writer.WriteString (code);
((IMarkdownViewBackend) this).EmitEndParagraph (buffer);
}
void IMarkdownViewBackend.SetBuffer (object buffer)
{
Writer.WriteEndElement ();
Writer.Flush ();
if (Widget.Document != null)
Widget.Document.ClearValue (FlowDocument.PageWidthProperty);
Widget.Document = (FlowDocument) XamlReader.Parse (Builder.ToString ());
Widget.Document.SetBinding (FlowDocument.PageWidthProperty, new Binding ("ActualWidth") { Source = Widget });
Widget.IsDocumentEnabled = true;
Widget.Document.IsEnabled = true;
Widget.IsReadOnly = true;
}
void IMarkdownViewBackend.EmitStyledText (object buffer, string text, MarkdownInlineStyle style)
{
switch (style) {
case MarkdownInlineStyle.Bold:
case MarkdownInlineStyle.Italic:
Writer.WriteStartElement (style.ToString ());
((IMarkdownViewBackend) this).EmitText (buffer, text);
break;
case MarkdownInlineStyle.Monospace:
Writer.WriteStartElement ("Run");
Writer.WriteAttributeString ("FontFamily", "GlobalMonospace.CompositeFont");
Writer.WriteString (text);
break;
}
Writer.WriteEndElement ();
}
void IMarkdownViewBackend.EmitStartParagraph (object buffer)
{
Writer.WriteStartElement ("Paragraph");
}
void IMarkdownViewBackend.EmitEndParagraph (object buffer)
{
Writer.WriteEndElement ();
}
void IMarkdownViewBackend.EmitHorizontalRuler (object buffer)
{
Writer.WriteStartElement("BlockUIContainer");
Writer.WriteAttributeString ("Margin", "0,0,0,0");
Writer.WriteElementString("Separator", "");
Writer.WriteEndElement();
}
}
}

Просмотреть файл

@ -0,0 +1,238 @@
//
// RichTextViewBackend.cs
//
// Author:
// Alan McGovern <alan@xamarin.com>
//
// Copyright (c) 2012 Xamarin, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Markup;
using Xwt.Backends;
using Xwt.WPFBackend.Utilities;
using System.Windows.Data;
using System.Windows.Navigation;
namespace Xwt.WPFBackend
{
class RichTextViewBackend
: WidgetBackend, IRichTextViewBackend
{
public new IRichTextViewEventSink EventSink {
get { return (IRichTextViewEventSink) base.EventSink; }
}
public new ExRichTextBox Widget
{
get { return (ExRichTextBox) base.Widget; }
set { base.Widget = value; }
}
public RichTextViewBackend ()
{
Widget = new ExRichTextBox ();
}
public override void EnableEvent (object eventId)
{
base.EnableEvent (eventId);
if (eventId is RichTextViewEvent) {
switch ((RichTextViewEvent) eventId) {
case RichTextViewEvent.NavigateToUrl:
Widget.AddHandler (Hyperlink.RequestNavigateEvent, new RequestNavigateEventHandler (HyperlinkNavigated), true);
break;
}
}
}
public override void DisableEvent (object eventId)
{
base.DisableEvent (eventId);
if (eventId is RichTextViewEvent) {
switch ((RichTextViewEvent) eventId) {
case RichTextViewEvent.NavigateToUrl:
Widget.RemoveHandler (Hyperlink.RequestNavigateEvent, new RequestNavigateEventHandler (HyperlinkNavigated));
break;
}
}
}
void HyperlinkNavigated (object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
{
Xwt.Engine.Toolkit.Invoke (() => {
EventSink.OnNavigateToUrl (e.Uri);
e.Handled = true;
});
}
public IRichTextBuffer CreateBuffer ()
{
return new RichTextBuffer ();
}
public void SetBuffer (IRichTextBuffer buffer)
{
var buf = buffer as RichTextBuffer;
if (buf == null)
throw new ArgumentException ("Passed buffer is of incorrect type", "buffer");
if (Widget.Document != null)
Widget.Document.ClearValue (FlowDocument.PageWidthProperty);
Widget.Document = buf.ToFlowDocument ();
Widget.Document.SetBinding (FlowDocument.PageWidthProperty, new Binding ("ActualWidth") { Source = Widget });
Widget.IsDocumentEnabled = true;
Widget.Document.IsEnabled = true;
Widget.IsReadOnly = true;
}
class RichTextBuffer : IRichTextBuffer
{
const int FontSize = 16;
const int HeaderIncrement = 8;
StringBuilder builder;
XmlWriter writer;
FlowDocument doc;
public RichTextBuffer ()
{
builder = new StringBuilder ();
writer = XmlWriter.Create (builder, new XmlWriterSettings () { OmitXmlDeclaration = true, NewLineOnAttributes = true, Indent = true, IndentChars = "\t" });
writer.WriteStartElement ("FlowDocument", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
}
public void EmitText (string text)
{
if (!string.IsNullOrEmpty (text))
writer.WriteElementString ("Run", text);
}
public void EmitHeader (string title, int level)
{
EmitStartParagraph ();
writer.WriteStartElement ("Run");
writer.WriteAttributeString ("FontSize", (FontSize + HeaderIncrement * level).ToString ());
writer.WriteString (title);
writer.WriteEndElement ();
EmitEndParagraph ();
}
public void EmitOpenList ()
{
writer.WriteStartElement ("List");
}
public void EmitOpenBullet ()
{
writer.WriteStartElement ("ListItem");
EmitStartParagraph ();
}
public void EmitCloseBullet ()
{
writer.WriteEndElement ();
writer.WriteEndElement ();
}
public void EmitCloseList ()
{
// Close the list
writer.WriteEndElement ();
}
public void EmitLink (string href, string text)
{
writer.WriteStartElement ("Hyperlink");
writer.WriteAttributeString ("NavigateUri", href);
EmitText (text);
writer.WriteEndElement ();
}
public void EmitCodeBlock (string code)
{
EmitStartParagraph ();
writer.WriteAttributeString ("xml", "space", null, "preserve");
writer.WriteAttributeString ("TextIndent", "0");
writer.WriteAttributeString ("Margin", "50,0,0,0");
writer.WriteAttributeString ("FontFamily", "GlobalMonospace.CompositeFont");
writer.WriteString (code);
EmitEndParagraph ();
}
public void EmitStyledText (string text, RichTextInlineStyle style)
{
switch (style) {
case RichTextInlineStyle.Bold:
case RichTextInlineStyle.Italic:
writer.WriteStartElement (style.ToString ());
EmitText (text);
break;
case RichTextInlineStyle.Monospace:
writer.WriteStartElement ("Run");
writer.WriteAttributeString ("FontFamily", "GlobalMonospace.CompositeFont");
writer.WriteString (text);
break;
}
writer.WriteEndElement ();
}
public void EmitStartParagraph ()
{
writer.WriteStartElement ("Paragraph");
}
public void EmitEndParagraph ()
{
writer.WriteEndElement ();
}
public void EmitHorizontalRuler ()
{
writer.WriteStartElement("BlockUIContainer");
writer.WriteAttributeString ("Margin", "0,0,0,0");
writer.WriteElementString("Separator", "");
writer.WriteEndElement();
}
public FlowDocument ToFlowDocument ()
{
if (doc == null) {
writer.WriteEndElement ();
writer.Flush ();
doc = (FlowDocument) XamlReader.Parse (builder.ToString ());
builder = null;
writer = null;
}
return doc;
}
}
}
}

2
Xwt.WPF/Xwt.WPFBackend/WPFEngine.cs Normal file → Executable file
Просмотреть файл

@ -94,7 +94,7 @@ namespace Xwt.WPFBackend
WidgetRegistry.RegisterBackend (typeof (Placement), typeof (BoxBackend));
WidgetRegistry.RegisterBackend (typeof (Popover), typeof (PopoverBackend));
WidgetRegistry.RegisterBackend (typeof (ProgressBar), typeof (ProgressBarBackend));
WidgetRegistry.RegisterBackend (typeof (MarkdownView), typeof (MarkdownViewBackend));
WidgetRegistry.RegisterBackend (typeof (RichTextView), typeof (RichTextViewBackend));
WidgetRegistry.RegisterBackend (typeof (LinkLabel), typeof (LinkLabelBackend));
WidgetRegistry.RegisterBackend (typeof (Spinner), typeof (SpinnerBackend));
}

Просмотреть файл

@ -1,67 +0,0 @@
//
// IMarkdownViewBackend.cs
//
// Author:
// Jérémie Laval <jeremie.laval@xamarin.com>
//
// Copyright (c) 2012 Xamarin, Inc.
using System;
namespace Xwt.Backends
{
[Flags]
public enum MarkdownInlineStyle
{
Italic,
Bold,
Monospace
}
public interface IMarkdownViewBackend : IWidgetBackend
{
object CreateBuffer ();
// Emit unstyled text
void EmitText (object buffer, string text);
// Emit text using combination of the MarkdownInlineStyle
void EmitStyledText (object buffer, string text, MarkdownInlineStyle style);
// Emit a header (h1, h2, ...)
void EmitHeader (object buffer, string title, int level);
// What's outputed afterwards will be a in new paragrapgh
void EmitStartParagraph (object buffer);
void EmitEndParagraph (object buffer);
// Emit a list
// Chain is:
// open-list, open-bullet, <above methods>, close-bullet, close-list
void EmitOpenList (object buffer);
void EmitOpenBullet (object buffer);
void EmitCloseBullet (object buffer);
void EmitCloseList (object buffer);
// Emit a link displaying text and opening the href URL
void EmitLink (object buffer, string href, string text);
// Emit code in a preformated blockquote
void EmitCodeBlock (object buffer, string code);
// Emit an horizontal ruler
void EmitHorizontalRuler (object buffer);
// Display the passed buffer
void SetBuffer (object buffer);
}
public interface IMarkdownViewEventSink : IWidgetEventSink
{
void OnNavigateToUrl (Uri uri);
}
public enum MarkdownViewEvent
{
NavigateToUrl = 1
}
}

Просмотреть файл

@ -0,0 +1,70 @@
//
// IMarkdownViewBackend.cs
//
// Author:
// Jérémie Laval <jeremie.laval@xamarin.com>
//
// Copyright (c) 2012 Xamarin, Inc.
using System;
namespace Xwt.Backends
{
[Flags]
public enum RichTextInlineStyle
{
Italic,
Bold,
Monospace
}
public interface IRichTextViewBackend : IWidgetBackend
{
IRichTextBuffer CreateBuffer ();
// Display the passed buffer
void SetBuffer (IRichTextBuffer buffer);
}
public interface IRichTextBuffer
{
// Emit unstyled text
void EmitText (string text);
// Emit text using combination of the MarkdownInlineStyle
void EmitStyledText (string text, RichTextInlineStyle style);
// Emit a header (h1, h2, ...)
void EmitHeader (string title, int level);
// What's outputed afterwards will be a in new paragrapgh
void EmitStartParagraph ();
void EmitEndParagraph ();
// Emit a list
// Chain is:
// open-list, open-bullet, <above methods>, close-bullet, close-list
void EmitOpenList ();
void EmitOpenBullet ();
void EmitCloseBullet ();
void EmitCloseList ();
// Emit a link displaying text and opening the href URL
void EmitLink (string href, string text);
// Emit code in a preformated blockquote
void EmitCodeBlock (string code);
// Emit an horizontal ruler
void EmitHorizontalRuler ();
}
public interface IRichTextViewEventSink : IWidgetEventSink
{
void OnNavigateToUrl (Uri uri);
}
public enum RichTextViewEvent
{
NavigateToUrl = 1
}
}

Просмотреть файл

@ -1,10 +1,10 @@
//
// MarkdownView.cs
// MarkdownTextFormat.cs
//
// Author:
// Jérémie Laval <jeremie.laval@xamarin.com>
// Alex Corrado <corrado@xamarin.com>
//
// Copyright (c) 2012 Xamarin, Inc.
// Copyright (c) 2012 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@ -23,79 +23,25 @@
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Linq;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using Xwt.Backends;
namespace Xwt
namespace Xwt.Formats
{
public class MarkdownView : Widget
public class MarkdownTextFormat : TextFormat
{
protected new class WidgetBackendHost : Widget.WidgetBackendHost, IMarkdownViewEventSink
public override void Parse (Stream input, IRichTextBuffer buffer)
{
public void OnNavigateToUrl (Uri uri)
{
((MarkdownView) Parent).OnNavigateToUrl (new NavigateToUrlEventArgs (uri));
}
}
string markdown;
IMarkdownViewBackend Backend {
get { return (IMarkdownViewBackend) BackendHost.Backend; }
}
public string Markdown
{
get
{
return markdown;
}
set
{
markdown = value;
object buffer = ParseMarkdown (value);
Backend.SetBuffer (buffer);
}
}
EventHandler<NavigateToUrlEventArgs> navigateToUrl;
public event EventHandler<NavigateToUrlEventArgs> NavigateToUrl
{
add
{
BackendHost.OnBeforeEventAdd (MarkdownViewEvent.NavigateToUrl, navigateToUrl);
navigateToUrl += value;
}
remove
{
navigateToUrl -= value;
BackendHost.OnAfterEventRemove (MarkdownViewEvent.NavigateToUrl, navigateToUrl);
}
}
public MarkdownView ()
{
NavigateToUrl += delegate { }; // ensure the virtual method is always called
Markdown = string.Empty;
}
protected override BackendHost CreateBackendHost ()
{
return new WidgetBackendHost ();
}
protected virtual void OnNavigateToUrl (NavigateToUrlEventArgs e)
{
if (navigateToUrl != null)
navigateToUrl (this, e);
if (!e.Handled)
Application.EngineBackend.ShowWebBrowser (e);
using (var reader = new StreamReader (input))
ParseMarkdown (reader.ReadToEnd (), buffer);
}
/* The subset we support:
@ -108,10 +54,9 @@ namespace Xwt
* - Inline code is wrapped between the '`' character
* - horizontal ruler, a line with at least 3 hyphens
*/
object ParseMarkdown (string markdown)
void ParseMarkdown (string markdown, IRichTextBuffer buffer)
{
var lines = markdown.Replace ("\r\n", "\n").Split (new[] { '\n' });
var buffer = Backend.CreateBuffer ();
var wasParagraph = false;
for (int i = 0; i < lines.Length; i++) {
@ -119,7 +64,7 @@ namespace Xwt
// New paragraph
if (string.IsNullOrWhiteSpace (line)) {
if (wasParagraph) {
Backend.EmitEndParagraph (buffer);
buffer.EmitEndParagraph ();
wasParagraph = false;
}
}
@ -127,7 +72,7 @@ namespace Xwt
// Title
else if (line.StartsWith ("#")) {
var level = line.TakeWhile (c => c == '#').Count ();
Backend.EmitHeader (buffer, line.Trim (' ', '#'), level);
buffer.EmitHeader (line.Trim (' ', '#'), level);
}
// Title (setex-style)
@ -142,15 +87,15 @@ namespace Xwt
// must close the paragraph containing 'FooBarBaz' first. Or we should disallow this construct
if (wasParagraph) {
wasParagraph = false;
Backend.EmitEndParagraph (buffer);
buffer.EmitEndParagraph ();
}
Backend.EmitHeader (buffer, line, level);
buffer.EmitHeader (line, level);
i++;
}
// Ruler
else if (line.All (c => c == '-') && line.Length >= 3) {
Backend.EmitHorizontalRuler (buffer);
buffer.EmitHorizontalRuler ();
}
// Code blocks
@ -164,32 +109,32 @@ namespace Xwt
}
i--;
if (wasParagraph) {
Backend.EmitEndParagraph (buffer);
buffer.EmitEndParagraph ();
wasParagraph = false;
}
Backend.EmitCodeBlock (buffer, codeblock.Replace ("\n", Environment.NewLine).ToString ());
buffer.EmitCodeBlock (codeblock.Replace ("\n", Environment.NewLine).ToString ());
}
// List
else if (new[] { '+', '-', '*' }.Contains (line.TrimStart()[0])) {
Backend.EmitOpenList (buffer);
buffer.EmitOpenList ();
var bullet = line[0].ToString ();
for (; i < lines.Length; i++) {
line = lines[i];
if (!line.StartsWith (bullet))
break;
Backend.EmitOpenBullet (buffer);
buffer.EmitOpenBullet ();
ParseText (buffer, line.TrimStart (' ', '-'));
Backend.EmitCloseBullet (buffer);
buffer.EmitCloseBullet ();
}
i--;
Backend.EmitCloseList (buffer);
buffer.EmitCloseList ();
}
// Normal paragraph
else {
if (!wasParagraph)
Backend.EmitStartParagraph (buffer);
buffer.EmitStartParagraph ();
ParseText (buffer, line);
wasParagraph = true;
}
@ -197,12 +142,10 @@ namespace Xwt
// If we don't end in a newline we need to end the open paragrah
if (wasParagraph)
Backend.EmitEndParagraph (buffer);
return buffer;
buffer.EmitEndParagraph ();
}
void ParseText (object buffer, string line)
void ParseText (IRichTextBuffer buffer, string line)
{
// First transform any embedded URL into a proper format
line = autoUrl.Replace (line, m => string.Format ("[{0}]({1})", m.Value, m.Value));
@ -213,34 +156,34 @@ namespace Xwt
while (match.Success) {
var text = line.Substring (currentIndex, match.Index - currentIndex);
if (!string.IsNullOrEmpty (text))
Backend.EmitText (buffer, text);
buffer.EmitText (text);
// Emphasis
if (match.Groups["char"].Success) {
MarkdownInlineStyle style = 0;
RichTextInlineStyle style = 0;
switch (match.Groups["char"].Value[0]) {
case '*':
style |= MarkdownInlineStyle.Bold;
style |= RichTextInlineStyle.Bold;
break;
case '_':
style |= MarkdownInlineStyle.Italic;
style |= RichTextInlineStyle.Italic;
break;
case '`':
style |= MarkdownInlineStyle.Monospace;
style |= RichTextInlineStyle.Monospace;
break;
}
Backend.EmitStyledText (buffer, match.Groups["emph"].Value, style);
buffer.EmitStyledText (match.Groups["emph"].Value, style);
}
// Link
else {
var url = match.Groups["url"].Value;
var name = match.Groups["name"].Value;
Backend.EmitLink (buffer, url, name);
buffer.EmitLink (url, name);
}
currentIndex = match.Index + match.Length;
match = match.NextMatch ();
}
// Add remaining text
Backend.EmitText (buffer, line.Substring (currentIndex));
buffer.EmitText (line.Substring (currentIndex));
}
static Regex richText = new Regex (@"\[(?<name>.+)\]\((?<url>.+)\)

Просмотреть файл

@ -0,0 +1,42 @@
//
// TextFormat.cs
//
// Author:
// Alex Corrado <corrado@xamarin.com>
//
// Copyright (c) 2012 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.IO;
using Xwt.Backends;
namespace Xwt.Formats
{
public abstract class TextFormat
{
public static readonly TextFormat Markdown = new MarkdownTextFormat ();
// Parses the given input stream into the given buffer
public abstract void Parse (Stream input, IRichTextBuffer buffer);
}
}

Просмотреть файл

@ -351,15 +351,17 @@
<SubType>Component</SubType>
</Compile>
<Compile Include="Xwt.Backends\ISpinnerBackend.cs" />
<Compile Include="Xwt.Backends\IMarkdownViewBackend.cs" />
<Compile Include="Xwt\MarkdownView.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Xwt\CloseRequestedHandler.cs" />
<Compile Include="Xwt\CloseRequestedEventArgs.cs" />
<Compile Include="Xwt\WrapMode.cs" />
<Compile Include="Xwt\XwtTaskScheduler.cs" />
<Compile Include="Xwt\ExceptionEventArgs.cs" />
<Compile Include="Xwt\RichTextView.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Xwt.Backends\IRichTextViewBackend.cs" />
<Compile Include="Xwt.Formats\TextFormat.cs" />
<Compile Include="Xwt.Formats\MarkdownTextFormat.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup />
@ -372,4 +374,7 @@
</Properties>
</MonoDevelop>
</ProjectExtensions>
<ItemGroup>
<Folder Include="Xwt.Formats\" />
</ItemGroup>
</Project>

128
Xwt/Xwt/RichTextView.cs Normal file
Просмотреть файл

@ -0,0 +1,128 @@
//
// RichTextView.cs
//
// Author:
// Jérémie Laval <jeremie.laval@xamarin.com>
// Alex Corrado <corrado@xamarin.com>
//
// Copyright (c) 2012 Xamarin, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.IO;
using System.Linq;
using System.Text;
using Xwt.Backends;
using Xwt.Formats;
namespace Xwt
{
public class RichTextView : Widget
{
protected new class WidgetBackendHost : Widget.WidgetBackendHost, IRichTextViewEventSink
{
public void OnNavigateToUrl (Uri uri)
{
((RichTextView) Parent).OnNavigateToUrl (new NavigateToUrlEventArgs (uri));
}
}
IRichTextViewBackend Backend {
get { return (IRichTextViewBackend) BackendHost.Backend; }
}
EventHandler<NavigateToUrlEventArgs> navigateToUrl;
public event EventHandler<NavigateToUrlEventArgs> NavigateToUrl
{
add
{
BackendHost.OnBeforeEventAdd (RichTextViewEvent.NavigateToUrl, navigateToUrl);
navigateToUrl += value;
}
remove
{
navigateToUrl -= value;
BackendHost.OnAfterEventRemove (RichTextViewEvent.NavigateToUrl, navigateToUrl);
}
}
public RichTextView ()
{
NavigateToUrl += delegate { }; // ensure the virtual method is always called
}
public void LoadFile (string fileName, TextFormat format)
{
using (var stream = new FileStream (fileName, FileMode.Open, FileAccess.Read))
LoadStream (stream, format);
}
public void LoadText (string text, TextFormat format)
{
using (var stream = new MemoryStream (Encoding.UTF8.GetBytes (text), false))
LoadStream (stream, format);
}
public virtual void LoadStream (Stream input, TextFormat format)
{
var buffer = Backend.CreateBuffer ();
format.Parse (input, buffer);
Backend.SetBuffer (buffer);
}
protected override BackendHost CreateBackendHost ()
{
return new WidgetBackendHost ();
}
protected virtual void OnNavigateToUrl (NavigateToUrlEventArgs e)
{
if (navigateToUrl != null)
navigateToUrl (this, e);
if (!e.Handled)
Application.EngineBackend.ShowWebBrowser (e);
}
}
public class MarkdownView : RichTextView
{
string markdown;
public string Markdown
{
get
{
return markdown;
}
set
{
markdown = value;
LoadText (value, TextFormat.Markdown);
}
}
public MarkdownView ()
{
Markdown = string.Empty;
}
}
}