Daniel Fernandez wrote:
Hi,
Sorry by my last mail. I sent you a wrong script. This it's my modified
MDI script with your code. The problem is that the new child window is
not visualized but the activate/deactivate events works when the window
is created.
Is there are any wrong in my code?
I'll appreciate your comments about this.
Best regards.
I think this is close to what you want (this is the first time I've
played with the MDI classes, so there may be better ways). The tricks I
used were:
(1) The class needs to use the MDIFrame message loop, so I added
-widget => "MDIFrame" to the class definition.
(2) MDIFrame sets WM_CLIPCHILDREN by default, but as the MDIClient
window fill the MDIFrame window, nothing is ever drawn; so I removed
the WM_CLIPCHILDREN from the MDIFrame with
-popsyle => WM_CLIPCHILDREN
(3) Now that we've removed the WM_CLIPCHILDREN from the MDIFrame, when
we draw on the whole of the MDIFrame's client area, we overwrite the
MDIChild windows, causing very nasty flickering, so I extended the
paint() function to clip the MDIChild windows from the MDIFrame's DC
that we are drawing into.
(4) Finally the MDIClient does not invalidate the regions exposed by an
MDIChild when it moves or is terminated, as it assumes that we want it
to paint the background, so I invalidate the regions in explicit
handlers for termination and moving (See NewChild(), -onTerminate
handler for the window and Hook(WM_MOVE ... ).
Really we want to do exactly what I did with a single window in the
MDIClient's WM_PAINT handler. But I can't see any way to get at that
with the current Win32::GUI codebase.
Regards,
Rob.
#!perl -w
#
# MDI sample
#
use strict;
use warnings;
use Win32::GUI;
use Win32::GUI::BitmapInline();
# My child counter for unique name.
my $ChildCount = 0;
my $Window;
# Load our bitmap
#my $bm = Win32::GUI::Bitmap->new("sail.bmp") or die "Creating Bitmap";
my $bm = get_bitmap();
# Store the width and height, as we'll use them a lot
my($bmw, $bmh) = ($bm->Info())[0..1];
# create a class without a background brush: this prevents
# defFrameWindowProc erasing the window background, as we want to
# paint it ourselves to avoid flicker
my $class = Win32::GUI::Class->new(
-name => 'noflicker',
-brush => 0,
-widget => "MDIFrame", # use the MDIFrame windowproc
);
# Create Main menu.
my $Menu = Win32::GUI::MakeMenu(
"&File" => "File",
" > &New" => { -name => "File_New", -onClick => \&NewChild },
" > -" => 0,
" > E&xit" => { -name => "File_Exit", -onClick => sub { -1; } },
"&Window" => "Window",
" > &Next" => { -name => "Next", -onClick => sub {
$Window->{Client}->Next; } },
" > &Previous"=> { -name => "Prev", -onClick => sub {
$Window->{Client}->Previous; } },
" > -" => 0,
" > &Cascade" => { -name => "Cascade", -onClick => sub {
$Window->{Client}->Cascade(); 0; } },
" > Tile &Horizontally" => { -name => "TileH", -onClick => sub
{ $Window->{Client}->Tile(1); } },
" > Tile &Vertically" => { -name => "TileV", -onClick => sub
{ $Window->{Client}->Tile(0); } },
"&Help" => "Help",
" > &About" => { -name => "About", -onClick => sub { 1; } },
);
# First we create an MDIFrame window.
$Window = new Win32::GUI::MDIFrame (
-title => "Win32::GUI MDI Sample",
-left => 100,
-top => 100,
-width => 280,
-height => 280,
-name => "Window",
-menu => $Menu,
-class => $class,
-popstyle => WS_CLIPCHILDREN, # So that the MDIClient's window is
not clipped
-onPaint => \&paint,
-onTerminate => sub {print "Terminate\n"; -1}, ) or die "Window";
# We add an MDIClient window, This window manage Child Window.
$Window->AddMDIClient(
-name => "Client",
-firstchild => 100, # Define Child ID for
menu item
-windowmenu => $Menu->{Window}->{-handle}, # Define Menu Handle
where Add Window Child name
) or die "Client";
# Create a memory DC, compatible with our window's
# DC, containing our bitmap. Do this once, here, to speed
# up the painting routine. Use a local block, so that $dc
# goes out of scope, and $dc gets released (could call
# $dc->ReleaseDC() instead).
my $memDC;
{
my $dc = $Window->GetDC();
$memDC = $dc->CreateCompatibleDC();
$memDC->SelectObject($bm);
}
# We need a brush to paint the window background
# with, select a grey one. We don't need to worry
# about freeing stock objects when we're done with them
my $bkBrush = Win32::GUI::GetStockObject(1);
# Show main window and go to event loop
$Window->Show;
Win32::GUI::Dialog();
# We've got some closures on $Window in the
# functions below, which appear to be the cause of
# a 'Can't call Delete .. in global clean up' warning,
# udefining $Window here stops that.
undef $Window;
exit(0);
# Our window painting routine. To avoid flicker we will
# paint the whole of the window, taking care not to draw any
# pixel more than once.
sub paint
{
print "Painting...\n";
my($window, $dc) = @_;
# I will add StretchBlt to the next release so that we can stretch the
# image to fit the window, but it's not there right now.
#$dc->StretchBlt(0, 0, ($window->GetClientRect())[2..3], $memDC, 0,
0, $bmw, $bmh);
# clip each of the MDIChild windows, to avoid us drawing over them
# and so removing flicker
# $clip_rgn is the rgion into which we can draw, initialise
# it to the client window
my $clip_rgn =
Win32::GUI::Region->CreateRectRgn($window->GetClientRect());
while (my ($name, $value) = each %{$window->{Client}}) {
next unless $name =~ /^Child\d+/;
my($l, $t, $r, $b) = $value->GetWindowRect();
# $child_rgn is the MDI child's region in our window's client
# co-ordinates
my $child_rgn = Win32::GUI::Region->CreateRectRgn(
$Window->ScreenToClient($l, $t), $Window->ScreenToClient($r,
$b));
# remove $child_rgn from the area we can draw
$clip_rgn->CombineRgn($clip_rgn, $child_rgn, 4);
}
# selct the clip region into our DC.
$dc->SelectClipRgn($clip_rgn) if $clip_rgn;
#calculate the image position to center it in the window
my ($ww, $wh) = ($window->GetClientRect())[2..3];
my $l = ($ww - $bmw)/2;
my $t = ($wh - $bmh)/2;
my $r = $l + $bmw;
my $b = $t + $bmh;
# copy the image from the memory DC to the window's DC
$dc->BitBlt($l, $t, $bmw, $bmh, $memDC, 0, 0);
# fill the spaces around the image with our background brush.
# We should probably not draw when it is not necessary (i.e. when
# the image meets the side(s), but we can get away with not checking,
# as the DC is always clipped to the window's client rect.
$dc->FillRect(0, 0, $ww, $t, $bkBrush);
$dc->FillRect(0, $b, $ww, $wh, $bkBrush);
$dc->FillRect(0, $t, $l, $b, $bkBrush);
$dc->FillRect($r, $t, $ww, $b, $bkBrush);
# We've drawn the background, so inform windows that there is
nothing left
# to draw.
$dc->Validate();
# we've processed the message, so return 0.
return 0;
}
# This function create a new child window.
sub NewChild {
# Create a child window.
my $Child = $Window->{Client}->AddMDIChild (
-name => "Child".$ChildCount++,
-onActivate => sub { print "Activate\n"; },
-onDeactivate => sub { print "Deactivate\n"; },
-onTerminate => sub { $Window->InvalidateRect(0); print
"Terminate\n";},
-onResize => \&ChildSize,
) or die "Child";
$Child->Hook(WM_MOVE, sub { $Window->InvalidateRect(0); return 1;});
# Add a text filed into child window.
$Child->AddTextfield (
-name => "Edit",
-multiline => 1,
-pos => [0,0],
-size => [100,100], );
# Force a resize.
ChildSize($Child);
}
# This function manage child window resize.
sub ChildSize {
my $self = shift;
my ($width, $height) = ($self->GetClientRect())[2..3];
# TextField take all client aera
$self->{Edit}->Resize($width, $height) if exists $self->{Edit};
}
sub get_bitmap
{
return Win32::GUI::BitmapInline->new(
[bitmap data removed to save size]
);
}