Started drawing uiTable text. We're drawing fully manually from here on out. Also fixed LVIF_INDENT settings and a few other things.

This commit is contained in:
Pietro Gagliardi 2018-06-13 21:06:08 -04:00
parent 15bc55dd5e
commit caa0688687
5 changed files with 139 additions and 50 deletions

View File

@ -52,6 +52,7 @@ list(APPEND _LIBUI_SOURCES
windows/tab.cpp
windows/table.cpp
windows/tableimages.cpp
windows/tabletext.cpp
windows/tabpage.cpp
windows/text.cpp
windows/utf16.cpp

View File

@ -79,24 +79,13 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex)
static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm)
{
static uiprivTableColumnParams *p;
uiTableData *data;
WCHAR *wstr;
HRESULT hr;
p = (*(t->columns))[nm->item.iSubItem];
if ((nm->item.mask & LVIF_TEXT) != 0)
if (p->textModelColumn != -1) {
data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn);
wstr = toUTF16(uiTableDataString(data));
uiFreeTableData(data);
// we could just make pszText into a freshly allocated conversion and avoid the limitation of cchTextMax
// but then we would have to keep things around for some amount of time (some pages on MSDN say 2 additional LVN_GETDISPINFO messages)
// and in practice, anything that results in extra LVN_GETDISPINFO messages (such as fillSubitemDrawParams() below) will break this counting
// TODO make it so we don't have to make a copy; instead we can convert directly into pszText (this will also avoid the risk of having a dangling surrogate pair at the end)
wcscpy_s(nm->item.pszText, nm->item.cchTextMax, wstr);
uiprivFree(wstr);
}
hr = uiprivLVN_GETDISPINFOText(t, nm, p);
if (hr != S_OK) {
// TODO
}
hr = uiprivLVN_GETDISPINFOImagesCheckboxes(t, nm, p);
if (hr != S_OK) {
// TODO
@ -126,31 +115,47 @@ static COLORREF blend(COLORREF base, double r, double g, double b, double a)
(BYTE) (bb * 255));
}
static HRESULT fillSubitemDrawParams(HWND hwnd, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp)
COLORREF uiprivTableBlendedColorFromModel(uiTable *t, NMLVCUSTOMDRAW *nm, int modelColumn, int fallbackSysColorID)
{
uiTableData *data;
double r, g, b, a;
data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->nmcd.dwItemSpec, modelColumn);
if (data == NULL)
return GetSysColor(fallbackSysColorID);
uiTableDataColor(data, &r, &g, &b, &a);
uiFreeTableData(data);
return blend(nm->clrTextBk, r, g, b, a);
}
static HRESULT fillSubitemDrawParams(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp)
{
LRESULT state;
RECT r;
HRESULT hr;
// note that we can't just copy nm->nmcd.rc into p->bounds because that is only defined during prepaint stages
// note: nm->nmcd.uItemState CDIS_SELECTED is unreliable for the listview configuration we have
state = SendMessageW(t->hwnd, LVM_GETITEMSTATE, nm->nmcd.dwItemSpec, LVIS_SELECTED);
dp->selected = (state & LVIS_SELECTED) != 0;
if (nm->iSubItem == 0) {
ZeroMemory(&r, sizeof (RECT));
r.left = LVIR_BOUNDS;
if (SendMessageW(hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) {
if (SendMessageW(t->hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) {
logLastError(L"LVM_GETITEMRECT LVIR_BOUNDS");
return E_FAIL;
}
dp->bounds = r;
ZeroMemory(&r, sizeof (RECT));
r.left = LVIR_ICON;
if (SendMessageW(hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) {
if (SendMessageW(t->hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) {
logLastError(L"LVM_GETITEMRECT LVIR_ICON");
return E_FAIL;
}
dp->icon = r;
ZeroMemory(&r, sizeof (RECT));
r.left = LVIR_LABEL;
if (SendMessageW(hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) {
if (SendMessageW(t->hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) {
logLastError(L"LVM_GETITEMRECT LVIR_LABEL");
return E_FAIL;
}
@ -161,7 +166,7 @@ static HRESULT fillSubitemDrawParams(HWND hwnd, NMLVCUSTOMDRAW *nm, uiprivSubite
ZeroMemory(&r, sizeof (RECT));
r.left = LVIR_BOUNDS;
r.top = nm->iSubItem;
if (SendMessageW(hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) {
if (SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) {
logLastError(L"LVM_GETSUBITEMRECT LVIR_BOUNDS");
return E_FAIL;
}
@ -169,7 +174,7 @@ static HRESULT fillSubitemDrawParams(HWND hwnd, NMLVCUSTOMDRAW *nm, uiprivSubite
ZeroMemory(&r, sizeof (RECT));
r.left = LVIR_ICON;
r.top = nm->iSubItem;
if (SendMessageW(hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) {
if (SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) {
logLastError(L"LVM_GETSUBITEMRECT LVIR_ICON");
return E_FAIL;
}
@ -229,8 +234,7 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm)
logLastError(L"DeleteObject()");
}
t->clrItemText = nm->clrText;
ret = CDRF_NEWFONT | CDRF_NOTIFYSUBITEMDRAW;
break;
return CDRF_NEWFONT | CDRF_NOTIFYSUBITEMDRAW;
case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
p = (*(t->columns))[nm->iSubItem];
// TODO none of this runs on the first item
@ -245,26 +249,14 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm)
}
}
// TODO draw background on image columns if needed
ret = /*CDRF_SKIPDEFAULT | */CDRF_NEWFONT;
ret = CDRF_SKIPDEFAULT | CDRF_NEWFONT;
break;
//case CDDS_SUBITEM | CDDS_ITEMPOSTPAINT:
if(0){//nm->iSubItem == 1) {
RECT r, r2;
r.left = LVIR_LABEL;
r.top = 1;
SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM)(&r));
r2.left = LVIR_ICON;
r2.top = 1;
SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM)(&r2));
r.left = r2.right + 2;
DrawTextW(nm->nmcd.hdc, L"Part", -1,
&r, DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX | DT_EDITCONTROL);}
default:
return CDRF_DODEFAULT;
}
ZeroMemory(&dp, sizeof (uiprivSubitemDrawParams));
hr = fillSubitemDrawParams(t->hwnd, nm, &dp);
hr = fillSubitemDrawParams(t, nm, &dp);
if (hr != S_OK) {
// TODO
}
@ -272,6 +264,10 @@ DrawTextW(nm->nmcd.hdc, L"Part", -1,
if (hr != S_OK) {
// TODO
}
hr = uiprivNM_CUSTOMDRAWText(t, nm, p, &dp);
if (hr != S_OK) {
// TODO
}
return ret;
}

View File

@ -40,11 +40,17 @@ struct uiTable {
};
typedef struct uiprivSubitemDrawParams uiprivSubitemDrawParams;
struct uiprivSubitemDrawParams {
bool selected;
RECT bounds;
RECT icon;
RECT label;
};
extern COLORREF uiprivTableBlendedColorFromModel(uiTable *t, NMLVCUSTOMDRAW *nm, int modelColumn, int fallbackSysColorID);
// tabletext.cpp
extern HRESULT uiprivLVN_GETDISPINFOText(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p);
extern HRESULT uiprivNM_CUSTOMDRAWText(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivTableColumnParams *p, uiprivSubitemDrawParams *dp);
// tableimages.cpp
extern HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p);
extern HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp);

View File

@ -95,8 +95,14 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip
uiTableData *data;
HRESULT hr;
if (nm->item.iSubItem == 0 && p->imageModelColumn == -1 && p->checkboxModelColumn == -1) {
// having an image list always leaves space for an image on the main item :|
// other places on the internet imply that you should be able to do this but that it shouldn't work
// but it works perfectly (and pixel-perfectly too) for me, so...
nm->item.mask |= LVIF_INDENT;
nm->item.iIndent = -1;
}
if ((nm->item.mask & LVIF_IMAGE) == 0)
// TODO we actually need to do the first column fix here too...
return S_OK; // nothing to do here
if (p->imageModelColumn != -1) {
@ -122,16 +128,8 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip
return S_OK;
}
// if we got here, there's no image in this cell
nm->item.mask &= ~LVIF_IMAGE;
// having an image list always leaves space for an image on the main item :|
// other places on the internet imply that you should be able to do this but that it shouldn't work
// but it works perfectly (and pixel-perfectly too) for me, so...
// TODO it doesn't work anymore...
if (nm->item.iSubItem == 0) {
nm->item.mask |= LVIF_INDENT;
nm->item.iIndent = -1;
}
// TODO see if this is correct
nm->item.iImage = -1;
return S_OK;
}

88
windows/tabletext.cpp Normal file
View File

@ -0,0 +1,88 @@
// 13 june 2018
#include "uipriv_windows.hpp"
#include "table.hpp"
// This file handles text in tables.
HRESULT uiprivLVN_GETDISPINFOText(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p)
{
uiTableData *data;
WCHAR *wstr;
HRESULT hr;
if ((nm->item.mask & LVIF_TEXT) == 0)
return S_OK;
if (p->textModelColumn != -1)
return S_OK;
data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn);
wstr = toUTF16(uiTableDataString(data));
uiFreeTableData(data);
// we could just make pszText into a freshly allocated conversion and avoid the limitation of cchTextMax
// but then we would have to keep things around for some amount of time (some pages on MSDN say 2 additional LVN_GETDISPINFO messages)
// and in practice, anything that results in extra LVN_GETDISPINFO messages (such as fillSubitemDrawParams() below) will break this counting
// TODO make it so we don't have to make a copy; instead we can convert directly into pszText (this will also avoid the risk of having a dangling surrogate pair at the end)
wcsncpy(nm->item.pszText, wstr, nm->item.cchTextMax);
nm->item.pszText[nm->item.cchTextMax - 1] = L'\0';
uiprivFree(wstr);
return S_OK;
}
HRESULT uiprivNM_CUSTOMDRAWText(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivTableColumnParams *p, uiprivSubitemDrawParams *dp)
{
COLORREF color;
COLORREF prev;
int prevMode;
RECT r;
uiTableData *data;
WCHAR *wstr;
if (p->textModelColumn == -1)
return S_OK;
if (dp->selected)
color = GetSysColor(COLOR_HIGHLIGHTTEXT);
else if (p->textParams.ColorModelColumn != -1)
color = uiprivTableBlendedColorFromModel(t, nm, p->textParams.ColorModelColumn, COLOR_WINDOWTEXT);
else
color = GetSysColor(COLOR_WINDOWTEXT);
prev = SetTextColor(nm->nmcd.hdc, color);
if (prev == CLR_INVALID) {
logLastError(L"SetTextColor()");
return E_FAIL;
}
prevMode = SetBkMode(nm->nmcd.hdc, TRANSPARENT);
if (prevMode == 0) {
logLastError(L"SetBkMode()");
return E_FAIL;
}
// text is actually drawn two logical units to the right of the beginning of the text rect
// TODO confirm this for the first column on both image and imageless cases
// TODO actually this whole thing is wrong for imageless columns
r = dp->label;
r.left += 2;
data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->nmcd.dwItemSpec, p->textModelColumn);
wstr = toUTF16(uiTableDataString(data));
uiFreeTableData(data);
// these flags are a menagerie of flags from various sources: guessing, the Windows 2000 source leak, various custom draw examples on the web, etc.
// TODO find the real correct flags
if (DrawTextW(nm->nmcd.hdc, wstr, -1, &r, DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX | DT_EDITCONTROL) == 0) {
uiprivFree(wstr);
logLastError(L"DrawTextW()");
return E_FAIL;
}
uiprivFree(wstr);
// TODO decide once and for all what to compare to here and with SelectObject()
if (SetBkMode(nm->nmcd.hdc, prevMode) != TRANSPARENT) {
logLastError(L"SetBkMode() prev");
return E_FAIL;
}
if (SetTextColor(nm->nmcd.hdc, prev) != color) {
logLastError(L"SetTextColor() prev");
return E_FAIL;
}
return S_OK;
}