This source file includes following definitions.
- GetOffScreenLength
- GetTitleInsets
- titlebar_extra_view_
- GetBoundsForClientView
- GetWindowBoundsForClientBounds
- NonClientHitTest
- GetWindowMask
- ResetWindowControls
- UpdateWindowIcon
- UpdateWindowTitle
- GetInsets
- GetPreferredSize
- GetMinimumSize
- Layout
- GetClassName
- ChildPreferredSizeChanged
- OnThemeChanged
- ButtonPressed
- SetBubbleBorder
- SetTitlebarExtraView
- GetUpdatedWindowBounds
- GetAvailableScreenBounds
- MirrorArrowIfOffScreen
- OffsetArrowIfOffScreen
- GetSizeForClientSize
#include "ui/views/bubble/bubble_frame_view.h"
#include <algorithm>
#include "grit/ui_resources.h"
#include "ui/base/hit_test.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/path.h"
#include "ui/gfx/screen.h"
#include "ui/gfx/skia_util.h"
#include "ui/views/bubble/bubble_border.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/views/window/client_view.h"
namespace {
const int kTitleTopInset = 12;
const int kTitleLeftInset = 19;
const int kTitleBottomInset = 12;
int GetOffScreenLength(const gfx::Rect& available_bounds,
const gfx::Rect& window_bounds,
bool vertical) {
if (available_bounds.IsEmpty() || available_bounds.Contains(window_bounds))
return 0;
if (vertical)
return std::max(0, available_bounds.y() - window_bounds.y()) +
std::max(0, window_bounds.bottom() - available_bounds.bottom());
return std::max(0, available_bounds.x() - window_bounds.x()) +
std::max(0, window_bounds.right() - available_bounds.right());
}
}
namespace views {
const char BubbleFrameView::kViewClassName[] = "BubbleFrameView";
gfx::Insets BubbleFrameView::GetTitleInsets() {
return gfx::Insets(kTitleTopInset, kTitleLeftInset, kTitleBottomInset, 0);
}
BubbleFrameView::BubbleFrameView(const gfx::Insets& content_margins)
: bubble_border_(NULL),
content_margins_(content_margins),
title_(NULL),
close_(NULL),
titlebar_extra_view_(NULL) {
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
title_ = new Label(base::string16(),
rb.GetFontList(ui::ResourceBundle::MediumFont));
title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
AddChildView(title_);
close_ = new LabelButton(this, base::string16());
close_->SetImage(CustomButton::STATE_NORMAL,
*rb.GetImageNamed(IDR_CLOSE_DIALOG).ToImageSkia());
close_->SetImage(CustomButton::STATE_HOVERED,
*rb.GetImageNamed(IDR_CLOSE_DIALOG_H).ToImageSkia());
close_->SetImage(CustomButton::STATE_PRESSED,
*rb.GetImageNamed(IDR_CLOSE_DIALOG_P).ToImageSkia());
close_->SetSize(close_->GetPreferredSize());
close_->SetBorder(scoped_ptr<Border>());
close_->SetVisible(false);
AddChildView(close_);
}
BubbleFrameView::~BubbleFrameView() {}
gfx::Rect BubbleFrameView::GetBoundsForClientView() const {
gfx::Rect client_bounds = GetLocalBounds();
client_bounds.Inset(GetInsets());
client_bounds.Inset(bubble_border_->GetInsets());
return client_bounds;
}
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) {
if (!bounds().Contains(point))
return HTNOWHERE;
if (close_->visible() && close_->GetMirroredBounds().Contains(point))
return HTCLOSE;
if (GetWidget()->widget_delegate()->AsDialogDelegate()) {
gfx::Rect sys_rect(0, 0, title_->x(), title_->y());
sys_rect.set_origin(gfx::Point(GetMirroredXForRect(sys_rect), 0));
if (sys_rect.Contains(point))
return HTSYSMENU;
if (point.y() < title_->bounds().bottom())
return HTCAPTION;
}
return GetWidget()->client_view()->NonClientHitTest(point);
}
void BubbleFrameView::GetWindowMask(const gfx::Size& size,
gfx::Path* window_mask) {
if ((bubble_border_->arrow() != BubbleBorder::NONE &&
bubble_border_->arrow() != BubbleBorder::FLOAT) ||
(bubble_border_->shadow() != BubbleBorder::SMALL_SHADOW &&
bubble_border_->shadow() != BubbleBorder::NO_SHADOW_OPAQUE_BORDER))
return;
static const int kBorderStrokeSize = 1;
static const SkScalar kCornerRadius = SkIntToScalar(6);
const gfx::Insets border_insets = bubble_border_->GetInsets();
SkRect rect = { SkIntToScalar(border_insets.left() - kBorderStrokeSize),
SkIntToScalar(border_insets.top() - kBorderStrokeSize),
SkIntToScalar(size.width() - border_insets.right() +
kBorderStrokeSize),
SkIntToScalar(size.height() - border_insets.bottom() +
kBorderStrokeSize) };
if (bubble_border_->shadow() == BubbleBorder::NO_SHADOW_OPAQUE_BORDER) {
window_mask->addRoundRect(rect, kCornerRadius, kCornerRadius);
} else {
static const int kBottomBorderShadowSize = 2;
rect.fBottom += SkIntToScalar(kBottomBorderShadowSize);
window_mask->addRect(rect);
}
}
void BubbleFrameView::ResetWindowControls() {
close_->SetVisible(GetWidget()->widget_delegate()->ShouldShowCloseButton());
}
void BubbleFrameView::UpdateWindowIcon() {}
void BubbleFrameView::UpdateWindowTitle() {
title_->SetText(GetWidget()->widget_delegate()->ShouldShowWindowTitle() ?
GetWidget()->widget_delegate()->GetWindowTitle() : base::string16());
ResetWindowControls();
}
gfx::Insets BubbleFrameView::GetInsets() const {
gfx::Insets insets = content_margins_;
const int title_height = title_->text().empty() ? 0 :
title_->GetPreferredSize().height() + kTitleTopInset + kTitleBottomInset;
const int close_height = close_->visible() ? close_->height() : 0;
insets += gfx::Insets(std::max(title_height, close_height), 0, 0, 0);
return insets;
}
gfx::Size BubbleFrameView::GetPreferredSize() {
return GetSizeForClientSize(GetWidget()->client_view()->GetPreferredSize());
}
gfx::Size BubbleFrameView::GetMinimumSize() {
return GetSizeForClientSize(GetWidget()->client_view()->GetMinimumSize());
}
void BubbleFrameView::Layout() {
gfx::Rect bounds(GetContentsBounds());
if (bounds.IsEmpty())
return;
bounds.Inset(0, 0, close_->width() + 1, 0);
close_->SetPosition(gfx::Point(bounds.right(), bounds.y() + 2));
gfx::Rect title_bounds(bounds);
title_bounds.Inset(kTitleLeftInset, kTitleTopInset, 0, 0);
gfx::Size title_size(title_->GetPreferredSize());
const int title_width = std::max(0, close_->bounds().x() - title_bounds.x());
title_size.SetToMin(gfx::Size(title_width, title_size.height()));
title_bounds.set_size(title_size);
title_->SetBoundsRect(title_bounds);
if (titlebar_extra_view_) {
const int extra_width = close_->bounds().x() - title_->bounds().right();
gfx::Size size = titlebar_extra_view_->GetPreferredSize();
size.SetToMin(gfx::Size(std::max(0, extra_width), size.height()));
gfx::Rect titlebar_extra_view_bounds(
bounds.right() - size.width(),
title_bounds.y(),
size.width(),
title_bounds.height());
titlebar_extra_view_bounds.Subtract(title_bounds);
titlebar_extra_view_->SetBoundsRect(titlebar_extra_view_bounds);
}
}
const char* BubbleFrameView::GetClassName() const {
return kViewClassName;
}
void BubbleFrameView::ChildPreferredSizeChanged(View* child) {
if (child == titlebar_extra_view_ || child == title_)
Layout();
}
void BubbleFrameView::OnThemeChanged() {
UpdateWindowTitle();
ResetWindowControls();
UpdateWindowIcon();
}
void BubbleFrameView::ButtonPressed(Button* sender, const ui::Event& event) {
if (sender == close_)
GetWidget()->Close();
}
void BubbleFrameView::SetBubbleBorder(scoped_ptr<BubbleBorder> border) {
bubble_border_ = border.get();
SetBorder(border.PassAs<Border>());
set_background(new views::BubbleBackground(bubble_border_));
}
void BubbleFrameView::SetTitlebarExtraView(View* view) {
DCHECK(view);
DCHECK(!titlebar_extra_view_);
AddChildView(view);
titlebar_extra_view_ = view;
}
gfx::Rect BubbleFrameView::GetUpdatedWindowBounds(const gfx::Rect& anchor_rect,
gfx::Size client_size,
bool adjust_if_offscreen) {
gfx::Size size(GetSizeForClientSize(client_size));
const BubbleBorder::Arrow arrow = bubble_border_->arrow();
if (adjust_if_offscreen && BubbleBorder::has_arrow(arrow)) {
if (!bubble_border_->is_arrow_at_center(arrow)) {
MirrorArrowIfOffScreen(true, anchor_rect, size);
MirrorArrowIfOffScreen(false, anchor_rect, size);
} else {
const bool mirror_vertical = BubbleBorder::is_arrow_on_horizontal(arrow);
MirrorArrowIfOffScreen(mirror_vertical, anchor_rect, size);
OffsetArrowIfOffScreen(anchor_rect, size);
}
}
return bubble_border_->GetBounds(anchor_rect, size);
}
gfx::Rect BubbleFrameView::GetAvailableScreenBounds(const gfx::Rect& rect) {
return gfx::Screen::GetNativeScreen()->GetDisplayNearestPoint(
rect.CenterPoint()).work_area();
}
void BubbleFrameView::MirrorArrowIfOffScreen(
bool vertical,
const gfx::Rect& anchor_rect,
const gfx::Size& client_size) {
gfx::Rect available_bounds(GetAvailableScreenBounds(anchor_rect));
gfx::Rect window_bounds(bubble_border_->GetBounds(anchor_rect, client_size));
if (GetOffScreenLength(available_bounds, window_bounds, vertical) > 0) {
BubbleBorder::Arrow arrow = bubble_border()->arrow();
bubble_border_->set_arrow(
vertical ? BubbleBorder::vertical_mirror(arrow) :
BubbleBorder::horizontal_mirror(arrow));
gfx::Rect mirror_bounds =
bubble_border_->GetBounds(anchor_rect, client_size);
if (GetOffScreenLength(available_bounds, mirror_bounds, vertical) >=
GetOffScreenLength(available_bounds, window_bounds, vertical))
bubble_border_->set_arrow(arrow);
else if (parent())
parent()->Layout();
}
}
void BubbleFrameView::OffsetArrowIfOffScreen(const gfx::Rect& anchor_rect,
const gfx::Size& client_size) {
BubbleBorder::Arrow arrow = bubble_border()->arrow();
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 available_bounds(GetAvailableScreenBounds(anchor_rect));
if (available_bounds.IsEmpty() || available_bounds.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() < available_bounds.x())
offscreen_adjust = available_bounds.x() - window_bounds.x();
else if (window_bounds.right() > available_bounds.right())
offscreen_adjust = available_bounds.right() - window_bounds.right();
} else {
if (window_bounds.y() < available_bounds.y())
offscreen_adjust = available_bounds.y() - window_bounds.y();
else if (window_bounds.bottom() > available_bounds.bottom())
offscreen_adjust = available_bounds.bottom() - window_bounds.bottom();
}
bubble_border_->set_arrow_offset(
bubble_border_->GetArrowOffset(window_bounds.size()) - offscreen_adjust);
if (offscreen_adjust)
SchedulePaint();
}
gfx::Size BubbleFrameView::GetSizeForClientSize(const gfx::Size& client_size) {
int title_bar_width = GetInsets().width() + border()->GetInsets().width();
if (!title_->text().empty())
title_bar_width += kTitleLeftInset + title_->GetPreferredSize().width();
if (close_->visible())
title_bar_width += close_->width() + 1;
if (titlebar_extra_view_ != NULL)
title_bar_width += titlebar_extra_view_->GetPreferredSize().width();
gfx::Size size(client_size);
size.SetToMax(gfx::Size(title_bar_width, 0));
const gfx::Insets insets(GetInsets());
size.Enlarge(insets.width(), insets.height());
return size;
}
}