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 "chrome/browser/chromeos/views/menu_locator.h" | |
6 | |
7 #include "base/i18n/rtl.h" | |
8 #include "base/logging.h" | |
9 #include "chrome/browser/chromeos/views/webui_menu_widget.h" | |
10 #include "ui/gfx/insets.h" | |
11 #include "ui/gfx/point.h" | |
12 #include "ui/gfx/rect.h" | |
13 #include "views/screen.h" | |
14 #include "views/widget/widget.h" | |
15 | |
16 namespace { | |
17 | |
18 using chromeos::WebUIMenuWidget; | |
19 | |
20 // Menu's corner radious. | |
21 const int kMenuCornerRadius = 0; // crosbug.com/7718. | |
22 const int kSubmenuOverlapPx = 1; | |
23 | |
24 gfx::Rect GetBoundsOf(const views::Widget* widget) { | |
25 return widget->GetClientAreaScreenBounds(); | |
26 } | |
27 | |
28 // Returns the Rect of the screen that contains the point (x, y). | |
29 gfx::Rect GetScreenRectAt(int x, int y) { | |
30 return views::Screen::GetMonitorAreaNearestPoint(gfx::Point(x, y)); | |
31 } | |
32 | |
33 // Returns adjusted height of the menu that fits to the screen's | |
34 // height, and enables scrolling if necessary. | |
35 int AdjustHeight(WebUIMenuWidget* widget, | |
36 int screen_height, | |
37 int height) { | |
38 // TODO(oshima): Locator needs a preferred size so that | |
39 // 1) we can tell height == screen_rect is the result of | |
40 // locator resizing it, or preferred size happens to be | |
41 // same hight of the screen (which is rare). | |
42 // 2) when the menu is moved to place where it has more space, it can | |
43 // hide the scrollbar again. (which won't happen on chromeos now) | |
44 if (height >= screen_height) { | |
45 widget->EnableScroll(true); | |
46 return screen_height; | |
47 } | |
48 widget->EnableScroll(false); | |
49 return height; | |
50 } | |
51 | |
52 // Updates the root menu's bounds to fit to the screen. | |
53 void UpdateRootMenuBounds(WebUIMenuWidget* widget, | |
54 const gfx::Point& origin, | |
55 const gfx::Size& size, | |
56 bool align_right) { | |
57 gfx::Rect screen_rect = GetScreenRectAt(origin.x(), origin.y()); | |
58 int width = std::min(screen_rect.width(), size.width()); | |
59 int height = AdjustHeight(widget, screen_rect.height(), size.height()); | |
60 | |
61 int x = align_right ? origin.x() - width : origin.x(); | |
62 int y = origin.y(); | |
63 if (x + width > screen_rect.right()) | |
64 x = screen_rect.right() - width; | |
65 if (y + height > screen_rect.bottom()) | |
66 y = screen_rect.bottom() - height; | |
67 widget->SetBounds(gfx::Rect(x, y, width, height)); | |
68 } | |
69 | |
70 // MenuLocator for dropdown menu. | |
71 class DropDownMenuLocator : public chromeos::MenuLocator { | |
72 public: | |
73 explicit DropDownMenuLocator(const gfx::Point& origin) | |
74 : origin_(origin) { | |
75 } | |
76 | |
77 private: | |
78 virtual SubmenuDirection GetSubmenuDirection() const { | |
79 return DEFAULT; | |
80 } | |
81 | |
82 virtual void Move(WebUIMenuWidget* widget) { | |
83 // TODO(oshima): | |
84 // Dropdown Menu has to be shown above the button, which is not currently | |
85 // possible with Menu2. I'll update Menu2 and this code | |
86 // after beta. | |
87 gfx::Rect bounds = widget->GetClientAreaScreenBounds(); | |
88 UpdateRootMenuBounds(widget, origin_, bounds.size(), !base::i18n::IsRTL()); | |
89 } | |
90 | |
91 virtual void SetBounds(WebUIMenuWidget* widget, const gfx::Size& size) { | |
92 gfx::Size new_size(size); | |
93 new_size.Enlarge(0, kMenuCornerRadius); | |
94 UpdateRootMenuBounds(widget, origin_, size, !base::i18n::IsRTL()); | |
95 } | |
96 | |
97 virtual void GetInsets(gfx::Insets* insets) const { | |
98 insets->Set(0, 0, kMenuCornerRadius, 0); | |
99 } | |
100 | |
101 virtual const SkScalar* GetCorners() const { | |
102 static const SkScalar corners[] = { | |
103 0, 0, | |
104 0, 0, | |
105 kMenuCornerRadius, kMenuCornerRadius, | |
106 kMenuCornerRadius, kMenuCornerRadius, | |
107 }; | |
108 return corners; | |
109 } | |
110 | |
111 gfx::Point origin_; | |
112 | |
113 DISALLOW_COPY_AND_ASSIGN(DropDownMenuLocator); | |
114 }; | |
115 | |
116 // MenuLocator for context menu. | |
117 class ContextMenuLocator : public chromeos::MenuLocator { | |
118 public: | |
119 explicit ContextMenuLocator(const gfx::Point& origin) | |
120 : origin_(origin) { | |
121 } | |
122 | |
123 private: | |
124 virtual SubmenuDirection GetSubmenuDirection() const { | |
125 return DEFAULT; | |
126 } | |
127 | |
128 virtual void Move(WebUIMenuWidget* widget) { | |
129 gfx::Rect bounds = widget->GetClientAreaScreenBounds(); | |
130 UpdateRootMenuBounds(widget, origin_, bounds.size(), base::i18n::IsRTL()); | |
131 } | |
132 | |
133 virtual void SetBounds(WebUIMenuWidget* widget, const gfx::Size& size) { | |
134 gfx::Size new_size(size); | |
135 new_size.Enlarge(0, kMenuCornerRadius * 2); | |
136 UpdateRootMenuBounds(widget, origin_, new_size, base::i18n::IsRTL()); | |
137 } | |
138 | |
139 virtual const SkScalar* GetCorners() const { | |
140 static const SkScalar corners[] = { | |
141 kMenuCornerRadius, kMenuCornerRadius, | |
142 kMenuCornerRadius, kMenuCornerRadius, | |
143 kMenuCornerRadius, kMenuCornerRadius, | |
144 kMenuCornerRadius, kMenuCornerRadius, | |
145 }; | |
146 return corners; | |
147 } | |
148 | |
149 virtual void GetInsets(gfx::Insets* insets) const { | |
150 insets->Set(kMenuCornerRadius, 0, kMenuCornerRadius, 0); | |
151 } | |
152 | |
153 gfx::Point origin_; | |
154 | |
155 DISALLOW_COPY_AND_ASSIGN(ContextMenuLocator); | |
156 }; | |
157 | |
158 // MenuLocator for submenu. | |
159 class SubMenuLocator : public chromeos::MenuLocator { | |
160 public: | |
161 SubMenuLocator(const WebUIMenuWidget* parent, | |
162 MenuLocator::SubmenuDirection parent_direction, | |
163 int y) | |
164 : parent_rect_(GetBoundsOf(parent)), | |
165 parent_direction_(parent_direction), | |
166 root_y_(parent_rect_.y() + y), | |
167 corners_(NULL), | |
168 direction_(DEFAULT) { | |
169 } | |
170 | |
171 private: | |
172 virtual SubmenuDirection GetSubmenuDirection() const { | |
173 return direction_; | |
174 } | |
175 | |
176 virtual void Move(WebUIMenuWidget* widget) { | |
177 gfx::Rect bounds = widget->GetClientAreaScreenBounds(); | |
178 UpdateBounds(widget, bounds.size()); | |
179 } | |
180 | |
181 virtual void SetBounds(WebUIMenuWidget* widget, const gfx::Size& size) { | |
182 gfx::Size new_size(size); | |
183 new_size.Enlarge(0, kMenuCornerRadius * 2); | |
184 UpdateBounds(widget, new_size); | |
185 } | |
186 | |
187 virtual const SkScalar* GetCorners() const { | |
188 return corners_; | |
189 } | |
190 | |
191 virtual void GetInsets(gfx::Insets* insets) const { | |
192 insets->Set(kMenuCornerRadius, 0, kMenuCornerRadius, 0); | |
193 } | |
194 | |
195 // Rounded corner definitions for right/left attached submenu. | |
196 static const SkScalar kRightCorners[]; | |
197 static const SkScalar kLeftCorners[]; | |
198 | |
199 void UpdateBounds(WebUIMenuWidget* widget, const gfx::Size& size) { | |
200 gfx::Rect screen_rect = GetScreenRectAt(parent_rect_.x(), root_y_); | |
201 int width = std::min(screen_rect.width(), size.width()); | |
202 int height = AdjustHeight(widget, size.height(), screen_rect.height()); | |
203 | |
204 SubmenuDirection direction = parent_direction_; | |
205 if (direction == DEFAULT) { | |
206 if (base::i18n::IsRTL()) { | |
207 direction = LEFT; | |
208 } else { | |
209 direction = RIGHT; | |
210 } | |
211 } | |
212 // Adjust Y to fit the screen. | |
213 int y = root_y_; | |
214 if (root_y_ + height > screen_rect.bottom()) | |
215 y = screen_rect.bottom() - height; | |
216 // Determine the attachment. | |
217 // TODO(oshima): | |
218 // Come up with better placement when menu is wide, | |
219 // probably limit max width and let each menu scroll | |
220 // horizontally when selected. | |
221 int x = direction == RIGHT ? | |
222 ComputeXToRight(screen_rect, width) : | |
223 ComputeXToLeft(screen_rect, width); | |
224 corners_ = direction_ == RIGHT ? kRightCorners : kLeftCorners; | |
225 widget->SetBounds(gfx::Rect(x, y, width, height)); | |
226 } | |
227 | |
228 int ComputeXToRight(const gfx::Rect& screen_rect, int width) { | |
229 if (parent_rect_.right() + width > screen_rect.right()) { | |
230 if (parent_rect_.x() - width < screen_rect.x()) { | |
231 // Place on the right to fit to the screen if no space on left | |
232 direction_ = RIGHT; | |
233 return screen_rect.right() - width; | |
234 } | |
235 direction_ = LEFT; | |
236 return parent_rect_.x() - width + kSubmenuOverlapPx; | |
237 } else { | |
238 direction_ = RIGHT; | |
239 return parent_rect_.right() - kSubmenuOverlapPx; | |
240 } | |
241 } | |
242 | |
243 int ComputeXToLeft(const gfx::Rect& screen_rect, int width) { | |
244 if (parent_rect_.x() - width < screen_rect.x()) { | |
245 if (parent_rect_.right() + width > screen_rect.right()) { | |
246 // no space on right | |
247 direction_ = LEFT; | |
248 return screen_rect.x(); | |
249 } | |
250 corners_ = kRightCorners; | |
251 direction_ = RIGHT; | |
252 return parent_rect_.right() - kSubmenuOverlapPx; | |
253 } else { | |
254 corners_ = kLeftCorners; | |
255 direction_ = LEFT; | |
256 return parent_rect_.x() - width + kSubmenuOverlapPx; | |
257 } | |
258 } | |
259 | |
260 const gfx::Rect parent_rect_; | |
261 | |
262 const MenuLocator::SubmenuDirection parent_direction_; | |
263 | |
264 const int root_y_; | |
265 | |
266 SkScalar const* corners_; | |
267 | |
268 // The direction the this menu is attached to its parent. Submenu may still | |
269 // choose different direction if there is no spece for that direction | |
270 // (2nd turnaround). | |
271 SubmenuDirection direction_; | |
272 | |
273 DISALLOW_COPY_AND_ASSIGN(SubMenuLocator); | |
274 }; | |
275 | |
276 // Rounded corners of the submenu attached to right side. | |
277 const SkScalar SubMenuLocator::kRightCorners[] = { | |
278 0, 0, | |
279 kMenuCornerRadius, kMenuCornerRadius, | |
280 kMenuCornerRadius, kMenuCornerRadius, | |
281 kMenuCornerRadius, kMenuCornerRadius, | |
282 }; | |
283 | |
284 // Rounded corners of the submenu attached to left side. | |
285 const SkScalar SubMenuLocator::kLeftCorners[] = { | |
286 kMenuCornerRadius, kMenuCornerRadius, | |
287 0, 0, | |
288 kMenuCornerRadius, kMenuCornerRadius, | |
289 kMenuCornerRadius, kMenuCornerRadius, | |
290 }; | |
291 | |
292 | |
293 } // namespace | |
294 | |
295 namespace chromeos { | |
296 | |
297 // static | |
298 MenuLocator* MenuLocator::CreateDropDownMenuLocator(const gfx::Point& p) { | |
299 return new DropDownMenuLocator(p); | |
300 } | |
301 | |
302 MenuLocator* MenuLocator::CreateContextMenuLocator(const gfx::Point& p) { | |
303 return new ContextMenuLocator(p); | |
304 } | |
305 | |
306 MenuLocator* MenuLocator::CreateSubMenuLocator( | |
307 const WebUIMenuWidget* parent, | |
308 MenuLocator::SubmenuDirection parent_direction, | |
309 int y) { | |
310 return new SubMenuLocator(parent, parent_direction, y); | |
311 } | |
312 | |
313 } // namespace chromeos | |
OLD | NEW |