On Mon, 2014-01-06 at 17:16 -0500, Chris Lumens wrote:
This clears up the problems of funny spacing in the views by getting
rid of
the stores and views, which were causing the problems. Instead I'm just using
a ListBox of boxes, each of which contains a button and a label describing the
environment or addon.
Note that for now, this reverts proper keyboard scrolling behavior.
Change for a
lot better code anyway. Three comments below.
diff --git a/pyanaconda/ui/gui/spokes/software.py
b/pyanaconda/ui/gui/spokes/software.py
index 4fbd16c..021187a 100644
--- a/pyanaconda/ui/gui/spokes/software.py
+++ b/pyanaconda/ui/gui/spokes/software.py
@@ -19,7 +19,7 @@
# Red Hat Author(s): Chris Lumens <clumens(a)redhat.com>
#
-from gi.repository import Gdk
+from gi.repository import Gtk, Pango
from pyanaconda.flags import flags
from pyanaconda.i18n import _, C_, CN_
@@ -65,8 +65,8 @@ class SoftwareSelectionSpoke(NormalSpoke):
self.excludedGroups = []
self.environment = None
- self._addonStore = self.builder.get_object("addonStore")
- self._environmentStore = self.builder.get_object("environmentStore")
+ self._environmentListBox =
self.builder.get_object("environmentListBox")
+ self._addonListBox = self.builder.get_object("addonListBox")
# Used to determine which add-ons to display for each environment.
# The dictionary keys are environment IDs. The dictionary values are two-tuples
@@ -84,12 +84,9 @@ class SoftwareSelectionSpoke(NormalSpoke):
self._origAddons = []
self._origEnvironment = None
- # We need to tell the addon view whether something is a separator or not.
-
self.builder.get_object("addonView").set_row_separator_func(self._addon_row_is_separator,
None)
-
def _apply(self):
- row = self._get_selected_environment()
- if not row:
+ env = self._get_selected_environment()
+ if not env:
return
addons = self._get_selected_addons()
@@ -99,8 +96,8 @@ class SoftwareSelectionSpoke(NormalSpoke):
self._selectFlag = False
self.payload.data.packages.groupList = []
- self.payload.selectEnvironment(row[2])
- self.environment = row[2]
+ self.payload.selectEnvironment(env)
+ self.environment = env
for group in self.selectedGroups:
self.payload.selectGroup(group)
@@ -145,14 +142,14 @@ class SoftwareSelectionSpoke(NormalSpoke):
@property
def changed(self):
- row = self._get_selected_environment()
- if not row:
+ env = self._get_selected_environment()
+ if not env:
return True
addons = self._get_selected_addons()
# Don't redo dep solving if nothing's changed.
- if row[2] == self._origEnvironment and set(addons) == set(self._origAddons) and
\
+ if env == self._origEnvironment and set(addons) == set(self._origAddons) and \
self.txid_valid:
return False
@@ -189,8 +186,8 @@ class SoftwareSelectionSpoke(NormalSpoke):
if not self.txid_valid:
return _("Source changed - please verify")
- row = self._get_selected_environment()
- if not row:
+ env = self._get_selected_environment()
+ if not env:
# Kickstart installs with %packages will have a row selected, unless
# they did an install without a desktop environment. This should
# catch that one case.
@@ -199,7 +196,7 @@ class SoftwareSelectionSpoke(NormalSpoke):
return _("Nothing selected")
- return self.payload.environmentDescription(row[2])[0]
+ return self.payload.environmentDescription(env)[0]
def initialize(self):
NormalSpoke.initialize(self)
@@ -279,40 +276,58 @@ class SoftwareSelectionSpoke(NormalSpoke):
hubQ.send_message(self.__class__.__name__, _("No installation source
available"))
return False
+ def _add_row(self, listbox, name, desc, button):
I think it would be nicer if
the _add_row function instantiated a new
Gtk.RadioButton on its own. Maybe it could take 'group' instead of
'button' as the last parameter and return the newly created button for
the outer scope to decide on groups?
+ row = Gtk.ListBoxRow()
+ box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
+ box.set_spacing(6)
+
+ button.set_valign(Gtk.Align.START)
+ button.connect("clicked", self.on_button_toggled, row)
+ box.add(button)
+
+ label = Gtk.Label()
+ label.set_line_wrap(True)
+ label.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR)
+ label.set_markup("<b>%s</b>\n%s" % (escape_markup(name),
escape_markup(desc)))
+ label.set_hexpand(True)
+ label.set_alignment(0, 0.5)
+ box.add(label)
+
+ row.add(box)
+ listbox.insert(row, -1)
+
def refresh(self):
NormalSpoke.refresh(self)
threadMgr.wait(constants.THREAD_PAYLOAD_MD)
- self._environmentStore.clear()
if self.environment not in self.payload.environments:
self.environment = None
firstEnvironment = True
+ firstRadio = None
+
+ self._clear_listbox(self._environmentListBox)
+
for environment in self.payload.environments:
(name, desc) = self.payload.environmentDescription(environment)
- itr = self._environmentStore.append([environment == self.environment,
"<b>%s</b>\n%s" % \
- (escape_markup(name), escape_markup(desc)), environment])
- # Either:
- # (1) Select the environment given by kickstart or selected last
- # time this spoke was displayed; or
- # (2) Select the first environment given by display order as the
- # default if nothing is selected.
- if (environment == self.environment) or \
- (not self.environment and firstEnvironment):
+ radio = Gtk.RadioButton(group=firstRadio)
+
+ active = environment == self.environment or \
+ not self.environment and firstEnvironment
+ radio.set_active(active)
+ if active:
self.environment = environment
- sel = self.builder.get_object("environmentSelector")
- sel.select_iter(itr)
+
+ self._add_row(self._environmentListBox, name, desc, radio)
+ if not firstRadio:
+ firstRadio = radio
I know you guys don't like those funky
one-liners, but I'd prefer
firstRadio = firstRadio or radio
here instead of the if statement. It looks and reads completely natural
to me.
firstEnvironment = False
self.refreshAddons()
- def _addon_row_is_separator(self, model, itr, *args):
- # The last column of the model tells us if this row is a separator or not.
- return model[itr][3]
-
def _addAddon(self, grp):
(name, desc) = self.payload.groupDescription(grp)
@@ -326,17 +341,18 @@ class SoftwareSelectionSpoke(NormalSpoke):
else:
selected = self.payload.environmentOptionIsDefault(self.environment, grp)
- self._addonStore.append([selected, "<b>%s</b>\n%s" % \
- (escape_markup(name), escape_markup(desc)), grp, False])
+ check = Gtk.CheckButton()
+ check.set_active(selected)
+ self._add_row(self._addonListBox, name, desc, check)
def refreshAddons(self):
- self._addonStore.clear()
-
# The source was changed, make sure the list is current
if not self.txid_valid:
self._parseEnvironments()
if self.environment and (self.environment in self._environmentAddons):
+ self._clear_listbox(self._addonListBox)
+
# We have two lists: One of addons specific to this environment,
# and one of all the others. The environment-specific ones will be
displayed
# first and then a separator, and then the generic ones. This is to make
it
@@ -356,7 +372,7 @@ class SoftwareSelectionSpoke(NormalSpoke):
# This marks a separator in the view - only add it if there's both
environment
# specific and generic addons.
if addSep:
- self._addonStore.append([False, "", "", True])
+ self._addonListBox.insert(Gtk.Separator(), -1)
for grp in self._environmentAddons[self.environment][1]:
self._addAddon(grp)
@@ -368,95 +384,99 @@ class SoftwareSelectionSpoke(NormalSpoke):
else:
self.clear_info()
+ def _allAddons(self):
+ return self._environmentAddons[self.environment][0] + \
+ [""] + \
+ self._environmentAddons[self.environment][1]
+
def _get_selected_addons(self):
- return [row[2] for row in self._addonStore if row[0]]
+ retval = []
+
+ addons = self._allAddons()
+
+ for (ndx, row) in enumerate(self._addonListBox.get_children()):
+ box = row.get_children()[0]
+
+ if isinstance(box, Gtk.Separator):
+ continue
+
+ button = box.get_children()[0]
+ if button.get_active():
+ retval.append(addons[ndx])
+
+ return retval
# Returns the row in the store corresponding to what's selected on the
# left hand panel, or None if nothing's selected.
def _get_selected_environment(self):
- environmentView = self.builder.get_object("environmentView")
- itr = environmentView.get_selection().get_selected()[1]
- if not itr:
- return None
+ for (ndx, row) in enumerate(self._environmentListBox.get_children()):
+ box = row.get_children()[0]
+ button = box.get_children()[0]
+ if button.get_active():
+ return self.payload.environments[ndx]
- return self._environmentStore[itr]
+ return None
+
+ def _clear_listbox(self, listbox):
+ for child in listbox.get_children():
+ listbox.remove(child)
+ del(child)
@property
def txid_valid(self):
return self._tx_id == self.payload.txID
# Signal handlers
- def on_environment_toggled(self, renderer, path):
+ def on_button_toggled(self, radio, row):
+ row.activate()
+
+ def on_environment_activated(self, listbox, row):
if not self._selectFlag:
return
- # First, mark every row as unselected so the radio button on whatever
- # row was previously selected will be cleared out.
- for row in self._environmentStore:
- row[0] = False
+ box = row.get_children()[0]
+ button = box.get_children()[0]
- # Then, remove all the groups that were selected by the previously
+ button.handler_block_by_func(self.on_button_toggled)
+ button.set_active(not button.get_active())
+ button.handler_unblock_by_func(self.on_button_toggled)
Does this
(un)blocking really work? I believe I've seen some issues with
it in other places of our code and the documentation says:
Blocks all handlers on an instance that match func and data.
instance :
The instance to block handlers from.
func :
The C closure callback of the handlers (useless for non-C closures).
data :
The closure data of the handlers' closures.
Returns :
The number of handlers that matched.
If it works, I'm looking forward to start using it and we could file a
bug on that docs.
--
Vratislav Podzimek
Anaconda Rider | Red Hat, Inc. | Brno - Czech Republic