Skip to content

Commit 3b59425

Browse files
committed
Add windows resize feature
Co-authored-by: Termina94
1 parent 7c98b1c commit 3b59425

File tree

4 files changed

+238
-11
lines changed

4 files changed

+238
-11
lines changed

helix-term/src/commands.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use helix_view::{
3737
info::Info,
3838
input::KeyEvent,
3939
keyboard::KeyCode,
40-
tree,
40+
tree::{self, Dimension, Resize},
4141
view::View,
4242
Document, DocumentId, Editor, ViewId,
4343
};
@@ -347,6 +347,11 @@ impl MappableCommand {
347347
goto_last_diag, "Goto last diagnostic",
348348
goto_next_diag, "Goto next diagnostic",
349349
goto_prev_diag, "Goto previous diagnostic",
350+
grow_buffer_width, "Grow focused container width",
351+
shrink_buffer_width, "Shrink focused container width",
352+
grow_buffer_height, "Grow focused container height",
353+
shrink_buffer_height, "Shrink focused container height",
354+
toggle_focus_window, "Toggle focus mode on buffer",
350355
goto_next_change, "Goto next change",
351356
goto_prev_change, "Goto previous change",
352357
goto_first_change, "Goto first change",
@@ -3360,6 +3365,25 @@ fn goto_first_change_impl(cx: &mut Context, reverse: bool) {
33603365
}
33613366
}
33623367

3368+
fn grow_buffer_width(cx: &mut Context) {
3369+
cx.editor.resize_buffer(Resize::Grow, Dimension::Width);
3370+
}
3371+
3372+
fn shrink_buffer_width(cx: &mut Context) {
3373+
cx.editor.resize_buffer(Resize::Shrink, Dimension::Width);
3374+
}
3375+
fn grow_buffer_height(cx: &mut Context) {
3376+
cx.editor.resize_buffer(Resize::Grow, Dimension::Height);
3377+
}
3378+
3379+
fn shrink_buffer_height(cx: &mut Context) {
3380+
cx.editor.resize_buffer(Resize::Shrink, Dimension::Height);
3381+
}
3382+
3383+
fn toggle_focus_window(cx: &mut Context) {
3384+
cx.editor.toggle_focus_window();
3385+
}
3386+
33633387
fn goto_next_change(cx: &mut Context) {
33643388
goto_next_change_impl(cx, Direction::Forward)
33653389
}

helix-term/src/keymap/default.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,23 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
204204
},
205205
},
206206

207+
"A-w" => { "Alter Window"
208+
"A-h" | "A-left" | "h"|"left" => shrink_buffer_width,
209+
"A-l" | "A-right" | "l"|"right" => grow_buffer_width,
210+
"A-j" | "A-down" | "j"|"down" => shrink_buffer_height,
211+
"A-k" | "A-up" | "k"|"up" => grow_buffer_height,
212+
"A-f" => toggle_focus_window,
213+
},
214+
215+
216+
"A-W" => { "Alter Window" sticky=true
217+
"h" | "left" => shrink_buffer_width,
218+
"l" | "right" => grow_buffer_width,
219+
"j" | "down" => shrink_buffer_height,
220+
"k" | "up" => grow_buffer_height,
221+
"f" => toggle_focus_window,
222+
},
223+
207224
// move under <space>c
208225
"C-c" => toggle_comments,
209226

helix-view/src/editor.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
input::KeyEvent,
77
register::Registers,
88
theme::{self, Theme},
9-
tree::{self, Tree},
9+
tree::{self, Dimension, Resize, Tree},
1010
view::ViewPosition,
1111
Align, Document, DocumentId, View, ViewId,
1212
};
@@ -1645,6 +1645,14 @@ impl Editor {
16451645
self.tree.transpose();
16461646
}
16471647

1648+
pub fn resize_buffer(&mut self, resize_type: Resize, dimension: Dimension) {
1649+
self.tree.resize_buffer(resize_type, dimension);
1650+
}
1651+
1652+
pub fn toggle_focus_window(&mut self) {
1653+
self.tree.toggle_focus_window();
1654+
}
1655+
16481656
pub fn should_close(&self) -> bool {
16491657
self.tree.is_empty()
16501658
}

helix-view/src/tree.rs

Lines changed: 187 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ pub enum Content {
2929
Container(Box<Container>),
3030
}
3131

32+
pub enum Resize {
33+
Shrink,
34+
Grow,
35+
}
36+
37+
pub enum Dimension {
38+
Width,
39+
Height,
40+
}
41+
3242
impl Node {
3343
pub fn container(layout: Layout) -> Self {
3444
Self {
@@ -65,6 +75,14 @@ pub struct Container {
6575
layout: Layout,
6676
children: Vec<ViewId>,
6777
area: Rect,
78+
node_bounds: Vec<ContainerBounds>,
79+
}
80+
81+
#[derive(Debug, Clone, Copy)]
82+
pub struct ContainerBounds {
83+
width: usize,
84+
height: usize,
85+
expand: bool,
6886
}
6987

7088
impl Container {
@@ -73,8 +91,80 @@ impl Container {
7391
layout,
7492
children: Vec::new(),
7593
area: Rect::default(),
94+
node_bounds: Vec::new(),
7695
}
7796
}
97+
98+
fn get_child_by_view_id(&mut self, node: ViewId) -> Option<&mut ContainerBounds> {
99+
self.children
100+
.iter()
101+
.position(|child| child == &node)
102+
.and_then(|index| self.node_bounds.get_mut(index))
103+
}
104+
105+
fn push_child(&mut self, node: ViewId) -> &mut Self {
106+
self.children.push(node);
107+
self.add_child_bounds();
108+
self
109+
}
110+
111+
fn insert_child(&mut self, index: usize, node: ViewId) -> &mut Self {
112+
self.children.insert(index, node);
113+
self.insert_child_bounds(index);
114+
self
115+
}
116+
117+
fn remove_child(&mut self, index: usize) -> &mut Self {
118+
self.children.remove(index);
119+
self.remove_child_bounds(index);
120+
self
121+
}
122+
123+
fn add_child_bounds(&mut self) -> &mut Self {
124+
self.node_bounds.push(ContainerBounds {
125+
width: 10,
126+
height: 10,
127+
expand: false,
128+
});
129+
self
130+
}
131+
132+
fn insert_child_bounds(&mut self, index: usize) -> &mut Self {
133+
self.node_bounds.insert(
134+
index,
135+
ContainerBounds {
136+
width: 10,
137+
height: 10,
138+
expand: false,
139+
},
140+
);
141+
self
142+
}
143+
144+
fn remove_child_bounds(&mut self, index: usize) -> &mut Self {
145+
self.node_bounds.remove(index);
146+
self
147+
}
148+
149+
fn calculate_slots_width(&self) -> usize {
150+
self.node_bounds
151+
.iter()
152+
.map(|bounds| match bounds.expand {
153+
true => 40,
154+
false => bounds.width,
155+
})
156+
.sum()
157+
}
158+
159+
fn calculate_slots_height(&self) -> usize {
160+
self.node_bounds
161+
.iter()
162+
.map(|bounds| match bounds.expand {
163+
true => 40,
164+
false => bounds.height,
165+
})
166+
.sum()
167+
}
78168
}
79169

80170
impl Default for Container {
@@ -131,7 +221,7 @@ impl Tree {
131221
pos + 1
132222
};
133223

134-
container.children.insert(pos, node);
224+
container.insert_child(pos, node);
135225
// focus the new node
136226
self.focus = node;
137227

@@ -168,7 +258,7 @@ impl Tree {
168258
.unwrap();
169259
pos + 1
170260
};
171-
container.children.insert(pos, node);
261+
container.insert_child(pos, node);
172262
self.nodes[node].parent = parent;
173263
} else {
174264
let mut split = Node::container(layout);
@@ -182,8 +272,8 @@ impl Tree {
182272
} => container,
183273
_ => unreachable!(),
184274
};
185-
container.children.push(focus);
186-
container.children.push(node);
275+
container.push_child(focus);
276+
container.push_child(node);
187277
self.nodes[focus].parent = split;
188278
self.nodes[node].parent = split;
189279

@@ -232,7 +322,7 @@ impl Tree {
232322
} = &mut self.nodes[parent_id]
233323
{
234324
if let Some(pos) = container.children.iter().position(|&child| child == index) {
235-
container.children.remove(pos);
325+
container.remove_child(pos);
236326
// TODO: if container now only has one child, remove it and place child in parent
237327
if container.children.is_empty() && parent_id != self.root {
238328
// if container now empty, remove it
@@ -360,11 +450,17 @@ impl Tree {
360450
Layout::Horizontal => {
361451
let len = container.children.len();
362452

363-
let height = area.height / len as u16;
453+
let slots = container.calculate_slots_height();
454+
let slot_height = area.height as f32 / slots as f32;
364455

365456
let mut child_y = area.y;
366457

367458
for (i, child) in container.children.iter().enumerate() {
459+
let bounds = container.node_bounds[i];
460+
let height = match bounds.expand {
461+
true => (40.0 * slot_height) as u16,
462+
false => (slot_height * bounds.height as f32).floor() as u16,
463+
};
368464
let mut area = Rect::new(
369465
container.area.x,
370466
child_y,
@@ -373,7 +469,7 @@ impl Tree {
373469
);
374470
child_y += height;
375471

376-
// last child takes the remaining width because we can get uneven
472+
// last child takes the remaining height because we can get uneven
377473
// space from rounding
378474
if i == len - 1 {
379475
area.height = container.area.y + container.area.height - area.y;
@@ -385,14 +481,19 @@ impl Tree {
385481
Layout::Vertical => {
386482
let len = container.children.len();
387483

388-
let width = area.width / len as u16;
389-
484+
let slots = container.calculate_slots_width();
485+
let slot_width: f32 = area.width as f32 / slots as f32;
390486
let inner_gap = 1u16;
391487
// let total_gap = inner_gap * (len as u16 - 1);
392488

393489
let mut child_x = area.x;
394490

395491
for (i, child) in container.children.iter().enumerate() {
492+
let bounds = container.node_bounds[i];
493+
let width = match bounds.expand {
494+
true => (40.0 * slot_width) as u16,
495+
false => (slot_width * bounds.width as f32).floor() as u16,
496+
};
396497
let mut area = Rect::new(
397498
child_x,
398499
container.area.y,
@@ -571,6 +672,83 @@ impl Tree {
571672
}
572673
}
573674

675+
fn get_active_node_bounds_mut(
676+
&mut self,
677+
expect_layout: Layout,
678+
) -> Option<&mut ContainerBounds> {
679+
let mut focus = self.focus;
680+
let mut parent = self.nodes[focus].parent;
681+
682+
// Parent expected to be container
683+
if let Some(focused_layout) = match &self.nodes[parent].content {
684+
Content::View(_) => unreachable!(),
685+
Content::Container(node) => Some(node.layout),
686+
} {
687+
// if we want to make a width change and we have a `Horizontal` layout focused,
688+
// alter the parent `Vertical` layout instead and vice versa
689+
if focused_layout != expect_layout {
690+
focus = parent;
691+
parent = self.nodes[parent].parent;
692+
}
693+
694+
if let Content::Container(node) = &mut self.nodes[parent].content {
695+
return node.as_mut().get_child_by_view_id(focus);
696+
};
697+
}
698+
None
699+
}
700+
701+
pub fn resize_buffer(&mut self, resize_type: Resize, dimension: Dimension) {
702+
match dimension {
703+
Dimension::Width => {
704+
if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Vertical) {
705+
match resize_type {
706+
Resize::Shrink => {
707+
if bounds.width > 1 {
708+
bounds.width -= 1;
709+
}
710+
}
711+
Resize::Grow => {
712+
if bounds.width < 20 {
713+
bounds.width += 1;
714+
}
715+
}
716+
};
717+
self.recalculate();
718+
}
719+
}
720+
Dimension::Height => {
721+
if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Horizontal) {
722+
match resize_type {
723+
Resize::Shrink => {
724+
if bounds.height > 1 {
725+
bounds.height -= 1;
726+
}
727+
}
728+
Resize::Grow => {
729+
if bounds.height < 20 {
730+
bounds.height += 1;
731+
}
732+
}
733+
};
734+
self.recalculate();
735+
}
736+
}
737+
}
738+
}
739+
740+
pub fn toggle_focus_window(&mut self) {
741+
if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Horizontal) {
742+
bounds.expand = !bounds.expand;
743+
}
744+
745+
if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Vertical) {
746+
bounds.expand = !bounds.expand;
747+
}
748+
749+
self.recalculate();
750+
}
751+
574752
pub fn swap_split_in_direction(&mut self, direction: Direction) -> Option<()> {
575753
let focus = self.focus;
576754
let target = self.find_split_in_direction(focus, direction)?;

0 commit comments

Comments
 (0)