From 3c0e11d69e61fa5e44aa39352eb1ab301fe7d05f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Sun, 27 Mar 2022 13:46:41 +0900 Subject: [PATCH] Implement a separate RenderContext --- helix-term/src/application.rs | 3 --- helix-term/src/commands.rs | 1 - helix-term/src/compositor.rs | 23 +++++++++++++++++------ helix-term/src/ui/completion.rs | 8 ++++---- helix-term/src/ui/editor.rs | 12 +++++------- helix-term/src/ui/info.rs | 4 ++-- helix-term/src/ui/markdown.rs | 4 ++-- helix-term/src/ui/menu.rs | 4 ++-- helix-term/src/ui/overlay.rs | 4 ++-- helix-term/src/ui/picker.rs | 6 +++--- helix-term/src/ui/popup.rs | 15 +++++++++------ helix-term/src/ui/prompt.rs | 6 +++--- helix-term/src/ui/text.rs | 4 ++-- helix-view/src/info.rs | 2 +- 14 files changed, 52 insertions(+), 44 deletions(-) diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 91caade79..c4b623948 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -209,7 +209,6 @@ fn render(&mut self) { let mut cx = crate::compositor::Context { editor: &mut self.editor, jobs: &mut self.jobs, - scroll: None, }; self.compositor.render(&mut cx); @@ -358,7 +357,6 @@ pub fn handle_idle_timeout(&mut self) { let mut cx = crate::compositor::Context { editor: &mut self.editor, jobs: &mut self.jobs, - scroll: None, }; if let EventResult::Consumed(_) = editor_view.handle_idle_timeout(&mut cx) { self.render(); @@ -369,7 +367,6 @@ pub fn handle_terminal_events(&mut self, event: Option { pub editor: &'a mut Editor, - pub scroll: Option, pub jobs: &'a mut Jobs, } +pub struct RenderContext<'a> { + pub editor: &'a Editor, + pub scroll: Option, +} + pub trait Component: Any + AnyComponent { /// Process input events, return true if handled. fn handle_event(&mut self, _event: Event, _ctx: &mut Context) -> EventResult { @@ -38,7 +42,7 @@ fn should_update(&self) -> bool { } /// Render the component onto the provided surface. - fn render(&mut self, area: Rect, frame: &mut Surface, ctx: &mut Context); + fn render(&mut self, area: Rect, frame: &mut Surface, ctx: &mut RenderContext); /// Get cursor position and cursor kind. fn cursor(&self, _area: Rect, _ctx: &Editor) -> (Option, CursorKind) { @@ -169,18 +173,25 @@ pub fn handle_event(&mut self, event: Event, cx: &mut Context) -> bool { } pub fn render(&mut self, cx: &mut Context) { - self.terminal + let area = self + .terminal .autoresize() .expect("Unable to determine terminal size"); - // TODO: need to recalculate view tree if necessary + // if the terminal size suddenly changed, we need to trigger a resize + cx.editor.resize(area.clip_bottom(1)); // -1 from bottom for commandline let surface = self.terminal.current_buffer_mut(); - let area = *surface.area(); + // let area = *surface.area(); + + let mut cx = RenderContext { + editor: cx.editor, + scroll: None, + }; for layer in &mut self.layers { - layer.render(area, surface, cx); + layer.render(area, surface, &mut 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 38005aad0..422d6d793 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -1,4 +1,4 @@ -use crate::compositor::{Component, Context, EventResult}; +use crate::compositor::{Component, Context, EventResult, RenderContext}; use crossterm::event::{Event, KeyCode, KeyEvent}; use helix_view::editor::CompleteAction; use tui::buffer::Buffer as Surface; @@ -301,7 +301,7 @@ fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> { self.popup.required_size(viewport) } - fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) { + fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { self.popup.render(area, surface, cx); // if we have a selection, render a markdown popup on top/below with info @@ -311,7 +311,7 @@ fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) { // --- // option.documentation - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let language = doc .language() .and_then(|scope| scope.strip_prefix("source.")) @@ -369,7 +369,7 @@ fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) { None => return, }; - let (popup_x, popup_y) = self.popup.get_rel_position(area, cx); + let (popup_x, popup_y) = self.popup.get_rel_position(area, cx.editor); let (popup_width, _popup_height) = self.popup.get_size(); let mut width = area .width diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 52e581632..84c3c149b 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -1,6 +1,6 @@ use crate::{ commands, - compositor::{Component, Context, EventResult}, + compositor::{Component, Context, EventResult, RenderContext}, key, keymap::{KeymapResult, Keymaps}, ui::{Completion, ProgressSpinners}, @@ -1201,7 +1201,6 @@ fn handle_event( let mut cx = Context { editor: cx.editor, jobs: cx.jobs, - scroll: None, }; let res = completion.handle_event(event, &mut cx); @@ -1288,12 +1287,10 @@ fn handle_event( } } - fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) { + fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { // clear with background color surface.set_style(area, cx.editor.theme.get("ui.background")); let config = cx.editor.config(); - // if the terminal size suddenly changed, we need to trigger a resize - 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(); @@ -1301,9 +1298,10 @@ fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) { } if config.auto_info { - if let Some(mut info) = cx.editor.autoinfo.take() { + // TODO: drop &mut self on render + if let Some(mut info) = cx.editor.autoinfo.clone() { info.render(area, surface, cx); - cx.editor.autoinfo = Some(info) + // cx.editor.autoinfo = Some(info) } } diff --git a/helix-term/src/ui/info.rs b/helix-term/src/ui/info.rs index 272244c1c..24c35c92f 100644 --- a/helix-term/src/ui/info.rs +++ b/helix-term/src/ui/info.rs @@ -1,11 +1,11 @@ -use crate::compositor::{Component, Context}; +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 Context) { + fn render(&mut self, viewport: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { let text_style = cx.editor.theme.get("ui.text.info"); let popup_style = cx.editor.theme.get("ui.popup.info"); diff --git a/helix-term/src/ui/markdown.rs b/helix-term/src/ui/markdown.rs index 5f78c3cc4..ac948ace3 100644 --- a/helix-term/src/ui/markdown.rs +++ b/helix-term/src/ui/markdown.rs @@ -1,4 +1,4 @@ -use crate::compositor::{Component, Context}; +use crate::compositor::{Component, RenderContext}; use tui::{ buffer::Buffer as Surface, text::{Span, Spans, Text}, @@ -259,7 +259,7 @@ fn parse(&self, theme: Option<&Theme>) -> tui::text::Text<'_> { } impl Component for Markdown { - fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) { + fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { use tui::widgets::{Paragraph, Widget, Wrap}; let text = self.parse(Some(&cx.editor.theme)); diff --git a/helix-term/src/ui/menu.rs b/helix-term/src/ui/menu.rs index d67a429e3..402bad979 100644 --- a/helix-term/src/ui/menu.rs +++ b/helix-term/src/ui/menu.rs @@ -1,5 +1,5 @@ use crate::{ - compositor::{Callback, Component, Compositor, Context, EventResult}, + compositor::{Callback, Component, Compositor, Context, EventResult, RenderContext}, ctrl, key, shift, }; use crossterm::event::Event; @@ -265,7 +265,7 @@ fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> { Some(self.size) } - fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) { + fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { let theme = &cx.editor.theme; let style = theme .try_get("ui.menu") diff --git a/helix-term/src/ui/overlay.rs b/helix-term/src/ui/overlay.rs index 9f522e355..75f7855f5 100644 --- a/helix-term/src/ui/overlay.rs +++ b/helix-term/src/ui/overlay.rs @@ -6,7 +6,7 @@ }; use tui::buffer::Buffer; -use crate::compositor::{Component, Context, EventResult}; +use crate::compositor::{Component, Context, EventResult, RenderContext}; /// Contains a component placed in the center of the parent component pub struct Overlay { @@ -44,7 +44,7 @@ fn mul_and_cast(size: u16, factor: u8) -> u16 { } impl Component for Overlay { - fn render(&mut self, area: Rect, frame: &mut Buffer, ctx: &mut Context) { + fn render(&mut self, area: Rect, frame: &mut Buffer, ctx: &mut RenderContext<'_>) { let dimensions = (self.calc_child_size)(area); self.content.render(dimensions, frame, ctx) } diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 3ca6965cf..fd7a62561 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -1,5 +1,5 @@ use crate::{ - compositor::{Component, Compositor, Context, EventResult}, + compositor::{Component, Compositor, Context, EventResult, RenderContext}, ctrl, key, shift, ui::{self, EditorView}, }; @@ -164,7 +164,7 @@ fn get_preview<'picker, 'editor>( } impl Component for FilePicker { - fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) { + fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { // +---------+ +---------+ // |prompt | |preview | // +---------+ | | @@ -552,7 +552,7 @@ fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult { EventResult::Consumed(None) } - fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) { + fn render(&mut self, area: Rect, surface: &mut Surface, 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); diff --git a/helix-term/src/ui/popup.rs b/helix-term/src/ui/popup.rs index 069a22f45..a97eb37fa 100644 --- a/helix-term/src/ui/popup.rs +++ b/helix-term/src/ui/popup.rs @@ -1,12 +1,15 @@ use crate::{ - compositor::{Callback, Component, Context, EventResult}, + compositor::{Callback, Component, Context, EventResult, RenderContext}, ctrl, key, }; use crossterm::event::Event; use tui::buffer::Buffer as Surface; use helix_core::Position; -use helix_view::graphics::{Margin, Rect}; +use helix_view::{ + graphics::{Margin, Rect}, + Editor, +}; // TODO: share logic with Menu, it's essentially Popup(render_fn), but render fn needs to return // a width/height hint. maybe Popup(Box) @@ -53,10 +56,10 @@ pub fn auto_close(mut self, auto_close: bool) -> Self { self } - pub fn get_rel_position(&mut self, viewport: Rect, cx: &Context) -> (u16, u16) { + pub fn get_rel_position(&mut self, viewport: Rect, editor: &Editor) -> (u16, u16) { let position = self .position - .get_or_insert_with(|| cx.editor.cursor().0.unwrap_or_default()); + .get_or_insert_with(|| editor.cursor().0.unwrap_or_default()); let (width, height) = self.size; @@ -176,13 +179,13 @@ fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> { Some(self.size) } - fn render(&mut self, viewport: Rect, surface: &mut Surface, cx: &mut Context) { + fn render(&mut self, viewport: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { // trigger required_size so we recalculate if the child changed self.required_size((viewport.width, viewport.height)); cx.scroll = Some(self.scroll); - let (rel_x, rel_y) = self.get_rel_position(viewport, cx); + let (rel_x, rel_y) = self.get_rel_position(viewport, cx.editor); // clip to viewport let area = viewport.intersection(Rect::new(rel_x, rel_y, self.size.0, self.size.1)); diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs index 165d87c79..d119f3623 100644 --- a/helix-term/src/ui/prompt.rs +++ b/helix-term/src/ui/prompt.rs @@ -1,4 +1,4 @@ -use crate::compositor::{Component, Compositor, Context, EventResult}; +use crate::compositor::{Component, Compositor, Context, EventResult, RenderContext}; use crate::{alt, ctrl, key, shift, ui}; use crossterm::event::Event; use helix_view::input::KeyEvent; @@ -327,7 +327,7 @@ pub fn exit_selection(&mut self) { const BASE_WIDTH: u16 = 30; impl Prompt { - pub fn render_prompt(&self, area: Rect, surface: &mut Surface, cx: &mut Context) { + pub fn render_prompt(&self, area: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { let theme = &cx.editor.theme; let prompt_color = theme.get("ui.text"); let completion_color = theme.get("ui.statusline"); @@ -547,7 +547,7 @@ fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult { EventResult::Consumed(None) } - fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) { + fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut RenderContext<'_>) { self.render_prompt(area, surface, cx) } diff --git a/helix-term/src/ui/text.rs b/helix-term/src/ui/text.rs index c318052b2..81b1cdc1a 100644 --- a/helix-term/src/ui/text.rs +++ b/helix-term/src/ui/text.rs @@ -1,4 +1,4 @@ -use crate::compositor::{Component, Context}; +use crate::compositor::{Component, RenderContext}; use tui::buffer::Buffer as Surface; use helix_view::graphics::Rect; @@ -30,7 +30,7 @@ fn from(contents: tui::text::Text<'static>) -> Self { } impl Component for Text { - fn render(&mut self, area: Rect, surface: &mut Surface, _cx: &mut Context) { + fn render(&mut self, area: Rect, surface: &mut Surface, _cx: &mut RenderContext<'_>) { use tui::widgets::{Paragraph, Widget, Wrap}; let par = Paragraph::new(self.contents.clone()).wrap(Wrap { trim: false }); diff --git a/helix-view/src/info.rs b/helix-view/src/info.rs index 5ad6a60c7..3877df21a 100644 --- a/helix-view/src/info.rs +++ b/helix-view/src/info.rs @@ -2,7 +2,7 @@ use helix_core::{register::Registers, unicode::width::UnicodeWidthStr}; use std::{collections::BTreeSet, fmt::Write}; -#[derive(Debug)] +#[derive(Debug, Clone)] /// Info box used in editor. Rendering logic will be in other crate. pub struct Info { /// Title shown at top.