From de4cc138ae3c632a688ca098a57ecdd920ade882 Mon Sep 17 00:00:00 2001 From: FlorianDahn Date: Sun, 14 Jul 2019 22:27:16 +0200 Subject: [PATCH] - New control: CalendarPicker - New control: MonthPicker - New control TreeView - adding examples for all 3 - small change on Progress Bar Control - due latest changes on the base it is now easier to create controls which will be rendered (or not)in several forms depending on user context --- .../Tests/Controls/CalendarPickerForm.cs | 79 +++++ .../Tests/Controls/MonthPickerForm.cs | 77 +++++ .../Tests/Controls/TreeViewForms.cs | 99 +++++++ TelegramBotBase/Controls/CalendarPicker.cs | 277 ++++++++++++++++++ TelegramBotBase/Controls/MonthPicker.cs | 26 ++ TelegramBotBase/Controls/ProgressBar.cs | 3 +- TelegramBotBase/Controls/TreeView.cs | 120 ++++++++ TelegramBotBase/Controls/TreeViewNode.cs | 65 ++++ TelegramBotBase/Tools/Arrays.cs | 22 ++ TelegramBotBase/Tools/Time.cs | 36 +++ 10 files changed, 803 insertions(+), 1 deletion(-) create mode 100644 TelegramBaseTest/Tests/Controls/CalendarPickerForm.cs create mode 100644 TelegramBaseTest/Tests/Controls/MonthPickerForm.cs create mode 100644 TelegramBaseTest/Tests/Controls/TreeViewForms.cs create mode 100644 TelegramBotBase/Controls/CalendarPicker.cs create mode 100644 TelegramBotBase/Controls/MonthPicker.cs create mode 100644 TelegramBotBase/Controls/TreeView.cs create mode 100644 TelegramBotBase/Controls/TreeViewNode.cs create mode 100644 TelegramBotBase/Tools/Arrays.cs create mode 100644 TelegramBotBase/Tools/Time.cs diff --git a/TelegramBaseTest/Tests/Controls/CalendarPickerForm.cs b/TelegramBaseTest/Tests/Controls/CalendarPickerForm.cs new file mode 100644 index 0000000..8b85b16 --- /dev/null +++ b/TelegramBaseTest/Tests/Controls/CalendarPickerForm.cs @@ -0,0 +1,79 @@ +using System; + +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TelegramBotBase.Base; +using TelegramBotBase.Form; +using TelegramBotBase.Controls; + +namespace TelegramBaseTest.Tests.Controls +{ + public class CalendarPickerForm : AutoCleanForm + { + + public CalendarPicker Picker { get; set; } + + int? selectedDateMessage { get; set; } + + public CalendarPickerForm() + { + this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm; + this.Init += CalendarPickerForm_Init; + } + + private async Task CalendarPickerForm_Init(object sender, InitEventArgs e) + { + this.Picker = new CalendarPicker(); + this.Picker.Title = "Datum auswählen / Pick date"; + + this.AddControl(Picker); + } + + + public override async Task Action(MessageResult message) + { + + switch(message.RawData) + { + case "back": + + var s = new Start(); + + await this.NavigateTo(s); + + break; + } + + } + + public override async Task Render(MessageResult message) + { + String s = ""; + + s = "Selected date is " + this.Picker.SelectedDate.ToShortDateString() + "\r\n"; + s += "Selected month is " + this.Picker.Culture.DateTimeFormat.MonthNames[this.Picker.VisibleMonth.Month - 1] + "\r\n"; + s += "Selected year is " + this.Picker.VisibleMonth.Year.ToString(); + + ButtonForm bf = new ButtonForm(); + bf.AddButtonRow(new ButtonBase("Back","back")); + + if (selectedDateMessage != null) + { + await this.Device.Edit(this.selectedDateMessage.Value, s, bf); + } + else + { + var m = await this.Device.Send(s, bf); + this.selectedDateMessage = m.MessageId; + } + + + + } + + + + } +} diff --git a/TelegramBaseTest/Tests/Controls/MonthPickerForm.cs b/TelegramBaseTest/Tests/Controls/MonthPickerForm.cs new file mode 100644 index 0000000..1d27fa0 --- /dev/null +++ b/TelegramBaseTest/Tests/Controls/MonthPickerForm.cs @@ -0,0 +1,77 @@ +using System; + +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TelegramBotBase.Base; +using TelegramBotBase.Form; +using TelegramBotBase.Controls; + +namespace TelegramBaseTest.Tests.Controls +{ + public class MonthPickerForm : AutoCleanForm + { + + public MonthPicker Picker { get; set; } + + int? selectedDateMessage { get; set; } + + public MonthPickerForm() + { + this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm; + this.Init += MonthPickerForm_Init; + } + + private async Task MonthPickerForm_Init(object sender, InitEventArgs e) + { + this.Picker = new MonthPicker(); + this.Picker.Title = "Monat auswählen / Pick month"; + this.AddControl(Picker); + } + + + public override async Task Action(MessageResult message) + { + + switch(message.RawData) + { + case "back": + + var s = new Start(); + + await this.NavigateTo(s); + + break; + } + + } + + public override async Task Render(MessageResult message) + { + String s = ""; + + s += "Selected month is " + this.Picker.Culture.DateTimeFormat.MonthNames[this.Picker.SelectedDate.Month - 1] + "\r\n"; + s += "Selected year is " + this.Picker.VisibleMonth.Year.ToString(); + + ButtonForm bf = new ButtonForm(); + bf.AddButtonRow(new ButtonBase("Back","back")); + + if (selectedDateMessage != null) + { + await this.Device.Edit(this.selectedDateMessage.Value, s, bf); + } + else + { + var m = await this.Device.Send(s, bf); + this.selectedDateMessage = m.MessageId; + } + + + + } + + + + } +} diff --git a/TelegramBaseTest/Tests/Controls/TreeViewForms.cs b/TelegramBaseTest/Tests/Controls/TreeViewForms.cs new file mode 100644 index 0000000..3799964 --- /dev/null +++ b/TelegramBaseTest/Tests/Controls/TreeViewForms.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TelegramBotBase.Form; +using TelegramBotBase.Controls; +using TelegramBotBase.Base; + +namespace TelegramBaseTest.Tests.Controls +{ + public class TreeViewForms : AutoCleanForm + { + public TreeView view { get; set; } + + private int? MessageId { get; set; } + + public TreeViewForms() + { + this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm; + this.Init += TreeViewForms_Init; + } + + private async Task TreeViewForms_Init(object sender, TelegramBotBase.Base.InitEventArgs e) + { + view = new TreeView(); + + var tvn = new TreeViewNode("Cars", "cars"); + + tvn.AddNode(new TreeViewNode("Porsche", "porsche", new TreeViewNode("Website", "web", "https://www.porsche.com/germany/"), new TreeViewNode("911", "911"), new TreeViewNode("918 Spyder", "918"))); + tvn.AddNode(new TreeViewNode("BMW", "bmw")); + tvn.AddNode(new TreeViewNode("Audi", "audi")); + tvn.AddNode(new TreeViewNode("VW", "vw")); + tvn.AddNode(new TreeViewNode("Lamborghini", "lamborghini")); + + view.Nodes.Add(tvn); + + tvn = new TreeViewNode("Fruits", "fruits"); + + tvn.AddNode(new TreeViewNode("Apple", "apple")); + tvn.AddNode(new TreeViewNode("Orange", "orange")); + tvn.AddNode(new TreeViewNode("Lemon", "lemon")); + + view.Nodes.Add(tvn); + + this.AddControl(view); + + } + + public override async Task Action(MessageResult message) + { + await message.ConfirmAction(); + + if (message.Handled) + return; + + switch (message.RawData) + { + case "back": + + message.Handled = true; + + var start = new Start(); + + await this.NavigateTo(start); + + break; + + } + + } + + public override async Task Render(MessageResult message) + { + String s = ""; + + s += "Selected Node: " + (this.view.SelectedNode?.Text ?? "(null)") + "\r\n"; + + s += "Visible Node: " + (this.view.VisibleNode?.Text ?? "(top)") + "\r\n"; + + s += "Visible Path: " + this.view.GetPath() + "\r\n"; + s += "Selected Path: " + (this.view.SelectedNode?.GetPath() ?? "(null)") + "\r\n"; + + ButtonForm bf = new ButtonForm(); + bf.AddButtonRow(new ButtonBase("Back", "back")); + + if (MessageId != null) + { + await this.Device.Edit(this.MessageId.Value, s, bf); + } + else + { + var m = await this.Device.Send(s, bf); + this.MessageId = m.MessageId; + } + } + + } +} diff --git a/TelegramBotBase/Controls/CalendarPicker.cs b/TelegramBotBase/Controls/CalendarPicker.cs new file mode 100644 index 0000000..027e4df --- /dev/null +++ b/TelegramBotBase/Controls/CalendarPicker.cs @@ -0,0 +1,277 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TelegramBotBase.Base; +using TelegramBotBase.Enums; +using TelegramBotBase.Form; + +using static TelegramBotBase.Tools.Arrays; +using static TelegramBotBase.Tools.Time; + +namespace TelegramBotBase.Controls +{ + public class CalendarPicker : Base.ControlBase + { + + public DateTime SelectedDate { get; set; } + + public DateTime VisibleMonth { get; set; } + + public DayOfWeek FirstDayOfWeek { get; set; } + + public CultureInfo Culture { get; set; } + + + private int? MessageId { get; set; } + + public String Title { get; set; } = "Pick Date"; + + public eMonthPickerMode PickerMode { get; set; } + + public bool EnableDayView { get; set; } = true; + + public bool EnableMonthView { get; set; } = true; + + public bool EnableYearView { get; set; } = true; + + public CalendarPicker() + { + this.SelectedDate = DateTime.Today; + this.VisibleMonth = DateTime.Today; + this.FirstDayOfWeek = DayOfWeek.Monday; + this.Culture = new CultureInfo("de-de"); + this.PickerMode = eMonthPickerMode.day; + } + + + + + public override async Task Action(MessageResult result) + { + await result.ConfirmAction(); + + switch (result.RawData) + { + case "next": + + switch (this.PickerMode) + { + case eMonthPickerMode.day: + this.VisibleMonth = this.VisibleMonth.AddMonths(1); + break; + + case eMonthPickerMode.month: + this.VisibleMonth = this.VisibleMonth.AddYears(1); + break; + + case eMonthPickerMode.year: + this.VisibleMonth = this.VisibleMonth.AddYears(10); + break; + } + + + break; + case "prev": + + switch (this.PickerMode) + { + case eMonthPickerMode.day: + this.VisibleMonth = this.VisibleMonth.AddMonths(-1); + break; + + case eMonthPickerMode.month: + this.VisibleMonth = this.VisibleMonth.AddYears(-1); + break; + + case eMonthPickerMode.year: + this.VisibleMonth = this.VisibleMonth.AddYears(-10); + break; + } + + break; + + case "monthtitle": + + if (this.EnableMonthView) + { + this.PickerMode = eMonthPickerMode.month; + } + + break; + + case "yeartitle": + + if (this.EnableYearView) + { + this.PickerMode = eMonthPickerMode.year; + } + + break; + case "yearstitle": + + if (this.EnableMonthView) + { + this.PickerMode = eMonthPickerMode.month; + } + + this.VisibleMonth = this.SelectedDate; + + break; + + default: + + int day = 0; + if (result.RawData.StartsWith("d-") && int.TryParse(result.RawData.Split('-')[1], out day)) + { + this.SelectedDate = new DateTime(this.VisibleMonth.Year, this.VisibleMonth.Month, day); + } + + int month = 0; + if (result.RawData.StartsWith("m-") && int.TryParse(result.RawData.Split('-')[1], out month)) + { + this.SelectedDate = new DateTime(this.VisibleMonth.Year, month, 1); + this.VisibleMonth = this.SelectedDate; + + if (this.EnableDayView) + { + this.PickerMode = eMonthPickerMode.day; + } + } + + int year = 0; + if (result.RawData.StartsWith("y-") && int.TryParse(result.RawData.Split('-')[1], out year)) + { + this.SelectedDate = new DateTime(year, SelectedDate.Month, SelectedDate.Day); + this.VisibleMonth = this.SelectedDate; + + if (this.EnableMonthView) + { + this.PickerMode = eMonthPickerMode.month; + } + + } + + break; + } + + + + } + + + + public override async Task Render(MessageResult result) + { + + + + ButtonForm bf = new ButtonForm(); + + switch (this.PickerMode) + { + case eMonthPickerMode.day: + + var month = this.VisibleMonth; + + string[] dayNamesNormal = this.Culture.DateTimeFormat.ShortestDayNames; + string[] dayNamesShifted = Shift(dayNamesNormal, (int)this.FirstDayOfWeek); + + bf.AddButtonRow(new ButtonBase("<<", "prev"), new ButtonBase(this.Culture.DateTimeFormat.MonthNames[month.Month - 1] + " " + month.Year.ToString(), "monthtitle"), new ButtonBase(">>", "next")); + + bf.AddButtonRow(dayNamesShifted.Select(a => new ButtonBase(a, a)).ToList()); + + //First Day of month + var firstDay = new DateTime(month.Year, month.Month, 1); + + //Last Day of month + var lastDay = firstDay.LastDayOfMonth(); + + //Start of Week where first day of month is (left border) + var start = firstDay.StartOfWeek(this.FirstDayOfWeek); + + //End of week where last day of month is (right border) + var end = lastDay.EndOfWeek(this.FirstDayOfWeek); + + for (int i = 0; i <= ((end - start).Days / 7); i++) + { + var lst = new List(); + for (int id = 0; id < 7; id++) + { + var d = start.AddDays((i * 7) + id); + if (d < firstDay | d > lastDay) + { + lst.Add(new ButtonBase("-", "m-" + d.Day.ToString())); + continue; + } + + var day = d.Day.ToString(); + + if (d == DateTime.Today) + { + day = "(" + day + ")"; + } + + lst.Add(new ButtonBase((this.SelectedDate == d ? "[" + day + "]" : day), "d-" + d.Day.ToString())); + } + bf.AddButtonRow(lst); + } + + break; + + case eMonthPickerMode.month: + + bf.AddButtonRow(new ButtonBase("<<", "prev"), new ButtonBase(this.VisibleMonth.Year.ToString("0000"), "yeartitle"), new ButtonBase(">>", "next")); + + var months = this.Culture.DateTimeFormat.MonthNames; + + var buttons = months.Select((a, b) => new ButtonBase((b == this.SelectedDate.Month - 1 && this.SelectedDate.Year == this.VisibleMonth.Year ? "[ " + a + " ]" : a), "m-" + (b + 1).ToString())); + + bf.AddSplitted(buttons, 2); + + break; + + case eMonthPickerMode.year: + + bf.AddButtonRow(new ButtonBase("<<", "prev"), new ButtonBase("Year", "yearstitle"), new ButtonBase(">>", "next")); + + var starti = Math.Floor(this.VisibleMonth.Year / 10f) * 10; + + for (int i = 0; i < 10; i++) + { + var m = starti + (i * 2); + bf.AddButtonRow(new ButtonBase((this.SelectedDate.Year == m ? "[ " + m.ToString() + " ]" : m.ToString()), "y-" + m.ToString()), new ButtonBase((this.SelectedDate.Year == (m + 1) ? "[ " + (m + 1).ToString() + " ]" : (m + 1).ToString()), "y-" + (m + 1).ToString())); + } + + break; + + } + + + if (this.MessageId != null) + { + var m = await this.Device.Edit(this.MessageId.Value, this.Title, bf); + } + else + { + var m = await this.Device.Send(this.Title, bf); + this.MessageId = m.MessageId; + } + } + + + + public override async Task Cleanup() + { + + if (this.MessageId != null) + { + await this.Device.DeleteMessage(this.MessageId.Value); + } + + } + + } +} diff --git a/TelegramBotBase/Controls/MonthPicker.cs b/TelegramBotBase/Controls/MonthPicker.cs new file mode 100644 index 0000000..93bf715 --- /dev/null +++ b/TelegramBotBase/Controls/MonthPicker.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TelegramBotBase.Base; +using TelegramBotBase.Enums; +using TelegramBotBase.Form; + +namespace TelegramBotBase.Controls +{ + public class MonthPicker : CalendarPicker + { + + + + public MonthPicker() + { + this.PickerMode = eMonthPickerMode.month; + this.EnableDayView = false; + } + + + } +} diff --git a/TelegramBotBase/Controls/ProgressBar.cs b/TelegramBotBase/Controls/ProgressBar.cs index 77678f6..36f91c3 100644 --- a/TelegramBotBase/Controls/ProgressBar.cs +++ b/TelegramBotBase/Controls/ProgressBar.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using TelegramBotBase.Base; namespace TelegramBotBase.Controls { @@ -220,7 +221,7 @@ namespace TelegramBotBase.Controls } - public async override Task Render() + public async override Task Render(MessageResult result) { if (!this.RenderNecessary) { diff --git a/TelegramBotBase/Controls/TreeView.cs b/TelegramBotBase/Controls/TreeView.cs new file mode 100644 index 0000000..250b842 --- /dev/null +++ b/TelegramBotBase/Controls/TreeView.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TelegramBotBase.Base; +using TelegramBotBase.Form; + +namespace TelegramBotBase.Controls +{ + public class TreeView : ControlBase + { + public List Nodes { get; set; } + + public TreeViewNode SelectedNode { get; set; } + + public TreeViewNode VisibleNode { get; set; } + + public String Title { get; set; } + + private int? MessageId { get; set; } + + public String MoveUpIcon { get; set; } = "🔼 up"; + + public TreeView() + { + this.Nodes = new List(); + this.Title = "Select node"; + } + + + public override async Task Action(MessageResult result) + { + await result.ConfirmAction(); + + if (result.Handled) + return; + + var value = result.RawData; + + switch (value) + { + case "up": + case "parent": + + this.VisibleNode = (this.VisibleNode?.ParentNode); + + result.Handled = true; + + break; + default: + + var n = (this.VisibleNode != null ? this.VisibleNode.FindNodeByValue(value) : this.Nodes.FirstOrDefault(a => a.Value == value)); + + if (n != null) + { + if (n.ChildNodes.Count > 0) + { + this.VisibleNode = n; + } + else + { + this.SelectedNode = (this.SelectedNode != n ? n : null); + } + + result.Handled = true; + } + + break; + + } + + } + + + public override async Task Render(MessageResult result) + { + var startnode = this.VisibleNode; + + var nodes = (startnode?.ChildNodes ?? this.Nodes); + + ButtonForm bf = new ButtonForm(); + + if (startnode != null) + { + bf.AddButtonRow(new ButtonBase(this.MoveUpIcon, "up"), new ButtonBase(startnode.Text, "parent")); + } + + foreach (var n in nodes) + { + var s = n.Text; + if (this.SelectedNode == n) + { + s = "[ " + s + " ]"; + } + + bf.AddButtonRow(new ButtonBase(s, n.Value, n.Url)); + } + + + + if (this.MessageId != null) + { + var m = await this.Device.Edit(this.MessageId.Value, this.Title, bf); + } + else + { + var m = await this.Device.Send(this.Title, bf); + this.MessageId = m.MessageId; + } + } + + public String GetPath() + { + return (this.VisibleNode?.GetPath() ?? "\\"); + } + + + } +} diff --git a/TelegramBotBase/Controls/TreeViewNode.cs b/TelegramBotBase/Controls/TreeViewNode.cs new file mode 100644 index 0000000..11630f4 --- /dev/null +++ b/TelegramBotBase/Controls/TreeViewNode.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TelegramBotBase.Controls +{ + public class TreeViewNode + { + public String Text { get; set; } + + public String Value { get; set; } + + public String Url { get; set; } + + public List ChildNodes { get; set; } = new List(); + + public TreeViewNode ParentNode { get; set; } + + public TreeViewNode(String Text, String Value) + { + this.Text = Text; + this.Value = Value; + } + + public TreeViewNode(String Text, String Value, String Url) : this(Text, Value) + { + this.Url = Url; + } + + public TreeViewNode(String Text, String Value, params TreeViewNode[] childnodes) : this(Text, Value) + { + foreach(var c in childnodes) + { + AddNode(c); + } + } + + + public void AddNode(TreeViewNode node) + { + node.ParentNode = this; + ChildNodes.Add(node); + } + + public TreeViewNode FindNodeByValue(String Value) + { + return this.ChildNodes.FirstOrDefault(a => a.Value == Value); + } + + public String GetPath() + { + String s = "\\" + this.Value; + var p = this; + while (p.ParentNode != null) + { + s = "\\" + p.ParentNode.Value + s; + p = p.ParentNode; + } + return s; + } + + } +} diff --git a/TelegramBotBase/Tools/Arrays.cs b/TelegramBotBase/Tools/Arrays.cs new file mode 100644 index 0000000..01efa34 --- /dev/null +++ b/TelegramBotBase/Tools/Arrays.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TelegramBotBase.Base; +using TelegramBotBase.Form; + +namespace TelegramBotBase.Tools +{ + public static class Arrays + { + public static T[] Shift(T[] array, int positions) + { + T[] copy = new T[array.Length]; + Array.Copy(array, 0, copy, array.Length - positions, positions); + Array.Copy(array, positions, copy, 0, array.Length - positions); + return copy; + } + } +} diff --git a/TelegramBotBase/Tools/Time.cs b/TelegramBotBase/Tools/Time.cs new file mode 100644 index 0000000..66cdb85 --- /dev/null +++ b/TelegramBotBase/Tools/Time.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TelegramBotBase.Tools +{ + public static class Time + { + public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek) + { + int diff = dt.DayOfWeek - startOfWeek; + if (diff < 0) + { + diff += 7; + } + return dt.AddDays(-1 * diff).Date; + } + + public static DateTime EndOfWeek(this DateTime dt, DayOfWeek startOfWeek) + { + return StartOfWeek(dt, startOfWeek).AddDays(6); + } + + public static DateTime FirstDayOfMonth(this DateTime date) + { + return new DateTime(date.Year, date.Month, 1); + } + + public static DateTime LastDayOfMonth(this DateTime date) + { + return FirstDayOfMonth(date).AddMonths(1).AddDays(-1); + } + } +}