@@ -63,11 +63,20 @@ pub struct Home {
6363pub struct MenuItem {
6464 pub name : String ,
6565 pub action : Action ,
66+ pub key : Option < KeyCode > ,
6667}
6768
6869impl MenuItem {
69- pub fn new ( name : & str , action : Action ) -> Self {
70- Self { name : name. to_owned ( ) , action }
70+ pub fn new ( name : & str , action : Action , key : Option < KeyCode > ) -> Self {
71+ Self { name : name. to_owned ( ) , action, key }
72+ }
73+
74+ pub fn key_string ( & self ) -> String {
75+ if let Some ( key) = self . key {
76+ format ! ( "{}" , key)
77+ } else {
78+ String :: new ( )
79+ }
7180 }
7281}
7382
@@ -500,6 +509,14 @@ impl Component for Home {
500509 vec ! [ Action :: Render ]
501510 } ,
502511 KeyCode :: Char ( '/' ) => vec ! [ Action :: EnterMode ( Mode :: Search ) ] ,
512+ KeyCode :: Char ( 'e' ) => {
513+ if let Some ( selected) = self . filtered_units . selected ( ) {
514+ if let Some ( Ok ( file_path) ) = & selected. file_path {
515+ return vec ! [ Action :: EditUnitFile { unit: selected. id( ) , path: file_path. clone( ) } ] ;
516+ }
517+ }
518+ vec ! [ ]
519+ } ,
503520 KeyCode :: Enter | KeyCode :: Char ( ' ' ) => vec ! [ Action :: EnterMode ( Mode :: ActionMenu ) ] ,
504521 _ => vec ! [ ] ,
505522 }
@@ -548,7 +565,16 @@ impl Component for Home {
548565 Some ( i) => vec ! [ i. action. clone( ) ] ,
549566 None => vec ! [ Action :: EnterMode ( Mode :: ServiceList ) ] ,
550567 } ,
551- _ => vec ! [ ] ,
568+ _ => {
569+ for item in self . menu_items . items . iter ( ) {
570+ if let Some ( key_code) = item. key {
571+ if key_code == key. code {
572+ return vec ! [ item. action. clone( ) ] ;
573+ }
574+ }
575+ }
576+ vec ! [ ]
577+ } ,
552578 } ,
553579 Mode :: Processing => match key. code {
554580 KeyCode :: Esc => vec ! [ Action :: CancelTask ] ,
@@ -567,20 +593,25 @@ impl Component for Home {
567593 if mode == Mode :: ActionMenu {
568594 if let Some ( selected) = self . filtered_units . selected ( ) {
569595 let mut menu_items = vec ! [
570- MenuItem :: new( "Start" , Action :: StartService ( selected. id( ) ) ) ,
571- MenuItem :: new( "Stop" , Action :: StopService ( selected. id( ) ) ) ,
572- MenuItem :: new( "Restart" , Action :: RestartService ( selected. id( ) ) ) ,
573- MenuItem :: new( "Reload" , Action :: ReloadService ( selected. id( ) ) ) ,
596+ MenuItem :: new( "Start" , Action :: StartService ( selected. id( ) ) , Some ( KeyCode :: Char ( 's' ) ) ) ,
597+ MenuItem :: new( "Stop" , Action :: StopService ( selected. id( ) ) , Some ( KeyCode :: Char ( 't' ) ) ) ,
598+ MenuItem :: new( "Restart" , Action :: RestartService ( selected. id( ) ) , Some ( KeyCode :: Char ( 'r' ) ) ) ,
599+ MenuItem :: new( "Reload" , Action :: ReloadService ( selected. id( ) ) , Some ( KeyCode :: Char ( 'l' ) ) ) ,
574600 // TODO add these
575601 // MenuItem::new("Enable", Action::EnableService(selected.clone())),
576602 // MenuItem::new("Disable", Action::DisableService(selected.clone())),
577603 ] ;
578604
579605 if let Some ( Ok ( file_path) ) = & selected. file_path {
580- menu_items. push ( MenuItem :: new ( "Copy unit file path to clipboard" , Action :: CopyUnitFilePath ) ) ;
606+ menu_items. push ( MenuItem :: new (
607+ "Copy unit file path to clipboard" ,
608+ Action :: CopyUnitFilePath ,
609+ Some ( KeyCode :: Char ( 'c' ) ) ,
610+ ) ) ;
581611 menu_items. push ( MenuItem :: new (
582612 "Edit unit file" ,
583613 Action :: EditUnitFile { unit : selected. id ( ) , path : file_path. clone ( ) } ,
614+ Some ( KeyCode :: Char ( 'e' ) ) ,
584615 ) ) ;
585616 }
586617
@@ -695,6 +726,18 @@ impl Component for Home {
695726 }
696727
697728 fn render ( & mut self , f : & mut Frame < ' _ > , rect : Rect ) {
729+ fn primary ( s : & str ) -> Span {
730+ Span :: styled ( s, Style :: default ( ) . fg ( Color :: Cyan ) )
731+ }
732+
733+ fn span ( s : & str , color : Color ) -> Span {
734+ Span :: styled ( s, Style :: default ( ) . fg ( color) )
735+ }
736+
737+ fn colored_line ( value : & str , color : Color ) -> Line {
738+ Line :: from ( vec ! [ Span :: styled( value, Style :: default ( ) . fg( color) ) ] )
739+ }
740+
698741 let rect = if self . show_logger {
699742 let chunks = Layout :: new ( Direction :: Vertical , Constraint :: from_percentages ( [ 50 , 50 ] ) ) . split ( rect) ;
700743
@@ -704,13 +747,12 @@ impl Component for Home {
704747 rect
705748 } ;
706749
707- let rects = Layout :: new ( Direction :: Vertical , [ Constraint :: Min ( 3 ) , Constraint :: Percentage ( 100 ) ] ) . split ( rect) ;
750+ let rects =
751+ Layout :: new ( Direction :: Vertical , [ Constraint :: Min ( 3 ) , Constraint :: Percentage ( 100 ) , Constraint :: Length ( 1 ) ] )
752+ . split ( rect) ;
708753 let search_panel = rects[ 0 ] ;
709754 let main_panel = rects[ 1 ] ;
710-
711- fn colored_line ( value : & str , color : Color ) -> Line {
712- Line :: from ( vec ! [ Span :: styled( value, Style :: default ( ) . fg( color) ) ] )
713- }
755+ let help_line_rect = rects[ 2 ] ;
714756
715757 // Helper for colouring based on the same logic as sysz
716758 // https://github.com/joehillen/sysz/blob/8da8e0dcbfde8d68fbdb22382671e395bd370d69/sysz#L69C1-L72C24
@@ -840,7 +882,9 @@ impl Component for Home {
840882 . rev ( )
841883 . map ( |l| {
842884 if let Some ( ( date, rest) ) = l. splitn ( 2 , ' ' ) . collect_tuple ( ) {
843- if date. len ( ) != 24 {
885+ // This is not a good way to identify dates; the length can vary by system.
886+ // TODO: find a better way to identify dates
887+ if date. len ( ) != 25 {
844888 return Line :: from ( l. as_str ( ) ) ;
845889 }
846890 Line :: from ( vec ! [ Span :: styled( date, Style :: default ( ) . fg( Color :: DarkGray ) ) , Span :: raw( " " ) , Span :: raw( rest) ] )
@@ -898,10 +942,6 @@ impl Component for Home {
898942 if self . mode == Mode :: Help {
899943 let popup = centered_rect_abs ( 50 , 18 , f. area ( ) ) ;
900944
901- fn primary ( s : & str ) -> Span {
902- Span :: styled ( s, Style :: default ( ) . fg ( Color :: Cyan ) )
903- }
904-
905945 let help_lines = vec ! [
906946 Line :: from( "" ) ,
907947 Line :: from( Span :: styled( "Shortcuts" , Style :: default ( ) . add_modifier( Modifier :: UNDERLINED ) ) ) ,
@@ -955,6 +995,28 @@ impl Component for Home {
955995 None => return ,
956996 } ;
957997
998+ // Help line at the bottom
999+
1000+ let version = format ! ( "v{}" , env!( "CARGO_PKG_VERSION" ) ) ;
1001+
1002+ let help_line_rects =
1003+ Layout :: new ( Direction :: Horizontal , [ Constraint :: Fill ( 1 ) , Constraint :: Length ( version. len ( ) as u16 ) ] )
1004+ . split ( help_line_rect) ;
1005+ let help_rect = help_line_rects[ 0 ] ;
1006+ let version_rect = help_line_rects[ 1 ] ;
1007+
1008+ let help_line = match self . mode {
1009+ Mode :: Search => Line :: from ( span ( "Show actions: <enter>" , Color :: Blue ) ) ,
1010+ Mode :: ServiceList => Line :: from ( span ( "Show actions: <enter> | Open unit file: e | Quit: q" , Color :: Blue ) ) ,
1011+ Mode :: Help => Line :: from ( span ( "Close menu: <esc>" , Color :: Blue ) ) ,
1012+ Mode :: ActionMenu => Line :: from ( span ( "Execute action: <enter> | Close menu: <esc>" , Color :: Blue ) ) ,
1013+ Mode :: Processing => Line :: from ( span ( "Cancel task: <esc>" , Color :: Blue ) ) ,
1014+ Mode :: Error => Line :: from ( span ( "Close menu: <esc>" , Color :: Blue ) ) ,
1015+ } ;
1016+
1017+ f. render_widget ( help_line, help_rect) ;
1018+ f. render_widget ( Line :: from ( version) , version_rect) ;
1019+
9581020 let min_width = selected_item. name . len ( ) as u16 + 14 ;
9591021 let desired_width = min_width + 4 ; // idk, looks alright
9601022 let popup_width = desired_width. min ( f. area ( ) . width ) ;
@@ -963,7 +1025,16 @@ impl Component for Home {
9631025 let height = self . menu_items . items . len ( ) as u16 + 2 ;
9641026 let popup = centered_rect_abs ( popup_width, height, f. area ( ) ) ;
9651027
966- let items: Vec < ListItem > = self . menu_items . items . iter ( ) . map ( |i| ListItem :: new ( i. name . as_str ( ) ) ) . collect ( ) ;
1028+ let items: Vec < ListItem > = self
1029+ . menu_items
1030+ . items
1031+ . iter ( )
1032+ . map ( |i| {
1033+ let key_string = Span :: styled ( format ! ( " {:1} " , i. key_string( ) ) , Style :: default ( ) . fg ( Color :: Blue ) ) ;
1034+ let line = Line :: from ( vec ! [ key_string, Span :: raw( & i. name) ] ) ;
1035+ ListItem :: new ( line)
1036+ } )
1037+ . collect ( ) ;
9671038 let items = List :: new ( items)
9681039 . block (
9691040 Block :: default ( )
0 commit comments