mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Work on controls for highlighting UI
This commit is contained in:
parent
947f28e3cc
commit
26d405a957
@ -1,6 +1,6 @@
|
|||||||
<svg width="64" height="96" viewBox="0 0 16.933333 25.4" version="1.1">
|
<svg width="64" height="96" viewBox="0 0 16.933333 25.4" version="1.1">
|
||||||
<path
|
<path
|
||||||
style="fill:#3cef3d;fill-opacity:1;stroke:#000000;stroke-width:0.48521861;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
style="fill-opacity:1;stroke-width:0.48521861;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
d="M 0.24260931,23.981882 3.1289727,19.087752 5.0532146,13.214802 15.636548,8.32069 V 0.49006772 H 0.24260931 Z"
|
d="M 0.24260931,23.981882 3.1289727,19.087752 5.0532146,13.214802 15.636548,8.32069 V 0.49006772 H 0.24260931 Z"
|
||||||
id="selection-handle-path"
|
id="selection-handle-path"
|
||||||
/>
|
/>
|
||||||
|
Before Width: | Height: | Size: 426 B After Width: | Height: | Size: 398 B |
14
imgsrc/srv/swatch.svg
Normal file
14
imgsrc/srv/swatch.svg
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 33.866666 33.866668"
|
||||||
|
height="128"
|
||||||
|
width="128"
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
width="31.75"
|
||||||
|
height="31.75"
|
||||||
|
x="1.0583335"
|
||||||
|
y="1.0583304"
|
||||||
|
ry="3.6852679" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 262 B |
@ -89,13 +89,17 @@ def change_icon_image(icon_element, new_name):
|
|||||||
else:
|
else:
|
||||||
icon_element.firstChild.removeAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href')
|
icon_element.firstChild.removeAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href')
|
||||||
|
|
||||||
def svgicon(name, height, width):
|
def svgicon(name, height, width, tooltip):
|
||||||
ans = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
|
ans = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
|
||||||
ans.setAttribute('style', 'fill: currentColor; height: {}; width: {}; vertical-align: text-top'.format(height ? '2ex', width ? '2ex'))
|
ans.setAttribute('style', 'fill: currentColor; height: {}; width: {}; vertical-align: text-top'.format(height ? '2ex', width ? '2ex'))
|
||||||
u = document.createElementNS('http://www.w3.org/2000/svg', 'use')
|
u = document.createElementNS('http://www.w3.org/2000/svg', 'use')
|
||||||
ans.appendChild(u)
|
ans.appendChild(u)
|
||||||
if name:
|
if name:
|
||||||
ans.firstChild.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#icon-' + name)
|
ans.firstChild.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#icon-' + name)
|
||||||
|
if tooltip:
|
||||||
|
tt = document.createElementNS('http://www.w3.org/2000/svg', 'title')
|
||||||
|
tt.textContent = tooltip
|
||||||
|
ans.appendChild(tt)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
def element(elem_id, child_selector):
|
def element(elem_id, child_selector):
|
||||||
|
@ -2,7 +2,12 @@
|
|||||||
# License: GPL v3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
from __python__ import bound_methods, hash_literals
|
from __python__ import bound_methods, hash_literals
|
||||||
|
|
||||||
from dom import ensure_id, svgicon
|
from elementmaker import E
|
||||||
|
from gettext import gettext as _
|
||||||
|
|
||||||
|
from book_list.globals import get_session_data
|
||||||
|
from book_list.theme import get_color
|
||||||
|
from dom import add_extra_css, ensure_id, svgicon, unique_id
|
||||||
from read_book.shortcuts import shortcut_for_key_event
|
from read_book.shortcuts import shortcut_for_key_event
|
||||||
|
|
||||||
WAITING_FOR_CLICK = 1
|
WAITING_FOR_CLICK = 1
|
||||||
@ -11,8 +16,35 @@ DRAGGING_LEFT = 3
|
|||||||
DRAGGING_RIGHT = 4
|
DRAGGING_RIGHT = 4
|
||||||
|
|
||||||
|
|
||||||
|
add_extra_css(def():
|
||||||
|
ans = ''
|
||||||
|
ans += '.selection-handle { fill: #3cef3d; stroke: black }'
|
||||||
|
ans += '.selection-handle:active { fill: #FCE883; }'
|
||||||
|
return ans
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
dark_fg = '#111'
|
||||||
|
light_fg = '#eee'
|
||||||
|
highlight_colors = {
|
||||||
|
'#FCE2AE': dark_fg,
|
||||||
|
'#B6FFEA': dark_fg,
|
||||||
|
'#FFB3B3': dark_fg,
|
||||||
|
'#FFDCF7': dark_fg,
|
||||||
|
'#cae8d5': dark_fg,
|
||||||
|
|
||||||
|
'#204051': light_fg,
|
||||||
|
'#3b6978': light_fg,
|
||||||
|
'#2b580c': light_fg,
|
||||||
|
'#512b58': light_fg,
|
||||||
|
}
|
||||||
|
default_highlight_color = '#fce2ae'
|
||||||
|
|
||||||
|
|
||||||
def selection_handle(invert):
|
def selection_handle(invert):
|
||||||
ans = svgicon('selection-handle')
|
ans = svgicon('selection-handle')
|
||||||
|
use = ans.querySelector('use')
|
||||||
|
use.classList.add('selection-handle')
|
||||||
s = ans.style
|
s = ans.style
|
||||||
if invert:
|
if invert:
|
||||||
s.transform = 'scaleX(-1)'
|
s.transform = 'scaleX(-1)'
|
||||||
@ -38,6 +70,14 @@ def map_to_iframe_coords(point):
|
|||||||
return point
|
return point
|
||||||
|
|
||||||
|
|
||||||
|
def create_bar(size=32):
|
||||||
|
ans = E.div(
|
||||||
|
id=unique_id('annot-bar'),
|
||||||
|
style=f'height: {size}px; width: 100vw; display: flex; justify-content: space-between;',
|
||||||
|
)
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
class CreateAnnotation:
|
class CreateAnnotation:
|
||||||
|
|
||||||
container_id = 'create-annotation-overlay'
|
container_id = 'create-annotation-overlay'
|
||||||
@ -48,8 +88,40 @@ class CreateAnnotation:
|
|||||||
self.left_line_height = self.right_line_height = 8
|
self.left_line_height = self.right_line_height = 8
|
||||||
self.in_flow_mode = False
|
self.in_flow_mode = False
|
||||||
container = self.container
|
container = self.container
|
||||||
|
container.style.display = 'flex'
|
||||||
|
container.style.flexDirection = 'column'
|
||||||
|
container.style.justifyContent = 'space-between'
|
||||||
self.position_in_handle = {'x': 0, 'y': 0}
|
self.position_in_handle = {'x': 0, 'y': 0}
|
||||||
|
|
||||||
|
def button(bar, icon, tt, action):
|
||||||
|
cb = svgicon(icon, bar.style.height, bar.style.height, tt)
|
||||||
|
document.createElement
|
||||||
|
cb.setAttribute('title', tt)
|
||||||
|
cb.classList.add('annot-button')
|
||||||
|
cb.classList.add(f'annot-button-{icon}')
|
||||||
|
cb.style.backgroundColor = get_color('window-background')
|
||||||
|
cb.style.boxSizing = 'border-box'
|
||||||
|
cb.style.padding = '2px'
|
||||||
|
cb.classList.add('simple-link')
|
||||||
|
cb.addEventListener('click', def(ev):
|
||||||
|
ev.preventDefault(), ev.stopPropagation()
|
||||||
|
action()
|
||||||
|
)
|
||||||
|
bar.appendChild(cb)
|
||||||
|
return cb
|
||||||
|
|
||||||
|
tb = create_bar()
|
||||||
|
container.appendChild(tb)
|
||||||
|
button(tb, 'close', _('Cancel creation of highlight'), self.hide)
|
||||||
|
button(tb, 'chevron-up', _('Scroll up'), self.scroll_up)
|
||||||
|
button(tb, 'check', _('Finish creation of highlight'), self.accept)
|
||||||
|
|
||||||
|
bb = create_bar()
|
||||||
|
container.appendChild(bb)
|
||||||
|
button(bb, 'fg', _('Change highlight color'), self.choose_color)
|
||||||
|
button(bb, 'chevron-down', _('Scroll down'), self.scroll_down)
|
||||||
|
button(bb, 'pencil', _('Add a note'), self.add_text)
|
||||||
|
|
||||||
lh = selection_handle(True)
|
lh = selection_handle(True)
|
||||||
self.left_handle_id = ensure_id(lh, 'handle')
|
self.left_handle_id = ensure_id(lh, 'handle')
|
||||||
lh.addEventListener('mousedown', self.mousedown_on_handle, {'passive': False})
|
lh.addEventListener('mousedown', self.mousedown_on_handle, {'passive': False})
|
||||||
@ -64,6 +136,25 @@ class CreateAnnotation:
|
|||||||
container.addEventListener('mousemove', self.mousemove_on_container, {'passive': False})
|
container.addEventListener('mousemove', self.mousemove_on_container, {'passive': False})
|
||||||
container.addEventListener('keydown', self.on_keydown, {'passive': False})
|
container.addEventListener('keydown', self.on_keydown, {'passive': False})
|
||||||
|
|
||||||
|
sd = get_session_data()
|
||||||
|
style = sd.get('highlight_style') or {
|
||||||
|
'background-color': default_highlight_color,
|
||||||
|
'color': highlight_colors[default_highlight_color]
|
||||||
|
}
|
||||||
|
self.current_highlight_style = style
|
||||||
|
|
||||||
|
def scroll_up(self):
|
||||||
|
self.send_message('scroll', backwards=True)
|
||||||
|
|
||||||
|
def scroll_down(self):
|
||||||
|
self.send_message('scroll', backwards=False)
|
||||||
|
|
||||||
|
def choose_color(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def accept(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def on_keydown(self, ev):
|
def on_keydown(self, ev):
|
||||||
ev.stopPropagation(), ev.preventDefault()
|
ev.stopPropagation(), ev.preventDefault()
|
||||||
sc_name = shortcut_for_key_event(ev, self.view.keyboard_shortcut_map)
|
sc_name = shortcut_for_key_event(ev, self.view.keyboard_shortcut_map)
|
||||||
@ -147,15 +238,25 @@ class CreateAnnotation:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_highlight_style(self):
|
||||||
|
return JSON.parse(self.container.querySelector('.annot-button-fg').dataset.style)
|
||||||
|
|
||||||
|
@current_highlight_style.setter
|
||||||
|
def current_highlight_style(self, val):
|
||||||
|
b = self.container.querySelector('.annot-button-fg')
|
||||||
|
b.dataset.style = JSON.stringify(val)
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
c = self.container
|
c = self.container
|
||||||
c.style.display = 'block'
|
c.style.display = 'flex'
|
||||||
c.focus()
|
c.focus()
|
||||||
|
|
||||||
def hide(self):
|
def hide(self):
|
||||||
if self.is_visible:
|
if self.is_visible:
|
||||||
self.container.style.display = 'none'
|
self.container.style.display = 'none'
|
||||||
self.view.focus_iframe()
|
self.view.focus_iframe()
|
||||||
|
self.send_message('set-highlight-style', style=None)
|
||||||
|
|
||||||
def send_message(self, type, **kw):
|
def send_message(self, type, **kw):
|
||||||
self.view.iframe_wrapper.send_message('annotations', type=type, **kw)
|
self.view.iframe_wrapper.send_message('annotations', type=type, **kw)
|
||||||
@ -170,6 +271,7 @@ class CreateAnnotation:
|
|||||||
if msg.extents.start.x is not None:
|
if msg.extents.start.x is not None:
|
||||||
self.place_handles(msg.extents)
|
self.place_handles(msg.extents)
|
||||||
self.in_flow_mode = msg.in_flow_mode
|
self.in_flow_mode = msg.in_flow_mode
|
||||||
|
self.send_message('set-highlight-style', style=self.current_highlight_style)
|
||||||
elif msg.type is 'position-handles':
|
elif msg.type is 'position-handles':
|
||||||
if self.state is WAITING_FOR_CLICK:
|
if self.state is WAITING_FOR_CLICK:
|
||||||
self.place_handles(msg.extents)
|
self.place_handles(msg.extents)
|
||||||
|
@ -46,7 +46,7 @@ from read_book.referencing import (
|
|||||||
from read_book.resources import finalize_resources, unserialize_html
|
from read_book.resources import finalize_resources, unserialize_html
|
||||||
from read_book.settings import (
|
from read_book.settings import (
|
||||||
apply_colors, apply_font_size, apply_settings, apply_stylesheet, opts,
|
apply_colors, apply_font_size, apply_settings, apply_stylesheet, opts,
|
||||||
set_color_scheme_class, update_settings
|
set_color_scheme_class, set_selection_style, update_settings
|
||||||
)
|
)
|
||||||
from read_book.shortcuts import (
|
from read_book.shortcuts import (
|
||||||
create_shortcut_map, keyevent_as_shortcut, shortcut_for_key_event
|
create_shortcut_map, keyevent_as_shortcut, shortcut_for_key_event
|
||||||
@ -635,6 +635,8 @@ class IframeBoss:
|
|||||||
elif data.type is 'perp-scroll':
|
elif data.type is 'perp-scroll':
|
||||||
if in_flow_mode and flow_annotation_scroll(data.backwards, True):
|
if in_flow_mode and flow_annotation_scroll(data.backwards, True):
|
||||||
self.send_message('annotations', type='update-handles', extents=selection_extents(in_flow_mode))
|
self.send_message('annotations', type='update-handles', extents=selection_extents(in_flow_mode))
|
||||||
|
elif data.type is 'set-highlight-style':
|
||||||
|
set_selection_style(data.style)
|
||||||
else:
|
else:
|
||||||
console.log('Ignoring annotations message to iframe with unknown type: ' + data.type)
|
console.log('Ignoring annotations message to iframe with unknown type: ' + data.type)
|
||||||
|
|
||||||
|
@ -36,6 +36,15 @@ def apply_font_size():
|
|||||||
document.documentElement.style.fontSize = '{}px'.format(opts.base_font_size)
|
document.documentElement.style.fontSize = '{}px'.format(opts.base_font_size)
|
||||||
|
|
||||||
|
|
||||||
|
def default_selection_colors():
|
||||||
|
if opts.is_dark_theme:
|
||||||
|
return dark_link_color, '#111'
|
||||||
|
return '#3297FD', '#eee'
|
||||||
|
|
||||||
|
|
||||||
|
styles_id = 'calibre-color-scheme-style-overrides'
|
||||||
|
|
||||||
|
|
||||||
def apply_colors():
|
def apply_colors():
|
||||||
for elem in (document.documentElement, document.body):
|
for elem in (document.documentElement, document.body):
|
||||||
elem.style.color = opts.color_scheme.foreground
|
elem.style.color = opts.color_scheme.foreground
|
||||||
@ -45,7 +54,7 @@ def apply_colors():
|
|||||||
document.documentElement.style.backgroundColor = opts.bg_image_fade
|
document.documentElement.style.backgroundColor = opts.bg_image_fade
|
||||||
ss = document.getElementById('calibre-color-scheme-style-overrides')
|
ss = document.getElementById('calibre-color-scheme-style-overrides')
|
||||||
if not ss:
|
if not ss:
|
||||||
ss = E.style(id='calibre-color-scheme-style-overrides', type='text/css')
|
ss = E.style(id=styles_id, type='text/css')
|
||||||
document.documentElement.appendChild(ss)
|
document.documentElement.appendChild(ss)
|
||||||
text = ''
|
text = ''
|
||||||
if opts.override_book_colors is not 'never':
|
if opts.override_book_colors is not 'never':
|
||||||
@ -64,18 +73,28 @@ def apply_colors():
|
|||||||
# priority than the override all selectors above
|
# priority than the override all selectors above
|
||||||
text += f'\nhtml > body :link, html > body :link * {{ color: {c} !important }} html > body :visited, html > body :visited * {{ color: {c} !important }}'
|
text += f'\nhtml > body :link, html > body :link * {{ color: {c} !important }} html > body :visited, html > body :visited * {{ color: {c} !important }}'
|
||||||
|
|
||||||
if opts.is_dark_theme:
|
selbg, selfg = default_selection_colors()
|
||||||
selbg = dark_link_color
|
|
||||||
selfg = 'black'
|
|
||||||
else:
|
|
||||||
selbg = '#3297FD'
|
|
||||||
selfg = 'white'
|
|
||||||
text += f'\n::selection {{ background-color: {selbg}; color: {selfg} }}'
|
text += f'\n::selection {{ background-color: {selbg}; color: {selfg} }}'
|
||||||
text += f'\n::selection:window-inactive {{ background-color: {selbg}; color: {selfg} }}'
|
text += f'\n::selection:window-inactive {{ background-color: {selbg}; color: {selfg} }}'
|
||||||
|
|
||||||
ss.textContent = text
|
ss.textContent = text
|
||||||
|
|
||||||
|
|
||||||
|
def set_selection_style(style):
|
||||||
|
if not style:
|
||||||
|
selbg, selfg = default_selection_colors()
|
||||||
|
style = {'color': selfg, 'background-color': selbg}
|
||||||
|
sheet = document.getElementById(styles_id)
|
||||||
|
if not sheet:
|
||||||
|
return
|
||||||
|
css_text = ''
|
||||||
|
for prop in Object.keys(style):
|
||||||
|
css_text += f'{prop}: {style[prop]}; '
|
||||||
|
for rule in sheet.sheet.cssRules:
|
||||||
|
if rule.type is rule.STYLE_RULE and rule.selectorText.indexOf('selection') > -1:
|
||||||
|
rule.style.cssText = css_text
|
||||||
|
|
||||||
|
|
||||||
def set_color_scheme_class():
|
def set_color_scheme_class():
|
||||||
if opts.is_dark_theme:
|
if opts.is_dark_theme:
|
||||||
document.body.classList.add('calibre-viewer-dark-colors')
|
document.body.classList.add('calibre-viewer-dark-colors')
|
||||||
|
@ -219,7 +219,7 @@ class View:
|
|||||||
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; display:none', id='book-content-popup-overlay'), # content popup overlay
|
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; display:none', id='book-content-popup-overlay'), # content popup overlay
|
||||||
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; overflow: auto; display:none', id='book-overlay'), # main overlay
|
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; overflow: auto; display:none', id='book-overlay'), # main overlay
|
||||||
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; display:none', id='controls-help-overlay'), # controls help overlay
|
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; display:none', id='controls-help-overlay'), # controls help overlay
|
||||||
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; display:none', id=CreateAnnotation.container_id, tabindex='0'), # create annotation overlay
|
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; display:none; overflow: hidden', id=CreateAnnotation.container_id, tabindex='0'), # create annotation overlay
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
E.div(
|
E.div(
|
||||||
|
@ -59,6 +59,7 @@ defaults = {
|
|||||||
'user_color_schemes': {},
|
'user_color_schemes': {},
|
||||||
'user_stylesheet': '',
|
'user_stylesheet': '',
|
||||||
'word_actions': v'[]',
|
'word_actions': v'[]',
|
||||||
|
'highlight_style': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
is_local_setting = {
|
is_local_setting = {
|
||||||
@ -85,6 +86,7 @@ is_local_setting = {
|
|||||||
'standalone_misc_settings': True,
|
'standalone_misc_settings': True,
|
||||||
'standalone_recently_opened': True,
|
'standalone_recently_opened': True,
|
||||||
'user_stylesheet': True,
|
'user_stylesheet': True,
|
||||||
|
'highlight_style': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user