352 lines
8.8 KiB
C
352 lines
8.8 KiB
C
// 8 september 2015
|
|
#include "area.h"
|
|
|
|
#define areaClass L"libui_uiAreaClass"
|
|
|
|
struct uiArea {
|
|
// uiWindowsControl c;
|
|
HWND hwnd;
|
|
uiAreaHandler *ah;
|
|
intmax_t hscrollpos;
|
|
intmax_t vscrollpos;
|
|
int hwheelCarry;
|
|
int vwheelCarry;
|
|
};
|
|
|
|
static void doPaint(uiArea *a, HDC dc, RECT *client, RECT *clip)
|
|
{
|
|
uiAreaHandler *ah = a->ah;
|
|
uiAreaDrawParams dp;
|
|
|
|
dp.Context = newContext(dc);
|
|
|
|
dp.ClientWidth = client->right - client->left;
|
|
dp.ClientHeight = client->bottom - client->top;
|
|
|
|
dp.ClipX = clip->left;
|
|
dp.ClipY = clip->top;
|
|
dp.ClipWidth = clip->right - clip->left;
|
|
dp.ClipHeight = clip->bottom - clip->top;
|
|
|
|
// TODO is this really the best for multimonitor setups?
|
|
dp.DPIX = GetDeviceCaps(dc, LOGPIXELSX);
|
|
dp.DPIY = GetDeviceCaps(dc, LOGPIXELSY);
|
|
|
|
dp.HScrollPos = a->hscrollpos;
|
|
dp.VScrollPos = a->vscrollpos;
|
|
|
|
(*(ah->Draw))(ah, a, &dp);
|
|
}
|
|
|
|
struct scrollParams {
|
|
intmax_t *pos;
|
|
intmax_t pagesize;
|
|
intmax_t length;
|
|
int *wheelCarry;
|
|
UINT wheelSPIAction;
|
|
};
|
|
|
|
void scrollto(uiArea *a, int which, struct scrollParams *p, intmax_t pos)
|
|
{
|
|
SCROLLINFO si;
|
|
intmax_t xamount, yamount;
|
|
|
|
// note that the pos < 0 check is /after/ the p->length - p->pagesize check
|
|
// it used to be /before/; this was actually a bug in Raymond Chen's original algorithm: if there are fewer than a page's worth of items, p->length - p->pagesize will be negative and our content draw at the bottom of the window
|
|
// this SHOULD have the same effect with that bug fixed and no others introduced... (thanks to devin on irc.badnik.net for confirming this logic)
|
|
// TODO verify this still holds with uiArea
|
|
if (pos > p->length - p->pagesize)
|
|
pos = p->length - p->pagesize;
|
|
if (pos < 0)
|
|
pos = 0;
|
|
|
|
// negative because ScrollWindowEx() is "backwards"
|
|
xamount = -(pos - *(p->pos));
|
|
yamount = 0;
|
|
if (which == SB_VERT) {
|
|
yamount = xamount;
|
|
xamount = 0;
|
|
}
|
|
|
|
if (ScrollWindowEx(a->hwnd, xamount, yamount,
|
|
NULL, NULL, NULL, NULL,
|
|
SW_ERASE | SW_INVALIDATE) == ERROR)
|
|
;//TODO logLastError("error scrolling area in scrollto()");
|
|
|
|
*(p->pos) = pos;
|
|
|
|
// now commit our new scrollbar setup...
|
|
ZeroMemory(&si, sizeof (SCROLLINFO));
|
|
si.cbSize = sizeof (SCROLLINFO);
|
|
si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
|
|
si.nPage = p->pagesize;
|
|
si.nMin = 0;
|
|
si.nMax = p->length - 1; // endpoint inclusive
|
|
si.nPos = *(p->pos);
|
|
SetScrollInfo(a->hwnd, which, &si, TRUE);
|
|
}
|
|
|
|
void scrollby(uiArea *a, int which, struct scrollParams *p, intmax_t delta)
|
|
{
|
|
scrollto(a, which, p, *(p->pos) + delta);
|
|
}
|
|
|
|
void scroll(uiArea *a, int which, struct scrollParams *p, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
intmax_t pos;
|
|
SCROLLINFO si;
|
|
|
|
pos = *(p->pos);
|
|
switch (LOWORD(wParam)) {
|
|
case SB_LEFT: // also SB_TOP
|
|
pos = 0;
|
|
break;
|
|
case SB_RIGHT: // also SB_BOTTOM
|
|
pos = p->length - p->pagesize;
|
|
break;
|
|
case SB_LINELEFT: // also SB_LINEUP
|
|
pos--;
|
|
break;
|
|
case SB_LINERIGHT: // also SB_LINEDOWN
|
|
pos++;
|
|
break;
|
|
case SB_PAGELEFT: // also SB_PAGEUP
|
|
pos -= p->pagesize;
|
|
break;
|
|
case SB_PAGERIGHT: // also SB_PAGEDOWN
|
|
pos += p->pagesize;
|
|
break;
|
|
case SB_THUMBPOSITION:
|
|
ZeroMemory(&si, sizeof (SCROLLINFO));
|
|
si.cbSize = sizeof (SCROLLINFO);
|
|
si.fMask = SIF_POS;
|
|
if (GetScrollInfo(a->hwnd, which, &si) == 0)
|
|
logLastError("error getting thumb position for area in scroll()");
|
|
pos = si.nPos;
|
|
break;
|
|
case SB_THUMBTRACK:
|
|
ZeroMemory(&si, sizeof (SCROLLINFO));
|
|
si.cbSize = sizeof (SCROLLINFO);
|
|
si.fMask = SIF_TRACKPOS;
|
|
if (GetScrollInfo(a->hwnd, which, &si) == 0)
|
|
logLastError("error getting thumb track position for area in scroll()");
|
|
pos = si.nTrackPos;
|
|
break;
|
|
}
|
|
scrollto(a, which, p, pos);
|
|
}
|
|
|
|
static void wheelscroll(uiArea *a, int which, struct scrollParams *p, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
int delta;
|
|
int lines;
|
|
UINT scrollAmount;
|
|
|
|
delta = GET_WHEEL_DELTA_WPARAM(wParam);
|
|
if (SystemParametersInfoW(p->wheelSPIAction, 0, &scrollAmount, 0) == 0)
|
|
// TODO use scrollAmount == 3 (for both v and h) instead?
|
|
logLastError("error getting area wheel scroll amount in wheelscroll()");
|
|
if (scrollAmount == WHEEL_PAGESCROLL)
|
|
scrollAmount = p->pagesize;
|
|
if (scrollAmount == 0) // no mouse wheel scrolling (or t->pagesize == 0)
|
|
return;
|
|
// the rest of this is basically http://blogs.msdn.com/b/oldnewthing/archive/2003/08/07/54615.aspx and http://blogs.msdn.com/b/oldnewthing/archive/2003/08/11/54624.aspx
|
|
// see those pages for information on subtleties
|
|
delta += *(p->wheelCarry);
|
|
lines = delta * ((int) scrollAmount) / WHEEL_DELTA;
|
|
*(p->wheelCarry) = delta - lines * WHEEL_DELTA / ((int) scrollAmount);
|
|
return scrollby(a, which, p, -lines);
|
|
}
|
|
|
|
static void hscrollParams(uiArea *a, struct scrollParams *p)
|
|
{
|
|
RECT r;
|
|
|
|
ZeroMemory(p, sizeof (struct scrollParams));
|
|
p->pos = &(a->hscrollpos);
|
|
if (GetClientRect(a->hwnd, &r) == 0)
|
|
logLastError("error getting area client rect in hscrollParams()");
|
|
p->pagesize = r.right - r.left;
|
|
p->length = (*(a->ah->HScrollMax))(a->ah, a);
|
|
p->wheelCarry = &(a->hwheelCarry);
|
|
p->wheelSPIAction = SPI_GETWHEELSCROLLCHARS;
|
|
}
|
|
|
|
static void hscrollto(uiArea *a, intmax_t pos)
|
|
{
|
|
struct scrollParams p;
|
|
|
|
hscrollParams(a, &p);
|
|
scrollto(a, SB_HORZ, &p, pos);
|
|
}
|
|
|
|
static void hscrollby(uiArea *a, intmax_t delta)
|
|
{
|
|
struct scrollParams p;
|
|
|
|
hscrollParams(a, &p);
|
|
scrollby(a, SB_HORZ, &p, delta);
|
|
}
|
|
|
|
static void hscroll(uiArea *a, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
struct scrollParams p;
|
|
|
|
hscrollParams(a, &p);
|
|
scroll(a, SB_HORZ, &p, wParam, lParam);
|
|
}
|
|
|
|
static void hwheelscroll(uiArea *a, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
struct scrollParams p;
|
|
|
|
hscrollParams(a, &p);
|
|
wheelscroll(a, SB_HORZ, &p, wParam, lParam);
|
|
}
|
|
|
|
static void vscrollParams(uiArea *a, struct scrollParams *p)
|
|
{
|
|
RECT r;
|
|
|
|
ZeroMemory(p, sizeof (struct scrollParams));
|
|
p->pos = &(a->vscrollpos);
|
|
if (GetClientRect(a->hwnd, &r) == 0)
|
|
logLastError("error getting area client rect in vscrollParams()");
|
|
p->pagesize = r.bottom - r.top;
|
|
p->length = (*(a->ah->VScrollMax))(a->ah, a);
|
|
p->wheelCarry = &(a->vwheelCarry);
|
|
p->wheelSPIAction = SPI_GETWHEELSCROLLLINES;
|
|
}
|
|
|
|
static void vscrollto(uiArea *a, intmax_t pos)
|
|
{
|
|
struct scrollParams p;
|
|
|
|
vscrollParams(a, &p);
|
|
scrollto(a, SB_VERT, &p, pos);
|
|
}
|
|
|
|
static void vscrollby(uiArea *a, intmax_t delta)
|
|
{
|
|
struct scrollParams p;
|
|
|
|
vscrollParams(a, &p);
|
|
scrollby(a, SB_VERT, &p, delta);
|
|
}
|
|
|
|
static void vscroll(uiArea *a, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
struct scrollParams p;
|
|
|
|
vscrollParams(a, &p);
|
|
scroll(a, SB_VERT, &p, wParam, lParam);
|
|
}
|
|
|
|
static void vwheelscroll(uiArea *a, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
struct scrollParams p;
|
|
|
|
vscrollParams(a, &p);
|
|
wheelscroll(a, SB_VERT, &p, wParam, lParam);
|
|
}
|
|
|
|
static LRESULT CALLBACK areaWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
uiArea *a;
|
|
CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
|
|
HDC dc;
|
|
PAINTSTRUCT ps;
|
|
RECT client;
|
|
|
|
a = (uiArea *) GetWindowLongPtrW(hwnd, GWLP_USERDATA);
|
|
if (a == NULL) {
|
|
if (uMsg == WM_CREATE)
|
|
SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR) (cs->lpCreateParams));
|
|
// fall through to DefWindowProcW() anyway
|
|
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
switch (uMsg) {
|
|
case WM_PAINT:
|
|
dc = BeginPaint(a->hwnd, &ps);
|
|
if (dc == NULL)
|
|
logLastError("error beginning paint in areaWndProc");
|
|
if (GetClientRect(a->hwnd, &client) == 0)
|
|
logLastError("error getting client rect in WM_PAINT in areaWndProc()");
|
|
doPaint(a, dc, &client, &(ps.rcPaint));
|
|
EndPaint(a->hwnd, &ps);
|
|
return 0;
|
|
case WM_PRINTCLIENT:
|
|
if (GetClientRect(a->hwnd, &client) == 0)
|
|
logLastError("error getting client rect in WM_PRINTCLIENT in areaWndProc()");
|
|
doPaint(a, (HDC) wParam, &client, &client);
|
|
return 0;
|
|
case WM_HSCROLL:
|
|
hscroll(a, wParam, lParam);
|
|
return 0;
|
|
case WM_MOUSEHWHEEL:
|
|
hwheelscroll(a, wParam, lParam);
|
|
return 0;
|
|
case WM_VSCROLL:
|
|
vscroll(a, wParam, lParam);
|
|
return 0;
|
|
case WM_MOUSEWHEEL:
|
|
vwheelscroll(a, wParam, lParam);
|
|
return 0;
|
|
case WM_LBUTTONDOWN:
|
|
// TODO
|
|
SetFocus(a->hwnd);
|
|
break;
|
|
}
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
ATOM registerAreaClass(void)
|
|
{
|
|
WNDCLASSW wc;
|
|
|
|
ZeroMemory(&wc, sizeof (WNDCLASSW));
|
|
wc.lpszClassName = areaClass;
|
|
wc.lpfnWndProc = areaWndProc;
|
|
wc.hInstance = hInstance;
|
|
//TODO wc.hIcon = hDefaultIcon;
|
|
//TODO wc.hCursor = hDefaultCursor;
|
|
wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
|
|
wc.style = CS_HREDRAW | CS_VREDRAW;
|
|
return RegisterClassW(&wc);
|
|
}
|
|
|
|
void unregisterAreaClass(void)
|
|
{
|
|
if (UnregisterClassW(areaClass, hInstance) == 0)
|
|
logLastError("error unregistering uiArea window class in unregisterAreaClass()");
|
|
}
|
|
|
|
HWND makeArea(DWORD exstyle, DWORD style, int x, int y, int cx, int cy, HWND parent, uiAreaHandler *ah)
|
|
{
|
|
uiArea *a;
|
|
|
|
// TODO
|
|
a = malloc(sizeof (uiArea));
|
|
|
|
a->ah = ah;
|
|
|
|
a->hwnd = CreateWindowExW(exstyle,
|
|
areaClass, L"",
|
|
style | WS_HSCROLL | WS_VSCROLL,
|
|
x, y, cx, cy,
|
|
parent, NULL, hInstance, a);
|
|
|
|
return a->hwnd;
|
|
}
|
|
|
|
void areaUpdateScroll(HWND area)
|
|
{
|
|
uiArea *a;
|
|
|
|
a = (uiArea *) GetWindowLongPtrW(area, GWLP_USERDATA);
|
|
// use a no-op scroll to simulate scrolling
|
|
hscrollby(a, 0);
|
|
vscrollby(a, 0);
|
|
}
|