nim_duilib/examples/virtualbox/VirtualTileBox.cpp

564 lines
11 KiB
C++
Raw Normal View History

2019-07-06 12:45:48 +08:00
#include "stdafx.h"
#include "VirtualTileBox.h"
VirtualTileInterface::VirtualTileInterface()
:m_CountChangedNotify()
, m_DataChangedNotify()
{
}
void VirtualTileInterface::RegNotifys(const DataChangedNotify& dcNotify, const CountChangedNotify& ccNotify)
{
m_DataChangedNotify = dcNotify;
m_CountChangedNotify = ccNotify;
}
void VirtualTileInterface::EmitDataChanged(int nStartIndex, int nEndIndex)
{
if (m_DataChangedNotify) m_DataChangedNotify(nStartIndex, nEndIndex);
}
void VirtualTileInterface::EmitCountChanged()
{
if (m_CountChangedNotify) m_CountChangedNotify();
}
VirtualTileLayout::VirtualTileLayout()
:m_bAutoCalcColumn(true)
{
m_nColumns = -1;
}
ui::CSize VirtualTileLayout::ArrangeChild(const std::vector<ui::Control*>& items, ui::UiRect rc)
{
ui::CSize sz(rc.GetWidth(), rc.GetHeight());
VirtualTileBox *pList = dynamic_cast<VirtualTileBox*>(m_pOwner);
ASSERT(pList);
int nTotalHeight = GetElementsHeight(-1);
sz.cy = max(nTotalHeight, sz.cy);
LazyArrangeChild();
return sz;
}
ui::CSize VirtualTileLayout::AjustSizeByChild(const std::vector<ui::Control*>& items, ui::CSize szAvailable)
{
VirtualTileBox *pList = dynamic_cast<VirtualTileBox*>(m_pOwner);
ASSERT(pList);
ui::CSize size = m_pOwner->Control::EstimateSize(szAvailable);
if (size.cx == DUI_LENGTH_AUTO || size.cx == 0)
{
size.cx = m_szItem.cx * m_nColumns + m_iChildMargin * (m_nColumns - 1);
}
return size;
}
bool VirtualTileLayout::SetAttribute(const std::wstring& strName, const std::wstring& strValue)
{
if (strName == L"column")
{
int iValue = _ttoi(strValue.c_str());
if (iValue > 0)
{
SetColumns(iValue);
m_bAutoCalcColumn = false;
}
else {
m_bAutoCalcColumn = true;
}
return true;
}
else {
return __super::SetAttribute(strName, strValue);
}
}
int VirtualTileLayout::GetElementsHeight(int nCount)
{
if (nCount < m_nColumns && nCount != -1) return m_szItem.cy + m_iChildMargin;
VirtualTileBox *pList = dynamic_cast<VirtualTileBox*>(m_pOwner);
ASSERT(pList);
if (nCount < 0)
nCount = pList->GetElementCount();
int rows = nCount / m_nColumns;
if (nCount % m_nColumns != 0) {
rows += 1;
}
if (nCount > 0) {
int childMarginTotal;
if (nCount % m_nColumns == 0) {
childMarginTotal = (nCount / m_nColumns - 1) * m_iChildMargin;
}
else {
childMarginTotal = (nCount / m_nColumns) * m_iChildMargin;
}
return m_szItem.cy * (rows+ 1) + childMarginTotal;
}
return 0;
}
void VirtualTileLayout::LazyArrangeChild()
{
VirtualTileBox *pList = dynamic_cast<VirtualTileBox*>(m_pOwner);
ASSERT(pList);
// <20><><EFBFBD><EFBFBD>SetPosʱ<73>Ѿ<EFBFBD><D1BE><EFBFBD><EFBFBD>ú<EFBFBD>
ASSERT(m_nColumns);
// <20><>ȡVirtualTileBox<6F><78>Rect
ui::UiRect rc = pList->GetPaddingPos();
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼλ<CABC><CEBB>
int iPosLeft = rc.left;
// <20><><EFBFBD><EFBFBD><EFBFBD>Ķ<EFBFBD><C4B6><EFBFBD><EFBFBD><EFBFBD>ʼλ<CABC><CEBB>
int iPosTop = rc.top + pList->GetScrollPos().cy;
ui::CPoint ptTile(iPosLeft, iPosTop);
// <20><><EFBFBD><EFBFBD>index
int nTopBottom = 0;
int nTopIndex = pList->GetTopElementIndex(nTopBottom);
int iCount = 0;
for (auto pControl : pList->m_items)
{
// Determine size
ui::UiRect rcTile(ptTile.x, ptTile.y, ptTile.x + m_szItem.cx, ptTile.y + m_szItem.cy);
pControl->SetPos(rcTile);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
int nElementIndex = nTopIndex + iCount;
if (nElementIndex < pList->GetElementCount())
{
if (!pControl->IsVisible()) pControl->SetVisible(true);
pList->FillElement(pControl, nElementIndex);
}
else {
if (pControl->IsVisible()) pControl->SetVisible(false);
}
if ((++iCount % m_nColumns) == 0) {
ptTile.x = iPosLeft;
ptTile.y += m_szItem.cy + m_iChildMargin;
}
else {
ptTile.x += rcTile.GetWidth() + m_iChildMargin;
}
}
}
int VirtualTileLayout::AjustMaxItem()
{
ui::UiRect rc = m_pOwner->GetPaddingPos();
if (m_bAutoCalcColumn)
{
if (m_szItem.cx > 0) m_nColumns = (rc.right - rc.left) / (m_szItem.cx + m_iChildMargin / 2);
if (m_nColumns == 0) m_nColumns = 1;
}
int nHeight = m_szItem.cy + m_iChildMargin;
int nRow = (rc.bottom - rc.top) / nHeight + 1;
return nRow * m_nColumns;
}
VirtualTileBox::VirtualTileBox(ui::Layout* pLayout /*= new VirtualTileLayout*/)
: ui::ListBox(pLayout)
, m_pDataProvider(nullptr)
, m_nMaxItemCount(0)
, m_nOldYScrollPos(0)
, m_bArrangedOnce(false)
, m_bForceArrange(false)
{
}
void VirtualTileBox::SetDataProvider(VirtualTileInterface *pProvider)
{
ASSERT(pProvider);
m_pDataProvider = pProvider;
// ע<><D7A2>ģ<EFBFBD><C4A3><EFBFBD><EFBFBD><EFBFBD>ݱ䶯֪ͨ<CDA8>ص<EFBFBD>
pProvider->RegNotifys(
nbase::Bind(&VirtualTileBox::OnModelDataChanged, this, std::placeholders::_1, std::placeholders::_2),
nbase::Bind(&VirtualTileBox::OnModelCountChanged, this));
}
VirtualTileInterface* VirtualTileBox::GetDataProvider()
{
return m_pDataProvider;
}
2019-07-06 12:45:48 +08:00
void VirtualTileBox::Refresh()
{
m_nMaxItemCount = GetTileLayout()->AjustMaxItem();
int nElementCount = GetElementCount();
int nItemCount = GetCount();
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>Ƴ<EFBFBD><C6B3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (nItemCount > nElementCount)
{
int n = nItemCount - nElementCount;
for (int i = 0; i < n; i++)
this->RemoveAt(0);
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>С<EFBFBD><D0A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
else if (nItemCount < nElementCount) {
int n = 0;
if (nElementCount <= m_nMaxItemCount)
{
n = nElementCount - nItemCount;
}
else {
n = m_nMaxItemCount - nItemCount;
}
for (int i = 0; i < n; i++) {
Control *pControl = CreateElement();
this->Add(pControl);
}
}
if (nElementCount <= 0)
return;
ReArrangeChild(true);
Arrange();
}
void VirtualTileBox::RemoveAll()
{
__super::RemoveAll();
if (m_pVerticalScrollBar)
m_pVerticalScrollBar->SetScrollPos(0);
m_nOldYScrollPos = 0;
m_bArrangedOnce = false;
m_bForceArrange = false;
}
void VirtualTileBox::SetForceArrange(bool bForce)
{
m_bForceArrange = bForce;
}
void VirtualTileBox::GetDisplayCollection(std::vector<int>& collection)
{
collection.clear();
if (GetCount() == 0)
return;
// <20><>ȡBox<6F><78>Rect
ui::UiRect rcThis = this->GetPaddingPos();
int nEleHeight = GetRealElementHeight();
int min = (GetScrollPos().cy / nEleHeight) * GetColumns();
int max = min + (rcThis.GetHeight() / nEleHeight) * GetColumns();
int nCount = GetElementCount();
if (max >= nCount)
max = nCount - 1;
for (auto i = min; i <= max; i++)
collection.push_back(i);
}
void VirtualTileBox::EnsureVisible(int iIndex, bool bToTop /*= false*/)
{
if (iIndex < 0 || iIndex >= GetElementCount())
return;
if (!m_pVerticalScrollBar)
return;
auto nPos = GetScrollPos().cy;
int nTopIndex = (nPos / GetRealElementHeight()) * GetColumns();
int nNewPos = 0;
if (bToTop)
{
nNewPos = CalcElementsHeight(iIndex);
if (nNewPos >= m_pVerticalScrollBar->GetScrollRange())
return;
}
else {
if (IsElementDisplay(iIndex))
return;
if (iIndex > nTopIndex)
{
// <20><><EFBFBD><EFBFBD>
int height = CalcElementsHeight(iIndex + 1);
nNewPos = height - m_rcItem.GetHeight();
}
else {
// <20><><EFBFBD><EFBFBD>
nNewPos = CalcElementsHeight(iIndex);
}
}
ui::CSize sz(0, nNewPos);
SetScrollPos(sz);
}
void VirtualTileBox::SetScrollPos(ui::CSize szPos)
{
m_nOldYScrollPos = GetScrollPos().cy;
ListBox::SetScrollPos(szPos);
ReArrangeChild(false);
}
void VirtualTileBox::HandleMessage(ui::EventArgs& event)
{
if (!IsMouseEnabled() && event.Type > ui::kEventMouseBegin && event.Type < ui::kEventMouseEnd) {
if (m_pParent != nullptr)
m_pParent->HandleMessageTemplate(event);
else
ui::ScrollableBox::HandleMessage(event);
return;
}
switch (event.Type) {
case ui::kEventKeyDown: {
switch (event.chKey) {
case VK_UP: {
OnKeyDown(VK_UP);
return;
}
case VK_DOWN: {
OnKeyDown(VK_DOWN);
return;
}
case VK_HOME:
SetScrollPosY(0);
return;
case VK_END: {
int range = GetScrollPos().cy;
SetScrollPosY(range);
return;
}
}
}
case ui::kEventKeyUp: {
switch (event.chKey) {
case VK_UP: {
OnKeyUp(VK_UP);
return;
}
case VK_DOWN: {
OnKeyUp(VK_DOWN);
return;
}
}
}
}
__super::HandleMessage(event);
}
void VirtualTileBox::SetPos(ui::UiRect rc)
{
bool bChange = false;
if (!m_rcItem.Equal(rc))
bChange = true;
ListBox::SetPos(rc);
if (bChange) {
Refresh();
}
}
void VirtualTileBox::ReArrangeChild(bool bForce)
{
ScrollDirection direction = kScrollUp;
if (!bForce && !m_bForceArrange) {
if (!NeedReArrange(direction))
return;
}
LazyArrangeChild();
}
ui::Control* VirtualTileBox::CreateElement()
{
if (m_pDataProvider)
return m_pDataProvider->CreateElement();
return nullptr;
}
void VirtualTileBox::FillElement(Control *pControl, int iIndex)
{
if (m_pDataProvider)
m_pDataProvider->FillElement(pControl, iIndex);
}
int VirtualTileBox::GetElementCount()
{
if (m_pDataProvider)
return m_pDataProvider->GetElementtCount();
return 0;
}
int VirtualTileBox::CalcElementsHeight(int nCount)
{
return GetTileLayout()->GetElementsHeight(nCount);
}
int VirtualTileBox::GetTopElementIndex(int &bottom)
{
int nPos = GetScrollPos().cy;
int nHeight = GetRealElementHeight();
int iIndex = (nPos / nHeight) * GetColumns();
bottom = iIndex * nHeight;
return iIndex;
}
bool VirtualTileBox::IsElementDisplay(int iIndex)
{
if (iIndex < 0)
return false;
int nPos = GetScrollPos().cy;
int nElementPos = CalcElementsHeight(iIndex);
if (nElementPos >= nPos) {
int nHeight = this->GetHeight();
if (nElementPos + GetRealElementHeight() <= nPos + nHeight)
return true;
}
return false;
}
bool VirtualTileBox::NeedReArrange(ScrollDirection &direction)
{
direction = kScrollUp;
if (!m_bArrangedOnce) {
m_bArrangedOnce = true;
return true;
}
int nCount = GetCount();
if (nCount <= 0)
return false;
if (GetElementCount() <= nCount)
return false;
ui::UiRect rcThis = this->GetPos();
if (rcThis.GetWidth() <= 0)
return false;
int nPos = GetScrollPos().cy;
ui::UiRect rcItem;
rcItem = m_items[0]->GetPos();
if (nPos >= m_nOldYScrollPos) {
// <20><>
rcItem = m_items[nCount - 1]->GetPos();
int nSub = (rcItem.bottom - rcThis.top) - (nPos + rcThis.GetHeight());
if (nSub < 0) {
direction = kScrollDown;
return true;
}
}
else {
// <20><>
rcItem = m_items[0]->GetPos();
if (nPos < (rcItem.top - rcThis.top)) {
direction = kScrollUp;
return true;
}
}
return false;
}
VirtualTileLayout* VirtualTileBox::GetTileLayout()
{
auto* pLayout = dynamic_cast<VirtualTileLayout*>(m_pLayout.get());
return pLayout;
}
int VirtualTileBox::GetRealElementHeight()
{
return GetTileLayout()->GetElementsHeight(1);
}
int VirtualTileBox::GetColumns()
{
return GetTileLayout()->GetColumns();
}
void VirtualTileBox::LazyArrangeChild()
{
GetTileLayout()->LazyArrangeChild();
}
void VirtualTileBox::OnModelDataChanged(int nStartIndex, int nEndIndex)
{
for (auto i = nStartIndex; i <= nEndIndex; i++)
{
int nItemIndex = ElementIndexToItemIndex(nStartIndex);
if (nItemIndex >= 0) {
2019-07-06 12:45:48 +08:00
FillElement(m_items[nItemIndex], i);
}
}
}
void VirtualTileBox::OnModelCountChanged()
{
Refresh();
}
int VirtualTileBox::ElementIndexToItemIndex(int nElementIndex)
{
if (IsElementDisplay(nElementIndex))
{
int nTopItemHeight = 0;
return nElementIndex - GetTopElementIndex(nTopItemHeight);
}
return -1;
}
int VirtualTileBox::ItemIndexToElementIndex(int nItemIndex)
{
int nTopItemHeight = 0;
return GetTopElementIndex(nTopItemHeight) + nItemIndex;
}