mercredi 17 juin 2015

Extracting Boxes' wizard's pages

A few days ago, Zeeshan suggested me to extract Boxes' wizard's pages out of the Wizard class and to make them full fledged widgets/classes. This idea is very welcome as it would move a lot of complexity out of the Wizard class (as it is currently implementing most of the pages) and, in the long run, make its port to GtkAssistant easier.

I spent the last few days playing with the pages, discovering how they are implemented and extracting some of them.

Boxes' wizard's page graph
Extracting pages, kind of

The current state of Boxes' installation wizard

Here is the wizard's page flow as presented to the user:

Boxes' wizard's page graph
The current page flow

The wizard is currently implemented with such a widget hierarchy:

  • the WizardWindow class
    • the WizardToolbar class
    • the Wizard class, a stack of pages
      • the WizardSource class
        • the source selection page
        • the URL entry page
      • the preparation page
      • the setup page
      • the review page
    • the file chooser page
    • the customization page

The wizard has three layers of pages, most of them behaving slightly differently: the WizardSource class is composed of the source selection page and of the URL entry page; the preparation, setup and review pages are directly implemented by the Wizard class; and the file chooser and customization pages are implemented by the WizardWindow class. Also, the URL entry page have its own back button and doesn't use the common one in the headerbar to go back to the source selection page, which is inconsistent with every other page's behaviour.

Most pages are directly implemented by the Wizard class, making of it a mishmash of intertwined behaviours and responsibilities, especially as every page is directly modifying the wizard's state. Some other are handled by the WizardWindow and WizardSource classes, but in the end, no page exist as a single class. All of this lead to a great complexity which should be corrected in order to make the wizard more maintainable and to port it to GtkAssistant.

Where to go

I want to keep the page flow the same while changing the implementation drastically: the Wizard should inherit GtkAssistant, each page should be a widget with its own class and no page should be nested. Here is what I want to achieve:

  • Wizard
    • WizardSourceSelectionPage
    • WizardFileChooserPage
    • WizardUrlEntryPage
    • WizardPreparationPage
    • WizardSetupPage
    • WizardCustomizationPage
    • WizardReviewPage

This refactoring has to be done properly: what's the point of moving from known code to something not inherently better and known only by the person who wrote it?

In order to untangle the pages from the wizard without changing their code too much, I want to use more event-driven programming so that the pages don't change the wizard's state directly but notify it of changes, letting it react accordingly. It will help to uncouple the wizard's behaviour from the implementations of the pages, hence making its code simpler and its porting to GtkAssistant easier.

Going there

What have been done:

  • spliting the model of the WizardSource class from the widget: patch submitted
  • renaming WizardSource as WizardSourcePage to keep the page names consistent
  • extracting the preparation page from the Wizard class: patch submitted
  • extracting the setup page from the Wizard class: patch submitted
  • extracting the review page from the Wizard class: patch submitted

What has to be done:

  • editing the Wizard class' API to mimick GtkAssistant's one: in progress
  • extracting the source selection page from the WizardSourcePage class
  • extracting the URL entry page from the WizardSourcePage class
  • removing the WizardSourcePage class
  • extracting the file chooser page from the WizardWindow
  • extracting the customization page from the WizardWindow
  • merging the Wizard, WizardToolbar and WizardWindow classes
  • making the Wizard class inherit from GtkAssistant

As pointed out in my last article, GtkAssistant forced a padding to its pages which was problematic as some of Boxes' wizard's pages need to take all the available space to display complex widgets or infobars. This bug have been solved pretty quickly by Matthias Classen who added a way to disable the padding for specific pages. Thanks Matthias, it will be useful. =)

Other stuff

I started working on other stuff too.

I tried to add support for the application/x-cd-image MIME type to Boxes but without success. Starting Boxes with a path or an URI pointing to an ISO file works perfectly, but trying to open one with Boxes via Nautilus or xdg-open didn't work. I need to investigate this.

Last march I started adding a list view to Boxes' collection view. It worked well except for a warning that I didn't understand and didn't have time to investigate. The warning was still present and I tracked it as coming from libgd, I then opened a ticket and submitted a patch, which so far are still waiting for reviews.

I have no time to get bored when working on Boxes! =D

mardi 9 juin 2015

Playing with GtkAssistant

I decided to spend some time today to play with GtkAssistant, more precisely, I tried to build a mock installation wizard mimicking Boxes' one in order to test how I could adapt its behaviour to make it GtkAssistant ready.

Overall, I enjoyed using GtkAssistant, it is a quite well thought out widget offering a lot of potential for a small API.

But despite how good GtkAssistant is, I encountered problems adapting it to my need. Here follows a list of what bogged me down.

A sequence of pages

GtkAssistant is great at implementing a sequence of pages, unfortunately, Boxes' wizard is more like a graph of pages.

Boxes' wizard's page graph
Boxes' wizard's page graph

Such a configuration is clearly out of GtkAssistant's scope and it can easily be solved by arranging the pages as a sequence and setting a custom "forward" function, so it's not that much of a problem.

Action area

GtkAssistant allows you to mark a page as custom, which will show no button in the action area when visiting this page. You can then add buttons to the action area in order to give some controls to the user. Unfortunately, I found no way to add buttons to the left side of the action area, usually containing the "back" and "cancel" buttons, which is pretty annoying.

Adding a way to add widgets to the left side or to hide only the buttons of the right side of the action area would help. In the end, I didn't found a way to make the "custom" page type useful to my case (which may just be better in the long run).

Content padding

GtkAssistant sourround its pages with some padding, which most of the time is a good idea, unfortunately one of the pages I want to use is a big and complex widget (a GtkFileChooserWidget) which need to take as much space as possible for the user's ease, but also not to look ugly.

In red, the unwanted padding
In red, the unwanted padding

Cancelling a progressing page

You can't cancel (and go back from) a page typed as "progress" if it haven't been completed. It may make sense if the work in progress must be finished properly, but it's not always the case.

For example, Boxes use a progress bar to indicate that an installation medium is being downloaded, but such a task should be cancellable by the user (he could notice that he isn't downloading the right image), so even if the "progress" page type seems to correspond to this use case, it can't be used because it doesn't allow you to cancel a possibly wrong and probably long task.

It's not that much of a problem though as a regular page can be tweaked to have the desired behaviour.

Transition animations

There is no transition animation when moving between pages. It's not a huge problem but having some animation could make the page transitions more understandable. I would love to have a left to right animation when progressing forward and vice versa when progressing backward. =)

Help wanted

If you are a Gtk+ developer or that you know how to fix some of these problems, especially the padding one, I would greatly appreciate you help! =D

dimanche 7 juin 2015

Boxes' hardening sprint: two weeks in

Finishing my 4th year of CS studies

I spent the last two weeks working hard on the report and the presentation of the project my colleagues and I worked on all the semester long: creating the Stibbons multi-agent system programming language and development environment.

I am very proud of what we accomplished and I’ll probably present it to you in the upcoming weeks. =)

Planning the port of Boxes' installation wizard to GtkAssistant

All this work unfortunately let me little time to work on Boxes, but I nonetheless took some time to look at how its installation wizard is implemented and planned how to port it to GtkAssistant.

Boxes' installation wizard

Currently, the wizard is ordered that way:

  • WizardWindow
    • WizardToolbar: the toolbar containing the navigation buttons
    • Wizard: the stack of pages

Most of the wizard’s intelligence seems to lie in Wizard and its pages, I’ll have to dig further into Boxes' code in order to fully understand how the wizard work.

GtkAssistant

A GtkAssistant is a window which allow a developer to guide the end users through complex configuration processes by splitting them into several steps represented by pages that can be completed.

GtkAssistant’s API is pretty simple, you can:

  • add or remove pages to the assistant
  • navigate through them
  • set the title of a page
  • set the type of a page
  • set whether a page has been completed or not

If a page has been completed, the toolbar allow the end user to move to the next page, except if it is the last page in which case it will allow him to complete the whole configuration process.

The plan

Despite a similar behaviour from the end user’s perspective, the wizard’s behaviour differ significantly from the one of GtkAssistant. Some tailoring will be required to make them match.

To smoothly pass from the current wizard to a new one using GtkAssistant, I plan to follow these steps:

  • implement one by one the needed components (understand “logical sets of public methods”) of GtkAssistant into WizardWindow:
    • setting and getting the current page
    • setting and getting a page’s title
    • setting and getting whether a page have been completed or not
  • once all the necessary components have been implemented, make WizardWindow inherit from GtkAssistant and remove the custom method implementations
  • remove the now unneeded WizardToolbar and Wizard classes.

Hopefully, next week will be enough to implement such a change!