Archived

This topic is now archived and is closed to further replies.

Windowing again....

This topic is 5666 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I start things and then leave then out for the longest of times... So now am back to my Windowing class... Here is the code...
  
#ifndef G3DGUI_H
#define G3DGUI_H

// Required include files.

#include <windows.h>
#include <windowsx.h>

namespace G3DGUI
{
	class Object
	{
	public:

		Object(HINSTANCE pInstance, HWND pParent)
		{
			setInstance(pInstance);
			setParent(pParent);
			setHandle(NULL);
			setClassName(NULL);
			setX(0);
			setY(0);
			setWidth(0);
			setHeight(0);
			setStyle(0);
			setStyleEx(0);
			setCaption(NULL);
			setMenu(NULL);			
		}
		
		virtual ~Object() {}

		const HINSTANCE getInstance() const { return(instance); }
		const HWND getParent() const { return(parent); }
		const HWND getHandle() const { return(handle); }
		const char* getClassName() const { return(className); }
		const getX() const { return(x); }		
		const getY() const { return(y); }		
		const getWidth() const { return(width); }		
		const getHeight() const { return(height); }		
		const unsigned int getStyleEx() const { return(styleEx); }
		const unsigned int getStyle() const { return(style); }
		const char* getCaption() const { return(caption); }
		const HMENU getMenu() const { return(menu); }

		void create(const char *pClassName, int pX, int pY, int pWidth, int pHeight, unsigned int pStyle, unsigned int pStyleEx, const char *pCaption, HMENU pMenu = NULL)
		{
			setClassName(pClassName);
			setX(pX);
			setY(pY);
			setWidth(pWidth);
			setHeight(pHeight);
			setStyle(pStyle);
			setStyleEx(pStyleEx);
			setCaption(pCaption);
			setMenu(pMenu);

			windowClass.lpfnWndProc = defaultProc;
			windowClass.hInstance = getInstance();
			windowClass.lpszClassName = getClassName();
			windowClass.cbSize = sizeof(WNDCLASSEX);
			windowClass.cbClsExtra = 0;
			windowClass.cbWndExtra = 0;
			windowClass.style = 0;
			windowClass.hCursor = LoadCursor(0, IDC_ARROW);
			windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
			windowClass.hIcon = LoadIcon(0, IDI_APPLICATION);
			windowClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
			windowClass.lpszMenuName = NULL;

			registerClass();
			
			setHandle(CreateWindowEx(getStyleEx(), getClassName(), getCaption(), getStyle(),
									 getX(), getY(), getWidth(), getHeight(), getParent(),
									 getMenu(), getInstance(), (LPSTR)this));
			update();
			
			show();
		}
		
		void update() { UpdateWindow(getHandle()); }
		void show()	{ ShowWindow(getHandle(), SW_SHOW); }
		void hide()	{ ShowWindow(getHandle(), SW_HIDE); }
		void focus() { SetFocus(getHandle()); }

		static LRESULT CALLBACK defaultProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
		{
			Object *thisObject = (Object*)GetWindowLong(hWnd, GWL_USERDATA);
			
			if(!thisObject)
			{
				if(Msg == WM_CREATE)
				{
					LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
					thisObject = (Object*)lpcs->lpCreateParams;
					SetWindowLong(hWnd, GWL_USERDATA, (LONG)thisObject);
					return thisObject->winProc(Msg, wParam, lParam);
				}
				else
					return DefWindowProc(hWnd, Msg, wParam, lParam); // should never be called

			}
			else
			{
				return thisObject->winProc(Msg, wParam, lParam);
			}
		}
	
		virtual LRESULT winProc(UINT Msg, WPARAM wParam, LPARAM lParam) = 0;

	protected:

		void setInstance(HINSTANCE pInstance) { instance = pInstance; };
		void setParent(HWND pParent) { parent = pParent; };
		void setHandle (HWND pHandle) { handle = pHandle; }		
		void setClassName(const char *pClassName) { className = pClassName; }
		void setX(int pX) { x = pX; }		
		void setY(int pY) { y = pY; }		
		void setWidth(int pWidth) { width = pWidth; }		
		void setHeight(int pHeight) { height = pHeight; }		
		void setStyle(unsigned int pStyle) { style = pStyle; }
		void setStyleEx(unsigned int pStyleEx) { styleEx = pStyleEx; }
		void setCaption(const char *pCaption) { caption = pCaption; }
		void setMenu(HMENU pMenu) { menu = pMenu; }

		bool registerClass()
		{
			if(!RegisterClassEx(&windowClass))
				return(false);
			else
				return(true);
		}
		
		void unRegisterClass()
		{
			UnregisterClass(getClassName(), getInstance());
		}

	private:

		HINSTANCE instance;
		HWND parent;
		HWND handle;
		WNDCLASSEX windowClass;
		const char* className;
		const char*	caption;
		HMENU menu;
		int x;
		int	y;
		int	width;
		int	height;
		unsigned int style;
		unsigned int styleEx;
	};
}

#endif
  
People mentioned that this was unflexible and only good if I wanted to create windows only. How would I fix this problem without having to use templates, like some other gui API out there etc...

Share this post


Link to post
Share on other sites
For one, separate your abstraction of the WNDCLASS(EX) from that of the HWND.

"Don''t be afraid to dream, for out of such fragile things come miracles."

Share this post


Link to post
Share on other sites
So Redleaf eventually I will finish this...

So I should keep windowclas(ex) in the base "Object" class and have the hwnd per sub class or the other way around? Keep the hwnd in the base class and put the windowclass(ex) in the sub classes?

Share this post


Link to post
Share on other sites
Actually, I would probably encapsulate the WNDCLASSEX as a specification or blueprint for a particular type of window. Something that can make the "Objects" you refer to. Then, the Object class would encapsulate the HWND.

"Don''t be afraid to dream, for out of such fragile things come miracles."

Share this post


Link to post
Share on other sites
So I said it right... The Object will be my base class that will encap the HWND. All other classes that sublclass from Object will have there own WindowClass with required info... Also where shoudl I put the default WindowProc, I guess, with the WindowClass....

Share this post


Link to post
Share on other sites
Well they wouldn''t inherit from Object.

Your classes encapsulating WNDCLASSEXs would be in a parellel inheritence tree with those that encapsulate HWNDs.

For Example:
ObjectMaker encapsulates WNDCLASSEX
Object encapsulates HWND

ObjectMaker has some method for creating Objects

SpecialObjectMaker inherits from ObjectMaker
SpecialObject inherits from Object

SpecialObjectMaker would make SpecialObjects

"Don''t be afraid to dream, for out of such fragile things come miracles."

Share this post


Link to post
Share on other sites
Ok now your pushing the factory patern...

So If I understand you correctly...


        

class ObjectFactory
{
public:
ObjectFactory();
virtual ~ObjectFactory();

Object *create(x, y, with, height, parent, menu, classname, caption)
{
// setup the windowClass structure...


Object object = new Object(x, y, width, height, menu, classname, caption); <<---- Is this correct, should only the HWND be passed or the entire WNDCLASSEX struct be passed? Should the parent be passed or is parent attached to the handle one CReateWindowEX is called?

return(object);
}
private:
WNDCLASSEX windowClass;
}

class Object
{
public:
Object(x, y, width, height, menu, classname, caption);
virtual ~Object();
void update();
void show();
void hide();
void focus();
etc...
private:
HWND hwnd;
int x, y, width, height;
char classname[128], caption[128];
HMENU menu;
}


Now where would I put the defaultProc and windowProc CALLBACK?
What else is missing?

[edited by - ANSI2000 on June 6, 2002 10:26:13 AM]

[edited by - ANSI2000 on June 6, 2002 10:29:15 AM]

[edited by - ANSI2000 on June 6, 2002 1:16:10 PM]

[edited by - ANSI2000 on June 6, 2002 1:17:07 PM]

Share this post


Link to post
Share on other sites
Yes, I suppose I am.

Everything that is tied to the WNDCLASS directly, as a parameter for determining the type of window you would create with it, belongs encapsulated along with it. This includes the classname member you have in the Object class. It also includes the static CALLBACK function for the WndProc that you use as the WNDCLASS function pointer parameter. Then each Object has a member procedure of its own that you can have that static callback relay to, using the creation techniques from your previous method. This allows your window procedure access to your Object properties.

I''m not sure what you mean by defaultProc, as windows has its own default that you can call.


"Don''t be afraid to dream, for out of such fragile things come miracles."

Share this post


Link to post
Share on other sites
This is what I wrote so far...

BUt of course it is wrong. It does not look like the CALLBACKS are being called...


  
namespace G3DGUI
{
class Object
{
public:

Object() {}

Object(HWND pParent, HWND pHandle, char *pClassName, int pX, int pY, int pWidth, int pHeight, char *pCaption)
{
MessageBox(NULL, NULL, "boo 4", 0);

parent = pParent;
handle = pHandle;
}

~Object() {}

virtual LRESULT winProc(UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg)
{
case WM_CREATE:

MessageBox(NULL, NULL, "Create", 0);

break;

case WM_DESTROY:
PostQuitMessage(0);

break;
default:
return(DefWindowProc(handle, Msg, wParam, lParam));
}
return(0);
};

void update() { UpdateWindow(handle); }
void show() { ShowWindow(handle, SW_SHOW); }

private:

HWND parent;
HWND handle;
int x, y, width, height;
char className[128];
char caption[128];

};

class ObjectFactory
{
public:
ObjectFactory(HINSTANCE pInstance) : instance(pInstance)
{}

Object *createObject(HWND pParent, char *pClassName, int pX, int pY, int pWidth, int pHeight, char *pCaption)
{
MessageBox(NULL, NULL, "boo 1", 0);

windowClass.lpfnWndProc = defaultProc;
windowClass.hInstance = instance;
windowClass.lpszClassName = pClassName;
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.style = 0;
windowClass.hCursor = LoadCursor(0, IDC_ARROW);
windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
windowClass.hIcon = LoadIcon(0, IDI_APPLICATION);
windowClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
windowClass.lpszMenuName = NULL;

HWND handle;

handle = CreateWindowEx(NULL, pClassName, pCaption, WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
pX, pY, pWidth, pHeight, pParent,
NULL, instance, (LPSTR)this);

MessageBox(NULL, NULL, "boo 2", 0);

registerClass();

object = new Object(pParent, handle, pClassName, pX, pY, pWidth, pHeight, pCaption);

object->update();
object->show();

return(object);
}

static LRESULT CALLBACK defaultProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
MessageBox(NULL, NULL, "Call 1", 0);

Object *thisObject = (Object*)GetWindowLong(hWnd, GWL_USERDATA);

if(!thisObject)
{
MessageBox(NULL, NULL, "Call 2", 0);
if(Msg == WM_CREATE)
{
MessageBox(NULL, NULL, "Call 3", 0);

LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
thisObject = (Object*)lpcs->lpCreateParams;
SetWindowLong(hWnd, GWL_USERDATA, (LONG)thisObject);
return thisObject->winProc(Msg, wParam, lParam);
}
else
{
MessageBox(NULL, NULL, "Call 4", 0);

return DefWindowProc(hWnd, Msg, wParam, lParam); // should never be called

}
}
else
{
MessageBox(NULL, NULL, "Call 5", 0);

return thisObject->winProc(Msg, wParam, lParam);
}
}

bool registerClass()
{
if(!RegisterClassEx(&windowClass))
{
MessageBox(NULL, NULL, "boo 3a", 0);

return(false);
}
else
{
MessageBox(NULL, NULL, "boo 3b", 0);

return(true);
}
}

private:

Object *object;
HINSTANCE instance;
WNDCLASSEX windowClass;
};
}

Share this post


Link to post
Share on other sites
Your problem lies here:
handle = CreateWindowEx(NULL, pClassName, pCaption,
WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
pX, pY, pWidth, pHeight, pParent, NULL, instance, (LPSTR)this);


The last parameter is supposed to be a pointer to the "Object" instance you are creating, not to the ObjectFactory itself. Because you are changing the location of your Creation functionality, you have to adjust accordingly. It is no longer in the Object class. You cannot use the this pointer. You will need to be able to create an instance of your Object class before calling CreateWindowEx, so that you can pass the address of that instance as the final parameter for creation.

"Don't be afraid to dream, for out of such fragile things come miracles."

Edit: Well that was certainly lousy formatting on my part.

[edited by - Redleaf on June 6, 2002 7:18:23 PM]

[edited by - Redleaf on June 6, 2002 7:19:24 PM]

Share this post


Link to post
Share on other sites
Ok so my ObjectFactory will have a private memeber

Object *object;

object = new Object();

handle = CreateWindowEx(...., object);

so when do I pass the handle to the object? And what about properties x, y, width, height, caption etc...

I guess I can give teh object it''s properties when I instantiate it... And once CreateWindowEx has returned a handle call a setHandle method from the object?

Share this post


Link to post
Share on other sites
Ok got it working now. Here is what I have so far...


    
namespace G3DGUI
{
class Object
{
public:

Object() {};
~Object() {};

void setProperties(HWND pHandle, int pX, int pY, int pWidth, int pHeight, char *pCaption, char *pMenu = NULL)
{
handle = pHandle;
x = pX;
y = pY;
width = pWidth;
height = pHeight;
strcpy(caption, pCaption);
//strcpy(menu, pMenu);

}

//virtual LRESULT winProc(UINT Msg, WPARAM wParam, LPARAM lParam) = 0;



LRESULT winProc(UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg)
{

case WM_CREATE:
break;

case WM_DESTROY:
PostQuitMessage(0);
break;

default:
return(DefWindowProc(handle, Msg, wParam, lParam));
}
return 0;
}

void update() { UpdateWindow(handle); }
void show() { ShowWindow(handle, SW_SHOW); }
void hide() { ShowWindow(handle, SW_HIDE); }
void focus() { SetFocus(handle); }
void destroy() { DestroyWindow(handle); }

private:

HWND handle;
int x, y, width, height;
unsigned int style, styleEx;
char caption[128];
char menu[128];

};

class ObjectFactory
{
public:

ObjectFactory(HINSTANCE pInstance)
{
instance = pInstance;
}

~ObjectFactory() {};

Object *createObject(HWND pParent, char *pClassName, int pX, int pY, int pWidth, int pHeight, unsigned int pStyle, unsigned int pStyleEx, char *pCaption, char *pMenu = NULL)
{
windowClass.lpfnWndProc = defaultProc;
windowClass.hInstance = instance;
windowClass.lpszClassName = pClassName;
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.style = 0;
windowClass.hCursor = LoadCursor(0, IDC_ARROW);
windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
windowClass.hIcon = LoadIcon(0, IDI_APPLICATION);
windowClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
windowClass.lpszMenuName = pMenu;

registerClass();

object = new Object();

HWND hWnd;

hWnd = CreateWindowEx(pStyleEx, pClassName, pCaption, pStyle,
pX, pY, pWidth, pHeight, pParent,
NULL, instance, (LPSTR)object);


object->setProperties(hWnd, pX, pY, pWidth, pHeight, pCaption);

object->update();
object->show();

return(object);
}

static LRESULT CALLBACK defaultProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
Object *thisObject = (Object*)GetWindowLong(hWnd, GWL_USERDATA);

if(!thisObject)
{
if(Msg == WM_CREATE)
{
LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
thisObject = (Object*)lpcs->lpCreateParams;
SetWindowLong(hWnd, GWL_USERDATA, (LONG)thisObject);

return(thisObject->winProc(Msg, wParam, lParam));
}
else
return(DefWindowProc(hWnd, Msg, wParam, lParam)); // should never be called

}
else
{
return(thisObject->winProc(Msg, wParam, lParam));
}
}

protected:

bool registerClass()
{
if(!RegisterClassEx(&windowClass))
return(false);
else
return(true);
}

private:

HINSTANCE instance;
WNDCLASSEX windowClass;
Object *object;
};
}


I dont like this section of code though...

object = new Object();

HWND hWnd;

hWnd = CreateWindowEx(styleEx, className, caption, style, x, y, width, height, parent, NULL, instance, (LPSTR)object);

object->setProperties(hWnd, x, y, width, height, caption);

object->update();
object->show();

return(object);

Next I will abstarct the Object, ObjectFactory classes. Also any other improvements to make?

[edited by - ANSI2000 on June 10, 2002 2:52:37 PM]

Share this post


Link to post
Share on other sites
I started thinking of subclassing...

The only problem I see here is... The point of a factory is to create instances of different kinds of objects. The problem arise when you abstract the object to overide the winProc...

Well this can be fixed by createing specialized Factories, but like I think isn''t a factory suposed to make different kinds of "Objects"?

Share this post


Link to post
Share on other sites
That''s sort of why I was hesitant to call it a factory. Because the class responsible for creating gui objects is a mold of sorts, with its WNDCLASS determining the type of objects it creates, it really isn''t capable of creating various types of objects.

If you desire a factory pattern, such a factory would probably contain such "molds" for use when instantiating various types of objects. But each WindowMaker mold would only be responsible for creating one type using its encapsulated WNDCLASS, which includes the WndProc.

"Don''t be afraid to dream, for out of such fragile things come miracles."

Share this post


Link to post
Share on other sites
So what I have so far is it ok? Or do I need to do more? Change stuff add stuff?

I dont like this to much :/ It will require to subclass every time you want to make a different "object"

[edited by - ANSI2000 on June 10, 2002 4:48:24 PM]

Share this post


Link to post
Share on other sites
I''m not too crazy about it either, which is why I stay away from it.

As for subclassing, it''s not always necessary. For style differences, you simply give a basic WindowMaker class methods which are used to enable or change particular style properties for the windows it creates. The only time you would really have to subclass is when you need fundamentally different message handlers, (hence a change in your WndProc as well).

"Don''t be afraid to dream, for out of such fragile things come miracles."

Share this post


Link to post
Share on other sites
So as for things to add, I''d simply include a simple style setting interface for your WindowMaker. For inspiration when creating this, take a look at that code I gave you a while back (which you hopefully still have).

Plus, I wouldn''t have your basic Object post a quit message when it''s destroyed. You probably want to reserve that for a MainWindow class.

"Don''t be afraid to dream, for out of such fragile things come miracles."

Share this post


Link to post
Share on other sites
I dont think I still have that...

Do you still have it? If so e-mail it to me at voodoo@videotron.ca

Actually I rember liking that code much better then this pattern we have now...

As for teh PostQuit, I put that there for the hell of it... It''s just a proc I copied and pasted from another app...

Share this post


Link to post
Share on other sites