Skip to content
Commits on Source (90)
40.0
=======
Stable release.
GNOME Tweaks 40 brings several bug fixes, support for GNOME 40 settings
changes, and removes GNOME Shell Extensions support. Extensions support
can now be found in the GNOME Extensions app.
The stable release includes a fix causing some installations to not detect
GNOME Shell.
Contributors:
Thanks to mkrajnak for diagnosing a bug in the beta release!
Translations:
Thank you to all the translators who've contributed to Tweaks!
In chronological order since the last stable release:
Bruce Cowan, Hannie Dumoleyn, Seong-ho Cho, Baurzhan Muftakhidinov,
Asier Sarasua Garmendia, Goran Vidović, Fran Dieguez, emintufan,
Danial Behzadi, Balázs Meskó, Boyuan Yang, Марко М. Костић (Marko M. Kostić),
Kjartan Maraas, Jordi Mas, Aurimas Černius, Janvitus, Matej Urbančič,
sicklylife, Fabio Tomat, Marek Černocký, Enrico Nicoletto, Florentina Mușat,
Juliano de Souza Camargo, Piotr Drąg, Guillaume Bernard, Andika Triwidada,
Trần Ngọc Quân, Yuri Chornoivan, Daniel Mustieles García, Cheng-Chia Tseng,
Dingzhong Chen (FeralMeow), Yosef Or Boczko, Jwtiyar Neriman, Daniel Korostil,
Andre Klapper, Zander Brown, Sabri Ünal, Dušan Kazik, Charles Monzat,
Umarzuki Mochlis, Tim Sabsch, Yi-Jyun Pan, Ask Hjorth Larsen
40.beta
=======
Beta release.
GNOME Tweaks 40 brings several bug fixes, support for GNOME 40 settings
changes, and removes GNOME Shell Extensions support. Extensions support
can now be found in the GNOME Extensions app.
* Show the back button when leaflet is folded (Çağatay Yiğit Şahin)
* 'Additional Layout Options' sorted alphabetically (Lukáš Kotek)
* Update to new gsettings-desktop-schemas location (Carlos Garnacho)
* Add Flatpak manifest (Jordan Petridis)
* Drop unused extensions support (Florian Müllner)
* Port to libhandy 1.0 (Xi Ruoyao)
* Use Gtk.HeaderBar instead of Handy.HeaderBar. (Evan Welsh)
* Adapt flatpak manifest to new dependencies. (Evan Welsh)
* Fix margins in startup application selector. (Evan Welsh)
* Add notice for Extensions removal. (Evan Welsh)
* Fix default window sizing and requests. (Evan Welsh)
Contributors:
Evan Welsh, Xi Ruoyao, Florian Müllner, Jordan Petridis, Carlos Garnacho,
Lukáš Kotek, Çağatay Yiğit Şahin
Translations:
Due to the long release gap I'll be compiling all the translation credits
for the stable release.
3.34.0
=======
Stable release.
......
......@@ -21,7 +21,7 @@ RUNTIME DEPENDENCIES
- GLib (>= 2.58)
- GTK+ 3 (>= 3.12)
- gnome-desktop (>= 3.30)
- libhandy
- libhandy (>= 1.0)
- libsoup
- libnotify
- Pango
......
......@@ -23,6 +23,12 @@ gui_data = [
]
install_data(gui_data, install_dir: pkgdatadir)
# Install the settings schema file
install_data(
'org.gnome.tweaks.gschema.xml',
install_dir: 'share/glib-2.0/schemas'
)
install_data ('org.gnome.tweaks.svg',
install_dir: join_paths(datadir, 'icons', 'hicolor', 'scalable', 'apps'))
install_data ('org.gnome.tweaks-symbolic.svg',
......
......@@ -17,6 +17,31 @@
manage startup applications, and enable desktop icons among other settings.
</p>
</description>
<releases>
<release version="40.0" date="2021-03-27">
<p>
First stable release for GNOME 40. This version is mainly comprised
of bug fixes and changes for GNOME 40. There are also some notable
improvements:
</p>
<ul>
<li>Remove Extensions support (now found in the Extensions app)</li>
<li>Support new font settings for GNOME 40</li>
<li>Fix UI on small screens</li>
<li>Update translations</li>
</ul>
</release>
<release version="40.beta" date="2021-02-13">
<ul>
<li>Remove Extensions support</li>
<li>Add Flatpak support</li>
<li>Support new font settings</li>
<li>Fix leaflet on small screens</li>
<li>Various other bug fixes</li>
<li>Update translations</li>
</ul>
</release>
</releases>
<provides>
<id>gnome-tweak-tool.desktop</id>
</provides>
......
<?xml version="1.0" encoding="UTF-8"?>
<schemalist>
<schema path="/org/gnome/tweaks/" id="org.gnome.tweaks" gettext-domain="org.gnome.tweaks">
<key type="b" name="show-extensions-notice">
<default>true</default>
<summary>Show Extensions Notice</summary>
<description>When first installed the user should be notified that extensions support has moved to a dedicated app, Extensions.</description>
</key>
</schema>
</schemalist>
......@@ -7,10 +7,6 @@
<attribute name="label" translatable="yes">_Reset to Defaults</attribute>
<attribute name="action">app.reset</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Disable All Shell Extensions</attribute>
<attribute name="action">app.disable_extension</attribute>
</item>
</section>
<section>
<item>
......
......@@ -12,7 +12,7 @@ import sys
import gi
gi.require_version("Gtk", "3.0")
gi.require_version("Handy", "0.0")
gi.require_version("Handy", "1")
import gtweak
from gtweak.defs import VERSION
......
......@@ -23,4 +23,11 @@ management behavior, GNOME Shell appearance and extension, etc.</description>
<gnome:userid>jbicha</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>
<foaf:Person>
<foaf:name>Evan Welsh</foaf:name>
<foaf:mbox rdf:resource="mailto:contact@evanwelsh.com" />
<gnome:userid>ewlsh</gnome:userid>
</foaf:Person>
</maintainer>
</Project>
......@@ -14,8 +14,27 @@ from gtweak.tweakmodel import TweakModel
from gtweak.tweakview import Window
from gtweak.utils import SchemaList
from gtweak.gshellwrapper import GnomeShellFactory
from gtweak.utils import DisableExtension
class ExtensionNotice(Gtk.MessageDialog):
def __init__(self, modal, transient_for):
Gtk.Dialog.__init__(self, modal=modal, transient_for=transient_for)
self.add_button(_("_Continue"), Gtk.ResponseType.NONE)
self.set_markup("<b>{0}</b>".format(_("Extensions Has Moved")))
self.format_secondary_markup(
"{0}\n\n{1}".format(
# Translators: Placeholder will be replaced with "GNOME Extensions" in active link form
_("Extensions management has been moved to {0}.").format(
'<a href="https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/master/subprojects/extensions-app/README.md">GNOME Extensions</a>',
),
# Translators: Placeholder will be replaced with "Flathub" in active link form
_("We recommend downloading GNOME Extensions from {0} if your distribution does not include it.").format(
'<a href="https://flathub.org/apps/details/org.gnome.Extensions">Flathub</a>'
)
)
)
class GnomeTweaks(Gtk.Application):
......@@ -24,14 +43,21 @@ class GnomeTweaks(Gtk.Application):
Gtk.Application.__init__(self, application_id="org.gnome.tweaks")
self.win = None
self._settings = Gio.Settings.new('org.gnome.tweaks')
def do_activate(self):
if not self.win:
model = TweakModel()
self.win = Window(self, model)
self.win.show_all()
if not self.win.get_titlebar().props.folded:
self.win.back_button.props.visible = False
self.win.present()
if self._settings.get_boolean('show-extensions-notice'):
self.show_extensions_notice()
self._settings.set_boolean('show-extensions-notice', False)
def do_startup(self):
Gtk.Application.do_startup(self)
......@@ -39,10 +65,6 @@ class GnomeTweaks(Gtk.Application):
reset_action.connect("activate", self.reset_cb)
self.add_action(reset_action)
disable_extension_action = Gio.SimpleAction.new("disable_extension", None)
disable_extension_action.connect("activate", self.disable_cb)
self.add_action(disable_extension_action)
help_action = Gio.SimpleAction.new("help", None)
help_action.connect("activate", self.help_cb)
self.add_action(help_action)
......@@ -64,10 +86,6 @@ class GnomeTweaks(Gtk.Application):
def help_cb(self, action, parameter):
print("This does nothing. It is only a demonstration.")
def disable_cb(self, action, parameter):
ds = DisableExtension()
ds.disable()
def about_cb(self, action, parameter):
aboutdialog = Gtk.AboutDialog(modal=True, transient_for=self.win)
aboutdialog.set_program_name(aboutdialog.get_program_name() + " %s" % VERSION)
......@@ -102,3 +120,12 @@ class GnomeTweaks(Gtk.Application):
def quit_cb(self, action, parameter):
self.quit()
def show_extensions_notice(self):
extensionsdialog = ExtensionNotice(
modal=True,
transient_for=self.win
)
extensionsdialog.run()
extensionsdialog.destroy()
# Copyright (c) 2011 John Stowers
# SPDX-License-Identifier: GPL-3.0+
# License-Filename: LICENSES/GPL-3.0
import json
import logging
import gi
gi.require_version("Soup", "2.4")
from gi.repository import GObject
from gi.repository import Soup
class ExtensionsDotGnomeDotOrg(GObject.GObject):
__gsignals__ = {
"got-extensions": (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE,
(GObject.TYPE_PYOBJECT,)),
"got-extension-info": (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE,
(GObject.TYPE_PYOBJECT, GObject.TYPE_STRING)),
}
def __init__(self, shell_version_tuple):
GObject.GObject.__init__(self)
self._session = Soup.Session.new()
self._shell_version_tuple = shell_version_tuple
self._extensions = {}
def _query_extensions_finished(self, msg, url):
if msg.status_code == 200:
# server returns a list of extensions which may contain duplicates, dont know
resp = json.loads(msg.response_body.data)
print(resp)
for e in resp["extensions"]:
self._extensions[e["uuid"]] = e
self.emit("got-extensions", self._extensions)
def _query_extension_info_finished(self, msg, uuid):
if msg.status_code == 200:
self.emit("got-extension-info", json.loads(msg.response_body.data), uuid)
def query_extensions(self):
url = "https://extensions.gnome.org/extension-query/?"
ver = self._shell_version_tuple
if ver[1] % 2:
# if this is a development version (odd) then query the full version
url += "shell_version=%d.%d.%d&" % ver
else:
# else query in point releases up to the current version
# and filter duplicates from the reply
url += "shell_version=%d.%d&" % (ver[0], ver[1])
for i in range(1, ver[2]+1):
url += "shell_version=%d.%d.%d&" % (ver[0], ver[1], i)
# non-paginated
url += "n_per_page=-1"
logging.debug("Query URL: %s" % url)
message = Soup.Message.new('GET', url)
message.connect("finished", self._query_extensions_finished, url)
self._session.queue_message(message, None, None)
def query_extension_info(self, extension_uuid):
if extension_uuid in self._extensions:
print("CACHED")
self.emit("got-extension-info", self._extensions[extension_uuid])
return
url = "https://extensions.gnome.org/extension-info/?uuid=%s" % extension_uuid
logging.debug("Query URL: %s" % url)
message = Soup.Message.new('GET', url)
message.connect("finished", self._query_extension_info_finished, extension_uuid)
self._session.queue_message(message, None, None)
def get_download_url(self, extinfo):
url = "https://extensions.gnome.org/download-extension/%s.shell-extension.zip?version_tag=%d"
# version tag is the pk in the shell_version_map
# url = url % (extinfo["uuid"],
if __name__ == "__main__":
import pprint
from gi.repository import Gtk
def _got_ext(ego, extensions):
print("="*80)
pprint.pprint(list(extensions.values()))
def _got_ext_info(ego, extension):
pprint.pprint(extension)
logging.basicConfig(format="%(levelname)-8s: %(message)s", level=logging.DEBUG)
e = ExtensionsDotGnomeDotOrg((3, 4, 1))
e.connect("got-extensions", _got_ext)
e.connect("got-extension-info", _got_ext_info)
e.query_extensions()
# e.query_extensions((3, 4, 0))
# e.query_extensions((3, 3, 2))
e.query_extension_info("user-theme@gnome-shell-extensions.gcampax.github.com")
Gtk.main()
......@@ -102,6 +102,9 @@ class GSettingsFakeSetting:
def get_string(self, *args, **kwargs):
return ""
def __getitem__(self, key):
return ""
def __getattr__(self, name):
def noop(*args, **kwargs):
pass
......
......@@ -24,7 +24,6 @@ class _ShellProxy:
'org.gnome.Shell',
None)
# GNOME Shell > 3.5 added a separate extension interface
self.proxy_extensions = Gio.DBusProxy.new_sync(
d, 0, None,
'org.gnome.Shell',
......@@ -32,31 +31,19 @@ class _ShellProxy:
'org.gnome.Shell.Extensions',
None)
# GNOME Shell > 3.7.2 added the Mode to the DBus API
val = self.proxy.get_cached_property("Mode")
if val is not None:
self._mode = val.unpack()
else:
js = 'global.session_mode'
result, output = self.proxy.Eval('(s)', js)
if result and output:
self._mode = json.loads(output)
else:
logging.warning("Error getting shell mode via Eval JS")
self._mode = "user"
# GNOME Shell > 3.3 added the Version to the DBus API and disabled execute_js
logging.warning("Error getting shell mode")
self._mode = "user"
val = self.proxy.get_cached_property("ShellVersion")
if val is not None:
self._version = val.unpack()
else:
js = 'const Config = imports.misc.config; Config.PACKAGE_VERSION'
result, output = self.proxy.Eval('(s)', js)
if result and output:
self._version = json.loads(output)
else:
logging.critical("Error getting shell version via Eval JS")
self._version = "0.0.0"
logging.critical("Error getting shell version")
self._version = "0.0.0"
@property
def mode(self):
......@@ -69,23 +56,6 @@ class _ShellProxy:
class GnomeShell:
EXTENSION_STATE = {
"ENABLED" : 1,
"DISABLED" : 2,
"ERROR" : 3,
"OUT_OF_DATE" : 4,
"DOWNLOADING" : 5,
"INITIALIZED" : 6,
}
EXTENSION_TYPE = {
"SYSTEM" : 1,
"PER_USER" : 2
}
DATA_DIR = os.path.join(GLib.get_user_data_dir(), "gnome-shell")
EXTENSION_DIR = os.path.join(GLib.get_user_data_dir(), "gnome-shell", "extensions")
def __init__(self, shellproxy, shellsettings):
self._proxy = shellproxy
self._settings = shellsettings
......@@ -102,8 +72,8 @@ class GnomeShell:
def reload_theme(self):
self._execute_js('const Main = imports.ui.main; Main.loadTheme();')
def uninstall_extension(self, uuid):
pass
def list_extensions(self):
return self._proxy.proxy_extensions.ListExtensions()
@property
def mode(self):
......@@ -114,72 +84,16 @@ class GnomeShell:
return self._proxy.version
class GnomeShell32(GnomeShell):
EXTENSION_ENABLED_KEY = "enabled-extensions"
SUPPORTS_EXTENSION_PREFS = False
def list_extensions(self):
return self._proxy.proxy.ListExtensions()
def extension_is_active(self, state, uuid):
return state == GnomeShell.EXTENSION_STATE["ENABLED"] and \
self._settings.setting_is_in_list(self.EXTENSION_ENABLED_KEY, uuid)
def enable_extension(self, uuid):
self._settings.setting_add_to_list(self.EXTENSION_ENABLED_KEY, uuid)
def disable_extension(self, uuid):
self._settings.setting_remove_from_list(self.EXTENSION_ENABLED_KEY, uuid)
class GnomeShell34(GnomeShell32):
SUPPORTS_EXTENSION_PREFS = True
def restart(self):
logging.warning("Restarting Shell Not Supported")
def reload_theme(self):
logging.warning("Reloading Theme Not Supported")
def uninstall_extension(self, uuid):
return self._proxy.proxy.UninstallExtension('(s)', uuid)
class GnomeShell36(GnomeShell34):
def list_extensions(self):
return self._proxy.proxy_extensions.ListExtensions()
def uninstall_extension(self, uuid):
return self._proxy.proxy_extensions.UninstallExtension('(s)', uuid)
def install_remote_extension(self, uuid, reply_handler, error_handler, user_data):
self._proxy.proxy_extensions.InstallRemoteExtension('(s)', uuid,
result_handler=reply_handler, error_handler=error_handler, user_data=user_data)
@gtweak.utils.singleton
class GnomeShellFactory:
def __init__(self):
try:
proxy = _ShellProxy()
settings = GSettingsSetting("org.gnome.shell")
v = list(map(int, proxy.version.split(".")))
if v >= [3, 5, 0]:
self.shell = GnomeShell36(proxy, settings)
elif v >= [3, 3, 2]:
self.shell = GnomeShell34(proxy, settings)
elif v >= [3, 1, 4]:
self.shell = GnomeShell32(proxy, settings)
else:
logging.warn("Shell version not supported")
self.shell = None
self.shell = GnomeShell(proxy, settings)
logging.debug("Shell version: %s", str(v))
logging.debug("Shell version: %s", str(proxy.version))
except:
self.shell = None
logging.warn("Shell not installed or running")
......
......@@ -11,13 +11,11 @@ configure_file(
input: 'defs.py.in',
output: 'defs.py',
configuration: defs_conf,
install: true,
install_dir: gtweakdir
)
shell_sources = [
'app.py',
'egowrapper.py',
'gsettings.py',
'gshellwrapper.py',
'gtksettings.py',
......@@ -35,7 +33,6 @@ tweak_sources = [
'tweaks/tweak_group_font.py',
'tweaks/tweak_group_general.py',
'tweaks/tweak_group_keymouse.py',
'tweaks/tweak_group_shell_extensions.py',
'tweaks/tweak_group_startup.py',
'tweaks/tweak_group_test.py',
'tweaks/tweak_group_title_bar.py',
......@@ -46,5 +43,5 @@ tweak_sources = [
'tweaks/tweak_wacom.py',
]
install_data(shell_sources, install_dir: gtweakdir)
install_data(tweak_sources, install_dir: gtweakdir + '/tweaks')
python3.install_sources(shell_sources, subdir: 'gtweak')
python3.install_sources(tweak_sources, subdir: join_paths('gtweak', 'tweaks'))
......@@ -2,19 +2,28 @@
# SPDX-License-Identifier: GPL-3.0+
# License-Filename: LICENSES/GPL-3.0
import logging
from gi.repository import Gio, Gtk
from gtweak.tweakmodel import Tweak
from gtweak.widgets import ListBoxTweakGroup, GSettingsSpinButtonTweak, GSettingsFontButtonTweak
from gtweak.gsettings import GSettingsSetting
class FontXSettingsTweak(Gtk.Box, Tweak):
def __init__(self, **options):
Gtk.Box.__init__(self)
Tweak.__init__(self, _("Hinting"), _("Antialiasing"))
self.settings = Gio.Settings("org.gnome.settings-daemon.plugins.xsettings")
try:
self.settings = GSettingsSetting("org.gnome.desktop.interface")
except:
self.settings = None
logging.warn("org.gnome.desktop.interface not installed or running")
if not self.settings:
return
self.set_spacing(12)
self.props.margin_top = 12
......@@ -29,25 +38,25 @@ class FontXSettingsTweak(Gtk.Box, Tweak):
self.btn_full = Gtk.RadioButton.new_from_widget(None)
self.btn_full.set_label(_("Full"))
self.btn_full.set_active(self.settings["hinting"] == "full")
self.btn_full.set_active(self.settings["font-hinting"] == "full")
self.btn_full.connect("toggled", self.on_hint_button_toggled)
hint_box.pack_start(self.btn_full, False, False, 0)
self.btn_med = Gtk.RadioButton.new_from_widget(self.btn_full)
self.btn_med.set_label(_("Medium"))
self.btn_med.set_active(self.settings["hinting"] == "medium")
self.btn_med.set_active(self.settings["font-hinting"] == "medium")
self.btn_med.connect("toggled", self.on_hint_button_toggled)
hint_box.pack_start(self.btn_med, False, False, 0)
self.btn_slight = Gtk.RadioButton.new_from_widget(self.btn_full)
self.btn_slight.set_label(_("Slight"))
self.btn_slight.set_active(self.settings["hinting"] == "slight")
self.btn_slight.set_active(self.settings["font-hinting"] == "slight")
self.btn_slight.connect("toggled", self.on_hint_button_toggled)
hint_box.pack_start(self.btn_slight, False, False, 0)
self.btn_hnone = Gtk.RadioButton.new_from_widget(self.btn_full)
self.btn_hnone.set_label(_("None"))
self.btn_hnone.set_active(self.settings["hinting"] == "none")
self.btn_hnone.set_active(self.settings["font-hinting"] == "none")
self.btn_hnone.connect("toggled", self.on_hint_button_toggled)
hint_box.pack_start(self.btn_hnone, False, False, 0)
......@@ -60,40 +69,40 @@ class FontXSettingsTweak(Gtk.Box, Tweak):
self.btn_rgba = Gtk.RadioButton.new_from_widget(None)
self.btn_rgba.set_label(_("Subpixel (for LCD screens)"))
self.btn_rgba.set_active(self.settings["antialiasing"] == "rgba")
self.btn_rgba.set_active(self.settings["font-antialiasing"] == "rgba")
self.btn_rgba.connect("toggled", self.on_aa_button_toggled)
aa_box.pack_start(self.btn_rgba, False, False, 0)
self.btn_gray = Gtk.RadioButton.new_from_widget(self.btn_rgba)
self.btn_gray.set_label(_("Standard (grayscale)"))
self.btn_gray.set_active(self.settings["antialiasing"] == "grayscale")
self.btn_gray.set_active(self.settings["font-antialiasing"] == "grayscale")
self.btn_gray.connect("toggled", self.on_aa_button_toggled)
aa_box.pack_start(self.btn_gray, False, False, 0)
self.btn_anone = Gtk.RadioButton.new_from_widget(self.btn_rgba)
self.btn_anone.set_label(_("None"))
self.btn_anone.set_active(self.settings["antialiasing"] == "none")
self.btn_anone.set_active(self.settings["font-antialiasing"] == "none")
self.btn_anone.connect("toggled", self.on_aa_button_toggled)
aa_box.pack_start(self.btn_anone, False, False, 0)
def on_hint_button_toggled(self, button):
if self.btn_full.get_active():
self.settings["hinting"] ="full"
self.settings["font-hinting"] ="full"
elif self.btn_med.get_active():
self.settings["hinting"] = "medium"
self.settings["font-hinting"] = "medium"
elif self.btn_slight.get_active():
self.settings["hinting"] = "slight"
self.settings["font-hinting"] = "slight"
else:
print("none")
self.settings["hinting"] = "none"
self.settings["font-hinting"] = "none"
def on_aa_button_toggled(self, button):
if self.btn_rgba.get_active():
self.settings["antialiasing"] = "rgba"
self.settings["font-antialiasing"] = "rgba"
elif self.btn_gray.get_active():
self.settings["antialiasing"] = "grayscale"
self.settings["font-antialiasing"] = "grayscale"
else:
self.settings["antialiasing"] = "none"
self.settings["font-antialiasing"] = "none"
TWEAK_GROUPS = [
ListBoxTweakGroup(_("Fonts"),
......
# SPDX-License-Identifier: GPL-3.0+
# License-Filename: LICENSES/GPL-3.0
import os.path
import zipfile
import tempfile
import logging
import json
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import Gio
from gi.repository import Pango
from operator import itemgetter
from gtweak.utils import extract_zip_file, execute_subprocess
from gtweak.gshellwrapper import GnomeShell, GnomeShellFactory
from gtweak.tweakmodel import Tweak
from gtweak.widgets import FileChooserButton, build_label_beside_widget, build_horizontal_sizegroup, build_tight_button, ListBoxTweakGroup
from gtweak.egowrapper import ExtensionsDotGnomeDotOrg
from gtweak.utils import DisableExtension
def N_(x): return x
def _fix_shell_version_for_ego(version):
#extensions.gnome.org uses a weird versioning system,
#3.10.0 is 3.10, 3.10.0.x (x is ignored)
#drop the pico? release
version = '.'.join(version.split('.')[0:3])
if version[-1] == '0':
#if it is .0, drop that too
return _get_shell_major_minor_version(version)
else:
return version
def _get_shell_major_minor_version(version):
return '.'.join(version.split('.')[0:2])
class _ExtensionsBlankState(Gtk.Box, Tweak):
def __init__(self):
Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL, spacing=18,
valign=Gtk.Align.CENTER)
Tweak.__init__(self, 'extensions', '')
self.add(Gtk.Image(icon_name="org.gnome.tweaks-symbolic",
pixel_size=128, opacity=0.3))
self.add(Gtk.Label(label="<b>" + _("No Extensions Installed") + "</b>",
use_markup=True, opacity=0.3))
try:
self._swInfo = Gio.DesktopAppInfo.new("org.gnome.Software.desktop")
if self._swInfo:
btn = Gtk.Button(label=_("Browse in Software"),
always_show_image=True, halign=Gtk.Align.CENTER,
image=Gtk.Image(icon_name="org.gnome.Software-symbolic"))
btn.connect("clicked", self._on_browse_clicked)
self.add(btn)
except:
logging.warning("Error detecting shell", exc_info=True)
self.show_all()
def _on_browse_clicked(self, btn):
self._swInfo.launch([], None)
class _ExtensionDescriptionLabel(Gtk.Label):
def do_get_preferred_height_for_width(self, width):
# Hack: Request the maximum height allowed by the line limit
if self.get_lines() > 0:
return Gtk.Label.do_get_preferred_height_for_width(self, 0)
return Gtk.Label.do_get_preferred_height_for_width(self, width)
class _ShellExtensionTweak(Gtk.ListBoxRow, Tweak):
def __init__(self, shell, ext, **options):
Gtk.ListBoxRow.__init__(self)
Tweak.__init__(self, ext["name"], ext.get("description",""), **options)
self.hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.hbox.props.border_width = 10
self.hbox.props.spacing = 12
self._shell = shell
state = ext.get("state")
uuid = ext["uuid"]
self._app_id = "user/*/extensions-web/shell-extension/" + uuid.replace('@', '_') + "/*"
shell._settings.bind("disable-user-extensions", self,
"sensitive", Gio.SettingsBindFlags.INVERT_BOOLEAN)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
lbl_name = Gtk.Label(xalign=0.0)
name_markup = GLib.markup_escape_text(ext["name"].lower().capitalize())
lbl_name.set_markup("<span size='medium'><b>"+name_markup+"</b></span>")
lbl_desc = _ExtensionDescriptionLabel(xalign=0.0, yalign=0.0, wrap=True, lines=2)
desc = GLib.markup_escape_text(ext["description"].lower().capitalize().split('\n')[0])
lbl_desc.set_markup("<span size='small'>"+desc+"</span>")
lbl_desc.get_style_context().add_class("dim-label")
lbl_desc.props.ellipsize = Pango.EllipsizeMode.END
vbox.pack_start(lbl_name, False, False, 0)
vbox.pack_start(lbl_desc, False, False, 0)
self.hbox.pack_start(vbox, True, True, 10)
info = None
warning = None
sensitive = False
if state == GnomeShell.EXTENSION_STATE["ENABLED"] or \
state == GnomeShell.EXTENSION_STATE["DISABLED"] or \
state == GnomeShell.EXTENSION_STATE["INITIALIZED"]:
sensitive = True
elif state == GnomeShell.EXTENSION_STATE["DOWNLOADING"]:
info = _("Extension downloading")
elif state == GnomeShell.EXTENSION_STATE["ERROR"]:
warning = _("Error loading extension")
elif state == GnomeShell.EXTENSION_STATE["OUT_OF_DATE"]:
warning = _("Extension does not support shell version")
else:
warning = _("Unknown extension error")
logging.critical(warning)
if info:
inf = self.make_image("dialog-information-symbolic", info)
self.hbox.pack_start(inf, False, False, 0)
if warning:
wg = self.make_image("dialog-warning-symbolic", warning)
self.hbox.pack_start(wg, False, False, 0)
if self._shell.SUPPORTS_EXTENSION_PREFS:
prefs = os.path.join(ext['path'], "prefs.js")
if os.path.exists(prefs):
btn = Gtk.Button.new_from_icon_name("emblem-system-symbolic", Gtk.IconSize.BUTTON)
btn.props.valign = Gtk.Align.CENTER
btn.connect("clicked", self._on_configure_clicked, uuid)
self.hbox.pack_start(btn, False, False, 0)
sw = Gtk.Switch(sensitive=sensitive)
sw.props.vexpand = False
sw.props.valign = Gtk.Align.CENTER
sw.set_active(self._shell.extension_is_active(state, uuid))
sw.connect('notify::active', self._on_extension_toggled, uuid)
self.hbox.pack_start(sw, False, False, 0)
de = DisableExtension()
de.connect('disable-extension', self._on_disable_extension, sw)
self.add(self.hbox)
self.widget_for_size_group = None
def _on_disable_extension(self, de, sw):
sw.set_active(False)
def _on_configure_clicked(self, btn, uuid):
execute_subprocess(['gnome-shell-extension-prefs', uuid], block=False)
def _on_extension_toggled(self, sw, active, uuid):
if not sw.get_active():
self._shell.disable_extension(uuid)
else:
self._shell.enable_extension(uuid)
def _on_extension_update(self, btn, uuid):
self._shell.uninstall_extension(uuid)
btn.get_style_context().remove_class("suggested-action")
btn.set_label(_("Updating"))
self.set_sensitive(False)
self._shell.install_remote_extension(uuid,self.reply_handler, self.error_handler, btn)
def do_activate(self):
bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
bus.call('org.gnome.Software',
'/org/gnome/Software',
'org.freedesktop.Application',
'ActivateAction',
GLib.Variant('(sava{sv})',
('details', [GLib.Variant('(ss)', (self._app_id, ''))], {})),
None, 0, -1, None)
def reply_handler(self, proxy_object, result, user_data):
if result == 's':
user_data.hide()
self.set_sensitive(True)
def error_handler(self, proxy_object, result, user_data):
user_data.set_label(_("Error"))
print(result)
def add_update_button(self, uuid):
updateButton = Gtk.Button(_("Update"))
updateButton.get_style_context().add_class("suggested-action")
updateButton.connect("clicked", self._on_extension_update, uuid)
updateButton.show()
self.hbox.pack_end(updateButton, False, False, 0)
def make_image(self, icon, tip):
image = Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.MENU)
image.set_tooltip_text(tip)
return image
class ShellExtensionTweakGroup(ListBoxTweakGroup):
def __init__(self):
extension_tweaks = []
sg = build_horizontal_sizegroup()
#check the shell is running
try:
shell = GnomeShellFactory().get_shell()
if shell is None:
raise Exception("Shell not running or DBus service not available")
version = tuple(shell.version.split("."))
ego = ExtensionsDotGnomeDotOrg(version)
try:
#add a tweak for each installed extension
extensions = sorted(list(shell.list_extensions().values()), key=itemgetter("name"))
for extension in extensions:
try:
extension_widget = _ShellExtensionTweak(shell, extension, size_group=sg)
extension_tweaks.append(extension_widget)
if extension.get("type") == GnomeShell.EXTENSION_TYPE["PER_USER"]:
ego.connect("got-extension-info", self._got_info, extension, extension_widget)
ego.query_extension_info(extension["uuid"])
except:
logging.warning("Invalid extension", exc_info=True)
except:
logging.warning("Error listing extensions", exc_info=True)
except:
logging.warning("Error detecting shell", exc_info=True)
ListBoxTweakGroup.__init__(self,
_("Extensions"),
*extension_tweaks,
activatable=True)
if shell is None:
return # we're done
self.props.valign = Gtk.Align.FILL
self.titlebar_widget = Gtk.Switch(visible=True)
shell._settings.bind("disable-user-extensions", self.titlebar_widget,
"active", Gio.SettingsBindFlags.INVERT_BOOLEAN)
self.set_header_func(self._list_header_func, None)
self.connect("row-activated", self._on_row_activated, None);
if not len(extension_tweaks):
placeholder = _ExtensionsBlankState()
self.set_placeholder(placeholder)
self.tweaks.append(placeholder)
def _got_info(self, ego, resp, uuid, extension, widget):
if uuid == extension["uuid"]:
resp = resp['shell_version_map']
shell = GnomeShellFactory().get_shell()
version = _fix_shell_version_for_ego(shell.version)
if version in resp:
resp = resp[version]
ext_version = extension["version"] if "version" in extension else 0
if int(resp["version"]) > ext_version:
widget.add_update_button(uuid)
else:
ext_version = extension["version"] if "version" in extension else "unknown"
logging.info("e.g.o no updates for %s (shell version %s extension version %s)" % (
uuid, version, ext_version))
def _list_header_func(self, row, before, user_data):
if before and not row.get_header():
row.set_header (Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL))
def _on_row_activated(self, list, row, user_data):
row.activate()
TWEAK_GROUPS = [
ShellExtensionTweakGroup(),
]
......@@ -58,7 +58,6 @@ class _AppChooser(Gtk.Dialog):
self._search_key, self._search_mods = Gtk.accelerator_parse(_("<primary>f"))
lb = Gtk.ListBox()
lb.props.margin = 5
lb.props.activate_on_single_click = False
lb.set_sort_func(self._sort_apps, None)
lb.set_header_func(_list_header_func, None)
......@@ -81,6 +80,7 @@ class _AppChooser(Gtk.Dialog):
lb.add(w)
sw = Gtk.ScrolledWindow()
sw.props.margin = 2
sw.props.hscrollbar_policy = Gtk.PolicyType.NEVER
sw.add(lb)
......@@ -131,6 +131,8 @@ class _AppChooser(Gtk.Dialog):
def _build_widget(self, a, extra):
row = Gtk.ListBoxRow()
g = Gtk.Grid()
g.props.margin = 5
if not a.get_name():
return None
icn = a.get_icon()
......
......@@ -42,7 +42,7 @@ class _TestButtonTweak(Gtk.Box, Tweak):
self.notify_logout()
css_provider = Gtk.CssProvider()
css_provider.load_from_data("""
css_provider.load_from_data(b"""
.list-row.tweak#tweak-test-foo {
background-color: red;
}
......
......@@ -162,7 +162,8 @@ class TypingTweakGroup(Gtk.Box):
for opt in set(self._xkb_info.get_all_option_groups()) - self.XKB_OPTIONS_BLACKLIST:
obj = _XkbOption(opt, self._kbdsettings, self._xkb_info)
self._option_objects.append(obj)
self.pack_start(obj, False, False, 0)
self._option_objects.sort(key=lambda item_desc: item_desc.name)
for item in self._option_objects: self.pack_start(item, False, False, 0)
TweakGroup.__init__(self, _("Typing"), *self._option_objects)
self.connect("destroy", self._on_destroy)
......
......@@ -16,15 +16,15 @@ class Window(Gtk.ApplicationWindow):
Gtk.ApplicationWindow.__init__(self,
application=app,
show_menubar=False)
self.set_size_request(-1, 700)
self.set_default_size(980, 640)
self.set_size_request(-1, 300)
self.set_position(Gtk.WindowPosition.CENTER)
self.set_icon_name("org.gnome.tweaks")
self.hsize_group = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL)
self.main_box = Handy.Leaflet()
self.main_box.set_mode_transition_type(Handy.LeafletModeTransitionType.SLIDE)
self.main_box.set_child_transition_type(Handy.LeafletChildTransitionType.SLIDE)
self.main_box.set_transition_type(Handy.LeafletTransitionType.SLIDE)
left_box = self.sidebar()
right_box = self.main_content()
......@@ -68,8 +68,7 @@ class Window(Gtk.ApplicationWindow):
def titlebar(self):
header = Handy.Leaflet()
header.set_mode_transition_type(Handy.LeafletModeTransitionType.SLIDE)
header.set_child_transition_type(Handy.LeafletChildTransitionType.SLIDE)
header.set_transition_type(Handy.LeafletTransitionType.SLIDE)
header.connect("notify::visible-child", self._update_decorations)
header.connect("notify::fold", self._update_decorations)
......@@ -130,8 +129,8 @@ class Window(Gtk.ApplicationWindow):
header.child_set(right_header, name="content")
self.header_group = Handy.HeaderGroup()
self.header_group.add_header_bar(left_header)
self.header_group.add_header_bar(right_header)
self.header_group.add_gtk_header_bar(left_header)
self.header_group.add_gtk_header_bar(right_header)
self.hsize_group.add_widget(left_header)
......@@ -139,6 +138,7 @@ class Window(Gtk.ApplicationWindow):
def sidebar(self):
left_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
left_box.set_size_request(200, -1)
self.entry = Gtk.SearchEntry(placeholder_text=_("Search Tweaks…"))
if (Gtk.check_version(3, 22, 20) is None):
......@@ -151,7 +151,6 @@ class Window(Gtk.ApplicationWindow):
self.listbox = Gtk.ListBox()
self.listbox.get_style_context().add_class("tweak-categories")
self.listbox.set_size_request(200, -1)
self.listbox.connect("row-selected", self._on_select_row)
self.listbox.set_header_func(self._list_header_func, None)
scroll = Gtk.ScrolledWindow()
......@@ -168,7 +167,7 @@ class Window(Gtk.ApplicationWindow):
def main_content(self):
right_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
right_box.set_size_request(750, -1)
right_box.set_size_request(540, -1)
self.stack = Gtk.Stack()
self.stack.get_style_context().add_class("main-container")
......@@ -226,9 +225,9 @@ class Window(Gtk.ApplicationWindow):
def _update_decorations(self, *_):
header = self.get_titlebar()
if header.props.folded:
self.header_group.set_focus(header.get_visible_child())
self.header_group.set_decorate_all(True)
else:
self.header_group.set_focus(None)
self.header_group.set_decorate_all(False)
def _after_key_press(self, widget, event):
if not self.button.get_active() or not self.entry.is_focus():
......
......@@ -9,6 +9,7 @@ import shutil
import subprocess
import glob
import itertools
import logging
import gi
gi.require_version("Notify", "0.7")
......@@ -18,7 +19,7 @@ from gi.repository import Gio
from gi.repository import Notify
import gtweak
from gtweak.gsettings import GSettingsSetting
def singleton(cls):
"""
......@@ -251,20 +252,6 @@ class SchemaList:
s = Gio.Settings(i[1])
s.reset(i[0])
@singleton
class DisableExtension(GObject.GObject):
__gsignals__ = {
"disable-extension": (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE,()),
}
def __init__(self):
GObject.GObject.__init__(self)
def disable(self):
self.emit("disable-extension")
@singleton
class XSettingsOverrides:
......@@ -275,8 +262,15 @@ class XSettingsOverrides:
}
def __init__(self):
self._settings = Gio.Settings(schema='org.gnome.settings-daemon.plugins.xsettings')
self._variant = self._settings.get_value("overrides")
# Ensure we don't error out
try:
self._settings = GSettingsSetting(schema='org.gnome.settings-daemon.plugins.xsettings')
except:
self._settings = None
logging.warn("org.gnome.settings-daemon.plugins.xsettings not installed or running")
if self._settings:
self._variant = self._settings.get_value("overrides")
def _dup_variant_as_dict(self):
items = {}
......