5d85b449aa
Added setDrawBorder to IGUIEditBox and IGUIStaticText. Added setTextAlignment to GUIEditBox and IGUIStaticText, for text justification and vertical alignment. IGUIEditbox now supports multiple lines using setWordWrap and setMultiLine. IGUIElement now calls getter and setter functions when serializing, in case of getter/setter overrides. git-svn-id: http://svn.code.sf.net/p/irrlicht/code/trunk@745 dfc29bdd-3216-0410-991c-e03cc46cb475
1227 lines
28 KiB
C++
1227 lines
28 KiB
C++
// Copyright (C) 2002-2007 Nikolaus Gebhardt
|
|
// This file is part of the "Irrlicht Engine".
|
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
|
|
|
#include "CGUIEditBox.h"
|
|
#include "IGUISkin.h"
|
|
#include "IGUIEnvironment.h"
|
|
#include "IGUIFont.h"
|
|
#include "IVideoDriver.h"
|
|
#include "rect.h"
|
|
#include "os.h"
|
|
#include "Keycodes.h"
|
|
|
|
/*
|
|
todo:
|
|
optional scrollbars
|
|
ctrl+left/right to select word
|
|
double click/ctrl click: word select + drag to select whole words
|
|
optional dragging selected text
|
|
optional triple click to select line
|
|
*/
|
|
|
|
namespace irr
|
|
{
|
|
namespace gui
|
|
{
|
|
|
|
//! constructor
|
|
CGUIEditBox::CGUIEditBox(const wchar_t* text, bool border, IGUIEnvironment* environment,
|
|
IGUIElement* parent, s32 id,
|
|
const core::rect<s32>& rectangle)
|
|
: IGUIEditBox(environment, parent, id, rectangle), MouseMarking(false),
|
|
Border(border), OverrideColorEnabled(false), MarkBegin(0), MarkEnd(0),
|
|
OverrideColor(video::SColor(101,255,255,255)),
|
|
OverrideFont(0), LastBreakFont(0), CursorPos(0), HScrollPos(0), VScrollPos(0), Max(0),
|
|
WordWrap(false), MultiLine(false), AutoScroll(true),
|
|
HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_CENTER)
|
|
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("CGUIEditBox");
|
|
#endif
|
|
|
|
Text = text;
|
|
|
|
Operator = environment->getOSOperator();
|
|
|
|
if (Operator)
|
|
Operator->grab();
|
|
|
|
breakText();
|
|
}
|
|
|
|
|
|
//! destructor
|
|
CGUIEditBox::~CGUIEditBox()
|
|
{
|
|
if (OverrideFont)
|
|
OverrideFont->drop();
|
|
|
|
if (Operator)
|
|
Operator->drop();
|
|
}
|
|
|
|
|
|
//! Sets another skin independent font.
|
|
void CGUIEditBox::setOverrideFont(IGUIFont* font)
|
|
{
|
|
if (OverrideFont)
|
|
OverrideFont->drop();
|
|
|
|
OverrideFont = font;
|
|
|
|
if (OverrideFont)
|
|
OverrideFont->grab();
|
|
|
|
breakText();
|
|
}
|
|
|
|
|
|
//! Sets another color for the text.
|
|
void CGUIEditBox::setOverrideColor(video::SColor color)
|
|
{
|
|
OverrideColor = color;
|
|
OverrideColorEnabled = true;
|
|
}
|
|
|
|
//! Turns the border on or off
|
|
void CGUIEditBox::setDrawBorder(bool border)
|
|
{
|
|
Border = border;
|
|
}
|
|
|
|
|
|
//! Sets if the text should use the overide color or the color in the gui skin.
|
|
void CGUIEditBox::enableOverrideColor(bool enable)
|
|
{
|
|
OverrideColorEnabled = enable;
|
|
}
|
|
|
|
//! Enables or disables word wrap
|
|
void CGUIEditBox::setWordWrap(bool enable)
|
|
{
|
|
WordWrap = enable;
|
|
breakText();
|
|
}
|
|
|
|
void CGUIEditBox::updateAbsolutePosition()
|
|
{
|
|
IGUIElement::updateAbsolutePosition();
|
|
breakText();
|
|
}
|
|
|
|
//! Checks if word wrap is enabled
|
|
bool CGUIEditBox::isWordWrapEnabled()
|
|
{
|
|
return WordWrap;
|
|
}
|
|
|
|
//! Enables or disables newlines.
|
|
void CGUIEditBox::setMultiLine(bool enable)
|
|
{
|
|
MultiLine = enable;
|
|
}
|
|
|
|
//! Checks if multi line editing is enabled
|
|
bool CGUIEditBox::isMultiLineEnabled()
|
|
{
|
|
return MultiLine;
|
|
}
|
|
|
|
//! Sets text justification
|
|
void CGUIEditBox::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
|
|
{
|
|
HAlign = horizontal;
|
|
VAlign = vertical;
|
|
}
|
|
|
|
//! called if an event happened.
|
|
bool CGUIEditBox::OnEvent(SEvent event)
|
|
{
|
|
switch(event.EventType)
|
|
{
|
|
case EET_GUI_EVENT:
|
|
if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
|
|
{
|
|
if (event.GUIEvent.Caller == (IGUIElement*)this)
|
|
{
|
|
MouseMarking = false;
|
|
MarkBegin = 0;
|
|
MarkEnd = 0;
|
|
}
|
|
}
|
|
break;
|
|
case EET_KEY_INPUT_EVENT:
|
|
if (processKey(event))
|
|
return true;
|
|
break;
|
|
case EET_MOUSE_INPUT_EVENT:
|
|
if (processMouse(event))
|
|
return true;
|
|
break;
|
|
}
|
|
|
|
return Parent ? Parent->OnEvent(event) : false;
|
|
}
|
|
|
|
|
|
bool CGUIEditBox::processKey(const SEvent& event)
|
|
{
|
|
if (!event.KeyInput.PressedDown)
|
|
return false;
|
|
|
|
bool textChanged = false;
|
|
|
|
// control shortcut handling
|
|
|
|
if (event.KeyInput.Control)
|
|
{
|
|
switch(event.KeyInput.Key)
|
|
{
|
|
case KEY_KEY_A:
|
|
// select all
|
|
MarkBegin = 0;
|
|
MarkEnd = Text.size();
|
|
break;
|
|
case KEY_KEY_C:
|
|
// copy to clipboard
|
|
if (Operator && MarkBegin != MarkEnd)
|
|
{
|
|
s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
|
|
s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
|
|
|
|
core::stringc s;
|
|
s = Text.subString(realmbgn, realmend - realmbgn).c_str();
|
|
Operator->copyToClipboard(s.c_str());
|
|
}
|
|
break;
|
|
case KEY_KEY_X:
|
|
// cut to the clipboard
|
|
if (Operator && MarkBegin != MarkEnd)
|
|
{
|
|
s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
|
|
s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
|
|
|
|
// copy
|
|
core::stringc sc;
|
|
sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
|
|
Operator->copyToClipboard(sc.c_str());
|
|
|
|
if (IsEnabled)
|
|
{
|
|
// delete
|
|
core::stringw s;
|
|
s = Text.subString(0, realmbgn);
|
|
s.append( Text.subString(realmend, Text.size()-realmend) );
|
|
Text = s;
|
|
|
|
CursorPos = realmbgn;
|
|
MarkBegin = 0;
|
|
MarkEnd = 0;
|
|
textChanged = true;
|
|
}
|
|
}
|
|
break;
|
|
case KEY_KEY_V:
|
|
if ( !IsEnabled )
|
|
break;
|
|
|
|
// paste from the clipboard
|
|
if (Operator)
|
|
{
|
|
s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
|
|
s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
|
|
|
|
// add new character
|
|
const c8* p = Operator->getTextFromClipboard();
|
|
if (p)
|
|
{
|
|
if (MarkBegin == MarkEnd)
|
|
{
|
|
// insert text
|
|
core::stringw s = Text.subString(0, CursorPos);
|
|
s.append(p);
|
|
s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
|
|
|
|
if (!Max || s.size()<=(u32)Max) // thx to Fish FH for fix
|
|
{
|
|
Text = s;
|
|
s = p;
|
|
CursorPos += s.size();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// replace text
|
|
|
|
core::stringw s = Text.subString(0, realmbgn);
|
|
s.append(p);
|
|
s.append( Text.subString(realmend, Text.size()-realmend) );
|
|
|
|
if (!Max || s.size()<=(u32)Max) // thx to Fish FH for fix
|
|
{
|
|
Text = s;
|
|
s = p;
|
|
CursorPos = realmbgn + s.size();
|
|
}
|
|
}
|
|
}
|
|
|
|
MarkBegin = 0;
|
|
MarkEnd = 0;
|
|
textChanged = true;
|
|
}
|
|
break;
|
|
case KEY_HOME:
|
|
// move/highlight to start of text
|
|
if (event.KeyInput.Shift)
|
|
{
|
|
MarkEnd = CursorPos;
|
|
MarkBegin = 0;
|
|
CursorPos = 0;
|
|
}
|
|
else
|
|
{
|
|
CursorPos = 0;
|
|
MarkBegin = 0;
|
|
MarkEnd = 0;
|
|
}
|
|
break;
|
|
case KEY_END:
|
|
// move/highlight to end of text
|
|
if (event.KeyInput.Shift)
|
|
{
|
|
MarkBegin = CursorPos;
|
|
MarkEnd = Text.size();
|
|
CursorPos = 0;
|
|
}
|
|
else
|
|
{
|
|
CursorPos = Text.size();
|
|
MarkBegin = 0;
|
|
MarkEnd = 0;
|
|
}
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// default keyboard handling
|
|
|
|
if (!event.KeyInput.Control)
|
|
switch(event.KeyInput.Key)
|
|
{
|
|
case KEY_END:
|
|
{
|
|
s32 p = Text.size();
|
|
if (WordWrap || MultiLine)
|
|
{
|
|
p = getLineFromPos(CursorPos);
|
|
p = BrokenTextPositions[p] + (s32)BrokenText[p].size();
|
|
if (p > 0 && (Text[p-1] == L'\r' || Text[p-1] == L'\n' ))
|
|
p-=1;
|
|
}
|
|
|
|
if (event.KeyInput.Shift)
|
|
{
|
|
if (MarkBegin == MarkEnd)
|
|
MarkBegin = CursorPos;
|
|
|
|
MarkEnd = p;
|
|
}
|
|
else
|
|
{
|
|
MarkBegin = 0;
|
|
MarkEnd = 0;
|
|
}
|
|
CursorPos = p;
|
|
BlinkStartTime = os::Timer::getTime();
|
|
}
|
|
break;
|
|
case KEY_HOME:
|
|
{
|
|
|
|
s32 p = 0;
|
|
if (WordWrap || MultiLine)
|
|
{
|
|
p = getLineFromPos(CursorPos);
|
|
p = BrokenTextPositions[p];
|
|
}
|
|
|
|
if (event.KeyInput.Shift)
|
|
{
|
|
if (MarkBegin == MarkEnd)
|
|
MarkBegin = CursorPos;
|
|
MarkEnd = p;
|
|
}
|
|
else
|
|
{
|
|
MarkBegin = 0;
|
|
MarkEnd = 0;
|
|
}
|
|
CursorPos = p;
|
|
BlinkStartTime = os::Timer::getTime();
|
|
}
|
|
break;
|
|
case KEY_RETURN:
|
|
if (MultiLine)
|
|
{
|
|
inputChar(L'\n');
|
|
}
|
|
else
|
|
{
|
|
SEvent e;
|
|
e.EventType = EET_GUI_EVENT;
|
|
e.GUIEvent.Caller = this;
|
|
e.GUIEvent.EventType = EGET_EDITBOX_ENTER;
|
|
Parent->OnEvent(e);
|
|
}
|
|
break;
|
|
case KEY_LEFT:
|
|
|
|
if (event.KeyInput.Shift)
|
|
{
|
|
if (CursorPos > 0)
|
|
{
|
|
if (MarkBegin == MarkEnd)
|
|
MarkBegin = CursorPos;
|
|
|
|
MarkEnd = CursorPos-1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MarkBegin = 0;
|
|
MarkEnd = 0;
|
|
}
|
|
|
|
if (CursorPos > 0) CursorPos--;
|
|
BlinkStartTime = os::Timer::getTime();
|
|
break;
|
|
|
|
case KEY_RIGHT:
|
|
if (event.KeyInput.Shift)
|
|
{
|
|
if (Text.size() > (u32)CursorPos)
|
|
{
|
|
if (MarkBegin == MarkEnd)
|
|
MarkBegin = CursorPos;
|
|
|
|
MarkEnd = CursorPos+1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MarkBegin = 0;
|
|
MarkEnd = 0;
|
|
}
|
|
|
|
if (Text.size() > (u32)CursorPos) CursorPos++;
|
|
BlinkStartTime = os::Timer::getTime();
|
|
break;
|
|
case KEY_UP:
|
|
{
|
|
s32 lineNo = getLineFromPos(CursorPos);
|
|
s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin > MarkEnd ? MarkBegin : MarkEnd);
|
|
if (lineNo > 0)
|
|
{
|
|
s32 cp = CursorPos - BrokenTextPositions[lineNo];
|
|
if ((s32)BrokenText[lineNo-1].size() < cp)
|
|
CursorPos = BrokenTextPositions[lineNo-1] + (s32)BrokenText[lineNo-1].size()-1;
|
|
else
|
|
CursorPos = BrokenTextPositions[lineNo-1] + cp;
|
|
}
|
|
|
|
if (event.KeyInput.Shift)
|
|
{
|
|
MarkBegin = mb;
|
|
MarkEnd = CursorPos;
|
|
}
|
|
else
|
|
{
|
|
MarkBegin = 0;
|
|
MarkEnd = 0;
|
|
}
|
|
|
|
}
|
|
break;
|
|
case KEY_DOWN:
|
|
{
|
|
s32 lineNo = getLineFromPos(CursorPos);
|
|
s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin < MarkEnd ? MarkBegin : MarkEnd);
|
|
if (lineNo < (s32)BrokenText.size()-1)
|
|
{
|
|
s32 cp = CursorPos - BrokenTextPositions[lineNo];
|
|
if ((s32)BrokenText[lineNo+1].size() < cp)
|
|
CursorPos = BrokenTextPositions[lineNo+1] + BrokenText[lineNo+1].size()-1;
|
|
else
|
|
CursorPos = BrokenTextPositions[lineNo+1] + cp;
|
|
}
|
|
|
|
if (event.KeyInput.Shift)
|
|
{
|
|
MarkBegin = mb;
|
|
MarkEnd = CursorPos;
|
|
}
|
|
else
|
|
{
|
|
MarkBegin = 0;
|
|
MarkEnd = 0;
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case KEY_BACK:
|
|
if ( !this->IsEnabled )
|
|
break;
|
|
|
|
if (Text.size())
|
|
{
|
|
core::stringw s;
|
|
|
|
if (MarkBegin != MarkEnd)
|
|
{
|
|
// delete marked text
|
|
s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
|
|
s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
|
|
|
|
s = Text.subString(0, realmbgn);
|
|
s.append( Text.subString(realmend, Text.size()-realmend) );
|
|
Text = s;
|
|
|
|
CursorPos = realmbgn;
|
|
}
|
|
else
|
|
{
|
|
// delete text behind cursor
|
|
if (CursorPos>0)
|
|
s = Text.subString(0, CursorPos-1);
|
|
else
|
|
s = L"";
|
|
s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
|
|
Text = s;
|
|
--CursorPos;
|
|
}
|
|
|
|
if (CursorPos < 0)
|
|
CursorPos = 0;
|
|
BlinkStartTime = os::Timer::getTime();
|
|
MarkBegin = 0;
|
|
MarkEnd = 0;
|
|
textChanged = true;
|
|
}
|
|
break;
|
|
case KEY_DELETE:
|
|
if ( !this->IsEnabled )
|
|
break;
|
|
|
|
if (Text.size() != 0)
|
|
{
|
|
core::stringw s;
|
|
|
|
if (MarkBegin != MarkEnd)
|
|
{
|
|
// delete marked text
|
|
s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
|
|
s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
|
|
|
|
s = Text.subString(0, realmbgn);
|
|
s.append( Text.subString(realmend, Text.size()-realmend) );
|
|
Text = s;
|
|
|
|
CursorPos = realmbgn;
|
|
}
|
|
else
|
|
{
|
|
// delete text before cursor
|
|
s = Text.subString(0, CursorPos);
|
|
s.append( Text.subString(CursorPos+1, Text.size()-CursorPos-1) );
|
|
Text = s;
|
|
}
|
|
|
|
if (CursorPos > (s32)Text.size())
|
|
CursorPos = (s32)Text.size();
|
|
|
|
BlinkStartTime = os::Timer::getTime();
|
|
MarkBegin = 0;
|
|
MarkEnd = 0;
|
|
textChanged = true;
|
|
}
|
|
break;
|
|
|
|
case KEY_ESCAPE:
|
|
case KEY_TAB:
|
|
case KEY_SHIFT:
|
|
case KEY_F1:
|
|
case KEY_F2:
|
|
case KEY_F3:
|
|
case KEY_F4:
|
|
case KEY_F5:
|
|
case KEY_F6:
|
|
case KEY_F7:
|
|
case KEY_F8:
|
|
case KEY_F9:
|
|
case KEY_F10:
|
|
case KEY_F11:
|
|
case KEY_F12:
|
|
case KEY_F13:
|
|
case KEY_F14:
|
|
case KEY_F15:
|
|
case KEY_F16:
|
|
case KEY_F17:
|
|
case KEY_F18:
|
|
case KEY_F19:
|
|
case KEY_F20:
|
|
case KEY_F21:
|
|
case KEY_F22:
|
|
case KEY_F23:
|
|
case KEY_F24:
|
|
// ignore these keys
|
|
return false;
|
|
|
|
default:
|
|
inputChar(event.KeyInput.Char);
|
|
break;
|
|
}
|
|
|
|
// break the text if it has changed
|
|
if (textChanged)
|
|
breakText();
|
|
|
|
calculateScrollPos();
|
|
|
|
return true;
|
|
}
|
|
|
|
//! draws the element and its children
|
|
void CGUIEditBox::draw()
|
|
{
|
|
if (!IsVisible)
|
|
return;
|
|
|
|
bool focus = Environment->hasFocus(this);
|
|
|
|
IGUISkin* skin = Environment->getSkin();
|
|
if (!skin)
|
|
return;
|
|
|
|
irr::video::IVideoDriver* driver = Environment->getVideoDriver();
|
|
|
|
frameRect = AbsoluteRect;
|
|
|
|
// draw the border
|
|
|
|
if (Border)
|
|
{
|
|
skin->draw3DSunkenPane(this, skin->getColor(EGDC_WINDOW),
|
|
false, true, frameRect, &AbsoluteClippingRect);
|
|
|
|
frameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
|
|
frameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
|
|
frameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
|
|
frameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
|
|
}
|
|
core::rect<s32> localClipRect = frameRect;
|
|
localClipRect.clipAgainst(AbsoluteClippingRect);
|
|
|
|
// draw the text
|
|
|
|
IGUIFont* font = OverrideFont;
|
|
if (!OverrideFont)
|
|
font = skin->getFont();
|
|
|
|
s32 cursorLine = 0;
|
|
s32 charcursorpos = 0;
|
|
|
|
if (font)
|
|
{
|
|
if (LastBreakFont != font)
|
|
breakText();
|
|
|
|
// calculate cursor pos
|
|
|
|
core::stringw *txtLine = &Text;
|
|
s32 startPos = 0;
|
|
|
|
core::stringw s, s2;
|
|
|
|
// get mark position
|
|
s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
|
|
s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
|
|
s32 hlineStart = (WordWrap || MultiLine) ? getLineFromPos(realmbgn) : 0;
|
|
s32 hlineCount = (WordWrap || MultiLine) ? getLineFromPos(realmend) - hlineStart + 1 : 1;
|
|
s32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
|
|
|
|
// Save the override color information.
|
|
// Then, alter it if the edit box is disabled.
|
|
bool prevOver = OverrideColorEnabled;
|
|
video::SColor prevColor = OverrideColor;
|
|
|
|
if (Text.size())
|
|
{
|
|
if ( !this->IsEnabled && !OverrideColorEnabled )
|
|
{
|
|
OverrideColorEnabled = true;
|
|
OverrideColor = skin->getColor( EGDC_GRAY_TEXT );
|
|
}
|
|
|
|
for (s32 i=0; i < lineCount; ++i)
|
|
{
|
|
setTextRect(i);
|
|
|
|
// clipping test - don't draw anything outside the visible area
|
|
core::rect<s32> c = localClipRect;
|
|
c.clipAgainst(CurrentTextRect);
|
|
if (!c.isValid())
|
|
continue;
|
|
|
|
// get current line
|
|
txtLine = (WordWrap || MultiLine) ? &BrokenText[i] : &Text;
|
|
startPos = (WordWrap || MultiLine) ? BrokenTextPositions[i] : 0;
|
|
|
|
// draw normal text
|
|
font->draw(txtLine->c_str(), CurrentTextRect,
|
|
OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
|
|
false, true, &localClipRect);
|
|
|
|
// draw mark and marked text
|
|
if (focus && MarkBegin != MarkEnd && i >= hlineStart && i < hlineStart + hlineCount)
|
|
{
|
|
|
|
s32 mbegin = 0, mend = 0;
|
|
s32 lineStartPos = 0, lineEndPos = txtLine->size();
|
|
|
|
if (i == hlineStart)
|
|
{
|
|
// highlight start is on this line
|
|
s = txtLine->subString(0, realmbgn - startPos);
|
|
mbegin = font->getDimension(s.c_str()).Width;
|
|
lineStartPos = realmbgn - startPos;
|
|
}
|
|
if (i == hlineStart + hlineCount - 1)
|
|
{
|
|
// highlight end is on this line
|
|
s2 = txtLine->subString(0, realmend - startPos);
|
|
mend = font->getDimension(s2.c_str()).Width;
|
|
lineEndPos = (s32)s2.size();
|
|
}
|
|
else
|
|
mend = font->getDimension(txtLine->c_str()).Width;
|
|
|
|
CurrentTextRect.UpperLeftCorner.X += mbegin;
|
|
CurrentTextRect.LowerRightCorner.X = CurrentTextRect.UpperLeftCorner.X + mend - mbegin;
|
|
|
|
// draw mark
|
|
driver->draw2DRectangle(skin->getColor(EGDC_HIGH_LIGHT), CurrentTextRect, &localClipRect);
|
|
|
|
// draw marked text
|
|
s = txtLine->subString(lineStartPos, lineEndPos - lineStartPos);
|
|
|
|
if (s.size())
|
|
font->draw(s.c_str(), CurrentTextRect,
|
|
OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_HIGH_LIGHT_TEXT),
|
|
false, true, &localClipRect);
|
|
|
|
}
|
|
}
|
|
|
|
// Return the override color information to its previous settings.
|
|
OverrideColorEnabled = prevOver;
|
|
OverrideColor = prevColor;
|
|
}
|
|
|
|
// draw cursor
|
|
|
|
if (WordWrap || MultiLine)
|
|
{
|
|
cursorLine = getLineFromPos(CursorPos);
|
|
txtLine = &BrokenText[cursorLine];
|
|
startPos = BrokenTextPositions[cursorLine];
|
|
}
|
|
s = txtLine->subString(0,CursorPos-startPos);
|
|
charcursorpos = font->getDimension(s.c_str()).Width;
|
|
|
|
if (focus && (os::Timer::getTime() - BlinkStartTime) % 700 < 350)
|
|
{
|
|
setTextRect(cursorLine);
|
|
CurrentTextRect.UpperLeftCorner.X += charcursorpos;
|
|
|
|
font->draw(L"_", CurrentTextRect,
|
|
OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
|
|
false, true, &localClipRect);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//! Sets the new caption of this element.
|
|
void CGUIEditBox::setText(const wchar_t* text)
|
|
{
|
|
Text = text;
|
|
CursorPos = 0;
|
|
HScrollPos = 0;
|
|
MarkBegin = 0;
|
|
MarkEnd = 0;
|
|
breakText();
|
|
}
|
|
|
|
|
|
//! Sets the maximum amount of characters which may be entered in the box.
|
|
//! \param max: Maximum amount of characters. If 0, the character amount is
|
|
//! infinity.
|
|
void CGUIEditBox::setMax(s32 max)
|
|
{
|
|
Max = max;
|
|
if (Max < 0)
|
|
Max = 0;
|
|
|
|
if (Text.size() > (u32)Max && Max != 0)
|
|
Text = Text.subString(0, Max);
|
|
}
|
|
|
|
|
|
//! Returns maximum amount of characters, previously set by setMax();
|
|
s32 CGUIEditBox::getMax()
|
|
{
|
|
return Max;
|
|
}
|
|
|
|
|
|
bool CGUIEditBox::processMouse(const SEvent& event)
|
|
{
|
|
switch(event.MouseInput.Event)
|
|
{
|
|
case irr::EMIE_LMOUSE_LEFT_UP:
|
|
if (Environment->hasFocus(this))
|
|
{
|
|
CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
|
|
if (MouseMarking)
|
|
MarkEnd = CursorPos;
|
|
MouseMarking = false;
|
|
return true;
|
|
}
|
|
break;
|
|
case irr::EMIE_MOUSE_MOVED:
|
|
{
|
|
if (MouseMarking)
|
|
{
|
|
CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
|
|
MarkEnd = CursorPos;
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
case EMIE_LMOUSE_PRESSED_DOWN:
|
|
if (!Environment->hasFocus(this))
|
|
{
|
|
// get focus
|
|
BlinkStartTime = os::Timer::getTime();
|
|
Environment->setFocus(this);
|
|
MouseMarking = true;
|
|
CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
|
|
MarkBegin = CursorPos;
|
|
MarkEnd = CursorPos;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (!AbsoluteClippingRect.isPointInside(
|
|
core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y)))
|
|
{
|
|
// remove focus
|
|
Environment->removeFocus(this);
|
|
return false;
|
|
}
|
|
|
|
// move cursor
|
|
CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
|
|
|
|
if (!MouseMarking)
|
|
MarkBegin = CursorPos;
|
|
|
|
MouseMarking = true;
|
|
MarkEnd = CursorPos;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
s32 CGUIEditBox::getCursorPos(s32 x, s32 y)
|
|
{
|
|
IGUIFont* font = OverrideFont;
|
|
IGUISkin* skin = Environment->getSkin();
|
|
if (!OverrideFont)
|
|
font = skin->getFont();
|
|
|
|
u32 lineCount = 1;
|
|
|
|
if (WordWrap || MultiLine)
|
|
lineCount = BrokenText.size();
|
|
|
|
core::stringw *txtLine;
|
|
s32 startPos=0;
|
|
x+=3;
|
|
|
|
for (u32 i=0; i < lineCount; ++i)
|
|
{
|
|
setTextRect(i);
|
|
if (i == 0 && y < CurrentTextRect.UpperLeftCorner.Y)
|
|
y = CurrentTextRect.UpperLeftCorner.Y;
|
|
if (i == lineCount - 1 && y > CurrentTextRect.LowerRightCorner.Y )
|
|
y = CurrentTextRect.LowerRightCorner.Y;
|
|
|
|
// is it inside this region?
|
|
if (y >= CurrentTextRect.UpperLeftCorner.Y && y <= CurrentTextRect.LowerRightCorner.Y)
|
|
{
|
|
// we've found the clicked line
|
|
txtLine = (WordWrap || MultiLine) ? &BrokenText[i] : &Text;
|
|
startPos = (WordWrap || MultiLine) ? BrokenTextPositions[i] : 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (x < CurrentTextRect.UpperLeftCorner.X)
|
|
x = CurrentTextRect.UpperLeftCorner.X;
|
|
|
|
s32 idx = font->getCharacterFromPos(Text.c_str(), x - CurrentTextRect.UpperLeftCorner.X);
|
|
|
|
// click was on or left of the line
|
|
if (idx != -1)
|
|
return idx + startPos;
|
|
|
|
// click was off the right edge of the line, go to end.
|
|
return txtLine->size() + startPos;
|
|
}
|
|
|
|
//! Breaks the single text line.
|
|
void CGUIEditBox::breakText()
|
|
{
|
|
IGUISkin* skin = Environment->getSkin();
|
|
|
|
if ((!WordWrap && !MultiLine) || !skin)
|
|
return;
|
|
|
|
BrokenText.clear(); // need to reallocate :/
|
|
BrokenTextPositions.set_used(0);
|
|
|
|
IGUIFont* font = OverrideFont;
|
|
if (!OverrideFont)
|
|
font = skin->getFont();
|
|
|
|
if (!font)
|
|
return;
|
|
|
|
LastBreakFont = font;
|
|
|
|
core::stringw line;
|
|
core::stringw word;
|
|
core::stringw whitespace;
|
|
s32 lastLineStart = 0;
|
|
s32 size = Text.size();
|
|
s32 length = 0;
|
|
s32 elWidth = RelativeRect.getWidth() - 6;
|
|
wchar_t c;
|
|
|
|
for (s32 i=0; i<size; ++i)
|
|
{
|
|
c = Text[i];
|
|
bool lineBreak = false;
|
|
|
|
if (c == L'\r') // Mac or Windows breaks
|
|
{
|
|
lineBreak = true;
|
|
c = ' ';
|
|
if (Text[i+1] == L'\n') // Windows breaks
|
|
{
|
|
Text.erase(i+1);
|
|
--size;
|
|
}
|
|
}
|
|
else if (c == L'\n') // Unix breaks
|
|
{
|
|
lineBreak = true;
|
|
c = ' ';
|
|
}
|
|
|
|
// don't break if we're not a multi-line edit box
|
|
if (!MultiLine)
|
|
lineBreak = false;
|
|
|
|
if (c == L' ' || c == 0 || i == (size-1))
|
|
{
|
|
if (word.size())
|
|
{
|
|
// here comes the next whitespace, look if
|
|
// we can break the last word to the next line.
|
|
s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
|
|
s32 worldlgth = font->getDimension(word.c_str()).Width;
|
|
|
|
if (WordWrap && length + worldlgth + whitelgth > elWidth)
|
|
{
|
|
// break to next line
|
|
length = worldlgth;
|
|
BrokenText.push_back(line);
|
|
BrokenTextPositions.push_back(lastLineStart);
|
|
lastLineStart = i - (s32)word.size();
|
|
line = word;
|
|
}
|
|
else
|
|
{
|
|
// add word to line
|
|
line += whitespace;
|
|
line += word;
|
|
length += whitelgth + worldlgth;
|
|
}
|
|
|
|
word = L"";
|
|
whitespace = L"";
|
|
}
|
|
|
|
whitespace += c;
|
|
|
|
// compute line break
|
|
if (lineBreak)
|
|
{
|
|
line += whitespace;
|
|
line += word;
|
|
BrokenText.push_back(line);
|
|
BrokenTextPositions.push_back(lastLineStart);
|
|
lastLineStart = i+1;
|
|
line = L"";
|
|
word = L"";
|
|
whitespace = L"";
|
|
length = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// yippee this is a word..
|
|
word += c;
|
|
}
|
|
}
|
|
|
|
line += whitespace;
|
|
line += word;
|
|
BrokenText.push_back(line);
|
|
BrokenTextPositions.push_back(lastLineStart);
|
|
}
|
|
|
|
void CGUIEditBox::setTextRect(s32 line)
|
|
{
|
|
core::dimension2di d;
|
|
s32 lineCount = 1;
|
|
|
|
IGUIFont* font = OverrideFont;
|
|
IGUISkin* skin = Environment->getSkin();
|
|
if (!OverrideFont)
|
|
font = skin->getFont();
|
|
|
|
// get text dimension
|
|
if (WordWrap || MultiLine)
|
|
{
|
|
lineCount = BrokenText.size();
|
|
d = font->getDimension(BrokenText[line].c_str());
|
|
}
|
|
else
|
|
{
|
|
d = font->getDimension(Text.c_str());
|
|
d.Height = AbsoluteRect.getHeight();
|
|
}
|
|
d.Height += font->getKerningHeight();
|
|
|
|
// justification
|
|
switch (HAlign)
|
|
{
|
|
case EGUIA_CENTER:
|
|
// align to h centre
|
|
CurrentTextRect.UpperLeftCorner.X = (frameRect.getWidth()/2) - (d.Width/2);
|
|
CurrentTextRect.LowerRightCorner.X = (frameRect.getWidth()/2) + (d.Width/2);
|
|
break;
|
|
case EGUIA_LOWERRIGHT:
|
|
// align to right edge
|
|
CurrentTextRect.UpperLeftCorner.X = frameRect.getWidth() - d.Width;
|
|
CurrentTextRect.LowerRightCorner.X = frameRect.getWidth();
|
|
break;
|
|
default:
|
|
// align to left edge
|
|
CurrentTextRect.UpperLeftCorner.X = 0;
|
|
CurrentTextRect.LowerRightCorner.X = d.Width;
|
|
|
|
}
|
|
|
|
switch (VAlign)
|
|
{
|
|
case EGUIA_CENTER:
|
|
// align to v centre
|
|
CurrentTextRect.UpperLeftCorner.Y =
|
|
(frameRect.getHeight()/2) - (lineCount*d.Height)/2 + d.Height*line;
|
|
break;
|
|
case EGUIA_LOWERRIGHT:
|
|
// align to bottom edge
|
|
CurrentTextRect.UpperLeftCorner.Y =
|
|
frameRect.getHeight() - lineCount*d.Height + d.Height*line;
|
|
break;
|
|
default:
|
|
// align to top edge
|
|
CurrentTextRect.UpperLeftCorner.Y = d.Height*line;
|
|
break;
|
|
}
|
|
|
|
CurrentTextRect.UpperLeftCorner.X -= HScrollPos;
|
|
CurrentTextRect.LowerRightCorner.X -= HScrollPos;
|
|
CurrentTextRect.UpperLeftCorner.Y -= VScrollPos;
|
|
CurrentTextRect.LowerRightCorner.Y = CurrentTextRect.UpperLeftCorner.Y + d.Height;
|
|
|
|
CurrentTextRect += frameRect.UpperLeftCorner;
|
|
|
|
}
|
|
|
|
s32 CGUIEditBox::getLineFromPos(s32 pos)
|
|
{
|
|
if (!WordWrap && !MultiLine)
|
|
return 0;
|
|
|
|
s32 i=0;
|
|
while (i < (s32)BrokenTextPositions.size())
|
|
{
|
|
if (BrokenTextPositions[i] > pos)
|
|
return i-1;
|
|
++i;
|
|
}
|
|
return (s32)BrokenTextPositions.size() - 1;
|
|
}
|
|
|
|
void CGUIEditBox::inputChar(wchar_t c)
|
|
{
|
|
if (!IsEnabled)
|
|
return;
|
|
|
|
if (c != 0)
|
|
{
|
|
if (Text.size() < (u32)Max || Max == 0)
|
|
{
|
|
core::stringw s;
|
|
|
|
if (MarkBegin != MarkEnd)
|
|
{
|
|
// replace marked text
|
|
s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
|
|
s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
|
|
|
|
s = Text.subString(0, realmbgn);
|
|
s.append(c);
|
|
s.append( Text.subString(realmend, Text.size()-realmend) );
|
|
Text = s;
|
|
CursorPos = realmbgn+1;
|
|
}
|
|
else
|
|
{
|
|
// add new character
|
|
s = Text.subString(0, CursorPos);
|
|
s.append(c);
|
|
s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
|
|
Text = s;
|
|
++CursorPos;
|
|
}
|
|
|
|
BlinkStartTime = os::Timer::getTime();
|
|
MarkBegin = 0;
|
|
MarkEnd = 0;
|
|
}
|
|
}
|
|
breakText();
|
|
}
|
|
|
|
|
|
void CGUIEditBox::calculateScrollPos()
|
|
{
|
|
|
|
if (!AutoScroll)
|
|
return;
|
|
|
|
// calculate horizontal scroll position
|
|
s32 cursLine = getLineFromPos(CursorPos);
|
|
setTextRect(cursLine);
|
|
|
|
// don't do horizontal scrolling when wordwrap is enabled.
|
|
if (!WordWrap)
|
|
{
|
|
// get cursor position
|
|
IGUIFont* font = OverrideFont;
|
|
IGUISkin* skin = Environment->getSkin();
|
|
if (!OverrideFont)
|
|
font = skin->getFont();
|
|
|
|
core::stringw *txtLine = MultiLine ? &BrokenText[cursLine] : &Text;
|
|
s32 cPos = MultiLine ? CursorPos - BrokenTextPositions[cursLine] : CursorPos;
|
|
|
|
s32 cStart = CurrentTextRect.UpperLeftCorner.X + HScrollPos +
|
|
font->getDimension(txtLine->subString(0, cPos).c_str()).Width;
|
|
|
|
s32 cEnd = cStart + font->getDimension(L"_ ").Width;
|
|
|
|
if (frameRect.LowerRightCorner.X < cEnd)
|
|
HScrollPos = cEnd - frameRect.LowerRightCorner.X;
|
|
else if (frameRect.UpperLeftCorner.X > cStart)
|
|
HScrollPos = cStart - frameRect.UpperLeftCorner.X;
|
|
else
|
|
HScrollPos = 0;
|
|
|
|
// todo: adjust scrollbar
|
|
|
|
}
|
|
|
|
// vertical scroll position
|
|
if (frameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y + VScrollPos)
|
|
VScrollPos = CurrentTextRect.LowerRightCorner.Y - frameRect.LowerRightCorner.Y + VScrollPos;
|
|
|
|
else if (frameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y + VScrollPos)
|
|
VScrollPos = CurrentTextRect.UpperLeftCorner.Y - frameRect.UpperLeftCorner.Y + VScrollPos;
|
|
else
|
|
VScrollPos = 0;
|
|
|
|
// todo: adjust scrollbar
|
|
}
|
|
|
|
//! Writes attributes of the element.
|
|
void CGUIEditBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0)
|
|
{
|
|
// IGUIEditBox::serializeAttributes(out,options);
|
|
|
|
out->addBool ("OverrideColorEnabled", OverrideColorEnabled );
|
|
out->addColor ("OverrideColor", OverrideColor);
|
|
// out->addFont("OverrideFont",OverrideFont);
|
|
out->addInt ("MaxChars", Max);
|
|
out->addBool ("WordWrap", WordWrap);
|
|
out->addEnum ("HTextAlign", HAlign, GUIAlignmentNames);
|
|
out->addEnum ("VTextAlign", VAlign, GUIAlignmentNames);
|
|
|
|
IGUIEditBox::serializeAttributes(out,options);
|
|
}
|
|
|
|
//! Reads attributes of the element
|
|
void CGUIEditBox::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
|
|
{
|
|
IGUIEditBox::deserializeAttributes(in,options);
|
|
|
|
setOverrideColor(in->getAttributeAsColor("OverrideColor"));
|
|
enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
|
|
setMax(in->getAttributeAsInt("MaxChars"));
|
|
|
|
setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
|
|
(EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
|
|
|
|
// setOverrideFont(in->getAttributeAsFont("OverrideFont"));
|
|
}
|
|
|
|
|
|
} // end namespace gui
|
|
} // end namespace irr
|
|
|