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

NAME

Win32::GUI::Tutorial::Part5 - More than one Window

Win32::GUI Tutorial - Part 5

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 all the windows disappear.

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, and exit the windows message loop. 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.

"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.

So, the behaviour of a modal window is to diable interaction with the window used to launch it (know as its parent window). To achieve this we first have to tell the window who its parent is. We do this when we create W2, using the -parent option:

        $W2 = new Win32::GUI::Window(
                ...
                -parent => $W1,
        );

Now, when we want to display the W2 we use the DoModal() method: this disables W2's parent window (W1), and starts a new message loop to process events for W2. When we are done with W2, we return -1 from an event handler, which causes DoModal() to return, but in this case does not cause any of the windows to be destroyed. Here's the full code:

        use Win32::GUI();

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

        $W1->AddButton(
                -name => "Button1",
                -text => "Open popup window",
                -pos  => [ 10, 10 ],
        );

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

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

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

        sub W1_Terminate {
                return -1;
        }

        sub Button1_Click {
                $W2->DoModal();
                return 0;
        }

        sub W2_Terminate {
                return -1;
        }

        sub Button2_Click {
                return -1;
        }

__W32G_POSTAMBLE__