@@ -8,157 +8,35 @@ use figment::{
88} ;
99use std:: path:: PathBuf ;
1010
11- use crossterm:: {
12- event:: { self , DisableMouseCapture , EnableMouseCapture , Event , KeyCode } ,
13- execute,
14- terminal:: { disable_raw_mode, enable_raw_mode, EnterAlternateScreen , LeaveAlternateScreen } ,
15- } ;
16- use ratatui:: {
17- backend:: CrosstermBackend ,
18- layout:: { Constraint , Direction , Layout } ,
19- style:: { Color , Modifier , Style } ,
20- text:: Span ,
21- widgets:: { Block , Borders , List , ListItem , ListState } ,
22- Terminal ,
23- } ;
24- use std:: { error:: Error , io} ;
25-
26- /// Main event loop for our TUI
27- fn run_app (
28- terminal : & mut Terminal < CrosstermBackend < io:: Stdout > > ,
29- menu_items : Vec < & str > ,
30- list_state : & mut ListState ,
31- ) -> Result < ( ) , Box < dyn Error > > {
32- loop {
33- // Draw the UI
34- terminal. draw ( |f| {
35- let size = f. size ( ) ;
36-
37- // Split the layout if you want multiple sections, but here we just use one
38- let chunks = Layout :: default ( )
39- . direction ( Direction :: Vertical )
40- . margin ( 1 )
41- . constraints ( [ Constraint :: Min ( 0 ) ] . as_ref ( ) )
42- . split ( size) ;
43-
44- // Prepare list items
45- let items: Vec < ListItem > = menu_items
46- . iter ( )
47- . map ( |item| ListItem :: new ( Span :: raw ( * item) ) )
48- . collect ( ) ;
49-
50- // Create the list widget
51- let list = List :: new ( items)
52- . block (
53- Block :: default ( )
54- . title ( "Ratatui List Selector" )
55- . borders ( Borders :: ALL ) ,
56- )
57- . highlight_style (
58- Style :: default ( )
59- . fg ( Color :: Yellow )
60- . add_modifier ( Modifier :: BOLD ) ,
61- )
62- . highlight_symbol ( ">> " ) ;
63-
64- // Render the stateful list widget
65- f. render_stateful_widget ( list, chunks[ 0 ] , list_state) ;
66- } ) ?;
67-
68- // Handle input
69- if crossterm:: event:: poll ( std:: time:: Duration :: from_millis ( 100 ) ) ? {
70- if let Event :: Key ( key) = event:: read ( ) ? {
71- match key. code {
72- KeyCode :: Up => {
73- // Move selection up
74- let i = match list_state. selected ( ) {
75- Some ( i) => {
76- if i == 0 {
77- menu_items. len ( ) - 1
78- } else {
79- i - 1
80- }
81- }
82- None => 0 ,
83- } ;
84- list_state. select ( Some ( i) ) ;
85- }
86- KeyCode :: Down => {
87- // Move selection down
88- let i = match list_state. selected ( ) {
89- Some ( i) => {
90- if i >= menu_items. len ( ) - 1 {
91- 0
92- } else {
93- i + 1
94- }
95- }
96- None => 0 ,
97- } ;
98- list_state. select ( Some ( i) ) ;
99- }
100- KeyCode :: Char ( 'q' ) => {
101- // Quit on 'q'
102- return Ok ( ( ) ) ;
103- }
104- KeyCode :: Enter => {
105- // If you want to handle pressing Enter on a selection,
106- // you could do so here.
107- if let Some ( selected) = list_state. selected ( ) {
108- println ! ( "You selected: {}" , menu_items[ selected] ) ;
109- return Ok ( ( ) ) ;
110- }
111- }
112- _ => { }
113- }
114- }
115- }
116- }
117- }
118-
119- fn run ( ) -> Result < ( ) , Box < dyn Error > > {
120- // Set up the terminal
121- enable_raw_mode ( ) ?;
122- let mut stdout = io:: stdout ( ) ;
123- execute ! ( stdout, EnterAlternateScreen , EnableMouseCapture ) ?;
124- let backend = CrosstermBackend :: new ( stdout) ;
125- let mut terminal = Terminal :: new ( backend) ?;
126-
127- // Create application state
128- let menu_items = vec ! [ "Option 1" , "Option 2" , "Option 3" , "Option 4" , "Option 5" ] ;
129- let mut list_state = ListState :: default ( ) ;
130- // By default, select the first item
131- list_state. select ( Some ( 0 ) ) ;
11+ extern crate skim;
12+ use skim:: prelude:: * ;
13+ use std:: io:: Cursor ;
13214
133- // Main loop
134- let res = run_app ( & mut terminal, menu_items, & mut list_state) ;
135-
136- // Restore the terminal
137- disable_raw_mode ( ) ?;
138- execute ! (
139- terminal. backend_mut( ) ,
140- LeaveAlternateScreen ,
141- DisableMouseCapture
142- ) ?;
143- terminal. show_cursor ( ) ?;
144-
145- // If there was an error inside the TUI, return it
146- if let Err ( err) = res {
147- println ! ( "{:?}" , err)
148- }
149-
150- Ok ( ( ) )
151- }
152-
153- pub fn handle ( matches : ArgMatches , machines_file : PathBuf , terminal_size : TerminalSize ) {
15+ pub fn handle ( _matches : ArgMatches , machines_file : PathBuf , _terminal_size : TerminalSize ) {
15416 if let Some ( proj_dirs) = ProjectDirs :: from ( "com" , "bresilla" , "dotpilot" ) {
15517 proj_dirs. config_dir ( ) ;
15618 }
15719
20+ let options = SkimOptionsBuilder :: default ( )
21+ . height ( String :: from ( "100%" ) )
22+ . multi ( true )
23+ . build ( )
24+ . unwrap ( ) ;
25+
15826 let machines: Machines = Figment :: new ( )
15927 . merge ( Toml :: file ( & machines_file) )
16028 . extract ( )
16129 . unwrap ( ) ;
16230
163- run ( ) . unwrap ( ) ;
31+ let item_reader = SkimItemReader :: default ( ) ;
32+ let items = item_reader. of_bufread ( Cursor :: new ( machines. to_listed ( ) ) ) ;
33+
34+ // `run_with` would read and show items from the stream
35+ let selected_items = Skim :: run_with ( & options, Some ( items) )
36+ . map ( |out| out. selected_items )
37+ . unwrap_or_else ( || Vec :: new ( ) ) ;
38+
39+ for item in selected_items. iter ( ) {
40+ println ! ( "{}" , item. output( ) ) ;
41+ }
16442}
0 commit comments