diff --git a/imgsrc/srv/pencil.svg b/imgsrc/srv/pencil.svg
new file mode 100644
index 0000000000..5d78ebd07c
--- /dev/null
+++ b/imgsrc/srv/pencil.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/imgsrc/srv/plus.svg b/imgsrc/srv/plus.svg
new file mode 100644
index 0000000000..3f6a9da524
--- /dev/null
+++ b/imgsrc/srv/plus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/pyj/dom.pyj b/src/pyj/dom.pyj
index e90fd65668..3ca575aa8c 100644
--- a/src/pyj/dom.pyj
+++ b/src/pyj/dom.pyj
@@ -92,15 +92,16 @@ def element(elem_id, child_selector):
ans = ans.querySelector(child_selector)
return ans
-def unique_id():
- unique_id.count += 1
- return 'auto-id-' + unique_id.count
-unique_id.count = 0
+def unique_id(prefix):
+ prefix = prefix or 'auto-id'
+ unique_id.counts[prefix] = num = (unique_id.counts[prefix] or 0) + 1
+ return prefix + '-' + num
+unique_id.counts = {}
-def ensure_id(w):
+def ensure_id(w, prefix):
ans = w.getAttribute('id')
if not ans:
- ans = unique_id()
+ ans = unique_id(prefix)
w.setAttribute('id', ans)
return ans
diff --git a/src/pyj/read_book/globals.pyj b/src/pyj/read_book/globals.pyj
index d2a92835a0..442d902112 100644
--- a/src/pyj/read_book/globals.pyj
+++ b/src/pyj/read_book/globals.pyj
@@ -53,8 +53,8 @@ def set_current_spine_item(val):
default_color_schemes = {
- 'white':{'foreground':'black', 'background':'white', 'name':_('White')},
- 'black':{'foreground':'white', 'background':'black', 'name':_('Black')},
+ 'white':{'foreground':'#000000', 'background':'#ffffff', 'name':_('White')},
+ 'black':{'foreground':'#ffffff', 'background':'#000000', 'name':_('Black')},
'sepia-light':{'foreground':'#39322B', 'background':'#F6F3E9', 'name':_('Sepia Light')},
'sepia-dark': {'background':'#39322B', 'foreground':'#F6F3E9', 'name':_('Sepia Dark')},
}
diff --git a/src/pyj/read_book/overlay.pyj b/src/pyj/read_book/overlay.pyj
index 733072930f..78168f2245 100644
--- a/src/pyj/read_book/overlay.pyj
+++ b/src/pyj/read_book/overlay.pyj
@@ -9,7 +9,7 @@ from book_list.globals import get_boss
from widgets import create_spinner, create_button
from gettext import gettext as _
from read_book.toc import create_toc_panel
-from read_book.prefs import create_prefs_panel
+from read_book.prefs.main import create_prefs_panel
class LoadingMessage: # {{{
diff --git a/src/pyj/read_book/prefs/__init__.pyj b/src/pyj/read_book/prefs/__init__.pyj
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/pyj/read_book/prefs/colors.pyj b/src/pyj/read_book/prefs/colors.pyj
new file mode 100644
index 0000000000..4f0a07014f
--- /dev/null
+++ b/src/pyj/read_book/prefs/colors.pyj
@@ -0,0 +1,186 @@
+# vim:fileencoding=utf-8
+# License: GPL v3 Copyright: 2016, Kovid Goyal
+from __python__ import hash_literals, bound_methods
+
+from gettext import gettext as _
+from dom import svgicon, set_css, add_extra_css, build_rule, clear, unique_id
+from elementmaker import E
+from book_list.globals import get_session_data
+from read_book.globals import default_color_schemes
+from modals import error_dialog
+from widgets import create_button
+
+
+CONTAINER = unique_id('reader-color-scheme')
+COLOR_LIST = unique_id()
+ACTION_BUTTONS = unique_id()
+EDIT_SCHEME = unique_id()
+
+add_extra_css(def():
+ sel = '#' + COLOR_LIST
+ style = build_rule(sel, list_style_type='none', display='flex', flex_wrap='wrap')
+ sel += ' > li'
+ style += build_rule(sel, padding='1ex 1rem', margin='1ex 0.5rem', border_radius='4px', cursor='pointer', border='solid 1px currentColor')
+ style += build_rule(sel + ' svg', visibility='hidden')
+ sel += '.current-color'
+ style += build_rule(sel + ' svg', visibility='visible')
+ style += build_rule('#{} #{} td'.format(CONTAINER, EDIT_SCHEME), padding='1ex 1em')
+ sel = '#' + ACTION_BUTTONS
+ style += sel + '{margin-top:2ex; padding-top:1ex; border-top: solid 1px currentColor; margin-bottom: 2ex; padding-bottom: 1ex; border-bottom: solid 1px currentColor}'
+ style += build_rule(sel + ' > span ', margin='1ex 0.5rem', display='inline-block')
+ return style
+)
+
+def get_container():
+ return document.getElementById(CONTAINER)
+
+def change_current_color(ev):
+ ul = ev.currentTarget.parentNode
+ for li in ul.childNodes:
+ li.setAttribute('class', 'current-color' if li is ev.currentTarget else '')
+ set_action_button_visibility(ul.parentNode)
+
+def new_color_scheme(ev):
+ container = get_container()
+ container.lastChild.style.display = 'block'
+ for inp in container.lastChild.querySelectorAll('input'):
+ n = inp.getAttribute('name')
+ inp.value = {'name':'', 'bg':'#ffffff', 'fg':'#000000'}[n]
+ container.lastChild.querySelector('input').focus()
+ return container
+
+def edit_color_scheme(ev):
+ container = new_color_scheme(ev)
+ ccs = current_color_scheme(container)
+ all_schemes = all_color_schemes()
+ if all_schemes[ccs]:
+ container = document.getElementById(EDIT_SCHEME)
+ container.querySelector('input').value = all_schemes[ccs].name
+ container.querySelector('input[name=bg]').value = all_schemes[ccs].background
+ container.querySelector('input[name=fg]').value = all_schemes[ccs].foreground
+
+def remove_color_scheme(ev):
+ ccs = current_color_scheme()
+ if default_color_schemes[ccs]:
+ return error_dialog(_('Cannot remove'), _('Cannot remove a builtin color scheme'))
+ sd = get_session_data()
+ ucs = sd.get('user_color_schemes')
+ v'delete ucs[ccs]'
+ sd.set('user_color_schemes', ucs)
+ create_color_buttons()
+ set_current_color_scheme()
+
+
+def current_color_scheme():
+ return get_container().querySelector('li.current-color').getAttribute('data-name')
+
+def set_current_color_scheme(value):
+ ul = document.getElementById(COLOR_LIST)
+ done = False
+ for li in ul.childNodes:
+ li.classList.remove('current-color')
+ if li.getAttribute('data-name') is value:
+ li.classList.add('current-color')
+ done = True
+ if not done:
+ for li in ul.childNodes:
+ li.classList.add('current-color')
+ break
+ set_action_button_visibility()
+
+def add_color_scheme(ev):
+ div = document.getElementById(EDIT_SCHEME)
+ if this is not 'cancel':
+ name = div.querySelector('input[name=name]').value
+ if not name:
+ error_dialog(_('Name not specified'), _(
+ 'You must specify a name for the color scheme'))
+ return
+ bg = div.querySelector('input[name=bg]').value
+ fg = div.querySelector('input[name=fg]').value
+ for col in (bg, fg):
+ if not /^#[0-9A-F]{6}$/i.test(bg):
+ error_dialog(_('Invalid color'), _(
+ 'The color {} is not a valid color').format(col))
+ return
+ key = '*' + name
+ sd = get_session_data()
+ ucs = sd.get('user_color_schemes')
+ ucs[key] = {'name':name, 'foreground':fg, 'background':bg}
+ sd.set('user_color_schemes', ucs)
+ create_color_buttons()
+ set_current_color_scheme(key)
+ div.style.display = 'none'
+
+
+def all_color_schemes():
+ all_schemes = {}
+ for k in default_color_schemes:
+ all_schemes[k] = default_color_schemes[k]
+ sd = get_session_data()
+ ucs = sd.get('user_color_schemes')
+ for k in ucs:
+ all_schemes[k] = ucs[k]
+ return all_schemes
+
+
+def create_color_buttons():
+ ul = document.getElementById(COLOR_LIST)
+ sd = get_session_data()
+ clear(ul)
+ all_schemes = all_color_schemes()
+ ccs = sd.get('current_color_scheme')
+ for name in sorted(all_schemes, key=def(k):return all_schemes[k].name.toLowerCase();):
+ scheme = all_schemes[name]
+ item = set_css(E.li(svgicon('check'), '\xa0' + scheme.name, data_name=name, onclick=change_current_color,
+ class_='current-color' if name is ccs else ''),
+ color=scheme.foreground, background_color=scheme.background)
+ ul.appendChild(item)
+
+
+def set_action_button_visibility():
+ container = get_container()
+ ccs = current_color_scheme(container)
+ is_custom = ccs.startswith('*')
+ is_first = True
+ for button in container.querySelectorAll('#' + ACTION_BUTTONS + ' > span'):
+ if is_first:
+ is_first = False
+ else:
+ button.style.display = 'inline-block' if is_custom else 'none'
+
+def create_colors_panel(container):
+ container.appendChild(E.div(id=CONTAINER))
+ container = container.lastChild
+ container.appendChild(E.p(_('Choose a color scheme below'), style='margin:1ex 1em; padding: 1ex 0'))
+ ul = E.ul(id=COLOR_LIST)
+ container.appendChild(ul)
+ create_color_buttons()
+
+ container.appendChild(E.div(
+ E.span(create_button(_('New scheme'), 'plus', new_color_scheme)),
+ E.span(create_button(_('Edit scheme'), 'pencil', edit_color_scheme)),
+ E.span(create_button(_('Remove scheme'), 'trash', remove_color_scheme)),
+ id=ACTION_BUTTONS,
+ ))
+
+ container.appendChild(E.div(id=EDIT_SCHEME, style='display:none',
+ E.table(
+ E.tr(E.td(_('Name:')), E.td(E.input(name='name'))),
+ E.tr(E.td(_('Background:')), E.td(E.input(name='bg', type='color', value='#ffffff'))),
+ E.tr(E.td(_('Foreground:')), E.td(E.input(name='fg', type='color', value='#000000'))),
+ ),
+ E.div(style="display:flex; justify-content: flex-end; margin: 1ex 1em",
+ create_button(_('OK'), 'check', add_color_scheme), E.span('\xa0'), create_button(_('Cancel'), 'close', add_color_scheme.bind('cancel'))
+ )))
+ set_action_button_visibility()
+
+develop = create_colors_panel
+
+def commit_colors(onchange):
+ ccs = current_color_scheme()
+ sd = get_session_data()
+ if sd.get('current_color_scheme') is not ccs:
+ sd.set('current_color_scheme', ccs)
+ onchange()
+
diff --git a/src/pyj/read_book/prefs.pyj b/src/pyj/read_book/prefs/main.pyj
similarity index 53%
rename from src/pyj/read_book/prefs.pyj
rename to src/pyj/read_book/prefs/main.pyj
index 115f89a1ca..443c24d7b0 100644
--- a/src/pyj/read_book/prefs.pyj
+++ b/src/pyj/read_book/prefs/main.pyj
@@ -3,57 +3,11 @@
from __python__ import hash_literals, bound_methods
from gettext import gettext as _
-from dom import svgicon, ensure_id, clear, set_css, add_extra_css, build_rule
+from dom import svgicon, ensure_id, clear
from elementmaker import E
from book_list.item_list import build_list, create_item
-from book_list.globals import get_session_data
-from read_book.globals import default_color_schemes
+from read_book.prefs.colors import create_colors_panel, commit_colors
-# Colors {{{
-add_extra_css(def():
- sel = 'ul.color-preferences-list'
- style = build_rule(sel, list_style_type='none', display='flex', flex_wrap='wrap')
- sel += ' > li'
- style += build_rule(sel, padding='1ex 1rem', margin='1ex 1rem', border_radius='4px', cursor='pointer', border='solid 1px currentColor')
- style += build_rule(sel + ' svg', visibility='hidden')
- sel += '.current-color'
- style += build_rule(sel + ' svg', visibility='visible')
- return style
-)
-
-def change_current_color(ev):
- ul = ev.currentTarget.parentNode
- for li in ul.childNodes:
- li.setAttribute('class', 'current-color' if li is ev.currentTarget else '')
-
-def current_color_scheme(container):
- return container.querySelector('li.current-color').getAttribute('data-name')
-
-def create_colors_panel(container):
- container.appendChild(E.p(_('Choose a color scheme below'), style='margin:1ex 1em; padding: 1ex 0'))
- all_schemes = {}
- for k in default_color_schemes:
- all_schemes[k] = default_color_schemes[k]
- sd = get_session_data()
- ucs = sd.get('user_color_schemes')
- for k in ucs:
- all_schemes[k] = ucs[k]
- ul = E.ul(class_='color-preferences-list')
- container.appendChild(ul)
- for name in sorted(all_schemes, key=def(k):all_schemes[k].name;):
- scheme = all_schemes[name]
- item = set_css(E.li(svgicon('check'), '\xa0' + scheme.name, data_name=name, onclick=change_current_color,
- class_='current-color' if name is sd.get('current_color_scheme') else ''),
- color=scheme.foreground, background_color=scheme.background)
- ul.appendChild(item)
-
-def commit_colors(onchange, container):
- ccs = current_color_scheme(container)
- sd = get_session_data()
- if sd.get('current_color_scheme') is not ccs:
- sd.set('current_color_scheme', ccs)
- onchange()
-# }}}
class Prefs:
@@ -119,6 +73,3 @@ class Prefs:
def create_prefs_panel(container, close_func):
Prefs(container, close_func)
-
-def develop(container):
- create_colors_panel(container)