Click to position handles implemented

This commit is contained in:
Kovid Goyal 2020-03-20 15:41:23 +05:30
parent 97da8da4c9
commit 4defd82bed
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 84 additions and 29 deletions

View File

@ -4,10 +4,10 @@ from __python__ import bound_methods, hash_literals
from dom import svgicon, ensure_id from dom import svgicon, ensure_id
WAITING_FOR_CLICK = 0 WAITING_FOR_CLICK = 1
WAITING_FOR_DRAG = 1 WAITING_FOR_DRAG = 2
DRAGGING_LEFT = 2 DRAGGING_LEFT = 3
DRAGGING_RIGHT = 3 DRAGGING_RIGHT = 4
def selection_handle(invert): def selection_handle(invert):
@ -17,6 +17,7 @@ def selection_handle(invert):
s.transform = 'scaleX(-1)' s.transform = 'scaleX(-1)'
s.position = 'absolute' s.position = 'absolute'
s.boxSizing = 'border-box' s.boxSizing = 'border-box'
s.touchAction = 'none'
return ans return ans
@ -28,6 +29,14 @@ def map_from_iframe_coords(point):
return point 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
class CreateAnnotation: class CreateAnnotation:
container_id = 'create-annotation-overlay' container_id = 'create-annotation-overlay'
@ -44,6 +53,14 @@ class CreateAnnotation:
self.right_handle_id = ensure_id(rh, 'handle') self.right_handle_id = ensure_id(rh, 'handle')
container.appendChild(rh) container.appendChild(rh)
container.addEventListener('click', self.container_clicked)
def container_clicked(self, ev):
ev.stopPropagation(), ev.preventDefault()
if self.state is WAITING_FOR_CLICK:
pt = map_to_iframe_coords({'x': ev.clientX, 'y': ev.clientY})
self.send_message(type='position-handles-at-point', x=pt.x, y=pt.y)
@property @property
def container(self): def container(self):
return document.getElementById(self.container_id) return document.getElementById(self.container_id)
@ -78,6 +95,9 @@ class CreateAnnotation:
self.hide_handles() self.hide_handles()
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)
elif msg.type is 'position-handles':
if self.state is WAITING_FOR_CLICK:
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)

View File

@ -4,6 +4,7 @@ from __python__ import bound_methods, hash_literals
import traceback import traceback
from gettext import gettext as _ from gettext import gettext as _
from select import selection_extents, selection_extents_at_point
from fs_images import fix_fullscreen_svg_images from fs_images import fix_fullscreen_svg_images
from iframe_comm import IframeClient from iframe_comm import IframeClient
@ -50,7 +51,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, selection_extents from utils import debounce, html_escape, is_ios
FORCE_FLOW_MODE = False FORCE_FLOW_MODE = False
CALIBRE_VERSION = '__CALIBRE_VERSION__' CALIBRE_VERSION = '__CALIBRE_VERSION__'
@ -120,6 +121,7 @@ class IframeBoss:
'overlay_visibility_changed': self.on_overlay_visibility_changed, 'overlay_visibility_changed': self.on_overlay_visibility_changed,
'show_search_result': self.show_search_result, 'show_search_result': self.show_search_result,
'handle_navigation_shortcut': self.on_handle_navigation_shortcut, 'handle_navigation_shortcut': self.on_handle_navigation_shortcut,
'annotations': self.annotations_msg_received,
} }
self.comm = IframeClient(handlers) self.comm = IframeClient(handlers)
self.last_window_ypos = 0 self.last_window_ypos = 0
@ -608,6 +610,15 @@ class IframeBoss:
extents=selection_extents(), extents=selection_extents(),
) )
def annotations_msg_received(self, data):
if data.type is 'position-handles-at-point':
self.send_message(
'annotations',
type='position-handles',
extents=selection_extents_at_point(data.x, data.y))
else:
console.log('Ignoring annotations message to iframe with unknown type: ' + data.type)
def main(): def main():
main.boss = IframeBoss() main.boss = IframeBoss()

View File

@ -42,3 +42,51 @@ def word_at_point(x, y):
r.setStart(r.startContainer, word_info.start) r.setStart(r.startContainer, word_info.start)
r.setEnd(r.startContainer, word_info.end) r.setEnd(r.startContainer, word_info.end)
return r return r
def range_extents(start, end):
ans = {'start': {'x': None, 'y': None, 'height': None}, 'end': {'x': None, 'y': None, 'height': None}}
if not start or not end:
return ans
start = start.cloneRange()
end = end.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
def selection_extents():
sel = window.getSelection()
if not sel or not sel.rangeCount:
return range_extents()
start = sel.getRangeAt(0)
end = sel.getRangeAt(sel.rangeCount - 1)
return range_extents(start, end)
def selection_extents_at_point(x, y):
r = word_at_point(x, y)
if r:
sel = window.getSelection()
sel.removeAllRanges()
sel.addRange(r)
return selection_extents(r, r)
ans = range_extents()
ans.start.y = ans.end.y = y
ans.start.height = ans.end.height = parseInt(window.getComputedStyle(document.body).fontSize) + 4
ans.start.x = x
ans.end.x = x + ans.start.height * 3
return ans

View File

@ -252,30 +252,6 @@ 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()