feat(mirada): ventanas flotantes — toggle-float
Una ventana puede salir del teselado y flotar: conserva su propio rectángulo y se compone por encima de las teseladas. - Workspace guarda las flotantes en un mapa aparte; layout() tesela sólo las no-flotantes y añade las flotantes al final (orden de pintado). set_floating / is_floating. - WindowPlacement y BodyOp::Configure llevan floating: bool. BodyState detecta el cambio de floating como cualquier otro reconfigure. - DesktopAction::ToggleFloat (Super+f): saca la enfocada a un rectángulo centrado al 60 % de la pantalla, o la devuelve al teselado. En Monocle, una flotante sigue visible. - mirada-compositor ordena las flotantes al frente de la lista front-to-back de elementos → se pintan encima. - HUD de mirada marca las flotantes; mirada-ctl toggle-float. Verificado end-to-end con headless-ctl. mirada-layout 30->32, mirada-protocol 9->10, mirada-body 13->14, mirada-brain 41->42. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -46,6 +46,8 @@ pub struct WindowPlacement {
|
||||
pub visible: bool,
|
||||
/// `true` si esta ventana tiene el foco del teclado.
|
||||
pub focused: bool,
|
||||
/// `true` si flota (fuera del teselado): el Cuerpo la pinta encima.
|
||||
pub floating: bool,
|
||||
}
|
||||
|
||||
/// Una orden del Cerebro al Cuerpo.
|
||||
@@ -144,11 +146,14 @@ pub fn placements(ws: &Workspace, screen: Rect) -> Vec<WindowPlacement> {
|
||||
.into_iter()
|
||||
.map(|(id, rect)| {
|
||||
let is_focused = focused == Some(id);
|
||||
let floating = ws.is_floating(id);
|
||||
WindowPlacement {
|
||||
id,
|
||||
rect,
|
||||
visible: !monocle || is_focused,
|
||||
// Una flotante siempre se ve; en `Monocle`, sólo la enfocada.
|
||||
visible: floating || !monocle || is_focused,
|
||||
focused: is_focused,
|
||||
floating,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
@@ -177,6 +182,7 @@ mod tests {
|
||||
rect: Rect::new(0, 0, 800, 600),
|
||||
visible: true,
|
||||
focused: true,
|
||||
floating: false,
|
||||
}]);
|
||||
let mut buf = Vec::new();
|
||||
write_frame(&mut buf, &cmd).unwrap();
|
||||
@@ -259,6 +265,18 @@ mod tests {
|
||||
assert!(placements(&empty, SCREEN).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_floating_window_is_marked_and_stays_visible_in_monocle() {
|
||||
let mut w = ws(LayoutMode::Monocle); // Monocle oculta las no enfocadas
|
||||
w.set_floating(10, Some(Rect::new(0, 0, 200, 200)));
|
||||
let p = placements(&w, SCREEN);
|
||||
let f = p.iter().find(|x| x.id == 10).unwrap();
|
||||
assert!(f.floating);
|
||||
assert!(f.visible, "una flotante se ve aunque el modo sea Monocle");
|
||||
// Y conserva su rectángulo flotante.
|
||||
assert_eq!(f.rect, Rect::new(0, 0, 200, 200));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn placements_fill_a_place_command_round_trip() {
|
||||
let cmd = BrainCommand::Place(placements(&ws(LayoutMode::Grid), SCREEN));
|
||||
|
||||
Reference in New Issue
Block a user