Changes since last time: - Fixed the stuff that Vratislav pointed out: * Added a macro for the sidebar width percentage * Changed finalize to dispose since that's more in the spirit of reference releases * Fixed a couple of comments * Changed Spoke._visitedSinceApplied to a public property * Added MainWindow to ANACONDA_WINDOW_GROUP. Added the exception window to it, too, since there is no longer a separate Gtk.main loop for spokes and no separate spoke window to get stuck under.
- Fixed makeupdates to work with the widgets version increase
- Increased the version of the .gir and .typelib files too since that seems like the thing to do
- Replaced the lightbox with something hopefully less fragile
- Removed the resize on realize part from MainWindow since it turns out it didn't actually work
- Updated initial-setup for the new HubWindow
- Added a couple more deprecation warning ignores since I apparently I missed some last time
David Shea (10): Add a couple more deprecation warning ignores Remove the custom accelerators from custom storage. Use globs for the anaconda widgets library paths Increased the version of anaconda-widgets to 3.0 Add a class BaseStandalone. Add a window to manage Anaconda screen transitions. Add a delete-event handler for the main window Added a method to create new GdkPixbufs from in-memory data Implement the lightbox in MainWindow Remove the Lightbox widget
po/POTFILES.in | 2 +- pyanaconda/ui/common.py | 2 +- pyanaconda/ui/gui/__init__.py | 212 ++++++++++-- pyanaconda/ui/gui/hubs/__init__.py | 91 ++--- pyanaconda/ui/gui/hubs/progress.glade | 5 +- pyanaconda/ui/gui/hubs/progress.py | 11 +- pyanaconda/ui/gui/hubs/summary.glade | 6 +- pyanaconda/ui/gui/hubs/summary.py | 7 - pyanaconda/ui/gui/spokes/__init__.py | 19 +- pyanaconda/ui/gui/spokes/custom.glade | 3 +- pyanaconda/ui/gui/spokes/custom.py | 24 +- pyanaconda/ui/gui/spokes/datetime_spoke.py | 4 +- pyanaconda/ui/gui/spokes/filter.py | 7 +- pyanaconda/ui/gui/spokes/keyboard.py | 8 +- .../ui/gui/spokes/lib/custom_storage_helpers.glade | 1 - pyanaconda/ui/gui/spokes/network.py | 8 +- pyanaconda/ui/gui/spokes/software.py | 4 +- pyanaconda/ui/gui/spokes/source.py | 10 +- pyanaconda/ui/gui/spokes/storage.py | 8 +- pyanaconda/ui/gui/spokes/user.py | 3 +- pyanaconda/ui/gui/spokes/welcome.py | 10 +- pyanaconda/ui/gui/tools/run-spoke.py | 7 +- pyanaconda/ui/gui/utils.py | 15 +- scripts/makeupdates | 12 +- widgets/configure.ac | 2 +- widgets/doc/AnacondaWidgets-docs.xml | 2 +- widgets/glade/AnacondaWidgets.xml | 13 +- widgets/src/BaseStandalone.c | 377 +++++++++++++++++++++ widgets/src/BaseStandalone.h | 81 +++++ widgets/src/BaseWindow.c | 14 +- widgets/src/BaseWindow.h | 6 +- widgets/src/HubWindow.c | 94 +---- widgets/src/HubWindow.h | 6 +- widgets/src/LayoutIndicator.c | 2 + widgets/src/Lightbox.c | 360 -------------------- widgets/src/Lightbox.h | 67 ---- widgets/src/Makefile.am | 20 +- widgets/src/SpokeWindow.c | 29 +- widgets/src/StandaloneWindow.c | 242 +++---------- widgets/src/StandaloneWindow.h | 15 +- widgets/src/widgets-common.c | 51 ++- widgets/src/widgets-common.h | 3 + 42 files changed, 893 insertions(+), 970 deletions(-) create mode 100644 widgets/src/BaseStandalone.c create mode 100644 widgets/src/BaseStandalone.h delete mode 100644 widgets/src/Lightbox.c delete mode 100644 widgets/src/Lightbox.h
Missed these two in the last batch --- widgets/src/HubWindow.c | 2 ++ widgets/src/LayoutIndicator.c | 2 ++ 2 files changed, 4 insertions(+)
diff --git a/widgets/src/HubWindow.c b/widgets/src/HubWindow.c index b7a0ed6..d1b4e56 100644 --- a/widgets/src/HubWindow.c +++ b/widgets/src/HubWindow.c @@ -210,8 +210,10 @@ static void anaconda_hub_window_init(AnacondaHubWindow *win) { gtk_box_pack_start(GTK_BOX(action_area), win->priv->scrolled_window, TRUE, TRUE, 0);
/* The hub has different alignment requirements than a spoke. */ +G_GNUC_BEGIN_IGNORE_DEPRECATIONS gtk_alignment_set(GTK_ALIGNMENT(anaconda_base_window_get_alignment(ANACONDA_BASE_WINDOW(win))), 0.5, 0.0, 0.5, 1.0); +G_GNUC_END_IGNORE_DEPRECATIONS }
/** diff --git a/widgets/src/LayoutIndicator.c b/widgets/src/LayoutIndicator.c index 49f584f..cebc21d 100644 --- a/widgets/src/LayoutIndicator.c +++ b/widgets/src/LayoutIndicator.c @@ -217,7 +217,9 @@ static void anaconda_layout_indicator_init(AnacondaLayoutIndicator *self) { gtk_label_set_max_width_chars(self->priv->layout_label, DEFAULT_LABEL_MAX_CHAR_WIDTH); gtk_label_set_width_chars(self->priv->layout_label, DEFAULT_LABEL_MAX_CHAR_WIDTH); gtk_label_set_ellipsize(self->priv->layout_label, PANGO_ELLIPSIZE_END); +G_GNUC_BEGIN_IGNORE_DEPRECATIONS gtk_misc_set_alignment(GTK_MISC(self->priv->layout_label), 0.0, 0.5); +G_GNUC_END_IGNORE_DEPRECATIONS
/* initialize the label with the current layout name */ anaconda_layout_indicator_refresh_ui_elements(self);
These accelerators are already covered by the underline mnemonics, and having them defined separately as accelerators on the entry widgets makes them untranslatable and potentially conflict with the translated mnemonics. --- pyanaconda/ui/gui/spokes/custom.glade | 3 +-- pyanaconda/ui/gui/spokes/lib/custom_storage_helpers.glade | 1 - 2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/pyanaconda/ui/gui/spokes/custom.glade b/pyanaconda/ui/gui/spokes/custom.glade index 4a4900a..a8004a7 100644 --- a/pyanaconda/ui/gui/spokes/custom.glade +++ b/pyanaconda/ui/gui/spokes/custom.glade @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.18.1 --> +<!-- Generated with glade 3.18.3 --> <interface> <requires lib="gtk+" version="3.2"/> <requires lib="AnacondaWidgets" version="1.0"/> @@ -744,7 +744,6 @@ until you click on the main menu's 'Begin Installation' button.</property> <property name="can_focus">True</property> <property name="visibility">False</property> <property name="invisible_char">●</property> - <accelerator key="p" signal="grab-focus" modifiers="GDK_MOD1_MASK"/> <signal name="activate" handler="on_unlock_clicked" swapped="no"/> </object> <packing> diff --git a/pyanaconda/ui/gui/spokes/lib/custom_storage_helpers.glade b/pyanaconda/ui/gui/spokes/lib/custom_storage_helpers.glade index ae9baca..43ddba9 100644 --- a/pyanaconda/ui/gui/spokes/lib/custom_storage_helpers.glade +++ b/pyanaconda/ui/gui/spokes/lib/custom_storage_helpers.glade @@ -746,7 +746,6 @@ use. Try something else?</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">●</property> - <accelerator key="n" signal="grab-focus" modifiers="GDK_MOD1_MASK"/> </object> <packing> <property name="expand">False</property>
--- scripts/makeupdates | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/scripts/makeupdates b/scripts/makeupdates index 1de193a..f323f01 100755 --- a/scripts/makeupdates +++ b/scripts/makeupdates @@ -391,17 +391,17 @@ def copyUpdatedWidgets(updates, srcdir, builddir):
os.system('make -C %s' % builddir)
- files = ["libAnacondaWidgets.so", "libAnacondaWidgets.so.1", "libAnacondaWidgets.so.1.0.0"] - for f in files: - path = os.path.normpath(builddir + "/widgets/src/.libs/" + f) + libglob = os.path.normpath(builddir + "/widgets/src/.libs") + "/libAnacondaWidgets.so*" + for path in glob.glob(libglob): if os.path.islink(path) and not os.path.exists(updates + libdir + os.path.basename(path)): os.symlink(os.readlink(path), updates + libdir + os.path.basename(path)) elif os.path.isfile(path): shutil.copy2(path, updates + libdir)
- typelib = os.path.realpath(builddir + "/widgets/src/AnacondaWidgets-1.0.typelib") - if os.path.isfile(typelib): - shutil.copy2(typelib, updates + libdir + "girepository-1.0") + typeglob = os.path.realpath(builddir + "/widgets/src") + "/AnacondaWidgets-*.typelib" + for typelib in glob.glob(typeglob): + if os.path.isfile(typelib): + shutil.copy2(typelib, updates + libdir + "girepository-1.0")
def copyTranslations(updates, srcdir, builddir): localedir = "/usr/share/locale/"
Bumped the soversion and the glade catalog version. Increased the version of the gir and typelib files to 3.0. We were probably supposed to these things with the last soversion bump too, oops. --- widgets/configure.ac | 2 +- widgets/glade/AnacondaWidgets.xml | 3 ++- widgets/src/Makefile.am | 16 ++++++++-------- 3 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/widgets/configure.ac b/widgets/configure.ac index d24f8eb..49c4104 100644 --- a/widgets/configure.ac +++ b/widgets/configure.ac @@ -20,7 +20,7 @@ #
AC_PREREQ([2.63]) -AC_INIT([AnacondaWidgets], [2.0], [clumens@redhat.com]) +AC_INIT([AnacondaWidgets], [3.0], [clumens@redhat.com]) AM_INIT_AUTOMAKE([foreign]) AM_SILENT_RULES([yes])
diff --git a/widgets/glade/AnacondaWidgets.xml b/widgets/glade/AnacondaWidgets.xml index 5cf298e..b061609 100644 --- a/widgets/glade/AnacondaWidgets.xml +++ b/widgets/glade/AnacondaWidgets.xml @@ -1,5 +1,6 @@ <glade-catalog name="AnacondaWidgets" - version="1.0" + version="3.0" + targetable="2.0,1.0" library="AnacondaWidgets" domain="glade-3" depends="gtk+"> diff --git a/widgets/src/Makefile.am b/widgets/src/Makefile.am index 28fa27c..ebeca9a 100644 --- a/widgets/src/Makefile.am +++ b/widgets/src/Makefile.am @@ -65,7 +65,7 @@ libAnacondaWidgets_la_CFLAGS = $(GTK_CFLAGS) $(GLADEUI_CFLAGS) $(LIBXKLAVIER_CFL -DWIDGETS_DATADIR=$(WIDGETSDATA)\ -DTZMAP_DATADIR=$(TZMAPDATA) libAnacondaWidgets_la_LIBADD = $(GTK_LIBS) $(GLADEUI_LIBS) $(LIBXKLAVIER_LIBS) -libAnacondaWidgets_la_LDFLAGS = $(LTLIBINTL) -version-info 1:0:0 +libAnacondaWidgets_la_LDFLAGS = $(LTLIBINTL) -version-info 2:0:0 libAnacondaWidgets_la_SOURCES = $(SOURCES) $(HDRS) \ glade-adaptor.c
@@ -75,18 +75,18 @@ lib_include_HEADERS = $(HDRS) MAINTAINERCLEANFILES = gettext.h
if HAVE_INTROSPECTION -AnacondaWidgets-1.0.gir: libAnacondaWidgets.la +AnacondaWidgets-3.0.gir: libAnacondaWidgets.la
-AnacondaWidgets_1_0_gir_FILES = $(GISOURCES) $(GIHDRS) -AnacondaWidgets_1_0_gir_LIBS = libAnacondaWidgets.la -AnacondaWidgets_1_0_gir_SCANNERFLAGS = --warn-all --identifier-prefix=Anaconda --symbol-prefix=anaconda -AnacondaWidgets_1_0_gir_INCLUDES = Gtk-3.0 Xkl-1.0 +AnacondaWidgets_3_0_gir_FILES = $(GISOURCES) $(GIHDRS) +AnacondaWidgets_3_0_gir_LIBS = libAnacondaWidgets.la +AnacondaWidgets_3_0_gir_SCANNERFLAGS = --warn-all --identifier-prefix=Anaconda --symbol-prefix=anaconda +AnacondaWidgets_3_0_gir_INCLUDES = Gtk-3.0 Xkl-1.0
-INTROSPECTION_GIRS = AnacondaWidgets-1.0.gir +INTROSPECTION_GIRS = AnacondaWidgets-3.0.gir
typelibdir = $(libdir)/girepository-1.0 typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
-CLEANFILES = AnacondaWidgets-1.0.gir $(typelib_DATA) +CLEANFILES = AnacondaWidgets-3.0.gir $(typelib_DATA) MAINTAINERCLEANFILES += Makefile.in endif
This class is an abstract class containing the shared parts of HubWindow and StandaloneWindow. It provides quit-button and continue-button properties, quit-clicked and continue-clicked signals emitted when the buttons are pressed, and the sidebar sizing and drawing methods.
Glade files using StandaloneWindow do not require any change. Glade files using HubWindow may need to be modified to connect the quit-button or continue-button properties.
On the python side, this removes the need for the register_event_cb proxy for quit and continue events. Connect GraphicalUserInterface directly to the now-shared signals. --- po/POTFILES.in | 1 + pyanaconda/ui/gui/__init__.py | 17 +- pyanaconda/ui/gui/hubs/__init__.py | 27 +-- pyanaconda/ui/gui/hubs/progress.glade | 5 +- pyanaconda/ui/gui/hubs/progress.py | 11 +- pyanaconda/ui/gui/hubs/summary.glade | 6 +- pyanaconda/ui/gui/hubs/summary.py | 7 - pyanaconda/ui/gui/spokes/__init__.py | 12 +- pyanaconda/ui/gui/spokes/network.py | 4 +- pyanaconda/ui/gui/spokes/welcome.py | 4 +- pyanaconda/ui/gui/tools/run-spoke.py | 7 +- widgets/doc/AnacondaWidgets-docs.xml | 1 + widgets/glade/AnacondaWidgets.xml | 10 + widgets/src/BaseStandalone.c | 377 ++++++++++++++++++++++++++++++++++ widgets/src/BaseStandalone.h | 81 ++++++++ widgets/src/HubWindow.c | 83 +------- widgets/src/HubWindow.h | 6 +- widgets/src/Makefile.am | 2 + widgets/src/StandaloneWindow.c | 221 +++++--------------- widgets/src/StandaloneWindow.h | 15 +- 20 files changed, 569 insertions(+), 328 deletions(-) create mode 100644 widgets/src/BaseStandalone.c create mode 100644 widgets/src/BaseStandalone.h
diff --git a/po/POTFILES.in b/po/POTFILES.in index 943d15a..fc832df 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -99,6 +99,7 @@ pyanaconda/ui/gui/spokes/lib/summary.py
# Custom widgets. widgets/src/BaseWindow.c +widgets/src/BaseStandalone.c widgets/src/DiskOverview.c widgets/src/HubWindow.c widgets/src/MountpointSelector.c diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py index 2a1dad2..604a3b5 100644 --- a/pyanaconda/ui/gui/__init__.py +++ b/pyanaconda/ui/gui/__init__.py @@ -370,8 +370,9 @@ class GraphicalUserInterface(UserInterface): del(obj) return None
- obj.register_event_cb("continue", self._on_continue_clicked) - obj.register_event_cb("quit", self._on_quit_clicked) + # Use connect_after so classes can add actions before we change screens + obj.window.connect_after("continue-clicked", self._on_continue_clicked) + obj.window.connect_after("quit-clicked", self._on_quit_clicked)
return obj
@@ -471,7 +472,10 @@ class GraphicalUserInterface(UserInterface): ### ### SIGNAL HANDLING METHODS ### - def _on_continue_clicked(self): + def _on_continue_clicked(self, win, user_data=None): + if not win.get_may_continue(): + return + # If we're on the last screen, clicking Continue quits. if len(self._actions) == 1: Gtk.main_quit() @@ -515,7 +519,7 @@ class GraphicalUserInterface(UserInterface): if not nextAction.showable: self._currentAction.window.hide() self._actions.pop(0) - self._on_continue_clicked() + self._on_continue_clicked(nextAction) return
self._currentAction.exit_logger() @@ -530,7 +534,10 @@ class GraphicalUserInterface(UserInterface): self._currentAction = nextAction self._actions.pop(0)
- def _on_quit_clicked(self): + def _on_quit_clicked(self, win, userData=None): + if not win.get_quit_button(): + return + dialog = self._quitDialog(None) with enlightbox(self._currentAction.window, dialog.window): rc = dialog.run() diff --git a/pyanaconda/ui/gui/hubs/__init__.py b/pyanaconda/ui/gui/hubs/__init__.py index b42bd91..3645759 100644 --- a/pyanaconda/ui/gui/hubs/__init__.py +++ b/pyanaconda/ui/gui/hubs/__init__.py @@ -273,10 +273,7 @@ class Hub(GUIObject, common.Hub): return len(self._incompleteSpokes) == 0 and len(self._notReadySpokes) == 0 and getattr(self._checker, "success", True)
def _updateContinueButton(self): - if not self.continueButton: - return - - self.continueButton.set_sensitive(self.continuePossible) + self.window.set_may_continue(self.continuePossible)
def _update_spokes(self): from pyanaconda.ui.communication import hubQ @@ -284,10 +281,10 @@ class Hub(GUIObject, common.Hub):
q = hubQ.q
- if not self._spokes and self.continueButton: + if not self._spokes and self.window.get_may_continue(): # no spokes, move on log.info("no spokes available on %s, continuing automatically", self) - gtk_call_once(self.continueButton.emit, "clicked") + gtk_call_once(self.window.emit, "continue-clicked")
click_continue = False # Grab all messages that may have appeared since last time this method ran. @@ -347,10 +344,10 @@ class Hub(GUIObject, common.Hub): q.task_done()
# queue is now empty, should continue be clicked? - if self._autoContinue and click_continue and self.continueButton: + if self._autoContinue and click_continue and self.window.get_may_continue(): # enqueue the emit to the Gtk message queue log.info("_autoContinue clicking continue button") - gtk_call_once(self.continueButton.emit, "clicked") + gtk_call_once(self.window.emit, "continue-clicked")
return True
@@ -360,22 +357,8 @@ class Hub(GUIObject, common.Hub):
GLib.timeout_add(100, self._update_spokes)
- @property - def continueButton(self): - return None - - @property - def quitButton(self): - return None - ### SIGNAL HANDLERS
- def register_event_cb(self, event, cb): - if event == "continue" and self.continueButton: - self.continueButton.connect("clicked", lambda *args: cb()) - elif event == "quit" and self.quitButton: - self.quitButton.connect("clicked", lambda *args: cb()) - def _on_spoke_clicked(self, selector, event, spoke): from gi.repository import Gdk
diff --git a/pyanaconda/ui/gui/hubs/progress.glade b/pyanaconda/ui/gui/hubs/progress.glade index b15e253..1dd82f4 100644 --- a/pyanaconda/ui/gui/hubs/progress.glade +++ b/pyanaconda/ui/gui/hubs/progress.glade @@ -1,11 +1,12 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.18.1 --> +<!-- Generated with glade 3.18.3 --> <interface> <requires lib="gtk+" version="3.0"/> - <requires lib="AnacondaWidgets" version="1.0"/> + <requires lib="AnacondaWidgets" version="3.0"/> <object class="AnacondaHubWindow" id="progressWindow"> <property name="can_focus">False</property> <property name="window_name" translatable="yes">CONFIGURATION</property> + <property name="continue_button">rebootButton</property> <child internal-child="main_box"> <object class="GtkBox" id="AnacondaHubWindow-main_box1"> <property name="can_focus">False</property> diff --git a/pyanaconda/ui/gui/hubs/progress.py b/pyanaconda/ui/gui/hubs/progress.py index 1c33abd..2fa9dac 100644 --- a/pyanaconda/ui/gui/hubs/progress.py +++ b/pyanaconda/ui/gui/hubs/progress.py @@ -133,8 +133,7 @@ class ProgressHub(Hub):
# kickstart install, continue automatically if reboot or shutdown selected if flags.automatedInstall and self.data.reboot.action in [KS_REBOOT, KS_SHUTDOWN]: - self.continueButton.emit("clicked") - + self.window.emit("continue-clicked")
def _install_done(self): # package installation done, check personalization spokes @@ -208,7 +207,7 @@ class ProgressHub(Hub): continueText.set_text(_("%s is now successfully installed on your system and ready " "for you to use! When you are ready, reboot your system to start using it!")) continueText.set_line_wrap(True) - self.continueButton.set_label(C_("GUI|Progress", "_Quit")) + self.window.get_continue_button().set_label(C_("GUI|Progress", "_Quit"))
self._progressBar = self.builder.get_object("progressBar") self._progressLabel = self.builder.get_object("progressLabel") @@ -258,14 +257,10 @@ class ProgressHub(Hub):
def _updateContinueButton(self): if self._configurationDone: - self.continueButton.set_sensitive(self.continuePossible) + self.window.set_may_continue(self.continuePossible) else: self.builder.get_object("configureButton").set_sensitive(self.continuePossible)
- @property - def continueButton(self): - return self.builder.get_object("rebootButton") - def _init_progress_bar(self, steps): self._totalSteps = steps self._currentStep = 0 diff --git a/pyanaconda/ui/gui/hubs/summary.glade b/pyanaconda/ui/gui/hubs/summary.glade index 3d896a9..cae6cb7 100644 --- a/pyanaconda/ui/gui/hubs/summary.glade +++ b/pyanaconda/ui/gui/hubs/summary.glade @@ -1,11 +1,13 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.18.1 --> +<!-- Generated with glade 3.18.3 --> <interface> <requires lib="gtk+" version="3.0"/> - <requires lib="AnacondaWidgets" version="1.0"/> + <requires lib="AnacondaWidgets" version="3.0"/> <object class="AnacondaHubWindow" id="summaryWindow"> <property name="can_focus">False</property> <property name="window_name" translatable="yes">INSTALLATION SUMMARY</property> + <property name="quit_button">quitButton</property> + <property name="continue_button">continueButton</property> <child internal-child="main_box"> <object class="GtkBox" id="AnacondaHubWindow-main_box1"> <property name="can_focus">False</property> diff --git a/pyanaconda/ui/gui/hubs/summary.py b/pyanaconda/ui/gui/hubs/summary.py index 2615377..28f93be 100644 --- a/pyanaconda/ui/gui/hubs/summary.py +++ b/pyanaconda/ui/gui/hubs/summary.py @@ -59,10 +59,3 @@ class SummaryHub(Hub): else: self._checker = DirInstallSpaceChecker(storage, payload)
- @property - def continueButton(self): - return self.builder.get_object("continueButton") - - @property - def quitButton(self): - return self.builder.get_object("quitButton") diff --git a/pyanaconda/ui/gui/spokes/__init__.py b/pyanaconda/ui/gui/spokes/__init__.py index 658e6f7..9a620b6 100644 --- a/pyanaconda/ui/gui/spokes/__init__.py +++ b/pyanaconda/ui/gui/spokes/__init__.py @@ -67,15 +67,11 @@ class StandaloneSpoke(Spoke, common.StandaloneSpoke): Spoke.__init__(self, data) common.StandaloneSpoke.__init__(self, data, storage, payload, instclass)
- def _on_continue_clicked(self, cb): + # Add a continue-clicked handler to save the data before leaving the window + self.window.connect("continue-clicked", self._on_continue_clicked) + + def _on_continue_clicked(self, win, user_data=None): self.apply() - cb() - - def register_event_cb(self, event, cb): - if event == "continue": - self.window.connect("continue-clicked", lambda *args: self._on_continue_clicked(cb)) - elif event == "quit": - self.window.connect("quit-clicked", lambda *args: cb())
# Inherit abstract methods from common.NormalSpoke # pylint: disable=abstract-method diff --git a/pyanaconda/ui/gui/spokes/network.py b/pyanaconda/ui/gui/spokes/network.py index 2275c6e..73390e1 100644 --- a/pyanaconda/ui/gui/spokes/network.py +++ b/pyanaconda/ui/gui/spokes/network.py @@ -1446,7 +1446,7 @@ class NetworkStandaloneSpoke(StandaloneSpoke): StandaloneSpoke.refresh(self) self.network_control_box.refresh()
- def _on_continue_clicked(self, cb): + def _on_continue_clicked(self, window, user_data=None): hostname = self.network_control_box.hostname (valid, error) = network.sanityCheckHostname(hostname) if not valid: @@ -1457,7 +1457,7 @@ class NetworkStandaloneSpoke(StandaloneSpoke): self.window.show_all() else: self.clear_info() - StandaloneSpoke._on_continue_clicked(self, cb) + StandaloneSpoke._on_continue_clicked(self, window, user_data)
# Use case: slow dhcp has connected when on spoke def on_nm_state_changed(self, *args): diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py index 31603b8..5c0ff83 100644 --- a/pyanaconda/ui/gui/spokes/welcome.py +++ b/pyanaconda/ui/gui/spokes/welcome.py @@ -305,7 +305,7 @@ class WelcomeLanguageSpoke(LangLocaleHandler, StandaloneSpoke):
# Override the default in StandaloneSpoke so we can display the beta # warning dialog first. - def _on_continue_clicked(self, cb): + def _on_continue_clicked(self, window, user_data=None): # Don't display the betanag dialog if this is the final release. if not isFinal: dlg = self.builder.get_object("betaWarnDialog") @@ -324,4 +324,4 @@ class WelcomeLanguageSpoke(LangLocaleHandler, StandaloneSpoke): if rc != 1: sys.exit(0)
- StandaloneSpoke._on_continue_clicked(self, cb) + StandaloneSpoke._on_continue_clicked(self, window, user_data) diff --git a/pyanaconda/ui/gui/tools/run-spoke.py b/pyanaconda/ui/gui/tools/run-spoke.py index 4aa3d96..4edaacd 100755 --- a/pyanaconda/ui/gui/tools/run-spoke.py +++ b/pyanaconda/ui/gui/tools/run-spoke.py @@ -96,10 +96,11 @@ instclass = DefaultInstall() payload = YumPayload(ksdata) payload.setup(storage)
+from pyanaconda.ui.gui.spokes import StandaloneSpoke spoke = spokeClass(ksdata, storage, payload, instclass) -if hasattr(spoke, "register_event_cb"): - spoke.register_event_cb("continue", Gtk.main_quit) - spoke.register_event_cb("quit", Gtk.main_quit) +if isinstance(spoke, StandaloneSpoke): + spoke.window.connect_after("continue-clicked", Gtk.main_quit) + spoke.window.connect("quit-clicked", Gtk.main_quit)
if hasattr(spoke, "set_path"): spoke.set_path("categories", [ diff --git a/widgets/doc/AnacondaWidgets-docs.xml b/widgets/doc/AnacondaWidgets-docs.xml index 5588b07..7282a24 100644 --- a/widgets/doc/AnacondaWidgets-docs.xml +++ b/widgets/doc/AnacondaWidgets-docs.xml @@ -24,6 +24,7 @@ <chapter id="windows"> <title>Window Widgets</title> <xi:include href="xml/BaseWindow.xml" /> + <xi:include href="xml/BaseStandalone.xml" /> <xi:include href="xml/HubWindow.xml" /> <xi:include href="xml/SpokeWindow.xml" /> <xi:include href="xml/StandaloneWindow.xml" /> diff --git a/widgets/glade/AnacondaWidgets.xml b/widgets/glade/AnacondaWidgets.xml index b061609..42c4194 100644 --- a/widgets/glade/AnacondaWidgets.xml +++ b/widgets/glade/AnacondaWidgets.xml @@ -56,6 +56,16 @@ <!-- Not a typo - we really can use the same functions here (for now) --> <post-create-function>anaconda_standalone_window_post_create</post-create-function>
+ <properties> + <property since="3.0" id="quit-button" /> + <property since="3.0" id="continue-button" /> + </properties> + + <signals> + <signal since="3.0" id="quit-clicked" /> + <signal since="3.0" id="continue-clicked" /> + </signals> + <internal-children> <object name="main_box"> <object name="nav_box"> diff --git a/widgets/src/BaseStandalone.c b/widgets/src/BaseStandalone.c new file mode 100644 index 0000000..ab4bb05 --- /dev/null +++ b/widgets/src/BaseStandalone.c @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + * Author: David Shea dshea@redhat.com + */ + +#include "BaseWindow.h" +#include "BaseStandalone.h" +#include "intl.h" + +/** + * SECTION: BaseStandalone + * @title: AnacondaBaseStandalone + * @short_description: Abstract base class for standalone Anaconda windows. + * + * #AnacondaBaseStandalone is an abstract base class for standalone windows + * in Anaconda; i.e., windows that do not appear in or depend upon a hub. + * A #AnacondaBaseStandalone can continue to the next #AnacondaBaseStandalone + * or quit the installer. + * + * Since: 3.0 + */ + +#define STANDALONE_SIDEBAR_WIDTH_PCT (0.15) + +enum { + SIGNAL_QUIT_CLICKED, + SIGNAL_CONTINUE_CLICKED, + LAST_SIGNAL +}; + +static guint standalone_signals[LAST_SIGNAL] = { 0 }; + +enum { + PROP_QUIT_BUTTON = 1, + PROP_CONTINUE_BUTTON +}; + +struct _AnacondaBaseStandalonePrivate { + GtkWidget *quit_button, *continue_button; + gulong quit_clicked_handler_id, continue_clicked_handler_id; +}; + +static void anaconda_base_standalone_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); +static void anaconda_base_standalone_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static void anaconda_base_standalone_size_allocate(GtkWidget *window, GtkAllocation *allocation); +static gboolean anaconda_base_standalone_on_draw(GtkWidget *window, cairo_t *cr); +static void anaconda_base_standalone_dispose(GObject *object); +static void anaconda_base_standalone_set_quit_button(AnacondaBaseStandalone *win, GtkButton *button); +static void anaconda_base_standalone_set_continue_button(AnacondaBaseStandalone *win, GtkButton *button); +static void anaconda_base_standalone_quit_clicked(GtkButton *button, gpointer user_data); +static void anaconda_base_standalone_continue_clicked(GtkButton *button, gpointer user_data); + +G_DEFINE_ABSTRACT_TYPE(AnacondaBaseStandalone, anaconda_base_standalone, ANACONDA_TYPE_BASE_WINDOW) + +static void anaconda_base_standalone_class_init(AnacondaBaseStandaloneClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + + object_class->set_property = anaconda_base_standalone_set_property; + object_class->get_property = anaconda_base_standalone_get_property; + object_class->dispose = anaconda_base_standalone_dispose; + + widget_class->draw = anaconda_base_standalone_on_draw; + widget_class->size_allocate = anaconda_base_standalone_size_allocate; + + /** + * AnacondaBaseStandalone::quit-clicked: + * @window: the window that received the signal + * + * Emitted when the quit button has been activated (pressed and released). + * + * Since: 3.0 + */ + standalone_signals[SIGNAL_QUIT_CLICKED] = g_signal_new("quit-clicked", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET(AnacondaBaseStandaloneClass, quit_clicked), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /** + * AnacondaBaseStandalone::continue-clicked: + * @window: the window that received the signal + * + * Emitted when the continue button has been activated (pressed and released). + * + * Since: 3.0 + */ + standalone_signals[SIGNAL_CONTINUE_CLICKED] = g_signal_new("continue-clicked", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET(AnacondaBaseStandaloneClass, continue_clicked), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /* + * These two properties can't be CONSTRUCT_ONLY, since GtkBuilder will normally + * set them after construction. For a window that sets its quit-button or continue-button + * property to a GtkButton defined as a child of the window, Builder will construct + * the window, then construct the Button and set the property on the window. + */ + + /** + * AnacondaBaseStandalone::quit-button: + * + * The button to quit anaconda. + * + * Since: 3.0 + */ + g_object_class_install_property(object_class, + PROP_QUIT_BUTTON, + g_param_spec_object("quit-button", + P_("Quit button"), + P_("The button to quit Anaconda"), + GTK_TYPE_BUTTON, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + /** + * AnacondaBaseStandalone::continue-button: + * + * The button to continue to the next window. + * + * Since: 3.0 + */ + g_object_class_install_property(object_class, + PROP_CONTINUE_BUTTON, + g_param_spec_object("continue-button", + P_("Continue button"), + P_("The button to continue to the next window"), + GTK_TYPE_BUTTON, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_type_class_add_private(object_class, sizeof(AnacondaBaseStandalonePrivate)); +} + +static void anaconda_base_standalone_init(AnacondaBaseStandalone *win) { + win->priv = G_TYPE_INSTANCE_GET_PRIVATE(win, + ANACONDA_TYPE_BASE_STANDALONE, + AnacondaBaseStandalonePrivate); +} + +static void anaconda_base_standalone_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { + AnacondaBaseStandalone *win = ANACONDA_BASE_STANDALONE(object); + AnacondaBaseStandalonePrivate *priv = win->priv; + + switch (prop_id) { + case PROP_QUIT_BUTTON: + g_value_set_object(value, priv->quit_button); + break; + case PROP_CONTINUE_BUTTON: + g_value_set_object(value, priv->continue_button); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void anaconda_base_standalone_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { + AnacondaBaseStandalone *win = ANACONDA_BASE_STANDALONE(object); + + switch (prop_id) { + case PROP_QUIT_BUTTON: + anaconda_base_standalone_set_quit_button(win, g_value_get_object(value)); + break; + case PROP_CONTINUE_BUTTON: + anaconda_base_standalone_set_continue_button(win, g_value_get_object(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static int get_sidebar_width(GtkWidget *window) { + GtkAllocation allocation; + + /* change value below to make sidebar bigger / smaller */ + float sidebar_width_percentage = STANDALONE_SIDEBAR_WIDTH_PCT; + + gtk_widget_get_allocation(window, &allocation); + return allocation.width * sidebar_width_percentage; +} + +static int get_sidebar_height(GtkWidget *window) { + GtkAllocation allocation; + gtk_widget_get_allocation(window, &allocation); + return allocation.height; +} + +/* Move base window content appropriate amount of space to make room for sidebar */ +static void anaconda_base_standalone_size_allocate(GtkWidget *window, GtkAllocation *allocation) { + GtkAllocation child_allocation; + GtkWidget *child; + int sidebar_width; + + GTK_WIDGET_CLASS(anaconda_base_standalone_parent_class)->size_allocate(window, allocation); + + /* + * For RTL languages, the width is reduced by the same amount, but the + * start of the window does not need to move. + */ + gtk_widget_set_allocation(window, allocation); + sidebar_width = get_sidebar_width(window); + child_allocation.y = allocation->y; + child_allocation.width = allocation->width-sidebar_width; + child_allocation.height = allocation->height; + + if (gtk_get_locale_direction() == GTK_TEXT_DIR_LTR) + child_allocation.x = allocation->x+sidebar_width; + else + child_allocation.x = allocation->x; + + child = gtk_bin_get_child (GTK_BIN (window)); + if (child && gtk_widget_get_visible (child)) + gtk_widget_size_allocate (child, &child_allocation); +} + +/* function to override default drawing to insert sidebar image */ +static gboolean anaconda_base_standalone_on_draw(GtkWidget *win, cairo_t *cr) { + GtkStyleContext *context; + gdouble sidebar_x; + gdouble sidebar_width; + + /* calls parent class' draw handler */ + GTK_WIDGET_CLASS(anaconda_base_standalone_parent_class)->draw(win,cr); + + sidebar_width = get_sidebar_width(win); + + /* For RTL languages, move the sidebar to the right edge */ + if (gtk_get_locale_direction() == GTK_TEXT_DIR_LTR) { + sidebar_x = 0; + } else { + GtkAllocation allocation; + gtk_widget_get_allocation(win, &allocation); + sidebar_x = allocation.width - sidebar_width; + } + + context = gtk_widget_get_style_context(win); + gtk_style_context_save (context); + + gtk_style_context_add_class(context, "logo-sidebar"); + gtk_render_background(context, cr, sidebar_x, 0, sidebar_width, get_sidebar_height(win)); + gtk_style_context_remove_class(context, "logo-sidebar"); + + gtk_style_context_add_class(context, "logo"); + gtk_render_background(context, cr, sidebar_x, 0, sidebar_width, get_sidebar_height(win)); + gtk_style_context_remove_class(context, "logo"); + + gtk_style_context_restore (context); + + return TRUE; /* TRUE to avoid default draw handler */ +} + +static void anaconda_base_standalone_dispose(GObject *object) { + AnacondaBaseStandalone *win = ANACONDA_BASE_STANDALONE(object); + + anaconda_base_standalone_set_quit_button(win, NULL); + anaconda_base_standalone_set_continue_button(win, NULL); + + G_OBJECT_CLASS(anaconda_base_standalone_parent_class)->dispose(object); +} + +static void anaconda_base_standalone_set_quit_button(AnacondaBaseStandalone *win, GtkButton *button) { + AnacondaBaseStandalonePrivate *priv = win->priv; + + if (priv->quit_button) { + g_signal_handler_disconnect(priv->quit_button, priv->quit_clicked_handler_id); + g_object_unref(priv->quit_button); + } + + priv->quit_button = GTK_WIDGET(button); + + if (priv->quit_button) { + g_object_ref(priv->quit_button); + priv->quit_clicked_handler_id = g_signal_connect(priv->quit_button, "clicked", G_CALLBACK(anaconda_base_standalone_quit_clicked), win); + } +} + +static void anaconda_base_standalone_set_continue_button(AnacondaBaseStandalone *win, GtkButton *button) { + AnacondaBaseStandalonePrivate *priv = win->priv; + + if (priv->continue_button) { + g_signal_handler_disconnect(priv->continue_button, priv->continue_clicked_handler_id); + g_object_unref(priv->continue_button); + } + + priv->continue_button = GTK_WIDGET(button); + + if (priv->continue_button) { + g_object_ref(priv->continue_button); + priv->continue_clicked_handler_id = g_signal_connect(priv->continue_button, "clicked", G_CALLBACK(anaconda_base_standalone_continue_clicked), win); + } +} + +static void anaconda_base_standalone_quit_clicked(GtkButton *button, gpointer user_data) { + g_signal_emit(user_data, standalone_signals[SIGNAL_QUIT_CLICKED], 0); +} + +static void anaconda_base_standalone_continue_clicked(GtkButton *button, gpointer user_data) { + g_signal_emit(user_data, standalone_signals[SIGNAL_CONTINUE_CLICKED], 0); +} + +/** + * anaconda_base_standalone_get_may_continue: + * @win: a #AnacondaBaseStandalone + * + * Returns: Whether or not the continue button is sensitive (thus, whether the + * user may continue forward from this window). + * + * Since: 3.0 + */ +gboolean anaconda_base_standalone_get_may_continue(AnacondaBaseStandalone *win) { + if (win->priv->continue_button) { + return gtk_widget_get_sensitive(win->priv->continue_button); + } + return FALSE; +} + +/** + * anaconda_base_standalone_set_may_continue: + * @win: a #AnacondaBaseStandalone + * @may_continue: %TRUE if this window's continue buttons should be sensitive + * + * Specifies whether the user may continue forward from this window. If so, + * the continue button will be made sensitive. Windows default to continuable + * so you must set it as false if you want. The reason the user may not be + * able to continue is if there is required information the user must enter + * when no reasonable default may be given. + * + * Since: 3.0 + */ +void anaconda_base_standalone_set_may_continue(AnacondaBaseStandalone *win, gboolean may_continue) { + if (win->priv->continue_button) { + gtk_widget_set_sensitive(win->priv->continue_button, may_continue); + } +} + +/** + * anaconda_base_standalone_get_quit_button: + * @win: a #AnacondaBaseStandalone + * + * Returns: (transfer none): the quit button + * + * Since: 3.0 + */ +GtkButton * anaconda_base_standalone_get_quit_button(AnacondaBaseStandalone *win) { + return GTK_BUTTON(win->priv->quit_button); +} + +/** + * anaconda_base_standalone_get_continue_button: + * @win: a #AnacondaBaseStandalone + * + * Returns: (transfer none): the continue button + * + * Since: 3.0 + */ +GtkButton * anaconda_base_standalone_get_continue_button(AnacondaBaseStandalone *win) { + return GTK_BUTTON(win->priv->continue_button); +} diff --git a/widgets/src/BaseStandalone.h b/widgets/src/BaseStandalone.h new file mode 100644 index 0000000..5f2e663 --- /dev/null +++ b/widgets/src/BaseStandalone.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + * Author: David Shea dshea@redhat.com + */ + +#ifndef _BASE_STANDALONE_H +#define _BASE_STANDALONE_H + +#include <gtk/gtk.h> + +#include "BaseWindow.h" + +G_BEGIN_DECLS + +#define ANACONDA_TYPE_BASE_STANDALONE (anaconda_base_standalone_get_type()) +#define ANACONDA_BASE_STANDALONE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ANACONDA_TYPE_BASE_STANDALONE, AnacondaBaseStandalone)) +#define ANACONDA_IS_BASE_STANDALONE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ANACONDA_TYPE_BASE_STANDALONE)) +#define ANACONDA_BASE_STANDALONE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ANACONDA_TYPE_BASE_STANDALONE, AnacondaBaseStandaloneClass)) +#define ANACONDA_IS_BASE_STANDALONE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ANACONDA_TYPE_BASE_STANDALONE)) +#define ANACONDA_BASE_STANDALONE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ANACONDA_TYPE_BASE_STANDALONE, AnacondaBaseStandaloneClass)) + +typedef struct _AnacondaBaseStandalone AnacondaBaseStandalone; +typedef struct _AnacondaBaseStandaloneClass AnacondaBaseStandaloneClass; +typedef struct _AnacondaBaseStandalonePrivate AnacondaBaseStandalonePrivate; + +/** + * AnacondaBaseStandalone: + * + * The AnacondaBaseStandalone class contains only private fields and should not + * be directly accessed. + */ +struct _AnacondaBaseStandalone { + AnacondaBaseWindow parent; + + /*< private >*/ + AnacondaBaseStandalonePrivate *priv; +}; + +/** + * AnacondaBaseStandaloneClass: + * @parent_class: The object class structure needs to be the first element in + * the widget class structure in order for the class mechanism + * to work correctly. This allows a AnacondaBaseStandaloneClass + * pointer to be cast to a #AnacondaBaseWindowClass pointer. + * @quit_clicked: Function pointer called when the #AnacondaBaseStandalone::quit-clicked + * signal is emitted. + * @continue_clicked: Function pointer called when the #AnacondaBaseStandalone::continue-clicked + * signal is emitted. + */ +struct _AnacondaBaseStandaloneClass { + AnacondaBaseWindowClass parent_class; + + void (* quit_clicked) (AnacondaBaseStandalone *window); + void (* continue_clicked) (AnacondaBaseStandalone *window); +}; + +GType anaconda_base_standalone_get_type(void); + +gboolean anaconda_base_standalone_get_may_continue(AnacondaBaseStandalone *win); +void anaconda_base_standalone_set_may_continue(AnacondaBaseStandalone *win, gboolean may_continue); + +GtkButton * anaconda_base_standalone_get_quit_button(AnacondaBaseStandalone *win); +GtkButton * anaconda_base_standalone_get_continue_button(AnacondaBaseStandalone *win); + +G_END_DECLS + +#endif diff --git a/widgets/src/HubWindow.c b/widgets/src/HubWindow.c index d1b4e56..927dd3d 100644 --- a/widgets/src/HubWindow.c +++ b/widgets/src/HubWindow.c @@ -17,7 +17,7 @@ * Author: Chris Lumens clumens@redhat.com */
-#include "BaseWindow.h" +#include "BaseStandalone.h" #include "HubWindow.h" #include "intl.h"
@@ -97,91 +97,12 @@ struct _AnacondaHubWindowPrivate {
static void anaconda_hub_window_buildable_init(GtkBuildableIface *iface);
-G_DEFINE_TYPE_WITH_CODE(AnacondaHubWindow, anaconda_hub_window, ANACONDA_TYPE_BASE_WINDOW, +G_DEFINE_TYPE_WITH_CODE(AnacondaHubWindow, anaconda_hub_window, ANACONDA_TYPE_BASE_STANDALONE, G_IMPLEMENT_INTERFACE(GTK_TYPE_BUILDABLE, anaconda_hub_window_buildable_init))
-static int get_sidebar_width(GtkWidget *window) { - GtkAllocation allocation; - - /* change value below to make sidebar bigger / smaller */ - float sidebar_width_percentage = 0.15; - - gtk_widget_get_allocation(window, &allocation); - return allocation.width * sidebar_width_percentage; -} - -static int get_sidebar_height(GtkWidget *window) { - GtkAllocation allocation; - gtk_widget_get_allocation(window, &allocation); - return allocation.height; -} - -/* function to override default drawing to insert sidebar image */ -static gboolean anaconda_hub_window_on_draw(GtkWidget *win, cairo_t *cr) { - GtkStyleContext *context; - gdouble sidebar_x; - gdouble sidebar_width; - - /* calls parent class' draw handler */ - GTK_WIDGET_CLASS(anaconda_hub_window_parent_class)->draw(win,cr); - - sidebar_width = get_sidebar_width(win); - - /* For RTL languages, move the sidebar to the right edge */ - if (gtk_get_locale_direction() == GTK_TEXT_DIR_LTR) { - sidebar_x = 0; - } else { - GtkAllocation allocation; - gtk_widget_get_allocation(win, &allocation); - sidebar_x = allocation.width - sidebar_width; - } - - context = gtk_widget_get_style_context(win); - gtk_style_context_save (context); - - gtk_style_context_add_class(context, "logo-sidebar"); - gtk_render_background(context, cr, sidebar_x, 0, sidebar_width, get_sidebar_height(win)); - gtk_style_context_remove_class(context, "logo-sidebar"); - - gtk_style_context_add_class(context, "logo"); - gtk_render_background(context, cr, sidebar_x, 0, sidebar_width, get_sidebar_height(win)); - gtk_style_context_remove_class(context, "logo"); - - gtk_style_context_restore (context); - - return TRUE; /* TRUE to avoid default draw handler */ -} - -/* Move base window content appropriate amount of space to make room for sidebar */ -static void anaconda_hub_window_size_allocate (GtkWidget *window, GtkAllocation *allocation) { - GtkAllocation child_allocation; - GtkWidget *child; - int sidebar_width; - - GTK_WIDGET_CLASS(anaconda_hub_window_parent_class)->size_allocate(window, allocation); - - gtk_widget_set_allocation(window, allocation); - sidebar_width = get_sidebar_width(window); - child_allocation.y = allocation->y; - child_allocation.width = allocation->width-sidebar_width; - child_allocation.height = allocation->height; - - if (gtk_get_locale_direction() == GTK_TEXT_DIR_LTR) - child_allocation.x = allocation->x+sidebar_width; - else - child_allocation.x = allocation->x; - - child = gtk_bin_get_child (GTK_BIN (window)); - if (child && gtk_widget_get_visible (child)) - gtk_widget_size_allocate (child, &child_allocation); -} - static void anaconda_hub_window_class_init(AnacondaHubWindowClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
- widget_class->draw = anaconda_hub_window_on_draw; - widget_class->size_allocate = anaconda_hub_window_size_allocate; g_type_class_add_private(object_class, sizeof(AnacondaHubWindowPrivate)); }
diff --git a/widgets/src/HubWindow.h b/widgets/src/HubWindow.h index 8040eb8..9ff91e0 100644 --- a/widgets/src/HubWindow.h +++ b/widgets/src/HubWindow.h @@ -22,7 +22,7 @@
#include <gtk/gtk.h>
-#include "BaseWindow.h" +#include "BaseStandalone.h"
G_BEGIN_DECLS
@@ -44,7 +44,7 @@ typedef struct _AnacondaHubWindowPrivate AnacondaHubWindowPrivate; * be directly accessed. */ struct _AnacondaHubWindow { - AnacondaBaseWindow parent; + AnacondaBaseStandalone parent;
/*< private >*/ AnacondaHubWindowPrivate *priv; @@ -58,7 +58,7 @@ struct _AnacondaHubWindow { * pointer to be cast to an #AnacondaBaseWindow pointer. */ struct _AnacondaHubWindowClass { - AnacondaBaseWindowClass parent_class; + AnacondaBaseStandaloneClass parent_class; };
GType anaconda_hub_window_get_type (void); diff --git a/widgets/src/Makefile.am b/widgets/src/Makefile.am index ebeca9a..5862fcd 100644 --- a/widgets/src/Makefile.am +++ b/widgets/src/Makefile.am @@ -34,6 +34,7 @@ GISOURCES = BaseWindow.c \ StandaloneWindow.c \ LayoutIndicator.c \ Lightbox.c \ + BaseStandalone.c \ widgets-common.c
GIHDRS = BaseWindow.h \ @@ -45,6 +46,7 @@ GIHDRS = BaseWindow.h \ StandaloneWindow.h \ LayoutIndicator.h \ Lightbox.h \ + BaseStandalone.h \ widgets-common.h
NONGISOURCES = diff --git a/widgets/src/StandaloneWindow.c b/widgets/src/StandaloneWindow.c index 7e20270..4c489c2 100644 --- a/widgets/src/StandaloneWindow.c +++ b/widgets/src/StandaloneWindow.c @@ -17,7 +17,7 @@ * Author: Chris Lumens clumens@redhat.com */
-#include "BaseWindow.h" +#include "BaseStandalone.h" #include "StandaloneWindow.h" #include "intl.h"
@@ -46,155 +46,79 @@ * space. This is where widgets will be added and the user will do things. */
-enum { - SIGNAL_QUIT_CLICKED, - SIGNAL_CONTINUE_CLICKED, - LAST_SIGNAL -}; - #define QUIT_TEXT N_("_Quit") #define CONTINUE_TEXT N_("_Continue")
-static guint window_signals[LAST_SIGNAL] = { 0 }; - struct _AnacondaStandaloneWindowPrivate { GtkWidget *button_box; GtkWidget *continue_button, *quit_button; };
-static void anaconda_standalone_window_quit_clicked(GtkButton *button, - AnacondaStandaloneWindow *win); -static void anaconda_standalone_window_continue_clicked(GtkButton *button, - AnacondaStandaloneWindow *win); +enum { + PROP_QUIT_BUTTON = 1, + PROP_CONTINUE_BUTTON +}; + +static void anaconda_standalone_window_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void anaconda_standalone_window_realize(GtkWidget *widget, AnacondaStandaloneWindow *win);
-G_DEFINE_TYPE(AnacondaStandaloneWindow, anaconda_standalone_window, ANACONDA_TYPE_BASE_WINDOW) - -static int get_sidebar_width(GtkWidget *window) { - GtkAllocation allocation; - - /* change value below to make sidebar bigger / smaller */ - float sidebar_width_percentage = 0.15; - - gtk_widget_get_allocation(window, &allocation); - return allocation.width * sidebar_width_percentage; -} - -static int get_sidebar_height(GtkWidget *window) { - GtkAllocation allocation; - gtk_widget_get_allocation(window, &allocation); - return allocation.height; -} - -/* function to override default drawing to insert sidebar image */ -static gboolean anaconda_standalone_window_on_draw(GtkWidget *win, cairo_t *cr) { - GtkStyleContext *context; - gdouble sidebar_x; - gdouble sidebar_width; - - /* calls parent class' draw handler */ - GTK_WIDGET_CLASS(anaconda_standalone_window_parent_class)->draw(win,cr); - - sidebar_width = get_sidebar_width(win); - - /* For RTL languages, move the sidebar to the right edge */ - if (gtk_get_locale_direction() == GTK_TEXT_DIR_LTR) { - sidebar_x = 0; - } else { - GtkAllocation allocation; - gtk_widget_get_allocation(win, &allocation); - sidebar_x = allocation.width - sidebar_width; - } - - context = gtk_widget_get_style_context(win); - gtk_style_context_save (context); - - gtk_style_context_add_class(context, "logo-sidebar"); - gtk_render_background(context, cr, sidebar_x, 0, sidebar_width, get_sidebar_height(win)); - gtk_style_context_remove_class(context, "logo-sidebar"); - - gtk_style_context_add_class(context, "logo"); - gtk_render_background(context, cr, sidebar_x, 0, sidebar_width, get_sidebar_height(win)); - gtk_style_context_remove_class(context, "logo"); - - gtk_style_context_restore (context); - - return TRUE; /* TRUE to avoid default draw handler */ -} - -/* Move base window content appropriate amount of space to make room for sidebar */ -static void anaconda_standalone_window_size_allocate (GtkWidget *window, GtkAllocation *allocation) { - GtkAllocation child_allocation; - GtkWidget *child; - int sidebar_width; - - GTK_WIDGET_CLASS(anaconda_standalone_window_parent_class)->size_allocate(window, allocation); - - /* - * For RTL languages, the width is reduced by the same amount, but the - * start of the window does not need to move. - */ - gtk_widget_set_allocation(window, allocation); - sidebar_width = get_sidebar_width(window); - child_allocation.y = allocation->y; - child_allocation.width = allocation->width-sidebar_width; - child_allocation.height = allocation->height; - - if (gtk_get_locale_direction() == GTK_TEXT_DIR_LTR) - child_allocation.x = allocation->x+sidebar_width; - else - child_allocation.x = allocation->x; - - child = gtk_bin_get_child (GTK_BIN (window)); - if (child && gtk_widget_get_visible (child)) - gtk_widget_size_allocate (child, &child_allocation); -} +G_DEFINE_TYPE(AnacondaStandaloneWindow, anaconda_standalone_window, ANACONDA_TYPE_BASE_STANDALONE)
static void anaconda_standalone_window_class_init(AnacondaStandaloneWindowClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); - widget_class->draw = anaconda_standalone_window_on_draw; - widget_class->size_allocate = anaconda_standalone_window_size_allocate;
- klass->quit_clicked = NULL; - klass->continue_clicked = NULL; + object_class->get_property = anaconda_standalone_window_get_property; + + /* + * Override the quit-button and continue-button properties to make them + * read only. The buttons will be create during the object's init method. + */
/** - * AnacondaStandaloneWindow::quit-clicked: - * @window: the window that received the signal + * AnacondaStandaloneWindow::quit-button: * - * Emitted when the quit button has been activated (pressed and released). + * The button to quit anaconda. * - * Since: 1.0 + * This overrides #AnacondaBaseStandalone:quit-button in #AnacondaBaseStandalone to be read-only. + * + * Since: 3.0 */ - window_signals[SIGNAL_QUIT_CLICKED] = g_signal_new("quit-clicked", - G_TYPE_FROM_CLASS(object_class), - G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET(AnacondaStandaloneWindowClass, quit_clicked), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); + g_object_class_install_property(object_class, + PROP_QUIT_BUTTON, + g_param_spec_object("quit-button", + P_("Quit button"), + P_("The button to quit Anaconda"), + GTK_TYPE_BUTTON, + G_PARAM_READABLE));
/** - * AnacondaStandaloneWindow::continue-clicked: - * @window: the window that received the signal + * AnacondaStandaloneWindow::continue-button: + * + * The button to continue to the next window. * - * Emitted when the continue button has been activated (pressed and released). + * This overrides #AnacondaBaseStandalone:continue-button in #AnacondaBaseStandalone to be read-only. * - * Since: 1.0 + * Since: 3.0 */ - window_signals[SIGNAL_CONTINUE_CLICKED] = g_signal_new("continue-clicked", - G_TYPE_FROM_CLASS(object_class), - G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET(AnacondaStandaloneWindowClass, continue_clicked), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); + g_object_class_install_property(object_class, + PROP_CONTINUE_BUTTON, + g_param_spec_object("continue-button", + P_("Continue button"), + P_("The button to continue to the next window"), + GTK_TYPE_BUTTON, + G_PARAM_READABLE));
g_type_class_add_private(object_class, sizeof(AnacondaStandaloneWindowPrivate)); }
+static void anaconda_standalone_window_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { + /* Just chain up to the parent class get_property */ + gchar *parent_property = g_strdup_printf("%s::%s", G_OBJECT_CLASS_NAME(anaconda_standalone_window_parent_class), pspec->name); + g_object_get_property(object, parent_property, value); + g_free(parent_property); +} + /** * anaconda_standalone_window_new: * @@ -225,13 +149,9 @@ static void anaconda_standalone_window_init(AnacondaStandaloneWindow *win) { atk = gtk_widget_get_accessible(win->priv->continue_button); atk_object_set_name(atk, _(CONTINUE_TEXT));
- /* Hook up some signals for those buttons. The signal handlers here will - * just raise our own custom signals for the whole window. - */ - g_signal_connect(win->priv->quit_button, "clicked", - G_CALLBACK(anaconda_standalone_window_quit_clicked), win); - g_signal_connect(win->priv->continue_button, "clicked", - G_CALLBACK(anaconda_standalone_window_continue_clicked), win); + /* Set the properties on AnacondaBaseStandalone */ + g_object_set(G_OBJECT(win), "AnacondaBaseStandalone::quit-button", win->priv->quit_button, NULL); + g_object_set(G_OBJECT(win), "AnacondaBaseStandalone::continue-button", win->priv->continue_button, NULL);
/* Create the button box and pack the buttons into it. */ win->priv->button_box = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); @@ -240,6 +160,7 @@ static void anaconda_standalone_window_init(AnacondaStandaloneWindow *win) { gtk_widget_set_margin_bottom(win->priv->button_box, 6); gtk_button_box_set_layout(GTK_BUTTON_BOX(win->priv->button_box), GTK_BUTTONBOX_END); gtk_box_set_spacing(GTK_BOX(win->priv->button_box), 12); + gtk_container_add(GTK_CONTAINER(win->priv->button_box), win->priv->quit_button); gtk_container_add(GTK_CONTAINER(win->priv->button_box), win->priv->continue_button);
@@ -263,49 +184,6 @@ static void anaconda_standalone_window_realize(GtkWidget *widget, 0); }
-static void anaconda_standalone_window_quit_clicked(GtkButton *button, - AnacondaStandaloneWindow *win) { - g_signal_emit(win, window_signals[SIGNAL_QUIT_CLICKED], 0); -} - -static void anaconda_standalone_window_continue_clicked(GtkButton *button, - AnacondaStandaloneWindow *win) { - g_signal_emit(win, window_signals[SIGNAL_CONTINUE_CLICKED], 0); -} - -/** - * anaconda_standalone_window_get_may_continue: - * @win: a #AnacondaStandaloneWindow - * - * Returns whether or not the continue button is sensitive (thus, whether the - * user may continue forward from this window). - * - * Returns: Whether the continue button on @win is sensitive. - * - * Since: 1.0 - */ -gboolean anaconda_standalone_window_get_may_continue(AnacondaStandaloneWindow *win) { - return gtk_widget_get_sensitive(win->priv->continue_button); -} - -/** - * anaconda_standalone_window_set_may_continue: - * @win: a #AnacondaStandaloneWindow - * @may_continue: %TRUE if this window's continue button should be sensitive. - * - * Specifies whether the user may continue forward from this window. If so, - * the continue button will be made sensitive. Windows default to continuable - * so you must set it as false if you want. The reason the user may not be - * able to continue is if there is required information the user must enter - * when no reasonable default may be given. - * - * Since: 1.0 - */ -void anaconda_standalone_window_set_may_continue(AnacondaStandaloneWindow *win, - gboolean may_continue) { - gtk_widget_set_sensitive(win->priv->continue_button, may_continue); -} - /** * anaconda_standalone_window_retranslate: * @win: a #AnacondaStandaloneWindow @@ -320,6 +198,7 @@ void anaconda_standalone_window_set_may_continue(AnacondaStandaloneWindow *win, */ void anaconda_standalone_window_retranslate(AnacondaStandaloneWindow *win, const char *lang) { anaconda_base_window_retranslate(ANACONDA_BASE_WINDOW(win), lang); + gtk_button_set_label(GTK_BUTTON(win->priv->quit_button), _(QUIT_TEXT)); gtk_button_set_label(GTK_BUTTON(win->priv->continue_button), _(CONTINUE_TEXT)); } diff --git a/widgets/src/StandaloneWindow.h b/widgets/src/StandaloneWindow.h index d156205..63a9e9e 100644 --- a/widgets/src/StandaloneWindow.h +++ b/widgets/src/StandaloneWindow.h @@ -22,7 +22,7 @@
#include <gtk/gtk.h>
-#include "BaseWindow.h" +#include "BaseStandalone.h"
G_BEGIN_DECLS
@@ -44,7 +44,7 @@ typedef struct _AnacondaStandaloneWindowPrivate AnacondaStandaloneWindowPrivat * be directly accessed. */ struct _AnacondaStandaloneWindow { - AnacondaBaseWindow parent; + AnacondaBaseStandalone parent;
/*< private >*/ AnacondaStandaloneWindowPrivate *priv; @@ -56,22 +56,13 @@ struct _AnacondaStandaloneWindow { * the widget class structure in order for the class mechanism * to work correctly. This allows an AnacondaStandaloneWindowClass * pointer to be cast to an #AnacondaBaseWindow pointer. - * @quit_clicked: Function pointer called when the #AnacondaStandaloneWindow::quit-clicked - * signal is emitted. - * @continue_clicked: Function pointer called when the #AnacondaStandaloneWindow::continue-clicked - * signal is emitted. */ struct _AnacondaStandaloneWindowClass { - AnacondaBaseWindowClass parent_class; - - void (* quit_clicked) (AnacondaStandaloneWindow *window); - void (* continue_clicked) (AnacondaStandaloneWindow *window); + AnacondaBaseStandaloneClass parent_class; };
GType anaconda_standalone_window_get_type (void); GtkWidget *anaconda_standalone_window_new (); -gboolean anaconda_standalone_window_get_may_continue (AnacondaStandaloneWindow *win); -void anaconda_standalone_window_set_may_continue (AnacondaStandaloneWindow *win, gboolean may_continue); void anaconda_standalone_window_retranslate (AnacondaStandaloneWindow *win, const char *lang);
G_END_DECLS
On Mon, 2014-06-30 at 15:41 -0400, David Shea wrote:
This class is an abstract class containing the shared parts of HubWindow and StandaloneWindow. It provides quit-button and continue-button properties, quit-clicked and continue-clicked signals emitted when the buttons are pressed, and the sidebar sizing and drawing methods.
Glade files using StandaloneWindow do not require any change. Glade files using HubWindow may need to be modified to connect the quit-button or continue-button properties.
On the python side, this removes the need for the register_event_cb proxy for quit and continue events. Connect GraphicalUserInterface directly to the now-shared signals.
po/POTFILES.in | 1 + pyanaconda/ui/gui/__init__.py | 17 +- pyanaconda/ui/gui/hubs/__init__.py | 27 +-- pyanaconda/ui/gui/hubs/progress.glade | 5 +- pyanaconda/ui/gui/hubs/progress.py | 11 +- pyanaconda/ui/gui/hubs/summary.glade | 6 +- pyanaconda/ui/gui/hubs/summary.py | 7 - pyanaconda/ui/gui/spokes/__init__.py | 12 +- pyanaconda/ui/gui/spokes/network.py | 4 +- pyanaconda/ui/gui/spokes/welcome.py | 4 +- pyanaconda/ui/gui/tools/run-spoke.py | 7 +- widgets/doc/AnacondaWidgets-docs.xml | 1 + widgets/glade/AnacondaWidgets.xml | 10 + widgets/src/BaseStandalone.c | 377 ++++++++++++++++++++++++++++++++++ widgets/src/BaseStandalone.h | 81 ++++++++ widgets/src/HubWindow.c | 83 +------- widgets/src/HubWindow.h | 6 +- widgets/src/Makefile.am | 2 + widgets/src/StandaloneWindow.c | 221 +++++--------------- widgets/src/StandaloneWindow.h | 15 +- 20 files changed, 569 insertions(+), 328 deletions(-) create mode 100644 widgets/src/BaseStandalone.c create mode 100644 widgets/src/BaseStandalone.h
...
+static int get_sidebar_width(GtkWidget *window) {
- GtkAllocation allocation;
- /* change value below to make sidebar bigger / smaller */
- float sidebar_width_percentage = STANDALONE_SIDEBAR_WIDTH_PCT;
Any reason for this assignment and the sidebar_width_percentage variable?
Instead of each hub and spoke being a separate Gtk window, create a single window to contain every non-dialog screen. This changes the parent type of AnacondaBaseWindow to GtkBin instead of GtkWindow.
This change is mostly helpful in live environments where we have less control over the window manager. Each spoke and hub appeared as a separate window the in the window manager and it made things look kind of weird, as well as opening up the possibility for some troublesome window interactions.
Remove the separate GtkWindowGroup for exception windows, since there's no longer a separate Gtk.main running for spokes. --- pyanaconda/ui/common.py | 2 +- pyanaconda/ui/gui/__init__.py | 98 ++++++++++++++++++++++++++++++++---- pyanaconda/ui/gui/hubs/__init__.py | 64 +++++++++-------------- pyanaconda/ui/gui/spokes/__init__.py | 7 ++- pyanaconda/ui/gui/utils.py | 2 +- widgets/src/BaseWindow.c | 14 +++--- widgets/src/BaseWindow.h | 6 +-- widgets/src/HubWindow.c | 9 ++-- widgets/src/SpokeWindow.c | 29 ++--------- widgets/src/StandaloneWindow.c | 23 +-------- 10 files changed, 136 insertions(+), 118 deletions(-)
diff --git a/pyanaconda/ui/common.py b/pyanaconda/ui/common.py index d083ba4..f0ac602 100644 --- a/pyanaconda/ui/common.py +++ b/pyanaconda/ui/common.py @@ -226,7 +226,7 @@ class Spoke(UIObject): self.instclass = instclass self.applyOnSkip = False
- self._visitedSinceApplied = True + self.visitedSinceApplied = True
@property def storage(self): diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py index 604a3b5..db3eb82 100644 --- a/pyanaconda/ui/gui/__init__.py +++ b/pyanaconda/ui/gui/__init__.py @@ -123,7 +123,6 @@ class GUIObject(common.UIObject): else: self.builder.add_from_file(self._findUIFile())
- ANACONDA_WINDOW_GROUP.add_window(self.window) self.builder.connect_signals(self)
# Keybinder from GI needs to be initialized before use @@ -169,7 +168,7 @@ class GUIObject(common.UIObject):
@property def window(self): - """Return the top-level object out of the GtkBuilder representation + """Return the object out of the GtkBuilder representation previously loaded by the load method. """
@@ -180,6 +179,11 @@ class GUIObject(common.UIObject):
return self._window
+ @property + def main_window(self): + """Return the top-level window containing this GUIObject.""" + return self.window.get_toplevel() + def clear_info(self): """Clear any info bar from the bottom of the screen.""" self.window.clear_info() @@ -236,6 +240,80 @@ class ErrorDialog(GUIObject): rc = self.window.run() return rc
+class MainWindow(Gtk.Window): + """This is a top-level, full size window containing the Anaconda screens.""" + + def __init__(self): + Gtk.Window.__init__(self) + + # Create a stack and a list of what's been added to the stack + self._stack = Gtk.Stack() + self._stack_contents = set() + + # Create an accel group for the F12 accelerators added after window transitions + self._accel_group = Gtk.AccelGroup() + self.add_accel_group(self._accel_group) + + # Set properties on the window + self.set_decorated(False) + self.maximize() + self.add(self._stack) + self.show_all() + + self._current_action = None + + @property + def current_action(self): + return self._current_action + + def _setVisibleChild(self, child): + # Remove the F12 accelerator from the old window + old_screen = self._stack.get_visible_child() + if old_screen: + old_screen.remove_accelerator(self._accel_group, Gdk.KEY_F12, 0) + + # Check if the widget is already on the stack + if child not in self._stack_contents: + self._stack.add(child.window) + self._stack_contents.add(child) + child.window.show_all() + + # It would be handy for F12 to continue to work like it did in the old + # UI, by skipping you to the next screen or sending you back to the hub + if isinstance(child.window, AnacondaWidgets.BaseStandalone): + child.window.add_accelerator("continue-clicked", self._accel_group, + Gdk.KEY_F12, 0, 0) + elif isinstance(child.window, AnacondaWidgets.SpokeWindow): + child.window.add_accelerator("button-clicked", self._accel_group, + Gdk.KEY_F12, 0, 0) + + self._stack.set_visible_child(child.window) + + def setCurrentAction(self, standalone): + """Set the current standalone widget. + + This changes the currently displayed screen and, if the standalone + is a hub, sets the hub as the screen to which spokes will return. + + :param AnacondaWidgets.BaseStandalone standalone: the new standalone action + """ + self._current_action = standalone + self._setVisibleChild(standalone) + + def enterSpoke(self, spoke): + """Enter a spoke. + + The spoke will be displayed as the current screen, but the current-action + to which the spoke will return will not be changed. + + :param AnacondaWidgets.SpokeWindow spoke: a spoke to enter + """ + self._setVisibleChild(spoke) + + def returnToHub(self): + """Exit a spoke and return to a hub.""" + self._setVisibleChild(self._current_action) + class GraphicalUserInterface(UserInterface): """This is the standard GTK+ interface we try to steer everything to using. It is suitable for use both directly and via VNC. @@ -252,12 +330,16 @@ class GraphicalUserInterface(UserInterface):
self.data = None
+ self.mainWindow = MainWindow() + self._distributionText = distributionText self._isFinal = isFinal self._quitDialog = quitDialog self._mehInterface = GraphicalExceptionHandlingIface( self.lightbox_over_current_action)
+ ANACONDA_WINDOW_GROUP.add_window(self.mainWindow) + basemask = "pyanaconda.ui" basepath = os.path.dirname(__file__) updatepath = "/tmp/updates/pyanaconda/ui" @@ -349,7 +431,7 @@ class GraphicalUserInterface(UserInterface):
# if there are no actions (not populated yet), we can do nothing if len(self._actions) > 0 and self._currentAction: - lightbox = AnacondaWidgets.Lightbox(parent_window=self._currentAction.window) + lightbox = AnacondaWidgets.Lightbox(parent_window=self.mainWindow) ANACONDA_WINDOW_GROUP.add_window(lightbox) window.main_window.set_transient_for(lightbox)
@@ -423,7 +505,7 @@ class GraphicalUserInterface(UserInterface): # try to make sure a logo image is present self._assureLogoImage()
- self._currentAction.window.show_all() + self.mainWindow.setCurrentAction(self._currentAction)
# Do this at the last possible minute. unbusyCursor() @@ -529,8 +611,7 @@ class GraphicalUserInterface(UserInterface):
# Do this last. Setting up curAction could take a while, and we want # to leave something on the screen while we work. - nextAction.window.show_all() - self._currentAction.window.hide() + self.mainWindow.setCurrentAction(nextAction) self._currentAction = nextAction self._actions.pop(0)
@@ -572,10 +653,7 @@ class GraphicalExceptionHandlingIface(meh.ui.gui.GraphicalIntf):
self._lightbox_func(exc_window)
- # without a new GtkWindowGroup, python-meh's window is insensitive if it - # appears above a spoke (Gtk.Window running its own Gtk.main loop) - window_group = Gtk.WindowGroup() - window_group.add_window(exc_window.main_window) + ANACONDA_WINDOW_GROUP.add_window(exc_window.main_window)
# the busy cursor may be set unbusyCursor() diff --git a/pyanaconda/ui/gui/hubs/__init__.py b/pyanaconda/ui/gui/hubs/__init__.py index 3645759..b89e596 100644 --- a/pyanaconda/ui/gui/hubs/__init__.py +++ b/pyanaconda/ui/gui/hubs/__init__.py @@ -95,40 +95,6 @@ class Hub(GUIObject, common.Hub):
self._checker = None
- def _runSpoke(self, action): - from gi.repository import Gtk - - # This duplicates code in widgets/src/BaseWindow.c, but we want to make sure - # maximize gets called every time a spoke is displayed to prevent the 25% - # UI from showing up. - action.window.maximize() - action.window.set_property("expand", True) - - action.entry_logger() - - action.refresh() - - action.window.set_transient_for(self.window) - action.window.show_all() - - # Start a recursive main loop for this spoke, which will prevent - # signals from going to the underlying (but still displayed) Hub and - # prevent the user from switching away. It's up to the spoke's back - # button handler to kill its own layer of main loop. - Gtk.main() - action.window.set_transient_for(None) - - action._visitedSinceApplied = True - - # Don't take _visitedSinceApplied into account here. It will always be - # True from the line above. - if action.changed and (not action.skipTo or (action.skipTo and action.applyOnSkip)): - action.apply() - action.execute() - action._visitedSinceApplied = False - - action.exit_logger() - def _createBox(self): from gi.repository import Gtk, AnacondaWidgets from pyanaconda.ui.gui.utils import setViewportBackground @@ -198,9 +164,9 @@ class Hub(GUIObject, common.Hub):
# If this is a kickstart install, attempt to execute any provided ksdata now. if flags.automatedInstall and spoke.ready and spoke.changed and \ - spoke._visitedSinceApplied: + spoke.visitedSinceApplied: spoke.execute() - spoke._visitedSinceApplied = False + spoke.visitedSinceApplied = False
selectors.append(spoke.selector)
@@ -327,9 +293,9 @@ class Hub(GUIObject, common.Hub): # _createBox skipped. Now that it's become ready, do it. Note # that we also provide a way to skip this processing (see comments # communication.py) to prevent getting caught in a loop. - if not args[1] and spoke.changed and spoke._visitedSinceApplied: + if not args[1] and spoke.changed and spoke.visitedSinceApplied: spoke.execute() - spoke._visitedSinceApplied = False + spoke.visitedSinceApplied = False
if self.continuePossible: if self._inSpoke: @@ -383,8 +349,24 @@ class Hub(GUIObject, common.Hub): # that he is done configuring by pressing the continue button. self._autoContinue = False
+ # Enter the spoke self._inSpoke = True - self._runSpoke(spoke) + spoke.entry_logger() + spoke.refresh() + self.main_window.enterSpoke(spoke) + + def spoke_done(self, spoke): + spoke.visitedSinceApplied = True + + # Don't take visitedSinceApplied into account here. It will always be + # True from the line above. + if spoke.changed and (not spoke.skipTo or (spoke.skipTo and spoke.applyOnSkip)): + spoke.apply() + spoke.execute() + spoke.visitedSinceApplied = False + + spoke.exit_logger() + self._inSpoke = False
# Now update the selector with the current status and completeness. @@ -403,5 +385,7 @@ class Hub(GUIObject, common.Hub): spoke.skipTo = None
self._on_spoke_clicked(self._spokes[dest].selector, None, self._spokes[dest]) - + # Otherwise, switch back to the hub (that's us!) + else: + self.main_window.returnToHub()
diff --git a/pyanaconda/ui/gui/spokes/__init__.py b/pyanaconda/ui/gui/spokes/__init__.py index 9a620b6..86706a4 100644 --- a/pyanaconda/ui/gui/spokes/__init__.py +++ b/pyanaconda/ui/gui/spokes/__init__.py @@ -84,7 +84,6 @@ class NormalSpoke(Spoke, common.NormalSpoke): common.NormalSpoke.__init__(self, data, storage, payload, instclass)
def on_back_clicked(self, window): - from gi.repository import Gtk - - self.window.hide() - Gtk.main_quit() + # Notify the hub that we're finished. + # The hub will be the current-action of the main window. + self.main_window.current_action.spoke_done(self) diff --git a/pyanaconda/ui/gui/utils.py b/pyanaconda/ui/gui/utils.py index 4a3bc21..51bcc0d 100644 --- a/pyanaconda/ui/gui/utils.py +++ b/pyanaconda/ui/gui/utils.py @@ -302,7 +302,7 @@ def enlightbox(mainWindow, dialog): # importing globally would cause a circular dependency from pyanaconda.ui.gui import ANACONDA_WINDOW_GROUP
- lightbox = AnacondaWidgets.Lightbox(parent_window=mainWindow) + lightbox = AnacondaWidgets.Lightbox(parent_window=mainWindow.get_toplevel()) ANACONDA_WINDOW_GROUP.add_window(lightbox) dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) dialog.set_transient_for(lightbox) diff --git a/widgets/src/BaseWindow.c b/widgets/src/BaseWindow.c index cabbcfc..9f5282a 100644 --- a/widgets/src/BaseWindow.c +++ b/widgets/src/BaseWindow.c @@ -31,11 +31,11 @@ * @title: AnacondaBaseWindow * @short_description: Top-level, non-resizeable window * - * A #AnacondaBaseWindow is a top-level, non-resizeable window that contains - * other widgets and serves as the base class from which all other specialized - * Anaconda windows are derived. It is undecorated. + * A #AnacondaBaseWindow is a widget that contains other widgets and serves as + * the base class from which all other specialized Anaconda windows are + * derived. * - * The window consists of two areas: + * The AnacondaBaseWindow consists of two areas: * * - A navigation area in the top of the screen, consisting of some basic * information about what is being displayed and what is being installed. @@ -127,7 +127,7 @@ static void format_beta_label(AnacondaBaseWindow *window, const char *markup);
static gboolean anaconda_base_window_info_bar_clicked(GtkWidget *widget, GdkEvent *event, AnacondaBaseWindow *win);
-G_DEFINE_TYPE_WITH_CODE(AnacondaBaseWindow, anaconda_base_window, GTK_TYPE_WINDOW, +G_DEFINE_TYPE_WITH_CODE(AnacondaBaseWindow, anaconda_base_window, GTK_TYPE_BIN, G_IMPLEMENT_INTERFACE(GTK_TYPE_BUILDABLE, anaconda_base_window_buildable_init))
static void anaconda_base_window_class_init(AnacondaBaseWindowClass *klass) { @@ -223,9 +223,7 @@ static void anaconda_base_window_init(AnacondaBaseWindow *win) { win->priv->orig_distro = NULL; win->priv->orig_beta = NULL;
- /* Set properties on the parent (Gtk.Window) class. */ - gtk_window_set_decorated(GTK_WINDOW(win), FALSE); - gtk_window_maximize(GTK_WINDOW(win)); + /* Set properties on the parent (Gtk.Bin) class. */ gtk_widget_set_hexpand(GTK_WIDGET(win), TRUE); gtk_widget_set_vexpand(GTK_WIDGET(win), TRUE); gtk_container_set_border_width(GTK_CONTAINER(win), 0); diff --git a/widgets/src/BaseWindow.h b/widgets/src/BaseWindow.h index 6c08ee8..eb6191d 100644 --- a/widgets/src/BaseWindow.h +++ b/widgets/src/BaseWindow.h @@ -42,7 +42,7 @@ typedef struct _AnacondaBaseWindowPrivate AnacondaBaseWindowPrivate; * be directly accessed. */ struct _AnacondaBaseWindow { - GtkWindow parent; + GtkBin parent;
/*< private >*/ AnacondaBaseWindowPrivate *priv; @@ -53,12 +53,12 @@ struct _AnacondaBaseWindow { * @parent_class: The object class structure needs to be the first element in * the widget class structure in order for the class mechanism * to work correctly. This allows a AnacondaBaseWindowClass - * pointer to be cast to a #GtkWindow pointer. + * pointer to be cast to a #GtkBin pointer. * @info_bar_clicked : Function pointer called when the #AnacondaBaseWindow::info-bar-clicked * signal is emitted. */ struct _AnacondaBaseWindowClass { - GtkWindowClass parent_class; + GtkBinClass parent_class;
void (* info_bar_clicked) (AnacondaBaseWindow *window); }; diff --git a/widgets/src/HubWindow.c b/widgets/src/HubWindow.c index 927dd3d..3b5cdea 100644 --- a/widgets/src/HubWindow.c +++ b/widgets/src/HubWindow.c @@ -26,12 +26,11 @@ * @title: AnacondaHubWindow * @short_description: Window for displaying a Hub * - * A #AnacondaHubWindow is a top-level window that displays a hub on the - * entire screen. A Hub allows selection of multiple configuration spokes - * from a single interface, as well as a place to display current configuration - * selections. + * A #AnacondaHubWindow is a widget that displays a hub on the screen. A Hub + * allows selection of multiple configuration spokes from a single interface, + * as well as a place to display current configuration selections. * - * The window consists of three areas: + * The AnacondaHubWindow consists of three areas: * * - A navigation area in the top of the screen, inherited from #AnacondaBaseWindow. * diff --git a/widgets/src/SpokeWindow.c b/widgets/src/SpokeWindow.c index 461e493..a1a46f1 100644 --- a/widgets/src/SpokeWindow.c +++ b/widgets/src/SpokeWindow.c @@ -30,11 +30,11 @@ * @title: AnacondaSpokeWindow * @short_description: Window for displaying single spokes * - * A #AnacondaSpokeWindow is a top-level window that displays a single spoke - * on the entire screen. Examples include the keyboard and language - * configuration screens off the first hub. + * A #AnacondaSpokeWindow is a widget that displays a single spoke on the + * screen. Examples include the keyboard and language configuration screens + * off the first hub. * - * The window consists of two areas: + * The AnacondaSpokeWindow consists of two areas: * * - A navigation area in the top of the screen, inherited from #AnacondaBaseWindow * and augmented with a button in the upper left corner. @@ -58,7 +58,6 @@ struct _AnacondaSpokeWindowPrivate {
G_DEFINE_TYPE(AnacondaSpokeWindow, anaconda_spoke_window, ANACONDA_TYPE_BASE_WINDOW)
-static void anaconda_spoke_window_realize(GtkWidget *widget, gpointer user_data); static void anaconda_spoke_window_button_clicked(GtkButton *button, AnacondaSpokeWindow *win);
@@ -113,11 +112,6 @@ static void anaconda_spoke_window_init(AnacondaSpokeWindow *win) { ANACONDA_TYPE_SPOKE_WINDOW, AnacondaSpokeWindowPrivate);
- g_signal_connect(win, "map", G_CALLBACK(anaconda_spoke_window_realize), NULL); - - /* Set some default properties. */ - gtk_window_set_modal(GTK_WINDOW(win), TRUE); - /* Create the button. */ win->priv->button = gtk_button_new_with_mnemonic(DEFAULT_BUTTON_LABEL); gtk_widget_set_halign(win->priv->button, GTK_ALIGN_START); @@ -143,21 +137,6 @@ static void anaconda_spoke_window_init(AnacondaSpokeWindow *win) { gtk_grid_attach(GTK_GRID(nav_area), win->priv->button, 0, 1, 1, 2); }
-static void anaconda_spoke_window_realize(GtkWidget *widget, gpointer user_data) { - GtkAccelGroup *accel_group; - AnacondaSpokeWindow *window = ANACONDA_SPOKE_WINDOW(widget); - - /* Pressing F12 should send you back to the hub, similar to how the old UI worked. */ - accel_group = gtk_accel_group_new(); - gtk_window_add_accel_group(GTK_WINDOW(window), accel_group); - gtk_widget_add_accelerator(window->priv->button, - "clicked", - accel_group, - GDK_KEY_F12, - 0, - 0); -} - static void anaconda_spoke_window_button_clicked(GtkButton *button, AnacondaSpokeWindow *win) { g_signal_emit(win, window_signals[SIGNAL_BUTTON_CLICKED], 0); diff --git a/widgets/src/StandaloneWindow.c b/widgets/src/StandaloneWindow.c index 4c489c2..5d0836a 100644 --- a/widgets/src/StandaloneWindow.c +++ b/widgets/src/StandaloneWindow.c @@ -29,12 +29,12 @@ * @title: AnacondaStandaloneWindow * @short_description: Window for displaying standalone spokes * - * A #AnacondaStandaloneWindow is a top-level window that displays a standalone + * A #AnacondaStandaloneWindow is a widget that displays a standalone * spoke. A standalone spoke is like a normal spoke, but is not entered via a * hub. Instead, it is displayed by itself. Examples include the welcome and * network configuration screens at the beginning of installation. * - * The window consist of three areas: + * The AnacondaStandaloneWindow consist of three areas: * * - A navigation area in the top of the screen, inherited from #AnacondaBaseWindow. * @@ -60,8 +60,6 @@ enum { };
static void anaconda_standalone_window_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); -static void anaconda_standalone_window_realize(GtkWidget *widget, - AnacondaStandaloneWindow *win);
G_DEFINE_TYPE(AnacondaStandaloneWindow, anaconda_standalone_window, ANACONDA_TYPE_BASE_STANDALONE)
@@ -165,23 +163,6 @@ static void anaconda_standalone_window_init(AnacondaStandaloneWindow *win) { gtk_container_add(GTK_CONTAINER(win->priv->button_box), win->priv->continue_button);
gtk_box_pack_start(GTK_BOX(main_box), win->priv->button_box, FALSE, TRUE, 0); - - /* It would be handy for F12 to continue to work like it did in the old - * UI, by skipping you to the next screen. - */ - g_signal_connect(win, "realize", G_CALLBACK(anaconda_standalone_window_realize), win); -} - -static void anaconda_standalone_window_realize(GtkWidget *widget, - AnacondaStandaloneWindow *win) { - GtkAccelGroup *accel_group = gtk_accel_group_new(); - gtk_window_add_accel_group(GTK_WINDOW(win), accel_group); - gtk_widget_add_accelerator(win->priv->continue_button, - "clicked", - accel_group, - GDK_KEY_F12, - 0, - 0); }
/**
If someone does tell the window to close, we don't want anaconda to just disappear. --- pyanaconda/ui/gui/__init__.py | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py index db3eb82..0640418 100644 --- a/pyanaconda/ui/gui/__init__.py +++ b/pyanaconda/ui/gui/__init__.py @@ -246,6 +246,9 @@ class MainWindow(Gtk.Window): def __init__(self): Gtk.Window.__init__(self)
+ # Treat an attempt to close the window the same as hitting quit + self.connect("delete-event", self._on_delete_event) + # Create a stack and a list of what's been added to the stack self._stack = Gtk.Stack() self._stack_contents = set() @@ -262,6 +265,15 @@ class MainWindow(Gtk.Window):
self._current_action = None
+ def _on_delete_event(self, widget, event, user_data=None): + # Use the quit-clicked signal on the the current standalone, even if the + # standalone is not currently displayed. + if self.current_action: + self.current_action.window.emit("quit-clicked") + + # Stop the window from being closed here + return True + @property def current_action(self): return self._current_action
This is handy, for example, for creating small transparent overlays. Creating GdkPixbuf objects from any source other than a file is broken in the gobject-introspection bindings, so this saves us from keeping track of more files. --- widgets/src/widgets-common.c | 51 +++++++++++++++++++++++++++++++++++++++++++- widgets/src/widgets-common.h | 3 +++ 2 files changed, 53 insertions(+), 1 deletion(-)
diff --git a/widgets/src/widgets-common.c b/widgets/src/widgets-common.c index 60ad6a9..459289a 100644 --- a/widgets/src/widgets-common.c +++ b/widgets/src/widgets-common.c @@ -20,8 +20,10 @@ * */
-#include <glib.h> +#include "widgets-common.h" + #include <stdlib.h> +#include <string.h>
/** * anaconda_get_widgets_datadir: @@ -44,3 +46,50 @@ const gchar *anaconda_get_widgets_datadir(void) { else return env_value; } + +static void free_pixbuf(guchar *pixels, gpointer data) { + g_free(pixels); +} + +/** + * anaconda_make_pixbuf: + * @data: (array): The data that would be passed to gdk_pixbuf_new_from_data() were + * that actually possible + * @has_alpha: Whether the data has an opacity channel + * @width: Width of the image in pixels, must be > 0 + * @height: Height of the image in pixels, must be > 0 + * @rowstride: Distance in bytes between row starts + * + * Create a GdkPixbuf in a way that actually works in gobject-introspection bindings. + * + * See also: https://bugzilla.gnome.org/show_bug.cgi?id=732297 + * + * colorspace and bits_per_sample are not provided as parameters because it would break + * something if they were ever not GDK_COLORSPACE_RGB and 8, respectively. + * + * Returns: (transfer full): A new GdkPixbuf + * + * Since: 3.0 + */ +GdkPixbuf * anaconda_make_pixbuf(const guint8 *data, gboolean has_alpha, + int width, int height, int rowstride) { + guchar *data_copy; + + /* Length of the data is max_y * rowstride + max_x * n_channels */ + size_t data_len = (width * rowstride) + (height * (has_alpha ? 4 : 3)); + + /* Create a copy of the data because whoever wrote gdk-pixbuf doesn't understand + * reference ownership. */ + data_copy = g_malloc(data_len); + memcpy(data_copy, data, data_len); + return gdk_pixbuf_new_from_data(data_copy, + GDK_COLORSPACE_RGB, + has_alpha, + 8, + width, + height, + rowstride, + free_pixbuf, + NULL + ); +} diff --git a/widgets/src/widgets-common.h b/widgets/src/widgets-common.h index 941ef83..d3aa488 100644 --- a/widgets/src/widgets-common.h +++ b/widgets/src/widgets-common.h @@ -21,10 +21,13 @@ #define _WIDGETS_COMMON_H
#include <glib.h> +#include <gdk-pixbuf/gdk-pixbuf.h>
G_BEGIN_DECLS
const gchar *anaconda_get_widgets_datadir(void); +GdkPixbuf * anaconda_make_pixbuf(const guint8 *data, gboolean has_alpha, + int width, int height, int rowstride);
G_END_DECLS
This implements the lightbox by using a GtkOverlay as the child of GtkWindow and overlaying a transparent GdkPixbuf as needed. This works in both compositing and non-compositing modes. --- pyanaconda/ui/gui/__init__.py | 89 ++++++++++++++++++++++-------- pyanaconda/ui/gui/spokes/custom.py | 24 ++++---- pyanaconda/ui/gui/spokes/datetime_spoke.py | 4 +- pyanaconda/ui/gui/spokes/filter.py | 7 +-- pyanaconda/ui/gui/spokes/keyboard.py | 8 +-- pyanaconda/ui/gui/spokes/network.py | 4 +- pyanaconda/ui/gui/spokes/software.py | 4 +- pyanaconda/ui/gui/spokes/source.py | 10 ++-- pyanaconda/ui/gui/spokes/storage.py | 8 +-- pyanaconda/ui/gui/spokes/user.py | 3 +- pyanaconda/ui/gui/spokes/welcome.py | 6 +- pyanaconda/ui/gui/utils.py | 15 +---- 12 files changed, 105 insertions(+), 77 deletions(-)
diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py index 0640418..7055414 100644 --- a/pyanaconda/ui/gui/__init__.py +++ b/pyanaconda/ui/gui/__init__.py @@ -21,13 +21,15 @@ import inspect, os, sys, time, site import meh.ui.gui
-from gi.repository import Gdk, Gtk, AnacondaWidgets, Keybinder +from contextlib import contextmanager + +from gi.repository import Gdk, Gtk, AnacondaWidgets, Keybinder, GdkPixbuf
from pyanaconda.i18n import _ from pyanaconda import product
from pyanaconda.ui import UserInterface, common -from pyanaconda.ui.gui.utils import enlightbox, gtk_action_wait, busyCursor, unbusyCursor +from pyanaconda.ui.gui.utils import gtk_action_wait, busyCursor, unbusyCursor import os.path
import logging @@ -249,6 +251,15 @@ class MainWindow(Gtk.Window): # Treat an attempt to close the window the same as hitting quit self.connect("delete-event", self._on_delete_event)
+ # Create a black, 50% opacity pixel that will be scaled to fit the lightbox overlay + self._transparent_base = AnacondaWidgets.make_pixbuf([0, 0, 0, 127], True, 1, 1, 1) + + # Contain everything in an overlay so the window can be overlayed with the transparency + # for the lightbox effect + self._overlay = Gtk.Overlay() + self._overlay_img = None + self._overlay.connect("get-child-position", self._on_overlay_get_child_position) + # Create a stack and a list of what's been added to the stack self._stack = Gtk.Stack() self._stack_contents = set() @@ -260,7 +271,9 @@ class MainWindow(Gtk.Window): # Set properties on the window self.set_decorated(False) self.maximize() - self.add(self._stack) + + self._overlay.add(self._stack) + self.add(self._overlay) self.show_all()
self._current_action = None @@ -274,6 +287,21 @@ class MainWindow(Gtk.Window): # Stop the window from being closed here return True
+ def _on_overlay_get_child_position(self, overlay_container, overlayed_widget, allocation, user_data=None): + overlay_allocation = overlay_container.get_allocation() + + # Scale the overlayed image's pixbuf to the size of the GtkOverlay + overlayed_widget.set_from_pixbuf(self._transparent_base.scale_simple( + overlay_allocation.width, overlay_allocation.height, GdkPixbuf.InterpType.NEAREST)) + + # Set the allocation for the overlayed image to the full size of the GtkOverlay + allocation.x = 0 + allocation.y = 0 + allocation.width = overlay_allocation.width + allocation.height = overlay_allocation.height + + return True + @property def current_action(self): return self._current_action @@ -326,6 +354,34 @@ class MainWindow(Gtk.Window): """Exit a spoke and return to a hub.""" self._setVisibleChild(self._current_action)
+ def lightbox_on(self): + # Add an overlay image that will be filled and scaled in get-child-position + self._overlay_img = Gtk.Image() + self._overlay_img.show_all() + self._overlay.add_overlay(self._overlay_img) + + def lightbox_off(self): + # Remove the overlay image + self._overlay_img.destroy() + self._overlay_img = None + + @contextmanager + def enlightbox(self, dialog): + """Display a dialog in a lightbox over the main window. + + :param GtkDialog: the dialog to display + """ + self.lightbox_on() + + # Set the dialog as transient for ourself + ANACONDA_WINDOW_GROUP.add_window(dialog) + dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) + dialog.set_transient_for(self) + + yield + + self.lightbox_off() + class GraphicalUserInterface(UserInterface): """This is the standard GTK+ interface we try to steer everything to using. It is suitable for use both directly and via VNC. @@ -348,7 +404,7 @@ class GraphicalUserInterface(UserInterface): self._isFinal = isFinal self._quitDialog = quitDialog self._mehInterface = GraphicalExceptionHandlingIface( - self.lightbox_over_current_action) + self.mainWindow.lightbox_on)
ANACONDA_WINDOW_GROUP.add_window(self.mainWindow)
@@ -434,19 +490,6 @@ class GraphicalUserInterface(UserInterface): # Second, order them according to their relationship return self._orderActionClasses(standalones, hubs)
- def lightbox_over_current_action(self, window): - """ - Creates lightbox over current action for the given window. Or - DOES NOTHING IF THERE ARE NO ACTIONS. - - """ - - # if there are no actions (not populated yet), we can do nothing - if len(self._actions) > 0 and self._currentAction: - lightbox = AnacondaWidgets.Lightbox(parent_window=self.mainWindow) - ANACONDA_WINDOW_GROUP.add_window(lightbox) - window.main_window.set_transient_for(lightbox) - def _instantiateAction(self, actionClass): # Instantiate an action on-demand, passing the arguments defining our # spoke API and setting up continue/quit signal handlers. @@ -531,7 +574,7 @@ class GraphicalUserInterface(UserInterface): def showError(self, message): dlg = ErrorDialog(None)
- with enlightbox(self._currentAction.window, dlg.window): + with self.mainWindow.enlightbox(dlg.window): dlg.refresh(message) dlg.run() dlg.window.destroy() @@ -542,7 +585,7 @@ class GraphicalUserInterface(UserInterface): dlg = DetailedErrorDialog(None, buttons=[_("_Quit")], label=message)
- with enlightbox(self._currentAction.window, dlg.window): + with self.mainWindow.enlightbox(dlg.window): dlg.refresh(details) rc = dlg.run() dlg.window.destroy() @@ -557,7 +600,7 @@ class GraphicalUserInterface(UserInterface): dlg.add_buttons(_("_No"), 0, _("_Yes"), 1) dlg.set_default_response(1)
- with enlightbox(self._currentAction.window, dlg): + with self.mainWindow.enlightbox(dlg): rc = dlg.run() dlg.destroy()
@@ -632,7 +675,7 @@ class GraphicalUserInterface(UserInterface): return
dialog = self._quitDialog(None) - with enlightbox(self._currentAction.window, dialog.window): + with self.mainWindow.enlightbox(dialog.window): rc = dialog.run() dialog.window.destroy()
@@ -651,7 +694,7 @@ class GraphicalExceptionHandlingIface(meh.ui.gui.GraphicalIntf): """ :param lightbox_func: a function that creates lightbox for a given window - :type lightbox_func: GtkWindow -> None + :type lightbox_func: None -> None
""" meh.ui.gui.GraphicalIntf.__init__(self) @@ -663,7 +706,7 @@ class GraphicalExceptionHandlingIface(meh.ui.gui.GraphicalIntf): exc_window = meh_intf.mainExceptionWindow(text, exn_file) exc_window.main_window.set_decorated(False)
- self._lightbox_func(exc_window) + self._lightbox_func()
ANACONDA_WINDOW_GROUP.add_window(exc_window.main_window)
diff --git a/pyanaconda/ui/gui/spokes/custom.py b/pyanaconda/ui/gui/spokes/custom.py index 5c91069..f555d85 100644 --- a/pyanaconda/ui/gui/spokes/custom.py +++ b/pyanaconda/ui/gui/spokes/custom.py @@ -82,7 +82,7 @@ from pyanaconda.ui.gui.spokes.lib.custom_storage_helpers import selectedRaidLeve from pyanaconda.ui.gui.spokes.lib.custom_storage_helpers import get_container_type_name, RAID_NOT_ENOUGH_DISKS from pyanaconda.ui.gui.spokes.lib.custom_storage_helpers import AddDialog, ConfirmDeleteDialog, DisksDialog, ContainerDialog, HelpDialog
-from pyanaconda.ui.gui.utils import setViewportBackground, enlightbox, fancy_set_sensitive, ignoreEscape +from pyanaconda.ui.gui.utils import setViewportBackground, fancy_set_sensitive, ignoreEscape from pyanaconda.ui.gui.utils import really_hide, really_show, GtkActionList, timed_action from pyanaconda.ui.categories.system import SystemCategory
@@ -1507,7 +1507,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): if d.format.type == "luks" and not d.format.exists] if new_luks: dialog = PassphraseDialog(self.data) - with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): rc = dialog.run()
if rc != 1: @@ -1526,7 +1526,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): if len(self._storage_playground.devicetree.findActions()) > 0: dialog = ActionSummaryDialog(self.data) dialog.refresh(self._storage_playground.devicetree.findActions()) - with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): rc = dialog.run()
if rc != 1: @@ -1604,7 +1604,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): dialog = AddDialog(self.data, mountpoints=self._storage_playground.mountpoints.keys()) dialog.refresh() - with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): rc = dialog.run()
if rc != 1: @@ -1811,7 +1811,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): snapshots = (device.direct and not device.isleaf) dialog.refresh(getattr(device.format, "mountpoint", ""), device.name, root_name, snapshots=snapshots) - with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): rc = dialog.run()
if rc != 1: @@ -1836,12 +1836,12 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): dialog = SelectedDisksDialog(self.data) dialog.refresh(self._clearpartDevices, self._currentFreeInfo, showRemove=False, setBoot=False) - with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): dialog.run()
def on_help_clicked(self, button): help_window = HelpDialog(self.data) - with enlightbox(self.window, help_window.window): + with self.main_window.enlightbox(help_window.window): help_window.run()
def on_configure_clicked(self, button): @@ -1863,7 +1863,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): disks=self._clearpartDevices, free=self._currentFreeInfo, selected=self._device_disks) - with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): rc = dialog.run()
if rc != 1: @@ -1918,7 +1918,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): storage=self._storage_playground, exists=getattr(container, "exists", False))
- with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): rc = dialog.run() dialog.window.destroy()
@@ -2435,7 +2435,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): dlg.add_buttons(_("_Reset selections"), 0, _("_Preserve current selections"), 1) dlg.set_default_response(1)
- with enlightbox(self.window, dlg): + with self.main_window.enlightbox(dlg): rc = dlg.run() dlg.destroy()
@@ -2447,7 +2447,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): def on_refresh_clicked(self, *args): dialog = RefreshDialog(self.data, self.storage) ignoreEscape(dialog.window) - with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): rc = dialog.run() dialog.window.destroy()
@@ -2480,7 +2480,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): message_format=str(self._error)) dlg.set_decorated(False)
- with enlightbox(self.window, dlg): + with self.main_window.enlightbox(dlg): dlg.run() dlg.destroy()
diff --git a/pyanaconda/ui/gui/spokes/datetime_spoke.py b/pyanaconda/ui/gui/spokes/datetime_spoke.py index 68696c5..5504f95 100644 --- a/pyanaconda/ui/gui/spokes/datetime_spoke.py +++ b/pyanaconda/ui/gui/spokes/datetime_spoke.py @@ -29,7 +29,7 @@ from pyanaconda.ui.common import FirstbootSpokeMixIn from pyanaconda.ui.gui import GUIObject from pyanaconda.ui.gui.spokes import NormalSpoke from pyanaconda.ui.categories.localization import LocalizationCategory -from pyanaconda.ui.gui.utils import enlightbox, gtk_action_nowait, gtk_call_once +from pyanaconda.ui.gui.utils import gtk_action_nowait, gtk_call_once from pyanaconda.ui.gui.helpers import GUIDialogInputCheckHandler from pyanaconda.ui.helpers import InputCheck
@@ -1055,7 +1055,7 @@ class DatetimeSpoke(FirstbootSpokeMixIn, NormalSpoke): def on_ntp_config_clicked(self, *args): self._config_dialog.refresh()
- with enlightbox(self.window, self._config_dialog.window): + with self.main_window.enlightbox(self._config_dialog.window): response = self._config_dialog.run()
if response == 1: diff --git a/pyanaconda/ui/gui/spokes/filter.py b/pyanaconda/ui/gui/spokes/filter.py index c342972..6405781 100644 --- a/pyanaconda/ui/gui/spokes/filter.py +++ b/pyanaconda/ui/gui/spokes/filter.py @@ -30,7 +30,6 @@ from pyanaconda.flags import flags from pyanaconda.i18n import CN_, CP_
from pyanaconda.ui.lib.disks import getDisks, isLocalDisk -from pyanaconda.ui.gui.utils import enlightbox from pyanaconda.ui.gui.spokes import NormalSpoke from pyanaconda.ui.gui.spokes.advstorage.fcoe import FCoEDialog from pyanaconda.ui.gui.spokes.advstorage.iscsi import ISCSIDialog @@ -511,7 +510,7 @@ class FilterSpoke(NormalSpoke): disks = [disk for disk in self.disks if disk.name in self.selected_disks] free_space = self.storage.getFreeSpace(disks=disks)
- with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): dialog.refresh(disks, free_space, showRemove=False, setBoot=False) dialog.run()
@@ -550,7 +549,7 @@ class FilterSpoke(NormalSpoke): def on_add_iscsi_clicked(self, widget, *args): dialog = ISCSIDialog(self.data, self.storage)
- with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): dialog.refresh() dialog.run()
@@ -561,7 +560,7 @@ class FilterSpoke(NormalSpoke): def on_add_fcoe_clicked(self, widget, *args): dialog = FCoEDialog(self.data, self.storage)
- with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): dialog.refresh() dialog.run()
diff --git a/pyanaconda/ui/gui/spokes/keyboard.py b/pyanaconda/ui/gui/spokes/keyboard.py index c001e38..f76fa84 100644 --- a/pyanaconda/ui/gui/spokes/keyboard.py +++ b/pyanaconda/ui/gui/spokes/keyboard.py @@ -25,7 +25,7 @@ from gi.repository import Gkbd, Gdk, Gtk from pyanaconda.ui.gui import GUIObject from pyanaconda.ui.gui.spokes import NormalSpoke from pyanaconda.ui.categories.localization import LocalizationCategory -from pyanaconda.ui.gui.utils import enlightbox, gtk_call_once, escape_markup, gtk_batch_map, timed_action +from pyanaconda.ui.gui.utils import gtk_call_once, escape_markup, gtk_batch_map, timed_action from pyanaconda import keyboard from pyanaconda import flags from pyanaconda.i18n import _, N_, CN_ @@ -424,7 +424,7 @@ class KeyboardSpoke(NormalSpoke): def on_add_clicked(self, button): self._add_dialog.refresh()
- with enlightbox(self.window, self._add_dialog.window): + with self.main_window.enlightbox(self._add_dialog.window): response = self._add_dialog.run()
if response == 1: @@ -541,7 +541,7 @@ class KeyboardSpoke(NormalSpoke): lay_var_spec) dialog.set_size_request(750, 350) dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) - with enlightbox(self.window, dialog): + with self.main_window.enlightbox(dialog): dialog.show_all() dialog.run()
@@ -581,7 +581,7 @@ class KeyboardSpoke(NormalSpoke): def on_options_clicked(self, *args): self._switching_dialog.refresh()
- with enlightbox(self.window, self._switching_dialog.window): + with self.main_window.enlightbox(self._switching_dialog.window): response = self._switching_dialog.run()
if response != 1: diff --git a/pyanaconda/ui/gui/spokes/network.py b/pyanaconda/ui/gui/spokes/network.py index 73390e1..461377d 100644 --- a/pyanaconda/ui/gui/spokes/network.py +++ b/pyanaconda/ui/gui/spokes/network.py @@ -39,7 +39,7 @@ from pyanaconda.ui.gui import GUIObject from pyanaconda.ui.gui.spokes import NormalSpoke, StandaloneSpoke from pyanaconda.ui.categories.system import SystemCategory from pyanaconda.ui.gui.hubs.summary import SummaryHub -from pyanaconda.ui.gui.utils import gtk_call_once, enlightbox, escape_markup +from pyanaconda.ui.gui.utils import gtk_call_once, escape_markup from pyanaconda.ui.common import FirstbootSpokeMixIn
from pyanaconda import network @@ -1204,7 +1204,7 @@ class SecretAgent(dbus.service.Object):
content = self._get_content(setting_name, connection_hash) dialog = SecretAgentDialog(self.spoke.data, content=content) - with enlightbox(self.spoke.window, dialog.window): + with self.spoke.main_window.enlightbox(dialog.window): rc = dialog.run()
secrets = dbus.Dictionary() diff --git a/pyanaconda/ui/gui/spokes/software.py b/pyanaconda/ui/gui/spokes/software.py index c585d67..f68dfd6 100644 --- a/pyanaconda/ui/gui/spokes/software.py +++ b/pyanaconda/ui/gui/spokes/software.py @@ -30,7 +30,7 @@ from pyanaconda import constants from pyanaconda.ui.communication import hubQ from pyanaconda.ui.gui.spokes import NormalSpoke from pyanaconda.ui.gui.spokes.lib.detailederror import DetailedErrorDialog -from pyanaconda.ui.gui.utils import enlightbox, gtk_action_wait, escape_markup +from pyanaconda.ui.gui.utils import gtk_action_wait, escape_markup from pyanaconda.ui.categories.software import SoftwareCategory
import logging @@ -473,7 +473,7 @@ class SoftwareSelectionSpoke(NormalSpoke): C_("GUI|Software Selection|Error Dialog", "_Modify Software Source"), C_("GUI|Software Selection|Error Dialog", "Modify _Selections")], label=label) - with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): dialog.refresh(self._errorMsgs) rc = dialog.run()
diff --git a/pyanaconda/ui/gui/spokes/source.py b/pyanaconda/ui/gui/spokes/source.py index 17fcec8..2040ea6 100644 --- a/pyanaconda/ui/gui/spokes/source.py +++ b/pyanaconda/ui/gui/spokes/source.py @@ -38,7 +38,7 @@ from pyanaconda.ui.gui import GUIObject from pyanaconda.ui.gui.helpers import GUIDialogInputCheckHandler, GUISpokeInputCheckHandler from pyanaconda.ui.gui.spokes import NormalSpoke from pyanaconda.ui.categories.software import SoftwareCategory -from pyanaconda.ui.gui.utils import enlightbox, fire_gtk_action +from pyanaconda.ui.gui.utils import fire_gtk_action from pyanaconda.iutil import ProxyString, ProxyStringError, cmp_obj_attrs from pyanaconda.ui.gui.utils import gtk_call_once, really_hide, really_show, fancy_set_sensitive from pyanaconda.threads import threadMgr, AnacondaThread @@ -1040,7 +1040,7 @@ class SourceSpoke(NormalSpoke, GUISpokeInputCheckHandler): else: dialog.refresh()
- with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): f = dialog.run(self._get_selected_partition())
if f and f.endswith(".iso"): @@ -1051,7 +1051,7 @@ class SourceSpoke(NormalSpoke, GUISpokeInputCheckHandler):
def on_proxy_clicked(self, button): dialog = ProxyDialog(self.data, self._proxyUrl) - with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): dialog.refresh() dialog.run()
@@ -1067,7 +1067,7 @@ class SourceSpoke(NormalSpoke, GUISpokeInputCheckHandler): return
dialog = MediaCheckDialog(self.data) - with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): unmount = not p.format.status mounts = get_mount_paths(p.path) # We have to check both ISO_DIR and the DRACUT_ISODIR because we @@ -1085,7 +1085,7 @@ class SourceSpoke(NormalSpoke, GUISpokeInputCheckHandler): return
dialog = MediaCheckDialog(self.data) - with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): dialog.run("/dev/" + self._cdrom.name)
def on_protocol_changed(self, combo): diff --git a/pyanaconda/ui/gui/spokes/storage.py b/pyanaconda/ui/gui/spokes/storage.py index f531cc6..bc391c6 100644 --- a/pyanaconda/ui/gui/spokes/storage.py +++ b/pyanaconda/ui/gui/spokes/storage.py @@ -51,7 +51,7 @@ from pyanaconda.ui.gui.spokes.lib.detailederror import DetailedErrorDialog from pyanaconda.ui.gui.spokes.lib.resize import ResizeDialog from pyanaconda.ui.gui.spokes.lib.dasdfmt import DasdFormatDialog from pyanaconda.ui.categories.system import SystemCategory -from pyanaconda.ui.gui.utils import enlightbox, escape_markup, gtk_action_nowait, ignoreEscape +from pyanaconda.ui.gui.utils import escape_markup, gtk_action_nowait, ignoreEscape from pyanaconda.ui.helpers import StorageChecker
from pyanaconda.kickstart import doKickstartStorage, getAvailableDiskSpace @@ -681,7 +681,7 @@ class StorageSpoke(NormalSpoke, StorageChecker): self.clear_info()
def run_lightbox_dialog(self, dialog): - with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): rc = dialog.run()
return rc @@ -908,7 +908,7 @@ class StorageSpoke(NormalSpoke, StorageChecker): C_("GUI|Storage|Error Dialog", "_Quit"), C_("GUI|Storage|Error Dialog", "_Modify Storage Layout")], label=label) - with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): errors = "\n".join(self.errors) dialog.refresh(errors) rc = dialog.run() @@ -924,7 +924,7 @@ class StorageSpoke(NormalSpoke, StorageChecker): "changes to your storage layout.")
dialog = DetailedErrorDialog(self.data, buttons=[_("_OK")], label=label) - with enlightbox(self.window, dialog.window): + with self.main_window.enlightbox(dialog.window): warnings = "\n".join(self.warnings) dialog.refresh(warnings) rc = dialog.run() diff --git a/pyanaconda/ui/gui/spokes/user.py b/pyanaconda/ui/gui/spokes/user.py index 6fb6e8c..6fc8b1c 100644 --- a/pyanaconda/ui/gui/spokes/user.py +++ b/pyanaconda/ui/gui/spokes/user.py @@ -30,7 +30,6 @@ from pyanaconda.ui.gui.spokes import NormalSpoke from pyanaconda.ui.gui import GUIObject from pyanaconda.ui.categories.user_settings import UserSettingsCategory from pyanaconda.ui.common import FirstbootSpokeMixIn -from pyanaconda.ui.gui.utils import enlightbox from pyanaconda.ui.helpers import InputCheck from pyanaconda.ui.gui.helpers import GUISpokeInputCheckHandler, GUIDialogInputCheckHandler
@@ -598,7 +597,7 @@ class UserSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler): self._user.groups.remove(self._wheel.name)
self._advanced.refresh() - with enlightbox(self.window, self._advanced.window): + with self.main_window.enlightbox(self._advanced.window): self._advanced.run()
self.admin.set_active(self._wheel.name in self._user.groups) diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py index 5c0ff83..cab1472 100644 --- a/pyanaconda/ui/gui/spokes/welcome.py +++ b/pyanaconda/ui/gui/spokes/welcome.py @@ -26,7 +26,7 @@ import langtable
from pyanaconda.ui.gui.hubs.summary import SummaryHub from pyanaconda.ui.gui.spokes import StandaloneSpoke -from pyanaconda.ui.gui.utils import enlightbox, setup_gtk_direction, escape_markup +from pyanaconda.ui.gui.utils import setup_gtk_direction, escape_markup from pyanaconda.ui.gui.spokes.lib.lang_locale_handler import LangLocaleHandler
from pyanaconda import localization @@ -309,7 +309,7 @@ class WelcomeLanguageSpoke(LangLocaleHandler, StandaloneSpoke): # Don't display the betanag dialog if this is the final release. if not isFinal: dlg = self.builder.get_object("betaWarnDialog") - with enlightbox(self.window, dlg): + with self.main_window.enlightbox(dlg): rc = dlg.run() dlg.destroy() if rc != 1: @@ -318,7 +318,7 @@ class WelcomeLanguageSpoke(LangLocaleHandler, StandaloneSpoke): if productName.startswith("Red Hat ") and \ is_unsupported_hw() and not self.data.unsupportedhardware.unsupported_hardware: dlg = self.builder.get_object("unsupportedHardwareDialog") - with enlightbox(self.window, dlg): + with self.main_window.enlightbox(dlg): rc = dlg.run() dlg.destroy() if rc != 1: diff --git a/pyanaconda/ui/gui/utils.py b/pyanaconda/ui/gui/utils.py index 51bcc0d..027cf12 100644 --- a/pyanaconda/ui/gui/utils.py +++ b/pyanaconda/ui/gui/utils.py @@ -24,8 +24,7 @@ from pyanaconda.threads import threadMgr, AnacondaThread
from pyanaconda.constants import NOTICEABLE_FREEZE -from contextlib import contextmanager -from gi.repository import Gdk, Gtk, GLib, AnacondaWidgets +from gi.repository import Gdk, Gtk, GLib import Queue import time import threading @@ -297,18 +296,6 @@ def unbusyCursor(): window = Gdk.get_default_root_window() window.set_cursor(Gdk.Cursor(Gdk.CursorType.ARROW))
-@contextmanager -def enlightbox(mainWindow, dialog): - # importing globally would cause a circular dependency - from pyanaconda.ui.gui import ANACONDA_WINDOW_GROUP - - lightbox = AnacondaWidgets.Lightbox(parent_window=mainWindow.get_toplevel()) - ANACONDA_WINDOW_GROUP.add_window(lightbox) - dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) - dialog.set_transient_for(lightbox) - yield - lightbox.destroy() - def ignoreEscape(dlg): """Prevent a dialog from accepting the escape keybinding, which emits a close signal and will cause the dialog to close with some return value
--- po/POTFILES.in | 1 - widgets/doc/AnacondaWidgets-docs.xml | 1 - widgets/src/Lightbox.c | 360 ----------------------------------- widgets/src/Lightbox.h | 67 ------- widgets/src/Makefile.am | 2 - 5 files changed, 431 deletions(-) delete mode 100644 widgets/src/Lightbox.c delete mode 100644 widgets/src/Lightbox.h
diff --git a/po/POTFILES.in b/po/POTFILES.in index fc832df..daf7460 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -107,7 +107,6 @@ widgets/src/SpokeSelector.c widgets/src/SpokeWindow.c widgets/src/StandaloneWindow.c widgets/src/LayoutIndicator.c -widgets/src/Lightbox.c widgets/src/glade-adaptor.c widgets/src/widgets-common.c
diff --git a/widgets/doc/AnacondaWidgets-docs.xml b/widgets/doc/AnacondaWidgets-docs.xml index 7282a24..b67f97d 100644 --- a/widgets/doc/AnacondaWidgets-docs.xml +++ b/widgets/doc/AnacondaWidgets-docs.xml @@ -28,7 +28,6 @@ <xi:include href="xml/HubWindow.xml" /> <xi:include href="xml/SpokeWindow.xml" /> <xi:include href="xml/StandaloneWindow.xml" /> - <xi:include href="xml/Lightbox.xml" /> </chapter>
<chapter id="disks"> diff --git a/widgets/src/Lightbox.c b/widgets/src/Lightbox.c deleted file mode 100644 index 70479e4..0000000 --- a/widgets/src/Lightbox.c +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright (C) 2011 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. - * - * Author: Ales Kozumplik akozumpl@redhat.com - */ - -/* based on an example by Ray Strode rstrode@redhat.com */ - -/** - * SECTION: Lightbox - * @title: Lightbox - * @short_description: Functions to draw a window over a shaded background - * - * The lightbox is a widget used to display one window (a dialog or other - * similar window, typically) over top of the main window in the background. - * The main window is shaded out to make the foreground window stand out more, - * as well as to reinforce to the user that the background window may not be - * interacted with. - * - * The lightbox window will show as soon as it is created. - */ - -/* - * We have two methods for drawing the transparent background, depending - * on whether we can use a compositing window manager for alpha blending - * or not. - * - * In a compositing window manager (e.g., gnome-shell on the livecd), we - * create the transparent background using Gtk by overriding the window's - * background color and setting an opacity. - * - * In a non-compositing window manager (e.g., metacity on the install DVD), - * we override Gtk's drawing of the widget entirely. We set paintable to - * false to indicate that theme information should not be applied, and then - * as the parent-window property is being set, we use cairo to paint a new - * surface using the parent's Gdk window as the source pattern, apply a 50% - * translucent fill to the surface, and then use this surface as the - * background for the lightbox's Gdk window. - */ - -#include <cairo.h> -#include <gtk/gtk.h> - -#include "Lightbox.h" - -#include "intl.h" - -enum { - PROP_PARENT_WINDOW = 1 -}; - -struct _AnacondaLightboxPrivate { - GtkWindow *transient_parent; - gboolean parent_configure_event_handler_set; - guint parent_configure_event_handler; - - gboolean composited; -}; - -static void anaconda_lightbox_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); -static void anaconda_lightbox_set_parent_window(GObject *gobject, GParamSpec *psec, gpointer user_data); - -static gboolean anaconda_lb_parent_configure_event(GtkWidget *parent, GdkEvent *event, gpointer lightbox); -static void anaconda_lb_cleanup(GtkWidget *widget, gpointer user_data); - -G_DEFINE_TYPE(AnacondaLightbox, anaconda_lightbox, GTK_TYPE_WINDOW) - -static void anaconda_lightbox_class_init(AnacondaLightboxClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - - object_class->set_property = anaconda_lightbox_set_property; - - /** - * AnacondaLightbox:parent-window: - * - * The parent of this window. This value is used as the transient parent - * for this window. - * - * Since: 2.0 - */ - g_object_class_install_property(object_class, - PROP_PARENT_WINDOW, - g_param_spec_object("parent-window", - P_("Parent Window"), - P_("The parent of this window"), - GTK_TYPE_WINDOW, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); - - g_type_class_add_private(object_class, sizeof(AnacondaLightboxPrivate)); -} - -static void anaconda_lightbox_init(AnacondaLightbox *lightbox) -{ - lightbox->priv = G_TYPE_INSTANCE_GET_PRIVATE(lightbox, - ANACONDA_TYPE_LIGHTBOX, - AnacondaLightboxPrivate - ); - - /* Disable the window decorations on the parent (Gtk.Window) class */ - gtk_container_set_border_width(GTK_CONTAINER(lightbox), 0); - gtk_window_set_decorated(GTK_WINDOW(lightbox), FALSE); - gtk_window_set_has_resize_grip(GTK_WINDOW(lightbox), FALSE); - - gtk_window_set_type_hint(GTK_WINDOW(lightbox), GDK_WINDOW_TYPE_HINT_SPLASHSCREEN); - - /* Decide now which background drawing method to use */ - lightbox->priv->composited = gtk_widget_is_composited(GTK_WIDGET(lightbox)); - if (lightbox->priv->composited) - { - GdkRGBA color = {0.0, 0.0, 0.0, 1.0}; /* opaque black */ - - /* Set the background to black */ - gtk_widget_override_background_color(GTK_WIDGET(lightbox), - GTK_STATE_FLAG_NORMAL, - &color - ); - - /* Set the opacity to 50% */ - gtk_widget_set_opacity(GTK_WIDGET(lightbox), 0.5); - } - else - { - /* - * Indicate we will handle drawing the widget, do the rest in - * anaconda_lightbox_set_parent_window - */ - gtk_widget_set_app_paintable(GTK_WIDGET(lightbox), TRUE); - } - - /* cleanup */ - g_signal_connect(lightbox, "destroy", G_CALLBACK(anaconda_lb_cleanup), NULL); -} - -static void anaconda_lightbox_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - AnacondaLightbox *lightbox = ANACONDA_LIGHTBOX(object); - AnacondaLightboxPrivate *priv = lightbox->priv; - - switch (prop_id) - { - case PROP_PARENT_WINDOW: - priv->transient_parent = GTK_WINDOW(g_object_ref(g_value_get_object(value))); - /* The property is CONSTRUCT_ONLY, so no point calling notify */ - anaconda_lightbox_set_parent_window(object, pspec, NULL); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -/* - * Adjust the lightbox any time the parent window's size, position or stacking - * changes. Returns FALSE to allow signal processing to continue. - */ -static gboolean anaconda_lb_parent_configure_event( - GtkWidget *parent, - GdkEvent *event, - gpointer lightbox - ) -{ - /* Always return FALSE to continue processing for this signal. */ - GdkWindow *g_lightbox_window; - gint x, y, width, height; - - if ((event->type != GDK_CONFIGURE) || - !GTK_IS_WIDGET(parent) || - !GTK_IS_WINDOW(lightbox)) - { - return FALSE; - } - - g_lightbox_window = gtk_widget_get_window(GTK_WIDGET(lightbox)); - if (NULL == g_lightbox_window) - { - /* - * No underlying GdkWindow. This may mean the lightbox is not yet - * realized, but whatever the cause, there's nothing we can do here. - */ - return FALSE; - } - - /* Get the current size and position of the lightbox */ - gdk_window_get_geometry(g_lightbox_window, &x, &y, &width, &height); - - /* Resize and move the lightbox if anything changed */ - if ((event->configure.x != x) || (event->configure.y != y) || - (event->configure.width != width) || (event->configure.height != height)) - { - gdk_window_move_resize(g_lightbox_window, - event->configure.x, - event->configure.y, - event->configure.width, - event->configure.height - ); - } - - return FALSE; -} - - -/* - * Draw the window background. Uses the gobject notify handler signature - * in case we want to allow parent-window to change in the future. - */ -static void anaconda_lightbox_set_parent_window( - GObject *gobject, - GParamSpec *psec, - gpointer user_data - ) -{ - AnacondaLightbox *lightbox; - - GdkWindow *g_lightbox_window; - GdkWindow *g_parent_window; - cairo_surface_t *surface; - cairo_pattern_t *pattern; - cairo_t *cr; - - if (!ANACONDA_IS_LIGHTBOX(gobject)) - { - return; - } - - lightbox = ANACONDA_LIGHTBOX(gobject); - - /* - * Skip the check for whether the value has changed, since we only allow - * it to be set in the constructor - */ - - if (lightbox->priv->transient_parent) - { - gtk_window_set_transient_for(GTK_WINDOW(lightbox), lightbox->priv->transient_parent); - - /* Destroy the lightbox when the parent is destroyed */ - gtk_window_set_destroy_with_parent(GTK_WINDOW(lightbox), TRUE); - - /* Set the initial position to the center of the parent */ - gtk_window_set_position(GTK_WINDOW(lightbox), GTK_WIN_POS_CENTER_ON_PARENT); - - /* Set the lightbox to the parent window's dimensions */ - g_parent_window = gtk_widget_get_window(GTK_WIDGET(lightbox->priv->transient_parent)); - gtk_window_set_default_size(GTK_WINDOW(lightbox), - gdk_window_get_width(g_parent_window), - gdk_window_get_height(g_parent_window) - ); - - /* make the shade move with the parent window */ - /* Add a reference for the lightbox pointer held for the handler */ - g_object_ref(lightbox); - lightbox->priv->parent_configure_event_handler = - g_signal_connect(lightbox->priv->transient_parent, "configure-event", - G_CALLBACK(anaconda_lb_parent_configure_event), lightbox); - lightbox->priv->parent_configure_event_handler_set = TRUE; - - /* Handle the non-compositing background case */ - if (!lightbox->priv->composited) - { - /* - * NB: We should probably be handling the draw signal in order to refresh - * the transparent pattern from the parent window whenver something - * changes, but by the time things get to the signal handler the surface is - * already set up and doesn't support alpha channels and replacing it - * doesn't seem to work quite right. Besides, none of these windows are - * supposed to move anyway. - */ - - /* Realize the window to initialize the Gdk objects */ - if (!gtk_widget_get_realized(GTK_WIDGET(lightbox))) - { - gtk_widget_realize(GTK_WIDGET(lightbox)); - } - g_lightbox_window = gtk_widget_get_window(GTK_WIDGET(lightbox)); - - /* Create a new surface that supports alpha content */ - surface = gdk_window_create_similar_surface(g_lightbox_window, - CAIRO_CONTENT_COLOR_ALPHA, - gdk_window_get_width(g_parent_window), - gdk_window_get_height(g_parent_window)); - cr = cairo_create(surface); - - /* Use the parent window as a pattern and paint it on the surface */ - gdk_cairo_set_source_window(cr, g_parent_window, 0, 0); - cairo_paint(cr); - - /* Paint a black, 50% transparent shade */ - cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.5); - cairo_paint(cr); - - cairo_destroy(cr); - - /* Use the surface we painted as the window background */ - pattern = cairo_pattern_create_for_surface(surface); - gdk_window_set_background_pattern(g_lightbox_window, pattern); - cairo_pattern_destroy(pattern); - } - } - - gtk_widget_show(GTK_WIDGET(lightbox)); -} - -/* Clean up references to lightbox held by the parent window */ -static void anaconda_lb_cleanup(GtkWidget *widget, gpointer user_data) -{ - AnacondaLightbox *lightbox; - - /* Remove the signal handlers set on the parent window */ - if (ANACONDA_IS_LIGHTBOX(widget)) - { - lightbox = ANACONDA_LIGHTBOX(widget); - - if (lightbox->priv->parent_configure_event_handler_set) - { - g_signal_handler_disconnect(lightbox->priv->transient_parent, - lightbox->priv->parent_configure_event_handler); - lightbox->priv->parent_configure_event_handler_set = FALSE; - g_object_unref(lightbox); - } - - /* Drop the reference for the parent window */ - g_object_unref(lightbox->priv->transient_parent); - lightbox->priv->transient_parent = NULL; - } -} - -/** - * anaconda_lightbox_new: - * @parent: The parent for this window - * - * Creates a new #AnacondaLightbox, which is a top-level, undecorated window - * that uses a shaded version of its parent window's background as its own - * background. - * - * Returns: the new lightbox as a #GtkWidget - */ -GtkWidget* anaconda_lightbox_new(GtkWindow *parent) -{ - AnacondaLightbox *lightbox; - - lightbox = ANACONDA_LIGHTBOX(g_object_new(ANACONDA_TYPE_LIGHTBOX, - "parent-window", parent, - NULL)); - - return GTK_WIDGET(lightbox); -} diff --git a/widgets/src/Lightbox.h b/widgets/src/Lightbox.h deleted file mode 100644 index 7e645e2..0000000 --- a/widgets/src/Lightbox.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2011 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. - * - * Author: Ales Kozumplik akozumpl@redhat.com - */ - -#ifndef LIGHTBOX_H -#define LIGHTBOX_H - -#include <gtk/gtk.h> - -G_BEGIN_DECLS - -#define ANACONDA_TYPE_LIGHTBOX (anaconda_lightbox_get_type()) -#define ANACONDA_LIGHTBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ANACONDA_TYPE_LIGHTBOX, AnacondaLightbox)) -#define ANACONDA_LIGHTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ANACONDA_TYPE_LIGHTBOX, AnacondaLightboxClass)) -#define ANACONDA_IS_LIGHTBOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ANACONDA_TYPE_LIGHTBOX)) -#define ANACONDA_IS_LIGHTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ANACONDA_TYPE_LIGHTBOX)) -#define ANACONDA_LIGHTBOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ANACONDA_TYPE_LIGHTBOX, AnacondaLightboxClass)) - -typedef struct _AnacondaLightbox AnacondaLightbox; -typedef struct _AnacondaLightboxClass AnacondaLightboxClass; -typedef struct _AnacondaLightboxPrivate AnacondaLightboxPrivate; - -/** - * AnacondaLightbox: - * - * The AnacondaLightbox struct contains only private fields and should not - * be directly accessed. - */ -struct _AnacondaLightbox { - GtkWindow parent; - - /*< private >*/ - AnacondaLightboxPrivate *priv; -}; - -/** - * AnacondaLightboxClass: - * @parent_class: The object class structure needs to be the first element in - * the widget class structure in order for the class mechanism - * to work correctly. This allows a AnacondaLightbox - * pointer to be cast to a #GtkWindow pointer. - */ -struct _AnacondaLightboxClass { - GtkWindowClass parent_class; -}; - -GType anaconda_lightbox_get_type(void); -GtkWidget *anaconda_lightbox_new(GtkWindow *parent); - -G_END_DECLS - -#endif diff --git a/widgets/src/Makefile.am b/widgets/src/Makefile.am index 5862fcd..dfed620 100644 --- a/widgets/src/Makefile.am +++ b/widgets/src/Makefile.am @@ -33,7 +33,6 @@ GISOURCES = BaseWindow.c \ SpokeWindow.c \ StandaloneWindow.c \ LayoutIndicator.c \ - Lightbox.c \ BaseStandalone.c \ widgets-common.c
@@ -45,7 +44,6 @@ GIHDRS = BaseWindow.h \ SpokeWindow.h \ StandaloneWindow.h \ LayoutIndicator.h \ - Lightbox.h \ BaseStandalone.h \ widgets-common.h
--- initial_setup/gui/hubs/initial_setup.glade | 27 +++++++++++++++++---------- initial_setup/gui/hubs/initial_setup_hub.py | 9 --------- 2 files changed, 17 insertions(+), 19 deletions(-)
diff --git a/initial_setup/gui/hubs/initial_setup.glade b/initial_setup/gui/hubs/initial_setup.glade index 3fc725d..b6a4ad2 100644 --- a/initial_setup/gui/hubs/initial_setup.glade +++ b/initial_setup/gui/hubs/initial_setup.glade @@ -1,24 +1,31 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.18.3 --> <interface> - <!-- interface-requires gtk+ 3.0 --> - <!-- interface-requires AnacondaWidgets 1.0 --> + <requires lib="gtk+" version="3.0"/> + <requires lib="AnacondaWidgets" version="3.0"/> <object class="AnacondaHubWindow" id="summaryWindow"> - <property name="startup_id">filler</property> <property name="can_focus">False</property> - <property name="startup_id">filler</property> - <property name="mnemonics_visible">False</property> - <property name="focus_visible">False</property> <property name="distribution" translatable="yes">DISTRIBUTION SETUP</property> <property name="window_name" translatable="yes">INITIAL SETUP</property> + <property name="quit_button">quitButton</property> + <property name="continue_button">continueButton</property> <child internal-child="main_box"> <object class="GtkBox" id="AnacondaHubWindow-main_box1"> <property name="can_focus">False</property> <property name="orientation">vertical</property> - <child internal-child="nav_area"> - <object class="GtkGrid" id="AnacondaHubWindow-nav_area1"> + <child internal-child="nav_box"> + <object class="GtkEventBox" id="AnacondaHubWindow-nav_box1"> <property name="can_focus">False</property> - <child> - <placeholder/> + <child internal-child="nav_area"> + <object class="GtkGrid" id="AnacondaHubWindow-nav_area1"> + <property name="can_focus">False</property> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + </object> </child> </object> <packing> diff --git a/initial_setup/gui/hubs/initial_setup_hub.py b/initial_setup/gui/hubs/initial_setup_hub.py index 45c8033..22b7add 100644 --- a/initial_setup/gui/hubs/initial_setup_hub.py +++ b/initial_setup/gui/hubs/initial_setup_hub.py @@ -27,12 +27,3 @@ class InitialSetupMainHub(Hub): for spoke in self._spokes.itervalues(): spoke.window.set_property("distribution", product.product_title().upper()) - - - @property - def continueButton(self): - return self.builder.get_object("continueButton") - - @property - def quitButton(self): - return self.builder.get_object("quitButton")
On Mon, 2014-06-30 at 15:41 -0400, David Shea wrote:
Changes since last time:
Fixed the stuff that Vratislav pointed out:
- Added a macro for the sidebar width percentage
- Changed finalize to dispose since that's more in the spirit of reference releases
- Fixed a couple of comments
- Changed Spoke._visitedSinceApplied to a public property
- Added MainWindow to ANACONDA_WINDOW_GROUP. Added the exception window to it, too, since there is no longer a separate Gtk.main loop for spokes and no separate spoke window to get stuck under.
Fixed makeupdates to work with the widgets version increase
Increased the version of the .gir and .typelib files too since that seems like the thing to do
Replaced the lightbox with something hopefully less fragile
Removed the resize on realize part from MainWindow since it turns out it didn't actually work
Updated initial-setup for the new HubWindow
Added a couple more deprecation warning ignores since I apparently I missed some last time
David Shea (10): Add a couple more deprecation warning ignores Remove the custom accelerators from custom storage. Use globs for the anaconda widgets library paths Increased the version of anaconda-widgets to 3.0 Add a class BaseStandalone. Add a window to manage Anaconda screen transitions. Add a delete-event handler for the main window Added a method to create new GdkPixbufs from in-memory data Implement the lightbox in MainWindow Remove the Lightbox widget
po/POTFILES.in | 2 +- pyanaconda/ui/common.py | 2 +- pyanaconda/ui/gui/__init__.py | 212 ++++++++++-- pyanaconda/ui/gui/hubs/__init__.py | 91 ++--- pyanaconda/ui/gui/hubs/progress.glade | 5 +- pyanaconda/ui/gui/hubs/progress.py | 11 +- pyanaconda/ui/gui/hubs/summary.glade | 6 +- pyanaconda/ui/gui/hubs/summary.py | 7 - pyanaconda/ui/gui/spokes/__init__.py | 19 +- pyanaconda/ui/gui/spokes/custom.glade | 3 +- pyanaconda/ui/gui/spokes/custom.py | 24 +- pyanaconda/ui/gui/spokes/datetime_spoke.py | 4 +- pyanaconda/ui/gui/spokes/filter.py | 7 +- pyanaconda/ui/gui/spokes/keyboard.py | 8 +- .../ui/gui/spokes/lib/custom_storage_helpers.glade | 1 - pyanaconda/ui/gui/spokes/network.py | 8 +- pyanaconda/ui/gui/spokes/software.py | 4 +- pyanaconda/ui/gui/spokes/source.py | 10 +- pyanaconda/ui/gui/spokes/storage.py | 8 +- pyanaconda/ui/gui/spokes/user.py | 3 +- pyanaconda/ui/gui/spokes/welcome.py | 10 +- pyanaconda/ui/gui/tools/run-spoke.py | 7 +- pyanaconda/ui/gui/utils.py | 15 +- scripts/makeupdates | 12 +- widgets/configure.ac | 2 +- widgets/doc/AnacondaWidgets-docs.xml | 2 +- widgets/glade/AnacondaWidgets.xml | 13 +- widgets/src/BaseStandalone.c | 377 +++++++++++++++++++++ widgets/src/BaseStandalone.h | 81 +++++ widgets/src/BaseWindow.c | 14 +- widgets/src/BaseWindow.h | 6 +- widgets/src/HubWindow.c | 94 +---- widgets/src/HubWindow.h | 6 +- widgets/src/LayoutIndicator.c | 2 + widgets/src/Lightbox.c | 360 -------------------- widgets/src/Lightbox.h | 67 ---- widgets/src/Makefile.am | 20 +- widgets/src/SpokeWindow.c | 29 +- widgets/src/StandaloneWindow.c | 242 +++---------- widgets/src/StandaloneWindow.h | 15 +- widgets/src/widgets-common.c | 51 ++- widgets/src/widgets-common.h | 3 + 42 files changed, 893 insertions(+), 970 deletions(-) create mode 100644 widgets/src/BaseStandalone.c create mode 100644 widgets/src/BaseStandalone.h delete mode 100644 widgets/src/Lightbox.c delete mode 100644 widgets/src/Lightbox.h
These all look good to me with that one minor comment on PATCH 05/10. Thanks for applying all the suggestions.
anaconda-patches@lists.fedorahosted.org