Clean up RichLabel
This commit is contained in:
parent
151bc4c9cd
commit
4ab2a032af
@ -18,7 +18,10 @@ Label::Label(Point position, Point size, String labelText):
|
||||
selecting(false),
|
||||
autoHeight(size.Y==-1?true:false)
|
||||
{
|
||||
SetText(labelText);
|
||||
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));
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
{
|
||||
while(originalText[originalTextPos])
|
||||
auto find = [&newText](auto it, String::value_type ch) {
|
||||
while (it != newText.end())
|
||||
{
|
||||
char current = originalText[originalTextPos];
|
||||
|
||||
if(state == ReadText)
|
||||
if (*it == ch)
|
||||
{
|
||||
if(current == '{')
|
||||
{
|
||||
if(stackPos > 255)
|
||||
throw RichTextParseException("Too many nested regions");
|
||||
stackPos++;
|
||||
regionsStack[stackPos].start = finalTextPos;
|
||||
regionsStack[stackPos].finish = finalTextPos;
|
||||
state = ReadRegion;
|
||||
}
|
||||
else if(current == '}')
|
||||
{
|
||||
if(stackPos >= 0)
|
||||
{
|
||||
currentData[currentDataPos] = 0;
|
||||
regionsStack[stackPos].actionData = String(currentData);
|
||||
regions.push_back(regionsStack[stackPos]);
|
||||
stackPos--;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw RichTextParseException("Unexpected '}'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
finalText[finalTextPos++] = current;
|
||||
finalText[finalTextPos] = 0;
|
||||
if(stackPos >= 0)
|
||||
{
|
||||
regionsStack[stackPos].finish = finalTextPos;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
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++;
|
||||
++it;
|
||||
}
|
||||
|
||||
if(stackPos != -1)
|
||||
throw RichTextParseException("Unclosed region");
|
||||
|
||||
finalText[finalTextPos] = 0;
|
||||
displayText = String(finalText);
|
||||
}
|
||||
catch (const RichTextParseException & e)
|
||||
return it;
|
||||
};
|
||||
auto beginRegionIt = find(it, '{');
|
||||
auto beginDataIt = find(beginRegionIt, ':');
|
||||
auto beginTextIt = find(beginDataIt, '|');
|
||||
auto endRegionIt = find(beginTextIt, '}');
|
||||
if (endRegionIt == newText.end())
|
||||
{
|
||||
displayText = "\br[Parse exception: " + ByteString(e.what()).FromUtf8() + "]";
|
||||
regions.clear();
|
||||
break;
|
||||
}
|
||||
delete[] currentData;
|
||||
delete[] finalText;
|
||||
delete[] originalText;
|
||||
delete[] regionsStack;
|
||||
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;
|
||||
}
|
||||
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 ®ion : 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>(®ion.action))
|
||||
{
|
||||
case 'a':
|
||||
Platform::OpenURI(region.actionData.ToUtf8());
|
||||
break;
|
||||
Platform::OpenURI(linkAction->uri);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Label::OnMouseClick(x, y, button);
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) { }
|
||||
motdLabel = new ui::RichLabel(ui::Point(51, WINDOWH-18), ui::Point(WINDOWW-102, 16), Client::Ref().GetMessageOfTheDay());
|
||||
|
||||
pageTextbox = new ui::Textbox(ui::Point(283, WINDOWH-18), ui::Point(41, 16), "");
|
||||
pageTextbox->SetActionCallback({ [this] { textChanged(); } });
|
||||
|
Loading…
Reference in New Issue
Block a user