[C++] Win32 letter spacing, letter width, and letter background?

The title pretty much says it all. I have a subclassed editbox, and for it I'm going to need to give each letter a bit of padding, actually, that's partially how the letter spacing/width both come in. I need to put space around the letter so that it's X in width, and I need to do that for all of the letters. Then on each letter I need a background, prefereable with a way to round the corners.

So how can I do this? Is there a way to do it with a subclassed edit control, or am I going to have to create a window, and each time I press a letter, create another window with the desired results? If that's the case, then how do I go about having things like the blinking text cursor, using backspace properly, erasing in the middle of a word (Instead of just at the end) and so on. If I'm going to have to create my own edit control, where to start would be highly appreciated.
All those characteristics you mentioned meesa are characteristics of a particular font. Did anyone ever mention to you about getting a copy of Petzold? All the things you ask here are pretty much covered textbook fashion in his books - especially font and graphics related topics.

For doing extremely non-standard special effects like you are describing my guess is that you'll have to create your own edit control. Doing so just starts with registering a window class in the typical manner, for example, "MyEdit". Then you create a window of the class and if it has focus, it will receive all keystrokes in the wParam. Once you get a keypress you'll be testing them against the VK_XXXXXXXX message constants to see what they are. Then you decide where to output them and at what pixel location in your window. If someone presses the backspace key you decide how far to back up (pixel-wise) in your window, and you need to do whatever you deem necessary in the buffer where you are saving keycodes.

All this presupposes you are good with pointers and buffer minipulation, memory allocation, so on and so forth. If I recall you are a competant coder, so my guess is you have that down.

I build a lot of grid custom controls myself, and sometimes I use some of my homemade edit controls as the grid cells. However, my edit controls are not real advanced. For example, they don't implement copy - paste, inverse selection, and internal scrolling like what happens in a real edit control when the text automatically scrolls to the left when you get to the end of the control. I'd like to take the time to implement those things, but just haven't gotten around to it. If anyone has any code like that that they would be willing to share, I'd really like to see it.

I can't now, but tomorrow I could post a simple edit control example if you would like.


...or am I going to have to create a window, and each time I press a letter, create another window with the desired results?


Not quite sure what you are talking about there. If you create your own edit control, there need'nt be more than one window involved.

About cursors and such there are Windows Api functions for creating, managing, and destroying cursors. They take some careful management at times.
Last edited on
are characteristics of a particular font.


I considered that, but for one user, the I can be a different width than for another. So even a custom font wouldn't work. However, since I would be using a custom control, I would guess that I would want to use TextOut, or some similiar function to write the character, correct? That would take care of the spacing, although I'm not sure exactly how to use the backspace properly. That should be explained by your example code, which I would like to see.


Thanks for all of the help you've given. Now to go start researching cursors. :)
They're actually called 'carets'....

http://msdn.microsoft.com/en-us/library/ff468799(v=vs.85).aspx

I'll post it tomorrow.

That saved me some time. If http://msdn.microsoft.com/en-us/library/ms648398(v=vs.85).aspx is similar to your code, then you may not need to post it. Unless your's is better, which it probably is.
I got side tracked on a number of issues. That's why I didn't get back when I said. I don't know if this code is better, but its a complete working example. I really would like to get back to this sometime soon to improve it. What I did was created a dll that contains the code for a stripped down edit control. So, compile this as a Windows dll project...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
#define   UNICODE
#define   _UNICODE
#include  <windows.h>
#include  <tchar.h>
#include  "WinTypes.h"
#define   BUFFER_LENGTH  40


long fnCellProc_OnCreate(lpWndEventArgs Wea)      // Offset     What's Stored There
{                                                 // =================================================
 int x,cxChar,cyChar;                             // 0  - 3     pBuffer for text typed
 TCHAR* pBuffer;                                  // 4  - 7     cxChar - width of char
 TEXTMETRIC tm;                                   // 8  - 11    cyChar - height of char
 HANDLE hHeap;                                    // 12 - 15    cxClient - width of client area
 HDC hdc;                                         // 16 - 19    cyClient - height of client area
                                                  // 20 - 23    count of chars that fit in cxClient
 hHeap=GetProcessHeap();                          // 24 - 27    caret position in cxClient (zero based)
 pBuffer=(TCHAR*)HeapAlloc
 (
  hHeap,
  HEAP_ZERO_MEMORY,
  40*sizeof(TCHAR)
 );
 for(x=0;x<BUFFER_LENGTH;x++)
     pBuffer[x]=_T(' ');
 SetWindowLong(Wea->hWnd,0,(long)pBuffer);
 hdc=GetDC(Wea->hWnd);
 SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));
 GetTextMetrics(hdc,&tm);
 cxChar=tm.tmAveCharWidth;
 cyChar=tm.tmHeight;
 SetWindowLong(Wea->hWnd,4,cxChar);
 SetWindowLong(Wea->hWnd,8,cyChar);
 ReleaseDC(Wea->hWnd,hdc);

 return 0;
}


long fnCellProc_OnSetFocus(lpWndEventArgs Wea)
{
 int xCaret,cxChar,cyChar;

 xCaret=GetWindowLong(Wea->hWnd,24);
 cxChar=GetWindowLong(Wea->hWnd,4);
 cyChar=GetWindowLong(Wea->hWnd,8);
 CreateCaret(Wea->hWnd,NULL,cxChar,cyChar);
 SetCaretPos(xCaret*cxChar,0);
 ShowCaret(Wea->hWnd);

 return 0;
}


long fnCellProc_OnLButtonDown(lpWndEventArgs Wea)
{
 SetFocus(Wea->hWnd);
 return 0;
}


long fnCellProc_OnSize(lpWndEventArgs Wea)
{
 int cxClient,cyClient,cxBuffer,cxChar,xCaret;

 cxChar=GetWindowLong(Wea->hWnd,4);
 cxClient=LOWORD(Wea->lParam);
 cyClient=HIWORD(Wea->lParam);
 SetWindowLong(Wea->hWnd,12,cxClient);
 SetWindowLong(Wea->hWnd,16,cyClient);
 cxBuffer=cxClient/cxChar;
 SetWindowLong(Wea->hWnd,20,cxBuffer);
 xCaret=0;
 SetWindowLong(Wea->hWnd,24,xCaret);
 if(Wea->hWnd==GetFocus())
    SetCaretPos(xCaret*cxChar,0);

 return 0;
}


long fnCellProc_OnPaint(lpWndEventArgs Wea)
{
 TCHAR* pBuffer=NULL;
 PAINTSTRUCT ps;
 int cxBuffer;
 HDC hdc;
 RECT rc;

 pBuffer=(TCHAR*)GetWindowLong(Wea->hWnd,0);
 cxBuffer=GetWindowLong(Wea->hWnd,20);
 hdc=BeginPaint(Wea->hWnd,&ps);
 SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));
 rc.bottom=18, rc.top=0, rc.left=0, rc.right=320;
 DrawText(hdc,pBuffer,-1,&rc,DT_SINGLELINE);
 EndPaint(Wea->hWnd,&ps);

 return 0;
}



long fnCellProc_OnKeyDown(lpWndEventArgs Wea)
{
 int cxBuffer,cxChar,xCaret;
 TCHAR* pBuffer;
 RECT rc;
 HDC hdc;

 pBuffer=(TCHAR*)GetWindowLong(Wea->hWnd,0);
 cxBuffer=GetWindowLong(Wea->hWnd,20);
 cxChar=GetWindowLong(Wea->hWnd,4);
 xCaret=GetWindowLong(Wea->hWnd,24);
 switch (Wea->wParam)
 {
  case VK_LEFT:
    xCaret=max(xCaret-1,0);
    break;
  case VK_RIGHT:
    xCaret=min(xCaret+1,cxBuffer-1);
    break;
  case VK_DELETE:
    {
       int x;
       for(x=xCaret;x<cxBuffer-1;x++)
           *(pBuffer+x)=*(pBuffer+x+1);
       *(pBuffer+cxBuffer-1)=TEXT(' ');
       HideCaret(Wea->hWnd);
       hdc=GetDC(Wea->hWnd);
       SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));
       rc.bottom=18, rc.top=0, rc.left=0, rc.right=320;
       DrawText(hdc,pBuffer,-1,&rc,DT_SINGLELINE);
       ShowCaret(Wea->hWnd);
       ReleaseDC(Wea->hWnd,hdc);
       break;
    }
 }
 SetCaretPos(xCaret*cxChar,0);
 SetWindowLong(Wea->hWnd,24,xCaret);

 return 0;
}


long fnCellProc_OnChar(lpWndEventArgs Wea)
{
 int cxChar,cxBuffer,xCaret;
 TCHAR *pBuffer;
 RECT rc;
 HDC hdc;

 pBuffer=(TCHAR*)GetWindowLong(Wea->hWnd,0);
 cxChar=GetWindowLong(Wea->hWnd,4);
 cxBuffer=GetWindowLong(Wea->hWnd,20);
 xCaret=GetWindowLong(Wea->hWnd,24);
 switch(Wea->wParam)
 {
  case TEXT('\b'):   // backspace
    if(xCaret==0)
       SendMessage(Wea->hWnd,WM_KEYDOWN,VK_DELETE,1L);
    else
    {
       xCaret-- ;
       SendMessage(Wea->hWnd,WM_KEYDOWN,VK_DELETE,1L);
    }
    break ;
  default:     // character codes
    {
       int i=0;
       HideCaret(Wea->hWnd);
       for(i=BUFFER_LENGTH-2;i>xCaret;i--)
           pBuffer[i]=pBuffer[i-1];
       pBuffer[BUFFER_LENGTH-1]=TEXT('\0');
       *(pBuffer+xCaret)=(TCHAR)Wea->wParam;
       hdc=GetDC(Wea->hWnd);
       SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));
       rc.bottom=18, rc.top=0, rc.left=0, rc.right=320;
       DrawText(hdc,pBuffer,-1,&rc,DT_SINGLELINE);
       ShowCaret(Wea->hWnd),     ReleaseDC(Wea->hWnd,hdc);
       if(xCaret+1<cxBuffer)
          xCaret++;
       break;
    }
 }
 SetCaretPos(xCaret*cxChar,0);
 SetWindowLong(Wea->hWnd,24,xCaret);

 return 0;
}


long fnCellProc_OnKillFocus(lpWndEventArgs Wea)
{
 HideCaret(Wea->hWnd);
 DestroyCaret();

 return 0;
}


long fnCellProc_OnDestroy(lpWndEventArgs Wea)
{
 TCHAR* pBuffer;

 pBuffer=(TCHAR*)GetWindowLong(Wea->hWnd,0);
 HeapFree(GetProcessHeap(),0,pBuffer);
 PostQuitMessage(0);

 return 0;
}


LRESULT CALLBACK fnCellProc(HWND hWnd, unsigned int iMsg, WPARAM wParam, LPARAM lParam)
{
 WndEventArgs Wea;

 for(unsigned int i=0; i<dim(EventHandler); i++)
 {
     if(EventHandler[i].Code==iMsg)
     {
        Wea.hWnd=hWnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(&Wea);
     }
 }

 return (DefWindowProc(hWnd, iMsg, wParam, lParam));
}


extern "C" __declspec(dllexport) ATOM Initialize(HINSTANCE hIns)
{
 TCHAR szClassName[]=TEXT("Cell");
 WNDCLASSEX wc;

 wc.cbSize=sizeof(wc);
 wc.style=CS_HREDRAW | CS_VREDRAW;
 wc.lpfnWndProc=fnCellProc;
 wc.cbClsExtra=0;
 wc.cbWndExtra=28;
 wc.hInstance=hIns;    //GetModuleHandle(0);
 wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
 wc.hCursor=LoadCursor(NULL,IDC_ARROW) ;
 wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
 wc.lpszMenuName=NULL;
 wc.lpszClassName=szClassName;
 wc.hIconSm=0;

 return RegisterClassEx(&wc);
}


Here's the WinTypes.h include...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//WinTypes.h
#ifndef WINTYPES_H
#define WINTYPES_H
#define dim(x) (sizeof(x) / sizeof(x[0]))

typedef struct                     WindowsEventArguments
{
 HWND                              hWnd;
 WPARAM                            wParam;
 LPARAM                            lParam;
 HINSTANCE                         hIns;
}WndEventArgs,                     *lpWndEventArgs;

long fnCellProc_OnCreate           (lpWndEventArgs Wea);
long fnCellProc_OnSetFocus         (lpWndEventArgs Wea);
long fnCellProc_OnLButtonDown      (lpWndEventArgs Wea);
long fnCellProc_OnSize             (lpWndEventArgs Wea);
long fnCellProc_OnPaint            (lpWndEventArgs Wea);
long fnCellProc_OnKeyDown          (lpWndEventArgs Wea);
long fnCellProc_OnChar             (lpWndEventArgs Wea);
long fnCellProc_OnKillFocus        (lpWndEventArgs Wea);
long fnCellProc_OnDestroy          (lpWndEventArgs Wea);

struct EVENTHANDLER
{
 unsigned int                      Code;
 long                              (*fnPtr)(lpWndEventArgs);
};

const EVENTHANDLER EventHandler[]=
{
 {WM_CREATE,                       fnCellProc_OnCreate},
 {WM_SETFOCUS,                     fnCellProc_OnSetFocus},
 {WM_LBUTTONDOWN,                  fnCellProc_OnLButtonDown},
 {WM_SIZE,                         fnCellProc_OnSize},
 {WM_PAINT,                        fnCellProc_OnPaint},
 {WM_KEYDOWN,                      fnCellProc_OnKeyDown},
 {WM_CHAR,                         fnCellProc_OnChar},
 {WM_KILLFOCUS,                    fnCellProc_OnKillFocus},
 {WM_DESTROY,                      fnCellProc_OnDestroy}
};
#endif 
Last edited on
And here is a host app that creates an instance of the "Cell" custom control home made edit box. It just puts one of them on a form with a CreateWindowEx() call, and it puts a button under it that, when clicked, retrieves whatever you typed into the edit control, and displays it in a Message Box. Note my code doesn't statically link to the dll. The dll should compile to dllCell.dll, and that file you need to place in the same directory as this code's exe. My code then does a LoadLibrary() on the dll, and a GetProcAddress() on the Initialize() exported function in the dll, which registers the "Cell" class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#define    UNICODE       //Main.cpp
#define    _UNICODE
#include   <windows.h>
#include   <tchar.h>
#include   <stdio.h>
#include   "WinTypes.h"
#define    IDC_MYEDIT    1500
#define    IDC_BUTTON    1505



long fnWndProc_OnCreate(lpWndEventArgs Wea)
{
 ATOM (*pInit)(HINSTANCE);
 HINSTANCE hDll=0;
 HWND hCtrl;

 Wea->hIns=((LPCREATESTRUCT)Wea->lParam)->hInstance;
 hDll=LoadLibrary(_T("dllCell.dll"));
 if(hDll)
 {
    SetWindowLong(Wea->hWnd,0,(long)hDll);
    pInit=(ATOM (*)(HINSTANCE))GetProcAddress(hDll,"Initialize");
    if(pInit)
    {
       pInit(Wea->hIns);
       hCtrl=CreateWindowEx(WS_EX_CLIENTEDGE,_T("Cell"),_T(""),WS_CHILD|WS_VISIBLE,10,10,325,25,Wea->hWnd,(HMENU)IDC_MYEDIT,Wea->hIns,0);
       hCtrl=CreateWindow(_T("button"),_T("Get Text"),WS_CHILD|WS_VISIBLE,130,50,80,25,Wea->hWnd,(HMENU)IDC_BUTTON,Wea->hIns,0);
    }
 }

 return 0;
}



long fnWndProc_OnCommand(lpWndEventArgs Wea)
{
 if(LOWORD(Wea->wParam)==IDC_BUTTON && HIWORD(Wea->wParam)==BN_CLICKED)
 {
    TCHAR* pBuffer=(TCHAR*)GetWindowLong(GetDlgItem(Wea->hWnd,IDC_MYEDIT),0);
    MessageBox(Wea->hWnd,pBuffer,_T("Here Is What Is In The Buffer"),MB_OK);
 }

 return 0;
}



long fnWndProc_OnClose(lpWndEventArgs Wea)
{
 HINSTANCE hDll=0;

 DestroyWindow(GetDlgItem(Wea->hWnd,IDC_MYEDIT));
 hDll=(HINSTANCE)GetWindowLong(Wea->hWnd,0);
 if(hDll)
    FreeLibrary(hDll);
 DestroyWindow(Wea->hWnd);
 PostQuitMessage(0);

 return 0;
}



LRESULT CALLBACK fnWndProc(HWND hWnd, unsigned int iMsg, WPARAM wParam, LPARAM lParam)
{
 WndEventArgs Wea;

 for(unsigned int i=0; i<dim(EventHandler); i++)
 {
     if(EventHandler[i].Code==iMsg)
     {
        Wea.hWnd=hWnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(&Wea);
     }
 }

 return (DefWindowProc(hWnd, iMsg, wParam, lParam));
}



int WINAPI WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 TCHAR szCaption[]=_T("Home Made Edit Control");
 TCHAR szClassName[]=_T("Form1");
 WNDCLASSEX wc;
 MSG messages;
 HWND hWnd;

 wc.lpszClassName=szClassName;                wc.lpfnWndProc=fnWndProc;
 wc.cbSize=sizeof (WNDCLASSEX);               wc.style=CS_DBLCLKS;
 wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);     wc.hInstance=hIns;
 wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION);  wc.hCursor=LoadCursor(NULL,IDC_ARROW);
 wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;    wc.cbWndExtra=4;
 wc.lpszMenuName=NULL;                        wc.cbClsExtra=0;
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szClassName,szCaption,WS_OVERLAPPEDWINDOW,200,200,355,125,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}


Here's the include...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//WinTypes.h
#ifndef WINTYPES_H
#define WINTYPES_H
#define dim(x) (sizeof(x) / sizeof(x[0]))

typedef struct             WindowsEventArguments
{
 HWND                      hWnd;
 WPARAM                    wParam;
 LPARAM                    lParam;
 HINSTANCE                 hIns;
}WndEventArgs,             *lpWndEventArgs;

long fnWndProc_OnCreate    (lpWndEventArgs Wea);
long fnWndProc_OnCommand   (lpWndEventArgs Wea);
long fnWndProc_OnClose     (lpWndEventArgs Wea);

struct EVENTHANDLER
{
 unsigned int              Code;
 long                      (*fnPtr)(lpWndEventArgs);
};

const EVENTHANDLER EventHandler[]=
{
 {WM_CREATE,               fnWndProc_OnCreate},
 {WM_COMMAND,              fnWndProc_OnCommand},
 {WM_CLOSE,                fnWndProc_OnClose}
};
#endif 
Last edited on
Many thanks. I'll be studying out this code very closly, although I probably won't keep it as a DLL. :)
The above code is code I spent a fair amount of time on three or four years ago to get working; however, its not perfect, and could use some work.

Also, I'd really like to add the following capabilities to it...

1) Copy/Paste;
2) Horizontal scrolling when caret reaches end of control;
3) Inverse video selection (highlighting) when selected.

One and two above I could presently do with what I know. Number three though I'd have to research/learn.

The question arises as to why one would want to do this oneself when the "edit" control built into Windows exists. Well, if one had the code right in one's app one could do things that aren't presently possibe - at least easily. Subclassing is great but only goes so far. I build grids, and it would really help me to accomplish this.

I'll be studying out this code very closly, although I probably won't keep it as a DLL. :)


I feared that. I first created it in one simple app, then put the custom control code in the dll. I'll post the thing with everything in the host. Just give me a minute to find it!
everything in one app...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
#include  <windows.h>
#define   UNICODE
#include  <tchar.h>
#include  "WinTypes.h"
#define   BUFFER_LENGTH    40
#define   IDC_CELL1      1500
#define   IDC_BUTTON     1510

long fnCellProc_OnCreate(lpWndEventArgs Wea)
{
 int x,cxChar,cyChar;
 TEXTMETRIC tm;
 TCHAR* pBuffer;
 HANDLE hHeap;
 HDC hdc;

 hHeap=GetProcessHeap();
 pBuffer=(TCHAR*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,40*sizeof(TCHAR));
 for(x=0;x<BUFFER_LENGTH;x++)
     pBuffer[x]=_T(' ');
 SetWindowLong(Wea->hWnd,0,(long)pBuffer);            //0 -3  pBuffer for text typed
 hdc=GetDC(Wea->hWnd);                                //4 -7  cxChar - width of char
 SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT)); //8 -11 cyChar - height of char
 GetTextMetrics(hdc,&tm);                             //12-15 cxClient - width of client area
 cxChar=tm.tmAveCharWidth;                            //16-19 cyClient - height of client area
 cyChar=tm.tmHeight;                                  //20-23 count of chars that fit in cxClient
 SetWindowLong(Wea->hWnd,4,cxChar);                   //24-27 caret position in cxClient (zero based)
 SetWindowLong(Wea->hWnd,8,cyChar);
 ReleaseDC(Wea->hWnd,hdc);

 return 0;
}


long fnCellProc_OnSetFocus(lpWndEventArgs Wea)
{
 int xCaret,cxChar,cyChar;

 xCaret=GetWindowLong(Wea->hWnd,24);
 cxChar=GetWindowLong(Wea->hWnd,4);
 cyChar=GetWindowLong(Wea->hWnd,8);
 CreateCaret(Wea->hWnd,NULL,cxChar,cyChar);
 SetCaretPos(xCaret*cxChar,0);
 ShowCaret(Wea->hWnd);

 return 0;
}


long fnCellProc_OnLButtonDown(lpWndEventArgs Wea)
{
 SetFocus(Wea->hWnd);
 return 0;
}


long fnCellProc_OnSize(lpWndEventArgs Wea)
{
 int cxClient,cyClient,cxBuffer,cxChar,xCaret;

 cxChar=GetWindowLong(Wea->hWnd,4);
 cxClient=LOWORD(Wea->lParam);
 cyClient=HIWORD(Wea->lParam);
 SetWindowLong(Wea->hWnd,12,cxClient);
 SetWindowLong(Wea->hWnd,16,cyClient);
 cxBuffer=cxClient/cxChar;
 SetWindowLong(Wea->hWnd,20,cxBuffer);
 xCaret=0;
 SetWindowLong(Wea->hWnd,24,xCaret);
 if(Wea->hWnd==GetFocus())
    SetCaretPos(xCaret*cxChar,0);

 return 0;
}


long fnCellProc_OnPaint(lpWndEventArgs Wea)
{
 TCHAR* pBuffer=NULL;
 PAINTSTRUCT ps;
 int cxBuffer;
 HDC hdc;
 RECT rc;

 pBuffer=(TCHAR*)GetWindowLong(Wea->hWnd,0);
 cxBuffer=GetWindowLong(Wea->hWnd,20);
 hdc=BeginPaint(Wea->hWnd,&ps);
 SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));
 rc.bottom=18, rc.top=0, rc.left=0, rc.right=320;
 DrawText(hdc,pBuffer,-1,&rc,DT_SINGLELINE);
 EndPaint(Wea->hWnd,&ps);

 return 0;
}


long fnCellProc_OnKeyDown(lpWndEventArgs Wea)
{
 int cxBuffer,cxChar,xCaret;
 TCHAR* pBuffer;
 RECT rc;
 HDC hdc;

 pBuffer=(TCHAR*)GetWindowLong(Wea->hWnd,0);
 cxBuffer=GetWindowLong(Wea->hWnd,20);
 cxChar=GetWindowLong(Wea->hWnd,4);
 xCaret=GetWindowLong(Wea->hWnd,24);
 switch (Wea->wParam)
 {
  case VK_LEFT:
    xCaret=max(xCaret-1,0);
    break;
  case VK_RIGHT:
    xCaret=min(xCaret+1,cxBuffer-1);
    break;
  case VK_DELETE:
    {
       int x;
       for(x=xCaret;x<cxBuffer-1;x++)
           *(pBuffer+x)=*(pBuffer+x+1);
       *(pBuffer+cxBuffer-1)=TEXT(' ');
       HideCaret(Wea->hWnd);
       hdc=GetDC(Wea->hWnd);
       SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));
       rc.bottom=18, rc.top=0, rc.left=0, rc.right=320;
       DrawText(hdc,pBuffer,-1,&rc,DT_SINGLELINE);
       ShowCaret(Wea->hWnd);
       ReleaseDC(Wea->hWnd,hdc);
       break;
    }
 }
 SetCaretPos(xCaret*cxChar,0);
 SetWindowLong(Wea->hWnd,24,xCaret);

 return 0;
}


long fnCellProc_OnChar(lpWndEventArgs Wea)
{
 int cxChar,cxBuffer,xCaret;
 TCHAR *pBuffer;
 RECT rc;
 HDC hdc;

 pBuffer=(TCHAR*)GetWindowLong(Wea->hWnd,0);
 cxChar=GetWindowLong(Wea->hWnd,4);
 cxBuffer=GetWindowLong(Wea->hWnd,20);
 xCaret=GetWindowLong(Wea->hWnd,24);
 switch(Wea->wParam)
 {
  case TEXT('\b'):   // backspace
    if(xCaret==0)
       SendMessage(Wea->hWnd,WM_KEYDOWN,VK_DELETE,1L);
    else
    {
       xCaret-- ;
       SendMessage(Wea->hWnd,WM_KEYDOWN,VK_DELETE,1L);
    }
    break ;
  default:     // character codes
    {
       int i=0;
       HideCaret(Wea->hWnd);
       for(i=BUFFER_LENGTH-2;i>xCaret;i--)
           pBuffer[i]=pBuffer[i-1];
       pBuffer[BUFFER_LENGTH-1]=TEXT('\0');
       *(pBuffer+xCaret)=(TCHAR)Wea->wParam;
       hdc=GetDC(Wea->hWnd);
       SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));
       rc.bottom=18, rc.top=0, rc.left=0, rc.right=320;
       DrawText(hdc,pBuffer,-1,&rc,DT_SINGLELINE);
       ShowCaret(Wea->hWnd);
       ReleaseDC(Wea->hWnd,hdc);
       if(xCaret+1<cxBuffer)
          xCaret++;
       break;
    }
 }
 SetCaretPos(xCaret*cxChar,0);
 SetWindowLong(Wea->hWnd,24,xCaret);

 return 0;
}


long fnCellProc_OnKillFocus(lpWndEventArgs Wea)
{
 HideCaret(Wea->hWnd);
 DestroyCaret();

 return 0;
}


long fnCellProc_OnDestroy(lpWndEventArgs Wea)
{
 TCHAR* pBuffer;

 pBuffer=(TCHAR*)GetWindowLong(Wea->hWnd,0);
 HeapFree(GetProcessHeap(),0,pBuffer);
 PostQuitMessage(0);

 return 0;
}


LRESULT CALLBACK fnCellProc(HWND hWnd, unsigned int iMsg, WPARAM wParam, LPARAM lParam)
{
 WndEventArgs Wea;

 for(unsigned int i=0; i<dim(EventHandler); i++)
 {
     if(EventHandler[i].Code==iMsg)
     {
        Wea.hWnd=hWnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(&Wea);
     }
 }

 return (DefWindowProc(hWnd, iMsg, wParam, lParam));
}


LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
 switch (msg)
 {
  case WM_CREATE:
    {
       WNDCLASSEX wc;
       TCHAR szClassName[]=TEXT("Cell");
       HWND hCell,hButton;
       wc.cbSize=sizeof(wc);
       wc.style=CS_HREDRAW | CS_VREDRAW;
       wc.lpfnWndProc=fnCellProc;
       wc.cbClsExtra=0;
       wc.cbWndExtra=28;
       wc.hInstance=((LPCREATESTRUCT)lParam)->hInstance;
       wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
       wc.hCursor=LoadCursor(NULL,IDC_ARROW) ;
       wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
       wc.lpszMenuName=NULL;
       wc.lpszClassName=szClassName;
       wc.hIconSm=0;
       RegisterClassEx(&wc);
       hCell=CreateWindowEx(WS_EX_CLIENTEDGE,szClassName,_T(""),WS_CHILD|WS_VISIBLE|WS_BORDER,10,10,320,22,hwnd,(HMENU)IDC_CELL1,wc.hInstance,0);
       hButton=CreateWindow(_T("button"),_T("Get Text"),WS_CHILD|WS_VISIBLE,135,45,80,25,hwnd,(HMENU)IDC_BUTTON,wc.hInstance,0);
       SetWindowLong(hwnd,0,(long)hCell);
       SetWindowLong(hCell,28,(long)40);
       return 0;
    }
  case WM_COMMAND:
    if(LOWORD(wParam)==IDC_BUTTON && HIWORD(wParam)==BN_CLICKED)
    {
       TCHAR* pBuffer=(TCHAR*)GetWindowLong(GetDlgItem(hwnd,IDC_CELL1),0);
       MessageBox(hwnd,pBuffer,_T("Here Is What Is In The Buffer"),MB_OK);
    }
    return 0;
  case WM_DESTROY:
    DestroyWindow(GetDlgItem(hwnd,IDC_CELL1));
    PostQuitMessage (0);
    return 0;
 }

 return DefWindowProc(hwnd,msg,wParam,lParam);
}

int WINAPI WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 TCHAR szCaption[]=_T("Home-Made Edit Control");
 TCHAR szClassName[]=_T("Main");
 WNDCLASSEX wc;
 MSG messages;
 HWND hWnd;

 wc.lpszClassName=szClassName,               wc.lpfnWndProc=WndProc;
 wc.hInstance=hIns,                          wc.style=0;
 wc.cbSize=sizeof (WNDCLASSEX),              wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
 wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION), wc.hCursor=LoadCursor(NULL,IDC_ARROW);
 wc.lpszMenuName=NULL,                       wc.cbClsExtra=0;
 wc.cbWndExtra=4,                            wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;
 RegisterClassEx(&wc);
 hWnd=CreateWindow(szClassName,szCaption,WS_OVERLAPPEDWINDOW,375,300,350,120,0,0,hIns,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}


compiled with Code::Blocks ( mingw )
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//WinTypes.h
#ifndef WINTYPES_H
#define WINTYPES_H
#define dim(x) (sizeof(x) / sizeof(x[0]))

typedef struct                     WindowsEventArguments
{
 HWND                              hWnd;
 WPARAM                            wParam;
 LPARAM                            lParam;
 HINSTANCE                         hIns;
}WndEventArgs,                     *lpWndEventArgs;

long fnCellProc_OnCreate           (lpWndEventArgs Wea);
long fnCellProc_OnSetFocus         (lpWndEventArgs Wea);
long fnCellProc_OnLButtonDown      (lpWndEventArgs Wea);
long fnCellProc_OnSize             (lpWndEventArgs Wea);
long fnCellProc_OnPaint            (lpWndEventArgs Wea);
long fnCellProc_OnKeyDown          (lpWndEventArgs Wea);
long fnCellProc_OnChar             (lpWndEventArgs Wea);
long fnCellProc_OnKillFocus        (lpWndEventArgs Wea);
long fnCellProc_OnDestroy          (lpWndEventArgs Wea);

struct EVENTHANDLER
{
 unsigned int                      Code;
 long                              (*fnPtr)(lpWndEventArgs);
};

const EVENTHANDLER EventHandler[]=
{
 {WM_CREATE,                       fnCellProc_OnCreate},
 {WM_SETFOCUS,                     fnCellProc_OnSetFocus},
 {WM_LBUTTONDOWN,                  fnCellProc_OnLButtonDown},
 {WM_SIZE,                         fnCellProc_OnSize},
 {WM_PAINT,                        fnCellProc_OnPaint},
 {WM_KEYDOWN,                      fnCellProc_OnKeyDown},
 {WM_CHAR,                         fnCellProc_OnChar},
 {WM_KILLFOCUS,                    fnCellProc_OnKillFocus},
 {WM_DESTROY,                      fnCellProc_OnDestroy}
};
#endif 
Ahh, thank you very much.

Two quick questions (I hope)

In CSS (Web design) there's an attribute known as letter-spacing, which does just what it says, how can I do that same principal? I'll be needing to to make each letting have the corect padding to make it it's proper width. What I'm going for is to make say the A, B, C, and D 4" while the I may be 1.875" and the L 2.25" Due to the fact that I'm not working with a monospace font, I can't just set the text background, also I plan on having rounded corners, which means each letter must be separate.

That brings me to my second question, is there a way to delete something outputted by BitBlt or some other image function?

Got to love that there's more issues getting it to look right than with the actual core. :)
I used SYSTEM_FIXED_FONT in the example above to simplify things. When you get into non-fixed width fonts things become complicated rapidly. For what you want you'll be needing to create your own fonts with CreateFont() or CreateFontIndirect(). As I mentioned you need to study Petzold to get a handle on these things as they are rather involved, and Petzold is pretty much the graphics expert.

I can't help you with the bitblit question as, while I'm aware of it and what it does, I have no experience with it. I'm not really a graphics type person; more into GUI design for data entry and databases, and number crunching. I don't do anything with the web.
Topic archived. No new replies allowed.