[PATCH 4/5] Add a window to manage Anaconda screen transitions.
Vratislav Podzimek
vpodzime at redhat.com
Wed Jun 18 10:24:36 UTC 2014
On Mon, 2014-06-16 at 15:09 -0400, David Shea wrote:
> 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.
> ---
> pyanaconda/ui/gui/__init__.py | 100 ++++++++++++++++++++++++++++++++---
> pyanaconda/ui/gui/hubs/__init__.py | 56 +++++++-------------
> 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 +-------
> 9 files changed, 137 insertions(+), 109 deletions(-)
>
> diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py
> index 15c55aa..38576a1 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,89 @@ 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()
> +
> + # Connect to realize to perform some actions with the GdkScreen
> + self.connect("realize", self._on_realize)
> +
> + self._current_action = None
> +
> + def _on_realize(self, widget, user_data=None):
> + # Change the default size to the full size of the GdkScreen so that when
> + # we're unmaximized the size of the anaconda window isn't something dumb
> + screen = self.get_screen()
> + self.set_default_size(screen.get_width(), screen.get_height())
> +
> + @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.
> @@ -258,6 +345,8 @@ class GraphicalUserInterface(UserInterface):
> self._mehInterface = GraphicalExceptionHandlingIface(
> self.lightbox_over_current_action)
>
> + self.mainWindow = MainWindow()
This window should be added to the ANACONDA_WINDOW_GROUP or we should
get rid of it (may be unnecessary now that we have just a single base
window).
> +
> basemask = "pyanaconda.ui"
> basepath = os.path.dirname(__file__)
> updatepath = "/tmp/updates/pyanaconda/ui"
> @@ -349,7 +438,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)
>
> @@ -425,7 +514,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()
> @@ -531,8 +620,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)
>
> diff --git a/pyanaconda/ui/gui/hubs/__init__.py b/pyanaconda/ui/gui/hubs/__init__.py
> index 3645759..d9e904d 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
> @@ -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
Maybe we could create a @property for this attribute instead of
accessing it being private.
--
Vratislav Podzimek
Anaconda Rider | RHCE | Red Hat, Inc. | Brno - Czech Republic
More information about the anaconda-patches
mailing list