diff --git a/src/gui/interface/Label.cpp b/src/gui/interface/Label.cpp index dfd72cd1f..181db8def 100644 --- a/src/gui/interface/Label.cpp +++ b/src/gui/interface/Label.cpp @@ -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)); diff --git a/src/gui/interface/Label.h b/src/gui/interface/Label.h index 3dfa6ae92..12fa6451e 100644 --- a/src/gui/interface/Label.h +++ b/src/gui/interface/Label.h @@ -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; diff --git a/src/gui/interface/RichLabel.cpp b/src/gui/interface/RichLabel.cpp index efa6a7d5f..1dcd7f5aa 100644 --- a/src/gui/interface/RichLabel.cpp +++ b/src/gui/interface/RichLabel.cpp @@ -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 -#include +#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 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(®ion.action)) { - case 'a': - Platform::OpenURI(region.actionData.ToUtf8()); - break; + Platform::OpenURI(linkAction->uri); + return; } } } + Label::OnMouseClick(x, y, button); } diff --git a/src/gui/interface/RichLabel.h b/src/gui/interface/RichLabel.h index 1f7326924..8001fa657 100644 --- a/src/gui/interface/RichLabel.h +++ b/src/gui/interface/RichLabel.h @@ -1,41 +1,29 @@ #pragma once - -#include "common/String.h" -#include "Component.h" -#include "TextWrapper.h" +#include "Label.h" +#include +#include 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; + 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 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; }; } diff --git a/src/gui/interface/TextWrapper.cpp b/src/gui/interface/TextWrapper.cpp index 0a4575e9b..20b5c9c48 100644 --- a/src/gui/interface/TextWrapper.cpp +++ b/src/gui/interface/TextWrapper.cpp @@ -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; } diff --git a/src/gui/search/SearchView.cpp b/src/gui/search/SearchView.cpp index edb5eba49..c9326d115 100644 --- a/src/gui/search/SearchView.cpp +++ b/src/gui/search/SearchView.cpp @@ -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(); } });