nim_duilib/examples/virtualbox/VirtualTileBox.cpp
jiajia_deng 4933d1f2bc Remove dependency on shared
Signed-off-by: jiajia_deng <2894220@gmail.com>
2019-09-20 16:27:58 +08:00

564 lines
11 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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;
}