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
|
In mono mode, scroll over the title bar to cycle between windows in the
|
||||||
container.
|
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
|
## Fullscreen
|
||||||
|
|
||||||
Press `alt-u` (`toggle-fullscreen`) to make the focused window fill the entire
|
Press `alt-u` (`toggle-fullscreen`) to make the focused window fill the entire
|
||||||
|
|
|
||||||
|
|
@ -2079,6 +2079,12 @@ impl ConfigClient {
|
||||||
self.send(&ClientMessage::SetAutotile { enabled });
|
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) {
|
pub fn set_tab_title_align(&self, align: u32) {
|
||||||
self.send(&ClientMessage::SetTabTitleAlign { align });
|
self.send(&ClientMessage::SetTabTitleAlign { align });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -923,6 +923,7 @@ pub enum ClientMessage<'a> {
|
||||||
SetAutotile {
|
SetAutotile {
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
},
|
},
|
||||||
|
GetAutotile,
|
||||||
SetTabTitleAlign {
|
SetTabTitleAlign {
|
||||||
align: u32,
|
align: u32,
|
||||||
},
|
},
|
||||||
|
|
@ -1189,6 +1190,9 @@ pub enum Response {
|
||||||
GetCornerRadius {
|
GetCornerRadius {
|
||||||
radius: f32,
|
radius: f32,
|
||||||
},
|
},
|
||||||
|
GetAutotile {
|
||||||
|
enabled: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -453,14 +453,21 @@ pub fn get_corner_radius() -> f32 {
|
||||||
|
|
||||||
/// Enables or disables autotiling.
|
/// Enables or disables autotiling.
|
||||||
///
|
///
|
||||||
/// When enabled, new windows are automatically placed in a perpendicular
|
/// When enabled, newly tiled windows alternate split orientation from the
|
||||||
/// sub-container if the predicted body would be narrower than tall (or vice versa).
|
/// 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`.
|
/// The default is `false`.
|
||||||
pub fn set_autotile(enabled: bool) {
|
pub fn set_autotile(enabled: bool) {
|
||||||
get!().set_autotile(enabled)
|
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.
|
/// Sets the horizontal alignment of title text within tab buttons.
|
||||||
///
|
///
|
||||||
/// - `"start"` — left-aligned (default)
|
/// - `"start"` — left-aligned (default)
|
||||||
|
|
|
||||||
|
|
@ -3587,6 +3587,11 @@ impl ConfigProxyHandler {
|
||||||
ClientMessage::SetAutotile { enabled } => {
|
ClientMessage::SetAutotile { enabled } => {
|
||||||
self.state.theme.autotile_enabled.set(enabled);
|
self.state.theme.autotile_enabled.set(enabled);
|
||||||
}
|
}
|
||||||
|
ClientMessage::GetAutotile => {
|
||||||
|
self.respond(Response::GetAutotile {
|
||||||
|
enabled: self.state.theme.autotile_enabled.get(),
|
||||||
|
});
|
||||||
|
}
|
||||||
ClientMessage::SeatToggleExpand { .. } => {
|
ClientMessage::SeatToggleExpand { .. } => {
|
||||||
// Removed feature; kept for binary protocol compatibility.
|
// Removed feature; kept for binary protocol compatibility.
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -331,6 +331,10 @@ impl TestConfig {
|
||||||
pub fn set_show_titles(&self, show: bool) -> TestResult {
|
pub fn set_show_titles(&self, show: bool) -> TestResult {
|
||||||
self.send(ClientMessage::SetShowTitles { show })
|
self.send(ClientMessage::SetShowTitles { show })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_autotile(&self, enabled: bool) -> TestResult {
|
||||||
|
self.send(ClientMessage::SetAutotile { enabled })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for TestConfig {
|
impl Drop for TestConfig {
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@ mod t0051_pointer_warp;
|
||||||
mod t0052_bar;
|
mod t0052_bar;
|
||||||
mod t0053_theme;
|
mod t0053_theme;
|
||||||
mod t0054_subsurface_already_attached;
|
mod t0054_subsurface_already_attached;
|
||||||
|
mod t0055_autotiling;
|
||||||
|
|
||||||
pub trait TestCase: Sync {
|
pub trait TestCase: Sync {
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
|
|
@ -158,5 +159,6 @@ pub fn tests() -> Vec<&'static dyn TestCase> {
|
||||||
t0052_bar,
|
t0052_bar,
|
||||||
t0053_theme,
|
t0053_theme,
|
||||||
t0054_subsurface_already_attached,
|
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().kind.is_app_window()
|
||||||
&& !node.tl_data().visible.get();
|
&& !node.tl_data().visible.get();
|
||||||
if animate_new_app_map {
|
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 {
|
} 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());
|
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);
|
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>) {
|
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() {
|
if let Some(c) = ws.container.get() {
|
||||||
let la = c.clone().tl_last_active_child();
|
let la = c.clone().tl_last_active_child();
|
||||||
let lap = la
|
let lap = la
|
||||||
|
|
@ -946,7 +966,11 @@ impl State {
|
||||||
.get()
|
.get()
|
||||||
.and_then(|n| n.node_into_container());
|
.and_then(|n| n.node_into_container());
|
||||||
if let Some(lap) = lap {
|
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 {
|
} else {
|
||||||
c.append_child(node);
|
c.append_child(node);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -290,6 +290,47 @@ impl ContainerNode {
|
||||||
self.add_child_x(prev, new, |prev, new| self.add_child_after_(prev, new));
|
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>) {
|
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));
|
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) {
|
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);
|
let (split, right) = direction_to_split(direction);
|
||||||
if split != self.split.get() || right {
|
if split != self.split.get() || right {
|
||||||
self.append_child(node);
|
self.append_child(node);
|
||||||
|
|
|
||||||
|
|
@ -979,7 +979,7 @@ impl ToplevelData {
|
||||||
}
|
}
|
||||||
fd.workspace.remove_fullscreen_node();
|
fd.workspace.remove_fullscreen_node();
|
||||||
if fd.placeholder.is_destroyed() {
|
if fd.placeholder.is_destroyed() {
|
||||||
state.map_tiled(node);
|
state.map_tiled_without_autotile(node);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let parent = fd.placeholder.tl_data().parent.take().unwrap();
|
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 {
|
if !floating {
|
||||||
parent.cnode_remove_child2(&*tl, true);
|
parent.cnode_remove_child2(&*tl, true);
|
||||||
state.map_tiled(tl);
|
state.map_tiled_without_autotile(tl);
|
||||||
} else if let Some(ws) = data.workspace.get() {
|
} else if let Some(ws) = data.workspace.get() {
|
||||||
let node_id = data.node_id;
|
let node_id = data.node_id;
|
||||||
let old_body =
|
let old_body =
|
||||||
|
|
|
||||||
|
|
@ -600,6 +600,14 @@ pub struct Config {
|
||||||
pub simple_im: Option<SimpleIm>,
|
pub simple_im: Option<SimpleIm>,
|
||||||
pub fallback_output_mode: Option<FallbackOutputMode>,
|
pub fallback_output_mode: Option<FallbackOutputMode>,
|
||||||
pub mouse_follows_focus: Option<bool>,
|
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)]
|
#[derive(Debug, Error)]
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
mouse_follows_focus,
|
mouse_follows_focus,
|
||||||
animations_val,
|
animations_val,
|
||||||
),
|
),
|
||||||
|
(scratchpads_val, autotile),
|
||||||
) = ext.extract((
|
) = ext.extract((
|
||||||
(
|
(
|
||||||
opt(val("keymap")),
|
opt(val("keymap")),
|
||||||
|
|
@ -217,6 +218,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
recover(opt(bol("unstable-mouse-follows-focus"))),
|
recover(opt(bol("unstable-mouse-follows-focus"))),
|
||||||
opt(val("animations")),
|
opt(val("animations")),
|
||||||
),
|
),
|
||||||
|
(opt(val("scratchpads")), recover(opt(bol("autotile")))),
|
||||||
))?;
|
))?;
|
||||||
let mut keymap = None;
|
let mut keymap = None;
|
||||||
if let Some(value) = keymap_val {
|
if let Some(value) = keymap_val {
|
||||||
|
|
@ -618,6 +620,8 @@ impl Parser for ConfigParser<'_> {
|
||||||
simple_im,
|
simple_im,
|
||||||
fallback_output_mode,
|
fallback_output_mode,
|
||||||
mouse_follows_focus: mouse_follows_focus.despan(),
|
mouse_follows_focus: mouse_follows_focus.despan(),
|
||||||
|
scratchpads,
|
||||||
|
autotile: autotile.despan(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use {
|
||||||
client::Client,
|
client::Client,
|
||||||
config, config_dir,
|
config, config_dir,
|
||||||
exec::{Command, set_env, unset_env},
|
exec::{Command, set_env, unset_env},
|
||||||
get_workspace,
|
get_autotile, get_workspace,
|
||||||
input::{
|
input::{
|
||||||
FocusFollowsMouseMode, InputDevice, Seat, SwitchEvent, capability::CAP_SWITCH,
|
FocusFollowsMouseMode, InputDevice, Seat, SwitchEvent, capability::CAP_SWITCH,
|
||||||
get_seat, input_devices, on_input_device_removed, on_new_input_device,
|
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,
|
on_devices_enumerated, on_idle, on_unload, quit, reload, set_animation_cubic_bezier,
|
||||||
set_animation_curve, set_animation_duration_ms, set_animation_style,
|
set_animation_curve, set_animation_duration_ms, set_animation_style,
|
||||||
set_animations_enabled, set_autotile, set_color_management_enabled, set_corner_radius,
|
set_animations_enabled, set_autotile, set_color_management_enabled, set_corner_radius,
|
||||||
set_default_workspace_capture,
|
set_default_workspace_capture, set_explicit_sync_enabled, set_float_above_fullscreen,
|
||||||
set_explicit_sync_enabled, set_float_above_fullscreen, set_floating_titles, set_idle,
|
set_floating_titles, set_idle, set_idle_grace_period, set_middle_click_paste_enabled,
|
||||||
set_idle_grace_period, set_middle_click_paste_enabled, set_show_bar,
|
set_show_bar, set_show_float_pin_icon, set_show_titles, set_tab_title_align,
|
||||||
set_show_float_pin_icon, set_show_titles, set_tab_title_align, set_ui_drag_enabled,
|
set_ui_drag_enabled, set_ui_drag_threshold,
|
||||||
set_ui_drag_threshold,
|
|
||||||
status::{set_i3bar_separator, set_status, set_status_command, unset_status_command},
|
status::{set_i3bar_separator, set_status, set_status_command, unset_status_command},
|
||||||
switch_to_vt,
|
switch_to_vt,
|
||||||
tasks::{self, JoinHandle},
|
tasks::{self, JoinHandle},
|
||||||
|
|
@ -270,12 +269,7 @@ impl Action {
|
||||||
SimpleCommand::MoveTabLeft => b.new(move || s.move_tab(false)),
|
SimpleCommand::MoveTabLeft => b.new(move || s.move_tab(false)),
|
||||||
SimpleCommand::MoveTabRight => b.new(move || s.move_tab(true)),
|
SimpleCommand::MoveTabRight => b.new(move || s.move_tab(true)),
|
||||||
SimpleCommand::SetAutotile(enabled) => b.new(move || set_autotile(enabled)),
|
SimpleCommand::SetAutotile(enabled) => b.new(move || set_autotile(enabled)),
|
||||||
SimpleCommand::ToggleAutotile => {
|
SimpleCommand::ToggleAutotile => b.new(move || set_autotile(!get_autotile())),
|
||||||
b.new(move || {
|
|
||||||
// Toggle not directly supported; set to true
|
|
||||||
set_autotile(true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Action::Multi { actions } => {
|
Action::Multi { actions } => {
|
||||||
let actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();
|
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
|
.seat
|
||||||
.unstable_set_mouse_follows_focus(mouse_follows_focus);
|
.unstable_set_mouse_follows_focus(mouse_follows_focus);
|
||||||
}
|
}
|
||||||
|
if let Some(v) = config.autotile {
|
||||||
|
set_autotile(v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_command(exec: &Exec) -> Command {
|
fn create_command(exec: &Exec) -> Command {
|
||||||
|
|
|
||||||
|
|
@ -1209,6 +1209,10 @@
|
||||||
"type": "boolean",
|
"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"
|
"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": {
|
"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",
|
"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",
|
"type": "object",
|
||||||
|
|
@ -2068,6 +2072,9 @@
|
||||||
"make-group-tab",
|
"make-group-tab",
|
||||||
"change-group-opposite",
|
"change-group-opposite",
|
||||||
"toggle-tab",
|
"toggle-tab",
|
||||||
|
"enable-autotile",
|
||||||
|
"disable-autotile",
|
||||||
|
"toggle-autotile",
|
||||||
"toggle-fullscreen",
|
"toggle-fullscreen",
|
||||||
"enter-fullscreen",
|
"enter-fullscreen",
|
||||||
"exit-fullscreen",
|
"exit-fullscreen",
|
||||||
|
|
|
||||||
|
|
@ -2489,6 +2489,18 @@ The table has the following fields:
|
||||||
|
|
||||||
The value of this field should be a boolean.
|
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):
|
- `modes` (optional):
|
||||||
|
|
||||||
Configures the input modes.
|
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.
|
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-fullscreen`:
|
||||||
|
|
||||||
Toggle the currently focused window between fullscreen and windowed.
|
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 scaling mode of X windows.
|
||||||
|
|
||||||
The value of this field should be a [XScalingMode](#types-XScalingMode).
|
The value of this field should be a [XScalingMode](#types-XScalingMode).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1064,6 +1064,12 @@ SimpleActionName:
|
||||||
description: Toggles the current group's direction.
|
description: Toggles the current group's direction.
|
||||||
- value: toggle-tab
|
- value: toggle-tab
|
||||||
description: Toggles the current group between tabbed and split mode.
|
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
|
- value: toggle-fullscreen
|
||||||
description: Toggle the currently focused window between fullscreen and windowed.
|
description: Toggle the currently focused window between fullscreen and windowed.
|
||||||
- value: enter-fullscreen
|
- value: enter-fullscreen
|
||||||
|
|
@ -3133,6 +3139,17 @@ Config:
|
||||||
Changing this has no effect on running applications.
|
Changing this has no effect on running applications.
|
||||||
|
|
||||||
The default is `true`.
|
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:
|
modes:
|
||||||
kind: map
|
kind: map
|
||||||
values:
|
values:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue