1212// see the license for the specific language governing permissions and
1313// limitations under the license.
1414
15- use crossterm:: event:: KeyCode ;
16- use anyhow:: Result ;
17- use tui:: widgets:: { ListState , TableState } ;
1815use crate :: { filehost, serial} ;
16+ use anyhow:: Result ;
17+ use crossterm:: event:: KeyCode ;
1918use serialport:: SerialPort ;
19+ use tui:: widgets:: { ListState , TableState } ;
2020
2121pub mod terminal;
2222mod ui;
2323
24- pub struct FilesApp {
25- pub filetable : StatefulTable < filehost:: Record > ,
26- pub port : Box < dyn SerialPort > ,
27- toggle_sort : bool ,
28- /// Selected CBM disk
29- pub cbm_disk : Option < Box < dyn cbm:: disk:: Disk > > ,
30- /// Browser for files CBM disk images (d81 etc)
31- pub cbm_browser : StatefulList < String > ,
32- }
33-
34- impl FilesApp {
35- pub fn new ( port : & mut Box < dyn SerialPort > , filehost_items : & [ filehost:: Record ] ) -> FilesApp {
36- FilesApp {
37- filetable : StatefulTable :: with_items ( filehost_items. to_vec ( ) ) ,
38- port : port. try_clone ( ) . unwrap ( ) ,
39- toggle_sort : false ,
40- cbm_disk : None ,
41- cbm_browser : StatefulList :: with_items ( Vec :: < String > :: new ( ) ) ,
42- }
43- }
44-
45- pub fn keypress ( & mut self , key : crossterm:: event:: KeyCode ) -> Result < ( ) > {
46- match key {
47- KeyCode :: Down => self . filetable . next ( ) ,
48- KeyCode :: Up => self . filetable . previous ( ) ,
49- KeyCode :: Char ( 's' ) => self . sort_filehost ( ) ,
50- _ => { }
51- }
52- Ok ( ( ) )
53- }
54-
55- /// Toggles filehost file sorting by date or title
56- fn sort_filehost ( & mut self ) {
57- if self . toggle_sort {
58- self . filetable . items . sort_by_key ( |i| i. published . clone ( ) ) ;
59- self . filetable . items . reverse ( ) ;
60- } else {
61- self . filetable . items . sort_by_key ( |i| i. title . clone ( ) ) ;
62- }
63- self . toggle_sort = !self . toggle_sort ;
64- }
65-
66- pub fn selected_url ( & self ) -> String {
67- let sel = self . filetable . state . selected ( ) . unwrap_or ( 0 ) ;
68- let item = & self . filetable . items [ sel] ;
69- format ! ( "https://files.mega65.org/{}" , & item. location)
70- }
71-
72- /// Transfer and run selected file
73- pub fn run ( & mut self , reset_before_run : bool ) -> Result < ( ) > {
74- let url = self . selected_url ( ) ;
75- if url. ends_with ( ".prg" ) {
76- serial:: handle_prg ( & mut self . port , & url, reset_before_run, true ) ?;
77- } else if url. ends_with ( ".d81" ) & self . cbm_disk . is_some ( ) & self . cbm_browser . is_selected ( ) {
78- let selected_file = self . cbm_browser . state . selected ( ) . unwrap ( ) ;
79- let ( load_address, bytes) =
80- crate :: io:: cbm_load_file ( self . cbm_disk . as_ref ( ) . unwrap ( ) . as_ref ( ) , selected_file) ?;
81- serial:: handle_prg_from_bytes (
82- & mut self . port ,
83- & bytes,
84- load_address,
85- reset_before_run,
86- true ,
87- ) ?;
88- self . cbm_browser . unselect ( ) ;
89- self . cbm_disk = None ;
90- } else {
91- return Err ( anyhow:: Error :: msg ( "Cannot run selection" ) ) ;
92- }
93- Ok ( ( ) )
94- }
95- }
96-
9724/// Specified the currently active widget of the TUI
9825#[ derive( PartialEq , Eq ) ]
9926pub enum AppWidgets {
@@ -104,8 +31,6 @@ pub enum AppWidgets {
10431}
10532
10633pub struct App {
107- /// FileHost file browser
108- files : FilesApp ,
10934 /// Status messages presented in the UI
11035 messages : Vec < String > ,
11136 /// Holds the active widget
@@ -114,12 +39,21 @@ pub struct App {
11439 file_action : StatefulList < String > ,
11540 /// Set to true when UI is unresponsive
11641 busy : bool ,
42+ /// FileHost file browser
43+ pub filetable : StatefulTable < filehost:: Record > ,
44+ /// Serial port to communicate on
45+ pub port : Box < dyn SerialPort > ,
46+ /// Determines how to sort the filehost table
47+ toggle_sort : bool ,
48+ /// Selected CBM disk
49+ pub cbm_disk : Option < Box < dyn cbm:: disk:: Disk > > ,
50+ /// Browser for files CBM disk images (d81 etc)
51+ pub cbm_browser : StatefulList < String > ,
11752}
11853
11954impl App {
12055 fn new ( port : & mut Box < dyn SerialPort > , filehost_items : & [ filehost:: Record ] ) -> App {
12156 App {
122- files : FilesApp :: new ( port, filehost_items) ,
12357 messages : vec ! [
12458 "Matrix65 welcomes you to the FileHost!" . to_string( ) ,
12559 "Press 'h' for help" . to_string( ) ,
@@ -132,6 +66,11 @@ impl App {
13266 "Cancel" . to_string( ) ,
13367 ] ) ,
13468 busy : false ,
69+ filetable : StatefulTable :: with_items ( filehost_items. to_vec ( ) ) ,
70+ port : port. try_clone ( ) . unwrap ( ) ,
71+ toggle_sort : false ,
72+ cbm_disk : None ,
73+ cbm_browser : StatefulList :: with_items ( Vec :: < String > :: new ( ) ) ,
13574 }
13675 }
13776
@@ -143,15 +82,15 @@ impl App {
14382 fn activate_cbm_browser ( & mut self ) -> Result < ( ) > {
14483 self . busy = false ;
14584 self . set_current_widget ( AppWidgets :: CBMBrowser ) ;
146- let url = self . files . selected_url ( ) ;
147- self . files . cbm_disk = Some ( crate :: io:: cbm_open ( & url) ?) ;
148- if self . files . cbm_disk . is_some ( ) {
149- let dir = self . files . cbm_disk . as_ref ( ) . unwrap ( ) . directory ( ) ?;
85+ let url = self . selected_url ( ) ;
86+ self . cbm_disk = Some ( crate :: io:: cbm_open ( & url) ?) ;
87+ if self . cbm_disk . is_some ( ) {
88+ let dir = self . cbm_disk . as_ref ( ) . unwrap ( ) . directory ( ) ?;
15089 let files: Vec < String > = dir
15190 . iter ( )
15291 . map ( |i| format ! ( "{}.{}" , i. filename. to_string( ) , i. file_attributes. file_type) )
15392 . collect ( ) ;
154- self . files . cbm_browser . items = files;
93+ self . cbm_browser . items = files;
15594 }
15695 Ok ( ( ) )
15796 }
@@ -185,17 +124,17 @@ impl App {
185124 AppWidgets :: FileAction => {
186125 self . set_current_widget ( AppWidgets :: FileSelector ) ;
187126 match self . file_action . state . selected ( ) {
188- Some ( 0 ) => self . files . run ( false ) ?, // run
189- Some ( 1 ) => self . files . run ( true ) ?, // reset, then run
127+ Some ( 0 ) => self . run ( false ) ?, // run
128+ Some ( 1 ) => self . run ( true ) ?, // reset, then run
190129 Some ( 2 ) => self . activate_cbm_browser ( ) ?,
191130 _ => { }
192131 } ;
193132 self . file_action . unselect ( ) ;
194133 }
195134 AppWidgets :: CBMBrowser => {
196- match self . files . cbm_browser . state . selected ( ) {
135+ match self . cbm_browser . state . selected ( ) {
197136 _ => {
198- self . files . run ( false ) ?;
137+ self . run ( false ) ?;
199138 self . busy = false ;
200139 self . active_widget = AppWidgets :: FileSelector ;
201140 }
@@ -208,9 +147,25 @@ impl App {
208147 _ => { }
209148 }
210149 match self . active_widget {
211- AppWidgets :: CBMBrowser => self . files . cbm_browser . keypress ( key) ,
150+ AppWidgets :: CBMBrowser => self . cbm_browser . keypress ( key) ,
212151 AppWidgets :: FileAction => self . file_action . keypress ( key) ,
213- AppWidgets :: FileSelector => self . files . keypress ( key) ,
152+ AppWidgets :: FileSelector => {
153+ match key {
154+ KeyCode :: Down => {
155+ self . filetable . next ( ) ;
156+ Ok ( ( ) )
157+ } ,
158+ KeyCode :: Up => {
159+ self . filetable . previous ( ) ;
160+ Ok ( ( ) )
161+ } ,
162+ KeyCode :: Char ( 's' ) => {
163+ self . sort_filehost ( ) ;
164+ Ok ( ( ) )
165+ } ,
166+ _ => { Ok ( ( ) ) }
167+ }
168+ }
214169 _ => Ok ( ( ) ) ,
215170 }
216171 }
@@ -231,6 +186,47 @@ impl App {
231186 pub fn clear_status_line ( & mut self ) {
232187 //self.messages.clear();
233188 }
189+
190+ /// Toggles filehost file sorting by date or title
191+ fn sort_filehost ( & mut self ) {
192+ if self . toggle_sort {
193+ self . filetable . items . sort_by_key ( |i| i. published . clone ( ) ) ;
194+ self . filetable . items . reverse ( ) ;
195+ } else {
196+ self . filetable . items . sort_by_key ( |i| i. title . clone ( ) ) ;
197+ }
198+ self . toggle_sort = !self . toggle_sort ;
199+ }
200+
201+ pub fn selected_url ( & self ) -> String {
202+ let sel = self . filetable . state . selected ( ) . unwrap_or ( 0 ) ;
203+ let item = & self . filetable . items [ sel] ;
204+ format ! ( "https://files.mega65.org/{}" , & item. location)
205+ }
206+
207+ /// Transfer and run selected file
208+ pub fn run ( & mut self , reset_before_run : bool ) -> Result < ( ) > {
209+ let url = self . selected_url ( ) ;
210+ if url. ends_with ( ".prg" ) {
211+ serial:: handle_prg ( & mut self . port , & url, reset_before_run, true ) ?;
212+ } else if url. ends_with ( ".d81" ) & self . cbm_disk . is_some ( ) & self . cbm_browser . is_selected ( ) {
213+ let selected_file = self . cbm_browser . state . selected ( ) . unwrap ( ) ;
214+ let ( load_address, bytes) =
215+ crate :: io:: cbm_load_file ( self . cbm_disk . as_ref ( ) . unwrap ( ) . as_ref ( ) , selected_file) ?;
216+ serial:: handle_prg_from_bytes (
217+ & mut self . port ,
218+ & bytes,
219+ load_address,
220+ reset_before_run,
221+ true ,
222+ ) ?;
223+ self . cbm_browser . unselect ( ) ;
224+ self . cbm_disk = None ;
225+ } else {
226+ return Err ( anyhow:: Error :: msg ( "Cannot run selection" ) ) ;
227+ }
228+ Ok ( ( ) )
229+ }
234230}
235231
236232pub struct StatefulList < T > {
@@ -342,5 +338,4 @@ impl<T> StatefulTable<T> {
342338 pub fn unselect ( & mut self ) {
343339 self . state . select ( None ) ;
344340 }
345-
346341}
0 commit comments