040. How to have a login prompt before main program starts - Part 2?

Problem

You have set up a login prompt in Part 1. However, there are a number of "annoyances":

  1. Pressing Enter does not automatically go to next field, or activates the "OK" button.
  2. Suppose the user enters an invalid username or password, the login form gets destroyed, the alert box appears, and the login form re-appears agian. Ideally the login form should only disappear after the user has keyed in the correct username and password.

We will fix (1) in this article. (2) will be fixed in Part 3.


Solution

  • Use GtkWidget::key-press-event() to "intercept" the <Enter> key and make it behave the way we want it to be.
  • If user has not input username or password, we can use GtkWidget::grab_focus() to put the focus on the field that is still empty.
  • When user has input both username and password, we can use GtkButton::clicked() to simulate a button click on the "OK" button.

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   
27   
29   
30   
31   
32   
33   
34   
35   
36   
37   
38   
39   
40   
41   
42   
43   
44   
45   
46   
47   
49   
50   
51   
52   
53   
54   
55   
56   
58   
59   
61   
62   
63   
64   
65   
67   
68   
69   
76   
77   
78   
79   
80   
81   
82   
83   
84   
88   
89   
90   
91   
92   
93   
96   
98   
99   
101   
102   
103   
104   
105   
106   
107   
108   
109   
112   
113   
114   
115   
116   
117   
118   
119   
120   
121   
122   
123   
124   
125   
127   
128   
129   
130   
131   
132   
133   
134   
135   
136   
137   
<?php

$login_success = login(); // calls the login function
if (!$login_success) exit(0); // exit if login not successful

// starts the main program only if login successful
$window = new GtkWindow();
$window->set_size_request(400, 150);
$window->connect_simple('destroy', array('Gtk','main_quit'));
$window->add($vbox = new GtkVBox());

// display title
$title = new GtkLabel("Login prompt - Part 2");
$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("User verified"));
$vbox->pack_start(new GtkLabel("Main program starts..."));

// the login function
function login() {
    $count = 0;
    while ($count<3) {
        $data = get_data("Login", array("Username:", "Password:"));
        list($username, $password) = $data;
        if ($username=='user1' && $password=='phpgtk2') {
            return true;
        } else {
            alert("Incorrect username and password!\nHint: username=user1\npassword=phpgtk2");
        }
        ++$count;
    }
    return false;
}

// display a popup dialog box to prompt for data
function get_data($title, $field_labels) {
    $dialog = new GtkDialog($title, null, Gtk::DIALOG_MODAL);
    $dialog->set_position(Gtk::WIN_POS_CENTER_ALWAYS);
    $top_area = $dialog->vbox;
    $top_area->pack_start($hbox = new GtkHBox());
    $stock = GtkImage::new_from_stock(Gtk::STOCK_DIALOG_QUESTION, Gtk::ICON_SIZE_DIALOG);
    $hbox->pack_start($stock, 0, 0);

    $table = new GtkTable();
    $row = 0;
    $input = array();
    foreach ($field_labels as $field_label) {
        $label = new GtkLabel($field_label);
        $label->set_alignment(0,0);
        $table->attach($label, 0, 1, $row, $row+1);
        $input[$row] = new GtkEntry();
        $table->attach($input[$row], 1, 2, $row, $row+1);
        if (eregi("password", $field_label))
            $input[$row]->set_visibility(false);
        ++$row;
    }
    $hbox->pack_start($table);
    $dialog->add_button(Gtk::STOCK_OK, Gtk::RESPONSE_OK);
    $buttons = $dialog->action_area->get_children(); // note 1
    $button_ok = $buttons[0]; // note 1
    $dialog->connect('key-press-event', 'on_key', $input, $button_ok); // note 1
    $dialog->set_has_separator(false);
    $dialog->show_all();
    $dialog->run();

    $data = array();
    for ($i=0; $i<count($input); ++$i) {
        $data[] = $input[$i]->get_text();
    }
    $dialog->destroy();
    return $data;
}

// process key press
function on_key($widget, $event, $input, $button) {
    if ($event->keyval!=Gdk::KEY_Return) return false; 
// note 2
    $username = $input[0]->get_text(); 
// get username
    $passwd = $input[1]->get_text(); // get password
    if ($username=='') {
        $input[0]->grab_focus(); 
// note 3
        return true; 
// note 3
    }
    if ($passwd=='') {
        $input[1]->grab_focus(); 
// note 4
        return true; 
// note 4
    }
    if ($username!='' && $passwd!='') {
        $button->clicked(); 
// note 5
    }

}


function alert($msg) {
    $dialog = new GtkDialog('Alert', null, Gtk::DIALOG_MODAL);
    $dialog->set_position(Gtk::WIN_POS_CENTER_ALWAYS);
    $top_area = $dialog->vbox;
    $top_area->pack_start($hbox = new GtkHBox());
    $stock = GtkImage::new_from_stock(Gtk::STOCK_DIALOG_WARNING,
        Gtk::ICON_SIZE_DIALOG);
    $hbox->pack_start($stock, 0, 0);
    $hbox->pack_start(new GtkLabel($msg));
    $dialog->add_button(Gtk::STOCK_OK, Gtk::RESPONSE_OK);
    $dialog->set_has_separator(false);
    $dialog->show_all();
    $dialog->run();
    $dialog->destroy();
}

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

?>

Output

How to have a login prompt before main program starts - Part 2?

 

Explanation

  1. Set up the event handler to capture all key-press events. Note that we need to pass along the IDs of both the input fields and the OK button. The IDs of the input fields are stored in the array $input. The OK button is created using php-gtk's built-in function $dialog->add_button. Php-gtk does not directly tell us what's the ID of the button. However, we can "indirectly" get the ID of the button. First use $dialog->action_area->get_children() to get the list of buttons created by $dialog->add_button. We have only one OK button here. Hence $buttons[0] is our OK button.
  2. We only want to "intercept" the <Enter> key. So if the keypress is not <Enter>, we return a false, and php-gtk will take over and handle this signal with its default handler.
  3. If user has not input username, we use grab_focus() to prompt the user for input. Remember to use return true to tell php-gtk that we're done processing the <Enter> key.
  4. If user has not input password, we use grab_focus() to prompt the user for input. Remember to use return true to tell php-gtk that we're done processing the <Enter> key.
  5. When user has input both username and password, we simulate a click on the OK button with GtkButton::clicked().

The net effect of the above is that:

  1. When user enters username and press <Enter>, the cursor will automatically move to the password field.
  2. When user enters the password and press <Enter>, the OK button will automatically be clicked and enters validation check of username and password.
  3. If any of the field is still empty, the cursor will be placed on that field.

Note

We still have not fixed the other annoyance: "Suppose the user enters an invalid username or password, the login form gets destroyed, the alert box appears, and the login form re-appears agian. Ideally the login form should only disappear after the user has keyed in the correct username and password."

We will fix this in Part 3.

Related Articles

Add comment


Security code
Refresh