mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 18:24:30 -04:00
Remove the create_annotation module
The entire UI is being removed since all operations will be performed via the selection bar
This commit is contained in:
parent
948acd6a55
commit
1ce8b68de6
@ -285,13 +285,10 @@ class HighlightsPanel(QWidget):
|
||||
b.clicked.connect(target)
|
||||
return b
|
||||
|
||||
self.add_button = button('plus.png', _('Add'), _('Create a new highlight'), self.add_highlight)
|
||||
self.edit_button = button('edit_input.png', _('Edit'), _('Edit the selected highlight'), self.edit_highlight)
|
||||
self.remove_button = button('trash.png', _('Remove'), _('Remove the selected highlights'), self.remove_highlight)
|
||||
h.addWidget(self.add_button), h.addWidget(self.edit_button), h.addWidget(self.remove_button)
|
||||
|
||||
self.export_button = button('save.png', _('Export'), _('Export all highlights'), self.export)
|
||||
l.addWidget(self.export_button)
|
||||
h.addWidget(self.edit_button), h.addWidget(self.remove_button), h.addWidget(self.export_button)
|
||||
|
||||
self.notes_display = nd = NotesDisplay(self)
|
||||
nd.notes_edited.connect(self.notes_edited)
|
||||
@ -356,9 +353,6 @@ class HighlightsPanel(QWidget):
|
||||
for h in highlights:
|
||||
self.request_highlight_action.emit(h['uuid'], 'delete')
|
||||
|
||||
def add_highlight(self):
|
||||
self.request_highlight_action.emit(None, 'create')
|
||||
|
||||
def export(self):
|
||||
hl = list(self.highlights.all_highlights)
|
||||
if not hl:
|
||||
|
@ -1,708 +0,0 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
from __python__ import bound_methods, hash_literals
|
||||
|
||||
from elementmaker import E
|
||||
from gettext import gettext as _
|
||||
from uuid import short_uuid
|
||||
|
||||
from book_list.globals import get_session_data
|
||||
from book_list.theme import cached_color_to_rgba, get_color
|
||||
from dom import clear, ensure_id, svgicon, unique_id
|
||||
from modals import error_dialog, question_dialog
|
||||
from read_book.globals import ui_operations
|
||||
from read_book.shortcuts import shortcut_for_key_event
|
||||
from widgets import create_button
|
||||
|
||||
|
||||
WAITING_FOR_CLICK = 1
|
||||
WAITING_FOR_DRAG = 2
|
||||
DRAGGING_LEFT = 3
|
||||
DRAGGING_RIGHT = 4
|
||||
|
||||
dark_fg = '#111'
|
||||
light_fg = '#eee'
|
||||
builtin_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 default_highlight_style():
|
||||
return {
|
||||
'background-color': default_highlight_color,
|
||||
'color': builtin_highlight_colors[default_highlight_color]
|
||||
}
|
||||
|
||||
|
||||
def selection_handle(invert, style):
|
||||
ans = svgicon('selection-handle')
|
||||
use = ans.querySelector('use')
|
||||
use.style.stroke = style['color']
|
||||
use.style.fill = style['background-color']
|
||||
s = ans.style
|
||||
if invert:
|
||||
s.transform = 'scaleX(-1)'
|
||||
s.position = 'absolute'
|
||||
s.boxSizing = 'border-box'
|
||||
s.touchAction = 'none'
|
||||
return ans
|
||||
|
||||
|
||||
def map_from_iframe_coords(point):
|
||||
l = document.getElementById('book-left-margin')
|
||||
point.x += l.offsetWidth
|
||||
t = document.getElementById('book-top-margin')
|
||||
point.y += t.offsetHeight
|
||||
return point
|
||||
|
||||
|
||||
def map_to_iframe_coords(point):
|
||||
l = document.getElementById('book-left-margin')
|
||||
point.x -= l.offsetWidth
|
||||
t = document.getElementById('book-top-margin')
|
||||
point.y -= t.offsetHeight
|
||||
return point
|
||||
|
||||
|
||||
BAR_SIZE = 32
|
||||
DRAG_SCROLL_ZONE_MIN_HEIGHT = 10
|
||||
|
||||
|
||||
def create_bar():
|
||||
style = f'min-height: 1px; min-width: 1px; max-height: {BAR_SIZE}px; height: {BAR_SIZE}px'
|
||||
ans = E.div(
|
||||
id=unique_id('annot-bar'),
|
||||
style=f'height: {BAR_SIZE}px; max-height: {BAR_SIZE}px; width: 100vw; display: flex; justify-content: space-between;',
|
||||
E.div(style=style), E.div(style=style), E.div(style=style),
|
||||
)
|
||||
return ans
|
||||
|
||||
|
||||
class EditNotesAndColors: # {{{
|
||||
|
||||
def __init__(self, container, hide_middle, accept, current_notes, current_style):
|
||||
self.initial_style = current_style
|
||||
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():
|
||||
hide_middle()
|
||||
accept()
|
||||
|
||||
def handle_keypress(ev):
|
||||
ev.stopPropagation()
|
||||
if ev.key is 'Escape':
|
||||
hide_middle()
|
||||
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({BAR_SIZE}px * 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(_('Adjust selection'), 'arrows-h', hide_middle, _('Accept changes and then adjust the selected text') + ' [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_colors = get_session_data().get('custom_highlight_colors')
|
||||
for bg in custom_highlight_colors:
|
||||
self.add_color(bg).classList.add('custom-color')
|
||||
for bg in builtin_highlight_colors:
|
||||
self.add_color(bg)
|
||||
if not c.querySelector('.current-swatch'):
|
||||
self.add_color(self.initial_style['background-color'])
|
||||
|
||||
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-color')
|
||||
visibility = 'unset' if item else 'hidden'
|
||||
c.querySelector('.remove-custom-color').style.visibility = visibility
|
||||
|
||||
def add_color(self, bg, at_start):
|
||||
if self.seen_colors[bg]:
|
||||
return
|
||||
self.seen_colors[bg] = True
|
||||
ic = svgicon('swatch', BAR_SIZE, BAR_SIZE)
|
||||
ic.classList.add('simple-link')
|
||||
is_current = bg.lower() is self.initial_style['background-color'].lower()
|
||||
sqbg = get_color('window-background2') if is_current else 'unset'
|
||||
ic.querySelector('use').style.fill = bg
|
||||
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.bg = bg
|
||||
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
|
||||
item = self.add_color(bg, True)
|
||||
item.classList.add('custom-color')
|
||||
self.make_swatch_current(item)
|
||||
sd = get_session_data()
|
||||
custom_highlight_colors = sd.get('custom_highlight_colors')
|
||||
custom_highlight_colors.unshift(bg)
|
||||
sd.set('custom_highlight_colors', custom_highlight_colors)
|
||||
|
||||
def remove_custom_color(self):
|
||||
item = self.container.getElementsByClassName('current-swatch')[0]
|
||||
bg = item.dataset.bg
|
||||
p = item.parentNode
|
||||
p.removeChild(item)
|
||||
self.make_swatch_current(p.firstChild)
|
||||
sd = get_session_data()
|
||||
custom_highlight_colors = sd.get('custom_highlight_colors')
|
||||
idx = custom_highlight_colors.indexOf(bg)
|
||||
if idx > -1:
|
||||
custom_highlight_colors.splice(idx, 1)
|
||||
sd.set('custom_highlight_colors', custom_highlight_colors)
|
||||
|
||||
@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):
|
||||
bg = self.container.getElementsByClassName('current-swatch')[0].dataset.bg
|
||||
fg = builtin_highlight_colors[bg]
|
||||
if not fg:
|
||||
rgba = cached_color_to_rgba(bg)
|
||||
is_dark = max(rgba[0], rgba[1], rgba[2]) < 115
|
||||
fg = light_fg if is_dark else dark_fg
|
||||
return {'background-color': bg, 'color': fg}
|
||||
# }}}
|
||||
|
||||
|
||||
class CreateAnnotation: # {{{
|
||||
|
||||
container_id = 'create-annotation-overlay'
|
||||
|
||||
def __init__(self, view):
|
||||
self.view = view
|
||||
self.active_touch = None
|
||||
self.drag_scroll_timer = None
|
||||
self.last_drag_scroll_at = -100000
|
||||
self.editing_annot_uuid = None
|
||||
self.current_notes = ''
|
||||
self.annotations_manager = self.view.annotations_manager
|
||||
self.state = WAITING_FOR_CLICK
|
||||
self.left_line_height = self.right_line_height = 8
|
||||
self.in_flow_mode = False
|
||||
container = self.container
|
||||
container.style.flexDirection = 'column'
|
||||
container.style.justifyContent = 'space-between'
|
||||
self.position_in_handle = {'x': 0, 'y': 0}
|
||||
|
||||
def button(name, bar, icon, tt, action):
|
||||
cb = svgicon(icon, bar.style.height, bar.style.height, tt)
|
||||
cb.setAttribute('title', tt)
|
||||
cb.style.backgroundColor = get_color('window-background')
|
||||
cb.style.boxSizing = 'border-box'
|
||||
cb.style.padding = '2px'
|
||||
cb.style.border = 'solid 2px currentColor'
|
||||
cb.style.borderRadius = '4px'
|
||||
cb.style.marginLeft = '0.5rem'
|
||||
cb.style.marginRight = '0.5rem'
|
||||
cb.classList.add('simple-link')
|
||||
cb.classList.add('adjust-button')
|
||||
cb.classList.add(f'button-{name}')
|
||||
cb.addEventListener('click', def(ev):
|
||||
ev.preventDefault(), ev.stopPropagation()
|
||||
action()
|
||||
)
|
||||
bar.appendChild(cb)
|
||||
return cb
|
||||
|
||||
tb = create_bar()
|
||||
container.appendChild(tb)
|
||||
button('close', tb.firstChild, 'close', _('Cancel creation of highlight') + ' [Esc]', self.hide)
|
||||
button('up', tb.firstChild.nextSibling, 'chevron-up', _('Scroll up') + ' [Up]', self.button_scroll.bind(None, True))
|
||||
button('copy', tb.lastChild, 'copy', _('Copy to clipboard'), self.copy_to_clipboard)
|
||||
button('finish', tb.lastChild, 'check', _('Finish creation of highlight') + ' [Enter]', self.accept)
|
||||
|
||||
middle = E.div(id=unique_id('middle'), style='display: none; text-align: center; z-index: 90000')
|
||||
self.middle_id = middle.id
|
||||
container.appendChild(middle)
|
||||
|
||||
bb = create_bar()
|
||||
container.appendChild(bb)
|
||||
button('remove', bb.firstChild, 'trash', _('Remove this highlight'), self.delete_highlight)
|
||||
button('down', bb.firstChild.nextSibling, 'chevron-down', _('Scroll down') + ' [Down]', self.button_scroll)
|
||||
button('edit', bb.lastChild, 'pencil', _('Edit notes and change highlight color') + ' [e]', self.edit_notes_and_colors)
|
||||
|
||||
sd = get_session_data()
|
||||
style = sd.get('highlight_style') or default_highlight_style()
|
||||
if not style['background-color'] or not style['color']:
|
||||
style = default_highlight_style()
|
||||
self.current_highlight_style = style
|
||||
|
||||
lh = selection_handle(False, style)
|
||||
self.left_handle_id = ensure_id(lh, 'handle')
|
||||
lh.addEventListener('mousedown', self.mousedown_on_handle, {'passive': False})
|
||||
lh.addEventListener('touchstart', self.touchstart_on_handle, {'passive': False})
|
||||
container.appendChild(lh)
|
||||
rh = selection_handle(True, style)
|
||||
self.right_handle_id = ensure_id(rh, 'handle')
|
||||
rh.addEventListener('mousedown', self.mousedown_on_handle, {'passive': False})
|
||||
rh.addEventListener('touchstart', self.touchstart_on_handle, {'passive': False})
|
||||
container.appendChild(rh)
|
||||
|
||||
container.addEventListener('click', self.container_clicked, {'passive': False})
|
||||
container.addEventListener('mouseup', self.mouseup_on_container, {'passive': False})
|
||||
container.addEventListener('mousemove', self.mousemove_on_container, {'passive': False})
|
||||
container.addEventListener('touchmove', self.touchmove_on_container, {'passive': False})
|
||||
container.addEventListener('touchend', self.touchend_on_container, {'passive': False})
|
||||
container.addEventListener('touchcancel', self.touchend_on_container, {'passive': False})
|
||||
container.addEventListener('keydown', self.on_keydown, {'passive': False})
|
||||
|
||||
def copy_to_clipboard(self):
|
||||
ui_operations.copy_selection(self.view.currently_showing.selection.text or '')
|
||||
|
||||
@property
|
||||
def middle(self):
|
||||
return document.getElementById(self.middle_id)
|
||||
|
||||
def edit_notes_and_colors(self):
|
||||
if self.editor:
|
||||
return
|
||||
current_notes = self.current_notes
|
||||
if not current_notes and self.editing_annot_uuid:
|
||||
current_notes = self.annotations_manager.notes_for_highlight(self.editing_annot_uuid)
|
||||
current_style = self.current_highlight_style
|
||||
self.show_middle(self.editing_done)
|
||||
container = self.middle
|
||||
clear(container)
|
||||
self.editor = EditNotesAndColors(container, self.hide_middle, self.accept, current_notes, current_style)
|
||||
|
||||
def editing_done(self):
|
||||
self.current_notes = self.editor.current_notes
|
||||
new_highlight_style = self.editor.current_style
|
||||
self.editor = None
|
||||
if self.current_highlight_style['background-color'] is not new_highlight_style['background_color']:
|
||||
self.current_highlight_style = new_highlight_style
|
||||
self.send_message('set-highlight-style', style=self.current_highlight_style)
|
||||
get_session_data().set('highlight_style', self.current_highlight_style)
|
||||
self.update_handle_colors()
|
||||
return True
|
||||
|
||||
def update_handle_colors(self):
|
||||
fill = self.current_highlight_style['background-color']
|
||||
stroke = self.view.current_color_scheme.foreground
|
||||
for handle in (self.left_handle, self.right_handle):
|
||||
use = handle.querySelector('use')
|
||||
use.style.stroke = stroke
|
||||
use.style.fill = fill
|
||||
|
||||
def show_middle(self, pre_close_callback):
|
||||
self.pre_middle_close_callback = pre_close_callback
|
||||
for h in (self.left_handle, self.right_handle):
|
||||
h.style.visibility = 'hidden'
|
||||
for button in self.container.querySelectorAll('.adjust-button'):
|
||||
button.style.visibility = 'hidden'
|
||||
self.middle.style.display = 'block'
|
||||
|
||||
def hide_middle(self):
|
||||
m = self.middle
|
||||
if m.style.display is not 'none':
|
||||
if self.pre_middle_close_callback:
|
||||
if not self.pre_middle_close_callback():
|
||||
return
|
||||
self.pre_middle_close_callback = None
|
||||
for button in self.container.querySelectorAll('.adjust-button'):
|
||||
button.style.visibility = 'unset'
|
||||
for h in (self.left_handle, self.right_handle):
|
||||
h.style.visibility = 'unset'
|
||||
m.style.display = 'none'
|
||||
self.container.focus()
|
||||
|
||||
def accept(self):
|
||||
s = self.current_highlight_style
|
||||
style = ''
|
||||
for k in Object.keys(self.current_highlight_style):
|
||||
style += f'{k}: {s[k]}; '
|
||||
self.send_message(
|
||||
'apply-highlight', style=style, uuid=short_uuid(), existing=self.editing_annot_uuid
|
||||
)
|
||||
self.hide()
|
||||
|
||||
def quick_create(self):
|
||||
s = self.current_highlight_style
|
||||
style = ''
|
||||
for k in Object.keys(self.current_highlight_style):
|
||||
style += f'{k}: {s[k]}; '
|
||||
self.send_message(
|
||||
'apply-highlight', style=style, uuid=short_uuid()
|
||||
)
|
||||
|
||||
def on_keydown(self, ev):
|
||||
ev.stopPropagation(), ev.preventDefault()
|
||||
if ev.key is 'Enter':
|
||||
return self.accept()
|
||||
if ev.key is 'e' or ev.key is 'E':
|
||||
return self.edit_notes_and_colors()
|
||||
sc_name = shortcut_for_key_event(ev, self.view.keyboard_shortcut_map)
|
||||
if sc_name is 'show_chrome':
|
||||
self.hide()
|
||||
elif sc_name in ('up', 'down', 'pageup', 'pagedown'):
|
||||
backwards = 'up' in sc_name
|
||||
if 'page' in sc_name or not self.in_flow_mode:
|
||||
self.paged_scroll(backwards)
|
||||
else:
|
||||
self.send_drag_scroll_message(backwards, 'left' if backwards else 'right', False)
|
||||
elif sc_name in ('left', 'right'):
|
||||
if self.in_flow_mode:
|
||||
self.send_message('perp-scroll', backwards=bool(sc_name is 'left'))
|
||||
else:
|
||||
self.paged_scroll(sc_name is 'left')
|
||||
|
||||
def container_clicked(self, ev):
|
||||
ev.stopPropagation(), ev.preventDefault()
|
||||
if self.middle.style.display is not 'none':
|
||||
self.hide_middle()
|
||||
self.accept()
|
||||
return
|
||||
if self.state is WAITING_FOR_CLICK:
|
||||
pt = map_to_iframe_coords({'x': ev.clientX, 'y': ev.clientY})
|
||||
self.send_message('position-handles-at-point', x=pt.x, y=pt.y)
|
||||
|
||||
def start_handle_drag(self, ev, q):
|
||||
if q is self.left_handle_id:
|
||||
self.state = DRAGGING_LEFT
|
||||
handle = self.left_handle
|
||||
elif q is self.right_handle_id:
|
||||
self.state = DRAGGING_RIGHT
|
||||
handle = self.right_handle
|
||||
r = handle.getBoundingClientRect()
|
||||
self.position_in_handle.x = Math.round(ev.clientX - r.left)
|
||||
self.position_in_handle.y = Math.round(ev.clientY - r.top)
|
||||
|
||||
def mousedown_on_handle(self, ev):
|
||||
ev.stopPropagation(), ev.preventDefault()
|
||||
if self.state is WAITING_FOR_CLICK:
|
||||
return
|
||||
self.start_handle_drag(ev, ev.currentTarget.id)
|
||||
|
||||
def touchstart_on_handle(self, ev):
|
||||
ev.stopPropagation(), ev.preventDefault()
|
||||
if self.state is WAITING_FOR_CLICK:
|
||||
return
|
||||
for touch in ev.changedTouches:
|
||||
self.active_touch = touch.identifier
|
||||
self.start_handle_drag(touch, ev.currentTarget.id)
|
||||
break
|
||||
|
||||
def button_scroll(self, backwards):
|
||||
if self.in_flow_mode:
|
||||
self.send_drag_scroll_message(backwards, 'left' if backwards else 'right', False)
|
||||
else:
|
||||
self.paged_scroll(backwards)
|
||||
|
||||
def paged_scroll(self, backwards):
|
||||
self.send_message('paged-scroll', backwards=backwards)
|
||||
|
||||
def run_drag_scroll(self, mouse_y, top, bottom):
|
||||
backwards = mouse_y <= top
|
||||
self.do_one_drag_scroll(backwards, top - mouse_y if backwards else mouse_y - bottom)
|
||||
|
||||
def do_one_drag_scroll(self, backwards, distance_from_boundary):
|
||||
window.clearTimeout(self.drag_scroll_timer)
|
||||
self.drag_scroll_timer = None
|
||||
if self.state not in (DRAGGING_RIGHT, DRAGGING_LEFT):
|
||||
return
|
||||
sd = get_session_data()
|
||||
interval = 1000/sd.get('lines_per_sec_smooth') if self.in_flow_mode else 1200
|
||||
self.drag_scroll_timer = window.setTimeout(self.do_one_drag_scroll.bind(None, backwards, distance_from_boundary), interval)
|
||||
now = window.performance.now()
|
||||
if now - self.last_drag_scroll_at > interval:
|
||||
self.send_drag_scroll_message(backwards, 'left' if self.state is DRAGGING_LEFT else 'right', True)
|
||||
self.last_drag_scroll_at = now
|
||||
|
||||
def send_drag_scroll_message(self, backwards, handle, extend_selection):
|
||||
self.send_message(
|
||||
'drag-scroll', backwards=backwards, handle=handle, extents=self.current_handle_position,
|
||||
extend_selection=extend_selection)
|
||||
|
||||
def end_drag_scroll(self):
|
||||
if self.drag_scroll_timer is not None:
|
||||
window.clearTimeout(self.drag_scroll_timer)
|
||||
self.drag_scroll_timer = None
|
||||
self.last_drag_scroll_at = -10000
|
||||
|
||||
def mouseup_on_container(self, ev):
|
||||
if self.state in (DRAGGING_RIGHT, DRAGGING_LEFT):
|
||||
self.state = WAITING_FOR_DRAG
|
||||
self.end_drag_scroll()
|
||||
ev.preventDefault(), ev.stopPropagation()
|
||||
|
||||
def touchend_on_container(self, ev):
|
||||
if self.state in (DRAGGING_RIGHT, DRAGGING_LEFT):
|
||||
ev.preventDefault(), ev.stopPropagation()
|
||||
for touch in ev.changedTouches:
|
||||
if touch.identifier is self.active_touch:
|
||||
self.active_touch = None
|
||||
self.state = WAITING_FOR_DRAG
|
||||
self.end_drag_scroll()
|
||||
return
|
||||
|
||||
def handle_moved(self, ev):
|
||||
handle = self.left_handle if self.state is DRAGGING_LEFT else self.right_handle
|
||||
s = handle.style
|
||||
s.left = (ev.clientX - self.position_in_handle.x) + 'px'
|
||||
s.top = (ev.clientY - self.position_in_handle.y) + 'px'
|
||||
pos = self.current_handle_position
|
||||
pos.start = map_to_iframe_coords(pos.start)
|
||||
pos.end = map_to_iframe_coords(pos.end)
|
||||
self.send_message('set-selection', extents=pos)
|
||||
c = self.container
|
||||
rect = c.getBoundingClientRect()
|
||||
t = document.getElementById('book-top-margin').offsetHeight
|
||||
top = rect.top + max(t, DRAG_SCROLL_ZONE_MIN_HEIGHT)
|
||||
t = document.getElementById('book-bottom-margin').offsetHeight
|
||||
bottom = rect.bottom - max(t, DRAG_SCROLL_ZONE_MIN_HEIGHT)
|
||||
if ev.clientY < top or ev.clientY > bottom:
|
||||
self.run_drag_scroll(ev.clientY, top, bottom)
|
||||
else:
|
||||
self.end_drag_scroll()
|
||||
|
||||
def mousemove_on_container(self, ev):
|
||||
if self.state not in (DRAGGING_RIGHT, DRAGGING_LEFT):
|
||||
return
|
||||
ev.stopPropagation(), ev.preventDefault()
|
||||
self.handle_moved(ev)
|
||||
|
||||
def touchmove_on_container(self, ev):
|
||||
if self.state not in (DRAGGING_RIGHT, DRAGGING_LEFT):
|
||||
return
|
||||
ev.stopPropagation(), ev.preventDefault()
|
||||
for touch in ev.changedTouches:
|
||||
if touch.identifier is self.active_touch:
|
||||
self.handle_moved(touch)
|
||||
return
|
||||
|
||||
@property
|
||||
def container(self):
|
||||
return document.getElementById(self.container_id)
|
||||
|
||||
@property
|
||||
def left_handle(self):
|
||||
return document.getElementById(self.left_handle_id)
|
||||
|
||||
@property
|
||||
def right_handle(self):
|
||||
return document.getElementById(self.right_handle_id)
|
||||
|
||||
@property
|
||||
def is_visible(self):
|
||||
return self.container.style.display is not 'none'
|
||||
|
||||
@property
|
||||
def current_handle_position(self):
|
||||
lh, rh = self.left_handle, self.right_handle
|
||||
lbr, rbr = self.left_handle.getBoundingClientRect(), self.right_handle.getBoundingClientRect()
|
||||
return {
|
||||
'start': {
|
||||
'onscreen': lh.style.display is not 'none',
|
||||
'x': Math.round(lbr.right), 'y': Math.round(lbr.bottom - self.left_line_height // 2)
|
||||
},
|
||||
'end': {
|
||||
'onscreen': rh.style.display is not 'none',
|
||||
'x': Math.round(rbr.left), 'y': Math.round(rbr.bottom - self.right_line_height // 2)
|
||||
}
|
||||
}
|
||||
|
||||
def show(self):
|
||||
self.middle.style.display = 'none'
|
||||
c = self.container
|
||||
c.style.display = 'flex'
|
||||
c.focus()
|
||||
self.update_handle_colors()
|
||||
self.view.selection_bar.hide()
|
||||
|
||||
def hide(self):
|
||||
if self.is_visible:
|
||||
self.container.style.display = 'none'
|
||||
self.view.focus_iframe()
|
||||
self.send_message('set-highlight-style', style=None)
|
||||
self.view.selection_bar.update_position()
|
||||
|
||||
def send_message(self, type, **kw):
|
||||
self.view.iframe_wrapper.send_message('annotations', type=type, **kw)
|
||||
|
||||
def initiate_create_annotation(self, start_in_notes_edit):
|
||||
self.send_message('create', start_in_notes_edit=v'!!start_in_notes_edit')
|
||||
|
||||
def edit_highlight(self, uuid):
|
||||
self.send_message('edit-highlight', uuid=uuid)
|
||||
self.show()
|
||||
|
||||
def remove_highlight(self, uuid):
|
||||
self.send_message('remove-highlight', uuid=uuid)
|
||||
self.annotations_manager.delete_highlight(uuid)
|
||||
self.hide()
|
||||
|
||||
def delete_highlight(self):
|
||||
uuid = self.editing_annot_uuid
|
||||
question_dialog(_('Are you sure?'), _('Are you sure you want to delete this highlight permanently?'),
|
||||
def (yes):
|
||||
if yes:
|
||||
self.remove_highlight(uuid) if uuid else self.hide()
|
||||
)
|
||||
|
||||
def handle_message(self, msg):
|
||||
if msg.type is 'create-annotation':
|
||||
self.editing_annot_uuid = msg.existing or None
|
||||
self.current_notes = ''
|
||||
if self.editing_annot_uuid:
|
||||
self.current_notes = self.annotations_manager.notes_for_highlight(self.editing_annot_uuid) or ''
|
||||
hs = self.annotations_manager.style_for_highlight(self.editing_annot_uuid)
|
||||
if hs:
|
||||
self.current_highlight_style = hs
|
||||
get_session_data().set('highlight_style', self.current_highlight_style)
|
||||
if not self.is_visible:
|
||||
self.view.hide_overlays()
|
||||
self.state = WAITING_FOR_CLICK
|
||||
self.show()
|
||||
self.hide_handles()
|
||||
if msg.extents and not msg.extents.start.is_empty:
|
||||
self.place_handles(msg.extents)
|
||||
self.in_flow_mode = msg.in_flow_mode
|
||||
self.send_message('set-highlight-style', style=self.current_highlight_style)
|
||||
if msg.start_in_notes_edit:
|
||||
self.edit_notes_and_colors()
|
||||
elif msg.type is 'position-handles':
|
||||
if self.state is WAITING_FOR_CLICK:
|
||||
self.place_handles(msg.extents)
|
||||
self.editing_annot_uuid = msg.existing or None
|
||||
if self.editing_annot_uuid:
|
||||
self.current_notes = self.annotations_manager.notes_for_highlight(self.editing_annot_uuid) or ''
|
||||
elif msg.type is 'scrolled':
|
||||
self.place_handles_after_scroll(msg.extents, msg.handle, msg.extended)
|
||||
elif msg.type is 'update-handles':
|
||||
self.place_handles(msg.extents)
|
||||
elif msg.type is 'highlight-applied':
|
||||
if not msg.ok:
|
||||
return error_dialog(
|
||||
_('Highlighting failed'),
|
||||
_('Failed to apply highlighting, try adjusting extent of highlight')
|
||||
)
|
||||
self.annotations_manager.add_highlight(msg, self.current_highlight_style, self.current_notes)
|
||||
else:
|
||||
print('Ignoring annotations message with unknown type:', msg.type)
|
||||
|
||||
def hide_handles(self):
|
||||
self.left_handle.style.display = 'none'
|
||||
self.right_handle.style.display = 'none'
|
||||
|
||||
def place_single_handle(self, handle, data):
|
||||
map_from_iframe_coords(data)
|
||||
s = handle.style
|
||||
s.display = 'block' if data.onscreen else 'none'
|
||||
height = data.height * 3
|
||||
width = data.height * 2
|
||||
s.width = f'{width}px'
|
||||
s.height = f'{height}px'
|
||||
bottom = data.y + data.height
|
||||
top = bottom - height
|
||||
s.top = f'{top}px'
|
||||
if handle.id is self.left_handle_id:
|
||||
s.left = (data.x - width) + 'px'
|
||||
else:
|
||||
s.left = data.x + 'px'
|
||||
|
||||
def place_handles(self, extents):
|
||||
self.place_single_handle(self.left_handle, extents.start)
|
||||
self.place_single_handle(self.right_handle, extents.end)
|
||||
self.state = WAITING_FOR_DRAG
|
||||
self.left_line_height = extents.start.height
|
||||
self.right_line_height = extents.end.height
|
||||
|
||||
def place_handles_after_scroll(self, extents, handle, extended):
|
||||
if extended:
|
||||
if handle is 'right':
|
||||
h = self.left_handle
|
||||
data = extents.start
|
||||
else:
|
||||
h = self.right_handle
|
||||
data = extents.end
|
||||
self.place_single_handle(h, data)
|
||||
else:
|
||||
self.place_single_handle(self.left_handle, extents.start)
|
||||
self.place_single_handle(self.right_handle, extents.end)
|
||||
# }}}
|
@ -316,17 +316,12 @@ class MainOverlay: # {{{
|
||||
actions_div.appendChild(E.ul(*full_screen_actions))
|
||||
|
||||
no_selection_bar = not sd.get('show_selection_bar')
|
||||
if no_selection_bar:
|
||||
highlight_action = ac(_('Highlight'), _('Highlight text in the book'),
|
||||
def(): self.overlay.hide(), self.overlay.view.initiate_create_annotation();, 'highlight')
|
||||
|
||||
if runtime.is_standalone_viewer:
|
||||
if no_selection_bar:
|
||||
actions_div.appendChild(E.ul(
|
||||
ac(_('Lookup/search word'), _('Lookup or search for the currently selected word'),
|
||||
def(): self.overlay.hide(), ui_operations.toggle_lookup(True);, 'library')
|
||||
))
|
||||
actions_div.lastChild.appendChild(highlight_action)
|
||||
actions_div.lastChild.appendChild(
|
||||
ac(_('Browse highlights'), _('Browse all highlights'),
|
||||
def(): self.overlay.hide(), ui_operations.toggle_highlights();, 'image')
|
||||
@ -355,8 +350,7 @@ class MainOverlay: # {{{
|
||||
def(): self.overlay.hide(), ui_operations.quit();, 'remove'),
|
||||
))
|
||||
else:
|
||||
if no_selection_bar:
|
||||
actions_div.appendChild(E.ul(highlight_action))
|
||||
pass
|
||||
container.appendChild(set_css(E.div(class_=MAIN_OVERLAY_TS_CLASS, # top section
|
||||
onclick=def (evt):evt.stopPropagation();,
|
||||
|
||||
|
@ -156,7 +156,7 @@ def all_actions():
|
||||
'copy': a('copy', _('Copy to clipboard'), 'copy_to_clipboard'),
|
||||
'lookup': a('library', _('Lookup/search selected word'), 'lookup'),
|
||||
'quick_highlight': a('highlight', _('Quick highlight in current color'), 'quick_highlight'),
|
||||
'highlight': a('highlight', _('Highlight selection in notes mode'), 'create_highlight'),
|
||||
'highlight': a('highlight', _('Highlight selection'), 'create_highlight'),
|
||||
'search_net': a('search', _('Search for selection on the net'), 'internet_search'),
|
||||
'remove_highlight': a('trash', _('Remove this highlight'), 'remove_highlight', True),
|
||||
'clear': a('close', _('Clear selection'), 'clear_selection'),
|
||||
@ -617,20 +617,27 @@ class SelectionBar:
|
||||
self.hide()
|
||||
|
||||
def create_highlight(self):
|
||||
self.view.initiate_create_annotation(True)
|
||||
pass # TODO: Implement this
|
||||
|
||||
def quick_highlight(self):
|
||||
cs = self.view.currently_showing.selection
|
||||
if cs.text:
|
||||
# TODO: Implement this
|
||||
if cs.annot_id:
|
||||
self.view.initiate_create_annotation(True)
|
||||
pass
|
||||
else:
|
||||
self.view.create_annotation.quick_create()
|
||||
pass
|
||||
|
||||
def remove_highlight(self):
|
||||
annot_id = self.view.currently_showing.selection.annot_id
|
||||
if annot_id:
|
||||
self.view.create_annotation.remove_highlight(annot_id)
|
||||
self.remove_highlight_with_id(annot_id)
|
||||
|
||||
def remove_highlight_with_id(self, annot_id):
|
||||
pass # TODO: Implement this
|
||||
|
||||
def edit_highlight(self, annot_id):
|
||||
pass # TODO: Implement this
|
||||
# }}}
|
||||
|
||||
# Interact with iframe {{{
|
||||
@ -638,4 +645,7 @@ class SelectionBar:
|
||||
def send_message(self, type, **kw):
|
||||
self.view.iframe_wrapper.send_message('annotations', type=type, **kw)
|
||||
|
||||
def handle_message(self, data):
|
||||
pass # TODO: Implement this
|
||||
|
||||
# }}}
|
||||
|
@ -328,12 +328,6 @@ def shortcuts_definition():
|
||||
_('Auto scroll slower'),
|
||||
),
|
||||
|
||||
'create_annotation': desc(
|
||||
"Ctrl+h",
|
||||
'ui',
|
||||
_('Create a highlight'),
|
||||
),
|
||||
|
||||
}
|
||||
return ans
|
||||
|
||||
@ -396,7 +390,7 @@ def add_standalone_viewer_shortcuts():
|
||||
)
|
||||
|
||||
sc['toggle_highlights'] = desc(
|
||||
"Ctrl+Alt+h",
|
||||
"Ctrl+h",
|
||||
'ui',
|
||||
_('Toggle the highlights panel')
|
||||
)
|
||||
|
@ -14,7 +14,6 @@ from iframe_comm import IframeWrapper
|
||||
from modals import error_dialog, warning_dialog
|
||||
from read_book.annotations import AnnotationsManager
|
||||
from read_book.content_popup import ContentPopupOverlay
|
||||
from read_book.create_annotation import CreateAnnotation
|
||||
from read_book.globals import (
|
||||
current_book, rtl_page_progression, runtime, set_current_spine_item,
|
||||
ui_operations
|
||||
@ -243,7 +242,6 @@ 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%; 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; overflow: hidden', id=CreateAnnotation.container_id, tabindex='0'), # create annotation overlay
|
||||
)
|
||||
),
|
||||
E.div(
|
||||
@ -312,7 +310,6 @@ class View:
|
||||
self.currently_showing = {'selection': {'empty': True}}
|
||||
self.book_scrollbar.apply_visibility()
|
||||
self.annotations_manager = AnnotationsManager(self)
|
||||
self.create_annotation = CreateAnnotation(self)
|
||||
|
||||
@property
|
||||
def iframe(self):
|
||||
@ -332,7 +329,7 @@ class View:
|
||||
self.set_scrollbar_visibility(not sd.get('book_scrollbar'))
|
||||
|
||||
def on_annotations_message(self, data):
|
||||
self.create_annotation.handle_message(data)
|
||||
self.selection_bar.handle_message(data)
|
||||
|
||||
def left_margin_clicked(self, event):
|
||||
if event.button is 0:
|
||||
@ -451,8 +448,6 @@ class View:
|
||||
ui_operations.toggle_bookmarks()
|
||||
elif data.name is 'toggle_highlights':
|
||||
ui_operations.toggle_highlights()
|
||||
elif data.name is 'create_annotation':
|
||||
self.initiate_create_annotation()
|
||||
elif data.name is 'new_bookmark':
|
||||
ui_operations.new_bookmark()
|
||||
elif data.name is 'toggle_inspector':
|
||||
@ -626,7 +621,6 @@ class View:
|
||||
self.search_overlay.hide()
|
||||
self.content_popup_overlay.hide()
|
||||
self.reference_mode_overlay.style.display = 'none'
|
||||
self.create_annotation.hide()
|
||||
self.focus_iframe()
|
||||
|
||||
def focus_iframe(self):
|
||||
@ -1249,26 +1243,18 @@ class View:
|
||||
else:
|
||||
self.show_name(sr.file_name, initial_position={'type':'search_result', 'search_result':sr, 'replace_history':True})
|
||||
|
||||
def initiate_create_annotation(self, start_in_notes_edit):
|
||||
self.create_annotation.initiate_create_annotation(start_in_notes_edit)
|
||||
|
||||
def highlight_action(self, uuid, which):
|
||||
if self.create_annotation.is_visible:
|
||||
return
|
||||
if which is 'create':
|
||||
self.initiate_create_annotation(False)
|
||||
return
|
||||
spine = self.book.manifest.spine
|
||||
spine_index = self.annotations_manager.spine_index_for_highlight(uuid, spine)
|
||||
if spine_index < 0 or spine_index >= spine.length:
|
||||
return
|
||||
if which is 'edit':
|
||||
if self.currently_showing.spine_index is spine_index:
|
||||
self.create_annotation.edit_highlight(uuid)
|
||||
self.selection_bar.edit_highlight(uuid)
|
||||
else:
|
||||
self.show_name(spine[spine_index], initial_position={'type':'edit_annotation', 'uuid': uuid, 'replace_history':True})
|
||||
elif which is 'delete':
|
||||
self.create_annotation.remove_highlight(uuid)
|
||||
self.selection_bar.remove_highlight_with_id(uuid)
|
||||
elif which is 'goto':
|
||||
cfi = self.annotations_manager.cfi_for_highlight(uuid, spine_index)
|
||||
if cfi:
|
||||
|
Loading…
x
Reference in New Issue
Block a user