mirror of
https://github.com/sbrl/Nibriboard.git
synced 2018-01-10 21:33:49 +00:00
Begin working on the client, and fix a ton of bugs in the server deserialisation process.
This commit is contained in:
parent
199ffbfe40
commit
690abc6445
14 changed files with 1653 additions and 17 deletions
20
Nibriboard/Client/Messages/ClientStateMessage.cs
Normal file
20
Nibriboard/Client/Messages/ClientStateMessage.cs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using RippleSpace;
|
||||||
|
|
||||||
|
namespace Nibriboard.Client.Messages
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an update of where a group of connected clients are.
|
||||||
|
/// </summary>
|
||||||
|
public class ClientStateMessage : Message
|
||||||
|
{
|
||||||
|
public List<ClientState> ClientStates = new List<ClientState>();
|
||||||
|
|
||||||
|
public ClientStateMessage()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
21
Nibriboard/Client/Messages/CursorPositionMessage.cs
Normal file
21
Nibriboard/Client/Messages/CursorPositionMessage.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace Nibriboard.Client.Messages
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an update by the client of their cursor position.
|
||||||
|
/// </summary>
|
||||||
|
public class CursorPositionMessage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The absolute cursor position.
|
||||||
|
/// </summary>
|
||||||
|
public Point AbsCursorPosition;
|
||||||
|
|
||||||
|
public CursorPositionMessage()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
// TODO: In C# you can either have namespaces or types in a namespace - not both.
|
||||||
|
using Nibriboard.Utilities.JsonConverters;
|
||||||
|
|
||||||
namespace Nibriboard.Client.Messages
|
namespace Nibriboard.Client.Messages
|
||||||
{
|
{
|
||||||
public class HandshakeRequestMessage : Message
|
public class HandshakeRequestMessage : Message
|
||||||
|
@ -9,10 +13,12 @@ namespace Nibriboard.Client.Messages
|
||||||
/// The initial visible area on the client's screen.
|
/// The initial visible area on the client's screen.
|
||||||
/// Very useful for determining which chunks we should send a client when they first connect.
|
/// Very useful for determining which chunks we should send a client when they first connect.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonConverter(typeof(RectangleConverter))]
|
||||||
public Rectangle InitialViewport = Rectangle.Empty;
|
public Rectangle InitialViewport = Rectangle.Empty;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The initial position of the user's cursor.
|
/// The initial position of the user's cursor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonConverter(typeof(RectangleConverter))]
|
||||||
public Point InitialAbsCursorPosition = Point.Empty;
|
public Point InitialAbsCursorPosition = Point.Empty;
|
||||||
|
|
||||||
public HandshakeRequestMessage()
|
public HandshakeRequestMessage()
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.Threading.Tasks;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
using IotWeb.Common.Http;
|
using IotWeb.Common.Http;
|
||||||
using SBRL.Utilities;
|
using SBRL.Utilities;
|
||||||
|
@ -88,12 +89,19 @@ namespace Nibriboard.Client
|
||||||
{
|
{
|
||||||
string eventName = JsonUtilities.DeserializeProperty<string>(frame, "event");
|
string eventName = JsonUtilities.DeserializeProperty<string>(frame, "event");
|
||||||
|
|
||||||
if (!messageEventTypes.ContainsKey(eventName))
|
if(eventName == null) {
|
||||||
Log.WriteLine("Received message with invalid event {1} from Client #{0}", Id, eventName);
|
Log.WriteLine("[NibriClient#{0}] Received message that didn't have an event.", Id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!messageEventTypes.ContainsKey(eventName)) {
|
||||||
|
Log.WriteLine("[NibriClient#{0}] Received message with invalid event {1}.", Id, eventName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Type messageType = messageEventTypes[eventName];
|
Type messageType = messageEventTypes[eventName];
|
||||||
Type jsonNet = typeof(JsonConvert);
|
Type jsonNet = typeof(JsonConvert);
|
||||||
MethodInfo deserialiserInfo = jsonNet.GetMethod("DeserailizeObject");
|
MethodInfo deserialiserInfo = jsonNet.GetMethods().First(method => method.Name == "DeserializeObject" && method.IsGenericMethod);
|
||||||
MethodInfo genericInfo = deserialiserInfo.MakeGenericMethod(messageType);
|
MethodInfo genericInfo = deserialiserInfo.MakeGenericMethod(messageType);
|
||||||
var decodedMessage = genericInfo.Invoke(null, new object[] { frame });
|
var decodedMessage = genericInfo.Invoke(null, new object[] { frame });
|
||||||
|
|
||||||
|
@ -147,6 +155,7 @@ namespace Nibriboard.Client
|
||||||
CurrentViewPort = message.InitialViewport;
|
CurrentViewPort = message.InitialViewport;
|
||||||
AbsoluteCursorPosition = message.InitialAbsCursorPosition;
|
AbsoluteCursorPosition = message.InitialAbsCursorPosition;
|
||||||
|
|
||||||
|
// Tell everyone else about the new client
|
||||||
Send(GenerateClientStateUpdate());
|
Send(GenerateClientStateUpdate());
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
@ -158,6 +167,11 @@ namespace Nibriboard.Client
|
||||||
protected Task handleCursorPositionMessage(CursorPositionMessage message) {
|
protected Task handleCursorPositionMessage(CursorPositionMessage message) {
|
||||||
AbsoluteCursorPosition = message.AbsCursorPosition;
|
AbsoluteCursorPosition = message.AbsCursorPosition;
|
||||||
|
|
||||||
|
// Send the update to the other clients
|
||||||
|
ClientStateMessage updateMessage = new ClientStateMessage();
|
||||||
|
updateMessage.ClientStates.Add(this.GenerateStateSnapshot());
|
||||||
|
manager.Broadcast(this, updateMessage);
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
|
@ -3,6 +3,7 @@ using IotWeb.Common.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using Nibriboard.Client.Messages;
|
||||||
|
|
||||||
namespace Nibriboard.Client
|
namespace Nibriboard.Client
|
||||||
{
|
{
|
||||||
|
@ -51,7 +52,8 @@ namespace Nibriboard.Client
|
||||||
Clients.Add(client);
|
Clients.Add(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Broadcast(NibriClient sendingClient, string message)
|
|
||||||
|
public void Broadcast(NibriClient sendingClient, Message message)
|
||||||
{
|
{
|
||||||
foreach(NibriClient client in Clients)
|
foreach(NibriClient client in Clients)
|
||||||
{
|
{
|
||||||
|
@ -59,7 +61,7 @@ namespace Nibriboard.Client
|
||||||
if (client == sendingClient)
|
if (client == sendingClient)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
client.SendRaw(message);
|
client.Send(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
// npm modules
|
// npm modules
|
||||||
window.EventEmitter = require("event-emitter-es6");
|
window.EventEmitter = require("event-emitter-es6");
|
||||||
window.FaviconNotification = require("favicon-notification");
|
window.FaviconNotification = require("favicon-notification");
|
||||||
|
window.panzoom = require("pan-zoom");
|
||||||
|
|
||||||
// Our files
|
// Our files
|
||||||
import RippleLink from './RippleLink';
|
import RippleLink from './RippleLink';
|
||||||
|
@ -14,18 +15,31 @@ class BoardWindow extends EventEmitter
|
||||||
{
|
{
|
||||||
super(); // Run the parent constructor
|
super(); // Run the parent constructor
|
||||||
|
|
||||||
|
// The maximum target fps.
|
||||||
this.maxFps = 60;
|
this.maxFps = 60;
|
||||||
|
// Setup the fps indicator in the corner
|
||||||
this.renderTimeIndicator = document.createElement("span");
|
this.renderTimeIndicator = document.createElement("span");
|
||||||
this.renderTimeIndicator.innerHTML = "0ms";
|
this.renderTimeIndicator.innerHTML = "0ms";
|
||||||
document.querySelector(".fps").appendChild(this.renderTimeIndicator);
|
document.querySelector(".fps").appendChild(this.renderTimeIndicator);
|
||||||
|
|
||||||
|
// Setup the canvas
|
||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
this.context = canvas.getContext("2d");
|
this.context = canvas.getContext("2d");
|
||||||
|
|
||||||
|
// --~~~--
|
||||||
|
|
||||||
|
// Setup the favicon thingy
|
||||||
|
|
||||||
FaviconNotification.init({
|
FaviconNotification.init({
|
||||||
color: '#ff6333'
|
color: '#ff6333'
|
||||||
});
|
});
|
||||||
FaviconNotification.add();
|
FaviconNotification.add();
|
||||||
|
|
||||||
|
// Setup the input controls
|
||||||
|
window.panzoom(canvas, this.handleCanvasMovement.bind(this));
|
||||||
|
|
||||||
|
// Fetch the RippleLink connection information and other settings from
|
||||||
|
// the server
|
||||||
get("/Settings.json").then(JSON.parse).then((function(settings) {
|
get("/Settings.json").then(JSON.parse).then((function(settings) {
|
||||||
console.info("[setup]", "Obtained settings from server:", settings);
|
console.info("[setup]", "Obtained settings from server:", settings);
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
@ -34,13 +48,39 @@ class BoardWindow extends EventEmitter
|
||||||
console.error(`Error: Failed to fetch settings from server! Response: ${errorMessage}`);
|
console.error(`Error: Failed to fetch settings from server! Response: ${errorMessage}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Make the canvas track the window size
|
||||||
this.trackWindowSize();
|
this.trackWindowSize();
|
||||||
|
// Track the mouse position
|
||||||
|
this.trackMousePosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup ready for user input.
|
||||||
|
* This mainly consists of establishing the RippleLink connection to the server.
|
||||||
|
*/
|
||||||
setup() {
|
setup() {
|
||||||
this.rippleLink = new RippleLink(this.settings.WebsocketUri, this);
|
this.rippleLink = new RippleLink(this.settings.WebsocketUri, this);
|
||||||
|
this.rippleLink.on("connect", (function(event) {
|
||||||
|
// Send the handshake request
|
||||||
|
this.rippleLink.send({
|
||||||
|
event: "handshakeRequest",
|
||||||
|
InitialViewport: { // TODO: Add support for persisting this between sessions
|
||||||
|
X: 0,
|
||||||
|
Y: 0,
|
||||||
|
Width: window.innerWidth,
|
||||||
|
Height: window.innerHeight
|
||||||
|
},
|
||||||
|
InitialAbsCursorPosition: this.cursorPosition
|
||||||
|
});
|
||||||
|
}).bind(this));
|
||||||
|
|
||||||
|
// RippleLink message bindings
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the next frame.
|
||||||
|
*/
|
||||||
nextFrame()
|
nextFrame()
|
||||||
{
|
{
|
||||||
// The time at which the current frame started rendering.
|
// The time at which the current frame started rendering.
|
||||||
|
@ -63,11 +103,17 @@ class BoardWindow extends EventEmitter
|
||||||
requestAnimationFrame(this.nextFrame.bind(this));
|
requestAnimationFrame(this.nextFrame.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates everything ready for the next frame to be rendered.
|
||||||
|
*/
|
||||||
update()
|
update()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the next frame.
|
||||||
|
*/
|
||||||
render(canvas, context)
|
render(canvas, context)
|
||||||
{
|
{
|
||||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
@ -93,6 +139,23 @@ class BoardWindow extends EventEmitter
|
||||||
this.matchWindowSize();
|
this.matchWindowSize();
|
||||||
window.addEventListener("resize", this.matchWindowSize.bind(this));
|
window.addEventListener("resize", this.matchWindowSize.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trackMousePosition() {
|
||||||
|
document.addEventListener("mousemove", (function(event) {
|
||||||
|
this.cursorPosition = {
|
||||||
|
X: event.clientX,
|
||||||
|
Y: event.clientY
|
||||||
|
};
|
||||||
|
}).bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles events generated by pan-zoom, the package that handles the
|
||||||
|
* dragging and zooming of the whiteboard.
|
||||||
|
*/
|
||||||
|
handleCanvasMovement(event) {
|
||||||
|
this.viewportState = event; // Store the viewport information for later
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BoardWindow;
|
export default BoardWindow;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,15 +1,59 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
import WebSocketStates from './Utilities/WebsocketStates';
|
||||||
|
|
||||||
class RippleLink
|
const EventEmitter = require("event-emitter-es6");
|
||||||
|
|
||||||
|
class RippleLink extends EventEmitter
|
||||||
{
|
{
|
||||||
constructor(inSocketUrl, inBoardWindow)
|
constructor(inSocketUrl, inBoardWindow)
|
||||||
{
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
this.socketUrl = inSocketUrl;
|
this.socketUrl = inSocketUrl;
|
||||||
this.boardWindow = inBoardWindow;
|
this.boardWindow = inBoardWindow;
|
||||||
this.settings = this.boardWindow.settings;
|
this.settings = this.boardWindow.settings;
|
||||||
|
|
||||||
|
// Create the websocket and commect to the server
|
||||||
this.websocket = new WebSocket(this.socketUrl, [ this.settings.WebsocketProtocol ]);
|
this.websocket = new WebSocket(this.socketUrl, [ this.settings.WebsocketProtocol ]);
|
||||||
|
this.websocket.addEventListener("open", this.handleConnection.bind(this));
|
||||||
|
this.websocket.addEventListener("message", this.handleMessage.bind(this));
|
||||||
|
this.websocket.addEventListener("close", this.handleDisconnection.bind(this));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleConnection(event) {
|
||||||
|
console.info("[ripple link] Established connection successfully.");
|
||||||
|
// Tell everyone about it
|
||||||
|
this.emit("connect", event);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDisconnection(event) {
|
||||||
|
console.error("[ripple link] Lost connection.");
|
||||||
|
this.emit("disconnect", event);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMessage(event) {
|
||||||
|
// Decode the message form the server
|
||||||
|
var message = JSON.parse(event.data);
|
||||||
|
console.debug(message);
|
||||||
|
|
||||||
|
// Pass it on to the board manager by triggering the appropriate event
|
||||||
|
this.emit(message.event, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a message object to the server.
|
||||||
|
*/
|
||||||
|
send(message) {
|
||||||
|
if(this.websocket.readyState !== WebSocketStates.ready)
|
||||||
|
{
|
||||||
|
console.error(`Attempt to send data on the RippleLine when it is not ready (state ${this.websocket.readyState})`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.websocket.send(JSON.stringify(message));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
30
Nibriboard/ClientFiles/Utilities/WebsocketStates.js
Normal file
30
Nibriboard/ClientFiles/Utilities/WebsocketStates.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants for the different readyStates that a WebSocket can be in.
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
const WebSocketStates = {
|
||||||
|
/**
|
||||||
|
* Indicates that the WebSocket is connecting to the remote server.
|
||||||
|
* @type {Number}
|
||||||
|
*/
|
||||||
|
connecting: 0,
|
||||||
|
/**
|
||||||
|
* Indicates that the WebSocket is connected to the remote server and ready to send / receive data.
|
||||||
|
* @type {Number}
|
||||||
|
*/
|
||||||
|
ready: 1,
|
||||||
|
/**
|
||||||
|
* Indicates that the WebSocket is in the process of closing the connection to the remote server.
|
||||||
|
* @type {Number}
|
||||||
|
*/
|
||||||
|
closing: 2,
|
||||||
|
/**
|
||||||
|
* Indicates that hte WebSocket is not connected to the remote server (either because the connection was closed, or dropped by the remote server).
|
||||||
|
* @type {Number}
|
||||||
|
*/
|
||||||
|
closed: 3
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WebSocketStates;
|
|
@ -5,9 +5,9 @@
|
||||||
"homepage": "https://git.starbeamrainbowlabs.com/sbrl/Nibriboard#nibriboard",
|
"homepage": "https://git.starbeamrainbowlabs.com/sbrl/Nibriboard#nibriboard",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"pan-zoom": "^2.0.0",
|
||||||
"rollupify": "^0.3.8"
|
"rollupify": "^0.3.8"
|
||||||
},
|
},
|
||||||
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"rollupify": "^0.3.8"
|
"rollupify": "^0.3.8"
|
||||||
},
|
},
|
||||||
|
|
|
@ -77,6 +77,8 @@
|
||||||
<Compile Include="Client\Messages\CursorPositionMessage.cs" />
|
<Compile Include="Client\Messages\CursorPositionMessage.cs" />
|
||||||
<Compile Include="Client\Messages\ClientStateMessage.cs" />
|
<Compile Include="Client\Messages\ClientStateMessage.cs" />
|
||||||
<Compile Include="RippleSpace\ClientState.cs" />
|
<Compile Include="RippleSpace\ClientState.cs" />
|
||||||
|
<Compile Include="Utilities\JsonConverters\RectangleConverter.cs" />
|
||||||
|
<Compile Include="Utilities\JsonConverters\PointConverter.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="ClientFiles\index.html" />
|
<EmbeddedResource Include="ClientFiles\index.html" />
|
||||||
|
@ -90,6 +92,7 @@
|
||||||
<Folder Include="ClientFiles\" />
|
<Folder Include="ClientFiles\" />
|
||||||
<Folder Include="Client\" />
|
<Folder Include="Client\" />
|
||||||
<Folder Include="Client\Messages\" />
|
<Folder Include="Client\Messages\" />
|
||||||
|
<Folder Include="Utilities\JsonConverters\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
|
|
40
Nibriboard/RippleSpace/ClientState.cs
Normal file
40
Nibriboard/RippleSpace/ClientState.cs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace RippleSpace
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a client's state at a particular point in time.
|
||||||
|
/// </summary>
|
||||||
|
public class ClientState
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The id of the client.
|
||||||
|
/// </summary>
|
||||||
|
public int Id;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The date and time at which this client state snapshot was captured.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime TimeCaptured = DateTime.Now;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name the client chose to identify themselves with.
|
||||||
|
/// </summary>
|
||||||
|
public string Name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The size and position of the client's viewport.
|
||||||
|
/// </summary>
|
||||||
|
public Rectangle Viewport = Rectangle.Empty;
|
||||||
|
/// <summary>
|
||||||
|
/// The absolute position of the client's cursor.
|
||||||
|
/// </summary>
|
||||||
|
public Point AbsCursorPosition = Point.Empty;
|
||||||
|
|
||||||
|
public ClientState()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
37
Nibriboard/Utilities/JsonConverters/PointConverter.cs
Normal file
37
Nibriboard/Utilities/JsonConverters/PointConverter.cs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Nibriboard.Utilities.JsonConverters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Deserialises objects into points from the System.Drawing namespace.
|
||||||
|
/// </summary>
|
||||||
|
public class PointConverter : JsonConverter
|
||||||
|
{
|
||||||
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
JObject jsonObject = JObject.Load(reader);
|
||||||
|
|
||||||
|
return new Point(
|
||||||
|
jsonObject.Value<int>("X"),
|
||||||
|
jsonObject.Value<int>("Y")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanConvert(Type objectType)
|
||||||
|
{
|
||||||
|
if (objectType != typeof(Rectangle))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
39
Nibriboard/Utilities/JsonConverters/RectangleConverter.cs
Normal file
39
Nibriboard/Utilities/JsonConverters/RectangleConverter.cs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Nibriboard.Utilities.JsonConverters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Deserialises objects into rectangles from the System.Drawing namespace.
|
||||||
|
/// </summary>
|
||||||
|
public class RectangleConverter : JsonConverter
|
||||||
|
{
|
||||||
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
JObject jsonObject = JObject.Load(reader);
|
||||||
|
|
||||||
|
return new Rectangle(
|
||||||
|
jsonObject.Value<int>("X"),
|
||||||
|
jsonObject.Value<int>("Y"),
|
||||||
|
jsonObject.Value<int>("Width"),
|
||||||
|
jsonObject.Value<int>("Height")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanConvert(Type objectType)
|
||||||
|
{
|
||||||
|
if (objectType != typeof(Rectangle))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue