Files
2022-09-05 21:45:43 +08:00

369 lines
9.8 KiB
C++

#include "stdafx.h"
#include "CheckCombo.h"
namespace nim_comp
{
class CCheckComboWnd : public Window
{
public:
void Init(CheckCombo* pOwner);
virtual std::wstring GetWindowClassName() const override;
virtual void OnFinalMessage(HWND hWnd) override;
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
void OnSeleteItem();
private:
CheckCombo *m_pOwner;
int m_iOldSel;
bool m_bClosing = false;
};
void CCheckComboWnd::Init(CheckCombo* pOwner)
{
m_pOwner = pOwner;
//m_iOldSel = m_pOwner->GetCurSel();
// Position the popup window in absolute space
CSize szDrop = m_pOwner->GetDropBoxSize();
UiRect rcOwner = m_pOwner->GetOrgPos();
UiRect rc = rcOwner;
rc.top = rc.bottom + 1; // 父窗口left、bottom位置作为弹出窗口起点
rc.bottom = rc.top + szDrop.cy; // 计算弹出窗口高度
if (szDrop.cx > 0) rc.right = rc.left + szDrop.cx; // 计算弹出窗口宽度
CSize szAvailable(rc.right - rc.left, rc.bottom - rc.top);
int cyFixed = 0;
for (int it = 0; it < pOwner->GetListBox()->GetCount(); it++) {
Control* pControl = pOwner->GetListBox()->GetItemAt(it);
if (!pControl->IsVisible()) continue;
CSize sz = pControl->EstimateSize(szAvailable);
cyFixed += sz.cy;
}
cyFixed += 2; // VBox 默认的Padding 调整
rc.bottom = rc.top + min(cyFixed, szDrop.cy);
::MapWindowRect(pOwner->GetWindow()->GetHWND(), HWND_DESKTOP, &rc);
MONITORINFO oMonitor = {};
oMonitor.cbSize = sizeof(oMonitor);
::GetMonitorInfo(::MonitorFromWindow(GetHWND(), MONITOR_DEFAULTTOPRIMARY), &oMonitor);
UiRect rcWork = oMonitor.rcWork;
if (rc.bottom > rcWork.bottom || m_pOwner->IsPopupTop()) {
rc.left = rcOwner.left;
rc.right = rcOwner.right;
if (szDrop.cx > 0) rc.right = rc.left + szDrop.cx;
rc.top = rcOwner.top - min(cyFixed, szDrop.cy);
rc.bottom = rcOwner.top;
::MapWindowRect(pOwner->GetWindow()->GetHWND(), HWND_DESKTOP, &rc);
}
Create(pOwner->GetWindow()->GetHWND(), NULL, WS_POPUP, WS_EX_TOOLWINDOW, true, rc);
// HACK: Don't deselect the parent's caption
HWND hWndParent = m_hWnd;
while (::GetParent(hWndParent) != NULL)
hWndParent = ::GetParent(hWndParent);
::ShowWindow(m_hWnd, SW_SHOW);
::SendMessage(hWndParent, WM_NCACTIVATE, TRUE, 0L);
}
std::wstring CCheckComboWnd::GetWindowClassName() const
{
return _T("ComboWnd");
}
void CCheckComboWnd::OnFinalMessage(HWND hWnd)
{
m_pOwner->m_pCheckComboWnd = NULL;
m_pOwner->m_uButtonState = kControlStateNormal;
m_pOwner->Invalidate();
delete this;
}
void CCheckComboWnd::OnSeleteItem()
{
PostMessage(WM_KILLFOCUS);
}
LRESULT CCheckComboWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_CREATE) {
this->Window::Init(m_hWnd);
Box* pRoot = new Box;
pRoot->SetAutoDestroyChild(false);
pRoot->Add(m_pOwner->GetListBox());
this->AttachDialog(pRoot);
this->SetWindowResourcePath(m_pOwner->GetWindow()->GetWindowResourcePath());
this->SetShadowAttached(false);
return 0;
}
else if (uMsg == WM_CLOSE) {
m_pOwner->SetWindow(m_pOwner->GetWindow(), m_pOwner->GetParent(), false);
m_pOwner->SetPos(m_pOwner->GetPos());
m_pOwner->SetFocus();
}
else if (uMsg == WM_KILLFOCUS) {
if (m_hWnd != (HWND)wParam) {
m_bClosing = true;
PostMessage(WM_CLOSE);
((Box*)this->GetRoot())->RemoveAt(0);
m_pOwner->GetListBox()->PlaceHolder::SetWindow(nullptr, nullptr, false);
}
}
if (m_bClosing) {
return CallWindowProc(uMsg, wParam, lParam);
}
else {
bool handled = false;
LRESULT ret = this->DoHandlMessage(uMsg, wParam, lParam, handled);
if (handled) {
return ret;
}
else {
return CallWindowProc(uMsg, wParam, lParam);
}
}
}
////////////////////////////////////////////////////////
CheckCombo::CheckCombo() :
m_pCheckComboWnd(nullptr),
m_szDropBox(0, 150),
m_uButtonState(kControlStateNormal),
m_sDropBoxAttributes(),
m_bPopupTop(false),
m_iOrgHeight(20)
{
// The trick is to add the items to the new container. Their owner gets
// reassigned by this operation - which is why it is important to reassign
// the items back to the righfull owner/manager when the window closes.
m_pDropList.reset(new ListBox(new VLayout));
m_pDropList->GetLayout()->SetPadding(UiRect(1, 1, 1, 1));
m_pDropList->SetBkColor(L"bk_wnd_lightcolor");
m_pDropList->SetBorderColor(L"splitline_level1");
m_pDropList->SetBorderSize(UiRect(1, 1, 1, 1));
m_pDropList->EnableScrollBar();
m_pDropList->ApplyAttributeList(GetDropBoxAttributeList());
m_pList.reset(new ListBox(new VLayout));
//m_pList->SetMouseEnabled(false);
m_pList->AttachButtonDown(std::bind(&CheckCombo::OnListButtonDown, this, std::placeholders::_1));
m_pList->SetMouseChildEnabled(false);
//m_pList->SetAutoDestroyChild(false);
m_pList->EnableScrollBar();
Box::Add(m_pList.get());
SetMaxHeight(m_iOrgHeight * 3);
SetMinHeight(m_iOrgHeight);
}
CheckCombo::~CheckCombo()
{
Box::Remove(m_pList.get());
}
bool CheckCombo::Add(Control* pControl)
{
CheckBoxBox *pCheckBoxBox = nullptr;
CheckBox *pCheckBox = dynamic_cast<CheckBox*>(pControl);
if (pCheckBox)
{
pCheckBox->AttachSelect(std::bind(&CheckCombo::OnSelectItem, this, std::placeholders::_1));
pCheckBox->AttachUnSelect(std::bind(&CheckCombo::OnUnSelectItem, this, std::placeholders::_1));
}
else if (pCheckBoxBox = dynamic_cast<CheckBoxBox*>(pControl)) {
pCheckBoxBox->AttachSelect(std::bind(&CheckCombo::OnSelectItem, this, std::placeholders::_1));
pCheckBoxBox->AttachUnSelect(std::bind(&CheckCombo::OnUnSelectItem, this, std::placeholders::_1));
}
else
{
printf("CheckCombo::Add pControl is not CheckBox object\n");
assert(0);
return true;
}
m_pDropList->Add(pControl);
pControl->SetReceivePointerMsg(true);
return true;
}
bool CheckCombo::Remove(Control * pControl)
{
bool ret = m_pDropList->Remove(pControl);
return ret;
}
bool CheckCombo::RemoveAt(std::size_t iIndex)
{
bool ret = m_pDropList->RemoveAt((int)iIndex);
return ret;
}
void CheckCombo::RemoveAll()
{
m_pDropList->RemoveAll();
}
void CheckCombo::Activate()
{
if (!IsActivatable()) return;
if (m_pCheckComboWnd) return;
m_pCheckComboWnd = new CCheckComboWnd();
ASSERT(m_pCheckComboWnd);
m_pCheckComboWnd->Init(this);
if (m_pCheckComboWnd != NULL) m_pCheckComboWnd->SendNotify(this, kEventClick);
Invalidate();
}
void CheckCombo::SetAttribute(const std::wstring& strName, const std::wstring& strValue)
{
if (strName == _T("dropbox")) SetDropBoxAttributeList(strValue);
else if (strName == _T("vscrollbar")) {}
else if (strName == _T("dropboxsize")){
CSize szDropBoxSize;
LPTSTR pstr = NULL;
szDropBoxSize.cx = _tcstol(strValue.c_str(), &pstr, 10); ASSERT(pstr);
szDropBoxSize.cy = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr);
SetDropBoxSize(szDropBoxSize);
}
else if (strName == _T("popuptop")) SetPopupTop(strValue == _T("true"));
else if (strName == _T("height")) {
__super::SetAttribute(strName, strValue);
if (strValue != _T("stretch") && strValue != _T("auto")) {
ASSERT(_ttoi(strValue.c_str()) >= 0);
m_iOrgHeight = _ttoi(strValue.c_str());
SetMaxHeight(m_iOrgHeight * 3);
SetMinHeight(m_iOrgHeight);
}
}
else Box::SetAttribute(strName, strValue);
}
UiRect CheckCombo::GetOrgPos() const
{
UiRect rc = GetPosWithScrollOffset();
rc.bottom = rc.top + m_iOrgHeight;
return UiRect(rc);
}
std::wstring CheckCombo::GetDropBoxAttributeList()
{
return m_sDropBoxAttributes;
}
void CheckCombo::SetDropBoxAttributeList(const std::wstring& pstrList)
{
m_sDropBoxAttributes = pstrList;
}
CSize CheckCombo::GetDropBoxSize() const
{
return m_szDropBox;
}
void CheckCombo::SetDropBoxSize(CSize szDropBox)
{
DpiManager::GetInstance()->ScaleSize(szDropBox);
m_szDropBox = szDropBox;
}
bool CheckCombo::SelectItem(int iIndex)
{
/*if (iIndex < 0 || iIndex >= m_pDropList->GetCount() || m_iCurSel == iIndex)
return false;
m_iCurSel = iIndex;
m_pDropList->SelectItem(m_iCurSel);*/
return true;
}
Control* CheckCombo::GetItemAt(int iIndex)
{
return m_pDropList->GetItemAt(iIndex);
}
bool CheckCombo::OnSelectItem(EventArgs* args)
{
std::string date = args->pSender->GetUTF8DataID();
std::string text = date;
CheckBox *check = dynamic_cast<CheckBox*>(args->pSender);
if (check)
{
text = check->GetUTF8Text();
}
if (date.empty())
{
#ifdef _DEBUG
printf("CheckCombo::OnSelectItem date.empty()\n");
assert(0);
#endif
return true;
}
m_vecDate.push_back(date);
Label *item = new Label;
item->SetFixedWidth(DUI_LENGTH_AUTO);
item->SetFixedHeight(22);
item->SetMargin({ 4, 2, 4, 2 });
item->SetBkColor(L"bk_menuitem_selected");
item->SetTextPadding({ 2, 3, 2, 3 });
item->SetUTF8Name(date);
item->SetUTF8Text(text);
m_pList->Add(item);
SetFixedHeight(m_pList->GetCount() * m_iOrgHeight);
return true;
}
bool CheckCombo::OnUnSelectItem(EventArgs* args)
{
std::string date = args->pSender->GetUTF8DataID();
if (date.empty())
{
#ifdef _DEBUG
printf("CheckCombo::OnSelectItem date.empty()\n");
assert(0);
#endif
return true;
}
assert(std::find(m_vecDate.cbegin(), m_vecDate.cend(), date) != m_vecDate.cend());
if (std::find(m_vecDate.cbegin(), m_vecDate.cend(), date) != m_vecDate.cend())
{
m_vecDate.erase(std::find(m_vecDate.cbegin(), m_vecDate.cend(), date));
}
std::wstring utf16;
StringHelper::MBCSToUnicode(date, utf16, CP_UTF8);
Control *pRemove = m_pList->FindSubControl(utf16);
assert(pRemove);
if (pRemove)
m_pList->Remove(pRemove);
SetFixedHeight(m_pList->GetCount() * m_iOrgHeight);
return true;
}
void CheckCombo::ClearAllDate()
{
m_pList->RemoveAll();
m_pDropList->RemoveAll();
SetFixedHeight(m_iOrgHeight);
m_vecDate.clear();
}
bool CheckCombo::OnListButtonDown(EventArgs* args)
{
Activate();
return true;
}
}