#include "ui/views/bubble/bubble_frame_view.h"
#include <algorithm>
#include "ui/gfx/screen.h"
#include "ui/views/bubble/bubble_border.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/client_view.h"
namespace {
int GetOffScreenLength(const gfx::Rect& monitor_bounds,
const gfx::Rect& window_bounds,
bool vertical) {
if (monitor_bounds.IsEmpty() || monitor_bounds.Contains(window_bounds))
return 0;
if (vertical)
return std::max(0, monitor_bounds.y() - window_bounds.y()) +
std::max(0, window_bounds.bottom() - monitor_bounds.bottom());
return std::max(0, monitor_bounds.x() - window_bounds.x()) +
std::max(0, window_bounds.right() - monitor_bounds.right());
}
}
namespace views {
BubbleFrameView::BubbleFrameView(const gfx::Insets& margins,
BubbleBorder* border)
: bubble_border_(border),
content_margins_(margins) {
set_border(bubble_border_);
}
BubbleFrameView::~BubbleFrameView() {}
gfx::Rect BubbleFrameView::GetBoundsForClientView() const {
gfx::Insets margin;
bubble_border()->GetInsets(&margin);
margin += content_margins();
return gfx::Rect(margin.left(), margin.top(),
std::max(width() - margin.width(), 0),
std::max(height() - margin.height(), 0));
}
gfx::Rect BubbleFrameView::GetWindowBoundsForClientBounds(
const gfx::Rect& client_bounds) const {
return const_cast<BubbleFrameView*>(this)->GetUpdatedWindowBounds(
gfx::Rect(), client_bounds.size(), false);
}
int BubbleFrameView::NonClientHitTest(const gfx::Point& point) {
return GetWidget()->client_view()->NonClientHitTest(point);
}
gfx::Size BubbleFrameView::GetPreferredSize() {
gfx::Size client_size(GetWidget()->client_view()->GetPreferredSize());
return GetUpdatedWindowBounds(gfx::Rect(), client_size, false).size();
}
gfx::Rect BubbleFrameView::GetUpdatedWindowBounds(const gfx::Rect& anchor_rect,
gfx::Size client_size,
bool adjust_if_offscreen) {
client_size.Enlarge(content_margins_.width(), content_margins_.height());
const BubbleBorder::ArrowLocation arrow = bubble_border_->arrow_location();
if (adjust_if_offscreen && BubbleBorder::has_arrow(arrow)) {
if (!bubble_border_->is_arrow_at_center(arrow)) {
MirrorArrowIfOffScreen(true, anchor_rect, client_size);
MirrorArrowIfOffScreen(false, anchor_rect, client_size);
} else {
OffsetArrowIfOffScreen(anchor_rect, client_size);
}
}
return bubble_border_->GetBounds(anchor_rect, client_size);
}
void BubbleFrameView::SetBubbleBorder(BubbleBorder* border) {
bubble_border_ = border;
set_border(bubble_border_);
set_background(new views::BubbleBackground(border));
}
gfx::Rect BubbleFrameView::GetMonitorBounds(const gfx::Rect& rect) {
return gfx::Screen::GetDisplayNearestPoint(rect.CenterPoint()).work_area();
}
void BubbleFrameView::MirrorArrowIfOffScreen(
bool vertical,
const gfx::Rect& anchor_rect,
const gfx::Size& client_size) {
gfx::Rect monitor_rect(GetMonitorBounds(anchor_rect));
gfx::Rect window_bounds(bubble_border_->GetBounds(anchor_rect, client_size));
if (GetOffScreenLength(monitor_rect, window_bounds, vertical) > 0) {
BubbleBorder::ArrowLocation arrow = bubble_border()->arrow_location();
bubble_border_->set_arrow_location(
vertical ? BubbleBorder::vertical_mirror(arrow) :
BubbleBorder::horizontal_mirror(arrow));
gfx::Rect mirror_bounds =
bubble_border_->GetBounds(anchor_rect, client_size);
if (GetOffScreenLength(monitor_rect, mirror_bounds, vertical) >=
GetOffScreenLength(monitor_rect, window_bounds, vertical))
bubble_border_->set_arrow_location(arrow);
else
SchedulePaint();
}
}
void BubbleFrameView::OffsetArrowIfOffScreen(const gfx::Rect& anchor_rect,
const gfx::Size& client_size) {
BubbleBorder::ArrowLocation arrow = bubble_border()->arrow_location();
DCHECK(BubbleBorder::is_arrow_at_center(arrow));
bubble_border_->set_arrow_offset(0);
gfx::Rect window_bounds(bubble_border_->GetBounds(anchor_rect, client_size));
gfx::Rect monitor_rect(GetMonitorBounds(anchor_rect));
if (monitor_rect.IsEmpty() || monitor_rect.Contains(window_bounds))
return;
const bool is_horizontal = BubbleBorder::is_arrow_on_horizontal(arrow);
int offscreen_adjust = 0;
if (is_horizontal) {
if (window_bounds.x() < monitor_rect.x())
offscreen_adjust = monitor_rect.x() - window_bounds.x();
else if (window_bounds.right() > monitor_rect.right())
offscreen_adjust = monitor_rect.right() - window_bounds.right();
} else {
if (window_bounds.y() < monitor_rect.y())
offscreen_adjust = monitor_rect.y() - window_bounds.y();
else if (window_bounds.bottom() > monitor_rect.bottom())
offscreen_adjust = monitor_rect.bottom() - window_bounds.bottom();
}
bubble_border_->set_arrow_offset(
bubble_border_->GetArrowOffset(window_bounds.size()) - offscreen_adjust);
if (offscreen_adjust)
SchedulePaint();
}
}