Here is an example using GtkEventBox and
GtkStyle to create standard Buttons.
See the complete story
and source code on my own site.
2011-12-10, by fgm: This code was hosted on the currently unavailable phpclasses.free.fr. Since it was declared as open source on that site (page bottom: "Content is available under GNU Free Documentation License 1.2"), I went ahead and restored the article from Google cache. Note that the original license still applies.
GtkWidget standards buttons are built and can be drawn. Here we use ready to use methods from GtkStyle class to draw buttons, checkboxes and handles. You can use theses classes to override standard buttons features. It's also useful to understand how GtkWidget works. This class study started when I was trying to display a handle and did not found any one.
<?php
error_reporting(E_ALL);
/**
* author : Marc Quinton / march 2007.
*
* simulate button drawing with GtkStyle::paint_*() method.
* - take care of events (button, enter, leave)
*
* The main purpose of this script is to demonstrate use of GtkStyle pain method with state_type, shadow_type params
* and to have the ability to override buttons features.
* class Tree:
*
* GtkEventBox
* GadgetBox
* ButtonGadget (contains GtkLabel)
* ToggleButtonGadget
* CheckButtonGadget
* HandleGadget
*
* Button (contains ButtonGadget)
* ToggleButton (contains ToggleButtonGadget)
* CheckButton (contains GtkHbox(CheckButtonGadget,GtkLabel))
* HandleBox (contains HandleGadget)
*
* limitations :
* - need to implement signals ; not very difficult
* - there is a probleme displaying Gadget widget in standard Gtk composite widget, so we have created composite classes
*
*/
abstract class GadgetBox extends GtkEventBox{
protected $_state;
protected $_style;
public function __construct(){
parent::__construct();
$this->add_events(Gdk::EXPOSURE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::BUTTON_PRESS_MASK);
$this->connect('expose-event', array($this, 'expose_event'));
$this->connect('leave-notify-event', array($this, 'leave_notify_event'));
$this->connect('enter-notify-event', array($this, 'enter_notify_event'));
$this->connect('button-press-event', array($this, 'button_press_event'));
$this->connect('button-release-event', array($this, 'button_release_event'));
$this->_state = Gtk::STATE_NORMAL;
$this->_style = Gtk::SHADOW_OUT;
}
public function expose_event($self, $event){
$this->draw();
if($child = $this->get_child() != null)
$this->propagate_expose($this->get_child(), $event);
return true; # returning true to tell we have done expose ourself ;
}
public function enter_notify_event($self, $event){
$this->_state = Gtk::STATE_PRELIGHT;
$this->redraw();
}
public function leave_notify_event($self, $event){
$this->_state = Gtk::STATE_NORMAL;
$this->redraw();
}
public function button_press_event($self, $event){
$this->_state = Gtk::STATE_ACTIVE;
$this->redraw();
}
public function button_release_event($self, $event){
$this->_state = Gtk::STATE_PRELIGHT;
$this->redraw();
}
protected function redraw(){
$alloc = $this->allocation;
$this->queue_draw();
}
public function draw(){
}
public function draw_border($x, $y, $w, $h){
$style = $this->get_style();
$style->paint_box($this->window, $this->_state, $this->_style, null, $this, "test", $x, $y, $w, $h);
}
public function draw_string($x, $y, $string){
$style = $this->get_style();
# void paint_string(GdkWindow window, state_type, GdkRectangle area, GtkWidget widget, detail, x, y, string);
$style->paint_string($this->window, Gtk::STATE_NORMAL, null, $this, "button", $x, $y, $string);
}
}
class ButtonGadget extends GadgetBox{
protected $_name;
protected $label;
public function __construct($name = , $font = null){
parent::__construct();
$this->_name = $name;
# $this->set_border_width(6);
# $this->set_size_request(20,20);
if($name != null)
$this->add($this->label = new GtkLabel($name));
if($font != null)
$this->label->modify_font(new PangoFontDescription($font));
}
public function button_press_event($self, $event){
$this->_state = Gtk::STATE_ACTIVE;
$this->_style = Gtk::SHADOW_IN;
$this->redraw();
}
public function button_release_event($self, $event){
$this->_state = Gtk::STATE_PRELIGHT;
$this->_style = Gtk::SHADOW_OUT;
$this->redraw();
}
public function draw(){
$alloc = $this->allocation;
$border = $this->get_border_width();
$x = $alloc->x;
$y = $alloc->y;
$w = $alloc->width - 2*$border;
$h = $alloc->height - 2* $border;
$style = $this->get_style();
$style->paint_box($this->window, $this->_state, $this->_style, null, $this, "test", $x, $y, $w, $h);
return true; # returning true is : yes I have done all expose event for childs
}
}
class ToggleButtonGadget extends ButtonGadget{
protected $toggled;
protected $count;
public function __construct($name = , $font = null){
parent::__construct($name, $font);
$this->toggled = false; # off
$this->count = 1;
}
public function button_press_event($self, $event){
$this->count++;
if($this->count%2 == 0){
$this->_state = Gtk::STATE_ACTIVE;
$this->_style = Gtk::SHADOW_IN;
$this->toggled = true;
}
$this->redraw();
}
public function button_release_event($self, $event){
if($this->count%2 == 1){
$this->_style = Gtk::SHADOW_OUT;
$this->_state = Gtk::STATE_PRELIGHT;
$this->toggled = false;
}
$this->redraw();
}
public function enter_notify_event($self, $event){
if($this->toggled)
$this->_state = Gtk::STATE_ACTIVE;
else
$this->_state = Gtk::STATE_PRELIGHT;
$this->redraw();
}
public function leave_notify_event($self, $event){
if($this->toggled)
$this->_state = Gtk::STATE_ACTIVE;
else
$this->_state = Gtk::STATE_NORMAL;
$this->redraw();
}
}
class CheckButtonGadget extends ToggleButtonGadget{
protected $size;
protected $border;
public function __construct(){
parent::__construct(null);
$this->size = 15;
$this->border = 3;
}
public function draw(){
$alloc = $this->allocation;
$border = $this->get_border_width();
$x = 0;
$y = intval(($alloc->height - $this->size) / 2);
$w = $this->size;
$h = $this->size;
$style = $this->get_style();
# void paint_check(GdkWindow window, state_type, shadow_type, GdkRectangle area, GtkWidget widget, detail, x, y, width, height);
$style->paint_check($this->window, $this->_state, $this->_style, null, $this, "test", $x, $y, $w, $h);
return true;
}
}
class HandleGadget extends GadgetBox{
protected $size;
protected $orientation;
public function __construct($orientation = Gtk::ORIENTATION_HORIZONTAL){
parent::__construct();
$this->orientation = $orientation;
$this->setup();
}
protected function setup(){
$this->size = array(
'w' => 40,
'h' => 8
);
if($this->orientation == Gtk::ORIENTATION_HORIZONTAL)
$this->set_size_request($this->size['w'], $this->size['h']);
else
$this->set_size_request($this->size['h'], $this->size['w']);
}
public function draw(){
$alloc = $this->allocation;
$border = $this->get_border_width();
if($this->orientation == Gtk::ORIENTATION_HORIZONTAL){
$x = $alloc->x + intval(($alloc->width-$this->size['w']) / 2);
$y = $alloc->y;
$w = $this->size['w'];
$h = $this->size['h'];
} else{
$y = $alloc->y + intval(($alloc->height-$this->size['w']) / 2);
$x = $alloc->x;
$w = $this->size['h'];
$h = $this->size['w'];
}
$style = $this->get_style();
# void paint_check(GdkWindow window, state_type, shadow_type, GdkRectangle area, GtkWidget widget, detail, x, y, width, height);
$style->paint_handle($this->window, $this->_state, $this->_style, null, $this, "handle",
$x, $y, $w, $h, $this->orientation);
return true;
}
}
# ------------------------------------------------------------------------------------------------------------
# bellow, it's composition of GtkEventBox with Gadget to insure correct
# management in composite widget (GtkVbox for example)
# without this composition, gadget are not drawn properly ; probably a clip problem with GtkStyle::paint*() methods.
#
#
class Button extends GtkEventBox{
protected $button;
public function __construct($name = , $font = null){
parent::__construct();
$this->button = new ButtonGadget($name, $font);
$this->add($this->button);
}
}
class ToggleButton extends GtkEventBox{
protected $button;
public function __construct($name = , $font = null){
parent::__construct();
$this->button = new ToggleButtonGadget($name, $font);
$this->add($this->button);
}
}
class CheckButton extends GtkEventBox{
protected $hbox;
protected $check;
protected $label;
public function __construct($name = , $font = null){
parent::__construct();
$this->add($this->hbox = new GtkHbox());
$this->hbox->pack_start($this->check = new CheckButtonGadget(), false, false);
$this->hbox->pack_end($this->label = new GtkLabel($name), true, true);
$this->check->set_size_request(15,15);
$this->label->set_alignment(0.02, 0.5);
$this->show_all();
}
}
class HandleBox extends GtkEventBox{
protected $button;
public function __construct($orientation = Gtk::ORIENTATION_HORIZONTAL){
parent::__construct();
$this->button = new HandleGadget($orientation);
$this->add($this->button);
}
}
$window = new GtkWindow();
$window->connect_simple('destroy', array('Gtk','main_quit'));
$window->set_size_request( 200, 200 ); # set window size
$window->set_position(Gtk::WIN_POS_CENTER); # place window to screen center
$window->set_title("GtkStyle::paint_box test");
$window->add($hbox1 = new GtkHbox());
$hbox1->pack_start($vbox = new GtkVbox());
$vbox->pack_start(new HandleBox(Gtk::ORIENTATION_HORIZONTAL), false, false);
$vbox->pack_start($l = new GtkLabel('Horizontal HandleBox')); $l->set_angle(90);
$hbox1->pack_end($vbox = new GtkVbox());
$vbox->pack_start($hbox2 = new GtkHbox());
$hbox2->pack_start(new HandleBox(Gtk::ORIENTATION_VERTICAL), false, false);
$hbox2->pack_start(new GtkLabel('Vertical HandleBox'));
$vbox->pack_start(new Button('button'));
$vbox->pack_start(new CheckButton('check button'));
$vbox->pack_start(new ToggleButton('toggle button'));
$window->show_all();
Gtk::main();
?>