From f020efedcdc718f553328b7312fe9be4e735714d Mon Sep 17 00:00:00 2001 From: Carl de Billy Date: Sat, 4 Sep 2021 22:31:21 -0400 Subject: [PATCH 1/7] Added HtmlHxText elements, Added HtmlSelect/HtmlOption elements --- .../HtmlControls.Shared/MainPage.xaml | 59 ++++++--- UI/HtmlControls/HtmlControls/HtmlH1Text.cs | 46 +++++++ UI/HtmlControls/HtmlControls/HtmlH2Text.cs | 47 +++++++ UI/HtmlControls/HtmlControls/HtmlH3Text.cs | 47 +++++++ UI/HtmlControls/HtmlControls/HtmlH4Text.cs | 47 +++++++ UI/HtmlControls/HtmlControls/HtmlMeter.cs | 79 ++++++++++++ UI/HtmlControls/HtmlControls/HtmlOption.cs | 52 ++++++++ UI/HtmlControls/HtmlControls/HtmlProgress.cs | 62 ++++++++++ UI/HtmlControls/HtmlControls/HtmlSelect.cs | 117 ++++++++++++++++++ 9 files changed, 539 insertions(+), 17 deletions(-) create mode 100644 UI/HtmlControls/HtmlControls/HtmlH1Text.cs create mode 100644 UI/HtmlControls/HtmlControls/HtmlH2Text.cs create mode 100644 UI/HtmlControls/HtmlControls/HtmlH3Text.cs create mode 100644 UI/HtmlControls/HtmlControls/HtmlH4Text.cs create mode 100644 UI/HtmlControls/HtmlControls/HtmlMeter.cs create mode 100644 UI/HtmlControls/HtmlControls/HtmlOption.cs create mode 100644 UI/HtmlControls/HtmlControls/HtmlProgress.cs create mode 100644 UI/HtmlControls/HtmlControls/HtmlSelect.cs diff --git a/UI/HtmlControls/HtmlControls.Shared/MainPage.xaml b/UI/HtmlControls/HtmlControls.Shared/MainPage.xaml index ba73cdf74..47aca9356 100644 --- a/UI/HtmlControls/HtmlControls.Shared/MainPage.xaml +++ b/UI/HtmlControls/HtmlControls.Shared/MainPage.xaml @@ -8,25 +8,50 @@ xmlns:html="using:HtmlControls" mc:Ignorable="d"> - - HtmlTimePicker <input type='time' /> - - + + + HtmlTimePicker <input type='time' /> + + - HtmlDatePicker <input type='date' /> - - + HtmlDatePicker <input type='date' /> + + - HtmlNumberInput <input type='number' /> - - + HtmlNumberInput <input type='number' /> + + - HtmlColorPicker <input type='color' /> - - + HtmlColorPicker <input type='color' /> + + - HtmlRangeInput <input type='range' /> - - - + HtmlRangeInput <input type='range' /> + + + + HtmlProgress <progress/> + + + HtmlMeter <meter/> + + + HtmlHxText <h1 />, <h2 />, <h3 />, <h4 /> + + + + + + + HtmlSelect / HtmlOption <select />, <option /> + + + Option 1 + Option 2 + Option 3 + + + + + diff --git a/UI/HtmlControls/HtmlControls/HtmlH1Text.cs b/UI/HtmlControls/HtmlControls/HtmlH1Text.cs new file mode 100644 index 000000000..9e7b1f580 --- /dev/null +++ b/UI/HtmlControls/HtmlControls/HtmlH1Text.cs @@ -0,0 +1,46 @@ +using Windows.Foundation; +using Windows.UI; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Media; +using Uno.Extensions; +using Uno.UI.Runtime.WebAssembly; + +namespace HtmlControls +{ + [HtmlElement("h1")] + public partial class HtmlH1Text : FrameworkElement + { + public HtmlH1Text() + { + // Set a background to ensure pointer events are allowed + Background = new SolidColorBrush(Colors.Transparent); + + // Avoid flicking while we're measuring the element + this.SetCssStyle("overflow", "hidden"); + } + + public static readonly DependencyProperty TextProperty = DependencyProperty.Register( + "Text", typeof(string), typeof(HtmlH1Text), new PropertyMetadata("", propertyChangedCallback: OnTextChanged)); + + public string Text + { + get => (string)GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + private static void OnTextChanged(DependencyObject o, DependencyPropertyChangedEventArgs args) + { + if (o is HtmlH1Text element && args.NewValue is string text) + { + element.SetHtmlContent(text); + element.InvalidateMeasure(); + } + } + + protected override Size MeasureOverride(Size availableSize) + { + // Delegate measurement to Html element + return this.MeasureHtmlView(availableSize, false); + } + } +} diff --git a/UI/HtmlControls/HtmlControls/HtmlH2Text.cs b/UI/HtmlControls/HtmlControls/HtmlH2Text.cs new file mode 100644 index 000000000..b8cd90123 --- /dev/null +++ b/UI/HtmlControls/HtmlControls/HtmlH2Text.cs @@ -0,0 +1,47 @@ +using Windows.Foundation; +using Windows.UI; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Media; +using Uno.UI.Runtime.WebAssembly; + +namespace HtmlControls +{ + [HtmlElement("h2")] + public partial class HtmlH2Text : FrameworkElement + { + public HtmlH2Text() + { + // Set a background to ensure pointer events are allowed + Background = new SolidColorBrush(Colors.Transparent); + + // Avoid flicking while we're measuring the element + this.SetCssStyle("overflow", "hidden"); + } + + public static readonly DependencyProperty TextProperty = DependencyProperty.Register( + "Text", typeof(string), typeof(HtmlH2Text), new PropertyMetadata("", propertyChangedCallback: OnTextChanged)); + + public string Text + { + get => (string)GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + private static void OnTextChanged(DependencyObject o, DependencyPropertyChangedEventArgs args) + { + if (o is HtmlH2Text element && args.NewValue is string text) + { + element.SetHtmlContent(text); + element.InvalidateMeasure(); + } + } + + protected override Size MeasureOverride(Size availableSize) + { + // Delegate measurement to Html element + return this.MeasureHtmlView(availableSize, false); + } + } + + +} diff --git a/UI/HtmlControls/HtmlControls/HtmlH3Text.cs b/UI/HtmlControls/HtmlControls/HtmlH3Text.cs new file mode 100644 index 000000000..4ba8162db --- /dev/null +++ b/UI/HtmlControls/HtmlControls/HtmlH3Text.cs @@ -0,0 +1,47 @@ +using Windows.Foundation; +using Windows.UI; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Media; +using Uno.UI.Runtime.WebAssembly; + +namespace HtmlControls +{ + [HtmlElement("h3")] + public partial class HtmlH3Text : FrameworkElement + { + public HtmlH3Text() + { + // Set a background to ensure pointer events are allowed + Background = new SolidColorBrush(Colors.Transparent); + + // Avoid flicking while we're measuring the element + this.SetCssStyle("overflow", "hidden"); + } + + public static readonly DependencyProperty TextProperty = DependencyProperty.Register( + "Text", typeof(string), typeof(HtmlH3Text), new PropertyMetadata("", propertyChangedCallback: OnTextChanged)); + + public string Text + { + get => (string)GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + private static void OnTextChanged(DependencyObject o, DependencyPropertyChangedEventArgs args) + { + if (o is HtmlH3Text element && args.NewValue is string text) + { + element.SetHtmlContent(text); + element.InvalidateMeasure(); + } + } + + protected override Size MeasureOverride(Size availableSize) + { + // Delegate measurement to Html element + return this.MeasureHtmlView(availableSize, false); + } + } + + +} diff --git a/UI/HtmlControls/HtmlControls/HtmlH4Text.cs b/UI/HtmlControls/HtmlControls/HtmlH4Text.cs new file mode 100644 index 000000000..8d72727db --- /dev/null +++ b/UI/HtmlControls/HtmlControls/HtmlH4Text.cs @@ -0,0 +1,47 @@ +using Windows.Foundation; +using Windows.UI; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Media; +using Uno.UI.Runtime.WebAssembly; + +namespace HtmlControls +{ + [HtmlElement("h4")] + public partial class HtmlH4Text : FrameworkElement + { + public HtmlH4Text() + { + // Set a background to ensure pointer events are allowed + Background = new SolidColorBrush(Colors.Transparent); + + // Avoid flicking while we're measuring the element + this.SetCssStyle("overflow", "hidden"); + } + + public static readonly DependencyProperty TextProperty = DependencyProperty.Register( + "Text", typeof(string), typeof(HtmlH4Text), new PropertyMetadata("", propertyChangedCallback: OnTextChanged)); + + public string Text + { + get => (string)GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + private static void OnTextChanged(DependencyObject o, DependencyPropertyChangedEventArgs args) + { + if (o is HtmlH4Text element && args.NewValue is string text) + { + element.SetHtmlContent(text); + element.InvalidateMeasure(); + } + } + + protected override Size MeasureOverride(Size availableSize) + { + // Delegate measurement to Html element + return this.MeasureHtmlView(availableSize, false); + } + } + + +} diff --git a/UI/HtmlControls/HtmlControls/HtmlMeter.cs b/UI/HtmlControls/HtmlControls/HtmlMeter.cs new file mode 100644 index 000000000..0482cb150 --- /dev/null +++ b/UI/HtmlControls/HtmlControls/HtmlMeter.cs @@ -0,0 +1,79 @@ +using System; +using System.Globalization; +using System.Runtime.CompilerServices; +using Windows.Foundation; +using Windows.UI; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Media; +using Uno.Extensions; +using Uno.UI.Runtime.WebAssembly; + +namespace HtmlControls +{ + [HtmlElement("meter")] + public partial class HtmlMeter : FrameworkElement + { + public HtmlMeter() + { + // Set a background to ensure pointer events are allowed + Background = new SolidColorBrush(Colors.Transparent); + } + + public static readonly DependencyProperty MinProperty = DependencyProperty.Register( + "Min", typeof(double), typeof(HtmlMeter), new PropertyMetadata(0d, propertyChangedCallback: OnMinChanged)); + + public double Min + { + get => (double)GetValue(MinProperty); + set => SetValue(MinProperty, value); + } + + private static void OnMinChanged(DependencyObject o, DependencyPropertyChangedEventArgs args) + { + if (o is HtmlMeter meter && args.NewValue is double min) + { + meter.SetAttribute("min", min.ToStringInvariant()); + } + } + + public static readonly DependencyProperty MaxProperty = DependencyProperty.Register( + "Max", typeof(double), typeof(HtmlMeter), new PropertyMetadata(1.0d, propertyChangedCallback: OnMaxChanged)); + + public double Max + { + get => (double)GetValue(MaxProperty); + set => SetValue(MaxProperty, value); + } + + private static void OnMaxChanged(DependencyObject o, DependencyPropertyChangedEventArgs args) + { + if (o is HtmlMeter meter && args.NewValue is double max) + { + meter.SetAttribute("max", max.ToStringInvariant()); + } + } + + public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( + "Value", typeof(double), typeof(HtmlMeter), new PropertyMetadata(default(double), propertyChangedCallback: OnValueChanged)); + + public double Value + { + get => (double)GetValue(ValueProperty); + set => SetValue(ValueProperty, value); + } + + private static void OnValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs args) + { + if (o is HtmlMeter meter && args.NewValue is double value) + { + meter.SetAttribute("value", value.ToStringInvariant()); + } + } + + protected override Size MeasureOverride(Size availableSize) + { + // Delegate measurement to Html element + return this.MeasureHtmlView(availableSize, false); + } + } +} \ No newline at end of file diff --git a/UI/HtmlControls/HtmlControls/HtmlOption.cs b/UI/HtmlControls/HtmlControls/HtmlOption.cs new file mode 100644 index 000000000..d1543bc8e --- /dev/null +++ b/UI/HtmlControls/HtmlControls/HtmlOption.cs @@ -0,0 +1,52 @@ +using Windows.UI.Xaml; +using Windows.UI.Xaml.Markup; +using Uno.UI.Runtime.WebAssembly; + +namespace HtmlControls +{ + [HtmlElement("option")] + [ContentProperty(Name = nameof(Content))] + public partial class HtmlOption : UIElement + { + public static DependencyProperty ContentProperty { get; } = DependencyProperty.Register( + "ContentProperty", typeof(string), typeof(HtmlOption), new PropertyMetadata(null, OnContentChanged)); + + public string Content + { + get => (string)GetValue(ContentProperty); + set => SetValue(ContentProperty, value); + } + + private static void OnContentChanged(DependencyObject o, DependencyPropertyChangedEventArgs args) + { + if (o is HtmlOption option) + { + if (args.NewValue is string str) + { + option.ClearChildren(); + option.SetHtmlContent(str); + } + } + } + + public static DependencyProperty ValueProperty { get; } = DependencyProperty.Register( + "ValueProperty", typeof(string), typeof(HtmlOption), new PropertyMetadata(null, OnValueChanged)); + + public string Value + { + get => (string)GetValue(ValueProperty); + set => SetValue(ValueProperty, value); + } + + private static void OnValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs args) + { + if (o is HtmlOption option) + { + if (args.NewValue is string str) + { + option.SetAttribute("value", str); + } + } + } + } +} diff --git a/UI/HtmlControls/HtmlControls/HtmlProgress.cs b/UI/HtmlControls/HtmlControls/HtmlProgress.cs new file mode 100644 index 000000000..e17068dc2 --- /dev/null +++ b/UI/HtmlControls/HtmlControls/HtmlProgress.cs @@ -0,0 +1,62 @@ +using System; +using System.Globalization; +using System.Runtime.CompilerServices; +using Windows.Foundation; +using Windows.UI; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Media; +using Uno.Extensions; +using Uno.UI.Runtime.WebAssembly; + +namespace HtmlControls +{ + [HtmlElement("progress")] + public partial class HtmlProgress : FrameworkElement + { + public HtmlProgress() + { + // Set a background to ensure pointer events are allowed + Background = new SolidColorBrush(Colors.Transparent); + } + + public static readonly DependencyProperty MaxProperty = DependencyProperty.Register( + "Max", typeof(double), typeof(HtmlProgress), new PropertyMetadata(1.0d, propertyChangedCallback: OnMaxChanged)); + + public double Max + { + get => (double)GetValue(MaxProperty); + set => SetValue(MaxProperty, value); + } + + private static void OnMaxChanged(DependencyObject o, DependencyPropertyChangedEventArgs args) + { + if (o is HtmlProgress progress && args.NewValue is double max) + { + progress.SetAttribute("max", max.ToStringInvariant()); + } + } + + public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( + "Value", typeof(double), typeof(HtmlProgress), new PropertyMetadata(default(double), propertyChangedCallback: OnValueChanged)); + + public double Value + { + get => (double)GetValue(ValueProperty); + set => SetValue(ValueProperty, value); + } + + private static void OnValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs args) + { + if (o is HtmlProgress progress && args.NewValue is double value) + { + progress.SetAttribute("value", value.ToStringInvariant()); + } + } + + protected override Size MeasureOverride(Size availableSize) + { + // Delegate measurement to Html element + return this.MeasureHtmlView(availableSize, false); + } + } +} \ No newline at end of file diff --git a/UI/HtmlControls/HtmlControls/HtmlSelect.cs b/UI/HtmlControls/HtmlControls/HtmlSelect.cs new file mode 100644 index 000000000..7db2d0f21 --- /dev/null +++ b/UI/HtmlControls/HtmlControls/HtmlSelect.cs @@ -0,0 +1,117 @@ +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using Windows.Foundation; +using Windows.UI; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Markup; +using Windows.UI.Xaml.Media; +using Uno.UI.Runtime.WebAssembly; +using System; +using Uno.Extensions; +using System.Globalization; + +namespace HtmlControls +{ + [HtmlElement("select")] + [ContentProperty(Name = nameof(Options))] + public partial class HtmlSelect : FrameworkElement + { + public ObservableCollection Options { get; } = new ObservableCollection(); + + public HtmlSelect() + { + // Set a background to ensure pointer events are allowed + Background = new SolidColorBrush(Colors.Transparent); + + Options.CollectionChanged += OnOptionsChanged; + + this.ExecuteJavascript("element.addEventListener(\"change\", ()=>element.dispatchEvent(new CustomEvent(\"value\", {detail: \"\"+element.selectedIndex})));"); + this.ExecuteJavascript("element.addEventListener(\"loaded\", ()=>element.dispatchEvent(new CustomEvent(\"value\", {detail: \"\"+element.selectedIndex})));"); + this.RegisterHtmlCustomEventHandler("value", OnHtmlSelectionChanged); + } + + private void OnHtmlSelectionChanged(object sender, HtmlCustomEventArgs e) + { + if(int.TryParse(e.Detail, System.Globalization.NumberStyles.Integer | NumberStyles.AllowLeadingSign, NumberFormatInfo.InvariantInfo, out var index)) + { + SelectedIndex = index; + + if (index < 0) + { + // No selection + SelectedValue = null; + } + else + { + SelectedValue = Options[index].Value; + } + } + } + + public static readonly DependencyProperty SelectedValueProperty = DependencyProperty.Register( + "SelectedValue", typeof(string), typeof(HtmlSelect), new PropertyMetadata(default(string), propertyChangedCallback: OnSelectedValueChanged)); + + public string SelectedValue + { + get => (string)GetValue(SelectedValueProperty); + set => SetValue(SelectedValueProperty, value); + } + + private static void OnSelectedValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs args) + { + if (o is HtmlSelect select && args.NewValue is string selectedValue) + { + // TODO + } + } + + public static readonly DependencyProperty SelectedIndexProperty = DependencyProperty.Register( + "SelectedIndex", typeof(int), typeof(HtmlSelect), new PropertyMetadata(default(int), propertyChangedCallback: OnSelectedIndexChanged)); + + public int SelectedIndex + { + get => (int)GetValue(SelectedIndexProperty); + set => SetValue(SelectedIndexProperty, value); + } + + private static void OnSelectedIndexChanged(DependencyObject o, DependencyPropertyChangedEventArgs args) + { + if (o is HtmlSelect select && args.NewValue is int selectedIndex) + { + select.ExecuteJavascript($"element.selectedIndex={selectedIndex};"); + } + } + + private void OnOptionsChanged(object sender, NotifyCollectionChangedEventArgs e) + { + switch(e.Action) + { + case NotifyCollectionChangedAction.Add: + { + var index = e.NewStartingIndex; + foreach(var newItem in e.NewItems) + { + AddChild(newItem as UIElement, index++); + } + break; + } + case NotifyCollectionChangedAction.Remove: + { + foreach (var removedItem in e.OldItems) + { + RemoveChild(removedItem as UIElement); + } + break; + } + default: + throw new NotSupportedException($"CollectionChanged Action \"{e.Action}\" not supported yet."); + } + } + + protected override Size MeasureOverride(Size availableSize) + { + // Delegate measurement to Html