diff --git a/Ooui.Forms/Exports.cs b/Ooui.Forms/Exports.cs index 49e6edc..c823d1b 100644 --- a/Ooui.Forms/Exports.cs +++ b/Ooui.Forms/Exports.cs @@ -5,11 +5,14 @@ using Xamarin.Forms; using Xamarin.Forms.Internals; [assembly: Dependency (typeof (ResourcesProvider))] +[assembly: ExportRenderer (typeof (ActivityIndicator), typeof (ActivityIndicatorRenderer))] [assembly: ExportRenderer (typeof (BoxView), typeof (BoxRenderer))] [assembly: ExportRenderer (typeof (Button), typeof (ButtonRenderer))] -[assembly: ExportRenderer(typeof(Editor), typeof(EditorRenderer))] +[assembly: ExportRenderer (typeof (DatePicker), typeof (DatePickerRenderer))] +[assembly: ExportRenderer (typeof (Editor), typeof (EditorRenderer))] [assembly: ExportRenderer (typeof (Entry), typeof (EntryRenderer))] [assembly: ExportRenderer (typeof (Label), typeof (LabelRenderer))] +[assembly: ExportRenderer (typeof (ProgressBar), typeof (ProgressBarRenderer))] namespace Ooui.Forms { diff --git a/Ooui.Forms/Renderers/ActivityIndicatorRenderer.cs b/Ooui.Forms/Renderers/ActivityIndicatorRenderer.cs new file mode 100644 index 0000000..e2dec9d --- /dev/null +++ b/Ooui.Forms/Renderers/ActivityIndicatorRenderer.cs @@ -0,0 +1,67 @@ +using System; +using System.ComponentModel; +using System.Linq; +using Xamarin.Forms; + +namespace Ooui.Forms.Renderers +{ + public class ActivityIndicatorRenderer : ViewRenderer + { + public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint) + { + var size = new Size (40, 20); + return new SizeRequest (size, size); + } + + protected override void OnElementChanged (ElementChangedEventArgs e) + { + if (e.NewElement != null) { + if (Control == null) { + var p = new Div { ClassName = "progress" }; + var pb = new Div { ClassName = "progress-bar progress-bar-striped" }; + pb.SetAttribute ("role", "progressbar"); + pb.SetAttribute ("aria-valuenow", "0"); + pb.SetAttribute ("aria-valuemin", "0"); + pb.SetAttribute ("aria-valuemax", "100"); + pb.Style.Width = "0%"; + p.AppendChild (pb); + SetNativeControl (p); + } + + UpdateColor (); + UpdateIsRunning (); + } + + base.OnElementChanged (e); + } + + protected override void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged (sender, e); + + if (e.PropertyName == ActivityIndicator.ColorProperty.PropertyName) + UpdateColor (); + else if (e.PropertyName == ActivityIndicator.IsRunningProperty.PropertyName) + UpdateIsRunning (); + } + + void UpdateColor () + { + } + + void UpdateIsRunning () + { + var pb = (Div)Control.Children[0]; + if (Element.IsRunning) { + pb.SetAttribute ("aria-valuenow", "100"); + pb.Style.Width = "100%"; + pb.ClassName = "progress-bar progress-bar-striped active"; + } + else { + pb.SetAttribute ("aria-valuenow", "0"); + pb.Style.Width = "0%"; + pb.ClassName = "progress-bar progress-bar-striped"; + } + } + } +} diff --git a/Ooui.Forms/Renderers/DatePickerRenderer.cs b/Ooui.Forms/Renderers/DatePickerRenderer.cs new file mode 100644 index 0000000..79bb422 --- /dev/null +++ b/Ooui.Forms/Renderers/DatePickerRenderer.cs @@ -0,0 +1,111 @@ +using System; +using System.ComponentModel; +using Xamarin.Forms; +using Ooui.Forms.Extensions; + +namespace Ooui.Forms.Renderers +{ + public class DatePickerRenderer : ViewRenderer + { + bool _disposed; + + IElementController ElementController => Element as IElementController; + + public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint) + { + var size = "00/00/0000".MeasureSize ("", 16.0, FontAttributes.None); + size = new Size (size.Width, size.Height * 1.428 + 14); + return new SizeRequest (size, size); + } + + protected override void OnElementChanged (ElementChangedEventArgs e) + { + base.OnElementChanged (e); + + if (e.NewElement == null) + return; + + if (Control == null) { + var entry = new Input { + ClassName = "form-control", + Type = InputType.Date, + }; + + entry.Inputted += OnStarted; + entry.Changed += OnEnded; + + SetNativeControl (entry); + } + + UpdateDateFromModel (false); + UpdateMaximumDate (); + UpdateMinimumDate (); + UpdateTextColor (); + UpdateFlowDirection (); + } + + protected override void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged (sender, e); + + if (e.PropertyName == DatePicker.DateProperty.PropertyName || e.PropertyName == DatePicker.FormatProperty.PropertyName) + UpdateDateFromModel (true); + else if (e.PropertyName == DatePicker.MinimumDateProperty.PropertyName) + UpdateMinimumDate (); + else if (e.PropertyName == DatePicker.MaximumDateProperty.PropertyName) + UpdateMaximumDate (); + else if (e.PropertyName == DatePicker.TextColorProperty.PropertyName || e.PropertyName == VisualElement.IsEnabledProperty.PropertyName) + UpdateTextColor (); + } + + void OnEnded (object sender, EventArgs eventArgs) + { + DateTime.TryParseExact (Control.Value, "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AssumeUniversal, out var date); + ElementController?.SetValueFromRenderer (DatePicker.DateProperty, date); + ElementController?.SetValueFromRenderer (VisualElement.IsFocusedPropertyKey, false); + } + + void OnStarted (object sender, EventArgs eventArgs) + { + ElementController?.SetValueFromRenderer (VisualElement.IsFocusedPropertyKey, true); + } + + void UpdateDateFromModel (bool animate) + { + Control.Value = Element.Date.ToString ("yyyy-MM-dd"); + } + + void UpdateFlowDirection () + { + } + + void UpdateMaximumDate () + { + } + + void UpdateMinimumDate () + { + } + + void UpdateTextColor () + { + } + + protected override void Dispose (bool disposing) + { + if (_disposed) + return; + + _disposed = true; + + if (disposing) { + if (Control != null) { + Control.Inputted -= OnStarted; + Control.Changed -= OnEnded; + } + } + + base.Dispose (disposing); + } + } +} diff --git a/Ooui.Forms/Renderers/EditorRenderer.cs b/Ooui.Forms/Renderers/EditorRenderer.cs index d5b9137..880c51e 100644 --- a/Ooui.Forms/Renderers/EditorRenderer.cs +++ b/Ooui.Forms/Renderers/EditorRenderer.cs @@ -1,124 +1,129 @@ -using Ooui.Forms.Extensions; +using System; using System.ComponentModel; -using System.Diagnostics; using Xamarin.Forms; +using Ooui.Forms.Extensions; namespace Ooui.Forms.Renderers { - public class EditorRenderer : ViewRenderer + public class EditorRenderer : ViewRenderer { - private bool _disposed; - private Ooui.Color _defaultTextColor; + bool _disposed; + IEditorController ElementController => Element; - static Size initialSize = Size.Zero; - - protected IElementController ElementController => Element as IElementController; - - public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) - { - var size = Element.Text.MeasureSize(Element.FontFamily, Element.FontSize, Element.FontAttributes); - size = new Size(size.Width, size.Height * 1.428 + 14); - - return new SizeRequest(size, size); - } - - protected override void Dispose(bool disposing) + protected override void Dispose (bool disposing) { if (_disposed) return; _disposed = true; - if (disposing) - { - if (Control != null) - { - Control.Inputted -= OnEditingChanged; - Control.Changed -= OnEditingEnded; + if (disposing) { + if (Control != null) { + Control.Changed -= HandleChanged; + //Control.Started -= OnStarted; + //Control.Ended -= OnEnded; } } - base.Dispose(disposing); + base.Dispose (disposing); } - protected override void OnElementChanged(ElementChangedEventArgs e) + protected override void OnElementChanged (ElementChangedEventArgs e) { + base.OnElementChanged (e); + if (e.NewElement == null) return; - if (Control == null) - { - SetNativeControl(new Ooui.TextArea()); + if (Control == null) { + SetNativeControl (new TextArea { + ClassName = "form-control" + }); - _defaultTextColor = Colors.Black; - - Debug.Assert(Control != null, "Control != null"); - - Control.Inputted += OnEditingChanged; - Control.Changed += OnEditingEnded; + Control.Changed += HandleChanged; + //Control.Started += OnStarted; + //Control.Ended += OnEnded; } - UpdateText(); - UpdateTextColor(); - UpdateFont(); - - base.OnElementChanged(e); + UpdateText (); + UpdateFont (); + UpdateTextColor (); + UpdateKeyboard (); + UpdateEditable (); + UpdateTextAlignment (); } - protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + protected override void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e) { - base.OnElementPropertyChanged(sender, e); + base.OnElementPropertyChanged (sender, e); if (e.PropertyName == Editor.TextProperty.PropertyName) - UpdateText(); + UpdateText (); + else if (e.PropertyName == Xamarin.Forms.InputView.KeyboardProperty.PropertyName) + UpdateKeyboard (); + else if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName) + UpdateEditable (); else if (e.PropertyName == Editor.TextColorProperty.PropertyName) - UpdateTextColor(); + UpdateTextColor (); else if (e.PropertyName == Editor.FontAttributesProperty.PropertyName) - UpdateFont(); + UpdateFont (); else if (e.PropertyName == Editor.FontFamilyProperty.PropertyName) - UpdateFont(); + UpdateFont (); else if (e.PropertyName == Editor.FontSizeProperty.PropertyName) - UpdateFont(); + UpdateFont (); } - void UpdateText() + void HandleChanged (object sender, EventArgs e) { - if (Control.Value != Element.Text) - Control.Value = Element.Text; + ElementController.SetValueFromRenderer (Editor.TextProperty, Control.Text); } - void UpdateTextColor() + void OnEnded (object sender, EventArgs eventArgs) + { + if (Control.Text != Element.Text) + ElementController.SetValueFromRenderer (Editor.TextProperty, Control.Text); + + Element.SetValue (VisualElement.IsFocusedPropertyKey, false); + ElementController.SendCompleted (); + } + + void OnStarted (object sender, EventArgs eventArgs) + { + ElementController.SetValueFromRenderer (VisualElement.IsFocusedPropertyKey, true); + } + + void UpdateEditable () + { + Control.IsDisabled = !Element.IsEnabled; + } + + void UpdateFont () + { + Element.SetStyleFont (Element.FontFamily, Element.FontSize, Element.FontAttributes, Control.Style); + } + + void UpdateKeyboard () + { + } + + void UpdateText () + { + if (Control.Text != Element.Text) + Control.Text = Element.Text; + } + + void UpdateTextAlignment () + { + } + + void UpdateTextColor () { var textColor = Element.TextColor; - if (textColor.IsDefault || !Element.IsEnabled) - Control.Style.Color = _defaultTextColor; + if (textColor.IsDefault) + Control.Style.Color = "black"; else - Control.Style.Color = textColor.ToOouiColor(); - } - - void UpdateFont() - { - if (initialSize == Size.Zero) - { - var testString = "Tj"; - initialSize = testString.MeasureSize(Control.Style); - } - - Element.SetStyleFont(Element.FontFamily, Element.FontSize, Element.FontAttributes, Control.Style); - } - - private void OnEditingChanged(object sender, TargetEventArgs e) - { - ElementController.SetValueFromRenderer(Editor.TextProperty, Control.Value); - } - - private void OnEditingEnded(object sender, TargetEventArgs e) - { - if (Control.Text != Element.Text) - { - ElementController.SetValueFromRenderer(Editor.TextProperty, Control.Text); - } + Control.Style.Color = textColor.ToOouiColor (); } } } diff --git a/Ooui.Forms/Renderers/ProgressBarRenderer.cs b/Ooui.Forms/Renderers/ProgressBarRenderer.cs new file mode 100644 index 0000000..ddeff72 --- /dev/null +++ b/Ooui.Forms/Renderers/ProgressBarRenderer.cs @@ -0,0 +1,51 @@ +using System; +using System.ComponentModel; +using System.Linq; +using Xamarin.Forms; + +namespace Ooui.Forms.Renderers +{ + public class ProgressBarRenderer : ViewRenderer + { + public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint) + { + var size = new Size (80, 20); + return new SizeRequest (size, size); + } + + protected override void OnElementChanged (ElementChangedEventArgs e) + { + if (e.NewElement != null) { + if (Control == null) { + var p = new Div { ClassName = "progress" }; + var pb = new Div { ClassName = "progress-bar progress-bar" }; + pb.SetAttribute ("role", "progressbar"); + pb.SetAttribute ("aria-valuenow", "0"); + pb.SetAttribute ("aria-valuemin", "0"); + pb.SetAttribute ("aria-valuemax", "100"); + pb.Style.Width = "0%"; + p.AppendChild (pb); + SetNativeControl (p); + } + + UpdateProgress (); + } + + base.OnElementChanged (e); + } + + protected override void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged (sender, e); + + if (e.PropertyName == ProgressBar.ProgressProperty.PropertyName) + UpdateProgress (); + } + + void UpdateProgress () + { + var pb = Control?.Children.FirstOrDefault () as Div; + pb.Style.Width = string.Format (System.Globalization.CultureInfo.InvariantCulture, "{0}%", Element.Progress*100); + } + } +} diff --git a/Ooui.Forms/Renderers/ViewRenderer.cs b/Ooui.Forms/Renderers/ViewRenderer.cs index 75dfb02..edbe129 100644 --- a/Ooui.Forms/Renderers/ViewRenderer.cs +++ b/Ooui.Forms/Renderers/ViewRenderer.cs @@ -100,11 +100,8 @@ namespace Ooui.Forms.Renderers public override void SetControlSize (Size size) { if (Control != null) { - //Control.Style.Position = "absolute"; - //Control.Style.Left = "0px"; - //Control.Style.Top = "0px"; - Control.Style.Width = size.Width + "px"; - Control.Style.Height = size.Height + "px"; + Control.Style.Width = size.Width; + Control.Style.Height = size.Height; } } diff --git a/Ooui/Client.js b/Ooui/Client.js index 108fc35..e796069 100644 --- a/Ooui/Client.js +++ b/Ooui/Client.js @@ -84,7 +84,7 @@ function ooui (rootElementPath) { if (!resizeTimeout) { resizeTimeout = setTimeout(function() { resizeTimeout = null; - actualResizeHandler(); + resizeHandler(); }, 100); } } @@ -145,6 +145,17 @@ function msgSet (m) { if (debug) console.log ("Set", node, parts, value); } +function msgSetAttr (m) { + const id = m.id; + const node = getNode (id); + if (!node) { + console.error ("Unknown node id", m); + return; + } + node.setAttribute(m.k, m.v); + if (debug) console.log ("SetAttr", node, m.k, m.v); +} + function msgCall (m) { const id = m.id; const node = getNode (id); @@ -203,6 +214,9 @@ function processMessage (m) { case "set": msgSet (m); break; + case "setAttr": + msgSetAttr (m); + break; case "call": msgCall (m); break; diff --git a/Ooui/Element.cs b/Ooui/Element.cs index 8ba85e9..a7287d1 100644 --- a/Ooui/Element.cs +++ b/Ooui/Element.cs @@ -102,6 +102,16 @@ namespace Ooui Style.PropertyChanged += HandleStylePropertyChanged; } + public void SetAttribute (string attributeName, string value) + { + Send (new Message { + MessageType = MessageType.SetAttribute, + TargetId = Id, + Key = attributeName, + Value = value, + }); + } + void HandleStylePropertyChanged (object sender, PropertyChangedEventArgs e) { SendSet ("style." + Style.GetJsName (e.PropertyName), Style[e.PropertyName]); diff --git a/Ooui/EventTarget.cs b/Ooui/EventTarget.cs index 2f66139..a7be894 100644 --- a/Ooui/EventTarget.cs +++ b/Ooui/EventTarget.cs @@ -163,6 +163,12 @@ namespace Ooui state.Add (message); }); break; + case MessageType.SetAttribute: + UpdateStateMessages (state => { + state.RemoveAll (x => x.MessageType == MessageType.SetAttribute && x.Key == message.Key); + state.Add (message); + }); + break; case MessageType.Listen: AddStateMessage (message); break; diff --git a/Ooui/Message.cs b/Ooui/Message.cs index ae9ffb1..0498437 100644 --- a/Ooui/Message.cs +++ b/Ooui/Message.cs @@ -46,6 +46,8 @@ namespace Ooui Create, [EnumMember(Value = "set")] Set, + [EnumMember (Value = "setAttr")] + SetAttribute, [EnumMember(Value = "call")] Call, [EnumMember(Value = "listen")] diff --git a/Samples/DisplayAlertPage.xaml b/Samples/DisplayAlertPage.xaml index 8f3c57f..fd29d53 100644 --- a/Samples/DisplayAlertPage.xaml +++ b/Samples/DisplayAlertPage.xaml @@ -4,8 +4,11 @@ x:Class="Samples.DisplayAlertPage"> -