Aura: Update window frames, allow resize from outside window

* New window frames, still behind a flag as there's more refinement to do for incognito, popups, and maximized windows.
* Translucent frames now only require a single layer the size of the window instead of two.  We may be able to cut the layer size in the future to the area of the window header + toolbar
* Rewrite BrowserNonClientFrameViewAura
* Use updated art assets from crrev.com/124602
* Allow windows optionally to specify that hit tests should include a region a few pixels outside the window. For Aura browser windows this area acts as non-client area resize handles which we need because the window frame borders are now very thin or not present at all.

BUG=113075
TEST=manual, visual inspection of browser active, inactive, incognito, popup, hosted, and app windows.  Updated aura_unittest for Window to check the expanded bounds.

Review URL: https://chromiumcodereview.appspot.com/9580001

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@124743 0039d316-1c4b-4281-b951-d872f2087c98
parent 3970c423
...@@ -26,10 +26,6 @@ namespace internal { ...@@ -26,10 +26,6 @@ namespace internal {
namespace { namespace {
ShadowType GetShadowTypeFromWindow(aura::Window* window) { ShadowType GetShadowTypeFromWindow(aura::Window* window) {
// No shadow for transparent window.
if (window->transparent())
return SHADOW_TYPE_NONE;
switch (window->type()) { switch (window->type()) {
case aura::client::WINDOW_TYPE_NORMAL: case aura::client::WINDOW_TYPE_NORMAL:
case aura::client::WINDOW_TYPE_PANEL: case aura::client::WINDOW_TYPE_PANEL:
......
...@@ -72,79 +72,23 @@ void ToolbarBackground::Paint(gfx::Canvas* canvas, views::View* view) const { ...@@ -72,79 +72,23 @@ void ToolbarBackground::Paint(gfx::Canvas* canvas, views::View* view) const {
SkBitmap* toolbar_left = tp->GetBitmapNamed(IDR_CONTENT_TOP_LEFT_CORNER); SkBitmap* toolbar_left = tp->GetBitmapNamed(IDR_CONTENT_TOP_LEFT_CORNER);
int bottom_edge_height = std::min(toolbar_left->height(), h) - split_point; 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);
canvas->FillRect(gfx::Rect(x, bottom_y, w, bottom_edge_height), canvas->FillRect(gfx::Rect(x, bottom_y, w, bottom_edge_height),
tp->GetColor(ThemeService::COLOR_TOOLBAR)); tp->GetColor(ThemeService::COLOR_TOOLBAR));
// Tile the toolbar image starting at the frame edge on the left and where the // Tile the toolbar image starting at the frame edge on the left and where the
// horizontal tabstrip is (or would be) on the top. // horizontal tabstrip is (or would be) on the top.
SkBitmap* theme_toolbar = tp->GetBitmapNamed(IDR_THEME_TOOLBAR); SkBitmap* theme_toolbar = tp->GetBitmapNamed(IDR_THEME_TOOLBAR);
canvas->TileImageInt(*theme_toolbar, x, canvas->TileImageInt(*theme_toolbar,
bottom_y, x, x, bottom_y,
bottom_y, w, theme_toolbar->height()); 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 = SkBitmap* toolbar_center =
tp->GetBitmapNamed(IDR_CONTENT_TOP_CENTER); tp->GetBitmapNamed(IDR_CONTENT_TOP_CENTER);
canvas->TileImageInt(*toolbar_center, 0, 0, left_x + toolbar_left->width(), canvas->TileImageInt(*toolbar_center,
y, right_x - (left_x + toolbar_left->width()), 0, 0,
split_point); x, y,
w, 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. // Draw the content/toolbar separator.
canvas->FillRect(gfx::Rect( canvas->FillRect(gfx::Rect(
...@@ -270,10 +214,6 @@ BrowserFrameAura::BrowserFrameAura(BrowserFrame* browser_frame, ...@@ -270,10 +214,6 @@ BrowserFrameAura::BrowserFrameAura(BrowserFrame* browser_frame,
window_property_watcher_(new WindowPropertyWatcher(this, browser_frame)) { window_property_watcher_(new WindowPropertyWatcher(this, browser_frame)) {
CommandLine* command_line = CommandLine::ForCurrentProcess(); CommandLine* command_line = CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(ash::switches::kAuraTranslucentFrames)) { if (command_line->HasSwitch(ash::switches::kAuraTranslucentFrames)) {
// Aura paints layers behind this view, so this must be a layer also.
// TODO: see if we can avoid this, layers are expensive.
browser_view_->SetPaintToLayer(true);
browser_view_->layer()->SetFillsBoundsOpaquely(false);
// Background only needed for Aura-style windows. // Background only needed for Aura-style windows.
browser_view_->set_background(new ToolbarBackground(browser_view)); browser_view_->set_background(new ToolbarBackground(browser_view));
} }
......
...@@ -6,213 +6,93 @@ ...@@ -6,213 +6,93 @@
#include "chrome/browser/ui/views/frame/browser_frame.h" #include "chrome/browser/ui/views/frame/browser_frame.h"
#include "chrome/browser/ui/views/frame/browser_view.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/generated_resources.h" // Accessibility names
#include "grit/theme_resources.h"
#include "grit/theme_resources_standard.h"
#include "grit/ui_resources.h" #include "grit/ui_resources.h"
#include "third_party/skia/include/core/SkCanvas.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 "third_party/skia/include/core/SkPaint.h"
#include "ui/aura/cursor.h" #include "third_party/skia/include/core/SkPath.h"
#include "ui/base/animation/throb_animation.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/hit_test.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h" #include "ui/base/resource/resource_bundle.h"
#include "ui/base/theme_provider.h"
#include "ui/gfx/canvas.h" #include "ui/gfx/canvas.h"
#include "ui/gfx/compositor/layer.h" #include "ui/gfx/font.h"
#include "ui/views/controls/button/custom_button.h" #include "ui/views/controls/button/image_button.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h" #include "ui/views/widget/widget_delegate.h"
namespace { 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);
}
} // 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: // Size of border along top edge, used for resize handle computations.
SkColor GetBackgroundColor() { const int kTopThickness = 1;
// Background animates in separately, so handle opacity manually. // TODO(jamescook): Border is specified to be a single pixel overlapping
return SkColorSetARGB(hover_animation_->CurrentValueBetween(0, 150), // the web content and may need to be built into the shadow layers instead.
SkColorGetR(color_), const int kBorderThickness = 0;
SkColorGetG(color_), // Number of pixels outside the window frame to look for resize events.
SkColorGetB(color_)); 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.
BrowserNonClientFrameViewAura* owner_; const int kResizeAreaCornerSize = 16;
SkColor color_; // Space between left edge of window and popup window icon.
SkBitmap icon_; const int kIconOffsetX = 4;
scoped_ptr<ui::SlideAnimation> show_animation_; // Space between top of window and popup window icon.
const int kIconOffsetY = 4;
DISALLOW_COPY_AND_ASSIGN(WindowControlButton); // Height and width of window icon.
}; const int kIconSize = 16;
// Space between the title text and the caption buttons.
// Layer that visually sits "behind" the window contents and expands out to const int kTitleLogoSpacing = 5;
// provide visual resize handles on the sides. Hit testing and resize handling // Space between title text and icon.
// is in the parent NonClientFrameView. const int kTitleOffsetX = 4;
class FrameBackgroundView : public views::View, // Space between title text and top of window.
public ui::AnimationDelegate { const int kTitleOffsetY = 6;
public: // Space between close button and right edge of window.
FrameBackgroundView() const int kCloseButtonOffsetX = 0;
: ALLOW_THIS_IN_INITIALIZER_LIST( // Space between close button and top edge of window.
size_animation_(new ui::SlideAnimation(this))), const int kCloseButtonOffsetY = 0;
ALLOW_THIS_IN_INITIALIZER_LIST( // Space between left edge of window and tabstrip.
color_animation_(new ui::SlideAnimation(this))) { const int kTabstripLeftSpacing = 4;
size_animation_->SetSlideDuration(kHoverFadeDurationMs); // Space between right edge of tabstrip and maximize button.
color_animation_->SetSlideDuration(kHoverFadeDurationMs); const int kTabstripRightSpacing = 10;
SetPaintToLayer(true); // Space between top of window and top of tabstrip for restored windows.
UpdateOpacity(); const int kTabstripTopSpacingRestored = 10;
} // Space between top of window and top of tabstrip for maximized windows.
virtual ~FrameBackgroundView() { const int kTabstripTopSpacingMaximized = 1;
}
// Tiles an image into an area, rounding the top corners.
void Configure(const gfx::Rect& start_bounds, const gfx::Rect& end_bounds) { void TileRoundRect(gfx::Canvas* canvas,
start_bounds_ = start_bounds; int x, int y, int w, int h,
end_bounds_ = end_bounds; const SkBitmap& bitmap,
UpdateBounds(); int corner_radius) {
} SkRect rect;
void SetEndBounds(const gfx::Rect& end_bounds) { rect.iset(x, y, x + w, y + h);
end_bounds_ = end_bounds; const SkScalar kRadius = SkIntToScalar(corner_radius);
UpdateBounds(); SkScalar radii[8] = {
} kRadius, kRadius, // top-left
void Show() { kRadius, kRadius, // top-right
size_animation_->Show(); 0, 0, // bottom-right
color_animation_->Show(); 0, 0}; // bottom-left
} SkPath path;
void Hide() { path.addRoundRect(rect, radii, SkPath::kCW_Direction);
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; SkPaint paint;
// Animation handles setting the opacity for the whole layer. SkShader* shader = SkShader::CreateBitmapShader(bitmap,
paint.setColor(kFrameColor); SkShader::kRepeat_TileMode,
paint.setStyle(SkPaint::kFill_Style); SkShader::kRepeat_TileMode);
canvas->GetSkCanvas()->drawRoundRect(rect, radius, radius, paint); paint.setShader(shader);
} paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
// CreateBitmapShader returns a Shader with a reference count of one, we
// Overridden from ui::AnimationDelegate: // need to unref after paint takes ownership of the shader.
virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE { shader->unref();
if (animation == color_animation_.get()) { canvas->GetSkCanvas()->drawPath(path, paint);
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); } // namespace
};
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// BrowserNonClientFrameViewAura, public: // BrowserNonClientFrameViewAura, public:
...@@ -220,163 +100,67 @@ class FrameBackgroundView : public views::View, ...@@ -220,163 +100,67 @@ class FrameBackgroundView : public views::View,
BrowserNonClientFrameViewAura::BrowserNonClientFrameViewAura( BrowserNonClientFrameViewAura::BrowserNonClientFrameViewAura(
BrowserFrame* frame, BrowserView* browser_view) BrowserFrame* frame, BrowserView* browser_view)
: BrowserNonClientFrameView(frame, browser_view), : BrowserNonClientFrameView(frame, browser_view),
last_hittest_code_(HTNOWHERE) { maximize_button_(NULL),
frame_background_ = new FrameBackgroundView(); close_button_(NULL),
AddChildView(frame_background_); window_icon_(NULL),
button_separator_(NULL),
ResourceBundle& rb = ResourceBundle::GetSharedInstance(); top_left_corner_(NULL),
maximize_button_ = top_edge_(NULL),
new WindowControlButton(this, top_right_corner_(NULL),
kMaximizeButtonBackgroundColor, header_left_edge_(NULL),
*rb.GetBitmapNamed(IDR_AURA_WINDOW_ZOOM_ICON)); 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( maximize_button_->SetAccessibleName(
l10n_util::GetStringUTF16(IDS_ACCNAME_MAXIMIZE)); l10n_util::GetStringUTF16(IDS_ACCNAME_MAXIMIZE));
AddChildView(maximize_button_); AddChildView(maximize_button_);
close_button_ = new views::ImageButton(this);
close_button_ = SetButtonImages(close_button_,
new WindowControlButton(this, IDR_AURA_WINDOW_CLOSE,
kCloseButtonBackgroundColor, IDR_AURA_WINDOW_CLOSE_H,
*rb.GetBitmapNamed(IDR_AURA_WINDOW_CLOSE_ICON)); IDR_AURA_WINDOW_CLOSE_P);
close_button_->SetAccessibleName( close_button_->SetAccessibleName(
l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE)); l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE));
AddChildView(close_button_); AddChildView(close_button_);
// The BrowserFrame will become our owning window/widget. // Window frame image parts.
frame->AsWidget()->AddObserver(this); ResourceBundle& bundle = ResourceBundle::GetSharedInstance();
// Associate our WindowFrame interface with our owning window/widget so button_separator_ =
// we get callbacks from aura_shell. bundle.GetBitmapNamed(IDR_AURA_WINDOW_BUTTON_SEPARATOR);
frame->AsWidget()->GetNativeWindow()->SetProperty( top_left_corner_ =
ash::kWindowFrameKey, bundle.GetBitmapNamed(IDR_AURA_WINDOW_HEADER_SHADE_TOP_LEFT);
static_cast<ash::WindowFrame*>(this)); top_edge_ =
} bundle.GetBitmapNamed(IDR_AURA_WINDOW_HEADER_SHADE_TOP);
top_right_corner_ =
BrowserNonClientFrameViewAura::~BrowserNonClientFrameViewAura() { bundle.GetBitmapNamed(IDR_AURA_WINDOW_HEADER_SHADE_TOP_RIGHT);
// Don't need to remove the Widget observer, the window is deleted before us. header_left_edge_ =
} bundle.GetBitmapNamed(IDR_AURA_WINDOW_HEADER_SHADE_LEFT);
header_right_edge_ =
void BrowserNonClientFrameViewAura::ShowFrameBackground() { bundle.GetBitmapNamed(IDR_AURA_WINDOW_HEADER_SHADE_RIGHT);
UpdateFrameBackground(ShouldPaintAsActive()); // TODO(jamescook): Should we paint the header bottom edge here, or keep
frame_background_->Show(); // it in BrowserFrameAura::ToolbarBackground as we do now?
}
// Initializing the TabIconView is expensive, so only do it if we need to.
void BrowserNonClientFrameViewAura::HideFrameBackground() { if (browser_view()->ShouldShowWindowIcon()) {
frame_background_->Hide(); window_icon_ = new TabIconView(this);
} window_icon_->set_is_light(true);
AddChildView(window_icon_);
/////////////////////////////////////////////////////////////////////////////// window_icon_->Update();
// 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;
} }
// 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( ...@@ -386,56 +170,83 @@ gfx::Rect BrowserNonClientFrameViewAura::GetBoundsForTabStrip(
views::View* tabstrip) const { views::View* tabstrip) const {
if (!tabstrip) if (!tabstrip)
return gfx::Rect(); return gfx::Rect();
// TODO(jamescook): Avatar icon support. bool restored = !frame()->IsMaximized();
// Reserve space on the right for close/maximize buttons. return gfx::Rect(kTabstripLeftSpacing,
int tabstrip_x = kResizeBorderThickness; GetHorizontalTabStripVerticalOffset(restored),
int tabstrip_width = maximize_button_->x() - tabstrip_x; maximize_button_->x() - kTabstripRightSpacing,
return gfx::Rect(tabstrip_x,
GetHorizontalTabStripVerticalOffset(false),
tabstrip_width,
tabstrip->GetPreferredSize().height()); tabstrip->GetPreferredSize().height());
} }
int BrowserNonClientFrameViewAura::GetHorizontalTabStripVerticalOffset( int BrowserNonClientFrameViewAura::GetHorizontalTabStripVerticalOffset(
bool restored) const { bool restored) const {
return kTopBorderThickness; return NonClientTopBorderHeight(restored);
} }
void BrowserNonClientFrameViewAura::UpdateThrobber(bool running) { void BrowserNonClientFrameViewAura::UpdateThrobber(bool running) {
// TODO(jamescook): Do we need this? if (window_icon_)
window_icon_->Update();
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// views::NonClientFrameView overrides: // views::NonClientFrameView overrides:
gfx::Rect BrowserNonClientFrameViewAura::GetBoundsForClientView() const { gfx::Rect BrowserNonClientFrameViewAura::GetBoundsForClientView() const {
gfx::Rect bounds = GetLocalBounds(); int top_height = NonClientTopBorderHeight(false);
bounds.Inset(kResizeBorderThickness, return gfx::Rect(kBorderThickness,
kTopBorderThickness, top_height,
kResizeBorderThickness, std::max(0, width() - (2 * kBorderThickness)),
kResizeBorderThickness); std::max(0, height() - top_height - kBorderThickness));
return bounds;
} }
gfx::Rect BrowserNonClientFrameViewAura::GetWindowBoundsForClientBounds( gfx::Rect BrowserNonClientFrameViewAura::GetWindowBoundsForClientBounds(
const gfx::Rect& client_bounds) const { const gfx::Rect& client_bounds) const {
gfx::Rect bounds = client_bounds; int top_height = NonClientTopBorderHeight(false);
bounds.Inset(-kResizeBorderThickness, return gfx::Rect(std::max(0, client_bounds.x() - kBorderThickness),
-kTopBorderThickness, std::max(0, client_bounds.y() - top_height),
-kResizeBorderThickness, client_bounds.width() + (2 * kBorderThickness),
-kResizeBorderThickness); client_bounds.height() + top_height + kBorderThickness);
return bounds;
} }
int BrowserNonClientFrameViewAura::NonClientHitTest(const gfx::Point& point) { int BrowserNonClientFrameViewAura::NonClientHitTest(const gfx::Point& point) {
last_hittest_code_ = NonClientHitTestImpl(point); gfx::Rect expanded_bounds = bounds();
return last_hittest_code_; 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, void BrowserNonClientFrameViewAura::GetWindowMask(const gfx::Size& size,
gfx::Path* window_mask) { gfx::Path* window_mask) {
// Nothing. // Aura does not use window masks.
} }
void BrowserNonClientFrameViewAura::ResetWindowControls() { void BrowserNonClientFrameViewAura::ResetWindowControls() {
...@@ -444,104 +255,92 @@ void BrowserNonClientFrameViewAura::ResetWindowControls() { ...@@ -444,104 +255,92 @@ void BrowserNonClientFrameViewAura::ResetWindowControls() {
} }
void BrowserNonClientFrameViewAura::UpdateWindowIcon() { void BrowserNonClientFrameViewAura::UpdateWindowIcon() {
// TODO(jamescook): We will need this for app frames. if (window_icon_)
} window_icon_->SchedulePaint();
void BrowserNonClientFrameViewAura::ShouldPaintAsActiveChanged() {
ActiveStateChanged();
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// views::View overrides: // views::View overrides:
void BrowserNonClientFrameViewAura::Layout() { void BrowserNonClientFrameViewAura::OnPaint(gfx::Canvas* canvas) {
// Layout window buttons from right to left. if (frame()->IsFullscreen())
int right = width() - kResizeBorderThickness; return; // Nothing visible, don't paint.
gfx::Size preferred = close_button_->GetPreferredSize(); PaintHeader(canvas);
close_button_->SetBounds(right - preferred.width(), kTopBorderThickness, PaintTitleBar(canvas);
preferred.width(), preferred.height()); // Paint the view hierarchy, which draws the caption buttons.
right -= preferred.width(); // No padding. BrowserNonClientFrameView::OnPaint(canvas);
preferred = maximize_button_->GetPreferredSize();
maximize_button_->SetBounds(right - preferred.width(), kTopBorderThickness,
preferred.width(), preferred.height());
UpdateFrameBackground(ShouldPaintAsActive());
} }
views::View* BrowserNonClientFrameViewAura::GetEventHandlerForPoint( void BrowserNonClientFrameViewAura::Layout() {
const gfx::Point& point) { // Maximized windows and app/popup windows use shorter buttons.
// Mouse hovers near the resizing edges result in the animation starting and if (frame()->IsMaximized() ||
// stopping as the frame background changes size. Just ignore events !browser_view()->IsBrowserTypeNormal()) {
// destined for the frame background and handle them at this level. SetButtonImages(close_button_,
views::View* view = View::GetEventHandlerForPoint(point); IDR_AURA_WINDOW_MAXIMIZED_CLOSE,
if (view == frame_background_) IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H,
return this; IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P);
return view; 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 { bool BrowserNonClientFrameViewAura::HitTest(const gfx::Point& l) const {
// Claim all events outside the client area. // If the point is outside the bounds of the client area, claim it.
bool in_client = GetWidget()->client_view()->bounds().Contains(p); if (NonClientFrameView::HitTest(l))
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; return true;
// Otherwise claim it only if it's in a non-tab portion of the tabstrip. // Otherwise claim it only if it's in a non-tab portion of the tabstrip.
if (!browser_view()->tabstrip()) if (!browser_view()->tabstrip())
return false; return false;
gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds()); gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds());
gfx::Point tabstrip_origin(tabstrip_bounds.origin()); gfx::Point tabstrip_origin(tabstrip_bounds.origin());
View::ConvertPointToView( View::ConvertPointToView(frame()->client_view(), this, &tabstrip_origin);
frame()->client_view(), this, &tabstrip_origin);
tabstrip_bounds.set_origin(tabstrip_origin); tabstrip_bounds.set_origin(tabstrip_origin);
if (p.y() > tabstrip_bounds.bottom()) if (l.y() > tabstrip_bounds.bottom())
return false; return false;
// We convert from our parent's coordinates since we assume we fill its bounds // 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, // completely. We need to do this since we're not a parent of the tabstrip,
// meaning ConvertPointToView would otherwise return something bogus. // 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); View::ConvertPointToView(parent(), browser_view(), &browser_view_point);
return browser_view()->IsPositionInWindowCaption(browser_view_point); return browser_view()->IsPositionInWindowCaption(browser_view_point);
} }
void BrowserNonClientFrameViewAura::OnMouseMoved( void BrowserNonClientFrameViewAura::GetAccessibleState(
const views::MouseEvent& event) { ui::AccessibleViewState* state) {
// We may be hovering over the resize edge. state->role = ui::AccessibilityTypes::ROLE_TITLEBAR;
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;
}
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
...@@ -549,34 +348,191 @@ gfx::NativeCursor BrowserNonClientFrameViewAura::GetCursor( ...@@ -549,34 +348,191 @@ gfx::NativeCursor BrowserNonClientFrameViewAura::GetCursor(
void BrowserNonClientFrameViewAura::ButtonPressed(views::Button* sender, void BrowserNonClientFrameViewAura::ButtonPressed(views::Button* sender,
const views::Event& event) { const views::Event& event) {
if (sender == close_button_) { if (sender == maximize_button_) {
frame()->Close();
} else if (sender == maximize_button_) {
if (frame()->IsMaximized()) if (frame()->IsMaximized())
frame()->Restore(); frame()->Restore();
else else
frame()->Maximize(); 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( SkBitmap BrowserNonClientFrameViewAura::GetFaviconForTabIconView() {
views::Widget* widget, views::WidgetDelegate* delegate = frame()->widget_delegate();
bool active) { if (!delegate)
ActiveStateChanged(); 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));
}
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::OnWindowHoverChanged(bool hovered) { void BrowserNonClientFrameViewAura::PaintHeader(gfx::Canvas* canvas) {
if (hovered) { // The primary header image changes based on window activation state and
maximize_button_->Show(); // theme, so we look it up for each paint.
close_button_->Show(); 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 { } else {
maximize_button_->Hide(); resource_id = is_incognito ?
close_button_->Hide(); 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);
} }
// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
...@@ -6,43 +6,24 @@ ...@@ -6,43 +6,24 @@
#define 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 #pragma once
#include "ash/wm/window_frame.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h" #include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
#include "ui/views/controls/button/button.h" #include "chrome/browser/ui/views/tab_icon_view.h"
#include "ui/views/widget/widget.h" #include "ui/views/controls/button/button.h" // ButtonListener
class BrowserFrame; namespace views {
class BrowserView; class FrameBackground;
class FrameBackgroundView; class ImageButton;
class WindowControlButton; }
class BrowserNonClientFrameViewAura : public BrowserNonClientFrameView, class BrowserNonClientFrameViewAura : public BrowserNonClientFrameView,
public views::ButtonListener, public views::ButtonListener,
public views::Widget::Observer, public TabIconView::TabIconViewModel {
public ash::WindowFrame {
public: public:
BrowserNonClientFrameViewAura(BrowserFrame* frame, BrowserView* browser_view); BrowserNonClientFrameViewAura(BrowserFrame* frame, BrowserView* browser_view);
virtual ~BrowserNonClientFrameViewAura(); virtual ~BrowserNonClientFrameViewAura();
// Control the slide-in animation of the frame background. void Init();
void ShowFrameBackground();
void HideFrameBackground();
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);
// Invoked when the active state changes.
void ActiveStateChanged();
// BrowserNonClientFrameView overrides: // BrowserNonClientFrameView overrides:
virtual gfx::Rect GetBoundsForTabStrip(views::View* tabstrip) const OVERRIDE; virtual gfx::Rect GetBoundsForTabStrip(views::View* tabstrip) const OVERRIDE;
...@@ -58,32 +39,57 @@ class BrowserNonClientFrameViewAura : public BrowserNonClientFrameView, ...@@ -58,32 +39,57 @@ class BrowserNonClientFrameViewAura : public BrowserNonClientFrameView,
gfx::Path* window_mask) OVERRIDE; gfx::Path* window_mask) OVERRIDE;
virtual void ResetWindowControls() OVERRIDE; virtual void ResetWindowControls() OVERRIDE;
virtual void UpdateWindowIcon() OVERRIDE; virtual void UpdateWindowIcon() OVERRIDE;
virtual void ShouldPaintAsActiveChanged() OVERRIDE;
// views::View overrides: // views::View overrides:
virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
virtual void Layout() OVERRIDE; virtual void Layout() OVERRIDE;
virtual views::View* GetEventHandlerForPoint( virtual bool HitTest(const gfx::Point& l) const OVERRIDE;
const gfx::Point& point) OVERRIDE; virtual void GetAccessibleState(ui::AccessibleViewState* state) 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;
// views::ButtonListener overrides: // views::ButtonListener overrides:
virtual void ButtonPressed(views::Button* sender, virtual void ButtonPressed(views::Button* sender,
const views::Event& event) OVERRIDE; const views::Event& event) OVERRIDE;
// views::Widget::Observer overrides: // Overridden from TabIconView::TabIconViewModel:
virtual void OnWidgetActivationChanged(views::Widget* widget, virtual bool ShouldTabIconViewAnimate() const OVERRIDE;
bool active) OVERRIDE; virtual SkBitmap GetFaviconForTabIconView() OVERRIDE;
// ash::WindowFrame overrides:
virtual void OnWindowHoverChanged(bool hovered) OVERRIDE;
int last_hittest_code_; private:
WindowControlButton* maximize_button_; // Sets the images for a button base on IDs from the frame's theme provider.
WindowControlButton* close_button_; void SetButtonImages(views::ImageButton* button,
FrameBackgroundView* frame_background_; int normal_bitmap_id,
int hot_bitmap_id,
int pushed_bitmap_id);
// Distance between top of window and client area.
int NonClientTopBorderHeight(bool restored) const;
void PaintHeader(gfx::Canvas* canvas);
void PaintTitleBar(gfx::Canvas* canvas);
// Returns the correct bitmap for the frame header based on activation state
// and incognito mode.
SkBitmap* GetThemeFrameBitmap() const;
SkBitmap* GetThemeFrameOverlayBitmap() const;
// Returns the theme image bitmap for |bitmap_id| if the user has a custom
// theme image, or the bitmap for |fallback_bitmap_id| if not.
SkBitmap* GetCustomBitmap(int bitmap_id, int fallback_bitmap_id) const;
// Window controls.
views::ImageButton* maximize_button_;
views::ImageButton* close_button_;
// For popups, the window icon.
TabIconView* window_icon_;
// Window frame header/caption parts.
SkBitmap* button_separator_;
SkBitmap* top_left_corner_;
SkBitmap* top_edge_;
SkBitmap* top_right_corner_;
SkBitmap* header_left_edge_;
SkBitmap* header_right_edge_;
DISALLOW_COPY_AND_ASSIGN(BrowserNonClientFrameViewAura); DISALLOW_COPY_AND_ASSIGN(BrowserNonClientFrameViewAura);
}; };
......
...@@ -27,14 +27,18 @@ BrowserNonClientFrameView* CreateBrowserNonClientFrameView( ...@@ -27,14 +27,18 @@ BrowserNonClientFrameView* CreateBrowserNonClientFrameView(
if (ash::Shell::GetInstance()->IsWindowModeCompact()) if (ash::Shell::GetInstance()->IsWindowModeCompact())
return new CompactBrowserFrameView(frame, browser_view); return new CompactBrowserFrameView(frame, browser_view);
CommandLine* command_line = CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(ash::switches::kAuraTranslucentFrames))
return new BrowserNonClientFrameViewAura(frame, browser_view);
// If this is an app window and it's maximized, use the special frame_view. // If this is an app window and it's maximized, use the special frame_view.
if (browser_view->browser()->is_app() && browser_view->IsMaximized()) if (browser_view->browser()->is_app() && browser_view->IsMaximized())
return new AppNonClientFrameViewAura(frame, browser_view); return new AppNonClientFrameViewAura(frame, browser_view);
CommandLine* command_line = CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(ash::switches::kAuraTranslucentFrames)) {
BrowserNonClientFrameViewAura* frame_view =
new BrowserNonClientFrameViewAura(frame, browser_view);
frame_view->Init();
return frame_view;
}
return new OpaqueBrowserFrameView(frame, browser_view); return new OpaqueBrowserFrameView(frame, browser_view);
} }
......
...@@ -54,7 +54,8 @@ Window::Window(WindowDelegate* delegate) ...@@ -54,7 +54,8 @@ Window::Window(WindowDelegate* delegate)
transparent_(false), transparent_(false),
user_data_(NULL), user_data_(NULL),
stops_event_propagation_(false), stops_event_propagation_(false),
ignore_events_(false) { ignore_events_(false),
hit_test_bounds_inset_(0) {
} }
Window::~Window() { Window::~Window() {
...@@ -401,8 +402,10 @@ bool Window::ContainsPoint(const gfx::Point& local_point) { ...@@ -401,8 +402,10 @@ bool Window::ContainsPoint(const gfx::Point& local_point) {
} }
bool Window::HitTest(const gfx::Point& local_point) { bool Window::HitTest(const gfx::Point& local_point) {
gfx::Rect local_bounds(gfx::Point(), bounds().size());
local_bounds.Inset(hit_test_bounds_inset_, hit_test_bounds_inset_);
// TODO(beng): hittest masks. // TODO(beng): hittest masks.
return ContainsPoint(local_point); return local_bounds.Contains(local_point);
} }
Window* Window::GetEventHandlerForPoint(const gfx::Point& local_point) { Window* Window::GetEventHandlerForPoint(const gfx::Point& local_point) {
......
...@@ -225,6 +225,12 @@ class AURA_EXPORT Window : public ui::LayerDelegate { ...@@ -225,6 +225,12 @@ class AURA_EXPORT Window : public ui::LayerDelegate {
void set_ignore_events(bool ignore_events) { ignore_events_ = ignore_events; } void set_ignore_events(bool ignore_events) { ignore_events_ = ignore_events; }
// When non-zero insets the window's bounds by |inset| when performing hit
// tests for event handling. Pass a negative value for |inset| to respond to
// events that occur slightly outside a window's bounds.
void set_hit_test_bounds_inset(int inset) { hit_test_bounds_inset_ = inset; }
int hit_test_bounds_inset() const { return hit_test_bounds_inset_; }
// Returns true if the |point_in_root| in root window's coordinate falls // Returns true if the |point_in_root| in root window's coordinate falls
// within this window's bounds. Returns false if the window is detached // within this window's bounds. Returns false if the window is detached
// from root window. // from root window.
...@@ -411,6 +417,9 @@ class AURA_EXPORT Window : public ui::LayerDelegate { ...@@ -411,6 +417,9 @@ class AURA_EXPORT Window : public ui::LayerDelegate {
// Makes the window pass all events through to any windows behind it. // Makes the window pass all events through to any windows behind it.
bool ignore_events_; bool ignore_events_;
// Inset the window bounds by this amount when doing hit testing for events.
int hit_test_bounds_inset_;
ObserverList<WindowObserver> observers_; ObserverList<WindowObserver> observers_;
std::map<const void*, intptr_t> prop_map_; std::map<const void*, intptr_t> prop_map_;
......
...@@ -273,7 +273,7 @@ TEST_F(WindowTest, HitTest) { ...@@ -273,7 +273,7 @@ TEST_F(WindowTest, HitTest) {
Window w1(new ColorTestWindowDelegate(SK_ColorWHITE)); Window w1(new ColorTestWindowDelegate(SK_ColorWHITE));
w1.set_id(1); w1.set_id(1);
w1.Init(ui::Layer::LAYER_TEXTURED); w1.Init(ui::Layer::LAYER_TEXTURED);
w1.SetBounds(gfx::Rect(10, 10, 50, 50)); w1.SetBounds(gfx::Rect(10, 20, 50, 60));
w1.Show(); w1.Show();
w1.SetParent(NULL); w1.SetParent(NULL);
...@@ -281,6 +281,11 @@ TEST_F(WindowTest, HitTest) { ...@@ -281,6 +281,11 @@ TEST_F(WindowTest, HitTest) {
EXPECT_TRUE(w1.HitTest(gfx::Point(1, 1))); EXPECT_TRUE(w1.HitTest(gfx::Point(1, 1)));
EXPECT_FALSE(w1.HitTest(gfx::Point(-1, -1))); EXPECT_FALSE(w1.HitTest(gfx::Point(-1, -1)));
// We can expand the bounds slightly to track events outside our border.
w1.set_hit_test_bounds_inset(-1);
EXPECT_TRUE(w1.HitTest(gfx::Point(-1, -1)));
EXPECT_FALSE(w1.HitTest(gfx::Point(-2, -2)));
// TODO(beng): clip Window to parent. // TODO(beng): clip Window to parent.
} }
......
...@@ -48,6 +48,8 @@ void PrintLayerHierarchyImp(const Layer* layer, int indent, ...@@ -48,6 +48,8 @@ void PrintLayerHierarchyImp(const Layer* layer, int indent,
break; break;
case Layer::LAYER_TEXTURED: case Layer::LAYER_TEXTURED:
buf << L" textured"; buf << L" textured";
if (layer->fills_bounds_opaquely())
buf << L" opaque";
break; break;
case Layer::LAYER_SOLID_COLOR: case Layer::LAYER_SOLID_COLOR:
buf << L" solid"; buf << L" solid";
......
...@@ -196,13 +196,42 @@ ...@@ -196,13 +196,42 @@
<include name="IDR_AURA_WALLPAPER_8_THUMB" file="aura/wallpaper_8_thumb.jpg" type="BINDATA" /> <include name="IDR_AURA_WALLPAPER_8_THUMB" file="aura/wallpaper_8_thumb.jpg" type="BINDATA" />
<include name="IDR_AURA_WALLPAPER_9" file="aura/wallpaper_9.jpg" type="BINDATA" /> <include name="IDR_AURA_WALLPAPER_9" file="aura/wallpaper_9.jpg" type="BINDATA" />
<include name="IDR_AURA_WALLPAPER_9_THUMB" file="aura/wallpaper_9_thumb.jpg" type="BINDATA" /> <include name="IDR_AURA_WALLPAPER_9_THUMB" file="aura/wallpaper_9_thumb.jpg" type="BINDATA" />
<!-- TODO(jamescook): Delete these two corners if we ship translucent frames on all platforms. -->
<include name="IDR_AURA_WINDOW_BOTTOM_LEFT" file="aura/window_bottom_left.png" type="BINDATA" /> <include name="IDR_AURA_WINDOW_BOTTOM_LEFT" file="aura/window_bottom_left.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_BOTTOM_RIGHT" file="aura/window_bottom_right.png" type="BINDATA" /> <include name="IDR_AURA_WINDOW_BOTTOM_RIGHT" file="aura/window_bottom_right.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_BUTTON_SEPARATOR" file="aura/window_button_separator.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_CLOSE_ICON" file="aura/slab_close.png" type="BINDATA" /> <include name="IDR_AURA_WINDOW_CLOSE_ICON" file="aura/slab_close.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_CLOSE" file="aura/window_close_normal.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_CLOSE_H" file="aura/window_close_hot.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_CLOSE_P" file="aura/window_close_pressed.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_FULLSCREEN_CLOSE" file="aura/window_fullscreen_close.png" type="BINDATA" /> <include name="IDR_AURA_WINDOW_FULLSCREEN_CLOSE" file="aura/window_fullscreen_close.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_FULLSCREEN_RESTORE" file="aura/window_fullscreen_restore.png" type="BINDATA" /> <include name="IDR_AURA_WINDOW_FULLSCREEN_RESTORE" file="aura/window_fullscreen_restore.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_FULLSCREEN_SEPARATOR" file="aura/window_fullscreen_separator.png" type="BINDATA" /> <include name="IDR_AURA_WINDOW_FULLSCREEN_SEPARATOR" file="aura/window_fullscreen_separator.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_FULLSCREEN_SHADOW" file="aura/window_fullscreen_shadow.png" type="BINDATA" /> <include name="IDR_AURA_WINDOW_FULLSCREEN_SHADOW" file="aura/window_fullscreen_shadow.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_HEADER_BASE_ACTIVE" file="aura/window_header_base_active.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_HEADER_BASE_INACTIVE" file="aura/window_header_base_inactive.png" type="BINDATA" />
<!-- TODO(jamescook): We need updated incognito art for Aura. -->
<include name="IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_ACTIVE" file="aura/window_header_base_incognito_active.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_INACTIVE" file="aura/window_header_base_incognito_inactive.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_HEADER_SHADE_BOTTOM" file="aura/window_header_shade_bottom.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_HEADER_SHADE_BOTTOM_LEFT" file="aura/window_header_shade_bottom_left.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_HEADER_SHADE_BOTTOM_RIGHT" file="aura/window_header_shade_bottom_right.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_HEADER_SHADE_LEFT" file="aura/window_header_shade_left.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_HEADER_SHADE_MIDDLE" file="aura/window_header_shade_middle.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_HEADER_SHADE_RIGHT" file="aura/window_header_shade_right.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_HEADER_SHADE_TOP" file="aura/window_header_shade_top.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_HEADER_SHADE_TOP_LEFT" file="aura/window_header_shade_top_left.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_HEADER_SHADE_TOP_RIGHT" file="aura/window_header_shade_top_right.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_MAXIMIZE" file="aura/window_maximize_normal.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_MAXIMIZE_H" file="aura/window_maximize_hot.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_MAXIMIZE_P" file="aura/window_maximize_pressed.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_MAXIMIZED_CLOSE" file="aura/window_maximized_close_normal.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H" file="aura/window_maximized_close_hot.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P" file="aura/window_maximized_close_pressed.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_MAXIMIZED_RESTORE" file="aura/window_maximized_restore_normal.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H" file="aura/window_maximized_restore_hot.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P" file="aura/window_maximized_restore_pressed.png" type="BINDATA" />
<!-- TODO(jamescook): Delete these two corners if we ship translucent frames on all platforms. -->
<include name="IDR_AURA_WINDOW_TOP_LEFT" file="aura/window_top_left.png" type="BINDATA" /> <include name="IDR_AURA_WINDOW_TOP_LEFT" file="aura/window_top_left.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_TOP_RIGHT" file="aura/window_top_right.png" type="BINDATA" /> <include name="IDR_AURA_WINDOW_TOP_RIGHT" file="aura/window_top_right.png" type="BINDATA" />
<include name="IDR_AURA_WINDOW_ZOOM_ICON" file="aura/slab_zoom.png" type="BINDATA" /> <include name="IDR_AURA_WINDOW_ZOOM_ICON" file="aura/slab_zoom.png" type="BINDATA" />
......
...@@ -305,6 +305,10 @@ void RootView::OnMouseMoved(const MouseEvent& event) { ...@@ -305,6 +305,10 @@ void RootView::OnMouseMoved(const MouseEvent& event) {
widget_->SetCursor(mouse_move_handler_->GetCursor(moved_event)); widget_->SetCursor(mouse_move_handler_->GetCursor(moved_event));
} else if (mouse_move_handler_ != NULL) { } else if (mouse_move_handler_ != NULL) {
mouse_move_handler_->OnMouseExited(e); mouse_move_handler_->OnMouseExited(e);
// On Aura the non-client area extends slightly outside the root view for
// some windows. Let the non-client cursor handling code set the cursor
// as we do above.
if (!(e.flags() & ui::EF_IS_NON_CLIENT))
widget_->SetCursor(gfx::kNullCursor); widget_->SetCursor(gfx::kNullCursor);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment