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

Side by Side Diff: chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.mm

Issue 2263863002: Remove implementation of Panels on OSes other than ChromeOS. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: CR feedback Created 4 years, 4 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
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.h"
6
7 #import <Cocoa/Cocoa.h>
8
9 #include "base/auto_reset.h"
10 #include "base/logging.h"
11 #include "base/mac/bundle_locations.h"
12 #include "base/mac/foundation_util.h"
13 #include "base/mac/mac_util.h"
14 #include "base/mac/scoped_nsautorelease_pool.h"
15 #include "base/strings/sys_string_conversions.h"
16 #include "chrome/app/chrome_command_ids.h" // IDC_*
17 #import "chrome/browser/app_controller_mac.h"
18 #include "chrome/browser/chrome_browser_application_mac.h"
19 #include "chrome/browser/profiles/profile.h"
20 #import "chrome/browser/ui/cocoa/browser_window_utils.h"
21 #import "chrome/browser/ui/cocoa/panels/mouse_drag_controller.h"
22 #import "chrome/browser/ui/cocoa/panels/panel_cocoa.h"
23 #import "chrome/browser/ui/cocoa/panels/panel_titlebar_view_cocoa.h"
24 #import "chrome/browser/ui/cocoa/panels/panel_utils_cocoa.h"
25 #import "chrome/browser/ui/cocoa/sprite_view.h"
26 #import "chrome/browser/ui/cocoa/tab_contents/favicon_util_mac.h"
27 #import "chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.h"
28 #include "chrome/browser/ui/panels/panel_bounds_animation.h"
29 #include "chrome/browser/ui/panels/panel_collection.h"
30 #include "chrome/browser/ui/panels/panel_constants.h"
31 #include "chrome/browser/ui/panels/panel_manager.h"
32 #include "chrome/browser/ui/panels/stacked_panel_collection.h"
33 #include "chrome/browser/ui/tabs/tab_strip_model.h"
34 #include "chrome/browser/ui/toolbar/encoding_menu_controller.h"
35 #include "content/public/browser/render_widget_host_view.h"
36 #include "content/public/browser/web_contents.h"
37 #include "ui/base/resource/resource_bundle.h"
38 #include "ui/gfx/image/image.h"
39 #include "ui/gfx/mac/scoped_cocoa_disable_screen_updates.h"
40 #include "ui/resources/grit/ui_resources.h"
41
42 using content::WebContents;
43
44 const int kMinimumWindowSize = 1;
45 const double kBoundsAnimationSpeedPixelsPerSecond = 1000;
46 const double kBoundsAnimationMaxDurationSeconds = 0.18;
47
48 // Edge thickness to trigger user resizing via system, in screen pixels.
49 const double kWidthOfMouseResizeArea = 15.0;
50
51 // Notification observer to prevent panels becoming key when windows are closed.
52 @interface WindowCloseWatcher : NSObject
53 - (void)windowWillClose:(NSNotification*)notification;
54 @end
55
56 @interface PanelWindowControllerCocoa (PanelsCanBecomeKey)
57 // Internal helper method for extracting the total number of panel windows
58 // from the panel manager. Used to decide if panel can become the key window.
59 - (int)numPanels;
60 @end
61
62 @implementation PanelWindowCocoaImpl
63 // The panels cannot be reduced to 3-px windows on the edge of the screen
64 // active area (above Dock). Default constraining logic makes at least a height
65 // of the titlebar visible, so the user could still grab it. We do 'restore'
66 // differently, and minimize panels to 3 px. Hence the need to override the
67 // constraining logic.
68 - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen {
69 return frameRect;
70 }
71
72 // Prevent panel window from becoming key - for example when it is minimized.
73 // Panel windows use a higher priority NSWindowLevel to ensure they are always
74 // visible, causing the OS to prefer panel windows when selecting a window
75 // to make the key window. To counter this preference, we override
76 // -[NSWindow:canBecomeKeyWindow] to restrict when the panel can become the
77 // key window to a limited set of scenarios, such as when cycling through
78 // windows, when panels are the only remaining windows, when an event
79 // triggers window activation, etc. The panel may also be prevented from
80 // becoming the key window, regardless of the above scenarios, such as when
81 // a panel is minimized.
82 - (BOOL)canBecomeKeyWindow {
83 // Give precedence to controller preventing activation of the window.
84 PanelWindowControllerCocoa* controller =
85 base::mac::ObjCCast<PanelWindowControllerCocoa>([self windowController]);
86 if (![controller canBecomeKeyWindow])
87 return NO;
88
89 BrowserCrApplication* app = base::mac::ObjCCast<BrowserCrApplication>(
90 [BrowserCrApplication sharedApplication]);
91
92 // A Panel window can become the key window only in limited scenarios.
93 // This prevents the system from always preferring a Panel window due
94 // to its higher priority NSWindowLevel when selecting a window to make key.
95 return ([app isHandlingSendEvent] && [[app currentEvent] window] == self) ||
96 [controller activationRequestedByPanel] ||
97 [app isCyclingWindows] ||
98 [self isKeyWindow] ||
99 [[app windows] count] == static_cast<NSUInteger>([controller numPanels]);
100 }
101
102 - (void)performMiniaturize:(id)sender {
103 [[self windowController] minimizeButtonClicked:0];
104 }
105
106 - (void)mouseMoved:(NSEvent*)event {
107 // Cocoa does not support letting the application determine the edges that
108 // can trigger the user resizing. To work around this, we track the mouse
109 // location. When it is close to the edge/corner where the user resizing
110 // is not desired, we force the min and max size of the window to be same
111 // as current window size. For all other cases, we restore the min and max
112 // size.
113 PanelWindowControllerCocoa* controller =
114 base::mac::ObjCCast<PanelWindowControllerCocoa>([self windowController]);
115 NSRect frame = [self frame];
116 if ([controller canResizeByMouseAtCurrentLocation]) {
117 // Mac window server limits window sizes to 10000.
118 NSSize maxSize = NSMakeSize(10000, 10000);
119
120 // If the user is resizing a stacked panel by its bottom edge, make sure its
121 // height cannot grow more than what the panel below it could offer. This is
122 // because growing a stacked panel by y amount will shrink the panel below
123 // it by same amount and we do not want the panel below it being shrunk to
124 // be smaller than the titlebar.
125 Panel* panel = [controller panel];
126 NSPoint point = [NSEvent mouseLocation];
127 if (point.y < NSMinY(frame) + kWidthOfMouseResizeArea && panel->stack()) {
128 Panel* belowPanel = panel->stack()->GetPanelBelow(panel);
129 if (belowPanel && !belowPanel->IsMinimized()) {
130 maxSize.height = panel->GetBounds().height() +
131 belowPanel->GetBounds().height() - panel::kTitlebarHeight;
132 }
133 }
134
135 // Enable the user-resizing by setting both min and max size to the right
136 // values.
137 [self setMinSize:NSMakeSize(panel::kPanelMinWidth,
138 panel::kPanelMinHeight)];
139 [self setMaxSize:maxSize];
140 } else {
141 // Disable the user-resizing by setting both min and max size to be same as
142 // current window size.
143 [self setMinSize:frame.size];
144 [self setMaxSize:frame.size];
145 }
146
147 [super mouseMoved:event];
148 }
149 @end
150
151 @implementation PanelWindowControllerCocoa
152
153 - (id)initWithPanel:(PanelCocoa*)window {
154 NSString* nibpath =
155 [base::mac::FrameworkBundle() pathForResource:@"Panel" ofType:@"nib"];
156 if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
157 windowShim_.reset(window);
158 animateOnBoundsChange_ = YES;
159 canBecomeKeyWindow_ = YES;
160 activationRequestedByPanel_ = NO;
161
162 // Leaky singleton. Initialized when the first panel is created.
163 static WindowCloseWatcher* watcher = [[WindowCloseWatcher alloc] init];
164 (void)watcher; // Suppress the unused variable warning.
165 }
166 return self;
167 }
168
169 - (Panel*)panel {
170 return windowShim_->panel();
171 }
172
173 - (void)awakeFromNib {
174 NSWindow* window = [self window];
175
176 DCHECK(window);
177 DCHECK(titlebar_view_);
178 DCHECK_EQ(self, [window delegate]);
179
180 [self updateWindowLevel];
181
182 [self updateWindowCollectionBehavior];
183
184 [titlebar_view_ attach];
185
186 // Set initial size of the window to match the size of the panel to give
187 // the renderer the proper size to work with earlier, avoiding a resize
188 // after the window is revealed.
189 gfx::Rect panelBounds = windowShim_->panel()->GetBounds();
190 NSRect frame = [window frame];
191 frame.size.width = panelBounds.width();
192 frame.size.height = panelBounds.height();
193 [window setFrame:frame display:NO];
194
195 // MacOS will turn the user-resizing to the user-dragging if the direction of
196 // the dragging is orthogonal to the direction of the arrow cursor. We do not
197 // want this since it will bypass our dragging logic. The panel window is
198 // still draggable because we track and handle the dragging in our custom way.
199 [[self window] setMovable:NO];
200
201 [self updateTrackingArea];
202 }
203
204 - (void)updateWebContentsViewFrame {
205 content::WebContents* webContents = windowShim_->panel()->GetWebContents();
206 if (!webContents)
207 return;
208
209 // Compute the size of the web contents view. Don't assume it's similar to the
210 // size of the contentView, because the contentView is managed by the Cocoa
211 // to be (window - standard titlebar), while we have taller custom titlebar
212 // instead. In coordinate system of window's contentView.
213 NSRect contentFrame = [self contentRectForFrameRect:[[self window] frame]];
214 contentFrame.origin = NSZeroPoint;
215
216 NSView* contentView = webContents->GetNativeView();
217 if (!NSEqualRects([contentView frame], contentFrame))
218 [contentView setFrame:contentFrame];
219 }
220
221 - (void)disableWebContentsViewAutosizing {
222 [[[self window] contentView] setAutoresizesSubviews:NO];
223 }
224
225 - (void)enableWebContentsViewAutosizing {
226 [self updateWebContentsViewFrame];
227 [[[self window] contentView] setAutoresizesSubviews:YES];
228 }
229
230 - (void)revealAnimatedWithFrame:(const NSRect&)frame {
231 NSWindow* window = [self window]; // This ensures loading the nib.
232
233 // Disable subview resizing while resizing the window to avoid renderer
234 // resizes during intermediate stages of animation.
235 [self disableWebContentsViewAutosizing];
236
237 // We grow the window from the bottom up to produce a 'reveal' animation.
238 NSRect startFrame = NSMakeRect(NSMinX(frame), NSMinY(frame),
239 NSWidth(frame), kMinimumWindowSize);
240 [window setFrame:startFrame display:NO animate:NO];
241 // Shows the window without making it key, on top of its layer, even if
242 // Chromium is not an active app.
243 [window orderFrontRegardless];
244 // TODO(dcheng): Temporary hack to work around the fact that
245 // orderFrontRegardless causes us to become the first responder. The usual
246 // Chrome assumption is that becoming the first responder = you have focus, so
247 // we always deactivate the controls here. If we're created as an active
248 // panel, we'll get a NSWindowDidBecomeKeyNotification and reactivate the web
249 // view properly. See crbug.com/97831 for more details.
250 WebContents* web_contents = windowShim_->panel()->GetWebContents();
251 // RWHV may be NULL in unit tests.
252 if (web_contents && web_contents->GetRenderWidgetHostView())
253 web_contents->GetRenderWidgetHostView()->SetActive(false);
254
255 // This will re-enable the content resizing after it finishes.
256 [self setPanelFrame:frame animate:YES];
257 }
258
259 - (void)updateTitleBar {
260 NSString* newTitle = base::SysUTF16ToNSString(
261 windowShim_->panel()->GetWindowTitle());
262 pendingWindowTitle_.reset(
263 [BrowserWindowUtils scheduleReplaceOldTitle:pendingWindowTitle_.get()
264 withNewTitle:newTitle
265 forWindow:[self window]]);
266 [titlebar_view_ setTitle:newTitle];
267 [self updateIcon];
268 }
269
270 - (void)updateIcon {
271 base::scoped_nsobject<NSView> iconView;
272 if (throbberShouldSpin_) {
273 // If the throbber is spinning now, no need to replace it.
274 if ([[titlebar_view_ icon] isKindOfClass:[SpriteView class]])
275 return;
276
277 NSImage* iconImage =
278 ResourceBundle::GetSharedInstance().GetNativeImageNamed(
279 IDR_THROBBER).ToNSImage();
280 SpriteView* spriteView = [[SpriteView alloc] init];
281 [spriteView setImage:iconImage];
282 iconView.reset(spriteView);
283 } else {
284 const gfx::Image& page_icon = windowShim_->panel()->GetCurrentPageIcon();
285 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
286 NSRect iconFrame = [[titlebar_view_ icon] frame];
287 NSImage* iconImage = page_icon.IsEmpty() ?
288 rb.GetNativeImageNamed(IDR_DEFAULT_FAVICON).ToNSImage() :
289 page_icon.ToNSImage();
290 NSImageView* imageView = [[NSImageView alloc] initWithFrame:iconFrame];
291 [imageView setImage:iconImage];
292 iconView.reset(imageView);
293 }
294 [titlebar_view_ setIcon:iconView];
295 }
296
297 - (void)updateThrobber:(BOOL)shouldSpin {
298 if (throbberShouldSpin_ == shouldSpin)
299 return;
300 throbberShouldSpin_ = shouldSpin;
301
302 // If the titlebar view has not been attached, bail out.
303 if (!titlebar_view_)
304 return;
305
306 [self updateIcon];
307 }
308
309 - (void)updateTitleBarMinimizeRestoreButtonVisibility {
310 Panel* panel = windowShim_->panel();
311 [titlebar_view_ setMinimizeButtonVisibility:panel->CanShowMinimizeButton()];
312 [titlebar_view_ setRestoreButtonVisibility:panel->CanShowRestoreButton()];
313 }
314
315 - (void)webContentsInserted:(WebContents*)contents {
316 NSView* view = contents->GetNativeView();
317 [[[self window] contentView] addSubview:view];
318 [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
319
320 [self enableWebContentsViewAutosizing];
321 }
322
323 - (void)webContentsDetached:(WebContents*)contents {
324 [contents->GetNativeView() removeFromSuperview];
325 }
326
327 - (PanelTitlebarViewCocoa*)titlebarView {
328 return titlebar_view_;
329 }
330
331 // Called to validate menu and toolbar items when this window is key. All the
332 // items we care about have been set with the |-commandDispatch:|
333 // action and a target of FirstResponder in IB.
334 // Delegate to the NSApp delegate if Panel does not care about the command or
335 // shortcut, to make sure the global items in Chrome main app menu still work.
336 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
337 if ([item action] == @selector(commandDispatch:)) {
338 NSInteger tag = [item tag];
339 CommandUpdater* command_updater = windowShim_->panel()->command_updater();
340 if (command_updater->SupportsCommand(tag))
341 return command_updater->IsCommandEnabled(tag);
342
343 AppController* appController =
344 base::mac::ObjCCastStrict<AppController>([NSApp delegate]);
345 return [appController validateUserInterfaceItem:item];
346 }
347 return NO;
348 }
349
350 // Called when the user picks a menu or toolbar item when this window is key.
351 // Calls through to the panel object to execute the command or delegates up.
352 - (void)commandDispatch:(id)sender {
353 DCHECK(sender);
354 NSInteger tag = [sender tag];
355 CommandUpdater* command_updater = windowShim_->panel()->command_updater();
356 if (command_updater->SupportsCommand(tag)) {
357 windowShim_->panel()->ExecuteCommandIfEnabled(tag);
358 } else {
359 AppController* appController =
360 base::mac::ObjCCastStrict<AppController>([NSApp delegate]);
361 [appController commandDispatch:sender];
362 }
363 }
364
365 // Handler for the custom Close button.
366 - (void)closePanel {
367 windowShim_->panel()->Close();
368 }
369
370 // Handler for the custom Minimize button.
371 - (void)minimizeButtonClicked:(int)modifierFlags {
372 Panel* panel = windowShim_->panel();
373 panel->OnMinimizeButtonClicked((modifierFlags & NSShiftKeyMask) ?
374 panel::APPLY_TO_ALL : panel::NO_MODIFIER);
375 }
376
377 // Handler for the custom Restore button.
378 - (void)restoreButtonClicked:(int)modifierFlags {
379 Panel* panel = windowShim_->panel();
380 panel->OnRestoreButtonClicked((modifierFlags & NSShiftKeyMask) ?
381 panel::APPLY_TO_ALL : panel::NO_MODIFIER);
382 }
383
384 // Called when the user wants to close the panel or from the shutdown process.
385 // The Panel object is in control of whether or not we're allowed to close. It
386 // may defer closing due to several states, such as onbeforeUnload handlers
387 // needing to be fired. If closing is deferred, the Panel will handle the
388 // processing required to get us to the closing state and (by watching for
389 // the web content going away) will again call to close the window when it's
390 // finally ready.
391 - (BOOL)windowShouldClose:(id)sender {
392 Panel* panel = windowShim_->panel();
393 // Give beforeunload handlers the chance to cancel the close before we hide
394 // the window below.
395 if (!panel->ShouldCloseWindow())
396 return NO;
397
398 if (panel->GetWebContents()) {
399 // Terminate any playing animations.
400 [self terminateBoundsAnimation];
401 animateOnBoundsChange_ = NO;
402 // Make panel close the web content, allowing the renderer to shut down
403 // and call us back again.
404 panel->OnWindowClosing();
405 return NO;
406 }
407
408 // No web content; it's ok to close the window.
409 return YES;
410 }
411
412 // When windowShouldClose returns YES (or if controller receives direct 'close'
413 // signal), window will be unconditionally closed. Clean up.
414 - (void)windowWillClose:(NSNotification*)notification {
415 DCHECK(!windowShim_->panel()->GetWebContents());
416 // Avoid callbacks from a nonblocking animation in progress, if any.
417 [self terminateBoundsAnimation];
418 windowShim_->DidCloseNativeWindow();
419 // Call |-autorelease| after a zero-length delay to avoid deadlock from
420 // code in the current run loop that waits on PANEL_CLOSED notification.
421 // The notification is sent when this object is freed, but this object
422 // cannot be freed until the current run loop completes.
423 [self performSelector:@selector(autorelease)
424 withObject:nil
425 afterDelay:0];
426 }
427
428 - (void)startDrag:(NSPoint)mouseLocation {
429 // Convert from Cocoa's screen coordinates to platform-indepedent screen
430 // coordinates because PanelManager method takes platform-indepedent screen
431 // coordinates.
432 windowShim_->panel()->manager()->StartDragging(
433 windowShim_->panel(),
434 cocoa_utils::ConvertPointFromCocoaCoordinates(mouseLocation));
435 }
436
437 - (void)endDrag:(BOOL)cancelled {
438 windowShim_->panel()->manager()->EndDragging(cancelled);
439 }
440
441 - (void)drag:(NSPoint)mouseLocation {
442 // Convert from Cocoa's screen coordinates to platform-indepedent screen
443 // coordinates because PanelManager method takes platform-indepedent screen
444 // coordinates.
445 windowShim_->panel()->manager()->Drag(
446 cocoa_utils::ConvertPointFromCocoaCoordinates(mouseLocation));
447 }
448
449 - (void)setPanelFrame:(NSRect)frame
450 animate:(BOOL)animate {
451 BOOL jumpToDestination = (!animateOnBoundsChange_ || !animate);
452
453 // If no animation is in progress, apply bounds change instantly.
454 if (jumpToDestination && ![self isAnimatingBounds]) {
455 [[self window] setFrame:frame display:YES animate:NO];
456 return;
457 }
458
459 NSDictionary *windowResize = [NSDictionary dictionaryWithObjectsAndKeys:
460 [self window], NSViewAnimationTargetKey,
461 [NSValue valueWithRect:frame], NSViewAnimationEndFrameKey, nil];
462 NSArray *animations = [NSArray arrayWithObjects:windowResize, nil];
463
464 // If an animation is in progress, update the animation with new target
465 // bounds. Also, set the destination frame bounds to the new value.
466 if (jumpToDestination && [self isAnimatingBounds]) {
467 [boundsAnimation_ setViewAnimations:animations];
468 [[self window] setFrame:frame display:YES animate:NO];
469 return;
470 }
471
472 // Will be enabled back in animationDidEnd callback.
473 [self disableWebContentsViewAutosizing];
474
475 // Terminate previous animation, if it is still playing.
476 [self terminateBoundsAnimation];
477
478 boundsAnimation_ =
479 [[NSViewAnimation alloc] initWithViewAnimations:animations];
480 [boundsAnimation_ setDelegate:self];
481
482 NSRect currentFrame = [[self window] frame];
483 // Compute duration. We use constant speed of animation, however if the change
484 // is too large, we clip the duration (effectively increasing speed) to
485 // limit total duration of animation. This makes 'small' transitions fast.
486 // 'distance' is the max travel between 4 potentially traveling corners.
487 double distanceX = std::max(std::abs(NSMinX(currentFrame) - NSMinX(frame)),
488 std::abs(NSMaxX(currentFrame) - NSMaxX(frame)));
489 double distanceY = std::max(std::abs(NSMinY(currentFrame) - NSMinY(frame)),
490 std::abs(NSMaxY(currentFrame) - NSMaxY(frame)));
491 double distance = std::max(distanceX, distanceY);
492 double duration = std::min(distance / kBoundsAnimationSpeedPixelsPerSecond,
493 kBoundsAnimationMaxDurationSeconds);
494 // Detect animation that happens when expansion state is set to MINIMIZED
495 // and there is relatively big portion of the panel to hide from view.
496 // Initialize animation differently in this case, using fast-pause-slow
497 // method, see below for more details.
498 if (distanceY > 0 &&
499 windowShim_->panel()->expansion_state() == Panel::MINIMIZED) {
500 animationStopToShowTitlebarOnly_ = 1.0 -
501 (windowShim_->panel()->TitleOnlyHeight() - NSHeight(frame)) / distanceY;
502 if (animationStopToShowTitlebarOnly_ > 0.7) { // Relatively big movement.
503 playingMinimizeAnimation_ = YES;
504 duration = 1.5;
505 }
506 }
507 [boundsAnimation_ setDuration: PanelManager::AdjustTimeInterval(duration)];
508 [boundsAnimation_ setFrameRate:0.0];
509 [boundsAnimation_ setAnimationBlockingMode: NSAnimationNonblocking];
510 [boundsAnimation_ startAnimation];
511 }
512
513 - (float)animation:(NSAnimation*)animation
514 valueForProgress:(NSAnimationProgress)progress {
515 return PanelBoundsAnimation::ComputeAnimationValue(
516 progress, playingMinimizeAnimation_, animationStopToShowTitlebarOnly_);
517 }
518
519 - (void)cleanupAfterAnimation {
520 playingMinimizeAnimation_ = NO;
521 if (!windowShim_->panel()->IsMinimized())
522 [self enableWebContentsViewAutosizing];
523 }
524
525 - (void)animationDidEnd:(NSAnimation*)animation {
526 [self cleanupAfterAnimation];
527
528 // Only invoke this callback from animationDidEnd, since animationDidStop can
529 // be called when we interrupt/restart animation which is in progress.
530 // We only need this notification when animation indeed finished moving
531 // the panel bounds.
532 Panel* panel = windowShim_->panel();
533 panel->manager()->OnPanelAnimationEnded(panel);
534 }
535
536 - (void)animationDidStop:(NSAnimation*)animation {
537 [self cleanupAfterAnimation];
538 }
539
540 - (void)terminateBoundsAnimation {
541 if (!boundsAnimation_)
542 return;
543 [boundsAnimation_ stopAnimation];
544 [boundsAnimation_ setDelegate:nil];
545 [boundsAnimation_ release];
546 boundsAnimation_ = nil;
547 }
548
549 - (BOOL)isAnimatingBounds {
550 return boundsAnimation_ && [boundsAnimation_ isAnimating];
551 }
552
553 - (void)onTitlebarMouseClicked:(int)modifierFlags {
554 Panel* panel = windowShim_->panel();
555 panel->OnTitlebarClicked((modifierFlags & NSShiftKeyMask) ?
556 panel::APPLY_TO_ALL : panel::NO_MODIFIER);
557 }
558
559 - (void)onTitlebarDoubleClicked:(int)modifierFlags {
560 // Double-clicking is only allowed to minimize docked panels.
561 Panel* panel = windowShim_->panel();
562 if (panel->collection()->type() != PanelCollection::DOCKED ||
563 panel->IsMinimized())
564 return;
565 [self minimizeButtonClicked:modifierFlags];
566 }
567
568 - (int)titlebarHeightInScreenCoordinates {
569 NSView* titlebar = [self titlebarView];
570 return NSHeight([titlebar convertRect:[titlebar bounds] toView:nil]);
571 }
572
573 // TODO(dcheng): These two selectors are almost copy-and-paste from
574 // BrowserWindowController. Figure out the appropriate way of code sharing,
575 // whether it's refactoring more things into BrowserWindowUtils or making a
576 // common base controller for browser windows.
577 - (void)windowDidBecomeKey:(NSNotification*)notification {
578 windowShim_->panel()->OnActiveStateChanged(true);
579
580 // Make the window user-resizable when it gains the focus.
581 [[self window] setStyleMask:
582 [[self window] styleMask] | NSResizableWindowMask];
583 }
584
585 - (void)windowDidResignKey:(NSNotification*)notification {
586 // If our app is still active and we're still the key window, ignore this
587 // message, since it just means that a menu extra (on the "system status bar")
588 // was activated; we'll get another |-windowDidResignKey| if we ever really
589 // lose key window status.
590 if ([NSApp isActive] && ([NSApp keyWindow] == [self window]))
591 return;
592
593 [self onWindowDidResignKey];
594 }
595
596 - (void)windowWillStartLiveResize:(NSNotification*)notification {
597 // Check if the user-resizing is allowed for the triggering edge/corner.
598 // This is an extra safe guard because we are not able to track the mouse
599 // movement outside the window and Cocoa could trigger the user-resizing
600 // when the mouse moves a bit outside the edge/corner.
601 if (![self canResizeByMouseAtCurrentLocation])
602 return;
603 userResizing_ = YES;
604 windowShim_->panel()->OnPanelStartUserResizing();
605 }
606
607 - (void)windowDidEndLiveResize:(NSNotification*)notification {
608 if (!userResizing_)
609 return;
610 userResizing_ = NO;
611
612 Panel* panel = windowShim_->panel();
613 panel->OnPanelEndUserResizing();
614
615 gfx::Rect newBounds =
616 cocoa_utils::ConvertRectFromCocoaCoordinates([[self window] frame]);
617 if (windowShim_->panel()->GetBounds() == newBounds)
618 return;
619 windowShim_->set_cached_bounds_directly(newBounds);
620
621 panel->IncreaseMaxSize(newBounds.size());
622 panel->set_full_size(newBounds.size());
623
624 panel->collection()->RefreshLayout();
625 }
626
627 - (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)newSize {
628 // As an extra safe guard, we avoid the user resizing if it is deemed not to
629 // be allowed (see comment in windowWillStartLiveResize).
630 if ([[self window] inLiveResize] && !userResizing_)
631 return [[self window] frame].size;
632 return newSize;
633 }
634
635 - (void)windowDidResize:(NSNotification*)notification {
636 Panel* panel = windowShim_->panel();
637 if (userResizing_) {
638 panel->collection()->OnPanelResizedByMouse(
639 panel,
640 cocoa_utils::ConvertRectFromCocoaCoordinates([[self window] frame]));
641 }
642
643 [self updateTrackingArea];
644
645 if (![self isAnimatingBounds] ||
646 panel->collection()->type() != PanelCollection::DOCKED)
647 return;
648
649 // Remove the web contents view from the view hierarchy when the panel is not
650 // taller than the titlebar. Put it back when the panel grows taller than
651 // the titlebar. Note that RenderWidgetHostViewMac works for the case that
652 // the web contents view does not exist in the view hierarchy (i.e. the tab
653 // is not the main one), but it does not work well, like causing occasional
654 // crashes (http://crbug.com/265932), if the web contents view is made hidden.
655 //
656 // This is needed when the docked panels are being animated. When the
657 // animation starts, the contents view autosizing is disabled. After the
658 // animation ends, the contents view autosizing is reenabled and the frame
659 // of contents view is updated. Thus it is likely that the contents view will
660 // overlap with the titlebar view when the panel shrinks to be very small.
661 // The implementation of the web contents view assumes that it will never
662 // overlap with another view in order to paint the web contents view directly.
663 content::WebContents* webContents = panel->GetWebContents();
664 if (!webContents)
665 return;
666 NSView* contentView = webContents->GetNativeView();
667 if (NSHeight([self contentRectForFrameRect:[[self window] frame]]) <= 0) {
668 // No need to retain the view before it is removed from its superview
669 // because WebContentsView keeps a reference to this view.
670 if ([contentView superview])
671 [contentView removeFromSuperview];
672 } else {
673 if (![contentView superview]) {
674 [[[self window] contentView] addSubview:contentView];
675
676 // When the web contents view is put back, we need to tell its render
677 // widget host view to accept focus.
678 content::RenderWidgetHostView* rwhv =
679 webContents->GetRenderWidgetHostView();
680 if (rwhv) {
681 [[self window] makeFirstResponder:rwhv->GetNativeView()];
682 rwhv->SetActive([[self window] isMainWindow]);
683 }
684 }
685 }
686 }
687
688 - (void)activate {
689 // Activate the window. -|windowDidBecomeKey:| will be called when
690 // window becomes active.
691 base::AutoReset<BOOL> pin(&activationRequestedByPanel_, true);
692 [BrowserWindowUtils activateWindowForController:self];
693 }
694
695 - (void)deactivate {
696 if (![[self window] isMainWindow])
697 return;
698
699 [NSApp deactivate];
700
701 // Cocoa does not support deactivating a NSWindow explicitly. To work around
702 // this, we call orderOut and orderFront to force the window to lose its key
703 // window state.
704
705 // Before doing this, we need to disable screen updates to prevent flickering.
706 {
707 gfx::ScopedCocoaDisableScreenUpdates disabler;
708
709 // If a panel is in stacked mode, the window has a background parent window.
710 // We need to detach it from its parent window before applying the ordering
711 // change and then put it back because otherwise tha background parent
712 // window might show up.
713 NSWindow* parentWindow = [[self window] parentWindow];
714 if (parentWindow)
715 [parentWindow removeChildWindow:[self window]];
716
717 [[self window] orderOut:nil];
718 [[self window] orderFront:nil];
719
720 if (parentWindow)
721 [parentWindow addChildWindow:[self window] ordered:NSWindowAbove];
722
723 }
724
725 // Though the above workaround causes the window to lose its key window state,
726 // it does not trigger the system to call windowDidResignKey.
727 [self onWindowDidResignKey];
728 }
729
730 - (void)onWindowDidResignKey {
731 windowShim_->panel()->OnActiveStateChanged(false);
732
733 // Make the window not user-resizable when it loses the focus. This is to
734 // solve the problem that the bottom edge of the active panel does not
735 // trigger the user-resizing if this panel stacks with another inactive
736 // panel at the bottom.
737 [[self window] setStyleMask:
738 [[self window] styleMask] & ~NSResizableWindowMask];
739 }
740
741 - (void)preventBecomingKeyWindow:(BOOL)prevent {
742 canBecomeKeyWindow_ = !prevent;
743 }
744
745 - (void)fullScreenModeChanged:(bool)isFullScreen {
746 [self updateWindowLevel];
747
748 // If the panel is not always on top, its z-order should not be affected if
749 // some other window enters fullscreen mode.
750 if (!windowShim_->panel()->IsAlwaysOnTop())
751 return;
752
753 // The full-screen window is in normal level and changing the panel window
754 // to same normal level will not move it below the full-screen window. Thus
755 // we need to reorder the panel window.
756 if (isFullScreen)
757 [[self window] orderOut:nil];
758 else
759 [[self window] orderFrontRegardless];
760 }
761
762 - (BOOL)canBecomeKeyWindow {
763 // Panel can only gain focus if it is expanded. Minimized panels do not
764 // participate in Cmd-~ rotation.
765 // TODO(dimich): If it will be ever desired to expand/focus the Panel on
766 // keyboard navigation or via main menu, the care should be taken to avoid
767 // cases when minimized Panel is getting keyboard input, invisibly.
768 return canBecomeKeyWindow_;
769 }
770
771 - (int)numPanels {
772 return windowShim_->panel()->manager()->num_panels();
773 }
774
775 - (BOOL)activationRequestedByPanel {
776 return activationRequestedByPanel_;
777 }
778
779 - (void)updateWindowLevel {
780 [self updateWindowLevel:windowShim_->panel()->IsMinimized()];
781 }
782
783 - (void)updateWindowLevel:(BOOL)panelIsMinimized {
784 if (![self isWindowLoaded])
785 return;
786 Panel* panel = windowShim_->panel();
787 if (!panel->IsAlwaysOnTop()) {
788 [[self window] setLevel:NSNormalWindowLevel];
789 return;
790 }
791 // If we simply use NSStatusWindowLevel (25) for all docked panel windows,
792 // IME composition windows for things like CJK languages appear behind panels.
793 // Pre 10.7, IME composition windows have a window level of 19, which is
794 // lower than the dock at level 20. Since we want panels to appear on top of
795 // the dock, it is impossible to enforce an ordering where IME > panel > dock,
796 // since IME < dock.
797 // On 10.7, IME composition windows and the dock both live at level 20, so we
798 // use the same window level for panels. Since newly created windows appear at
799 // the top of their window level, panels are typically on top of the dock, and
800 // the IME composition window correctly draws over the panel.
801 // An autohide dock causes problems though: since it's constantly being
802 // revealed, it ends up drawing on top of other windows at the same level.
803 // While this is OK for expanded panels, it makes minimized panels impossible
804 // to activate. As a result, we still use NSStatusWindowLevel for minimized
805 // panels, since it's impossible to compose IME text in them anyway.
806 if (panelIsMinimized) {
807 [[self window] setLevel:NSStatusWindowLevel];
808 return;
809 }
810 [[self window] setLevel:NSDockWindowLevel];
811 }
812
813 - (void)updateWindowCollectionBehavior {
814 if (![self isWindowLoaded])
815 return;
816 NSWindowCollectionBehavior collectionBehavior =
817 NSWindowCollectionBehaviorParticipatesInCycle;
818 if (windowShim_->panel()->IsAlwaysOnTop())
819 collectionBehavior |= NSWindowCollectionBehaviorCanJoinAllSpaces;
820 [[self window] setCollectionBehavior:collectionBehavior];
821 }
822
823 - (void)updateTrackingArea {
824 NSView* superview = [[[self window] contentView] superview];
825
826 if (trackingArea_.get())
827 [superview removeTrackingArea:trackingArea_.get()];
828
829 trackingArea_.reset(
830 [[CrTrackingArea alloc] initWithRect:[superview bounds]
831 options:NSTrackingInVisibleRect |
832 NSTrackingMouseMoved |
833 NSTrackingActiveInKeyWindow
834 owner:superview
835 userInfo:nil]);
836 [superview addTrackingArea:trackingArea_.get()];
837 }
838
839 - (void)showShadow:(BOOL)show {
840 if (![self isWindowLoaded])
841 return;
842 [[self window] setHasShadow:show];
843 }
844
845 - (void)miniaturize {
846 [[self window] miniaturize:nil];
847 }
848
849 - (BOOL)isMiniaturized {
850 return [[self window] isMiniaturized];
851 }
852
853 - (BOOL)canResizeByMouseAtCurrentLocation {
854 panel::Resizability resizability = windowShim_->panel()->CanResizeByMouse();
855 NSRect frame = [[self window] frame];
856 NSPoint point = [NSEvent mouseLocation];
857
858 if (point.y < NSMinY(frame) + kWidthOfMouseResizeArea) {
859 if (point.x < NSMinX(frame) + kWidthOfMouseResizeArea &&
860 (resizability & panel::RESIZABLE_BOTTOM_LEFT) == 0) {
861 return NO;
862 }
863 if (point.x > NSMaxX(frame) - kWidthOfMouseResizeArea &&
864 (resizability & panel::RESIZABLE_BOTTOM_RIGHT) == 0) {
865 return NO;
866 }
867 if ((resizability & panel::RESIZABLE_BOTTOM) == 0)
868 return NO;
869 } else if (point.y > NSMaxY(frame) - kWidthOfMouseResizeArea) {
870 if (point.x < NSMinX(frame) + kWidthOfMouseResizeArea &&
871 (resizability & panel::RESIZABLE_TOP_LEFT) == 0) {
872 return NO;
873 }
874 if (point.x > NSMaxX(frame) - kWidthOfMouseResizeArea &&
875 (resizability & panel::RESIZABLE_TOP_RIGHT) == 0) {
876 return NO;
877 }
878 if ((resizability & panel::RESIZABLE_TOP) == 0)
879 return NO;
880 } else {
881 if (point.x < NSMinX(frame) + kWidthOfMouseResizeArea &&
882 (resizability & panel::RESIZABLE_LEFT) == 0) {
883 return NO;
884 }
885 if (point.x > NSMaxX(frame) - kWidthOfMouseResizeArea &&
886 (resizability & panel::RESIZABLE_RIGHT) == 0) {
887 return NO;
888 }
889 }
890 return YES;
891 }
892
893 // We have custom implementation of these because our titlebar height is custom
894 // and does not match the standard one.
895 - (NSRect)frameRectForContentRect:(NSRect)contentRect {
896 // contentRect is in contentView coord system. We should add a titlebar on top
897 // and then convert to the windows coord system.
898 contentRect.size.height += panel::kTitlebarHeight;
899 NSRect frameRect = [[[self window] contentView] convertRect:contentRect
900 toView:nil];
901 return frameRect;
902 }
903
904 - (NSRect)contentRectForFrameRect:(NSRect)frameRect {
905 NSRect contentRect = [[[self window] contentView] convertRect:frameRect
906 fromView:nil];
907 contentRect.size.height -= panel::kTitlebarHeight;
908 if (contentRect.size.height < 0)
909 contentRect.size.height = 0;
910 return contentRect;
911 }
912
913 @end
914
915 @implementation WindowCloseWatcher
916
917 - (id)init {
918 if ((self = [super init])) {
919 [[NSNotificationCenter defaultCenter]
920 addObserver:self
921 selector:@selector(windowWillClose:)
922 name:NSWindowWillCloseNotification
923 object:nil];
924 }
925 return self;
926 }
927
928 - (void)windowWillClose:(NSNotification*)notification {
929 // If it looks like a panel may (refuse to) become key after this window is
930 // closed, then explicitly set the topmost browser window on the active space
931 // to be key (if there is one). Otherwise AppKit will stop looking for windows
932 // to make key once it encounters the panel.
933 id closingWindow = [notification object];
934 BOOL orderNext = NO;
935 for (NSWindow* window : [NSApp orderedWindows]) {
936 if ([window isEqual:closingWindow] || ![window isOnActiveSpace])
937 continue;
938
939 if ([window isKindOfClass:[PanelWindowCocoaImpl class]] &&
940 ![window canBecomeKeyWindow]) {
941 orderNext = YES;
942 continue;
943 }
944
945 if (orderNext) {
946 if (![window canBecomeKeyWindow])
947 continue;
948
949 [window makeKeyWindow];
950 }
951 return;
952 }
953 }
954
955 @end
OLDNEW
« no previous file with comments | « chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.h ('k') | chrome/browser/ui/panels/OWNERS » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698