scroll.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. """
  2. Key bindings, for scrolling up and down through pages.
  3. This are separate bindings, because GNU readline doesn't have them, but
  4. they are very useful for navigating through long multiline buffers, like in
  5. Vi, Emacs, etc...
  6. """
  7. from __future__ import unicode_literals
  8. from prompt_toolkit.layout.utils import find_window_for_buffer_name
  9. from six.moves import range
  10. __all__ = (
  11. 'scroll_forward',
  12. 'scroll_backward',
  13. 'scroll_half_page_up',
  14. 'scroll_half_page_down',
  15. 'scroll_one_line_up',
  16. 'scroll_one_line_down',
  17. )
  18. def _current_window_for_event(event):
  19. """
  20. Return the `Window` for the currently focussed Buffer.
  21. """
  22. return find_window_for_buffer_name(event.cli, event.cli.current_buffer_name)
  23. def scroll_forward(event, half=False):
  24. """
  25. Scroll window down.
  26. """
  27. w = _current_window_for_event(event)
  28. b = event.cli.current_buffer
  29. if w and w.render_info:
  30. info = w.render_info
  31. ui_content = info.ui_content
  32. # Height to scroll.
  33. scroll_height = info.window_height
  34. if half:
  35. scroll_height //= 2
  36. # Calculate how many lines is equivalent to that vertical space.
  37. y = b.document.cursor_position_row + 1
  38. height = 0
  39. while y < ui_content.line_count:
  40. line_height = info.get_height_for_line(y)
  41. if height + line_height < scroll_height:
  42. height += line_height
  43. y += 1
  44. else:
  45. break
  46. b.cursor_position = b.document.translate_row_col_to_index(y, 0)
  47. def scroll_backward(event, half=False):
  48. """
  49. Scroll window up.
  50. """
  51. w = _current_window_for_event(event)
  52. b = event.cli.current_buffer
  53. if w and w.render_info:
  54. info = w.render_info
  55. # Height to scroll.
  56. scroll_height = info.window_height
  57. if half:
  58. scroll_height //= 2
  59. # Calculate how many lines is equivalent to that vertical space.
  60. y = max(0, b.document.cursor_position_row - 1)
  61. height = 0
  62. while y > 0:
  63. line_height = info.get_height_for_line(y)
  64. if height + line_height < scroll_height:
  65. height += line_height
  66. y -= 1
  67. else:
  68. break
  69. b.cursor_position = b.document.translate_row_col_to_index(y, 0)
  70. def scroll_half_page_down(event):
  71. """
  72. Same as ControlF, but only scroll half a page.
  73. """
  74. scroll_forward(event, half=True)
  75. def scroll_half_page_up(event):
  76. """
  77. Same as ControlB, but only scroll half a page.
  78. """
  79. scroll_backward(event, half=True)
  80. def scroll_one_line_down(event):
  81. """
  82. scroll_offset += 1
  83. """
  84. w = find_window_for_buffer_name(event.cli, event.cli.current_buffer_name)
  85. b = event.cli.current_buffer
  86. if w:
  87. # When the cursor is at the top, move to the next line. (Otherwise, only scroll.)
  88. if w.render_info:
  89. info = w.render_info
  90. if w.vertical_scroll < info.content_height - info.window_height:
  91. if info.cursor_position.y <= info.configured_scroll_offsets.top:
  92. b.cursor_position += b.document.get_cursor_down_position()
  93. w.vertical_scroll += 1
  94. def scroll_one_line_up(event):
  95. """
  96. scroll_offset -= 1
  97. """
  98. w = find_window_for_buffer_name(event.cli, event.cli.current_buffer_name)
  99. b = event.cli.current_buffer
  100. if w:
  101. # When the cursor is at the bottom, move to the previous line. (Otherwise, only scroll.)
  102. if w.render_info:
  103. info = w.render_info
  104. if w.vertical_scroll > 0:
  105. first_line_height = info.get_height_for_line(info.first_visible_line())
  106. cursor_up = info.cursor_position.y - (info.window_height - 1 - first_line_height -
  107. info.configured_scroll_offsets.bottom)
  108. # Move cursor up, as many steps as the height of the first line.
  109. # TODO: not entirely correct yet, in case of line wrapping and many long lines.
  110. for _ in range(max(0, cursor_up)):
  111. b.cursor_position += b.document.get_cursor_up_position()
  112. # Scroll window
  113. w.vertical_scroll -= 1
  114. def scroll_page_down(event):
  115. """
  116. Scroll page down. (Prefer the cursor at the top of the page, after scrolling.)
  117. """
  118. w = _current_window_for_event(event)
  119. b = event.cli.current_buffer
  120. if w and w.render_info:
  121. # Scroll down one page.
  122. line_index = max(w.render_info.last_visible_line(), w.vertical_scroll + 1)
  123. w.vertical_scroll = line_index
  124. b.cursor_position = b.document.translate_row_col_to_index(line_index, 0)
  125. b.cursor_position += b.document.get_start_of_line_position(after_whitespace=True)
  126. def scroll_page_up(event):
  127. """
  128. Scroll page up. (Prefer the cursor at the bottom of the page, after scrolling.)
  129. """
  130. w = _current_window_for_event(event)
  131. b = event.cli.current_buffer
  132. if w and w.render_info:
  133. # Put cursor at the first visible line. (But make sure that the cursor
  134. # moves at least one line up.)
  135. line_index = max(0, min(w.render_info.first_visible_line(),
  136. b.document.cursor_position_row - 1))
  137. b.cursor_position = b.document.translate_row_col_to_index(line_index, 0)
  138. b.cursor_position += b.document.get_start_of_line_position(after_whitespace=True)
  139. # Set the scroll offset. We can safely set it to zero; the Window will
  140. # make sure that it scrolls at least until the cursor becomes visible.
  141. w.vertical_scroll = 0