diff options
author | jamescook@chromium.org <jamescook@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-25 22:52:24 +0000 |
---|---|---|
committer | jamescook@chromium.org <jamescook@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-25 22:52:24 +0000 |
commit | da93d567b20e325537e14abe508748d9d695f6c5 (patch) | |
tree | a3e84803e2354aa4c6429b495d49f5c9a10fd608 /chrome/browser | |
parent | 594e66fe7b8cff055de73257cbad8c01f9c0c5ac (diff) | |
download | chromium_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')
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 |