Refactor new Rect construction (#575)

* Refactor new Rect construction

Introduces methods that can be chained to construct new Rects
out of pre-existing ones

* Clamp x and y to edges in Rect chop methods

* Rename Rect clipping functions
This commit is contained in:
Gokul Soumya 2021-08-21 10:51:20 +05:30 committed by GitHub
parent ac8bc54108
commit f9375f449c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 119 additions and 53 deletions

View file

@ -108,13 +108,11 @@ impl EditorView {
self.render_diagnostics(doc, view, inner, surface, theme);
let area = Rect::new(
view.area.x,
view.area.y + view.area.height.saturating_sub(1),
view.area.width,
1,
);
self.render_statusline(doc, view, area, surface, theme, is_focused);
let statusline_area = view
.area
.clip_top(view.area.height.saturating_sub(1))
.clip_bottom(1); // -1 from bottom to remove commandline
self.render_statusline(doc, view, statusline_area, surface, theme, is_focused);
}
/// Get syntax highlights for a document in a view represented by the first line
@ -528,12 +526,7 @@ impl EditorView {
let width = 80.min(viewport.width);
let height = 15.min(viewport.height);
paragraph.render(
Rect::new(
viewport.right() - width,
viewport.y as u16 + 1,
width,
height,
),
Rect::new(viewport.right() - width, viewport.y + 1, width, height),
surface,
);
}
@ -572,7 +565,7 @@ impl EditorView {
theme.get("ui.statusline.inactive")
};
// statusline
surface.set_style(Rect::new(viewport.x, viewport.y, viewport.width, 1), style);
surface.set_style(viewport.with_height(1), style);
if is_focused {
surface.set_string(viewport.x + 1, viewport.y, mode, style);
}
@ -1001,8 +994,7 @@ impl Component for EditorView {
surface.set_style(area, cx.editor.theme.get("ui.background"));
// if the terminal size suddenly changed, we need to trigger a resize
cx.editor
.resize(Rect::new(area.x, area.y, area.width, area.height - 1)); // - 1 to account for commandline
cx.editor.resize(area.clip_bottom(1)); // -1 from bottom for commandline
for (view, is_focused) in cx.editor.tree.views() {
let doc = cx.editor.document(view.doc).unwrap();

View file

@ -13,7 +13,7 @@ use helix_core::{
Rope,
};
use helix_view::{
graphics::{Color, Rect, Style},
graphics::{Color, Margin, Rect, Style},
Theme,
};
@ -207,8 +207,11 @@ impl Component for Markdown {
.wrap(Wrap { trim: false })
.scroll((cx.scroll.unwrap_or_default() as u16, 0));
let area = Rect::new(area.x + 1, area.y + 1, area.width - 2, area.height - 2);
par.render(area, surface);
let margin = Margin {
vertical: 1,
horizontal: 1,
};
par.render(area.inner(&margin), surface);
}
fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> {

View file

@ -304,14 +304,6 @@ impl<T: Item + 'static> Component for Menu<T> {
},
);
// // TODO: set bg for the whole row if selected
// if line == self.cursor {
// surface.set_style(
// Rect::new(area.x, area.y + i as u16, area.width - 1, 1),
// selected,
// )
// }
for (i, _) in (scroll..(scroll + win_height).min(len)).enumerate() {
let is_marked = i >= scroll_line && i < scroll_line + scroll_height;

View file

@ -19,7 +19,7 @@ use helix_core::Position;
use helix_view::{
document::canonicalize_path,
editor::Action,
graphics::{Color, CursorKind, Rect, Style},
graphics::{Color, CursorKind, Margin, Rect, Style},
Document, Editor,
};
@ -90,23 +90,26 @@ impl<T: 'static> Component for FilePicker<T> {
area.width
};
let picker_area = Rect::new(area.x, area.y, picker_width, area.height);
let picker_area = area.with_width(picker_width);
self.picker.render(picker_area, surface, cx);
if !render_preview {
return;
}
let preview_area = Rect::new(area.x + picker_width, area.y, area.width / 2, area.height);
let preview_area = area.clip_left(picker_width);
// don't like this but the lifetime sucks
let block = Block::default().borders(Borders::ALL);
// calculate the inner area inside the box
let mut inner = block.inner(preview_area);
let inner = block.inner(preview_area);
// 1 column gap on either side
inner.x += 1;
inner.width = inner.width.saturating_sub(2);
let margin = Margin {
vertical: 1,
horizontal: 0,
};
let inner = inner.inner(&margin);
block.render(preview_area, surface);
@ -282,15 +285,11 @@ impl<T> Picker<T> {
// - score all the names in relation to input
fn inner_rect(area: Rect) -> Rect {
let padding_vertical = area.height * 10 / 100;
let padding_horizontal = area.width * 10 / 100;
Rect::new(
area.x + padding_horizontal,
area.y + padding_vertical,
area.width - padding_horizontal * 2,
area.height - padding_vertical * 2,
)
let margin = Margin {
vertical: area.height * 10 / 100,
horizontal: area.width * 10 / 100,
};
area.inner(&margin)
}
impl<T: 'static> Component for Picker<T> {
@ -410,7 +409,7 @@ impl<T: 'static> Component for Picker<T> {
// -- Render the input bar:
let area = Rect::new(inner.x + 1, inner.y, inner.width - 1, 1);
let area = inner.clip_left(1).with_height(1);
let count = format!("{}/{}", self.matches.len(), self.options.len());
surface.set_stringn(
@ -434,8 +433,8 @@ impl<T: 'static> Component for Picker<T> {
}
// -- Render the contents:
// subtract the area of the prompt (-2) and current item marker " > " (-3)
let inner = Rect::new(inner.x + 3, inner.y + 2, inner.width - 3, inner.height - 2);
// subtract area of prompt from top and current item marker " > " from left
let inner = inner.clip_top(2).clip_left(3);
let selected = cx.editor.theme.get("ui.text.focus");
@ -474,7 +473,7 @@ impl<T: 'static> Component for Picker<T> {
let inner = block.inner(area);
// prompt area
let area = Rect::new(inner.x + 1, inner.y, inner.width - 1, 1);
let area = inner.clip_left(1).with_height(1);
self.prompt.cursor(area, editor)
}

View file

@ -24,7 +24,7 @@ pub struct Margin {
}
/// A simple rectangle used in the computation of the layout and to give widgets an hint about the
/// area they are supposed to render to.
/// area they are supposed to render to. (x, y) = (0, 0) is at the top left corner of the screen.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct Rect {
pub x: u16,
@ -92,6 +92,57 @@ impl Rect {
self.y.saturating_add(self.height)
}
// Returns a new Rect with width reduced from the left side.
// This changes the `x` coordinate and clamps it to the right
// edge of the original Rect.
pub fn clip_left(self, width: u16) -> Rect {
let width = std::cmp::min(width, self.width);
Rect {
x: self.x.saturating_add(width),
width: self.width.saturating_sub(width),
..self
}
}
// Returns a new Rect with width reduced from the right side.
// This does _not_ change the `x` coordinate.
pub fn clip_right(self, width: u16) -> Rect {
Rect {
width: self.width.saturating_sub(width),
..self
}
}
// Returns a new Rect with height reduced from the top.
// This changes the `y` coordinate and clamps it to the bottom
// edge of the original Rect.
pub fn clip_top(self, height: u16) -> Rect {
let height = std::cmp::min(height, self.height);
Rect {
y: self.y.saturating_add(height),
height: self.height.saturating_sub(height),
..self
}
}
// Returns a new Rect with height reduced from the bottom.
// This does _not_ change the `y` coordinate.
pub fn clip_bottom(self, height: u16) -> Rect {
Rect {
height: self.height.saturating_sub(height),
..self
}
}
pub fn with_height(self, height: u16) -> Rect {
// new height may make area > u16::max_value, so use new()
Self::new(self.x, self.y, self.width, height)
}
pub fn with_width(self, width: u16) -> Rect {
Self::new(self.x, self.y, width, self.height)
}
pub fn inner(self, margin: &Margin) -> Rect {
if self.width < 2 * margin.horizontal || self.height < 2 * margin.vertical {
Rect::default()
@ -495,6 +546,40 @@ mod tests {
assert_eq!(rect.height, 100);
}
#[test]
fn test_rect_chop_from_left() {
let rect = Rect::new(0, 0, 20, 30);
assert_eq!(Rect::new(10, 0, 10, 30), rect.clip_left(10));
assert_eq!(
Rect::new(20, 0, 0, 30),
rect.clip_left(40),
"x should be clamped to original width if new width is bigger"
);
}
#[test]
fn test_rect_chop_from_right() {
let rect = Rect::new(0, 0, 20, 30);
assert_eq!(Rect::new(0, 0, 10, 30), rect.clip_right(10));
}
#[test]
fn test_rect_chop_from_top() {
let rect = Rect::new(0, 0, 20, 30);
assert_eq!(Rect::new(0, 10, 20, 20), rect.clip_top(10));
assert_eq!(
Rect::new(0, 30, 20, 0),
rect.clip_top(50),
"y should be clamped to original height if new height is bigger"
);
}
#[test]
fn test_rect_chop_from_bottom() {
let rect = Rect::new(0, 0, 20, 30);
assert_eq!(Rect::new(0, 0, 20, 20), rect.clip_bottom(10));
}
fn styles() -> Vec<Style> {
vec![
Style::default(),

View file

@ -83,12 +83,7 @@ impl View {
pub fn inner_area(&self) -> Rect {
// TODO: not ideal
const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
Rect::new(
self.area.x + OFFSET,
self.area.y,
self.area.width - OFFSET,
self.area.height.saturating_sub(1), // -1 for statusline
)
self.area.clip_left(OFFSET).clip_bottom(1) // -1 for statusline
}
pub fn ensure_cursor_in_view(&mut self, doc: &Document, scrolloff: usize) {