| OLD | NEW |
| (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 #import "chrome/browser/ui/cocoa/panels/panel_titlebar_view_cocoa.h" | |
| 6 | |
| 7 #import <Cocoa/Cocoa.h> | |
| 8 | |
| 9 #include "base/logging.h" | |
| 10 #include "base/mac/scoped_nsautorelease_pool.h" | |
| 11 #import "chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.h" | |
| 12 #import "chrome/browser/ui/panels/panel_constants.h" | |
| 13 #include "chrome/grit/generated_resources.h" | |
| 14 #include "grit/theme_resources.h" | |
| 15 #include "ui/base/cocoa/cocoa_base_utils.h" | |
| 16 #import "ui/base/cocoa/hover_image_button.h" | |
| 17 #import "ui/base/cocoa/nsview_additions.h" | |
| 18 #include "ui/base/l10n/l10n_util_mac.h" | |
| 19 #include "ui/base/resource/resource_bundle.h" | |
| 20 #include "ui/gfx/image/image.h" | |
| 21 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" | |
| 22 | |
| 23 // 'Glint' is a glowing light animation on the titlebar to attract user's | |
| 24 // attention. Numbers are arbitrary, based on several tries. | |
| 25 const double kGlintAnimationDuration = 1.5; | |
| 26 const double kGlintRepeatIntervalSeconds = 1.0; | |
| 27 const int kNumberOfGlintRepeats = 4; // 5 total, including initial flash. | |
| 28 | |
| 29 // Used to implement TestingAPI | |
| 30 static NSEvent* MakeMouseEvent(NSEventType type, | |
| 31 NSPoint point, | |
| 32 int modifierFlags, | |
| 33 int clickCount) { | |
| 34 return [NSEvent mouseEventWithType:type | |
| 35 location:point | |
| 36 modifierFlags:modifierFlags | |
| 37 timestamp:0 | |
| 38 windowNumber:0 | |
| 39 context:nil | |
| 40 eventNumber:0 | |
| 41 clickCount:clickCount | |
| 42 pressure:0.0]; | |
| 43 } | |
| 44 | |
| 45 // Test drag controller - does not contain a nested message loop, directly | |
| 46 // invokes the dragStarted/dragProgress instead. | |
| 47 @interface TestDragController : MouseDragController { | |
| 48 @private | |
| 49 BOOL dragStarted_; | |
| 50 } | |
| 51 - (void)mouseDragged:(NSEvent*)event; | |
| 52 @end | |
| 53 | |
| 54 @implementation TestDragController | |
| 55 // Bypass nested message loop for tests. There is no need to check for | |
| 56 // threshold here as the base class does because tests only simulate a single | |
| 57 // 'mouse drag' to the destination point. | |
| 58 - (void)mouseDragged:(NSEvent*)event { | |
| 59 if (!dragStarted_) { | |
| 60 [[self client] dragStarted:[self initialMouseLocation]]; | |
| 61 dragStarted_ = YES; | |
| 62 } | |
| 63 | |
| 64 [[self client] dragProgress:[event locationInWindow]]; | |
| 65 } | |
| 66 @end | |
| 67 | |
| 68 @implementation PanelTitlebarOverlayView | |
| 69 // Sometimes we do not want to bring chrome window to foreground when we click | |
| 70 // on any part of the titlebar. To do this, we first postpone the window | |
| 71 // reorder here (shouldDelayWindowOrderingForEvent is called during when mouse | |
| 72 // button is pressed but before mouseDown: is dispatched) and then complete | |
| 73 // canceling the reorder by [NSApp preventWindowOrdering] in mouseDown handler | |
| 74 // of this view. | |
| 75 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent*)theEvent { | |
| 76 disableReordering_ = ![controller_ canBecomeKeyWindow]; | |
| 77 return disableReordering_; | |
| 78 } | |
| 79 | |
| 80 - (void)mouseDown:(NSEvent*)event { | |
| 81 if (disableReordering_) | |
| 82 [NSApp preventWindowOrdering]; | |
| 83 disableReordering_ = NO; | |
| 84 // Continue bubbling the event up the chain of responders. | |
| 85 [super mouseDown:event]; | |
| 86 } | |
| 87 | |
| 88 - (BOOL)acceptsFirstMouse:(NSEvent*)event { | |
| 89 return YES; | |
| 90 } | |
| 91 @end | |
| 92 | |
| 93 @implementation RepaintAnimation | |
| 94 - (id)initWithView:(NSView*)targetView duration:(double) duration { | |
| 95 if ((self = [super initWithDuration:duration | |
| 96 animationCurve:NSAnimationEaseInOut])) { | |
| 97 [self setAnimationBlockingMode:NSAnimationNonblocking]; | |
| 98 targetView_ = targetView; | |
| 99 } | |
| 100 return self; | |
| 101 } | |
| 102 | |
| 103 - (void)setCurrentProgress:(NSAnimationProgress)progress { | |
| 104 [super setCurrentProgress:progress]; | |
| 105 [targetView_ setNeedsDisplay:YES]; | |
| 106 } | |
| 107 @end | |
| 108 | |
| 109 | |
| 110 @implementation PanelTitlebarViewCocoa | |
| 111 | |
| 112 - (id)initWithFrame:(NSRect)frame { | |
| 113 if ((self = [super initWithFrame:frame])) | |
| 114 dragController_.reset([[MouseDragController alloc] initWithClient:self]); | |
| 115 return self; | |
| 116 } | |
| 117 | |
| 118 - (void)dealloc { | |
| 119 [[NSNotificationCenter defaultCenter] removeObserver:self]; | |
| 120 [self stopGlintAnimation]; | |
| 121 [super dealloc]; | |
| 122 } | |
| 123 | |
| 124 - (void)onCloseButtonClick:(id)sender { | |
| 125 [controller_ closePanel]; | |
| 126 } | |
| 127 | |
| 128 - (void)onMinimizeButtonClick:(id)sender { | |
| 129 [controller_ minimizeButtonClicked:[[NSApp currentEvent] modifierFlags]]; | |
| 130 } | |
| 131 | |
| 132 - (void)onRestoreButtonClick:(id)sender { | |
| 133 [controller_ restoreButtonClicked:[[NSApp currentEvent] modifierFlags]]; | |
| 134 } | |
| 135 | |
| 136 - (void)drawRect:(NSRect)rect { | |
| 137 if (isDrawingAttention_) { | |
| 138 NSColor* attentionColor = [NSColor colorWithCalibratedRed:0x53/255.0 | |
| 139 green:0xa9/255.0 | |
| 140 blue:0x3f/255.0 | |
| 141 alpha:1.0]; | |
| 142 [attentionColor set]; | |
| 143 NSRectFillUsingOperation([self bounds], NSCompositeSourceOver); | |
| 144 | |
| 145 if ([glintAnimation_ isAnimating]) { | |
| 146 base::scoped_nsobject<NSGradient> glint([NSGradient alloc]); | |
| 147 float currentAlpha = 0.8 * [glintAnimation_ currentValue]; | |
| 148 NSColor* startColor = [NSColor colorWithCalibratedWhite:1.0 | |
| 149 alpha:currentAlpha]; | |
| 150 NSColor* endColor = [NSColor colorWithCalibratedWhite:1.0 | |
| 151 alpha:0.0]; | |
| 152 [glint initWithColorsAndLocations: | |
| 153 startColor, 0.0, startColor, 0.3, endColor, 1.0, nil]; | |
| 154 NSRect bounds = [self bounds]; | |
| 155 [glint drawInRect:bounds relativeCenterPosition:NSZeroPoint]; | |
| 156 } | |
| 157 } else { | |
| 158 BOOL isActive = [[self window] isMainWindow]; | |
| 159 | |
| 160 // If titlebar is close to minimized state or is at minimized state and only | |
| 161 // shows a few pixels, change the color to something light and add border. | |
| 162 NSRect windowFrame = [[self window] frame]; | |
| 163 if (NSHeight(windowFrame) < 8) { | |
| 164 NSColor* lightBackgroundColor = | |
| 165 [NSColor colorWithCalibratedRed:0xf5/255.0 | |
| 166 green:0xf4/255.0 | |
| 167 blue:0xf0/255.0 | |
| 168 alpha:1.0]; | |
| 169 [lightBackgroundColor set]; | |
| 170 NSRectFill([self bounds]); | |
| 171 | |
| 172 NSColor* borderColor = | |
| 173 [NSColor colorWithCalibratedRed:0xc9/255.0 | |
| 174 green:0xc9/255.0 | |
| 175 blue:0xc9/255.0 | |
| 176 alpha:1.0]; | |
| 177 [borderColor set]; | |
| 178 NSFrameRect([self bounds]); | |
| 179 } else { | |
| 180 // use solid black-ish colors. | |
| 181 NSColor* backgroundColor = isActive ? | |
| 182 [NSColor colorWithCalibratedRed:0x3a/255.0 | |
| 183 green:0x3d/255.0 | |
| 184 blue:0x3d/255.0 | |
| 185 alpha:1.0] : | |
| 186 [NSColor colorWithCalibratedRed:0x7a/255.0 | |
| 187 green:0x7c/255.0 | |
| 188 blue:0x7c/255.0 | |
| 189 alpha:1.0]; | |
| 190 | |
| 191 [backgroundColor set]; | |
| 192 NSRectFill([self bounds]); | |
| 193 } | |
| 194 } | |
| 195 | |
| 196 NSColor* titleColor = [NSColor colorWithCalibratedRed:0xf9/255.0 | |
| 197 green:0xf9/255.0 | |
| 198 blue:0xf9/255.0 | |
| 199 alpha:1.0]; | |
| 200 [title_ setTextColor:titleColor]; | |
| 201 } | |
| 202 | |
| 203 - (void)attach { | |
| 204 // Interface Builder can not put a view as a sibling of contentView, | |
| 205 // so need to do it here. Placing ourself as the last child of the | |
| 206 // internal view allows us to draw on top of the titlebar. | |
| 207 // Note we must use [controller_ window] here since we have not been added | |
| 208 // to the view hierarchy yet. | |
| 209 NSView* contentView = [[controller_ window] contentView]; | |
| 210 NSView* rootView = [contentView superview]; | |
| 211 [rootView addSubview:self]; | |
| 212 | |
| 213 // Figure out the rectangle of the titlebar and set us on top of it. | |
| 214 // The titlebar covers window's root view where not covered by contentView. | |
| 215 // Compute the titlebar frame in coordinate system of the window's root view. | |
| 216 // NSWindow | |
| 217 // | | |
| 218 // ___root_view____ | |
| 219 // | | | |
| 220 // contentView titlebar | |
| 221 NSSize titlebarSize = NSMakeSize(0, panel::kTitlebarHeight); | |
| 222 titlebarSize = [contentView convertSize:titlebarSize toView:rootView]; | |
| 223 NSRect rootViewBounds = [[self superview] bounds]; | |
| 224 NSRect titlebarFrame = | |
| 225 NSMakeRect(NSMinX(rootViewBounds), | |
| 226 NSMaxY(rootViewBounds) - titlebarSize.height, | |
| 227 NSWidth(rootViewBounds), | |
| 228 titlebarSize.height); | |
| 229 [self setFrame:titlebarFrame]; | |
| 230 | |
| 231 [title_ setFont:[[NSFontManager sharedFontManager] | |
| 232 fontWithFamily:@"Arial" | |
| 233 traits:NSBoldFontMask | |
| 234 weight:0 | |
| 235 size:14.0]]; | |
| 236 [title_ setDrawsBackground:NO]; | |
| 237 | |
| 238 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 239 | |
| 240 [self initializeImageButton:customCloseButton_ | |
| 241 image:rb.GetNativeImageNamed(IDR_PANEL_CLOSE).ToNSImage() | |
| 242 hoverImage:rb.GetNativeImageNamed(IDR_PANEL_CLOSE_H).ToNSImage() | |
| 243 pressedImage:rb.GetNativeImageNamed(IDR_PANEL_CLOSE_C).ToNSImage() | |
| 244 toolTip:l10n_util::GetNSStringWithFixup(IDS_PANEL_CLOSE_TOOLTIP)]; | |
| 245 | |
| 246 // Iniitalize the minimize and restore buttons. | |
| 247 [self initializeImageButton:minimizeButton_ | |
| 248 image:rb.GetNativeImageNamed(IDR_PANEL_MINIMIZE).ToNSImage() | |
| 249 hoverImage:rb.GetNativeImageNamed(IDR_PANEL_MINIMIZE_H).ToNSImage() | |
| 250 pressedImage:rb.GetNativeImageNamed(IDR_PANEL_MINIMIZE_C).ToNSImage() | |
| 251 toolTip:l10n_util::GetNSStringWithFixup(IDS_PANEL_MINIMIZE_TOOLTIP)]; | |
| 252 | |
| 253 [self initializeImageButton:restoreButton_ | |
| 254 image:rb.GetNativeImageNamed(IDR_PANEL_RESTORE).ToNSImage() | |
| 255 hoverImage:rb.GetNativeImageNamed(IDR_PANEL_RESTORE_H).ToNSImage() | |
| 256 pressedImage:rb.GetNativeImageNamed(IDR_PANEL_RESTORE_C).ToNSImage() | |
| 257 toolTip:l10n_util::GetNSStringWithFixup(IDS_PANEL_RESTORE_TOOLTIP)]; | |
| 258 | |
| 259 [controller_ updateTitleBarMinimizeRestoreButtonVisibility]; | |
| 260 | |
| 261 [self updateCustomButtonsLayout]; | |
| 262 | |
| 263 // Set autoresizing behavior: glued to edges on left, top and right. | |
| 264 [self setAutoresizingMask:(NSViewMinYMargin | NSViewWidthSizable)]; | |
| 265 | |
| 266 [[NSNotificationCenter defaultCenter] | |
| 267 addObserver:self | |
| 268 selector:@selector(didChangeFrame:) | |
| 269 name:NSViewFrameDidChangeNotification | |
| 270 object:self]; | |
| 271 [[NSNotificationCenter defaultCenter] | |
| 272 addObserver:self | |
| 273 selector:@selector(didChangeMainWindow:) | |
| 274 name:NSWindowDidBecomeMainNotification | |
| 275 object:[self window]]; | |
| 276 [[NSNotificationCenter defaultCenter] | |
| 277 addObserver:self | |
| 278 selector:@selector(didChangeMainWindow:) | |
| 279 name:NSWindowDidResignMainNotification | |
| 280 object:[self window]]; | |
| 281 } | |
| 282 | |
| 283 - (void)initializeImageButton:(HoverImageButton*)button | |
| 284 image:(NSImage*)image | |
| 285 hoverImage:(NSImage*)hoverImage | |
| 286 pressedImage:(NSImage*)pressedImage | |
| 287 toolTip:(NSString*)toolTip { | |
| 288 [button setDefaultImage:image]; | |
| 289 [button setHoverImage:hoverImage]; | |
| 290 [button setPressedImage:pressedImage]; | |
| 291 [button setToolTip:toolTip]; | |
| 292 [[button cell] setHighlightsBy:NSNoCellMask]; | |
| 293 } | |
| 294 | |
| 295 - (void)setTitle:(NSString*)newTitle { | |
| 296 [title_ setStringValue:newTitle]; | |
| 297 [self updateIconAndTitleLayout]; | |
| 298 } | |
| 299 | |
| 300 - (void)setIcon:(NSView*)newIcon { | |
| 301 [icon_ removeFromSuperview]; | |
| 302 icon_ = newIcon; | |
| 303 if (icon_) { | |
| 304 [self addSubview:icon_ positioned:NSWindowBelow relativeTo:overlay_]; | |
| 305 [icon_ setWantsLayer:YES]; | |
| 306 } | |
| 307 [self updateIconAndTitleLayout]; | |
| 308 } | |
| 309 | |
| 310 - (NSView*)icon { | |
| 311 return icon_; | |
| 312 } | |
| 313 | |
| 314 - (void)setMinimizeButtonVisibility:(BOOL)visible { | |
| 315 [minimizeButton_ setHidden:!visible]; | |
| 316 } | |
| 317 | |
| 318 - (void)setRestoreButtonVisibility:(BOOL)visible { | |
| 319 [restoreButton_ setHidden:!visible]; | |
| 320 } | |
| 321 | |
| 322 - (void)updateCustomButtonsLayout { | |
| 323 NSRect bounds = [self bounds]; | |
| 324 NSRect closeButtonFrame = [customCloseButton_ frame]; | |
| 325 closeButtonFrame.size.width = panel::kPanelButtonSize; | |
| 326 closeButtonFrame.size.height = panel::kPanelButtonSize; | |
| 327 closeButtonFrame.origin.x = | |
| 328 NSWidth(bounds) - NSWidth(closeButtonFrame) - panel::kButtonPadding; | |
| 329 closeButtonFrame.origin.y = | |
| 330 (NSHeight(bounds) - NSHeight(closeButtonFrame)) / 2; | |
| 331 [customCloseButton_ setFrame:closeButtonFrame]; | |
| 332 | |
| 333 NSRect buttonFrame = [minimizeButton_ frame]; | |
| 334 buttonFrame.size.width = panel::kPanelButtonSize; | |
| 335 buttonFrame.size.height = panel::kPanelButtonSize; | |
| 336 buttonFrame.origin.x = | |
| 337 closeButtonFrame.origin.x - NSWidth(buttonFrame) - panel::kButtonPadding; | |
| 338 buttonFrame.origin.y = (NSHeight(bounds) - NSHeight(buttonFrame)) / 2; | |
| 339 [minimizeButton_ setFrame:buttonFrame]; | |
| 340 [restoreButton_ setFrame:buttonFrame]; | |
| 341 } | |
| 342 | |
| 343 - (void)updateIconAndTitleLayout { | |
| 344 NSRect iconFrame = [icon_ frame]; | |
| 345 // NSTextField for title_ is set to Layout:Truncate, LineBreaks:TruncateTail | |
| 346 // in Interface Builder so it is sized in a single-line mode. | |
| 347 [title_ sizeToFit]; | |
| 348 NSRect titleFrame = [title_ frame]; | |
| 349 // Only one of minimize/restore button is visible at a time so just allow for | |
| 350 // the width of one of them. | |
| 351 NSRect minimizeRestoreButtonFrame = [minimizeButton_ frame]; | |
| 352 NSRect bounds = [self bounds]; | |
| 353 | |
| 354 // Place the icon and title at the left edge of the titlebar. | |
| 355 int iconWidth = NSWidth(iconFrame); | |
| 356 int titleWidth = NSWidth(titleFrame); | |
| 357 int availableWidth = minimizeRestoreButtonFrame.origin.x - | |
| 358 panel::kTitleAndButtonPadding; | |
| 359 | |
| 360 int paddings = panel::kTitlebarLeftPadding + panel::kIconAndTitlePadding; | |
| 361 if (paddings + iconWidth + titleWidth > availableWidth) | |
| 362 titleWidth = availableWidth - iconWidth - paddings; | |
| 363 if (titleWidth < 0) | |
| 364 titleWidth = 0; | |
| 365 | |
| 366 iconFrame.origin.x = panel::kTitlebarLeftPadding; | |
| 367 iconFrame.origin.y = (NSHeight(bounds) - NSHeight(iconFrame)) / 2; | |
| 368 [icon_ setFrame:iconFrame]; | |
| 369 | |
| 370 titleFrame.origin.x = paddings + iconWidth; | |
| 371 // In bottom-heavy text labels, let's compensate for occasional integer | |
| 372 // rounding to avoid text label to feel too low. | |
| 373 titleFrame.origin.y = (NSHeight(bounds) - NSHeight(titleFrame)) / 2 + 2; | |
| 374 titleFrame.size.width = titleWidth; | |
| 375 [title_ setFrame:titleFrame]; | |
| 376 } | |
| 377 | |
| 378 // PanelManager controls size/position of the window. | |
| 379 - (BOOL)mouseDownCanMoveWindow { | |
| 380 return NO; | |
| 381 } | |
| 382 | |
| 383 - (BOOL)acceptsFirstMouse:(NSEvent*)event { | |
| 384 return YES; | |
| 385 } | |
| 386 | |
| 387 - (void)didChangeFrame:(NSNotification*)notification { | |
| 388 // Update buttons first because title layout depends on buttons layout. | |
| 389 [self updateCustomButtonsLayout]; | |
| 390 [self updateIconAndTitleLayout]; | |
| 391 } | |
| 392 | |
| 393 - (void)didChangeMainWindow:(NSNotification*)notification { | |
| 394 [self setNeedsDisplay:YES]; | |
| 395 } | |
| 396 | |
| 397 - (void)mouseDown:(NSEvent*)event { | |
| 398 [dragController_ mouseDown:event]; | |
| 399 } | |
| 400 | |
| 401 - (void)mouseUp:(NSEvent*)event { | |
| 402 [dragController_ mouseUp:event]; | |
| 403 | |
| 404 if ([event clickCount] == 1) | |
| 405 [controller_ onTitlebarMouseClicked:[event modifierFlags]]; | |
| 406 else if ([event clickCount] == 2) | |
| 407 [controller_ onTitlebarDoubleClicked:[event modifierFlags]]; | |
| 408 } | |
| 409 | |
| 410 - (void)mouseDragged:(NSEvent*)event { | |
| 411 [dragController_ mouseDragged:event]; | |
| 412 } | |
| 413 | |
| 414 // MouseDragControllerClient implementaiton | |
| 415 | |
| 416 - (void)prepareForDrag { | |
| 417 } | |
| 418 | |
| 419 - (void)dragStarted:(NSPoint)initialMouseLocation { | |
| 420 NSPoint initialMouseLocationScreen = ui::ConvertPointFromWindowToScreen( | |
| 421 [self window], initialMouseLocation); | |
| 422 [controller_ startDrag:initialMouseLocationScreen]; | |
| 423 } | |
| 424 | |
| 425 - (void)dragEnded:(BOOL)cancelled { | |
| 426 [controller_ endDrag:cancelled]; | |
| 427 } | |
| 428 | |
| 429 - (void)dragProgress:(NSPoint)mouseLocation { | |
| 430 NSPoint mouseLocationScreen = ui::ConvertPointFromWindowToScreen( | |
| 431 [self window], mouseLocation); | |
| 432 [controller_ drag:mouseLocationScreen]; | |
| 433 } | |
| 434 | |
| 435 - (void)cleanupAfterDrag { | |
| 436 } | |
| 437 | |
| 438 // End of MouseDragControllerClient implementaiton | |
| 439 | |
| 440 - (void)drawAttention { | |
| 441 if (isDrawingAttention_) | |
| 442 return; | |
| 443 isDrawingAttention_ = YES; | |
| 444 | |
| 445 [self startGlintAnimation]; | |
| 446 } | |
| 447 | |
| 448 - (void)stopDrawingAttention { | |
| 449 if (!isDrawingAttention_) | |
| 450 return; | |
| 451 isDrawingAttention_ = NO; | |
| 452 | |
| 453 [self stopGlintAnimation]; | |
| 454 [self setNeedsDisplay:YES]; | |
| 455 } | |
| 456 | |
| 457 - (BOOL)isDrawingAttention { | |
| 458 return isDrawingAttention_; | |
| 459 } | |
| 460 | |
| 461 - (void)startGlintAnimation { | |
| 462 glintCounter_ = 0; | |
| 463 [self restartGlintAnimation:nil]; | |
| 464 } | |
| 465 | |
| 466 - (void)stopGlintAnimation { | |
| 467 if (glintAnimationTimer_.get()) { | |
| 468 [glintAnimationTimer_ invalidate]; | |
| 469 glintAnimationTimer_.reset(); | |
| 470 } | |
| 471 if ([glintAnimation_ isAnimating]) | |
| 472 [glintAnimation_ stopAnimation]; | |
| 473 } | |
| 474 | |
| 475 - (void)restartGlintAnimation:(NSTimer*)timer { | |
| 476 if (!glintAnimation_.get()) { | |
| 477 glintAnimation_.reset( | |
| 478 [[RepaintAnimation alloc] initWithView:self | |
| 479 duration:kGlintAnimationDuration]); | |
| 480 [glintAnimation_ setDelegate:self]; | |
| 481 } | |
| 482 [glintAnimation_ startAnimation]; | |
| 483 } | |
| 484 | |
| 485 // NSAnimationDelegate method. | |
| 486 - (void)animationDidEnd:(NSAnimation*)animation { | |
| 487 if (animation != glintAnimation_.get()) | |
| 488 return; | |
| 489 if (glintCounter_ >= kNumberOfGlintRepeats) | |
| 490 return; | |
| 491 glintCounter_++; | |
| 492 // Restart after a timeout. | |
| 493 glintAnimationTimer_.reset([[NSTimer | |
| 494 scheduledTimerWithTimeInterval:kGlintRepeatIntervalSeconds | |
| 495 target:self | |
| 496 selector:@selector(restartGlintAnimation:) | |
| 497 userInfo:nil | |
| 498 repeats:NO] retain]); | |
| 499 } | |
| 500 | |
| 501 // NSAnimationDelegate method. | |
| 502 - (float)animation:(NSAnimation *)animation | |
| 503 valueForProgress:(NSAnimationProgress)progress { | |
| 504 if (animation != glintAnimation_.get()) | |
| 505 return progress; | |
| 506 | |
| 507 // Converts 0..1 progression into a sharper raise/fall. | |
| 508 float result = progress < 0.5 ? progress : 1.0 - progress; | |
| 509 result = 4.0 * result * result; | |
| 510 return result; | |
| 511 } | |
| 512 | |
| 513 // (Private/TestingAPI) | |
| 514 - (PanelWindowControllerCocoa*)controller { | |
| 515 return controller_; | |
| 516 } | |
| 517 | |
| 518 - (NSTextField*)title { | |
| 519 return title_; | |
| 520 } | |
| 521 | |
| 522 - (void)simulateCloseButtonClick { | |
| 523 [[customCloseButton_ cell] performClick:customCloseButton_]; | |
| 524 } | |
| 525 | |
| 526 - (void)pressLeftMouseButtonTitlebar:(NSPoint)mouseLocation | |
| 527 modifiers:(int)modifierFlags { | |
| 528 // Override the drag controller. It's ok to create a new one for each drag. | |
| 529 dragController_.reset([[TestDragController alloc] initWithClient:self]); | |
| 530 // Convert from Cocoa's screen coordinates to base coordinates since the mouse | |
| 531 // event takes base (NSWindow) coordinates. | |
| 532 NSPoint mouseLocationWindow = ui::ConvertPointFromScreenToWindow( | |
| 533 [self window], mouseLocation); | |
| 534 NSEvent* event = MakeMouseEvent(NSLeftMouseDown, mouseLocationWindow, | |
| 535 modifierFlags, 0); | |
| 536 [self mouseDown:event]; | |
| 537 } | |
| 538 | |
| 539 - (void)releaseLeftMouseButtonTitlebar:(int)modifierFlags { | |
| 540 NSEvent* event = MakeMouseEvent(NSLeftMouseUp, NSZeroPoint, modifierFlags, 1); | |
| 541 [self mouseUp:event]; | |
| 542 } | |
| 543 | |
| 544 - (void)dragTitlebar:(NSPoint)mouseLocation { | |
| 545 // Convert from Cocoa's screen coordinates to base coordinates since the mouse | |
| 546 // event takes base (NSWindow) coordinates. | |
| 547 NSPoint mouseLocationWindow = ui::ConvertPointFromScreenToWindow( | |
| 548 [self window], mouseLocation); | |
| 549 NSEvent* event = | |
| 550 MakeMouseEvent(NSLeftMouseDragged, mouseLocationWindow, 0, 0); | |
| 551 [self mouseDragged:event]; | |
| 552 } | |
| 553 | |
| 554 - (void)cancelDragTitlebar { | |
| 555 [self dragEnded:YES]; | |
| 556 } | |
| 557 | |
| 558 - (void)finishDragTitlebar { | |
| 559 [self dragEnded:NO]; | |
| 560 } | |
| 561 | |
| 562 - (NSButton*)closeButton { | |
| 563 return closeButton_; | |
| 564 } | |
| 565 | |
| 566 - (NSButton*)minimizeButton { | |
| 567 return minimizeButton_; | |
| 568 } | |
| 569 | |
| 570 - (NSButton*)restoreButton { | |
| 571 return restoreButton_; | |
| 572 } | |
| 573 | |
| 574 @end | |
| 575 | |
| OLD | NEW |