/* * PolyUITextInput.cpp * Poly * * Created by Ivan Safrin on 7/30/08. * Copyright 2008 __MyCompanyName__. All rights reserved. * */ #include "PolyUITextInput.h" using namespace Polycode; UITextInput::UITextInput(bool multiLine, Number width, Number height) : ScreenEntity() { this->multiLine = multiLine; draggingSelection = false; hasSelection = false; doSelectToCaret = false; caretPosition = 0; caretImagePosition = 0; currentLine = NULL; lineOffset = -1; numLines = 0; this->positionMode = ScreenEntity::POSITION_TOPLEFT; Config *conf = CoreServices::getInstance()->getConfig(); if(multiLine) fontName = conf->getStringValue("Polycode", "uiTextInputFontNameMultiLine"); else fontName = conf->getStringValue("Polycode", "uiTextInputFontName"); fontSize = conf->getNumericValue("Polycode", "uiTextInputFontSize"); Number rectHeight = height; if(!multiLine) { rectHeight = fontSize+12; } lineSpacing = conf->getNumericValue("Polycode", "textEditLineSpacing"); Number st = conf->getNumericValue("Polycode", "textBgSkinT"); Number sr = conf->getNumericValue("Polycode", "textBgSkinR"); Number sb = conf->getNumericValue("Polycode", "textBgSkinB"); Number sl = conf->getNumericValue("Polycode", "textBgSkinL"); padding = conf->getNumericValue("Polycode", "textBgSkinPadding"); inputRect = new UIBox(conf->getStringValue("Polycode", "textBgSkin"), st,sr,sb,sl, width+(padding*2), height+(padding*2)); addChild(inputRect); inputRect->addEventListener(this, InputEvent::EVENT_MOUSEDOWN); inputRect->addEventListener(this, InputEvent::EVENT_MOUSEUP); inputRect->addEventListener(this, InputEvent::EVENT_DOUBLECLICK); inputRect->addEventListener(this, InputEvent::EVENT_MOUSEMOVE); inputRect->addEventListener(this, InputEvent::EVENT_MOUSEOVER); inputRect->addEventListener(this, InputEvent::EVENT_MOUSEOUT); selectorRectTop = new ScreenShape(ScreenShape::SHAPE_RECT, 1,1); selectorRectTop->setPositionMode(ScreenEntity::POSITION_TOPLEFT); selectorRectTop->setColor(181.0f/255.0f, 213.0f/255.0f, 255.0f/255.0f, 1); selectorRectTop->visible = false; addChild(selectorRectTop); selectorRectMiddle = new ScreenShape(ScreenShape::SHAPE_RECT, 1,1); selectorRectMiddle->setPositionMode(ScreenEntity::POSITION_TOPLEFT); selectorRectMiddle->setColor(181.0f/255.0f, 213.0f/255.0f, 255.0f/255.0f, 1); selectorRectMiddle->visible = false; addChild(selectorRectMiddle); selectorRectBottom = new ScreenShape(ScreenShape::SHAPE_RECT, 1,1); selectorRectBottom->setPositionMode(ScreenEntity::POSITION_TOPLEFT); selectorRectBottom->setColor(181.0f/255.0f, 213.0f/255.0f, 255.0f/255.0f, 1); selectorRectBottom->visible = false; addChild(selectorRectBottom); insertLine(true); blinkerRect = new ScreenShape(ScreenShape::SHAPE_RECT, 1, fontSize,0,0); blinkerRect->setPositionMode(ScreenEntity::POSITION_TOPLEFT); blinkerRect->setColor(0,0,0,1); addChild(blinkerRect); blinkerRect->visible = false; blinkerRect->setPosition(0,3); blinkTimer = new Timer(true, 500); blinkTimer->addEventListener(this, Timer::EVENT_TRIGGER); focusable = true; this->width = width; this->height = rectHeight; hitwidth = width; hitheight = rectHeight; updateCaretPosition(); } void UITextInput::clearSelection() { hasSelection = false; selectorRectTop->visible = false; selectorRectMiddle->visible = false; selectorRectBottom->visible = false; } void UITextInput::setSelection(int lineStart, int lineEnd, int colStart, int colEnd) { if(lineStart == lineOffset) { selectionLine = lineEnd; } else { selectionLine = lineStart; } if(colStart == caretPosition) { selectionCaretPosition = colEnd; } else { selectionCaretPosition = colStart; } // Logger::log("SET lineStart:%d lineEnd:%d colStart:%d colEnd:%d\n", lineStart, lineEnd, colStart, colEnd); if(lineStart > lineEnd) { int tmp = lineStart; lineStart = lineEnd; lineEnd = tmp; tmp = colStart; colStart = colEnd; colEnd = tmp; } if(colStart > colEnd && lineStart == lineEnd) { int tmp = colStart; colStart = colEnd; colEnd = tmp; } clearSelection(); if(lineStart > lines.size()-1) return; ScreenLabel *topLine = lines[lineStart]; if(colStart+1 > topLine->getText().length()) return; Number fColEnd = colEnd; if(colEnd > topLine->getText().length() || lineStart != lineEnd) fColEnd = topLine->getText().length(); Number topSize, topHeight, topX; selectorRectTop->visible = true; topSize = topLine->getLabel()->getTextWidth(CoreServices::getInstance()->getFontManager()->getFontByName(fontName), topLine->getText().substr(colStart,fColEnd-colStart), fontSize) - 4; topHeight = lineHeight+lineSpacing; if(colStart > 0) { topX = topLine->getLabel()->getTextWidth(CoreServices::getInstance()->getFontManager()->getFontByName(fontName), topLine->getText().substr(0,colStart-1), fontSize); ; } else { topX = 0; } selectorRectTop->setScale(topSize, topHeight); selectorRectTop->setPosition(topX + padding + (topSize/2.0)+ 1, padding + (lineStart * (lineHeight+lineSpacing)) + (topHeight/2.0)); if(lineEnd > lineStart && lineEnd < lines.size()) { ScreenLabel *bottomLine = lines[lineEnd]; selectorRectBottom->visible = true; Number bottomSize = bottomLine->getLabel()->getTextWidth(CoreServices::getInstance()->getFontManager()->getFontByName(fontName), bottomLine->getText().substr(0,colEnd), fontSize) - 4; Number bottomHeight = lineHeight+lineSpacing; selectorRectBottom->setScale(bottomSize, bottomHeight); selectorRectBottom->setPosition(padding + (bottomSize/2.0) + 1, padding + (lineEnd * (lineHeight+lineSpacing)) + (bottomHeight/2.0)); if(lineEnd != lineStart+1) { // need filler selectorRectMiddle->visible = true; Number midSize = this->width-padding; Number midHeight = 0; for(int i=lineStart+1; i < lineEnd;i++) { midHeight += lineHeight+lineSpacing; } selectorRectMiddle->setScale(midSize, midHeight); selectorRectMiddle->setPosition(padding + (midSize/2.0), padding + ((lineStart+1) * (lineHeight+lineSpacing)) + (midHeight/2.0)); } } hasSelection = true; selectionTop = lineStart; selectionBottom = lineEnd; selectionL = colStart; selectionR = colEnd; } void UITextInput::deleteSelection() { if(selectionTop == selectionBottom) { String ctext = lines[selectionTop]->getText(); String newText = ctext.substr(0, selectionL); int rside = selectionR; if(rside > ctext.length()-1) rside = ctext.length() - 1; newText += ctext.substr(rside,ctext.length() - selectionR); lines[selectionTop]->setText(newText); } else { String ctext = lines[selectionTop]->getText(); String newText = ctext.substr(0, selectionL); lines[selectionTop]->setText(newText); ScreenLabel *bottomLine = lines[selectionBottom]; // if whole lines to remove, do it vector linesToRemove; if(selectionBottom > selectionTop + 1) { for(int i=selectionTop+1; i < selectionBottom; i++) { linesToRemove.push_back(lines[i]); } for(int i=0; i < linesToRemove.size(); i++) { removeLine(linesToRemove[i]); } } ctext = bottomLine->getText(); int rside = selectionR; if(rside > ctext.length()-1) rside = ctext.length() - 1; newText = ctext.substr(rside,ctext.length() - selectionR); lineOffset = selectionTop; selectLineFromOffset(); caretPosition = currentLine->getText().length(); updateCaretPosition(); currentLine->setText(currentLine->getText() + newText); removeLine(bottomLine); } clearSelection(); caretPosition = selectionL; updateCaretPosition(); } void UITextInput::Resize(int x, int y) { inputRect->resizeBox(x, y); } int UITextInput::insertLine(bool after) { numLines++; ScreenLabel *newLine = new ScreenLabel(fontName, L"", fontSize, Label::ANTIALIAS_FULL); newLine->setColor(0,0,0,1); lineHeight = newLine->getHeight(); addChild(newLine); if(after) { if(currentLine) { String ctext = currentLine->getText(); String text2 = ctext.substr(caretPosition, ctext.length()-caretPosition); ctext = ctext.substr(0,caretPosition); currentLine->setText(ctext); newLine->setText(text2); caretPosition=0; } currentLine = newLine; vector::iterator it; it = lines.begin(); for(int i=0; i < lineOffset+1; i++) { it++; } lineOffset = lineOffset + 1; lines.insert(it,newLine); restructLines(); } else { // do we even need that? I don't think so. } return 1; } void UITextInput::restructLines() { for(int i=0; i < lines.size(); i++) { lines[i]->setPosition(padding,padding + (i*(lineHeight+lineSpacing)) ,0.0f); } } void UITextInput::setText(String text) { if(!multiLine) { currentLine->setText(text); } else { } // this->text = text; // currentLine->setText(text); } void UITextInput::onLoseFocus() { blinkerRect->visible = false; } String UITextInput::getText() { if(!multiLine) { return currentLine->getText(); } else { String totalText = L""; for(int i=0; i < lines.size(); i++) { totalText += lines[i]->getText(); totalText += L"\n"; } return totalText; } } void UITextInput::updateCaretPosition() { caretImagePosition = padding; if(caretPosition == 0) { caretImagePosition = padding; } else if(caretPosition > currentLine->getText().length()) { caretPosition = currentLine->getText().length(); String caretSubString = currentLine->getText().substr(0,caretPosition); caretImagePosition = currentLine->getLabel()->getTextWidth(CoreServices::getInstance()->getFontManager()->getFontByName(fontName), caretSubString, fontSize); caretImagePosition = caretImagePosition - 4.0f + padding; } else { String caretSubString = currentLine->getText().substr(0,caretPosition); caretImagePosition = currentLine->getLabel()->getTextWidth(CoreServices::getInstance()->getFontManager()->getFontByName(fontName), caretSubString, fontSize); caretImagePosition = caretImagePosition - 4.0f + padding; } blinkerRect->visible = true; blinkTimer->Reset(); if(doSelectToCaret) { doSelectToCaret = false; } } void UITextInput::selectLineFromOffset() { for(int i=0; i < lines.size(); i++) { if(i == lineOffset) { currentLine = lines[i]; return; } } } void UITextInput::dragSelectionTo(Number x, Number y) { x -= padding; y -= padding; int lineOffset = y / (lineHeight+lineSpacing); if(lineOffset > lines.size()-1) lineOffset = lines.size()-1; ScreenLabel *selectToLine = lines[lineOffset]; int len = selectToLine->getText().length(); Number slen; int caretPosition = selectToLine->getLabel()->getTextWidth(CoreServices::getInstance()->getFontManager()->getFontByName(fontName), selectToLine->getText().substr(0,len), fontSize); for(int i=0; i < len; i++) { slen = selectToLine->getLabel()->getTextWidth(CoreServices::getInstance()->getFontManager()->getFontByName(fontName), selectToLine->getText().substr(0,i), fontSize); if(slen > x) { caretPosition = i; break; } } if(x > slen) caretPosition = len; if(caretPosition < 0) caretPosition = 0; setSelection(this->lineOffset, lineOffset, this->caretPosition, caretPosition); } int UITextInput::caretSkipWordBack(int caretLine, int caretPosition) { for(int i=caretPosition; i > 0; i--) { String bit = lines[caretLine]->getText().substr(i,1); wchar_t chr = ((wchar_t*)bit.c_str())[0]; if(((chr > 0 && chr < 48) || (chr > 57 && chr < 65) || (chr > 90 && chr < 97) || (chr > 122 && chr < 127)) && i < caretPosition-1) { return i+1; } } return 0; } int UITextInput::caretSkipWordForward(int caretLine, int caretPosition) { int len = lines[caretLine]->getText().length(); for(int i=caretPosition; i < len; i++) { String bit = lines[caretLine]->getText().substr(i,1); wchar_t chr = ((wchar_t*)bit.c_str())[0]; if(((chr > 0 && chr < 48) || (chr > 57 && chr < 65) || (chr > 90 && chr < 97) || (chr > 122 && chr < 127)) && i > caretPosition) { return i; } } return lines[caretLine]->getText().length(); } void UITextInput::selectWordAtCaret() { int selectStart = 0; int len = currentLine->getText().length(); int selectEnd = len; for(int i=this->caretPosition; i > 0; i--) { String bit = currentLine->getText().substr(i,1); wchar_t chr = ((wchar_t*)bit.c_str())[0]; if( (chr > 0 && chr < 48) || (chr > 57 && chr < 65) || (chr > 90 && chr < 97) || (chr > 122 && chr < 127)) { selectStart = i+1; break; } } for(int i=this->caretPosition; i < len; i++) { String bit = currentLine->getText().substr(i,1); wchar_t chr = ((wchar_t*)bit.c_str())[0]; if( (chr > 0 && chr < 48) || (chr > 57 && chr < 65) || (chr > 90 && chr < 97) || (chr > 122 && chr < 127)) { selectEnd = i; break; } } setSelection(this->lineOffset, this->lineOffset, selectStart, selectEnd); } void UITextInput::setCaretToMouse(Number x, Number y) { clearSelection(); x -= padding; y -= padding; //if(lines.size() > 1) { lineOffset = y / (lineHeight+lineSpacing); if(lineOffset > lines.size()-1) lineOffset = lines.size()-1; selectLineFromOffset(); //} int len = currentLine->getText().length(); Number slen; for(int i=0; i < len; i++) { slen = currentLine->getLabel()->getTextWidth(CoreServices::getInstance()->getFontManager()->getFontByName(fontName), currentLine->getText().substr(0,i), fontSize); if(slen > x) { caretPosition = i; break; } } if(x > slen) caretPosition = len; updateCaretPosition(); } void UITextInput::removeLine(ScreenLabel *line) { for(int i=0;igetText().length()); } void UITextInput::insertText(String text) { vector strings = text.split("\n"); int numLines = lines.size(); for(int i=0; i < strings.size(); i++) { if(i < numLines) { lines[i]->setText(strings[i]); } else { numLines++; ScreenLabel *newLine = new ScreenLabel(fontName, L"", fontSize, Label::ANTIALIAS_FULL); newLine->setColor(0,0,0,1); addChild(newLine); lines.push_back(newLine); newLine->setText(strings[i]); } } restructLines(); } String UITextInput::getSelectionText() { String totalText = L""; if(selectionTop == selectionBottom) { totalText = lines[selectionTop]->getText().substr(selectionL, selectionR-selectionL); return totalText; } else { totalText += lines[selectionTop]->getText().substr(selectionL, lines[selectionTop]->getText().length()-selectionL); totalText += L"\n"; } if(selectionBottom > selectionTop+1) { for(int i=selectionTop+1; i <= selectionBottom; i++) { totalText += lines[i]->getText(); totalText += L"\n"; } } totalText += lines[selectionBottom]->getText().substr(0, selectionL); return totalText; } void UITextInput::onKeyDown(TAUKey key, wchar_t charCode) { if(!hasFocus) return; // Logger::log("UCHAR: %d\n", charCode); CoreInput *input = CoreServices::getInstance()->getCore()->getInput(); if(key == KEY_a && (input->getKeyState(KEY_LSUPER) || input->getKeyState(KEY_RSUPER))) { selectAll(); return; } if(key == KEY_c && (input->getKeyState(KEY_LSUPER) || input->getKeyState(KEY_RSUPER))) { CoreServices::getInstance()->getCore()->copyStringToClipboard(getSelectionText()); return; } if(key == KEY_x && (input->getKeyState(KEY_LSUPER) || input->getKeyState(KEY_RSUPER))) { return; } if(key == KEY_v && (input->getKeyState(KEY_LSUPER) || input->getKeyState(KEY_RSUPER))) { insertText(CoreServices::getInstance()->getCore()->getClipboardString()); return; } if(key == KEY_LEFT) { if(input->getKeyState(KEY_LSUPER) || input->getKeyState(KEY_RSUPER)) { if(input->getKeyState(KEY_LSHIFT) || input->getKeyState(KEY_RSHIFT)) { if(hasSelection) { setSelection(this->lineOffset, selectionLine, this->caretPosition, 0); } else { setSelection(this->lineOffset, this->lineOffset, this->caretPosition, 0); } } else { caretPosition = 0; clearSelection(); updateCaretPosition(); } } else if (input->getKeyState(KEY_LALT) || input->getKeyState(KEY_RALT)) { if(input->getKeyState(KEY_LSHIFT) || input->getKeyState(KEY_RSHIFT)) { if(hasSelection) { setSelection(this->lineOffset, selectionLine, this->caretPosition, caretSkipWordBack(selectionLine, selectionCaretPosition)); } else { setSelection(this->lineOffset, this->lineOffset, this->caretPosition, caretSkipWordBack(this->lineOffset, caretPosition)); } } else { caretPosition = caretSkipWordBack(this->lineOffset,caretPosition); clearSelection(); updateCaretPosition(); } } else { if(caretPosition > 0) { if(input->getKeyState(KEY_LSHIFT) || input->getKeyState(KEY_RSHIFT)) { if(hasSelection) { if(selectionCaretPosition > 0) setSelection(this->lineOffset, selectionLine, this->caretPosition, selectionCaretPosition-1); } else { setSelection(this->lineOffset, this->lineOffset, this->caretPosition, caretPosition-1); } } else { caretPosition--; clearSelection(); updateCaretPosition(); } } } return; } if(key == KEY_RIGHT) { if(caretPosition < currentLine->getText().length()) { if(input->getKeyState(KEY_LSUPER) || input->getKeyState(KEY_RSUPER)) { if(input->getKeyState(KEY_LSHIFT) || input->getKeyState(KEY_RSHIFT)) { if(hasSelection) { setSelection(this->lineOffset, selectionLine, this->caretPosition, lines[selectionLine]->getText().length()); } else { setSelection(this->lineOffset, this->lineOffset, this->caretPosition, currentLine->getText().length()); } } else { caretPosition = currentLine->getText().length(); clearSelection(); } } else if (input->getKeyState(KEY_LALT) || input->getKeyState(KEY_RALT)) { if(input->getKeyState(KEY_LSHIFT) || input->getKeyState(KEY_RSHIFT)) { if(hasSelection) { setSelection(this->lineOffset, selectionLine, this->caretPosition, caretSkipWordForward(selectionLine, selectionCaretPosition)); } else { setSelection(this->lineOffset, this->lineOffset, this->caretPosition, caretSkipWordForward(this->lineOffset, caretPosition)); } } else { caretPosition = caretSkipWordForward(this->lineOffset,caretPosition); clearSelection(); } } else { if(input->getKeyState(KEY_LSHIFT) || input->getKeyState(KEY_RSHIFT)) { if(hasSelection) { setSelection(this->lineOffset, selectionLine, this->caretPosition, selectionCaretPosition+1); } else { setSelection(this->lineOffset, this->lineOffset, this->caretPosition, caretPosition+1); } } else { caretPosition++; clearSelection(); } } updateCaretPosition(); } return; } if(key == KEY_UP) { if(multiLine) { if(input->getKeyState(KEY_LSHIFT) || input->getKeyState(KEY_RSHIFT)) { if(hasSelection) { if(selectionLine > 0) setSelection(this->lineOffset, selectionLine-1, this->caretPosition, selectionCaretPosition); } else { if(this->lineOffset > 0) setSelection(this->lineOffset, this->lineOffset-1, this->caretPosition, caretPosition); } } else { clearSelection(); if(lineOffset > 0) { lineOffset--; selectLineFromOffset(); updateCaretPosition(); } } } blinkerRect->visible = true; return; } if(key == KEY_DOWN) { if(multiLine) { if(input->getKeyState(KEY_LSHIFT) || input->getKeyState(KEY_RSHIFT)) { if(hasSelection) { if(selectionLine < lines.size()-1) setSelection(this->lineOffset, selectionLine+1, this->caretPosition, selectionCaretPosition); } else { if(this->lineOffset < lines.size()-1) setSelection(this->lineOffset, this->lineOffset+1, this->caretPosition, caretPosition); } } else { clearSelection(); if(lineOffset < lines.size()-1) { lineOffset++; selectLineFromOffset(); updateCaretPosition(); } } } blinkerRect->visible = true; return; } if(key == KEY_RETURN) { if(hasSelection) deleteSelection(); if(multiLine) { insertLine(true); updateCaretPosition(); } return; } String ctext = currentLine->getText(); // if(1) { if((charCode > 31 && charCode < 127) || charCode > 127) { if(hasSelection) deleteSelection(); ctext = currentLine->getText(); String text2 = ctext.substr(caretPosition, ctext.length()-caretPosition); ctext = ctext.substr(0,caretPosition); ctext += charCode + text2; caretPosition++; } if(key == KEY_TAB && multiLine) { if(hasSelection) deleteSelection(); ctext = currentLine->getText(); String text2 = ctext.substr(caretPosition, ctext.length()-caretPosition); ctext = ctext.substr(0,caretPosition); ctext += (wchar_t)'\t' + text2; caretPosition++; } if(key == KEY_BACKSPACE) { if(hasSelection) { deleteSelection(); return; } else { ctext = currentLine->getText(); if(caretPosition > 0) { if(ctext.length() > 0) { String text2 = ctext.substr(caretPosition, ctext.length()-caretPosition); ctext = ctext.substr(0,caretPosition-1); ctext += text2; caretPosition--; } } else { if(lineOffset > 0) { ScreenLabel *lineToRemove = currentLine; lineOffset--; selectLineFromOffset(); caretPosition = currentLine->getText().length(); updateCaretPosition(); currentLine->setText(currentLine->getText() + ctext); removeLine(lineToRemove); return; } } } } currentLine->setText(ctext); updateCaretPosition(); } void UITextInput::Update() { if(hasSelection) { blinkerRect->visible = false; } blinkerRect->setPosition(caretImagePosition,currentLine->getPosition2D().y); if(hasFocus) { // inputRect->setStrokeColor(1.0f, 1.0f, 1.0f, 0.25f); } else { blinkerRect->visible = false; // inputRect->setStrokeColor(1.0f, 1.0f, 1.0f, 0.1f); } } UITextInput::~UITextInput() { } void UITextInput::handleEvent(Event *event) { if(event->getDispatcher() == inputRect) { switch(event->getEventCode()) { case InputEvent::EVENT_MOUSEDOWN: if(parentEntity) { ((ScreenEntity*)parentEntity)->focusChild(this); } else { hasFocus = true; } setCaretToMouse(((InputEvent*)event)->mousePosition.x, ((InputEvent*)event)->mousePosition.y); draggingSelection = true; break; case InputEvent::EVENT_MOUSEUP: draggingSelection = false; break; case InputEvent::EVENT_DOUBLECLICK: selectWordAtCaret(); break; case InputEvent::EVENT_MOUSEMOVE: if(draggingSelection) { dragSelectionTo(((InputEvent*)event)->mousePosition.x, ((InputEvent*)event)->mousePosition.y); } break; case InputEvent::EVENT_MOUSEOVER: CoreServices::getInstance()->getCore()->setCursor(CURSOR_TEXT); break; case InputEvent::EVENT_MOUSEOUT: CoreServices::getInstance()->getCore()->setCursor(CURSOR_ARROW); break; } } if(event->getDispatcher() == blinkTimer) { if(hasSelection || draggingSelection) { blinkerRect->visible = false; } else { if(hasFocus) blinkerRect->visible = !blinkerRect->visible; else blinkerRect->visible = false; } } }