@@ -118,8 +118,19 @@ impl StatefulWidget for ScrollView {
118118 fn render ( self , area : Rect , buf : & mut Buffer , state : & mut Self :: State ) {
119119 let ( mut x, mut y) = state. offset . into ( ) ;
120120 // ensure that we don't scroll past the end of the buffer in either direction
121- x = x. min ( self . buf . area . width . saturating_sub ( 1 ) ) ;
122- y = y. min ( self . buf . area . height . saturating_sub ( 1 ) ) ;
121+ let max_x_offset = self
122+ . buf
123+ . area
124+ . width
125+ . saturating_sub ( area. width . saturating_sub ( 1 ) ) ;
126+ let max_y_offset = self
127+ . buf
128+ . area
129+ . height
130+ . saturating_sub ( area. height . saturating_sub ( 1 ) ) ;
131+
132+ x = x. min ( max_x_offset) ;
133+ y = y. min ( max_y_offset) ;
123134 state. offset = ( x, y) . into ( ) ;
124135 state. size = Some ( self . size ) ;
125136 state. page_size = Some ( area. into ( ) ) ;
@@ -135,6 +146,7 @@ impl ScrollView {
135146 /// scrollbars
136147 fn render_scrollbars ( & self , area : Rect , buf : & mut Buffer , state : & mut ScrollViewState ) -> Rect {
137148 let size = self . size ;
149+
138150 let width = size. width . saturating_sub ( area. width ) ;
139151 let height = size. height . saturating_sub ( area. height ) ;
140152 match ( width, height) {
@@ -147,46 +159,48 @@ impl ScrollView {
147159 // area is taller and narrower than the scroll_view
148160 state. offset . y = 0 ;
149161 self . render_horizontal_scrollbar ( area, buf, state) ;
150- Rect :: new ( state. offset . x , 0 , area. width , area. height - 1 )
162+ Rect :: new ( state. offset . x , 0 , area. width , area. height . saturating_sub ( 1 ) )
151163 }
152164 ( 0 , _) if area. width > size. width => {
153165 // area is wider and shorter than the scroll_view
154166 state. offset . x = 0 ;
155167 self . render_vertical_scrollbar ( area, buf, state) ;
156- Rect :: new ( 0 , state. offset . y , area. width - 1 , area. height )
168+ Rect :: new ( 0 , state. offset . y , area. width . saturating_sub ( 1 ) , area. height )
157169 }
158170 ( _, _) => {
159171 // scroll_view is both wider and taller than the area
160172 let vertical_area = Rect {
161- height : area. height - 1 ,
173+ height : area. height . saturating_sub ( 1 ) ,
162174 ..area
163175 } ;
164176 let horizontal_area = Rect {
165- width : area. width - 1 ,
177+ width : area. width . saturating_sub ( 1 ) ,
166178 ..area
167179 } ;
168180 self . render_vertical_scrollbar ( vertical_area, buf, state) ;
169181 self . render_horizontal_scrollbar ( horizontal_area, buf, state) ;
170182 Rect :: new (
171183 state. offset . x ,
172184 state. offset . y ,
173- area. width - 1 ,
174- area. height - 1 ,
185+ area. width . saturating_sub ( 1 ) ,
186+ area. height . saturating_sub ( 1 ) ,
175187 )
176188 }
177189 }
178190 }
179191
180192 fn render_vertical_scrollbar ( & self , area : Rect , buf : & mut Buffer , state : & ScrollViewState ) {
193+ let scrollbar_height = self . size . height . saturating_sub ( area. height ) ;
181194 let mut scrollbar_state =
182- ScrollbarState :: new ( self . size . height as usize ) . position ( state. offset . y as usize ) ;
195+ ScrollbarState :: new ( scrollbar_height as usize ) . position ( state. offset . y as usize ) ;
183196 let scrollbar = Scrollbar :: new ( ScrollbarOrientation :: VerticalRight ) ;
184197 scrollbar. render ( area, buf, & mut scrollbar_state) ;
185198 }
186199
187200 fn render_horizontal_scrollbar ( & self , area : Rect , buf : & mut Buffer , state : & ScrollViewState ) {
201+ let scrollbar_width = self . size . width . saturating_sub ( area. width ) ;
188202 let mut scrollbar_state =
189- ScrollbarState :: new ( self . size . width as usize ) . position ( state. offset . x as usize ) ;
203+ ScrollbarState :: new ( scrollbar_width as usize ) . position ( state. offset . x as usize ) ;
190204 let scrollbar = Scrollbar :: new ( ScrollbarOrientation :: HorizontalBottom ) ;
191205 scrollbar. render ( area, buf, & mut scrollbar_state) ;
192206 }
@@ -246,10 +260,10 @@ mod tests {
246260 Buffer :: with_lines( vec![
247261 "ABCDE▲" ,
248262 "KLMNO█" ,
249- "UVWXY║ " ,
263+ "UVWXY█ " ,
250264 "EFGHI║" ,
251265 "OPQRS▼" ,
252- "◄█═ ═► " ,
266+ "◄██ ═► " ,
253267 ] )
254268 )
255269 }
@@ -264,10 +278,10 @@ mod tests {
264278 Buffer :: with_lines( vec![
265279 "DEFGH▲" ,
266280 "NOPQR█" ,
267- "XYZAB║ " ,
281+ "XYZAB█ " ,
268282 "HIJKL║" ,
269283 "RSTUV▼" ,
270- "◄═█═ ► " ,
284+ "◄═██ ► " ,
271285 ] )
272286 )
273287 }
@@ -283,9 +297,9 @@ mod tests {
283297 "EFGHI▲" ,
284298 "OPQRS║" ,
285299 "YZABC█" ,
286- "IJKLM║ " ,
300+ "IJKLM█ " ,
287301 "STUVW▼" ,
288- "◄█═ ═► " ,
302+ "◄██ ═► " ,
289303 ] )
290304 )
291305 }
@@ -325,9 +339,9 @@ mod tests {
325339 "UVWXYZABCD█" ,
326340 "EFGHIJKLMN█" ,
327341 "OPQRSTUVWX█" ,
328- "YZABCDEFGH║ " ,
329- "IJKLMNOPQR║ " ,
330- "STUVWXYZAB║ " ,
342+ "YZABCDEFGH█ " ,
343+ "IJKLMNOPQR█ " ,
344+ "STUVWXYZAB█ " ,
331345 "CDEFGHIJKL▼" ,
332346 ] )
333347 )
@@ -351,7 +365,7 @@ mod tests {
351365 "STUVWXYZA" ,
352366 "CDEFGHIJK" ,
353367 "MNOPQRSTU" ,
354- "◄████═══ ►" ,
368+ "◄███████ ►" ,
355369 ] )
356370 )
357371 }
@@ -370,11 +384,11 @@ mod tests {
370384 "KLMNOPQRS█" ,
371385 "UVWXYZABC█" ,
372386 "EFGHIJKLM█" ,
373- "OPQRSTUVW║ " ,
374- "YZABCDEFG║ " ,
387+ "OPQRSTUVW█ " ,
388+ "YZABCDEFG█ " ,
375389 "IJKLMNOPQ║" ,
376390 "STUVWXYZA▼" ,
377- "◄████═══ ► " ,
391+ "◄███████ ► " ,
378392 ] )
379393 )
380394 }
@@ -394,21 +408,21 @@ mod tests {
394408 "UVWXYZAB█" ,
395409 "EFGHIJKL█" ,
396410 "OPQRSTUV█" ,
397- "YZABCDEF║ " ,
398- "IJKLMNOP║ " ,
399- "STUVWXYZ║ " ,
411+ "YZABCDEF█ " ,
412+ "IJKLMNOP█ " ,
413+ "STUVWXYZ█ " ,
400414 "CDEFGHIJ▼" ,
401- "◄███══ ═► " ,
415+ "◄█████ ═► " ,
402416 ] )
403417 )
404418 }
405419
406420 /// The purpose of this test is to ensure that the buffer offset is correctly calculated when
407- /// rendering a scroll view into a buffer (i.e. the buffer offset is not always (0, 0))
421+ /// rendering a scroll view into a buffer (i.e. the buffer offset is not always (0, 0)).
408422 #[ rstest]
409423 fn ensure_buffer_offset_is_correct ( scroll_view : ScrollView ) {
410424 let mut buf = Buffer :: empty ( Rect :: new ( 0 , 0 , 20 , 20 ) ) ;
411- let mut state = ScrollViewState :: with_offset ( ( 3 , 4 ) . into ( ) ) ;
425+ let mut state = ScrollViewState :: with_offset ( ( 2 , 3 ) . into ( ) ) ;
412426 scroll_view. render ( Rect :: new ( 5 , 6 , 7 , 8 ) , & mut buf, & mut state) ;
413427 assert_eq ! (
414428 buf,
@@ -419,14 +433,14 @@ mod tests {
419433 " " ,
420434 " " ,
421435 " " ,
422- " RSTUVW ▲ " ,
423- " BCDEFG ║ " ,
424- " LMNOPQ █ " ,
425- " VWXYZA █ " ,
426- " FGHIJK║ " ,
427- " PQRSTU║ " ,
428- " ▼ " ,
429- " ◄═█══ ► " ,
436+ " GHIJKL ▲ " ,
437+ " QRSTUV ║ " ,
438+ " ABCDEF █ " ,
439+ " KLMNOP █ " ,
440+ " UVWXYZ█ " ,
441+ " EFGHIJ█ " ,
442+ " OPQRST ▼ " ,
443+ " ◄═███ ► " ,
430444 " " ,
431445 " " ,
432446 " " ,
@@ -436,4 +450,36 @@ mod tests {
436450 ] )
437451 )
438452 }
453+ /// The purpose of this test is to ensure that the last elements are rendered.
454+ #[ rstest]
455+ fn ensure_buffer_last_elements ( scroll_view : ScrollView ) {
456+ let mut buf = Buffer :: empty ( Rect :: new ( 0 , 0 , 6 , 6 ) ) ;
457+ let mut state = ScrollViewState :: with_offset ( ( 5 , 5 ) . into ( ) ) ;
458+ scroll_view. render ( buf. area , & mut buf, & mut state) ;
459+ assert_eq ! (
460+ buf,
461+ Buffer :: with_lines( vec![
462+ "DEFGH▲" ,
463+ "NOPQR║" ,
464+ "XYZAB█" ,
465+ "HIJKL█" ,
466+ "RSTUV▼" ,
467+ "◄═██► " ,
468+ ] )
469+ )
470+ }
471+ #[ rstest]
472+ #[ should_panic( expected = "Scrollbar area is empty" ) ]
473+ fn zero_width ( scroll_view : ScrollView ) {
474+ let mut buf = Buffer :: empty ( Rect :: new ( 0 , 0 , 0 , 10 ) ) ;
475+ let mut state = ScrollViewState :: new ( ) ;
476+ scroll_view. render ( buf. area , & mut buf, & mut state) ;
477+ }
478+ #[ rstest]
479+ #[ should_panic( expected = "Scrollbar area is empty" ) ]
480+ fn zero_height ( scroll_view : ScrollView ) {
481+ let mut buf = Buffer :: empty ( Rect :: new ( 0 , 0 , 10 , 0 ) ) ;
482+ let mut state = ScrollViewState :: new ( ) ;
483+ scroll_view. render ( buf. area , & mut buf, & mut state) ;
484+ }
439485}
0 commit comments