564 lines
11 KiB
C++
564 lines
11 KiB
C++
#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;
|
||
}
|
||
|
||
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) {
|
||
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;
|
||
}
|
||
|