mirror of https://github.com/stella-emu/stella.git
enhanced StaticTextWidget to display links
adapted AboutDialog accordingly
This commit is contained in:
parent
b945e15adc
commit
f641457083
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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(); }
|
||||
|
||||
|
|
|
@ -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}
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue