feat(windows): surface shortcuts on a Help screen; exit fullscreen when the stream stops
apple / swift (push) Successful in 1m10s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m13s
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m13s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 44s
windows / build (x86_64-pc-windows-msvc) (push) Failing after 41s
android / android (push) Successful in 4m11s
ci / rust (push) Failing after 1m21s
ci / web (push) Successful in 55s
apple / screenshots (push) Successful in 5m52s
ci / docs-site (push) Successful in 1m2s
arch / build-publish (push) Has been cancelled
ci / bench (push) Has been cancelled
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Has been cancelled
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Has been cancelled
deb / build-publish (push) Has been cancelled
decky / build-publish (push) Has been cancelled
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Has been cancelled
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Has been cancelled
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Has been cancelled
docker / deploy-docs (push) Has been cancelled
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Has been cancelled
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Has been cancelled

The in-stream keyboard shortcuts were only reachable deep in Settings > Input.
Move them to a dedicated Help screen reached from a Help button on the host
list (mirrors the Licenses screen — a hook-free page routed from root), and drop
the reference card out of Settings so there's one home for it.

Fullscreen (F11) was a window-style toggle that stream stop never undid, so
disconnecting while fullscreen left the GUI borderless-fullscreen. Split
toggle_fullscreen into is_fullscreen/enter_fullscreen/exit_fullscreen sharing a
module-level SAVED_PLACEMENT, and have input::uninstall() call exit_fullscreen()
on teardown. exit_fullscreen is a no-op when nothing was saved, so it's safe on
every stream stop and idempotent with F11 — fullscreen is now streaming-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-08 11:17:19 +02:00
parent d490397908
commit 8a4b0c7bae
5 changed files with 193 additions and 117 deletions
+90
View File
@@ -0,0 +1,90 @@
//! The Help screen: a short note on the in-stream capture model plus a reference of the keyboard
//! shortcuts — reached from the Help button on the host list. The Windows counterpart of the GTK
//! client's Keyboard Shortcuts window; the bindings themselves live in [`crate::input`], so both
//! clients document the same set.
use super::style::*;
use super::Screen;
use windows_reactor::*;
/// The in-stream keyboard shortcuts, in the GTK Shortcuts window's order: the chord, then what it
/// does. Read-only — the bindings themselves live in the input hook ([`crate::input`]).
const STREAM_SHORTCUTS: &[(&str, &str)] = &[
("F11", "Toggle fullscreen"),
(
"Ctrl+Alt+Shift+Q",
"Release captured input (click the stream to recapture)",
),
("Ctrl+Alt+Shift+D", "Disconnect"),
("Ctrl+Alt+Shift+S", "Toggle the statistics overlay"),
];
/// A subtle key-cap chip for the shortcuts reference — the chord on a filled, bordered pill.
fn key_chip(keys: &str) -> Element {
border(text_block(keys).font_size(12.0).semibold())
.background(ThemeRef::SubtleFill)
.border_brush(ThemeRef::CardStroke)
.border_thickness(uniform(1.0))
.corner_radius(6.0)
.padding(edges(8.0, 3.0, 8.0, 3.0))
.horizontal_alignment(HorizontalAlignment::Left)
.into()
}
/// A read-only reference card listing the in-stream keyboard shortcuts. One grid, chord chip then
/// action, so the actions line up across rows.
fn shortcuts_reference() -> Element {
let mut children: Vec<Element> = Vec::new();
for (i, (keys, action)) in STREAM_SHORTCUTS.iter().enumerate() {
let row = i as i32;
children.push(key_chip(keys).grid_row(row).grid_column(0));
let action_cell: Element = text_block(*action)
.foreground(ThemeRef::SecondaryText)
.vertical_alignment(VerticalAlignment::Center)
.into();
children.push(action_cell.grid_row(row).grid_column(1));
}
let table = grid(children)
.columns([GridLength::Auto, GridLength::Star(1.0)])
.rows(vec![GridLength::Auto; STREAM_SHORTCUTS.len()])
.column_spacing(12.0)
.row_spacing(6.0);
card(vstack((
text_block("In-stream keyboard shortcuts")
.semibold()
.margin(edges(0.0, 0.0, 0.0, 8.0)),
table,
)))
.into()
}
/// The Help screen: a `page`-column with a Back button to the host list, an intro card on the
/// capture model, and the shortcuts reference. Hook-free — called inline from `root` like the
/// other static screens.
pub(crate) fn help_page(set_screen: &AsyncSetState<Screen>) -> Element {
let back_btn = button("Back").accent().icon(Symbol::Back).on_click({
let ss = set_screen.clone();
move || ss.call(Screen::Hosts)
});
let intro = card(
vstack((
text_block("During a stream").font_size(15.0).semibold(),
text_block(
"Click the stream to capture your mouse and keyboard \u{2014} the shortcuts below \
then work while you play. Release capture to hand the cursor back to this \
computer, and click the stream again to retake it.",
)
.font_size(12.0)
.wrap()
.foreground(ThemeRef::SecondaryText),
))
.spacing(8.0),
);
page(vec![
page_header("Help", back_btn),
intro.into(),
shortcuts_reference(),
])
}