Merge pull request #2354 from myk002/myk_scrollbars_redux

make scrollbars more responsive when dragging
develop
Myk 2022-10-22 20:28:52 -07:00 committed by GitHub
commit a854e379b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 46 additions and 30 deletions

@ -4052,7 +4052,11 @@ Scrollbars have the following attributes:
:fg: Specifies the pen for the scroll icons and the active part of the bar. Default is ``COLOR_LIGHTGREEN``.
:bg: Specifies the pen for the background part of the scrollbar. Default is ``COLOR_CYAN``.
:on_scroll: A callback called when the scrollbar is scrolled. It will be called with a single string parameter with a value of "up_large", "down_large", "up_small", or "down_small".
:on_scroll: A callback called when the scrollbar is scrolled. If the scrollbar is clicked,
the callback will be called with one of the following string parameters: "up_large",
"down_large", "up_small", or "down_small". If the scrollbar is dragged, the callback will
be called with the value that ``top_elem`` should be set to on the next call to
``update()`` (see below).
The Scrollbar widget implements the following methods:
@ -4062,8 +4066,10 @@ The Scrollbar widget implements the following methods:
The ``top_elem`` param is the (one-based) index of the first visible element.
The ``elems_per_page`` param is the maximum number of elements that can be
shown at one time. The ``num_elems`` param is the total number of elements
that the paried widget can scroll through. The scrollbar will adjust its
scrollbar size and position accordingly.
that the paried widget can scroll through. If ``elems_per_page`` or
``num_elems`` is not specified, the most recently specified value for these
parameters is used. The scrollbar will adjust its scrollbar size and position
according to the values passed to this function.
Clicking on the arrows at the top or the bottom of a scrollbar will scroll an
associated widget by a small amount. Clicking on the unfilled portion of the

@ -365,8 +365,6 @@ end
-- Scrollbar --
---------------
-- these can be overridden by the user, e.g.:
-- require('gui.widgets').SCROLL_DELAY_MS = 100
SCROLL_INITIAL_DELAY_MS = 300
SCROLL_DELAY_MS = 20
@ -391,6 +389,17 @@ function Scrollbar:init()
self:update(1, 1, 1)
end
local function scrollbar_get_max_pos_and_height(scrollbar)
local frame_body = scrollbar.frame_body
local scrollbar_body_height = (frame_body and frame_body.height or 3) - 2
local height = math.max(1, math.floor(
(math.min(scrollbar.elems_per_page, scrollbar.num_elems) * scrollbar_body_height) /
scrollbar.num_elems))
return scrollbar_body_height - height, height
end
-- calculate and cache the number of tiles of empty space above the top of the
-- scrollbar and the number of tiles the scrollbar should occupy to represent
-- the percentage of text that is on the screen.
@ -400,39 +409,33 @@ function Scrollbar:update(top_elem, elems_per_page, num_elems)
if not top_elem then error('must specify index of new top element') end
elems_per_page = elems_per_page or self.elems_per_page
num_elems = num_elems or self.num_elems
self.top_elem = top_elem
self.elems_per_page, self.num_elems = elems_per_page, num_elems
local frame_height = self.frame_body and self.frame_body.height or 3
local scrollbar_body_height = frame_height - 2
local height = math.max(1, math.floor(
(math.min(elems_per_page, num_elems) * scrollbar_body_height) /
num_elems))
local max_pos = scrollbar_body_height - height
local max_pos, height = scrollbar_get_max_pos_and_height(self)
local pos = (num_elems == elems_per_page) and 0 or
math.ceil(((top_elem-1) * max_pos) /
(num_elems - elems_per_page))
self.top_elem = top_elem
self.elems_per_page, self.num_elems = elems_per_page, num_elems
self.bar_offset, self.bar_height = pos, height
end
local function scrollbar_do_drag(scrollbar)
local x,y = dfhack.screen.getMousePos()
x,y = scrollbar.frame_body:localXY(x,y)
local bar_idx = y - scrollbar.bar_offset
local delta = bar_idx - scrollbar.is_dragging
if delta < -scrollbar.bar_height then
scrollbar.on_scroll('up_large')
elseif delta < 0 then
scrollbar.on_scroll('up_small')
elseif delta > scrollbar.bar_height then
scrollbar.on_scroll('down_large')
elseif delta > 0 then
scrollbar.on_scroll('down_small')
local _,y = scrollbar.frame_body:localXY(dfhack.screen.getMousePos())
local cur_pos = y - scrollbar.is_dragging
local max_top = scrollbar.num_elems - scrollbar.elems_per_page + 1
local max_pos = scrollbar_get_max_pos_and_height(scrollbar)
local new_top_elem = math.floor(cur_pos * max_top / max_pos) + 1
new_top_elem = math.max(1, math.min(new_top_elem, max_top))
if new_top_elem ~= scrollbar.top_elem then
scrollbar.on_scroll(new_top_elem)
end
end
local function scrollbar_is_visible(scrollbar)
return scrollbar.elems_per_page < scrollbar.num_elems
end
local UP_ARROW_CHAR = string.char(24)
local DOWN_ARROW_CHAR = string.char(25)
local NO_ARROW_CHAR = string.char(32)
@ -441,7 +444,7 @@ local BAR_BG_CHAR = string.char(179)
function Scrollbar:onRenderBody(dc)
-- don't draw if all elements are visible
if self.elems_per_page >= self.num_elems then return end
if not scrollbar_is_visible(self) then return end
-- render up arrow if we're not at the top
dc:seek(0, 0):char(
self.top_elem == 1 and NO_ARROW_CHAR or UP_ARROW_CHAR, self.fg, self.bg)
@ -484,7 +487,10 @@ function Scrollbar:onRenderBody(dc)
end
function Scrollbar:onInput(keys)
if not keys._MOUSE_L_DOWN or not self.on_scroll then return false end
if not keys._MOUSE_L_DOWN or not self.on_scroll
or not scrollbar_is_visible(self) then
return false
end
local _,y = self:getMousePos()
if not y then return false end
local scroll_spec = nil
@ -763,7 +769,9 @@ end
function Label:on_scrollbar(scroll_spec)
local v = 0
if scroll_spec == 'down_large' then
if tonumber(scroll_spec) then
v = scroll_spec - self.start_line_num
elseif scroll_spec == 'down_large' then
v = '+halfpage'
elseif scroll_spec == 'up_large' then
v = '-halfpage'
@ -1136,7 +1144,9 @@ end
function List:on_scrollbar(scroll_spec)
local v = 0
if scroll_spec == 'down_large' then
if tonumber(scroll_spec) then
v = scroll_spec - self.page_top
elseif scroll_spec == 'down_large' then
v = math.ceil(self.page_size / 2)
elseif scroll_spec == 'up_large' then
v = -math.ceil(self.page_size / 2)