From b3346b9a43e71ab8992d5257c7195c1496fd0f3a Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Tue, 24 Jul 2018 16:47:41 -0700 Subject: [PATCH] ensure that we set the cursor attributes Set the cursor to match the positioning info reported by the focused widget --- examples/widgets_basic.rs | 16 +++++++++++ src/render/terminfo.rs | 37 +++++++++++++++++++++++++- src/render/windows.rs | 2 ++ src/surface.rs | 33 +++++++++++++++++++++-- src/terminal/buffered.rs | 2 +- src/widgets/mod.rs | 56 +++++++++++++++++++++++++++++++++++---- 6 files changed, 137 insertions(+), 9 deletions(-) diff --git a/examples/widgets_basic.rs b/examples/widgets_basic.rs index 5b58acca7..983ba97ad 100644 --- a/examples/widgets_basic.rs +++ b/examples/widgets_basic.rs @@ -1,7 +1,10 @@ +//! This example shows how to make a basic widget that accumulates +//! text input and renders it to the screen extern crate failure; extern crate termwiz; use failure::Error; +use std::cell::Cell; use termwiz::caps::Capabilities; use termwiz::color::AnsiColor; use termwiz::input::*; @@ -14,6 +17,7 @@ use termwiz::widgets::*; #[derive(Default)] struct MainScreen { buf: String, + cursor: Cell, } impl WidgetImpl for MainScreen { @@ -45,6 +49,18 @@ impl WidgetImpl for MainScreen { fn render_to_surface(&self, surface: &mut Surface) { surface.add_change(Change::ClearScreen(AnsiColor::Blue.into())); surface.add_change(self.buf.clone()); + // Allow the surface rendering code to figure out where the + // cursor ends up, then stash a copy of that information for + // later retrieval by get_cursor_shape_and_position(). + let (x, y) = surface.cursor_position(); + self.cursor.set(ParentRelativeCoords::new(x, y)); + } + + fn get_cursor_shape_and_position(&self) -> CursorShapeAndPosition { + CursorShapeAndPosition { + coords: self.cursor.get(), + ..Default::default() + } } } diff --git a/src/render/terminfo.rs b/src/render/terminfo.rs index b507e0602..fef7d9172 100644 --- a/src/render/terminfo.rs +++ b/src/render/terminfo.rs @@ -6,7 +6,7 @@ use escape::csi::{Cursor, Edit, EraseInDisplay, EraseInLine, Sgr, CSI}; use escape::osc::OperatingSystemCommand; use failure; use std::io::{Read, Write}; -use surface::{Change, Position}; +use surface::{Change, CursorShape, Position}; use terminal::unix::UnixTty; use terminfo::{capability as cap, Capability as TermInfoCapability}; @@ -14,6 +14,8 @@ pub struct TerminfoRenderer { caps: Capabilities, current_attr: CellAttributes, pending_attr: Option, + /* TODO: we should record cursor position, shape and color here + * so that we can optimize updating them on screen. */ } impl TerminfoRenderer { @@ -477,6 +479,39 @@ impl TerminfoRenderer { change ); } + Change::CursorColor(_color) => { + // TODO: this isn't spec'd by terminfo, but some terminals + // support it. Add this to capabilities? + } + Change::CursorShape(shape) => match shape { + CursorShape::Default => { + if let Some(reset) = self.get_capability::() { + reset.expand().to(out.by_ref())?; + } + } + CursorShape::Hidden => { + if let Some(hide) = self.get_capability::() { + hide.expand().to(out.by_ref())?; + } + } + _ => { + if let Some(show) = self.get_capability::() { + show.expand().to(out.by_ref())?; + } + let param = match shape { + CursorShape::Default | CursorShape::Hidden => unreachable!(), + CursorShape::BlinkingBlock => 1, + CursorShape::SteadyBlock => 2, + CursorShape::BlinkingUnderline => 3, + CursorShape::SteadyUnderline => 4, + CursorShape::BlinkingBar => 6, + CursorShape::SteadyBar => 7, + }; + if let Some(set) = self.get_capability::() { + set.expand().kind(param).to(out.by_ref())?; + } + } + }, } } diff --git a/src/render/windows.rs b/src/render/windows.rs index dcb4076b3..fcb31de24 100644 --- a/src/render/windows.rs +++ b/src/render/windows.rs @@ -260,6 +260,8 @@ impl WindowsConsoleRenderer { Change::AllAttributes(all) => { self.current_attr = all.clone(); } + Change::CursorColor(_color) => {} + Change::CursorShape(_shape) => {} } } out.flush()?; diff --git a/src/surface.rs b/src/surface.rs index 746b14301..60930b281 100644 --- a/src/surface.rs +++ b/src/surface.rs @@ -19,6 +19,24 @@ pub enum Position { EndRelative(usize), } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CursorShape { + Hidden, + Default, + BlinkingBlock, + SteadyBlock, + BlinkingUnderline, + SteadyUnderline, + BlinkingBar, + SteadyBar, +} + +impl Default for CursorShape { + fn default() -> CursorShape { + CursorShape::Default + } +} + /// `Change` describes an update operation to be applied to a `Surface`. /// Changes to the active attributes (color, style), moving the cursor /// and outputting text are examples of some of the values. @@ -52,8 +70,11 @@ pub enum Change { ClearToEndOfScreen(ColorAttribute), /// Move the cursor to the specified `Position`. CursorPosition { x: Position, y: Position }, - /* CursorVisibility(bool), - * ChangeScrollRegion{top: usize, bottom: usize}, */ + /// Change the cursor color. + CursorColor(ColorAttribute), + /// Change the cursor shape + CursorShape(CursorShape), + /* ChangeScrollRegion{top: usize, bottom: usize}, */ } impl Change { @@ -206,6 +227,8 @@ pub struct Surface { ypos: usize, seqno: SequenceNo, changes: Vec, + cursor_shape: CursorShape, + cursor_color: ColorAttribute, } impl Surface { @@ -225,6 +248,10 @@ impl Surface { (self.width, self.height) } + pub fn cursor_position(&self) -> (usize, usize) { + (self.xpos, self.ypos) + } + /// Resize the Surface to the specified width and height. /// If the width and/or height are smaller than previously, the rows and/or /// columns are truncated. If the width and/or height are larger than @@ -294,6 +321,8 @@ impl Surface { Change::ClearScreen(color) => self.clear_screen(color), Change::ClearToEndOfLine(color) => self.clear_eol(color), Change::ClearToEndOfScreen(color) => self.clear_eos(color), + Change::CursorColor(color) => self.cursor_color = color.clone(), + Change::CursorShape(shape) => self.cursor_shape = shape.clone(), } } diff --git a/src/terminal/buffered.rs b/src/terminal/buffered.rs index 1e4da56c3..f12297ad5 100644 --- a/src/terminal/buffered.rs +++ b/src/terminal/buffered.rs @@ -55,7 +55,7 @@ impl BufferedTerminal { // renders all. self.seqno = 0; self.terminal.render(&changes)?; - self.terminal.flush()?; + //self.terminal.flush()?; self.seqno = seq; } self.surface.flush_changes_older_than(self.seqno); diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 14abebf3f..8ad2addf6 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -1,7 +1,8 @@ +use color::ColorAttribute; use input::InputEvent; use std::cell::{Ref, RefCell, RefMut}; use std::rc::{Rc, Weak}; -use surface::{SequenceNo, Surface}; +use surface::{Change, CursorShape, Position, SequenceNo, Surface}; /// Describes an event that may need to be processed by the widget pub enum WidgetEvent { @@ -46,6 +47,13 @@ impl SizeConstraints { } } +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct CursorShapeAndPosition { + pub shape: CursorShape, + pub coords: ParentRelativeCoords, + pub color: ColorAttribute, +} + pub trait WidgetImpl { /// Called once by the widget manager to inform the widget /// of its identifier @@ -58,12 +66,17 @@ pub trait WidgetImpl { fn get_size_constraints(&self) -> SizeConstraints; fn render_to_surface(&self, surface: &mut Surface); + + /// Called for the focused widget to determine how to render + /// the cursor. + fn get_cursor_shape_and_position(&self) -> CursorShapeAndPosition; } /// Relative to the top left of the parent container +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct ParentRelativeCoords { - x: usize, - y: usize, + pub x: usize, + pub y: usize, } impl ParentRelativeCoords { @@ -73,9 +86,10 @@ impl ParentRelativeCoords { } /// Relative to the top left of the screen +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct ScreenRelativeCoords { - x: usize, - y: usize, + pub x: usize, + pub y: usize, } impl ScreenRelativeCoords { @@ -192,6 +206,25 @@ impl Widget { pub fn add_child(&mut self, widget: &WidgetHandle) { self.children.push(widget.clone()); } + + pub fn to_screen_coords(&self, coords: &ParentRelativeCoords) -> ScreenRelativeCoords { + let mut x = coords.x; + let mut y = coords.y; + let mut widget = self.parent(); + loop { + let parent = match widget { + Some(parent) => { + let p = parent.borrow(); + x += p.coordinates.x; + y += p.coordinates.y; + p.parent() + } + None => break, + }; + widget = parent; + } + ScreenRelativeCoords { x, y } + } } pub struct Screen { @@ -243,5 +276,18 @@ impl Screen { /// and then progresses up through its children. pub fn render_to_screen(&mut self, screen: &mut Surface) { self.root_widget.borrow_mut().render_to_screen(screen); + + let focused = self.focused_widget.borrow(); + let cursor = focused.inner.get_cursor_shape_and_position(); + let coords = focused.to_screen_coords(&cursor.coords); + + screen.add_changes(vec![ + Change::CursorShape(cursor.shape), + Change::CursorColor(cursor.color), + Change::CursorPosition { + x: Position::Absolute(coords.x), + y: Position::Absolute(coords.y), + }, + ]); } }