mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Track annot over entire selection
Also work on notes/color editor panel
This commit is contained in:
parent
1ce8b68de6
commit
8fa67e7bfd
@ -1,14 +0,0 @@
|
|||||||
<?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>
|
|
Before Width: | Height: | Size: 262 B |
@ -106,7 +106,7 @@ def create_wrapper_function(wrapper_elem, r, intersecting_wrappers, process_wrap
|
|||||||
wrapper_counter = 0
|
wrapper_counter = 0
|
||||||
|
|
||||||
|
|
||||||
def wrap_text_in_range(style, r, process_wrapper):
|
def wrap_text_in_range(styler, r, process_wrapper):
|
||||||
if not r:
|
if not r:
|
||||||
sel = window.getSelection()
|
sel = window.getSelection()
|
||||||
if not sel or not sel.rangeCount:
|
if not sel or not sel.rangeCount:
|
||||||
@ -117,12 +117,8 @@ def wrap_text_in_range(style, r, process_wrapper):
|
|||||||
|
|
||||||
wrapper_elem = document.createElement('span')
|
wrapper_elem = document.createElement('span')
|
||||||
wrapper_elem.dataset.calibreRangeWrapper = v'++wrapper_counter' + ''
|
wrapper_elem.dataset.calibreRangeWrapper = v'++wrapper_counter' + ''
|
||||||
if style:
|
if styler:
|
||||||
style = style.rstrip()
|
styler(wrapper_elem)
|
||||||
if !style.endsWith(';'):
|
|
||||||
style += ';'
|
|
||||||
style = str.replace(style, ';', ' !important;')
|
|
||||||
wrapper_elem.setAttribute('style', style)
|
|
||||||
|
|
||||||
intersecting_wrappers = {}
|
intersecting_wrappers = {}
|
||||||
wrap_node = create_wrapper_function(wrapper_elem, r, intersecting_wrappers, process_wrapper)
|
wrap_node = create_wrapper_function(wrapper_elem, r, intersecting_wrappers, process_wrapper)
|
||||||
@ -152,27 +148,38 @@ def set_selection_to_highlight():
|
|||||||
return crw or None
|
return crw or None
|
||||||
|
|
||||||
|
|
||||||
|
def get_annot_id_for(node, offset, annot_id_uuid_map):
|
||||||
|
|
||||||
|
if not node:
|
||||||
|
return
|
||||||
|
if node.nodeType is Node.ELEMENT_NODE:
|
||||||
|
if node.dataset.calibreRangeWrapper:
|
||||||
|
return annot_id_uuid_map[node.dataset.calibreRangeWrapper]
|
||||||
|
if offset is 0:
|
||||||
|
|
||||||
|
if node.firstChild?.nodeType is Node.ELEMENT_NODE and node.firstChild.dataset.calibreRangeWrapper:
|
||||||
|
return annot_id_uuid_map[node.firstChild.dataset.calibreRangeWrapper]
|
||||||
|
elif offset < node.childNodes.length:
|
||||||
|
node = node.childNodes[offset]
|
||||||
|
return get_annot_id_for(node, 0)
|
||||||
|
elif node.nodeType is Node.TEXT_NODE:
|
||||||
|
if node.parentNode?.nodeType is Node.ELEMENT_NODE and node.parentNode.dataset.calibreRangeWrapper:
|
||||||
|
return annot_id_uuid_map[node.parentNode.dataset.calibreRangeWrapper]
|
||||||
|
|
||||||
|
|
||||||
def highlight_associated_with_selection(sel, annot_id_uuid_map):
|
def highlight_associated_with_selection(sel, annot_id_uuid_map):
|
||||||
# Return the annotation id for a highlight that contains the focus or
|
# Return the annotation id for a highlight intersecting the selection
|
||||||
# anchor of the selection
|
if sel.rangeCount:
|
||||||
|
annot_id = get_annot_id_for(sel.focusNode, sel.focusOffset, annot_id_uuid_map) or get_annot_id_for(sel.anchorNode, sel.anchorOffset, annot_id_uuid_map)
|
||||||
|
if annot_id:
|
||||||
|
return annot_id
|
||||||
|
|
||||||
def get_annot_id_for(node, offset):
|
all_wrappers = document.querySelectorAll('span[data-calibre-range-wrapper]')
|
||||||
if not node:
|
for v'var i = 0; i < sel.rangeCount; i++':
|
||||||
return
|
r = sel.getRangeAt(i)
|
||||||
if node.nodeType is Node.ELEMENT_NODE:
|
for v'var x = 0; x < all_wrappers.length; x++':
|
||||||
if node.dataset.calibreRangeWrapper:
|
wrapper = all_wrappers[x]
|
||||||
return annot_id_uuid_map[node.dataset.calibreRangeWrapper]
|
if r.intersectsNode(wrapper):
|
||||||
if offset is 0:
|
annot_id = annot_id_uuid_map[wrapper.dataset.calibreRangeWrapper]
|
||||||
if node.firstChild?.nodeType is Node.ELEMENT_NODE and node.firstChild.dataset.calibreRangeWrapper:
|
if annot_id:
|
||||||
return annot_id_uuid_map[node.firstChild.dataset.calibreRangeWrapper]
|
return annot_id
|
||||||
elif offset < node.childNodes.length:
|
|
||||||
node = node.childNodes[offset]
|
|
||||||
return get_annot_id_for(node, 0)
|
|
||||||
elif node.nodeType is Node.TEXT_NODE:
|
|
||||||
if node.parentNode?.nodeType is Node.ELEMENT_NODE and node.parentNode.dataset.calibreRangeWrapper:
|
|
||||||
return annot_id_uuid_map[node.parentNode.dataset.calibreRangeWrapper]
|
|
||||||
|
|
||||||
annot_id = get_annot_id_for(sel.focusNode, sel.focusOffset)
|
|
||||||
if not annot_id:
|
|
||||||
annot_id = get_annot_id_for(sel.anchorNode, sel.anchorOffset)
|
|
||||||
return annot_id
|
|
||||||
|
@ -2,6 +2,15 @@
|
|||||||
# 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 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 unique_id
|
||||||
|
from widgets import create_button
|
||||||
|
|
||||||
|
ICON_SIZE = '3ex'
|
||||||
builtin_colors_light = {
|
builtin_colors_light = {
|
||||||
'yellow': '#ffeb6b',
|
'yellow': '#ffeb6b',
|
||||||
'green': '#c0ed72',
|
'green': '#c0ed72',
|
||||||
@ -27,29 +36,237 @@ def default_color(is_dark):
|
|||||||
return builtin_color('yellow', is_dark)
|
return builtin_color('yellow', is_dark)
|
||||||
|
|
||||||
|
|
||||||
|
def all_builtin_styles():
|
||||||
|
ans = v'[]'
|
||||||
|
for col in builtin_colors_light:
|
||||||
|
ans.push({'type': 'builtin', 'kind': 'color', 'which': col})
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
def custom_color_theme(bg):
|
||||||
|
return {'type': 'custom', 'kind': 'color', 'which': bg}
|
||||||
|
|
||||||
|
|
||||||
class HighlightStyle:
|
class HighlightStyle:
|
||||||
|
|
||||||
def __init__(self, style):
|
def __init__(self, style):
|
||||||
|
if jstype(style) is 'string':
|
||||||
|
style = JSON.parse(style)
|
||||||
self.style = style or {'type': 'builtin', 'kind': 'color', 'which': 'yellow'}
|
self.style = style or {'type': 'builtin', 'kind': 'color', 'which': 'yellow'}
|
||||||
|
self.key = f'type:{style.type} kind:{style.kind} which: {style.which} bg: {style["background-color"]}'
|
||||||
|
|
||||||
|
def make_swatch(self, container, is_dark):
|
||||||
|
s = container.style
|
||||||
|
s.width = s.height = s.minimumWidth = s.minimumHeight = ICON_SIZE
|
||||||
|
bg = None
|
||||||
|
if s.type is 'builtin':
|
||||||
|
if s.kind is 'color':
|
||||||
|
bg = builtin_color(s.which, is_dark)
|
||||||
|
if bg is None and s['background-color']:
|
||||||
|
bg = s['background-color']
|
||||||
|
if bg:
|
||||||
|
s.backgroundColor = bg
|
||||||
|
s.borderRadius = '4px'
|
||||||
|
|
||||||
def highlight_shade(self, is_dark):
|
def highlight_shade(self, is_dark):
|
||||||
s = self.style
|
s = self.style
|
||||||
if s.type is 'builtin':
|
if s.type is 'builtin':
|
||||||
if s.kind is 'color':
|
if s.kind is 'color':
|
||||||
return builtin_color(s.which, is_dark)
|
return builtin_color(s.which, is_dark)
|
||||||
return default_color(is_dark)
|
|
||||||
return s['background-color'] or default_color(is_dark)
|
return s['background-color'] or default_color(is_dark)
|
||||||
|
|
||||||
def as_css(self, is_dark, foreground):
|
def serialized(self):
|
||||||
s = self.style
|
return JSON.stringify(self.style)
|
||||||
|
|
||||||
|
|
||||||
|
def highlight_style_as_css(s, is_dark, foreground):
|
||||||
|
|
||||||
|
def styler(node):
|
||||||
|
node = node.style
|
||||||
if s.type is 'builtin':
|
if s.type is 'builtin':
|
||||||
if s.kind is 'color':
|
if s.kind is 'color':
|
||||||
ans = 'background-color: ' + builtin_color(s.which, is_dark) + ';'
|
node.backgroundColor = builtin_color(s.which, is_dark)
|
||||||
if foreground:
|
if foreground:
|
||||||
ans += 'color: ' + foreground + ';'
|
node.color = foreground
|
||||||
return ans
|
return
|
||||||
ans = 'background-color: ' + (s['background-color'] or default_color(is_dark)) + ';'
|
node.backgroundColor = s['background-color'] or default_color(is_dark)
|
||||||
fg = s.color or foreground
|
fg = s.color or foreground
|
||||||
if fg:
|
if fg:
|
||||||
ans += 'color: ' + fg + ';'
|
node.color = fg
|
||||||
return ans
|
|
||||||
|
return styler
|
||||||
|
|
||||||
|
|
||||||
|
def custom_styles_equal(a, b):
|
||||||
|
seen = {}
|
||||||
|
for k in a:
|
||||||
|
seen[k] = True
|
||||||
|
if a[k] is not b[k]:
|
||||||
|
return False
|
||||||
|
for k in b:
|
||||||
|
if not seen[k]:
|
||||||
|
if a[k] is not b[k]:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class EditNotesAndColors: # {{{
|
||||||
|
|
||||||
|
def __init__(self, container, is_dark_theme, current_notes, current_style, close_editor):
|
||||||
|
self.initial_style = current_style
|
||||||
|
self.is_dark_theme = is_dark_theme
|
||||||
|
|
||||||
|
def separator():
|
||||||
|
return E.hr(style='max-width: 80em; width: 80vw; border-top: solid 1px; margin: auto; margin-top: 2ex; margin-bottom: 2ex')
|
||||||
|
|
||||||
|
def finish():
|
||||||
|
close_editor(True)
|
||||||
|
|
||||||
|
def abort():
|
||||||
|
close_editor(False)
|
||||||
|
|
||||||
|
def handle_keypress(ev):
|
||||||
|
ev.stopPropagation()
|
||||||
|
if ev.key is 'Escape':
|
||||||
|
abort()
|
||||||
|
elif ev.key is 'Enter' and ev.ctrlKey:
|
||||||
|
finish()
|
||||||
|
|
||||||
|
c = E.div(
|
||||||
|
style=f'background: {get_color("window-background")}; margin: auto; padding: 1rem',
|
||||||
|
onclick=def(ev): ev.stopPropagation();,
|
||||||
|
id=unique_id(),
|
||||||
|
E.h3(_('Add notes for this highlight')),
|
||||||
|
E.textarea(
|
||||||
|
current_notes or '',
|
||||||
|
rows='10', spellcheck='true', style='resize: none; width: 80vw; max-width: 80em; margin: 1ex',
|
||||||
|
onkeydown=handle_keypress,
|
||||||
|
),
|
||||||
|
E.div(
|
||||||
|
style='margin: 1ex; font-size: smaller',
|
||||||
|
_('Double click or long tap on a highlight to see its notes')
|
||||||
|
),
|
||||||
|
|
||||||
|
separator(),
|
||||||
|
|
||||||
|
E.h3(_('Choose the color for this highlight'), style='margin-bottom: 2ex'),
|
||||||
|
E.div(
|
||||||
|
class_='color-block',
|
||||||
|
style=f'display: flex; flex-wrap: wrap; max-width: calc({ICON_SIZE} * 8); margin: auto',
|
||||||
|
),
|
||||||
|
E.div(
|
||||||
|
style='max-width: 80em; width: 80vw; margin: auto; margin-top: 2ex; display: flex; justify-content: space-between; align-items: center',
|
||||||
|
E.div(
|
||||||
|
E.label(_('New color:'), ' ', E.input(type='color', onchange=self.add_custom_color))
|
||||||
|
),
|
||||||
|
E.div(
|
||||||
|
E.a(_('Remove color'), class_='simple-link remove-custom-color', onclick=self.remove_custom_color),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
separator(),
|
||||||
|
|
||||||
|
E.div(
|
||||||
|
style='max-width: 80em; width: 80vw; margin: auto; display: flex; justify-content: space-between',
|
||||||
|
create_button(_('Cancel'), 'close', abort, _('Abort') + ' [Esc]'),
|
||||||
|
create_button(_('Finish'), 'check', finish, _('Finish editing highlight') + ' [Ctrl+Enter]', True),
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
self.container_id = c.id
|
||||||
|
container.appendChild(c)
|
||||||
|
self.seen_colors = {}
|
||||||
|
custom_highlight_styles = get_session_data().get('custom_highlight_styles')
|
||||||
|
for raw in custom_highlight_styles:
|
||||||
|
self.add_color(HighlightStyle(raw)).classList.add('custom-style')
|
||||||
|
for raw in all_builtin_styles():
|
||||||
|
self.add_color(HighlightStyle(raw))
|
||||||
|
if not c.querySelector('.current-swatch'):
|
||||||
|
self.add_color(self.initial_style)
|
||||||
|
|
||||||
|
self.set_visibility_of_remove_button()
|
||||||
|
self.notes_edit.focus()
|
||||||
|
|
||||||
|
def set_visibility_of_remove_button(self):
|
||||||
|
c = self.container
|
||||||
|
item = c.querySelector('.current-swatch.custom-style')
|
||||||
|
visibility = 'unset' if item else 'hidden'
|
||||||
|
c.querySelector('.remove-custom-color').style.visibility = visibility
|
||||||
|
|
||||||
|
def add_color(self, hs, at_start):
|
||||||
|
if self.seen_colors[hs.key]:
|
||||||
|
return
|
||||||
|
self.seen_colors[hs.key] = True
|
||||||
|
ic = E.div()
|
||||||
|
hs.make_swatch(ic, self.is_dark_theme)
|
||||||
|
ic.classList.add('simple-link')
|
||||||
|
is_current = hs.key is self.initial_style.key
|
||||||
|
sqbg = get_color('window-background2') if is_current else 'unset'
|
||||||
|
item = E.div(
|
||||||
|
ic, style=f'padding: 4px; background-color: {sqbg}; margin: 4px',
|
||||||
|
onclick=self.change_color
|
||||||
|
)
|
||||||
|
if is_current:
|
||||||
|
item.classList.add('current-swatch')
|
||||||
|
item.dataset.style = hs.serialized()
|
||||||
|
parent = self.container.getElementsByClassName('color-block')[0]
|
||||||
|
if at_start:
|
||||||
|
parent.insertBefore(item, parent.firstChild)
|
||||||
|
else:
|
||||||
|
parent.appendChild(item)
|
||||||
|
return item
|
||||||
|
|
||||||
|
def add_custom_color(self):
|
||||||
|
bg = self.container.querySelector('input[type=color]').value
|
||||||
|
cct = custom_color_theme(bg)
|
||||||
|
item = self.add_color(HighlightStyle(cct), True)
|
||||||
|
item.classList.add('custom-style')
|
||||||
|
self.make_swatch_current(item)
|
||||||
|
sd = get_session_data()
|
||||||
|
custom_highlight_styles = sd.get('custom_highlight_styles')
|
||||||
|
custom_highlight_styles.unshift(cct)
|
||||||
|
sd.set('custom_highlight_styles', custom_highlight_styles)
|
||||||
|
|
||||||
|
def remove_custom_color(self):
|
||||||
|
item = self.container.getElementsByClassName('current-swatch')[0]
|
||||||
|
cct = JSON.parse(item.dataset.style)
|
||||||
|
p = item.parentNode
|
||||||
|
p.removeChild(item)
|
||||||
|
self.make_swatch_current(p.firstChild)
|
||||||
|
sd = get_session_data()
|
||||||
|
custom_highlight_styles = sd.get('custom_highlight_styles')
|
||||||
|
ans = v'[]'
|
||||||
|
for x in custom_highlight_styles:
|
||||||
|
if not custom_styles_equal(x, cct):
|
||||||
|
ans.push(x)
|
||||||
|
sd.set('custom_highlight_styles', ans)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def container(self):
|
||||||
|
return document.getElementById(self.container_id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def notes_edit(self):
|
||||||
|
return self.container.getElementsByTagName('textarea')[0]
|
||||||
|
|
||||||
|
def change_color(self, evt):
|
||||||
|
evt.stopPropagation()
|
||||||
|
self.make_swatch_current(evt.currentTarget)
|
||||||
|
|
||||||
|
def make_swatch_current(self, item):
|
||||||
|
for child in item.parentNode.childNodes:
|
||||||
|
child.style.backgroundColor = 'unset'
|
||||||
|
child.classList.remove('current-swatch')
|
||||||
|
item.style.backgroundColor = get_color('window-background2')
|
||||||
|
item.classList.add('current-swatch')
|
||||||
|
self.notes_edit.focus()
|
||||||
|
self.set_visibility_of_remove_button()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_notes(self):
|
||||||
|
return self.notes_edit.value or ''
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_style(self):
|
||||||
|
return HighlightStyle(self.container.getElementsByClassName('current-swatch')[0].dataset.style)
|
||||||
|
# }}}
|
||||||
|
@ -35,6 +35,7 @@ from read_book.globals import (
|
|||||||
current_spine_item, runtime, set_boss, set_current_spine_item, set_layout_mode,
|
current_spine_item, runtime, set_boss, set_current_spine_item, set_layout_mode,
|
||||||
set_toc_anchor_map
|
set_toc_anchor_map
|
||||||
)
|
)
|
||||||
|
from read_book.highlights import highlight_style_as_css
|
||||||
from read_book.mathjax import apply_mathjax
|
from read_book.mathjax import apply_mathjax
|
||||||
from read_book.paged_mode import (
|
from read_book.paged_mode import (
|
||||||
anchor_funcs as paged_anchor_funcs,
|
anchor_funcs as paged_anchor_funcs,
|
||||||
@ -782,7 +783,8 @@ class IframeBoss:
|
|||||||
if not sel.rangeCount:
|
if not sel.rangeCount:
|
||||||
return
|
return
|
||||||
bounds = cfi_for_selection()
|
bounds = cfi_for_selection()
|
||||||
annot_id, intersecting_wrappers = wrap_text_in_range(data.style, None, self.add_highlight_listeners)
|
style = highlight_style_as_css(data.style, opts.is_dark_theme, opts.color_scheme.foreground)
|
||||||
|
annot_id, intersecting_wrappers = wrap_text_in_range(style, None, self.add_highlight_listeners)
|
||||||
removed_highlights = v'[]'
|
removed_highlights = v'[]'
|
||||||
if annot_id is not None:
|
if annot_id is not None:
|
||||||
intersecting_uuids = {annot_id_uuid_map[x]:True for x in intersecting_wrappers}
|
intersecting_uuids = {annot_id_uuid_map[x]:True for x in intersecting_wrappers}
|
||||||
@ -821,7 +823,7 @@ class IframeBoss:
|
|||||||
r = range_from_cfi(h.start_cfi, h.end_cfi)
|
r = range_from_cfi(h.start_cfi, h.end_cfi)
|
||||||
if not r:
|
if not r:
|
||||||
continue
|
continue
|
||||||
style = f'color: {h.style.color}; background-color: {h.style["background-color"]}'
|
style = highlight_style_as_css(h.style, opts.is_dark_theme, opts.color_scheme.foreground)
|
||||||
annot_id, intersecting_wrappers = wrap_text_in_range(style, r, self.add_highlight_listeners)
|
annot_id, intersecting_wrappers = wrap_text_in_range(style, r, self.add_highlight_listeners)
|
||||||
if annot_id is not None:
|
if annot_id is not None:
|
||||||
annot_id_uuid_map[annot_id] = h.uuid
|
annot_id_uuid_map[annot_id] = h.uuid
|
||||||
|
@ -9,9 +9,8 @@ from book_list.globals import get_session_data
|
|||||||
from book_list.theme import get_color
|
from book_list.theme import get_color
|
||||||
from dom import clear, svgicon, unique_id
|
from dom import clear, svgicon, unique_id
|
||||||
from read_book.globals import runtime, ui_operations
|
from read_book.globals import runtime, ui_operations
|
||||||
from read_book.highlights import HighlightStyle
|
from read_book.highlights import ICON_SIZE, EditNotesAndColors, HighlightStyle
|
||||||
|
|
||||||
ICON_SIZE = '3ex'
|
|
||||||
DRAG_SCROLL_ZONE_MIN_HEIGHT = 10
|
DRAG_SCROLL_ZONE_MIN_HEIGHT = 10
|
||||||
|
|
||||||
# Utils {{{
|
# Utils {{{
|
||||||
@ -220,6 +219,7 @@ class SelectionBar:
|
|||||||
self.drag_scroll_timer = None
|
self.drag_scroll_timer = None
|
||||||
self.last_drag_scroll_at = -100000
|
self.last_drag_scroll_at = -100000
|
||||||
self.left_line_height = self.right_line_height = 0
|
self.left_line_height = self.right_line_height = 0
|
||||||
|
self.current_editor = None
|
||||||
|
|
||||||
left_handle = selection_handle(True)
|
left_handle = selection_handle(True)
|
||||||
left_handle.id = self.left_handle_id
|
left_handle.id = self.left_handle_id
|
||||||
@ -235,6 +235,7 @@ class SelectionBar:
|
|||||||
'left: 0; top: 0; display: flex; flex-direction: column;'
|
'left: 0; top: 0; display: flex; flex-direction: column;'
|
||||||
))
|
))
|
||||||
container.appendChild(E.div(id=self.editor_id))
|
container.appendChild(E.div(id=self.editor_id))
|
||||||
|
container.lastChild.addEventListener('click', self.editor_container_clicked, {'passive': False})
|
||||||
|
|
||||||
# bar and handles markup {{{
|
# bar and handles markup {{{
|
||||||
|
|
||||||
@ -262,7 +263,7 @@ class SelectionBar:
|
|||||||
]:
|
]:
|
||||||
bar_container.appendChild(x)
|
bar_container.appendChild(x)
|
||||||
bar = bar_container.firstChild
|
bar = bar_container.firstChild
|
||||||
hs = self.current_highlight_style.highlight_shade
|
hs = self.current_highlight_style.highlight_shade(self.view.current_color_scheme.is_dark_theme)
|
||||||
|
|
||||||
def cb(ac, callback):
|
def cb(ac, callback):
|
||||||
ans = ac.icon_function(hs)
|
ans = ac.icon_function(hs)
|
||||||
@ -381,7 +382,7 @@ class SelectionBar:
|
|||||||
def container_clicked(self, ev):
|
def container_clicked(self, ev):
|
||||||
ev.stopPropagation(), ev.preventDefault()
|
ev.stopPropagation(), ev.preventDefault()
|
||||||
if self.state is EDITING:
|
if self.state is EDITING:
|
||||||
return # TODO: accept edit and apply highlight
|
self.hide_editor(True)
|
||||||
if self.state is WAITING:
|
if self.state is WAITING:
|
||||||
for x in (self.bar, self.left_handle, self.right_handle):
|
for x in (self.bar, self.left_handle, self.right_handle):
|
||||||
if near_element(x, ev.clientX, ev.clientY):
|
if near_element(x, ev.clientX, ev.clientY):
|
||||||
@ -498,6 +499,7 @@ class SelectionBar:
|
|||||||
self.editor.style.display = 'none'
|
self.editor.style.display = 'none'
|
||||||
self.set_handle_colors()
|
self.set_handle_colors()
|
||||||
if self.state is DRAGGING:
|
if self.state is DRAGGING:
|
||||||
|
self.show()
|
||||||
return
|
return
|
||||||
self.left_handle.style.display = 'none'
|
self.left_handle.style.display = 'none'
|
||||||
self.right_handle.style.display = 'none'
|
self.right_handle.style.display = 'none'
|
||||||
@ -516,7 +518,7 @@ class SelectionBar:
|
|||||||
|
|
||||||
self.show()
|
self.show()
|
||||||
if self.state is EDITING:
|
if self.state is EDITING:
|
||||||
return self.show_editor() # TODO: Implement this
|
return
|
||||||
self.bar.style.display = self.left_handle.style.display = self.right_handle.style.display = 'block'
|
self.bar.style.display = self.left_handle.style.display = self.right_handle.style.display = 'block'
|
||||||
start = map_boundary(cs.start)
|
start = map_boundary(cs.start)
|
||||||
end = map_boundary(cs.end)
|
end = map_boundary(cs.end)
|
||||||
@ -591,7 +593,24 @@ class SelectionBar:
|
|||||||
start, end = end, start
|
start, end = end, start
|
||||||
place_single_handle(left_handle, start, True)
|
place_single_handle(left_handle, start, True)
|
||||||
place_single_handle(right_handle, end, False)
|
place_single_handle(right_handle, end, False)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# Editor {{{
|
||||||
|
def show_editor(self, highlight_style, notes):
|
||||||
|
for x in (self.bar, self.left_handle, self.right_handle):
|
||||||
|
x.style.display = 'none'
|
||||||
|
container = self.editor
|
||||||
|
clear(container)
|
||||||
|
container.style.display = 'block'
|
||||||
|
self.state = EDITING
|
||||||
|
self.current_editor = EditNotesAndColors(
|
||||||
|
container, self.view.current_color_scheme.is_dark_theme, notes, highlight_style, self.hide_editor)
|
||||||
|
|
||||||
|
def hide_editor(self, apply):
|
||||||
|
pass # TODO: Implement this
|
||||||
|
|
||||||
|
def editor_container_clicked(self, ev):
|
||||||
|
ev.stopPropagation(), ev.preventDefault()
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
# Actions {{{
|
# Actions {{{
|
||||||
@ -617,7 +636,17 @@ class SelectionBar:
|
|||||||
self.hide()
|
self.hide()
|
||||||
|
|
||||||
def create_highlight(self):
|
def create_highlight(self):
|
||||||
pass # TODO: Implement this
|
cs = self.view.currently_showing.selection
|
||||||
|
hs = self.current_highlight_style
|
||||||
|
notes = ''
|
||||||
|
if cs.annot_id:
|
||||||
|
am = self.view.annotations_manager
|
||||||
|
q = am.style_for_highlight(cs.annot_id)
|
||||||
|
if q:
|
||||||
|
hs = HighlightStyle(q)
|
||||||
|
notes = am.notes_for_highlight(cs.annot_id) or notes
|
||||||
|
self.show()
|
||||||
|
self.show_editor(hs, notes)
|
||||||
|
|
||||||
def quick_highlight(self):
|
def quick_highlight(self):
|
||||||
cs = self.view.currently_showing.selection
|
cs = self.view.currently_showing.selection
|
||||||
|
@ -63,7 +63,7 @@ defaults = {
|
|||||||
'user_stylesheet': '',
|
'user_stylesheet': '',
|
||||||
'word_actions': v'[]',
|
'word_actions': v'[]',
|
||||||
'highlight_style': None,
|
'highlight_style': None,
|
||||||
'custom_highlight_colors': v'[]',
|
'custom_highlight_styles': v'[]',
|
||||||
'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']",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user