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

Side by Side Diff: mash/shelf/shelf_view.cc

Issue 1585363002: Fork a subset of ash/shelf for use in mash. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Revert ash changes. Created 4 years, 11 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
« mash/shelf/shelf_tooltip_manager.cc ('K') | « mash/shelf/shelf_view.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "mash/shelf/shelf_view.h" 5 #include "mash/shelf/shelf_view.h"
6 6
7 #include "base/strings/stringprintf.h" 7 #include <algorithm>
8 #include "base/strings/utf_string_conversions.h" 8
9 #include "mojo/common/common_type_converters.h" 9 #include "base/auto_reset.h"
10 #include "mojo/shell/public/cpp/application_impl.h" 10 #include "mash/shelf/shelf_button.h"
11 #include "mash/shelf/shelf_constants.h"
12 #include "ui/accessibility/ax_view_state.h"
13 #include "ui/aura/window.h"
14 #include "ui/aura/window_event_dispatcher.h"
15 #include "ui/base/l10n/l10n_util.h"
16 #include "ui/base/models/simple_menu_model.h"
17 #include "ui/base/resource/resource_bundle.h"
18 #include "ui/compositor/layer.h"
19 #include "ui/compositor/layer_animator.h"
20 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
21 #include "ui/events/event_utils.h"
11 #include "ui/gfx/canvas.h" 22 #include "ui/gfx/canvas.h"
23 #include "ui/gfx/geometry/point.h"
24 #include "ui/resources/grit/ui_resources.h"
25 #include "ui/views/animation/bounds_animator.h"
26 #include "ui/views/background.h"
27 #include "ui/views/border.h"
28 #include "ui/views/controls/button/image_button.h"
12 #include "ui/views/controls/button/label_button.h" 29 #include "ui/views/controls/button/label_button.h"
13 #include "ui/views/layout/box_layout.h" 30 #include "ui/views/controls/menu/menu_model_adapter.h"
31 #include "ui/views/controls/menu/menu_runner.h"
32 #include "ui/views/focus/focus_search.h"
33 #include "ui/views/view_model_utils.h"
34 #include "ui/views/widget/widget.h"
35 #include "ui/wm/core/coordinate_conversion.h"
14 36
15 namespace mash { 37 namespace mash {
16 namespace shelf { 38 namespace shelf {
17 39
18 ShelfView::ShelfView(mojo::ApplicationImpl* app) : binding_(this) { 40 const int SHELF_ALIGNMENT_UMA_ENUM_VALUE_BOTTOM = 0;
19 app->ConnectToService("mojo:desktop_wm", &user_window_controller_); 41 const int SHELF_ALIGNMENT_UMA_ENUM_VALUE_LEFT = 1;
20 42 const int SHELF_ALIGNMENT_UMA_ENUM_VALUE_RIGHT = 2;
21 user_window_controller_->AddUserWindowObserver( 43 const int SHELF_ALIGNMENT_UMA_ENUM_VALUE_COUNT = 3;
22 binding_.CreateInterfacePtrAndBind()); 44
23 45 // Default amount content is inset on the left edge.
24 SetLayoutManager( 46 const int kDefaultLeadingInset = 8;
25 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0)); 47
26 } 48 // Minimum distance before drag starts.
27 49 const int kMinimumDragDistance = 8;
28 ShelfView::~ShelfView() {} 50
29 51 // The proportion of the shelf space reserved for non-panel icons. Panels
30 size_t ShelfView::GetButtonIndexById(uint32_t window_id) const { 52 // may flow into this space but will be put into the overflow bubble if there
31 for (size_t i = 0; i < open_window_buttons_.size(); ++i) 53 // is contention for the space.
32 if (static_cast<uint32_t>(open_window_buttons_[i]->tag()) == window_id) 54 const float kReservedNonPanelIconProportion = 0.67f;
33 return i; 55
34 return open_window_buttons_.size(); 56 // The distance of the cursor from the outer rim of the shelf before it
35 } 57 // separates.
36 58 const int kRipOffDistance = 48;
37 void ShelfView::OnPaint(gfx::Canvas* canvas) { 59
38 canvas->FillRect(GetLocalBounds(), SK_ColorYELLOW); 60 // The rip off drag and drop proxy image should get scaled by this factor.
bungeman-skia 2016/01/27 19:59:48 Was already implicitly using a Skia header here?
msw 2016/01/27 20:21:50 Yup! Looks like it's a public deps of ui/gfx, and
39 views::View::OnPaint(canvas); 61 const float kDragAndDropProxyScale = 1.5f;
62
63 // The opacity represents that this partially disappeared item will get removed.
64 const float kDraggedImageOpacity = 0.5f;
65
66 namespace {
67
68 // A class to temporarily disable a given bounds animator.
69 class BoundsAnimatorDisabler {
70 public:
71 explicit BoundsAnimatorDisabler(views::BoundsAnimator* bounds_animator)
72 : old_duration_(bounds_animator->GetAnimationDuration()),
73 bounds_animator_(bounds_animator) {
74 bounds_animator_->SetAnimationDuration(1);
75 }
76
77 ~BoundsAnimatorDisabler() {
78 bounds_animator_->SetAnimationDuration(old_duration_);
79 }
80
81 private:
82 // The previous animation duration.
83 int old_duration_;
84 // The bounds animator which gets used.
85 views::BoundsAnimator* bounds_animator_;
86
87 DISALLOW_COPY_AND_ASSIGN(BoundsAnimatorDisabler);
88 };
89
90 // Custom FocusSearch used to navigate the shelf in the order items are in
91 // the ViewModel.
92 class ShelfFocusSearch : public views::FocusSearch {
93 public:
94 explicit ShelfFocusSearch(views::ViewModel* view_model)
95 : FocusSearch(nullptr, true, true),
96 view_model_(view_model) {}
97 ~ShelfFocusSearch() override {}
98
99 // views::FocusSearch overrides:
100 views::View* FindNextFocusableView(
101 views::View* starting_view,
102 bool reverse,
103 Direction direction,
104 bool check_starting_view,
105 views::FocusTraversable** focus_traversable,
106 views::View** focus_traversable_view) override {
107 int index = view_model_->GetIndexOfView(starting_view);
108 if (index == -1)
109 return view_model_->view_at(0);
110
111 if (reverse) {
112 --index;
113 if (index < 0)
114 index = view_model_->view_size() - 1;
115 } else {
116 ++index;
117 if (index >= view_model_->view_size())
118 index = 0;
119 }
120 return view_model_->view_at(index);
121 }
122
123 private:
124 views::ViewModel* view_model_;
125
126 DISALLOW_COPY_AND_ASSIGN(ShelfFocusSearch);
127 };
128
129 // AnimationDelegate used when inserting a new item. This steadily increases the
130 // opacity of the layer as the animation progress.
131 class FadeInAnimationDelegate : public gfx::AnimationDelegate {
132 public:
133 explicit FadeInAnimationDelegate(views::View* view) : view_(view) {}
134 ~FadeInAnimationDelegate() override {}
135
136 // AnimationDelegate overrides:
137 void AnimationProgressed(const gfx::Animation* animation) override {
138 view_->layer()->SetOpacity(animation->GetCurrentValue());
139 view_->layer()->ScheduleDraw();
140 }
141 void AnimationEnded(const gfx::Animation* animation) override {
142 view_->layer()->SetOpacity(1.0f);
143 view_->layer()->ScheduleDraw();
144 }
145 void AnimationCanceled(const gfx::Animation* animation) override {
146 view_->layer()->SetOpacity(1.0f);
147 view_->layer()->ScheduleDraw();
148 }
149
150 private:
151 views::View* view_;
152
153 DISALLOW_COPY_AND_ASSIGN(FadeInAnimationDelegate);
154 };
155
156 void ReflectItemStatus(const ShelfItem& item, ShelfButton* button) {
157 switch (item.status) {
158 case STATUS_CLOSED:
159 button->ClearState(ShelfButton::STATE_ACTIVE);
160 button->ClearState(ShelfButton::STATE_RUNNING);
161 button->ClearState(ShelfButton::STATE_ATTENTION);
162 break;
163 case STATUS_RUNNING:
164 button->ClearState(ShelfButton::STATE_ACTIVE);
165 button->AddState(ShelfButton::STATE_RUNNING);
166 button->ClearState(ShelfButton::STATE_ATTENTION);
167 break;
168 case STATUS_ACTIVE:
169 button->AddState(ShelfButton::STATE_ACTIVE);
170 button->ClearState(ShelfButton::STATE_RUNNING);
171 button->ClearState(ShelfButton::STATE_ATTENTION);
172 break;
173 case STATUS_ATTENTION:
174 button->ClearState(ShelfButton::STATE_ACTIVE);
175 button->ClearState(ShelfButton::STATE_RUNNING);
176 button->AddState(ShelfButton::STATE_ATTENTION);
177 break;
178 }
179 }
180
181 } // namespace
182
183 // AnimationDelegate used when deleting an item. This steadily decreased the
184 // opacity of the layer as the animation progress.
185 class ShelfView::FadeOutAnimationDelegate : public gfx::AnimationDelegate {
186 public:
187 FadeOutAnimationDelegate(ShelfView* host, views::View* view)
188 : shelf_view_(host),
189 view_(view) {}
190 ~FadeOutAnimationDelegate() override {}
191
192 // AnimationDelegate overrides:
193 void AnimationProgressed(const gfx::Animation* animation) override {
194 view_->layer()->SetOpacity(1 - animation->GetCurrentValue());
195 view_->layer()->ScheduleDraw();
196 }
197 void AnimationEnded(const gfx::Animation* animation) override {
198 shelf_view_->OnFadeOutAnimationEnded();
199 }
200 void AnimationCanceled(const gfx::Animation* animation) override {}
201
202 private:
203 ShelfView* shelf_view_;
204 scoped_ptr<views::View> view_;
205
206 DISALLOW_COPY_AND_ASSIGN(FadeOutAnimationDelegate);
207 };
208
209 // AnimationDelegate used to trigger fading an element in. When an item is
210 // inserted this delegate is attached to the animation that expands the size of
211 // the item. When done it kicks off another animation to fade the item in.
212 class ShelfView::StartFadeAnimationDelegate : public gfx::AnimationDelegate {
213 public:
214 StartFadeAnimationDelegate(ShelfView* host, views::View* view)
215 : shelf_view_(host),
216 view_(view) {}
217 ~StartFadeAnimationDelegate() override {}
218
219 // AnimationDelegate overrides:
220 void AnimationEnded(const gfx::Animation* animation) override {
221 shelf_view_->FadeIn(view_);
222 }
223 void AnimationCanceled(const gfx::Animation* animation) override {
224 view_->layer()->SetOpacity(1.0f);
225 }
226
227 private:
228 ShelfView* shelf_view_;
229 views::View* view_;
230
231 DISALLOW_COPY_AND_ASSIGN(StartFadeAnimationDelegate);
232 };
233
234 ShelfView::ShelfView(mojo::ApplicationImpl* app)
235 : app_(app),
236 model_(app),
237 alignment_(SHELF_ALIGNMENT_BOTTOM),
238 first_visible_index_(0),
239 last_visible_index_(-1),
240 /* TODO(msw): Restore functionality:
241 overflow_button_(nullptr),
242 owner_overflow_bubble_(nullptr),*/
243 tooltip_(this),
244 drag_pointer_(NONE),
245 drag_view_(nullptr),
246 start_drag_index_(-1),
247 context_menu_id_(0),
248 leading_inset_(kDefaultLeadingInset),
249 cancelling_drag_model_changed_(false),
250 last_hidden_index_(0),
251 closing_event_time_(),
252 got_deleted_(nullptr),
253 drag_and_drop_item_pinned_(false),
254 drag_and_drop_shelf_id_(0),
255 drag_replaced_view_(nullptr),
256 dragged_off_shelf_(false),
257 snap_back_from_rip_off_view_(nullptr),
258 /* TODO(msw): Restore functionality:
259 item_manager_(Shell::GetInstance()->shelf_item_delegate_manager()),*/
260 overflow_mode_(false),
261 main_shelf_(nullptr),
262 dragged_off_from_overflow_to_shelf_(false),
263 is_repost_event_(false),
264 last_pressed_index_(-1) {
265 bounds_animator_.reset(new views::BoundsAnimator(this));
266 bounds_animator_->AddObserver(this);
267 set_context_menu_controller(this);
268 focus_search_.reset(new ShelfFocusSearch(&view_model_));
269
270 model_.AddObserver(this);
271 const ShelfItems& items(model_.items());
272 for (ShelfItems::const_iterator i = items.begin(); i != items.end(); ++i) {
273 views::View* child = CreateViewForItem(*i);
274 view_model_.Add(child, static_cast<int>(i - items.begin()));
275 AddChildView(child);
276 }
277
278 /* TODO(msw): Restore functionality:
279 overflow_button_ = new OverflowButton(this);
280 overflow_button_->set_context_menu_controller(this);
281 ConfigureChildView(overflow_button_);
282 AddChildView(overflow_button_);*/
283
284 /* TODO(msw): Add a stub apps button?
285 ShelfItem app_list;
286 app_list.type = TYPE_APP_LIST;
287 app_list.title = base::ASCIIToUTF16("APPS");
288 model()->Add(app_list);*/
289
290 // TODO(msw): Needed to paint children as layers???
291 SetPaintToLayer(true);
292 set_background(views::Background::CreateSolidBackground(SK_ColorYELLOW));
293
294 // We'll layout when our bounds change.
295 }
296
297 ShelfView::~ShelfView() {
298 bounds_animator_->RemoveObserver(this);
299 model_.RemoveObserver(this);
300 // If we are inside the MenuRunner, we need to know if we were getting
301 // deleted while it was running.
302 if (got_deleted_)
303 *got_deleted_ = true;
304 }
305
306 void ShelfView::SetAlignment(ShelfAlignment alignment) {
307 if (alignment_ == alignment)
308 return;
309
310 alignment_ = alignment;
311 /* TODO(msw): Restore functionality:
312 overflow_button_->OnShelfAlignmentChanged();*/
313 LayoutToIdealBounds();
314 for (int i = 0; i < view_model_.view_size(); ++i) {
315 if (i >= first_visible_index_ && i <= last_visible_index_)
316 view_model_.view_at(i)->Layout();
317 }
318 tooltip_.Close();
319 /* TODO(msw): Restore functionality:
320 if (overflow_bubble_)
321 overflow_bubble_->Hide();*/
322 }
323
324 void ShelfView::SchedulePaintForAllButtons() {
325 for (int i = 0; i < view_model_.view_size(); ++i) {
326 if (i >= first_visible_index_ && i <= last_visible_index_)
327 view_model_.view_at(i)->SchedulePaint();
328 }
329 /* TODO(msw): Restore functionality:
330 if (overflow_button_ && overflow_button_->visible())
331 overflow_button_->SchedulePaint();*/
332 }
333
334 gfx::Rect ShelfView::GetIdealBoundsOfItemIcon(ShelfID id) {
335 int index = model_.ItemIndexByID(id);
336 if (index == -1)
337 return gfx::Rect();
338 // Map all items from overflow area to the overflow button. Note that the
339 // section between last_index_hidden_ and model_.FirstPanelIndex() is the
340 // list of invisible panel items. However, these items are currently nowhere
341 // represented and get dropped instead - see (crbug.com/378907). As such there
342 // is no way to address them or place them. We therefore move them over the
343 // overflow button.
344 if (index > last_visible_index_ && index < model_.FirstPanelIndex())
345 index = last_visible_index_ + 1;
346 const gfx::Rect& ideal_bounds(view_model_.ideal_bounds(index));
347 DCHECK_NE(TYPE_APP_LIST, model_.items()[index].type);
348 views::View* view = view_model_.view_at(index);
349 CHECK_EQ(ShelfButton::kViewClassName, view->GetClassName());
350 ShelfButton* button = static_cast<ShelfButton*>(view);
351 gfx::Rect icon_bounds = button->GetIconBounds();
352 return gfx::Rect(GetMirroredXWithWidthInView(
353 ideal_bounds.x() + icon_bounds.x(), icon_bounds.width()),
354 ideal_bounds.y() + icon_bounds.y(),
355 icon_bounds.width(),
356 icon_bounds.height());
357 }
358
359 void ShelfView::UpdatePanelIconPosition(ShelfID id,
360 const gfx::Point& midpoint) {
361 int current_index = model_.ItemIndexByID(id);
362 int first_panel_index = model_.FirstPanelIndex();
363 if (current_index < first_panel_index)
364 return;
365
366 gfx::Point midpoint_in_view(GetMirroredXInView(midpoint.x()),
367 midpoint.y());
368 int target_index = current_index;
369 while (target_index > first_panel_index &&
370 PrimaryAxisValue(view_model_.ideal_bounds(target_index).x(),
371 view_model_.ideal_bounds(target_index).y()) >
372 PrimaryAxisValue(midpoint_in_view.x(),
373 midpoint_in_view.y())) {
374 --target_index;
375 }
376 while (target_index < view_model_.view_size() - 1 &&
377 PrimaryAxisValue(view_model_.ideal_bounds(target_index).right(),
378 view_model_.ideal_bounds(target_index).bottom()) <
379 PrimaryAxisValue(midpoint_in_view.x(),
380 midpoint_in_view.y())) {
381 ++target_index;
382 }
383 if (current_index != target_index)
384 model_.Move(current_index, target_index);
385 }
386
387 bool ShelfView::IsShowingMenu() const {
388 return (launcher_menu_runner_.get() &&
389 launcher_menu_runner_->IsRunning());
390 }
391
392 bool ShelfView::IsShowingOverflowBubble() const {
393 /* TODO(msw): Restore functionality:
394 return overflow_bubble_.get() && overflow_bubble_->IsShowing();*/
395 return false;
396 }
397
398 views::View* ShelfView::GetAppListButtonView() const {
399 for (int i = 0; i < model_.item_count(); ++i) {
400 if (model_.items()[i].type == TYPE_APP_LIST)
401 return view_model_.view_at(i);
402 }
403
404 NOTREACHED() << "Applist button not found";
405 return nullptr;
406 }
407
408 ////////////////////////////////////////////////////////////////////////////////
409 // ShelfView, FocusTraversable implementation:
410
411 views::FocusSearch* ShelfView::GetFocusSearch() {
412 return focus_search_.get();
413 }
414
415 views::FocusTraversable* ShelfView::GetFocusTraversableParent() {
416 return parent()->GetFocusTraversable();
417 }
418
419 views::View* ShelfView::GetFocusTraversableParentView() {
420 return this;
421 }
422
423 /* TODO(msw): Restore drag/drop functionality.
424 void ShelfView::CreateDragIconProxy(
425 const gfx::Point& location_in_screen_coordinates,
426 const gfx::ImageSkia& icon,
427 views::View* replaced_view,
428 const gfx::Vector2d& cursor_offset_from_center,
429 float scale_factor) {
430 drag_replaced_view_ = replaced_view;
431 drag_image_.reset(new ash::DragImageView(
432 drag_replaced_view_->GetWidget()->GetNativeWindow()->GetRootWindow(),
433 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE));
434 drag_image_->SetImage(icon);
435 gfx::Size size = drag_image_->GetPreferredSize();
436 size.set_width(size.width() * scale_factor);
437 size.set_height(size.height() * scale_factor);
438 drag_image_offset_ = gfx::Vector2d(size.width() / 2, size.height() / 2) +
439 cursor_offset_from_center;
440 gfx::Rect drag_image_bounds(
441 location_in_screen_coordinates - drag_image_offset_,
442 size);
443 drag_image_->SetBoundsInScreen(drag_image_bounds);
444 drag_image_->SetWidgetVisible(true);
445 }
446
447 void ShelfView::UpdateDragIconProxy(
448 const gfx::Point& location_in_screen_coordinates) {
449 // TODO(jennyz): Investigate why drag_image_ becomes NULL at this point per
450 // crbug.com/34722, while the app list item is still being dragged around.
451 if (drag_image_) {
452 drag_image_->SetScreenPosition(
453 location_in_screen_coordinates - drag_image_offset_);
454 }
455 }
456
457 void ShelfView::DestroyDragIconProxy() {
458 drag_image_.reset();
459 drag_image_offset_ = gfx::Vector2d(0, 0);
460 }
461
462 bool ShelfView::StartDrag(const std::string& app_id,
463 const gfx::Point& location_in_screen_coordinates) {
464 // Bail if an operation is already going on - or the cursor is not inside.
465 // This could happen if mouse / touch operations overlap.
466 if (drag_and_drop_shelf_id_ ||
467 !GetBoundsInScreen().Contains(location_in_screen_coordinates))
468 return false;
469
470 // If the AppsGridView (which was dispatching this event) was opened by our
471 // button, ShelfView dragging operations are locked and we have to unlock.
472 CancelDrag(-1);
473 drag_and_drop_item_pinned_ = false;
474 drag_and_drop_app_id_ = app_id;
475 drag_and_drop_shelf_id_ =
476 delegate_->GetShelfIDForAppID(drag_and_drop_app_id_);
477 // Check if the application is known and pinned - if not, we have to pin it so
478 // that we can re-arrange the shelf order accordingly. Note that items have
479 // to be pinned to give them the same (order) possibilities as a shortcut.
480 // When an item is dragged from overflow to shelf, IsShowingOverflowBubble()
481 // returns true. At this time, we don't need to pin the item.
482 if (!IsShowingOverflowBubble() &&
483 (!drag_and_drop_shelf_id_ ||
484 !delegate_->IsAppPinned(app_id))) {
485 delegate_->PinAppWithID(app_id);
486 drag_and_drop_shelf_id_ =
487 delegate_->GetShelfIDForAppID(drag_and_drop_app_id_);
488 if (!drag_and_drop_shelf_id_)
489 return false;
490 drag_and_drop_item_pinned_ = true;
491 }
492 views::View* drag_and_drop_view = view_model_->view_at(
493 model_.ItemIndexByID(drag_and_drop_shelf_id_));
494 DCHECK(drag_and_drop_view);
495
496 // Since there is already an icon presented by the caller, we hide this item
497 // for now. That has to be done by reducing the size since the visibility will
498 // change once a regrouping animation is performed.
499 pre_drag_and_drop_size_ = drag_and_drop_view->size();
500 drag_and_drop_view->SetSize(gfx::Size());
501
502 // First we have to center the mouse cursor over the item.
503 gfx::Point pt = drag_and_drop_view->GetBoundsInScreen().CenterPoint();
504 views::View::ConvertPointFromScreen(drag_and_drop_view, &pt);
505 gfx::Point point_in_root = location_in_screen_coordinates;
506 ::wm::ConvertPointFromScreen(
507 ash::wm::GetRootWindowAt(location_in_screen_coordinates), &point_in_root);
508 ui::MouseEvent event(ui::ET_MOUSE_PRESSED, pt, point_in_root,
509 ui::EventTimeForNow(), 0, 0);
510 PointerPressedOnButton(drag_and_drop_view,
511 ShelfButtonHost::DRAG_AND_DROP,
512 event);
513
514 // Drag the item where it really belongs.
515 Drag(location_in_screen_coordinates);
516 return true;
517 }
518
519 bool ShelfView::Drag(const gfx::Point& location_in_screen_coordinates) {
520 if (!drag_and_drop_shelf_id_ ||
521 !GetBoundsInScreen().Contains(location_in_screen_coordinates))
522 return false;
523
524 gfx::Point pt = location_in_screen_coordinates;
525 views::View* drag_and_drop_view = view_model_->view_at(
526 model_.ItemIndexByID(drag_and_drop_shelf_id_));
527 ConvertPointFromScreen(drag_and_drop_view, &pt);
528 gfx::Point point_in_root = location_in_screen_coordinates;
529 ::wm::ConvertPointFromScreen(
530 ash::wm::GetRootWindowAt(location_in_screen_coordinates), &point_in_root);
531 ui::MouseEvent event(ui::ET_MOUSE_DRAGGED, pt, point_in_root,
532 ui::EventTimeForNow(), 0, 0);
533 PointerDraggedOnButton(drag_and_drop_view,
534 ShelfButtonHost::DRAG_AND_DROP,
535 event);
536 return true;
537 }
538
539 void ShelfView::EndDrag(bool cancel) {
540 if (!drag_and_drop_shelf_id_)
541 return;
542
543 views::View* drag_and_drop_view = view_model_->view_at(
544 model_.ItemIndexByID(drag_and_drop_shelf_id_));
545 PointerReleasedOnButton(
546 drag_and_drop_view, ShelfButtonHost::DRAG_AND_DROP, cancel);
547
548 // Either destroy the temporarily created item - or - make the item visible.
549 if (drag_and_drop_item_pinned_ && cancel) {
550 delegate_->UnpinAppWithID(drag_and_drop_app_id_);
551 } else if (drag_and_drop_view) {
552 if (cancel) {
553 // When a hosted drag gets canceled, the item can remain in the same slot
554 // and it might have moved within the bounds. In that case the item need
555 // to animate back to its correct location.
556 AnimateToIdealBounds();
557 } else {
558 drag_and_drop_view->SetSize(pre_drag_and_drop_size_);
559 }
560 }
561
562 drag_and_drop_shelf_id_ = 0;
563 }*/
564
565 void ShelfView::LayoutToIdealBounds() {
566 if (bounds_animator_->IsAnimating()) {
567 AnimateToIdealBounds();
568 return;
569 }
570
571 CalculateIdealBounds();
572 /* TODO(msw): Restore functionality:
573 gfx::Rect overflow_bounds = CalculateIdealBounds();*/
574 views::ViewModelUtils::SetViewBoundsToIdealBounds(view_model_);
575 /* TODO(msw): Restore functionality:
576 overflow_button_->SetBoundsRect(overflow_bounds);*/
577 }
578
579 void ShelfView::UpdateAllButtonsVisibilityInOverflowMode() {
580 // The overflow button is not shown in overflow mode.
581 /* TODO(msw): Restore functionality:
582 overflow_button_->SetVisible(false);*/
583 DCHECK_LT(last_visible_index_, view_model_.view_size());
584 for (int i = 0; i < view_model_.view_size(); ++i) {
585 bool visible = i >= first_visible_index_ &&
586 i <= last_visible_index_;
587 // To track the dragging of |drag_view_| continuously, its visibility
588 // should be always true regardless of its position.
589 if (dragged_off_from_overflow_to_shelf_ &&
590 view_model_.view_at(i) == drag_view_)
591 view_model_.view_at(i)->SetVisible(true);
592 else
593 view_model_.view_at(i)->SetVisible(visible);
594 }
595 }
596
597 gfx::Rect ShelfView::CalculateIdealBounds() {
598 int available_size = PrimaryAxisValue(width(), height());
599 DCHECK_EQ(model_.item_count(), view_model_.view_size());
600 if (!available_size || model_.items().empty())
601 return gfx::Rect();
602
603 int x = 0;
604 int y = 0;
605 int button_size = kShelfButtonSize;
606 int button_spacing = kShelfButtonSpacing;
607
608 int w = PrimaryAxisValue(button_size, width());
609 int h = PrimaryAxisValue(height(), button_size);
610 for (int i = 0; i < view_model_.view_size(); ++i) {
611 if (i < first_visible_index_) {
612 view_model_.set_ideal_bounds(i, gfx::Rect(x, y, 0, 0));
613 continue;
614 }
615
616 view_model_.set_ideal_bounds(i, gfx::Rect(x, y, w, h));
617 x = PrimaryAxisValue(x + w + button_spacing, x);
618 y = PrimaryAxisValue(y, y + h + button_spacing);
619 }
620
621 if (is_overflow_mode()) {
622 UpdateAllButtonsVisibilityInOverflowMode();
623 return gfx::Rect();
624 }
625
626 // Right aligned icons.
627 int end_position = available_size - button_spacing;
628 x = PrimaryAxisValue(end_position, 0);
629 y = PrimaryAxisValue(0, end_position);
630 int first_panel_index = model_.FirstPanelIndex();
631 for (int i = view_model_.view_size() - 1; i >= first_panel_index; --i) {
632 x = PrimaryAxisValue(x - w - button_spacing, x);
633 y = PrimaryAxisValue(y, y - h - button_spacing);
634 view_model_.set_ideal_bounds(i, gfx::Rect(x, y, w, h));
635 end_position = PrimaryAxisValue(x, y);
636 }
637
638 int last_button_index = first_panel_index - 1;
639 if (last_button_index < 0)
640 return gfx::Rect();
641
642 // Icons on the left / top are guaranteed up to kLeftIconProportion of
643 // the available space.
644 int last_icon_position = PrimaryAxisValue(
645 view_model_.ideal_bounds(last_button_index).right(),
646 view_model_.ideal_bounds(last_button_index).bottom()) + button_size;
647 int reserved_icon_space = available_size * kReservedNonPanelIconProportion;
648 if (last_icon_position < reserved_icon_space)
649 end_position = last_icon_position;
650 else
651 end_position = std::max(end_position, reserved_icon_space);
652
653 gfx::Rect overflow_bounds(PrimaryAxisValue(w, width()),
654 PrimaryAxisValue(height(), h));
655
656 last_visible_index_ = DetermineLastVisibleIndex(end_position - button_size);
657 last_hidden_index_ = DetermineFirstVisiblePanelIndex(end_position) - 1;
658 bool show_overflow = last_visible_index_ < last_button_index ||
659 last_hidden_index_ >= first_panel_index;
660
661 // Create Space for the overflow button
662 if (show_overflow) {
663 // The following code makes sure that platform apps icons (aligned to left /
664 // top) are favored over panel apps icons (aligned to right / bottom).
665 if (last_visible_index_ > 0 && last_visible_index_ < last_button_index) {
666 // This condition means that we will take one platform app and replace it
667 // with the overflow button and put the app in the overflow bubble.
668 // This happens when the space needed for platform apps exceeds the
669 // reserved area for non-panel icons,
670 // (i.e. |last_icon_position| > |reserved_icon_space|).
671 --last_visible_index_;
672 } else if (last_hidden_index_ >= first_panel_index &&
673 last_hidden_index_ < view_model_.view_size() - 1) {
674 // This condition means that we will take a panel app icon and replace it
675 // with the overflow button.
676 // This happens when there is still room for platform apps in the reserved
677 // area for non-panel icons,
678 // (i.e. |last_icon_position| < |reserved_icon_space|).
679 ++last_hidden_index_;
680 }
681 }
682
683 for (int i = 0; i < view_model_.view_size(); ++i) {
684 bool visible = i <= last_visible_index_ || i > last_hidden_index_;
685 // To receive drag event continuously from |drag_view_| during the dragging
686 // off from the shelf, don't make |drag_view_| invisible. It will be
687 // eventually invisible and removed from the |view_model_| by
688 // FinalizeRipOffDrag().
689 if (dragged_off_shelf_ && view_model_.view_at(i) == drag_view_)
690 continue;
691 view_model_.view_at(i)->SetVisible(visible);
692 }
693
694 /* TODO(msw): Restore functionality:
695 overflow_button_->SetVisible(show_overflow);*/
696 if (show_overflow) {
697 DCHECK_NE(0, view_model_.view_size());
698 if (last_visible_index_ == -1) {
699 x = 0;
700 y = 0;
701 } else {
702 x = PrimaryAxisValue(
703 view_model_.ideal_bounds(last_visible_index_).right(),
704 view_model_.ideal_bounds(last_visible_index_).x());
705 y = PrimaryAxisValue(
706 view_model_.ideal_bounds(last_visible_index_).y(),
707 view_model_.ideal_bounds(last_visible_index_).bottom());
708 }
709 // Set all hidden panel icon positions to be on the overflow button.
710 for (int i = first_panel_index; i <= last_hidden_index_; ++i)
711 view_model_.set_ideal_bounds(i, gfx::Rect(x, y, w, h));
712
713 // Add more space between last visible item and overflow button.
714 // Without this, two buttons look too close compared with other items.
715 x = PrimaryAxisValue(x + button_spacing, x);
716 y = PrimaryAxisValue(y, y + button_spacing);
717
718 overflow_bounds.set_x(x);
719 overflow_bounds.set_y(y);
720 /* TODO(msw): Restore functionality:
721 if (overflow_bubble_.get() && overflow_bubble_->IsShowing())
722 UpdateOverflowRange(overflow_bubble_->shelf_view());*/
723 } else {
724 /* TODO(msw): Restore functionality:
725 if (overflow_bubble_)
726 overflow_bubble_->Hide();*/
727 }
728 return overflow_bounds;
729 }
730
731 int ShelfView::DetermineLastVisibleIndex(int max_value) const {
732 int index = model_.FirstPanelIndex() - 1;
733 while (index >= 0 &&
734 PrimaryAxisValue(
735 view_model_.ideal_bounds(index).right(),
736 view_model_.ideal_bounds(index).bottom()) > max_value) {
737 index--;
738 }
739 return index;
740 }
741
742 int ShelfView::DetermineFirstVisiblePanelIndex(int min_value) const {
743 int index = model_.FirstPanelIndex();
744 while (index < view_model_.view_size() &&
745 PrimaryAxisValue(
746 view_model_.ideal_bounds(index).right(),
747 view_model_.ideal_bounds(index).bottom()) < min_value) {
748 ++index;
749 }
750 return index;
751 }
752
753 /* TODO(msw): Restore functionality:
754 void ShelfView::AddIconObserver(ShelfIconObserver* observer) {
755 observers_.AddObserver(observer);
756 }
757
758 void ShelfView::RemoveIconObserver(ShelfIconObserver* observer) {
759 observers_.RemoveObserver(observer);
760 }*/
761
762 void ShelfView::AnimateToIdealBounds() {
763 /* TODO(msw): Restore functionality:
764 gfx::Rect overflow_bounds = CalculateIdealBounds();*/
765 CalculateIdealBounds();
766 for (int i = 0; i < view_model_.view_size(); ++i) {
767 views::View* view = view_model_.view_at(i);
768 bounds_animator_->AnimateViewTo(view, view_model_.ideal_bounds(i));
769 // Now that the item animation starts, we have to make sure that the
770 // padding of the first gets properly transferred to the new first item.
771 if (i && view->border())
772 view->SetBorder(views::Border::NullBorder());
773 }
774 /* TODO(msw): Restore functionality:
775 overflow_button_->SetBoundsRect(overflow_bounds);*/
776 }
777
778 views::View* ShelfView::CreateViewForItem(const ShelfItem& item) {
779 views::View* view = nullptr;
780 switch (item.type) {
781 case TYPE_BROWSER_SHORTCUT:
782 case TYPE_APP_SHORTCUT:
783 case TYPE_WINDOWED_APP:
784 case TYPE_PLATFORM_APP:
785 case TYPE_DIALOG:
786 case TYPE_APP_PANEL: {
787 ShelfButton* button = ShelfButton::Create(this, this);
788 button->SetImage(item.image);
789 ReflectItemStatus(item, button);
790 view = button;
791 break;
792 }
793
794 case TYPE_APP_LIST: {
795 /* TODO(msw): Restore functionality:
796 view = new AppListButton(this, this);*/
797 view = new views::LabelButton(nullptr, item.title);
798 break;
799 }
800
801 case TYPE_MOJO_APP: {
802 // TODO(msw): Support item images, etc.
803 ShelfButton* button = ShelfButton::Create(this, this);
804 int image_resource_id = IDR_DEFAULT_FAVICON;
805 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
806 button->SetImage(*rb.GetImageSkiaNamed(image_resource_id));
807 ReflectItemStatus(item, button);
808 view = button;
809 break;
810 }
811
812 default:
813 break;
814 }
815 view->set_context_menu_controller(this);
816
817 DCHECK(view);
818 ConfigureChildView(view);
819 return view;
820 }
821
822 void ShelfView::FadeIn(views::View* view) {
823 view->SetVisible(true);
824 view->layer()->SetOpacity(0);
825 AnimateToIdealBounds();
826 bounds_animator_->SetAnimationDelegate(
827 view,
828 scoped_ptr<gfx::AnimationDelegate>(new FadeInAnimationDelegate(view)));
829 }
830
831 void ShelfView::PrepareForDrag(Pointer pointer, const ui::LocatedEvent& event) {
832 DCHECK(!dragging());
833 DCHECK(drag_view_);
834 drag_pointer_ = pointer;
835 start_drag_index_ = view_model_.GetIndexOfView(drag_view_);
836
837 if (start_drag_index_== -1) {
838 CancelDrag(-1);
839 return;
840 }
841
842 // If the item is no longer draggable, bail out.
843 /* TODO(msw): Restore functionality:
844 ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
845 model_.items()[start_drag_index_].id);
846 if (!item_delegate->IsDraggable()) {
847 CancelDrag(-1);
848 return;
849 }*/
850
851 // Move the view to the front so that it appears on top of other views.
852 ReorderChildView(drag_view_, -1);
853 bounds_animator_->StopAnimatingView(drag_view_);
854 }
855
856 void ShelfView::ContinueDrag(const ui::LocatedEvent& event) {
857 // Due to a syncing operation the application might have been removed.
858 // Bail if it is gone.
859 int current_index = view_model_.GetIndexOfView(drag_view_);
860 DCHECK_NE(-1, current_index);
861
862 /* TODO(msw): Restore functionality:
863 ShelfItemDelegate* item_delegate =
864 item_manager_->GetShelfItemDelegate(model_.items()[current_index].id);
865 if (!item_delegate->IsDraggable()) {
866 CancelDrag(-1);
867 return;
868 }*/
869
870 // If this is not a drag and drop host operation and not the app list item,
871 // check if the item got ripped off the shelf - if it did we are done.
872 if (!drag_and_drop_shelf_id_ &&
873 RemovableByRipOff(current_index) != NOT_REMOVABLE) {
874 if (HandleRipOffDrag(event))
875 return;
876 // The rip off handler could have changed the location of the item.
877 current_index = view_model_.GetIndexOfView(drag_view_);
878 }
879
880 // TODO: I don't think this works correctly with RTL.
881 gfx::Point drag_point(event.location());
882 ConvertPointToTarget(drag_view_, this, &drag_point);
883
884 // Constrain the location to the range of valid indices for the type.
885 std::pair<int, int> indices(GetDragRange(current_index));
886 int first_drag_index = indices.first;
887 int last_drag_index = indices.second;
888 // If the last index isn't valid, we're overflowing. Constrain to the app list
889 // (which is the last visible item).
890 if (first_drag_index < model_.FirstPanelIndex() &&
891 last_drag_index > last_visible_index_)
892 last_drag_index = last_visible_index_;
893 int x = 0, y = 0;
894 if (IsHorizontalAlignment()) {
895 x = std::max(view_model_.ideal_bounds(indices.first).x(),
896 drag_point.x() - drag_origin_.x());
897 x = std::min(view_model_.ideal_bounds(last_drag_index).right() -
898 view_model_.ideal_bounds(current_index).width(),
899 x);
900 if (drag_view_->x() == x)
901 return;
902 drag_view_->SetX(x);
903 } else {
904 y = std::max(view_model_.ideal_bounds(indices.first).y(),
905 drag_point.y() - drag_origin_.y());
906 y = std::min(view_model_.ideal_bounds(last_drag_index).bottom() -
907 view_model_.ideal_bounds(current_index).height(),
908 y);
909 if (drag_view_->y() == y)
910 return;
911 drag_view_->SetY(y);
912 }
913
914 int target_index =
915 views::ViewModelUtils::DetermineMoveIndex(
916 view_model_, drag_view_,
917 IsHorizontalAlignment() ? views::ViewModelUtils::HORIZONTAL :
918 views::ViewModelUtils::VERTICAL,
919 x, y);
920 target_index =
921 std::min(indices.second, std::max(target_index, indices.first));
922
923 int first_draggable_item = 0;
924 /* TODO(msw): Restore functionality:
925 while (first_draggable_item < static_cast<int>(model_.items().size()) &&
926 !item_manager_->GetShelfItemDelegate(
927 model_.items()[first_draggable_item].id)->IsDraggable()) {
928 first_draggable_item++;
929 }*/
930
931 target_index = std::max(target_index, first_draggable_item);
932
933 if (target_index == current_index)
934 return;
935
936 // Change the model, the ShelfItemMoved() callback will handle the
937 // |view_model_| update.
938 model_.Move(current_index, target_index);
939 bounds_animator_->StopAnimatingView(drag_view_);
940 }
941
942 bool ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) {
943 /* TODO(msw): Restore functionality:
944 int current_index = view_model_.GetIndexOfView(drag_view_);
945 DCHECK_NE(-1, current_index);
946 std::string dragged_app_id =
947 delegate_->GetAppIDForShelfID(model_.items()[current_index].id);
948
949 gfx::Point screen_location = event.root_location();
950 views::View::ConvertPointToScreen(this, &screen_location);
951
952 // To avoid ugly forwards and backwards flipping we use different constants
953 // for ripping off / re-inserting the items.
954 if (dragged_off_shelf_) {
955 // If the shelf/overflow bubble bounds contains |screen_location| we insert
956 // the item back into the shelf.
957 if (GetBoundsForDragInsertInScreen().Contains(screen_location)) {
958 if (dragged_off_from_overflow_to_shelf_) {
959 // During the dragging an item from Shelf to Overflow, it can enter here
960 // directly because both are located very closly.
961 main_shelf_->EndDrag(true);
962 // Stops the animation of |drag_view_| and sets its bounds explicitly
963 // becase ContinueDrag() stops its animation. Without this, unexpected
964 // bounds will be set.
965 bounds_animator_->StopAnimatingView(drag_view_);
966 int drag_view_index = view_model_.GetIndexOfView(drag_view_);
967 drag_view_->SetBoundsRect(view_model_.ideal_bounds(drag_view_index));
968 dragged_off_from_overflow_to_shelf_ = false;
969 }
970 // Destroy our proxy view item.
971 DestroyDragIconProxy();
972 // Re-insert the item and return simply false since the caller will handle
973 // the move as in any normal case.
974 dragged_off_shelf_ = false;
975 drag_view_->layer()->SetOpacity(1.0f);
976 // The size of Overflow bubble should be updated immediately when an item
977 // is re-inserted.
978 if (is_overflow_mode())
979 PreferredSizeChanged();
980 return false;
981 } else if (is_overflow_mode() &&
982 main_shelf_->GetBoundsForDragInsertInScreen().Contains(
983 screen_location)) {
984 if (!dragged_off_from_overflow_to_shelf_) {
985 dragged_off_from_overflow_to_shelf_ = true;
986 drag_image_->SetOpacity(1.0f);
987 main_shelf_->StartDrag(dragged_app_id, screen_location);
988 } else {
989 main_shelf_->Drag(screen_location);
990 }
991 } else if (dragged_off_from_overflow_to_shelf_) {
992 // Makes the |drag_image_| partially disappear again.
993 dragged_off_from_overflow_to_shelf_ = false;
994 drag_image_->SetOpacity(kDraggedImageOpacity);
995 main_shelf_->EndDrag(true);
996 bounds_animator_->StopAnimatingView(drag_view_);
997 int drag_view_index = view_model_.GetIndexOfView(drag_view_);
998 drag_view_->SetBoundsRect(view_model_.ideal_bounds(drag_view_index));
999 }
1000 // Move our proxy view item.
1001 UpdateDragIconProxy(screen_location);
1002 return true;
1003 }
1004 // Check if we are too far away from the shelf to enter the ripped off state.
1005 // Determine the distance to the shelf.
1006 int delta = CalculateShelfDistance(screen_location);
1007 if (delta > kRipOffDistance) {
1008 // Create a proxy view item which can be moved anywhere.
1009 CreateDragIconProxy(event.root_location(),
1010 drag_view_->GetImage(),
1011 drag_view_,
1012 gfx::Vector2d(0, 0),
1013 kDragAndDropProxyScale);
1014 drag_view_->layer()->SetOpacity(0.0f);
1015 dragged_off_shelf_ = true;
1016 if (RemovableByRipOff(current_index) == REMOVABLE) {
1017 // Move the item to the front of the first panel item and hide it.
1018 // ShelfItemMoved() callback will handle the |view_model_| update and
1019 // call AnimateToIdealBounds().
1020 if (current_index != model_.FirstPanelIndex() - 1) {
1021 model_.Move(current_index, model_.FirstPanelIndex() - 1);
1022 StartFadeInLastVisibleItem();
1023 } else if (is_overflow_mode()) {
1024 // Overflow bubble should be shrunk when an item is ripped off.
1025 PreferredSizeChanged();
1026 }
1027 // Make the item partially disappear to show that it will get removed if
1028 // dropped.
1029 drag_image_->SetOpacity(kDraggedImageOpacity);
1030 }
1031 return true;
1032 }*/
1033 return false;
1034 }
1035
1036 void ShelfView::FinalizeRipOffDrag(bool cancel) {
1037 /* TODO(msw): Restore functionality:
1038 if (!dragged_off_shelf_)
1039 return;
1040 // Make sure we do not come in here again.
1041 dragged_off_shelf_ = false;
1042
1043 // Coming here we should always have a |drag_view_|.
1044 DCHECK(drag_view_);
1045 int current_index = view_model_.GetIndexOfView(drag_view_);
1046 // If the view isn't part of the model anymore (|current_index| == -1), a sync
1047 // operation must have removed it. In that case we shouldn't change the model
1048 // and only delete the proxy image.
1049 if (current_index == -1) {
1050 DestroyDragIconProxy();
1051 return;
1052 }
1053
1054 // Set to true when the animation should snap back to where it was before.
1055 bool snap_back = false;
1056 // Items which cannot be dragged off will be handled as a cancel.
1057 if (!cancel) {
1058 if (dragged_off_from_overflow_to_shelf_) {
1059 dragged_off_from_overflow_to_shelf_ = false;
1060 main_shelf_->EndDrag(false);
1061 drag_view_->layer()->SetOpacity(1.0f);
1062 } else if (RemovableByRipOff(current_index) != REMOVABLE) {
1063 // Make sure we do not try to remove un-removable items like items which
1064 // were not pinned or have to be always there.
1065 cancel = true;
1066 snap_back = true;
1067 } else {
1068 // Make sure the item stays invisible upon removal.
1069 drag_view_->SetVisible(false);
1070 std::string app_id = 0;
1071 std::string app_id =
1072 delegate_->GetAppIDForShelfID(model_.items()[current_index].id);
1073 delegate_->UnpinAppWithID(app_id);
1074 }
1075 }
1076 if (cancel || snap_back) {
1077 if (dragged_off_from_overflow_to_shelf_) {
1078 dragged_off_from_overflow_to_shelf_ = false;
1079 // Main shelf handles revert of dragged item.
1080 main_shelf_->EndDrag(true);
1081 drag_view_->layer()->SetOpacity(1.0f);
1082 } else if (!cancelling_drag_model_changed_) {
1083 // Only do something if the change did not come through a model change.
1084 gfx::Rect drag_bounds;
1085 gfx::Rect drag_bounds = drag_image_->GetBoundsInScreen();
1086 gfx::Point relative_to = GetBoundsInScreen().origin();
1087 gfx::Rect target(
1088 gfx::PointAtOffsetFromOrigin(drag_bounds.origin()- relative_to),
1089 drag_bounds.size());
1090 drag_view_->SetBoundsRect(target);
1091 // Hide the status from the active item since we snap it back now. Upon
1092 // animation end the flag gets cleared if |snap_back_from_rip_off_view_|
1093 // is set.
1094 snap_back_from_rip_off_view_ = drag_view_;
1095 drag_view_->AddState(ShelfButton::STATE_HIDDEN);
1096 // When a canceling drag model is happening, the view model is diverged
1097 // from the menu model and movements / animations should not be done.
1098 model_.Move(current_index, start_drag_index_);
1099 AnimateToIdealBounds();
1100 }
1101 drag_view_->layer()->SetOpacity(1.0f);
1102 }
1103 DestroyDragIconProxy();*/
1104 }
1105
1106 ShelfView::RemovableState ShelfView::RemovableByRipOff(int index) const {
1107 DCHECK(index >= 0 && index < model_.item_count());
1108 ShelfItemType type = model_.items()[index].type;
1109 if (type == TYPE_APP_LIST || type == TYPE_DIALOG)
1110 return NOT_REMOVABLE;
1111
1112 /* TODO(msw): Restore functionality:
1113 std::string app_id =
1114 delegate_->GetAppIDForShelfID(model_.items()[index].id);
1115 ShelfItemDelegate* item_delegate =
1116 item_manager_->GetShelfItemDelegate(model_.items()[index].id);
1117 if (!item_delegate->CanPin())
1118 return NOT_REMOVABLE;
1119 Note: Only pinned app shortcuts can be removed!
1120 return (type == TYPE_APP_SHORTCUT && delegate_->IsAppPinned(app_id)) ?
1121 REMOVABLE : DRAGGABLE;*/
1122 return REMOVABLE;
1123 }
1124
1125 bool ShelfView::SameDragType(ShelfItemType typea, ShelfItemType typeb) const {
1126 switch (typea) {
1127 case TYPE_APP_SHORTCUT:
1128 case TYPE_BROWSER_SHORTCUT:
1129 return (typeb == TYPE_APP_SHORTCUT || typeb == TYPE_BROWSER_SHORTCUT);
1130 case TYPE_APP_LIST:
1131 case TYPE_PLATFORM_APP:
1132 case TYPE_WINDOWED_APP:
1133 case TYPE_MOJO_APP:
1134 case TYPE_APP_PANEL:
1135 case TYPE_DIALOG:
1136 return typeb == typea;
1137 case TYPE_UNDEFINED:
1138 NOTREACHED() << "ShelfItemType must be set.";
1139 return false;
1140 }
1141 NOTREACHED();
1142 return false;
1143 }
1144
1145 std::pair<int, int> ShelfView::GetDragRange(int index) {
1146 int min_index = -1;
1147 int max_index = -1;
1148 ShelfItemType type = model_.items()[index].type;
1149 for (int i = 0; i < model_.item_count(); ++i) {
1150 if (SameDragType(model_.items()[i].type, type)) {
1151 if (min_index == -1)
1152 min_index = i;
1153 max_index = i;
1154 }
1155 }
1156 return std::pair<int, int>(min_index, max_index);
1157 }
1158
1159 void ShelfView::ConfigureChildView(views::View* view) {
1160 view->SetPaintToLayer(true);
1161 view->layer()->SetFillsBoundsOpaquely(false);
1162 }
1163
1164 void ShelfView::ToggleOverflowBubble() {
1165 /* TODO(msw): Restore functionality:
1166 if (IsShowingOverflowBubble()) {
1167 overflow_bubble_->Hide();
1168 return;
1169 }
1170
1171 if (!overflow_bubble_)
1172 overflow_bubble_.reset(new OverflowBubble());
1173
1174 ShelfView* overflow_view = new ShelfView(app);
1175 overflow_view->overflow_mode_ = true;
1176 overflow_view->Init();
1177 overflow_view->set_owner_overflow_bubble(overflow_bubble_.get());
1178 overflow_view->OnShelfAlignmentChanged();
1179 overflow_view->main_shelf_ = this;
1180 UpdateOverflowRange(overflow_view);
1181
1182 overflow_bubble_->Show(overflow_button_, overflow_view);
1183
1184 Shell::GetInstance()->UpdateShelfVisibility();*/
1185 }
1186
1187 void ShelfView::OnFadeOutAnimationEnded() {
1188 AnimateToIdealBounds();
1189 StartFadeInLastVisibleItem();
1190 }
1191
1192 void ShelfView::StartFadeInLastVisibleItem() {
1193 // If overflow button is visible and there is a valid new last item, fading
1194 // the new last item in after sliding animation is finished.
1195 /* TODO(msw): Restore functionality:
1196 if (overflow_button_->visible() && last_visible_index_ >= 0) {
1197 views::View* last_visible_view =
1198 view_model_.view_at(last_visible_index_);
1199 last_visible_view->layer()->SetOpacity(0);
1200 bounds_animator_->SetAnimationDelegate(
1201 last_visible_view,
1202 scoped_ptr<gfx::AnimationDelegate>(
1203 new StartFadeAnimationDelegate(this, last_visible_view)));
1204 }*/
1205 }
1206
1207 void ShelfView::UpdateOverflowRange(ShelfView* overflow_view) const {
1208 const int first_overflow_index = last_visible_index_ + 1;
1209 const int last_overflow_index = last_hidden_index_;
1210 DCHECK_LE(first_overflow_index, last_overflow_index);
1211 DCHECK_LT(last_overflow_index, view_model_.view_size());
1212
1213 /* TODO(msw): Restore functionality:
1214 overflow_view->first_visible_index_ = first_overflow_index;
1215 overflow_view->last_visible_index_ = last_overflow_index;*/
1216 }
1217
1218 bool ShelfView::ShouldHideTooltip(const gfx::Point& cursor_location) {
1219 gfx::Rect active_bounds;
1220
1221 for (int i = 0; i < child_count(); ++i) {
1222 views::View* child = child_at(i);
1223 /* TODO(msw): Restore functionality:
1224 if (child == overflow_button_)
1225 continue;*/
1226 if (!ShouldShowTooltipForView(child))
1227 continue;
1228
1229 gfx::Rect child_bounds = child->GetMirroredBounds();
1230 active_bounds.Union(child_bounds);
1231 }
1232
1233 return !active_bounds.Contains(cursor_location);
1234 }
1235
1236 gfx::Rect ShelfView::GetVisibleItemsBoundsInScreen() {
1237 gfx::Size preferred_size = GetPreferredSize();
1238 gfx::Point origin(GetMirroredXWithWidthInView(0, preferred_size.width()), 0);
1239 ConvertPointToScreen(this, &origin);
1240 return gfx::Rect(origin, preferred_size);
1241 }
1242
1243 gfx::Rect ShelfView::GetBoundsForDragInsertInScreen() {
1244 gfx::Size preferred_size;
1245 if (is_overflow_mode()) {
1246 /* TODO(msw): Restore functionality:
1247 DCHECK(owner_overflow_bubble_);
1248 gfx::Rect bubble_bounds =
1249 owner_overflow_bubble_->bubble_view()->GetBubbleBounds();
1250 preferred_size = bubble_bounds.size();*/
1251 } else {
1252 const int last_button_index = view_model_.view_size() - 1;
1253 gfx::Rect last_button_bounds =
1254 view_model_.view_at(last_button_index)->bounds();
1255 /* TODO(msw): Restore functionality:
1256 if (overflow_button_->visible() &&
1257 model_.GetItemIndexForType(TYPE_APP_PANEL) == -1) {
1258 // When overflow button is visible and shelf has no panel items,
1259 // last_button_bounds should be overflow button's bounds.
1260 last_button_bounds = overflow_button_->bounds();
1261 }*/
1262
1263 if (IsHorizontalAlignment()) {
1264 preferred_size = gfx::Size(last_button_bounds.right() + leading_inset_,
1265 kShelfSize);
1266 } else {
1267 preferred_size = gfx::Size(kShelfSize,
1268 last_button_bounds.bottom() + leading_inset_);
1269 }
1270 }
1271 gfx::Point origin(GetMirroredXWithWidthInView(0, preferred_size.width()), 0);
1272
1273 // In overflow mode, we should use OverflowBubbleView as a source for
1274 // converting |origin| to screen coordinates. When a scroll operation is
1275 // occurred in OverflowBubble, the bounds of ShelfView in OverflowBubble can
1276 // be changed.
1277 /* TODO(msw): Restore functionality:
1278 if (is_overflow_mode())
1279 ConvertPointToScreen(owner_overflow_bubble_->bubble_view(), &origin);
1280 else
1281 ConvertPointToScreen(this, &origin);*/
1282
1283 return gfx::Rect(origin, preferred_size);
1284 }
1285
1286 int ShelfView::CancelDrag(int modified_index) {
1287 FinalizeRipOffDrag(true);
1288 if (!drag_view_)
1289 return modified_index;
1290 bool was_dragging = dragging();
1291 int drag_view_index = view_model_.GetIndexOfView(drag_view_);
1292 drag_pointer_ = NONE;
1293 drag_view_ = nullptr;
1294 if (drag_view_index == modified_index) {
1295 // The view that was being dragged is being modified. Don't do anything.
1296 return modified_index;
1297 }
1298 if (!was_dragging)
1299 return modified_index;
1300
1301 // Restore previous position, tracking the position of the modified view.
1302 bool at_end = modified_index == view_model_.view_size();
1303 views::View* modified_view =
1304 (modified_index >= 0 && !at_end) ?
1305 view_model_.view_at(modified_index) : nullptr;
1306 model_.Move(drag_view_index, start_drag_index_);
1307
1308 // If the modified view will be at the end of the list, return the new end of
1309 // the list.
1310 if (at_end)
1311 return view_model_.view_size();
1312 return modified_view ? view_model_.GetIndexOfView(modified_view) : -1;
40 } 1313 }
41 1314
42 gfx::Size ShelfView::GetPreferredSize() const { 1315 gfx::Size ShelfView::GetPreferredSize() const {
43 return gfx::Size(1, 48); 1316 const_cast<ShelfView*>(this)->CalculateIdealBounds();
44 } 1317
45 1318 int last_button_index = is_overflow_mode() ?
46 views::View* ShelfView::GetContentsView() { 1319 last_visible_index_ : view_model_.view_size() - 1;
1320
1321 // When an item is dragged off from the overflow bubble, it is moved to last
1322 // position and and changed to invisible. Overflow bubble size should be
1323 // shrunk to fit only for visible items.
1324 // If |dragged_off_from_overflow_to_shelf_| is set, there will be no invisible
1325 // items in the shelf.
1326 if (is_overflow_mode() &&
1327 dragged_off_shelf_ &&
1328 !dragged_off_from_overflow_to_shelf_ &&
1329 RemovableByRipOff(view_model_.GetIndexOfView(drag_view_)) == REMOVABLE)
1330 last_button_index--;
1331
1332 const gfx::Rect last_button_bounds =
1333 last_button_index >= first_visible_index_ ?
1334 view_model_.ideal_bounds(last_button_index) :
1335 gfx::Rect(gfx::Size(kShelfSize, kShelfSize));
1336
1337 if (IsHorizontalAlignment())
1338 return gfx::Size(last_button_bounds.right() + leading_inset_, kShelfSize);
1339
1340 return gfx::Size(kShelfSize, last_button_bounds.bottom() + leading_inset_);
1341 }
1342
1343 void ShelfView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
1344 // This bounds change is produced by the shelf movement and all content has
1345 // to follow. Using an animation at that time would produce a time lag since
1346 // the animation of the BoundsAnimator has itself a delay before it arrives
1347 // at the required location. As such we tell the animator to go there
1348 // immediately.
1349 BoundsAnimatorDisabler disabler(bounds_animator_.get());
1350 LayoutToIdealBounds();
1351 /* TODO(msw): Restore functionality:
1352 FOR_EACH_OBSERVER(ShelfIconObserver, observers_,
1353 OnShelfIconPositionsChanged());
1354
1355 if (IsShowingOverflowBubble())
1356 overflow_bubble_->Hide();*/
1357 }
1358
1359 views::FocusTraversable* ShelfView::GetPaneFocusTraversable() {
47 return this; 1360 return this;
48 } 1361 }
49 1362
1363 void ShelfView::GetAccessibleState(ui::AXViewState* state) {
1364 state->role = ui::AX_ROLE_TOOLBAR;
1365 /* TODO(msw): Restore functionality:
1366 state->name = l10n_util::GetStringUTF16(IDS_ASH_SHELF_ACCESSIBLE_NAME);*/
1367 }
1368
1369 /* TODO(msw): Restore functionality:
1370 void ShelfView::OnGestureEvent(ui::GestureEvent* event) {
1371 aura::Window* target_window = static_cast<views::View*>(event->target())
1372 ->GetWidget()
1373 ->GetNativeWindow();
1374 if (gesture_handler_.ProcessGestureEvent(*event, target_window))
1375 event->StopPropagation();
1376 }*/
1377
1378 void ShelfView::ShelfItemAdded(int model_index) {
1379 {
1380 base::AutoReset<bool> cancelling_drag(
1381 &cancelling_drag_model_changed_, true);
1382 model_index = CancelDrag(model_index);
1383 }
1384 views::View* view = CreateViewForItem(model_.items()[model_index]);
1385 AddChildView(view);
1386 // Hide the view, it'll be made visible when the animation is done. Using
1387 // opacity 0 here to avoid messing with CalculateIdealBounds which touches
1388 // the view's visibility.
1389 view->layer()->SetOpacity(0);
1390 view_model_.Add(view, model_index);
1391
1392 // Give the button its ideal bounds. That way if we end up animating the
1393 // button before this animation completes it doesn't appear at some random
1394 // spot (because it was in the middle of animating from 0,0 0x0 to its
1395 // target).
1396 CalculateIdealBounds();
1397 view->SetBoundsRect(view_model_.ideal_bounds(model_index));
1398
1399 // The first animation moves all the views to their target position. |view|
1400 // is hidden, so it visually appears as though we are providing space for
1401 // it. When done we'll fade the view in.
1402 AnimateToIdealBounds();
1403 if (model_index <= last_visible_index_ ||
1404 model_index >= model_.FirstPanelIndex()) {
1405 bounds_animator_->SetAnimationDelegate(
1406 view,
1407 scoped_ptr<gfx::AnimationDelegate>(
1408 new StartFadeAnimationDelegate(this, view)));
1409 } else {
1410 // Undo the hiding if animation does not run.
1411 view->layer()->SetOpacity(1.0f);
1412 }
1413 }
1414
1415 void ShelfView::ShelfItemRemoved(int model_index, ShelfID id) {
1416 if (id == context_menu_id_)
1417 launcher_menu_runner_.reset();
1418 {
1419 base::AutoReset<bool> cancelling_drag(
1420 &cancelling_drag_model_changed_, true);
1421 model_index = CancelDrag(model_index);
1422 }
1423 views::View* view = view_model_.view_at(model_index);
1424 view_model_.Remove(model_index);
1425
1426 // When the overflow bubble is visible, the overflow range needs to be set
1427 // before CalculateIdealBounds() gets called. Otherwise CalculateIdealBounds()
1428 // could trigger a ShelfItemChanged() by hiding the overflow bubble and
1429 // since the overflow bubble is not yet synced with the ShelfModel this
1430 // could cause a crash.
1431 /* TODO(msw): Restore functionality:
1432 if (overflow_bubble_ && overflow_bubble_->IsShowing()) {
1433 last_hidden_index_ = std::min(last_hidden_index_,
1434 view_model_.view_size() - 1);
1435 UpdateOverflowRange(overflow_bubble_->shelf_view());
1436 }*/
1437
1438 if (view->visible()) {
1439 // The first animation fades out the view. When done we'll animate the rest
1440 // of the views to their target location.
1441 bounds_animator_->AnimateViewTo(view, view->bounds());
1442 bounds_animator_->SetAnimationDelegate(
1443 view,
1444 scoped_ptr<gfx::AnimationDelegate>(
1445 new FadeOutAnimationDelegate(this, view)));
1446 } else {
1447 // We don't need to show a fade out animation for invisible |view|. When an
1448 // item is ripped out from the shelf, its |view| is already invisible.
1449 AnimateToIdealBounds();
1450 }
1451
1452 // Close the tooltip because it isn't needed any longer and its anchor view
1453 // will be deleted soon.
1454 if (tooltip_.GetCurrentAnchorView() == view)
1455 tooltip_.Close();
1456 }
1457
1458 void ShelfView::ShelfItemChanged(int model_index, const ShelfItem& old_item) {
1459 const ShelfItem& item(model_.items()[model_index]);
1460 if (old_item.type != item.type) {
1461 // Type changed, swap the views.
1462 model_index = CancelDrag(model_index);
1463 scoped_ptr<views::View> old_view(view_model_.view_at(model_index));
1464 bounds_animator_->StopAnimatingView(old_view.get());
1465 // Removing and re-inserting a view in our view model will strip the ideal
1466 // bounds from the item. To avoid recalculation of everything the bounds
1467 // get remembered and restored after the insertion to the previous value.
1468 gfx::Rect old_ideal_bounds = view_model_.ideal_bounds(model_index);
1469 view_model_.Remove(model_index);
1470 views::View* new_view = CreateViewForItem(item);
1471 AddChildView(new_view);
1472 view_model_.Add(new_view, model_index);
1473 view_model_.set_ideal_bounds(model_index, old_ideal_bounds);
1474 new_view->SetBoundsRect(old_view->bounds());
1475 return;
1476 }
1477
1478 views::View* view = view_model_.view_at(model_index);
1479 switch (item.type) {
1480 case TYPE_BROWSER_SHORTCUT:
1481 // Fallthrough for the new Shelf since it needs to show the activation
1482 // change as well.
1483 case TYPE_APP_SHORTCUT:
1484 case TYPE_WINDOWED_APP:
1485 case TYPE_PLATFORM_APP:
1486 case TYPE_DIALOG:
1487 case TYPE_APP_PANEL: {
1488 CHECK_EQ(ShelfButton::kViewClassName, view->GetClassName());
1489 ShelfButton* button = static_cast<ShelfButton*>(view);
1490 ReflectItemStatus(item, button);
1491 // The browser shortcut is currently not a "real" item and as such the
1492 // the image is bogous as well. We therefore keep the image as is for it.
1493 if (item.type != TYPE_BROWSER_SHORTCUT)
1494 button->SetImage(item.image);
1495 button->SchedulePaint();
1496 break;
1497 }
1498 case TYPE_MOJO_APP: {
1499 CHECK_EQ(ShelfButton::kViewClassName, view->GetClassName());
1500 ShelfButton* button = static_cast<ShelfButton*>(view);
1501 button->SetTooltipText(item.title);
1502 break;
1503 }
1504
1505 default:
1506 break;
1507 }
1508 }
1509
1510 void ShelfView::ShelfItemMoved(int start_index, int target_index) {
1511 view_model_.Move(start_index, target_index);
1512 // When cancelling a drag due to a shelf item being added, the currently
1513 // dragged item is moved back to its initial position. AnimateToIdealBounds
1514 // will be called again when the new item is added to the |view_model_| but
1515 // at this time the |view_model_| is inconsistent with the |model_|.
1516 if (!cancelling_drag_model_changed_)
1517 AnimateToIdealBounds();
1518 }
1519
1520 void ShelfView::ShelfStatusChanged() {
1521 // Nothing to do here.
1522 }
1523
1524 ShelfAlignment ShelfView::GetAlignment() const {
1525 return alignment_;
1526 }
1527
1528 bool ShelfView::IsHorizontalAlignment() const {
1529 return alignment_ == SHELF_ALIGNMENT_BOTTOM ||
1530 alignment_ == SHELF_ALIGNMENT_TOP;
1531 }
1532
1533 void ShelfView::PointerPressedOnButton(views::View* view,
1534 Pointer pointer,
1535 const ui::LocatedEvent& event) {
1536 if (drag_view_)
1537 return;
1538
1539 int index = view_model_.GetIndexOfView(view);
1540 if (index == -1)
1541 return;
1542
1543 /* TODO(msw): Restore functionality:
1544 ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
1545 model_.items()[index].id);
1546 if (view_model_.view_size() <= 1 || !item_delegate->IsDraggable())
1547 return; // View is being deleted or not draggable, ignore request.*/
1548
1549 // Only when the repost event occurs on the same shelf item, we should ignore
1550 // the call in ShelfView::ButtonPressed(...).
1551 is_repost_event_ = IsRepostEvent(event) && (last_pressed_index_ == index);
1552
1553 CHECK_EQ(ShelfButton::kViewClassName, view->GetClassName());
1554 drag_view_ = static_cast<ShelfButton*>(view);
1555 drag_origin_ = gfx::Point(event.x(), event.y());
1556 /* TODO(msw): Restore functionality:
1557 UMA_HISTOGRAM_ENUMERATION("Ash.ShelfAlignmentUsage",
1558 layout_manager_->SelectValueForShelfAlignment(
1559 SHELF_ALIGNMENT_UMA_ENUM_VALUE_BOTTOM,
1560 SHELF_ALIGNMENT_UMA_ENUM_VALUE_LEFT,
1561 SHELF_ALIGNMENT_UMA_ENUM_VALUE_RIGHT,
1562 -1),
1563 SHELF_ALIGNMENT_UMA_ENUM_VALUE_COUNT);*/
1564 }
1565
1566 void ShelfView::PointerDraggedOnButton(views::View* view,
1567 Pointer pointer,
1568 const ui::LocatedEvent& event) {
1569 // To prepare all drag types (moving an item in the shelf and dragging off),
1570 // we should check the x-axis and y-axis offset.
1571 if (!dragging() && drag_view_ &&
1572 ((std::abs(event.x() - drag_origin_.x()) >= kMinimumDragDistance) ||
1573 (std::abs(event.y() - drag_origin_.y()) >= kMinimumDragDistance))) {
1574 PrepareForDrag(pointer, event);
1575 }
1576 if (drag_pointer_ == pointer)
1577 ContinueDrag(event);
1578 }
1579
1580 void ShelfView::PointerReleasedOnButton(views::View* view,
1581 Pointer pointer,
1582 bool canceled) {
1583 // Reset |is_repost_event| to false.
1584 is_repost_event_ = false;
1585
1586 if (canceled) {
1587 CancelDrag(-1);
1588 } else if (drag_pointer_ == pointer) {
1589 FinalizeRipOffDrag(false);
1590 drag_pointer_ = NONE;
1591 AnimateToIdealBounds();
1592 }
1593 // If the drag pointer is NONE, no drag operation is going on and the
1594 // drag_view can be released.
1595 if (drag_pointer_ == NONE)
1596 drag_view_ = nullptr;
1597 }
1598
1599 void ShelfView::MouseMovedOverButton(views::View* view) {
1600 if (!ShouldShowTooltipForView(view))
1601 return;
1602
1603 if (!tooltip_.IsVisible())
1604 tooltip_.ResetTimer();
1605 }
1606
1607 void ShelfView::MouseEnteredButton(views::View* view) {
1608 // TODO(msw): Fix the initial (0,0) mouse event (causes tooltip on startup).
1609 if (!ShouldShowTooltipForView(view))
1610 return;
1611
1612 if (tooltip_.IsVisible())
1613 tooltip_.ShowImmediately(view, GetAccessibleName(view));
1614 else
1615 tooltip_.ShowDelayed(view, GetAccessibleName(view));
1616 }
1617
1618 void ShelfView::MouseExitedButton(views::View* view) {
1619 if (!tooltip_.IsVisible())
1620 tooltip_.StopTimer();
1621 }
1622
1623 base::string16 ShelfView::GetAccessibleName(const views::View* view) {
1624 int view_index = view_model_.GetIndexOfView(view);
1625 // May be -1 while in the process of animating closed.
1626 if (view_index == -1)
1627 return base::string16();
1628 return model_.items()[view_index].title;
1629 }
1630
50 void ShelfView::ButtonPressed(views::Button* sender, const ui::Event& event) { 1631 void ShelfView::ButtonPressed(views::Button* sender, const ui::Event& event) {
51 user_window_controller_->FocusUserWindow(sender->tag()); 1632 // Do not handle mouse release during drag.
52 } 1633 if (dragging())
53 1634 return;
54 void ShelfView::OnUserWindowObserverAdded( 1635
55 mojo::Array<mash::wm::mojom::UserWindowPtr> user_windows) { 1636 /* TODO(msw): Restore functionality:
56 for (size_t i = 0; i < user_windows.size(); ++i) 1637 if (sender == overflow_button_) {
57 OnUserWindowAdded(std::move(user_windows[i])); 1638 ToggleOverflowBubble();
58 } 1639 shelf_button_pressed_metric_tracker_.ButtonPressed(
59 1640 event, sender, ShelfItemDelegate::kNoAction);
60 void ShelfView::OnUserWindowAdded(mash::wm::mojom::UserWindowPtr user_window) { 1641 return;
61 views::LabelButton* open_window_button = new views::LabelButton( 1642 }*/
62 this, user_window->window_title.To<base::string16>()); 1643
63 open_window_button->set_tag(user_window->window_id); 1644 int view_index = view_model_.GetIndexOfView(sender);
64 open_window_buttons_.push_back(open_window_button); 1645 // May be -1 while in the process of animating closed.
65 AddChildView(open_window_button); 1646 if (view_index == -1)
66 Layout(); 1647 return;
67 SchedulePaint(); 1648
68 } 1649 // If the menu was just closed by the same event as this one, we ignore
69 1650 // the call and don't open the menu again. See crbug.com/343005 for more
70 void ShelfView::OnUserWindowRemoved(uint32_t window_id) { 1651 // detail.
71 const size_t index = GetButtonIndexById(window_id); 1652 if (is_repost_event_)
72 if (index >= open_window_buttons_.size()) 1653 return;
73 return; 1654
74 1655 // Record the index for the last pressed shelf item.
75 views::LabelButton* button = open_window_buttons_[index]; 1656 last_pressed_index_ = view_index;
76 open_window_buttons_.erase(open_window_buttons_.begin() + index); 1657
77 RemoveChildView(button); 1658 // Don't activate the item twice on double-click. Otherwise the window starts
78 delete button; 1659 // animating open due to the first click, then immediately minimizes due to
79 Layout(); 1660 // the second click. The user most likely intended to open or minimize the
80 SchedulePaint(); 1661 // item once, not do both.
81 } 1662 if (event.flags() & ui::EF_IS_DOUBLE_CLICK)
82 1663 return;
83 void ShelfView::OnUserWindowTitleChanged(uint32_t window_id, 1664
84 const mojo::String& window_title) { 1665 {
85 const size_t index = GetButtonIndexById(window_id); 1666 /* TODO(msw): Restore functionality:
86 if (index >= open_window_buttons_.size()) 1667 ScopedTargetRootWindow scoped_target(
87 return; 1668 sender->GetWidget()->GetNativeView()->GetRootWindow());*/
88 1669 // Slow down activation animations if shift key is pressed.
89 open_window_buttons_[index]->SetText(window_title.To<base::string16>()); 1670 scoped_ptr<ui::ScopedAnimationDurationScaleMode> slowing_animations;
90 open_window_buttons_[index]->SetMinSize(gfx::Size()); 1671 if (event.IsShiftDown()) {
91 Layout(); 1672 slowing_animations.reset(new ui::ScopedAnimationDurationScaleMode(
92 SchedulePaint(); 1673 ui::ScopedAnimationDurationScaleMode::SLOW_DURATION));
1674 }
1675
1676 // Collect usage statistics before we decide what to do with the click.
1677 const ShelfItem& item(model_.items()[view_index]);
1678 switch (item.type) {
1679 case TYPE_APP_SHORTCUT:
1680 case TYPE_WINDOWED_APP:
1681 case TYPE_PLATFORM_APP:
1682 case TYPE_MOJO_APP:
1683 case TYPE_BROWSER_SHORTCUT:
1684 /* TODO(msw): Restore functionality:
1685 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
1686 UMA_LAUNCHER_CLICK_ON_APP);*/
1687 break;
1688
1689 case TYPE_APP_LIST:
1690 /* TODO(msw): Restore functionality:
1691 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
1692 UMA_LAUNCHER_CLICK_ON_APPLIST_BUTTON);*/
1693 break;
1694
1695 case TYPE_APP_PANEL:
1696 case TYPE_DIALOG:
1697 break;
1698
1699 case TYPE_UNDEFINED:
1700 NOTREACHED() << "ShelfItemType must be set.";
1701 break;
1702 }
1703
1704 // TODO(msw): Support GetShelfItemDelegate[Manager], actions, etc.
1705 if (item.type == TYPE_MOJO_APP)
1706 model_.user_window_controller()->FocusUserWindow(item.window_id);
1707
1708 /* TODO(msw): Restore functionality:
1709 ShelfItemDelegate::PerformedAction performed_action =
1710 item_manager_->GetShelfItemDelegate(model_.items()[view_index].id)
1711 ->ItemSelected(event);
1712
1713 shelf_button_pressed_metric_tracker_.ButtonPressed(event, sender,
1714 performed_action);
1715
1716 if (performed_action != ShelfItemDelegate::kNewWindowCreated)
1717 ShowListMenuForView(model_.items()[view_index], sender, event);*/
1718 }
1719 }
1720
1721 bool ShelfView::ShowListMenuForView(const ShelfItem& item,
1722 views::View* source,
1723 const ui::Event& event) {
1724 /* TODO(msw): Restore functionality:
1725 ShelfItemDelegate* item_delegate =
1726 item_manager_->GetShelfItemDelegate(item.id);
1727 scoped_ptr<ui::MenuModel> list_menu_model(
1728 item_delegate->CreateApplicationMenu(event.flags()));
1729
1730 Make sure we have a menu and it has at least two items in addition to the
1731 application title and the 3 spacing separators.
1732 if (!list_menu_model.get() || list_menu_model->GetItemCount() <= 5)
1733 return false;
1734
1735 ShowMenu(list_menu_model.get(),
1736 source,
1737 gfx::Point(),
1738 false,
1739 ui::GetMenuSourceTypeForEvent(event));*/
1740 return true;
1741 }
1742
1743 void ShelfView::ShowContextMenuForView(views::View* source,
1744 const gfx::Point& point,
1745 ui::MenuSourceType source_type) {
1746 int view_index = view_model_.GetIndexOfView(source);
1747 if (view_index == -1) {
1748 /* TODO(msw): Restore functionality:
1749 Shell::GetInstance()->ShowContextMenu(point, source_type);*/
1750 return;
1751 }
1752
1753 /* TODO(msw): Restore functionality:
1754 ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
1755 model_.items()[view_index].id);
1756 context_menu_model_.reset(item_delegate->CreateContextMenu(
1757 source->GetWidget()->GetNativeView()->GetRootWindow()));
1758 if (!context_menu_model_)
1759 return;
1760
1761 base::AutoReset<ShelfID> reseter(
1762 &context_menu_id_,
1763 view_index == -1 ? 0 : model_.items()[view_index].id);
1764
1765 ShowMenu(context_menu_model_.get(),
1766 source,
1767 point,
1768 true,
1769 source_type);*/
1770 }
1771
1772 void ShelfView::ShowMenu(ui::MenuModel* menu_model,
1773 views::View* source,
1774 const gfx::Point& click_point,
1775 bool context_menu,
1776 ui::MenuSourceType source_type) {
1777 closing_event_time_ = base::TimeDelta();
1778 launcher_menu_runner_.reset(new views::MenuRunner(
1779 menu_model, context_menu ? views::MenuRunner::CONTEXT_MENU : 0));
1780
1781 /* TODO(msw): Restore functionality:
1782 ScopedTargetRootWindow scoped_target(
1783 source->GetWidget()->GetNativeView()->GetRootWindow());*/
1784
1785 // Determine the menu alignment dependent on the shelf.
1786 views::MenuAnchorPosition menu_alignment = views::MENU_ANCHOR_TOPLEFT;
1787 gfx::Rect anchor_point = gfx::Rect(click_point, gfx::Size());
1788
1789 if (!context_menu) {
1790 // Application lists use a bubble.
1791 anchor_point = source->GetBoundsInScreen();
1792
1793 // It is possible to invoke the menu while it is sliding into view. To cover
1794 // that case, the screen coordinates are offsetted by the animation delta.
1795 gfx::Vector2d offset =
1796 source->GetWidget()->GetNativeWindow()->bounds().origin() -
1797 source->GetWidget()->GetNativeWindow()->GetTargetBounds().origin();
1798 anchor_point.set_x(anchor_point.x() - offset.x());
1799 anchor_point.set_y(anchor_point.y() - offset.y());
1800
1801 // Shelf items can have an asymmetrical border for spacing reasons.
1802 // Adjust anchor location for this.
1803 if (source->border())
1804 anchor_point.Inset(source->border()->GetInsets());
1805
1806 menu_alignment = SelectValueForShelfAlignment(
1807 views::MENU_ANCHOR_BUBBLE_ABOVE, views::MENU_ANCHOR_BUBBLE_RIGHT,
1808 views::MENU_ANCHOR_BUBBLE_LEFT, views::MENU_ANCHOR_BUBBLE_BELOW);
1809 }
1810 // If this gets deleted while we are in the menu, the shelf will be gone
1811 // as well.
1812 bool got_deleted = false;
1813 got_deleted_ = &got_deleted;
1814
1815 /* TODO(msw): Restore functionality:
1816 shelf->ForceUndimming(true);*/
1817 // NOTE: if you convert to HAS_MNEMONICS be sure and update menu building
1818 // code.
1819 if (launcher_menu_runner_->RunMenuAt(source->GetWidget(),
1820 nullptr,
1821 anchor_point,
1822 menu_alignment,
1823 source_type) ==
1824 views::MenuRunner::MENU_DELETED) {
1825 if (!got_deleted) {
1826 got_deleted_ = nullptr;
1827 /* TODO(msw): Restore functionality:
1828 shelf->ForceUndimming(false);*/
1829 }
1830 return;
1831 }
1832 got_deleted_ = nullptr;
1833 /* TODO(msw): Restore functionality:
1834 shelf->ForceUndimming(false);*/
1835
1836 // If it is a context menu and we are showing overflow bubble
1837 // we want to hide overflow bubble.
1838 /* TODO(msw): Restore functionality:
1839 if (owner_overflow_bubble_)
1840 owner_overflow_bubble_->HideBubbleAndRefreshButton();*/
1841
1842 // Unpinning an item will reset the |launcher_menu_runner_| before coming
1843 // here.
1844 if (launcher_menu_runner_)
1845 closing_event_time_ = launcher_menu_runner_->closing_event_time();
1846 /* TODO(msw): Restore functionality:
1847 Shell::GetInstance()->UpdateShelfVisibility();*/
1848 }
1849
1850 void ShelfView::OnBoundsAnimatorProgressed(views::BoundsAnimator* animator) {
1851 /* TODO(msw): Restore functionality:
1852 FOR_EACH_OBSERVER(ShelfIconObserver, observers_,
1853 OnShelfIconPositionsChanged());*/
1854 PreferredSizeChanged();
1855 }
1856
1857 void ShelfView::OnBoundsAnimatorDone(views::BoundsAnimator* animator) {
1858 if (snap_back_from_rip_off_view_ && animator == bounds_animator_.get()) {
1859 if (!animator->IsAnimating(snap_back_from_rip_off_view_)) {
1860 // Coming here the animation of the ShelfButton is finished and the
1861 // previously hidden status can be shown again. Since the button itself
1862 // might have gone away or changed locations we check that the button
1863 // is still in the shelf and show its status again.
1864 for (int index = 0; index < view_model_.view_size(); index++) {
1865 views::View* view = view_model_.view_at(index);
1866 if (view == snap_back_from_rip_off_view_) {
1867 CHECK_EQ(ShelfButton::kViewClassName, view->GetClassName());
1868 ShelfButton* button = static_cast<ShelfButton*>(view);
1869 button->ClearState(ShelfButton::STATE_HIDDEN);
1870 break;
1871 }
1872 }
1873 snap_back_from_rip_off_view_ = nullptr;
1874 }
1875 }
1876 }
1877
1878 bool ShelfView::IsRepostEvent(const ui::Event& event) {
1879 if (closing_event_time_ == base::TimeDelta())
1880 return false;
1881
1882 base::TimeDelta delta =
1883 base::TimeDelta(event.time_stamp() - closing_event_time_);
1884 closing_event_time_ = base::TimeDelta();
1885 // If the current (press down) event is a repost event, the time stamp of
1886 // these two events should be the same.
1887 return (delta.InMilliseconds() == 0);
1888 }
1889
1890 const ShelfItem* ShelfView::ShelfItemForView(const views::View* view) const {
1891 int view_index = view_model_.GetIndexOfView(view);
1892 if (view_index == -1)
1893 return nullptr;
1894 return &(model_.items()[view_index]);
1895 }
1896
1897 bool ShelfView::ShouldShowTooltipForView(const views::View* view) const {
1898 /* TODO(msw): Restore functionality:
1899 if (view == GetAppListButtonView() &&
1900 Shell::GetInstance()->GetAppListWindow())
1901 return false;
1902 const ShelfItem* item = ShelfItemForView(view);
1903 if (!item)
1904 return true;
1905 return item_manager_->GetShelfItemDelegate(item->id)->ShouldShowTooltip();*/
1906 return true;
1907 }
1908
1909 int ShelfView::CalculateShelfDistance(const gfx::Point& coordinate) const {
1910 const gfx::Rect bounds = GetBoundsInScreen();
1911 int distance = 0;
1912 switch (GetAlignment()) {
1913 case SHELF_ALIGNMENT_BOTTOM:
1914 distance = bounds.y() - coordinate.y();
1915 break;
1916 case SHELF_ALIGNMENT_LEFT:
1917 distance = coordinate.x() - bounds.right();
1918 break;
1919 case SHELF_ALIGNMENT_RIGHT:
1920 distance = bounds.x() - coordinate.x();
1921 break;
1922 case SHELF_ALIGNMENT_TOP:
1923 distance = coordinate.y() - bounds.bottom();
1924 break;
1925 }
1926 return distance > 0 ? distance : 0;
93 } 1927 }
94 1928
95 } // namespace shelf 1929 } // namespace shelf
96 } // namespace mash 1930 } // namespace mash
OLDNEW
« mash/shelf/shelf_tooltip_manager.cc ('K') | « mash/shelf/shelf_view.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698