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

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

Issue 10020048: Work on USE_ASH/USE_AURA split. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase to ToT Created 8 years, 8 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view_aura.h"
6
7 #include "ash/wm/frame_painter.h"
8 #include "ash/wm/workspace/frame_maximize_button.h"
9 #include "chrome/browser/themes/theme_service.h"
10 #include "chrome/browser/ui/views/avatar_menu_button.h"
11 #include "chrome/browser/ui/views/frame/browser_frame.h"
12 #include "chrome/browser/ui/views/frame/browser_view.h"
13 #include "content/public/browser/web_contents.h"
14 #include "grit/generated_resources.h" // Accessibility names
15 #include "grit/theme_resources.h"
16 #include "grit/theme_resources_standard.h"
17 #include "grit/ui_resources.h"
18 #include "ui/aura/window.h"
19 #include "ui/aura/client/aura_constants.h"
20 #include "ui/base/accessibility/accessible_view_state.h"
21 #include "ui/base/hit_test.h"
22 #include "ui/base/l10n/l10n_util.h"
23 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/base/theme_provider.h"
25 #include "ui/gfx/canvas.h"
26 #include "ui/views/controls/button/image_button.h"
27 #include "ui/views/widget/widget.h"
28 #include "ui/views/widget/widget_delegate.h"
29
30 namespace {
31
32 // The avatar ends 2 px above the bottom of the tabstrip (which, given the
33 // way the tabstrip draws its bottom edge, will appear like a 1 px gap to the
34 // user).
35 const int kAvatarBottomSpacing = 2;
36 // There are 2 px on each side of the avatar (between the frame border and
37 // it on the left, and between it and the tabstrip on the right).
38 const int kAvatarSideSpacing = 2;
39 // Space between left edge of window and tabstrip.
40 const int kTabstripLeftSpacing = 0;
41 // Space between right edge of tabstrip and maximize button.
42 const int kTabstripRightSpacing = 10;
43 // Space between top of window and top of tabstrip for restored windows.
44 const int kTabstripTopSpacingRestored = 7;
45 // Space between top of window and top of tabstrip for maximized windows.
46 // Place them flush to the top to make them clickable when the cursor is at
47 // the screen edge.
48 const int kTabstripTopSpacingMaximized = 0;
49 // Height of the shadow in the tab image, used to ensure clicks in the shadow
50 // area still drag restored windows. This keeps the clickable area large enough
51 // to hit easily.
52 const int kTabShadowHeight = 4;
53 // Height of the shadow of the content area, at the top of the toolbar.
54 const int kContentShadowHeight = 1;
55
56 } // namespace
57
58 ///////////////////////////////////////////////////////////////////////////////
59 // BrowserNonClientFrameViewAura, public:
60
61 BrowserNonClientFrameViewAura::BrowserNonClientFrameViewAura(
62 BrowserFrame* frame, BrowserView* browser_view)
63 : BrowserNonClientFrameView(frame, browser_view),
64 size_button_(NULL),
65 close_button_(NULL),
66 window_icon_(NULL),
67 frame_painter_(new ash::FramePainter),
68 size_button_minimizes_(false) {
69 }
70
71 BrowserNonClientFrameViewAura::~BrowserNonClientFrameViewAura() {
72 }
73
74 void BrowserNonClientFrameViewAura::Init() {
75 // Panels only minimize.
76 ash::FramePainter::SizeButtonBehavior size_button_behavior;
77 if (browser_view()->browser()->is_type_panel() &&
78 browser_view()->browser()->app_type() == Browser::APP_TYPE_CHILD) {
79 size_button_minimizes_ = true;
80 size_button_ = new views::ImageButton(this);
81 size_button_behavior = ash::FramePainter::SIZE_BUTTON_MINIMIZES;
82 } else {
83 size_button_ = new ash::FrameMaximizeButton(this, this);
84 size_button_behavior = ash::FramePainter::SIZE_BUTTON_MAXIMIZES;
85 }
86 size_button_->SetAccessibleName(
87 l10n_util::GetStringUTF16(IDS_ACCNAME_MAXIMIZE));
88 AddChildView(size_button_);
89 close_button_ = new views::ImageButton(this);
90 close_button_->SetAccessibleName(
91 l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE));
92 AddChildView(close_button_);
93
94 // Initializing the TabIconView is expensive, so only do it if we need to.
95 if (browser_view()->ShouldShowWindowIcon()) {
96 window_icon_ = new TabIconView(this);
97 window_icon_->set_is_light(true);
98 AddChildView(window_icon_);
99 window_icon_->Update();
100 }
101
102 // Create incognito icon if necessary.
103 UpdateAvatarInfo();
104
105 // Frame painter handles layout of these buttons.
106 frame_painter_->Init(frame(), window_icon_, size_button_, close_button_,
107 size_button_behavior);
108 }
109
110 ///////////////////////////////////////////////////////////////////////////////
111 // BrowserNonClientFrameView overrides:
112
113 gfx::Rect BrowserNonClientFrameViewAura::GetBoundsForTabStrip(
114 views::View* tabstrip) const {
115 if (!tabstrip)
116 return gfx::Rect();
117 int tabstrip_x =
118 avatar_button() ?
119 (avatar_button()->bounds().right() + kAvatarSideSpacing) :
120 kTabstripLeftSpacing;
121 int tabstrip_width =
122 size_button_->x() - kTabstripRightSpacing - tabstrip_x;
123 return gfx::Rect(tabstrip_x,
124 GetHorizontalTabStripVerticalOffset(false),
125 std::max(0, tabstrip_width),
126 tabstrip->GetPreferredSize().height());
127 }
128
129 int BrowserNonClientFrameViewAura::GetHorizontalTabStripVerticalOffset(
130 bool force_restored) const {
131 return NonClientTopBorderHeight(force_restored);
132 }
133
134 void BrowserNonClientFrameViewAura::UpdateThrobber(bool running) {
135 if (window_icon_)
136 window_icon_->Update();
137 }
138
139 ///////////////////////////////////////////////////////////////////////////////
140 // views::NonClientFrameView overrides:
141
142 gfx::Rect BrowserNonClientFrameViewAura::GetBoundsForClientView() const {
143 int top_height = NonClientTopBorderHeight(false);
144 return frame_painter_->GetBoundsForClientView(top_height, bounds());
145 }
146
147 gfx::Rect BrowserNonClientFrameViewAura::GetWindowBoundsForClientBounds(
148 const gfx::Rect& client_bounds) const {
149 int top_height = NonClientTopBorderHeight(false);
150 return frame_painter_->GetWindowBoundsForClientBounds(top_height,
151 client_bounds);
152 }
153
154 int BrowserNonClientFrameViewAura::NonClientHitTest(const gfx::Point& point) {
155 int hit_test = frame_painter_->NonClientHitTest(this, point);
156 // When the window is restored we want a large click target above the tabs
157 // to drag the window, so redirect clicks in the tab's shadow to caption.
158 if (hit_test == HTCLIENT && !frame()->IsMaximized()) {
159 // Convert point to client coordinates.
160 gfx::Point client_point(point);
161 View::ConvertPointToView(this, frame()->client_view(), &client_point);
162 // Report hits in shadow at top of tabstrip as caption.
163 gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds());
164 if (client_point.y() < tabstrip_bounds.y() + kTabShadowHeight)
165 hit_test = HTCAPTION;
166 }
167 return hit_test;
168 }
169
170 void BrowserNonClientFrameViewAura::GetWindowMask(const gfx::Size& size,
171 gfx::Path* window_mask) {
172 // Aura does not use window masks.
173 }
174
175 void BrowserNonClientFrameViewAura::ResetWindowControls() {
176 size_button_->SetState(views::CustomButton::BS_NORMAL);
177 // The close button isn't affected by this constraint.
178 }
179
180 void BrowserNonClientFrameViewAura::UpdateWindowIcon() {
181 if (window_icon_)
182 window_icon_->SchedulePaint();
183 }
184
185 ///////////////////////////////////////////////////////////////////////////////
186 // views::View overrides:
187
188 void BrowserNonClientFrameViewAura::OnPaint(gfx::Canvas* canvas) {
189 if (frame()->IsFullscreen())
190 return; // Nothing visible, don't paint.
191 // The primary header image changes based on window activation state and
192 // theme, so we look it up for each paint.
193 frame_painter_->PaintHeader(
194 this,
195 canvas,
196 ShouldPaintAsActive() ?
197 ash::FramePainter::ACTIVE : ash::FramePainter::INACTIVE,
198 GetThemeFrameBitmapId(),
199 GetThemeFrameOverlayBitmap());
200 if (browser_view()->ShouldShowWindowTitle())
201 frame_painter_->PaintTitleBar(this, canvas, BrowserFrame::GetTitleFont());
202 if (browser_view()->IsToolbarVisible())
203 PaintToolbarBackground(canvas);
204 else
205 PaintContentEdge(canvas);
206 }
207
208 void BrowserNonClientFrameViewAura::Layout() {
209 // Maximized windows and app/popup windows use shorter buttons.
210 bool maximized_layout =
211 frame()->IsMaximized() || !browser_view()->IsBrowserTypeNormal();
212 frame_painter_->LayoutHeader(this, maximized_layout);
213 if (avatar_button())
214 LayoutAvatar();
215 BrowserNonClientFrameView::Layout();
216 }
217
218 bool BrowserNonClientFrameViewAura::HitTest(const gfx::Point& l) const {
219 // If the point is outside the bounds of the client area, claim it.
220 if (NonClientFrameView::HitTest(l))
221 return true;
222
223 // Otherwise claim it only if it's in a non-tab portion of the tabstrip.
224 if (!browser_view()->tabstrip())
225 return false;
226 gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds());
227 gfx::Point tabstrip_origin(tabstrip_bounds.origin());
228 View::ConvertPointToView(frame()->client_view(), this, &tabstrip_origin);
229 tabstrip_bounds.set_origin(tabstrip_origin);
230 if (l.y() > tabstrip_bounds.bottom())
231 return false;
232
233 // We convert from our parent's coordinates since we assume we fill its bounds
234 // completely. We need to do this since we're not a parent of the tabstrip,
235 // meaning ConvertPointToView would otherwise return something bogus.
236 gfx::Point browser_view_point(l);
237 View::ConvertPointToView(parent(), browser_view(), &browser_view_point);
238 return browser_view()->IsPositionInWindowCaption(browser_view_point);
239 }
240
241 void BrowserNonClientFrameViewAura::GetAccessibleState(
242 ui::AccessibleViewState* state) {
243 state->role = ui::AccessibilityTypes::ROLE_TITLEBAR;
244 }
245
246 gfx::Size BrowserNonClientFrameViewAura::GetMinimumSize() {
247 return frame_painter_->GetMinimumSize(this);
248 }
249
250 ///////////////////////////////////////////////////////////////////////////////
251 // views::ButtonListener overrides:
252
253 void BrowserNonClientFrameViewAura::ButtonPressed(views::Button* sender,
254 const views::Event& event) {
255 if (sender == size_button_) {
256 // The maximize button may move out from under the cursor.
257 ResetWindowControls();
258 if (size_button_minimizes_)
259 frame()->Minimize();
260 else if (frame()->IsMaximized())
261 frame()->Restore();
262 else
263 frame()->Maximize();
264 // |this| may be deleted - some windows delete their frames on maximize.
265 } else if (sender == close_button_) {
266 frame()->Close();
267 }
268 }
269
270 ///////////////////////////////////////////////////////////////////////////////
271 // TabIconView::TabIconViewModel overrides:
272
273 bool BrowserNonClientFrameViewAura::ShouldTabIconViewAnimate() const {
274 // This function is queried during the creation of the window as the
275 // TabIconView we host is initialized, so we need to NULL check the selected
276 // WebContents because in this condition there is not yet a selected tab.
277 content::WebContents* current_tab = browser_view()->GetSelectedWebContents();
278 return current_tab ? current_tab->IsLoading() : false;
279 }
280
281 SkBitmap BrowserNonClientFrameViewAura::GetFaviconForTabIconView() {
282 views::WidgetDelegate* delegate = frame()->widget_delegate();
283 if (!delegate)
284 return SkBitmap();
285 return delegate->GetWindowIcon();
286 }
287
288 ///////////////////////////////////////////////////////////////////////////////
289 // BrowserNonClientFrameViewAura, private:
290
291
292 int BrowserNonClientFrameViewAura::NonClientTopBorderHeight(
293 bool force_restored) const {
294 if (force_restored)
295 return kTabstripTopSpacingRestored;
296 if (frame()->IsFullscreen())
297 return 0;
298 // Windows with tab strips need a smaller non-client area.
299 if (browser_view()->IsTabStripVisible()) {
300 if (frame()->IsMaximized())
301 return kTabstripTopSpacingMaximized;
302 return kTabstripTopSpacingRestored;
303 }
304 // For windows without a tab strip (popups, etc.) ensure we have enough space
305 // to see the window caption buttons and the content separator line.
306 return close_button_->bounds().bottom() + kClientEdgeThickness;
307 }
308
309 void BrowserNonClientFrameViewAura::LayoutAvatar() {
310 DCHECK(avatar_button());
311 SkBitmap incognito_icon = browser_view()->GetOTRAvatarIcon();
312
313 int avatar_bottom = GetHorizontalTabStripVerticalOffset(false) +
314 browser_view()->GetTabStripHeight() - kAvatarBottomSpacing;
315 int avatar_restored_y = avatar_bottom - incognito_icon.height();
316 int avatar_y = frame()->IsMaximized() ?
317 NonClientTopBorderHeight(false) + kContentShadowHeight:
318 avatar_restored_y;
319 gfx::Rect avatar_bounds(kAvatarSideSpacing,
320 avatar_y,
321 incognito_icon.width(),
322 avatar_bottom - avatar_y);
323 avatar_button()->SetBoundsRect(avatar_bounds);
324 }
325
326 void BrowserNonClientFrameViewAura::PaintToolbarBackground(
327 gfx::Canvas* canvas) {
328 gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds());
329 if (toolbar_bounds.IsEmpty())
330 return;
331 gfx::Point toolbar_origin(toolbar_bounds.origin());
332 ConvertPointToView(browser_view(), this, &toolbar_origin);
333 toolbar_bounds.set_origin(toolbar_origin);
334
335 int x = toolbar_bounds.x();
336 int w = toolbar_bounds.width();
337 int y = toolbar_bounds.y();
338 int h = toolbar_bounds.height();
339
340 // Gross hack: We split the toolbar images into two pieces, since sometimes
341 // (popup mode) the toolbar isn't tall enough to show the whole image. The
342 // split happens between the top shadow section and the bottom gradient
343 // section so that we never break the gradient.
344 int split_point = kFrameShadowThickness * 2;
345 int bottom_y = y + split_point;
346 ui::ThemeProvider* tp = GetThemeProvider();
347 int bottom_edge_height = h - split_point;
348
349 canvas->FillRect(gfx::Rect(x, bottom_y, w, bottom_edge_height),
350 tp->GetColor(ThemeService::COLOR_TOOLBAR));
351
352 // Paint the main toolbar image. Since this image is also used to draw the
353 // tab background, we must use the tab strip offset to compute the image
354 // source y position. If you have to debug this code use an image editor
355 // to paint a diagonal line through the toolbar image and ensure it lines up
356 // across the tab and toolbar.
357 SkBitmap* theme_toolbar = tp->GetBitmapNamed(IDR_THEME_TOOLBAR);
358 canvas->TileImageInt(
359 *theme_toolbar,
360 x, bottom_y - GetHorizontalTabStripVerticalOffset(false),
361 x, bottom_y,
362 w, theme_toolbar->height());
363
364 // The content area line has a shadow that extends a couple of pixels above
365 // the toolbar bounds.
366 const int kContentShadowHeight = 2;
367 SkBitmap* toolbar_top =
368 tp->GetBitmapNamed(IDR_TOOLBAR_SHADE_TOP);
369 canvas->TileImageInt(*toolbar_top,
370 0, 0,
371 x, y - kContentShadowHeight,
372 w, split_point + kContentShadowHeight + 1);
373
374 // Draw the "lightening" shade line around the edges of the toolbar.
375 SkBitmap* toolbar_left = tp->GetBitmapNamed(IDR_TOOLBAR_SHADE_LEFT);
376 canvas->TileImageInt(*toolbar_left,
377 0, 0,
378 x + kClientEdgeThickness,
379 y + kClientEdgeThickness + kContentShadowHeight,
380 toolbar_left->width(), theme_toolbar->height());
381 SkBitmap* toolbar_right = tp->GetBitmapNamed(IDR_TOOLBAR_SHADE_RIGHT);
382 canvas->TileImageInt(*toolbar_right,
383 0, 0,
384 w - toolbar_right->width() - 2 * kClientEdgeThickness,
385 y + kClientEdgeThickness + kContentShadowHeight,
386 toolbar_right->width(), theme_toolbar->height());
387
388 // Draw the content/toolbar separator.
389 canvas->FillRect(gfx::Rect(x + kClientEdgeThickness,
390 toolbar_bounds.bottom() - kClientEdgeThickness,
391 w - (2 * kClientEdgeThickness),
392 kClientEdgeThickness),
393 ThemeService::GetDefaultColor(ThemeService::COLOR_TOOLBAR_SEPARATOR));
394 }
395
396 void BrowserNonClientFrameViewAura::PaintContentEdge(gfx::Canvas* canvas) {
397 canvas->FillRect(gfx::Rect(0, close_button_->bounds().bottom(),
398 width(), kClientEdgeThickness),
399 ThemeService::GetDefaultColor(ThemeService::COLOR_TOOLBAR_SEPARATOR));
400 }
401
402 int BrowserNonClientFrameViewAura::GetThemeFrameBitmapId() const {
403 bool is_incognito = browser_view()->IsOffTheRecord();
404 int resource_id;
405 if (browser_view()->IsBrowserTypeNormal()) {
406 ui::ThemeProvider* tp = GetThemeProvider();
407 if (ShouldPaintAsActive()) {
408 // Use the standard resource ids to allow users to theme the frames.
409 // TODO(jamescook): If this becomes the only frame we use on Aura, define
410 // the resources to use the standard ids like IDR_THEME_FRAME, etc.
411 if (is_incognito) {
412 return tp->HasCustomImage(IDR_THEME_FRAME_INCOGNITO) ?
413 IDR_THEME_FRAME_INCOGNITO :
414 IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_ACTIVE;
415 }
416 return tp->HasCustomImage(IDR_THEME_FRAME) ?
417 IDR_THEME_FRAME :
418 IDR_AURA_WINDOW_HEADER_BASE_ACTIVE;
419 }
420 if (is_incognito) {
421 return tp->HasCustomImage(IDR_THEME_FRAME_INCOGNITO_INACTIVE) ?
422 IDR_THEME_FRAME_INCOGNITO_INACTIVE :
423 IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_INACTIVE;
424 }
425 return tp->HasCustomImage(IDR_THEME_FRAME_INACTIVE) ?
426 IDR_THEME_FRAME_INACTIVE :
427 IDR_AURA_WINDOW_HEADER_BASE_INACTIVE;
428 }
429 // Never theme app and popup windows.
430 if (ShouldPaintAsActive()) {
431 resource_id = is_incognito ?
432 IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_ACTIVE :
433 IDR_AURA_WINDOW_HEADER_BASE_ACTIVE;
434 } else {
435 resource_id = is_incognito ?
436 IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_INACTIVE :
437 IDR_AURA_WINDOW_HEADER_BASE_INACTIVE;
438 }
439 return resource_id;
440 }
441
442 const SkBitmap*
443 BrowserNonClientFrameViewAura::GetThemeFrameOverlayBitmap() const {
444 ui::ThemeProvider* tp = GetThemeProvider();
445 if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) &&
446 browser_view()->IsBrowserTypeNormal() &&
447 !browser_view()->IsOffTheRecord()) {
448 return tp->GetBitmapNamed(ShouldPaintAsActive() ?
449 IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE);
450 }
451 return NULL;
452 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698