| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "chrome/browser/ui/cocoa/profiles/profile_chooser_controller.h" | 5 #import "chrome/browser/ui/cocoa/profiles/profile_chooser_controller.h" |
| 6 | 6 |
| 7 #import <Carbon/Carbon.h> // kVK_Return. | 7 #import <Carbon/Carbon.h> // kVK_Return. |
| 8 #import <Cocoa/Cocoa.h> | 8 #import <Cocoa/Cocoa.h> |
| 9 #include <stddef.h> | 9 #include <stddef.h> |
| 10 | 10 |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 86 #include "ui/gfx/paint_vector_icon.h" | 86 #include "ui/gfx/paint_vector_icon.h" |
| 87 #include "ui/gfx/text_elider.h" | 87 #include "ui/gfx/text_elider.h" |
| 88 #include "ui/native_theme/common_theme.h" | 88 #include "ui/native_theme/common_theme.h" |
| 89 #include "ui/native_theme/native_theme.h" | 89 #include "ui/native_theme/native_theme.h" |
| 90 #include "ui/vector_icons/vector_icons.h" | 90 #include "ui/vector_icons/vector_icons.h" |
| 91 | 91 |
| 92 namespace { | 92 namespace { |
| 93 | 93 |
| 94 // Constants taken from the Windows/Views implementation at: | 94 // Constants taken from the Windows/Views implementation at: |
| 95 // chrome/browser/ui/views/profile_chooser_view.cc | 95 // chrome/browser/ui/views/profile_chooser_view.cc |
| 96 const int kLargeImageSide = 88; | |
| 97 const int kMdImageSide = 40; | 96 const int kMdImageSide = 40; |
| 98 | 97 |
| 99 const CGFloat kFixedMenuWidth = 240.0; | 98 const CGFloat kFixedMenuWidth = 240.0; |
| 100 const int kIconImageSide = 18; | 99 const int kIconImageSide = 18; |
| 101 const CGFloat kVerticalSpacing = 16.0; | 100 const CGFloat kVerticalSpacing = 16.0; |
| 102 const CGFloat kSmallVerticalSpacing = 10.0; | 101 const CGFloat kSmallVerticalSpacing = 10.0; |
| 103 const CGFloat kRelatedControllVerticalSpacing = 8.0; | 102 const CGFloat kRelatedControllVerticalSpacing = 8.0; |
| 104 const CGFloat kHorizontalSpacing = 16.0; | 103 const CGFloat kHorizontalSpacing = 16.0; |
| 105 const CGFloat kTitleFontSize = 15.0; | 104 const CGFloat kTitleFontSize = 15.0; |
| 106 const CGFloat kTextFontSize = 12.0; | 105 const CGFloat kTextFontSize = 12.0; |
| 107 const CGFloat kProfileButtonHeight = 30; | 106 const CGFloat kProfileButtonHeight = 30; |
| 108 const int kBezelThickness = 3; // Width of the bezel on an NSButton. | |
| 109 const int kBlueButtonHeight = 30; | 107 const int kBlueButtonHeight = 30; |
| 110 const CGFloat kFocusRingLineWidth = 2; | 108 const CGFloat kFocusRingLineWidth = 2; |
| 111 | 109 |
| 112 // Fixed size for embedded sign in pages as defined in Gaia. | 110 // Fixed size for embedded sign in pages as defined in Gaia. |
| 113 const CGFloat kFixedGaiaViewWidth = 360; | 111 const CGFloat kFixedGaiaViewWidth = 360; |
| 114 | 112 |
| 115 // Fixed size for the account removal view. | 113 // Fixed size for the account removal view. |
| 116 const CGFloat kFixedAccountRemovalViewWidth = 280; | 114 const CGFloat kFixedAccountRemovalViewWidth = 280; |
| 117 | 115 |
| 118 // Fixed size for the switch user view. | 116 // Fixed size for the switch user view. |
| (...skipping 407 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 526 | 524 |
| 527 @implementation CustomCircleImageCell | 525 @implementation CustomCircleImageCell |
| 528 - (void)drawWithFrame:(NSRect)frame inView:(NSView *)controlView { | 526 - (void)drawWithFrame:(NSRect)frame inView:(NSView *)controlView { |
| 529 // Display everything as a circle that spans the entire control. | 527 // Display everything as a circle that spans the entire control. |
| 530 NSBezierPath* path = [NSBezierPath bezierPathWithOvalInRect:frame]; | 528 NSBezierPath* path = [NSBezierPath bezierPathWithOvalInRect:frame]; |
| 531 [path addClip]; | 529 [path addClip]; |
| 532 [super drawImage:[self image] withFrame:frame inView:controlView]; | 530 [super drawImage:[self image] withFrame:frame inView:controlView]; |
| 533 } | 531 } |
| 534 @end | 532 @end |
| 535 | 533 |
| 536 // A custom image control that shows a "Change" button when moused over. | |
| 537 @interface EditableProfilePhoto : HoverImageButton { | |
| 538 @private | |
| 539 AvatarMenu* avatarMenu_; // Weak; Owned by ProfileChooserController. | |
| 540 base::scoped_nsobject<TransparentBackgroundImageView> changePhotoImage_; | |
| 541 ProfileChooserController* controller_; | |
| 542 } | |
| 543 | |
| 544 - (id)initWithFrame:(NSRect)frameRect | |
| 545 avatarMenu:(AvatarMenu*)avatarMenu | |
| 546 profileIcon:(const gfx::Image&)profileIcon | |
| 547 editingAllowed:(BOOL)editingAllowed | |
| 548 withController:(ProfileChooserController*)controller; | |
| 549 | |
| 550 // Called when the "Change" button is clicked. | |
| 551 - (void)editPhoto:(id)sender; | |
| 552 | |
| 553 @end | |
| 554 | |
| 555 @implementation EditableProfilePhoto | |
| 556 - (id)initWithFrame:(NSRect)frameRect | |
| 557 avatarMenu:(AvatarMenu*)avatarMenu | |
| 558 profileIcon:(const gfx::Image&)profileIcon | |
| 559 editingAllowed:(BOOL)editingAllowed | |
| 560 withController:(ProfileChooserController*)controller { | |
| 561 if ((self = [super initWithFrame:frameRect])) { | |
| 562 avatarMenu_ = avatarMenu; | |
| 563 controller_ = controller; | |
| 564 | |
| 565 [self setBordered:NO]; | |
| 566 | |
| 567 base::scoped_nsobject<CustomCircleImageCell> cell( | |
| 568 [[CustomCircleImageCell alloc] init]); | |
| 569 [self setCell:cell.get()]; | |
| 570 | |
| 571 [self setDefaultImage:CreateProfileImage(profileIcon, kLargeImageSide, | |
| 572 profiles::SHAPE_SQUARE)]; | |
| 573 [self setImagePosition:NSImageOnly]; | |
| 574 | |
| 575 if (editingAllowed) { | |
| 576 NSRect bounds = NSMakeRect(0, 0, kLargeImageSide, kLargeImageSide); | |
| 577 [self setTarget:self]; | |
| 578 [self setAction:@selector(editPhoto:)]; | |
| 579 changePhotoImage_.reset([[TransparentBackgroundImageView alloc] | |
| 580 initWithFrame:bounds]); | |
| 581 [changePhotoImage_ setImage:ui::ResourceBundle::GetSharedInstance(). | |
| 582 GetNativeImageNamed(IDR_ICON_PROFILES_EDIT_CAMERA).AsNSImage()]; | |
| 583 [self addSubview:changePhotoImage_]; | |
| 584 | |
| 585 // Hide the image until the button is hovered over. | |
| 586 [changePhotoImage_ setHidden:YES]; | |
| 587 } | |
| 588 | |
| 589 // Set the image cell's accessibility strings to be the same as the | |
| 590 // button's strings. | |
| 591 [[self cell] accessibilitySetOverrideValue:l10n_util::GetNSString( | |
| 592 editingAllowed ? | |
| 593 IDS_PROFILES_NEW_AVATAR_MENU_CHANGE_PHOTO_ACCESSIBLE_NAME : | |
| 594 IDS_PROFILES_NEW_AVATAR_MENU_PHOTO_ACCESSIBLE_NAME) | |
| 595 forAttribute:NSAccessibilityTitleAttribute]; | |
| 596 [[self cell] accessibilitySetOverrideValue: | |
| 597 editingAllowed ? NSAccessibilityButtonRole : NSAccessibilityImageRole | |
| 598 forAttribute:NSAccessibilityRoleAttribute]; | |
| 599 [[self cell] accessibilitySetOverrideValue: | |
| 600 NSAccessibilityRoleDescription(NSAccessibilityButtonRole, nil) | |
| 601 forAttribute:NSAccessibilityRoleDescriptionAttribute]; | |
| 602 | |
| 603 // The button and the cell should read the same thing. | |
| 604 [self accessibilitySetOverrideValue:l10n_util::GetNSString( | |
| 605 editingAllowed ? | |
| 606 IDS_PROFILES_NEW_AVATAR_MENU_CHANGE_PHOTO_ACCESSIBLE_NAME : | |
| 607 IDS_PROFILES_NEW_AVATAR_MENU_PHOTO_ACCESSIBLE_NAME) | |
| 608 forAttribute:NSAccessibilityTitleAttribute]; | |
| 609 [self accessibilitySetOverrideValue:NSAccessibilityButtonRole | |
| 610 forAttribute:NSAccessibilityRoleAttribute]; | |
| 611 [self accessibilitySetOverrideValue: | |
| 612 NSAccessibilityRoleDescription(NSAccessibilityButtonRole, nil) | |
| 613 forAttribute:NSAccessibilityRoleDescriptionAttribute]; | |
| 614 } | |
| 615 return self; | |
| 616 } | |
| 617 | |
| 618 - (void)editPhoto:(id)sender { | |
| 619 avatarMenu_->EditProfile(avatarMenu_->GetActiveProfileIndex()); | |
| 620 [controller_ | |
| 621 postActionPerformed:ProfileMetrics::PROFILE_DESKTOP_MENU_EDIT_IMAGE]; | |
| 622 } | |
| 623 | |
| 624 - (void)setHoverState:(HoverState)state { | |
| 625 [super setHoverState:state]; | |
| 626 [changePhotoImage_ setHidden:([self hoverState] == kHoverStateNone)]; | |
| 627 } | |
| 628 | |
| 629 - (BOOL)canBecomeKeyView { | |
| 630 return false; | |
| 631 } | |
| 632 | |
| 633 - (BOOL)accessibilityIsIgnored { | |
| 634 return NO; | |
| 635 } | |
| 636 | |
| 637 - (NSArray*)accessibilityActionNames { | |
| 638 NSArray* parentActions = [super accessibilityActionNames]; | |
| 639 return [parentActions arrayByAddingObject:NSAccessibilityPressAction]; | |
| 640 } | |
| 641 | |
| 642 - (void)accessibilityPerformAction:(NSString*)action { | |
| 643 if ([action isEqualToString:NSAccessibilityPressAction]) { | |
| 644 avatarMenu_->EditProfile(avatarMenu_->GetActiveProfileIndex()); | |
| 645 } | |
| 646 | |
| 647 [super accessibilityPerformAction:action]; | |
| 648 } | |
| 649 | |
| 650 @end | |
| 651 | |
| 652 // A custom view with a filled circular background. | 534 // A custom view with a filled circular background. |
| 653 @interface BackgroundCircleView : NSView { | 535 @interface BackgroundCircleView : NSView { |
| 654 @private | 536 @private |
| 655 base::scoped_nsobject<NSColor> fillColor_; | 537 base::scoped_nsobject<NSColor> fillColor_; |
| 656 } | 538 } |
| 657 @end | 539 @end |
| 658 | 540 |
| 659 @implementation BackgroundCircleView | 541 @implementation BackgroundCircleView |
| 660 - (id)initWithFrame:(NSRect)frameRect withFillColor:(NSColor*)fillColor { | 542 - (id)initWithFrame:(NSRect)frameRect withFillColor:(NSColor*)fillColor { |
| 661 if ((self = [super initWithFrame:frameRect])) | 543 if ((self = [super initWithFrame:frameRect])) |
| 662 fillColor_.reset([fillColor retain]); | 544 fillColor_.reset([fillColor retain]); |
| 663 return self; | 545 return self; |
| 664 } | 546 } |
| 665 | 547 |
| 666 - (void)drawRect:(NSRect)dirtyRect { | 548 - (void)drawRect:(NSRect)dirtyRect { |
| 667 [fillColor_ setFill]; | 549 [fillColor_ setFill]; |
| 668 NSBezierPath* circlePath = [NSBezierPath bezierPath]; | 550 NSBezierPath* circlePath = [NSBezierPath bezierPath]; |
| 669 [circlePath appendBezierPathWithOvalInRect:[self bounds]]; | 551 [circlePath appendBezierPathWithOvalInRect:[self bounds]]; |
| 670 [circlePath fill]; | 552 [circlePath fill]; |
| 671 | 553 |
| 672 [super drawRect:dirtyRect]; | 554 [super drawRect:dirtyRect]; |
| 673 } | 555 } |
| 674 @end | 556 @end |
| 675 | 557 |
| 676 // A custom text control that turns into a textfield for editing when clicked. | |
| 677 @interface EditableProfileNameButton : HoverImageButton<NSTextFieldDelegate> { | |
| 678 @private | |
| 679 base::scoped_nsobject<NSTextField> profileNameTextField_; | |
| 680 Profile* profile_; // Weak. | |
| 681 ProfileChooserController* controller_; | |
| 682 } | |
| 683 | |
| 684 - (id)initWithFrame:(NSRect)frameRect | |
| 685 profile:(Profile*)profile | |
| 686 profileName:(NSString*)profileName | |
| 687 editingAllowed:(BOOL)editingAllowed | |
| 688 withController:(ProfileChooserController*)controller; | |
| 689 | |
| 690 // Called when the button is clicked. | |
| 691 - (void)showEditableView:(id)sender; | |
| 692 | |
| 693 // Called when enter is pressed in the text field. | |
| 694 - (void)saveProfileName; | |
| 695 | |
| 696 @end | |
| 697 | |
| 698 @implementation EditableProfileNameButton | |
| 699 - (id)initWithFrame:(NSRect)frameRect | |
| 700 profile:(Profile*)profile | |
| 701 profileName:(NSString*)profileName | |
| 702 editingAllowed:(BOOL)editingAllowed | |
| 703 withController:(ProfileChooserController*)controller { | |
| 704 if ((self = [super initWithFrame:frameRect])) { | |
| 705 profile_ = profile; | |
| 706 controller_ = controller; | |
| 707 | |
| 708 CGFloat availableWidth = frameRect.size.width; | |
| 709 NSSize textSize = [profileName sizeWithAttributes:@{ | |
| 710 NSFontAttributeName : [self font] | |
| 711 }]; | |
| 712 | |
| 713 if (editingAllowed) { | |
| 714 // Show an "edit" pencil icon when hovering over. In the default state, | |
| 715 // we need to create an empty placeholder of the correct size, so that | |
| 716 // the text doesn't jump around when the hovered icon appears. | |
| 717 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); | |
| 718 NSImage* hoverImage = rb->GetNativeImageNamed( | |
| 719 IDR_ICON_PROFILES_EDIT_HOVER).AsNSImage(); | |
| 720 | |
| 721 // In order to center the button title, we need to add a left padding of | |
| 722 // the same width as the pencil icon. | |
| 723 base::scoped_nsobject<CustomPaddingImageButtonCell> cell( | |
| 724 [[CustomPaddingImageButtonCell alloc] | |
| 725 initWithLeftMarginSpacing:[hoverImage size].width | |
| 726 imageTitleSpacing:0]); | |
| 727 [self setCell:cell.get()]; | |
| 728 | |
| 729 NSImage* placeholder = [[NSImage alloc] initWithSize:[hoverImage size]]; | |
| 730 [self setDefaultImage:placeholder]; | |
| 731 [self setHoverImage:hoverImage]; | |
| 732 [self setAlternateImage: | |
| 733 rb->GetNativeImageNamed(IDR_ICON_PROFILES_EDIT_PRESSED).AsNSImage()]; | |
| 734 [self setImagePosition:NSImageRight]; | |
| 735 [self setTarget:self]; | |
| 736 [self setAction:@selector(showEditableView:)]; | |
| 737 | |
| 738 // We need to subtract the width of the bezel from the frame rect, so that | |
| 739 // the textfield can take the exact same space as the button. | |
| 740 frameRect.size.height -= 2 * kBezelThickness; | |
| 741 frameRect.origin = NSMakePoint(0, kBezelThickness); | |
| 742 profileNameTextField_.reset( | |
| 743 [[NSTextField alloc] initWithFrame:frameRect]); | |
| 744 [profileNameTextField_ setStringValue:profileName]; | |
| 745 [profileNameTextField_ setFont:[NSFont labelFontOfSize:kTitleFontSize]]; | |
| 746 [profileNameTextField_ setEditable:YES]; | |
| 747 [profileNameTextField_ setDrawsBackground:YES]; | |
| 748 [profileNameTextField_ setBezeled:YES]; | |
| 749 [profileNameTextField_ setAlignment:NSCenterTextAlignment]; | |
| 750 [[profileNameTextField_ cell] setWraps:NO]; | |
| 751 [[profileNameTextField_ cell] setLineBreakMode: | |
| 752 NSLineBreakByTruncatingTail]; | |
| 753 [[profileNameTextField_ cell] setUsesSingleLineMode:YES]; | |
| 754 [self addSubview:profileNameTextField_]; | |
| 755 [profileNameTextField_ setDelegate:self]; | |
| 756 | |
| 757 // Hide the textfield until the user clicks on the button. | |
| 758 [profileNameTextField_ setHidden:YES]; | |
| 759 | |
| 760 [[self cell] accessibilitySetOverrideValue:l10n_util::GetNSStringF( | |
| 761 IDS_PROFILES_NEW_AVATAR_MENU_EDIT_NAME_ACCESSIBLE_NAME, | |
| 762 base::SysNSStringToUTF16(profileName)) | |
| 763 forAttribute:NSAccessibilityTitleAttribute]; | |
| 764 | |
| 765 // Recompute the available width for the name since the icon takes space. | |
| 766 availableWidth -= [hoverImage size].width * 2; | |
| 767 // The profileNameTextField_ might size the text differently. | |
| 768 textSize = [profileName sizeWithAttributes:@{ | |
| 769 NSFontAttributeName : [profileNameTextField_ font] | |
| 770 }]; | |
| 771 } | |
| 772 | |
| 773 if (textSize.width > availableWidth) | |
| 774 [self setToolTip:profileName]; | |
| 775 | |
| 776 [[self cell] accessibilitySetOverrideValue:NSAccessibilityButtonRole | |
| 777 forAttribute:NSAccessibilityRoleAttribute]; | |
| 778 [[self cell] | |
| 779 accessibilitySetOverrideValue:NSAccessibilityRoleDescription( | |
| 780 NSAccessibilityButtonRole, nil) | |
| 781 forAttribute:NSAccessibilityRoleDescriptionAttribute]; | |
| 782 | |
| 783 [self setBordered:NO]; | |
| 784 [self setFont:[NSFont labelFontOfSize:kTitleFontSize]]; | |
| 785 [self setAlignment:NSCenterTextAlignment]; | |
| 786 [[self cell] setLineBreakMode:NSLineBreakByTruncatingTail]; | |
| 787 [self setTitle:profileName]; | |
| 788 } | |
| 789 return self; | |
| 790 } | |
| 791 | |
| 792 - (void)saveProfileName { | |
| 793 base::string16 newProfileName = | |
| 794 base::SysNSStringToUTF16([profileNameTextField_ stringValue]); | |
| 795 | |
| 796 // Empty profile names are not allowed, and do nothing. | |
| 797 base::TrimWhitespace(newProfileName, base::TRIM_ALL, &newProfileName); | |
| 798 if (!newProfileName.empty()) { | |
| 799 profiles::UpdateProfileName(profile_, newProfileName); | |
| 800 [controller_ | |
| 801 postActionPerformed:ProfileMetrics::PROFILE_DESKTOP_MENU_EDIT_NAME]; | |
| 802 [profileNameTextField_ setHidden:YES]; | |
| 803 } | |
| 804 } | |
| 805 | |
| 806 - (void)showEditableView:(id)sender { | |
| 807 [profileNameTextField_ setHidden:NO]; | |
| 808 [[self window] makeFirstResponder:profileNameTextField_]; | |
| 809 } | |
| 810 | |
| 811 - (BOOL)canBecomeKeyView { | |
| 812 return false; | |
| 813 } | |
| 814 | |
| 815 - (BOOL)control:(NSControl*)control | |
| 816 textView:(NSTextView*)textView | |
| 817 doCommandBySelector:(SEL)commandSelector { | |
| 818 if (commandSelector == @selector(insertTab:) || | |
| 819 commandSelector == @selector(insertNewline:)) { | |
| 820 [self saveProfileName]; | |
| 821 return YES; | |
| 822 } | |
| 823 return NO; | |
| 824 } | |
| 825 | |
| 826 @end | |
| 827 | |
| 828 // A custom button that allows for setting a background color when hovered over. | 558 // A custom button that allows for setting a background color when hovered over. |
| 829 @interface BackgroundColorHoverButton : HoverImageButton { | 559 @interface BackgroundColorHoverButton : HoverImageButton { |
| 830 @private | 560 @private |
| 831 base::scoped_nsobject<NSColor> backgroundColor_; | 561 base::scoped_nsobject<NSColor> backgroundColor_; |
| 832 base::scoped_nsobject<NSColor> hoverColor_; | 562 base::scoped_nsobject<NSColor> hoverColor_; |
| 833 } | 563 } |
| 834 | 564 |
| 835 - (void)setRightMarginSpacing:(int)rightMarginSpacing; | 565 - (void)setRightMarginSpacing:(int)rightMarginSpacing; |
| 836 @end | 566 @end |
| 837 | 567 |
| (...skipping 1806 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2644 | 2374 |
| 2645 - (void)showWindow:(id)sender { | 2375 - (void)showWindow:(id)sender { |
| 2646 [super showWindow:sender]; | 2376 [super showWindow:sender]; |
| 2647 NSEvent *event = [[NSApplication sharedApplication] currentEvent]; | 2377 NSEvent *event = [[NSApplication sharedApplication] currentEvent]; |
| 2648 if (firstProfileView_ && [event type] == NSKeyDown) { | 2378 if (firstProfileView_ && [event type] == NSKeyDown) { |
| 2649 [[self window] makeFirstResponder:firstProfileView_]; | 2379 [[self window] makeFirstResponder:firstProfileView_]; |
| 2650 } | 2380 } |
| 2651 } | 2381 } |
| 2652 | 2382 |
| 2653 @end | 2383 @end |
| OLD | NEW |