Refactor how we send text field movement keys to a table view

The code is now easier to reuse.
This commit is contained in:
Allan Odgaard
2014-08-07 20:43:33 +02:00
parent 5bb42e8bc6
commit 357c66b4d1
4 changed files with 86 additions and 87 deletions

View File

@@ -1,5 +1,6 @@
#import "OakChooser.h"
#import "ui/TableView.h"
#import "ui/TableViewAction.h"
#import "ui/SearchField.h"
#import <OakAppKit/OakAppKit.h>
#import <OakAppKit/OakUIConstructionFunctions.h>
@@ -31,7 +32,7 @@ NSMutableAttributedString* CreateAttributedStringWithMarkedUpRanges (std::string
return res;
}
@interface OakChooser () <NSWindowDelegate, NSTableViewDataSource, NSTableViewDelegate>
@interface OakChooser () <NSWindowDelegate, NSTextFieldDelegate, NSTableViewDataSource, NSTableViewDelegate>
@end
static void* kFirstResponderBinding = &kFirstResponderBinding;
@@ -48,6 +49,7 @@ static void* kFirstResponderBinding = &kFirstResponderBinding;
[_searchField.cell setSendsSearchStringImmediately:YES];
if(![NSApp isFullKeyboardAccessEnabled])
_searchField.focusRingType = NSFocusRingTypeNone;
_searchField.delegate = self;
NSTableColumn* tableColumn = [[NSTableColumn alloc] initWithIdentifier:@"name"];
tableColumn.dataCell = [[NSTextFieldCell alloc] initTextCell:@""];
@@ -64,7 +66,6 @@ static void* kFirstResponderBinding = &kFirstResponderBinding;
tableView.target = self;
tableView.dataSource = self;
tableView.delegate = self;
tableView.linkedTextField = _searchField;
if(nil != &NSAccessibilitySharedFocusElementsAttribute)
[_searchField.cell accessibilitySetOverrideValue:@[tableView] forAttribute:NSAccessibilitySharedFocusElementsAttribute];
_tableView = tableView;
@@ -118,6 +119,7 @@ static void* kFirstResponderBinding = &kFirstResponderBinding;
- (void)dealloc
{
_searchField.delegate = nil;
[_searchField unbind:NSValueBinding];
[_window removeObserver:self forKeyPath:@"firstResponder" context:kFirstResponderBinding];
@@ -168,6 +170,15 @@ static void* kFirstResponderBinding = &kFirstResponderBinding;
}
}
// ======================================================
// = Forward Search Field Movement Actions to TableView =
// ======================================================
- (BOOL)control:(NSControl*)aControl textView:(NSTextView*)aTextView doCommandBySelector:(SEL)aCommand
{
return OakPerformTableViewActionFromSelector(self.tableView, aCommand, aTextView);
}
// ==============
// = Properties =
// ==============

View File

@@ -1,75 +1,5 @@
#import "TableView.h"
#import <OakFoundation/NSString Additions.h>
#import <OakAppKit/OakAppKit.h> // Accessibility API missing from 10.7/10.8 SDK.
#import <oak/algorithm.h>
// ========================================
// = Forward NSTextField Movement Actions =
// ========================================
@interface OakTextFieldMovementDelegate : NSObject <NSTextFieldDelegate>
@property (nonatomic, weak) NSTableView* tableView;
@end
static NSString* const kUserDefaultsEnableLoopFilterList = @"enableLoopFilterList";
@implementation OakTextFieldMovementDelegate
- (void)moveSelectedRowByOffset:(NSInteger)anOffset extendingSelection:(BOOL)extend sender:(id)sender
{
if([_tableView numberOfRows])
{
if(_tableView.allowsMultipleSelection == NO)
extend = NO;
NSInteger row = [_tableView selectedRow] + anOffset;
NSInteger numberOfRows = [_tableView numberOfRows];
if(abs(anOffset) == 1 && numberOfRows && [[NSUserDefaults standardUserDefaults] boolForKey:kUserDefaultsEnableLoopFilterList])
row = (row + numberOfRows) % numberOfRows;
else row = oak::cap((NSInteger)0, row, numberOfRows - 1);
[_tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:extend];
[_tableView scrollRowToVisible:row];
}
}
- (int)visibleRows { return (int)floor(NSHeight([_tableView visibleRect]) / ([_tableView rowHeight]+[_tableView intercellSpacing].height)) - 1; }
- (void)moveUp:(id)sender { [self moveSelectedRowByOffset:-1 extendingSelection:NO sender:sender]; }
- (void)moveDown:(id)sender { [self moveSelectedRowByOffset:+1 extendingSelection:NO sender:sender]; }
- (void)moveUpAndModifySelection:(id)sender { [self moveSelectedRowByOffset:-1 extendingSelection:YES sender:sender]; }
- (void)moveDownAndModifySelection:(id)sender { [self moveSelectedRowByOffset:+1 extendingSelection:YES sender:sender]; }
- (void)movePageUp:(id)sender { [self moveSelectedRowByOffset:-[self visibleRows] extendingSelection:NO sender:sender]; }
- (void)movePageDown:(id)sender { [self moveSelectedRowByOffset:+[self visibleRows] extendingSelection:NO sender:sender]; }
- (void)moveToBeginningOfDocument:(id)sender { [self moveSelectedRowByOffset:-(INT_MAX >> 1) extendingSelection:NO sender:sender]; }
- (void)moveToEndOfDocument:(id)sender { [self moveSelectedRowByOffset:+(INT_MAX >> 1) extendingSelection:NO sender:sender]; }
- (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]; }
- (void)scrollToBeginningOfDocument:(id)sender { [self moveToBeginningOfDocument:sender]; }
- (void)scrollToEndOfDocument:(id)sender { [self moveToEndOfDocument: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<SEL>{ @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(scrollToBeginningOfDocument:), @selector(scrollToEndOfDocument:), @selector(insertNewline:), @selector(insertNewlineIgnoringFieldEditor:), @selector(cancelOperation:) };
if(aCommand == @selector(deleteToBeginningOfLine:) && [aControl.window tryToPerform:@selector(delete:) with:aControl])
return YES;
else if(forward->find(aCommand) != forward->end() && [self respondsToSelector:aCommand])
return [NSApp sendAction:aCommand to:self from:aControl];
return NO;
}
@end
// ========================================
@interface OakInactiveTableView ()
@property (nonatomic) OakTextFieldMovementDelegate* textFieldMovementDelegate;
@end
@implementation OakInactiveTableView
- (NSCell*)preparedCellAtColumn:(NSInteger)column row:(NSInteger)row
@@ -99,21 +29,6 @@ static NSString* const kUserDefaultsEnableLoopFilterList = @"enableLoopFilterLis
}];
}
- (void)setLinkedTextField:(NSTextField*)aTextField
{
_textFieldMovementDelegate = [OakTextFieldMovementDelegate new];
_textFieldMovementDelegate.tableView = self;
_linkedTextField.delegate = nil;
_linkedTextField = aTextField;
_linkedTextField.delegate = _textFieldMovementDelegate;
}
- (void)dealloc
{
_linkedTextField.delegate = nil;
}
- (void)setDrawAsHighlighted:(BOOL)flag
{
if(_drawAsHighlighted == flag)

View File

@@ -0,0 +1 @@
BOOL OakPerformTableViewActionFromSelector (NSTableView* tableView, SEL selector, NSTextView* textView);

View File

@@ -0,0 +1,72 @@
#import "TableViewAction.h"
#import <oak/algorithm.h>
static NSString* const kUserDefaultsEnableLoopFilterList = @"enableLoopFilterList";
@interface OakTableViewActionHelper : NSResponder
@property (nonatomic) NSTableView* tableView;
@end
@implementation OakTableViewActionHelper
+ (instancetype)tableViewActionHelperWithTableView:(NSTableView*)aTableView
{
OakTableViewActionHelper* helper = [[self alloc] init];
helper.tableView = aTableView;
return helper;
}
- (void)moveSelectedRowByOffset:(NSInteger)anOffset extendingSelection:(BOOL)extend sender:(id)sender
{
if([_tableView numberOfRows])
{
if(_tableView.allowsMultipleSelection == NO)
extend = NO;
NSInteger row = [_tableView selectedRow] + anOffset;
NSInteger numberOfRows = [_tableView numberOfRows];
if(abs(anOffset) == 1 && numberOfRows && [[NSUserDefaults standardUserDefaults] boolForKey:kUserDefaultsEnableLoopFilterList])
row = (row + numberOfRows) % numberOfRows;
else row = oak::cap((NSInteger)0, row, numberOfRows - 1);
[_tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:extend];
[_tableView scrollRowToVisible:row];
}
}
- (int)visibleRows { return (int)floor(NSHeight([_tableView visibleRect]) / ([_tableView rowHeight]+[_tableView intercellSpacing].height)) - 1; }
- (void)moveUp:(id)sender { [self moveSelectedRowByOffset:-1 extendingSelection:NO sender:sender]; }
- (void)moveDown:(id)sender { [self moveSelectedRowByOffset:+1 extendingSelection:NO sender:sender]; }
- (void)moveUpAndModifySelection:(id)sender { [self moveSelectedRowByOffset:-1 extendingSelection:YES sender:sender]; }
- (void)moveDownAndModifySelection:(id)sender { [self moveSelectedRowByOffset:+1 extendingSelection:YES sender:sender]; }
- (void)movePageUp:(id)sender { [self moveSelectedRowByOffset:-[self visibleRows] extendingSelection:NO sender:sender]; }
- (void)movePageDown:(id)sender { [self moveSelectedRowByOffset:+[self visibleRows] extendingSelection:NO sender:sender]; }
- (void)moveToBeginningOfDocument:(id)sender { [self moveSelectedRowByOffset:-(INT_MAX >> 1) extendingSelection:NO sender:sender]; }
- (void)moveToEndOfDocument:(id)sender { [self moveSelectedRowByOffset:+(INT_MAX >> 1) extendingSelection:NO sender:sender]; }
- (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]; }
- (void)scrollToBeginningOfDocument:(id)sender { [self moveToBeginningOfDocument:sender]; }
- (void)scrollToEndOfDocument:(id)sender { [self moveToEndOfDocument: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)doCommandBySelector:(SEL)aSelector withSender:(id)aSender
{
static auto const forward = new std::set<SEL>{ @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(scrollToBeginningOfDocument:), @selector(scrollToEndOfDocument:), @selector(insertNewline:), @selector(insertNewlineIgnoringFieldEditor:), @selector(cancelOperation:) };
if(forward->find(aSelector) != forward->end() && [self respondsToSelector:aSelector])
return [NSApp sendAction:aSelector to:self from:aSender];
return NO;
}
@end
BOOL OakPerformTableViewActionFromSelector (NSTableView* tableView, SEL selector, NSTextView* textView)
{
if(selector == @selector(deleteToBeginningOfLine:) && [textView.window tryToPerform:@selector(delete:) with:textView])
return YES;
return [[OakTableViewActionHelper tableViewActionHelperWithTableView:tableView] doCommandBySelector:selector withSender:textView];
}