From c43c4facbb2055ebff178c80ccdd5b72fc50e55e Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 26 May 2014 11:24:50 -0400 Subject: [PATCH 1/4] An outline view that can be scrolled without it having fits. --- Rebel.xcodeproj/project.pbxproj | 8 +++++++ Rebel/RBLOutlineView.h | 22 ++++++++++++++++++++ Rebel/RBLOutlineView.m | 37 +++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 Rebel/RBLOutlineView.h create mode 100644 Rebel/RBLOutlineView.m diff --git a/Rebel.xcodeproj/project.pbxproj b/Rebel.xcodeproj/project.pbxproj index ad2b80b..36d2843 100644 --- a/Rebel.xcodeproj/project.pbxproj +++ b/Rebel.xcodeproj/project.pbxproj @@ -67,6 +67,8 @@ D0E91C581603CC7600D23E93 /* RBLClipView.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E91C561603CC7600D23E93 /* RBLClipView.h */; settings = {ATTRIBUTES = (Public, ); }; }; D0E91C591603CC7600D23E93 /* RBLClipView.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E91C571603CC7600D23E93 /* RBLClipView.m */; }; D0E91C5B1603CDC300D23E93 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0E91C5A1603CDC300D23E93 /* QuartzCore.framework */; }; + D419E3B7193392AD00F77217 /* RBLOutlineView.h in Headers */ = {isa = PBXBuildFile; fileRef = D419E3B5193392AD00F77217 /* RBLOutlineView.h */; }; + D419E3B8193392AD00F77217 /* RBLOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = D419E3B6193392AD00F77217 /* RBLOutlineView.m */; }; D4ACA4CB18DB95F900EBD899 /* libExpecta.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D4ACA4CA18DB95F900EBD899 /* libExpecta.a */; }; D4ACA4CD18DB95FC00EBD899 /* libSpecta.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D4ACA4CC18DB95FC00EBD899 /* libSpecta.a */; }; F63D7C78171D31010006515F /* RBLResizableImageSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = F63D7C77171D31010006515F /* RBLResizableImageSpec.m */; }; @@ -173,6 +175,8 @@ D0E91C561603CC7600D23E93 /* RBLClipView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RBLClipView.h; sourceTree = ""; }; D0E91C571603CC7600D23E93 /* RBLClipView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBLClipView.m; sourceTree = ""; }; D0E91C5A1603CDC300D23E93 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + D419E3B5193392AD00F77217 /* RBLOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RBLOutlineView.h; sourceTree = ""; }; + D419E3B6193392AD00F77217 /* RBLOutlineView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBLOutlineView.m; sourceTree = ""; }; D4ACA4CA18DB95F900EBD899 /* libExpecta.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libExpecta.a; path = External/expecta/build/Debug/libExpecta.a; sourceTree = ""; }; D4ACA4CC18DB95FC00EBD899 /* libSpecta.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libSpecta.a; path = External/specta/build/Debug/libSpecta.a; sourceTree = ""; }; F63D7C77171D31010006515F /* RBLResizableImageSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBLResizableImageSpec.m; sourceTree = ""; }; @@ -239,6 +243,8 @@ 88347E6916F213C500872A0A /* RBLHTMLView.m */, 30309E8B17228C1700BC2923 /* RBLTableView.h */, 30309E8C17228C1700BC2923 /* RBLTableView.m */, + D419E3B5193392AD00F77217 /* RBLOutlineView.h */, + D419E3B6193392AD00F77217 /* RBLOutlineView.m */, ); name = Classes; sourceTree = ""; @@ -473,6 +479,7 @@ B85EE8ED163EE15800265870 /* NSObject+RBObjectSizzlingAdditions.h in Headers */, AB61F6D41638370900DD5D2C /* NSApplication+RBLBlockAdditions.h in Headers */, 88347E6A16F213C500872A0A /* RBLHTMLView.h in Headers */, + D419E3B7193392AD00F77217 /* RBLOutlineView.h in Headers */, AB10DB4D166EA9CA00AB7AB1 /* RBLScrollView.h in Headers */, 30309E8D17228C1700BC2923 /* RBLTableView.h in Headers */, 303DEF4416D2B88700BD65C4 /* RBLShadowedTextFieldCell.h in Headers */, @@ -581,6 +588,7 @@ D0410EAC15C61895003A3203 /* NSTextView+RBLAntialiasingAdditions.m in Sources */, D04CBFC915F6D87B004A5BCC /* NSView+RBLAnimationAdditions.m in Sources */, 306A81DC1601C7A300BF45F3 /* RBLPopover.m in Sources */, + D419E3B8193392AD00F77217 /* RBLOutlineView.m in Sources */, 306A81E91601E92000BF45F3 /* CAAnimation+RBLBlockAdditions.m in Sources */, D0E91C591603CC7600D23E93 /* RBLClipView.m in Sources */, D011FD5F16239A8600A27946 /* NSImage+RBLResizableImageAdditions.m in Sources */, diff --git a/Rebel/RBLOutlineView.h b/Rebel/RBLOutlineView.h new file mode 100644 index 0000000..ec6e08c --- /dev/null +++ b/Rebel/RBLOutlineView.h @@ -0,0 +1,22 @@ +// +// RBLOutlineView.h +// Rebel +// +// Created by Rob Rix on 26/05/2014. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +#import + +/// A standard outline view with one fix. +/// +/// As opposed to trying to scroll rects into the middle of the view each time, +/// we move them just enough as to make them visible. This fixes the outline +/// view appearing to have some kind of seizure when you, for example, hold +/// down an arrow key to scroll through its cells really fast. +/// +/// This fix applies to both cell and view based outline views. This is the +/// same fix as implemented in `RBLTableView`. +@interface RBLOutlineView : NSOutlineView + +@end diff --git a/Rebel/RBLOutlineView.m b/Rebel/RBLOutlineView.m new file mode 100644 index 0000000..a4eb9ee --- /dev/null +++ b/Rebel/RBLOutlineView.m @@ -0,0 +1,37 @@ +// +// RBLOutlineView.m +// Rebel +// +// Created by Rob Rix on 26/05/2014. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +#import "RBLOutlineView.h" + +@implementation RBLOutlineView + +- (BOOL)scrollRectToVisible:(NSRect)aRect { + NSScrollView *scrollView = self.enclosingScrollView; + NSRect visibleRect = self.visibleRect; + + void (^scrollToY)(CGFloat) = ^(CGFloat y) { + NSPoint pointToScrollTo = NSMakePoint(0, y); + + [scrollView.contentView scrollToPoint:pointToScrollTo]; + [scrollView reflectScrolledClipView:scrollView.contentView]; + }; + + if (NSMinY(aRect) < NSMinY(visibleRect)) { + scrollToY(NSMinY(aRect)); + return YES; + } + + if (NSMaxY(aRect) > NSMaxY(visibleRect)) { + scrollToY(NSMaxY(aRect) - NSHeight(visibleRect)); + return YES; + } + + return NO; +} + +@end From 53329828412a74c6203db3a2860f1ad718f9f939 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Tue, 27 May 2014 09:03:16 -0400 Subject: [PATCH 2/4] Factor -scrollRectToVisible: out into a function. --- Rebel.xcodeproj/project.pbxproj | 8 ++++++++ Rebel/RBLOutlineView.m | 23 ++--------------------- Rebel/RBLScrolling.h | 17 +++++++++++++++++ Rebel/RBLScrolling.m | 33 +++++++++++++++++++++++++++++++++ Rebel/RBLTableView.m | 23 ++--------------------- 5 files changed, 62 insertions(+), 42 deletions(-) create mode 100644 Rebel/RBLScrolling.h create mode 100644 Rebel/RBLScrolling.m diff --git a/Rebel.xcodeproj/project.pbxproj b/Rebel.xcodeproj/project.pbxproj index 36d2843..261751c 100644 --- a/Rebel.xcodeproj/project.pbxproj +++ b/Rebel.xcodeproj/project.pbxproj @@ -69,6 +69,8 @@ D0E91C5B1603CDC300D23E93 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0E91C5A1603CDC300D23E93 /* QuartzCore.framework */; }; D419E3B7193392AD00F77217 /* RBLOutlineView.h in Headers */ = {isa = PBXBuildFile; fileRef = D419E3B5193392AD00F77217 /* RBLOutlineView.h */; }; D419E3B8193392AD00F77217 /* RBLOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = D419E3B6193392AD00F77217 /* RBLOutlineView.m */; }; + D4559AE81934C2910051A6BE /* RBLScrolling.h in Headers */ = {isa = PBXBuildFile; fileRef = D4559AE61934C2910051A6BE /* RBLScrolling.h */; }; + D4559AE91934C2910051A6BE /* RBLScrolling.m in Sources */ = {isa = PBXBuildFile; fileRef = D4559AE71934C2910051A6BE /* RBLScrolling.m */; }; D4ACA4CB18DB95F900EBD899 /* libExpecta.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D4ACA4CA18DB95F900EBD899 /* libExpecta.a */; }; D4ACA4CD18DB95FC00EBD899 /* libSpecta.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D4ACA4CC18DB95FC00EBD899 /* libSpecta.a */; }; F63D7C78171D31010006515F /* RBLResizableImageSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = F63D7C77171D31010006515F /* RBLResizableImageSpec.m */; }; @@ -177,6 +179,8 @@ D0E91C5A1603CDC300D23E93 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; D419E3B5193392AD00F77217 /* RBLOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RBLOutlineView.h; sourceTree = ""; }; D419E3B6193392AD00F77217 /* RBLOutlineView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBLOutlineView.m; sourceTree = ""; }; + D4559AE61934C2910051A6BE /* RBLScrolling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RBLScrolling.h; sourceTree = ""; }; + D4559AE71934C2910051A6BE /* RBLScrolling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBLScrolling.m; sourceTree = ""; }; D4ACA4CA18DB95F900EBD899 /* libExpecta.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libExpecta.a; path = External/expecta/build/Debug/libExpecta.a; sourceTree = ""; }; D4ACA4CC18DB95FC00EBD899 /* libSpecta.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libSpecta.a; path = External/specta/build/Debug/libSpecta.a; sourceTree = ""; }; F63D7C77171D31010006515F /* RBLResizableImageSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBLResizableImageSpec.m; sourceTree = ""; }; @@ -245,6 +249,8 @@ 30309E8C17228C1700BC2923 /* RBLTableView.m */, D419E3B5193392AD00F77217 /* RBLOutlineView.h */, D419E3B6193392AD00F77217 /* RBLOutlineView.m */, + D4559AE61934C2910051A6BE /* RBLScrolling.h */, + D4559AE71934C2910051A6BE /* RBLScrolling.m */, ); name = Classes; sourceTree = ""; @@ -475,6 +481,7 @@ B85EE8DE163ECA8C00265870 /* RBLViewController.h in Headers */, F64F8DEA1797E1F400082A3F /* RBLSlidingContainerView.h in Headers */, F6A562C01797DC5A00B651C0 /* RBLExpandingContainerView.h in Headers */, + D4559AE81934C2910051A6BE /* RBLScrolling.h in Headers */, B85EE8E9163ECF4C00265870 /* NSView+RBLViewControllerAdditions.h in Headers */, B85EE8ED163EE15800265870 /* NSObject+RBObjectSizzlingAdditions.h in Headers */, AB61F6D41638370900DD5D2C /* NSApplication+RBLBlockAdditions.h in Headers */, @@ -595,6 +602,7 @@ AB61F63716373FE000DD5D2C /* RBLTableCellView.m in Sources */, F64F8DEB1797E1F400082A3F /* RBLSlidingContainerView.m in Sources */, B85EE8DF163ECA8C00265870 /* RBLViewController.m in Sources */, + D4559AE91934C2910051A6BE /* RBLScrolling.m in Sources */, B85EE8EA163ECF4C00265870 /* NSView+RBLViewControllerAdditions.m in Sources */, B85EE8EE163EE15800265870 /* NSObject+RBObjectSizzlingAdditions.m in Sources */, AB61F6D51638370900DD5D2C /* NSApplication+RBLBlockAdditions.m in Sources */, diff --git a/Rebel/RBLOutlineView.m b/Rebel/RBLOutlineView.m index a4eb9ee..58c7d7b 100644 --- a/Rebel/RBLOutlineView.m +++ b/Rebel/RBLOutlineView.m @@ -7,31 +7,12 @@ // #import "RBLOutlineView.h" +#import "RBLScrolling.h" @implementation RBLOutlineView - (BOOL)scrollRectToVisible:(NSRect)aRect { - NSScrollView *scrollView = self.enclosingScrollView; - NSRect visibleRect = self.visibleRect; - - void (^scrollToY)(CGFloat) = ^(CGFloat y) { - NSPoint pointToScrollTo = NSMakePoint(0, y); - - [scrollView.contentView scrollToPoint:pointToScrollTo]; - [scrollView reflectScrolledClipView:scrollView.contentView]; - }; - - if (NSMinY(aRect) < NSMinY(visibleRect)) { - scrollToY(NSMinY(aRect)); - return YES; - } - - if (NSMaxY(aRect) > NSMaxY(visibleRect)) { - scrollToY(NSMaxY(aRect) - NSHeight(visibleRect)); - return YES; - } - - return NO; + return RBLScrollRectInViewToVisible(self, aRect); } @end diff --git a/Rebel/RBLScrolling.h b/Rebel/RBLScrolling.h new file mode 100644 index 0000000..00c7ff7 --- /dev/null +++ b/Rebel/RBLScrolling.h @@ -0,0 +1,17 @@ +// +// RBLScrolling.h +// Rebel +// +// Created by Rob Rix on 27/05/2014. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +#import + +/// Scrolls `view`’s `enclosingScrollView` just enough to reveal `rect`, +/// rather than scrolling it such that it lies in the middle of the new +/// `visibleRect`. This resolves issues where scrolling quickly through +/// long lists, e.g. by holding down the down arrow key in a table or +/// outline view, will otherwise jump wildly around the scrollable +/// region. +BOOL RBLScrollRectInViewToVisible(NSView *view, NSRect rect); diff --git a/Rebel/RBLScrolling.m b/Rebel/RBLScrolling.m new file mode 100644 index 0000000..95d5c17 --- /dev/null +++ b/Rebel/RBLScrolling.m @@ -0,0 +1,33 @@ +// +// RBLScrolling.m +// Rebel +// +// Created by Rob Rix on 27/05/2014. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +#import "RBLScrolling.h" + +BOOL RBLScrollRectInViewToVisible(NSView *view, NSRect rect) { + NSScrollView *scrollView = view.enclosingScrollView; + NSRect visibleRect = view.visibleRect; + + void (^scrollToY)(CGFloat) = ^(CGFloat y) { + NSPoint pointToScrollTo = NSMakePoint(0, y); + + [scrollView.contentView scrollToPoint:pointToScrollTo]; + [scrollView reflectScrolledClipView:scrollView.contentView]; + }; + + if (NSMinY(rect) < NSMinY(visibleRect)) { + scrollToY(NSMinY(rect)); + return YES; + } + + if (NSMaxY(rect) > NSMaxY(visibleRect)) { + scrollToY(NSMaxY(rect) - NSHeight(visibleRect)); + return YES; + } + + return NO; +} diff --git a/Rebel/RBLTableView.m b/Rebel/RBLTableView.m index a75eef5..856a339 100644 --- a/Rebel/RBLTableView.m +++ b/Rebel/RBLTableView.m @@ -7,31 +7,12 @@ // #import "RBLTableView.h" +#import "RBLScrolling.h" @implementation RBLTableView - (BOOL)scrollRectToVisible:(NSRect)aRect { - NSScrollView *scrollView = self.enclosingScrollView; - NSRect visibleRect = self.visibleRect; - - void (^scrollToY)(CGFloat) = ^(CGFloat y) { - NSPoint pointToScrollTo = NSMakePoint(0, y); - - [scrollView.contentView scrollToPoint:pointToScrollTo]; - [scrollView reflectScrolledClipView:scrollView.contentView]; - }; - - if (NSMinY(aRect) < NSMinY(visibleRect)) { - scrollToY(NSMinY(aRect)); - return YES; - } - - if (NSMaxY(aRect) > NSMaxY(visibleRect)) { - scrollToY(NSMaxY(aRect) - NSHeight(visibleRect)); - return YES; - } - - return NO; + return RBLScrollRectInViewToVisible(self, aRect); } @end From e1a62b1f179b22244cd4a9ee7b4efa095fff8e93 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Tue, 27 May 2014 09:03:36 -0400 Subject: [PATCH 3/4] Correct the visibility of the outline view header. --- Rebel.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rebel.xcodeproj/project.pbxproj b/Rebel.xcodeproj/project.pbxproj index 261751c..f960e1f 100644 --- a/Rebel.xcodeproj/project.pbxproj +++ b/Rebel.xcodeproj/project.pbxproj @@ -67,7 +67,7 @@ D0E91C581603CC7600D23E93 /* RBLClipView.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E91C561603CC7600D23E93 /* RBLClipView.h */; settings = {ATTRIBUTES = (Public, ); }; }; D0E91C591603CC7600D23E93 /* RBLClipView.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E91C571603CC7600D23E93 /* RBLClipView.m */; }; D0E91C5B1603CDC300D23E93 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0E91C5A1603CDC300D23E93 /* QuartzCore.framework */; }; - D419E3B7193392AD00F77217 /* RBLOutlineView.h in Headers */ = {isa = PBXBuildFile; fileRef = D419E3B5193392AD00F77217 /* RBLOutlineView.h */; }; + D419E3B7193392AD00F77217 /* RBLOutlineView.h in Headers */ = {isa = PBXBuildFile; fileRef = D419E3B5193392AD00F77217 /* RBLOutlineView.h */; settings = {ATTRIBUTES = (Public, ); }; }; D419E3B8193392AD00F77217 /* RBLOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = D419E3B6193392AD00F77217 /* RBLOutlineView.m */; }; D4559AE81934C2910051A6BE /* RBLScrolling.h in Headers */ = {isa = PBXBuildFile; fileRef = D4559AE61934C2910051A6BE /* RBLScrolling.h */; }; D4559AE91934C2910051A6BE /* RBLScrolling.m in Sources */ = {isa = PBXBuildFile; fileRef = D4559AE71934C2910051A6BE /* RBLScrolling.m */; }; From ffb777f1896e614b69d6ef955e0cf5f34d2768cd Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Tue, 27 May 2014 11:30:21 -0400 Subject: [PATCH 4/4] Add the outline view to the umbrella header. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There doesn’t seem to be any specific style of organization to this, so I’ve just added it at the end. --- Rebel/Rebel.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Rebel/Rebel.h b/Rebel/Rebel.h index 4cac595..8dddcdd 100644 --- a/Rebel/Rebel.h +++ b/Rebel/Rebel.h @@ -26,3 +26,4 @@ #import #import #import +#import