Merge remote-tracking branch 'refs/remotes/praeclarum/master'
This commit is contained in:
commit
03d545f3e3
|
@ -14,13 +14,16 @@ var ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
var r = "static readonly double[] CharacterProportions = {\n ";
|
var r = "static readonly double[] CharacterProportions = {\n ";
|
||||||
var head = "";
|
var head = "";
|
||||||
let size = 16;
|
let size = 24;
|
||||||
|
|
||||||
ctx.font = "bold " + size + "px \"Helvetica Neue\"";
|
ctx.font = "bold " + size + "px \"Helvetica Neue\"";
|
||||||
var mmm = ctx.measureText("MM");
|
var mmm = ctx.measureText("MM");
|
||||||
let sp = 0;
|
let sp = 0;
|
||||||
let np = 0;
|
let np = 0;
|
||||||
let mw = 0;
|
let mw = 0;
|
||||||
|
let tx = 0;
|
||||||
|
let ty = size;
|
||||||
|
let widths = {};
|
||||||
for (let i = 0; i < 128; i++) {
|
for (let i = 0; i < 128; i++) {
|
||||||
if (i > 0 && i % 8 == 0) {
|
if (i > 0 && i % 8 == 0) {
|
||||||
head = ",\n ";
|
head = ",\n ";
|
||||||
|
@ -32,6 +35,13 @@ for (let i = 0; i < 128; i++) {
|
||||||
let s = "M" + c + "M";
|
let s = "M" + c + "M";
|
||||||
let m = ctx.measureText(s);
|
let m = ctx.measureText(s);
|
||||||
let w = m.width - mmm.width;
|
let w = m.width - mmm.width;
|
||||||
|
if (tx + w > 320) {
|
||||||
|
tx = 0;
|
||||||
|
ty += size;
|
||||||
|
}
|
||||||
|
ctx.fillText(c, tx, ty);
|
||||||
|
ctx.strokeRect(tx, ty - size, w, size);
|
||||||
|
tx += w;
|
||||||
let p = w / size;
|
let p = w / size;
|
||||||
if (p > 1e-4) {
|
if (p > 1e-4) {
|
||||||
sp += p;
|
sp += p;
|
||||||
|
@ -41,9 +51,19 @@ for (let i = 0; i < 128; i++) {
|
||||||
mw = w;
|
mw = w;
|
||||||
}
|
}
|
||||||
r += head + p;
|
r += head + p;
|
||||||
|
widths[c] = w;
|
||||||
console.log (c + " = " + w);
|
console.log (c + " = " + w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let test = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
|
||||||
|
let testLen = 0;
|
||||||
|
for (const i in test) {
|
||||||
|
const w = widths[test[i]];
|
||||||
|
testLen += w;
|
||||||
|
}
|
||||||
|
console.log("TEST COMP LEN = " + testLen);
|
||||||
|
console.log("TEST REAL LEN = " + ctx.measureText(test).width);
|
||||||
|
|
||||||
let ap = sp / np;
|
let ap = sp / np;
|
||||||
let padding = (mmm.width - mw*2)/size;
|
let padding = (mmm.width - mw*2)/size;
|
||||||
r += "\n};\nconst double AverageCharProportion = " + ap + ";";
|
r += "\n};\nconst double AverageCharProportion = " + ap + ";";
|
||||||
|
|
|
@ -1,33 +1,56 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Ooui.AspNetCore
|
namespace Ooui.AspNetCore
|
||||||
{
|
{
|
||||||
public class ElementResult : ActionResult
|
public class ElementResult : ActionResult
|
||||||
{
|
{
|
||||||
readonly Element element;
|
readonly Element element;
|
||||||
readonly string title;
|
readonly string title;
|
||||||
|
|
||||||
public ElementResult (Element element, string title = "")
|
public ElementResult (Element element, string title = "")
|
||||||
{
|
{
|
||||||
this.element = element;
|
this.element = element;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task ExecuteResultAsync (ActionContext context)
|
public override async Task ExecuteResultAsync (ActionContext context)
|
||||||
{
|
{
|
||||||
var response = context.HttpContext.Response;
|
var response = context.HttpContext.Response;
|
||||||
response.StatusCode = 200;
|
response.StatusCode = 200;
|
||||||
response.ContentType = "text/html; charset=utf-8";
|
response.ContentType = "text/html; charset=utf-8";
|
||||||
|
|
||||||
|
if (element.WantsFullScreen) {
|
||||||
|
element.Style.Width = GetCookieDouble (context.HttpContext.Request.Cookies, "oouiWindowWidth", 32, 640, 10000);
|
||||||
|
element.Style.Height = GetCookieDouble (context.HttpContext.Request.Cookies, "oouiWindowHeight", 24, 480, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
var sessionId = WebSocketHandler.BeginSession (context.HttpContext, element);
|
var sessionId = WebSocketHandler.BeginSession (context.HttpContext, element);
|
||||||
var html = UI.RenderTemplate (WebSocketHandler.WebSocketPath + "?id=" + sessionId, title: title);
|
var initialHtml = element.OuterHtml;
|
||||||
|
var html = UI.RenderTemplate (WebSocketHandler.WebSocketPath + "?id=" + sessionId, title: title, initialHtml: initialHtml);
|
||||||
var htmlBytes = Encoding.UTF8.GetBytes (html);
|
var htmlBytes = Encoding.UTF8.GetBytes (html);
|
||||||
response.ContentLength = htmlBytes.Length;
|
response.ContentLength = htmlBytes.Length;
|
||||||
using (var s = response.Body) {
|
using (var s = response.Body) {
|
||||||
await s.WriteAsync (htmlBytes, 0, htmlBytes.Length).ConfigureAwait (false);
|
await s.WriteAsync (htmlBytes, 0, htmlBytes.Length).ConfigureAwait (false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static double GetCookieDouble (IRequestCookieCollection cookies, string key, double min, double def, double max)
|
||||||
|
{
|
||||||
|
if (cookies.TryGetValue (key, out var s)) {
|
||||||
|
if (double.TryParse (s, out var d)) {
|
||||||
|
if (d < min) return min;
|
||||||
|
if (d > max) return max;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
|
|
||||||
|
namespace Ooui.AspNetCore.TagHelpers
|
||||||
|
{
|
||||||
|
public class OouiTagHelper : TagHelper
|
||||||
|
{
|
||||||
|
public Ooui.Element Element { get; set; }
|
||||||
|
|
||||||
|
public override void Process (TagHelperContext context, TagHelperOutput output)
|
||||||
|
{
|
||||||
|
output.TagName = "div";
|
||||||
|
output.TagMode = TagMode.StartTagAndEndTag;
|
||||||
|
output.Content.SetHtmlContent (Element.OuterHtml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,19 +13,19 @@ namespace Ooui.AspNetCore
|
||||||
|
|
||||||
public static TimeSpan SessionTimeout { get; set; } = TimeSpan.FromMinutes (5);
|
public static TimeSpan SessionTimeout { get; set; } = TimeSpan.FromMinutes (5);
|
||||||
|
|
||||||
static readonly ConcurrentDictionary<string, PendingSession> pendingSessions =
|
static readonly ConcurrentDictionary<string, ActiveSession> activeSessions =
|
||||||
new ConcurrentDictionary<string, PendingSession> ();
|
new ConcurrentDictionary<string, ActiveSession> ();
|
||||||
|
|
||||||
public static string BeginSession (HttpContext context, Element element)
|
public static string BeginSession (HttpContext context, Element element)
|
||||||
{
|
{
|
||||||
var id = Guid.NewGuid ().ToString ("N");
|
var id = Guid.NewGuid ().ToString ("N");
|
||||||
|
|
||||||
var s = new PendingSession {
|
var s = new ActiveSession {
|
||||||
Element = element,
|
Element = element,
|
||||||
RequestTimeUtc = DateTime.UtcNow,
|
LastConnectTimeUtc = DateTime.UtcNow,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!pendingSessions.TryAdd (id, s)) {
|
if (!activeSessions.TryAdd (id, s)) {
|
||||||
throw new Exception ("Failed to schedule pending session");
|
throw new Exception ("Failed to schedule pending session");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,20 +60,21 @@ namespace Ooui.AspNetCore
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Find the pending session
|
// Clear old sessions
|
||||||
//
|
//
|
||||||
if (!pendingSessions.TryRemove (id, out var pendingSession)) {
|
var toClear = activeSessions.Where (x => (DateTime.UtcNow - x.Value.LastConnectTimeUtc) > SessionTimeout).ToList ();
|
||||||
BadRequest ("Unknown `id`");
|
foreach (var c in toClear) {
|
||||||
return;
|
activeSessions.TryRemove (c.Key, out var _);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Reject the session if it's old
|
// Find the pending session
|
||||||
//
|
//
|
||||||
if ((DateTime.UtcNow - pendingSession.RequestTimeUtc) > SessionTimeout) {
|
if (!activeSessions.TryGetValue (id, out var activeSession)) {
|
||||||
BadRequest ("Old `id`");
|
BadRequest ("Unknown `id`");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
activeSession.LastConnectTimeUtc = DateTime.UtcNow;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Set the element's dimensions
|
// Set the element's dimensions
|
||||||
|
@ -97,14 +98,14 @@ namespace Ooui.AspNetCore
|
||||||
//
|
//
|
||||||
var token = CancellationToken.None;
|
var token = CancellationToken.None;
|
||||||
var webSocket = await context.WebSockets.AcceptWebSocketAsync ("ooui");
|
var webSocket = await context.WebSockets.AcceptWebSocketAsync ("ooui");
|
||||||
var session = new Ooui.UI.Session (webSocket, pendingSession.Element, w, h, token);
|
var session = new Ooui.UI.Session (webSocket, activeSession.Element, w, h, token);
|
||||||
await session.RunAsync ().ConfigureAwait (false);
|
await session.RunAsync ().ConfigureAwait (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
class PendingSession
|
class ActiveSession
|
||||||
{
|
{
|
||||||
public Element Element;
|
public Element Element;
|
||||||
public DateTime RequestTimeUtc;
|
public DateTime LastConnectTimeUtc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,21 @@ namespace Ooui.Forms
|
||||||
public class LinkLabel : Xamarin.Forms.Label
|
public class LinkLabel : Xamarin.Forms.Label
|
||||||
{
|
{
|
||||||
public static readonly BindableProperty HRefProperty = BindableProperty.Create ("HRef", typeof (string),
|
public static readonly BindableProperty HRefProperty = BindableProperty.Create ("HRef", typeof (string),
|
||||||
typeof (LinkView), string.Empty, BindingMode.OneWay, null, null, null, null);
|
typeof (LinkLabel), string.Empty, BindingMode.OneWay, null, null, null, null);
|
||||||
|
|
||||||
public string HRef {
|
public string HRef {
|
||||||
get { return (string)base.GetValue (HRefProperty); }
|
get { return (string)base.GetValue (HRefProperty); }
|
||||||
set { base.SetValue (HRefProperty, value); }
|
set { base.SetValue (HRefProperty, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static readonly BindableProperty TargetProperty = BindableProperty.Create ("Target", typeof (string),
|
||||||
|
typeof (LinkLabel), string.Empty, BindingMode.OneWay, null, null, null, null);
|
||||||
|
|
||||||
|
public string Target {
|
||||||
|
get { return (string)base.GetValue (TargetProperty); }
|
||||||
|
set { base.SetValue (TargetProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
public LinkLabel ()
|
public LinkLabel ()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,14 @@ namespace Ooui.Forms
|
||||||
set { base.SetValue (HRefProperty, value); }
|
set { base.SetValue (HRefProperty, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static readonly BindableProperty TargetProperty = BindableProperty.Create ("Target", typeof (string),
|
||||||
|
typeof (LinkView), string.Empty, BindingMode.OneWay, null, null, null, null);
|
||||||
|
|
||||||
|
public string Target {
|
||||||
|
get { return (string)base.GetValue (TargetProperty); }
|
||||||
|
set { base.SetValue (TargetProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
public LinkView ()
|
public LinkView ()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace Ooui.Forms.Extensions
|
||||||
var measured = false;
|
var measured = false;
|
||||||
|
|
||||||
if (self.Style.Width.Equals ("inherit")) {
|
if (self.Style.Width.Equals ("inherit")) {
|
||||||
s = self.Text.MeasureSize (self.Style);
|
s = self.Text.MeasureSize (self.Style, widthConstraint, heightConstraint);
|
||||||
measured = true;
|
measured = true;
|
||||||
rw = double.IsPositiveInfinity (s.Width) ? double.PositiveInfinity : Math.Ceiling (s.Width);
|
rw = double.IsPositiveInfinity (s.Width) ? double.PositiveInfinity : Math.Ceiling (s.Width);
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ namespace Ooui.Forms.Extensions
|
||||||
|
|
||||||
if (self.Style.Height.Equals ("inherit")) {
|
if (self.Style.Height.Equals ("inherit")) {
|
||||||
if (!measured) {
|
if (!measured) {
|
||||||
s = self.Text.MeasureSize (self.Style);
|
s = self.Text.MeasureSize (self.Style, widthConstraint, heightConstraint);
|
||||||
measured = true;
|
measured = true;
|
||||||
}
|
}
|
||||||
rh = double.IsPositiveInfinity (s.Height) ? double.PositiveInfinity : Math.Ceiling (s.Height * 1.4);
|
rh = double.IsPositiveInfinity (s.Height) ? double.PositiveInfinity : Math.Ceiling (s.Height * 1.4);
|
||||||
|
|
|
@ -38,36 +38,65 @@ namespace Ooui.Forms.Extensions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Size MeasureSize (this string text, string fontFamily, double fontSize, FontAttributes fontAttrs)
|
public static Size MeasureSize (this string text, string fontFamily, double fontSize, FontAttributes fontAttrs, double widthConstraint, double heightConstraint)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty (text))
|
if (string.IsNullOrEmpty (text))
|
||||||
return Size.Zero;
|
return Size.Zero;
|
||||||
|
|
||||||
var fontHeight = fontSize;
|
var fontHeight = fontSize;
|
||||||
|
var lineHeight = fontHeight * 1.4;
|
||||||
|
|
||||||
var isBold = fontAttrs.HasFlag (FontAttributes.Bold);
|
var isBold = fontAttrs.HasFlag (FontAttributes.Bold);
|
||||||
|
|
||||||
var props = isBold ? BoldCharacterProportions : CharacterProportions;
|
var props = isBold ? BoldCharacterProportions : CharacterProportions;
|
||||||
var avgp = isBold ? BoldAverageCharProportion : AverageCharProportion;
|
var avgp = isBold ? BoldAverageCharProportion : AverageCharProportion;
|
||||||
|
|
||||||
var pwidth = 1.0e-6; // Tiny little padding to account for sampling errors
|
var px = 0.0;
|
||||||
for (var i = 0; i < text.Length; i++) {
|
var lines = 1;
|
||||||
var c = (int)text[i];
|
var maxPWidth = 0.0;
|
||||||
if (c < 128) {
|
var pwidthConstraint = double.IsPositiveInfinity (widthConstraint) ? double.PositiveInfinity : widthConstraint / fontSize;
|
||||||
pwidth += props[c];
|
var lastSpaceWidth = -1.0;
|
||||||
}
|
|
||||||
else {
|
|
||||||
pwidth += avgp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var width = fontSize * pwidth;
|
|
||||||
|
|
||||||
return new Size (width, fontHeight);
|
// Tiny little padding to account for sampling errors
|
||||||
|
var pwidthHack = 1.0e-6;
|
||||||
|
var plineHack = 0.333;
|
||||||
|
|
||||||
|
var n = text != null ? text.Length : 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
var c = (int)text[i];
|
||||||
|
var pw = (c < 128) ? props[c] : avgp;
|
||||||
|
// Should we wrap?
|
||||||
|
if (px + pw + plineHack > pwidthConstraint) {
|
||||||
|
lines++;
|
||||||
|
if (lastSpaceWidth > 0) {
|
||||||
|
maxPWidth = Math.Max (maxPWidth, lastSpaceWidth + pwidthHack);
|
||||||
|
px = pw - lastSpaceWidth;
|
||||||
|
lastSpaceWidth = -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
maxPWidth = Math.Max (maxPWidth, px + pwidthHack);
|
||||||
|
px = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c == ' ') {
|
||||||
|
lastSpaceWidth = pw;
|
||||||
|
}
|
||||||
|
px += pw;
|
||||||
|
}
|
||||||
|
maxPWidth = Math.Max (maxPWidth, px + pwidthHack);
|
||||||
|
var width = fontSize * maxPWidth;
|
||||||
|
var height = lines * lineHeight;
|
||||||
|
|
||||||
|
// Console.WriteLine ($"MEASURE TEXT SIZE {widthConstraint}x{heightConstraint} \"{text}\" == {width}x{height}");
|
||||||
|
|
||||||
|
return new Size (width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Size MeasureSize (this string text, Style style)
|
public static Size MeasureSize (this string text, Style style, double widthConstraint, double heightConstraint)
|
||||||
{
|
{
|
||||||
return MeasureSize (text, "", 14, FontAttributes.None);
|
// System.Console.WriteLine("!!! MEASURE STYLED TEXT SIZE: " + style);
|
||||||
|
return MeasureSize (text, "", 14, FontAttributes.None, widthConstraint, heightConstraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ToOouiTextAlign (this TextAlignment align)
|
public static string ToOouiTextAlign (this TextAlignment align)
|
||||||
|
|
|
@ -139,6 +139,10 @@ namespace Xamarin.Forms
|
||||||
}), null, (int)interval.TotalMilliseconds, (int)interval.TotalMilliseconds);
|
}), null, (int)interval.TotalMilliseconds, (int)interval.TotalMilliseconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void QuitApplication()
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ViewInitializedEventArgs
|
public class ViewInitializedEventArgs
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Xamarin.Forms" Version="2.4.0.38779" />
|
<PackageReference Include="Xamarin.Forms" Version="2.5.0.122203" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Extensions\" />
|
<Folder Include="Extensions\" />
|
||||||
|
|
|
@ -14,8 +14,8 @@ namespace Ooui.Forms.Renderers
|
||||||
|
|
||||||
public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint)
|
public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint)
|
||||||
{
|
{
|
||||||
var size = Element.Text.MeasureSize (Element.FontFamily, Element.FontSize, Element.FontAttributes);
|
var size = Element.Text.MeasureSize (Element.FontFamily, Element.FontSize, Element.FontAttributes, widthConstraint, heightConstraint);
|
||||||
size = new Size (size.Width, size.Height * 1.428 + 14);
|
size = new Size (size.Width + 2 * Element.FontSize, size.Height + Element.FontSize);
|
||||||
return new SizeRequest (size, size);
|
return new SizeRequest (size, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@ namespace Ooui.Forms.Renderers
|
||||||
|
|
||||||
public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint)
|
public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint)
|
||||||
{
|
{
|
||||||
var size = "00/00/0000".MeasureSize ("", 16.0, FontAttributes.None);
|
var size = "00/00/0000".MeasureSize ("", 16.0, FontAttributes.None, widthConstraint, heightConstraint);
|
||||||
size = new Size (size.Width, size.Height * 1.428 + 14);
|
size = new Size (size.Width, size.Height);
|
||||||
return new SizeRequest (size, size);
|
return new SizeRequest (size, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,14 +21,12 @@ namespace Ooui.Forms.Renderers
|
||||||
if (text == null || text.Length == 0) {
|
if (text == null || text.Length == 0) {
|
||||||
text = Element.Placeholder;
|
text = Element.Placeholder;
|
||||||
}
|
}
|
||||||
Size size;
|
|
||||||
if (text == null || text.Length == 0) {
|
if (text == null || text.Length == 0) {
|
||||||
size = new Size (Element.FontSize * 0.25, Element.FontSize);
|
text = " ";
|
||||||
}
|
}
|
||||||
else {
|
var size = text.MeasureSize (Element.FontFamily, Element.FontSize, Element.FontAttributes, widthConstraint, heightConstraint);
|
||||||
size = text.MeasureSize (Element.FontFamily, Element.FontSize, Element.FontAttributes);
|
var vpadding = Element.FontSize;
|
||||||
}
|
size = new Size (size.Width, size.Height + vpadding);
|
||||||
size = new Size (size.Width, size.Height * 1.428 + 14);
|
|
||||||
return new SizeRequest (size, size);
|
return new SizeRequest (size, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +147,7 @@ namespace Ooui.Forms.Renderers
|
||||||
{
|
{
|
||||||
if (initialSize == Size.Zero) {
|
if (initialSize == Size.Zero) {
|
||||||
var testString = "Tj";
|
var testString = "Tj";
|
||||||
initialSize = testString.MeasureSize (Control.Style);
|
initialSize = testString.MeasureSize (Control.Style, double.PositiveInfinity, double.PositiveInfinity);
|
||||||
}
|
}
|
||||||
|
|
||||||
Element.SetStyleFont (Element.FontFamily, Element.FontSize, Element.FontAttributes, Control.Style);
|
Element.SetStyleFont (Element.FontFamily, Element.FontSize, Element.FontAttributes, Control.Style);
|
||||||
|
|
|
@ -16,11 +16,12 @@ namespace Ooui.Forms.Renderers
|
||||||
|
|
||||||
public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint)
|
public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint)
|
||||||
{
|
{
|
||||||
|
// System.Console.WriteLine($"Label.GetDesiredSize ({widthConstraint}, {heightConstraint})");
|
||||||
if (!_perfectSizeValid) {
|
if (!_perfectSizeValid) {
|
||||||
var size = Element.Text.MeasureSize (Element.FontFamily, Element.FontSize, Element.FontAttributes);
|
var size = Element.Text.MeasureSize (Element.FontFamily, Element.FontSize, Element.FontAttributes, double.PositiveInfinity, double.PositiveInfinity);
|
||||||
size.Width = Math.Ceiling (size.Width);
|
size.Width = Math.Ceiling (size.Width);
|
||||||
size.Height = Math.Ceiling (size.Height * 1.4);
|
size.Height = Math.Ceiling (size.Height);
|
||||||
_perfectSize = new SizeRequest (size, size);
|
_perfectSize = new SizeRequest (size, new Size (Element.FontSize, Element.FontSize));
|
||||||
_perfectSizeValid = true;
|
_perfectSizeValid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +31,8 @@ namespace Ooui.Forms.Renderers
|
||||||
if (widthFits && heightFits)
|
if (widthFits && heightFits)
|
||||||
return _perfectSize;
|
return _perfectSize;
|
||||||
|
|
||||||
var result = base.GetDesiredSize (widthConstraint, heightConstraint);
|
var resultRequestSize = Element.Text.MeasureSize (Element.FontFamily, Element.FontSize, Element.FontAttributes, widthConstraint, heightConstraint);
|
||||||
|
var result = new SizeRequest (resultRequestSize, resultRequestSize);
|
||||||
var tinyWidth = Math.Min (10, result.Request.Width);
|
var tinyWidth = Math.Min (10, result.Request.Width);
|
||||||
result.Minimum = new Size (tinyWidth, result.Request.Height);
|
result.Minimum = new Size (tinyWidth, result.Request.Height);
|
||||||
|
|
||||||
|
|
|
@ -14,10 +14,10 @@ namespace Ooui.Forms.Renderers
|
||||||
public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint)
|
public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint)
|
||||||
{
|
{
|
||||||
if (!_perfectSizeValid) {
|
if (!_perfectSizeValid) {
|
||||||
var size = Element.Text.MeasureSize (Element.FontFamily, Element.FontSize, Element.FontAttributes);
|
var size = Element.Text.MeasureSize (Element.FontFamily, Element.FontSize, Element.FontAttributes, double.PositiveInfinity, double.PositiveInfinity);
|
||||||
size.Width = Math.Ceiling (size.Width);
|
size.Width = Math.Ceiling (size.Width);
|
||||||
size.Height = Math.Ceiling (size.Height * 1.4);
|
size.Height = Math.Ceiling (size.Height);
|
||||||
_perfectSize = new SizeRequest (size, size);
|
_perfectSize = new SizeRequest (size, new Size (Element.FontSize, Element.FontSize));
|
||||||
_perfectSizeValid = true;
|
_perfectSizeValid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,8 @@ namespace Ooui.Forms.Renderers
|
||||||
if (widthFits && heightFits)
|
if (widthFits && heightFits)
|
||||||
return _perfectSize;
|
return _perfectSize;
|
||||||
|
|
||||||
var result = base.GetDesiredSize (widthConstraint, heightConstraint);
|
var resultRequestSize = Element.Text.MeasureSize (Element.FontFamily, Element.FontSize, Element.FontAttributes, widthConstraint, heightConstraint);
|
||||||
|
var result = new SizeRequest (resultRequestSize, resultRequestSize);
|
||||||
var tinyWidth = Math.Min (10, result.Request.Width);
|
var tinyWidth = Math.Min (10, result.Request.Width);
|
||||||
result.Minimum = new Size (tinyWidth, result.Request.Height);
|
result.Minimum = new Size (tinyWidth, result.Request.Height);
|
||||||
|
|
||||||
|
@ -57,6 +58,7 @@ namespace Ooui.Forms.Renderers
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateHRef ();
|
UpdateHRef ();
|
||||||
|
UpdateTarget ();
|
||||||
|
|
||||||
UpdateText ();
|
UpdateText ();
|
||||||
UpdateTextColor ();
|
UpdateTextColor ();
|
||||||
|
@ -78,6 +80,8 @@ namespace Ooui.Forms.Renderers
|
||||||
|
|
||||||
if (e.PropertyName == Ooui.Forms.LinkLabel.HRefProperty.PropertyName)
|
if (e.PropertyName == Ooui.Forms.LinkLabel.HRefProperty.PropertyName)
|
||||||
UpdateHRef ();
|
UpdateHRef ();
|
||||||
|
if (e.PropertyName == Ooui.Forms.LinkLabel.TargetProperty.PropertyName)
|
||||||
|
UpdateTarget ();
|
||||||
else if (e.PropertyName == Xamarin.Forms.Label.HorizontalTextAlignmentProperty.PropertyName)
|
else if (e.PropertyName == Xamarin.Forms.Label.HorizontalTextAlignmentProperty.PropertyName)
|
||||||
UpdateAlignment ();
|
UpdateAlignment ();
|
||||||
else if (e.PropertyName == Xamarin.Forms.Label.VerticalTextAlignmentProperty.PropertyName)
|
else if (e.PropertyName == Xamarin.Forms.Label.VerticalTextAlignmentProperty.PropertyName)
|
||||||
|
@ -107,6 +111,11 @@ namespace Ooui.Forms.Renderers
|
||||||
Control.HRef = Element.HRef;
|
Control.HRef = Element.HRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UpdateTarget ()
|
||||||
|
{
|
||||||
|
Control.Target = Element.Target;
|
||||||
|
}
|
||||||
|
|
||||||
void UpdateAlignment ()
|
void UpdateAlignment ()
|
||||||
{
|
{
|
||||||
this.Style.Display = "table";
|
this.Style.Display = "table";
|
||||||
|
|
|
@ -15,6 +15,7 @@ namespace Ooui.Forms.Renderers
|
||||||
base.OnElementChanged (e);
|
base.OnElementChanged (e);
|
||||||
|
|
||||||
UpdateHRef ();
|
UpdateHRef ();
|
||||||
|
UpdateTarget ();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e)
|
protected override void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e)
|
||||||
|
@ -24,13 +25,20 @@ namespace Ooui.Forms.Renderers
|
||||||
if (Control == null)
|
if (Control == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (e.PropertyName == Ooui.Forms.LinkLabel.HRefProperty.PropertyName)
|
if (e.PropertyName == Ooui.Forms.LinkView.HRefProperty.PropertyName)
|
||||||
UpdateHRef ();
|
UpdateHRef ();
|
||||||
|
if (e.PropertyName == Ooui.Forms.LinkView.TargetProperty.PropertyName)
|
||||||
|
UpdateTarget ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateHRef ()
|
void UpdateHRef ()
|
||||||
{
|
{
|
||||||
this.SetAttribute ("href", Element.HRef);
|
this.SetAttribute ("href", Element.HRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UpdateTarget ()
|
||||||
|
{
|
||||||
|
this.SetAttribute ("target", Element.Target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,9 @@ namespace Ooui.Forms.Renderers
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
size = text.MeasureSize(Element.FontFamily, Element.FontSize, Element.FontAttributes);
|
size = text.MeasureSize(Element.FontFamily, Element.FontSize, Element.FontAttributes, widthConstraint, heightConstraint);
|
||||||
}
|
}
|
||||||
size = new Size(size.Width, size.Height * 1.428 + 14);
|
size = new Size(size.Width, size.Height + Element.FontSize);
|
||||||
return new SizeRequest(size, size);
|
return new SizeRequest(size, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,9 @@ namespace Ooui.Forms.Renderers
|
||||||
|
|
||||||
public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
|
public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
|
||||||
{
|
{
|
||||||
var size = "00:00:00".MeasureSize(string.Empty, 16.0, FontAttributes.None);
|
var fontSize = 16.0;
|
||||||
size = new Size(size.Width, size.Height * 1.428 + 14);
|
var size = "00:00:00".MeasureSize(string.Empty, fontSize, FontAttributes.None, widthConstraint, heightConstraint);
|
||||||
|
size = new Size(size.Width, size.Height + fontSize);
|
||||||
return new SizeRequest(size, size);
|
return new SizeRequest(size, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@ namespace Ooui.Forms.Renderers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual bool ManageNativeControlLifetime => true;
|
protected virtual bool ManageNativeControlLifetime => true;
|
||||||
|
|
||||||
|
protected override bool HtmlNeedsFullEndElement => TagName == "div";
|
||||||
|
|
||||||
public ViewRenderer (string tagName = "div")
|
public ViewRenderer (string tagName = "div")
|
||||||
: base (tagName)
|
: base (tagName)
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace Ooui.Forms.Renderers
|
||||||
|
|
||||||
if (_iframe != null)
|
if (_iframe != null)
|
||||||
{
|
{
|
||||||
_iframe.Src = html;
|
_iframe.Source = html;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -47,7 +47,7 @@ namespace Ooui.Forms.Renderers
|
||||||
|
|
||||||
if (_iframe != null)
|
if (_iframe != null)
|
||||||
{
|
{
|
||||||
_iframe.Src = url;
|
_iframe.Source = url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
@ -62,6 +62,8 @@ namespace Ooui.Forms
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool HtmlNeedsFullEndElement => TagName == "div";
|
||||||
|
|
||||||
public VisualElementRenderer (string tagName = "div") : base (tagName)
|
public VisualElementRenderer (string tagName = "div") : base (tagName)
|
||||||
{
|
{
|
||||||
_propertyChangedHandler = OnElementPropertyChanged;
|
_propertyChangedHandler = OnElementPropertyChanged;
|
||||||
|
|
|
@ -4,15 +4,32 @@ namespace Ooui
|
||||||
{
|
{
|
||||||
public class Anchor : Element
|
public class Anchor : Element
|
||||||
{
|
{
|
||||||
string href = "";
|
|
||||||
public string HRef {
|
public string HRef {
|
||||||
get => href;
|
get => GetStringAttribute ("href", "");
|
||||||
set => SetProperty (ref href, value ?? "", "href");
|
set => SetAttributeProperty ("href", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Target {
|
||||||
|
get => GetStringAttribute ("target", "");
|
||||||
|
set => SetAttributeProperty ("target", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Anchor ()
|
public Anchor ()
|
||||||
: base ("a")
|
: base ("a")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Anchor (string href)
|
||||||
|
: this ()
|
||||||
|
{
|
||||||
|
HRef = href;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Anchor (string href, string text)
|
||||||
|
: this ()
|
||||||
|
{
|
||||||
|
HRef = href;
|
||||||
|
Text = text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@ namespace Ooui
|
||||||
{
|
{
|
||||||
ButtonType typ = ButtonType.Submit;
|
ButtonType typ = ButtonType.Submit;
|
||||||
public ButtonType Type {
|
public ButtonType Type {
|
||||||
get => typ;
|
get => GetAttribute ("type", ButtonType.Submit);
|
||||||
set => SetProperty (ref typ, value, "type");
|
set => SetAttributeProperty ("type", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Button ()
|
public Button ()
|
||||||
|
|
|
@ -7,16 +7,14 @@ namespace Ooui
|
||||||
CanvasRenderingContext2D context2d = new CanvasRenderingContext2D ();
|
CanvasRenderingContext2D context2d = new CanvasRenderingContext2D ();
|
||||||
int gotContext2d = 0;
|
int gotContext2d = 0;
|
||||||
|
|
||||||
int width = 300;
|
|
||||||
public int Width {
|
public int Width {
|
||||||
get => width;
|
get => GetAttribute ("width", 300);
|
||||||
set => SetProperty (ref width, value <= 0 ? 150 : value, "width");
|
set => SetAttributeProperty ("width", value < 0 ? 0 : value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int height = 150;
|
|
||||||
public int Height {
|
public int Height {
|
||||||
get => height;
|
get => GetAttribute ("height", 150);
|
||||||
set => SetProperty (ref height, value <= 0 ? 150 : value, "height");
|
set => SetAttributeProperty ("height", value < 0 ? 0 : value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Canvas ()
|
public Canvas ()
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
// Ooui v1.0.0
|
||||||
|
|
||||||
var debug = false;
|
var debug = false;
|
||||||
|
|
||||||
const nodes = {};
|
const nodes = {};
|
||||||
|
const hasText = {};
|
||||||
|
|
||||||
let socket = null;
|
let socket = null;
|
||||||
|
|
||||||
|
@ -35,19 +37,39 @@ function getSize () {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setCookie (name, value, days) {
|
||||||
|
var expires = "";
|
||||||
|
if (days) {
|
||||||
|
var date = new Date ();
|
||||||
|
date.setTime(date.getTime () + (days*24*60*60*1000));
|
||||||
|
expires = "; expires=" + date.toUTCString();
|
||||||
|
}
|
||||||
|
document.cookie = name + "=" + (value || "") + expires + "; path=/";
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveSize (s) {
|
||||||
|
setCookie ("oouiWindowWidth", s.width, 7);
|
||||||
|
setCookie ("oouiWindowHeight", s.height, 7);
|
||||||
|
}
|
||||||
|
|
||||||
// Main entrypoint
|
// Main entrypoint
|
||||||
function ooui (rootElementPath) {
|
function ooui (rootElementPath) {
|
||||||
var opened = false;
|
|
||||||
|
|
||||||
var initialSize = getSize ();
|
var initialSize = getSize ();
|
||||||
|
saveSize (initialSize);
|
||||||
|
|
||||||
var wsArgs = (rootElementPath.indexOf("?") >= 0 ? "&" : "?") +
|
var wsArgs = (rootElementPath.indexOf("?") >= 0 ? "&" : "?") +
|
||||||
"w=" + initialSize.width + "&h=" + initialSize.height;
|
"w=" + initialSize.width + "&h=" + initialSize.height;
|
||||||
|
|
||||||
socket = new WebSocket ("ws://" + document.location.host + rootElementPath + wsArgs, "ooui");
|
var proto = "ws";
|
||||||
|
if (location.protocol == "https:") {
|
||||||
|
proto = "wss";
|
||||||
|
}
|
||||||
|
|
||||||
|
socket = new WebSocket (proto + "://" + document.location.host + rootElementPath + wsArgs, "ooui");
|
||||||
|
|
||||||
socket.addEventListener ("open", function (event) {
|
socket.addEventListener ("open", function (event) {
|
||||||
console.log ("Web socket opened");
|
console.log ("Web socket opened");
|
||||||
opened = true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.addEventListener ("error", function (event) {
|
socket.addEventListener ("error", function (event) {
|
||||||
|
@ -56,29 +78,17 @@ function ooui (rootElementPath) {
|
||||||
|
|
||||||
socket.addEventListener ("close", function (event) {
|
socket.addEventListener ("close", function (event) {
|
||||||
console.error ("Web socket close", event);
|
console.error ("Web socket close", event);
|
||||||
if (opened) {
|
|
||||||
alert ("Connection to the server has been lost. Please try refreshing the page.");
|
|
||||||
opened = false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.addEventListener("message", function (event) {
|
socket.addEventListener("message", function (event) {
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -105,6 +115,7 @@ function ooui (rootElementPath) {
|
||||||
k: "resize",
|
k: "resize",
|
||||||
v: getSize (),
|
v: getSize (),
|
||||||
};
|
};
|
||||||
|
saveSize (em.v);
|
||||||
const ems = JSON.stringify (em);
|
const ems = JSON.stringify (em);
|
||||||
if (socket != null)
|
if (socket != null)
|
||||||
socket.send (ems);
|
socket.send (ems);
|
||||||
|
@ -124,12 +135,22 @@ function getNode (id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getOrCreateElement (id, tagName) {
|
||||||
|
var e = document.getElementById (id);
|
||||||
|
if (e) {
|
||||||
|
if (e.firstChild && e.firstChild.nodeType == Node.TEXT_NODE)
|
||||||
|
hasText[e.id] = true;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
return document.createElement (tagName);
|
||||||
|
}
|
||||||
|
|
||||||
function msgCreate (m) {
|
function msgCreate (m) {
|
||||||
const id = m.id;
|
const id = m.id;
|
||||||
const tagName = m.k;
|
const tagName = m.k;
|
||||||
const node = tagName === "#text" ?
|
const node = tagName === "#text" ?
|
||||||
document.createTextNode ("") :
|
document.createTextNode ("") :
|
||||||
document.createElement (tagName);
|
getOrCreateElement (id, tagName);
|
||||||
if (tagName !== "#text")
|
if (tagName !== "#text")
|
||||||
node.id = id;
|
node.id = id;
|
||||||
nodes[id] = node;
|
nodes[id] = node;
|
||||||
|
@ -165,6 +186,17 @@ function msgSetAttr (m) {
|
||||||
if (debug) console.log ("SetAttr", node, m.k, m.v);
|
if (debug) console.log ("SetAttr", node, m.k, m.v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function msgRemAttr (m) {
|
||||||
|
const id = m.id;
|
||||||
|
const node = getNode (id);
|
||||||
|
if (!node) {
|
||||||
|
console.error ("Unknown node id", m);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
node.removeAttribute(m.k);
|
||||||
|
if (debug) console.log ("RemAttr", node, m.k);
|
||||||
|
}
|
||||||
|
|
||||||
function msgCall (m) {
|
function msgCall (m) {
|
||||||
const id = m.id;
|
const id = m.id;
|
||||||
const node = getNode (id);
|
const node = getNode (id);
|
||||||
|
@ -172,9 +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]) {
|
||||||
const f = isJQuery ? target[m.k.slice(2)] : target[m.k];
|
// Text is already set so it clear it first
|
||||||
|
if (target.firstChild)
|
||||||
|
target.removeChild (target.firstChild);
|
||||||
|
delete hasText[id];
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
@ -228,6 +265,9 @@ function processMessage (m) {
|
||||||
case "setAttr":
|
case "setAttr":
|
||||||
msgSetAttr (m);
|
msgSetAttr (m);
|
||||||
break;
|
break;
|
||||||
|
case "remAttr":
|
||||||
|
msgRemAttr (m);
|
||||||
|
break;
|
||||||
case "call":
|
case "call":
|
||||||
msgCall (m);
|
msgCall (m);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -5,6 +5,8 @@ namespace Ooui
|
||||||
{
|
{
|
||||||
public class Div : Element
|
public class Div : Element
|
||||||
{
|
{
|
||||||
|
protected override bool HtmlNeedsFullEndElement => true;
|
||||||
|
|
||||||
public Div ()
|
public Div ()
|
||||||
: base ("div")
|
: base ("div")
|
||||||
{
|
{
|
||||||
|
|
170
Ooui/Element.cs
170
Ooui/Element.cs
|
@ -1,28 +1,29 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
|
||||||
namespace Ooui
|
namespace Ooui
|
||||||
{
|
{
|
||||||
public abstract class Element : Node
|
public abstract class Element : Node
|
||||||
{
|
{
|
||||||
string className = "";
|
readonly Dictionary<string, object> attributes = new Dictionary<string, object> ();
|
||||||
|
|
||||||
public string ClassName {
|
public string ClassName {
|
||||||
get => className;
|
get => GetStringAttribute ("class", "");
|
||||||
set => SetProperty (ref className, value, "className");
|
set => SetAttributeProperty ("class", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Style Style { get; private set; } = new Style ();
|
public Style Style { get; private set; } = new Style ();
|
||||||
|
|
||||||
string title = "";
|
|
||||||
public string Title {
|
public string Title {
|
||||||
get => title;
|
get => GetStringAttribute ("title", "");
|
||||||
set => SetProperty (ref title, value, "title");
|
set => SetAttributeProperty ("title", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hidden = false;
|
bool hidden = false;
|
||||||
public bool IsHidden {
|
public bool IsHidden {
|
||||||
get => hidden;
|
get => GetBooleanAttribute ("hidden");
|
||||||
set => SetProperty (ref hidden, value, "hidden");
|
set => SetBooleanAttributeProperty ("hidden", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public event TargetEventHandler Click {
|
public event TargetEventHandler Click {
|
||||||
|
@ -102,8 +103,64 @@ namespace Ooui
|
||||||
Style.PropertyChanged += HandleStylePropertyChanged;
|
Style.PropertyChanged += HandleStylePropertyChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetAttribute (string attributeName, string value)
|
protected bool SetAttributeProperty (string attributeName, object newValue, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
|
||||||
{
|
{
|
||||||
|
var old = GetAttribute (attributeName);
|
||||||
|
if (old != null && old.Equals (newValue))
|
||||||
|
return false;
|
||||||
|
SetAttribute (attributeName, newValue);
|
||||||
|
OnPropertyChanged (propertyName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool SetBooleanAttributeProperty (string attributeName, bool newValue, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
|
||||||
|
{
|
||||||
|
var old = GetAttribute (attributeName) != null;
|
||||||
|
if (old == newValue)
|
||||||
|
return false;
|
||||||
|
if (newValue)
|
||||||
|
SetAttribute (attributeName, string.Empty);
|
||||||
|
else
|
||||||
|
RemoveAttribute (attributeName);
|
||||||
|
OnPropertyChanged (propertyName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool UpdateAttributeProperty (string attributeName, object newValue, string propertyName)
|
||||||
|
{
|
||||||
|
lock (attributes) {
|
||||||
|
if (attributes.TryGetValue (attributeName, out var oldValue)) {
|
||||||
|
if (newValue != null && newValue.Equals (oldValue))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
attributes[attributeName] = newValue;
|
||||||
|
}
|
||||||
|
OnPropertyChanged (propertyName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool UpdateBooleanAttributeProperty (string attributeName, bool newValue, string propertyName)
|
||||||
|
{
|
||||||
|
lock (attributes) {
|
||||||
|
var oldValue = attributes.ContainsKey (attributeName);
|
||||||
|
if (newValue == oldValue)
|
||||||
|
return false;
|
||||||
|
if (newValue) {
|
||||||
|
attributes[attributeName] = "";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
attributes.Remove (attributeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OnPropertyChanged (propertyName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetAttribute (string attributeName, object value)
|
||||||
|
{
|
||||||
|
lock (attributes) {
|
||||||
|
attributes[attributeName] = value;
|
||||||
|
}
|
||||||
Send (new Message {
|
Send (new Message {
|
||||||
MessageType = MessageType.SetAttribute,
|
MessageType = MessageType.SetAttribute,
|
||||||
TargetId = Id,
|
TargetId = Id,
|
||||||
|
@ -112,22 +169,97 @@ namespace Ooui
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public object GetAttribute (string attributeName)
|
||||||
|
{
|
||||||
|
lock (attributes) {
|
||||||
|
attributes.TryGetValue (attributeName, out var v);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GetAttribute<T> (string attributeName, T defaultValue)
|
||||||
|
{
|
||||||
|
lock (attributes) {
|
||||||
|
attributes.TryGetValue (attributeName, out var v);
|
||||||
|
if (v is T) {
|
||||||
|
return (T)v;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GetBooleanAttribute (string attributeName)
|
||||||
|
{
|
||||||
|
lock (attributes) {
|
||||||
|
return attributes.TryGetValue (attributeName, out var _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetStringAttribute (string attributeName, string defaultValue)
|
||||||
|
{
|
||||||
|
lock (attributes) {
|
||||||
|
if (attributes.TryGetValue (attributeName, out var v)) {
|
||||||
|
if (v == null) return "null";
|
||||||
|
else return v.ToString ();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAttribute (string attributeName)
|
||||||
|
{
|
||||||
|
bool removed;
|
||||||
|
lock (attributes) {
|
||||||
|
removed = attributes.Remove (attributeName);
|
||||||
|
}
|
||||||
|
if (removed) {
|
||||||
|
Send (new Message {
|
||||||
|
MessageType = MessageType.RemoveAttribute,
|
||||||
|
TargetId = Id,
|
||||||
|
Key = attributeName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void HandleStylePropertyChanged (object sender, PropertyChangedEventArgs e)
|
void HandleStylePropertyChanged (object sender, PropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
SendSet ("style." + Style.GetJsName (e.PropertyName), Style[e.PropertyName]);
|
SendSet ("style." + Style.GetJsName (e.PropertyName), Style[e.PropertyName]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool SaveStateMessageIfNeeded (Message message)
|
protected virtual bool HtmlNeedsFullEndElement => false;
|
||||||
{
|
|
||||||
if (message.TargetId != Id)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
switch (message.MessageType) {
|
public override void WriteOuterHtml (System.Xml.XmlWriter w)
|
||||||
case MessageType.Call when message.Key.StartsWith ("$.", StringComparison.Ordinal):
|
{
|
||||||
AddStateMessage (message);
|
w.WriteStartElement (TagName);
|
||||||
return true;
|
w.WriteAttributeString ("id", Id);
|
||||||
default:
|
var style = Style.ToString ();
|
||||||
return base.SaveStateMessageIfNeeded (message);
|
if (style.Length > 0) {
|
||||||
|
w.WriteAttributeString ("style", style);
|
||||||
|
}
|
||||||
|
lock (attributes) {
|
||||||
|
foreach (var a in attributes) {
|
||||||
|
var value = (a.Value == null) ? "null" : Convert.ToString (a.Value, System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
w.WriteAttributeString (a.Key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WriteInnerHtml (w);
|
||||||
|
if (HtmlNeedsFullEndElement) {
|
||||||
|
w.WriteFullEndElement ();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
w.WriteEndElement ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void WriteInnerHtml (System.Xml.XmlWriter w)
|
||||||
|
{
|
||||||
|
var children = Children;
|
||||||
|
foreach (var c in children) {
|
||||||
|
c.WriteOuterHtml (w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,12 +83,12 @@ namespace Ooui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool SetProperty<T> (ref T backingStore, T newValue, string attributeName, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
|
protected bool SetProperty<T> (ref T backingStore, T newValue, string jsPropertyName, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
|
||||||
{
|
{
|
||||||
if (EqualityComparer<T>.Default.Equals (backingStore, newValue))
|
if (EqualityComparer<T>.Default.Equals (backingStore, newValue))
|
||||||
return false;
|
return false;
|
||||||
backingStore = newValue;
|
backingStore = newValue;
|
||||||
SendSet (attributeName, newValue);
|
SendSet (jsPropertyName, newValue);
|
||||||
OnPropertyChanged (propertyName);
|
OnPropertyChanged (propertyName);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -120,12 +120,12 @@ namespace Ooui
|
||||||
Send (Message.Call (Id, methodName, args));
|
Send (Message.Call (Id, methodName, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void SendSet (string attributeName, object value)
|
protected void SendSet (string jsPropertyName, object value)
|
||||||
{
|
{
|
||||||
Send (new Message {
|
Send (new Message {
|
||||||
MessageType = MessageType.Set,
|
MessageType = MessageType.Set,
|
||||||
TargetId = Id,
|
TargetId = Id,
|
||||||
Key = attributeName,
|
Key = jsPropertyName,
|
||||||
Value = value,
|
Value = value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -169,6 +169,11 @@ namespace Ooui
|
||||||
state.Add (message);
|
state.Add (message);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case MessageType.RemoveAttribute:
|
||||||
|
this.UpdateStateMessages (state => {
|
||||||
|
state.RemoveAll (x => x.MessageType == MessageType.SetAttribute && x.Key == message.Key);
|
||||||
|
});
|
||||||
|
return true;
|
||||||
case MessageType.Listen:
|
case MessageType.Listen:
|
||||||
AddStateMessage (message);
|
AddStateMessage (message);
|
||||||
break;
|
break;
|
||||||
|
@ -177,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)
|
||||||
|
|
14
Ooui/Form.cs
14
Ooui/Form.cs
|
@ -6,20 +6,18 @@ namespace Ooui
|
||||||
{
|
{
|
||||||
string action = "";
|
string action = "";
|
||||||
public string Action {
|
public string Action {
|
||||||
get => action;
|
get => GetStringAttribute ("action", "");
|
||||||
set => SetProperty (ref action, value ?? "", "action");
|
set => SetAttributeProperty ("action", value ?? "");
|
||||||
}
|
}
|
||||||
|
|
||||||
string method = "GET";
|
|
||||||
public string Method {
|
public string Method {
|
||||||
get => method;
|
get => GetStringAttribute ("method", "GET");
|
||||||
set => SetProperty (ref method, value ?? "", "method");
|
set => SetAttributeProperty ("method", value ?? "");
|
||||||
}
|
}
|
||||||
|
|
||||||
string enctype = "application/x-www-form-urlencoded";
|
|
||||||
public string EncodingType {
|
public string EncodingType {
|
||||||
get => enctype;
|
get => GetStringAttribute ("enctype", "application/x-www-form-urlencoded");
|
||||||
set => SetProperty (ref enctype, value ?? "", "enctype");
|
set => SetAttributeProperty ("enctype", value ?? "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public event TargetEventHandler Submit {
|
public event TargetEventHandler Submit {
|
||||||
|
|
|
@ -4,16 +4,15 @@ namespace Ooui
|
||||||
{
|
{
|
||||||
public abstract class FormControl : Element
|
public abstract class FormControl : Element
|
||||||
{
|
{
|
||||||
string name = "";
|
|
||||||
public string Name {
|
public string Name {
|
||||||
get => name;
|
get => GetStringAttribute ("name", "");
|
||||||
set => SetProperty (ref name, value, "name");
|
set => SetAttributeProperty ("name", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDisabled = false;
|
bool isDisabled = false;
|
||||||
public bool IsDisabled {
|
public bool IsDisabled {
|
||||||
get => isDisabled;
|
get => GetBooleanAttribute ("disabled");
|
||||||
set => SetProperty (ref isDisabled, value, "disabled");
|
set => SetBooleanAttributeProperty ("disabled", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FormControl (string tagName)
|
public FormControl (string tagName)
|
||||||
|
|
|
@ -2,17 +2,15 @@
|
||||||
{
|
{
|
||||||
public class Iframe : Element
|
public class Iframe : Element
|
||||||
{
|
{
|
||||||
public Iframe()
|
public string Source
|
||||||
: base("iframe")
|
|
||||||
{
|
{
|
||||||
|
get => GetStringAttribute ("src", null);
|
||||||
|
set => SetAttributeProperty ("src", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
string src = null;
|
public Iframe ()
|
||||||
public string Src
|
: base ("iframe")
|
||||||
{
|
{
|
||||||
get => src;
|
|
||||||
set => SetProperty(ref src, value, "src");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,10 @@ namespace Ooui
|
||||||
{
|
{
|
||||||
public class Image : Element
|
public class Image : Element
|
||||||
{
|
{
|
||||||
string src = "";
|
public string Source
|
||||||
public string Source {
|
{
|
||||||
get => src;
|
get => GetStringAttribute ("src", null);
|
||||||
set => SetProperty (ref src, value ?? "", "src");
|
set => SetAttributeProperty ("src", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Image ()
|
public Image ()
|
||||||
|
|
|
@ -7,16 +7,14 @@ namespace Ooui
|
||||||
{
|
{
|
||||||
public class Input : FormControl
|
public class Input : FormControl
|
||||||
{
|
{
|
||||||
InputType typ = InputType.Text;
|
|
||||||
public InputType Type {
|
public InputType Type {
|
||||||
get => typ;
|
get => GetAttribute ("type", InputType.Text);
|
||||||
set => SetProperty (ref typ, value, "type");
|
set => SetAttributeProperty ("type", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
string val = "";
|
|
||||||
public string Value {
|
public string Value {
|
||||||
get => val;
|
get => GetStringAttribute ("value", "");
|
||||||
set => SetProperty (ref val, value ?? "", "value");
|
set => SetAttributeProperty ("value", value ?? "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public double NumberValue {
|
public double NumberValue {
|
||||||
|
@ -35,37 +33,33 @@ namespace Ooui
|
||||||
remove => RemoveEventListener ("change", value);
|
remove => RemoveEventListener ("change", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
string placeholder = "";
|
|
||||||
public string Placeholder {
|
public string Placeholder {
|
||||||
get => placeholder;
|
get => GetStringAttribute ("placeholder", "");
|
||||||
set => SetProperty (ref placeholder, value, "placeholder");
|
set => SetAttributeProperty ("placeholder", value ?? "");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isChecked = false;
|
|
||||||
public bool IsChecked {
|
public bool IsChecked {
|
||||||
get => isChecked;
|
get => GetBooleanAttribute ("checked");
|
||||||
set {
|
set {
|
||||||
SetProperty (ref isChecked, value, "checked");
|
if (SetBooleanAttributeProperty ("checked", value)) {
|
||||||
TriggerEventFromMessage (Message.Event (Id, "change", isChecked));
|
TriggerEvent ("change");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double minimum = 0;
|
|
||||||
public double Minimum {
|
public double Minimum {
|
||||||
get => minimum;
|
get => GetAttribute ("min", 0.0);
|
||||||
set => SetProperty (ref minimum, value, "min");
|
set => SetAttributeProperty ("min", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
double maximum = 100;
|
|
||||||
public double Maximum {
|
public double Maximum {
|
||||||
get => maximum;
|
get => GetAttribute ("max", 100.0);
|
||||||
set => SetProperty (ref maximum, value, "max");
|
set => SetAttributeProperty ("max", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
double step = 1;
|
|
||||||
public double Step {
|
public double Step {
|
||||||
get => step;
|
get => GetAttribute ("step", 1.0);
|
||||||
set => SetProperty (ref step, value, "step");
|
set => SetAttributeProperty ("step", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Input ()
|
public Input ()
|
||||||
|
@ -86,10 +80,10 @@ namespace Ooui
|
||||||
if (message.TargetId == Id && message.MessageType == MessageType.Event && (message.Key == "change" || message.Key == "input")) {
|
if (message.TargetId == Id && message.MessageType == MessageType.Event && (message.Key == "change" || message.Key == "input")) {
|
||||||
// Don't need to notify here because the base implementation will fire the event
|
// Don't need to notify here because the base implementation will fire the event
|
||||||
if (Type == InputType.Checkbox) {
|
if (Type == InputType.Checkbox) {
|
||||||
isChecked = message.Value != null ? Convert.ToBoolean (message.Value) : false;
|
UpdateBooleanAttributeProperty ("checked", message.Value != null ? Convert.ToBoolean (message.Value) : false, "IsChecked");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val = message.Value != null ? Convert.ToString (message.Value) : "";
|
UpdateAttributeProperty ("value", message.Value != null ? Convert.ToString (message.Value) : "", "Value");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return base.TriggerEventFromMessage (message);
|
return base.TriggerEventFromMessage (message);
|
||||||
|
|
|
@ -4,10 +4,9 @@ namespace Ooui
|
||||||
{
|
{
|
||||||
public class Label : Element
|
public class Label : Element
|
||||||
{
|
{
|
||||||
Element htmlFor = null;
|
|
||||||
public Element For {
|
public Element For {
|
||||||
get => htmlFor;
|
get => GetAttribute<Element> ("for", null);
|
||||||
set => SetProperty (ref htmlFor, value, "htmlFor");
|
set => SetAttributeProperty ("for", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Label ()
|
public Label ()
|
||||||
|
|
|
@ -48,6 +48,8 @@ namespace Ooui
|
||||||
Set,
|
Set,
|
||||||
[EnumMember (Value = "setAttr")]
|
[EnumMember (Value = "setAttr")]
|
||||||
SetAttribute,
|
SetAttribute,
|
||||||
|
[EnumMember(Value = "remAttr")]
|
||||||
|
RemoveAttribute,
|
||||||
[EnumMember(Value = "call")]
|
[EnumMember(Value = "call")]
|
||||||
Call,
|
Call,
|
||||||
[EnumMember(Value = "listen")]
|
[EnumMember(Value = "listen")]
|
||||||
|
|
19
Ooui/Node.cs
19
Ooui/Node.cs
|
@ -180,5 +180,24 @@ namespace Ooui
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual string OuterHtml {
|
||||||
|
get {
|
||||||
|
using (var stream = new System.IO.MemoryStream ()) {
|
||||||
|
var settings = new System.Xml.XmlWriterSettings {
|
||||||
|
OmitXmlDeclaration = true,
|
||||||
|
ConformanceLevel = System.Xml.ConformanceLevel.Fragment,
|
||||||
|
CloseOutput = false,
|
||||||
|
};
|
||||||
|
using (var w = System.Xml.XmlWriter.Create (stream, settings)) {
|
||||||
|
WriteOuterHtml (w);
|
||||||
|
}
|
||||||
|
stream.Position = 0;
|
||||||
|
return new System.IO.StreamReader (stream).ReadToEnd ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void WriteOuterHtml (System.Xml.XmlWriter w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,22 +4,19 @@ namespace Ooui
|
||||||
{
|
{
|
||||||
public class Option : Element
|
public class Option : Element
|
||||||
{
|
{
|
||||||
string val = "";
|
|
||||||
public string Value {
|
public string Value {
|
||||||
get => val;
|
get => GetStringAttribute ("value", "");
|
||||||
set => SetProperty (ref val, value ?? "", "value");
|
set => SetAttributeProperty ("value", value ?? "");
|
||||||
}
|
}
|
||||||
|
|
||||||
string label = "";
|
|
||||||
public string Label {
|
public string Label {
|
||||||
get => label;
|
get => GetStringAttribute ("label", "");
|
||||||
set => SetProperty (ref label, value ?? "", "label");
|
set => SetAttributeProperty ("label", value ?? "");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool defaultSelected = false;
|
|
||||||
public bool DefaultSelected {
|
public bool DefaultSelected {
|
||||||
get => defaultSelected;
|
get => GetBooleanAttribute ("selected");
|
||||||
set => SetProperty (ref defaultSelected, value, "defaultSelected");
|
set => SetBooleanAttributeProperty ("selected", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Option ()
|
public Option ()
|
||||||
|
|
|
@ -4,10 +4,9 @@ namespace Ooui
|
||||||
{
|
{
|
||||||
public class Select : FormControl
|
public class Select : FormControl
|
||||||
{
|
{
|
||||||
string val = "";
|
|
||||||
public string Value {
|
public string Value {
|
||||||
get => val;
|
get => GetStringAttribute ("value", "");
|
||||||
set => SetProperty (ref val, value ?? "", "value");
|
set => SetAttributeProperty ("value", value ?? "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public event TargetEventHandler Change {
|
public event TargetEventHandler Change {
|
||||||
|
@ -35,6 +34,7 @@ namespace Ooui
|
||||||
protected override void OnChildInsertedBefore (Node newChild, Node referenceChild)
|
protected override void OnChildInsertedBefore (Node newChild, Node referenceChild)
|
||||||
{
|
{
|
||||||
base.OnChildInsertedBefore (newChild, referenceChild);
|
base.OnChildInsertedBefore (newChild, referenceChild);
|
||||||
|
var val = Value;
|
||||||
if (string.IsNullOrEmpty (val) && newChild is Option o && !string.IsNullOrEmpty (o.Value)) {
|
if (string.IsNullOrEmpty (val) && newChild is Option o && !string.IsNullOrEmpty (o.Value)) {
|
||||||
val = o.Value;
|
val = o.Value;
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,8 @@ namespace Ooui
|
||||||
protected override bool TriggerEventFromMessage (Message message)
|
protected override bool TriggerEventFromMessage (Message message)
|
||||||
{
|
{
|
||||||
if (message.TargetId == Id && message.MessageType == MessageType.Event && (message.Key == "change" || message.Key == "input")) {
|
if (message.TargetId == Id && message.MessageType == MessageType.Event && (message.Key == "change" || message.Key == "input")) {
|
||||||
val = message.Value != null ? Convert.ToString (message.Value) : "";
|
SetAttribute ("value", message.Value != null ? Convert.ToString (message.Value) : "");
|
||||||
|
OnPropertyChanged ("Value");
|
||||||
}
|
}
|
||||||
return base.TriggerEventFromMessage (message);
|
return base.TriggerEventFromMessage (message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -399,7 +399,7 @@ namespace Ooui
|
||||||
o.Append (head);
|
o.Append (head);
|
||||||
o.Append (p.Key);
|
o.Append (p.Key);
|
||||||
o.Append (":");
|
o.Append (":");
|
||||||
o.Append (String.Format (System.Globalization.CultureInfo.InvariantCulture, "{0}", p.Value));
|
o.Append (Convert.ToString (p.Value, System.Globalization.CultureInfo.InvariantCulture));
|
||||||
head = ";";
|
head = ";";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,18 +20,19 @@ namespace Ooui
|
||||||
set => SetProperty (ref val, value ?? "", "value");
|
set => SetProperty (ref val, value ?? "", "value");
|
||||||
}
|
}
|
||||||
|
|
||||||
int rows = 2;
|
|
||||||
public int Rows {
|
public int Rows {
|
||||||
get => rows;
|
get => GetAttribute ("rows", 2);
|
||||||
set => SetProperty (ref rows, value, "rows");
|
set => SetAttributeProperty ("rows", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cols = 20;
|
int cols = 20;
|
||||||
public int Columns {
|
public int Columns {
|
||||||
get => cols;
|
get => GetAttribute ("cols", 20);
|
||||||
set => SetProperty (ref cols, value, "cols");
|
set => SetAttributeProperty ("cols", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool HtmlNeedsFullEndElement => true;
|
||||||
|
|
||||||
public TextArea ()
|
public TextArea ()
|
||||||
: base ("textarea")
|
: base ("textarea")
|
||||||
{
|
{
|
||||||
|
@ -53,5 +54,10 @@ namespace Ooui
|
||||||
}
|
}
|
||||||
return base.TriggerEventFromMessage (message);
|
return base.TriggerEventFromMessage (message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void WriteInnerHtml (System.Xml.XmlWriter w)
|
||||||
|
{
|
||||||
|
w.WriteString (val ?? "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,5 +20,10 @@ namespace Ooui
|
||||||
{
|
{
|
||||||
Text = text;
|
Text = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void WriteOuterHtml (System.Xml.XmlWriter w)
|
||||||
|
{
|
||||||
|
w.WriteString (text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
12
Ooui/UI.cs
12
Ooui/UI.cs
|
@ -40,14 +40,14 @@ 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>
|
||||||
<div id=""ooui-body"" class=""container-fluid""></div>
|
|
||||||
|
|
||||||
<script type=""text/javascript"" src=""https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js""></script>
|
<div id=""ooui-body"" class=""container-fluid"">
|
||||||
<script type=""text/javascript"" src=""https://gitcdn.github.io/bootstrap-toggle/2.2.2/js/bootstrap-toggle.min.js""></script>
|
@InitialHtml
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src=""/ooui.js""></script>
|
<script src=""/ooui.js""></script>
|
||||||
<script>ooui(""@WebSocketPath"");</script>
|
<script>ooui(""@WebSocketPath"");</script>
|
||||||
</body>
|
</body>
|
||||||
|
@ -390,9 +390,9 @@ namespace Ooui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string RenderTemplate (string webSocketPath, string title = "")
|
public static string RenderTemplate (string webSocketPath, string title = "", string initialHtml = "")
|
||||||
{
|
{
|
||||||
return Template.Replace ("@WebSocketPath", webSocketPath).Replace ("@Styles", rules.ToString ()).Replace ("@Title", title);
|
return Template.Replace ("@WebSocketPath", webSocketPath).Replace ("@Styles", rules.ToString ()).Replace ("@Title", title).Replace ("@InitialHtml", initialHtml);
|
||||||
}
|
}
|
||||||
|
|
||||||
class DataHandler : RequestHandler
|
class DataHandler : RequestHandler
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||||
<PackageReference Include="Xamarin.Forms" Version="2.4.0.38779" />
|
<PackageReference Include="Xamarin.Forms" Version="2.5.0.122203" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Ooui";
|
ViewData["Title"] = "Ooui";
|
||||||
|
var formsSamples = SamplesController.Samples.Where(x => x.Title.StartsWith("Xamarin.Forms "));
|
||||||
|
var otherSamples = SamplesController.Samples.Where(x => !x.Title.StartsWith("Xamarin.Forms "));
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="row" style="margin-top:4em;">
|
<div class="row" style="margin-top:4em;">
|
||||||
|
@ -9,14 +11,26 @@
|
||||||
<div class="col-md-2">
|
<div class="col-md-2">
|
||||||
<h1>Ooui</h1>
|
<h1>Ooui</h1>
|
||||||
<p>Write interactive web apps in C# and F#</p>
|
<p>Write interactive web apps in C# and F#</p>
|
||||||
|
<p><a href="https://github.com/praeclarum/Ooui">Source Code on GitHub</a></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row" style="margin-top:4em;">
|
<div class="row" style="margin-top:4em;">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<h3>Samples</h3>
|
<h3>Xamarin.Forms Samples</h3>
|
||||||
<ul>
|
<ul>
|
||||||
@foreach (var s in SamplesController.Samples) {
|
@foreach (var s in formsSamples) {
|
||||||
|
<li>
|
||||||
|
<a asp-area="" asp-controller="Samples" asp-action="Run" asp-route-name="@s.Title">@s.Title.Substring(14)</a>
|
||||||
|
(<a asp-area="" asp-controller="Samples" asp-action="Run" asp-route-name="@s.Title" asp-route-shared="@true">Shared</a>)
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h3>Plain Web Samples</h3>
|
||||||
|
<ul>
|
||||||
|
@foreach (var s in otherSamples) {
|
||||||
<li>
|
<li>
|
||||||
<a asp-area="" asp-controller="Samples" asp-action="Run" asp-route-name="@s.Title">@s.Title</a>
|
<a asp-area="" asp-controller="Samples" asp-action="Run" asp-route-name="@s.Title">@s.Title</a>
|
||||||
(<a asp-area="" asp-controller="Samples" asp-action="Run" asp-route-name="@s.Title" asp-route-shared="@true">Shared</a>)
|
(<a asp-area="" asp-controller="Samples" asp-action="Run" asp-route-name="@s.Title" asp-route-shared="@true">Shared</a>)
|
||||||
|
@ -24,10 +38,4 @@
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
|
||||||
<h3>Get it</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://github.com/praeclarum/Ooui">Source Code on Github</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -40,7 +40,10 @@
|
||||||
@RenderBody()
|
@RenderBody()
|
||||||
<hr />
|
<hr />
|
||||||
<footer>
|
<footer>
|
||||||
<p>© 2017 - Frank A. Krueger</p>
|
<p>© 2017 - @DateTime.UtcNow.Year Frank A. Krueger</p>
|
||||||
|
@{ var e = new Ooui.Anchor ("https://github.com/praeclarum/Ooui", "π"); }
|
||||||
|
<ooui element="e" />
|
||||||
|
<!--Html.Ooui (e)-->
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -2,3 +2,4 @@
|
||||||
@using AspNetCoreMvc.Models
|
@using AspNetCoreMvc.Models
|
||||||
@using AspNetCoreMvc.Controllers
|
@using AspNetCoreMvc.Controllers
|
||||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
@addTagHelper *, Ooui.AspNetCore
|
||||||
|
|
15
README.md
15
README.md
|
@ -27,7 +27,7 @@ msbuild
|
||||||
dotnet run --project Samples/Samples.csproj --no-build
|
dotnet run --project Samples/Samples.csproj --no-build
|
||||||
```
|
```
|
||||||
|
|
||||||
There is currently an issue with Xamarin.Forms and building from the dotnet cli, so for now we use the msbuild command and then set the --no-build flag on dotnet run but this will eventually change when the issue is resolved.
|
*(There is currently an issue with Xamarin.Forms and building from the dotnet cli, so for now we use the msbuild command and then set the --no-build flag on dotnet run but this will eventually change when the issue is resolved.)*
|
||||||
|
|
||||||
This will open the default starting page for the Samples. Now point your browser at [http://localhost:8080/shared-button](http://localhost:8080/shared-button)
|
This will open the default starting page for the Samples. Now point your browser at [http://localhost:8080/shared-button](http://localhost:8080/shared-button)
|
||||||
|
|
||||||
|
@ -111,8 +111,8 @@ When the user clicks or otherwise interacts with the UI, those events are sent b
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>How big is it?</th>
|
<th>How big is it?</th>
|
||||||
<td>50 KB</td>
|
<td>80 KB</td>
|
||||||
<td>650 KB</td>
|
<td>850 KB</td>
|
||||||
<td>1,300 KB</td>
|
<td>1,300 KB</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
@ -160,18 +160,11 @@ When the user clicks or otherwise interacts with the UI, those events are sent b
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>How do I style things?</th>
|
<th>How do I style things?</th>
|
||||||
<td>CSS baby! (soon)</td>
|
<td>CSS baby!</td>
|
||||||
<td>XAML resources</td>
|
<td>XAML resources</td>
|
||||||
<td>CSS</td>
|
<td>CSS</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
|
||||||
<th>Is there databinding?</th>
|
|
||||||
<td>No :-(</td>
|
|
||||||
<td>Yes!</td>
|
|
||||||
<td>Debatable</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>Do I need to run a server?</th>
|
<th>Do I need to run a server?</th>
|
||||||
<td>Nope</td>
|
<td>Nope</td>
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Xamarin.Forms" Version="2.4.0.38779" />
|
<PackageReference Include="Xamarin.Forms" Version="2.5.0.122203" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
using System;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Samples
|
||||||
|
{
|
||||||
|
public class WrappingTextSample : ISample
|
||||||
|
{
|
||||||
|
public string Title => "Xamarin.Forms Wrapping Text";
|
||||||
|
|
||||||
|
public Ooui.Element CreateElement()
|
||||||
|
{
|
||||||
|
var rows = new StackLayout { Orientation = StackOrientation.Vertical };
|
||||||
|
|
||||||
|
var row0 = new StackLayout { Orientation = StackOrientation.Horizontal, BackgroundColor = Color.Azure };
|
||||||
|
row0.Children.Add (new Label { Text = shortText, LineBreakMode = LineBreakMode.WordWrap });
|
||||||
|
row0.Children.Add (new Label { Text = mediumText, LineBreakMode = LineBreakMode.WordWrap });
|
||||||
|
row0.Children.Add (new Label { Text = longText, LineBreakMode = LineBreakMode.WordWrap });
|
||||||
|
rows.Children.Add (row0);
|
||||||
|
|
||||||
|
var row1 = new StackLayout { Orientation = StackOrientation.Horizontal, BackgroundColor = Color.GhostWhite };
|
||||||
|
row1.Children.Add (new Label { Text = shortText, FontAttributes = FontAttributes.Bold, HorizontalOptions = LayoutOptions.Start });
|
||||||
|
row1.Children.Add (new Label { Text = mediumText, FontAttributes = FontAttributes.Bold, HorizontalOptions = LayoutOptions.FillAndExpand });
|
||||||
|
row1.Children.Add (new Label { Text = longText, FontAttributes = FontAttributes.Bold, HorizontalOptions = LayoutOptions.End });
|
||||||
|
rows.Children.Add (row1);
|
||||||
|
|
||||||
|
var page = new ContentPage
|
||||||
|
{
|
||||||
|
Content = rows
|
||||||
|
};
|
||||||
|
|
||||||
|
return page.GetOouiElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Publish()
|
||||||
|
{
|
||||||
|
Ooui.UI.Publish("/wrapping", CreateElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
const string shortText = "Lorem ipsum dolor sit amet.";
|
||||||
|
const string mediumText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
|
||||||
|
const string longText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
|
||||||
|
}
|
||||||
|
}
|
|
@ -58,8 +58,8 @@ namespace Tests
|
||||||
Assert.AreEqual (480, c.Height);
|
Assert.AreEqual (480, c.Height);
|
||||||
c.Width = 0;
|
c.Width = 0;
|
||||||
c.Height = -100;
|
c.Height = -100;
|
||||||
Assert.AreEqual (150, c.Width);
|
Assert.AreEqual (0, c.Width);
|
||||||
Assert.AreEqual (150, c.Height);
|
Assert.AreEqual (0, c.Height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
#if NUNIT
|
||||||
|
using NUnit.Framework;
|
||||||
|
using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
|
||||||
|
using TestMethodAttribute = NUnit.Framework.TestCaseAttribute;
|
||||||
|
#else
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using Ooui;
|
||||||
|
|
||||||
|
namespace Tests
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class WriteHtmlTests
|
||||||
|
{
|
||||||
|
System.Text.RegularExpressions.Regex idre = new System.Text.RegularExpressions.Regex ("\\sid=\"[^\"]*\"");
|
||||||
|
|
||||||
|
string OuterHtmlWithoutIds (Element e)
|
||||||
|
{
|
||||||
|
return idre.Replace (e.OuterHtml, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TextAreaWithTextStyled ()
|
||||||
|
{
|
||||||
|
var e = new TextArea {
|
||||||
|
Value = "Hello World!",
|
||||||
|
};
|
||||||
|
e.Style.BackgroundColor = "#18f";
|
||||||
|
Assert.AreEqual ("<textarea style=\"background-color:#18f\">Hello World!</textarea>", OuterHtmlWithoutIds (e));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TextAreaEmptyStyled ()
|
||||||
|
{
|
||||||
|
var e = new TextArea ();
|
||||||
|
e.Style.BackgroundColor = "#18f";
|
||||||
|
Assert.AreEqual ("<textarea style=\"background-color:#18f\"></textarea>", OuterHtmlWithoutIds (e));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Style ()
|
||||||
|
{
|
||||||
|
var e = new Div ();
|
||||||
|
e.Style.BackgroundColor = "#18f";
|
||||||
|
Assert.AreEqual ("<div style=\"background-color:#18f\"></div>", OuterHtmlWithoutIds (e));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TwoGrandChildren ()
|
||||||
|
{
|
||||||
|
var e = new Div (new Div (new Anchor (), new Anchor ()), new Paragraph ());
|
||||||
|
Assert.AreEqual ("<div><div><a /><a /></div><p /></div>", OuterHtmlWithoutIds (e));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Child ()
|
||||||
|
{
|
||||||
|
var e = new Div (new Anchor ());
|
||||||
|
Assert.AreEqual ("<div><a /></div>", OuterHtmlWithoutIds (e));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TextChild ()
|
||||||
|
{
|
||||||
|
var e = new Paragraph ("Hello world!");
|
||||||
|
Assert.AreEqual ("<p>Hello world!</p>", OuterHtmlWithoutIds (e));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void IdIsFirst ()
|
||||||
|
{
|
||||||
|
var e = new Anchor ();
|
||||||
|
Assert.IsTrue (e.OuterHtml.StartsWith ("<a id=\""));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void EmptyElement ()
|
||||||
|
{
|
||||||
|
var e = new Anchor ();
|
||||||
|
Assert.AreEqual ("<a />", OuterHtmlWithoutIds (e));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void AnchorHRef ()
|
||||||
|
{
|
||||||
|
var e = new Anchor {
|
||||||
|
HRef = "http://google.com"
|
||||||
|
};
|
||||||
|
Assert.AreEqual ("<a href=\"http://google.com\" />", OuterHtmlWithoutIds (e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue