Merge pull request #76 from praeclarum/issue48
Re-implement Switch without jQuery
This commit is contained in:
commit
313ff9e3da
|
@ -3,7 +3,7 @@ using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Ooui.Forms.Renderers
|
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)
|
public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint)
|
||||||
{
|
{
|
||||||
|
@ -26,10 +26,8 @@ namespace Ooui.Forms.Renderers
|
||||||
|
|
||||||
if (e.NewElement != null) {
|
if (e.NewElement != null) {
|
||||||
if (Control == null) {
|
if (Control == null) {
|
||||||
var input = new Input (InputType.Checkbox);
|
var input = new SwitchElement ();
|
||||||
input.SetAttribute ("data-toggle", "toggle");
|
|
||||||
SetNativeControl (input);
|
SetNativeControl (input);
|
||||||
input.Call ("$.bootstrapToggle");
|
|
||||||
Control.Change += OnControlValueChanged;
|
Control.Change += OnControlValueChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,5 +47,52 @@ namespace Ooui.Forms.Renderers
|
||||||
{
|
{
|
||||||
Control.IsChecked = Element.IsToggled;
|
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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,19 +84,11 @@ function ooui (rootElementPath) {
|
||||||
const messages = JSON.parse (event.data);
|
const messages = JSON.parse (event.data);
|
||||||
if (debug) console.log("Messages", messages);
|
if (debug) console.log("Messages", messages);
|
||||||
if (Array.isArray (messages)) {
|
if (Array.isArray (messages)) {
|
||||||
const jqs = []
|
|
||||||
messages.forEach (function (m) {
|
messages.forEach (function (m) {
|
||||||
// console.log('Raw value from server', m.v);
|
// console.log('Raw value from server', m.v);
|
||||||
m.v = fixupValue (m.v);
|
m.v = fixupValue (m.v);
|
||||||
if (m.k.startsWith ("$.")) {
|
processMessage (m);
|
||||||
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);
|
console.error ("Unknown node id", m);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const isJQuery = m.k.startsWith ("$.");
|
const target = node;
|
||||||
const target = isJQuery ? $(node) : node;
|
|
||||||
if (m.k === "insertBefore" && m.v[0].nodeType == Node.TEXT_NODE && m.v[1] == null && hasText[id]) {
|
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
|
// Text is already set so it clear it first
|
||||||
if (target.firstChild)
|
if (target.firstChild)
|
||||||
target.removeChild (target.firstChild);
|
target.removeChild (target.firstChild);
|
||||||
delete hasText[id];
|
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);
|
if (debug) console.log ("Call", node, f, m.v);
|
||||||
const r = f.apply (target, m.v);
|
const r = f.apply (target, m.v);
|
||||||
if (typeof m.rid === 'string' || m.rid instanceof String) {
|
if (typeof m.rid === 'string' || m.rid instanceof String) {
|
||||||
|
|
|
@ -65,15 +65,47 @@ namespace Ooui
|
||||||
if (styleValue == "inherit")
|
if (styleValue == "inherit")
|
||||||
return Colors.Clear;
|
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));
|
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 ()
|
public override string ToString ()
|
||||||
{
|
{
|
||||||
if (A == 255)
|
if (A == 255)
|
||||||
|
|
|
@ -116,7 +116,7 @@ namespace Ooui
|
||||||
protected bool SetBooleanAttributeProperty (string attributeName, bool newValue, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
|
protected bool SetBooleanAttributeProperty (string attributeName, bool newValue, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
|
||||||
{
|
{
|
||||||
var old = GetAttribute (attributeName) != null;
|
var old = GetAttribute (attributeName) != null;
|
||||||
if (old != newValue)
|
if (old == newValue)
|
||||||
return false;
|
return false;
|
||||||
if (newValue)
|
if (newValue)
|
||||||
SetAttribute (attributeName, string.Empty);
|
SetAttribute (attributeName, string.Empty);
|
||||||
|
@ -230,20 +230,6 @@ namespace Ooui
|
||||||
SendSet ("style." + Style.GetJsName (e.PropertyName), Style[e.PropertyName]);
|
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;
|
protected virtual bool HtmlNeedsFullEndElement => false;
|
||||||
|
|
||||||
public override void WriteOuterHtml (System.Xml.XmlWriter w)
|
public override void WriteOuterHtml (System.Xml.XmlWriter w)
|
||||||
|
|
|
@ -182,6 +182,24 @@ namespace Ooui
|
||||||
return true;
|
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)
|
protected virtual bool TriggerEventFromMessage (Message message)
|
||||||
{
|
{
|
||||||
if (message.TargetId != Id)
|
if (message.TargetId != Id)
|
||||||
|
|
|
@ -42,7 +42,7 @@ namespace Ooui
|
||||||
get => GetBooleanAttribute ("checked");
|
get => GetBooleanAttribute ("checked");
|
||||||
set {
|
set {
|
||||||
if (SetBooleanAttributeProperty ("checked", value)) {
|
if (SetBooleanAttributeProperty ("checked", value)) {
|
||||||
TriggerEventFromMessage (Message.Event (Id, "change", IsChecked));
|
TriggerEvent ("change");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,6 @@ namespace Ooui
|
||||||
<title>@Title</title>
|
<title>@Title</title>
|
||||||
<meta name=""viewport"" content=""width=device-width, initial-scale=1"" />
|
<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://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>
|
<style>@Styles</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -49,8 +48,6 @@ namespace Ooui
|
||||||
@InitialHtml
|
@InitialHtml
|
||||||
</div>
|
</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 src=""/ooui.js""></script>
|
||||||
<script>ooui(""@WebSocketPath"");</script>
|
<script>ooui(""@WebSocketPath"");</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue