• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Init()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 
SetBackgroundColor(SkColor color)25 void BorderContents::SetBackgroundColor(SkColor color) {
26   bubble_border_->set_background_color(color);
27 }
28 
SizeAndGetBounds(const gfx::Rect & position_relative_to,BubbleBorder::ArrowLocation arrow_location,bool allow_bubble_offscreen,const gfx::Size & contents_size,gfx::Rect * contents_bounds,gfx::Rect * window_bounds)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 
GetMonitorBounds(const gfx::Rect & rect)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 
MirrorArrowIfOffScreen(bool vertical,const gfx::Rect & position_relative_to,const gfx::Rect & monitor_bounds,const gfx::Size & local_contents_size,BubbleBorder::ArrowLocation * arrow_location,gfx::Rect * window_bounds)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
ComputeOffScreenInsets(const gfx::Rect & monitor_bounds,const gfx::Rect & window_bounds,gfx::Insets * offscreen_insets)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
GetInsetsLength(const gfx::Insets & insets,bool vertical)145 int BorderContents::GetInsetsLength(const gfx::Insets& insets, bool vertical) {
146   return vertical ? insets.height() : insets.width();
147 }
148