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
6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"chat.tools.terminal.autoApprove": {
"git add": true,
"git commit": true
}
}
25 changes: 25 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,28 @@ When configuration is hot-reloaded, only default or unmodified values should be
Even after validation, code that iterates based on external values should have caps. This is defense-in-depth: validation catches expected bad input, caps catch unexpected arithmetic.

When computing resize edges, drag thresholds, or hit testing, consider degenerate geometry. A window narrower than twice the resize threshold can have overlapping left and right edges.

## Scroll layout

The scroll layout is an alternative to the default tree layout, inspired by niri and PaperWM. Instead of subdividing the screen into tiles, columns extend in a horizontal strip and the user scrolls a viewport across them.

The tree supports two layout modes, selected per-space:

- **Tree** – traditional tiling with horizontal/vertical splitting (i3-style)
- **Scroll** – scrollable column layout

Both modes share the same tree structure, weight-based sizing, and selection model. The scroll mode adds a `ViewportState` per layout and routes additional events (scroll wheel, interactive resize/move) through the LayoutManager.

### Viewport and animation

`ViewportState` manages the horizontal scroll offset. It is either static or animating via a `SpringAnimation`. The spring uses a classical damped model with configurable response time and damping fraction (default: critically damped). `retarget()` preserves continuity of position and velocity when the target changes mid-animation, so rapid focus changes feel fluid rather than jerky.

Three centering modes control when the viewport scrolls to keep the focused column visible: `Always`, `OnOverflow`, and `Never`.

After layout calculation, `apply_viewport_to_frames` offsets window positions by the scroll offset and hides off-screen windows by moving them out of view. This function is generic over the window identifier type to keep the model layer free of actor-layer dependencies.

The Reactor drives animation with a timer that fires only when a scroll animation is active.

### Interactive resize and move

The LayoutManager handles interactive resize and move via mouse drag. `detect_edges` determines which edges of the focused window are near the cursor and sets the drag mode. Interactive resize works by converting pixel deltas into weight adjustments on the layout tree.
2 changes: 1 addition & 1 deletion examples/devtool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ fn inspect(mtm: MainThreadMarker) {

async fn inspect_inner(mut rx: UnboundedReceiver<()>, mtm: MainThreadMarker) {
let mut screen_cache = ScreenCache::new(mtm);
let Some((_, _, converter)) = screen_cache.update_screen_config() else {
let Some((_, converter)) = screen_cache.update_screen_config() else {
return;
};
while let Some(()) = rx.recv().await {
Expand Down
42 changes: 42 additions & 0 deletions glide.default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ outer_gap = 0
# Gap between adjacent windows (in pixels).
inner_gap = 0

# The default layout kind for new spaces: "tree" or "scroll".
# Note: "scroll" requires settings.experimental.scroll.enable = true.
default_layout_kind = "tree"

# Visual bars for window groups (tabbed/stacked containers).
group_bars.enable = true
group_bars.thickness = 6
Expand Down Expand Up @@ -139,6 +143,15 @@ default_keys = false
# Print the current layout in the logs.
"Alt + Shift + D" = "debug"

# Scroll layout commands are experimental and intentionally not bound by
# default. If you enable settings.experimental.scroll.enable, add explicit
# keybindings in your personal config under [keys], for example:
# "Alt + Shift + S" = "change_layout_kind"
# "Alt + Shift + T" = "toggle_column_tabbed"
# "Alt + Shift + W" = "cycle_column_width"
# "Alt + Shift + BracketLeft" = { consume_or_expel_window = "left" }
# "Alt + Shift + BracketRight" = { consume_or_expel_window = "right" }

# WARNING:
# This section contains experimental features that might break or be removed in
# the future. Use at your own risk!
Expand All @@ -152,3 +165,32 @@ status_icon.color = false

# Ignored; kept for compatibility.
status_icon.enable = true

# Scroll layout settings.

# Enable the experimental scroll/niri layout and related commands.
scroll.enable = false

# When to center the focused column: "never", "always", or "on_overflow".
scroll.center_focused_column = "never"

# Number of columns visible at once (1-5).
scroll.visible_columns = 2

# Preset column width proportions to cycle through with cycle_column_width.
scroll.column_width_presets = [0.333, 0.5, 0.667, 1.0]

# Where to place new windows: "new_column" or "same_column".
scroll.new_window_in_column = "new_column"

# Scroll sensitivity multiplier for trackpad/mouse wheel.
scroll.scroll_sensitivity = 20.0

# Invert the scroll direction (natural scrolling).
scroll.invert_scroll_direction = false

# Allow focus to wrap around from last column to first and vice versa.
scroll.infinite_loop = false

# Aspect ratio for single-column mode (e.g. "16:9"). Empty string disables.
scroll.single_column_aspect_ratio = ""
Loading
Loading