diff --git a/QControlKit/QBrowser.cs b/QControlKit/QBrowser.cs index faa71cc..83a4b33 100644 --- a/QControlKit/QBrowser.cs +++ b/QControlKit/QBrowser.cs @@ -70,9 +70,11 @@ namespace QControlKit if (found == null) { - server.disconnect(); - servers.Remove(server); Log.Information($"[qbrowser] Lost {server.name} : {server.host} : {server.port}"); + server.disconnect(); + Log.Information($"[qbrowser] after server disconnect()"); + + servers.Remove(server); OnServerLost(server); } } @@ -121,7 +123,8 @@ namespace QControlKit public void Close() { - foreach(var server in servers) + Log.Information($"[qbrowser] Close requested"); + foreach (var server in servers) { server.disconnect(); } diff --git a/QControlKit/QClient.cs b/QControlKit/QClient.cs index 11f9dbb..5e9d0fb 100644 --- a/QControlKit/QClient.cs +++ b/QControlKit/QClient.cs @@ -52,7 +52,7 @@ namespace QControlKit public void disconnect() { - Log.Debug($"[client] disconnecting from {tcpClient.Address}"); + Log.Information($"[client] disconnecting from {tcpClient.Address}"); tcpClient.Close(); } @@ -60,7 +60,7 @@ namespace QControlKit public void sendMessage(string address, params object[] args) { tcpClient.QueueForSending(new OscMessage(address, args)); - Log.Debug($"[client] send message {address} : {args}"); + Log.Information($"[client] send message {address} : {args}"); } private void ProcessMessage(object source, MessageEventArgs args) @@ -97,7 +97,7 @@ namespace QControlKit } else { - Log.Warning($"[client] unhandled reply from cue: Type: {data.Type} value: {message.response}"); + Log.Error($"[client] unhandled reply from cue: Type: {data.Type} value: {message.response}"); } } @@ -118,7 +118,7 @@ namespace QControlKit } else { - Log.Warning($"[client] unhandled reply message: {message.address}"); + Log.Error($"[client] unhandled reply message: {message.address}"); } } else if(message.IsUpdate) { @@ -162,12 +162,12 @@ namespace QControlKit } else { - Log.Warning($"[client] unhandled update message: {message.address}"); + Log.Error($"[client] unhandled update message: {message.address}"); } } else { - Log.Warning($"[client] unhandled message: {message.address}"); + Log.Error($"[client] unhandled message: {message.address}"); } } diff --git a/QControlKit/QControlKit.csproj b/QControlKit/QControlKit.csproj index 1250793..1f52d5e 100644 --- a/QControlKit/QControlKit.csproj +++ b/QControlKit/QControlKit.csproj @@ -6,7 +6,7 @@ Joel Wetzell 1.0.0-devel true - 0.0.21 + 0.0.23 QControlKit MIT Unofficial C# port of Figure53's QLabKit.objc @@ -18,6 +18,7 @@ + @@ -29,5 +30,7 @@ + + diff --git a/QControlKit/QCue.cs b/QControlKit/QCue.cs index 05b48e5..c8ece0d 100644 --- a/QControlKit/QCue.cs +++ b/QControlKit/QCue.cs @@ -18,6 +18,7 @@ namespace QControlKit public List childCues; public Dictionary childCuesUIDMap; + public int sortIndex; public int nestLevel; @@ -43,32 +44,9 @@ namespace QControlKit { init(); this.workspace = workspace; - - //this breaks things??? - //it definitely is in the original but somehow the recursion is not working as I expect.. - //but cue population still works without this as child cues are populated later - - /*JToken children = dict[QOSCKey.Cues]; - if(children != null && children.Type == JTokenType.Array) - { - Log.Debug($"[cue] new cue being created with {children.Count()} childCues"); - foreach (var aChildDict in children) - { - string uid = (string)aChildDict[QOSCKey.UID]; - //Log.Debug($"[cue] childDict with uid: {uid} being processed"); - if (uid == null) - continue; - string name = dict[QOSCKey.UID].ToString(); - Log.Debug($"[cue] calling new cue from {name}"); - - //QCue child = new QCue(aChildDict, workspace); - - *//*childCues.Add(child); - childCuesUIDMap.Add(uid, child);*//* - } - }*/ - updatePropertiesWithDictionary(dict); + workspace.fetchBasicPropertiesForCue(this); + } public QCue(JToken dict, QWorkspace workspace, int nestLevel) @@ -76,31 +54,8 @@ namespace QControlKit init(); this.workspace = workspace; this.nestLevel = nestLevel + 1; - //this breaks things??? - //it definitely is in the original but somehow the recursion is not working as I expect.. - //but cue population still works without this as child cues are populated later - - /*JToken children = dict[QOSCKey.Cues]; - if(children != null && children.Type == JTokenType.Array) - { - Log.Debug($"[cue] new cue being created with {children.Count()} childCues"); - foreach (var aChildDict in children) - { - string uid = (string)aChildDict[QOSCKey.UID]; - //Log.Debug($"[cue] childDict with uid: {uid} being processed"); - if (uid == null) - continue; - string name = dict[QOSCKey.UID].ToString(); - Log.Debug($"[cue] calling new cue from {name}"); - - //QCue child = new QCue(aChildDict, workspace); - - *//*childCues.Add(child); - childCuesUIDMap.Add(uid, child);*//* - } - }*/ - updatePropertiesWithDictionary(dict); + workspace.fetchBasicPropertiesForCue(this); } public void init() @@ -244,7 +199,14 @@ namespace QControlKit { get { - return propertyForKey(QOSCKey.Parent).ToString(); + if(propertyForKey(QOSCKey.Parent) != null) + { + return propertyForKey(QOSCKey.Parent).ToString(); + } + else + { + return ""; + } } } public string playbackPositionID { @@ -301,7 +263,14 @@ namespace QControlKit { get { - return propertyForKey(QOSCKey.ListName).ToString(); + if(propertyForKey(QOSCKey.ListName) == null) + { + return ""; + } + else + { + return propertyForKey(QOSCKey.ListName).ToString(); + } } set { @@ -353,46 +322,39 @@ namespace QControlKit if (workspace.connectedToQLab3 || workspace.isOlderThanVersion("4.2.0")) return false; - bool overridden = (bool)propertyForKey(QOSCKey.IsOverridden); - - if (overridden) - return true; foreach (var cue in cues) { if (cue.IsOverridden) return true; } - return false; + + return getBoolForKey(QOSCKey.IsOverridden); } } public bool IsBroken { get { - if ((bool)propertyForKey(QOSCKey.IsBroken)) - return true; - foreach(var cue in cues) + foreach (var cue in cues) { if (cue.IsBroken) return true; } - return false; + + return getBoolForKey(QOSCKey.IsBroken); } } public bool IsRunning { get { - bool running = (bool)propertyForKey(QOSCKey.IsRunning); - - if (running) - return true; - foreach(var cue in cues) + foreach (var cue in cues) { if (cue.IsRunning) return true; } - return false; + + return getBoolForKey(QOSCKey.IsRunning); } } public bool IsTailingOut @@ -402,16 +364,13 @@ namespace QControlKit if (workspace.connectedToQLab3) return false; - bool tailingOut = (bool)propertyForKey(QOSCKey.IsTailingOut); - - if (tailingOut) - return true; - foreach(var cue in cues) + foreach (var cue in cues) { if (cue.IsTailingOut) return true; } - return false; + + return getBoolForKey(QOSCKey.IsTailingOut); } } public bool IsPanicking @@ -421,16 +380,13 @@ namespace QControlKit if (workspace.connectedToQLab3) return false; - bool panicking = (bool)propertyForKey(QOSCKey.IsPanicking); - - if (panicking) - return true; foreach (var cue in cues) { if (cue.IsPanicking) return true; } - return false; + + return getBoolForKey(QOSCKey.IsPanicking); } } public bool IsGroup @@ -571,6 +527,23 @@ namespace QControlKit } } + + public float actionElapsed + { + get + { + object secs = propertyForKey(QOSCKey.ActionElapsed); + if (secs != null) + { + return float.Parse(secs.ToString()); + } + else + { + return 0F; + } + } + } + public QTriggerAction secondTriggerAction { get @@ -827,6 +800,32 @@ namespace QControlKit } return null; } + + public bool getBoolForKey(string key) + { + if (propertyForKey(key) != null) + { + object prop = propertyForKey(key); + //Log.Warning($"{listName} IsBroken: {propertyForKey(QOSCKey.IsBroken).ToString()} {prop.GetType().ToString()}"); + if(prop.GetType() == typeof(Boolean)){ + return (bool)prop; + } + else if (prop.GetType() == typeof(JValue)) + { + JValue value = (JValue)prop; + return (bool)value.Value; + } + else + { + return false; + } + } + else + { + return false; + } + } + public object propertyForKey(string key) { //TODO: implerment special key checks color, surfaceName, patchDescription, @@ -996,7 +995,10 @@ namespace QControlKit #region Event Handling void OnCuePropertiesUpdated(List properties) { - Log.Debug($"[cue] <{nonEmptyName}> properties have been updated."); + if(parentID != null && parentID != "") + { + workspace.fetchBasicPropertiesForCue(workspace.cueWithID(parentID)); + } CuePropertiesUpdated?.Invoke(this, new QCuePropertiesUpdatedArgs { properties = properties }); } #endregion diff --git a/QControlKit/QMessage.cs b/QControlKit/QMessage.cs index 154b17e..ce8d9de 100644 --- a/QControlKit/QMessage.cs +++ b/QControlKit/QMessage.cs @@ -143,5 +143,21 @@ namespace QControlKit } } + override + public string ToString() + { + string stringified = ""; + + stringified += $"\nAddress: {this.address}\nArguments: \n"; + + + for(int i = 0; i"); foreach (var workspace in workspaces) { if (workspace.connected) diff --git a/QControlKit/QWorkspace.cs b/QControlKit/QWorkspace.cs index 08513dc..ca969ef 100644 --- a/QControlKit/QWorkspace.cs +++ b/QControlKit/QWorkspace.cs @@ -3,8 +3,6 @@ using Serilog; using System; using System.Collections.Generic; using System.Linq; -using System.Timers; - using QControlKit.Events; using QControlKit.Constants; @@ -217,15 +215,13 @@ namespace QControlKit return; stopReceivingUpdates(); disconnectFromWorkspace(); - - connected = false; client.disconnect(); - + connected = false; } public void temporarilyDisconnect() { - //TODO + //TODO what does this mean? } #endregion @@ -245,6 +241,7 @@ namespace QControlKit #region Workspace Methods public void disconnectFromWorkspace() { client.sendMessage($"{workspacePrefix}/disconnect"); } public void startReceivingUpdates() { client.sendMessage($"{workspacePrefix}/updates", 1); } + public void getPropertyForActive(string prop) { client.sendMessage($"{workspacePrefix}/cue/active/{prop}"); } public void stopReceivingUpdates() { client.sendMessage($"{workspacePrefix}/updates", 0); } public void enableAlwaysReply() { client.sendMessage($"{workspacePrefix}/alwaysReply", 1); } public void disableAlwaysReply() { client.sendMessage($"{workspacePrefix}/alwaysReply", 0); } @@ -320,18 +317,48 @@ namespace QControlKit public void fetchDefaultPropertiesForCue(QCue cue) { - List keys = new List { QOSCKey.UID, QOSCKey.Number, QOSCKey.Name, - QOSCKey.ListName, QOSCKey.Type, QOSCKey.ColorName, - QOSCKey.Flagged, QOSCKey.Armed, QOSCKey.Notes }; + List keys = new List { + QOSCKey.UID, + QOSCKey.Number, + QOSCKey.Name, + QOSCKey.ListName, + QOSCKey.Type, + QOSCKey.ColorName, + QOSCKey.Flagged, + QOSCKey.Armed, + QOSCKey.IsRunning, + QOSCKey.IsBroken, + QOSCKey.Parent, + QOSCKey.Notes + }; + fetchPropertiesForCue(cue, keys.ToArray(), false); } public void fetchBasicPropertiesForCue(QCue cue) { - List keys = new List { QOSCKey.Name, QOSCKey.ListName, QOSCKey.Number, QOSCKey.FileTarget, QOSCKey.CueTargetNumber, - QOSCKey.HasFileTargets, QOSCKey.HasCueTargets, QOSCKey.Armed, QOSCKey.ColorName, - QOSCKey.ContinueMode, QOSCKey.Flagged, QOSCKey.PreWait, QOSCKey.PostWait, - QOSCKey.Duration, QOSCKey.AllowsEditingDuration, QOSCKey.Notes }; + List keys = new List { + QOSCKey.Name, + QOSCKey.ListName, + QOSCKey.Number, + QOSCKey.FileTarget, + QOSCKey.CueTargetNumber, + QOSCKey.HasFileTargets, + QOSCKey.HasCueTargets, + QOSCKey.Armed, + QOSCKey.ColorName, + QOSCKey.ContinueMode, + QOSCKey.Flagged, + QOSCKey.PreWait, + QOSCKey.PostWait, + QOSCKey.Duration, + QOSCKey.AllowsEditingDuration, + QOSCKey.IsBroken, + QOSCKey.IsRunning, + QOSCKey.IsLoaded, + QOSCKey.Notes + }; + if (cue.IsGroup) keys.Add(QOSCKey.Children); fetchPropertiesForCue(cue, keys.ToArray(), false); @@ -502,7 +529,7 @@ namespace QControlKit cueList.setProperty(args.cueID, QOSCKey.PlaybackPositionId, false); } - Log.Debug($"[workspace] cue list <{args.cueListID}> playback position changed to <{args.cueID}>"); + Log.Information($"[workspace] cue list <{args.cueListID}> playback position changed to <{args.cueID}>"); CueListChangedPlaybackPosition?.Invoke(this, new QCueListChangedPlaybackPositionArgs { cueListID = args.cueListID, cueID = args.cueID }); } diff --git a/QControlKit/SharpOSC/TCPClient.cs b/QControlKit/SharpOSC/TCPClient.cs index 23119bd..2f430bf 100644 --- a/QControlKit/SharpOSC/TCPClient.cs +++ b/QControlKit/SharpOSC/TCPClient.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Net.Sockets; using System.Threading; using Serilog; +using SuperSimpleTcp; namespace SharpOSC { @@ -38,32 +36,64 @@ namespace SharpOSC private Thread sendThread; string _address; - TcpClient client; byte END = 0xc0; byte ESC = 0xdb; byte ESC_END = 0xDC; byte ESC_ESC = 0xDD; + SimpleTcpClient simpleClient; + public TCPClient(string address, int port) { _port = port; _address = address; + + simpleClient = new SimpleTcpClient(address, port); + simpleClient.Events.Connected += Connected; + simpleClient.Events.Disconnected += Disconnected; + simpleClient.Events.DataReceived += DataReceived; + simpleClient.Logger = Log.Debug; + } + + public static string ByteArrayToString(byte[] ba) + { + return BitConverter.ToString(ba).Replace("-", ""); + } + + private void DataReceived(object sender, DataReceivedEventArgs e) + { + List messages = SlipDecode(e.Data); + for(int i = 0; i < messages.Count; i++) + { + OscPacket packet = OscPacket.GetPacket(messages[i]); + OscMessage responseMessage = (OscMessage)packet; + OnMessageReceived(responseMessage); + } + } + + private void Disconnected(object sender, ConnectionEventArgs e) + { + Log.Debug("[tcpClient] connection to " + e.IpPort + " was disconnected"); + } + + private void Connected(object sender, ConnectionEventArgs e) + { + Log.Debug($"[tcpclient] connected to <{e.IpPort}>"); } public bool Connect() { try { - client = new TcpClient(Address, Port); - receivingThread = new Thread(ReceiveLoop); - receivingThread.Start(); + simpleClient.Connect(); sendThread = new Thread(SendLoop); + sendThread.IsBackground = true; sendThread.Start(); - Log.Debug($"[tcpclient] connected to <{Address}:{Port}>"); + return true; } catch (Exception e) @@ -71,7 +101,6 @@ namespace SharpOSC Log.Error(e.Message); return false; } - } public void QueueForSending(OscPacket packet) @@ -81,7 +110,7 @@ namespace SharpOSC private void SendLoop() { - while (client != null && client.Connected) + while (simpleClient != null && simpleClient.IsConnected) { if(SendQueue.Count > 0) { @@ -94,8 +123,7 @@ namespace SharpOSC public void Send(byte[] message) { byte[] slipData = SlipEncode(message); - NetworkStream netStream = client.GetStream(); - netStream.Write(slipData.ToArray(), 0, slipData.ToArray().Length); + simpleClient.Send(slipData); } public void Send(OscPacket packet) @@ -108,57 +136,10 @@ namespace SharpOSC { get { - if (client == null) + if (simpleClient == null) return false; else - return client.Connected; - } - } - - public void ReceiveLoop() - { - while (client != null && client.Connected) - { - Receive(); - } - //Log.Debug("[tcpclient] - ReceiveLoop has exited"); - } - - public void Receive() - { - Random random = new Random(); - int num = random.Next(1000); - try - { - NetworkStream netStream = client.GetStream(); - netStream.ReadTimeout = 250; - List responseData = new List(); - if (netStream.CanRead) - { - //var watch = System.Diagnostics.Stopwatch.StartNew(); - byte[] buffer = new byte[256]; - - int bytesRead = 0; - int reads = 0; - do - { - bytesRead = netStream.Read(buffer, 0, buffer.Length); - responseData.AddRange(buffer); - reads += 1; - Thread.Sleep(1); - //Log.Debug("Thread " + num + ": Bytes read: " + bytesRead + " - " + Encoding.UTF8.GetString(buffer)); - } while (netStream.DataAvailable); - - //Console.WriteLine("Raw TCP In: " + System.Text.Encoding.UTF8.GetString(responseData.ToArray())); - OscPacket packet = OscPacket.GetPacket(responseData.Skip(1).ToArray()); - OscMessage responseMessage = (OscMessage)packet; - //watch.Stop(); - //Console.WriteLine($"TCPCLient - message receive took {watch.ElapsedMilliseconds}ms and {reads} reads"); - OnMessageReceived(responseMessage); - } - } catch(Exception e) - { - //Console.WriteLine("TCPSENDER - Receive Exception: " + e.ToString()); + return simpleClient.IsConnected; } } @@ -190,15 +171,35 @@ namespace SharpOSC return slipData.ToArray(); } + public List SlipDecode(byte[] data) + { + List messages = new List(); + + List buffer = new List(); + for(int i = 0; i < data.Length; i++) + { + if(data[i] == END && buffer.Count > 0) + { + messages.Add(buffer.ToArray()); + buffer.Clear(); + } + else if (data[i] != END) + { + buffer.Add(data[i]); + } + } + return messages; + } + public void Close() { - if (client != null) + Log.Information("[tcpclient] Close() was requested"); + if (simpleClient != null) { - if (client.Connected) + if (simpleClient.IsConnected) { - Log.Debug($"[tcpClient] closing connection to {Address}"); - client.GetStream().Close(); - client.Close(); + Log.Information($"[tcpClient] closing connection to {Address}"); + simpleClient.Disconnect(); } } } diff --git a/QControlKitXamDemo/QControlKitXamDemo.Android/Assets/materialdesignicons-webfont.ttf b/QControlKitXamDemo/QControlKitXamDemo.Android/Assets/materialdesignicons-webfont.ttf new file mode 100644 index 0000000..522dc31 Binary files /dev/null and b/QControlKitXamDemo/QControlKitXamDemo.Android/Assets/materialdesignicons-webfont.ttf differ diff --git a/QControlKitXamDemo/QControlKitXamDemo.Android/QControlKitXamDemo.Android.csproj b/QControlKitXamDemo/QControlKitXamDemo.Android/QControlKitXamDemo.Android.csproj index bcdd620..9a5d8c1 100644 --- a/QControlKitXamDemo/QControlKitXamDemo.Android/QControlKitXamDemo.Android.csproj +++ b/QControlKitXamDemo/QControlKitXamDemo.Android/QControlKitXamDemo.Android.csproj @@ -101,6 +101,9 @@ QControlKitXamDemo + + + diff --git a/QControlKitXamDemo/QControlKitXamDemo.iOS/AppDelegate.cs b/QControlKitXamDemo/QControlKitXamDemo.iOS/AppDelegate.cs index fc3ccfd..7b9b1e7 100644 --- a/QControlKitXamDemo/QControlKitXamDemo.iOS/AppDelegate.cs +++ b/QControlKitXamDemo/QControlKitXamDemo.iOS/AppDelegate.cs @@ -23,7 +23,7 @@ namespace QControlKitXamDemo.iOS public override bool FinishedLaunching(UIApplication app, NSDictionary options) { global::Xamarin.Forms.Forms.Init(); - Log.Logger = new LoggerConfiguration().WriteTo.NSLog().MinimumLevel.Debug().CreateLogger(); + Log.Logger = new LoggerConfiguration().WriteTo.NSLog().MinimumLevel.Information().CreateLogger(); LoadApplication(new App()); return base.FinishedLaunching(app, options); diff --git a/QControlKitXamDemo/QControlKitXamDemo.iOS/Info.plist b/QControlKitXamDemo/QControlKitXamDemo.iOS/Info.plist index b614303..6e76ccc 100644 --- a/QControlKitXamDemo/QControlKitXamDemo.iOS/Info.plist +++ b/QControlKitXamDemo/QControlKitXamDemo.iOS/Info.plist @@ -40,5 +40,9 @@ NSLocalNetworkUsageDescription Scan network for QLab instances + UIAppFonts + + materialdesignicons-webfont.ttf + diff --git a/QControlKitXamDemo/QControlKitXamDemo.iOS/QControlKitXamDemo.iOS.csproj b/QControlKitXamDemo/QControlKitXamDemo.iOS/QControlKitXamDemo.iOS.csproj index 3fe068e..fe3246f 100644 --- a/QControlKitXamDemo/QControlKitXamDemo.iOS/QControlKitXamDemo.iOS.csproj +++ b/QControlKitXamDemo/QControlKitXamDemo.iOS/QControlKitXamDemo.iOS.csproj @@ -133,6 +133,9 @@ + + 2.6.0 + @@ -141,4 +144,7 @@ QControlKitXamDemo + + + \ No newline at end of file diff --git a/QControlKitXamDemo/QControlKitXamDemo.iOS/Resources/materialdesignicons-webfont.ttf b/QControlKitXamDemo/QControlKitXamDemo.iOS/Resources/materialdesignicons-webfont.ttf new file mode 100644 index 0000000..522dc31 Binary files /dev/null and b/QControlKitXamDemo/QControlKitXamDemo.iOS/Resources/materialdesignicons-webfont.ttf differ diff --git a/QControlKitXamDemo/QControlKitXamDemo/App.xaml b/QControlKitXamDemo/QControlKitXamDemo/App.xaml index 6dfba81..8f6f0ac 100644 --- a/QControlKitXamDemo/QControlKitXamDemo/App.xaml +++ b/QControlKitXamDemo/QControlKitXamDemo/App.xaml @@ -6,6 +6,52 @@ mc:Ignorable="d" x:Class="QControlKitXamDemo.App"> + + Material Design Icons + materialdesignicons-webfont.ttf#Material Design Icons + + + + + + + 4 + 8 + 16 + + + + + + + + + + + + \ No newline at end of file diff --git a/QControlKitXamDemo/QControlKitXamDemo/App.xaml.cs b/QControlKitXamDemo/QControlKitXamDemo/App.xaml.cs index 2804dc9..b86146e 100644 --- a/QControlKitXamDemo/QControlKitXamDemo/App.xaml.cs +++ b/QControlKitXamDemo/QControlKitXamDemo/App.xaml.cs @@ -9,6 +9,8 @@ namespace QControlKitXamDemo InitializeComponent(); MainPage = new NavigationPage(new MainPage()); + Current.RequestedThemeChanged += Current_RequestedThemeChanged; + SetAppResources(); } protected override void OnStart() @@ -22,5 +24,80 @@ namespace QControlKitXamDemo protected override void OnResume() { } + + private void Current_RequestedThemeChanged(object sender, AppThemeChangedEventArgs e) + { + SetAppResources(); + } + + private void SetAppResources() + { + + switch (Current.RequestedTheme) + { + case OSAppTheme.Light: + SetLightResources(); + break; + case OSAppTheme.Dark: + SetDarkResources(); + break; + case OSAppTheme.Unspecified: + //Default to Light + SetLightResources(); + break; + default: + SetLightResources(); + break; + } + } + + private void SetDarkResources() + { + + Resources["NavigationBarColor"] = Color.FromHex("#3f6dab"); + Resources["PageBackgroundColor"] = Color.FromHex("#525252"); + + + Resources["PrimaryColor"] = Color.WhiteSmoke; + Resources["SecondaryColor"] = Color.White; + Resources["PrimaryTextColor"] = Color.LightGray; + Resources["SecondaryTextColor"] = Color.White; + Resources["TertiaryTextColor"] = Color.Gray; + Resources["TransparentColor"] = Color.Transparent; + + Resources["IconTextColor"] = Color.WhiteSmoke; + + Resources["ListViewBackgroundColor"] = Color.FromHex("#363636"); + + Resources["ServerCellBackgroundColor"] = Color.FromHex("#363636"); + Resources["WorkspaceCellBackgroundColor"] = Color.FromHex("#737373"); + + Resources["SelectedCueCellBackgroundColor"] = Color.FromHex("#737373"); + + } + + private void SetLightResources() + { + Resources["NavigationBarColor"] = Color.FromHex("#3089ff"); + Resources["PageBackgroundColor"] = Color.LightGray; + + + Resources["PrimaryColor"] = Color.WhiteSmoke; + Resources["SecondaryColor"] = Color.White; + Resources["PrimaryTextColor"] = Color.Black; + Resources["SecondaryTextColor"] = Color.White; + Resources["TertiaryTextColor"] = Color.Gray; + Resources["TransparentColor"] = Color.Transparent; + + Resources["IconTextColor"] = Color.Black; + + Resources["ListViewBackgroundColor"] = Color.White; + + Resources["ServerCellBackgroundColor"] = Color.White; + Resources["WorkspaceCellBackgroundColor"] = Color.FromHex("#D8D8D8"); + + Resources["SelectedCueCellBackgroundColor"] = Color.FromHex("#D8D8D8"); + + } } } diff --git a/QControlKitXamDemo/QControlKitXamDemo/CueListPage.xaml.cs b/QControlKitXamDemo/QControlKitXamDemo/CueListPage.xaml.cs index 7b3e377..89fc60f 100644 --- a/QControlKitXamDemo/QControlKitXamDemo/CueListPage.xaml.cs +++ b/QControlKitXamDemo/QControlKitXamDemo/CueListPage.xaml.cs @@ -95,7 +95,7 @@ namespace QControlKitXamDemo HorizontalOptions = LayoutOptions.StartAndExpand, VerticalTextAlignment = TextAlignment.Center, }; - cueLabel.SetBinding(Label.TextProperty, "name", BindingMode.OneWay); + cueLabel.SetBinding(Label.TextProperty, "status", BindingMode.OneWay); var cueBackground = new Frame { BindingContext = qCueViewModel, diff --git a/QControlKitXamDemo/QControlKitXamDemo/MainPage.xaml b/QControlKitXamDemo/QControlKitXamDemo/MainPage.xaml index a258309..6e65bad 100644 --- a/QControlKitXamDemo/QControlKitXamDemo/MainPage.xaml +++ b/QControlKitXamDemo/QControlKitXamDemo/MainPage.xaml @@ -3,14 +3,121 @@ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:d="http://xamarin.com/schemas/2014/forms/design" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - x:Class="QControlKitXamDemo.MainPage"> + xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core" + mc:Ignorable="d" + x:Class="QControlKitXamDemo.MainPage" + ios:Page.UseSafeArea="true" + BackgroundColor="{DynamicResource PageBackgroundColor}"> + - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/QControlKitXamDemo/QControlKitXamDemo/MainPage.xaml.cs b/QControlKitXamDemo/QControlKitXamDemo/MainPage.xaml.cs index 798e341..cecd380 100644 --- a/QControlKitXamDemo/QControlKitXamDemo/MainPage.xaml.cs +++ b/QControlKitXamDemo/QControlKitXamDemo/MainPage.xaml.cs @@ -2,150 +2,71 @@ using System.ComponentModel; using Xamarin.Forms; using QControlKit; -using System.Collections.ObjectModel; -using System.Collections.Generic; -using System.Linq; -using QControlKit.Events; -using System.Threading.Tasks; using Serilog; +using QControlKitXamDemo.ViewModels; +using Acr.UserDialogs; namespace QControlKitXamDemo { - // Learn more about making custom code visible in the Xamarin.Forms previewer - // by visiting https://aka.ms/xamarinforms-previewer - [DesignTimeVisible(false)] public partial class MainPage : ContentPage { - public ObservableCollection servers = new ObservableCollection(); + private QBrowserViewModel qBrowserViewModel; public MainPage() { InitializeComponent(); - serverListView.ItemsSource = servers; - QBrowser browser = new QBrowser(); - browser.ServerLost += Browser_ServerLost; - browser.ServerFound += Browser_ServerFound; - serverListView.ItemSelected += ServerListView_ItemSelected; - Device.StartTimer(TimeSpan.FromSeconds(5), () => - { - Task.Run(async () => - { - Device.BeginInvokeOnMainThread(() => - { - browser.ProbeForQLabInstances(); - }); - - }); - return true; - }); + qBrowserViewModel = new QBrowserViewModel(new QBrowser()); + serverListView.BindingContext = qBrowserViewModel; + serverListView.ItemSelected += QWorkspaceSelected; } - private void Browser_ServerFound(object source, QServerFoundArgs args) + async void QWorkspaceSelected(object sender, SelectedItemChangedEventArgs e) { - List serverGroups = servers.ToList(); - - bool serverFound = false; - foreach (var serverGroup in serverGroups) + if (e.SelectedItem == null) { - - if (serverGroup.host == args.server.host) - { - serverFound = true; - - } + return; } - if (!serverFound) - { - Device.BeginInvokeOnMainThread(() => - { - ServerGroup serverGroup = new ServerGroup(args.server.name); - args.server.WorkspaceAdded += Server_WorkspaceAdded; - args.server.WorkspaceRemoved += Server_WorkspaceRemoved; - serverGroup.host = args.server.host; - servers.Add(serverGroup); - }); - } - } + qBrowserViewModel.autoUpdate = false; + QWorkspace selectedWorkspace = (e.SelectedItem as QWorkspaceViewModel).workspace; + Log.Debug($"[demo] workspace: {selectedWorkspace.nameWithoutPathExtension} has been selected"); + ((ListView)sender).SelectedItem = null; - private void Server_WorkspaceRemoved(object source, QServerWorkspaceChangedArgs args) - { - foreach (var server in servers) + if (selectedWorkspace.hasPasscode) { - if (server.host == args.server.host) + UserDialogs.Instance.Prompt(new PromptConfig { - Device.BeginInvokeOnMainThread(() => + InputType = InputType.Number, + MaxLength = 4, + Title = "Workspace Requires Passcode", + OkText = "Connect", + IsCancellable = true, + OnTextChanged = args => { - server.Remove(args.workspace); - }); - } - } - } - - private void Server_WorkspaceAdded(object source, QServerWorkspaceChangedArgs args) - { - foreach(var server in servers) - { - if(server.host == args.server.host) - { - Device.BeginInvokeOnMainThread(() => + args.IsValid = args.Value != null && !args.Value.Equals("") && args.Value.Length == 4; + }, + OnAction = async (resp) => { - server.Add(args.workspace); - }); - } - } - } - - private void Browser_ServerLost(object source, QServerLostArgs args) - { - - List serverGroups = servers.ToList(); - foreach(var serverGroup in serverGroups) - { - if(serverGroup.host == args.server.host) - { - Device.BeginInvokeOnMainThread(() => - { - servers.Remove(serverGroup); - }); - } - } - } - - async void ServerListView_ItemSelected(object sender, SelectedItemChangedEventArgs e) - { - if(e.SelectedItem != null) - { - QWorkspace selectedWorkspace = e.SelectedItem as QWorkspace; - Log.Debug($"[demo] Workspace <{selectedWorkspace.nameWithoutPathExtension}> has been selected"); - if (selectedWorkspace.hasPasscode) - { - string passcode = await DisplayPromptAsync("Workspace has passcode", "Enter Passcode", maxLength: 4, keyboard: Keyboard.Numeric); - if (passcode != null) - { - - await Navigation.PushAsync(new CueListPage(selectedWorkspace, passcode)); + if (resp.Ok) + { + await Navigation.PushAsync(new CueListPage(selectedWorkspace, resp.Value)); + } } - } - else - { - await Navigation.PushAsync(new CueListPage(selectedWorkspace)); - } - serverListView.SelectedItem = null; + }); + } + else + { + await Navigation.PushAsync(new CueListPage(selectedWorkspace)); } } - } - public class ServerGroup : ObservableCollection{ - public string name { get; set; } - public string host { get; set; } - public ServerGroup(string name) + + protected override void OnAppearing() { - this.name = name; + base.OnAppearing(); + qBrowserViewModel.autoUpdate = true; } - - } -} +} \ No newline at end of file diff --git a/QControlKitXamDemo/QControlKitXamDemo/QControlKitXamDemo.csproj b/QControlKitXamDemo/QControlKitXamDemo/QControlKitXamDemo.csproj index ffec7a3..fd63104 100644 --- a/QControlKitXamDemo/QControlKitXamDemo/QControlKitXamDemo.csproj +++ b/QControlKitXamDemo/QControlKitXamDemo/QControlKitXamDemo.csproj @@ -14,6 +14,7 @@ + @@ -28,4 +29,7 @@ + + + \ No newline at end of file diff --git a/QControlKitXamDemo/QControlKitXamDemo/ViewModels/QBrowserViewModel.cs b/QControlKitXamDemo/QControlKitXamDemo/ViewModels/QBrowserViewModel.cs new file mode 100644 index 0000000..e5785ad --- /dev/null +++ b/QControlKitXamDemo/QControlKitXamDemo/ViewModels/QBrowserViewModel.cs @@ -0,0 +1,93 @@ +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using Xamarin.Forms; + +using QControlKit; +using QControlKit.Events; +using System; +using System.Threading.Tasks; +using Serilog; + +namespace QControlKitXamDemo.ViewModels +{ + public class QBrowserViewModel : INotifyPropertyChanged + { + QBrowser browser; + + public event PropertyChangedEventHandler PropertyChanged; + public bool autoUpdate = false; + public ObservableCollection ServersGrouped { get; set; } + public QBrowserViewModel(QBrowser browser) + { + this.browser = browser; + ServersGrouped = new ObservableCollection(); + + this.browser.ServerFound += OnServerFound; + this.browser.ServerLost += OnServerLost; + Device.StartTimer(TimeSpan.FromSeconds(4), () => + { + Task.Run(() => + { + if (autoUpdate) + { + Device.BeginInvokeOnMainThread(() => + { + Log.Debug("[QBrowserViewModel] QBrowser probe triggered"); + this.browser.ProbeForQLabInstances(); + }); + } + }); + return true; + }); + + } + + private void OnServerFound(object source, QServerFoundArgs args) + { + Device.BeginInvokeOnMainThread(() => + { + Log.Debug($"[QBrowserViewModel] adding server: {args.server.description}"); + ServersGrouped.Add(new QServerViewModel(args.server)); + }); + } + + + private void OnServerLost(object source, QServerLostArgs args) + { + QServerViewModel serverToRemove = null; + + foreach (QServerViewModel serverGroup in ServersGrouped) + { + if (serverGroup.host.Equals(args.server.host)) + { + serverToRemove = serverGroup; + break; + } + } + + if (serverToRemove != null) + { + Device.BeginInvokeOnMainThread(() => + { + Log.Debug($"[QBrowserViewModel] removing server: {args.server.description}"); + ServersGrouped.Remove(serverToRemove); + }); + } + } + + void OnPropertyChanged([CallerMemberName] string name = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + } + + public void InitiateScan() + { + if (!autoUpdate) + { + Log.Debug("[QBrowserViewModel] Manual Scan Initiated"); + browser.ProbeForQLabInstances(); + } + } + } +} diff --git a/QControlKitXamDemo/QControlKitXamDemo/ViewModels/QCueViewModel.cs b/QControlKitXamDemo/QControlKitXamDemo/ViewModels/QCueViewModel.cs index 05a3b16..c73c51d 100644 --- a/QControlKitXamDemo/QControlKitXamDemo/ViewModels/QCueViewModel.cs +++ b/QControlKitXamDemo/QControlKitXamDemo/ViewModels/QCueViewModel.cs @@ -5,6 +5,7 @@ using Xamarin.Forms; using Serilog; using QControlKit.Events; using QControlKit.Constants; +using System; namespace QControlKitXamDemo.ViewModels { @@ -26,13 +27,19 @@ namespace QControlKitXamDemo.ViewModels { foreach(var property in args.properties) { - Log.Debug($"[cueviewmodel] property <{property}> has been updated."); + Log.Debug($"[cueviewmodel] property <{property}> has been updated for cue {name}."); if (property.Equals(QOSCKey.Name) || property.Equals(QOSCKey.ListName)) { OnPropertyChanged("name"); }else if (property.Equals(QOSCKey.ColorName)) { OnPropertyChanged("color"); + }else if (property.Equals(QOSCKey.ActionElapsed)) + { + OnPropertyChanged("actionElapsed"); + }else if (property.Equals(QOSCKey.IsBroken) || property.Equals(QOSCKey.IsRunning)) + { + OnPropertyChanged("status"); } } } @@ -84,6 +91,34 @@ namespace QControlKitXamDemo.ViewModels } } + public string actionElapsed + { + get + { + TimeSpan time = TimeSpan.FromSeconds(cue.actionElapsed); + + return time.ToString(@"mm\:ss\:ff"); + } + } + + public string status + { + get + { + if (cue.IsRunning) + { + return "running"; + }else if (cue.IsBroken) + { + return "broken"; + } + else + { + return " "; + } + } + } + public bool IsSelected { get { diff --git a/QControlKitXamDemo/QControlKitXamDemo/ViewModels/QServerViewModel.cs b/QControlKitXamDemo/QControlKitXamDemo/ViewModels/QServerViewModel.cs new file mode 100644 index 0000000..1e83cd4 --- /dev/null +++ b/QControlKitXamDemo/QControlKitXamDemo/ViewModels/QServerViewModel.cs @@ -0,0 +1,87 @@ +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using Xamarin.Forms; + +using QControlKit; +using QControlKit.Events; + +namespace QControlKitXamDemo.ViewModels +{ + public class QServerViewModel : ObservableCollection, INotifyPropertyChanged + { + QServer server; + public new event PropertyChangedEventHandler PropertyChanged; + public string name + { + get => server.name; + set => server.name = value; + } + + public string host => server.host; + + public string version => server.version == null ? "" : $"v{server.version}"; + + public string GroupName => $"{name} ({host})"; + + public QServerViewModel(QServer server) + { + this.server = server; + name = this.server.name; + foreach (QWorkspace workspace in this.server.workspaces) + { + Device.BeginInvokeOnMainThread(() => + { + Add(new QWorkspaceViewModel(workspace)); + }); + } + this.server.WorkspaceAdded += Server_WorkspaceAdded; + this.server.WorkspaceRemoved += Server_WorkspaceRemoved; + this.server.ServerUpdated += OnServerUpdated; + } + + private void Server_WorkspaceRemoved(object source, QServerWorkspaceChangedArgs args) + { + QWorkspaceViewModel workspaceToRemove = null; + + foreach (QWorkspaceViewModel workspaceViewModel in this) + { + if (workspaceViewModel.workspace.uniqueID == args.workspace.uniqueID) + { + workspaceToRemove = workspaceViewModel; + break; + } + } + if (workspaceToRemove != null) + { + Device.BeginInvokeOnMainThread(() => + { + Remove(workspaceToRemove); + }); + } + + } + + private void Server_WorkspaceAdded(object source, QServerWorkspaceChangedArgs args) + { + QWorkspaceViewModel workspaceViewModel = new QWorkspaceViewModel(args.workspace); + Device.BeginInvokeOnMainThread(() => + { + Add(workspaceViewModel); + }); + } + + + + private void OnServerUpdated(object source, QServerUpdatedArgs args) + { + OnPropertyChanged("GroupName"); + OnPropertyChanged("version"); + } + + void OnPropertyChanged([CallerMemberName] string name = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + } + } +} diff --git a/QControlKitXamDemo/QControlKitXamDemo/ViewModels/QWorkspaceViewModel.cs b/QControlKitXamDemo/QControlKitXamDemo/ViewModels/QWorkspaceViewModel.cs new file mode 100644 index 0000000..3a893b3 --- /dev/null +++ b/QControlKitXamDemo/QControlKitXamDemo/ViewModels/QWorkspaceViewModel.cs @@ -0,0 +1,17 @@ +using QControlKit; + +namespace QControlKitXamDemo.ViewModels +{ + public class QWorkspaceViewModel + { + public QWorkspace workspace; + public QWorkspaceViewModel(QWorkspace workspace) + { + this.workspace = workspace; + } + + public string Name => workspace.name; + + public bool HasPasscode => workspace.hasPasscode; + } +}