diff --git a/1.0/FirstFloor.ModernUI/FirstFloor.ModernUI.App/Content/ControlsModernBBCodeBlock.xaml b/1.0/FirstFloor.ModernUI/FirstFloor.ModernUI.App/Content/ControlsModernBBCodeBlock.xaml index 13e0d2a9..a5eb7d23 100644 --- a/1.0/FirstFloor.ModernUI/FirstFloor.ModernUI.App/Content/ControlsModernBBCodeBlock.xaml +++ b/1.0/FirstFloor.ModernUI/FirstFloor.ModernUI.App/Content/ControlsModernBBCodeBlock.xaml @@ -10,7 +10,7 @@ - + diff --git a/1.0/FirstFloor.ModernUI/FirstFloor.ModernUI.App/Content/ControlsModernBBCodeBlock.xaml.cs b/1.0/FirstFloor.ModernUI/FirstFloor.ModernUI.App/Content/ControlsModernBBCodeBlock.xaml.cs index e25f6e5b..c6399d5b 100644 --- a/1.0/FirstFloor.ModernUI/FirstFloor.ModernUI.App/Content/ControlsModernBBCodeBlock.xaml.cs +++ b/1.0/FirstFloor.ModernUI/FirstFloor.ModernUI.App/Content/ControlsModernBBCodeBlock.xaml.cs @@ -1,28 +1,28 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace FirstFloor.ModernUI.App.Content -{ - /// - /// Interaction logic for ControlsModernBBCodeBlock.xaml - /// - public partial class ControlsModernBBCodeBlock : UserControl - { - public ControlsModernBBCodeBlock() - { - InitializeComponent(); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace FirstFloor.ModernUI.App.Content +{ + /// + /// Interaction logic for ControlsModernBBCodeBlock.xaml + /// + public partial class ControlsModernBBCodeBlock : UserControl + { + public ControlsModernBBCodeBlock() + { + InitializeComponent(); + } + } +} diff --git a/1.0/FirstFloor.ModernUI/FirstFloor.ModernUI.App/MainWindow.xaml b/1.0/FirstFloor.ModernUI/FirstFloor.ModernUI.App/MainWindow.xaml index 48fd7f22..26d8c68e 100644 --- a/1.0/FirstFloor.ModernUI/FirstFloor.ModernUI.App/MainWindow.xaml +++ b/1.0/FirstFloor.ModernUI/FirstFloor.ModernUI.App/MainWindow.xaml @@ -3,8 +3,12 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mui="http://firstfloorsoftware.com/ModernUI" xmlns:app="clr-namespace:FirstFloor.ModernUI.App" - Title="Modern UI for WPF Demo" IsTitleVisible="True" + Title="Modern UI for WPF Demo" + BBCodeTitle="[b]Modern UI[/b] for [b]WPF[/b] [i]Demo[/i]" + IsTitleVisible="True" LogoData="F1 M 24.9015,43.0378L 25.0963,43.4298C 26.1685,49.5853 31.5377,54.2651 38,54.2651C 44.4623,54.2651 49.8315,49.5854 50.9037,43.4299L 51.0985,43.0379C 51.0985,40.7643 52.6921,39.2955 54.9656,39.2955C 56.9428,39.2955 58.1863,41.1792 58.5833,43.0379C 57.6384,52.7654 47.9756,61.75 38,61.75C 28.0244,61.75 18.3616,52.7654 17.4167,43.0378C 17.8137,41.1792 19.0572,39.2954 21.0344,39.2954C 23.3079,39.2954 24.9015,40.7643 24.9015,43.0378 Z M 26.7727,20.5833C 29.8731,20.5833 32.3864,23.0966 32.3864,26.197C 32.3864,29.2973 29.8731,31.8106 26.7727,31.8106C 23.6724,31.8106 21.1591,29.2973 21.1591,26.197C 21.1591,23.0966 23.6724,20.5833 26.7727,20.5833 Z M 49.2273,20.5833C 52.3276,20.5833 54.8409,23.0966 54.8409,26.197C 54.8409,29.2973 52.3276,31.8106 49.2273,31.8106C 46.127,31.8106 43.6136,29.2973 43.6136,26.197C 43.6136,23.0966 46.127,20.5833 49.2273,20.5833 Z" + LogoCommand="mui:LinkCommands.NavigateLink" + LogoCommandParameter="https://github.com/firstfloorsoftware/mui" ContentSource="/Pages/Introduction.xaml" Width="800" Height="700"> diff --git a/1.0/FirstFloor.ModernUI/FirstFloor.ModernUI/Themes/ModernWindow.xaml b/1.0/FirstFloor.ModernUI/FirstFloor.ModernUI/Themes/ModernWindow.xaml index 9db7dbf2..6dd2b722 100644 --- a/1.0/FirstFloor.ModernUI/FirstFloor.ModernUI/Themes/ModernWindow.xaml +++ b/1.0/FirstFloor.ModernUI/FirstFloor.ModernUI/Themes/ModernWindow.xaml @@ -59,9 +59,23 @@ - + + + + + @@ -91,11 +105,33 @@ - - - + + + + + diff --git a/1.0/FirstFloor.ModernUI/Shared/Windows/Controls/BBCode/BBCodeLexer.cs b/1.0/FirstFloor.ModernUI/Shared/Windows/Controls/BBCode/BBCodeLexer.cs index c228db68..560f5846 100644 --- a/1.0/FirstFloor.ModernUI/Shared/Windows/Controls/BBCode/BBCodeLexer.cs +++ b/1.0/FirstFloor.ModernUI/Shared/Windows/Controls/BBCode/BBCodeLexer.cs @@ -99,6 +99,17 @@ private Token Text() return new Token(GetMark(), TokenText); } + private Token EscapedTag() + { + Mark(); + Consume(); + while (LA(1) != '[' && LA(1) != char.MaxValue && !IsInRange(NewlineChars)) { + Consume(); + } + var result = GetMark(); + return new Token(result.Substring(0, 1) + result.Substring(2), TokenText); + } + private Token Attribute() { Match('='); @@ -153,6 +164,9 @@ public override Token NextToken() if (State == StateNormal) { if (LA(1) == '[') { + if (LA(2) == '\\') { + return EscapedTag(); + } if (LA(2) == '/') { return CloseTag(); } diff --git a/1.0/FirstFloor.ModernUI/Shared/Windows/Controls/BBCode/BBCodeParser.cs b/1.0/FirstFloor.ModernUI/Shared/Windows/Controls/BBCode/BBCodeParser.cs index 008bd085..c2bdc033 100644 --- a/1.0/FirstFloor.ModernUI/Shared/Windows/Controls/BBCode/BBCodeParser.cs +++ b/1.0/FirstFloor.ModernUI/Shared/Windows/Controls/BBCode/BBCodeParser.cs @@ -24,6 +24,11 @@ internal class BBCodeParser private const string TagSize = "size"; private const string TagUnderline = "u"; private const string TagUrl = "url"; + private const string TagStrikethrough = "s"; + private const string TagQuote = "quote"; + private const string TagList = "list"; + private const string TagOrderedList = "ol"; + private const string TagListItem = "li"; class ParseContext { @@ -36,8 +41,14 @@ public ParseContext(Span parent) public FontWeight? FontWeight { get; set; } public FontStyle? FontStyle { get; set; } public Brush Foreground { get; set; } + public Brush Background { get; set; } public TextDecorationCollection TextDecorations { get; set; } public string NavigateUri { get; set; } + public bool IsList { get; set; } + public bool IsOrderedList { get; set; } + public int ListCounter { get; set; } + public bool IsListItem { get; set; } + public bool IsFirstListItem { get; set; } /// /// Creates a run reflecting the current context settings. @@ -58,6 +69,10 @@ public Run CreateRun(string text) if (this.Foreground != null) { run.Foreground = this.Foreground; } + if (this.Background != null) + { + run.Background = this.Background; + } run.TextDecorations = this.TextDecorations; return run; @@ -65,19 +80,22 @@ public Run CreateRun(string text) } private FrameworkElement source; + private Brush quoteBrush; /// /// Initializes a new instance of the class. /// /// The value. /// The framework source element this parser operates in. - public BBCodeParser(string value, FrameworkElement source) + /// The Brush used for quoting + public BBCodeParser(string value, FrameworkElement source, Brush quoteBrush = null) : base(new BBCodeLexer(value)) { if (source == null) { throw new ArgumentNullException("source"); } this.source = source; + this.quoteBrush = quoteBrush; } /// @@ -131,6 +149,10 @@ private void ParseTag(string tag, bool start, ParseContext context) else if (tag == TagUnderline) { context.TextDecorations = start ? TextDecorations.Underline : null; } + else if (tag == TagStrikethrough) + { + context.TextDecorations = start ? TextDecorations.Strikethrough : null; + } else if (tag == TagUrl) { if (start) { Token token = LA(1); @@ -143,20 +165,59 @@ private void ParseTag(string tag, bool start, ParseContext context) context.NavigateUri = null; } } + else if (tag == TagQuote) { + if (start) { + context.Background = quoteBrush; + } + else { + context.Background = null; + } + } + else if (tag == TagList) { + context.IsList = start; + context.IsFirstListItem = true; + } + else if (tag == TagOrderedList) { + context.IsOrderedList = start; + context.ListCounter = 0; + context.IsFirstListItem = true; + } + else if (tag == TagListItem) { + context.IsListItem = start; + } } private void Parse(Span span) { var context = new ParseContext(span); - + while (true) { Token token = LA(1); Consume(); if (token.TokenType == BBCodeLexer.TokenStartTag) { + var parent = span; + if (token.Value == TagListItem) + { + if (context.IsFirstListItem) + { + context.IsFirstListItem = false; + parent.Inlines.Add(new LineBreak()); + } + + parent.Inlines.Add( + new Run(string.Format("\u0020\u0020{0}\u0020\u0020", + context.IsOrderedList ? string.Format("{0}.", ++context.ListCounter) : "\u2022"))); + } + ParseTag(token.Value, true, context); } else if (token.TokenType == BBCodeLexer.TokenEndTag) { + var parent = span; + if (context.IsListItem && token.Value == TagListItem) + { + parent.Inlines.Add(new LineBreak()); + } ParseTag(token.Value, false, context); } else if (token.TokenType == BBCodeLexer.TokenText) { @@ -183,10 +244,20 @@ private void Parse(Span span) link.TargetName = parameter; } parent = link; + if (context.Foreground != null) { + link.Foreground = context.Foreground; + } span.Inlines.Add(parent); } - var run = context.CreateRun(token.Value); - parent.Inlines.Add(run); + + if (context.IsListItem) { + parent.Inlines.Add(context.CreateRun(token.Value)); + } + else { + var run = context.CreateRun(token.Value); + parent.Inlines.Add(run); + } + } else if (token.TokenType == BBCodeLexer.TokenLineBreak) { span.Inlines.Add(new LineBreak()); diff --git a/1.0/FirstFloor.ModernUI/Shared/Windows/Controls/BBCodeBlock.cs b/1.0/FirstFloor.ModernUI/Shared/Windows/Controls/BBCodeBlock.cs index fb16f446..96447249 100644 --- a/1.0/FirstFloor.ModernUI/Shared/Windows/Controls/BBCodeBlock.cs +++ b/1.0/FirstFloor.ModernUI/Shared/Windows/Controls/BBCodeBlock.cs @@ -32,6 +32,10 @@ public class BBCodeBlock /// Identifies the LinkNavigator dependency property. /// public static DependencyProperty LinkNavigatorProperty = DependencyProperty.Register("LinkNavigator", typeof(ILinkNavigator), typeof(BBCodeBlock), new PropertyMetadata(new DefaultLinkNavigator(), OnLinkNavigatorChanged)); + /// + /// Identifies the BBCodeQuote dependency property. + /// + public static DependencyProperty BBCodeQuoteBackgroundProperty = DependencyProperty.Register("BBCodeQuoteBackground", typeof(Brush), typeof(BBCodeBlock), new PropertyMetadata(new PropertyChangedCallback(OnBBCodeQuoteBackgroundChanged))); private bool dirty = false; @@ -52,6 +56,11 @@ private static void OnBBCodeChanged(DependencyObject o, DependencyPropertyChange ((BBCodeBlock)o).UpdateDirty(); } + private static void OnBBCodeQuoteBackgroundChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + ((BBCodeBlock)o).UpdateDirty(); + } + private static void OnLinkNavigatorChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { if (e.NewValue == null) { @@ -86,7 +95,7 @@ private void Update() if (!string.IsNullOrWhiteSpace(bbcode)) { Inline inline; try { - var parser = new BBCodeParser(bbcode, this) { + var parser = new BBCodeParser(bbcode, this, BBCodeQuoteBackground) { Commands = this.LinkNavigator.Commands }; inline = parser.Parse(); @@ -131,5 +140,16 @@ public ILinkNavigator LinkNavigator get { return (ILinkNavigator)GetValue(LinkNavigatorProperty); } set { SetValue(LinkNavigatorProperty, value); } } + + /// + /// Gets or sets the BB quote background. + /// + /// The BB code. + public Brush BBCodeQuoteBackground + { + get { return (Brush)GetValue(BBCodeQuoteBackgroundProperty); } + set { SetValue(BBCodeQuoteBackgroundProperty, value); } + } + } } diff --git a/1.0/FirstFloor.ModernUI/Shared/Windows/Controls/ModernWindow.cs b/1.0/FirstFloor.ModernUI/Shared/Windows/Controls/ModernWindow.cs index cac6cddb..e3f8403e 100644 --- a/1.0/FirstFloor.ModernUI/Shared/Windows/Controls/ModernWindow.cs +++ b/1.0/FirstFloor.ModernUI/Shared/Windows/Controls/ModernWindow.cs @@ -41,6 +41,14 @@ public class ModernWindow /// public static readonly DependencyProperty LogoDataProperty = DependencyProperty.Register("LogoData", typeof(Geometry), typeof(ModernWindow)); /// + /// Identifies the LogoCommand dependency property. + /// + public static readonly DependencyProperty LogoCommandProperty = DependencyProperty.Register("LogoCommand", typeof(ICommand), typeof(ModernWindow)); + /// + /// Identifies the LogoCommandParameter dependency property. + /// + public static readonly DependencyProperty LogoCommandParameterProperty = DependencyProperty.Register("LogoCommandParameter", typeof(object), typeof(ModernWindow)); + /// /// Defines the ContentSource dependency property. /// public static readonly DependencyProperty ContentSourceProperty = DependencyProperty.Register("ContentSource", typeof(Uri), typeof(ModernWindow)); @@ -53,6 +61,11 @@ public class ModernWindow /// public static DependencyProperty LinkNavigatorProperty = DependencyProperty.Register("LinkNavigator", typeof(ILinkNavigator), typeof(ModernWindow), new PropertyMetadata(new DefaultLinkNavigator())); + /// + /// Identifies the BBCodeTitle dependency property. + /// + public static DependencyProperty BBCodeTitleProperty = DependencyProperty.Register("BBCodeTitle", typeof(string), typeof(ModernWindow), new PropertyMetadata(null)); + private Storyboard backgroundAnimation; /// @@ -248,6 +261,24 @@ public Geometry LogoData set { SetValue(LogoDataProperty, value); } } + /// + /// Gets or sets the command for the logo. + /// + public ICommand LogoCommand + { + get { return (ICommand)GetValue(LogoCommandProperty); } + set { SetValue(LogoCommandProperty, value); } + } + + /// + /// Gets or sets the command parameter for the logo. + /// + public object LogoCommandParameter + { + get { return (object)GetValue(LogoCommandParameterProperty); } + set { SetValue(LogoCommandParameterProperty, value); } + } + /// /// Gets or sets the source uri of the current content. /// @@ -275,5 +306,15 @@ public ILinkNavigator LinkNavigator get { return (ILinkNavigator)GetValue(LinkNavigatorProperty); } set { SetValue(LinkNavigatorProperty, value); } } + + /// + /// Gets or sets the BBCode Title. + /// + /// The link navigator. + public string BBCodeTitle + { + get { return (string)GetValue(BBCodeTitleProperty); } + set { SetValue(BBCodeTitleProperty, value); } + } } }