feat(mirada): 3 layouts nuevos + redimensionar el área maestra

mirada-layout pasa de 4 a 7 modos de teselado, todos intercambiables
por el API (SetLayout / CycleLayout / mirada-ctl layout <modo>):

- Rows: filas horizontales de igual alto (complemento de Columns).
- Spiral: espiral de Fibonacci — cada ventana parte por la mitad el
  espacio restante, alternando el sentido del corte.
- CenteredMaster: maestra centrada + pila a ambos lados (monitores
  anchos).

LayoutMode::ALL + next() definen el ciclo. Añade dos acciones,
GrowMaster/ShrinkMaster (Super+l / Super+h), que ajustan master_ratio
en caliente — ese parámetro existía pero no había forma de tocarlo.

Cableado completo: tile(), cycle, slugs Display/FromStr, keymap por
defecto (Super+r/d/s), HUD de mirada, mirada-ctl actions. El ejemplo
headless-ctl ahora imprime la geometría para verificar los layouts.

mirada-layout 22->26 tests, mirada-brain 37->39.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-21 00:37:16 +00:00
parent b31f988833
commit 8821d34bd5
10 changed files with 293 additions and 68 deletions
@@ -42,6 +42,10 @@ pub enum DesktopAction {
CycleLayout,
/// Fija un modo de teselado concreto.
SetLayout(LayoutMode),
/// Agranda el área de la ventana maestra (`MasterStack`/`CenteredMaster`).
GrowMaster,
/// Encoge el área de la ventana maestra.
ShrinkMaster,
/// Activa el escritorio virtual `n` (índice 0-based).
SwitchWorkspace(usize),
/// Manda la ventana enfocada al escritorio virtual `n`.
@@ -58,6 +62,9 @@ fn layout_slug(mode: LayoutMode) -> &'static str {
LayoutMode::Monocle => "monocle",
LayoutMode::Grid => "grid",
LayoutMode::Columns => "columns",
LayoutMode::Rows => "rows",
LayoutMode::CenteredMaster => "centered-master",
LayoutMode::Spiral => "spiral",
}
}
@@ -68,6 +75,9 @@ fn layout_from_slug(slug: &str) -> Option<LayoutMode> {
"monocle" => LayoutMode::Monocle,
"grid" => LayoutMode::Grid,
"columns" => LayoutMode::Columns,
"rows" => LayoutMode::Rows,
"centered-master" => LayoutMode::CenteredMaster,
"spiral" => LayoutMode::Spiral,
_ => return None,
})
}
@@ -84,6 +94,8 @@ impl fmt::Display for DesktopAction {
DesktopAction::CloseFocused => f.write_str("close-focused"),
DesktopAction::CycleLayout => f.write_str("cycle-layout"),
DesktopAction::SetLayout(m) => write!(f, "layout:{}", layout_slug(*m)),
DesktopAction::GrowMaster => f.write_str("grow-master"),
DesktopAction::ShrinkMaster => f.write_str("shrink-master"),
// Los escritorios se numeran 1-based de cara al usuario.
DesktopAction::SwitchWorkspace(n) => write!(f, "workspace:{}", n + 1),
DesktopAction::SendToWorkspace(n) => write!(f, "send-to-workspace:{}", n + 1),
@@ -105,6 +117,8 @@ impl FromStr for DesktopAction {
"move-backward" => Self::MoveBackward,
"close-focused" => Self::CloseFocused,
"cycle-layout" => Self::CycleLayout,
"grow-master" => Self::GrowMaster,
"shrink-master" => Self::ShrinkMaster,
"quit" => Self::Quit,
_ => {
if let Some(slug) = s.strip_prefix("layout:") {
@@ -162,6 +176,11 @@ pub fn default_keymap() -> Vec<(String, DesktopAction)> {
("Super+m".into(), DesktopAction::SetLayout(LayoutMode::Monocle)),
("Super+g".into(), DesktopAction::SetLayout(LayoutMode::Grid)),
("Super+c".into(), DesktopAction::SetLayout(LayoutMode::Columns)),
("Super+r".into(), DesktopAction::SetLayout(LayoutMode::Rows)),
("Super+d".into(), DesktopAction::SetLayout(LayoutMode::CenteredMaster)),
("Super+s".into(), DesktopAction::SetLayout(LayoutMode::Spiral)),
("Super+h".into(), DesktopAction::ShrinkMaster),
("Super+l".into(), DesktopAction::GrowMaster),
("Super+Shift+e".into(), DesktopAction::Quit),
];
// Un escritorio por dígito: `Super+1`..`Super+9` lo activan,
@@ -214,12 +233,7 @@ mod tests {
#[test]
fn every_layout_mode_round_trips() {
for mode in [
LayoutMode::MasterStack,
LayoutMode::Monocle,
LayoutMode::Grid,
LayoutMode::Columns,
] {
for mode in LayoutMode::ALL {
let a = DesktopAction::SetLayout(mode);
assert_eq!(a, a.to_string().parse().unwrap());
}