Add Sessions menu with keyboard shortcuts for tab navigation

Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-02-13 10:36:32 +00:00
parent e9f94cbe31
commit 71911bba7b
5 changed files with 202 additions and 1 deletions

View File

@@ -2559,4 +2559,16 @@ Nightly Channel includes Alphas, Betas &amp; Release Candidates.</value>
<data name="VaultOpenbaoSecretEngineSSHOTP" xml:space="preserve">
<value>SSH engine OTP mode</value>
</data>
<data name="_Sessions" xml:space="preserve">
<value>&amp;Sessions</value>
</data>
<data name="NextSession" xml:space="preserve">
<value>Next Session</value>
</data>
<data name="PreviousSession" xml:space="preserve">
<value>Previous Session</value>
</data>
<data name="JumpToSession" xml:space="preserve">
<value>Jump to Session {0}</value>
</data>
</root>

View File

@@ -38,6 +38,7 @@ namespace mRemoteNG.UI.Forms
this.pnlDock = new WeifenLuo.WinFormsUI.Docking.DockPanel();
this.msMain = new System.Windows.Forms.MenuStrip();
this.fileMenu = new mRemoteNG.UI.Menu.FileMenu();
this.sessionsMenu = new mRemoteNG.UI.Menu.SessionsMenu();
this.viewMenu = new mRemoteNG.UI.Menu.ViewMenu();
this.toolsMenu = new mRemoteNG.UI.Menu.ToolsMenu();
this.helpMenu = new mRemoteNG.UI.Menu.HelpMenu();
@@ -75,13 +76,14 @@ namespace mRemoteNG.UI.Forms
this.msMain.GripStyle = System.Windows.Forms.ToolStripGripStyle.Visible;
this.msMain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.fileMenu,
this.sessionsMenu,
this.viewMenu,
this.toolsMenu,
this.helpMenu});
this.msMain.Location = new System.Drawing.Point(3, 0);
this.msMain.Name = "msMain";
this.msMain.Padding = new System.Windows.Forms.Padding(0, 0, 1, 0);
this.msMain.Size = new System.Drawing.Size(151, 25);
this.msMain.Size = new System.Drawing.Size(212, 25);
this.msMain.Stretch = false;
this.msMain.TabIndex = 0;
this.msMain.Text = "Main Toolbar";
@@ -94,6 +96,13 @@ namespace mRemoteNG.UI.Forms
this.fileMenu.Text = "&File";
this.fileMenu.TreeWindow = null;
//
// sessionsMenu
//
this.sessionsMenu.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
this.sessionsMenu.Name = "mMenSessions";
this.sessionsMenu.Size = new System.Drawing.Size(61, 19);
this.sessionsMenu.Text = "&Sessions";
//
// viewMenu
//
this.viewMenu.FullscreenHandler = null;
@@ -253,6 +262,7 @@ namespace mRemoteNG.UI.Forms
internal System.Windows.Forms.ToolStripSeparator mMenSep3;
private System.ComponentModel.IContainer components;
private Menu.FileMenu fileMenu;
private Menu.SessionsMenu sessionsMenu;
private Menu.ViewMenu viewMenu;
private Menu.ToolsMenu toolsMenu;
private Menu.HelpMenu helpMenu;

View File

@@ -292,6 +292,7 @@ namespace mRemoteNG.UI.Forms
private void ApplyLanguage()
{
fileMenu.ApplyLanguage();
sessionsMenu.ApplyLanguage();
viewMenu.ApplyLanguage();
toolsMenu.ApplyLanguage();
helpMenu.ApplyLanguage();

View File

@@ -0,0 +1,150 @@
using System;
using System.Windows.Forms;
using mRemoteNG.UI.Window;
using mRemoteNG.Resources.Language;
using System.Runtime.Versioning;
using mRemoteNG.UI.Forms;
namespace mRemoteNG.UI.Menu
{
[SupportedOSPlatform("windows")]
public class SessionsMenu : ToolStripMenuItem
{
private ToolStripMenuItem _mMenSessionsNextSession;
private ToolStripMenuItem _mMenSessionsPreviousSession;
private ToolStripSeparator _mMenSessionsSep1;
private readonly ToolStripMenuItem[] _sessionNumberItems = new ToolStripMenuItem[9];
public SessionsMenu()
{
Initialize();
}
private void Initialize()
{
_mMenSessionsNextSession = new ToolStripMenuItem();
_mMenSessionsPreviousSession = new ToolStripMenuItem();
_mMenSessionsSep1 = new ToolStripSeparator();
// Initialize session number menu items (Ctrl+1 through Ctrl+9)
for (int i = 0; i < 9; i++)
{
_sessionNumberItems[i] = new ToolStripMenuItem();
}
//
// mMenSessions
//
DropDownItems.Add(_mMenSessionsNextSession);
DropDownItems.Add(_mMenSessionsPreviousSession);
DropDownItems.Add(_mMenSessionsSep1);
for (int i = 0; i < 9; i++)
{
DropDownItems.Add(_sessionNumberItems[i]);
}
Name = "mMenSessions";
Size = new System.Drawing.Size(61, 20);
Text = Language._Sessions;
//
// mMenSessionsNextSession
//
_mMenSessionsNextSession.Name = "mMenSessionsNextSession";
_mMenSessionsNextSession.ShortcutKeys = Keys.Control | Keys.Right;
_mMenSessionsNextSession.Size = new System.Drawing.Size(230, 22);
_mMenSessionsNextSession.Text = Language.NextSession;
_mMenSessionsNextSession.Click += mMenSessionsNextSession_Click;
//
// mMenSessionsPreviousSession
//
_mMenSessionsPreviousSession.Name = "mMenSessionsPreviousSession";
_mMenSessionsPreviousSession.ShortcutKeys = Keys.Control | Keys.Left;
_mMenSessionsPreviousSession.Size = new System.Drawing.Size(230, 22);
_mMenSessionsPreviousSession.Text = Language.PreviousSession;
_mMenSessionsPreviousSession.Click += mMenSessionsPreviousSession_Click;
//
// mMenSessionsSep1
//
_mMenSessionsSep1.Name = "mMenSessionsSep1";
_mMenSessionsSep1.Size = new System.Drawing.Size(227, 6);
// Initialize session number items (Ctrl+1 through Ctrl+9)
for (int i = 0; i < 9; i++)
{
int sessionNumber = i + 1;
_sessionNumberItems[i].Name = $"mMenSessionsSession{sessionNumber}";
_sessionNumberItems[i].ShortcutKeys = Keys.Control | (Keys.D1 + i);
_sessionNumberItems[i].Size = new System.Drawing.Size(230, 22);
_sessionNumberItems[i].Text = string.Format(Language.JumpToSession, sessionNumber);
int capturedIndex = i; // Capture the index for the lambda
_sessionNumberItems[i].Click += (s, e) => JumpToSessionNumber(capturedIndex);
}
// Hook up the dropdown opening event to update enabled state
DropDownOpening += SessionsMenu_DropDownOpening;
}
public void ApplyLanguage()
{
Text = Language._Sessions;
_mMenSessionsNextSession.Text = Language.NextSession;
_mMenSessionsPreviousSession.Text = Language.PreviousSession;
for (int i = 0; i < 9; i++)
{
_sessionNumberItems[i].Text = string.Format(Language.JumpToSession, i + 1);
}
}
private void SessionsMenu_DropDownOpening(object sender, EventArgs e)
{
// Update enabled state of menu items based on active sessions
var connectionWindow = GetActiveConnectionWindow();
bool hasMultipleSessions = false;
int sessionCount = 0;
if (connectionWindow != null)
{
var documents = connectionWindow.GetDocuments();
sessionCount = documents.Length;
hasMultipleSessions = sessionCount > 1;
}
_mMenSessionsNextSession.Enabled = hasMultipleSessions;
_mMenSessionsPreviousSession.Enabled = hasMultipleSessions;
// Enable/disable session number items based on session count
for (int i = 0; i < 9; i++)
{
_sessionNumberItems[i].Enabled = (i < sessionCount);
}
}
private void mMenSessionsNextSession_Click(object sender, EventArgs e)
{
var connectionWindow = GetActiveConnectionWindow();
connectionWindow?.NavigateToNextTab();
}
private void mMenSessionsPreviousSession_Click(object sender, EventArgs e)
{
var connectionWindow = GetActiveConnectionWindow();
connectionWindow?.NavigateToPreviousTab();
}
private void JumpToSessionNumber(int index)
{
var connectionWindow = GetActiveConnectionWindow();
connectionWindow?.NavigateToTab(index);
}
private ConnectionWindow GetActiveConnectionWindow()
{
return FrmMain.Default.pnlDock?.ActiveDocument as ConnectionWindow;
}
}
}

View File

@@ -387,6 +387,34 @@ namespace mRemoteNG.UI.Window
}
}
internal void NavigateToTab(int index)
{
try
{
var documents = connDock.DocumentsToArray();
if (index < 0 || index >= documents.Length) return;
documents[index].DockHandler.Activate();
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionMessage("NavigateToTab (UI.Window.ConnectionWindow) failed", ex);
}
}
internal IDockContent[] GetDocuments()
{
try
{
return connDock.DocumentsToArray();
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionMessage("GetDocuments (UI.Window.ConnectionWindow) failed", ex);
return Array.Empty<IDockContent>();
}
}
#endregion
#region Events