Remote control: scroll-window: Allow fractional scrolling since we now have pixel scroll
This commit is contained in:
parent
82d3364e4a
commit
75ce50400e
5 changed files with 69 additions and 18 deletions
|
|
@ -1317,6 +1317,9 @@ class Screen:
|
|||
def scroll(self, amt: int, upwards: bool) -> bool:
|
||||
pass
|
||||
|
||||
def fractional_scroll(self, amt: float) -> bool:
|
||||
pass
|
||||
|
||||
def scroll_to_next_mark(self, mark: int = 0, backwards: bool = True) -> bool:
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class ScrollWindow(RemoteCommand):
|
|||
amount+/list.scroll_amount: The amount to scroll, a two item list with the first item being \
|
||||
either a number or the keywords, start and end. \
|
||||
And the second item being either 'p' for pages or 'l' for lines or 'u'
|
||||
for unscrolling by lines, or 'r' for scrolling ot prompt.
|
||||
for unscrolling by lines, or 'r' for scrolling to prompt.
|
||||
match/str: The window to scroll
|
||||
'''
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ class ScrollWindow(RemoteCommand):
|
|||
' :italic:`SCROLL_AMOUNT` can be either the keywords :code:`start` or :code:`end` or an'
|
||||
' argument of the form :italic:`<number>[unit][+-]`. :code:`unit` can be :code:`l` for lines, :code:`p` for pages,'
|
||||
' :code:`u` for unscroll and :code:`r` for scroll to prompt. If unspecified, :code:`l` is the default.'
|
||||
' For example, :code:`30` will scroll down 30 lines, :code:`2p-`'
|
||||
' For example, :code:`30.5` will scroll down 30 and a half lines, :code:`2p-`'
|
||||
' will scroll up 2 pages and :code:`0.5p` will scroll down half page.'
|
||||
' :code:`3u` will *unscroll* by 3 lines, which means that 3 lines will move from the'
|
||||
' scrollback buffer onto the top of the screen. :code:`1r-` will scroll to the previous prompt and :code:`1r` to the next prompt.'
|
||||
|
|
@ -52,7 +52,7 @@ def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: Arg
|
|||
prompt = 'r' in amt
|
||||
mult = -1 if amt.endswith('-') and not unscroll else 1
|
||||
q = float(amt.rstrip('+-plur'))
|
||||
if not pages and not q.is_integer():
|
||||
if (unscroll or prompt) and not q.is_integer():
|
||||
self.fatal('The number must be an integer')
|
||||
amount = q * mult, 'p' if pages else ('u' if unscroll else ('r' if prompt else 'l'))
|
||||
|
||||
|
|
@ -64,22 +64,23 @@ def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: Pa
|
|||
for window in self.windows_for_match_payload(boss, window, payload_get):
|
||||
if window:
|
||||
if amt[0] in ('start', 'end'):
|
||||
getattr(window, {'start': 'scroll_home'}.get(amt[0], 'scroll_end'))()
|
||||
(window.scroll_home if amt[0] == 'start' else window.scroll_end)()
|
||||
else:
|
||||
amt, unit = amt
|
||||
if unit == 'u':
|
||||
window.screen.reverse_scroll(int(abs(amt)), True)
|
||||
elif unit == 'r':
|
||||
window.scroll_to_prompt(int(amt))
|
||||
else:
|
||||
unit = 'page' if unit == 'p' else 'line'
|
||||
if unit == 'page' and not isinstance(amt, int) and not amt.is_integer():
|
||||
amt = round(window.screen.lines * amt)
|
||||
unit = 'line'
|
||||
direction = 'up' if amt < 0 else 'down'
|
||||
func = getattr(window, f'scroll_{unit}_{direction}')
|
||||
for i in range(int(abs(amt))):
|
||||
func()
|
||||
match unit:
|
||||
case 'u':
|
||||
window.screen.reverse_scroll(int(abs(amt)), True)
|
||||
case 'r':
|
||||
window.scroll_to_prompt(int(amt))
|
||||
case 'l':
|
||||
window.scroll_fractional_lines(amt)
|
||||
case 'p':
|
||||
if not isinstance(amt, int) and not amt.is_integer():
|
||||
amt = round(window.screen.lines * amt)
|
||||
unit = 'line'
|
||||
func = window.scroll_page_up if amt < 0 else window.scroll_page_down
|
||||
for i in range(int(abs(amt))):
|
||||
func()
|
||||
return None
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5066,6 +5066,45 @@ screen_history_scroll(Screen *self, int amt, bool upwards) {
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
screen_fractional_scroll(Screen *self, double amt) {
|
||||
if (amt == 0) return false;
|
||||
index_type before_scrolled_by = self->scrolled_by;
|
||||
double before_pixels = self->pixel_scroll_offset_y;
|
||||
double integral_part, fractional_part = modf(amt, &integral_part);
|
||||
int lines = (int)integral_part;
|
||||
double pixels = fractional_part * self->cell_size.height;
|
||||
if (amt > 0) { // downwards
|
||||
pixels = pixels > self->pixel_scroll_offset_y ? pixels - self->pixel_scroll_offset_y : 0;
|
||||
self->pixel_scroll_offset_y = 0;
|
||||
self->scrolled_by = self->scrolled_by > (unsigned)lines ? self->scrolled_by - lines : 0;
|
||||
if (pixels > 0 && self->scrolled_by > 0) {
|
||||
self->scrolled_by--; self->pixel_scroll_offset_y = self->cell_size.height - pixels;
|
||||
}
|
||||
} else {
|
||||
self->pixel_scroll_offset_y -= pixels; // pixels is negative
|
||||
if (self->pixel_scroll_offset_y >= self->cell_size.height) {
|
||||
self->pixel_scroll_offset_y = 0; self->scrolled_by++;
|
||||
}
|
||||
self->scrolled_by = MIN(self->scrolled_by - lines, self->historybuf->count);
|
||||
if (self->scrolled_by >= self->historybuf->count) self->pixel_scroll_offset_y = 0;
|
||||
}
|
||||
if (self->scrolled_by != before_scrolled_by || self->pixel_scroll_offset_y != before_pixels) {
|
||||
dirty_scroll(self);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
fractional_scroll(Screen *self, PyObject *amt) {
|
||||
double y;
|
||||
if (PyFloat_Check(amt)) y = PyFloat_AS_DOUBLE(amt);
|
||||
else if (PyLong_Check(amt)) y = PyLong_AsDouble(amt);
|
||||
else { PyErr_SetString(PyExc_TypeError, "amt must be a float"); return NULL; }
|
||||
return Py_NewRef(screen_fractional_scroll(self, y) ? Py_True : Py_False);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
scroll(Screen *self, PyObject *args) {
|
||||
int amt, upwards;
|
||||
|
|
@ -5939,6 +5978,7 @@ static PyMethodDef methods[] = {
|
|||
MND(text_for_marked_url, METH_VARARGS)
|
||||
MND(is_rectangle_select, METH_NOARGS)
|
||||
MND(scroll, METH_VARARGS)
|
||||
MND(fractional_scroll, METH_O)
|
||||
MND(scroll_to_prompt, METH_VARARGS)
|
||||
MND(set_last_visited_prompt, METH_VARARGS)
|
||||
MND(send_escape_code_to_child, METH_VARARGS)
|
||||
|
|
|
|||
|
|
@ -2234,6 +2234,13 @@ def pass_selection_to_program(self, *args: str) -> None:
|
|||
def clear_selection(self) -> None:
|
||||
self.screen.clear_selection()
|
||||
|
||||
def scroll_fractional_lines(self, amt: float) -> bool | None:
|
||||
' Scroll fractionally, negative values are up and positive values are down '
|
||||
if self.screen.is_main_linebuf():
|
||||
self.screen.fractional_scroll(amt)
|
||||
return None
|
||||
return True
|
||||
|
||||
@ac('sc', 'Scroll up by one line when in main screen. To scroll by different amounts, you can map the remote_control scroll-window action.')
|
||||
def scroll_line_up(self) -> bool | None:
|
||||
if self.screen.is_main_linebuf():
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ func parse_scroll_amount(amt string) ([]any, error) {
|
|||
if err != nil {
|
||||
return ans, err
|
||||
}
|
||||
if !pages && q != float64(int(q)) {
|
||||
if (unscroll || prompt) && q != float64(int(q)) {
|
||||
return ans, fmt.Errorf("The number must be an integer")
|
||||
}
|
||||
ans[0] = q * mult
|
||||
|
|
|
|||
Loading…
Reference in a new issue