Duilib自定義控件——水平佈局的滾動擴展布局,實現菜單的可滑動效果

爲了實現如下效果:

左側爲固定菜單,右側爲可滑動菜單。當窗口足夠大,菜單可全部展顯示。

 

 窗口變小時,菜單隻能顯示一部分。滑動到最左側,左滑按鈕灰掉

 

 左滑右滑按鈕皆可用

 

 滑動到最右側,右滑按鈕灰掉。

 

需求:

當調整窗口大小,菜單不能完全顯示時,右側出現調整按鈕,左鍵右鍵可調整切換菜單。

1.當只是調整窗口大小時,要顯示可顯示item的全部,而不是顯示item的一部分

2.當點擊向左向右時,一次調整一個菜單,且是一整個菜單而不是一部分

 

思路:

1.調整窗口大小時,計算出菜單全部顯示寬度,和該控件大小做比較,若不能全部顯示,則只顯示到能夠完全顯示的菜單。並顯示右側調整區域

2.右鍵按下時菜單要整體左移。將每個菜單用索引0,1,2…表示,並記錄顯示的起始索引。nBeginIndex。遍歷控件,索引小於nBeginIndex的不顯示,大於nBeginIndex的計算總長度,小於目前控件大小的顯示,大於的時候就不可見。

代碼如下:

自定義控件CHScrollLayoutUI

custom_scrollh_layout.h

#pragma once
/*!
 * \class CHScrollLayoutUI
 *
 * \水平佈局的滾動擴展布局,當水平佈局中的子控件的所有寬度超過水平佈局的可見寬度時候可以滾動切換,解決水平佈局中item遮擋的問題
 *
 */
class CHScrollLayoutUI :
	public CHorizontalLayoutUI
{
public:
	CHScrollLayoutUI();
	~CHScrollLayoutUI();

	virtual LPCTSTR GetClass() const override;
	virtual LPVOID GetInterface(LPCTSTR pstrName) override;
	virtual void SetPos(RECT rc, bool bNeedInvalidate = true) override;
	virtual void DoInit() override;
private:
	bool OnNotify(void *pParam);
	int GetItemWidth(int nIndex);
private:
	CContainerUI *m_pAdjustContainer;
	CButtonUI *m_pButtonTag;
	CButtonUI *m_pButtonPre;
	CButtonUI *m_pButtonNext;
	int m_nBeginItemIndex;		//左側顯示的第一個可見item的索引
	int m_nAdjustRegionWidth;
};

custom_scrollh_layout.cpp

#include "..\stdafx.h"
#include "custom_scrollh_layout.h"

#define WATCHING_DETAILS_LIST_HEAD_XML				_T("scroll_layout_adjust_region.xml")

CHScrollLayoutUI::CHScrollLayoutUI() :m_pAdjustContainer(NULL)
{
	m_nBeginItemIndex = 0;
	m_nAdjustRegionWidth = 0;
	m_pButtonPre = NULL; 
	m_pButtonNext = NULL;
}


CHScrollLayoutUI::~CHScrollLayoutUI()
{
}

LPCTSTR CHScrollLayoutUI::GetClass() const
{
	return _T("HScrollLayoutUI");
}

LPVOID CHScrollLayoutUI::GetInterface(LPCTSTR pstrName)
{
	if (_tcsicmp(pstrName, _T("HScrollLayout")) == 0)
	{
		return static_cast<CHScrollLayoutUI*>(this);
	}
	return CHorizontalLayoutUI::GetInterface(pstrName);
}

void CHScrollLayoutUI::SetPos(RECT rc, bool bNeedInvalidate /*= true*/)
{
	int nTotalWidth = 0;
	//計算整體寬度
	for (int it1 = 0; it1 < m_items.GetSize() - 2; it1++)
	{
		nTotalWidth += GetItemWidth(it1);
	}
	//計算是否顯示右側索引切換區域
	int nrcWidth = rc.right - rc.left;
	bool bShowAdjustRegion = false;
	int nShowWidth = 0;
	int nRightItemIndex = 0;//最右側顯示的item索引
	if (nrcWidth > nTotalWidth)
	{
		nShowWidth = nrcWidth;
		//全部能夠顯示[0,end]
		m_nBeginItemIndex = 0;
		nRightItemIndex = m_items.GetSize() - 2;
	}
	else
	{
		bShowAdjustRegion = true;
		nShowWidth = nrcWidth - m_nAdjustRegionWidth;
		int nItemWidth = 0;
		for (int it1 = m_nBeginItemIndex; it1 < m_items.GetSize() - 2; it1++)
		{
			nItemWidth += GetItemWidth(it1);
			if (nItemWidth < nShowWidth)
			{
				//最多顯示到第幾個索引
				nRightItemIndex = it1;
			}
			else
			{
				break;
			}
		}
		//在[begin,end]閉區間索引內item足夠顯示後還有剩餘
		int nDeltaX = nShowWidth - nItemWidth;
		while (nDeltaX > 0 && m_nBeginItemIndex >= 1 && nDeltaX > GetItemWidth(m_nBeginItemIndex - 1))
		{
			nDeltaX -= GetItemWidth(--m_nBeginItemIndex);
		}
	}
	if (m_pAdjustContainer)
	{
		m_pAdjustContainer->SetVisible(bShowAdjustRegion);
	}
	
	for (int it1 = 0; it1 < m_items.GetSize() - 2; it1++)
	{
		CControlUI* pControl = static_cast<CControlUI*>(m_items[it1]);
		//設置索引內可見的item
		if (it1 < m_nBeginItemIndex || it1 > nRightItemIndex)
		{
			pControl->SetVisible(false);
		}
		else
		{
			pControl->SetVisible(true);
		}
	}
	if (bShowAdjustRegion)
	{
		if (m_pButtonPre && m_pButtonNext)
		{
			//調整按鈕狀態
			if (m_nBeginItemIndex == 0)
			{
				//最左
				m_pButtonPre->SetEnabled(false);
				m_pButtonNext->SetEnabled(true);
			}
			else
			{
				m_pButtonPre->SetEnabled(true);
				if (nRightItemIndex == m_items.GetSize() - 3)
				{
					m_pButtonNext->SetEnabled(false);
				}
				else
				{
					m_pButtonNext->SetEnabled(true);
				}
			}
		}
	}
	__super::SetPos(rc, bNeedInvalidate);
}

void CHScrollLayoutUI::DoInit()
{
	__super::DoInit();

	CDuiString xmlAdjustPos = _T("<Window><Control /></Window>");
	CDialogBuilder builder1;
	CContainerUI *pContaine1 = static_cast<CContainerUI*>(builder1.Create(xmlAdjustPos.GetData(), (UINT)0, NULL, m_pManager));
	if (pContaine1) {
		this->Add(pContaine1);
	}

	CDialogBuilder builder2;
	CContainerUI *pContainer2 = static_cast<CContainerUI*>(builder2.Create(WATCHING_DETAILS_LIST_HEAD_XML, (UINT)0, NULL, m_pManager));
	if (pContainer2) {
		this->Add(pContainer2);
	}
	m_pAdjustContainer = pContainer2;

	if (m_pAdjustContainer)
	{
		int nAdjustRegionWidth = m_pAdjustContainer->GetFixedWidth();
		if (nAdjustRegionWidth <= 0)
		{
			nAdjustRegionWidth = m_pAdjustContainer->GetMaxWidth();
		}
		m_nAdjustRegionWidth = nAdjustRegionWidth;
	}
	
	if (pContainer2)
	{
		CButtonUI *pLeftBtn = static_cast<CButtonUI*>(pContainer2->FindSubControl(_T("btn_pre")));
		CButtonUI *pRightBtn = static_cast<CButtonUI*>(pContainer2->FindSubControl(_T("btn_next")));

		if (pLeftBtn && pRightBtn)
		{
			pLeftBtn->OnNotify += MakeDelegate(this, &CHScrollLayoutUI::OnNotify);
			pRightBtn->OnNotify += MakeDelegate(this, &CHScrollLayoutUI::OnNotify);
			m_pButtonPre = pLeftBtn;
			m_pButtonNext = pRightBtn;
		}
	}
	
}

bool CHScrollLayoutUI::OnNotify(void *pParam)
{
	TNotifyUI* pNotify = static_cast<TNotifyUI*>(pParam);
	if (pNotify == NULL)
	{
		return false;
	}
	if (pNotify->sType == DUI_MSGTYPE_CLICK && pNotify->pSender->GetName() == _T("btn_pre"))
	{
		m_nBeginItemIndex--;
		SetPos(m_rcItem, true);
	}
	if (pNotify->sType == DUI_MSGTYPE_CLICK && pNotify->pSender->GetName() == _T("btn_next"))
	{
		m_nBeginItemIndex++;
		SetPos(m_rcItem, true);
	}
	return true;
}

int CHScrollLayoutUI::GetItemWidth(int nIndex)
{
	int nWidth = 0;
	CControlUI* pControl = static_cast<CControlUI*>(m_items[nIndex]);
	//間距
	RECT rcPadding = pControl->GetPadding();
	//寬度
	int iControlMaxWidth = pControl->GetFixedWidth();
	if (iControlMaxWidth <= 0) iControlMaxWidth = pControl->GetMaxWidth();

	nWidth += rcPadding.left;
	nWidth += rcPadding.right;
	nWidth += iControlMaxWidth;

	return nWidth;
}

 main_frame.h

#pragma once
class CMainFrame : public WindowImplBase
{
public:
	CMainFrame();
	~CMainFrame();
	virtual CDuiString GetSkinFolder();
	virtual CDuiString GetSkinFile();
	virtual LPCTSTR GetWindowClassName(void) const;
	virtual void InitWindow() override;
	virtual CControlUI* CreateControl(LPCTSTR pstrClass) override;

protected:
	CWndShadow m_WndShadow;
};

  main_frame.cpp

#include "stdafx.h"
#include "main_frame.h"
#include "dui_base\custom_scrollh_layout.h"


CMainFrame::CMainFrame()
{
}


CMainFrame::~CMainFrame()
{
}

DuiLib::CDuiString CMainFrame::GetSkinFolder()
{
	return _T("");
}

DuiLib::CDuiString CMainFrame::GetSkinFile()
{
	return _T("main_frame.xml");
}

LPCTSTR CMainFrame::GetWindowClassName(void) const
{
	return _T("TestWindowClass");
}

void CMainFrame::InitWindow()
{
	m_WndShadow.Initialize(GetModuleHandle(NULL));
	m_WndShadow.Create(m_hWnd);
	m_WndShadow.SetSize(6);
	RECT rcCorner = { 3,4,3,4 };
	RECT rcHoleOffset = { 0,0,0,0 };
	m_WndShadow.SetImage(_T("res/normal_shadow.png"), rcCorner, rcHoleOffset);
}

DuiLib::CControlUI* CMainFrame::CreateControl(LPCTSTR pstrClass)
{
	if (_tcsicmp(pstrClass, "HScrollLayout") == 0)
	{
		return	new CHScrollLayoutUI();
	}
	return NULL;
}

TestDui.cpp

// TestDui.cpp : 定義應用程序的入口點。
#include "stdafx.h"
#include "main_frame.h"
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int nCmdShow)
{
	CPaintManagerUI::SetInstance(hInstance);
	CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath() + _T("skinTestDui\\"));
	
	HRESULT Hr = ::CoInitialize(NULL);
	if (FAILED(Hr)) return 0;

	CMainFrame *pMainDlg = new CMainFrame();
	pMainDlg->Create(NULL, _T("測試duilib"), UI_WNDSTYLE_FRAME, 0L, 0, 0, 0, 0);
	pMainDlg->CenterWindow();
	pMainDlg->ShowWindow(true);
	CPaintManagerUI::MessageLoop();

	::CoUninitialize();
    return (int) 0;
}

stdafx.h 

#if !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)
#define AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_

#pragma once

#define WIN32_LEAN_AND_MEAN	
#define _CRT_SECURE_NO_DEPRECATE

#include <windows.h>
#include <objbase.h>

#include "..\DuiLib\UIlib.h"

using namespace DuiLib;

#ifdef _DEBUG
#   ifdef _UNICODE
#       pragma comment(lib, "..\\Lib\\DuiLib_ud.lib")
#   else
#       pragma comment(lib, "..\\Lib\\DuiLib_d.lib")
#   endif
#else
#   ifdef _UNICODE
#       pragma comment(lib, "..\\Lib\\DuiLib_u.lib")
#   else
#       pragma comment(lib, "..\\Lib\\DuiLib.lib")
#   endif
#endif


//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)

stdafx.cpp

// stdafx.cpp : source file that includes just the standard includes
//	App.pch will be the pre-compiled header
//	stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"

#if defined _M_IX86
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_IA64
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_X64
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#else
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif

 資源文件

font.xml

<?xml version="1.0" encoding="utf-8"?>
<Window>
	<!--字體命名規則[字號&UNDERLINE&BOLD],UNDERLINE取值0,1,BOLD取值0,1-->
	<Font shared="true" id="1700" name="微軟雅黑" size="17" bold="false"/>
	<Font shared="true" id="1701" name="微軟雅黑" size="17" bold="true"/>
</Window>

main_frame.xml

<?xml version="1.0" encoding="utf-8"?>
<Window size="700,500" mininfo="300,300" sizebox="4,4,6,6" caption="0,0,0,30">
	<Include source="font.xml"/>
	<VerticalLayout  bkcolor="#FFFFFFFF" > 
		<!--一、標題欄 -->
		<HorizontalLayout name="title" height="30" bkcolor="#FF27282E">
			<Control />
			<Label width="100" bkcolor="#FF262A30" text="測試用" font="1700" textcolor="#FFE6E6E6"/>
			<Control />
		</HorizontalLayout>
		<Control height="1" bkcolor="#FF0C0E17"/>
		
		<!--二、菜單欄-->		
		<HorizontalLayout name="menu" inset="0,3,0,0" height="50" bkcolor="#FF212126" inset="0,3,0,0">
			<!--1、左側固定菜單-->
			<Control width="12" />
			<Button name="nav_back_btn" width="48" height="42" cursor="hand"  
					normalimage="file='res/nav_back_bk.png' source='0,0,48,42' dest='0,0,48,42'" 
					hotimage="file='res/nav_back_bk.png' source='48,0,96,42' dest='0,0,48,42'"  
					pushedimage="file='res/nav_back_bk.png' source='96,0,144,42' dest='0,0,48,42'" />
			<Control width="2"/>
			<Button name="home_page_btn" width="51" height="42" cursor="hand"  
					normalimage="file='res/nav_bk_home.png' source='0,0,51,42' dest='0,0,51,42'" 
					hotimage="file='res/nav_bk_home.png' source='51,0,102,42' dest='0,0,51,42'"  
					pushedimage="file='res/nav_bk_home.png' source='102,0,153,42' dest='0,0,51,42'" />
			<Control width="50"/>
			
			<!--2、右側可滑動菜單-->
			<HScrollLayout>	
				<Button name="nav_mine_btn" width="51" height="42" cursor="hand"  padding="0,0,2,0"
						normalimage="file='res/nav_bk_mine.png' source='0,0,51,42' dest='0,0,51,42'" 
						hotimage="file='res/nav_bk_mine.png' source='51,0,102,42' dest='0,0,51,42'"  
						pushedimage="file='res/nav_bk_mine.png' source='102,0,153,42' dest='0,0,51,42'" />
				<Button name="nav_zhibo_btn" width="51" height="42" cursor="hand"  padding="0,0,2,0"
						normalimage="file='res/nav_bk_video.png' source='0,0,51,42' dest='0,0,51,42'" 
						hotimage="file='res/nav_bk_video.png' source='51,0,102,42' dest='0,0,51,42'"  
						pushedimage="file='res/nav_bk_video.png' source='102,0,153,42' dest='0,0,51,42'" />
				<Button name="nav_scjk_btn" width="68" height="42" cursor="hand"  padding="0,0,2,0"
						normalimage="file='res/nav_bk_scjk.png' source='0,0,68,42' dest='0,0,68,42'" 
						hotimage="file='res/nav_bk_scjk.png' source='68,0,136,42' dest='0,0,68,42'"  
						pushedimage="file='res/nav_bk_scjk.png' source='136,0,204,42' dest='0,0,68,42'" />
				<VerticalLayout width="50" height="42" padding="0,0,2,0">
					<Button name="nav_block_hy_btn" width="50" height="20" cursor="hand"  
							normalimage="file='res/nav_bk_hy.png' source='0,0,50,20' dest='0,0,50,20'" 
							hotimage="file='res/nav_bk_hy.png' source='50,0,100,20' dest='0,0,50,20'"  
							pushedimage="file='res/nav_bk_hy.png' source='100,0,150,20' dest='0,0,50,20'" />
					<Control width="2"/>
					<Button name="nav_block_dy_btn" width="50" height="20" cursor="hand"  
							normalimage="file='res/nav_bk_dy.png' source='0,0,50,20' dest='0,0,50,20'" 
							hotimage="file='res/nav_bk_dy.png' source='50,0,100,20' dest='0,0,50,20'"  
							pushedimage="file='res/nav_bk_dy.png' source='100,0,150,20' dest='0,0,50,20'" />
				</VerticalLayout>			
				<VerticalLayout width="50" height="42" padding="0,0,5,0">
					<Button name="nav_block_gn_btn" width="50" height="20" cursor="hand"  
							normalimage="file='res/nav_bk_gn.png' source='0,0,50,20' dest='0,0,50,20'" 
							hotimage="file='res/nav_bk_gn.png' source='50,0,100,20' dest='0,0,50,20'"  
							pushedimage="file='res/nav_bk_gn.png' source='100,0,150,20' dest='0,0,50,20'" />
					<Control width="2"/>
					<Button name="nav_block_fg_btn" width="50" height="20" cursor="hand"  
							normalimage="file='res/nav_bk_fg.png' source='0,0,50,20' dest='0,0,50,20'" 
							hotimage="file='res/nav_bk_fg.png' source='50,0,100,20' dest='0,0,50,20'"  
							pushedimage="file='res/nav_bk_fg.png' source='100,0,150,20' dest='0,0,50,20'" />
				</VerticalLayout>
				<Button name="nav_zjph_btn" width="68" height="42" cursor="hand"  padding="0,0,2,0"
							normalimage="file='res/nav_bk_zjph.png' source='0,0,68,42' dest='0,0,68,42'" 
							hotimage="file='res/nav_bk_zjph.png' source='68,0,136,42' dest='0,0,68,42'"  
							pushedimage="file='res/nav_bk_zjph.png' source='136,0,204,42' dest='0,0,68,42'" />				
				<Button name="nav_lhb_btn" width="68" height="42" cursor="hand"  
							normalimage="file='res/nav_bk_lhb.png' source='0,0,68,42' dest='0,0,68,42'" 
							hotimage="file='res/nav_bk_lhb.png' source='68,0,136,42' dest='0,0,68,42'"  
							pushedimage="file='res/nav_bk_lhb.png' source='136,0,204,42' dest='0,0,68,42'" />
			</HScrollLayout>			
		</HorizontalLayout>
	</VerticalLayout>
</Window>

scroll_layout_adjust_region.xml

<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<Window>
	<HorizontalLayout width="60"> 
		<Control />
		<VerticalLayout width="2" padding="10,0,10,0">
			<Control />
			<Label width="2" height="30" bkimage="res/sep_2.png"  />
			<Control />
		</VerticalLayout>
		<VerticalLayout width="11">
			<Control />
			<Button name="btn_pre" width="11" height="16" keyboard="false" normalimage="file='res/button_pre.png' source='0,0,11,16'"	hotimage="file='res/button_pre.png' source='11,0,22,16'" pushedimage="file='res/button_pre.png' source='22,0,33,16'" disabledimage="file='res/button_pre.png' source='33,0,44,16'" />
			<Control />
		</VerticalLayout>
		<VerticalLayout width="11" padding="5,0,0,0">
			<Control />
			<Button name="btn_next" width="11" height="16" keyboard="false" normalimage="file='res/button_next.png' source='0,0,11,16'"	hotimage="file='res/button_next.png' source='11,0,22,16'" pushedimage="file='res/button_next.png' source='22,0,33,16'" disabledimage="file='res/button_next.png' source='33,0,44,16'" />
			<Control />
		</VerticalLayout>
		<Control />
	</HorizontalLayout> 
</Window>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章