diff --git a/Ooui.AspNetCore/WebSocketHandler.cs b/Ooui.AspNetCore/WebSocketHandler.cs
index 0c22f74..ce6f417 100644
--- a/Ooui.AspNetCore/WebSocketHandler.cs
+++ b/Ooui.AspNetCore/WebSocketHandler.cs
@@ -32,19 +32,30 @@ namespace Ooui.AspNetCore
return id;
}
+
+
public static async Task HandleWebSocketRequestAsync (HttpContext context)
{
+ void BadRequest (string message)
+ {
+ context.Response.StatusCode = StatusCodes.Status400BadRequest;
+ context.Response.ContentType = "text/plain; charset=utf-8";
+ using (var sw = new System.IO.StreamWriter (context.Response.Body)) {
+ sw.WriteLine (message);
+ }
+ }
+
//
// Make sure we get a good ID
//
if (!context.Request.Query.TryGetValue ("id", out var idValues)) {
- context.Response.StatusCode = StatusCodes.Status400BadRequest;
+ BadRequest ("Missing `id`");
return;
}
- var id = idValues.FirstOrDefault ();
+ var id = idValues.LastOrDefault ();
if (id == null || id.Length != 32) {
- context.Response.StatusCode = StatusCodes.Status400BadRequest;
+ BadRequest ("Invalid `id`");
return;
}
@@ -52,7 +63,7 @@ namespace Ooui.AspNetCore
// Find the pending session
//
if (!pendingSessions.TryRemove (id, out var pendingSession)) {
- context.Response.StatusCode = StatusCodes.Status400BadRequest;
+ BadRequest ("Unknown `id`");
return;
}
@@ -60,16 +71,32 @@ namespace Ooui.AspNetCore
// Reject the session if it's old
//
if ((DateTime.UtcNow - pendingSession.RequestTimeUtc) > SessionTimeout) {
- context.Response.StatusCode = StatusCodes.Status400BadRequest;
+ BadRequest ("Old `id`");
return;
}
+ //
+ // Set the element's dimensions
+ //
+ if (!context.Request.Query.TryGetValue ("w", out var wValues) || wValues.Count < 1) {
+ BadRequest ("Missing `w`");
+ return;
+ }
+ if (!context.Request.Query.TryGetValue ("h", out var hValues) || hValues.Count < 1) {
+ BadRequest ("Missing `h`");
+ return;
+ }
+ if (!double.TryParse (wValues.Last (), out var w))
+ w = 640;
+ if (!double.TryParse (hValues.Last (), out var h))
+ h = 480;
+
//
// OK, Run
//
var token = CancellationToken.None;
var webSocket = await context.WebSockets.AcceptWebSocketAsync ("ooui");
- var session = new Ooui.UI.Session (webSocket, pendingSession.Element, token);
+ var session = new Ooui.UI.Session (webSocket, pendingSession.Element, w, h, token);
await session.RunAsync ().ConfigureAwait (false);
}
diff --git a/Ooui.Forms/Extensions/ElementExtensions.cs b/Ooui.Forms/Extensions/ElementExtensions.cs
index eb64812..50b7cb6 100644
--- a/Ooui.Forms/Extensions/ElementExtensions.cs
+++ b/Ooui.Forms/Extensions/ElementExtensions.cs
@@ -8,15 +8,35 @@ namespace Ooui.Forms.Extensions
public static SizeRequest GetSizeRequest (this Ooui.Element self, double widthConstraint, double heightConstraint,
double minimumWidth = -1, double minimumHeight = -1)
{
- var s = self.Text.MeasureSize (self.Style);
+ var rw = 0.0;
+ var rh = 0.0;
+ Size s = new Size (0, 0);
+ var measured = false;
- var request = new Size (
- double.IsPositiveInfinity (s.Width) ? double.PositiveInfinity : s.Width,
- double.IsPositiveInfinity (s.Height) ? double.PositiveInfinity : s.Height);
- var minimum = new Size (minimumWidth < 0 ? request.Width : minimumWidth,
- minimumHeight < 0 ? request.Height : minimumHeight);
+ if (self.Style.Width.Equals ("inherit")) {
+ s = self.Text.MeasureSize (self.Style);
+ measured = true;
+ rw = double.IsPositiveInfinity (s.Width) ? double.PositiveInfinity : s.Width;
+ }
+ else {
+ rw = self.Style.GetNumberWithUnits ("width", "px", 640);
+ }
- return new SizeRequest (request, minimum);
+ if (self.Style.Height.Equals ("inherit")) {
+ if (!measured) {
+ s = self.Text.MeasureSize (self.Style);
+ measured = true;
+ }
+ rh = double.IsPositiveInfinity (s.Height) ? double.PositiveInfinity : s.Height;
+ }
+ else {
+ rh = self.Style.GetNumberWithUnits ("height", "px", 480);
+ }
+
+ var minimum = new Size (minimumWidth < 0 ? rw : minimumWidth,
+ minimumHeight < 0 ? rh : minimumHeight);
+
+ return new SizeRequest (new Size (rw, rh), minimum);
}
}
}
diff --git a/Ooui.Forms/Platform.cs b/Ooui.Forms/Platform.cs
index 5295ac6..938adb2 100644
--- a/Ooui.Forms/Platform.cs
+++ b/Ooui.Forms/Platform.cs
@@ -33,16 +33,17 @@ namespace Ooui.Forms
{
_renderer = new PlatformRenderer (this);
- MessagingCenter.Subscribe(this, Page.AlertSignalName, (Page sender, AlertArguments arguments) =>
- {
- var alert = new DisplayAlert(arguments);
+ _renderer.Style.PropertyChanged += HandleRendererStyle_PropertyChanged;
+
+ MessagingCenter.Subscribe (this, Page.AlertSignalName, (Page sender, AlertArguments arguments) => {
+ var alert = new DisplayAlert (arguments);
alert.Clicked += CloseAlert;
- _renderer.AppendChild(alert.Element);
+ _renderer.AppendChild (alert.Element);
- void CloseAlert(object s, EventArgs e)
+ void CloseAlert (object s, EventArgs e)
{
- _renderer.RemoveChild(alert.Element);
+ _renderer.RemoveChild (alert.Element);
}
});
}
@@ -128,6 +129,12 @@ namespace Ooui.Forms
Console.Error.WriteLine ("Potential view double add");
}
+ void HandleRendererStyle_PropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ var pageRenderer = GetRenderer (Page);
+ pageRenderer?.SetElementSize (Ooui.Forms.Extensions.ElementExtensions.GetSizeRequest (_renderer, double.PositiveInfinity, double.PositiveInfinity).Request);
+ }
+
void INavigation.InsertPageBefore (Page page, Page before)
{
throw new NotImplementedException ();
diff --git a/Ooui.Forms/PlatformRenderer.cs b/Ooui.Forms/PlatformRenderer.cs
index 6e8d939..d555803 100644
--- a/Ooui.Forms/PlatformRenderer.cs
+++ b/Ooui.Forms/PlatformRenderer.cs
@@ -9,6 +9,8 @@ namespace Ooui.Forms
public Platform Platform => platform;
+ public override bool WantsFullScreen => true;
+
public PlatformRenderer (Platform platform)
{
this.platform = platform;
diff --git a/Ooui.Forms/VisualElementTracker.cs b/Ooui.Forms/VisualElementTracker.cs
index 6efb363..1e570ee 100644
--- a/Ooui.Forms/VisualElementTracker.cs
+++ b/Ooui.Forms/VisualElementTracker.cs
@@ -142,10 +142,10 @@ namespace Ooui.Forms
bool shouldUpdate = width > 0 && height > 0 && parent != null && (boundsChanged || parentBoundsChanged);
if (shouldUpdate) {
uiview.Style.Position = "absolute";
- uiview.Style.Left = x + "px";
- uiview.Style.Top = y + "px";
- uiview.Style.Width = width + "px";
- uiview.Style.Height = height + "px";
+ uiview.Style.Left = x;
+ uiview.Style.Top = y;
+ uiview.Style.Width = width;
+ uiview.Style.Height = height;
Renderer.SetControlSize (new Size (width, height));
}
else if (width <= 0 || height <= 0) {
diff --git a/Ooui/Client.js b/Ooui/Client.js
index 779ca36..108fc35 100644
--- a/Ooui/Client.js
+++ b/Ooui/Client.js
@@ -18,6 +18,7 @@ const mouseEvents = {
wheel: true,
};
+// Try to close the socket gracefully
window.onbeforeunload = function() {
if (socket != null) {
socket.close (1001, "Unloading page");
@@ -27,10 +28,22 @@ window.onbeforeunload = function() {
return null;
}
+function getSize () {
+ return {
+ height: window.innerHeight,
+ width: window.innerWidth
+ };
+}
+
+// Main entrypoint
function ooui (rootElementPath) {
var opened = false;
- socket = new WebSocket ("ws://" + document.location.host + rootElementPath, "ooui");
+ var initialSize = getSize ();
+ var wsArgs = (rootElementPath.indexOf("?") >= 0 ? "&" : "?") +
+ "w=" + initialSize.width + "&h=" + initialSize.height;
+
+ socket = new WebSocket ("ws://" + document.location.host + rootElementPath + wsArgs, "ooui");
socket.addEventListener ("open", function (event) {
console.log ("Web socket opened");
@@ -61,6 +74,34 @@ function ooui (rootElementPath) {
});
console.log("Web socket created");
+
+ // Throttled window resize event
+ (function() {
+ window.addEventListener("resize", resizeThrottler, false);
+
+ var resizeTimeout;
+ function resizeThrottler() {
+ if (!resizeTimeout) {
+ resizeTimeout = setTimeout(function() {
+ resizeTimeout = null;
+ actualResizeHandler();
+ }, 100);
+ }
+ }
+
+ function resizeHandler() {
+ const em = {
+ m: "event",
+ id: 42,
+ k: "window.resize",
+ v: getSize (),
+ };
+ const ems = JSON.stringify (em);
+ if (socket != null)
+ socket.send (ems);
+ if (debug) console.log ("Event", em);
+ }
+ }());
}
function getNode (id) {
diff --git a/Ooui/Element.cs b/Ooui/Element.cs
index 253f57d..8ba85e9 100644
--- a/Ooui/Element.cs
+++ b/Ooui/Element.cs
@@ -90,6 +90,12 @@ namespace Ooui
remove => RemoveEventListener ("wheel", value);
}
+ ///
+ /// A signal to Ooui that this element should take up the
+ /// entire browser window.
+ ///
+ public virtual bool WantsFullScreen => false;
+
protected Element (string tagName)
: base (tagName)
{
diff --git a/Ooui/Style.cs b/Ooui/Style.cs
index 1a0fedb..cec8577 100644
--- a/Ooui/Style.cs
+++ b/Ooui/Style.cs
@@ -11,6 +11,10 @@ namespace Ooui
readonly Dictionary properties =
new Dictionary ();
+ static readonly private char[] numberChars = new char[] {
+ '0','1','2','3','4','5','6','7','8','9',
+ '.','-','+',
+ };
public Value AlignSelf {
get => this["align-self"];
@@ -126,7 +130,7 @@ namespace Ooui
public Value Bottom {
get => this["bottom"];
- set => this["bottom"] = value;
+ set => this["bottom"] = AddNumberUnits (value, "px");
}
public Value Clear {
@@ -196,12 +200,12 @@ namespace Ooui
public Value Height {
get => this["height"];
- set => this["height"] = value;
+ set => this["height"] = AddNumberUnits (value, "px");
}
public Value Left {
get => this["left"];
- set => this["left"] = value;
+ set => this["left"] = AddNumberUnits (value, "px");
}
public Value LineHeight {
@@ -301,7 +305,7 @@ namespace Ooui
public Value Top {
get => this["top"];
- set => this["top"] = value;
+ set => this["top"] = AddNumberUnits (value, "px");
}
public Value Transform {
@@ -326,7 +330,7 @@ namespace Ooui
public Value Width {
get => this["width"];
- set => this["width"] = value;
+ set => this["width"] = AddNumberUnits (value, "px");
}
public Value ZIndex {
@@ -396,5 +400,34 @@ namespace Ooui
}
return o.ToString ();
}
+
+ static string AddNumberUnits (object val, string units)
+ {
+ if (val is string s)
+ return s;
+ if (val is IConvertible c)
+ return c.ToString (System.Globalization.CultureInfo.InvariantCulture) + units;
+ return val.ToString ();
+ }
+
+ public double GetNumberWithUnits (string key, string units, double baseValue)
+ {
+ var v = this[key];
+ if (v == null)
+ return 0;
+
+ if (v is string s) {
+ var lastIndex = s.LastIndexOfAny (numberChars);
+ if (lastIndex < 0)
+ return 0;
+ var num = double.Parse (s.Substring (0, lastIndex + 1), System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture);
+ return num;
+ }
+
+ if (v is IConvertible c)
+ return c.ToDouble (System.Globalization.CultureInfo.InvariantCulture);
+
+ return 0;
+ }
}
}
diff --git a/Ooui/UI.cs b/Ooui/UI.cs
index 079972b..b3a3ac9 100644
--- a/Ooui/UI.cs
+++ b/Ooui/UI.cs
@@ -436,7 +436,7 @@ namespace Ooui
// Create a new session and let it handle everything from here
//
try {
- var session = new Session (webSocket, element, serverToken);
+ var session = new Session (webSocket, element, 1024, 768, serverToken);
await session.RunAsync ().ConfigureAwait (false);
}
catch (WebSocketException ex) when (ex.WebSocketErrorCode == WebSocketError.ConnectionClosedPrematurely) {
@@ -473,11 +473,15 @@ namespace Ooui
readonly System.Timers.Timer sendThrottle;
DateTime lastTransmitTime = DateTime.MinValue;
readonly TimeSpan throttleInterval = TimeSpan.FromSeconds (1.0 / 30); // 30 FPS max
+ readonly double initialWidth;
+ readonly double initialHeight;
- public Session (WebSocket webSocket, Element element, CancellationToken serverToken)
+ public Session (WebSocket webSocket, Element element, double initialWidth, double initialHeight, CancellationToken serverToken)
{
this.webSocket = webSocket;
this.element = element;
+ this.initialWidth = initialWidth;
+ this.initialHeight = initialHeight;
//
// Create a new session cancellation token that will trigger
@@ -525,6 +529,10 @@ namespace Ooui
//
// Add it to the document body
//
+ if (element.WantsFullScreen) {
+ element.Style.Width = initialWidth;
+ element.Style.Height = initialHeight;
+ }
QueueMessage (Message.Call ("document.body", "appendChild", element));
//