Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(59)

Side by Side Diff: chrome/browser/cocoa/extensions/browser_actions_controller.mm

Issue 811002: Merge 41114 - [Mac] More progress towards resizing the Browser Actions contai... (Closed) Base URL: svn://svn.chromium.org/chrome/branches/342/src/
Patch Set: Created 10 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "browser_actions_controller.h" 5 #import "browser_actions_controller.h"
6 6
7 #include <string> 7 #include <string>
8 8
9 #include "base/sys_string_conversions.h" 9 #include "base/sys_string_conversions.h"
10 #include "chrome/browser/browser.h" 10 #include "chrome/browser/browser.h"
11 #include "chrome/browser/cocoa/extensions/browser_action_button.h" 11 #include "chrome/browser/pref_service.h"
12 #include "chrome/browser/cocoa/extensions/browser_actions_container_view.h" 12 #import "chrome/browser/cocoa/extensions/browser_action_button.h"
13 #include "chrome/browser/cocoa/extensions/extension_popup_controller.h" 13 #import "chrome/browser/cocoa/extensions/browser_actions_container_view.h"
14 #import "chrome/browser/cocoa/extensions/extension_popup_controller.h"
14 #include "chrome/browser/extensions/extension_browser_event_router.h" 15 #include "chrome/browser/extensions/extension_browser_event_router.h"
15 #include "chrome/browser/extensions/extension_toolbar_model.h" 16 #include "chrome/browser/extensions/extension_toolbar_model.h"
16 #include "chrome/browser/extensions/extensions_service.h" 17 #include "chrome/browser/extensions/extensions_service.h"
17 #include "chrome/browser/profile.h" 18 #include "chrome/browser/profile.h"
18 #include "chrome/browser/tab_contents/tab_contents.h" 19 #include "chrome/browser/tab_contents/tab_contents.h"
19 #include "chrome/common/notification_observer.h" 20 #include "chrome/common/notification_observer.h"
20 #include "chrome/common/notification_registrar.h" 21 #include "chrome/common/notification_registrar.h"
22 #include "chrome/common/pref_names.h"
21 23
22 // The padding between browser action buttons.
23 extern const CGFloat kBrowserActionButtonPadding = 3; 24 extern const CGFloat kBrowserActionButtonPadding = 3;
24 25
26 extern const NSString* kBrowserActionVisibilityChangedNotification =
27 @"BrowserActionVisibilityChangedNotification";
28
25 namespace { 29 namespace {
30 const CGFloat kAnimationDuration = 0.2;
26 const CGFloat kContainerPadding = 2.0; 31 const CGFloat kContainerPadding = 2.0;
27 const CGFloat kGrippyXOffset = 8.0; 32 const CGFloat kGrippyXOffset = 8.0;
33 const CGFloat kButtonOpacityLeadPadding = 5.0;
28 } // namespace 34 } // namespace
29 35
30 NSString* const kBrowserActionsChangedNotification = @"BrowserActionsChanged";
31
32 @interface BrowserActionsController(Private) 36 @interface BrowserActionsController(Private)
37 - (void)createButtons;
33 - (void)createActionButtonForExtension:(Extension*)extension 38 - (void)createActionButtonForExtension:(Extension*)extension
34 withIndex:(int)index; 39 withIndex:(NSUInteger)index;
35 - (void)removeActionButtonForExtension:(Extension*)extension; 40 - (void)removeActionButtonForExtension:(Extension*)extension;
41 - (CGFloat)containerWidthWithButtonCount:(NSUInteger)buttonCount;
36 - (void)repositionActionButtons; 42 - (void)repositionActionButtons;
43 - (void)updateButtonOpacityAndDragAbilities;
44 - (void)containerFrameChanged;
45 - (void)containerDragging;
46 - (void)containerDragFinished;
37 - (int)currentTabId; 47 - (int)currentTabId;
38 - (bool)shouldDisplayBrowserAction:(Extension*)extension; 48 - (bool)shouldDisplayBrowserAction:(Extension*)extension;
39 @end 49 @end
40 50
41 // A helper class to proxy extension notifications to the view controller's 51 // A helper class to proxy extension notifications to the view controller's
42 // appropriate methods. 52 // appropriate methods.
43 class ExtensionsServiceObserverBridge : public NotificationObserver, 53 class ExtensionsServiceObserverBridge : public NotificationObserver,
44 public ExtensionToolbarModel::Observer { 54 public ExtensionToolbarModel::Observer {
45 public: 55 public:
46 ExtensionsServiceObserverBridge(BrowserActionsController* owner, 56 ExtensionsServiceObserverBridge(BrowserActionsController* owner,
(...skipping 15 matching lines...) Expand all
62 break; 72 break;
63 } 73 }
64 default: 74 default:
65 NOTREACHED() << L"Unexpected notification"; 75 NOTREACHED() << L"Unexpected notification";
66 } 76 }
67 } 77 }
68 78
69 // ExtensionToolbarModel::Observer implementation. 79 // ExtensionToolbarModel::Observer implementation.
70 void BrowserActionAdded(Extension* extension, int index) { 80 void BrowserActionAdded(Extension* extension, int index) {
71 [owner_ createActionButtonForExtension:extension withIndex:index]; 81 [owner_ createActionButtonForExtension:extension withIndex:index];
82 [owner_ resizeContainerWithAnimation:NO];
72 } 83 }
73 84
74 void BrowserActionRemoved(Extension* extension) { 85 void BrowserActionRemoved(Extension* extension) {
75 [owner_ removeActionButtonForExtension:extension]; 86 [owner_ removeActionButtonForExtension:extension];
87 [owner_ resizeContainerWithAnimation:NO];
76 } 88 }
77 89
78 private: 90 private:
79 // The object we need to inform when we get a notification. Weak. Owns us. 91 // The object we need to inform when we get a notification. Weak. Owns us.
80 BrowserActionsController* owner_; 92 BrowserActionsController* owner_;
81 93
82 // Used for registering to receive notifications and automatic clean up. 94 // Used for registering to receive notifications and automatic clean up.
83 NotificationRegistrar registrar_; 95 NotificationRegistrar registrar_;
84 96
85 DISALLOW_COPY_AND_ASSIGN(ExtensionsServiceObserverBridge); 97 DISALLOW_COPY_AND_ASSIGN(ExtensionsServiceObserverBridge);
86 }; 98 };
87 99
88 @implementation BrowserActionsController 100 @implementation BrowserActionsController
89 101
90 @synthesize containerView = containerView_; 102 @synthesize containerView = containerView_;
91 103
92 - (id)initWithBrowser:(Browser*)browser 104 - (id)initWithBrowser:(Browser*)browser
93 containerView:(BrowserActionsContainerView*)container { 105 containerView:(BrowserActionsContainerView*)container {
94 DCHECK(browser && container); 106 DCHECK(browser && container);
95 107
96 if ((self = [super init])) { 108 if ((self = [super init])) {
97 browser_ = browser; 109 browser_ = browser;
98 profile_ = browser->profile(); 110 profile_ = browser->profile();
99 111
112 if (!profile_->GetPrefs()->FindPreference(
113 prefs::kBrowserActionContainerWidth))
114 [BrowserActionsController registerUserPrefs:profile_->GetPrefs()];
115
100 observer_.reset(new ExtensionsServiceObserverBridge(self, profile_)); 116 observer_.reset(new ExtensionsServiceObserverBridge(self, profile_));
101 ExtensionsService* extensionsService = profile_->GetExtensionsService(); 117 ExtensionsService* extensionsService = profile_->GetExtensionsService();
102 // |extensionsService| can be NULL in Incognito. 118 // |extensionsService| can be NULL in Incognito.
103 if (extensionsService) { 119 if (extensionsService) {
104 toolbarModel_ = extensionsService->toolbar_model(); 120 toolbarModel_ = extensionsService->toolbar_model();
105 toolbarModel_->AddObserver(observer_.get()); 121 toolbarModel_->AddObserver(observer_.get());
106 } 122 }
107 123
108 containerView_ = container; 124 containerView_ = container;
109 [containerView_ setHidden:YES]; 125 [containerView_ setHidden:YES];
126 [containerView_ setCanDragLeft:YES];
127 [containerView_ setCanDragRight:YES];
128 [containerView_ setPostsFrameChangedNotifications:YES];
129 [[NSNotificationCenter defaultCenter]
130 addObserver:self
131 selector:@selector(containerFrameChanged)
132 name:NSViewFrameDidChangeNotification
133 object:containerView_];
134 [[NSNotificationCenter defaultCenter]
135 addObserver:self
136 selector:@selector(containerDragging)
137 name:kBrowserActionGrippyDraggingNotification
138 object:containerView_];
139 [[NSNotificationCenter defaultCenter]
140 addObserver:self
141 selector:@selector(containerDragFinished)
142 name:kBrowserActionGrippyDragFinishedNotification
143 object:containerView_];
110 144
111 buttons_.reset([[NSMutableDictionary alloc] init]); 145 buttons_.reset([[NSMutableDictionary alloc] init]);
112 buttonOrder_.reset([[NSMutableArray alloc] init]); 146 [self createButtons];
113 } 147 }
114 148
115 return self; 149 return self;
116 } 150 }
117 151
118 - (void)dealloc { 152 - (void)dealloc {
119 if (toolbarModel_) 153 if (toolbarModel_)
120 toolbarModel_->RemoveObserver(observer_.get()); 154 toolbarModel_->RemoveObserver(observer_.get());
121 155
122 [[NSNotificationCenter defaultCenter] removeObserver:self]; 156 [[NSNotificationCenter defaultCenter] removeObserver:self];
123 [super dealloc]; 157 [super dealloc];
124 } 158 }
125 159
126 - (void)update { 160 - (void)update {
127 for (BrowserActionButton* button in [buttons_ allValues]) { 161 for (BrowserActionButton* button in [buttons_ allValues]) {
128 [button setTabId:[self currentTabId]]; 162 [button setTabId:[self currentTabId]];
129 [button updateState]; 163 [button updateState];
130 } 164 }
131 } 165 }
132 166
133 - (void)createButtons { 167 - (void)createButtons {
134 // No extensions in incognito mode. 168 // No extensions in incognito mode.
135 if (!toolbarModel_) 169 if (!toolbarModel_)
136 return; 170 return;
137 171
138 int i = 0; 172 NSUInteger i = 0;
139 for (ExtensionList::iterator iter = toolbarModel_->begin(); 173 for (ExtensionList::iterator iter = toolbarModel_->begin();
140 iter != toolbarModel_->end(); ++iter) { 174 iter != toolbarModel_->end(); ++iter) {
141 [self createActionButtonForExtension:*iter withIndex:i++]; 175 [self createActionButtonForExtension:*iter withIndex:i++];
142 } 176 }
143 }
144 177
145 - (CGFloat)idealContainerWidth { 178 CGFloat width = [self savedWidth];
146 NSUInteger buttonCount = [self visibleButtonCount]; 179 // The width will never be 0 (due to the container's minimum size restriction)
147 if (buttonCount == 0) 180 // except when no width has been saved. In this case, set the width to be as
148 return 0.0; 181 // if all buttons are shown.
182 if (width == 0)
183 width = [self containerWidthWithButtonCount:[self buttonCount]];
149 184
150 return kGrippyXOffset + kContainerPadding + (buttonCount * 185 [containerView_ resizeToWidth:width animate:NO];
151 (kBrowserActionWidth + kBrowserActionButtonPadding));
152 } 186 }
153 187
154 - (void)createActionButtonForExtension:(Extension*)extension 188 - (void)createActionButtonForExtension:(Extension*)extension
155 withIndex:(int)index { 189 withIndex:(NSUInteger)index {
156 if (!extension->browser_action()) 190 if (!extension->browser_action())
157 return; 191 return;
158 192
159 if (![self shouldDisplayBrowserAction:extension]) 193 if (![self shouldDisplayBrowserAction:extension])
160 return; 194 return;
161 195
162 // Show the container if it's the first button. Otherwise it will be shown 196 // Show the container if it's the first button. Otherwise it will be shown
163 // already. 197 // already.
164 if ([buttons_ count] == 0) 198 if ([buttons_ count] == 0)
165 [containerView_ setHidden:NO]; 199 [containerView_ setHidden:NO];
166 200
167 BrowserActionButton* newButton = [[[BrowserActionButton alloc] 201 BrowserActionButton* newButton = [[[BrowserActionButton alloc]
168 initWithExtension:extension 202 initWithExtension:extension
169 profile:profile_ 203 profile:profile_
170 tabId:[self currentTabId]] autorelease]; 204 tabId:[self currentTabId]] autorelease];
171 [newButton setTarget:self]; 205 [newButton setTarget:self];
172 [newButton setAction:@selector(browserActionClicked:)]; 206 [newButton setAction:@selector(browserActionClicked:)];
173 NSString* buttonKey = base::SysUTF8ToNSString(extension->id()); 207 NSString* buttonKey = base::SysUTF8ToNSString(extension->id());
208 if (!buttonKey)
209 return;
174 [buttons_ setObject:newButton forKey:buttonKey]; 210 [buttons_ setObject:newButton forKey:buttonKey];
175 [buttonOrder_ insertObject:newButton atIndex:index];
176 [containerView_ addSubview:newButton]; 211 [containerView_ addSubview:newButton];
212 if (index >= [self visibleButtonCount])
213 [newButton setAlphaValue:0.0];
214
177 [self repositionActionButtons]; 215 [self repositionActionButtons];
178
179 [[NSNotificationCenter defaultCenter]
180 postNotificationName:kBrowserActionsChangedNotification object:self];
181 [containerView_ setNeedsDisplay:YES]; 216 [containerView_ setNeedsDisplay:YES];
182 } 217 }
183 218
184 - (void)removeActionButtonForExtension:(Extension*)extension { 219 - (void)removeActionButtonForExtension:(Extension*)extension {
185 if (!extension->browser_action()) 220 if (!extension->browser_action())
186 return; 221 return;
187 222
188 NSString* buttonKey = base::SysUTF8ToNSString(extension->id()); 223 NSString* buttonKey = base::SysUTF8ToNSString(extension->id());
224 if (!buttonKey)
225 return;
189 226
190 BrowserActionButton* button = [buttons_ objectForKey:buttonKey]; 227 BrowserActionButton* button = [buttons_ objectForKey:buttonKey];
191 if (!button) { 228 if (!button) {
192 NOTREACHED(); 229 NOTREACHED();
193 return; 230 return;
194 } 231 }
195 [button removeFromSuperview]; 232 [button removeFromSuperview];
196 [buttons_ removeObjectForKey:buttonKey]; 233 [buttons_ removeObjectForKey:buttonKey];
197 [buttonOrder_ removeObject:button];
198 if ([buttons_ count] == 0) { 234 if ([buttons_ count] == 0) {
199 // No more buttons? Hide the container. 235 // No more buttons? Hide the container.
200 [containerView_ setHidden:YES]; 236 [containerView_ setHidden:YES];
201 } else { 237 } else {
202 [self repositionActionButtons]; 238 [self repositionActionButtons];
203 } 239 }
204 [[NSNotificationCenter defaultCenter]
205 postNotificationName:kBrowserActionsChangedNotification object:self];
206 [containerView_ setNeedsDisplay:YES]; 240 [containerView_ setNeedsDisplay:YES];
207 } 241 }
208 242
209 - (void)repositionActionButtons { 243 - (void)repositionActionButtons {
210 for (NSUInteger i = 0; i < [buttonOrder_ count]; ++i) { 244 NSUInteger i = 0;
245 for (ExtensionList::iterator iter = toolbarModel_->begin();
246 iter != toolbarModel_->end(); ++iter) {
211 CGFloat xOffset = kGrippyXOffset + 247 CGFloat xOffset = kGrippyXOffset +
212 (i * (kBrowserActionWidth + kBrowserActionButtonPadding)); 248 (i * (kBrowserActionWidth + kBrowserActionButtonPadding));
213 BrowserActionButton* button = [buttonOrder_ objectAtIndex:i]; 249 NSString* extensionId = base::SysUTF8ToNSString((*iter)->id());
250 DCHECK(extensionId);
251 if (!extensionId)
252 continue;
253 BrowserActionButton* button = [buttons_ objectForKey:extensionId];
214 NSRect buttonFrame = [button frame]; 254 NSRect buttonFrame = [button frame];
215 buttonFrame.origin.x = xOffset; 255 buttonFrame.origin.x = xOffset;
216 [button setFrame:buttonFrame]; 256 [button setFrame:buttonFrame];
257 ++i;
217 } 258 }
218 } 259 }
219 260
261 - (CGFloat)containerWidthWithButtonCount:(NSUInteger)buttonCount {
262 CGFloat width = 0.0;
263 if (buttonCount > 0) {
264 width = kGrippyXOffset + kContainerPadding +
265 (buttonCount * (kBrowserActionWidth + kBrowserActionButtonPadding));
266 }
267 return width;
268 }
269
270 // Resizes the container given the number of visible buttons in the container,
271 // taking into account the size of the grippy. Also updates the persistent
272 // width preference.
273 - (void)resizeContainerWithAnimation:(BOOL)animate {
274 CGFloat width =
275 [self containerWidthWithButtonCount:[self visibleButtonCount]];
276 [containerView_ resizeToWidth:width animate:animate];
277 profile_->GetPrefs()->SetReal(prefs::kBrowserActionContainerWidth,
278 NSWidth([containerView_ frame]));
279
280 [[NSNotificationCenter defaultCenter]
281 postNotificationName:kBrowserActionVisibilityChangedNotification
282 object:self];
283 }
284
285 - (void)updateButtonOpacityAndDragAbilities {
286 for (BrowserActionButton* button in [buttons_ allValues]) {
287 NSRect buttonFrame = [button frame];
288 buttonFrame.origin.x += kButtonOpacityLeadPadding;
289 if (NSContainsRect([containerView_ bounds], buttonFrame)) {
290 if ([button alphaValue] != 1.0)
291 [button setAlphaValue:1.0];
292
293 continue;
294 }
295 CGFloat intersectionWidth =
296 NSWidth(NSIntersectionRect([containerView_ bounds], buttonFrame));
297 CGFloat alpha = std::max(0.0f,
298 (intersectionWidth - kButtonOpacityLeadPadding) / NSWidth(buttonFrame));
299 [button setAlphaValue:alpha];
300 [button setNeedsDisplay:YES];
301 }
302
303 // Updates the drag direction constraint variables based on relevant metrics.
304 [containerView_ setCanDragLeft:
305 ([self visibleButtonCount] != [self buttonCount])];
306 [[containerView_ window] invalidateCursorRectsForView:containerView_];
307 }
308
309 - (void)containerFrameChanged {
310 [self updateButtonOpacityAndDragAbilities];
311 }
312
313 - (void)containerDragging {
314 [[NSNotificationCenter defaultCenter]
315 postNotificationName:kBrowserActionGrippyDraggingNotification
316 object:self];
317 }
318
319 // Handles when a user initiated drag to resize the container has finished.
320 - (void)containerDragFinished {
321 for (BrowserActionButton* button in [buttons_ allValues]) {
322 NSRect buttonFrame = [button frame];
323 if (NSContainsRect([containerView_ bounds], buttonFrame))
324 continue;
325
326 CGFloat intersectionWidth =
327 NSWidth(NSIntersectionRect([containerView_ bounds], buttonFrame));
328 if (([containerView_ grippyPinned] && intersectionWidth > 0) ||
329 (intersectionWidth <= (NSWidth(buttonFrame) / 2)))
330 [button setAlphaValue:0.0];
331 }
332
333 [self resizeContainerWithAnimation:NO];
334 }
335
220 - (NSUInteger)buttonCount { 336 - (NSUInteger)buttonCount {
221 return [buttons_ count]; 337 return [buttons_ count];
222 } 338 }
223 339
224 - (NSUInteger)visibleButtonCount { 340 - (NSUInteger)visibleButtonCount {
225 int count = 0; 341 int count = 0;
226 for (BrowserActionButton* button in [buttons_ allValues]) { 342 for (BrowserActionButton* button in [buttons_ allValues]) {
227 if (![button isHidden]) 343 if ([button alphaValue] > 0.0)
228 ++count; 344 ++count;
229 } 345 }
230 return count; 346 return count;
231 } 347 }
232 348
233 - (void)browserActionClicked:(BrowserActionButton*)sender { 349 - (void)browserActionClicked:(BrowserActionButton*)sender {
234 int tabId = [self currentTabId]; 350 int tabId = [self currentTabId];
235 if (tabId < 0) { 351 if (tabId < 0) {
236 NOTREACHED() << "No current tab."; 352 NOTREACHED() << "No current tab.";
237 return; 353 return;
(...skipping 30 matching lines...) Expand all
268 384
269 - (int)currentTabId { 385 - (int)currentTabId {
270 TabContents* selected_tab = browser_->GetSelectedTabContents(); 386 TabContents* selected_tab = browser_->GetSelectedTabContents();
271 if (!selected_tab) 387 if (!selected_tab)
272 return -1; 388 return -1;
273 389
274 return selected_tab->controller().session_id().id(); 390 return selected_tab->controller().session_id().id();
275 } 391 }
276 392
277 - (NSView*)browserActionViewForExtension:(Extension*)extension { 393 - (NSView*)browserActionViewForExtension:(Extension*)extension {
278 for (BrowserActionButton* button in buttonOrder_.get()) { 394 for (BrowserActionButton* button in [buttons_ allValues]) {
279 if ([button extension] == extension) 395 if ([button extension] == extension)
280 return button; 396 return button;
281 } 397 }
282 NOTREACHED(); 398 NOTREACHED();
283 return nil; 399 return nil;
284 } 400 }
285 401
286 - (NSButton*)buttonWithIndex:(int)index { 402 - (NSButton*)buttonWithIndex:(NSUInteger)index {
287 return [buttonOrder_ objectAtIndex:(NSUInteger)index]; 403 NSUInteger i = 0;
404 for (ExtensionList::iterator iter = toolbarModel_->begin();
405 iter != toolbarModel_->end(); ++iter) {
406 if (i == index)
407 return [buttons_ objectForKey:base::SysUTF8ToNSString((*iter)->id())];
408
409 ++i;
410 }
411 return nil;
288 } 412 }
289 413
290 - (bool)shouldDisplayBrowserAction:(Extension*)extension { 414 - (bool)shouldDisplayBrowserAction:(Extension*)extension {
291 return (!profile_->IsOffTheRecord() || 415 return (!profile_->IsOffTheRecord() ||
292 profile_->GetExtensionsService()-> 416 profile_->GetExtensionsService()->
293 IsIncognitoEnabled(extension->id())); 417 IsIncognitoEnabled(extension->id()));
294 } 418 }
295 419
420 - (CGFloat)savedWidth {
421 return profile_->GetPrefs()->GetReal(prefs::kBrowserActionContainerWidth);
422 }
423
424 + (void)registerUserPrefs:(PrefService*)prefs {
425 prefs->RegisterRealPref(prefs::kBrowserActionContainerWidth, 0);
426 }
427
296 @end 428 @end
OLDNEW
« no previous file with comments | « chrome/browser/cocoa/extensions/browser_actions_controller.h ('k') | chrome/browser/cocoa/toolbar_controller.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698