Re: [rhinstaller/anaconda/pulls/483 master] Enable multiselection in
Custom spoke
by dashea
> @@ -489,9 +467,42 @@ def get_new_devices(self):
>
> return new_devices
>
> + def _set_page_label_text(self):
> + if self._accordion.is_multiselection:
> + select_tmpl = _("{fmt_start}{selected}{fmt_end} of "
This is going to be a mess to translate. There are a lot pieces that will mean nothing to translators, which creates the potential for the translations to break it, and I'm not really sure what the purpose of all the fmt_start and fmt_end is anyway. The entire string is going to be made black or gray, and not just the pieces wrapped in span tags, right? I can see that fmt_start+fmt_end will also make it large and bold, but I don't understand what the color adds at that point. Either way, I think it would be more readable for both us and for translators if all of, for example, "{fmt_start}{selected}{fmt_end}" was a single parameter in the format string.
Additionally, it would be nicer if this used the old-style % formatting that we use elsewhere in anaconda. For one, I'm old and I hate new things, and for two, the {} style is not regular which makes some of the analysis we do in the tests a giant pain.
--
To view this pull request on github, visit https://github.com/rhinstaller/anaconda/pull/483#discussion_r51495363
8 years, 4 months
[master 3/3] Apply the fallback style to anaconda selectors.
by dashea
From: David Shea <dshea(a)redhat.com>
This isn't particularly new or well-documented, but it's more
straightforward and less gross than carrying around a block of
deprecated CSS.
---
data/anaconda-gtk.css | 32 --------------------------------
widgets/src/DiskOverview.c | 6 ++++++
widgets/src/MountpointSelector.c | 6 ++++++
widgets/src/SpokeSelector.c | 6 ++++++
4 files changed, 18 insertions(+), 32 deletions(-)
diff --git a/data/anaconda-gtk.css b/data/anaconda-gtk.css
index 5b36996..045c4ed 100644
--- a/data/anaconda-gtk.css
+++ b/data/anaconda-gtk.css
@@ -126,38 +126,6 @@ AnacondaSpokeWindow #nav-box {
background-color: @fedora;
}
-/* These rules were removed when the Adwaita theme moved from
- * gnome-themes-standard to gtk. The selectors had been wildcards, but after
- * the move they were replaced with more specific selectors. We need to apply
- * the old style to anconda's custom widgets in order for the selection
- * highlight and insensitive shading to appear.
- */
-@define-color anaconda_selected_bg_color #4a90d9;
-@define-color anaconda_selected_fg_color #ffffff;
-
-AnacondaMountpointSelector:selected,
-AnacondaDiskOverview:selected,
-AnacondaSpokeSelector:selected,
-AnacondaMountpointSelector:selected:focus,
-AnacondaDiskOverview:selected:focus,
-AnacondaSpokeSelector:selected:focus {
- background-color: @anaconda_selected_bg_color;
- color: @anaconda_selected_fg_color;
-}
-
-@define-color anaconda_insensitive_bg_color #f4f4f2;
-@define-color anaconda_insensitive_fg_color #a7aba7;
-@define-color anaconda_internal_element_color #888a85;
-@define-color anaconda_insensitive_borders shade(@anaconda_internal_element_color, 1.37);
-
-AnacondaMountpointSelector:insensitive,
-AnacondaDiskOverview:insensitive,
-AnacondaSpokeSelector:insensitive {
- background-color: @anaconda_insensitive_bg_color;
- color: @anaconda_insensitive_fg_color;
- border-color: @anaconda_insensitive_borders;
-}
-
/* When multi-column GtkTreeViews set a row separator, the horizontal-separator
* style property is still applied to the row separator, breaking the row
* separator up for each column. It looks kind of dumb. Provide a way to not do
diff --git a/widgets/src/DiskOverview.c b/widgets/src/DiskOverview.c
index b9ba6f1..5242d5e 100644
--- a/widgets/src/DiskOverview.c
+++ b/widgets/src/DiskOverview.c
@@ -262,6 +262,7 @@ static void anaconda_disk_overview_init(AnacondaDiskOverview *widget) {
AtkObject *atk;
AtkRole role;
char *markup;
+ GtkStyleContext *context;
widget->priv = G_TYPE_INSTANCE_GET_PRIVATE(widget,
ANACONDA_TYPE_DISK_OVERVIEW,
@@ -337,6 +338,11 @@ G_GNUC_END_IGNORE_DEPRECATIONS
atk = gtk_widget_get_accessible(GTK_WIDGET(widget));
atk_object_set_role(atk, role);
+
+ /* Apply the "fallback" style so that the widgets are colored correctly when
+ * selected, insensitive, etc */
+ context = gtk_widget_get_style_context(GTK_WIDGET(widget));
+ gtk_style_context_add_class(context, "gtkstyle-fallback");
}
gboolean anaconda_disk_overview_clicked(AnacondaDiskOverview *widget, GdkEvent *event) {
diff --git a/widgets/src/MountpointSelector.c b/widgets/src/MountpointSelector.c
index 4301a3b..8540ca2 100644
--- a/widgets/src/MountpointSelector.c
+++ b/widgets/src/MountpointSelector.c
@@ -213,6 +213,7 @@ static void format_name_label(AnacondaMountpointSelector *widget, const char *va
static void anaconda_mountpoint_selector_init(AnacondaMountpointSelector *mountpoint) {
gchar *file;
+ GtkStyleContext *context;
mountpoint->priv = G_TYPE_INSTANCE_GET_PRIVATE(mountpoint,
ANACONDA_TYPE_MOUNTPOINT_SELECTOR,
@@ -285,6 +286,11 @@ G_GNUC_END_IGNORE_DEPRECATIONS
/* Set NULL to parent_page while it's not set already */
mountpoint->priv->parent_page = NULL;
+
+ /* Apply the "fallback" style so that the widgets are colored correctly when
+ * selected, insensitive, etc. */
+ context = gtk_widget_get_style_context(GTK_WIDGET(mountpoint));
+ gtk_style_context_add_class(context, "gtkstyle-fallback");
}
static void anaconda_mountpoint_selector_finalize(GObject *object) {
diff --git a/widgets/src/SpokeSelector.c b/widgets/src/SpokeSelector.c
index 2e34ed6..6ebeec9 100644
--- a/widgets/src/SpokeSelector.c
+++ b/widgets/src/SpokeSelector.c
@@ -271,6 +271,7 @@ static void set_icon(AnacondaSpokeSelector *widget, const char *icon_name) {
static void anaconda_spoke_selector_init(AnacondaSpokeSelector *spoke) {
AtkObject *atk;
AtkRole role;
+ GtkStyleContext *context;
spoke->priv = G_TYPE_INSTANCE_GET_PRIVATE(spoke,
ANACONDA_TYPE_SPOKE_SELECTOR,
@@ -339,6 +340,11 @@ G_GNUC_END_IGNORE_DEPRECATIONS
atk_object_set_name(atk, _(DEFAULT_TITLE));
atk_object_set_description(atk, _(DEFAULT_STATUS));
atk_object_set_role(atk, role);
+
+ /* Apply the "fallback" style so that the widgets are colored correctly when
+ * selected, insensitive, etc */
+ context = gtk_widget_get_style_context(GTK_WIDGET(spoke));
+ gtk_style_context_add_class(context, "gtkstyle-fallback");
}
static void anaconda_spoke_selector_finalize(GObject *object) {
--
To view this commit on github, visit https://github.com/rhinstaller/anaconda/commit/7ca20cf2d8cd3aeac9f373d692...
8 years, 4 months
[master 2/3] Redo the stylesheet for Gtk 3.19+
by dashea
From: David Shea <dshea(a)redhat.com>
The new version of gtk includes a lot of documentation and improvements
regarding how to apply stylesheets to widgets, but the names of a lot of
stuff changed as well.
---
data/anaconda-gtk.css | 50 +++++++++-------------------------------------
pyanaconda/ui/gui/utils.py | 2 +-
2 files changed, 10 insertions(+), 42 deletions(-)
diff --git a/data/anaconda-gtk.css b/data/anaconda-gtk.css
index e96a0ba..5b36996 100644
--- a/data/anaconda-gtk.css
+++ b/data/anaconda-gtk.css
@@ -8,59 +8,27 @@
* default, but it defines them for level bars using a continuous value between
* 0 and 1, so our discrete level bars are effectively always at the "high"
* level.
- *
- * Fun surprises that might change in future versions:
- *
- * - Defining properties for a level will set properties on both the filled
- * portion and the empty portion of the level bar. So if all of the display
- * properties are set, which we do, the level bar effectively becomes 100%
- * filled with whatever properties are set. Copying the .empty-fill-block
- * properties from the theme so that they apply at the application level
- * works around this.
- *
- * - There's a bug in the handling of the maximum offset in that it only
- * applies when value == max-value, rather than when it's between
- * max-value and the next lower offset. For example, consider a
- * discrete-mode level bar with a max-value of 4 and offsets defined as
- * "low": 2.0, "medium": 3.0 and "high": 4.0. Value 1 will be low,
- * value 2 will be medium, value 3 will be in an undefined no-man's land,
- * and value 4 will be high. To get around this we re-define the default
- * fill-block values as the same as fill-block.level-high.
- *
- * - The GNOME Adwaita theme applies a gradient to the progress bar when the
- * window is focused. It does this by redefining the colors in a
- * background-image property, so unless we reset background-image our colors
- * will only apply when the window is out of focus, which uses the :backdrop
- * selector.
*/
@define-color anaconda_level_bar_low red;
@define-color anaconda_level_bar_medium orange;
@define-color anaconda_level_bar_high green;
-.level-bar.fill-block.level-low {
+/* The .fill part works around https://bugzilla.gnome.org/show_bug.cgi?id=761416 */
+levelbar.discrete trough block.filled.full,
+levelbar.discrete trough block.filled.low {
border-color: darker(@anaconda_level_bar_low);
- background-color: @anaconda_level_bar_low;
- background-image: none;
+ background: @anaconda_level_bar_low;
}
-.level-bar.fill-block.level-medium {
+levelbar.discrete trough block.filled.medium {
border-color: darker(@anaconda_level_bar_medium);
- background-color: @anaconda_level_bar_medium;
- background-image: none;
+ background: @anaconda_level_bar_medium;
}
-.level-bar.fill-block,
-.level-bar.fill-block.level-high {
+levelbar.discrete trough block.filled.high {
border-color: darker(@anaconda_level_bar_high);
- background-color: @anaconda_level_bar_high;
- background-image: none;
-}
-
-.level-bar.fill-block.empty-fill-block {
- background-color: transparent;
- background-image: none;
- border-color: alpha(#000000, 0.1);
+ background: @anaconda_level_bar_high;
}
/* As of gnome-themes-standard 3.9.90, the Adwaita theme uses the same color
@@ -195,7 +163,7 @@ AnacondaSpokeSelector:insensitive {
* separator up for each column. It looks kind of dumb. Provide a way to not do
* that.
*/
-GtkTreeView.solid-separator {
+treeview.solid-separator {
-GtkTreeView-horizontal-separator: 0;
}
diff --git a/pyanaconda/ui/gui/utils.py b/pyanaconda/ui/gui/utils.py
index 9ca1733..0369cc2 100644
--- a/pyanaconda/ui/gui/utils.py
+++ b/pyanaconda/ui/gui/utils.py
@@ -372,7 +372,7 @@ def setViewportBackground(vp, color="@theme_bg_color"):
"""
provider = Gtk.CssProvider()
- provider.load_from_data(bytes("GtkViewport { background-color: %s }" % color, "utf-8"))
+ provider.load_from_data(bytes("viewport { background: %s }" % color, "utf-8"))
context = vp.get_style_context()
context.add_provider(provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
--
To view this commit on github, visit https://github.com/rhinstaller/anaconda/commit/80fb3968c8a6912b1e197e25f4...
8 years, 4 months
[master 1/3] Directly overwrite /usr/share/anaconda/anaconda-gtk.css
by dashea
From: David Shea <dshea(a)redhat.com>
Overwriting the installed copy of anaconda-gtk.css is more helpful than
adding a copy in the updates directory, since overwriting allows rules
to be deleted in an updates.img.
---
scripts/makeupdates | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/makeupdates b/scripts/makeupdates
index 0beeeec..cda244c4 100755
--- a/scripts/makeupdates
+++ b/scripts/makeupdates
@@ -324,7 +324,7 @@ def copyUpdatedFiles(tag, updates, cwd, builddir):
elif gitfile == "data/tmux.conf":
install_to_dir(gitfile, "usr/share/anaconda")
elif gitfile == "data/anaconda-gtk.css":
- install_to_dir(gitfile, "run/install/updates")
+ install_to_dir(gitfile, "usr/share/anaconda")
elif gitfile == "data/interactive-defaults.ks":
install_to_dir(gitfile, "usr/share/anaconda")
elif gitfile == "data/anaconda_options.txt":
--
To view this commit on github, visit https://github.com/rhinstaller/anaconda/commit/11163ab074daa3c7a150dfff24...
8 years, 4 months
[master 30/30] Update sysfs path before updating size in
StorageDevice ctor.
by dashea
From: David Lehman <dlehman(a)redhat.com>
---
blivet/devices/storage.py | 6 ++++--
tests/devices_test/device_properties_test.py | 2 +-
tests/populator_test.py | 1 +
3 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/blivet/devices/storage.py b/blivet/devices/storage.py
index 39cf18d..b6eedcc 100644
--- a/blivet/devices/storage.py
+++ b/blivet/devices/storage.py
@@ -141,8 +141,10 @@ def __init__(self, name, fmt=None, uuid=None,
self.device_links = []
- if self.exists and self.status:
- self.update_size()
+ if self.exists:
+ self.update_sysfs_path()
+ if self.status:
+ self.update_size()
def __str__(self):
exist = "existing"
diff --git a/tests/devices_test/device_properties_test.py b/tests/devices_test/device_properties_test.py
index 21b9226..552acb8 100644
--- a/tests/devices_test/device_properties_test.py
+++ b/tests/devices_test/device_properties_test.py
@@ -696,7 +696,7 @@ def test_btrfssnap_shot_device_init(self):
with self.assertRaisesRegex(ValueError, "btrfs snapshot source must be a btrfs subvolume"):
BTRFSSnapShotDevice("snap1", parents=[vol], source=parents[0])
- parents2 = [StorageDevice("p1", fmt=blivet.formats.get_format("btrfs"), size=BTRFS_MIN_MEMBER_SIZE)]
+ parents2 = [StorageDevice("p1", fmt=blivet.formats.get_format("btrfs"), size=BTRFS_MIN_MEMBER_SIZE, exists=True)]
vol2 = BTRFSVolumeDevice("test2", parents=parents2, exists=True)
with self.assertRaisesRegex(ValueError, ".*snapshot and source must be in the same volume"):
BTRFSSnapShotDevice("snap1", parents=[vol], source=vol2)
diff --git a/tests/populator_test.py b/tests/populator_test.py
index bd76289..162b88a 100644
--- a/tests/populator_test.py
+++ b/tests/populator_test.py
@@ -131,6 +131,7 @@ def test_get_helper(self, *args):
@patch.object(DeviceTree, "get_device_by_name")
@patch.object(DMDevice, "status", return_value=True)
+ @patch.object(DMDevice, "update_sysfs_path")
@patch.object(DeviceTree, "_add_slave_devices")
@patch("blivet.udev.device_is_dm_livecd", return_value=False)
@patch("blivet.udev.device_get_name")
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/3a666ad9d64dbc63c071a681b856...
8 years, 4 months
[master 29/30] Validate event handling callbacks.
by dashea
From: David Lehman <dlehman(a)redhat.com>
---
blivet/events/manager.py | 45 ++++++++++++++++++++++++++++++++++++---------
tests/events_test.py | 5 +++--
2 files changed, 39 insertions(+), 11 deletions(-)
diff --git a/blivet/events/manager.py b/blivet/events/manager.py
index 4dfc76e..fb5a1e5 100644
--- a/blivet/events/manager.py
+++ b/blivet/events/manager.py
@@ -21,6 +21,7 @@
#
import abc
+import inspect
from threading import current_thread, RLock, Thread
import pyudev
import sys
@@ -39,6 +40,32 @@
event_log = logging.getLogger("blivet.event")
+def validate_cb(cb, kwargs=None, arg_count=None):
+ """ Validate signature of callback function, returning True on success. """
+ if not callable(cb):
+ return False
+
+ kwargs = kwargs or []
+ arg_count = arg_count or 0
+
+ params = inspect.signature(cb).parameters
+ if len(params) < arg_count:
+ return False
+
+ if not all(kw in params for kw in kwargs):
+ return False
+
+ for kw in kwargs:
+ if kw not in params:
+ return False
+
+ p = params[kw]
+ if p.kind not in (p.KEYWORD_ONLY, p.POSITIONAL_OR_KEYWORD):
+ return False
+
+ return True
+
+
#
# Event
#
@@ -105,13 +132,13 @@ def match(self, event):
class EventManager(object, metaclass=abc.ABCMeta):
def __init__(self, handler_cb=None, notify_cb=None, error_cb=None):
self._handler_cb = None
- """ event handler """
+ """ event handler (must accept 'event', 'notify_cb' kwargs """
self._notify_cb = None
- """ notification callback """
+ """ notification callback (must accept 'event', 'changes' kwargs) """
self._error_cb = None
- """ exception handler """
+ """ exception handler (must be callable with one argument) """
if handler_cb is not None:
self.handler_cb = handler_cb
@@ -135,8 +162,8 @@ def handler_cb(self):
@handler_cb.setter
def handler_cb(self, cb):
- if not callable(cb):
- raise EventParamError("handler must be callable")
+ if not validate_cb(cb, kwargs=["event", "notify_cb"]):
+ raise EventParamError("invalid handler callback specified")
self._handler_cb = cb
@@ -147,8 +174,8 @@ def notify_cb(self):
@notify_cb.setter
def notify_cb(self, cb):
- if not callable(cb) or cb.func_code.argcount < 1:
- raise EventParamError("callback function must accept at least one arg")
+ if not validate_cb(cb, kwargs=["event", "changes"]):
+ raise EventParamError("invalid notification callback specified")
self._notify_cb = cb
@@ -159,8 +186,8 @@ def error_cb(self):
@error_cb.setter
def error_cb(self, cb):
- if not callable(cb) or cb.func_code.argcount < 1:
- raise EventParamError("callback function must accept at least one arg")
+ if not validate_cb(cb, arg_count=1):
+ raise EventParamError("invalid error callback specified")
self._error_cb = cb
diff --git a/tests/events_test.py b/tests/events_test.py
index 511fb7f..84463a2 100644
--- a/tests/events_test.py
+++ b/tests/events_test.py
@@ -1,6 +1,6 @@
from unittest import TestCase
-from unittest.mock import Mock
+from unittest.mock import Mock, patch
from blivet.events.manager import Event, EventManager
@@ -22,7 +22,8 @@ def _create_event(self, *args, **kwargs):
class EventManagerTest(TestCase):
def testEventMask(self):
handler_cb = Mock()
- mgr = FakeEventManager(handler_cb=handler_cb)
+ with patch("blivet.events.manager.validate_cb", return_value=True):
+ mgr = FakeEventManager(handler_cb=handler_cb)
device = "sdc"
action = "add"
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/7d8a02c079f06f22b1a1bc84d277...
8 years, 4 months
[master 28/30] Hook up the event notify callback.
by dashea
From: David Lehman <dlehman(a)redhat.com>
---
blivet/events/handler.py | 6 +++++-
blivet/events/manager.py | 4 +++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/blivet/events/handler.py b/blivet/events/handler.py
index 5601a7f..b21d9e3 100644
--- a/blivet/events/handler.py
+++ b/blivet/events/handler.py
@@ -38,10 +38,11 @@ class EventHandlerMixin(metaclass=SynchronizedMeta):
def __init__(self):
event_manager.handler_cb = self.handle_event
- def handle_event(self, event):
+ def handle_event(self, event, notify_cb):
""" Handle an event on a block device.
:param :class:`~.event.Event` event: information about the event
+ :param callable notify_cb: notification callback
TODO: Rename all this stuff so it's explicit that it only handles uevents.
"""
@@ -54,6 +55,9 @@ def handle_event(self, event):
if handler is not None:
handler(event)
+ if notify_cb is not None:
+ notify_cb(event=event, changes=event_data.changes)
+
def _event_device_is_dm(self, event):
""" Return True if event operand is a dm device.
diff --git a/blivet/events/manager.py b/blivet/events/manager.py
index 14ebb90..4dfc76e 100644
--- a/blivet/events/manager.py
+++ b/blivet/events/manager.py
@@ -256,7 +256,9 @@ def _run_event_handler(self, event):
return
try:
- self.handler_cb(event) # pylint: disable=not-callable
+ # Pass the notify callback to the handler so it can run the
+ # callback and pass thread-local data to it.
+ self.handler_cb(event=event, notify_cb=self.notify_cb) # pylint: disable=not-callable
except Exception: # pylint: disable=broad-except
event_log.error(traceback.format_exc())
exc_info = sys.exc_info()
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/782d90a4470bfeb3557546e60cfa...
8 years, 4 months