Skip to main content
GameDev.net gamedev.net
🔒 Locked

Scroll Bar Problem

Started by dev578 Oct 23, 2005 at 8:49 AM 0 replies 3.6k views
Original Post
dev578
dev578
I am using C++ with the Win32 API. I am loading one bitmap into the client area of a window. When the bitmap is larger than the client area, I need scroll bars. When the window is sized, and it is larger than the bitmap, I don't need the bars, but when it is sized and it is smaller than the bitmap, I need to create scroll bars. Everything seems to be working fine except that they are triggering off and on at the incorrect position. Here is how I handle WM_SIZE, almost exactly what MSDN does in their "How to scroll a bitmap" article:

case WM_SIZE:
{
     int nHMax = max(FontCreator.m_nBmpWidth - LOWORD(lParam),0);
     int nHPage = LOWORD(lParam);

     SCROLLINFO si = {0};

     si.cbSize = sizeof(si);
     si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS;
     si.nMax   = nHMax;
     si.nMin   = 0;
     si.nPage  = nHPage;
     si.nPos   = cur_xpos;

     SetScrollInfo(hWnd, SB_HORZ, &si, true);

     //then I do the same for the vertical scroll bar
}
break;

They tigger on and off at about half way through the bitmap. Another odd find is that when I set the page to 0, it triggers at the correct position, but then the size of the bar is a constant small. When the page is set to the client width or height, then it grows and shrinks as you size it, like it should. Does anyone know how I can fix this problem? Any help is appreciated. Thank you, -Dev578
yadango
yadango
almost forgot...
msdn bitmap scroll example not very good as it doesn't clamp the
nPage parameter.

From MSDN:
The SetScrollInfo function performs range checking on the values specified by the nPage and nPos members of the SCROLLINFO structure. The nPage member must specify a value from 0 to nMax - nMin +1. The nPos member must specify a value between nMin and nMax - max( nPage– 1, 0). If either value is beyond its range, the function sets it to a value that is just within the range.

Basically this means if using SIF_PAGE, you assume you can scroll the entire
image (by setting nMin and nMax to the bitmap extremes), but clamp nPos and
nPage.

#include<windows.h>#include<fstream>using namespace std;// window variablesstatic HWND window = 0;static int window_dx = 0;static int window_dy = 0;// bitmap variablesstatic HBITMAP bitmap_handle = 0;static BYTE* bitmap_memory = 0;static int bitmap_dx = 0;static int bitmap_dy = 0;// scrolling variablesstatic int scroll_origin_x = 0;static int scroll_origin_y = 0;static int scroll_min_x = 0;static int scroll_min_y = 0;static int scroll_max_x = 0;static int scroll_max_y = 0;// functionsLRESULT CALLBACK MainWndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam);bool LoadBitmap(HWND parent);void FreeBitmap(void);void DrawBitmap(HDC winDC, HBITMAP bitmap, int x, int y, int xoff, int yoff);void InitScrollers(void);void RecomputeScrollers(void);bool OffsetScrollers(int dx, int dy);bool AdjustScrollers(int x, int y);template<class T>inline void clamp(T& value, const T& minval, const T& maxval){ if(value < minval) value = minval; if(value > maxval) value = maxval;}int WINAPI WinMain(HINSTANCE basead, HINSTANCE, char*, int){ WNDCLASS wc; wc.style = CS_DBLCLKS; wc.lpfnWndProc = MainWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = basead; wc.hIcon = 0; wc.hCursor = LoadCursor(0, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = 0; wc.lpszClassName = "ScrollWindow"; if(!RegisterClass(&wc)) return MessageBox(0, "Could not register main window.", "Error", MB_ICONSTOP); // create window DWORD style = WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL; int x = CW_USEDEFAULT; int w = CW_USEDEFAULT; int y = CW_USEDEFAULT; int h = CW_USEDEFAULT; HWND retval = CreateWindow("ScrollWindow", "ScrollWindow", style, x, y, w, h, 0, 0, basead, 0); if(!retval) return MessageBox(0, "Could not create main window.", "Error", MB_ICONSTOP); ShowWindow(retval, SW_SHOW); UpdateWindow(retval); // load bitmap and initialize scrollers and repaint LoadBitmap(retval); InitScrollers(); InvalidateRect(retval, 0, TRUE); MSG msg; while(GetMessage(&msg, 0, 0, 0)) {       TranslateMessage(&msg);       DispatchMessage(&msg);      } return (int)msg.wParam;}LRESULT CALLBACK MainWndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam){  switch(message) {   case(WM_NCCREATE) : {        // set window handle        ::window = window;        return DefWindowProc(window, message, wparam, lparam);       }   case(WM_NCDESTROY) : {        // set window handle        ::window = 0;        return DefWindowProc(window, message, wparam, lparam);       }   case(WM_CREATE) : {        return DefWindowProc(window, message, wparam, lparam);       }   case(WM_DESTROY) : {        FreeBitmap();        PostQuitMessage(0);        return DefWindowProc(window, message, wparam, lparam);       }   case(WM_SIZE) : {        // set window dimensions        window_dx = LOWORD(lparam);        window_dy = HIWORD(lparam);        // recompute scroll information and repaint        RecomputeScrollers();        InvalidateRect(window, 0, TRUE);        return 0;       }   case(WM_PAINT) : {        PAINTSTRUCT ps;        HDC winDC = BeginPaint(window, &ps);        DrawBitmap(winDC, bitmap_handle, 0, 0, scroll_origin_x, scroll_origin_y);        EndPaint(window, &ps);        return 0;       }   case(WM_HSCROLL) : {        bool repaint = false;        int code = (int)LOWORD(wparam);        int npos = (int)HIWORD(wparam);        switch(code) {          case(SB_LINELEFT) :               repaint = OffsetScrollers(-1, 0);               break;          case(SB_LINERIGHT) :               repaint = OffsetScrollers(+1, 0);               break;          case(SB_PAGELEFT) : {               SCROLLINFO scrollinfo;               scrollinfo.cbSize = sizeof(SCROLLINFO);               scrollinfo.fMask = SIF_PAGE;               GetScrollInfo(::window, SB_HORZ, &scrollinfo);               repaint = OffsetScrollers(-scrollinfo.nPage, 0);               break;              }          case(SB_PAGERIGHT) : {               SCROLLINFO scrollinfo;               scrollinfo.cbSize = sizeof(SCROLLINFO);               scrollinfo.fMask = SIF_PAGE;               GetScrollInfo(::window, SB_HORZ, &scrollinfo);               repaint = OffsetScrollers(+scrollinfo.nPage, 0);               break;              }          case(SB_THUMBTRACK) :               repaint = AdjustScrollers(npos, -1);               break;          case(SB_THUMBPOSITION) :               repaint = AdjustScrollers(npos, -1);               break;         }        if(repaint) InvalidateRect(window, 0, TRUE);        return 0;       }   case(WM_VSCROLL) : {        bool repaint = false;        int code = (int)LOWORD(wparam);        int npos = (int)HIWORD(wparam);        switch(code) {          case(SB_LINEUP) :               repaint = OffsetScrollers(0, -1);               break;          case(SB_LINEDOWN) :               repaint = OffsetScrollers(0, +1);               break;          case(SB_PAGEUP) : {               SCROLLINFO scrollinfo;               scrollinfo.cbSize = sizeof(SCROLLINFO);               scrollinfo.fMask = SIF_PAGE;               GetScrollInfo(::window, SB_VERT, &scrollinfo);               repaint = OffsetScrollers(0, -scrollinfo.nPage);               break;              }          case(SB_PAGEDOWN) : {               SCROLLINFO scrollinfo;               scrollinfo.cbSize = sizeof(SCROLLINFO);               scrollinfo.fMask = SIF_PAGE;               GetScrollInfo(::window, SB_VERT, &scrollinfo);               repaint = OffsetScrollers(0, +scrollinfo.nPage);               break;              }          case(SB_THUMBTRACK) :               repaint = AdjustScrollers(-1, npos);               break;          case(SB_THUMBPOSITION) :               repaint = AdjustScrollers(-1, npos);               break;         }        if(repaint) InvalidateRect(window, 0, TRUE);        return 0;       }   default : return DefWindowProc(window, message, wparam, lparam);  } return 0;}bool LoadBitmap(HWND parent){ // open file dialog char buffer1[MAX_PATH]; char buffer2[MAX_PATH]; buffer1[0] = '\0'; buffer2[0] = '\0'; OPENFILENAME ofn; ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = parent; ofn.hInstance = (HINSTANCE)GetWindowLong(parent, GWL_HINSTANCE); ofn.lpstrFilter = "Bitmap Files (*.bmp)\0*.bmp\0"; ofn.lpstrFile = buffer1; ofn.nMaxFile = MAX_PATH; ofn.lpstrFileTitle = buffer2; ofn.nMaxFileTitle = MAX_PATH; ofn.lpstrInitialDir = 0; ofn.lpstrTitle = "Open"; ofn.Flags = OFN_FILEMUSTEXIST; ofn.lpstrDefExt = "bmp"; if(!GetOpenFileName(&ofn)) return false; // delete previous bitmap FreeBitmap(); // open file ifstream file(buffer1, ios::binary); if(!file) return false; // read headers BITMAPFILEHEADER fileHeader; BITMAPINFOHEADER infoHeader; if(!file.read((char*)&fileHeader, sizeof(fileHeader))) return false; if(!file.read((char*)&infoHeader, sizeof(infoHeader))) return false; // assume 24-bit bitmap if(fileHeader.bfType != 0x4d42) return false; if(infoHeader.biSize != sizeof(infoHeader)) return false; if(infoHeader.biPlanes != 1) return false; if(infoHeader.biBitCount != 24) return false; if(infoHeader.biCompression != BI_RGB) return false; // create new bitmap BITMAPINFO info; info.bmiHeader = infoHeader; HDC winDC = GetDC(parent); bitmap_handle = CreateDIBSection(winDC, &info, DIB_RGB_COLORS, (void**)&bitmap_memory, 0, 0); ReleaseDC(parent, winDC); if(!bitmap_handle) return false; // read bitmap data bitmap_dx = infoHeader.biWidth; bitmap_dy = infoHeader.biHeight; int total_bytes = (((24*bitmap_dx + 31) & (~31))/8)*bitmap_dy; if(!file.read((char*)bitmap_memory, total_bytes)) return false; return true;}void FreeBitmap(void){ DeleteObject(bitmap_handle); bitmap_handle = 0; bitmap_memory = 0; bitmap_dx = bitmap_dy = 0;}void DrawBitmap(HDC winDC, HBITMAP bitmap, int x, int y, int xoff, int yoff){ if(!bitmap) return; HDC memDC = CreateCompatibleDC(winDC); if(!memDC) return; BITMAP bm; GetObject(bitmap, sizeof(bm), &bm); HBITMAP old_bitmap = (HBITMAP)SelectObject(memDC, bitmap); BitBlt(winDC, x, y, bm.bmWidth, bm.bmHeight, memDC, xoff, yoff, SRCCOPY); SelectObject(memDC, old_bitmap); DeleteDC(memDC);}void InitScrollers(void){ // must have bitmap if(!bitmap_handle) {    EnableScrollBar(::window, SB_HORZ, ESB_DISABLE_BOTH);    EnableScrollBar(::window, SB_VERT, ESB_DISABLE_BOTH);    return;   } // enable/disable scroll bars if client is larger than bitmap if(window_dx > bitmap_dx) EnableScrollBar(::window, SB_HORZ, ESB_DISABLE_BOTH); else EnableScrollBar(::window, SB_HORZ, ESB_ENABLE_BOTH); if(window_dy > bitmap_dy) EnableScrollBar(::window, SB_VERT, ESB_DISABLE_BOTH); else EnableScrollBar(::window, SB_VERT, ESB_ENABLE_BOTH); // initialize scroll information scroll_origin_x = 0; scroll_origin_y = 0; scroll_min_x = 0; scroll_min_y = 0; scroll_max_x = bitmap_dx; scroll_max_y = bitmap_dy; // set scroll bar parameters SCROLLINFO scrollinfo; scrollinfo.cbSize = sizeof(SCROLLINFO); scrollinfo.fMask  = SIF_RANGE | SIF_POS | SIF_PAGE; scrollinfo.nMin   = scroll_min_x; scrollinfo.nMax   = scroll_max_x; scrollinfo.nPage  = min(window_dx, scroll_max_x + 1); scrollinfo.nPos   = scroll_origin_x; scrollinfo.nTrackPos = 0; SetScrollInfo(::window, SB_HORZ, &scrollinfo, TRUE); scrollinfo.cbSize = sizeof(SCROLLINFO); scrollinfo.fMask  = SIF_RANGE | SIF_POS | SIF_PAGE; scrollinfo.nMin   = scroll_min_y; scrollinfo.nMax   = scroll_max_y; scrollinfo.nPage  = min(window_dy, scroll_max_y + 1); scrollinfo.nPos   = scroll_origin_y; scrollinfo.nTrackPos = 0; SetScrollInfo(::window, SB_VERT, &scrollinfo, TRUE);}void RecomputeScrollers(void){ // must have bitmap if(!bitmap_handle) {    EnableScrollBar(::window, SB_HORZ, ESB_DISABLE_BOTH);    EnableScrollBar(::window, SB_VERT, ESB_DISABLE_BOTH);    return;   } // enable/disable scroll bars if client is larger than bitmap if(window_dx > bitmap_dx) EnableScrollBar(::window, SB_HORZ, ESB_DISABLE_BOTH); else EnableScrollBar(::window, SB_HORZ, ESB_ENABLE_BOTH); if(window_dy > bitmap_dy) EnableScrollBar(::window, SB_VERT, ESB_DISABLE_BOTH); else EnableScrollBar(::window, SB_VERT, ESB_ENABLE_BOTH); // this is different than in InitScrollers... // reinitialize scroll information, but don't change the origin scroll_min_x = 0; scroll_min_y = 0; scroll_max_x = bitmap_dx; scroll_max_y = bitmap_dy; // this is different than in InitScrollers... // clamp the scroll origins to the min and max values int limit_x = max(0, bitmap_dx - window_dx); int limit_y = max(0, bitmap_dy - window_dy); clamp(scroll_origin_x, scroll_min_x, limit_x); clamp(scroll_origin_y, scroll_min_y, limit_y); // set scroll bar parameters SCROLLINFO scrollinfo; scrollinfo.cbSize = sizeof(SCROLLINFO); scrollinfo.fMask  = SIF_RANGE | SIF_POS | SIF_PAGE; scrollinfo.nMin   = scroll_min_x; scrollinfo.nMax   = scroll_max_x; scrollinfo.nPage  = min(window_dx, scroll_max_x + 1); scrollinfo.nPos   = scroll_origin_x; scrollinfo.nTrackPos = 0; SetScrollInfo(::window, SB_HORZ, &scrollinfo, TRUE); scrollinfo.cbSize = sizeof(SCROLLINFO); scrollinfo.fMask  = SIF_RANGE | SIF_POS | SIF_PAGE; scrollinfo.nMin   = scroll_min_y; scrollinfo.nMax   = scroll_max_y; scrollinfo.nPage  = min(window_dy, scroll_max_y + 1); scrollinfo.nPos   = scroll_origin_y; scrollinfo.nTrackPos = 0; SetScrollInfo(::window, SB_VERT, &scrollinfo, TRUE);}bool OffsetScrollers(int dx, int dy){ // set and adjust new scroll origin int new_x_origin = scroll_origin_x + dx; clamp(new_x_origin, scroll_min_x, scroll_max_x); int new_y_origin = scroll_origin_y + dy; clamp(new_y_origin, scroll_min_y, scroll_max_y); // no need to repaint, nothing has changed if(new_x_origin == scroll_origin_x && new_y_origin == scroll_origin_y)    return false; // set new scroll origins scroll_origin_x = new_x_origin; scroll_origin_y = new_y_origin; // clamp the scroll origins to the min and max values int limit_x = max(0, bitmap_dx - window_dx); int limit_y = max(0, bitmap_dy - window_dy); clamp(scroll_origin_x, scroll_min_x, limit_x); clamp(scroll_origin_y, scroll_min_y, limit_y); // set scroll bar parameters SCROLLINFO scrollinfo; scrollinfo.cbSize = sizeof(SCROLLINFO); scrollinfo.fMask  = SIF_RANGE | SIF_POS; scrollinfo.nMin   = scroll_min_x; scrollinfo.nMax   = scroll_max_x; scrollinfo.nPos   = scroll_origin_x; scrollinfo.nTrackPos = 0; SetScrollInfo(::window, SB_HORZ, &scrollinfo, TRUE); scrollinfo.cbSize = sizeof(SCROLLINFO); scrollinfo.fMask  = SIF_RANGE | SIF_POS; scrollinfo.nMin   = scroll_min_y; scrollinfo.nMax   = scroll_max_y; scrollinfo.nPos   = scroll_origin_y; scrollinfo.nTrackPos = 0; SetScrollInfo(::window, SB_VERT, &scrollinfo, TRUE); return true;}bool AdjustScrollers(int x, int y){ // set and adjust new scroll origin int new_x_origin = (x == -1 ? scroll_origin_x : x); // -1 = no change clamp(new_x_origin, scroll_min_x, scroll_max_x); int new_y_origin = (y == -1 ? scroll_origin_y : y); // -1 = no change clamp(new_x_origin, scroll_min_y, scroll_max_y); // no need to repaint, nothing has changed if(new_x_origin == scroll_origin_x && new_y_origin == scroll_origin_y)    return false; // set new scroll origins scroll_origin_x = new_x_origin; scroll_origin_y = new_y_origin; // clamp the scroll origins to the min and max values int limit_x = max(0, bitmap_dx - window_dx); int limit_y = max(0, bitmap_dy - window_dy); clamp(scroll_origin_x, scroll_min_x, limit_x); clamp(scroll_origin_y, scroll_min_y, limit_y); // set scroll bar parameters SCROLLINFO scrollinfo; scrollinfo.cbSize = sizeof(SCROLLINFO); scrollinfo.fMask  = SIF_RANGE | SIF_POS; scrollinfo.nMin   = scroll_min_x; scrollinfo.nMax   = scroll_max_x; scrollinfo.nPos   = scroll_origin_x; scrollinfo.nTrackPos = 0; SetScrollInfo(::window, SB_HORZ, &scrollinfo, TRUE); scrollinfo.cbSize = sizeof(SCROLLINFO); scrollinfo.fMask  = SIF_RANGE | SIF_POS; scrollinfo.nMin   = scroll_min_y; scrollinfo.nMax   = scroll_max_y; scrollinfo.nPos   = scroll_origin_y; scrollinfo.nTrackPos = 0; SetScrollInfo(::window, SB_VERT, &scrollinfo, TRUE); return true;}

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.