mirror of https://github.com/helix-editor/helix
Split out render & cursor methods into a tui renderer
parent
1aa2b027d7
commit
eadb2eaad1
|
@ -23,7 +23,7 @@ pub struct Context<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "term")]
|
#[cfg(feature = "term")]
|
||||||
mod term {
|
pub mod term {
|
||||||
use super::*;
|
use super::*;
|
||||||
pub use tui::buffer::Buffer as Surface;
|
pub use tui::buffer::Buffer as Surface;
|
||||||
|
|
||||||
|
@ -32,12 +32,24 @@ mod term {
|
||||||
pub surface: &'a mut Surface,
|
pub surface: &'a mut Surface,
|
||||||
pub scroll: Option<usize>,
|
pub scroll: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait Render {
|
||||||
|
/// Render the component onto the provided surface.
|
||||||
|
fn render(&mut self, area: Rect, ctx: &mut RenderContext);
|
||||||
|
|
||||||
|
// TODO: make required_size be layout() instead and part of this trait?
|
||||||
|
|
||||||
|
/// Get cursor position and cursor kind.
|
||||||
|
fn cursor(&self, _area: Rect, _ctx: &Editor) -> (Option<Position>, CursorKind) {
|
||||||
|
(None, CursorKind::Hidden)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "term")]
|
#[cfg(feature = "term")]
|
||||||
pub use term::*;
|
pub use term::*;
|
||||||
|
|
||||||
pub trait Component: Any + AnyComponent {
|
pub trait Component: Any + AnyComponent + Render {
|
||||||
/// Process input events, return true if handled.
|
/// Process input events, return true if handled.
|
||||||
fn handle_event(&mut self, _event: Event, _ctx: &mut Context) -> EventResult {
|
fn handle_event(&mut self, _event: Event, _ctx: &mut Context) -> EventResult {
|
||||||
EventResult::Ignored(None)
|
EventResult::Ignored(None)
|
||||||
|
@ -48,14 +60,6 @@ pub trait Component: Any + AnyComponent {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render the component onto the provided surface.
|
|
||||||
fn render(&mut self, area: Rect, ctx: &mut RenderContext);
|
|
||||||
|
|
||||||
/// Get cursor position and cursor kind.
|
|
||||||
fn cursor(&self, _area: Rect, _ctx: &Editor) -> (Option<Position>, CursorKind) {
|
|
||||||
(None, CursorKind::Hidden)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// May be used by the parent component to compute the child area.
|
/// May be used by the parent component to compute the child area.
|
||||||
/// viewport is the maximum allowed area, and the child should stay within those bounds.
|
/// viewport is the maximum allowed area, and the child should stay within those bounds.
|
||||||
///
|
///
|
||||||
|
|
|
@ -77,12 +77,13 @@ impl Info {
|
||||||
// term
|
// term
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
compositor::{Component, RenderContext},
|
compositor::{self, Component, RenderContext},
|
||||||
graphics::{Margin, Rect},
|
graphics::{Margin, Rect},
|
||||||
};
|
};
|
||||||
use tui::widgets::{Block, Borders, Paragraph, Widget};
|
use tui::widgets::{Block, Borders, Paragraph, Widget};
|
||||||
|
|
||||||
impl Component for Info {
|
#[cfg(feature = "term")]
|
||||||
|
impl compositor::term::Render for Info {
|
||||||
fn render(&mut self, viewport: Rect, cx: &mut RenderContext<'_>) {
|
fn render(&mut self, viewport: Rect, cx: &mut RenderContext<'_>) {
|
||||||
let text_style = cx.editor.theme.get("ui.text.info");
|
let text_style = cx.editor.theme.get("ui.text.info");
|
||||||
let popup_style = cx.editor.theme.get("ui.popup.info");
|
let popup_style = cx.editor.theme.get("ui.popup.info");
|
||||||
|
@ -117,3 +118,5 @@ impl Component for Info {
|
||||||
.render(inner, cx.surface);
|
.render(inner, cx.surface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Component for Info {}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
use crate::compositor::{Component, Context, Event, EventResult, RenderContext};
|
use crate::compositor::{self, Component, Context, Event, EventResult};
|
||||||
use crate::editor::CompleteAction;
|
use crate::editor::CompleteAction;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use helix_core::{Change, Transaction};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
graphics::Rect,
|
graphics::Rect,
|
||||||
input::{KeyCode, KeyEvent},
|
input::{KeyCode, KeyEvent},
|
||||||
Document, Editor,
|
Document, Editor,
|
||||||
};
|
};
|
||||||
|
use helix_core::{Change, Transaction};
|
||||||
|
|
||||||
use crate::commands;
|
use crate::commands;
|
||||||
use crate::ui::{menu, Markdown, Menu, Popup, PromptEvent};
|
use crate::ui::{menu, Markdown, Menu, Popup, PromptEvent};
|
||||||
|
@ -302,8 +302,11 @@ impl Component for Completion {
|
||||||
fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> {
|
fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> {
|
||||||
self.popup.required_size(viewport)
|
self.popup.required_size(viewport)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) {
|
#[cfg(feature = "term")]
|
||||||
|
impl compositor::term::Render for Completion {
|
||||||
|
fn render(&mut self, area: Rect, cx: &mut compositor::term::RenderContext<'_>) {
|
||||||
self.popup.render(area, cx);
|
self.popup.render(area, cx);
|
||||||
|
|
||||||
// if we have a selection, render a markdown popup on top/below with info
|
// if we have a selection, render a markdown popup on top/below with info
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
commands, key,
|
commands, compositor, key,
|
||||||
keymap::{KeymapResult, Keymaps},
|
keymap::{KeymapResult, Keymaps},
|
||||||
ui::{Completion, ProgressSpinners},
|
ui::{Completion, ProgressSpinners},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::compositor::{Component, Context, Event, EventResult, RenderContext};
|
use crate::compositor::{Component, Context, Event, EventResult};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
document::{Mode, SCRATCH_BUFFER_NAME},
|
||||||
|
editor::{CompleteAction, CursorShapeConfig},
|
||||||
|
graphics::{CursorKind, Modifier, Rect, Style},
|
||||||
|
input::{KeyEvent, MouseButton, MouseEvent, MouseEventKind},
|
||||||
|
keyboard::{KeyCode, KeyModifiers},
|
||||||
|
Document, Editor, Theme, View,
|
||||||
|
};
|
||||||
use helix_core::{
|
use helix_core::{
|
||||||
coords_at_pos, encoding,
|
coords_at_pos, encoding,
|
||||||
graphemes::{
|
graphemes::{
|
||||||
|
@ -17,18 +25,8 @@ use helix_core::{
|
||||||
unicode::width::UnicodeWidthStr,
|
unicode::width::UnicodeWidthStr,
|
||||||
LineEnding, Position, Range, Selection, Transaction,
|
LineEnding, Position, Range, Selection, Transaction,
|
||||||
};
|
};
|
||||||
use crate::{
|
|
||||||
document::{Mode, SCRATCH_BUFFER_NAME},
|
|
||||||
editor::{CompleteAction, CursorShapeConfig},
|
|
||||||
graphics::{CursorKind, Modifier, Rect, Style},
|
|
||||||
input::{KeyEvent, MouseButton, MouseEvent, MouseEventKind},
|
|
||||||
keyboard::{KeyCode, KeyModifiers},
|
|
||||||
Document, Editor, Theme, View,
|
|
||||||
};
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use tui::buffer::Buffer as Surface;
|
|
||||||
|
|
||||||
pub struct EditorView {
|
pub struct EditorView {
|
||||||
pub keymaps: Keymaps,
|
pub keymaps: Keymaps,
|
||||||
on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
|
on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
|
||||||
|
@ -64,7 +62,13 @@ impl EditorView {
|
||||||
pub fn spinners_mut(&mut self) -> &mut ProgressSpinners {
|
pub fn spinners_mut(&mut self) -> &mut ProgressSpinners {
|
||||||
&mut self.spinners
|
&mut self.spinners
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "term")]
|
||||||
|
use tui::buffer::Buffer as Surface;
|
||||||
|
|
||||||
|
#[cfg(feature = "term")]
|
||||||
|
impl EditorView {
|
||||||
pub fn render_view(
|
pub fn render_view(
|
||||||
&self,
|
&self,
|
||||||
editor: &Editor,
|
editor: &Editor,
|
||||||
|
@ -777,7 +781,9 @@ impl EditorView {
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EditorView {
|
||||||
/// Handle events by looking them up in `self.keymaps`. Returns None
|
/// Handle events by looking them up in `self.keymaps`. Returns None
|
||||||
/// if event was handled (a command was executed or a subkeymap was
|
/// if event was handled (a command was executed or a subkeymap was
|
||||||
/// activated). Only KeymapResult::{NotFound, Cancelled} is returned
|
/// activated). Only KeymapResult::{NotFound, Cancelled} is returned
|
||||||
|
@ -948,9 +954,7 @@ impl EditorView {
|
||||||
|
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl EditorView {
|
|
||||||
fn handle_mouse_event(
|
fn handle_mouse_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: MouseEvent,
|
event: MouseEvent,
|
||||||
|
@ -1288,8 +1292,11 @@ impl Component for EditorView {
|
||||||
Event::Mouse(event) => self.handle_mouse_event(event, &mut cx),
|
Event::Mouse(event) => self.handle_mouse_event(event, &mut cx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) {
|
#[cfg(feature = "term")]
|
||||||
|
impl compositor::term::Render for EditorView {
|
||||||
|
fn render(&mut self, area: Rect, cx: &mut compositor::term::RenderContext<'_>) {
|
||||||
// clear with background color
|
// clear with background color
|
||||||
cx.surface
|
cx.surface
|
||||||
.set_style(area, cx.editor.theme.get("ui.background"));
|
.set_style(area, cx.editor.theme.get("ui.background"));
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
use crate::compositor::{Component, RenderContext};
|
use crate::compositor::{self, Component, RenderContext};
|
||||||
use tui::text::{Span, Spans, Text};
|
use tui::text::{Span, Spans, Text};
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use pulldown_cmark::{CodeBlockKind, Event, HeadingLevel, Options, Parser, Tag};
|
use pulldown_cmark::{CodeBlockKind, Event, HeadingLevel, Options, Parser, Tag};
|
||||||
|
|
||||||
use helix_core::{
|
|
||||||
syntax::{self, HighlightEvent, Syntax},
|
|
||||||
Rope,
|
|
||||||
};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
graphics::{Margin, Rect, Style},
|
graphics::{Margin, Rect, Style},
|
||||||
Theme,
|
Theme,
|
||||||
};
|
};
|
||||||
|
use helix_core::{
|
||||||
|
syntax::{self, HighlightEvent, Syntax},
|
||||||
|
Rope,
|
||||||
|
};
|
||||||
|
|
||||||
fn styled_multiline_text<'a>(text: String, style: Style) -> Text<'a> {
|
fn styled_multiline_text<'a>(text: String, style: Style) -> Text<'a> {
|
||||||
let spans: Vec<_> = text
|
let spans: Vec<_> = text
|
||||||
|
@ -255,7 +255,8 @@ impl Markdown {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Markdown {
|
#[cfg(feature = "term")]
|
||||||
|
impl compositor::term::Render for Markdown {
|
||||||
fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) {
|
fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) {
|
||||||
use tui::widgets::{Paragraph, Widget, Wrap};
|
use tui::widgets::{Paragraph, Widget, Wrap};
|
||||||
|
|
||||||
|
@ -271,7 +272,9 @@ impl Component for Markdown {
|
||||||
};
|
};
|
||||||
par.render(area.inner(&margin), cx.surface);
|
par.render(area.inner(&margin), cx.surface);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Markdown {
|
||||||
fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> {
|
fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> {
|
||||||
let padding = 2;
|
let padding = 2;
|
||||||
if padding >= viewport.1 || padding >= viewport.0 {
|
if padding >= viewport.1 || padding >= viewport.0 {
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
use crate::{ctrl, key, shift};
|
|
||||||
use crate::compositor::{
|
use crate::compositor::{
|
||||||
Callback, Component, Compositor, Context, Event, EventResult, RenderContext,
|
self, Callback, Component, Compositor, Context, Event, EventResult, RenderContext,
|
||||||
};
|
};
|
||||||
use tui::widgets::Table;
|
use crate::{ctrl, key, shift};
|
||||||
|
|
||||||
pub use tui::widgets::{Cell, Row};
|
|
||||||
|
|
||||||
use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
|
use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
|
||||||
use fuzzy_matcher::FuzzyMatcher;
|
use fuzzy_matcher::FuzzyMatcher;
|
||||||
|
|
||||||
use crate::{graphics::Rect, Editor};
|
use crate::{graphics::Rect, Editor};
|
||||||
|
|
||||||
use tui::layout::Constraint;
|
use tui::layout::Constraint;
|
||||||
|
pub use tui::widgets::{Cell, Row};
|
||||||
|
|
||||||
pub trait Item {
|
pub trait Item {
|
||||||
fn label(&self) -> &str;
|
fn label(&self) -> &str;
|
||||||
|
@ -263,8 +262,13 @@ impl<T: Item + 'static> Component for Menu<T> {
|
||||||
|
|
||||||
Some(self.size)
|
Some(self.size)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "term")]
|
||||||
|
impl<T: Item + 'static> compositor::term::Render for Menu<T> {
|
||||||
fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) {
|
fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) {
|
||||||
|
use tui::widgets::Table;
|
||||||
|
|
||||||
let theme = &cx.editor.theme;
|
let theme = &cx.editor.theme;
|
||||||
let style = theme
|
let style = theme
|
||||||
.try_get("ui.menu")
|
.try_get("ui.menu")
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use helix_core::Position;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
compositor,
|
||||||
graphics::{CursorKind, Rect},
|
graphics::{CursorKind, Rect},
|
||||||
Editor,
|
Editor,
|
||||||
};
|
};
|
||||||
|
use helix_core::Position;
|
||||||
|
|
||||||
use crate::compositor::{Component, Context, Event, EventResult, RenderContext};
|
use crate::compositor::{Component, Context, Event, EventResult, RenderContext};
|
||||||
|
|
||||||
|
@ -41,12 +42,15 @@ fn clip_rect_relative(rect: Rect, percent_horizontal: u8, percent_vertical: u8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Component + 'static> Component for Overlay<T> {
|
#[cfg(feature = "term")]
|
||||||
|
impl<T: Component + 'static> compositor::term::Render for Overlay<T> {
|
||||||
fn render(&mut self, area: Rect, ctx: &mut RenderContext<'_>) {
|
fn render(&mut self, area: Rect, ctx: &mut RenderContext<'_>) {
|
||||||
let dimensions = (self.calc_child_size)(area);
|
let dimensions = (self.calc_child_size)(area);
|
||||||
self.content.render(dimensions, ctx)
|
self.content.render(dimensions, ctx)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Component + 'static> Component for Overlay<T> {
|
||||||
fn required_size(&mut self, (width, height): (u16, u16)) -> Option<(u16, u16)> {
|
fn required_size(&mut self, (width, height): (u16, u16)) -> Option<(u16, u16)> {
|
||||||
let area = Rect {
|
let area = Rect {
|
||||||
x: 0,
|
x: 0,
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
|
use crate::compositor::{self, Component, Compositor, Context, Event, EventResult, RenderContext};
|
||||||
use crate::{
|
use crate::{
|
||||||
ctrl, key, shift,
|
ctrl, key, shift,
|
||||||
ui::{self, EditorView},
|
ui::{self, EditorView},
|
||||||
};
|
};
|
||||||
use crate::compositor::{Component, Compositor, Context, Event, EventResult, RenderContext};
|
|
||||||
use tui::widgets::{Block, BorderType, Borders};
|
|
||||||
|
|
||||||
use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
|
use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
|
||||||
use fuzzy_matcher::FuzzyMatcher;
|
use fuzzy_matcher::FuzzyMatcher;
|
||||||
use tui::widgets::Widget;
|
|
||||||
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -19,12 +17,12 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::ui::{Prompt, PromptEvent};
|
use crate::ui::{Prompt, PromptEvent};
|
||||||
use helix_core::{movement::Direction, Position};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
editor::Action,
|
editor::Action,
|
||||||
graphics::{Color, CursorKind, Margin, Modifier, Rect, Style},
|
graphics::{Color, CursorKind, Margin, Modifier, Rect, Style},
|
||||||
Document, Editor,
|
Document, Editor,
|
||||||
};
|
};
|
||||||
|
use helix_core::{movement::Direction, Position};
|
||||||
|
|
||||||
pub const MIN_AREA_WIDTH_FOR_PREVIEW: u16 = 72;
|
pub const MIN_AREA_WIDTH_FOR_PREVIEW: u16 = 72;
|
||||||
/// Biggest file size to preview in bytes
|
/// Biggest file size to preview in bytes
|
||||||
|
@ -159,8 +157,11 @@ impl<T> FilePicker<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: 'static> Component for FilePicker<T> {
|
#[cfg(feature = "term")]
|
||||||
|
impl<T: 'static> compositor::term::Render for FilePicker<T> {
|
||||||
fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) {
|
fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) {
|
||||||
|
use tui::widgets::Widget;
|
||||||
|
use tui::widgets::{Block, Borders};
|
||||||
// +---------+ +---------+
|
// +---------+ +---------+
|
||||||
// |prompt | |preview |
|
// |prompt | |preview |
|
||||||
// +---------+ | |
|
// +---------+ | |
|
||||||
|
@ -260,15 +261,17 @@ impl<T: 'static> Component for FilePicker<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cursor(&self, area: Rect, ctx: &Editor) -> (Option<Position>, CursorKind) {
|
||||||
|
self.picker.cursor(area, ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static> Component for FilePicker<T> {
|
||||||
fn handle_event(&mut self, event: Event, ctx: &mut Context) -> EventResult {
|
fn handle_event(&mut self, event: Event, ctx: &mut Context) -> EventResult {
|
||||||
// TODO: keybinds for scrolling preview
|
// TODO: keybinds for scrolling preview
|
||||||
self.picker.handle_event(event, ctx)
|
self.picker.handle_event(event, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cursor(&self, area: Rect, ctx: &Editor) -> (Option<Position>, CursorKind) {
|
|
||||||
self.picker.cursor(area, ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn required_size(&mut self, (width, height): (u16, u16)) -> Option<(u16, u16)> {
|
fn required_size(&mut self, (width, height): (u16, u16)) -> Option<(u16, u16)> {
|
||||||
let picker_width = if width > MIN_AREA_WIDTH_FOR_PREVIEW {
|
let picker_width = if width > MIN_AREA_WIDTH_FOR_PREVIEW {
|
||||||
width / 2
|
width / 2
|
||||||
|
@ -548,8 +551,13 @@ impl<T: 'static> Component for Picker<T> {
|
||||||
|
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "term")]
|
||||||
|
impl<T: 'static> compositor::term::Render for Picker<T> {
|
||||||
fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) {
|
fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) {
|
||||||
|
use tui::widgets::Widget;
|
||||||
|
use tui::widgets::{Block, BorderType, Borders};
|
||||||
let text_style = cx.editor.theme.get("ui.text");
|
let text_style = cx.editor.theme.get("ui.text");
|
||||||
let selected = cx.editor.theme.get("ui.text.focus");
|
let selected = cx.editor.theme.get("ui.text.focus");
|
||||||
let highlighted = cx.editor.theme.get("special").add_modifier(Modifier::BOLD);
|
let highlighted = cx.editor.theme.get("special").add_modifier(Modifier::BOLD);
|
||||||
|
@ -639,6 +647,7 @@ impl<T: 'static> Component for Picker<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cursor(&self, area: Rect, editor: &Editor) -> (Option<Position>, CursorKind) {
|
fn cursor(&self, area: Rect, editor: &Editor) -> (Option<Position>, CursorKind) {
|
||||||
|
use tui::widgets::{Block, Borders};
|
||||||
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 inner = block.inner(area);
|
let inner = block.inner(area);
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
|
use crate::compositor::{self, Callback, Component, Context, Event, EventResult, RenderContext};
|
||||||
use crate::{ctrl, key};
|
use crate::{ctrl, key};
|
||||||
use crate::compositor::{Callback, Component, Context, Event, EventResult, RenderContext};
|
|
||||||
|
|
||||||
use helix_core::Position;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
graphics::{Margin, Rect},
|
graphics::{Margin, Rect},
|
||||||
Editor,
|
Editor,
|
||||||
};
|
};
|
||||||
|
use helix_core::Position;
|
||||||
|
|
||||||
// TODO: share logic with Menu, it's essentially Popup(render_fn), but render fn needs to return
|
// TODO: share logic with Menu, it's essentially Popup(render_fn), but render fn needs to return
|
||||||
// a width/height hint. maybe Popup(Box<Component>)
|
// a width/height hint. maybe Popup(Box<Component>)
|
||||||
|
@ -175,6 +175,13 @@ impl<T: Component> Component for Popup<T> {
|
||||||
Some(self.size)
|
Some(self.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn id(&self) -> Option<&'static str> {
|
||||||
|
Some(self.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "term")]
|
||||||
|
impl<T: Component> compositor::term::Render for Popup<T> {
|
||||||
fn render(&mut self, viewport: Rect, cx: &mut RenderContext<'_>) {
|
fn render(&mut self, viewport: Rect, cx: &mut RenderContext<'_>) {
|
||||||
// trigger required_size so we recalculate if the child changed
|
// trigger required_size so we recalculate if the child changed
|
||||||
self.required_size((viewport.width, viewport.height));
|
self.required_size((viewport.width, viewport.height));
|
||||||
|
@ -193,8 +200,4 @@ impl<T: Component> Component for Popup<T> {
|
||||||
let inner = area.inner(&self.margin);
|
let inner = area.inner(&self.margin);
|
||||||
self.contents.render(inner, cx);
|
self.contents.render(inner, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn id(&self) -> Option<&'static str> {
|
|
||||||
Some(self.id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use crate::compositor::{Component, Compositor, Context, Event, EventResult, RenderContext};
|
use crate::compositor::{self, Component, Compositor, Context, Event, EventResult, RenderContext};
|
||||||
use crate::input::KeyEvent;
|
use crate::input::KeyEvent;
|
||||||
use crate::keyboard::KeyCode;
|
use crate::keyboard::KeyCode;
|
||||||
use crate::{alt, ctrl, key, shift, ui};
|
use crate::{alt, ctrl, key, shift, ui};
|
||||||
use std::{borrow::Cow, ops::RangeFrom};
|
use std::{borrow::Cow, ops::RangeFrom};
|
||||||
use tui::widgets::{Block, Borders, Widget};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
graphics::{CursorKind, Margin, Rect},
|
graphics::{CursorKind, Margin, Rect},
|
||||||
|
@ -324,8 +323,11 @@ impl Prompt {
|
||||||
|
|
||||||
const BASE_WIDTH: u16 = 30;
|
const BASE_WIDTH: u16 = 30;
|
||||||
|
|
||||||
impl Prompt {
|
#[cfg(feature = "term")]
|
||||||
pub fn render_prompt(&self, area: Rect, cx: &mut RenderContext<'_>) {
|
impl compositor::term::Render for Prompt {
|
||||||
|
fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) {
|
||||||
|
use tui::widgets::{Block, Borders, Widget};
|
||||||
|
|
||||||
let theme = &cx.editor.theme;
|
let theme = &cx.editor.theme;
|
||||||
let prompt_color = theme.get("ui.text");
|
let prompt_color = theme.get("ui.text");
|
||||||
let completion_color = theme.get("ui.statusline");
|
let completion_color = theme.get("ui.statusline");
|
||||||
|
@ -438,6 +440,19 @@ impl Prompt {
|
||||||
prompt_color,
|
prompt_color,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cursor(&self, area: Rect, _editor: &Editor) -> (Option<Position>, CursorKind) {
|
||||||
|
let line = area.height as usize - 1;
|
||||||
|
(
|
||||||
|
Some(Position::new(
|
||||||
|
area.y as usize + line,
|
||||||
|
area.x as usize
|
||||||
|
+ self.prompt.len()
|
||||||
|
+ UnicodeWidthStr::width(&self.line[..self.cursor]),
|
||||||
|
)),
|
||||||
|
CursorKind::Block,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Prompt {
|
impl Component for Prompt {
|
||||||
|
@ -545,21 +560,4 @@ impl Component for Prompt {
|
||||||
|
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) {
|
|
||||||
self.render_prompt(area, cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cursor(&self, area: Rect, _editor: &Editor) -> (Option<Position>, CursorKind) {
|
|
||||||
let line = area.height as usize - 1;
|
|
||||||
(
|
|
||||||
Some(Position::new(
|
|
||||||
area.y as usize + line,
|
|
||||||
area.x as usize
|
|
||||||
+ self.prompt.len()
|
|
||||||
+ UnicodeWidthStr::width(&self.line[..self.cursor]),
|
|
||||||
)),
|
|
||||||
CursorKind::Block,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::compositor::{Component, RenderContext};
|
use crate::compositor::{self, Component, RenderContext};
|
||||||
use crate::graphics::Rect;
|
use crate::graphics::Rect;
|
||||||
|
|
||||||
pub struct Text {
|
pub struct Text {
|
||||||
|
@ -27,7 +27,8 @@ impl From<tui::text::Text<'static>> for Text {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Text {
|
#[cfg(feature = "term")]
|
||||||
|
impl compositor::term::Render for Text {
|
||||||
fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) {
|
fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) {
|
||||||
use tui::widgets::{Paragraph, Widget, Wrap};
|
use tui::widgets::{Paragraph, Widget, Wrap};
|
||||||
|
|
||||||
|
@ -36,7 +37,9 @@ impl Component for Text {
|
||||||
|
|
||||||
par.render(area, cx.surface);
|
par.render(area, cx.surface);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Text {
|
||||||
fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> {
|
fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> {
|
||||||
if viewport != self.viewport {
|
if viewport != self.viewport {
|
||||||
let width = std::cmp::min(self.contents.width() as u16, viewport.0);
|
let width = std::cmp::min(self.contents.width() as u16, viewport.0);
|
||||||
|
|
Loading…
Reference in New Issue