This is a prerequisite for making ScrollPanel work nicely on touch screens. Engine used the terms MouseClick and MouseUnclick to refer to events that are traditionally called MouseDown and MouseUp, this was fixed with simple renaming. Component and friends similarly used the terms MouseClick and MouseUnclick to refer to events that are traditionally called MouseDown and MouseUp and, succumbing to their own confusing terminology, also implemented behaviours associated with both the actual events MouseDown and MouseClick in code that was responsible for handling only the actual event MouseDown (i.e. what they called MouseClick). This had been overlooked for a long time because nobody cares that a checkbox changes state when the mouse button is pressed on it rather than when it is released. The fix is to migrate many pieces of code that run in response to MouseDown events, such as checkbox state change code, to MouseClick events, and to redefine a MouseClick to mean a sequence of MouseDown and MouseUp inside the component, rather than just a MouseDown. This is complicated by the fact that MouseClick events report mouse coordinates relative to the top left corner of the component, while MouseDown events report them relative to the top left corner of the container of the component. Other pieces of code that make sense to be run in response to MouseDown events, such as label selection code, were left alone.
86 lines
2.1 KiB
C++
86 lines
2.1 KiB
C++
#include "RichLabel.h"
|
|
#include "common/platform/Platform.h"
|
|
#include "Format.h"
|
|
|
|
using namespace ui;
|
|
|
|
RichLabel::RichLabel(Point position, Point size, String text) : Label(position, size, "")
|
|
{
|
|
SetText(text);
|
|
}
|
|
|
|
void RichLabel::SetText(String newText)
|
|
{
|
|
Label::SetText(newText);
|
|
std::vector<RichTextRegion> newRegions;
|
|
StringBuilder sb;
|
|
auto it = newText.begin();
|
|
while (it != newText.end())
|
|
{
|
|
auto find = [&newText](auto it, String::value_type ch) {
|
|
while (it != newText.end())
|
|
{
|
|
if (*it == ch)
|
|
{
|
|
break;
|
|
}
|
|
++it;
|
|
}
|
|
return it;
|
|
};
|
|
auto beginRegionIt = find(it, '{');
|
|
auto beginDataIt = find(beginRegionIt, ':');
|
|
auto beginTextIt = find(beginDataIt, '|');
|
|
auto endRegionIt = find(beginTextIt, '}');
|
|
if (endRegionIt == newText.end())
|
|
{
|
|
break;
|
|
}
|
|
auto action = String(beginRegionIt + 1, beginDataIt);
|
|
auto data = String(beginDataIt + 1, beginTextIt);
|
|
auto text = String(beginTextIt + 1, endRegionIt);
|
|
sb << String(it, beginRegionIt);
|
|
auto good = false;
|
|
if (action == "a" && data.size() && text.size())
|
|
{
|
|
RichTextRegion region;
|
|
region.begin = sb.Size();
|
|
sb << text;
|
|
region.end = sb.Size();
|
|
region.action = RichTextRegion::LinkAction{ data.ToUtf8() };
|
|
newRegions.push_back(region);
|
|
good = true;
|
|
}
|
|
if (!good)
|
|
{
|
|
sb << String(beginRegionIt, endRegionIt + 1);
|
|
}
|
|
it = endRegionIt + 1;
|
|
}
|
|
sb << String(it, newText.end());
|
|
auto newDisplayText = sb.Build();
|
|
Label::SetText(format::CleanString(newDisplayText, false, true, false));
|
|
Label::SetDisplayText(newDisplayText);
|
|
regions = newRegions;
|
|
}
|
|
|
|
void RichLabel::OnMouseDown(int x, int y, unsigned button)
|
|
{
|
|
if (MouseDownInside)
|
|
{
|
|
int cursorPosition = displayTextWrapper.Point2Index(x - Position.X - textPosition.X, y - Position.Y - textPosition.Y).raw_index;
|
|
for (auto const ®ion : regions)
|
|
{
|
|
if (region.begin <= cursorPosition && region.end > cursorPosition)
|
|
{
|
|
if (auto *linkAction = std::get_if<RichTextRegion::LinkAction>(®ion.action))
|
|
{
|
|
Platform::OpenURI(linkAction->uri);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Label::OnMouseDown(x, y, button);
|
|
}
|