Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(48)

Side by Side Diff: chrome/browser/ui/views/frame/browser_non_client_frame_view_aura.cc

Issue 9580001: Aura: Update window frames, allow resize from outside window (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix caption for maximized windows, new resize cursor tweak Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view_aura.h" 5 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view_aura.h"
6 6
7 #include "chrome/browser/ui/views/frame/browser_frame.h" 7 #include "chrome/browser/ui/views/frame/browser_frame.h"
8 #include "chrome/browser/ui/views/frame/browser_view.h" 8 #include "chrome/browser/ui/views/frame/browser_view.h"
9 #include "content/public/browser/web_contents.h"
9 #include "grit/generated_resources.h" // Accessibility names 10 #include "grit/generated_resources.h" // Accessibility names
11 #include "grit/theme_resources.h"
12 #include "grit/theme_resources_standard.h"
10 #include "grit/ui_resources.h" 13 #include "grit/ui_resources.h"
11 #include "third_party/skia/include/core/SkCanvas.h" 14 #include "third_party/skia/include/core/SkCanvas.h"
12 #include "third_party/skia/include/core/SkColor.h"
13 #include "third_party/skia/include/core/SkPaint.h" 15 #include "third_party/skia/include/core/SkPaint.h"
14 #include "ui/aura/cursor.h" 16 #include "third_party/skia/include/core/SkPath.h"
15 #include "ui/base/animation/throb_animation.h" 17 #include "third_party/skia/include/core/SkShader.h"
18 #include "ui/aura/window.h"
19 #include "ui/base/accessibility/accessible_view_state.h"
16 #include "ui/base/hit_test.h" 20 #include "ui/base/hit_test.h"
17 #include "ui/base/l10n/l10n_util.h" 21 #include "ui/base/l10n/l10n_util.h"
18 #include "ui/base/resource/resource_bundle.h" 22 #include "ui/base/resource/resource_bundle.h"
23 #include "ui/base/theme_provider.h"
19 #include "ui/gfx/canvas.h" 24 #include "ui/gfx/canvas.h"
20 #include "ui/gfx/compositor/layer.h" 25 #include "ui/gfx/font.h"
21 #include "ui/views/controls/button/custom_button.h" 26 #include "ui/views/controls/button/image_button.h"
22 #include "ui/views/widget/widget.h" 27 #include "ui/views/widget/widget.h"
23 #include "ui/views/widget/widget_delegate.h" 28 #include "ui/views/widget/widget_delegate.h"
24 29
25 namespace { 30 namespace {
26 // Our window is larger than it appears, as it includes space around the edges
27 // where resize handles can appear.
28 const int kResizeBorderThickness = 8;
29 // The top edge is a little thinner, as it is not draggable for resize.
30 const int kTopBorderThickness = 4;
31 // Offset between top of non-client frame and top edge of opaque frame
32 // background at start of slide-in animation.
33 const int kFrameBackgroundTopOffset = 25;
34 31
35 // Width of a persistent border that we show around the window (using 32 // Size of border along top edge, used for resize handle computations.
36 // FrameBackground) even when the resize border isn't visible. 33 const int kTopThickness = 1;
37 const int kPersistentBorderThickness = 2; 34 // TODO(jamescook): Border is specified to be a single pixel overlapping
35 // the web content and may need to be built into the shadow layers instead.
36 const int kBorderThickness = 0;
37 // Number of pixels outside the window frame to look for resize events.
38 const int kResizeAreaOutsideBounds = 6;
39 // In the window corners, the resize areas don't actually expand bigger, but the
40 // 16 px at the end of each edge triggers diagonal resizing.
41 const int kResizeAreaCornerSize = 16;
42 // Space between left edge of window and popup window icon.
43 const int kIconOffsetX = 4;
44 // Space between top of window and popup window icon.
45 const int kIconOffsetY = 4;
46 // Height and width of window icon.
47 const int kIconSize = 16;
48 // Space between the title text and the caption buttons.
49 const int kTitleLogoSpacing = 5;
50 // Space between title text and icon.
51 const int kTitleOffsetX = 4;
52 // Space between title text and top of window.
53 const int kTitleOffsetY = 6;
54 // Space between close button and right edge of window.
55 const int kCloseButtonOffsetX = 0;
56 // Space between close button and top edge of window.
57 const int kCloseButtonOffsetY = 0;
58 // Space between left edge of window and tabstrip.
59 const int kTabstripLeftSpacing = 4;
60 // Space between right edge of tabstrip and maximize button.
61 const int kTabstripRightSpacing = 10;
62 // Space between top of window and top of tabstrip for restored windows.
63 const int kTabstripTopSpacingRestored = 10;
64 // Space between top of window and top of tabstrip for maximized windows.
65 const int kTabstripTopSpacingMaximized = 1;
38 66
39 // The color used to fill the frame. Opacity is handled in the layer. 67 // Tiles an image into an area, rounding the top corners.
40 const SkColor kFrameColor = SK_ColorBLACK; 68 void TileRoundRect(gfx::Canvas* canvas,
41 // Radius of rounded rectangle corners. 69 int x, int y, int w, int h,
42 const int kRoundedRectRadius = 3; 70 const SkBitmap& bitmap,
43 // Frame border fades in over this range of opacity. 71 int corner_radius) {
44 const double kFrameBorderStartOpacity = 0.2; 72 SkRect rect;
45 const double kFrameBorderEndOpacity = 0.3; 73 rect.iset(x, y, x + w, y + h);
46 // How long the hover animation takes if uninterrupted. 74 const SkScalar kRadius = SkIntToScalar(corner_radius);
47 const int kHoverFadeDurationMs = 250; 75 SkScalar radii[8] = {
76 kRadius, kRadius, // top-left
77 kRadius, kRadius, // top-right
78 0, 0, // bottom-right
79 0, 0}; // bottom-left
80 SkPath path;
81 path.addRoundRect(rect, radii, SkPath::kCW_Direction);
48 82
49 // Color shown when window control is hovered. 83 SkPaint paint;
50 const SkColor kMaximizeButtonBackgroundColor = SkColorSetRGB(0, 255, 0); 84 SkShader* shader = SkShader::CreateBitmapShader(bitmap,
51 const SkColor kCloseButtonBackgroundColor = SkColorSetRGB(255, 0, 0); 85 SkShader::kRepeat_TileMode,
52 86 SkShader::kRepeat_TileMode);
53 bool HitVisibleView(views::View* view, gfx::Point point) { 87 paint.setShader(shader);
54 return view->visible() && view->GetMirroredBounds().Contains(point); 88 paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
89 // CreateBitmapShader returns a Shader with a reference count of one, we
90 // need to unref after paint takes ownership of the shader.
91 shader->unref();
92 canvas->GetSkCanvas()->drawPath(path, paint);
55 } 93 }
56 94
57 } // namespace 95 } // namespace
58 96
59 // Buttons for window controls - close, zoom, etc.
60 // Note that views::CustomButton is already a ui::AnimationDelegate.
61 class WindowControlButton : public views::CustomButton {
62 public:
63 WindowControlButton(BrowserNonClientFrameViewAura* owner,
64 SkColor color,
65 const SkBitmap& icon)
66 : views::CustomButton(owner),
67 owner_(owner),
68 color_(color),
69 icon_(icon),
70 ALLOW_THIS_IN_INITIALIZER_LIST(
71 show_animation_(new ui::SlideAnimation(this))) {
72 show_animation_->SetSlideDuration(kHoverFadeDurationMs);
73 SetPaintToLayer(true);
74 layer()->SetFillsBoundsOpaquely(false);
75 layer()->SetOpacity(0.f);
76 }
77 virtual ~WindowControlButton() {}
78
79 void Show() {
80 show_animation_->Show();
81 }
82 void Hide() {
83 show_animation_->Hide();
84 }
85
86 // Overridden from views::View:
87 virtual void OnMouseEntered(const views::MouseEvent& event) OVERRIDE {
88 // Ensure the caption/frame background shows when we hover this button.
89 owner_->ShowFrameBackground();
90 views::CustomButton::OnMouseEntered(event);
91 }
92 virtual void OnMouseExited(const views::MouseEvent& event) OVERRIDE {
93 owner_->HideFrameBackground();
94 views::CustomButton::OnMouseExited(event);
95 }
96 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
97 canvas->FillRect(GetLocalBounds(), GetBackgroundColor());
98 canvas->DrawBitmapInt(icon_, 0, 0);
99 }
100 virtual gfx::Size GetPreferredSize() OVERRIDE {
101 return gfx::Size(icon_.width(), icon_.height());
102 }
103
104 // Overridden from ui::AnimationDelegate:
105 virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE {
106 if (animation == show_animation_.get()) {
107 double opacity = show_animation_->GetCurrentValue();
108 layer()->SetOpacity(static_cast<float>(opacity));
109 return;
110 }
111 views::CustomButton::AnimationProgressed(animation);
112 }
113
114 private:
115 SkColor GetBackgroundColor() {
116 // Background animates in separately, so handle opacity manually.
117 return SkColorSetARGB(hover_animation_->CurrentValueBetween(0, 150),
118 SkColorGetR(color_),
119 SkColorGetG(color_),
120 SkColorGetB(color_));
121 }
122
123 BrowserNonClientFrameViewAura* owner_;
124 SkColor color_;
125 SkBitmap icon_;
126 scoped_ptr<ui::SlideAnimation> show_animation_;
127
128 DISALLOW_COPY_AND_ASSIGN(WindowControlButton);
129 };
130
131 // Layer that visually sits "behind" the window contents and expands out to
132 // provide visual resize handles on the sides. Hit testing and resize handling
133 // is in the parent NonClientFrameView.
134 class FrameBackgroundView : public views::View,
135 public ui::AnimationDelegate {
136 public:
137 FrameBackgroundView()
138 : ALLOW_THIS_IN_INITIALIZER_LIST(
139 size_animation_(new ui::SlideAnimation(this))),
140 ALLOW_THIS_IN_INITIALIZER_LIST(
141 color_animation_(new ui::SlideAnimation(this))) {
142 size_animation_->SetSlideDuration(kHoverFadeDurationMs);
143 color_animation_->SetSlideDuration(kHoverFadeDurationMs);
144 SetPaintToLayer(true);
145 UpdateOpacity();
146 }
147 virtual ~FrameBackgroundView() {
148 }
149
150 void Configure(const gfx::Rect& start_bounds, const gfx::Rect& end_bounds) {
151 start_bounds_ = start_bounds;
152 end_bounds_ = end_bounds;
153 UpdateBounds();
154 }
155 void SetEndBounds(const gfx::Rect& end_bounds) {
156 end_bounds_ = end_bounds;
157 UpdateBounds();
158 }
159 void Show() {
160 size_animation_->Show();
161 color_animation_->Show();
162 }
163 void Hide() {
164 size_animation_->Hide();
165 color_animation_->Hide();
166 }
167
168 protected:
169 // Overridden from views::View:
170 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
171 SkRect rect = { SkIntToScalar(0), SkIntToScalar(0),
172 SkIntToScalar(width()), SkIntToScalar(height()) };
173 SkScalar radius = SkIntToScalar(kRoundedRectRadius);
174 SkPaint paint;
175 // Animation handles setting the opacity for the whole layer.
176 paint.setColor(kFrameColor);
177 paint.setStyle(SkPaint::kFill_Style);
178 canvas->GetSkCanvas()->drawRoundRect(rect, radius, radius, paint);
179 }
180
181 // Overridden from ui::AnimationDelegate:
182 virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE {
183 if (animation == color_animation_.get()) {
184 UpdateOpacity();
185 } else if (animation == size_animation_.get()) {
186 UpdateBounds();
187 }
188 }
189
190 private:
191 void UpdateOpacity() {
192 double opacity = color_animation_->CurrentValueBetween(
193 kFrameBorderStartOpacity, kFrameBorderEndOpacity);
194 layer()->SetOpacity(static_cast<float>(opacity));
195 }
196
197 void UpdateBounds() {
198 gfx::Rect current_bounds =
199 size_animation_->CurrentValueBetween(start_bounds_, end_bounds_);
200 SetBoundsRect(current_bounds);
201 SchedulePaint();
202 }
203
204 scoped_ptr<ui::SlideAnimation> size_animation_;
205 scoped_ptr<ui::SlideAnimation> color_animation_;
206 // Default "hidden" rectangle.
207 gfx::Rect default_bounds_;
208 // When moving mouse from one target to another (e.g. from edge to corner)
209 // the size animation start point may not be the default size.
210 gfx::Rect start_bounds_;
211 // Expanded bounds, with edges visible from behind the client area.
212 gfx::Rect end_bounds_;
213
214 DISALLOW_COPY_AND_ASSIGN(FrameBackgroundView);
215 };
216
217 /////////////////////////////////////////////////////////////////////////////// 97 ///////////////////////////////////////////////////////////////////////////////
218 // BrowserNonClientFrameViewAura, public: 98 // BrowserNonClientFrameViewAura, public:
219 99
220 BrowserNonClientFrameViewAura::BrowserNonClientFrameViewAura( 100 BrowserNonClientFrameViewAura::BrowserNonClientFrameViewAura(
221 BrowserFrame* frame, BrowserView* browser_view) 101 BrowserFrame* frame, BrowserView* browser_view)
222 : BrowserNonClientFrameView(frame, browser_view), 102 : BrowserNonClientFrameView(frame, browser_view),
223 last_hittest_code_(HTNOWHERE) { 103 maximize_button_(NULL),
224 frame_background_ = new FrameBackgroundView(); 104 close_button_(NULL),
225 AddChildView(frame_background_); 105 window_icon_(NULL),
106 button_separator_(NULL),
107 top_left_corner_(NULL),
108 top_edge_(NULL),
109 top_right_corner_(NULL),
110 header_left_edge_(NULL),
111 header_right_edge_(NULL) {
112 }
226 113
227 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 114 BrowserNonClientFrameViewAura::~BrowserNonClientFrameViewAura() {
228 maximize_button_ = 115 }
229 new WindowControlButton(this, 116
230 kMaximizeButtonBackgroundColor, 117 void BrowserNonClientFrameViewAura::Init() {
231 *rb.GetBitmapNamed(IDR_AURA_WINDOW_ZOOM_ICON)); 118 // Ensure we get resize cursors for a few pixels outside our bounds.
119 frame()->GetNativeWindow()->set_hit_test_bounds_inset(
120 -kResizeAreaOutsideBounds);
121
122 // Caption buttons.
123 maximize_button_ = new views::ImageButton(this);
124 SetButtonImages(maximize_button_,
125 IDR_AURA_WINDOW_MAXIMIZE,
126 IDR_AURA_WINDOW_MAXIMIZE_H,
127 IDR_AURA_WINDOW_MAXIMIZE_P);
232 maximize_button_->SetAccessibleName( 128 maximize_button_->SetAccessibleName(
233 l10n_util::GetStringUTF16(IDS_ACCNAME_MAXIMIZE)); 129 l10n_util::GetStringUTF16(IDS_ACCNAME_MAXIMIZE));
234 AddChildView(maximize_button_); 130 AddChildView(maximize_button_);
235 131 close_button_ = new views::ImageButton(this);
236 close_button_ = 132 SetButtonImages(close_button_,
237 new WindowControlButton(this, 133 IDR_AURA_WINDOW_CLOSE,
238 kCloseButtonBackgroundColor, 134 IDR_AURA_WINDOW_CLOSE_H,
239 *rb.GetBitmapNamed(IDR_AURA_WINDOW_CLOSE_ICON)); 135 IDR_AURA_WINDOW_CLOSE_P);
240 close_button_->SetAccessibleName( 136 close_button_->SetAccessibleName(
241 l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE)); 137 l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE));
242 AddChildView(close_button_); 138 AddChildView(close_button_);
243 139
244 // The BrowserFrame will become our owning window/widget. 140 // Window frame image parts.
245 frame->AsWidget()->AddObserver(this); 141 ResourceBundle& bundle = ResourceBundle::GetSharedInstance();
246 // Associate our WindowFrame interface with our owning window/widget so 142 button_separator_ =
247 // we get callbacks from aura_shell. 143 bundle.GetBitmapNamed(IDR_AURA_WINDOW_BUTTON_SEPARATOR);
248 frame->AsWidget()->GetNativeWindow()->SetProperty( 144 top_left_corner_ =
249 ash::kWindowFrameKey, 145 bundle.GetBitmapNamed(IDR_AURA_WINDOW_HEADER_SHADE_TOP_LEFT);
250 static_cast<ash::WindowFrame*>(this)); 146 top_edge_ =
251 } 147 bundle.GetBitmapNamed(IDR_AURA_WINDOW_HEADER_SHADE_TOP);
148 top_right_corner_ =
149 bundle.GetBitmapNamed(IDR_AURA_WINDOW_HEADER_SHADE_TOP_RIGHT);
150 header_left_edge_ =
151 bundle.GetBitmapNamed(IDR_AURA_WINDOW_HEADER_SHADE_LEFT);
152 header_right_edge_ =
153 bundle.GetBitmapNamed(IDR_AURA_WINDOW_HEADER_SHADE_RIGHT);
154 // TODO(jamescook): Should we paint the header bottom edge here, or keep
155 // it in BrowserFrameAura::ToolbarBackground as we do now?
252 156
253 BrowserNonClientFrameViewAura::~BrowserNonClientFrameViewAura() { 157 // Initializing the TabIconView is expensive, so only do it if we need to.
254 // Don't need to remove the Widget observer, the window is deleted before us. 158 if (browser_view()->ShouldShowWindowIcon()) {
255 } 159 window_icon_ = new TabIconView(this);
256 160 window_icon_->set_is_light(true);
257 void BrowserNonClientFrameViewAura::ShowFrameBackground() { 161 AddChildView(window_icon_);
258 UpdateFrameBackground(ShouldPaintAsActive()); 162 window_icon_->Update();
259 frame_background_->Show(); 163 }
260 }
261
262 void BrowserNonClientFrameViewAura::HideFrameBackground() {
263 frame_background_->Hide();
264 } 164 }
265 165
266 /////////////////////////////////////////////////////////////////////////////// 166 ///////////////////////////////////////////////////////////////////////////////
267 // BrowserNonClientFrameViewAura, private:
268
269 int BrowserNonClientFrameViewAura::NonClientHitTestImpl(
270 const gfx::Point& point) {
271 if (!GetLocalBounds().Contains(point))
272 return HTNOWHERE;
273
274 // Window controls get first try because they overlap the client area.
275 if (HitVisibleView(maximize_button_, point))
276 return HTMAXBUTTON;
277 if (HitVisibleView(close_button_, point))
278 return HTCLOSE;
279
280 int frame_component = GetWidget()->client_view()->NonClientHitTest(point);
281 if (frame_component != HTNOWHERE)
282 return frame_component;
283
284 // Test window resize components.
285 bool can_resize = GetWidget()->widget_delegate()->CanResize();
286 // TODO(derat): Disallow resizing via the top border in the Aura shell
287 // instead of enforcing it here. See http://crbug.com/101830.
288 frame_component = GetHTComponentForFrame(point,
289 0,
290 kResizeBorderThickness,
291 kResizeBorderThickness,
292 kResizeBorderThickness,
293 can_resize);
294 if (frame_component != HTNOWHERE)
295 return frame_component;
296 // Use HTCAPTION as a final fallback.
297 return HTCAPTION;
298 }
299
300 // Pass |active_window| explicitly because deactivating a window causes
301 // OnWidgetActivationChanged() to be called before GetWidget()->IsActive()
302 // changes state.
303 gfx::Rect BrowserNonClientFrameViewAura::GetFrameBackgroundBounds(
304 int hittest_code,
305 bool active_window) {
306 bool show_left = false;
307 bool show_top = false;
308 bool show_right = false;
309 bool show_bottom = false;
310 switch (hittest_code) {
311 case HTBOTTOM:
312 show_bottom = true;
313 break;
314 case HTBOTTOMLEFT:
315 show_bottom = true;
316 show_left = true;
317 break;
318 case HTBOTTOMRIGHT:
319 show_bottom = true;
320 show_right = true;
321 break;
322 case HTCAPTION:
323 show_top = true;
324 break;
325 case HTCLOSE:
326 show_top = true;
327 break;
328 case HTLEFT:
329 show_left = true;
330 break;
331 case HTMAXBUTTON:
332 show_top = true;
333 break;
334 case HTRIGHT:
335 show_right = true;
336 break;
337 case HTTOP:
338 show_top = true;
339 break;
340 case HTTOPLEFT:
341 show_top = true;
342 show_left = true;
343 break;
344 case HTTOPRIGHT:
345 show_top = true;
346 show_right = true;
347 break;
348 default:
349 break;
350 }
351 // Always show top edge for the active window so that you can tell which
352 // window has focus.
353 if (active_window)
354 show_top = true;
355 gfx::Rect target = bounds();
356 // Inset the sides that are not showing.
357 target.Inset(
358 (show_left ? 0 : kResizeBorderThickness - kPersistentBorderThickness),
359 (show_top ? 0 : kTopBorderThickness + kFrameBackgroundTopOffset),
360 (show_right ? 0 : kResizeBorderThickness - kPersistentBorderThickness),
361 (show_bottom ? 0 : kResizeBorderThickness - kPersistentBorderThickness));
362 return target;
363 }
364
365 void BrowserNonClientFrameViewAura::UpdateFrameBackground(bool active_window) {
366 gfx::Rect start_bounds = GetFrameBackgroundBounds(HTNOWHERE, active_window);
367 gfx::Rect end_bounds =
368 GetFrameBackgroundBounds(last_hittest_code_, active_window);
369 frame_background_->Configure(start_bounds, end_bounds);
370 }
371
372 void BrowserNonClientFrameViewAura::ActiveStateChanged() {
373 bool active = ShouldPaintAsActive();
374 // Active windows have different background bounds.
375 UpdateFrameBackground(active);
376 if (active)
377 frame_background_->Show();
378 else
379 frame_background_->Hide();
380 }
381
382 ///////////////////////////////////////////////////////////////////////////////
383 // BrowserNonClientFrameView overrides: 167 // BrowserNonClientFrameView overrides:
384 168
385 gfx::Rect BrowserNonClientFrameViewAura::GetBoundsForTabStrip( 169 gfx::Rect BrowserNonClientFrameViewAura::GetBoundsForTabStrip(
386 views::View* tabstrip) const { 170 views::View* tabstrip) const {
387 if (!tabstrip) 171 if (!tabstrip)
388 return gfx::Rect(); 172 return gfx::Rect();
389 // TODO(jamescook): Avatar icon support. 173 bool restored = !frame()->IsMaximized();
390 // Reserve space on the right for close/maximize buttons. 174 return gfx::Rect(kTabstripLeftSpacing,
391 int tabstrip_x = kResizeBorderThickness; 175 GetHorizontalTabStripVerticalOffset(restored),
392 int tabstrip_width = maximize_button_->x() - tabstrip_x; 176 maximize_button_->x() - kTabstripRightSpacing,
393 return gfx::Rect(tabstrip_x,
394 GetHorizontalTabStripVerticalOffset(false),
395 tabstrip_width,
396 tabstrip->GetPreferredSize().height()); 177 tabstrip->GetPreferredSize().height());
397
398 } 178 }
399 179
400 int BrowserNonClientFrameViewAura::GetHorizontalTabStripVerticalOffset( 180 int BrowserNonClientFrameViewAura::GetHorizontalTabStripVerticalOffset(
401 bool restored) const { 181 bool restored) const {
402 return kTopBorderThickness; 182 return NonClientTopBorderHeight(restored);
403 } 183 }
404 184
405 void BrowserNonClientFrameViewAura::UpdateThrobber(bool running) { 185 void BrowserNonClientFrameViewAura::UpdateThrobber(bool running) {
406 // TODO(jamescook): Do we need this? 186 if (window_icon_)
187 window_icon_->Update();
407 } 188 }
408 189
409 /////////////////////////////////////////////////////////////////////////////// 190 ///////////////////////////////////////////////////////////////////////////////
410 // views::NonClientFrameView overrides: 191 // views::NonClientFrameView overrides:
411 192
412 gfx::Rect BrowserNonClientFrameViewAura::GetBoundsForClientView() const { 193 gfx::Rect BrowserNonClientFrameViewAura::GetBoundsForClientView() const {
413 gfx::Rect bounds = GetLocalBounds(); 194 int top_height = NonClientTopBorderHeight(false);
414 bounds.Inset(kResizeBorderThickness, 195 return gfx::Rect(kBorderThickness,
415 kTopBorderThickness, 196 top_height,
416 kResizeBorderThickness, 197 std::max(0, width() - (2 * kBorderThickness)),
417 kResizeBorderThickness); 198 std::max(0, height() - top_height - kBorderThickness));
418 return bounds;
419 } 199 }
420 200
421 gfx::Rect BrowserNonClientFrameViewAura::GetWindowBoundsForClientBounds( 201 gfx::Rect BrowserNonClientFrameViewAura::GetWindowBoundsForClientBounds(
422 const gfx::Rect& client_bounds) const { 202 const gfx::Rect& client_bounds) const {
423 gfx::Rect bounds = client_bounds; 203 int top_height = NonClientTopBorderHeight(false);
424 bounds.Inset(-kResizeBorderThickness, 204 return gfx::Rect(std::max(0, client_bounds.x() - kBorderThickness),
425 -kTopBorderThickness, 205 std::max(0, client_bounds.y() - top_height),
426 -kResizeBorderThickness, 206 client_bounds.width() + (2 * kBorderThickness),
427 -kResizeBorderThickness); 207 client_bounds.height() + top_height + kBorderThickness);
428 return bounds;
429 } 208 }
430 209
431 int BrowserNonClientFrameViewAura::NonClientHitTest(const gfx::Point& point) { 210 int BrowserNonClientFrameViewAura::NonClientHitTest(const gfx::Point& point) {
432 last_hittest_code_ = NonClientHitTestImpl(point); 211 gfx::Rect expanded_bounds = bounds();
433 return last_hittest_code_; 212 expanded_bounds.Inset(-kResizeAreaOutsideBounds, -kResizeAreaOutsideBounds);
213 if (!expanded_bounds.Contains(point))
214 return HTNOWHERE;
215
216 // No avatar button for Chrome OS.
217
218 // Check the client view first, as it overlaps the window caption area.
219 int client_component = frame()->client_view()->NonClientHitTest(point);
220 if (client_component != HTNOWHERE)
221 return client_component;
222
223 // Then see if the point is within any of the window controls.
224 if (close_button_->visible() &&
225 close_button_->GetMirroredBounds().Contains(point))
226 return HTCLOSE;
227 if (maximize_button_->visible() &&
228 maximize_button_->GetMirroredBounds().Contains(point))
229 return HTMAXBUTTON;
230
231 bool can_resize = frame()->widget_delegate() ?
232 frame()->widget_delegate()->CanResize() :
233 false;
234 int frame_component = GetHTComponentForFrame(point,
235 kTopThickness,
236 kBorderThickness,
237 kResizeAreaCornerSize,
238 kResizeAreaCornerSize,
239 can_resize);
240 if (frame_component != HTNOWHERE)
241 return frame_component;
242
243 // Caption is a safe default.
244 return HTCAPTION;
434 } 245 }
435 246
436 void BrowserNonClientFrameViewAura::GetWindowMask(const gfx::Size& size, 247 void BrowserNonClientFrameViewAura::GetWindowMask(const gfx::Size& size,
437 gfx::Path* window_mask) { 248 gfx::Path* window_mask) {
438 // Nothing. 249 // Aura does not use window masks.
439 } 250 }
440 251
441 void BrowserNonClientFrameViewAura::ResetWindowControls() { 252 void BrowserNonClientFrameViewAura::ResetWindowControls() {
442 maximize_button_->SetState(views::CustomButton::BS_NORMAL); 253 maximize_button_->SetState(views::CustomButton::BS_NORMAL);
443 // The close button isn't affected by this constraint. 254 // The close button isn't affected by this constraint.
444 } 255 }
445 256
446 void BrowserNonClientFrameViewAura::UpdateWindowIcon() { 257 void BrowserNonClientFrameViewAura::UpdateWindowIcon() {
447 // TODO(jamescook): We will need this for app frames. 258 if (window_icon_)
448 } 259 window_icon_->SchedulePaint();
449
450 void BrowserNonClientFrameViewAura::ShouldPaintAsActiveChanged() {
451 ActiveStateChanged();
452 } 260 }
453 261
454 /////////////////////////////////////////////////////////////////////////////// 262 ///////////////////////////////////////////////////////////////////////////////
455 // views::View overrides: 263 // views::View overrides:
456 264
457 void BrowserNonClientFrameViewAura::Layout() { 265 void BrowserNonClientFrameViewAura::OnPaint(gfx::Canvas* canvas) {
458 // Layout window buttons from right to left. 266 if (frame()->IsFullscreen())
459 int right = width() - kResizeBorderThickness; 267 return; // Nothing visible, don't paint.
460 gfx::Size preferred = close_button_->GetPreferredSize(); 268 PaintHeader(canvas);
461 close_button_->SetBounds(right - preferred.width(), kTopBorderThickness, 269 PaintTitleBar(canvas);
462 preferred.width(), preferred.height()); 270 // Paint the view hierarchy, which draws the caption buttons.
463 right -= preferred.width(); // No padding. 271 BrowserNonClientFrameView::OnPaint(canvas);
464 preferred = maximize_button_->GetPreferredSize();
465 maximize_button_->SetBounds(right - preferred.width(), kTopBorderThickness,
466 preferred.width(), preferred.height());
467 UpdateFrameBackground(ShouldPaintAsActive());
468 } 272 }
469 273
470 views::View* BrowserNonClientFrameViewAura::GetEventHandlerForPoint( 274 void BrowserNonClientFrameViewAura::Layout() {
471 const gfx::Point& point) { 275 // Maximized windows and app/popup windows use shorter buttons.
472 // Mouse hovers near the resizing edges result in the animation starting and 276 if (frame()->IsMaximized() ||
473 // stopping as the frame background changes size. Just ignore events 277 !browser_view()->IsBrowserTypeNormal()) {
474 // destined for the frame background and handle them at this level. 278 SetButtonImages(close_button_,
475 views::View* view = View::GetEventHandlerForPoint(point); 279 IDR_AURA_WINDOW_MAXIMIZED_CLOSE,
476 if (view == frame_background_) 280 IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H,
477 return this; 281 IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P);
478 return view; 282 SetButtonImages(maximize_button_,
283 IDR_AURA_WINDOW_MAXIMIZED_RESTORE,
284 IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H,
285 IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P);
286 } else {
287 SetButtonImages(close_button_,
288 IDR_AURA_WINDOW_CLOSE,
289 IDR_AURA_WINDOW_CLOSE_H,
290 IDR_AURA_WINDOW_CLOSE_P);
291 SetButtonImages(maximize_button_,
292 IDR_AURA_WINDOW_MAXIMIZE,
293 IDR_AURA_WINDOW_MAXIMIZE_H,
294 IDR_AURA_WINDOW_MAXIMIZE_P);
295 }
296
297 gfx::Size close_size = close_button_->GetPreferredSize();
298 close_button_->SetBounds(
299 width() - close_size.width() - kCloseButtonOffsetX,
300 kCloseButtonOffsetY,
301 close_size.width(),
302 close_size.height());
303
304 gfx::Size maximize_size = maximize_button_->GetPreferredSize();
305 maximize_button_->SetBounds(
306 close_button_->x() - button_separator_->width() - maximize_size.width(),
307 close_button_->y(),
308 maximize_size.width(),
309 maximize_size.height());
310
311 if (window_icon_)
312 window_icon_->SetBoundsRect(
313 gfx::Rect(kIconOffsetX, kIconOffsetY, kIconSize, kIconSize));
314
315 BrowserNonClientFrameView::Layout();
479 } 316 }
480 317
481 bool BrowserNonClientFrameViewAura::HitTest(const gfx::Point& p) const { 318 bool BrowserNonClientFrameViewAura::HitTest(const gfx::Point& l) const {
482 // Claim all events outside the client area. 319 // If the point is outside the bounds of the client area, claim it.
483 bool in_client = GetWidget()->client_view()->bounds().Contains(p); 320 if (NonClientFrameView::HitTest(l))
484 if (!in_client)
485 return true; 321 return true;
486 // Window controls overlap the client area, so explicitly check for points 322
487 // inside of them.
488 if (close_button_->bounds().Contains(p) ||
489 maximize_button_->bounds().Contains(p))
490 return true;
491 // Otherwise claim it only if it's in a non-tab portion of the tabstrip. 323 // Otherwise claim it only if it's in a non-tab portion of the tabstrip.
492 if (!browser_view()->tabstrip()) 324 if (!browser_view()->tabstrip())
493 return false; 325 return false;
494 gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds()); 326 gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds());
495 gfx::Point tabstrip_origin(tabstrip_bounds.origin()); 327 gfx::Point tabstrip_origin(tabstrip_bounds.origin());
496 View::ConvertPointToView( 328 View::ConvertPointToView(frame()->client_view(), this, &tabstrip_origin);
497 frame()->client_view(), this, &tabstrip_origin);
498 tabstrip_bounds.set_origin(tabstrip_origin); 329 tabstrip_bounds.set_origin(tabstrip_origin);
499 if (p.y() > tabstrip_bounds.bottom()) 330 if (l.y() > tabstrip_bounds.bottom())
500 return false; 331 return false;
501 332
502 // We convert from our parent's coordinates since we assume we fill its bounds 333 // We convert from our parent's coordinates since we assume we fill its bounds
503 // completely. We need to do this since we're not a parent of the tabstrip, 334 // completely. We need to do this since we're not a parent of the tabstrip,
504 // meaning ConvertPointToView would otherwise return something bogus. 335 // meaning ConvertPointToView would otherwise return something bogus.
505 gfx::Point browser_view_point(p); 336 gfx::Point browser_view_point(l);
506 View::ConvertPointToView(parent(), browser_view(), &browser_view_point); 337 View::ConvertPointToView(parent(), browser_view(), &browser_view_point);
507 return browser_view()->IsPositionInWindowCaption(browser_view_point); 338 return browser_view()->IsPositionInWindowCaption(browser_view_point);
508 } 339 }
509 340
510 void BrowserNonClientFrameViewAura::OnMouseMoved( 341 void BrowserNonClientFrameViewAura::GetAccessibleState(
511 const views::MouseEvent& event) { 342 ui::AccessibleViewState* state) {
512 // We may be hovering over the resize edge. 343 state->role = ui::AccessibilityTypes::ROLE_TITLEBAR;
513 ShowFrameBackground();
514 }
515
516 void BrowserNonClientFrameViewAura::OnMouseExited(
517 const views::MouseEvent& event) {
518 // We hovered away from the resize edge.
519 HideFrameBackground();
520 }
521
522 gfx::NativeCursor BrowserNonClientFrameViewAura::GetCursor(
523 const views::MouseEvent& event) {
524 switch (last_hittest_code_) {
525 case HTBOTTOM:
526 return aura::kCursorSouthResize;
527 case HTBOTTOMLEFT:
528 return aura::kCursorSouthWestResize;
529 case HTBOTTOMRIGHT:
530 return aura::kCursorSouthEastResize;
531 case HTLEFT:
532 return aura::kCursorWestResize;
533 case HTRIGHT:
534 return aura::kCursorEastResize;
535 case HTTOP:
536 // Resizing from the top edge is not allowed.
537 return aura::kCursorNull;
538 case HTTOPLEFT:
539 return aura::kCursorWestResize;
540 case HTTOPRIGHT:
541 return aura::kCursorEastResize;
542 default:
543 return aura::kCursorNull;
544 }
545 } 344 }
546 345
547 /////////////////////////////////////////////////////////////////////////////// 346 ///////////////////////////////////////////////////////////////////////////////
548 // views::ButtonListener overrides: 347 // views::ButtonListener overrides:
549 348
550 void BrowserNonClientFrameViewAura::ButtonPressed(views::Button* sender, 349 void BrowserNonClientFrameViewAura::ButtonPressed(views::Button* sender,
551 const views::Event& event) { 350 const views::Event& event) {
552 if (sender == close_button_) { 351 if (sender == maximize_button_) {
553 frame()->Close();
554 } else if (sender == maximize_button_) {
555 if (frame()->IsMaximized()) 352 if (frame()->IsMaximized())
556 frame()->Restore(); 353 frame()->Restore();
557 else 354 else
558 frame()->Maximize(); 355 frame()->Maximize();
356 // The maximize button may have moved out from under the cursor.
357 ResetWindowControls();
358 } else if (sender == close_button_) {
359 frame()->Close();
559 } 360 }
560 } 361 }
561 362
562 /////////////////////////////////////////////////////////////////////////////// 363 ///////////////////////////////////////////////////////////////////////////////
563 // views::Widget::Observer overrides: 364 // TabIconView::TabIconViewModel overrides:
564 365
565 void BrowserNonClientFrameViewAura::OnWidgetActivationChanged( 366 bool BrowserNonClientFrameViewAura::ShouldTabIconViewAnimate() const {
566 views::Widget* widget, 367 // This function is queried during the creation of the window as the
567 bool active) { 368 // TabIconView we host is initialized, so we need to NULL check the selected
568 ActiveStateChanged(); 369 // WebContents because in this condition there is not yet a selected tab.
370 content::WebContents* current_tab = browser_view()->GetSelectedWebContents();
371 return current_tab ? current_tab->IsLoading() : false;
372 }
373
374 SkBitmap BrowserNonClientFrameViewAura::GetFaviconForTabIconView() {
375 views::WidgetDelegate* delegate = frame()->widget_delegate();
376 if (!delegate)
377 return SkBitmap();
378 return delegate->GetWindowIcon();
569 } 379 }
570 380
571 /////////////////////////////////////////////////////////////////////////////// 381 ///////////////////////////////////////////////////////////////////////////////
572 // ash::WindowFrame overrides: 382 // BrowserNonClientFrameViewAura, private:
573 383
574 void BrowserNonClientFrameViewAura::OnWindowHoverChanged(bool hovered) { 384 void BrowserNonClientFrameViewAura::SetButtonImages(views::ImageButton* button,
575 if (hovered) { 385 int normal_bitmap_id,
576 maximize_button_->Show(); 386 int hot_bitmap_id,
577 close_button_->Show(); 387 int pushed_bitmap_id) {
578 } else { 388 ui::ThemeProvider* tp = frame()->GetThemeProvider();
579 maximize_button_->Hide(); 389 button->SetImage(views::CustomButton::BS_NORMAL,
580 close_button_->Hide(); 390 tp->GetBitmapNamed(normal_bitmap_id));
391 button->SetImage(views::CustomButton::BS_HOT,
392 tp->GetBitmapNamed(hot_bitmap_id));
393 button->SetImage(views::CustomButton::BS_PUSHED,
394 tp->GetBitmapNamed(pushed_bitmap_id));
395 }
396
397 int BrowserNonClientFrameViewAura::NonClientTopBorderHeight(
398 bool restored) const {
399 if (frame()->widget_delegate() &&
400 frame()->widget_delegate()->ShouldShowWindowTitle()) {
401 // For popups ensure we have enough space to see the full window buttons.
402 return kCloseButtonOffsetY + close_button_->height();
403 }
404 if (restored)
405 return kTabstripTopSpacingRestored;
406 return kTabstripTopSpacingMaximized;
407 }
408
409 void BrowserNonClientFrameViewAura::PaintHeader(gfx::Canvas* canvas) {
410 // The primary header image changes based on window activation state and
411 // theme, so we look it up for each paint.
412 SkBitmap* theme_frame = GetThemeFrameBitmap();
413 SkBitmap* theme_frame_overlay = GetThemeFrameOverlayBitmap();
414
415 // Draw the header background, clipping the corners to be rounded.
416 const int kCornerRadius = 2;
417 TileRoundRect(canvas,
418 0, 0, width(), theme_frame->height(),
419 *theme_frame,
420 kCornerRadius);
421
422 // Draw the theme frame overlay, if available.
423 if (theme_frame_overlay)
424 canvas->DrawBitmapInt(*theme_frame_overlay, 0, 0);
425
426 // Separator between the maximize and close buttons.
427 canvas->DrawBitmapInt(*button_separator_,
428 close_button_->x() - button_separator_->width(),
429 close_button_->y());
430
431 // Draw the top corners and edge.
432 int top_left_height = top_left_corner_->height();
433 canvas->DrawBitmapInt(*top_left_corner_,
434 0, 0, top_left_corner_->width(), top_left_height,
435 0, 0, top_left_corner_->width(), top_left_height,
436 false);
437 canvas->TileImageInt(*top_edge_,
438 top_left_corner_->width(),
439 0,
440 width() - top_left_corner_->width() - top_right_corner_->width(),
441 top_edge_->height());
442 int top_right_height = top_right_corner_->height();
443 canvas->DrawBitmapInt(*top_right_corner_,
444 0, 0,
445 top_right_corner_->width(), top_right_height,
446 width() - top_right_corner_->width(), 0,
447 top_right_corner_->width(), top_right_height,
448 false);
449
450 // Header left edge.
451 int header_left_height = theme_frame->height() - top_left_height;
452 canvas->TileImageInt(*header_left_edge_,
453 0, top_left_height,
454 header_left_edge_->width(), header_left_height);
455
456 // Header right edge.
457 int header_right_height = theme_frame->height() - top_right_height;
458 canvas->TileImageInt(*header_right_edge_,
459 width() - header_right_edge_->width(), top_right_height,
460 header_right_edge_->width(), header_right_height);
461
462 // We don't draw edges around the content area. Web content goes flush
463 // to the edge of the window.
464 }
465
466 void BrowserNonClientFrameViewAura::PaintTitleBar(gfx::Canvas* canvas) {
467 // The window icon is painted by the TabIconView.
468 views::WidgetDelegate* delegate = frame()->widget_delegate();
469 if (delegate && delegate->ShouldShowWindowTitle()) {
470 int icon_right = window_icon_ ? window_icon_->bounds().right() : 0;
471 gfx::Rect title_bounds(
472 icon_right + kTitleOffsetX,
473 kTitleOffsetY,
474 std::max(0, maximize_button_->x() - kTitleLogoSpacing - icon_right),
475 BrowserFrame::GetTitleFont().GetHeight());
476 canvas->DrawStringInt(delegate->GetWindowTitle(),
477 BrowserFrame::GetTitleFont(),
478 SK_ColorWHITE,
479 GetMirroredXForRect(title_bounds),
480 title_bounds.y(),
481 title_bounds.width(),
482 title_bounds.height());
581 } 483 }
582 } 484 }
485
486 SkBitmap* BrowserNonClientFrameViewAura::GetThemeFrameBitmap() const {
487 bool is_incognito = browser_view()->IsOffTheRecord();
488 int resource_id;
489 if (browser_view()->IsBrowserTypeNormal()) {
490 if (ShouldPaintAsActive()) {
491 // Use the standard resource ids to allow users to theme the frames.
492 // TODO(jamescook): If this becomes the only frame we use on Aura, define
493 // the resources to use the standard ids like IDR_THEME_FRAME, etc.
494 if (is_incognito) {
495 return GetCustomBitmap(IDR_THEME_FRAME_INCOGNITO,
496 IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_ACTIVE);
497 }
498 return GetCustomBitmap(IDR_THEME_FRAME,
499 IDR_AURA_WINDOW_HEADER_BASE_ACTIVE);
500 }
501 if (is_incognito) {
502 return GetCustomBitmap(IDR_THEME_FRAME_INCOGNITO_INACTIVE,
503 IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_INACTIVE);
504 }
505 return GetCustomBitmap(IDR_THEME_FRAME_INACTIVE,
506 IDR_AURA_WINDOW_HEADER_BASE_INACTIVE);
507 }
508 // Never theme app and popup windows.
509 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
510 if (ShouldPaintAsActive()) {
511 resource_id = is_incognito ?
512 IDR_THEME_FRAME_INCOGNITO : IDR_FRAME;
513 } else {
514 resource_id = is_incognito ?
515 IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE;
516 }
517 return rb.GetBitmapNamed(resource_id);
518 }
519
520 SkBitmap* BrowserNonClientFrameViewAura::GetThemeFrameOverlayBitmap() const {
521 ui::ThemeProvider* tp = GetThemeProvider();
522 if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) &&
523 browser_view()->IsBrowserTypeNormal() &&
524 !browser_view()->IsOffTheRecord()) {
525 return tp->GetBitmapNamed(ShouldPaintAsActive() ?
526 IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE);
527 }
528 return NULL;
529 }
530
531 SkBitmap* BrowserNonClientFrameViewAura::GetCustomBitmap(
532 int bitmap_id,
533 int fallback_bitmap_id) const {
534 ui::ThemeProvider* tp = GetThemeProvider();
535 if (tp->HasCustomImage(bitmap_id))
536 return tp->GetBitmapNamed(bitmap_id);
537 return tp->GetBitmapNamed(fallback_bitmap_id);
538 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698