Merge branch '0.3.0' into main

This commit is contained in:
0xMRTT 2022-09-23 13:13:57 +02:00 committed by GitHub
commit 8bf32aa5b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 589 additions and 166 deletions

View file

@ -73,3 +73,4 @@ endif
subdir('icons')
subdir('theme')
subdir('plugins')

View file

@ -0,0 +1,225 @@
from yapsy.IPlugin import IPlugin
from gi.repository import Gtk, Adw
import os
from pathlib import Path
BASE = """
################################################################################
# Generated by the firefox-gnome-theme plugin
# Made by Gradience Team for GNOME Firefox Theme
################################################################################
:root {{
--d_1: {d_1};
--d_2: {d_2};
--d_3: {d_3};
--d_4: {d_4};
--l_1: {l_1};
--l_2: {l_2};
--l_3: {l_3};
--l_4: {l_4};
--bg: {bg};
--fg: {fg};
--blk: {blk};
--red: {red};
--grn: {grn};
--ylw: {ylw};
--blu: {blu};
--pnk: {pnk};
--cyn: {cyn};
--wht: {wht};
--b_blk: {b_blk};
--b_red: {b_red};
--b_grn: {b_grn};
--b_ylw: {b_ylw};
--b_blu: {b_blu};
--b_pnk: {b_pnk};
--b_cyn: {b_cyn};
--b_wht: {b_wht};
}}
:root {{
--gnome-browser-before-load-background: var(--s_1);
--gnome-accent-bg: var(--blu);
--gnome-accent: var(--b_blu);
--gnome-toolbar-background: var(--s_1);
--gnome-toolbar-color: var(--l_4);
--gnome-toolbar-icon-fill: var(--l_3);
--gnome-inactive-toolbar-color: var(--l_2);
--gnome-inactive-toolbar-border-color: var(--d_4);
--gnome-inactive-toolbar-icon-fill: var(--l_1);
--gnome-menu-background: var(--d_3);
--gnome-headerbar-background: var(--d_1);
--gnome-button-destructive-action-background: var(--red);
--gnome-entry-color: var(--l_4);
--gnome-inactive-entry-color: var(--l_3);
--gnome-switch-slider-background: var(--l_3);
--gnome-switch-active-slider-background: var(--l_4);
--gnome-inactive-tabbar-tab-background: var(--d_1);
--gnome-inactive-tabbar-tab-active-background: var(--d_1);
--gnome-tabbar-tab-background: var(--d_1);
--gnome-tabbar-tab-hover-background: var(--d_2);
--gnome-tabbar-tab-active-background: var(--d_2);
--gnome-tabbar-tab-active-hover-background: var(--d_3);
--gnome-tabbar-tab-active-background-contrast: var(--d_4);
}}
"""
class FirefoxGnomeThemePlugin(IPlugin):
title = "Firefox Gnome Theme"
author = "Gradience Team"
description = "This plugin will customize the Gnome theme for Firefox"
variables = None
palette = None
preset = {
"variables": None,
"palette": None,
"custom_css": None,
}
plugin_id = "firefox_gnome_theme"
# Custom settings shown on a separate view
custom_settings = {"overwrite": True}
css = BASE
browser_row = Adw.EntryRow(
title="Path to the profile directory",
)
profile_dir = None
def activate(self):
# This is called when the plugin is activated
# It does nothing here, but you can use it to initialize
pass
def deactivate(self):
# This is called when the plugin is deactivated
# It does nothing here, but you can use it to clean up
pass
def give_preset_settings(self, preset_settings, custom_settings=None):
# This is called by Gradience for giving to the plugin, the preset settings
self.preset = preset_settings
self.variables = preset_settings["variables"]
self.palette = preset_settings["palette"]
if custom_settings:
self.custom_settings = custom_settings
def open_settings(self):
# This is called when the user clicks on the settings button
# I've choosed to leave the liberty to the plugin creator to decide how to show the settings
# But it's recommended to use a Adw.PreferencesWindow
self.window = Adw.PreferencesWindow()
self.window.set_title("Firefox Gnome Theme Plugin")
self.main_page = Adw.PreferencesPage()
# Apply
self.apply_pref = Adw.PreferencesGroup()
self.apply_pref.set_title("Apply")
self.apply_pref.set_description("Preferences for applying the theme")
self.overwrite_row = Adw.ActionRow(
title="Overwrite",
subtitle="Overwrite the existing userChrome.css file",
)
self.overwrite_switch = Gtk.Switch()
self.overwrite_switch.set_active(self.custom_settings["overwrite"])
self.overwrite_switch.connect("notify::active", self.on_overwrite)
self.overwrite_switch.set_valign(Gtk.Align.CENTER)
self.overwrite_row.add_suffix(self.overwrite_switch)
self.overwrite_row.connect("activate", self.on_overwrite)
self.apply_pref.add(self.overwrite_row)
self.main_page.add(self.apply_pref)
# Browser
self.browser_pref = Adw.PreferencesGroup()
self.browser_pref.set_title("Browser")
self.browser_pref.set_description("Choose where profiles are stored. If you don't know what this is, leave it as default, it will work in most cases.")
self.browser_row = Adw.EntryRow(
title="Path to the directory where profiles are stored",
)
self.browser_row.set_text("~/.mozilla/firefox")
self.browser_row.set_show_apply_button(True)
self.browser_row.connect("apply", self.on_apply)
self.browser_pref.add(self.browser_row)
self.main_page.add(self.browser_pref)
self.window.add(self.main_page)
self.window.present()
def on_apply(self, widget):
self.profile_dir = Path(self.browser_row.get_text()).expanduser()
if not self.profile_dir.exists():
self.browser_row.set_css_classes(["error"])
self.profile_dir = None
else:
self.browser_row.remove_css_class("error")
def on_overwrite(self, widget, _):
# This is called when the user changes the overwrite setting
self.custom_settings["overwrite"] = not self.custom_settings["overwrite"]
def validate(self):
# Normally, it would be a good idea to validate the settings here
# But because there is only one setting and it can onbly be a boolean
# It's not necessary, but it's good practice
# If there would be an error, it's should return True, and the error message in a dictionary
return False, None
def apply(self, dark_theme=False):
# This is called when the user clicks on the apply button (the one in the headerbar)
# You can use dark_theme to know if the user wants a dark theme or not
if self.profile_dir:
profile_dir = self.profile_dir
else:
profile_dir = Path("~/.mozilla/firefox/").expanduser()
profiles = []
if profile_dir.exists():
os.chdir(profile_dir)
for folder in profile_dir.iterdir():
if folder.is_dir():
if (
str(folder).endswith(".default-release")
or str(folder).endswith(".default")
or str(folder).endswith(".default-nightly")
or str(folder).endswith(".dev-edition-default")
):
profiles.append(folder)
if len(profiles) == 0:
print("No profiles found")
return
else:
self.css = self.css.format(d_1=self.palette["dark_"]["1"], d_2=self.palette["dark_"]["2"], d_3=self.palette["dark_"]["3"], d_4=self.palette["dark_"]["4"],
l_1=self.palette["light_"]["1"], l_2=self.palette["light_"][
"2"], l_3=self.palette["light_"]["3"], l_4=self.palette["light_"]["4"],
bg=self.variables["window_bg_color"], fg=self.variables["window_fg_color"],
red=self.palette["red_"]["5"], grn=self.palette["green_"][
"5"], ylw=self.palette["yellow_"]["5"], blu=self.palette["blue_"]["5"],
pnk=self.palette["purple_"]["5"], cyn=self.palette["blue_"][
"5"], wht=self.palette["light_"]["5"], blk=self.palette["dark_"]["5"],
b_red=self.palette["red_"]["1"], b_grn=self.palette["green_"][
"1"], b_ylw=self.palette["yellow_"]["1"], b_blu=self.palette["blue_"]["1"],
b_pnk=self.palette["purple_"]["1"], b_cyn=self.palette["blue_"]["1"], b_wht=self.palette["light_"]["1"], b_blk=self.palette["dark_"]["1"])
for profile in profiles:
profile = profile / "chrome" / "firefox-gnome-theme" / "customChrome.css"
if profile.exists():
if self.custom_settings["overwrite"]:
with open(profile, "w", encoding="utf-8") as f:
f.write(self.css)
else:
with open(profile, "w", encoding="utf-8") as f:
f.write(self.css)
def save(self):
return self.custom_settings

View file

@ -0,0 +1,9 @@
[Core]
Name = Firefox GNOME Theme
Module = firefox_gnome_theme
[Documentation]
Description = Firefox GNOME Theme
Author = Gradience Team
Version = 0.1.0
Website = https://gradienceteam.github.io/plugins/firefox-gnome-theme/

9
data/plugins/meson.build Normal file
View file

@ -0,0 +1,9 @@
builtin_plugins_list = files([
'firefox_gnome_theme.py',
'firefox_gnome_theme.yapsy-plugin',
])
install_data(
builtin_plugins_list,
install_dir: join_paths(get_option('datadir'), 'gradience', 'plugins')
)

View file

@ -67,6 +67,7 @@
}
.badge-yellow {
background-color: #f6d32d;
color: #3d3846;
}
.badge-white {
background-color: #deddda;

View file

@ -49,13 +49,13 @@ template GradiencePresetRow : Adw.ExpanderRow {
transition-type: crossfade;
Button apply_button {
valign: center;
icon-name: "checkmark-small-symbolic";
tooltip-text: _("Apply preset");
clicked => on_apply_button_clicked();
styles [
"flat",
]
valign: center;
icon-name: "checkmark-small-symbolic";
tooltip-text: _("Apply preset");
clicked => on_apply_button_clicked();
styles [
"flat",
]
}
Entry name_entry {
text: "Name";

View file

@ -10,7 +10,7 @@ template GradienceWelcomeWindow: Adw.Window {
Adw.ToastOverlay toast_overlay {
Adw.Leaflet leaflet {
can-navigate-back: true;
can-navigate-back: false;
can-unfold: false;
Gtk.Box main_view {

View file

@ -60,7 +60,10 @@ class GradienceApplication(Adw.Application):
settings = Gio.Settings.new(app_id)
def __init__(self):
super().__init__(application_id=app_id, flags=Gio.ApplicationFlags.FLAGS_NONE)
super().__init__(
application_id=app_id,
flags=Gio.ApplicationFlags.FLAGS_NONE
)
self.set_resource_base_path(rootdir)
self.portal = Xdp.Portal()
@ -89,6 +92,8 @@ class GradienceApplication(Adw.Application):
self.style_manager = Adw.StyleManager.get_default()
self.preset = None
def do_activate(self):
"""Called when the application is activated.
@ -139,10 +144,10 @@ class GradienceApplication(Adw.Application):
self.reload_user_defined_presets()
if self.first_run or version != self.last_opened_version:
buglog("first run")
buglog(version)
buglog(self.last_opened_version)
if version != self.last_opened_version:
welcome = GradienceWelcomeWindow(self.win, update=True)
welcome.present()
elif self.first_run:
welcome = GradienceWelcomeWindow(self.win)
welcome.present()
else:
@ -150,7 +155,6 @@ class GradienceApplication(Adw.Application):
self.win.present()
def reload_user_defined_presets(self):
print("reload")
if self.props.active_window.presets_menu.get_n_items() > 1:
self.props.active_window.presets_menu.remove(1)
@ -176,9 +180,8 @@ class GradienceApplication(Adw.Application):
raise KeyError("variables")
if preset.get("palette") is None:
raise KeyError("palette")
presets_list[file_name.replace(".json", "")] = preset[
"name"
]
presets_list[file_name.replace(".json", "")] \
= preset["name"]
except Exception:
self.win.toast_overlay.add_toast(
Adw.Toast(title=_("Failed to load preset"))
@ -204,9 +207,8 @@ class GradienceApplication(Adw.Application):
raise KeyError("variables")
if preset.get("palette") is None:
raise KeyError("palette")
presets_list["user"][file_name.replace(".json", "")] = preset[
"name"
]
presets_list["user"][file_name.replace(".json", "")] \
= preset["name"]
except Exception:
self.win.toast_overlay.add_toast(
Adw.Toast(title=_("Failed to load preset"))
@ -392,50 +394,63 @@ class GradienceApplication(Adw.Application):
else:
monet_theme = "light"
print(theme)
if monet_theme == "dark":
dark_theme = theme["schemes"]["dark"]
variable = {
"accent_color": self.rgba_from_argb(dark_theme.primary),
"accent_bg_color": self.rgba_from_argb(dark_theme.primaryContainer),
"accent_fg_color": self.rgba_from_argb(dark_theme.onPrimaryContainer),
"accent_bg_color":
self.rgba_from_argb(dark_theme.primaryContainer),
"accent_fg_color":
self.rgba_from_argb(dark_theme.onPrimaryContainer),
"destructive_color": self.rgba_from_argb(dark_theme.error),
"destructive_bg_color": self.rgba_from_argb(dark_theme.errorContainer),
"destructive_bg_color":
self.rgba_from_argb(dark_theme.errorContainer),
"destructive_fg_color": self.rgba_from_argb(
dark_theme.onErrorContainer
),
"success_color": self.rgba_from_argb(dark_theme.tertiary),
"success_bg_color": self.rgba_from_argb(dark_theme.onTertiary),
"success_fg_color": self.rgba_from_argb(dark_theme.onTertiaryContainer),
"success_fg_color":
self.rgba_from_argb(dark_theme.onTertiaryContainer),
"warning_color": self.rgba_from_argb(dark_theme.secondary),
"warning_bg_color": self.rgba_from_argb(dark_theme.onSecondary),
"warning_fg_color": self.rgba_from_argb(dark_theme.primary, "0.8"),
"warning_bg_color":
self.rgba_from_argb(dark_theme.onSecondary),
"warning_fg_color":
self.rgba_from_argb(dark_theme.primary, "0.8"),
"error_color": self.rgba_from_argb(dark_theme.error),
"error_bg_color": self.rgba_from_argb(dark_theme.errorContainer),
"error_bg_color":
self.rgba_from_argb(dark_theme.errorContainer),
"error_fg_color": self.rgba_from_argb(dark_theme.onError),
"window_bg_color": self.rgba_from_argb(dark_theme.surface),
"window_fg_color": self.rgba_from_argb(dark_theme.onSurface),
"view_bg_color": self.rgba_from_argb(dark_theme.surface),
"view_fg_color": self.rgba_from_argb(dark_theme.onSurface),
"headerbar_bg_color": self.rgba_from_argb(dark_theme.surface),
"headerbar_fg_color": self.rgba_from_argb(dark_theme.onSurface),
"headerbar_fg_color":
self.rgba_from_argb(dark_theme.onSurface),
"headerbar_border_color": self.rgba_from_argb(
dark_theme.primary, "0.8"
),
"headerbar_backdrop_color": "@window_bg_color",
"headerbar_shade_color": self.rgba_from_argb(dark_theme.shadow),
"card_bg_color": self.rgba_from_argb(dark_theme.primary, "0.05"),
"card_fg_color": self.rgba_from_argb(dark_theme.onSecondaryContainer),
"headerbar_shade_color":
self.rgba_from_argb(dark_theme.shadow),
"card_bg_color":
self.rgba_from_argb(dark_theme.primary, "0.05"),
"card_fg_color":
self.rgba_from_argb(dark_theme.onSecondaryContainer),
"card_shade_color": self.rgba_from_argb(dark_theme.shadow),
"dialog_bg_color": self.rgba_from_argb(dark_theme.secondaryContainer),
"dialog_fg_color": self.rgba_from_argb(dark_theme.onSecondaryContainer),
"popover_bg_color": self.rgba_from_argb(dark_theme.secondaryContainer),
"dialog_bg_color":
self.rgba_from_argb(dark_theme.secondaryContainer),
"dialog_fg_color":
self.rgba_from_argb(dark_theme.onSecondaryContainer),
"popover_bg_color":
self.rgba_from_argb(dark_theme.secondaryContainer),
"popover_fg_color": self.rgba_from_argb(
dark_theme.onSecondaryContainer
),
"shade_color": self.rgba_from_argb(dark_theme.shadow),
"scrollbar_outline_color": self.rgba_from_argb(dark_theme.outline),
"scrollbar_outline_color":
self.rgba_from_argb(dark_theme.outline),
}
else: # light
light_theme = theme["schemes"]["light"]
@ -444,31 +459,38 @@ class GradienceApplication(Adw.Application):
"accent_bg_color": self.rgba_from_argb(light_theme.primary),
"accent_fg_color": self.rgba_from_argb(light_theme.onPrimary),
"destructive_color": self.rgba_from_argb(light_theme.error),
"destructive_bg_color": self.rgba_from_argb(light_theme.errorContainer),
"destructive_bg_color":
self.rgba_from_argb(light_theme.errorContainer),
"destructive_fg_color": self.rgba_from_argb(
light_theme.onErrorContainer
),
"success_color": self.rgba_from_argb(light_theme.tertiary),
"success_bg_color": self.rgba_from_argb(light_theme.tertiaryContainer),
"success_bg_color":
self.rgba_from_argb(light_theme.tertiaryContainer),
"success_fg_color": self.rgba_from_argb(
light_theme.onTertiaryContainer
),
"warning_color": self.rgba_from_argb(light_theme.secondary),
"warning_bg_color": self.rgba_from_argb(light_theme.secondaryContainer),
"warning_bg_color":
self.rgba_from_argb(light_theme.secondaryContainer),
"warning_fg_color": self.rgba_from_argb(
light_theme.onSecondaryContainer
),
"error_color": self.rgba_from_argb(light_theme.error),
"error_bg_color": self.rgba_from_argb(light_theme.errorContainer),
"error_bg_color":
self.rgba_from_argb(light_theme.errorContainer),
"error_fg_color": self.rgba_from_argb(light_theme.onError),
"window_bg_color": self.rgba_from_argb(light_theme.secondaryContainer),
"window_bg_color":
self.rgba_from_argb(light_theme.secondaryContainer),
"window_fg_color": self.rgba_from_argb(light_theme.onSurface),
"view_bg_color": self.rgba_from_argb(light_theme.secondaryContainer),
"view_bg_color":
self.rgba_from_argb(light_theme.secondaryContainer),
"view_fg_color": self.rgba_from_argb(light_theme.onSurface),
"headerbar_bg_color": self.rgba_from_argb(
light_theme.secondaryContainer
),
"headerbar_fg_color": self.rgba_from_argb(light_theme.onSurface),
"headerbar_fg_color":
self.rgba_from_argb(light_theme.onSurface),
"headerbar_border_color": self.rgba_from_argb(
light_theme.primary, "0.8"
),
@ -476,19 +498,24 @@ class GradienceApplication(Adw.Application):
"headerbar_shade_color": self.rgba_from_argb(
light_theme.secondaryContainer
),
"card_bg_color": self.rgba_from_argb(light_theme.primary, "0.05"),
"card_fg_color": self.rgba_from_argb(light_theme.onSecondaryContainer),
"card_bg_color":
self.rgba_from_argb(light_theme.primary, "0.05"),
"card_fg_color":
self.rgba_from_argb(light_theme.onSecondaryContainer),
"card_shade_color": self.rgba_from_argb(light_theme.shadow),
"dialog_bg_color": self.rgba_from_argb(light_theme.secondaryContainer),
"dialog_bg_color":
self.rgba_from_argb(light_theme.secondaryContainer),
"dialog_fg_color": self.rgba_from_argb(
light_theme.onSecondaryContainer
),
"popover_bg_color": self.rgba_from_argb(light_theme.secondaryContainer),
"popover_bg_color":
self.rgba_from_argb(light_theme.secondaryContainer),
"popover_fg_color": self.rgba_from_argb(
light_theme.onSecondaryContainer
),
"shade_color": self.rgba_from_argb(light_theme.shadow),
"scrollbar_outline_color": self.rgba_from_argb(light_theme.outline),
"scrollbar_outline_color":
self.rgba_from_argb(light_theme.outline),
}
for key in variable:
@ -586,7 +613,8 @@ class GradienceApplication(Adw.Application):
dialog = GradienceAppTypeDialog(
_("Apply this color scheme?"),
_(
"Warning: any custom CSS files for those app types will be irreversibly overwritten!"
"Warning: any custom CSS files for those app types will be \
irreversibly overwritten!"
),
"apply",
_("Apply"),
@ -626,7 +654,8 @@ class GradienceApplication(Adw.Application):
transient_for=self.props.active_window,
heading=_("Save preset as..."),
body=_(
"Saving preset to <tt>{0}</tt>. If that preset already exists, it will be overwritten!"
"Saving preset to <tt>{0}</tt>. If that preset already \
exists, it will be overwritten!"
).format(
os.path.join(
os.environ.get("XDG_CONFIG_HOME",
@ -653,7 +682,8 @@ class GradienceApplication(Adw.Application):
if len(preset_entry.get_text()) == 0:
dialog.set_body(
_(
"Saving preset to <tt>{0}</tt>. If that preset already exists, it will be overwritten!"
"Saving preset to <tt>{0}</tt>. If that preset \
already exists, it will be overwritten!"
).format(
os.path.join(
os.environ.get(
@ -669,7 +699,8 @@ class GradienceApplication(Adw.Application):
else:
dialog.set_body(
_(
"Saving preset to <tt>{0}</tt>. If that preset already exists, it will be overwritten!"
"Saving preset to <tt>{0}</tt>. If that preset \
already exists, it will be overwritten!"
).format(
os.path.join(
os.environ.get(
@ -696,7 +727,8 @@ class GradienceApplication(Adw.Application):
transient_for=self.props.active_window,
heading=_("You have unsaved changes!"),
body=_(
"Saving preset to <tt>{0}</tt>. If that preset already exists, it will be overwritten!"
"Saving preset to <tt>{0}</tt>. If that preset already \
exists, it will be overwritten!"
).format(
os.path.join(
os.environ.get("XDG_CONFIG_HOME",
@ -726,7 +758,8 @@ class GradienceApplication(Adw.Application):
if len(preset_entry.get_text()) == 0:
dialog.set_body(
_(
"Saving preset to <tt>{0}</tt>. If that preset already exists, it will be overwritten!"
"Saving preset to <tt>{0}</tt>. If that preset \
already exists, it will be overwritten!"
).format(
os.path.join(
os.environ.get(
@ -742,7 +775,8 @@ class GradienceApplication(Adw.Application):
else:
dialog.set_body(
_(
"Saving preset to <tt>{0}</tt>. If that preset already exists, it will be overwritten!"
"Saving preset to <tt>{0}</tt>. If that preset \
already exists, it will be overwritten!"
).format(
os.path.join(
os.environ.get(
@ -795,14 +829,19 @@ class GradienceApplication(Adw.Application):
pass
else:
with open(
os.path.join(gtk4_dir, "gtk.css.bak"), "w", encoding="utf-8"
os.path.join(gtk4_dir, "gtk.css.bak"),
"w",
encoding="utf-8"
) as file:
file.write(contents)
finally:
with open(
os.path.join(gtk4_dir, "gtk.css"), "w", encoding="utf-8"
os.path.join(gtk4_dir, "gtk.css"),
"w",
encoding="utf-8"
) as file:
file.write(gtk4_css)
if widget.get_app_types()["gtk3"]:
gtk3_dir = os.path.join(
os.environ.get("XDG_CONFIG_HOME",
@ -815,21 +854,28 @@ class GradienceApplication(Adw.Application):
contents = ""
try:
with open(
os.path.join(gtk3_dir, "gtk.css"), "r", encoding="utf-8"
os.path.join(gtk3_dir, "gtk.css"),
"r",
encoding="utf-8"
) as file:
contents = file.read()
except FileNotFoundError: # first run
pass
else:
with open(
os.path.join(gtk3_dir, "gtk.css.bak"), "w", encoding="utf-8"
os.path.join(gtk3_dir, "gtk.css.bak"),
"w",
encoding="utf-8"
) as file:
file.write(contents)
finally:
with open(
os.path.join(gtk3_dir, "gtk.css"), "w", encoding="utf-8"
os.path.join(gtk3_dir, "gtk.css"),
"w",
encoding="utf-8"
) as file:
file.write(gtk3_css)
self.reload_plugins()
self.plugins_list.apply()
@ -933,9 +979,9 @@ class GradienceApplication(Adw.Application):
],
artists=["David Lapshin https://github.com/daudix-UFO"],
designers=["David Lapshin https://github.com/daudix-UFO"],
# Translators: This is a place to put your credits (formats: "Name
# https://example.com" or "Name <email@example.com>", no quotes)
# and is not meant to be translated literally.
# Translators: This is a place to put your credits (formats:
# "Name https://example.com" or "Name <email@example.com>",
# no quotes) and is not meant to be translated literally.
# TODO: Automate this process using CI, because not everyone knows
# about this
translator_credits="""Maxime V https://www.transifex.com/user/profile/Adaoh/
@ -956,26 +1002,32 @@ class GradienceApplication(Adw.Application):
release_notes=_(
"""
<ul>
<li>Add AdwViewSwitcher in the header bar.</li>
<li>Move CSS to the "Advanced" tab</li>
<li>Move the rest to the "Colours" tab</li>
<li>Add Monet tab which generates a theme from a background</li>
<li>Add disk saved and disk unsaved icon in the header bar</li>
<li>Update about dialog</li>
<li>Change license to GNU GPLv3</li>
<li>Begin plugin support</li>
<li>Move preset selector to a drop-down called palette (icon palette)</li>
<li>Add ability to apply the theme onlyfor dark theme or oy for light theme</li>
<li>Automaticly use Adwaita-dark preset if the user prefered scheme is dark.</li>
<li>Added Flatpak CI build</li>
<li>Added issue template for bug and feature request </li>
<li>`Main` branch is now protected by GitHub branch protection. The development is done on `next` branch </li>
</ul>
"""
<li>Add AdwViewSwitcher in the header bar.</li>
<li>Move CSS to the "Advanced" tab</li>
<li>Move the rest to the "Colours" tab</li>
<li>Add Monet tab which generates a theme from a background
</li>
<li>Add disk saved and disk unsaved icon in the header bar</li>
<li>Update about dialog</li>
<li>Change license to GNU GPLv3</li>
<li>Begin plugin support</li>
<li>Move preset selector to a drop-down called palette (icon \
palette)</li>
<li>Add ability to apply the theme onlyfor dark theme or oy \
for light theme</li>
<li>Automaticly use Adwaita-dark preset if the user prefered \
scheme is dark.</li>
<li>Added Flatpak CI build</li>
<li>Added issue template for bug and feature request </li>
<li>`Main` branch is now protected by GitHub branch \
protection. The development is done on `next` branch </li>
</ul>
"""
),
comments=_(
"""
Gradience is a tool for customizing Libadwaita applications and the adw-gtk3 theme.
Gradience is a tool for customizing Libadwaita applications and the adw-gtk3 \
theme.
With Gradience you can:
- Change any color of Adwaita theme

View file

@ -22,7 +22,8 @@ configure_file(
'PROJECT_URL': PROJECT_URL,
'BUGTRACKER_URL': BUGTRACKER_URL,
'HELP_URL': HELP_URL,
'TRANSLATE_URL': TRANSLATE_URL
'TRANSLATE_URL': TRANSLATE_URL,
'PKGDATA_DIR': PKGDATA_DIR,
}),
install: true,
install_dir: PY_INSTALLDIR.get_install_dir() / 'gradience',

View file

@ -58,10 +58,16 @@ def get_user_flatpak_path():
return GLib.build_filenamev([userDataDir, "flatpak"])
def user_save_keyfile(toast_overlay, settings, user_keyfile, filename, gtk_ver):
def user_save_keyfile(
toast_overlay,
settings,
user_keyfile,
filename,
gtk_ver
):
try:
user_keyfile.save_to_file(filename)
except Glib.GError as e:
except GLib.GError as e:
toast_overlay.add_toast(Adw.Toast(title=_("Failed to save override")))
buglog(f"Failed to save keyfile structure to override. Exc: {e}")
else:
@ -77,10 +83,16 @@ def user_save_keyfile(toast_overlay, settings, user_keyfile, filename, gtk_ver):
)
def global_save_keyfile(toast_overlay, settings, global_keyfile, filename, gtk_ver):
def global_save_keyfile(
toast_overlay,
settings,
global_keyfile,
filename,
gtk_ver
):
try:
global_keyfile.save_to_file(filename)
except Glib.GError as e:
except GLib.GError as e:
toast_overlay.add_toast(Adw.Toast(title=_("Failed to save override")))
buglog(f"Failed to save keyfile structure to override. Exc: {e}")
else:

View file

@ -22,7 +22,14 @@ class Preset:
name = "new_preset"
badges = {}
def __init__(self, name=None, repo=None, preset_path=None, text=None, preset=None):
def __init__(
self,
name=None,
repo=None,
preset_path=None,
text=None,
preset=None
):
if text: # load from ressource
self.load_preset(text=text)
elif preset: # css or dict

View file

@ -34,7 +34,13 @@ class GradienceOption(Adw.ActionRow):
explanation_button = Gtk.Template.Child("explanation-button")
explanation_label = Gtk.Template.Child("explanation-label")
def __init__(self, name, title, explanation, adw_gtk3_support="yes", **kwargs):
def __init__(self,
name,
title,
explanation,
adw_gtk3_support="yes",
**kwargs
):
super().__init__(**kwargs)
self.set_name(name)
@ -46,7 +52,8 @@ class GradienceOption(Adw.ActionRow):
elif adw_gtk3_support == "partial":
self.warning_button.add_css_class("warning")
self.warning_label.set_label(
_("This option is only partially supported by the adw-gtk3 theme.")
_("This option is only partially supported by the adw-gtk3 \
theme.")
)
elif adw_gtk3_support == "no":
self.warning_button.add_css_class("error")

View file

@ -59,9 +59,8 @@ class GradiencePaletteShades(Adw.ActionRow):
Gtk.Application.get_default().is_ready
and kwargs.get("update_from") == "color_value"
):
Gtk.Application.get_default().palette[self.prefix][str(i)] = shades[
str(i)
]
Gtk.Application.get_default().palette[self.prefix][str(i)] = \
shades[str(i)]
if (
Gtk.Application.get_default().is_ready

View file

@ -46,6 +46,9 @@ class GradiencePluginRow(Adw.ActionRow):
self.plugins_list = plugins_list
self.plugin_object = plugin_object
if not os.path.exists(USER_PLUGIN_DIR / f"{self.plugin_object.plugin_id}.yapsy-plugin"):
self.remove_button.set_visible(False)
self.set_name(plugin_object.plugin_id)
self.set_title(plugin_object.title)
self.set_subtitle("@" + plugin_object.plugin_id)
@ -66,7 +69,19 @@ class GradiencePluginRow(Adw.ActionRow):
USER_PLUGIN_DIR / f"{self.plugin_object.plugin_id}.yapsy-plugin"
)
buglog("remove", plugin_yapsy_file)
os.remove(plugin_yapsy_file)
try:
os.remove(plugin_yapsy_file)
except FileNotFoundError:
error_dialog = Adw.MessageDialog(
#transient_for=self.props.active_window,
heading=_("Unable to remove"),
body=_(
"This is a system plugin, and cannot be removed. "
)
)
error_dialog.add_response("close", _("Close"))
error_dialog.present()
buglog("remove", plugin_yapsy_file)
Gtk.Application.get_default().reload_plugins()
@Gtk.Template.Callback()

View file

@ -34,7 +34,6 @@ SYSTEM_PLUGIN_DIR = os.path.join(
"plugins",
)
class GradiencePluginsList:
"""Represent the plugin group in Advanced"""
@ -95,7 +94,8 @@ class GradiencePluginsList:
group.set_title(_("Plugins"))
group.set_description(
_(
"Plugins add additional features to Gradience, plugins are made by Gradience community and can make issues."
"Plugins add additional features to Gradience, plugins are \
made by Gradience community and can make issues."
)
)
empty = True
@ -106,14 +106,15 @@ class GradiencePluginsList:
empty = False
if empty:
row = Adw.ActionRow()
row.set_title(_("No plugins found"))
row.set_title(_("No plugins found."))
group.add(row)
return group
def save(self):
saved = {}
for pluginInfo in self.pm.getAllPlugins():
saved[pluginInfo.plugin_object.plugin_id] = pluginInfo.plugin_object.save()
saved[pluginInfo.plugin_object.plugin_id] = \
pluginInfo.plugin_object.save()
return saved
def validate(self):

View file

@ -76,6 +76,11 @@ class GradiencePresetRow(Adw.ExpanderRow):
)
)
def on_undo_button_clicked(self, *_args):
buglog("undo")
self.delete_preset = False
self.delete_toast.dismiss()
@Gtk.Template.Callback()
def on_name_entry_changed(self, *_args):
self.name = self.name_entry.get_text()
@ -112,11 +117,13 @@ class GradiencePresetRow(Adw.ExpanderRow):
self.delete_toast = Adw.Toast(title=_("Preset removed"))
self.delete_toast.set_button_label(_("Undo"))
self.delete_toast.connect("dismissed", self.on_delete_toast_dismissed)
self.delete_toast.connect(
"button-clicked",
self.on_undo_button_clicked
)
self.toast_overlay.add_toast(self.delete_toast)
self.win.old_name = self.name
try:
os.rename(
os.path.join(
@ -134,13 +141,13 @@ class GradiencePresetRow(Adw.ExpanderRow):
to_slug_case(self.old_name) + ".json.to_delete",
),
)
print("rename")
self.set_name(self.name + "(" + _("Pending deletion") + ")")
print("renamed")
except Exception as exception:
buglog(exception)
self.delete_preset = True
else:
self.props.visible = False
finally:
self.delete_preset = True
def update_value(self):
self.preset.preset_name = self.name
@ -156,7 +163,9 @@ class GradiencePresetRow(Adw.ExpanderRow):
self.old_name = self.name
def on_delete_toast_dismissed(self, widget):
buglog("dismissed")
if self.delete_preset:
buglog("delete")
try:
os.remove(
os.path.join(
@ -176,6 +185,7 @@ class GradiencePresetRow(Adw.ExpanderRow):
finally:
self.win.reload_pref_group()
else:
buglog("undo")
try:
os.rename(
os.path.join(
@ -202,6 +212,4 @@ class GradiencePresetRow(Adw.ExpanderRow):
self.delete_preset = True
def on_undo_button_clicked(self, *_args):
self.delete_preset = False
self.delete_toast.dismiss()

View file

@ -17,7 +17,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import os
from random import random
from random import choice
import shutil
import json
@ -62,9 +62,10 @@ class GradiencePresetWindow(Adw.Window):
custom_presets = {}
official_repositories = {
_(
"Official"
): "https://github.com/GradienceTeam/Community/raw/next/official.json",
_("Official"):
"https://github.com/GradienceTeam/Community/raw/next/official.json",
_("Curated"):
"https://github.com/GradienceTeam/Community/raw/next/curated.json"
}
search_results_list = []
@ -79,9 +80,6 @@ class GradiencePresetWindow(Adw.Window):
self.settings = parent.settings
self.user_repositories = self.settings.get_value("repos").unpack()
self.user_repositories[
_("Curated")
] = "https://github.com/GradienceTeam/Community/raw/next/curated.json"
self.enabled_repos = self.settings.get_value("enabled-repos").unpack()
self.setup_signals()
@ -130,7 +128,6 @@ class GradiencePresetWindow(Adw.Window):
offline = False
def fetch(repo_name, repo, badge):
global offline
explore_presets, urls = fetch_presets(repo)
if explore_presets:
@ -150,7 +147,7 @@ class GradiencePresetWindow(Adw.Window):
print(self._repos)
for repo_name, repo in self._repos.items():
self.search_string_list.append(repo_name)
badge_color = random.choice(BADGE_COLORS)
badge_color = choice(BADGE_COLORS)
buglog(
f"Selected badge color: {badge_color} if it's look bad, please report it"
)
@ -294,7 +291,8 @@ class GradiencePresetWindow(Adw.Window):
Adw.Toast(title=_("Preset imported")))
else:
self.toast_overlay.add_toast(
Adw.Toast(title=_("Unsupported file format, must be .json"))
Adw.Toast(title=_("Unsupported file format, must be \
.json"))
)
self.reload_pref_group()
@ -332,9 +330,8 @@ class GradiencePresetWindow(Adw.Window):
raise KeyError("variables")
if preset.get("palette") is None:
raise KeyError("palette")
presets_list[file_name.replace(".json", "")] = preset[
"name"
]
presets_list[file_name.replace(".json", "")] = \
preset["name"]
except Exception:
self.toast_overlay.add_toast(
Adw.Toast(title=_("Failed to load preset"))
@ -359,9 +356,8 @@ class GradiencePresetWindow(Adw.Window):
raise KeyError("variables")
if preset.get("palette") is None:
raise KeyError("palette")
presets_list["user"][file_name.replace(".json", "")] = preset[
"name"
]
presets_list["user"][file_name.replace(".json", "")] \
= preset["name"]
except Exception:
self.toast_overlay.add_toast(
Adw.Toast(title=_("Failed to load preset"))
@ -383,7 +379,9 @@ class GradiencePresetWindow(Adw.Window):
self.preset_list.set_title(_("User Presets"))
self.preset_list.set_description(
_(
'See <a href="https://github.com/GradienceTeam/Community">GradienceTeam/Community</a> on Github for more presets'
'See \
<a href="https://github.com/GradienceTeam/Community">GradienceTeam/Community</a> \
on Github for more presets'
)
)
@ -408,7 +406,8 @@ class GradiencePresetWindow(Adw.Window):
self.preset_empty = Adw.ActionRow()
self.preset_empty.set_title(
_(
"No preset found! Use the import button to import one or search one on the Explore tab"
"No preset found! Use the import button to import one or \
search one on the Explore tab"
)
)
self.preset_list.add(self.preset_empty)
@ -420,12 +419,12 @@ class GradiencePresetWindow(Adw.Window):
self.repos_list = Adw.PreferencesGroup()
self.repos_list.set_title(_("Repositories"))
self.add_repo_button = Gtk.Button.new_from_icon_name(
"list-add-symbolic")
self.add_repo_button.connect(
"clicked", self.on_add_repo_button_clicked)
# self.add_repo_button = Gtk.Button.new_from_icon_name(
# "list-add-symbolic")
# self.add_repo_button.connect(
# "clicked", self.on_add_repo_button_clicked)
self.repos_list.set_header_suffix(self.add_repo_button)
# self.repos_list.set_header_suffix(self.add_repo_button)
for repo_name, repo in self.official_repositories.items():
row = GradienceRepoRow(repo, repo_name, self, deletable=False)

View file

@ -18,7 +18,13 @@
class GradienceSetting:
def __init__(self, name, title, value_type, explanation=None, default_value=None):
def __init__(self,
name,
title,
value_type,
explanation=None,
default_value=None
):
# TODO supported types:
# text
# integer

View file

@ -22,14 +22,19 @@ settings_schema = {
"name": "accent_colors",
"title": _("Accent Colors"),
"description": _(
"These colors are used across many different widgets, such as buttons, labels, and entries, to indicate that a widget is important, interactive, or currently active."
"These colors are used across many different widgets, such as \
buttons, labels, and entries, to indicate that a widget is \
important, interactive, or currently active."
),
"variables": [
{
"name": "accent_color",
"title": _("Standalone Color"),
"explanation": _(
"The standalone colors are similar to the background ones, but provide better contrast when used as foreground color on top of a neutral background - for example, colorful text in a window."
"The standalone colors are similar to the background \
ones, but provide better contrast when used as \
foreground color on top of a neutral background - for \
example, colorful text in a window."
),
"adw_gtk3_support": "yes",
},
@ -49,14 +54,18 @@ settings_schema = {
"name": "destructive_colors",
"title": _("Destructive Colors"),
"description": _(
"These colors are used for buttons to indicate a dangerous action, such as deleting a file."
"These colors are used for buttons to indicate a dangerous \
action, such as deleting a file."
),
"variables": [
{
"name": "destructive_color",
"title": _("Standalone Color"),
"explanation": _(
"The standalone colors are similar to the background ones, but provide better contrast when used as foreground color on top of a neutral background - for example, colorful text in a window."
"The standalone colors are similar to the background \
ones, but provide better contrast when used as \
foreground color on top of a neutral background - for \
example, colorful text in a window."
),
"adw_gtk3_support": "yes",
},
@ -76,14 +85,19 @@ settings_schema = {
"name": "success_colors",
"title": _("Success Colors"),
"description": _(
"These colors are used across many different widgets, such as buttons, labels, entries, and level bars, to indicate a success or a high level."
"These colors are used across many different widgets, such as \
buttons, labels, entries, and level bars, to indicate a \
success or a high level."
),
"variables": [
{
"name": "success_color",
"title": _("Standalone Color"),
"explanation": _(
"The standalone colors are similar to the background ones, but provide better contrast when used as foreground color on top of a neutral background - for example, colorful text in a window."
"The standalone colors are similar to the background \
ones, but provide better contrast when used as \
foreground color on top of a neutral background - for \
example, colorful text in a window."
),
"adw_gtk3_support": "yes",
},
@ -103,14 +117,19 @@ settings_schema = {
"name": "warning_colors",
"title": _("Warning Colors"),
"description": _(
"These colors are used across many different widgets, such as buttons, labels, entries, and level bars, to indicate a warning or a low level."
"These colors are used across many different widgets, such as \
buttons, labels, entries, and level bars, to indicate a \
warning or a low level."
),
"variables": [
{
"name": "warning_color",
"title": _("Standalone Color"),
"explanation": _(
"The standalone colors are similar to the background ones, but provide better contrast when used as foreground color on top of a neutral background - for example, colorful text in a window."
"The standalone colors are similar to the background \
ones, but provide better contrast when used as \
foreground color on top of a neutral background - for \
example, colorful text in a window."
),
"adw_gtk3_support": "yes",
},
@ -130,14 +149,18 @@ settings_schema = {
"name": "error_colors",
"title": _("Error Colors"),
"description": _(
"These colors are used across many different widgets, such as buttons, labels, and entries, to indicate a failure."
"These colors are used across many different widgets, such as \
buttons, labels, and entries, to indicate a failure."
),
"variables": [
{
"name": "error_color",
"title": _("Standalone Color"),
"explanation": _(
"The standalone colors are similar to the background ones, but provide better contrast when used as foreground color on top of a neutral background - for example, colorful text in a window."
"The standalone colors are similar to the background \
ones, but provide better contrast when used as \
foreground color on top of a neutral background - for \
example, colorful text in a window."
),
"adw_gtk3_support": "yes",
},
@ -174,7 +197,8 @@ settings_schema = {
"name": "view_colors",
"title": _("View Colors"),
"description": _(
"These colors are used in a variety of widgets, such as text views and entries."
"These colors are used in a variety of widgets, such as text \
views and entries."
),
"variables": [
{
@ -193,7 +217,9 @@ settings_schema = {
"name": "headerbar_colors",
"title": _("Header Bar Colors"),
"description": _(
"These colors are used for header bars, as well as widgets that are meant to be visually attached to it, such as search bars or tab bars."
"These colors are used for header bars, as well as widgets \
that are meant to be visually attached to it, such as search \
bars or tab bars."
),
"variables": [
{
@ -210,7 +236,14 @@ settings_schema = {
"name": "headerbar_border_color",
"title": _("Border Color"),
"explanation": _(
"The border color has the same default value as a foreground color, but doesn't change along with it. This can be useful if a light window has a dark header bar with light text; in this case it may be desirable to keep the border dark. This variable is only used for vertical borders - for example, separators between the two header bars in a split header bar layout."
"The border color has the same default value as a \
foreground color, but doesn't change along with it. \
This can be useful if a light window has a dark \
header bar with light text; in this case it may be \
desirable to keep the border dark. This variable is \
only used for vertical borders - for example, \
separators between the two header bars in a split \
header bar layout."
),
"adw_gtk3_support": "no",
},
@ -218,7 +251,12 @@ settings_schema = {
"name": "headerbar_backdrop_color",
"title": _("Backdrop Color"),
"explanation": _(
"The backdrop color is used instead of the background color when the window is not focused. By default it's an alias of the window's background color and changes together with it. When changing this variable, make sure to set it to a value matching your header bar background color."
"The backdrop color is used instead of the background \
color when the window is not focused. By default it's \
an alias of the window's background color and changes \
together with it. When changing this variable, make \
sure to set it to a value matching your header bar \
background color."
),
"adw_gtk3_support": "yes",
},
@ -226,7 +264,9 @@ settings_schema = {
"name": "headerbar_shade_color",
"title": _("Shade Color"),
"explanation": _(
"The shade color is used to provide a dark border for header bars and similar widgets that separates them from the main window."
"The shade color is used to provide a dark border for \
header bars and similar widgets that separates them \
from the main window."
),
"adw_gtk3_support": "no",
},
@ -235,7 +275,8 @@ settings_schema = {
{
"name": "card_colors",
"title": _("Card Colors"),
"description": _("These colors are used for cards and boxed lists."),
"description": _("These colors are used for cards and boxed \
lists."),
"variables": [
{
"name": "card_bg_color",
@ -251,7 +292,9 @@ settings_schema = {
"name": "card_shade_color",
"title": _("Shade Color"),
"explanation": _(
"The shade color is used for shadows that are used by cards to separate themselves from the window background, as well as for row dividers in the cards."
"The shade color is used for shadows that are used by \
cards to separate themselves from the window \
background, as well as for row dividers in the cards."
),
"adw_gtk3_support": "no",
},
@ -300,7 +343,9 @@ settings_schema = {
"name": "shade_color",
"title": _("Shade Color"),
"explanation": _(
"The shade color is used by inline tab bars, as well as the transitions in leaflets and flaps, and info bar borders."
"The shade color is used by inline tab bars, as well \
as the transitions in leaflets and flaps, and info \
bar borders."
),
"adw_gtk3_support": "no",
},
@ -308,7 +353,9 @@ settings_schema = {
"name": "scrollbar_outline_color",
"title": _("Scrollbar Outline Color"),
"explanation": _(
"The scrollbar outline color is used by scrollbars to ensure that overlay scrollbars are visible regardless of the content color."
"The scrollbar outline color is used by scrollbars to \
ensure that overlay scrollbars are visible regardless \
of the content color."
),
"adw_gtk3_support": "no",
},

View file

@ -50,23 +50,28 @@ class GradienceWelcomeWindow(Adw.Window):
img_welcome = Gtk.Template.Child()
label_skip = Gtk.Template.Child()
carousel_pages = [
"welcome",
"release",
"agreement",
"gradience",
"configure",
"download",
"finish",
]
images = [
f"{rootdir}/images/welcome.svg",
f"{rootdir}/images/welcome-dark.svg",
]
def __init__(self, window, **kwargs) -> None:
carousel_pages = [
"welcome", # 0
"release", # 1
"agreement", # 2
"gradience", # 3
"configure", # 4
"download", # 5
"finish", # 6
]
page_welcome = Gtk.Template.Child()
page_release = Gtk.Template.Child()
def __init__(self, window, update=False, **kwargs) -> None:
super().__init__(**kwargs)
self.set_transient_for(window)
self.update = update
# common variables and references
self.window = window
@ -85,6 +90,11 @@ class GradienceWelcomeWindow(Adw.Window):
"notify::gtk-application-prefer-dark-theme", self.theme_changed
)
if self.update:
self.page_welcome.set_title("Thanks for updating Gradience!")
self.page_release.set_title(f"Gradience {version}")
self.btn_close.set_sensitive(False)
if self.settings.get_property("gtk-application-prefer-dark-theme"):
@ -131,7 +141,15 @@ class GradienceWelcomeWindow(Adw.Window):
self.btn_back.set_visible(False)
self.btn_next.set_visible(True)
self.btn_install.set_visible(False)
self.next_page()
if self.update:
self.window.last_opened_version = \
self.window.settings.set_string("last-opened-version", version)
self.btn_close.set_sensitive(True)
self.label_skip.set_visible(False)
self.next_page(index=5)
else:
self.next_page()
@staticmethod
def quit(widget=False):
@ -187,13 +205,15 @@ class GradienceWelcomeWindow(Adw.Window):
callback=set_completed,
)
def previous_page(self, widget=False):
index = int(self.carousel.get_position())
def previous_page(self, widget=False, index=None):
if index is None:
index = int(self.carousel.get_position())
previous_page = self.carousel.get_nth_page(index - 1)
self.carousel.scroll_to(previous_page, True)
def next_page(self, widget=False):
index = int(self.carousel.get_position())
def next_page(self, widget=False, index=None):
if index is None:
index = int(self.carousel.get_position())
next_page = self.carousel.get_nth_page(index + 1)
self.carousel.scroll_to(next_page, True)

View file

@ -132,7 +132,8 @@ class GradienceMainWindow(Adw.ApplicationWindow):
self.monet_pref_group.set_title(_("Monet Engine"))
self.monet_pref_group.set_description(
_(
"Monet is an engine that generates a Material Design 3 palette from an image's color."
"Monet is an engine that generates a Material Design 3 \
palette from an image's color."
)
)
@ -268,7 +269,10 @@ class GradienceMainWindow(Adw.ApplicationWindow):
palette_pref_group.set_title(_("Palette Colors"))
palette_pref_group.set_description(
_(
'Named palette colors used by some applications. Default colors follow the <a href="https://developer.gnome.org/hig/reference/palette.html">GNOME Human Interface Guidelines</a>.'
'Named palette colors used by some applications. Default \
colors follow the \
<a href="https://developer.gnome.org/hig/reference/palette.html">\
GNOME Human Interface Guidelines</a>.'
)
)
for color in settings_schema["palette"]: