From 5a984a1705bf94f17a8ef6a05ce170d1a47e49b1 Mon Sep 17 00:00:00 2001 From: Paul Wisbey Date: Tue, 17 Mar 2015 19:14:26 +0000 Subject: [PATCH] Basic IMF->KeyEvent and scrolling support Change-Id: I3477eecc33299231920f7c8be06ae5e7d17e90a6 --- dali-toolkit/images/decorator-cursor.png | Bin 2802 -> 0 bytes .../controls/text-controls/text-field-impl.cpp | 65 ++--- .../controls/text-controls/text-field-impl.h | 7 +- .../internal/text/decorator/text-decorator.cpp | 42 ++-- .../internal/text/decorator/text-decorator.h | 17 +- dali-toolkit/internal/text/text-controller.cpp | 275 ++++++++++++++++++--- dali-toolkit/internal/text/text-controller.h | 16 ++ dali-toolkit/internal/text/text-io.cpp | 18 ++ dali-toolkit/internal/text/text-io.h | 10 + .../public-api/controls/text-controls/text-field.h | 1 - 10 files changed, 324 insertions(+), 127 deletions(-) delete mode 100644 dali-toolkit/images/decorator-cursor.png diff --git a/dali-toolkit/images/decorator-cursor.png b/dali-toolkit/images/decorator-cursor.png deleted file mode 100644 index bbb993fbefd60843ce61e8172c76fc3d653f6f37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2802 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 Ef(9KmX8-^I diff --git a/dali-toolkit/internal/controls/text-controls/text-field-impl.cpp b/dali-toolkit/internal/controls/text-controls/text-field-impl.cpp index b5900c6..3bf4e67 100644 --- a/dali-toolkit/internal/controls/text-controls/text-field-impl.cpp +++ b/dali-toolkit/internal/controls/text-controls/text-field-impl.cpp @@ -78,7 +78,6 @@ DALI_PROPERTY_REGISTRATION( TextField, "font-family", STRING, FON DALI_PROPERTY_REGISTRATION( TextField, "font-style", STRING, FONT_STYLE ) DALI_PROPERTY_REGISTRATION( TextField, "point-size", FLOAT, POINT_SIZE ) DALI_PROPERTY_REGISTRATION( TextField, "exceed-policy", INTEGER, EXCEED_POLICY ) -DALI_PROPERTY_REGISTRATION( TextField, "cursor-image", STRING, CURSOR_IMAGE ) DALI_PROPERTY_REGISTRATION( TextField, "primary-cursor-color", VECTOR4, PRIMARY_CURSOR_COLOR ) DALI_PROPERTY_REGISTRATION( TextField, "secondary-cursor-color", VECTOR4, SECONDARY_CURSOR_COLOR ) DALI_PROPERTY_REGISTRATION( TextField, "enable-cursor-blink", BOOLEAN, ENABLE_CURSOR_BLINK ) @@ -191,16 +190,6 @@ void TextField::SetProperty( BaseObject* object, Property::Index index, const Pr impl.mExceedPolicy = value.Get< int >(); break; } - case Toolkit::TextField::Property::CURSOR_IMAGE: - { - ResourceImage image = ResourceImage::New( value.Get< std::string >() ); - - if( impl.mDecorator ) - { - impl.mDecorator->SetCursorImage( image ); - } - break; - } case Toolkit::TextField::Property::PRIMARY_CURSOR_COLOR: { if( impl.mDecorator ) @@ -319,18 +308,6 @@ Property::Value TextField::GetProperty( BaseObject* object, Property::Index inde value = impl.mExceedPolicy; break; } - case Toolkit::TextField::Property::CURSOR_IMAGE: - { - if( impl.mDecorator ) - { - ResourceImage image = ResourceImage::DownCast( impl.mDecorator->GetCursorImage() ); - if( image ) - { - value = image.GetUrl(); - } - } - break; - } case Toolkit::TextField::Property::PRIMARY_CURSOR_COLOR: { if( impl.mDecorator ) @@ -368,18 +345,6 @@ Property::Value TextField::GetProperty( BaseObject* object, Property::Index inde } break; } - case Toolkit::TextField::Property::GRAB_HANDLE_IMAGE: - { - if( impl.mDecorator ) - { - ResourceImage image = ResourceImage::DownCast( impl.mDecorator->GetCursorImage() ); - if( image ) - { - value = image.GetUrl(); - } - } - break; - } case Toolkit::TextField::Property::DECORATION_BOUNDING_BOX: { if( impl.mDecorator ) @@ -417,6 +382,7 @@ void TextField::OnInitialize() // Forward input events to controller EnableGestureDetection(Gesture::Tap); GetTapGestureDetector().SetMaximumTapsRequired( 2 ); + EnableGestureDetection(Gesture::Pan); // Set BoundingBox to stage size if not already set. if ( mDecorator->GetBoundingBox().IsEmpty() ) @@ -448,7 +414,7 @@ void TextField::OnRelayout( const Vector2& size, ActorSizeContainer& container ) { if( mDecorator ) { - mDecorator->Relayout( size ); + mDecorator->Relayout( size, mController->GetScrollPosition() ); } if( !mRenderer ) @@ -472,6 +438,9 @@ void TextField::OnRelayout( const Vector2& size, ActorSizeContainer& container ) if( mRenderableActor ) { + const Vector2& scrollPosition = mController->GetScrollPosition(); + mRenderableActor.SetPosition( scrollPosition.x, scrollPosition.y ); + // Make sure the actor is parented correctly with/without clipping if( mClipper ) { @@ -537,6 +506,11 @@ void TextField::OnTap( const TapGesture& gesture ) mController->TapEvent( gesture.numberOfTaps, gesture.localPoint.x, gesture.localPoint.y ); } +void TextField::OnPan( const PanGesture& gesture ) +{ + mController->PanEvent( gesture.state, gesture.displacement ); +} + bool TextField::OnKeyEvent( const KeyEvent& event ) { if( Dali::DALI_KEY_ESCAPE == event.keyCode ) @@ -547,30 +521,19 @@ bool TextField::OnKeyEvent( const KeyEvent& event ) return mController->KeyEvent( event ); } -ImfManager::ImfCallbackData TextField::OnImfEvent( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent ) +ImfManager::ImfCallbackData TextField::OnImfEvent( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent ) { switch ( imfEvent.eventName ) { - case ImfManager::PREEDIT: - { - // TODO - break; - } case ImfManager::COMMIT: { - // TODO + KeyEvent event( "", imfEvent.predictiveString, 0, 0, 0, KeyEvent::Down ); + mController->KeyEvent( event ); break; } + case ImfManager::PREEDIT: // fall through case ImfManager::DELETESURROUNDING: - { - // TODO - break; - } case ImfManager::GETSURROUNDING: - { - // TODO - break; - } case ImfManager::VOID: { // do nothing diff --git a/dali-toolkit/internal/controls/text-controls/text-field-impl.h b/dali-toolkit/internal/controls/text-controls/text-field-impl.h index fa55c77..a4f4831 100644 --- a/dali-toolkit/internal/controls/text-controls/text-field-impl.h +++ b/dali-toolkit/internal/controls/text-controls/text-field-impl.h @@ -109,11 +109,16 @@ private: // From Control virtual void OnKeyInputFocusLost(); /** - * Received for single & double taps + * @copydoc Control::OnTap() */ virtual void OnTap( const TapGesture& tap ); /** + * @copydoc Control::OnPan() + */ + virtual void OnPan( const PanGesture& gesture ); + + /** * @copydoc Dali::CustomActorImpl::OnKeyEvent(const KeyEvent&) */ virtual bool OnKeyEvent(const KeyEvent& event); diff --git a/dali-toolkit/internal/text/decorator/text-decorator.cpp b/dali-toolkit/internal/text/decorator/text-decorator.cpp index 4df7c51..4cff454 100644 --- a/dali-toolkit/internal/text/decorator/text-decorator.cpp +++ b/dali-toolkit/internal/text/decorator/text-decorator.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #ifdef DEBUG_ENABLED #define DECORATOR_DEBUG @@ -58,7 +59,6 @@ const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-h const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" ); //const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" ); //const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" ); -const char* DEFAULT_CURSOR_IMAGE( DALI_IMAGE_DIR "decorator-cursor.png"); const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f ); const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f ); @@ -287,7 +287,7 @@ struct Decorator::Impl : public ConnectionTracker * Relayout of the decorations owned by the decorator. * @param[in] size The Size of the UI control the decorater is adding it's decorations to. */ - void Relayout( const Vector2& size ) + void Relayout( const Vector2& size, const Vector2& scrollPosition ) { // TODO - Remove this if nothing is active CreateActiveLayer(); @@ -296,11 +296,15 @@ struct Decorator::Impl : public ConnectionTracker CreateCursors(); if( mPrimaryCursor ) { - mPrimaryCursor.SetPosition( mCursor[PRIMARY_CURSOR].x, mCursor[PRIMARY_CURSOR].y ); + mPrimaryCursor.SetPosition( mCursor[PRIMARY_CURSOR].x + scrollPosition.x, + mCursor[PRIMARY_CURSOR].y + scrollPosition.y ); + mPrimaryCursor.SetSize( 1.0f, mCursor[PRIMARY_CURSOR].height ); } if( mSecondaryCursor ) { - mSecondaryCursor.SetPosition( mCursor[SECONDARY_CURSOR].x, mCursor[SECONDARY_CURSOR].y ); + mSecondaryCursor.SetPosition( mCursor[SECONDARY_CURSOR].x + scrollPosition.x, + mCursor[SECONDARY_CURSOR].y + scrollPosition.y ); + mSecondaryCursor.SetSize( 1.0f, mCursor[SECONDARY_CURSOR].height ); } // Show or hide the grab handle @@ -310,7 +314,8 @@ struct Decorator::Impl : public ConnectionTracker CreateGrabHandle(); - mGrabHandle.SetPosition( mCursor[PRIMARY_CURSOR].x, mCursor[PRIMARY_CURSOR].y + mCursor[PRIMARY_CURSOR].height ); + mGrabHandle.SetPosition( mCursor[PRIMARY_CURSOR].x + scrollPosition.x, + mCursor[PRIMARY_CURSOR].y + scrollPosition.y + mCursor[PRIMARY_CURSOR].height ); } else if( mGrabHandle ) { @@ -325,10 +330,12 @@ struct Decorator::Impl : public ConnectionTracker CreateSelectionHandles(); SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ]; - primary.actor.SetPosition( primary.x, primary.y + primary.cursorHeight ); + primary.actor.SetPosition( primary.x + scrollPosition.x, + primary.y + scrollPosition.y + primary.cursorHeight ); SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ]; - secondary.actor.SetPosition( secondary.x, secondary.y + secondary.cursorHeight ); + secondary.actor.SetPosition( secondary.x + scrollPosition.x, + secondary.y + scrollPosition.y + secondary.cursorHeight ); //CreateHighlight(); TODO } @@ -351,11 +358,8 @@ struct Decorator::Impl : public ConnectionTracker void CreateCursor( ImageActor& cursor ) { - if ( !mCursorImage ) - { - mCursorImage = ResourceImage::New( DEFAULT_CURSOR_IMAGE ); - } - cursor = ImageActor::New( mCursorImage ); + cursor = CreateSolidColorActor( Color::WHITE ); + cursor.SetParentOrigin( ParentOrigin::TOP_LEFT ); cursor.SetAnchorPoint( AnchorPoint::TOP_CENTER ); } @@ -941,9 +945,9 @@ const Rect& Decorator::GetBoundingBox() const return mImpl->mBoundingBox; } -void Decorator::Relayout( const Vector2& size ) +void Decorator::Relayout( const Vector2& size, const Vector2& scrollPosition ) { - mImpl->Relayout( size ); + mImpl->Relayout( size, scrollPosition ); } /** Cursor **/ @@ -976,16 +980,6 @@ void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& height ) height = mImpl->mCursor[cursor].height; } -void Decorator::SetCursorImage( Dali::Image image ) -{ - mImpl->mCursorImage = image; -} - -Dali::Image Decorator::GetCursorImage() const -{ - return mImpl->mCursorImage; -} - void Decorator::SetColor( Cursor cursor, const Dali::Vector4& color ) { mImpl->mCursor[cursor].color = color; diff --git a/dali-toolkit/internal/text/decorator/text-decorator.h b/dali-toolkit/internal/text/decorator/text-decorator.h index 70b6b65..9ea86c6 100644 --- a/dali-toolkit/internal/text/decorator/text-decorator.h +++ b/dali-toolkit/internal/text/decorator/text-decorator.h @@ -166,8 +166,9 @@ public: * @brief The decorator waits until a relayout before creating actors etc. * * @param[in] size The size of the parent control after size-negotiation. + * @param[in] scrollPosition The cursor, grab-handle positions etc. should be offset by this. */ - void Relayout( const Dali::Vector2& size ); + void Relayout( const Dali::Vector2& size, const Vector2& scrollPosition ); /** * @brief Sets which of the cursors are active. @@ -205,20 +206,6 @@ public: void GetPosition( Cursor cursor, float& x, float& y, float& height ) const; /** - * @brief Sets the image for a cursor. - * - * @param[in] image The image to use. - */ - void SetCursorImage( Dali::Image image ); - - /** - * @brief Retrieves the image for a cursor. - * - * @return The cursor image. - */ - Dali::Image GetCursorImage() const; - - /** * @brief Sets the color for a cursor. * * @param[in] cursor Whether this color is for the primary or secondary cursor. diff --git a/dali-toolkit/internal/text/text-controller.cpp b/dali-toolkit/internal/text/text-controller.cpp index 74ab9ae..47b0115 100644 --- a/dali-toolkit/internal/text/text-controller.cpp +++ b/dali-toolkit/internal/text/text-controller.cpp @@ -79,6 +79,7 @@ struct Controller::TextInput KEYBOARD_FOCUS_LOST_EVENT, CURSOR_KEY_EVENT, TAP_EVENT, + PAN_EVENT, GRAB_HANDLE_EVENT }; @@ -121,14 +122,20 @@ struct Controller::TextInput mPrimaryCursorPosition( 0u ), mSecondaryCursorPosition( 0u ), mDecoratorUpdated( false ), - mCursorBlinkEnabled( true ) + mCursorBlinkEnabled( true ), + mGrabHandleEnabled( false ), + mGrabHandlePopupEnabled( false ), + mSelectionEnabled( false ), + mHorizontalScrollingEnabled( true ), + mVerticalScrollingEnabled( false ), + mUpdateCursorPosition( false ) { } /** * @brief Helper to move the cursor, grab handle etc. */ - bool ProcessInputEvents() + bool ProcessInputEvents( const Vector2& controlSize ) { mDecoratorUpdated = false; @@ -158,6 +165,11 @@ struct Controller::TextInput OnTapEvent( *iter ); break; } + case PAN_EVENT: + { + OnPanEvent( *iter, controlSize ); + break; + } case GRAB_HANDLE_EVENT: { OnGrabHandleEvent( *iter ); @@ -167,6 +179,13 @@ struct Controller::TextInput } } + // The cursor must also be repositioned after inserts into the model + if( mUpdateCursorPosition ) + { + UpdateCursorPosition(); + mUpdateCursorPosition = false; + } + mEventQueue.clear(); return mDecoratorUpdated; @@ -206,21 +225,11 @@ struct Controller::TextInput } } - void HandleBackspaceKey() - { - // TODO - } - void HandleCursorKey( int keyCode ) { // TODO } - void HandleKeyString( const char* keyString ) - { - // TODO - } - void OnTapEvent( const Event& event ) { unsigned int tapCount = event.p1.mUint; @@ -232,17 +241,56 @@ struct Controller::TextInput float xPosition = event.p2.mFloat; float yPosition = event.p3.mFloat; float height(0.0f); - GetClosestCursorPosition( xPosition, yPosition, height ); + GetClosestCursorPosition( mPrimaryCursorPosition, xPosition, yPosition, height ); mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height ); + mUpdateCursorPosition = false; mDecoratorUpdated = true; } - else if( 2u == tapCount ) + else if( mSelectionEnabled && + 2u == tapCount ) { ChangeState( SELECTING ); } } + void OnPanEvent( const Event& event, const Vector2& controlSize ) + { + int state = event.p1.mInt; + + if( Gesture::Started == state || + Gesture::Continuing == state ) + { + const Vector2& actualSize = mVisualModel->GetActualSize(); + + if( mHorizontalScrollingEnabled ) + { + float displacementX = event.p2.mFloat; + mScrollPosition.x += displacementX; + + // Clamp between -space & 0 + float contentWidth = actualSize.width; + float space = (contentWidth > controlSize.width) ? contentWidth - controlSize.width : 0.0f; + mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x; + mScrollPosition.x = ( mScrollPosition.x > 0 ) ? 0 : mScrollPosition.x; + + mDecoratorUpdated = true; + } + if( mVerticalScrollingEnabled ) + { + float displacementY = event.p3.mFloat; + mScrollPosition.y += displacementY; + + // Clamp between -space & 0 + float space = (actualSize.height > controlSize.height) ? actualSize.height - controlSize.height : 0.0f; + mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y; + mScrollPosition.y = ( mScrollPosition.y > 0 ) ? 0 : mScrollPosition.y; + + mDecoratorUpdated = true; + } + } + } + void OnGrabHandleEvent( const Event& event ) { unsigned int state = event.p1.mUint; @@ -253,17 +301,17 @@ struct Controller::TextInput float yPosition = event.p3.mFloat; float height(0.0f); - GetClosestCursorPosition( xPosition, yPosition, height ); + GetClosestCursorPosition( mPrimaryCursorPosition, xPosition, yPosition, height ); mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height ); mDecorator->HidePopup(); mDecoratorUpdated = true; } - else if ( GRAB_HANDLE_RELEASED == state ) + else if ( mGrabHandlePopupEnabled && + GRAB_HANDLE_RELEASED == state ) { mDecorator->ShowPopup(); } - } void ChangeState( State newState ) @@ -296,55 +344,150 @@ struct Controller::TextInput { mDecorator->StartCursorBlink(); } - mDecorator->SetGrabHandleActive( true ); + if( mGrabHandleEnabled ) + { + mDecorator->SetGrabHandleActive( true ); + } mDecorator->SetSelectionActive( false ); mDecoratorUpdated = true; } } } - void GetClosestCursorPosition( float& x, float& y, float& height ) + LineIndex GetClosestLine( float y ) { - // TODO - Look at LineRuns first + LineIndex lineIndex( 0u ); + + const Vector& lines = mVisualModel->mLines; + for( float totalHeight = 0; lineIndex < lines.Count(); ++lineIndex ) + { + totalHeight += lines[lineIndex].lineSize.height; + if( y < totalHeight ) + { + break; + } + } - Text::Length numberOfGlyphs = mVisualModel->GetNumberOfGlyphs(); - if( 0 == numberOfGlyphs ) + return lineIndex; + } + + void GetClosestCursorPosition( CharacterIndex& logical, float& visualX, float& visualY, float& height ) + { + Length numberOfGlyphs = mVisualModel->mGlyphs.Count(); + Length numberOfLines = mVisualModel->mLines.Count(); + if( 0 == numberOfGlyphs || + 0 == numberOfLines ) { return; } - Vector& glyphs = mVisualModel->mGlyphs; + // Transform to visual model coords + visualX -= mScrollPosition.x; + visualY -= mScrollPosition.y; + + // Find which line is closest + LineIndex lineIndex( GetClosestLine( visualY ) ); + + const Vector& glyphs = mVisualModel->mGlyphs; const GlyphInfo* const glyphsBuffer = glyphs.Begin(); - Vector& positions = mVisualModel->mGlyphPositions; + const Vector& positions = mVisualModel->mGlyphPositions; const Vector2* const positionsBuffer = positions.Begin(); unsigned int closestGlyph = 0; + bool leftOfGlyph( false ); // which side of the glyph? float closestDistance = MAX_FLOAT; - for( unsigned int i = 0, numberOfGLyphs = glyphs.Count(); i < numberOfGLyphs; ++i ) + const LineRun& line = mVisualModel->mLines[lineIndex]; + GlyphIndex startGlyph = line.glyphIndex; + GlyphIndex endGlyph = line.glyphIndex + line.numberOfGlyphs; + DALI_ASSERT_DEBUG( endGlyph <= glyphs.Count() && "Invalid line info" ); + + for( GlyphIndex i = startGlyph; i < endGlyph; ++i ) { const GlyphInfo& glyphInfo = *( glyphsBuffer + i ); const Vector2& position = *( positionsBuffer + i ); float glyphX = position.x + glyphInfo.width*0.5f; float glyphY = position.y + glyphInfo.height*0.5f; - float distanceToGlyph = fabsf( glyphX - x ) + fabsf( glyphY - y ); + float distanceToGlyph = fabsf( glyphX - visualX ) + fabsf( glyphY - visualY ); if( distanceToGlyph < closestDistance ) { closestDistance = distanceToGlyph; closestGlyph = i; + leftOfGlyph = ( visualX < glyphX ); + } + } + + // Calculate the logical position + logical = mVisualModel->GetCharacterIndex( closestGlyph ); + + // Returns the visual position of the glyph + visualX = positions[closestGlyph].x; + if( !leftOfGlyph ) + { + visualX += glyphs[closestGlyph].width; + + //if( LTR ) TODO + ++logical; + } + else// if ( RTL ) TODO + { + //++logical; + } + visualY = 0.0f; + + height = line.lineSize.height; + } + + void UpdateCursorPosition() + { + if( 0 == mVisualModel->mGlyphs.Count() ) + { + return; + } + + // FIXME GetGlyphIndex() is behaving strangely +#if 0 + GlyphIndex cursorGlyph = mVisualModel->GetGlyphIndex( mPrimaryCursorPosition ); +#else + GlyphIndex cursorGlyph( 0u ); + for( cursorGlyph = 0; cursorGlyph < mVisualModel->mGlyphs.Count(); ++cursorGlyph ) + { + if( mPrimaryCursorPosition == mVisualModel->GetCharacterIndex( cursorGlyph ) ) + { + break; } } +#endif + + float visualX( 0.0f ); + float visualY( 0.0f ); + LineIndex lineIndex( 0u ); + const Vector& lineRuns = mVisualModel->mLines; + + if( cursorGlyph > 0 ) + { + --cursorGlyph; - // TODO - Consider RTL languages - x = positions[closestGlyph].x + glyphs[closestGlyph].width; - y = 0.0f; + visualX = mVisualModel->mGlyphPositions[ cursorGlyph ].x; + //if( LTR ) TODO + visualX += mVisualModel->mGlyphs[ cursorGlyph ].width; + + // Find the line height + for( GlyphIndex lastGlyph = 0; lineIndex < lineRuns.Count(); ++lineIndex ) + { + lastGlyph = (lineRuns[lineIndex].glyphIndex + lineRuns[lineIndex].numberOfGlyphs); + if( cursorGlyph < lastGlyph ) + { + break; + } + } + } - FontMetrics metrics; - TextAbstraction::FontClient::Get().GetFontMetrics( glyphs[closestGlyph].fontId, metrics ); - height = metrics.height; // TODO - Fix for multi-line + mDecorator->SetPosition( PRIMARY_CURSOR, visualX, visualY, lineRuns[lineIndex].lineSize.height ); + mDecoratorUpdated = true; } LogicalModelPtr mLogicalModel; @@ -364,8 +507,20 @@ struct Controller::TextInput CharacterIndex mPrimaryCursorPosition; ///< Index into logical model for primary cursor CharacterIndex mSecondaryCursorPosition; ///< Index into logical model for secondary cursor - bool mDecoratorUpdated : 1; ///< True if the decorator was updated during event processing - bool mCursorBlinkEnabled : 1; ///< True if cursor should blink when active + /** + * 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::FontDefaults @@ -607,6 +762,16 @@ bool Controller::GetEnableCursorBlink() const return false; } +const Vector2& Controller::GetScrollPosition() const +{ + if( mImpl->mTextInput ) + { + return mImpl->mTextInput->mScrollPosition; + } + + return Vector2::ZERO; +} + Vector3 Controller::GetNaturalSize() { Vector3 naturalSize; @@ -744,7 +909,7 @@ bool Controller::Relayout( const Vector2& size ) if( mImpl->mTextInput ) { // Move the cursor, grab handle etc. - updated = mImpl->mTextInput->ProcessInputEvents() || updated; + updated = mImpl->mTextInput->ProcessInputEvents( mImpl->mControlSize ) || updated; } return updated; @@ -838,7 +1003,16 @@ void Controller::InsertTextEvent( const std::string& text ) mImpl->mLogicalModel->mLineBreakInfo.Clear(); mImpl->mLogicalModel->mWordBreakInfo.Clear(); mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear(); + mImpl->mLogicalModel->mBidirectionalLineInfo.Clear(); + mImpl->mLogicalModel->mLogicalToVisualMap.Clear(); + mImpl->mLogicalModel->mVisualToLogicalMap.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(); // Convert text into UTF-32 Vector utf32Characters; @@ -878,6 +1052,9 @@ void Controller::InsertTextEvent( const std::string& text ) ALIGN | UPDATE_ACTUAL_SIZE | REORDER ); + + // Queue a cursor reposition event; this must wait until after DoRelayout() + mImpl->mTextInput->mUpdateCursorPosition = true; } void Controller::DeleteTextEvent() @@ -890,7 +1067,16 @@ void Controller::DeleteTextEvent() mImpl->mLogicalModel->mLineBreakInfo.Clear(); mImpl->mLogicalModel->mWordBreakInfo.Clear(); mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear(); + mImpl->mLogicalModel->mBidirectionalLineInfo.Clear(); + mImpl->mLogicalModel->mLogicalToVisualMap.Clear(); + mImpl->mLogicalModel->mVisualToLogicalMap.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(); // Delte at current cursor position Vector& modifyText = mImpl->mLogicalModel->mText; @@ -915,6 +1101,9 @@ void Controller::DeleteTextEvent() ALIGN | UPDATE_ACTUAL_SIZE | REORDER ); + + // Queue a cursor reposition event; this must wait until after DoRelayout() + mImpl->mTextInput->mUpdateCursorPosition = true; } void Controller::UpdateModel( OperationsMask operationsRequired ) @@ -1286,6 +1475,22 @@ void Controller::TapEvent( unsigned int tapCount, float x, float y ) } } +void Controller::PanEvent( Gesture::State state, const Vector2& displacement ) +{ + DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected PanEvent" ); + + if( mImpl->mTextInput ) + { + TextInput::Event event( TextInput::PAN_EVENT ); + event.p1.mInt = state; + event.p2.mFloat = displacement.x; + event.p3.mFloat = displacement.y; + mImpl->mTextInput->mEventQueue.push_back( event ); + + RequestRelayout(); + } +} + void Controller::GrabHandleEvent( GrabHandleState state, float x, float y ) { DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" ); diff --git a/dali-toolkit/internal/text/text-controller.h b/dali-toolkit/internal/text/text-controller.h index e1d831e..cea9f39 100644 --- a/dali-toolkit/internal/text/text-controller.h +++ b/dali-toolkit/internal/text/text-controller.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -195,6 +196,13 @@ public: bool GetEnableCursorBlink() const; /** + * @brief Query the current scroll position; the UI control is responsible for moving actors to this position. + * + * @return The scroll position. + */ + const Vector2& GetScrollPosition() const; + + /** * @copydoc Control::GetNaturalSize() */ Vector3 GetNaturalSize(); @@ -299,6 +307,14 @@ public: void TapEvent( unsigned int tapCount, float x, float y ); /** + * @brief Caller by editable UI controls when a pan gesture occurs. + * + * @param[in] state The state of the gesture. + * @param[in] displacement This distance panned since the last pan gesture. + */ + void PanEvent( Gesture::State state, const Vector2& displacement ); + + /** * @copydoc Dali::Toolkit::Text::Decorator::Observer::GrabHandleEvent() */ virtual void GrabHandleEvent( GrabHandleState state, float x, float y ); diff --git a/dali-toolkit/internal/text/text-io.cpp b/dali-toolkit/internal/text/text-io.cpp index 9e0a0b5..3a0fe9b 100644 --- a/dali-toolkit/internal/text/text-io.cpp +++ b/dali-toolkit/internal/text/text-io.cpp @@ -88,6 +88,24 @@ std::ostream& operator<< (std::ostream& o, const Vector& fontRun) return o << std::dec; } +std::ostream& operator<< (std::ostream& o, const Vector& lineRuns) +{ + for( unsigned int i=0; i9 Characters: 0->9 (10)" for a ten character run staring from beginning of the model + o << "Line " << i << " Glyphs: " << lineRuns[i].glyphIndex << "->" << (lineRuns[i].glyphIndex + lineRuns[i].numberOfGlyphs ); + o << " Characters: " << lineRuns[i].characterRun.characterIndex << "->" << (lineRuns[i].characterRun.characterIndex + lineRuns[i].characterRun.numberOfCharacters ); + o << " Size: " << lineRuns[i].lineSize; + + if( i+1 < lineRuns.Count() ) + { + o << ", "; + } + } + + return o << std::dec; +} + } // namespace Text } // namespace Toolkit diff --git a/dali-toolkit/internal/text/text-io.h b/dali-toolkit/internal/text/text-io.h index b65d917..25620e0 100644 --- a/dali-toolkit/internal/text/text-io.h +++ b/dali-toolkit/internal/text/text-io.h @@ -24,6 +24,7 @@ // INTERNAL INCLUDES #include +#include #include #include @@ -63,6 +64,15 @@ std::ostream& operator<< (std::ostream& o, const Vector& scriptRuns); */ std::ostream& operator<< (std::ostream& o, const Vector& fontRuns); +/** + * @brief Print a vector of line runs. + * + * @param [in] o The output stream operator. + * @param [in] lineRuns The line runs to print. + * @return The output stream operator. + */ +std::ostream& operator<< (std::ostream& o, const Vector& lineRuns); + } // namespace Text } // namespace Toolkit diff --git a/dali-toolkit/public-api/controls/text-controls/text-field.h b/dali-toolkit/public-api/controls/text-controls/text-field.h index 3721c4b..2e37724 100644 --- a/dali-toolkit/public-api/controls/text-controls/text-field.h +++ b/dali-toolkit/public-api/controls/text-controls/text-field.h @@ -62,7 +62,6 @@ public: FONT_STYLE, ///< name "font-style", The requested font style e.g. Regular/Italic, type STRING POINT_SIZE, ///< name "point-size", The size of font in points, type FLOAT EXCEED_POLICY, ///< name "exceed-policy" Specifies how the text is truncated when it does not fit, type INT - CURSOR_IMAGE, ///< name "cursor-image", The image to display for cursors, type STRING PRIMARY_CURSOR_COLOR, ///< name "primary-cursor-color", The color to apply to the primary cursor, type VECTOR4 SECONDARY_CURSOR_COLOR, ///< name "secondary-cursor-color", The color to apply to the secondary cursor, type VECTOR4 ENABLE_CURSOR_BLINK, ///< name "enable-cursor-blink", Whether the cursor should blink or not, type BOOLEAN -- 2.7.4