diff --git a/Cargo.lock b/Cargo.lock index e1a72fea0..8d284945b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -133,6 +133,7 @@ dependencies = [ "once_cell", "png", "rand 0.8.5", + "ratatui", "re_sdk", "serde", "serde_json", @@ -142,7 +143,6 @@ dependencies = [ "thiserror", "tokio", "trycmd", - "tui", "unicode-width", "winit", "yield-progress", @@ -1500,11 +1500,11 @@ dependencies = [ [[package]] name = "crossterm" -version = "0.25.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", "crossterm_winapi", "libc", "mio", @@ -3004,6 +3004,15 @@ dependencies = [ "log", ] +[[package]] +name = "lru" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7" +dependencies = [ + "hashbrown 0.14.2", +] + [[package]] name = "lz4_flex" version = "0.10.0" @@ -4038,6 +4047,24 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" +[[package]] +name = "ratatui" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ebc917cfb527a566c37ecb94c7e3fd098353516fb4eb6bea17015ade0182425" +dependencies = [ + "bitflags 2.4.1", + "cassowary", + "crossterm", + "indoc", + "itertools 0.11.0", + "lru", + "paste", + "strum 0.25.0", + "unicode-segmentation", + "unicode-width", +] + [[package]] name = "raw-window-handle" version = "0.5.2" @@ -5428,19 +5455,6 @@ dependencies = [ "toml_edit 0.20.4", ] -[[package]] -name = "tui" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1" -dependencies = [ - "bitflags 1.3.2", - "cassowary", - "crossterm", - "unicode-segmentation", - "unicode-width", -] - [[package]] name = "twox-hash" version = "1.6.3" diff --git a/all-is-cubes-desktop/Cargo.toml b/all-is-cubes-desktop/Cargo.toml index 0baa046ea..80463608c 100644 --- a/all-is-cubes-desktop/Cargo.toml +++ b/all-is-cubes-desktop/Cargo.toml @@ -36,8 +36,8 @@ anyhow = { workspace = true } bytemuck = { workspace = true } cfg-if = { workspace = true } clap = { workspace = true } -# Note: keep crossterm in sync with tui's crossterm dependency. -crossterm = "0.25.0" +# Note: keep crossterm in sync with ratatui's crossterm dependency. +crossterm = "0.27.0" directories-next = "2.0.0" indicatif = { version = "0.17.0", default-features = false } kira = { version = "0.8.3", default-features = false, features = ["cpal"] } @@ -54,7 +54,7 @@ simplelog = { workspace = true } strum = { workspace = true, features = ["derive", "std"] } thiserror = { workspace = true } tokio = { workspace = true, features = ["rt-multi-thread", "sync"] } -tui = { version = "0.19.0", default-features = false, features = ["crossterm"] } +ratatui = { version = "0.24.0", default-features = false, features = ["crossterm"] } unicode-width = { version = "0.1.9", default-features = false } # Note on feature selection: winit requires either "x11" or "wayland" to build at all on Linux, which is harmless elsewhere. I picked x11 because it should be the most compatible. # TODO: drop rwh_05 when wgpu is updated diff --git a/all-is-cubes-desktop/src/terminal.rs b/all-is-cubes-desktop/src/terminal.rs index 3d5bfc655..f3c12f9f7 100644 --- a/all-is-cubes-desktop/src/terminal.rs +++ b/all-is-cubes-desktop/src/terminal.rs @@ -5,7 +5,7 @@ use std::time::{Duration, Instant}; use anyhow::Context; use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind}; -use tui::layout::Rect; +use ratatui::layout::Rect; use all_is_cubes::arcstr::literal_substr; use all_is_cubes::camera::{self, Camera, StandardCameras, Viewport}; @@ -235,6 +235,8 @@ fn run( MouseEventKind::Up(_) | MouseEventKind::Drag(_) | MouseEventKind::Moved + | MouseEventKind::ScrollLeft + | MouseEventKind::ScrollRight | MouseEventKind::ScrollDown | MouseEventKind::ScrollUp => {} } diff --git a/all-is-cubes-desktop/src/terminal/chars.rs b/all-is-cubes-desktop/src/terminal/chars.rs index cf598081b..a4b0be08b 100644 --- a/all-is-cubes-desktop/src/terminal/chars.rs +++ b/all-is-cubes-desktop/src/terminal/chars.rs @@ -9,6 +9,7 @@ use crossterm::QueueableCommand as _; use all_is_cubes::camera::ImagePixel; use all_is_cubes::euclid::Vector2D; use all_is_cubes::math::{Rgb, Rgba}; +use ratatui::backend::Backend; use super::options::{CharacterMode, ColorMode}; use super::{TextAndColor, TextRayImage}; @@ -190,7 +191,7 @@ const BRAILLE_TABLE: [&str; 256] = [ "⣰","⣱","⣲","⣳","⣴","⣵","⣶","⣷","⣸","⣹","⣺","⣻","⣼","⣽","⣾","⣿", ]; -pub(crate) fn write_colored_and_measure( +pub(crate) fn write_colored_and_measure( backend: &mut B, has_terminal_stdin: bool, width_table: &mut HashMap, @@ -214,7 +215,7 @@ pub(crate) fn write_colored_and_measure( /// /// Returns an error if the string could not be written. If an error was encountered /// measuring the width, returns an estimate instead. -fn write_and_measure( +fn write_and_measure( backend: &mut B, has_terminal_stdin: bool, width_table: &mut HashMap, diff --git a/all-is-cubes-desktop/src/terminal/ui.rs b/all-is-cubes-desktop/src/terminal/ui.rs index 764b02882..733a5e0f1 100644 --- a/all-is-cubes-desktop/src/terminal/ui.rs +++ b/all-is-cubes-desktop/src/terminal/ui.rs @@ -1,6 +1,5 @@ use std::borrow::Cow; use std::collections::HashMap; -use std::convert::TryInto; use std::io::{self, Write as _}; use std::sync::mpsc; @@ -8,12 +7,12 @@ use anyhow::Context as _; use crossterm::cursor::{self, MoveTo}; use crossterm::style::{Attribute, Color, Colors, SetAttribute, SetColors}; use crossterm::QueueableCommand as _; -use tui::backend::CrosstermBackend; -use tui::layout::{Constraint, Direction, Layout, Rect}; -use tui::style::{Color as TuiColor, Modifier, Style}; -use tui::text::{Span, Spans}; -use tui::widgets::{Borders, Paragraph}; -use tui::Terminal; +use ratatui::backend::CrosstermBackend; +use ratatui::layout::{Constraint, Direction, Layout, Rect}; +use ratatui::style::{Color as TuiColor, Modifier, Style}; +use ratatui::text::Span; +use ratatui::widgets::{Borders, Paragraph}; +use ratatui::Terminal; use all_is_cubes::character::{Character, Cursor}; use all_is_cubes::euclid::Vector2D; @@ -250,7 +249,7 @@ impl TerminalState { } /// Reset terminal state, as before exiting. - fn clean_up_terminal(&mut self) -> crossterm::Result<()> { + fn clean_up_terminal(&mut self) -> io::Result<()> { if self.terminal_state_dirty { fn log_if_fails(r: Result) { match r { @@ -276,7 +275,7 @@ impl TerminalState { /// /// If `draw_into_rect` is true, moves the cursor to fit into `self.viewport_position`. /// If it is false, does not affect the cursor position and uses newlines. - fn write_frame(&mut self, image: &TextRayImage, draw_into_rect: bool) -> crossterm::Result<()> { + fn write_frame(&mut self, image: &TextRayImage, draw_into_rect: bool) -> io::Result<()> { // Now separately draw the frame data. This is done because we want to use a precise // strategy for measuring the width of characters (draw them and read back the cursor // position) whereas tui-rs assumes that `unicode_width`'s answers match the terminal. @@ -358,8 +357,8 @@ impl TerminalState { Term color: N Term chars: M\n\ Quit: Esc, ^C, or ^D"; - let [viewport_rect_tmp, toolbar_rect, gfx_info_rect, cursor_and_help_rect]: [Rect; 4] = - Layout::default() + let [viewport_rect_tmp, toolbar_rect, gfx_info_rect, cursor_and_help_rect] = + *Layout::default() .direction(Direction::Vertical) .constraints([ Constraint::Min(1), @@ -368,14 +367,16 @@ impl TerminalState { Constraint::Length(3), ]) .split(f.size()) - .try_into() - .unwrap(); - let [cursor_rect, help_rect]: [Rect; 2] = Layout::default() + else { + unreachable!() + }; + let [cursor_rect, help_rect] = *Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Min(0), Constraint::Length(30)]) .split(cursor_and_help_rect) - .try_into() - .unwrap(); + else { + unreachable!() + }; // Toolbar { const SLOTS: usize = InventoryDisplay::SLOTS; @@ -407,12 +408,10 @@ impl TerminalState { .split(toolbar_rect); let selected_slots = ui_frame.inventory.selected_slots; - for ((i, rect), slot) in slot_rects - .into_iter() - .enumerate() - .zip(&ui_frame.inventory.slots) + for ((i, &rect), slot) in + slot_rects.iter().enumerate().zip(&ui_frame.inventory.slots) { - let slot_info = Spans::from(vec![ + let slot_info: Vec> = vec![ if selected_slots[0] == i { SELECTED_0 } else { @@ -424,8 +423,8 @@ impl TerminalState { } else { SELECTED_BLANK }, - ]); - let block = tui::widgets::Block::default() + ]; + let block = ratatui::widgets::Block::default() .title(slot_info) .borders(Borders::ALL); f.render_widget( @@ -452,17 +451,17 @@ impl TerminalState { // Graphics info line { - let [frame_info_rect, colors_info_rect, render_info_rect]: [Rect; 3] = - Layout::default() - .direction(Direction::Horizontal) - .constraints([ - Constraint::Percentage(30), - Constraint::Percentage(30), - Constraint::Percentage(30), - ]) - .split(gfx_info_rect) - .try_into() - .unwrap(); + let [frame_info_rect, colors_info_rect, render_info_rect] = *Layout::default() + .direction(Direction::Horizontal) + .constraints([ + Constraint::Percentage(30), + Constraint::Percentage(30), + Constraint::Percentage(30), + ]) + .split(gfx_info_rect) + else { + unreachable!() + }; f.render_widget( Paragraph::new(format!("{:5.1} FPS", ui_frame.frames_per_second)),