|
3 | 3 | """
|
4 | 4 | from abc import ABCMeta, abstractmethod
|
5 | 5 | from typing import TYPE_CHECKING, Callable, Optional
|
| 6 | +import math |
6 | 7 |
|
7 | 8 | from prompt_toolkit.filters import FilterOrBool, to_filter
|
8 | 9 | from prompt_toolkit.formatted_text import (
|
@@ -181,66 +182,61 @@ def create_margin(
|
181 | 182 | self, window_render_info: "WindowRenderInfo", width: int, height: int
|
182 | 183 | ) -> StyleAndTextTuples:
|
183 | 184 | content_height = window_render_info.content_height
|
| 185 | + if content_height == 0: |
| 186 | + return [] |
| 187 | + |
| 188 | + # Scrollbar body. |
| 189 | + scrollbar_background = "class:scrollbar.background" |
| 190 | + scrollbar_background_start = "class:scrollbar.background,scrollbar.start" |
| 191 | + scrollbar_button = "class:scrollbar.button" |
| 192 | + scrollbar_button_end = "class:scrollbar.button,scrollbar.end" |
| 193 | + |
184 | 194 | window_height = window_render_info.window_height
|
185 | 195 | display_arrows = self.display_arrows()
|
186 | 196 |
|
187 | 197 | if display_arrows:
|
188 | 198 | window_height -= 2
|
189 | 199 |
|
190 |
| - try: |
191 |
| - fraction_visible = len(window_render_info.displayed_lines) / float( |
192 |
| - content_height |
193 |
| - ) |
194 |
| - fraction_above = window_render_info.vertical_scroll / float(content_height) |
195 |
| - |
196 |
| - scrollbar_height = int( |
197 |
| - min(window_height, max(1, window_height * fraction_visible)) |
198 |
| - ) |
199 |
| - scrollbar_top = int(window_height * fraction_above) |
200 |
| - except ZeroDivisionError: |
201 |
| - return [] |
202 |
| - else: |
203 |
| - |
204 |
| - def is_scroll_button(row: int) -> bool: |
205 |
| - "True if we should display a button on this row." |
206 |
| - return scrollbar_top <= row <= scrollbar_top + scrollbar_height |
| 200 | + fraction_visible = len(window_render_info.displayed_lines) / content_height |
| 201 | + |
| 202 | + fraction_above = window_render_info.vertical_scroll / content_height |
| 203 | + scrollbar_height = min( |
| 204 | + window_height, |
| 205 | + int(math.ceil(window_height * fraction_visible)), |
| 206 | + ) |
| 207 | + # Without math.ceil, the scrollbar will never reach the end. |
| 208 | + # Unless you increase the scrollbar height by one (like it was done |
| 209 | + # before, implicitly) which makes it less accurate, and may also |
| 210 | + # cause a misconception that there are no items after the last visible |
| 211 | + # item when scrollbar reaches the end before the last item is visible |
| 212 | + scrollbar_top = int(window_height * fraction_above) |
| 213 | + scrollbar_bottom = window_height - scrollbar_height - scrollbar_top |
| 214 | + |
| 215 | + # Up arrow. |
| 216 | + result: StyleAndTextTuples = [] |
207 | 217 |
|
208 |
| - # Up arrow. |
209 |
| - result: StyleAndTextTuples = [] |
210 |
| - if display_arrows: |
211 |
| - result.extend( |
212 |
| - [ |
213 |
| - ("class:scrollbar.arrow", self.up_arrow_symbol), |
214 |
| - ("class:scrollbar", "\n"), |
215 |
| - ] |
216 |
| - ) |
| 218 | + if display_arrows: |
| 219 | + result.append(("class:scrollbar.arrow", self.up_arrow_symbol)) |
| 220 | + result.append(("class:scrollbar", "\n")) |
217 | 221 |
|
218 |
| - # Scrollbar body. |
219 |
| - scrollbar_background = "class:scrollbar.background" |
220 |
| - scrollbar_background_start = "class:scrollbar.background,scrollbar.start" |
221 |
| - scrollbar_button = "class:scrollbar.button" |
222 |
| - scrollbar_button_end = "class:scrollbar.button,scrollbar.end" |
223 |
| - |
224 |
| - for i in range(window_height): |
225 |
| - if is_scroll_button(i): |
226 |
| - if not is_scroll_button(i + 1): |
227 |
| - # Give the last cell a different style, because we |
228 |
| - # want to underline this. |
229 |
| - result.append((scrollbar_button_end, " ")) |
230 |
| - else: |
231 |
| - result.append((scrollbar_button, " ")) |
232 |
| - else: |
233 |
| - if is_scroll_button(i + 1): |
234 |
| - result.append((scrollbar_background_start, " ")) |
235 |
| - else: |
236 |
| - result.append((scrollbar_background, " ")) |
| 222 | + def add_rows(count: int, style: str) -> None: |
| 223 | + for i in range(count): |
| 224 | + result.append((style, " ")) |
237 | 225 | result.append(("", "\n"))
|
238 | 226 |
|
239 |
| - # Down arrow |
240 |
| - if display_arrows: |
241 |
| - result.append(("class:scrollbar.arrow", self.down_arrow_symbol)) |
| 227 | + if scrollbar_top > 0: |
| 228 | + add_rows(scrollbar_top - 1, scrollbar_background) |
| 229 | + add_rows(1, scrollbar_background_start) |
242 | 230 |
|
243 |
| - return result |
| 231 | + add_rows(scrollbar_height - 1, scrollbar_button) |
| 232 | + add_rows(1, scrollbar_button_end) |
| 233 | + add_rows(scrollbar_bottom, scrollbar_background) |
| 234 | + |
| 235 | + # Down arrow |
| 236 | + if display_arrows: |
| 237 | + result.append(("class:scrollbar.arrow", self.down_arrow_symbol)) |
| 238 | + |
| 239 | + return result |
244 | 240 |
|
245 | 241 |
|
246 | 242 | class PromptMargin(Margin):
|
|
0 commit comments