Centering and 'tracking' Windows

... or Win32lib in 'style'.

As was pointed out in Lesson 5, all Win32lib windows are created when the program initially starts up. Unfortunately, any secondary windows can be dragged anywhere by the user, and then closed. The next time the window is opened, it will be shown where the user last left it. This may be confusing to the user, especially if he has moved the main window.
...and not at all what you want !

Also, if you are using a secondary window as a message box, and don't want to use the plain generic one, how do you center it? Another problem with this, is that you have no way of dealing with different screen resolutions. If you want to center a custom message box, how do you center it for both 640 by 480, 800 by 600 and other screen sizes ?

This lesson shows you two simple routines to handle window locations, to deal with the above problems.

The first routine, to center a window on the screen is fairly simple. The second routine, which always tries to open a secondary window 'relative' to the main window, is a bit more complicated. Since this routine might be used for modal windows, one has to make sure the window is always visible enough to get to it's [exit] button, or whatever...
...so I've made sure the secondary window is always fully visible on the screen, no matter where the main window has been dragged.

Keep in mind that simple message boxes are modal windows, and have no minimize/maximize, or [x], and no taskbar icon to escape through, which is why I'm using {WS_CAPTION,WS_THICKFRAME},
{WS_CAPTION},WS_EX_DLGMODALFRAME), and
{WS_POPUP,WS_DLGFRAME}
...as the last create() parameter in the three modal secondary windows. This gives us three different 'modal' styles.

Note that Win4 is created with the createEx() function, which uses an extra style parameter.

You obviously 'MUST' have a button or hot-key routine to escape these, unless you prefer to use ctrl/alt/delete... ;-)

Just to fancy it up a bit, I've created eu.bmp, which is a 16 color version of the new Euphoria logo, and used it in the custom and untitled message boxes.

So first, lets create our usual program front-end.

--winstyles.exw

include win32lib.ew
without warning

constant
string1 = "Your Main Window",
string2 = "'tracking' window",
string3 = "'tracking' modal window",
string4 = "custom message box",
string5 = "untitled message box",
string6 = "OK !",
string7 = "a win32lib tutorial",
string8 = "~Wolf 11/24/99 .",

-- our main window
Win1    = create( Window, string1, 0, 0, 0, 370, 320, 0 ),
Button1 = create( PushButton, string2, Win1, 5, 5, 170, 25, 0 ),
Button2 = create( PushButton, string3, Win1, 5, 31, 170, 25, 0 ),
Button3 = create( PushButton, string4, Win1, 5, 57, 170, 25, 0 ),
Button4 = create(PushButton,string5,Win1,5,83,170,25,0),
-- 'tracking' window
Win2    = create( Window, string2, Win1, 0, 0, 338, 190, 0 ),
Button5 = create( PushButton, string6, Win2, 135, 130, 70, 25, 0 ),
Title1  = create( CText, string7, Win2, 1, 135, 130, 20, 0 ),
-- 'tracking' modal window
Win3    = create( Window, string3, Win1, 0, 0, 338, 190, {WS_CAPTION,WS_THICKFRAME} ),
Button6 = create( PushButton, string6, Win3, 135, 130, 70, 25, 0 ),
Title2  = create( CText, string8, Win3, 206, 135, 130, 20, 0 ),
-- custom message-box
Win4    = createEx( Window, string4, Win1, 0, 0, 338, 190, {WS_CAPTION}, WS_EX_DLGMODALFRAME ),
Button7 = create( PushButton, string6, Win4, 135, 130, 70, 25, 0 ),
Title3  = create( CText, string7, Win4, 1, 135, 130, 20, 0 ),
Title4  = create( CText, string8, Win4, 206, 135, 130, 20, 0 ),
Bitmap1 = create( Bitmap, "", Win4, 0, 0, 332, 126, 0 ),
-- untitled message-box
Win5    = create( Window, "", Win1, 0, 0, 338, 165, {WS_POPUP,WS_DLGFRAME} ),
Button8 = create( PushButton, string6, Win5, 135, 130, 70, 25, 0 ),
Title5  = create( CText, "your", Win5, 1, 135, 130, 20, 0 ),
Title6  = create( CText, "message", Win5, 206, 135, 130, 20, 0 ),
Bitmap2 = create( Bitmap, "", Win5, 0, 0, 332, 126, 0 )


-- get the bitmaps from the \Bin sub-folder.
setBitmap( Bitmap1,".\\Bin\\eu.bmp" )
setBitmap( Bitmap2,".\\Bin\\eu.bmp" )

atom rgbColor
 rgbColor = getSysColor(COLOR_BTNFACE)

-- set the window background colors
 setWindowBackColor(Win1,rgb(255,0,255))
 setWindowBackColor(Win2,rgbColor)
 setWindowBackColor(Win3,rgb(255,255,0))
 setWindowBackColor(Win4,rgbColor)
 setWindowBackColor(Win5,rgbColor)

The next procedure first uses the getRect() function to get the size of a window. If you only had one window, I suppose you could hard-code the window's size into the following lines, however I wanted a generic portable routine to be used more than once.

The getRect() function returns the absolute locations of the four window corners as a four number sequence. These values are then used to calculate the size of the actual window.( xsize,ysize )
Calling the xGetSystemMetrics() function gives us the screen size presently set up in the user's computer. Dividing this by two gives us the center of the screen. Subtracting one-half of our window's size gives us the location we want to position our window so it is centered. This is done for both the x and y values.

We then simply use the setRect() procedure, which both re-locates and re-paints the window.

procedure center_win(atom win_id)
sequence loc
atom xsize,ysize,posx,posy
loc=getRect(win_id)
xsize=loc[3]-loc[1]
ysize=loc[4]-loc[2]
posx=floor((w32Func(xGetSystemMetrics,{SM_CXFULLSCREEN})/2)-(xsize/2))
posy=floor((w32Func(xGetSystemMetrics,{SM_CYFULLSCREEN})/2)-(ysize/2))
setRect(win_id,posx,posy,xsize,ysize,1 )
end procedure

The next procedure uses the same ideas as the one above, but by passing both win_id parameters to it, and the offset values desired. This will allow any ( second ) window to stay 'glued' to the relative location of the first.

procedure track_main( atom win_id, atom modal_win_id, atom xoffset, atom yoffset )
sequence loc,loc2
atom xsize,ysize,posx,posy,maxx,maxy,diff
loc=getRect(win_id)
loc2=getRect(modal_win_id)
xsize=loc2[3]-loc2[1]
ysize=loc2[4]-loc2[2]
posx=loc[1]+xoffset
--find left edge of screen.
maxx=floor((w32Func(xGetSystemMetrics,{SM_CXSCREEN})))
--don't go beyond left edge.
if posx > maxx-xsize then posx=maxx-xsize  end if
--don't go beyond right edge either.
if posx < 0 then posx=0 end if
posy=loc[2]+yoffset
--find bottom edge of screen.
maxy=floor((w32Func(xGetSystemMetrics,{SM_CYSCREEN})))
--account for task-bar.
diff=floor((w32Func(xGetSystemMetrics,{SM_CYSCREEN}))-
           (w32Func(xGetSystemMetrics,{SM_CYFULLSCREEN})))
--don't go below bottom edge, or under task-bar.
if posy > maxy-ysize then posy=floor(maxy-(ysize+diff/2)-3)  end if
setRect(modal_win_id,posx,posy,xsize,ysize,1 )
end procedure

procedure show_tracking_win( integer self, integer event, sequence params )
  track_main(Win1,Win2,70,40)
  openWindow(Win2,Normal)
end procedure

procedure close_tracking_win( integer self, integer event, sequence params )
  closeWindow(Win2)
end procedure

procedure show_tracking_modal( integer self, integer event, sequence params )
  setRect(Win3,0,0,338,190,1 )  -- restore size !
  track_main(Win1,Win3,70,40)
  openWindow(Win3,Modal)
end procedure

-- the first 'must-have' close routine
procedure close_tracking_modal()
   closeWindow(Win3)
end procedure

procedure show_custom_message( integer self, integer event, sequence params )
  center_win(Win4)
  openWindow(Win4,Modal)
end procedure

procedure close_custom_message( integer self, integer event, sequence params )
  closeWindow(Win4)
end procedure

procedure show_untitled( integer self, integer event, sequence params )
  center_win(Win5)
  openWindow(Win5,Modal)
end procedure

procedure close_untitled( integer self, integer event, sequence params )
  closeWindow(Win5)
  -- center main 'IF' it's not maximized
  if w32Func(xIsZoomed,{getHandle(Win1)}) = 0 then
      center_win(Win1)
  end if
end procedure


setHandler( Button1, w32HClick,  routine_id("show_tracking_win") )
setHandler( Button2, w32HClick,  routine_id("show_tracking_modal") )
setHandler( Button3, w32HClick,  routine_id("show_custom_message") )
setHandler( Button4, w32HClick,  routine_id("show_untitled") )
setHandler( Button5, w32HClick,  routine_id("close_tracking_win") )
setHandler( Button6, w32HClick,  routine_id("close_tracking_modal") )
setHandler( Button7, w32HClick,  routine_id("close_custom_message") )
setHandler( Button8, w32HClick,  routine_id("close_untitled") )


WinMain( Win1,Normal )
--end--

You will notice, when running this program, that exiting the untitled message box will center the main window. This is because the close_untitled() procedure includes a call to center_win().

I have called the IsZoomed() function in user32.dll here.

This function returns 0 if a window is not maximized, so the main window will ONLY be centered if it has NOT been maximized. You can try commenting out the if.. and end if.. lines to see what happens when you maximize the main window, and then exit the message box, effectively centering a maximized window. There is some interaction with the taskbar size here, that makes it 'strange'. Using CX_SCREEN, and CY_SCREEN in our routines would eliminate this problem, but then our 'centered' windows would NOT take the taskbar into account. ...so pick the constant of your choice :)

This obviously means that a better center_win() procedure should be written as...

procedure center_win(atom win_id)
sequence loc
atom xsize,ysize,posx,posy
if w32Func(zIsZoomed,{getHandle(win_id)}) = 0 then
  loc=getRect(win_id)
  xsize=loc[3]-loc[1]
  ysize=loc[4]-loc[2]
  posx=floor((w32Func(xGetSystemMetrics,{SM_CXFULLSCREEN})/2)-(xsize/2))
  posy=floor((w32Func(xGetSystemMetrics,{SM_CYFULLSCREEN})/2)-(ysize/2))
  setRect(win_id,posx,posy,xsize,ysize,1 )
end if
end procedure

...and of course, since the zIsZoomed() function equivalent is not yet in win32lib.ew, don't forget to add it to 'your' program !

While the two message boxes can not be re-sized by dragging their sides or their corners, the 'tracking_modal' window can be re-sized this way. ...so I've included a call to setRect() in it's open routine. This is not really to 'move' it, but merely to re-size it. This 'cleans up' the window to it's normal size whenever the window is re-opened.

The custom message box can still be 'dragged' by it's title bar, but the untitled message box cannot !

There is a window style, ( WS_POPUPWINDOW - #80880000 ) already defined in win32lib.ew, which is similar to the untitled message box style shown here, except it has a thin border, instead of bevelled.

moving on...

CONTENTS