Chromium Code Reviews| Index: chrome/browser/cocoa/preferences_window_controller.mm | 
| =================================================================== | 
| --- chrome/browser/cocoa/preferences_window_controller.mm (revision 30612) | 
| +++ chrome/browser/cocoa/preferences_window_controller.mm (working copy) | 
| @@ -37,6 +37,7 @@ | 
| #include "grit/generated_resources.h" | 
| #include "grit/locale_settings.h" | 
| #include "net/base/cookie_policy.h" | 
| +#import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" | 
| NSString* const kUserDoneEditingPrefsNotification = | 
| @"kUserDoneEditingPrefsNotification"; | 
| @@ -63,6 +64,174 @@ | 
| } | 
| } | 
| +// Helper for tweaking the prefs window, if view is a: | 
| +// checkbox, radio group or label: it gets a forced wrap at current size | 
| +// editable field: left as is | 
| +// anything else: do +[GTMUILocalizerAndLayoutTweaker sizeToFitView:] | 
| +NSSize WrapOrSizeToFit(NSView* view) { | 
| + if ([view isKindOfClass:[NSTextField class]]) { | 
| + NSTextField* textField = static_cast<NSTextField*>(view); | 
| + if ([textField isEditable]) | 
| + return NSZeroSize; | 
| + CGFloat heightChange = | 
| + [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:textField]; | 
| + return NSMakeSize(0.0, heightChange); | 
| + } | 
| + if ([view isKindOfClass:[NSMatrix class]]) { | 
| + NSMatrix* radioGroup = static_cast<NSMatrix*>(view); | 
| + [GTMUILocalizerAndLayoutTweaker wrapRadioGroupForWidth:radioGroup]; | 
| + return [GTMUILocalizerAndLayoutTweaker sizeToFitView:view]; | 
| + } | 
| + if ([view isKindOfClass:[NSButton class]]) { | 
| + NSButton* button = static_cast<NSButton*>(view); | 
| + NSButtonCell* buttonCell = [button cell]; | 
| + // Decide it's a checkbox via showsStateBy and highlightsBy. | 
| + if (([buttonCell showsStateBy] == NSCellState) && | 
| + ([buttonCell highlightsBy] == NSCellState)) { | 
| + [GTMUILocalizerAndLayoutTweaker wrapButtonTitleForWidth:button]; | 
| + return [GTMUILocalizerAndLayoutTweaker sizeToFitView:view]; | 
| + } | 
| + } | 
| + return [GTMUILocalizerAndLayoutTweaker sizeToFitView:view]; | 
| +} | 
| + | 
| +// The different behaviors for the "pref group" auto sizing. | 
| +enum AutoSizeGroupBehavior { | 
| + kAutoSizeGroupBehaviorVerticalToFit, | 
| + kAutoSizeGroupBehaviorVerticalFirstToFit, | 
| + kAutoSizeGroupBehaviorHorizontalToFit, | 
| + kAutoSizeGroupBehaviorHorizontalFirstGrows | 
| +}; | 
| + | 
| +// Helper to tweak the layout of the "pref groups" and also ripple any height | 
| +// changes from one group to the next groups' origins. | 
| +// |views| is an ordered list of views with first being the label for the | 
| +// group and the rest being top down or left to right ordering of the views. | 
| +// The label is assumed to already be the same height as all the views it is | 
| +// next too. | 
| +CGFloat AutoSizeGroup(NSArray* views, AutoSizeGroupBehavior behavior, | 
| + CGFloat verticalShift) { | 
| + DCHECK_GE([views count], 2U) << "Should be at least a label and a control"; | 
| + NSTextField* label = [views objectAtIndex:0]; | 
| + | 
| +#ifndef NDEBUG | 
| + // Validate the helpers assumptions in debug builds. | 
| + DCHECK([label isKindOfClass:[NSTextField class]]) | 
| + << "First view should be the label for the group"; | 
| + NSRect initialLabelRect = [label frame]; | 
| + for (NSView* view in views) { | 
| + if (view != label) { | 
| + DCHECK_GE(NSMaxY(initialLabelRect), NSMaxY([view frame])) | 
| + << "Got a view that goes above the group label"; | 
| + DCHECK_LE(NSMinY(initialLabelRect), NSMinY([view frame])) | 
| + << "Got a view that goes below the group label"; | 
| + } | 
| + } | 
| +#endif // DEBUG | 
| 
 
Mark Mentovai
2009/10/30 22:19:27
NDEBUG
 
 | 
| + | 
| + // Auto size the label to see if we need more vertical space for its localized | 
| + // string. | 
| + CGFloat labelHeightChange = | 
| + [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:label]; | 
| + | 
| + CGFloat localVerticalShift = 0.0; | 
| + switch (behavior) { | 
| + case kAutoSizeGroupBehaviorVerticalToFit: { | 
| + // Walk bottom up doing the sizing and moves. | 
| + for (NSUInteger idx = [views count] - 1; idx > 0; --idx) { | 
| + NSView* view = [views objectAtIndex:idx]; | 
| + NSSize delta = WrapOrSizeToFit(view); | 
| + DCHECK_GE(delta.height, 0.0) << "Should NOT shrink in height"; | 
| + if (localVerticalShift) { | 
| + NSPoint origin = [view frame].origin; | 
| + origin.y += localVerticalShift; | 
| + [view setFrameOrigin:origin]; | 
| + } | 
| + localVerticalShift += delta.height; | 
| + } | 
| + break; | 
| + } | 
| + case kAutoSizeGroupBehaviorVerticalFirstToFit: { | 
| + // Just size the top one. | 
| + NSView* view = [views objectAtIndex:1]; | 
| + NSSize delta = WrapOrSizeToFit(view); | 
| + DCHECK_GE(delta.height, 0.0) << "Should NOT shrink in height"; | 
| + localVerticalShift += delta.height; | 
| + break; | 
| + } | 
| + case kAutoSizeGroupBehaviorHorizontalToFit: { | 
| + // Walk left to right doing the sizing and moves. | 
| + // NOTE: Don't worry about vertical, assume it always fits. | 
| + CGFloat horizontalShift = 0.0; | 
| + for (NSUInteger idx = 1; idx < [views count]; ++idx) { | 
| + NSView* view = [views objectAtIndex:idx]; | 
| + NSSize delta = WrapOrSizeToFit(view); | 
| + DCHECK_GE(delta.height, 0.0) << "Should NOT shrink in height"; | 
| + if (horizontalShift) { | 
| + NSPoint origin = [view frame].origin; | 
| + origin.x += horizontalShift; | 
| + [view setFrameOrigin:origin]; | 
| + } | 
| + horizontalShift += delta.width; | 
| + } | 
| + break; | 
| + } | 
| + case kAutoSizeGroupBehaviorHorizontalFirstGrows: { | 
| + // Walk right to left doing the sizing and moves, then apply the space | 
| + // collected into the first. | 
| + // NOTE: Don't worry about vertical, assume it always all fits. | 
| + CGFloat horizontalShift = 0.0; | 
| + for (NSUInteger idx = [views count] - 1; idx > 1; --idx) { | 
| + NSView* view = [views objectAtIndex:idx]; | 
| + NSSize delta = WrapOrSizeToFit(view); | 
| + DCHECK_GE(delta.height, 0.0) << "Should NOT shrink in height"; | 
| + horizontalShift -= delta.width; | 
| + NSPoint origin = [view frame].origin; | 
| + origin.x += horizontalShift; | 
| + [view setFrameOrigin:origin]; | 
| + } | 
| + if (horizontalShift) { | 
| + NSView* view = [views objectAtIndex:1]; | 
| + NSSize delta = NSMakeSize(horizontalShift, 0.0); | 
| + [GTMUILocalizerAndLayoutTweaker | 
| + resizeViewWithoutAutoResizingSubViews:view | 
| + delta:delta]; | 
| + } | 
| + break; | 
| + } | 
| + default: | 
| + NOTREACHED(); | 
| + break; | 
| + } | 
| + | 
| + // If the label grew more then the views, the other views get an extra shift. | 
| + // Otherwise, move the label to its top is aligned with the other views. | 
| + CGFloat nonLabelShift = 0.0; | 
| + if (labelHeightChange > localVerticalShift) { | 
| + // Since the lable is taller, centering the other views looks best, just | 
| + // shift the views by 1/2 of the size difference. | 
| + nonLabelShift = (labelHeightChange - localVerticalShift) / 2.0; | 
| + } else { | 
| + NSPoint origin = [label frame].origin; | 
| + origin.y += localVerticalShift - labelHeightChange; | 
| + [label setFrameOrigin:origin]; | 
| + } | 
| + | 
| + // Apply the input shift requested along with any the shift from label being | 
| + // taller then the rest of the group. | 
| + for (NSView* view in views) { | 
| + NSPoint origin = [view frame].origin; | 
| + origin.y += verticalShift; | 
| + if (view != label) { | 
| + origin.y += nonLabelShift; | 
| + } | 
| + [view setFrameOrigin:origin]; | 
| + } | 
| + | 
| + // Return how much the group grew. | 
| + return localVerticalShift + nonLabelShift; | 
| +} | 
| + | 
| } // namespace | 
| //------------------------------------------------------------------------- | 
| @@ -178,9 +347,48 @@ | 
| } | 
| - (void)awakeFromNib { | 
| - NSRect underTheHoodFrame = [underTheHoodView_ frame]; | 
| + // Do runtime fixup of the "basics" and "personal stuff" pages for the | 
| + // strings. Work bottom up shifting views up as needed, and then resize the | 
| + // page. | 
| + CGFloat verticalShift = 0.0; | 
| + verticalShift += AutoSizeGroup(basicsGroupDefaultBrowser_, | 
| + kAutoSizeGroupBehaviorVerticalFirstToFit, | 
| + verticalShift); | 
| + verticalShift += AutoSizeGroup(basicsGroupSearchEngine_, | 
| + kAutoSizeGroupBehaviorHorizontalFirstGrows, | 
| + verticalShift); | 
| + verticalShift += AutoSizeGroup(basicsGroupToolbar_, | 
| + kAutoSizeGroupBehaviorVerticalToFit, | 
| + verticalShift); | 
| + verticalShift += AutoSizeGroup(basicsGroupHomePage_, | 
| + kAutoSizeGroupBehaviorVerticalToFit, | 
| + verticalShift); | 
| + verticalShift += AutoSizeGroup(basicsGroupStartup_, | 
| + kAutoSizeGroupBehaviorVerticalFirstToFit, | 
| + verticalShift); | 
| + [GTMUILocalizerAndLayoutTweaker | 
| + resizeViewWithoutAutoResizingSubViews:basicsView_ | 
| + delta:NSMakeSize(0.0, verticalShift)]; | 
| + verticalShift = 0.0; | 
| 
 
Mark Mentovai
2009/10/30 22:19:27
nit: blank line before doing the second pane.
 
 | 
| + verticalShift += AutoSizeGroup(personalStuffGroupThemes_, | 
| + kAutoSizeGroupBehaviorHorizontalToFit, | 
| + verticalShift); | 
| + verticalShift += AutoSizeGroup(personalStuffGroupBrowserData_, | 
| + kAutoSizeGroupBehaviorVerticalToFit, | 
| + verticalShift); | 
| + verticalShift += AutoSizeGroup(personalStuffGroupAutofill_, | 
| + kAutoSizeGroupBehaviorVerticalToFit, | 
| + verticalShift); | 
| + verticalShift += AutoSizeGroup(personalStuffGroupPasswords_, | 
| + kAutoSizeGroupBehaviorVerticalFirstToFit, | 
| + verticalShift); | 
| + [GTMUILocalizerAndLayoutTweaker | 
| + resizeViewWithoutAutoResizingSubViews:personalStuffView_ | 
| + delta:NSMakeSize(0.0, verticalShift)]; | 
| + | 
| // Make sure the window is wide enough to fit the the widest view | 
| + NSRect underTheHoodFrame = [underTheHoodView_ frame]; | 
| CGFloat widest = std::max(NSWidth([basicsView_ frame]), | 
| NSWidth([personalStuffView_ frame])); | 
| widest = std::max(widest, NSWidth(underTheHoodFrame)); | 
| @@ -198,19 +406,18 @@ | 
| advancedContentSize.width = [advancedScroller_ contentSize].width; | 
| [advancedView_ setFrameSize:advancedContentSize]; | 
| - // Put the advanced view into the scroller and scroll it to the top. | 
| - [advancedScroller_ setDocumentView:advancedView_]; | 
| - [advancedView_ scrollPoint:NSMakePoint(0, advancedContentSize.height)]; | 
| - | 
| // Adjust the view origins so they show up centered. | 
| CenterViewForWidth(basicsView_, widest); | 
| CenterViewForWidth(personalStuffView_, widest); | 
| CenterViewForWidth(underTheHoodView_, widest); | 
| - // Ensure the "basics" is selected. | 
| + // Put the advanced view into the scroller and scroll it to the top. | 
| + [advancedScroller_ setDocumentView:advancedView_]; | 
| + [advancedView_ scrollPoint:NSMakePoint(0, advancedContentSize.height)]; | 
| + | 
| + // Ensure the "basics" page is selected. | 
| // TODO: change this to remember what's selected in a preference and restore | 
| // it. | 
| - | 
| NSToolbarItem* firstItem = [[toolbar_ items] objectAtIndex:0]; | 
| [self displayPreferenceViewForToolbarItem:firstItem animate:NO]; | 
| [toolbar_ setSelectedItemIdentifier:[firstItem itemIdentifier]]; |