summaryrefslogtreecommitdiffstats
path: root/views/bubble/border_contents_view.cc
diff options
context:
space:
mode:
Diffstat (limited to 'views/bubble/border_contents_view.cc')
-rw-r--r--views/bubble/border_contents_view.cc177
1 files changed, 177 insertions, 0 deletions
diff --git a/views/bubble/border_contents_view.cc b/views/bubble/border_contents_view.cc
new file mode 100644
index 0000000..1582751
--- /dev/null
+++ b/views/bubble/border_contents_view.cc
@@ -0,0 +1,177 @@
+// Copyright (c) 2011 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 "views/bubble/border_contents_view.h"
+
+#include <algorithm>
+
+#include "ui/gfx/screen.h"
+
+static const int kTopMargin = 6;
+static const int kLeftMargin = 6;
+static const int kBottomMargin = 6;
+static const int kRightMargin = 6;
+
+namespace {
+
+// Computes how much |window_bounds| is off-screen of the monitor bounds
+// |monitor_bounds| and puts the values in |offscreen_insets|.
+// Returns false if |window_bounds| is actually contained in |monitor_bounds|,
+// in which case |offscreen_insets| is not modified.
+bool ComputeOffScreenInsets(const gfx::Rect& monitor_bounds,
+ const gfx::Rect& window_bounds,
+ gfx::Insets* offscreen_insets) {
+ if (monitor_bounds.Contains(window_bounds))
+ return false;
+
+ if (!offscreen_insets)
+ return true;
+
+ // window_bounds
+ // +-------------------------------+
+ // | top |
+ // | +----------------+ |
+ // | left | monitor_bounds | right |
+ // | +----------------+ |
+ // | bottom |
+ // +-------------------------------+
+ int top = std::max(0, monitor_bounds.y() - window_bounds.y());
+ int left = std::max(0, monitor_bounds.x() - window_bounds.x());
+ int bottom = std::max(0, window_bounds.bottom() - monitor_bounds.bottom());
+ int right = std::max(0, window_bounds.right() - monitor_bounds.right());
+
+ offscreen_insets->Set(top, left, bottom, right);
+ return true;
+}
+
+// Convenience method that returns the height of |insets| if |vertical| is
+// true, its width otherwise.
+int GetInsetsLength(const gfx::Insets& insets, bool vertical) {
+ return vertical ? insets.height() : insets.width();
+}
+
+} // namespace
+
+namespace views {
+
+BorderContentsView::BorderContentsView()
+ : bubble_border_(NULL),
+ content_margins_(kTopMargin, kLeftMargin, kBottomMargin, kRightMargin) {
+}
+
+BorderContentsView::BorderContentsView(int top_margin,
+ int left_margin,
+ int bottom_margin,
+ int right_margin)
+ : bubble_border_(NULL),
+ content_margins_(top_margin, left_margin, bottom_margin, right_margin) {
+}
+
+BorderContentsView::~BorderContentsView() {}
+
+void BorderContentsView::Init() {
+ // Default arrow location.
+ BubbleBorder::ArrowLocation arrow_location =
+ BubbleBorder::TOP_LEFT;
+ if (base::i18n::IsRTL())
+ arrow_location = BubbleBorder::horizontal_mirror(arrow_location);
+ DCHECK(!bubble_border_);
+
+ bubble_border_ = new BubbleBorder(arrow_location);
+ set_border(bubble_border_);
+ set_background(new BubbleBackground(bubble_border_));
+}
+
+void BorderContentsView::SetBackgroundColor(SkColor color) {
+ bubble_border_->set_background_color(color);
+}
+
+void BorderContentsView::SizeAndGetBounds(
+ const gfx::Rect& position_relative_to,
+ BubbleBorder::ArrowLocation arrow_location,
+ bool allow_bubble_offscreen,
+ const gfx::Size& contents_size,
+ gfx::Rect* contents_bounds,
+ gfx::Rect* window_bounds) {
+ if (base::i18n::IsRTL())
+ arrow_location = BubbleBorder::horizontal_mirror(arrow_location);
+ bubble_border_->set_arrow_location(arrow_location);
+ // Set the border.
+ set_border(bubble_border_);
+
+ // Give the contents a margin.
+ gfx::Size local_contents_size(contents_size);
+ local_contents_size.Enlarge(content_margins_.width(),
+ content_margins_.height());
+
+ // Try putting the arrow in its initial location, and calculating the bounds.
+ *window_bounds =
+ bubble_border_->GetBounds(position_relative_to, local_contents_size);
+ if (!allow_bubble_offscreen) {
+ gfx::Rect monitor_bounds = GetMonitorBounds(position_relative_to);
+ if (!monitor_bounds.IsEmpty()) {
+ // Try to resize vertically if this does not fit on the screen.
+ MirrorArrowIfOffScreen(true, // |vertical|.
+ position_relative_to, monitor_bounds,
+ local_contents_size, &arrow_location,
+ window_bounds);
+ // Then try to resize horizontally if it still does not fit on the screen.
+ MirrorArrowIfOffScreen(false, // |vertical|.
+ position_relative_to, monitor_bounds,
+ local_contents_size, &arrow_location,
+ window_bounds);
+ }
+ }
+
+ // Calculate the bounds of the contained contents (in window coordinates) by
+ // subtracting the border dimensions and margin amounts.
+ *contents_bounds = gfx::Rect(gfx::Point(), window_bounds->size());
+ gfx::Insets insets;
+ bubble_border_->GetInsets(&insets);
+ insets += content_margins_;
+ contents_bounds->Inset(insets);
+}
+
+gfx::Rect BorderContentsView::GetMonitorBounds(const gfx::Rect& rect) {
+ return gfx::Screen::GetMonitorWorkAreaNearestPoint(rect.CenterPoint());
+}
+
+void BorderContentsView::MirrorArrowIfOffScreen(
+ bool vertical,
+ const gfx::Rect& position_relative_to,
+ const gfx::Rect& monitor_bounds,
+ const gfx::Size& local_contents_size,
+ BubbleBorder::ArrowLocation* arrow_location,
+ gfx::Rect* window_bounds) {
+ // If the bounds don't fit, move the arrow to its mirrored position to see if
+ // it improves things.
+ gfx::Insets offscreen_insets;
+ if (ComputeOffScreenInsets(monitor_bounds, *window_bounds,
+ &offscreen_insets) &&
+ GetInsetsLength(offscreen_insets, vertical) > 0) {
+ BubbleBorder::ArrowLocation original_arrow_location =
+ *arrow_location;
+ *arrow_location =
+ vertical ? BubbleBorder::vertical_mirror(*arrow_location) :
+ BubbleBorder::horizontal_mirror(*arrow_location);
+
+ // Change the arrow and get the new bounds.
+ bubble_border_->set_arrow_location(*arrow_location);
+ *window_bounds = bubble_border_->GetBounds(position_relative_to,
+ local_contents_size);
+ gfx::Insets new_offscreen_insets;
+ // If there is more of the window offscreen, we'll keep the old arrow.
+ if (ComputeOffScreenInsets(monitor_bounds, *window_bounds,
+ &new_offscreen_insets) &&
+ GetInsetsLength(new_offscreen_insets, vertical) >=
+ GetInsetsLength(offscreen_insets, vertical)) {
+ *arrow_location = original_arrow_location;
+ bubble_border_->set_arrow_location(*arrow_location);
+ *window_bounds = bubble_border_->GetBounds(position_relative_to,
+ local_contents_size);
+ }
+ }
+}
+
+} // namespace views