Add new API for sort related operations.

Add functions to get/set table header sort indicators:
uiTableHeaderSortIndicator
uiTableHeaderSetSortIndicator

Add callback setter for table header clicks:
uiTableHeaderOnClicked
This commit is contained in:
Angelo Haller 2019-07-08 16:53:50 -05:00
parent b673b26f58
commit 844da8d312
7 changed files with 211 additions and 1 deletions

View File

@ -16,6 +16,8 @@ struct uiTable {
uiprivScrollViewData *d;
int backgroundColumn;
uiTableModel *m;
void (*headerOnClicked)(uiTable *, int, void *);
void *headerOnClickedData;
};
// tablecolumn.m

View File

@ -16,6 +16,7 @@
uiTableModel *uiprivM;
}
- (id)initWithFrame:(NSRect)r uiprivT:(uiTable *)t uiprivM:(uiTableModel *)m;
- (uiTable *)uiTable;
@end
@implementation uiprivTableView
@ -30,6 +31,11 @@
return self;
}
- (uiTable *)uiTable
{
return self->uiprivT;
}
// TODO is this correct for overflow scrolling?
static void setBackgroundColor(uiprivTableView *t, NSTableRowView *rv, NSInteger row)
{
@ -88,6 +94,12 @@ static void setBackgroundColor(uiprivTableView *t, NSTableRowView *rv, NSInteger
setBackgroundColor((uiprivTableView *) tv, rv, row);
}
- (void)tableView:(uiprivTableView *)tv didClickTableColumn:(NSTableColumn *) tc
{
uiTable *t = [tv uiTable];
t->headerOnClicked(t, [[tc identifier] intValue], t->headerOnClickedData);
}
@end
uiTableModel *uiNewTableModel(uiTableModelHandler *mh)
@ -170,6 +182,17 @@ static void uiTableDestroy(uiControl *c)
uiFreeControl(uiControl(t));
}
void uiTableHeaderOnClicked(uiTable *t, void (*f)(uiTable *, int, void *), void *data)
{
t->headerOnClicked = f;
t->headerOnClickedData = data;
}
static void defaultHeaderOnClicked(uiTable *table, int column, void *data)
{
// do nothing
}
uiTable *uiNewTable(uiTableParams *p)
{
uiTable *t;
@ -209,6 +232,8 @@ uiTable *uiNewTable(uiTableParams *p)
sp.VScroll = YES;
t->sv = uiprivMkScrollView(&sp, &(t->d));
uiTableHeaderOnClicked(t, defaultHeaderOnClicked, NULL);
// TODO WHY DOES THIS REMOVE ALL GRAPHICAL GLITCHES?
// I got the idea from http://jwilling.com/blog/optimized-nstableview-scrolling/ but that was on an unrelated problem I didn't seem to have (although I have small-ish tables to start with)
// I don't get layer-backing... am I supposed to layer-back EVERYTHING manually? I need to check Interface Builder again...
@ -216,3 +241,27 @@ uiTable *uiNewTable(uiTableParams *p)
return t;
}
uiSort uiTableHeaderSortIndicator(uiTable *t, int lcol)
{
NSTableColumn *tc = [t->tv tableColumnWithIdentifier:[@(lcol) stringValue]];
NSString *si = [[t->tv indicatorImageInTableColumn:tc] name];
if ([si isEqualToString:@"NSAscendingSortIndicator"])
return uiSortAscending;
else if ([si isEqualToString:@"NSDescendingSortIndicator"])
return uiSortDescending;
return uiSortNone;
}
void uiTableHeaderSetSortIndicator(uiTable *t, int lcol, uiSort order)
{
NSTableColumn *tc = [t->tv tableColumnWithIdentifier:[@(lcol) stringValue]];
NSImage *img;
if (order == uiSortAscending)
img = [NSImage imageNamed:@"NSAscendingSortIndicator"];
else if (order == uiSortDescending)
img = [NSImage imageNamed:@"NSDescendingSortIndicator"];
else
img = nil;
[t->tv setIndicatorImage:img inTableColumn:tc];
}

View File

@ -100,6 +100,21 @@ static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row,
static uiTableModel *m;
static void headerOnClicked(uiTable *t, int col, void *data)
{
static int prev = 0;
if (prev != col)
uiTableHeaderSetSortIndicator(t, prev, uiSortNone);
if (uiTableHeaderSortIndicator(t, col) == uiSortAscending)
uiTableHeaderSetSortIndicator(t, col, uiSortDescending);
else
uiTableHeaderSetSortIndicator(t, col, uiSortAscending);
prev = col;
}
uiBox *makePage16(void)
{
uiBox *page16;
@ -152,6 +167,8 @@ uiBox *makePage16(void)
uiTableAppendProgressBarColumn(t, "Progress Bar",
8);
uiTableHeaderOnClicked(t, headerOnClicked, NULL);
return page16;
}

16
ui.h
View File

@ -1464,6 +1464,22 @@ _UI_EXTERN void uiTableAppendButtonColumn(uiTable *t,
// uiNewTable() creates a new uiTable with the specified parameters.
_UI_EXTERN uiTable *uiNewTable(uiTableParams *params);
// uiTableHeaderSetSortIndicator() sets the sort indicator of the table
// header to display an appropriate arrow on the column header
_UI_EXTERN void uiTableHeaderSetSortIndicator(uiTable *t,
int column,
uiSort order);
// uiTableHeaderSortIndicator returns the sort indicator of the specified
// column
_UI_EXTERN uiSort uiTableHeaderSortIndicator(uiTable *t, int column);
// uiTableHeaderOnClicked() sets a callback function to be called
// when a table column header is clicked
_UI_EXTERN void uiTableHeaderOnClicked(uiTable *t,
void (*f)(uiTable *table, int column, void *data),
void *data);
#ifdef __cplusplus
}
#endif

View File

@ -18,6 +18,8 @@ struct uiTable {
// TODO document this properly
GHashTable *indeterminatePositions;
guint indeterminateTimer;
void (*headerOnClicked)(uiTable *, int, void *);
void *headerOnClickedData;
};
// use the same size as GtkFileChooserWidget's treeview
@ -327,6 +329,59 @@ static void buttonColumnClicked(GtkCellRenderer *r, gchar *pathstr, gpointer dat
onEdited(p->m, p->modelColumn, pathstr, NULL, NULL);
}
uiSort uiTableHeaderSortIndicator(uiTable *t, int lcol)
{
GtkTreeViewColumn *c = gtk_tree_view_get_column(t->tv, lcol);
if (c == NULL || gtk_tree_view_column_get_sort_indicator(c) == FALSE)
return uiSortNone;
if (gtk_tree_view_column_get_sort_order(c) == GTK_SORT_ASCENDING)
return uiSortAscending;
else
return uiSortDescending;
}
void uiTableHeaderSetSortIndicator(uiTable *t, int lcol, uiSort order)
{
GtkTreeViewColumn *c = gtk_tree_view_get_column(t->tv, lcol);
if (c == NULL)
return;
if (order == uiSortNone) {
gtk_tree_view_column_set_sort_indicator(c, FALSE);
return;
}
gtk_tree_view_column_set_sort_indicator(c, TRUE);
if (order == uiSortAscending)
gtk_tree_view_column_set_sort_order(c, GTK_SORT_ASCENDING);
else
gtk_tree_view_column_set_sort_order(c, GTK_SORT_DESCENDING);
}
void uiTableHeaderOnClicked(uiTable *t, void (*f)(uiTable *, int, void *), void *data)
{
t->headerOnClicked = f;
t->headerOnClickedData = data;
}
static void defaultHeaderOnClicked(uiTable *table, int column, void *data)
{
// do nothing
}
static void headerOnClicked(GtkTreeViewColumn *c, gpointer data)
{
guint i;
uiTable *t = uiTable(data);
for (i = 0; i < gtk_tree_view_get_n_columns(t->tv); ++i)
if (gtk_tree_view_get_column(t->tv, i) == c)
t->headerOnClicked(t, i, t->headerOnClickedData);
}
static GtkTreeViewColumn *addColumn(uiTable *t, const char *name)
{
GtkTreeViewColumn *c;
@ -334,6 +389,8 @@ static GtkTreeViewColumn *addColumn(uiTable *t, const char *name)
c = gtk_tree_view_column_new();
gtk_tree_view_column_set_resizable(c, TRUE);
gtk_tree_view_column_set_title(c, name);
gtk_tree_view_column_set_clickable(c, 1);
g_signal_connect(c, "clicked", G_CALLBACK(headerOnClicked), t);
gtk_tree_view_append_column(t->tv, c);
return c;
}
@ -520,5 +577,7 @@ uiTable *uiNewTable(uiTableParams *p)
t->indeterminatePositions = g_hash_table_new_full(rowcolHash, rowcolEqual,
uiprivFree, uiprivFree);
uiTableHeaderOnClicked(t, defaultHeaderOnClicked, NULL);
return t;
}

View File

@ -230,6 +230,59 @@ int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG
return progress;
}
void uiTableHeaderSetSortIndicator(uiTable *t, int column, uiSort order)
{
HWND lvhdr;
int fmt;
if (order == uiSortAscending)
fmt = HDF_SORTUP;
else if (order == uiSortDescending)
fmt = HDF_SORTDOWN;
else
fmt = 0;
lvhdr = ListView_GetHeader(t->hwnd);
if (lvhdr) {
HDITEM hdri = {};
hdri.mask = HDI_FORMAT;
if (Header_GetItem(lvhdr, column, &hdri)) {
hdri.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN);
hdri.fmt |= fmt;
Header_SetItem(lvhdr, column, &hdri);
}
}
}
uiSort uiTableHeaderSortIndicator(uiTable *t, int column)
{
HWND lvhdr;
lvhdr = ListView_GetHeader(t->hwnd);
if (lvhdr) {
HDITEM hdri = {};
hdri.mask = HDI_FORMAT;
if (Header_GetItem(lvhdr, column, &hdri)) {
if (hdri.fmt & HDF_SORTUP)
return uiSortAscending;
if (hdri.fmt & HDF_SORTDOWN)
return uiSortDescending;
}
}
return uiSortNone;
}
void uiTableHeaderOnClicked(uiTable *t, void (*f)(uiTable *table, int column, void *data), void *data)
{
t->headerOnClicked = f;
t->headerOnClickedData = data;
}
static void defaultHeaderOnClicked(uiTable *table, int column, void *data)
{
// do nothing
}
// TODO properly integrate compound statements
static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult)
{
@ -310,9 +363,20 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult)
}
return FALSE;
}
case LVN_COLUMNCLICK:
{
NMLISTVIEW *nm = (NMLISTVIEW *) nmhdr;
hr = uiprivTableFinishEditingText(t);
if (hr != S_OK) {
// TODO
return FALSE;
}
t->headerOnClicked(t, nm->iSubItem, t->headerOnClickedData);
return TRUE;
}
// the real list view accepts changes when scrolling or clicking column headers
case LVN_BEGINSCROLL:
case LVN_COLUMNCLICK:
hr = uiprivTableFinishEditingText(t);
if (hr != S_OK) {
// TODO
@ -490,6 +554,7 @@ uiTable *uiNewTable(uiTableParams *p)
t->columns = new std::vector<uiprivTableColumnParams *>;
t->model = p->Model;
t->backgroundColumn = p->RowBackgroundColorModelColumn;
uiTableHeaderOnClicked(t, defaultHeaderOnClicked, NULL);
// WS_CLIPCHILDREN is here to prevent drawing over the edit box used for editing text
t->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE,

View File

@ -40,6 +40,8 @@ struct uiTable {
HWND edit;
int editedItem;
int editedSubitem;
void (*headerOnClicked)(uiTable *, int, void *);
void *headerOnClickedData;
};
extern int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG *pos);