diff --git a/src/calibre/srv/TODO b/src/calibre/srv/TODO index 6593076cc4..8044f3535e 100644 --- a/src/calibre/srv/TODO +++ b/src/calibre/srv/TODO @@ -1,5 +1,3 @@ -Add a couple more book list visualizations - Rewrite calibredb to connect to running server Prevent standalone and embedded servers from running simultaneously Prevent more than a single instance of the standalone server from running diff --git a/src/pyj/book_list/cover_grid.pyj b/src/pyj/book_list/cover_grid.pyj index 9aa3c0abe1..bff4878790 100644 --- a/src/pyj/book_list/cover_grid.pyj +++ b/src/pyj/book_list/cover_grid.pyj @@ -11,6 +11,7 @@ COVER_GRID_CLASS = 'book-list-cover-grid' THUMBNAIL_MAX_WIDTH = 300 THUMBNAIL_MAX_HEIGHT = 400 BORDER_RADIUS = 10 +DESCRIPTION = _('A grid of book covers') def cover_grid_css(): sel = '.' + COVER_GRID_CLASS diff --git a/src/pyj/book_list/details_list.pyj b/src/pyj/book_list/details_list.pyj new file mode 100644 index 0000000000..3d52a7a40c --- /dev/null +++ b/src/pyj/book_list/details_list.pyj @@ -0,0 +1,54 @@ +# vim:fileencoding=utf-8 +# License: GPL v3 Copyright: 2017, Kovid Goyal +from __python__ import bound_methods, hash_literals + +from dom import clear, set_css, build_rule +from elementmaker import E +from gettext import gettext as _ + +DETAILS_LIST_CLASS = 'book-list-details-list' +DESCRIPTION = _('A list with thumbnails and some book details') + +THUMBNAIL_MAX_WIDTH = 90 +THUMBNAIL_MAX_HEIGHT = 120 +BORDER_RADIUS = 5 + + +def details_list_css(): + ans = '' + sel = '.' + DETAILS_LIST_CLASS + ans += build_rule(sel + ' img', border_radius=BORDER_RADIUS+'px') + return ans + + +def init(container): + clear(container) + container.appendChild(E.div(class_=DETAILS_LIST_CLASS)) + + +def on_img_load(img, load_type): + div = img.parentNode + if not div: + return + if load_type is not 'load': + clear(div) + div.appendChild(E.div( + E.h2(img.dataset.title, style='text-align:center; font-size:larger; font-weight: bold'), + E.div(_('by'), style='text-align: center'), + E.h2(img.dataset.authors, style='text-align:center; font-size:larger; font-weight: bold') + )) + set_css(div, border='dashed 1px currentColor', border_radius=BORDER_RADIUS+'px') + + +def create_item(book_id, metadata, create_image, show_book_details): + authors = metadata.authors.join(' & ') + img = create_image(book_id, THUMBNAIL_MAX_WIDTH, THUMBNAIL_MAX_HEIGHT, on_img_load) + img.setAttribute('alt', _('{} by {}').format(metadata.title, authors)) + img.dataset.title, img.dataset.authors = metadata.title, authors + img_div = E.div(img) + ans = E.div(img_div, style=f'height:{THUMBNAIL_MAX_HEIGHT}px') + return ans + + +def append_item(container, item): + container.lastChild.appendChild(item) diff --git a/src/pyj/book_list/views.pyj b/src/pyj/book_list/views.pyj index 1317413dda..50bad35fa7 100644 --- a/src/pyj/book_list/views.pyj +++ b/src/pyj/book_list/views.pyj @@ -8,8 +8,13 @@ from gettext import gettext as _ from ajax import ajax_send from book_list.cover_grid import ( - append_item as cover_grid_append_item, cover_grid_css, - create_item as create_cover_grid_item, init as init_cover_grid + DESCRIPTION as COVER_GRID_DESCRIPTION, append_item as cover_grid_append_item, + cover_grid_css, create_item as create_cover_grid_item, init as init_cover_grid +) +from book_list.details_list import ( + DESCRIPTION as DETAILS_LIST_DESCRIPTION, append_item as details_list_append_item, + create_item as create_details_list_item, details_list_css, + init as init_details_list ) from book_list.globals import get_session_data from book_list.item_list import create_item, create_item_list @@ -31,13 +36,14 @@ from utils import conditional_timeout from widgets import create_button, create_spinner CLASS_NAME = 'book-list-container' -ALLOWED_MODES = {'cover_grid'} +ALLOWED_MODES = {'cover_grid', 'details_list'} DEFAULT_MODE = 'cover_grid' add_extra_css(def(): sel = '.' + CLASS_NAME + ' ' ans = build_rule(sel + '[data-component="top_message"]', margin='1ex 1em') - ans += cover_grid_css.call() + ans += cover_grid_css() + ans += details_list_css() return ans ) @@ -94,6 +100,10 @@ def apply_view_mode(mode=DEFAULT_MODE): book_list_data.render_book = create_cover_grid_item book_list_data.init_grid = init_cover_grid book_list_data.append_item = cover_grid_append_item + elif mode is 'details_list': + book_list_data.render_book = create_details_list_item + book_list_data.init_grid = init_details_list + book_list_data.append_item = details_list_append_item clear_grid() render_ids() @@ -319,13 +329,40 @@ def show_vl(vl_name, replace): # }}} +# Modes {{{ +def create_mode_panel(container_id): + if not library_data.sortable_fields: + show_panel('book_list', replace=True) + return + container = document.getElementById(container_id) + create_top_bar(container, title=_('Choose book list mode…'), action=back, icon='close') + items = [] + current_mode = get_session_data().get('view_mode') + + def ci(title, desc, name): + ic = 'check' if name is current_mode else None + items.push(create_item(title, subtitle=desc, icon=ic, action=def(): + if name is not current_mode: + get_session_data().set('view_mode', name) + back() + )) + + ci(_('Cover grid'), COVER_GRID_DESCRIPTION, 'cover_grid') + ci(_('Detailed list'), DETAILS_LIST_DESCRIPTION, 'details_list') + container.appendChild(E.div()) + create_item_list(container.lastChild, items, _('Choose a display mode for the list of books from below')) + +# }}} + # More actions {{{ def create_more_actions_panel(container_id): container = document.getElementById(container_id) create_top_bar(container, title=_('Sort books by…'), action=back, icon='close') items = [ - create_item(_('Book list mode'), subtitle=_('Change how the list of books is displayed')), + create_item(_('Book list mode'), subtitle=_('Change how the list of books is displayed'), action=def(): + show_panel('book_list^choose_mode', replace=True) + ), create_item(_('A random book'), subtitle=_('Choose a random book from the library'), action=def(): query = {'book_id':'0'} show_panel('book_details', query=query, replace=True) @@ -345,5 +382,6 @@ set_panel_handler('book_list', init) set_panel_handler('book_list^sort', create_sort_panel) set_panel_handler('book_list^vl', create_vl_panel) set_panel_handler('book_list^search', init_search_panel) +set_panel_handler('book_list^choose_mode', create_mode_panel) set_panel_handler('book_list^search^prefs', tb_config_panel_handler()) set_panel_handler('book_list^more_actions', create_more_actions_panel)