1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-07-03 00:46:55 +02:00

Some refactoring of text display widgets:

- renamed some methods/classes with better names + some consistency (no more print, setTxt and setText that do exactly same thing)
- TextBox now contains label widget instead of inheriting it
- smooth scrolling support (up/down buttons still scroll one line)
This commit is contained in:
Ivan Savenko
2013-08-29 13:46:27 +00:00
parent fb4fb79be2
commit 6bee105d7d
14 changed files with 347 additions and 325 deletions

View File

@ -376,8 +376,8 @@ void CAdventureMapButton::hover (bool on)
else if(GH.statusbar) //for other buttons
{
if (on)
GH.statusbar->print(*name);
else if ( GH.statusbar->getCurrent()==(*name) )
GH.statusbar->setText(*name);
else if ( GH.statusbar->getText()==(*name) )
GH.statusbar->clear();
}
}
@ -725,8 +725,12 @@ CSlider::~CSlider()
}
CSlider::CSlider(int x, int y, int totalw, std::function<void(int)> Moved, int Capacity, int Amount, int Value, bool Horizontal, int style)
:capacity(Capacity),amount(Amount),horizontal(Horizontal), moved(Moved)
CSlider::CSlider(int x, int y, int totalw, std::function<void(int)> Moved, int Capacity, int Amount, int Value, bool Horizontal, int style):
capacity(Capacity),
amount(Amount),
scrollStep(1),
horizontal(Horizontal),
moved(Moved)
{
OBJ_CONSTRUCTION_CAPTURING_ALL;
setAmount(amount);
@ -827,7 +831,7 @@ void CSlider::showAll(SDL_Surface * to)
void CSlider::wheelScrolled(bool down, bool in)
{
moveTo(value + 3 * (down ? +1 : -1));
moveTo(value + 3 * (down ? +scrollStep : -scrollStep));
}
void CSlider::keyPressed(const SDL_KeyboardEvent & key)
@ -839,17 +843,17 @@ void CSlider::keyPressed(const SDL_KeyboardEvent & key)
{
case SDLK_UP:
case SDLK_LEFT:
moveDest = value - 1;
moveDest = value - scrollStep;
break;
case SDLK_DOWN:
case SDLK_RIGHT:
moveDest = value + 1;
moveDest = value + scrollStep;
break;
case SDLK_PAGEUP:
moveDest = value - capacity + 1;
moveDest = value - capacity + scrollStep;
break;
case SDLK_PAGEDOWN:
moveDest = value + capacity - 1;
moveDest = value + capacity - scrollStep;
break;
case SDLK_HOME:
moveDest = 0;
@ -1104,8 +1108,8 @@ CSimpleWindow::~CSimpleWindow()
void CHoverableArea::hover (bool on)
{
if (on)
GH.statusbar->print(hoverText);
else if (GH.statusbar->getCurrent()==hoverText)
GH.statusbar->setText(hoverText);
else if (GH.statusbar->getText()==hoverText)
GH.statusbar->clear();
}
@ -1156,24 +1160,20 @@ void CLabel::showAll(SDL_Surface * to)
{
CIntObject::showAll(to);
std::string toPrint = visibleText();
if(!toPrint.length())
return;
//blitLine(to, pos.topLeft()/2 + pos.bottomRight()/2, toPrint);
blitLine(to, pos.topLeft() + textOffset, toPrint);
if(!text.empty())
blitLine(to, pos, text);
}
CLabel::CLabel(int x, int y, EFonts Font /*= FONT_SMALL*/, EAlignment Align, const SDL_Color &Color /*= Colors::WHITE*/, const std::string &Text /*= ""*/)
:CTextContainer(Align, Font, Color), text(Text)
{
type |= REDRAW_PARENT;
autoRedraw = true;
pos.x += x;
pos.y += y;
pos.w = pos.h = 0;
bg = nullptr;
ignoreLeadingWhitespace = false;
if (alignment == TOPLEFT) // causes issues for MIDDLE
{
@ -1182,15 +1182,17 @@ CLabel::CLabel(int x, int y, EFonts Font /*= FONT_SMALL*/, EAlignment Align, con
}
}
std::string CLabel::visibleText()
Point CLabel::getBorderSize()
{
std::string ret = text;
if(ignoreLeadingWhitespace)
boost::trim_left(ret);
return ret;
return Point(0, 0);
}
void CLabel::setTxt(const std::string &Txt)
std::string CLabel::getText()
{
return text;
}
void CLabel::setText(const std::string &Txt)
{
text = Txt;
if(autoRedraw)
@ -1202,33 +1204,60 @@ void CLabel::setTxt(const std::string &Txt)
}
}
void CBoundedLabel::setBounds(int limitW, int limitH)
CMultiLineLabel::CMultiLineLabel(int x, int y, EFonts Font, EAlignment Align, const SDL_Color &Color, const std::string &Text):
CLabel(x, y, Font, Align, Color, Text),
visibleSize(0, 0, 0, 0)
{
pos.h = limitH;
pos.w = limitW;
recalculateLines(text);
splitText(Text);
}
void CBoundedLabel::setTxt(const std::string &Txt)
void CMultiLineLabel::setVisibleSize(Rect visibleSize)
{
recalculateLines(Txt);
CLabel::setTxt(Txt);
this->visibleSize = visibleSize;
redraw();
}
void CTextContainer::blitLine(SDL_Surface *to, Point where, std::string what)
void CMultiLineLabel::scrollTextBy(int distance)
{
scrollTextTo(visibleSize.y + distance);
}
void CMultiLineLabel::scrollTextTo(int distance)
{
Rect size = visibleSize;
size.y = distance;
setVisibleSize(size);
}
void CMultiLineLabel::setText(const std::string &Txt)
{
splitText(Txt);
CLabel::setText(Txt);
}
void CTextContainer::blitLine(SDL_Surface *to, Rect destRect, std::string what)
{
const IFont * f = graphics->fonts[font];
Point where = destRect.topLeft();
// input is rect in which given text should be placed
// calculate proper position for top-left corner of the text
if (alignment == TOPLEFT)
{
where.x += getBorderSize().x;
where.y += getBorderSize().y;
}
if (alignment == CENTER)
{
where.x -= f->getStringWidth(what) / 2;
where.y -= f->getLineHeight() / 2;
where.x += (destRect.w - f->getStringWidth(what)) / 2;
where.y += (destRect.h - f->getLineHeight()) / 2;
}
if (alignment == BOTTOMRIGHT)
{
where.x -= f->getStringWidth(what);
where.y -= f->getLineHeight();
where.x += getBorderSize().x + destRect.w - f->getStringWidth(what);
where.y += getBorderSize().y + destRect.h - f->getLineHeight();
}
size_t begin = 0;
@ -1245,11 +1274,11 @@ void CTextContainer::blitLine(SDL_Surface *to, Point where, std::string what)
if (currDelimeter % 2) // Enclosed in {} text - set to yellow
f->renderTextLeft(to, toPrint, Colors::YELLOW, where);
else // Non-enclosed text
else // Non-enclosed text, use default color
f->renderTextLeft(to, toPrint, color, where);
begin = end;
where.x += f->getStringWidth(toPrint);
destRect.x += f->getStringWidth(toPrint);
}
currDelimeter++;
}
@ -1262,36 +1291,33 @@ CTextContainer::CTextContainer(EAlignment alignment, EFonts font, SDL_Color colo
color(color)
{}
void CBoundedLabel::showAll(SDL_Surface * to)
void CMultiLineLabel::showAll(SDL_Surface * to)
{
CIntObject::showAll(to);
const IFont * f = graphics->fonts[font];
int lineHeight = f->getLineHeight();
int lineCapacity = pos.h / lineHeight;
int dy = f->getLineHeight(); //line height
int base_y = pos.y;
if(alignment == CENTER)
base_y += std::max((pos.h - maxH)/2,0);
// calculate which lines should be visible
size_t totalLines = lines.size();
size_t beginLine = visibleSize.y / f->getLineHeight();
size_t endLine = (getTextLocation().h + visibleSize.y) / f->getLineHeight() + 1;
for (int i = 0; i < lineCapacity; i++)
// and where they should be displayed
Point lineStart = getTextLocation().topLeft() - visibleSize + Point(0, beginLine * f->getLineHeight());
Point lineSize = Point(getTextLocation().w, f->getLineHeight());
CSDL_Ext::CClipRectGuard guard(to, getTextLocation()); // to properly trim text that is too big to fit
for (size_t i = beginLine; i < std::min(totalLines, endLine); i++)
{
const std::string &line = lines[i];
if ( !(lines.size() && line.size())) //empty message or empty line
continue;
if (!lines[i].empty()) //non-empty line
blitLine(to, Rect(lineStart, lineSize), lines[i]);
int x = pos.x;
if(alignment == CENTER)
{
x += pos.w - f->getStringWidth(line.c_str()) / 2;
}
blitLine(to, Point(x, base_y + i * dy), line);
lineStart.y += f->getLineHeight();
}
}
void CBoundedLabel::recalculateLines(const std::string &Txt)
void CMultiLineLabel::splitText(const std::string &Txt)
{
lines.clear();
@ -1300,10 +1326,32 @@ void CBoundedLabel::recalculateLines(const std::string &Txt)
lines = CMessage::breakText(Txt, pos.w, font);
maxH = lineHeight * lines.size();
maxW = 0;
textSize.y = lineHeight * lines.size();
textSize.x = 0;
for(const std::string &line : lines)
vstd::amax(maxW, f->getStringWidth(line.c_str()));
vstd::amax( textSize.x, f->getStringWidth(line.c_str()));
redraw();
}
Rect CMultiLineLabel::getTextLocation()
{
// this method is needed for vertical alignment alignment of text
// when height of available text is smaller than height of widget
// in this case - we should add proper offset to display text at required position
if (pos.h <= textSize.y)
return pos;
Point textSize(pos.w, graphics->fonts[font]->getLineHeight() * lines.size());
Point textOffset(pos.w - textSize.x, pos.h - textSize.y);
switch(alignment)
{
case TOPLEFT: return Rect(pos.topLeft(), textSize);
case CENTER: return Rect(pos.topLeft() + textOffset / 2, textSize);
case BOTTOMRIGHT: return Rect(pos.topLeft() + textOffset, textSize);
}
assert(0);
return Rect();
}
CLabelGroup::CLabelGroup(EFonts Font, EAlignment Align, const SDL_Color &Color):
@ -1316,104 +1364,72 @@ void CLabelGroup::add(int x, int y, const std::string &text)
new CLabel(x, y, font, align, color, text);
}
CTextBox::CTextBox(std::string Text, const Rect &rect, int SliderStyle, EFonts Font /*= FONT_SMALL*/, EAlignment Align /*= TOPLEFT*/, const SDL_Color &Color /*= Colors::WHITE*/)
:CBoundedLabel(rect.x, rect.y, Font, Align, Color, Text), sliderStyle(SliderStyle), slider(nullptr)
CTextBox::CTextBox(std::string Text, const Rect &rect, int SliderStyle, EFonts Font /*= FONT_SMALL*/, EAlignment Align /*= TOPLEFT*/, const SDL_Color &Color /*= Colors::WHITE*/):
sliderStyle(SliderStyle),
slider(nullptr)
{
OBJ_CONSTRUCTION_CAPTURING_ALL;
label = new CMultiLineLabel(rect.x, rect.y, Font, Align, Color);
type |= REDRAW_PARENT;
autoRedraw = false;
pos.h = rect.h;
pos.w = rect.w;
assert(Align == TOPLEFT || Align == CENTER); //TODO: support for other alignments
label->pos.h = pos.h = rect.h;
label->pos.w = pos.w = rect.w;
assert(pos.w >= 40); //we need some space
setTxt(Text);
}
void CTextBox::recalculateLines(const std::string &Txt)
{
//TODO: merge with CBoundedlabel::recalculateLines
vstd::clear_pointer(slider);
lines.clear();
const IFont * f = graphics->fonts[font];
int lineHeight = f->getLineHeight();
int lineCapacity = pos.h / lineHeight;
lines = CMessage::breakText(Txt, pos.w, font);
if (lines.size() > lineCapacity) //we need to add a slider
{
lines = CMessage::breakText(Txt, pos.w - 32 - 10, font);
OBJ_CONSTRUCTION_CAPTURING_ALL;
slider = new CSlider(pos.w - 32, 0, pos.h, boost::bind(&CTextBox::sliderMoved, this, _1), lineCapacity, lines.size(), 0, false, sliderStyle);
if(active)
slider->activate();
}
maxH = lineHeight * lines.size();
maxW = 0;
for(const std::string &line : lines)
vstd::amax(maxW, f->getStringWidth(line));
}
void CTextBox::showAll(SDL_Surface * to)
{
CIntObject::showAll(to);
const IFont * f = graphics->fonts[font];
int dy = f->getLineHeight(); //line height
int base_y = pos.y;
if (alignment == CENTER)
base_y += std::max((pos.h - maxH)/2,0);
int howManyLinesToPrint = slider ? slider->capacity : lines.size();
int firstLineToPrint = slider ? slider->value : 0;
for (int i = 0; i < howManyLinesToPrint; i++)
{
const std::string &line = lines[i + firstLineToPrint];
if(!line.size()) continue;
int width = pos.w + (slider ? (slider->pos.w) : 0);
int x = pos.x + int(alignment) * width / 2;
blitLine(to, Point(x, base_y + i * dy), line);
}
setText(Text);
}
void CTextBox::sliderMoved(int to)
{
if(!slider)
return;
redraw();
label->scrollTextTo(to);
}
const Point CGStatusBar::edgeOffset = Point(5, 1);
void CGStatusBar::print(const std::string & Text)
void CTextBox::resize(Point newSize)
{
if(!textLock)
setTxt(Text);
pos.w = newSize.x;
pos.h = newSize.y;
label->pos.w = pos.w;
label->pos.h = pos.h;
if (slider)
vstd::clear_pointer(slider); // will be recreated if needed later
setText(label->getText()); // force refresh
}
void CTextBox::setText(const std::string &text)
{
label->setText(text);
if (label->textSize.y <= label->pos.h && slider)
{
// slider is no longer needed
vstd::clear_pointer(slider);
label->pos.w = pos.w;
label->setText(text);
}
else if (label->textSize.y > label->pos.h && !slider)
{
// create slider and update widget
label->pos.w = pos.w - 32;
label->setText(text);
OBJ_CONSTRUCTION_CAPTURING_ALL;
slider = new CSlider(pos.w - 32, 0, pos.h, boost::bind(&CTextBox::sliderMoved, this, _1),
label->pos.h, label->textSize.y, 0, false, sliderStyle);
slider->scrollStep = graphics->fonts[label->font]->getLineHeight();
}
}
void CGStatusBar::setText(const std::string & Text)
{
if(!textLock)
CLabel::setText(Text);
}
void CGStatusBar::clear()
{
if(!textLock)
setTxt("");
setText("");
}
std::string CGStatusBar::getCurrent()
{
return text;
}
//CGStatusBar::CGStatusBar(int x, int y, EFonts Font /*= FONT_SMALL*/, EAlignment Align, const SDL_Color &Color /*= Colors::WHITE*/, const std::string &Text /*= ""*/)
//: CLabel(x, y, Font, Align, Color, Text)
//{
// init();
//}
CGStatusBar::CGStatusBar(CPicture *BG, EFonts Font /*= FONT_SMALL*/, EAlignment Align /*= CENTER*/, const SDL_Color &Color /*= Colors::WHITE*/)
: CLabel(BG->pos.x, BG->pos.y, Font, Align, Color, "")
{
@ -1421,7 +1437,7 @@ CGStatusBar::CGStatusBar(CPicture *BG, EFonts Font /*= FONT_SMALL*/, EAlignment
bg = BG;
addChild(bg);
pos = bg->pos;
calcOffset();
getBorderSize();
textLock = false;
}
@ -1437,7 +1453,6 @@ CGStatusBar::CGStatusBar(int x, int y, std::string name/*="ADROLLVR.bmp"*/, int
vstd::amin(pos.w, maxw);
bg->srcRect = new Rect(0, 0, maxw, pos.h);
}
calcOffset();
textLock = false;
}
@ -1457,20 +1472,19 @@ void CGStatusBar::init()
GH.statusbar = this;
}
void CGStatusBar::calcOffset()
Point CGStatusBar::getBorderSize()
{
//Width of borders where text should not be printed
static const Point borderSize(5,1);
switch(alignment)
{
case TOPLEFT:
textOffset = Point(edgeOffset.x, edgeOffset.y);
break;
case CENTER:
textOffset = Point(pos.w/2, pos.h/2);
break;
case BOTTOMRIGHT:
textOffset = Point(pos.w - edgeOffset.x, pos.h - edgeOffset.y);
break;
case TOPLEFT: return Point(borderSize.x, borderSize.y);
case CENTER: return Point(pos.w/2, pos.h/2);
case BOTTOMRIGHT: return Point(pos.w - borderSize.x, pos.h - borderSize.y);
}
assert(0);
return Point();
}
void CGStatusBar::lock(bool shouldLock)
@ -1486,7 +1500,6 @@ CTextInput::CTextInput(const Rect &Pos, EFonts font, const CFunctionList<void(co
focus = false;
pos.h = Pos.h;
pos.w = Pos.w;
textOffset = Point(pos.w/2, pos.h/2);
captureAllKeys = true;
bg = nullptr;
addUsedEvents(LCLICK | KEYBOARD);
@ -1570,9 +1583,9 @@ void CTextInput::keyPressed( const SDL_KeyboardEvent & key )
}
}
void CTextInput::setTxt( const std::string &nText, bool callCb )
void CTextInput::setText( const std::string &nText, bool callCb )
{
CLabel::setTxt(nText);
CLabel::setText(nText);
if(callCb)
cb(text);
}