enhanced StaticTextWidget to display links

adapted AboutDialog accordingly
This commit is contained in:
thrust26 2021-04-22 11:43:08 +02:00
parent b945e15adc
commit f641457083
6 changed files with 189 additions and 17 deletions

View File

@ -383,7 +383,8 @@ int FBSurface::drawString(const GUI::Font& font, const string& s,
void FBSurface::drawString(const GUI::Font& font, const string& s,
int x, int y, int w,
ColorId color, TextAlign align,
int deltax, bool useEllipsis, ColorId shadowColor)
int deltax, bool useEllipsis, ColorId shadowColor,
size_t linkStart, int linkLen, bool underline)
{
#ifdef GUI_SUPPORT
const string ELLIPSIS = "\x1d"; // "..."
@ -424,16 +425,30 @@ void FBSurface::drawString(const GUI::Font& font, const string& s,
x = x + w - width;
x += deltax;
int x0 = 0, x1 = 0;
for(i = 0; i < str.size(); ++i)
{
w = font.getCharWidth(str[i]);
if(x+w > rightX)
if(x + w > rightX)
break;
if(x >= leftX)
drawChar(font, str[i], x, y, color, shadowColor);
{
if(i == linkStart)
x0 = x;
else if(i < linkStart + linkLen)
x1 = x + w;
drawChar(font, str[i], x, y,
(i >= linkStart && i < linkStart + linkLen) ? kTextColorEm : color,
shadowColor);
}
x += w;
}
if(underline && x1 > 0)
hLine(x0, y + font.getFontHeight() - 1, x1, kTextColorEm);
#endif
}

View File

@ -245,7 +245,8 @@ class FBSurface
virtual void drawString(
const GUI::Font& font, const string& s, int x, int y, int w,
ColorId color, TextAlign align = TextAlign::Left,
int deltax = 0, bool useEllipsis = true, ColorId shadowColor = kNone);
int deltax = 0, bool useEllipsis = true, ColorId shadowColor = kNone,
size_t linkStart = string::npos, int linkLen = 0, bool underline = false);
/**
Splits a given string to a given width considering whitespaces.

View File

@ -21,6 +21,7 @@
#include "Widget.hxx"
#include "Font.hxx"
#include "WhatsNewDialog.hxx"
#include "MediaFactory.hxx"
#include "AboutDialog.hxx"
@ -80,8 +81,10 @@ AboutDialog::AboutDialog(OSystem& osystem, DialogContainer& parent,
xpos = HBORDER * 2; ypos += lineHeight + VGAP * 2;
for(int i = 0; i < myLinesPerPage; i++)
{
myDesc.push_back(new StaticTextWidget(this, font, xpos, ypos, _w - xpos * 2,
fontHeight, "", TextAlign::Left));
StaticTextWidget* s = new StaticTextWidget(this, font, xpos, ypos, _w - xpos * 2,
fontHeight, "", TextAlign::Left, kNone);
s->setID(i);
myDesc.push_back(s);
myDescStr.emplace_back("");
ypos += fontHeight;
}
@ -162,7 +165,7 @@ void AboutDialog::updateStrings(int page, int lines, string& title)
title = "Cast of thousands";
ADD_ATEXT("\\L\\c0""Special thanks to AtariAge for introducing the");
ADD_ATEXT("\\L\\c0""Atari 2600 to a whole new generation.");
ADD_ATEXT("\\L\\c2"" http://www.atariage.com");
ADD_ATEXT("\\L http://www.atariage.com");
ADD_ALINE();
ADD_ATEXT("\\L\\c0""Finally, a huge thanks to the original Atari 2600");
ADD_ATEXT("\\L\\c0""VCS team for giving us the magic, and to the");
@ -242,6 +245,7 @@ void AboutDialog::displayInfo()
myDesc[i]->setAlign(align);
myDesc[i]->setTextColor(color);
myDesc[i]->setLabel(str);
myDesc[i]->setUrl(); // extract URL from label
}
// Redraw entire dialog
@ -280,7 +284,52 @@ void AboutDialog::handleCommand(CommandSender* sender, int cmd, int data, int id
myWhatsNewDialog->open();
break;
case StaticTextWidget::kOpenUrlCmd:
{
const string url = myDesc[id]->getUrl(); // getUrl(myDescStr[id]);
if(url != EmptyString)
MediaFactory::openURL(url);
break;
}
default:
Dialog::handleCommand(sender, cmd, data, 0);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string AboutDialog::getUrl(const string& str) const
{
bool isUrl = false;
int start = 0, len = 0;
for(int i = 0; i < str.size(); ++i)
{
string remainder = str.substr(i);
char ch = str[i];
if(!isUrl
&& (BSPF::startsWithIgnoreCase(remainder, "http://")
|| BSPF::startsWithIgnoreCase(remainder, "https://")
|| BSPF::startsWithIgnoreCase(remainder, "www.")))
{
isUrl = true;
start = i;
}
// hack, change mode without changing string length
if(isUrl)
{
if((ch == ' ' || ch == ')' || ch == '>'))
isUrl = false;
else
len++;
}
}
if(len)
return str.substr(start, len);
else
return EmptyString;
}

View File

@ -38,6 +38,7 @@ class AboutDialog : public Dialog
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
void updateStrings(int page, int lines, string& title);
void displayInfo();
const string getUrl(const string& text) const;
void loadConfig() override { displayInfo(); }

View File

@ -462,6 +462,7 @@ StaticTextWidget::StaticTextWidget(GuiObject* boss, const GUI::Font& font,
const string& text, TextAlign align,
ColorId shadowColor)
: Widget(boss, font, x, y, w, h),
CommandSender(boss),
_label{text},
_align{align}
{
@ -472,6 +473,7 @@ StaticTextWidget::StaticTextWidget(GuiObject* boss, const GUI::Font& font,
_textcolor = kTextColor;
_textcolorhi = kTextColor;
_shadowcolor = shadowColor;
_cmd = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -500,29 +502,116 @@ void StaticTextWidget::setLabel(const string& label)
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StaticTextWidget::setLink(size_t start, int len, bool underline)
{
if(_linkStart != start || _linkLen != len || _linkUnderline != underline)
{
_linkStart = start;
_linkLen = len;
_linkUnderline = underline;
setCmd(len ? kClickedCmd : 0);
setDirty();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool StaticTextWidget::setUrl(const string& url, const string& label)
{
size_t start = string::npos;
int len = 0;
const string text = label != EmptyString ? label : url;
if(text != EmptyString)
{
// determine position of label
if((start = BSPF::findIgnoreCase(_label, text)) != string::npos)
{
len = int(text.size());
_url = url;
}
}
else
{
// extract URL from _label
start = BSPF::findIgnoreCase(_label, "http://");
if(start == string::npos)
start = BSPF::findIgnoreCase(_label, "https://");
if(start == string::npos)
start = BSPF::findIgnoreCase(_label, "www.");
if(start != string::npos)
{
// find end of URL
for(int i = int(start); i < _label.size(); ++i)
{
char ch = _label[i];
if(ch == ' ' || ch == ')' || ch == '>')
{
len = i - int(start);
_url = _label.substr(start, len);
break;
}
}
if(!len)
{
len = int(_label.size() - start);
_url = _label.substr(start);
}
}
}
if(len)
{
setLink(start, len, true);
setCmd(kOpenUrlCmd);
return true;
}
else
{
setLink(); // clear link
_url = EmptyString;
return false;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StaticTextWidget::handleMouseEntered()
{
if(isEnabled())
// Mouse focus for tooltips must not change dirty status
setFlags(Widget::FLAG_MOUSE_FOCUS, false);
setFlags(Widget::FLAG_HILITED | Widget::FLAG_MOUSE_FOCUS, _linkLen);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StaticTextWidget::handleMouseLeft()
{
if(isEnabled())
// Mouse focus for tooltips must not change dirty status
clearFlags(Widget::FLAG_MOUSE_FOCUS, false);
clearFlags(Widget::FLAG_HILITED | Widget::FLAG_MOUSE_FOCUS, _linkLen);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StaticTextWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount)
{
if(_cmd && isEnabled() && x >= 0 && x < _w && y >= 0 && y < _h)
{
clearFlags(Widget::FLAG_HILITED);
sendCommand(_cmd, 0, _id);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StaticTextWidget::drawWidget(bool hilite)
{
FBSurface& s = _boss->dialog().surface();
s.drawString(_font, _label, _x, _y, _w,
isEnabled() ? _textcolor : kColor, _align, 0, true, _shadowcolor);
isEnabled() ? _textcolor : kColor, _align, 0, true,
_shadowcolor, _linkStart, _linkLen, _linkUnderline && hilite);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -530,7 +619,6 @@ ButtonWidget::ButtonWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, int w, int h,
const string& label, int cmd, bool repeat)
: StaticTextWidget(boss, font, x, y, w, h, label, TextAlign::Center),
CommandSender(boss),
_cmd{cmd},
_repeat{repeat}
{

View File

@ -191,8 +191,14 @@ class Widget : public GuiObject
};
/* StaticTextWidget */
class StaticTextWidget : public Widget
class StaticTextWidget : public Widget, public CommandSender
{
public:
enum {
kClickedCmd = 'STcl',
kOpenUrlCmd = 'STou'
};
public:
StaticTextWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, int w, int h,
@ -204,8 +210,7 @@ class StaticTextWidget : public Widget
ColorId shadowColor = kNone);
~StaticTextWidget() override = default;
void handleMouseEntered() override;
void handleMouseLeft() override;
void setCmd(int cmd) { _cmd = cmd; }
void setValue(int value);
void setLabel(const string& label);
@ -213,13 +218,26 @@ class StaticTextWidget : public Widget
const string& getLabel() const { return _label; }
bool isEditable() const { return _editable; }
void setLink(size_t start = string::npos, int len = 0, bool underline = false);
bool setUrl(const string& url = EmptyString, const string& label = EmptyString);
const string& getUrl() const { return _url; };
protected:
void handleMouseEntered() override;
void handleMouseLeft() override;
void handleMouseUp(int x, int y, MouseButton b, int clickCount) override;
void drawWidget(bool hilite) override;
protected:
string _label;
bool _editable{false};
TextAlign _align{TextAlign::Left};
int _cmd{0};
size_t _linkStart{string::npos};
int _linkLen{0};
bool _linkUnderline{false};
string _url;
private:
// Following constructors and assignment operators not supported
@ -231,7 +249,7 @@ class StaticTextWidget : public Widget
};
/* ButtonWidget */
class ButtonWidget : public StaticTextWidget, public CommandSender
class ButtonWidget : public StaticTextWidget
{
public:
ButtonWidget(GuiObject* boss, const GUI::Font& font,