Status speech: read line-by-line, MB->GB on totals, double-press to copy (held for next release)
Andre's feedback on the spoken status (the issue #13 feature): - Speak the status one line at a time instead of collapsing the lines into a run-on sentence — this also removes the doubled "." the collapse produced (each line already ended in a period). - Totals switch to GB once they reach a gigabyte ("4.5 GB" vs "4558.2 MB"), via a FormatDataSize helper used by the readout itself so the on-screen and spoken figures stay consistent. - A quick DOUBLE press of the speak-status hotkey copies the status to the clipboard (with a "copied" announcement) so it can be shared with others. Held for the 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
f9b3ed9e67
commit
0fe9637356
@@ -267,6 +267,8 @@ public sealed class MainForm : Form
|
|||||||
// Tracks when the FIRST healthy-peer transition happened in the current "connected"
|
// Tracks when the FIRST healthy-peer transition happened in the current "connected"
|
||||||
// span. Cleared when no peers are healthy. Used for the uptime line.
|
// span. Cleared when no peers are healthy. Used for the uptime line.
|
||||||
private DateTime? statusConnectedSinceUtc;
|
private DateTime? statusConnectedSinceUtc;
|
||||||
|
// Time of the last "speak status" hotkey press, for double-press-to-copy detection.
|
||||||
|
private DateTime lastSpeakStatusPressUtc = DateTime.MinValue;
|
||||||
// Per-list state (used by sync helpers — was per-method in the old dialog).
|
// Per-list state (used by sync helpers — was per-method in the old dialog).
|
||||||
private bool suppressConnectedCheck;
|
private bool suppressConnectedCheck;
|
||||||
private bool suppressDiscoveredCheck;
|
private bool suppressDiscoveredCheck;
|
||||||
@@ -3348,18 +3350,40 @@ public sealed class MainForm : Form
|
|||||||
statusReadout.Text = text;
|
statusReadout.Text = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Speak the current connection status line aloud through the active screen reader, via
|
/// <summary>Speak the current connection status aloud through the active screen reader, via Tolk
|
||||||
/// Tolk (<see cref="ScreenReader"/>). Answers issue #13: NVDA sometimes can't see the status
|
/// (<see cref="ScreenReader"/>). Answers issue #13: NVDA sometimes can't see the status readout
|
||||||
/// readout ("no status line found"), so this reads it on demand. Triggered only by a user-set
|
/// ("no status line found"), so this reads it on demand. Triggered only by a user-set global hotkey
|
||||||
/// global hotkey (screen-reader only, unset by default) — being a system-wide hotkey it works from
|
/// (screen-reader only, unset by default) — being a system-wide hotkey it works from anywhere. The
|
||||||
/// anywhere, whether or not RemSound is focused. Newlines become ". " so the multi-line readout
|
/// status is read line by line (the multi-line text is passed straight to the screen reader, which
|
||||||
/// speaks as a sentence rather than running together.</summary>
|
/// pauses at each line) rather than collapsed into a run-on sentence. A quick DOUBLE press copies
|
||||||
|
/// the same text to the clipboard instead, so it can be shared. (Andre's suggestion, 2026-06-24.)</summary>
|
||||||
private void SpeakStatusLine()
|
private void SpeakStatusLine()
|
||||||
{
|
{
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
var doublePress = now - lastSpeakStatusPressUtc <= TimeSpan.FromMilliseconds(600);
|
||||||
|
lastSpeakStatusPressUtc = now;
|
||||||
|
|
||||||
var text = statusReadout.Text;
|
var text = statusReadout.Text;
|
||||||
text = string.IsNullOrWhiteSpace(text)
|
if (string.IsNullOrWhiteSpace(text)) text = "No status information available.";
|
||||||
? "No status information available."
|
|
||||||
: text.Replace("\r\n", ". ").Replace("\n", ". ");
|
if (doublePress)
|
||||||
|
{
|
||||||
|
// Second quick press: copy the status to the clipboard so it can be shared. Clipboard access
|
||||||
|
// needs the UI thread — this runs on it (the hotkey is marshalled onto the owner form).
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Clipboard.SetText(text);
|
||||||
|
ScreenReader.Speak("RemSound status copied to the clipboard.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logFile.Event($"speak status: clipboard copy failed: {ex.GetType().Name}: {ex.Message}");
|
||||||
|
ScreenReader.Speak("Could not copy the status to the clipboard.");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single press: read it out, line by line (the screen reader pauses at each line break).
|
||||||
ScreenReader.Speak(text);
|
ScreenReader.Speak(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3446,10 +3470,20 @@ public sealed class MainForm : Form
|
|||||||
}
|
}
|
||||||
|
|
||||||
sb.AppendLine($"Receiving {rxKbs:0.0} kB/s; sending {txKbs:0.0} kB/s.");
|
sb.AppendLine($"Receiving {rxKbs:0.0} kB/s; sending {txKbs:0.0} kB/s.");
|
||||||
sb.Append($"Total received {receiver.BytesReceived / 1048576.0:0.0} MB; sent {sender.BytesSent / 1048576.0:0.0} MB.");
|
sb.Append($"Total received {FormatDataSize(receiver.BytesReceived)}; sent {FormatDataSize(sender.BytesSent)}.");
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>A running data total as MB, switching to GB once it reaches a gigabyte — "935.8 MB",
|
||||||
|
/// "4.5 GB" — so a big figure reads more naturally (Andre's suggestion). Binary units, matching the
|
||||||
|
/// 1048576-byte MB used elsewhere.</summary>
|
||||||
|
private static string FormatDataSize(long bytes)
|
||||||
|
{
|
||||||
|
const double mb = 1048576.0;
|
||||||
|
const double gb = 1073741824.0;
|
||||||
|
return bytes >= gb ? $"{bytes / gb:0.0} GB" : $"{bytes / mb:0.0} MB";
|
||||||
|
}
|
||||||
|
|
||||||
private static string FormatUptime(TimeSpan span)
|
private static string FormatUptime(TimeSpan span)
|
||||||
{
|
{
|
||||||
if (span.TotalSeconds < 1) return "0 seconds";
|
if (span.TotalSeconds < 1) return "0 seconds";
|
||||||
|
|||||||
Reference in New Issue
Block a user