feat(mirada): pantalla completa real — toggle-fullscreen

ToggleFullscreen (Super+Shift+f) lleva la ventana enfocada a pantalla
completa: cubre toda la salida sin gap, oculta al resto y se lleva el
foco. Distinto del modo Monocle (un modo de teselado): es un estado
por ventana que ignora el layout.

- Workspace.fullscreen: Option<WindowId>; set_fullscreen / fullscreen();
  remove() lo limpia si se cierra esa ventana.
- placements() da a la fullscreen el rect completo y marca al resto
  visible: false. WindowPlacement y BodyOp::Configure llevan
  fullscreen: bool.
- mirada-compositor fija el estado xdg_toplevel::Fullscreen en la
  superficie, para que el cliente lo sepa.
- Cableado en keymap, HUD de mirada y mirada-ctl.

Verificado end-to-end con headless-ctl. mirada-protocol 10->11,
mirada-brain 51->52.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-21 01:07:01 +00:00
parent 6dfd9e62ac
commit be61ddb6eb
12 changed files with 133 additions and 11 deletions
@@ -40,6 +40,8 @@ pub enum DesktopAction {
CloseFocused,
/// Alterna entre flotante y teselada la ventana enfocada.
ToggleFloat,
/// Alterna pantalla completa en la ventana enfocada.
ToggleFullscreen,
/// Pasa al siguiente modo de teselado.
CycleLayout,
/// Fija un modo de teselado concreto.
@@ -101,6 +103,7 @@ impl fmt::Display for DesktopAction {
DesktopAction::MoveBackward => f.write_str("move-backward"),
DesktopAction::CloseFocused => f.write_str("close-focused"),
DesktopAction::ToggleFloat => f.write_str("toggle-float"),
DesktopAction::ToggleFullscreen => f.write_str("toggle-fullscreen"),
DesktopAction::CycleLayout => f.write_str("cycle-layout"),
DesktopAction::SetLayout(m) => write!(f, "layout:{}", layout_slug(*m)),
DesktopAction::GrowMaster => f.write_str("grow-master"),
@@ -129,6 +132,7 @@ impl FromStr for DesktopAction {
"move-backward" => Self::MoveBackward,
"close-focused" => Self::CloseFocused,
"toggle-float" => Self::ToggleFloat,
"toggle-fullscreen" => Self::ToggleFullscreen,
"cycle-layout" => Self::CycleLayout,
"grow-master" => Self::GrowMaster,
"shrink-master" => Self::ShrinkMaster,
@@ -188,6 +192,7 @@ pub fn default_keymap() -> Vec<(String, DesktopAction)> {
("Super+Shift+k".into(), DesktopAction::MoveBackward),
("Super+q".into(), DesktopAction::CloseFocused),
("Super+f".into(), DesktopAction::ToggleFloat),
("Super+Shift+f".into(), DesktopAction::ToggleFullscreen),
("Super+space".into(), DesktopAction::CycleLayout),
("Super+t".into(), DesktopAction::SetLayout(LayoutMode::MasterStack)),
("Super+m".into(), DesktopAction::SetLayout(LayoutMode::Monocle)),
@@ -218,6 +218,18 @@ impl Desktop {
}
self.relayout()
}
DesktopAction::ToggleFullscreen => {
let Some(id) = self.workspaces[self.active].focused() else {
return Vec::new();
};
let ws = &mut self.workspaces[self.active];
if ws.fullscreen() == Some(id) {
ws.set_fullscreen(None);
} else {
ws.set_fullscreen(Some(id));
}
self.relayout()
}
DesktopAction::CycleLayout => {
let next = self.workspaces[self.active].params().mode.next();
self.workspaces[self.active].set_mode(next);
@@ -475,6 +487,23 @@ mod tests {
assert!(!places(&cmds).iter().find(|x| x.id == 2).unwrap().floating);
}
#[test]
fn toggle_fullscreen_covers_the_screen_and_hides_the_rest() {
let mut d = desktop_with_screen();
for id in [1, 2, 3] {
open(&mut d, id);
}
let cmds = d.apply(DesktopAction::ToggleFullscreen); // sobre la 3
let p = places(&cmds);
let fs = p.iter().find(|x| x.id == 3).unwrap();
assert!(fs.fullscreen && fs.visible);
assert_eq!(fs.rect, d.screen().unwrap());
assert!(p.iter().filter(|x| x.id != 3).all(|x| !x.visible));
// Alternar de nuevo restaura el teselado: las tres visibles.
let cmds = d.apply(DesktopAction::ToggleFullscreen);
assert_eq!(places(&cmds).iter().filter(|x| x.visible).count(), 3);
}
#[test]
fn a_rule_sends_a_new_window_to_its_workspace() {
let mut d = desktop_with_screen();
@@ -238,6 +238,7 @@ const KEYMAP_HEADER: &str = "\
// move-forward / move-backward reordena la ventana enfocada
// close-focused cierra la enfocada
// toggle-float alterna flotante / teselada
// toggle-fullscreen alterna pantalla completa
// cycle-layout siguiente modo de teselado
// layout:<modo> master-stack | centered-master | spiral
// grid | columns | rows | monocle