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]]; |