Granite.Widgets.ModeButton()

Tags: Granite Gdk.Pixbuf Gtk.IconTheme Granite.Widgets.ModeButton Gtk.Label Gtk.Image Gtk.Box

Images: Screenshot 1

There are a few different concepts that ended up getting rolled all into one nice package for this tutorial. I'm also programming with Elementary's Granite framework and in this little application I showcase the ModeButton.

Now the function of this application is simply to change the icon that shows in the middle depending on which button at the top has been pressed. So lets get started. Have our boilerplate template ready so we can add the necessary code. If you don't have it, head over to the homepage.

First the button buttons need to be made at the top of the page. This "Mode Button" is included in the Granite framework. I don't know if granite works on non Elementary OS distrubutions but I don't see why not.

var mb = new Granite.Widgets.ModeButton(); //Make a new ModeButton widget.
	
	//Create two labels. Assign names for a check later on.
	var label1 = new Gtk.Label("Its ok!");
	label1.name = "ok";
        
	var label2 = new Gtk.Label("Cancel!");
	label2.name = "cancel";
        
	//Add each label to the Mode Button.
	mb.append(label1);
	mb.append(label2);
        
	//Specify which button is active on initialization
	mb.set_active(0);

You may put this code in the application constructor or in its own method inside the application class and then call it from the constructor. But this won't appear on the application yet. We have to add it to a Gtk.Box otherwise we can only add one widget to the application window.

var layout = new Gtk.Box(Gtk.Orientation.VERTICAL, 5); //Set the box to vertical orientation with a spacing of 5
	this.add(layout); //Add the box layout to the window.

If the ModeButton was added directly to the Gtk.Box then the button will stretch across the width of the window and touch the edges. We fix this by adding another layout (sublayout) to add a little spacing to the situation.

	var sublayout = new Gtk.ButtonBox(Gtk.Orientation.HORIZONTAL); //Make sublayout with a horizontal layout.
	sublayout.set_layout(Gtk.ButtonBoxStyle.CENTER); //Push widgets to the center
	sublayout.add(mb); //Add the modebutton to the sublayout.
        
	layout.pack_start(sublayout,false,false, 0); //Then add the sublayout to the window layout.

Most of this code is self explanitory but pay close attention to layout.pack_start(sublayout,false,false,0);. The first and second parameters define the widget your adding in parameter one will stretch to the global window. In cases like toolbars and menu items you dont want the widget to stretch. In cases such as text areas and the like you want them to fill the entire window. The forth parameter is the pixel amount of padding you want between each of the box's children. Normally you dont have to pass in any parameters but the first.

Next we are going to load in the default current icon theme so we can display each image accordingly.


	//Initialize 2 pixbuf variables. These are going to hold our pixel buffer information. 
	Gdk.Pixbuf? icon1 = null;
    	Gdk.Pixbuf? icon2 = null;
        
        //Make sure the default theme is loaded.
        var icontheme = new Gtk.IconTheme().get_default();
        
        /*
         * We are going to catch any exceptions that are thrown.
         * If we don't the application will not run. I've found.
         */
        try {
            //Load the default icons dialog-ok and dialog cancel into our pixbuff variables.
            icon1 = icontheme.load_icon("dialog-ok",128, Gtk.IconLookupFlags.GENERIC_FALLBACK);
            icon2 = icontheme.load_icon("dialog-cancel",128, Gtk.IconLookupFlags.GENERIC_FALLBACK);
        } catch (Error e) {
            //Display warning in our terminal if an icon cannot be loaded.
            warning("Icon cannot be loaded");
        }
		
	//Initialize a new Gtk.Image using the pixbuf of one of the icons previously loaded.
        var imageHolder = new Gtk.Image.from_pixbuf(icon1);
        
        //Put the image inside the application layout object.
        layout.pack_start(imageHolder);

This section is kind of a doozy. But the jist of it includes

  1. Initializing Pixbuf variables.
  2. Make sure we load the default icon theme. get_default() only seems to work on initialization.
  3. Catch any exceptions while trying to load 'dialog-ok' and 'dialog-cancel' icons at 128 pixels.
  4. Load in the Pixbuf data into an Gtk.Image
  5. Then put our shiny new image in the application layout object.
But the good news is we are almost done! We just have to connect the signals to change the image when a different button is clicked. Here we go:

	mb.mode_changed.connect((obj, widget) => {
                warning(widget.name); //For debug purposes we print the name of the widget being passed in
                imageHolder.clear(); // Clear the current image holder
                
                //And next we check the widget name to see which icon we should assign to the place holder.
               if (widget.name == "ok") {
                    imageHolder.set_from_pixbuf(icon1);
                }     else {
                    imageHolder.set_from_pixbuf(icon2);
                }

     });

Place all this however you like inside the the application template (for simplicity I've done it in the constructor). You should have a working application that will toggle back and forth between the two icons.

Excercise

For an excercise remove the ModeButton and add a Gtk.Switch() instead.

comments powered by Disqus