我正在编写GUI的代码(用C ++编写),现在我关注的是文本的组织。我遇到的一个问题是代码变得非常冗长和混乱,我开始进入^ 2场景,我为文本演示添加了每个选项,我必须使用的函数数量写是正方形。在试图解决这个问题时,出现了一个特定的设计选择,我不知道更好的方法,或者它们之间的优缺点程度:
我有两种流程非常相似的方法,即迭代相同的对象,考虑相同的约束,但最终在此流程之间执行不同的操作。对于任何感兴趣的人,这些方法会渲染文本,并确定是否有任何文本溢出该行,因为将文本分别包裹在其他对象周围或仅仅是行的末尾。
这些函数需要复制和重写为左,右或居中文本,这些函数具有不同的流程,因此我所做的任何设计选择都会重复三次。
基本上,我可以继续我现在拥有的东西,这是处理这些不同动作的两个独立方法,或者我可以将它们合并到一个函数中,其中有if语句来确定是否渲染文本或图形如果有任何文字溢出。
是否有普遍接受的正确方法来解决这个问题?否则,有什么权衡取舍,哪些迹象可能表明一种方式应该用于另一种方式?还有其他一些我错过的事情吗?
我已经编辑过几次尝试让它更容易理解,但如果不是,请问我一些问题,以便我可以编辑和解释。我也可以发布两种不同方法的源代码,但它们使用了很多函数和对象,需要很长时间才能解释。
//编辑:源代码//
功能1:
void GUITextLine :: renderLeftShifted(const GUIRenderInfo& renderInfo){ 如果(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! }
功能2:
vector 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,一个对字体对象的引用,它执行渲染,计算字形的宽度等。
希望这可以解决问题。
答案 0 :(得分:1)
Visitor Pattern的实施怎么样?听起来这可能是你追求的事情。
答案 1 :(得分:1)
在这种情况下,没有普遍接受的方式。 但是,任何编程方案中的常见做法是删除重复的代码。 我认为你在如何按方向划分代码时会遇到困难,当方向改变结果太多而不能进行这种划分时。在这些情况下,请关注三种算法的常见部分,并将它们划分为任务。
当我为MFC复制WinForms流布局控件时,我做了类似的事情。我处理了两种类型的对象:固定位置(您的图片等)和自动位置(您的单词)。
在您提供的示例中,我可以列出示例的常见部分。
写行(方向)
无论您面对的是什么方向,都会执行这些操作。
最终,每个方向的算法都太不同了,不再简化了。