//
// PSBrowser.m
// Perforce
//
// Created by Adam Czubernat on 18.06.2013.
// Copyright (c) 2013 Perforce Software, Inc. All rights reserved.
//
#import "PSBrowser.h"
@interface PSBrowser () {
CGFloat detailViewWidth;
}
- (void)layoutDetailViewAnimated:(BOOL)animated;
- (void)detailViewFrameChange:(NSNotification *)notification;
- (void)lastColumnFrameChange:(NSNotification *)notification;
- (NSTableView *)browserTableViewForColumn:(NSInteger)column;
@end
@implementation PSBrowser
@synthesize detailViewController;
// Dirty hack
typedef struct __BrcvFlags {
unsigned int isEmptyColumn:1;
unsigned int hasMarkedWidth:1;
unsigned int tileDisabled:1;
unsigned int drawsColumnDividerLine:1;
unsigned int reserved:28;
} __BrcvFlags;
- (BOOL)sendAction {
// Remove details pane
if ([self selectedRowInColumn:[self lastColumn]] == -1 || // When deselected row
[self selectionIndexPaths].count > 1) // or multiple selection
[self setDetailViewController:nil];
return [super sendAction];
}
- (void)loadColumnZero {
[super loadColumnZero];
[self setDetailViewController:nil];
}
- (void)addColumn {
[super addColumn];
// Hack to get column view
NSTableView *tableView = [self browserTableViewForColumn:self.lastColumn];
NSClipView *clipView = (id)tableView.superview;
NSView *columnView = clipView.superview;
// Hack to disable divider line in _NSBrowserColumnView from private api
__BrcvFlags *flags = PSRuntimeAddressOfInstanceVariable(columnView, "_brcvFlags");
flags->drawsColumnDividerLine = 0;
// Set background color
static NSColor *color;
if (!color)
color = [NSUserDefaults colorForKey:kColorBackgroundColumn];
[tableView setBackgroundColor:color];
// Remove tableColumn with default indicators
if ([tableView isKindOfClass:[NSTableView class]] && tableView.tableColumns.count)
[tableView removeTableColumn:tableView.tableColumns.lastObject];
// Insert separator at the end of columnTableView
static NSImage *image;
if (!image) {
image = [NSImage imageNamed:@"SeparatorVerticalDark.png"];
image = [image resizableImageWithTopCap:10.0f bottomCap:10.0f];
}
CGRect frame = columnView.bounds;
frame.origin.x = frame.size.width - image.size.width + 2.0f;
frame.size.width = image.size.width;
frame.origin.y = -200.0f; // Size it bigger than scroll
frame.size.height += 400.0f;
PSView *separator = [[PSView alloc] initWithFrame:frame];
[separator setAutoresizing:NSViewAutoresizingRightEdge | NSViewAutoresizingVertical];
[separator setImage:image];
[clipView addSubview:separator];
}
- (NSRect)frameOfColumn:(NSInteger)column {
CGRect frame = [super frameOfColumn:column];
if (column)
frame.origin.x -= 1.0f; // Remove divider width
return frame;
}
- (void)setFrame:(NSRect)frameRect {
[super setFrame:frameRect];
[self layoutDetailViewAnimated:NO];
}
- (BOOL)ignoreModifierKeysForDraggingSession:(NSDraggingSession *)session {
return NO;
}
- (void)keyDown:(NSEvent *)event {
unichar keyChar = [[event charactersIgnoringModifiers] characterAtIndex:0];
NSUInteger flags = [event modifierFlags] & NSDeviceIndependentModifierFlagsMask;
if (keyChar == NSDeleteCharacter && flags == NSCommandKeyMask) {
// Send delete notification
if ([_delegate respondsToSelector:@selector(browserDidReceiveCommandDeleteKey:)])
[_delegate browserDidReceiveCommandDeleteKey:self];
} else if (keyChar == ' ' && flags == 0) {
// Send space bar notification
if ([_delegate respondsToSelector:@selector(browserDidReceiveSpacebarKey:)])
[_delegate browserDidReceiveSpacebarKey:self];
} else if (keyChar == NSUpArrowFunctionKey || keyChar == NSDownArrowFunctionKey) {
// Relay Up and Down arrow keys from other views (QuickLook)
NSView *tableView = [self browserTableViewForColumn:self.selectedColumn];
[tableView keyDown:event];
} else {
[super keyDown:event];
}
}
- (BOOL)performKeyEquivalent:(NSEvent *)theEvent {
unichar keyChar = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
if (keyChar == 27) {
[self abortEditing];
[[self window] makeFirstResponder:self];
return YES;
}
return NO;
}
#pragma mark - Public
- (void)setDetailViewController:(NSViewController *)aDetailViewController {
if (detailViewController == aDetailViewController)
return;
[[NSNotificationCenter defaultCenter]
removeObserver:self name:NSViewFrameDidChangeNotification object:nil];
[detailViewController.view removeFromSuperview];
detailViewController = aDetailViewController;
detailViewWidth = detailViewController.view.frame.size.width;
if (detailViewController) {
NSArray *columns = [self valueForKey:@"_columns"];
NSView *lastColumn = [columns objectAtIndex:self.lastColumn];
NSView *view = detailViewController.view;
[view setAutoresizing:NSViewAutoresizingLeftEdge | NSViewAutoresizingVertical];
[lastColumn.superview addSubview:view];
[self layoutDetailViewAnimated:YES];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:@selector(detailViewFrameChange:)
name:NSViewFrameDidChangeNotification object:view];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:@selector(lastColumnFrameChange:)
name:NSViewFrameDidChangeNotification object:lastColumn];
}
}
#pragma mark - Private
- (void)layoutDetailViewAnimated:(BOOL)animated {
if (!detailViewController)
return;
NSView *detailView = detailViewController.view;
NSArray *columns = [self valueForKey:@"_columns"];
NSView *lastColumn = [columns objectAtIndex:self.lastColumn];
NSView *columnContainer = [lastColumn superview];
NSScrollView *scrollView = self.subviews.lastObject;
NSClipView *contentView = [scrollView contentView];
CGRect frame = lastColumn.frame;
frame.origin.x += frame.size.width;
frame.size.width = detailView.bounds.size.width;
detailView.frame = frame;
if ([self inLiveResize]) {
frame = columnContainer.frame;
frame.size.width = CGRectGetMaxX(detailView.frame);
columnContainer.frame = frame;
return;
}
frame = columnContainer.frame;
CGFloat oldWidth = frame.size.width;
CGFloat newWidth = CGRectGetMaxX(detailView.frame);
if (oldWidth == newWidth)
return;
void (^animation)() = ^{ };
frame.size.width = newWidth;
if (oldWidth < newWidth) {
CGPoint boundsOrigin = contentView.bounds.origin;
columnContainer.frame = frame;
CGRect bounds = contentView.bounds;
bounds.origin = boundsOrigin;
[contentView setBounds:bounds];
CGPoint point = detailView.frame.origin;
point.x -= self.bounds.size.width - detailView.bounds.size.width;
point.x = fmaxf(point.x, 0.0f);
animation = ^{
[(animated ? [contentView animator] : contentView) setBoundsOrigin:point];
[scrollView reflectScrolledClipView:contentView];
};
}
if (animated)
[NSView animateWithDuration:0.1f animations:animation];
else
animation();
}
- (void)detailViewFrameChange:(NSNotification *)notification {
CGFloat newDetailViewWidth = detailViewController.view.frame.size.width;
if (detailViewWidth != newDetailViewWidth) {
[self layoutDetailViewAnimated:YES];
detailViewWidth = newDetailViewWidth;
}
}
- (void)lastColumnFrameChange:(NSNotification *)notification {
[self layoutDetailViewAnimated:NO];
[self performSelector:@selector(layoutDetailViewAnimated:) withObject:@(YES) afterDelay:0];
}
- (NSTableView *)browserTableViewForColumn:(NSInteger)column {
if (column < 0)
return nil;
// Hack to get column views
NSArray *columns = [self valueForKey:@"_columns"];
NSView *columnView = [columns objectAtIndex:column];
// Retrieve NSBrowserTableView from subviews
NSClipView *clipView = nil;
NSTableView *tableView = nil;
for (NSView *subview in columnView.subviews) {
if ([subview isKindOfClass:[NSClipView class]])
clipView = (id)subview;
}
for (NSView *subview in clipView.subviews) {
if ([subview isKindOfClass:NSClassFromString(@"NSBrowserTableView")])
tableView = (id)subview;
}
return tableView;
}
@end
# |
Change |
User |
Description |
Committed |
|
#1
|
10691 |
DWishR |
Populate //guest/DWishR/piper/.... |
|
|
//guest/perforce_software/piper/Perforce/Classes/Views/PSBrowser.m |
#1
|
8919 |
Matt Attaway |
Initial add of Piper, a lightweight Perforce client for artists and designers. |
|
|