Files
textmate/Frameworks/OakTextView/src/OakChoiceMenu.mm
Allan Odgaard c7934a5ae5 Do not hide pop-up list when losing focus
The “hide on focus” setting is inherited by the parent window, causing the entire window to hide.

Closes issue #590.
2012-12-15 20:41:03 +01:00

229 lines
7.8 KiB
Plaintext

#import "OakChoiceMenu.h"
#import <oak/algorithm.h>
NSUInteger const OakChoiceMenuKeyUnused = 0;
NSUInteger const OakChoiceMenuKeyReturn = 1;
NSUInteger const OakChoiceMenuKeyTab = 2;
NSUInteger const OakChoiceMenuKeyCancel = 3;
NSUInteger const OakChoiceMenuKeyMovement = 4;
@interface OakChoiceMenu ()
@property (nonatomic, retain) NSWindow* window;
@end
enum action_t { kActionNop, kActionTab, kActionReturn, kActionCancel, kActionMoveUp, kActionMoveDown, kActionPageUp, kActionPageDown, kActionMoveToBeginning, kActionMoveToEnd };
@implementation OakChoiceMenu
@synthesize choices, choiceIndex, window;
- (id)init
{
if((self = [super init]))
{
self.choices = [NSArray array];
choiceIndex = NSNotFound;
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[tableView release];
self.window = nil;
self.choices = nil;
[super dealloc];
}
- (void)sizeToFit
{
CGFloat width = 60;
NSCell* dataCell = [[tableView.tableColumns lastObject] dataCell];
for(size_t i = 0; i < [choices count]; ++i)
{
[dataCell setStringValue:[choices objectAtIndex:i]];
width = std::max(width, [dataCell cellSize].width + 4);
}
if([choices count] > 10)
width += 15;
CGFloat height = std::min<NSUInteger>([choices count], 10) * ([tableView rowHeight]+[tableView intercellSpacing].height);
NSRect frame = { { NSMinX(window.frame), NSMaxY(window.frame) - height }, { std::min<CGFloat>(ceil(width), 400), height } };
[window setFrame:frame display:YES];
}
- (void)setWindow:(NSWindow*)aWindow
{
if(aWindow == window)
return;
[[window parentWindow] removeChildWindow:window];
[window release];
window = [aWindow retain];
}
- (void)viewBoundsDidChange:(NSNotification*)aNotification
{
NSView* aView = [[aNotification object] documentView];
[window setFrameTopLeftPoint:[[aView window] convertBaseToScreen:[aView convertPointToBase:topLeftPosition]]];
}
- (NSString*)selectedChoice
{
return choiceIndex == NSNotFound ? nil : [choices objectAtIndex:choiceIndex];
}
- (void)setChoices:(NSArray*)newChoices
{
if([choices isEqualToArray:newChoices])
return;
id oldSelection = self.selectedChoice;
self.choiceIndex = NSNotFound;
[choices autorelease];
choices = [newChoices retain];
[tableView reloadData];
self.choiceIndex = [choices indexOfObject:oldSelection];
[self sizeToFit];
}
- (void)setChoiceIndex:(NSUInteger)newIndex
{
if(choiceIndex != newIndex)
{
choiceIndex = newIndex;
if(choiceIndex == NSNotFound)
{
[tableView deselectAll:self];
}
else
{
[tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:choiceIndex] byExtendingSelection:NO];
[tableView scrollRectToVisible:[tableView rectOfRow:choiceIndex]];
}
}
}
- (NSInteger)numberOfRowsInTableView:(NSTableView*)aTableView
{
return [choices count];
}
- (id)tableView:(NSTableView*)aTableView objectValueForTableColumn:(NSTableColumn*)aTableColumn row:(NSInteger)rowIndex
{
return [choices objectAtIndex:rowIndex];
}
- (void)showAtTopLeftPoint:(NSPoint)aPoint forView:(NSView*)aView
{
window = [[NSPanel alloc] initWithContentRect:NSMakeRect(aPoint.x, aPoint.y, 0, 0) styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
[window setReleasedWhenClosed:NO];
[window setOpaque:NO];
window.alphaValue = 0.97;
window.backgroundColor = [NSColor colorWithCalibratedRed:1.0 green:0.96 blue:0.76 alpha:1.0];
window.hasShadow = YES;
window.level = NSStatusWindowLevel;
window.ignoresMouseEvents = YES;
tableView = [[NSTableView alloc] initWithFrame:NSZeroRect];
[tableView addTableColumn:[[[NSTableColumn alloc] initWithIdentifier:@"mainColumn"] autorelease]];
tableView.headerView = nil;
tableView.focusRingType = NSFocusRingTypeNone;
tableView.autoresizingMask = NSViewWidthSizable|NSViewHeightSizable;
tableView.usesAlternatingRowBackgroundColors = YES;
tableView.allowsMultipleSelection = YES;
tableView.dataSource = self;
// tableView.delegate = self;
[tableView reloadData];
NSScrollView* scrollView = [[[NSScrollView alloc] initWithFrame:NSZeroRect] autorelease];
scrollView.hasVerticalScroller = YES;
scrollView.hasHorizontalScroller = NO;
scrollView.autohidesScrollers = YES;
scrollView.borderType = NSNoBorder;
scrollView.documentView = tableView;
scrollView.autoresizingMask = NSViewWidthSizable|NSViewHeightSizable;
[window setContentView:scrollView];
if(choiceIndex != NSNotFound)
[tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:choiceIndex] byExtendingSelection:NO];
[self sizeToFit];
topLeftPosition = [aView convertPointFromBase:[[aView window] convertScreenToBase:aPoint]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewBoundsDidChange:) name:NSViewBoundsDidChangeNotification object:[[aView enclosingScrollView] contentView]];
[[aView window] addChildWindow:window ordered:NSWindowAbove];
[window orderFront:self];
}
- (BOOL)isVisible
{
return [window isVisible];
}
- (NSUInteger)didHandleKeyEvent:(NSEvent*)anEvent
{
NSUInteger res = OakChoiceMenuKeyUnused;
if(!window)
return res;
keyAction = kActionNop;
[self interpretKeyEvents:@[ anEvent ]];
if(keyAction == kActionNop)
return res;
NSInteger offset = 0;
NSInteger visibleRows = floor(NSHeight([tableView visibleRect]) / ([tableView rowHeight]+[tableView intercellSpacing].height)) - 1;
res = OakChoiceMenuKeyMovement;
switch(keyAction)
{
case kActionMoveUp: offset = -1; break;
case kActionMoveDown: offset = +1; break;
case kActionPageUp: offset = -visibleRows; break;
case kActionPageDown: offset = +visibleRows; break;
case kActionMoveToBeginning: offset = -(INT_MAX >> 1); break;
case kActionMoveToEnd: offset = +(INT_MAX >> 1); break;
case kActionReturn: res = OakChoiceMenuKeyReturn; break;
case kActionTab: res = OakChoiceMenuKeyTab; break;
case kActionCancel: res = OakChoiceMenuKeyCancel; break;
}
if(res == OakChoiceMenuKeyMovement)
self.choiceIndex = oak::cap<NSInteger>(0, (choiceIndex == NSNotFound ? (offset > 0 ? -1 : [choices count]) : choiceIndex) + offset, [choices count] - 1);;
return res;
}
- (void)doCommandBySelector:(SEL)aSelector
{
if([self respondsToSelector:aSelector])
[self performSelector:aSelector withObject:self];
}
- (void)insertText:(id)aString { }
- (void)insertNewline:(id)sender { keyAction = kActionReturn; }
- (void)insertTab:(id)sender { keyAction = kActionTab; }
- (void)cancelOperation:(id)sender { keyAction = kActionCancel; }
- (void)moveUp:(id)sender { keyAction = kActionMoveUp; }
- (void)moveDown:(id)sender { keyAction = kActionMoveDown; }
- (void)movePageUp:(id)sender { keyAction = kActionPageUp; }
- (void)movePageDown:(id)sender { keyAction = kActionPageDown; }
- (void)pageUp:(id)sender { keyAction = kActionPageUp; }
- (void)pageDown:(id)sender { keyAction = kActionPageDown; }
- (void)scrollPageUp:(id)sender { keyAction = kActionPageUp; }
- (void)scrollPageDown:(id)sender { keyAction = kActionPageDown; }
- (void)moveToBeginningOfDocument:(id)sender { keyAction = kActionMoveToBeginning; }
- (void)moveToEndOfDocument:(id)sender { keyAction = kActionMoveToEnd; }
- (void)scrollToBeginningOfDocument:(id)sender { keyAction = kActionMoveToBeginning; }
- (void)scrollToEndOfDocument:(id)sender { keyAction = kActionMoveToEnd; }
@end