651 lines
14 KiB
C++
651 lines
14 KiB
C++
#include <string>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
#include <time.h>
|
|
#include "Config.h"
|
|
#include "interface/Point.h"
|
|
#include "interface/Textbox.h"
|
|
#include "interface/Keys.h"
|
|
#include "ContextMenu.h"
|
|
|
|
using namespace ui;
|
|
|
|
Textbox::Textbox(Point position, Point size, std::string textboxText, std::string textboxPlaceholder):
|
|
Label(position, size, ""),
|
|
actionCallback(NULL),
|
|
masked(false),
|
|
border(true),
|
|
mouseDown(false),
|
|
limit(std::string::npos),
|
|
inputType(All),
|
|
keyDown(0),
|
|
characterDown(0)
|
|
{
|
|
placeHolder = textboxPlaceholder;
|
|
|
|
SetText(textboxText);
|
|
cursor = text.length();
|
|
|
|
menu->RemoveItem(0);
|
|
menu->AddItem(ContextMenuItem("Cut", 1, true));
|
|
menu->AddItem(ContextMenuItem("Copy", 0, true));
|
|
menu->AddItem(ContextMenuItem("Paste", 2, true));
|
|
}
|
|
|
|
Textbox::~Textbox()
|
|
{
|
|
if(actionCallback)
|
|
delete actionCallback;
|
|
}
|
|
|
|
void Textbox::SetHidden(bool hidden)
|
|
{
|
|
menu->RemoveItem(0);
|
|
menu->RemoveItem(1);
|
|
menu->RemoveItem(2);
|
|
menu->AddItem(ContextMenuItem("Cut", 1, !hidden));
|
|
menu->AddItem(ContextMenuItem("Copy", 0, !hidden));
|
|
menu->AddItem(ContextMenuItem("Paste", 2, true));
|
|
|
|
masked = hidden;
|
|
}
|
|
|
|
void Textbox::SetPlaceholder(std::string text)
|
|
{
|
|
placeHolder = text;
|
|
}
|
|
|
|
void Textbox::SetText(std::string newText)
|
|
{
|
|
backingText = newText;
|
|
|
|
if(masked)
|
|
{
|
|
std::string maskedText = std::string(newText);
|
|
std::fill(maskedText.begin(), maskedText.end(), '\x8D');
|
|
Label::SetText(maskedText);
|
|
}
|
|
else
|
|
Label::SetText(newText);
|
|
|
|
cursor = newText.length();
|
|
|
|
if(cursor)
|
|
{
|
|
Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY);
|
|
}
|
|
else
|
|
{
|
|
cursorPositionY = cursorPositionX = 0;
|
|
}
|
|
}
|
|
|
|
Textbox::ValidInput Textbox::GetInputType()
|
|
{
|
|
return inputType;
|
|
}
|
|
|
|
void Textbox::SetInputType(ValidInput input)
|
|
{
|
|
inputType = input;
|
|
}
|
|
|
|
void Textbox::SetLimit(size_t limit)
|
|
{
|
|
this->limit = limit;
|
|
}
|
|
|
|
size_t Textbox::GetLimit()
|
|
{
|
|
return limit;
|
|
}
|
|
|
|
std::string Textbox::GetText()
|
|
{
|
|
return backingText;
|
|
}
|
|
|
|
void Textbox::OnContextMenuAction(int item)
|
|
{
|
|
switch(item)
|
|
{
|
|
case 0:
|
|
copySelection();
|
|
break;
|
|
case 1:
|
|
cutSelection();
|
|
break;
|
|
case 2:
|
|
pasteIntoSelection();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Textbox::cutSelection()
|
|
{
|
|
char * clipboardText;
|
|
clipboardText = clipboard_pull_text();
|
|
std::string newText = std::string(clipboardText);
|
|
free(clipboardText);
|
|
if(HasSelection())
|
|
{
|
|
if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length())
|
|
return;
|
|
clipboard_push_text((char*)backingText.substr(getLowerSelectionBound(), getHigherSelectionBound()-getLowerSelectionBound()).c_str());
|
|
backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound());
|
|
cursor = getLowerSelectionBound();
|
|
}
|
|
else
|
|
{
|
|
clipboard_push_text((char*)backingText.c_str());
|
|
backingText.clear();
|
|
}
|
|
ClearSelection();
|
|
|
|
if(masked)
|
|
{
|
|
std::string maskedText = std::string(backingText);
|
|
std::fill(maskedText.begin(), maskedText.end(), '\x8D');
|
|
Label::SetText(maskedText);
|
|
}
|
|
else
|
|
{
|
|
text = backingText;
|
|
}
|
|
if(actionCallback)
|
|
actionCallback->TextChangedCallback(this);
|
|
|
|
if(multiline)
|
|
updateMultiline();
|
|
updateSelection();
|
|
TextPosition(text);
|
|
|
|
if(cursor)
|
|
{
|
|
Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY);
|
|
}
|
|
else
|
|
{
|
|
cursorPositionY = cursorPositionX = 0;
|
|
}
|
|
}
|
|
|
|
void Textbox::selectAll()
|
|
{
|
|
selectionIndex0 = 0;
|
|
selectionIndex1 = text.length();
|
|
updateSelection();
|
|
}
|
|
|
|
void Textbox::pasteIntoSelection()
|
|
{
|
|
char * clipboardText;
|
|
clipboardText = clipboard_pull_text();
|
|
std::string newText = std::string(clipboardText);
|
|
free(clipboardText);
|
|
if(HasSelection())
|
|
{
|
|
if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length())
|
|
return;
|
|
backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound());
|
|
cursor = getLowerSelectionBound();
|
|
}
|
|
backingText.insert(cursor, newText);
|
|
cursor = cursor+newText.length();
|
|
ClearSelection();
|
|
|
|
if(masked)
|
|
{
|
|
std::string maskedText = std::string(backingText);
|
|
std::fill(maskedText.begin(), maskedText.end(), '\x8D');
|
|
Label::SetText(maskedText);
|
|
}
|
|
else
|
|
{
|
|
text = backingText;
|
|
}
|
|
if(actionCallback)
|
|
actionCallback->TextChangedCallback(this);
|
|
|
|
if(multiline)
|
|
updateMultiline();
|
|
updateSelection();
|
|
TextPosition(text);
|
|
|
|
if(cursor)
|
|
{
|
|
Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY);
|
|
}
|
|
else
|
|
{
|
|
cursorPositionY = cursorPositionX = 0;
|
|
}
|
|
}
|
|
|
|
bool Textbox::CharacterValid(Uint16 character)
|
|
{
|
|
switch(inputType)
|
|
{
|
|
case Number:
|
|
case Numeric:
|
|
return (character >= '0' && character <= '9');
|
|
case All:
|
|
default:
|
|
return (character >= ' ' && character < 127);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Textbox::Tick(float dt)
|
|
{
|
|
Label::Tick(dt);
|
|
if((keyDown || characterDown) && repeatTime <= clock())
|
|
{
|
|
OnVKeyPress(keyDown, characterDown, false, false, false);
|
|
repeatTime = clock()+(0.03 * CLOCKS_PER_SEC);
|
|
}
|
|
}
|
|
|
|
void Textbox::OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt)
|
|
{
|
|
keyDown = 0;
|
|
characterDown = 0;
|
|
}
|
|
|
|
void Textbox::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
|
|
{
|
|
characterDown = character;
|
|
keyDown = key;
|
|
repeatTime = clock()+(0.3 * CLOCKS_PER_SEC);
|
|
OnVKeyPress(key, character, shift, ctrl, alt);
|
|
}
|
|
|
|
void Textbox::OnVKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
|
|
{
|
|
bool changed = false;
|
|
if(ctrl && key == 'c' && !masked)
|
|
{
|
|
copySelection();
|
|
return;
|
|
}
|
|
if(ctrl && key == 'v')
|
|
{
|
|
pasteIntoSelection();
|
|
return;
|
|
}
|
|
if(ctrl && key == 'x' && !masked)
|
|
{
|
|
cutSelection();
|
|
return;
|
|
}
|
|
if(ctrl && key == 'a')
|
|
{
|
|
selectAll();
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
switch(key)
|
|
{
|
|
case KEY_HOME:
|
|
cursor = 0;
|
|
ClearSelection();
|
|
break;
|
|
case KEY_END:
|
|
cursor = backingText.length();
|
|
ClearSelection();
|
|
break;
|
|
case KEY_LEFT:
|
|
if(cursor > 0)
|
|
cursor--;
|
|
ClearSelection();
|
|
break;
|
|
case KEY_RIGHT:
|
|
if(cursor < backingText.length())
|
|
cursor++;
|
|
ClearSelection();
|
|
break;
|
|
case KEY_DELETE:
|
|
if(HasSelection())
|
|
{
|
|
if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length())
|
|
return;
|
|
backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound());
|
|
cursor = getLowerSelectionBound();
|
|
changed = true;
|
|
}
|
|
else if(backingText.length() && cursor < backingText.length())
|
|
{
|
|
if(ctrl)
|
|
backingText.erase(cursor, backingText.length()-cursor);
|
|
else
|
|
backingText.erase(cursor, 1);
|
|
changed = true;
|
|
}
|
|
ClearSelection();
|
|
break;
|
|
case KEY_BACKSPACE:
|
|
if(HasSelection())
|
|
{
|
|
if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length())
|
|
return;
|
|
backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound());
|
|
cursor = getLowerSelectionBound();
|
|
changed = true;
|
|
}
|
|
else if(backingText.length() && cursor > 0)
|
|
{
|
|
if(ctrl)
|
|
{
|
|
backingText.erase(0, cursor);
|
|
cursor = 0;
|
|
}
|
|
else
|
|
{
|
|
backingText.erase(cursor-1, 1);
|
|
cursor--;
|
|
}
|
|
changed = true;
|
|
}
|
|
ClearSelection();
|
|
break;
|
|
}
|
|
if(CharacterValid(character))
|
|
{
|
|
if(HasSelection())
|
|
{
|
|
if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length())
|
|
return;
|
|
backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound());
|
|
cursor = getLowerSelectionBound();
|
|
}
|
|
|
|
if(limit==std::string::npos || backingText.length() < limit)
|
|
{
|
|
if(cursor == backingText.length())
|
|
{
|
|
backingText += character;
|
|
}
|
|
else
|
|
{
|
|
backingText.insert(cursor, 1, (char)character);
|
|
}
|
|
}
|
|
cursor++;
|
|
changed = true;
|
|
ClearSelection();
|
|
}
|
|
}
|
|
catch(std::out_of_range &e)
|
|
{
|
|
cursor = 0;
|
|
backingText = "";
|
|
}
|
|
if(inputType == Number)
|
|
{
|
|
//Remove extra preceding 0's
|
|
while(backingText[0] == '0' && backingText.length()>1)
|
|
backingText.erase(backingText.begin());
|
|
|
|
//If there is no content, replace with 0
|
|
if(!backingText.length())
|
|
backingText = "0";
|
|
}
|
|
if(cursor > backingText.length())
|
|
cursor = backingText.length();
|
|
if(changed)
|
|
{
|
|
if(masked)
|
|
{
|
|
std::string maskedText = std::string(backingText);
|
|
std::fill(maskedText.begin(), maskedText.end(), '\x8D');
|
|
Label::SetText(maskedText);
|
|
}
|
|
else
|
|
{
|
|
text = backingText;
|
|
}
|
|
if(actionCallback)
|
|
actionCallback->TextChangedCallback(this);
|
|
}
|
|
|
|
if(multiline)
|
|
updateMultiline();
|
|
updateSelection();
|
|
TextPosition(text);
|
|
|
|
if(cursor)
|
|
{
|
|
Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY);
|
|
}
|
|
else
|
|
{
|
|
cursorPositionY = cursorPositionX = 0;
|
|
}
|
|
}
|
|
|
|
void Textbox::OnMouseClick(int x, int y, unsigned button)
|
|
{
|
|
|
|
if(button != BUTTON_RIGHT)
|
|
{
|
|
mouseDown = true;
|
|
cursor = Graphics::CharIndexAtPosition(multiline?((char*)textLines.c_str()):((char*)text.c_str()), x-textPosition.X, y-textPosition.Y);
|
|
if(cursor)
|
|
{
|
|
Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY);
|
|
}
|
|
else
|
|
{
|
|
cursorPositionY = cursorPositionX = 0;
|
|
}
|
|
}
|
|
Label::OnMouseClick(x, y, button);
|
|
}
|
|
|
|
void Textbox::OnMouseUp(int x, int y, unsigned button)
|
|
{
|
|
mouseDown = false;
|
|
Label::OnMouseUp(x, y, button);
|
|
}
|
|
|
|
void Textbox::OnMouseMoved(int localx, int localy, int dx, int dy)
|
|
{
|
|
if(mouseDown)
|
|
{
|
|
cursor = Graphics::CharIndexAtPosition(multiline?((char*)textLines.c_str()):((char*)text.c_str()), localx-textPosition.X, localy-textPosition.Y);
|
|
if(cursor)
|
|
{
|
|
Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY);
|
|
}
|
|
else
|
|
{
|
|
cursorPositionY = cursorPositionX = 0;
|
|
}
|
|
}
|
|
Label::OnMouseMoved(localx, localy, dx, dy);
|
|
}
|
|
|
|
void Textbox::Draw(const Point& screenPos)
|
|
{
|
|
Label::Draw(screenPos);
|
|
|
|
Graphics * g = Engine::Ref().g;
|
|
if(IsFocused())
|
|
{
|
|
if(border) g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 255, 255, 255, 255);
|
|
g->draw_line(screenPos.X+textPosition.X+cursorPositionX, screenPos.Y-2+textPosition.Y+cursorPositionY, screenPos.X+textPosition.X+cursorPositionX, screenPos.Y+9+textPosition.Y+cursorPositionY, 255, 255, 255, 255);
|
|
}
|
|
else
|
|
{
|
|
if(!text.length())
|
|
{
|
|
g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, placeHolder, textColour.Red, textColour.Green, textColour.Blue, 170);
|
|
}
|
|
if(border) g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 160, 160, 160, 255);
|
|
}
|
|
if(Appearance.icon)
|
|
g->draw_icon(screenPos.X+iconPosition.X, screenPos.Y+iconPosition.Y, Appearance.icon);
|
|
}
|
|
|
|
/*
|
|
Textbox::Textbox(Point position, Point size, std::string textboxText):
|
|
Component(position, size),
|
|
text(textboxText),
|
|
actionCallback(NULL),
|
|
masked(false),
|
|
border(true)
|
|
{
|
|
SetText(textboxText);
|
|
cursor = text.length();
|
|
}
|
|
|
|
Textbox::~Textbox()
|
|
{
|
|
if(actionCallback)
|
|
delete actionCallback;
|
|
}
|
|
|
|
void Textbox::TextPosition()
|
|
{
|
|
if(cursor)
|
|
{
|
|
cursorPosition = Graphics::textnwidth((char *)displayText.c_str(), cursor);
|
|
}
|
|
else
|
|
{
|
|
cursorPosition = 0;
|
|
}
|
|
Component::TextPosition(displayText);
|
|
}
|
|
|
|
void Textbox::SetText(std::string text)
|
|
{
|
|
cursor = text.length();
|
|
this->text = text;
|
|
this->displayText = text;
|
|
TextPosition();
|
|
}
|
|
|
|
|
|
void Textbox::SetDisplayText(std::string text)
|
|
{
|
|
displayText = text;
|
|
TextPosition();
|
|
}
|
|
|
|
std::string Textbox::GetText()
|
|
{
|
|
return text;
|
|
}
|
|
|
|
void Textbox::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
|
|
{
|
|
bool changed = false;
|
|
try
|
|
{
|
|
switch(key)
|
|
{
|
|
case KEY_HOME:
|
|
cursor = 0;
|
|
break;
|
|
case KEY_END:
|
|
cursor = text.length();
|
|
break;
|
|
case KEY_LEFT:
|
|
if(cursor > 0)
|
|
cursor--;
|
|
break;
|
|
case KEY_RIGHT:
|
|
if(cursor < text.length())
|
|
cursor++;
|
|
break;
|
|
case KEY_DELETE:
|
|
if(text.length() && cursor < text.length())
|
|
{
|
|
if(ctrl)
|
|
text.erase(cursor, text.length()-cursor);
|
|
else
|
|
text.erase(cursor, 1);
|
|
changed = true;
|
|
}
|
|
break;
|
|
case KEY_BACKSPACE:
|
|
if(text.length() && cursor > 0)
|
|
{
|
|
if(ctrl)
|
|
{
|
|
text.erase(0, cursor);
|
|
cursor = 0;
|
|
}
|
|
else
|
|
{
|
|
text.erase(cursor-1, 1);
|
|
cursor--;
|
|
}
|
|
changed = true;
|
|
}
|
|
break;
|
|
}
|
|
if(character >= ' ' && character < 127)
|
|
{
|
|
if(cursor == text.length())
|
|
{
|
|
text += character;
|
|
}
|
|
else
|
|
{
|
|
text.insert(cursor, 1, (char)character);
|
|
}
|
|
cursor++;
|
|
changed = true;
|
|
}
|
|
}
|
|
catch(std::out_of_range &e)
|
|
{
|
|
cursor = 0;
|
|
text = "";
|
|
}
|
|
if(changed)
|
|
{
|
|
if(masked)
|
|
{
|
|
char * tempText = new char[text.length()+1];
|
|
std::fill(tempText, tempText+text.length(), 0x8d);
|
|
tempText[text.length()] = 0;
|
|
displayText = std::string(tempText);
|
|
delete tempText;
|
|
}
|
|
else
|
|
{
|
|
displayText = text;
|
|
}
|
|
if(actionCallback)
|
|
actionCallback->TextChangedCallback(this);
|
|
}
|
|
TextPosition();
|
|
}
|
|
|
|
void Textbox::Draw(const Point& screenPos)
|
|
{
|
|
if(!drawn)
|
|
{
|
|
TextPosition();
|
|
drawn = true;
|
|
}
|
|
Graphics * g = Engine::Ref().g;
|
|
if(IsFocused())
|
|
{
|
|
if(border) g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 255, 255, 255, 255);
|
|
g->draw_line(screenPos.X+textPosition.X+cursorPosition, screenPos.Y+3, screenPos.X+textPosition.X+cursorPosition, screenPos.Y+12, 255, 255, 255, XRES+BARSIZE);
|
|
}
|
|
else
|
|
{
|
|
if(border) g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 160, 160, 160, 255);
|
|
}
|
|
g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, displayText, 255, 255, 255, 255);
|
|
if(Appearance.icon)
|
|
g->draw_icon(screenPos.X+iconPosition.X, screenPos.Y+iconPosition.Y, Appearance.icon);
|
|
}*/
|