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 "chrome/browser/ui/views/bubble/border_contents.h" 6 7 #include <algorithm> 8 9 #include "base/logging.h" 10 #include "chrome/browser/ui/window_sizer.h" 11 #include "third_party/skia/include/core/SkPaint.h" 12 13 void BorderContents::Init() { 14 // Default arrow location. 15 BubbleBorder::ArrowLocation arrow_location = BubbleBorder::TOP_LEFT; 16 if (base::i18n::IsRTL()) 17 arrow_location = BubbleBorder::horizontal_mirror(arrow_location); 18 DCHECK(!bubble_border_); 19 20 bubble_border_ = new BubbleBorder(arrow_location); 21 set_border(bubble_border_); 22 set_background(new BubbleBackground(bubble_border_)); 23 } 24 25 void BorderContents::SetBackgroundColor(SkColor color) { 26 bubble_border_->set_background_color(color); 27 } 28 29 void BorderContents::SizeAndGetBounds( 30 const gfx::Rect& position_relative_to, 31 BubbleBorder::ArrowLocation arrow_location, 32 bool allow_bubble_offscreen, 33 const gfx::Size& contents_size, 34 gfx::Rect* contents_bounds, 35 gfx::Rect* window_bounds) { 36 if (base::i18n::IsRTL()) 37 arrow_location = BubbleBorder::horizontal_mirror(arrow_location); 38 bubble_border_->set_arrow_location(arrow_location); 39 // Set the border. 40 set_border(bubble_border_); 41 42 // Give the contents a margin. 43 gfx::Size local_contents_size(contents_size); 44 local_contents_size.Enlarge(kLeftMargin + kRightMargin, 45 kTopMargin + kBottomMargin); 46 47 // Try putting the arrow in its initial location, and calculating the bounds. 48 *window_bounds = 49 bubble_border_->GetBounds(position_relative_to, local_contents_size); 50 if (!allow_bubble_offscreen) { 51 gfx::Rect monitor_bounds = GetMonitorBounds(position_relative_to); 52 if (!monitor_bounds.IsEmpty()) { 53 // Try to resize vertically if this does not fit on the screen. 54 MirrorArrowIfOffScreen(true, // |vertical|. 55 position_relative_to, monitor_bounds, 56 local_contents_size, &arrow_location, 57 window_bounds); 58 // Then try to resize horizontally if it still does not fit on the screen. 59 MirrorArrowIfOffScreen(false, // |vertical|. 60 position_relative_to, monitor_bounds, 61 local_contents_size, &arrow_location, 62 window_bounds); 63 } 64 } 65 66 // Calculate the bounds of the contained contents (in window coordinates) by 67 // subtracting the border dimensions and margin amounts. 68 *contents_bounds = gfx::Rect(gfx::Point(), window_bounds->size()); 69 gfx::Insets insets; 70 bubble_border_->GetInsets(&insets); 71 contents_bounds->Inset(insets.left() + kLeftMargin, insets.top() + kTopMargin, 72 insets.right() + kRightMargin, insets.bottom() + kBottomMargin); 73 } 74 75 gfx::Rect BorderContents::GetMonitorBounds(const gfx::Rect& rect) { 76 scoped_ptr<WindowSizer::MonitorInfoProvider> monitor_provider( 77 WindowSizer::CreateDefaultMonitorInfoProvider()); 78 return monitor_provider->GetMonitorWorkAreaMatching(rect); 79 } 80 81 void BorderContents::MirrorArrowIfOffScreen( 82 bool vertical, 83 const gfx::Rect& position_relative_to, 84 const gfx::Rect& monitor_bounds, 85 const gfx::Size& local_contents_size, 86 BubbleBorder::ArrowLocation* arrow_location, 87 gfx::Rect* window_bounds) { 88 // If the bounds don't fit, move the arrow to its mirrored position to see if 89 // it improves things. 90 gfx::Insets offscreen_insets; 91 if (ComputeOffScreenInsets(monitor_bounds, *window_bounds, 92 &offscreen_insets) && 93 GetInsetsLength(offscreen_insets, vertical) > 0) { 94 BubbleBorder::ArrowLocation original_arrow_location = *arrow_location; 95 *arrow_location = 96 vertical ? BubbleBorder::vertical_mirror(*arrow_location) : 97 BubbleBorder::horizontal_mirror(*arrow_location); 98 99 // Change the arrow and get the new bounds. 100 bubble_border_->set_arrow_location(*arrow_location); 101 *window_bounds = bubble_border_->GetBounds(position_relative_to, 102 local_contents_size); 103 gfx::Insets new_offscreen_insets; 104 // If there is more of the window offscreen, we'll keep the old arrow. 105 if (ComputeOffScreenInsets(monitor_bounds, *window_bounds, 106 &new_offscreen_insets) && 107 GetInsetsLength(new_offscreen_insets, vertical) >= 108 GetInsetsLength(offscreen_insets, vertical)) { 109 *arrow_location = original_arrow_location; 110 bubble_border_->set_arrow_location(*arrow_location); 111 *window_bounds = bubble_border_->GetBounds(position_relative_to, 112 local_contents_size); 113 } 114 } 115 } 116 117 // static 118 bool BorderContents::ComputeOffScreenInsets(const gfx::Rect& monitor_bounds, 119 const gfx::Rect& window_bounds, 120 gfx::Insets* offscreen_insets) { 121 if (monitor_bounds.Contains(window_bounds)) 122 return false; 123 124 if (!offscreen_insets) 125 return true; 126 127 // window_bounds 128 // +-------------------------------+ 129 // | top | 130 // | +----------------+ | 131 // | left | monitor_bounds | right | 132 // | +----------------+ | 133 // | bottom | 134 // +-------------------------------+ 135 int top = std::max(0, monitor_bounds.y() - window_bounds.y()); 136 int left = std::max(0, monitor_bounds.x() - window_bounds.x()); 137 int bottom = std::max(0, window_bounds.bottom() - monitor_bounds.bottom()); 138 int right = std::max(0, window_bounds.right() - monitor_bounds.right()); 139 140 offscreen_insets->Set(top, left, bottom, right); 141 return true; 142 } 143 144 // static 145 int BorderContents::GetInsetsLength(const gfx::Insets& insets, bool vertical) { 146 return vertical ? insets.height() : insets.width(); 147 } 148