// 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 "chrome/browser/ui/views/bubble/border_contents.h" #include <algorithm> #include "base/logging.h" #include "chrome/browser/ui/window_sizer.h" #include "third_party/skia/include/core/SkPaint.h" void BorderContents::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 BorderContents::SetBackgroundColor(SkColor color) { bubble_border_->set_background_color(color); } void BorderContents::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(kLeftMargin + kRightMargin, kTopMargin + kBottomMargin); // 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); contents_bounds->Inset(insets.left() + kLeftMargin, insets.top() + kTopMargin, insets.right() + kRightMargin, insets.bottom() + kBottomMargin); } gfx::Rect BorderContents::GetMonitorBounds(const gfx::Rect& rect) { scoped_ptr<WindowSizer::MonitorInfoProvider> monitor_provider( WindowSizer::CreateDefaultMonitorInfoProvider()); return monitor_provider->GetMonitorWorkAreaMatching(rect); } void BorderContents::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); } } } // static bool BorderContents::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; } // static int BorderContents::GetInsetsLength(const gfx::Insets& insets, bool vertical) { return vertical ? insets.height() : insets.width(); }