[PATCH 4/5] Add a window to manage Anaconda screen transitions.

David Shea dshea at redhat.com
Wed Jun 18 13:07:24 UTC 2014


On 06/18/2014 06:24 AM, Vratislav Podzimek wrote:
> 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).

Yes, I'll add it to the group. I kept the group around because it seemed 
important for lightbox stuff, so the main window should be part of the 
group at least until we figure out how to get rid of the lightbox widget.

>> +
>>       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.
>

Sure, that seems reasonable.


More information about the anaconda-patches mailing list