Skip to main content

Tiling

Struct Tiling 

Source
pub struct Tiling {
    nodes: Vec<Option<Node>>,
    root: Option<usize>,
    floors: Floors,
}
Expand description

A dwindle tree.

Fields§

§nodes: Vec<Option<Node>>§root: Option<usize>§floors: Floors

Each window’s own floor, as the caller last handed them in. See Self::set_floors.

Implementations§

Source§

impl Tiling

Source

pub fn new() -> Self

Source

pub fn set_floors(&mut self, floors: Floors)

Each window’s own floor, by id, for every call after this one.

Replaced whole, not merged: the caller hands in what every window says now, and a window missing from it has no floor (floor_tests::a_window_whose_floor_goes_away_hands_the_room_back). A window that is not in the tree yet is read when it is inserted (floor_tests::a_split_that_would_put_a_window_under_its_floor_is_refused).

Source

pub fn cramped(&self, id: u64, rect: Rect) -> bool

Whether rect, a tile this tree laid out for window id, is smaller than that window’s own floor on either side.

Its own floor and nothing else: a tile under Settings::minimum is one the layout chose or allowed (#134), and a client inside it is not refusing anything (floor_tests::a_sibling_gives_no_more_than_it_holds has one of each). This is the window whose client will commit more than it was given and be cut to its tile (#133).

Source

fn floor_of(&self, id: u64) -> Minimum

Window id’s own floor, each side read as a length.

Source

fn leaf_need(&self, id: u64, axis: Axis, settings: Settings) -> Need

What window id asks of the room along axis. See need_of.

Source

fn need(&self, index: usize, axis: Axis, settings: Settings) -> Need

What the subtree at index asks of the room along axis. See Need.

Cut along axis, its children share that room, so it needs both of theirs and the gap – and once anything in it pulls, it needs every window in it at what that window will give down to, since those are what the pulling one takes its room from. Cut the other way, each child has the whole extent, so it needs only the larger. Both are floor_tests::a_floor_deep_in_a_subtree_takes_room_across_the_root.

Source

fn share( &self, axis: Axis, ratio: f64, children: [usize; 2], rect: Rect, settings: Settings, ) -> f64

The ratio a split is laid out at, in rect: its own, or the one that gives a side under its floor its floor. See shared.

No floors at all is the ratio as it is, without any arithmetic on it: a tree nobody has handed floors lays out what it did before #115, and every test above floor_tests is one that has none.

Source

pub fn is_empty(&self) -> bool

Source

pub fn contains(&self, id: u64) -> bool

Source

pub fn windows(&self) -> Vec<u64>

Every window in the tree, in insertion-independent tree order.

Source

fn walk(&self, index: usize, out: &mut Vec<u64>)

Source

pub fn insert( &mut self, id: u64, target: Option<u64>, at: Option<(f64, f64)>, area: Rect, settings: Settings, )

Add a window by splitting target, or the whole area if there is none.

at is where the pointer was. It decides which side of the split the new window lands on — dropping a window on the right half of another puts it on the right — which is the part that makes this feel like placing a window rather than appending to a list.

Splits whatever it is pointed at, across that box’s longer side, however small the halves come out: Settings::minimum is not read here. This is the last resort, "allow" in config.tiling.overflow; Self::insert_fitting is the same placement with the floor on it.

Source

pub fn insert_fitting( &mut self, id: u64, target: Option<u64>, at: Option<(f64, f64)>, area: Rect, settings: Settings, ) -> bool

Self::insert, unless that would make a tile smaller than Settings::minimum, or leave the window being split or the one being added under its own floor (Self::set_floors) — in which case the other axis is tried, and failing that nothing is done at all.

The same target as insert and the same side of it: this is “open where the pointer is” with a floor under it, not a different rule. The other axis first because a box that cannot be divided one way often can be divided the other — a wide, short tile that has no room side by side may still have room one above the other — and that is still the tile the user was pointing at.

Returns whether the window is in the tree now. false leaves the tree exactly as it was, so the caller can try somewhere else; nothing is pushed until a split has been chosen, because a node pushed and then abandoned would be found by Self::contains for ever after. An empty tree always has room: its one window takes the whole area whatever size that is, since there is nothing to divide.

Source

pub fn insert_largest( &mut self, id: u64, area: Rect, settings: Settings, ) -> bool

Split the largest tile that has room for another window without going under Settings::minimum or either window’s own floor: "largest" in config.tiling.overflow.

Largest by area, and the largest with room rather than the largest: a big square tile can fail both ways where a smaller, longer one has room along its length, and skipping it is the difference between placing the window and sending it off this workspace. Ties go to the earlier tile in tree order, so the choice does not depend on anything the tree does not hold.

Each tile is tried across its longer side and then the other, as Self::insert_fitting tries the one under the pointer. The new window takes the far side, as it does from insert with no pointer: the pointer is in some other tile by now, so it says nothing about this one.

Only windows the tree holds have tiles here. A window a layout took out at closing (#128) has none, and the neighbour that grew into its space is measured at the size it is now.

Returns whether the window is in the tree now; false leaves the tree as it was. An empty tree always has room, as for insert_fitting.

Source

fn split_target( &self, id: u64, target: Option<u64>, at: Option<(f64, f64)>, boxes: &[(u64, Rect)], ) -> Option<usize>

The leaf a new window would split: the one named, else the one under the pointer, else the one nearest it. None only for an empty tree.

The last step matters more than it looks: falling back to the root instead means splitting the whole screen, and every new window then lands as another full-height column — which is not dwindle at all, and is exactly what this did before. Hyprland calls this getClosestNode.

Source

fn box_of(&self, target: usize, boxes: &[(u64, Rect)], area: Rect) -> Rect

The box a split target has now, which is what decides the axis: the split follows the shape of the space being divided rather than the shape of the screen.

Source

fn split( &mut self, id: u64, target: usize, rect: Rect, axis: Axis, at: Option<(f64, f64)>, settings: Settings, )

Hang window id beside target, which occupies rect, across axis.

Everything that decides whether and where is done by the callers; this is the one place a node is pushed for a window that is not the root, so a caller that has decided against a split has pushed nothing.

Source

pub fn swap(&mut self, a: u64, b: u64) -> bool

Trade the tiles of windows a and b, and change nothing else: every split keeps its axis and its ratio, so the arrangement is the one it was with two windows in each other’s places, and a second swap puts it back. What a keyboard move does in tiling (#150). Returns whether both were in the tree; false leaves it as it was. swap_trades_two_tiles_and_nothing_else.

Source

pub fn remove(&mut self, id: u64)

Take a window out. Its sibling inherits the space, which is what makes closing a window a local change rather than a re-tile of the screen.

Source

pub fn drag_seam( &mut self, id: u64, edge: Edge, edge_at: (f64, f64), area: Rect, settings: Settings, )

Put the seam beside a window’s edge where edge_at says that edge belongs.

edge_at is a position on each axis and only the one edge names is read — .0 for a left or right side, .1 for a top or bottom — which is what lets a corner drag call this twice with one pair and have each axis take its own edge. The coordinates are the ones Self::layout hands back, so “where this window’s right edge should be” is directly comparable with the x + w of the slot this same tree produced for it.

It is not where the pointer is, and calling it that was #124. The two coincide only for a drag that began exactly on the edge, and no gesture does: a border grab is a band sixteen pixels wide, and the modifier drag starts from wherever in the window the button went down. The compositor derives this from the rectangle the drag has produced — solium::input::resize::dragged_edge — so what arrives here has already moved exactly as far as the pointer has, from exactly where the edge already was. The arithmetic below did not change for #124 and did not need to.

This is the tiled resize path: a tiled window has no size of its own, so an edge drag moves the division it shares with its neighbour and the compositor never resizes the window directly. The floating path is the other one — see Solium::settle_resize, which only reaches hold_resize when no layout claimed the drag — and the two are deliberately independent.

Three things this does that adding a delta to the nearest parent cannot.

It finds the seam on the side that was grabbed. Self::seam_beside carries the argument; the short version is that “the nearest ancestor cut on this axis” is not the same branch as “the branch whose seam is under the pointer”, and taking the first for the second is #120.

It reaches past the immediate parent. A window’s parent may have been cut the other way — in a two-by-two, the seam beside a side edge is two levels up — which is why width could be dragged in some arrangements and not others.

And it is idempotent. The ratio comes from a position, not from how far anything moved, so handling the same drag twice gives the same layout. Accumulating deltas fed the layout’s own response back in as the next input, and the windows shook themselves apart for as long as the button was held. A relative gesture and a delta are not the same thing: #124 made the gesture relative by choosing a better position to send, and left this idempotence exactly where it was.

Source

fn room(&self, seam: usize, rect: Rect, settings: Settings) -> (f64, f64)

The ratios a seam may take: 0.05..0.95, narrowed so that no tile on either side of it is made smaller than Settings::minimum.

rect is the seam’s own box, from Self::node_box. Each side needs Self::floor of it along the seam’s axis, and a side’s share of the box less the gap is what the ratio divides, so the floors turn into ratios by the same arithmetic cut runs forwards.

A side already under its floor is held where it is rather than moved up to it. Tiles under the minimum are ordinary: "allow" makes them, a monitor change or a smaller work area can, and so can raising the minimum in a running session. Clamping such a seam into its range on the first frame of a drag would throw it across the screen before the pointer had moved – the #124 class of bug, reached through a floor instead of a skew. So the range is widened to take in the ratio the seam has now: that side can be grown, and cannot be made smaller still. With both sides under, the seam cannot move at all, because any move shrinks one of them.

No minimum on the seam’s axis leaves exactly 0.05..0.95, which is what this clamp was before #134.

Each window’s own floor narrows it further (#115), by what each side needs for every window in it with a floor to reach it – Need, the same measure Self::layout rebalances by. So the seam itself stops there, and not only the picture of it: a floor that goes away afterwards leaves the seam where the drag was stopped (floor_tests::a_seam_stops_at_a_neighbours_own_floor). “The ratio it has” is the one it is drawn at, Self::share, which is the one it holds unless a floor has moved it.

Source

fn floor(&self, index: usize, axis: Axis, least: f64, gap: f64) -> f64

The least room along axis the subtree at index needs for every tile in it to be at least least long on that axis, with its own seams where they are now.

At the ratios it has rather than at the best ratios it could have: a drag moves one seam, so a subtree’s inner seams stay where they are and its tiles grow and shrink in proportion. A branch cut along axis needs each child’s floor divided by that child’s share, plus the gap between them. A branch cut the other way gives each child the whole of its extent along axis, so it needs only the larger of the two floors – a column of stacked windows is as narrow as its widest floor, not the sum of them.

Source

fn seam_beside(&self, leaf: usize, edge: Edge) -> Option<usize>

The branch whose seam runs along edge of the window at leaf.

Two conditions, and the old walk checked only the first. The branch has to be cut along the dragged axis, and this subtree has to be on the side of it that puts the seam under the grabbed edge — which is Edge::child. A window that is the second child of a vertical split has that seam on its left and nowhere else; matching it for a right-edge drag moves the window’s far side while the pointer’s side stands still.

The walk continues towards the root rather than stopping at the first branch on the axis, because a branch on the wrong side is not a near-miss: the seam beside a right edge may be any number of levels up, past ancestors cut the other way and past ancestors cut the same way that this subtree happens to sit on the far side of.

None when the walk reaches the root having found no such branch, which means exactly one thing: the window is flush against its container on that side, and there is no seam there to move.

Source

fn node_box( &self, wanted: usize, area: Rect, settings: Settings, ) -> Option<Rect>

The rectangle a node occupies, branches included.

Source

pub fn resize( &mut self, id: u64, axis: Axis, by: f64, area: Rect, settings: Settings, )

Grow a window along axis by a fraction, moving one seam. Everything on the far side stays put.

The keyboard path. It takes an axis and not an Edge because a keypress names neither side: super+equal means “make this wider”, and which of the two seams beside it gives up the space is not something the user expressed. A drag is the opposite — the hand is on one specific edge — which is why the two take different arguments.

So it prefers the trailing seam (right, or below) and falls back to the leading one, with the sign flipped so that positive by still grows the window either way. Only a window with no seam on either side — which on that axis means the only window on the screen — does nothing.

This used to adjust the immediate parent and nothing else, which is the same shortcut Self::drag_seam made: in an ordinary four-window dwindle every leaf’s parent is a horizontal split, so the centre vertical seam could not be reached from the keyboard at all and super+equal silently changed the height instead.

Bounded as a drag is, by Self::room: a press does not make a tile smaller than Settings::minimum, nor one already under it any smaller. That is why this takes the area and the settings, which it did not need before #134 – the floor is in pixels, and the seam’s box is what turns pixels into a ratio.

Source

pub fn layout(&self, area: Rect, settings: Settings) -> Vec<(u64, Rect)>

Where every window goes.

Each split at the ratio it holds, unless a window beside it is under its own floor – see the module note and shared.

Source

fn place( &self, index: usize, rect: Rect, settings: Settings, out: &mut Vec<(u64, Rect)>, )

Source

fn push(&mut self, node: Node) -> usize

Source

fn leaf(&self, id: u64) -> Option<usize>

Source

fn id_of(&self, index: usize) -> Option<u64>

Source

fn leaf_at( &self, at: Option<(f64, f64)>, boxes: &[(u64, Rect)], ) -> Option<usize>

The window a point lands in.

Source

fn closest_leaf( &self, at: Option<(f64, f64)>, boxes: &[(u64, Rect)], ) -> Option<usize>

The window nearest a point, by distance from its centre.

Used when the pointer is over no window at all — off the edge, or on a gap. There is always a window to split; the question is only which.

Source

fn parent(&self, index: usize) -> Option<usize>

Source

fn replace(&mut self, what: usize, with: usize)

Put with wherever what was.

Source

fn attach(&mut self, parent: Option<usize>, what: usize, with: usize)

Put with where what hung, given the parent it hung from.

Taking the parent as an argument rather than looking it up is what makes this safe to call once the replacement already references the thing being replaced.

Source§

impl Tiling

Source

fn room_in( &self, rect: Rect, old: usize, new: u64, at: Option<(f64, f64)>, settings: Settings, ) -> Option<Axis>

Which way rect, window old’s tile, can be split to add window new with neither half under Settings::minimum or under its own window’s floor: across its longer side if that has room, else across the other, else neither.

Measured with cut itself, at the configured split and gap, so “has room” means exactly what the layout will then draw – and since #115 at the ratio shared moves that split to, which is what the layout will draw when one of the two has a floor of its own. at puts the new window on the side Self::split will, since a split that is not at one half is not the same split mirrored. Both halves on both sides: a split across the width leaves the height alone, and a tile that is already too short does not become tall enough by being divided.

Neither window with a floor of its own is the check #134 made, to the bit: the ratio is the split, and each half is held to the minimum.

Trait Implementations§

Source§

impl Clone for Tiling

Source§

fn clone(&self) -> Tiling

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Tiling

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Tiling

Source§

fn default() -> Tiling

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.