- // TODO: check for more than one line!
- characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
- characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
-
- const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
- const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
-
- const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
-
- // Get the metrics for the group of glyphs.
- GlyphMetrics glyphMetrics;
- GetGlyphsMetrics( glyphIndex,
- numberOfGlyphs,
- glyphMetrics );
-
- cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
-
- cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
- }
- else
- {
- if( !isCurrentRightToLeft )
- {
- cursorInfo.primaryPosition.x += glyphMetrics.advance;
- }
- else
- {
- cursorInfo.primaryPosition.x -= glyphMetrics.advance;
- }
- }
- }
-
- // Set the alternative cursor position.
- if( cursorInfo.isSecondaryCursor )
- {
- // Convert the cursor position into the glyph position.
- const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
- const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
- const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
-
- // Get the glyph position.
- const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
-
- // Get the metrics for the group of glyphs.
- GlyphMetrics glyphMetrics;
- GetGlyphsMetrics( previousGlyphIndex,
- numberOfGlyphs,
- glyphMetrics );
-
- // Set the cursor position and height.
- cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
- ( !isLastPosition && isCurrentRightToLeft ) ) ? glyphMetrics.advance : 0.f );
-
- cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
-
- cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
-
- // Update the primary cursor height as well.
- cursorInfo.primaryCursorHeight *= 0.5f;
- }
- }
-
- /**
- * @brief Get some glyph's metrics of a group of glyphs formed as a result of shaping one character.
- *
- * @param[in] glyphIndex The index to the first glyph.
- * @param[in] numberOfGlyphs The number of glyphs.
- * @param[out] glyphMetrics Some glyph metrics (font height, advance, ascender and x bearing).
- */
- void GetGlyphsMetrics( GlyphIndex glyphIndex,
- Length numberOfGlyphs,
- GlyphMetrics& glyphMetrics ) const
- {
- const GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
-
- const GlyphInfo& firstGlyph = *( glyphsBuffer + glyphIndex );
-
- Text::FontMetrics fontMetrics;
- mFontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
-
- glyphMetrics.fontHeight = fontMetrics.height;
- glyphMetrics.advance = firstGlyph.advance;
- glyphMetrics.ascender = fontMetrics.ascender;
- glyphMetrics.xBearing = firstGlyph.xBearing;
-
- for( unsigned int i = 1u; i < numberOfGlyphs; ++i )
- {
- const GlyphInfo& glyphInfo = *( glyphsBuffer + glyphIndex + i );
-
- glyphMetrics.advance += glyphInfo.advance;
- }
- }
-
- /**
- * @brief Calculates the new cursor index.
- *
- * It takes into account that in some scripts multiple characters can form a glyph and all of them
- * need to be jumped with one key event.
- *
- * @param[in] index The initial new index.
- *
- * @return The new cursor index.
- */
- CharacterIndex CalculateNewCursorIndex( CharacterIndex index ) const
- {
- CharacterIndex cursorIndex = mPrimaryCursorPosition;
-
- const Script script = mLogicalModel->GetScript( index );
- const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
- const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
-
- Length numberOfCharacters = 0u;
- if( TextAbstraction::LATIN == script )
- {
- // Prevents to jump the whole Latin ligatures like fi, ff, ...
- numberOfCharacters = 1u;
- }
- else
- {
- GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
- numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
-
- while( 0u == numberOfCharacters )
- {
- numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
- ++glyphIndex;
- }
- }
-
- if( index < mPrimaryCursorPosition )
- {
- cursorIndex -= numberOfCharacters;
- }
- else
- {
- cursorIndex += numberOfCharacters;
- }
-
- return cursorIndex;
- }
-
- void UpdateCursorPosition()
- {
- CursorInfo cursorInfo;
-
- GetCursorPosition( mPrimaryCursorPosition,
- cursorInfo );
-
- mDecorator->SetPosition( PRIMARY_CURSOR,
- cursorInfo.primaryPosition.x,
- cursorInfo.primaryPosition.y,
- cursorInfo.primaryCursorHeight,
- cursorInfo.lineHeight );
-
- if( cursorInfo.isSecondaryCursor )
- {
- mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
- mDecorator->SetPosition( SECONDARY_CURSOR,
- cursorInfo.secondaryPosition.x,
- cursorInfo.secondaryPosition.y,
- cursorInfo.secondaryCursorHeight,
- cursorInfo.lineHeight );
- }
- else
- {
- mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
- }
-
- mUpdateCursorPosition = false;
- mDecoratorUpdated = true;
- }
-
- LogicalModelPtr mLogicalModel;
- VisualModelPtr mVisualModel;
- DecoratorPtr mDecorator;
- FontDefaults* mFontDefaults;
- TextAbstraction::FontClient& mFontClient;
- std::string mPlaceholderText;
-
- /**
- * This is used to delay handling events until after the model has been updated.
- * The number of updates to the model is minimized to improve performance.
- */
- vector<Event> mEventQueue; ///< The queue of touch events etc.
-
- State mState; ///< Selection mode, edit mode etc.
-
- CharacterIndex mPrimaryCursorPosition; ///< Index into logical model for primary cursor
- CharacterIndex mSecondaryCursorPosition; ///< Index into logical model for secondary cursor
-
- /**
- * 0,0 means that the top-left corner of the layout matches the top-left corner of the UI control.
- * Typically this will have a negative value with scrolling occurs.
- */
- Vector2 mScrollPosition; ///< The text is offset by this position when scrolling.
-
- bool mDecoratorUpdated : 1; ///< True if the decorator was updated during event processing
- bool mCursorBlinkEnabled : 1; ///< True if cursor should blink when active
- bool mGrabHandleEnabled : 1; ///< True if grab handle is enabled
- bool mGrabHandlePopupEnabled : 1; ///< True if the grab handle popu-up should be shown
- bool mSelectionEnabled : 1; ///< True if selection handles, highlight etc. are enabled
- bool mHorizontalScrollingEnabled : 1; ///< True if horizontal scrolling is enabled
- bool mVerticalScrollingEnabled : 1; ///< True if vertical scrolling is enabled
- bool mUpdateCursorPosition : 1; ///< True if the visual position of the cursor must be recalculated
-};
-
-struct Controller::Impl
-{
- Impl( ControlInterface& controlInterface )
- : mControlInterface( controlInterface ),
- mLogicalModel(),
- mVisualModel(),
- mFontDefaults( NULL ),
- mTextInput( NULL ),
- mFontClient(),
- mView(),
- mLayoutEngine(),
- mModifyEvents(),
- mControlSize(),
- mAlignmentOffset(),
- mOperationsPending( NO_OPERATION ),
- mRecalculateNaturalSize( true )
- {
- mLogicalModel = LogicalModel::New();
- mVisualModel = VisualModel::New();
-
- mFontClient = TextAbstraction::FontClient::Get();
-
- mView.SetVisualModel( mVisualModel );
-
- // Set the text properties to default
- mVisualModel->SetTextColor( Color::WHITE );
- mVisualModel->SetShadowOffset( Vector2::ZERO );
- mVisualModel->SetShadowColor( Vector4::ZERO );
- mVisualModel->SetUnderlineEnabled( false );
- mVisualModel->SetUnderlineHeight( 0.0f );
- }
-
- ~Impl()
- {
- delete mTextInput;
- }
-
- ControlInterface& mControlInterface; ///< Reference to the text controller.
- LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
- VisualModelPtr mVisualModel; ///< Pointer to the visual model.
- FontDefaults* mFontDefaults; ///< Avoid allocating this when the user does not specify a font.
- Controller::TextInput* mTextInput; ///< Avoid allocating everything for text input until EnableTextInput().
- TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
- View mView; ///< The view interface to the rendering back-end.
- LayoutEngine mLayoutEngine; ///< The layout engine.
- std::vector<ModifyEvent> mModifyEvents; ///< Temporary stores the text set until the next relayout.
- Size mControlSize; ///< The size of the control.
- Vector2 mAlignmentOffset; ///< Vertical and horizontal offset of the whole text inside the control due to alignment.
- OperationsMask mOperationsPending; ///< Operations pending to be done to layout the text.
- bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated.
-};
-
-ControllerPtr Controller::New( ControlInterface& controlInterface )
-{
- return ControllerPtr( new Controller( controlInterface ) );
-}
-
-void Controller::SetText( const std::string& text )
-{
- // Cancel previously queued inserts etc.
- mImpl->mModifyEvents.clear();
-
- // Keep until size negotiation
- ModifyEvent event;
- event.type = REPLACE_TEXT;
- event.text = text;
- mImpl->mModifyEvents.push_back( event );
-
- if( mImpl->mTextInput )
- {
- // Cancel previously queued events
- mImpl->mTextInput->mEventQueue.clear();
-
- // TODO - Hide selection decorations
- }
-}
-
-void Controller::GetText( std::string& text ) const
-{
- if( !mImpl->mModifyEvents.empty() &&
- REPLACE_TEXT == mImpl->mModifyEvents[0].type )
- {
- text = mImpl->mModifyEvents[0].text;
- }
- else
- {
- // TODO - Convert from UTF-32
- }
-}
-
-void Controller::SetPlaceholderText( const std::string& text )
-{
- if( !mImpl->mTextInput )
- {
- mImpl->mTextInput->mPlaceholderText = text;
- }
-}
-
-void Controller::GetPlaceholderText( std::string& text ) const
-{
- if( !mImpl->mTextInput )
- {
- text = mImpl->mTextInput->mPlaceholderText;
- }
-}
-
-void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
-{
- if( !mImpl->mFontDefaults )
- {
- mImpl->mFontDefaults = new Controller::FontDefaults();
- }
-
- mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
- mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
- mImpl->mOperationsPending = ALL_OPERATIONS;
- mImpl->mRecalculateNaturalSize = true;
-
- // Clear the font-specific data
- mImpl->mLogicalModel->mFontRuns.Clear();
- mImpl->mVisualModel->mGlyphs.Clear();
- mImpl->mVisualModel->mGlyphsToCharacters.Clear();
- mImpl->mVisualModel->mCharactersToGlyph.Clear();
- mImpl->mVisualModel->mCharactersPerGlyph.Clear();
- mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
- mImpl->mVisualModel->mGlyphPositions.Clear();
- mImpl->mVisualModel->mLines.Clear();
- mImpl->mVisualModel->ClearCaches();
-
- RequestRelayout();
-}
-
-const std::string& Controller::GetDefaultFontFamily() const
-{
- if( mImpl->mFontDefaults )
- {
- return mImpl->mFontDefaults->mDefaultFontFamily;
- }
-
- return String::EMPTY;;
-}
-
-void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
-{
- if( !mImpl->mFontDefaults )
- {
- mImpl->mFontDefaults = new Controller::FontDefaults();
- }
-
- mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
- mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
- mImpl->mOperationsPending = ALL_OPERATIONS;
- mImpl->mRecalculateNaturalSize = true;
-
- // Clear the font-specific data
- mImpl->mLogicalModel->mFontRuns.Clear();
- mImpl->mVisualModel->mGlyphs.Clear();
- mImpl->mVisualModel->mGlyphsToCharacters.Clear();
- mImpl->mVisualModel->mCharactersToGlyph.Clear();
- mImpl->mVisualModel->mCharactersPerGlyph.Clear();
- mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
- mImpl->mVisualModel->mGlyphPositions.Clear();
- mImpl->mVisualModel->mLines.Clear();
- mImpl->mVisualModel->ClearCaches();
-
- RequestRelayout();
-}
-
-const std::string& Controller::GetDefaultFontStyle() const
-{
- if( mImpl->mFontDefaults )
- {
- return mImpl->mFontDefaults->mDefaultFontStyle;
- }
-
- return String::EMPTY;
-}
-
-void Controller::SetDefaultPointSize( float pointSize )
-{
- if( !mImpl->mFontDefaults )
- {
- mImpl->mFontDefaults = new Controller::FontDefaults();
- }
-
- mImpl->mFontDefaults->mDefaultPointSize = pointSize;
- mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
- mImpl->mOperationsPending = ALL_OPERATIONS;
- mImpl->mRecalculateNaturalSize = true;
-
- // Clear the font-specific data
- mImpl->mLogicalModel->mFontRuns.Clear();
- mImpl->mVisualModel->mGlyphs.Clear();
- mImpl->mVisualModel->mGlyphsToCharacters.Clear();
- mImpl->mVisualModel->mCharactersToGlyph.Clear();
- mImpl->mVisualModel->mCharactersPerGlyph.Clear();
- mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
- mImpl->mVisualModel->mGlyphPositions.Clear();
- mImpl->mVisualModel->mLines.Clear();
- mImpl->mVisualModel->ClearCaches();
-
- RequestRelayout();
-}
-
-float Controller::GetDefaultPointSize() const
-{
- if( mImpl->mFontDefaults )
- {
- return mImpl->mFontDefaults->mDefaultPointSize;
- }
-
- return 0.0f;
-}
-
-void Controller::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters ) const