0

我正在为 GUI 编写代码(在 C++ 中),现在我关心的是行中文本的组织。我遇到的一个问题是代码变得非常长且令人困惑,并且我开始进入 ^2 场景,其中对于我为文本演示添加的每个选项,我必须使用的函数数量写是它的平方。在试图解决这个问题时,出现了一个特定的设计选择,我不知道更好的方法,也不知道它们之间的优缺点有多大:

我有两种在流程中非常相似的方法,即遍历相同的对象,考虑到相同的约束,但最终在此流程之间执行不同的操作。对于任何人的兴趣,这些方法都会渲染文本,并确定是否有任何文本由于将文本分别环绕在其他对象周围或仅是行尾而溢出行。

这些函数需要为左、右或居中的文本复制和重写,它们有不同的流向,所以我做出的任何设计选择都会重复三遍。

基本上,我可以继续我现在所拥有的,这是处理这些不同操作的两种不同方法,或者我可以将它们合并到一个函数中,其中包含 if 语句以确定是否呈现文本或找出是否有文本溢出。

有没有普遍接受的正确方法来解决这个问题?否则,所涉及的权衡是什么,有哪些迹象表明应该使用一种方式而不是另一种方式?还有其他方法可以做我错过的事情吗?

我已经对此进行了几次编辑,以尝试使其更易于理解,但如果不是,请问我一些问题,以便我进行编辑和解释。我也可以贴出这两种不同方法的源代码,但是它们使用了很多函数和对象,解释起来需要很长时间。

// 编辑:源代码 //

功能一:

无效 GUITextLine::renderLeftShifted(const GUIRenderInfo& renderInfo) { if(m_renderLines.empty()) 返回;

Uint iL = 0;

Array2t<float> renderCoords;
renderCoords.s_x = renderInfo.s_offset.s_x + m_renderLines[0].s_x;
renderCoords.s_y = renderInfo.s_offset.s_y + m_y;
float remainingPixelsInLine = m_renderLines[0].s_y;

for (Uint iTO= 0;iTO != m_text.size();++iTO)
{
    if(m_text[iTO].s_pixelWidth <= remainingPixelsInLine)
    {
        string preview = m_text[iTO].s_string;

        m_text[iTO].render(&renderCoords);
        remainingPixelsInLine -= m_text[iTO].s_pixelWidth;
    }

    else
    {
        FSInternalGlyphData intData = m_text[iTO].stealFSFastFontInternalData();

        float characterWidth = 0;

        Uint iFirstCharacterOfRenderLine = 0;

        for(Uint iC = 0;;++iC)
        {
            if(iC == m_text[iTO].s_string.size()) 
            {
                // wrap up
                string renderPart = m_text[iTO].s_string;
                renderPart.erase(iC, renderPart.size());
                renderPart.erase(0, iFirstCharacterOfRenderLine);
                m_text[iTO].s_font->renderString(renderPart.c_str(), intData,
                    &renderCoords);
                break;
            }

            characterWidth += m_text[iTO].s_font->getWidthOfGlyph(intData,
                m_text[iTO].s_string[iC]);

            if(characterWidth > remainingPixelsInLine) 
            {
                // Can't push in the last character
                // No more space in this line

                // First though, render what we already have:

                string renderPart = m_text[iTO].s_string;
                renderPart.erase(iC, renderPart.size());
                renderPart.erase(0, iFirstCharacterOfRenderLine);
                m_text[iTO].s_font->renderString(renderPart.c_str(), intData,
                    &renderCoords);


                if(++iL != m_renderLines.size())
                {
                    remainingPixelsInLine = m_renderLines[iL].s_y;
                    renderCoords.s_x = renderInfo.s_offset.s_x + m_renderLines[iL].s_x;

                    // Cool, so now try rendering this character again
                    --iC;
                    iFirstCharacterOfRenderLine = iC;
                    characterWidth = 0;
                }
                else
                {

                    // Quit

                    break;
                }
            }
        }
    }
}

// Done! }

功能二:

矢量 GUITextLine::recalculateWrappingContraints_LeftShift() { m_pixelsOfCharacters = 0;

float pixelsRemaining = m_renderLines[0].s_y;

Uint iRL = 0;

// Go through every text object, fiting them into render lines
for(Uint iTO = 0;iTO != m_text.size();++iTO)
{
    // If an entire text object fits in a single line
    if(pixelsRemaining >= m_text[iTO].s_pixelWidth)
    {
        pixelsRemaining -= m_text[iTO].s_pixelWidth;
        m_pixelsOfCharacters += m_text[iTO].s_pixelWidth;
    }

    // Otherwise, character by character
    else
    {
        // Get some data now we don't get it every function call
        FSInternalGlyphData intData = m_text[iTO].stealFSFastFontInternalData();

        for(Uint iC = 0; iC != m_text[iTO].s_string.size();++iC)
        {
            float characterWidth = m_text[iTO].s_font->getWidthOfGlyph(intData, '-');

            if(characterWidth < pixelsRemaining)
            {
                pixelsRemaining -= characterWidth;
                m_pixelsOfCharacters += characterWidth;
            }

            else // End of render line! 
            {
                m_pixelsOfWrapperCharacters += pixelsRemaining; // we might track how much wrapping px we use

                // If this is true, then we ran out of render lines before we ran out of text. Means we have some overflow to return
                if(++iRL == m_renderLines.size())
                {
                    return harvestOverflowFrom(iTO, iC);
                }

                else
                {
                    pixelsRemaining = m_renderLines[iRL].s_y;
                }
            }
        }

    }
}

vector<GUIText> emptyOverflow;
return emptyOverflow; }

所以基本上,render() 将 renderCoordinates 作为参数并从中获取它需要渲染的全局位置。calcWrappingConstraints 计算出对象中有多少文本超出了分配的空间,并将该文本作为函数返回。

m_renderLines 是两个浮点结构的 std::vector,其中 .s_x = 可以开始渲染的位置和 .s_y = 渲染空间的大小 - 不是,它本质上是“renderLine”的宽度,而不是它的结束位置。

m_text 是 GUIText 对象的 std::vector,其中包含文本字符串和一些数据,如样式、颜色、大小等。它还包含在 s_font 下,对字体对象的引用,该对象执行渲染,计算字形的宽度等。

希望这可以解决问题。

4

2 回答 2

1

访问者模式的实现怎么样?听起来这可能是你所追求的那种东西。

于 2011-12-02T15:04:19.173 回答
1

在这种情况下,没有普遍接受的方法。但是,任何编程场景中的常见做法是删除重复的代码。我认为当方向改变结果太多而无法进行这种划分时,您会陷入如何按方向划分代码的问题。在这些情况下,重点关注三种算法的共同部分,并将它们划分为任务。

当我为 MFC 复制 WinForms 流布局控件时,我做了类似的事情。我处理了两种类型的对象:固定位置(您的图片等)和自动位置(您的文字)。

在您提供的示例中,我可以列出您示例的常见部分。

写线(方向)

  • bool TestPlaceWord (direction) // 如果不能将单词放在前一个单词旁边,则返回 false
  • bool WrapPastObject (direction) // 如果超出范围则返回 false
  • bool WrapLine (direction) // 如果新行空间不足,则返回 false。

无论您面对什么方向,这些都将执行。

最终,每个方向的算法都太不同了,不能再简化了。

于 2011-12-02T15:21:32.167 回答