OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2008 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/views/frame/opaque_non_client_view.h" | |
6 | |
7 #include "chrome/browser/views/frame/browser_view.h" | |
8 #include "chrome/browser/views/tabs/tab_strip.h" | |
9 #include "chrome/common/gfx/chrome_font.h" | |
10 #include "chrome/common/gfx/path.h" | |
11 #include "chrome/common/l10n_util.h" | |
12 #include "chrome/common/resource_bundle.h" | |
13 #include "chrome/common/win_util.h" | |
14 #include "chrome/views/root_view.h" | |
15 #include "chrome/views/window_resources.h" | |
16 #include "grit/chromium_strings.h" | |
17 #include "grit/generated_resources.h" | |
18 #include "grit/theme_resources.h" | |
19 | |
20 // An enumeration of bitmap resources used by this window. | |
21 enum { | |
22 // Window Controls. | |
23 FRAME_CLOSE_BUTTON_ICON, | |
24 FRAME_CLOSE_BUTTON_ICON_H, | |
25 FRAME_CLOSE_BUTTON_ICON_P, | |
26 FRAME_CLOSE_BUTTON_ICON_SA, | |
27 FRAME_CLOSE_BUTTON_ICON_SA_H, | |
28 FRAME_CLOSE_BUTTON_ICON_SA_P, | |
29 FRAME_RESTORE_BUTTON_ICON, | |
30 FRAME_RESTORE_BUTTON_ICON_H, | |
31 FRAME_RESTORE_BUTTON_ICON_P, | |
32 FRAME_MAXIMIZE_BUTTON_ICON, | |
33 FRAME_MAXIMIZE_BUTTON_ICON_H, | |
34 FRAME_MAXIMIZE_BUTTON_ICON_P, | |
35 FRAME_MINIMIZE_BUTTON_ICON, | |
36 FRAME_MINIMIZE_BUTTON_ICON_H, | |
37 FRAME_MINIMIZE_BUTTON_ICON_P, | |
38 | |
39 // Window Frame Border. | |
40 FRAME_BOTTOM_EDGE, | |
41 FRAME_BOTTOM_LEFT_CORNER, | |
42 FRAME_BOTTOM_RIGHT_CORNER, | |
43 FRAME_LEFT_EDGE, | |
44 FRAME_RIGHT_EDGE, | |
45 FRAME_TOP_EDGE, | |
46 FRAME_TOP_LEFT_CORNER, | |
47 FRAME_TOP_RIGHT_CORNER, | |
48 | |
49 // Client Edge Border. | |
50 FRAME_CLIENT_EDGE_TOP_LEFT, | |
51 FRAME_CLIENT_EDGE_TOP, | |
52 FRAME_CLIENT_EDGE_TOP_RIGHT, | |
53 FRAME_CLIENT_EDGE_RIGHT, | |
54 FRAME_CLIENT_EDGE_BOTTOM_RIGHT, | |
55 FRAME_CLIENT_EDGE_BOTTOM, | |
56 FRAME_CLIENT_EDGE_BOTTOM_LEFT, | |
57 FRAME_CLIENT_EDGE_LEFT, | |
58 | |
59 // No-toolbar client edge. | |
60 FRAME_NO_TOOLBAR_TOP_LEFT, | |
61 FRAME_NO_TOOLBAR_TOP_CENTER, | |
62 FRAME_NO_TOOLBAR_TOP_RIGHT, | |
63 | |
64 FRAME_PART_BITMAP_COUNT // Must be last. | |
65 }; | |
66 | |
67 class ActiveWindowResources : public views::WindowResources { | |
68 public: | |
69 ActiveWindowResources() { | |
70 InitClass(); | |
71 } | |
72 virtual ~ActiveWindowResources() { } | |
73 | |
74 // WindowResources implementation: | |
75 virtual SkBitmap* GetPartBitmap(views::FramePartBitmap part) const { | |
76 return standard_frame_bitmaps_[part]; | |
77 } | |
78 | |
79 private: | |
80 static void InitClass() { | |
81 static bool initialized = false; | |
82 if (!initialized) { | |
83 static const int kFramePartBitmapIds[] = { | |
84 IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P, | |
85 IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, | |
86 IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P, | |
87 IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P, | |
88 IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P, | |
89 IDR_WINDOW_BOTTOM_CENTER, IDR_WINDOW_BOTTOM_LEFT_CORNER, | |
90 IDR_WINDOW_BOTTOM_RIGHT_CORNER, IDR_WINDOW_LEFT_SIDE, | |
91 IDR_WINDOW_RIGHT_SIDE, IDR_WINDOW_TOP_CENTER, | |
92 IDR_WINDOW_TOP_LEFT_CORNER, IDR_WINDOW_TOP_RIGHT_CORNER, | |
93 IDR_CONTENT_TOP_LEFT_CORNER, IDR_CONTENT_TOP_CENTER, | |
94 IDR_CONTENT_TOP_RIGHT_CORNER, IDR_CONTENT_RIGHT_SIDE, | |
95 IDR_CONTENT_BOTTOM_RIGHT_CORNER, IDR_CONTENT_BOTTOM_CENTER, | |
96 IDR_CONTENT_BOTTOM_LEFT_CORNER, IDR_CONTENT_LEFT_SIDE, | |
97 IDR_APP_TOP_LEFT, IDR_APP_TOP_CENTER, IDR_APP_TOP_RIGHT, | |
98 }; | |
99 | |
100 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
101 for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) | |
102 standard_frame_bitmaps_[i] = rb.GetBitmapNamed(kFramePartBitmapIds[i]); | |
103 initialized = true; | |
104 } | |
105 } | |
106 | |
107 static SkBitmap* standard_frame_bitmaps_[FRAME_PART_BITMAP_COUNT]; | |
108 | |
109 DISALLOW_EVIL_CONSTRUCTORS(ActiveWindowResources); | |
110 }; | |
111 | |
112 class InactiveWindowResources : public views::WindowResources { | |
113 public: | |
114 InactiveWindowResources() { | |
115 InitClass(); | |
116 } | |
117 virtual ~InactiveWindowResources() { } | |
118 | |
119 // WindowResources implementation: | |
120 virtual SkBitmap* GetPartBitmap(views::FramePartBitmap part) const { | |
121 return standard_frame_bitmaps_[part]; | |
122 } | |
123 | |
124 private: | |
125 static void InitClass() { | |
126 static bool initialized = false; | |
127 if (!initialized) { | |
128 static const int kFramePartBitmapIds[] = { | |
129 IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P, | |
130 IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, | |
131 IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P, | |
132 IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P, | |
133 IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P, | |
134 IDR_DEWINDOW_BOTTOM_CENTER, IDR_DEWINDOW_BOTTOM_LEFT_CORNER, | |
135 IDR_DEWINDOW_BOTTOM_RIGHT_CORNER, IDR_DEWINDOW_LEFT_SIDE, | |
136 IDR_DEWINDOW_RIGHT_SIDE, IDR_DEWINDOW_TOP_CENTER, | |
137 IDR_DEWINDOW_TOP_LEFT_CORNER, IDR_DEWINDOW_TOP_RIGHT_CORNER, | |
138 IDR_CONTENT_TOP_LEFT_CORNER, IDR_CONTENT_TOP_CENTER, | |
139 IDR_CONTENT_TOP_RIGHT_CORNER, IDR_CONTENT_RIGHT_SIDE, | |
140 IDR_CONTENT_BOTTOM_RIGHT_CORNER, IDR_CONTENT_BOTTOM_CENTER, | |
141 IDR_CONTENT_BOTTOM_LEFT_CORNER, IDR_CONTENT_LEFT_SIDE, | |
142 IDR_APP_TOP_LEFT, IDR_APP_TOP_CENTER, IDR_APP_TOP_RIGHT, | |
143 }; | |
144 | |
145 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
146 for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) | |
147 standard_frame_bitmaps_[i] = rb.GetBitmapNamed(kFramePartBitmapIds[i]); | |
148 initialized = true; | |
149 } | |
150 } | |
151 | |
152 static SkBitmap* standard_frame_bitmaps_[FRAME_PART_BITMAP_COUNT]; | |
153 | |
154 DISALLOW_EVIL_CONSTRUCTORS(InactiveWindowResources); | |
155 }; | |
156 | |
157 class OTRActiveWindowResources : public views::WindowResources { | |
158 public: | |
159 OTRActiveWindowResources() { | |
160 InitClass(); | |
161 } | |
162 virtual ~OTRActiveWindowResources() { } | |
163 | |
164 // WindowResources implementation: | |
165 virtual SkBitmap* GetPartBitmap(views::FramePartBitmap part) const { | |
166 return standard_frame_bitmaps_[part]; | |
167 } | |
168 | |
169 private: | |
170 static void InitClass() { | |
171 static bool initialized = false; | |
172 if (!initialized) { | |
173 static const int kFramePartBitmapIds[] = { | |
174 IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P, | |
175 IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, | |
176 IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P, | |
177 IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P, | |
178 IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P, | |
179 IDR_WINDOW_BOTTOM_CENTER_OTR, IDR_WINDOW_BOTTOM_LEFT_CORNER_OTR, | |
180 IDR_WINDOW_BOTTOM_RIGHT_CORNER_OTR, IDR_WINDOW_LEFT_SIDE_OTR, | |
181 IDR_WINDOW_RIGHT_SIDE_OTR, IDR_WINDOW_TOP_CENTER_OTR, | |
182 IDR_WINDOW_TOP_LEFT_CORNER_OTR, IDR_WINDOW_TOP_RIGHT_CORNER_OTR, | |
183 IDR_CONTENT_TOP_LEFT_CORNER, IDR_CONTENT_TOP_CENTER, | |
184 IDR_CONTENT_TOP_RIGHT_CORNER, IDR_CONTENT_RIGHT_SIDE, | |
185 IDR_CONTENT_BOTTOM_RIGHT_CORNER, IDR_CONTENT_BOTTOM_CENTER, | |
186 IDR_CONTENT_BOTTOM_LEFT_CORNER, IDR_CONTENT_LEFT_SIDE, | |
187 IDR_APP_TOP_LEFT, IDR_APP_TOP_CENTER, IDR_APP_TOP_RIGHT, | |
188 }; | |
189 | |
190 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
191 for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) | |
192 standard_frame_bitmaps_[i] = rb.GetBitmapNamed(kFramePartBitmapIds[i]); | |
193 initialized = true; | |
194 } | |
195 } | |
196 | |
197 static SkBitmap* standard_frame_bitmaps_[FRAME_PART_BITMAP_COUNT]; | |
198 | |
199 DISALLOW_EVIL_CONSTRUCTORS(OTRActiveWindowResources); | |
200 }; | |
201 | |
202 class OTRInactiveWindowResources : public views::WindowResources { | |
203 public: | |
204 OTRInactiveWindowResources() { | |
205 InitClass(); | |
206 } | |
207 virtual ~OTRInactiveWindowResources() { } | |
208 | |
209 // WindowResources implementation: | |
210 virtual SkBitmap* GetPartBitmap(views::FramePartBitmap part) const { | |
211 return standard_frame_bitmaps_[part]; | |
212 } | |
213 | |
214 private: | |
215 static void InitClass() { | |
216 static bool initialized = false; | |
217 if (!initialized) { | |
218 static const int kFramePartBitmapIds[] = { | |
219 IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P, | |
220 IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, | |
221 IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P, | |
222 IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P, | |
223 IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P, | |
224 IDR_DEWINDOW_BOTTOM_CENTER_OTR, IDR_DEWINDOW_BOTTOM_LEFT_CORNER_OTR, | |
225 IDR_DEWINDOW_BOTTOM_RIGHT_CORNER_OTR, IDR_DEWINDOW_LEFT_SIDE_OTR, | |
226 IDR_DEWINDOW_RIGHT_SIDE_OTR, IDR_DEWINDOW_TOP_CENTER_OTR, | |
227 IDR_DEWINDOW_TOP_LEFT_CORNER_OTR, | |
228 IDR_DEWINDOW_TOP_RIGHT_CORNER_OTR, | |
229 IDR_CONTENT_TOP_LEFT_CORNER, IDR_CONTENT_TOP_CENTER, | |
230 IDR_CONTENT_TOP_RIGHT_CORNER, IDR_CONTENT_RIGHT_SIDE, | |
231 IDR_CONTENT_BOTTOM_RIGHT_CORNER, IDR_CONTENT_BOTTOM_CENTER, | |
232 IDR_CONTENT_BOTTOM_LEFT_CORNER, IDR_CONTENT_LEFT_SIDE, | |
233 IDR_APP_TOP_LEFT, IDR_APP_TOP_CENTER, IDR_APP_TOP_RIGHT, | |
234 }; | |
235 | |
236 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
237 for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) | |
238 standard_frame_bitmaps_[i] = rb.GetBitmapNamed(kFramePartBitmapIds[i]); | |
239 initialized = true; | |
240 } | |
241 } | |
242 | |
243 static SkBitmap* standard_frame_bitmaps_[FRAME_PART_BITMAP_COUNT]; | |
244 | |
245 DISALLOW_EVIL_CONSTRUCTORS(OTRInactiveWindowResources); | |
246 }; | |
247 | |
248 // static | |
249 SkBitmap* ActiveWindowResources::standard_frame_bitmaps_[]; | |
250 SkBitmap* InactiveWindowResources::standard_frame_bitmaps_[]; | |
251 SkBitmap* OTRActiveWindowResources::standard_frame_bitmaps_[]; | |
252 SkBitmap* OTRInactiveWindowResources::standard_frame_bitmaps_[]; | |
253 | |
254 views::WindowResources* OpaqueNonClientView::active_resources_ = NULL; | |
255 views::WindowResources* OpaqueNonClientView::inactive_resources_ = NULL; | |
256 views::WindowResources* OpaqueNonClientView::active_otr_resources_ = NULL; | |
257 views::WindowResources* OpaqueNonClientView::inactive_otr_resources_ = NULL; | |
258 SkBitmap* OpaqueNonClientView::distributor_logo_ = NULL; | |
259 ChromeFont OpaqueNonClientView::title_font_; | |
260 | |
261 namespace { | |
262 // The frame border is only visible in restored mode and is hardcoded to 4 px on | |
263 // each side regardless of the system window border size. | |
264 const int kFrameBorderThickness = 4; | |
265 // Besides the frame border, there's another 11 px of empty space atop the | |
266 // window in restored mode, to use to drag the window around. | |
267 const int kNonClientRestoredExtraThickness = 11; | |
268 // While resize areas on Windows are normally the same size as the window | |
269 // borders, our top area is shrunk by 1 px to make it easier to move the window | |
270 // around with our thinner top grabbable strip. (Incidentally, our side and | |
271 // bottom resize areas don't match the frame border thickness either -- they | |
272 // span the whole nonclient area, so there's no "dead zone" for the mouse.) | |
273 const int kTopResizeAdjust = 1; | |
274 // In the window corners, the resize areas don't actually expand bigger, but the | |
275 // 16 px at the end of each edge triggers diagonal resizing. | |
276 const int kResizeAreaCornerSize = 16; | |
277 // The titlebar never shrinks to less than 18 px tall, plus the height of the | |
278 // frame border and any bottom edge. | |
279 const int kTitlebarMinimumHeight = 18; | |
280 // The icon is inset 2 px from the left frame border. | |
281 const int kIconLeftSpacing = 2; | |
282 // The icon takes up 16/25th of the available titlebar height. (This is | |
283 // expressed as two ints to avoid precision losses leading to off-by-one pixel | |
284 // errors.) | |
285 const int kIconHeightFractionNumerator = 16; | |
286 const int kIconHeightFractionDenominator = 25; | |
287 // The icon never shrinks below 16 px on a side. | |
288 const int kIconMinimumSize = 16; | |
289 // Because our frame border has a different "3D look" than Windows', with a less | |
290 // cluttered top edge, we need to shift the icon up by 1 px in restored mode so | |
291 // it looks more centered. | |
292 const int kIconRestoredAdjust = 1; | |
293 // There is a 4 px gap between the icon and the title text. | |
294 const int kIconTitleSpacing = 4; | |
295 // The title text starts 2 px below the bottom of the top frame border. | |
296 const int kTitleTopSpacing = 2; | |
297 // There is a 5 px gap between the title text and the distributor logo (if | |
298 // present) or caption buttons. | |
299 const int kTitleLogoSpacing = 5; | |
300 // In maximized mode, the OTR avatar starts 2 px below the top of the screen, so | |
301 // that it doesn't extend into the "3D edge" portion of the titlebar. | |
302 const int kOTRMaximizedTopSpacing = 2; | |
303 // The OTR avatar ends 2 px above the bottom of the tabstrip (which, given the | |
304 // way the tabstrip draws its bottom edge, will appear like a 1 px gap to the | |
305 // user). | |
306 const int kOTRBottomSpacing = 2; | |
307 // There are 2 px on each side of the OTR avatar (between the frame border and | |
308 // it on the left, and between it and the tabstrip on the right). | |
309 const int kOTRSideSpacing = 2; | |
310 // In restored mode, the New Tab button isn't at the same height as the caption | |
311 // buttons, but the space will look cluttered if it actually slides under them, | |
312 // so we stop it when the gap between the two is down to 5 px. | |
313 const int kNewTabCaptionRestoredSpacing = 5; | |
314 // In maximized mode, where the New Tab button and the caption buttons are at | |
315 // similar vertical coordinates, we need to reserve a larger, 16 px gap to avoid | |
316 // looking too cluttered. | |
317 const int kNewTabCaptionMaximizedSpacing = 16; | |
318 // When there's a distributor logo, we leave a 7 px gap between it and the | |
319 // caption buttons. | |
320 const int kLogoCaptionSpacing = 7; | |
321 // The caption buttons are always drawn 1 px down from the visible top of the | |
322 // window (the true top in restored mode, or the top of the screen in maximized | |
323 // mode). | |
324 const int kCaptionTopSpacing = 1; | |
325 } | |
326 | |
327 /////////////////////////////////////////////////////////////////////////////// | |
328 // OpaqueNonClientView, public: | |
329 | |
330 OpaqueNonClientView::OpaqueNonClientView(OpaqueFrame* frame, | |
331 BrowserView* browser_view) | |
332 : NonClientView(), | |
333 minimize_button_(new views::Button), | |
334 maximize_button_(new views::Button), | |
335 restore_button_(new views::Button), | |
336 close_button_(new views::Button), | |
337 window_icon_(NULL), | |
338 frame_(frame), | |
339 browser_view_(browser_view) { | |
340 InitClass(); | |
341 if (browser_view->IsOffTheRecord()) { | |
342 if (!active_otr_resources_) { | |
343 // Lazy load OTR resources only when we first show an OTR frame. | |
344 active_otr_resources_ = new OTRActiveWindowResources; | |
345 inactive_otr_resources_ = new OTRInactiveWindowResources; | |
346 } | |
347 current_active_resources_ = active_otr_resources_; | |
348 current_inactive_resources_= inactive_otr_resources_; | |
349 } else { | |
350 current_active_resources_ = active_resources_; | |
351 current_inactive_resources_ = inactive_resources_; | |
352 } | |
353 | |
354 views::WindowResources* resources = current_active_resources_; | |
355 minimize_button_->SetImage( | |
356 views::Button::BS_NORMAL, | |
357 resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON)); | |
358 minimize_button_->SetImage( | |
359 views::Button::BS_HOT, | |
360 resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON_H)); | |
361 minimize_button_->SetImage( | |
362 views::Button::BS_PUSHED, | |
363 resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON_P)); | |
364 minimize_button_->SetListener(this, -1); | |
365 minimize_button_->SetAccessibleName( | |
366 l10n_util::GetString(IDS_ACCNAME_MINIMIZE)); | |
367 AddChildView(minimize_button_); | |
368 | |
369 maximize_button_->SetImage( | |
370 views::Button::BS_NORMAL, | |
371 resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON)); | |
372 maximize_button_->SetImage( | |
373 views::Button::BS_HOT, | |
374 resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON_H)); | |
375 maximize_button_->SetImage( | |
376 views::Button::BS_PUSHED, | |
377 resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON_P)); | |
378 maximize_button_->SetListener(this, -1); | |
379 maximize_button_->SetAccessibleName( | |
380 l10n_util::GetString(IDS_ACCNAME_MAXIMIZE)); | |
381 AddChildView(maximize_button_); | |
382 | |
383 restore_button_->SetImage( | |
384 views::Button::BS_NORMAL, | |
385 resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON)); | |
386 restore_button_->SetImage( | |
387 views::Button::BS_HOT, | |
388 resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON_H)); | |
389 restore_button_->SetImage( | |
390 views::Button::BS_PUSHED, | |
391 resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON_P)); | |
392 restore_button_->SetListener(this, -1); | |
393 restore_button_->SetAccessibleName( | |
394 l10n_util::GetString(IDS_ACCNAME_RESTORE)); | |
395 AddChildView(restore_button_); | |
396 | |
397 close_button_->SetImage( | |
398 views::Button::BS_NORMAL, | |
399 resources->GetPartBitmap(FRAME_CLOSE_BUTTON_ICON)); | |
400 close_button_->SetImage( | |
401 views::Button::BS_HOT, | |
402 resources->GetPartBitmap(FRAME_CLOSE_BUTTON_ICON_H)); | |
403 close_button_->SetImage( | |
404 views::Button::BS_PUSHED, | |
405 resources->GetPartBitmap(FRAME_CLOSE_BUTTON_ICON_P)); | |
406 close_button_->SetListener(this, -1); | |
407 close_button_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_CLOSE)); | |
408 AddChildView(close_button_); | |
409 | |
410 // Initializing the TabIconView is expensive, so only do it if we need to. | |
411 if (browser_view_->ShouldShowWindowIcon()) { | |
412 window_icon_ = new TabIconView(this); | |
413 window_icon_->set_is_light(true); | |
414 AddChildView(window_icon_); | |
415 window_icon_->Update(); | |
416 } | |
417 // Only load the title font if we're going to need to use it to paint. | |
418 // Loading fonts is expensive. | |
419 if (browser_view_->ShouldShowWindowTitle()) | |
420 InitAppWindowResources(); | |
421 } | |
422 | |
423 OpaqueNonClientView::~OpaqueNonClientView() { | |
424 } | |
425 | |
426 gfx::Rect OpaqueNonClientView::GetWindowBoundsForClientBounds( | |
427 const gfx::Rect& client_bounds) { | |
428 int top_height = NonClientTopBorderHeight(); | |
429 int border_thickness = NonClientBorderThickness(); | |
430 return gfx::Rect(std::max(0, client_bounds.x() - border_thickness), | |
431 std::max(0, client_bounds.y() - top_height), | |
432 client_bounds.width() + (2 * border_thickness), | |
433 client_bounds.height() + top_height + border_thickness); | |
434 } | |
435 | |
436 gfx::Rect OpaqueNonClientView::GetBoundsForTabStrip(TabStrip* tabstrip) { | |
437 int tabstrip_x = browser_view_->ShouldShowOffTheRecordAvatar() ? | |
438 (otr_avatar_bounds_.right() + kOTRSideSpacing) : | |
439 NonClientBorderThickness(); | |
440 int tabstrip_width = minimize_button_->x() - tabstrip_x - | |
441 (frame_->IsMaximized() ? | |
442 kNewTabCaptionMaximizedSpacing : kNewTabCaptionRestoredSpacing); | |
443 return gfx::Rect(tabstrip_x, NonClientTopBorderHeight(), | |
444 std::max(0, tabstrip_width), tabstrip->GetPreferredHeight()); | |
445 } | |
446 | |
447 void OpaqueNonClientView::UpdateWindowIcon() { | |
448 if (window_icon_) | |
449 window_icon_->Update(); | |
450 } | |
451 | |
452 /////////////////////////////////////////////////////////////////////////////// | |
453 // OpaqueNonClientView, views::NonClientView implementation: | |
454 | |
455 gfx::Rect OpaqueNonClientView::CalculateClientAreaBounds(int width, | |
456 int height) const { | |
457 int top_height = NonClientTopBorderHeight(); | |
458 int border_thickness = NonClientBorderThickness(); | |
459 return gfx::Rect(border_thickness, top_height, | |
460 std::max(0, width - (2 * border_thickness)), | |
461 std::max(0, height - top_height - border_thickness)); | |
462 } | |
463 | |
464 gfx::Size OpaqueNonClientView::CalculateWindowSizeForClientSize( | |
465 int width, | |
466 int height) const { | |
467 int border_thickness = NonClientBorderThickness(); | |
468 return gfx::Size(width + (2 * border_thickness), | |
469 height + NonClientTopBorderHeight() + border_thickness); | |
470 } | |
471 | |
472 gfx::Point OpaqueNonClientView::GetSystemMenuPoint() const { | |
473 gfx::Point system_menu_point(FrameBorderThickness(), | |
474 NonClientTopBorderHeight() + browser_view_->GetTabStripHeight() - | |
475 (browser_view_->IsFullscreen() ? 0 : kClientEdgeThickness)); | |
476 ConvertPointToScreen(this, &system_menu_point); | |
477 return system_menu_point; | |
478 } | |
479 | |
480 int OpaqueNonClientView::NonClientHitTest(const gfx::Point& point) { | |
481 if (!bounds().Contains(point)) | |
482 return HTNOWHERE; | |
483 | |
484 int frame_component = frame_->client_view()->NonClientHitTest(point); | |
485 if (frame_component != HTNOWHERE) | |
486 return frame_component; | |
487 | |
488 // Then see if the point is within any of the window controls. | |
489 if (close_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains(point)) | |
490 return HTCLOSE; | |
491 if (restore_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains( | |
492 point)) | |
493 return HTMAXBUTTON; | |
494 if (maximize_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains( | |
495 point)) | |
496 return HTMAXBUTTON; | |
497 if (minimize_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains( | |
498 point)) | |
499 return HTMINBUTTON; | |
500 if (window_icon_ && | |
501 window_icon_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains(point)) | |
502 return HTSYSMENU; | |
503 | |
504 int window_component = GetHTComponentForFrame(point, TopResizeHeight(), | |
505 NonClientBorderThickness(), kResizeAreaCornerSize, kResizeAreaCornerSize, | |
506 frame_->window_delegate()->CanResize()); | |
507 // Fall back to the caption if no other component matches. | |
508 return (window_component == HTNOWHERE) ? HTCAPTION : window_component; | |
509 } | |
510 | |
511 void OpaqueNonClientView::GetWindowMask(const gfx::Size& size, | |
512 gfx::Path* window_mask) { | |
513 DCHECK(window_mask); | |
514 | |
515 if (browser_view_->IsFullscreen()) | |
516 return; | |
517 | |
518 // Redefine the window visible region for the new size. | |
519 window_mask->moveTo(0, 3); | |
520 window_mask->lineTo(1, 2); | |
521 window_mask->lineTo(1, 1); | |
522 window_mask->lineTo(2, 1); | |
523 window_mask->lineTo(3, 0); | |
524 | |
525 window_mask->lineTo(SkIntToScalar(size.width() - 3), 0); | |
526 window_mask->lineTo(SkIntToScalar(size.width() - 2), 1); | |
527 window_mask->lineTo(SkIntToScalar(size.width() - 1), 1); | |
528 window_mask->lineTo(SkIntToScalar(size.width() - 1), 2); | |
529 window_mask->lineTo(SkIntToScalar(size.width()), 3); | |
530 | |
531 window_mask->lineTo(SkIntToScalar(size.width()), | |
532 SkIntToScalar(size.height())); | |
533 window_mask->lineTo(0, SkIntToScalar(size.height())); | |
534 window_mask->close(); | |
535 } | |
536 | |
537 void OpaqueNonClientView::EnableClose(bool enable) { | |
538 close_button_->SetEnabled(enable); | |
539 } | |
540 | |
541 void OpaqueNonClientView::ResetWindowControls() { | |
542 restore_button_->SetState(views::Button::BS_NORMAL); | |
543 minimize_button_->SetState(views::Button::BS_NORMAL); | |
544 maximize_button_->SetState(views::Button::BS_NORMAL); | |
545 // The close button isn't affected by this constraint. | |
546 } | |
547 | |
548 /////////////////////////////////////////////////////////////////////////////// | |
549 // OpaqueNonClientView, views::View overrides: | |
550 | |
551 void OpaqueNonClientView::Paint(ChromeCanvas* canvas) { | |
552 if (browser_view_->IsFullscreen()) | |
553 return; // Nothing is visible, so don't bother to paint. | |
554 | |
555 if (frame_->IsMaximized()) | |
556 PaintMaximizedFrameBorder(canvas); | |
557 else | |
558 PaintRestoredFrameBorder(canvas); | |
559 PaintDistributorLogo(canvas); | |
560 PaintTitleBar(canvas); | |
561 PaintToolbarBackground(canvas); | |
562 PaintOTRAvatar(canvas); | |
563 if (!frame_->IsMaximized()) | |
564 PaintRestoredClientEdge(canvas); | |
565 } | |
566 | |
567 void OpaqueNonClientView::Layout() { | |
568 LayoutWindowControls(); | |
569 LayoutDistributorLogo(); | |
570 LayoutTitleBar(); | |
571 LayoutOTRAvatar(); | |
572 LayoutClientView(); | |
573 } | |
574 | |
575 views::View* OpaqueNonClientView::GetViewForPoint(const gfx::Point& point, | |
576 bool can_create_floating) { | |
577 // We override this function because the ClientView can overlap the non - | |
578 // client view, making it impossible to click on the window controls. We need | |
579 // to ensure the window controls are checked _first_. | |
580 views::View* views[] = | |
581 { close_button_, restore_button_, maximize_button_, minimize_button_ }; | |
582 for (int i = 0; i < arraysize(views); ++i) { | |
583 if (!views[i]->IsVisible()) | |
584 continue; | |
585 // Apply mirroring transformation on view bounds for RTL chrome. | |
586 if (views[i]->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains(point)) | |
587 return views[i]; | |
588 } | |
589 return View::GetViewForPoint(point, can_create_floating); | |
590 } | |
591 | |
592 void OpaqueNonClientView::ViewHierarchyChanged(bool is_add, | |
593 views::View* parent, | |
594 views::View* child) { | |
595 if (is_add && child == this) { | |
596 DCHECK(GetWidget()); | |
597 DCHECK(frame_->client_view()->GetParent() != this); | |
598 AddChildView(frame_->client_view()); | |
599 | |
600 // The Accessibility glue looks for the product name on these two views to | |
601 // determine if this is in fact a Chrome window. | |
602 GetRootView()->SetAccessibleName(l10n_util::GetString(IDS_PRODUCT_NAME)); | |
603 SetAccessibleName(l10n_util::GetString(IDS_PRODUCT_NAME)); | |
604 } | |
605 } | |
606 | |
607 bool OpaqueNonClientView::GetAccessibleRole(VARIANT* role) { | |
608 DCHECK(role); | |
609 // We aren't actually the client area of the window, but we act like it as | |
610 // far as MSAA and the UI tests are concerned. | |
611 role->vt = VT_I4; | |
612 role->lVal = ROLE_SYSTEM_CLIENT; | |
613 return true; | |
614 } | |
615 | |
616 bool OpaqueNonClientView::GetAccessibleName(std::wstring* name) { | |
617 if (!accessible_name_.empty()) { | |
618 *name = accessible_name_; | |
619 return true; | |
620 } | |
621 return false; | |
622 } | |
623 | |
624 void OpaqueNonClientView::SetAccessibleName(const std::wstring& name) { | |
625 accessible_name_ = name; | |
626 } | |
627 | |
628 /////////////////////////////////////////////////////////////////////////////// | |
629 // OpaqueNonClientView, views::BaseButton::ButtonListener implementation: | |
630 | |
631 void OpaqueNonClientView::ButtonPressed(views::BaseButton* sender) { | |
632 if (sender == minimize_button_) | |
633 frame_->ExecuteSystemMenuCommand(SC_MINIMIZE); | |
634 else if (sender == maximize_button_) | |
635 frame_->ExecuteSystemMenuCommand(SC_MAXIMIZE); | |
636 else if (sender == restore_button_) | |
637 frame_->ExecuteSystemMenuCommand(SC_RESTORE); | |
638 else if (sender == close_button_) | |
639 frame_->ExecuteSystemMenuCommand(SC_CLOSE); | |
640 } | |
641 | |
642 /////////////////////////////////////////////////////////////////////////////// | |
643 // OpaqueNonClientView, TabIconView::TabContentsProvider implementation: | |
644 | |
645 bool OpaqueNonClientView::ShouldTabIconViewAnimate() const { | |
646 // This function is queried during the creation of the window as the | |
647 // TabIconView we host is initialized, so we need to NULL check the selected | |
648 // TabContents because in this condition there is not yet a selected tab. | |
649 TabContents* current_tab = browser_view_->GetSelectedTabContents(); | |
650 return current_tab ? current_tab->is_loading() : false; | |
651 } | |
652 | |
653 SkBitmap OpaqueNonClientView::GetFavIconForTabIconView() { | |
654 return frame_->window_delegate()->GetWindowIcon(); | |
655 } | |
656 | |
657 /////////////////////////////////////////////////////////////////////////////// | |
658 // OpaqueNonClientView, private: | |
659 | |
660 int OpaqueNonClientView::FrameBorderThickness() const { | |
661 if (browser_view_->IsFullscreen()) | |
662 return 0; | |
663 return frame_->IsMaximized() ? | |
664 GetSystemMetrics(SM_CXSIZEFRAME) : kFrameBorderThickness; | |
665 } | |
666 | |
667 int OpaqueNonClientView::TopResizeHeight() const { | |
668 return FrameBorderThickness() - kTopResizeAdjust; | |
669 } | |
670 | |
671 int OpaqueNonClientView::NonClientBorderThickness() const { | |
672 // When we fill the screen, we don't show a client edge. | |
673 return FrameBorderThickness() + | |
674 (browser_view_->CanCurrentlyResize() ? kClientEdgeThickness : 0); | |
675 } | |
676 | |
677 int OpaqueNonClientView::NonClientTopBorderHeight() const { | |
678 if (frame_->window_delegate()->ShouldShowWindowTitle()) { | |
679 int title_top_spacing, title_thickness; | |
680 return TitleCoordinates(&title_top_spacing, &title_thickness); | |
681 } | |
682 | |
683 return FrameBorderThickness() + (browser_view_->CanCurrentlyResize() ? | |
684 kNonClientRestoredExtraThickness : 0); | |
685 } | |
686 | |
687 int OpaqueNonClientView::UnavailablePixelsAtBottomOfNonClientHeight() const { | |
688 // Tricky: When a toolbar is edging the titlebar, it not only draws its own | |
689 // shadow and client edge, but an extra, light "shadow" pixel as well, which | |
690 // is treated as available space. Thus the nonclient area actually _fails_ to | |
691 // include some available pixels, leading to a negative number here. | |
692 if (browser_view_->IsToolbarVisible()) | |
693 return -kFrameShadowThickness; | |
694 | |
695 return kFrameShadowThickness + | |
696 (frame_->IsMaximized() ? 0 : kClientEdgeThickness); | |
697 } | |
698 | |
699 int OpaqueNonClientView::TitleCoordinates(int* title_top_spacing, | |
700 int* title_thickness) const { | |
701 int frame_thickness = FrameBorderThickness(); | |
702 int min_titlebar_height = kTitlebarMinimumHeight + frame_thickness; | |
703 *title_top_spacing = frame_thickness + kTitleTopSpacing; | |
704 // The bottom spacing should be the same apparent height as the top spacing. | |
705 // Because the actual top spacing height varies based on the system border | |
706 // thickness, we calculate this based on the restored top spacing and then | |
707 // adjust for maximized mode. We also don't include the frame shadow here, | |
708 // since while it's part of the bottom spacing it will be added in at the end | |
709 // as necessary (when a toolbar is present, the "shadow" is actually drawn by | |
710 // the toolbar). | |
711 int title_bottom_spacing = | |
712 kFrameBorderThickness + kTitleTopSpacing - kFrameShadowThickness; | |
713 if (frame_->IsMaximized()) { | |
714 // When we maximize, the top border appears to be chopped off; shift the | |
715 // title down to stay centered within the remaining space. | |
716 int title_adjust = (kFrameBorderThickness / 2); | |
717 *title_top_spacing += title_adjust; | |
718 title_bottom_spacing -= title_adjust; | |
719 } | |
720 *title_thickness = std::max(title_font_.height(), | |
721 min_titlebar_height - *title_top_spacing - title_bottom_spacing); | |
722 return *title_top_spacing + *title_thickness + title_bottom_spacing + | |
723 UnavailablePixelsAtBottomOfNonClientHeight(); | |
724 } | |
725 | |
726 void OpaqueNonClientView::PaintRestoredFrameBorder(ChromeCanvas* canvas) { | |
727 SkBitmap* top_left_corner = resources()->GetPartBitmap(FRAME_TOP_LEFT_CORNER); | |
728 SkBitmap* top_right_corner = | |
729 resources()->GetPartBitmap(FRAME_TOP_RIGHT_CORNER); | |
730 SkBitmap* top_edge = resources()->GetPartBitmap(FRAME_TOP_EDGE); | |
731 SkBitmap* right_edge = resources()->GetPartBitmap(FRAME_RIGHT_EDGE); | |
732 SkBitmap* left_edge = resources()->GetPartBitmap(FRAME_LEFT_EDGE); | |
733 SkBitmap* bottom_left_corner = | |
734 resources()->GetPartBitmap(FRAME_BOTTOM_LEFT_CORNER); | |
735 SkBitmap* bottom_right_corner = | |
736 resources()->GetPartBitmap(FRAME_BOTTOM_RIGHT_CORNER); | |
737 SkBitmap* bottom_edge = resources()->GetPartBitmap(FRAME_BOTTOM_EDGE); | |
738 | |
739 // Top. | |
740 canvas->DrawBitmapInt(*top_left_corner, 0, 0); | |
741 canvas->TileImageInt(*top_edge, top_left_corner->width(), 0, | |
742 width() - top_right_corner->width(), top_edge->height()); | |
743 canvas->DrawBitmapInt(*top_right_corner, | |
744 width() - top_right_corner->width(), 0); | |
745 // Note: When we don't have a toolbar, we need to draw some kind of bottom | |
746 // edge here. Because the App Window graphics we use for this have an | |
747 // attached client edge and their sizing algorithm is a little involved, we do | |
748 // all this in PaintRestoredClientEdge(). | |
749 | |
750 // Right. | |
751 canvas->TileImageInt(*right_edge, width() - right_edge->width(), | |
752 top_right_corner->height(), right_edge->width(), | |
753 height() - top_right_corner->height() - | |
754 bottom_right_corner->height()); | |
755 | |
756 // Bottom. | |
757 canvas->DrawBitmapInt(*bottom_right_corner, | |
758 width() - bottom_right_corner->width(), | |
759 height() - bottom_right_corner->height()); | |
760 canvas->TileImageInt(*bottom_edge, bottom_left_corner->width(), | |
761 height() - bottom_edge->height(), | |
762 width() - bottom_left_corner->width() - | |
763 bottom_right_corner->width(), | |
764 bottom_edge->height()); | |
765 canvas->DrawBitmapInt(*bottom_left_corner, 0, | |
766 height() - bottom_left_corner->height()); | |
767 | |
768 // Left. | |
769 canvas->TileImageInt(*left_edge, 0, top_left_corner->height(), | |
770 left_edge->width(), | |
771 height() - top_left_corner->height() - bottom_left_corner->height()); | |
772 } | |
773 | |
774 void OpaqueNonClientView::PaintMaximizedFrameBorder(ChromeCanvas* canvas) { | |
775 SkBitmap* top_edge = resources()->GetPartBitmap(FRAME_TOP_EDGE); | |
776 canvas->TileImageInt(*top_edge, 0, FrameBorderThickness(), width(), | |
777 top_edge->height()); | |
778 | |
779 if (!browser_view_->IsToolbarVisible()) { | |
780 // There's no toolbar to edge the frame border, so we need to draw a bottom | |
781 // edge. The graphic we use for this has a built in client edge, so we clip | |
782 // it off the bottom. | |
783 SkBitmap* top_center = | |
784 resources()->GetPartBitmap(FRAME_NO_TOOLBAR_TOP_CENTER); | |
785 int edge_height = top_center->height() - kClientEdgeThickness; | |
786 canvas->TileImageInt(*top_center, 0, | |
787 frame_->client_view()->y() - edge_height, width(), edge_height); | |
788 } | |
789 } | |
790 | |
791 void OpaqueNonClientView::PaintDistributorLogo(ChromeCanvas* canvas) { | |
792 // The distributor logo is only painted when the frame is not maximized and | |
793 // when we actually have a logo. | |
794 if (!frame_->IsMaximized() && distributor_logo_) { | |
795 canvas->DrawBitmapInt(*distributor_logo_, | |
796 MirroredLeftPointForRect(logo_bounds_), logo_bounds_.y()); | |
797 } | |
798 } | |
799 | |
800 void OpaqueNonClientView::PaintTitleBar(ChromeCanvas* canvas) { | |
801 // The window icon is painted by the TabIconView. | |
802 views::WindowDelegate* d = frame_->window_delegate(); | |
803 if (d->ShouldShowWindowTitle()) { | |
804 canvas->DrawStringInt(d->GetWindowTitle(), title_font_, SK_ColorWHITE, | |
805 MirroredLeftPointForRect(title_bounds_), title_bounds_.y(), | |
806 title_bounds_.width(), title_bounds_.height()); | |
807 /* TODO(pkasting): If this window is active, we should also draw a drop | |
808 * shadow on the title. This is tricky, because we don't want to hardcode a | |
809 * shadow color (since we want to work with various themes), but we can't | |
810 * alpha-blend either (since the Windows text APIs don't really do this). | |
811 * So we'd need to sample the background color at the right location and | |
812 * synthesize a good shadow color. */ | |
813 } | |
814 } | |
815 | |
816 void OpaqueNonClientView::PaintToolbarBackground(ChromeCanvas* canvas) { | |
817 if (!browser_view_->IsToolbarVisible()) | |
818 return; | |
819 | |
820 gfx::Rect toolbar_bounds(browser_view_->GetToolbarBounds()); | |
821 gfx::Point toolbar_origin(toolbar_bounds.origin()); | |
822 View::ConvertPointToView(frame_->client_view(), this, &toolbar_origin); | |
823 toolbar_bounds.set_origin(toolbar_origin); | |
824 | |
825 SkBitmap* toolbar_left = | |
826 resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP_LEFT); | |
827 canvas->DrawBitmapInt(*toolbar_left, | |
828 toolbar_bounds.x() - toolbar_left->width(), | |
829 toolbar_bounds.y()); | |
830 | |
831 // Gross hack: We split the toolbar image into two pieces, since sometimes | |
832 // (popup mode) the toolbar isn't tall enough to show the whole image. The | |
833 // split happens between the top shadow section and the bottom gradient | |
834 // section so that we never break the gradient. | |
835 int split_point = kFrameShadowThickness * 2; | |
836 SkBitmap* toolbar_center = | |
837 resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP); | |
838 canvas->TileImageInt(*toolbar_center, 0, 0, toolbar_bounds.x(), | |
839 toolbar_bounds.y(), toolbar_bounds.width(), split_point); | |
840 canvas->TileImageInt(*toolbar_center, 0, | |
841 toolbar_center->height() - toolbar_bounds.height() + split_point, | |
842 toolbar_bounds.x(), toolbar_bounds.y() + split_point, | |
843 toolbar_bounds.width(), toolbar_bounds.height() - split_point); | |
844 | |
845 canvas->DrawBitmapInt( | |
846 *resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP_RIGHT), | |
847 toolbar_bounds.right(), toolbar_bounds.y()); | |
848 } | |
849 | |
850 void OpaqueNonClientView::PaintOTRAvatar(ChromeCanvas* canvas) { | |
851 if (!browser_view_->ShouldShowOffTheRecordAvatar()) | |
852 return; | |
853 | |
854 SkBitmap otr_avatar_icon = browser_view_->GetOTRAvatarIcon(); | |
855 canvas->DrawBitmapInt(otr_avatar_icon, 0, | |
856 (otr_avatar_icon.height() - otr_avatar_bounds_.height()) / 2, | |
857 otr_avatar_bounds_.width(), otr_avatar_bounds_.height(), | |
858 MirroredLeftPointForRect(otr_avatar_bounds_), otr_avatar_bounds_.y(), | |
859 otr_avatar_bounds_.width(), otr_avatar_bounds_.height(), false); | |
860 } | |
861 | |
862 void OpaqueNonClientView::PaintRestoredClientEdge(ChromeCanvas* canvas) { | |
863 int client_area_top = frame_->client_view()->y(); | |
864 | |
865 gfx::Rect client_area_bounds = CalculateClientAreaBounds(width(), height()); | |
866 if (browser_view_->IsToolbarVisible()) { | |
867 // The client edges start below the toolbar upper corner images regardless | |
868 // of how tall the toolbar itself is. | |
869 client_area_top += browser_view_->GetToolbarBounds().y() + | |
870 resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP_LEFT)->height(); | |
871 } else { | |
872 // The toolbar isn't going to draw a client edge for us, so draw one | |
873 // ourselves. | |
874 // This next calculation is necessary because the top center bitmap is | |
875 // shorter than the top left and right bitmaps. We need their top edges to | |
876 // line up, and we need the left and right edges to start below the corners' | |
877 // bottoms. | |
878 SkBitmap* top_center = | |
879 resources()->GetPartBitmap(FRAME_NO_TOOLBAR_TOP_CENTER); | |
880 SkBitmap* top_left = resources()->GetPartBitmap(FRAME_NO_TOOLBAR_TOP_LEFT); | |
881 int top_edge_y = client_area_top - top_center->height(); | |
882 client_area_top = top_edge_y + top_left->height(); | |
883 canvas->DrawBitmapInt(*top_left, client_area_bounds.x() - top_left->width(), | |
884 top_edge_y); | |
885 canvas->TileImageInt(*top_center, client_area_bounds.x(), top_edge_y, | |
886 client_area_bounds.width(), top_center->height()); | |
887 canvas->DrawBitmapInt( | |
888 *resources()->GetPartBitmap(FRAME_NO_TOOLBAR_TOP_RIGHT), | |
889 client_area_bounds.right(), top_edge_y); | |
890 } | |
891 | |
892 int client_area_bottom = | |
893 std::max(client_area_top, height() - NonClientBorderThickness()); | |
894 int client_area_height = client_area_bottom - client_area_top; | |
895 SkBitmap* right = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_RIGHT); | |
896 canvas->TileImageInt(*right, client_area_bounds.right(), client_area_top, | |
897 right->width(), client_area_height); | |
898 | |
899 canvas->DrawBitmapInt( | |
900 *resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM_RIGHT), | |
901 client_area_bounds.right(), client_area_bottom); | |
902 | |
903 SkBitmap* bottom = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM); | |
904 canvas->TileImageInt(*bottom, client_area_bounds.x(), | |
905 client_area_bottom, client_area_bounds.width(), | |
906 bottom->height()); | |
907 | |
908 SkBitmap* bottom_left = | |
909 resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM_LEFT); | |
910 canvas->DrawBitmapInt(*bottom_left, | |
911 client_area_bounds.x() - bottom_left->width(), client_area_bottom); | |
912 | |
913 SkBitmap* left = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_LEFT); | |
914 canvas->TileImageInt(*left, client_area_bounds.x() - left->width(), | |
915 client_area_top, left->width(), client_area_height); | |
916 } | |
917 | |
918 void OpaqueNonClientView::LayoutWindowControls() { | |
919 close_button_->SetImageAlignment(views::Button::ALIGN_LEFT, | |
920 views::Button::ALIGN_BOTTOM); | |
921 // Maximized buttons start at window top so that even if their images aren't | |
922 // drawn flush with the screen edge, they still obey Fitts' Law. | |
923 bool is_maximized = frame_->IsMaximized(); | |
924 int frame_thickness = FrameBorderThickness(); | |
925 int caption_y = is_maximized ? frame_thickness : kCaptionTopSpacing; | |
926 int top_extra_height = is_maximized ? kCaptionTopSpacing : 0; | |
927 // There should always be the same number of non-shadow pixels visible to the | |
928 // side of the caption buttons. In maximized mode we extend the rightmost | |
929 // button to the screen corner to obey Fitts' Law. | |
930 int right_extra_width = is_maximized ? | |
931 (kFrameBorderThickness - kFrameShadowThickness) : 0; | |
932 int right_spacing = is_maximized ? | |
933 (GetSystemMetrics(SM_CXSIZEFRAME) + right_extra_width) : frame_thickness; | |
934 gfx::Size close_button_size = close_button_->GetPreferredSize(); | |
935 close_button_->SetBounds(width() - close_button_size.width() - right_spacing, | |
936 caption_y, | |
937 close_button_size.width() + right_extra_width, | |
938 close_button_size.height() + top_extra_height); | |
939 | |
940 // When the window is restored, we show a maximized button; otherwise, we show | |
941 // a restore button. | |
942 bool is_restored = !is_maximized && !frame_->IsMinimized(); | |
943 views::Button* invisible_button = is_restored ? | |
944 restore_button_ : maximize_button_; | |
945 invisible_button->SetVisible(false); | |
946 | |
947 views::Button* visible_button = is_restored ? | |
948 maximize_button_ : restore_button_; | |
949 visible_button->SetVisible(true); | |
950 visible_button->SetImageAlignment(views::Button::ALIGN_LEFT, | |
951 views::Button::ALIGN_BOTTOM); | |
952 gfx::Size visible_button_size = visible_button->GetPreferredSize(); | |
953 visible_button->SetBounds(close_button_->x() - visible_button_size.width(), | |
954 caption_y, visible_button_size.width(), | |
955 visible_button_size.height() + top_extra_height); | |
956 | |
957 minimize_button_->SetVisible(true); | |
958 minimize_button_->SetImageAlignment(views::Button::ALIGN_LEFT, | |
959 views::Button::ALIGN_BOTTOM); | |
960 gfx::Size minimize_button_size = minimize_button_->GetPreferredSize(); | |
961 minimize_button_->SetBounds( | |
962 visible_button->x() - minimize_button_size.width(), caption_y, | |
963 minimize_button_size.width(), | |
964 minimize_button_size.height() + top_extra_height); | |
965 } | |
966 | |
967 void OpaqueNonClientView::LayoutDistributorLogo() { | |
968 // Always lay out the logo, even when it's not present, so we can lay out the | |
969 // window title based on its position. | |
970 if (distributor_logo_) { | |
971 logo_bounds_.SetRect(minimize_button_->x() - distributor_logo_->width() - | |
972 kLogoCaptionSpacing, TopResizeHeight(), distributor_logo_->width(), | |
973 distributor_logo_->height()); | |
974 } else { | |
975 logo_bounds_.SetRect(minimize_button_->x(), TopResizeHeight(), 0, 0); | |
976 } | |
977 } | |
978 | |
979 void OpaqueNonClientView::LayoutTitleBar() { | |
980 // Always lay out the icon, even when it's not present, so we can lay out the | |
981 // window title based on its position. | |
982 int frame_thickness = FrameBorderThickness(); | |
983 int icon_x = frame_thickness + kIconLeftSpacing; | |
984 | |
985 // The usable height of the titlebar area is the total height minus the top | |
986 // resize border and any edge area we draw at its bottom. | |
987 int title_top_spacing, title_thickness; | |
988 int top_height = TitleCoordinates(&title_top_spacing, &title_thickness); | |
989 int available_height = top_height - frame_thickness - | |
990 UnavailablePixelsAtBottomOfNonClientHeight(); | |
991 | |
992 // The icon takes up a constant fraction of the available height, down to a | |
993 // minimum size, and is always an even number of pixels on a side (presumably | |
994 // to make scaled icons look better). It's centered within the usable height. | |
995 int icon_size = std::max((available_height * kIconHeightFractionNumerator / | |
996 kIconHeightFractionDenominator) / 2 * 2, kIconMinimumSize); | |
997 int icon_y = ((available_height - icon_size) / 2) + frame_thickness; | |
998 | |
999 // Hack: Our frame border has a different "3D look" than Windows'. Theirs has | |
1000 // a more complex gradient on the top that they push their icon/title below; | |
1001 // then the maximized window cuts this off and the icon/title are centered in | |
1002 // the remaining space. Because the apparent shape of our border is simpler, | |
1003 // using the same positioning makes things look slightly uncentered with | |
1004 // restored windows, so we come up to compensate. | |
1005 if (!frame_->IsMaximized()) | |
1006 icon_y -= kIconRestoredAdjust; | |
1007 | |
1008 views::WindowDelegate* d = frame_->window_delegate(); | |
1009 if (!d->ShouldShowWindowIcon()) | |
1010 icon_size = 0; | |
1011 if (window_icon_) | |
1012 window_icon_->SetBounds(icon_x, icon_y, icon_size, icon_size); | |
1013 | |
1014 // Size the title, if visible. | |
1015 if (d->ShouldShowWindowTitle()) { | |
1016 int title_x = icon_x + icon_size + | |
1017 (d->ShouldShowWindowIcon() ? kIconTitleSpacing : 0); | |
1018 title_bounds_.SetRect(title_x, | |
1019 title_top_spacing + ((title_thickness - title_font_.height()) / 2), | |
1020 std::max(0, logo_bounds_.x() - kTitleLogoSpacing - title_x), | |
1021 title_font_.height()); | |
1022 } | |
1023 } | |
1024 | |
1025 void OpaqueNonClientView::LayoutOTRAvatar() { | |
1026 SkBitmap otr_avatar_icon = browser_view_->GetOTRAvatarIcon(); | |
1027 int top_height = NonClientTopBorderHeight(); | |
1028 int tabstrip_height, otr_height; | |
1029 if (browser_view_->IsTabStripVisible()) { | |
1030 tabstrip_height = browser_view_->GetTabStripHeight() - kOTRBottomSpacing; | |
1031 otr_height = frame_->IsMaximized() ? | |
1032 (tabstrip_height - kOTRMaximizedTopSpacing) : | |
1033 otr_avatar_icon.height(); | |
1034 } else { | |
1035 tabstrip_height = otr_height = 0; | |
1036 } | |
1037 otr_avatar_bounds_.SetRect(NonClientBorderThickness() + kOTRSideSpacing, | |
1038 top_height + tabstrip_height - otr_height, | |
1039 otr_avatar_icon.width(), otr_height); | |
1040 } | |
1041 | |
1042 void OpaqueNonClientView::LayoutClientView() { | |
1043 frame_->client_view()->SetBounds(CalculateClientAreaBounds(width(), | |
1044 height())); | |
1045 } | |
1046 | |
1047 // static | |
1048 void OpaqueNonClientView::InitClass() { | |
1049 static bool initialized = false; | |
1050 if (!initialized) { | |
1051 active_resources_ = new ActiveWindowResources; | |
1052 inactive_resources_ = new InactiveWindowResources; | |
1053 | |
1054 #if defined(GOOGLE_CHROME_BUILD) | |
1055 distributor_logo_ = ResourceBundle::GetSharedInstance(). | |
1056 GetBitmapNamed(IDR_DISTRIBUTOR_LOGO_LIGHT); | |
1057 #endif | |
1058 | |
1059 initialized = true; | |
1060 } | |
1061 } | |
1062 | |
1063 // static | |
1064 void OpaqueNonClientView::InitAppWindowResources() { | |
1065 static bool initialized = false; | |
1066 if (!initialized) { | |
1067 title_font_ = win_util::GetWindowTitleFont(); | |
1068 initialized = true; | |
1069 } | |
1070 } | |
OLD | NEW |