From 5aacdba7826365453ad38938c3229d071f89beb9 Mon Sep 17 00:00:00 2001 From: GabrielTofvesson Date: Tue, 3 Apr 2018 23:51:06 +0200 Subject: [PATCH] Started implementing ListView Started standardizing event system --- Client/Client.csproj | 3 + Client/ConsoleForms/ConsoleController.cs | 30 +++--- .../Events/ISubmissionListener.cs | 15 +++ .../Events/ITextInputEventListener.cs | 15 +++ Client/ConsoleForms/Graphics/ButtonView.cs | 26 +++++ Client/ConsoleForms/Graphics/DialogView.cs | 2 +- Client/ConsoleForms/Graphics/InputView.cs | 2 +- Client/ConsoleForms/Graphics/ListView.cs | 99 +++++++++++++++++-- Client/ConsoleForms/Graphics/TextView.cs | 6 +- Client/ConsoleForms/Graphics/View.cs | 36 +++++-- Client/ConsoleForms/Helpers.cs | 27 +++++ Client/Context/WelcomeContext.cs | 18 +++- Client/Layout/Networking.xml | 30 +++--- Client/Layout/Session.xml | 6 +- Client/Layout/Setup.xml | 42 ++++---- Client/Program.cs | 9 +- 16 files changed, 281 insertions(+), 85 deletions(-) create mode 100644 Client/ConsoleForms/Events/ISubmissionListener.cs create mode 100644 Client/ConsoleForms/Events/ITextInputEventListener.cs create mode 100644 Client/ConsoleForms/Graphics/ButtonView.cs diff --git a/Client/Client.csproj b/Client/Client.csproj index 74c72a4..8db79c0 100644 --- a/Client/Client.csproj +++ b/Client/Client.csproj @@ -46,6 +46,9 @@ + + + diff --git a/Client/ConsoleForms/ConsoleController.cs b/Client/ConsoleForms/ConsoleController.cs index 7712fb7..d4af291 100644 --- a/Client/ConsoleForms/ConsoleController.cs +++ b/Client/ConsoleForms/ConsoleController.cs @@ -253,28 +253,28 @@ namespace Client.ConsoleForms List> views = new List>(); foreach (var child in doc.FirstChild.NextSibling.ChildNodes) - { if (!(child is XmlNode) || child is XmlComment) continue; - ViewData data = DoElementParse((XmlNode)child); - View load; - Type type; - try { type = Type.GetType(ns + '.' + data.Name, true); } - catch { type = Type.GetType(data.Name, true); } - - ConstructorInfo info = type.GetConstructor(new Type[] { typeof(ViewData) }); - - string id = data.attributes.ContainsKey("id") ? data.attributes["id"] : ""; - - load = (View)info.Invoke(new object[] { data }); - - views.Add(new Tuple(id, load)); - } + else views.Add(LoadView(ns, DoElementParse((XmlNode)child))); if (cacheID != null) cache[cacheID] = views; return views; } + public static Tuple LoadView(string ns, ViewData data) + { + Type type; + try { type = Type.GetType(ns + '.' + data.Name, true); } + catch { type = Type.GetType(data.Name, true); } + + ConstructorInfo info = type.GetConstructor(new Type[] { typeof(ViewData) }); + + string id = data.attributes.ContainsKey("id") ? data.attributes["id"] : ""; + data.attributes["xmlns"] = ns; + + return new Tuple(id, (View)info.Invoke(new object[] { data })); + } + public delegate void Runnable(); public void Popup(string message, long timeout, ConsoleColor borderColor = ConsoleColor.Blue, Runnable onExpire = null) { diff --git a/Client/ConsoleForms/Events/ISubmissionListener.cs b/Client/ConsoleForms/Events/ISubmissionListener.cs new file mode 100644 index 0000000..915d92f --- /dev/null +++ b/Client/ConsoleForms/Events/ISubmissionListener.cs @@ -0,0 +1,15 @@ +using Client.ConsoleForms.Graphics; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Client.ConsoleForms.Events +{ + public delegate void SubmissionEvent(View listener); + public interface ISubmissionListener + { + void SetEvent(SubmissionEvent listener); + } +} diff --git a/Client/ConsoleForms/Events/ITextInputEventListener.cs b/Client/ConsoleForms/Events/ITextInputEventListener.cs new file mode 100644 index 0000000..b05fda6 --- /dev/null +++ b/Client/ConsoleForms/Events/ITextInputEventListener.cs @@ -0,0 +1,15 @@ +using Client.ConsoleForms.Graphics; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Client.ConsoleForms.Events +{ + public delegate void TextInputEvent(ConsoleKeyInfo info, View listener); + public interface ITextInputEventListener + { + void SetEvent(TextInputEvent listener); + } +} diff --git a/Client/ConsoleForms/Graphics/ButtonView.cs b/Client/ConsoleForms/Graphics/ButtonView.cs new file mode 100644 index 0000000..40a354e --- /dev/null +++ b/Client/ConsoleForms/Graphics/ButtonView.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Client.ConsoleForms.Parameters; +using Client.ConsoleForms.Events; + +namespace Client.ConsoleForms.Graphics +{ + public class ButtonView : TextView, ISubmissionListener + { + protected SubmissionEvent evt; + + public ButtonView(ViewData parameters) : base(parameters) + { + } + + public override bool HandleKeyEvent(ConsoleController.KeyEvent info, bool inFocus) + { + return base.HandleKeyEvent(info, inFocus); + } + + public void SetEvent(SubmissionEvent listener) => evt = listener; + } +} diff --git a/Client/ConsoleForms/Graphics/DialogView.cs b/Client/ConsoleForms/Graphics/DialogView.cs index d7b17c6..dc731f1 100644 --- a/Client/ConsoleForms/Graphics/DialogView.cs +++ b/Client/ConsoleForms/Graphics/DialogView.cs @@ -45,7 +45,7 @@ namespace Client.ConsoleForms.Graphics NotSelectColor = (ConsoleColor)parameters.AttribueAsInt("unselect_color", (int)ConsoleColor.White); } - protected override void _Draw(int left, int top) + protected override void _Draw(int left, ref int top) { DrawEmptyPadding(left, ref top, padding.Top()); base.DrawContent(left, ref top); diff --git a/Client/ConsoleForms/Graphics/InputView.cs b/Client/ConsoleForms/Graphics/InputView.cs index 6804e19..3142a24 100644 --- a/Client/ConsoleForms/Graphics/InputView.cs +++ b/Client/ConsoleForms/Graphics/InputView.cs @@ -159,7 +159,7 @@ namespace Client.ConsoleForms.Graphics ContentHeight += computedSize + Inputs.Length * 2; } - protected override void _Draw(int left, int top) + protected override void _Draw(int left, ref int top) { DrawEmptyPadding(left, ref top, padding.Top()); DrawContent(left, ref top); diff --git a/Client/ConsoleForms/Graphics/ListView.cs b/Client/ConsoleForms/Graphics/ListView.cs index 1cd583a..abc7459 100644 --- a/Client/ConsoleForms/Graphics/ListView.cs +++ b/Client/ConsoleForms/Graphics/ListView.cs @@ -1,26 +1,113 @@ using Client.ConsoleForms.Parameters; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Client.ConsoleForms.Graphics { public class ListView : View { + protected readonly List> innerViews = new List>(); + public int SelectedView { get; set; } + public int ViewCount { get => innerViews.Count; } + public ConsoleColor SelectBackground { get; set; } + public ConsoleColor SelectText { get; set; } + + public override Region Occlusion => new Region(new Rectangle(0, 0, ContentWidth, ContentHeight)); public ListView(ViewData parameters) : base(parameters) { + SelectBackground = (ConsoleColor)parameters.AttribueAsInt("background_select_color", (int)ConsoleColor.Gray); + SelectText = (ConsoleColor)parameters.AttribueAsInt("text_select_color", (int)ConsoleColor.Gray); + + int maxWidth = parameters.AttribueAsInt("width", -1); + bool limited = maxWidth != -1; + + foreach (var view in parameters.nestedData.FirstOrNull(n => n.Name.Equals("Views"))?.nestedData ?? new List()) + { + // Limit content width + if (limited && view.AttribueAsInt("width") > maxWidth) view.attributes["width"] = maxWidth.ToString(); + + Tuple v = ConsoleController.LoadView(parameters.attributes["xmlns"], view); // Load the view in with standard namespace + innerViews.Add(v); + + if (!limited) maxWidth = Math.Max(v.Item2.ContentWidth, maxWidth); + + ContentHeight += v.Item2.ContentHeight + 1; + } + ++ContentHeight; + + SelectedView = 0; + + ContentWidth = maxWidth; } - public override Region Occlusion => throw new NotImplementedException(); + public View GetView(string name) => innerViews.FirstOrNull(v => v.Item1.Equals(name))?.Item2; - protected override void _Draw(int left, int top) + protected override void _Draw(int left, ref int top) { - throw new NotImplementedException(); + foreach(var view in innerViews) + { + DrawBlankLine(left, ref top); + ConsoleColor + bgHold = view.Item2.BackgroundColor, + fgHold = view.Item2.TextColor; + + if(view == innerViews[SelectedView]) + { + view.Item2.BackgroundColor = SelectBackground; + view.Item2.TextColor = SelectText; + } + + DrawView(left, ref top, view.Item2); + + if (view == innerViews[SelectedView]) + { + view.Item2.BackgroundColor = bgHold; + view.Item2.TextColor = fgHold; + } + } + DrawBlankLine(left, ref top); + } + + protected virtual void DrawView(int left, ref int top, View v) => v.Draw(left, ref top); + + protected virtual void DrawBlankLine(int left, ref int top) + { + ResetRenderColors(); + Console.SetCursorPosition(left, top++); + Console.Write(Filler(' ', ContentWidth)); + } + + public override bool HandleKeyEvent(ConsoleController.KeyEvent info, bool inFocus) + { + if (!inFocus) return false; + if (innerViews[SelectedView].Item2.HandleKeyEvent(info, inFocus)) return true; + else if (!info.ValidEvent) return false; + + // Handle navigation + switch (info.Event.Key) + { + case ConsoleKey.UpArrow: + if (SelectedView > 0) + { + info.ValidEvent = false; + --SelectedView; + return true; + } + break; + case ConsoleKey.DownArrow: + if(SelectedView < innerViews.Count - 1) + { + info.ValidEvent = false; + ++SelectedView; + return true; + } + break; + } + + return base.HandleKeyEvent(info, inFocus); } } } diff --git a/Client/ConsoleForms/Graphics/TextView.cs b/Client/ConsoleForms/Graphics/TextView.cs index 1590200..ff07dfb 100644 --- a/Client/ConsoleForms/Graphics/TextView.cs +++ b/Client/ConsoleForms/Graphics/TextView.cs @@ -40,14 +40,10 @@ namespace Client.ConsoleForms.Graphics //public char Border { get; set; } //public ConsoleColor BorderColor { get; set; } - public ConsoleColor BackgroundColor { get; set; } - public ConsoleColor TextColor { get; set; } public TextView(ViewData parameters) : base(parameters) { //BorderColor = (ConsoleColor) parameters.AttribueAsInt("border", (int)ConsoleColor.Blue); - BackgroundColor = (ConsoleColor)parameters.AttribueAsInt("color_background", (int)ConsoleColor.White); - TextColor = (ConsoleColor)parameters.AttribueAsInt("color_text", (int)ConsoleColor.Black); Border = ' '; this.text = parameters.NestedText("Text").Split(' '); @@ -151,7 +147,7 @@ namespace Client.ConsoleForms.Graphics private static bool WillSubSplit(string s, int max) => ((s.Length / max) + (s.Length % max != 0 ? 1 : 0)) > 1 || s.Contains('\n'); - protected override void _Draw(int left, int top) + protected override void _Draw(int left, ref int top) { DrawEmptyPadding(left, ref top, padding.Top()); DrawContent(left, ref top); diff --git a/Client/ConsoleForms/Graphics/View.cs b/Client/ConsoleForms/Graphics/View.cs index a8a8cd1..e6ac36a 100644 --- a/Client/ConsoleForms/Graphics/View.cs +++ b/Client/ConsoleForms/Graphics/View.cs @@ -23,6 +23,8 @@ namespace Client.ConsoleForms.Graphics public char Border { get; set; } public bool DrawBorder { get; set; } public ConsoleColor BorderColor { get; set; } + public ConsoleColor BackgroundColor { get; set; } + public ConsoleColor TextColor { get; set; } public int ContentWidth { get; protected set; } public int ContentHeight { get; protected set; } public abstract Region Occlusion { get; } @@ -30,10 +32,12 @@ namespace Client.ConsoleForms.Graphics public View(ViewData parameters) { - this.padding = new AbsolutePadding(parameters.AttribueAsInt("padding_left"), parameters.AttribueAsInt("padding_right"), parameters.AttribueAsInt("padding_top"), parameters.AttribueAsInt("padding_bottom")); - this.gravity = (Gravity)parameters.AttribueAsInt("gravity"); - this.BorderColor = (ConsoleColor)parameters.AttribueAsInt("border", (int)ConsoleColor.Blue); - this.Border = ' '; + padding = new AbsolutePadding(parameters.AttribueAsInt("padding_left"), parameters.AttribueAsInt("padding_right"), parameters.AttribueAsInt("padding_top"), parameters.AttribueAsInt("padding_bottom")); + gravity = (Gravity)parameters.AttribueAsInt("gravity"); + BorderColor = (ConsoleColor)parameters.AttribueAsInt("border", (int)ConsoleColor.Blue); + BackgroundColor = (ConsoleColor)parameters.AttribueAsInt("color_background", (int)ConsoleColor.White); + TextColor = (ConsoleColor)parameters.AttribueAsInt("color_text", (int)ConsoleColor.Black); + Border = ' '; DrawBorder = parameters.attributes.ContainsKey("border"); back_data = parameters.GetAttribute("back"); @@ -44,12 +48,20 @@ namespace Client.ConsoleForms.Graphics hCenter = !Enums.HasFlag(gravity, Gravity.TOP) && !Enums.HasFlag(gravity, Gravity.BOTTOM); } + public void ResetRenderColors() => SetRenderColors(BackgroundColor, TextColor); + public void SetRenderColors(ConsoleColor bg, ConsoleColor fg) + { + Console.BackgroundColor = bg; + Console.ForegroundColor = fg; + } + public void Draw(Tuple t) => Draw(t.Item1, t.Item2); - public void Draw(int left, int top) + public void Draw(int left, int top) => Draw(left, ref top); + public void Draw(int left, ref int top) { Dirty = false; if (DrawBorder) _DrawBorder(left, top); - _Draw(left + 1, top); + _Draw(left + 1, ref top); } public virtual void _DrawBorder(int left, int top) { @@ -67,7 +79,7 @@ namespace Client.ConsoleForms.Graphics Console.Write(Filler(Border, ContentWidth + 2)); Console.BackgroundColor = ConsoleColor.Black; } - protected abstract void _Draw(int left, int top); + protected abstract void _Draw(int left, ref int top); public virtual bool HandleKeyEvent(ConsoleController.KeyEvent info, bool inFocus) { if (back_data.Length != 0 && info.ValidEvent && inFocus && info.Event.Key == ConsoleKey.Escape) @@ -77,6 +89,16 @@ namespace Client.ConsoleForms.Graphics } return false; } + protected void DrawTopPadding(int left, ref int top) => DrawPadding(left, ref top, padding.Top()); + protected void DrawBottomPadding(int left, ref int top) => DrawPadding(left, ref top, padding.Bottom()); + private void DrawPadding(int left, ref int top, int count) + { + for (int i = 0; i < count; ++i) + { + Console.SetCursorPosition(left, top++); + Console.Write(Filler(' ', ContentWidth)); + } + } protected EventAction ParseAction(ViewData data) { bool.TryParse(data.GetAttribute("close"), out bool close); diff --git a/Client/ConsoleForms/Helpers.cs b/Client/ConsoleForms/Helpers.cs index 3ec3e88..c51f105 100644 --- a/Client/ConsoleForms/Helpers.cs +++ b/Client/ConsoleForms/Helpers.cs @@ -1,5 +1,6 @@ using Client.ConsoleForms.Parameters; using System; +using System.Collections.Generic; using System.Diagnostics; namespace Client.ConsoleForms @@ -37,6 +38,32 @@ namespace Client.ConsoleForms len += val?.InnerText.Length ?? 0; return len; } + + public static List Collect(this IEnumerable l, Predicate p, List collector = null) + { + List res = collector ?? new List(); + foreach (var t in l) + if (p(t)) + res.Add(t); + return res; + } + + public static int Matches(this IEnumerable l, Predicate p) + { + int i = 0; + foreach (var t in l) + if (p(t)) + ++i; + return i; + } + + public static T FirstOrNull(this IEnumerable l, Predicate p) + { + foreach (var t in l) + if (p(t)) + return t; + return default(T); + } } diff --git a/Client/Context/WelcomeContext.cs b/Client/Context/WelcomeContext.cs index 4a6d894..38d58e8 100644 --- a/Client/Context/WelcomeContext.cs +++ b/Client/Context/WelcomeContext.cs @@ -32,14 +32,13 @@ namespace Client bool success = true; foreach (var input in i.Inputs) - { if (input.Text.Length == 0) { success = false; input.SelectBackgroundColor = ConsoleColor.Red; input.BackgroundColor = ConsoleColor.DarkRed; } - } + if (success) { @@ -142,8 +141,21 @@ namespace Client public override void OnDestroy() { - // TODO: Save state + ((InputView)views.GetNamed("Register")).SelectedField = 0; + foreach (var v in ((InputView)views.GetNamed("Register")).Inputs) + { + v.Text = ""; + v.SelectIndex = 0; + v.RenderStart = 0; + } + ((InputView)views.GetNamed("Login")).SelectedField = 0; + foreach (var v in ((InputView)views.GetNamed("Login")).Inputs) + { + v.Text = ""; + v.SelectIndex = 0; + v.RenderStart = 0; + } // Close views foreach (var view in views) diff --git a/Client/Layout/Networking.xml b/Client/Layout/Networking.xml index e94b812..c4af0c3 100644 --- a/Client/Layout/Networking.xml +++ b/Client/Layout/Networking.xml @@ -1,7 +1,7 @@  - + - Port: Server connection configuration - + - Cancel The identity of the server could not be verified. Continue? - + - Cancel Connecting to server... - + - Ok One or more required input field is empty - + - Ok The supplied IP-address is not valid - + - Ok The supplied port is not valid - + - Ok Could not connect to server - + \ No newline at end of file diff --git a/Client/Layout/Session.xml b/Client/Layout/Session.xml index f8e3802..29acf1d 100644 --- a/Client/Layout/Session.xml +++ b/Client/Layout/Session.xml @@ -1,6 +1,6 @@  - - + Quit Login succeeded! - + \ No newline at end of file diff --git a/Client/Layout/Setup.xml b/Client/Layout/Setup.xml index 131b4ed..cba1902 100644 --- a/Client/Layout/Setup.xml +++ b/Client/Layout/Setup.xml @@ -1,8 +1,8 @@  - + - Register Welcome to the Tofvesson banking system! To continue, press [ENTER] To go back, press [ESCAPE] - + - Repeat password: Register Account - + - Registering... - + - Ok An account with this username already exists! - + - Ok The entered passwords don't match! - + - No The password you have supplied has been deemed to be weak. Are you sure you want to continue? - + - Password: Log in - + - Authenticating... - + - Ok One or more required input field is empty - + - Ok The given username or password was incorrect - + \ No newline at end of file diff --git a/Client/Program.cs b/Client/Program.cs index ae5a522..2676bf0 100644 --- a/Client/Program.cs +++ b/Client/Program.cs @@ -1,17 +1,10 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; using Client; using Client.ConsoleForms; using Client.ConsoleForms.Parameters; -using Client.Properties; using Common; -using Tofvesson.Collections; namespace ConsoleForms { @@ -61,4 +54,4 @@ namespace ConsoleForms [DllImport("msvcrt")] public static extern int _kbhit(); } -} +} \ No newline at end of file