I spent one day coding my own custom edit control - it works well, so I would like to share my experience here, maybe this code could be useful for someone ... Since custom drawing a common edit control impossible ( see here ), you must write your own control for editing. The main steps:
// global vars int select; // current selection position int cursor; // current cursor position HWND parent; // parent window wchar_t buf[MAXINPUTBUF]; // edit buffer WNDPROC oldproc; // old window procedure // create custom control window hWnd = CreateWindowW(L"static", NULL, WS_CHILD | WS_TABSTOP | SS_LEFT | SS_NOTIFY, 0, 0, 0, 0, parent, NULL, (HINSTANCE)GetWindowLongPtr(parent, GWL_HINSTANCE), NULL); // todo: use SetProp() to store all global vars oldproc = (WNDPROC)SetWindowLongPtrW(hWnd, GWL_WNDPROC, (LONG_PTR)InputWndProc); SetWindowPos(hWnd, HWND_TOP, x, y, cx, cy, 0);
How to display keyboard input is described here . My window procedure is as follows
LRESULT CALLBACK InputWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_LBUTTONDOWN: //SetFocus(hWnd); PostMessageW(GetParent(hWnd), WM_NEXTDLGCTL, (WPARAM)hWnd, TRUE); break; case WM_KILLFOCUS: HideCaret(hWnd); DestroyCaret(); break; case WM_SETFOCUS: { RECT r; GetClientRect(hWnd, &r); // Create a solid black caret. CreateCaret(hWnd, (HBITMAP) NULL, 2, r.bottom-r.top); ShowCaret(hWnd); InputWndRedraw(hWnd); } return FALSE; case WM_GETDLGCODE: return DLGC_WANTALLKEYS | DLGC_WANTARROWS; case WM_KEYDOWN: { switch (wParam) { case 'V': if (0x8000 & GetKeyState(VK_CONTROL)) { HANDLE h; wchar_t *cb; int len,slen; InputWndDelete(hWnd); OpenClipboard(NULL); h = GetClipboardData(CF_UNICODETEXT); cb = (wchar_t*)GlobalLock(h); if (cb) { memcpy(buf+(cursor+len)*sizeof(wchar_t), buf+cursor*sizeof(wchar_t), (slen-cursor)*sizeof(wchar_t)); memcpy(buf+cursor*sizeof(wchar_t), cb, len*sizeof(wchar_t)); } GlobalUnlock(h); CloseClipboard(); InputWndRedraw(hWnd); } break; case VK_RIGHT: if (cursor-1 >= MAXINPUTBUF || cursor >= (int)wcslen(buf)) break; cursor++; if (!(GetKeyState(VK_SHIFT) & 0x8000)) select = cursor; InputWndRedraw(hWnd); break; case VK_TAB: PostMessageW(GetParent(hWnd), WM_NEXTDLGCTL, GetKeyState(VK_SHIFT) & 0x8000, FALSE); break; case VK_LEFT: if (cursor <= 0) break; cursor--; if (!(GetKeyState(VK_SHIFT) & 0x8000)) select = cursor; InputWndRedraw(hWnd); break; case VK_HOME: cursor = 0; if (!(GetKeyState(VK_SHIFT) & 0x8000)) select = cursor; InputWndRedraw(hWnd); break; case VK_END: cursor = wcslen(buf); if (!(GetKeyState(VK_SHIFT) & 0x8000)) select = cursor; InputWndRedraw(hWnd); break; case VK_DELETE: if (cursor >= (int)wcslen(buf)) { InputWndDelete(hWnd); InputWndRedraw(hWnd); break; } if (select == cursor) select ++; InputWndDelete(hWnd); InputWndRedraw(hWnd); break; case VK_BACK: if (cursor <= 0) { InputWndDelete(hWnd); InputWndRedraw(hWnd); break; } if (select == cursor) cursor --; InputWndDelete(hWnd); InputWndRedraw(hWnd); } } break; case WM_CHAR: if (wParam < VK_SPACE) break; InputWndDelete(hWnd); if (wcslen(buf)+1 < MAXINPUTBUF) { wmemmove(buf+(cursor+1)*sizeof(wchar_t), buf+cursor*sizeof(wchar_t), wcslen(s->buf)-cursor); buf[cursor] = wParam; cursor++; select = cursor; } InputWndRedraw(hWnd); break; case WM_ERASEBKGND: // no flickering return TRUE; case WM_PAINT: { HDC dc; PAINTSTRUCT paint; dc = BeginPaint(hWnd, &paint); InputWndDraw(hWnd, dc); EndPaint(hWnd, &paint); } return TRUE; } return CallWindowProcW(oldproc, hWnd, msg, wParam, lParam); }
Delete the currently selected text (from selection to cursor).
void InputWndDelete(HWND hWnd) { int len; len = wcslen(buf); if (select > cursor) { memcpy(buf+cursor*sizeof(wchar_t), buf+select*sizeof(wchar_t), (len - select)*sizeof(wchar_t)); ZeroMemory(buf+(len-select+cursor)*sizeof(wchar_t), (MAXINPUTBUF-len+select-cursor)*sizeof(wchar_t)); select = cursor; } else if (select < cursor) { memcpy(buf+select*sizeof(wchar_t), buf+cursor*sizeof(wchar_t), (len - cursor)*sizeof(wchar_t)); ZeroMemory(buf+(len-cursor+select)*sizeof(wchar_t), (MAXINPUTBUF-len+cursor-select)*sizeof(wchar_t)); cursor = select; } else { select = cursor; } }
Draw a window on a DC window
void InputWndRedraw(HWND hWnd) { HDC hdc; HideCaret(hWnd); hdc = GetDC(hWnd); InputWndDraw(hWnd, hdc); ReleaseDC(hWnd, hdc); ShowCaret(hWnd); }
Draw an input buffer (buf *) in the device context. Syntax highlighting and other formatting features go here ...
void InputWndDraw(HWND hWnd, HDC hdc) { RECT r,cr; GetClientRect(hWnd, &cr); // draw selected rectangle FillRect()... CopyRect(&r,&cr); DrawTextW(hdc, buf, -1, &r, DT_LEFT | DT_TOP); if (cursor) DrawTextW(hdc, buf, cursor, &r, DT_LEFT | DT_TOP | DT_CALCRECT); else r.right = cr.left; if (GetFocus() == hWnd) { if (r.right > cr.right) SetCaretPos(cr.right, cr.top); else SetCaretPos(r.right, cr.top); } }