This source file includes following definitions.
- OnNativeThemeChanged
- SetThemeSpecificState
- CheckScrollBounds
- CheckScrollBounds
- AdjustPosition
- Viewport
- Viewport
- GetClassName
- ScrollRectToVisible
- ChildPreferredSizeChanged
- hide_horizontal_scrollbar_
- CreateScrollViewWithBorder
- SetContents
- SetHeader
- GetVisibleRect
- ClipHeightTo
- GetScrollBarWidth
- GetScrollBarHeight
- SetHorizontalScrollBar
- SetVerticalScrollBar
- GetPreferredSize
- GetHeightForWidth
- Layout
- OnKeyPressed
- OnMouseWheel
- OnMouseEntered
- OnMouseExited
- OnGestureEvent
- GetClassName
- ScrollToPosition
- GetScrollIncrement
- SetHeaderOrContents
- ScrollContentsRegionToBeVisible
- ComputeScrollBarsVisibility
- SetControlVisibility
- UpdateScrollBarPositions
- GetPageScrollIncrement
- GetLineScrollIncrement
- GetRowInfo
- row_height_
- GetRowInfo
#include "ui/views/controls/scroll_view.h"
#include "base/logging.h"
#include "ui/events/event.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/border.h"
#include "ui/views/controls/scrollbar/native_scroll_bar.h"
#include "ui/views/widget/root_view.h"
namespace views {
const char ScrollView::kViewClassName[] = "ScrollView";
namespace {
class ScrollViewWithBorder : public views::ScrollView {
public:
ScrollViewWithBorder() {
SetThemeSpecificState();
}
virtual void OnNativeThemeChanged(const ui::NativeTheme* theme) OVERRIDE {
SetThemeSpecificState();
}
private:
void SetThemeSpecificState() {
SetBorder(Border::CreateSolidBorder(
1,
GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_UnfocusedBorderColor)));
}
DISALLOW_COPY_AND_ASSIGN(ScrollViewWithBorder);
};
int CheckScrollBounds(int viewport_size, int content_size, int current_pos) {
int max = std::max(content_size - viewport_size, 0);
if (current_pos < 0)
return 0;
if (current_pos > max)
return max;
return current_pos;
}
void CheckScrollBounds(View* viewport, View* view) {
if (!view)
return;
int x = CheckScrollBounds(viewport->width(), view->width(), -view->x());
int y = CheckScrollBounds(viewport->height(), view->height(), -view->y());
view->SetBounds(-x, -y, view->width(), view->height());
}
int AdjustPosition(int current_position,
int new_position,
int content_size,
int viewport_size) {
if (-current_position == new_position)
return new_position;
if (new_position < 0)
return 0;
const int max_position = std::max(0, content_size - viewport_size);
return (new_position > max_position) ? max_position : new_position;
}
}
class ScrollView::Viewport : public View {
public:
Viewport() {}
virtual ~Viewport() {}
virtual const char* GetClassName() const OVERRIDE {
return "ScrollView::Viewport";
}
virtual void ScrollRectToVisible(const gfx::Rect& rect) OVERRIDE {
if (!has_children() || !parent())
return;
View* contents = child_at(0);
gfx::Rect scroll_rect(rect);
scroll_rect.Offset(-contents->x(), -contents->y());
static_cast<ScrollView*>(parent())->ScrollContentsRegionToBeVisible(
scroll_rect);
}
virtual void ChildPreferredSizeChanged(View* child) OVERRIDE {
if (parent())
parent()->Layout();
}
private:
DISALLOW_COPY_AND_ASSIGN(Viewport);
};
ScrollView::ScrollView()
: contents_(NULL),
contents_viewport_(new Viewport()),
header_(NULL),
header_viewport_(new Viewport()),
horiz_sb_(new NativeScrollBar(true)),
vert_sb_(new NativeScrollBar(false)),
resize_corner_(NULL),
min_height_(-1),
max_height_(-1),
hide_horizontal_scrollbar_(false) {
set_notify_enter_exit_on_child(true);
AddChildView(contents_viewport_);
AddChildView(header_viewport_);
horiz_sb_->SetVisible(false);
horiz_sb_->set_controller(this);
vert_sb_->SetVisible(false);
vert_sb_->set_controller(this);
if (resize_corner_)
resize_corner_->SetVisible(false);
}
ScrollView::~ScrollView() {
delete horiz_sb_;
delete vert_sb_;
if (resize_corner_ && !resize_corner_->parent())
delete resize_corner_;
}
ScrollView* ScrollView::CreateScrollViewWithBorder() {
return new ScrollViewWithBorder();
}
void ScrollView::SetContents(View* a_view) {
SetHeaderOrContents(contents_viewport_, a_view, &contents_);
}
void ScrollView::SetHeader(View* header) {
SetHeaderOrContents(header_viewport_, header, &header_);
}
gfx::Rect ScrollView::GetVisibleRect() const {
if (!contents_)
return gfx::Rect();
return gfx::Rect(-contents_->x(), -contents_->y(),
contents_viewport_->width(), contents_viewport_->height());
}
void ScrollView::ClipHeightTo(int min_height, int max_height) {
min_height_ = min_height;
max_height_ = max_height;
}
int ScrollView::GetScrollBarWidth() const {
return vert_sb_ ? vert_sb_->GetLayoutSize() : 0;
}
int ScrollView::GetScrollBarHeight() const {
return horiz_sb_ ? horiz_sb_->GetLayoutSize() : 0;
}
void ScrollView::SetHorizontalScrollBar(ScrollBar* horiz_sb) {
DCHECK(horiz_sb);
horiz_sb->SetVisible(horiz_sb_->visible());
delete horiz_sb_;
horiz_sb->set_controller(this);
horiz_sb_ = horiz_sb;
}
void ScrollView::SetVerticalScrollBar(ScrollBar* vert_sb) {
DCHECK(vert_sb);
vert_sb->SetVisible(vert_sb_->visible());
delete vert_sb_;
vert_sb->set_controller(this);
vert_sb_ = vert_sb;
}
gfx::Size ScrollView::GetPreferredSize() {
if (!is_bounded())
return View::GetPreferredSize();
gfx::Size size = contents()->GetPreferredSize();
size.SetToMax(gfx::Size(size.width(), min_height_));
size.SetToMin(gfx::Size(size.width(), max_height_));
gfx::Insets insets = GetInsets();
size.Enlarge(insets.width(), insets.height());
return size;
}
int ScrollView::GetHeightForWidth(int width) {
if (!is_bounded())
return View::GetHeightForWidth(width);
gfx::Insets insets = GetInsets();
width = std::max(0, width - insets.width());
int height = contents()->GetHeightForWidth(width) + insets.height();
return std::min(std::max(height, min_height_), max_height_);
}
void ScrollView::Layout() {
if (is_bounded()) {
int content_width = width();
int content_height = contents()->GetHeightForWidth(content_width);
if (content_height > height()) {
content_width = std::max(content_width - GetScrollBarWidth(), 0);
content_height = contents()->GetHeightForWidth(content_width);
}
if (contents()->bounds().size() != gfx::Size(content_width, content_height))
contents()->SetBounds(0, 0, content_width, content_height);
}
gfx::Rect viewport_bounds = GetContentsBounds();
const int contents_x = viewport_bounds.x();
const int contents_y = viewport_bounds.y();
if (viewport_bounds.IsEmpty()) {
return;
}
const int header_height =
std::min(viewport_bounds.height(),
header_ ? header_->GetPreferredSize().height() : 0);
viewport_bounds.set_height(
std::max(0, viewport_bounds.height() - header_height));
viewport_bounds.set_y(viewport_bounds.y() + header_height);
gfx::Size viewport_size = viewport_bounds.size();
int horiz_sb_height = GetScrollBarHeight();
int vert_sb_width = GetScrollBarWidth();
viewport_bounds.set_width(viewport_bounds.width() - vert_sb_width);
contents_viewport_->SetBoundsRect(viewport_bounds);
if (contents_)
contents_->Layout();
bool should_layout_contents = false;
bool horiz_sb_required = false;
bool vert_sb_required = false;
if (contents_) {
gfx::Size content_size = contents_->size();
ComputeScrollBarsVisibility(viewport_size,
content_size,
&horiz_sb_required,
&vert_sb_required);
}
bool resize_corner_required = resize_corner_ && horiz_sb_required &&
vert_sb_required;
SetControlVisibility(horiz_sb_, horiz_sb_required);
SetControlVisibility(vert_sb_, vert_sb_required);
SetControlVisibility(resize_corner_, resize_corner_required);
if (horiz_sb_required) {
viewport_bounds.set_height(
std::max(0, viewport_bounds.height() - horiz_sb_height));
should_layout_contents = true;
}
if (!vert_sb_required) {
viewport_bounds.set_width(viewport_bounds.width() + vert_sb_width);
should_layout_contents = true;
}
if (horiz_sb_required) {
int height_offset = horiz_sb_->GetContentOverlapSize();
horiz_sb_->SetBounds(0,
viewport_bounds.bottom() - height_offset,
viewport_bounds.right(),
horiz_sb_height + height_offset);
}
if (vert_sb_required) {
int width_offset = vert_sb_->GetContentOverlapSize();
vert_sb_->SetBounds(viewport_bounds.right() - width_offset,
0,
vert_sb_width + width_offset,
viewport_bounds.bottom());
}
if (resize_corner_required) {
resize_corner_->SetBounds(viewport_bounds.right(),
viewport_bounds.bottom(),
vert_sb_width,
horiz_sb_height);
}
contents_viewport_->SetBoundsRect(viewport_bounds);
if (should_layout_contents && contents_)
contents_->Layout();
header_viewport_->SetBounds(contents_x, contents_y,
viewport_bounds.width(), header_height);
if (header_)
header_->Layout();
CheckScrollBounds(header_viewport_, header_);
CheckScrollBounds(contents_viewport_, contents_);
SchedulePaint();
UpdateScrollBarPositions();
}
bool ScrollView::OnKeyPressed(const ui::KeyEvent& event) {
bool processed = false;
if (vert_sb_->visible())
processed = vert_sb_->OnKeyPressed(event);
if (!processed && horiz_sb_->visible())
processed = horiz_sb_->OnKeyPressed(event);
return processed;
}
bool ScrollView::OnMouseWheel(const ui::MouseWheelEvent& e) {
bool processed = false;
if (vert_sb_->visible())
processed = vert_sb_->OnMouseWheel(e);
if (!processed && horiz_sb_->visible())
processed = horiz_sb_->OnMouseWheel(e);
return processed;
}
void ScrollView::OnMouseEntered(const ui::MouseEvent& event) {
if (horiz_sb_)
horiz_sb_->OnMouseEnteredScrollView(event);
if (vert_sb_)
vert_sb_->OnMouseEnteredScrollView(event);
}
void ScrollView::OnMouseExited(const ui::MouseEvent& event) {
if (horiz_sb_)
horiz_sb_->OnMouseExitedScrollView(event);
if (vert_sb_)
vert_sb_->OnMouseExitedScrollView(event);
}
void ScrollView::OnGestureEvent(ui::GestureEvent* event) {
bool scroll_event = event->type() == ui::ET_GESTURE_SCROLL_UPDATE ||
event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
event->type() == ui::ET_GESTURE_SCROLL_END ||
event->type() == ui::ET_SCROLL_FLING_START;
if (vert_sb_->visible()) {
if (vert_sb_->bounds().Contains(event->location()) || scroll_event)
vert_sb_->OnGestureEvent(event);
}
if (!event->handled() && horiz_sb_->visible()) {
if (horiz_sb_->bounds().Contains(event->location()) || scroll_event)
horiz_sb_->OnGestureEvent(event);
}
}
const char* ScrollView::GetClassName() const {
return kViewClassName;
}
void ScrollView::ScrollToPosition(ScrollBar* source, int position) {
if (!contents_)
return;
if (source == horiz_sb_ && horiz_sb_->visible()) {
position = AdjustPosition(contents_->x(), position, contents_->width(),
contents_viewport_->width());
if (-contents_->x() == position)
return;
contents_->SetX(-position);
if (header_) {
header_->SetX(-position);
header_->SchedulePaintInRect(header_->GetVisibleBounds());
}
} else if (source == vert_sb_ && vert_sb_->visible()) {
position = AdjustPosition(contents_->y(), position, contents_->height(),
contents_viewport_->height());
if (-contents_->y() == position)
return;
contents_->SetY(-position);
}
contents_->SchedulePaintInRect(contents_->GetVisibleBounds());
}
int ScrollView::GetScrollIncrement(ScrollBar* source, bool is_page,
bool is_positive) {
bool is_horizontal = source->IsHorizontal();
int amount = 0;
if (contents_) {
if (is_page) {
amount = contents_->GetPageScrollIncrement(
this, is_horizontal, is_positive);
} else {
amount = contents_->GetLineScrollIncrement(
this, is_horizontal, is_positive);
}
if (amount > 0)
return amount;
}
if (is_page) {
return is_horizontal ? contents_viewport_->width() :
contents_viewport_->height();
}
return is_horizontal ? contents_viewport_->width() / 5 :
contents_viewport_->height() / 5;
}
void ScrollView::SetHeaderOrContents(View* parent,
View* new_view,
View** member) {
if (*member == new_view)
return;
delete *member;
*member = new_view;
if (*member)
parent->AddChildView(*member);
Layout();
}
void ScrollView::ScrollContentsRegionToBeVisible(const gfx::Rect& rect) {
if (!contents_ || (!horiz_sb_->visible() && !vert_sb_->visible()))
return;
const int contents_max_x =
std::max(contents_viewport_->width(), contents_->width());
const int contents_max_y =
std::max(contents_viewport_->height(), contents_->height());
int x = std::max(0, std::min(contents_max_x, rect.x()));
int y = std::max(0, std::min(contents_max_y, rect.y()));
const int max_x = std::min(contents_max_x,
x + std::min(rect.width(), contents_viewport_->width()));
const int max_y = std::min(contents_max_y,
y + std::min(rect.height(), contents_viewport_->height()));
const gfx::Rect vis_rect = GetVisibleRect();
if (vis_rect.Contains(gfx::Rect(x, y, max_x - x, max_y - y)))
return;
const int new_x =
(vis_rect.x() > x) ? x : std::max(0, max_x - contents_viewport_->width());
const int new_y =
(vis_rect.y() > y) ? y : std::max(0, max_y -
contents_viewport_->height());
contents_->SetX(-new_x);
if (header_)
header_->SetX(-new_x);
contents_->SetY(-new_y);
UpdateScrollBarPositions();
}
void ScrollView::ComputeScrollBarsVisibility(const gfx::Size& vp_size,
const gfx::Size& content_size,
bool* horiz_is_shown,
bool* vert_is_shown) const {
if (content_size.width() <= vp_size.width() &&
content_size.height() <= vp_size.height()) {
*horiz_is_shown = false;
*vert_is_shown = false;
} else if (content_size.width() <= vp_size.width() - GetScrollBarWidth()) {
*horiz_is_shown = false;
*vert_is_shown = true;
} else if (content_size.height() <= vp_size.height() - GetScrollBarHeight()) {
*horiz_is_shown = true;
*vert_is_shown = false;
} else {
*horiz_is_shown = true;
*vert_is_shown = true;
}
if (hide_horizontal_scrollbar_)
*horiz_is_shown = false;
}
void ScrollView::SetControlVisibility(View* control, bool should_show) {
if (!control)
return;
if (should_show) {
if (!control->visible()) {
AddChildView(control);
control->SetVisible(true);
}
} else {
RemoveChildView(control);
control->SetVisible(false);
}
}
void ScrollView::UpdateScrollBarPositions() {
if (!contents_)
return;
if (horiz_sb_->visible()) {
int vw = contents_viewport_->width();
int cw = contents_->width();
int origin = contents_->x();
horiz_sb_->Update(vw, cw, -origin);
}
if (vert_sb_->visible()) {
int vh = contents_viewport_->height();
int ch = contents_->height();
int origin = contents_->y();
vert_sb_->Update(vh, ch, -origin);
}
}
VariableRowHeightScrollHelper::VariableRowHeightScrollHelper(
Controller* controller) : controller_(controller) {
}
VariableRowHeightScrollHelper::~VariableRowHeightScrollHelper() {
}
int VariableRowHeightScrollHelper::GetPageScrollIncrement(
ScrollView* scroll_view, bool is_horizontal, bool is_positive) {
if (is_horizontal)
return 0;
int y = abs(scroll_view->contents()->y());
int vis_height = scroll_view->contents()->parent()->height();
if (is_positive) {
int bottom = std::min(scroll_view->contents()->height() - 1,
y + vis_height);
RowInfo bottom_row_info = GetRowInfo(bottom);
return std::max(0, bottom_row_info.origin - y);
} else {
int last_page_y = y - vis_height;
RowInfo last_page_info = GetRowInfo(std::max(0, last_page_y));
if (last_page_y != last_page_info.origin)
return std::max(0, y - last_page_info.origin - last_page_info.height);
return std::max(0, y - last_page_info.origin);
}
}
int VariableRowHeightScrollHelper::GetLineScrollIncrement(
ScrollView* scroll_view, bool is_horizontal, bool is_positive) {
if (is_horizontal)
return 0;
int y = abs(scroll_view->contents()->y());
RowInfo row = GetRowInfo(y);
if (is_positive) {
return row.height - (y - row.origin);
} else if (y == row.origin) {
row = GetRowInfo(std::max(0, row.origin - 1));
return y - row.origin;
} else {
return y - row.origin;
}
}
VariableRowHeightScrollHelper::RowInfo
VariableRowHeightScrollHelper::GetRowInfo(int y) {
return controller_->GetRowInfo(y);
}
FixedRowHeightScrollHelper::FixedRowHeightScrollHelper(int top_margin,
int row_height)
: VariableRowHeightScrollHelper(NULL),
top_margin_(top_margin),
row_height_(row_height) {
DCHECK_GT(row_height, 0);
}
VariableRowHeightScrollHelper::RowInfo
FixedRowHeightScrollHelper::GetRowInfo(int y) {
if (y < top_margin_)
return RowInfo(0, top_margin_);
return RowInfo((y - top_margin_) / row_height_ * row_height_ + top_margin_,
row_height_);
}
}