| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- // ================================================================================
- // == This file is a part of Turbo Badger. (C) 2011-2014, Emil Segerås ==
- // == See tb_core.h for more information. ==
- // ================================================================================
- #include "tb_scroll_container.h"
- #include "tb_system.h"
- #include <assert.h>
- namespace tb {
- // == TBScrollBarVisibility ===================================
- TBScrollBarVisibility TBScrollBarVisibility::Solve(SCROLL_MODE mode, int content_w, int content_h,
- int available_w, int available_h,
- int scrollbar_x_h, int scrollbar_y_w)
- {
- TBScrollBarVisibility visibility;
- visibility.visible_w = available_w;
- visibility.visible_h = available_h;
- if (mode == SCROLL_MODE_X_Y)
- {
- visibility.y_on = true;
- visibility.x_on = true;
- visibility.visible_w -= scrollbar_y_w;
- visibility.visible_h -= scrollbar_x_h;
- }
- else if (mode == SCROLL_MODE_OFF)
- {
- }
- else if (mode == SCROLL_MODE_Y)
- {
- visibility.y_on = true;
- visibility.visible_w -= scrollbar_y_w;
- }
- else if (mode == SCROLL_MODE_Y_AUTO)
- {
- if (content_h > available_h)
- {
- visibility.y_on = true;
- visibility.visible_w -= scrollbar_y_w;
- }
- }
- else if (mode == SCROLL_MODE_X_AUTO_Y_AUTO)
- {
- if (content_w > visibility.visible_w)
- {
- visibility.x_on = true;
- visibility.visible_h = available_h - scrollbar_x_h;
- }
- if (content_h > visibility.visible_h)
- {
- visibility.y_on = true;
- visibility.visible_w = available_w - scrollbar_y_w;
- }
- if (content_w > visibility.visible_w)
- {
- visibility.x_on = true;
- visibility.visible_h = available_h - scrollbar_x_h;
- }
- }
- return visibility;
- }
- // == TBScrollContainerRoot ===================================
- void TBScrollContainerRoot::OnPaintChildren(const PaintProps &paint_props)
- {
- // We only want clipping in one axis (the overflowing one) so we
- // don't damage any expanded skins on the other axis. Add some fluff.
- const int fluff = 100;
- TBScrollContainer *sc = static_cast<TBScrollContainer *>(GetParent());
- TBRect clip_rect = GetPaddingRect().Expand(sc->m_scrollbar_x.CanScrollNegative() ? 0 : fluff,
- sc->m_scrollbar_y.CanScrollNegative() ? 0 : fluff,
- sc->m_scrollbar_x.CanScrollPositive() ? 0 : fluff,
- sc->m_scrollbar_y.CanScrollPositive() ? 0 : fluff);
- TBRect old_clip_rect = g_renderer->SetClipRect(clip_rect, true);
- TB_IF_DEBUG_SETTING(LAYOUT_CLIPPING, g_renderer->DrawRect(clip_rect, TBColor(255, 0, 0, 200)));
- TBWidget::OnPaintChildren(paint_props);
- g_renderer->SetClipRect(old_clip_rect, false);
- }
- void TBScrollContainerRoot::GetChildTranslation(int &x, int &y) const
- {
- TBScrollContainer *sc = static_cast<TBScrollContainer *>(GetParent());
- x = (int) -sc->m_scrollbar_x.GetValue();
- y = (int) -sc->m_scrollbar_y.GetValue();
- }
- // == TBScrollContainer =======================================
- TBScrollContainer::TBScrollContainer()
- : m_adapt_to_content_size(false)
- , m_adapt_content_size(false)
- , m_layout_is_invalid(false)
- , m_ignore_scroll_events(false)
- , m_mode(SCROLL_MODE_X_Y)
- {
- AddChild(&m_scrollbar_x);
- AddChild(&m_scrollbar_y);
- AddChild(&m_root);
- m_scrollbar_y.SetAxis(AXIS_Y);
- }
- TBScrollContainer::~TBScrollContainer()
- {
- RemoveChild(&m_root);
- RemoveChild(&m_scrollbar_y);
- RemoveChild(&m_scrollbar_x);
- }
- void TBScrollContainer::SetAdaptToContentSize(bool adapt)
- {
- if (m_adapt_to_content_size == adapt)
- return;
- InvalidateLayout(INVALIDATE_LAYOUT_RECURSIVE);
- m_adapt_to_content_size = adapt;
- InvalidateLayout(INVALIDATE_LAYOUT_RECURSIVE);
- }
- void TBScrollContainer::SetAdaptContentSize(bool adapt)
- {
- if (m_adapt_content_size == adapt)
- return;
- m_adapt_content_size = adapt;
- InvalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY);
- }
- void TBScrollContainer::SetScrollMode(SCROLL_MODE mode)
- {
- if (mode == m_mode)
- return;
- m_mode = mode;
- InvalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY);
- }
- void TBScrollContainer::ScrollTo(int x, int y)
- {
- int old_x = m_scrollbar_x.GetValue();
- int old_y = m_scrollbar_y.GetValue();
- m_scrollbar_x.SetValue(x);
- m_scrollbar_y.SetValue(y);
- if (old_x != m_scrollbar_x.GetValue() ||
- old_y != m_scrollbar_y.GetValue())
- Invalidate();
- }
- TBWidget::ScrollInfo TBScrollContainer::GetScrollInfo()
- {
- ScrollInfo info;
- info.min_x = static_cast<int>(m_scrollbar_x.GetMinValue());
- info.min_y = static_cast<int>(m_scrollbar_y.GetMinValue());
- info.max_x = static_cast<int>(m_scrollbar_x.GetMaxValue());
- info.max_y = static_cast<int>(m_scrollbar_y.GetMaxValue());
- info.x = m_scrollbar_x.GetValue();
- info.y = m_scrollbar_y.GetValue();
- return info;
- }
- void TBScrollContainer::InvalidateLayout(INVALIDATE_LAYOUT il)
- {
- m_layout_is_invalid = true;
- // No recursion up to parents here unless we adapt to content size.
- if (m_adapt_to_content_size)
- TBWidget::InvalidateLayout(il);
- }
- TBRect TBScrollContainer::GetPaddingRect()
- {
- int visible_w = GetRect().w;
- int visible_h = GetRect().h;
- if (m_scrollbar_x.GetOpacity())
- visible_h -= m_scrollbar_x.GetPreferredSize().pref_h;
- if (m_scrollbar_y.GetOpacity())
- visible_w -= m_scrollbar_y.GetPreferredSize().pref_w;
- return TBRect(0, 0, visible_w, visible_h);
- }
- PreferredSize TBScrollContainer::OnCalculatePreferredContentSize(const SizeConstraints &constraints)
- {
- PreferredSize ps;
- ps.pref_w = ps.pref_h = 100;
- ps.min_w = ps.min_h = 50;
- if (m_adapt_to_content_size)
- {
- if (TBWidget *content_child = m_root.GetFirstChild())
- {
- ps = content_child->GetPreferredSize(constraints);
- int scrollbar_y_w = m_scrollbar_y.GetPreferredSize().pref_w;
- int scrollbar_x_h = m_scrollbar_x.GetPreferredSize().pref_h;
- ps.pref_w += scrollbar_y_w;
- ps.max_w += scrollbar_y_w;
- if (m_mode == SCROLL_MODE_X_Y ||
- m_mode == SCROLL_MODE_X_AUTO_Y_AUTO)
- {
- ps.pref_h += scrollbar_x_h;
- ps.max_h += scrollbar_x_h;
- }
- }
- }
- return ps;
- }
- bool TBScrollContainer::OnEvent(const TBWidgetEvent &ev)
- {
- if (ev.type == EVENT_TYPE_CHANGED && (ev.target == &m_scrollbar_x || ev.target == &m_scrollbar_y))
- {
- Invalidate();
- OnScroll(m_scrollbar_x.GetValue(), m_scrollbar_y.GetValue());
- return true;
- }
- else if (ev.type == EVENT_TYPE_WHEEL && ev.modifierkeys == TB_MODIFIER_NONE && !m_ignore_scroll_events)
- {
- double old_val_y = m_scrollbar_y.GetValueDouble();
- m_scrollbar_y.SetValueDouble(old_val_y + ev.delta_y * TBSystem::GetPixelsPerLine());
- double old_val_x = m_scrollbar_x.GetValueDouble();
- m_scrollbar_x.SetValueDouble(old_val_x + ev.delta_x * TBSystem::GetPixelsPerLine());
- return (m_scrollbar_x.GetValueDouble() != old_val_x || m_scrollbar_y.GetValueDouble() != old_val_y);
- }
- else if (ev.type == EVENT_TYPE_KEY_DOWN && !m_ignore_scroll_events)
- {
- // ATOMIC: Disabling arrow key scroll
- /*
- if (ev.special_key == TB_KEY_LEFT && m_scrollbar_x.CanScrollNegative())
- ScrollBySmooth(-TBSystem::GetPixelsPerLine(), 0);
- else if (ev.special_key == TB_KEY_RIGHT && m_scrollbar_x.CanScrollPositive())
- ScrollBySmooth(TBSystem::GetPixelsPerLine(), 0);
- else if (ev.special_key == TB_KEY_UP && m_scrollbar_y.CanScrollNegative())
- ScrollBySmooth(0, -TBSystem::GetPixelsPerLine());
- else if (ev.special_key == TB_KEY_DOWN && m_scrollbar_y.CanScrollPositive())
- ScrollBySmooth(0, TBSystem::GetPixelsPerLine());
- else*/ if (ev.special_key == TB_KEY_PAGE_UP && m_scrollbar_y.CanScrollNegative())
- ScrollBySmooth(0, -GetPaddingRect().h);
- else if (ev.special_key == TB_KEY_PAGE_DOWN && m_scrollbar_y.CanScrollPositive())
- ScrollBySmooth(0, GetPaddingRect().h);
- else if (ev.special_key == TB_KEY_HOME)
- ScrollToSmooth(m_scrollbar_x.GetValue(), 0);
- else if (ev.special_key == TB_KEY_END)
- ScrollToSmooth(m_scrollbar_x.GetValue(), (int)m_scrollbar_y.GetMaxValue());
- else
- return false;
- return true;
- }
- return false;
- }
- void TBScrollContainer::OnProcess()
- {
- SizeConstraints sc(GetRect().w, GetRect().h);
- ValidateLayout(sc);
- }
- void TBScrollContainer::ValidateLayout(const SizeConstraints &constraints)
- {
- if (!m_layout_is_invalid)
- return;
- m_layout_is_invalid = false;
- // Layout scrollbars (no matter if they are visible or not)
- int scrollbar_y_w = m_scrollbar_y.GetPreferredSize().pref_w;
- int scrollbar_x_h = m_scrollbar_x.GetPreferredSize().pref_h;
- m_scrollbar_x.SetRect(TBRect(0, GetRect().h - scrollbar_x_h, GetRect().w - scrollbar_y_w, scrollbar_x_h));
- m_scrollbar_y.SetRect(TBRect(GetRect().w - scrollbar_y_w, 0, scrollbar_y_w, GetRect().h));
- if (TBWidget *content_child = m_root.GetFirstChild())
- {
- int horizontal_padding = TBScrollBarVisibility::IsAlwaysOnY(m_mode) ? scrollbar_y_w : 0;
- int vertical_padding = TBScrollBarVisibility::IsAlwaysOnX(m_mode) ? scrollbar_x_h : 0;
- SizeConstraints inner_sc = constraints.ConstrainByPadding(horizontal_padding, vertical_padding);
- PreferredSize ps = content_child->GetPreferredSize(inner_sc);
- TBScrollBarVisibility visibility = TBScrollBarVisibility::Solve(m_mode, ps.pref_w, ps.pref_h,
- GetRect().w, GetRect().h,
- scrollbar_x_h, scrollbar_y_w);
- m_scrollbar_x.SetOpacity(visibility.x_on ? 1.f : 0.f);
- m_scrollbar_y.SetOpacity(visibility.y_on ? 1.f : 0.f);
- m_root.SetRect(TBRect(0, 0, visibility.visible_w, visibility.visible_h));
- int content_w, content_h;
- if (m_adapt_content_size)
- {
- content_w = MAX(ps.pref_w, m_root.GetRect().w);
- content_h = MAX(ps.pref_h, m_root.GetRect().h);
- if (!visibility.x_on && m_root.GetRect().w < ps.pref_w)
- content_w = MIN(ps.pref_w, m_root.GetRect().w);
- }
- else
- {
- content_w = ps.pref_w;
- content_h = ps.pref_h;
- }
- content_child->SetRect(TBRect(0, 0, content_w, content_h));
- double limit_max_w = MAX(0, content_w - m_root.GetRect().w);
- double limit_max_h = MAX(0, content_h - m_root.GetRect().h);
- m_scrollbar_x.SetLimits(0, limit_max_w, m_root.GetRect().w);
- m_scrollbar_y.SetLimits(0, limit_max_h, m_root.GetRect().h);
- }
- }
- void TBScrollContainer::OnResized(int old_w, int old_h)
- {
- InvalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY);
- SizeConstraints sc(GetRect().w, GetRect().h);
- ValidateLayout(sc);
- // ATOMIC BEGIN So pure UIWidgets can handle resize
- if (GetDelegate()) { GetDelegate()->OnResized(old_w, old_h); }
- // ATOMIC END
- }
- }; // namespace tb
|