From 4734106fea34a5f9aa8389af23dcbf994ecfe9a9 Mon Sep 17 00:00:00 2001 From: Termina94 Date: Tue, 7 Jun 2022 22:21:50 +0100 Subject: [PATCH 01/15] Add flex resize, focus mode, hard-coded limits for now --- helix-term/src/commands.rs | 25 +++++ helix-term/src/keymap/default.rs | 8 ++ helix-view/src/editor.rs | 20 ++++ helix-view/src/tree.rs | 183 +++++++++++++++++++++++++++++-- 4 files changed, 229 insertions(+), 7 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 9e8cae00c798..c6b5f4f35b85 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -458,6 +458,11 @@ impl MappableCommand { goto_prev_change, "Goto previous change", goto_first_change, "Goto first change", goto_last_change, "Goto last change", + grow_buffer_width, "Grow focused container width", + shrink_buffer_width, "Shrink focused container width", + grow_buffer_height, "Grow focused container height", + shrink_buffer_height, "Shrink focused container height", + buffer_focus_mode, "Enable focus mode on buffer", goto_line_start, "Goto line start", goto_line_end, "Goto line end", goto_column, "Goto column", @@ -906,6 +911,26 @@ fn goto_line_start(cx: &mut Context) { ) } +fn grow_buffer_width(cx: &mut Context) { + cx.editor.grow_buffer_width(); +} + +fn shrink_buffer_width(cx: &mut Context) { + cx.editor.shrink_buffer_width(); +} + +fn grow_buffer_height(cx: &mut Context) { + cx.editor.grow_buffer_height(); +} + +fn shrink_buffer_height(cx: &mut Context) { + cx.editor.shrink_buffer_height(); +} + +fn buffer_focus_mode(cx: &mut Context) { + cx.editor.buffer_focus_mode(); +} + fn goto_next_buffer(cx: &mut Context) { goto_buffer(cx.editor, Direction::Forward, cx.count()); } diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index 849f47c71ed0..a70676b3b01e 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -26,6 +26,14 @@ pub fn default() -> HashMap { "home" => goto_line_start, "end" => goto_line_end, + "A-w" => { "Alter Window" + "A-w"|"A-left" => shrink_buffer_width, + "A-l"|"A-right" => grow_buffer_width, + "A-j"|"A-down" => shrink_buffer_height, + "A-k"|"A-up" => grow_buffer_height, + "A-f" => buffer_focus_mode, + }, + "w" => move_next_word_start, "b" => move_prev_word_start, "e" => move_next_word_end, diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 3e7956802c13..1fbfd3c9ac4e 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -2236,6 +2236,26 @@ impl Editor { self.tree.transpose(); } + pub fn grow_buffer_width(&mut self) { + self.tree.grow_buffer_width(); + } + + pub fn shrink_buffer_width(&mut self) { + self.tree.shrink_buffer_width(); + } + + pub fn grow_buffer_height(&mut self) { + self.tree.grow_buffer_height(); + } + + pub fn shrink_buffer_height(&mut self) { + self.tree.shrink_buffer_height(); + } + + pub fn buffer_focus_mode(&mut self) { + self.tree.buffer_focus_mode(); + } + pub fn should_close(&self) -> bool { self.tree.is_empty() } diff --git a/helix-view/src/tree.rs b/helix-view/src/tree.rs index 2c1ddbd78551..df3902cd3014 100644 --- a/helix-view/src/tree.rs +++ b/helix-view/src/tree.rs @@ -65,6 +65,14 @@ pub struct Container { layout: Layout, children: Vec, area: Rect, + node_bounds: Vec, +} + +#[derive(Debug, Clone, Copy)] +pub struct ContainerBounds { + width: usize, + height: usize, + focus: bool, } impl Container { @@ -73,8 +81,80 @@ impl Container { layout, children: Vec::new(), area: Rect::default(), + node_bounds: Vec::new(), } } + + fn get_child_by_view_id(&mut self, node: ViewId) -> Option<&mut ContainerBounds> { + if let Some(index) = self.children.iter().position(|child| child == &node) { + return self.node_bounds.get_mut(index); + }; + None + } + + fn push_child(&mut self, node: ViewId) -> &mut Self { + self.children.push(node); + self.add_child_bounds(); + self + } + + fn insert_child(&mut self, index: usize, node: ViewId) -> &mut Self { + self.children.insert(index, node); + self.insert_child_bounds(index); + self + } + + fn remove_child(&mut self, index: usize) -> &mut Self { + self.children.remove(index); + self.remove_child_bounds(index); + self + } + + fn add_child_bounds(&mut self) -> &mut Self { + self.node_bounds.push(ContainerBounds { + width: 10, + height: 10, + focus: false, + }); + self + } + + fn insert_child_bounds(&mut self, index: usize) -> &mut Self { + self.node_bounds.insert( + index, + ContainerBounds { + width: 10, + height: 10, + focus: false, + }, + ); + self + } + + fn remove_child_bounds(&mut self, index: usize) -> &mut Self { + self.node_bounds.remove(index); + self + } + + fn calculate_slots_width(&self) -> usize { + self.node_bounds + .iter() + .map(|bounds| match bounds.focus { + true => 40, + false => bounds.width, + }) + .sum() + } + + fn calculate_slots_height(&self) -> usize { + self.node_bounds + .iter() + .map(|bounds| match bounds.focus { + true => 40, + false => bounds.height, + }) + .sum() + } } impl Default for Container { @@ -131,7 +211,7 @@ impl Tree { pos + 1 }; - container.children.insert(pos, node); + container.insert_child(pos, node); // focus the new node self.focus = node; @@ -168,7 +248,7 @@ impl Tree { .unwrap(); pos + 1 }; - container.children.insert(pos, node); + container.insert_child(pos, node); self.nodes[node].parent = parent; } else { let mut split = Node::container(layout); @@ -182,8 +262,8 @@ impl Tree { } => container, _ => unreachable!(), }; - container.children.push(focus); - container.children.push(node); + container.push_child(focus); + container.push_child(node); self.nodes[focus].parent = split; self.nodes[node].parent = split; @@ -383,11 +463,18 @@ impl Tree { Layout::Horizontal => { let len = container.children.len(); - let height = area.height / len as u16; + let slots = container.calculate_slots_height(); + let slot_height = area.height as f32 / slots as f32; let mut child_y = area.y; for (i, child) in container.children.iter().enumerate() { + let bounds = container.node_bounds[i]; + let height = match bounds.focus { + true => (40.0 * slot_height) as u16, + false => (slot_height * bounds.height as f32).floor() as u16, + }; + let mut area = Rect::new( container.area.x, child_y, @@ -396,7 +483,7 @@ impl Tree { ); child_y += height; - // last child takes the remaining width because we can get uneven + // last child takes the remaining height because we can get uneven // space from rounding if i == len - 1 { area.height = container.area.y + container.area.height - area.y; @@ -413,11 +500,19 @@ impl Tree { let total_gap = inner_gap * len_u16.saturating_sub(2); let used_area = area.width.saturating_sub(total_gap); - let width = used_area / len_u16; + + let slots = container.calculate_slots_width(); + let slot_width: f32 = used_area as f32 / slots as f32; let mut child_x = area.x; for (i, child) in container.children.iter().enumerate() { + let bounds = container.node_bounds[i]; + let width = match bounds.focus { + true => (40.0 * slot_width) as u16, + false => (slot_width * bounds.width as f32).floor() as u16, + }; + let mut area = Rect::new( child_x, container.area.y, @@ -596,6 +691,80 @@ impl Tree { } } + fn get_active_node_bounds_mut( + &mut self, + expect_layout: Layout, + ) -> Option<&mut ContainerBounds> { + let mut focus = self.focus; + let mut parent = self.nodes[focus].parent; + + // Parent expected to be container + if let Some(focused_layout) = match &self.nodes[parent].content { + Content::View(_) => unreachable!(), + Content::Container(node) => Some(node.layout), + } { + // if we want to make a width change and we have a `Horizontal` layout focused, + // alter the parent `Vertical` layout instead and vice versa + if focused_layout != expect_layout { + focus = parent; + parent = self.nodes[parent].parent; + } + + if let Content::Container(node) = &mut self.nodes[parent].content { + return node.as_mut().get_child_by_view_id(focus); + }; + } + None + } + + pub fn grow_buffer_width(&mut self) { + if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Vertical) { + if bounds.width < 20 { + bounds.width += 1; + self.recalculate(); + } + } + } + + pub fn shrink_buffer_width(&mut self) { + if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Vertical) { + if bounds.width > 1 { + bounds.width -= 1; + self.recalculate(); + } + } + } + + pub fn grow_buffer_height(&mut self) { + if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Horizontal) { + if bounds.height < 20 { + bounds.height += 1; + self.recalculate(); + } + } + } + + pub fn shrink_buffer_height(&mut self) { + if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Horizontal) { + if bounds.height > 1 { + bounds.height -= 1; + self.recalculate(); + } + } + } + + pub fn buffer_focus_mode(&mut self) { + if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Horizontal) { + bounds.focus = !bounds.focus; + } + + if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Vertical) { + bounds.focus = !bounds.focus; + } + + self.recalculate(); + } + pub fn swap_split_in_direction(&mut self, direction: Direction) -> Option<()> { let focus = self.focus; let target = self.find_split_in_direction(focus, direction)?; From 89b343c3501b81f8701f4fc67880972692ab1903 Mon Sep 17 00:00:00 2001 From: Termina94 Date: Tue, 12 Jul 2022 21:56:52 +0100 Subject: [PATCH 02/15] refactor buffer resize to single function --- helix-view/src/editor.rs | 10 +++--- helix-view/src/tree.rs | 75 +++++++++++++++++++++++----------------- 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 1fbfd3c9ac4e..d77cf4f1b705 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -11,7 +11,7 @@ use crate::{ input::KeyEvent, register::Registers, theme::{self, Theme}, - tree::{self, Tree}, + tree::{self, Dimension, Resize, Tree}, Document, DocumentId, View, ViewId, }; use helix_event::dispatch; @@ -2237,19 +2237,19 @@ impl Editor { } pub fn grow_buffer_width(&mut self) { - self.tree.grow_buffer_width(); + self.tree.resize_buffer(Resize::Grow, Dimension::Width); } pub fn shrink_buffer_width(&mut self) { - self.tree.shrink_buffer_width(); + self.tree.resize_buffer(Resize::Shrink, Dimension::Width); } pub fn grow_buffer_height(&mut self) { - self.tree.grow_buffer_height(); + self.tree.resize_buffer(Resize::Grow, Dimension::Height); } pub fn shrink_buffer_height(&mut self) { - self.tree.shrink_buffer_height(); + self.tree.resize_buffer(Resize::Shrink, Dimension::Height); } pub fn buffer_focus_mode(&mut self) { diff --git a/helix-view/src/tree.rs b/helix-view/src/tree.rs index df3902cd3014..82f6b2bb0325 100644 --- a/helix-view/src/tree.rs +++ b/helix-view/src/tree.rs @@ -29,6 +29,16 @@ pub enum Content { Container(Box), } +pub enum Resize { + Shrink, + Grow, +} + +pub enum Dimension { + Width, + Height, +} + impl Node { pub fn container(layout: Layout) -> Self { Self { @@ -717,38 +727,41 @@ impl Tree { None } - pub fn grow_buffer_width(&mut self) { - if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Vertical) { - if bounds.width < 20 { - bounds.width += 1; - self.recalculate(); - } - } - } - - pub fn shrink_buffer_width(&mut self) { - if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Vertical) { - if bounds.width > 1 { - bounds.width -= 1; - self.recalculate(); - } - } - } - - pub fn grow_buffer_height(&mut self) { - if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Horizontal) { - if bounds.height < 20 { - bounds.height += 1; - self.recalculate(); + pub fn resize_buffer(&mut self, resize_type: Resize, dimension: Dimension) { + match dimension { + Dimension::Width => { + if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Vertical) { + match resize_type { + Resize::Shrink => { + if bounds.width > 1 { + bounds.width -= 1; + } + } + Resize::Grow => { + if bounds.width < 20 { + bounds.width += 1; + } + } + }; + self.recalculate(); + } } - } - } - - pub fn shrink_buffer_height(&mut self) { - if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Horizontal) { - if bounds.height > 1 { - bounds.height -= 1; - self.recalculate(); + Dimension::Height => { + if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Horizontal) { + match resize_type { + Resize::Shrink => { + if bounds.height > 1 { + bounds.height -= 1; + } + } + Resize::Grow => { + if bounds.height < 20 { + bounds.height += 1; + } + } + }; + self.recalculate(); + } } } } From dbe86dd9d8dbd55d5a9254f58e9dd67b63cea4d8 Mon Sep 17 00:00:00 2001 From: Termina94 Date: Tue, 12 Jul 2022 22:05:22 +0100 Subject: [PATCH 03/15] rename focus to expand --- helix-term/src/commands.rs | 6 +++--- helix-term/src/keymap/default.rs | 2 +- helix-view/src/editor.rs | 4 ++-- helix-view/src/tree.rs | 20 ++++++++++---------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index c6b5f4f35b85..c137960bcb85 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -462,7 +462,7 @@ impl MappableCommand { shrink_buffer_width, "Shrink focused container width", grow_buffer_height, "Grow focused container height", shrink_buffer_height, "Shrink focused container height", - buffer_focus_mode, "Enable focus mode on buffer", + buffer_expand_mode, "Enable expand mode on buffer", goto_line_start, "Goto line start", goto_line_end, "Goto line end", goto_column, "Goto column", @@ -927,8 +927,8 @@ fn shrink_buffer_height(cx: &mut Context) { cx.editor.shrink_buffer_height(); } -fn buffer_focus_mode(cx: &mut Context) { - cx.editor.buffer_focus_mode(); +fn buffer_expand_mode(cx: &mut Context) { + cx.editor.buffer_expand_mode(); } fn goto_next_buffer(cx: &mut Context) { diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index a70676b3b01e..5e3b59fae2e0 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -31,7 +31,7 @@ pub fn default() -> HashMap { "A-l"|"A-right" => grow_buffer_width, "A-j"|"A-down" => shrink_buffer_height, "A-k"|"A-up" => grow_buffer_height, - "A-f" => buffer_focus_mode, + "A-f" => buffer_expand_mode, }, "w" => move_next_word_start, diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index d77cf4f1b705..0f7be195673e 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -2252,8 +2252,8 @@ impl Editor { self.tree.resize_buffer(Resize::Shrink, Dimension::Height); } - pub fn buffer_focus_mode(&mut self) { - self.tree.buffer_focus_mode(); + pub fn buffer_expand_mode(&mut self) { + self.tree.buffer_expand_mode(); } pub fn should_close(&self) -> bool { diff --git a/helix-view/src/tree.rs b/helix-view/src/tree.rs index 82f6b2bb0325..b54da233eec0 100644 --- a/helix-view/src/tree.rs +++ b/helix-view/src/tree.rs @@ -82,7 +82,7 @@ pub struct Container { pub struct ContainerBounds { width: usize, height: usize, - focus: bool, + expand: bool, } impl Container { @@ -124,7 +124,7 @@ impl Container { self.node_bounds.push(ContainerBounds { width: 10, height: 10, - focus: false, + expand: false, }); self } @@ -135,7 +135,7 @@ impl Container { ContainerBounds { width: 10, height: 10, - focus: false, + expand: false, }, ); self @@ -149,7 +149,7 @@ impl Container { fn calculate_slots_width(&self) -> usize { self.node_bounds .iter() - .map(|bounds| match bounds.focus { + .map(|bounds| match bounds.expand { true => 40, false => bounds.width, }) @@ -159,7 +159,7 @@ impl Container { fn calculate_slots_height(&self) -> usize { self.node_bounds .iter() - .map(|bounds| match bounds.focus { + .map(|bounds| match bounds.expand { true => 40, false => bounds.height, }) @@ -480,7 +480,7 @@ impl Tree { for (i, child) in container.children.iter().enumerate() { let bounds = container.node_bounds[i]; - let height = match bounds.focus { + let height = match bounds.expand { true => (40.0 * slot_height) as u16, false => (slot_height * bounds.height as f32).floor() as u16, }; @@ -518,7 +518,7 @@ impl Tree { for (i, child) in container.children.iter().enumerate() { let bounds = container.node_bounds[i]; - let width = match bounds.focus { + let width = match bounds.expand { true => (40.0 * slot_width) as u16, false => (slot_width * bounds.width as f32).floor() as u16, }; @@ -766,13 +766,13 @@ impl Tree { } } - pub fn buffer_focus_mode(&mut self) { + pub fn buffer_expand_mode(&mut self) { if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Horizontal) { - bounds.focus = !bounds.focus; + bounds.expand = !bounds.expand; } if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Vertical) { - bounds.focus = !bounds.focus; + bounds.expand = !bounds.expand; } self.recalculate(); From 504a5d47497132c403902723e82b598833e275e9 Mon Sep 17 00:00:00 2001 From: Termina94 Date: Tue, 12 Jul 2022 22:33:34 +0100 Subject: [PATCH 04/15] refactor editor resize functions --- helix-term/src/commands.rs | 10 +++++----- helix-view/src/editor.rs | 16 ++-------------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index c137960bcb85..cac3d85fccda 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -52,7 +52,7 @@ use helix_view::{ input::KeyEvent, keyboard::KeyCode, theme::Style, - tree, + tree::{Dimension, Resize}, view::View, Document, DocumentId, Editor, ViewId, }; @@ -912,19 +912,19 @@ fn goto_line_start(cx: &mut Context) { } fn grow_buffer_width(cx: &mut Context) { - cx.editor.grow_buffer_width(); + cx.editor.resize_buffer(Resize::Grow, Dimension::Width); } fn shrink_buffer_width(cx: &mut Context) { - cx.editor.shrink_buffer_width(); + cx.editor.resize_buffer(Resize::Shrink, Dimension::Width); } fn grow_buffer_height(cx: &mut Context) { - cx.editor.grow_buffer_height(); + cx.editor.resize_buffer(Resize::Grow, Dimension::Height); } fn shrink_buffer_height(cx: &mut Context) { - cx.editor.shrink_buffer_height(); + cx.editor.resize_buffer(Resize::Shrink, Dimension::Height); } fn buffer_expand_mode(cx: &mut Context) { diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 0f7be195673e..b45525238046 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -2236,20 +2236,8 @@ impl Editor { self.tree.transpose(); } - pub fn grow_buffer_width(&mut self) { - self.tree.resize_buffer(Resize::Grow, Dimension::Width); - } - - pub fn shrink_buffer_width(&mut self) { - self.tree.resize_buffer(Resize::Shrink, Dimension::Width); - } - - pub fn grow_buffer_height(&mut self) { - self.tree.resize_buffer(Resize::Grow, Dimension::Height); - } - - pub fn shrink_buffer_height(&mut self) { - self.tree.resize_buffer(Resize::Shrink, Dimension::Height); + pub fn resize_buffer(&mut self, resize_type: Resize, dimension: Dimension) { + self.tree.resize_buffer(resize_type, dimension); } pub fn buffer_expand_mode(&mut self) { From 2f0cbee9a7fac806be0e3cf15deea94d11b18a1d Mon Sep 17 00:00:00 2001 From: Termina94 Date: Thu, 21 Jul 2022 19:11:24 +0100 Subject: [PATCH 05/15] add sticky mode for 'alter window' pane --- helix-term/src/keymap/default.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index 5e3b59fae2e0..7adc3ba1ffdd 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -27,13 +27,21 @@ pub fn default() -> HashMap { "end" => goto_line_end, "A-w" => { "Alter Window" - "A-w"|"A-left" => shrink_buffer_width, + "A-h"|"A-left" => shrink_buffer_width, "A-l"|"A-right" => grow_buffer_width, "A-j"|"A-down" => shrink_buffer_height, "A-k"|"A-up" => grow_buffer_height, "A-f" => buffer_expand_mode, }, + "A-W" => { "Alter Window" sticky=true + "h"|"left" => shrink_buffer_width, + "l"|"right" => grow_buffer_width, + "j"|"down" => shrink_buffer_height, + "k"|"up" => grow_buffer_height, + "f" => buffer_expand_mode, + }, + "w" => move_next_word_start, "b" => move_prev_word_start, "e" => move_next_word_end, From 3e2c560aabb8763999a45f71d92f52995fc783f6 Mon Sep 17 00:00:00 2001 From: sander777 Date: Mon, 23 Oct 2023 18:56:00 +0300 Subject: [PATCH 06/15] renaming, some key-mapping fixes, refactoring --- helix-term/src/commands.rs | 8 ++++---- helix-term/src/keymap/default.rs | 12 ++++++------ helix-view/src/editor.rs | 4 ++-- helix-view/src/tree.rs | 10 +++++----- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index cac3d85fccda..a32a7a8ffaac 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -52,7 +52,7 @@ use helix_view::{ input::KeyEvent, keyboard::KeyCode, theme::Style, - tree::{Dimension, Resize}, + tree::{self, Dimension, Resize}, view::View, Document, DocumentId, Editor, ViewId, }; @@ -462,7 +462,7 @@ impl MappableCommand { shrink_buffer_width, "Shrink focused container width", grow_buffer_height, "Grow focused container height", shrink_buffer_height, "Shrink focused container height", - buffer_expand_mode, "Enable expand mode on buffer", + toggle_focus_window, "Toggle focus mode on buffer", goto_line_start, "Goto line start", goto_line_end, "Goto line end", goto_column, "Goto column", @@ -927,8 +927,8 @@ fn shrink_buffer_height(cx: &mut Context) { cx.editor.resize_buffer(Resize::Shrink, Dimension::Height); } -fn buffer_expand_mode(cx: &mut Context) { - cx.editor.buffer_expand_mode(); +fn toggle_focus_window(cx: &mut Context) { + cx.editor.toggle_focus_window(); } fn goto_next_buffer(cx: &mut Context) { diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index 7adc3ba1ffdd..aa6e6f4bdc5d 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -27,11 +27,11 @@ pub fn default() -> HashMap { "end" => goto_line_end, "A-w" => { "Alter Window" - "A-h"|"A-left" => shrink_buffer_width, - "A-l"|"A-right" => grow_buffer_width, - "A-j"|"A-down" => shrink_buffer_height, - "A-k"|"A-up" => grow_buffer_height, - "A-f" => buffer_expand_mode, + "A-h"|"A-left" |"h"|"left" => shrink_buffer_width, + "A-l"|"A-right"|"l"|"right" => grow_buffer_width, + "A-j"|"A-down" |"j"|"down" => shrink_buffer_height, + "A-k"|"A-up" |"k"|"up" => grow_buffer_height, + "A-f"|"f" => toggle_focus_window, }, "A-W" => { "Alter Window" sticky=true @@ -39,7 +39,7 @@ pub fn default() -> HashMap { "l"|"right" => grow_buffer_width, "j"|"down" => shrink_buffer_height, "k"|"up" => grow_buffer_height, - "f" => buffer_expand_mode, + "f" => toggle_focus_window, }, "w" => move_next_word_start, diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index b45525238046..0339bb4d84ea 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -2240,8 +2240,8 @@ impl Editor { self.tree.resize_buffer(resize_type, dimension); } - pub fn buffer_expand_mode(&mut self) { - self.tree.buffer_expand_mode(); + pub fn toggle_focus_window(&mut self) { + self.tree.toggle_focus_window(); } pub fn should_close(&self) -> bool { diff --git a/helix-view/src/tree.rs b/helix-view/src/tree.rs index b54da233eec0..20e7f961d736 100644 --- a/helix-view/src/tree.rs +++ b/helix-view/src/tree.rs @@ -96,10 +96,10 @@ impl Container { } fn get_child_by_view_id(&mut self, node: ViewId) -> Option<&mut ContainerBounds> { - if let Some(index) = self.children.iter().position(|child| child == &node) { - return self.node_bounds.get_mut(index); - }; - None + self.children + .iter() + .position(|child| child == &node) + .and_then(|index| self.node_bounds.get_mut(index)) } fn push_child(&mut self, node: ViewId) -> &mut Self { @@ -766,7 +766,7 @@ impl Tree { } } - pub fn buffer_expand_mode(&mut self) { + pub fn toggle_focus_window(&mut self) { if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Horizontal) { bounds.expand = !bounds.expand; } From 4f90918140a911e7b8db79f05c71311e1e89300a Mon Sep 17 00:00:00 2001 From: Sander Cyber Date: Thu, 4 Apr 2024 23:42:50 +0300 Subject: [PATCH 07/15] fix lints errors --- helix-view/src/tree.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/helix-view/src/tree.rs b/helix-view/src/tree.rs index 20e7f961d736..0637e1232cc2 100644 --- a/helix-view/src/tree.rs +++ b/helix-view/src/tree.rs @@ -114,12 +114,6 @@ impl Container { self } - fn remove_child(&mut self, index: usize) -> &mut Self { - self.children.remove(index); - self.remove_child_bounds(index); - self - } - fn add_child_bounds(&mut self) -> &mut Self { self.node_bounds.push(ContainerBounds { width: 10, @@ -141,11 +135,6 @@ impl Container { self } - fn remove_child_bounds(&mut self, index: usize) -> &mut Self { - self.node_bounds.remove(index); - self - } - fn calculate_slots_width(&self) -> usize { self.node_bounds .iter() From 04f9cf8af720147fbb12bafddcd5f79799af50b9 Mon Sep 17 00:00:00 2001 From: may Date: Wed, 8 Oct 2025 15:41:34 +0200 Subject: [PATCH 08/15] more consistently use view instead of buffer / window --- helix-term/src/commands.rs | 30 +++++++++++++++--------------- helix-term/src/keymap/default.rs | 20 ++++++++++---------- helix-view/src/editor.rs | 8 ++++---- helix-view/src/tree.rs | 4 ++-- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index a32a7a8ffaac..e3f48e796456 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -458,11 +458,11 @@ impl MappableCommand { goto_prev_change, "Goto previous change", goto_first_change, "Goto first change", goto_last_change, "Goto last change", - grow_buffer_width, "Grow focused container width", - shrink_buffer_width, "Shrink focused container width", - grow_buffer_height, "Grow focused container height", - shrink_buffer_height, "Shrink focused container height", - toggle_focus_window, "Toggle focus mode on buffer", + grow_view_width, "Grow focused container width", + shrink_view_width, "Shrink focused container width", + grow_view_height, "Grow focused container height", + shrink_view_height, "Shrink focused container height", + toggle_view_focus, "Toggle focus mode on buffer", goto_line_start, "Goto line start", goto_line_end, "Goto line end", goto_column, "Goto column", @@ -911,24 +911,24 @@ fn goto_line_start(cx: &mut Context) { ) } -fn grow_buffer_width(cx: &mut Context) { - cx.editor.resize_buffer(Resize::Grow, Dimension::Width); +fn grow_view_width(cx: &mut Context) { + cx.editor.resize_view(Resize::Grow, Dimension::Width); } -fn shrink_buffer_width(cx: &mut Context) { - cx.editor.resize_buffer(Resize::Shrink, Dimension::Width); +fn shrink_view_width(cx: &mut Context) { + cx.editor.resize_view(Resize::Shrink, Dimension::Width); } -fn grow_buffer_height(cx: &mut Context) { - cx.editor.resize_buffer(Resize::Grow, Dimension::Height); +fn grow_view_height(cx: &mut Context) { + cx.editor.resize_view(Resize::Grow, Dimension::Height); } -fn shrink_buffer_height(cx: &mut Context) { - cx.editor.resize_buffer(Resize::Shrink, Dimension::Height); +fn shrink_view_height(cx: &mut Context) { + cx.editor.resize_view(Resize::Shrink, Dimension::Height); } -fn toggle_focus_window(cx: &mut Context) { - cx.editor.toggle_focus_window(); +fn toggle_view_focus(cx: &mut Context) { + cx.editor.toggle_view_focus(); } fn goto_next_buffer(cx: &mut Context) { diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index aa6e6f4bdc5d..d91e7bfb91b0 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -27,19 +27,19 @@ pub fn default() -> HashMap { "end" => goto_line_end, "A-w" => { "Alter Window" - "A-h"|"A-left" |"h"|"left" => shrink_buffer_width, - "A-l"|"A-right"|"l"|"right" => grow_buffer_width, - "A-j"|"A-down" |"j"|"down" => shrink_buffer_height, - "A-k"|"A-up" |"k"|"up" => grow_buffer_height, - "A-f"|"f" => toggle_focus_window, + "A-h"|"A-left" |"h"|"left" => shrink_view_width, + "A-l"|"A-right"|"l"|"right" => grow_view_width, + "A-j"|"A-down" |"j"|"down" => shrink_view_height, + "A-k"|"A-up" |"k"|"up" => grow_view_height, + "A-f"|"f" => toggle_view_focus, }, "A-W" => { "Alter Window" sticky=true - "h"|"left" => shrink_buffer_width, - "l"|"right" => grow_buffer_width, - "j"|"down" => shrink_buffer_height, - "k"|"up" => grow_buffer_height, - "f" => toggle_focus_window, + "h"|"left" => shrink_view_width, + "l"|"right" => grow_view_width, + "j"|"down" => shrink_view_height, + "k"|"up" => grow_view_height, + "f" => toggle_view_focus, }, "w" => move_next_word_start, diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 0339bb4d84ea..75ac0411a7ec 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -2236,12 +2236,12 @@ impl Editor { self.tree.transpose(); } - pub fn resize_buffer(&mut self, resize_type: Resize, dimension: Dimension) { - self.tree.resize_buffer(resize_type, dimension); + pub fn resize_view(&mut self, resize_type: Resize, dimension: Dimension) { + self.tree.resize_view(resize_type, dimension); } - pub fn toggle_focus_window(&mut self) { - self.tree.toggle_focus_window(); + pub fn toggle_view_focus(&mut self) { + self.tree.toggle_view_focus(); } pub fn should_close(&self) -> bool { diff --git a/helix-view/src/tree.rs b/helix-view/src/tree.rs index 0637e1232cc2..077dbe83d215 100644 --- a/helix-view/src/tree.rs +++ b/helix-view/src/tree.rs @@ -716,7 +716,7 @@ impl Tree { None } - pub fn resize_buffer(&mut self, resize_type: Resize, dimension: Dimension) { + pub fn resize_view(&mut self, resize_type: Resize, dimension: Dimension) { match dimension { Dimension::Width => { if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Vertical) { @@ -755,7 +755,7 @@ impl Tree { } } - pub fn toggle_focus_window(&mut self) { + pub fn toggle_view_focus(&mut self) { if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Horizontal) { bounds.expand = !bounds.expand; } From ef6729a538b0e579b3a06948d9e70333b37d72df Mon Sep 17 00:00:00 2001 From: may Date: Wed, 8 Oct 2025 15:43:00 +0200 Subject: [PATCH 09/15] move window resize mode to -w menu, remove non-sticky keybinds --- helix-term/src/keymap/default.rs | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index d91e7bfb91b0..af22bc4c572f 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -26,22 +26,6 @@ pub fn default() -> HashMap { "home" => goto_line_start, "end" => goto_line_end, - "A-w" => { "Alter Window" - "A-h"|"A-left" |"h"|"left" => shrink_view_width, - "A-l"|"A-right"|"l"|"right" => grow_view_width, - "A-j"|"A-down" |"j"|"down" => shrink_view_height, - "A-k"|"A-up" |"k"|"up" => grow_view_height, - "A-f"|"f" => toggle_view_focus, - }, - - "A-W" => { "Alter Window" sticky=true - "h"|"left" => shrink_view_width, - "l"|"right" => grow_view_width, - "j"|"down" => shrink_view_height, - "k"|"up" => grow_view_height, - "f" => toggle_view_focus, - }, - "w" => move_next_word_start, "b" => move_prev_word_start, "e" => move_next_word_end, @@ -211,6 +195,12 @@ pub fn default() -> HashMap { "C-s" | "s" => hsplit, "C-v" | "v" => vsplit, "C-t" | "t" => transpose_view, + "r" => { "Window resize mode" sticky=true + "h" | "left" => shrink_view_width, + "l" | "right" => grow_view_width, + "j" | "down" => shrink_view_height, + "k" | "up" => grow_view_height, + }, "f" => goto_file_hsplit, "F" => goto_file_vsplit, "C-q" | "q" => wclose, @@ -278,6 +268,13 @@ pub fn default() -> HashMap { "C-s" | "s" => hsplit, "C-v" | "v" => vsplit, "C-t" | "t" => transpose_view, + "r" => { "Window resize mode" sticky=true + "h" | "left" => shrink_view_width, + "l" | "right" => grow_view_width, + "j" | "down" => shrink_view_height, + "k" | "up" => grow_view_height, + "f" => toggle_view_focus, + }, "f" => goto_file_hsplit, "F" => goto_file_vsplit, "C-q" | "q" => wclose, From a86c4edd625fb5a8235bbafea1a89f55ba33833a Mon Sep 17 00:00:00 2001 From: may Date: Wed, 8 Oct 2025 17:52:10 +0200 Subject: [PATCH 10/15] remodel container bounds to be percentage-based --- helix-view/src/tree.rs | 193 ++++++++++++++++++++++------------------- 1 file changed, 105 insertions(+), 88 deletions(-) diff --git a/helix-view/src/tree.rs b/helix-view/src/tree.rs index 077dbe83d215..ca173856e1b4 100644 --- a/helix-view/src/tree.rs +++ b/helix-view/src/tree.rs @@ -80,9 +80,7 @@ pub struct Container { #[derive(Debug, Clone, Copy)] pub struct ContainerBounds { - width: usize, - height: usize, - expand: bool, + portion: f64, } impl Container { @@ -114,45 +112,50 @@ impl Container { self } + fn pop_child(&mut self) -> Option { + let bound = self.node_bounds.pop()?; + + let prev = 1.0 - bound.portion; + for bound in &mut self.node_bounds { + bound.portion /= prev; + } + + self.children.pop() + } + + fn remove_child(&mut self, index: usize) -> ViewId { + let bound = self.node_bounds.remove(index); + + let prev = 1.0 - bound.portion; + for bound in &mut self.node_bounds { + bound.portion /= prev; + } + + self.children.remove(index) + } + fn add_child_bounds(&mut self) -> &mut Self { - self.node_bounds.push(ContainerBounds { - width: 10, - height: 10, - expand: false, - }); + let portion = 1.0 / self.children.len() as f64; + + let prev = 1.0 - portion; + for bound in &mut self.node_bounds { + bound.portion *= prev; + } + + self.node_bounds.push(ContainerBounds { portion }); self } fn insert_child_bounds(&mut self, index: usize) -> &mut Self { - self.node_bounds.insert( - index, - ContainerBounds { - width: 10, - height: 10, - expand: false, - }, - ); - self - } + let portion = 1.0 / self.children.len() as f64; - fn calculate_slots_width(&self) -> usize { - self.node_bounds - .iter() - .map(|bounds| match bounds.expand { - true => 40, - false => bounds.width, - }) - .sum() - } + let prev = 1.0 - portion; + for bound in &mut self.node_bounds { + bound.portion *= prev; + } - fn calculate_slots_height(&self) -> usize { - self.node_bounds - .iter() - .map(|bounds| match bounds.expand { - true => 40, - false => bounds.height, - }) - .sum() + self.node_bounds.insert(index, ContainerBounds { portion }); + self } } @@ -322,7 +325,7 @@ impl Tree { container.children[pos] = new; self.nodes[new].parent = parent; } else { - container.children.remove(pos); + container.remove_child(pos); } } @@ -341,7 +344,7 @@ impl Tree { if parent_container.children.len() == 1 && !parent_is_root { // Lets merge the only child back to its grandparent so that Views // are equally spaced. - let sibling = parent_container.children.pop().unwrap(); + let sibling = parent_container.pop_child().unwrap(); self.remove_or_replace(parent, Some(sibling)); } @@ -462,17 +465,11 @@ impl Tree { Layout::Horizontal => { let len = container.children.len(); - let slots = container.calculate_slots_height(); - let slot_height = area.height as f32 / slots as f32; - let mut child_y = area.y; for (i, child) in container.children.iter().enumerate() { let bounds = container.node_bounds[i]; - let height = match bounds.expand { - true => (40.0 * slot_height) as u16, - false => (slot_height * bounds.height as f32).floor() as u16, - }; + let height = (area.height as f64 * bounds.portion).floor() as u16; let mut area = Rect::new( container.area.x, @@ -500,17 +497,11 @@ impl Tree { let used_area = area.width.saturating_sub(total_gap); - let slots = container.calculate_slots_width(); - let slot_width: f32 = used_area as f32 / slots as f32; - let mut child_x = area.x; for (i, child) in container.children.iter().enumerate() { let bounds = container.node_bounds[i]; - let width = match bounds.expand { - true => (40.0 * slot_width) as u16, - false => (slot_width * bounds.width as f32).floor() as u16, - }; + let width = (used_area as f64 * bounds.portion).floor() as u16; let mut area = Rect::new( child_x, @@ -716,52 +707,78 @@ impl Tree { None } - pub fn resize_view(&mut self, resize_type: Resize, dimension: Dimension) { - match dimension { - Dimension::Width => { - if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Vertical) { - match resize_type { - Resize::Shrink => { - if bounds.width > 1 { - bounds.width -= 1; - } - } - Resize::Grow => { - if bounds.width < 20 { - bounds.width += 1; - } - } - }; - self.recalculate(); - } + fn find_container(&mut self, layout: Layout) -> Option<(&mut Container, usize)> { + let mut focus = self.focus; + let mut parent = self.nodes[focus].parent; + + let mut found = false; + while parent != focus { + let Content::Container(container) = &self.nodes[parent].content else { + unreachable!("parent should be a container"); + }; + + if container.layout == layout { + found = true; + break; } - Dimension::Height => { - if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Horizontal) { - match resize_type { - Resize::Shrink => { - if bounds.height > 1 { - bounds.height -= 1; - } - } - Resize::Grow => { - if bounds.height < 20 { - bounds.height += 1; - } - } - }; - self.recalculate(); + + focus = parent; + parent = self.nodes[focus].parent; + } + + if found { + let Content::Container(container) = &mut self.nodes[parent].content else { + unreachable!("parent should be a container"); + }; + + let idx = container.children.iter().position(|node| *node == focus)?; + Some((container, idx)) + } else { + None + } + } + + pub fn resize_view(&mut self, resize_type: Resize, dimension: Dimension) { + let layout = match dimension { + Dimension::Width => Layout::Vertical, + Dimension::Height => Layout::Horizontal, + }; + + if let Some((container, idx)) = self.find_container(layout) { + let diff = match dimension { + Dimension::Width => 1.0 / container.area.width as f64 * 2.0, + Dimension::Height => 1.0 / container.area.height as f64 * 2.0, + }; + let diff = match resize_type { + Resize::Shrink => -diff, + Resize::Grow => diff, + }; + + let bounds = &mut container.node_bounds; + bounds[idx].portion += diff; + + if let Some(prev) = idx.checked_sub(1) { + if let Some(next) = bounds.get_mut(idx + 1) { + next.portion -= diff / 2.0; + bounds[prev].portion -= diff / 2.0; + } else { + bounds[prev].portion -= diff; } + } else if let Some(next) = bounds.get_mut(idx + 1) { + next.portion -= diff; } + + self.recalculate(); } } pub fn toggle_view_focus(&mut self) { - if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Horizontal) { - bounds.expand = !bounds.expand; + if let Some(_bounds) = self.get_active_node_bounds_mut(Layout::Horizontal) { + // bounds.expand = !bounds.expand; } - if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Vertical) { - bounds.expand = !bounds.expand; + if let Some(_bounds) = self.get_active_node_bounds_mut(Layout::Vertical) { + // bounds.expand = !bounds.expand; } self.recalculate(); From 04872a76853900677de110a9f3a487e8cf0954fb Mon Sep 17 00:00:00 2001 From: may Date: Wed, 8 Oct 2025 17:59:42 +0200 Subject: [PATCH 11/15] remove toggle_view_focus command this currently does nothing anyway and i am unsure about how to do that in this resizing model anyway. --- helix-term/src/commands.rs | 5 ---- helix-term/src/keymap/default.rs | 1 - helix-view/src/editor.rs | 4 --- helix-view/src/tree.rs | 45 -------------------------------- 4 files changed, 55 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index e3f48e796456..bb24658cd9fd 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -462,7 +462,6 @@ impl MappableCommand { shrink_view_width, "Shrink focused container width", grow_view_height, "Grow focused container height", shrink_view_height, "Shrink focused container height", - toggle_view_focus, "Toggle focus mode on buffer", goto_line_start, "Goto line start", goto_line_end, "Goto line end", goto_column, "Goto column", @@ -927,10 +926,6 @@ fn shrink_view_height(cx: &mut Context) { cx.editor.resize_view(Resize::Shrink, Dimension::Height); } -fn toggle_view_focus(cx: &mut Context) { - cx.editor.toggle_view_focus(); -} - fn goto_next_buffer(cx: &mut Context) { goto_buffer(cx.editor, Direction::Forward, cx.count()); } diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index af22bc4c572f..4413bd4e6320 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -273,7 +273,6 @@ pub fn default() -> HashMap { "l" | "right" => grow_view_width, "j" | "down" => shrink_view_height, "k" | "up" => grow_view_height, - "f" => toggle_view_focus, }, "f" => goto_file_hsplit, "F" => goto_file_vsplit, diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 75ac0411a7ec..63ca38eaca5c 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -2240,10 +2240,6 @@ impl Editor { self.tree.resize_view(resize_type, dimension); } - pub fn toggle_view_focus(&mut self) { - self.tree.toggle_view_focus(); - } - pub fn should_close(&self) -> bool { self.tree.is_empty() } diff --git a/helix-view/src/tree.rs b/helix-view/src/tree.rs index ca173856e1b4..4ea1c4d1833c 100644 --- a/helix-view/src/tree.rs +++ b/helix-view/src/tree.rs @@ -93,13 +93,6 @@ impl Container { } } - fn get_child_by_view_id(&mut self, node: ViewId) -> Option<&mut ContainerBounds> { - self.children - .iter() - .position(|child| child == &node) - .and_then(|index| self.node_bounds.get_mut(index)) - } - fn push_child(&mut self, node: ViewId) -> &mut Self { self.children.push(node); self.add_child_bounds(); @@ -681,32 +674,6 @@ impl Tree { } } - fn get_active_node_bounds_mut( - &mut self, - expect_layout: Layout, - ) -> Option<&mut ContainerBounds> { - let mut focus = self.focus; - let mut parent = self.nodes[focus].parent; - - // Parent expected to be container - if let Some(focused_layout) = match &self.nodes[parent].content { - Content::View(_) => unreachable!(), - Content::Container(node) => Some(node.layout), - } { - // if we want to make a width change and we have a `Horizontal` layout focused, - // alter the parent `Vertical` layout instead and vice versa - if focused_layout != expect_layout { - focus = parent; - parent = self.nodes[parent].parent; - } - - if let Content::Container(node) = &mut self.nodes[parent].content { - return node.as_mut().get_child_by_view_id(focus); - }; - } - None - } - fn find_container(&mut self, layout: Layout) -> Option<(&mut Container, usize)> { let mut focus = self.focus; let mut parent = self.nodes[focus].parent; @@ -772,18 +739,6 @@ impl Tree { } } - pub fn toggle_view_focus(&mut self) { - if let Some(_bounds) = self.get_active_node_bounds_mut(Layout::Horizontal) { - // bounds.expand = !bounds.expand; - } - - if let Some(_bounds) = self.get_active_node_bounds_mut(Layout::Vertical) { - // bounds.expand = !bounds.expand; - } - - self.recalculate(); - } - pub fn swap_split_in_direction(&mut self, direction: Direction) -> Option<()> { let focus = self.focus; let target = self.find_split_in_direction(focus, direction)?; From 12e89cc86e21d7413df6e3ffb3882cc20d44e964 Mon Sep 17 00:00:00 2001 From: may Date: Wed, 8 Oct 2025 18:54:49 +0200 Subject: [PATCH 12/15] clamp when resizing views --- helix-view/src/tree.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/helix-view/src/tree.rs b/helix-view/src/tree.rs index 4ea1c4d1833c..364781b1febe 100644 --- a/helix-view/src/tree.rs +++ b/helix-view/src/tree.rs @@ -712,6 +712,10 @@ impl Tree { }; if let Some((container, idx)) = self.find_container(layout) { + if container.children.len() == 1 { + return; + } + let diff = match dimension { Dimension::Width => 1.0 / container.area.width as f64 * 2.0, Dimension::Height => 1.0 / container.area.height as f64 * 2.0, @@ -722,8 +726,17 @@ impl Tree { }; let bounds = &mut container.node_bounds; - bounds[idx].portion += diff; + let min = 0.05; + let max = 1.0 - ((container.children.len() - 1) as f64 * min); + if bounds[idx].portion <= min && bounds[idx].portion >= max { + return; + } + + let portion = bounds[idx].portion; + bounds[idx].portion = f64::clamp(portion + diff, min, max); + + let diff = bounds[idx].portion - portion; if let Some(prev) = idx.checked_sub(1) { if let Some(next) = bounds.get_mut(idx + 1) { next.portion -= diff / 2.0; From 8d4c7019b2046f20f83bf2f9fa030de07c534ba1 Mon Sep 17 00:00:00 2001 From: may Date: Wed, 8 Oct 2025 22:16:58 +0200 Subject: [PATCH 13/15] use cx.count when resizing a view --- helix-term/src/commands.rs | 12 ++++++++---- helix-view/src/editor.rs | 4 ++-- helix-view/src/tree.rs | 3 ++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index bb24658cd9fd..0011ff207d6a 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -911,19 +911,23 @@ fn goto_line_start(cx: &mut Context) { } fn grow_view_width(cx: &mut Context) { - cx.editor.resize_view(Resize::Grow, Dimension::Width); + cx.editor + .resize_view(Resize::Grow, Dimension::Width, cx.count()); } fn shrink_view_width(cx: &mut Context) { - cx.editor.resize_view(Resize::Shrink, Dimension::Width); + cx.editor + .resize_view(Resize::Shrink, Dimension::Width, cx.count()); } fn grow_view_height(cx: &mut Context) { - cx.editor.resize_view(Resize::Grow, Dimension::Height); + cx.editor + .resize_view(Resize::Grow, Dimension::Height, cx.count()); } fn shrink_view_height(cx: &mut Context) { - cx.editor.resize_view(Resize::Shrink, Dimension::Height); + cx.editor + .resize_view(Resize::Shrink, Dimension::Height, cx.count()); } fn goto_next_buffer(cx: &mut Context) { diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 63ca38eaca5c..e0e536589eec 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -2236,8 +2236,8 @@ impl Editor { self.tree.transpose(); } - pub fn resize_view(&mut self, resize_type: Resize, dimension: Dimension) { - self.tree.resize_view(resize_type, dimension); + pub fn resize_view(&mut self, resize_type: Resize, dimension: Dimension, count: usize) { + self.tree.resize_view(resize_type, dimension, count); } pub fn should_close(&self) -> bool { diff --git a/helix-view/src/tree.rs b/helix-view/src/tree.rs index 364781b1febe..166991404a1f 100644 --- a/helix-view/src/tree.rs +++ b/helix-view/src/tree.rs @@ -705,7 +705,7 @@ impl Tree { } } - pub fn resize_view(&mut self, resize_type: Resize, dimension: Dimension) { + pub fn resize_view(&mut self, resize_type: Resize, dimension: Dimension, count: usize) { let layout = match dimension { Dimension::Width => Layout::Vertical, Dimension::Height => Layout::Horizontal, @@ -724,6 +724,7 @@ impl Tree { Resize::Shrink => -diff, Resize::Grow => diff, }; + let diff = diff * count as f64; let bounds = &mut container.node_bounds; From 38c62c6069e3a9e90ba3894a0e5e0abdcac123c5 Mon Sep 17 00:00:00 2001 From: may Date: Wed, 8 Oct 2025 19:12:10 +0200 Subject: [PATCH 14/15] refactor: move children and bounds into a Children struct this makes it easier to keep bounds and views in sync and makes it harder to accidentally modify the views without modifying the bounds and vice-versa. --- helix-view/src/tree.rs | 151 ++++++++++++++++++++++++++--------------- 1 file changed, 95 insertions(+), 56 deletions(-) diff --git a/helix-view/src/tree.rs b/helix-view/src/tree.rs index 166991404a1f..04ea7e074cf3 100644 --- a/helix-view/src/tree.rs +++ b/helix-view/src/tree.rs @@ -1,5 +1,6 @@ use crate::{graphics::Rect, View, ViewId}; use slotmap::SlotMap; +use std::ops::{Index, IndexMut}; // the dimensions are recomputed on window resize/tree change. // @@ -73,82 +74,120 @@ pub enum Direction { #[derive(Debug)] pub struct Container { layout: Layout, - children: Vec, + children: Children, area: Rect, - node_bounds: Vec, } -#[derive(Debug, Clone, Copy)] -pub struct ContainerBounds { - portion: f64, +#[derive(Debug)] +struct Children { + pub views: Vec, + pub bounds: Vec, } -impl Container { - pub fn new(layout: Layout) -> Self { - Self { - layout, - children: Vec::new(), - area: Rect::default(), - node_bounds: Vec::new(), +impl Children { + fn new() -> Self { + Children { + views: Vec::new(), + bounds: Vec::new(), } } - fn push_child(&mut self, node: ViewId) -> &mut Self { - self.children.push(node); - self.add_child_bounds(); - self + pub fn iter(&self) -> std::slice::Iter<'_, ViewId> { + self.views.iter() } - fn insert_child(&mut self, index: usize, node: ViewId) -> &mut Self { - self.children.insert(index, node); - self.insert_child_bounds(index); - self + pub fn len(&self) -> usize { + debug_assert_eq!(self.views.len(), self.bounds.len()); + self.views.len() } - fn pop_child(&mut self) -> Option { - let bound = self.node_bounds.pop()?; + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} - let prev = 1.0 - bound.portion; - for bound in &mut self.node_bounds { - bound.portion /= prev; +impl Children { + fn push(&mut self, node: ViewId) { + self.views.push(node); + + let portion = 1.0 / self.views.len() as f64; + + let prev = 1.0 - portion; + for bound in &mut self.bounds { + bound.portion *= prev; + } + + self.bounds.push(ContainerBounds { portion }); + } + + fn insert(&mut self, index: usize, node: ViewId) { + self.views.insert(index, node); + + let portion = 1.0 / self.views.len() as f64; + + let prev = 1.0 - portion; + for bound in &mut self.bounds { + bound.portion *= prev; } - self.children.pop() + self.bounds.insert(index, ContainerBounds { portion }); } - fn remove_child(&mut self, index: usize) -> ViewId { - let bound = self.node_bounds.remove(index); + fn pop(&mut self) -> Option { + let bound = self.bounds.pop()?; let prev = 1.0 - bound.portion; - for bound in &mut self.node_bounds { + for bound in &mut self.bounds { bound.portion /= prev; } - self.children.remove(index) + self.views.pop() } - fn add_child_bounds(&mut self) -> &mut Self { - let portion = 1.0 / self.children.len() as f64; + fn remove(&mut self, index: usize) -> ViewId { + let bound = self.bounds.remove(index); - let prev = 1.0 - portion; - for bound in &mut self.node_bounds { - bound.portion *= prev; + let prev = 1.0 - bound.portion; + for bound in &mut self.bounds { + bound.portion /= prev; } - self.node_bounds.push(ContainerBounds { portion }); - self + self.views.remove(index) } +} - fn insert_child_bounds(&mut self, index: usize) -> &mut Self { - let portion = 1.0 / self.children.len() as f64; +impl Index for Children +where + Idx: std::slice::SliceIndex<[ViewId]>, +{ + type Output = Idx::Output; + fn index(&self, index: Idx) -> &Self::Output { + self.views.index(index) + } +} - let prev = 1.0 - portion; - for bound in &mut self.node_bounds { - bound.portion *= prev; - } +impl IndexMut for Children +where + Idx: std::slice::SliceIndex<[ViewId]>, +{ + #[inline(always)] + fn index_mut(&mut self, index: Idx) -> &mut Self::Output { + self.views.index_mut(index) + } +} - self.node_bounds.insert(index, ContainerBounds { portion }); - self +#[derive(Debug, Clone, Copy)] +pub struct ContainerBounds { + portion: f64, +} + +impl Container { + pub fn new(layout: Layout) -> Self { + Self { + layout, + children: Children::new(), + area: Rect::default(), + } } } @@ -206,7 +245,7 @@ impl Tree { pos + 1 }; - container.insert_child(pos, node); + container.children.insert(pos, node); // focus the new node self.focus = node; @@ -243,7 +282,7 @@ impl Tree { .unwrap(); pos + 1 }; - container.insert_child(pos, node); + container.children.insert(pos, node); self.nodes[node].parent = parent; } else { let mut split = Node::container(layout); @@ -257,8 +296,8 @@ impl Tree { } => container, _ => unreachable!(), }; - container.push_child(focus); - container.push_child(node); + container.children.push(focus); + container.children.push(node); self.nodes[focus].parent = split; self.nodes[node].parent = split; @@ -318,7 +357,7 @@ impl Tree { container.children[pos] = new; self.nodes[new].parent = parent; } else { - container.remove_child(pos); + container.children.remove(pos); } } @@ -337,7 +376,7 @@ impl Tree { if parent_container.children.len() == 1 && !parent_is_root { // Lets merge the only child back to its grandparent so that Views // are equally spaced. - let sibling = parent_container.pop_child().unwrap(); + let sibling = parent_container.children.pop().unwrap(); self.remove_or_replace(parent, Some(sibling)); } @@ -461,7 +500,7 @@ impl Tree { let mut child_y = area.y; for (i, child) in container.children.iter().enumerate() { - let bounds = container.node_bounds[i]; + let bounds = container.children.bounds[i]; let height = (area.height as f64 * bounds.portion).floor() as u16; let mut area = Rect::new( @@ -493,7 +532,7 @@ impl Tree { let mut child_x = area.x; for (i, child) in container.children.iter().enumerate() { - let bounds = container.node_bounds[i]; + let bounds = container.children.bounds[i]; let width = (used_area as f64 * bounds.portion).floor() as u16; let mut area = Rect::new( @@ -553,7 +592,7 @@ impl Tree { // It's possible to move in the desired direction within // the parent container so an attempt is made to find the // correct child. - match self.find_child(id, &parent_container.children, direction) { + match self.find_child(id, &parent_container.children.views, direction) { // Child is found, search is ended Some(id) => Some(id), // A child is not found. This could be because of either two scenarios @@ -726,10 +765,10 @@ impl Tree { }; let diff = diff * count as f64; - let bounds = &mut container.node_bounds; - let min = 0.05; let max = 1.0 - ((container.children.len() - 1) as f64 * min); + + let bounds = &mut container.children.bounds; if bounds[idx].portion <= min && bounds[idx].portion >= max { return; } From 847241eade852e819a74ff75e18394e445e6480c Mon Sep 17 00:00:00 2001 From: may Date: Wed, 8 Oct 2025 23:08:36 +0200 Subject: [PATCH 15/15] cargo xtask docgen --- book/src/generated/static-cmd.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/book/src/generated/static-cmd.md b/book/src/generated/static-cmd.md index 1d831f26eb5f..22d80d1ff6b8 100644 --- a/book/src/generated/static-cmd.md +++ b/book/src/generated/static-cmd.md @@ -155,6 +155,10 @@ | `goto_prev_change` | Goto previous change | normal: `` [g ``, select: `` [g `` | | `goto_first_change` | Goto first change | normal: `` [G ``, select: `` [G `` | | `goto_last_change` | Goto last change | normal: `` ]G ``, select: `` ]G `` | +| `grow_view_width` | Grow focused container width | normal: `` rl ``, `` wrl ``, `` r ``, `` wr ``, select: `` rl ``, `` wrl ``, `` r ``, `` wr `` | +| `shrink_view_width` | Shrink focused container width | normal: `` rh ``, `` wrh ``, `` r ``, `` wr ``, select: `` rh ``, `` wrh ``, `` r ``, `` wr `` | +| `grow_view_height` | Grow focused container height | normal: `` rk ``, `` r ``, `` wrk ``, `` wr ``, select: `` rk ``, `` r ``, `` wrk ``, `` wr `` | +| `shrink_view_height` | Shrink focused container height | normal: `` rj ``, `` wrj ``, `` r ``, `` wr ``, select: `` rj ``, `` wrj ``, `` r ``, `` wr `` | | `goto_line_start` | Goto line start | normal: `` gh ``, `` ``, select: `` gh ``, insert: `` `` | | `goto_line_end` | Goto line end | normal: `` gl ``, `` ``, select: `` gl `` | | `goto_column` | Goto column | normal: `` g\| `` |