mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Initial positioning of selection handles works
This commit is contained in:
parent
cdb8e6025f
commit
18a7ad73a4
7
imgsrc/srv/selection-handle.svg
Normal file
7
imgsrc/srv/selection-handle.svg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<svg width="64" height="96" viewBox="0 0 16.933333 25.4" version="1.1">
|
||||||
|
<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"
|
||||||
|
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"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 426 B |
@ -2,6 +2,31 @@
|
|||||||
# 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 svgicon, ensure_id
|
||||||
|
|
||||||
|
WAITING_FOR_CLICK = 0
|
||||||
|
WAITING_FOR_DRAG = 1
|
||||||
|
DRAGGING_LEFT = 2
|
||||||
|
DRAGGING_RIGHT = 3
|
||||||
|
|
||||||
|
|
||||||
|
def selection_handle(invert):
|
||||||
|
ans = svgicon('selection-handle')
|
||||||
|
s = ans.style
|
||||||
|
if invert:
|
||||||
|
s.transform = 'scaleX(-1)'
|
||||||
|
s.position = 'absolute'
|
||||||
|
s.boxSizing = 'border-box'
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
class CreateAnnotation:
|
class CreateAnnotation:
|
||||||
|
|
||||||
@ -9,11 +34,28 @@ class CreateAnnotation:
|
|||||||
|
|
||||||
def __init__(self, view):
|
def __init__(self, view):
|
||||||
self.view = view
|
self.view = view
|
||||||
|
self.state = WAITING_FOR_CLICK
|
||||||
|
container = self.container
|
||||||
|
|
||||||
|
lh = selection_handle()
|
||||||
|
self.left_handle_id = ensure_id(lh, 'handle')
|
||||||
|
container.appendChild(lh)
|
||||||
|
rh = selection_handle(True)
|
||||||
|
self.right_handle_id = ensure_id(rh, 'handle')
|
||||||
|
container.appendChild(rh)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def container(self):
|
def container(self):
|
||||||
return document.getElementById(self.container_id)
|
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
|
@property
|
||||||
def is_visible(self):
|
def is_visible(self):
|
||||||
return self.container.style.display is not 'none'
|
return self.container.style.display is not 'none'
|
||||||
@ -29,6 +71,38 @@ class CreateAnnotation:
|
|||||||
|
|
||||||
def handle_message(self, msg):
|
def handle_message(self, msg):
|
||||||
if msg.type is 'create-annotation':
|
if msg.type is 'create-annotation':
|
||||||
|
if not self.is_visible:
|
||||||
|
self.view.hide_overlays()
|
||||||
|
self.state = WAITING_FOR_CLICK
|
||||||
self.show()
|
self.show()
|
||||||
|
self.hide_handles()
|
||||||
|
if msg.extents.start.x is not None:
|
||||||
|
self.place_handles(msg.extents)
|
||||||
else:
|
else:
|
||||||
print('Ignoring annotations message with unknown type:', msg.type)
|
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_handles(self, extents):
|
||||||
|
lh, rh = self.left_handle, self.right_handle
|
||||||
|
|
||||||
|
def do_it(handle, data):
|
||||||
|
map_from_iframe_coords(data)
|
||||||
|
s = handle.style
|
||||||
|
s.display = 'block'
|
||||||
|
height = data.height * 3
|
||||||
|
width = data.height * 2
|
||||||
|
s.width = f'{width}px'
|
||||||
|
s.height = f'{height}px'
|
||||||
|
bottom = min(max(0, data.y + data.height), window.innerHeight)
|
||||||
|
top = bottom - height
|
||||||
|
s.top = f'{top}px'
|
||||||
|
return s, width
|
||||||
|
|
||||||
|
style, width = do_it(lh, extents.start)
|
||||||
|
style.left = min(max(0, extents.start.x), window.innerWidth - width // 2) + 'px'
|
||||||
|
style, width = do_it(rh, extents.end)
|
||||||
|
style.left = (min(max(width // 2, extents.end.x), window.innerWidth) - width) + 'px'
|
||||||
|
self.state = WAITING_FOR_DRAG
|
||||||
|
@ -12,9 +12,9 @@ from read_book.extract import get_elements
|
|||||||
from read_book.find import reset_find_caches, select_search_result
|
from read_book.find import reset_find_caches, select_search_result
|
||||||
from read_book.flow_mode import (
|
from read_book.flow_mode import (
|
||||||
anchor_funcs as flow_anchor_funcs, auto_scroll_action as flow_auto_scroll_action,
|
anchor_funcs as flow_anchor_funcs, auto_scroll_action as flow_auto_scroll_action,
|
||||||
flow_onwheel, flow_to_scroll_fraction, handle_gesture as flow_handle_gesture,
|
ensure_selection_visible, flow_onwheel, flow_to_scroll_fraction,
|
||||||
handle_shortcut as flow_handle_shortcut, layout as flow_layout,
|
handle_gesture as flow_handle_gesture, handle_shortcut as flow_handle_shortcut,
|
||||||
scroll_by_page as flow_scroll_by_page, ensure_selection_visible
|
layout as flow_layout, scroll_by_page as flow_scroll_by_page
|
||||||
)
|
)
|
||||||
from read_book.footnotes import is_footnote_link
|
from read_book.footnotes import is_footnote_link
|
||||||
from read_book.globals import (
|
from read_book.globals import (
|
||||||
@ -50,7 +50,7 @@ from read_book.touch import (
|
|||||||
create_handlers as create_touch_handlers, reset_handlers as reset_touch_handlers
|
create_handlers as create_touch_handlers, reset_handlers as reset_touch_handlers
|
||||||
)
|
)
|
||||||
from read_book.viewport import scroll_viewport
|
from read_book.viewport import scroll_viewport
|
||||||
from utils import debounce, html_escape, is_ios
|
from utils import debounce, html_escape, is_ios, selection_extents
|
||||||
|
|
||||||
FORCE_FLOW_MODE = False
|
FORCE_FLOW_MODE = False
|
||||||
CALIBRE_VERSION = '__CALIBRE_VERSION__'
|
CALIBRE_VERSION = '__CALIBRE_VERSION__'
|
||||||
@ -495,7 +495,7 @@ class IframeBoss:
|
|||||||
if self.handle_navigation_shortcut(sc_name, evt):
|
if self.handle_navigation_shortcut(sc_name, evt):
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
elif sc_name is 'create_annotation':
|
elif sc_name is 'create_annotation':
|
||||||
self.send_message('annotations', type='create-annotation')
|
self.initiate_creation_of_annotation()
|
||||||
else:
|
else:
|
||||||
self.send_message('handle_shortcut', name=sc_name)
|
self.send_message('handle_shortcut', name=sc_name)
|
||||||
|
|
||||||
@ -600,6 +600,14 @@ class IframeBoss:
|
|||||||
else:
|
else:
|
||||||
end_reference_mode()
|
end_reference_mode()
|
||||||
|
|
||||||
|
def initiate_creation_of_annotation(self):
|
||||||
|
self.send_message(
|
||||||
|
'annotations',
|
||||||
|
type='create-annotation',
|
||||||
|
in_flow_mode=current_layout_mode() is 'flow',
|
||||||
|
extents=selection_extents(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
main.boss = IframeBoss()
|
main.boss = IframeBoss()
|
||||||
|
@ -560,6 +560,7 @@ class View:
|
|||||||
self.search_overlay.hide()
|
self.search_overlay.hide()
|
||||||
self.content_popup_overlay.hide()
|
self.content_popup_overlay.hide()
|
||||||
self.reference_mode_overlay.style.display = 'none'
|
self.reference_mode_overlay.style.display = 'none'
|
||||||
|
self.create_annotation.hide()
|
||||||
self.focus_iframe()
|
self.focus_iframe()
|
||||||
|
|
||||||
def focus_iframe(self):
|
def focus_iframe(self):
|
||||||
|
@ -252,6 +252,30 @@ def sandboxed_html(html, style, sandbox):
|
|||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
def selection_extents():
|
||||||
|
ans = {'start': {'x': None, 'y': None, 'height': None}, 'end': {'x': None, 'y': None, 'height': None}}
|
||||||
|
sel = window.getSelection()
|
||||||
|
if not sel or not sel.rangeCount:
|
||||||
|
return ans
|
||||||
|
start = sel.getRangeAt(0).cloneRange()
|
||||||
|
end = sel.getRangeAt(sel.rangeCount - 1).cloneRange()
|
||||||
|
start.collapse(True)
|
||||||
|
end.collapse(False)
|
||||||
|
|
||||||
|
def for_boundary(r, ans):
|
||||||
|
rects = r.getClientRects()
|
||||||
|
if not rects.length:
|
||||||
|
return
|
||||||
|
rect = rects[0]
|
||||||
|
ans.x = rect.left
|
||||||
|
ans.y = rect.top
|
||||||
|
ans.height = rect.bottom - rect.top
|
||||||
|
|
||||||
|
for_boundary(start, ans.start)
|
||||||
|
for_boundary(end, ans.end)
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
if __name__ is '__main__':
|
if __name__ is '__main__':
|
||||||
from pythonize import strings
|
from pythonize import strings
|
||||||
strings()
|
strings()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user