diff --git a/src/pyj/read_book/content_popup.pyj b/src/pyj/read_book/content_popup.pyj new file mode 100644 index 0000000000..e06f4ad14b --- /dev/null +++ b/src/pyj/read_book/content_popup.pyj @@ -0,0 +1,94 @@ +# vim:fileencoding=utf-8 +# License: GPL v3 Copyright: 2017, Kovid Goyal +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 diff --git a/src/pyj/read_book/iframe.pyj b/src/pyj/read_book/iframe.pyj index de49bed260..6104f8deb8 100644 --- a/src/pyj/read_book/iframe.pyj +++ b/src/pyj/read_book/iframe.pyj @@ -19,11 +19,12 @@ from read_book.globals import ( ) from read_book.mathjax import apply_mathjax from read_book.paged_mode import ( - anchor_funcs as paged_anchor_funcs, handle_gesture as paged_handle_gesture, - jump_to_cfi as paged_jump_to_cfi, layout as paged_layout, - onkeydown as paged_onkeydown, onwheel as paged_onwheel, progress_frac, - reset_paged_mode_globals, scroll_by_page as paged_scroll_by_page, scroll_to_elem, - scroll_to_fraction as paged_scroll_to_fraction, snap_to_selection + anchor_funcs as paged_anchor_funcs, calc_columns_per_screen, + handle_gesture as paged_handle_gesture, jump_to_cfi as paged_jump_to_cfi, + layout as paged_layout, onkeydown as paged_onkeydown, onwheel as paged_onwheel, + progress_frac, reset_paged_mode_globals, scroll_by_page as paged_scroll_by_page, + 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.settings import apply_settings, opts @@ -391,7 +392,8 @@ class IframeBoss: traceback.print_exc() is_popup = False 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: self.replace_history_on_next_cfi_update = False self.scroll_to_anchor(frag) diff --git a/src/pyj/read_book/paged_mode.pyj b/src/pyj/read_book/paged_mode.pyj index 50307d2b97..357ea383e0 100644 --- a/src/pyj/read_book/paged_mode.pyj +++ b/src/pyj/read_book/paged_mode.pyj @@ -114,10 +114,7 @@ def fit_images(): set_elem_data(img, 'height-limited', True) -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 +def calc_columns_per_screen(): cps = opts.columns_per_screen or {} cps = cps.landscape if scroll_viewport.width() > scroll_viewport.height() else cps.portrait try: @@ -127,6 +124,14 @@ def layout(is_single_page): if not cps: cps = int(Math.floor(scroll_viewport.width() / 500.0)) 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: handle_rtl_body(body_style) # Check if the current document is a full screen layout like diff --git a/src/pyj/read_book/view.pyj b/src/pyj/read_book/view.pyj index e222893bb4..f96f30a176 100644 --- a/src/pyj/read_book/view.pyj +++ b/src/pyj/read_book/view.pyj @@ -2,27 +2,29 @@ # License: GPL v3 Copyright: 2016, Kovid Goyal 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 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 book_list.globals import get_session_data, main_js, get_translations +from ajax import ajax_send +from book_list.globals import get_session_data, get_translations, main_js 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 read_book.resources import load_resources +from book_list.theme import get_color, get_font_family +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.search import SearchOverlay, find_in_spine from read_book.prefs.colors import resolve_color_scheme 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.goto import get_next_section -from book_list.theme import get_color, get_font_family -from utils import parse_url_params, username_key, is_ios +from read_book.touch import set_left_margin_handler, set_right_margin_handler +from session import get_device_uuid, get_interface_data +from utils import html_escape, is_ios, parse_url_params, username_key LOADING_DOC = ''' @@ -128,6 +130,7 @@ class View: ), 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%; 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='controls-help-overlay'), # controls help overlay ) @@ -135,6 +138,7 @@ class View: ) ) self.search_overlay = SearchOverlay(self) + self.content_popup_overlay = ContentPopupOverlay(self) self.overlay = Overlay(self) self.processing_spine_item_display = False self.iframe_ready = False @@ -155,6 +159,7 @@ class View: 'bump_font_size': self.bump_font_size, 'find_in_spine': self.on_find_in_spine, 'request_size': self.on_request_size, + 'show_footnote': self.on_show_footnote, 'print': self.on_print, } self.currently_showing = {} @@ -227,14 +232,25 @@ class View: delta = 2 if data.increase else -2 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): self.search_overlay.hide() + self.content_popup_overlay.hide() self.overlay.show() def show_search(self): self.overlay.hide() + self.content_popup_overlay.hide() 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): no_margins = self.currently_showing.name is self.book.manifest.title_page_name 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.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 + self.content_popup_overlay.apply_color_scheme(ans.background, ans.foreground) return ans def on_resize(self): @@ -376,6 +393,7 @@ class View: def display_book(self, book): self.overlay.hide() self.search_overlay.hide() + self.content_popup_overlay.hide() self.book = current_book.book = book self.ui.db.update_last_read_time(book) self.loaded_resources = {} @@ -479,7 +497,7 @@ class View: def on_update_cfi(self, data): 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 # screen keyboard is displayed. This means that the push_state() # below causes the overlay to be closed, making it impossible to