diff --git a/src/pyj/book_list/boss.pyj b/src/pyj/book_list/boss.pyj index 6f051a74a8..6b69120a7c 100644 --- a/src/pyj/book_list/boss.pyj +++ b/src/pyj/book_list/boss.pyj @@ -1,7 +1,7 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2015, Kovid Goyal -from book_list.ui import BookList +from book_list.ui import UI class Boss: @@ -10,7 +10,7 @@ class Boss: self.current_library_id = interface_data['default_library'] self.current_library_name = interface_data['library_map'][self.current_library_id] self.update_window_title() - self.book_list = BookList(interface_data) + self.ui = UI(interface_data) def update_window_title(self): document.title = 'calibre :: ' + self.current_library_name diff --git a/src/pyj/book_list/item_list.pyj b/src/pyj/book_list/item_list.pyj new file mode 100644 index 0000000000..f835f271f3 --- /dev/null +++ b/src/pyj/book_list/item_list.pyj @@ -0,0 +1,78 @@ +# vim:fileencoding=utf-8 +# License: GPL v3 Copyright: 2015, Kovid Goyal + +from dom import build_rule +from elementmaker import E + +from book_list.theme import get_font_size, get_color + +iv_counter = 0 + +class ItemsView: + + def __init__(self, interface_data): + nonlocal iv_counter + iv_counter += 1 + self.container_id = 'items-view-' + iv_counter + style = '' + cid = '#' + self.container_id + style += build_rule(cid + ' li', padding='1em', border_bottom='solid 1px ' + get_color('window-foreground'), border_top='solid 1px ' + get_color('window-background'), cursor='pointer', list_style='none') + style += build_rule(cid + ' .title', font_size=get_font_size('item-list-title')) + style += build_rule(cid + ' .subtitle', font_size=get_font_size('item-list-subtitle'), font_style='italic') + style += build_rule(cid + ' li:hover', color=get_color('bar-foreground'), background_color=get_color('bar-background'), border_top_color=get_color('bar-foreground')) + style += build_rule(cid + ' li:active', color=get_color('bar-highlight')) + self.base_style = style + div = E.div( + id=self.container_id, style='display:none', + E.style(style, type='text/css') + ) + document.body.appendChild(div) + + @property + def container(self): + return document.getElementById(self.container_id) + + @property + def is_visible(self): + self.container.style.display == 'block' + + @is_visible.setter + def is_visible(self, val): + self.container.style.display = 'block' if val else 'none' + + def clear(self): + c = self.container + while c.lastChild is not c.firstChild: + c.removeChild(c.lastChild) + return c + + def init(self, data): + items = getattr(data, 'items', data) + subtitle = getattr(data, 'subtitle', None) + c = self.clear() + if subtitle: + c.appendChild(E.p(subtitle)) + ul = E.ul() + c.appendChild(ul) + has_icons = has_subtitles = False + for item in items: + if item.icon_name: + has_icons = True + if item.subtitle: + has_subtitles = True + if has_icons and has_subtitles: + break + + for item in items: + ul.appendChild(E.li(E.a(href='javascript:void(0)', + E.div(item.title, class_='title') + ))) + a = ul.lastChild.firstChild + if item.subtitle: + a.appendChild(E.div(item.subtitle, class_='subtitle')) + if item.action: + a.addEventListener('click', def(event): event.preventDefault(), item.action();) + + +def create_item(title, action=None, subtitle=None, icon_name=None): + return {'title':title, 'action':action, 'subtitle':subtitle, 'icon_name':icon_name} diff --git a/src/pyj/book_list/theme.pyj b/src/pyj/book_list/theme.pyj index f342749b9d..dcdd18f57c 100644 --- a/src/pyj/book_list/theme.pyj +++ b/src/pyj/book_list/theme.pyj @@ -14,4 +14,6 @@ def get_color(name): def get_font_size(name): return { 'title': '1.4rem', + 'item-list-title': '1.2rem', + 'item-list-subtitle': '0.8 rem', }[name] diff --git a/src/pyj/book_list/top_bar.pyj b/src/pyj/book_list/top_bar.pyj index 02a5a67b4f..93fced4325 100644 --- a/src/pyj/book_list/top_bar.pyj +++ b/src/pyj/book_list/top_bar.pyj @@ -81,7 +81,7 @@ class TopBar: href="javascript:void(0)", title=tooltip, E.i(class_='fa fa-' + icon_name) )) - a = right.firstChild + a = right.lastChild if bar is self.bar: if action is not None: a.addEventListener('click', def(event): event.preventDefault(), action();) diff --git a/src/pyj/book_list/ui.pyj b/src/pyj/book_list/ui.pyj index 5b47460d99..4a4db343e7 100644 --- a/src/pyj/book_list/ui.pyj +++ b/src/pyj/book_list/ui.pyj @@ -1,9 +1,11 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2015, Kovid Goyal +from book_list.globals import get_boss from book_list.theme import get_color from book_list.top_bar import TopBar from book_list.views import BooksView +from book_list.item_list import ItemsView, create_item from dom import set_css from gettext import gettext as _ from utils import debounce @@ -17,21 +19,94 @@ class BarState: def add_button(self, **kw): self.buttons.push(kw) -class BookList: +class ClosePanelBar(BarState): + + def __init__(self, title, tooltip=''): + tooltip = tooltip or _('Close this panel') + BarState.__init__(self, title=title, tooltip=tooltip, action=close_panel, icon_name='times') + +class UIState: + + def __init__(self, top_bar_state=None, main_panel=None, panel_data=None): + self.top_bar_state = top_bar_state + self.main_panel = main_panel or get_boss().ui.items_view + self.panel_data = panel_data + + def add_button(self, **kw): + self.top_bar_state.add_button(**kw) + +panels = {} + +def panel(key): + ans = panels[key] + if not ans: + ans = panels[key] = create_panel[key]() + return ans + +def close_panel(): + get_boss().ui.close_panel() + +def replace_panel_action(replacement): + return def(): + get_boss().ui.replace_panel(panel(replacement)) + +def show_panel_action(key): + return def(): + get_boss().ui.show_panel(panel(key)) + +create_panel = { + 'more-actions-menu': def more_actions_menu(): + return UIState(ClosePanelBar(_('More actions')), panel_data=[ + create_item(_('Book List Mode'), replace_panel_action('booklist-mode-menu'), _('Change how the list of books is displayed')), + ]) + , + + 'booklist-mode-menu': def booklist_mode_menu(): + return UIState(ClosePanelBar(_('Book List Mode')), panel_data=[ + ]) + , +} + + +class UI: def __init__(self, interface_data): set_css(document.body, background_color=get_color('window-background')) - self.states = [] - self.initial_bar_state = ibs = BarState(run_animation=True) + self.states, self.panels = [], [] + self.top_bar = TopBar() + self.books_view = BooksView(interface_data) + self.items_view = ItemsView(interface_data) + ibs = BarState(run_animation=True) ibs.add_button(icon_name='sort-alpha-asc', tooltip=_('Sort books')) ibs.add_button(icon_name='search', tooltip=_('Search for books')) - ibs.add_button(icon_name='bars', tooltip=_('More actions')) - self.states.append(ibs) - self.top_bar = TopBar() - self.top_bar.apply_state(ibs.left_state, ibs.buttons) - self.books_view = BooksView(interface_data) + ibs.add_button(icon_name='ellipsis-v', tooltip=_('More actions'), action=show_panel_action('more-actions-menu')) + self.states.append(UIState(ibs, self.books_view)) + self.apply_state(self.states[0]) ibs.left_state.run_animation = False window.addEventListener('resize', debounce(bind(self.on_resize, self), 250)) + self.panels = v'[self.books_view, self.items_view]' def on_resize(self): self.books_view.on_resize() + + def apply_state(self, state): + self.top_bar.apply_state(state.top_bar_state.left_state, state.top_bar_state.buttons) + for panel in self.panels: + panel.is_visible = panel is state.main_panel + if callable(state.main_panel.init): + state.main_panel.init(state.panel_data) + + def close_panel(self): + if len(self.states) > 1: + self.states.pop() + self.apply_state(self.states[-1]) + + def replace_panel(self, state): + if len(self.states) > 1: + self.states.pop() + self.states.append(state) + self.apply_state(self.states[-1]) + + def show_panel(self, state): + self.states.append(state) + self.apply_state(self.states[-1])