diff --git a/helix-term/src/compositor.rs b/helix-term/src/compositor.rs index 3d1507fa2..2c0c1c119 100644 --- a/helix-term/src/compositor.rs +++ b/helix-term/src/compositor.rs @@ -26,6 +26,7 @@ pub struct Context<'a> { pub struct RenderContext<'a> { pub editor: &'a Editor, + pub surface: &'a mut Surface, pub scroll: Option, } @@ -34,7 +35,6 @@ pub trait Component: Any + AnyComponent { fn handle_event(&mut self, _event: Event, _ctx: &mut Context) -> EventResult { EventResult::Ignored(None) } - // , args: () /// Should redraw? Useful for saving redraw cycles if we know component didn't change. fn should_update(&self) -> bool { @@ -42,7 +42,7 @@ pub trait Component: Any + AnyComponent { } /// Render the component onto the provided surface. - fn render(&mut self, area: Rect, frame: &mut Surface, ctx: &mut RenderContext); + fn render(&mut self, area: Rect, ctx: &mut RenderContext); /// Get cursor position and cursor kind. fn cursor(&self, _area: Rect, _ctx: &Editor) -> (Option, CursorKind) { @@ -185,13 +185,14 @@ impl Compositor { // let area = *surface.area(); - let mut cx = RenderContext { + let mut render_cx = RenderContext { editor: cx.editor, + surface, scroll: None, }; for layer in &mut self.layers { - layer.render(area, surface, &mut cx); + layer.render(area, &mut render_cx); } let (pos, kind) = self.cursor(area, cx.editor); diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index 422d6d793..ec1ca326d 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -1,7 +1,6 @@ use crate::compositor::{Component, Context, EventResult, RenderContext}; use crossterm::event::{Event, KeyCode, KeyEvent}; use helix_view::editor::CompleteAction; -use tui::buffer::Buffer as Surface; use std::borrow::Cow; @@ -301,8 +300,8 @@ impl Component for Completion { self.popup.required_size(viewport) } - fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { - self.popup.render(area, surface, cx); + fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) { + self.popup.render(area, cx); // if we have a selection, render a markdown popup on top/below with info if let Some(option) = self.popup.contents().selection() { @@ -403,8 +402,8 @@ impl Component for Completion { // clear area let background = cx.editor.theme.get("ui.popup"); - surface.clear_with(area, background); - markdown_doc.render(area, surface, cx); + cx.surface.clear_with(area, background); + markdown_doc.render(area, cx); } } } diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 84c3c149b..d2f4d1467 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -1287,20 +1287,21 @@ impl Component for EditorView { } } - fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { + fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) { // clear with background color - surface.set_style(area, cx.editor.theme.get("ui.background")); + cx.surface + .set_style(area, cx.editor.theme.get("ui.background")); let config = cx.editor.config(); for (view, is_focused) in cx.editor.tree.views() { let doc = cx.editor.document(view.doc).unwrap(); - self.render_view(cx.editor, doc, view, area, surface, is_focused); + self.render_view(cx.editor, doc, view, area, cx.surface, is_focused); } if config.auto_info { // TODO: drop &mut self on render if let Some(mut info) = cx.editor.autoinfo.clone() { - info.render(area, surface, cx); + info.render(area, cx); // cx.editor.autoinfo = Some(info) } } @@ -1318,7 +1319,7 @@ impl Component for EditorView { cx.editor.theme.get("ui.text") }; - surface.set_string( + cx.surface.set_string( area.x, area.y + area.height.saturating_sub(1), status_msg, @@ -1348,7 +1349,7 @@ impl Component for EditorView { } else { 0 }; - surface.set_string( + cx.surface.set_string( area.x + area.width.saturating_sub(key_width + macro_width), area.y + area.height.saturating_sub(1), disp.get(disp.len().saturating_sub(key_width as usize)..) @@ -1360,7 +1361,7 @@ impl Component for EditorView { let style = style .fg(helix_view::graphics::Color::Yellow) .add_modifier(Modifier::BOLD); - surface.set_string( + cx.surface.set_string( area.x + area.width.saturating_sub(3), area.y + area.height.saturating_sub(1), &disp, @@ -1370,7 +1371,7 @@ impl Component for EditorView { } if let Some(completion) = self.completion.as_mut() { - completion.render(area, surface, cx); + completion.render(area, cx); } } diff --git a/helix-term/src/ui/info.rs b/helix-term/src/ui/info.rs index 24c35c92f..1530bd297 100644 --- a/helix-term/src/ui/info.rs +++ b/helix-term/src/ui/info.rs @@ -1,11 +1,10 @@ use crate::compositor::{Component, RenderContext}; use helix_view::graphics::{Margin, Rect}; use helix_view::info::Info; -use tui::buffer::Buffer as Surface; use tui::widgets::{Block, Borders, Paragraph, Widget}; impl Component for Info { - fn render(&mut self, viewport: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { + fn render(&mut self, viewport: Rect, cx: &mut RenderContext<'_>) { let text_style = cx.editor.theme.get("ui.text.info"); let popup_style = cx.editor.theme.get("ui.popup.info"); @@ -20,7 +19,7 @@ impl Component for Info { width, height, )); - surface.clear_with(area, popup_style); + cx.surface.clear_with(area, popup_style); let block = Block::default() .title(self.title.as_str()) @@ -32,10 +31,10 @@ impl Component for Info { horizontal: 1, }; let inner = block.inner(area).inner(&margin); - block.render(area, surface); + block.render(area, cx.surface); Paragraph::new(self.text.as_str()) .style(text_style) - .render(inner, surface); + .render(inner, cx.surface); } } diff --git a/helix-term/src/ui/markdown.rs b/helix-term/src/ui/markdown.rs index ac948ace3..830f59a1b 100644 --- a/helix-term/src/ui/markdown.rs +++ b/helix-term/src/ui/markdown.rs @@ -1,8 +1,5 @@ use crate::compositor::{Component, RenderContext}; -use tui::{ - buffer::Buffer as Surface, - text::{Span, Spans, Text}, -}; +use tui::text::{Span, Spans, Text}; use std::sync::Arc; @@ -259,7 +256,7 @@ impl Markdown { } impl Component for Markdown { - fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { + fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) { use tui::widgets::{Paragraph, Widget, Wrap}; let text = self.parse(Some(&cx.editor.theme)); @@ -272,7 +269,7 @@ impl Component for Markdown { vertical: 1, horizontal: 1, }; - par.render(area.inner(&margin), surface); + par.render(area.inner(&margin), cx.surface); } fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> { diff --git a/helix-term/src/ui/menu.rs b/helix-term/src/ui/menu.rs index 402bad979..7d831e15f 100644 --- a/helix-term/src/ui/menu.rs +++ b/helix-term/src/ui/menu.rs @@ -3,7 +3,7 @@ use crate::{ ctrl, key, shift, }; use crossterm::event::Event; -use tui::{buffer::Buffer as Surface, widgets::Table}; +use tui::widgets::Table; pub use tui::widgets::{Cell, Row}; @@ -265,7 +265,7 @@ impl Component for Menu { Some(self.size) } - fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { + fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) { let theme = &cx.editor.theme; let style = theme .try_get("ui.menu") @@ -307,7 +307,7 @@ impl Component for Menu { table.render_table( area, - surface, + cx.surface, &mut TableState { offset: scroll, selected: self.cursor, @@ -320,7 +320,7 @@ impl Component for Menu { let is_marked = i >= scroll_line && i < scroll_line + scroll_height; if !fits && is_marked { - let cell = &mut surface[(area.x + area.width - 2, area.y + i as u16)]; + let cell = &mut cx.surface[(area.x + area.width - 2, area.y + i as u16)]; cell.set_symbol("▐"); // cell.set_style(selected); // cell.set_style(if is_marked { selected } else { style }); diff --git a/helix-term/src/ui/overlay.rs b/helix-term/src/ui/overlay.rs index 75f7855f5..b2ab0d491 100644 --- a/helix-term/src/ui/overlay.rs +++ b/helix-term/src/ui/overlay.rs @@ -4,7 +4,6 @@ use helix_view::{ graphics::{CursorKind, Rect}, Editor, }; -use tui::buffer::Buffer; use crate::compositor::{Component, Context, EventResult, RenderContext}; @@ -44,9 +43,9 @@ fn clip_rect_relative(rect: Rect, percent_horizontal: u8, percent_vertical: u8) } impl Component for Overlay { - fn render(&mut self, area: Rect, frame: &mut Buffer, ctx: &mut RenderContext<'_>) { + fn render(&mut self, area: Rect, ctx: &mut RenderContext<'_>) { let dimensions = (self.calc_child_size)(area); - self.content.render(dimensions, frame, ctx) + self.content.render(dimensions, ctx) } fn required_size(&mut self, (width, height): (u16, u16)) -> Option<(u16, u16)> { diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index fd7a62561..6012510b9 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -4,10 +4,7 @@ use crate::{ ui::{self, EditorView}, }; use crossterm::event::Event; -use tui::{ - buffer::Buffer as Surface, - widgets::{Block, BorderType, Borders}, -}; +use tui::widgets::{Block, BorderType, Borders}; use fuzzy_matcher::skim::SkimMatcherV2 as Matcher; use fuzzy_matcher::FuzzyMatcher; @@ -164,7 +161,7 @@ impl FilePicker { } impl Component for FilePicker { - fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { + fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) { // +---------+ +---------+ // |prompt | |preview | // +---------+ | | @@ -177,7 +174,7 @@ impl Component for FilePicker { // clear area let background = cx.editor.theme.get("ui.background"); let text = cx.editor.theme.get("ui.text"); - surface.clear_with(area, background); + cx.surface.clear_with(area, background); let picker_width = if render_preview { area.width / 2 @@ -186,7 +183,7 @@ impl Component for FilePicker { }; let picker_area = area.with_width(picker_width); - self.picker.render(picker_area, surface, cx); + self.picker.render(picker_area, cx); if !render_preview { return; @@ -205,7 +202,7 @@ impl Component for FilePicker { horizontal: 1, }; let inner = inner.inner(&margin); - block.render(preview_area, surface); + block.render(preview_area, cx.surface); if let Some((path, range)) = self.current_file(cx.editor) { let preview = self.get_preview(&path, cx.editor); @@ -215,7 +212,8 @@ impl Component for FilePicker { let alt_text = preview.placeholder(); let x = inner.x + inner.width.saturating_sub(alt_text.len() as u16) / 2; let y = inner.y + inner.height / 2; - surface.set_stringn(x, y, alt_text, inner.width as usize, text); + cx.surface + .set_stringn(x, y, alt_text, inner.width as usize, text); return; } }; @@ -237,7 +235,7 @@ impl Component for FilePicker { doc, offset, inner, - surface, + cx.surface, &cx.editor.theme, highlights, &cx.editor.config().whitespace, @@ -246,7 +244,7 @@ impl Component for FilePicker { // highlight the line if let Some((start, end)) = range { let offset = start.saturating_sub(first_line) as u16; - surface.set_style( + cx.surface.set_style( Rect::new( inner.x, inner.y + offset, @@ -552,7 +550,7 @@ impl Component for Picker { EventResult::Consumed(None) } - fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { + fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) { let text_style = cx.editor.theme.get("ui.text"); let selected = cx.editor.theme.get("ui.text.focus"); let highlighted = cx.editor.theme.get("special").add_modifier(Modifier::BOLD); @@ -560,7 +558,7 @@ impl Component for Picker { // -- Render the frame: // clear area let background = cx.editor.theme.get("ui.background"); - surface.clear_with(area, background); + cx.surface.clear_with(area, background); // don't like this but the lifetime sucks let block = Block::default().borders(Borders::ALL); @@ -568,14 +566,14 @@ impl Component for Picker { // calculate the inner area inside the box let inner = block.inner(area); - block.render(area, surface); + block.render(area, cx.surface); // -- Render the input bar: let area = inner.clip_left(1).with_height(1); let count = format!("{}/{}", self.matches.len(), self.options.len()); - surface.set_stringn( + cx.surface.set_stringn( (area.x + area.width).saturating_sub(count.len() as u16 + 1), area.y, &count, @@ -583,13 +581,13 @@ impl Component for Picker { text_style, ); - self.prompt.render(area, surface, cx); + self.prompt.render(area, cx); // -- Separator let sep_style = Style::default().fg(Color::Rgb(90, 89, 119)); let borders = BorderType::line_symbols(BorderType::Plain); for x in inner.left()..inner.right() { - if let Some(cell) = surface.get_mut(x, inner.y + 1) { + if let Some(cell) = cx.surface.get_mut(x, inner.y + 1) { cell.set_symbol(borders.horizontal).set_style(sep_style); } } @@ -610,7 +608,8 @@ impl Component for Picker { for (i, (_index, option)) in files.take(rows as usize).enumerate() { let is_active = i == (self.cursor - offset); if is_active { - surface.set_string(inner.x.saturating_sub(2), inner.y + i as u16, ">", selected); + cx.surface + .set_string(inner.x.saturating_sub(2), inner.y + i as u16, ">", selected); } let formatted = (self.format_fn)(option); @@ -620,7 +619,7 @@ impl Component for Picker { .fuzzy_indices(&formatted, self.prompt.line()) .unwrap_or_default(); - surface.set_string_truncated( + cx.surface.set_string_truncated( inner.x, inner.y + i as u16, &formatted, diff --git a/helix-term/src/ui/popup.rs b/helix-term/src/ui/popup.rs index a97eb37fa..95d55d0ac 100644 --- a/helix-term/src/ui/popup.rs +++ b/helix-term/src/ui/popup.rs @@ -3,7 +3,6 @@ use crate::{ ctrl, key, }; use crossterm::event::Event; -use tui::buffer::Buffer as Surface; use helix_core::Position; use helix_view::{ @@ -179,7 +178,7 @@ impl Component for Popup { Some(self.size) } - fn render(&mut self, viewport: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { + fn render(&mut self, viewport: Rect, cx: &mut RenderContext<'_>) { // trigger required_size so we recalculate if the child changed self.required_size((viewport.width, viewport.height)); @@ -192,10 +191,10 @@ impl Component for Popup { // clear area let background = cx.editor.theme.get("ui.popup"); - surface.clear_with(area, background); + cx.surface.clear_with(area, background); let inner = area.inner(&self.margin); - self.contents.render(inner, surface, cx); + self.contents.render(inner, cx); } fn id(&self) -> Option<&'static str> { diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs index d119f3623..a24ed36b0 100644 --- a/helix-term/src/ui/prompt.rs +++ b/helix-term/src/ui/prompt.rs @@ -4,7 +4,6 @@ use crossterm::event::Event; use helix_view::input::KeyEvent; use helix_view::keyboard::KeyCode; use std::{borrow::Cow, ops::RangeFrom}; -use tui::buffer::Buffer as Surface; use tui::widgets::{Block, Borders, Widget}; use helix_core::{ @@ -327,7 +326,7 @@ impl Prompt { const BASE_WIDTH: u16 = 30; impl Prompt { - pub fn render_prompt(&self, area: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { + pub fn render_prompt(&self, area: Rect, cx: &mut RenderContext<'_>) { let theme = &cx.editor.theme; let prompt_color = theme.get("ui.text"); let completion_color = theme.get("ui.statusline"); @@ -367,7 +366,7 @@ impl Prompt { .map(|selection| selection / items * items) .unwrap_or_default(); - surface.clear_with(area, background); + cx.surface.clear_with(area, background); let mut row = 0; let mut col = 0; @@ -380,7 +379,7 @@ impl Prompt { } else { completion_color }; - surface.set_stringn( + cx.surface.set_stringn( area.x + col * (1 + col_width), area.y + row, &completion, @@ -413,7 +412,7 @@ impl Prompt { )); let background = theme.get("ui.help"); - surface.clear_with(area, background); + cx.surface.clear_with(area, background); let block = Block::default() // .title(self.title.as_str()) @@ -425,14 +424,15 @@ impl Prompt { horizontal: 1, }); - block.render(area, surface); - text.render(inner, surface, cx); + block.render(area, cx.surface); + text.render(inner, cx); } let line = area.height - 1; // render buffer text - surface.set_string(area.x, area.y + line, &self.prompt, prompt_color); - surface.set_string( + cx.surface + .set_string(area.x, area.y + line, &self.prompt, prompt_color); + cx.surface.set_string( area.x + self.prompt.len() as u16, area.y + line, &self.line, @@ -547,8 +547,8 @@ impl Component for Prompt { EventResult::Consumed(None) } - fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { - self.render_prompt(area, surface, cx) + fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) { + self.render_prompt(area, cx) } fn cursor(&self, area: Rect, _editor: &Editor) -> (Option, CursorKind) { diff --git a/helix-term/src/ui/text.rs b/helix-term/src/ui/text.rs index 81b1cdc1a..00cd21db3 100644 --- a/helix-term/src/ui/text.rs +++ b/helix-term/src/ui/text.rs @@ -1,5 +1,4 @@ use crate::compositor::{Component, RenderContext}; -use tui::buffer::Buffer as Surface; use helix_view::graphics::Rect; @@ -30,13 +29,13 @@ impl From> for Text { } impl Component for Text { - fn render(&mut self, area: Rect, surface: &mut Surface, _cx: &mut RenderContext<'_>) { + fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) { use tui::widgets::{Paragraph, Widget, Wrap}; let par = Paragraph::new(self.contents.clone()).wrap(Wrap { trim: false }); // .scroll(x, y) offsets - par.render(area, surface); + par.render(area, cx.surface); } fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> {