2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 // Licensed under the Flora License, Version 1.0 (the License);
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
8 // http://floralicense.org/license/
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an AS IS BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
18 #include <dali-toolkit/internal/controls/text-input/text-input-text-highlight-impl.h>
20 #include <dali/dali.h>
22 #include <dali/integration-api/debug.h>
35 * Selection state enumeration (FSM)
39 SelectionNone, ///< Currently not encountered selected section.
40 SelectionStarted, ///< Encountered selected section
41 SelectionFinished ///< Finished selected section
44 const Vector4 LIGHTBLUE( 10.0f/255.0f, 140.0f/255.0f, 210.0f/255.0f, 1.0f ); // todo make this a setting
46 const float CHARACTER_THRESHOLD( 2.5f ); // todo check if unified method to do this in Text
58 // Default constructor
59 TextHighlight::TextHighlight( TextViewCharacterPositioning& textViewCharacterPositioning ) :
60 mTextViewCharacterPositioning( textViewCharacterPositioning )
64 TextHighlight::~TextHighlight()
68 void TextHighlight::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection,
69 Toolkit::TextView::TextLayoutInfo& textLayoutInfo )
71 std::vector<int>::iterator it = textLayoutInfo.mCharacterLogicalToVisualMap.begin();
72 std::vector<int>::iterator startSelectionIt = textLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
73 std::vector<int>::iterator endSelectionIt = textLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
74 std::vector<int>::iterator end = textLayoutInfo.mCharacterLogicalToVisualMap.end();
76 selectedVisualText.resize( mTextViewCharacterPositioning.GetNumberOfCharactersInText() );
78 // Deselect text prior to startSelectionIt
79 for(;it!=startSelectionIt;++it)
81 selectedVisualText[*it] = false;
84 // Select text from startSelectionIt -> endSelectionIt
85 for(;it!=endSelectionIt;++it)
87 selectedVisualText[*it] = true;
90 // Deselect text after endSelection
93 selectedVisualText[*it] = false;
96 selectedVisualText.resize( mTextViewCharacterPositioning.GetNumberOfCharactersInText(), false );
99 // Calculate the dimensions of the quads they will make the highlight mesh
100 TextHighlight::HighlightInfo TextHighlight::CalculateHighlightInfo( std::size_t handlePositionStart, std::size_t handlePositionEnd, Toolkit::TextView::TextLayoutInfo& textLayoutInfo )
102 // At the moment there is no public API to modify the block alignment option.
103 const bool blockAlignEnabled = true;
105 TextHighlight::HighlightInfo newHighlightInfo;
106 //newHighlightInfo.mQuadList.clear(); // clear last quad information.
108 if ( !mTextViewCharacterPositioning.IsTextEmpty() && !textLayoutInfo.mCharacterLogicalToVisualMap.empty() )
110 // Get vector of flags representing characters that are selected (true) vs unselected (false).
111 std::vector<bool> selectedVisualText;
112 GetVisualTextSelection(selectedVisualText, handlePositionStart, handlePositionEnd, textLayoutInfo );
113 std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
114 std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
116 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
117 float rowLeft = 0.0f;
118 float rowRight = 0.0f;
119 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
120 float maxRowLeft = std::numeric_limits<float>::max();
121 float maxRowRight = 0.0f;
123 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = textLayoutInfo.mCharacterLayoutInfoTable.begin();
124 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = textLayoutInfo.mCharacterLayoutInfoTable.end();
126 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
128 // Scan through entire text.
131 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
133 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
134 bool charSelected( false );
135 if( selectedIt != selectedEndIt )
137 charSelected = *selectedIt++;
140 if(selectionState == SelectionNone)
144 selectionState = SelectionStarted;
145 rowLeft = charInfo.mPosition.x - textLayoutInfo.mScrollOffset.x;
146 rowRight = rowLeft + charInfo.mSize.width;
149 else if(selectionState == SelectionStarted)
151 // break selection on:
152 // 1. new line causing selection break. (\n or wordwrap)
153 // 2. character not selected.
154 if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ||
157 // finished selection.
158 // TODO: TextView should have a table of visual rows, and each character a reference to the row
159 // that it resides on. That way this enumeration is not necessary.
161 if(lastIt->mIsNewLineChar)
163 // If the last character is a new line, then to get the row rect, we need to scan from the character before the new line.
164 lastIt = std::max( textLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
166 const Size rowSize( mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( lastIt - textLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
167 maxRowLeft = std::min(maxRowLeft, min.x);
168 maxRowRight = std::max(maxRowRight, max.x);
169 float rowBottom = lastIt->mPosition.y - textLayoutInfo.mScrollOffset.y;
170 float rowTop = rowBottom - rowSize.height;
172 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
173 if(charSelected && blockAlignEnabled)
175 rowRight = std::numeric_limits<float>::max();
177 newHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
179 selectionState = SelectionNone;
181 // Still selected? start a new selection
184 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
185 rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - textLayoutInfo.mScrollOffset.x;
186 rowRight = ( charInfo.mPosition.x - textLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
187 selectionState = SelectionStarted;
192 // build up highlight(s) with this selection data.
193 rowLeft = std::min( charInfo.mPosition.x - textLayoutInfo.mScrollOffset.x, rowLeft );
194 rowRight = std::max( ( charInfo.mPosition.x - textLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
201 // If reached end, and still on selection, then close selection.
204 if(selectionState == SelectionStarted)
206 // finished selection.
208 if(lastIt->mIsNewLineChar)
210 lastIt = std::max( textLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
212 const Size rowSize( mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( lastIt - textLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
213 maxRowLeft = std::min(maxRowLeft, min.x);
214 maxRowRight = std::max(maxRowRight, max.x);
215 float rowBottom = lastIt->mPosition.y - textLayoutInfo.mScrollOffset.y;
216 float rowTop = rowBottom - rowSize.height;
217 newHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
221 // Get the top left and bottom right corners.
222 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *textLayoutInfo.mCharacterLayoutInfoTable.begin() );
223 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
224 const Vector2 bottomRight( topLeft.x + textLayoutInfo.mTextSize.width, topLeft.y + textLayoutInfo.mTextSize.height );
226 // Clamp quads so they appear to clip to borders of the whole text.
227 newHighlightInfo.Clamp2D( topLeft, bottomRight );
229 // For block-align align Further Clamp quads to max left and right extents
230 if(blockAlignEnabled)
232 // BlockAlign: Will adjust highlight to block:
234 // H[ello] (top row right = max of all rows right)
235 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
236 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
237 // [text] (bottom row left = min of all rows left)
238 // (common in SMS messaging selection)
240 // As opposed to the default which is tight text highlighting.
245 // (common in regular text editors/web browser selection)
247 newHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
251 return newHighlightInfo;
254 void TextHighlight::UpdateHighlight( TextHighlight::HighlightInfo& newHighlightInfo )
256 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
258 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
260 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
261 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
262 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
263 // [BOTTOM] [ MIDDLE ]
266 // Each quad is created as 2 triangles.
267 // Middle is just 1 quad regardless of its size.
281 // vertex and triangle buffers should always be present if MeshActor is alive.
282 //HighlightInfo newHighlightInfo = CalculateHighlightInfo( handlePositionStart, handlePositionEnd );
283 MeshData::VertexContainer vertices;
284 Dali::MeshData::FaceIndices faceIndices;
286 if( !newHighlightInfo.mQuadList.empty() )
288 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
289 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
291 // vertex position defaults to (0 0 0)
292 MeshData::Vertex vertex;
293 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
296 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
298 // Add each quad geometry (a sub-selection) to the mesh data.
308 QuadCoordinates& quad = *iter;
310 vertex.x = quad.min.x;
311 vertex.y = quad.min.y;
312 vertices.push_back( vertex );
315 vertex.x = quad.max.x;
316 vertex.y = quad.min.y;
317 vertices.push_back( vertex );
320 vertex.x = quad.min.x;
321 vertex.y = quad.max.y;
322 vertices.push_back( vertex );
324 // bottom-right (v+3)
325 vertex.x = quad.max.x;
326 vertex.y = quad.max.y;
327 vertices.push_back( vertex );
329 // triangle A (3, 1, 0)
330 faceIndices.push_back( v + 3 );
331 faceIndices.push_back( v + 1 );
332 faceIndices.push_back( v );
334 // triangle B (0, 2, 3)
335 faceIndices.push_back( v );
336 faceIndices.push_back( v + 2 );
337 faceIndices.push_back( v + 3 );
340 mMeshData.SetVertices( vertices );
341 mMeshData.SetFaceIndices( faceIndices );
343 mHighlightMesh.UpdateMeshData(mMeshData);
347 Mesh TextHighlight::CreateHighLightMesh()
349 mMeshData = MeshData( );
350 mMeshData.SetHasNormals( true );
352 mCustomMaterial = Material::New("CustomMaterial");
353 mCustomMaterial.SetDiffuseColor( LIGHTBLUE );
355 mMeshData.SetMaterial( mCustomMaterial );
357 mHighlightMesh = Mesh::New( mMeshData );
359 return mHighlightMesh;
362 void TextHighlight::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
364 QuadCoordinates quad(x1, y1, x2, y2);
365 mQuadList.push_back( quad );
368 void TextHighlight::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
370 for(std::size_t i = 0;i < mQuadList.size(); i++)
372 QuadCoordinates& quad = mQuadList[i];
374 quad.min.Clamp(min, max);
375 quad.max.Clamp(min, max);
381 } // namespace Toolkit