Update to v094r40 release.

byuu says:

Changelog:
- updated to newest hiro API
- SFC performance profile builds once again
- hiro: Qt port completed

Errata 1: the hiro/Qt target won't run tomoko just yet. Starts by
crashing inside InputSettings because hiro/Qt isn't forcefully selecting
the first item added to a ComboButton just yet. Even with a monkey patch
to get around that, the UI is incredibly unstable. Lots of geometry
calculation bugs, and a crash when you try and access certain folders in
the browser dialog. Lots of work left to be done there, sadly.

Errata 2: the hiro/Windows port has black backgrounds on all ListView
items. It's because I need to test for unassigned colors and grab the
default Windows brush colors in those cases.

Note: alternating row colors on multi-column ListView widgets is gone
now. Not a bug. May add it back later, but I'm not sure. It doesn't
interact nicely with per-cell background colors.

Things left to do:

First, I have to fix the Windows and Qt target bugs.

Next, I need to go through and revise the hiro API even more (nothing
too major.)

Next, I need to update icarus to use the new hiro API, and add support
for the SFC games database.

Next, I have to rewrite my TSV->BML cheat code tool.

Next, I need to post a final WIP of higan+icarus publicly and wait a few
days.

Next, I need to fix any bugs reported from the final WIP that I can.

Finally, I should be able to release v095.
This commit is contained in:
Tim Allen
2015-08-18 20:18:00 +10:00
parent 0271d6a12b
commit 4344b916b6
200 changed files with 7246 additions and 5659 deletions

View File

@@ -43,6 +43,7 @@
#include "widget/label.cpp"
#include "widget/line-edit.cpp"
#include "widget/list-view.cpp"
#include "widget/list-view-header.cpp"
#include "widget/list-view-column.cpp"
#include "widget/list-view-item.cpp"
#include "widget/list-view-cell.cpp"

View File

@@ -72,6 +72,7 @@ static vector<wObject> windows;
#include "widget/label.hpp"
#include "widget/line-edit.hpp"
#include "widget/list-view.hpp"
#include "widget/list-view-header.hpp"
#include "widget/list-view-column.hpp"
#include "widget/list-view-item.hpp"
#include "widget/list-view-cell.hpp"

View File

@@ -301,6 +301,10 @@ static auto CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT ms
listView->self()->onSort(lparam);
break;
}
if(header->code == NM_CLICK || header->code == NM_DBLCLK) {
listView->self()->onToggle(lparam);
break;
}
if(header->code == NM_RCLICK) {
listView->self()->onContext(lparam);
break;

View File

@@ -73,9 +73,9 @@ auto pHexEdit::construct() -> void {
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)HexEdit_windowProc);
pWidget::_setState();
setAddress(state().address);
setBackgroundColor(state().backgroundColor);
setLength(state().length);
setOffset(state().offset);
update();
PostMessage(hwnd, EM_SETSEL, 10, 10);
}
@@ -84,6 +84,11 @@ auto pHexEdit::destruct() -> void {
DestroyWindow(hwnd);
}
auto pHexEdit::setAddress(unsigned address) -> void {
SetScrollPos(scrollBar, SB_CTL, address / state().columns, true);
update();
}
auto pHexEdit::setBackgroundColor(Color color) -> void {
if(backgroundBrush) DeleteObject(backgroundBrush);
backgroundBrush = CreateSolidBrush(color ? CreateRGB(color) : GetSysColor(COLOR_WINDOW));
@@ -102,11 +107,6 @@ auto pHexEdit::setLength(unsigned length) -> void {
update();
}
auto pHexEdit::setOffset(unsigned offset) -> void {
SetScrollPos(scrollBar, SB_CTL, offset / state().columns, true);
update();
}
auto pHexEdit::setRows(unsigned rows) -> void {
update();
}
@@ -120,16 +120,16 @@ auto pHexEdit::update() -> void {
unsigned cursorPosition = Edit_GetSel(hwnd);
string output;
unsigned offset = state().offset;
unsigned address = state().address;
for(auto row : range(state().rows)) {
output.append(hex(offset, 8L));
output.append(hex(address, 8L));
output.append(" ");
string hexdata;
string ansidata = " ";
for(auto column : range(state().columns)) {
if(offset < state().length) {
uint8_t data = self().doRead(offset++);
if(address < state().length) {
uint8_t data = self().doRead(address++);
hexdata.append(hex(data, 2L));
hexdata.append(" ");
ansidata.append(data >= 0x20 && data <= 0x7e ? (char)data : '.');
@@ -141,7 +141,7 @@ auto pHexEdit::update() -> void {
output.append(hexdata);
output.append(ansidata);
if(offset >= state().length) break;
if(address >= state().length) break;
if(row != state().rows - 1) output.append("\r\n");
}
@@ -199,7 +199,7 @@ bool pHexEdit::keyPress(unsigned scancode) {
else return false;
if(cursorX >= 10) {
//not on an offset
//not on an address
cursorX -= 10;
if((cursorX % 3) != 2) {
//not on a space
@@ -207,10 +207,10 @@ bool pHexEdit::keyPress(unsigned scancode) {
cursorX /= 3;
if(cursorX < state().columns) {
//not in ANSI region
unsigned offset = state().offset + (cursorY * state().columns + cursorX);
unsigned address = state().address + (cursorY * state().columns + cursorX);
if(offset >= state().length) return false; //do not edit past end of data
uint8_t data = self().doRead(offset);
if(address >= state().length) return false; //do not edit past end of data
uint8_t data = self().doRead(address);
//write modified value
if(cursorNibble == 1) {
@@ -218,7 +218,7 @@ bool pHexEdit::keyPress(unsigned scancode) {
} else {
data = (data & 0x0f) | (scancode << 4);
}
self().doWrite(offset, data);
self().doWrite(address, data);
//auto-advance cursor to next nibble or byte
position++;
@@ -243,14 +243,14 @@ signed pHexEdit::rowsScrollable() {
}
signed pHexEdit::scrollPosition() {
return state().offset / state().columns;
return state().address / state().columns;
}
void pHexEdit::scrollTo(signed position) {
if(position > rowsScrollable()) position = rowsScrollable();
if(position < 0) position = 0;
if(position == scrollPosition()) return;
self().setOffset(position * state().columns);
self().setAddress(position * state().columns);
}
}

View File

@@ -5,11 +5,11 @@ namespace hiro {
struct pHexEdit : pWidget {
Declare(HexEdit, Widget)
auto setAddress(unsigned address) -> void;
auto setBackgroundColor(Color color) -> void;
auto setColumns(unsigned columns) -> void;
auto setForegroundColor(Color color) -> void;
auto setLength(unsigned length) -> void;
auto setOffset(unsigned offset) -> void;
auto setRows(unsigned rows) -> void;
auto update() -> void;

View File

@@ -8,6 +8,7 @@ auto pLabel::construct() -> void {
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
setAlignment(state().alignment);
setText(state().text);
}
@@ -16,11 +17,12 @@ auto pLabel::destruct() -> void {
}
auto pLabel::minimumSize() const -> Size {
Size size = pFont::size(hfont, state().text);
auto size = pFont::size(hfont, state().text);
return {size.width(), size.height()};
}
auto pLabel::setHorizontalAlignment(double alignment) -> void {
auto pLabel::setAlignment(Alignment alignment) -> void {
InvalidateRect(hwnd, 0, false);
}
auto pLabel::setText(const string& text) -> void {
@@ -28,9 +30,6 @@ auto pLabel::setText(const string& text) -> void {
InvalidateRect(hwnd, 0, false);
}
auto pLabel::setVerticalAlignment(double alignment) -> void {
}
static auto CALLBACK Label_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
auto label = (mLabel*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(!label) return DefWindowProc(hwnd, msg, wparam, lparam);

View File

@@ -6,9 +6,8 @@ struct pLabel : pWidget {
Declare(Label, Widget)
auto minimumSize() const -> Size override;
auto setHorizontalAlignment(double alignment) -> void;
auto setAlignment(Alignment alignment) -> void;
auto setText(const string& text) -> void;
auto setVerticalAlignment(double alignment) -> void;
};
}

View File

@@ -8,22 +8,31 @@ auto pListViewCell::construct() -> void {
auto pListViewCell::destruct() -> void {
}
auto pListViewCell::setAlignment(Alignment alignment) -> void {
}
auto pListViewCell::setBackgroundColor(Color color) -> void {
_repaint();
}
auto pListViewCell::setCheckable(bool checkable) -> void {
_repaint();
}
auto pListViewCell::setChecked(bool checked) -> void {
_repaint();
}
auto pListViewCell::setForegroundColor(Color color) -> void {
_repaint();
}
auto pListViewCell::setIcon(const image& icon) -> void {
_repaint();
}
auto pListViewCell::setText(const string& text) -> void {
if(auto parent = _parent()) {
if(auto listView = parent->_parent()) {
//ListView uses a custom drawing routine; so we need to tell the control to repaint itself manually
PostMessageOnce(listView->_parentHandle(), AppMessage::ListView_doPaint, 0, (LPARAM)&listView->reference);
}
}
_repaint();
}
auto pListViewCell::_parent() -> maybe<pListViewItem&> {
@@ -33,16 +42,24 @@ auto pListViewCell::_parent() -> maybe<pListViewItem&> {
return nothing;
}
auto pListViewCell::_repaint() -> void {
if(auto parent = _parent()) {
if(auto listView = parent->_parent()) {
//ListView uses a custom drawing routine; so we need to tell the control to repaint itself manually
PostMessageOnce(listView->_parentHandle(), AppMessage::ListView_doPaint, 0, (LPARAM)&listView->reference);
}
}
}
auto pListViewCell::_setState() -> void {
if(auto item = _parent()) {
if(auto parent = item->_parent()) {
parent->lock();
wchar_t text[] = L"";
LVITEM lvItem;
lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
lvItem.mask = LVIF_TEXT;
lvItem.iItem = item->self().offset();
lvItem.iSubItem = self().offset();
lvItem.iImage = parent->self().columns();
lvItem.pszText = text;
ListView_SetItem(parent->hwnd, &lvItem);
parent->unlock();

View File

@@ -5,12 +5,16 @@ namespace hiro {
struct pListViewCell : pObject {
Declare(ListViewCell, Object)
auto setAlignment(Alignment alignment) -> void;
auto setBackgroundColor(Color color) -> void;
auto setCheckable(bool checkable) -> void;
auto setChecked(bool checked) -> void;
auto setForegroundColor(Color color) -> void;
auto setIcon(const image& icon) -> void;
auto setText(const string& text) -> void;
auto _parent() -> maybe<pListViewItem&>;
auto _repaint() -> void;
auto _setState() -> void;
};

View File

@@ -3,15 +3,35 @@
namespace hiro {
auto pListViewColumn::construct() -> void {
if(auto grandparent = _grandparent()) {
grandparent->lock();
wchar_t text[] = L"";
LVCOLUMN lvColumn{0};
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.iSubItem = self().offset();
lvColumn.pszText = text;
ListView_InsertColumn(grandparent->hwnd, self().offset(), &lvColumn);
_setState();
grandparent->unlock();
}
}
auto pListViewColumn::destruct() -> void {
if(auto grandparent = _grandparent()) {
grandparent->lock();
ListView_DeleteColumn(grandparent->hwnd, self().offset());
grandparent->unlock();
}
}
auto pListViewColumn::setActive() -> void {
//unsupported
}
auto pListViewColumn::setAlignment(Alignment alignment) -> void {
}
auto pListViewColumn::setBackgroundColor(Color color) -> void {
}
@@ -37,6 +57,9 @@ auto pListViewColumn::setResizable(bool resizable) -> void {
_setState();
}
auto pListViewColumn::setSortable(bool sortable) -> void {
}
auto pListViewColumn::setText(const string& text) -> void {
_setState();
}
@@ -48,17 +71,22 @@ auto pListViewColumn::setWidth(signed width) -> void {
_setState();
}
auto pListViewColumn::_parent() -> maybe<pListView&> {
if(auto parent = self().parentListView()) {
auto pListViewColumn::_grandparent() -> maybe<pListView&> {
if(auto parent = _parent()) return parent->_parent();
return nothing;
}
auto pListViewColumn::_parent() -> maybe<pListViewHeader&> {
if(auto parent = self().parentListViewHeader()) {
if(auto self = parent->self()) return *self;
}
return nothing;
}
auto pListViewColumn::_setState() -> void {
if(auto parent = _parent()) {
parent->lock();
parent->_setIcons();
if(auto grandparent = _grandparent()) {
grandparent->lock();
grandparent->_setIcons();
utf16_t text(state().text);
LVCOLUMN lvColumn;
lvColumn.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH;
@@ -71,8 +99,8 @@ auto pListViewColumn::_setState() -> void {
if(state().horizontalAlignment > 0.666) lvColumn.fmt = LVCFMT_RIGHT;
if(state().icon) lvColumn.mask |= LVCF_IMAGE;
if(!state().resizable) lvColumn.fmt |= LVCFMT_FIXED_WIDTH;
ListView_SetColumn(parent->hwnd, self().offset(), &lvColumn);
parent->unlock();
ListView_SetColumn(grandparent->hwnd, self().offset(), &lvColumn);
grandparent->unlock();
}
}

View File

@@ -6,6 +6,7 @@ struct pListViewColumn : pObject {
Declare(ListViewColumn, Object)
auto setActive() -> void;
auto setAlignment(Alignment alignment) -> void;
auto setBackgroundColor(Color color) -> void;
auto setEditable(bool editable) -> void;
auto setExpandable(bool expandable) -> void;
@@ -13,11 +14,13 @@ struct pListViewColumn : pObject {
auto setHorizontalAlignment(double alignment) -> void;
auto setIcon(const image& icon) -> void;
auto setResizable(bool resizable) -> void;
auto setSortable(bool sortable) -> void;
auto setText(const string& text) -> void;
auto setVerticalAlignment(double alignment) -> void;
auto setWidth(signed width) -> void;
auto _parent() -> maybe<pListView&>;
auto _grandparent() -> maybe<pListView&>;
auto _parent() -> maybe<pListViewHeader&>;
auto _setState() -> void;
signed _width = 128; //computed width (via ListView::resizeColumns)

View File

@@ -0,0 +1,42 @@
#if defined(Hiro_ListView)
namespace hiro {
auto pListViewHeader::construct() -> void {
_setState();
}
auto pListViewHeader::destruct() -> void {
}
auto pListViewHeader::append(sListViewColumn column) -> void {
}
auto pListViewHeader::remove(sListViewColumn column) -> void {
}
auto pListViewHeader::setVisible(bool visible) -> void {
_setState();
}
auto pListViewHeader::_parent() -> maybe<pListView&> {
if(auto parent = self().parentListView()) {
if(auto self = parent->self()) return *self;
}
return nothing;
}
auto pListViewHeader::_setState() -> void {
if(auto parent = _parent()) {
auto style = GetWindowLong(parent->hwnd, GWL_STYLE);
self().visible() ? style &=~ LVS_NOCOLUMNHEADER : style |= LVS_NOCOLUMNHEADER;
SetWindowLong(parent->hwnd, GWL_STYLE, style);
for(auto& column : state().columns) {
if(auto self = column->self()) self->_setState();
}
}
}
}
#endif

View File

@@ -0,0 +1,18 @@
#if defined(Hiro_ListView)
namespace hiro {
struct pListViewHeader : pObject {
Declare(ListViewHeader, Object)
auto append(sListViewColumn column) -> void;
auto remove(sListViewColumn column) -> void;
auto setVisible(bool visible) -> void override;
auto _parent() -> maybe<pListView&>;
auto _setState() -> void;
};
}
#endif

View File

@@ -3,9 +3,26 @@
namespace hiro {
auto pListViewItem::construct() -> void {
if(auto parent = _parent()) {
parent->lock();
wchar_t text[] = L"";
LVITEM lvItem{0};
lvItem.mask = LVIF_TEXT;
lvItem.iItem = self().offset();
lvItem.iSubItem = 0;
lvItem.pszText = text;
ListView_InsertItem(parent->hwnd, &lvItem);
_setState();
parent->unlock();
}
}
auto pListViewItem::destruct() -> void {
if(auto parent = _parent()) {
parent->lock();
ListView_DeleteItem(parent->hwnd, self().offset());
parent->unlock();
}
}
auto pListViewItem::append(sListViewCell cell) -> void {
@@ -14,20 +31,12 @@ auto pListViewItem::append(sListViewCell cell) -> void {
auto pListViewItem::remove(sListViewCell cell) -> void {
}
auto pListViewItem::setAlignment(Alignment alignment) -> void {
}
auto pListViewItem::setBackgroundColor(Color color) -> void {
}
auto pListViewItem::setCheckable(bool checkable) -> void {
}
auto pListViewItem::setChecked(bool checked) -> void {
if(auto parent = _parent()) {
parent->lock();
ListView_SetCheckState(parent->hwnd, self().offset(), checked);
parent->unlock();
}
}
auto pListViewItem::setFocused() -> void {
if(auto parent = _parent()) {
parent->lock();
@@ -40,12 +49,7 @@ auto pListViewItem::setForegroundColor(Color color) -> void {
}
auto pListViewItem::setSelected(bool selected) -> void {
if(auto parent = _parent()) {
parent->lock();
unsigned state = selected ? LVIS_SELECTED : 0;
ListView_SetItemState(parent->hwnd, self().offset(), state, LVIS_SELECTED);
parent->unlock();
}
_setState();
}
auto pListViewItem::_parent() -> maybe<pListView&> {
@@ -55,6 +59,17 @@ auto pListViewItem::_parent() -> maybe<pListView&> {
return nothing;
}
auto pListViewItem::_setState() -> void {
if(auto parent = _parent()) {
parent->lock();
ListView_SetItemState(parent->hwnd, self().offset(), state().selected ? LVIS_SELECTED : 0, LVIS_SELECTED);
for(auto& cell : state().cells) {
if(auto self = cell->self()) self->_setState();
}
parent->unlock();
}
}
}
#endif

View File

@@ -7,14 +7,14 @@ struct pListViewItem : pObject {
auto append(sListViewCell cell) -> void;
auto remove(sListViewCell cell) -> void;
auto setAlignment(Alignment alignment) -> void;
auto setBackgroundColor(Color color) -> void;
auto setCheckable(bool checkable) -> void;
auto setChecked(bool checked) -> void;
auto setFocused() -> void;
auto setForegroundColor(Color color) -> void;
auto setSelected(bool selected) -> void;
auto _parent() -> maybe<pListView&>;
auto _setState() -> void;
};
}

View File

@@ -34,72 +34,27 @@ auto pListView::construct() -> void {
pWidget::_setState();
setBackgroundColor(state().backgroundColor);
setBatchable(state().batchable);
setCheckable(state().checkable);
setGridVisible(state().gridVisible);
setHeaderVisible(state().headerVisible);
setSortable(state().sortable);
setBordered(state().bordered);
_setIcons();
_setSortable();
resizeColumns();
}
auto pListView::destruct() -> void {
if(imageList) { ImageList_Destroy(imageList); imageList = 0; }
if(imageList) { ImageList_Destroy(imageList); imageList = nullptr; }
DestroyWindow(hwnd);
}
auto pListView::append(sListViewColumn column) -> void {
lock();
wchar_t text[] = L"";
LVCOLUMN lvColumn;
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.iSubItem = column->offset();
lvColumn.pszText = text;
ListView_InsertColumn(hwnd, column->offset(), &lvColumn);
if(auto self = column->self()) {
self->_setState();
}
resizeColumns();
unlock();
auto pListView::append(sListViewHeader header) -> void {
}
auto pListView::append(sListViewItem item) -> void {
lock();
wchar_t text[] = L"";
LVITEM lvItem;
lvItem.mask = LVIF_TEXT;
lvItem.iItem = item->offset();
lvItem.iSubItem = 0;
lvItem.pszText = text;
ListView_InsertItem(hwnd, &lvItem);
if(auto self = item->self()) {
self->setChecked(item->state.checked);
self->setSelected(item->state.selected);
}
for(auto& cell : item->state.cells) {
if(auto self = cell->self()) {
self->_setState();
}
}
unlock();
}
auto pListView::checkAll() -> void {
for(auto& item : state().items) {
item->self()->setChecked(true);
}
}
auto pListView::remove(sListViewColumn column) -> void {
lock();
ListView_DeleteColumn(hwnd, column->offset());
unlock();
auto pListView::remove(sListViewHeader header) -> void {
}
auto pListView::remove(sListViewItem item) -> void {
lock();
ListView_DeleteItem(hwnd, item->offset());
unlock();
}
auto pListView::reset() -> void {
@@ -114,45 +69,44 @@ auto pListView::reset() -> void {
auto pListView::resizeColumns() -> void {
lock();
vector<signed> widths;
signed minimumWidth = 0;
signed expandable = 0;
for(auto column : range(state().columns)) {
signed width = _width(column);
widths.append(width);
minimumWidth += width;
if(state().columns[column]->expandable()) expandable++;
}
signed maximumWidth = self().geometry().width() - 4;
SCROLLBARINFO sbInfo{sizeof(SCROLLBARINFO)};
if(GetScrollBarInfo(hwnd, OBJID_VSCROLL, &sbInfo)) {
if(!(sbInfo.rgstate[0] & STATE_SYSTEM_INVISIBLE)) {
maximumWidth -= sbInfo.rcScrollBar.right - sbInfo.rcScrollBar.left;
if(auto& header = state().header) {
vector<signed> widths;
signed minimumWidth = 0;
signed expandable = 0;
for(auto column : range(header->columnCount())) {
signed width = _width(column);
widths.append(width);
minimumWidth += width;
if(header->column(column).expandable()) expandable++;
}
}
signed expandWidth = 0;
if(expandable && maximumWidth > minimumWidth) {
expandWidth = (maximumWidth - minimumWidth) / expandable;
}
signed maximumWidth = self().geometry().width() - 4;
SCROLLBARINFO sbInfo{sizeof(SCROLLBARINFO)};
if(GetScrollBarInfo(hwnd, OBJID_VSCROLL, &sbInfo)) {
if(!(sbInfo.rgstate[0] & STATE_SYSTEM_INVISIBLE)) {
maximumWidth -= sbInfo.rcScrollBar.right - sbInfo.rcScrollBar.left;
}
}
for(auto column : range(state().columns)) {
if(auto self = state().columns[column]->self()) {
signed width = widths[column];
if(self->state().expandable) width += expandWidth;
self->_width = width;
self->_setState();
signed expandWidth = 0;
if(expandable && maximumWidth > minimumWidth) {
expandWidth = (maximumWidth - minimumWidth) / expandable;
}
for(auto column : range(header->columnCount())) {
if(auto self = header->state.columns[column]->self()) {
signed width = widths[column];
if(self->state().expandable) width += expandWidth;
self->_width = width;
self->_setState();
}
}
}
unlock();
}
auto pListView::selectAll() -> void {
lock();
ListView_SetItemState(hwnd, -1, LVIS_SELECTED, LVIS_SELECTED);
unlock();
auto pListView::setAlignment(Alignment alignment) -> void {
}
auto pListView::setBackgroundColor(Color color) -> void {
@@ -166,42 +120,27 @@ auto pListView::setBatchable(bool batchable) -> void {
SetWindowLong(hwnd, GWL_STYLE, style);
}
auto pListView::setCheckable(bool checkable) -> void {
auto style = ListView_GetExtendedListViewStyle(hwnd);
checkable ? style |= LVS_EX_CHECKBOXES : style &=~ LVS_EX_CHECKBOXES;
ListView_SetExtendedListViewStyle(hwnd, style);
auto pListView::setBordered(bool bordered) -> void {
//rendered via onCustomDraw
}
auto pListView::setForegroundColor(Color color) -> void {
}
auto pListView::setGridVisible(bool visible) -> void {
//rendered via onCustomDraw
}
auto pListView::setHeaderVisible(bool visible) -> void {
auto style = GetWindowLong(hwnd, GWL_STYLE);
!visible ? style |= LVS_NOCOLUMNHEADER : style &=~ LVS_NOCOLUMNHEADER;
SetWindowLong(hwnd, GWL_STYLE, style);
}
auto pListView::setSortable(bool sortable) -> void {
auto style = GetWindowLong(hwnd, GWL_STYLE);
!sortable ? style |= LVS_NOSORTHEADER : style &=~ LVS_NOSORTHEADER;
SetWindowLong(hwnd, GWL_STYLE, style);
}
auto pListView::uncheckAll() -> void {
for(auto& item : state().items) {
item->self()->setChecked(false);
auto pListView::setGeometry(Geometry geometry) -> void {
pWidget::setGeometry(geometry);
if(auto& header = state().header) {
for(auto& column : header->state.columns) {
if(column->state.expandable) return resizeColumns();
}
}
}
auto pListView::unselectAll() -> void {
lock();
ListView_SetItemState(hwnd, -1, 0, LVIS_FOCUSED | LVIS_SELECTED);
unlock();
}
/*auto pListView::setHeaderVisible(bool visible) -> void {
auto style = GetWindowLong(hwnd, GWL_STYLE);
!visible ? style |= LVS_NOCOLUMNHEADER : style &=~ LVS_NOCOLUMNHEADER;
SetWindowLong(hwnd, GWL_STYLE, style);
}*/
auto pListView::onActivate(LPARAM lparam) -> void {
auto nmlistview = (LPNMLISTVIEW)lparam;
@@ -231,20 +170,6 @@ auto pListView::onChange(LPARAM lparam) -> void {
//we use a delayed AppMessage so that only one callback event is fired off
PostMessageOnce(_parentHandle(), AppMessage::ListView_onChange, 0, (LPARAM)&reference);
}
if(!locked()) {
unsigned row = nmlistview->iItem;
unsigned mask = nmlistview->uNewState & LVIS_STATEIMAGEMASK;
if(mask == 0x1000 || mask == 0x2000) {
bool checked = mask == 0x2000;
if(auto item = self().item(row)) {
if(checked != item->state.checked) { //WC_LISTVIEW sends this message twice
item->state.checked = checked;
self().doToggle(item);
}
}
}
}
}
auto pListView::onContext(LPARAM lparam) -> void {
@@ -264,59 +189,71 @@ auto pListView::onCustomDraw(LPARAM lparam) -> LRESULT {
HDC hdc = lvcd->nmcd.hdc;
HDC hdcSource = CreateCompatibleDC(hdc);
unsigned row = lvcd->nmcd.dwItemSpec;
for(auto column : range(state().columns)) {
auto& header = state().header;
if(!header) break;
for(auto column : range(header->columnCount())) {
RECT rc, rcLabel;
ListView_GetSubItemRect(hwnd, row, column, LVIR_BOUNDS, &rc);
ListView_GetSubItemRect(hwnd, row, column, LVIR_LABEL, &rcLabel);
rc.right = rcLabel.right; //bounds of column 0 returns width of entire item
signed iconSize = rc.bottom - rc.top - 1;
bool checked = state().items(row)->state.checked;
bool selected = state().items(row)->state.selected;
HBRUSH brush = CreateSolidBrush(selected ? GetSysColor(COLOR_HIGHLIGHT) : CreateRGB(_backgroundColor(row, column)));
FillRect(hdc, &rc, brush);
DeleteObject(brush);
if(state().checkable && self().item(row).checkable() && column == 0) {
if(auto htheme = OpenThemeData(hwnd, L"BUTTON")) {
unsigned state = checked ? CBS_CHECKEDNORMAL : CBS_UNCHECKEDNORMAL;
SIZE size;
GetThemePartSize(htheme, hdc, BP_CHECKBOX, state, NULL, TS_TRUE, &size);
signed center = max(0, (rc.bottom - rc.top - size.cy) / 2);
RECT rd{rc.left + center, rc.top + center, rc.left + center + size.cx, rc.top + center + size.cy};
DrawThemeBackground(htheme, hdc, BP_CHECKBOX, state, &rd, NULL);
CloseThemeData(htheme);
if(auto cell = self().item(row)->cell(column)) {
HBRUSH brush = CreateSolidBrush(selected ? GetSysColor(COLOR_HIGHLIGHT) : CreateRGB(cell->backgroundColor(true)));
FillRect(hdc, &rc, brush);
DeleteObject(brush);
if(cell->state.checkable) {
if(auto htheme = OpenThemeData(hwnd, L"BUTTON")) {
unsigned state = cell->state.checked ? CBS_CHECKEDNORMAL : CBS_UNCHECKEDNORMAL;
SIZE size;
GetThemePartSize(htheme, hdc, BP_CHECKBOX, state, nullptr, TS_TRUE, &size);
signed center = max(0, (rc.bottom - rc.top - size.cy) / 2);
RECT rd{rc.left + center, rc.top + center, rc.left + center + size.cx, rc.top + center + size.cy};
DrawThemeBackground(htheme, hdc, BP_CHECKBOX, state, &rd, nullptr);
CloseThemeData(htheme);
}
rc.left += iconSize + 2;
} else {
rc.left += 2;
}
if(auto icon = cell->state.icon) {
icon.scale(iconSize, iconSize);
icon.transform();
auto bitmap = CreateBitmap(icon);
SelectBitmap(hdcSource, bitmap);
BLENDFUNCTION blend{AC_SRC_OVER, 0, (BYTE)(selected ? 128 : 255), AC_SRC_ALPHA};
AlphaBlend(hdc, rc.left, rc.top, iconSize, iconSize, hdcSource, 0, 0, iconSize, iconSize, blend);
DeleteObject(bitmap);
rc.left += iconSize + 2;
}
if(auto text = cell->state.text) {
auto alignment = cell->alignment(true);
if(!alignment) alignment = {0.0, 0.5};
utf16_t wText(text);
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, selected ? GetSysColor(COLOR_HIGHLIGHTTEXT) : CreateRGB(cell->foregroundColor(true)));
auto style = DT_SINGLELINE | DT_NOPREFIX | DT_END_ELLIPSIS;
style |= alignment.horizontal() < 0.333 ? DT_LEFT : alignment.horizontal() > 0.666 ? DT_RIGHT : DT_CENTER;
style |= alignment.vertical() < 0.333 ? DT_TOP : alignment.vertical() > 0.666 ? DT_BOTTOM : DT_VCENTER;
rc.right -= 2;
auto font = pFont::create(cell->font(true));
SelectObject(hdc, font);
DrawText(hdc, wText, -1, &rc, style);
DeleteObject(font);
}
rc.left += iconSize + 2;
} else {
rc.left += 2;
auto color = state().backgroundColor;
if(!color) color = {255, 255, 255};
HBRUSH brush = CreateSolidBrush(selected ? GetSysColor(COLOR_HIGHLIGHT) : CreateRGB(color));
FillRect(hdc, &rc, brush);
DeleteObject(brush);
}
auto cell = self().item(row)->cell(column);
if(!cell) continue;
if(auto icon = cell->state.icon) {
icon.scale(iconSize, iconSize);
icon.transform();
auto bitmap = CreateBitmap(icon);
SelectBitmap(hdcSource, bitmap);
BLENDFUNCTION blend{AC_SRC_OVER, 0, (BYTE)(selected ? 128 : 255), AC_SRC_ALPHA};
AlphaBlend(hdc, rc.left, rc.top, iconSize, iconSize, hdcSource, 0, 0, iconSize, iconSize, blend);
DeleteObject(bitmap);
rc.left += iconSize + 2;
}
if(auto text = cell->state.text) {
auto halign = state().columns(column)->state.horizontalAlignment;
auto valign = state().columns(column)->state.verticalAlignment;
utf16_t wText(text);
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, selected ? GetSysColor(COLOR_HIGHLIGHTTEXT) : CreateRGB(_foregroundColor(row, column)));
auto style = DT_SINGLELINE | DT_NOPREFIX | DT_END_ELLIPSIS;
style |= halign < 0.333 ? DT_LEFT : halign > 0.666 ? DT_RIGHT : DT_CENTER;
style |= valign < 0.333 ? DT_TOP : valign > 0.666 ? DT_BOTTOM : DT_VCENTER;
rc.right -= 2;
auto font = pFont::create(_font(row, column));
SelectObject(hdc, font);
DrawText(hdc, wText, -1, &rc, style);
DeleteObject(font);
}
if(state().gridVisible) {
if(state().bordered) {
ListView_GetSubItemRect(hwnd, row, column, LVIR_BOUNDS, &rc);
rc.top = rc.bottom - 1;
FillRect(hdc, &rc, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
@@ -329,11 +266,33 @@ auto pListView::onCustomDraw(LPARAM lparam) -> LRESULT {
return CDRF_SKIPDEFAULT;
}
}
return CDRF_SKIPDEFAULT;
}
auto pListView::onSort(LPARAM lparam) -> void {
auto nmlistview = (LPNMLISTVIEW)lparam;
self().doSort(self().column(nmlistview->iSubItem));
if(auto& header = state().header) {
if(auto column = header->column(nmlistview->iSubItem)) {
if(column->sortable()) self().doSort(column);
}
}
}
auto pListView::onToggle(LPARAM lparam) -> void {
auto itemActivate = (LPNMITEMACTIVATE)lparam;
LVHITTESTINFO hitTestInfo{0};
hitTestInfo.pt = itemActivate->ptAction;
ListView_SubItemHitTest(hwnd, &hitTestInfo);
if(auto cell = self().item(hitTestInfo.iItem).cell(hitTestInfo.iSubItem)) {
if(cell->state.checkable) {
cell->state.checked = !cell->state.checked;
if(!locked()) self().doToggle(cell);
//todo: try to find a way to only repaint this cell instead of the entire control to reduce flickering
PostMessageOnce(_parentHandle(), AppMessage::ListView_doPaint, 0, (LPARAM)&reference);
}
}
}
auto pListView::_backgroundColor(unsigned _row, unsigned _column) -> Color {
@@ -343,19 +302,21 @@ auto pListView::_backgroundColor(unsigned _row, unsigned _column) -> Color {
}
if(auto color = item->backgroundColor()) return color;
}
if(auto column = self().column(_column)) {
if(auto color = column->backgroundColor()) return color;
}
// if(auto column = self().column(_column)) {
// if(auto color = column->backgroundColor()) return color;
// }
if(auto color = self().backgroundColor()) return color;
if(state().columns.size() >= 2 && _row % 2) return {240, 240, 240};
// if(state().columns.size() >= 2 && _row % 2) return {240, 240, 240};
return {255, 255, 255};
}
auto pListView::_cellWidth(unsigned _row, unsigned _column) -> unsigned {
unsigned width = 6;
if(state().checkable && _column == 0) width += 16 + 2;
if(auto item = self().item(_row)) {
if(auto cell = item->cell(_column)) {
if(cell->state.checkable) {
width += 16 + 2;
}
if(auto& icon = cell->state.icon) {
width += 16 + 2;
}
@@ -369,12 +330,14 @@ auto pListView::_cellWidth(unsigned _row, unsigned _column) -> unsigned {
auto pListView::_columnWidth(unsigned _column) -> unsigned {
unsigned width = 12;
if(auto column = self().column(_column)) {
if(auto& icon = column->state.icon) {
width += 16 + 12; //yes; icon spacing in column headers is excessive
}
if(auto& text = column->state.text) {
width += Font::size(self().font(true), text).width();
if(auto header = state().header) {
if(auto column = header->column(_column)) {
if(auto& icon = column->state.icon) {
width += 16 + 12; //yes; icon spacing in column headers is excessive
}
if(auto& text = column->state.text) {
width += Font::size(self().font(true), text).width();
}
}
}
return width;
@@ -387,9 +350,9 @@ auto pListView::_font(unsigned _row, unsigned _column) -> string {
}
if(auto font = item->font()) return font;
}
if(auto column = self().column(_column)) {
if(auto font = column->font()) return font;
}
// if(auto column = self().column(_column)) {
// if(auto font = column->font()) return font;
// }
if(auto font = self().font(true)) return font;
return Font::sans(8);
}
@@ -401,31 +364,33 @@ auto pListView::_foregroundColor(unsigned _row, unsigned _column) -> Color {
}
if(auto color = item->foregroundColor()) return color;
}
if(auto column = self().column(_column)) {
if(auto color = column->foregroundColor()) return color;
}
// if(auto column = self().column(_column)) {
// if(auto color = column->foregroundColor()) return color;
// }
if(auto color = self().foregroundColor()) return color;
return {0, 0, 0};
}
auto pListView::_setIcons() -> void {
ListView_SetImageList(hwnd, NULL, LVSIL_SMALL);
ListView_SetImageList(hwnd, nullptr, LVSIL_SMALL);
if(imageList) ImageList_Destroy(imageList);
imageList = ImageList_Create(16, 16, ILC_COLOR32, 1, 0);
ListView_SetImageList(hwnd, imageList, LVSIL_SMALL);
for(auto column : range(state().columns)) {
auto icon = state().columns(column)->state.icon;
if(icon) {
icon.scale(16, 16);
icon.transform();
} else {
icon.allocate(16, 16);
icon.fill(0x00ffffff);
if(auto& header = state().header) {
for(auto column : range(header->columnCount())) {
auto icon = header->state.columns[column]->state.icon;
if(icon) {
icon.scale(16, 16);
icon.transform();
} else {
icon.allocate(16, 16);
icon.fill(0x00ffffff);
}
auto bitmap = CreateBitmap(icon);
ImageList_Add(imageList, bitmap, nullptr);
DeleteObject(bitmap);
}
auto bitmap = CreateBitmap(icon);
ImageList_Add(imageList, bitmap, NULL);
DeleteObject(bitmap);
}
//empty icon used for ListViewItems (drawn manually via onCustomDraw)
@@ -433,20 +398,35 @@ auto pListView::_setIcons() -> void {
icon.allocate(16, 16);
icon.fill(0x00ffffff);
auto bitmap = CreateBitmap(icon);
ImageList_Add(imageList, bitmap, NULL);
ImageList_Add(imageList, bitmap, nullptr);
DeleteObject(bitmap);
}
auto pListView::_setSortable() -> void {
bool sortable = false;
if(auto& header = state().header) {
for(auto& column : header->state.columns) {
if(column->sortable()) sortable = true;
}
}
//note: this won't change the visual style: WC_LISTVIEW caches this in CreateWindow
auto style = GetWindowLong(hwnd, GWL_STYLE);
!sortable ? style |= LVS_NOSORTHEADER : style &=~ LVS_NOSORTHEADER;
SetWindowLong(hwnd, GWL_STYLE, style);
}
auto pListView::_width(unsigned column) -> unsigned {
if(auto width = state().columns[column]->width()) return width;
unsigned width = 1;
if(state().headerVisible) {
width = max(width, _columnWidth(column));
if(auto& header = state().header) {
if(auto width = header->state.columns[column]->width()) return width;
unsigned width = 1;
if(header->visible()) width = max(width, _columnWidth(column));
for(auto row : range(state().items)) {
width = max(width, _cellWidth(row, column));
}
return width;
}
for(auto row : range(state().items)) {
width = max(width, _cellWidth(row, column));
}
return width;
return 1;
}
}

View File

@@ -5,30 +5,25 @@ namespace hiro {
struct pListView : pWidget {
Declare(ListView, Widget)
auto append(sListViewColumn column) -> void;
auto append(sListViewHeader header) -> void;
auto append(sListViewItem item) -> void;
auto checkAll() -> void;
auto remove(sListViewColumn column) -> void;
auto remove(sListViewHeader header) -> void;
auto remove(sListViewItem item) -> void;
auto reset() -> void;
auto resizeColumns() -> void;
auto selectAll() -> void;
auto setAlignment(Alignment alignment) -> void;
auto setBackgroundColor(Color color) -> void;
auto setBatchable(bool batchable) -> void;
auto setCheckable(bool checkable) -> void;
auto setChecked(bool checked) -> void;
auto setBordered(bool bordered) -> void;
auto setForegroundColor(Color color) -> void;
auto setGridVisible(bool visible) -> void;
auto setHeaderVisible(bool visible) -> void;
auto setSortable(bool sortable) -> void;
auto uncheckAll() -> void;
auto unselectAll() -> void;
auto setGeometry(Geometry geometry) -> void override;
auto onActivate(LPARAM lparam) -> void;
auto onChange(LPARAM lparam) -> void;
auto onContext(LPARAM lparam) -> void;
auto onCustomDraw(LPARAM lparam) -> LRESULT;
auto onSort(LPARAM lparam) -> void;
auto onToggle(LPARAM lparam) -> void;
auto _backgroundColor(unsigned row, unsigned column) -> Color;
auto _cellWidth(unsigned row, unsigned column) -> unsigned;
@@ -36,6 +31,7 @@ struct pListView : pWidget {
auto _font(unsigned row, unsigned column) -> string;
auto _foregroundColor(unsigned row, unsigned column) -> Color;
auto _setIcons() -> void;
auto _setSortable() -> void;
auto _width(unsigned column) -> unsigned;
WindowProc windowProc = nullptr;