/************************************************************************* Handler for when character (printable keys) are typed *************************************************************************/ void MultiLineEditbox::onCharacter(KeyEventArgs& e) { // base class processing Window::onCharacter(e); // only need to take notice if we have focus if (hasInputFocus() && !isReadOnly() && getFont()->isCodepointAvailable(e.codepoint)) { // erase selected text eraseSelectedText(); // if there is room if (d_text.length() - 1 < d_maxTextLen) { d_text.insert(getCaratIndex(), 1, e.codepoint); d_caratPos++; WindowEventArgs args(this); onTextChanged(args); } else { // Trigger text box full event WindowEventArgs args(this); onEditboxFullEvent(args); } } e.handled = true; }
/************************************************************************* Processing to move carat one word left *************************************************************************/ void MultiLineEditbox::handleWordLeft(uint sysKeys) { if (d_caratPos > 0) { setCaratIndex(TextUtils::getWordStartIdx(d_text, getCaratIndex())); } if (sysKeys & Shift) { setSelection(d_caratPos, d_dragAnchorIdx); } else { clearSelection(); } }
/************************************************************************* Processing to move carat one word right *************************************************************************/ void MultiLineEditbox::handleWordRight(UINT sysKeys) { if (d_caratPos < d_text.length() - 1) { setCaratIndex(TextUtils::getNextWordStartIdx(d_text, getCaratIndex())); } if (sysKeys & Shift) { setSelection(d_caratPos, d_dragAnchorIdx); } else { clearSelection(); } }
/************************************************************************* Processing for Delete key *************************************************************************/ void MultiLineEditbox::handleDelete(void) { if (!isReadOnly()) { if (getSelectionLength() != 0) { eraseSelectedText(); } else if (getCaratIndex() < d_text.length() - 1) { d_text.erase(d_caratPos, 1); ensureCaratIsVisible(); WindowEventArgs args(this); onTextChanged(args); } } }
/************************************************************************* Processing to insert a new line / paragraph. *************************************************************************/ void MultiLineEditbox::handleNewLine(uint sysKeys) { if (!isReadOnly()) { // erase selected text eraseSelectedText(); // if there is room if (d_text.length() - 1 < d_maxTextLen) { d_text.insert(getCaratIndex(), 1, 0x0a); d_caratPos++; WindowEventArgs args(this); onTextChanged(args); } } }
/************************************************************************* Handler for when text is programmatically changed. *************************************************************************/ void MultiLineEditbox::onTextChanged(WindowEventArgs& e) { // ensure last character is a new line if ((d_text.length() == 0) || (d_text[d_text.length() - 1] != '\n')) d_text.append(1, '\n'); // base class processing Window::onTextChanged(e); // clear selection clearSelection(); // layout new text formatText(); // layout child windows (scrollbars) since text layout may have changed performChildWindowLayout(); // ensure carat is still within the text setCaratIndex(getCaratIndex()); // ensure carat is visible // NB: this will already have been called at least once, but since we // may have changed the formatting of the text, it needs to be called again. ensureCaratIsVisible(); e.handled = true; }
void FalagardEditbox::populateRenderCache() { const StateImagery* imagery; // draw container etc // get WidgetLookFeel for the assigned look. const WidgetLookFeel& wlf = WidgetLookManager::getSingleton().getWidgetLook(d_lookName); // try and get imagery for the approprite state. imagery = &wlf.getStateImagery(isDisabled() ? "Disabled" : (isReadOnly() ? "ReadOnly" : "Enabled")); // peform the rendering operation for the container. imagery->render(*this); // get destination area for text const Rect textArea(wlf.getNamedArea("TextArea").getArea().getPixelRect(*this)); // // Required preliminary work for text rendering operations // const Font* font = getFont(); // no font == no more rendering if (!font) return; // This will point to the final string to be used for rendering. Useful because it means we // do not have to have duplicate code or be copying d_text for handling masked/unmasked text. String* editText; // Create a 'masked' version of the string if needed. String maskedText; if (isTextMasked()) { maskedText.insert(0, d_text.length(), getMaskCodePoint()); editText = &maskedText; } // text not masked to editText will be the windows d_text String. else { editText = &d_text; } // calculate best position to render text to ensure carat is always visible float textOffset; float extentToCarat = font->getTextExtent(editText->substr(0, getCaratIndex())); // get carat imagery const ImagerySection& caratImagery = wlf.getImagerySection("Carat"); // store carat width float caratWidth = caratImagery.getBoundingRect(*this, textArea).getWidth(); // if box is inactive if (!hasInputFocus()) { textOffset = d_lastTextOffset; } // if carat is to the left of the box else if ((d_lastTextOffset + extentToCarat) < 0) { textOffset = -extentToCarat; } // if carat is off to the right. else if ((d_lastTextOffset + extentToCarat) >= (textArea.getWidth() - caratWidth)) { textOffset = textArea.getWidth() - extentToCarat - caratWidth; } // else carat is already within the box else { textOffset = d_lastTextOffset; } ColourRect colours; float alpha_comp = getEffectiveAlpha(); // // Draw label text // // setup initial rect for text formatting Rect text_part_rect(textArea); // allow for scroll position text_part_rect.d_left += textOffset; // centre text vertically within the defined text area text_part_rect.d_top += (textArea.getHeight() - font->getLineSpacing()) * 0.5f; // draw pre-highlight text String sect = editText->substr(0, getSelectionStartIndex()); colours.setColours(d_normalTextColour); colours.modulateAlpha(alpha_comp); d_renderCache.cacheText(sect, font, LeftAligned, text_part_rect, 0, colours, &textArea); // adjust rect for next section text_part_rect.d_left += font->getTextExtent(sect); // draw highlight text sect = editText->substr(getSelectionStartIndex(), getSelectionLength()); colours.setColours(d_selectTextColour); colours.modulateAlpha(alpha_comp); d_renderCache.cacheText(sect, font, LeftAligned, text_part_rect, 0, colours, &textArea); // adjust rect for next section text_part_rect.d_left += font->getTextExtent(sect); // draw post-highlight text sect = editText->substr(getSelectionEndIndex()); colours.setColours(d_normalTextColour); colours.modulateAlpha(alpha_comp); d_renderCache.cacheText(sect, font, LeftAligned, text_part_rect, 0, colours, &textArea); // remember this for next time. d_lastTextOffset = textOffset; // see if the editbox is active or inactive. bool active = (!isReadOnly()) && hasInputFocus(); // // Render selection imagery. // if (getSelectionLength() != 0) { // calculate required start and end offsets of selection imagery. float selStartOffset = font->getTextExtent(editText->substr(0, getSelectionStartIndex())); float selEndOffset = font->getTextExtent(editText->substr(0, getSelectionEndIndex())); // calculate area for selection imagery. Rect hlarea(textArea); hlarea.d_left += textOffset + selStartOffset; hlarea.d_right = hlarea.d_left + (selEndOffset - selStartOffset); // render the selection imagery. wlf.getStateImagery(active ? "ActiveSelection" : "InactiveSelection").render(*this, hlarea, 0, &textArea); } // // Render carat // if (active) { Rect caratRect(textArea); caratRect.d_left += extentToCarat + textOffset; caratImagery.render(*this, caratRect, 0, 0, &textArea); } }
/************************************************************************* Handler for when non-printable keys are typed. *************************************************************************/ void MultiLineEditbox::onKeyDown(KeyEventArgs& e) { // base class processing Window::onKeyDown(e); if (hasInputFocus() && !isReadOnly()) { WindowEventArgs args(this); switch (e.scancode) { case Key::LeftShift: case Key::RightShift: if (getSelectionLength() == 0) { d_dragAnchorIdx = getCaratIndex(); } break; case Key::Backspace: handleBackspace(); break; case Key::Delete: handleDelete(); break; case Key::Return: case Key::NumpadEnter: handleNewLine(e.sysKeys); break; case Key::ArrowLeft: if (e.sysKeys & Control) { handleWordLeft(e.sysKeys); } else { handleCharLeft(e.sysKeys); } break; case Key::ArrowRight: if (e.sysKeys & Control) { handleWordRight(e.sysKeys); } else { handleCharRight(e.sysKeys); } break; case Key::ArrowUp: handleLineUp(e.sysKeys); break; case Key::ArrowDown: handleLineDown(e.sysKeys); break; case Key::Home: if (e.sysKeys & Control) { handleDocHome(e.sysKeys); } else { handleLineHome(e.sysKeys); } break; case Key::End: if (e.sysKeys & Control) { handleDocEnd(e.sysKeys); } else { handleLineEnd(e.sysKeys); } break; // default case is now to leave event as (possibly) unhandled. default: return; } e.handled = true; } }