123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476 |
- #if defined(Hiro_TableView)
- @implementation CocoaTableView : NSScrollView
- -(id) initWith:(hiro::mTableView&)tableViewReference {
- if(self = [super initWithFrame:NSMakeRect(0, 0, 0, 0)]) {
- tableView = &tableViewReference;
- content = [[CocoaTableViewContent alloc] initWith:tableViewReference];
- [self setDocumentView:content];
- [self setBorderType:NSBezelBorder];
- [self setHasVerticalScroller:YES];
- [content setDataSource:self];
- [content setDelegate:self];
- [content setTarget:self];
- [content setDoubleAction:@selector(doubleAction:)];
- [content setAllowsColumnReordering:NO];
- [content setAllowsColumnResizing:YES];
- [content setAllowsColumnSelection:NO];
- [content setAllowsEmptySelection:YES];
- [content setColumnAutoresizingStyle:NSTableViewLastColumnOnlyAutoresizingStyle];
- font = nil;
- [self setFont:nil];
- }
- return self;
- }
- -(void) dealloc {
- [content release];
- [font release];
- [super dealloc];
- }
- -(CocoaTableViewContent*) content {
- return content;
- }
- -(NSFont*) font {
- return font;
- }
- -(void) setFont:(NSFont*)fontPointer {
- if(!fontPointer) fontPointer = [NSFont systemFontOfSize:12];
- [fontPointer retain];
- if(font) [font release];
- font = fontPointer;
- uint fontHeight = hiro::pFont::size(font, " ").height();
- [content setFont:font];
- [content setRowHeight:fontHeight];
- [self reloadColumns];
- tableView->resizeColumns();
- }
- -(void) reloadColumns {
- while([[content tableColumns] count]) {
- [content removeTableColumn:[[content tableColumns] lastObject]];
- }
- for(auto& tableViewColumn : tableView->state.columns) {
- auto column = tableViewColumn->offset();
- NSTableColumn* tableColumn = [[NSTableColumn alloc] initWithIdentifier:[[NSNumber numberWithInteger:column] stringValue]];
- NSTableHeaderCell* headerCell = [[NSTableHeaderCell alloc] initTextCell:[NSString stringWithUTF8String:tableViewColumn->state.text]];
- CocoaTableViewCell* dataCell = [[CocoaTableViewCell alloc] initWith:*tableView];
- [dataCell setEditable:NO];
- [tableColumn setResizingMask:NSTableColumnAutoresizingMask | NSTableColumnUserResizingMask];
- [tableColumn setHeaderCell:headerCell];
- [tableColumn setDataCell:dataCell];
- [content addTableColumn:tableColumn];
- }
- }
- -(NSInteger) numberOfRowsInTableView:(NSTableView*)table {
- return tableView->state.items.size();
- }
- -(id) tableView:(NSTableView*)table objectValueForTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row {
- if(auto tableViewItem = tableView->item(row)) {
- if(auto tableViewCell = tableViewItem->cell([[tableColumn identifier] integerValue])) {
- NSString* text = [NSString stringWithUTF8String:tableViewCell->state.text];
- return @{ @"text":text }; //used by type-ahead
- }
- }
- return @{};
- }
- -(BOOL) tableView:(NSTableView*)table shouldShowCellExpansionForTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row {
- return NO;
- }
- -(NSString*) tableView:(NSTableView*)table toolTipForCell:(NSCell*)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row mouseLocation:(NSPoint)mouseLocation {
- return nil;
- }
- -(void) tableView:(NSTableView*)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row {
- [cell setFont:[self font]];
- }
- -(void) tableViewSelectionDidChange:(NSNotification*)notification {
- if(tableView->self()->locked()) return;
- for(auto& tableViewItem : tableView->state.items) {
- tableViewItem->state.selected = tableViewItem->offset() == [content selectedRow];
- }
- tableView->doChange();
- }
- -(IBAction) doubleAction:(id)sender {
- int row = [content clickedRow];
- if(row >= 0 && row < tableView->state.items.size()) {
- int column = [content clickedColumn];
- if(column >= 0 && column < tableView->state.columns.size()) {
- auto item = tableView->state.items[row];
- auto cell = item->cell(column);
- tableView->doActivate(cell);
- }
- }
- }
- @end
- @implementation CocoaTableViewContent : NSTableView
- -(id) initWith:(hiro::mTableView&)tableViewReference {
- if(self = [super initWithFrame:NSMakeRect(0, 0, 0, 0)]) {
- tableView = &tableViewReference;
- }
- return self;
- }
- -(void) keyDown:(NSEvent*)event {
- auto character = [[event characters] characterAtIndex:0];
- if(character == NSEnterCharacter || character == NSCarriageReturnCharacter) {
- int row = [self selectedRow];
- if(row >= 0 && row < tableView->state.items.size()) {
- int column = max(0, [self selectedColumn]); //can be -1?
- if(column >= 0 && column < tableView->state.columns.size()) {
- auto item = tableView->state.items[row];
- auto cell = item->cell(column);
- tableView->doActivate(cell);
- }
- }
- }
- [super keyDown:event];
- }
- -(void) reloadData {
- // Acquire lock to prevent tableViewSelectionDidChange from invoking the onChange callback
- auto lock = tableView->self()->acquire();
- [super reloadData];
- }
- -(NSMenu*) menuForEvent:(NSEvent*)event {
- // Right-clicks don't change the selected item by default on macOS, so do it manually
- NSInteger row = [self rowAtPoint:[self convertPoint:event.locationInWindow fromView:nil]];
- if (row >= 0 && ![self isRowSelected:row]) {
- [self selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
- }
- tableView->doContext();
- return nil;
- }
- @end
- @implementation CocoaTableViewCell : NSCell
- -(id) initWith:(hiro::mTableView&)tableViewReference {
- if(self = [super initTextCell:@""]) {
- tableView = &tableViewReference;
- buttonCell = [[NSButtonCell alloc] initTextCell:@""];
- [buttonCell setButtonType:NSSwitchButton];
- [buttonCell setControlSize:NSSmallControlSize];
- [buttonCell setRefusesFirstResponder:YES];
- [buttonCell setTarget:self];
- }
- return self;
- }
- //used by type-ahead
- -(NSString*) stringValue {
- return [[self objectValue] objectForKey:@"text"];
- }
- -(void) drawWithFrame:(NSRect)frame inView:(NSView*)view {
- if(auto tableViewItem = tableView->item([view rowAtPoint:frame.origin])) {
- if(auto tableViewCell = tableViewItem->cell([view columnAtPoint:frame.origin])) {
- NSColor* backgroundColor = nil;
- if([self isHighlighted]) backgroundColor = [NSColor alternateSelectedControlColor];
- else if(!tableView->enabled(true)) backgroundColor = [NSColor controlBackgroundColor];
- else if(auto color = tableViewCell->state.backgroundColor) backgroundColor = NSMakeColor(color);
- else backgroundColor = [NSColor controlBackgroundColor];
- [backgroundColor set];
- [NSBezierPath fillRect:frame];
- if(tableViewCell->state.checkable) {
- [buttonCell setHighlighted:YES];
- [buttonCell setState:(tableViewCell->state.checked ? NSOnState : NSOffState)];
- [buttonCell drawWithFrame:frame inView:view];
- frame.origin.x += frame.size.height + 2;
- frame.size.width -= frame.size.height + 2;
- }
- if(tableViewCell->state.icon) {
- NSImage* image = NSMakeImage(tableViewCell->state.icon, frame.size.height, frame.size.height);
- [[NSGraphicsContext currentContext] saveGraphicsState];
- NSRect targetRect = NSMakeRect(frame.origin.x, frame.origin.y, frame.size.height, frame.size.height);
- NSRect sourceRect = NSMakeRect(0, 0, [image size].width, [image size].height);
- [image drawInRect:targetRect fromRect:sourceRect operation:NSCompositeSourceOver fraction:1.0 respectFlipped:YES hints:nil];
- [[NSGraphicsContext currentContext] restoreGraphicsState];
- frame.origin.x += frame.size.height + 2;
- frame.size.width -= frame.size.height + 2;
- }
- if(tableViewCell->state.text) {
- NSMutableParagraphStyle* paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
- paragraphStyle.alignment = NSTextAlignmentCenter;
- if(tableViewCell->state.alignment.horizontal() < 0.333) paragraphStyle.alignment = NSTextAlignmentLeft;
- if(tableViewCell->state.alignment.horizontal() > 0.666) paragraphStyle.alignment = NSTextAlignmentRight;
- NSColor* foregroundColor = nil;
- if([self isHighlighted]) foregroundColor = [NSColor alternateSelectedControlTextColor];
- else if(!tableView->enabled(true)) foregroundColor = [NSColor disabledControlTextColor];
- else if(auto color = tableViewCell->state.foregroundColor) foregroundColor = NSMakeColor(color);
- else foregroundColor = [NSColor textColor];
- NSString* text = [NSString stringWithUTF8String:tableViewCell->state.text];
- [text drawInRect:frame withAttributes:@{
- NSBackgroundColorAttributeName:backgroundColor,
- NSForegroundColorAttributeName:foregroundColor,
- NSFontAttributeName:hiro::pFont::create(tableViewCell->font(true)),
- NSParagraphStyleAttributeName:paragraphStyle
- }];
- }
- }
- }
- }
- //needed to trigger trackMouse events
- -(NSUInteger) hitTestForEvent:(NSEvent*)event inRect:(NSRect)frame ofView:(NSView*)view {
- NSUInteger hitTest = [super hitTestForEvent:event inRect:frame ofView:view];
- NSPoint point = [view convertPoint:[event locationInWindow] fromView:nil];
- NSRect rect = NSMakeRect(frame.origin.x, frame.origin.y, frame.size.height, frame.size.height);
- if(NSMouseInRect(point, rect, [view isFlipped])) {
- hitTest |= NSCellHitTrackableArea;
- }
- return hitTest;
- }
- //I am unable to get startTrackingAt:, continueTracking:, stopTracking: to work
- //so instead, I have to run a modal loop on events until the mouse button is released
- -(BOOL) trackMouse:(NSEvent*)event inRect:(NSRect)frame ofView:(NSView*)view untilMouseUp:(BOOL)flag {
- if([event type] == NSLeftMouseDown) {
- NSWindow* window = [view window];
- NSEvent* nextEvent;
- while((nextEvent = [window nextEventMatchingMask:(NSLeftMouseDragged | NSLeftMouseUp)])) {
- if([nextEvent type] == NSLeftMouseUp) {
- NSPoint point = [view convertPoint:[nextEvent locationInWindow] fromView:nil];
- NSRect rect = NSMakeRect(frame.origin.x, frame.origin.y, frame.size.height, frame.size.height);
- if(NSMouseInRect(point, rect, [view isFlipped])) {
- if(auto tableViewItem = tableView->item([view rowAtPoint:point])) {
- if(auto tableViewCell = tableViewItem->cell([view columnAtPoint:point])) {
- tableViewCell->state.checked = !tableViewCell->state.checked;
- tableView->doToggle(tableViewCell->instance);
- }
- }
- }
- break;
- }
- }
- }
- return YES;
- }
- +(BOOL) prefersTrackingUntilMouseUp {
- return YES;
- }
- @end
- namespace hiro {
- auto pTableView::construct() -> void {
- @autoreleasepool {
- cocoaView = cocoaTableView = [[CocoaTableView alloc] initWith:self()];
- pWidget::construct();
- setAlignment(state().alignment);
- setBackgroundColor(state().backgroundColor);
- setBatchable(state().batchable);
- setBordered(state().bordered);
- setFont(self().font(true));
- setForegroundColor(state().foregroundColor);
- setHeadered(state().headered);
- setSortable(state().sortable);
- }
- }
- auto pTableView::destruct() -> void {
- @autoreleasepool {
- [cocoaView removeFromSuperview];
- [cocoaView release];
- }
- }
- auto pTableView::append(sTableViewColumn column) -> void {
- @autoreleasepool {
- [cocoaView reloadColumns];
- resizeColumns();
- }
- }
- auto pTableView::append(sTableViewItem item) -> void {
- @autoreleasepool {
- [[cocoaView content] reloadData];
- }
- }
- auto pTableView::remove(sTableViewColumn column) -> void {
- @autoreleasepool {
- [cocoaView reloadColumns];
- resizeColumns();
- }
- }
- auto pTableView::remove(sTableViewItem item) -> void {
- @autoreleasepool {
- [[cocoaView content] reloadData];
- }
- }
- auto pTableView::resizeColumns() -> void {
- @autoreleasepool {
- vector<int> widths;
- int minimumWidth = 0;
- int expandable = 0;
- for(uint column : range(self().columnCount())) {
- int width = _width(column);
- widths.append(width);
- minimumWidth += width;
- if(state().columns[column]->expandable()) expandable++;
- }
- int maximumWidth = self().geometry().width() - 18; //include margin for vertical scroll bar
- int expandWidth = 0;
- if(expandable && maximumWidth > minimumWidth) {
- expandWidth = (maximumWidth - minimumWidth) / expandable;
- }
- for(uint column : range(self().columnCount())) {
- if(auto self = state().columns[column]->self()) {
- int width = widths[column];
- if(self->state().expandable) width += expandWidth;
- NSTableColumn* tableColumn = [[cocoaView content] tableColumnWithIdentifier:[[NSNumber numberWithInteger:column] stringValue]];
- [tableColumn setWidth:width];
- }
- }
- }
- }
- auto pTableView::setAlignment(Alignment alignment) -> void {
- }
- auto pTableView::setBackgroundColor(Color color) -> void {
- }
- auto pTableView::setBatchable(bool batchable) -> void {
- @autoreleasepool {
- [[cocoaView content] setAllowsMultipleSelection:(batchable ? YES : NO)];
- }
- }
- auto pTableView::setBordered(bool bordered) -> void {
- }
- auto pTableView::setEnabled(bool enabled) -> void {
- pWidget::setEnabled(enabled);
- @autoreleasepool {
- [[cocoaView content] setEnabled:enabled];
- }
- }
- auto pTableView::setFont(const Font& font) -> void {
- @autoreleasepool {
- [cocoaView setFont:pFont::create(font)];
- }
- }
- auto pTableView::setForegroundColor(Color color) -> void {
- }
- auto pTableView::setHeadered(bool headered) -> void {
- @autoreleasepool {
- if(headered) {
- [[cocoaView content] setHeaderView:[[[NSTableHeaderView alloc] init] autorelease]];
- } else {
- [[cocoaView content] setHeaderView:nil];
- }
- }
- }
- auto pTableView::setSortable(bool sortable) -> void {
- //TODO
- }
- auto pTableView::_cellWidth(uint row, uint column) -> uint {
- uint width = 8;
- if(auto pTableViewItem = self().item(row)) {
- if(auto pTableViewCell = pTableViewItem->cell(column)) {
- if(pTableViewCell->state.checkable) {
- width += 24;
- }
- if(auto& icon = pTableViewCell->state.icon) {
- width += icon.width() + 2;
- }
- if(auto& text = pTableViewCell->state.text) {
- width += pFont::size(pTableViewCell->font(true), text).width();
- }
- }
- }
- return width;
- }
- auto pTableView::_columnWidth(uint column_) -> uint {
- uint width = 8;
- if(auto column = self().column(column_)) {
- if(auto& icon = column->state.icon) {
- width += icon.width() + 2;
- }
- if(auto& text = column->state.text) {
- width += pFont::size(column->font(true), text).width();
- }
- if(column->state.sorting != Sort::None) {
- width += 16;
- }
- }
- return width;
- }
- auto pTableView::_width(uint column) -> uint {
- if(auto width = self().column(column).width()) return width;
- uint width = 1;
- if(!self().column(column).visible()) return width;
- if(state().headered) width = max(width, _columnWidth(column));
- for(auto row : range(state().items.size())) {
- width = max(width, _cellWidth(row, column));
- }
- return width;
- }
- /*
- auto pTableView::setSelected(bool selected) -> void {
- @autoreleasepool {
- if(selected == false) {
- [[cocoaView content] deselectAll:nil];
- }
- }
- }
- auto pTableView::setSelection(unsigned selection) -> void {
- @autoreleasepool {
- [[cocoaView content] selectRowIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(selection, 1)] byExtendingSelection:NO];
- }
- }
- */
- }
- #endif
|