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
pull/637/head
Gokul Soumya 3 years ago committed by GitHub
parent ac8bc54108
commit f9375f449c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

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

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

@ -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() { for (i, _) in (scroll..(scroll + win_height).min(len)).enumerate() {
let is_marked = i >= scroll_line && i < scroll_line + scroll_height; let is_marked = i >= scroll_line && i < scroll_line + scroll_height;

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

@ -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 /// 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)] #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct Rect { pub struct Rect {
pub x: u16, pub x: u16,
@ -92,6 +92,57 @@ impl Rect {
self.y.saturating_add(self.height) 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 { pub fn inner(self, margin: &Margin) -> Rect {
if self.width < 2 * margin.horizontal || self.height < 2 * margin.vertical { if self.width < 2 * margin.horizontal || self.height < 2 * margin.vertical {
Rect::default() Rect::default()
@ -495,6 +546,40 @@ mod tests {
assert_eq!(rect.height, 100); 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> { fn styles() -> Vec<Style> {
vec![ vec![
Style::default(), Style::default(),

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

Loading…
Cancel
Save