zoukankan      html  css  js  c++  java
  • 提高duilib的richedit控制的一些特征

    转载请注明原始出处。谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/41208207


          假设要使用透明异形窗口功能,首先要改进duilib库让他本身支持(能够下载duilib扩展群群主改进的库。或者下载我的库),然后要开启窗口的bktrans属性。

    这时仅仅要使用透明的背景素材就能做出透明异形窗口。可是透明窗口并不好驾驭,会带来非常多麻烦。当中之中的一个就是原Edit控件无法使用。这时改用Richedit控件是不错的选择。


          RichEdit有非常多优势,一是支持透明窗口、二十属性更丰富功能很多其它,他本身就能够是透明背景,同一时候还是容器,能够容纳其它控件。

    只是我在使用他的过程中发现几点不足,所以做了简单的改进,记录到博客里。


         改进例如以下:


    1.richedit 控件的容器布局基类从Container改为HorizontalLayout。能够支持相对布局。让richedit能够内嵌更复杂灵活的布局
    2.richedit 添加textpadding属性。方便控制布局。控制文字和光标的输出范围,而不须要用原来的inset属性来控制光标的位置
    3.richedit添加四种状态的图片。normal、hot、focus、disable。来完毕一些细节效果的显示


    改进1:

         richedit本身是个容器。这点非常不错,可是他继承自CContainer容器,本身没有布局功能,这点非常不好。我这里做改进时没有让他继承CHorizontalLayout类,由于richedit已经重写了SetPos函数,直接用CHorizontalLayout的SetPos函数的逻辑代码替换掉richedit的SetPos函数的部分代码即可了。记住不要所有替换,由于richedit的SetPos函数的前段的代码是处理richedit光标的代码。改动后的完整代码例如以下:


    void CRichEditUI::SetPos(RECT rc)
    {
        CControlUI::SetPos(rc);
        rc = m_rcItem;
    
        rc.left += m_rcInset.left;
        rc.top += m_rcInset.top;
        rc.right -= m_rcInset.right;
        rc.bottom -= m_rcInset.bottom;
        bool bVScrollBarVisiable = false;
        if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) {
            bVScrollBarVisiable = true;
            rc.right -= m_pVerticalScrollBar->GetFixedWidth();
        }
        if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) {
            rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight();
        }
    
    	if( m_pTwh ) {
    		RECT rcRich = rc;
    		rcRich.left += m_rcTextPadding.left;
    		rcRich.right -= m_rcTextPadding.right;
    		rcRich.top += m_rcTextPadding.top;
    		rcRich.bottom -= m_rcTextPadding.bottom;
    		m_pTwh->SetClientRect(&rcRich);
    		if( bVScrollBarVisiable && (!m_pVerticalScrollBar->IsVisible() || m_bVScrollBarFixing) ) {
    			LONG lWidth = rcRich.right - rcRich.left + m_pVerticalScrollBar->GetFixedWidth();
    			LONG lHeight = 0;
    			SIZEL szExtent = { -1, -1 };
    			m_pTwh->GetTextServices()->TxGetNaturalSize(
    				DVASPECT_CONTENT, 
    				GetManager()->GetPaintDC(), 
    				NULL,
    				NULL,
    				TXTNS_FITTOCONTENT,
    				&szExtent,
    				&lWidth,
    				&lHeight);
    			if( lHeight > rcRich.bottom - rcRich.top ) {
    				m_pVerticalScrollBar->SetVisible(true);
    				m_pVerticalScrollBar->SetScrollPos(0);
    				m_bVScrollBarFixing = true;
    			}
    			else {
    				if( m_bVScrollBarFixing ) {
    					m_pVerticalScrollBar->SetVisible(false);
    					m_bVScrollBarFixing = false;
    				}
    			}
    		}
    	}
    
        if( m_pVerticalScrollBar != NULL && m_pVerticalScrollBar->IsVisible() ) {
            RECT rcScrollBarPos = { rc.right, rc.top, rc.right + m_pVerticalScrollBar->GetFixedWidth(), rc.bottom};
            m_pVerticalScrollBar->SetPos(rcScrollBarPos);
        }
        if( m_pHorizontalScrollBar != NULL && m_pHorizontalScrollBar->IsVisible() ) {
            RECT rcScrollBarPos = { rc.left, rc.bottom, rc.right, rc.bottom + m_pHorizontalScrollBar->GetFixedHeight()};
            m_pHorizontalScrollBar->SetPos(rcScrollBarPos);
        }
    
    	SIZE szAvailable = { rc.right - rc.left, rc.bottom - rc.top };
    	if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) 
    		szAvailable.cx += m_pHorizontalScrollBar->GetScrollRange();
    
    	int nAdjustables = 0;
    	int cxFixed = 0;
    	int nEstimateNum = 0;
    	for( int it1 = 0; it1 < m_items.GetSize(); it1++ ) {
    		CControlUI* pControl = static_cast<CControlUI*>(m_items[it1]);
    		if( !pControl->IsVisible() ) continue;
    		if( pControl->IsFloat() ) continue;
    		SIZE sz = pControl->EstimateSize(szAvailable);
    		if( sz.cx == 0 ) {
    			nAdjustables++;
    		}
    		else {
    			if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth();
    			if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth();
    		}
    		cxFixed += sz.cx +  pControl->GetPadding().left + pControl->GetPadding().right;
    		nEstimateNum++;
    	}
    	cxFixed += (nEstimateNum - 1) * m_iChildPadding;
    
    	int cxExpand = 0;
        int cxNeeded = 0;
    	if( nAdjustables > 0 ) cxExpand = MAX(0, (szAvailable.cx - cxFixed) / nAdjustables);
    	// Position the elements
    	SIZE szRemaining = szAvailable;
    	int iPosX = rc.left;
    	if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) {
    		iPosX -= m_pHorizontalScrollBar->GetScrollPos();
    	}
    	int iAdjustable = 0;
    	int cxFixedRemaining = cxFixed;
    	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.cx -= rcPadding.left;
    		SIZE sz = pControl->EstimateSize(szRemaining);
    		if( sz.cx == 0 ) {
    			iAdjustable++;
    			sz.cx = cxExpand;
    
    			if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth();
    			if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth();
    		}
    		else {
    			if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth();
    			if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth();
    
    		}
    
    		sz.cy = pControl->GetFixedHeight();
    		if( sz.cy == 0 ) sz.cy = rc.bottom - rc.top - rcPadding.top - rcPadding.bottom;
    		if( sz.cy < 0 ) sz.cy = 0;
    		if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight();
    		if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight();
    
    		RECT rcCtrl = { iPosX + rcPadding.left, rc.top + rcPadding.top, iPosX + sz.cx + rcPadding.left , rc.top + rcPadding.top + sz.cy};
    		pControl->SetPos(rcCtrl);
    		iPosX += sz.cx + m_iChildPadding + rcPadding.left + rcPadding.right;
            cxNeeded += sz.cx + rcPadding.left + rcPadding.right;
    		szRemaining.cx -= sz.cx + m_iChildPadding + rcPadding.right;
    	}
        cxNeeded += (nEstimateNum - 1) * m_iChildPadding;
    	//reddrain
    	if( m_pHorizontalScrollBar != NULL ) {
    		if( cxNeeded > rc.right - rc.left ) {
    			if( m_pHorizontalScrollBar->IsVisible() ) {
    				m_pHorizontalScrollBar->SetScrollRange(cxNeeded - (rc.right - rc.left));
    			}
    			else {
    				m_pHorizontalScrollBar->SetVisible(true);
    				m_pHorizontalScrollBar->SetScrollRange(cxNeeded - (rc.right - rc.left));
    				m_pHorizontalScrollBar->SetScrollPos(0);
    				rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight();
    			}
    		}
    		else {
    			if( m_pHorizontalScrollBar->IsVisible() ) {
    				m_pHorizontalScrollBar->SetVisible(false);
    				m_pHorizontalScrollBar->SetScrollRange(0);
    				m_pHorizontalScrollBar->SetScrollPos(0);
    				rc.bottom += m_pHorizontalScrollBar->GetFixedHeight();
    			}
    		}
    	}
    	//redrain
    
    }
    



            这样子改进后,就能够非常easy的做出仿酷狗的搜索栏的效果,也就是richedit内嵌一个button。而不须要用到绝对布局来控制button,也不须要为button的位置自适应文字操心。



             相应的布局代码例如以下:

    <RichEdit name="Edt_Title_Search" rich="false" multiline="false" font="0" text="张学友 童真年代" height="27" textpadding="9,3,35,5" textcolor="#646464" bkcolor="#00FFFFFF" bkimage="UI	itleedit.png" >
    	<Control height="1"/>
    	<Button name="Btn_Title_Search" width="33" height="27" normalimage="UI	itlesearch_normal.png" hotimage="UI	itlesearch_hover.png" pushedimage="UI	itlesearch_down.png" />
    </RichEdit>	

           这个改进我已经用到仿酷狗里面了,大家能够下载我的源代码去看。布局中设置了一个高度为1的Control,起到了占位作用。让button能够自适应位置。


    改进2:


         在duilib的CRichEditUI控件中,richedit的功能,实际上是调用了系统的richedit的接口来完毕的。在CRichEditUI中把这个richedit当作了容器中的一个控件,所以这个richedit的位置,是在SetPos函数中去指定的。在原本CRichEditUI中。要想控制文本的输入区域和光标的位置,须要用到inset属性来控制,这显然和Label以及Edit控件的用法不一样。Label和Edit都是用textpadding属性来控制的,所以改动了CRichEdit的代码。让他改用textpadding属性来控制。


         首先要在UIRichEdit.h文件里添加两个成员函数和一个成员变量。而且在构造函数里初始化,具体的相信也不须要多说:

    	RECT GetTextPadding() const;
    	void SetTextPadding(RECT rc);
    	RECT m_rcTextPadding;


         然后改动SetAttribute函数,添加例如以下代码:

    	else if( _tcscmp(pstrName, _T("textpadding")) == 0 ) {
    		RECT rcTextPadding = { 0 };
    		LPTSTR pstr = NULL;
    		rcTextPadding.left = _tcstol(pstrValue, &pstr, 10);  ASSERT(pstr);    
    		rcTextPadding.top = _tcstol(pstr + 1, &pstr, 10);    ASSERT(pstr);    
    		rcTextPadding.right = _tcstol(pstr + 1, &pstr, 10);  ASSERT(pstr);    
    		rcTextPadding.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr);    
    		SetTextPadding(rcTextPadding);
    	}

         最后改动SetPos函数。在函数里依据m_rcTextPadding的值去布局richedit组件的位置就能够了。SetPos函数已经在前面给出了。


    改进3:


         在Edit控件中,支持四种状态的图片:normal、hot、focus、disable。这样能够做到一些细微的效果,比方鼠标移动到edit上面后让控件边框变亮,这点能够看QQ登录器的帐号和password输入框。可是richedit控件却没有,所以我给他添加了这几个状态图。


        1、 首先还是成员函数和成员变量:

    	LPCTSTR GetNormalImage();
    	void SetNormalImage(LPCTSTR pStrImage);
    	LPCTSTR GetHotImage();
    	void SetHotImage(LPCTSTR pStrImage);
    	LPCTSTR GetFocusedImage();
    	void SetFocusedImage(LPCTSTR pStrImage);
    	LPCTSTR GetDisabledImage();
    	void SetDisabledImage(LPCTSTR pStrImage);
    	void PaintStatusImage(HDC hDC);

    	CDuiString m_sNormalImage;
    	CDuiString m_sHotImage;
    	CDuiString m_sFocusedImage;
    	CDuiString m_sDisabledImage;
          

          然后相应的函数定义为:

    LPCTSTR CRichEditUI::GetNormalImage()
    {
    	return m_sNormalImage;
    }
    
    void CRichEditUI::SetNormalImage(LPCTSTR pStrImage)
    {
    	m_sNormalImage = pStrImage;
    	Invalidate();
    }
    
    LPCTSTR CRichEditUI::GetHotImage()
    {
    	return m_sHotImage;
    }
    
    void CRichEditUI::SetHotImage(LPCTSTR pStrImage)
    {
    	m_sHotImage = pStrImage;
    	Invalidate();
    }
    
    LPCTSTR CRichEditUI::GetFocusedImage()
    {
    	return m_sFocusedImage;
    }
    
    void CRichEditUI::SetFocusedImage(LPCTSTR pStrImage)
    {
    	m_sFocusedImage = pStrImage;
    	Invalidate();
    }
    
    LPCTSTR CRichEditUI::GetDisabledImage()
    {
    	return m_sDisabledImage;
    }
    
    void CRichEditUI::SetDisabledImage(LPCTSTR pStrImage)
    {
    	m_sDisabledImage = pStrImage;
    	Invalidate();
    }
    
    RECT CRichEditUI::GetTextPadding() const
    {
    	return m_rcTextPadding;
    }
    
    void CRichEditUI::SetTextPadding(RECT rc)
    {
    	m_rcTextPadding = rc;
    	Invalidate();
    }
    
    void CRichEditUI::PaintStatusImage(HDC hDC)
    {
    	if( IsFocused() ) m_uButtonState |= UISTATE_FOCUSED;
    	else m_uButtonState &= ~ UISTATE_FOCUSED;
    	if( !IsEnabled() ) m_uButtonState |= UISTATE_DISABLED;
    	else m_uButtonState &= ~ UISTATE_DISABLED;
    
    	if( (m_uButtonState & UISTATE_DISABLED) != 0 ) {
    		if( !m_sDisabledImage.IsEmpty() ) {
    			if( !DrawImage(hDC, (LPCTSTR)m_sDisabledImage) ) m_sDisabledImage.Empty();
    			else return;
    		}
    	}
    	else if( (m_uButtonState & UISTATE_FOCUSED) != 0 ) {
    		if( !m_sFocusedImage.IsEmpty() ) {
    			if( !DrawImage(hDC, (LPCTSTR)m_sFocusedImage) ) m_sFocusedImage.Empty();
    			else return;
    		}
    	}
    	else if( (m_uButtonState & UISTATE_HOT ) != 0 ) {
    		if( !m_sHotImage.IsEmpty() ) {
    			if( !DrawImage(hDC, (LPCTSTR)m_sHotImage) ) m_sHotImage.Empty();
    			else return;
    		}
    	}
    
    	if( !m_sNormalImage.IsEmpty() ) {
    		if( !DrawImage(hDC, (LPCTSTR)m_sNormalImage) ) m_sNormalImage.Empty();
    		else return;
    	}
    }

           2、相同也须要在SetAttribute中添加例如以下代码:

    	else if( _tcscmp(pstrName, _T("normalimage")) == 0 ) SetNormalImage(pstrValue);
    	else if( _tcscmp(pstrName, _T("hotimage")) == 0 ) SetHotImage(pstrValue);
    	else if( _tcscmp(pstrName, _T("focusedimage")) == 0 ) SetFocusedImage(pstrValue);
    	else if( _tcscmp(pstrName, _T("disabledimage")) == 0 ) SetDisabledImage(pstrValue);

            3、另外还须要添加一个成员变量来记录控件是否处在hot状态下

            UINT m_uButtonState;

           然后在DoEvent函数中增加例如以下代码来改变成员变量

    	else if( event.Type == UIEVENT_MOUSEMOVE ) 
        <span style="white-space:pre">	</span>{
    <span style="white-space:pre">		</span>if( IsEnabled() ) {
    <span style="white-space:pre">			</span>m_uButtonState |= UISTATE_HOT;
    <span style="white-space:pre">			</span>Invalidate();
    <span style="white-space:pre">		</span>}
        <span style="white-space:pre">		</span>return;
        <span style="white-space:pre">	</span>}
        <span style="white-space:pre">	</span>else if( event.Type == UIEVENT_BUTTONUP ) 
        <span style="white-space:pre">	</span>{
    
    
          <span style="white-space:pre">		</span>return;
            }
    <span style="white-space:pre">	</span>else if( event.Type == UIEVENT_MOUSEENTER )
    <span style="white-space:pre">	</span>{
    <span style="white-space:pre">		</span>if( IsEnabled() ) {
    <span style="white-space:pre">			</span>m_uButtonState |= UISTATE_HOT;
    <span style="white-space:pre">			</span>Invalidate();
    <span style="white-space:pre">		</span>}
    <span style="white-space:pre">		</span>return;
    <span style="white-space:pre">	</span>}
    <span style="white-space:pre">	</span>else if( event.Type == UIEVENT_MOUSELEAVE )
    <span style="white-space:pre">	</span>{
    <span style="white-space:pre">		</span>if( IsEnabled() ) {
    <span style="white-space:pre">			</span>m_uButtonState &= ~UISTATE_HOT;
    <span style="white-space:pre">			</span>Invalidate();
    <span style="white-space:pre">		</span>}
    <span style="white-space:pre">		</span>return;
    <span style="white-space:pre">	</span>}

    总结:

          上面的全部代码的改进。我都已经在我自己的库里面改好了,我自己的库下载地址为:点击打开链接

          假设有错误或者不妥,请联系我。


    Redrain 2014.11.17


    QQ:491646717

    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    机器学习之线性回归
    最长回文字串——manacher算法
    linux系统下pdf操作软件pdftk
    markdown表格
    3.9 标准化,让运营数据落入相同的范围
    3.numpy_array数组
    4. 归并排序和快速排序
    3.病毒分裂
    2. 大整数乘法
    1.单峰序列
  • 原文地址:https://www.cnblogs.com/yxwkf/p/4718625.html
Copyright © 2011-2022 走看看