#![allow(dead_code)] use crate::mini_salsa::endless_scroll::{EndlessScroll, EndlessScrollState}; use crate::mini_salsa::theme::THEME; use crate::mini_salsa::{run_ui, setup_logging, MiniSalsaState}; use rat_event::{ct_event, flow, ConsumedEvent, HandleEvent, Regular}; use rat_focus::{Focus, FocusBuilder}; use rat_menu::event::MenuOutcome; use rat_menu::menuline::{MenuLine, MenuLineState}; use rat_scrolled::Scroll; use rat_widget::event::Outcome; use rat_widget::list::selection::RowSelection; use rat_widget::list::{List, ListState}; use rat_widget::statusline::StatusLineState; use rat_widget::tabbed::{TabPlacement, TabType, Tabbed, TabbedState}; use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::prelude::Line; use ratatui::style::{Style, Stylize}; use ratatui::symbols::border; use ratatui::widgets::{Block, BorderType, StatefulWidget, Widget}; use ratatui::Frame; mod mini_salsa; fn main() -> Result<(), anyhow::Error> { setup_logging()?; let mut data = Data {}; let mut state = State { border_type: None, placement: TabPlacement::default(), style: TabType::default(), close: false, tabbed: TabbedState::default(), tabs_0: Default::default(), tabs_1: Default::default(), tabs_2: Default::default(), menu: MenuLineState::default(), status: StatusLineState::default(), }; state.menu.focus.set(true); run_ui( "tabbed1", handle_input, repaint_input, &mut data, &mut state, ) } struct Data {} struct State { border_type: Option<(BorderType, border::Set)>, placement: TabPlacement, style: TabType, close: bool, tabbed: TabbedState, tabs_0: ListState, tabs_1: EndlessScrollState, tabs_2: EndlessScrollState, menu: MenuLineState, status: StatusLineState, } fn repaint_input( frame: &mut Frame<'_>, area: Rect, _data: &mut Data, _istate: &mut MiniSalsaState, state: &mut State, ) -> Result<(), anyhow::Error> { let l1 = Layout::vertical([ Constraint::Length(1), Constraint::Fill(1), Constraint::Length(1), Constraint::Length(1), ]) .split(area); let l2 = Layout::horizontal([ Constraint::Length(25), Constraint::Fill(1), Constraint::Length(15), ]) .split(l1[1]); let mut tab = Tabbed::new() .styles(THEME.tabbed_style()) .tab_type(state.style) .placement(state.placement); if state.close { tab = tab.closeable(true); } if let Some(border_type) = state.border_type { tab = tab.block( Block::bordered() .border_type(border_type.0) .border_set(border_type.1), ); } tab = tab.tabs(["Issues", "Numbers", "More numbers"]); tab.render(l2[1], frame.buffer_mut(), &mut state.tabbed); match state.tabbed.selected().expect("tab") { 0 => List::::new(LIST) .styles(THEME.list_style()) .scroll(Scroll::new().styles(THEME.scroll_style())) .render( state.tabbed.widget_area, frame.buffer_mut(), &mut state.tabs_0, ), 1 => EndlessScroll::new() .max(2024) // .style(THEME.bluegreen(0)) .focus_style(THEME.focus()) .v_scroll( Scroll::new() // .styles(THEME.scroll_style()), ) .render( state.tabbed.widget_area, frame.buffer_mut(), &mut state.tabs_1, ), 2 => EndlessScroll::new() .max(2024) // .style(THEME.cyan(0)) .focus_style(THEME.focus()) .v_scroll( Scroll::new() // .styles(THEME.scroll_style()), ) .render( state.tabbed.widget_area, frame.buffer_mut(), &mut state.tabs_2, ), _ => {} } let mut area = Rect::new(l2[0].x, l2[0].y, l2[0].width, 1); Line::from("F1: close") .yellow() .render(area, frame.buffer_mut()); area.y += 1; Line::from("F2: type") .yellow() .render(area, frame.buffer_mut()); area.y += 1; Line::from("F3: alignment") .yellow() .render(area, frame.buffer_mut()); area.y += 1; Line::from("F5: border") .yellow() .render(area, frame.buffer_mut()); area.y += 1; Line::from("F6: reorder") .yellow() .render(area, frame.buffer_mut()); area.y += 1; Line::from("F12: key-nav") .yellow() .render(area, frame.buffer_mut()); area.y += 1; let menu1 = MenuLine::new() .title("||||") .item_parsed("_Quit") .title_style(Style::default().black().on_yellow()) .style(Style::default().black().on_dark_gray()); frame.render_stateful_widget(menu1, l1[3], &mut state.menu); Ok(()) } fn focus(state: &State) -> Focus { let mut fb = FocusBuilder::default(); fb.widget(&state.tabbed); if let Some(sel) = state.tabbed.selected { match sel { 0 => _ = fb.widget(&state.tabs_0), 1 => _ = fb.widget(&state.tabs_1), 2 => _ = fb.widget(&state.tabs_2), _ => {} } } fb.widget(&state.menu); fb.build() } fn handle_input( event: &crossterm::event::Event, _data: &mut Data, istate: &mut MiniSalsaState, state: &mut State, ) -> Result { let f = focus(state).handle(event, Regular); let r = f.and(|| { flow!(match event { ct_event!(keycode press F(1)) => { state.close = !state.close; Outcome::Changed } ct_event!(keycode press F(2)) => { state.style = match state.style { TabType::Glued => TabType::Attached, TabType::Attached => TabType::Glued, _ => TabType::Glued, }; Outcome::Changed } ct_event!(keycode press SHIFT-F(2)) => { state.style = match state.style { TabType::Glued => TabType::Attached, TabType::Attached => TabType::Glued, _ => TabType::Glued, }; Outcome::Changed } ct_event!(keycode press F(3)) => { state.placement = match state.placement { TabPlacement::Top => TabPlacement::Right, TabPlacement::Right => TabPlacement::Bottom, TabPlacement::Bottom => TabPlacement::Left, TabPlacement::Left => TabPlacement::Top, }; Outcome::Changed } ct_event!(keycode press SHIFT-F(3)) => { state.placement = match state.placement { TabPlacement::Top => TabPlacement::Left, TabPlacement::Right => TabPlacement::Top, TabPlacement::Bottom => TabPlacement::Right, TabPlacement::Left => TabPlacement::Bottom, }; Outcome::Changed } ct_event!(keycode press F(5)) => { state.border_type = match state.border_type { None => Some((BorderType::Plain, border::PLAIN)), Some((BorderType::Plain, border::PLAIN)) => { Some((BorderType::Plain, border::ONE_EIGHTH_TALL)) } Some((BorderType::Plain, border::ONE_EIGHTH_TALL)) => { Some((BorderType::Plain, border::ONE_EIGHTH_WIDE)) } Some((BorderType::Plain, border::ONE_EIGHTH_WIDE)) => { Some((BorderType::Plain, border::PROPORTIONAL_WIDE)) } Some((BorderType::Plain, border::PROPORTIONAL_WIDE)) => { Some((BorderType::Plain, border::PROPORTIONAL_TALL)) } Some((BorderType::Plain, border::PROPORTIONAL_TALL)) => { Some((BorderType::Double, border::DOUBLE)) } Some((BorderType::Double, border::DOUBLE)) => { Some((BorderType::Rounded, border::ROUNDED)) } Some((BorderType::Rounded, border::ROUNDED)) => { Some((BorderType::Thick, border::THICK)) } Some((BorderType::Thick, border::THICK)) => { Some((BorderType::QuadrantInside, border::QUADRANT_INSIDE)) } Some((BorderType::QuadrantInside, border::QUADRANT_INSIDE)) => { Some((BorderType::QuadrantOutside, border::QUADRANT_OUTSIDE)) } Some((BorderType::QuadrantOutside, border::QUADRANT_OUTSIDE)) => None, _ => Some((BorderType::Plain, border::PLAIN)), }; Outcome::Changed } ct_event!(keycode press SHIFT-F(5)) => { state.border_type = match state.border_type { None => Some((BorderType::QuadrantOutside, border::QUADRANT_OUTSIDE)), Some((BorderType::Plain, border::PLAIN)) => None, Some((BorderType::Plain, border::ONE_EIGHTH_TALL)) => { Some((BorderType::Plain, border::PLAIN)) } Some((BorderType::Plain, border::ONE_EIGHTH_WIDE)) => { Some((BorderType::Plain, border::ONE_EIGHTH_TALL)) } Some((BorderType::Plain, border::PROPORTIONAL_WIDE)) => { Some((BorderType::Plain, border::ONE_EIGHTH_WIDE)) } Some((BorderType::Plain, border::PROPORTIONAL_TALL)) => { Some((BorderType::Plain, border::PROPORTIONAL_WIDE)) } Some((BorderType::Double, border::DOUBLE)) => { Some((BorderType::Plain, border::PROPORTIONAL_TALL)) } Some((BorderType::Rounded, border::ROUNDED)) => { Some((BorderType::Double, border::DOUBLE)) } Some((BorderType::Thick, border::THICK)) => { Some((BorderType::Rounded, border::ROUNDED)) } Some((BorderType::QuadrantInside, border::QUADRANT_INSIDE)) => { Some((BorderType::Thick, border::THICK)) } Some((BorderType::QuadrantOutside, border::QUADRANT_OUTSIDE)) => { Some((BorderType::QuadrantInside, border::QUADRANT_INSIDE)) } _ => Some((BorderType::Plain, border::PLAIN)), }; Outcome::Changed } _ => Outcome::Continue, }); flow!(HandleEvent::handle(&mut state.tabbed, event, Regular)); if let Some(sel) = state.tabbed.selected() { match sel { 0 => flow!(state.tabs_0.handle(event, Regular)), 1 => flow!(state.tabs_1.handle(event, Regular)), 2 => flow!(state.tabs_2.handle(event, Regular)), _ => {} } } flow!(match state.menu.handle(event, Regular) { MenuOutcome::Activated(0) => { istate.quit = true; Outcome::Changed } _ => { Outcome::Continue } }); Outcome::Continue }); Ok(r) } static LIST: [&str; 28] = [ "Advisory: Ratatui / Crossterm Version incompatibility (0.27 / 0.28 and beyond) #1298 · joshka opened on Aug 6, 2024", "Examples may not be compatible with the latest release #779 · joshka opened on Jan 10, 2024", "Ratatui's Vision #1321 · orhun opened on Aug 13, 2024", "Convert the buffer type into a trait with the current buffer as one of it's implementations Type: Enhancement Status: Open. #1450 In ratatui/ratatui;· giocri opened on Oct 25, 2024", "Canvas rendering issue when area is huge Type: Bug Status: Open. #1449 In ratatui/ratatui;· JeromeSchmied opened on Oct 22, 2024", "Checkbox and Radio button widgets Status: Design Needed Type: Enhancement Status: Open. #1446 In ratatui/ratatui;· fujiapple852 opened on Oct 21, 2024", "Stylize the highlight_symbol in List Widget Effort: Good First Issue Type: Enhancement Status: Open. #1443 In ratatui/ratatui;· aktaboot opened on Oct 21, 2024", "Implement Rect::resize similar to Rect::offset Type: Enhancement Status: Open. #1440 In ratatui/ratatui;· kardwen opened on Oct 20, 2024", "Inline viewport should support an Terminal::insert_lines_before method. Type: Enhancement Status: Open. #1426 In ratatui/ratatui;· nfachan opened on Oct 17, 2024", "Rendering Caching needs to be one level deeper Priority: Low Type: Bug Status: Open. #1405 In ratatui/ratatui;· joshka opened on Oct 6, 2024", "Support asserting TestBackend buffer with color Effort: Good First Issue Type: Enhancement Status: Open. #1402 In ratatui/ratatui;· orhun opened on Oct 5, 2024", "Introduce new way to compare visually output of layout tests Type: Enhancement Status: Open. #1400 In ratatui/ratatui;· kdheepak opened on Oct 3, 2024", "Korean characters are not rendered correctly Type: Bug Status: Open. #1396 In ratatui/ratatui;· sxyazi opened on Oct 2, 2024", "GraphType::Bar does not work when y-axis minimum bound is > 0 Type: Bug Status: Open. #1391 In ratatui/ratatui;· Yomguithereal opened on Sep 30, 2024", "Modularize Ratatui crate Type: Enhancement Type: RFC Status: Open. #1388 In ratatui/ratatui;· joshka opened on Sep 27, 2024", "Most examples don't work or look bad in macOS's Terminal.app Type: Bug Status: Open. #1387 In ratatui/ratatui;· ccbrown opened on Sep 25, 2024", "Scrolling the list widget by page Effort: Difficult Status: Design Needed Type: Enhancement Status: Open. #1370 In ratatui/ratatui;· francisdb opened on Sep 12, 2024", "insert_before dynamic height based on content Type: Enhancement Status: Open. #1365 In ratatui/ratatui;· staehle opened on Sep 11, 2024", "Add support for Dash borders Effort: Easy Effort: Good First Issue Type: Enhancement Status: Open. #1355 In ratatui/ratatui;· 0xfalafel opened on Sep 6, 2024", "dweatherstone crossterm::style::Stylize is imported instead of ratatui::style::Stylize Type: Bug Status: Open. #1347 In ratatui/ratatui;· orhun opened on Aug 28, 2024", "underline_color() on an input field makes the screen flicker from that line to the bottom Status: Pending Type: Bug Status: Open. #1346 In ratatui/ratatui;· mazzi opened on Aug 27, 2024", "Extremely high CPU usage of the terminal.draw method Type: Bug Status: Open. #1338 In ratatui/ratatui;· gtema opened on Aug 23, 2024", "insert_before and emoji width empty space issue Type: Bug Status: Open. #1332 In ratatui/ratatui;· Kongga666 opened on Aug 20, 2024", "Ratatui's Vision Type: Enhancement Status: Open. #1321 In ratatui/ratatui;· orhun opened on Aug 13, 2024", "Feature request: Add feature flags for backends Type: Enhancement Status: Open. #1319 In ratatui/ratatui;· nick42d opened on Aug 11, 2024", "Advisory: Ratatui / Crossterm Version incompatibility (0.27 / 0.28 and beyond) Type: Documentation Status: Open. #1298 In ratatui/ratatui;· joshka opened on Aug 6, 2024", "Allow reversing Chart Legend direction Type: Enhancement Status: Open. #1290 In ratatui/ratatui;· nullstalgia opened on Aug 5, 2024", "WidgetRef / StatefulWidgetRef tracking issue Type: Documentation Status: Open. #1287 In ratatui/ratatui;· joshka opened on Aug 5, 2024", ];