Merge pull request #76 from praeclarum/issue48

Re-implement Switch without jQuery
This commit is contained in:
Frank A. Krueger 2018-02-02 21:08:39 -08:00 committed by GitHub
commit 313ff9e3da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 148 additions and 39 deletions

View File

@ -3,7 +3,7 @@ using Xamarin.Forms;
namespace Ooui.Forms.Renderers
{
public class SwitchRenderer : ViewRenderer<Switch, Input>
public class SwitchRenderer : ViewRenderer<Switch, SwitchRenderer.SwitchElement>
{
public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint)
{
@ -26,10 +26,8 @@ namespace Ooui.Forms.Renderers
if (e.NewElement != null) {
if (Control == null) {
var input = new Input (InputType.Checkbox);
input.SetAttribute ("data-toggle", "toggle");
var input = new SwitchElement ();
SetNativeControl (input);
input.Call ("$.bootstrapToggle");
Control.Change += OnControlValueChanged;
}
@ -49,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";
}
}
}
}
}

View File

@ -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);
}
});
// 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) {

View File

@ -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)

View File

@ -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);
@ -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)

View File

@ -182,6 +182,24 @@ namespace Ooui
return true;
}
protected virtual bool TriggerEvent (string name)
{
List<TargetEventHandler> handlers = null;
lock (eventListeners) {
List<TargetEventHandler> hs;
if (eventListeners.TryGetValue (name, out hs)) {
handlers = new List<TargetEventHandler> (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)

View File

@ -42,7 +42,7 @@ namespace Ooui
get => GetBooleanAttribute ("checked");
set {
if (SetBooleanAttributeProperty ("checked", value)) {
TriggerEventFromMessage (Message.Event (Id, "change", IsChecked));
TriggerEvent ("change");
}
}
}

View File

@ -40,7 +40,6 @@ namespace Ooui
<title>@Title</title>
<meta name=""viewport"" content=""width=device-width, initial-scale=1"" />
<link rel=""stylesheet"" href=""https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"" />
<link rel=""stylesheet"" href=""https://gitcdn.github.io/bootstrap-toggle/2.2.2/css/bootstrap-toggle.min.css"" />
<style>@Styles</style>
</head>
<body>
@ -49,8 +48,6 @@ namespace Ooui
@InitialHtml
</div>
<script type=""text/javascript"" src=""https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js""></script>
<script type=""text/javascript"" src=""https://gitcdn.github.io/bootstrap-toggle/2.2.2/js/bootstrap-toggle.min.js""></script>
<script src=""/ooui.js""></script>
<script>ooui(""@WebSocketPath"");</script>
</body>

View File

@ -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);
}
}
}