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

476 lines
12 KiB
C++

#include "StdAfx.h"
namespace ui
{
ListBox::ListBox(Layout* pLayout) :
ScrollableBox(pLayout),
m_bScrollSelect(false),
m_iCurSel(-1),
m_pCompareFunc(nullptr),
m_pCompareData(NULL),
m_bSelNextWhenRemoveActive(true)
{
}
void ListBox::SetAttribute(const std::wstring& strName, const std::wstring& strValue)
{
if( strName == _T("scrollselect") ) {
SetScrollSelect(strValue == _T("true"));
}
else {
ScrollableBox::SetAttribute(strName, strValue);
}
}
void ListBox::HandleMessage(EventArgs& event)
{
if (!IsMouseEnabled() && event.Type > kEventMouseBegin && event.Type < kEventMouseEnd) {
if (m_pParent != NULL) m_pParent->HandleMessageTemplate(event);
else ScrollableBox::HandleMessage(event);
return;
}
switch (event.Type) {
case kEventMouseButtonDown:
case kEventMouseButtonUp:
return;
case kEventKeyDown:
switch (event.chKey) {
case VK_UP:
SelectItem(FindSelectable(m_iCurSel - 1, false), true);
return;
case VK_DOWN:
SelectItem(FindSelectable(m_iCurSel + 1, true), true);
return;
case VK_HOME:
SelectItem(FindSelectable(0, false), true);
return;
case VK_END:
SelectItem(FindSelectable(GetCount() - 1, true), true);
return;
}
break;
case kEventMouseScrollWheel:
{
int detaValue = event.wParam;
if (detaValue > 0) {
if (m_bScrollSelect) {
SelectItem(FindSelectable(m_iCurSel - 1, false), true);
return;
}
break;
}
else {
if (m_bScrollSelect) {
SelectItem(FindSelectable(m_iCurSel + 1, true), true);
return;
}
break;
}
}
break;
}
ScrollableBox::HandleMessage(event);
}
void ListBox::HandleMessageTemplate(EventArgs& event)
{
ScrollableBox::HandleMessageTemplate(event);
}
int ListBox::GetCurSel() const
{
return m_iCurSel;
}
void ListBox::SelectNextWhenActiveRemoved(bool bSelectNextItem)
{
m_bSelNextWhenRemoveActive = bSelectNextItem;
}
bool ListBox::SelectItem(int iIndex, bool bTakeFocus, bool bTrigger)
{
//if( iIndex == m_iCurSel ) return true;
int iOldSel = m_iCurSel;
// We should first unselect the currently selected item
if (m_iCurSel >= 0) {
Control* pControl = GetItemAt(m_iCurSel);
if (pControl != NULL) {
ListContainerElement* pListItem = dynamic_cast<ListContainerElement*>(pControl);
if (pListItem != NULL) pListItem->OptionTemplate<Box>::Selected(false, bTrigger);
}
m_iCurSel = -1;
}
if (iIndex < 0) return false;
Control* pControl = GetItemAt(iIndex);
if (pControl == NULL) return false;
if (!pControl->IsVisible()) return false;
if (!pControl->IsEnabled()) return false;
ListContainerElement* pListItem = dynamic_cast<ListContainerElement*>(pControl);
if (pListItem == NULL) return false;
m_iCurSel = iIndex;
pListItem->OptionTemplate<Box>::Selected(true, bTrigger);
if (GetItemAt(m_iCurSel)) {
UiRect rcItem = GetItemAt(m_iCurSel)->GetPos();
EnsureVisible(rcItem);
}
if (bTakeFocus) pControl->SetFocus();
if (m_pWindow != NULL && bTrigger) {
m_pWindow->SendNotify(this, kEventSelect, m_iCurSel, iOldSel);
}
return true;
}
void ListBox::EnsureVisible(const UiRect& rcItem)
{
UiRect rcNewItem = rcItem;
rcNewItem.Offset(-GetScrollPos().cx, -GetScrollPos().cy);
UiRect rcList = GetPos();
UiRect rcListInset = m_pLayout->GetPadding();
rcList.left += rcListInset.left;
rcList.top += rcListInset.top;
rcList.right -= rcListInset.right;
rcList.bottom -= rcListInset.bottom;
ScrollBar* pHorizontalScrollBar = GetHorizontalScrollBar();
if (pHorizontalScrollBar && pHorizontalScrollBar->IsVisible()) rcList.bottom -= pHorizontalScrollBar->GetFixedHeight();
if (rcNewItem.left >= rcList.left && rcNewItem.top >= rcList.top
&& rcNewItem.right <= rcList.right && rcNewItem.bottom <= rcList.bottom) {
if (m_pParent && dynamic_cast<ListContainerElement*>(m_pParent) != NULL) {
dynamic_cast<ListContainerElement*>(m_pParent)->GetOwner()->EnsureVisible(rcNewItem);
}
return;
}
int dx = 0;
if (rcNewItem.left < rcList.left) dx = rcNewItem.left - rcList.left;
if (rcNewItem.right > rcList.right) dx = rcNewItem.right - rcList.right;
int dy = 0;
if (rcNewItem.top < rcList.top) dy = rcNewItem.top - rcList.top;
if (rcNewItem.bottom > rcList.bottom) dy = rcNewItem.bottom - rcList.bottom;
CSize sz = GetScrollPos();
SetScrollPos(CSize(sz.cx + dx, sz.cy + dy));
}
void ListBox::StopScroll()
{
m_scrollAnimation.Reset();
}
bool ListBox::ButtonDown(EventArgs& msg)
{
bool ret = __super::ButtonDown(msg);
StopScroll();
return ret;
}
bool ListBox::ScrollItemToTop(const std::wstring& strItemName)
{
for (auto it = m_items.begin(); it != m_items.end(); it++) {
if ((*it)->GetName() == strItemName) {
if (GetScrollRange().cy != 0) {
CSize scrollPos = GetScrollPos();
scrollPos.cy = (*it)->GetPos().top - m_pLayout->GetInternalPos().top;
if (scrollPos.cy >= 0) {
SetScrollPos(scrollPos);
return true;
}
else {
return false;
}
}
else {
return false;
}
}
}
return false;
}
Control* ListBox::GetTopItem()
{
int listTop = GetPos().top + m_pLayout->GetPadding().top + GetScrollPos().cy;
for (auto it = m_items.begin(); it != m_items.end(); it++) {
if ((*it)->IsVisible() && !(*it)->IsFloat() && (*it)->GetPos().bottom >= listTop) {
return (*it);
}
}
return nullptr;
}
bool ListBox::SetItemIndex(Control* pControl, std::size_t iIndex)
{
int iOrginIndex = GetItemIndex(pControl);
if( iOrginIndex == -1 ) return false;
if( iOrginIndex == (int)iIndex ) return true;
ListContainerElement* pSelectedListItem = NULL;
if( m_iCurSel >= 0 ) pSelectedListItem = dynamic_cast<ListContainerElement*>(GetItemAt(m_iCurSel));
if( !ScrollableBox::SetItemIndex(pControl, iIndex) ) return false;
std::size_t iMinIndex = min((std::size_t)iOrginIndex, iIndex);
std::size_t iMaxIndex = max((std::size_t)iOrginIndex, iIndex);
for(std::size_t i = iMinIndex; i < iMaxIndex + 1; ++i) {
Control* pControl = GetItemAt(i);
ListContainerElement* pListItem = dynamic_cast<ListContainerElement*>(pControl);
if( pListItem != NULL ) {
pListItem->SetIndex((int)i);
}
}
if( m_iCurSel >= 0 && pSelectedListItem != NULL ) m_iCurSel = pSelectedListItem->GetIndex();
return true;
}
void ListBox::Previous()
{
if (m_iCurSel > 0) {
SelectItem(m_iCurSel - 1);
}
}
void ListBox::Next()
{
int count = GetCount();
if (m_iCurSel < count - 1) {
SelectItem(m_iCurSel + 1);
}
}
void ListBox::ActiveItem()
{
if (m_iCurSel >= 0) {
ListContainerElement* item = dynamic_cast<ListContainerElement*>( GetItemAt(m_iCurSel) );
item->InvokeDoubleClickEvent();
}
}
bool ListBox::Add(Control* pControl)
{
// Override the Add() method so we can add items specifically to
// the intended widgets. Headers are assumed to be
// answer the correct interface so we can add multiple list headers.
// The list items should know about us
ListContainerElement* pListItem = dynamic_cast<ListContainerElement*>(pControl);
if( pListItem != NULL ) {
pListItem->SetOwner(this);
pListItem->SetIndex(GetCount());
}
return ScrollableBox::Add(pControl);
}
bool ListBox::AddAt(Control* pControl, int iIndex)
{
// Override the AddAt() method so we can add items specifically to
// the intended widgets. Headers and are assumed to be
// answer the correct interface so we can add multiple list headers.
if (!ScrollableBox::AddAt(pControl, iIndex)) return false;
// The list items should know about us
ListContainerElement* pListItem = dynamic_cast<ListContainerElement*>(pControl);
if( pListItem != NULL ) {
pListItem->SetOwner(this);
pListItem->SetIndex(iIndex);
}
for(int i = iIndex + 1; i < GetCount(); ++i) {
Control* p = GetItemAt(i);
pListItem = dynamic_cast<ListContainerElement*>(p);
if( pListItem != NULL ) {
pListItem->SetIndex(i);
}
}
if( m_iCurSel >= iIndex ) m_iCurSel += 1;
return true;
}
bool ListBox::Remove(Control* pControl)
{
int iIndex = GetItemIndex(pControl);
if (iIndex == -1) return false;
return RemoveAt(iIndex);
}
bool ListBox::RemoveAt(int iIndex)
{
if (!ScrollableBox::RemoveAt(iIndex)) return false;
for(int i = iIndex; i < GetCount(); ++i) {
Control* p = GetItemAt(i);
ListContainerElement* pListItem = dynamic_cast<ListContainerElement*>(p);
if( pListItem != NULL ) pListItem->SetIndex(i);
}
if( iIndex == m_iCurSel && m_iCurSel >= 0 ) {
if (m_bSelNextWhenRemoveActive)
SelectItem(FindSelectable(m_iCurSel--, false));
else
m_iCurSel = -1;
}
else if( iIndex < m_iCurSel ) m_iCurSel -= 1;
return true;
}
void ListBox::RemoveAll()
{
m_iCurSel = -1;
ScrollableBox::RemoveAll();
}
bool ListBox::SortItems(PULVCompareFunc pfnCompare, UINT_PTR dwData)
{
if (!pfnCompare)
return false;
if (m_items.size() == 0)
{
return true;
}
m_pCompareFunc = pfnCompare;
m_pCompareData = dwData;
qsort_s(&(*m_items.begin()), m_items.size(), sizeof(Control*), ListBox::ItemComareFunc, this);
ListContainerElement *pItem = NULL;
for (int i = 0; i < (int)m_items.size(); ++i)
{
pItem = dynamic_cast<ListContainerElement*>(static_cast<Control*>(m_items[i]));
if (pItem) {
pItem->SetIndex(i);
pItem->Selected(false, true);
}
}
SelectItem(-1);
SetPos(GetPos());
Invalidate();
return true;
}
int __cdecl ListBox::ItemComareFunc(void *pvlocale, const void *item1, const void *item2)
{
ListBox *pThis = (ListBox*)pvlocale;
if (!pThis || !item1 || !item2)
return 0;
return pThis->ItemComareFunc(item1, item2);
}
int __cdecl ListBox::ItemComareFunc(const void *item1, const void *item2)
{
Control *pControl1 = *(Control**)item1;
Control *pControl2 = *(Control**)item2;
return m_pCompareFunc((UINT_PTR)pControl1, (UINT_PTR)pControl2, m_pCompareData);
}
bool ListBox::GetScrollSelect()
{
return m_bScrollSelect;
}
void ListBox::SetScrollSelect(bool bScrollSelect)
{
m_bScrollSelect = bScrollSelect;
}
/////////////////////////////////////////////////////////////////////////////////////
//
//
ListContainerElement::ListContainerElement() :
m_iIndex(-1),
m_pOwner(nullptr)
{
m_uTextStyle = DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_NOCLIP | DT_SINGLELINE;
SetReceivePointerMsg(false);
}
void ListContainerElement::SetVisible(bool bVisible)
{
__super::SetVisible(bVisible);
if (!IsVisible() && m_bSelected) {
m_bSelected = false;
if (m_pOwner != NULL) m_pOwner->SelectItem(-1);
}
}
void ListContainerElement::Selected(bool bSelected, bool trigger)
{
if (!IsEnabled()) return;
if (bSelected && m_pOwner != NULL) m_pOwner->SelectItem(m_iIndex, false, trigger);
}
void ListContainerElement::HandleMessage(EventArgs& event)
{
if (!IsMouseEnabled() && event.Type > kEventMouseBegin && event.Type < kEventMouseEnd) {
if (m_pOwner != NULL) m_pOwner->HandleMessageTemplate(event);
else Box::HandleMessage(event);
return;
}
else if (event.Type == kEventInternalDoubleClick) {
if (IsActivatable()) {
InvokeDoubleClickEvent();
}
return;
}
else if (event.Type == kEventKeyDown && IsEnabled()) {
if (event.chKey == VK_RETURN) {
if (IsActivatable()) {
if (m_pWindow != NULL) m_pWindow->SendNotify(this, kEventReturn);
}
return;
}
}
else if (event.Type == kEventInternalMenu && IsEnabled()) {
Selected(true, true);
m_pWindow->SendNotify(this, kEventMouseMenu);
Invalidate();
return;
}
__super::HandleMessage(event);
// An important twist: The list-item will send the event not to its immediate
// parent but to the "attached" list. A list may actually embed several components
// in its path to the item, but key-presses etc. needs to go to the actual list.
//if( m_pOwner != NULL ) m_pOwner->HandleMessage(event); else Control::HandleMessage(event);
}
IListOwner* ListContainerElement::GetOwner()
{
return m_pOwner;
}
void ListContainerElement::SetOwner(IListOwner* pOwner)
{
m_pOwner = pOwner;
}
int ListContainerElement::GetIndex() const
{
return m_iIndex;
}
void ListContainerElement::SetIndex(int iIndex)
{
m_iIndex = iIndex;
}
void ListContainerElement::InvokeDoubleClickEvent()
{
if( m_pWindow != NULL ) m_pWindow->SendNotify(this, kEventMouseDoubleClick);
}
} // namespace ui