OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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 "views/window/custom_frame_view.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/utf_string_conversions.h" | |
10 #include "grit/ui_resources.h" | |
11 #include "grit/ui_strings.h" | |
12 #include "ui/base/hit_test.h" | |
13 #include "ui/base/l10n/l10n_util.h" | |
14 #include "ui/base/resource/resource_bundle.h" | |
15 #include "ui/gfx/canvas.h" | |
16 #include "ui/gfx/font.h" | |
17 #include "ui/gfx/path.h" | |
18 #include "views/widget/widget_delegate.h" | |
19 #include "views/window/client_view.h" | |
20 #include "views/window/window_resources.h" | |
21 #include "views/window/window_shape.h" | |
22 | |
23 #if defined(USE_AURA) | |
24 #include "views/widget/native_widget_aura.h" | |
25 #elif defined(OS_WIN) | |
26 #include "views/widget/native_widget_win.h" | |
27 #endif | |
28 | |
29 namespace views { | |
30 | |
31 // static | |
32 gfx::Font* CustomFrameView::title_font_ = NULL; | |
33 | |
34 namespace { | |
35 // The frame border is only visible in restored mode and is hardcoded to 4 px on | |
36 // each side regardless of the system window border size. | |
37 const int kFrameBorderThickness = 4; | |
38 // Various edges of the frame border have a 1 px shadow along their edges; in a | |
39 // few cases we shift elements based on this amount for visual appeal. | |
40 const int kFrameShadowThickness = 1; | |
41 // While resize areas on Windows are normally the same size as the window | |
42 // borders, our top area is shrunk by 1 px to make it easier to move the window | |
43 // around with our thinner top grabbable strip. (Incidentally, our side and | |
44 // bottom resize areas don't match the frame border thickness either -- they | |
45 // span the whole nonclient area, so there's no "dead zone" for the mouse.) | |
46 const int kTopResizeAdjust = 1; | |
47 // In the window corners, the resize areas don't actually expand bigger, but the | |
48 // 16 px at the end of each edge triggers diagonal resizing. | |
49 const int kResizeAreaCornerSize = 16; | |
50 // The titlebar never shrinks too short to show the caption button plus some | |
51 // padding below it. | |
52 const int kCaptionButtonHeightWithPadding = 19; | |
53 // The titlebar has a 2 px 3D edge along the top and bottom. | |
54 const int kTitlebarTopAndBottomEdgeThickness = 2; | |
55 // The icon is inset 2 px from the left frame border. | |
56 const int kIconLeftSpacing = 2; | |
57 // The icon never shrinks below 16 px on a side. | |
58 const int kIconMinimumSize = 16; | |
59 // There is a 4 px gap between the icon and the title text. | |
60 const int kIconTitleSpacing = 4; | |
61 // There is a 5 px gap between the title text and the caption buttons. | |
62 const int kTitleCaptionSpacing = 5; | |
63 } | |
64 | |
65 /////////////////////////////////////////////////////////////////////////////// | |
66 // CustomFrameView, public: | |
67 | |
68 CustomFrameView::CustomFrameView(Widget* frame) | |
69 : ALLOW_THIS_IN_INITIALIZER_LIST(close_button_(new ImageButton(this))), | |
70 ALLOW_THIS_IN_INITIALIZER_LIST(restore_button_(new ImageButton(this))), | |
71 ALLOW_THIS_IN_INITIALIZER_LIST(maximize_button_(new ImageButton(this))), | |
72 ALLOW_THIS_IN_INITIALIZER_LIST(minimize_button_(new ImageButton(this))), | |
73 window_icon_(NULL), | |
74 should_show_minmax_buttons_(false), | |
75 should_show_client_edge_(false), | |
76 frame_(frame) { | |
77 InitClass(); | |
78 | |
79 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
80 | |
81 close_button_->SetAccessibleName( | |
82 l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE)); | |
83 | |
84 // Close button images will be set in LayoutWindowControls(). | |
85 AddChildView(close_button_); | |
86 | |
87 restore_button_->SetAccessibleName( | |
88 l10n_util::GetStringUTF16(IDS_APP_ACCNAME_RESTORE)); | |
89 restore_button_->SetImage(CustomButton::BS_NORMAL, | |
90 rb.GetBitmapNamed(IDR_RESTORE)); | |
91 restore_button_->SetImage(CustomButton::BS_HOT, | |
92 rb.GetBitmapNamed(IDR_RESTORE_H)); | |
93 restore_button_->SetImage(CustomButton::BS_PUSHED, | |
94 rb.GetBitmapNamed(IDR_RESTORE_P)); | |
95 AddChildView(restore_button_); | |
96 | |
97 maximize_button_->SetAccessibleName( | |
98 l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE)); | |
99 maximize_button_->SetImage(CustomButton::BS_NORMAL, | |
100 rb.GetBitmapNamed(IDR_MAXIMIZE)); | |
101 maximize_button_->SetImage(CustomButton::BS_HOT, | |
102 rb.GetBitmapNamed(IDR_MAXIMIZE_H)); | |
103 maximize_button_->SetImage(CustomButton::BS_PUSHED, | |
104 rb.GetBitmapNamed(IDR_MAXIMIZE_P)); | |
105 AddChildView(maximize_button_); | |
106 | |
107 minimize_button_->SetAccessibleName( | |
108 l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE)); | |
109 minimize_button_->SetImage(CustomButton::BS_NORMAL, | |
110 rb.GetBitmapNamed(IDR_MINIMIZE)); | |
111 minimize_button_->SetImage(CustomButton::BS_HOT, | |
112 rb.GetBitmapNamed(IDR_MINIMIZE_H)); | |
113 minimize_button_->SetImage(CustomButton::BS_PUSHED, | |
114 rb.GetBitmapNamed(IDR_MINIMIZE_P)); | |
115 AddChildView(minimize_button_); | |
116 | |
117 should_show_minmax_buttons_ = frame_->widget_delegate()->CanMaximize(); | |
118 should_show_client_edge_ = frame_->widget_delegate()->ShouldShowClientEdge(); | |
119 | |
120 if (frame_->widget_delegate()->ShouldShowWindowIcon()) { | |
121 window_icon_ = new ImageButton(this); | |
122 AddChildView(window_icon_); | |
123 } | |
124 } | |
125 | |
126 CustomFrameView::~CustomFrameView() { | |
127 } | |
128 | |
129 /////////////////////////////////////////////////////////////////////////////// | |
130 // CustomFrameView, NonClientFrameView implementation: | |
131 | |
132 gfx::Rect CustomFrameView::GetBoundsForClientView() const { | |
133 return client_view_bounds_; | |
134 } | |
135 | |
136 gfx::Rect CustomFrameView::GetWindowBoundsForClientBounds( | |
137 const gfx::Rect& client_bounds) const { | |
138 int top_height = NonClientTopBorderHeight(); | |
139 int border_thickness = NonClientBorderThickness(); | |
140 return gfx::Rect(std::max(0, client_bounds.x() - border_thickness), | |
141 std::max(0, client_bounds.y() - top_height), | |
142 client_bounds.width() + (2 * border_thickness), | |
143 client_bounds.height() + top_height + border_thickness); | |
144 } | |
145 | |
146 int CustomFrameView::NonClientHitTest(const gfx::Point& point) { | |
147 // Sanity check. | |
148 if (!bounds().Contains(point)) | |
149 return HTNOWHERE; | |
150 | |
151 int frame_component = frame_->client_view()->NonClientHitTest(point); | |
152 | |
153 // See if we're in the sysmenu region. (We check the ClientView first to be | |
154 // consistent with OpaqueBrowserFrameView; it's not really necessary here.) | |
155 gfx::Rect sysmenu_rect(IconBounds()); | |
156 // In maximized mode we extend the rect to the screen corner to take advantage | |
157 // of Fitts' Law. | |
158 if (frame_->IsMaximized()) | |
159 sysmenu_rect.SetRect(0, 0, sysmenu_rect.right(), sysmenu_rect.bottom()); | |
160 sysmenu_rect.set_x(GetMirroredXForRect(sysmenu_rect)); | |
161 if (sysmenu_rect.Contains(point)) | |
162 return (frame_component == HTCLIENT) ? HTCLIENT : HTSYSMENU; | |
163 | |
164 if (frame_component != HTNOWHERE) | |
165 return frame_component; | |
166 | |
167 // Then see if the point is within any of the window controls. | |
168 if (close_button_->GetMirroredBounds().Contains(point)) | |
169 return HTCLOSE; | |
170 if (restore_button_->GetMirroredBounds().Contains(point)) | |
171 return HTMAXBUTTON; | |
172 if (maximize_button_->GetMirroredBounds().Contains(point)) | |
173 return HTMAXBUTTON; | |
174 if (minimize_button_->GetMirroredBounds().Contains(point)) | |
175 return HTMINBUTTON; | |
176 if (window_icon_ && window_icon_->GetMirroredBounds().Contains(point)) | |
177 return HTSYSMENU; | |
178 | |
179 int window_component = GetHTComponentForFrame(point, FrameBorderThickness(), | |
180 NonClientBorderThickness(), kResizeAreaCornerSize, kResizeAreaCornerSize, | |
181 frame_->widget_delegate()->CanResize()); | |
182 // Fall back to the caption if no other component matches. | |
183 return (window_component == HTNOWHERE) ? HTCAPTION : window_component; | |
184 } | |
185 | |
186 void CustomFrameView::GetWindowMask(const gfx::Size& size, | |
187 gfx::Path* window_mask) { | |
188 DCHECK(window_mask); | |
189 if (frame_->IsMaximized()) | |
190 return; | |
191 | |
192 views::GetDefaultWindowMask(size, window_mask); | |
193 } | |
194 | |
195 void CustomFrameView::EnableClose(bool enable) { | |
196 close_button_->SetEnabled(enable); | |
197 } | |
198 | |
199 void CustomFrameView::ResetWindowControls() { | |
200 restore_button_->SetState(CustomButton::BS_NORMAL); | |
201 minimize_button_->SetState(CustomButton::BS_NORMAL); | |
202 maximize_button_->SetState(CustomButton::BS_NORMAL); | |
203 // The close button isn't affected by this constraint. | |
204 } | |
205 | |
206 void CustomFrameView::UpdateWindowIcon() { | |
207 window_icon_->SchedulePaint(); | |
208 } | |
209 | |
210 /////////////////////////////////////////////////////////////////////////////// | |
211 // CustomFrameView, View overrides: | |
212 | |
213 void CustomFrameView::OnPaint(gfx::Canvas* canvas) { | |
214 if (frame_->IsMaximized()) | |
215 PaintMaximizedFrameBorder(canvas); | |
216 else | |
217 PaintRestoredFrameBorder(canvas); | |
218 PaintTitleBar(canvas); | |
219 if (ShouldShowClientEdge()) | |
220 PaintRestoredClientEdge(canvas); | |
221 } | |
222 | |
223 void CustomFrameView::Layout() { | |
224 LayoutWindowControls(); | |
225 LayoutTitleBar(); | |
226 LayoutClientView(); | |
227 } | |
228 | |
229 gfx::Size CustomFrameView::GetPreferredSize() { | |
230 gfx::Size pref = frame_->client_view()->GetPreferredSize(); | |
231 gfx::Rect bounds(0, 0, pref.width(), pref.height()); | |
232 return frame_->non_client_view()->GetWindowBoundsForClientBounds( | |
233 bounds).size(); | |
234 } | |
235 | |
236 /////////////////////////////////////////////////////////////////////////////// | |
237 // CustomFrameView, ButtonListener implementation: | |
238 | |
239 void CustomFrameView::ButtonPressed(Button* sender, const views::Event& event) { | |
240 if (sender == close_button_) | |
241 frame_->Close(); | |
242 else if (sender == minimize_button_) | |
243 frame_->Minimize(); | |
244 else if (sender == maximize_button_) | |
245 frame_->Maximize(); | |
246 else if (sender == restore_button_) | |
247 frame_->Restore(); | |
248 } | |
249 | |
250 /////////////////////////////////////////////////////////////////////////////// | |
251 // CustomFrameView, private: | |
252 | |
253 int CustomFrameView::FrameBorderThickness() const { | |
254 return frame_->IsMaximized() ? 0 : kFrameBorderThickness; | |
255 } | |
256 | |
257 int CustomFrameView::NonClientBorderThickness() const { | |
258 // In maximized mode, we don't show a client edge. | |
259 return FrameBorderThickness() + | |
260 (ShouldShowClientEdge() ? kClientEdgeThickness : 0); | |
261 } | |
262 | |
263 int CustomFrameView::NonClientTopBorderHeight() const { | |
264 return std::max(FrameBorderThickness() + IconSize(), | |
265 CaptionButtonY() + kCaptionButtonHeightWithPadding) + | |
266 TitlebarBottomThickness(); | |
267 } | |
268 | |
269 int CustomFrameView::CaptionButtonY() const { | |
270 // Maximized buttons start at window top so that even if their images aren't | |
271 // drawn flush with the screen edge, they still obey Fitts' Law. | |
272 return frame_->IsMaximized() ? FrameBorderThickness() : kFrameShadowThickness; | |
273 } | |
274 | |
275 int CustomFrameView::TitlebarBottomThickness() const { | |
276 return kTitlebarTopAndBottomEdgeThickness + | |
277 (ShouldShowClientEdge() ? kClientEdgeThickness : 0); | |
278 } | |
279 | |
280 int CustomFrameView::IconSize() const { | |
281 #if defined(OS_WIN) | |
282 // This metric scales up if either the titlebar height or the titlebar font | |
283 // size are increased. | |
284 return GetSystemMetrics(SM_CYSMICON); | |
285 #else | |
286 return std::max(title_font_->GetHeight(), kIconMinimumSize); | |
287 #endif | |
288 } | |
289 | |
290 bool CustomFrameView::ShouldShowClientEdge() const { | |
291 return should_show_client_edge_ && !frame_->IsMaximized(); | |
292 } | |
293 | |
294 gfx::Rect CustomFrameView::IconBounds() const { | |
295 int size = IconSize(); | |
296 int frame_thickness = FrameBorderThickness(); | |
297 // Our frame border has a different "3D look" than Windows'. Theirs has a | |
298 // more complex gradient on the top that they push their icon/title below; | |
299 // then the maximized window cuts this off and the icon/title are centered | |
300 // in the remaining space. Because the apparent shape of our border is | |
301 // simpler, using the same positioning makes things look slightly uncentered | |
302 // with restored windows, so when the window is restored, instead of | |
303 // calculating the remaining space from below the frame border, we calculate | |
304 // from below the 3D edge. | |
305 int unavailable_px_at_top = frame_->IsMaximized() ? | |
306 frame_thickness : kTitlebarTopAndBottomEdgeThickness; | |
307 // When the icon is shorter than the minimum space we reserve for the caption | |
308 // button, we vertically center it. We want to bias rounding to put extra | |
309 // space above the icon, since the 3D edge (+ client edge, for restored | |
310 // windows) below looks (to the eye) more like additional space than does the | |
311 // 3D edge (or nothing at all, for maximized windows) above; hence the +1. | |
312 int y = unavailable_px_at_top + (NonClientTopBorderHeight() - | |
313 unavailable_px_at_top - size - TitlebarBottomThickness() + 1) / 2; | |
314 return gfx::Rect(frame_thickness + kIconLeftSpacing, y, size, size); | |
315 } | |
316 | |
317 void CustomFrameView::PaintRestoredFrameBorder(gfx::Canvas* canvas) { | |
318 // Window frame mode. | |
319 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
320 | |
321 SkBitmap* frame_image; | |
322 SkColor frame_color; | |
323 if (frame_->IsActive()) { | |
324 frame_image = rb.GetBitmapNamed(IDR_FRAME); | |
325 frame_color = ResourceBundle::frame_color; | |
326 } else { | |
327 frame_image = rb.GetBitmapNamed(IDR_FRAME_INACTIVE); | |
328 frame_color = ResourceBundle::frame_color_inactive; | |
329 } | |
330 | |
331 SkBitmap* top_left_corner = rb.GetBitmapNamed(IDR_WINDOW_TOP_LEFT_CORNER); | |
332 SkBitmap* top_right_corner = | |
333 rb.GetBitmapNamed(IDR_WINDOW_TOP_RIGHT_CORNER); | |
334 SkBitmap* top_edge = rb.GetBitmapNamed(IDR_WINDOW_TOP_CENTER); | |
335 SkBitmap* right_edge = rb.GetBitmapNamed(IDR_WINDOW_RIGHT_SIDE); | |
336 SkBitmap* left_edge = rb.GetBitmapNamed(IDR_WINDOW_LEFT_SIDE); | |
337 SkBitmap* bottom_left_corner = | |
338 rb.GetBitmapNamed(IDR_WINDOW_BOTTOM_LEFT_CORNER); | |
339 SkBitmap* bottom_right_corner = | |
340 rb.GetBitmapNamed(IDR_WINDOW_BOTTOM_RIGHT_CORNER); | |
341 SkBitmap* bottom_edge = rb.GetBitmapNamed(IDR_WINDOW_BOTTOM_CENTER); | |
342 | |
343 // Fill with the frame color first so we have a constant background for | |
344 // areas not covered by the theme image. | |
345 canvas->FillRect(frame_color, | |
346 gfx::Rect(0, 0, width(), frame_image->height())); | |
347 | |
348 int remaining_height = height() - frame_image->height(); | |
349 if (remaining_height > 0) { | |
350 // Now fill down the sides. | |
351 canvas->FillRect(frame_color, | |
352 gfx::Rect(0, frame_image->height(), left_edge->width(), | |
353 remaining_height)); | |
354 canvas->FillRect(frame_color, | |
355 gfx::Rect(width() - right_edge->width(), | |
356 frame_image->height(), right_edge->width(), | |
357 remaining_height)); | |
358 int center_width = width() - left_edge->width() - right_edge->width(); | |
359 if (center_width > 0) { | |
360 // Now fill the bottom area. | |
361 canvas->FillRect(frame_color, | |
362 gfx::Rect(left_edge->width(), | |
363 height() - bottom_edge->height(), | |
364 center_width, bottom_edge->height())); | |
365 } | |
366 } | |
367 | |
368 // Draw the theme frame. | |
369 canvas->TileImageInt(*frame_image, 0, 0, width(), frame_image->height()); | |
370 | |
371 // Top. | |
372 canvas->DrawBitmapInt(*top_left_corner, 0, 0); | |
373 canvas->TileImageInt(*top_edge, top_left_corner->width(), 0, | |
374 width() - top_right_corner->width(), top_edge->height()); | |
375 canvas->DrawBitmapInt(*top_right_corner, | |
376 width() - top_right_corner->width(), 0); | |
377 | |
378 // Right. | |
379 canvas->TileImageInt(*right_edge, width() - right_edge->width(), | |
380 top_right_corner->height(), right_edge->width(), | |
381 height() - top_right_corner->height() - bottom_right_corner->height()); | |
382 | |
383 // Bottom. | |
384 canvas->DrawBitmapInt(*bottom_right_corner, | |
385 width() - bottom_right_corner->width(), | |
386 height() - bottom_right_corner->height()); | |
387 canvas->TileImageInt(*bottom_edge, bottom_left_corner->width(), | |
388 height() - bottom_edge->height(), | |
389 width() - bottom_left_corner->width() - bottom_right_corner->width(), | |
390 bottom_edge->height()); | |
391 canvas->DrawBitmapInt(*bottom_left_corner, 0, | |
392 height() - bottom_left_corner->height()); | |
393 | |
394 // Left. | |
395 canvas->TileImageInt(*left_edge, 0, top_left_corner->height(), | |
396 left_edge->width(), | |
397 height() - top_left_corner->height() - bottom_left_corner->height()); | |
398 } | |
399 | |
400 void CustomFrameView::PaintMaximizedFrameBorder(gfx::Canvas* canvas) { | |
401 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
402 | |
403 SkBitmap* frame_image = rb.GetBitmapNamed(frame_->IsActive() ? | |
404 IDR_FRAME : IDR_FRAME_INACTIVE); | |
405 canvas->TileImageInt(*frame_image, 0, FrameBorderThickness(), width(), | |
406 frame_image->height()); | |
407 | |
408 // The bottom of the titlebar actually comes from the top of the Client Edge | |
409 // graphic, with the actual client edge clipped off the bottom. | |
410 SkBitmap* titlebar_bottom = rb.GetBitmapNamed(IDR_APP_TOP_CENTER); | |
411 int edge_height = titlebar_bottom->height() - | |
412 (ShouldShowClientEdge() ? kClientEdgeThickness : 0); | |
413 canvas->TileImageInt(*titlebar_bottom, 0, | |
414 frame_->client_view()->y() - edge_height, width(), edge_height); | |
415 } | |
416 | |
417 void CustomFrameView::PaintTitleBar(gfx::Canvas* canvas) { | |
418 WidgetDelegate* d = frame_->widget_delegate(); | |
419 | |
420 // It seems like in some conditions we can be asked to paint after the window | |
421 // that contains us is WM_DESTROYed. At this point, our delegate is NULL. The | |
422 // correct long term fix may be to shut down the RootView in WM_DESTROY. | |
423 if (!d) | |
424 return; | |
425 | |
426 canvas->DrawStringInt(d->GetWindowTitle(), *title_font_, | |
427 SK_ColorWHITE, GetMirroredXForRect(title_bounds_), | |
428 title_bounds_.y(), title_bounds_.width(), | |
429 title_bounds_.height()); | |
430 } | |
431 | |
432 void CustomFrameView::PaintRestoredClientEdge(gfx::Canvas* canvas) { | |
433 gfx::Rect client_area_bounds = frame_->client_view()->bounds(); | |
434 int client_area_top = client_area_bounds.y(); | |
435 | |
436 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
437 SkBitmap* top_left = rb.GetBitmapNamed(IDR_APP_TOP_LEFT); | |
438 SkBitmap* top = rb.GetBitmapNamed(IDR_APP_TOP_CENTER); | |
439 SkBitmap* top_right = rb.GetBitmapNamed(IDR_APP_TOP_RIGHT); | |
440 SkBitmap* right = rb.GetBitmapNamed(IDR_CONTENT_RIGHT_SIDE); | |
441 SkBitmap* bottom_right = | |
442 rb.GetBitmapNamed(IDR_CONTENT_BOTTOM_RIGHT_CORNER); | |
443 SkBitmap* bottom = rb.GetBitmapNamed(IDR_CONTENT_BOTTOM_CENTER); | |
444 SkBitmap* bottom_left = | |
445 rb.GetBitmapNamed(IDR_CONTENT_BOTTOM_LEFT_CORNER); | |
446 SkBitmap* left = rb.GetBitmapNamed(IDR_CONTENT_LEFT_SIDE); | |
447 | |
448 // Top. | |
449 int top_edge_y = client_area_top - top->height(); | |
450 canvas->DrawBitmapInt(*top_left, client_area_bounds.x() - top_left->width(), | |
451 top_edge_y); | |
452 canvas->TileImageInt(*top, client_area_bounds.x(), top_edge_y, | |
453 client_area_bounds.width(), top->height()); | |
454 canvas->DrawBitmapInt(*top_right, client_area_bounds.right(), top_edge_y); | |
455 | |
456 // Right. | |
457 int client_area_bottom = | |
458 std::max(client_area_top, client_area_bounds.bottom()); | |
459 int client_area_height = client_area_bottom - client_area_top; | |
460 canvas->TileImageInt(*right, client_area_bounds.right(), client_area_top, | |
461 right->width(), client_area_height); | |
462 | |
463 // Bottom. | |
464 canvas->DrawBitmapInt(*bottom_right, client_area_bounds.right(), | |
465 client_area_bottom); | |
466 canvas->TileImageInt(*bottom, client_area_bounds.x(), client_area_bottom, | |
467 client_area_bounds.width(), bottom_right->height()); | |
468 canvas->DrawBitmapInt(*bottom_left, | |
469 client_area_bounds.x() - bottom_left->width(), client_area_bottom); | |
470 | |
471 // Left. | |
472 canvas->TileImageInt(*left, client_area_bounds.x() - left->width(), | |
473 client_area_top, left->width(), client_area_height); | |
474 | |
475 // Draw the toolbar color to fill in the edges. | |
476 canvas->DrawRectInt(ResourceBundle::toolbar_color, | |
477 client_area_bounds.x() - 1, client_area_top - 1, | |
478 client_area_bounds.width() + 1, client_area_bottom - client_area_top + 1); | |
479 } | |
480 | |
481 void CustomFrameView::LayoutWindowControls() { | |
482 close_button_->SetImageAlignment(ImageButton::ALIGN_LEFT, | |
483 ImageButton::ALIGN_BOTTOM); | |
484 int caption_y = CaptionButtonY(); | |
485 bool is_maximized = frame_->IsMaximized(); | |
486 // There should always be the same number of non-shadow pixels visible to the | |
487 // side of the caption buttons. In maximized mode we extend the rightmost | |
488 // button to the screen corner to obey Fitts' Law. | |
489 int right_extra_width = is_maximized ? | |
490 (kFrameBorderThickness - kFrameShadowThickness) : 0; | |
491 gfx::Size close_button_size = close_button_->GetPreferredSize(); | |
492 close_button_->SetBounds(width() - FrameBorderThickness() - | |
493 right_extra_width - close_button_size.width(), caption_y, | |
494 close_button_size.width() + right_extra_width, | |
495 close_button_size.height()); | |
496 | |
497 // When the window is restored, we show a maximized button; otherwise, we show | |
498 // a restore button. | |
499 bool is_restored = !is_maximized && !frame_->IsMinimized(); | |
500 views::ImageButton* invisible_button = is_restored ? | |
501 restore_button_ : maximize_button_; | |
502 invisible_button->SetVisible(false); | |
503 | |
504 views::ImageButton* visible_button = is_restored ? | |
505 maximize_button_ : restore_button_; | |
506 FramePartBitmap normal_part, hot_part, pushed_part; | |
507 if (should_show_minmax_buttons_) { | |
508 visible_button->SetVisible(true); | |
509 visible_button->SetImageAlignment(ImageButton::ALIGN_LEFT, | |
510 ImageButton::ALIGN_BOTTOM); | |
511 gfx::Size visible_button_size = visible_button->GetPreferredSize(); | |
512 visible_button->SetBounds(close_button_->x() - visible_button_size.width(), | |
513 caption_y, visible_button_size.width(), | |
514 visible_button_size.height()); | |
515 | |
516 minimize_button_->SetVisible(true); | |
517 minimize_button_->SetImageAlignment(ImageButton::ALIGN_LEFT, | |
518 ImageButton::ALIGN_BOTTOM); | |
519 gfx::Size minimize_button_size = minimize_button_->GetPreferredSize(); | |
520 minimize_button_->SetBounds( | |
521 visible_button->x() - minimize_button_size.width(), caption_y, | |
522 minimize_button_size.width(), | |
523 minimize_button_size.height()); | |
524 | |
525 normal_part = IDR_CLOSE; | |
526 hot_part = IDR_CLOSE_H; | |
527 pushed_part = IDR_CLOSE_P; | |
528 } else { | |
529 visible_button->SetVisible(false); | |
530 minimize_button_->SetVisible(false); | |
531 | |
532 normal_part = IDR_CLOSE_SA; | |
533 hot_part = IDR_CLOSE_SA_H; | |
534 pushed_part = IDR_CLOSE_SA_P; | |
535 } | |
536 | |
537 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
538 | |
539 close_button_->SetImage(CustomButton::BS_NORMAL, | |
540 rb.GetBitmapNamed(normal_part)); | |
541 close_button_->SetImage(CustomButton::BS_HOT, | |
542 rb.GetBitmapNamed(hot_part)); | |
543 close_button_->SetImage(CustomButton::BS_PUSHED, | |
544 rb.GetBitmapNamed(pushed_part)); | |
545 } | |
546 | |
547 void CustomFrameView::LayoutTitleBar() { | |
548 // The window title is based on the calculated icon position, even when there | |
549 // is no icon. | |
550 gfx::Rect icon_bounds(IconBounds()); | |
551 if (frame_->widget_delegate()->ShouldShowWindowIcon()) | |
552 window_icon_->SetBoundsRect(icon_bounds); | |
553 | |
554 // Size the title. | |
555 int title_x = frame_->widget_delegate()->ShouldShowWindowIcon() ? | |
556 icon_bounds.right() + kIconTitleSpacing : icon_bounds.x(); | |
557 int title_height = title_font_->GetHeight(); | |
558 // We bias the title position so that when the difference between the icon and | |
559 // title heights is odd, the extra pixel of the title is above the vertical | |
560 // midline rather than below. This compensates for how the icon is already | |
561 // biased downwards (see IconBounds()) and helps prevent descenders on the | |
562 // title from overlapping the 3D edge at the bottom of the titlebar. | |
563 title_bounds_.SetRect(title_x, | |
564 icon_bounds.y() + ((icon_bounds.height() - title_height - 1) / 2), | |
565 std::max(0, (should_show_minmax_buttons_ ? | |
566 minimize_button_->x() : close_button_->x()) - kTitleCaptionSpacing - | |
567 title_x), title_height); | |
568 } | |
569 | |
570 void CustomFrameView::LayoutClientView() { | |
571 int top_height = NonClientTopBorderHeight(); | |
572 int border_thickness = NonClientBorderThickness(); | |
573 client_view_bounds_.SetRect(border_thickness, top_height, | |
574 std::max(0, width() - (2 * border_thickness)), | |
575 std::max(0, height() - top_height - border_thickness)); | |
576 } | |
577 | |
578 // static | |
579 void CustomFrameView::InitClass() { | |
580 static bool initialized = false; | |
581 if (!initialized) { | |
582 #if defined(USE_AURA) | |
583 title_font_ = new gfx::Font(NativeWidgetAura::GetWindowTitleFont()); | |
584 #elif defined(OS_WIN) | |
585 title_font_ = new gfx::Font(NativeWidgetWin::GetWindowTitleFont()); | |
586 #elif defined(OS_LINUX) | |
587 // TODO(ben): need to resolve what font this is. | |
588 title_font_ = new gfx::Font(); | |
589 #endif | |
590 initialized = true; | |
591 } | |
592 } | |
593 | |
594 } // namespace views | |
OLD | NEW |