Init Xamarin.Forms pages with the browser size

This commit is contained in:
Frank A. Krueger 2017-11-19 14:19:44 -06:00
parent 010d1f75d0
commit 1f1c90c250
9 changed files with 175 additions and 31 deletions

View File

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

View File

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

View File

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

View File

@ -9,6 +9,8 @@ namespace Ooui.Forms
public Platform Platform => platform;
public override bool WantsFullScreen => true;
public PlatformRenderer (Platform platform)
{
this.platform = platform;

View File

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

View File

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

View File

@ -90,6 +90,12 @@ namespace Ooui
remove => RemoveEventListener ("wheel", value);
}
/// <summary>
/// A signal to Ooui that this element should take up the
/// entire browser window.
/// </summary>
public virtual bool WantsFullScreen => false;
protected Element (string tagName)
: base (tagName)
{

View File

@ -11,6 +11,10 @@ namespace Ooui
readonly Dictionary<string, Value> properties =
new Dictionary<string, Value> ();
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;
}
}
}

View File

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