Skip to content

Commit de7b934

Browse files
committed
refactor and improve ScrollbarMargin.create_margin
1 parent df8246c commit de7b934

File tree

1 file changed

+45
-49
lines changed

1 file changed

+45
-49
lines changed

Diff for: prompt_toolkit/layout/margins.py

+45-49
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44
from abc import ABCMeta, abstractmethod
55
from typing import TYPE_CHECKING, Callable, Optional
6+
import math
67

78
from prompt_toolkit.filters import FilterOrBool, to_filter
89
from prompt_toolkit.formatted_text import (
@@ -181,66 +182,61 @@ def create_margin(
181182
self, window_render_info: "WindowRenderInfo", width: int, height: int
182183
) -> StyleAndTextTuples:
183184
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+
184194
window_height = window_render_info.window_height
185195
display_arrows = self.display_arrows()
186196

187197
if display_arrows:
188198
window_height -= 2
189199

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 = []
207217

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"))
217221

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, " "))
237225
result.append(("", "\n"))
238226

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)
242230

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
244240

245241

246242
class PromptMargin(Margin):

0 commit comments

Comments
 (0)