+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( LogicalModelPtr logicalModel,
+ VisualModelPtr visualModel,
+ DecoratorPtr decorator )
+ : mLogicalModel( logicalModel ),
+ mVisualModel( visualModel ),
+ 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->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
+ mDecorator->StartCursorBlink();
+ mDecorator->SetGrabHandleActive( true );
+
+ float xPosition = event.p2.mFloat;
+ float yPosition = event.p3.mFloat;
+ float height(0.0f);
+ GetClosestCursorPosition( xPosition, yPosition, height );
+ mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
+
+ mDecoratorUpdated = true;
+ }
+ else if( 2u == event.p1.mUint )
+ {
+ mState = TextInput::SELECTING;
+ mDecorator->SetGrabHandleActive( false );
+ mDecorator->SetSelectionActive( true );
+ mDecoratorUpdated = true;
+ }
+ }
+
+ void OnGrabHandleEvent( const Event& event )
+ {
+ unsigned int state = event.p1.mUint;
+
+ if( GRAB_HANDLE_PRESSED == state )
+ {
+ float xPosition = event.p2.mFloat;
+ float yPosition = event.p3.mFloat;
+ float height(0.0f);
+
+ GetClosestCursorPosition( xPosition, yPosition, height );
+
+ mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
+ mDecoratorUpdated = true;
+ }
+ }
+
+ void GetClosestCursorPosition( float& x, float& y, float& height )
+ {
+ // TODO - Look at LineRuns first
+
+ Text::Length numberOfGlyphs = mVisualModel->GetNumberOfGlyphs();
+ if( 0 == numberOfGlyphs )
+ {
+ return;
+ }
+
+ Vector<GlyphInfo> glyphs;
+ glyphs.Resize( numberOfGlyphs );
+ mVisualModel->GetGlyphs( &glyphs[0], 0, numberOfGlyphs );
+
+ std::vector<Vector2> positions;
+ positions.resize( numberOfGlyphs );
+ mVisualModel->GetGlyphPositions( &positions[0], 0, numberOfGlyphs );
+
+ unsigned int closestGlyph = 0;
+ float closestDistance = std::numeric_limits<float>::max();
+
+ for( unsigned int i=0; i<glyphs.Count(); ++i )
+ {
+ float glyphX = positions[i].x + glyphs[i].width*0.5f;
+ float glyphY = positions[i].y + glyphs[i].height*0.5f;
+
+ float distanceToGlyph = fabsf( glyphX - x ) + fabsf( glyphY - y );
+
+ if( distanceToGlyph < closestDistance )
+ {
+ closestDistance = distanceToGlyph;
+ closestGlyph = i;
+ }
+ }
+
+ // TODO - Consider RTL languages
+ x = positions[closestGlyph].x + glyphs[closestGlyph].width;
+ y = 0.0f;
+
+ FontMetrics metrics;
+ TextAbstraction::FontClient::Get().GetFontMetrics( glyphs[closestGlyph].fontId, metrics );
+ height = metrics.height; // TODO - Fix for multi-line
+ }
+
+ LogicalModelPtr mLogicalModel;
+ VisualModelPtr mVisualModel;
+ DecoratorPtr mDecorator;
+
+ 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.
+
+ bool mDecoratorUpdated;
+};
+