Fix text wrapping (fixes #166)
This breaks the syntax highlighting in ConsoleView. I haven't yet thought of a way to fix that properly, ideas anyone?
This commit is contained in:
parent
c5c4d92c32
commit
4bb85578a8
@ -644,71 +644,6 @@ int Graphics::textwidthx(String str, int w)
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Graphics::PositionAtCharIndex(String str, int charIndex, int & positionX, int & positionY)
|
|
||||||
{
|
|
||||||
int x = 0, y = 0, lines = 1;
|
|
||||||
String::value_type const *s = str.c_str();
|
|
||||||
for (; *s; s++)
|
|
||||||
{
|
|
||||||
if (!charIndex)
|
|
||||||
break;
|
|
||||||
if(*s == '\n') {
|
|
||||||
lines++;
|
|
||||||
x = 0;
|
|
||||||
y += FONT_H;
|
|
||||||
charIndex--;
|
|
||||||
continue;
|
|
||||||
} else if(*s =='\b') {
|
|
||||||
if(!s[1]) break;
|
|
||||||
s++;
|
|
||||||
charIndex-=2;
|
|
||||||
continue;
|
|
||||||
} else if(*s == '\x0F') {
|
|
||||||
if(!s[1] || !s[2] || !s[3]) break;
|
|
||||||
s+=3;
|
|
||||||
charIndex-=4;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
x += FontReader(*s).GetWidth();
|
|
||||||
charIndex--;
|
|
||||||
}
|
|
||||||
positionX = x;
|
|
||||||
positionY = y;
|
|
||||||
return lines;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Graphics::CharIndexAtPosition(String str, int positionX, int positionY)
|
|
||||||
{
|
|
||||||
int x=0, y=-2,charIndex=0,cw;
|
|
||||||
String::value_type const *s = str.c_str();
|
|
||||||
for (; *s; s++)
|
|
||||||
{
|
|
||||||
if(*s == '\n') {
|
|
||||||
x = 0;
|
|
||||||
y += FONT_H;
|
|
||||||
charIndex++;
|
|
||||||
continue;
|
|
||||||
} else if(*s == '\b') {
|
|
||||||
if(!s[1]) break;
|
|
||||||
s++;
|
|
||||||
charIndex+=2;
|
|
||||||
continue;
|
|
||||||
} else if (*s == '\x0F') {
|
|
||||||
if(!s[1] || !s[2] || !s[3]) break;
|
|
||||||
s+=3;
|
|
||||||
charIndex+=4;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
cw = FontReader(*s).GetWidth();
|
|
||||||
if ((x+(cw/2) >= positionX && y+FONT_H >= positionY) || y > positionY)
|
|
||||||
break;
|
|
||||||
x += cw;
|
|
||||||
charIndex++;
|
|
||||||
}
|
|
||||||
return charIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int Graphics::textwrapheight(String str, int width)
|
int Graphics::textwrapheight(String str, int width)
|
||||||
{
|
{
|
||||||
int x=0, height=FONT_H, cw;
|
int x=0, height=FONT_H, cw;
|
||||||
|
@ -113,8 +113,6 @@ public:
|
|||||||
static pixel *render_packed_rgb(void *image, int width, int height, int cmp_size);
|
static pixel *render_packed_rgb(void *image, int width, int height, int cmp_size);
|
||||||
|
|
||||||
//Font/text metrics
|
//Font/text metrics
|
||||||
static int CharIndexAtPosition(String s, int positionX, int positionY);
|
|
||||||
static int PositionAtCharIndex(String s, int charIndex, int & positionX, int & positionY);
|
|
||||||
static int CharWidth(String::value_type c);
|
static int CharWidth(String::value_type c);
|
||||||
static int textnwidth(String s, int n);
|
static int textnwidth(String s, int n);
|
||||||
static void textnpos(String s, int n, int w, int *cx, int *cy);
|
static void textnpos(String s, int n, int w, int *cx, int *cy);
|
||||||
|
@ -26,7 +26,7 @@ ConsoleView::ConsoleView():
|
|||||||
CommandHighlighter(ConsoleView * v_) { v = v_; }
|
CommandHighlighter(ConsoleView * v_) { v = v_; }
|
||||||
void TextChangedCallback(ui::Textbox * sender) override
|
void TextChangedCallback(ui::Textbox * sender) override
|
||||||
{
|
{
|
||||||
sender->SetDisplayText(v->c->FormatCommand(sender->GetText()));
|
// sender->SetDisplayText(v->c->FormatCommand(sender->GetText()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
commandField = new ui::Textbox(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), "");
|
commandField = new ui::Textbox(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), "");
|
||||||
@ -52,7 +52,7 @@ void ConsoleView::DoKeyPress(int key, int scan, bool repeat, bool shift, bool ct
|
|||||||
case SDLK_KP_ENTER:
|
case SDLK_KP_ENTER:
|
||||||
c->EvaluateCommand(commandField->GetText());
|
c->EvaluateCommand(commandField->GetText());
|
||||||
commandField->SetText("");
|
commandField->SetText("");
|
||||||
commandField->SetDisplayText("");
|
// commandField->SetDisplayText("");
|
||||||
break;
|
break;
|
||||||
case SDLK_DOWN:
|
case SDLK_DOWN:
|
||||||
c->NextCommand();
|
c->NextCommand();
|
||||||
@ -106,7 +106,7 @@ void ConsoleView::NotifyPreviousCommandsChanged(ConsoleModel * sender)
|
|||||||
void ConsoleView::NotifyCurrentCommandChanged(ConsoleModel * sender)
|
void ConsoleView::NotifyCurrentCommandChanged(ConsoleModel * sender)
|
||||||
{
|
{
|
||||||
commandField->SetText(sender->GetCurrentCommand().Command);
|
commandField->SetText(sender->GetCurrentCommand().Command);
|
||||||
commandField->SetDisplayText(c->FormatCommand(commandField->GetText()));
|
// commandField->SetDisplayText(c->FormatCommand(commandField->GetText()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,164 +13,53 @@ using namespace ui;
|
|||||||
|
|
||||||
Label::Label(Point position, Point size, String labelText):
|
Label::Label(Point position, Point size, String labelText):
|
||||||
Component(position, size),
|
Component(position, size),
|
||||||
text(labelText),
|
|
||||||
textColour(255, 255, 255),
|
textColour(255, 255, 255),
|
||||||
selectionIndex0(-1),
|
selectionIndexL(textWrapper.IndexBegin()),
|
||||||
selectionIndex1(-1),
|
selectionIndexH(textWrapper.IndexBegin()),
|
||||||
selectionXL(-1),
|
|
||||||
selectionXH(-1),
|
|
||||||
multiline(false),
|
multiline(false),
|
||||||
selecting(false),
|
selecting(false),
|
||||||
autoHeight(size.Y==-1?true:false)
|
autoHeight(size.Y==-1?true:false)
|
||||||
{
|
{
|
||||||
|
SetText(labelText);
|
||||||
|
|
||||||
menu = new ContextMenu(this);
|
menu = new ContextMenu(this);
|
||||||
menu->AddItem(ContextMenuItem("Copy", 0, true));
|
menu->AddItem(ContextMenuItem("Copy", 0, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
Label::~Label()
|
Label::~Label()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Label::SetMultiline(bool status)
|
void Label::SetMultiline(bool status)
|
||||||
{
|
{
|
||||||
multiline = status;
|
multiline = status;
|
||||||
if(status)
|
updateTextWrapper();
|
||||||
{
|
updateSelection();
|
||||||
updateMultiline();
|
TextPosition(textWrapper.WrappedText());
|
||||||
updateSelection();
|
|
||||||
TextPosition(textLines);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
TextPosition(text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Label::SetText(String text)
|
void Label::SetText(String newText)
|
||||||
{
|
{
|
||||||
this->text = text;
|
this->text = newText;
|
||||||
if(multiline)
|
updateTextWrapper();
|
||||||
{
|
updateSelection();
|
||||||
updateMultiline();
|
TextPosition(textWrapper.WrappedText());
|
||||||
updateSelection();
|
|
||||||
TextPosition(textLines);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
TextPosition(text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Label::AutoHeight()
|
void Label::AutoHeight()
|
||||||
{
|
{
|
||||||
bool oldAH = autoHeight;
|
bool oldAH = autoHeight;
|
||||||
autoHeight = true;
|
autoHeight = true;
|
||||||
updateMultiline();
|
updateTextWrapper();
|
||||||
autoHeight = oldAH;
|
autoHeight = oldAH;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Label::updateMultiline()
|
void Label::updateTextWrapper()
|
||||||
{
|
{
|
||||||
int lines = 1;
|
int lines = textWrapper.Update(text, multiline, Size.X - Appearance.Margin.Left - Appearance.Margin.Right);
|
||||||
if (text.length()>0)
|
if (autoHeight)
|
||||||
{
|
{
|
||||||
String::value_type *rawText = new String::value_type[text.length()+1];
|
Size.Y = lines * 12 + 3;
|
||||||
std::copy(text.begin(), text.end(), rawText);
|
|
||||||
rawText[text.length()] = 0;
|
|
||||||
|
|
||||||
String::value_type c, pc = 0;
|
|
||||||
int charIndex = 0;
|
|
||||||
|
|
||||||
int wordWidth = 0;
|
|
||||||
int lineWidth = 0;
|
|
||||||
String::value_type *wordStart = NULL;
|
|
||||||
while ((c = rawText[charIndex++]))
|
|
||||||
{
|
|
||||||
switch(c)
|
|
||||||
{
|
|
||||||
case ' ':
|
|
||||||
lineWidth += Graphics::CharWidth(c);
|
|
||||||
lineWidth += wordWidth;
|
|
||||||
wordWidth = 0;
|
|
||||||
break;
|
|
||||||
case '\n':
|
|
||||||
lineWidth = wordWidth = 0;
|
|
||||||
lines++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
wordWidth += Graphics::CharWidth(c);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (pc == ' ')
|
|
||||||
{
|
|
||||||
wordStart = &rawText[charIndex-2];
|
|
||||||
}
|
|
||||||
if ((c != ' ' || pc == ' ') && lineWidth + wordWidth >= Size.X-(Appearance.Margin.Left+Appearance.Margin.Right))
|
|
||||||
{
|
|
||||||
if (wordStart && *wordStart)
|
|
||||||
{
|
|
||||||
*wordStart = '\n';
|
|
||||||
if (lineWidth != 0)
|
|
||||||
lineWidth = wordWidth;
|
|
||||||
}
|
|
||||||
else if (!wordStart)
|
|
||||||
{
|
|
||||||
rawText[charIndex-1] = '\n';
|
|
||||||
lineWidth = 0;
|
|
||||||
}
|
|
||||||
wordWidth = 0;
|
|
||||||
wordStart = 0;
|
|
||||||
lines++;
|
|
||||||
}
|
|
||||||
pc = c;
|
|
||||||
}
|
|
||||||
if (autoHeight)
|
|
||||||
{
|
|
||||||
Size.Y = lines*12+3;
|
|
||||||
}
|
|
||||||
textLines = rawText;
|
|
||||||
delete[] rawText;
|
|
||||||
/*int currentWidth = 0;
|
|
||||||
char * lastSpace = NULL;
|
|
||||||
char * currentWord = rawText;
|
|
||||||
char * nextSpace;
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
nextSpace = strchr(currentWord+1, ' ');
|
|
||||||
if(nextSpace)
|
|
||||||
nextSpace[0] = 0;
|
|
||||||
int width = Graphics::textwidth(currentWord);
|
|
||||||
if(width+currentWidth >= Size.X-(Appearance.Margin.Left+Appearance.Margin.Right))
|
|
||||||
{
|
|
||||||
currentWidth = width;
|
|
||||||
if(currentWord!=rawText)
|
|
||||||
{
|
|
||||||
currentWord[0] = '\n';
|
|
||||||
lines++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
currentWidth += width;
|
|
||||||
if(nextSpace)
|
|
||||||
nextSpace[0] = ' ';
|
|
||||||
if(!currentWord[0] || !currentWord[1] || !(currentWord = strchr(currentWord+1, ' ')))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(autoHeight)
|
|
||||||
{
|
|
||||||
Size.Y = lines*12;
|
|
||||||
}
|
|
||||||
textLines = std::string(rawText);
|
|
||||||
delete[] rawText;*/
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (autoHeight)
|
|
||||||
{
|
|
||||||
Size.Y = 15;
|
|
||||||
}
|
|
||||||
textLines = "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,17 +82,17 @@ void Label::OnMouseClick(int x, int y, unsigned button)
|
|||||||
{
|
{
|
||||||
if(button == SDL_BUTTON_RIGHT)
|
if(button == SDL_BUTTON_RIGHT)
|
||||||
{
|
{
|
||||||
if(menu)
|
if (menu)
|
||||||
|
{
|
||||||
menu->Show(GetScreenPos() + ui::Point(x, y));
|
menu->Show(GetScreenPos() + ui::Point(x, y));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
selecting = true;
|
selecting = true;
|
||||||
if(multiline)
|
selectionIndex0 = textWrapper.Point2Index(x - textPosition.X, y - textPosition.Y);
|
||||||
selectionIndex0 = Graphics::CharIndexAtPosition(textLines, x-textPosition.X, y-textPosition.Y);
|
selectionIndexL = selectionIndex0;
|
||||||
else
|
selectionIndexH = selectionIndex0;
|
||||||
selectionIndex0 = Graphics::CharIndexAtPosition(text, x-textPosition.X, y-textPosition.Y);
|
|
||||||
selectionIndex1 = selectionIndex0;
|
|
||||||
|
|
||||||
updateSelection();
|
updateSelection();
|
||||||
}
|
}
|
||||||
@ -211,18 +100,10 @@ void Label::OnMouseClick(int x, int y, unsigned button)
|
|||||||
|
|
||||||
void Label::copySelection()
|
void Label::copySelection()
|
||||||
{
|
{
|
||||||
String currentText = text;
|
if (HasSelection())
|
||||||
String copyText;
|
{
|
||||||
|
ClipboardPush(format::CleanString(text.Between(selectionIndexL.raw_index, selectionIndexH.raw_index), false, true, false).ToUtf8());
|
||||||
if (selectionIndex1 > selectionIndex0)
|
}
|
||||||
copyText = currentText.Between(selectionIndex0, selectionIndex1).c_str();
|
|
||||||
else if(selectionIndex0 > selectionIndex1)
|
|
||||||
copyText = currentText.Between(selectionIndex1, selectionIndex0).c_str();
|
|
||||||
else if (!currentText.length())
|
|
||||||
return;
|
|
||||||
else
|
|
||||||
copyText = currentText.c_str();
|
|
||||||
ClipboardPush(format::CleanString(copyText, false, true, false).ToUtf8());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Label::OnMouseUp(int x, int y, unsigned button)
|
void Label::OnMouseUp(int x, int y, unsigned button)
|
||||||
@ -233,7 +114,9 @@ void Label::OnMouseUp(int x, int y, unsigned button)
|
|||||||
void Label::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt)
|
void Label::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt)
|
||||||
{
|
{
|
||||||
if (repeat)
|
if (repeat)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
if (ctrl && scan == SDL_SCANCODE_C)
|
if (ctrl && scan == SDL_SCANCODE_C)
|
||||||
{
|
{
|
||||||
copySelection();
|
copySelection();
|
||||||
@ -247,19 +130,26 @@ void Label::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bo
|
|||||||
|
|
||||||
void Label::OnMouseMoved(int localx, int localy, int dx, int dy)
|
void Label::OnMouseMoved(int localx, int localy, int dx, int dy)
|
||||||
{
|
{
|
||||||
if(selecting)
|
if (selecting)
|
||||||
{
|
{
|
||||||
if(multiline)
|
selectionIndex1 = textWrapper.Point2Index(localx - textPosition.X, localy - textPosition.Y);
|
||||||
selectionIndex1 = Graphics::CharIndexAtPosition(textLines, localx-textPosition.X, localy-textPosition.Y);
|
if (selectionIndex1.raw_index < selectionIndex0.raw_index)
|
||||||
|
{
|
||||||
|
selectionIndexL = selectionIndex1;
|
||||||
|
selectionIndexH = selectionIndex0;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
selectionIndex1 = Graphics::CharIndexAtPosition(text, localx-textPosition.X, localy-textPosition.Y);
|
{
|
||||||
|
selectionIndexL = selectionIndex0;
|
||||||
|
selectionIndexH = selectionIndex1;
|
||||||
|
}
|
||||||
updateSelection();
|
updateSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Label::Tick(float dt)
|
void Label::Tick(float dt)
|
||||||
{
|
{
|
||||||
if(!this->IsFocused() && (selecting || (selectionIndex0 != -1 && selectionIndex1 != -1)))
|
if (!this->IsFocused() && (HasSelection() || selecting))
|
||||||
{
|
{
|
||||||
ClearSelection();
|
ClearSelection();
|
||||||
}
|
}
|
||||||
@ -267,174 +157,123 @@ void Label::Tick(float dt)
|
|||||||
|
|
||||||
int Label::getLowerSelectionBound()
|
int Label::getLowerSelectionBound()
|
||||||
{
|
{
|
||||||
return (selectionIndex0 > selectionIndex1) ? selectionIndex1 : selectionIndex0;
|
return selectionIndexL.raw_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Label::getHigherSelectionBound()
|
int Label::getHigherSelectionBound()
|
||||||
{
|
{
|
||||||
return (selectionIndex0 > selectionIndex1) ? selectionIndex0 : selectionIndex1;
|
return selectionIndexH.raw_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Label::HasSelection()
|
bool Label::HasSelection()
|
||||||
{
|
{
|
||||||
if(selectionIndex0 != -1 && selectionIndex1 != -1 && selectionIndex0 != selectionIndex1)
|
return selectionIndexH.raw_index > selectionIndexL.raw_index;
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Label::ClearSelection()
|
void Label::ClearSelection()
|
||||||
{
|
{
|
||||||
selecting = false;
|
selecting = false;
|
||||||
selectionIndex0 = -1;
|
selectionIndexL = textWrapper.IndexBegin();
|
||||||
selectionIndex1 = -1;
|
selectionIndexH = textWrapper.IndexBegin();
|
||||||
updateSelection();
|
updateSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Label::selectAll()
|
void Label::selectAll()
|
||||||
{
|
{
|
||||||
selectionIndex0 = 0;
|
selectionIndexL = textWrapper.IndexBegin();
|
||||||
selectionIndex1 = text.length();
|
selectionIndexH = textWrapper.IndexEnd();
|
||||||
updateSelection();
|
updateSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Label::updateSelection()
|
void Label::updateSelection()
|
||||||
{
|
{
|
||||||
String currentText;
|
if (selectionIndexL.raw_index < 0) selectionIndexL = textWrapper.IndexBegin();
|
||||||
|
if (selectionIndexL.raw_index > (int)text.length()) selectionIndexL = textWrapper.IndexEnd();
|
||||||
|
if (selectionIndexH.raw_index < 0) selectionIndexH = textWrapper.IndexBegin();
|
||||||
|
if (selectionIndexH.raw_index > (int)text.length()) selectionIndexH = textWrapper.IndexEnd();
|
||||||
|
|
||||||
if (selectionIndex0 < 0) selectionIndex0 = 0;
|
displayTextWithSelection = textWrapper.WrappedText();
|
||||||
if (selectionIndex0 > (int)text.length()) selectionIndex0 = text.length();
|
if (HasSelection())
|
||||||
if (selectionIndex1 < 0) selectionIndex1 = 0;
|
|
||||||
if (selectionIndex1 > (int)text.length()) selectionIndex1 = text.length();
|
|
||||||
|
|
||||||
if(selectionIndex0 == -1 || selectionIndex1 == -1)
|
|
||||||
{
|
{
|
||||||
selectionXH = -1;
|
displayTextWithSelection.Insert(selectionIndexL.wrapped_index, "\x01");
|
||||||
selectionXL = -1;
|
displayTextWithSelection.Insert(selectionIndexH.wrapped_index + 1, "\x01");
|
||||||
|
|
||||||
textFragments = currentText;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(multiline)
|
|
||||||
currentText = textLines;
|
|
||||||
else
|
|
||||||
currentText = text;
|
|
||||||
|
|
||||||
if(selectionIndex1 > selectionIndex0) {
|
|
||||||
selectionLineH = Graphics::PositionAtCharIndex(currentText, selectionIndex1, selectionXH, selectionYH);
|
|
||||||
selectionLineL = Graphics::PositionAtCharIndex(currentText, selectionIndex0, selectionXL, selectionYL);
|
|
||||||
|
|
||||||
textFragments = currentText;
|
|
||||||
//textFragments.insert(selectionIndex1, "\x0E");
|
|
||||||
//textFragments.insert(selectionIndex0, "\x0F\x01\x01\x01");
|
|
||||||
textFragments.Insert(selectionIndex1, "\x01");
|
|
||||||
textFragments.Insert(selectionIndex0, "\x01");
|
|
||||||
} else if(selectionIndex0 > selectionIndex1) {
|
|
||||||
selectionLineH = Graphics::PositionAtCharIndex(currentText, selectionIndex0, selectionXH, selectionYH);
|
|
||||||
selectionLineL = Graphics::PositionAtCharIndex(currentText, selectionIndex1, selectionXL, selectionYL);
|
|
||||||
|
|
||||||
textFragments = currentText;
|
|
||||||
//textFragments.insert(selectionIndex0, "\x0E");
|
|
||||||
//textFragments.insert(selectionIndex1, "\x0F\x01\x01\x01");
|
|
||||||
textFragments.Insert(selectionIndex0, "\x01");
|
|
||||||
textFragments.Insert(selectionIndex1, "\x01");
|
|
||||||
} else {
|
|
||||||
selectionXH = -1;
|
|
||||||
selectionXL = -1;
|
|
||||||
|
|
||||||
textFragments = currentText;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(displayText.length())
|
|
||||||
{
|
|
||||||
displayText = tDisplayText;
|
|
||||||
if(selectionIndex1 > selectionIndex0) {
|
|
||||||
int tSelectionIndex1 = Graphics::CharIndexAtPosition(displayText, selectionXH, selectionYH);
|
|
||||||
int tSelectionIndex0 = Graphics::CharIndexAtPosition(displayText, selectionXL, selectionYL);
|
|
||||||
|
|
||||||
displayText.Insert(tSelectionIndex1, "\x01");
|
|
||||||
displayText.Insert(tSelectionIndex0, "\x01");
|
|
||||||
} else if(selectionIndex0 > selectionIndex1) {
|
|
||||||
int tSelectionIndex0 = Graphics::CharIndexAtPosition(displayText, selectionXH, selectionYH);
|
|
||||||
int tSelectionIndex1 = Graphics::CharIndexAtPosition(displayText, selectionXL, selectionYL);
|
|
||||||
|
|
||||||
displayText.Insert(tSelectionIndex0, "\x01");
|
|
||||||
displayText.Insert(tSelectionIndex1, "\x01");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Label::SetDisplayText(String newText)
|
// void Label::SetDisplayText(String newText)
|
||||||
{
|
// {
|
||||||
ClearSelection();
|
// displayText = newText;
|
||||||
displayText = tDisplayText = newText;
|
// ClearSelection();
|
||||||
}
|
// updateTextWrapper();
|
||||||
|
// updateSelection();
|
||||||
|
// TextPosition(textWrapper.WrappedText());
|
||||||
|
// }
|
||||||
|
|
||||||
void Label::Draw(const Point& screenPos)
|
void Label::Draw(const Point& screenPos)
|
||||||
{
|
{
|
||||||
if(!drawn)
|
if (!drawn)
|
||||||
{
|
{
|
||||||
if(multiline)
|
TextPosition(textWrapper.WrappedText());
|
||||||
{
|
updateTextWrapper();
|
||||||
TextPosition(textLines);
|
updateSelection();
|
||||||
updateMultiline();
|
|
||||||
updateSelection();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
TextPosition(text);
|
|
||||||
drawn = true;
|
drawn = true;
|
||||||
}
|
}
|
||||||
Graphics * g = GetGraphics();
|
Graphics *g = GetGraphics();
|
||||||
|
|
||||||
String cDisplayText = displayText;
|
int selectionXL;
|
||||||
|
int selectionYL;
|
||||||
|
int selectionLineL = textWrapper.Index2Point(selectionIndexL, selectionXL, selectionYL);
|
||||||
|
|
||||||
if(!cDisplayText.length())
|
int selectionXH;
|
||||||
|
int selectionYH;
|
||||||
|
int selectionLineH = textWrapper.Index2Point(selectionIndexH, selectionXH, selectionYH);
|
||||||
|
|
||||||
|
if (HasSelection())
|
||||||
{
|
{
|
||||||
if(selectionXL != -1 && selectionXH != -1)
|
if (selectionLineH == selectionLineL)
|
||||||
{
|
{
|
||||||
cDisplayText = textFragments;
|
g->fillrect(
|
||||||
|
screenPos.X + textPosition.X + selectionXL,
|
||||||
|
screenPos.Y + textPosition.Y + selectionYL - 1,
|
||||||
|
selectionXH - selectionXL,
|
||||||
|
10,
|
||||||
|
255, 255, 255, 255
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(multiline)
|
g->fillrect(
|
||||||
cDisplayText = textLines;
|
screenPos.X + textPosition.X + selectionXL,
|
||||||
else
|
screenPos.Y + textPosition.Y + selectionYL - 1,
|
||||||
cDisplayText = text;
|
textSize.X - selectionXL,
|
||||||
}
|
10,
|
||||||
}
|
255, 255, 255, 255
|
||||||
|
);
|
||||||
if(multiline)
|
for (int i = 1; i < selectionLineH - selectionLineL; ++i)
|
||||||
{
|
|
||||||
if(selectionXL != -1 && selectionXH != -1)
|
|
||||||
{
|
|
||||||
if(selectionLineH - selectionLineL > 0)
|
|
||||||
{
|
{
|
||||||
g->fillrect(screenPos.X+textPosition.X+selectionXL, (screenPos.Y+textPosition.Y-1)+selectionYL, textSize.X-(selectionXL), 10, 255, 255, 255, 255);
|
g->fillrect(
|
||||||
for(int i = 1; i < selectionLineH-selectionLineL; i++)
|
screenPos.X + textPosition.X,
|
||||||
{
|
screenPos.Y + textPosition.Y + selectionYL - 1 + i * 12,
|
||||||
g->fillrect(screenPos.X+textPosition.X, (screenPos.Y+textPosition.Y-1)+selectionYL+(i*12), textSize.X, 10, 255, 255, 255, 255);
|
textSize.X,
|
||||||
}
|
10,
|
||||||
g->fillrect(screenPos.X+textPosition.X, (screenPos.Y+textPosition.Y-1)+selectionYH, selectionXH, 10, 255, 255, 255, 255);
|
255, 255, 255, 255
|
||||||
|
);
|
||||||
} else {
|
|
||||||
g->fillrect(screenPos.X+textPosition.X+selectionXL, screenPos.Y+selectionYL+textPosition.Y-1, selectionXH-(selectionXL), 10, 255, 255, 255, 255);
|
|
||||||
}
|
}
|
||||||
g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, cDisplayText, textColour.Red, textColour.Green, textColour.Blue, 255);
|
g->fillrect(
|
||||||
}
|
screenPos.X + textPosition.X,
|
||||||
else
|
screenPos.Y + textPosition.Y + selectionYH - 1,
|
||||||
{
|
selectionXH,
|
||||||
g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, cDisplayText, textColour.Red, textColour.Green, textColour.Blue, 255);
|
10,
|
||||||
}
|
255, 255, 255, 255
|
||||||
} else {
|
);
|
||||||
if(selectionXL != -1 && selectionXH != -1)
|
|
||||||
{
|
|
||||||
g->fillrect(screenPos.X+textPosition.X+selectionXL, screenPos.Y+textPosition.Y-1, selectionXH-(selectionXL), 10, 255, 255, 255, 255);
|
|
||||||
g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, cDisplayText, textColour.Red, textColour.Green, textColour.Blue, 255);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, cDisplayText, textColour.Red, textColour.Green, textColour.Blue, 255);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g->drawtext(
|
||||||
|
screenPos.X + textPosition.X,
|
||||||
|
screenPos.Y + textPosition.Y,
|
||||||
|
displayTextWithSelection,
|
||||||
|
textColour.Red, textColour.Green, textColour.Blue, 255
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "Component.h"
|
#include "Component.h"
|
||||||
#include "Colour.h"
|
#include "Colour.h"
|
||||||
|
#include "TextWrapper.h"
|
||||||
|
|
||||||
namespace ui
|
namespace ui
|
||||||
{
|
{
|
||||||
@ -12,32 +13,27 @@ namespace ui
|
|||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
String textFragments;
|
String textFragments;
|
||||||
String textLines;
|
String displayTextWithSelection;
|
||||||
String displayText;
|
|
||||||
String tDisplayText;
|
|
||||||
|
|
||||||
String text;
|
String text;
|
||||||
Colour textColour;
|
Colour textColour;
|
||||||
int selectionIndex0;
|
TextWrapper::Index selectionIndex0;
|
||||||
int selectionIndex1;
|
TextWrapper::Index selectionIndex1;
|
||||||
|
TextWrapper::Index selectionIndexL;
|
||||||
int selectionXL;
|
TextWrapper::Index selectionIndexH;
|
||||||
int selectionXH;
|
|
||||||
int selectionYL;
|
|
||||||
int selectionYH;
|
|
||||||
int selectionLineL;
|
|
||||||
int selectionLineH;
|
|
||||||
|
|
||||||
bool multiline;
|
bool multiline;
|
||||||
bool selecting;
|
bool selecting;
|
||||||
bool autoHeight;
|
bool autoHeight;
|
||||||
|
|
||||||
void updateMultiline();
|
void updateTextWrapper();
|
||||||
void updateSelection();
|
void updateSelection();
|
||||||
|
|
||||||
int getLowerSelectionBound();
|
int getLowerSelectionBound();
|
||||||
int getHigherSelectionBound();
|
int getHigherSelectionBound();
|
||||||
|
|
||||||
|
TextWrapper textWrapper;
|
||||||
|
|
||||||
void copySelection();
|
void copySelection();
|
||||||
public:
|
public:
|
||||||
//Label(Window* parent_state, String labelText);
|
//Label(Window* parent_state, String labelText);
|
||||||
@ -48,7 +44,6 @@ namespace ui
|
|||||||
void SetMultiline(bool status);
|
void SetMultiline(bool status);
|
||||||
|
|
||||||
virtual void SetText(String text);
|
virtual void SetText(String text);
|
||||||
virtual void SetDisplayText(String newText);
|
|
||||||
virtual String GetText();
|
virtual String GetText();
|
||||||
|
|
||||||
bool HasSelection();
|
bool HasSelection();
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "gui/interface/Component.h"
|
#include "gui/interface/Component.h"
|
||||||
|
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
|
#include "graphics/FontReader.h"
|
||||||
|
|
||||||
#include "Colour.h"
|
#include "Colour.h"
|
||||||
|
|
||||||
@ -185,17 +186,49 @@ void RichLabel::Draw(const Point& screenPos)
|
|||||||
g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, displayText, textColour.Red, textColour.Green, textColour.Blue, 255);
|
g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, displayText, textColour.Red, textColour.Green, textColour.Blue, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// don't ever use this for anything ever again eww
|
||||||
|
int EndMySuffering(String const &displayText, int positionX, int positionY)
|
||||||
|
{
|
||||||
|
int x=0, y=-2,charIndex=0,cw;
|
||||||
|
auto s = displayText.begin();
|
||||||
|
for (; s != displayText.end(); ++s)
|
||||||
|
{
|
||||||
|
if(*s == '\n') {
|
||||||
|
x = 0;
|
||||||
|
y += FONT_H;
|
||||||
|
charIndex++;
|
||||||
|
continue;
|
||||||
|
} else if(*s == '\b') {
|
||||||
|
if((displayText.end() - s) < 2) break;
|
||||||
|
s++;
|
||||||
|
charIndex+=2;
|
||||||
|
continue;
|
||||||
|
} else if (*s == '\x0F') {
|
||||||
|
if((displayText.end() - s) < 4) break;
|
||||||
|
s+=3;
|
||||||
|
charIndex+=4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cw = FontReader(*s).GetWidth();
|
||||||
|
if ((x+(cw/2) >= positionX && y+FONT_H >= positionY) || y > positionY)
|
||||||
|
break;
|
||||||
|
x += cw;
|
||||||
|
charIndex++;
|
||||||
|
}
|
||||||
|
return charIndex;
|
||||||
|
}
|
||||||
|
|
||||||
void RichLabel::OnMouseClick(int x, int y, unsigned button)
|
void RichLabel::OnMouseClick(int x, int y, unsigned button)
|
||||||
{
|
{
|
||||||
int cursorPosition = Graphics::CharIndexAtPosition(displayText, x-textPosition.X, y-textPosition.Y);
|
int cursorPosition = EndMySuffering(displayText, x-textPosition.X, y-textPosition.Y);
|
||||||
for(std::vector<RichTextRegion>::iterator iter = regions.begin(), end = regions.end(); iter != end; ++iter)
|
for (auto const ®ion : regions)
|
||||||
{
|
{
|
||||||
if((*iter).start <= cursorPosition && (*iter).finish >= cursorPosition)
|
if (region.start <= cursorPosition && region.finish >= cursorPosition)
|
||||||
{
|
{
|
||||||
switch((*iter).action)
|
switch (region.action)
|
||||||
{
|
{
|
||||||
case 'a':
|
case 'a':
|
||||||
Platform::OpenURI((*iter).actionData.ToUtf8());
|
Platform::OpenURI(region.actionData.ToUtf8());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ namespace ui
|
|||||||
|
|
||||||
void Draw(const Point& screenPos) override;
|
void Draw(const Point& screenPos) override;
|
||||||
void OnMouseClick(int x, int y, unsigned button) override;
|
void OnMouseClick(int x, int y, unsigned button) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
String textSource;
|
String textSource;
|
||||||
String displayText;
|
String displayText;
|
||||||
|
260
src/gui/interface/TextWrapper.cpp
Normal file
260
src/gui/interface/TextWrapper.cpp
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
#include "TextWrapper.h"
|
||||||
|
|
||||||
|
#include "graphics/Graphics.h"
|
||||||
|
#include "graphics/FontReader.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
int TextWrapper::Update(String const &text, bool do_wrapping, int max_width)
|
||||||
|
{
|
||||||
|
raw_text_size = (int)text.size();
|
||||||
|
|
||||||
|
struct wrap_record
|
||||||
|
{
|
||||||
|
String::value_type character;
|
||||||
|
int width;
|
||||||
|
std::iterator_traits<String::iterator>::difference_type position;
|
||||||
|
bool wraps;
|
||||||
|
};
|
||||||
|
int line_width = 0;
|
||||||
|
std::vector<wrap_record> records;
|
||||||
|
|
||||||
|
int word_begins_at = -1; // this is a pointer into records; we're not currently in a word
|
||||||
|
int word_width;
|
||||||
|
int lines = 1;
|
||||||
|
for (auto it = text.begin(); it != text.end(); ++it)
|
||||||
|
{
|
||||||
|
auto char_width = Graphics::CharWidth(*it);
|
||||||
|
|
||||||
|
int sequence_length = 0;
|
||||||
|
switch (*it) // set sequence_length if *it starts a sequence that should be forwarded as-is
|
||||||
|
{
|
||||||
|
case '\b': sequence_length = 2; break;
|
||||||
|
case '\x0f': sequence_length = 4; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (*it)
|
||||||
|
{
|
||||||
|
// add more supported spaces here
|
||||||
|
case ' ':
|
||||||
|
if (do_wrapping && line_width + char_width > max_width)
|
||||||
|
{
|
||||||
|
records.push_back(wrap_record{
|
||||||
|
'\n', // character; makes the line wrap when rendered
|
||||||
|
0, // width; fools the clickmap generator into not seeing this newline
|
||||||
|
0, // position; the clickmap generator is fooled, this can be anything
|
||||||
|
true // signal the end of the line to the clickmap generator
|
||||||
|
});
|
||||||
|
line_width = 0;
|
||||||
|
lines += 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// this is in an else branch to make spaces immediately following
|
||||||
|
// newline characters inserted by the wrapper disappear
|
||||||
|
records.push_back(wrap_record{
|
||||||
|
*it,
|
||||||
|
char_width,
|
||||||
|
it - text.begin(),
|
||||||
|
false
|
||||||
|
});
|
||||||
|
line_width += char_width;
|
||||||
|
}
|
||||||
|
word_begins_at = -1; // reset word state
|
||||||
|
break;
|
||||||
|
|
||||||
|
// add more supported linebreaks here
|
||||||
|
case '\n':
|
||||||
|
records.push_back(wrap_record{
|
||||||
|
*it, // character; makes the line wrap when rendered
|
||||||
|
max_width - line_width, // width; make it span all the way to the end
|
||||||
|
it - text.begin(), // position; so the clickmap generator knows where *it is
|
||||||
|
true // signal the end of the line to the clickmap generator
|
||||||
|
});
|
||||||
|
lines += 1;
|
||||||
|
line_width = 0;
|
||||||
|
word_begins_at = -1; // reset word state
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (sequence_length) // *it starts a sequence such as \b? or \x0f???
|
||||||
|
{
|
||||||
|
if (text.end() - it < sequence_length)
|
||||||
|
{
|
||||||
|
it = text.end() - 1;
|
||||||
|
continue; // text is broken, we might as well skip the whole thing
|
||||||
|
}
|
||||||
|
for (auto skip = it + sequence_length; it != skip; ++it)
|
||||||
|
{
|
||||||
|
records.push_back(wrap_record{
|
||||||
|
*it, // character; forward the sequence to the output
|
||||||
|
0, // width; fools the clickmap generator into not seeing this sequence
|
||||||
|
0, // position; the clickmap generator is fooled, this can be anything
|
||||||
|
false // signal nothing to the clickmap generator
|
||||||
|
});
|
||||||
|
}
|
||||||
|
--it;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (word_begins_at == -1)
|
||||||
|
{
|
||||||
|
word_begins_at = records.size();
|
||||||
|
word_width = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (do_wrapping && word_width + char_width > max_width)
|
||||||
|
{
|
||||||
|
records.push_back(wrap_record{
|
||||||
|
'\n', // character; makes the line wrap when rendered
|
||||||
|
0, // width; fools the clickmap generator into not seeing this newline
|
||||||
|
0, // position; the clickmap generator is fooled, this can be anything
|
||||||
|
true // signal the end of the line to the clickmap generator
|
||||||
|
});
|
||||||
|
lines += 1;
|
||||||
|
word_begins_at = records.size();
|
||||||
|
word_width = 0;
|
||||||
|
line_width = 0;
|
||||||
|
}
|
||||||
|
if (do_wrapping && line_width + char_width > max_width)
|
||||||
|
{
|
||||||
|
// if we get in here, we skipped the previous block (since line_width
|
||||||
|
// would have been set to 0 (unless of course (char_width > max_width) which
|
||||||
|
// is dumb)). since (word_width + char_width) <= (line_width + char_width) always
|
||||||
|
// holds and we are in this block, we can be sure that word_width < line_width,
|
||||||
|
// so breaking the line by the preceding space is sure to decrease line_width.
|
||||||
|
records.insert(records.begin() + word_begins_at, wrap_record{
|
||||||
|
'\n', // character; makes the line wrap when rendered
|
||||||
|
0, // width; fools the clickmap generator into not seeing this newline
|
||||||
|
0, // position; the clickmap generator is fooled, this can be anything
|
||||||
|
true // signal the end of the line to the clickmap generator
|
||||||
|
});
|
||||||
|
lines += 1;
|
||||||
|
word_begins_at += 1;
|
||||||
|
line_width = word_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
records.push_back(wrap_record{
|
||||||
|
*it, // character; make the line wrap with *it
|
||||||
|
char_width, // width; make it span all the way to the end
|
||||||
|
it - text.begin(), // position; so the clickmap generator knows where *it is
|
||||||
|
false // signal nothing to the clickmap generator
|
||||||
|
});
|
||||||
|
word_width += char_width;
|
||||||
|
line_width += char_width;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
regions.clear();
|
||||||
|
wrapped_text.clear();
|
||||||
|
int x = 0;
|
||||||
|
int l = 0;
|
||||||
|
int counter = 0;
|
||||||
|
for (auto const &record : records)
|
||||||
|
{
|
||||||
|
regions.push_back(clickmap_region{ x, l * FONT_H, record.width, l + 1, Index{ (int)record.position, counter } });
|
||||||
|
++counter;
|
||||||
|
x += record.width;
|
||||||
|
if (record.wraps)
|
||||||
|
{
|
||||||
|
x = 0;
|
||||||
|
l += 1;
|
||||||
|
}
|
||||||
|
wrapped_text.append(1, record.character);
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapped_lines = lines;
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextWrapper::Index TextWrapper::Point2Index(int x, int y) const
|
||||||
|
{
|
||||||
|
if (y < 0)
|
||||||
|
{
|
||||||
|
return IndexBegin();
|
||||||
|
}
|
||||||
|
if (regions.size())
|
||||||
|
{
|
||||||
|
auto curr = regions.begin();
|
||||||
|
auto end = regions.end();
|
||||||
|
|
||||||
|
auto find_next_nonempty = [end](decltype(end) it) {
|
||||||
|
++it;
|
||||||
|
while (it != end && !it->width)
|
||||||
|
{
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
return it;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto next = find_next_nonempty(curr);
|
||||||
|
while (next != end)
|
||||||
|
{
|
||||||
|
if (curr->pos_y + FONT_H > y)
|
||||||
|
{
|
||||||
|
if (curr->pos_x + curr->width / 2 > x)
|
||||||
|
{
|
||||||
|
// if x is to the left of the vertical bisector of the current region,
|
||||||
|
// return this one; really we should have returned 'the next one' in
|
||||||
|
// the previous iteration
|
||||||
|
return curr->index;
|
||||||
|
}
|
||||||
|
if (curr->pos_x + curr->width / 2 <= x && next->pos_x + next->width / 2 > x)
|
||||||
|
{
|
||||||
|
// if x is to the right of the vertical bisector of the current region
|
||||||
|
// but to the left of the next one's, return the next one
|
||||||
|
return next->index;
|
||||||
|
}
|
||||||
|
if (curr->pos_x + curr->width / 2 <= x && next->pos_y > curr->pos_y)
|
||||||
|
{
|
||||||
|
// nominate the next region if x is to the right of the vertical bisector of
|
||||||
|
// the current region and the next one is on a new line
|
||||||
|
return next->index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
curr = next;
|
||||||
|
next = find_next_nonempty(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return IndexEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
int TextWrapper::Index2Point(Index index, int &x, int &y) const
|
||||||
|
{
|
||||||
|
if (index.wrapped_index < 0 || index.wrapped_index > (int)regions.size() || !regions.size())
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (index.wrapped_index == (int)regions.size())
|
||||||
|
{
|
||||||
|
x = regions[index.wrapped_index - 1].pos_x + regions[index.wrapped_index - 1].width;
|
||||||
|
y = regions[index.wrapped_index - 1].pos_y;
|
||||||
|
return regions[index.wrapped_index - 1].pos_line;
|
||||||
|
}
|
||||||
|
x = regions[index.wrapped_index].pos_x;
|
||||||
|
y = regions[index.wrapped_index].pos_y;
|
||||||
|
return regions[index.wrapped_index].pos_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextWrapper::Index TextWrapper::Raw2Index(int raw_index) const
|
||||||
|
{
|
||||||
|
if (raw_index < 0)
|
||||||
|
{
|
||||||
|
return IndexBegin();
|
||||||
|
}
|
||||||
|
for (auto const ®ion : regions)
|
||||||
|
{
|
||||||
|
if (region.index.raw_index >= raw_index)
|
||||||
|
{
|
||||||
|
return region.index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return IndexEnd();
|
||||||
|
}
|
||||||
|
}
|
51
src/gui/interface/TextWrapper.h
Normal file
51
src/gui/interface/TextWrapper.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/String.h"
|
||||||
|
#include "Point.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
class TextWrapper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Index
|
||||||
|
{
|
||||||
|
int raw_index;
|
||||||
|
int wrapped_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
int raw_text_size;
|
||||||
|
String wrapped_text;
|
||||||
|
struct clickmap_region
|
||||||
|
{
|
||||||
|
int pos_x, pos_y, width, pos_line;
|
||||||
|
Index index;
|
||||||
|
};
|
||||||
|
int wrapped_lines;
|
||||||
|
std::vector<clickmap_region> regions;
|
||||||
|
|
||||||
|
public:
|
||||||
|
int Update(String const &text, bool do_wrapping, int max_width);
|
||||||
|
Index Raw2Index(int raw_index) const;
|
||||||
|
Index Point2Index(int x, int y) const;
|
||||||
|
int Index2Point(Index index, int &x, int &y) const;
|
||||||
|
|
||||||
|
String const &WrappedText() const
|
||||||
|
{
|
||||||
|
return wrapped_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
Index IndexBegin() const
|
||||||
|
{
|
||||||
|
return Index{ 0, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
Index IndexEnd() const
|
||||||
|
{
|
||||||
|
return Index{ raw_text_size, (int)wrapped_text.size() };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -77,7 +77,7 @@ void Textbox::SetText(String newText)
|
|||||||
|
|
||||||
if(cursor)
|
if(cursor)
|
||||||
{
|
{
|
||||||
Graphics::PositionAtCharIndex(multiline?textLines:text, cursor, cursorPositionX, cursorPositionY);
|
textWrapper.Index2Point(textWrapper.Raw2Index(cursor), cursorPositionX, cursorPositionY);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -128,7 +128,7 @@ void Textbox::OnContextMenuAction(int item)
|
|||||||
|
|
||||||
void Textbox::resetCursorPosition()
|
void Textbox::resetCursorPosition()
|
||||||
{
|
{
|
||||||
Graphics::PositionAtCharIndex(multiline?textLines:text, cursor, cursorPositionX, cursorPositionY);
|
textWrapper.Index2Point(textWrapper.Raw2Index(cursor), cursorPositionX, cursorPositionY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Textbox::TabFocus()
|
void Textbox::TabFocus()
|
||||||
@ -169,14 +169,13 @@ void Textbox::cutSelection()
|
|||||||
text = backingText;
|
text = backingText;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(multiline)
|
updateTextWrapper();
|
||||||
updateMultiline();
|
|
||||||
updateSelection();
|
updateSelection();
|
||||||
TextPosition(text);
|
TextPosition(text);
|
||||||
|
|
||||||
if(cursor)
|
if(cursor)
|
||||||
{
|
{
|
||||||
Graphics::PositionAtCharIndex(multiline?textLines:text, cursor, cursorPositionX, cursorPositionY);
|
textWrapper.Index2Point(textWrapper.Raw2Index(cursor), cursorPositionX, cursorPositionY);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -210,12 +209,19 @@ void Textbox::pasteIntoSelection()
|
|||||||
if (!multiline && Graphics::textwidth(backingText + newText) > regionWidth)
|
if (!multiline && Graphics::textwidth(backingText + newText) > regionWidth)
|
||||||
{
|
{
|
||||||
int pLimit = regionWidth - Graphics::textwidth(backingText);
|
int pLimit = regionWidth - Graphics::textwidth(backingText);
|
||||||
int cIndex = Graphics::CharIndexAtPosition(newText, pLimit, 0);
|
int pWidth = 0;
|
||||||
|
auto it = newText.begin();
|
||||||
if (cIndex > 0)
|
while (it != newText.end())
|
||||||
newText = newText.Substr(0, cIndex);
|
{
|
||||||
else
|
auto w = Graphics::CharWidth(*it);
|
||||||
newText = "";
|
if (pWidth + w > pLimit)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pWidth += w;
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
newText = String(newText.begin(), it);
|
||||||
}
|
}
|
||||||
|
|
||||||
backingText.Insert(cursor, newText);
|
backingText.Insert(cursor, newText);
|
||||||
@ -233,17 +239,13 @@ void Textbox::pasteIntoSelection()
|
|||||||
text = backingText;
|
text = backingText;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(multiline)
|
updateTextWrapper();
|
||||||
updateMultiline();
|
|
||||||
updateSelection();
|
updateSelection();
|
||||||
if(multiline)
|
TextPosition(textWrapper.WrappedText());
|
||||||
TextPosition(textLines);
|
|
||||||
else
|
|
||||||
TextPosition(text);
|
|
||||||
|
|
||||||
if(cursor)
|
if(cursor)
|
||||||
{
|
{
|
||||||
Graphics::PositionAtCharIndex(multiline?textLines:text, cursor, cursorPositionX, cursorPositionY);
|
textWrapper.Index2Point(textWrapper.Raw2Index(cursor), cursorPositionX, cursorPositionY);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -455,17 +457,13 @@ void Textbox::AfterTextChange(bool changed)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (multiline)
|
updateTextWrapper();
|
||||||
updateMultiline();
|
|
||||||
updateSelection();
|
updateSelection();
|
||||||
if (multiline)
|
TextPosition(textWrapper.WrappedText());
|
||||||
TextPosition(textLines);
|
|
||||||
else
|
|
||||||
TextPosition(text);
|
|
||||||
|
|
||||||
if(cursor)
|
if(cursor)
|
||||||
{
|
{
|
||||||
Graphics::PositionAtCharIndex(multiline?textLines:text, cursor, cursorPositionX, cursorPositionY);
|
textWrapper.Index2Point(textWrapper.Raw2Index(cursor), cursorPositionX, cursorPositionY);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -515,10 +513,11 @@ void Textbox::OnMouseClick(int x, int y, unsigned button)
|
|||||||
if (button != SDL_BUTTON_RIGHT)
|
if (button != SDL_BUTTON_RIGHT)
|
||||||
{
|
{
|
||||||
mouseDown = true;
|
mouseDown = true;
|
||||||
cursor = Graphics::CharIndexAtPosition(multiline?textLines:text, x-textPosition.X, y-textPosition.Y);
|
auto index = textWrapper.Point2Index(x-textPosition.X, y-textPosition.Y);
|
||||||
|
cursor = index.raw_index;
|
||||||
if(cursor)
|
if(cursor)
|
||||||
{
|
{
|
||||||
Graphics::PositionAtCharIndex(multiline?textLines:text, cursor, cursorPositionX, cursorPositionY);
|
textWrapper.Index2Point(index, cursorPositionX, cursorPositionY);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -538,10 +537,11 @@ void Textbox::OnMouseMoved(int localx, int localy, int dx, int dy)
|
|||||||
{
|
{
|
||||||
if(mouseDown)
|
if(mouseDown)
|
||||||
{
|
{
|
||||||
cursor = Graphics::CharIndexAtPosition(multiline?textLines:text, localx-textPosition.X, localy-textPosition.Y);
|
auto index = textWrapper.Point2Index(localx-textPosition.X, localy-textPosition.Y);
|
||||||
|
cursor = index.raw_index;
|
||||||
if(cursor)
|
if(cursor)
|
||||||
{
|
{
|
||||||
Graphics::PositionAtCharIndex(multiline?textLines:text, cursor, cursorPositionX, cursorPositionY);
|
textWrapper.Index2Point(index, cursorPositionX, cursorPositionY);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user