提高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\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,起到了占位作用。让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

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

时间: 2024-10-15 20:50:07

提高duilib的richedit控制的一些特征的相关文章

洛谷P1084 [NOIP2012提高组Day2T3]疫情控制

P1084 疫情控制 题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从 首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点.但特别要注意的是,首都是不能建立检查点的. 现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队.一支军队可以

duilib中Richedit如何支持TextChanged

在duilib开发中,小伙伴会因为CEditUI的各种问题,而转为使用CRichEditUI来代替.但是CRichEditUI控件却不支持文字变化通知事件,下面通过简单几行代码让RichEdit支持文字变化事件. 在UIRichEdith.cpp文件中搜索::OnTxNotify,然后使用以下代码替换此函数: void CRichEditUI::OnTxNotify(DWORD iNotify, void *pv) { switch(iNotify) { case EN_CHANGE: { Ge

【noip 2012】提高组Day2T3.疫情控制

Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点.但特别要注意的是,首都是不能建立检查点的. 现在,在H国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队.一支军队可以在有道路连接的城市间移动,并在除

云信Duilib的richedit prompttext 设置了无效问题

需要设置class="prompt" 即<Class name="prompt" value="promptmode="true" promptcolor="edit_tip""/> promptmode=“true” promptcolor=“edit_tip” 如: <RichEdit class="simple input" height="30&quo

【NOIP】提高组2012 疫情控制

[题意]n个点的树,1为根,要求删除一些点使得截断根节点和所有叶子结点的路径(不能删根,可以删叶子).有m支军队在m个点上,每时刻所有军队可以走一步,最终走到的地方就是删除的点,求最短时间. [题解] 所有点同时走路,求最短时间,这样的询问通常考虑二分转化为判定性问题.(实际上,这题用二分确实没有想到,如果能想到二分整道题就好写一些了) 容易发现,每支军队贪心地往上走最优. 那么对于二分的时间,有一部分军队可以到达根,A数组记录这些军队到达根后的剩余时间,待会可以走到第二层覆盖其它节点. 有一部

Java_7面向对象的三大特征

一.封装(模块化) 1.问题的引入: 当我们创建一个类的对象后,可以通过对象.属性的方式,对对象进行赋值. 这里,赋值操作受到属性的数据类型和存储范围的制约,除次之外,没有其他制约条件. 但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件,这个条件又不能在属性声明时体现 ,我们只能通过一个public 方法对属性进行限制条件的添加(get(),set())同时,我们需要避免用户再使用"对象.属性"进行赋值,则需要将属性声明为私有化.-->此时,针对属性就体现了封装. 2

IT兄弟连 Java语法教程 流程控制语句 控制循环结构1

Java语言没有提供goto语句来控制程序的跳转,这种做法提高了程序流程控制的可读性,但降低了程序流程控制的灵活性.为了弥补这种不足,Java提供了continue和break来控制循环结构.除此之外,return可以结束整个方法,当然也就结束了一次循环. 使用break结束循环 某些时候需要在某种条件出现时强行终止循环,而不是等到循环条件为false时才退出循环.此时,可以使用break来完成这个功能.break用于完全结束一个循环,跳出循环体.不管是哪种循环,一旦在循环体中遇到break,系

我的数据分析入门

从昨天开始抽空看了<一本书玩转数据分析>,以前虽然我也做过一些数据分析方面的工作,但是没有系统的了解过这方便的知识理论方法.看完这本书,做了简单笔记如下,可以作为了解数据分析的入门吧. 数据对应企业而言:深入了解业务情况:明确当前现状:把控当下,针对调整:预测未来发展趋势 数据对决策而言:有助于客户关系管理:挖掘潜在客户:提高用户黏度:控制企业成本:把控当下成本:减少存货,降低损耗,提高资源利用:管理员工绩效:知晓员工工作状态:了解员工绩效差异 数据的重要性:有助于监督管理,能够客观反映问题,

互联网如何改变一间公司的寿命?

在过去几十年中世界变得越来越复杂,一直以来,公司的使用寿命从60岁大幅下降到30多岁.技术呈指数级增长; 到2025年,预计将有1万亿个传感器连接到互联网,可能影响因素数量是不可估量的. 在这种情况下,我们的组织如何面向未来?虽然高层领导人在某些方面对于员工来说他是最强大的,但中层管理人员对实际行为和团队绩效影响是最大的.中层管理人员与员工有更多的互动,因此对他们的经验,工作满意度和成效影响更大.他们还具有比高级领导更多的知识,了解什么是有效的,而不是日常运作.在一项研究中发现,中层管理人员比蓝