From 124514aa7024b0cf40bf01def54d280fcc86897c Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Tue, 15 Jun 2021 13:03:56 +0800 Subject: [PATCH] Add cursor kind to separate hidden cursor from pos Now IME cursor position should be correct since we can still set cursor position without drawing the cursor. --- helix-term/src/compositor.rs | 22 +++++++++++----------- helix-term/src/ui/editor.rs | 8 +++----- helix-term/src/ui/picker.rs | 5 +++-- helix-term/src/ui/popup.rs | 2 +- helix-term/src/ui/prompt.rs | 14 +++++++++----- helix-tui/src/terminal.rs | 32 +++++++++++++++++++++++++------- helix-view/src/editor.rs | 9 ++++++--- 7 files changed, 58 insertions(+), 34 deletions(-) diff --git a/helix-term/src/compositor.rs b/helix-term/src/compositor.rs index 99f9bb0e..6b39bb62 100644 --- a/helix-term/src/compositor.rs +++ b/helix-term/src/compositor.rs @@ -4,7 +4,7 @@ use crossterm::event::Event; use helix_core::Position; -use tui::{buffer::Buffer as Surface, layout::Rect}; +use tui::{buffer::Buffer as Surface, layout::Rect, terminal::CursorKind}; pub type Callback = Box; @@ -47,8 +47,9 @@ pub trait Component: Any + AnyComponent { /// Render the component onto the provided surface. fn render(&self, area: Rect, frame: &mut Surface, ctx: &mut Context); - fn cursor_position(&self, area: Rect, ctx: &Editor) -> Option { - None + /// Get cursor position and cursor kind. + fn cursor(&self, area: Rect, ctx: &Editor) -> (Option, CursorKind) { + (None, CursorKind::Hidden) } /// May be used by the parent component to compute the child area. @@ -137,20 +138,19 @@ impl Compositor { layer.render(area, surface, cx) } - let pos = self - .cursor_position(area, cx.editor) - .map(|pos| (pos.col as u16, pos.row as u16)); + let (pos, kind) = self.cursor(area, cx.editor); + let pos = pos.map(|pos| (pos.col as u16, pos.row as u16)); - self.terminal.draw(pos); + self.terminal.draw(pos, kind); } - pub fn cursor_position(&self, area: Rect, editor: &Editor) -> Option { + pub fn cursor(&self, area: Rect, editor: &Editor) -> (Option, CursorKind) { for layer in self.layers.iter().rev() { - if let Some(pos) = layer.cursor_position(area, editor) { - return Some(pos); + if let (Some(pos), kind) = layer.cursor(area, editor) { + return (Some(pos), kind); } } - None + (None, CursorKind::Hidden) } pub fn find(&mut self, type_name: &str) -> Option<&mut dyn Component> { diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 5913df29..95587b4c 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -26,6 +26,7 @@ use tui::{ buffer::Buffer as Surface, layout::Rect, style::{Color, Modifier, Style}, + terminal::CursorKind, }; pub struct EditorView { @@ -739,15 +740,12 @@ impl Component for EditorView { } } - fn cursor_position(&self, area: Rect, editor: &Editor) -> Option { + fn cursor(&self, area: Rect, editor: &Editor) -> (Option, CursorKind) { // match view.doc.mode() { // Mode::Insert => write!(stdout, "\x1B[6 q"), // mode => write!(stdout, "\x1B[2 q"), // }; - // return editor.cursor_position() - - // It's easier to just not render the cursor and use selection rendering instead. - None + editor.cursor() } } diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 20d51d5d..0aec9894 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -16,6 +16,7 @@ use crate::ui::{Prompt, PromptEvent}; use helix_core::Position; use helix_view::editor::Action; use helix_view::Editor; +use tui::terminal::CursorKind; pub struct Picker { options: Vec, @@ -304,7 +305,7 @@ impl Component for Picker { } } - fn cursor_position(&self, area: Rect, editor: &Editor) -> Option { + fn cursor(&self, area: Rect, editor: &Editor) -> (Option, CursorKind) { // TODO: this is mostly duplicate code let area = inner_rect(area); let block = Block::default().borders(Borders::ALL); @@ -314,6 +315,6 @@ impl Component for Picker { // prompt area let area = Rect::new(inner.x + 1, inner.y, inner.width - 1, 1); - self.prompt.cursor_position(area, editor) + self.prompt.cursor(area, editor) } } diff --git a/helix-term/src/ui/popup.rs b/helix-term/src/ui/popup.rs index ca349403..8488d1c6 100644 --- a/helix-term/src/ui/popup.rs +++ b/helix-term/src/ui/popup.rs @@ -116,7 +116,7 @@ impl Component for Popup { let position = self .position - .or_else(|| cx.editor.cursor_position()) + .or_else(|| cx.editor.cursor().0) .unwrap_or_default(); let (width, height) = self.size; diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs index c388c315..7b8af820 100644 --- a/helix-term/src/ui/prompt.rs +++ b/helix-term/src/ui/prompt.rs @@ -4,6 +4,7 @@ use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers}; use helix_core::Position; use helix_view::{Editor, Theme}; use std::{borrow::Cow, ops::RangeFrom}; +use tui::terminal::CursorKind; pub type Completion = (RangeFrom, Cow<'static, str>); @@ -342,11 +343,14 @@ impl Component for Prompt { self.render_prompt(area, surface, cx) } - fn cursor_position(&self, area: Rect, editor: &Editor) -> Option { + fn cursor(&self, area: Rect, editor: &Editor) -> (Option, CursorKind) { let line = area.height as usize - 1; - Some(Position::new( - area.y as usize + line, - area.x as usize + self.prompt.len() + self.cursor, - )) + ( + Some(Position::new( + area.y as usize + line, + area.x as usize + self.prompt.len() + self.cursor, + )), + CursorKind::Block, + ) } } diff --git a/helix-tui/src/terminal.rs b/helix-tui/src/terminal.rs index ad1e8da3..e8f8f359 100644 --- a/helix-tui/src/terminal.rs +++ b/helix-tui/src/terminal.rs @@ -8,6 +8,19 @@ enum ResizeBehavior { Auto, } +#[derive(Debug)] +/// UNSTABLE +pub enum CursorKind { + /// █ + Block, + /// | + // Bar, + /// _ + // Underline, + /// Hidden cursor, can set cursor position with this to let IME have correct cursor position. + Hidden, +} + #[derive(Debug, Clone, PartialEq)] /// UNSTABLE pub struct Viewport { @@ -147,7 +160,11 @@ where /// Synchronizes terminal size, calls the rendering closure, flushes the current internal state /// and prepares for the next draw call. - pub fn draw(&mut self, cursor_position: Option<(u16, u16)>) -> io::Result<()> { + pub fn draw( + &mut self, + cursor_position: Option<(u16, u16)>, + cursor_kind: CursorKind, + ) -> io::Result<()> { // // Autoresize - otherwise we get glitches if shrinking or potential desync between widgets // // and the terminal (if growing), which may OOB. // self.autoresize()?; @@ -162,12 +179,13 @@ where // Draw to stdout self.flush()?; - match cursor_position { - None => self.hide_cursor()?, - Some((x, y)) => { - self.show_cursor()?; - self.set_cursor(x, y)?; - } + if let Some((x, y)) = cursor_position { + self.set_cursor(x, y)?; + } + + match cursor_kind { + CursorKind::Block => self.show_cursor()?, + CursorKind::Hidden => self.hide_cursor()?, } // Swap buffers diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 2fd01817..24f43c0e 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -1,5 +1,6 @@ use crate::{theme::Theme, tree::Tree, Document, DocumentId, RegisterSelection, View, ViewId}; use tui::layout::Rect; +use tui::terminal::CursorKind; use std::path::PathBuf; @@ -9,6 +10,7 @@ use anyhow::Error; pub use helix_core::diagnostic::Severity; pub use helix_core::register::Registers; +use helix_core::Position; #[derive(Debug)] pub struct Editor { @@ -276,7 +278,7 @@ impl Editor { // let doc = &mut editor.documents[id]; // } - pub fn cursor_position(&self) -> Option { + pub fn cursor(&self) -> (Option, CursorKind) { const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter let view = self.view(); let doc = &self.documents[view.doc]; @@ -284,8 +286,9 @@ impl Editor { if let Some(mut pos) = view.screen_coords_at_pos(doc, doc.text().slice(..), cursor) { pos.col += view.area.x as usize + OFFSET as usize; pos.row += view.area.y as usize; - return Some(pos); + (Some(pos), CursorKind::Hidden) + } else { + (None, CursorKind::Hidden) } - None } }