@@ -16,6 +16,7 @@ use crate::{
1616} ;
1717
1818const MAX_NUMBER_ALERTS : usize = 5 ;
19+ const MIN_HEIGHT : u16 = 2 ;
1920
2021#[ derive( Clone , Debug , Eq , PartialEq ) ]
2122enum AlertKind {
@@ -24,29 +25,28 @@ enum AlertKind {
2425 Error ,
2526}
2627
28+ impl AlertKind {
29+ fn to_style ( & self , theme : & Theme ) -> Style {
30+ match self {
31+ AlertKind :: Info => theme. alert_info ( ) ,
32+ AlertKind :: Warn => theme. alert_warning ( ) ,
33+ AlertKind :: Error => theme. alert_error ( ) ,
34+ }
35+ }
36+ }
37+
2738#[ derive( Default ) ]
2839pub ( super ) struct AlertsView {
2940 alerts : VecDeque < ( AlertKind , String ) > ,
3041 area : Rect ,
3142}
3243
3344impl AlertsView {
34- pub ( super ) fn height ( & self , width : u16 ) -> u16 {
35- if self . should_show ( ) {
36- // TODO cache `self.list_items()` result for use in render()
37- let width = width. saturating_sub ( 2 ) ; // -2 for horizontal borders
38- let items = self . list_items ( width) ;
39- items. len ( ) as u16 + 2 // +2 for vertical borders
40- } else {
41- 0
42- }
43- }
44-
4545 fn add_alert ( & mut self , kind : AlertKind , message : String ) -> CommandResult {
4646 if self . alerts . len ( ) == MAX_NUMBER_ALERTS {
47- self . alerts . pop_front ( ) ;
47+ self . alerts . pop_back ( ) ;
4848 }
49- self . alerts . push_back ( ( kind, message) ) ;
49+ self . alerts . push_front ( ( kind, message) ) ;
5050 CommandResult :: none ( )
5151 }
5252
@@ -55,32 +55,35 @@ impl AlertsView {
5555 CommandResult :: none ( )
5656 }
5757
58- fn list_items ( & self , width : u16 ) -> Vec < ( Line < ' _ > , AlertKind ) > {
58+ fn height ( & self , area : & Rect ) -> u16 {
59+ if !self . should_show ( area) {
60+ return 0 ;
61+ }
62+ // First subtract borders from the outer area
63+ let inner_width = area. width . saturating_sub ( 2 ) ;
64+ let items = self . alerts ( inner_width) ;
65+ items. len ( ) as u16 + 2 // +2 for vertical borders
66+ }
67+
68+ fn alerts ( & self , width_without_borders : u16 ) -> Vec < ( AlertKind , Line < ' _ > ) > {
69+ let width_without_prefix = width_without_borders. saturating_sub ( 2 ) ;
70+
5971 self . alerts
6072 . iter ( )
61- . rev ( ) // Newest alert messages near the top
6273 . flat_map ( |( kind, message) | {
63- split_with_ellipsis ( message, width . saturating_sub ( 2 ) )
74+ split_with_ellipsis ( message, width_without_prefix )
6475 . into_iter ( )
6576 . enumerate ( )
6677 . map ( |( i, line) | {
6778 let prefix = if i == 0 { "•" } else { " " } ;
68- ( Line :: from ( format ! ( "{prefix} {line}" ) ) , kind . clone ( ) )
79+ ( kind . clone ( ) , Line :: from ( format ! ( "{prefix} {line}" ) ) )
6980 } )
7081 } )
7182 . collect ( )
7283 }
7384
74- fn should_show ( & self ) -> bool {
75- !self . alerts . is_empty ( )
76- }
77-
78- fn get_style ( & self , kind : & AlertKind , theme : & Theme ) -> Style {
79- match kind {
80- AlertKind :: Info => theme. alert_info ( ) ,
81- AlertKind :: Warn => theme. alert_warning ( ) ,
82- AlertKind :: Error => theme. alert_error ( ) ,
83- }
85+ fn should_show ( & self , area : & Rect ) -> bool {
86+ !self . alerts . is_empty ( ) && area. height >= MIN_HEIGHT
8487 }
8588}
8689
@@ -103,7 +106,6 @@ impl CommandHandler for AlertsView {
103106 fn handle_mouse ( & mut self , event : & MouseEvent ) -> CommandResult {
104107 match event. kind {
105108 MouseEventKind :: Down ( MouseButton :: Left ) => {
106- // `self.should_receive_mouse()` guards this method to ensure that the click intersects with this view.
107109 self . clear_alerts ( ) ;
108110 CommandResult :: none ( )
109111 }
@@ -118,25 +120,27 @@ impl CommandHandler for AlertsView {
118120
119121impl View for AlertsView {
120122 fn constraint ( & self , area : Rect , _: & InputMode ) -> Constraint {
121- Constraint :: Length ( self . height ( area. width ) )
123+ Constraint :: Length ( self . height ( & area) )
122124 }
123125
124126 fn render ( & mut self , area : Rect , buf : & mut Buffer , _: & InputMode , theme : & Theme ) {
125- self . area = area;
126- if !self . should_show ( ) {
127+ if !self . should_show ( & area) {
127128 return ;
128129 }
129-
130- let bordered_area = bordered ( buf, area, theme. alert ( ) , Some ( "Alerts" . into ( ) ) ) ;
131- let items = self . list_items ( bordered_area. width ) ;
132- let mut text: Text < ' _ > = Text :: default ( ) ;
133-
134- for ( line, kind) in items {
135- let style = self . get_style ( & kind, theme) ;
136- text. lines . push ( Line :: from ( line. spans ) . style ( style) ) ;
137- }
138-
139- let widget = Paragraph :: new ( text) ;
130+ self . area = area;
131+ let bordered_area = bordered (
132+ buf,
133+ area,
134+ theme. alert ( ) ,
135+ Some ( "Alerts (Press \" a\" to clear)" . into ( ) ) ,
136+ ) ;
137+ let text = Text :: from (
138+ self . alerts ( bordered_area. width )
139+ . into_iter ( )
140+ . map ( |( kind, line) | line. style ( kind. to_style ( theme) ) )
141+ . collect :: < Vec < _ > > ( ) ,
142+ ) ;
143+ let widget = Paragraph :: new ( text) . style ( theme. alert ( ) ) ;
140144 widget. render ( bordered_area, buf) ;
141145 }
142146}
0 commit comments