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

Unified Diff: ash/launcher/overflow_bubble.cc

Issue 10659003: ash: Add launcher overflow bubble. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase + fix clang 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ash/launcher/overflow_bubble.h ('k') | ash/wm/shelf_layout_manager.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ash/launcher/overflow_bubble.cc
diff --git a/ash/launcher/overflow_bubble.cc b/ash/launcher/overflow_bubble.cc
new file mode 100644
index 0000000000000000000000000000000000000000..e335f5b2bb00a6c3a55c6535d9a0a267b2204b1c
--- /dev/null
+++ b/ash/launcher/overflow_bubble.cc
@@ -0,0 +1,291 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/launcher/overflow_bubble.h"
+
+#include <algorithm>
+
+#include "ash/launcher/launcher_types.h"
+#include "ash/launcher/launcher_view.h"
+#include "ui/gfx/screen.h"
+#include "ui/views/bubble/bubble_delegate.h"
+#include "ui/views/bubble/bubble_frame_view.h"
+
+namespace ash {
+namespace internal {
+
+namespace {
+
+// This should be the same color as the darkest launcher bar.
+const SkColor kLauncherColor = SkColorSetARGB(0x80, 0, 0, 0);
+
+// Max bubble size to screen size ratio.
+const float kMaxBubbleSizeToScreenRatio = 0.5f;
+
+// Inner padding in pixels for launcher view inside bubble.
+const int kPadding = 2;
+
+// Padding space in pixels between LauncherView's left/top edge to its contents.
+const int kLauncherViewLeadingInset = 8;
+
+// Gets arrow location based on shelf alignment.
+views::BubbleBorder::ArrowLocation GetBubbleArrowLocation(
+ ShelfAlignment shelf_alignment) {
+ switch (shelf_alignment) {
+ case ash::SHELF_ALIGNMENT_BOTTOM:
+ return views::BubbleBorder::BOTTOM_LEFT;
+ case ash::SHELF_ALIGNMENT_LEFT:
+ return views::BubbleBorder::LEFT_TOP;
+ case ash::SHELF_ALIGNMENT_RIGHT:
+ return views::BubbleBorder::RIGHT_TOP;
+ default:
+ NOTREACHED() << "Unknown shelf alignment " << shelf_alignment;
+ return views::BubbleBorder::BOTTOM_LEFT;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// OverflowBubbleView
+// OverflowBubbleView hosts a LauncherView to display overflown items.
+
+class OverflowBubbleView : public views::BubbleDelegateView {
+ public:
+ OverflowBubbleView();
+ virtual ~OverflowBubbleView();
+
+ void InitOverflowBubble(LauncherDelegate* delegate,
+ LauncherModel* model,
+ views::View* anchor,
+ ShelfAlignment shelf_alignment,
+ int overflow_start_index);
+
+ private:
+ bool is_horizontal_alignment() const {
+ return shelf_alignment_ == SHELF_ALIGNMENT_BOTTOM;
+ }
+
+ const gfx::Size GetContentsSize() const {
+ return static_cast<views::View*>(launcher_view_)->GetPreferredSize();
+ }
+
+ void ScrollByXOffset(int x_offset);
+ void ScrollByYOffset(int y_offset);
+
+ // views::View overrides:
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+ virtual void Layout() OVERRIDE;
+ virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE;
+ virtual bool OnMouseWheel(const views::MouseWheelEvent& event) OVERRIDE;
+ virtual bool OnScrollEvent(const views::ScrollEvent & event) OVERRIDE;
+
+ // views::BubbleDelegate overrides:
+ virtual gfx::Rect GetBubbleBounds() OVERRIDE;
+
+ ShelfAlignment shelf_alignment_;
+ LauncherView* launcher_view_; // Owned by views hierarchy.
+ gfx::Point scroll_offset_;
+
+ DISALLOW_COPY_AND_ASSIGN(OverflowBubbleView);
+};
+
+OverflowBubbleView::OverflowBubbleView()
+ : shelf_alignment_(SHELF_ALIGNMENT_BOTTOM),
+ launcher_view_(NULL) {
+}
+
+OverflowBubbleView::~OverflowBubbleView() {
+}
+
+void OverflowBubbleView::InitOverflowBubble(LauncherDelegate* delegate,
+ LauncherModel* model,
+ views::View* anchor,
+ ShelfAlignment shelf_alignment,
+ int overflow_start_index) {
+ shelf_alignment_ = shelf_alignment;
+
+ // Makes bubble view has a layer and clip its children layers.
+ SetPaintToLayer(true);
+ SetFillsBoundsOpaquely(false);
+ layer()->SetMasksToBounds(true);
+
+ launcher_view_ = new LauncherView(model, delegate, NULL);
+ launcher_view_->set_first_visible_index(overflow_start_index);
+ launcher_view_->set_leading_inset(kLauncherViewLeadingInset);
+ launcher_view_->Init();
+ launcher_view_->SetAlignment(shelf_alignment);
+ AddChildView(launcher_view_);
+
+ set_anchor_view(anchor);
+ set_arrow_location(GetBubbleArrowLocation(shelf_alignment));
+ set_background(NULL);
+ set_color(kLauncherColor);
+ set_margin(kPadding);
+ set_move_with_anchor(true);
+ views::BubbleDelegateView::CreateBubble(this);
+}
+
+void OverflowBubbleView::ScrollByXOffset(int x_offset) {
+ const gfx::Rect visible_bounds(GetContentsBounds());
+ const gfx::Size contents_size(GetContentsSize());
+
+ int x = std::min(contents_size.width() - visible_bounds.width(),
+ std::max(0, scroll_offset_.x() + x_offset));
+ scroll_offset_.set_x(x);
+}
+
+void OverflowBubbleView::ScrollByYOffset(int y_offset) {
+ const gfx::Rect visible_bounds(GetContentsBounds());
+ const gfx::Size contents_size(GetContentsSize());
+
+ int y = std::min(contents_size.height() - visible_bounds.height(),
+ std::max(0, scroll_offset_.y() + y_offset));
+ scroll_offset_.set_y(y);
+}
+
+gfx::Size OverflowBubbleView::GetPreferredSize() {
+ gfx::Size preferred_size = GetContentsSize();
+
+ const gfx::Rect monitor_rect = gfx::Screen::GetDisplayNearestPoint(
+ GetAnchorRect().CenterPoint()).work_area();
+ if (!monitor_rect.IsEmpty()) {
+ if (is_horizontal_alignment()) {
+ preferred_size.set_width(std::min(
+ preferred_size.width(),
+ static_cast<int>(monitor_rect.width() *
+ kMaxBubbleSizeToScreenRatio)));
+ } else {
+ preferred_size.set_height(std::min(
+ preferred_size.height(),
+ static_cast<int>(monitor_rect.height() *
+ kMaxBubbleSizeToScreenRatio)));
+ }
+ }
+
+ return preferred_size;
+}
+
+void OverflowBubbleView::Layout() {
+ const gfx::Point origin(-scroll_offset_.x(), -scroll_offset_.y());
+ launcher_view_->SetBoundsRect(gfx::Rect(origin, GetContentsSize()));
+}
+
+void OverflowBubbleView::ChildPreferredSizeChanged(views::View* child) {
+ // Ensures |launch_view_| is still visible.
+ ScrollByXOffset(0);
+ ScrollByYOffset(0);
+ Layout();
+
+ SizeToContents();
+}
+
+bool OverflowBubbleView::OnMouseWheel(const views::MouseWheelEvent& event) {
+ if (is_horizontal_alignment())
+ ScrollByXOffset(-event.offset());
+ else
+ ScrollByYOffset(-event.offset());
+ Layout();
+
+ return true;
+}
+
+bool OverflowBubbleView::OnScrollEvent(const views::ScrollEvent & event) {
+ ScrollByXOffset(-event.x_offset());
+ ScrollByYOffset(-event.y_offset());
+ Layout();
+ return true;
+}
+
+gfx::Rect OverflowBubbleView::GetBubbleBounds() {
+ views::BubbleBorder* border = GetBubbleFrameView()->bubble_border();
+ gfx::Insets bubble_insets;
+ border->GetInsets(&bubble_insets);
+
+ const int border_size =
+ views::BubbleBorder::is_arrow_on_horizontal(arrow_location()) ?
+ bubble_insets.left() : bubble_insets.top();
+ const int arrow_offset = border_size + kPadding + kLauncherViewLeadingInset +
+ kLauncherPreferredSize / 2;
+
+ const gfx::Size content_size = GetPreferredSize();
+ border->SetArrowOffset(arrow_offset, content_size);
+
+ const gfx::Rect anchor_rect = GetAnchorRect();
+ gfx::Rect bubble_rect = GetBubbleFrameView()->GetUpdatedWindowBounds(
+ anchor_rect,
+ content_size,
+ false);
+
+ gfx::Rect monitor_rect = gfx::Screen::GetDisplayNearestPoint(
+ anchor_rect.CenterPoint()).work_area();
+
+ int offset = 0;
+ if (views::BubbleBorder::is_arrow_on_horizontal(arrow_location())) {
+ if (bubble_rect.x() < monitor_rect.x())
+ offset = monitor_rect.x() - bubble_rect.x();
+ else if (bubble_rect.right() > monitor_rect.right())
+ offset = monitor_rect.right() - bubble_rect.right();
+
+ bubble_rect.Offset(offset, 0);
+ border->SetArrowOffset(anchor_rect.CenterPoint().x() - bubble_rect.x(),
+ content_size);
+ } else {
+ if (bubble_rect.y() < monitor_rect.y())
+ offset = monitor_rect.y() - bubble_rect.y();
+ else if (bubble_rect.bottom() > monitor_rect.bottom())
+ offset = monitor_rect.bottom() - bubble_rect.bottom();
+
+ bubble_rect.Offset(0, offset);
+ border->SetArrowOffset(anchor_rect.CenterPoint().y() - bubble_rect.y(),
+ content_size);
+ }
+
+ GetBubbleFrameView()->SchedulePaint();
+ return bubble_rect;
+}
+
+} // namespace
+
+OverflowBubble::OverflowBubble()
+ : bubble_(NULL) {
+}
+
+OverflowBubble::~OverflowBubble() {
+ Hide();
+}
+
+void OverflowBubble::Show(LauncherDelegate* delegate,
+ LauncherModel* model,
+ views::View* anchor,
+ ShelfAlignment shelf_alignment,
+ int overflow_start_index) {
+ Hide();
+
+ OverflowBubbleView* bubble_view = new OverflowBubbleView();
+ bubble_view->InitOverflowBubble(delegate,
+ model,
+ anchor,
+ shelf_alignment,
+ overflow_start_index);
+
+ bubble_ = bubble_view;
+ bubble_->GetWidget()->AddObserver(this);
+ bubble_->GetWidget()->Show();
+}
+
+void OverflowBubble::Hide() {
+ if (!IsShowing())
+ return;
+
+ bubble_->GetWidget()->RemoveObserver(this);
+ bubble_->GetWidget()->Close();
+ bubble_ = NULL;
+}
+
+void OverflowBubble::OnWidgetClosing(views::Widget* widget) {
+ DCHECK(widget == bubble_->GetWidget());
+ bubble_ = NULL;
+}
+
+} // namespace internal
+} // namespace ash
« no previous file with comments | « ash/launcher/overflow_bubble.h ('k') | ash/wm/shelf_layout_manager.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698