diff --git a/src/HASS.Agent.Staging/HASS.Agent/Forms/Main.cs b/src/HASS.Agent.Staging/HASS.Agent/Forms/Main.cs index 26881537..4340f0a8 100644 --- a/src/HASS.Agent.Staging/HASS.Agent/Forms/Main.cs +++ b/src/HASS.Agent.Staging/HASS.Agent/Forms/Main.cs @@ -347,10 +347,15 @@ private async void ShowMain() /// private async void ShowQuickActions() { + // check if quickactions aren't already open, and if there are any actions if (HelperFunctions.CheckIfFormIsOpen("QuickActions")) { - await HelperFunctions.TryBringToFront("QuickActions"); + var quickActionsForm = HelperFunctions.GetForm("QuickActions") as QuickActions.QuickActions; + var result = await HelperFunctions.TryBringToFront(quickActionsForm); + if (result) + quickActionsForm.SelectNextQuickActionItem(); + return; } diff --git a/src/HASS.Agent.Staging/HASS.Agent/Forms/QuickActions/QuickActions.cs b/src/HASS.Agent.Staging/HASS.Agent/Forms/QuickActions/QuickActions.cs index a2381add..0c54706b 100644 --- a/src/HASS.Agent.Staging/HASS.Agent/Forms/QuickActions/QuickActions.cs +++ b/src/HASS.Agent.Staging/HASS.Agent/Forms/QuickActions/QuickActions.cs @@ -12,7 +12,7 @@ namespace HASS.Agent.Forms.QuickActions public partial class QuickActions : MetroForm { public event EventHandler ClearFocus; - + private readonly List _quickActions = new(); private readonly List _quickActionPanelControls = new(); @@ -26,7 +26,10 @@ public partial class QuickActions : MetroForm public QuickActions(List quickActions) { - foreach (var quickAction in quickActions) _quickActions.Add(quickAction); + foreach (var quickAction in quickActions) + { + _quickActions.Add(quickAction); + } InitializeComponent(); } @@ -55,7 +58,11 @@ private async void QuickActions_Load(object sender, EventArgs e) // check hass status var hass = await CheckHassManagerAsync(); - if (!hass) CloseWindow(); + if (!hass) + CloseWindow(); + + // select first item + SelectQuickActionItem(0, 0); } /// @@ -73,24 +80,33 @@ private void BuildLayout() _columns = columns; _rows = rows; - + // prepare our panel PnlActions.AutoSize = true; - for (var c = 0; c <= _columns; c++) PnlActions.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 152)); PnlActions.ColumnCount = _columns; - - for (var r = 0; r <= _columns; r++) PnlActions.RowStyles.Add(new RowStyle(SizeType.Absolute, 255)); PnlActions.RowCount = _rows; PnlActions.CellBorderStyle = TableLayoutPanelCellBorderStyle.None; - + + for (var c = 0; c <= _columns; c++) + { + PnlActions.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 152)); + } + + for (var r = 0; r <= _columns; r++) + { + PnlActions.RowStyles.Add(new RowStyle(SizeType.Absolute, 255)); + } + // resize window Width = 152 * columns + 20; Height = 255 * rows + 30; - if (columns > 1) Width += 5 * (columns - 1); - if (rows > 1) Height += 5 * (rows - 1); + if (columns > 1) + Width += 5 * (columns - 1); + if (rows > 1) + Height += 5 * (rows - 1); // add the quickactions as controls var currentColumn = 0; @@ -105,34 +121,57 @@ private void BuildLayout() Row = currentRow }; + // update position when item is selected by mouse cursor + panelControl.QuickActionControl.MouseEnter += QuickActionItemMouseEnter; + // add to list _quickActionPanelControls.Add(panelControl); // store position - if (!_rowColumnCounts.ContainsKey(currentRow)) _rowColumnCounts.Add(currentRow, currentColumn); - else _rowColumnCounts[currentRow] = currentColumn; - + if (!_rowColumnCounts.ContainsKey(currentRow)) + { + _rowColumnCounts.Add(currentRow, currentColumn); + } + else + { + _rowColumnCounts[currentRow] = currentColumn; + } + // add to the panel PnlActions.Controls.Add(quickAction, currentColumn, currentRow); // set next column & row - if (currentColumn < columns - 1) currentColumn++; + if (currentColumn < columns - 1) + { + currentColumn++; + } else { // on to the next row (if there is one) currentColumn = 0; - if (currentRow < rows - 1) currentRow++; + if (currentRow < rows - 1) + currentRow++; } } } + private void QuickActionItemMouseEnter(object sender, EventArgs e) + { + var position = PnlActions.GetPositionFromControl(sender as QuickActionControl); + _selectedColumn = position.Column; + _selectedRow = position.Row; + return; + } + /// /// Tries to close the window /// internal void CloseWindow() { - if (!IsHandleCreated) return; - if (IsDisposed) return; + if (!IsHandleCreated) + return; + if (IsDisposed) + return; Invoke(new MethodInvoker(delegate { @@ -184,8 +223,10 @@ private async Task CheckHassManagerAsync() /// private void SetGuiLoading(bool loading) { - if (!IsHandleCreated) return; - if (IsDisposed) return; + if (!IsHandleCreated) + return; + if (IsDisposed) + return; Invoke(new MethodInvoker(delegate { @@ -216,6 +257,25 @@ private void QuickActions_FormClosing(object sender, FormClosingEventArgs e) } } + /// + /// Selects QuickAction item at given position + /// + /// + /// + /// + private bool SelectQuickActionItem(int row, int column) + { + var control = _quickActionPanelControls.Find(x => x.Row == row && x.Column == column); + if (control == null) + return false; + + control.QuickActionControl.OnFocus(); + _selectedColumn = column; + _selectedRow = row; + + return true; + } + /// /// Intercepts and processes the arrow keys /// @@ -226,83 +286,89 @@ protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { try { - // if never pressed before .. + // should not happen, but select first item if nothing is selected if (_selectedColumn == -1) { - // .. always select first one - var control = _quickActionPanelControls.Find(x => x.Row == 0 && x.Column == 0); - if (control == null) return true; + SelectQuickActionItem(0, 0); - control.QuickActionControl.OnFocus(); - _selectedColumn = 0; - _selectedRow = 0; return true; } if (keyData == Keys.Down) { - // is there a next row? - if (_selectedRow == _rows - 1) return true; - - // jep, select the control below (or the last) - _selectedRow++; - var control = _quickActionPanelControls.Find(x => x.Row == _selectedRow && x.Column == _selectedColumn); - if (control == null) + // wrap up if we're at the last row + if (_selectedRow == _rows - 1) { - // none found with same column, get the last - _selectedColumn = _rowColumnCounts[_selectedRow]; - control = _quickActionPanelControls.Find(x => x.Row == _selectedRow && x.Column == _selectedColumn); - control?.QuickActionControl.OnFocus(); + SelectQuickActionItem(0, _selectedColumn); + return true; } - control.QuickActionControl.OnFocus(); + var nextRow = _selectedRow + 1; + var selected = SelectQuickActionItem(nextRow, _selectedColumn); + if (!selected) + { + SelectQuickActionItem(nextRow, _rowColumnCounts[nextRow]); + } + return true; } if (keyData == Keys.Right) { - // is there a next column? var maxColumnsForRow = _rowColumnCounts[_selectedRow]; - if (_selectedColumn == maxColumnsForRow) return true; - // jep, select the control to the right - _selectedColumn++; - var control = _quickActionPanelControls.Find(x => x.Row == _selectedRow && x.Column == _selectedColumn); - control?.QuickActionControl.OnFocus(); + // wrap up to first row if there is nothing below + if (_selectedColumn == maxColumnsForRow) + { + var nextRow = _selectedRow == (_rows - 1) ? 0 : _selectedRow + 1; + SelectQuickActionItem(nextRow, 0); + + return true; + } + + SelectQuickActionItem(_selectedRow, _selectedColumn + 1); + return true; } if (keyData == Keys.Left) { - // is there a previous column? - if (_selectedColumn == 0) return true; + // wrap up to last row if there is nothing above + if (_selectedColumn == 0) + { + var nextRow = _selectedRow == 0 ? _rows - 1 : _selectedRow - 1; + SelectQuickActionItem(nextRow, _rowColumnCounts[nextRow]); + + return true; + } + + SelectQuickActionItem(_selectedRow, _selectedColumn - 1); - // jep, select the control to the left - _selectedColumn--; - var control = _quickActionPanelControls.Find(x => x.Row == _selectedRow && x.Column == _selectedColumn); - control?.QuickActionControl.OnFocus(); return true; } if (keyData == Keys.Up) { - // is there a previous row? - if (_selectedRow == 0) return true; + var nextRow = _selectedRow - 1; - // jep, select the control above (or the last) - _selectedRow--; - var control = _quickActionPanelControls.Find(x => x.Row == _selectedRow && x.Column == _selectedColumn); - if (control == null) + // wrap down if we're at the first row + if (_selectedRow == 0) { - // none found with same column, get the first - _selectedColumn = 0; - control = _quickActionPanelControls.Find(x => x.Row == _selectedRow && x.Column == _selectedColumn); - control?.QuickActionControl.OnFocus(); + nextRow = _rows - 1; + var maxColumnsForNextRow = _rowColumnCounts[nextRow]; + var nextColumn = maxColumnsForNextRow < _selectedColumn ? maxColumnsForNextRow : _selectedColumn; + SelectQuickActionItem(nextRow, nextColumn); + return true; } - control.QuickActionControl.OnFocus(); + var selected = SelectQuickActionItem(nextRow, _selectedColumn); + if (!selected) + { + SelectQuickActionItem(nextRow, _rowColumnCounts[nextRow]); + } + return true; } } @@ -315,6 +381,27 @@ protected override bool ProcessCmdKey(ref Message msg, Keys keyData) return base.ProcessCmdKey(ref msg, keyData); } + /// + /// Selects next Quick Action item following right->down->up pattern + /// + public void SelectNextQuickActionItem() + { + var maxColumnsForRow = _rowColumnCounts[_selectedRow]; + + // are we at the end of the row / wrap up to first row if there is nothing below + if (_selectedColumn == maxColumnsForRow) + { + var nextRow = _selectedRow == (_rows - 1) ? 0 : _selectedRow + 1; + SelectQuickActionItem(nextRow, 0); + + return; + } + + SelectQuickActionItem(_selectedRow, _selectedColumn + 1); + + return; + } + /// /// Triggers clearing the focus of all controls /// @@ -323,9 +410,12 @@ protected override bool ProcessCmdKey(ref Message msg, Keys keyData) private void QuickActions_ResizeEnd(object sender, EventArgs e) { - if (Variables.ShuttingDown) return; - if (!IsHandleCreated) return; - if (IsDisposed) return; + if (Variables.ShuttingDown) + return; + if (!IsHandleCreated) + return; + if (IsDisposed) + return; try { diff --git a/src/HASS.Agent.Staging/HASS.Agent/Functions/HelperFunctions.cs b/src/HASS.Agent.Staging/HASS.Agent/Functions/HelperFunctions.cs index e41bfe44..f8f7b359 100644 --- a/src/HASS.Agent.Staging/HASS.Agent/Functions/HelperFunctions.cs +++ b/src/HASS.Agent.Staging/HASS.Agent/Functions/HelperFunctions.cs @@ -360,9 +360,11 @@ internal static void OpenLocalFolder(string path) /// /// Checks whether the provided form is already opened /// - /// + /// /// - internal static bool CheckIfFormIsOpen(string formname) => Application.OpenForms.Cast
().Any(form => form?.Name == formname); + internal static bool CheckIfFormIsOpen(string formName) => Application.OpenForms.Cast().Any(form => form?.Name == formName); + + internal static Form GetForm(string formName) => Application.OpenForms.Cast().FirstOrDefault(x => x.Name == formName); /// /// Launches the url with the user's custom browser if provided, or the system's default @@ -486,7 +488,7 @@ internal static void LaunchTrayIconWebView(WebViewInfo webViewInfo) // show a new webview from within the UI thread LaunchTrayIconCustomWebView(webViewInfo); } - + private static void LaunchTrayIconBackgroundLoadedWebView() { Variables.MainForm.Invoke(new MethodInvoker(delegate @@ -566,14 +568,12 @@ internal static bool InputLanguageCheckDiffers(out bool knownToCollide, out stri /// /// Attempts to bring the provided form to the foreground if it's open /// - /// + /// /// - internal static async Task TryBringToFront(string formName) + internal static async Task TryBringToFront(Form form) { try { - // is it open? - var form = Application.OpenForms.Cast().FirstOrDefault(x => x.Name == formName); if (form == null) return false; // yep, check if we need to undo minimized @@ -594,6 +594,25 @@ internal static async Task TryBringToFront(string formName) } } + /// + /// Attempts to bring the provided form to the foreground if it's open + /// + /// + /// + internal static async Task TryBringToFront(string formName) + { + try + { + var form = GetForm(formName); + return await TryBringToFront(form); + } + catch (Exception ex) + { + Log.Fatal(ex, ex.Message); + return false; + } + } + /// /// Checks the local file to see if it has the right signature /// @@ -642,16 +661,16 @@ internal static bool ConfirmCertificate(string localFile) return false; } } - + /// /// Returns the configured device name, or a safe version of the machinename if nothing's stored /// /// internal static string GetConfiguredDeviceName() => - string.IsNullOrEmpty(Variables.AppSettings?.DeviceName) - ? SharedHelperFunctions.GetSafeDeviceName() + string.IsNullOrEmpty(Variables.AppSettings?.DeviceName) + ? SharedHelperFunctions.GetSafeDeviceName() : SharedHelperFunctions.GetSafeValue(Variables.AppSettings.DeviceName); - + /// /// Checks whether the process is currently running under the current user, by default ignoring the current process ///