[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