Clean up RichLabel

This commit is contained in:
Tamás Bálint Misius 2023-12-16 10:41:16 +01:00
parent 151bc4c9cd
commit 4ab2a032af
No known key found for this signature in database
GPG Key ID: 5B472A12F6ECA9F2
6 changed files with 76 additions and 206 deletions

View File

@ -18,7 +18,10 @@ Label::Label(Point position, Point size, String labelText):
selecting(false),
autoHeight(size.Y==-1?true:false)
{
if (labelText.size()) // Don't call virtual function in ctor unless absolutely necessary. Deriveds set labelText to "".
{
SetText(labelText);
}
menu = new ContextMenu(this);
menu->AddItem(ContextMenuItem("Copy", 0, true));

View File

@ -58,7 +58,7 @@ namespace ui
void SetTextColour(Colour textColour) { this->textColour = textColour; }
void OnContextMenuAction(int item) override;
void OnMouseClick(int x, int y, unsigned button) override;
virtual void OnMouseClick(int x, int y, unsigned button) override;
void OnMouseUp(int x, int y, unsigned button) override;
void OnMouseMoved(int localx, int localy, int dx, int dy) override;
void OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override;

View File

@ -1,185 +1,67 @@
#include "RichLabel.h"
#include "Colour.h"
#include "common/platform/Platform.h"
#include "graphics/FontReader.h"
#include "graphics/Graphics.h"
#include "gui/interface/Component.h"
#include "gui/interface/Point.h"
#include <vector>
#include <exception>
#include "Format.h"
using namespace ui;
struct RichTextParseException: public std::exception {
ByteString message;
public:
RichTextParseException(String message = String("Parse error")): message(message.ToUtf8()) {}
const char * what() const throw() override
{
return message.c_str();
}
~RichTextParseException() throw() {};
};
RichLabel::RichLabel(Point position, Point size, String labelText):
Component(position, size),
textSource(labelText),
displayText("")
RichLabel::RichLabel(Point position, Point size, String text) : Label(position, size, "")
{
updateRichText();
SetText(text);
}
RichLabel::~RichLabel()
void RichLabel::SetText(String newText)
{
}
void RichLabel::updateRichText()
{
regions.clear();
displayText = "";
if(textSource.length())
Label::SetText(newText);
std::vector<RichTextRegion> newRegions;
StringBuilder sb;
auto it = newText.begin();
while (it != newText.end())
{
enum State { ReadText, ReadData, ReadRegion, ReadDataStart };
State state = ReadText;
int currentDataPos = 0;
String::value_type * currentData = new String::value_type[textSource.length()+1];
std::fill(currentData, currentData+textSource.length()+1, 0);
int finalTextPos = 0;
String::value_type * finalText = new String::value_type[textSource.length()+1];
std::fill(finalText, finalText+textSource.length()+1, 0);
int originalTextPos = 0;
String::value_type * originalText = new String::value_type[textSource.length()+1];
std::copy(textSource.begin(), textSource.end(), originalText);
originalText[textSource.length()] = 0;
int stackPos = -1;
RichTextRegion * regionsStack = new RichTextRegion[256];
try
auto find = [&newText](auto it, String::value_type ch) {
while (it != newText.end())
{
while(originalText[originalTextPos])
if (*it == ch)
{
char current = originalText[originalTextPos];
if(state == ReadText)
break;
}
++it;
}
return it;
};
auto beginRegionIt = find(it, '{');
auto beginDataIt = find(beginRegionIt, ':');
auto beginTextIt = find(beginDataIt, '|');
auto endRegionIt = find(beginTextIt, '}');
if (endRegionIt == newText.end())
{
if(current == '{')
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())
{
if(stackPos > 255)
throw RichTextParseException("Too many nested regions");
stackPos++;
regionsStack[stackPos].start = finalTextPos;
regionsStack[stackPos].finish = finalTextPos;
state = ReadRegion;
RichTextRegion region;
region.begin = sb.Size();
sb << text;
region.end = sb.Size();
region.action = RichTextRegion::LinkAction{ data.ToUtf8() };
newRegions.push_back(region);
good = true;
}
else if(current == '}')
if (!good)
{
if(stackPos >= 0)
{
currentData[currentDataPos] = 0;
regionsStack[stackPos].actionData = String(currentData);
regions.push_back(regionsStack[stackPos]);
stackPos--;
sb << String(beginRegionIt, endRegionIt + 1);
}
else
{
throw RichTextParseException("Unexpected '}'");
it = endRegionIt + 1;
}
}
else
{
finalText[finalTextPos++] = current;
finalText[finalTextPos] = 0;
if(stackPos >= 0)
{
regionsStack[stackPos].finish = finalTextPos;
}
}
}
else if(state == ReadData)
{
if(current == '|')
{
state = ReadText;
}
else
{
currentData[currentDataPos++] = current;
currentData[currentDataPos] = 0;
}
}
else if(state == ReadDataStart)
{
if(current != ':')
{
throw RichTextParseException("Expected ':'");
}
state = ReadData;
currentDataPos = 0;
}
else if(state == ReadRegion)
{
if(stackPos >= 0)
{
regionsStack[stackPos].action = current;
state = ReadDataStart;
}
else
{
throw RichTextParseException();
}
}
originalTextPos++;
}
if(stackPos != -1)
throw RichTextParseException("Unclosed region");
finalText[finalTextPos] = 0;
displayText = String(finalText);
}
catch (const RichTextParseException & e)
{
displayText = "\br[Parse exception: " + ByteString(e.what()).FromUtf8() + "]";
regions.clear();
}
delete[] currentData;
delete[] finalText;
delete[] originalText;
delete[] regionsStack;
}
TextPosition(displayText);
displayTextWrapper.Update(displayText, false, 0);
}
void RichLabel::SetText(String text)
{
textSource = text;
updateRichText();
}
String RichLabel::GetDisplayText()
{
return displayText;
}
String RichLabel::GetText()
{
return textSource;
}
void RichLabel::Draw(const Point& screenPos)
{
Graphics * g = GetGraphics();
ui::Colour textColour = Appearance.TextInactive;
g->BlendText(screenPos + textPosition, displayText, textColour.NoAlpha().WithAlpha(255));
sb << String(it, newText.end());
auto newDisplayText = sb.Build();
Label::SetText(format::CleanString(newDisplayText, false, true, false));
Label::SetDisplayText(newDisplayText);
regions = newRegions;
}
void RichLabel::OnMouseClick(int x, int y, unsigned button)
@ -187,14 +69,14 @@ void RichLabel::OnMouseClick(int x, int y, unsigned button)
int cursorPosition = displayTextWrapper.Point2Index(x - textPosition.X, y - textPosition.Y).raw_index;
for (auto const &region : regions)
{
if (region.start <= cursorPosition && region.finish >= cursorPosition)
if (region.begin <= cursorPosition && region.end > cursorPosition)
{
switch (region.action)
if (auto *linkAction = std::get_if<RichTextRegion::LinkAction>(&region.action))
{
case 'a':
Platform::OpenURI(region.actionData.ToUtf8());
break;
Platform::OpenURI(linkAction->uri);
return;
}
}
}
Label::OnMouseClick(x, y, button);
}

View File

@ -1,41 +1,29 @@
#pragma once
#include "common/String.h"
#include "Component.h"
#include "TextWrapper.h"
#include "Label.h"
#include <vector>
#include <variant>
namespace ui
{
class RichLabel : public Component
class RichLabel : public Label
{
public:
struct RichTextRegion
{
int start;
int finish;
int action;
String actionData;
int begin;
int end;
struct LinkAction
{
ByteString uri;
};
using Action = std::variant<LinkAction>;
Action action;
};
TextWrapper displayTextWrapper;
RichLabel(Point position, Point size, String richText);
virtual ~RichLabel();
void SetText(String text);
String GetDisplayText();
String GetText();
void Draw(const Point& screenPos) override;
void OnMouseClick(int x, int y, unsigned button) override;
protected:
String textSource;
String displayText;
std::vector<RichTextRegion> regions;
void updateRichText();
public:
RichLabel(Point position, Point size, String text);
void SetText(String newText) override;
void OnMouseClick(int x, int y, unsigned button) override;
};
}

View File

@ -63,6 +63,7 @@ namespace ui
switch (*it) // set sequence_length if *it starts a sequence that should be forwarded as-is
{
case '\b': sequence_length = 2; break;
case '\x0e': sequence_length = 1; break;
case '\x0f': sequence_length = 4; break;
}

View File

@ -30,11 +30,7 @@ SearchView::SearchView():
nextButton = new ui::Button(ui::Point(WINDOWW-52, WINDOWH-18), ui::Point(50, 16), String("Next ") + 0xE015);
previousButton = new ui::Button(ui::Point(2, WINDOWH-18), ui::Point(50, 16), 0xE016 + String(" Prev"));
tagsLabel = new ui::Label(ui::Point(270, WINDOWH-18), ui::Point(WINDOWW-540, 16), "\boPopular Tags:");
try
{
motdLabel = new ui::RichLabel(ui::Point(51, WINDOWH-18), ui::Point(WINDOWW-102, 16), Client::Ref().GetMessageOfTheDay());
}
catch (std::exception & e) { }
pageTextbox = new ui::Textbox(ui::Point(283, WINDOWH-18), ui::Point(41, 16), "");
pageTextbox->SetActionCallback({ [this] { textChanged(); } });