Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(440)

Side by Side Diff: ash/wm/window_maximize.cc

Issue 10823025: Adding new maximize menu according to spec (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Fixed merging issues Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2012 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 // Needs to be included first to get the enum resolved.
6 #include "ash/wm/workspace/frame_maximize_button.h"
7 #include "ash/wm/window_maximize.h"
8
9 #include "ash/shell.h"
10 #include "ash/shell_window_ids.h"
11 #include "ash/wm/window_animations.h"
12 #include "base/bind.h"
13 #include "base/command_line.h"
14 #include "base/timer.h"
15 #include "grit/ash_strings.h"
16 #include "grit/ui_resources.h"
17 #include "ui/aura/aura_switches.h"
18 #include "ui/base/ui_base_switches.h"
19 #include "ui/aura/event.h"
20 #include "ui/aura/window.h"
21 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/base/resource/resource_bundle.h"
23 #include "ui/gfx/canvas.h"
24 #include "ui/gfx/screen.h"
25 #include "ui/views/bubble/bubble_delegate.h"
26 #include "ui/views/bubble/bubble_frame_view.h"
27 #include "ui/views/controls/button/button.h"
28 #include "ui/views/controls/button/custom_button.h"
29 #include "ui/views/controls/label.h"
30 #include "ui/views/events/event.h"
31 #include "ui/views/layout/box_layout.h"
32 #include "ui/views/radial_menu/radial_menu_views.h"
33
34 namespace {
35
36 // The command codes returned from the radial menu.
37 enum RadialMenuCommands {
38 RADIAL_MENU_NONE = 0,
39 RADIAL_MENU_RIGHT,
40 RADIAL_MENU_MINIMIZE,
41 RADIAL_MENU_LEFT
42 };
43
44 // Bubble constants
45 const int kMaximumBubbleWidth = 200;
46 const int kArrowOffset = 10;
47
48 // The spacing between two buttons.
49 const int kLayoutSpacing = 1;
50
51 const int kAnimationDurationForPopupMS = 200;
52
53 // The background color
54 const SkColor kBubbleBackgroundColor = 0xf2141414;
55
56 // The text color within the bubble
57 const SkColor kBubbleTextColor = 0xffffffff;
58
59 // The line width of the bubble.
60 const int kLineHeight = 1;
61
62 // The pixel dimensions of the arrow
63 const int kArrowHeight = 10;
64 const int kArrowWidth = 20;
65
66 // The delay of the bubble appearance.
67 const int kBubbleAppearanceDelay = 200; // msec
68
69 // The active area behind a segment in a radial menu (in segment widths):
70 // In case of hover the user wants to cancel out when getting reasonably far
71 // away from the menu.
72 const int kHoverRadialMenuExtension = 3;
73 // In case of dragging the menu extends till eternity.
74 const int kDragRadialMenuExtension = 10000;
75
76 class MaximizeBubbleBorder : public views::BubbleBorder {
77 public:
78 MaximizeBubbleBorder(views::View* owner,
79 views::View* anchor)
80 : views::BubbleBorder(views::BubbleBorder::TOP_RIGHT,
81 views::BubbleBorder::NO_SHADOW),
82 owner_(owner),
83 anchor_(anchor) {
84 set_alignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE);
85 }
86
87 virtual ~MaximizeBubbleBorder() {}
88
89 // Overridden from views::BubbleBorder to match the design specs.
90 virtual gfx::Rect GetBounds(const gfx::Rect& position_relative_to,
91 const gfx::Size& contents_size) const OVERRIDE {
92 gfx::Size border_size(contents_size);
93 gfx::Insets insets;
94 GetInsets(&insets);
95 border_size.Enlarge(insets.width(), insets.height());
96
97 // Position the bubble to center the box on the anchor.
98 int x = -insets.left() -
99 (border_size.width() - anchor_->width() - kArrowWidth) / 2;
100 // Position the bubble under the anchor, overlapping the arrow with it.
101 int y = anchor_->height() - insets.top();
102
103 gfx::Point view_topleft(x, y);
104 views::View::ConvertPointToScreen(anchor_, &view_topleft);
105
106 return gfx::Rect(view_topleft.x(), view_topleft.y(),
107 border_size.width(), border_size.height());
108 }
109
110 // Overridden from views::Border
111 virtual void Paint(const views::View& view,
112 gfx::Canvas* canvas) const OVERRIDE {
113 gfx::Insets inset;
114 GetInsets(&inset);
115
116 // Draw the border line around everything.
117 int y = inset.top();
118 // Top
119 canvas->FillRect(gfx::Rect(inset.left(),
120 y - kLineHeight,
121 owner_->width(),
122 kLineHeight),
123 kBubbleBackgroundColor);
124 // Bottom
125 canvas->FillRect(gfx::Rect(inset.left(),
126 y + owner_->height(),
127 owner_->width(),
128 kLineHeight),
129 kBubbleBackgroundColor);
130 // Left
131 canvas->FillRect(gfx::Rect(inset.left() - kLineHeight,
132 y - kLineHeight,
133 kLineHeight,
134 owner_->height() + 2 * kLineHeight),
135 kBubbleBackgroundColor);
136 // Right
137 canvas->FillRect(gfx::Rect(inset.left() + owner_->width(),
138 y - kLineHeight,
139 kLineHeight,
140 owner_->height() + 2 * kLineHeight),
141 kBubbleBackgroundColor);
142
143 // Draw the arrow afterwards covering the border.
144 SkPath path;
145 path.incReserve(4);
146 // The center of the tip should be in the middle of the button.
147 int tip_x = inset.left() + owner_->width() / 2;
148 int left_base_x = tip_x - kArrowWidth / 2;
149 int left_base_y = y;
150 int tip_y = left_base_y - kArrowHeight;
151 path.moveTo(SkIntToScalar(left_base_x), SkIntToScalar(left_base_y));
152 path.lineTo(SkIntToScalar(tip_x), SkIntToScalar(tip_y));
153 path.lineTo(SkIntToScalar(left_base_x + kArrowWidth),
154 SkIntToScalar(left_base_y));
155
156 SkPaint paint;
157 paint.setStyle(SkPaint::kFill_Style);
158 paint.setColor(kBubbleBackgroundColor);
159 canvas->DrawPath(path, paint);
160 }
161
162 private:
163 views::View* owner_;
164 views::View* anchor_;
165
166 DISALLOW_COPY_AND_ASSIGN(MaximizeBubbleBorder);
167 };
168
169 } // namespace
170
171 namespace ash {
172
173 // The image button gets overridden to be able to capture mouse hover events.
174 class MaximizeBubble::BubbleMenuButton : public views::ImageButton {
175 public:
176 explicit BubbleMenuButton(MaximizeBubble::BubbleContentsButtonRow* listener);
177 virtual ~BubbleMenuButton() {};
178
179 // CustomButton overrides:
180 virtual void OnMouseEntered(const views::MouseEvent& event) OVERRIDE;
181 virtual void OnMouseExited(const views::MouseEvent& event) OVERRIDE;
182
183 private:
184 // The creating class which needs to get notified in case of a hover event.
185 MaximizeBubble::BubbleContentsButtonRow* owner_;
186
187 DISALLOW_COPY_AND_ASSIGN(BubbleMenuButton);
188 };
189
190 // A class that creates all buttons and put them into a view.
191 class MaximizeBubble::BubbleContentsButtonRow : public views::View,
192 public views::ButtonListener {
193 public:
194 explicit BubbleContentsButtonRow(MaximizeBubble* bubble)
195 : bubble_(bubble),
196 left_button_(NULL),
197 minimize_button_(NULL),
198 right_button_(NULL) {
199 SetLayoutManager(new views::BoxLayout(
200 views::BoxLayout::kHorizontal, 0, 0, kLayoutSpacing));
201 set_background(
202 views::Background::CreateSolidBackground(kBubbleBackgroundColor));
203
204 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
205 left_button_ = new MaximizeBubble::BubbleMenuButton(this);
206 left_button_->SetImage(views::CustomButton::BS_NORMAL,
207 // TODO(skuhne): Replace images as soon as they come in.
208 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_SNAP_LEFT_P));
209 left_button_->SetImage(views::CustomButton::BS_HOT,
210 // TODO(skuhne): Replace images as soon as they come in.
211 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_SNAP_LEFT_P));
212 left_button_->SetImage(views::CustomButton::BS_PUSHED,
213 // TODO(skuhne): Replace images as soon as they come in.
214 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_SNAP_LEFT_P));
215 AddChildView(left_button_);
216
217 minimize_button_ = new MaximizeBubble::BubbleMenuButton(this);
218 minimize_button_->SetImage(views::CustomButton::BS_NORMAL,
219 // TODO(skuhne): Replace images as soon as they come in.
220 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_SNAP_P));
221 minimize_button_->SetImage(views::CustomButton::BS_HOT,
222 // TODO(skuhne): Replace images as soon as they come in.
223 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_SNAP_P));
224 minimize_button_->SetImage(views::CustomButton::BS_PUSHED,
225 // TODO(skuhne): Replace images as soon as they come in.
226 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_SNAP_P));
227 AddChildView(minimize_button_);
228
229 right_button_ = new MaximizeBubble::BubbleMenuButton(this);
230 right_button_->SetImage(views::CustomButton::BS_NORMAL,
231 // TODO(skuhne): Replace images as soon as they come in.
232 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_SNAP_RIGHT_P));
233 right_button_->SetImage(views::CustomButton::BS_HOT,
234 // TODO(skuhne): Replace images as soon as they come in.
235 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_SNAP_RIGHT_P));
236 right_button_->SetImage(views::CustomButton::BS_PUSHED,
237 // TODO(skuhne): Replace images as soon as they come in.
238 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_SNAP_RIGHT_P));
239 AddChildView(right_button_);
240 }
241
242 virtual ~BubbleContentsButtonRow() {}
243
244 // Overridden from ButtonListener.
245 virtual void ButtonPressed(views::Button* sender,
246 const views::Event& event) OVERRIDE {
247 if (sender == left_button_)
248 bubble_->OnButtonClicked(FrameMaximizeButton::SNAP_LEFT);
249 if (sender == minimize_button_)
250 bubble_->OnButtonClicked(FrameMaximizeButton::SNAP_MINIMIZE);
251 if (sender == right_button_)
252 bubble_->OnButtonClicked(FrameMaximizeButton::SNAP_RIGHT);
253 }
254
255 // Called from BubbleMenuButton.
256 void ButtonHovered(MaximizeBubble::BubbleMenuButton* sender) {
257 if (sender == left_button_)
258 bubble_->OnButtonHover(FrameMaximizeButton::SNAP_LEFT);
259 else if (sender == minimize_button_)
260 bubble_->OnButtonHover(FrameMaximizeButton::SNAP_MINIMIZE);
261 else if (sender == right_button_)
262 bubble_->OnButtonHover(FrameMaximizeButton::SNAP_RIGHT);
263 else
264 bubble_->OnButtonHover(FrameMaximizeButton::SNAP_NONE);
265 }
266
267 private:
268 // The owning object which gets notifications.
269 MaximizeBubble* bubble_;
270
271 // The created buttons for our menu.
272 MaximizeBubble::BubbleMenuButton* left_button_;
273 MaximizeBubble::BubbleMenuButton* minimize_button_;
274 MaximizeBubble::BubbleMenuButton* right_button_;
275
276 DISALLOW_COPY_AND_ASSIGN(BubbleContentsButtonRow);
277 };
278
279 MaximizeBubble::BubbleMenuButton::BubbleMenuButton(
280 MaximizeBubble::BubbleContentsButtonRow* listener)
281 : views::ImageButton(listener),
282 owner_(listener) {}
283
284 void MaximizeBubble::BubbleMenuButton::OnMouseEntered(
285 const views::MouseEvent& event) OVERRIDE {
286 owner_->ButtonHovered(this);
287 views::ImageButton::OnMouseEntered(event);
288 }
289
290 void MaximizeBubble::BubbleMenuButton::OnMouseExited(
291 const views::MouseEvent& event) OVERRIDE {
292 owner_->ButtonHovered(NULL);
293 views::ImageButton::OnMouseExited(event);
294 }
295
296 // A class which creates the content of the bubble: The buttons, and the label.
297 class MaximizeBubble::BubbleContentsView : public views::View {
298 public:
299 explicit BubbleContentsView(MaximizeBubble* bubble)
300 : bubble_(bubble) {
301 SetLayoutManager(new views::BoxLayout(
302 views::BoxLayout::kVertical, 0, 0, kLayoutSpacing));
303 set_background(
304 views::Background::CreateSolidBackground(kBubbleBackgroundColor));
305
306 buttons_view_ = new BubbleContentsButtonRow(bubble);
307 AddChildView(buttons_view_);
308
309 label_view_ = new views::Label();
310 SetMenuState(FrameMaximizeButton::SNAP_NONE);
311 label_view_->SetHorizontalAlignment(views::Label::ALIGN_CENTER);
312 label_view_->SetBackgroundColor(kBubbleBackgroundColor);
313 label_view_->SetEnabledColor(kBubbleTextColor);
314 AddChildView(label_view_);
315 }
316
317 virtual ~BubbleContentsView() {}
318
319 // Set the label content to reflect the currently selected |snap_type|.
320 // This function can be executed through the frame maximize button as well as
321 // through hover operations.
322 void SetMenuState(FrameMaximizeButton::SnapType snap_type) {
323 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
324 switch (snap_type) {
325 case FrameMaximizeButton::SNAP_LEFT:
326 label_view_->SetText(rb.GetLocalizedString(IDS_ASH_SNAP_WINDOW_LEFT));
327 return;
328 case FrameMaximizeButton::SNAP_RIGHT:
329 label_view_->SetText(rb.GetLocalizedString(IDS_ASH_SNAP_WINDOW_RIGHT));
330 return;
331 case FrameMaximizeButton::SNAP_MAXIMIZE:
332 DCHECK(!bubble_->is_maximized());
333 label_view_->SetText(rb.GetLocalizedString(IDS_ASH_MAXIMIZE_WINDOW));
334 return;
335 case FrameMaximizeButton::SNAP_MINIMIZE:
336 label_view_->SetText(rb.GetLocalizedString(IDS_ASH_MINIMIZE_WINDOW));
337 return;
338 case FrameMaximizeButton::SNAP_RESTORE:
339 DCHECK(bubble_->is_maximized());
340 label_view_->SetText(rb.GetLocalizedString(IDS_ASH_RESTORE_WINDOW));
341 return;
342 default:
343 // If nothing is selected, we automatically select the click operation.
344 label_view_->SetText(rb.GetLocalizedString(
345 bubble_->is_maximized() ? IDS_ASH_RESTORE_WINDOW :
346 IDS_ASH_MAXIMIZE_WINDOW));
347 return;
348 }
349 }
350
351 private:
352 // The owning class.
353 MaximizeBubble* bubble_;
354
355 // The object which owns all the buttons.
356 BubbleContentsButtonRow* buttons_view_;
357
358 // The label object which shows the user the selected action.
359 views::Label* label_view_;
360
361 DISALLOW_COPY_AND_ASSIGN(BubbleContentsView);
362 };
363
364 // The class which creates and manages the bubble menu element.
365 // It creates a "bubble border" and the content accordingly.
366 // Note: Since the SnapSizer will show animations on top of the maximize button
367 // this menu gets creates as a separate window and the SnapSizer will be
368 // created underneath this window.
369 class MaximizeBubble::Bubble : public views::BubbleDelegateView {
370 public:
371 explicit Bubble(MaximizeBubble* bubble)
372 : views::BubbleDelegateView(bubble->frame_maximize_button(),
373 views::BubbleBorder::TOP_RIGHT),
374 bubble_(bubble),
375 bubble_widget_(NULL),
376 contents_view_(NULL) {
377 set_margins(gfx::Insets());
378
379 // The window needs to be owned by the root so that the SnapSizer does not
380 // cover it upon animation.
381 aura::Window* parent = Shell::GetContainer(
382 Shell::GetActiveRootWindow(),
383 internal::kShellWindowId_LauncherContainer);
384 set_parent_window(parent);
385
386 set_notify_enter_exit_on_child(true);
387 set_try_mirroring_arrow(false);
388 SetPaintToLayer(true);
389 set_color(kBubbleBackgroundColor);
390 set_close_on_deactivate(false);
391 set_background(
392 views::Background::CreateSolidBackground(kBubbleBackgroundColor));
393
394 SetLayoutManager(new views::BoxLayout(
395 views::BoxLayout::kVertical, 0, 0, kLayoutSpacing));
396
397 contents_view_ = new BubbleContentsView(bubble);
398 AddChildView(contents_view_);
399
400 // Note that the returned widget has an observer which points to our
401 // functions.
402 bubble_widget_ = views::BubbleDelegateView::CreateBubble(this);
403
404 SetAlignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE);
405 bubble_widget_->non_client_view()->frame_view()->set_background(NULL);
406
407 MaximizeBubbleBorder* bubble_border = new MaximizeBubbleBorder(
408 this, anchor_view());
409 GetBubbleFrameView()->SetBubbleBorder(bubble_border);
410 GetBubbleFrameView()->set_background(NULL);
411
412 // Recalculate size with new border.
413 SizeToContents();
414
415 // Setup animation.
416 ash::SetWindowVisibilityAnimationType(
417 bubble_widget_->GetNativeWindow(),
418 ash::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
419 ash::SetWindowVisibilityAnimationTransition(
420 bubble_widget_->GetNativeWindow(),
421 ash::ANIMATE_BOTH);
422 ash::SetWindowVisibilityAnimationDuration(
423 bubble_widget_->GetNativeWindow(),
424 base::TimeDelta::FromMilliseconds(kAnimationDurationForPopupMS));
425
426 Show();
427 }
428
429 virtual ~Bubble() {
430 if (bubble_widget_) {
431 bubble_widget_->RemoveObserver(this);
432 anchor_widget()->RemoveObserver(this);
433 bubble_widget_->Close();
434 bubble_widget_ = NULL;
435 }
436 }
437
438 // The window of the menu under which the SnapSizer will get created.
439 aura::Window* GetMenuWindow() {
440 return bubble_widget_ ? bubble_widget_->GetNativeWindow() : NULL;
441 }
442
443 // Overridden from views::BubbleDelegateView.
444 virtual gfx::Rect GetAnchorRect() const OVERRIDE {
445 gfx::Rect anchor_rect =
446 bubble_->frame_maximize_button()->GetBoundsInScreen();
447 return anchor_rect;
448 }
449
450 // Overridden from View
451 virtual void OnMouseExited(const views::MouseEvent& event) OVERRIDE {
452 // When we leave the bubble, we might be still be in gesture mode or over
453 // the maximize button. So only close if none of the other cases apply.
454 if (!bubble_->frame_maximize_button()->is_snap_enabled()) {
455 gfx::Point screen_location = gfx::Screen::GetCursorScreenPoint();
456 if (!bubble_->frame_maximize_button()->GetBoundsInScreen().Contains(
457 screen_location))
458 bubble_->Close();
459 }
460 }
461
462 virtual void OnClickedOutsideView() OVERRIDE {
463 // Don't destroy the menu when the click happened on the frame maximize
464 // button.
465 if (!bubble_->frame_maximize_button()->is_snap_enabled())
466 bubble_->Close(); // Will destroy |this|.
467 }
468
469 // Overridden from views::View.
470 virtual gfx::Size GetPreferredSize() OVERRIDE {
471 return contents_view_->GetPreferredSize();
472 }
473
474 // Overridden from views::Widget::Observer.
475 virtual void OnWidgetClosing(views::Widget* widget) OVERRIDE {
476 if (bubble_widget_) {
477 CHECK_EQ(bubble_widget_, widget);
478 bubble_widget_ = NULL;
479 bubble_->Close(); // Will destroy |this|.
480 }
481 }
482
483 // Called from the owning class to indicate that the menu should get
484 // destroyed.
485 virtual void Close() {
486 if (bubble_widget_) {
487 bubble_widget_->Close();
488 bubble_widget_ = NULL;
489 }
490 bubble_->Close(); // Will destroy |this|.
491 }
492
493 // Called from the owning class to change the menu content to the given
494 // |snap_type| so that the user knows what is selected.
495 void SetMenuState(FrameMaximizeButton::SnapType snap_type) {
496 if (contents_view_)
497 contents_view_->SetMenuState(snap_type);
498 }
499
500 private:
501 // Our owning class.
502 MaximizeBubble* bubble_;
503
504 // The widget which contains our menu and the bubble border.
505 views::Widget* bubble_widget_;
506
507 // The content accessor of the menu.
508 BubbleContentsView* contents_view_;
509
510 DISALLOW_COPY_AND_ASSIGN(Bubble);
511 };
512
513 MaximizeBubble::MaximizeBubble(FrameMaximizeButton* frame_maximize_button,
514 bool is_maximized)
515 : frame_maximize_button_(frame_maximize_button),
516 bubble_(NULL),
517 radial_menu_(NULL),
518 current_radial_snap_hover_type_(FrameMaximizeButton::SNAP_NONE),
519 is_maximized_(is_maximized) {
520 // Create the task which will create the bubble delayed.
521 base::OneShotTimer<MaximizeBubble>* new_timer =
522 new base::OneShotTimer<MaximizeBubble>();
523 new_timer->Start(
524 FROM_HERE,
525 base::TimeDelta::FromMilliseconds(kBubbleAppearanceDelay),
526 this,
527 &MaximizeBubble::DelayedBubbleCreation);
528 timer_.reset(new_timer);
529 }
530
531 void MaximizeBubble::DelayCreation() {
532 if (timer_.get() && timer_->IsRunning())
533 timer_->Reset();
534 }
535
536 void MaximizeBubble::DelayedBubbleCreation() {
537 if (!bubble_ && !radial_menu_) {
538 if (CommandLine::ForCurrentProcess()->HasSwitch(
539 switches::kEnableTouchRadialMenu)) {
540 std::vector<views::RadialMenuItem*> items;
541 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
542 DCHECK(RADIAL_MENU_NONE == items.size());
543 items.push_back(views::RadialMenuItem::CreateRadialMenuItemInstance(
544 views::RadialMenuItem::RADIAL_SEPARATOR,
545 NULL,
546 false));
547 DCHECK(RADIAL_MENU_RIGHT == items.size());
548 items.push_back(views::RadialMenuItem::CreateRadialMenuItemInstance(
549 views::RadialMenuItem::RADIAL_BUTTON,
550 // TODO(skuhne): Replace images as soon as they come in.
551 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_SNAP_RIGHT_P),
552 false));
553 DCHECK(RADIAL_MENU_MINIMIZE == items.size());
554 items.push_back(views::RadialMenuItem::CreateRadialMenuItemInstance(
555 views::RadialMenuItem::RADIAL_BUTTON,
556 // TODO(skuhne): Replace images as soon as they come in.
557 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_SNAP_P),
558 false));
559 DCHECK(RADIAL_MENU_LEFT == items.size());
560 items.push_back(views::RadialMenuItem::CreateRadialMenuItemInstance(
561 views::RadialMenuItem::RADIAL_BUTTON,
562 // TODO(skuhne): Replace images as soon as they come in.
563 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_SNAP_LEFT_P),
564 false));
565 gfx::Rect rect = frame_maximize_button()->GetBoundsInScreen();
566 gfx::Point location = rect.CenterPoint();
567 int radius = (rect.width() < rect.height() ? rect.width() :
568 rect.height()) / 2;
569 // Shrinking the radius slightly to avoid having the radial menu not
570 // covering the entire button.
571 radius -= radius / 10;
572 radial_menu_ = views::RadialMenu::CreateRadialMenuInstance(
573 location, // Location center is always the center of the button.
574 3 * radius, // The outer radius hits the end of the window.
575 radius, // The button itself is minimally covered.
576 0.0,
577 items,
578 kHoverRadialMenuExtension,
579 false); // Do not crop the menu to the screen.
580 // A global event handler gets added to keep track of radial menu related
581 // mouse move updates. This filter only tracks mouse movement when no key
582 // is pressed.
583 ash::Shell::GetInstance()->AddEnvEventFilter(this);
584 } else {
585 bubble_ = new Bubble(this);
586 }
587 }
588 timer_.reset(NULL);
589 }
590
591 MaximizeBubble::~MaximizeBubble() {
592 timer_.reset(NULL);
593 if (bubble_) {
594 // To avoid recursive destruction we make sure to clear the destroyed
595 // objects pointer before we tell it to destroy itself.
596 Bubble* bubble = bubble_;
597 bubble_ = NULL;
598 bubble->Close();
599 }
600 if (radial_menu_) {
601 // To avoid recursive destruction we make sure to clear the destroyed
602 // objects pointer before we tell it to destroy itself.
603 ash::Shell::GetInstance()->RemoveEnvEventFilter(this);
604 views::RadialMenu* radial_menu = radial_menu_;
605 radial_menu_ = NULL;
606 radial_menu->CloseAndDelete(false);
607 }
608 }
609
610 void MaximizeBubble::Close() {
611 timer_.reset(NULL);
612 // Note: If |bubble_| is NULL, we are in process of deleting the menu.
613 if (bubble_ || radial_menu_)
614 frame_maximize_button_->DestroyMaximizeMenu();
615 }
616
617 void MaximizeBubble::OnButtonClicked(FrameMaximizeButton::SnapType snap_type) {
618 frame_maximize_button_->SnapButtonClicked(snap_type);
619 // Once it was clicked we can close the menu.
620 Close();
621 }
622
623 void MaximizeBubble::OnButtonHover(FrameMaximizeButton::SnapType snap_type) {
624 frame_maximize_button_->SnapButtonHovered(snap_type);
625 }
626
627 void MaximizeBubble::SetMenuState(FrameMaximizeButton::SnapType snap_type) {
628 if (bubble_)
629 bubble_->SetMenuState(snap_type);
630 }
631
632 aura::Window* MaximizeBubble::GetMenuWindow() {
633 return bubble_ ? bubble_->GetMenuWindow() :
634 (radial_menu_ ? radial_menu_->GetWindow() : NULL);
635 }
636
637 // Functions added for the experimental radial menu.
638
639 bool MaximizeBubble::IsRadialMenu() {
640 return radial_menu_ ? true : false;
641 }
642
643 bool MaximizeBubble::SnapTypeForLocation(
644 const gfx::Point& location,
645 FrameMaximizeButton::SnapType& type) {
646 int item = -1;
647 if (radial_menu_) {
648 int count = 0;
649 // Depending on the hover / drag operation we use different
650 // 'activateable areas' behind a segment: In case of hover we cancel the
651 // menu when we get a bit away - for drag operations however we do not
652 // cancel the operation at all (at least not from within the menu).
653 int max_activatable_area = frame_maximize_button_->is_snap_enabled() ?
654 kDragRadialMenuExtension : kHoverRadialMenuExtension;
655 radial_menu_->set_pointer_activation_area_extension(max_activatable_area);
656 radial_menu_->HitMenuItemTest(location, &item, &count);
657 }
658 switch (item) {
659 case RADIAL_MENU_RIGHT:
660 type = FrameMaximizeButton::SNAP_RIGHT;
661 return true;
662 case RADIAL_MENU_MINIMIZE:
663 type = FrameMaximizeButton::SNAP_MINIMIZE;
664 return true;
665 case RADIAL_MENU_LEFT:
666 type = FrameMaximizeButton::SNAP_LEFT;
667 return true;
668 default:
669 type = FrameMaximizeButton::SNAP_NONE;
670 return false;
671 }
672 }
673
674 bool MaximizeBubble::PreHandleKeyEvent(aura::Window* target,
675 aura::KeyEvent* event) {
676 return false;
677 }
678
679 bool MaximizeBubble::PreHandleMouseEvent(aura::Window* target,
680 aura::MouseEvent* event) {
681 if (event->type() == ui::ET_MOUSE_MOVED &&
682 !frame_maximize_button_->is_snap_enabled()) {
683 // If we are neither over our menu, nor over the button itself we close
684 // the menu.
685 gfx::Point pt = gfx::Screen::GetCursorScreenPoint();
686 if (!SnapTypeForLocation(pt, current_radial_snap_hover_type_) &&
687 !frame_maximize_button()->GetBoundsInScreen().Contains(pt)) {
688 OnButtonHover(FrameMaximizeButton::SNAP_NONE);
689 Close();
690 } else
691 OnButtonHover(current_radial_snap_hover_type_);
692 }
693
694 if (event->type() == ui::ET_MOUSE_PRESSED &&
695 !frame_maximize_button_->is_snap_enabled() &&
696 current_radial_snap_hover_type_ != FrameMaximizeButton::SNAP_NONE) {
697 OnButtonClicked(current_radial_snap_hover_type_);
698 // This action was consumed here.
699 return true;
700 }
701
702 return false;
703 }
704
705 ui::TouchStatus MaximizeBubble::PreHandleTouchEvent(
706 aura::Window* target,
707 aura::TouchEvent* event) {
708 return ui::TOUCH_STATUS_UNKNOWN;
709 }
710
711 ui::GestureStatus MaximizeBubble::PreHandleGestureEvent(
712 aura::Window* target, aura::GestureEvent* event) {
713 return ui::GESTURE_STATUS_UNKNOWN;
714 }
715
716 } // namespace ash
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698