Index: chrome/browser/ui/views/frame/browser_non_client_frame_view_aura.cc |
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 |
index b44cba650fe1996c94ddb147a4f2ac8b52d8f522..f47526ccd6bb5a1521c0aeaeea7208958035149a 100644 |
--- 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 |
@@ -6,377 +6,161 @@ |
#include "chrome/browser/ui/views/frame/browser_frame.h" |
#include "chrome/browser/ui/views/frame/browser_view.h" |
+#include "content/public/browser/web_contents.h" |
#include "grit/generated_resources.h" // Accessibility names |
+#include "grit/theme_resources.h" |
+#include "grit/theme_resources_standard.h" |
#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 "third_party/skia/include/core/SkPath.h" |
+#include "third_party/skia/include/core/SkShader.h" |
+#include "ui/aura/window.h" |
+#include "ui/base/accessibility/accessible_view_state.h" |
#include "ui/base/hit_test.h" |
#include "ui/base/l10n/l10n_util.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/views/controls/button/custom_button.h" |
+#include "ui/gfx/font.h" |
+#include "ui/views/controls/button/image_button.h" |
#include "ui/views/widget/widget.h" |
#include "ui/views/widget/widget_delegate.h" |
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; |
- |
-// Width of a persistent border that we show around the window (using |
-// FrameBackground) even when the resize border isn't visible. |
-const int kPersistentBorderThickness = 2; |
- |
-// 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; |
- |
-// 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->visible() && view->GetMirroredBounds().Contains(point); |
+ |
+// Size of border along top edge, used for resize handle computations. |
+const int kTopThickness = 1; |
+// TODO(jamescook): Border is specified to be a single pixel overlapping |
+// the web content and may need to be built into the shadow layers instead. |
+const int kBorderThickness = 0; |
+// Number of pixels outside the window frame to look for resize events. |
+const int kResizeAreaOutsideBounds = 6; |
+// In the window corners, the resize areas don't actually expand bigger, but the |
+// 16 px at the end of each edge triggers diagonal resizing. |
+const int kResizeAreaCornerSize = 16; |
+// Space between left edge of window and popup window icon. |
+const int kIconOffsetX = 4; |
+// Space between top of window and popup window icon. |
+const int kIconOffsetY = 4; |
+// Height and width of window icon. |
+const int kIconSize = 16; |
+// Space between the title text and the caption buttons. |
+const int kTitleLogoSpacing = 5; |
+// Space between title text and icon. |
+const int kTitleOffsetX = 4; |
+// Space between title text and top of window. |
+const int kTitleOffsetY = 6; |
+// Space between close button and right edge of window. |
+const int kCloseButtonOffsetX = 0; |
+// Space between close button and top edge of window. |
+const int kCloseButtonOffsetY = 0; |
+// Space between left edge of window and tabstrip. |
+const int kTabstripLeftSpacing = 4; |
+// Space between right edge of tabstrip and maximize button. |
+const int kTabstripRightSpacing = 10; |
+// Space between top of window and top of tabstrip for restored windows. |
+const int kTabstripTopSpacingRestored = 10; |
+// Space between top of window and top of tabstrip for maximized windows. |
+const int kTabstripTopSpacingMaximized = 1; |
+ |
+// Tiles an image into an area, rounding the top corners. |
+void TileRoundRect(gfx::Canvas* canvas, |
+ int x, int y, int w, int h, |
+ const SkBitmap& bitmap, |
+ int corner_radius) { |
+ SkRect rect; |
+ rect.iset(x, y, x + w, y + h); |
+ const SkScalar kRadius = SkIntToScalar(corner_radius); |
+ SkScalar radii[8] = { |
+ kRadius, kRadius, // top-left |
+ kRadius, kRadius, // top-right |
+ 0, 0, // bottom-right |
+ 0, 0}; // bottom-left |
+ SkPath path; |
+ path.addRoundRect(rect, radii, SkPath::kCW_Direction); |
+ |
+ SkPaint paint; |
+ SkShader* shader = SkShader::CreateBitmapShader(bitmap, |
+ SkShader::kRepeat_TileMode, |
+ SkShader::kRepeat_TileMode); |
+ paint.setShader(shader); |
+ paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); |
+ // CreateBitmapShader returns a Shader with a reference count of one, we |
+ // need to unref after paint takes ownership of the shader. |
+ shader->unref(); |
+ canvas->GetSkCanvas()->drawPath(path, paint); |
} |
} // namespace |
-// Buttons for window controls - close, zoom, etc. |
-// Note that views::CustomButton is already a ui::AnimationDelegate. |
-class WindowControlButton : public views::CustomButton { |
- public: |
- WindowControlButton(BrowserNonClientFrameViewAura* owner, |
- SkColor color, |
- const SkBitmap& icon) |
- : views::CustomButton(owner), |
- owner_(owner), |
- color_(color), |
- icon_(icon), |
- ALLOW_THIS_IN_INITIALIZER_LIST( |
- show_animation_(new ui::SlideAnimation(this))) { |
- show_animation_->SetSlideDuration(kHoverFadeDurationMs); |
- SetPaintToLayer(true); |
- layer()->SetFillsBoundsOpaquely(false); |
- layer()->SetOpacity(0.f); |
- } |
- virtual ~WindowControlButton() {} |
- |
- void Show() { |
- show_animation_->Show(); |
- } |
- void Hide() { |
- show_animation_->Hide(); |
- } |
- |
- // Overridden from views::View: |
- virtual void OnMouseEntered(const views::MouseEvent& event) OVERRIDE { |
- // Ensure the caption/frame background shows when we hover this button. |
- owner_->ShowFrameBackground(); |
- views::CustomButton::OnMouseEntered(event); |
- } |
- virtual void OnMouseExited(const views::MouseEvent& event) OVERRIDE { |
- owner_->HideFrameBackground(); |
- views::CustomButton::OnMouseExited(event); |
- } |
- virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { |
- canvas->FillRect(GetLocalBounds(), GetBackgroundColor()); |
- canvas->DrawBitmapInt(icon_, 0, 0); |
- } |
- virtual gfx::Size GetPreferredSize() OVERRIDE { |
- return gfx::Size(icon_.width(), icon_.height()); |
- } |
- |
- // Overridden from ui::AnimationDelegate: |
- virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE { |
- if (animation == show_animation_.get()) { |
- double opacity = show_animation_->GetCurrentValue(); |
- layer()->SetOpacity(static_cast<float>(opacity)); |
- return; |
- } |
- views::CustomButton::AnimationProgressed(animation); |
- } |
- |
- private: |
- SkColor GetBackgroundColor() { |
- // Background animates in separately, so handle opacity manually. |
- return SkColorSetARGB(hover_animation_->CurrentValueBetween(0, 150), |
- SkColorGetR(color_), |
- SkColorGetG(color_), |
- SkColorGetB(color_)); |
- } |
- |
- BrowserNonClientFrameViewAura* owner_; |
- SkColor color_; |
- SkBitmap icon_; |
- scoped_ptr<ui::SlideAnimation> show_animation_; |
- |
- 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 FrameBackgroundView : public views::View, |
- public ui::AnimationDelegate { |
- public: |
- FrameBackgroundView() |
- : 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 ~FrameBackgroundView() { |
- } |
- |
- 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(FrameBackgroundView); |
-}; |
- |
/////////////////////////////////////////////////////////////////////////////// |
// BrowserNonClientFrameViewAura, public: |
BrowserNonClientFrameViewAura::BrowserNonClientFrameViewAura( |
BrowserFrame* frame, BrowserView* browser_view) |
: BrowserNonClientFrameView(frame, browser_view), |
- last_hittest_code_(HTNOWHERE) { |
- frame_background_ = new FrameBackgroundView(); |
- AddChildView(frame_background_); |
- |
- ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
- maximize_button_ = |
- new WindowControlButton(this, |
- kMaximizeButtonBackgroundColor, |
- *rb.GetBitmapNamed(IDR_AURA_WINDOW_ZOOM_ICON)); |
+ maximize_button_(NULL), |
+ close_button_(NULL), |
+ window_icon_(NULL), |
+ button_separator_(NULL), |
+ top_left_corner_(NULL), |
+ top_edge_(NULL), |
+ top_right_corner_(NULL), |
+ header_left_edge_(NULL), |
+ header_right_edge_(NULL) { |
+} |
+ |
+BrowserNonClientFrameViewAura::~BrowserNonClientFrameViewAura() { |
+} |
+ |
+void BrowserNonClientFrameViewAura::Init() { |
+ // Ensure we get resize cursors for a few pixels outside our bounds. |
+ frame()->GetNativeWindow()->set_hit_test_bounds_inset( |
+ -kResizeAreaOutsideBounds); |
+ |
+ // Caption buttons. |
+ maximize_button_ = new views::ImageButton(this); |
+ SetButtonImages(maximize_button_, |
+ IDR_AURA_WINDOW_MAXIMIZE, |
+ IDR_AURA_WINDOW_MAXIMIZE_H, |
+ IDR_AURA_WINDOW_MAXIMIZE_P); |
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_ = new views::ImageButton(this); |
+ SetButtonImages(close_button_, |
+ IDR_AURA_WINDOW_CLOSE, |
+ IDR_AURA_WINDOW_CLOSE_H, |
+ IDR_AURA_WINDOW_CLOSE_P); |
close_button_->SetAccessibleName( |
l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE)); |
AddChildView(close_button_); |
- // The BrowserFrame will become our owning window/widget. |
- frame->AsWidget()->AddObserver(this); |
- // Associate our WindowFrame interface with our owning window/widget so |
- // we get callbacks from aura_shell. |
- frame->AsWidget()->GetNativeWindow()->SetProperty( |
- ash::kWindowFrameKey, |
- static_cast<ash::WindowFrame*>(this)); |
-} |
- |
-BrowserNonClientFrameViewAura::~BrowserNonClientFrameViewAura() { |
- // Don't need to remove the Widget observer, the window is deleted before us. |
-} |
- |
-void BrowserNonClientFrameViewAura::ShowFrameBackground() { |
- UpdateFrameBackground(ShouldPaintAsActive()); |
- frame_background_->Show(); |
-} |
- |
-void BrowserNonClientFrameViewAura::HideFrameBackground() { |
- frame_background_->Hide(); |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
-// 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(); |
- // TODO(derat): Disallow resizing via the top border in the Aura shell |
- // instead of enforcing it here. See http://crbug.com/101830. |
- frame_component = GetHTComponentForFrame(point, |
- 0, |
- 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 HTCLOSE: |
- show_top = true; |
- break; |
- case HTLEFT: |
- show_left = true; |
- break; |
- case HTMAXBUTTON: |
- show_top = 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; |
+ // Window frame image parts. |
+ ResourceBundle& bundle = ResourceBundle::GetSharedInstance(); |
+ button_separator_ = |
+ bundle.GetBitmapNamed(IDR_AURA_WINDOW_BUTTON_SEPARATOR); |
+ top_left_corner_ = |
+ bundle.GetBitmapNamed(IDR_AURA_WINDOW_HEADER_SHADE_TOP_LEFT); |
+ top_edge_ = |
+ bundle.GetBitmapNamed(IDR_AURA_WINDOW_HEADER_SHADE_TOP); |
+ top_right_corner_ = |
+ bundle.GetBitmapNamed(IDR_AURA_WINDOW_HEADER_SHADE_TOP_RIGHT); |
+ header_left_edge_ = |
+ bundle.GetBitmapNamed(IDR_AURA_WINDOW_HEADER_SHADE_LEFT); |
+ header_right_edge_ = |
+ bundle.GetBitmapNamed(IDR_AURA_WINDOW_HEADER_SHADE_RIGHT); |
+ // TODO(jamescook): Should we paint the header bottom edge here, or keep |
+ // it in BrowserFrameAura::ToolbarBackground as we do now? |
+ |
+ // Initializing the TabIconView is expensive, so only do it if we need to. |
+ if (browser_view()->ShouldShowWindowIcon()) { |
+ window_icon_ = new TabIconView(this); |
+ window_icon_->set_is_light(true); |
+ AddChildView(window_icon_); |
+ window_icon_->Update(); |
} |
- // 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 - kPersistentBorderThickness), |
- (show_top ? 0 : kTopBorderThickness + kFrameBackgroundTopOffset), |
- (show_right ? 0 : kResizeBorderThickness - kPersistentBorderThickness), |
- (show_bottom ? 0 : kResizeBorderThickness - kPersistentBorderThickness)); |
- 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); |
-} |
- |
-void BrowserNonClientFrameViewAura::ActiveStateChanged() { |
- bool active = ShouldPaintAsActive(); |
- // Active windows have different background bounds. |
- UpdateFrameBackground(active); |
- if (active) |
- frame_background_->Show(); |
- else |
- frame_background_->Hide(); |
} |
/////////////////////////////////////////////////////////////////////////////// |
@@ -386,56 +170,83 @@ 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, |
+ bool restored = !frame()->IsMaximized(); |
+ return gfx::Rect(kTabstripLeftSpacing, |
+ GetHorizontalTabStripVerticalOffset(restored), |
+ maximize_button_->x() - kTabstripRightSpacing, |
tabstrip->GetPreferredSize().height()); |
- |
} |
int BrowserNonClientFrameViewAura::GetHorizontalTabStripVerticalOffset( |
bool restored) const { |
- return kTopBorderThickness; |
+ return NonClientTopBorderHeight(restored); |
} |
void BrowserNonClientFrameViewAura::UpdateThrobber(bool running) { |
- // TODO(jamescook): Do we need this? |
+ if (window_icon_) |
+ window_icon_->Update(); |
} |
/////////////////////////////////////////////////////////////////////////////// |
// views::NonClientFrameView overrides: |
gfx::Rect BrowserNonClientFrameViewAura::GetBoundsForClientView() const { |
- gfx::Rect bounds = GetLocalBounds(); |
- bounds.Inset(kResizeBorderThickness, |
- kTopBorderThickness, |
- kResizeBorderThickness, |
- kResizeBorderThickness); |
- return bounds; |
+ int top_height = NonClientTopBorderHeight(false); |
+ return gfx::Rect(kBorderThickness, |
+ top_height, |
+ std::max(0, width() - (2 * kBorderThickness)), |
+ std::max(0, height() - top_height - kBorderThickness)); |
} |
gfx::Rect BrowserNonClientFrameViewAura::GetWindowBoundsForClientBounds( |
const gfx::Rect& client_bounds) const { |
- gfx::Rect bounds = client_bounds; |
- bounds.Inset(-kResizeBorderThickness, |
- -kTopBorderThickness, |
- -kResizeBorderThickness, |
- -kResizeBorderThickness); |
- return bounds; |
+ int top_height = NonClientTopBorderHeight(false); |
+ return gfx::Rect(std::max(0, client_bounds.x() - kBorderThickness), |
+ std::max(0, client_bounds.y() - top_height), |
+ client_bounds.width() + (2 * kBorderThickness), |
+ client_bounds.height() + top_height + kBorderThickness); |
} |
int BrowserNonClientFrameViewAura::NonClientHitTest(const gfx::Point& point) { |
- last_hittest_code_ = NonClientHitTestImpl(point); |
- return last_hittest_code_; |
+ gfx::Rect expanded_bounds = bounds(); |
+ expanded_bounds.Inset(-kResizeAreaOutsideBounds, -kResizeAreaOutsideBounds); |
+ if (!expanded_bounds.Contains(point)) |
+ return HTNOWHERE; |
+ |
+ // No avatar button for Chrome OS. |
+ |
+ // Check the client view first, as it overlaps the window caption area. |
+ int client_component = frame()->client_view()->NonClientHitTest(point); |
+ if (client_component != HTNOWHERE) |
+ return client_component; |
+ |
+ // Then see if the point is within any of the window controls. |
+ if (close_button_->visible() && |
+ close_button_->GetMirroredBounds().Contains(point)) |
+ return HTCLOSE; |
+ if (maximize_button_->visible() && |
+ maximize_button_->GetMirroredBounds().Contains(point)) |
+ return HTMAXBUTTON; |
+ |
+ bool can_resize = frame()->widget_delegate() ? |
+ frame()->widget_delegate()->CanResize() : |
+ false; |
+ int frame_component = GetHTComponentForFrame(point, |
+ kTopThickness, |
+ kBorderThickness, |
+ kResizeAreaCornerSize, |
+ kResizeAreaCornerSize, |
+ can_resize); |
+ if (frame_component != HTNOWHERE) |
+ return frame_component; |
+ |
+ // Caption is a safe default. |
+ return HTCAPTION; |
} |
void BrowserNonClientFrameViewAura::GetWindowMask(const gfx::Size& size, |
gfx::Path* window_mask) { |
- // Nothing. |
+ // Aura does not use window masks. |
} |
void BrowserNonClientFrameViewAura::ResetWindowControls() { |
@@ -444,104 +255,92 @@ void BrowserNonClientFrameViewAura::ResetWindowControls() { |
} |
void BrowserNonClientFrameViewAura::UpdateWindowIcon() { |
- // TODO(jamescook): We will need this for app frames. |
-} |
- |
-void BrowserNonClientFrameViewAura::ShouldPaintAsActiveChanged() { |
- ActiveStateChanged(); |
+ if (window_icon_) |
+ window_icon_->SchedulePaint(); |
} |
/////////////////////////////////////////////////////////////////////////////// |
// 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(ShouldPaintAsActive()); |
+void BrowserNonClientFrameViewAura::OnPaint(gfx::Canvas* canvas) { |
+ if (frame()->IsFullscreen()) |
+ return; // Nothing visible, don't paint. |
+ PaintHeader(canvas); |
+ PaintTitleBar(canvas); |
+ // Paint the view hierarchy, which draws the caption buttons. |
+ BrowserNonClientFrameView::OnPaint(canvas); |
} |
-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; |
+void BrowserNonClientFrameViewAura::Layout() { |
+ // Maximized windows and app/popup windows use shorter buttons. |
+ if (frame()->IsMaximized() || |
+ !browser_view()->IsBrowserTypeNormal()) { |
+ SetButtonImages(close_button_, |
+ IDR_AURA_WINDOW_MAXIMIZED_CLOSE, |
+ IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H, |
+ IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P); |
+ SetButtonImages(maximize_button_, |
+ IDR_AURA_WINDOW_MAXIMIZED_RESTORE, |
+ IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H, |
+ IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P); |
+ } else { |
+ SetButtonImages(close_button_, |
+ IDR_AURA_WINDOW_CLOSE, |
+ IDR_AURA_WINDOW_CLOSE_H, |
+ IDR_AURA_WINDOW_CLOSE_P); |
+ SetButtonImages(maximize_button_, |
+ IDR_AURA_WINDOW_MAXIMIZE, |
+ IDR_AURA_WINDOW_MAXIMIZE_H, |
+ IDR_AURA_WINDOW_MAXIMIZE_P); |
+ } |
+ |
+ gfx::Size close_size = close_button_->GetPreferredSize(); |
+ close_button_->SetBounds( |
+ width() - close_size.width() - kCloseButtonOffsetX, |
+ kCloseButtonOffsetY, |
+ close_size.width(), |
+ close_size.height()); |
+ |
+ gfx::Size maximize_size = maximize_button_->GetPreferredSize(); |
+ maximize_button_->SetBounds( |
+ close_button_->x() - button_separator_->width() - maximize_size.width(), |
+ close_button_->y(), |
+ maximize_size.width(), |
+ maximize_size.height()); |
+ |
+ if (window_icon_) |
+ window_icon_->SetBoundsRect( |
+ gfx::Rect(kIconOffsetX, kIconOffsetY, kIconSize, kIconSize)); |
+ |
+ BrowserNonClientFrameView::Layout(); |
} |
-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)) |
+bool BrowserNonClientFrameViewAura::HitTest(const gfx::Point& l) const { |
+ // If the point is outside the bounds of the client area, claim it. |
+ if (NonClientFrameView::HitTest(l)) |
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( |
- frame()->client_view(), this, &tabstrip_origin); |
+ View::ConvertPointToView(frame()->client_view(), this, &tabstrip_origin); |
tabstrip_bounds.set_origin(tabstrip_origin); |
- if (p.y() > tabstrip_bounds.bottom()) |
+ if (l.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); |
+ gfx::Point browser_view_point(l); |
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. |
- ShowFrameBackground(); |
-} |
- |
-void BrowserNonClientFrameViewAura::OnMouseExited( |
- const views::MouseEvent& event) { |
- // We hovered away from the resize edge. |
- HideFrameBackground(); |
-} |
- |
-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::GetAccessibleState( |
+ ui::AccessibleViewState* state) { |
+ state->role = ui::AccessibilityTypes::ROLE_TITLEBAR; |
} |
/////////////////////////////////////////////////////////////////////////////// |
@@ -549,34 +348,191 @@ gfx::NativeCursor BrowserNonClientFrameViewAura::GetCursor( |
void BrowserNonClientFrameViewAura::ButtonPressed(views::Button* sender, |
const views::Event& event) { |
- if (sender == close_button_) { |
- frame()->Close(); |
- } else if (sender == maximize_button_) { |
+ if (sender == maximize_button_) { |
if (frame()->IsMaximized()) |
frame()->Restore(); |
else |
frame()->Maximize(); |
+ // The maximize button may have moved out from under the cursor. |
+ ResetWindowControls(); |
+ } else if (sender == close_button_) { |
+ frame()->Close(); |
} |
} |
/////////////////////////////////////////////////////////////////////////////// |
-// views::Widget::Observer overrides: |
+// TabIconView::TabIconViewModel overrides: |
+ |
+bool BrowserNonClientFrameViewAura::ShouldTabIconViewAnimate() const { |
+ // This function is queried during the creation of the window as the |
+ // TabIconView we host is initialized, so we need to NULL check the selected |
+ // WebContents because in this condition there is not yet a selected tab. |
+ content::WebContents* current_tab = browser_view()->GetSelectedWebContents(); |
+ return current_tab ? current_tab->IsLoading() : false; |
+} |
-void BrowserNonClientFrameViewAura::OnWidgetActivationChanged( |
- views::Widget* widget, |
- bool active) { |
- ActiveStateChanged(); |
+SkBitmap BrowserNonClientFrameViewAura::GetFaviconForTabIconView() { |
+ views::WidgetDelegate* delegate = frame()->widget_delegate(); |
+ if (!delegate) |
+ return SkBitmap(); |
+ return delegate->GetWindowIcon(); |
} |
/////////////////////////////////////////////////////////////////////////////// |
-// ash::WindowFrame overrides: |
+// BrowserNonClientFrameViewAura, private: |
+ |
+void BrowserNonClientFrameViewAura::SetButtonImages(views::ImageButton* button, |
+ int normal_bitmap_id, |
+ int hot_bitmap_id, |
+ int pushed_bitmap_id) { |
+ ui::ThemeProvider* tp = frame()->GetThemeProvider(); |
+ button->SetImage(views::CustomButton::BS_NORMAL, |
+ tp->GetBitmapNamed(normal_bitmap_id)); |
+ button->SetImage(views::CustomButton::BS_HOT, |
+ tp->GetBitmapNamed(hot_bitmap_id)); |
+ button->SetImage(views::CustomButton::BS_PUSHED, |
+ tp->GetBitmapNamed(pushed_bitmap_id)); |
+} |
-void BrowserNonClientFrameViewAura::OnWindowHoverChanged(bool hovered) { |
- if (hovered) { |
- maximize_button_->Show(); |
- close_button_->Show(); |
+int BrowserNonClientFrameViewAura::NonClientTopBorderHeight( |
+ bool restored) const { |
+ if (frame()->widget_delegate() && |
+ frame()->widget_delegate()->ShouldShowWindowTitle()) { |
+ // For popups ensure we have enough space to see the full window buttons. |
+ return kCloseButtonOffsetY + close_button_->height(); |
+ } |
+ if (restored) |
+ return kTabstripTopSpacingRestored; |
+ return kTabstripTopSpacingMaximized; |
+} |
+ |
+void BrowserNonClientFrameViewAura::PaintHeader(gfx::Canvas* canvas) { |
+ // The primary header image changes based on window activation state and |
+ // theme, so we look it up for each paint. |
+ SkBitmap* theme_frame = GetThemeFrameBitmap(); |
+ SkBitmap* theme_frame_overlay = GetThemeFrameOverlayBitmap(); |
+ |
+ // Draw the header background, clipping the corners to be rounded. |
+ const int kCornerRadius = 2; |
+ TileRoundRect(canvas, |
+ 0, 0, width(), theme_frame->height(), |
+ *theme_frame, |
+ kCornerRadius); |
+ |
+ // Draw the theme frame overlay, if available. |
+ if (theme_frame_overlay) |
+ canvas->DrawBitmapInt(*theme_frame_overlay, 0, 0); |
+ |
+ // Separator between the maximize and close buttons. |
+ canvas->DrawBitmapInt(*button_separator_, |
+ close_button_->x() - button_separator_->width(), |
+ close_button_->y()); |
+ |
+ // Draw the top corners and edge. |
+ int top_left_height = top_left_corner_->height(); |
+ canvas->DrawBitmapInt(*top_left_corner_, |
+ 0, 0, top_left_corner_->width(), top_left_height, |
+ 0, 0, top_left_corner_->width(), top_left_height, |
+ false); |
+ canvas->TileImageInt(*top_edge_, |
+ top_left_corner_->width(), |
+ 0, |
+ width() - top_left_corner_->width() - top_right_corner_->width(), |
+ top_edge_->height()); |
+ int top_right_height = top_right_corner_->height(); |
+ canvas->DrawBitmapInt(*top_right_corner_, |
+ 0, 0, |
+ top_right_corner_->width(), top_right_height, |
+ width() - top_right_corner_->width(), 0, |
+ top_right_corner_->width(), top_right_height, |
+ false); |
+ |
+ // Header left edge. |
+ int header_left_height = theme_frame->height() - top_left_height; |
+ canvas->TileImageInt(*header_left_edge_, |
+ 0, top_left_height, |
+ header_left_edge_->width(), header_left_height); |
+ |
+ // Header right edge. |
+ int header_right_height = theme_frame->height() - top_right_height; |
+ canvas->TileImageInt(*header_right_edge_, |
+ width() - header_right_edge_->width(), top_right_height, |
+ header_right_edge_->width(), header_right_height); |
+ |
+ // We don't draw edges around the content area. Web content goes flush |
+ // to the edge of the window. |
+} |
+ |
+void BrowserNonClientFrameViewAura::PaintTitleBar(gfx::Canvas* canvas) { |
+ // The window icon is painted by the TabIconView. |
+ views::WidgetDelegate* delegate = frame()->widget_delegate(); |
+ if (delegate && delegate->ShouldShowWindowTitle()) { |
+ int icon_right = window_icon_ ? window_icon_->bounds().right() : 0; |
+ gfx::Rect title_bounds( |
+ icon_right + kTitleOffsetX, |
+ kTitleOffsetY, |
+ std::max(0, maximize_button_->x() - kTitleLogoSpacing - icon_right), |
+ BrowserFrame::GetTitleFont().GetHeight()); |
+ canvas->DrawStringInt(delegate->GetWindowTitle(), |
+ BrowserFrame::GetTitleFont(), |
+ SK_ColorWHITE, |
+ GetMirroredXForRect(title_bounds), |
+ title_bounds.y(), |
+ title_bounds.width(), |
+ title_bounds.height()); |
+ } |
+} |
+ |
+SkBitmap* BrowserNonClientFrameViewAura::GetThemeFrameBitmap() const { |
+ bool is_incognito = browser_view()->IsOffTheRecord(); |
+ int resource_id; |
+ if (browser_view()->IsBrowserTypeNormal()) { |
+ if (ShouldPaintAsActive()) { |
+ // Use the standard resource ids to allow users to theme the frames. |
+ // TODO(jamescook): If this becomes the only frame we use on Aura, define |
+ // the resources to use the standard ids like IDR_THEME_FRAME, etc. |
+ if (is_incognito) { |
+ return GetCustomBitmap(IDR_THEME_FRAME_INCOGNITO, |
+ IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_ACTIVE); |
+ } |
+ return GetCustomBitmap(IDR_THEME_FRAME, |
+ IDR_AURA_WINDOW_HEADER_BASE_ACTIVE); |
+ } |
+ if (is_incognito) { |
+ return GetCustomBitmap(IDR_THEME_FRAME_INCOGNITO_INACTIVE, |
+ IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_INACTIVE); |
+ } |
+ return GetCustomBitmap(IDR_THEME_FRAME_INACTIVE, |
+ IDR_AURA_WINDOW_HEADER_BASE_INACTIVE); |
+ } |
+ // Never theme app and popup windows. |
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
+ if (ShouldPaintAsActive()) { |
+ resource_id = is_incognito ? |
+ IDR_THEME_FRAME_INCOGNITO : IDR_FRAME; |
} else { |
- maximize_button_->Hide(); |
- close_button_->Hide(); |
+ resource_id = is_incognito ? |
+ IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE; |
} |
+ return rb.GetBitmapNamed(resource_id); |
+} |
+ |
+SkBitmap* BrowserNonClientFrameViewAura::GetThemeFrameOverlayBitmap() const { |
+ ui::ThemeProvider* tp = GetThemeProvider(); |
+ if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) && |
+ browser_view()->IsBrowserTypeNormal() && |
+ !browser_view()->IsOffTheRecord()) { |
+ return tp->GetBitmapNamed(ShouldPaintAsActive() ? |
+ IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE); |
+ } |
+ return NULL; |
+} |
+ |
+SkBitmap* BrowserNonClientFrameViewAura::GetCustomBitmap( |
+ int bitmap_id, |
+ int fallback_bitmap_id) const { |
+ ui::ThemeProvider* tp = GetThemeProvider(); |
+ if (tp->HasCustomImage(bitmap_id)) |
+ return tp->GetBitmapNamed(bitmap_id); |
+ return tp->GetBitmapNamed(fallback_bitmap_id); |
} |