Started using normal images for checkboxes in Windows uiTables. It doesn't quite work yet.

This commit is contained in:
Pietro Gagliardi 2018-06-09 23:25:16 -04:00
parent 4c5f0961fe
commit 405a6defd2
1 changed files with 28 additions and 47 deletions

View File

@ -9,6 +9,9 @@ static uiTableTextColumnOptionalParams defaultTextColumnOptionalParams = {
/*TODO.ColorModelColumn = */-1, /*TODO.ColorModelColumn = */-1,
}; };
#define nCheckboxImages 4
#define nLVN_GETDISPINFOSkip 3
struct columnParams { struct columnParams {
int textModelColumn; int textModelColumn;
int textEditableColumn; int textEditableColumn;
@ -159,17 +162,13 @@ nm->item.pszText=L"abcdefg";
b = uiprivWICToGDI(wb, dc); b = uiprivWICToGDI(wb, dc);
if (ReleaseDC(t->hwnd, dc) == 0) if (ReleaseDC(t->hwnd, dc) == 0)
logLastError(L"error calling ReleaseDC() in onLVN_GETDISPINFO()"); logLastError(L"error calling ReleaseDC() in onLVN_GETDISPINFO()");
if (ImageList_GetImageCount(t->smallImages) <= t->smallIndex) { if (ImageList_Replace(t->smallImages, t->smallIndex + nCheckboxImages, b, NULL) == 0)
if (ImageList_Add(t->smallImages, b, NULL) == -1) logLastError(L"error calling ImageList_Replace() in onLVN_GETDISPINFO()");
logLastError(L"error calling ImageList_Add() in onLVN_GETDISPINFO()");
} else
if (ImageList_Replace(t->smallImages, t->smallIndex, b, NULL) == 0)
logLastError(L"error calling ImageList_Replace() in onLVN_GETDISPINFO()");
// TODO error check // TODO error check
DeleteObject(b); DeleteObject(b);
nm->item.iImage = t->smallIndex; nm->item.iImage = t->smallIndex + nCheckboxImages;
t->smallIndex++; t->smallIndex++;
t->smallIndex %= 3; // TODO don't hardcode this t->smallIndex %= nLVN_GETDISPINFOSkip;
} }
// having an image list always leaves space for an image on the main item :| // having an image list always leaves space for an image on the main item :|
@ -180,20 +179,16 @@ nm->item.pszText=L"abcdefg";
nm->item.iIndent = -1; nm->item.iIndent = -1;
} }
if ((nm->item.mask & LVIF_STATE) != 0) if (p->checkboxModelColumn != -1) {
if (p->checkboxModelColumn != -1) { // TODO handle enabled
// TODO handle enabled data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn);
data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); checked = uiTableDataInt(data) != 0;
checked = uiTableDataInt(data) != 0; uiFreeTableData(data);
uiFreeTableData(data); nm->item.iImage = 0;
nm->item.state = INDEXTOSTATEIMAGEMASK(1); if (checked)
if (checked) nm->item.iImage = 1;
nm->item.state = INDEXTOSTATEIMAGEMASK(2); nm->item.mask |= LVIF_IMAGE;
nm->item.stateMask = LVIS_STATEIMAGEMASK; }
} else {
nm->item.state = INDEXTOSTATEIMAGEMASK(0);
nm->item.stateMask = LVIS_STATEIMAGEMASK;
}
// we don't want to pop from an empty queue, so if nothing updated the queue (no info was filled in above), just push NULL // we don't want to pop from an empty queue, so if nothing updated the queue (no info was filled in above), just push NULL
if (!queueUpdated) if (!queueUpdated)
@ -448,17 +443,14 @@ static void mkCheckboxesUnthemed(uiTable *t, int cx, int cy)
HDC cdc; HDC cdc;
HBITMAP prevBitmap; HBITMAP prevBitmap;
RECT r; RECT r;
int i, n; int i;
// + 1 because we can't actually use image index 0 — that index is used to mean "no state image" in LVITEMW
n = ARRAYSIZE(unthemedStates) + 1;
dc = GetDC(t->hwnd); dc = GetDC(t->hwnd);
if (dc == NULL) if (dc == NULL)
logLastError(L"error calling GetDC() in mkCheckboxesUnthemed()"); logLastError(L"error calling GetDC() in mkCheckboxesUnthemed()");
ZeroMemory(&bmi, sizeof (BITMAPINFO)); ZeroMemory(&bmi, sizeof (BITMAPINFO));
bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = cx * n; bmi.bmiHeader.biWidth = cx * nCheckboxImages;
bmi.bmiHeader.biHeight = cy; bmi.bmiHeader.biHeight = cy;
bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biBitCount = 32;
@ -474,12 +466,11 @@ static void mkCheckboxesUnthemed(uiTable *t, int cx, int cy)
// TODO error check // TODO error check
prevBitmap = (HBITMAP) SelectObject(cdc, b); prevBitmap = (HBITMAP) SelectObject(cdc, b);
// note we start at cx to start at index 1 r.left = 0;
r.left = cx;
r.top = 0; r.top = 0;
r.right = cx * 2; r.right = cx;
r.bottom = cy; r.bottom = cy;
for (i = 0; i < ARRAYSIZE(unthemedStates); i++) { for (i = 0; i < nCheckboxImages; i++) {
if (DrawFrameControl(cdc, &r, if (DrawFrameControl(cdc, &r,
DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT | unthemedStates[i]) == 0) DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT | unthemedStates[i]) == 0)
logLastError(L"error calling DrawFrameControl() in mkCheckboxesUnthemed()"); logLastError(L"error calling DrawFrameControl() in mkCheckboxesUnthemed()");
@ -494,14 +485,8 @@ static void mkCheckboxesUnthemed(uiTable *t, int cx, int cy)
if (ReleaseDC(t->hwnd, dc) == 0) if (ReleaseDC(t->hwnd, dc) == 0)
logLastError(L"error calling ReleaseDC() in mkCheckboxesUnthemed()"); logLastError(L"error calling ReleaseDC() in mkCheckboxesUnthemed()");
t->checkboxImages = ImageList_Create(cx, cy, ILC_COLOR32, if (ImageList_Replace(t->smallImages, 0, b, NULL) == 0)
n, n); logLastError(L"error calling ImageList_Replace() in mkCheckboxesUnthemed()");
if (t->checkboxImages == NULL)
logLastError(L"error calling ImageList_Create() in mkCheckboxesUnthemed()");
if (ImageList_Add(t->checkboxImages, b, NULL) == -1)
logLastError(L"error calling ImageList_Add() in mkCheckboxesUnthemed()");
// TODO will this return NULL here because it's an initial state?
SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_STATE, (LPARAM) (t->checkboxImages));
// TODO error check // TODO error check
DeleteObject(b); DeleteObject(b);
@ -511,6 +496,7 @@ uiTable *uiNewTable(uiTableModel *model)
{ {
uiTable *t; uiTable *t;
int n; int n;
int i;
uiWindowsNewControl(uiTable, t); uiWindowsNewControl(uiTable, t);
@ -536,26 +522,21 @@ uiTable *uiNewTable(uiTableModel *model)
t->dispinfoStrings = new std::queue<WCHAR *>; t->dispinfoStrings = new std::queue<WCHAR *>;
// this encodes the idea that two LVN_GETDISPINFOs must complete before we can free a string: the first real one is for the fourth call to free // this encodes the idea that two LVN_GETDISPINFOs must complete before we can free a string: the first real one is for the fourth call to free
t->dispinfoStrings->push(NULL); for (i = 0; i < nLVN_GETDISPINFOSkip; i++)
t->dispinfoStrings->push(NULL); t->dispinfoStrings->push(NULL);
t->dispinfoStrings->push(NULL);
// TODO update these when the DPI changes // TODO update these when the DPI changes
// TODO handle errors // TODO handle errors
// TODO try adding a real transparent image
t->smallImages = ImageList_Create( t->smallImages = ImageList_Create(
GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
ILC_COLOR32, ILC_COLOR32,
0, 3); nCheckboxImages + nLVN_GETDISPINFOSkip, nCheckboxImages + nLVN_GETDISPINFOSkip);
if (t->smallImages == NULL) if (t->smallImages == NULL)
logLastError(L"error calling ImageList_Create() in uiNewTable()"); logLastError(L"error calling ImageList_Create() in uiNewTable()");
// TODO will this return NULL here because it's an initial state? // TODO will this return NULL here because it's an initial state?
SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) (t->smallImages)); SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) (t->smallImages));
mkCheckboxesUnthemed(t, 16, 16); mkCheckboxesUnthemed(t, 16, 16);
// and we need to enable LVN_GETDISPINFO for states
if (SendMessageW(t->hwnd, LVM_SETCALLBACKMASK, LVIS_STATEIMAGEMASK, 0) == FALSE)
logLastError(L"error calling LVM_SETCALLBACKMASK in uiTableAdd()");
return t; return t;
} }