#include <dali-toolkit/public-api/text/visual-model.h>
// EXTERNAL INCLUDES
-#include <dali/public-api/text-abstraction/font-client.h>
#include <limits>
+#include <vector>
+#include <dali/public-api/text-abstraction/font-client.h>
+
+using std::vector;
namespace Dali
{
namespace Text
{
+struct Controller::TextInput
+{
+ // Used to queue input events until DoRelayout()
+ enum EventType
+ {
+ KEYBOARD_FOCUS_GAIN_EVENT,
+ KEYBOARD_FOCUS_LOST_EVENT,
+ TAP_EVENT,
+ GRAB_HANDLE_EVENT
+ };
+
+ union Param
+ {
+ int mInt;
+ unsigned int mUint;
+ float mFloat;
+ };
+
+ struct Event
+ {
+ Event( EventType eventType )
+ : type( eventType )
+ {
+ p1.mInt = 0;
+ p2.mInt = 0;
+ }
+
+ EventType type;
+ Param p1;
+ Param p2;
+ Param p3;
+ };
+
+ enum State
+ {
+ INACTIVE,
+ SELECTING,
+ EDITING
+ };
+
+ TextInput( DecoratorPtr decorator )
+ : mDecorator( decorator ),
+ mState( INACTIVE )
+ {
+ }
+
+ /**
+ * @brief Helper to move the cursor, grab handle etc.
+ */
+ bool ProcessTouchEvents()
+ {
+ mDecoratorUpdated = false;
+
+ if( mDecorator )
+ {
+ for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
+ {
+ switch( iter->type )
+ {
+ case KEYBOARD_FOCUS_GAIN_EVENT:
+ {
+ OnKeyboardFocus( true );
+ break;
+ }
+ case KEYBOARD_FOCUS_LOST_EVENT:
+ {
+ OnKeyboardFocus( false );
+ break;
+ }
+ case TAP_EVENT:
+ {
+ OnTapEvent( *iter );
+ break;
+ }
+ case GRAB_HANDLE_EVENT:
+ {
+ OnGrabHandleEvent( *iter );
+ break;
+ }
+ }
+ }
+ }
+
+ mEventQueue.clear();
+
+ return mDecoratorUpdated;
+ }
+
+ void OnKeyboardFocus( bool hasFocus )
+ {
+ // TODO
+ }
+
+ void OnTapEvent( const Event& event )
+ {
+ if( 1u == event.p1.mUint )
+ {
+ mState = TextInput::EDITING;
+ mDecorator->SetGrabHandleActive( true );
+ mDecorator->SetPosition( PRIMARY_CURSOR, 0, 0, 10 );
+ mDecoratorUpdated = true;
+ }
+ else if( 2u == event.p1.mUint )
+ {
+ mState = TextInput::SELECTING;
+ mDecorator->SetGrabHandleActive( false );
+ mDecorator->SetSelectionActive( true );
+ mDecoratorUpdated = true;
+ }
+ }
+
+ void OnGrabHandleEvent( const Event& event )
+ {
+ // TODO
+ }
+
+ DecoratorPtr mDecorator;
+ bool mDecoratorUpdated;
+
+ State mState;
+
+ /**
+ * 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.
+};
+
struct Controller::Impl
{
- Impl()
- : mNewText(),
+ Impl( ControlInterface& controlInterface )
+ : mControlInterface( controlInterface ),
+ mNewText(),
mOperations( NO_OPERATION ),
- mControlSize()
+ mControlSize(),
+ mTextInput( NULL )
{
mLogicalModel = LogicalModel::New();
mVisualModel = VisualModel::New();
mFontClient = TextAbstraction::FontClient::Get();
}
+ ~Impl()
+ {
+ delete mTextInput;
+ }
+
+ ControlInterface& mControlInterface;
+
std::string mNewText;
LogicalModelPtr mLogicalModel;
OperationsMask mOperations;
Size mControlSize;
+
+ // Avoid allocating everything for text input until EnableTextInput()
+ Controller::TextInput* mTextInput;
};
-ControllerPtr Controller::New()
+ControllerPtr Controller::New( ControlInterface& controlInterface )
{
- return ControllerPtr( new Controller() );
+ return ControllerPtr( new Controller( controlInterface ) );
}
void Controller::SetText( const std::string& text )
// Keep until size negotiation
mImpl->mNewText = text;
mImpl->mOperations = ALL_OPERATIONS;
+
+ if( mImpl->mTextInput )
+ {
+ // Cancel previously queued events
+ mImpl->mTextInput->mEventQueue.clear();
+
+ // TODO - Hide selection decorations
+ }
+}
+
+void Controller::EnableTextInput( DecoratorPtr decorator )
+{
+ if( !mImpl->mTextInput )
+ {
+ mImpl->mTextInput = new TextInput( decorator );
+ }
}
bool Controller::Relayout( const Vector2& size )
return false;
}
- bool viewUpdated = false;
+ bool updated = false;
if( size != mImpl->mControlSize )
{
- viewUpdated = DoRelayout( size, mImpl->mOperations );
+ updated = DoRelayout( size, mImpl->mOperations );
// Do not re-do any operation until something changes.
mImpl->mOperations = NO_OPERATION;
mImpl->mControlSize = size;
}
- return viewUpdated;
+ if( mImpl->mTextInput )
+ {
+ // Move the cursor, grab handle etc.
+ updated = mImpl->mTextInput->ProcessTouchEvents() || updated;
+ }
+
+ return updated;
}
bool Controller::DoRelayout( const Vector2& size, OperationsMask operations )
return mImpl->mLayoutEngine;
}
+void Controller::RequestRelayout()
+{
+ mImpl->mControlInterface.RequestTextRelayout();
+}
+
+void Controller::KeyboardFocusGainEvent()
+{
+ DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
+
+ if( mImpl->mTextInput )
+ {
+ TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
+ mImpl->mTextInput->mEventQueue.push_back( event );
+
+ RequestRelayout();
+ }
+}
+
+void Controller::KeyboardFocusLostEvent()
+{
+ DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
+
+ if( mImpl->mTextInput )
+ {
+ TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
+ mImpl->mTextInput->mEventQueue.push_back( event );
+
+ RequestRelayout();
+ }
+}
+
+void Controller::TapEvent( unsigned int tapCount, float x, float y )
+{
+ DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
+
+ if( mImpl->mTextInput )
+ {
+ TextInput::Event event( TextInput::TAP_EVENT );
+ event.p1.mUint = tapCount;
+ event.p2.mFloat = x;
+ event.p3.mFloat = y;
+ mImpl->mTextInput->mEventQueue.push_back( event );
+
+ RequestRelayout();
+ }
+}
+
+void Controller::GrabHandleEvent( GrabHandleState state, float x )
+{
+ DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
+
+ if( mImpl->mTextInput )
+ {
+ TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
+ event.p1.mInt = state;
+ event.p2.mFloat = x;
+ mImpl->mTextInput->mEventQueue.push_back( event );
+
+ RequestRelayout();
+ }
+}
+
Controller::~Controller()
{
delete mImpl;
}
-Controller::Controller()
+Controller::Controller( ControlInterface& controlInterface )
: mImpl( NULL )
{
- mImpl = new Controller::Impl();
+ mImpl = new Controller::Impl( controlInterface );
}
} // namespace Text