script GUIs: fixed text clipping and scrolling (#1605, related to #1612)

Previously, text windows would just change their own size and leave cropping and scrolling to their parent. This made the code easier, but was apparently unintuitive for scripters.
Now text windows do not change their size but show a scrollbar themselves (unless GUI_FitChildren or GUI_NoCrop of course).

This implied some other changes, because now parents without a scroll bar need to clip, too. (Or the clipping needs to be moved to the child window. But then it would have to be made sure that menu decoration can still go out of the bounds.)
And this also needed some script fixes where scripters assumed the text windows would not scroll (and thus made them smaller than 1em).

related to https://git.openclonk.org/openclonk.git/commit/46ad28ea652fad34814a866f3b9c305aa7cc6faa
objectmenu
David Dormagen 2016-01-18 21:39:54 +01:00
parent 4b98e71271
commit cd57e47493
4 changed files with 42 additions and 37 deletions

View File

@ -220,8 +220,9 @@ private func Construction()
text =
{
Target = this,
Top = "-0.45em",
Style = GUI_TextHCenter,
Top = "-0.5em",
Bottom = "0.5em",
Style = GUI_TextHCenter | GUI_TextVCenter,
Text = "",
Priority = 9
}
@ -231,7 +232,6 @@ private func Construction()
Target = this,
Player = NO_OWNER,
ID = crew_next_id + 3,
Style = GUI_NoCrop,
Top = "100%+0.6em",
Bottom = "100%+0.85em",
BackgroundColor = RGB(40, 40, 40),
@ -826,8 +826,9 @@ private func AddCrewBar(int foreground, int background)
text =
{
Target = this,
Top = "-0.45em",
Style = GUI_TextHCenter,
Top = "-0.5em",
Bottom = "0.5em",
Style = GUI_TextHCenter | GUI_TextVCenter,
Text = "",
Priority = 5
}

View File

@ -750,7 +750,7 @@ func CreateMainMenu(object obj, int slot)
{
Priority = 1,
Style = GUI_TextVCenter | GUI_TextHCenter,
Bottom = "+0.75em",
Bottom = "+1em",
Text = menu.title,
BackgroundColor = 0xa0000000,
//Decoration = menu.decoration

View File

@ -147,13 +147,9 @@ private func InitializeMenu()
{
Left = Format("0%%%s", ToEmString(10 * menu_height + text_margin)),
Right = Format("100%%%s", ToEmString(- 5 * menu_height - text_margin)),
// Wrap the text again to scroll only one window instead of also scrolling e.g. the portrait.
text =
{
Target = this,
ID = 2,
Text = nil,
}
Target = this,
ID = 2,
Text = nil,
};
prop_next =
{
@ -222,8 +218,8 @@ private func ShowGuideMenu(int index)
private func UpdateGuideMenu(string guide_message, bool has_next, bool has_prev, bool has_close)
{
// Update the text message entry.
prop_menu.text.text.Text = guide_message;
GuiUpdateText(guide_message, id_menu, prop_menu.text.text.ID, this);
prop_menu.text.Text = guide_message;
GuiUpdateText(guide_message, id_menu, prop_menu.text.ID, this);
// Update the next/close button.
if (has_next || has_close)

View File

@ -1694,13 +1694,22 @@ bool C4ScriptGuiWindow::UpdateLayout(C4TargetFacet &cgo, float parentWidth, floa
// If this window contains text, we auto-fit to the text height;
// but we only break text when the window /would/ crop it otherwise.
StdCopyStrBuf *strBuf = props[C4ScriptGuiWindowPropertyName::text].GetStrBuf();
int minRequiredTextHeight = 0;
if (strBuf && !(style & C4ScriptGuiWindowStyleFlag::NoCrop))
{
StdStrBuf sText;
int32_t textHgt = ::GraphicsResource.FontRegular.BreakMessage(strBuf->getData(), rcBounds.Wdt, &sText, true);
const int32_t rawTextHeight = ::GraphicsResource.FontRegular.BreakMessage(strBuf->getData(), rcBounds.Wdt, &sText, true);
// enable auto scroll
if (textHgt > rcBounds.Hgt)
rcBounds.Hgt = textHgt;
if (rawTextHeight - 1 > rcBounds.Hgt)
{
// If we need to scroll, we will also have to add a scrollbar that takes up some width.
// Recalculate the actual height, taking into account the scrollbar.
// This is not done in the calculation earlier, because then e.g. a 2x1em field could not contain two letters
// but would immediately add a linebreak.
// In the case that this window auto-resizes (FitChildren), the small additional margin to the bottom should not matter much.
const int32_t actualTextHeight = ::GraphicsResource.FontRegular.BreakMessage(strBuf->getData(), rcBounds.Wdt - pScrollBar->rcBounds.Wdt, &sText, true);
minRequiredTextHeight = actualTextHeight;
}
}
UpdateChildLayout(cgo, width, height);
@ -1716,7 +1725,7 @@ bool C4ScriptGuiWindow::UpdateLayout(C4TargetFacet &cgo, float parentWidth, floa
// check if we need a scroll-bar
int32_t topMostChild = 0;
int32_t bottomMostChild = 0;
int32_t bottomMostChild = minRequiredTextHeight;
for (Element * element : *this)
{
C4ScriptGuiWindow *child = static_cast<C4ScriptGuiWindow*>(element);
@ -1843,12 +1852,18 @@ bool C4ScriptGuiWindow::Draw(C4TargetFacet &cgo, int32_t player, C4Rect *current
{
StdStrBuf sText;
int alignment = ALeft;
// If we are showing a scrollbar, we need to leave a bit of space for it so that it doesn't overlap the text.
const int scrollbarXOffset = pScrollBar->IsVisible() ? pScrollBar->rcBounds.Wdt : 0;
const int scrollbarScroll = pScrollBar->IsVisible() ? this->GetScrollY() : 0;
const int actualDrawingWidth = outDrawWdt - scrollbarXOffset;
// If we are set to NoCrop, the message will be split by string-defined line breaks only.
int allowedTextWidth = outDrawWdt;
int allowedTextWidth = actualDrawingWidth;
if (style & C4ScriptGuiWindowStyleFlag::NoCrop)
allowedTextWidth = std::numeric_limits<int>::max();
int32_t textHgt = ::GraphicsResource.FontRegular.BreakMessage(strBuf->getData(), allowedTextWidth, &sText, true);
float textYOffset = 0.0f, textXOffset = 0.0f;
float textYOffset = static_cast<float>(-scrollbarScroll), textXOffset = 0.0f;
if (style & C4ScriptGuiWindowStyleFlag::TextVCenter)
textYOffset = float(outDrawHgt) / 2.0f - float(textHgt) / 2.0f;
else if (style & C4ScriptGuiWindowStyleFlag::TextBottom)
@ -1857,7 +1872,7 @@ bool C4ScriptGuiWindow::Draw(C4TargetFacet &cgo, int32_t player, C4Rect *current
{
int wdt, hgt;
::GraphicsResource.FontRegular.GetTextExtent(sText.getData(), wdt, hgt, true);
textXOffset = float(outDrawWdt) / 2.0f;
textXOffset = float(actualDrawingWidth) / 2.0f;
alignment = ACenter;
}
else if (style & C4ScriptGuiWindowStyleFlag::TextRight)
@ -1865,7 +1880,7 @@ bool C4ScriptGuiWindow::Draw(C4TargetFacet &cgo, int32_t player, C4Rect *current
alignment = ARight;
int wdt, hgt;
::GraphicsResource.FontRegular.GetTextExtent(sText.getData(), wdt, hgt, true);
textXOffset = float(outDrawWdt);
textXOffset = float(actualDrawingWidth);
}
pDraw->TextOut(sText.getData(), ::GraphicsResource.FontRegular, 1.0, cgo.Surface, outDrawX + textXOffset, outDrawY + textYOffset, 0xffffffff, alignment);
}
@ -1896,20 +1911,13 @@ bool C4ScriptGuiWindow::GetClippingRect(int32_t &left, int32_t &top, int32_t &ri
if (IsRoot() || (style & C4ScriptGuiWindowStyleFlag::NoCrop))
return false;
if (pScrollBar->IsVisible())
{
left = rcBounds.x;
top = rcBounds.y;
right = rcBounds.Wdt + left;
bottom = rcBounds.Hgt + top;
return true;
}
/*const int32_t &style = props[C4ScriptGuiWindowPropertyName::style].GetInt();
if (!isMainWindow && !(style & C4ScriptGuiWindowStyleFlag::NoCrop))
return static_cast<C4ScriptGuiWindow*>(GetParent())->GetClippingRect(left, top, right, bottom);
*/
return false;
// Other windows always clip their children.
// This implicitly clips childrens' text to the parent size, too.
left = rcBounds.x;
top = rcBounds.y;
right = rcBounds.Wdt + left;
bottom = rcBounds.Hgt + top;
return true;
}
void C4ScriptGuiWindow::SetTag(C4String *tag)