为了实现如下效果:
左侧为固定菜单,右侧为可滑动菜单。当窗口足够大,菜单可全部展显示。
窗口变小时,菜单只能显示一部分。滑动到最左侧,左滑按钮灰掉
左滑右滑按钮皆可用
滑动到最右侧,右滑按钮灰掉。
需求:
当调整窗口大小,菜单不能完全显示时,右侧出现调整按钮,左键右键可调整切换菜单。
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>