container: autotile uses major axis as first split
This commit is contained in:
parent
dc62d2240f
commit
c1b2c7f17c
8 changed files with 83 additions and 11 deletions
|
|
@ -80,9 +80,9 @@ container.
|
|||
## Autotiling
|
||||
|
||||
Autotiling makes newly tiled windows alternate split direction from the focused
|
||||
tiled window. The first split uses the containing group direction, then later
|
||||
windows wrap the focused tile in the opposite direction, producing a horizontal,
|
||||
vertical, horizontal pattern as the layout grows.
|
||||
tiled window by splitting the focused tile along its largest axis. On an empty
|
||||
workspace, the first split uses the largest axis of the output. This starts with
|
||||
horizontal splits on landscape outputs and vertical splits on portrait outputs.
|
||||
|
||||
```toml
|
||||
[shortcuts]
|
||||
|
|
|
|||
|
|
@ -438,9 +438,9 @@ pub fn get_corner_radius() -> f32 {
|
|||
/// Enables or disables autotiling.
|
||||
///
|
||||
/// When enabled, newly tiled windows alternate split orientation from the
|
||||
/// focused tiled window: the first split uses the containing group's direction,
|
||||
/// then subsequent splits wrap the focused window in the perpendicular
|
||||
/// direction.
|
||||
/// focused tiled window by splitting the focused tile along its largest axis.
|
||||
/// On an empty workspace, the first split uses the workspace output's largest
|
||||
/// axis.
|
||||
///
|
||||
/// The default is `false`.
|
||||
pub fn set_autotile(enabled: bool) {
|
||||
|
|
|
|||
|
|
@ -1215,7 +1215,7 @@
|
|||
},
|
||||
"autotile": {
|
||||
"type": "boolean",
|
||||
"description": "Configures whether autotiling is enabled by default.\n\nWhen enabled, newly mapped tiled windows alternate their split\norientation automatically. This can also be toggled at runtime via the\n`enable-autotile`, `disable-autotile`, and `toggle-autotile` actions.\n\nThe default is `false`.\n"
|
||||
"description": "Configures whether autotiling is enabled by default.\n\nWhen enabled, newly mapped tiled windows alternate their split\norientation automatically by splitting the focused tile along its\nlargest axis. On an empty workspace, the first split uses the largest\naxis of the output, so landscape outputs start horizontally and portrait\noutputs start vertically. This can also be toggled at runtime via the\n`enable-autotile`, `disable-autotile`, and `toggle-autotile` actions.\n\nThe default is `false`.\n"
|
||||
},
|
||||
"modes": {
|
||||
"description": "Configures the input modes.\n\nModes can be used to define shortcuts that are only active when the mode is\nactive.\n\n- Example\n\n ```toml\n [modes.\"navigation\".shortcuts]\n w = \"focus-up\"\n a = \"focus-left\"\n s = \"focus-down\"\n d = \"focus-right\"\n r = \"focus-above\"\n f = \"focus-below\"\n q = \"focus-prev\"\n e = \"focus-next\"\n ```\n\nModes can be activated with the `push-mode` and `latch-mode` actions.\n",
|
||||
|
|
|
|||
|
|
@ -3188,7 +3188,10 @@ Config:
|
|||
Configures whether autotiling is enabled by default.
|
||||
|
||||
When enabled, newly mapped tiled windows alternate their split
|
||||
orientation automatically. This can also be toggled at runtime via the
|
||||
orientation automatically by splitting the focused tile along its
|
||||
largest axis. On an empty workspace, the first split uses the largest
|
||||
axis of the output, so landscape outputs start horizontally and portrait
|
||||
outputs start vertically. This can also be toggled at runtime via the
|
||||
`enable-autotile`, `disable-autotile`, and `toggle-autotile` actions.
|
||||
|
||||
The default is `false`.
|
||||
|
|
|
|||
|
|
@ -344,6 +344,12 @@ impl TestConfig {
|
|||
pub fn set_autotile(&self, enabled: bool) -> TestResult {
|
||||
self.send(ClientMessage::SetAutotile { enabled })
|
||||
}
|
||||
|
||||
pub fn get_autotile(&self) -> Result<bool, TestError> {
|
||||
let reply = self.send_with_reply(ClientMessage::GetAutotile)?;
|
||||
get_response!(reply, GetAutotile { enabled });
|
||||
Ok(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TestConfig {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ testcase!();
|
|||
async fn test(run: Rc<TestRun>) -> TestResult {
|
||||
run.backend.install_default()?;
|
||||
run.cfg.set_autotile(true)?;
|
||||
tassert_eq!(run.cfg.get_autotile()?, true);
|
||||
|
||||
let client = run.create_client().await?;
|
||||
|
||||
|
|
|
|||
|
|
@ -89,11 +89,27 @@ impl State {
|
|||
} else {
|
||||
lap.add_child_after(&*la, node);
|
||||
}
|
||||
} else if autotile {
|
||||
if let Some(last) = c.children.last() {
|
||||
let la = last_child_descendant(last.node.clone());
|
||||
let lap = la
|
||||
.tl_data()
|
||||
.parent
|
||||
.get()
|
||||
.and_then(|n| n.node_into_container());
|
||||
if let Some(lap) = lap {
|
||||
lap.add_tiled_child_after(&*la, node);
|
||||
} else {
|
||||
c.append_child(node);
|
||||
}
|
||||
} else {
|
||||
c.append_child(node);
|
||||
}
|
||||
} else {
|
||||
c.append_child(node);
|
||||
}
|
||||
} else {
|
||||
let container = ContainerNode::new(self, ws, node, ContainerSplit::Horizontal);
|
||||
let container = ContainerNode::new(self, ws, node, initial_split_for_workspace(ws));
|
||||
ws.set_container(&container);
|
||||
}
|
||||
}
|
||||
|
|
@ -537,3 +553,40 @@ impl State {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
fn initial_split_for_workspace(ws: &WorkspaceNode) -> ContainerSplit {
|
||||
ContainerSplit::for_largest_axis(ws.output.get().global.pos.get())
|
||||
}
|
||||
|
||||
fn last_child_descendant(mut node: Rc<dyn ToplevelNode>) -> Rc<dyn ToplevelNode> {
|
||||
loop {
|
||||
let Some(container) = node.clone().node_into_container() else {
|
||||
return node;
|
||||
};
|
||||
let Some(last) = container.children.last() else {
|
||||
return container;
|
||||
};
|
||||
node = last.node.clone();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn initial_split_uses_largest_output_axis() {
|
||||
assert_eq!(
|
||||
ContainerSplit::for_largest_axis(Rect::new_sized_saturating(0, 0, 800, 600)),
|
||||
ContainerSplit::Horizontal
|
||||
);
|
||||
assert_eq!(
|
||||
ContainerSplit::for_largest_axis(Rect::new_sized_saturating(0, 0, 600, 800)),
|
||||
ContainerSplit::Vertical
|
||||
);
|
||||
assert_eq!(
|
||||
ContainerSplit::for_largest_axis(Rect::new_sized_saturating(0, 0, 600, 600)),
|
||||
ContainerSplit::Horizontal
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,14 @@ pub enum ContainerSplit {
|
|||
}
|
||||
|
||||
impl ContainerSplit {
|
||||
pub fn for_largest_axis(rect: Rect) -> Self {
|
||||
if rect.height() > rect.width() {
|
||||
ContainerSplit::Vertical
|
||||
} else {
|
||||
ContainerSplit::Horizontal
|
||||
}
|
||||
}
|
||||
|
||||
pub fn other(self) -> Self {
|
||||
match self {
|
||||
ContainerSplit::Horizontal => ContainerSplit::Vertical,
|
||||
|
|
@ -302,11 +310,12 @@ impl ContainerNode {
|
|||
};
|
||||
let focused_node = focused.node.clone();
|
||||
let focused_active = focused_node.tl_data().active();
|
||||
let split = ContainerSplit::for_largest_axis(focused.body.get());
|
||||
let sub = ContainerNode::new(
|
||||
&self.state,
|
||||
&self.workspace.get(),
|
||||
focused_node.clone(),
|
||||
self.split.get().other(),
|
||||
split,
|
||||
);
|
||||
// Autotile-created groups are structural and collapse once only one
|
||||
// child remains. Explicit make-group commands control their own
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue