Meet Bob, I am sure most of us remember him from ‘Bob the Builder’. An animated series many of us would’ve watched as children.
Bob the Builder and his machine team are ready to tackle any project. Bob and the Can-Do Crew demonstrate the power of positive thinking, problem-solving, teamwork, and follow-through. The team always shows that “The Fun Is In Getting It Done!” Bob the Builder can be seen building, digging, and hauling.
Developers : (Summon GtkBuilder) GtkBuilder !
#define BobTheBuilder GtkBuilder
GTK+ supports the separation of user-interface layout from your business logic, by using UI descriptions in an XML format that can be parsed by the GtkBuilder class. So what GtkBuilder does is it processed a UI definition given in XML format and does all the heavy-lifting that needs to be done for allocating widegets, styling them ID-ing, packing etc. and than allows you to obtain a reference to the concerned widget in C code which allows for tweaking or manipulating the behaviour of the widget based on business logic. Thus importantly keeping the UI definitions seperate from the business logic. Thus very similar to what Bob The Builder does, takes a blueprint and turns it into real houses and building. GtkBuilder parses objects definitions in XML and creates real runtime objects of the same property, thus sparing us the part where we call the same functions again and again to create, destroy and style widgets.
Nautilus Properties Window : Porting to GtkBuilder
GtkDialog vs GtkWindow ?
Dialog boxes are a convenient way to prompt the user for a small amount of input, e.g. to display a message, ask a question, or anything else that does not require extensive effort on the user’s part. GTK+ treats a dialog as a window split vertically. The top section is a GtkVBox, and is where widgets such as a GtkLabel or a GtkEntry should be packed. The bottom area is known as the “action area”. This is generally used for packing buttons into the dialog which may perform functions such as cancel, ok, or apply.
GtkWindow is a toplevel window which can contain other widgets. Windows normally have decorations that are under the control of the windowing system and allow the user to manipulate the window (resize it, move it, close it,…).
Old Implementation of the Properties Window.
Uses Action area for Help and Close Button.
Current Implementation of the properties Window.
Doesn't Utilize Action Area
So now we know that the only reason the properties window is still a GtkDialog is because that is where it’s History has lead it to. Today It doesn’t Utilize or make use of any special feature offered by a GtkDialog over GtkWindow.
Thanks @alexm for the idea !
Commit cb706825 preserves the current behaviour of properties window and makes it Inherit GtkWindow instead of GtkDialog.
Here Comes the part where Bob’s (aka. GtkBuilder) gonna take charge of the applications UI building and rids the code base of some huge complex thickets of UI building code.
We often find ourselves creating controller object classes when we realize that Objects offered natively do not satisfy our specific tailored needs. Be it storing information or, references to signal handlers or references to those UI components which need conditional tweaking.
GtkBuilder supports the use of templates, where the Class Name of the toplevel container is set same as the corresponding Controller Class name in C code. And the internal child widgets whose handles are required in the c-code are ID-ed with same identifiers as the one used for the corresponding reference variable in the Class definition.
Icon Widget and Name Field Widget:
Name Field is a GtkEntry
Icon Widget is a GtkButton
Name Field is a GtkLabel
Icon Widget is a GtkImage
So what do you do when you have two widgets which are possible candidates for a given place holder in the UI.
Traditional approach calls for what (@antoniof) calls the gtk_widget_destroy() dance where the business logic determines which kind of Widget is required and packs it into the container.Further logic determines what kind of Widget has been packed and proceeds accordingly. Or alternatively handles each and every possibility of Widget Creation, replacement with if-else statements and destroys the Widget not required, with gtk_widget_destroy().
Contemporary approach with the use of GtkBuilder provides us with GtkStack which is a widget capable of containing more than one GtkWidget as its children, and makes one of these visible with gtk_stack_set_visible_child(). So an elegant solution for this problem was to create both children an packing them in the GtkStack. Than populating and setting visible the appropriate child.
Rest of the Rows
It can be noticed that there is a different set of rows displayed based on the type of file selection the properties window is launched for! It's also notice-able that each row is a combination of GtkLabel : GtkLabel, all title:value pairs!
Handling Conditional Rows : Each and every widget which could ever find itself in the UI is defined in nautilus-properties-window.ui XML definition. However its visibility is controlled by the logical structure of the program which calls gtk_widget_show() and gtk_widget_hide() on the widgets which are supposed to be shown and hidden accordingly.
Generating each row : The existing code achieves it with the help of an over-engineered hierarchy of function calls, it requsting appednd_title_and_ellipsizing_value() for a row of particular kind which is constructed in the calls which follow that.
While the above mechanism can be conveniently used to generate rows but anything which has to do with debugging this mechanism or imagining the UI structure anywhere midway in the function calls is very difficult, and messed up. Moreover It doesn’t allow for a granular control on the properties of a particular row, since all of them are generated by the same interface.
Easier Solution : An easier solution is to define each and every Row GtkLabel pair in the XML definition and obtaining its reference in the NautilusPropertiesWindow struct members. The properties of these labels could be found out by tracing the execution of the row generating mechanism. The rows can be populated and be made visible/hidden based on the business logic, from the control structures of the program.
Commit 08fbb49b obtains all the rows og the basic grid from template and gets rid of the complex row generating mechanism present currently.
And this is the progress till now ! This was long post : p !