Implement theme switcher

auth.: @Exalm
ui
Manuel Genovés 2020-02-25 13:32:10 +01:00
parent a6f7e85255
commit a02c61ec7e
13 changed files with 359 additions and 74 deletions

View File

@ -171,9 +171,48 @@
} }
.quick-preview-popup label { .quick-preview-popup label {
color: @theme_fg_color; color: @theme_fg_color;
} }
.plain-listview { .plain-listview {
background-color: @fg-color; background-color: @fg-color;
}
/* theme selector */
.color-button {
padding: 12px;
border-radius: 999px;
-gtk-outline-radius: 999px;
outline-offset: 1px;
border: none;
-gtk-icon-shadow: none;
min-width: 0px;
min-height: 0px;
color: transparent;
transition: all 200ms ease-out;
}
.color-light {
background: #ffffff;
box-shadow: inset 0 0 0 1px #2e3436;
}
.color-light:checked {
color: #2e3436;
box-shadow: inset 0 0 0 2px @theme_selected_bg_color;
}
.color-dark {
background: #2d2d2d;
box-shadow: inset 0 0 0 1px alpha(black, .35);
}
.color-dark:checked {
color: #eeeeec;
box-shadow: inset 0 0 0 2px @theme_selected_bg_color;
}
.color-button:disabled {
background: #929292;
box-shadow: inset 0 0 0 1px #2e3436;
} }

View File

@ -15,5 +15,23 @@
--kbd-background-color: #f1f1f1; --kbd-background-color: #f1f1f1;
--kbd-border-color: #bdc1c6; --kbd-border-color: #bdc1c6;
--kbd-shadow-color: #8c939a; --kbd-shadow-color: #8c939a;
}
} @media (prefers-color-scheme: dark) {
:root {
--text-color: #eeeeec;
--background-color: #353535;
--alt-background-color: #3a3a3a;
--link-color: #b5daff;
--blockquote-text-color: #a8a8a6;
--blockquote-border-color: #525252;
--header-border-color: #474747;
--hr-background-color: #505050;
--table-tr-border-color: #696969;
--table-td-border-color: #525252;
--kbd-text-color: #cececc;
--kbd-background-color: #3c3c3c;
--kbd-border-color: #696969;
--kbd-shadow-color: #979797;
}
}

View File

@ -1,18 +0,0 @@
@import url("base.css");
:root {
--text-color: #eeeeec;
--background-color: #353535;
--alt-background-color: #3a3a3a;
--link-color: #b5daff;
--blockquote-text-color: #a8a8a6;
--blockquote-border-color: #525252;
--header-border-color: #474747;
--hr-background-color: #505050;
--table-tr-border-color: #696969;
--table-td-border-color: #525252;
--kbd-text-color: #cececc;
--kbd-background-color: #3c3c3c;
--kbd-border-color: #696969;
--kbd-shadow-color: #979797;
}

View File

@ -15,4 +15,23 @@
--kbd-background-color: #f0f2f4; --kbd-background-color: #f0f2f4;
--kbd-border-color: #bcc2c9; --kbd-border-color: #bcc2c9;
--kbd-shadow-color: #8b949d; --kbd-shadow-color: #8b949d;
} }
@media (prefers-color-scheme: dark) {
:root {
--text-color: #d3dae3;
--background-color: #383c4a;
--alt-background-color: #3d414f;
--link-color: #9ac6ff;
--blockquote-text-color: #8d949d;
--blockquote-border-color: #555967;
--header-border-color: #4a4e5c;
--hr-background-color: #535765;
--table-tr-border-color: #6c707e;
--table-td-border-color: #555967;
--kbd-text-color: #b3bac3;
--kbd-background-color: #3f4351;
--kbd-border-color: #6c707e;
--kbd-shadow-color: #9a9eac;
}
}

View File

@ -1,18 +0,0 @@
@import url("base.css");
:root {
--text-color: #d3dae3;
--background-color: #383c4a;
--alt-background-color: #3d414f;
--link-color: #9ac6ff;
--blockquote-text-color: #8d949d;
--blockquote-border-color: #555967;
--header-border-color: #4a4e5c;
--hr-background-color: #535765;
--table-tr-border-color: #6c707e;
--table-td-border-color: #555967;
--kbd-text-color: #b3bac3;
--kbd-background-color: #3f4351;
--kbd-border-color: #6c707e;
--kbd-shadow-color: #9a9eac;
}

View File

@ -1 +0,0 @@
@import url("arc.css");

View File

@ -8,6 +8,7 @@
<file compressed="true">media/css/gtk/base.css</file> <file compressed="true">media/css/gtk/base.css</file>
<file compressed="true" preprocess="xml-stripblanks">ui/Export.ui</file> <file compressed="true" preprocess="xml-stripblanks">ui/Export.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/Menu.ui</file> <file compressed="true" preprocess="xml-stripblanks">ui/Menu.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/Menu2.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/Preferences.ui</file> <file compressed="true" preprocess="xml-stripblanks">ui/Preferences.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/Preview.ui</file> <file compressed="true" preprocess="xml-stripblanks">ui/Preview.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/Recents.ui</file> <file compressed="true" preprocess="xml-stripblanks">ui/Recents.ui</file>

229
data/ui/Menu2.ui 100644
View File

@ -0,0 +1,229 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkPopover" id="Menu">
<property name="can_focus">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="margin_right">12</property>
<property name="margin_top">12</property>
<property name="margin_bottom">12</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="margin_left">12</property>
<property name="margin_right">12</property>
<property name="margin_bottom">6</property>
<property name="spacing">24</property>
<child>
<object class="GtkRadioButton" id="light_mode_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="halign">center</property>
<property name="action_name">app.dark_mode</property>
<property name="draw_indicator">False</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">emblem-ok-symbolic</property>
</object>
</child>
<style>
<class name="color-button"/>
<class name="color-light"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="dark_mode_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="halign">center</property>
<property name="draw_indicator">False</property>
<property name="group">light_mode_button</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">emblem-ok-symbolic</property>
</object>
</child>
<style>
<class name="color-button"/>
<class name="color-dark"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="action_name">app.hemingway_mode</property>
<property name="text" translatable="yes">Hemingway mode</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="action_name">app.focus_mode</property>
<property name="text" translatable="yes">Focus Mode</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="action_name">app.search_replace</property>
<property name="text" translatable="yes">Find and Replace</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">6</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="action_name">app.preferences</property>
<property name="text" translatable="yes">Preferences</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">7</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">8</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="action_name">app.open_tutorial</property>
<property name="text" translatable="yes">Open Tutorial</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">9</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="action_name">app.shortcuts</property>
<property name="text" translatable="yes">Keyboard Shortcuts</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">10</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="action_name">app.about</property>
<property name="text" translatable="yes">About Uberwriter</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">11</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View File

@ -41,6 +41,7 @@ class Application(Gtk.Application):
Gtk.Application.do_startup(self) Gtk.Application.do_startup(self)
self.settings.connect("changed", self.on_settings_changed) self.settings.connect("changed", self.on_settings_changed)
self._set_dark_mode ()
# Header bar # Header bar
@ -65,6 +66,10 @@ class Application(Gtk.Application):
self.add_action(action) self.add_action(action)
# App Menu # App Menu
action = Gio.SimpleAction.new_stateful(
"dark_mode", None, GLib.Variant.new_boolean(False))
action.connect("change-state", self.on_dark_mode)
self.add_action(action)
action = Gio.SimpleAction.new_stateful( action = Gio.SimpleAction.new_stateful(
"focus_mode", None, GLib.Variant.new_boolean(False)) "focus_mode", None, GLib.Variant.new_boolean(False))
@ -184,9 +189,20 @@ class Application(Gtk.Application):
self.activate() self.activate()
return 0 return 0
def _set_dark_mode (self):
dark = self.settings.get_value("dark-mode")
settings = Gtk.Settings.get_default()
settings.props.gtk_application_prefer_dark_theme = dark
if settings.props.gtk_theme_name == "HighContrast" and dark:
settings.props.gtk_theme_name = "HighContrastInverse"
elif settings.props.gtk_theme_name == "HighContrastInverse" and not dark:
settings.props.gtk_theme_name = "HighContrast"
def on_settings_changed(self, settings, key): def on_settings_changed(self, settings, key):
if key == "dark-mode-auto" or key == "dark-mode": if key == "dark-mode-auto" or key == "dark-mode":
self.window.apply_current_theme() self._set_dark_mode ()
elif key == "spellcheck": elif key == "spellcheck":
self.window.toggle_spellcheck(settings.get_value(key)) self.window.toggle_spellcheck(settings.get_value(key))
elif key == "gradient-overlay": elif key == "gradient-overlay":
@ -203,6 +219,9 @@ class Application(Gtk.Application):
def on_new(self, _action, _value): def on_new(self, _action, _value):
self.window.new_document() self.window.new_document()
def on_dark_mode(self, action, value):
print(action, value)
def on_open(self, _action, _value): def on_open(self, _action, _value):
self.window.open_document() self.window.open_document()

View File

@ -63,6 +63,17 @@ class BaseHeaderbar:
self.menu_button = self.builder.get_object("menu_button") self.menu_button = self.builder.get_object("menu_button")
self.recents_button = self.builder.get_object("recents_button") self.recents_button = self.builder.get_object("recents_button")
add_menus(self, app)
settings = Gtk.Settings.get_default()
if global_dark:= settings.props.gtk_theme_name.endswith("-dark"):
self.light_button.set_sensitive(False)
self.light_button.set_tooltip_text(_("Light mode isn't available while using a dark global theme"))
self.dark_button.set_active(self.settings.get_boolean("dark-mode") or global_dark)
self.light_button.connect("toggled", self.__on_dark_mode)
def update_preview_layout_icon(self): def update_preview_layout_icon(self):
mode = self.settings.get_enum("preview-mode") mode = self.settings.get_enum("preview-mode")
self.preview_switcher_icon.set_from_icon_name( self.preview_switcher_icon.set_from_icon_name(
@ -118,6 +129,8 @@ class BaseHeaderbar:
self.settings.set_boolean("sync-scroll", state) self.settings.set_boolean("sync-scroll", state)
return False return False
def __on_dark_mode(self, _):
self.settings.set_boolean("dark-mode", self.dark_button.get_active())
class MainHeaderbar(BaseHeaderbar): # pylint: disable=too-few-public-methods class MainHeaderbar(BaseHeaderbar): # pylint: disable=too-few-public-methods
"""Sets up the main application headerbar """Sets up the main application headerbar
@ -129,7 +142,7 @@ class MainHeaderbar(BaseHeaderbar): # pylint: disable=too-few-public-methods
self.hb.set_show_close_button(True) self.hb.set_show_close_button(True)
add_menus(self, app) #add_menus(self, app)
self.hb_revealer.props.transition_duration = 0 self.hb_revealer.props.transition_duration = 0
@ -147,7 +160,7 @@ class FullscreenHeaderbar(BaseHeaderbar):
self.exit_fs_button = self.builder.get_object("exit_fs_button") self.exit_fs_button = self.builder.get_object("exit_fs_button")
self.exit_fs_button.set_visible(True) self.exit_fs_button.set_visible(True)
add_menus(self, app) #add_menus(self, app)
self.events = fs_builder.get_object("FullscreenEventbox") self.events = fs_builder.get_object("FullscreenEventbox")
self.events.add(self.hb_revealer) self.events.add(self.hb_revealer)
@ -248,10 +261,12 @@ def add_menus(headerbar, app):
builder_window_menu = Gtk.Builder() builder_window_menu = Gtk.Builder()
builder_window_menu.add_from_resource( builder_window_menu.add_from_resource(
"/de/wolfvollprecht/UberWriter/ui/Menu.ui") "/de/wolfvollprecht/UberWriter/ui/Menu2.ui")
model = builder_window_menu.get_object("Menu") model = builder_window_menu.get_object("Menu")
headerbar.light_button = builder_window_menu.get_object("light_mode_button")
headerbar.dark_button = builder_window_menu.get_object("dark_mode_button")
headerbar.menu_button.set_menu_model(model) headerbar.menu_button.set_popover(model)
# Add recents menu to the open recents button # Add recents menu to the open recents button

View File

@ -35,7 +35,6 @@ from gi.repository import Gtk, Gdk, GObject, GLib, Gio
import cairo import cairo
from uberwriter import helpers from uberwriter import helpers
from uberwriter.theme import Theme
from uberwriter.sidebar import Sidebar from uberwriter.sidebar import Sidebar
from uberwriter.search_and_replace import SearchAndReplace from uberwriter.search_and_replace import SearchAndReplace

View File

@ -1,7 +1,6 @@
import gi import gi
from uberwriter import helpers from uberwriter import helpers
from uberwriter.theme import Theme
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib, Gio from gi.repository import Gtk, GLib, Gio
@ -13,27 +12,11 @@ class StyledWindow(Gtk.ApplicationWindow):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.connect("style-updated", self.apply_current_theme) # Set theme css
self.apply_current_theme() css_provider_file = Gio.File.new_for_uri(
"resource:///de/wolfvollprecht/UberWriter/media/css/gtk/base.css")
def apply_current_theme(self, *_): style_provider = Gtk.CssProvider()
"""Adjusts the window, CSD and preview for the current theme.""" style_provider.load_from_file(css_provider_file)
# Get current theme Gtk.StyleContext.add_provider_for_screen(
theme, changed = Theme.get_current_changed() self.get_screen(), style_provider,
if changed: Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
# Set theme variant (dark/light)
Gtk.Settings.get_default().set_property(
"gtk-application-prefer-dark-theme",
GLib.Variant("b", theme.is_dark))
# Set theme css
css_provider_file = Gio.File.new_for_uri(
"resource:///de/wolfvollprecht/UberWriter/media/css/gtk/base.css")
style_provider = Gtk.CssProvider()
style_provider.load_from_file(css_provider_file)
Gtk.StyleContext.add_provider_for_screen(
self.get_screen(), style_provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
# Redraw contents of window
self.queue_draw()

View File

@ -56,11 +56,11 @@ class Theme:
defaultThemes = [ defaultThemes = [
# https://gitlab.gnome.org/GNOME/gtk/tree/master/gtk/theme/Adwaita # https://gitlab.gnome.org/GNOME/gtk/tree/master/gtk/theme/Adwaita
Theme('Adwaita', get_css_path('web/adwaita.css'), False, 'Adwaita-dark'), Theme('Adwaita', get_css_path('web/adwaita.css'), False, 'Adwaita-dark'),
Theme('Adwaita-dark', get_css_path('web/adwaita_dark.css'), True, 'Adwaita'), Theme('Adwaita-dark', get_css_path('web/adwaita.css'), True, 'Adwaita'),
# https://github.com/NicoHood/arc-theme/tree/master/common/gtk-3.0/3.20/sass # https://github.com/NicoHood/arc-theme/tree/master/common/gtk-3.0/3.20/sass
Theme('Arc', get_css_path('web/arc.css'), False, 'Arc-Dark'), Theme('Arc', get_css_path('web/arc.css'), False, 'Arc-Dark'),
Theme('Arc-Darker', get_css_path('web/arc_darker.css'), False, 'Arc-Dark'), Theme('Arc-Darker', get_css_path('web/arc.css'), False, 'Arc-Dark'),
Theme('Arc-Dark', get_css_path('web/arc_dark.css'), True, 'Arc'), Theme('Arc-Dark', get_css_path('web/arc.css'), True, 'Arc'),
# https://gitlab.gnome.org/GNOME/gtk/tree/master/gtk/theme/HighContrast # https://gitlab.gnome.org/GNOME/gtk/tree/master/gtk/theme/HighContrast
Theme('HighContrast', get_css_path('web/highcontrast.css'), False, 'HighContrastInverse'), Theme('HighContrast', get_css_path('web/highcontrast.css'), False, 'HighContrastInverse'),
Theme('HighContrastInverse', get_css_path('web/highcontrast_inverse.css'), True, 'HighContrast') Theme('HighContrastInverse', get_css_path('web/highcontrast_inverse.css'), True, 'HighContrast')