| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/views/info_bubble.h" | 5 #include "chrome/browser/views/info_bubble.h" |
| 6 | 6 |
| 7 #include "app/gfx/canvas.h" | 7 #include "app/gfx/canvas.h" |
| 8 #include "app/gfx/color_utils.h" | 8 #include "app/gfx/color_utils.h" |
| 9 #include "app/gfx/path.h" | 9 #include "app/gfx/path.h" |
| 10 #include "app/resource_bundle.h" | |
| 11 #include "chrome/browser/window_sizer.h" | 10 #include "chrome/browser/window_sizer.h" |
| 12 #include "chrome/common/notification_service.h" | 11 #include "chrome/common/notification_service.h" |
| 13 #include "grit/theme_resources.h" | 12 #include "third_party/Skia/include/core/SkPaint.h" |
| 13 #include "views/fill_layout.h" |
| 14 #include "views/widget/root_view.h" | 14 #include "views/widget/root_view.h" |
| 15 #include "views/window/window.h" | 15 #include "views/window/window.h" |
| 16 | 16 |
| 17 #if defined(OS_WIN) | |
| 18 #include "base/win_util.h" | |
| 19 #endif | |
| 20 | |
| 21 using views::View; | |
| 22 | |
| 23 namespace { | 17 namespace { |
| 24 | 18 |
| 25 // All sizes are in pixels. | |
| 26 | |
| 27 // Size of the border, along each edge. | |
| 28 const int kBorderSize = 1; | |
| 29 | |
| 30 // Size of the arrow. | |
| 31 const int kArrowSize = 5; | |
| 32 | |
| 33 // Number of pixels to the start of the arrow from the edge of the window. | |
| 34 const int kArrowXOffset = 13; | |
| 35 | |
| 36 // Number of pixels between the tip of the arrow and the region we're | |
| 37 // pointing to. | |
| 38 const int kArrowToContentPadding = -4; | |
| 39 | |
| 40 // Background color of the bubble. | 19 // Background color of the bubble. |
| 41 #if defined(OS_WIN) | 20 #if defined(OS_WIN) |
| 42 const SkColor kBackgroundColor = color_utils::GetSysSkColor(COLOR_WINDOW); | 21 const SkColor kBackgroundColor = color_utils::GetSysSkColor(COLOR_WINDOW); |
| 43 #else | 22 #else |
| 44 // TODO(beng): source from theme provider. | 23 // TODO(beng): source from theme provider. |
| 45 const SkColor kBackgroundColor = SK_ColorWHITE; | 24 const SkColor kBackgroundColor = SK_ColorWHITE; |
| 46 #endif | 25 #endif |
| 47 | 26 |
| 48 // Color of the border and arrow. | 27 } |
| 49 const SkColor kBorderColor1 = SkColorSetRGB(99, 99, 99); | |
| 50 // Border shadow color. | |
| 51 const SkColor kBorderColor2 = SkColorSetRGB(160, 160, 160); | |
| 52 | 28 |
| 53 // Intended dimensions of the bubble's corner images. If you update these, | 29 #if defined(OS_WIN) |
| 54 // make sure that the OnSize code works. | 30 // BorderContents ------------------------------------------------------------- |
| 55 const int kInfoBubbleCornerWidth = 3; | |
| 56 const int kInfoBubbleCornerHeight = 3; | |
| 57 | 31 |
| 58 // Bubble corner images. | 32 // This is used to paint the border; see comments on BorderWidget below. |
| 59 const SkBitmap* kInfoBubbleCornerTopLeft = NULL; | 33 class BorderContents : public views::View { |
| 60 const SkBitmap* kInfoBubbleCornerTopRight = NULL; | 34 public: |
| 61 const SkBitmap* kInfoBubbleCornerBottomLeft = NULL; | 35 BorderContents() { } |
| 62 const SkBitmap* kInfoBubbleCornerBottomRight = NULL; | |
| 63 | 36 |
| 64 // Margins around the content. | 37 // Given the size of the contents and the rect (in screen coordinates) to |
| 65 const int kInfoBubbleViewTopMargin = 6; | 38 // point at, initializes the bubble and returns the bounds (in screen |
| 66 const int kInfoBubbleViewBottomMargin = 9; | 39 // coordinates) of both the border and the contents inside the bubble. |
| 67 const int kInfoBubbleViewLeftMargin = 6; | 40 // |is_rtl| is true if the UI is RTL and thus the arrow should default to the |
| 68 const int kInfoBubbleViewRightMargin = 6; | 41 // right side of the bubble; otherwise it defaults to the left top corner, and |
| 42 // then is moved as necessary to try and fit the whole bubble on the same |
| 43 // monitor as the rect being pointed to. |
| 44 // |
| 45 // TODO(pkasting): Maybe this should use mirroring transformations instead, |
| 46 // which would hopefully simplify this code. |
| 47 void InitAndGetBounds(const gfx::Rect& position_relative_to, |
| 48 const gfx::Size& contents_size, |
| 49 bool is_rtl, |
| 50 gfx::Rect* inner_bounds, |
| 51 gfx::Rect* outer_bounds); |
| 69 | 52 |
| 70 } // namespace | 53 private: |
| 54 virtual ~BorderContents() { } |
| 55 |
| 56 // Overridden from View: |
| 57 virtual void Paint(gfx::Canvas* canvas); |
| 58 |
| 59 DISALLOW_COPY_AND_ASSIGN(BorderContents); |
| 60 }; |
| 61 |
| 62 void BorderContents::InitAndGetBounds( |
| 63 const gfx::Rect& position_relative_to, |
| 64 const gfx::Size& contents_size, |
| 65 bool is_rtl, |
| 66 gfx::Rect* inner_bounds, |
| 67 gfx::Rect* outer_bounds) { |
| 68 // Set the border. |
| 69 BubbleBorder* bubble_border = new BubbleBorder; |
| 70 set_border(bubble_border); |
| 71 bubble_border->set_background_color(kBackgroundColor); |
| 72 |
| 73 // Try putting the arrow in its default location, and calculating the bounds. |
| 74 BubbleBorder::ArrowLocation arrow_location(is_rtl ? |
| 75 BubbleBorder::TOP_RIGHT : BubbleBorder::TOP_LEFT); |
| 76 bubble_border->set_arrow_location(arrow_location); |
| 77 *outer_bounds = bubble_border->GetBounds(position_relative_to, contents_size); |
| 78 |
| 79 // See if those bounds will fit on the monitor. |
| 80 scoped_ptr<WindowSizer::MonitorInfoProvider> monitor_provider( |
| 81 WindowSizer::CreateDefaultMonitorInfoProvider()); |
| 82 gfx::Rect monitor_bounds( |
| 83 monitor_provider->GetMonitorWorkAreaMatching(position_relative_to)); |
| 84 if (!monitor_bounds.IsEmpty() && !monitor_bounds.Contains(*outer_bounds)) { |
| 85 // The bounds don't fit. Move the arrow to try and improve things. |
| 86 bool arrow_on_left = |
| 87 (is_rtl ? (outer_bounds->x() < monitor_bounds.x()) : |
| 88 (outer_bounds->right() <= monitor_bounds.right())); |
| 89 if (outer_bounds->bottom() > monitor_bounds.bottom()) { |
| 90 arrow_location = arrow_on_left ? |
| 91 BubbleBorder::BOTTOM_LEFT : BubbleBorder::BOTTOM_RIGHT; |
| 92 } else { |
| 93 arrow_location = arrow_on_left ? |
| 94 BubbleBorder::TOP_LEFT : BubbleBorder::TOP_RIGHT; |
| 95 } |
| 96 bubble_border->set_arrow_location(arrow_location); |
| 97 |
| 98 // Now get the recalculated bounds. |
| 99 *outer_bounds = bubble_border->GetBounds(position_relative_to, |
| 100 contents_size); |
| 101 } |
| 102 |
| 103 // Calculate the bounds of the contained contents by subtracting the border |
| 104 // dimensions. |
| 105 *inner_bounds = *outer_bounds; |
| 106 gfx::Insets insets; |
| 107 bubble_border->GetInsets(&insets); |
| 108 inner_bounds->Inset(insets.left(), insets.top(), insets.right(), |
| 109 insets.bottom()); |
| 110 } |
| 111 |
| 112 void BorderContents::Paint(gfx::Canvas* canvas) { |
| 113 // The border of this view creates an anti-aliased round-rect region for the |
| 114 // contents, which we need to fill with the background color. |
| 115 SkPaint paint; |
| 116 paint.setAntiAlias(true); |
| 117 paint.setStyle(SkPaint::kFill_Style); |
| 118 paint.setColor(kBackgroundColor); |
| 119 gfx::Path path; |
| 120 gfx::Rect bounds(GetLocalBounds(false)); |
| 121 SkRect rect; |
| 122 rect.set(SkIntToScalar(bounds.x()), SkIntToScalar(bounds.y()), |
| 123 SkIntToScalar(bounds.right()), SkIntToScalar(bounds.bottom())); |
| 124 SkScalar radius = SkIntToScalar(BubbleBorder::GetCornerRadius()); |
| 125 path.addRoundRect(rect, radius, radius); |
| 126 canvas->drawPath(path, paint); |
| 127 |
| 128 // Now we paint the border, so it will be alpha-blended atop the contents. |
| 129 // This looks slightly better in the corners than drawing the contents atop |
| 130 // the border. |
| 131 PaintBorder(canvas); |
| 132 } |
| 133 |
| 134 // BorderWidget --------------------------------------------------------------- |
| 135 |
| 136 BorderWidget::BorderWidget() { |
| 137 set_delete_on_destroy(false); // Our owner will free us manually. |
| 138 set_window_style(WS_POPUP); |
| 139 set_window_ex_style(WS_EX_TOOLWINDOW | WS_EX_LAYERED); |
| 140 } |
| 141 |
| 142 gfx::Rect BorderWidget::InitAndGetBounds( |
| 143 HWND owner, |
| 144 const gfx::Rect& position_relative_to, |
| 145 const gfx::Size& contents_size, |
| 146 bool is_rtl) { |
| 147 // Margins around the contents. |
| 148 const int kLeftMargin = 6; |
| 149 const int kTopMargin = 6; |
| 150 const int kRightMargin = 6; |
| 151 const int kBottomMargin = 9; |
| 152 |
| 153 // Set up the border view and ask it to calculate our bounds (and our |
| 154 // contents'). |
| 155 gfx::Size local_contents_size(contents_size); |
| 156 local_contents_size.Enlarge(kLeftMargin + kRightMargin, |
| 157 kTopMargin + kBottomMargin); |
| 158 BorderContents* border_contents = new BorderContents; |
| 159 gfx::Rect inner_bounds, outer_bounds; |
| 160 border_contents->InitAndGetBounds(position_relative_to, local_contents_size, |
| 161 is_rtl, &inner_bounds, &outer_bounds); |
| 162 |
| 163 // Initialize ourselves. |
| 164 WidgetWin::Init(GetAncestor(owner, GA_ROOT), outer_bounds); |
| 165 SetContentsView(border_contents); |
| 166 SetWindowPos(owner, 0, 0, 0, 0, |
| 167 SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW); |
| 168 |
| 169 // Chop a hole out of our region to show the contents through. |
| 170 // CreateRectRgn() expects (left, top, right, bottom) in window coordinates. |
| 171 inner_bounds.Inset(kLeftMargin, kTopMargin, kRightMargin, kBottomMargin); |
| 172 gfx::Rect region_bounds(inner_bounds); |
| 173 region_bounds.Offset(-outer_bounds.x(), -outer_bounds.y()); |
| 174 HRGN inner_region = CreateRectRgn(region_bounds.x(), region_bounds.y(), |
| 175 region_bounds.right(), region_bounds.bottom()); |
| 176 HRGN outer_region = CreateRectRgn(0, 0, |
| 177 outer_bounds.right(), outer_bounds.bottom()); |
| 178 CombineRgn(outer_region, outer_region, inner_region, RGN_XOR); |
| 179 DeleteObject(inner_region); |
| 180 SetWindowRgn(outer_region, true); |
| 181 |
| 182 return inner_bounds; |
| 183 } |
| 184 |
| 185 LRESULT BorderWidget::OnMouseActivate(HWND window, |
| 186 UINT hit_test, |
| 187 UINT mouse_message) { |
| 188 // Never activate. |
| 189 return MA_NOACTIVATE; |
| 190 } |
| 191 #endif |
| 71 | 192 |
| 72 // InfoBubble ----------------------------------------------------------------- | 193 // InfoBubble ----------------------------------------------------------------- |
| 73 | 194 |
| 74 // static | 195 // static |
| 75 InfoBubble* InfoBubble::Show(views::Window* parent, | 196 InfoBubble* InfoBubble::Show(views::Window* parent, |
| 76 const gfx::Rect& position_relative_to, | 197 const gfx::Rect& position_relative_to, |
| 77 views::View* contents, | 198 views::View* contents, |
| 78 InfoBubbleDelegate* delegate) { | 199 InfoBubbleDelegate* delegate) { |
| 79 InfoBubble* window = new InfoBubble(); | 200 InfoBubble* window = new InfoBubble; |
| 80 window->Init(parent, position_relative_to, contents, delegate); | 201 window->Init(parent, position_relative_to, contents, delegate); |
| 81 return window; | 202 return window; |
| 82 } | 203 } |
| 83 | 204 |
| 84 void InfoBubble::Close() { | 205 void InfoBubble::Close() { |
| 85 Close(false); | 206 Close(false); |
| 86 } | 207 } |
| 87 | 208 |
| 88 InfoBubble::InfoBubble() | 209 InfoBubble::InfoBubble() |
| 89 : | 210 : |
| 90 #if defined(OS_LINUX) | 211 #if defined(OS_LINUX) |
| 91 WidgetGtk(TYPE_POPUP), | 212 WidgetGtk(TYPE_POPUP), |
| 92 #endif | 213 #endif |
| 93 delegate_(NULL), | 214 delegate_(NULL), |
| 94 parent_(NULL), | 215 parent_(NULL), |
| 95 content_view_(NULL), | |
| 96 closed_(false) { | 216 closed_(false) { |
| 97 } | 217 } |
| 98 | 218 |
| 99 void InfoBubble::Init(views::Window* parent, | 219 void InfoBubble::Init(views::Window* parent, |
| 100 const gfx::Rect& position_relative_to, | 220 const gfx::Rect& position_relative_to, |
| 101 views::View* contents, | 221 views::View* contents, |
| 102 InfoBubbleDelegate* delegate) { | 222 InfoBubbleDelegate* delegate) { |
| 103 parent_ = parent; | 223 parent_ = parent; |
| 104 parent_->DisableInactiveRendering(); | 224 parent_->DisableInactiveRendering(); |
| 105 | 225 |
| 106 delegate_ = delegate; | 226 delegate_ = delegate; |
| 107 | 227 |
| 108 if (kInfoBubbleCornerTopLeft == NULL) { | |
| 109 kInfoBubbleCornerTopLeft = ResourceBundle::GetSharedInstance() | |
| 110 .GetBitmapNamed(IDR_INFO_BUBBLE_CORNER_TOP_LEFT); | |
| 111 kInfoBubbleCornerTopRight = ResourceBundle::GetSharedInstance() | |
| 112 .GetBitmapNamed(IDR_INFO_BUBBLE_CORNER_TOP_RIGHT); | |
| 113 kInfoBubbleCornerBottomLeft = ResourceBundle::GetSharedInstance() | |
| 114 .GetBitmapNamed(IDR_INFO_BUBBLE_CORNER_BOTTOM_LEFT); | |
| 115 kInfoBubbleCornerBottomRight = ResourceBundle::GetSharedInstance() | |
| 116 .GetBitmapNamed(IDR_INFO_BUBBLE_CORNER_BOTTOM_RIGHT); | |
| 117 } | |
| 118 #if defined(OS_WIN) | 228 #if defined(OS_WIN) |
| 119 set_window_style(WS_POPUP | WS_CLIPCHILDREN); | 229 set_window_style(WS_POPUP | WS_CLIPCHILDREN); |
| 120 set_window_ex_style(WS_EX_TOOLWINDOW); | 230 set_window_ex_style(WS_EX_TOOLWINDOW); |
| 121 set_initial_class_style( | 231 border_.reset(new BorderWidget); |
| 122 (win_util::GetWinVersion() < win_util::WINVERSION_XP) ? | |
| 123 0 : CS_DROPSHADOW); | |
| 124 #endif | 232 #endif |
| 125 content_view_ = CreateContentView(contents); | |
| 126 | 233 |
| 127 #if defined(OS_WIN) | 234 #if defined(OS_WIN) |
| 128 WidgetWin::Init(parent->GetNativeWindow(), gfx::Rect()); | 235 WidgetWin::Init(parent->GetNativeWindow(), gfx::Rect()); |
| 129 #else | 236 #else |
| 130 WidgetGtk::Init(GTK_WIDGET(parent->GetNativeWindow()), gfx::Rect()); | 237 WidgetGtk::Init(GTK_WIDGET(parent->GetNativeWindow()), gfx::Rect()); |
| 131 #endif | 238 #endif |
| 132 | 239 |
| 133 SetContentsView(content_view_); | 240 views::View* contents_view = new views::View; |
| 134 // The preferred size may differ when parented. Ask for the bounds again | 241 contents_view->set_background( |
| 135 // and if they differ reset the bounds. | 242 views::Background::CreateSolidBackground(kBackgroundColor)); |
| 136 gfx::Rect parented_bounds = | 243 contents_view->SetLayoutManager(new views::FillLayout); |
| 137 content_view_->CalculateWindowBoundsAndAjust(position_relative_to); | 244 // Adding |contents| as a child has to be done before we call |
| 138 SetBounds(parented_bounds); | 245 // contents->GetPreferredSize() below, since some supplied views don't |
| 246 // actually set themselves up until they're added to a hierarchy. |
| 247 contents_view->AddChildView(contents); |
| 248 SetContentsView(contents_view); |
| 139 | 249 |
| 250 gfx::Rect bounds; |
| 140 #if defined(OS_WIN) | 251 #if defined(OS_WIN) |
| 252 bounds = border_->InitAndGetBounds(GetNativeView(), |
| 253 position_relative_to, |
| 254 contents_view->GetPreferredSize(), |
| 255 contents->UILayoutIsRightToLeft()); |
| 256 |
| 141 // Register the Escape accelerator for closing. | 257 // Register the Escape accelerator for closing. |
| 142 GetFocusManager()->RegisterAccelerator( | 258 GetFocusManager()->RegisterAccelerator( |
| 143 views::Accelerator(VK_ESCAPE, false, false, false), this); | 259 views::Accelerator(VK_ESCAPE, false, false, false), this); |
| 144 #endif | 260 #endif |
| 261 SetBounds(bounds); |
| 145 | 262 |
| 146 NotificationService::current()->Notify(NotificationType::INFO_BUBBLE_CREATED, | 263 NotificationService::current()->Notify(NotificationType::INFO_BUBBLE_CREATED, |
| 147 Source<InfoBubble>(this), | 264 Source<InfoBubble>(this), |
| 148 NotificationService::NoDetails()); | 265 NotificationService::NoDetails()); |
| 149 | 266 |
| 150 // Show the window. | 267 // Show the window. |
| 151 #if defined(OS_WIN) | 268 #if defined(OS_WIN) |
| 269 border_->ShowWindow(SW_SHOW); |
| 152 ShowWindow(SW_SHOW); | 270 ShowWindow(SW_SHOW); |
| 153 #else | 271 #else |
| 154 views::WidgetGtk::Show(); | 272 views::WidgetGtk::Show(); |
| 155 #endif | 273 #endif |
| 156 } | 274 } |
| 157 | 275 |
| 158 InfoBubble::ContentView* InfoBubble::CreateContentView(View* content) { | |
| 159 return new ContentView(content, this); | |
| 160 } | |
| 161 | |
| 162 #if defined(OS_WIN) | 276 #if defined(OS_WIN) |
| 163 void InfoBubble::OnActivate(UINT action, BOOL minimized, HWND window) { | 277 void InfoBubble::OnActivate(UINT action, BOOL minimized, HWND window) { |
| 164 // The popup should close when it is deactivated. | 278 // The popup should close when it is deactivated. |
| 165 if (action == WA_INACTIVE && !closed_) { | 279 if (action == WA_INACTIVE && !closed_) { |
| 166 Close(); | 280 Close(); |
| 167 } else if (action == WA_ACTIVE) { | 281 } else if (action == WA_ACTIVE) { |
| 168 DCHECK(GetRootView()->GetChildViewCount() > 0); | 282 DCHECK(GetRootView()->GetChildViewCount() > 0); |
| 169 GetRootView()->GetChildViewAt(0)->RequestFocus(); | 283 GetRootView()->GetChildViewAt(0)->RequestFocus(); |
| 170 } | 284 } |
| 171 } | 285 } |
| 172 | |
| 173 void InfoBubble::OnSize(UINT param, const CSize& size) { | |
| 174 // See OnSizeAllocate for the Linux version. | |
| 175 gfx::Path path; | |
| 176 content_view_->GetMask(gfx::Size(size.cx, size.cy), &path); | |
| 177 SetWindowRgn(path.CreateHRGN(), TRUE); | |
| 178 WidgetWin::OnSize(param, size); | |
| 179 } | |
| 180 #elif defined(OS_LINUX) | |
| 181 void InfoBubble::OnSizeAllocate(GtkWidget* widget, GtkAllocation* allocation) { | |
| 182 gfx::Path path; | |
| 183 content_view_->GetMask(gfx::Size(allocation->width, allocation->height), | |
| 184 &path); | |
| 185 SetShape(path); | |
| 186 WidgetGtk::OnSizeAllocate(widget, allocation); | |
| 187 } | |
| 188 #endif | 286 #endif |
| 189 | 287 |
| 190 void InfoBubble::Close(bool closed_by_escape) { | 288 void InfoBubble::Close(bool closed_by_escape) { |
| 191 if (closed_) | 289 if (closed_) |
| 192 return; | 290 return; |
| 193 | 291 |
| 194 if (delegate_) | 292 if (delegate_) |
| 195 delegate_->InfoBubbleClosing(this, closed_by_escape); | 293 delegate_->InfoBubbleClosing(this, closed_by_escape); |
| 196 closed_ = true; | 294 closed_ = true; |
| 197 #if defined(OS_WIN) | 295 #if defined(OS_WIN) |
| 296 border_->Close(); |
| 198 WidgetWin::Close(); | 297 WidgetWin::Close(); |
| 199 #else | 298 #else |
| 200 WidgetGtk::Close(); | 299 WidgetGtk::Close(); |
| 201 #endif | 300 #endif |
| 202 } | 301 } |
| 203 | 302 |
| 204 bool InfoBubble::AcceleratorPressed(const views::Accelerator& accelerator) { | 303 bool InfoBubble::AcceleratorPressed(const views::Accelerator& accelerator) { |
| 205 if (!delegate_ || delegate_->CloseOnEscape()) { | 304 if (!delegate_ || delegate_->CloseOnEscape()) { |
| 206 Close(true); | 305 Close(true); |
| 207 return true; | 306 return true; |
| 208 } | 307 } |
| 209 return false; | 308 return false; |
| 210 } | 309 } |
| 211 | |
| 212 // ContentView ---------------------------------------------------------------- | |
| 213 | |
| 214 InfoBubble::ContentView::ContentView(views::View* content, InfoBubble* host) | |
| 215 : content_(content), | |
| 216 host_(host) { | |
| 217 if (UILayoutIsRightToLeft()) { | |
| 218 arrow_edge_ = TOP_RIGHT; | |
| 219 } else { | |
| 220 arrow_edge_ = TOP_LEFT; | |
| 221 } | |
| 222 } | |
| 223 | |
| 224 gfx::Rect InfoBubble::ContentView::CalculateWindowBoundsAndAjust( | |
| 225 const gfx::Rect& position_relative_to) { | |
| 226 scoped_ptr<WindowSizer::MonitorInfoProvider> monitor_provider( | |
| 227 WindowSizer::CreateDefaultMonitorInfoProvider()); | |
| 228 gfx::Rect monitor_bounds( | |
| 229 monitor_provider->GetMonitorWorkAreaMatching(position_relative_to)); | |
| 230 // Calculate the bounds using TOP_LEFT (the default). | |
| 231 gfx::Rect window_bounds = CalculateWindowBounds(position_relative_to); | |
| 232 if (monitor_bounds.IsEmpty() || monitor_bounds.Contains(window_bounds)) | |
| 233 return window_bounds; | |
| 234 // Didn't fit, adjust the edge to fit as much as we can. | |
| 235 if (window_bounds.bottom() > monitor_bounds.bottom()) | |
| 236 SetArrowEdge(BOTTOM_LEFT); | |
| 237 if (window_bounds.right() > monitor_bounds.right()) { | |
| 238 if (IsTop()) | |
| 239 SetArrowEdge(TOP_RIGHT); | |
| 240 else | |
| 241 SetArrowEdge(BOTTOM_RIGHT); | |
| 242 } | |
| 243 // And return new bounds. | |
| 244 return CalculateWindowBounds(position_relative_to); | |
| 245 } | |
| 246 | |
| 247 gfx::Size InfoBubble::ContentView::GetPreferredSize() { | |
| 248 DCHECK(GetChildViewCount() == 1); | |
| 249 View* content = GetChildViewAt(0); | |
| 250 gfx::Size pref = content->GetPreferredSize(); | |
| 251 pref.Enlarge(kBorderSize + kBorderSize + kInfoBubbleViewLeftMargin + | |
| 252 kInfoBubbleViewRightMargin, | |
| 253 kBorderSize + kBorderSize + kArrowSize + | |
| 254 kInfoBubbleViewTopMargin + kInfoBubbleViewBottomMargin); | |
| 255 return pref; | |
| 256 } | |
| 257 | |
| 258 void InfoBubble::ContentView::Layout() { | |
| 259 DCHECK(GetChildViewCount() == 1); | |
| 260 View* content = GetChildViewAt(0); | |
| 261 int x = kBorderSize; | |
| 262 int y = kBorderSize; | |
| 263 int content_width = width() - kBorderSize - kBorderSize - | |
| 264 kInfoBubbleViewLeftMargin - kInfoBubbleViewRightMargin; | |
| 265 int content_height = height() - kBorderSize - kBorderSize - kArrowSize - | |
| 266 kInfoBubbleViewTopMargin - kInfoBubbleViewBottomMargin; | |
| 267 if (IsTop()) | |
| 268 y += kArrowSize; | |
| 269 x += kInfoBubbleViewLeftMargin; | |
| 270 y += kInfoBubbleViewTopMargin; | |
| 271 content->SetBounds(x, y, content_width, content_height); | |
| 272 } | |
| 273 | |
| 274 void InfoBubble::ContentView::GetMask(const gfx::Size& size, gfx::Path* mask) { | |
| 275 // Redefine the window visible region so that our dropshadows look right. | |
| 276 SkScalar width = SkIntToScalar(size.width()); | |
| 277 SkScalar height = SkIntToScalar(size.height()); | |
| 278 SkScalar arrow_size = SkIntToScalar(kArrowSize); | |
| 279 SkScalar arrow_x = SkIntToScalar( | |
| 280 (IsLeft() ? kArrowXOffset : width - kArrowXOffset) - 1); | |
| 281 SkScalar corner_size = SkIntToScalar(kInfoBubbleCornerHeight); | |
| 282 | |
| 283 if (IsTop()) { | |
| 284 // Top left corner. | |
| 285 mask->moveTo(0, arrow_size + corner_size - 1); | |
| 286 mask->lineTo(corner_size - 1, arrow_size); | |
| 287 | |
| 288 // Draw the arrow and the notch of the arrow. | |
| 289 mask->lineTo(arrow_x - arrow_size, arrow_size); | |
| 290 mask->lineTo(arrow_x, 0); | |
| 291 mask->lineTo(arrow_x + 3, 0); | |
| 292 mask->lineTo(arrow_x + arrow_size + 3, arrow_size); | |
| 293 | |
| 294 // Top right corner. | |
| 295 mask->lineTo(width - corner_size + 1, arrow_size); | |
| 296 mask->lineTo(width, arrow_size + corner_size - 1); | |
| 297 | |
| 298 // Bottom right corner. | |
| 299 mask->lineTo(width, height - corner_size); | |
| 300 mask->lineTo(width - corner_size, height); | |
| 301 | |
| 302 // Bottom left corner. | |
| 303 mask->lineTo(corner_size, height); | |
| 304 mask->lineTo(0, height - corner_size); | |
| 305 } else { | |
| 306 // Top left corner. | |
| 307 mask->moveTo(0, corner_size - 1); | |
| 308 mask->lineTo(corner_size - 1, 0); | |
| 309 | |
| 310 // Top right corner. | |
| 311 mask->lineTo(width - corner_size + 1, 0); | |
| 312 mask->lineTo(width, corner_size - 1); | |
| 313 | |
| 314 // Bottom right corner. | |
| 315 mask->lineTo(width, height - corner_size - arrow_size); | |
| 316 mask->lineTo(width - corner_size, height - arrow_size); | |
| 317 | |
| 318 // Draw the arrow and the notch of the arrow. | |
| 319 mask->lineTo(arrow_x + arrow_size + 2, height - arrow_size); | |
| 320 mask->lineTo(arrow_x + 2, height); | |
| 321 mask->lineTo(arrow_x + 1, height); | |
| 322 mask->lineTo(arrow_x - arrow_size + 1, height - arrow_size); | |
| 323 | |
| 324 // Bottom left corner. | |
| 325 mask->lineTo(corner_size, height - arrow_size); | |
| 326 mask->lineTo(0, height - corner_size - arrow_size); | |
| 327 } | |
| 328 | |
| 329 mask->close(); | |
| 330 } | |
| 331 | |
| 332 void InfoBubble::ContentView::Paint(gfx::Canvas* canvas) { | |
| 333 int bubble_x = 0; | |
| 334 int bubble_y = 0; | |
| 335 int bubble_w = width(); | |
| 336 int bubble_h = height() - kArrowSize; | |
| 337 | |
| 338 int border_w = bubble_w - 2 * kInfoBubbleCornerWidth; | |
| 339 int border_h = bubble_h - 2 * kInfoBubbleCornerHeight; | |
| 340 | |
| 341 if (IsTop()) | |
| 342 bubble_y += kArrowSize; | |
| 343 | |
| 344 // Fill in the background. | |
| 345 // Left side. | |
| 346 canvas->FillRectInt(kBackgroundColor, | |
| 347 bubble_x, bubble_y + kInfoBubbleCornerHeight, | |
| 348 kInfoBubbleCornerWidth, border_h); | |
| 349 // Center Column. | |
| 350 canvas->FillRectInt(kBackgroundColor, | |
| 351 kInfoBubbleCornerWidth, bubble_y, | |
| 352 border_w, bubble_h); | |
| 353 // Right Column. | |
| 354 canvas->FillRectInt(kBackgroundColor, | |
| 355 bubble_w - kInfoBubbleCornerWidth, | |
| 356 bubble_y + kInfoBubbleCornerHeight, | |
| 357 kInfoBubbleCornerWidth, border_h); | |
| 358 | |
| 359 // Draw the border. | |
| 360 // Top border. | |
| 361 canvas->DrawLineInt(kBorderColor1, | |
| 362 kInfoBubbleCornerWidth, bubble_y, | |
| 363 kInfoBubbleCornerWidth + border_w, bubble_y); | |
| 364 // Bottom border. | |
| 365 canvas->DrawLineInt(kBorderColor1, | |
| 366 kInfoBubbleCornerWidth, bubble_y + bubble_h - 1, | |
| 367 kInfoBubbleCornerWidth + border_w, | |
| 368 bubble_y + bubble_h - 1); | |
| 369 // Left border. | |
| 370 canvas->DrawLineInt(kBorderColor1, | |
| 371 bubble_x, bubble_y + kInfoBubbleCornerHeight, | |
| 372 bubble_x, bubble_y + kInfoBubbleCornerHeight + border_h); | |
| 373 | |
| 374 // Right border. | |
| 375 canvas->DrawLineInt(kBorderColor1, | |
| 376 width() - 1, bubble_y + kInfoBubbleCornerHeight, | |
| 377 width() - 1, | |
| 378 bubble_y + kInfoBubbleCornerHeight + border_h); | |
| 379 | |
| 380 // Draw the corners. | |
| 381 canvas->DrawBitmapInt(*kInfoBubbleCornerTopLeft, 0, bubble_y); | |
| 382 canvas->DrawBitmapInt(*kInfoBubbleCornerTopRight, | |
| 383 bubble_w - kInfoBubbleCornerWidth, bubble_y); | |
| 384 canvas->DrawBitmapInt(*kInfoBubbleCornerBottomLeft, 0, | |
| 385 bubble_y + bubble_h - kInfoBubbleCornerHeight); | |
| 386 canvas->DrawBitmapInt(*kInfoBubbleCornerBottomRight, | |
| 387 bubble_w - kInfoBubbleCornerWidth, | |
| 388 bubble_y + bubble_h - kInfoBubbleCornerHeight); | |
| 389 | |
| 390 // Draw the arrow and the notch of the arrow. | |
| 391 int arrow_x = IsLeft() ? kArrowXOffset : width() - kArrowXOffset; | |
| 392 int arrow_y = IsTop() ? bubble_y : bubble_y + bubble_h - 1; | |
| 393 const int arrow_delta = IsTop() ? -1 : 1; | |
| 394 for (int i = 0, y = arrow_y; i <= kArrowSize; ++i, y += arrow_delta) { | |
| 395 if (kArrowSize != i) { | |
| 396 // Draw the notch formed by the arrow. | |
| 397 canvas->FillRectInt(kBackgroundColor, arrow_x - (kArrowSize - i) + 1, | |
| 398 y, (kArrowSize - i) * 2 - 1, 1); | |
| 399 } | |
| 400 // Draw the sides of the arrow. | |
| 401 canvas->FillRectInt(kBorderColor1, arrow_x - (kArrowSize - i), y, 1, 1); | |
| 402 canvas->FillRectInt(kBorderColor1, arrow_x + (kArrowSize - i), y, 1, 1); | |
| 403 if (i != 0) { | |
| 404 canvas->FillRectInt(kBorderColor2, arrow_x - (kArrowSize - i) - 1, y, 1, | |
| 405 1); | |
| 406 canvas->FillRectInt(kBorderColor2, arrow_x + (kArrowSize - i) + 1, y, 1, | |
| 407 1); | |
| 408 } | |
| 409 } | |
| 410 } | |
| 411 | |
| 412 void InfoBubble::ContentView::ViewHierarchyChanged(bool is_add, | |
| 413 View* parent, | |
| 414 View* child) { | |
| 415 if (is_add && child == this) | |
| 416 AddChildView(content_); | |
| 417 } | |
| 418 | |
| 419 gfx::Rect InfoBubble::ContentView::CalculateWindowBounds( | |
| 420 const gfx::Rect& position_relative_to) { | |
| 421 gfx::Size pref = GetPreferredSize(); | |
| 422 int x = position_relative_to.x() + position_relative_to.width() / 2; | |
| 423 int y; | |
| 424 if (IsLeft()) | |
| 425 x -= kArrowXOffset; | |
| 426 else | |
| 427 x = x + kArrowXOffset - pref.width(); | |
| 428 if (IsTop()) { | |
| 429 y = position_relative_to.bottom() + kArrowToContentPadding; | |
| 430 } else { | |
| 431 y = position_relative_to.y() - kArrowToContentPadding - pref.height(); | |
| 432 } | |
| 433 return gfx::Rect(x, y, pref.width(), pref.height()); | |
| 434 } | |
| OLD | NEW |