New Preferences "Appearance" tab: theme + show-pan/EQ moved in, tab reordering, peer-list toggles
Adds an Appearance tab (after General) and moves the colour theme and "show volume/pan/EQ tab" into it, per Ed. New on that tab: * Main tab order — a list of the four main-window tabs (all shown, even when the pan/EQ tab is hidden) with Move up / Move down buttons to reorder them. Saved to AppConfig.MainTabOrder and applied by the new ApplyMainTabLayout (rebuilds the tab strip in order, dropping pan/EQ when off, preserving selection). Replaces RefreshPanEqTabVisibility. * Enable discovered / remembered peers lists on the Connectivity tab (AppConfig.ShowDiscoveredPeers / ShowRememberedPeers, both default on). RefreshConnectivityListVisibility hides the row's label and its list wrapper when off. Row labels are now captured for this. All apply when Preferences closes. Manual updated. Build clean; --selftest passes; deployed to both test folders. Held for next release. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
9c8e50667b
commit
78684f29f1
@@ -276,7 +276,7 @@ Item| Shortcut| What it does
|
|||||||
**Manage named peers …**| Alt+O, N| Opens a list of every peer you've given a friendly name to, showing each one's machine name and where and when you last connected to it. Pick one and **Rename (Alt+R** , or F2) to change its name, or **Delete (Alt+D** , or the Del key) to forget it — deleting only drops the name, the peer still connects as normal under its machine name. See the peer-naming notes on the Connectivity tab.
|
**Manage named peers …**| Alt+O, N| Opens a list of every peer you've given a friendly name to, showing each one's machine name and where and when you last connected to it. Pick one and **Rename (Alt+R** , or F2) to change its name, or **Delete (Alt+D** , or the Del key) to forget it — deleting only drops the name, the peer still connects as normal under its machine name. See the peer-naming notes on the Connectivity tab.
|
||||||
**Reset the default audio device prompt**| Alt+O, R| Brings back the “use only the default audio device?” question if you previously ticked “Don't ask me this again” on it. See Following the Windows default audio device.
|
**Reset the default audio device prompt**| Alt+O, R| Brings back the “use only the default audio device?” question if you previously ticked “Don't ask me this again” on it. See Following the Windows default audio device.
|
||||||
**Enable / Disable Realtek ASIO**| —| Only shown if a Realtek ASIO driver is installed. Lets you reverse the choice RemSound offered about disabling that driver (Realtek's generic ASIO driver tends to grab the wrong device and clash with your screen reader).
|
**Enable / Disable Realtek ASIO**| —| Only shown if a Realtek ASIO driver is installed. Lets you reverse the choice RemSound offered about disabling that driver (Realtek's generic ASIO driver tends to grab the wrong device and clash with your screen reader).
|
||||||
**Preferences …**| Ctrl+P, or Alt+O, P| Opens the Preferences dialog, organised into five tabs (move between them with Ctrl+Tab, or the arrow keys when the tab names have focus): **General** — the profiles folder, accept remote volume commands, UPnP router opening, “Show the volume, pan and EQ for peers tab” (on by default; hides the Volume, pan and EQ for peers tab if you untick it), and **Colour theme (Alt+T)** — Match Windows (the default), Light, or Dark; this only changes how the window looks, takes effect the next time you start RemSound, and has no effect on the screen reader; **Audio cues** — the cue list and its sounds (see Audio cue sounds); **Startup behaviour** — start minimised / with Windows / with a specific profile; **Update settings** — the update checks and install options; and **Logging** — enable logs, write logs now, and the log-folder housekeeping (see Logs and diagnostics). Esc or the Close button dismisses it.
|
**Preferences …**| Ctrl+P, or Alt+O, P| Opens the Preferences dialog, organised into six tabs (move between them with Ctrl+Tab, or the arrow keys when the tab names have focus): **General** — the profiles folder, accept remote volume commands, and UPnP router opening; **Appearance** — **Colour theme (Alt+T)** (Match Windows / Light / Dark; changes only how the window looks, takes effect next launch, no effect on the screen reader), “Show the volume, pan and EQ for peers tab” (on by default; hides the Volume, pan and EQ for peers tab if you untick it), **Main tab order (Alt+O)** — a list of the window's tabs with **Move up (Alt+U)** and **Move down (Alt+N)** buttons to reorder them, and two toggles to **enable the Discovered (Alt+D)** and **Remembered (Alt+R)** peer lists on the Connectivity tab (both on by default — untick one to hide that list); **Audio cues** — the cue list and its sounds (see Audio cue sounds); **Startup behaviour** — start minimised / with Windows / with a specific profile; **Update settings** — the update checks and install options; and **Logging** — enable logs, write logs now, and the log-folder housekeeping (see Logs and diagnostics). Esc or the Close button dismisses it.
|
||||||
|
|
||||||
### Help menu
|
### Help menu
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -312,7 +312,7 @@ ul, ol { padding-left: 1.4em; }
|
|||||||
<tr><td><strong>Manage named peers…</strong></td><td>Alt+O, N</td><td>Opens a list of every peer you've given a friendly name to, showing each one's machine name and where and when you last connected to it. Pick one and <strong>Rename (Alt+R</strong>, or F2) to change its name, or <strong>Delete (Alt+D</strong>, or the Del key) to forget it — deleting only drops the name, the peer still connects as normal under its machine name. See <a href="#tab-connectivity">the peer-naming notes on the Connectivity tab</a>.</td></tr>
|
<tr><td><strong>Manage named peers…</strong></td><td>Alt+O, N</td><td>Opens a list of every peer you've given a friendly name to, showing each one's machine name and where and when you last connected to it. Pick one and <strong>Rename (Alt+R</strong>, or F2) to change its name, or <strong>Delete (Alt+D</strong>, or the Del key) to forget it — deleting only drops the name, the peer still connects as normal under its machine name. See <a href="#tab-connectivity">the peer-naming notes on the Connectivity tab</a>.</td></tr>
|
||||||
<tr><td><strong>Reset the default audio device prompt</strong></td><td>Alt+O, R</td><td>Brings back the “use only the default audio device?” question if you previously ticked “Don't ask me this again” on it. See <a href="#tab-io">Following the Windows default audio device</a>.</td></tr>
|
<tr><td><strong>Reset the default audio device prompt</strong></td><td>Alt+O, R</td><td>Brings back the “use only the default audio device?” question if you previously ticked “Don't ask me this again” on it. See <a href="#tab-io">Following the Windows default audio device</a>.</td></tr>
|
||||||
<tr><td><strong>Enable / Disable Realtek ASIO</strong></td><td>—</td><td>Only shown if a Realtek ASIO driver is installed. Lets you reverse the choice RemSound offered about disabling that driver (Realtek's generic ASIO driver tends to grab the wrong device and clash with your screen reader).</td></tr>
|
<tr><td><strong>Enable / Disable Realtek ASIO</strong></td><td>—</td><td>Only shown if a Realtek ASIO driver is installed. Lets you reverse the choice RemSound offered about disabling that driver (Realtek's generic ASIO driver tends to grab the wrong device and clash with your screen reader).</td></tr>
|
||||||
<tr><td><strong>Preferences…</strong></td><td>Ctrl+P, or Alt+O, P</td><td>Opens the Preferences dialog, organised into five tabs (move between them with Ctrl+Tab, or the arrow keys when the tab names have focus): <strong>General</strong> — the profiles folder, accept remote volume commands, UPnP router opening, “Show the volume, pan and EQ for peers tab” (on by default; hides the <a href="#pan-eq">Volume, pan and EQ for peers tab</a> if you untick it), and <strong>Colour theme (Alt+T)</strong> — Match Windows (the default), Light, or Dark; this only changes how the window looks, takes effect the next time you start RemSound, and has no effect on the screen reader; <strong>Audio cues</strong> — the cue list and its sounds (see <a href="#audio-cues">Audio cue sounds</a>); <strong>Startup behaviour</strong> — start minimised / with Windows / with a specific profile; <strong>Update settings</strong> — the update checks and install options; and <strong>Logging</strong> — enable logs, write logs now, and the log-folder housekeeping (see <a href="#logs">Logs and diagnostics</a>). Esc or the Close button dismisses it.</td></tr>
|
<tr><td><strong>Preferences…</strong></td><td>Ctrl+P, or Alt+O, P</td><td>Opens the Preferences dialog, organised into six tabs (move between them with Ctrl+Tab, or the arrow keys when the tab names have focus): <strong>General</strong> — the profiles folder, accept remote volume commands, and UPnP router opening; <strong>Appearance</strong> — <strong>Colour theme (Alt+T)</strong> (Match Windows / Light / Dark; changes only how the window looks, takes effect next launch, no effect on the screen reader), “Show the volume, pan and EQ for peers tab” (on by default; hides the <a href="#pan-eq">Volume, pan and EQ for peers tab</a> if you untick it), <strong>Main tab order (Alt+O)</strong> — a list of the window's tabs with <strong>Move up (Alt+U)</strong> and <strong>Move down (Alt+N)</strong> buttons to reorder them, and two toggles to <strong>enable the Discovered (Alt+D)</strong> and <strong>Remembered (Alt+R)</strong> peer lists on the Connectivity tab (both on by default — untick one to hide that list); <strong>Audio cues</strong> — the cue list and its sounds (see <a href="#audio-cues">Audio cue sounds</a>); <strong>Startup behaviour</strong> — start minimised / with Windows / with a specific profile; <strong>Update settings</strong> — the update checks and install options; and <strong>Logging</strong> — enable logs, write logs now, and the log-folder housekeeping (see <a href="#logs">Logs and diagnostics</a>). Esc or the Close button dismisses it.</td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h3>Help menu</h3>
|
<h3>Help menu</h3>
|
||||||
|
|||||||
@@ -274,6 +274,10 @@ public sealed class MainForm : Form
|
|||||||
// Read-only details for the peer highlighted in the connected list, and a button to give it a name.
|
// Read-only details for the peer highlighted in the connected list, and a button to give it a name.
|
||||||
private readonly TextBox peerDetailsBox = new() { Multiline = true, ReadOnly = true, Width = 430, Height = 132, ScrollBars = ScrollBars.Vertical, AccessibleName = "Peer details (Alt+E)" };
|
private readonly TextBox peerDetailsBox = new() { Multiline = true, ReadOnly = true, Width = 430, Height = 132, ScrollBars = ScrollBars.Vertical, AccessibleName = "Peer details (Alt+E)" };
|
||||||
private readonly Button renamePeerButton = new() { Text = "Rena&me peer (Alt+M)", AutoSize = true, AccessibleName = "Rename peer" };
|
private readonly Button renamePeerButton = new() { Text = "Rena&me peer (Alt+M)", AutoSize = true, AccessibleName = "Rename peer" };
|
||||||
|
// The Discovered / Remembered row labels, captured so those rows can be hidden when the user turns
|
||||||
|
// the lists off on the Preferences Appearance tab.
|
||||||
|
private MnemonicLabel? discoveredPeersLabel;
|
||||||
|
private MnemonicLabel? rememberedPeersLabel;
|
||||||
// Per-profile "pin to exact addresses" toggle (#17). When ticked, RefreshKnownPeers skips the
|
// Per-profile "pin to exact addresses" toggle (#17). When ticked, RefreshKnownPeers skips the
|
||||||
// discovered-peer merge and the address-follow, so the profile's peers stay exactly as the user set.
|
// discovered-peer merge and the address-follow, so the profile's peers stay exactly as the user set.
|
||||||
private readonly AccessibleCheckBox lockPeerAddressesBox = new()
|
private readonly AccessibleCheckBox lockPeerAddressesBox = new()
|
||||||
@@ -1749,11 +1753,8 @@ public sealed class MainForm : Form
|
|||||||
BuildAudioProfileTab();
|
BuildAudioProfileTab();
|
||||||
BuildPanEqTab();
|
BuildPanEqTab();
|
||||||
|
|
||||||
mainTabControl.TabPages.Add(connectivityTabPage);
|
// Add the tabs in the user's saved order (and honour the "show pan/EQ tab" toggle).
|
||||||
mainTabControl.TabPages.Add(audioIOTabPage);
|
ApplyMainTabLayout();
|
||||||
mainTabControl.TabPages.Add(audioProfileTabPage);
|
|
||||||
// Insert the optional Pan-and-EQ tab (just before Audio profile) if the preference is on.
|
|
||||||
RefreshPanEqTabVisibility();
|
|
||||||
// No SelectedIndexChanged handler. No focus management on tab change. Andre's
|
// No SelectedIndexChanged handler. No focus management on tab change. Andre's
|
||||||
// accessible app does ZERO event hooking on TabControl — relies entirely on
|
// accessible app does ZERO event hooking on TabControl — relies entirely on
|
||||||
// default WinForms + NVDA behaviour. Per Ed's repeated request: arrow keys cycle
|
// default WinForms + NVDA behaviour. Per Ed's repeated request: arrow keys cycle
|
||||||
@@ -2595,8 +2596,9 @@ public sealed class MainForm : Form
|
|||||||
unsubscribeUpnpStatusChanged: handler => routerPortMapper.StatusChanged -= handler);
|
unsubscribeUpnpStatusChanged: handler => routerPortMapper.StatusChanged -= handler);
|
||||||
dialog.ShowDialog(this);
|
dialog.ShowDialog(this);
|
||||||
if (dialog.ChangedAnyProfileSetting) MarkProfileDirty();
|
if (dialog.ChangedAnyProfileSetting) MarkProfileDirty();
|
||||||
// The "Show pan and EQ tab" preference may have been toggled — add/remove the tab to match.
|
// Appearance-tab changes (tab order, show pan/EQ tab, show discovered/remembered lists) apply now.
|
||||||
RefreshPanEqTabVisibility();
|
ApplyMainTabLayout();
|
||||||
|
RefreshConnectivityListVisibility();
|
||||||
// The Preferences dialog includes per-cue Browse buttons that can change custom
|
// The Preferences dialog includes per-cue Browse buttons that can change custom
|
||||||
// WAV paths in AppConfig.CustomCuePaths. Reload the cached SoundPlayer instances
|
// WAV paths in AppConfig.CustomCuePaths. Reload the cached SoundPlayer instances
|
||||||
// here unconditionally — cheap, only six small files, and guarantees the next
|
// here unconditionally — cheap, only six small files, and guarantees the next
|
||||||
@@ -3038,8 +3040,8 @@ public sealed class MainForm : Form
|
|||||||
panel.Controls.Add(renamePeerButton, 1, 3);
|
panel.Controls.Add(renamePeerButton, 1, 3);
|
||||||
connectedPeersList.SelectedIndexChanged += (_, _) => UpdatePeerDetails();
|
connectedPeersList.SelectedIndexChanged += (_, _) => UpdatePeerDetails();
|
||||||
|
|
||||||
FormLayoutRows.AddCheckedListRow(panel, 4, "Discovered peers (Alt+&D)", discoveredPeersList, discoveredPeersStatus, FocusListControl);
|
discoveredPeersLabel = FormLayoutRows.AddCheckedListRow(panel, 4, "Discovered peers (Alt+&D)", discoveredPeersList, discoveredPeersStatus, FocusListControl);
|
||||||
FormLayoutRows.AddCheckedListRow(panel, 5, "Remembered peers (Alt+&R)", rememberedPeersList, rememberedPeersStatus, FocusListControl);
|
rememberedPeersLabel = FormLayoutRows.AddCheckedListRow(panel, 5, "Remembered peers (Alt+&R)", rememberedPeersList, rememberedPeersStatus, FocusListControl);
|
||||||
|
|
||||||
// Add a peer by address, then the lock toggle — the manual / advanced options after the lists.
|
// Add a peer by address, then the lock toggle — the manual / advanced options after the lists.
|
||||||
panel.Controls.Add(new Label { Text = "Manual peer", AutoSize = true, Anchor = AnchorStyles.Left }, 0, 6);
|
panel.Controls.Add(new Label { Text = "Manual peer", AutoSize = true, Anchor = AnchorStyles.Left }, 0, 6);
|
||||||
@@ -3083,6 +3085,23 @@ public sealed class MainForm : Form
|
|||||||
connectivityTabPage.Controls.Add(panel);
|
connectivityTabPage.Controls.Add(panel);
|
||||||
// Initial population so screen readers see something on first open.
|
// Initial population so screen readers see something on first open.
|
||||||
SyncAllPeerLists();
|
SyncAllPeerLists();
|
||||||
|
RefreshConnectivityListVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Shows or hides the Discovered / Remembered peer rows to match the Appearance-tab
|
||||||
|
/// preferences. Hiding both a row's label and its list wrapper collapses the row. Called at build
|
||||||
|
/// and after Preferences close.</summary>
|
||||||
|
private void RefreshConnectivityListVisibility()
|
||||||
|
{
|
||||||
|
var cfg = AppConfig.Load();
|
||||||
|
SetConnectivityRowVisible(discoveredPeersLabel, discoveredPeersList, cfg.ShowDiscoveredPeers);
|
||||||
|
SetConnectivityRowVisible(rememberedPeersLabel, rememberedPeersList, cfg.ShowRememberedPeers);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetConnectivityRowVisible(Control? label, Control list, bool visible)
|
||||||
|
{
|
||||||
|
if (label is not null) label.Visible = visible;
|
||||||
|
if (list.Parent is not null) list.Parent.Visible = visible; // the FlowLayoutPanel wrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Audio I/O tab — full content. All the existing main-form audio controls
|
/// <summary>Audio I/O tab — full content. All the existing main-form audio controls
|
||||||
@@ -3288,22 +3307,53 @@ public sealed class MainForm : Form
|
|||||||
OnPanEqPeerSelected();
|
OnPanEqPeerSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Adds (or removes) the Pan-and-EQ tab to match the machine-wide "Show pan and EQ tab"
|
// The main-window tabs' stable keys, in the default order. The user can reorder them on the
|
||||||
/// preference. Placed just before the Audio profile tab. Called at build and after Preferences close.</summary>
|
// Preferences Appearance tab; "paneq" is only shown when the "show pan/EQ tab" preference is on.
|
||||||
private void RefreshPanEqTabVisibility()
|
private static readonly string[] DefaultTabOrder = ["connectivity", "audioio", "paneq", "audioprofile"];
|
||||||
|
|
||||||
|
private TabPage? TabPageForKey(string key) => key switch
|
||||||
{
|
{
|
||||||
bool show = AppConfig.Load().ShowPanEqTab;
|
"connectivity" => connectivityTabPage,
|
||||||
bool present = mainTabControl.TabPages.Contains(panEqTabPage);
|
"audioio" => audioIOTabPage,
|
||||||
if (show && !present)
|
"paneq" => panEqTabPage,
|
||||||
|
"audioprofile" => audioProfileTabPage,
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>Rebuilds the main tab strip in the user's saved order, dropping the pan/EQ tab if that
|
||||||
|
/// preference is off. Preserves the current selection. Called at build and after Preferences close.</summary>
|
||||||
|
private void ApplyMainTabLayout()
|
||||||
{
|
{
|
||||||
int idx = mainTabControl.TabPages.IndexOf(audioProfileTabPage);
|
var cfg = AppConfig.Load();
|
||||||
if (idx < 0) idx = mainTabControl.TabPages.Count;
|
var order = NormalizeTabOrder(cfg.MainTabOrder);
|
||||||
mainTabControl.TabPages.Insert(idx, panEqTabPage);
|
bool showEq = cfg.ShowPanEqTab;
|
||||||
|
var selected = mainTabControl.SelectedTab;
|
||||||
|
mainTabControl.SuspendLayout();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mainTabControl.TabPages.Clear();
|
||||||
|
foreach (var key in order)
|
||||||
|
{
|
||||||
|
if (key == "paneq" && !showEq) continue;
|
||||||
|
if (TabPageForKey(key) is { } page) mainTabControl.TabPages.Add(page);
|
||||||
}
|
}
|
||||||
else if (!show && present)
|
if (selected is not null && mainTabControl.TabPages.Contains(selected))
|
||||||
{
|
mainTabControl.SelectedTab = selected;
|
||||||
mainTabControl.TabPages.Remove(panEqTabPage);
|
|
||||||
}
|
}
|
||||||
|
finally { mainTabControl.ResumeLayout(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Cleans a saved tab order: keep only known keys (in saved order, de-duplicated), then
|
||||||
|
/// append any known keys the saved list was missing, so the result always has all four.</summary>
|
||||||
|
private static List<string> NormalizeTabOrder(List<string>? saved)
|
||||||
|
{
|
||||||
|
var result = new List<string>();
|
||||||
|
if (saved is not null)
|
||||||
|
foreach (var k in saved)
|
||||||
|
if (Array.IndexOf(DefaultTabOrder, k) >= 0 && !result.Contains(k)) result.Add(k);
|
||||||
|
foreach (var k in DefaultTabOrder)
|
||||||
|
if (!result.Contains(k)) result.Add(k);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Rebuilds the peer picker from the currently-connected peers, one row per address. Runs
|
/// <summary>Rebuilds the peer picker from the currently-connected peers, one row per address. Runs
|
||||||
|
|||||||
@@ -287,6 +287,45 @@ internal sealed class PreferencesDialog : Form
|
|||||||
AccessibleName = "Colour theme (Alt+T)",
|
AccessibleName = "Colour theme (Alt+T)",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Appearance tab — reorder the main-window tabs, and toggle the Connectivity-tab peer lists.
|
||||||
|
private readonly ListBox tabOrderList = new()
|
||||||
|
{
|
||||||
|
Width = 320,
|
||||||
|
Height = 96,
|
||||||
|
IntegralHeight = false,
|
||||||
|
AccessibleName = "Main tab order (Alt+O), then Move up / Move down to reorder",
|
||||||
|
};
|
||||||
|
private readonly Button moveTabUpButton = new() { Text = "Move &up (Alt+U)", AutoSize = true, AccessibleName = "Move tab up" };
|
||||||
|
private readonly Button moveTabDownButton = new() { Text = "Move dow&n (Alt+N)", AutoSize = true, AccessibleName = "Move tab down" };
|
||||||
|
private readonly AccessibleCheckBox enableDiscoveredPeersBox = new()
|
||||||
|
{
|
||||||
|
Text = "Enable the &discovered peers list on the Connectivity tab (Alt+D)",
|
||||||
|
AccessibleName = "Enable the discovered peers list on the Connectivity tab",
|
||||||
|
AutoSize = true,
|
||||||
|
};
|
||||||
|
private readonly AccessibleCheckBox enableRememberedPeersBox = new()
|
||||||
|
{
|
||||||
|
Text = "Enable the &remembered peers list on the Connectivity tab (Alt+R)",
|
||||||
|
AccessibleName = "Enable the remembered peers list on the Connectivity tab",
|
||||||
|
AutoSize = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
// The main-window tabs shown in the reorder list (always all four, even if the pan/EQ tab is
|
||||||
|
// currently hidden). Key matches AppConfig.MainTabOrder / MainForm.TabPageForKey.
|
||||||
|
private static readonly (string Key, string Display)[] KnownTabs =
|
||||||
|
[
|
||||||
|
("connectivity", "Connectivity"),
|
||||||
|
("audioio", "Audio inputs and outputs"),
|
||||||
|
("paneq", "Volume, pan and EQ for peers"),
|
||||||
|
("audioprofile", "Audio profile"),
|
||||||
|
];
|
||||||
|
|
||||||
|
private sealed class TabOrderItem(string key, string display)
|
||||||
|
{
|
||||||
|
public string Key { get; } = key;
|
||||||
|
public override string ToString() => display;
|
||||||
|
}
|
||||||
|
|
||||||
private readonly Label upnpStatusLabel = new()
|
private readonly Label upnpStatusLabel = new()
|
||||||
{
|
{
|
||||||
Text = "",
|
Text = "",
|
||||||
@@ -649,6 +688,59 @@ internal sealed class PreferencesDialog : Form
|
|||||||
themeRow.Controls.Add(themeBox);
|
themeRow.Controls.Add(themeBox);
|
||||||
themeRow.Controls.Add(themeHint);
|
themeRow.Controls.Add(themeHint);
|
||||||
|
|
||||||
|
// --- Appearance tab: reorder the main tabs, and toggle the Connectivity peer lists ---
|
||||||
|
// Populate the reorder list from the saved order, normalised so all four tabs always appear.
|
||||||
|
var orderedKeys = new List<string>();
|
||||||
|
if (cfgForLoad.MainTabOrder is { } savedOrder)
|
||||||
|
foreach (var k in savedOrder)
|
||||||
|
if (Array.Exists(KnownTabs, t => t.Key == k) && !orderedKeys.Contains(k)) orderedKeys.Add(k);
|
||||||
|
foreach (var (key, _) in KnownTabs)
|
||||||
|
if (!orderedKeys.Contains(key)) orderedKeys.Add(key);
|
||||||
|
foreach (var key in orderedKeys)
|
||||||
|
tabOrderList.Items.Add(new TabOrderItem(key, Array.Find(KnownTabs, t => t.Key == key).Display));
|
||||||
|
if (tabOrderList.Items.Count > 0) tabOrderList.SelectedIndex = 0;
|
||||||
|
|
||||||
|
void SaveTabOrder()
|
||||||
|
{
|
||||||
|
var cfg = AppConfig.Load();
|
||||||
|
cfg.MainTabOrder = tabOrderList.Items.Cast<TabOrderItem>().Select(t => t.Key).ToList();
|
||||||
|
try { cfg.Save(); } catch { /* harmless — order just won't survive a restart */ }
|
||||||
|
}
|
||||||
|
void MoveTab(int delta)
|
||||||
|
{
|
||||||
|
int i = tabOrderList.SelectedIndex;
|
||||||
|
if (i < 0) return;
|
||||||
|
int j = i + delta;
|
||||||
|
if (j < 0 || j >= tabOrderList.Items.Count) return;
|
||||||
|
var item = tabOrderList.Items[i];
|
||||||
|
tabOrderList.Items.RemoveAt(i);
|
||||||
|
tabOrderList.Items.Insert(j, item);
|
||||||
|
tabOrderList.SelectedIndex = j;
|
||||||
|
SaveTabOrder();
|
||||||
|
}
|
||||||
|
moveTabUpButton.Click += (_, _) => MoveTab(-1);
|
||||||
|
moveTabDownButton.Click += (_, _) => MoveTab(1);
|
||||||
|
|
||||||
|
var tabOrderLabel = new MnemonicLabel { Text = "Main tab &order (Alt+O)", AutoSize = true, Anchor = AnchorStyles.Left, MnemonicTarget = tabOrderList };
|
||||||
|
var tabOrderButtons = new FlowLayoutPanel { AutoSize = true, WrapContents = false, Margin = new Padding(0, 2, 0, 0) };
|
||||||
|
tabOrderButtons.Controls.Add(moveTabUpButton);
|
||||||
|
tabOrderButtons.Controls.Add(moveTabDownButton);
|
||||||
|
|
||||||
|
enableDiscoveredPeersBox.Checked = cfgForLoad.ShowDiscoveredPeers;
|
||||||
|
enableDiscoveredPeersBox.CheckedChanged += (_, _) =>
|
||||||
|
{
|
||||||
|
var cfg = AppConfig.Load();
|
||||||
|
cfg.ShowDiscoveredPeers = enableDiscoveredPeersBox.Checked;
|
||||||
|
try { cfg.Save(); } catch { /* harmless */ }
|
||||||
|
};
|
||||||
|
enableRememberedPeersBox.Checked = cfgForLoad.ShowRememberedPeers;
|
||||||
|
enableRememberedPeersBox.CheckedChanged += (_, _) =>
|
||||||
|
{
|
||||||
|
var cfg = AppConfig.Load();
|
||||||
|
cfg.ShowRememberedPeers = enableRememberedPeersBox.Checked;
|
||||||
|
try { cfg.Save(); } catch { /* harmless */ }
|
||||||
|
};
|
||||||
|
|
||||||
// Live UPnP status — the RouterPortMapper raises StatusChanged from a thread-pool
|
// Live UPnP status — the RouterPortMapper raises StatusChanged from a thread-pool
|
||||||
// thread, so marshal back onto the UI thread before touching the label. Subscribe
|
// thread, so marshal back onto the UI thread before touching the label. Subscribe
|
||||||
// on show and unsubscribe on close to avoid leaking the handler past the dialog.
|
// on show and unsubscribe on close to avoid leaking the handler past the dialog.
|
||||||
@@ -837,12 +929,16 @@ internal sealed class PreferencesDialog : Form
|
|||||||
pruneDaysRow.Controls.Add(pruneDaysBox);
|
pruneDaysRow.Controls.Add(pruneDaysBox);
|
||||||
pruneDaysRow.Controls.Add(pruneDaysUnitLabel);
|
pruneDaysRow.Controls.Add(pruneDaysUnitLabel);
|
||||||
|
|
||||||
// Five tabs, accessible (QuietTabControl) like the main window. Ctrl+Tab / arrows on the
|
// Six tabs (General, Appearance, Audio cues, Startup behaviour, Update settings, Logging),
|
||||||
|
// accessible (QuietTabControl) like the main window. Ctrl+Tab / arrows on the
|
||||||
// strip switch tabs; the active page's controls are the next tab stops. The control itself
|
// strip switch tabs; the active page's controls are the next tab stops. The control itself
|
||||||
// is a field (declared above) so OnShown can focus it when the dialog opens. Logging is its
|
// is a field (declared above) so OnShown can focus it when the dialog opens. Logging is its
|
||||||
// own tab (2026-06-19); the two logging controls moved off the General tab to lead it.
|
// own tab (2026-06-19); the two logging controls moved off the General tab to lead it.
|
||||||
tabs.TabPages.Add(MakeTab("General",
|
tabs.TabPages.Add(MakeTab("General",
|
||||||
themeRow, browseProfilesFolderButton, acceptRemoteVolumeBox, upnpEnabledBox, upnpStatusLabel, showPanEqTabBox));
|
browseProfilesFolderButton, acceptRemoteVolumeBox, upnpEnabledBox, upnpStatusLabel));
|
||||||
|
tabs.TabPages.Add(MakeTab("Appearance",
|
||||||
|
themeRow, showPanEqTabBox, tabOrderLabel, tabOrderList, tabOrderButtons,
|
||||||
|
enableDiscoveredPeersBox, enableRememberedPeersBox));
|
||||||
tabs.TabPages.Add(MakeTab("Audio cues", cueGroup));
|
tabs.TabPages.Add(MakeTab("Audio cues", cueGroup));
|
||||||
tabs.TabPages.Add(MakeTab("Startup behaviour",
|
tabs.TabPages.Add(MakeTab("Startup behaviour",
|
||||||
startMinimisedBox, startWithUserBox, startWithProfileBox, startupListPanel));
|
startMinimisedBox, startWithUserBox, startWithProfileBox, startupListPanel));
|
||||||
|
|||||||
@@ -80,6 +80,16 @@ public sealed class AppConfig
|
|||||||
/// changing it takes effect on the next launch. Machine-wide, purely visual.</summary>
|
/// changing it takes effect on the next launch. Machine-wide, purely visual.</summary>
|
||||||
public string ThemeMode { get; set; } = "system";
|
public string ThemeMode { get; set; } = "system";
|
||||||
|
|
||||||
|
/// <summary>The order of the main-window tabs, as tab keys ("connectivity", "audioio", "paneq",
|
||||||
|
/// "audioprofile"). Null / incomplete falls back to the default order. Set from the Preferences
|
||||||
|
/// Appearance tab. Machine-wide.</summary>
|
||||||
|
public List<string>? MainTabOrder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Whether the Discovered / Remembered peer lists appear on the Connectivity tab. Both on
|
||||||
|
/// by default; toggled on the Preferences Appearance tab.</summary>
|
||||||
|
public bool ShowDiscoveredPeers { get; set; } = true;
|
||||||
|
public bool ShowRememberedPeers { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>The machine-wide "named peers" book — peers the user has deliberately renamed, keyed by
|
/// <summary>The machine-wide "named peers" book — peers the user has deliberately renamed, keyed by
|
||||||
/// the peer's stable identity (machine name, or address for a nameless manual peer). A name applies in
|
/// the peer's stable identity (machine name, or address for a nameless manual peer). A name applies in
|
||||||
/// every profile and shows wherever that peer appears. Managed via the Connectivity tab's Rename peer
|
/// every profile and shows wherever that peer appears. Managed via the Connectivity tab's Rename peer
|
||||||
|
|||||||
Reference in New Issue
Block a user