| Index: chrome/browser/cocoa/disclosure_view_controller.mm
|
| ===================================================================
|
| --- chrome/browser/cocoa/disclosure_view_controller.mm (revision 0)
|
| +++ chrome/browser/cocoa/disclosure_view_controller.mm (revision 0)
|
| @@ -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
|
|
|