改进duilib的richedit控件的部分功能

简介: 转载请说明原出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/41208207       如果要使用透明异形窗体功能,首先要改进duilib库让他本身支持(可以下载duilib扩展群群主改进的库,或者下载我的库),然后要开启窗体的bktrans属性。

转载请说明原出处,谢谢~~: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

}



        这样子改进后,就可以很简单的做出仿酷狗的搜索栏的效果,也就是richedit内嵌一个按钮。而不需要用到绝对布局来控制按钮,也不需要为按钮的位置自适应文字担心。



         对应的布局代码如下:

<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\title\edit.png" >
	<Control height="1"/>
	<Button name="Btn_Title_Search" width="33" height="27" normalimage="UI\title\search_normal.png" hotimage="UI\title\search_hover.png" pushedimage="UI\title\search_down.png" />
</RichEdit>	

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


改进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登录器的帐号和密码输入框。但是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

目录
相关文章
|
C# 索引 Windows
Winform控件优化之TabControl控件的使用和常用功能
TabControl是一个分页切换(tab)控件,不同的页框内可以呈现不同的内容,将主要介绍调整tab的左右侧显示、设置多行tab、禁用或删除tabpage、隐藏TabControl头部的选项卡等
4985 0
Winform控件优化之TabControl控件的使用和常用功能
|
C# 容器
Winform控件优化之TabControl控件的美化和功能扩展
在基本的TabControl控件使用和功能之上,可以尝试对其进行美化和功能扩展,比如动态删除或添加tab、绘制图标按钮及鼠标hover时的背景变化、Tab从右向左布局的优化处理等。最重要...
2693 0
Winform控件优化之TabControl控件的美化和功能扩展
|
人工智能 搜索推荐 C#
Photoshop和WPF双剑配合,打造炫酷个性的进度条控件
结合Photoshop和WPF,共同创建一个矢量的个性化进度条。
551 0
Photoshop和WPF双剑配合,打造炫酷个性的进度条控件
|
程序员 C语言 索引
Qt编写自定义控件55-手机通讯录
一、前言 前面几篇文章中的控件基本上难度系数接近0,甚至有凑控件数量的嫌疑,这次必须来一个强悍的控件,本控件难度系数在所有控件中排前五,代码量也不少,头文件都550行,实现文件1600行,为什么这么多呢,其实本控件是由好多个子控件组成的,字母高亮背景类、中间字母分隔类、右侧字母导航类、通讯录按钮类、自定义滚动条类,我在写比较复杂的控件的时候,一般都会逐个功能拆分,然后思考是否该功能可以做成独立的类,这样管理起来比较方便,也方便查看代码。
839 0
|
XML 程序员 C语言
Qt编写控件属性设计器2-拖曳控件
一、前言 上一篇文章把插件加载好了,并且把插件中的所有控件都显示到了列表框中,这次要做的就是实现拖曳控件的功能,用户选择一个控件拖曳到画布上,松开,在松开位置处自动实例化该控件,这个需要用到dropEvent和dragEnterEvent事件,重新实现这两个事件,对拖曳的对象进行过滤并调用函数实例化该控件,在实例化该控件的同时实例化控件跟随控件以便拉伸调整大小和位置。
917 0
|
程序员 开发工具 C语言
Qt编写自定义控件42-开关按钮
一、前言 从2010年进入互联网+智能手机时代以来,各种各样的APP大行其道,手机上面的APP有很多流行的元素,开关按钮个人非常喜欢,手机QQ、360卫士、金山毒霸等,都有很多开关控制一些操作,在Qt widgets应用项目上,在项目中应用些类似的开关按钮,估计也会为项目增添不少新鲜感。
1428 0
|
程序员 C语言
Qt编写自定义控件48-面板窗体控件
一、前言 很多时候需要有一个控件,能够替代容器控件,自动容纳多个widget,自适应宽高,然后提供滚动条功能,这就必然需要用到QScrollArea控件,可设置各个子面板的间距等,也在很多系统中用到,比如温湿度设备面板,有几百个温湿度设备,需要一个容器放置,自动产生滚动条,可以设置面板的固定宽高或者自适应拉伸,其实就是放了表格布局+弹簧来设置。
1277 0
|
开发工具 C语言
Qt编写自定义控件40-导航进度条
一、前言 导航进度条控件,其实就是支付宝、京东、淘宝订单页面的进度控件,提示当前第几步,总共有几步,然后当前进度特殊颜色显示,每个进度带有时间文字等信息,本控件特意将三种样式风格都集成进去了,京东订单流程样式/淘宝订单流程样式/支付宝订单流程样式,可以动态切换样式,控件自适应任何分辨率,可以自由调整自身大小以适应分辨率的改变,总步骤以及当前步骤都是自动计算占用区域比例,直接提供接口设置步骤对应的文字信息等,接口非常友好。
1334 0
|
开发工具 C语言
Qt编写自定义控件32-等待进度条控件
一、前言 在各种各样的执行任务界面,有时候需要比较多的时间,需要给出一个直观的等待进度条表示当前正在执行的进度,而不至于懵逼在那里,用户不会觉得程序死了还是干嘛了。等待进度条有好几种办法,比如直接叫美工做好gif图,用QLabel配合QMovie来加载gif图片,这种方法最简单最省事,或者做好多张进度条的图片,采用定时贴图来实现,这些办法省事归省事,就是还不够灵活,写死了,比如有时候需要更换颜色或者换一种展示形式,又需要美工重新做图了,折磨的要死。
1928 0
|
开发工具 C语言
Qt编写自定义控件11-设备防区按钮控件
一、前言 在很多项目应用中,需要根据数据动态生成对象显示在地图上,比如地图标注,同时还需要可拖动对象到指定位置显示,能有多种状态指示,安防领域一般用来表示防区或者设备,可以直接显示防区号,有多种状态颜色指示,例如布防、撤防、旁路、报警、离线、在线等状态,可以作为一个通用的设备按钮对象使用。
837 0