053. How to let user enter date with a popup calendar - Part 2?

Problem

In Part 1, you allow users to enter date through a popup calendar. However, to select a date requires two mouse clicks.

You would like to simplify date selection by using only one mouse click - that is, the user just need to click on the desired date, and that's it. No more ok button as shown below:

How to let user enter date with a popup calendar - Part 2?


Solution

  • Use key-press-event to know when user pressed the F1 key.
  • When the F1 key is pressed, create a popup GtkDialog that contains a GtkCalendar.
  • Use get_date to retrieve the date selected by the user.
  • Use the signal day-selected to know when a day is selected.
  • Use the signal month-changed to know when user changes a month. Note that changing the year will also cause this signal to be emitted.

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   
20   
21   
22   
34   
35   
36   
37   
39   
41   
42   
43   
44   
45   
47   
48   
51   
52   
53   
54   
55   
56   
57   
58   
59   
60   
61   
62   
66   
68   
72   
73   
74   
77   
78   
79   
81   
82   
90   
91   
92   
93   
94   
95   
96   
102   
103   
105   
109   
111   
112   
114   
116   
117   
118   
119   
120   
122   
123   
124   
126   
127   
128   
129   
130   
131   
132   
133   
134   
<?php
$window = new GtkWindow();
$window->set_size_request(400, 150);
$window->set_position(Gtk::WIN_POS_CENTER_ALWAYS);
$window->connect_simple('destroy', array('Gtk','main_quit'));
$window->add($vbox = new GtkVBox());

// display title
$title = new GtkLabel("Press F1 to choose a date");
$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);

$hbox = new GtkHBox();
$vbox->pack_start($hbox, 0, 0);
$hbox->pack_start(new GtkLabel("Select Date: "), 0, 0);
$hbox->pack_start($entry = new GtkEntry(), 0, 0);

// let us know when user press F1 key
$entry->connect('key-press-event', 'on_keypress', $entry); 

function on_keypress($widget, $event, $entry) {
    // we only want F1. return all the rest
    if ($event->keyval!=Gdk::KEY_F1) return false; 
    $selected_date = get_date();
    $entry->set_text($selected_date);
    $entry->grab_focus();
}

function get_date() {
    $getdate_dialog = new GetDate();
    $date = $getdate_dialog->calendar->get_date();
    $selected_date = 1+$date[1].'/'.$date[2].'/'.$date[0];
    return $selected_date;
}

class GetDate{

    var $calendar;

    function GetDate() {
        $dialog = new GtkDialog('Get Date', null, Gtk::DIALOG_MODAL);
        $dialog->set_position(Gtk::WIN_POS_CENTER_ALWAYS);
        $top_area = $dialog->vbox;
        $top_area->pack_start($hbox = new GtkHBox());
        setlocale(LC_ALL, 'english');
        $this->calendar = new GtkCalendar();
        $top_area->pack_start($this->calendar, 0, 0);

        // listen to the following 3 signals note 1
        $this->calendar->connect('day-selected', array(&$this, 'on_select'), 'day-selected');
        $this->calendar->connect('month-changed', array(&$this, 'on_select'), 'month-changed');
        $dialog->connect('button-press-event', array(&$this, 'on_button_press'));
        

        $this->dialog = $dialog;
        $dialog->set_has_separator(false);
        $dialog->show_all();
        $dialog->run();
        $dialog->destroy();
    }

    // user selects a date or change a month
    function on_select($calendar, $signal_name) {
        if ($signal_name=='month-changed') {
            $this->month_changed = 1; 
// flag to indicate user changes mth note 2
            return false;
        }
        $this->month_changed = 0;
        return false;
    }


    // note 3
    function on_button_press($widget, $event) { 
        if (!$this->month_changed) { 
            $this->dialog->destroy(); 
        }
        $this->month_changed = 0; 
    }

}

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

?>

Output

As shown above.

 

Explanation

  1. Please take note that when the user change a month, in addition to the signal month-changed, the signal day-selected is also emitted.
  2. Because of the above, I used a flag $this->month_changed to let us know if it's a "real" date selection, as opposed to month change.
  3. If you do some testing, you will find that the two signals generated when user changes a month is so fast that it's so hard to differentiate a month change from a date selection. To resolve this, we handle the mouse click one more time with the signal on_button_press. This might seem relundant, but it allows us to process according to what we want by testing the flag $this->month_changed. If it's a month change, the flag will be 1 and we do nothing. If it's a 0, it means it's a date selection, and we proceed to close the popup dialog.

Add comment


Security code
Refresh