blob: 8b3ac3ed3bc79d71368dda6f519d570404b2574c [file] [log] [blame]
// Copyright 2013 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.
#import "chrome/browser/ui/cocoa/autofill/autofill_overlay_controller.h"
#include "base/logging.h"
#include "base/mac/foundation_util.h"
#include "base/strings/sys_string_conversions.h"
#include "chrome/browser/ui/autofill/autofill_dialog_types.h"
#include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h"
#include "chrome/browser/ui/cocoa/autofill/autofill_dialog_constants.h"
#include "skia/ext/skia_utils_mac.h"
namespace {
// Spacing around the message in the overlay view.
const CGFloat kOverlayLabelMargin = 10;
// Spacing below image and above text messages in overlay view.
const CGFloat kOverlayImageBottomMargin = 50;
// TODO(groby): Unify colors with Views.
// Slight shading for mouse hover and legal document background.
SkColor kShadingColor = 0xfff2f2f2;
// A border color for the legal document view.
SkColor kSubtleBorderColor = 0xffdfdfdf;
} // namespace
// An NSView encapsulating the message stack and its custom drawn elements.
@interface AutofillMessageView : NSView<AutofillLayout> {
@private
base::scoped_nsobject<NSTextField> label_;
}
- (id)initWithFrame:(NSRect)frame;
- (CGFloat)heightForWidth:(CGFloat)width;
- (void)setMessage:(const autofill::DialogOverlayString&)message;
@end
@implementation AutofillMessageView
- (void)drawRect:(NSRect)dirtyRect {
NSColor* shadingColor = gfx::SkColorToCalibratedNSColor(kShadingColor);
NSColor* borderColor = gfx::SkColorToCalibratedNSColor(kSubtleBorderColor);
CGFloat arrowHalfWidth = autofill::kArrowWidth / 2.0;
NSRect bounds = [self bounds];
CGFloat y = NSMaxY(bounds) - autofill::kArrowHeight;
NSBezierPath* arrow = [NSBezierPath bezierPath];
// Note that we purposely draw slightly outside of |bounds| so that the
// stroke is hidden on the sides and bottom.
NSRect arrowBounds = NSInsetRect(bounds, -1, -1);
arrowBounds.size.height--;
[arrow moveToPoint:NSMakePoint(NSMinX(arrowBounds), y)];
[arrow lineToPoint:
NSMakePoint(NSMidX(arrowBounds) - arrowHalfWidth, y)];
[arrow relativeLineToPoint:
NSMakePoint(arrowHalfWidth,autofill::kArrowHeight)];
[arrow relativeLineToPoint:
NSMakePoint(arrowHalfWidth, -autofill::kArrowHeight)];
[arrow lineToPoint:NSMakePoint(NSMaxX(arrowBounds), y)];
[arrow lineToPoint:NSMakePoint(NSMaxX(arrowBounds), NSMinY(arrowBounds))];
[arrow lineToPoint:NSMakePoint(NSMinX(arrowBounds), NSMinY(arrowBounds))];
[arrow closePath];
[shadingColor setFill];
[arrow fill];
[borderColor setStroke];
[arrow stroke];
}
- (id)initWithFrame:(NSRect)frame {
if (self = [super initWithFrame:frame]) {
label_.reset([[NSTextField alloc] initWithFrame:NSZeroRect]);
[label_ setEditable:NO];
[label_ setBordered:NO];
[label_ setDrawsBackground:NO];
[label_ setAlignment:NSCenterTextAlignment];
[self addSubview:label_];
}
return self;
}
- (CGFloat)heightForWidth:(CGFloat)width {
return NSHeight([label_ frame]) + autofill::kArrowHeight +
2 * kOverlayLabelMargin;
}
- (void)setMessage:(const autofill::DialogOverlayString&)message {
// We probably want to look at other multi-line messages somewhere.
[label_ setFont:message.font.GetNativeFont()];
[label_ setStringValue:base::SysUTF16ToNSString(message.text)];
[label_ setTextColor:gfx::SkColorToCalibratedNSColor(message.text_color)];
[label_ sizeToFit];
[self setHidden:message.text.empty()];
}
- (void)performLayout {
if ([self isHidden])
return;
CGFloat labelHeight = NSHeight([label_ frame]);
[label_ setFrame:NSMakeRect(0, kOverlayLabelMargin,
NSWidth([self bounds]), labelHeight)];
// TODO(groby): useful DCHECK() goes here.
}
- (NSSize)preferredSize {
NOTREACHED();
return NSZeroSize;
}
@end
@implementation AutofillOverlayController
- (id)initWithDelegate:(autofill::AutofillDialogViewDelegate*)delegate {
if (self = [super initWithNibName:nil bundle:nil]) {
delegate_ = delegate;
base::scoped_nsobject<NSBox> view([[NSBox alloc] initWithFrame:NSZeroRect]);
[view setBoxType:NSBoxCustom];
[view setBorderType:NSNoBorder];
[view setContentViewMargins:NSZeroSize];
[view setTitlePosition:NSNoTitle];
childView_.reset([[NSView alloc] initWithFrame:NSZeroRect]);
messageView_.reset([[AutofillMessageView alloc] initWithFrame:NSZeroRect]);
imageView_.reset([[NSImageView alloc] initWithFrame:NSZeroRect]);
[imageView_ setImageAlignment:NSImageAlignCenter];
[childView_ setSubviews:@[messageView_, imageView_]];
[view addSubview:childView_];
[self setView:view];
}
return self;
}
- (void)updateState {
const autofill::DialogOverlayState& state = delegate_->GetDialogOverlay();
if (state.image.IsEmpty()) {
[[self view] setHidden:YES];
return;
}
NSBox* view = base::mac::ObjCCastStrict<NSBox>([self view]);
[view setFillColor:[[view window] backgroundColor]];
[view setAlphaValue:1];
[childView_ setAlphaValue:1];
[imageView_ setImage:state.image.ToNSImage()];
[messageView_ setMessage:state.string];
[childView_ setHidden:NO];
[view setHidden:NO];
NSWindowController* delegate = [[[self view] window] windowController];
if ([delegate respondsToSelector:@selector(requestRelayout)])
[delegate performSelector:@selector(requestRelayout)];
}
- (CGFloat)heightForWidth:(int) width {
// 0 means "no preference". Image-only overlays fit the container.
if ([messageView_ isHidden])
return 0;
// Overlays with text messages express a size preference.
return kOverlayImageBottomMargin +
[messageView_ heightForWidth:width] +
[[imageView_ image] size].height;
}
- (NSSize)preferredSize {
NOTREACHED(); // Only implemented as part of AutofillLayout protocol.
return NSZeroSize;
}
- (void)performLayout {
NSRect bounds = [[self view] bounds];
[childView_ setFrame:bounds];
if ([messageView_ isHidden]) {
[imageView_ setFrame:bounds];
return;
}
int messageHeight = [messageView_ heightForWidth:NSWidth(bounds)];
[messageView_ setFrame:NSMakeRect(0, 0, NSWidth(bounds), messageHeight)];
[messageView_ performLayout];
NSSize imageSize = [[imageView_ image] size];
[imageView_ setFrame:NSMakeRect(
0, NSMaxY([messageView_ frame]) + kOverlayImageBottomMargin,
NSWidth(bounds), imageSize.height)];
}
@end