The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

Win32::GUI Tutorial - Part 5 - More than one window

A simple multi-window program

Before going further into control details, we'll explore the case of applications that make use of more than one window.

In principle, Win32::GUI does not limit the number of windows a program can have: we've seen that the usual procedure for a program is:

  1. create the main window, eg:

            $Window = new Win32::GUI::Window(...);
  2. create controls, eg:

            $Window->Add...(...);
  3. show the window, eg:

            $Window->Show();
  4. call the dialog phase, eg:

            Win32::GUI::Dialog();

Steps 1, 2 and 3 can be repeated how many times you like, to create several independent windows:

        use Win32::GUI;
        
        $W1 = new Win32::GUI::Window(
                -name  => "W1",
                -title => "First Window",
                -pos   => [ 100, 100 ],
                -size  => [ 300, 200 ],
        );
        $W2 = new Win32::GUI::Window(
                -name  => "W2",
                -title => "Second Window",
                -pos   => [ 150, 150 ],
                -size  => [ 300, 200 ],
        );

        $W1->Show();
        $W2->Show();
        
        Win32::GUI::Dialog();

the two windows we've created are displayed on the screen and are both able to intercept events and user interaction, with the rather obvious limitation that while the code for a window's event is executing, the other one is frozen; whis is due to the single-threaded nature of Perl, although work is being done for a full featured multithreaded Perl).



One thing to note is that even if the windows are two, they belong to the same process and share a single message loop, so when you exit from one window (or when the program terminates for whatever reason), they both disappear:

        sub W1_Terminate { return -1; }

Main and popup windows

A much more common case is to have a program using a main window, initially shown, and one or more popup windows (generally DialogBoxes) that are shown in response to a precise function; to make an example, you can imagine the Windows Explorer as your main window and the File Properties dialog as a popup window.

We'll modify our program so that the second window appears when the user clicks the button on the first window:

        use Win32::GUI;
        
        $W1 = new Win32::GUI::Window(
                -name  => "W1",
                -title => "Main Window",
                -pos   => [ 100, 100 ],
                -size  => [ 300, 200 ],
        );
        $W1->AddButton(
                -name => "Button1",
                -text => "Open popup window",
                -pos  => [ 10, 10 ],
        );
        
        $W2 = new Win32::GUI::Window(
                -name  => "W2",
                -title => "Popup Window",
                -pos   => [ 150, 150 ],
                -size  => [ 300, 200 ],
        );

        $W1->Show();
        
        Win32::GUI::Dialog();

        sub Button1_Click { $W2->Show(); }
        
        sub W1_Terminate { return -1; }

Furthermore, we put a button on the second window to make it go away:

        $W2->AddButton(
                -name => "Button2",
                -text => "Close this window",
                -pos  => [ 10, 10 ],
        );
        
        sub Button2_Click { $W2->Hide(); }


This rather basic example shows the skeleton framework for a typical multi-window application; but there are still two issues we want to address: the mortality of popup windows and their "modal" behaviour.

Keeping popup windows alive

If you choose to close the second window with the Close (little X) button on the upper right corner, you'll notice that the window disappear, and that it does not show anymore when you press the button on the main window!

What's happening here is that we requested a W2_Terminate action, and since we didn't provide an event for this, Win32::GUI proceeds with the default behaviour for the close button, which is to destroy the window. The result is that you can't show $W2 anymore, because it does not exists anymore as a valid window (although it still exists as a Perl object). To let our window survive the close action, we need to provide a customary Terminate event:

        sub W2_Terminate {
                $W2->Hide();
                return 0;
        }

The return 0 is very important in this case, because it tells Windows to avoid the destruction of our window (it is just hidden), so that we can show it again using the button in $W1.

Simulating "modal" windows

Here is another very common functionality you may want to implement: when a popup window is open, you don't want the user to interact with the main window; this is known as using a "modal" window, a window that gets all your attention and does not let you go forward with the program unless you have disposed of it.

Win32::GUI does not have direct support for modal windows, but you can simulate the behaviour with this trick:

        sub W1_Activate {
                if($W2->IsVisible) {
                        $W2->SetForegroundWindow();
                        return 0;
                }
                return 1;
        }

The meaning should be obvious enough: if the popup window is shown, trying to activate the main window automatically reports you to the popup window; otherwise, the main window becomes the active one as normal. Of course, if you have more than one popup window, you should add code for everyone of them:

        sub W1_Activate {
                if($W2->IsVisible) { $W2->SetForegroundWindow(); return 0; }
                if($W3->IsVisible) { $W3->SetForegroundWindow(); return 0; }
                # ... and so on
                return 1;
        }