Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Table #343

Closed
wants to merge 25 commits into from
Closed

Table #343

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
18d9aab
add table example
bcampbell Feb 10, 2017
3c8ada2
oops. table example now works.
bcampbell Feb 10, 2017
aef5f88
Merge remote-tracking branch 'upstream/table' into table
bcampbell Sep 8, 2017
62ca6d1
add bare-bones table support for win32 (using comctl listview)
bcampbell Sep 10, 2017
fc5df3c
windows comctl table: notify listview upon model changes
bcampbell Sep 11, 2017
24b7f3e
table example: switch to C++, generate dummy data
bcampbell Sep 17, 2017
b81111b
win32 comctl table: use std::vector instead of hand-rolled
bcampbell Sep 17, 2017
cff67d5
windows comctl table: tidy up WM_NOTIFY string hack
bcampbell Sep 22, 2017
620bfd2
windows comctl table: win32 unexpectedly less bonkers than feared
bcampbell Sep 24, 2017
067d605
windows comctl table: nicer default style
bcampbell Sep 24, 2017
6547cc9
add my notes on table API and implementation
bcampbell Sep 25, 2017
61e1cc2
table: add style flags and onSelectionChanged
bcampbell Sep 27, 2017
b5ed561
table (osx): support multiselect style flag
bcampbell Sep 27, 2017
de88440
refine table-selection notes across the platforms
bcampbell Sep 28, 2017
5be3285
table: add access to current selection (gtk code)
bcampbell Sep 29, 2017
e13d11e
windows: support uiTable OnSelectionChanged callback
bcampbell Sep 29, 2017
4870305
windows: uiTable support for selection iteration
bcampbell Sep 29, 2017
f75758e
table: move style flags into creation
bcampbell Sep 30, 2017
11a98bf
mac: implement uiTableGetSelection and uiTableIter
bcampbell Sep 30, 2017
0e56638
tables: add onSelectionChanged support to OSX version
bcampbell Oct 8, 2017
a9bacb2
table example: add delete button
bcampbell Jan 4, 2018
6953267
Merge remote-tracking branch 'upstream/table' into table
bcampbell Apr 20, 2018
c32baa4
table: update ui[Free|Alloc] => uipriv[Free|Alloc]
bcampbell Apr 20, 2018
d70e977
update test for new uiNewTable
bcampbell Apr 20, 2018
5f96010
apple-specific c++ build kludge for table example
bcampbell Apr 20, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions TABLE_NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Thoughts on table control support

(BenC - 2017-09-25)

## win32 comctl listview TODOs

- maintain proper list of column parts, even if dummy.
(or cut down the public API to text columns only for now
until a better windows table control is available - see below
for more thoughts on this)
- support editing of cells
- expandable parts (allocate leftover space)
- how to determine column width? random sample of data?


## Is the parts system the right abstraction?

Some toolkits support implementing cell views as custom
widget layouts eg [item delgates in QML](http://doc.qt.io/qt-5/qml-qtquick-controls-tableview.html#itemDelegate-prop).
So an item can be just about any widget layout you want.

Is that kind of thing a future aspiration for libui?
If so, I'd suggest cutting the parts API back to just
uiTableAppendTextColumn() for now.

The parts API gives _some_ of the flexiblity, but would likely
get in the way if libui went the whole hog on custom item view
layouts.


## API change: support bulk invalidation of model

Currently, the only way to notify the control that new data has
been added is via the uiTableModelRow[Inserted|Changed|Deleted]
functions. This inevitably causes redraws for each item, which
can get _really_ slow for large numbers of items.

The use cases are:
- adding large numbers of rows to existing models
- sorting (eg clicking on various column headings to change
the sort field and order.
The user-supplied model needs to handle the sorting, the
table control can't help.

I think a single uiTableModelAllChanged() function would be
enough. The underlying control needs to know that it should
to redraw everything visible (and the number of items might
have chnaged).
Copy link
Owner

@andlabs andlabs Apr 21, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As noted, the problem is GTK+ does not seem to provide a way to do this. There might be workarounds that are inefficient (requiring a signal emission for every row of the table, for instance), and I'm not sure if removing and re-adding the model will reset other settings like column widths and scroll positions. Will need to ask the GTK+ people about it.

Might want to note this here. Not required; not part of review.



## API change: support flags/styles

- single or multiselect
- hide column headers?
- allow column reordering?
- allow column resizing?
- allow click on columns (+indicators for ascending/descending)?
(will require corresponding event handling/callbacks)

I'd suggest implementing whichever of these flags are supported on
all platforms.




## API Change: Selection

In general, most toolkits avoid exposing a flat array of
selected items, presumably because the dataset could be
very large. Usually there are methods to iterate over
selection, often with simplified interface for
single-selection-only tables.

[GtkTreeView](https://developer.gnome.org/gtk3/stable/GtkTreeView.html)
- has a selection object for getting/setting selection
- can fetch selection as a list, or via a foreach callback
- need to map GtkTreeview paths back to row indexes

[QML TableView](http://doc.qt.io/qt-5/qml-qtquick-controls-tableview.html)
- has a selection object for getting/setting selection
- supports iteration of selected items

[OSX NSTableView](https://developer.apple.com/documentation/appkit/nstableview)
- has an IndexSet to hold selected items
- general purpose class? Lots of general set methods

win32 commonctrl listview
- items have LVIS_SELECTED flag
- state changes notified via
[LVN_ODSTATECHANGED](https://msdn.microsoft.com/en-us/library/windows/desktop/bb774859(v=vs.85).aspx)
(indicates one or more contiguous items have changed state)
- Use LVN_GETNEXTITEM to iterate through items with selected state

[wxWidgets wxDataViewCtrl](http://docs.wxwidgets.org/3.0/classwx_data_view_ctrl.html)
- provides `GetSelections( wxDataViewItemArray& sel)`, where `wxDataViewItemArray`
is presumably a growable array of some kind.


aiming to support:

- a "selection has changed" event
`uiTableOnSelectionChanged(uiTable* t, handlerfn fn, void* userdata)`?
- functions to programatically select/unselect item(s)
- access current selection (preferably by iteration rather than returning
possibly-huge arrays)

108 changes: 106 additions & 2 deletions darwin/table.m
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,60 @@ @interface tableView : NSTableView
tableView *tv;
struct scrollViewData *d;
int backgroundColumn;
void (*onSelectionChanged)(uiTable *, void *);
void *onSelectionChangedData;
};

@interface tableDelegateClass : NSObject {
struct mapTable *tables;
}
- (void)tableViewSelectionDidChange:(NSNotification *)notification;
- (void)registerTable:(uiTable *)t;
- (void)unregisterTable:(uiTable *)t;
@end

@implementation tableDelegateClass

- (id)init
{
self = [super init];
if (self)
self->tables = newMap();
return self;
}

- (void)dealloc
{
mapDestroy(self->tables);
[super dealloc];
}

- (void)tableViewSelectionDidChange:(NSNotification *)notification
{
uiTable *t;

t = (uiTable *) mapGet(self->tables, notification);
(*(t->onSelectionChanged))(t, t->onSelectionChangedData);
}

- (void)registerTable:(uiTable *)t
{
mapSet(self->tables, t->tv, t);
[t->tv setTarget:self];
[t->tv setAction:@selector(tableViewSelectionDidChange:)];
}

- (void)unregisterTable:(uiTable *)t
{
[t->tv setTarget:nil];
mapDelete(self->tables, t->tv);
}

@end

static tableDelegateClass *tableDelegate = nil;


@implementation tableModel

- (id)initWithModel:(uiTableModel *)m
Expand Down Expand Up @@ -510,6 +562,7 @@ static void uiTableDestroy(uiControl *c)
{
uiTable *t = uiTable(c);

[tableDelegate unregisterTable:t];
// TODO
[t->sv release];
uiFreeControl(uiControl(t));
Expand Down Expand Up @@ -538,7 +591,49 @@ void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn)
t->backgroundColumn = modelColumn;
}

uiTable *uiNewTable(uiTableModel *model)
void uiTableOnSelectionChanged(uiTable *t, void (*f)(uiTable *, void *), void *data)
{
t->onSelectionChanged = f;
t->onSelectionChangedData = data;
}

struct uiTableIter {
NSIndexSet *set;
int foo;
NSUInteger curr;
};

uiTableIter* uiTableGetSelection(uiTable *t)
{
uiTableIter *it = uiprivAlloc(sizeof(uiTableIter), "uiTableIter");
it->set = [t->tv selectedRowIndexes];
it->foo = 0;

it->curr = [it->set firstIndex];
return it;
}


int uiTableIterAdvance(uiTableIter *it)
{
if (it->foo>0) {
it->curr = [it->set indexGreaterThanIndex:it->curr];
}
it->foo++;
return (it->curr==NSNotFound) ? 0:1;
}

int uiTableIterCurrent(uiTableIter *it)
{
return (int)it->curr;
}

void uiTableIterComplete(uiTableIter *it)
{
uiprivFree(it);
}

uiTable *uiNewTable(uiTableModel *model, int styleFlags)
{
uiTable *t;
struct scrollViewCreateParams p;
Expand All @@ -556,7 +651,11 @@ void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn)
// TODO is this sufficient?
[t->tv setAllowsColumnReordering:NO];
[t->tv setAllowsColumnResizing:YES];
[t->tv setAllowsMultipleSelection:NO];
if (styleFlags & uiTableStyleMultiSelect) {
[t->tv setAllowsMultipleSelection:YES];
} else {
[t->tv setAllowsMultipleSelection:NO];
}
[t->tv setAllowsEmptySelection:YES];
[t->tv setAllowsColumnSelection:NO];
[t->tv setUsesAlternatingRowBackgroundColors:YES];
Expand All @@ -578,5 +677,10 @@ void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn)

t->backgroundColumn = -1;

if (tableDelegate == nil) {
tableDelegate = [[tableDelegateClass new] autorelease];
[delegates addObject:tableDelegate];
}
[tableDelegate registerTable:t];
return t;
}
9 changes: 9 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ _add_example(histogram
${_EXAMPLE_RESOURCES_RC}
)

_add_example(table
table/main.cpp
table/planet.cpp
${_EXAMPLE_RESOURCES_RC}
)

_add_example(cpp-multithread
cpp-multithread/main.cpp
${_EXAMPLE_RESOURCES_RC}
Expand All @@ -35,6 +41,8 @@ if(APPLE)
# see issue #302 for more details
target_compile_options(cpp-multithread PRIVATE --stdlib=libc++)
target_link_libraries(cpp-multithread --stdlib=libc++)
target_compile_options(table PRIVATE --stdlib=libc++)
target_link_libraries(table --stdlib=libc++)
endif()

_add_example(drawtext
Expand All @@ -45,6 +53,7 @@ _add_example(drawtext
add_custom_target(examples
DEPENDS
controlgallery
table
histogram
cpp-multithread
drawtext)
Loading