summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cocoa/disclosure_view_controller.mm
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/cocoa/disclosure_view_controller.mm')
-rw-r--r--chrome/browser/cocoa/disclosure_view_controller.mm189
1 files changed, 189 insertions, 0 deletions
diff --git a/chrome/browser/cocoa/disclosure_view_controller.mm b/chrome/browser/cocoa/disclosure_view_controller.mm
new file mode 100644
index 0000000..f41acd5
--- /dev/null
+++ b/chrome/browser/cocoa/disclosure_view_controller.mm
@@ -0,0 +1,189 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/cocoa/disclosure_view_controller.h"
+#include "base/logging.h"
+#include "base/scoped_nsobject.h"
+
+const NSCellStateValue kInitialDisclosureState = NSOffState;
+const NSInteger kClosedBoxHeight = 20;
+NSString* const kKVODisclosedKey = @"disclosed";
+
+// This class externalizes the state of the disclosure control. When the
+// disclosure control is pressed it changes the state of this object. In turn
+// the KVO machinery detects the change to |disclosed| and signals the
+// |observeValueForKeyPath| call in the |DisclosureViewController|.
+@interface DisclosureViewState : NSObject {
+ @private
+ NSCellStateValue disclosed;
+}
+@end
+
+@implementation DisclosureViewState
+@end
+
+@interface DisclosureViewController(PrivateMethods)
+
+- (void)initDisclosureState:(NSCellStateValue)state;
+- (NSRect)openStateFrameSize:(NSRect)startFrame;
+- (NSRect)closedStateFrameSize:(NSRect)startFrame;
+
+- (void)startAnimations:(NSView*)view
+ start:(NSRect)startFrame
+ end:(NSRect)endFrame;
+
+- (void)discloseDetails:(NSCellStateValue)state;
+
+- (void)observeValueForKeyPath:(NSString*)keyPath
+ ofObject:(id)object
+ change:(NSDictionary*)change
+ context:(void*)context;
+
+@end
+
+@implementation DisclosureViewController
+
+@synthesize disclosureState = disclosureState_;
+
+- (void)awakeFromNib {
+ // Create the disclosure state.
+ [self setDisclosureState:[[[DisclosureViewState alloc] init] autorelease]];
+
+ // Set up the initial disclosure state before we install the observer.
+ // We don't want our animations firing before we're done initializing.
+ [disclosureState_ setValue:[NSNumber numberWithInt:kInitialDisclosureState]
+ forKey:kKVODisclosedKey];
+
+ // Pick up "open" height from the initial state of the view in the nib.
+ openHeight_ = [[self view] frame].size.height;
+
+ // Set frame size according to initial disclosure state.
+ [self initDisclosureState:kInitialDisclosureState];
+
+ // Setup observers so that when disclosure state changes we resize frame
+ // accordingly.
+ [disclosureState_ addObserver:self forKeyPath:kKVODisclosedKey
+ options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld
+ context:nil];
+}
+
+- (void)dealloc {
+ [disclosureState_ removeObserver:self forKeyPath:kKVODisclosedKey];
+ [disclosureState_ release];
+ [super dealloc];
+}
+
+@end
+
+@implementation DisclosureViewController(PrivateMethods)
+
+// Initializes the view's frame geometry based on the input |state|.
+// If the |state| is NSOnState then the frame size corresponds to "open".
+// If the |state| is NSOffState then the frame size corresponds to "closed".
+// The |origin.x| and |size.width| remain unchanged, but the |origin.y| and
+// |size.height| may vary.
+- (void)initDisclosureState:(NSCellStateValue)state {
+ if (state == NSOnState) {
+ [[self view] setFrame:[self openStateFrameSize:[[self view] frame]]];
+ }
+ else if (state == NSOffState) {
+ [[self view] setFrame:[self closedStateFrameSize:[[self view] frame]]];
+ }
+ else {
+ NOTREACHED();
+ }
+}
+
+// Computes the frame geometry during the "open" state of the disclosure view.
+- (NSRect)openStateFrameSize:(NSRect)startFrame {
+ return NSMakeRect(startFrame.origin.x,
+ startFrame.size.height - openHeight_ +
+ startFrame.origin.y,
+ startFrame.size.width,
+ openHeight_);
+}
+
+// Computes the frame geometry during the "closed" state of the disclosure view.
+- (NSRect)closedStateFrameSize:(NSRect)startFrame {
+ return NSMakeRect(startFrame.origin.x,
+ startFrame.size.height - kClosedBoxHeight +
+ startFrame.origin.y,
+ startFrame.size.width,
+ kClosedBoxHeight);
+}
+
+// Animates the opening or closing of the disclosure view. The |startFrame|
+// specifies the frame geometry at the beginning of the animation and the
+// |endFrame| specifies the geometry at the end of the animation. The input
+// |view| is view managed by this controller.
+- (void)startAnimations:(NSView*)view
+ start:(NSRect)startFrame
+ end:(NSRect)endFrame
+{
+ // Setup dictionary describing animation.
+ // Create the attributes dictionary for the first view.
+ NSMutableDictionary* dictionary;
+ dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
+ // Specify which view to modify.
+ view, NSViewAnimationTargetKey,
+ // Specify the starting position of the view.
+ [NSValue valueWithRect:startFrame], NSViewAnimationStartFrameKey,
+ // Change the ending position of the view.
+ [NSValue valueWithRect:endFrame], NSViewAnimationEndFrameKey,
+ nil];
+
+ // Create the view animation object.
+ scoped_nsobject<NSViewAnimation> animation;
+ animation.reset([[NSViewAnimation alloc] initWithViewAnimations:
+ [NSArray arrayWithObject:dictionary]]);
+
+ // Set some additional attributes for the animation.
+ [animation.get() setDuration:.2];
+ [animation.get() setAnimationCurve:NSAnimationEaseIn];
+
+ // Run the animation.
+ [animation.get() startAnimation];
+}
+
+// This method is invoked when the disclosure state changes. It computes
+// the appropriate view frame geometry and then initiates the animation to
+// change that geometry.
+- (void)discloseDetails:(NSCellStateValue)state {
+ NSRect startFrame = [[self view] frame];
+ NSRect endFrame = startFrame;
+
+ if (state == NSOnState) {
+ endFrame = [self openStateFrameSize:startFrame];
+ } else if (state == NSOffState) {
+ endFrame = [self closedStateFrameSize:startFrame];
+ } else {
+ NOTREACHED();
+ return;
+ }
+
+ [self startAnimations:[self view] start:startFrame end:endFrame];
+}
+
+// The |DisclosureViewController| is an observer of an instance of a
+// |DisclosureViewState| object. This object lives within the controller's
+// nib file. When the KVO machinery detects a change to the state
+// it triggers this call and we initiate the change in frame geometry of the
+// view.
+- (void)observeValueForKeyPath:(NSString*)keyPath
+ ofObject:(id)object
+ change:(NSDictionary*)change
+ context:(void*)context {
+ if ([keyPath isEqualToString:kKVODisclosedKey]) {
+ NSCellStateValue newValue =
+ [[change objectForKey:NSKeyValueChangeNewKey] intValue];
+ NSCellStateValue oldValue =
+ [[change objectForKey:NSKeyValueChangeOldKey] intValue];
+
+ if (newValue != oldValue) {
+ [self discloseDetails:newValue];
+ }
+ }
+}
+
+@end