mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-31 14:33:54 -04:00
Selection bar: Allow customizing to add quick highlight buttons with specific styles
This commit is contained in:
parent
1ef9d7b7da
commit
8343e8165a
@ -119,3 +119,9 @@ annot_id_uuid_map = {}
|
|||||||
def clear_annot_id_uuid_map():
|
def clear_annot_id_uuid_map():
|
||||||
nonlocal annot_id_uuid_map
|
nonlocal annot_id_uuid_map
|
||||||
annot_id_uuid_map = {}
|
annot_id_uuid_map = {}
|
||||||
|
|
||||||
|
|
||||||
|
def is_dark_theme(set_val):
|
||||||
|
if set_val?:
|
||||||
|
is_dark_theme.ans = set_val
|
||||||
|
return v'!!is_dark_theme.ans'
|
||||||
|
@ -38,7 +38,7 @@ builtin_decorations_light = builtin_decorations_dark = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def friendly_name(kind, which):
|
def builtin_friendly_name(kind, which):
|
||||||
if kind is 'color':
|
if kind is 'color':
|
||||||
return {
|
return {
|
||||||
'yellow': _('Yellow highlight'),
|
'yellow': _('Yellow highlight'),
|
||||||
@ -49,7 +49,7 @@ def friendly_name(kind, which):
|
|||||||
}[which] or _('Unknown highlight')
|
}[which] or _('Unknown highlight')
|
||||||
return {
|
return {
|
||||||
'wavy': _('Red wavy underline'),
|
'wavy': _('Red wavy underline'),
|
||||||
'strikeout': _('Red underline'),
|
'strikeout': _('Red strikeout'),
|
||||||
}[which] or _('Unknown underline')
|
}[which] or _('Unknown underline')
|
||||||
|
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ class HighlightStyle:
|
|||||||
style.textDecorationStyle = tds
|
style.textDecorationStyle = tds
|
||||||
if tdc:
|
if tdc:
|
||||||
style.textDecorationColor = tdc
|
style.textDecorationColor = tdc
|
||||||
return
|
return container
|
||||||
|
|
||||||
bg = None
|
bg = None
|
||||||
if s.type is 'builtin':
|
if s.type is 'builtin':
|
||||||
@ -136,6 +136,7 @@ class HighlightStyle:
|
|||||||
if bg:
|
if bg:
|
||||||
style.backgroundColor = bg
|
style.backgroundColor = bg
|
||||||
style.borderRadius = f'{br}{ICON_SIZE_UNIT}'
|
style.borderRadius = f'{br}{ICON_SIZE_UNIT}'
|
||||||
|
return container
|
||||||
|
|
||||||
def highlight_shade(self, is_dark):
|
def highlight_shade(self, is_dark):
|
||||||
s = self.style
|
s = self.style
|
||||||
@ -158,7 +159,7 @@ class HighlightStyle:
|
|||||||
def friendly_name(self):
|
def friendly_name(self):
|
||||||
s = self.style
|
s = self.style
|
||||||
if s.type is 'builtin':
|
if s.type is 'builtin':
|
||||||
return friendly_name(s.kind, s.which)
|
return builtin_friendly_name(s.kind, s.which)
|
||||||
return s.friendly_name or _('Custom style')
|
return s.friendly_name or _('Custom style')
|
||||||
|
|
||||||
|
|
||||||
@ -206,22 +207,14 @@ def custom_styles_equal(a, b):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class StyleCollection:
|
def all_styles():
|
||||||
|
ans = v'[]'
|
||||||
def __init__(self):
|
custom_highlight_styles = get_session_data().get('custom_highlight_styles')
|
||||||
custom_highlight_styles = get_session_data().get('custom_highlight_styles')
|
for raw in custom_highlight_styles:
|
||||||
self.styles = []
|
ans.push(HighlightStyle(raw))
|
||||||
self.style_id_counter = 0
|
for raw in all_builtin_styles():
|
||||||
|
ans.push(HighlightStyle(raw))
|
||||||
def add(raw):
|
return ans
|
||||||
hs = HighlightStyle(raw)
|
|
||||||
hs.style_id = v'++self.style_id_counter'
|
|
||||||
self.styles.push(hs)
|
|
||||||
|
|
||||||
for raw in custom_highlight_styles:
|
|
||||||
add(raw)
|
|
||||||
for raw in all_builtin_styles():
|
|
||||||
add(raw)
|
|
||||||
|
|
||||||
|
|
||||||
class AddStyle: # {{{
|
class AddStyle: # {{{
|
||||||
|
@ -7,6 +7,8 @@ from gettext import gettext as _
|
|||||||
|
|
||||||
from book_list.globals import get_session_data
|
from book_list.globals import get_session_data
|
||||||
from dom import clear, svgicon, unique_id
|
from dom import clear, svgicon, unique_id
|
||||||
|
from read_book.globals import is_dark_theme
|
||||||
|
from read_book.highlights import all_styles
|
||||||
from read_book.prefs.utils import create_button_box
|
from read_book.prefs.utils import create_button_box
|
||||||
from read_book.selection_bar import all_actions
|
from read_book.selection_bar import all_actions
|
||||||
from session import defaults
|
from session import defaults
|
||||||
@ -17,12 +19,16 @@ CONTAINER = unique_id('selection-settings')
|
|||||||
def set_actions(use_defaults):
|
def set_actions(use_defaults):
|
||||||
c = get_container()
|
c = get_container()
|
||||||
adef = all_actions()
|
adef = all_actions()
|
||||||
actions = defaults.selection_bar_actions if use_defaults else get_session_data().get('selection_bar_actions')
|
sd = get_session_data()
|
||||||
|
actions = defaults.selection_bar_actions if use_defaults else sd.get('selection_bar_actions')
|
||||||
current = [x for x in actions if adef[x]]
|
current = [x for x in actions if adef[x]]
|
||||||
c.querySelector('.current-actions').dataset.actions = JSON.stringify(current)
|
c.querySelector('.current-actions').dataset.actions = JSON.stringify(current)
|
||||||
available_actions = [x for x in adef if current.indexOf(x) is -1]
|
available_actions = [x for x in adef if current.indexOf(x) is -1]
|
||||||
c.querySelector('.available-actions').dataset.actions = JSON.stringify(available_actions)
|
c.querySelector('.available-actions').dataset.actions = JSON.stringify(available_actions)
|
||||||
update_action_tables()
|
update_action_tables()
|
||||||
|
quick_actions = defaults.selection_bar_quick_highlights if use_defaults else sd.get('selection_bar_quick_highlights')
|
||||||
|
c.querySelector('.quick-actions').dataset.actions = JSON.stringify(quick_actions)
|
||||||
|
update_quick_action_table()
|
||||||
|
|
||||||
|
|
||||||
def restore_defaults():
|
def restore_defaults():
|
||||||
@ -113,6 +119,32 @@ def update_action_tables():
|
|||||||
build_action_table(current, False)
|
build_action_table(current, False)
|
||||||
|
|
||||||
|
|
||||||
|
def update_quick_action_table():
|
||||||
|
c = get_container().querySelector('.quick-actions')
|
||||||
|
clear(c)
|
||||||
|
c.style.display = 'flex'
|
||||||
|
c.style.flexWrap = 'wrap'
|
||||||
|
current = {x: True for x in JSON.parse(c.dataset.actions)}
|
||||||
|
for hs in all_styles():
|
||||||
|
c.appendChild(E.label(
|
||||||
|
style='margin: 1ex; display: flex; align-contents: center',
|
||||||
|
hs.make_swatch(E.span(), is_dark_theme()),
|
||||||
|
'\xa0',
|
||||||
|
hs.friendly_name,
|
||||||
|
'\xa0',
|
||||||
|
E.input(type='checkbox', value=hs.key, checked=current[hs.key]),
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def selected_quick_actions():
|
||||||
|
ans = v'[]'
|
||||||
|
c = get_container().querySelector('.quick-actions')
|
||||||
|
for inp in c.querySelectorAll('input:checked'):
|
||||||
|
if inp.value:
|
||||||
|
ans.push(inp.value)
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
def create_selection_panel(container, apply_func, cancel_func):
|
def create_selection_panel(container, apply_func, cancel_func):
|
||||||
container.appendChild(E.div(id=CONTAINER, style='margin: 1rem'))
|
container.appendChild(E.div(id=CONTAINER, style='margin: 1rem'))
|
||||||
container = container.lastChild
|
container = container.lastChild
|
||||||
@ -144,6 +176,11 @@ def create_selection_panel(container, apply_func, cancel_func):
|
|||||||
E.h3(_('Available actions')),
|
E.h3(_('Available actions')),
|
||||||
E.div(class_='available-actions')
|
E.div(class_='available-actions')
|
||||||
))
|
))
|
||||||
|
container.appendChild(E.div(style='padding: 1ex; border-bottom: solid 1px; margin-bottom: 1ex',
|
||||||
|
E.h3(_('Quick highlight actions')),
|
||||||
|
E.div(_('Choose highlight styles that will have dedicated buttons in the selection bar to create highlights with a single click')),
|
||||||
|
E.div(class_='quick-actions'),
|
||||||
|
))
|
||||||
set_actions()
|
set_actions()
|
||||||
|
|
||||||
container.appendChild(create_button_box(restore_defaults, apply_func, cancel_func))
|
container.appendChild(create_button_box(restore_defaults, apply_func, cancel_func))
|
||||||
@ -171,5 +208,9 @@ def commit_selection(onchange):
|
|||||||
if list(actions) != list(sd.get('selection_bar_actions')):
|
if list(actions) != list(sd.get('selection_bar_actions')):
|
||||||
changed = True
|
changed = True
|
||||||
sd.set('selection_bar_actions', actions)
|
sd.set('selection_bar_actions', actions)
|
||||||
|
quick_highlights = selected_quick_actions()
|
||||||
|
if list(quick_highlights) != list(sd.get('selection_bar_quick_highlights')):
|
||||||
|
changed = True
|
||||||
|
sd.set('selection_bar_quick_highlights', quick_highlights)
|
||||||
if changed:
|
if changed:
|
||||||
onchange()
|
onchange()
|
||||||
|
@ -11,10 +11,13 @@ from book_list.theme import get_color
|
|||||||
from dom import clear, svgicon, unique_id
|
from dom import clear, svgicon, unique_id
|
||||||
from modals import error_dialog, question_dialog
|
from modals import error_dialog, question_dialog
|
||||||
from read_book.globals import runtime, ui_operations
|
from read_book.globals import runtime, ui_operations
|
||||||
from read_book.highlights import ICON_SIZE, EditNotesAndColors, HighlightStyle
|
from read_book.highlights import (
|
||||||
|
ICON_SIZE, EditNotesAndColors, HighlightStyle, all_styles
|
||||||
|
)
|
||||||
from read_book.shortcuts import shortcut_for_key_event
|
from read_book.shortcuts import shortcut_for_key_event
|
||||||
|
|
||||||
DRAG_SCROLL_ZONE_MIN_HEIGHT = 10
|
DRAG_SCROLL_ZONE_MIN_HEIGHT = 10
|
||||||
|
BUTTON_MARGIN = '0.5rem'
|
||||||
|
|
||||||
# Utils {{{
|
# Utils {{{
|
||||||
|
|
||||||
@ -291,7 +294,7 @@ class SelectionBar:
|
|||||||
self.view.focus_iframe()
|
self.view.focus_iframe()
|
||||||
)
|
)
|
||||||
ans.classList.add('simple-link')
|
ans.classList.add('simple-link')
|
||||||
ans.style.marginLeft = ans.style.marginRight = '0.5rem'
|
ans.style.marginLeft = ans.style.marginRight = BUTTON_MARGIN
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
actions = all_actions()
|
actions = all_actions()
|
||||||
@ -300,9 +303,31 @@ class SelectionBar:
|
|||||||
ac = actions[acname]
|
ac = actions[acname]
|
||||||
if ac and (not ac.needs_highlight or v'!!annot_id'):
|
if ac and (not ac.needs_highlight or v'!!annot_id'):
|
||||||
bar.appendChild(cb(ac, self[ac.function_name]))
|
bar.appendChild(cb(ac, self[ac.function_name]))
|
||||||
|
selection_bar_quick_highlights = sd.get('selection_bar_quick_highlights')
|
||||||
|
if selection_bar_quick_highlights?.length:
|
||||||
|
self.show_quick_highlight_buttons(bar, selection_bar_quick_highlights)
|
||||||
self.show_notes(bar_container, notes)
|
self.show_notes(bar_container, notes)
|
||||||
return bar_container
|
return bar_container
|
||||||
|
|
||||||
|
def show_quick_highlight_buttons(self, bar, actions):
|
||||||
|
all = {x.key:x for x in all_styles()}
|
||||||
|
actions = [a for a in actions if all[a]]
|
||||||
|
if not actions.length:
|
||||||
|
return
|
||||||
|
bar.appendChild(E.div(
|
||||||
|
style=f'background: currentColor; width: 1px; height: {ICON_SIZE}; margin-left: {BUTTON_MARGIN}; margin-right: {BUTTON_MARGIN}'
|
||||||
|
))
|
||||||
|
dark = self.view.current_color_scheme.is_dark_theme
|
||||||
|
for key in actions:
|
||||||
|
hs = all[key]
|
||||||
|
sw = E.div(
|
||||||
|
class_='simple-link', style=f'margin-left: {BUTTON_MARGIN}; margin-right: {BUTTON_MARGIN}',
|
||||||
|
title=_('Highlight using: {}').format(hs.friendly_name),
|
||||||
|
onclick=self.quick_highlight_with_style.bind(None, hs),
|
||||||
|
)
|
||||||
|
hs.make_swatch(sw, dark)
|
||||||
|
bar.appendChild(sw)
|
||||||
|
|
||||||
def show_notes(self, bar, notes):
|
def show_notes(self, bar, notes):
|
||||||
notes = (notes or "").strip()
|
notes = (notes or "").strip()
|
||||||
if not notes:
|
if not notes:
|
||||||
@ -793,6 +818,10 @@ class SelectionBar:
|
|||||||
self.state = WAITING
|
self.state = WAITING
|
||||||
self.update_position()
|
self.update_position()
|
||||||
|
|
||||||
|
def quick_highlight_with_style(self, hs):
|
||||||
|
self.current_highlight_style = hs
|
||||||
|
self.quick_highlight()
|
||||||
|
|
||||||
def remove_highlight(self):
|
def remove_highlight(self):
|
||||||
annot_id = self.view.currently_showing.selection.annot_id
|
annot_id = self.view.currently_showing.selection.annot_id
|
||||||
if annot_id:
|
if annot_id:
|
||||||
|
@ -17,8 +17,8 @@ from read_book.annotations import AnnotationsManager
|
|||||||
from read_book.bookmarks import create_new_bookmark
|
from read_book.bookmarks import create_new_bookmark
|
||||||
from read_book.content_popup import ContentPopupOverlay
|
from read_book.content_popup import ContentPopupOverlay
|
||||||
from read_book.globals import (
|
from read_book.globals import (
|
||||||
current_book, rtl_page_progression, runtime, set_current_spine_item,
|
current_book, is_dark_theme, rtl_page_progression, runtime,
|
||||||
ui_operations
|
set_current_spine_item, ui_operations
|
||||||
)
|
)
|
||||||
from read_book.goto import get_next_section
|
from read_book.goto import get_next_section
|
||||||
from read_book.open_book import add_book_to_recently_viewed
|
from read_book.open_book import add_book_to_recently_viewed
|
||||||
@ -301,6 +301,7 @@ class View:
|
|||||||
if runtime.is_standalone_viewer:
|
if runtime.is_standalone_viewer:
|
||||||
document.documentElement.addEventListener('keydown', self.handle_keypress, {'passive': False})
|
document.documentElement.addEventListener('keydown', self.handle_keypress, {'passive': False})
|
||||||
set_ui_colors(self.current_color_scheme.is_dark_theme)
|
set_ui_colors(self.current_color_scheme.is_dark_theme)
|
||||||
|
is_dark_theme(self.current_color_scheme.is_dark_theme)
|
||||||
self.iframe_wrapper = IframeWrapper(
|
self.iframe_wrapper = IframeWrapper(
|
||||||
handlers, document.getElementById(iframe_id), entry_point, _('Bootstrapping book reader...'),
|
handlers, document.getElementById(iframe_id), entry_point, _('Bootstrapping book reader...'),
|
||||||
f'{runtime.FAKE_PROTOCOL}://{runtime.SANDBOX_HOST}/book/__index__')
|
f'{runtime.FAKE_PROTOCOL}://{runtime.SANDBOX_HOST}/book/__index__')
|
||||||
@ -741,6 +742,7 @@ class View:
|
|||||||
self.current_color_scheme = ans = resolve_color_scheme()
|
self.current_color_scheme = ans = resolve_color_scheme()
|
||||||
if runtime.is_standalone_viewer:
|
if runtime.is_standalone_viewer:
|
||||||
set_ui_colors(self.current_color_scheme.is_dark_theme)
|
set_ui_colors(self.current_color_scheme.is_dark_theme)
|
||||||
|
is_dark_theme(self.current_color_scheme.is_dark_theme)
|
||||||
for which in 'left top right bottom'.split(' '):
|
for which in 'left top right bottom'.split(' '):
|
||||||
m = document.getElementById('book-{}-margin'.format(which))
|
m = document.getElementById('book-{}-margin'.format(which))
|
||||||
s = m.style
|
s = m.style
|
||||||
|
@ -67,6 +67,7 @@ defaults = {
|
|||||||
'show_selection_bar': True,
|
'show_selection_bar': True,
|
||||||
'net_search_url': 'https://google.com/search?q={q}',
|
'net_search_url': 'https://google.com/search?q={q}',
|
||||||
'selection_bar_actions': v"['copy', 'lookup', 'highlight', 'remove_highlight', 'search_net', 'clear']",
|
'selection_bar_actions': v"['copy', 'lookup', 'highlight', 'remove_highlight', 'search_net', 'clear']",
|
||||||
|
'selection_bar_quick_highlights': v"[]",
|
||||||
}
|
}
|
||||||
|
|
||||||
is_local_setting = {
|
is_local_setting = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user