nuttx-apps/graphics/nxwidgets/src/cmultilinetextbox.cxx
Xiang Xiao 893387b2c5 Fix the minor style issue
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2022-10-16 19:07:16 +02:00

1271 lines
30 KiB
C++

/****************************************************************************
* apps/graphics/nxwidgets/src/cmultilinetextbox.cxx
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************
*
* Portions of this package derive from Woopsi (http://woopsi.org/) and
* portions are original efforts. It is difficult to determine at this
* point what parts are original efforts and which parts derive from Woopsi.
* However, in any event, the work of Antony Dzeryn will be acknowledged
* in most NxWidget files. Thanks Antony!
*
* Copyright (c) 2007-2011, Antony Dzeryn
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the names "Woopsi", "Simian Zombie" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Antony Dzeryn ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Antony Dzeryn BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <stdbool.h>
#include "graphics/nxwidgets/cwidgetcontrol.hxx"
#include "graphics/nxwidgets/cnxfont.hxx"
#include "graphics/nxwidgets/ctext.hxx"
#include "graphics/nxwidgets/cgraphicsport.hxx"
#include "graphics/nxwidgets/singletons.hxx"
#include "graphics/nxwidgets/cstringiterator.hxx"
#include "graphics/nxwidgets/cnxtimer.hxx"
#include "graphics/nxwidgets/cmultilinetextbox.hxx"
/****************************************************************************
* Pre-Processor Definitions
****************************************************************************/
/****************************************************************************
* Method Implementations
****************************************************************************/
using namespace NXWidgets;
/**
* Constructor.
*
* @param pWidgetControl The widget control for the display.
* @param x The x coordinate of the text box, relative to its parent.
* @param y The y coordinate of the text box, relative to its parent.
* @param width The width of the textbox.
* @param height The height of the textbox.
* @param text Pointer to a string to display in the textbox.
* @param flags Standard widget flag options.
* @param maxRows The maximum number of rows the textbox can track. Adding
* text beyond this number will cause rows at the start of the text to be
* forgotten; text is essentially stored as a queue, and adding to the back
* of a full queue causes the front items to be popped off. Setting this to
* 0 will make the textbox track only the visible rows.
* @param style The style that the widget should use. If this is not
* specified, the widget will use the values stored in the global
* g_defaultWidgetStyle object. The widget will copy the properties of
* the style into its own internal style object.
*/
CMultiLineTextBox::CMultiLineTextBox(CWidgetControl *pWidgetControl,
nxgl_coord_t x, nxgl_coord_t y,
nxgl_coord_t width, nxgl_coord_t height,
const CNxString &text, uint32_t flags,
nxgl_coord_t maxRows, CWidgetStyle *style)
: CScrollingPanel(pWidgetControl, x, y, width, height, flags, style)
{
m_hAlignment = TEXT_ALIGNMENT_HORIZ_CENTER;
m_vAlignment = TEXT_ALIGNMENT_VERT_CENTER;
m_topRow = 0;
// The border is one line thick
m_borderSize.top = 1;
m_borderSize.right = 1;
m_borderSize.bottom = 1;
m_borderSize.left = 1;
CRect rect;
getClientRect(rect);
m_text = new CText(getFont(), "", rect.getWidth());
m_canvasWidth = rect.getWidth();
m_flags.draggable = true;
m_flags.doubleClickable = true;
m_maxRows = maxRows;
calculateVisibleRows();
// Set maximum rows if value not set
if (m_maxRows == 0)
{
m_maxRows = m_visibleRows + 1;
}
m_cursorPos = 0;
m_showCursor = SHOW_CURSOR_NEVER;
m_wrapCursor = false;
setText(text);
}
/**
* Set the horizontal alignment of text within the textbox.
*
* @param alignment The horizontal position of the text.
*/
void CMultiLineTextBox::setTextAlignmentHoriz(TextAlignmentHoriz alignment)
{
m_hAlignment = alignment;
redraw();
}
/**
* Set the vertical alignment of text within the textbox.
*
* @param alignment The vertical position of the text.
*/
void CMultiLineTextBox::setTextAlignmentVert(TextAlignmentVert alignment)
{
m_vAlignment = alignment;
redraw();
}
/**
* Returns the number of "pages" that the text spans. A page
* is defined as the amount of text that can be displayed within
* the textbox at one time.
*
* @return The page count.
*/
const int CMultiLineTextBox::getPageCount(void) const
{
if (m_visibleRows > 0)
{
return (m_text->getLineCount() / m_visibleRows) + 1;
}
else
{
return 1;
}
}
/**
* Returns the current page.
*
* @return The current page.
* @see getPageCount().
*/
const int CMultiLineTextBox::getCurrentPage(void) const
{
// Calculate the top line of text
int topRow = -m_canvasY / m_text->getLineHeight();
// Return the page on which the top row falls
if (m_visibleRows > 0)
{
return topRow / m_visibleRows;
}
else
{
return 1;
}
}
/**
* Set the text displayed in the textbox.
*
* @param text String to display.
*/
void CMultiLineTextBox::setText(const CNxString &text)
{
bool drawingEnabled = m_flags.drawingEnabled;
disableDrawing();
m_text->setText(text);
cullTopLines();
limitCanvasHeight();
jumpToTextBottom();
if (drawingEnabled)
{
enableDrawing();
}
redraw();
m_widgetEventHandlers->raiseValueChangeEvent();
}
/**
* Append new text to the end of the current text
* displayed in the textbox.
*
* @param text String to append.
*/
void CMultiLineTextBox::appendText(const CNxString &text)
{
bool drawingEnabled = m_flags.drawingEnabled;
disableDrawing();
m_text->append(text);
cullTopLines();
limitCanvasHeight();
jumpToTextBottom();
if (drawingEnabled)
{
enableDrawing();
}
redraw();
m_widgetEventHandlers->raiseValueChangeEvent();
}
/**
* Remove all characters from the string from the start index onwards.
*
* @param startIndex Index to remove from.
*/
void CMultiLineTextBox::removeText(const unsigned int startIndex)
{
removeText(startIndex, m_text->getLength() - startIndex);
}
/**
* Remove specified number of characters from the string from the
* start index onwards.
*
* @param startIndex Index to remove from.
* @param count Number of characters to remove.
*/
void CMultiLineTextBox::removeText(const unsigned int startIndex,
const unsigned int count)
{
bool drawingEnabled = m_flags.drawingEnabled;
disableDrawing();
m_text->remove(startIndex, count);
limitCanvasHeight();
limitCanvasY();
moveCursorToPosition(startIndex);
if (drawingEnabled)
{
enableDrawing();
}
redraw();
m_widgetEventHandlers->raiseValueChangeEvent();
}
/**
* Set the font used in the textbox.
*
* @param font Pointer to the new font.
*/
void CMultiLineTextBox::setFont(CNxFont *font)
{
bool drawingEnabled = m_flags.drawingEnabled;
disableDrawing();
m_style.font = font;
m_text->setFont(font);
cullTopLines();
limitCanvasHeight();
limitCanvasY();
if (drawingEnabled)
{
enableDrawing();
}
redraw();
m_widgetEventHandlers->raiseValueChangeEvent();
}
/**
* Get the length of the text string.
*
* @return The length of the text string.
*/
const int CMultiLineTextBox::getTextLength(void) const
{
return m_text->getLength();
}
/**
* Sets the cursor display mode.
*
* @param cursorMode Determines cursor display mode
*/
void CMultiLineTextBox::showCursor(EShowCursor cursorMode)
{
if (m_showCursor != cursorMode)
{
m_showCursor = (uint8_t)cursorMode;
redraw();
}
}
/**
* Move the cursor to the text position specified. 0 indicates the start
* of the string. If position is greater than the length of the string,
* the cursor is moved to the end of the string.
*
* @param position The new cursor position.
*/
void CMultiLineTextBox::moveCursorToPosition(const int position)
{
CGraphicsPort *port = m_widgetControl->getGraphicsPort();
// Erase existing cursor
drawCursor(port);
// Force position to within confines of string
if (position < 0)
{
m_cursorPos = 0;
}
else
{
int len = (int)m_text->getLength();
m_cursorPos = len > position ? position : len;
}
// Draw cursor in new position
drawCursor(port);
}
/**
* Insert text at the specified index.
*
* @param text The text to insert.
* @param index Index at which to insert the text.
*/
void CMultiLineTextBox::insertText(const CNxString &text,
const unsigned int index)
{
bool drawingEnabled = m_flags.drawingEnabled;
disableDrawing();
m_text->insert(text, index);
cullTopLines();
limitCanvasHeight();
moveCursorToPosition(index + text.getLength());
if (drawingEnabled)
{
enableDrawing();
}
redraw();
m_widgetEventHandlers->raiseValueChangeEvent();
}
/**
* Insert text at the current cursor position.
*
* @param text The text to insert.
*/
void CMultiLineTextBox::insertTextAtCursor(const CNxString &text)
{
insertText(text, getCursorPosition());
}
/**
* Handle a keyboard press event.
*
* @param e The event data.
*/
void CMultiLineTextBox::handleKeyPressEvent(const CWidgetEventArgs &e)
{
nxwidget_char_t key = e.getKey();
if (key == KEY_CODE_BACKSPACE)
{
// Delete character in front of cursor
if (m_cursorPos > 0)
{
removeText(m_cursorPos - 1, 1);
}
}
else if (key != KEY_CODE_NONE)
{
// Not modifier; append value
insertTextAtCursor(key);
}
}
/**
* Get the coordinates of the cursor relative to the text.
*
* @param x Will be populated with the x coordinate of the cursor.
* @param y Will be populated with the y coordinate of the cursor.
*/
void CMultiLineTextBox::getCursorCoordinates(nxgl_coord_t &x, nxgl_coord_t &y) const
{
unsigned int cursorRow = 0;
x = 0;
y = 0;
// Only calculate the cursor position if the cursor isn't at the start
// of the text
if (m_cursorPos > 0)
{
// Calculate the row in which the cursor appears
cursorRow = m_text->getLineContainingCharIndex(m_cursorPos);
// Cursor line offset gives us the distance of the cursor from the
// start of the line
uint8_t cursorLineOffset = m_cursorPos - m_text->getLineStartIndex(cursorRow);
CStringIterator *iterator = m_text->newStringIterator();
iterator->moveTo(m_text->getLineStartIndex(cursorRow));
// Sum the width of each char in the row to find the x coordinate
for (int i = 0; i < cursorLineOffset; ++i)
{
x += getFont()->getCharWidth(iterator->getChar());
iterator->moveToNext();
}
delete iterator;
}
// Add offset of row to calculated value
x += getRowX(cursorRow);
// Calculate y coordinate of the cursor
y = getRowY(cursorRow);
}
/**
* Gets the index of the character at the specified x coordinate in the
* specified row.
*
* @param x X coordinate of the character.
* @param rowIndex Index of the row containing the character.
* @return The index of the character at the specified coordinate.
*/
int CMultiLineTextBox::getCharIndexAtCoordinate(nxgl_coord_t x, int rowIndex) const
{
// Locate the character within the row
int startIndex = m_text->getLineStartIndex(rowIndex);
int stopIndex = m_text->getLineLength(rowIndex);
int width = getRowX(rowIndex);
int index = -1;
CStringIterator *iterator = m_text->newStringIterator();
iterator->moveTo(startIndex);
width += m_text->getFont()->getCharWidth(iterator->getChar());
for (int i = 0; i < stopIndex; ++i)
{
if (width > x)
{
if (i == 0)
{
// If the coordinate is on the left of the text, we add nothing
// to the index
index = startIndex;
}
else
{
// Character within the row.
// This is the character that contains the coordinate.
index = startIndex + i;
}
break;
}
iterator->moveToNext();
width += m_text->getFont()->getCharWidth(iterator->getChar());
}
delete iterator;
// If the coordinate is past the last character, index will still be -1.
// We need to set it to the last character
if (index == -1)
{
if (rowIndex == m_text->getLineCount() - 1)
{
// Index past the end point of the text, so return an index
// just past the text
index = startIndex + stopIndex;
}
else
{
// Index at the end of a row, so return the last index of the
// row
index = startIndex + stopIndex - 1;
}
}
return index;
}
/**
* Get the index of the character at the specified coordinates.
*
* @param x X coordinate of the character.
* @param y Y coordinate of the character.
* @return The index of the character at the specified coordinates.
*/
unsigned int
CMultiLineTextBox::getCharIndexAtCoordinates(nxgl_coord_t x, nxgl_coord_t y) const
{
int rowIndex = getRowContainingCoordinate(y);
return getCharIndexAtCoordinate(x, rowIndex);
}
/**
* Get the row containing the specified Y coordinate.
*
* @param y Y coordinate to locate.
* @return The index of the row containing the specified Y coordinate.
*/
int CMultiLineTextBox::getRowContainingCoordinate(nxgl_coord_t y) const
{
int row = -1;
// Locate the row containing the character
for (int i = 0; i < m_text->getLineCount(); ++i)
{
// Abort search if we've found the row below the y coordinate
if (getRowY(i) > y)
{
if (i == 0)
{
// If the coordinate is above the text, we return the top
// row
row = 0;
}
else
{
// Row within the text, so return the previous row - this is
// the row that contains the coordinate.
row = i - 1;
}
break;
}
}
// If the coordinate is below the text, row will still be -1.
// We need to set it to the last row
if (row == -1)
{
row = m_text->getLineCount() - 1;
}
return row;
}
/**
* Draw the area of this widget that falls within the clipping region.
* Called by the redraw() function to draw all visible regions.
*
* @param port The CGraphicsPort to draw to.
* @see redraw()
*/
void CMultiLineTextBox::drawContents(CGraphicsPort *port)
{
drawText(port);
// Draw the cursor
drawCursor(port);
}
/**
* Draw the area of this widget that falls within the clipping region.
* Called by the redraw() function to draw all visible regions.
*
* @param port The CGraphicsPort to draw to.
* @see redraw()
*/
void CMultiLineTextBox::drawBorder(CGraphicsPort *port)
{
port->drawFilledRect(getX(), getY(), getWidth(), getHeight(),
getBackgroundColor());
// Stop drawing if the widget indicates it should not have an outline
if (!isBorderless())
{
port->drawBevelledRect(getX(), getY(), getWidth(), getHeight(),
getShadowEdgeColor(), getShineEdgeColor());
}
}
/**
* Move cursor one character to the left.
*/
void CMultiLineTextBox::moveCursorLeft(void)
{
if (m_cursorPos > 0)
{
moveCursorToPosition(m_cursorPos - 1);
}
jumpToCursor();
}
/**
* Move cursor one character to the right.
*/
void CMultiLineTextBox::moveCursorRight(void)
{
if (m_cursorPos < (int)m_text->getLength())
{
moveCursorToPosition(m_cursorPos + 1);
}
jumpToCursor();
}
/**
* Move cursor one row upwards.
*/
void CMultiLineTextBox::moveCursorUp(void)
{
nxgl_coord_t cursorX = 0;
nxgl_coord_t cursorY = 0;
getCursorCoordinates(cursorX, cursorY);
// Get the midpoint of the cursor. We use the midpoint to ensure that
// the cursor does not drift off to the left as it moves up the text, which
// is a problem when we use the left edge as the reference point when the
// font is proportional
cursorX += m_text->getFont()->getCharWidth(m_text->getCharAt(m_cursorPos)) >> 1;
// Locate the character above the midpoint
int index = getCharIndexAtCoordinates(cursorX, cursorY + m_text->getLineHeight());
moveCursorToPosition(index);
jumpToCursor();
}
/**
* Move cursor one row downwards.
*/
void CMultiLineTextBox::moveCursorDown(void)
{
nxgl_coord_t cursorX = 0;
nxgl_coord_t cursorY = 0;
getCursorCoordinates(cursorX, cursorY);
// Get the midpoint of the cursor. We use the midpoint to ensure that
// the cursor does not drift off to the left as it moves up the text, which
// is a problem when we use the left edge as the reference point when the
// font is proportional
cursorX += m_text->getFont()->getCharWidth(m_text->getCharAt(m_cursorPos)) >> 1;
// Locate the character above the midpoint
int index = getCharIndexAtCoordinates(cursorX, cursorY - m_text->getLineHeight());
moveCursorToPosition(index);
jumpToCursor();
}
/**
* Ensures that the textbox only contains the maximum allowed
* number of rows by culling any excess rows from the top of
* the text.
*
* @return True if lines were removed from the text; false if not.
*/
bool CMultiLineTextBox::cullTopLines(void)
{
// Ensure that we have the correct number of rows
if (m_text->getLineCount() > m_maxRows)
{
m_text->stripTopLines(m_text->getLineCount() - m_maxRows);
return true;
}
return false;
}
/**
* Ensures that the canvas height is the height of the widget,
* if the widget exceeds the size of the text, or the height of
* the text if the text exceeds the size of the widget.
*/
void CMultiLineTextBox::limitCanvasHeight(void)
{
m_canvasHeight = m_text->getPixelHeight();
CRect rect;
getClientRect(rect);
if (m_canvasHeight < rect.getHeight())
{
m_canvasHeight = rect.getHeight();
}
}
/**
* Ensures that the canvas cannot scroll beyond its height.
*/
void CMultiLineTextBox::limitCanvasY(void)
{
CRect rect;
getClientRect(rect);
// Ensure that the visible portion of the canvas is not less than the
// height of the viewer window
if (m_canvasY + m_canvasHeight < rect.getHeight())
{
jumpToTextBottom();
}
}
/**
* Jumps to the cursor coordinates of the text.
*/
void CMultiLineTextBox::jumpToCursor(void)
{
// Get the co-odinates of the cursor
nxgl_coord_t cursorX;
nxgl_coord_t cursorY;
getCursorCoordinates(cursorX, cursorY);
// Work out which row the cursor falls within
int cursorRow = m_text->getLineContainingCharIndex(m_cursorPos);
nxgl_coord_t rowY = getRowY(cursorRow);
// If the cursor is outside the visible portion of the canvas, jump to it
CRect rect;
getClientRect(rect);
if (rowY + m_text->getLineHeight() + m_canvasY > rect.getHeight())
{
// Cursor is below the visible portion of the canvas, so
// jump down so that the cursor's row is the bottom row of
// text
jump(0, -(rowY + m_text->getLineHeight() - rect.getHeight()));
}
else if (rowY + m_canvasY < 0)
{
// Cursor is above the visible portion of the canvas, so
// jump up so that the cursor's row is the top row of text
jump(0, -cursorY);
}
}
/**
* Jumps to the bottom of the text.
*/
void CMultiLineTextBox::jumpToTextBottom(void)
{
CRect rect;
getClientRect(rect);
jump(0, -(m_canvasHeight - rect.getHeight()));
}
/**
* Resize the textbox to the new dimensions.
*
* @param width The new width.
* @param height The new height.
*/
void CMultiLineTextBox::onResize(nxgl_coord_t width, nxgl_coord_t height)
{
// Ensure the base class resize method is called
CScrollingPanel::onResize(width, height);
// Resize the canvas' width
CRect rect;
getClientRect(rect);
m_canvasWidth = rect.getWidth();
m_canvasHeight = rect.getHeight();
m_canvasX = 0;
m_canvasY = 0;
calculateVisibleRows();
// Re-wrap the text
m_text->setWidth(getWidth());
m_text->wrap();
bool raiseEvent = cullTopLines();
limitCanvasHeight();
limitCanvasY();
if (raiseEvent)
{
m_widgetEventHandlers->raiseValueChangeEvent();
}
}
/**
* Starts the dragging system.
*
* @param x The x coordinate of the click.
* @param y The y coordinate of the click.
*/
void CMultiLineTextBox::onClick(nxgl_coord_t x, nxgl_coord_t y)
{
startDragging(x, y);
// Move cursor to clicked coordinates
CRect rect;
getClientRect(rect);
// Adjust x and y from screen coordinates to canvas coordinates
nxgl_coord_t canvasRelativeX = x - getX() - rect.getX() - m_canvasX;
nxgl_coord_t canvasRelativeY = y - getY() - rect.getY() - m_canvasY;
moveCursorToPosition(getCharIndexAtCoordinates(canvasRelativeX, canvasRelativeY));
}
/**
* Opens the keyboard on the bottom display.
*
* @param x The x coordinates of the click.
* @param y The y coordinates of the click.
*/
void CMultiLineTextBox::onDoubleClick(nxgl_coord_t x, nxgl_coord_t y)
{
}
/**
* Handles cursor control events. Moves the cursor
* in the direction selected.
*
* @param key The key that was pressed.
*/
void CMultiLineTextBox::handleCursorControlEvent(const CWidgetEventArgs &e)
{
ECursorControl control = e.getCursorControl();
switch (control)
{
case CURSOR_LEFT:
moveCursorLeft();
break;
case CURSOR_RIGHT:
moveCursorRight();
break;
case CURSOR_UP:
moveCursorDown();
break;
case CURSOR_DOWN:
moveCursorUp();
break;
default:
// Not interested in other controls
break;
}
}
/**
* Handle a keyboard press event.
*
* @param e The event data.
*/
void handleKeyPressEvent(const CWidgetEventArgs &e);
/**
* Gets the x position of a row of text based on the width of the row and the
* type of horizontal alignment currently set.
*
* @param row The index of the row.
* @return The x coordinate of the row.
*/
nxgl_coord_t CMultiLineTextBox::getRowX(int row) const
{
CRect rect;
getClientRect(rect);
uint8_t rowLength = m_text->getLineTrimmedLength(row);
uint8_t rowPixelWidth = m_text->getFont()->getStringWidth(*m_text, m_text->getLineStartIndex(row), rowLength);
// Calculate horizontal position
switch (m_hAlignment)
{
case TEXT_ALIGNMENT_HORIZ_CENTER:
return (rect.getWidth() - rowPixelWidth) >> 1;
case TEXT_ALIGNMENT_HORIZ_LEFT:
return 0;
case TEXT_ALIGNMENT_HORIZ_RIGHT:
return rect.getWidth() - rowPixelWidth;
}
// Will never be reached
return 0;
}
/**
* Gets the y position of the specified row of text based on the type of
* vertical alignment currently set.
*
* @param row The row number to find the y coordinate of.
* @return The y coordinate of the specified row of text.
*/
nxgl_coord_t CMultiLineTextBox::getRowY(int row) const
{
// If the amount of text exceeds the size of the widget, force
// the text to be top-aligned
if (m_visibleRows <= m_text->getLineCount())
{
return row * m_text->getLineHeight();
}
// All text falls within the textbox, so obey the alignment
// options
nxgl_coord_t textY = 0;
nxgl_coord_t startPos = 0;
int canvasRows = 0;
int textRows = 0;
CRect rect;
getClientRect(rect);
// Calculate vertical position
switch (m_vAlignment)
{
case TEXT_ALIGNMENT_VERT_CENTER:
// Calculate the maximum number of rows
canvasRows = m_canvasHeight / m_text->getLineHeight();
textY = row * m_text->getLineHeight();
// Get the number of rows of text
textRows = m_text->getLineCount();
// Ensure there's always one row
if (textRows == 0)
{
textRows = 1;
}
// Calculate the start position of the block of text
startPos = ((canvasRows - textRows) * m_text->getLineHeight()) >> 1;
// Calculate the row Y coordinate
textY = startPos + textY;
break;
case TEXT_ALIGNMENT_VERT_TOP:
textY = row * m_text->getLineHeight();
break;
case TEXT_ALIGNMENT_VERT_BOTTOM:
textY = rect.getHeight() - (((m_text->getLineCount() - row) * m_text->getLineHeight()));
break;
}
return textY;
}
/**
* Return true if the cursor is visible
*/
bool CMultiLineTextBox::isCursorVisible(void) const
{
return (m_showCursor == SHOW_CURSOR_ALWAYS ||
(m_showCursor == SHOW_CURSOR_ONFOCUS && hasFocus()));
}
/**
* Gets the character under the cursor.
*
* @return The character under the cursor.
*/
nxwidget_char_t CMultiLineTextBox::getCursorChar(void) const
{
if (m_cursorPos < (int)m_text->getLength())
{
return m_text->getCharAt(m_cursorPos);
}
else
{
return ' ';
}
}
/**
* Works out the number of visible rows within the textbox.
*/
void CMultiLineTextBox::calculateVisibleRows(void)
{
CRect rect;
getClientRect(rect);
m_visibleRows = rect.getHeight() / m_text->getLineHeight();
}
/**
* Draws text.
*
* @param port The CGraphicsPort to draw to.
*/
void CMultiLineTextBox::drawText(CGraphicsPort *port)
{
// Early exit if there is no text to display
if (m_text->getLineCount() == 0)
{
return;
}
// Determine the top and bottom rows within the graphicsport's clip rect.
// We only draw these rows in order to increase the speed of the routine.
int regionY = -m_canvasY; // Y coord of canvas visible region
int topRow = getRowContainingCoordinate(regionY);
int bottomRow = getRowContainingCoordinate(regionY + m_rect.getHeight());
// Early exit checks
if ((topRow < 0) && (bottomRow < 0))
{
return;
}
if ((bottomRow >= m_text->getLineCount()) && (topRow >= m_text->getLineCount()))
{
return;
}
// Prevent overflows
if (topRow < 0)
{
topRow = 0;
}
if (bottomRow >= m_text->getLineCount())
{
bottomRow = m_text->getLineCount() - 1;
}
// Draw lines of text
int currentRow = topRow;
// Draw all rows in this region
while (currentRow <= bottomRow)
{
drawRow(port, currentRow);
currentRow++;
}
}
/**
* Draws the cursor.
*
* @param port The CGraphicsPort to draw to.
*/
void CMultiLineTextBox::drawCursor(CGraphicsPort *port)
{
// Get the cursor coordinates
if (isCursorVisible())
{
nxgl_coord_t cursorX = 0;
nxgl_coord_t cursorY = 0;
getCursorCoordinates(cursorX, cursorY);
// Adjust for canvas offsets
cursorX += m_canvasX;
cursorY += m_canvasY;
// Draw cursor
port->invert(cursorX, cursorY,
m_text->getFont()->getCharWidth(getCursorChar()),
m_text->getFont()->getHeight());
}
}
/**
* Draws a single line of text.
*
* @param port The CGraphicsPort to draw to.
* @param row The index of the row to draw.
*/
void CMultiLineTextBox::drawRow(CGraphicsPort *port, int row)
{
// Get the drawing region
CRect rect;
getRect(rect);
uint8_t rowLength = m_text->getLineTrimmedLength(row);
struct nxgl_point_s pos;
pos.x = getRowX(row) + m_canvasX + rect.getX();
pos.y = getRowY(row) + m_canvasY + rect.getY();
// Determine the background and text color
nxgl_mxpixel_t textColor;
if (!isEnabled())
{
textColor = getDisabledTextColor();
}
else
{
textColor = getEnabledTextColor();
}
// And draw the text using the selected color
port->drawText(&pos, &rect, m_text->getFont(), *m_text,
m_text->getLineStartIndex(row), rowLength, textColor);
}