Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 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/opaque_browser_frame_view.h" |
| 6 | |
| 7 | #include <algorithm> |
| 8 | #include <string> |
| 9 | |
| 10 | #include "base/command_line.h" |
| 11 | #include "base/compiler_specific.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 12 | #include "base/prefs/pref_service.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 13 | #include "base/strings/utf_string_conversions.h" |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 14 | #include "chrome/browser/chrome_notification_types.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 15 | #include "chrome/browser/themes/theme_properties.h" |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 16 | #include "chrome/browser/ui/views/avatar_label.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 17 | #include "chrome/browser/ui/views/avatar_menu_button.h" |
| 18 | #include "chrome/browser/ui/views/frame/browser_frame.h" |
| 19 | #include "chrome/browser/ui/views/frame/browser_view.h" |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 20 | #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h" |
Torne (Richard Coles) | d0247b1 | 2013-09-19 22:36:51 +0100 | [diff] [blame] | 21 | #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_platform_specific.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 22 | #include "chrome/browser/ui/views/tab_icon_view.h" |
| 23 | #include "chrome/browser/ui/views/tabs/tab_strip.h" |
| 24 | #include "chrome/browser/ui/views/toolbar_view.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 25 | #include "chrome/common/chrome_switches.h" |
| 26 | #include "chrome/common/pref_names.h" |
| 27 | #include "content/public/browser/notification_service.h" |
| 28 | #include "content/public/browser/web_contents.h" |
| 29 | #include "grit/chromium_strings.h" |
| 30 | #include "grit/generated_resources.h" |
| 31 | #include "grit/theme_resources.h" |
| 32 | #include "grit/ui_resources.h" |
| 33 | #include "ui/base/accessibility/accessible_view_state.h" |
| 34 | #include "ui/base/hit_test.h" |
| 35 | #include "ui/base/l10n/l10n_util.h" |
| 36 | #include "ui/base/resource/resource_bundle.h" |
| 37 | #include "ui/base/theme_provider.h" |
| 38 | #include "ui/gfx/canvas.h" |
| 39 | #include "ui/gfx/font.h" |
| 40 | #include "ui/gfx/image/image.h" |
| 41 | #include "ui/gfx/image/image_skia.h" |
| 42 | #include "ui/gfx/path.h" |
| 43 | #include "ui/views/controls/button/image_button.h" |
| 44 | #include "ui/views/controls/image_view.h" |
| 45 | #include "ui/views/controls/label.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 46 | #include "ui/views/layout/layout_constants.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 47 | #include "ui/views/widget/root_view.h" |
| 48 | #include "ui/views/window/frame_background.h" |
| 49 | #include "ui/views/window/window_shape.h" |
| 50 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 51 | using content::WebContents; |
| 52 | |
| 53 | namespace { |
| 54 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 55 | // While resize areas on Windows are normally the same size as the window |
| 56 | // borders, our top area is shrunk by 1 px to make it easier to move the window |
| 57 | // around with our thinner top grabbable strip. (Incidentally, our side and |
| 58 | // bottom resize areas don't match the frame border thickness either -- they |
| 59 | // span the whole nonclient area, so there's no "dead zone" for the mouse.) |
| 60 | const int kTopResizeAdjust = 1; |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 61 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 62 | // In the window corners, the resize areas don't actually expand bigger, but the |
| 63 | // 16 px at the end of each edge triggers diagonal resizing. |
| 64 | const int kResizeAreaCornerSize = 16; |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 65 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 66 | // The content left/right images have a shadow built into them. |
| 67 | const int kContentEdgeShadowThickness = 2; |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 68 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 69 | // The icon never shrinks below 16 px on a side. |
| 70 | const int kIconMinimumSize = 16; |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 71 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 72 | // The top 3 px of the tabstrip is shadow; in maximized mode we push this off |
| 73 | // the top of the screen so the tabs appear flush against the screen edge. |
| 74 | const int kTabstripTopShadowThickness = 3; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 75 | |
| 76 | // Converts |bounds| from |src|'s coordinate system to |dst|, and checks if |
| 77 | // |pt| is contained within. |
| 78 | bool ConvertedContainsCheck(gfx::Rect bounds, const views::View* src, |
| 79 | const views::View* dst, const gfx::Point& pt) { |
| 80 | DCHECK(src); |
| 81 | DCHECK(dst); |
| 82 | gfx::Point origin(bounds.origin()); |
| 83 | views::View::ConvertPointToTarget(src, dst, &origin); |
| 84 | bounds.set_origin(origin); |
| 85 | return bounds.Contains(pt); |
| 86 | } |
| 87 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 88 | } // namespace |
| 89 | |
| 90 | /////////////////////////////////////////////////////////////////////////////// |
| 91 | // OpaqueBrowserFrameView, public: |
| 92 | |
| 93 | OpaqueBrowserFrameView::OpaqueBrowserFrameView(BrowserFrame* frame, |
| 94 | BrowserView* browser_view) |
| 95 | : BrowserNonClientFrameView(frame, browser_view), |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 96 | layout_(new OpaqueBrowserFrameViewLayout(this)), |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 97 | minimize_button_(NULL), |
| 98 | maximize_button_(NULL), |
| 99 | restore_button_(NULL), |
| 100 | close_button_(NULL), |
| 101 | window_icon_(NULL), |
| 102 | window_title_(NULL), |
| 103 | frame_background_(new views::FrameBackground()) { |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 104 | SetLayoutManager(layout_); |
| 105 | |
| 106 | if (OpaqueBrowserFrameViewLayout::ShouldAddDefaultCaptionButtons()) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 107 | minimize_button_ = InitWindowCaptionButton(IDR_MINIMIZE, |
| 108 | IDR_MINIMIZE_H, |
| 109 | IDR_MINIMIZE_P, |
| 110 | IDR_MINIMIZE_BUTTON_MASK, |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 111 | IDS_ACCNAME_MINIMIZE, |
| 112 | VIEW_ID_MINIMIZE_BUTTON); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 113 | maximize_button_ = InitWindowCaptionButton(IDR_MAXIMIZE, |
| 114 | IDR_MAXIMIZE_H, |
| 115 | IDR_MAXIMIZE_P, |
| 116 | IDR_MAXIMIZE_BUTTON_MASK, |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 117 | IDS_ACCNAME_MAXIMIZE, |
| 118 | VIEW_ID_MAXIMIZE_BUTTON); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 119 | restore_button_ = InitWindowCaptionButton(IDR_RESTORE, |
| 120 | IDR_RESTORE_H, |
| 121 | IDR_RESTORE_P, |
| 122 | IDR_RESTORE_BUTTON_MASK, |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 123 | IDS_ACCNAME_RESTORE, |
| 124 | VIEW_ID_RESTORE_BUTTON); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 125 | close_button_ = InitWindowCaptionButton(IDR_CLOSE, |
| 126 | IDR_CLOSE_H, |
| 127 | IDR_CLOSE_P, |
| 128 | IDR_CLOSE_BUTTON_MASK, |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 129 | IDS_ACCNAME_CLOSE, |
| 130 | VIEW_ID_CLOSE_BUTTON); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 131 | } |
| 132 | |
| 133 | // Initializing the TabIconView is expensive, so only do it if we need to. |
| 134 | if (browser_view->ShouldShowWindowIcon()) { |
| 135 | window_icon_ = new TabIconView(this); |
| 136 | window_icon_->set_is_light(true); |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 137 | window_icon_->set_id(VIEW_ID_WINDOW_ICON); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 138 | AddChildView(window_icon_); |
| 139 | window_icon_->Update(); |
| 140 | } |
| 141 | |
| 142 | window_title_ = new views::Label(browser_view->GetWindowTitle(), |
| 143 | BrowserFrame::GetTitleFont()); |
| 144 | window_title_->SetVisible(browser_view->ShouldShowWindowTitle()); |
| 145 | window_title_->SetEnabledColor(SK_ColorWHITE); |
| 146 | // TODO(msw): Use a transparent background color as a workaround to use the |
| 147 | // gfx::Canvas::NO_SUBPIXEL_RENDERING flag and avoid some visual artifacts. |
| 148 | window_title_->SetBackgroundColor(0x00000000); |
| 149 | window_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 150 | window_title_->set_id(VIEW_ID_WINDOW_TITLE); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 151 | AddChildView(window_title_); |
| 152 | |
| 153 | UpdateAvatarInfo(); |
| 154 | if (!browser_view->IsOffTheRecord()) { |
| 155 | registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, |
| 156 | content::NotificationService::AllSources()); |
| 157 | } |
Torne (Richard Coles) | d0247b1 | 2013-09-19 22:36:51 +0100 | [diff] [blame] | 158 | |
| 159 | platform_observer_.reset( |
| 160 | OpaqueBrowserFrameViewPlatformSpecific::Create(this, layout_)); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 161 | } |
| 162 | |
| 163 | OpaqueBrowserFrameView::~OpaqueBrowserFrameView() { |
| 164 | } |
| 165 | |
| 166 | /////////////////////////////////////////////////////////////////////////////// |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 167 | // OpaqueBrowserFrameView, BrowserNonClientFrameView implementation: |
| 168 | |
| 169 | gfx::Rect OpaqueBrowserFrameView::GetBoundsForTabStrip( |
| 170 | views::View* tabstrip) const { |
| 171 | if (!tabstrip) |
| 172 | return gfx::Rect(); |
| 173 | |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 174 | return layout_->GetBoundsForTabStrip(tabstrip->GetPreferredSize(), width()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 175 | } |
| 176 | |
| 177 | BrowserNonClientFrameView::TabStripInsets |
| 178 | OpaqueBrowserFrameView::GetTabStripInsets(bool restored) const { |
Torne (Richard Coles) | 4e180b6 | 2013-10-18 15:46:22 +0100 | [diff] [blame^] | 179 | if (!browser_view()->IsTabStripVisible()) |
| 180 | return TabStripInsets(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 181 | // TODO: include OTR and caption. |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 182 | return TabStripInsets(layout_->GetTabStripInsetsTop(restored), 0, 0); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 183 | } |
| 184 | |
| 185 | int OpaqueBrowserFrameView::GetThemeBackgroundXInset() const { |
| 186 | return 0; |
| 187 | } |
| 188 | |
| 189 | void OpaqueBrowserFrameView::UpdateThrobber(bool running) { |
| 190 | if (window_icon_) |
| 191 | window_icon_->Update(); |
| 192 | } |
| 193 | |
| 194 | gfx::Size OpaqueBrowserFrameView::GetMinimumSize() { |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 195 | return layout_->GetMinimumSize(width()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 196 | } |
| 197 | |
| 198 | /////////////////////////////////////////////////////////////////////////////// |
| 199 | // OpaqueBrowserFrameView, views::NonClientFrameView implementation: |
| 200 | |
| 201 | gfx::Rect OpaqueBrowserFrameView::GetBoundsForClientView() const { |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 202 | return layout_->client_view_bounds(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 203 | } |
| 204 | |
| 205 | gfx::Rect OpaqueBrowserFrameView::GetWindowBoundsForClientBounds( |
| 206 | const gfx::Rect& client_bounds) const { |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 207 | return layout_->GetWindowBoundsForClientBounds(client_bounds); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 208 | } |
| 209 | |
| 210 | int OpaqueBrowserFrameView::NonClientHitTest(const gfx::Point& point) { |
| 211 | if (!bounds().Contains(point)) |
| 212 | return HTNOWHERE; |
| 213 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 214 | // See if the point is within the avatar menu button or within the avatar |
| 215 | // label. |
| 216 | if ((avatar_button() && |
| 217 | avatar_button()->GetMirroredBounds().Contains(point)) || |
| 218 | (avatar_label() && avatar_label()->GetMirroredBounds().Contains(point))) |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 219 | return HTCLIENT; |
| 220 | |
| 221 | int frame_component = frame()->client_view()->NonClientHitTest(point); |
| 222 | |
| 223 | // See if we're in the sysmenu region. We still have to check the tabstrip |
| 224 | // first so that clicks in a tab don't get treated as sysmenu clicks. |
| 225 | gfx::Rect sysmenu_rect(IconBounds()); |
| 226 | // In maximized mode we extend the rect to the screen corner to take advantage |
| 227 | // of Fitts' Law. |
| 228 | if (frame()->IsMaximized()) |
| 229 | sysmenu_rect.SetRect(0, 0, sysmenu_rect.right(), sysmenu_rect.bottom()); |
| 230 | sysmenu_rect.set_x(GetMirroredXForRect(sysmenu_rect)); |
| 231 | if (sysmenu_rect.Contains(point)) |
| 232 | return (frame_component == HTCLIENT) ? HTCLIENT : HTSYSMENU; |
| 233 | |
| 234 | if (frame_component != HTNOWHERE) |
| 235 | return frame_component; |
| 236 | |
| 237 | // Then see if the point is within any of the window controls. |
| 238 | if (close_button_ && close_button_->visible() && |
| 239 | close_button_->GetMirroredBounds().Contains(point)) |
| 240 | return HTCLOSE; |
| 241 | if (restore_button_ && restore_button_->visible() && |
| 242 | restore_button_->GetMirroredBounds().Contains(point)) |
| 243 | return HTMAXBUTTON; |
| 244 | if (maximize_button_ && maximize_button_->visible() && |
| 245 | maximize_button_->GetMirroredBounds().Contains(point)) |
| 246 | return HTMAXBUTTON; |
| 247 | if (minimize_button_ && minimize_button_->visible() && |
| 248 | minimize_button_->GetMirroredBounds().Contains(point)) |
| 249 | return HTMINBUTTON; |
| 250 | |
| 251 | views::WidgetDelegate* delegate = frame()->widget_delegate(); |
| 252 | if (!delegate) { |
| 253 | LOG(WARNING) << "delegate is NULL, returning safe default."; |
| 254 | return HTCAPTION; |
| 255 | } |
| 256 | int window_component = GetHTComponentForFrame(point, TopResizeHeight(), |
| 257 | NonClientBorderThickness(), kResizeAreaCornerSize, kResizeAreaCornerSize, |
| 258 | delegate->CanResize()); |
| 259 | // Fall back to the caption if no other component matches. |
| 260 | return (window_component == HTNOWHERE) ? HTCAPTION : window_component; |
| 261 | } |
| 262 | |
| 263 | void OpaqueBrowserFrameView::GetWindowMask(const gfx::Size& size, |
| 264 | gfx::Path* window_mask) { |
| 265 | DCHECK(window_mask); |
| 266 | |
| 267 | if (frame()->IsMaximized() || frame()->IsFullscreen()) |
| 268 | return; |
| 269 | |
| 270 | views::GetDefaultWindowMask(size, window_mask); |
| 271 | } |
| 272 | |
| 273 | void OpaqueBrowserFrameView::ResetWindowControls() { |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 274 | if (!OpaqueBrowserFrameViewLayout::ShouldAddDefaultCaptionButtons()) |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 275 | return; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 276 | restore_button_->SetState(views::CustomButton::STATE_NORMAL); |
| 277 | minimize_button_->SetState(views::CustomButton::STATE_NORMAL); |
| 278 | maximize_button_->SetState(views::CustomButton::STATE_NORMAL); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 279 | // The close button isn't affected by this constraint. |
| 280 | } |
| 281 | |
| 282 | void OpaqueBrowserFrameView::UpdateWindowIcon() { |
| 283 | window_icon_->SchedulePaint(); |
| 284 | } |
| 285 | |
| 286 | void OpaqueBrowserFrameView::UpdateWindowTitle() { |
| 287 | if (!frame()->IsFullscreen()) |
| 288 | window_title_->SchedulePaint(); |
| 289 | } |
| 290 | |
| 291 | /////////////////////////////////////////////////////////////////////////////// |
| 292 | // OpaqueBrowserFrameView, views::View overrides: |
| 293 | |
| 294 | void OpaqueBrowserFrameView::OnPaint(gfx::Canvas* canvas) { |
| 295 | if (frame()->IsFullscreen()) |
| 296 | return; // Nothing is visible, so don't bother to paint. |
| 297 | |
| 298 | if (frame()->IsMaximized()) |
| 299 | PaintMaximizedFrameBorder(canvas); |
| 300 | else |
| 301 | PaintRestoredFrameBorder(canvas); |
| 302 | |
| 303 | // The window icon and title are painted by their respective views. |
| 304 | /* TODO(pkasting): If this window is active, we should also draw a drop |
| 305 | * shadow on the title. This is tricky, because we don't want to hardcode a |
| 306 | * shadow color (since we want to work with various themes), but we can't |
| 307 | * alpha-blend either (since the Windows text APIs don't really do this). |
| 308 | * So we'd need to sample the background color at the right location and |
| 309 | * synthesize a good shadow color. */ |
| 310 | |
| 311 | if (browser_view()->IsToolbarVisible()) |
| 312 | PaintToolbarBackground(canvas); |
| 313 | if (!frame()->IsMaximized()) |
| 314 | PaintRestoredClientEdge(canvas); |
| 315 | } |
| 316 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 317 | bool OpaqueBrowserFrameView::HitTestRect(const gfx::Rect& rect) const { |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 318 | if (!views::View::HitTestRect(rect)) { |
| 319 | // |rect| is outside OpaqueBrowserFrameView's bounds. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 320 | return false; |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 321 | } |
| 322 | |
| 323 | // If the rect is outside the bounds of the client area, claim it. |
| 324 | // TODO(tdanderson): Implement View::ConvertRectToTarget(). |
| 325 | gfx::Point rect_in_client_view_coords_origin(rect.origin()); |
| 326 | View::ConvertPointToTarget(this, frame()->client_view(), |
| 327 | &rect_in_client_view_coords_origin); |
| 328 | gfx::Rect rect_in_client_view_coords( |
| 329 | rect_in_client_view_coords_origin, rect.size()); |
| 330 | if (!frame()->client_view()->HitTestRect(rect_in_client_view_coords)) |
| 331 | return true; |
| 332 | |
| 333 | // Otherwise, claim |rect| only if it is above the bottom of the tabstrip in |
| 334 | // a non-tab portion. |
| 335 | TabStrip* tabstrip = browser_view()->tabstrip(); |
| 336 | if (!tabstrip || !browser_view()->IsTabStripVisible()) |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 337 | return false; |
| 338 | |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 339 | gfx::Point rect_in_tabstrip_coords_origin(rect.origin()); |
| 340 | View::ConvertPointToTarget(this, tabstrip, |
| 341 | &rect_in_tabstrip_coords_origin); |
| 342 | gfx::Rect rect_in_tabstrip_coords( |
| 343 | rect_in_tabstrip_coords_origin, rect.size()); |
| 344 | |
| 345 | if (rect_in_tabstrip_coords.bottom() > tabstrip->GetLocalBounds().bottom()) { |
| 346 | // |rect| is below the tabstrip. |
| 347 | return false; |
| 348 | } |
| 349 | |
| 350 | if (tabstrip->HitTestRect(rect_in_tabstrip_coords)) { |
| 351 | // Claim |rect| if it is in a non-tab portion of the tabstrip. |
| 352 | // TODO(tdanderson): Pass |rect_in_tabstrip_coords| instead of its center |
| 353 | // point to TabStrip::IsPositionInWindowCaption() once |
| 354 | // GetEventHandlerForRect() is implemented. |
| 355 | return tabstrip->IsPositionInWindowCaption( |
| 356 | rect_in_tabstrip_coords.CenterPoint()); |
| 357 | } |
| 358 | |
| 359 | // The window switcher button is to the right of the tabstrip but is |
| 360 | // part of the client view. |
| 361 | views::View* window_switcher_button = |
| 362 | browser_view()->window_switcher_button(); |
| 363 | if (window_switcher_button && window_switcher_button->visible()) { |
| 364 | gfx::Point rect_in_window_switcher_coords_origin(rect.origin()); |
| 365 | View::ConvertPointToTarget(this, window_switcher_button, |
| 366 | &rect_in_window_switcher_coords_origin); |
| 367 | gfx::Rect rect_in_window_switcher_coords( |
| 368 | rect_in_window_switcher_coords_origin, rect.size()); |
| 369 | |
| 370 | if (window_switcher_button->HitTestRect(rect_in_window_switcher_coords)) |
| 371 | return false; |
| 372 | } |
| 373 | |
| 374 | // We claim |rect| because it is above the bottom of the tabstrip, but |
| 375 | // neither in the tabstrip nor in the window switcher button. In particular, |
| 376 | // the avatar label/button is left of the tabstrip and the window controls |
| 377 | // are right of the tabstrip. |
| 378 | return true; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 379 | } |
| 380 | |
| 381 | void OpaqueBrowserFrameView::GetAccessibleState( |
| 382 | ui::AccessibleViewState* state) { |
| 383 | state->role = ui::AccessibilityTypes::ROLE_TITLEBAR; |
| 384 | } |
| 385 | |
| 386 | /////////////////////////////////////////////////////////////////////////////// |
| 387 | // OpaqueBrowserFrameView, views::ButtonListener implementation: |
| 388 | |
| 389 | void OpaqueBrowserFrameView::ButtonPressed(views::Button* sender, |
| 390 | const ui::Event& event) { |
| 391 | if (sender == minimize_button_) |
| 392 | frame()->Minimize(); |
| 393 | else if (sender == maximize_button_) |
| 394 | frame()->Maximize(); |
| 395 | else if (sender == restore_button_) |
| 396 | frame()->Restore(); |
| 397 | else if (sender == close_button_) |
| 398 | frame()->Close(); |
| 399 | } |
| 400 | |
| 401 | /////////////////////////////////////////////////////////////////////////////// |
| 402 | // OpaqueBrowserFrameView, TabIconView::TabContentsProvider implementation: |
| 403 | |
| 404 | bool OpaqueBrowserFrameView::ShouldTabIconViewAnimate() const { |
| 405 | // This function is queried during the creation of the window as the |
| 406 | // TabIconView we host is initialized, so we need to NULL check the selected |
| 407 | // WebContents because in this condition there is not yet a selected tab. |
| 408 | WebContents* current_tab = browser_view()->GetActiveWebContents(); |
| 409 | return current_tab ? current_tab->IsLoading() : false; |
| 410 | } |
| 411 | |
| 412 | gfx::ImageSkia OpaqueBrowserFrameView::GetFaviconForTabIconView() { |
| 413 | views::WidgetDelegate* delegate = frame()->widget_delegate(); |
| 414 | if (!delegate) { |
| 415 | LOG(WARNING) << "delegate is NULL, returning safe default."; |
| 416 | return gfx::ImageSkia(); |
| 417 | } |
| 418 | return delegate->GetWindowIcon(); |
| 419 | } |
| 420 | |
| 421 | /////////////////////////////////////////////////////////////////////////////// |
| 422 | // OpaqueBrowserFrameView, protected: |
| 423 | |
| 424 | void OpaqueBrowserFrameView::Observe( |
| 425 | int type, |
| 426 | const content::NotificationSource& source, |
| 427 | const content::NotificationDetails& details) { |
| 428 | switch (type) { |
| 429 | case chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED: |
| 430 | UpdateAvatarInfo(); |
| 431 | break; |
| 432 | default: |
| 433 | NOTREACHED() << "Got a notification we didn't register for!"; |
| 434 | break; |
| 435 | } |
| 436 | } |
| 437 | |
| 438 | /////////////////////////////////////////////////////////////////////////////// |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 439 | // OpaqueBrowserFrameView, OpaqueBrowserFrameViewLayoutDelegate implementation: |
| 440 | |
| 441 | bool OpaqueBrowserFrameView::ShouldShowWindowIcon() const { |
| 442 | views::WidgetDelegate* delegate = frame()->widget_delegate(); |
| 443 | return delegate && delegate->ShouldShowWindowIcon(); |
| 444 | } |
| 445 | |
| 446 | bool OpaqueBrowserFrameView::ShouldShowWindowTitle() const { |
| 447 | // |delegate| may be NULL if called from callback of InputMethodChanged while |
| 448 | // a window is being destroyed. |
| 449 | // See more discussion at http://crosbug.com/8958 |
| 450 | views::WidgetDelegate* delegate = frame()->widget_delegate(); |
| 451 | return delegate && delegate->ShouldShowWindowTitle(); |
| 452 | } |
| 453 | |
| 454 | string16 OpaqueBrowserFrameView::GetWindowTitle() const { |
| 455 | return frame()->widget_delegate()->GetWindowTitle(); |
| 456 | } |
| 457 | |
| 458 | int OpaqueBrowserFrameView::GetIconSize() const { |
| 459 | #if defined(OS_WIN) |
| 460 | // This metric scales up if either the titlebar height or the titlebar font |
| 461 | // size are increased. |
| 462 | return GetSystemMetrics(SM_CYSMICON); |
| 463 | #else |
| 464 | return std::max(BrowserFrame::GetTitleFont().GetHeight(), kIconMinimumSize); |
| 465 | #endif |
| 466 | } |
| 467 | |
| 468 | bool OpaqueBrowserFrameView::ShouldLeaveOffsetNearTopBorder() const { |
| 469 | return frame()->ShouldLeaveOffsetNearTopBorder(); |
| 470 | } |
| 471 | |
| 472 | gfx::Size OpaqueBrowserFrameView::GetBrowserViewMinimumSize() const { |
| 473 | return browser_view()->GetMinimumSize(); |
| 474 | } |
| 475 | |
| 476 | bool OpaqueBrowserFrameView::ShouldShowAvatar() const { |
| 477 | return browser_view()->ShouldShowAvatar(); |
| 478 | } |
| 479 | |
| 480 | gfx::ImageSkia OpaqueBrowserFrameView::GetOTRAvatarIcon() const { |
| 481 | return browser_view()->GetOTRAvatarIcon(); |
| 482 | } |
| 483 | |
| 484 | bool OpaqueBrowserFrameView::IsMaximized() const { |
| 485 | return frame()->IsMaximized(); |
| 486 | } |
| 487 | |
| 488 | bool OpaqueBrowserFrameView::IsMinimized() const { |
| 489 | return frame()->IsMinimized(); |
| 490 | } |
| 491 | |
| 492 | bool OpaqueBrowserFrameView::IsFullscreen() const { |
| 493 | return frame()->IsFullscreen(); |
| 494 | } |
| 495 | |
| 496 | bool OpaqueBrowserFrameView::IsTabStripVisible() const { |
| 497 | return browser_view()->IsTabStripVisible(); |
| 498 | } |
| 499 | |
| 500 | int OpaqueBrowserFrameView::GetTabStripHeight() const { |
| 501 | return browser_view()->GetTabStripHeight(); |
| 502 | } |
| 503 | |
| 504 | int OpaqueBrowserFrameView::GetAdditionalReservedSpaceInTabStrip() const { |
| 505 | // We don't have the sysmenu buttons in Windows 8 metro mode. However there |
| 506 | // are buttons like the window switcher which are drawn in the non client |
| 507 | // are in the BrowserView. We need to ensure that the tab strip does not |
| 508 | // draw on the window switcher button. |
| 509 | views::View* button = browser_view()->window_switcher_button(); |
| 510 | return button ? button->width() : 0; |
| 511 | } |
| 512 | |
| 513 | gfx::Size OpaqueBrowserFrameView::GetTabstripPreferredSize() const { |
| 514 | gfx::Size s = browser_view()->tabstrip()->GetPreferredSize(); |
| 515 | return s; |
| 516 | } |
| 517 | |
| 518 | /////////////////////////////////////////////////////////////////////////////// |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 519 | // OpaqueBrowserFrameView, private: |
| 520 | |
| 521 | views::ImageButton* OpaqueBrowserFrameView::InitWindowCaptionButton( |
| 522 | int normal_image_id, |
| 523 | int hot_image_id, |
| 524 | int pushed_image_id, |
| 525 | int mask_image_id, |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 526 | int accessibility_string_id, |
| 527 | ViewID view_id) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 528 | views::ImageButton* button = new views::ImageButton(this); |
| 529 | ui::ThemeProvider* tp = frame()->GetThemeProvider(); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 530 | button->SetImage(views::CustomButton::STATE_NORMAL, |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 531 | tp->GetImageSkiaNamed(normal_image_id)); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 532 | button->SetImage(views::CustomButton::STATE_HOVERED, |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 533 | tp->GetImageSkiaNamed(hot_image_id)); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 534 | button->SetImage(views::CustomButton::STATE_PRESSED, |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 535 | tp->GetImageSkiaNamed(pushed_image_id)); |
| 536 | if (browser_view()->IsBrowserTypeNormal()) { |
| 537 | button->SetBackground( |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 538 | tp->GetColor(ThemeProperties::COLOR_BUTTON_BACKGROUND), |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 539 | tp->GetImageSkiaNamed(IDR_THEME_WINDOW_CONTROL_BACKGROUND), |
| 540 | tp->GetImageSkiaNamed(mask_image_id)); |
| 541 | } |
| 542 | button->SetAccessibleName( |
| 543 | l10n_util::GetStringUTF16(accessibility_string_id)); |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 544 | button->set_id(view_id); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 545 | AddChildView(button); |
| 546 | return button; |
| 547 | } |
| 548 | |
| 549 | int OpaqueBrowserFrameView::FrameBorderThickness(bool restored) const { |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 550 | return layout_->FrameBorderThickness(restored); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 551 | } |
| 552 | |
| 553 | int OpaqueBrowserFrameView::TopResizeHeight() const { |
| 554 | return FrameBorderThickness(false) - kTopResizeAdjust; |
| 555 | } |
| 556 | |
| 557 | int OpaqueBrowserFrameView::NonClientBorderThickness() const { |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 558 | return layout_->NonClientBorderThickness(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 559 | } |
| 560 | |
| 561 | gfx::Rect OpaqueBrowserFrameView::IconBounds() const { |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 562 | return layout_->IconBounds(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 563 | } |
| 564 | |
| 565 | void OpaqueBrowserFrameView::PaintRestoredFrameBorder(gfx::Canvas* canvas) { |
| 566 | frame_background_->set_frame_color(GetFrameColor()); |
| 567 | frame_background_->set_theme_image(GetFrameImage()); |
| 568 | frame_background_->set_theme_overlay_image(GetFrameOverlayImage()); |
| 569 | frame_background_->set_top_area_height(GetTopAreaHeight()); |
| 570 | |
| 571 | ui::ThemeProvider* tp = GetThemeProvider(); |
| 572 | frame_background_->SetSideImages( |
| 573 | tp->GetImageSkiaNamed(IDR_WINDOW_LEFT_SIDE), |
| 574 | tp->GetImageSkiaNamed(IDR_WINDOW_TOP_CENTER), |
| 575 | tp->GetImageSkiaNamed(IDR_WINDOW_RIGHT_SIDE), |
| 576 | tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_CENTER)); |
| 577 | frame_background_->SetCornerImages( |
| 578 | tp->GetImageSkiaNamed(IDR_WINDOW_TOP_LEFT_CORNER), |
| 579 | tp->GetImageSkiaNamed(IDR_WINDOW_TOP_RIGHT_CORNER), |
| 580 | tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_LEFT_CORNER), |
| 581 | tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_RIGHT_CORNER)); |
| 582 | frame_background_->PaintRestored(canvas, this); |
| 583 | |
| 584 | // Note: When we don't have a toolbar, we need to draw some kind of bottom |
| 585 | // edge here. Because the App Window graphics we use for this have an |
| 586 | // attached client edge and their sizing algorithm is a little involved, we do |
| 587 | // all this in PaintRestoredClientEdge(). |
| 588 | } |
| 589 | |
| 590 | void OpaqueBrowserFrameView::PaintMaximizedFrameBorder(gfx::Canvas* canvas) { |
| 591 | frame_background_->set_frame_color(GetFrameColor()); |
| 592 | frame_background_->set_theme_image(GetFrameImage()); |
| 593 | frame_background_->set_theme_overlay_image(GetFrameOverlayImage()); |
| 594 | frame_background_->set_top_area_height(GetTopAreaHeight()); |
| 595 | |
| 596 | // Theme frame must be aligned with the tabstrip as if we were |
| 597 | // in restored mode. Note that the top of the tabstrip is |
| 598 | // kTabstripTopShadowThickness px off the top of the screen. |
| 599 | int theme_background_y = -(GetTabStripInsets(true).top + |
| 600 | kTabstripTopShadowThickness); |
| 601 | frame_background_->set_theme_background_y(theme_background_y); |
| 602 | |
| 603 | frame_background_->PaintMaximized(canvas, this); |
| 604 | |
| 605 | // TODO(jamescook): Migrate this into FrameBackground. |
| 606 | if (!browser_view()->IsToolbarVisible()) { |
| 607 | // There's no toolbar to edge the frame border, so we need to draw a bottom |
| 608 | // edge. The graphic we use for this has a built in client edge, so we clip |
| 609 | // it off the bottom. |
| 610 | gfx::ImageSkia* top_center = |
| 611 | GetThemeProvider()->GetImageSkiaNamed(IDR_APP_TOP_CENTER); |
| 612 | int edge_height = top_center->height() - kClientEdgeThickness; |
| 613 | canvas->TileImageInt(*top_center, 0, |
| 614 | frame()->client_view()->y() - edge_height, width(), edge_height); |
| 615 | } |
| 616 | } |
| 617 | |
| 618 | void OpaqueBrowserFrameView::PaintToolbarBackground(gfx::Canvas* canvas) { |
| 619 | gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds()); |
| 620 | if (toolbar_bounds.IsEmpty()) |
| 621 | return; |
| 622 | gfx::Point toolbar_origin(toolbar_bounds.origin()); |
| 623 | ConvertPointToTarget(browser_view(), this, &toolbar_origin); |
| 624 | toolbar_bounds.set_origin(toolbar_origin); |
| 625 | |
| 626 | int x = toolbar_bounds.x(); |
| 627 | int w = toolbar_bounds.width(); |
| 628 | int y = toolbar_bounds.y(); |
| 629 | int h = toolbar_bounds.height(); |
| 630 | |
| 631 | // Gross hack: We split the toolbar images into two pieces, since sometimes |
| 632 | // (popup mode) the toolbar isn't tall enough to show the whole image. The |
| 633 | // split happens between the top shadow section and the bottom gradient |
| 634 | // section so that we never break the gradient. |
| 635 | int split_point = kFrameShadowThickness * 2; |
| 636 | int bottom_y = y + split_point; |
| 637 | ui::ThemeProvider* tp = GetThemeProvider(); |
| 638 | gfx::ImageSkia* toolbar_left = tp->GetImageSkiaNamed( |
| 639 | IDR_CONTENT_TOP_LEFT_CORNER); |
| 640 | int bottom_edge_height = std::min(toolbar_left->height(), h) - split_point; |
| 641 | |
| 642 | // Split our canvas out so we can mask out the corners of the toolbar |
| 643 | // without masking out the frame. |
| 644 | canvas->SaveLayerAlpha( |
| 645 | 255, gfx::Rect(x - kClientEdgeThickness, y, w + kClientEdgeThickness * 3, |
| 646 | h)); |
| 647 | |
| 648 | // Paint the bottom rect. |
| 649 | canvas->FillRect(gfx::Rect(x, bottom_y, w, bottom_edge_height), |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 650 | tp->GetColor(ThemeProperties::COLOR_TOOLBAR)); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 651 | |
| 652 | // Tile the toolbar image starting at the frame edge on the left and where the |
| 653 | // horizontal tabstrip is (or would be) on the top. |
| 654 | gfx::ImageSkia* theme_toolbar = tp->GetImageSkiaNamed(IDR_THEME_TOOLBAR); |
| 655 | canvas->TileImageInt(*theme_toolbar, |
| 656 | x + GetThemeBackgroundXInset(), |
| 657 | bottom_y - GetTabStripInsets(false).top, |
| 658 | x, bottom_y, w, theme_toolbar->height()); |
| 659 | |
| 660 | // Draw rounded corners for the tab. |
| 661 | gfx::ImageSkia* toolbar_left_mask = |
| 662 | tp->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER_MASK); |
| 663 | gfx::ImageSkia* toolbar_right_mask = |
| 664 | tp->GetImageSkiaNamed(IDR_CONTENT_TOP_RIGHT_CORNER_MASK); |
| 665 | |
| 666 | // We mask out the corners by using the DestinationIn transfer mode, |
| 667 | // which keeps the RGB pixels from the destination and the alpha from |
| 668 | // the source. |
| 669 | SkPaint paint; |
| 670 | paint.setXfermodeMode(SkXfermode::kDstIn_Mode); |
| 671 | |
| 672 | // Mask the left edge. |
| 673 | int left_x = x - kContentEdgeShadowThickness; |
| 674 | canvas->DrawImageInt(*toolbar_left_mask, 0, 0, toolbar_left_mask->width(), |
| 675 | split_point, left_x, y, toolbar_left_mask->width(), |
| 676 | split_point, false, paint); |
| 677 | canvas->DrawImageInt(*toolbar_left_mask, 0, |
| 678 | toolbar_left_mask->height() - bottom_edge_height, |
| 679 | toolbar_left_mask->width(), bottom_edge_height, left_x, bottom_y, |
| 680 | toolbar_left_mask->width(), bottom_edge_height, false, paint); |
| 681 | |
| 682 | // Mask the right edge. |
| 683 | int right_x = |
| 684 | x + w - toolbar_right_mask->width() + kContentEdgeShadowThickness; |
| 685 | canvas->DrawImageInt(*toolbar_right_mask, 0, 0, toolbar_right_mask->width(), |
| 686 | split_point, right_x, y, toolbar_right_mask->width(), |
| 687 | split_point, false, paint); |
| 688 | canvas->DrawImageInt(*toolbar_right_mask, 0, |
| 689 | toolbar_right_mask->height() - bottom_edge_height, |
| 690 | toolbar_right_mask->width(), bottom_edge_height, right_x, bottom_y, |
| 691 | toolbar_right_mask->width(), bottom_edge_height, false, paint); |
| 692 | canvas->Restore(); |
| 693 | |
| 694 | canvas->DrawImageInt(*toolbar_left, 0, 0, toolbar_left->width(), split_point, |
| 695 | left_x, y, toolbar_left->width(), split_point, false); |
| 696 | canvas->DrawImageInt(*toolbar_left, 0, |
| 697 | toolbar_left->height() - bottom_edge_height, toolbar_left->width(), |
| 698 | bottom_edge_height, left_x, bottom_y, toolbar_left->width(), |
| 699 | bottom_edge_height, false); |
| 700 | |
| 701 | gfx::ImageSkia* toolbar_center = |
| 702 | tp->GetImageSkiaNamed(IDR_CONTENT_TOP_CENTER); |
| 703 | canvas->TileImageInt(*toolbar_center, 0, 0, left_x + toolbar_left->width(), |
| 704 | y, right_x - (left_x + toolbar_left->width()), |
| 705 | split_point); |
| 706 | |
| 707 | gfx::ImageSkia* toolbar_right = tp->GetImageSkiaNamed( |
| 708 | IDR_CONTENT_TOP_RIGHT_CORNER); |
| 709 | canvas->DrawImageInt(*toolbar_right, 0, 0, toolbar_right->width(), |
| 710 | split_point, right_x, y, toolbar_right->width(), split_point, false); |
| 711 | canvas->DrawImageInt(*toolbar_right, 0, |
| 712 | toolbar_right->height() - bottom_edge_height, toolbar_right->width(), |
| 713 | bottom_edge_height, right_x, bottom_y, toolbar_right->width(), |
| 714 | bottom_edge_height, false); |
| 715 | |
| 716 | // Draw the content/toolbar separator. |
| 717 | canvas->FillRect( |
| 718 | gfx::Rect(x + kClientEdgeThickness, |
| 719 | toolbar_bounds.bottom() - kClientEdgeThickness, |
| 720 | w - (2 * kClientEdgeThickness), |
| 721 | kClientEdgeThickness), |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 722 | ThemeProperties::GetDefaultColor( |
| 723 | ThemeProperties::COLOR_TOOLBAR_SEPARATOR)); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 724 | } |
| 725 | |
| 726 | void OpaqueBrowserFrameView::PaintRestoredClientEdge(gfx::Canvas* canvas) { |
| 727 | ui::ThemeProvider* tp = GetThemeProvider(); |
| 728 | int client_area_top = frame()->client_view()->y(); |
| 729 | int image_top = client_area_top; |
| 730 | |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 731 | gfx::Rect client_area_bounds = |
| 732 | layout_->CalculateClientAreaBounds(width(), height()); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 733 | SkColor toolbar_color = tp->GetColor(ThemeProperties::COLOR_TOOLBAR); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 734 | |
| 735 | if (browser_view()->IsToolbarVisible()) { |
| 736 | // The client edge images always start below the toolbar corner images. The |
| 737 | // client edge filled rects start there or at the bottom of the toolbar, |
| 738 | // whichever is shorter. |
| 739 | gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds()); |
| 740 | image_top += toolbar_bounds.y() + |
| 741 | tp->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER)->height(); |
| 742 | client_area_top = std::min(image_top, |
| 743 | client_area_top + toolbar_bounds.bottom() - kClientEdgeThickness); |
| 744 | } else if (!browser_view()->IsTabStripVisible()) { |
| 745 | // The toolbar isn't going to draw a client edge for us, so draw one |
| 746 | // ourselves. |
| 747 | gfx::ImageSkia* top_left = tp->GetImageSkiaNamed(IDR_APP_TOP_LEFT); |
| 748 | gfx::ImageSkia* top_center = tp->GetImageSkiaNamed(IDR_APP_TOP_CENTER); |
| 749 | gfx::ImageSkia* top_right = tp->GetImageSkiaNamed(IDR_APP_TOP_RIGHT); |
| 750 | int top_edge_y = client_area_top - top_center->height(); |
| 751 | int height = client_area_top - top_edge_y; |
| 752 | |
| 753 | canvas->DrawImageInt(*top_left, 0, 0, top_left->width(), height, |
| 754 | client_area_bounds.x() - top_left->width(), top_edge_y, |
| 755 | top_left->width(), height, false); |
| 756 | canvas->TileImageInt(*top_center, 0, 0, client_area_bounds.x(), top_edge_y, |
| 757 | client_area_bounds.width(), std::min(height, top_center->height())); |
| 758 | canvas->DrawImageInt(*top_right, 0, 0, top_right->width(), height, |
| 759 | client_area_bounds.right(), top_edge_y, |
| 760 | top_right->width(), height, false); |
| 761 | |
| 762 | // Draw the toolbar color across the top edge. |
| 763 | canvas->FillRect(gfx::Rect(client_area_bounds.x() - kClientEdgeThickness, |
| 764 | client_area_top - kClientEdgeThickness, |
| 765 | client_area_bounds.width() + (2 * kClientEdgeThickness), |
| 766 | kClientEdgeThickness), toolbar_color); |
| 767 | } |
| 768 | |
| 769 | int client_area_bottom = |
| 770 | std::max(client_area_top, height() - NonClientBorderThickness()); |
| 771 | int image_height = client_area_bottom - image_top; |
| 772 | |
| 773 | // Draw the client edge images. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 774 | gfx::ImageSkia* right = tp->GetImageSkiaNamed(IDR_CONTENT_RIGHT_SIDE); |
| 775 | canvas->TileImageInt(*right, client_area_bounds.right(), image_top, |
| 776 | right->width(), image_height); |
| 777 | canvas->DrawImageInt( |
| 778 | *tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_RIGHT_CORNER), |
| 779 | client_area_bounds.right(), client_area_bottom); |
| 780 | gfx::ImageSkia* bottom = tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_CENTER); |
| 781 | canvas->TileImageInt(*bottom, client_area_bounds.x(), |
| 782 | client_area_bottom, client_area_bounds.width(), |
| 783 | bottom->height()); |
| 784 | gfx::ImageSkia* bottom_left = |
| 785 | tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_LEFT_CORNER); |
| 786 | canvas->DrawImageInt(*bottom_left, |
| 787 | client_area_bounds.x() - bottom_left->width(), client_area_bottom); |
| 788 | gfx::ImageSkia* left = tp->GetImageSkiaNamed(IDR_CONTENT_LEFT_SIDE); |
| 789 | canvas->TileImageInt(*left, client_area_bounds.x() - left->width(), |
| 790 | image_top, left->width(), image_height); |
| 791 | |
| 792 | // Draw the toolbar color so that the client edges show the right color even |
| 793 | // where not covered by the toolbar image. NOTE: We do this after drawing the |
| 794 | // images because the images are meant to alpha-blend atop the frame whereas |
| 795 | // these rects are meant to be fully opaque, without anything overlaid. |
| 796 | canvas->FillRect(gfx::Rect(client_area_bounds.x() - kClientEdgeThickness, |
| 797 | client_area_top, kClientEdgeThickness, |
| 798 | client_area_bottom + kClientEdgeThickness - client_area_top), |
| 799 | toolbar_color); |
| 800 | canvas->FillRect(gfx::Rect(client_area_bounds.x(), client_area_bottom, |
| 801 | client_area_bounds.width(), kClientEdgeThickness), |
| 802 | toolbar_color); |
| 803 | canvas->FillRect(gfx::Rect(client_area_bounds.right(), client_area_top, |
| 804 | kClientEdgeThickness, |
| 805 | client_area_bottom + kClientEdgeThickness - client_area_top), |
| 806 | toolbar_color); |
| 807 | } |
| 808 | |
| 809 | SkColor OpaqueBrowserFrameView::GetFrameColor() const { |
| 810 | bool is_incognito = browser_view()->IsOffTheRecord(); |
| 811 | if (browser_view()->IsBrowserTypeNormal()) { |
| 812 | if (ShouldPaintAsActive()) { |
| 813 | return GetThemeProvider()->GetColor(is_incognito ? |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 814 | ThemeProperties::COLOR_FRAME_INCOGNITO : |
| 815 | ThemeProperties::COLOR_FRAME); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 816 | } |
| 817 | return GetThemeProvider()->GetColor(is_incognito ? |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 818 | ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE : |
| 819 | ThemeProperties::COLOR_FRAME_INACTIVE); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 820 | } |
| 821 | // Never theme app and popup windows. |
| 822 | if (ShouldPaintAsActive()) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 823 | return ThemeProperties::GetDefaultColor(is_incognito ? |
| 824 | ThemeProperties::COLOR_FRAME_INCOGNITO : ThemeProperties::COLOR_FRAME); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 825 | } |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 826 | return ThemeProperties::GetDefaultColor(is_incognito ? |
| 827 | ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE : |
| 828 | ThemeProperties::COLOR_FRAME_INACTIVE); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 829 | } |
| 830 | |
| 831 | gfx::ImageSkia* OpaqueBrowserFrameView::GetFrameImage() const { |
| 832 | bool is_incognito = browser_view()->IsOffTheRecord(); |
| 833 | int resource_id; |
| 834 | if (browser_view()->IsBrowserTypeNormal()) { |
| 835 | if (ShouldPaintAsActive()) { |
| 836 | resource_id = is_incognito ? |
| 837 | IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME; |
| 838 | } else { |
| 839 | resource_id = is_incognito ? |
| 840 | IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE; |
| 841 | } |
| 842 | return GetThemeProvider()->GetImageSkiaNamed(resource_id); |
| 843 | } |
| 844 | // Never theme app and popup windows. |
| 845 | ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| 846 | if (ShouldPaintAsActive()) { |
| 847 | resource_id = is_incognito ? |
| 848 | IDR_THEME_FRAME_INCOGNITO : IDR_FRAME; |
| 849 | } else { |
| 850 | resource_id = is_incognito ? |
| 851 | IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE; |
| 852 | } |
| 853 | return rb.GetImageSkiaNamed(resource_id); |
| 854 | } |
| 855 | |
| 856 | gfx::ImageSkia* OpaqueBrowserFrameView::GetFrameOverlayImage() const { |
| 857 | ui::ThemeProvider* tp = GetThemeProvider(); |
| 858 | if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) && |
| 859 | browser_view()->IsBrowserTypeNormal() && |
| 860 | !browser_view()->IsOffTheRecord()) { |
| 861 | return tp->GetImageSkiaNamed(ShouldPaintAsActive() ? |
| 862 | IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE); |
| 863 | } |
| 864 | return NULL; |
| 865 | } |
| 866 | |
| 867 | int OpaqueBrowserFrameView::GetTopAreaHeight() const { |
| 868 | gfx::ImageSkia* frame_image = GetFrameImage(); |
| 869 | int top_area_height = frame_image->height(); |
| 870 | if (browser_view()->IsTabStripVisible()) { |
| 871 | top_area_height = std::max(top_area_height, |
| 872 | GetBoundsForTabStrip(browser_view()->tabstrip()).bottom()); |
| 873 | } |
| 874 | return top_area_height; |
| 875 | } |