DuiDesigner控件Label无法设置文本对齐属性的BUG解决方案

1、修复duilib资源编辑器的bug:label、按钮控件无法设置除了center之外的文本对齐方式
2、修复duilb文本显示对齐方式的逻辑错误代码,DT_LEFT和DT_RIGHT必须配合属性DT_SINGLELINE使用
3、另外添加了左对齐和右对齐文本显示属性默认纵向居中的逻辑,便于编排控件

首先解决文字绘制不支持左对齐的BUG,因为文字属于绘制逻辑实现的,所以我们先从视图的绘制开始找起。
void CUIDesignerView::OnDraw(CDC* pDrawDC)
找到这一行:
m_LayoutManager.Draw(&hCloneDC);
这个就是布局的绘制逻辑:
void CLayoutManager::Draw(CDC* pDC) { CSize szForm = GetForm()->GetInitSize(); CRect rcPaint(0, 0, szForm.cx, szForm.cy); CControlUI* pForm = m_Manager.GetRoot(); pForm->DoPaint(pDC->GetSafeHdc(), rcPaint); CContainerUI* pContainer = static_cast<CContainerUI*>(pForm->GetInterface(_T("Container"))); ASSERT(pContainer); DrawAuxBorder(pDC, pContainer->GetItemAt(0)); DrawGrid(pDC, rcPaint); }
再继续看:
void CControlUI::DoPaint(HDC hDC, const RECT& rcPaint)
{
    if( !::IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem) ) return;

    // 绘制循序:背景颜色->背景图->状态图->文本->边框if( m_cxyBorderRound.cx > 0 || m_cxyBorderRound.cy > 0 ) {
        CRenderClip roundClip;
        CRenderClip::GenerateRoundClip(hDC, m_rcPaint,  m_rcItem, m_cxyBorderRound.cx, m_cxyBorderRound.cy, roundClip);
        PaintBkColor(hDC);
        PaintBkImage(hDC);
        PaintStatusImage(hDC);
        PaintText(hDC);
        PaintBorder(hDC);
    }
    else {
        PaintBkColor(hDC);
        PaintBkImage(hDC);
        PaintStatusImage(hDC);
        PaintText(hDC);
        PaintBorder(hDC);
    }
}
再跟进函数PaintText:
void CControlUI::PaintText(HDC hDC)
{
    return;
}
是一个空函数,一定是基类的虚函数,具体绘制实现在派生类对应的函数中,这里是为了解决按钮和label控件的文字绘制对齐问题,所以这里直接查看CLabelUI(CButtonUI继承自CLabelUI)的PaintText函数:
void CLabelUI::PaintText(HDC hDC)
    {
        if( m_dwTextColor == 0 ) m_dwTextColor = m_pManager->GetDefaultFontColor();
        if( m_dwDisabledTextColor == 0 ) m_dwDisabledTextColor = m_pManager->GetDefaultDisabledColor();

        RECT rc = m_rcItem;
        rc.left += m_rcTextPadding.left;
        rc.right -= m_rcTextPadding.right;
        rc.top += m_rcTextPadding.top;
        rc.bottom -= m_rcTextPadding.bottom;

        if(!GetEnabledEffect())
        {
            if( m_sText.IsEmpty() ) return;
            int nLinks = 0;
            if( IsEnabled() ) {
                if( m_bShowHtml )
                    CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, m_dwTextColor, \
                    NULL, NULL, nLinks, DT_SINGLELINE | m_uTextStyle);
                elseCRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, m_dwTextColor, \
                    m_iFont, DT_SINGLELINE |m_uTextStyle);
            }
            else {
                if( m_bShowHtml )
                    CRenderEngine::DrawHtmlText(hDC, m_pManager, rc, m_sText, m_dwDisabledTextColor, \
                    NULL, NULL, nLinks, DT_SINGLELINE | m_uTextStyle);
                elseCRenderEngine::DrawText(hDC, m_pManager, rc, m_sText, m_dwDisabledTextColor, \
                    m_iFont, DT_SINGLELINE |m_uTextStyle);
            }
        }
        else
        {
            ……
        }
}        
CRenderEngine::DrawText最后是调用API函数DrawText绘制的文本:
void CRenderEngine::DrawText(HDC hDC, CPaintManagerUI* pManager, RECT& rc, LPCTSTR pstrText, DWORD dwTextColor, int iFont, UINT uStyle)
{
    ASSERT(::GetObjectType(hDC)==OBJ_DC || ::GetObjectType(hDC)==OBJ_MEMDC);
    if( pstrText == NULL || pManager == NULL ) return;
    ::SetBkMode(hDC, TRANSPARENT);
    ::SetTextColor(hDC, RGB(GetBValue(dwTextColor), GetGValue(dwTextColor), GetRValue(dwTextColor)));
    HFONT hOldFont = (HFONT)::SelectObject(hDC, pManager->GetFont(iFont));
    ::DrawText(hDC, pstrText, -1, &rc, uStyle | DT_NOPREFIX);
    ::SelectObject(hDC, hOldFont);
}
文字对齐属性主要取决于m_uTextStyle,在CLabelUI的源码中搜索m_uTextStyle的引用,找到SetAttribute函数:
void CLabelUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
    {
        if( _tcscmp(pstrName, _T("align")) == 0 ) {
if( _tcsstr(pstrValue, _T("left")) != NULL ) { m_uTextStyle &= ~(DT_CENTER | DT_RIGHT | DT_VCENTER | DT_SINGLELINE); m_uTextStyle |= DT_LEFT; }if( _tcsstr(pstrValue, _T("center")) != NULL ) {
                m_uTextStyle &= ~(DT_LEFT | DT_RIGHT );
                m_uTextStyle |= DT_CENTER;
            }
 if( _tcsstr(pstrValue, _T("right")) != NULL ) { m_uTextStyle &= ~(DT_LEFT | DT_CENTER | DT_VCENTER | DT_SINGLELINE); m_uTextStyle |= DT_RIGHT; }if( _tcsstr(pstrValue, _T("top")) != NULL ) {
                m_uTextStyle &= ~(DT_BOTTOM | DT_VCENTER | DT_VCENTER);
                m_uTextStyle |= (DT_TOP | DT_SINGLELINE);
            }
            if( _tcsstr(pstrValue, _T("vcenter")) != NULL ) {
                m_uTextStyle &= ~(DT_TOP | DT_BOTTOM );            
                m_uTextStyle |= (DT_CENTER | DT_VCENTER | DT_SINGLELINE);
            }
            if( _tcsstr(pstrValue, _T("bottom")) != NULL ) {
                m_uTextStyle &= ~(DT_TOP | DT_VCENTER | DT_VCENTER);
                m_uTextStyle |= (DT_BOTTOM | DT_SINGLELINE);
            }
        }
属性的设置是又外部属性编辑器中发生改变时调用的,注意看,当设置的是文本对齐属性(align)且对齐方式为left/right时,其实是没有必要去掉DT_SINGLELINE属性的(虽然CLabelUI::PaintText在调用CRenderEngine::DrawText时有设置回去),另外左对齐或右对齐时我们希望能在纵向上居中,因此代码修改如下:
void CLabelUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
    {
        if( _tcscmp(pstrName, _T("align")) == 0 ) {
            if( _tcsstr(pstrValue, _T("left")) != NULL ) {
                m_uTextStyle &= ~(DT_CENTER | DT_RIGHT/* | DT_VCENTER | DT_SINGLELINE*/); m_uTextStyle |= DT_LEFT | DT_VCENTER | DT_SINGLELINE;
            }
            if( _tcsstr(pstrValue, _T("center")) != NULL ) {
                m_uTextStyle &= ~(DT_LEFT | DT_RIGHT );
                m_uTextStyle |= DT_CENTER;
            }
            if( _tcsstr(pstrValue, _T("right")) != NULL ) {
                m_uTextStyle &= ~(DT_LEFT | DT_CENTER/* | DT_VCENTER | DT_SINGLELINE*/); m_uTextStyle |= DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
            }
            if( _tcsstr(pstrValue, _T("top")) != NULL ) {
                m_uTextStyle &= ~(DT_BOTTOM | DT_VCENTER | DT_VCENTER);
                m_uTextStyle |= (DT_TOP | DT_SINGLELINE);
            }
            if( _tcsstr(pstrValue, _T("vcenter")) != NULL ) {
                m_uTextStyle &= ~(DT_TOP | DT_BOTTOM );            
                m_uTextStyle |= (DT_CENTER | DT_VCENTER | DT_SINGLELINE);
            }
            if( _tcsstr(pstrValue, _T("bottom")) != NULL ) {
                m_uTextStyle &= ~(DT_TOP | DT_VCENTER | DT_VCENTER);
                m_uTextStyle |= (DT_BOTTOM | DT_SINGLELINE);
            }
        }
也即都增加了属性:DT_VCENTER|DT_SINGLELINE。

现在再来解决问题1,属性的显示是在CUIProperties::ShowLabelProperty中实现的:
void CUIProperties::ShowLabelProperty(CControlUI* pControl)
{
 ShowControlProperty(pControl);

 ASSERT(pControl);
 CLabelUI* pLabel=static_cast<CLabelUI*>(pControl->GetInterface(_T("Label")));
 ASSERT(pLabel);

 CMFCPropertyGridProperty* pPropLabel=m_wndPropList.FindItemByData(classLabel,FALSE);
 ASSERT(pPropLabel);

 //align
 UINT uStyle=pLabel->GetTextStyle();
 CString strStyle;
 if(uStyle&DT_CENTER)
  strStyle=_T("Center");
 elseif(uStyle&DT_LEFT)
  strStyle=_T("Left");
 elseif(uStyle&DT_RIGHT)
  strStyle=_T("Right");
 elseif(uStyle&DT_TOP)
  strStyle=_T("Top");
 elseif(uStyle&DT_BOTTOM)
  strStyle=_T("Bottom");
注意看处理左对齐的代码逻辑,由于DT_LEFT被定义为0x00000000,因此uStyle&DT_LEFT恒不为真,这也就是为什么设置不了文字左对齐的原因。正确的逻辑如下:
void CUIProperties::ShowLabelProperty(CControlUI* pControl)
{
 ShowControlProperty(pControl);

 ASSERT(pControl);
 CLabelUI* pLabel=static_cast<CLabelUI*>(pControl->GetInterface(_T("Label")));
 ASSERT(pLabel);

 CMFCPropertyGridProperty* pPropLabel=m_wndPropList.FindItemByData(classLabel,FALSE);
 ASSERT(pPropLabel);

 //align
 UINT uStyle=pLabel->GetTextStyle();
 CString strStyle;
 if(uStyle&DT_CENTER)
  strStyle=_T("Center");
 elseif((~uStyle) & (~DT_LEFT))
  strStyle=_T("Left");
 elseif(uStyle&DT_RIGHT)
  strStyle=_T("Right");
 elseif(uStyle&DT_TOP)
  strStyle=_T("Top");
 elseif(uStyle&DT_BOTTOM)
  strStyle=_T("Bottom");
至此,在资源编辑器中便可以设置文本对齐属性了,但是修改完成后保存发现属性并未保存成功,继续排查。
布局文件最终保存为xml文件,因此肯定会使用到xml的操作,而工程中使用了tinyxml,因此通过搜索tinyxml文档类的引用最终找到保存布局文件的函数:CLayoutManager::SaveSkinFile,它会在最后调用:
SaveProperties(pForm->GetItemAt(0), pNode->ToElement());
我们看CLayoutManager::SaveProperties:
void CLayoutManager::SaveProperties(CControlUI* pControl, TiXmlElement* pParentNode
……
  case classLabel:
  case classText:
  SaveLabelProperty(pControl, pNode);
  break;
……
继续查看CLayoutManager::SaveLabelProperty,向下找到代码:
if(uTextStyle & DT_LEFT)
tstrAlgin = _T("left");
仍然是犯了相同的错误,代码应该修改为:
if((~uTextStyle) & (~DT_LEFT))
tstrAlgin = _T("left");

完成以上代码修改后,重新编译并运行DuiDesigner即可。


版权声明:本文为博主原创文章,未经博主允许不得转载。

你可能感兴趣的:(DuiDesigner控件Label无法设置文本对齐属性的BUG解决方案)