From 957f9d73339434ac254b53ca0d0188b17eed28c7 Mon Sep 17 00:00:00 2001 From: "Frank A. Krueger" Date: Thu, 1 Feb 2018 23:14:07 -0800 Subject: [PATCH 1/5] Added issue #48 repro --- Ooui/Client.js | 2 +- Samples/SwitchErrorSample.cs | 40 ++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 Samples/SwitchErrorSample.cs diff --git a/Ooui/Client.js b/Ooui/Client.js index 7e1116a..9d9e458 100644 --- a/Ooui/Client.js +++ b/Ooui/Client.js @@ -1,6 +1,6 @@ // Ooui v1.0.0 -var debug = false; +var debug = true; const nodes = {}; const hasText = {}; diff --git a/Samples/SwitchErrorSample.cs b/Samples/SwitchErrorSample.cs new file mode 100644 index 0000000..184f90d --- /dev/null +++ b/Samples/SwitchErrorSample.cs @@ -0,0 +1,40 @@ +using Ooui; +using Xamarin.Forms; + +namespace Samples +{ + // From https://github.com/praeclarum/Ooui/issues/48 + public class SwitchErrorSample : ISample + { + public string Title => "Xamarin.Forms Switch Error"; + + public Ooui.Element CreateElement () + { + var layout = new StackLayout(); + var label = new Xamarin.Forms.Label + { + Text = "Switch state goes here", + HorizontalTextAlignment = TextAlignment.Center + }; + var sw = new Switch + { + HorizontalOptions = LayoutOptions.CenterAndExpand + }; + sw.Toggled += (sender, args) => + { + label.Text = $"Switch state is :{((Switch)sender).IsToggled}"; + }; + layout.Children.Add(label); + layout.Children.Add(sw); + return new ContentPage + { + Content = layout + }.GetOouiElement(); + } + + public void Publish() + { + UI.Publish ("/switch", CreateElement); + } + } +} From 8c97a625e1816c64b2b76ec1f09acf208add49ea Mon Sep 17 00:00:00 2001 From: "Frank A. Krueger" Date: Fri, 2 Feb 2018 19:51:35 -0800 Subject: [PATCH 2/5] Fix checkbox IsChecked property --- Ooui/Element.cs | 2 +- Ooui/Input.cs | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Ooui/Element.cs b/Ooui/Element.cs index 2e32512..e3193e1 100644 --- a/Ooui/Element.cs +++ b/Ooui/Element.cs @@ -116,7 +116,7 @@ namespace Ooui protected bool SetBooleanAttributeProperty (string attributeName, bool newValue, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "") { var old = GetAttribute (attributeName) != null; - if (old != newValue) + if (old == newValue) return false; if (newValue) SetAttribute (attributeName, string.Empty); diff --git a/Ooui/Input.cs b/Ooui/Input.cs index 9574d11..d114c2d 100644 --- a/Ooui/Input.cs +++ b/Ooui/Input.cs @@ -40,11 +40,7 @@ namespace Ooui public bool IsChecked { get => GetBooleanAttribute ("checked"); - set { - if (SetBooleanAttributeProperty ("checked", value)) { - TriggerEventFromMessage (Message.Event (Id, "change", IsChecked)); - } - } + set => SetBooleanAttributeProperty ("checked", value); } public double Minimum { From e4b1c44aa3a05766408c77313908a1d9c6b8aeb5 Mon Sep 17 00:00:00 2001 From: "Frank A. Krueger" Date: Fri, 2 Feb 2018 20:18:03 -0800 Subject: [PATCH 3/5] Remove jQuery --- Ooui.Forms/Renderers/SwitchRenderer.cs | 2 -- Ooui/Client.js | 15 +++------------ Ooui/Element.cs | 14 -------------- Ooui/UI.cs | 3 --- 4 files changed, 3 insertions(+), 31 deletions(-) diff --git a/Ooui.Forms/Renderers/SwitchRenderer.cs b/Ooui.Forms/Renderers/SwitchRenderer.cs index 737eb50..8dd639b 100644 --- a/Ooui.Forms/Renderers/SwitchRenderer.cs +++ b/Ooui.Forms/Renderers/SwitchRenderer.cs @@ -27,9 +27,7 @@ namespace Ooui.Forms.Renderers if (e.NewElement != null) { if (Control == null) { var input = new Input (InputType.Checkbox); - input.SetAttribute ("data-toggle", "toggle"); SetNativeControl (input); - input.Call ("$.bootstrapToggle"); Control.Change += OnControlValueChanged; } diff --git a/Ooui/Client.js b/Ooui/Client.js index 9d9e458..d30cb0c 100644 --- a/Ooui/Client.js +++ b/Ooui/Client.js @@ -84,19 +84,11 @@ function ooui (rootElementPath) { const messages = JSON.parse (event.data); if (debug) console.log("Messages", messages); if (Array.isArray (messages)) { - const jqs = [] messages.forEach (function (m) { // console.log('Raw value from server', m.v); m.v = fixupValue (m.v); - if (m.k.startsWith ("$.")) { - jqs.push (m); - } - else { - processMessage (m); - } + processMessage (m); }); - // Run jQuery functions last since they usually require a fully built DOM - jqs.forEach (processMessage); } }); @@ -212,15 +204,14 @@ function msgCall (m) { console.error ("Unknown node id", m); return; } - const isJQuery = m.k.startsWith ("$."); - const target = isJQuery ? $(node) : node; + const target = node; if (m.k === "insertBefore" && m.v[0].nodeType == Node.TEXT_NODE && m.v[1] == null && hasText[id]) { // Text is already set so it clear it first if (target.firstChild) target.removeChild (target.firstChild); delete hasText[id]; } - const f = isJQuery ? target[m.k.slice(2)] : target[m.k]; + const f = target[m.k]; if (debug) console.log ("Call", node, f, m.v); const r = f.apply (target, m.v); if (typeof m.rid === 'string' || m.rid instanceof String) { diff --git a/Ooui/Element.cs b/Ooui/Element.cs index e3193e1..ea203b1 100644 --- a/Ooui/Element.cs +++ b/Ooui/Element.cs @@ -230,20 +230,6 @@ namespace Ooui SendSet ("style." + Style.GetJsName (e.PropertyName), Style[e.PropertyName]); } - protected override bool SaveStateMessageIfNeeded (Message message) - { - if (message.TargetId != Id) - return false; - - switch (message.MessageType) { - case MessageType.Call when message.Key.StartsWith ("$.", StringComparison.Ordinal): - AddStateMessage (message); - return true; - default: - return base.SaveStateMessageIfNeeded (message); - } - } - protected virtual bool HtmlNeedsFullEndElement => false; public override void WriteOuterHtml (System.Xml.XmlWriter w) diff --git a/Ooui/UI.cs b/Ooui/UI.cs index c865a3c..540f74c 100644 --- a/Ooui/UI.cs +++ b/Ooui/UI.cs @@ -40,7 +40,6 @@ namespace Ooui @Title - @@ -49,8 +48,6 @@ namespace Ooui @InitialHtml - - From d58625129b992d6150e008652a43a94dd7d882c9 Mon Sep 17 00:00:00 2001 From: "Frank A. Krueger" Date: Fri, 2 Feb 2018 20:51:05 -0800 Subject: [PATCH 4/5] Write custom switch control --- Ooui.Forms/Renderers/SwitchRenderer.cs | 51 +++++++++++++++++++++++++- Ooui/Client.js | 2 +- Ooui/Color.cs | 40 ++++++++++++++++++-- Samples/SwitchErrorSample.cs | 2 +- 4 files changed, 87 insertions(+), 8 deletions(-) diff --git a/Ooui.Forms/Renderers/SwitchRenderer.cs b/Ooui.Forms/Renderers/SwitchRenderer.cs index 8dd639b..771e737 100644 --- a/Ooui.Forms/Renderers/SwitchRenderer.cs +++ b/Ooui.Forms/Renderers/SwitchRenderer.cs @@ -3,7 +3,7 @@ using Xamarin.Forms; namespace Ooui.Forms.Renderers { - public class SwitchRenderer : ViewRenderer + public class SwitchRenderer : ViewRenderer { public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint) { @@ -26,7 +26,7 @@ namespace Ooui.Forms.Renderers if (e.NewElement != null) { if (Control == null) { - var input = new Input (InputType.Checkbox); + var input = new SwitchElement (); SetNativeControl (input); Control.Change += OnControlValueChanged; } @@ -47,5 +47,52 @@ namespace Ooui.Forms.Renderers { Control.IsChecked = Element.IsToggled; } + + public class SwitchElement : Div + { + public event EventHandler Change; + bool isChecked = false; + readonly Div knob = new Div (); + public bool IsChecked { + get => isChecked; + set { + isChecked = value; + UpdateUI (); + } + } + public SwitchElement () + { + AppendChild (knob); + knob.Style.Position = "absolute"; + knob.Style.BorderRadius = "10px"; + knob.Style.Cursor = "pointer"; + knob.Style.Top = "2px"; + knob.Style.Width = "18px"; + knob.Style.Height = "34px"; + + Style.BorderRadius = "10px"; + Style.Cursor = "pointer"; + Style.BorderStyle = "solid"; + Style.BorderWidth = "2px"; + Click += (s, e) => { + IsChecked = !IsChecked; + Change?.Invoke (this, EventArgs.Empty); + }; + UpdateUI (); + } + + void UpdateUI () + { + Style.BackgroundColor = isChecked ? "#337ab7" : "#888"; + Style.BorderColor = Style.BackgroundColor; + knob.Style.BackgroundColor = isChecked ? "#FFF" : "#EEE"; + if (isChecked) { + knob.Style.Left = "34px"; + } + else { + knob.Style.Left = "2px"; + } + } + } } } diff --git a/Ooui/Client.js b/Ooui/Client.js index d30cb0c..e50d972 100644 --- a/Ooui/Client.js +++ b/Ooui/Client.js @@ -1,6 +1,6 @@ // Ooui v1.0.0 -var debug = true; +var debug = false; const nodes = {}; const hasText = {}; diff --git a/Ooui/Color.cs b/Ooui/Color.cs index 476e1cd..0c072d6 100644 --- a/Ooui/Color.cs +++ b/Ooui/Color.cs @@ -65,15 +65,47 @@ namespace Ooui if (styleValue == "inherit") return Colors.Clear; - //if (styleValue[0] == '#' && styleValue.Length == 4) { - //} + if (styleValue[0] == '#' && styleValue.Length == 4) { + var r = ReadHexNibble (styleValue[1]); + var g = ReadHexNibble (styleValue[2]); + var b = ReadHexNibble (styleValue[3]); + return new Color (r, g, b, 255); + } - //if (styleValue[0] == '#' && styleValue.Length == 7) { - //} + if (styleValue[0] == '#' && styleValue.Length == 7) { + var r = ReadHexByte (styleValue[1], styleValue[2]); + var g = ReadHexByte (styleValue[3], styleValue[4]); + var b = ReadHexByte (styleValue[5], styleValue[6]); + return new Color (r, g, b, 255); + } throw new ArgumentException ($"Cannot parse color string `{styleValue}`", nameof (styleValue)); } + static byte ReadHexByte (char c0, char c1) + { + var n0 = ReadHex (c0); + var n1 = ReadHex (c1); + return (byte)((n0 << 4) | n1); + } + + static byte ReadHexNibble (char c) + { + var n = ReadHex (c); + return (byte)((n << 4) | n); + } + + static byte ReadHex (char c) + { + if ('0' <= c && c <= '9') + return (byte)(c - '0'); + if ('a' <= c && c <= 'z') + return (byte)((c - 'a') + 10); + if ('A' <= c && c <= 'Z') + return (byte)((c - 'A') + 10); + return 0; + } + public override string ToString () { if (A == 255) diff --git a/Samples/SwitchErrorSample.cs b/Samples/SwitchErrorSample.cs index 184f90d..98ba63c 100644 --- a/Samples/SwitchErrorSample.cs +++ b/Samples/SwitchErrorSample.cs @@ -22,7 +22,7 @@ namespace Samples }; sw.Toggled += (sender, args) => { - label.Text = $"Switch state is :{((Switch)sender).IsToggled}"; + label.Text = $"Switch state is: {((Switch)sender).IsToggled}"; }; layout.Children.Add(label); layout.Children.Add(sw); From 589e848f441e9a2e9febb9a56f073ed3c5019794 Mon Sep 17 00:00:00 2001 From: "Frank A. Krueger" Date: Fri, 2 Feb 2018 21:03:42 -0800 Subject: [PATCH 5/5] Trigger Change event when changing the checked state --- Ooui/EventTarget.cs | 18 ++++++++++++++++++ Ooui/Input.cs | 6 +++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Ooui/EventTarget.cs b/Ooui/EventTarget.cs index 4e8c273..a37b69a 100644 --- a/Ooui/EventTarget.cs +++ b/Ooui/EventTarget.cs @@ -182,6 +182,24 @@ namespace Ooui return true; } + protected virtual bool TriggerEvent (string name) + { + List handlers = null; + lock (eventListeners) { + List hs; + if (eventListeners.TryGetValue (name, out hs)) { + handlers = new List (hs); + } + } + if (handlers != null) { + var args = new TargetEventArgs (); + foreach (var h in handlers) { + h.Invoke (this, args); + } + } + return true; + } + protected virtual bool TriggerEventFromMessage (Message message) { if (message.TargetId != Id) diff --git a/Ooui/Input.cs b/Ooui/Input.cs index d114c2d..966bf08 100644 --- a/Ooui/Input.cs +++ b/Ooui/Input.cs @@ -40,7 +40,11 @@ namespace Ooui public bool IsChecked { get => GetBooleanAttribute ("checked"); - set => SetBooleanAttributeProperty ("checked", value); + set { + if (SetBooleanAttributeProperty ("checked", value)) { + TriggerEvent ("change"); + } + } } public double Minimum {