summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorjamescook@chromium.org <jamescook@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-25 22:52:24 +0000
committerjamescook@chromium.org <jamescook@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-25 22:52:24 +0000
commitda93d567b20e325537e14abe508748d9d695f6c5 (patch)
treea3e84803e2354aa4c6429b495d49f5c9a10fd608 /chrome/browser
parent594e66fe7b8cff055de73257cbad8c01f9c0c5ac (diff)
downloadchromium_src-da93d567b20e325537e14abe508748d9d695f6c5.zip
chromium_src-da93d567b20e325537e14abe508748d9d695f6c5.tar.gz
chromium_src-da93d567b20e325537e14abe508748d9d695f6c5.tar.bz2
Aura: Non-client frame view approximately like the mocks
+ Adds Aura-look window close and maximize buttons. + Adds a transparent window background at the caption, visible on mouse over. + Adds transparent resize handles on window edges, also visible on mouse over. BUG=97254 TEST=manual Review URL: http://codereview.chromium.org/8366014 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@107220 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/ui/views/frame/browser_frame_aura.cc149
-rw-r--r--chrome/browser/ui/views/frame/browser_non_client_frame_view_aura.cc518
-rw-r--r--chrome/browser/ui/views/frame/browser_non_client_frame_view_aura.h91
-rw-r--r--chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_aura.cc22
4 files changed, 780 insertions, 0 deletions
diff --git a/chrome/browser/ui/views/frame/browser_frame_aura.cc b/chrome/browser/ui/views/frame/browser_frame_aura.cc
index d320084..9646878 100644
--- a/chrome/browser/ui/views/frame/browser_frame_aura.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_aura.cc
@@ -4,7 +4,152 @@
#include "chrome/browser/ui/views/frame/browser_frame_aura.h"
+#include "chrome/browser/themes/theme_service.h"
+#include "chrome/browser/ui/views/frame/browser_non_client_frame_view_aura.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "grit/theme_resources_standard.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/theme_provider.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/compositor/layer.h"
#include "ui/gfx/font.h"
+#include "views/background.h"
+
+namespace {
+
+// The content left/right images have a shadow built into them.
+const int kContentEdgeShadowThickness = 2;
+
+// Background view to paint the gradient behind the back/forward/omnibox
+// toolbar area.
+class ToolbarBackground : public views::Background {
+ public:
+ explicit ToolbarBackground(BrowserView* browser_view);
+ virtual ~ToolbarBackground();
+
+ // views::Background overrides:
+ virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE;
+
+ private:
+ BrowserView* browser_view_;
+ DISALLOW_COPY_AND_ASSIGN(ToolbarBackground);
+};
+
+ToolbarBackground::ToolbarBackground(BrowserView* browser_view)
+ : browser_view_(browser_view) {
+}
+
+ToolbarBackground::~ToolbarBackground() {
+}
+
+void ToolbarBackground::Paint(gfx::Canvas* canvas, views::View* view) const {
+ gfx::Rect toolbar_bounds = browser_view_->GetToolbarBounds();
+ if (toolbar_bounds.IsEmpty())
+ return;
+
+ int x = toolbar_bounds.x();
+ int w = toolbar_bounds.width();
+ int y = toolbar_bounds.y();
+ int h = toolbar_bounds.bottom();
+
+ // Gross hack: We split the toolbar images into two pieces, since sometimes
+ // (popup mode) the toolbar isn't tall enough to show the whole image. The
+ // split happens between the top shadow section and the bottom gradient
+ // section so that we never break the gradient.
+ int split_point = views::NonClientFrameView::kFrameShadowThickness * 2;
+ int bottom_y = y + split_point;
+ ui::ThemeProvider* tp = browser_view_->GetThemeProvider();
+ SkBitmap* toolbar_left = tp->GetBitmapNamed(IDR_CONTENT_TOP_LEFT_CORNER);
+ int bottom_edge_height = std::min(toolbar_left->height(), h) - split_point;
+
+ // Split our canvas out so we can mask out the corners of the toolbar
+ // without masking out the frame.
+ canvas->SaveLayerAlpha(
+ 255, gfx::Rect(x - views::NonClientFrameView::kClientEdgeThickness,
+ y,
+ w + views::NonClientFrameView::kClientEdgeThickness * 3,
+ h));
+ canvas->GetSkCanvas()->drawARGB(0, 255, 255, 255, SkXfermode::kClear_Mode);
+
+ SkColor theme_toolbar_color =
+ tp->GetColor(ThemeService::COLOR_TOOLBAR);
+ canvas->FillRectInt(theme_toolbar_color, x, bottom_y, w, bottom_edge_height);
+
+ // Tile the toolbar image starting at the frame edge on the left and where the
+ // horizontal tabstrip is (or would be) on the top.
+ SkBitmap* theme_toolbar = tp->GetBitmapNamed(IDR_THEME_TOOLBAR);
+ canvas->TileImageInt(*theme_toolbar, x,
+ bottom_y, x,
+ bottom_y, w, theme_toolbar->height());
+
+ // Draw rounded corners for the tab.
+ SkBitmap* toolbar_left_mask =
+ tp->GetBitmapNamed(IDR_CONTENT_TOP_LEFT_CORNER_MASK);
+ SkBitmap* toolbar_right_mask =
+ tp->GetBitmapNamed(IDR_CONTENT_TOP_RIGHT_CORNER_MASK);
+
+ // We mask out the corners by using the DestinationIn transfer mode,
+ // which keeps the RGB pixels from the destination and the alpha from
+ // the source.
+ SkPaint paint;
+ paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
+
+ // Mask the left edge.
+ int left_x = x - kContentEdgeShadowThickness;
+ canvas->DrawBitmapInt(*toolbar_left_mask, 0, 0, toolbar_left_mask->width(),
+ split_point, left_x, y, toolbar_left_mask->width(),
+ split_point, false, paint);
+ canvas->DrawBitmapInt(*toolbar_left_mask, 0,
+ toolbar_left_mask->height() - bottom_edge_height,
+ toolbar_left_mask->width(), bottom_edge_height, left_x, bottom_y,
+ toolbar_left_mask->width(), bottom_edge_height, false, paint);
+
+ // Mask the right edge.
+ int right_x =
+ x + w - toolbar_right_mask->width() + kContentEdgeShadowThickness;
+ canvas->DrawBitmapInt(*toolbar_right_mask, 0, 0, toolbar_right_mask->width(),
+ split_point, right_x, y, toolbar_right_mask->width(),
+ split_point, false, paint);
+ canvas->DrawBitmapInt(*toolbar_right_mask, 0,
+ toolbar_right_mask->height() - bottom_edge_height,
+ toolbar_right_mask->width(), bottom_edge_height, right_x, bottom_y,
+ toolbar_right_mask->width(), bottom_edge_height, false, paint);
+ canvas->Restore();
+
+ canvas->DrawBitmapInt(*toolbar_left, 0, 0, toolbar_left->width(), split_point,
+ left_x, y, toolbar_left->width(), split_point, false);
+ canvas->DrawBitmapInt(*toolbar_left, 0,
+ toolbar_left->height() - bottom_edge_height, toolbar_left->width(),
+ bottom_edge_height, left_x, bottom_y, toolbar_left->width(),
+ bottom_edge_height, false);
+
+ SkBitmap* toolbar_center =
+ tp->GetBitmapNamed(IDR_CONTENT_TOP_CENTER);
+ canvas->TileImageInt(*toolbar_center, 0, 0, left_x + toolbar_left->width(),
+ y, right_x - (left_x + toolbar_left->width()),
+ split_point);
+
+ SkBitmap* toolbar_right = tp->GetBitmapNamed(IDR_CONTENT_TOP_RIGHT_CORNER);
+ canvas->DrawBitmapInt(*toolbar_right, 0, 0, toolbar_right->width(),
+ split_point, right_x, y, toolbar_right->width(), split_point, false);
+ canvas->DrawBitmapInt(*toolbar_right, 0,
+ toolbar_right->height() - bottom_edge_height, toolbar_right->width(),
+ bottom_edge_height, right_x, bottom_y, toolbar_right->width(),
+ bottom_edge_height, false);
+
+ // Draw the content/toolbar separator.
+ canvas->FillRectInt(
+ ResourceBundle::toolbar_separator_color,
+ x + views::NonClientFrameView::kClientEdgeThickness,
+ toolbar_bounds.bottom() - views::NonClientFrameView::kClientEdgeThickness,
+ w - (2 * views::NonClientFrameView::kClientEdgeThickness),
+ views::NonClientFrameView::kClientEdgeThickness);
+}
+
+} // namespace
///////////////////////////////////////////////////////////////////////////////
// BrowserFrameAura, public:
@@ -14,6 +159,10 @@ BrowserFrameAura::BrowserFrameAura(BrowserFrame* browser_frame,
: views::NativeWidgetAura(browser_frame),
browser_view_(browser_view),
browser_frame_(browser_frame) {
+ // Aura paints layers behind this view, so this must be a layer also.
+ browser_view_->SetPaintToLayer(true);
+ browser_view_->layer()->SetFillsBoundsOpaquely(false);
+ browser_view_->set_background(new ToolbarBackground(browser_view));
}
BrowserFrameAura::~BrowserFrameAura() {
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_aura.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_aura.cc
new file mode 100644
index 0000000..cbdcfce
--- /dev/null
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_aura.cc
@@ -0,0 +1,518 @@
+// Copyright (c) 2011 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/views/frame/browser_non_client_frame_view_aura.h"
+
+#include "chrome/browser/ui/views/frame/browser_frame.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "grit/generated_resources.h" // Accessibility names
+#include "grit/ui_resources.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "ui/aura/cursor.h"
+#include "ui/base/animation/throb_animation.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/compositor/layer.h"
+#include "views/controls/button/custom_button.h"
+#include "views/widget/widget.h"
+#include "views/widget/widget_delegate.h"
+
+#if !defined(OS_WIN)
+#include "views/window/hit_test.h"
+#endif
+
+namespace {
+// Our window is larger than it appears, as it includes space around the edges
+// where resize handles can appear.
+const int kResizeBorderThickness = 8;
+// The top edge is a little thinner, as it is not draggable for resize.
+const int kTopBorderThickness = 4;
+// Offset between top of non-client frame and top edge of opaque frame
+// background at start of slide-in animation.
+const int kFrameBackgroundTopOffset = 25;
+
+// The color used to fill the frame. Opacity is handled in the layer.
+const SkColor kFrameColor = SK_ColorBLACK;
+// Radius of rounded rectangle corners.
+const int kRoundedRectRadius = 3;
+// Frame border fades in over this range of opacity.
+const double kFrameBorderStartOpacity = 0.2;
+const double kFrameBorderEndOpacity = 0.3;
+// How long the hover animation takes if uninterrupted.
+const int kHoverFadeDurationMs = 250;
+
+// The color behind the toolbar (back, forward, omnibox, etc.)
+const SkColor kToolbarBackgroundColor = SkColorSetRGB(0xF6, 0xF6, 0xF6);
+// Color shown when window control is hovered.
+const SkColor kMaximizeButtonBackgroundColor = SkColorSetRGB(0, 255, 0);
+const SkColor kCloseButtonBackgroundColor = SkColorSetRGB(255, 0, 0);
+
+bool HitVisibleView(views::View* view, gfx::Point point) {
+ return view->IsVisible() && view->GetMirroredBounds().Contains(point);
+}
+
+} // namespace
+
+// Buttons for window controls - close, zoom, etc.
+class WindowControlButton : public views::CustomButton {
+ public:
+ WindowControlButton(views::ButtonListener* listener,
+ SkColor color,
+ const SkBitmap& icon)
+ : views::CustomButton(listener),
+ color_(color),
+ icon_(icon) {
+ SetPaintToLayer(true);
+ layer()->SetFillsBoundsOpaquely(false);
+ }
+ virtual ~WindowControlButton() {}
+
+ // Overridden from views::View:
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
+ canvas->FillRectInt(GetBackgroundColor(), 0, 0, width(), height());
+ canvas->DrawBitmapInt(icon_, 0, 0);
+ }
+ virtual gfx::Size GetPreferredSize() OVERRIDE {
+ return gfx::Size(icon_.width(), icon_.height());
+ }
+
+ private:
+ SkColor GetBackgroundColor() {
+ // Only the background animates, so handle opacity manually.
+ return SkColorSetARGB(hover_animation_->CurrentValueBetween(0, 150),
+ SkColorGetR(color_),
+ SkColorGetG(color_),
+ SkColorGetB(color_));
+ }
+
+ SkColor color_;
+ SkBitmap icon_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowControlButton);
+};
+
+// Layer that visually sits "behind" the window contents and expands out to
+// provide visual resize handles on the sides. Hit testing and resize handling
+// is in the parent NonClientFrameView.
+class FrameBackground : public views::View,
+ public ui::AnimationDelegate {
+ public:
+ FrameBackground()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(
+ size_animation_(new ui::SlideAnimation(this))),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ color_animation_(new ui::SlideAnimation(this))) {
+ size_animation_->SetSlideDuration(kHoverFadeDurationMs);
+ color_animation_->SetSlideDuration(kHoverFadeDurationMs);
+ SetPaintToLayer(true);
+ UpdateOpacity();
+ }
+ virtual ~FrameBackground() {
+ }
+
+ void Configure(const gfx::Rect& start_bounds, const gfx::Rect& end_bounds) {
+ start_bounds_ = start_bounds;
+ end_bounds_ = end_bounds;
+ UpdateBounds();
+ }
+ void SetEndBounds(const gfx::Rect& end_bounds) {
+ end_bounds_ = end_bounds;
+ UpdateBounds();
+ }
+ void Show() {
+ size_animation_->Show();
+ color_animation_->Show();
+ }
+ void Hide() {
+ size_animation_->Hide();
+ color_animation_->Hide();
+ }
+
+ protected:
+ // Overridden from views::View:
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
+ SkRect rect = { SkIntToScalar(0), SkIntToScalar(0),
+ SkIntToScalar(width()), SkIntToScalar(height()) };
+ SkScalar radius = SkIntToScalar(kRoundedRectRadius);
+ SkPaint paint;
+ // Animation handles setting the opacity for the whole layer.
+ paint.setColor(kFrameColor);
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->GetSkCanvas()->drawRoundRect(rect, radius, radius, paint);
+ }
+
+ // Overridden from ui::AnimationDelegate:
+ virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE {
+ if (animation == color_animation_.get()) {
+ UpdateOpacity();
+ } else if (animation == size_animation_.get()) {
+ UpdateBounds();
+ }
+ }
+
+ private:
+ void UpdateOpacity() {
+ double opacity = color_animation_->CurrentValueBetween(
+ kFrameBorderStartOpacity, kFrameBorderEndOpacity);
+ layer()->SetOpacity(static_cast<float>(opacity));
+ }
+
+ void UpdateBounds() {
+ gfx::Rect current_bounds =
+ size_animation_->CurrentValueBetween(start_bounds_, end_bounds_);
+ SetBoundsRect(current_bounds);
+ SchedulePaint();
+ }
+
+ scoped_ptr<ui::SlideAnimation> size_animation_;
+ scoped_ptr<ui::SlideAnimation> color_animation_;
+ // Default "hidden" rectangle.
+ gfx::Rect default_bounds_;
+ // When moving mouse from one target to another (e.g. from edge to corner)
+ // the size animation start point may not be the default size.
+ gfx::Rect start_bounds_;
+ // Expanded bounds, with edges visible from behind the client area.
+ gfx::Rect end_bounds_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameBackground);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// BrowserNonClientFrameViewAura, public:
+
+BrowserNonClientFrameViewAura::BrowserNonClientFrameViewAura(
+ BrowserFrame* frame, BrowserView* browser_view)
+ : BrowserNonClientFrameView(),
+ browser_frame_(frame),
+ browser_view_(browser_view),
+ last_hittest_code_(HTNOWHERE) {
+ frame_background_ = new FrameBackground();
+ AddChildView(frame_background_);
+
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ maximize_button_ =
+ new WindowControlButton(this,
+ kMaximizeButtonBackgroundColor,
+ *rb.GetBitmapNamed(IDR_AURA_WINDOW_ZOOM_ICON));
+ maximize_button_->SetVisible(false);
+ maximize_button_->SetAccessibleName(
+ l10n_util::GetStringUTF16(IDS_ACCNAME_MAXIMIZE));
+ AddChildView(maximize_button_);
+
+ close_button_ =
+ new WindowControlButton(this,
+ kCloseButtonBackgroundColor,
+ *rb.GetBitmapNamed(IDR_AURA_WINDOW_CLOSE_ICON));
+ close_button_->SetVisible(false);
+ close_button_->SetAccessibleName(
+ l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE));
+ AddChildView(close_button_);
+}
+
+BrowserNonClientFrameViewAura::~BrowserNonClientFrameViewAura() {
+ // Don't need to remove the Widget observer, the window is deleted before us.
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// BrowserNonClientFrameViewAura, private:
+
+int BrowserNonClientFrameViewAura::NonClientHitTestImpl(
+ const gfx::Point& point) {
+ if (!GetLocalBounds().Contains(point))
+ return HTNOWHERE;
+
+ // Window controls get first try because they overlap the client area.
+ if (HitVisibleView(maximize_button_, point))
+ return HTMAXBUTTON;
+ if (HitVisibleView(close_button_, point))
+ return HTCLOSE;
+
+ int frame_component = GetWidget()->client_view()->NonClientHitTest(point);
+ if (frame_component != HTNOWHERE)
+ return frame_component;
+
+ // Test window resize components.
+ bool can_resize = GetWidget()->widget_delegate()->CanResize();
+ frame_component = GetHTComponentForFrame(point,
+ kResizeBorderThickness,
+ kResizeBorderThickness,
+ kResizeBorderThickness,
+ kResizeBorderThickness,
+ can_resize);
+ if (frame_component != HTNOWHERE)
+ return frame_component;
+ // Use HTCAPTION as a final fallback.
+ return HTCAPTION;
+}
+
+// Pass |active_window| explicitly because deactivating a window causes
+// OnWidgetActivationChanged() to be called before GetWidget()->IsActive()
+// changes state.
+gfx::Rect BrowserNonClientFrameViewAura::GetFrameBackgroundBounds(
+ int hittest_code, bool active_window) {
+ bool show_left = false;
+ bool show_top = false;
+ bool show_right = false;
+ bool show_bottom = false;
+ switch (hittest_code) {
+ case HTBOTTOM:
+ show_bottom = true;
+ break;
+ case HTBOTTOMLEFT:
+ show_bottom = true;
+ show_left = true;
+ break;
+ case HTBOTTOMRIGHT:
+ show_bottom = true;
+ show_right = true;
+ break;
+ case HTCAPTION:
+ show_top = true;
+ break;
+ case HTLEFT:
+ show_left = true;
+ break;
+ case HTRIGHT:
+ show_right = true;
+ break;
+ case HTTOP:
+ show_top = true;
+ break;
+ case HTTOPLEFT:
+ show_top = true;
+ show_left = true;
+ break;
+ case HTTOPRIGHT:
+ show_top = true;
+ show_right = true;
+ break;
+ default:
+ break;
+ }
+ // Always show top edge for the active window so that you can tell which
+ // window has focus.
+ if (active_window)
+ show_top = true;
+ gfx::Rect target = bounds();
+ // Inset the sides that are not showing.
+ target.Inset((show_left ? 0 : kResizeBorderThickness),
+ (show_top ? 0 : kTopBorderThickness + kFrameBackgroundTopOffset),
+ (show_right ? 0 : kResizeBorderThickness),
+ (show_bottom ? 0 : kResizeBorderThickness));
+ return target;
+}
+
+void BrowserNonClientFrameViewAura::UpdateFrameBackground(bool active_window) {
+ gfx::Rect start_bounds = GetFrameBackgroundBounds(HTNOWHERE, active_window);
+ gfx::Rect end_bounds =
+ GetFrameBackgroundBounds(last_hittest_code_, active_window);
+ frame_background_->Configure(start_bounds, end_bounds);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// BrowserNonClientFrameView overrides:
+
+gfx::Rect BrowserNonClientFrameViewAura::GetBoundsForTabStrip(
+ views::View* tabstrip) const {
+ if (!tabstrip)
+ return gfx::Rect();
+ // TODO(jamescook): Avatar icon support.
+ // Reserve space on the right for close/maximize buttons.
+ int tabstrip_x = kResizeBorderThickness;
+ int tabstrip_width = maximize_button_->x() - tabstrip_x;
+ return gfx::Rect(tabstrip_x,
+ GetHorizontalTabStripVerticalOffset(false),
+ tabstrip_width,
+ tabstrip->GetPreferredSize().height());
+
+}
+
+int BrowserNonClientFrameViewAura::GetHorizontalTabStripVerticalOffset(
+ bool restored) const {
+ return kTopBorderThickness;
+}
+
+void BrowserNonClientFrameViewAura::UpdateThrobber(bool running) {
+ // TODO(jamescook): Do we need this?
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// views::NonClientFrameView overrides:
+
+gfx::Rect BrowserNonClientFrameViewAura::GetBoundsForClientView() const {
+ gfx::Rect bounds = GetLocalBounds();
+ bounds.Inset(kResizeBorderThickness,
+ kTopBorderThickness,
+ kResizeBorderThickness,
+ kResizeBorderThickness);
+ return bounds;
+}
+
+gfx::Rect BrowserNonClientFrameViewAura::GetWindowBoundsForClientBounds(
+ const gfx::Rect& client_bounds) const {
+ gfx::Rect bounds = client_bounds;
+ bounds.Inset(-kResizeBorderThickness,
+ -kTopBorderThickness,
+ -kResizeBorderThickness,
+ -kResizeBorderThickness);
+ return bounds;
+}
+
+int BrowserNonClientFrameViewAura::NonClientHitTest(const gfx::Point& point) {
+ last_hittest_code_ = NonClientHitTestImpl(point);
+ return last_hittest_code_;
+}
+
+void BrowserNonClientFrameViewAura::GetWindowMask(const gfx::Size& size,
+ gfx::Path* window_mask) {
+ // Nothing.
+}
+
+void BrowserNonClientFrameViewAura::EnableClose(bool enable) {
+ close_button_->SetEnabled(enable);
+}
+
+void BrowserNonClientFrameViewAura::ResetWindowControls() {
+ maximize_button_->SetState(views::CustomButton::BS_NORMAL);
+ // The close button isn't affected by this constraint.
+}
+
+void BrowserNonClientFrameViewAura::UpdateWindowIcon() {
+ // TODO(jamescook): We will need this for app frames.
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// views::View overrides:
+
+void BrowserNonClientFrameViewAura::Layout() {
+ // Layout window buttons from right to left.
+ int right = width() - kResizeBorderThickness;
+ gfx::Size preferred = close_button_->GetPreferredSize();
+ close_button_->SetBounds(right - preferred.width(), kTopBorderThickness,
+ preferred.width(), preferred.height());
+ right -= preferred.width(); // No padding.
+ preferred = maximize_button_->GetPreferredSize();
+ maximize_button_->SetBounds(right - preferred.width(), kTopBorderThickness,
+ preferred.width(), preferred.height());
+ UpdateFrameBackground(GetWidget()->IsActive());
+}
+
+views::View* BrowserNonClientFrameViewAura::GetEventHandlerForPoint(
+ const gfx::Point& point) {
+ // Mouse hovers near the resizing edges result in the animation starting and
+ // stopping as the frame background changes size. Just ignore events
+ // destined for the frame background and handle them at this level.
+ views::View* view = View::GetEventHandlerForPoint(point);
+ if (view == frame_background_)
+ return this;
+ return view;
+}
+
+bool BrowserNonClientFrameViewAura::HitTest(const gfx::Point& p) const {
+ // Claim all events outside the client area.
+ bool in_client = GetWidget()->client_view()->bounds().Contains(p);
+ if (!in_client)
+ return true;
+ // Window controls overlap the client area, so explicitly check for points
+ // inside of them.
+ if (close_button_->bounds().Contains(p) ||
+ maximize_button_->bounds().Contains(p))
+ return true;
+ // Otherwise claim it only if it's in a non-tab portion of the tabstrip.
+ if (!browser_view_->tabstrip())
+ return false;
+ gfx::Rect tabstrip_bounds(browser_view_->tabstrip()->bounds());
+ gfx::Point tabstrip_origin(tabstrip_bounds.origin());
+ View::ConvertPointToView(
+ browser_frame_->client_view(), this, &tabstrip_origin);
+ tabstrip_bounds.set_origin(tabstrip_origin);
+ if (p.y() > tabstrip_bounds.bottom())
+ return false;
+
+ // We convert from our parent's coordinates since we assume we fill its bounds
+ // completely. We need to do this since we're not a parent of the tabstrip,
+ // meaning ConvertPointToView would otherwise return something bogus.
+ gfx::Point browser_view_point(p);
+ View::ConvertPointToView(parent(), browser_view_, &browser_view_point);
+ return browser_view_->IsPositionInWindowCaption(browser_view_point);
+}
+
+void BrowserNonClientFrameViewAura::OnMouseMoved(
+ const views::MouseEvent& event) {
+ // We may be hovering over the resize edge.
+ UpdateFrameBackground(GetWidget()->IsActive());
+ frame_background_->Show();
+}
+
+void BrowserNonClientFrameViewAura::OnMouseExited(
+ const views::MouseEvent& event) {
+ frame_background_->Hide();
+}
+
+gfx::NativeCursor BrowserNonClientFrameViewAura::GetCursor(
+ const views::MouseEvent& event) {
+ switch (last_hittest_code_) {
+ case HTBOTTOM:
+ return aura::kCursorSouthResize;
+ case HTBOTTOMLEFT:
+ return aura::kCursorSouthWestResize;
+ case HTBOTTOMRIGHT:
+ return aura::kCursorSouthEastResize;
+ case HTLEFT:
+ return aura::kCursorWestResize;
+ case HTRIGHT:
+ return aura::kCursorEastResize;
+ case HTTOP:
+ // Resizing from the top edge is not allowed.
+ return aura::kCursorNull;
+ case HTTOPLEFT:
+ return aura::kCursorWestResize;
+ case HTTOPRIGHT:
+ return aura::kCursorEastResize;
+ default:
+ return aura::kCursorNull;
+ }
+}
+
+void BrowserNonClientFrameViewAura::ViewHierarchyChanged(bool is_add,
+ views::View* parent,
+ views::View* child) {
+ if (is_add && child == this && GetWidget()) {
+ // Defer adding the observer until we have a valid window/widget.
+ GetWidget()->AddObserver(this);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// views::ButtonListener overrides:
+
+void BrowserNonClientFrameViewAura::ButtonPressed(views::Button* sender,
+ const views::Event& event) {
+ if (sender == close_button_) {
+ browser_frame_->Close();
+ } else if (sender == maximize_button_) {
+ if (browser_frame_->IsMaximized())
+ browser_frame_->Restore();
+ else
+ browser_frame_->Maximize();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// views::ButtonListener overrides:
+
+void BrowserNonClientFrameViewAura::OnWidgetActivationChanged(
+ views::Widget* widget, bool active) {
+ // Active windows have different background bounds.
+ UpdateFrameBackground(active);
+ if (active)
+ frame_background_->Show();
+ else
+ frame_background_->Hide();
+ maximize_button_->SetVisible(active);
+ close_button_->SetVisible(active);
+}
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_aura.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_aura.h
new file mode 100644
index 0000000..0bb1c90
--- /dev/null
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_aura.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2011 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.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_NON_CLIENT_FRAME_VIEW_AURA_H_
+#define CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_NON_CLIENT_FRAME_VIEW_AURA_H_
+#pragma once
+
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
+#include "views/controls/button/button.h"
+#include "views/widget/widget.h"
+
+class BrowserFrame;
+class BrowserView;
+class FrameBackground;
+class WindowControlButton;
+
+namespace gfx {
+class canvas;
+}
+namespace views {
+class CustomButton;
+}
+
+class BrowserNonClientFrameViewAura : public BrowserNonClientFrameView,
+ public views::ButtonListener,
+ public views::Widget::Observer {
+ public:
+ BrowserNonClientFrameViewAura(BrowserFrame* frame, BrowserView* browser_view);
+ virtual ~BrowserNonClientFrameViewAura();
+
+ private:
+ // Returns a HitTest code.
+ int NonClientHitTestImpl(const gfx::Point& point);
+
+ // Returns the target rectangle for the frame background, based on a mouse
+ // position from |hittest_code| and the window's active/inactive state.
+ // Pass HTNOWHERE to get default bounds.
+ gfx::Rect GetFrameBackgroundBounds(int hittest_code, bool active_window);
+
+ // Recomputes the bounds of the semi-transparent frame background.
+ void UpdateFrameBackground(bool active_window);
+
+ // BrowserNonClientFrameView overrides:
+ virtual gfx::Rect GetBoundsForTabStrip(views::View* tabstrip) const OVERRIDE;
+ virtual int GetHorizontalTabStripVerticalOffset(bool restored) const OVERRIDE;
+ virtual void UpdateThrobber(bool running) OVERRIDE;
+
+ // views::NonClientFrameView overrides:
+ virtual gfx::Rect GetBoundsForClientView() const OVERRIDE;
+ virtual gfx::Rect GetWindowBoundsForClientBounds(
+ const gfx::Rect& client_bounds) const OVERRIDE;
+ virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE;
+ virtual void GetWindowMask(const gfx::Size& size,
+ gfx::Path* window_mask) OVERRIDE;
+ virtual void EnableClose(bool enable) OVERRIDE;
+ virtual void ResetWindowControls() OVERRIDE;
+ virtual void UpdateWindowIcon() OVERRIDE;
+
+ // views::View overrides:
+ virtual void Layout() OVERRIDE;
+ virtual views::View* GetEventHandlerForPoint(
+ const gfx::Point& point) OVERRIDE;
+ virtual bool HitTest(const gfx::Point& p) const OVERRIDE;
+ virtual void OnMouseMoved(const views::MouseEvent& event) OVERRIDE;
+ virtual void OnMouseExited(const views::MouseEvent& event) OVERRIDE;
+ virtual gfx::NativeCursor GetCursor(const views::MouseEvent& event) OVERRIDE;
+ virtual void ViewHierarchyChanged(bool is_add,
+ views::View* parent,
+ views::View* child) OVERRIDE;
+
+ // views::ButtonListener overrides:
+ virtual void ButtonPressed(views::Button* sender,
+ const views::Event& event) OVERRIDE;
+
+ // views::Widget::Observer overrides:
+ virtual void OnWidgetActivationChanged(views::Widget* widget,
+ bool active) OVERRIDE;
+
+ BrowserFrame* browser_frame_;
+ BrowserView* browser_view_;
+ int last_hittest_code_;
+ WindowControlButton* maximize_button_;
+ WindowControlButton* close_button_;
+ FrameBackground* frame_background_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserNonClientFrameViewAura);
+};
+
+#endif // CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_NON_CLIENT_FRAME_VIEW_AURA_H_
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_aura.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_aura.cc
new file mode 100644
index 0000000..5c12515
--- /dev/null
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_aura.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2011 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/views/frame/browser_non_client_frame_view_aura.h"
+
+#include "chrome/browser/ui/panels/panel_browser_frame_view.h"
+#include "chrome/browser/ui/panels/panel_browser_view.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+
+namespace browser {
+
+BrowserNonClientFrameView* CreateBrowserNonClientFrameView(
+ BrowserFrame* frame, BrowserView* browser_view) {
+ if (browser_view->IsBrowserTypePanel()) {
+ return new PanelBrowserFrameView(
+ frame, static_cast<PanelBrowserView*>(browser_view));
+ }
+ return new BrowserNonClientFrameViewAura(frame, browser_view);
+}
+
+} // namespace browser