mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 10:44:09 -04:00
Implement header bar for footnote popup
This commit is contained in:
parent
c2f050ed2a
commit
f4a6f0117d
94
src/pyj/read_book/content_popup.pyj
Normal file
94
src/pyj/read_book/content_popup.pyj
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
from __python__ import bound_methods, hash_literals
|
||||||
|
|
||||||
|
from elementmaker import E
|
||||||
|
from gettext import gettext as _
|
||||||
|
|
||||||
|
from dom import add_extra_css, build_rule, clear, svgicon
|
||||||
|
|
||||||
|
CLASS_NAME = 'book-content-popup-container'
|
||||||
|
TOP_LEVEL_DISPLAY = 'flex'
|
||||||
|
|
||||||
|
add_extra_css(def():
|
||||||
|
sel = '.' + CLASS_NAME
|
||||||
|
style = ''
|
||||||
|
style += build_rule(sel, justify_content='center', align_items='center', height='100%')
|
||||||
|
|
||||||
|
sel += ' > div'
|
||||||
|
style += build_rule(sel, border_radius='8px', border='solid currentColor 2px', margin='1rem', padding='0.5rem', box_shadow='2px 2px 4px currentColor')
|
||||||
|
|
||||||
|
sel += ' > div'
|
||||||
|
style += build_rule(sel, padding_bottom='1ex', margin_bottom='1ex', border_bottom='solid currentColor 2px', display='flex', justify_content='space-between', align_items='center')
|
||||||
|
|
||||||
|
sel += ' > div' # button container
|
||||||
|
style += build_rule(sel, display='flex', justify_content='space-between', align_items='center')
|
||||||
|
|
||||||
|
sel += ' > a' # buttons
|
||||||
|
style += build_rule(sel, margin_left='1ex', cursor='pointer', display='inline-block')
|
||||||
|
style += build_rule(sel + ':hover', transform='scale(1.5)')
|
||||||
|
style += build_rule(sel + ':active', transform='scale(2)')
|
||||||
|
return style
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ContentPopupOverlay:
|
||||||
|
|
||||||
|
def __init__(self, view):
|
||||||
|
self.view = view
|
||||||
|
c = self.container
|
||||||
|
c.classList.add(CLASS_NAME)
|
||||||
|
|
||||||
|
c.appendChild(E.div(
|
||||||
|
E.div(),
|
||||||
|
E.iframe(seamless=True, sandbox='allow-scripts')
|
||||||
|
))
|
||||||
|
c.addEventListener('click', self.hide)
|
||||||
|
c.firstChild.addEventListener('click', def(ev):
|
||||||
|
ev.stopPropagation(), ev.preventDefault()
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def container(self):
|
||||||
|
return document.getElementById('book-content-popup-overlay')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_visible(self):
|
||||||
|
return self.container.style.display is not 'none'
|
||||||
|
|
||||||
|
def hide(self):
|
||||||
|
self.container.style.display = 'none'
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
c = self.container
|
||||||
|
c.style.display = TOP_LEVEL_DISPLAY
|
||||||
|
|
||||||
|
def apply_color_scheme(self, bg, fg):
|
||||||
|
c = self.container.firstChild
|
||||||
|
c.style.backgroundColor = bg
|
||||||
|
c.style.color = fg
|
||||||
|
|
||||||
|
def create_footnote_header(self, header):
|
||||||
|
clear(header)
|
||||||
|
header.appendChild(
|
||||||
|
E.h3(self.current_footnote_data.title or _('Footnote')),
|
||||||
|
)
|
||||||
|
bc = E.div(
|
||||||
|
E.a(svgicon('arrow-right'), title=_('Go to this footnote in the main view'), href='javascript:void(0)'),
|
||||||
|
E.a(svgicon('close'), title=_('Close the footnotes window'), href='javascript:void(0)')
|
||||||
|
)
|
||||||
|
bc.firstChild.addEventListener('click', def():
|
||||||
|
self.hide()
|
||||||
|
self.view.goto_named_destination(self.current_footnote_data.name, self.current_footnote_data.frag)
|
||||||
|
)
|
||||||
|
bc.lastChild.addEventListener('click', self.hide)
|
||||||
|
header.appendChild(bc)
|
||||||
|
|
||||||
|
def show_footnote(self, data):
|
||||||
|
self.current_footnote_data = data
|
||||||
|
width = 100 // data.cols_per_screen
|
||||||
|
c = self.container.firstChild
|
||||||
|
c.style.width = f'{width}vw'
|
||||||
|
header = c.firstChild
|
||||||
|
self.create_footnote_header(header)
|
||||||
|
# iframe = c.lastChild
|
@ -19,11 +19,12 @@ from read_book.globals import (
|
|||||||
)
|
)
|
||||||
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, handle_gesture as paged_handle_gesture,
|
anchor_funcs as paged_anchor_funcs, calc_columns_per_screen,
|
||||||
jump_to_cfi as paged_jump_to_cfi, layout as paged_layout,
|
handle_gesture as paged_handle_gesture, jump_to_cfi as paged_jump_to_cfi,
|
||||||
onkeydown as paged_onkeydown, onwheel as paged_onwheel, progress_frac,
|
layout as paged_layout, onkeydown as paged_onkeydown, onwheel as paged_onwheel,
|
||||||
reset_paged_mode_globals, scroll_by_page as paged_scroll_by_page, scroll_to_elem,
|
progress_frac, reset_paged_mode_globals, scroll_by_page as paged_scroll_by_page,
|
||||||
scroll_to_fraction as paged_scroll_to_fraction, snap_to_selection
|
scroll_to_elem, scroll_to_fraction as paged_scroll_to_fraction,
|
||||||
|
snap_to_selection
|
||||||
)
|
)
|
||||||
from read_book.resources import finalize_resources, unserialize_html
|
from read_book.resources import finalize_resources, unserialize_html
|
||||||
from read_book.settings import apply_settings, opts
|
from read_book.settings import apply_settings, opts
|
||||||
@ -391,7 +392,8 @@ class IframeBoss:
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
is_popup = False
|
is_popup = False
|
||||||
if is_popup:
|
if is_popup:
|
||||||
pass
|
self.send_message('show_footnote', name=name, frag=frag, title=evt.currentTarget.textContent, cols_per_screen=calc_columns_per_screen())
|
||||||
|
return
|
||||||
if name is current_spine_item().name:
|
if name is current_spine_item().name:
|
||||||
self.replace_history_on_next_cfi_update = False
|
self.replace_history_on_next_cfi_update = False
|
||||||
self.scroll_to_anchor(frag)
|
self.scroll_to_anchor(frag)
|
||||||
|
@ -114,10 +114,7 @@ def fit_images():
|
|||||||
set_elem_data(img, 'height-limited', True)
|
set_elem_data(img, 'height-limited', True)
|
||||||
|
|
||||||
|
|
||||||
def layout(is_single_page):
|
def calc_columns_per_screen():
|
||||||
nonlocal _in_paged_mode, col_width, col_and_gap, screen_height, gap, screen_width, is_full_screen_layout, cols_per_screen
|
|
||||||
body_style = window.getComputedStyle(document.body)
|
|
||||||
first_layout = not _in_paged_mode
|
|
||||||
cps = opts.columns_per_screen or {}
|
cps = opts.columns_per_screen or {}
|
||||||
cps = cps.landscape if scroll_viewport.width() > scroll_viewport.height() else cps.portrait
|
cps = cps.landscape if scroll_viewport.width() > scroll_viewport.height() else cps.portrait
|
||||||
try:
|
try:
|
||||||
@ -127,6 +124,14 @@ def layout(is_single_page):
|
|||||||
if not cps:
|
if not cps:
|
||||||
cps = int(Math.floor(scroll_viewport.width() / 500.0))
|
cps = int(Math.floor(scroll_viewport.width() / 500.0))
|
||||||
cps = max(1, min(cps or 1, 20))
|
cps = max(1, min(cps or 1, 20))
|
||||||
|
return cps
|
||||||
|
|
||||||
|
|
||||||
|
def layout(is_single_page):
|
||||||
|
nonlocal _in_paged_mode, col_width, col_and_gap, screen_height, gap, screen_width, is_full_screen_layout, cols_per_screen
|
||||||
|
body_style = window.getComputedStyle(document.body)
|
||||||
|
first_layout = not _in_paged_mode
|
||||||
|
cps = calc_columns_per_screen()
|
||||||
if first_layout:
|
if first_layout:
|
||||||
handle_rtl_body(body_style)
|
handle_rtl_body(body_style)
|
||||||
# Check if the current document is a full screen layout like
|
# Check if the current document is a full screen layout like
|
||||||
|
@ -2,27 +2,29 @@
|
|||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
from __python__ import bound_methods, hash_literals
|
from __python__ import bound_methods, hash_literals
|
||||||
|
|
||||||
from ajax import ajax_send
|
|
||||||
from dom import set_css, add_extra_css, build_rule, svgicon
|
|
||||||
from elementmaker import E
|
from elementmaker import E
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from utils import html_escape
|
|
||||||
from session import get_interface_data, get_device_uuid
|
|
||||||
|
|
||||||
from modals import error_dialog, warning_dialog
|
from ajax import ajax_send
|
||||||
from book_list.globals import get_session_data, main_js, get_translations
|
from book_list.globals import get_session_data, get_translations, main_js
|
||||||
from book_list.router import push_state, read_book_mode
|
from book_list.router import push_state, read_book_mode
|
||||||
from read_book.globals import messenger, iframe_id, current_book, set_current_spine_item
|
from book_list.theme import get_color, get_font_family
|
||||||
from read_book.resources import load_resources
|
from dom import add_extra_css, build_rule, set_css, svgicon
|
||||||
|
from modals import error_dialog, warning_dialog
|
||||||
|
from read_book.content_popup import ContentPopupOverlay
|
||||||
|
from read_book.globals import (
|
||||||
|
current_book, iframe_id, messenger, set_current_spine_item
|
||||||
|
)
|
||||||
|
from read_book.goto import get_next_section
|
||||||
from read_book.overlay import Overlay
|
from read_book.overlay import Overlay
|
||||||
from read_book.search import SearchOverlay, find_in_spine
|
|
||||||
from read_book.prefs.colors import resolve_color_scheme
|
from read_book.prefs.colors import resolve_color_scheme
|
||||||
from read_book.prefs.font_size import change_font_size_by
|
from read_book.prefs.font_size import change_font_size_by
|
||||||
from read_book.touch import set_left_margin_handler, set_right_margin_handler
|
from read_book.resources import load_resources
|
||||||
|
from read_book.search import SearchOverlay, find_in_spine
|
||||||
from read_book.toc import update_visible_toc_nodes
|
from read_book.toc import update_visible_toc_nodes
|
||||||
from read_book.goto import get_next_section
|
from read_book.touch import set_left_margin_handler, set_right_margin_handler
|
||||||
from book_list.theme import get_color, get_font_family
|
from session import get_device_uuid, get_interface_data
|
||||||
from utils import parse_url_params, username_key, is_ios
|
from utils import html_escape, is_ios, parse_url_params, username_key
|
||||||
|
|
||||||
LOADING_DOC = '''
|
LOADING_DOC = '''
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
@ -128,6 +130,7 @@ class View:
|
|||||||
),
|
),
|
||||||
right_margin,
|
right_margin,
|
||||||
E.div(style='position: absolute; top:0; left:0; width: 100%; pointer-events:none; display:none', id='book-search-overlay'), # search overlay
|
E.div(style='position: absolute; top:0; left:0; width: 100%; pointer-events:none; display:none', id='book-search-overlay'), # search overlay
|
||||||
|
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%; display:none', id='book-overlay'), # main overlay
|
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; 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', id='controls-help-overlay'), # controls help overlay
|
||||||
)
|
)
|
||||||
@ -135,6 +138,7 @@ class View:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.search_overlay = SearchOverlay(self)
|
self.search_overlay = SearchOverlay(self)
|
||||||
|
self.content_popup_overlay = ContentPopupOverlay(self)
|
||||||
self.overlay = Overlay(self)
|
self.overlay = Overlay(self)
|
||||||
self.processing_spine_item_display = False
|
self.processing_spine_item_display = False
|
||||||
self.iframe_ready = False
|
self.iframe_ready = False
|
||||||
@ -155,6 +159,7 @@ class View:
|
|||||||
'bump_font_size': self.bump_font_size,
|
'bump_font_size': self.bump_font_size,
|
||||||
'find_in_spine': self.on_find_in_spine,
|
'find_in_spine': self.on_find_in_spine,
|
||||||
'request_size': self.on_request_size,
|
'request_size': self.on_request_size,
|
||||||
|
'show_footnote': self.on_show_footnote,
|
||||||
'print': self.on_print,
|
'print': self.on_print,
|
||||||
}
|
}
|
||||||
self.currently_showing = {}
|
self.currently_showing = {}
|
||||||
@ -227,14 +232,25 @@ class View:
|
|||||||
delta = 2 if data.increase else -2
|
delta = 2 if data.increase else -2
|
||||||
change_font_size_by(delta)
|
change_font_size_by(delta)
|
||||||
|
|
||||||
|
def on_show_footnote(self, data):
|
||||||
|
self.show_content_popup()
|
||||||
|
self.content_popup_overlay.show_footnote(data)
|
||||||
|
|
||||||
def show_chrome(self):
|
def show_chrome(self):
|
||||||
self.search_overlay.hide()
|
self.search_overlay.hide()
|
||||||
|
self.content_popup_overlay.hide()
|
||||||
self.overlay.show()
|
self.overlay.show()
|
||||||
|
|
||||||
def show_search(self):
|
def show_search(self):
|
||||||
self.overlay.hide()
|
self.overlay.hide()
|
||||||
|
self.content_popup_overlay.hide()
|
||||||
self.search_overlay.show()
|
self.search_overlay.show()
|
||||||
|
|
||||||
|
def show_content_popup(self):
|
||||||
|
self.overlay.hide()
|
||||||
|
self.search_overlay.hide()
|
||||||
|
self.content_popup_overlay.show()
|
||||||
|
|
||||||
def set_margins(self):
|
def set_margins(self):
|
||||||
no_margins = self.currently_showing.name is self.book.manifest.title_page_name
|
no_margins = self.currently_showing.name is self.book.manifest.title_page_name
|
||||||
sd = get_session_data()
|
sd = get_session_data()
|
||||||
@ -342,6 +358,7 @@ class View:
|
|||||||
s.color = ans.foreground # Setting a color for the side margins causes the hover arrow to become visible
|
s.color = ans.foreground # Setting a color for the side margins causes the hover arrow to become visible
|
||||||
s.backgroundColor = ans.background
|
s.backgroundColor = ans.background
|
||||||
m.parentNode.style.backgroundColor = ans.background # this is needed on iOS where the bottom margin has its own margin, so we dont want the body background color to bleed through
|
m.parentNode.style.backgroundColor = ans.background # this is needed on iOS where the bottom margin has its own margin, so we dont want the body background color to bleed through
|
||||||
|
self.content_popup_overlay.apply_color_scheme(ans.background, ans.foreground)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
def on_resize(self):
|
def on_resize(self):
|
||||||
@ -376,6 +393,7 @@ class View:
|
|||||||
def display_book(self, book):
|
def display_book(self, book):
|
||||||
self.overlay.hide()
|
self.overlay.hide()
|
||||||
self.search_overlay.hide()
|
self.search_overlay.hide()
|
||||||
|
self.content_popup_overlay.hide()
|
||||||
self.book = current_book.book = book
|
self.book = current_book.book = book
|
||||||
self.ui.db.update_last_read_time(book)
|
self.ui.db.update_last_read_time(book)
|
||||||
self.loaded_resources = {}
|
self.loaded_resources = {}
|
||||||
@ -479,7 +497,7 @@ class View:
|
|||||||
|
|
||||||
def on_update_cfi(self, data):
|
def on_update_cfi(self, data):
|
||||||
overlay_shown = not self.processing_spine_item_display and self.overlay.is_visible
|
overlay_shown = not self.processing_spine_item_display and self.overlay.is_visible
|
||||||
if overlay_shown or self.search_overlay.is_visible:
|
if overlay_shown or self.search_overlay.is_visible or self.content_popup_overlay.is_visible:
|
||||||
# Chrome on Android stupidly resizes the viewport when the on
|
# Chrome on Android stupidly resizes the viewport when the on
|
||||||
# screen keyboard is displayed. This means that the push_state()
|
# screen keyboard is displayed. This means that the push_state()
|
||||||
# below causes the overlay to be closed, making it impossible to
|
# below causes the overlay to be closed, making it impossible to
|
||||||
|
Loading…
x
Reference in New Issue
Block a user