From: Agnelo Vaz Date: Wed, 22 Apr 2015 15:58:46 +0000 (+0100) Subject: TextField to limit Input to maximum characters and emit signal X-Git-Tag: dali_1.0.40~3 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F56%2F38656%2F10;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git TextField to limit Input to maximum characters and emit signal Change-Id: I592740c84c1121b2e5c9d3f779b4639236fa4ea6 Signed-off-by: Agnelo Vaz --- diff --git a/automated-tests/src/dali-toolkit/utc-Dali-TextField.cpp b/automated-tests/src/dali-toolkit/utc-Dali-TextField.cpp index 9c3d4f2..bf3111d 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-TextField.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-TextField.cpp @@ -36,22 +36,22 @@ void dali_textfield_cleanup(void) namespace { -const char* const PROPERTY_NAME_RENDERING_BACKEND = "rendering-backend"; -const char* const PROPERTY_NAME_PLACEHOLDER_TEXT = "placeholder-text"; -const char* const PROPERTY_NAME_TEXT = "text"; -const char* const PROPERTY_NAME_FONT_FAMILY = "font-family"; -const char* const PROPERTY_NAME_FONT_STYLE = "font-style"; -const char* const PROPERTY_NAME_POINT_SIZE = "point-size"; -const char* const PROPERTY_NAME_EXCEED_POLICY = "exceed-policy"; -const char* const PROPERTY_NAME_PRIMARY_CURSOR_COLOR = "primary-cursor-color"; -const char* const PROPERTY_NAME_SECONDARY_CURSOR_COLOR = "secondary-cursor-color"; -const char* const PROPERTY_NAME_ENABLE_CURSOR_BLINK = "enable-cursor-blink"; -const char* const PROPERTY_NAME_CURSOR_BLINK_INTERVAL = "cursor-blink-interval"; -const char* const PROPERTY_NAME_CURSOR_BLINK_DURATION = "cursor-blink-duration"; -const char* const PROPERTY_NAME_GRAB_HANDLE_IMAGE = "grab-handle-image"; +const char* const PROPERTY_NAME_RENDERING_BACKEND = "rendering-backend"; +const char* const PROPERTY_NAME_PLACEHOLDER_TEXT = "placeholder-text"; +const char* const PROPERTY_NAME_TEXT = "text"; +const char* const PROPERTY_NAME_FONT_FAMILY = "font-family"; +const char* const PROPERTY_NAME_FONT_STYLE = "font-style"; +const char* const PROPERTY_NAME_POINT_SIZE = "point-size"; +const char* const PROPERTY_NAME_EXCEED_POLICY = "exceed-policy"; +const char* const PROPERTY_NAME_PRIMARY_CURSOR_COLOR = "primary-cursor-color"; +const char* const PROPERTY_NAME_SECONDARY_CURSOR_COLOR = "secondary-cursor-color"; +const char* const PROPERTY_NAME_ENABLE_CURSOR_BLINK = "enable-cursor-blink"; +const char* const PROPERTY_NAME_CURSOR_BLINK_INTERVAL = "cursor-blink-interval"; +const char* const PROPERTY_NAME_CURSOR_BLINK_DURATION = "cursor-blink-duration"; +const char* const PROPERTY_NAME_GRAB_HANDLE_IMAGE = "grab-handle-image"; const char* const PROPERTY_NAME_DECORATION_BOUNDING_BOX = "decoration-bounding-box"; -const char* const PROPERTY_NAME_HORIZONTAL_ALIGNMENT = "horizontal-alignment"; -const char* const PROPERTY_NAME_VERTICAL_ALIGNMENT = "vertical-alignment"; +const char* const PROPERTY_NAME_HORIZONTAL_ALIGNMENT = "horizontal-alignment"; +const char* const PROPERTY_NAME_VERTICAL_ALIGNMENT = "vertical-alignment"; } // namespace @@ -236,6 +236,11 @@ int UtcDaliTextFieldSetPropertyP(void) // Set Grab Handle image field.SetProperty( TextField::Property::GRAB_HANDLE_IMAGE, "" ); + // Check that the MAX_LENGTH property can be correctly set + const int maxNumberOfCharacters = 20; + field.SetProperty( TextField::Property::MAX_LENGTH, maxNumberOfCharacters ); + DALI_TEST_EQUALS( field.GetProperty( TextField::Property::MAX_LENGTH ), maxNumberOfCharacters, TEST_LOCATION ); + END_TEST; } 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 bb2fb04..d6e763d 100644 --- a/dali-toolkit/internal/controls/text-controls/text-field-impl.cpp +++ b/dali-toolkit/internal/controls/text-controls/text-field-impl.cpp @@ -20,6 +20,7 @@ // EXTERNAL INCLUDES #include +#include #include #include #include @@ -85,6 +86,7 @@ DALI_PROPERTY_REGISTRATION( Toolkit, TextField, "text", DALI_PROPERTY_REGISTRATION( Toolkit, TextField, "font-family", STRING, FONT_FAMILY ) DALI_PROPERTY_REGISTRATION( Toolkit, TextField, "font-style", STRING, FONT_STYLE ) DALI_PROPERTY_REGISTRATION( Toolkit, TextField, "point-size", FLOAT, POINT_SIZE ) +DALI_PROPERTY_REGISTRATION( Toolkit, TextField, "max-length", INTEGER, MAX_LENGTH ) DALI_PROPERTY_REGISTRATION( Toolkit, TextField, "exceed-policy", INTEGER, EXCEED_POLICY ) DALI_PROPERTY_REGISTRATION( Toolkit, TextField, "horizontal-alignment", STRING, HORIZONTAL_ALIGNMENT ) DALI_PROPERTY_REGISTRATION( Toolkit, TextField, "vertical-alignment", STRING, VERTICAL_ALIGNMENT ) @@ -107,6 +109,8 @@ DALI_PROPERTY_REGISTRATION( Toolkit, TextField, "selection-handle-pressed-image- DALI_PROPERTY_REGISTRATION( Toolkit, TextField, "selection-highlight-color", STRING, SELECTION_HIGHLIGHT_COLOR ) DALI_PROPERTY_REGISTRATION( Toolkit, TextField, "decoration-bounding-box", RECTANGLE, DECORATION_BOUNDING_BOX ) +DALI_SIGNAL_REGISTRATION( Toolkit, TextField, "max-length-reached", SIGNAL_MAX_LENGTH_REACHED ) + DALI_TYPE_REGISTRATION_END() } // namespace @@ -415,6 +419,14 @@ void TextField::SetProperty( BaseObject* object, Property::Index index, const Pr } break; } + case Toolkit::TextField::Property::MAX_LENGTH: + { + if( impl.mController ) + { + impl.mController->SetMaximumNumberOfCharacters( value.Get< int >() ); + } + break; + } } // switch } // textfield } @@ -646,12 +658,45 @@ Property::Value TextField::GetProperty( BaseObject* object, Property::Index inde } break; } + case Toolkit::TextField::Property::MAX_LENGTH: + { + if( impl.mController ) + { + value = impl.mController->GetMaximumNumberOfCharacters(); + } + break; + } } //switch } return value; } +bool TextField::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ) +{ + Dali::BaseHandle handle( object ); + + bool connected( true ); + Toolkit::TextField field = Toolkit::TextField::DownCast( handle ); + + if( 0 == strcmp( signalName.c_str(), SIGNAL_MAX_LENGTH_REACHED ) ) + { + field.MaxLengthReachedSignal().Connect( tracker, functor ); + } + else + { + // signalName does not match any signal + connected = false; + } + + return connected; +} + +Toolkit::TextField::MaxLengthReachedSignalType& TextField::MaxLengthReachedSignal() +{ + return mMaxLengthReachedSignal; +} + void TextField::OnInitialize() { Actor self = Self(); @@ -842,6 +887,12 @@ void TextField::RequestTextRelayout() RelayoutRequest(); } +void TextField::MaxLengthReached() +{ + Dali::Toolkit::TextField handle( GetOwner() ); + mMaxLengthReachedSignal.Emit( handle ); +} + void TextField::EnableClipping( bool clipping, const Vector2& size ) { if( clipping ) 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 5428275..63ded84 100644 --- a/dali-toolkit/internal/controls/text-controls/text-field-impl.h +++ b/dali-toolkit/internal/controls/text-controls/text-field-impl.h @@ -71,6 +71,22 @@ public: */ static Property::Value GetProperty( BaseObject* object, Property::Index index ); + /** + * Connects a callback function with the object's signals. + * @param[in] object The object providing the signal. + * @param[in] tracker Used to disconnect the signal. + * @param[in] signalName The signal to connect to. + * @param[in] functor A newly allocated FunctorDelegate. + * @return True if the signal was connected. + * @post If a signal was connected, ownership of functor was passed to CallbackBase. Otherwise the caller is responsible for deleting the unused functor. + */ + static bool DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ); + + /** + * @copydoc TextField::MaxLengthReachedSignal() + */ + Toolkit::TextField::MaxLengthReachedSignalType& MaxLengthReachedSignal(); + private: // From Control /** @@ -137,6 +153,11 @@ private: // From Control */ virtual void RequestTextRelayout(); + /** + * @copydoc Text::ControlInterface::MaxLengthReached() + */ + virtual void MaxLengthReached(); + private: // Implementation /** @@ -164,14 +185,15 @@ private: // Implementation */ virtual ~TextField(); -private: - // Undefined copy constructor and assignment operators TextField(const TextField&); TextField& operator=(const TextField& rhs); private: // Data + // Signals + Toolkit::TextField::MaxLengthReachedSignalType mMaxLengthReachedSignal; + Text::ControllerPtr mController; Text::RendererPtr mRenderer; Text::DecoratorPtr mDecorator; diff --git a/dali-toolkit/internal/controls/text-controls/text-label-impl.cpp b/dali-toolkit/internal/controls/text-controls/text-label-impl.cpp index 44bd0ae..3f0cf0d 100644 --- a/dali-toolkit/internal/controls/text-controls/text-label-impl.cpp +++ b/dali-toolkit/internal/controls/text-controls/text-label-impl.cpp @@ -481,6 +481,11 @@ void TextLabel::RequestTextRelayout() RelayoutRequest(); } +void TextLabel::MaxLengthReached() +{ + // Pure Virtual from TextController Interface, only needed when inputting text +} + TextLabel::TextLabel() : Control( ControlBehaviour( REQUIRES_STYLE_CHANGE_SIGNALS ) ), mRenderingBackend( DEFAULT_RENDERING_BACKEND ) diff --git a/dali-toolkit/internal/controls/text-controls/text-label-impl.h b/dali-toolkit/internal/controls/text-controls/text-label-impl.h index c69bd7c..54c2554 100644 --- a/dali-toolkit/internal/controls/text-controls/text-label-impl.h +++ b/dali-toolkit/internal/controls/text-controls/text-label-impl.h @@ -97,6 +97,11 @@ private: // From Control */ virtual void RequestTextRelayout(); + /** + * @copydoc Text::ControlInterface::MaxLengthReached() + */ + virtual void MaxLengthReached(); + private: // Implementation /** diff --git a/dali-toolkit/internal/text/text-control-interface.h b/dali-toolkit/internal/text/text-control-interface.h index 3eb26b6..d7ed8f4 100644 --- a/dali-toolkit/internal/text/text-control-interface.h +++ b/dali-toolkit/internal/text/text-control-interface.h @@ -48,6 +48,11 @@ public: * @brief Called to request a text relayout. */ virtual void RequestTextRelayout() = 0; + + /** + * @brief Called when the number of characters to be inserted exceeds the maximum limit + */ + virtual void MaxLengthReached() = 0; }; } // namespace Text diff --git a/dali-toolkit/internal/text/text-controller-impl.h b/dali-toolkit/internal/text/text-controller-impl.h index 103013a..51927a6 100644 --- a/dali-toolkit/internal/text/text-controller-impl.h +++ b/dali-toolkit/internal/text/text-controller-impl.h @@ -195,6 +195,7 @@ struct Controller::Impl mControlSize(), mAlignmentOffset(), mOperationsPending( NO_OPERATION ), + mMaximumNumberOfCharacters( 50 ), mRecalculateNaturalSize( true ) { mLogicalModel = LogicalModel::New(); @@ -222,7 +223,6 @@ struct Controller::Impl */ void RequestRelayout(); - /** * @brief Helper to move the cursor, grab handle etc. */ @@ -330,6 +330,7 @@ struct Controller::Impl 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. + Length mMaximumNumberOfCharacters; ///< Maximum number of characters that can be inserted. bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated. }; diff --git a/dali-toolkit/internal/text/text-controller.cpp b/dali-toolkit/internal/text/text-controller.cpp index da7da29..d599bdd 100644 --- a/dali-toolkit/internal/text/text-controller.cpp +++ b/dali-toolkit/internal/text/text-controller.cpp @@ -106,6 +106,19 @@ void Controller::GetPlaceholderText( std::string& text ) const } } +void Controller::SetMaximumNumberOfCharacters( int maxCharacters ) +{ + if ( maxCharacters >= 0 ) + { + mImpl->mMaximumNumberOfCharacters = maxCharacters; + } +} + +int Controller::GetMaximumNumberOfCharacters() +{ + return mImpl->mMaximumNumberOfCharacters; +} + void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily ) { if( !mImpl->mFontDefaults ) @@ -597,21 +610,26 @@ void Controller::InsertTextEvent( const std::string& text ) Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() ); utf32Characters.Resize( characterCount ); + const Length numberOfCharactersInModel = mImpl->mLogicalModel->GetNumberOfCharacters(); + + // Restrict new text to fit within Maximum characters setting + Length maxSizeOfNewText = std::min ( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount ); + // Insert at current cursor position - Vector& modifyText = mImpl->mLogicalModel->mText; CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition; - if( cursorIndex < modifyText.Count() ) + Vector& modifyText = mImpl->mLogicalModel->mText; + + if( cursorIndex < numberOfCharactersInModel ) { - modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.End() ); + modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin()+ maxSizeOfNewText ); } else { - modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.End() ); + modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText ); } - // Advance the cursor position - ++cursorIndex; + cursorIndex += maxSizeOfNewText; // The natural size needs to be re-calculated. mImpl->mRecalculateNaturalSize = true; @@ -627,6 +645,11 @@ void Controller::InsertTextEvent( const std::string& text ) // Queue a cursor reposition event; this must wait until after DoRelayout() mImpl->mEventData->mUpdateCursorPosition = true; mImpl->mEventData->mScrollAfterUpdateCursorPosition = true; + + if ( characterCount > maxSizeOfNewText ) + { + mImpl->mControlInterface.MaxLengthReached(); + } } void Controller::DeleteTextEvent() diff --git a/dali-toolkit/internal/text/text-controller.h b/dali-toolkit/internal/text/text-controller.h index bb44698..5ef498d 100644 --- a/dali-toolkit/internal/text/text-controller.h +++ b/dali-toolkit/internal/text/text-controller.h @@ -123,6 +123,20 @@ public: void GetPlaceholderText( std::string& text ) const; /** + * @brief Sets the maximum number of characters that can be inserted into the TextModel + * + * @param[in] maxCharacters maximum number of characters to be accepted + */ + void SetMaximumNumberOfCharacters( int maxCharacters ); + + /** + * @brief Sets the maximum number of characters that can be inserted into the TextModel + * + * @param[in] maxCharacters maximum number of characters to be accepted + */ + int GetMaximumNumberOfCharacters(); + + /** * @brief Set the default font family. * * @param[in] defaultFontFamily The default font family. diff --git a/dali-toolkit/public-api/controls/text-controls/text-field.cpp b/dali-toolkit/public-api/controls/text-controls/text-field.cpp index 7935752..5340137 100644 --- a/dali-toolkit/public-api/controls/text-controls/text-field.cpp +++ b/dali-toolkit/public-api/controls/text-controls/text-field.cpp @@ -59,6 +59,11 @@ TextField TextField::DownCast( BaseHandle handle ) return Control::DownCast(handle); } +TextField::MaxLengthReachedSignalType& TextField::MaxLengthReachedSignal() +{ + return Dali::Toolkit::GetImpl( *this ).MaxLengthReachedSignal(); +} + TextField::TextField( Internal::TextField& implementation ) : Control(implementation) { 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 8eac8f7..0af8dc0 100644 --- a/dali-toolkit/public-api/controls/text-controls/text-field.h +++ b/dali-toolkit/public-api/controls/text-controls/text-field.h @@ -34,6 +34,12 @@ class TextField; /** * @brief A control which provides a single-line editable text field. + * + * * Signals + * | %Signal Name | Method | + * |------------------------|-----------------------------------------------------| + * | max-length-reached | @ref MaxLengthReachedSignal() | + * */ class DALI_IMPORT_API TextField : public Control { @@ -61,7 +67,8 @@ public: FONT_FAMILY, ///< name "font-family", The requested font family, type STRING 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 + MAX_LENGTH, ///< name "max-length" The maximum number of characters that can be inserted, type INTEGER + EXCEED_POLICY, ///< name "exceed-policy" Specifies how the text is truncated when it does not fit, type INTEGER HORIZONTAL_ALIGNMENT, ///< name "horizontal-alignment", The line horizontal alignment, type STRING, values "BEGIN", "CENTER", "END" VERTICAL_ALIGNMENT, ///< name "vertical-alignment", The line vertical alignment, type STRING, values "TOP", "CENTER", "BOTTOM" TEXT_COLOR, ///< name "text-color", The text color, type VECTOR4 @@ -81,7 +88,7 @@ public: SELECTION_HANDLE_PRESSED_IMAGE_LEFT, ///< name "selection-handle-pressed-image-left" The image to display when the left selection handle is pressed, type STRING SELECTION_HANDLE_PRESSED_IMAGE_RIGHT, ///< name "selection-handle-pressed-image-right" The image to display when the right selection handle is pressed, type STRING SELECTION_HIGHLIGHT_COLOR, ///< name "selection-highlight-color" The color of the selection highlight, type VECTOR4 - DECORATION_BOUNDING_BOX ///< name "decoration-bounding-box", The decorations (handles etc) will positioned within this area on-screen, type RECTANGLE + DECORATION_BOUNDING_BOX ///< name "decoration-bounding-box" The decorations (handles etc) will positioned within this area on-screen, type RECTANGLE }; }; @@ -96,6 +103,11 @@ public: EXCEED_POLICY_CLIP ///< The end of text will be clipped to fit within the TextField. }; + // Type Defs + + /// @brief Max Characters Exceed signal type; + typedef Signal MaxLengthReachedSignalType; + /** * Create the TextField control. * @return A handle to the TextField control. @@ -140,6 +152,19 @@ public: */ static TextField DownCast( BaseHandle handle ); + // Signals + + /** + * @brief This signal is emitted when inserted text exceeds the maximum character limit. + * + * A callback of the following type may be connected: + * @code + * void YourCallbackName( TextField textField ); + * @endcode + * @return The signal to connect to. + */ + MaxLengthReachedSignalType& MaxLengthReachedSignal(); + public: // Not intended for application developers /**