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/bubble/border_contents_view.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "ui/gfx/screen.h" | |
10 | |
11 static const int kTopMargin = 6; | |
12 static const int kLeftMargin = 6; | |
13 static const int kBottomMargin = 6; | |
14 static const int kRightMargin = 6; | |
15 | |
16 namespace { | |
17 | |
18 // Computes how much |window_bounds| is off-screen of the monitor bounds | |
19 // |monitor_bounds| and puts the values in |offscreen_insets|. | |
20 // Returns false if |window_bounds| is actually contained in |monitor_bounds|, | |
21 // in which case |offscreen_insets| is not modified. | |
22 bool ComputeOffScreenInsets(const gfx::Rect& monitor_bounds, | |
23 const gfx::Rect& window_bounds, | |
24 gfx::Insets* offscreen_insets) { | |
25 if (monitor_bounds.Contains(window_bounds)) | |
26 return false; | |
27 | |
28 if (!offscreen_insets) | |
29 return true; | |
30 | |
31 // window_bounds | |
32 // +-------------------------------+ | |
33 // | top | | |
34 // | +----------------+ | | |
35 // | left | monitor_bounds | right | | |
36 // | +----------------+ | | |
37 // | bottom | | |
38 // +-------------------------------+ | |
39 int top = std::max(0, monitor_bounds.y() - window_bounds.y()); | |
40 int left = std::max(0, monitor_bounds.x() - window_bounds.x()); | |
41 int bottom = std::max(0, window_bounds.bottom() - monitor_bounds.bottom()); | |
42 int right = std::max(0, window_bounds.right() - monitor_bounds.right()); | |
43 | |
44 offscreen_insets->Set(top, left, bottom, right); | |
45 return true; | |
46 } | |
47 | |
48 // Convenience method that returns the height of |insets| if |vertical| is | |
49 // true, its width otherwise. | |
50 int GetInsetsLength(const gfx::Insets& insets, bool vertical) { | |
51 return vertical ? insets.height() : insets.width(); | |
52 } | |
53 | |
54 } // namespace | |
55 | |
56 namespace views { | |
57 | |
58 BorderContentsView::BorderContentsView() | |
59 : bubble_border_(NULL), | |
60 content_margins_(kTopMargin, kLeftMargin, kBottomMargin, kRightMargin) { | |
61 } | |
62 | |
63 BorderContentsView::BorderContentsView(int top_margin, | |
64 int left_margin, | |
65 int bottom_margin, | |
66 int right_margin) | |
67 : bubble_border_(NULL), | |
68 content_margins_(top_margin, left_margin, bottom_margin, right_margin) { | |
69 } | |
70 | |
71 BorderContentsView::~BorderContentsView() {} | |
72 | |
73 void BorderContentsView::Init() { | |
74 // Default arrow location. | |
75 BubbleBorder::ArrowLocation arrow_location = | |
76 BubbleBorder::TOP_LEFT; | |
77 if (base::i18n::IsRTL()) | |
78 arrow_location = BubbleBorder::horizontal_mirror(arrow_location); | |
79 DCHECK(!bubble_border_); | |
80 | |
81 // TODO(alicet): Expose the shadow option in BorderContentsView when we make | |
82 // the fullscreen exit bubble use the new bubble code. | |
83 bubble_border_ = new BubbleBorder(arrow_location, | |
84 views::BubbleBorder::NO_SHADOW); | |
85 set_border(bubble_border_); | |
86 set_background(new BubbleBackground(bubble_border_)); | |
87 } | |
88 | |
89 void BorderContentsView::SetBackgroundColor(SkColor color) { | |
90 bubble_border_->set_background_color(color); | |
91 } | |
92 | |
93 void BorderContentsView::SetAlignment( | |
94 views::BubbleBorder::BubbleAlignment alignment) { | |
95 bubble_border_->set_alignment(alignment); | |
96 } | |
97 | |
98 void BorderContentsView::SizeAndGetBounds( | |
99 const gfx::Rect& position_relative_to, | |
100 BubbleBorder::ArrowLocation arrow_location, | |
101 bool allow_bubble_offscreen, | |
102 const gfx::Size& contents_size, | |
103 gfx::Rect* contents_bounds, | |
104 gfx::Rect* window_bounds) { | |
105 if (base::i18n::IsRTL()) | |
106 arrow_location = BubbleBorder::horizontal_mirror(arrow_location); | |
107 bubble_border_->set_arrow_location(arrow_location); | |
108 // Set the border. | |
109 set_border(bubble_border_); | |
110 | |
111 // Give the contents a margin. | |
112 gfx::Size local_contents_size(contents_size); | |
113 local_contents_size.Enlarge(content_margins_.width(), | |
114 content_margins_.height()); | |
115 | |
116 // Try putting the arrow in its initial location, and calculating the bounds. | |
117 *window_bounds = | |
118 bubble_border_->GetBounds(position_relative_to, local_contents_size); | |
119 if (!allow_bubble_offscreen) { | |
120 gfx::Rect monitor_bounds = GetMonitorBounds(position_relative_to); | |
121 if (!monitor_bounds.IsEmpty()) { | |
122 // Try to resize vertically if this does not fit on the screen. | |
123 MirrorArrowIfOffScreen(true, // |vertical|. | |
124 position_relative_to, monitor_bounds, | |
125 local_contents_size, &arrow_location, | |
126 window_bounds); | |
127 // Then try to resize horizontally if it still does not fit on the screen. | |
128 MirrorArrowIfOffScreen(false, // |vertical|. | |
129 position_relative_to, monitor_bounds, | |
130 local_contents_size, &arrow_location, | |
131 window_bounds); | |
132 } | |
133 } | |
134 | |
135 // Calculate the bounds of the contained contents (in window coordinates) by | |
136 // subtracting the border dimensions and margin amounts. | |
137 *contents_bounds = gfx::Rect(gfx::Point(), window_bounds->size()); | |
138 gfx::Insets insets; | |
139 bubble_border_->GetInsets(&insets); | |
140 insets += content_margins_; | |
141 contents_bounds->Inset(insets); | |
142 } | |
143 | |
144 gfx::Rect BorderContentsView::GetMonitorBounds(const gfx::Rect& rect) { | |
145 return gfx::Screen::GetMonitorWorkAreaNearestPoint(rect.CenterPoint()); | |
146 } | |
147 | |
148 void BorderContentsView::MirrorArrowIfOffScreen( | |
149 bool vertical, | |
150 const gfx::Rect& position_relative_to, | |
151 const gfx::Rect& monitor_bounds, | |
152 const gfx::Size& local_contents_size, | |
153 BubbleBorder::ArrowLocation* arrow_location, | |
154 gfx::Rect* window_bounds) { | |
155 // If the bounds don't fit, move the arrow to its mirrored position to see if | |
156 // it improves things. | |
157 gfx::Insets offscreen_insets; | |
158 if (ComputeOffScreenInsets(monitor_bounds, *window_bounds, | |
159 &offscreen_insets) && | |
160 GetInsetsLength(offscreen_insets, vertical) > 0) { | |
161 BubbleBorder::ArrowLocation original_arrow_location = | |
162 *arrow_location; | |
163 *arrow_location = | |
164 vertical ? BubbleBorder::vertical_mirror(*arrow_location) : | |
165 BubbleBorder::horizontal_mirror(*arrow_location); | |
166 | |
167 // Change the arrow and get the new bounds. | |
168 bubble_border_->set_arrow_location(*arrow_location); | |
169 *window_bounds = bubble_border_->GetBounds(position_relative_to, | |
170 local_contents_size); | |
171 gfx::Insets new_offscreen_insets; | |
172 // If there is more of the window offscreen, we'll keep the old arrow. | |
173 if (ComputeOffScreenInsets(monitor_bounds, *window_bounds, | |
174 &new_offscreen_insets) && | |
175 GetInsetsLength(new_offscreen_insets, vertical) >= | |
176 GetInsetsLength(offscreen_insets, vertical)) { | |
177 *arrow_location = original_arrow_location; | |
178 bubble_border_->set_arrow_location(*arrow_location); | |
179 *window_bounds = bubble_border_->GetBounds(position_relative_to, | |
180 local_contents_size); | |
181 } | |
182 } | |
183 } | |
184 | |
185 } // namespace views | |
OLD | NEW |