Custom edit control win32 - c++

I finally managed to get my syntax highlighting done using richedit and iczelion's tutorials.
Now that i find it, it certainly is not fast enough. I am thinking of taking this one step ahead: a custom edit control. But i do not know how to go about it. Could you guys tell me how to go about it? Give me some info to start on? Maybe even some tutorial or suggest some book?
Now I'm not asking for you guys to spell it out for me, just something to start on. I will be using C++/ASM/Win32 API for this. I'm sure many of you have already made custom edit controls befores, so may be you could even share your experience.
Thanks,
Devjeet

I spent one day on coding my own custom edit control - it's working well, so I would like to share my experience here, maybe for someone this code might be helpful... Because custom draw an common edit control is impossible (see here), you must write your own edit control. The general steps are:
// 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 looks 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 current selected text (from select 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 window on window DC
void InputWndRedraw(HWND hWnd)
{
HDC hdc;
HideCaret(hWnd);
hdc = GetDC(hWnd);
InputWndDraw(hWnd, hdc);
ReleaseDC(hWnd, hdc);
ShowCaret(hWnd);
}
Draw input buffer (buf*) on device context. Syntax highlighting and other formatting features goes 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);
}
}

Related

Scrollbar duplicate winapi c++

I am making a scrollbar for the first time and don't understand why I have a duplicate of my scrollbar when content is scrollable.
Here is how it looks, I don't know if you can see it good enough but here's a screenshot; one scrollbar is the thick gray one (the one a want to keep, looks more like the classic scrollbar), and one is a little gray rectangle to the left of it. Both of these scrollbars work and scroll my program.
:
But when content is not scrollable then it looks like this:
Here is my ScrollBar.cpp if this will help:
#include "ScrollBar.h"
ScrollBar::ScrollBar(){}
ScrollBar::~ScrollBar(){
RemoveWindowSubclass(hWnd, scrollProc, IDC_STATIC);
delete con;
}
ScrollBar::ScrollBar(HWND hWnd, HINSTANCE hInst) : hWnd(hWnd),hInst(hInst) {
this->hWndParent = GetParent(hWnd);
con = new Controls(hWndParent);
}
void ScrollBar::createScroll(int x, int y, int w, int h, bool stickRight, bool stickBottom) {
this->x = x;
this->y = y;
this->width = w;
this->height = h;
hScroll = con->create("scrollbar", WS_VISIBLE | WS_CHILD | SBS_VERT, x, y, width, height,"scrollBar","",0, stickRight, stickBottom);
//Creates like this
/// CreateWindowEx(0, type, text, styles, cData.x, cData.y, cData.width, cData.height, hWnd, (HMENU)id, NULL, (LPVOID)lParam);
SetWindowSubclass(hScroll, scrollProc, IDC_STATIC, (DWORD_PTR)this);
SetWindowSubclass(hWndParent, scrollProc, IDC_STATIC, (DWORD_PTR)this);
con->setEventListener();
}
void ScrollBar::setScrollRange(int scrollMaxHeight) {
scrollMaxH = scrollMaxHeight == -1 ? scrollMaxH : scrollMaxHeight;
SCROLLINFO si = { 0 };
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE;
si.nMin = 0;
si.nMax = scrollMaxH;
SetScrollInfo(hScroll, SB_VERT, &si, false);
}
void ScrollBar::setScrollSize() {
SCROLLINFO oSi = { 0 };
oSi.cbSize = sizeof(oSi);
oSi.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;
GetScrollInfo(hScroll, SB_VERT, &oSi);
RECT rect = GetLocalCoordinates(hWnd);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
SCROLLINFO si = { 0 };
si.cbSize = sizeof(si);
si.fMask = SIF_PAGE;
si.nPage = height;
SetScrollInfo(hScroll, SB_VERT, &si, TRUE);
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;
GetScrollInfo(hScroll, SB_VERT, &si);
int res = oSi.nPos - si.nPos;
if (res > 0) {
setChildScrollOffset(hWnd, res);
}
}
void ScrollBar::handleVScroll(int action,int thum) {
int nPos;
int nOldPos;
SCROLLINFO si = { 0 };
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;
GetScrollInfo(hScroll, SB_VERT, &si);
nOldPos = si.nPos;
switch (action)
{
case SB_TOP:
nPos = si.nMin;
break;
case SB_BOTTOM:
nPos = si.nMax;
break;
case SB_LINEUP:
nPos = si.nPos - 30;
break;
case SB_LINEDOWN:
nPos = si.nPos + 30;
break;
case SB_PAGEUP:
nPos = si.nPos - 30;
break;
case SB_PAGEDOWN:
nPos = si.nPos + 30;
break;
case SB_THUMBTRACK:
nPos = thum;
break;
default:
nPos = si.nPos;
break;
}
SetScrollPos(hScroll, SB_VERT, nPos, TRUE);
nPos = GetScrollPos(hScroll, SB_VERT);
if (nOldPos != nPos) {
ScrollWindowEx(hWnd, 0, nOldPos - nPos, NULL, NULL, NULL, NULL,
SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN);
UpdateWindow(hWnd);
setChildScrollOffset(hWnd, nOldPos - nPos);
}
}
void ScrollBar::setChildScrollOffset(HWND h, int offset) {
Controls* childsCon = reinterpret_cast<Controls*>(GetWindowLongPtr(h, GWLP_USERDATA));
for (auto it : childsCon->sControls)
{
childsCon->getControlItem(it.first)->y += offset;
}
}
LRESULT CALLBACK ScrollBar::scrollProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
ScrollBar* lpData = (ScrollBar*)dwRefData;
HDC hdc = (HDC)wParam;
PAINTSTRUCT ps;
HBRUSH hbrBackground;
switch (uMsg) {
case WM_SIZE:
{
lpData->setScrollSize();
break;
}
case WM_VSCROLL:
{
lpData->handleVScroll(LOWORD(wParam), HIWORD(wParam));
return true;
}
case WM_MOUSEWHEEL:
{
short zDelta = (short)(0xFFFF & (wParam >> 16));
if (zDelta > 0)
PostMessage(lpData->hScroll, WM_VSCROLL, SB_LINEUP, NULL);
else
PostMessage(lpData->hScroll, WM_VSCROLL, SB_LINEDOWN, NULL);
return 0;
}
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
I am blind and so can't see your screen shots but does your parent window have WS_VSCROLL? (you don't show the styles associated with that window). If so remove it, the WS_VSCROLL adds a scrollbar that is not control-based to the window's non-client area.
You need to distinguish between Standard Scroll Bars and Scroll Bar Controls. A scroll bar control is a control window that belongs to the SCROLLBAR window class.
For more details about how to Create Scroll Bar, I suggest you could refer to the Doc: How to Create Scroll Bars
According to the code, you want to create a Scroll Bar Controls. So you should creates a window without standard horizontal and vertical scroll bars and then use the CreateWindowEx function to create a scroll bar by specifying the SCROLLBAR window class.
Could you please show a minimal, reproducible sample?

how to scroll a painted region?

I want to scroll a painted region. I use the CS_OWNDC style with WS_HSCROLL | WS_VSCROLL.My WM_PAINT block draws a line outgoing from the visible region (I suspect that I need to increase the buffer somehow).How can I make this?Please give me an example
PS. Block of code WM_HSCROLL incorrect working i don't know how to fix this.
My code:
// C++ WINAPI LEARN
#include <Windows.h>
WNDCLASS windowClass;
HDC hdc;
PAINTSTRUCT ps;
int hScroll, vScroll;
bool first = true;
LRESULT CALLBACK WndProc(HWND hWindow, UINT typeMessage, WPARAM wParam, LPARAM lParam)
{
switch (typeMessage)
{
case WM_PAINT:
hdc = BeginPaint(hWindow, &ps);
//HERE ANYTHING DRAW CODE (words, line any figure)
//(draw line out visible region)
if (first == true)
{
first = false;
MoveToEx(hdc, 5, 5, NULL);
LineTo(hdc, 900, 500);
}
EndPaint(hWindow, &ps);
break;
case WM_HSCROLL:
//SCOLLBAR NOT JOB!
if (LOWORD(wParam) == SB_LINERIGHT) // this block not job!!!
{
hScroll++;
}
else if (LOWORD(wParam) == SB_LINELEFT) // this block not job!!!
{
hScroll--;
}
else if (LOWORD(wParam) == SB_THUMBTRACK)
{
hScroll=HIWORD(wParam);
}
SetScrollPos(hWindow, SB_HORZ, hScroll, FALSE);
ScrollWindow(hWindow, hScroll, vScroll, NULL, NULL);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
}
return DefWindowProc(hWindow, typeMessage, wParam, lParam);
}
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstanse, LPSTR LpszCmdParam, int nCmdShow)
{
//CREATE WINDOW
windowClass = { 0 };
windowClass.lpszClassName = "ScrollTest";
windowClass.hInstance = hInstance;
windowClass.lpfnWndProc = WndProc;
windowClass.style = CS_OWNDC;
//REGISTER CLASS
RegisterClass(&windowClass);
HWND myWindow = ::CreateWindowEx(
0,
"ScrollTest",
"Scroll Paint",
WS_VISIBLE | WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
100, 100,
500, 500,
NULL,
0,
hInstance,
0
);
//TRANSLATE MESSAGE
for (MSG windowMessage; GetMessage(&windowMessage, NULL, 0, 0);)
{
DispatchMessage(&windowMessage); //TRANSLATE MESSAGES
TranslateMessage(&windowMessage); //TRANSLATE KEYS
}
}
Based on the demo, I modified part of the code and it works normally.
Some code:
LRESULT CALLBACK MyTextWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
TEXTMETRIC tm;
SCROLLINFO si;
// These variables are required to display text.
static int xChar; // horizontal scrolling unit
static int yChar; // vertical scrolling unit
static int xPos; // current horizontal scrolling position
static int yPos; // current vertical scrolling position
int x, y; // horizontal and vertical coordinates
// Create an array of lines to display.
switch (uMsg)
{
case WM_CREATE:
// Get the handle to the client area's device context.
hdc = GetDC(hwnd);
// Extract font dimensions from the text metrics.
GetTextMetrics(hdc, &tm);
xChar = tm.tmAveCharWidth;
yChar = tm.tmHeight + tm.tmExternalLeading;
// Free the device context.
ReleaseDC(hwnd, hdc);
return 0;
case WM_HSCROLL:
// Get all the vertial scroll bar information.
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
// Save the position for comparison later on.
GetScrollInfo(hwnd, SB_HORZ, &si);
xPos = si.nPos;
switch (LOWORD(wParam))
{
// User clicked the left arrow.
case SB_LINELEFT:
si.nPos -= 1;
break;
// User clicked the right arrow.
case SB_LINERIGHT:
si.nPos += 1;
break;
// User clicked the scroll bar shaft left of the scroll box.
case SB_PAGELEFT:
si.nPos -= si.nPage;
break;
// User clicked the scroll bar shaft right of the scroll box.
case SB_PAGERIGHT:
si.nPos += si.nPage;
break;
// User dragged the scroll box.
case SB_THUMBTRACK:
si.nPos = si.nTrackPos;
break;
default:
break;
}
// Set the position and then retrieve it. Due to adjustments
// by Windows it may not be the same as the value set.
si.fMask = SIF_POS;
SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
GetScrollInfo(hwnd, SB_HORZ, &si);
// If the position has changed, scroll the window.
if (si.nPos != xPos)
{
ScrollWindow(hwnd, xChar * (xPos - si.nPos), 0, NULL, NULL);
}
return 0;
case WM_VSCROLL:
// Get all the vertial scroll bar information.
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
GetScrollInfo(hwnd, SB_VERT, &si);
// Save the position for comparison later on.
yPos = si.nPos;
switch (LOWORD(wParam))
{
// User clicked the HOME keyboard key.
case SB_TOP:
si.nPos = si.nMin;
break;
// User clicked the END keyboard key.
case SB_BOTTOM:
si.nPos = si.nMax;
break;
// User clicked the top arrow.
case SB_LINEUP:
si.nPos -= 1;
break;
// User clicked the bottom arrow.
case SB_LINEDOWN:
si.nPos += 1;
break;
// User clicked the scroll bar shaft above the scroll box.
case SB_PAGEUP:
si.nPos -= si.nPage;
break;
// User clicked the scroll bar shaft below the scroll box.
case SB_PAGEDOWN:
si.nPos += si.nPage;
break;
// User dragged the scroll box.
case SB_THUMBTRACK:
si.nPos = si.nTrackPos;
break;
default:
break;
}
// Set the position and then retrieve it. Due to adjustments
// by Windows it may not be the same as the value set.
si.fMask = SIF_POS;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
GetScrollInfo(hwnd, SB_VERT, &si);
// If the position has changed, scroll window and update it.
if (si.nPos != yPos)
{
ScrollWindow(hwnd, 0, yChar * (yPos - si.nPos), NULL, NULL);
UpdateWindow(hwnd);
}
return 0;
case WM_PAINT:
// Prepare the window for painting.
hdc = BeginPaint(hwnd, &ps);
// Get vertical scroll bar position.
si.cbSize = sizeof(si);
si.fMask = SIF_POS;
GetScrollInfo(hwnd, SB_VERT, &si);
yPos = si.nPos;
// Get horizontal scroll bar position.
GetScrollInfo(hwnd, SB_HORZ, &si);
xPos = si.nPos;
x = xChar * (1 - xPos);
y = yChar * (1 - yPos);
MoveToEx(hdc, 5 + x, 5 + y, NULL);
LineTo(hdc, 900 + x, 500 + y);
// Indicate that painting is finished.
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Debug:

Updating camera movement with keys not working

I'm having a problem with updating camera's position based on keys pressed. Simply put, I'm trying to get the camera to move LEFT, RIGHT, FORWARD, BACKWARD using the arrow keys. When the program runs, the camera starts in the correct position, furthermore, when a key is pushed, the program registers that the key has been pressed, but the camera doesn't update accordingly.
If anything, I see the problem as the actions aren't being performed within the else statement of the loop (where my gyrating cube resides). My biggest obstacle is to get the value of the key pressed into the loop so I can update the camera position. Any ideas or suggestions as to where to look next?
Here is the code as follows:
Render.cpp
Render *renderDraw = new Render(renderWinWidth, renderWinHeight);
Camera *camera = new Camera();
void Render::renderScreen(Camera *cam)
{
...
gluLookAt(cam->getCameraX(), cam->getCameraY(), cam->getCameraZ(),
objPos[0], objPos[1], objPos[2], VECTOR_UP[0], VECTOR_UP[1], VECTOR_UP[2]);
...
glFlush();
}
Camera.cpp
void Camera::setkeyPressed(WPARAM wParam, HWND hWnd, bool isPressed)
{
keyPressed[wParam] = isPressed;
switch(wParam)
{
case VK_UP:
//MessageBox(hWnd, L"key up", L"Key pressed", MB_OK | MB_ICONASTERISK);
newPos_ = getCameraX();
newPos_ -= 2.0f;
setCameraX(newPos_);
break;
case VK_DOWN:
//MessageBox(hWnd, L"key down", L"Key pressed", MB_OK | MB_ICONASTERISK);
newPos_ = getCameraX();
newPos_ += 2.0f;
setCameraX(newPos_);
break;
case VK_LEFT:
//MessageBox(hWnd, L"key left", L"Key pressed", MB_OK | MB_ICONASTERISK);
newPos_ = getCameraZ();
newPos_ += 2.0f;
setCameraZ(newPos_);
break;
case VK_RIGHT:
//MessageBox(hWnd, L"key right", L"Key pressed", MB_OK | MB_ICONASTERISK);
newPos_ = getCameraZ();
newPos_ -= 2.0f;
setCameraZ(newPos_);
break;
}
}
main.cpp
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
...
// Main Loop
while (isRunning)
{
if (PeekMessage (&msg, hWnd, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
isRunning = false;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
renderDraw->renderScreen(camera); <-Contains gyrating cube
SwapBuffers(*hDC);
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
....
switch (message)
{
case WM_KEYDOWN:
switch (wParam)
{
camera->setkeyPressed(wParam, hWndChild, true);
break;
}
break;
....
}
}
Are you processing your key events in the while loop? And isn't the while loop getting all the processor time without allowing your application to listen to key presses?
try this and render your scene when you are not listening to keys
while( uMsg.message != WM_QUIT )
{
if( PeekMessage( &uMsg, NULL, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &uMsg );
DispatchMessage( &uMsg );
}
else
render();
}
Look at the directX examples only to see how they process the key events in the messsage loop. The logic should be the same for openGL.
The change in camera position isn't applied until you render your scene, so after you update your camera in Camera::setKeyPressed you need to force a redraw. You should be able to do this by calling InvalidateRect() at the end of ::setkeyPressed

Client Area Flickers when Drawing a Line

The problem I am having is that when I draw my line, the screen flickers every time it redraws. I just can't quite figure out how to not make it flicker. I understand the flicker is coming from me redrawing the client area hundreds of times a second as I move my mouse with my left button down but how would I be able to get around this?
LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
static HDC hdc;
PAINTSTRUCT ps;
RECT rect;
RECT size;
static POINT point1;
static POINT point2;
static HBRUSH origBrush;
static bool drawingLine = false;
switch (message)
{
case WM_CREATE:
origBrush = CreateSolidBrush(RGB(0, 0 , 0) );
break;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps);
GetClientRect (hwnd, &rect);
GetWindowRect(hwnd, &size);
Rectangle(hdc, 10, 10, 80, 80 );
if(drawingLine == true)
{
MoveToEx(hdc, point1.x, point1.y, NULL);
LineTo(hdc, point2.x, point2.y);
}
EndPaint (hwnd, &ps);
return 0;
//Has all the commands that exist in your program.
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_COLOR_RED:
break;
case ID_COLOR_BLUE:
break;
case ID_COLOR_BLACK:
break;
case ID_THICKNESS_1:
break;
case ID_THICKNESS_2:
break;
case ID_THICKNESS_3:
break;
}
break;
case WM_LBUTTONDOWN:
drawingLine = true;
SelectObject(hdc, GetStockObject(BLACK_BRUSH));
point1.x = LOWORD(lParam);
point1.y = HIWORD(lParam);
MoveToEx(hdc, point1.x, point1.y, NULL);
break;
case WM_MOUSEMOVE:
point2.x = LOWORD(lParam);
point2.y = HIWORD(lParam);
InvalidateRect(hwnd, NULL, 1);
break;
case WM_LBUTTONUP:
point2.x = LOWORD(lParam);
point2.y = HIWORD(lParam);
drawingLine = false;
break;
//Causes the program to exit.
case WM_DESTROY:
PostQuitMessage(0);
break;
}
The main thing to do is respond to WM_ERASEBKGND and return true to prevent the default re-drawing of the background.
Then you'll need to erase the old line you before drawing the new one. Just for example, you might save bits from under the line, draw it, then restore those (and only those) pixels before drawing the line, so when you need to erase it, you can just restore those pixels to erase it.
The obvious alternative (that's usually simpler though theoretically at least a little slower) is to use double buffering. Do roughly the drawing you are now, but to an off-screen bitmap. Then, when it's all complete just BitBlt from there to the screen.

Win32 Draw a Dragging Rectangle

I am trying to draw a rectangle as you left click on a point and then drag across , while your dragging across the rectangle gets form to show you a preview of how the rectangle will look . This works fine except when I drag back into a smaller rectangle the old rectangles still remain.
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
if(painting)
{
HPEN greenPen=CreatePen(PS_SOLID, 1, RGB(0,255,0));
SelectObject(hdc,greenPen);
Rectangle(hdc, x1, y1, x2, y2);
}
EndPaint(hWnd, &ps);
break;
case WM_LBUTTONDOWN:
painting=true;
x1=LOWORD(lParam);
y1=HIWORD(lParam);
InvalidateRect(hWnd, NULL ,false);
break;
case WM_MOUSEMOVE:
x2=LOWORD(lParam);
y2=HIWORD(lParam);
InvalidateRect(hWnd, NULL ,false);
break;
case WM_LBUTTONUP:
painting=false;
break;
#user1788175 - just use a RECT struct. When the drawing starts, you set the left & top members to the x,y pos of the mouse. When the mouse is released, set the right,bottom members. Swap left,right if necessary, to ensure that left
Here's some code ripped from a class I wrote to deal with with selection rectangles. You can ignore the normalize code - it just converts the pixel coordinates to ones in the range [0..1], so I can draw the selection on a scaled-down version of the image, yet still select the same region if the image is shown at a different scale. My images were 4944x6992, so I had to display a scaled-down version of them.
LRESULT CALLBACK cSelBoxImg::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
RECT mappedRect, tmpRect, myRect;
HBRUSH myBrush;
BITMAP bm;
PAINTSTRUCT ps;
HDC mDC;
HBITMAP tmpBmp;
switch (uMsg)
{
case WM_LBUTTONDOWN:
if (bMouseSelectionEnabled)
{
bRubberBanding = true;
mouseClickDownPos.x = LOWORD(lParam);
mouseClickDownPos.y = HIWORD(lParam);
curMousePos = mouseClickDownPos;
selectionRect.left = min(curMousePos.x, mouseClickDownPos.x);
selectionRect.right = max(curMousePos.x, mouseClickDownPos.x);
selectionRect.top = min(curMousePos.y, mouseClickDownPos.y);
selectionRect.bottom = max(curMousePos.y, mouseClickDownPos.y);
normalizeSelRect();
InvalidateRect(mHwnd, NULL, isBkgTransparent);
PostMessage(GetParent(hWnd), WM_COMMAND, GetDlgCtrlID(hWnd), (LPARAM)hWnd);
}
return 1;
case WM_LBUTTONUP:
if (bMouseSelectionEnabled)
if (bRubberBanding)
{
bRubberBanding = false;
mouseClickUpPos.x = LOWORD(lParam);
mouseClickUpPos.y = HIWORD(lParam);
selectionRect.left = min(mouseClickUpPos.x, mouseClickDownPos.x);
selectionRect.right = max(mouseClickUpPos.x, mouseClickDownPos.x);
selectionRect.top = min(mouseClickUpPos.y, mouseClickDownPos.y);
selectionRect.bottom = max(mouseClickUpPos.y, mouseClickDownPos.y);
normalizeSelRect();
InvalidateRect(mHwnd, NULL, isBkgTransparent);
PostMessage(GetParent(hWnd), WM_COMMAND, GetDlgCtrlID(hWnd), (LPARAM)hWnd);
}
return 1;
case WM_MOUSEMOVE:
if (bMouseSelectionEnabled)
if (bRubberBanding)
{
curMousePos.x = LOWORD(lParam);
curMousePos.y = HIWORD(lParam);
selectionRect.left = min(curMousePos.x, mouseClickDownPos.x);
selectionRect.right = max(curMousePos.x, mouseClickDownPos.x);
selectionRect.top = min(curMousePos.y, mouseClickDownPos.y);
selectionRect.bottom = max(curMousePos.y, mouseClickDownPos.y);
// UpdateWindow(hWnd);
//RedrawWindow(hWnd, NULL, NULL, RDW_UPDATENOW);
normalizeSelRect();
InvalidateRect(hWnd, NULL, false);
PostMessage(GetParent(hWnd), WM_COMMAND, GetDlgCtrlID(hWnd), (LPARAM)hWnd);
// printf("Message posted\n");
}
return 0;
case WM_PAINT:
mDC = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &tmpRect);
// GetObject(mBmp, sizeof(bm), &bm);
mappedRect.left = mLeft * tmpRect.right;
mappedRect.right = mRight * tmpRect.right;
mappedRect.top = mTop * tmpRect.bottom;
mappedRect.bottom = mBottom * tmpRect.bottom;
displayImage();
if (mBmp) drawRect(mDC, mappedRect, RGB(0,0,255));
DeleteObject(tmpBmp);
EndPaint(hWnd, &ps);
return 0;
case WM_ERASEBKGND:
if (isBkgTransparent)
{
GetClientRect(hWnd, &myRect);
myBrush = (HBRUSH) GetWindowLong(hWnd, GCL_HBRBACKGROUND);
FillRect((HDC)wParam, &myRect, myBrush);
printf("background cleared\n");
}
return true;
case WM_SETCURSOR:
SetCursor(mCursor);
return true;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}