Skip to content

Commit

Permalink
Load custom themes from config dir
Browse files Browse the repository at this point in the history
  • Loading branch information
johnfactotum committed Oct 9, 2023
1 parent 617e66c commit ce18087
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 116 deletions.
98 changes: 1 addition & 97 deletions src/book-viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { SelectionPopover } from './selection-tools.js'
import { ImageViewer } from './image-viewer.js'
import { formatAuthors, makeBookInfoWindow } from './book-info.js'
import { getURIStore, getBookList } from './library.js'
import { themes, invertTheme } from './themes.js'

// for use in the WebView
const uiText = {
Expand All @@ -31,103 +32,6 @@ const uiText = {
goToFootnote: _('Go to Footnote'),
}

const themes = [
{
name: 'default', label: _('Default'),
light: { fg: '#000000', bg: '#ffffff', link: '#0066cc' },
dark: { fg: '#e0e0e0', bg: '#222222', link: '#88ccee' },
},
{
name: 'gray', label: _('Gray'),
light: { fg: '#222222', bg: '#e0e0e0', link: '#4488cc' },
dark: { fg: '#c6c6c6', bg: '#444444', link: '#88ccee' },
},
{
name: 'sepia', label: _('Sepia'),
light: { fg: '#5b4636', bg: '#f1e8d0', link: '#008b8b' },
dark: { fg: '#ffd595', bg: '#342e25', link: '#48d1cc' },
},
{
name: 'grass', label: _('Grass'),
light: { fg: '#232c16', bg: '#d7dbbd', link: '#177b4d' },
dark: { fg: '#d8deba', bg: '#333627', link: '#a6d608' },
},
{
name: 'cherry', label: _('Cherry'),
light: { fg: '#4e1609', bg: '#f0d1d5', link: '#de3838' },
dark: { fg: '#e5c4c8', bg: '#462f32', link: '#ff646e' },
},
{
name: 'sky', label: _('Sky'),
light: { fg: '#262d48', bg: '#cedef5', link: '#2d53e5' },
dark: { fg: '#babee1', bg: '#282e47', link: '#ff646e' },
},
{
name: 'solarized', label: _('Solarized'),
light: { fg: '#586e75', bg: '#fdf6e3', link: '#268bd2' },
dark: { fg: '#93a1a1', bg: '#002b36', link: '#268bd2' },
},
{
name: 'gruvbox', label: _('Gruvbox'),
light: { fg: '#3c3836', bg: '#fbf1c7', link: '#076678' },
dark: { fg: '#ebdbb2', bg: '#282828', link: '#83a598' },
},
{
name: 'nord', label: _('Nord'),
light: { fg: '#2e3440', bg: '#eceff4', link: '#5e81ac' },
dark: { fg: '#d8dee9', bg: '#2e3440', link: '#88c0d0' },
},
]

const themeCssProvider = new Gtk.CssProvider()
themeCssProvider.load_from_data(`
.theme-container .card {
padding: 9px;
}
` + themes.map(theme => {
const id = `theme-${GLib.uuid_string_random()}`
theme.id = id
// NOTE: .92 matches Libadwaita's sidebar color when the bg is white
// in dark mode it should use 1.41 to match Libadwaita
// but it seems better to tone it down as many dark themes are quite bright
return `
.${id} {
color: ${theme.light.fg};
background: ${theme.light.bg};
}
.is-dark .${id} {
color: ${theme.dark.fg};
background: ${theme.dark.bg};
}
.sidebar-${id}:not(.background) {
color: ${theme.light.fg};
background: shade(${theme.light.bg}, .92);
}
.is-dark .sidebar-${id}:not(.background) {
color: ${theme.dark.fg};
background: shade(${theme.dark.bg}, 1.2);
}
.${id} highlight {
background: ${theme.light.link};
}
.is-dark .${id} highlight {
background: ${theme.dark.link};
}
.${id} popover highlight, .is-dark .${id} popover highlight {
background: @accent_bg_color;
}
`
}).join(''), -1)
Gtk.StyleContext.add_provider_for_display(
Gdk.Display.get_default(),
themeCssProvider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)

const invertTheme = ({ light, dark }) => ({ light, dark, inverted: {
fg: utils.invertColor(dark.fg),
link: utils.invertColor(dark.link),
} })

const userStylesheet = utils.readFile(Gio.File.new_for_path(
pkg.configpath('user-stylesheet.css')))

Expand Down
28 changes: 9 additions & 19 deletions src/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,14 @@ import { formatAuthors, makeBookInfoWindow } from './book-info.js'

const showCovers = utils.settings('library')?.get_boolean('show-covers') ?? true

const listDir = function* (path) {
const dir = Gio.File.new_for_path(path)
if (!GLib.file_test(path, GLib.FileTest.IS_DIR)) return null
const children = dir.enumerate_children('standard::name,time::modified',
Gio.FileQueryInfoFlags.NONE, null)
let info
while ((info = children.next_file(null)) != null) {
try {
const name = info.get_name()
if (!/\.json$/.test(name)) continue
const child = dir.get_child(name)
yield {
file: child,
modified: new Date(info.get_attribute_uint64('time::modified') * 1000),
}
} catch (e) {
continue
}
const listBooks = function* (path) {
const ls = utils.listDir(path, 'standard::name,time::modified')
for (const { file, name, info } of ls) try {
if (!/\.json$/.test(name)) continue
const modified = new Date(info.get_attribute_uint64('time::modified') * 1000)
yield { file, modified }
} catch (e) {
console.error(e)
}
}

Expand All @@ -57,7 +47,7 @@ const BookList = GObject.registerClass({
GTypeName: 'FoliateBookList',
}, class extends Gio.ListStore {
#uriStore = getURIStore()
#files = Array.from(listDir(pkg.datadir) ?? [])
#files = Array.from(listBooks(pkg.datadir) ?? [])
.sort((a, b) => b.modified - a.modified)
.map(x => x.file)
#iter = this.#files.values()
Expand Down
124 changes: 124 additions & 0 deletions src/themes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import Gtk from 'gi://Gtk'
import GLib from 'gi://GLib'
import Gdk from 'gi://Gdk'
import { gettext as _ } from 'gettext'
import * as utils from './utils.js'

export const themes = [
{
name: 'default', label: _('Default'),
light: { fg: '#000000', bg: '#ffffff', link: '#0066cc' },
dark: { fg: '#e0e0e0', bg: '#222222', link: '#77bbee' },
},
{
name: 'gray', label: _('Gray'),
light: { fg: '#222222', bg: '#e0e0e0', link: '#4488cc' },
dark: { fg: '#c6c6c6', bg: '#444444', link: '#88ccee' },
},
{
name: 'sepia', label: _('Sepia'),
light: { fg: '#5b4636', bg: '#f1e8d0', link: '#008b8b' },
dark: { fg: '#ffd595', bg: '#342e25', link: '#48d1cc' },
},
{
name: 'grass', label: _('Grass'),
light: { fg: '#232c16', bg: '#d7dbbd', link: '#177b4d' },
dark: { fg: '#d8deba', bg: '#333627', link: '#a6d608' },
},
{
name: 'cherry', label: _('Cherry'),
light: { fg: '#4e1609', bg: '#f0d1d5', link: '#de3838' },
dark: { fg: '#e5c4c8', bg: '#462f32', link: '#ff646e' },
},
{
name: 'sky', label: _('Sky'),
light: { fg: '#262d48', bg: '#cedef5', link: '#2d53e5' },
dark: { fg: '#babee1', bg: '#282e47', link: '#ff646e' },
},
{
name: 'solarized', label: _('Solarized'),
light: { fg: '#586e75', bg: '#fdf6e3', link: '#268bd2' },
dark: { fg: '#93a1a1', bg: '#002b36', link: '#268bd2' },
},
{
name: 'gruvbox', label: _('Gruvbox'),
light: { fg: '#3c3836', bg: '#fbf1c7', link: '#076678' },
dark: { fg: '#ebdbb2', bg: '#282828', link: '#83a598' },
},
{
name: 'nord', label: _('Nord'),
light: { fg: '#2e3440', bg: '#eceff4', link: '#5e81ac' },
dark: { fg: '#d8dee9', bg: '#2e3440', link: '#88c0d0' },
},
]

for (const { file, name } of utils.listDir(pkg.configpath('themes'))) try {
if (!/\.json$/.test(name)) continue
const name_ = name.replace(/\.json$/)
const theme = utils.readJSONFile(file)
themes.push({
name: name_,
label: theme.label ?? name_,
light: {
fg: theme.light.fg,
bg: theme.light.bg,
link: theme.light.link,
},
dark: {
fg: theme.dark.fg,
bg: theme.dark.bg,
link: theme.dark.link,
},
})
} catch (e) {
console.error(e)
}

const themeCssProvider = new Gtk.CssProvider()
themeCssProvider.load_from_data(`
.theme-container .card {
padding: 9px;
}
` + themes.map(theme => {
const id = `theme-${GLib.uuid_string_random()}`
theme.id = id
// NOTE: .92 matches Libadwaita's sidebar color when the bg is white
// in dark mode it should use 1.41 to match Libadwaita
// but it seems better to tone it down as many dark themes are quite bright
return `
.${id} {
color: ${theme.light.fg};
background: ${theme.light.bg};
}
.is-dark .${id} {
color: ${theme.dark.fg};
background: ${theme.dark.bg};
}
.sidebar-${id}:not(.background) {
color: ${theme.light.fg};
background: shade(${theme.light.bg}, .92);
}
.is-dark .sidebar-${id}:not(.background) {
color: ${theme.dark.fg};
background: shade(${theme.dark.bg}, 1.2);
}
.${id} highlight {
background: ${theme.light.link};
}
.is-dark .${id} highlight {
background: ${theme.dark.link};
}
.${id} popover highlight, .is-dark .${id} popover highlight {
background: @accent_bg_color;
}
`
}).join(''), -1)
Gtk.StyleContext.add_provider_for_display(
Gdk.Display.get_default(),
themeCssProvider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)

export const invertTheme = ({ light, dark }) => ({ light, dark, inverted: {
fg: utils.invertColor(dark.fg),
link: utils.invertColor(dark.link),
} })
13 changes: 13 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,19 @@ export const debounce = (f, wait, immediate) => {
}
}

export const listDir = function* (path, attributes = 'standard::name') {
const dir = Gio.File.new_for_path(path)
if (!GLib.file_test(path, GLib.FileTest.IS_DIR)) return
const children = dir.enumerate_children(attributes, Gio.FileQueryInfoFlags.NONE, null)
let info
while ((info = children.next_file(null)) != null) try {
const name = info.get_name()
yield { file: dir.get_child(name), name, info }
} catch (e) {
console.error(e)
}
}

const decoder = new TextDecoder()
export const readFile = (file, defaultValue = '') => {
try {
Expand Down

0 comments on commit ce18087

Please sign in to comment.