Win32, simple app, too much code?

Started by
7 comments, last by ApochPiQ 18 years, 10 months ago
Sup, I was wondering if anyone else working with win32 think it's kind of ridiculous at the amount of code one has to type for something simple? The reason I'm asking is because; I just finished a little app that accepts input and updates the characters in the middle of the screen, as well as reposition the Caret for every new char accepted and update the client area as needed. Free Image Hosting at www.ImageShack.us Here's two functions I wrote just for that.

static void CheckCharacterValue(struct _WndProcInfo *wpi, TCHAR ascii)
{
	if ( isalpha(ascii) || (ascii == ASCIIBSPACE)
		|| (ascii == ASCIISPACE) )
	{
		if ( ascii == ASCIIBSPACE )
		{
			if ( (((wpi->sub_sv.cxCaretPos >> 1)
			- (wpi->sub_sv.iFieldWidth >> 1)) >
			((wpi->sub_sv.cxClient >> 1)
			- (wpi->sub_sv.iFieldWidth >> 1))) )
			{
				bool_bspace = TRUE;

				GetCharWidth32(wpi->hdc,
					(UINT)szBuffer[iBuffIndex - 1],
					(UINT)szBuffer[iBuffIndex - 1],
					&wpi->sub_sv.iCharWidth);

				wpi->charRect.bottom	= (wpi->sub_sv.cyClient >> 1) 
					+ (wpi->sub_sv.cyChar >> 1);
				wpi->charRect.left		= (wpi->sub_sv.cxCaretPos >> 1) 
					- (wpi->sub_sv.iFieldWidth >> 1) - wpi->sub_sv.iCharWidth;
				wpi->charRect.right		= (wpi->sub_sv.cxCaretPos >> 1) 
					- (wpi->sub_sv.iFieldWidth >> 1);
				wpi->charRect.top		= (wpi->sub_sv.cyClient >> 1)
					- (wpi->sub_sv.cyChar >> 1);

				InvalidateRect(wpi->hwnd, &wpi->charRect, TRUE);
				UpdateWindow(wpi->hwnd);

				wpi->sub_sv.cxCaretPos -= wpi->sub_sv.iCharWidth << 1;
				SetCaretPos((wpi->sub_sv.cxCaretPos >> 1)
					- (wpi->sub_sv.iFieldWidth >> 1),
					(wpi->sub_sv.cyClient >> 1)
					- (wpi->sub_sv.cyChar >> 1));

				szBuffer[--iBuffIndex] = '\0';
				bool_bspace = FALSE;
			}
		}
		else if ( ((wpi->sub_sv.cxCaretPos >> 1)
			- (wpi->sub_sv.iFieldWidth >> 1)) < ((wpi->sub_sv.cxClient >> 1)
			+ (wpi->sub_sv.iFieldWidth >> 1))  && ((iBuffIndex) != (BUFFSIZE - 1)))
		{
			bool_addchar = TRUE;
			GetCharWidth32(wpi->hdc,
				(UINT)ascii, (UINT)ascii, &wpi->sub_sv.iCharWidth);

			szBuffer[iBuffIndex++] = ascii;

			InvalidateRect(wpi->hwnd, NULL, FALSE);
			UpdateWindow(wpi->hwnd);
			
			wpi->sub_sv.cxCaretPos += wpi->sub_sv.iCharWidth << 1;

			SetCaretPos((wpi->sub_sv.cxCaretPos >> 1)
				- (wpi->sub_sv.iFieldWidth >> 1),
				(wpi->sub_sv.cyClient >> 1) 
				- (wpi->sub_sv.cyChar >> 1));
		}
	}
}

static void GetFieldRectDimensions(struct _WndProcInfo *wpi)
{
	wpi->fieldRect.left		= (wpi->eraseBkgndRect.right >> 1) - (wpi->sub_sv.iFieldWidth >> 1);
	wpi->fieldRect.top		= (wpi->eraseBkgndRect.bottom >> 1) - (wpi->sub_sv.cyChar >> 1);
	wpi->fieldRect.right	= (wpi->eraseBkgndRect.right >> 1) + (wpi->sub_sv.iFieldWidth >> 1);
	wpi->fieldRect.bottom	= (wpi->eraseBkgndRect.bottom >> 1) + (wpi->sub_sv.cyChar >> 1);
}

Here's my WinMain and Window Procedure

#include <windows.h>
#define ASCIIBSPACE 0x08
#define ASCIISPACE 0x20
#define BUFFSIZE 11
int iBuffIndex;
BOOL bool_addchar = FALSE;
BOOL bool_bspace = FALSE;
TCHAR szBuffer[BUFFSIZE];

struct _SimpleVars
{
	int cxClient;
	int cyClient;
	int cyChar;
	int cxChar;
	int cxCaps;
	int cxCaretPos;
	int iFieldWidth;
	int iCharWidth;
};

struct _WndProcInfo
{
	HDC		hdc;
	HWND	hwnd;
	RECT	eraseBkgndRect;
	RECT	fieldRect;
	RECT	charRect;
	PAINTSTRUCT ps;
	TEXTMETRIC	tm;
	struct _SimpleVars sub_sv;
}wpi;

TCHAR szWinName[] = TEXT("WIN32 Project");
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
static void CheckCharacterValue(struct _WndProcInfo*, TCHAR);
static void GetFieldRectDimensions(struct _WndProcInfo*);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
				   PSTR szCmdLine, int iCmdShow)
{
	static TCHAR szClassName[] = TEXT("InputProject");

	HWND		hwnd;
	MSG			msg;
	WNDCLASS	wc;

	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 0;
	wc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.hCursor			= LoadCursor(NULL, IDC_ARROW);
	wc.hIcon			= LoadIcon(NULL, IDI_APPLICATION);
	wc.hInstance		= hInstance;
	wc.lpfnWndProc		= WndProc;
	wc.lpszClassName	= szClassName;
	wc.lpszMenuName		= NULL;
	wc.style			= CS_HREDRAW | CS_VREDRAW;

	if ( !RegisterClass(&wc) )
	{
		MessageBox(NULL, TEXT("This Program Requires Windows NT!"),
			szWinName, MB_ICONEXCLAMATION);

		return 0;
	}

	if ( !(hwnd = CreateWindow(szClassName,
		szWinName,
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		NULL,
		NULL,
		hInstance,
		NULL)) )
	{
		MessageBox(NULL, TEXT("COF::CreateWindow() Failed!"),
			szWinName, MB_ICONEXCLAMATION);

		return 0;
	}

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);

	while ( GetMessage(&msg, NULL, 0, 0) )
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	
	return msg.message;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	wpi.hwnd = hwnd;

	switch ( iMsg )
	{

	case WM_CREATE :
		wpi.hdc = GetDC(wpi.hwnd);

		GetTextMetrics(wpi.hdc, &wpi.tm);
		wpi.sub_sv.cxChar = wpi.tm.tmAveCharWidth;
		wpi.sub_sv.cxCaps = (wpi.tm.tmPitchAndFamily & 1 ? 3 : 2) 
			* wpi.sub_sv.cxChar >> 1;
		wpi.sub_sv.cyChar = wpi.tm.tmHeight + wpi.tm.tmExternalLeading;

		wpi.sub_sv.iFieldWidth = wpi.sub_sv.cxCaps * 15;
		ReleaseDC(wpi.hwnd, wpi.hdc);
		return 0;

	case WM_SETFOCUS :
		CreateCaret(wpi.hwnd, NULL, 1, wpi.sub_sv.cyChar);
		ShowCaret(wpi.hwnd);
		return 0;

	case WM_ERASEBKGND :
		wpi.hdc = GetDC(wpi.hwnd);
		HideCaret(wpi.hwnd);

		if ( bool_bspace )
		{
			FillRect(wpi.hdc, &wpi.charRect,
				(HBRUSH)GetStockObject(LTGRAY_BRUSH));
		}
		else
		{
			GetClientRect(wpi.hwnd, &wpi.eraseBkgndRect);
			
			GetFieldRectDimensions(&wpi);
			FillRect(wpi.hdc, &wpi.fieldRect, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
			
			ExcludeClipRect(wpi.hdc, wpi.fieldRect.left,
				wpi.fieldRect.top,
				wpi.fieldRect.right,
				wpi.fieldRect.bottom);

			FillRect(wpi.hdc, &wpi.eraseBkgndRect, (HBRUSH)GetStockObject(WHITE_BRUSH));
			SelectClipRgn(wpi.hdc, NULL);
		}

		ShowCaret(wpi.hwnd);
		ReleaseDC(wpi.hwnd, wpi.hdc);
		return 1;

	case WM_SIZE :
		wpi.sub_sv.cxCaretPos -= wpi.sub_sv.cxClient;
		wpi.sub_sv.cxClient = LOWORD(lParam);
		wpi.sub_sv.cyClient	= HIWORD(lParam);
		wpi.sub_sv.cxCaretPos += wpi.sub_sv.cxClient;

		SetCaretPos((wpi.sub_sv.cxCaretPos >> 1) - (wpi.sub_sv.iFieldWidth >> 1),
			(wpi.sub_sv.cyClient >> 1) - (wpi.sub_sv.cyChar >> 1));
		return 0;

	case WM_PAINT :
		wpi.hdc = BeginPaint(hwnd, &wpi.ps);

		if ( bool_addchar )
		{
			SetBkColor(wpi.hdc, RGB(192, 192, 192));
			TextOut(wpi.hdc, (wpi.sub_sv.cxCaretPos >> 1)
				- (wpi.sub_sv.iFieldWidth >> 1),
				(wpi.sub_sv.cyClient >> 1) - (wpi.sub_sv.cyChar >> 1),
				&(szBuffer[iBuffIndex - 1]), 1);

			bool_addchar = FALSE;
		}
		else if ( *szBuffer && !bool_bspace )
		{
			SetBkColor(wpi.hdc, RGB(192, 192, 192));
			TextOut(wpi.hdc, (wpi.sub_sv.cxClient >> 1) - (wpi.sub_sv.iFieldWidth >> 1),
				(wpi.sub_sv.cyClient >> 1) - (wpi.sub_sv.cyChar >> 1), szBuffer,
				(int)strlen(szBuffer));
		}

		EndPaint(wpi.hwnd, &wpi.ps);
		return 0;

	case WM_CHAR :
		wpi.hdc = GetDC(wpi.hwnd);
		CheckCharacterValue(&wpi, (TCHAR)wParam);
		ReleaseDC(wpi.hwnd, wpi.hdc);
		return 0;

	case WM_KILLFOCUS :
		DestroyCaret();
		return 0;

	case WM_DESTROY :
		PostQuitMessage(0);
		return 0;

	default :
		break;
	}

	return DefWindowProc(wpi.hwnd, iMsg, wParam, lParam);
}

I also have another question. I managned to get rid of the flickering for my input field's background everytime the app is resized (which is light gray as you can see in the pic). This is clearly seen when no chars have yet been displayed. But, I can't seem to get rid of the flickering that occurs as a result of the characters being displayed everytime my app is resized. That occurs because the string displayed is constantly updateing over itself with each resize. Is there a way to get rid of this problem without using double-buffering? Thanks.
Advertisement
You built half an Edit control. Of course that isn't simple, a Edit control is the most complicated control IMHO. Why don't you simply use a real Edit control?

Also, the flickering will always be there unless you do double buffering (or use the Edit).

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

Quote:Original post by Endurion
You built half an Edit control. Of course that isn't simple, a Edit control is the most complicated control IMHO. Why don't you simply use a real Edit control?

Also, the flickering will always be there unless you do double buffering (or use the Edit).



not really...EVERYTHING in Win32 is longer than they should be...i say, nice job micro$oft! (sarcasm)
I did'nt know an Edit control was the most complicated thing. It certainly did require me to think alot to get every coordinate right lol. So I concur, it can be a very complicated thing.

I'm still new to win32 and this was my first big project based upon my current learning. How would I go about using an "real edit control"?

Thanks.
After creating your main window call

CreateWindow( "EDIT", "", WS_CHILD | WS_VISIBLE, x, y, width, height, hwndOfMainWindow, 0, 0, 0 );

To get the text out of it call GetWindowText with the HWND from CreateWindow.

Edit controls are quite difficult with the coordinates, moreso if you go multilined. Then there's the clipboard functionality, (simple) undo, mouse selecting.
It's really way easier to use the build in controls if possible.

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

Thank you very much.. ++ =)
Win32 is an old API and not object-oriented (which is useful for GUIs). As the AA mentionned, it is by nature very complex, and low-level.

If you program directly with Win32, you will have to write huge amounts of code. I mean: just look at the code needed to have one standard window popup... Insane! Modern frameworks like .NET show the same window with a single line of code:
Application.Run(new Form());

Moreover, due to this enourmous quantity of code, if you want to do complex applications (well, even just useful ones), you have to design your application very carefully! Otherwise the sources become very fast overwhelmly complex, and unmaintainable.

The solution is to use Frameworks that are this "GUI layer", that I advised you to carefully design. The well-known one is MFC (made by Ms themselves). It's a C++ object-oriented layer, built on top of Win32. But it's still too complicated IMHO...

There are other alternatives, and I'll let you google it. If you are going to build GUI applications, .NET is really worth a look. It's an excellent framework, with lots of features and very easy to use.

Good luck,
jods
Quote:Original post by jods
Win32 is an old API and not object-oriented (which is useful for GUIs).


Win32 is as object oriented as it gets. What is a Window if not an object? What about all of the GDI objects? Just because it was coded in C doesn't mean it doesn't follow an OO design.
Quote:Original post by Aldacron
Quote:Original post by jods
Win32 is an old API and not object-oriented (which is useful for GUIs).


Win32 is as object oriented as it gets. What is a Window if not an object? What about all of the GDI objects? Just because it was coded in C doesn't mean it doesn't follow an OO design.



That's not really correct. By the same argument, variables and subroutines are "objects" in BASIC, so BASIC is an OO language. This is, of course, patently absurd. You may want to read up on OO design principles and terminology; "object" in OO means something a lot more specific than just "object" in general [wink]

Absolutely nothing in the Win32 API is object oriented. There are no classes, no templates, nothing. It is purely procedurally designed. There are basically three choices for doing OOP in Win32: MFC, ATL, and .Net. All of them have one thing in common: they sit "on top" of the Win32 API and act as an intermediate layer between an OO application and the procedural Win32 API.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

This topic is closed to new replies.

Advertisement