| Index: chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.mm
|
| diff --git a/chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.mm b/chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.mm
|
| deleted file mode 100644
|
| index 4c066a47de96ee7a03d1bc7b4675369a1849efce..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.mm
|
| +++ /dev/null
|
| @@ -1,955 +0,0 @@
|
| -// Copyright (c) 2012 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.
|
| -
|
| -#include "chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.h"
|
| -
|
| -#import <Cocoa/Cocoa.h>
|
| -
|
| -#include "base/auto_reset.h"
|
| -#include "base/logging.h"
|
| -#include "base/mac/bundle_locations.h"
|
| -#include "base/mac/foundation_util.h"
|
| -#include "base/mac/mac_util.h"
|
| -#include "base/mac/scoped_nsautorelease_pool.h"
|
| -#include "base/strings/sys_string_conversions.h"
|
| -#include "chrome/app/chrome_command_ids.h" // IDC_*
|
| -#import "chrome/browser/app_controller_mac.h"
|
| -#include "chrome/browser/chrome_browser_application_mac.h"
|
| -#include "chrome/browser/profiles/profile.h"
|
| -#import "chrome/browser/ui/cocoa/browser_window_utils.h"
|
| -#import "chrome/browser/ui/cocoa/panels/mouse_drag_controller.h"
|
| -#import "chrome/browser/ui/cocoa/panels/panel_cocoa.h"
|
| -#import "chrome/browser/ui/cocoa/panels/panel_titlebar_view_cocoa.h"
|
| -#import "chrome/browser/ui/cocoa/panels/panel_utils_cocoa.h"
|
| -#import "chrome/browser/ui/cocoa/sprite_view.h"
|
| -#import "chrome/browser/ui/cocoa/tab_contents/favicon_util_mac.h"
|
| -#import "chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.h"
|
| -#include "chrome/browser/ui/panels/panel_bounds_animation.h"
|
| -#include "chrome/browser/ui/panels/panel_collection.h"
|
| -#include "chrome/browser/ui/panels/panel_constants.h"
|
| -#include "chrome/browser/ui/panels/panel_manager.h"
|
| -#include "chrome/browser/ui/panels/stacked_panel_collection.h"
|
| -#include "chrome/browser/ui/tabs/tab_strip_model.h"
|
| -#include "chrome/browser/ui/toolbar/encoding_menu_controller.h"
|
| -#include "content/public/browser/render_widget_host_view.h"
|
| -#include "content/public/browser/web_contents.h"
|
| -#include "ui/base/resource/resource_bundle.h"
|
| -#include "ui/gfx/image/image.h"
|
| -#include "ui/gfx/mac/scoped_cocoa_disable_screen_updates.h"
|
| -#include "ui/resources/grit/ui_resources.h"
|
| -
|
| -using content::WebContents;
|
| -
|
| -const int kMinimumWindowSize = 1;
|
| -const double kBoundsAnimationSpeedPixelsPerSecond = 1000;
|
| -const double kBoundsAnimationMaxDurationSeconds = 0.18;
|
| -
|
| -// Edge thickness to trigger user resizing via system, in screen pixels.
|
| -const double kWidthOfMouseResizeArea = 15.0;
|
| -
|
| -// Notification observer to prevent panels becoming key when windows are closed.
|
| -@interface WindowCloseWatcher : NSObject
|
| -- (void)windowWillClose:(NSNotification*)notification;
|
| -@end
|
| -
|
| -@interface PanelWindowControllerCocoa (PanelsCanBecomeKey)
|
| -// Internal helper method for extracting the total number of panel windows
|
| -// from the panel manager. Used to decide if panel can become the key window.
|
| -- (int)numPanels;
|
| -@end
|
| -
|
| -@implementation PanelWindowCocoaImpl
|
| -// The panels cannot be reduced to 3-px windows on the edge of the screen
|
| -// active area (above Dock). Default constraining logic makes at least a height
|
| -// of the titlebar visible, so the user could still grab it. We do 'restore'
|
| -// differently, and minimize panels to 3 px. Hence the need to override the
|
| -// constraining logic.
|
| -- (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen {
|
| - return frameRect;
|
| -}
|
| -
|
| -// Prevent panel window from becoming key - for example when it is minimized.
|
| -// Panel windows use a higher priority NSWindowLevel to ensure they are always
|
| -// visible, causing the OS to prefer panel windows when selecting a window
|
| -// to make the key window. To counter this preference, we override
|
| -// -[NSWindow:canBecomeKeyWindow] to restrict when the panel can become the
|
| -// key window to a limited set of scenarios, such as when cycling through
|
| -// windows, when panels are the only remaining windows, when an event
|
| -// triggers window activation, etc. The panel may also be prevented from
|
| -// becoming the key window, regardless of the above scenarios, such as when
|
| -// a panel is minimized.
|
| -- (BOOL)canBecomeKeyWindow {
|
| - // Give precedence to controller preventing activation of the window.
|
| - PanelWindowControllerCocoa* controller =
|
| - base::mac::ObjCCast<PanelWindowControllerCocoa>([self windowController]);
|
| - if (![controller canBecomeKeyWindow])
|
| - return NO;
|
| -
|
| - BrowserCrApplication* app = base::mac::ObjCCast<BrowserCrApplication>(
|
| - [BrowserCrApplication sharedApplication]);
|
| -
|
| - // A Panel window can become the key window only in limited scenarios.
|
| - // This prevents the system from always preferring a Panel window due
|
| - // to its higher priority NSWindowLevel when selecting a window to make key.
|
| - return ([app isHandlingSendEvent] && [[app currentEvent] window] == self) ||
|
| - [controller activationRequestedByPanel] ||
|
| - [app isCyclingWindows] ||
|
| - [self isKeyWindow] ||
|
| - [[app windows] count] == static_cast<NSUInteger>([controller numPanels]);
|
| -}
|
| -
|
| -- (void)performMiniaturize:(id)sender {
|
| - [[self windowController] minimizeButtonClicked:0];
|
| -}
|
| -
|
| -- (void)mouseMoved:(NSEvent*)event {
|
| - // Cocoa does not support letting the application determine the edges that
|
| - // can trigger the user resizing. To work around this, we track the mouse
|
| - // location. When it is close to the edge/corner where the user resizing
|
| - // is not desired, we force the min and max size of the window to be same
|
| - // as current window size. For all other cases, we restore the min and max
|
| - // size.
|
| - PanelWindowControllerCocoa* controller =
|
| - base::mac::ObjCCast<PanelWindowControllerCocoa>([self windowController]);
|
| - NSRect frame = [self frame];
|
| - if ([controller canResizeByMouseAtCurrentLocation]) {
|
| - // Mac window server limits window sizes to 10000.
|
| - NSSize maxSize = NSMakeSize(10000, 10000);
|
| -
|
| - // If the user is resizing a stacked panel by its bottom edge, make sure its
|
| - // height cannot grow more than what the panel below it could offer. This is
|
| - // because growing a stacked panel by y amount will shrink the panel below
|
| - // it by same amount and we do not want the panel below it being shrunk to
|
| - // be smaller than the titlebar.
|
| - Panel* panel = [controller panel];
|
| - NSPoint point = [NSEvent mouseLocation];
|
| - if (point.y < NSMinY(frame) + kWidthOfMouseResizeArea && panel->stack()) {
|
| - Panel* belowPanel = panel->stack()->GetPanelBelow(panel);
|
| - if (belowPanel && !belowPanel->IsMinimized()) {
|
| - maxSize.height = panel->GetBounds().height() +
|
| - belowPanel->GetBounds().height() - panel::kTitlebarHeight;
|
| - }
|
| - }
|
| -
|
| - // Enable the user-resizing by setting both min and max size to the right
|
| - // values.
|
| - [self setMinSize:NSMakeSize(panel::kPanelMinWidth,
|
| - panel::kPanelMinHeight)];
|
| - [self setMaxSize:maxSize];
|
| - } else {
|
| - // Disable the user-resizing by setting both min and max size to be same as
|
| - // current window size.
|
| - [self setMinSize:frame.size];
|
| - [self setMaxSize:frame.size];
|
| - }
|
| -
|
| - [super mouseMoved:event];
|
| -}
|
| -@end
|
| -
|
| -@implementation PanelWindowControllerCocoa
|
| -
|
| -- (id)initWithPanel:(PanelCocoa*)window {
|
| - NSString* nibpath =
|
| - [base::mac::FrameworkBundle() pathForResource:@"Panel" ofType:@"nib"];
|
| - if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
|
| - windowShim_.reset(window);
|
| - animateOnBoundsChange_ = YES;
|
| - canBecomeKeyWindow_ = YES;
|
| - activationRequestedByPanel_ = NO;
|
| -
|
| - // Leaky singleton. Initialized when the first panel is created.
|
| - static WindowCloseWatcher* watcher = [[WindowCloseWatcher alloc] init];
|
| - (void)watcher; // Suppress the unused variable warning.
|
| - }
|
| - return self;
|
| -}
|
| -
|
| -- (Panel*)panel {
|
| - return windowShim_->panel();
|
| -}
|
| -
|
| -- (void)awakeFromNib {
|
| - NSWindow* window = [self window];
|
| -
|
| - DCHECK(window);
|
| - DCHECK(titlebar_view_);
|
| - DCHECK_EQ(self, [window delegate]);
|
| -
|
| - [self updateWindowLevel];
|
| -
|
| - [self updateWindowCollectionBehavior];
|
| -
|
| - [titlebar_view_ attach];
|
| -
|
| - // Set initial size of the window to match the size of the panel to give
|
| - // the renderer the proper size to work with earlier, avoiding a resize
|
| - // after the window is revealed.
|
| - gfx::Rect panelBounds = windowShim_->panel()->GetBounds();
|
| - NSRect frame = [window frame];
|
| - frame.size.width = panelBounds.width();
|
| - frame.size.height = panelBounds.height();
|
| - [window setFrame:frame display:NO];
|
| -
|
| - // MacOS will turn the user-resizing to the user-dragging if the direction of
|
| - // the dragging is orthogonal to the direction of the arrow cursor. We do not
|
| - // want this since it will bypass our dragging logic. The panel window is
|
| - // still draggable because we track and handle the dragging in our custom way.
|
| - [[self window] setMovable:NO];
|
| -
|
| - [self updateTrackingArea];
|
| -}
|
| -
|
| -- (void)updateWebContentsViewFrame {
|
| - content::WebContents* webContents = windowShim_->panel()->GetWebContents();
|
| - if (!webContents)
|
| - return;
|
| -
|
| - // Compute the size of the web contents view. Don't assume it's similar to the
|
| - // size of the contentView, because the contentView is managed by the Cocoa
|
| - // to be (window - standard titlebar), while we have taller custom titlebar
|
| - // instead. In coordinate system of window's contentView.
|
| - NSRect contentFrame = [self contentRectForFrameRect:[[self window] frame]];
|
| - contentFrame.origin = NSZeroPoint;
|
| -
|
| - NSView* contentView = webContents->GetNativeView();
|
| - if (!NSEqualRects([contentView frame], contentFrame))
|
| - [contentView setFrame:contentFrame];
|
| -}
|
| -
|
| -- (void)disableWebContentsViewAutosizing {
|
| - [[[self window] contentView] setAutoresizesSubviews:NO];
|
| -}
|
| -
|
| -- (void)enableWebContentsViewAutosizing {
|
| - [self updateWebContentsViewFrame];
|
| - [[[self window] contentView] setAutoresizesSubviews:YES];
|
| -}
|
| -
|
| -- (void)revealAnimatedWithFrame:(const NSRect&)frame {
|
| - NSWindow* window = [self window]; // This ensures loading the nib.
|
| -
|
| - // Disable subview resizing while resizing the window to avoid renderer
|
| - // resizes during intermediate stages of animation.
|
| - [self disableWebContentsViewAutosizing];
|
| -
|
| - // We grow the window from the bottom up to produce a 'reveal' animation.
|
| - NSRect startFrame = NSMakeRect(NSMinX(frame), NSMinY(frame),
|
| - NSWidth(frame), kMinimumWindowSize);
|
| - [window setFrame:startFrame display:NO animate:NO];
|
| - // Shows the window without making it key, on top of its layer, even if
|
| - // Chromium is not an active app.
|
| - [window orderFrontRegardless];
|
| - // TODO(dcheng): Temporary hack to work around the fact that
|
| - // orderFrontRegardless causes us to become the first responder. The usual
|
| - // Chrome assumption is that becoming the first responder = you have focus, so
|
| - // we always deactivate the controls here. If we're created as an active
|
| - // panel, we'll get a NSWindowDidBecomeKeyNotification and reactivate the web
|
| - // view properly. See crbug.com/97831 for more details.
|
| - WebContents* web_contents = windowShim_->panel()->GetWebContents();
|
| - // RWHV may be NULL in unit tests.
|
| - if (web_contents && web_contents->GetRenderWidgetHostView())
|
| - web_contents->GetRenderWidgetHostView()->SetActive(false);
|
| -
|
| - // This will re-enable the content resizing after it finishes.
|
| - [self setPanelFrame:frame animate:YES];
|
| -}
|
| -
|
| -- (void)updateTitleBar {
|
| - NSString* newTitle = base::SysUTF16ToNSString(
|
| - windowShim_->panel()->GetWindowTitle());
|
| - pendingWindowTitle_.reset(
|
| - [BrowserWindowUtils scheduleReplaceOldTitle:pendingWindowTitle_.get()
|
| - withNewTitle:newTitle
|
| - forWindow:[self window]]);
|
| - [titlebar_view_ setTitle:newTitle];
|
| - [self updateIcon];
|
| -}
|
| -
|
| -- (void)updateIcon {
|
| - base::scoped_nsobject<NSView> iconView;
|
| - if (throbberShouldSpin_) {
|
| - // If the throbber is spinning now, no need to replace it.
|
| - if ([[titlebar_view_ icon] isKindOfClass:[SpriteView class]])
|
| - return;
|
| -
|
| - NSImage* iconImage =
|
| - ResourceBundle::GetSharedInstance().GetNativeImageNamed(
|
| - IDR_THROBBER).ToNSImage();
|
| - SpriteView* spriteView = [[SpriteView alloc] init];
|
| - [spriteView setImage:iconImage];
|
| - iconView.reset(spriteView);
|
| - } else {
|
| - const gfx::Image& page_icon = windowShim_->panel()->GetCurrentPageIcon();
|
| - ResourceBundle& rb = ResourceBundle::GetSharedInstance();
|
| - NSRect iconFrame = [[titlebar_view_ icon] frame];
|
| - NSImage* iconImage = page_icon.IsEmpty() ?
|
| - rb.GetNativeImageNamed(IDR_DEFAULT_FAVICON).ToNSImage() :
|
| - page_icon.ToNSImage();
|
| - NSImageView* imageView = [[NSImageView alloc] initWithFrame:iconFrame];
|
| - [imageView setImage:iconImage];
|
| - iconView.reset(imageView);
|
| - }
|
| - [titlebar_view_ setIcon:iconView];
|
| -}
|
| -
|
| -- (void)updateThrobber:(BOOL)shouldSpin {
|
| - if (throbberShouldSpin_ == shouldSpin)
|
| - return;
|
| - throbberShouldSpin_ = shouldSpin;
|
| -
|
| - // If the titlebar view has not been attached, bail out.
|
| - if (!titlebar_view_)
|
| - return;
|
| -
|
| - [self updateIcon];
|
| -}
|
| -
|
| -- (void)updateTitleBarMinimizeRestoreButtonVisibility {
|
| - Panel* panel = windowShim_->panel();
|
| - [titlebar_view_ setMinimizeButtonVisibility:panel->CanShowMinimizeButton()];
|
| - [titlebar_view_ setRestoreButtonVisibility:panel->CanShowRestoreButton()];
|
| -}
|
| -
|
| -- (void)webContentsInserted:(WebContents*)contents {
|
| - NSView* view = contents->GetNativeView();
|
| - [[[self window] contentView] addSubview:view];
|
| - [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
| -
|
| - [self enableWebContentsViewAutosizing];
|
| -}
|
| -
|
| -- (void)webContentsDetached:(WebContents*)contents {
|
| - [contents->GetNativeView() removeFromSuperview];
|
| -}
|
| -
|
| -- (PanelTitlebarViewCocoa*)titlebarView {
|
| - return titlebar_view_;
|
| -}
|
| -
|
| -// Called to validate menu and toolbar items when this window is key. All the
|
| -// items we care about have been set with the |-commandDispatch:|
|
| -// action and a target of FirstResponder in IB.
|
| -// Delegate to the NSApp delegate if Panel does not care about the command or
|
| -// shortcut, to make sure the global items in Chrome main app menu still work.
|
| -- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
|
| - if ([item action] == @selector(commandDispatch:)) {
|
| - NSInteger tag = [item tag];
|
| - CommandUpdater* command_updater = windowShim_->panel()->command_updater();
|
| - if (command_updater->SupportsCommand(tag))
|
| - return command_updater->IsCommandEnabled(tag);
|
| -
|
| - AppController* appController =
|
| - base::mac::ObjCCastStrict<AppController>([NSApp delegate]);
|
| - return [appController validateUserInterfaceItem:item];
|
| - }
|
| - return NO;
|
| -}
|
| -
|
| -// Called when the user picks a menu or toolbar item when this window is key.
|
| -// Calls through to the panel object to execute the command or delegates up.
|
| -- (void)commandDispatch:(id)sender {
|
| - DCHECK(sender);
|
| - NSInteger tag = [sender tag];
|
| - CommandUpdater* command_updater = windowShim_->panel()->command_updater();
|
| - if (command_updater->SupportsCommand(tag)) {
|
| - windowShim_->panel()->ExecuteCommandIfEnabled(tag);
|
| - } else {
|
| - AppController* appController =
|
| - base::mac::ObjCCastStrict<AppController>([NSApp delegate]);
|
| - [appController commandDispatch:sender];
|
| - }
|
| -}
|
| -
|
| -// Handler for the custom Close button.
|
| -- (void)closePanel {
|
| - windowShim_->panel()->Close();
|
| -}
|
| -
|
| -// Handler for the custom Minimize button.
|
| -- (void)minimizeButtonClicked:(int)modifierFlags {
|
| - Panel* panel = windowShim_->panel();
|
| - panel->OnMinimizeButtonClicked((modifierFlags & NSShiftKeyMask) ?
|
| - panel::APPLY_TO_ALL : panel::NO_MODIFIER);
|
| -}
|
| -
|
| -// Handler for the custom Restore button.
|
| -- (void)restoreButtonClicked:(int)modifierFlags {
|
| - Panel* panel = windowShim_->panel();
|
| - panel->OnRestoreButtonClicked((modifierFlags & NSShiftKeyMask) ?
|
| - panel::APPLY_TO_ALL : panel::NO_MODIFIER);
|
| -}
|
| -
|
| -// Called when the user wants to close the panel or from the shutdown process.
|
| -// The Panel object is in control of whether or not we're allowed to close. It
|
| -// may defer closing due to several states, such as onbeforeUnload handlers
|
| -// needing to be fired. If closing is deferred, the Panel will handle the
|
| -// processing required to get us to the closing state and (by watching for
|
| -// the web content going away) will again call to close the window when it's
|
| -// finally ready.
|
| -- (BOOL)windowShouldClose:(id)sender {
|
| - Panel* panel = windowShim_->panel();
|
| - // Give beforeunload handlers the chance to cancel the close before we hide
|
| - // the window below.
|
| - if (!panel->ShouldCloseWindow())
|
| - return NO;
|
| -
|
| - if (panel->GetWebContents()) {
|
| - // Terminate any playing animations.
|
| - [self terminateBoundsAnimation];
|
| - animateOnBoundsChange_ = NO;
|
| - // Make panel close the web content, allowing the renderer to shut down
|
| - // and call us back again.
|
| - panel->OnWindowClosing();
|
| - return NO;
|
| - }
|
| -
|
| - // No web content; it's ok to close the window.
|
| - return YES;
|
| -}
|
| -
|
| -// When windowShouldClose returns YES (or if controller receives direct 'close'
|
| -// signal), window will be unconditionally closed. Clean up.
|
| -- (void)windowWillClose:(NSNotification*)notification {
|
| - DCHECK(!windowShim_->panel()->GetWebContents());
|
| - // Avoid callbacks from a nonblocking animation in progress, if any.
|
| - [self terminateBoundsAnimation];
|
| - windowShim_->DidCloseNativeWindow();
|
| - // Call |-autorelease| after a zero-length delay to avoid deadlock from
|
| - // code in the current run loop that waits on PANEL_CLOSED notification.
|
| - // The notification is sent when this object is freed, but this object
|
| - // cannot be freed until the current run loop completes.
|
| - [self performSelector:@selector(autorelease)
|
| - withObject:nil
|
| - afterDelay:0];
|
| -}
|
| -
|
| -- (void)startDrag:(NSPoint)mouseLocation {
|
| - // Convert from Cocoa's screen coordinates to platform-indepedent screen
|
| - // coordinates because PanelManager method takes platform-indepedent screen
|
| - // coordinates.
|
| - windowShim_->panel()->manager()->StartDragging(
|
| - windowShim_->panel(),
|
| - cocoa_utils::ConvertPointFromCocoaCoordinates(mouseLocation));
|
| -}
|
| -
|
| -- (void)endDrag:(BOOL)cancelled {
|
| - windowShim_->panel()->manager()->EndDragging(cancelled);
|
| -}
|
| -
|
| -- (void)drag:(NSPoint)mouseLocation {
|
| - // Convert from Cocoa's screen coordinates to platform-indepedent screen
|
| - // coordinates because PanelManager method takes platform-indepedent screen
|
| - // coordinates.
|
| - windowShim_->panel()->manager()->Drag(
|
| - cocoa_utils::ConvertPointFromCocoaCoordinates(mouseLocation));
|
| -}
|
| -
|
| -- (void)setPanelFrame:(NSRect)frame
|
| - animate:(BOOL)animate {
|
| - BOOL jumpToDestination = (!animateOnBoundsChange_ || !animate);
|
| -
|
| - // If no animation is in progress, apply bounds change instantly.
|
| - if (jumpToDestination && ![self isAnimatingBounds]) {
|
| - [[self window] setFrame:frame display:YES animate:NO];
|
| - return;
|
| - }
|
| -
|
| - NSDictionary *windowResize = [NSDictionary dictionaryWithObjectsAndKeys:
|
| - [self window], NSViewAnimationTargetKey,
|
| - [NSValue valueWithRect:frame], NSViewAnimationEndFrameKey, nil];
|
| - NSArray *animations = [NSArray arrayWithObjects:windowResize, nil];
|
| -
|
| - // If an animation is in progress, update the animation with new target
|
| - // bounds. Also, set the destination frame bounds to the new value.
|
| - if (jumpToDestination && [self isAnimatingBounds]) {
|
| - [boundsAnimation_ setViewAnimations:animations];
|
| - [[self window] setFrame:frame display:YES animate:NO];
|
| - return;
|
| - }
|
| -
|
| - // Will be enabled back in animationDidEnd callback.
|
| - [self disableWebContentsViewAutosizing];
|
| -
|
| - // Terminate previous animation, if it is still playing.
|
| - [self terminateBoundsAnimation];
|
| -
|
| - boundsAnimation_ =
|
| - [[NSViewAnimation alloc] initWithViewAnimations:animations];
|
| - [boundsAnimation_ setDelegate:self];
|
| -
|
| - NSRect currentFrame = [[self window] frame];
|
| - // Compute duration. We use constant speed of animation, however if the change
|
| - // is too large, we clip the duration (effectively increasing speed) to
|
| - // limit total duration of animation. This makes 'small' transitions fast.
|
| - // 'distance' is the max travel between 4 potentially traveling corners.
|
| - double distanceX = std::max(std::abs(NSMinX(currentFrame) - NSMinX(frame)),
|
| - std::abs(NSMaxX(currentFrame) - NSMaxX(frame)));
|
| - double distanceY = std::max(std::abs(NSMinY(currentFrame) - NSMinY(frame)),
|
| - std::abs(NSMaxY(currentFrame) - NSMaxY(frame)));
|
| - double distance = std::max(distanceX, distanceY);
|
| - double duration = std::min(distance / kBoundsAnimationSpeedPixelsPerSecond,
|
| - kBoundsAnimationMaxDurationSeconds);
|
| - // Detect animation that happens when expansion state is set to MINIMIZED
|
| - // and there is relatively big portion of the panel to hide from view.
|
| - // Initialize animation differently in this case, using fast-pause-slow
|
| - // method, see below for more details.
|
| - if (distanceY > 0 &&
|
| - windowShim_->panel()->expansion_state() == Panel::MINIMIZED) {
|
| - animationStopToShowTitlebarOnly_ = 1.0 -
|
| - (windowShim_->panel()->TitleOnlyHeight() - NSHeight(frame)) / distanceY;
|
| - if (animationStopToShowTitlebarOnly_ > 0.7) { // Relatively big movement.
|
| - playingMinimizeAnimation_ = YES;
|
| - duration = 1.5;
|
| - }
|
| - }
|
| - [boundsAnimation_ setDuration: PanelManager::AdjustTimeInterval(duration)];
|
| - [boundsAnimation_ setFrameRate:0.0];
|
| - [boundsAnimation_ setAnimationBlockingMode: NSAnimationNonblocking];
|
| - [boundsAnimation_ startAnimation];
|
| -}
|
| -
|
| -- (float)animation:(NSAnimation*)animation
|
| - valueForProgress:(NSAnimationProgress)progress {
|
| - return PanelBoundsAnimation::ComputeAnimationValue(
|
| - progress, playingMinimizeAnimation_, animationStopToShowTitlebarOnly_);
|
| -}
|
| -
|
| -- (void)cleanupAfterAnimation {
|
| - playingMinimizeAnimation_ = NO;
|
| - if (!windowShim_->panel()->IsMinimized())
|
| - [self enableWebContentsViewAutosizing];
|
| -}
|
| -
|
| -- (void)animationDidEnd:(NSAnimation*)animation {
|
| - [self cleanupAfterAnimation];
|
| -
|
| - // Only invoke this callback from animationDidEnd, since animationDidStop can
|
| - // be called when we interrupt/restart animation which is in progress.
|
| - // We only need this notification when animation indeed finished moving
|
| - // the panel bounds.
|
| - Panel* panel = windowShim_->panel();
|
| - panel->manager()->OnPanelAnimationEnded(panel);
|
| -}
|
| -
|
| -- (void)animationDidStop:(NSAnimation*)animation {
|
| - [self cleanupAfterAnimation];
|
| -}
|
| -
|
| -- (void)terminateBoundsAnimation {
|
| - if (!boundsAnimation_)
|
| - return;
|
| - [boundsAnimation_ stopAnimation];
|
| - [boundsAnimation_ setDelegate:nil];
|
| - [boundsAnimation_ release];
|
| - boundsAnimation_ = nil;
|
| -}
|
| -
|
| -- (BOOL)isAnimatingBounds {
|
| - return boundsAnimation_ && [boundsAnimation_ isAnimating];
|
| -}
|
| -
|
| -- (void)onTitlebarMouseClicked:(int)modifierFlags {
|
| - Panel* panel = windowShim_->panel();
|
| - panel->OnTitlebarClicked((modifierFlags & NSShiftKeyMask) ?
|
| - panel::APPLY_TO_ALL : panel::NO_MODIFIER);
|
| -}
|
| -
|
| -- (void)onTitlebarDoubleClicked:(int)modifierFlags {
|
| - // Double-clicking is only allowed to minimize docked panels.
|
| - Panel* panel = windowShim_->panel();
|
| - if (panel->collection()->type() != PanelCollection::DOCKED ||
|
| - panel->IsMinimized())
|
| - return;
|
| - [self minimizeButtonClicked:modifierFlags];
|
| -}
|
| -
|
| -- (int)titlebarHeightInScreenCoordinates {
|
| - NSView* titlebar = [self titlebarView];
|
| - return NSHeight([titlebar convertRect:[titlebar bounds] toView:nil]);
|
| -}
|
| -
|
| -// TODO(dcheng): These two selectors are almost copy-and-paste from
|
| -// BrowserWindowController. Figure out the appropriate way of code sharing,
|
| -// whether it's refactoring more things into BrowserWindowUtils or making a
|
| -// common base controller for browser windows.
|
| -- (void)windowDidBecomeKey:(NSNotification*)notification {
|
| - windowShim_->panel()->OnActiveStateChanged(true);
|
| -
|
| - // Make the window user-resizable when it gains the focus.
|
| - [[self window] setStyleMask:
|
| - [[self window] styleMask] | NSResizableWindowMask];
|
| -}
|
| -
|
| -- (void)windowDidResignKey:(NSNotification*)notification {
|
| - // If our app is still active and we're still the key window, ignore this
|
| - // message, since it just means that a menu extra (on the "system status bar")
|
| - // was activated; we'll get another |-windowDidResignKey| if we ever really
|
| - // lose key window status.
|
| - if ([NSApp isActive] && ([NSApp keyWindow] == [self window]))
|
| - return;
|
| -
|
| - [self onWindowDidResignKey];
|
| -}
|
| -
|
| -- (void)windowWillStartLiveResize:(NSNotification*)notification {
|
| - // Check if the user-resizing is allowed for the triggering edge/corner.
|
| - // This is an extra safe guard because we are not able to track the mouse
|
| - // movement outside the window and Cocoa could trigger the user-resizing
|
| - // when the mouse moves a bit outside the edge/corner.
|
| - if (![self canResizeByMouseAtCurrentLocation])
|
| - return;
|
| - userResizing_ = YES;
|
| - windowShim_->panel()->OnPanelStartUserResizing();
|
| -}
|
| -
|
| -- (void)windowDidEndLiveResize:(NSNotification*)notification {
|
| - if (!userResizing_)
|
| - return;
|
| - userResizing_ = NO;
|
| -
|
| - Panel* panel = windowShim_->panel();
|
| - panel->OnPanelEndUserResizing();
|
| -
|
| - gfx::Rect newBounds =
|
| - cocoa_utils::ConvertRectFromCocoaCoordinates([[self window] frame]);
|
| - if (windowShim_->panel()->GetBounds() == newBounds)
|
| - return;
|
| - windowShim_->set_cached_bounds_directly(newBounds);
|
| -
|
| - panel->IncreaseMaxSize(newBounds.size());
|
| - panel->set_full_size(newBounds.size());
|
| -
|
| - panel->collection()->RefreshLayout();
|
| -}
|
| -
|
| -- (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)newSize {
|
| - // As an extra safe guard, we avoid the user resizing if it is deemed not to
|
| - // be allowed (see comment in windowWillStartLiveResize).
|
| - if ([[self window] inLiveResize] && !userResizing_)
|
| - return [[self window] frame].size;
|
| - return newSize;
|
| -}
|
| -
|
| -- (void)windowDidResize:(NSNotification*)notification {
|
| - Panel* panel = windowShim_->panel();
|
| - if (userResizing_) {
|
| - panel->collection()->OnPanelResizedByMouse(
|
| - panel,
|
| - cocoa_utils::ConvertRectFromCocoaCoordinates([[self window] frame]));
|
| - }
|
| -
|
| - [self updateTrackingArea];
|
| -
|
| - if (![self isAnimatingBounds] ||
|
| - panel->collection()->type() != PanelCollection::DOCKED)
|
| - return;
|
| -
|
| - // Remove the web contents view from the view hierarchy when the panel is not
|
| - // taller than the titlebar. Put it back when the panel grows taller than
|
| - // the titlebar. Note that RenderWidgetHostViewMac works for the case that
|
| - // the web contents view does not exist in the view hierarchy (i.e. the tab
|
| - // is not the main one), but it does not work well, like causing occasional
|
| - // crashes (http://crbug.com/265932), if the web contents view is made hidden.
|
| - //
|
| - // This is needed when the docked panels are being animated. When the
|
| - // animation starts, the contents view autosizing is disabled. After the
|
| - // animation ends, the contents view autosizing is reenabled and the frame
|
| - // of contents view is updated. Thus it is likely that the contents view will
|
| - // overlap with the titlebar view when the panel shrinks to be very small.
|
| - // The implementation of the web contents view assumes that it will never
|
| - // overlap with another view in order to paint the web contents view directly.
|
| - content::WebContents* webContents = panel->GetWebContents();
|
| - if (!webContents)
|
| - return;
|
| - NSView* contentView = webContents->GetNativeView();
|
| - if (NSHeight([self contentRectForFrameRect:[[self window] frame]]) <= 0) {
|
| - // No need to retain the view before it is removed from its superview
|
| - // because WebContentsView keeps a reference to this view.
|
| - if ([contentView superview])
|
| - [contentView removeFromSuperview];
|
| - } else {
|
| - if (![contentView superview]) {
|
| - [[[self window] contentView] addSubview:contentView];
|
| -
|
| - // When the web contents view is put back, we need to tell its render
|
| - // widget host view to accept focus.
|
| - content::RenderWidgetHostView* rwhv =
|
| - webContents->GetRenderWidgetHostView();
|
| - if (rwhv) {
|
| - [[self window] makeFirstResponder:rwhv->GetNativeView()];
|
| - rwhv->SetActive([[self window] isMainWindow]);
|
| - }
|
| - }
|
| - }
|
| -}
|
| -
|
| -- (void)activate {
|
| - // Activate the window. -|windowDidBecomeKey:| will be called when
|
| - // window becomes active.
|
| - base::AutoReset<BOOL> pin(&activationRequestedByPanel_, true);
|
| - [BrowserWindowUtils activateWindowForController:self];
|
| -}
|
| -
|
| -- (void)deactivate {
|
| - if (![[self window] isMainWindow])
|
| - return;
|
| -
|
| - [NSApp deactivate];
|
| -
|
| - // Cocoa does not support deactivating a NSWindow explicitly. To work around
|
| - // this, we call orderOut and orderFront to force the window to lose its key
|
| - // window state.
|
| -
|
| - // Before doing this, we need to disable screen updates to prevent flickering.
|
| - {
|
| - gfx::ScopedCocoaDisableScreenUpdates disabler;
|
| -
|
| - // If a panel is in stacked mode, the window has a background parent window.
|
| - // We need to detach it from its parent window before applying the ordering
|
| - // change and then put it back because otherwise tha background parent
|
| - // window might show up.
|
| - NSWindow* parentWindow = [[self window] parentWindow];
|
| - if (parentWindow)
|
| - [parentWindow removeChildWindow:[self window]];
|
| -
|
| - [[self window] orderOut:nil];
|
| - [[self window] orderFront:nil];
|
| -
|
| - if (parentWindow)
|
| - [parentWindow addChildWindow:[self window] ordered:NSWindowAbove];
|
| -
|
| - }
|
| -
|
| - // Though the above workaround causes the window to lose its key window state,
|
| - // it does not trigger the system to call windowDidResignKey.
|
| - [self onWindowDidResignKey];
|
| -}
|
| -
|
| -- (void)onWindowDidResignKey {
|
| - windowShim_->panel()->OnActiveStateChanged(false);
|
| -
|
| - // Make the window not user-resizable when it loses the focus. This is to
|
| - // solve the problem that the bottom edge of the active panel does not
|
| - // trigger the user-resizing if this panel stacks with another inactive
|
| - // panel at the bottom.
|
| - [[self window] setStyleMask:
|
| - [[self window] styleMask] & ~NSResizableWindowMask];
|
| -}
|
| -
|
| -- (void)preventBecomingKeyWindow:(BOOL)prevent {
|
| - canBecomeKeyWindow_ = !prevent;
|
| -}
|
| -
|
| -- (void)fullScreenModeChanged:(bool)isFullScreen {
|
| - [self updateWindowLevel];
|
| -
|
| - // If the panel is not always on top, its z-order should not be affected if
|
| - // some other window enters fullscreen mode.
|
| - if (!windowShim_->panel()->IsAlwaysOnTop())
|
| - return;
|
| -
|
| - // The full-screen window is in normal level and changing the panel window
|
| - // to same normal level will not move it below the full-screen window. Thus
|
| - // we need to reorder the panel window.
|
| - if (isFullScreen)
|
| - [[self window] orderOut:nil];
|
| - else
|
| - [[self window] orderFrontRegardless];
|
| -}
|
| -
|
| -- (BOOL)canBecomeKeyWindow {
|
| - // Panel can only gain focus if it is expanded. Minimized panels do not
|
| - // participate in Cmd-~ rotation.
|
| - // TODO(dimich): If it will be ever desired to expand/focus the Panel on
|
| - // keyboard navigation or via main menu, the care should be taken to avoid
|
| - // cases when minimized Panel is getting keyboard input, invisibly.
|
| - return canBecomeKeyWindow_;
|
| -}
|
| -
|
| -- (int)numPanels {
|
| - return windowShim_->panel()->manager()->num_panels();
|
| -}
|
| -
|
| -- (BOOL)activationRequestedByPanel {
|
| - return activationRequestedByPanel_;
|
| -}
|
| -
|
| -- (void)updateWindowLevel {
|
| - [self updateWindowLevel:windowShim_->panel()->IsMinimized()];
|
| -}
|
| -
|
| -- (void)updateWindowLevel:(BOOL)panelIsMinimized {
|
| - if (![self isWindowLoaded])
|
| - return;
|
| - Panel* panel = windowShim_->panel();
|
| - if (!panel->IsAlwaysOnTop()) {
|
| - [[self window] setLevel:NSNormalWindowLevel];
|
| - return;
|
| - }
|
| - // If we simply use NSStatusWindowLevel (25) for all docked panel windows,
|
| - // IME composition windows for things like CJK languages appear behind panels.
|
| - // Pre 10.7, IME composition windows have a window level of 19, which is
|
| - // lower than the dock at level 20. Since we want panels to appear on top of
|
| - // the dock, it is impossible to enforce an ordering where IME > panel > dock,
|
| - // since IME < dock.
|
| - // On 10.7, IME composition windows and the dock both live at level 20, so we
|
| - // use the same window level for panels. Since newly created windows appear at
|
| - // the top of their window level, panels are typically on top of the dock, and
|
| - // the IME composition window correctly draws over the panel.
|
| - // An autohide dock causes problems though: since it's constantly being
|
| - // revealed, it ends up drawing on top of other windows at the same level.
|
| - // While this is OK for expanded panels, it makes minimized panels impossible
|
| - // to activate. As a result, we still use NSStatusWindowLevel for minimized
|
| - // panels, since it's impossible to compose IME text in them anyway.
|
| - if (panelIsMinimized) {
|
| - [[self window] setLevel:NSStatusWindowLevel];
|
| - return;
|
| - }
|
| - [[self window] setLevel:NSDockWindowLevel];
|
| -}
|
| -
|
| -- (void)updateWindowCollectionBehavior {
|
| - if (![self isWindowLoaded])
|
| - return;
|
| - NSWindowCollectionBehavior collectionBehavior =
|
| - NSWindowCollectionBehaviorParticipatesInCycle;
|
| - if (windowShim_->panel()->IsAlwaysOnTop())
|
| - collectionBehavior |= NSWindowCollectionBehaviorCanJoinAllSpaces;
|
| - [[self window] setCollectionBehavior:collectionBehavior];
|
| -}
|
| -
|
| -- (void)updateTrackingArea {
|
| - NSView* superview = [[[self window] contentView] superview];
|
| -
|
| - if (trackingArea_.get())
|
| - [superview removeTrackingArea:trackingArea_.get()];
|
| -
|
| - trackingArea_.reset(
|
| - [[CrTrackingArea alloc] initWithRect:[superview bounds]
|
| - options:NSTrackingInVisibleRect |
|
| - NSTrackingMouseMoved |
|
| - NSTrackingActiveInKeyWindow
|
| - owner:superview
|
| - userInfo:nil]);
|
| - [superview addTrackingArea:trackingArea_.get()];
|
| -}
|
| -
|
| -- (void)showShadow:(BOOL)show {
|
| - if (![self isWindowLoaded])
|
| - return;
|
| - [[self window] setHasShadow:show];
|
| -}
|
| -
|
| -- (void)miniaturize {
|
| - [[self window] miniaturize:nil];
|
| -}
|
| -
|
| -- (BOOL)isMiniaturized {
|
| - return [[self window] isMiniaturized];
|
| -}
|
| -
|
| -- (BOOL)canResizeByMouseAtCurrentLocation {
|
| - panel::Resizability resizability = windowShim_->panel()->CanResizeByMouse();
|
| - NSRect frame = [[self window] frame];
|
| - NSPoint point = [NSEvent mouseLocation];
|
| -
|
| - if (point.y < NSMinY(frame) + kWidthOfMouseResizeArea) {
|
| - if (point.x < NSMinX(frame) + kWidthOfMouseResizeArea &&
|
| - (resizability & panel::RESIZABLE_BOTTOM_LEFT) == 0) {
|
| - return NO;
|
| - }
|
| - if (point.x > NSMaxX(frame) - kWidthOfMouseResizeArea &&
|
| - (resizability & panel::RESIZABLE_BOTTOM_RIGHT) == 0) {
|
| - return NO;
|
| - }
|
| - if ((resizability & panel::RESIZABLE_BOTTOM) == 0)
|
| - return NO;
|
| - } else if (point.y > NSMaxY(frame) - kWidthOfMouseResizeArea) {
|
| - if (point.x < NSMinX(frame) + kWidthOfMouseResizeArea &&
|
| - (resizability & panel::RESIZABLE_TOP_LEFT) == 0) {
|
| - return NO;
|
| - }
|
| - if (point.x > NSMaxX(frame) - kWidthOfMouseResizeArea &&
|
| - (resizability & panel::RESIZABLE_TOP_RIGHT) == 0) {
|
| - return NO;
|
| - }
|
| - if ((resizability & panel::RESIZABLE_TOP) == 0)
|
| - return NO;
|
| - } else {
|
| - if (point.x < NSMinX(frame) + kWidthOfMouseResizeArea &&
|
| - (resizability & panel::RESIZABLE_LEFT) == 0) {
|
| - return NO;
|
| - }
|
| - if (point.x > NSMaxX(frame) - kWidthOfMouseResizeArea &&
|
| - (resizability & panel::RESIZABLE_RIGHT) == 0) {
|
| - return NO;
|
| - }
|
| - }
|
| - return YES;
|
| -}
|
| -
|
| -// We have custom implementation of these because our titlebar height is custom
|
| -// and does not match the standard one.
|
| -- (NSRect)frameRectForContentRect:(NSRect)contentRect {
|
| - // contentRect is in contentView coord system. We should add a titlebar on top
|
| - // and then convert to the windows coord system.
|
| - contentRect.size.height += panel::kTitlebarHeight;
|
| - NSRect frameRect = [[[self window] contentView] convertRect:contentRect
|
| - toView:nil];
|
| - return frameRect;
|
| -}
|
| -
|
| -- (NSRect)contentRectForFrameRect:(NSRect)frameRect {
|
| - NSRect contentRect = [[[self window] contentView] convertRect:frameRect
|
| - fromView:nil];
|
| - contentRect.size.height -= panel::kTitlebarHeight;
|
| - if (contentRect.size.height < 0)
|
| - contentRect.size.height = 0;
|
| - return contentRect;
|
| -}
|
| -
|
| -@end
|
| -
|
| -@implementation WindowCloseWatcher
|
| -
|
| -- (id)init {
|
| - if ((self = [super init])) {
|
| - [[NSNotificationCenter defaultCenter]
|
| - addObserver:self
|
| - selector:@selector(windowWillClose:)
|
| - name:NSWindowWillCloseNotification
|
| - object:nil];
|
| - }
|
| - return self;
|
| -}
|
| -
|
| -- (void)windowWillClose:(NSNotification*)notification {
|
| - // If it looks like a panel may (refuse to) become key after this window is
|
| - // closed, then explicitly set the topmost browser window on the active space
|
| - // to be key (if there is one). Otherwise AppKit will stop looking for windows
|
| - // to make key once it encounters the panel.
|
| - id closingWindow = [notification object];
|
| - BOOL orderNext = NO;
|
| - for (NSWindow* window : [NSApp orderedWindows]) {
|
| - if ([window isEqual:closingWindow] || ![window isOnActiveSpace])
|
| - continue;
|
| -
|
| - if ([window isKindOfClass:[PanelWindowCocoaImpl class]] &&
|
| - ![window canBecomeKeyWindow]) {
|
| - orderNext = YES;
|
| - continue;
|
| - }
|
| -
|
| - if (orderNext) {
|
| - if (![window canBecomeKeyWindow])
|
| - continue;
|
| -
|
| - [window makeKeyWindow];
|
| - }
|
| - return;
|
| - }
|
| -}
|
| -
|
| -@end
|
|
|