diff --git a/Frameworks/OakFilterList/src/FileChooser.mm b/Frameworks/OakFilterList/src/FileChooser.mm index 3cdde88d..253940a0 100644 --- a/Frameworks/OakFilterList/src/FileChooser.mm +++ b/Frameworks/OakFilterList/src/FileChooser.mm @@ -1,5 +1,6 @@ #import "FileChooser.h" #import "OakAbbreviations.h" +#import "ui/TableView.h" #import #import #import @@ -47,54 +48,6 @@ static NSButton* OakCreateScopeButton (NSString* label, SEL action, NSUInteger t } @end -@interface OakNonActivatingTableView : NSTableView -@end - -@implementation OakNonActivatingTableView -- (NSCell*)preparedCellAtColumn:(NSInteger)column row:(NSInteger)row -{ - OFBPathInfoCell* res = (OFBPathInfoCell*)[super preparedCellAtColumn:column row:row]; - res.disableHighlight = [self.window isKeyWindow]; - return res; -} - -- (void)highlightSelectionInClipRect:(NSRect)clipRect -{ - if(![self.window isKeyWindow]) - return [super highlightSelectionInClipRect:clipRect]; - - [[NSColor alternateSelectedControlColor] set]; - [[self selectedRowIndexes] enumerateRangesInRange:[self rowsInRect:clipRect] options:0 usingBlock:^(NSRange range, BOOL* stop){ - for(NSUInteger row = range.location; row < NSMaxRange(range); ++row) - { - NSRect rect = [self rectOfRow:row]; - rect.size.height -= 1; - NSRectFill(rect); - } - }]; -} -@end - -static NSMutableAttributedString* CreateAttributedStringWithMarkedUpRanges (NSFont* baseFont, NSColor* textColor, NSColor* matchedTextColor, std::string const& in, std::vector< std::pair > const& ranges, size_t offset = 0) -{ - NSDictionary* baseAttributes = @{ NSForegroundColorAttributeName : textColor, }; - NSDictionary* highlightAttributes = @{ NSForegroundColorAttributeName : matchedTextColor, NSUnderlineStyleAttributeName : @1 }; - - NSMutableAttributedString* res = [[NSMutableAttributedString alloc] init]; - - size_t from = 0; - for(auto range : ranges) - { - [res appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithCxxString:std::string(in.begin() + from, in.begin() + range.first + offset)] attributes:baseAttributes]]; - [res appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithCxxString:std::string(in.begin() + range.first + offset, in.begin() + range.second + offset)] attributes:highlightAttributes]]; - from = range.second + offset; - } - if(from < in.size()) - [res appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithCxxString:in.substr(from)] attributes:baseAttributes]]; - - return res; -} - // ======================= namespace @@ -193,7 +146,7 @@ static path::glob_list_t globs_for_path (std::string const& path) return res; } -@interface FileChooser () +@interface FileChooser () { scm::info_ptr _scmInfo; std::vector _openDocuments; @@ -206,7 +159,7 @@ static path::glob_list_t globs_for_path (std::string const& path) @property (nonatomic) NSButton* allButton; @property (nonatomic) NSButton* openDocumentsButton; @property (nonatomic) NSButton* scmChangesButton; -@property (nonatomic) NSTableView* tableView; +@property (nonatomic) OakInactiveTableView* tableView; @property (nonatomic) NSTextField* statusTextField; @property (nonatomic) NSTextField* itemCountTextField; @property (nonatomic) NSProgressIndicator* progressIndicator; @@ -234,13 +187,11 @@ static path::glob_list_t globs_for_path (std::string const& path) { _items = @[ ]; - _searchField = [[NSSearchField alloc] initWithFrame:NSZeroRect]; - _searchField.delegate = self; + _searchField = [[NSSearchField alloc] initWithFrame:NSZeroRect]; [_searchField.cell setScrollable:YES]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(controlTextDidChange:) name:NSControlTextDidChangeNotification object:_searchField]; if(![NSApp isFullKeyboardAccessEnabled]) - { _searchField.focusRingType = NSFocusRingTypeNone; - } _allButton = OakCreateScopeButton(@"All", @selector(takeSourceIndexFrom:), 0); _openDocumentsButton = OakCreateScopeButton(@"Open Documents", @selector(takeSourceIndexFrom:), 1); @@ -267,7 +218,7 @@ static path::glob_list_t globs_for_path (std::string const& path) NSTableColumn* tableColumn = [[NSTableColumn alloc] initWithIdentifier:@"name"]; [tableColumn setDataCell:cell]; - _tableView = [[OakNonActivatingTableView alloc] initWithFrame:NSZeroRect]; + _tableView = [[OakInactiveTableView alloc] initWithFrame:NSZeroRect]; [_tableView addTableColumn:tableColumn]; _tableView.headerView = nil; _tableView.focusRingType = NSFocusRingTypeNone; @@ -278,6 +229,7 @@ static path::glob_list_t globs_for_path (std::string const& path) _tableView.target = self; _tableView.dataSource = self; _tableView.delegate = self; + _tableView.linkedTextField = _searchField; NSScrollView* scrollView = [[NSScrollView alloc] initWithFrame:NSZeroRect]; scrollView.hasVerticalScroller = YES; @@ -364,8 +316,9 @@ static path::glob_list_t globs_for_path (std::string const& path) - (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSControlTextDidChangeNotification object:_searchField]; + _window.delegate = nil; - _searchField.delegate = nil; _tableView.target = nil; _tableView.dataSource = nil; _tableView.delegate = nil; @@ -947,49 +900,9 @@ inline void rank_record (document_record_t& record, filter_string_t const& filte return activate; } -// ========================= -// = Search Field Delegate = -// ========================= - -- (void)moveSelectedRowByOffset:(NSInteger)anOffset extendingSelection:(BOOL)extend -{ - if([_tableView numberOfRows]) - { - if(_tableView.allowsMultipleSelection == NO) - extend = NO; - NSInteger row = oak::cap((NSInteger)0, [_tableView selectedRow] + anOffset, [_tableView numberOfRows] - 1); - [_tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:extend]; - [_tableView scrollRowToVisible:row]; - } -} - -- (int)visibleRows { return (int)floorf(NSHeight([_tableView visibleRect]) / ([_tableView rowHeight]+[_tableView intercellSpacing].height)) - 1; } - -- (void)moveUp:(id)sender { [self moveSelectedRowByOffset:-1 extendingSelection:NO]; } -- (void)moveDown:(id)sender { [self moveSelectedRowByOffset:+1 extendingSelection:NO]; } -- (void)moveUpAndModifySelection:(id)sender { [self moveSelectedRowByOffset:-1 extendingSelection:YES];} -- (void)moveDownAndModifySelection:(id)sender { [self moveSelectedRowByOffset:+1 extendingSelection:YES];} -- (void)movePageUp:(id)sender { [self moveSelectedRowByOffset:-[self visibleRows] extendingSelection:NO]; } -- (void)movePageDown:(id)sender { [self moveSelectedRowByOffset:+[self visibleRows] extendingSelection:NO]; } -- (void)moveToBeginningOfDocument:(id)sender { [self moveSelectedRowByOffset:-(INT_MAX >> 1) extendingSelection:NO]; } -- (void)moveToEndOfDocument:(id)sender { [self moveSelectedRowByOffset:+(INT_MAX >> 1) extendingSelection:NO]; } - -- (void)pageUp:(id)sender { [self movePageUp:sender]; } -- (void)pageDown:(id)sender { [self movePageDown:sender]; } -- (void)scrollPageUp:(id)sender { [self movePageUp:sender]; } -- (void)scrollPageDown:(id)sender { [self movePageDown:sender]; } - -- (IBAction)insertNewline:(id)sender { [self accept:sender]; } -- (IBAction)insertNewlineIgnoringFieldEditor:(id)sender { [self accept:sender]; } -- (IBAction)cancelOperation:(id)sender { [self cancel:sender]; } - -- (BOOL)control:(NSControl*)aControl textView:(NSTextView*)aTextView doCommandBySelector:(SEL)aCommand -{ - static auto const forward = new std::set{ @selector(moveUp:), @selector(moveDown:), @selector(moveUpAndModifySelection:), @selector(moveDownAndModifySelection:), @selector(pageUp:), @selector(pageDown:), @selector(movePageUp:), @selector(movePageDown:), @selector(scrollPageUp:), @selector(scrollPageDown:), @selector(moveToBeginningOfDocument:), @selector(moveToEndOfDocument:), @selector(insertNewline:), @selector(insertNewlineIgnoringFieldEditor:), @selector(cancelOperation:) }; - if(forward->find(aCommand) != forward->end() && [self respondsToSelector:aCommand]) - return [NSApp sendAction:aCommand to:self from:aControl]; - return NO; -} +// ============================= +// = Search Field Notification = +// ============================= - (void)controlTextDidChange:(NSNotification*)aNotification { diff --git a/Frameworks/OakFilterList/src/ui/TableView.h b/Frameworks/OakFilterList/src/ui/TableView.h new file mode 100644 index 00000000..ce77b8b9 --- /dev/null +++ b/Frameworks/OakFilterList/src/ui/TableView.h @@ -0,0 +1,5 @@ +@interface OakInactiveTableView : NSTableView +@property (nonatomic, weak) NSTextField* linkedTextField; +@end + +NSMutableAttributedString* CreateAttributedStringWithMarkedUpRanges (NSFont* baseFont, NSColor* textColor, NSColor* matchedTextColor, std::string const& in, std::vector< std::pair > const& ranges, size_t offset = 0); diff --git a/Frameworks/OakFilterList/src/ui/TableView.mm b/Frameworks/OakFilterList/src/ui/TableView.mm new file mode 100644 index 00000000..b1e62422 --- /dev/null +++ b/Frameworks/OakFilterList/src/ui/TableView.mm @@ -0,0 +1,124 @@ +#import "TableView.h" +#import +#import + +// ======================================== +// = Forward NSTextField Movement Actions = +// ======================================== + +@interface OakTextFieldMovementDelegate : NSObject +@property (nonatomic, weak) NSTableView* tableView; +@end + +@implementation OakTextFieldMovementDelegate +- (void)moveSelectedRowByOffset:(NSInteger)anOffset extendingSelection:(BOOL)extend +{ + if([_tableView numberOfRows]) + { + if(_tableView.allowsMultipleSelection == NO) + extend = NO; + NSInteger row = oak::cap((NSInteger)0, [_tableView selectedRow] + anOffset, [_tableView numberOfRows] - 1); + [_tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:extend]; + [_tableView scrollRowToVisible:row]; + } +} + +- (int)visibleRows { return (int)floorf(NSHeight([_tableView visibleRect]) / ([_tableView rowHeight]+[_tableView intercellSpacing].height)) - 1; } + +- (void)moveUp:(id)sender { [self moveSelectedRowByOffset:-1 extendingSelection:NO]; } +- (void)moveDown:(id)sender { [self moveSelectedRowByOffset:+1 extendingSelection:NO]; } +- (void)moveUpAndModifySelection:(id)sender { [self moveSelectedRowByOffset:-1 extendingSelection:YES];} +- (void)moveDownAndModifySelection:(id)sender { [self moveSelectedRowByOffset:+1 extendingSelection:YES];} +- (void)movePageUp:(id)sender { [self moveSelectedRowByOffset:-[self visibleRows] extendingSelection:NO]; } +- (void)movePageDown:(id)sender { [self moveSelectedRowByOffset:+[self visibleRows] extendingSelection:NO]; } +- (void)moveToBeginningOfDocument:(id)sender { [self moveSelectedRowByOffset:-(INT_MAX >> 1) extendingSelection:NO]; } +- (void)moveToEndOfDocument:(id)sender { [self moveSelectedRowByOffset:+(INT_MAX >> 1) extendingSelection:NO]; } + +- (void)pageUp:(id)sender { [self movePageUp:sender]; } +- (void)pageDown:(id)sender { [self movePageDown:sender]; } +- (void)scrollPageUp:(id)sender { [self movePageUp:sender]; } +- (void)scrollPageDown:(id)sender { [self movePageDown:sender]; } + +- (IBAction)insertNewline:(id)sender { [NSApp sendAction:@selector(accept:) to:nil from:sender]; } +- (IBAction)insertNewlineIgnoringFieldEditor:(id)sender { [NSApp sendAction:@selector(accept:) to:nil from:sender]; } +- (IBAction)cancelOperation:(id)sender { [NSApp sendAction:@selector(cancel:) to:nil from:sender]; } + +- (BOOL)control:(NSControl*)aControl textView:(NSTextView*)aTextView doCommandBySelector:(SEL)aCommand +{ + static auto const forward = new std::set{ @selector(moveUp:), @selector(moveDown:), @selector(moveUpAndModifySelection:), @selector(moveDownAndModifySelection:), @selector(pageUp:), @selector(pageDown:), @selector(movePageUp:), @selector(movePageDown:), @selector(scrollPageUp:), @selector(scrollPageDown:), @selector(moveToBeginningOfDocument:), @selector(moveToEndOfDocument:), @selector(insertNewline:), @selector(insertNewlineIgnoringFieldEditor:), @selector(cancelOperation:) }; + if(forward->find(aCommand) != forward->end() && [self respondsToSelector:aCommand]) + return [NSApp sendAction:aCommand to:self from:aControl]; + return NO; +} +@end + +// ======================================== + +@interface NSCell (DisableHighlight) +@property (nonatomic) BOOL disableHighlight; +@end + +@interface OakInactiveTableView () +@property (nonatomic) OakTextFieldMovementDelegate* textFieldMovementDelegate; +@end + +@implementation OakInactiveTableView +- (NSCell*)preparedCellAtColumn:(NSInteger)column row:(NSInteger)row +{ + NSCell* res = [super preparedCellAtColumn:column row:row]; + if([res respondsToSelector:@selector(setDisableHighlight:)]) + [res setDisableHighlight:[self.window isKeyWindow]]; + return res; +} + +- (void)highlightSelectionInClipRect:(NSRect)clipRect +{ + if(![self.window isKeyWindow]) + return [super highlightSelectionInClipRect:clipRect]; + + [[NSColor alternateSelectedControlColor] set]; + [[self selectedRowIndexes] enumerateRangesInRange:[self rowsInRect:clipRect] options:0 usingBlock:^(NSRange range, BOOL* stop){ + for(NSUInteger row = range.location; row < NSMaxRange(range); ++row) + { + NSRect rect = [self rectOfRow:row]; + rect.size.height -= 1; + NSRectFill(rect); + } + }]; +} + +- (void)setLinkedTextField:(NSTextField*)aTextField +{ + _textFieldMovementDelegate = [OakTextFieldMovementDelegate new]; + _textFieldMovementDelegate.tableView = self; + + _linkedTextField.delegate = nil; + _linkedTextField = aTextField; + _linkedTextField.delegate = _textFieldMovementDelegate; +} + +- (void)dealloc +{ + _linkedTextField.delegate = nil; +} +@end + +NSMutableAttributedString* CreateAttributedStringWithMarkedUpRanges (NSFont* baseFont, NSColor* textColor, NSColor* matchedTextColor, std::string const& in, std::vector< std::pair > const& ranges, size_t offset) +{ + NSDictionary* baseAttributes = @{ NSForegroundColorAttributeName : textColor, }; + NSDictionary* highlightAttributes = @{ NSForegroundColorAttributeName : matchedTextColor, NSUnderlineStyleAttributeName : @1 }; + + NSMutableAttributedString* res = [[NSMutableAttributedString alloc] init]; + + size_t from = 0; + for(auto range : ranges) + { + [res appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithCxxString:std::string(in.begin() + from, in.begin() + range.first + offset)] attributes:baseAttributes]]; + [res appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithCxxString:std::string(in.begin() + range.first + offset, in.begin() + range.second + offset)] attributes:highlightAttributes]]; + from = range.second + offset; + } + if(from < in.size()) + [res appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithCxxString:in.substr(from)] attributes:baseAttributes]]; + + return res; +} diff --git a/Frameworks/OakFilterList/target b/Frameworks/OakFilterList/target index 58c5c9f9..a02cbb82 100644 --- a/Frameworks/OakFilterList/target +++ b/Frameworks/OakFilterList/target @@ -1,4 +1,4 @@ -SOURCES = src/*.mm src/datasources/*.{cc,mm} +SOURCES = src/*.mm src/datasources/*.{cc,mm} src/ui/*.mm EXPORT = src/OakFilterList.h src/FileChooser.h src/datasources/*.h CP_Resources = resources/* LINK += text bundles document io plist MGScopeBar ns OakAppKit OakFoundation OakSystem regexp scope OakFileBrowser