From cc71c8d8e655ffc084d361970b4b8fe32b71e2df Mon Sep 17 00:00:00 2001 From: frsaba Date: Sat, 5 Nov 2022 16:59:12 +0100 Subject: [PATCH 01/13] TodoItems, basic treeview --- W8G05A/SpectreTodo/Program.cs | 14 ++++++ W8G05A/SpectreTodo/SpectreTodo.csproj | 14 ++++++ W8G05A/SpectreTodo/TodoItem.cs | 54 +++++++++++++++++++++ W8G05A/SpectreTodo/Views/EditorView.cs | 12 +++++ W8G05A/SpectreTodo/Views/TaskView.cs | 47 ++++++++++++++++++ W8G05A/TUITodo/Program.cs | 62 ++++++++++++++++++++++++ W8G05A/TUITodo/TUITodo.csproj | 14 ++++++ W8G05A/TUITodo/TodoItem.cs | 65 +++++++++++++++++++++++++ W8G05A/TUITodo/Views/EditorView.cs | 13 +++++ W8G05A/TUITodo/Views/TaskListView.cs | 67 ++++++++++++++++++++++++++ hazifeladatok.sln | 17 +++++++ 11 files changed, 379 insertions(+) create mode 100644 W8G05A/SpectreTodo/Program.cs create mode 100644 W8G05A/SpectreTodo/SpectreTodo.csproj create mode 100644 W8G05A/SpectreTodo/TodoItem.cs create mode 100644 W8G05A/SpectreTodo/Views/EditorView.cs create mode 100644 W8G05A/SpectreTodo/Views/TaskView.cs create mode 100644 W8G05A/TUITodo/Program.cs create mode 100644 W8G05A/TUITodo/TUITodo.csproj create mode 100644 W8G05A/TUITodo/TodoItem.cs create mode 100644 W8G05A/TUITodo/Views/EditorView.cs create mode 100644 W8G05A/TUITodo/Views/TaskListView.cs diff --git a/W8G05A/SpectreTodo/Program.cs b/W8G05A/SpectreTodo/Program.cs new file mode 100644 index 0000000..dbe1a3a --- /dev/null +++ b/W8G05A/SpectreTodo/Program.cs @@ -0,0 +1,14 @@ +// See https://aka.ms/new-console-template for more information +using BallerTODO.Views; +using Spectre.Console; + +Console.WriteLine("Hello, World!"); + +string[] tasks = { "" }; + +//var panel = new Panel("Hello World\nIs this a problem?"); +//panel.Header = new PanelHeader("Some text"); +//panel.Border = BoxBorder.Rounded; +//AnsiConsole.Write(panel); + +await TaskView.Draw(); \ No newline at end of file diff --git a/W8G05A/SpectreTodo/SpectreTodo.csproj b/W8G05A/SpectreTodo/SpectreTodo.csproj new file mode 100644 index 0000000..731f95d --- /dev/null +++ b/W8G05A/SpectreTodo/SpectreTodo.csproj @@ -0,0 +1,14 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + diff --git a/W8G05A/SpectreTodo/TodoItem.cs b/W8G05A/SpectreTodo/TodoItem.cs new file mode 100644 index 0000000..a9b8172 --- /dev/null +++ b/W8G05A/SpectreTodo/TodoItem.cs @@ -0,0 +1,54 @@ +using Spectre.Console; +using Spectre.Console.Rendering; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BallerTODO +{ + + + internal record class TodoItem + { + private const string Unchecked = "O"; + private const string Checked = "X"; + + bool _done; + public bool Done + { + get { return _done; } + set + { + _done = value; + Subtasks.ForEach(t => t.Done = value); + } + } + string name; + string description; + + List subtasks; + + public TodoItem(string name, string description = "") : this(name, description, new List()) { } + + public TodoItem(string name, string description, List subtasks) + { + this._done = false; + this.name = name; + this.description = description; + + this.subtasks = subtasks; + + } + + public Markup GetMarkup(Style? style = null) => Markup.FromInterpolated($"{(Done ? Checked : Unchecked)} {name}", style); + + internal List Subtasks { get => subtasks; set => subtasks = value; } + + public void toggleDone() + { + Done = !Done; + } + } +} diff --git a/W8G05A/SpectreTodo/Views/EditorView.cs b/W8G05A/SpectreTodo/Views/EditorView.cs new file mode 100644 index 0000000..44580d2 --- /dev/null +++ b/W8G05A/SpectreTodo/Views/EditorView.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SpectreTodo.Views +{ + internal class EditorView + { + } +} diff --git a/W8G05A/SpectreTodo/Views/TaskView.cs b/W8G05A/SpectreTodo/Views/TaskView.cs new file mode 100644 index 0000000..cdb1433 --- /dev/null +++ b/W8G05A/SpectreTodo/Views/TaskView.cs @@ -0,0 +1,47 @@ +using Spectre.Console; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace BallerTODO.Views +{ + static internal class TaskView + { + public static async Task Draw() + { + var micsin = new TodoItem("subtask", "asd", new List { new TodoItem("micsin", "asd"), new TodoItem("nenen") }); + TodoItem t = new TodoItem("Feladatka", "Ez egy taszk", new List { micsin, new TodoItem("kettes") }); + + List tasks = new List { t, new TodoItem("Modernc#") }; + + + t.Done = true; + micsin.toggleDone(); + + var tree = GetTaskTree(new List { t }, micsin); + + var table = new Table().Centered(); + + + + AnsiConsole.Write(tree); + } + + public static Tree GetTaskTree(List tasks, TodoItem? selected = null) + { + Style selected_style = Style.Parse("bold red"); + + TreeNode taskToNode(TodoItem t) { + var node = new TreeNode(t.GetMarkup(t == selected ? selected_style : null )); + node.AddNodes(t.Subtasks.Select(sub => taskToNode(sub))); + return node; + } + + var root = new Tree("Tasks"); + root.AddNodes(tasks.Select(t => taskToNode(t))); + return root; + } + } +} diff --git a/W8G05A/TUITodo/Program.cs b/W8G05A/TUITodo/Program.cs new file mode 100644 index 0000000..3d58dcc --- /dev/null +++ b/W8G05A/TUITodo/Program.cs @@ -0,0 +1,62 @@ +using Terminal.Gui; +using Terminal.Gui.Trees; +using TUITodo.Views; + +namespace TUITodo +{ + internal class Program + { + + static void Main(string[] args) + { + Application.Init(); + + #region Color scheme + Colors.Base.Normal = Application.Driver.MakeAttribute(Color.Green, Color.Black); + Colors.Base.HotNormal = Application.Driver.MakeAttribute(Color.Brown, Color.Black); + #endregion + + #region MenuBar + + var menu = new MenuBar(new MenuBarItem[] { + new MenuBarItem ("_File", new MenuItem [] { + new MenuItem ("_Quit", "", () => { + Application.RequestStop (); + }) + }), + }); + + #endregion + + + + var win = new Window("Task list") + { + X = 0, + Y = 1, + Width = Dim.Fill(), + Height = Dim.Fill() - 1 + }; + + + var micsin = new TodoItem("subtask", "asd", new List { new TodoItem("micsin", "asd"), new TodoItem("nenen") }); + TodoItem t = new TodoItem("Feladatka", "Ez egy taszk", new List { micsin, new TodoItem("kettes") }); + + var tasklist = new TaskListView(new List { t }) + { + X = 1, + Y = 1, + Width = 40, + Height = Dim.Percent(60) + }; + win.Add(tasklist); + + tasklist.ExpandAll(); + + + Application.Top.Add(menu, win); + Application.Run(); + Application.Shutdown(); + } + } +} \ No newline at end of file diff --git a/W8G05A/TUITodo/TUITodo.csproj b/W8G05A/TUITodo/TUITodo.csproj new file mode 100644 index 0000000..2b2aa92 --- /dev/null +++ b/W8G05A/TUITodo/TUITodo.csproj @@ -0,0 +1,14 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + diff --git a/W8G05A/TUITodo/TodoItem.cs b/W8G05A/TUITodo/TodoItem.cs new file mode 100644 index 0000000..aa1e1d7 --- /dev/null +++ b/W8G05A/TUITodo/TodoItem.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; +using Terminal.Gui.Trees; + +namespace TUITodo +{ + + + internal record class TodoItem : ITreeNode + { + private const string Unchecked = "O"; + private const string Checked = "X"; + + bool _done; + public bool Done + { + get { return _done; } + set + { + _done = value; + subtasks.ForEach(t => t.Done = value); + } + } + public string name; + string description; + + List subtasks; + + public TodoItem(string name, string description = "") : this(name, description, new List()) { } + + public TodoItem(string name, string description, List subtasks) + { + this._done = false; + this.name = name; + this.description = description; + + this.subtasks = subtasks; + + } + + //public Markup GetMarkup(Style? style = null) => Markup.FromInterpolated($"{(Done ? Checked : Unchecked)} {name}", style); + + //internal List Subtasks { get => subtasks; set => subtasks = value; } + public string Text + { + get => $"{(Done ? Checked : Unchecked)} {name}"; set { name = value; } + } + + public IList Children => subtasks.Cast().ToList(); + + public object Tag + { + get => description; set { description = (string)value; } + } + + public void ToggleDone() + { + Done = !Done; + } + } +} diff --git a/W8G05A/TUITodo/Views/EditorView.cs b/W8G05A/TUITodo/Views/EditorView.cs new file mode 100644 index 0000000..69303e5 --- /dev/null +++ b/W8G05A/TUITodo/Views/EditorView.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terminal.Gui; + +namespace TUITodo.Views +{ + internal class EditorView : FrameView + { + } +} diff --git a/W8G05A/TUITodo/Views/TaskListView.cs b/W8G05A/TUITodo/Views/TaskListView.cs new file mode 100644 index 0000000..f47ea9f --- /dev/null +++ b/W8G05A/TUITodo/Views/TaskListView.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terminal.Gui; +using Terminal.Gui.Trees; + +namespace TUITodo.Views +{ + internal class TaskListView : TreeView + { + private List items = new(); + + public List Items + { + get => items; + protected set + { + items = value; + base.ClearObjects(); + AddObjects(value); + } + } + + public TaskListView(List items) + { + Items = items; + KeyDown += e => + { + Key k = e.KeyEvent.Key; + + if (k == Key.Space && SelectedObject is TodoItem t) + { + Application.MainLoop.Invoke(() => + { + t.ToggleDone(); + + // Tell view to redraw + SetNeedsDisplay(); + }); + } + if (k == (Key)'+') + { + Application.MainLoop.Invoke(() => + { + AddItem(new TodoItem("HAO")); + + SetNeedsDisplay(); + }); + } + + }; + + + } + + public void AddItem(TodoItem item) + { + Items.Add(item); + AddObject(item); + } + + + } +} diff --git a/hazifeladatok.sln b/hazifeladatok.sln index 58ea566..f108525 100644 --- a/hazifeladatok.sln +++ b/hazifeladatok.sln @@ -3,12 +3,29 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpectreTodo", "W8G05A\BallerTodo\SpectreTodo.csproj", "{58935C10-72AF-4AA7-B27F-978C76C06C1E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TUITodo", "W8G05A\TUITodo\TUITodo.csproj", "{5239D912-9C17-49FB-AE17-46D09AF520CD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {58935C10-72AF-4AA7-B27F-978C76C06C1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {58935C10-72AF-4AA7-B27F-978C76C06C1E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {58935C10-72AF-4AA7-B27F-978C76C06C1E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {58935C10-72AF-4AA7-B27F-978C76C06C1E}.Release|Any CPU.Build.0 = Release|Any CPU + {5239D912-9C17-49FB-AE17-46D09AF520CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5239D912-9C17-49FB-AE17-46D09AF520CD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5239D912-9C17-49FB-AE17-46D09AF520CD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5239D912-9C17-49FB-AE17-46D09AF520CD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7AC426C4-1B27-4610-87BB-F2200C776468} + EndGlobalSection EndGlobal From 44b2986f95e0d965780a9444c19371b87f255d2a Mon Sep 17 00:00:00 2001 From: frsaba Date: Sun, 27 Nov 2022 12:47:57 +0100 Subject: [PATCH 02/13] Editor view --- W8G05A/TUITodo/Program.cs | 65 ++++++++++++++++++---------- W8G05A/TUITodo/TodoItem.cs | 2 +- W8G05A/TUITodo/Views/EditorView.cs | 41 ++++++++++++++++++ W8G05A/TUITodo/Views/TaskListView.cs | 11 ++++- hazifeladatok.sln | 6 --- 5 files changed, 94 insertions(+), 31 deletions(-) diff --git a/W8G05A/TUITodo/Program.cs b/W8G05A/TUITodo/Program.cs index 3d58dcc..a043e60 100644 --- a/W8G05A/TUITodo/Program.cs +++ b/W8G05A/TUITodo/Program.cs @@ -1,4 +1,5 @@ -using Terminal.Gui; +using System.Text; +using Terminal.Gui; using Terminal.Gui.Trees; using TUITodo.Views; @@ -6,14 +7,48 @@ namespace TUITodo { internal class Program { + #region Views + public static View MainWindow { get; protected set; } = new Window("Task list") + { + X = 0, + Y = 1, + Width = Dim.Fill(), + Height = Dim.Fill() - 1 + }; + + public static TaskListView TaskListView { get; protected set; } = new() + { + X = 1, + Y = 1, + Width = 40, + Height = Dim.Percent(60) + }; + + public static EditorView EditorView { get; protected set; } = new() + { + X = 1, + Y = 1, + Width = 40, + Height = Dim.Percent(60) + }; + + #endregion + + public static void SwitchToView(View view) + { + MainWindow.RemoveAll(); + MainWindow.Add(view); + } static void Main(string[] args) { Application.Init(); - #region Color scheme + #region Color scheme, style setup Colors.Base.Normal = Application.Driver.MakeAttribute(Color.Green, Color.Black); Colors.Base.HotNormal = Application.Driver.MakeAttribute(Color.Brown, Color.Black); + + #endregion #region MenuBar @@ -29,32 +64,18 @@ static void Main(string[] args) #endregion - - var win = new Window("Task list") - { - X = 0, - Y = 1, - Width = Dim.Fill(), - Height = Dim.Fill() - 1 - }; - - var micsin = new TodoItem("subtask", "asd", new List { new TodoItem("micsin", "asd"), new TodoItem("nenen") }); TodoItem t = new TodoItem("Feladatka", "Ez egy taszk", new List { micsin, new TodoItem("kettes") }); - var tasklist = new TaskListView(new List { t }) - { - X = 1, - Y = 1, - Width = 40, - Height = Dim.Percent(60) - }; - win.Add(tasklist); + TaskListView.AddItem(t); + + SwitchToView(TaskListView); - tasklist.ExpandAll(); + TaskListView.ExpandAll(); + Application.Top.Add(menu, MainWindow); - Application.Top.Add(menu, win); + Application.Driver.SetCursorVisibility(CursorVisibility.Invisible); Application.Run(); Application.Shutdown(); } diff --git a/W8G05A/TUITodo/TodoItem.cs b/W8G05A/TUITodo/TodoItem.cs index aa1e1d7..c927014 100644 --- a/W8G05A/TUITodo/TodoItem.cs +++ b/W8G05A/TUITodo/TodoItem.cs @@ -26,7 +26,7 @@ public bool Done } } public string name; - string description; + public string description; List subtasks; diff --git a/W8G05A/TUITodo/Views/EditorView.cs b/W8G05A/TUITodo/Views/EditorView.cs index 69303e5..fb0a3ec 100644 --- a/W8G05A/TUITodo/Views/EditorView.cs +++ b/W8G05A/TUITodo/Views/EditorView.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -9,5 +10,45 @@ namespace TUITodo.Views { internal class EditorView : FrameView { + + TodoItem? editedItem; + + public void StartEditing(TodoItem item) + { + editedItem = item; + titleTextField.Text = item.Text; + descriptionTextView.Text = item.description; + } + + TextField titleTextField = new() + { + X = 0, + Y = 1, + Width = Dim.Fill(), + Height = 2 + }; + TextView descriptionTextView = new() + { + X = 0, + Y = 3, + Width = Dim.Fill(), + Height = Dim.Fill() - 1, + }; + + StatusBar statusBar = new StatusBar(new StatusItem[] + { + new StatusItem(Key.Esc, "Exit", () => { + Trace.WriteLine("Exit"); + Program.SwitchToView(Program.TaskListView); + }) + }) + { + + }; + public EditorView() + { + statusBar.Y = Pos.Bottom(descriptionTextView); + Add(titleTextField, descriptionTextView, statusBar); + } } } diff --git a/W8G05A/TUITodo/Views/TaskListView.cs b/W8G05A/TUITodo/Views/TaskListView.cs index f47ea9f..c55479a 100644 --- a/W8G05A/TUITodo/Views/TaskListView.cs +++ b/W8G05A/TUITodo/Views/TaskListView.cs @@ -24,9 +24,11 @@ protected set } } - public TaskListView(List items) + public TaskListView(List? items = null) { - Items = items; + + Items = items ?? new List(); + KeyDown += e => { Key k = e.KeyEvent.Key; @@ -50,6 +52,11 @@ public TaskListView(List items) SetNeedsDisplay(); }); } + if (k == Key.Enter && SelectedObject is TodoItem selected) + { + Program.SwitchToView(Program.EditorView); + Program.EditorView.StartEditing(selected); + } }; diff --git a/hazifeladatok.sln b/hazifeladatok.sln index f108525..605a7b1 100644 --- a/hazifeladatok.sln +++ b/hazifeladatok.sln @@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpectreTodo", "W8G05A\BallerTodo\SpectreTodo.csproj", "{58935C10-72AF-4AA7-B27F-978C76C06C1E}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TUITodo", "W8G05A\TUITodo\TUITodo.csproj", "{5239D912-9C17-49FB-AE17-46D09AF520CD}" EndProject Global @@ -13,10 +11,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {58935C10-72AF-4AA7-B27F-978C76C06C1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {58935C10-72AF-4AA7-B27F-978C76C06C1E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {58935C10-72AF-4AA7-B27F-978C76C06C1E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {58935C10-72AF-4AA7-B27F-978C76C06C1E}.Release|Any CPU.Build.0 = Release|Any CPU {5239D912-9C17-49FB-AE17-46D09AF520CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5239D912-9C17-49FB-AE17-46D09AF520CD}.Debug|Any CPU.Build.0 = Debug|Any CPU {5239D912-9C17-49FB-AE17-46D09AF520CD}.Release|Any CPU.ActiveCfg = Release|Any CPU From 9eb9f2a8f4516bef2ce28d922805ced683c8fa48 Mon Sep 17 00:00:00 2001 From: frsaba Date: Sun, 27 Nov 2022 13:36:29 +0100 Subject: [PATCH 03/13] Save changes on exiting edit mode --- W8G05A/TUITodo/Program.cs | 4 +++ W8G05A/TUITodo/Views/EditorView.cs | 52 ++++++++++++++++++++++++------ 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/W8G05A/TUITodo/Program.cs b/W8G05A/TUITodo/Program.cs index a043e60..272e14e 100644 --- a/W8G05A/TUITodo/Program.cs +++ b/W8G05A/TUITodo/Program.cs @@ -38,6 +38,10 @@ public static void SwitchToView(View view) { MainWindow.RemoveAll(); MainWindow.Add(view); + if(view == EditorView) + { + MainWindow.Add(EditorView.statusBar); + } } static void Main(string[] args) diff --git a/W8G05A/TUITodo/Views/EditorView.cs b/W8G05A/TUITodo/Views/EditorView.cs index fb0a3ec..0f998d4 100644 --- a/W8G05A/TUITodo/Views/EditorView.cs +++ b/W8G05A/TUITodo/Views/EditorView.cs @@ -16,7 +16,7 @@ internal class EditorView : FrameView public void StartEditing(TodoItem item) { editedItem = item; - titleTextField.Text = item.Text; + titleTextField.Text = item.name; descriptionTextView.Text = item.description; } @@ -35,20 +35,54 @@ public void StartEditing(TodoItem item) Height = Dim.Fill() - 1, }; - StatusBar statusBar = new StatusBar(new StatusItem[] - { - new StatusItem(Key.Esc, "Exit", () => { - Trace.WriteLine("Exit"); - Program.SwitchToView(Program.TaskListView); - }) - }) + public StatusBar statusBar { get; } = new StatusBar() { }; + + void Save() + { + if (editedItem == null) return; + + editedItem.name = (string)titleTextField.Text; + editedItem.description = (string)descriptionTextView.Text; + } + //Check for actual changes. TextField and TextView have an isDirty field, + //but it seems they are set to true even when we only just entered them and haven't made any edits + bool isDirty() + { + return editedItem?.name != titleTextField.Text || + editedItem?.description != descriptionTextView.Text; + } + + void Exit() + { + if (isDirty()) + { + int buttonIndex = MessageBox.Query( + "Unsaved changes", "Would you like to save?", "Save and exit", "Discard changes", "Stay editing"); + + if (buttonIndex == 0) Save(); + if (buttonIndex == -1 || buttonIndex == 2) return; + } + + Program.SwitchToView(Program.TaskListView); + } + + void SaveAndExit() + { + Save(); + Exit(); + } + public EditorView() { + statusBar.Items = new StatusItem[] + { + new StatusItem(Key.Esc, "~Esc Exit", Exit) + }; + Add(titleTextField, descriptionTextView); statusBar.Y = Pos.Bottom(descriptionTextView); - Add(titleTextField, descriptionTextView, statusBar); } } } From 304553fc12b3d98d08fc5e47d175c149e7bee64b Mon Sep 17 00:00:00 2001 From: frsaba Date: Sun, 27 Nov 2022 16:34:19 +0100 Subject: [PATCH 04/13] Task list view status bar --- W8G05A/TUITodo/Program.cs | 31 +++++++++---- W8G05A/TUITodo/TodoItem.cs | 49 ++++++++++++-------- W8G05A/TUITodo/Views/EditorView.cs | 22 +++++++-- W8G05A/TUITodo/Views/TaskListView.cs | 68 ++++++++++++++-------------- 4 files changed, 103 insertions(+), 67 deletions(-) diff --git a/W8G05A/TUITodo/Program.cs b/W8G05A/TUITodo/Program.cs index 272e14e..eabe964 100644 --- a/W8G05A/TUITodo/Program.cs +++ b/W8G05A/TUITodo/Program.cs @@ -20,28 +20,39 @@ internal class Program { X = 1, Y = 1, - Width = 40, - Height = Dim.Percent(60) + Width = Dim.Fill(), + Height = Dim.Fill() - 1 }; public static EditorView EditorView { get; protected set; } = new() { X = 1, Y = 1, - Width = 40, - Height = Dim.Percent(60) + Width = Dim.Fill(), + Height = Dim.Fill() - 1 }; #endregion - public static void SwitchToView(View view) + private static void SwitchToView(View view) { + Application.Top.RemoveAll(); MainWindow.RemoveAll(); + Application.Top.Add(MainWindow); MainWindow.Add(view); - if(view == EditorView) - { - MainWindow.Add(EditorView.statusBar); - } + } + + public static void EnterEditMode(TodoItem editedItem) + { + SwitchToView(EditorView); + Application.Top.Add(EditorView.statusBar); + EditorView.StartEditing(editedItem); + } + + public static void ShowTaskListView() + { + SwitchToView(TaskListView); + Application.Top.Add(TaskListView.statusBar); } static void Main(string[] args) @@ -73,7 +84,7 @@ static void Main(string[] args) TaskListView.AddItem(t); - SwitchToView(TaskListView); + ShowTaskListView(); TaskListView.ExpandAll(); diff --git a/W8G05A/TUITodo/TodoItem.cs b/W8G05A/TUITodo/TodoItem.cs index c927014..1b299a0 100644 --- a/W8G05A/TUITodo/TodoItem.cs +++ b/W8G05A/TUITodo/TodoItem.cs @@ -15,51 +15,62 @@ internal record class TodoItem : ITreeNode private const string Unchecked = "O"; private const string Checked = "X"; - bool _done; - public bool Done - { - get { return _done; } - set - { - _done = value; - subtasks.ForEach(t => t.Done = value); - } - } + public TodoItem? parentTask { get; protected set; } + + public bool Done { get; protected set; } public string name; public string description; - List subtasks; + public List Subtasks { get; protected set; } public TodoItem(string name, string description = "") : this(name, description, new List()) { } public TodoItem(string name, string description, List subtasks) { - this._done = false; + this.Done = false; this.name = name; this.description = description; - this.subtasks = subtasks; + this.Subtasks = new(); + subtasks.ForEach(AddSubtask); } - //public Markup GetMarkup(Style? style = null) => Markup.FromInterpolated($"{(Done ? Checked : Unchecked)} {name}", style); - - //internal List Subtasks { get => subtasks; set => subtasks = value; } public string Text { get => $"{(Done ? Checked : Unchecked)} {name}"; set { name = value; } } - public IList Children => subtasks.Cast().ToList(); + public IList Children => Subtasks.Cast().ToList(); public object Tag { get => description; set { description = (string)value; } } - public void ToggleDone() + /// Make every subtask recursively inherit the done value + public void ToggleDone(bool chainToSubtasks = true) + { + SetDone(!Done, chainToSubtasks); + } + + /// Make every subtask recursively inherit the done value + public void SetDone(bool value, bool chainToSubtasks = true) + { + Done = value; + if (chainToSubtasks) + Subtasks.ForEach(t => t.SetDone(value, chainToSubtasks)); + + //complete parent task if all of its subtasks are done + if (parentTask != null) + parentTask.SetDone(parentTask.Subtasks.All(t => t.Done), chainToSubtasks:false); + + } + + public void AddSubtask(TodoItem task) { - Done = !Done; + Subtasks.Add(task); + task.parentTask = this; } } } diff --git a/W8G05A/TUITodo/Views/EditorView.cs b/W8G05A/TUITodo/Views/EditorView.cs index 0f998d4..a1dff12 100644 --- a/W8G05A/TUITodo/Views/EditorView.cs +++ b/W8G05A/TUITodo/Views/EditorView.cs @@ -18,6 +18,7 @@ public void StartEditing(TodoItem item) editedItem = item; titleTextField.Text = item.name; descriptionTextView.Text = item.description; + titleTextField.SelectAll(); } TextField titleTextField = new() @@ -47,11 +48,12 @@ void Save() editedItem.name = (string)titleTextField.Text; editedItem.description = (string)descriptionTextView.Text; } + //Check for actual changes. TextField and TextView have an isDirty field, //but it seems they are set to true even when we only just entered them and haven't made any edits bool isDirty() { - return editedItem?.name != titleTextField.Text || + return editedItem?.name != titleTextField.Text || editedItem?.description != descriptionTextView.Text; } @@ -60,13 +62,14 @@ void Exit() if (isDirty()) { int buttonIndex = MessageBox.Query( - "Unsaved changes", "Would you like to save?", "Save and exit", "Discard changes", "Stay editing"); + "Unsaved changes", "Would you like to save?", + "Save and exit", "Discard changes", "Stay editing"); if (buttonIndex == 0) Save(); if (buttonIndex == -1 || buttonIndex == 2) return; } - Program.SwitchToView(Program.TaskListView); + Program.ShowTaskListView(); } void SaveAndExit() @@ -83,6 +86,19 @@ public EditorView() }; Add(titleTextField, descriptionTextView); statusBar.Y = Pos.Bottom(descriptionTextView); + + titleTextField.KeyDown += e => + { + Key k = e.KeyEvent.Key; + + if (k == Key.Enter) + { + Application.MainLoop.Invoke(() => + { + Program.EditorView.FocusNext(); + }); + } + }; } } } diff --git a/W8G05A/TUITodo/Views/TaskListView.cs b/W8G05A/TUITodo/Views/TaskListView.cs index c55479a..418ef54 100644 --- a/W8G05A/TUITodo/Views/TaskListView.cs +++ b/W8G05A/TUITodo/Views/TaskListView.cs @@ -15,7 +15,7 @@ internal class TaskListView : TreeView public List Items { - get => items; + get => items; protected set { items = value; @@ -24,51 +24,49 @@ protected set } } + public StatusBar statusBar = new(); + public TaskListView(List? items = null) { - - Items = items ?? new List(); - - KeyDown += e => + statusBar.Items = new StatusItem[] { - Key k = e.KeyEvent.Key; - - if (k == Key.Space && SelectedObject is TodoItem t) - { - Application.MainLoop.Invoke(() => - { - t.ToggleDone(); - - // Tell view to redraw - SetNeedsDisplay(); - }); - } - if (k == (Key)'+') - { - Application.MainLoop.Invoke(() => - { - AddItem(new TodoItem("HAO")); + new StatusItem(Key.E, "~Shift + E~ Edit task", () => { + if (SelectedObject is TodoItem selected) Program.EnterEditMode(selected); + }), + new StatusItem((Key)'+', "~+~ New task", () => { + AddItem(new TodoItem("HAO")); + }), + new StatusItem(Key.Space, "~Space~ Task complete", () => { + if (SelectedObject is TodoItem selected){ + selected.ToggleDone(); + SetNeedsDisplay(); + } + }) + }; - SetNeedsDisplay(); - }); - } - if (k == Key.Enter && SelectedObject is TodoItem selected) - { - Program.SwitchToView(Program.EditorView); - Program.EditorView.StartEditing(selected); - } + Items = items ?? new List(); - }; - } public void AddItem(TodoItem item) { - Items.Add(item); - AddObject(item); + TodoItem? parent = ((TodoItem)SelectedObject)?.parentTask; + + if (parent == null) //add to root level + { + Items.Add(item); + AddObject(item); + } + else //add as sibling + { + parent.AddSubtask(item); + } + + SetNeedsDisplay(); + SetChildNeedsDisplay(); } - + } } From 3496630da946dd95e108a2499174c1b2944d5bf5 Mon Sep 17 00:00:00 2001 From: frsaba Date: Sun, 27 Nov 2022 18:50:31 +0100 Subject: [PATCH 05/13] Create new task --- W8G05A/TUITodo/Program.cs | 3 +++ W8G05A/TUITodo/Views/TaskListView.cs | 15 ++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/W8G05A/TUITodo/Program.cs b/W8G05A/TUITodo/Program.cs index eabe964..c19ede3 100644 --- a/W8G05A/TUITodo/Program.cs +++ b/W8G05A/TUITodo/Program.cs @@ -88,6 +88,9 @@ static void Main(string[] args) TaskListView.ExpandAll(); + //AddItem focuses the last item, let's go back to the top + TaskListView.GoToFirst(); + Application.Top.Add(menu, MainWindow); Application.Driver.SetCursorVisibility(CursorVisibility.Invisible); diff --git a/W8G05A/TUITodo/Views/TaskListView.cs b/W8G05A/TUITodo/Views/TaskListView.cs index 418ef54..e953e1d 100644 --- a/W8G05A/TUITodo/Views/TaskListView.cs +++ b/W8G05A/TUITodo/Views/TaskListView.cs @@ -34,7 +34,11 @@ public TaskListView(List? items = null) if (SelectedObject is TodoItem selected) Program.EnterEditMode(selected); }), new StatusItem((Key)'+', "~+~ New task", () => { - AddItem(new TodoItem("HAO")); + AddItem(new TodoItem("New task")); + }), + new StatusItem((Key)'-', "~-~ New subtask", () => { + if (SelectedObject is TodoItem selected) + AddItem(new TodoItem("New subtask"), parent:selected); }), new StatusItem(Key.Space, "~Space~ Task complete", () => { if (SelectedObject is TodoItem selected){ @@ -49,9 +53,9 @@ public TaskListView(List? items = null) } - public void AddItem(TodoItem item) + public void AddItem(TodoItem item, TodoItem? parent = null) { - TodoItem? parent = ((TodoItem)SelectedObject)?.parentTask; + parent ??= ((TodoItem)SelectedObject)?.parentTask; if (parent == null) //add to root level { @@ -61,10 +65,11 @@ public void AddItem(TodoItem item) else //add as sibling { parent.AddSubtask(item); + RefreshObject(parent); } - SetNeedsDisplay(); - SetChildNeedsDisplay(); + this.Expand(); + this.GoTo(item); } From fe306c3f94dca0647a6166145c64bfe881b52128 Mon Sep 17 00:00:00 2001 From: frsaba Date: Sun, 27 Nov 2022 21:18:11 +0100 Subject: [PATCH 06/13] Delete task --- W8G05A/TUITodo/Program.cs | 7 +++-- W8G05A/TUITodo/TodoItem.cs | 9 +++++- W8G05A/TUITodo/Views/TaskListView.cs | 42 +++++++++++++++++++++++----- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/W8G05A/TUITodo/Program.cs b/W8G05A/TUITodo/Program.cs index c19ede3..4e34d70 100644 --- a/W8G05A/TUITodo/Program.cs +++ b/W8G05A/TUITodo/Program.cs @@ -36,7 +36,6 @@ internal class Program private static void SwitchToView(View view) { - Application.Top.RemoveAll(); MainWindow.RemoveAll(); Application.Top.Add(MainWindow); MainWindow.Add(view); @@ -44,15 +43,17 @@ private static void SwitchToView(View view) public static void EnterEditMode(TodoItem editedItem) { - SwitchToView(EditorView); + Application.Top.RemoveAll(); Application.Top.Add(EditorView.statusBar); + SwitchToView(EditorView); EditorView.StartEditing(editedItem); } public static void ShowTaskListView() { - SwitchToView(TaskListView); + Application.Top.RemoveAll(); Application.Top.Add(TaskListView.statusBar); + SwitchToView(TaskListView); } static void Main(string[] args) diff --git a/W8G05A/TUITodo/TodoItem.cs b/W8G05A/TUITodo/TodoItem.cs index 1b299a0..daf1f93 100644 --- a/W8G05A/TUITodo/TodoItem.cs +++ b/W8G05A/TUITodo/TodoItem.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -45,7 +46,7 @@ public string Text public object Tag { - get => description; set { description = (string)value; } + get => name; set { name = (string)value; } } /// Make every subtask recursively inherit the done value @@ -72,5 +73,11 @@ public void AddSubtask(TodoItem task) Subtasks.Add(task); task.parentTask = this; } + + public void RemoveSubtask(TodoItem task) + { + task.parentTask = null; + Subtasks.Remove(task); + } } } diff --git a/W8G05A/TUITodo/Views/TaskListView.cs b/W8G05A/TUITodo/Views/TaskListView.cs index e953e1d..6e566e5 100644 --- a/W8G05A/TUITodo/Views/TaskListView.cs +++ b/W8G05A/TUITodo/Views/TaskListView.cs @@ -28,29 +28,37 @@ protected set public TaskListView(List? items = null) { + #region status bar items statusBar.Items = new StatusItem[] { - new StatusItem(Key.E, "~Shift + E~ Edit task", () => { + new (Key.E, "~Shift + E~ Edit task", () => { if (SelectedObject is TodoItem selected) Program.EnterEditMode(selected); }), - new StatusItem((Key)'+', "~+~ New task", () => { + new ((Key)'+', "~+~ New task", () => { AddItem(new TodoItem("New task")); }), - new StatusItem((Key)'-', "~-~ New subtask", () => { + new ((Key)'-', "~-~ New subtask", () => { if (SelectedObject is TodoItem selected) AddItem(new TodoItem("New subtask"), parent:selected); }), - new StatusItem(Key.Space, "~Space~ Task complete", () => { + new (Key.Space, "~Space~ Task complete", () => { if (SelectedObject is TodoItem selected){ selected.ToggleDone(); - SetNeedsDisplay(); + SetNeedsDisplay(); + } + }), + new (Key.D, "~Shift + D~ Delete", () => { + if (SelectedObject is TodoItem selected){ + if (MessageBox.Query("Delete task", + $"Are you sure you want to delete the task '{selected.name}'?", + "Delete it", "Nevermind") == 0) DeleteItem(selected); } }) }; - Items = items ?? new List(); - + #endregion + Items = items ?? new List(); } public void AddItem(TodoItem item, TodoItem? parent = null) @@ -72,6 +80,26 @@ public void AddItem(TodoItem item, TodoItem? parent = null) this.GoTo(item); } + void DeleteItem(TodoItem item) + { + //Remove(item); + items.Remove(item); + var parent = item.parentTask; + if(parent != null) + parent.RemoveSubtask(item); + + RebuildTreeFromScratch(); + this.GoTo(parent); + } + //apparently RebuildTree() and RefreshObject do not work to get rid of a removed item, + //so we'll rebuild tree for real ourselves + void RebuildTreeFromScratch() + { + base.ClearObjects(); + AddObjects(items); + ExpandAll(); + } + } } From 4872528bf737ad58dfaf269f0353e9976e9c885b Mon Sep 17 00:00:00 2001 From: frsaba Date: Mon, 28 Nov 2022 00:18:20 +0100 Subject: [PATCH 07/13] JSON serialization --- W8G05A/TUITodo/Program.cs | 13 +++--- W8G05A/TUITodo/TodoItem.cs | 18 +++++++- W8G05A/TUITodo/Utils/TodoItemSerializer.cs | 50 ++++++++++++++++++++++ W8G05A/TUITodo/Views/EditorView.cs | 3 ++ 4 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 W8G05A/TUITodo/Utils/TodoItemSerializer.cs diff --git a/W8G05A/TUITodo/Program.cs b/W8G05A/TUITodo/Program.cs index 4e34d70..2ff83cf 100644 --- a/W8G05A/TUITodo/Program.cs +++ b/W8G05A/TUITodo/Program.cs @@ -1,6 +1,7 @@ using System.Text; using Terminal.Gui; using Terminal.Gui.Trees; +using TUITodo.Utils; using TUITodo.Views; namespace TUITodo @@ -56,8 +57,11 @@ public static void ShowTaskListView() SwitchToView(TaskListView); } - static void Main(string[] args) + static async Task Main(string[] args) { + + List savedTodos = await TodoItemSerializer.ReadFromJSON() ?? new List(); + Application.Init(); #region Color scheme, style setup @@ -80,11 +84,10 @@ static void Main(string[] args) #endregion - var micsin = new TodoItem("subtask", "asd", new List { new TodoItem("micsin", "asd"), new TodoItem("nenen") }); - TodoItem t = new TodoItem("Feladatka", "Ez egy taszk", new List { micsin, new TodoItem("kettes") }); - - TaskListView.AddItem(t); + //var micsin = new TodoItem("subtask", "asd", new List { new TodoItem("micsin", "asd"), new TodoItem("nenen") }); + //TodoItem t = new TodoItem("Feladatka", "Ez egy taszk", new List { micsin, new TodoItem("kettes") }); + savedTodos.ForEach(t => TaskListView.AddItem(t)); ShowTaskListView(); TaskListView.ExpandAll(); diff --git a/W8G05A/TUITodo/TodoItem.cs b/W8G05A/TUITodo/TodoItem.cs index daf1f93..3e81b97 100644 --- a/W8G05A/TUITodo/TodoItem.cs +++ b/W8G05A/TUITodo/TodoItem.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Linq; using System.Text; +using System.Text.Json.Serialization; using System.Threading.Tasks; using System.Xml.Linq; using Terminal.Gui.Trees; @@ -19,11 +20,12 @@ internal record class TodoItem : ITreeNode public TodoItem? parentTask { get; protected set; } public bool Done { get; protected set; } + [JsonInclude] public string name; + [JsonInclude] public string description; - public List Subtasks { get; protected set; } - + public List Subtasks { get; set; } public TodoItem(string name, string description = "") : this(name, description, new List()) { } public TodoItem(string name, string description, List subtasks) @@ -37,13 +39,25 @@ public TodoItem(string name, string description, List subtasks) } + [JsonConstructor] + public TodoItem() + { + Done = false; ; + this.name = ""; + this.description = ""; + Subtasks = new List(); + } + + [JsonIgnore] public string Text { get => $"{(Done ? Checked : Unchecked)} {name}"; set { name = value; } } + [JsonIgnore] public IList Children => Subtasks.Cast().ToList(); + [JsonIgnore] public object Tag { get => name; set { name = (string)value; } diff --git a/W8G05A/TUITodo/Utils/TodoItemSerializer.cs b/W8G05A/TUITodo/Utils/TodoItemSerializer.cs new file mode 100644 index 0000000..31541c3 --- /dev/null +++ b/W8G05A/TUITodo/Utils/TodoItemSerializer.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Diagnostics; + +namespace TUITodo.Utils +{ + internal static class TodoItemSerializer + { + static JsonSerializerOptions options = new() + { + ReferenceHandler = ReferenceHandler.Preserve, + WriteIndented = true + }; + + public static async Task WriteToJSON(IList items, string path = "todos.json") + { + string json = JsonSerializer.Serialize(items, options); + Trace.WriteLine(json); + using (StreamWriter writer = File.CreateText(path)) + { + await writer.WriteAsync(json); + } + } + + public async static Task?> ReadFromJSON(string path = "todos.json") + { + try + { + using (StreamReader sr = new StreamReader(path)) + { + string json = await sr.ReadToEndAsync(); + + return JsonSerializer.Deserialize>(json, options); + + } + } + catch (Exception e) + { + Trace.WriteLine($"Could not deserialize: {e}"); + return null; + } + + } + } +} diff --git a/W8G05A/TUITodo/Views/EditorView.cs b/W8G05A/TUITodo/Views/EditorView.cs index a1dff12..1f61794 100644 --- a/W8G05A/TUITodo/Views/EditorView.cs +++ b/W8G05A/TUITodo/Views/EditorView.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading.Tasks; using Terminal.Gui; +using TUITodo.Utils; namespace TUITodo.Views { @@ -47,6 +48,8 @@ void Save() editedItem.name = (string)titleTextField.Text; editedItem.description = (string)descriptionTextView.Text; + + Task.Run(() => TodoItemSerializer.WriteToJSON(Program.TaskListView.Items)); } //Check for actual changes. TextField and TextView have an isDirty field, From d5941aabbd6265243b8bb97e29e94c337b326e83 Mon Sep 17 00:00:00 2001 From: Tomikland Date: Sat, 3 Dec 2022 10:40:00 +0100 Subject: [PATCH 08/13] Removed SpectreTodo --- W8G05A/SpectreTodo/Program.cs | 14 ------- W8G05A/SpectreTodo/SpectreTodo.csproj | 14 ------- W8G05A/SpectreTodo/TodoItem.cs | 54 -------------------------- W8G05A/SpectreTodo/Views/EditorView.cs | 12 ------ W8G05A/SpectreTodo/Views/TaskView.cs | 47 ---------------------- 5 files changed, 141 deletions(-) delete mode 100644 W8G05A/SpectreTodo/Program.cs delete mode 100644 W8G05A/SpectreTodo/SpectreTodo.csproj delete mode 100644 W8G05A/SpectreTodo/TodoItem.cs delete mode 100644 W8G05A/SpectreTodo/Views/EditorView.cs delete mode 100644 W8G05A/SpectreTodo/Views/TaskView.cs diff --git a/W8G05A/SpectreTodo/Program.cs b/W8G05A/SpectreTodo/Program.cs deleted file mode 100644 index dbe1a3a..0000000 --- a/W8G05A/SpectreTodo/Program.cs +++ /dev/null @@ -1,14 +0,0 @@ -// See https://aka.ms/new-console-template for more information -using BallerTODO.Views; -using Spectre.Console; - -Console.WriteLine("Hello, World!"); - -string[] tasks = { "" }; - -//var panel = new Panel("Hello World\nIs this a problem?"); -//panel.Header = new PanelHeader("Some text"); -//panel.Border = BoxBorder.Rounded; -//AnsiConsole.Write(panel); - -await TaskView.Draw(); \ No newline at end of file diff --git a/W8G05A/SpectreTodo/SpectreTodo.csproj b/W8G05A/SpectreTodo/SpectreTodo.csproj deleted file mode 100644 index 731f95d..0000000 --- a/W8G05A/SpectreTodo/SpectreTodo.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - Exe - net6.0 - enable - enable - - - - - - - diff --git a/W8G05A/SpectreTodo/TodoItem.cs b/W8G05A/SpectreTodo/TodoItem.cs deleted file mode 100644 index a9b8172..0000000 --- a/W8G05A/SpectreTodo/TodoItem.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Spectre.Console; -using Spectre.Console.Rendering; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BallerTODO -{ - - - internal record class TodoItem - { - private const string Unchecked = "O"; - private const string Checked = "X"; - - bool _done; - public bool Done - { - get { return _done; } - set - { - _done = value; - Subtasks.ForEach(t => t.Done = value); - } - } - string name; - string description; - - List subtasks; - - public TodoItem(string name, string description = "") : this(name, description, new List()) { } - - public TodoItem(string name, string description, List subtasks) - { - this._done = false; - this.name = name; - this.description = description; - - this.subtasks = subtasks; - - } - - public Markup GetMarkup(Style? style = null) => Markup.FromInterpolated($"{(Done ? Checked : Unchecked)} {name}", style); - - internal List Subtasks { get => subtasks; set => subtasks = value; } - - public void toggleDone() - { - Done = !Done; - } - } -} diff --git a/W8G05A/SpectreTodo/Views/EditorView.cs b/W8G05A/SpectreTodo/Views/EditorView.cs deleted file mode 100644 index 44580d2..0000000 --- a/W8G05A/SpectreTodo/Views/EditorView.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SpectreTodo.Views -{ - internal class EditorView - { - } -} diff --git a/W8G05A/SpectreTodo/Views/TaskView.cs b/W8G05A/SpectreTodo/Views/TaskView.cs deleted file mode 100644 index cdb1433..0000000 --- a/W8G05A/SpectreTodo/Views/TaskView.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Spectre.Console; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml.Linq; - -namespace BallerTODO.Views -{ - static internal class TaskView - { - public static async Task Draw() - { - var micsin = new TodoItem("subtask", "asd", new List { new TodoItem("micsin", "asd"), new TodoItem("nenen") }); - TodoItem t = new TodoItem("Feladatka", "Ez egy taszk", new List { micsin, new TodoItem("kettes") }); - - List tasks = new List { t, new TodoItem("Modernc#") }; - - - t.Done = true; - micsin.toggleDone(); - - var tree = GetTaskTree(new List { t }, micsin); - - var table = new Table().Centered(); - - - - AnsiConsole.Write(tree); - } - - public static Tree GetTaskTree(List tasks, TodoItem? selected = null) - { - Style selected_style = Style.Parse("bold red"); - - TreeNode taskToNode(TodoItem t) { - var node = new TreeNode(t.GetMarkup(t == selected ? selected_style : null )); - node.AddNodes(t.Subtasks.Select(sub => taskToNode(sub))); - return node; - } - - var root = new Tree("Tasks"); - root.AddNodes(tasks.Select(t => taskToNode(t))); - return root; - } - } -} From 489d268fc57ac4f71d60ae5a0eaeb8d93c1e1c07 Mon Sep 17 00:00:00 2001 From: frsaba Date: Mon, 5 Dec 2022 16:26:54 +0100 Subject: [PATCH 09/13] Display status bar message --- W8G05A/TUITodo/Program.cs | 41 ++++++++++++++++++++---------- W8G05A/TUITodo/Views/EditorView.cs | 2 +- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/W8G05A/TUITodo/Program.cs b/W8G05A/TUITodo/Program.cs index 2ff83cf..8d735be 100644 --- a/W8G05A/TUITodo/Program.cs +++ b/W8G05A/TUITodo/Program.cs @@ -1,4 +1,5 @@ -using System.Text; +using System.Linq.Expressions; +using System.Text; using Terminal.Gui; using Terminal.Gui.Trees; using TUITodo.Utils; @@ -9,32 +10,48 @@ namespace TUITodo internal class Program { #region Views - public static View MainWindow { get; protected set; } = new Window("Task list") + public static View MainWindow { get; } = new Window("Task list") { X = 0, Y = 1, Width = Dim.Fill(), - Height = Dim.Fill() - 1 + Height = Dim.Fill() - 3 }; - public static TaskListView TaskListView { get; protected set; } = new() + public static TaskListView TaskListView { get; } = new() { X = 1, Y = 1, Width = Dim.Fill(), - Height = Dim.Fill() - 1 + Height = Dim.Fill() - 2 }; - public static EditorView EditorView { get; protected set; } = new() + public static EditorView EditorView { get; } = new() { X = 1, Y = 1, Width = Dim.Fill(), - Height = Dim.Fill() - 1 + Height = Dim.Fill() - 2 + }; + + + public static Label statusBarMessage { get; } = new() + { + X = 1, + Y = Pos.Bottom(MainWindow), + Width = Dim.Fill(), + Height = 2, }; #endregion + private static async void DisplayStatusNotification(string message, int expireSeconds = 2) + { + statusBarMessage.Text = message; + await Task.Delay(TimeSpan.FromSeconds(expireSeconds)); + statusBarMessage.Text = ""; + } + private static void SwitchToView(View view) { MainWindow.RemoveAll(); @@ -45,8 +62,8 @@ private static void SwitchToView(View view) public static void EnterEditMode(TodoItem editedItem) { Application.Top.RemoveAll(); - Application.Top.Add(EditorView.statusBar); SwitchToView(EditorView); + Application.Top.Add(EditorView.statusBar); EditorView.StartEditing(editedItem); } @@ -83,19 +100,17 @@ static async Task Main(string[] args) #endregion - - //var micsin = new TodoItem("subtask", "asd", new List { new TodoItem("micsin", "asd"), new TodoItem("nenen") }); - //TodoItem t = new TodoItem("Feladatka", "Ez egy taszk", new List { micsin, new TodoItem("kettes") }); - savedTodos.ForEach(t => TaskListView.AddItem(t)); ShowTaskListView(); TaskListView.ExpandAll(); + DisplayStatusNotification("HAO", 5); + //AddItem focuses the last item, let's go back to the top TaskListView.GoToFirst(); - Application.Top.Add(menu, MainWindow); + Application.Top.Add(menu, MainWindow, statusBarMessage); Application.Driver.SetCursorVisibility(CursorVisibility.Invisible); Application.Run(); diff --git a/W8G05A/TUITodo/Views/EditorView.cs b/W8G05A/TUITodo/Views/EditorView.cs index 1f61794..dcf140f 100644 --- a/W8G05A/TUITodo/Views/EditorView.cs +++ b/W8G05A/TUITodo/Views/EditorView.cs @@ -85,7 +85,7 @@ public EditorView() { statusBar.Items = new StatusItem[] { - new StatusItem(Key.Esc, "~Esc Exit", Exit) + new StatusItem(Key.Esc, "~Esc Exit", Exit), }; Add(titleTextField, descriptionTextView); statusBar.Y = Pos.Bottom(descriptionTextView); From 49d7e9ef0c740ef202c5978e0d3b31e9b0b3d169 Mon Sep 17 00:00:00 2001 From: frsaba Date: Mon, 5 Dec 2022 21:28:06 +0100 Subject: [PATCH 10/13] Show saved status message --- W8G05A/TUITodo/Program.cs | 45 +++++++++++++++------- W8G05A/TUITodo/Utils/TodoItemSerializer.cs | 10 +++-- W8G05A/TUITodo/Views/EditorView.cs | 7 +--- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/W8G05A/TUITodo/Program.cs b/W8G05A/TUITodo/Program.cs index 8d735be..6624aa5 100644 --- a/W8G05A/TUITodo/Program.cs +++ b/W8G05A/TUITodo/Program.cs @@ -18,12 +18,13 @@ internal class Program Height = Dim.Fill() - 3 }; + public static TaskListView TaskListView { get; } = new() { X = 1, Y = 1, Width = Dim.Fill(), - Height = Dim.Fill() - 2 + Height = Dim.Fill(2) - 2 }; public static EditorView EditorView { get; } = new() @@ -45,39 +46,50 @@ internal class Program #endregion - private static async void DisplayStatusNotification(string message, int expireSeconds = 2) + + public static async void DisplayStatusNotification(string message, int expireSeconds = 2) { statusBarMessage.Text = message; - await Task.Delay(TimeSpan.FromSeconds(expireSeconds)); - statusBarMessage.Text = ""; + if(expireSeconds > 0) + { + await Task.Delay(TimeSpan.FromSeconds(expireSeconds)); + statusBarMessage.Text = ""; + } + + } + + static StatusBar? activeStatusbar; + static void SetStatusBar(StatusBar statusBar) + { + Application.Top.Remove(activeStatusbar); + activeStatusbar = statusBar; + Application.Top.Add(activeStatusbar); } + private static void SwitchToView(View view) { MainWindow.RemoveAll(); - Application.Top.Add(MainWindow); MainWindow.Add(view); } public static void EnterEditMode(TodoItem editedItem) { - Application.Top.RemoveAll(); SwitchToView(EditorView); - Application.Top.Add(EditorView.statusBar); + SetStatusBar(EditorView.statusBar); EditorView.StartEditing(editedItem); } public static void ShowTaskListView() { - Application.Top.RemoveAll(); - Application.Top.Add(TaskListView.statusBar); + SetStatusBar(TaskListView.statusBar); SwitchToView(TaskListView); } - static async Task Main(string[] args) + static async Task Main() { - List savedTodos = await TodoItemSerializer.ReadFromJSON() ?? new List(); + List savedTodos = await TodoItemSerializer.Deserialize() ?? new List(); Application.Init(); @@ -105,8 +117,6 @@ static async Task Main(string[] args) TaskListView.ExpandAll(); - DisplayStatusNotification("HAO", 5); - //AddItem focuses the last item, let's go back to the top TaskListView.GoToFirst(); @@ -116,5 +126,14 @@ static async Task Main(string[] args) Application.Run(); Application.Shutdown(); } + + public static void SaveTasks() + { + DisplayStatusNotification("Saving...", 0); + Task.Run(async () => { + await TodoItemSerializer.Serialize(TaskListView.Items); + DisplayStatusNotification("Saved"); + }); + } } } \ No newline at end of file diff --git a/W8G05A/TUITodo/Utils/TodoItemSerializer.cs b/W8G05A/TUITodo/Utils/TodoItemSerializer.cs index 31541c3..fd59a34 100644 --- a/W8G05A/TUITodo/Utils/TodoItemSerializer.cs +++ b/W8G05A/TUITodo/Utils/TodoItemSerializer.cs @@ -17,17 +17,18 @@ internal static class TodoItemSerializer WriteIndented = true }; - public static async Task WriteToJSON(IList items, string path = "todos.json") + public static async Task Serialize(IList items, string path = "todos.json") { string json = JsonSerializer.Serialize(items, options); Trace.WriteLine(json); using (StreamWriter writer = File.CreateText(path)) { await writer.WriteAsync(json); + Program.DisplayStatusNotification($"Saved"); } } - public async static Task?> ReadFromJSON(string path = "todos.json") + public async static Task?> Deserialize(string path = "todos.json") { try { @@ -35,16 +36,17 @@ public static async Task WriteToJSON(IList items, string path = "todos { string json = await sr.ReadToEndAsync(); - return JsonSerializer.Deserialize>(json, options); + return JsonSerializer.Deserialize>(json, options); } } catch (Exception e) { - Trace.WriteLine($"Could not deserialize: {e}"); + Program.DisplayStatusNotification($"Could not deserialize: {e}"); return null; } } + } } diff --git a/W8G05A/TUITodo/Views/EditorView.cs b/W8G05A/TUITodo/Views/EditorView.cs index dcf140f..b0194fc 100644 --- a/W8G05A/TUITodo/Views/EditorView.cs +++ b/W8G05A/TUITodo/Views/EditorView.cs @@ -37,10 +37,7 @@ public void StartEditing(TodoItem item) Height = Dim.Fill() - 1, }; - public StatusBar statusBar { get; } = new StatusBar() - { - - }; + public StatusBar statusBar { get; } = new(); void Save() { @@ -49,7 +46,7 @@ void Save() editedItem.name = (string)titleTextField.Text; editedItem.description = (string)descriptionTextView.Text; - Task.Run(() => TodoItemSerializer.WriteToJSON(Program.TaskListView.Items)); + Program.SaveTasks(); } //Check for actual changes. TextField and TextView have an isDirty field, From b0e364b2456247e547e967025bb91d958f506be3 Mon Sep 17 00:00:00 2001 From: frsaba Date: Tue, 6 Dec 2022 22:14:39 +0100 Subject: [PATCH 11/13] Save automatically --- W8G05A/TUITodo/Program.cs | 7 +- W8G05A/TUITodo/Views/TaskListView.cs | 96 +---------------------- W8G05A/TUITodo/Views/TaskTree.cs | 111 +++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 96 deletions(-) create mode 100644 W8G05A/TUITodo/Views/TaskTree.cs diff --git a/W8G05A/TUITodo/Program.cs b/W8G05A/TUITodo/Program.cs index 6624aa5..9a786b8 100644 --- a/W8G05A/TUITodo/Program.cs +++ b/W8G05A/TUITodo/Program.cs @@ -19,7 +19,7 @@ internal class Program }; - public static TaskListView TaskListView { get; } = new() + public static TaskTree TaskListView { get; } = new() { X = 1, Y = 1, @@ -105,7 +105,10 @@ static async Task Main() var menu = new MenuBar(new MenuBarItem[] { new MenuBarItem ("_File", new MenuItem [] { new MenuItem ("_Quit", "", () => { - Application.RequestStop (); + Application.RequestStop(); + }), + new MenuItem ("_Save", "", () => { + SaveTasks(); }) }), }); diff --git a/W8G05A/TUITodo/Views/TaskListView.cs b/W8G05A/TUITodo/Views/TaskListView.cs index 6e566e5..631bc74 100644 --- a/W8G05A/TUITodo/Views/TaskListView.cs +++ b/W8G05A/TUITodo/Views/TaskListView.cs @@ -1,105 +1,13 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; -using Terminal.Gui; -using Terminal.Gui.Trees; namespace TUITodo.Views { - internal class TaskListView : TreeView + internal class TaskListView { - private List items = new(); - - public List Items - { - get => items; - protected set - { - items = value; - base.ClearObjects(); - AddObjects(value); - } - } - - public StatusBar statusBar = new(); - - public TaskListView(List? items = null) - { - #region status bar items - statusBar.Items = new StatusItem[] - { - new (Key.E, "~Shift + E~ Edit task", () => { - if (SelectedObject is TodoItem selected) Program.EnterEditMode(selected); - }), - new ((Key)'+', "~+~ New task", () => { - AddItem(new TodoItem("New task")); - }), - new ((Key)'-', "~-~ New subtask", () => { - if (SelectedObject is TodoItem selected) - AddItem(new TodoItem("New subtask"), parent:selected); - }), - new (Key.Space, "~Space~ Task complete", () => { - if (SelectedObject is TodoItem selected){ - selected.ToggleDone(); - SetNeedsDisplay(); - } - }), - new (Key.D, "~Shift + D~ Delete", () => { - if (SelectedObject is TodoItem selected){ - if (MessageBox.Query("Delete task", - $"Are you sure you want to delete the task '{selected.name}'?", - "Delete it", "Nevermind") == 0) DeleteItem(selected); - } - }) - }; - - #endregion - - Items = items ?? new List(); - } - - public void AddItem(TodoItem item, TodoItem? parent = null) - { - parent ??= ((TodoItem)SelectedObject)?.parentTask; - - if (parent == null) //add to root level - { - Items.Add(item); - AddObject(item); - } - else //add as sibling - { - parent.AddSubtask(item); - RefreshObject(parent); - } - - this.Expand(); - this.GoTo(item); - } - - void DeleteItem(TodoItem item) - { - //Remove(item); - items.Remove(item); - var parent = item.parentTask; - if(parent != null) - parent.RemoveSubtask(item); - - RebuildTreeFromScratch(); - this.GoTo(parent); - } - //apparently RebuildTree() and RefreshObject do not work to get rid of a removed item, - //so we'll rebuild tree for real ourselves - void RebuildTreeFromScratch() - { - base.ClearObjects(); - AddObjects(items); - ExpandAll(); - } - - + TaskTree tree; } } diff --git a/W8G05A/TUITodo/Views/TaskTree.cs b/W8G05A/TUITodo/Views/TaskTree.cs new file mode 100644 index 0000000..0bb318c --- /dev/null +++ b/W8G05A/TUITodo/Views/TaskTree.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terminal.Gui; +using Terminal.Gui.Trees; + +namespace TUITodo.Views +{ + internal class TaskTree : TreeView + { + private List items = new(); + + public List Items + { + get => items; + protected set + { + items = value; + base.ClearObjects(); + AddObjects(value); + } + } + + public StatusBar statusBar = new(); + + public TaskTree(List? items = null) + { + #region status bar items + statusBar.Items = new StatusItem[] + { + //Vannak olyan keyek, amikre az életbe nem érzékel + //Totál random hogy melyik működik + //Ezért hülyeségek a gombválasztások + new (Key.E, "~Shift + E~ Edit task", () => { + if (SelectedObject is TodoItem selected) Program.EnterEditMode(selected); + }), + new ((Key)'+', "~+~ New task", () => { + AddItem(new TodoItem("New task")); + }), + new ((Key)'-', "~-~ New subtask", () => { + if (SelectedObject is TodoItem selected) + AddItem(new TodoItem("New subtask"), parent:selected); + }), + new (Key.Space, "~Space~ Task complete", () => { + if (SelectedObject is TodoItem selected){ + selected.ToggleDone(); + SetNeedsDisplay(); + Program.SaveTasks(); + } + }), + new (Key.D, "~Shift + D~ Delete", () => { + if (SelectedObject is TodoItem selected){ + if (MessageBox.Query("Delete task", + $"Are you sure you want to delete the task '{selected.name}'?", + "Delete it", "Nevermind") == 0) DeleteItem(selected); + } + }) + }; + + #endregion + + Items = items ?? new List(); + } + + public void AddItem(TodoItem item, TodoItem? parent = null) + { + parent ??= ((TodoItem)SelectedObject)?.parentTask; + + if (parent == null) //add to root level + { + Items.Add(item); + AddObject(item); + } + else //add as sibling + { + parent.AddSubtask(item); + RefreshObject(parent); + } + + this.Expand(); + this.GoTo(item); + } + + void DeleteItem(TodoItem item) + { + //Remove(item); + items.Remove(item); + var parent = item.parentTask; + if(parent != null) + parent.RemoveSubtask(item); + + RebuildTreeFromScratch(); + this.GoTo(parent); + + Program.SaveTasks(); + } + //apparently RebuildTree() and RefreshObject do not work to get rid of a removed item, + //so we'll rebuild tree for real ourselves + void RebuildTreeFromScratch() + { + base.ClearObjects(); + AddObjects(items); + ExpandAll(); + } + + + } +} From 769b23be0821ec162ecc8674de026b904daec0a9 Mon Sep 17 00:00:00 2001 From: frsaba Date: Tue, 6 Dec 2022 23:31:51 +0100 Subject: [PATCH 12/13] Seperate TaskListTree --- W8G05A/TUITodo/Program.cs | 23 +++---- W8G05A/TUITodo/Utils/TodoItemSerializer.cs | 2 +- W8G05A/TUITodo/Views/TaskListView.cs | 71 +++++++++++++++++++++- W8G05A/TUITodo/Views/TaskTree.cs | 37 +---------- 4 files changed, 81 insertions(+), 52 deletions(-) diff --git a/W8G05A/TUITodo/Program.cs b/W8G05A/TUITodo/Program.cs index 9a786b8..bc9bec2 100644 --- a/W8G05A/TUITodo/Program.cs +++ b/W8G05A/TUITodo/Program.cs @@ -19,7 +19,7 @@ internal class Program }; - public static TaskTree TaskListView { get; } = new() + public static TaskListView TaskListView { get; } = new() { X = 1, Y = 1, @@ -86,18 +86,20 @@ public static void ShowTaskListView() SwitchToView(TaskListView); } - static async Task Main() + static void Main() { - List savedTodos = await TodoItemSerializer.Deserialize() ?? new List(); - Application.Init(); + Application.MainLoop.Invoke(async () => + { + await TaskListView.LoadSavedTasks(); + }); + #region Color scheme, style setup Colors.Base.Normal = Application.Driver.MakeAttribute(Color.Green, Color.Black); Colors.Base.HotNormal = Application.Driver.MakeAttribute(Color.Brown, Color.Black); - #endregion #region MenuBar @@ -114,19 +116,14 @@ static async Task Main() }); #endregion - - savedTodos.ForEach(t => TaskListView.AddItem(t)); + //TaskListView.LoadSavedTasks(savedTodos); ShowTaskListView(); - TaskListView.ExpandAll(); - - //AddItem focuses the last item, let's go back to the top - TaskListView.GoToFirst(); - Application.Top.Add(menu, MainWindow, statusBarMessage); Application.Driver.SetCursorVisibility(CursorVisibility.Invisible); Application.Run(); + Application.Shutdown(); } @@ -134,7 +131,7 @@ public static void SaveTasks() { DisplayStatusNotification("Saving...", 0); Task.Run(async () => { - await TodoItemSerializer.Serialize(TaskListView.Items); + await TaskListView.SaveTasks(); DisplayStatusNotification("Saved"); }); } diff --git a/W8G05A/TUITodo/Utils/TodoItemSerializer.cs b/W8G05A/TUITodo/Utils/TodoItemSerializer.cs index fd59a34..4592e29 100644 --- a/W8G05A/TUITodo/Utils/TodoItemSerializer.cs +++ b/W8G05A/TUITodo/Utils/TodoItemSerializer.cs @@ -20,7 +20,7 @@ internal static class TodoItemSerializer public static async Task Serialize(IList items, string path = "todos.json") { string json = JsonSerializer.Serialize(items, options); - Trace.WriteLine(json); + //Trace.WriteLine(json); using (StreamWriter writer = File.CreateText(path)) { await writer.WriteAsync(json); diff --git a/W8G05A/TUITodo/Views/TaskListView.cs b/W8G05A/TUITodo/Views/TaskListView.cs index 631bc74..0254140 100644 --- a/W8G05A/TUITodo/Views/TaskListView.cs +++ b/W8G05A/TUITodo/Views/TaskListView.cs @@ -3,11 +3,78 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Terminal.Gui; +using TUITodo.Utils; namespace TUITodo.Views { - internal class TaskListView + internal class TaskListView : View { - TaskTree tree; + public TaskTree taskTree = new TaskTree + { + X = 0, + Y = 0, + Width = Dim.Fill(), + Height = Dim.Fill() + }; + + public StatusBar statusBar = new(); + TodoItem? selectedItem => (TodoItem)taskTree.SelectedObject; + + public TaskListView() + { + + #region status bar items + statusBar.Items = new StatusItem[] + { + //Vannak olyan keyek, amikre az életbe nem érzékel + //Totál random hogy melyik működik + //Ezért hülyeségek a gombválasztások + new (Key.E, "~Shift + E~ Edit task", () => { + if (selectedItem != null) Program.EnterEditMode(selectedItem); + }), + new ((Key)'+', "~+~ New task", () => { + taskTree.AddItem(new TodoItem("New task")); + }), + new ((Key)'-', "~-~ New subtask", () => { + if (selectedItem != null) + taskTree.AddItem(new TodoItem("New subtask"), parent:selectedItem); + }), + new (Key.Space, "~Space~ Task complete", () => { + if (selectedItem != null){ + selectedItem.ToggleDone(); + taskTree.SetNeedsDisplay(); + Program.SaveTasks(); + } + }), + new (Key.D, "~Shift + D~ Delete", () => { + if (selectedItem != null){ + if (MessageBox.Query("Delete task", + $"Are you sure you want to delete the task '{selectedItem.name}'?", + "Delete it", "Nevermind") == 0) taskTree.DeleteItem(selectedItem); + } + }) + }; + + #endregion + + Add(taskTree); + } + + public async Task LoadSavedTasks() + { + List savedTodos = await TodoItemSerializer.Deserialize() ?? new List(); + savedTodos.ForEach(t => taskTree.AddItem(t)); + + taskTree.ExpandAll(); + + //AddItem az utolsóra rakja a fókuszt, szóval vissza az elejére + taskTree.GoToFirst(); + } + + public async Task SaveTasks() + { + await TodoItemSerializer.Serialize(taskTree.Items); + } } } diff --git a/W8G05A/TUITodo/Views/TaskTree.cs b/W8G05A/TUITodo/Views/TaskTree.cs index 0bb318c..bc03620 100644 --- a/W8G05A/TUITodo/Views/TaskTree.cs +++ b/W8G05A/TUITodo/Views/TaskTree.cs @@ -24,44 +24,9 @@ protected set } } - public StatusBar statusBar = new(); public TaskTree(List? items = null) { - #region status bar items - statusBar.Items = new StatusItem[] - { - //Vannak olyan keyek, amikre az életbe nem érzékel - //Totál random hogy melyik működik - //Ezért hülyeségek a gombválasztások - new (Key.E, "~Shift + E~ Edit task", () => { - if (SelectedObject is TodoItem selected) Program.EnterEditMode(selected); - }), - new ((Key)'+', "~+~ New task", () => { - AddItem(new TodoItem("New task")); - }), - new ((Key)'-', "~-~ New subtask", () => { - if (SelectedObject is TodoItem selected) - AddItem(new TodoItem("New subtask"), parent:selected); - }), - new (Key.Space, "~Space~ Task complete", () => { - if (SelectedObject is TodoItem selected){ - selected.ToggleDone(); - SetNeedsDisplay(); - Program.SaveTasks(); - } - }), - new (Key.D, "~Shift + D~ Delete", () => { - if (SelectedObject is TodoItem selected){ - if (MessageBox.Query("Delete task", - $"Are you sure you want to delete the task '{selected.name}'?", - "Delete it", "Nevermind") == 0) DeleteItem(selected); - } - }) - }; - - #endregion - Items = items ?? new List(); } @@ -84,7 +49,7 @@ public void AddItem(TodoItem item, TodoItem? parent = null) this.GoTo(item); } - void DeleteItem(TodoItem item) + public void DeleteItem(TodoItem item) { //Remove(item); items.Remove(item); From 920fb6f31b0777fbb29d717cd1047969c0a8dcb2 Mon Sep 17 00:00:00 2001 From: frsaba Date: Wed, 7 Dec 2022 00:30:52 +0100 Subject: [PATCH 13/13] Search filter --- W8G05A/TUITodo/Utils/TodoItemSerializer.cs | 2 + W8G05A/TUITodo/Views/EditorView.cs | 2 +- W8G05A/TUITodo/Views/TaskListView.cs | 85 +++++++++++++++++++--- W8G05A/TUITodo/Views/TaskTree.cs | 2 +- 4 files changed, 77 insertions(+), 14 deletions(-) diff --git a/W8G05A/TUITodo/Utils/TodoItemSerializer.cs b/W8G05A/TUITodo/Utils/TodoItemSerializer.cs index 4592e29..21d5b99 100644 --- a/W8G05A/TUITodo/Utils/TodoItemSerializer.cs +++ b/W8G05A/TUITodo/Utils/TodoItemSerializer.cs @@ -30,6 +30,8 @@ public static async Task Serialize(IList items, string path = "todos.j public async static Task?> Deserialize(string path = "todos.json") { + if (!File.Exists(path)) return null; + try { using (StreamReader sr = new StreamReader(path)) diff --git a/W8G05A/TUITodo/Views/EditorView.cs b/W8G05A/TUITodo/Views/EditorView.cs index b0194fc..d44edc5 100644 --- a/W8G05A/TUITodo/Views/EditorView.cs +++ b/W8G05A/TUITodo/Views/EditorView.cs @@ -95,7 +95,7 @@ public EditorView() { Application.MainLoop.Invoke(() => { - Program.EditorView.FocusNext(); + descriptionTextView.SetFocus(); }); } }; diff --git a/W8G05A/TUITodo/Views/TaskListView.cs b/W8G05A/TUITodo/Views/TaskListView.cs index 0254140..f3c32da 100644 --- a/W8G05A/TUITodo/Views/TaskListView.cs +++ b/W8G05A/TUITodo/Views/TaskListView.cs @@ -1,33 +1,63 @@ -using System; +using NStack; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Terminal.Gui; +using Terminal.Gui.Trees; using TUITodo.Utils; namespace TUITodo.Views { internal class TaskListView : View { - public TaskTree taskTree = new TaskTree - { - X = 0, - Y = 0, - Width = Dim.Fill(), - Height = Dim.Fill() - }; + TaskTree taskTree; + TextField searchField; + Label searchLabel; + Label description; public StatusBar statusBar = new(); TodoItem? selectedItem => (TodoItem)taskTree.SelectedObject; + List todos = new(); public TaskListView() { + taskTree = new() + { + X = 0, + Y = 1, + Width = Dim.Percent(70), + Height = Dim.Fill() + }; + searchLabel = new() + { + X = Pos.Right(taskTree) + 2, + Y = 0, + Width = Dim.Fill(), + Height = 1, + Text = "Search" + }; + searchField = new() + { + X = Pos.Right(taskTree) + 2, + Y = Pos.Bottom(searchLabel), + Width = Dim.Fill(), + Height = 2, + }; + description = new() + { + X = Pos.Right(taskTree) + 2, + Y = Pos.Bottom(searchField), + Width = Dim.Fill(), + Height = 10, + }; + #region status bar items statusBar.Items = new StatusItem[] { - //Vannak olyan keyek, amikre az életbe nem érzékel + //Vannak olyan keyek, amiket az életbe nem érzékel //Totál random hogy melyik működik //Ezért hülyeségek a gombválasztások new (Key.E, "~Shift + E~ Edit task", () => { @@ -53,18 +83,35 @@ public TaskListView() $"Are you sure you want to delete the task '{selectedItem.name}'?", "Delete it", "Nevermind") == 0) taskTree.DeleteItem(selectedItem); } + }), + new (Key.F, "~Shift + F~ Search", () => { + searchField.SetFocus(); }) }; #endregion - Add(taskTree); + Add(taskTree, searchField, searchLabel, description); + + searchField.TextChanged += (ustring searchText) => { + //a searchText valamiért le van maradva itt eggyel szóval nem azt használjuk + taskTree.Items = FilterTree((string)searchField.Text); + taskTree.ExpandAll(); + }; + + taskTree.SelectionChanged += (object? sender, SelectionChangedEventArgs e) => + { + if (selectedItem != null) description.Text = selectedItem.description; + }; + + } public async Task LoadSavedTasks() { - List savedTodos = await TodoItemSerializer.Deserialize() ?? new List(); - savedTodos.ForEach(t => taskTree.AddItem(t)); + todos = await TodoItemSerializer.Deserialize() ?? new List(); + + todos.ForEach(t => taskTree.AddItem(t)); taskTree.ExpandAll(); @@ -76,5 +123,19 @@ public async Task SaveTasks() { await TodoItemSerializer.Serialize(taskTree.Items); } + + List FilterTree(string query) + { + query = query.Trim().ToLower(); + + if (query == "") return todos; + + return todos.Where(t => MatchesQuery(t,query) || t.Subtasks.Any(t => MatchesQuery(t, query))).ToList(); + } + + bool MatchesQuery(TodoItem item, string query) + { + return item.name.ToLower().Contains(query) || item.description.Contains(query); + } } } diff --git a/W8G05A/TUITodo/Views/TaskTree.cs b/W8G05A/TUITodo/Views/TaskTree.cs index bc03620..73a5f3d 100644 --- a/W8G05A/TUITodo/Views/TaskTree.cs +++ b/W8G05A/TUITodo/Views/TaskTree.cs @@ -16,7 +16,7 @@ internal class TaskTree : TreeView public List Items { get => items; - protected set + set { items = value; base.ClearObjects();