duilib中的V和H佈局中滾動條問題

首先看一段xml代碼
 <?xml version="1.0" encoding="utf-8"?>
<Window size="300,200" caption="0,0,300,20">
	<HorizontalLayout name="aaaa" bkcolor="#FFEEEEEE" >

		<VerticalLayout name="xxxxxx" float="true" pos="50,20,250,195" hscrollbar="true" vscrollbar="true" sepheight="4" >
		
			<Label name="child1" text="text1" float="false" pos="0,0,300,200" bordercolor="#FFEE00EE" bkcolor="#FF0000EE" textcolor="#FF010101"/>
			<Label name="child2" text="text2" float="false" pos="0,0,200,200" bordercolor="#FFEE00EE" bkcolor="#FF0000EE" textcolor="#FF01FF01"/>

		</VerticalLayout>

	</HorizontalLayout>
</Window>

這是一個窗口,它包含一個豎的佈局,顯示出來初始狀態是這樣的:

在佈局中,2個子控件所需要的長和寬,比佈局本身的大小要大,所以需要2個滾動條來拉動顯示。

我們可以看到橫豎兩個滾動條。

不過,我們拉動滾動條,卻不能完全展示子控件。如下圖:

雖然橫向滾動條拉到了最右邊,但Label控件child1的右邊沒有展示出來。

檢查代碼,發現CVerticalLayoutUI的SetPos方法如下:

	void CVerticalLayoutUI::SetPos(RECT rc)
	{
		...省略若干代碼

		// Process the scrollbar
		ProcessScrollBar(rc, 0, cyNeeded);
	}

在最後面,調用了基類CContainerUI的ProcessScrollBar方法,來設置滾動條信息,ProcessScrollBar函數的第二個參數是設置橫向滾動條信息,第三個參數是豎向滾動條。此時設置橫向的參數爲0,豎向的是cyNeeded。顯然,這裏忽略了橫向的,所以在豎向佈局VerticalLayout中,橫向滾動條不能正常顯示。

在CVerticalLayoutUI::SetPos中,所有涉及到cyNeeded的代碼如下:

int cyNeeded = 0;
		int cyExpand = 0;
		if( nAdjustables > 0 ) cyExpand = MAX(0, (szAvailable.cy - cyFixed) / nAdjustables);
		// Position the elements
		SIZE szRemaining = szAvailable;
		int iPosY = rc.top;
		if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) {
			iPosY -= m_pVerticalScrollBar->GetScrollPos();
		}
		int iPosX = rc.left;
		if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) {
			iPosX -= m_pHorizontalScrollBar->GetScrollPos();
		}
		int iAdjustable = 0;
		int cyFixedRemaining = cyFixed;
		for( int it2 = 0; it2 < m_items.GetSize(); it2++ ) {
			CControlUI* pControl = static_cast<CControlUI*>(m_items[it2]);
			if( !pControl->IsVisible() ) continue;
			if( pControl->IsFloat() ) {
				SetFloatPos(it2);
				continue;
			}

			RECT rcPadding = pControl->GetPadding();
			szRemaining.cy -= rcPadding.top;
			SIZE sz = pControl->EstimateSize(szRemaining);
			if( sz.cy == 0 ) {
				iAdjustable++;
				sz.cy = cyExpand;
				// Distribute remaining to last element (usually round-off left-overs)
				if( iAdjustable == nAdjustables ) {
					sz.cy = MAX(0, szRemaining.cy - rcPadding.bottom - cyFixedRemaining);
				} 
				if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight();
				if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight();
			}
			else {
				if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight();
				if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight();
				cyFixedRemaining -= sz.cy;
			}

			sz.cx = pControl->GetFixedWidth();
			if( sz.cx == 0 ) sz.cx = szAvailable.cx - rcPadding.left - rcPadding.right;
			if( sz.cx < 0 ) sz.cx = 0;
			if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth();
			if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth();

			RECT rcCtrl = { iPosX + rcPadding.left, iPosY + rcPadding.top, iPosX + rcPadding.left + sz.cx, iPosY + sz.cy + rcPadding.top + rcPadding.bottom };
			pControl->SetPos(rcCtrl);

			iPosY += sz.cy + m_iChildPadding + rcPadding.top + rcPadding.bottom;
			cyNeeded += sz.cy + rcPadding.top + rcPadding.bottom;
			szRemaining.cy -= sz.cy + m_iChildPadding + rcPadding.bottom;
		}
		cyNeeded += (nEstimateNum - 1) * m_iChildPadding;
在for循環裏,會統計所有非float子控件的高度

在  cyNeeded += sz.cy + rcPadding.top + rcPadding.bottom;  裏,sz.cy是1個子控件的高度,而rcPadding = pControl->GetPadding(); 是控件的padding屬性,也就是外邊距。

最後yNeeded += (nEstimateNum - 1) * m_iChildPadding;  m_iChildPadding是佈局的childpadding屬性,也就是子控件之間的額外距離。

我們可以仿照cyNeeded的計算方式,來算出cxNeeded。因爲是豎向佈局,所以 cyNeeded += sz.cy 這裏是累加,但是橫向值,應該取最寬的控件的值,

所以for循環中的代碼應該是這樣:

int tmp = sz.cx + rcPadding.left + rcPadding.right;
cxNeeded = (tmp > cxNeeded) ? tmp: cxNeeded;

最後cxNeeded += (nEstimateNum - 1) * m_iChildPadding;

    此時通過對代碼的瞭解,可以得知:佈局在計算容量面積時,只計算非float類型的子控件,忽略了float類型的子控件。

算出了佈局的寬度後,就應用起來

修改原來的代碼爲:ProcessScrollBar(rc, cxNeeded, cyNeeded);


改完後,發現問題依然。於是,檢查了下ProcessScrollBar的代碼,如下

	void CContainerUI::ProcessScrollBar(RECT rc, int cxRequired, int cyRequired)
	{
		if( m_pHorizontalScrollBar != NULL && m_pHorizontalScrollBar->IsVisible() ) {
			RECT rcScrollBarPos = { rc.left, rc.bottom, rc.right, rc.bottom + m_pHorizontalScrollBar->GetFixedHeight()};
			m_pHorizontalScrollBar->SetPos(rcScrollBarPos);
		}

		if( m_pVerticalScrollBar == NULL ) return;

		if( cyRequired > rc.bottom - rc.top && !m_pVerticalScrollBar->IsVisible() ) {
			m_pVerticalScrollBar->SetVisible(true);
			m_pVerticalScrollBar->SetScrollRange(cyRequired - (rc.bottom - rc.top));
			m_pVerticalScrollBar->SetScrollPos(0);
			m_bScrollProcess = true;
			SetPos(m_rcItem);
			m_bScrollProcess = false;
			return;
		}
		// No scrollbar required
		if( !m_pVerticalScrollBar->IsVisible() ) return;

		// Scroll not needed anymore?
		int cyScroll = cyRequired - (rc.bottom - rc.top);
		if( cyScroll <= 0 && !m_bScrollProcess) {
			m_pVerticalScrollBar->SetVisible(false);
			m_pVerticalScrollBar->SetScrollPos(0);
			m_pVerticalScrollBar->SetScrollRange(0);
			SetPos(m_rcItem);
		}
		else
		{
			RECT rcScrollBarPos = { rc.right, rc.top, rc.right + m_pVerticalScrollBar->GetFixedWidth(), rc.bottom };
			m_pVerticalScrollBar->SetPos(rcScrollBarPos);

			if( m_pVerticalScrollBar->GetScrollRange() != cyScroll ) {
				int iScrollPos = m_pVerticalScrollBar->GetScrollPos();
				m_pVerticalScrollBar->SetScrollRange(::abs(cyScroll));
				if( m_pVerticalScrollBar->GetScrollRange() == 0 ) {
					m_pVerticalScrollBar->SetVisible(false);
					m_pVerticalScrollBar->SetScrollPos(0);
				}
				if( iScrollPos > m_pVerticalScrollBar->GetScrollPos() ) {
					SetPos(m_rcItem);
				}
			}
		}
	}
很明顯,cxRequired根本沒有用到。

對於橫向滾動條,只使用了這3行代碼:

		if( m_pHorizontalScrollBar != NULL && m_pHorizontalScrollBar->IsVisible() ) {
			RECT rcScrollBarPos = { rc.left, rc.bottom, rc.right, rc.bottom + m_pHorizontalScrollBar->GetFixedHeight()};
			m_pHorizontalScrollBar->SetPos(rcScrollBarPos);
		}
於是,我們需要仿照設置豎向滾動條的方式,來設置橫向滾動條:

void CContainerUI::ProcessScrollBar(RECT rc, int cxRequired, int cyRequired)
{
	while(m_pHorizontalScrollBar)
	{
		if (cxRequired > rc.right - rc.left && !m_pHorizontalScrollBar->IsVisible())
		{
			m_pHorizontalScrollBar->SetVisible(true);
			m_pHorizontalScrollBar->SetScrollRange(cxRequired - (rc.right - rc.left));
			m_pHorizontalScrollBar->SetScrollPos(0);
			m_bScrollProcess = true;
			SetPos(m_rcItem);
			m_bScrollProcess = false;
			break;
		}

		if( !m_pHorizontalScrollBar->IsVisible() ) break;

		int cxScroll = cxRequired - (rc.right - rc.left);
		if (cxScroll <= 0 && !m_bScrollProcess)
		{
			m_pHorizontalScrollBar->SetVisible(false);
			m_pHorizontalScrollBar->SetScrollPos(0);
			m_pHorizontalScrollBar->SetScrollRange(0);
			SetPos(m_rcItem);
		}
		else
		{
			RECT rcScrollBarPos = { rc.left, rc.bottom, rc.right, rc.bottom + m_pHorizontalScrollBar->GetFixedHeight() };
			m_pHorizontalScrollBar->SetPos(rcScrollBarPos);

			if( m_pHorizontalScrollBar->GetScrollRange() != cxScroll ) {
				int iScrollPos = m_pHorizontalScrollBar->GetScrollPos();
				m_pHorizontalScrollBar->SetScrollRange(::abs(cxScroll));
				if( m_pHorizontalScrollBar->GetScrollRange() == 0 ) {
					m_pHorizontalScrollBar->SetVisible(false);
					m_pHorizontalScrollBar->SetScrollPos(0);
				}
				if( iScrollPos > m_pHorizontalScrollBar->GetScrollPos() ) {
					SetPos(m_rcItem);
				}
			}
		}
		break;
	}

	while(m_pVerticalScrollBar)
	{
		if( cyRequired > rc.bottom - rc.top && !m_pVerticalScrollBar->IsVisible() ) {
			m_pVerticalScrollBar->SetVisible(true);
			m_pVerticalScrollBar->SetScrollRange(cyRequired - (rc.bottom - rc.top));
			m_pVerticalScrollBar->SetScrollPos(0);
			m_bScrollProcess = true;
			SetPos(m_rcItem);
			m_bScrollProcess = false;
			break;
		}
		// No scrollbar required
		if( !m_pVerticalScrollBar->IsVisible() ) break;

		// Scroll not needed anymore?
		int cyScroll = cyRequired - (rc.bottom - rc.top);
		if( cyScroll <= 0 && !m_bScrollProcess) {
			m_pVerticalScrollBar->SetVisible(false);
			m_pVerticalScrollBar->SetScrollPos(0);
			m_pVerticalScrollBar->SetScrollRange(0);
			SetPos(m_rcItem);
		}
		else
		{
			RECT rcScrollBarPos = { rc.right, rc.top, rc.right + m_pVerticalScrollBar->GetFixedWidth(), rc.bottom };
			m_pVerticalScrollBar->SetPos(rcScrollBarPos);

			if( m_pVerticalScrollBar->GetScrollRange() != cyScroll ) {
				int iScrollPos = m_pVerticalScrollBar->GetScrollPos();
				m_pVerticalScrollBar->SetScrollRange(::abs(cyScroll));
				if( m_pVerticalScrollBar->GetScrollRange() == 0 ) {
					m_pVerticalScrollBar->SetVisible(false);
					m_pVerticalScrollBar->SetScrollPos(0);
				}
				if( iScrollPos > m_pVerticalScrollBar->GetScrollPos() ) {
					SetPos(m_rcItem);
				}
			}
		}
		break;
	}
}

看看效果

可以看到控件child1的右邊框。


HorizontalLayout也有同樣的問題,可類似修改。

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