Encapsulate theme handling

The Theme class encapsulates theme handling, by listing default themes
and providing means to access their gtk/web css.

Besides cleaning things up, it makes it easy to support custom themes
in the future. The user just needs to provide two CSS files, and we'll
be able to instantiate and use a Theme from that.
ft.font-size
Gonçalo Silva 2019-03-26 14:18:19 +00:00
parent ea566b8d73
commit e7359c5776
14 changed files with 175 additions and 162 deletions

View File

@ -1,4 +0,0 @@
@define-color dark_bg #353535;
@define-color light_bg #F6F5F4;
@import url("style.css");

View File

@ -1,4 +0,0 @@
@define-color dark_bg #31373D;
@define-color light_bg #EDEDED;
@import url("style.css");

View File

@ -27,19 +27,8 @@
.uberwriter_window {
-gtk-key-bindings: window-bindings;
/*border-radius: 7px 7px 3px 3px;*/
background: @light_bg;
caret-color: @dark_bg;
}
.uberwriter_window.dark_mode {
background: @dark_bg;
caret-color: @light_bg;
}
.uberwriter_window.dark_mode .uberwriter-editor text{
background: @dark_bg;
caret-color: @light_bg;
color: @light_bg;
background: @background_color;
caret-color: @foreground_color;
}
.uberwriter_window.small .uberwriter-editor {
@ -47,12 +36,7 @@
font-size: 12px;
}
.uberwriter_window grid {
background-color: @light_bg;
}
.uberwriter_window.dark_mode grid,
.uberwriter_window.dark_mode scrolledwindow {
background-color: @dark_bg;
background-color: @background_color;
}
#UberwriterWindow.medium .uberwriter-editor {
@ -75,25 +59,22 @@
}
#titlebar_container {
background: @light_bg;
}
#titlebar_container.dark_mode {
background: @dark_bg;
background: @background_color;
}
.uberwriter-editor {
border: none;
background-color: transparent;
color: #222;
text-decoration-color: #ff0000;
/*-GtkWidget-cursor-color: shade(#4D9FCE, 0.9);*/
/*-GtkWidget-cursor-aspect-ratio: 0.1;*/
-gtk-key-bindings: editor-bindings;
}
.uberwriter-editor text {
background-color: @light_bg;
color: #222;
background-color: @background_color;
color: @foreground_color;
caret-color: @foreground_color;
}
.uberwriter-editor:selected {
@ -120,7 +101,7 @@
.status_bar_box button {
/* finding reset */
background-color: @light_bg;
background-color: @background_color;
text-shadow: inherit;
/*icon-shadow: inherit;*/
box-shadow: initial;
@ -146,13 +127,13 @@
.status_bar_box button:hover,
.status_bar_box button:checked {
transition: 0s ease-in;
color: @light_bg;
color: @background_color;
background-color: #666;
}
.status_bar_box button:hover label,
.status_bar_box button:checked label {
color: @light_bg;
color: @background_color;
}
.status_bar_box button:active {
@ -161,22 +142,7 @@
background-image: none;
box-shadow: 0 0 2px rgba(0,0,0,0.4)
}
.dark_mode .status_bar_box button {
background-color: @dark_bg;
}
.dark_mode .status_bar_box label {
color: @light_bg;
}
.dark_mode .status_bar_box button:hover,
.dark_mode .status_bar_box button:checked {
background-color: @light_bg;
color: #666;
}
.dark_mode .status_bar_box button:hover label,
.dark_mode .status_bar_box button:checked label{
color: #666;
}
.status_bar_box separator {
border-color: #999;
border-right: none;
@ -198,9 +164,9 @@
/*font: serif 10;*/
font-family: serif;
font-size: 10px;
background: @light_bg;
background: @background_color;
border-radius: 4px;
border-color: @light_bg;
border-color: @background_color;
margin: 5px;
padding: 5px;
}
@ -211,7 +177,7 @@
border: 1px solid #333;
background: @ligth_bg;
border-radius: 3px;
border-color: @light_bg;
border-color: @background_color;
} */
#LexikonBubble label {
@ -219,9 +185,8 @@
}
#LexikonBubble {
background-color: @light_bg;
border: 5px solid @light_bg;
border-color: @light_bg
background-color: @background_color;
border: 5px solid @background_color;
}
#LexikonBubble .lexikon_heading {
@ -240,15 +205,15 @@
}
.QuickPreviewPopup {
background-color: @light_bg;
background-color: @background_color;
}
.QuickPreviewPopup grid {
background-color: @light_bg;
color: @dark_bg;
border-color: @light_bg;
background-color: @background_color;
color: @foreground_color;
border-color: @background_color;
}
.QuickPreviewPopup label {
color: @dark_bg;
}
color: @foreground_color;
}

View File

@ -1,11 +1,11 @@
@font-face {
font-family: fira-sans;
src: url("fonts/fira-sans-v9-vietnamese_latin_cyrillic-ext_cyrillic_greek-ext_latin-ext_greek-regular.woff2") format("woff2");
src: url("../fonts/fira-sans-v9-vietnamese_latin_cyrillic-ext_cyrillic_greek-ext_latin-ext_greek-regular.woff2") format("woff2");
}
@font-face {
font-family: fira-mono;
src: url("fonts/fira-mono-v7-latin_cyrillic-ext_cyrillic_greek-ext_latin-ext_greek-regular.woff2") format("woff2");
src: url("../fonts/fira-mono-v7-latin_cyrillic-ext_cyrillic_greek-ext_latin-ext_greek-regular.woff2") format("woff2");
}
@font-face {
@ -14,22 +14,21 @@
}
:root {
--text-color: #242424;
--background-color: #f6f5f4;
--alt-background-color: #ebebeb;
/* This is GitHub's default color scheme, which should be overridden per theme. */
--text-color: #24292e;
--background-color: #ffffff;
--alt-background-color: #f6f8fa;
--link-color: #0366d6;
--blockquote-text-color: #606060;
--blockquote-border-color: #d8d8d8;
--header-border-color: #e2e2e2;
--hr-background-color: #dadada;
--hr-border-color: #e4e4e4;
--code-background-color: #eeeeee;
--table-td-border-color: #d8d8d8;
--table-tr-border-color: #c1c1c1;
--kbd-text-color: #444444;
--kbd-background-color: #f2f2f2;
--kbd-border-color: #c1c1c1;
--kbd-shadow-color: #939393;
--blockquote-text-color: #6a737d;
--blockquote-border-color: #dfe2e5;
--header-border-color: #eaecef;
--hr-background-color: #e1e4e8;
--table-tr-border-color: #c6cbd1;
--table-td-border-color: #dfe2e5;
--kbd-text-color: #444d56;
--kbd-background-color: #fafbfc;
--kbd-border-color: #c6cbd1;
--kbd-shadow-color: #959da5;
}
* {
@ -55,7 +54,7 @@ html {
body {
color: var(--text-color);
background-color: var(--background-color);
font-family: fira-sans, sans-serif, color-emoji;
font-family: "Fira Sans", fira-sans, sans-serif, color-emoji;
line-height: 1.5;
text-size-adjust: 100%;
word-wrap: break-word;
@ -93,7 +92,6 @@ hr {
overflow: hidden;
background-color: var(--hr-background-color);
border: 0;
border-bottom: 1px solid var(--hr-border-color);
}
hr::before {
@ -199,8 +197,7 @@ dd {
code,
kbd,
pre {
font-family: fira-mono, monospace, color-emoji;
background-color: var(--code-background-color);
font-family: "Fira Mono", fira-mono, monospace, color-emoji;
font-size: 1em;
word-wrap: normal;
}

View File

@ -0,0 +1,5 @@
@define-color foreground_color #2e3436;
@define-color background_color #f6f5f4;
@define-color math_text_color #00364c;
@import url("_gtk_base.css");

View File

@ -0,0 +1,5 @@
@define-color foreground_color #eeeeec;
@define-color background_color #353535;
@define-color math_text_color #ffc9b3;
@import url("_gtk_base.css");

View File

@ -0,0 +1,19 @@
@import url("_web_base.css");
:root {
--text-color: #2e3436;
--background-color: #f6f5f4;
--alt-background-color: #edeeef;
--link-color: #0d71de;
--blockquote-text-color: #747e85;
--blockquote-border-color: #d6d8da;
--header-border-color: #e1e2e4;
--hr-background-color: #d8dadd;
--table-tr-border-color: #bdc1c6;
--table-td-border-color: #d6d8da;
--kbd-text-color: #4e585e;
--kbd-background-color: #f1f1f1;
--kbd-border-color: #bdc1c6;
--kbd-shadow-color: #8c939a;
}

View File

@ -1,19 +1,17 @@
@import url("github-md.css");
@import url("_web_base.css");
:root {
--text-color: #dbdbdb;
--text-color: #eeeeec;
--background-color: #353535;
--alt-background-color: #3a3a3a;
--link-color: #4388d6;
--blockquote-text-color: #959595;
--link-color: #b5daff;
--blockquote-text-color: #a8a8a6;
--blockquote-border-color: #525252;
--header-border-color: #474747;
--hr-background-color: #505050;
--hr-border-color: #464646;
--code-background-color: #3e3e3e;
--table-td-border-color: #525252;
--table-tr-border-color: #696969;
--kbd-text-color: #bbbbbb;
--table-td-border-color: #525252;
--kbd-text-color: #cececc;
--kbd-background-color: #3c3c3c;
--kbd-border-color: #696969;
--kbd-shadow-color: #979797;

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,49 @@
from gi.repository import Gtk
from uberwriter.Settings import Settings
from uberwriter_lib.helpers import get_css_path
class Theme:
"""
The Theme enum lists all supported themes using their "gtk-theme-name" value.
The light variant is listed first, followed by the dark variant, if any.
"""
settings = Settings.new()
def __init__(self, name, gtk_css_path, web_css_path, is_dark, inverse_name):
self.name = name
self.gtk_css_path = gtk_css_path
self.web_css_path = web_css_path
self.is_dark = is_dark
self.inverse_name = inverse_name
@classmethod
def get_for_name(cls, name, default=None):
current_theme = default or defaultThemes[0]
for theme in defaultThemes:
if name == theme.name:
current_theme = theme
return current_theme
@classmethod
def get_current(cls):
theme_name = Gtk.Settings.get_default().get_property('gtk-theme-name')
dark_mode = cls.settings.get_value('dark-mode').get_boolean()
current_theme = cls.get_for_name(theme_name)
# Technically, we could very easily allow the user to force the light ui on a dark theme.
# However, as there is no inverse of "gtk-application-prefer-dark-theme", we shouldn't do that.
if dark_mode and not current_theme.is_dark and current_theme.inverse_name:
current_theme = cls.get_for_name(current_theme.inverse_name, current_theme.name)
return current_theme
defaultThemes = [
# https://gitlab.gnome.org/GNOME/gtk/tree/master/gtk/theme/Adwaita
Theme('Adwaita', get_css_path('gtk_adwaita.css'),
get_css_path('web_adwaita.css'), False, 'Adwaita-dark'),
Theme('Adwaita-dark', get_css_path('gtk_adwaita_dark.css'),
get_css_path('web_adwaita_dark.css'), True, 'Adwaita'),
]

View File

@ -24,6 +24,9 @@ import logging
from gettext import gettext as _
import gi
from uberwriter.Theme import Theme
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
@ -215,7 +218,7 @@ class Export:
args.append("-o%s.odt" % basename)
elif export_format == "html":
css = helpers.get_media_file('github-md.css')
css = Theme.ADWAITA.get_gtk_css_file()
relativize = helpers.get_script_path('relative_to_absolute.lua')
task_list = helpers.get_script_path('task-list.lua')
args.append("-c%s" % css)

View File

@ -14,20 +14,22 @@
# with this program. If not, see <http://www.gnu.org/licenses/>.
# END LICENSE
import locale
import subprocess
import os
import codecs
import webbrowser
import urllib
import locale
import logging
import mimetypes
import os
import re
import subprocess
import urllib
import webbrowser
from gettext import gettext as _
import gi
from gi.repository.GObject import param_spec_string
from uberwriter.Theme import Theme
gi.require_version('Gtk', '3.0')
gi.require_version('WebKit2', '4.0') # pylint: disable=wrong-import-position
from gi.repository import Gtk, Gdk, GObject, GLib, Gio
@ -181,21 +183,13 @@ class UberwriterWindow(Gtk.ApplicationWindow):
# Init file name with None
self.set_filename()
# self.style_provider = Gtk.CssProvider()
# self.style_provider.load_from_path(helpers.get_media_path('arc_style.css'))
# Gtk.StyleContext.add_provider_for_screen(
# Gdk.Screen.get_default(), self.style_provider,
# Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
# )
# Markup and Shortcuts for the TextBuffer
self.markup_buffer = MarkupBuffer(
self, self.text_buffer, base_leftmargin)
self.markup_buffer.markup_buffer()
# Setup dark mode if so
self.toggle_dark_mode(self.settings.get_value("dark-mode"))
# Set current theme
self.apply_current_theme()
# Scrolling -> Dark or not?
self.textchange = False
@ -266,6 +260,22 @@ class UberwriterWindow(Gtk.ApplicationWindow):
'close-window': (GObject.SIGNAL_ACTION, None, ())
}
def apply_current_theme(self):
"""Adjusts both the window and the CSD for the current theme.
"""
theme = Theme.get_current()
if theme.is_dark:
self.markup_buffer.dark_mode(True)
else:
self.markup_buffer.dark_mode(False)
# Reload preview if it exists, otherwise redraw contents of window (self)
if self.preview_webview:
self.show_preview()
else:
self.queue_draw()
def scrolled(self, widget):
"""if window scrolled + focusmode make font black again"""
# if self.focusmode:
@ -387,7 +397,7 @@ class UberwriterWindow(Gtk.ApplicationWindow):
else:
self.remove_typewriter()
self.focusmode = False
self.text_buffer.remove_tag(self.markup_buffer.grayfont,
self.text_buffer.remove_tag(self.markup_buffer.unfocused_text,
self.text_buffer.get_start_iter(),
self.text_buffer.get_end_iter())
self.text_buffer.remove_tag(self.markup_buffer.blackfont,
@ -865,20 +875,13 @@ class UberwriterWindow(Gtk.ApplicationWindow):
base_path = ''
os.environ['PANDOC_PREFIX'] = base_path + '/'
# Set the styles according the color theme
if self.settings.get_value("dark-mode"):
stylesheet = helpers.get_media_path('github-md-dark.css')
else:
stylesheet = helpers.get_media_path('github-md.css')
args = ['pandoc',
'-s',
'--from=markdown',
'--to=html5',
'--mathjax',
'--css=' + stylesheet,
'--lua-filter=' +
helpers.get_script_path('relative_to_absolute.lua'),
'--css=' + Theme.get_current().web_css_path,
'--lua-filter=' + helpers.get_script_path('relative_to_absolute.lua'),
'--lua-filter=' + helpers.get_script_path('task-list.lua')]
proc = subprocess.Popen(
@ -906,32 +909,6 @@ class UberwriterWindow(Gtk.ApplicationWindow):
self.preview_webview.load_html(output.decode("utf-8"), 'file://localhost/')
def toggle_dark_mode(self, state):
"""Toggle the dark mode, both for the window and for the CSD
Arguments:
state {gtk bool} -- Desired state of the dark mode (enabled/disabled)
"""
# Save state for saving settings later
if state:
# Dark Mode is on
self.get_style_context().add_class("dark_mode")
self.headerbar.hb_container.get_style_context().add_class("dark_mode")
self.markup_buffer.dark_mode(True)
else:
# Dark mode off
self.get_style_context().remove_class("dark_mode")
self.headerbar.hb_container.get_style_context().remove_class("dark_mode")
self.markup_buffer.dark_mode(False)
# Reload preview if it exists
if self.preview_webview:
self.show_preview()
# Redraw contents of window (self)
self.queue_draw()
def load_file(self, filename=None):
"""Open File from command line or open / open recent etc."""
if self.check_change() == Gtk.ResponseType.CANCEL:

View File

@ -49,16 +49,12 @@ def get_builder(builder_file_name):
# Owais Lone : To get quick access to icons and stuff.
def get_media_file(media_file_name):
def get_media_file(media_file_path):
"""Return the full path of a given filename under the media dir
(starts with file:///)
"""
media_filename = get_data_file('media', '%s' % (media_file_name,))
if not os.path.exists(media_filename):
media_filename = None
return "file:///" + media_filename
return "file:///" + get_media_path(media_file_path)
def get_media_path(media_file_name):
@ -66,19 +62,26 @@ def get_media_path(media_file_name):
(doesn't start with file:///)
"""
media_filename = get_data_file('media', '%s' % (media_file_name,))
if not os.path.exists(media_filename):
media_filename = None
return media_filename
media_path = get_data_file('media', '%s' % (media_file_name,))
if not os.path.exists(media_path):
media_path = None
return media_path
def get_css_path(css_file_name):
"""Return the full path of a given filename under the css dir
(doesn't start with file:///)
"""
return get_media_path("css/{}".format(css_file_name))
def get_script_path(script_file_name):
"""Return the full path of a given filename under the script dir
"""
script_filename = get_data_file('lua', '%s' % (script_file_name,))
if not os.path.exists(script_filename):
script_filename = None
return script_filename
script_path = get_data_file('lua', '%s' % (script_file_name,))
if not os.path.exists(script_path):
script_path = None
return script_path
class NullHandler(logging.Handler):