111. How to respond to key press event only when GtkTreeView in focus?

Problem

You would like to capture key press, say left and right arrow, only when the treeview is in focus, i.e. when the user's mouse is somewhere inside the treeview as shown below:

How to respond to key press event only when GtkTreeView in focus?


Solution

This might look simple. But when you tried it out, it's not that striaghtforward.

  • If you set up $treeview->connect('key-press-event', 'on_keypress'), you will find that you still receive key-press-events even when your cursor is outside treeview.
  • $treeview->is_focus() only works when the user clicks somewhere inside the treeview. If user clicks somewhere outside the treeview, and then move the mouse inside the treeview, $treeview->in_focus() will return a false.

The solution is to make use of the technique as explained in the article How to display tooltips in GtkTreeView - Part 2?


Sample Code

Note: If you have installed php-gtk2 using Gnope Installer on Windows, and if running the sample code below gives you warning that the Symbolic names for keys (e.g. Gdk::KEY_Return) is not defined, you might want to update your php-gtk2 with the latest php-gtk2.dll available here. Simply download the php-gtk2.dll and replace the copy in the folder php-gtk2xt. The latest compilation has put in the Symbolic names for keys listed here.

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   
63   
64   
65   
66   
67   
68   
69   
70   
71   
72   
73   
74   
75   
76   
77   
78   
79   
80   
81   
82   
83   
84   
85   
86   
87   
88   
89   
90   
91   
92   
93   
94   
95   
96   
97   
98   
99   
100   
103   
104   
105   
106   
107   
108   
112   
113   
114   
115   
116   
117   
118   
119   
120   
121   
122   
123   
124   
125   
126   
127   
128   
129   
130   
131   
132   
133   
134   
136   
139   
142   
144   
147   
148   
149   
150   
151   
152   
154   
155   
156   
157   
159   
162   
163   
164   
165   
166   
167   
168   
169   
170   
171   
172   
173   
174   
175   
176   
177   
178   
179   
180   
181   
182   
183   
184   
185   
186   
187   
188   
189   
190   
191   
192   
193   
194   
195   
196   
197   
198   
199   
<?php
$window = new GtkWindow();
$window->set_size_request(400, 300);
$window->connect_simple('destroy', array('Gtk','main_quit'));
$window->add($vbox = new GtkVBox());

// display title
$title = new GtkLabel("Capturing key-press-event in GtkTreeView\n");
$title->modify_font(new PangoFontDescription("Times New Roman Italic 10"));
$title->modify_fg(Gtk::STATE_NORMAL, GdkColor::parse("#0000ff"));
$title->set_size_request(-1, 40);
$vbox->pack_start($title, 0, 0);
$vbox->pack_start(new GtkLabel(
    'The following is active only when cursor is inside treeview'), 0, 0);
$vbox->pack_start(new GtkLabel('Return - display detail'), 0, 0);
$vbox->pack_start(new GtkLabel('Left Arrow - prev page'), 0, 0);
$vbox->pack_start(new GtkLabel('Right Arrow - next page'), 0, 0);
$vbox->pack_start(new GtkLabel('Alt Left Arrow - first page'), 0, 0);
$vbox->pack_start(new GtkLabel('Alt Right Arrow - last page'), 0, 0);
$vbox->pack_start(new GtkLabel(), 0, 0);

// the 2D table
$data = array(
array('row0', 'item 19', 2, 3.1),
array('row1', 'item 16', 20, 6.21),
array('row2', 'item 13', 8, 9.36),
array('row3', 'item 10', 11, 12.4),
array('row4', 'item 7', 5, 15.5),
array('row5', 'item 4', 17, 18.6),
array('row6', 'item 3', 20, 21.73));

$view_in_focus = 0; // note 1

display_table ($vbox, $data);

$window->show_all();
Gtk::main();

function display_table($vbox, $data) {

    // Set up a scroll window
    $scrolled_win = new GtkScrolledWindow();
    $scrolled_win->set_policy( Gtk::POLICY_AUTOMATIC,
        Gtk::POLICY_AUTOMATIC);
    $vbox->pack_start($scrolled_win);

    // Creates the list store
    if (defined("GObject::TYPE_STRING")) {
        $model = new GtkListStore(GObject::TYPE_STRING, GObject::TYPE_STRING,
                GObject::TYPE_LONG, GObject::TYPE_DOUBLE);
    } else {
        $model = new GtkListStore(Gtk::TYPE_STRING, Gtk::TYPE_STRING,
                    Gtk::TYPE_LONG, Gtk::TYPE_DOUBLE);
    }
    $field_header = array('Row #', 'Description', 'Qty', 'Price');
    $field_justification = array(0.0, 0.0, 0.5, 1.0);

    // Creates the view to display the list store
    $view = new GtkTreeView($model);
    $scrolled_win->add($view);

    // Creates the columns
    for ($col=0; $col<count($field_header); ++$col) {
        $cell_renderer = new GtkCellRendererText();
        $cell_renderer->set_property("xalign", $field_justification[$col]);
        $column = new GtkTreeViewColumn($field_header[$col],
            $cell_renderer, 'text', $col);
        $column->set_alignment($field_justification[$col]);
        $column->set_sort_column_id($col);

        // set the header font and color
        $label = new GtkLabel($field_header[$col]);
        $label->modify_font(new PangoFontDescription("Arial Bold"));
        $label->modify_fg(Gtk::STATE_NORMAL, GdkColor::parse("#0000FF"));
        $column->set_widget($label);
        $label->show();

        // setup self-defined function to display alternate row color
        $column->set_cell_data_func($cell_renderer, "format_col", $col);
        $view->append_column($column);
    }

    // pupulates the data
    for ($row=0; $row<count($data); ++$row) {
        $values = array();
        for ($col=0; $col<count($data[$row]); ++$col) {
            $values[] = $data[$row][$col];
        }
        $model->append($values);
    }

    // setup event handlers to let us know if mouse is in treeview
    $view->connect('motion-notify-event', 'on_motion');  // note 2
    $view->connect('leave-notify-event', 'on_leave'); // note 2

    // setup selection
    $selection = $view->get_selection();
    $selection->connect('changed', 'on_selection');

    $view->connect('key-press-event', 'on_key_press', $selection); // note 3
}

// set alternate row color
function format_col($column, $cell, $model, $iter, $col_num) {
    $path = $model->get_path($iter); // get the current path
    $row_num = $path[0]; // get the row number
    if ($col_num==3) {
        $amt = $model->get_value($iter, 3);
        $cell->set_property('text', '$'.number_format($amt,2));
    }
    $row_color = ($row_num%2==1) ? '#dddddd' : '#ffffff';
    $cell->set_property('cell-background', $row_color);
}

// process user selection
function on_selection($selection) {
    list($model, $iter) = $selection->get_selected();
    if ($iter==NULL) return;
    $desc = $model->get_value($iter, 1);
    $qty = $model->get_value($iter, 2);
    $price = $model->get_value($iter, 3);
    print "You have selected $desc: $qty ($price)\n";
}

function on_motion($view, $event) { // note 4
    global $view_in_focus;
    // check if mouse is in column headers
    $scroll_win_pos = $view->get_pointer(); // note 4
    if ($scroll_win_pos[1]<=25) { // in column header?
        $view_in_focus = 0;
    } else {
        $view_in_focus = 1;
    }
    return true;
}

function on_leave($view, $event) { // note 5
    global $view_in_focus;
    $view_in_focus = 0;
}

function on_key_press($widget, $event, $selection) {
    global $view_in_focus;
    if (!$view_in_focus) return; // note 6
    switch($event->keyval) {
        case Gdk::KEY_Return:
            list($model, $iter) = $selection->get_selected();
            if ($iter==NULL) {
                echo "no selection!\n";
            } else {
                echo "key press. Return: open detail\n";
            }
            return true;
            break;
        case Gdk::KEY_Left:
            if($event->state==0) {
                echo "key press: Left Arrow - prev page\n";
                return true;
            } elseif ($event->state & Gdk::MOD1_MASK){
                echo "key press. Alt Left Arrow: first page\n";
                return true;
            }
            return false;
            break;

        case Gdk::KEY_Right:
            if($event->state==0) {
                echo "key press: Right Arrow - next page\n";
                return true;
            } elseif ($event->state & Gdk::MOD1_MASK){
                echo "key press. Alt Right Arrow: last page\n";
                return true;
            }
            return false;
            break;

        default:
            return false;
    }
}

?>

Output

As shown above.
 

Explanation

The above sample code is based on How to display a 2D array in GtkTreeView - Part 5 - get user selection?

What's new here:

  1. A global variable to know if treeview is in focus.
  2. Check if treeview is in focus.
  3. Receive key-press-event.
  4. Check if mouse is in header region. Please refer to How to display tooltips in GtkTreeView - Part 2? for details.
  5. Treeview is now out of focus.
  6. Process key press only when treeview is in focus.

Related Links

Add comment


Security code
Refresh