| 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 |