feat: add alternating autotiling
This commit is contained in:
parent
ce14169d6b
commit
5c2f631fdb
17 changed files with 244 additions and 59 deletions
|
|
@ -77,6 +77,20 @@ You can also right-click any title in a container to toggle mono mode.
|
|||
In mono mode, scroll over the title bar to cycle between windows in the
|
||||
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.
|
||||
|
||||
```toml
|
||||
[shortcuts]
|
||||
alt-a = "toggle-autotile"
|
||||
```
|
||||
|
||||
Manual grouping and split commands still use the direction you request.
|
||||
|
||||
## Fullscreen
|
||||
|
||||
Press `alt-u` (`toggle-fullscreen`) to make the focused window fill the entire
|
||||
|
|
|
|||
|
|
@ -2079,6 +2079,12 @@ impl ConfigClient {
|
|||
self.send(&ClientMessage::SetAutotile { enabled });
|
||||
}
|
||||
|
||||
pub fn get_autotile(&self) -> bool {
|
||||
let res = self.send_with_response(&ClientMessage::GetAutotile);
|
||||
get_response!(res, false, GetAutotile { enabled });
|
||||
enabled
|
||||
}
|
||||
|
||||
pub fn set_tab_title_align(&self, align: u32) {
|
||||
self.send(&ClientMessage::SetTabTitleAlign { align });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -923,6 +923,7 @@ pub enum ClientMessage<'a> {
|
|||
SetAutotile {
|
||||
enabled: bool,
|
||||
},
|
||||
GetAutotile,
|
||||
SetTabTitleAlign {
|
||||
align: u32,
|
||||
},
|
||||
|
|
@ -1189,6 +1190,9 @@ pub enum Response {
|
|||
GetCornerRadius {
|
||||
radius: f32,
|
||||
},
|
||||
GetAutotile {
|
||||
enabled: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
|
|||
|
|
@ -453,14 +453,21 @@ pub fn get_corner_radius() -> f32 {
|
|||
|
||||
/// Enables or disables autotiling.
|
||||
///
|
||||
/// When enabled, new windows are automatically placed in a perpendicular
|
||||
/// sub-container if the predicted body would be narrower than tall (or vice versa).
|
||||
/// 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.
|
||||
///
|
||||
/// The default is `false`.
|
||||
pub fn set_autotile(enabled: bool) {
|
||||
get!().set_autotile(enabled)
|
||||
}
|
||||
|
||||
/// Returns whether autotiling is enabled.
|
||||
pub fn get_autotile() -> bool {
|
||||
get!(false).get_autotile()
|
||||
}
|
||||
|
||||
/// Sets the horizontal alignment of title text within tab buttons.
|
||||
///
|
||||
/// - `"start"` — left-aligned (default)
|
||||
|
|
|
|||
|
|
@ -3587,6 +3587,11 @@ impl ConfigProxyHandler {
|
|||
ClientMessage::SetAutotile { enabled } => {
|
||||
self.state.theme.autotile_enabled.set(enabled);
|
||||
}
|
||||
ClientMessage::GetAutotile => {
|
||||
self.respond(Response::GetAutotile {
|
||||
enabled: self.state.theme.autotile_enabled.get(),
|
||||
});
|
||||
}
|
||||
ClientMessage::SeatToggleExpand { .. } => {
|
||||
// Removed feature; kept for binary protocol compatibility.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -331,6 +331,10 @@ impl TestConfig {
|
|||
pub fn set_show_titles(&self, show: bool) -> TestResult {
|
||||
self.send(ClientMessage::SetShowTitles { show })
|
||||
}
|
||||
|
||||
pub fn set_autotile(&self, enabled: bool) -> TestResult {
|
||||
self.send(ClientMessage::SetAutotile { enabled })
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TestConfig {
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ mod t0051_pointer_warp;
|
|||
mod t0052_bar;
|
||||
mod t0053_theme;
|
||||
mod t0054_subsurface_already_attached;
|
||||
mod t0055_autotiling;
|
||||
|
||||
pub trait TestCase: Sync {
|
||||
fn name(&self) -> &'static str;
|
||||
|
|
@ -158,5 +159,6 @@ pub fn tests() -> Vec<&'static dyn TestCase> {
|
|||
t0052_bar,
|
||||
t0053_theme,
|
||||
t0054_subsurface_already_attached,
|
||||
t0055_autotiling,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
58
src/it/tests/t0055_autotiling.rs
Normal file
58
src/it/tests/t0055_autotiling.rs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
use {
|
||||
crate::{
|
||||
it::{test_error::TestResult, testrun::TestRun},
|
||||
tree::{ContainerSplit, Node, ToplevelNodeBase},
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
testcase!();
|
||||
|
||||
async fn test(run: Rc<TestRun>) -> TestResult {
|
||||
run.backend.install_default()?;
|
||||
run.cfg.set_autotile(true)?;
|
||||
|
||||
let client = run.create_client().await?;
|
||||
|
||||
let win1 = client.create_window().await?;
|
||||
win1.map().await?;
|
||||
let root = win1.tl.container_parent()?;
|
||||
tassert_eq!(root.split.get(), ContainerSplit::Horizontal);
|
||||
|
||||
let win2 = client.create_window().await?;
|
||||
win2.map().await?;
|
||||
client.sync().await;
|
||||
|
||||
tassert_eq!(root.split.get(), ContainerSplit::Horizontal);
|
||||
tassert_eq!(win1.tl.container_parent()?.node_id(), root.node_id());
|
||||
tassert_eq!(win2.tl.container_parent()?.node_id(), root.node_id());
|
||||
|
||||
let win3 = client.create_window().await?;
|
||||
win3.map().await?;
|
||||
client.sync().await;
|
||||
|
||||
let v_group = win3.tl.container_parent()?;
|
||||
tassert_eq!(root.split.get(), ContainerSplit::Horizontal);
|
||||
tassert_eq!(v_group.split.get(), ContainerSplit::Vertical);
|
||||
tassert_eq!(win2.tl.container_parent()?.node_id(), v_group.node_id());
|
||||
|
||||
let win4 = client.create_window().await?;
|
||||
win4.map().await?;
|
||||
client.sync().await;
|
||||
|
||||
let h_group = win4.tl.container_parent()?;
|
||||
tassert_eq!(h_group.split.get(), ContainerSplit::Horizontal);
|
||||
tassert_eq!(win3.tl.container_parent()?.node_id(), h_group.node_id());
|
||||
let h_parent = match h_group
|
||||
.tl_data()
|
||||
.parent
|
||||
.get()
|
||||
.and_then(|p| p.node_into_container())
|
||||
{
|
||||
Some(parent) => parent,
|
||||
None => bail!("autotile group does not have a container parent"),
|
||||
};
|
||||
tassert_eq!(h_parent.node_id(), v_group.node_id());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
34
src/state.rs
34
src/state.rs
|
|
@ -925,19 +925,39 @@ impl State {
|
|||
&& node.tl_data().kind.is_app_window()
|
||||
&& !node.tl_data().visible.get();
|
||||
if animate_new_app_map {
|
||||
self.with_layout_animations(|| self.do_map_tiled(seat.as_deref(), node.clone()));
|
||||
self.with_layout_animations(|| self.do_map_tiled(seat.as_deref(), node.clone(), true));
|
||||
} else {
|
||||
self.do_map_tiled(seat.as_deref(), node.clone());
|
||||
self.do_map_tiled(seat.as_deref(), node.clone(), true);
|
||||
}
|
||||
self.focus_after_map(node, seat.as_deref());
|
||||
}
|
||||
|
||||
fn do_map_tiled(self: &Rc<Self>, seat: Option<&Rc<WlSeatGlobal>>, node: Rc<dyn ToplevelNode>) {
|
||||
pub fn map_tiled_without_autotile(self: &Rc<Self>, node: Rc<dyn ToplevelNode>) {
|
||||
let seat = self.seat_queue.last();
|
||||
self.do_map_tiled(seat.as_deref(), node.clone(), false);
|
||||
self.focus_after_map(node, seat.as_deref());
|
||||
}
|
||||
|
||||
fn do_map_tiled(
|
||||
self: &Rc<Self>,
|
||||
seat: Option<&Rc<WlSeatGlobal>>,
|
||||
node: Rc<dyn ToplevelNode>,
|
||||
autotile: bool,
|
||||
) {
|
||||
let ws = self.ensure_map_workspace(seat);
|
||||
self.map_tiled_on(node, &ws);
|
||||
self.map_tiled_on_(node, &ws, autotile);
|
||||
}
|
||||
|
||||
pub fn map_tiled_on(self: &Rc<Self>, node: Rc<dyn ToplevelNode>, ws: &Rc<WorkspaceNode>) {
|
||||
self.map_tiled_on_(node, ws, false);
|
||||
}
|
||||
|
||||
fn map_tiled_on_(
|
||||
self: &Rc<Self>,
|
||||
node: Rc<dyn ToplevelNode>,
|
||||
ws: &Rc<WorkspaceNode>,
|
||||
autotile: bool,
|
||||
) {
|
||||
if let Some(c) = ws.container.get() {
|
||||
let la = c.clone().tl_last_active_child();
|
||||
let lap = la
|
||||
|
|
@ -946,7 +966,11 @@ impl State {
|
|||
.get()
|
||||
.and_then(|n| n.node_into_container());
|
||||
if let Some(lap) = lap {
|
||||
lap.add_child_after(&*la, node);
|
||||
if autotile {
|
||||
lap.add_tiled_child_after(&*la, node);
|
||||
} else {
|
||||
lap.add_child_after(&*la, node);
|
||||
}
|
||||
} else {
|
||||
c.append_child(node);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -290,6 +290,47 @@ impl ContainerNode {
|
|||
self.add_child_x(prev, new, |prev, new| self.add_child_after_(prev, new));
|
||||
}
|
||||
|
||||
pub fn add_tiled_child_after(self: &Rc<Self>, prev: &dyn Node, new: Rc<dyn ToplevelNode>) {
|
||||
if !self.state.theme.autotile_enabled.get()
|
||||
|| self.mono_child.is_some()
|
||||
|| self.num_children.get() <= 1
|
||||
{
|
||||
self.add_child_after(prev, new);
|
||||
return;
|
||||
}
|
||||
let focused = self
|
||||
.child_nodes
|
||||
.borrow()
|
||||
.get(&prev.node_id())
|
||||
.map(|n| n.to_ref());
|
||||
let Some(focused) = focused else {
|
||||
log::error!(
|
||||
"Tried to autotile a child into a container but the preceding node is not in the container"
|
||||
);
|
||||
return;
|
||||
};
|
||||
let focused_node = focused.node.clone();
|
||||
let focused_active = focused_node.tl_data().active();
|
||||
let sub = ContainerNode::new(
|
||||
&self.state,
|
||||
&self.workspace.get(),
|
||||
focused_node.clone(),
|
||||
self.split.get().other(),
|
||||
);
|
||||
// Autotile-created groups are structural and collapse once only one
|
||||
// child remains. Explicit make-group commands control their own
|
||||
// grouping through the regular manual paths.
|
||||
sub.ephemeral.set(Ephemeral::On);
|
||||
sub.append_child(new);
|
||||
let sub_id = sub.node_id();
|
||||
self.clone().cnode_replace_child(&*focused_node, sub);
|
||||
if focused_active
|
||||
&& let Some(group) = self.child_nodes.borrow().get(&sub_id).map(|n| n.to_ref())
|
||||
{
|
||||
self.update_child_active(&group, true, 1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_child_before(self: &Rc<Self>, prev: &dyn Node, new: Rc<dyn ToplevelNode>) {
|
||||
self.add_child_x(prev, new, |prev, new| self.add_child_before_(prev, new));
|
||||
}
|
||||
|
|
@ -1369,42 +1410,6 @@ impl ContainerNode {
|
|||
}
|
||||
|
||||
pub fn insert_child(self: &Rc<Self>, node: Rc<dyn ToplevelNode>, direction: Direction) {
|
||||
// Autotile: if the container would become too narrow/tall, wrap the
|
||||
// focused child and new node in a perpendicular sub-container.
|
||||
if self.state.theme.autotile_enabled.get() && self.mono_child.is_none() {
|
||||
let (pw, ph) = self.predict_child_body_size();
|
||||
let opposite = match self.split.get() {
|
||||
ContainerSplit::Horizontal if pw > 0 && ph > 0 && pw < ph => {
|
||||
Some(ContainerSplit::Vertical)
|
||||
}
|
||||
ContainerSplit::Vertical if pw > 0 && ph > 0 && ph < pw => {
|
||||
Some(ContainerSplit::Horizontal)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
if let Some(opp_split) = opposite {
|
||||
if let Some(focused) = self.focus_history.last() {
|
||||
if self.num_children.get() <= 1 {
|
||||
// Single child, autotile not applicable.
|
||||
} else {
|
||||
let focused_node = focused.node.clone();
|
||||
let was_ephemeral = self.ephemeral.replace(Ephemeral::Off);
|
||||
self.clone().cnode_remove_child2(&*focused_node, true);
|
||||
self.ephemeral.set(was_ephemeral);
|
||||
let sub = ContainerNode::new(
|
||||
&self.state,
|
||||
&self.workspace.get(),
|
||||
focused_node,
|
||||
opp_split,
|
||||
);
|
||||
sub.ephemeral.set(Ephemeral::On);
|
||||
sub.append_child(node);
|
||||
self.append_child(sub);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let (split, right) = direction_to_split(direction);
|
||||
if split != self.split.get() || right {
|
||||
self.append_child(node);
|
||||
|
|
|
|||
|
|
@ -979,7 +979,7 @@ impl ToplevelData {
|
|||
}
|
||||
fd.workspace.remove_fullscreen_node();
|
||||
if fd.placeholder.is_destroyed() {
|
||||
state.map_tiled(node);
|
||||
state.map_tiled_without_autotile(node);
|
||||
return;
|
||||
}
|
||||
let parent = fd.placeholder.tl_data().parent.take().unwrap();
|
||||
|
|
@ -1262,7 +1262,7 @@ pub fn toplevel_set_floating(state: &Rc<State>, tl: Rc<dyn ToplevelNode>, floati
|
|||
};
|
||||
if !floating {
|
||||
parent.cnode_remove_child2(&*tl, true);
|
||||
state.map_tiled(tl);
|
||||
state.map_tiled_without_autotile(tl);
|
||||
} else if let Some(ws) = data.workspace.get() {
|
||||
let node_id = data.node_id;
|
||||
let old_body =
|
||||
|
|
|
|||
|
|
@ -600,6 +600,14 @@ pub struct Config {
|
|||
pub simple_im: Option<SimpleIm>,
|
||||
pub fallback_output_mode: Option<FallbackOutputMode>,
|
||||
pub mouse_follows_focus: Option<bool>,
|
||||
pub scratchpads: Vec<Scratchpad>,
|
||||
pub autotile: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Scratchpad {
|
||||
pub name: String,
|
||||
pub exec: Option<Exec>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
|
|||
|
|
@ -156,6 +156,7 @@ impl Parser for ConfigParser<'_> {
|
|||
mouse_follows_focus,
|
||||
animations_val,
|
||||
),
|
||||
(scratchpads_val, autotile),
|
||||
) = ext.extract((
|
||||
(
|
||||
opt(val("keymap")),
|
||||
|
|
@ -217,6 +218,7 @@ impl Parser for ConfigParser<'_> {
|
|||
recover(opt(bol("unstable-mouse-follows-focus"))),
|
||||
opt(val("animations")),
|
||||
),
|
||||
(opt(val("scratchpads")), recover(opt(bol("autotile")))),
|
||||
))?;
|
||||
let mut keymap = None;
|
||||
if let Some(value) = keymap_val {
|
||||
|
|
@ -618,6 +620,8 @@ impl Parser for ConfigParser<'_> {
|
|||
simple_im,
|
||||
fallback_output_mode,
|
||||
mouse_follows_focus: mouse_follows_focus.despan(),
|
||||
scratchpads,
|
||||
autotile: autotile.despan(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ use {
|
|||
client::Client,
|
||||
config, config_dir,
|
||||
exec::{Command, set_env, unset_env},
|
||||
get_workspace,
|
||||
get_autotile, get_workspace,
|
||||
input::{
|
||||
FocusFollowsMouseMode, InputDevice, Seat, SwitchEvent, capability::CAP_SWITCH,
|
||||
get_seat, input_devices, on_input_device_removed, on_new_input_device,
|
||||
|
|
@ -40,11 +40,10 @@ use {
|
|||
on_devices_enumerated, on_idle, on_unload, quit, reload, set_animation_cubic_bezier,
|
||||
set_animation_curve, set_animation_duration_ms, set_animation_style,
|
||||
set_animations_enabled, set_autotile, set_color_management_enabled, set_corner_radius,
|
||||
set_default_workspace_capture,
|
||||
set_explicit_sync_enabled, set_float_above_fullscreen, set_floating_titles, set_idle,
|
||||
set_idle_grace_period, set_middle_click_paste_enabled, set_show_bar,
|
||||
set_show_float_pin_icon, set_show_titles, set_tab_title_align, set_ui_drag_enabled,
|
||||
set_ui_drag_threshold,
|
||||
set_default_workspace_capture, set_explicit_sync_enabled, set_float_above_fullscreen,
|
||||
set_floating_titles, set_idle, set_idle_grace_period, set_middle_click_paste_enabled,
|
||||
set_show_bar, set_show_float_pin_icon, set_show_titles, set_tab_title_align,
|
||||
set_ui_drag_enabled, set_ui_drag_threshold,
|
||||
status::{set_i3bar_separator, set_status, set_status_command, unset_status_command},
|
||||
switch_to_vt,
|
||||
tasks::{self, JoinHandle},
|
||||
|
|
@ -270,12 +269,7 @@ impl Action {
|
|||
SimpleCommand::MoveTabLeft => b.new(move || s.move_tab(false)),
|
||||
SimpleCommand::MoveTabRight => b.new(move || s.move_tab(true)),
|
||||
SimpleCommand::SetAutotile(enabled) => b.new(move || set_autotile(enabled)),
|
||||
SimpleCommand::ToggleAutotile => {
|
||||
b.new(move || {
|
||||
// Toggle not directly supported; set to true
|
||||
set_autotile(true)
|
||||
})
|
||||
}
|
||||
SimpleCommand::ToggleAutotile => b.new(move || set_autotile(!get_autotile())),
|
||||
},
|
||||
Action::Multi { actions } => {
|
||||
let actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();
|
||||
|
|
@ -1747,6 +1741,9 @@ fn load_config(initial_load: bool, auto_reload: bool, persistent: &Rc<Persistent
|
|||
.seat
|
||||
.unstable_set_mouse_follows_focus(mouse_follows_focus);
|
||||
}
|
||||
if let Some(v) = config.autotile {
|
||||
set_autotile(v);
|
||||
}
|
||||
}
|
||||
|
||||
fn create_command(exec: &Exec) -> Command {
|
||||
|
|
|
|||
|
|
@ -1209,6 +1209,10 @@
|
|||
"type": "boolean",
|
||||
"description": "Configures whether middle-click pasting is enabled.\n\nChanging this has no effect on running applications.\n\nThe default is `true`.\n"
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"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",
|
||||
"type": "object",
|
||||
|
|
@ -2068,6 +2072,9 @@
|
|||
"make-group-tab",
|
||||
"change-group-opposite",
|
||||
"toggle-tab",
|
||||
"enable-autotile",
|
||||
"disable-autotile",
|
||||
"toggle-autotile",
|
||||
"toggle-fullscreen",
|
||||
"enter-fullscreen",
|
||||
"exit-fullscreen",
|
||||
|
|
|
|||
|
|
@ -2489,6 +2489,18 @@ The table has the following fields:
|
|||
|
||||
The value of this field should be a boolean.
|
||||
|
||||
- `autotile` (optional):
|
||||
|
||||
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
|
||||
`enable-autotile`, `disable-autotile`, and `toggle-autotile` actions.
|
||||
|
||||
The default is `false`.
|
||||
|
||||
The value of this field should be a boolean.
|
||||
|
||||
- `modes` (optional):
|
||||
|
||||
Configures the input modes.
|
||||
|
|
@ -4613,6 +4625,18 @@ The string should have one of the following values:
|
|||
|
||||
Toggles the current group between tabbed and split mode.
|
||||
|
||||
- `enable-autotile`:
|
||||
|
||||
Enables alternating split orientation for newly tiled windows.
|
||||
|
||||
- `disable-autotile`:
|
||||
|
||||
Disables alternating split orientation for newly tiled windows.
|
||||
|
||||
- `toggle-autotile`:
|
||||
|
||||
Toggles alternating split orientation for newly tiled windows.
|
||||
|
||||
- `toggle-fullscreen`:
|
||||
|
||||
Toggle the currently focused window between fullscreen and windowed.
|
||||
|
|
@ -5806,4 +5830,3 @@ The table has the following fields:
|
|||
The scaling mode of X windows.
|
||||
|
||||
The value of this field should be a [XScalingMode](#types-XScalingMode).
|
||||
|
||||
|
|
|
|||
|
|
@ -1064,6 +1064,12 @@ SimpleActionName:
|
|||
description: Toggles the current group's direction.
|
||||
- value: toggle-tab
|
||||
description: Toggles the current group between tabbed and split mode.
|
||||
- value: enable-autotile
|
||||
description: Enables alternating split orientation for newly tiled windows.
|
||||
- value: disable-autotile
|
||||
description: Disables alternating split orientation for newly tiled windows.
|
||||
- value: toggle-autotile
|
||||
description: Toggles alternating split orientation for newly tiled windows.
|
||||
- value: toggle-fullscreen
|
||||
description: Toggle the currently focused window between fullscreen and windowed.
|
||||
- value: enter-fullscreen
|
||||
|
|
@ -3133,6 +3139,17 @@ Config:
|
|||
Changing this has no effect on running applications.
|
||||
|
||||
The default is `true`.
|
||||
autotile:
|
||||
kind: boolean
|
||||
required: false
|
||||
description: |
|
||||
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
|
||||
`enable-autotile`, `disable-autotile`, and `toggle-autotile` actions.
|
||||
|
||||
The default is `false`.
|
||||
modes:
|
||||
kind: map
|
||||
values:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue