summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Thiel <sebastian.thiel@icloud.com>2021-05-03 07:41:25 +0800
committerSebastian Thiel <sebastian.thiel@icloud.com>2021-05-03 07:41:25 +0800
commit1ddbeae87dc0c23edf412405d6a08696bc703c1b (patch)
tree97b6541f50855025e115722743431a07bcedaf9e
parente16a3e4908cdfed103c0c1d5e54c31f1c90d40df (diff)
Remove tui-react, it now lives in https://github.com/Byron/tui-crates
-rw-r--r--tui-react/.gitignore1
-rw-r--r--tui-react/Cargo.toml15
-rw-r--r--tui-react/LICENSE21
-rw-r--r--tui-react/README.md43
-rw-r--r--tui-react/src/lib.rs156
-rw-r--r--tui-react/src/list.rs78
-rw-r--r--tui-react/src/terminal.rs181
7 files changed, 0 insertions, 495 deletions
diff --git a/tui-react/.gitignore b/tui-react/.gitignore
deleted file mode 100644
index 5a44eef..0000000
--- a/tui-react/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/Cargo.lock
diff --git a/tui-react/Cargo.toml b/tui-react/Cargo.toml
deleted file mode 100644
index aa55aad..0000000
--- a/tui-react/Cargo.toml
+++ /dev/null
@@ -1,15 +0,0 @@
-[package]
-name = "tui-react"
-version = "0.15.0"
-authors = ["Sebastian Thiel <sthiel@thoughtworks.com>"]
-edition = "2018"
-repository = "https://github.com/Byron/dua-cli"
-description = "TUI widgets using a react-like paradigm, allowing mutable component state and render properties."
-readme = "README.md"
-license = "MIT"
-
-[dependencies]
-tui = { version = "0.15.0", default-features = false }
-log = "0.4.6"
-unicode-segmentation = "1.6.0"
-unicode-width = "0.1.7"
diff --git a/tui-react/LICENSE b/tui-react/LICENSE
deleted file mode 100644
index 86c3928..0000000
--- a/tui-react/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2019 Sebastian Thiel
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/tui-react/README.md b/tui-react/README.md
deleted file mode 100644
index 0fe3e09..0000000
--- a/tui-react/README.md
+++ /dev/null
@@ -1,43 +0,0 @@
-**tui-react** is a library to enable components with state, and with properties provided per render
-
-Please note that this crate is early in development and build for the needs of **dua**.
-
-### How it works
-
-It uses the TUI infrastructure Terminal, but alters it to not enforce implementing the `Widget` trait.
-It provides only a single, optional, trait called `TopLevelComponent`, which makes it convenient to
-draw its implementors with `Terminal::render(..)`. However, since this enforces the absence of
-refernces in your state, it's probably not suitable for most.
-
-Instead, any struct can implement `render` methods or functions, and freely write into the terminal.
-That way, one can leverage everything Rust has to offer, which allows stateful components which
-work in your favor. Thus, this crate does away with 'one size fits all' render implementations,
-greatly adding to flexibility.
-
-State that one wants within the component for instance could be the scoll location. Alternatively,
-one can configure windows by altering their public state.
-
-### What's the relation to TUI?
-
-This project coudln't exist without it, and is happy to provide an alternative set of components
-for use in command-line applications.
-
-
-### Why `tui-react`?
-
-I kept having a terrible feeling when managing state with tui widgets when writing **dua**, and
-after trying many things, I realized what the problem actually was. It took me some time to
-realize it's not possible to have stateful components in with TUI, and I admire the smarts
-that went into the API design! After all, it effectively prohibited this! Amazing!
-
-That's why I implemented my own terminal and some key components, based on the ones provided
-by TUI, which can serve as standard building blocks in a stateful world.
-
-Thus far, the experience was fantastic, and it feels much better than before. Let's see what
-happens with it.
-
-### Changelog
-
-#### v0.4.1 - Simplify `block_width(…)` function
-
-#### v0.2.1 - add license file to crate
diff --git a/tui-react/src/lib.rs b/tui-react/src/lib.rs
deleted file mode 100644
index 9333fb5..0000000
--- a/tui-react/src/lib.rs
+++ /dev/null
@@ -1,156 +0,0 @@
-#![forbid(unsafe_code)]
-
-mod list;
-mod terminal;
-
-pub use list::*;
-pub use terminal::*;
-
-use std::iter::repeat;
-use tui::{self, buffer::Buffer, layout::Rect, style::Color, style::Style};
-use unicode_segmentation::UnicodeSegmentation;
-use unicode_width::UnicodeWidthStr;
-
-pub fn fill_background_to_right(mut s: String, entire_width: u16) -> String {
- match (s.len(), entire_width as usize) {
- (x, y) if x >= y => s,
- (x, y) => {
- s.extend(repeat(' ').take(y - x));
- s
- }
- }
-}
-
-/// Helper method to quickly set the background of all cells inside the specified area.
-pub fn fill_background(area: Rect, buf: &mut Buffer, color: Color) {
- for y in area.top()..area.bottom() {
- for x in area.left()..area.right() {
- buf.get_mut(x, y).set_bg(color);
- }
- }
-}
-
-pub fn draw_text_with_ellipsis_nowrap(
- bound: Rect,
- buf: &mut Buffer,
- text: impl AsRef<str>,
- style: impl Into<Option<Style>>,
-) -> u16 {
- let s = style.into();
- let t = text.as_ref();
- let mut graphemes = t.graphemes(true);
- let mut total_width = 0;
- {
- let mut ellipsis_candidate_x = None;
- let mut x_offset = 0;
- for (g, mut x) in graphemes.by_ref().zip(bound.left()..bound.right()) {
- let width = g.width();
- total_width += width;
-
- x += x_offset;
- let cell = buf.get_mut(x, bound.y);
- if x + 1 == bound.right() {
- ellipsis_candidate_x = Some(x);
- }
- cell.symbol = g.into();
- if let Some(s) = s {
- cell.set_style(s);
- }
-
- x_offset += width.saturating_sub(1) as u16;
- if x + x_offset >= bound.right() {
- break;
- }
- let x = x as usize;
- for x in x + 1..x + width {
- let i = buf.index_of(x as u16, bound.y);
- buf.content[i].reset();
- }
- }
- if let (Some(_), Some(x)) = (graphemes.next(), ellipsis_candidate_x) {
- buf.get_mut(x, bound.y).symbol = "…".into();
- }
- }
- total_width as u16
-}
-
-pub fn draw_text_nowrap_fn(
- bound: Rect,
- buf: &mut Buffer,
- t: impl AsRef<str>,
- mut s: impl FnMut(&str, u16, u16) -> Style,
-) {
- if bound.width == 0 {
- return;
- }
- for (g, x) in t.as_ref().graphemes(true).zip(bound.left()..bound.right()) {
- let cell = buf.get_mut(x, bound.y);
- cell.symbol = g.into();
- cell.set_style(s(&cell.symbol, x, bound.y));
- }
-}
-
-pub mod util {
- use unicode_segmentation::UnicodeSegmentation;
- use unicode_width::UnicodeWidthStr;
-
- pub fn sanitize_offset(offset: u16, num_items: usize, num_displayable_lines: u16) -> u16 {
- offset.min((num_items.saturating_sub(num_displayable_lines as usize)) as u16)
- }
-
- #[derive(Default)]
- pub struct GraphemeCountWriter(pub usize);
-
- impl std::io::Write for GraphemeCountWriter {
- fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
- self.0 += String::from_utf8_lossy(buf).graphemes(true).count();
- Ok(buf.len())
- }
-
- fn flush(&mut self) -> Result<(), std::io::Error> {
- Ok(())
- }
- }
-
- pub fn block_width(s: &str) -> u16 {
- s.width() as u16
- }
-
- pub mod rect {
- use tui::layout::Rect;
-
- /// A safe version of Rect::intersection that doesn't suffer from underflows
- pub fn intersect(lhs: Rect, rhs: Rect) -> Rect {
- let x1 = lhs.x.max(rhs.x);
- let y1 = lhs.y.max(rhs.y);
- let x2 = lhs.right().min(rhs.right());
- let y2 = lhs.bottom().min(rhs.bottom());
- Rect {
- x: x1,
- y: y1,
- width: x2.saturating_sub(x1),
- height: y2.saturating_sub(y1),
- }
- }
-
- pub fn offset_x(r: Rect, offset: u16) -> Rect {
- Rect {
- x: r.x + offset,
- width: r.width.saturating_sub(offset),
- ..r
- }
- }
-
- pub fn snap_to_right(bound: Rect, new_width: u16) -> Rect {
- offset_x(bound, bound.width.saturating_sub(new_width))
- }
-
- pub fn line_bound(bound: Rect, line: usize) -> Rect {
- Rect {
- y: bound.y + line as u16,
- height: 1,
- ..bound
- }
- }
- }
-}
diff --git a/tui-react/src/list.rs b/tui-react/src/list.rs
deleted file mode 100644
index 0ffb3eb..0000000
--- a/tui-react/src/list.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-use tui::{
- buffer::Buffer,
- layout::Rect,
- text::{Span, Spans, Text},
- widgets::{Block, Paragraph, Widget},
-};
-
-#[derive(Default)]
-pub struct List {
- /// The index at which the list last started. Used for scrolling
- pub offset: usize,
-}
-
-impl List {
- fn list_offset_for(&self, entry_in_view: Option<usize>, height: usize) -> usize {
- match entry_in_view {
- Some(pos) => match height as usize {
- h if self.offset + h - 1 < pos => pos - h + 1,
- _ if self.offset > pos => pos,
- _ => self.offset,
- },
- None => 0,
- }
- }
-}
-
-#[derive(Default)]
-pub struct ListProps<'b> {
- pub block: Option<Block<'b>>,
- pub entry_in_view: Option<usize>,
-}
-
-impl List {
- pub fn render<'a, 't>(
- &mut self,
- props: ListProps<'a>,
- items: impl IntoIterator<Item = Vec<Span<'t>>>,
- area: Rect,
- buf: &mut Buffer,
- ) {
- let ListProps {
- block,
- entry_in_view,
- } = props;
-
- let list_area = match block {
- Some(b) => {
- let inner_area = b.inner(area);
- b.render(area, buf);
- inner_area
- }
- None => area,
- };
- self.offset = self.list_offset_for(entry_in_view, list_area.height as usize);
-
- if list_area.width < 1 || list_area.height < 1 {
- return;
- }
-
- for (i, vec_of_spans) in items
- .into_iter()
- .skip(self.offset)
- .enumerate()
- .take(list_area.height as usize)
- {
- let (x, y) = (list_area.left(), list_area.top() + i as u16);
- Paragraph::new(Text::from(Spans::from(vec_of_spans))).render(
- Rect {
- x,
- y,
- width: list_area.width,
- height: 1,
- },
- buf,
- );
- }
- }
-}
diff --git a/tui-react/src/terminal.rs b/tui-react/src/terminal.rs
deleted file mode 100644
index f38f8ea..0000000
--- a/tui-react/src/terminal.rs
+++ /dev/null
@@ -1,181 +0,0 @@
-//! Derived from TUI-rs, license: MIT, Copyright (c) 2016 Florian Dehau
-use log::error;
-use std::{borrow::Borrow, io};
-
-use tui::{backend::Backend, buffer::Buffer, layout::Rect};
-
-/// A component meant to be rendered by `Terminal::render(...)`.
-/// All other components don't have to implement this trait, and instead
-/// provide a render method by convention, tuned towards their needs using whichever
-/// generic types or lifetimes they need.
-pub trait ToplevelComponent {
- type Props;
-
- fn render(&mut self, props: impl Borrow<Self::Props>, area: Rect, buf: &mut Buffer);
-}
-
-#[derive(Debug)]
-pub struct Terminal<B>
-where
- B: Backend,
-{
- pub backend: B,
- buffers: [Buffer; 2],
- current: usize,
- hidden_cursor: bool,
- known_size: Rect,
-}
-
-impl<B> Drop for Terminal<B>
-where
- B: Backend,
-{
- fn drop(&mut self) {
- // Attempt to restore the cursor state
- if self.hidden_cursor {
- if let Err(err) = self.show_cursor() {
- error!("Failed to show the cursor: {}", err);
- }
- }
- }
-}
-
-impl<B> Terminal<B>
-where
- B: Backend,
-{
- pub fn new(backend: B) -> io::Result<Terminal<B>> {
- let size = backend.size()?;
- Ok(Terminal {
- backend,
- buffers: [Buffer::empty(size), Buffer::empty(size)],
- current: 0,
- hidden_cursor: false,
- known_size: size,
- })
- }
-
- pub fn current_buffer_mut(&mut self) -> &mut Buffer {
- &mut self.buffers[self.current]
- }
-
- pub fn reconcile_and_flush(&mut self) -> io::Result<()> {
- let previous_buffer = &self.buffers[1 - self.current];
- let current_buffer = &self.buffers[self.current];
- let updates = previous_buffer.diff(current_buffer);
- self.backend.draw(updates.into_iter())
- }
-
- pub fn resize(&mut self, area: Rect) -> io::Result<()> {
- self.buffers[self.current].resize(area);
- self.buffers[1 - self.current].reset();
- self.buffers[1 - self.current].resize(area);
- self.known_size = area;
- self.backend.clear()
- }
-
- pub fn autoresize(&mut self) -> io::Result<()> {
- let size = self.size()?;
- if self.known_size != size {
- self.resize(size)?;
- }
- Ok(())
- }
-
- /// Get ready for rendering and return the maximum display size as `Rect`
- pub fn pre_render(&mut self) -> io::Result<Rect> {
- // Autoresize - otherwise we get glitches if shrinking or potential desync between widgets
- // and the terminal (if growing), which may OOB.
- self.autoresize()?;
- Ok(self.known_size)
- }
-
- pub fn post_render(&mut self) -> io::Result<()> {
- self.reconcile_and_flush()?;
-
- self.buffers[1 - self.current].reset();
- self.current = 1 - self.current;
-
- self.backend.flush()?;
- Ok(())
- }
-
- pub fn render<C>(&mut self, component: &mut C, props: impl Borrow<C::Props>) -> io::Result<()>
- where
- C: ToplevelComponent,
- {
- self.pre_render()?;
- component.render(props, self.known_size, self.current_buffer_mut());
- self.post_render()
- }
-
- pub fn hide_cursor(&mut self) -> io::Result<()> {
- self.backend.hide_cursor()?;
- self.hidden_cursor = true;
- Ok(())
- }
- pub fn show_cursor(&mut self) -> io::Result<()> {
- self.backend.show_cursor()?;
- self.hidden_cursor = false;
- Ok(())
- }
- pub fn get_cursor(&mut self) -> io::Result<(u16, u16)> {
- self.backend.get_cursor()
- }
- pub fn set_cursor(&mut self, x: u16, y: u16) -> io::Result<()> {
- self.backend.set_cursor(x, y)
- }
- pub fn clear(&mut self) -> io::Result<()> {
- self.backend.clear()
- }
- pub fn size(&self) -> io::Result<Rect> {
- self.backend.size()
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use tui::backend::TestBackend;
-
- #[derive(Default, Clone)]
- struct ComplexProps {
- x: usize,
- y: String,
- }
-
- #[derive(Default)]
- struct StatefulComponent {
- x: usize,
- }
-
- #[derive(Default)]
- struct StatelessComponent;
-
- impl ToplevelComponent for StatefulComponent {
- type Props = usize;
-
- fn render(&mut self, props: impl Borrow<Self::Props>, _area: Rect, _buf: &mut Buffer) {
- self.x += *props.borrow();
- }
- }
-
- impl ToplevelComponent for StatelessComponent {
- type Props = ComplexProps;
- fn render(&mut self, _props: impl Borrow<Self::Props>, _area: Rect, _buf: &mut Buffer) {
- // does not matter - we want to see it compiles essentially
- }
- }
-
- #[test]
- fn it_does_render_with_simple_and_complex_props() {
- let mut term = Terminal::new(TestBackend::new(20, 20)).unwrap();
- let mut c = StatefulComponent::default();
-
- term.render(&mut c, 3usize).ok();
- assert_eq!(c.x, 3);
-
- let mut c = StatelessComponent::default();
- term.render(&mut c, ComplexProps::default()).ok();
- }
-}