Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/configuration/animations.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ The last kind of curves we support are spring curves. They use a physical model
[libadwaita's `SpringAnimation`](https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1.3/class.SpringAnimation.html).

Since they are much more tweakable, you'd rather use something like [Elastic](https://apps.gnome.org/Elastic/) to tweak
the parameters yourself, and get a preview of what the curve will look like, aswell as the total animation time.
the parameters yourself, and get a preview of what the curve will look like, as well as the total animation time.

![Elastic window](/assets/elastic.png)

Note however that you should be conservative with the valuess you pass into this animation, as it can *really quickly* cause
Note however that you should be conservative with the values you pass into this animation, as it can *really quickly* cause
values to overshoot/undershoot to infinity, and potentially causing crashes (integer overflows).

```toml
Expand Down
4 changes: 2 additions & 2 deletions docs/configuration/decorations.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ still enhance the looks of the desktop session
## Borders

Borders are an outline drawn around windows in a workspace. The focused window will get a different border color to indicate that it is focused and that
it has active keyboard focus. Borders can also apply rounded corner radius aroud windows.
it has active keyboard focus. Borders can also apply rounded corner radius around windows.

#### `border.focused-color`, `border.normal-color`

Expand Down Expand Up @@ -78,7 +78,7 @@ though nothing stops you from using high number of passes with low blur values,

> [!CAUTION]
> Higher blur passes will cause more rendering to happen, and thus put more strain on your GPU. It is recommended to keep this value below
> 3 or 4, because otherwise it will *kill* your performance and framerates
> 3 or 4, because otherwise it will *kill* your performance and frame rates

---

Expand Down
4 changes: 2 additions & 2 deletions docs/configuration/input.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ By default, all these are empty strings (using system defaults), and layout is `

#### `repeat-rate`, `repeat-delay`

These two options control key repeating. `repeat-delay` is the delay in milliseconds that you should hown a key for key repeating to
These two options control key repeating. `repeat-delay` is the delay in milliseconds that you should hold down a key for repeating to
start. `repeat-rate` is the frequency at which the key is repeated.

Default settings are `repeat-rate=25`, `repeat-delay=250`
Expand Down Expand Up @@ -76,7 +76,7 @@ For touchpads, determines how to emulate a scroll wheel using only your fingers
#### `scroll-button`, `scroll-button-lock`

The button used to enable `on-button-down` scroll method. When `scroll-button-lock` is enabled, the button does not need to be held
down, and insteads turns the button into a toggle switch.
down, and instead turns the button into a toggle switch.

---

Expand Down
7 changes: 3 additions & 4 deletions docs/configuration/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ The configuration is live-reloaded. You can edit and save the file and `fht-comp
apply changes.

If you made a mistake when writing your configuration (let that be syntax, invalid values, unknown enum variant, etc.), the
compositor will warn you with a popup window slidi
ng from the top of your screen. You can run `fht-comopsitor check-configuration`
compositor will warn you with a popup window sliding from the top of your screen. You can run `fht-comopsitor check-configuration`
to get that error in your terminal.


Expand Down Expand Up @@ -63,7 +62,7 @@ The merging logic does as so: For each file (starting from the main `compositor.
##### `autostart`

Command lines to run whenever the compositor starts up. Each line is evaluated using `/bin/sh -c "<command line>"`, meaning you have
access to shell-expansions like using variables, or exapding of `~` to `$HOME`
access to shell-expansions like using variables, or expanding of `~` to `$HOME`

> [!TIP] Autostart with systemd units
> While using this approach for autostart can work, using systemd user services are a much better! You benefit from having
Expand All @@ -83,7 +82,7 @@ access to shell-expansions like using variables, or exapding of `~` to `$HOME`

##### `env`

Environment variables to set. They are set before anything else starts up in the compositor, and before `autostart` is exxecuted
Environment variables to set. They are set before anything else starts up in the compositor, and before `autostart` is executed

```toml
[env]
Expand Down
2 changes: 1 addition & 1 deletion docs/configuration/keybindings.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ Swaps the currently focused window with next/previous window in the workspace. R

#### `focus-next-output`, `focus-previous-output`

Focus the next/previous output in the gloabl space. Outputs are ordered by the way they got detected/inserted.
Focus the next/previous output in the global space. Outputs are ordered by the way they got detected/inserted.

---

Expand Down
2 changes: 1 addition & 1 deletion docs/usage/layouts.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Dynamic layouts are extremely flexible and can be molded to adapt for any situat
fuss creating a specialized layout for your current job in other window management models like Sway's or River's, instead, you
pick a layout that suits your need, resize and move some window around, and start working.

You can create workflows and make them work with what you have to do quickly, and adapt for some unforseen cases or patterns
You can create workflows and make them work with what you have to do quickly, and adapt for some unforeseen cases or patterns
the main task has. You can build from the provided layouts and get started working.

Overall, the experience is very natural and you'll get the hang of it really fast.
4 changes: 2 additions & 2 deletions docs/usage/workspaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ instead you move individual windows from/to workspaces or outputs.

## Advantages of this system

Windows are organized in a predictible fashion, and you have full knowledge and control on where they are and where they go.
Windows are organized in a predictable fashion, and you have full knowledge and control on where they are and where they go.
Workspaces are always available for window rules and custom scripts (for example to create scratchpad workspaces).

## Example workflow
Expand All @@ -27,7 +27,7 @@ I am currently *working* on), second is for the web browser, third is for chat c
Fractal, etc.) sixth is for video games, etc.

This streamlined workflow allows me to switch working context **very** fast using keybinds, since I have keybinds
engrained in my mind, I can switly get to my browser to search up documentation, quickly respond to a notification from
engrained in my mind, I can swiftly get to my browser to search up documentation, quickly respond to a notification from
a chat client, and more.

If you want, you can even disable [animations](/configuration/animations) to make this even more immediate.
2 changes: 2 additions & 0 deletions fht-compositor-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,8 @@ pub enum WorkspaceLayout {
Tile,
BottomStack,
CenteredMaster,
BinaryTree,
SpiralTree,
Floating,
}

Expand Down
1 change: 1 addition & 0 deletions src/space/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use crate::utils::RectCenterExt;
use crate::window::Window;

mod border;
mod tree;
mod closing_tile;
mod monitor;
pub mod shadow;
Expand Down
234 changes: 234 additions & 0 deletions src/space/tree.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
use smithay::utils::{Logical, Rectangle};

use fht_compositor_config::WorkspaceLayout;

#[derive(Debug)]
pub enum Split {
Horizontal,
Vertical,
}

/// A BSP Tree Node can either have zero or two children.
#[derive(Debug)]
pub struct Node {
pub rect: Rectangle<i32, Logical>,
pub split: Split,
pub first_child: Option<usize>,
pub second_child: Option<usize>,
pub parent: Option<usize>,
}

#[derive(Debug)]
pub struct Tree {
pub arena: Vec<Node>,
pub leaves: usize,
layout: WorkspaceLayout,
inner_gaps: i32,
}

impl Node {
pub fn new(rect: Rectangle<i32, Logical>, split: Split, parent: Option<usize>) -> Node {
Node {
rect,
split,
first_child: None,
second_child: None,
parent,
}
}

pub fn is_leaf(&self) -> bool {
self.first_child.is_none() && self.second_child.is_none()
}
}

impl Tree {
pub fn new(layout: WorkspaceLayout, rect: Rectangle<i32, Logical>, len: usize, inner_gaps: i32) -> Self {
let root = Node::new(rect, Split::Horizontal, None);

let mut arena = Vec::with_capacity(len);
arena.push(root);

Tree {
arena,
leaves: 1,
layout,
inner_gaps,
}
}

pub fn add_child(&mut self, node: Node) -> usize {
let idx = self.arena.len();
if let Some(parent) = node.parent {
if self.arena[parent].first_child.is_none() {
self.arena[parent].first_child = Some(idx);
} else if self.arena[parent].second_child.is_none() {
self.arena[parent].second_child = Some(idx);
}
}
self.arena.push(node);

idx
}

pub fn grow(&mut self, idx: usize, len: usize, split_ratio: f64) {
if self.arena[idx].is_leaf() && self.leaves < len {
let mut first_rect = self.arena[idx].rect;
let mut second_rect = self.arena[idx].rect;
let mut first_split = Split::Vertical;
let mut second_split = Split::Vertical;

match self.arena[idx].split {
Split::Horizontal => {
let nh = (self.arena[idx].rect.size.h as f64 * split_ratio) as i32
- (self.inner_gaps / 2);
first_rect.size = (first_rect.size.w, nh).into();
second_rect.size = (second_rect.size.w, nh).into();
let nly = self.arena[idx].rect.loc.y + nh + self.inner_gaps;

if self.leaves % 4 == 3 && self.layout == WorkspaceLayout::SpiralTree {
first_rect.loc = (first_rect.loc.x, nly).into();
} else {
second_rect.loc = (second_rect.loc.x, nly).into();
}
}
Split::Vertical => {
let nw = (self.arena[idx].rect.size.w as f64 * split_ratio) as i32
- (self.inner_gaps / 2);
first_rect.size = (nw, first_rect.size.h).into();
second_rect.size = (nw, second_rect.size.h).into();
let nlx = self.arena[idx].rect.loc.x + nw + self.inner_gaps;

if self.leaves % 4 == 2 && self.layout == WorkspaceLayout::SpiralTree {
first_rect.loc = (nlx, first_rect.loc.y).into();
} else {
second_rect.loc = (nlx, second_rect.loc.y).into();
}

first_split = Split::Horizontal;
second_split = Split::Horizontal;
}
}

let first_child = Node::new(first_rect, first_split, Some(idx));
let second_child = Node::new(second_rect, second_split, Some(idx));

let _ = self.add_child(first_child);
let n_idx = self.add_child(second_child);

// We're technically adding two leaves, but then we iterate into another branch in the
// `build_tree` call, so it's plus two, minus one
self.leaves += 1;

self.grow(n_idx, len, split_ratio);
} else {
return;
}
}

pub fn into_leaves(&mut self, root: usize) -> Vec<Rectangle<i32, Logical>> {
let mut leaves = Vec::new();

if self.arena[root].is_leaf() {
leaves.push(self.arena[root].rect);
} else {
if let Some(first) = self.arena[root].first_child {
leaves.append(&mut self.into_leaves(first));
}
if let Some(second) = self.arena[root].second_child {
leaves.append(&mut self.into_leaves(second));
}
}

leaves
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn correct_sizes() {
let mut tree = Tree::new(WorkspaceLayout::BinaryTree, Rectangle::new((0, 0).into(), (100, 100).into()), 4, 0);
tree.grow(0, 4, 0.5);
let leaves = tree.into_leaves(0);

assert_eq!(leaves[0].size.w, 100);
assert_eq!(leaves[0].size.h, 50);

assert_eq!(leaves[1].size.w, 50);
assert_eq!(leaves[1].size.h, 50);

assert_eq!(leaves[2].size.w, 50);
assert_eq!(leaves[2].size.h, 25);

assert_eq!(leaves[3].size.w, 50);
assert_eq!(leaves[3].size.h, 25);

assert_eq!(leaves[0].loc.x, 0);
assert_eq!(leaves[0].loc.y, 0);

assert_eq!(leaves[1].loc.x, 0);
assert_eq!(leaves[1].loc.y, 50);

assert_eq!(leaves[2].loc.x, 50);
assert_eq!(leaves[2].loc.y, 50);

assert_eq!(leaves[3].loc.x, 50);
assert_eq!(leaves[3].loc.y, 75);
}

#[test]
fn correct_locs_for_spiral() {
let mut tree = Tree::new(WorkspaceLayout::SpiralTree, Rectangle::new((0, 0).into(), (100, 100).into()), 4, 0);
tree.grow(0, 5, 0.5);
let leaves = tree.into_leaves(0);

assert_eq!(leaves[0].loc.x, 0);
assert_eq!(leaves[0].loc.y, 0);

assert_eq!(leaves[1].loc.x, 50);
assert_eq!(leaves[1].loc.y, 50);

assert_eq!(leaves[2].loc.x, 0);
assert_eq!(leaves[2].loc.y, 75);

assert_eq!(leaves[3].loc.x, 0);
assert_eq!(leaves[3].loc.y, 50);

assert_eq!(leaves[4].loc.x, 25);
assert_eq!(leaves[4].loc.y, 50);
}

#[test]
fn correct_sizes_with_gaps() {
let mut tree = Tree::new(WorkspaceLayout::BinaryTree, Rectangle::new((0, 0).into(), (100, 100).into()), 4, 4);
tree.grow(0, 4, 0.5);
let leaves = tree.into_leaves(0);

assert_eq!(leaves[0].size.w, 100);
assert_eq!(leaves[0].size.h, 48);

assert_eq!(leaves[1].size.w, 48);
assert_eq!(leaves[1].size.h, 48);

assert_eq!(leaves[2].size.w, 48);
assert_eq!(leaves[2].size.h, 22);

assert_eq!(leaves[3].size.w, 48);
assert_eq!(leaves[3].size.h, 22);

assert_eq!(leaves[0].loc.x, 0);
assert_eq!(leaves[0].loc.y, 0);

assert_eq!(leaves[1].loc.x, 0);
assert_eq!(leaves[1].loc.y, 52);

assert_eq!(leaves[2].loc.x, 52);
assert_eq!(leaves[2].loc.y, 52);

assert_eq!(leaves[3].loc.x, 52);
assert_eq!(leaves[3].loc.y, 78);
}
}
Loading