Anchor one window to another?

BlitzMax Forums/BlitzMax GUI Programming/Anchor one window to another?

Anyone know how to anchor one window to another? I want a primary window with a canvas object in it, and then a secondary window which attaches to the right side of the first window.

I'd like for this second window to remain fixed in place whenever the main window moves. Neither will have titlebars, so it will appear as if the two windows combine into one primary window.

Is it possible to 'anchor' windows to one another?

I tried this using one big window with a canvas and a child window within it, but the child window remained entirely independent of the big window. How do I lock that bugger down?

Thanks in advance..

handle the main window move event, when the main windows moved process any anchor windows and position them at the correct position. use the desktop width and height and the main windows x,y,width,height positions to calulate where the anchor window should be.

one unsure point i make is does maxgui report window move events i dont recall, if not you would need to subclass and handle the WM_MOVE events yourself.

one unsure point i make is does maxgui report window move events i dont recall, if not you would need to subclass and handle the WM_MOVE events yourself.

Yes, MaxGUI does emit "window move" events. Just checked. :)

There used to be an undocumented WINDOW_CHILD but it seems to have been removed. It caused problems apparently
<edit> Hold on. This seems to have it working so it seems to be dropped from the svn version. Is that right?

Intriguing, the link tonyg posted to works IF you import:

Import MaxGUI.Win32MaxGUI


But, if you instead import
Import maxgui.drivers


The behavior is nullified. Unfortunately, importing both elements nullifies the proper behavior. Thus, I'd have to only use the Win32MaxGUI import

Could anyone explain to me the difference between these two.. um.. thingies?


EDIT

Just checked out maxgui.drivers.bmx - It auto-selects the correct import based upon your OS.

So, Win32MaxGUI DOES support the proper behavior I'm looking for, but Win32MaxGUIex does NOT support it. I am using XP, so maxgui.drivers automatically imports GuiEx.

So my question becomes... what kind of functionality do I lose by using Win32MaxGUI over Win32MaxGUIex ? I don't need to support Mac/Linux, so I'm only asking about Windows functionality.

Thanks!

Drat!

For some reason the child window will not display a menu.. what gives??

Think I better step in and say something here...

Basically, MaxGUI has never officially supported nested child windows. Mark/Skidracer seemed to have looked into the idea of MDI child windows at one point (hence the undocumented WINDOW_CHILD flag that only worked on the old Windows MaxGUI driver). However, I believe that it was quickly decided that MDI child windows weren't very cross-platform friendly (OS X for example, doesn't provide any API for such window management) but it is also considered quite an old-fashioned way of managing multiple documents in favour of tabbed/multiple window layouts. Because of this, only MaxGUI.Win32MaxGUI behaves differently with WINDOW_CHILD and the only reason this constant hasn't been removed is for backwards compatibility. However, the trial Windows implementation of WINDOW_CHILD had many inconsistencies/problems (one of which you've found to be an inability to add menus), which is a design constraint imposed by Microsoft in the MDI era.

This is not to say, however, that a window can't have a parent. The latest MaxGUI drivers cause windows (with a group parameter specified in the CreateWindow call) to be linked with the parent window. Although they won't move with the parent window, the window will stay-on-top of, and minimize with the parent. These are what I now refer to as child windows and are far more common place than MDI in modern day programs.

OK, onto MaxGUI.Drivers. You're half right in that MaxGUI.Drivers imports the correct module for the environment you are running, but this isn't purely OS dependent. Quite simply, it comes down to the version of Common Controls you are using. Although for Windows 9X/ME, the common control version will always fall below the threshold required for the newer MaxGUI.Win32MaxGUIEx to be used, the behaviour isn't as clear-cut for Windows XP/Vista, as the version of Common Controls your application uses is dependent upon the presence of a manifest file. Fortunately, MaxGUI imports this as an embedded resource automatically on Windows, so on the newer OSs you'll be using the newer MaxGUI driver.

Although that probably sounds very confusing, in practice there's very good reasoning. MaxGUI.Win32MaxGUIEx is an improved Windows MaxGUI implementation that allows you to build unicode apps for the newer OSs that can support it - without the help of MSLU, apps compiled only with MaxGUI.Win32MaxGUIEx will not work on Windows 9X. MaxGUI.Win32MaxGUI is the older ANSI MaxGUI implementation that is now somewhat redundant but kept for maintaining backwards compatibility on the older OSs. Importing MaxGUI.Drivers sorts out most of the hard-work for you, so that MaxGUI applications should at least do something on legacy operating systems.

You're right - you can circumvent this behaviour by directly importing MaxGUI.Win32MaxGUI yourself instead of MaxGUI.Drivers, but you will lose all the advantages MaxGUI.Win32MaxGUIEx gives on Windows XP/Vista. Here are just a few off the top of my head:

> Unicode support.
> More professional looking applications that are properly themed.
> Better support for gadget colours/pixmaps.
> Generic gadget tooltips.
> Faster resizing of gadgets, and text area highlighting.
> Other misc. bug fixes.

Checkout my worklog for more info.

As Kev said, monitor the EVENT_WINDOWMOVE, best in a hook function for instant reaction.
Then calculate the offset to your main window and let the second window follow by using the SetGadgetShape command.

Thanks Seb for the detailed explanation of what's going on.

I love this community, everybody is so helpful!

I will utilize an eventhook to monitor the mainwindow's position, and reposition the child accordingly.

Thanks again!

Just a quick update, I'm using a hook for EVENT_WINDOWMOVE as Kev suggested.

Works Great!

Here's the source if you're interested..

SuperStrict

Import MaxGUI.Drivers


Type TAppWind
	
Global	CanvasWindow:TGadget
Global	InterfaceWindow:TGadget
Global	InterfaceWindow_File:TGadget
Global	InterfaceWindow_Options:TGadget
Global	InterfaceWindow_Help:TGadget
Global	FileWindow:TGadget
Global 	OptionsWindow:TGadget
Global	HelpWindow:TGadget

Global CanvasWidth:Int = 400
Global CanvasHeight:Int = 300
Global InterfaceWidth:Int = 200


'Basic Setup Stuff in the 'Create' Function

Function Create:TAppWind(ApplicationTitle$)	
	If Not CanvasWindow
	AppTitle = ApplicationTitle
	
	Local style1:Int = WINDOW_TITLEBAR | WINDOW_MENU
	Local style2:Int = WINDOW_CLIENTCOORDS | WINDOW_TOOL 
	
	Local cx:Int = (ClientWidth(Desktop() ) - (canvasWidth+InterfaceWidth)) / 2
	Local cy:Int = (ClientHeight(Desktop() ) - CanvasHeight) / 2
	Local ix:Int = cx + canvasWidth + 1
	
	InterfaceWindow:TGadget = 		CreateWindow:TGadget(ApplicationTitle , ix , cy , InterfaceWidth , CanvasHeight , Null , style1 ) 
	CanvasWindow:TGadget = 			CreateWindow:TGadget(ApplicationTitle , 0 , 0 , CanvasWidth , CanvasHeight , Null , style2 ) 
	
	SetGadgetColor( CanvasWindow:TGadget , 0 , 0 , 0 , True ) 
	SetCanvasShape(GadgetX(InterfaceWindow),GadgetY(InterfaceWindow))
	
	InterfaceWindow_File:TGadget = 	CreateMenu( "File" , 100 , WindowMenu( InterfaceWindow:TGadget ) )
	InterfaceWindow_Options:TGadget = 	CreateMenu( "Options" , 200 , WindowMenu( InterfaceWindow:TGadget ) )
	InterfaceWindow_Help:TGadget = 	CreateMenu( "Help" , 300 , WindowMenu( InterfaceWindow:TGadget ) )
	UpdateWindowMenu( InterfaceWindow:TGadget )  
	
	
	OptionsWindow:TGadget = CreateWindow:TGadget("Options",ClientWidth(Desktop())/2-(200),ClientHeight(Desktop())/2-(90),400,180,Null,WINDOW_TITLEBAR|WINDOW_TOOL )
	HideGadget( OptionsWindow:TGadget ) 
	
	ActivateGadget(CanvasWindow) 
	ActivateGadget(InterfaceWindow) 
						
	AddHook EmitEventHook , AppWindEventManager
	
	EndIf
End Function



Function AppWindEventManager:Object(id:Int , data:Object , context:Object) 
	Local event:TEvent = TEvent(Data) 
	
	If event = Null Then Return Null
	
	Select event.id
		Case EVENT_WINDOWCLOSE
			Select event.source
				Case CanvasWindow , InterfaceWindow
					DisableGadget(CanvasWindow) 
					DisableGadget(InterfaceWindow) 
					
					If Confirm("Quit the application?")
						End
					Else
						resumemainapp()
					EndIf
						
									
				Case OptionsWindow , FileWindow , HelpWindow
						HideGadget(TGadget(event.source))
						ResumeMainApp() 				
			End Select

		Case EVENT_APPRESUME
			ResumeMainApp()
			
		Case EVENT_WINDOWMOVE
			Select event.source
				Case InterfaceWindow	
					InterfaceWindow_WM( event ) 
			End Select

		Case EVENT_MENUACTION
			Select event.data
				'These are Menu Events for the InterfaceWindow
				Case 100	'File
					'DisableGadget(InterfaceWindow) 	'no file window currently exists
					'DisableGadget(CanvasWindow)
					'ShowGadget(FileWindow)
				Case 200	'Options
					DisableGadget(InterfaceWindow) 
					DisableGadget(CanvasWindow)
					ShowGadget(OptionsWindow)
				Case 300	'Help
					'DisableGadget(InterfaceWindow) 	'no help window currently exists
					'DisableGadget(CanvasWindow)
					'ShowGadget(HelpWindow)
		End Select

	End Select
	
	Return data
End Function


Function InterfaceWindow_WM( event:TEvent )
	'if not in fullscreen mode..
		SetCanvasShape(event.x , event.y) 
	'else if in fullscreen mode.. do something else
End Function

Function SetCanvasShape(ix:Int,iy:Int) 
	SetGadgetShape(CanvasWindow,ix-CanvasWidth-1,iy,CanvasWidth,CanvasHeight)
End Function

Function ResumeMainApp()
	EnableGadget(CanvasWindow) 
	EnableGadget(InterfaceWindow) 
	ActivateGadget(CanvasWindow)
	
	
	'If Not GadgetHidden(FileWindow) then ActivateGadget(fileWindow) 	'no file window currently exists
	If Not GadgetHidden(OptionsWindow)Then ActivateGadget(OptionsWindow) 
	'If Not GadgetHidden(HelpWindow) Then ActivateGadget(HelpWindow)		'no help window currently exists
	
	
End Function

End Type





TAppWind.Create("Framework Application") 

Repeat
	WaitEvent()
		DebugLog CurrentEvent.tostring()
Forever





A quick note on this particular program... I'm aware this isn't the most clean OOP way of handling GUI functionality. But the idea is that all "Interface-specific" functions (Such as closing the window, setting the canvas' resolution via options menu, saving data, opening data, displaying the help file, moving the window, max/minimizing, etc ) are handled within the TAppWind object, and any program-specific functions (such as performing a program action upon clicking a button) will emit events to be intercepted by the specific program's own event hook.

Hopefully this will make it separable enough to use within some of my other as a good foundation.

On the OptionsWindow's group parameter, you can put InterfaceWindow.