diff --git a/TelegramBotBase/Args/ButtonClickedEventArgs.cs b/TelegramBotBase/Args/ButtonClickedEventArgs.cs index a99b9f8..ef9ef71 100644 --- a/TelegramBotBase/Args/ButtonClickedEventArgs.cs +++ b/TelegramBotBase/Args/ButtonClickedEventArgs.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using TelegramBotBase.Controls.Hybrid; using TelegramBotBase.Form; namespace TelegramBotBase.Args @@ -18,6 +19,8 @@ namespace TelegramBotBase.Args public object Tag { get; set; } + public ButtonRow Row { get; set; } + public ButtonClickedEventArgs() { @@ -36,5 +39,11 @@ namespace TelegramBotBase.Args this.Index = Index; } + public ButtonClickedEventArgs(ButtonBase button, int Index, ButtonRow row) + { + this.Button = button; + this.Index = Index; + this.Row = row; + } } } diff --git a/TelegramBotBase/Args/CheckedChangedEventArgs.cs b/TelegramBotBase/Args/CheckedChangedEventArgs.cs index 90d4014..4750334 100644 --- a/TelegramBotBase/Args/CheckedChangedEventArgs.cs +++ b/TelegramBotBase/Args/CheckedChangedEventArgs.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using TelegramBotBase.Controls.Hybrid; using TelegramBotBase.Form; namespace TelegramBotBase.Args @@ -17,7 +18,7 @@ namespace TelegramBotBase.Args /// /// Contains all buttons within this row, excluding the checkbox. /// - public List Row { get; set; } + public ButtonRow Row { get; set; } /// @@ -31,7 +32,7 @@ namespace TelegramBotBase.Args } - public CheckedChangedEventArgs(List row, int Index, bool Checked) + public CheckedChangedEventArgs(ButtonRow row, int Index, bool Checked) { this.Row = row; this.Index = Index; diff --git a/TelegramBotBase/Controls/Hybrid/ButtonGrid.cs b/TelegramBotBase/Controls/Hybrid/ButtonGrid.cs index fb116c7..d8bfdb1 100644 --- a/TelegramBotBase/Controls/Hybrid/ButtonGrid.cs +++ b/TelegramBotBase/Controls/Hybrid/ButtonGrid.cs @@ -10,6 +10,7 @@ using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.ReplyMarkups; using TelegramBotBase.Args; using TelegramBotBase.Base; +using TelegramBotBase.Datasources; using TelegramBotBase.Enums; using TelegramBotBase.Exceptions; using TelegramBotBase.Form; @@ -30,7 +31,26 @@ namespace TelegramBotBase.Controls.Hybrid private readonly EventHandlerList Events = new EventHandlerList(); - public ButtonForm ButtonsForm { get; set; } + /// + /// + /// + [Obsolete("This property is obsolete. Please use the DataSource property instead.")] + public ButtonForm ButtonsForm + { + get + { + return DataSource.ButtonForm; + } + set + { + DataSource = new ButtonFormDataSource(value); + } + } + + /// + /// Data source of the items. + /// + public ButtonFormDataSource DataSource { get; set; } public int? MessageId { get; set; } @@ -89,12 +109,12 @@ namespace TelegramBotBase.Controls.Hybrid /// /// Layout of the buttons which should be displayed always on top. /// - public List HeadLayoutButtonRow { get; set; } + public ButtonRow HeadLayoutButtonRow { get; set; } /// /// Layout of columns which should be displayed below the header /// - public List SubHeadLayoutButtonRow { get; set; } + public ButtonRow SubHeadLayoutButtonRow { get; set; } /// /// Defines which type of Button Keyboard should be rendered. @@ -123,8 +143,7 @@ namespace TelegramBotBase.Controls.Hybrid public ButtonGrid() { - this.ButtonsForm = new ButtonForm(); - + this.DataSource = new ButtonFormDataSource(); } @@ -136,7 +155,7 @@ namespace TelegramBotBase.Controls.Hybrid public ButtonGrid(ButtonForm form) { - this.ButtonsForm = form; + this.DataSource = new ButtonFormDataSource(form); } @@ -188,22 +207,49 @@ namespace TelegramBotBase.Controls.Hybrid if (!result.IsFirstHandler) return; - if (result.MessageText == null) + if (result.MessageText == null || result.MessageText == "") return; - var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText) - ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText) - ?? ButtonsForm.ToList().FirstOrDefault(a => a.Text.Trim() == result.MessageText); + var matches = new List(); + ButtonRow match = null; + int index = -1; - var index = ButtonsForm.FindRowByButton(button); - - if (button != null) + if (HeadLayoutButtonRow?.Matches(result.MessageText) ?? false) { - await OnButtonClicked(new ButtonClickedEventArgs(button, index)); + match = HeadLayoutButtonRow; + goto check; + } - //Remove button click message - if (this.DeleteReplyMessage) - await Device.DeleteMessage(result.MessageId); + if (SubHeadLayoutButtonRow?.Matches(result.MessageText) ?? false) + { + match = SubHeadLayoutButtonRow; + goto check; + } + + var br = DataSource.FindRow(result.MessageText); + if (br != null) + { + match = br.Item1; + index = br.Item2; + } + + + //var button = HeadLayoutButtonRow?. .FirstOrDefault(a => a.Text.Trim() == result.MessageText) + // ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText); + + // bf.ToList().FirstOrDefault(a => a.Text.Trim() == result.MessageText) + + //var index = bf.FindRowByButton(button); + + check: + + //Remove button click message + if (this.DeleteReplyMessage) + await Device.DeleteMessage(result.MessageId); + + if (match != null) + { + await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.MessageText), index, match)); result.Handled = true; return; @@ -261,22 +307,48 @@ namespace TelegramBotBase.Controls.Hybrid if (!result.IsFirstHandler) return; - await result.ConfirmAction(this.ConfirmationText ?? ""); - //Find clicked button depending on Text or Value (depending on markup type) if (this.KeyboardType != eKeyboardType.InlineKeyBoard) return; + await result.ConfirmAction(this.ConfirmationText ?? ""); - var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData) - ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData) - ?? ButtonsForm.ToList().FirstOrDefault(a => a.Value == result.RawData); + ButtonRow match = null; + int index = -1; - var index = ButtonsForm.FindRowByButton(button); - - if (button != null) + if (HeadLayoutButtonRow?.Matches(result.RawData, false) ?? false) { - await OnButtonClicked(new ButtonClickedEventArgs(button, index)); + match = HeadLayoutButtonRow; + goto check; + } + + if (SubHeadLayoutButtonRow?.Matches(result.RawData, false) ?? false) + { + match = SubHeadLayoutButtonRow; + goto check; + } + + var br = DataSource.FindRow(result.RawData, false); + if (br != null) + { + match = br.Item1; + index = br.Item2; + } + + + + //var bf = DataSource.ButtonForm; + + //var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData) + // ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData) + // ?? bf.ToList().FirstOrDefault(a => a.Value == result.RawData); + + //var index = bf.FindRowByButton(button); + + check: + if (match != null) + { + await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.RawData, false), index, match)); result.Handled = true; return; @@ -313,28 +385,28 @@ namespace TelegramBotBase.Controls.Hybrid { case eKeyboardType.InlineKeyBoard: - if (ButtonsForm.Rows > Constants.Telegram.MaxInlineKeyBoardRows && !this.EnablePaging) + if (DataSource.RowCount > Constants.Telegram.MaxInlineKeyBoardRows && !this.EnablePaging) { - throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxInlineKeyBoardRows }; + throw new MaximumRowsReachedException() { Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxInlineKeyBoardRows }; } - if (ButtonsForm.Cols > Constants.Telegram.MaxInlineKeyBoardCols) + if (DataSource.ColumnCount > Constants.Telegram.MaxInlineKeyBoardCols) { - throw new MaximumColsException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxInlineKeyBoardCols }; + throw new MaximumColsException() { Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxInlineKeyBoardCols }; } break; case eKeyboardType.ReplyKeyboard: - if (ButtonsForm.Rows > Constants.Telegram.MaxReplyKeyboardRows && !this.EnablePaging) + if (DataSource.RowCount > Constants.Telegram.MaxReplyKeyboardRows && !this.EnablePaging) { - throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxReplyKeyboardRows }; + throw new MaximumRowsReachedException() { Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxReplyKeyboardRows }; } - if (ButtonsForm.Cols > Constants.Telegram.MaxReplyKeyboardCols) + if (DataSource.ColumnCount > Constants.Telegram.MaxReplyKeyboardCols) { - throw new MaximumColsException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxReplyKeyboardCols }; + throw new MaximumColsException() { Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxReplyKeyboardCols }; } break; @@ -351,22 +423,21 @@ namespace TelegramBotBase.Controls.Hybrid this.RenderNecessary = false; - Message m = null; + ButtonForm form = this.DataSource.PickItems(CurrentPageIndex * ItemRowsPerPage, ItemRowsPerPage, (this.EnableSearch ? this.SearchQuery : null)); - ButtonForm form = this.ButtonsForm; - if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "") - { - form = form.FilterDuplicate(this.SearchQuery, true); - } - else - { - form = form.Duplicate(); - } + //if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "") + //{ + // form = form.FilterDuplicate(this.SearchQuery, true); + //} + //else + //{ + // form = form.Duplicate(); + //} if (this.EnablePaging) { - form = GeneratePagingView(form); + IntegratePagingView(form); } if (this.HeadLayoutButtonRow != null && HeadLayoutButtonRow.Count > 0) @@ -386,26 +457,37 @@ namespace TelegramBotBase.Controls.Hybrid } } + Message m = null; + switch (this.KeyboardType) { //Reply Keyboard could only be updated with a new keyboard. case eKeyboardType.ReplyKeyboard: - if (this.MessageId != null) + + if (form.Count == 0) { - if (form.Count == 0) + if (this.MessageId != null) { await this.Device.HideReplyKeyboard(); this.MessageId = null; - return; } - if (this.DeletePreviousMessage) - await this.Device.DeleteMessage(this.MessageId.Value); + return; } - if (form.Count == 0) - return; + //if (this.MessageId != null) + //{ + // if (form.Count == 0) + // { + // await this.Device.HideReplyKeyboard(); + // this.MessageId = null; + // return; + // } + //} + + //if (form.Count == 0) + // return; var rkm = (ReplyKeyboardMarkup)form; @@ -413,6 +495,10 @@ namespace TelegramBotBase.Controls.Hybrid rkm.OneTimeKeyboard = this.OneTimeKeyboard; m = await this.Device.Send(this.Title, rkm, disableNotification: true, parseMode: MessageParseMode, MarkdownV2AutoEscape: false); + //Prevent flicker of keyboard + if (this.DeletePreviousMessage && this.MessageId != null) + await this.Device.DeleteMessage(this.MessageId.Value); + break; case eKeyboardType.InlineKeyBoard: @@ -443,47 +529,31 @@ namespace TelegramBotBase.Controls.Hybrid } - private ButtonForm GeneratePagingView(ButtonForm dataForm) + private void IntegratePagingView(ButtonForm dataForm) { - - ButtonForm bf = new ButtonForm(); - - - for (int i = 0; i < this.MaximumRow - LayoutRows; i++) - { - int it = (this.CurrentPageIndex * (this.MaximumRow - LayoutRows)) + i; - - if (it > dataForm.Rows - 1) - break; - - bf.AddButtonRow(dataForm[it]); - } - //No Items - if (this.ButtonsForm.Count == 0) + if (dataForm.Rows == 0) { - bf.AddButtonRow(new ButtonBase(NoItemsLabel, "$")); + dataForm.AddButtonRow(new ButtonBase(NoItemsLabel, "$")); } if (this.IsNavigationBarVisible) { //🔍 - List lst = new List(); - lst.Add(new ButtonBase(PreviousPageLabel, "$previous$")); - lst.Add(new ButtonBase(String.Format(Localizations.Default.Language["ButtonGrid_CurrentPage"], this.CurrentPageIndex + 1, this.PageCount), "$site$")); - lst.Add(new ButtonBase(NextPageLabel, "$next$")); + ButtonRow row = new ButtonRow(); + row.Add(new ButtonBase(PreviousPageLabel, "$previous$")); + row.Add(new ButtonBase(String.Format(Localizations.Default.Language["ButtonGrid_CurrentPage"], this.CurrentPageIndex + 1, this.PageCount), "$site$")); + row.Add(new ButtonBase(NextPageLabel, "$next$")); if (this.EnableSearch) { - lst.Insert(2, new ButtonBase("🔍 " + (this.SearchQuery ?? ""), "$search$")); + row.Insert(2, new ButtonBase("🔍 " + (this.SearchQuery ?? ""), "$search$")); } - bf.InsertButtonRow(0, lst); + dataForm.InsertButtonRow(0, row); - bf.AddButtonRow(lst); + dataForm.AddButtonRow(row); } - - return bf; } public bool PagingNecessary @@ -545,7 +615,7 @@ namespace TelegramBotBase.Controls.Hybrid { get { - return this.LayoutRows + ButtonsForm.Rows; + return this.LayoutRows + DataSource.RowCount; } } @@ -572,24 +642,40 @@ namespace TelegramBotBase.Controls.Hybrid } } + /// + /// Returns the number of item rows per page. + /// + public int ItemRowsPerPage + { + get + { + return this.MaximumRow - this.LayoutRows; + } + } + + /// + /// Return the number of pages. + /// public int PageCount { get { - if (this.ButtonsForm.Count == 0) + if (DataSource.RowCount == 0) return 1; - var bf = this.ButtonsForm; + //var bf = this.DataSource.PickAllItems(this.EnableSearch ? this.SearchQuery : null); - if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "") - { - bf = bf.FilterDuplicate(this.SearchQuery); - } + var max = this.DataSource.CalculateMax(this.EnableSearch ? this.SearchQuery : null); - if (bf.Rows == 0) + //if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "") + //{ + // bf = bf.FilterDuplicate(this.SearchQuery); + //} + + if (max == 0) return 1; - return (int)Math.Ceiling((decimal)(bf.Rows / (decimal)(MaximumRow - this.LayoutRows))); + return (int)Math.Ceiling((decimal)((decimal)max / (decimal)ItemRowsPerPage)); } } diff --git a/TelegramBotBase/Controls/Hybrid/ButtonRow.cs b/TelegramBotBase/Controls/Hybrid/ButtonRow.cs new file mode 100644 index 0000000..2d0deab --- /dev/null +++ b/TelegramBotBase/Controls/Hybrid/ButtonRow.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using TelegramBotBase.Form; + +namespace TelegramBotBase.Controls.Hybrid +{ + [DebuggerDisplay("{Count} columns")] + public class ButtonRow + { + + List __buttons = new List(); + + public ButtonRow() + { + + + } + + public ButtonRow(params ButtonBase[] buttons) + { + __buttons = buttons.ToList(); + } + + + public ButtonBase this[int index] + { + get + { + return __buttons[index]; + } + } + + public int Count + { + get + { + return __buttons.Count; + } + } + + public void Add(ButtonBase button) + { + __buttons.Add(button); + } + + public void AddRange(ButtonBase button) + { + __buttons.Add(button); + } + + public void Insert(int index, ButtonBase button) + { + __buttons.Insert(index, button); + } + + public IEnumerator GetEnumerator() + { + return __buttons.GetEnumerator(); + } + + public ButtonBase[] ToArray() + { + return __buttons.ToArray(); + } + + public List ToList() + { + return __buttons.ToList(); + } + + public bool Matches(String text, bool useText = true) + { + foreach (var b in __buttons) + { + if (useText && b.Text.Trim().Equals(text, StringComparison.InvariantCultureIgnoreCase)) + return true; + + if (!useText && b.Value.Equals(text, StringComparison.InvariantCultureIgnoreCase)) + return true; + } + return false; + } + + /// + /// Returns the button inside of the row which matches. + /// + /// + /// + /// + public ButtonBase GetButtonMatch(String text, bool useText = true) + { + foreach (var b in __buttons) + { + if (useText && b.Text.Trim().Equals(text, StringComparison.InvariantCultureIgnoreCase)) + return b; + if (!useText && b.Value.Equals(text, StringComparison.InvariantCultureIgnoreCase)) + return b; + } + return null; + } + + public static implicit operator ButtonRow(List list) + { + return new ButtonRow() { __buttons = list }; + } + } +} diff --git a/TelegramBotBase/Controls/Hybrid/CheckedButtonList.cs b/TelegramBotBase/Controls/Hybrid/CheckedButtonList.cs index fe3b8c9..9c2e414 100644 --- a/TelegramBotBase/Controls/Hybrid/CheckedButtonList.cs +++ b/TelegramBotBase/Controls/Hybrid/CheckedButtonList.cs @@ -10,6 +10,7 @@ using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.ReplyMarkups; using TelegramBotBase.Args; using TelegramBotBase.Base; +using TelegramBotBase.Datasources; using TelegramBotBase.Enums; using TelegramBotBase.Exceptions; using TelegramBotBase.Form; @@ -32,7 +33,23 @@ namespace TelegramBotBase.Controls.Hybrid private readonly EventHandlerList Events = new EventHandlerList(); - public ButtonForm ButtonsForm { get; set; } + [Obsolete("This property is obsolete. Please use the DataSource property instead.")] + public ButtonForm ButtonsForm + { + get + { + return DataSource.ButtonForm; + } + set + { + DataSource = new ButtonFormDataSource(value); + } + } + + /// + /// Data source of the items. + /// + public ButtonFormDataSource DataSource { get; set; } List CheckedRows { get; set; } = new List(); @@ -51,6 +68,11 @@ namespace TelegramBotBase.Controls.Hybrid public bool DeletePreviousMessage { get; set; } = true; + /// + /// Removes the reply message from a user. + /// + public bool DeleteReplyMessage { get; set; } = true; + /// /// Parsemode of the message. /// @@ -92,12 +114,12 @@ namespace TelegramBotBase.Controls.Hybrid /// /// Layout of the buttons which should be displayed always on top. /// - public List HeadLayoutButtonRow { get; set; } + public ButtonRow HeadLayoutButtonRow { get; set; } /// /// Layout of columns which should be displayed below the header /// - public List SubHeadLayoutButtonRow { get; set; } + public ButtonRow SubHeadLayoutButtonRow { get; set; } /// /// Defines which type of Button Keyboard should be rendered. @@ -126,7 +148,7 @@ namespace TelegramBotBase.Controls.Hybrid public CheckedButtonList() { - this.ButtonsForm = new ButtonForm(); + this.DataSource = new ButtonFormDataSource(); } @@ -139,7 +161,7 @@ namespace TelegramBotBase.Controls.Hybrid public CheckedButtonList(ButtonForm form) { - this.ButtonsForm = form; + this.DataSource = new ButtonFormDataSource(form); } public event AsyncEventHandler ButtonClicked @@ -198,16 +220,50 @@ namespace TelegramBotBase.Controls.Hybrid if (!result.IsFirstHandler) return; - if (result.MessageText == null) + if (result.MessageText == null || result.MessageText == "") return; - var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText) - ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText) - ?? ButtonsForm.ToList().FirstOrDefault(a => a.Text.Trim() == result.MessageText); + var matches = new List(); + ButtonRow match = null; + int index = -1; - var index = ButtonsForm.FindRowByButton(button); + if (HeadLayoutButtonRow?.Matches(result.MessageText) ?? false) + { + match = HeadLayoutButtonRow; + goto check; + } - if (button == null) + if (SubHeadLayoutButtonRow?.Matches(result.MessageText) ?? false) + { + match = SubHeadLayoutButtonRow; + goto check; + } + + var br = DataSource.FindRow(result.MessageText); + if (br != null) + { + match = br.Item1; + index = br.Item2; + } + + + //var button = HeadLayoutButtonRow?. .FirstOrDefault(a => a.Text.Trim() == result.MessageText) + // ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText); + + // bf.ToList().FirstOrDefault(a => a.Text.Trim() == result.MessageText) + + //var index = bf.FindRowByButton(button); + + + + check: + + + //Remove button click message + if (this.DeleteReplyMessage) + await Device.DeleteMessage(result.MessageId); + + if (match == null) { if (result.MessageText == PreviousPageLabel) { @@ -226,7 +282,7 @@ namespace TelegramBotBase.Controls.Hybrid else if (result.MessageText.EndsWith(CheckedIconLabel)) { var s = result.MessageText.Split(' ', '.'); - index = int.Parse(s[0]); + index = int.Parse(s[0]) - 1; if (!this.CheckedRows.Contains(index)) return; @@ -241,7 +297,7 @@ namespace TelegramBotBase.Controls.Hybrid else if (result.MessageText.EndsWith(UncheckedIconLabel)) { var s = result.MessageText.Split(' ', '.'); - index = int.Parse(s[0]); + index = int.Parse(s[0]) - 1; if (this.CheckedRows.Contains(index)) return; @@ -285,14 +341,18 @@ namespace TelegramBotBase.Controls.Hybrid } - await OnButtonClicked(new ButtonClickedEventArgs(button, index)); - - //Remove button click message - if (this.DeletePreviousMessage) - await Device.DeleteMessage(result.MessageId); + await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.MessageText), index, match)); result.Handled = true; + //await OnButtonClicked(new ButtonClickedEventArgs(button, index)); + + ////Remove button click message + //if (this.DeletePreviousMessage) + // await Device.DeleteMessage(result.MessageId); + + //result.Handled = true; + } public async override Task Action(MessageResult result, string value = null) @@ -309,20 +369,55 @@ namespace TelegramBotBase.Controls.Hybrid await result.ConfirmAction(this.ConfirmationText ?? ""); - var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData) - ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData) - ?? ButtonsForm.ToList().FirstOrDefault(a => a.Value == result.RawData); + ButtonRow match = null; + int index = -1; - var index = ButtonsForm.FindRowByButton(button); - - if (button != null) + if (HeadLayoutButtonRow?.Matches(result.RawData, false) ?? false) { - await OnButtonClicked(new ButtonClickedEventArgs(button, index)); + match = HeadLayoutButtonRow; + goto check; + } + + if (SubHeadLayoutButtonRow?.Matches(result.RawData, false) ?? false) + { + match = SubHeadLayoutButtonRow; + goto check; + } + + var br = DataSource.FindRow(result.RawData, false); + if (br != null) + { + match = br.Item1; + index = br.Item2; + } + + + + //var bf = DataSource.ButtonForm; + + //var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData) + // ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData) + // ?? bf.ToList().FirstOrDefault(a => a.Value == result.RawData); + + //var index = bf.FindRowByButton(button); + + check: + if (match != null) + { + await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.RawData, false), index, match)); result.Handled = true; return; } + //if (button != null) + //{ + // await OnButtonClicked(new ButtonClickedEventArgs(button, index)); + + // result.Handled = true; + // return; + //} + switch (result.RawData) { case "$previous$": @@ -396,28 +491,28 @@ namespace TelegramBotBase.Controls.Hybrid { case eKeyboardType.InlineKeyBoard: - if (ButtonsForm.Rows > Constants.Telegram.MaxInlineKeyBoardRows && !this.EnablePaging) + if (DataSource.RowCount > Constants.Telegram.MaxInlineKeyBoardRows && !this.EnablePaging) { - throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxInlineKeyBoardRows }; + throw new MaximumRowsReachedException() { Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxInlineKeyBoardRows }; } - if (ButtonsForm.Cols > Constants.Telegram.MaxInlineKeyBoardCols) + if (DataSource.ColumnCount > Constants.Telegram.MaxInlineKeyBoardCols) { - throw new MaximumColsException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxInlineKeyBoardCols }; + throw new MaximumColsException() { Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxInlineKeyBoardCols }; } break; case eKeyboardType.ReplyKeyboard: - if (ButtonsForm.Rows > Constants.Telegram.MaxReplyKeyboardRows && !this.EnablePaging) + if (DataSource.RowCount > Constants.Telegram.MaxReplyKeyboardRows && !this.EnablePaging) { - throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxReplyKeyboardRows }; + throw new MaximumRowsReachedException() { Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxReplyKeyboardRows }; } - if (ButtonsForm.Cols > Constants.Telegram.MaxReplyKeyboardCols) + if (DataSource.ColumnCount > Constants.Telegram.MaxReplyKeyboardCols) { - throw new MaximumColsException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxReplyKeyboardCols }; + throw new MaximumColsException() { Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxReplyKeyboardCols }; } break; @@ -436,7 +531,7 @@ namespace TelegramBotBase.Controls.Hybrid Message m = null; - ButtonForm form = this.ButtonsForm; + ButtonForm form = this.DataSource.PickItems(CurrentPageIndex * ItemRowsPerPage, ItemRowsPerPage, null); //if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "") //{ @@ -444,12 +539,12 @@ namespace TelegramBotBase.Controls.Hybrid //} //else //{ - form = form.Duplicate(); + //form = form.Duplicate(); //} if (this.EnablePaging) { - form = GeneratePagingView(form); + IntegratePagingView(form); } else { @@ -458,18 +553,18 @@ namespace TelegramBotBase.Controls.Hybrid if (this.HeadLayoutButtonRow != null && HeadLayoutButtonRow.Count > 0) { - form.InsertButtonRow(0, this.HeadLayoutButtonRow); + form.InsertButtonRow(0, this.HeadLayoutButtonRow.ToArray()); } if (this.SubHeadLayoutButtonRow != null && SubHeadLayoutButtonRow.Count > 0) { if (this.IsNavigationBarVisible) { - form.InsertButtonRow(2, this.SubHeadLayoutButtonRow); + form.InsertButtonRow(2, this.SubHeadLayoutButtonRow.ToArray()); } else { - form.InsertButtonRow(1, this.SubHeadLayoutButtonRow); + form.InsertButtonRow(1, this.SubHeadLayoutButtonRow.ToArray()); } } @@ -478,21 +573,19 @@ namespace TelegramBotBase.Controls.Hybrid //Reply Keyboard could only be updated with a new keyboard. case eKeyboardType.ReplyKeyboard: - if (this.MessageId != null) + if (form.Count == 0) { - if (form.Count == 0) + if (this.MessageId != null) { await this.Device.HideReplyKeyboard(); this.MessageId = null; - return; } - if (this.DeletePreviousMessage) - await this.Device.DeleteMessage(this.MessageId.Value); + return; } - if (form.Count == 0) - return; + //if (form.Count == 0) + // return; var rkm = (ReplyKeyboardMarkup)form; @@ -500,6 +593,10 @@ namespace TelegramBotBase.Controls.Hybrid rkm.OneTimeKeyboard = this.OneTimeKeyboard; m = await this.Device.Send(this.Title, rkm, disableNotification: true, parseMode: MessageParseMode, MarkdownV2AutoEscape: false); + //Prevent flicker of keyboard + if (this.DeletePreviousMessage && this.MessageId != null) + await this.Device.DeleteMessage(this.MessageId.Value); + break; case eKeyboardType.InlineKeyBoard: @@ -524,58 +621,52 @@ namespace TelegramBotBase.Controls.Hybrid } - private ButtonForm GeneratePagingView(ButtonForm dataForm) + private void IntegratePagingView(ButtonForm dataForm) { + //No Items + if (dataForm.Rows == 0) + { + dataForm.AddButtonRow(new ButtonBase(NoItemsLabel, "$")); + } ButtonForm bf = new ButtonForm(); bf = PrepareCheckableLayout(dataForm); - //No Items - if (this.ButtonsForm.Count == 0) - { - bf.AddButtonRow(new ButtonBase(NoItemsLabel, "$")); - } if (this.IsNavigationBarVisible) { //🔍 - List lst = new List(); - lst.Add(new ButtonBase(PreviousPageLabel, "$previous$")); - lst.Add(new ButtonBase(String.Format(Localizations.Default.Language["ButtonGrid_CurrentPage"], this.CurrentPageIndex + 1, this.PageCount), "$site$")); - lst.Add(new ButtonBase(NextPageLabel, "$next$")); + ButtonRow row = new ButtonRow(); + row.Add(new ButtonBase(PreviousPageLabel, "$previous$")); + row.Add(new ButtonBase(String.Format(Localizations.Default.Language["ButtonGrid_CurrentPage"], this.CurrentPageIndex + 1, this.PageCount), "$site$")); + row.Add(new ButtonBase(NextPageLabel, "$next$")); - //if (this.EnableSearch) - //{ - // lst.Insert(2, new ButtonBase("🔍 " + (this.SearchQuery ?? ""), "$search$")); - //} - bf.InsertButtonRow(0, lst); + dataForm.InsertButtonRow(0, row); - bf.AddButtonRow(lst); + dataForm.AddButtonRow(row); } - - return bf; } private ButtonForm PrepareCheckableLayout(ButtonForm dataForm) { var bf = new ButtonForm(); - for (int i = 0; i < this.MaximumRow - LayoutRows; i++) + for (int i = 0; i < dataForm.Rows; i++) { int it = (this.CurrentPageIndex * (this.MaximumRow - LayoutRows)) + i; - if (it > dataForm.Rows - 1) - break; + //if (it > dataForm.Rows - 1) + // break; - var r = dataForm[it]; + var r = dataForm[i]; String s = CheckedRows.Contains(it) ? this.CheckedIconLabel : this.UncheckedIconLabel; //On reply keyboards we need a unique text. if (this.KeyboardType == eKeyboardType.ReplyKeyboard) { - s = $"{it}. " + s; + s = $"{it + 1}. " + s; } if (CheckedRows.Contains(it)) @@ -652,7 +743,7 @@ namespace TelegramBotBase.Controls.Hybrid { get { - return this.LayoutRows + ButtonsForm.Rows; + return this.LayoutRows + DataSource.RowCount; } } @@ -679,24 +770,37 @@ namespace TelegramBotBase.Controls.Hybrid } } + /// + /// Returns the number of item rows per page. + /// + public int ItemRowsPerPage + { + get + { + return this.MaximumRow - this.LayoutRows; + } + } + public int PageCount { get { - if (this.ButtonsForm.Count == 0) + if (DataSource.RowCount == 0) return 1; - var bf = this.ButtonsForm; + //var bf = this.DataSource.PickAllItems(this.EnableSearch ? this.SearchQuery : null); + + var max = this.DataSource.RowCount; //if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "") //{ // bf = bf.FilterDuplicate(this.SearchQuery); //} - if (bf.Rows == 0) + if (max == 0) return 1; - return (int)Math.Ceiling((decimal)(bf.Rows / (decimal)(MaximumRow - this.LayoutRows))); + return (int)Math.Ceiling((decimal)((decimal)max / (decimal)ItemRowsPerPage)); } } diff --git a/TelegramBotBase/Controls/Hybrid/TaggedButtonGrid.cs b/TelegramBotBase/Controls/Hybrid/TaggedButtonGrid.cs index d97f920..55ed12e 100644 --- a/TelegramBotBase/Controls/Hybrid/TaggedButtonGrid.cs +++ b/TelegramBotBase/Controls/Hybrid/TaggedButtonGrid.cs @@ -7,9 +7,11 @@ using System.Text; using System.Threading.Tasks; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; +using Telegram.Bot.Types.InlineQueryResults; using Telegram.Bot.Types.ReplyMarkups; using TelegramBotBase.Args; using TelegramBotBase.Base; +using TelegramBotBase.Datasources; using TelegramBotBase.Enums; using TelegramBotBase.Exceptions; using TelegramBotBase.Form; @@ -30,7 +32,23 @@ namespace TelegramBotBase.Controls.Hybrid private readonly EventHandlerList Events = new EventHandlerList(); - public ButtonForm ButtonsForm { get; set; } + [Obsolete("This property is obsolete. Please use the DataSource property instead.")] + public ButtonForm ButtonsForm + { + get + { + return DataSource.ButtonForm; + } + set + { + DataSource = new ButtonFormDataSource(value); + } + } + + /// + /// Data source of the items. + /// + public ButtonFormDataSource DataSource { get; set; } public int? MessageId { get; set; } @@ -62,6 +80,11 @@ namespace TelegramBotBase.Controls.Hybrid /// public bool EnablePaging { get; set; } = false; + /// + /// Shows un-/check all tags options + /// + public bool EnableCheckAllTools { get; set; } = false; + /// /// Enabled a search function. /// @@ -87,15 +110,24 @@ namespace TelegramBotBase.Controls.Hybrid public String BackLabel = Localizations.Default.Language["ButtonGrid_Back"]; + public String CheckAllLabel = Localizations.Default.Language["ButtonGrid_CheckAll"]; + + public String UncheckAllLabel = Localizations.Default.Language["ButtonGrid_UncheckAll"]; + /// /// Layout of the buttons which should be displayed always on top. /// - public List HeadLayoutButtonRow { get; set; } + public ButtonRow HeadLayoutButtonRow { get; set; } /// /// Layout of columns which should be displayed below the header /// - public List SubHeadLayoutButtonRow { get; set; } + public ButtonRow SubHeadLayoutButtonRow { get; set; } + + /// + /// Layout of columns which should be displayed below the header + /// + private ButtonRow TagsSubHeadLayoutButtonRow { get; set; } /// /// List of Tags which will be allowed to filter by. @@ -134,7 +166,7 @@ namespace TelegramBotBase.Controls.Hybrid public TaggedButtonGrid() { - this.ButtonsForm = new ButtonForm(); + this.DataSource = new ButtonFormDataSource(); this.SelectedViewIndex = 0; } @@ -147,7 +179,7 @@ namespace TelegramBotBase.Controls.Hybrid public TaggedButtonGrid(ButtonForm form) { - this.ButtonsForm = form; + this.DataSource = new ButtonFormDataSource(form); } @@ -202,27 +234,60 @@ namespace TelegramBotBase.Controls.Hybrid if (result.MessageText == null || result.MessageText == "") return; - var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText) - ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText) - ?? ButtonsForm.ToList().FirstOrDefault(a => a.Text.Trim() == result.MessageText); + var matches = new List(); + ButtonRow match = null; + int index = -1; + + if (HeadLayoutButtonRow?.Matches(result.MessageText) ?? false) + { + match = HeadLayoutButtonRow; + goto check; + } + + if (SubHeadLayoutButtonRow?.Matches(result.MessageText) ?? false) + { + match = SubHeadLayoutButtonRow; + goto check; + } + + if (TagsSubHeadLayoutButtonRow?.Matches(result.MessageText) ?? false) + { + match = TagsSubHeadLayoutButtonRow; + goto check; + } + + var br = DataSource.FindRow(result.MessageText); + if (br != null) + { + match = br.Item1; + index = br.Item2; + } + + + //var button = HeadLayoutButtonRow?. .FirstOrDefault(a => a.Text.Trim() == result.MessageText) + // ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText); + + // bf.ToList().FirstOrDefault(a => a.Text.Trim() == result.MessageText) + + //var index = bf.FindRowByButton(button); + + check: - var index = ButtonsForm.FindRowByButton(button); switch (this.SelectedViewIndex) { case 0: - if (button != null) - { - await OnButtonClicked(new ButtonClickedEventArgs(button, index)); + //Remove button click message + if (this.DeleteReplyMessage) + await Device.DeleteMessage(result.MessageId); - //Remove button click message - if (this.DeleteReplyMessage) - await Device.DeleteMessage(result.MessageId); + if (match != null) + { + await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.MessageText), index, match)); result.Handled = true; - return; } @@ -287,17 +352,25 @@ namespace TelegramBotBase.Controls.Hybrid break; case 1: + //Remove button click message + if (this.DeleteReplyMessage) + await Device.DeleteMessage(result.MessageId); + if (result.MessageText == this.BackLabel) { - //Remove button click message - if (this.DeletePreviousMessage) - await Device.DeleteMessage(result.MessageId); - this.SelectedViewIndex = 0; this.Updated(); result.Handled = true; return; } + else if (result.MessageText == this.CheckAllLabel) + { + this.CheckAllTags(); + } + else if (result.MessageText == this.UncheckAllLabel) + { + this.UncheckAllTags(); + } var i = result.MessageText.LastIndexOf(" "); if (i == -1) @@ -340,15 +413,48 @@ namespace TelegramBotBase.Controls.Hybrid await result.ConfirmAction(this.ConfirmationText ?? ""); - var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData) - ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData) - ?? ButtonsForm.ToList().FirstOrDefault(a => a.Value == result.RawData); + ButtonRow match = null; + int index = -1; - var index = ButtonsForm.FindRowByButton(button); - - if (button != null) + if (HeadLayoutButtonRow?.Matches(result.RawData, false) ?? false) { - await OnButtonClicked(new ButtonClickedEventArgs(button, index)); + match = HeadLayoutButtonRow; + goto check; + } + + if (SubHeadLayoutButtonRow?.Matches(result.RawData, false) ?? false) + { + match = SubHeadLayoutButtonRow; + goto check; + } + + if (TagsSubHeadLayoutButtonRow?.Matches(result.RawData) ?? false) + { + match = TagsSubHeadLayoutButtonRow; + goto check; + } + + var br = DataSource.FindRow(result.RawData, false); + if (br != null) + { + match = br.Item1; + index = br.Item2; + } + + + + //var bf = DataSource.ButtonForm; + + //var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData) + // ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData) + // ?? bf.ToList().FirstOrDefault(a => a.Value == result.RawData); + + //var index = bf.FindRowByButton(button); + + check: + if (match != null) + { + await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.RawData, false), index, match)); result.Handled = true; return; @@ -386,6 +492,18 @@ namespace TelegramBotBase.Controls.Hybrid this.SelectedViewIndex = 0; this.Updated(); + break; + + case "$checkall$": + + this.CheckAllTags(); + + break; + + case "$uncheckall$": + + this.UncheckAllTags(); + break; } @@ -400,28 +518,28 @@ namespace TelegramBotBase.Controls.Hybrid { case eKeyboardType.InlineKeyBoard: - if (ButtonsForm.Rows > Constants.Telegram.MaxInlineKeyBoardRows && !this.EnablePaging) + if (DataSource.RowCount > Constants.Telegram.MaxInlineKeyBoardRows && !this.EnablePaging) { - throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxInlineKeyBoardRows }; + throw new MaximumRowsReachedException() { Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxInlineKeyBoardRows }; } - if (ButtonsForm.Cols > Constants.Telegram.MaxInlineKeyBoardCols) + if (DataSource.ColumnCount > Constants.Telegram.MaxInlineKeyBoardCols) { - throw new MaximumColsException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxInlineKeyBoardCols }; + throw new MaximumColsException() { Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxInlineKeyBoardCols }; } break; case eKeyboardType.ReplyKeyboard: - if (ButtonsForm.Rows > Constants.Telegram.MaxReplyKeyboardRows && !this.EnablePaging) + if (DataSource.RowCount > Constants.Telegram.MaxReplyKeyboardRows && !this.EnablePaging) { - throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxReplyKeyboardRows }; + throw new MaximumRowsReachedException() { Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxReplyKeyboardRows }; } - if (ButtonsForm.Cols > Constants.Telegram.MaxReplyKeyboardCols) + if (DataSource.ColumnCount > Constants.Telegram.MaxReplyKeyboardCols) { - throw new MaximumColsException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxReplyKeyboardCols }; + throw new MaximumColsException() { Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxReplyKeyboardCols }; } break; @@ -468,16 +586,16 @@ namespace TelegramBotBase.Controls.Hybrid { Message m = null; - ButtonForm form = this.ButtonsForm; + ButtonForm form = this.DataSource.PickItems(CurrentPageIndex * ItemRowsPerPage, ItemRowsPerPage, (this.EnableSearch ? this.SearchQuery : null)); - if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "") - { - form = form.FilterDuplicate(this.SearchQuery, true); - } - else - { - form = form.Duplicate(); - } + //if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "") + //{ + // form = form.FilterDuplicate(this.SearchQuery, true); + //} + //else + //{ + // form = form.Duplicate(); + //} if (this.Tags != null && this.SelectedTags != null) { @@ -486,23 +604,23 @@ namespace TelegramBotBase.Controls.Hybrid if (this.EnablePaging) { - form = GeneratePagingView(form); + IntegratePagingView(form); } if (this.HeadLayoutButtonRow != null && HeadLayoutButtonRow.Count > 0) { - form.InsertButtonRow(0, this.HeadLayoutButtonRow); + form.InsertButtonRow(0, this.HeadLayoutButtonRow.ToArray()); } if (this.SubHeadLayoutButtonRow != null && SubHeadLayoutButtonRow.Count > 0) { if (this.IsNavigationBarVisible) { - form.InsertButtonRow(2, this.SubHeadLayoutButtonRow); + form.InsertButtonRow(2, this.SubHeadLayoutButtonRow.ToArray()); } else { - form.InsertButtonRow(1, this.SubHeadLayoutButtonRow); + form.InsertButtonRow(1, this.SubHeadLayoutButtonRow.ToArray()); } } @@ -559,52 +677,37 @@ namespace TelegramBotBase.Controls.Hybrid } } - private ButtonForm GeneratePagingView(ButtonForm dataForm) + private void IntegratePagingView(ButtonForm dataForm) { - ButtonForm bf = new ButtonForm(); - - - for (int i = 0; i < this.MaximumRow - LayoutRows; i++) - { - int it = (this.CurrentPageIndex * (this.MaximumRow - LayoutRows)) + i; - - if (it > dataForm.Rows - 1) - break; - - bf.AddButtonRow(dataForm[it]); - } - //No Items - if (this.ButtonsForm.Count == 0) + if (dataForm.Rows == 0) { - bf.AddButtonRow(new ButtonBase(NoItemsLabel, "$")); + dataForm.AddButtonRow(new ButtonBase(NoItemsLabel, "$")); } if (this.IsNavigationBarVisible) { //🔍 - List lst = new List(); - lst.Add(new ButtonBase(PreviousPageLabel, "$previous$")); - lst.Add(new ButtonBase(String.Format(Localizations.Default.Language["ButtonGrid_CurrentPage"], this.CurrentPageIndex + 1, this.PageCount), "$site$")); + ButtonRow row = new ButtonRow(); + row.Add(new ButtonBase(PreviousPageLabel, "$previous$")); + row.Add(new ButtonBase(String.Format(Localizations.Default.Language["ButtonGrid_CurrentPage"], this.CurrentPageIndex + 1, this.PageCount), "$site$")); if (this.Tags != null && this.Tags.Count > 0) { - lst.Add(new ButtonBase("📁", "$filter$")); + row.Add(new ButtonBase("📁", "$filter$")); } - lst.Add(new ButtonBase(NextPageLabel, "$next$")); + row.Add(new ButtonBase(NextPageLabel, "$next$")); if (this.EnableSearch) { - lst.Insert(2, new ButtonBase("🔍 " + (this.SearchQuery ?? ""), "$search$")); + row.Insert(2, new ButtonBase("🔍 " + (this.SearchQuery ?? ""), "$search$")); } - bf.InsertButtonRow(0, lst); + dataForm.InsertButtonRow(0, row); - bf.AddButtonRow(lst); + dataForm.AddButtonRow(row); } - - return bf; } #endregion @@ -620,6 +723,12 @@ namespace TelegramBotBase.Controls.Hybrid bf.AddButtonRow(this.BackLabel, "$back$"); + if (EnableCheckAllTools) + { + this.TagsSubHeadLayoutButtonRow = new ButtonRow(new ButtonBase(CheckAllLabel, "$checkall$"), new ButtonBase(UncheckAllLabel, "$uncheckall$")); + bf.AddButtonRow(TagsSubHeadLayoutButtonRow); + } + foreach (var t in this.Tags) { @@ -748,7 +857,7 @@ namespace TelegramBotBase.Controls.Hybrid { get { - return this.LayoutRows + ButtonsForm.Rows; + return this.LayoutRows + DataSource.RowCount; } } @@ -771,28 +880,46 @@ namespace TelegramBotBase.Controls.Hybrid if (this.SubHeadLayoutButtonRow != null && this.SubHeadLayoutButtonRow.Count > 0) layoutRows++; + if (EnableCheckAllTools && this.SelectedViewIndex == 1) + { + layoutRows++; + } + return layoutRows; } } + /// + /// Returns the number of item rows per page. + /// + public int ItemRowsPerPage + { + get + { + return this.MaximumRow - this.LayoutRows; + } + } + public int PageCount { get { - if (this.ButtonsForm.Count == 0) + if (DataSource.RowCount == 0) return 1; - var bf = this.ButtonsForm; + //var bf = this.DataSource.PickAllItems(this.EnableSearch ? this.SearchQuery : null); - if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "") - { - bf = bf.FilterDuplicate(this.SearchQuery); - } + var max = this.DataSource.CalculateMax(this.EnableSearch ? this.SearchQuery : null); - if (bf.Rows == 0) + //if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "") + //{ + // bf = bf.FilterDuplicate(this.SearchQuery); + //} + + if (max == 0) return 1; - return (int)Math.Ceiling((decimal)(bf.Rows / (decimal)(MaximumRow - this.LayoutRows))); + return (int)Math.Ceiling((decimal)((decimal)max / (decimal)ItemRowsPerPage)); } } @@ -844,6 +971,8 @@ namespace TelegramBotBase.Controls.Hybrid break; } + } + /// /// Checks all tags for filtering. diff --git a/TelegramBotBase/Datasources/ButtonFormDataSource.cs b/TelegramBotBase/Datasources/ButtonFormDataSource.cs new file mode 100644 index 0000000..e8fbfd4 --- /dev/null +++ b/TelegramBotBase/Datasources/ButtonFormDataSource.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.Text; +using TelegramBotBase.Controls.Hybrid; +using TelegramBotBase.Form; +using TelegramBotBase.Interfaces; + +namespace TelegramBotBase.Datasources +{ + public class ButtonFormDataSource : Interfaces.IDataSource + { + public virtual ButtonForm ButtonForm + { + get + { + return __buttonform; + } + set + { + __buttonform = value; + } + } + + private ButtonForm __buttonform = null; + + public ButtonFormDataSource() + { + __buttonform = new ButtonForm(); + } + + public ButtonFormDataSource(ButtonForm bf) + { + __buttonform = bf; + } + + + /// + /// Returns the amount of rows exisiting. + /// + /// + public virtual int Count => ButtonForm.Count; + + + /// + /// Returns the amount of rows. + /// + public virtual int RowCount => ButtonForm.Rows; + + /// + /// Returns the maximum amount of columns. + /// + public virtual int ColumnCount => ButtonForm.Cols; + + /// + /// Returns the row with the specific index. + /// + /// + /// + public virtual ButtonRow ItemAt(int index) + { + return ButtonForm[index]; + } + + public virtual List ItemRange(int start, int count) + { + return ButtonForm.GetRange(start, count); + } + + public virtual List AllItems() + { + return ButtonForm.ToArray(); + } + + public virtual ButtonForm PickItems(int start, int count, String filter = null) + { + ButtonForm bf = new ButtonForm(); + ButtonForm dataForm = null; + + if (filter == null) + { + dataForm = ButtonForm.Duplicate(); + } + else + { + dataForm = ButtonForm.FilterDuplicate(filter, true); + } + + for (int i = 0; i < count; i++) + { + int it = start + i; + + if (it > dataForm.Rows - 1) + break; + + bf.AddButtonRow(dataForm[it]); + } + + return bf; + } + + public virtual ButtonForm PickAllItems(String filter = null) + { + if (filter == null) + return ButtonForm.Duplicate(); + + + return ButtonForm.FilterDuplicate(filter, true); + } + + public virtual Tuple FindRow(String text, bool useText = true) + { + return ButtonForm.FindRow(text, useText); + } + + /// + /// Returns the maximum items of this data source. + /// + /// + /// + public virtual int CalculateMax(String filter = null) + { + return PickAllItems(filter).Rows; + } + + public virtual ButtonRow Render(object data) + { + return data as ButtonRow; + } + + + public static implicit operator ButtonFormDataSource(ButtonForm bf) + { + return new ButtonFormDataSource(bf); + } + + public static implicit operator ButtonForm(ButtonFormDataSource ds) + { + return ds.ButtonForm; + } + + } +} diff --git a/TelegramBotBase/Datasources/StaticDataSource.cs b/TelegramBotBase/Datasources/StaticDataSource.cs new file mode 100644 index 0000000..19ff96c --- /dev/null +++ b/TelegramBotBase/Datasources/StaticDataSource.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TelegramBotBase.Datasources +{ + public class StaticDataSource : Interfaces.IDataSource + { + List Data { get; set; } + + public StaticDataSource() + { + + } + + public StaticDataSource(List data) + { + this.Data = data; + } + + + public int Count + { + get + { + return Data.Count; + } + } + + public T ItemAt(int index) + { + return Data[index]; + } + + public List ItemRange(int start, int count) + { + return Data.Skip(start).Take(count).ToList(); + } + + public List AllItems() + { + return Data; + } + } +} diff --git a/TelegramBotBase/Form/ButtonForm.cs b/TelegramBotBase/Form/ButtonForm.cs index be32f49..c477b37 100644 --- a/TelegramBotBase/Form/ButtonForm.cs +++ b/TelegramBotBase/Form/ButtonForm.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading.Tasks; using Telegram.Bot.Types.ReplyMarkups; using TelegramBotBase.Base; +using TelegramBotBase.Controls.Hybrid; namespace TelegramBotBase.Form { @@ -13,7 +14,7 @@ namespace TelegramBotBase.Form /// public class ButtonForm { - List> Buttons = new List>(); + List Buttons = new List(); public IReplyMarkup Markup { get; set; } @@ -43,7 +44,7 @@ namespace TelegramBotBase.Form } - public List this[int row] + public ButtonRow this[int row] { get { @@ -66,9 +67,14 @@ namespace TelegramBotBase.Form Buttons.Add(new List() { new ButtonBase(Text, Value, Url) }); } - public void AddButtonRow(IEnumerable row) + //public void AddButtonRow(ButtonRow row) + //{ + // Buttons.Add(row.ToList()); + //} + + public void AddButtonRow(ButtonRow row) { - Buttons.Add(row.ToList()); + Buttons.Add(row); } public void AddButtonRow(params ButtonBase[] row) @@ -76,16 +82,26 @@ namespace TelegramBotBase.Form AddButtonRow(row.ToList()); } + public void AddButtonRows(IEnumerable rows) + { + Buttons.AddRange(rows); + } + public void InsertButtonRow(int index, IEnumerable row) { Buttons.Insert(index, row.ToList()); } - public void InsertButtonRow(int index, params ButtonBase[] row) + public void InsertButtonRow(int index, ButtonRow row) { - InsertButtonRow(index, row.ToList()); + Buttons.Insert(index, row); } + //public void InsertButtonRow(int index, params ButtonBase[] row) + //{ + // InsertButtonRow(index, row.ToList()); + //} + public static T[][] SplitTo(IEnumerable items, int itemsPerRow = 2) { T[][] splitted = default(T[][]); @@ -132,34 +148,62 @@ namespace TelegramBotBase.Form } } + /// + /// Returns a range of rows from the buttons. + /// + /// + /// + /// + public List GetRange(int start, int count) + { + return Buttons.Skip(start).Take(count).ToList(); + } + + public List ToList() { - return this.Buttons.DefaultIfEmpty(new List()).Aggregate((a, b) => a.Union(b).ToList()); + return this.Buttons.DefaultIfEmpty(new List()).Select(a => a.ToList()).Aggregate((a, b) => a.Union(b).ToList()); } public InlineKeyboardButton[][] ToInlineButtonArray() { - var ikb = this.Buttons.Select(a => a.Select(b => b.ToInlineButton(this)).ToArray()).ToArray(); + var ikb = this.Buttons.Select(a => a.ToArray().Select(b => b.ToInlineButton(this)).ToArray()).ToArray(); return ikb; } public KeyboardButton[][] ToReplyButtonArray() { - var ikb = this.Buttons.Select(a => a.Select(b => b.ToKeyboardButton(this)).ToArray()).ToArray(); + var ikb = this.Buttons.Select(a => a.ToArray().Select(b => b.ToKeyboardButton(this)).ToArray()).ToArray(); return ikb; } + public List ToArray() + { + return Buttons; + } + public int FindRowByButton(ButtonBase button) { - var row = this.Buttons.FirstOrDefault(a => a.Count(b => b == button) > 0); + var row = this.Buttons.FirstOrDefault(a => a.ToArray().Count(b => b == button) > 0); if (row == null) return -1; return this.Buttons.IndexOf(row); } + public Tuple FindRow(String text, bool useText = true) + { + var r = this.Buttons.FirstOrDefault(a => a.Matches(text, useText)); + if (r == null) + return null; + + var i = this.Buttons.IndexOf(r); + return new Tuple(r, i); + } + + /// /// Returns the first Button with the given value. /// @@ -204,7 +248,7 @@ namespace TelegramBotBase.Form foreach (var b in Buttons) { - var lst = new List(); + var lst = new ButtonRow(); foreach (var b2 in b) { lst.Add(b2); @@ -229,7 +273,7 @@ namespace TelegramBotBase.Form foreach (var b in Buttons) { - var lst = new List(); + var lst = new ButtonRow(); foreach (var b2 in b) { if (b2.Text.IndexOf(filter, StringComparison.InvariantCultureIgnoreCase) == -1) @@ -238,7 +282,7 @@ namespace TelegramBotBase.Form //Copy full row, when at least one match has found. if (ByRow) { - lst.AddRange(b); + lst = b; break; } else @@ -268,7 +312,7 @@ namespace TelegramBotBase.Form foreach (var b in Buttons) { - var lst = new List(); + var lst = new ButtonRow(); foreach (var b2 in b) { if (!(b2 is TagButtonBase tb)) @@ -280,7 +324,7 @@ namespace TelegramBotBase.Form //Copy full row, when at least one match has found. if (ByRow) { - lst.AddRange(b); + lst = b; break; } else diff --git a/TelegramBotBase/Interfaces/IDataSource.cs b/TelegramBotBase/Interfaces/IDataSource.cs new file mode 100644 index 0000000..d3662e5 --- /dev/null +++ b/TelegramBotBase/Interfaces/IDataSource.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Text; +using TelegramBotBase.Controls.Hybrid; + +namespace TelegramBotBase.Interfaces +{ + public interface IDataSource + { + + /// + /// Returns the amount of items within this source. + /// + /// + int Count { get; } + + + /// + /// Returns the item at the specific index. + /// + /// + /// + T ItemAt(int index); + + + /// + /// Get all items from this source within this range. + /// + /// + /// + /// + List ItemRange(int start, int count); + + + /// + /// Gets a list of all items of this datasource. + /// + /// + List AllItems(); + + + } +} diff --git a/TelegramBotBaseTest/Tests/Controls/ButtonGridTagForm.cs b/TelegramBotBaseTest/Tests/Controls/ButtonGridTagForm.cs index 1060754..c737d10 100644 --- a/TelegramBotBaseTest/Tests/Controls/ButtonGridTagForm.cs +++ b/TelegramBotBaseTest/Tests/Controls/ButtonGridTagForm.cs @@ -32,6 +32,7 @@ namespace TelegramBotBaseTest.Tests.Controls m_Buttons.EnablePaging = true; m_Buttons.HeadLayoutButtonRow = new List() { new ButtonBase("Back", "back") }; + var countries = CultureInfo.GetCultures(CultureTypes.SpecificCultures); @@ -45,7 +46,9 @@ namespace TelegramBotBaseTest.Tests.Controls m_Buttons.Tags = countries.Select(a => a.Parent.EnglishName).Distinct().OrderBy(a => a).ToList(); m_Buttons.SelectedTags = countries.Select(a => a.Parent.EnglishName).Distinct().OrderBy(a => a).ToList(); - m_Buttons.ButtonsForm = bf; + m_Buttons.EnableCheckAllTools = true; + + m_Buttons.DataSource = new TelegramBotBase.Datasources.ButtonFormDataSource(bf); m_Buttons.ButtonClicked += Bg_ButtonClicked; @@ -57,11 +60,13 @@ namespace TelegramBotBaseTest.Tests.Controls if (e.Button == null) return; - if (e.Button.Value == "back") + switch (e.Button.Value) { - var start = new Menu(); - await this.NavigateTo(start); - return; + + case "back": + var start = new Menu(); + await this.NavigateTo(start); + return; } diff --git a/TelegramBotBaseTest/Tests/Datasources/CustomDataSource.cs b/TelegramBotBaseTest/Tests/Datasources/CustomDataSource.cs new file mode 100644 index 0000000..3187968 --- /dev/null +++ b/TelegramBotBaseTest/Tests/Datasources/CustomDataSource.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using TelegramBotBase.Controls.Hybrid; +using TelegramBotBase.Datasources; +using TelegramBotBase.Form; + +namespace TelegramBotBaseTest.Tests.Datasources +{ + public class CustomDataSource : ButtonFormDataSource + { + + public List Countries = new List() { "Country 1", "Country 2", "Country 3" }; + + public CustomDataSource() + { + loadData(); + } + + /// + /// This method has the example purpose of creating and loading some example data. + /// When using a database you do not need this kind of method. + /// + private void loadData() + { + //Exists data source? Read it + if (File.Exists(AppContext.BaseDirectory + "countries.json")) + { + try + { + var List = Newtonsoft.Json.JsonConvert.DeserializeObject>(File.ReadAllText("countries.json")); + + + Countries = List; + } + catch + { + + } + + + return; + } + + //If not, create it + try + { + var countries = CultureInfo.GetCultures(CultureTypes.SpecificCultures).Select(a => a.DisplayName).ToList(); + + Countries = countries; + + var tmp = Newtonsoft.Json.JsonConvert.SerializeObject(countries); + + File.WriteAllText( AppContext.BaseDirectory + "countries.json", tmp); + } + catch + { + + } + + } + + public override ButtonRow ItemAt(int index) + { + var item = Countries.ElementAt(index); + if (item == null) + return new ButtonRow(); + + return Render(item); + } + + public override List ItemRange(int start, int count) + { + var items = Countries.Skip(start).Take(count); + + List lst = new List(); + foreach (var c in items) + { + lst.Add(Render(c)); + } + + return lst; + } + + public override List AllItems() + { + List lst = new List(); + foreach (var c in Countries) + { + lst.Add(Render(c)); + } + return lst; + } + + public override ButtonForm PickItems(int start, int count, string filter = null) + { + List rows = ItemRange(start, count); + + ButtonForm lst = new ButtonForm(); + foreach (var c in rows) + { + lst.AddButtonRow(c); + } + return lst; + } + + public override ButtonForm PickAllItems(string filter = null) + { + List rows = AllItems(); + + ButtonForm bf = new ButtonForm(); + + bf.AddButtonRows(rows); + + return bf; + } + + public override int CalculateMax(string filter = null) + { + if (filter == null) + return Countries.Count; + + return Countries.Where(a => a.IndexOf(filter, StringComparison.InvariantCultureIgnoreCase) != -1).Count(); + } + + public override ButtonRow Render(object data) + { + var s = data as String; + if (s == null) + return new ButtonRow(new ButtonBase("Empty", "zero")); + + return new ButtonRow(new ButtonBase(s, s)); + } + + public override int Count + { + get + { + return Countries.Count; + } + } + + public override int ColumnCount + { + get + { + return 1; + } + } + + public override int RowCount + { + get + { + return this.Count; + } + } + + } +} diff --git a/TelegramBotBaseTest/Tests/Datasources/List.cs b/TelegramBotBaseTest/Tests/Datasources/List.cs new file mode 100644 index 0000000..b2ec372 --- /dev/null +++ b/TelegramBotBaseTest/Tests/Datasources/List.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using TelegramBotBase.Base; +using TelegramBotBase.Controls.Hybrid; +using TelegramBotBase.Form; + +namespace TelegramBotBaseTest.Tests.Datasources +{ + public class List : FormBase + { + ButtonGrid __buttons = null; + + public List() + { + this.Init += List_Init; + } + + private async Task List_Init(object sender, TelegramBotBase.Args.InitEventArgs e) + { + + __buttons = new ButtonGrid(); + + __buttons.EnablePaging = true; + __buttons.EnableSearch = false; + __buttons.ButtonClicked += __buttons_ButtonClicked; + __buttons.KeyboardType = TelegramBotBase.Enums.eKeyboardType.ReplyKeyboard; + __buttons.DeleteReplyMessage = true; + + __buttons.HeadLayoutButtonRow = new ButtonRow(new ButtonBase("Back", "back")); + + var cds = new CustomDataSource(); + __buttons.DataSource = cds; + + AddControl(__buttons); + } + + private async Task __buttons_ButtonClicked(object sender, TelegramBotBase.Args.ButtonClickedEventArgs e) + { + switch(e.Button.Value) + { + case "back": + + var mn = new Menu(); + await NavigateTo(mn); + + break; + } + } + + public override async Task Load(MessageResult message) + { + + } + + + public override async Task Render(MessageResult message) + { + + } + + + } +} diff --git a/TelegramBotBaseTest/Tests/Menu.cs b/TelegramBotBaseTest/Tests/Menu.cs index e4de4f0..8f96053 100644 --- a/TelegramBotBaseTest/Tests/Menu.cs +++ b/TelegramBotBaseTest/Tests/Menu.cs @@ -190,6 +190,14 @@ namespace TelegramBotBaseTest.Tests await NavigateTo(nc); + break; + + case "dynamicbuttongrid": + + var dg = new Datasources.List(); + + await NavigateTo(dg); + break; default: @@ -237,6 +245,8 @@ namespace TelegramBotBaseTest.Tests btn.AddButtonRow(new ButtonBase("#17 NavigationController (Push/Pop)", new CallbackData("a", "navigationcontroller").Serialize())); + btn.AddButtonRow(new ButtonBase("#18 Dynamic ButtonGrid (DataSources)", new CallbackData("a", "dynamicbuttongrid").Serialize())); + await this.Device.Send("Choose your test:", btn); }