Merge branch 'main' into jm/fix-canvas-rounding

This commit is contained in:
Josh McKinney 2024-11-20 04:18:14 -08:00 committed by GitHub
commit c75b2802e0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 256 additions and 80 deletions

View file

@ -75,7 +75,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: DavidAnson/markdownlint-cli2-action@v17
- uses: DavidAnson/markdownlint-cli2-action@v18
with:
globs: |
'**/*.md'
@ -94,7 +94,7 @@ jobs:
- uses: taiki-e/install-action@cargo-llvm-cov
- uses: Swatinem/rust-cache@v2
- run: cargo xtask coverage
- uses: codecov/codecov-action@v4
- uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true

91
Cargo.lock generated
View file

@ -367,8 +367,10 @@ checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-targets",
]
@ -401,9 +403,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.20"
version = "4.5.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f"
dependencies = [
"clap_builder",
"clap_derive",
@ -422,9 +424,9 @@ dependencies = [
[[package]]
name = "clap-verbosity-flag"
version = "2.2.2"
version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e099138e1807662ff75e2cebe4ae2287add879245574489f9b1588eb5e5564ed"
checksum = "34c77f67047557f62582784fd7482884697731b2932c7d37ced54bce2312e1e2"
dependencies = [
"clap",
"log",
@ -432,9 +434,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.20"
version = "4.5.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec"
dependencies = [
"anstream",
"anstyle",
@ -664,6 +666,41 @@ dependencies = [
"phf",
]
[[package]]
name = "darling"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim 0.11.1",
"syn 2.0.85",
]
[[package]]
name = "darling_macro"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
dependencies = [
"darling_core",
"quote",
"syn 2.0.85",
]
[[package]]
name = "deltae"
version = "0.3.2"
@ -1175,6 +1212,12 @@ dependencies = [
"cc",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "0.5.0"
@ -1209,10 +1252,14 @@ checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
[[package]]
name = "instability"
version = "0.3.2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c"
checksum = "b829f37dead9dc39df40c2d3376c179fdfd2ac771f53f55d3c30dc096a3c0c6e"
dependencies = [
"darling",
"indoc",
"pretty_assertions",
"proc-macro2",
"quote",
"syn 2.0.85",
]
@ -1561,9 +1608,9 @@ dependencies = [
[[package]]
name = "octocrab"
version = "0.41.2"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2dfd11f6efbd39491d71a3864496f0b6f45e2d01b73b26c55d631c4e0dafaef"
checksum = "5235d5839910001bef2c3df99a88688c7c781e5b1fd5fe40c5d8fa8bd786ac5a"
dependencies = [
"arc-swap",
"async-trait",
@ -1596,6 +1643,7 @@ dependencies = [
"tower-http",
"tracing",
"url",
"web-time",
]
[[package]]
@ -1909,9 +1957,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.88"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
dependencies = [
"unicode-ident",
]
@ -2492,18 +2540,18 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.214"
version = "1.0.215"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.214"
version = "1.0.215"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
dependencies = [
"proc-macro2",
"quote",
@ -3357,6 +3405,17 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "web-time"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
dependencies = [
"js-sys",
"serde",
"wasm-bindgen",
]
[[package]]
name = "wezterm-bidi"
version = "0.2.3"

View file

@ -27,14 +27,14 @@ rust-version = "1.74.0"
bitflags = "2.6.0"
document-features = "0.2.7"
indoc = "2.0.5"
instability = "0.3.1"
instability = "0.3.3"
itertools = "0.13.0"
pretty_assertions = "1.4.1"
ratatui = { path = "ratatui" }
ratatui-core = { path = "ratatui-core" }
ratatui-widgets = { path = "ratatui-widgets" }
rstest = "0.23.0"
serde = { version = "1.0.214", features = ["derive"] }
serde = { version = "1.0.215", features = ["derive"] }
strum = { version = "0.26.3", features = ["derive"] }
unicode-segmentation = "1.12.0"
# See <https://github.com/ratatui/ratatui/issues/1271> for information about why we pin unicode-width

View file

@ -8,26 +8,17 @@
default_job = "check"
[jobs.check]
command = ["cargo", "check", "--all-features", "--color", "always"]
command = ["cargo", "check", "--all-features"]
need_stdout = false
[jobs.check-all]
command = [
"cargo",
"check",
"--all-targets",
"--all-features",
"--color",
"always",
]
command = ["cargo", "check", "--all-targets", "--all-features"]
need_stdout = false
[jobs.check-crossterm]
command = [
"cargo",
"check",
"--color",
"always",
"--all-targets",
"--no-default-features",
"--features",
@ -39,8 +30,6 @@ need_stdout = false
command = [
"cargo",
"check",
"--color",
"always",
"--all-targets",
"--no-default-features",
"--features",
@ -52,8 +41,6 @@ need_stdout = false
command = [
"cargo",
"check",
"--color",
"always",
"--all-targets",
"--no-default-features",
"--features",
@ -62,34 +49,15 @@ command = [
need_stdout = false
[jobs.clippy]
command = ["cargo", "clippy", "--all-targets", "--color", "always"]
command = ["cargo", "clippy", "--all-targets"]
need_stdout = false
[jobs.test]
command = [
"cargo",
"test",
"--all-features",
"--color",
"always",
"--",
"--color",
"always", # see https://github.com/Canop/bacon/issues/124
]
command = ["cargo", "test", "--all-features"]
need_stdout = true
[jobs.test-unit]
command = [
"cargo",
"test",
"--lib",
"--all-features",
"--color",
"always",
"--",
"--color",
"always", # see https://github.com/Canop/bacon/issues/124
]
command = ["cargo", "test", "--lib", "--all-features"]
need_stdout = true
[jobs.doc]
@ -100,8 +68,6 @@ command = [
"-Zunstable-options",
"-Zrustdoc-scrape-examples",
"--all-features",
"--color",
"always",
"--no-deps",
]
env.RUSTDOCFLAGS = "--cfg docsrs"
@ -117,8 +83,6 @@ command = [
"-Zunstable-options",
"-Zrustdoc-scrape-examples",
"--all-features",
"--color",
"always",
"--no-deps",
"--open",
]
@ -134,8 +98,6 @@ command = [
"--output-path",
"target/lcov.info",
"--all-features",
"--color",
"always",
]
[jobs.coverage-unit-tests-only]
@ -147,8 +109,6 @@ command = [
"target/lcov.info",
"--lib",
"--all-features",
"--color",
"always",
]
[jobs.hack]
@ -160,8 +120,6 @@ command = [
"--each-feature",
# "--all-targets",
"--workspace",
"--color",
"always",
]
# You may define here keybindings that would be specific to

View file

@ -1,3 +1,10 @@
// show the feature flags in the generated documentation
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/ratatui/ratatui/main/assets/logo.png",
html_favicon_url = "https://raw.githubusercontent.com/ratatui/ratatui/main/assets/favicon.ico"
)]
//! **ratatui-core** is the core library of the [ratatui] project,
//! providing the essential building blocks for creating rich terminal user interfaces in Rust.
//!

View file

@ -121,7 +121,7 @@ pub trait StatefulWidget {
/// If you don't need this then you probably want to implement [`Widget`] instead.
///
/// [`Widget`]: super::Widget
type State;
type State: ?Sized;
/// Draws the current state of the widget in the given buffer. That is the only method required
/// to implement a custom stateful widget.
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State);
@ -159,4 +159,23 @@ mod tests {
widget.render(buf.area, &mut buf, &mut state);
assert_eq!(buf, Buffer::with_lines(["Hello world "]));
}
struct Bytes;
/// A widget with an unsized state type.
impl StatefulWidget for Bytes {
type State = [u8];
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
let slice = std::str::from_utf8(state).unwrap();
Line::from(format!("Bytes: {slice}")).render(area, buf);
}
}
#[rstest]
fn render_unsized_state_type(mut buf: Buffer) {
let widget = Bytes;
let state = b"hello";
widget.render(buf.area, &mut buf, &mut state.clone());
assert_eq!(buf, Buffer::with_lines(["Bytes: hello "]));
}
}

View file

@ -1,3 +1,10 @@
// show the feature flags in the generated documentation
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/ratatui/ratatui/main/assets/logo.png",
html_favicon_url = "https://raw.githubusercontent.com/ratatui/ratatui/main/assets/favicon.ico"
)]
#![warn(missing_docs)]
//! **ratatui-widgets** contains all the widgets that were previously part of the [Ratatui] crate.
//! It is meant to be used in conjunction with `ratatui`, which provides the core functionality

View file

@ -43,7 +43,7 @@ fakeit = "1.1"
font8x8 = "0.3.1"
futures = "0.3.30"
indoc = "2"
octocrab = "0.41.0"
octocrab = "0.42.0"
pretty_assertions = "1.4.0"
rand = "0.8.5"
rand_chacha = "0.3.1"

View file

@ -1,3 +1,11 @@
// show the feature flags in the generated documentation
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/ratatui/ratatui/main/assets/logo.png",
html_favicon_url = "https://raw.githubusercontent.com/ratatui/ratatui/main/assets/favicon.ico"
)]
#![warn(missing_docs)]
//! ![Demo](https://github.com/ratatui/ratatui/blob/87ae72dbc756067c97f6400d3e2a58eeb383776e/examples/demo2-destroy.gif?raw=true)
//!
//! <div align="center">
@ -318,13 +326,6 @@
//! [Forum]: https://forum.ratatui.rs
//! [Sponsors Badge]: https://img.shields.io/github/sponsors/ratatui?logo=github&style=flat-square&color=1370D3
// show the feature flags in the generated documentation
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/ratatui/ratatui/main/assets/logo.png",
html_favicon_url = "https://raw.githubusercontent.com/ratatui/ratatui/main/assets/favicon.ico"
)]
/// re-export the `crossterm` crate so that users don't have to add it as a dependency
#[cfg(feature = "crossterm")]
pub use crossterm;

View file

@ -61,7 +61,7 @@ pub trait StatefulWidgetRef {
/// If you don't need this then you probably want to implement [`WidgetRef`] instead.
///
/// [`WidgetRef`]: super::WidgetRef
type State;
type State: ?Sized;
/// Draws the current state of the widget in the given buffer. That is the only method required
/// to implement a custom stateful widget.
fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State);
@ -70,7 +70,7 @@ pub trait StatefulWidgetRef {
/// Blanket implementation of `StatefulWidgetRef` for `&W` where `W` implements `StatefulWidget`.
///
/// This allows you to render a stateful widget by reference.
impl<W, State> StatefulWidgetRef for &W
impl<W, State: ?Sized> StatefulWidgetRef for &W
where
for<'a> &'a W: StatefulWidget<State = State>,
{
@ -119,4 +119,38 @@ mod tests {
widget.render_ref(buf.area, &mut buf, &mut state);
assert_eq!(buf, Buffer::with_lines(["Hello world "]));
}
#[rstest]
fn render_stateful_widget_ref_with_unsized_state(mut buf: Buffer) {
struct Bytes;
impl StatefulWidgetRef for Bytes {
type State = [u8];
fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
let slice = std::str::from_utf8(state).unwrap();
Line::from(format!("Bytes: {slice}")).render(area, buf);
}
}
let widget = Bytes;
let state = b"hello";
widget.render_ref(buf.area, &mut buf, &mut state.clone());
assert_eq!(buf, Buffer::with_lines(["Bytes: hello "]));
}
#[rstest]
fn render_stateful_widget_with_unsized_state(mut buf: Buffer) {
struct Bytes;
impl StatefulWidget for &Bytes {
type State = [u8];
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
let slice = std::str::from_utf8(state).unwrap();
Line::from(format!("Bytes: {slice}")).render(area, buf);
}
}
let widget = &Bytes;
let mut state = b"hello".to_owned();
let state = state.as_mut_slice();
widget.render_ref(buf.area, &mut buf, state);
assert_eq!(buf, Buffer::with_lines(["Bytes: hello "]));
}
}

View file

@ -0,0 +1,91 @@
#![cfg(feature = "unstable-widget-ref")]
use std::{
any::{type_name, Any},
cell::RefCell,
};
use pretty_assertions::assert_eq;
use ratatui::widgets::StatefulWidgetRef;
use ratatui_core::{buffer::Buffer, layout::Rect, text::Line, widgets::Widget};
trait AnyWindow: StatefulWidgetRef<State = dyn Any> {
fn title(&self) -> &str {
type_name::<Self>()
}
}
struct Window1;
struct Window1State {
pub value: u32,
}
impl AnyWindow for Window1 {}
impl StatefulWidgetRef for Window1 {
type State = dyn Any;
fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
let state = state.downcast_mut::<Window1State>().expect("window1 state");
Line::from(format!("{}, u32: {}", self.title(), state.value)).render(area, buf);
}
}
struct Window2;
struct Window2State {
pub value: String,
}
impl AnyWindow for Window2 {}
impl StatefulWidgetRef for Window2 {
type State = dyn Any;
fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
let state = state.downcast_mut::<Window2State>().expect("window2 state");
Line::from(format!("{}, String: {}", self.title(), state.value)).render(area, buf);
}
}
type BoxedWindow = Box<dyn AnyWindow>;
type BoxedState = Box<RefCell<dyn Any>>;
#[test]
fn render_dyn_widgets() {
let windows: Vec<(BoxedWindow, BoxedState)> = vec![
(
Box::new(Window1),
Box::new(RefCell::new(Window1State { value: 32 })),
),
(
Box::new(Window2),
Box::new(RefCell::new(Window2State {
value: "Some".to_string(),
})),
),
(
Box::new(Window1),
Box::new(RefCell::new(Window1State { value: 42 })),
),
];
let mut buf = Buffer::empty(Rect::new(0, 0, 50, 3));
let mut area = Rect::new(0, 0, 50, 1);
for (w, s) in &windows {
let mut s = s.borrow_mut();
w.render_ref(area, &mut buf, &mut *s);
area.y += 1;
}
assert_eq!(
buf,
Buffer::with_lines([
"stateful_widget_ref_dyn::Window1, u32: 32 ",
"stateful_widget_ref_dyn::Window2, String: Some ",
"stateful_widget_ref_dyn::Window1, u32: 42 ",
])
);
}

View file

@ -6,9 +6,9 @@ license.workspace = true
[dependencies]
cargo_metadata = "0.18.1"
clap = { version = "4.5.20", features = ["derive"] }
clap = { version = "4.5.21", features = ["derive"] }
clap-cargo = { version = "0.14.1", features = ["cargo_metadata"] }
clap-verbosity-flag = "2.2.2"
clap-verbosity-flag = "2.2.3"
color-eyre = "0.6.3"
duct = "0.13.7"
tracing = "0.1.40"