PHP-GTK2 Newsletter

PHP-GTK2 Tips & Techniques
FREE Newsletter
by kksou



Sample Code 381: How to create a round button - Part 3?
Written by kksou   
Wednesday, 28 November 2007
Problem

This is in response to the comments of Laurent from France with regards to creating round buttons with rollover effect.

We have created a round button with rollover effect in How to create a round button - Part 2?.

Laurent commented that the rollover effect can be achieved more easily with the use of GtkWidget::set_state().

This article shows you what the code will look like using GtkWidget::set_state() as suggested by Laurent as shown below. Do take a look at the code. You will find the code is indeed cleaner and more elegant. Also, you'll find this method GtkWidget::set_state() very useful in some of your applications.

How to create a round button - Part 3?


Solution
  • When setting the pixmap of the button with GtkStyle::bg_pixmap, we can define different pixmap for the different states: NORMAL, PRELIGHT and ACTIVE.
    • NORMAL: when the button is in the normal, unclicked state
    • PRELIGHT: when you move your mouse over the button
    • ACTIVE: when you have clicked the button
  • Once you have defined the pixmap for the different states, when you set the state of the button with GtkWidget::set_state(), the corresponding pixmap will be displayed automatically!

Sample Code

Note: The following image files are required by the sample code below. Please save a copy of the image files and put them in the same directory where you store the sample code.

 button_rew1.png
 button_rew2.png
 button_play1.png
 button_play2.png
 button_ff1.png
 button_ff2.png

1   
2   
3   
4   
5   
6   
7   
8   
9   
10   
11   
12   
13   
14   
15   
16   
17   
18   
19   
20   
21   
22   
23   
24   
25   
26   
27   
28   
29   
30   
31   
32   
33   
34   
35   
36   
37   
38   
39   
40   
41   
42   
43   
44   
45   
46   
47   
48   
49   
50   
51   
52   
53   
54   
55   
56   
57   
58   
59   
60   
61   
62   
65   
<?php
$window = new GtkWindow();
$window->set_size_request(400, 200);
$window->connect_simple('destroy', array('Gtk','main_quit'));
$vbox = new GtkVBox();
$window->add($vbox);

// create a hbox to hold the buttons
$vbox->pack_start($hbox = new GtkHBox());

$buttons2 = new RoundButtons($hbox, array(
    array('button_rew1.png', 'button_rew2.png'),
    array('button_play1.png', 'button_play2.png'),
    array('button_ff1.png', 'button_ff2.png')));

class RoundButtons {

    function RoundButtons($container, $buttons) {
        $button_id = 0;
        $this->button_count = count($buttons);
        foreach($buttons as $button_img) {
            $this->create_round_button($container,
                $button_id, $button_img[0], $button_img[1]);
            $this->button_state[$button_id] = 0;
            ++$button_id;
        }
    }

    function create_round_button($container, $button_id, $image0, $image1) {
        $pixbuf[0]=GdkPixbuf::new_from_file($image0);
        list($this->pixmap[$button_id][0],
            $this->mask[$button_id][0]) =
                $pixbuf[0]-> render_pixmap_and_mask(255);

        $pixbuf[1]=GdkPixbuf::new_from_file($image1);
        list($this->pixmap[$button_id][1],
            $this->mask[$button_id][1]) =
                $pixbuf[1]-> render_pixmap_and_mask(255);

        $eventbox = new GtkEventBox();
        $this->eventbox[$button_id] = $eventbox;
        $eventbox->set_size_request(100, -1);
        $container->pack_start($eventbox, 0, 0);

        $style = $eventbox->get_style();
        $style=$style->copy();

        $style->bg_pixmap[Gtk::STATE_NORMAL] = 
            $this->pixmap[$button_id][0]; // note 1

        $style->bg_pixmap[Gtk::STATE_PRELIGHT] =  
            $this->pixmap[$button_id][1]; // note 2

        $style->bg_pixmap[Gtk::STATE_ACTIVE] = 
            $this->pixmap[$button_id][1]; // note 3
        $eventbox->set_style($style);

        $eventbox->shape_combine_mask($this->mask[$button_id][0], 0, 0);


        $eventbox->connect('button-press-event',
            array(&$this, 'on_button_press'), $button_id);
        $eventbox->connect('leave-notify-event',
  • Note that this is only 70% of the sample code. You have to be a registered member to see the entire sample code. Please login or register.
  • Registration is free and immediate.
  • Have some doubt about the registration? Please read this forum article.
Explanation
  1. Define the pixmap of the button for the NORMAL state.
  2. Define the pixmap of the button for the PRELIGHT state.
  3. Define the pixmap of the button for the ACTIVE state.
  4. When the user clicks a button, we set the state of the clicked button to ACTIVE and the unclicked buttons to NORMAL. Note that the corresponding pixmap will be updated automatically.
  5. When the mouse is over a button, we set the state of the corresponding button to PRELIGHT and the others to NORMAL. When the mouse leaves a button, we set all buttons to state NORMAL. Note that the corresponding pixmap will be updated automatically.

Related Links

User reviews   Average user ratings:    5.0   (from 5 users)
  1. Kevin
    February 20, 2008 1:51pm

    Great example kksou (my first foray into PHP-GTK2), however I've searched far and wide for a way to implement the 'shape_combine_mask' method directly on a widget such as a button, in order to use the native button methods instead of reinventing the wheel. Several places I see people suggesting that this can be done or has been done, but I cannot find a live (working) example anywhere for PHP-GTK2.

    When I try I get this error--whether or not I have realized the widget or parent window, or not...

    (php.exe:4340): Gtk-CRITICAL **: gtk_widget_shape_combine_mask: assertion `!GTK_
    WIDGET_NO_WINDOW (widget)' failed

    Am I missing something here, or is this simply not supported in PHP-GTK2 (yet)?

    Thanks for any help

  2. kksou
    February 20, 2008 7:55pm

    Hi Kevin,

    If you look at the error message closely, it says 'GTK_WIDGET_NO_WINDOW'.

    True enough, if you refer to this: http://www.gtkmm.org/docs/gtkmm-2.4/docs/tutorial/html/chapter-widgets-without-xwindows.html,
    you will see that GtkButton is listed as one of the widgets with no associated GdkWindow!

    I know it sounds strange. Because a GtkButton does receive events. And you can also set the background color of a GtkButton.

    However, if you think about it. Suppose you add an image with GtkButton::set_image(). You will find that the internals of a GtkButton changed from a plain GtkLabel to a GtkAlignment, followed by a GtkHBox, followed by a GtkImage+GtkLabel. If they allow you to change the shape of the button with shape_combine_mask(), then what should they do with the label? It will be quite messy, isn't it?

    I guess this is one of the reasons why they set the GTK_NO_WINDOW flag of GtkButton to 1, even though it seems to have an associated GdkWindow.

    So I think at least for now, if you need a button with different shapes, you'll have to implement this manually with GtkEventBox.

    Regards,
    /kksou

  3. Kevin
    February 21, 2008 9:56am

    Thanks again /kksou,

    It had me confused because it does have a GdkWindow property and is a child of GtkBin (along with GtkEventBox, GtkWindow, ...), but I see your point and it makes sense even if it is disappointing.

    What gets me is that I found several references of people saying they have done it or it can be done. Admittedly the people saying they had done it may have been using Gtk, or Gtk2 in Perl or C, but one reference was definitely in Php-Gtk2 (!). I guess they didn't know what they were talking about since I've spent a very long time trying every angle on this and I still get the same message.

    Thanks again

  4. anton
    March 03, 2008 6:50pm

  5. Debapriya Rahut
    September 06, 2008 1:21pm

Note: You have to be a registered member to leave a comment. Free registration here.

 
< Prev   Next >

Blog - Forum - Privacy Policy - Contact Us
Copyright © 2006-2008. kksou.com. All Rights Reserved