mirror of
https://github.com/kovidgoyal/calibre.git
synced 2026-04-09 18:51:56 -04:00
131 lines
5.2 KiB
Python
131 lines
5.2 KiB
Python
# vim:fileencoding=utf-8
|
|
# License: GPL v3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>
|
|
from __python__ import bound_methods, hash_literals
|
|
|
|
from elementmaker import E
|
|
|
|
from book_list.globals import get_session_data
|
|
from book_list.theme import cached_color_to_rgba
|
|
from dom import unique_id
|
|
from read_book.globals import ui_operations
|
|
|
|
SIZE = 10
|
|
|
|
|
|
class BookScrollbar:
|
|
|
|
def __init__(self, view):
|
|
self.view = view
|
|
self.container_id = unique_id('book-scrollbar')
|
|
self.sync_to_contents_timer = 0
|
|
self.sync_contents_timer = 0
|
|
|
|
@property
|
|
def container(self):
|
|
return document.getElementById(self.container_id)
|
|
|
|
def create(self):
|
|
self.on_bob_mousedown = self.on_bob_mouse_event.bind(None, 'down')
|
|
self.on_bob_mousemove = self.on_bob_mouse_event.bind(None, 'move')
|
|
self.on_bob_mouseup = self.on_bob_mouse_event.bind(None, 'up')
|
|
return E.div(
|
|
id=self.container_id,
|
|
style=f'height: 100vh; background-color: #aaa; width: {SIZE}px; border-radius: 5px',
|
|
onclick=self.bar_clicked, oncontextmenu=self.context_menu,
|
|
E.div(
|
|
style=f'position: relative; width: 100%; height: {int(2.2*SIZE)}px; background-color: #444; border-radius: 5px',
|
|
onmousedown=self.on_bob_mousedown,
|
|
),
|
|
E.div(
|
|
style='position: absolute; z-index: 2147483647; width: 100vw; height: 100vh; left: 0; top: 0; display: none;'
|
|
)
|
|
)
|
|
|
|
def context_menu(self, ev):
|
|
if ui_operations.scrollbar_context_menu:
|
|
ev.preventDefault(), ev.stopPropagation()
|
|
c = self.container
|
|
bob = c.firstChild
|
|
height = c.clientHeight - bob.clientHeight
|
|
top = max(0, min(ev.clientY - bob.clientHeight, height))
|
|
frac = max(0, min(top / height, 1))
|
|
ui_operations.scrollbar_context_menu(ev.screenX, ev.screenY, frac)
|
|
|
|
def bar_clicked(self, evt):
|
|
if evt.button is 0:
|
|
c = self.container
|
|
b = c.firstChild
|
|
bob_top = b.offsetTop
|
|
bob_bottom = bob_top + b.offsetHeight
|
|
if evt.clientY < bob_top:
|
|
self.view.side_margin_clicked('left', evt)
|
|
elif evt.clientY > bob_bottom:
|
|
self.view.side_margin_clicked('right', evt)
|
|
|
|
def on_bob_mouse_event(self, which, evt):
|
|
c = self.container
|
|
bob = c.firstChild
|
|
mouse_grab = bob.nextSibling
|
|
if which is 'move':
|
|
top = evt.pageY - self.down_y
|
|
height = c.clientHeight - bob.clientHeight
|
|
top = max(0, min(top, height))
|
|
bob.style.top = f'{top}px'
|
|
evt.preventDefault(), evt.stopPropagation()
|
|
frac = bob.offsetTop / height
|
|
if self.sync_contents_timer:
|
|
window.clearTimeout(self.sync_contents_timer)
|
|
self.sync_contents_timer = window.setTimeout(self.view.goto_frac.bind(None, frac), 2)
|
|
elif which is 'down':
|
|
if evt.button is not 0:
|
|
return
|
|
evt.preventDefault(), evt.stopPropagation()
|
|
self.down_y = evt.clientY - bob.getBoundingClientRect().top
|
|
mouse_grab.style.display = 'block'
|
|
window.addEventListener('mousemove', self.on_bob_mousemove, {'capture': True, 'passive': False})
|
|
window.addEventListener('mouseup', self.on_bob_mouseup, {'capture': True, 'passive': False})
|
|
elif which is 'up':
|
|
self.down_y = 0
|
|
window.removeEventListener('mousemove', self.on_bob_mousemove, {'capture': True, 'passive': False})
|
|
window.removeEventListener('mouseup', self.on_bob_mouseup, {'capture': True, 'passive': False})
|
|
window.setTimeout(def(): self.container.firstChild.nextSibling.style.display = 'none';, 10)
|
|
evt.preventDefault(), evt.stopPropagation()
|
|
|
|
def apply_visibility(self):
|
|
sd = get_session_data()
|
|
self.container.style.display = 'block' if sd.get('book_scrollbar') else 'none'
|
|
|
|
@property
|
|
def effective_width(self):
|
|
return SIZE if self.container.style.display is 'block' else 0
|
|
|
|
def set_position(self, frac):
|
|
c = self.container
|
|
frac = max(0, min(frac, 1))
|
|
c.firstChild.style.top = f'{frac * (c.clientHeight - c.firstChild.clientHeight)}px'
|
|
|
|
def _sync_to_contents(self):
|
|
self.sync_to_contents_timer = 0
|
|
self.set_position(self.sync_to_contents_frac)
|
|
|
|
def sync_to_contents(self, frac):
|
|
self.sync_to_contents_frac = frac
|
|
if not self.sync_to_contents_timer:
|
|
self.sync_to_contents_timer = window.setTimeout(self._sync_to_contents, 50)
|
|
|
|
def apply_color_scheme(self, colors):
|
|
fg = cached_color_to_rgba(colors.foreground)
|
|
bg = cached_color_to_rgba(colors.background)
|
|
|
|
def mix(fg, bg, frac):
|
|
def m(x): # noqa: unused-local
|
|
return frac * fg[x] + (1-frac) * bg[x]
|
|
return v'[m[0], m[1], m[2]]'
|
|
|
|
rbg = mix(fg, bg, 0.3)
|
|
rfg = mix(fg, bg, 0.7)
|
|
|
|
c = self.container
|
|
c.style.backgroundColor = f'rgb({rbg[0]}, {rbg[1]}, {rbg[2]})'
|
|
c.firstChild.style.backgroundColor = f'rgb({rfg[0]}, {rfg[1]}, {rfg[2]})'
|