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:
parent
15bc55dd5e
commit
caa0688687
|
@ -52,6 +52,7 @@ list(APPEND _LIBUI_SOURCES
|
||||||
windows/tab.cpp
|
windows/tab.cpp
|
||||||
windows/table.cpp
|
windows/table.cpp
|
||||||
windows/tableimages.cpp
|
windows/tableimages.cpp
|
||||||
|
windows/tabletext.cpp
|
||||||
windows/tabpage.cpp
|
windows/tabpage.cpp
|
||||||
windows/text.cpp
|
windows/text.cpp
|
||||||
windows/utf16.cpp
|
windows/utf16.cpp
|
||||||
|
|
|
@ -79,24 +79,13 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex)
|
||||||
static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm)
|
static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm)
|
||||||
{
|
{
|
||||||
static uiprivTableColumnParams *p;
|
static uiprivTableColumnParams *p;
|
||||||
uiTableData *data;
|
|
||||||
WCHAR *wstr;
|
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
p = (*(t->columns))[nm->item.iSubItem];
|
p = (*(t->columns))[nm->item.iSubItem];
|
||||||
if ((nm->item.mask & LVIF_TEXT) != 0)
|
hr = uiprivLVN_GETDISPINFOText(t, nm, p);
|
||||||
if (p->textModelColumn != -1) {
|
if (hr != S_OK) {
|
||||||
data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn);
|
// TODO
|
||||||
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_GETDISPINFOImagesCheckboxes(t, nm, p);
|
hr = uiprivLVN_GETDISPINFOImagesCheckboxes(t, nm, p);
|
||||||
if (hr != S_OK) {
|
if (hr != S_OK) {
|
||||||
// TODO
|
// TODO
|
||||||
|
@ -126,31 +115,47 @@ static COLORREF blend(COLORREF base, double r, double g, double b, double a)
|
||||||
(BYTE) (bb * 255));
|
(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;
|
RECT r;
|
||||||
HRESULT hr;
|
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) {
|
if (nm->iSubItem == 0) {
|
||||||
ZeroMemory(&r, sizeof (RECT));
|
ZeroMemory(&r, sizeof (RECT));
|
||||||
r.left = LVIR_BOUNDS;
|
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");
|
logLastError(L"LVM_GETITEMRECT LVIR_BOUNDS");
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
dp->bounds = r;
|
dp->bounds = r;
|
||||||
ZeroMemory(&r, sizeof (RECT));
|
ZeroMemory(&r, sizeof (RECT));
|
||||||
r.left = LVIR_ICON;
|
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");
|
logLastError(L"LVM_GETITEMRECT LVIR_ICON");
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
dp->icon = r;
|
dp->icon = r;
|
||||||
ZeroMemory(&r, sizeof (RECT));
|
ZeroMemory(&r, sizeof (RECT));
|
||||||
r.left = LVIR_LABEL;
|
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");
|
logLastError(L"LVM_GETITEMRECT LVIR_LABEL");
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
@ -161,7 +166,7 @@ static HRESULT fillSubitemDrawParams(HWND hwnd, NMLVCUSTOMDRAW *nm, uiprivSubite
|
||||||
ZeroMemory(&r, sizeof (RECT));
|
ZeroMemory(&r, sizeof (RECT));
|
||||||
r.left = LVIR_BOUNDS;
|
r.left = LVIR_BOUNDS;
|
||||||
r.top = nm->iSubItem;
|
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");
|
logLastError(L"LVM_GETSUBITEMRECT LVIR_BOUNDS");
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
@ -169,7 +174,7 @@ static HRESULT fillSubitemDrawParams(HWND hwnd, NMLVCUSTOMDRAW *nm, uiprivSubite
|
||||||
ZeroMemory(&r, sizeof (RECT));
|
ZeroMemory(&r, sizeof (RECT));
|
||||||
r.left = LVIR_ICON;
|
r.left = LVIR_ICON;
|
||||||
r.top = nm->iSubItem;
|
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");
|
logLastError(L"LVM_GETSUBITEMRECT LVIR_ICON");
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
@ -229,8 +234,7 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm)
|
||||||
logLastError(L"DeleteObject()");
|
logLastError(L"DeleteObject()");
|
||||||
}
|
}
|
||||||
t->clrItemText = nm->clrText;
|
t->clrItemText = nm->clrText;
|
||||||
ret = CDRF_NEWFONT | CDRF_NOTIFYSUBITEMDRAW;
|
return CDRF_NEWFONT | CDRF_NOTIFYSUBITEMDRAW;
|
||||||
break;
|
|
||||||
case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
|
case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
|
||||||
p = (*(t->columns))[nm->iSubItem];
|
p = (*(t->columns))[nm->iSubItem];
|
||||||
// TODO none of this runs on the first item
|
// 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
|
// TODO draw background on image columns if needed
|
||||||
ret = /*CDRF_SKIPDEFAULT | */CDRF_NEWFONT;
|
ret = CDRF_SKIPDEFAULT | CDRF_NEWFONT;
|
||||||
break;
|
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:
|
default:
|
||||||
return CDRF_DODEFAULT;
|
return CDRF_DODEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
ZeroMemory(&dp, sizeof (uiprivSubitemDrawParams));
|
ZeroMemory(&dp, sizeof (uiprivSubitemDrawParams));
|
||||||
hr = fillSubitemDrawParams(t->hwnd, nm, &dp);
|
hr = fillSubitemDrawParams(t, nm, &dp);
|
||||||
if (hr != S_OK) {
|
if (hr != S_OK) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
@ -272,6 +264,10 @@ DrawTextW(nm->nmcd.hdc, L"Part", -1,
|
||||||
if (hr != S_OK) {
|
if (hr != S_OK) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
hr = uiprivNM_CUSTOMDRAWText(t, nm, p, &dp);
|
||||||
|
if (hr != S_OK) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,11 +40,17 @@ struct uiTable {
|
||||||
};
|
};
|
||||||
typedef struct uiprivSubitemDrawParams uiprivSubitemDrawParams;
|
typedef struct uiprivSubitemDrawParams uiprivSubitemDrawParams;
|
||||||
struct uiprivSubitemDrawParams {
|
struct uiprivSubitemDrawParams {
|
||||||
|
bool selected;
|
||||||
RECT bounds;
|
RECT bounds;
|
||||||
RECT icon;
|
RECT icon;
|
||||||
RECT label;
|
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
|
// tableimages.cpp
|
||||||
extern HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p);
|
extern HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p);
|
||||||
extern HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp);
|
extern HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp);
|
||||||
|
|
|
@ -95,8 +95,14 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip
|
||||||
uiTableData *data;
|
uiTableData *data;
|
||||||
HRESULT hr;
|
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)
|
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
|
return S_OK; // nothing to do here
|
||||||
|
|
||||||
if (p->imageModelColumn != -1) {
|
if (p->imageModelColumn != -1) {
|
||||||
|
@ -122,16 +128,8 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we got here, there's no image in this cell
|
// TODO see if this is correct
|
||||||
nm->item.mask &= ~LVIF_IMAGE;
|
nm->item.iImage = -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...
|
|
||||||
// TODO it doesn't work anymore...
|
|
||||||
if (nm->item.iSubItem == 0) {
|
|
||||||
nm->item.mask |= LVIF_INDENT;
|
|
||||||
nm->item.iIndent = -1;
|
|
||||||
}
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue