Copied some TextInput grab/selection handle code
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / public-api / text / decorator / text-decorator.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/public-api/text/decorator/text-decorator.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/actors/actor.h>
23 #include <dali/public-api/actors/image-actor.h>
24 #include <dali/public-api/actors/layer.h>
25 #include <dali/public-api/common/constants.h>
26 #include <dali/public-api/events/tap-gesture.h>
27 #include <dali/public-api/events/tap-gesture-detector.h>
28 #include <dali/public-api/events/pan-gesture.h>
29 #include <dali/public-api/events/pan-gesture-detector.h>
30 #include <dali/public-api/images/resource-image.h>
31 #include <dali/public-api/math/vector2.h>
32 #include <dali/public-api/math/vector4.h>
33 #include <dali/public-api/signals/connection-tracker.h>
34
35 // INTERNAL INCLUDES
36 #include <dali-toolkit/public-api/controls/control.h>
37 #include <dali-toolkit/public-api/controls/control-impl.h>
38
39 #ifdef DEBUG_ENABLED
40 #define DECORATOR_DEBUG
41 #endif
42
43 // Local Data
44 namespace
45 {
46
47 const char* DEFAULT_GRAB_HANDLE_IMAGE( DALI_IMAGE_DIR "insertpoint-icon.png" );
48 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
49 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
50 //const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
51 //const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
52
53 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
54 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
55
56 } // end of namespace
57
58 namespace Dali
59 {
60
61 namespace Toolkit
62 {
63
64 namespace Text
65 {
66
67 struct Decorator::Impl : public ConnectionTracker
68 {
69   struct CursorImpl
70   {
71     CursorImpl()
72     : x(0.0f),
73       y(0.0f),
74       height(0.0f),
75       color(Dali::Color::WHITE)
76     {
77     }
78
79     float x;
80     float y;
81     float height;
82
83     Vector4 color;
84   };
85
86   struct SelectionHandleImpl
87   {
88     SelectionHandleImpl()
89     : x(0.0f),
90       y(0.0f),
91       cursorHeight(0.0f),
92       flipped(false)
93     {
94     }
95
96     float x;
97     float y;
98     float cursorHeight; ///< Not the handle height
99     bool flipped;
100
101     ImageActor actor;
102     Actor grabArea;
103
104     Image pressedImage;
105     Image releasedImage;
106   };
107
108   Impl( Dali::Toolkit::Internal::Control& parent, Observer& observer )
109   : mParent(parent),
110     mObserver(observer),
111     mActiveCursor(ACTIVE_CURSOR_NONE),
112     mActiveGrabHandle(false),
113     mCursorBlinkInterval(0.5f),
114     mCursorBlinkDuration(0.0f)
115   {
116   }
117
118   void Relayout( const Vector2& size )
119   {
120     // Show or hide the grab handle
121     if( mActiveGrabHandle )
122     {
123       SetupTouchEvents();
124
125       CreateActiveLayer();
126       CreateGrabHandle();
127
128       mGrabHandle.SetPosition( mCursor[PRIMARY_CURSOR].x, mCursor[PRIMARY_CURSOR].y + mCursor[PRIMARY_CURSOR].height );
129     }
130     else if( mGrabHandle )
131     {
132       UnparentAndReset( mGrabHandle );
133     }
134
135     // Show or hide the selection handles/highlight
136     if( mActiveSelection )
137     {
138       SetupTouchEvents();
139
140       CreateActiveLayer();
141       CreateSelectionHandles();
142
143       SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ];
144       primary.actor.SetPosition( primary.x, primary.y + primary.cursorHeight );
145
146       SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ];
147       secondary.actor.SetPosition( secondary.x, secondary.y + secondary.cursorHeight );
148
149       //CreateHighlight(); TODO
150     }
151     else
152     {
153       UnparentAndReset( mSelectionHandle[ PRIMARY_SELECTION_HANDLE ].actor );
154       UnparentAndReset( mSelectionHandle[ SECONDARY_SELECTION_HANDLE ].actor );
155     }
156
157     // TODO
158   }
159
160   void SetupTouchEvents()
161   {
162     if ( !mTapDetector )
163     {
164       mTapDetector = TapGestureDetector::New();
165       mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap );
166     }
167
168     if ( !mPanGestureDetector )
169     {
170       mPanGestureDetector = PanGestureDetector::New();
171       mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
172     }
173   }
174
175   void CreateActiveLayer()
176   {
177     if( !mActiveLayer )
178     {
179       Actor parent = mParent.Self();
180
181       mActiveLayer = Layer::New();
182 #ifdef DECORATOR_DEBUG
183       mActiveLayer.SetName ( "ActiveLayerActor" );
184 #endif
185
186       mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
187       mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
188       mActiveLayer.SetSizeMode( SIZE_EQUAL_TO_PARENT );
189       mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
190
191       parent.Add( mActiveLayer );
192     }
193
194     mActiveLayer.RaiseToTop();
195   }
196
197   void CreateGrabHandle()
198   {
199     if( !mGrabHandle )
200     {
201       if ( !mGrabHandleImage )
202       {
203         mGrabHandleImage = ResourceImage::New( DEFAULT_GRAB_HANDLE_IMAGE );
204       }
205
206       mGrabHandle = ImageActor::New( mGrabHandleImage );
207 #ifdef DECORATOR_DEBUG
208       mGrabHandle.SetName( "GrabHandleActor" );
209 #endif
210       mGrabHandle.SetParentOrigin( ParentOrigin::TOP_LEFT );
211       mGrabHandle.SetAnchorPoint( AnchorPoint::TOP_CENTER );
212       mGrabHandle.SetDrawMode( DrawMode::OVERLAY );
213
214       mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
215 #ifdef DECORATOR_DEBUG
216       mGrabArea.SetName( "GrabArea" );
217 #endif
218       mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
219       mGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
220       mGrabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
221       mGrabHandle.Add(mGrabArea);
222
223       mTapDetector.Attach( mGrabArea );
224       mPanGestureDetector.Attach( mGrabArea );
225
226       mActiveLayer.Add(mGrabHandle);
227     }
228   }
229
230   void CreateSelectionHandles()
231   {
232     SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ];
233     if ( !primary.actor )
234     {
235       if ( !primary.releasedImage )
236       {
237         primary.releasedImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE );
238       }
239
240       primary.actor = ImageActor::New( primary.releasedImage );
241 #ifdef DECORATOR_DEBUG
242       primary.actor.SetName("SelectionHandleOne");
243 #endif
244       primary.actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
245       primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
246       primary.actor.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
247       primary.flipped = false;
248
249       primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
250 #ifdef DECORATOR_DEBUG
251       primary.grabArea.SetName("SelectionHandleOneGrabArea");
252 #endif
253       primary.grabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
254       primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
255       primary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
256
257       mTapDetector.Attach( primary.grabArea );
258       mPanGestureDetector.Attach( primary.grabArea );
259       primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
260
261       primary.actor.Add( primary.grabArea );
262       mActiveLayer.Add( primary.actor );
263     }
264
265     SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ];
266     if ( !secondary.actor )
267     {
268       if ( !secondary.releasedImage )
269       {
270         secondary.releasedImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO );
271       }
272
273       secondary.actor = ImageActor::New( secondary.releasedImage );
274 #ifdef DECORATOR_DEBUG
275       secondary.actor.SetName("SelectionHandleTwo");
276 #endif
277       secondary.actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
278       secondary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
279       secondary.actor.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
280       secondary.flipped = false;
281
282       secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
283 #ifdef DECORATOR_DEBUG
284       secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
285 #endif
286       secondary.grabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
287       secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
288       secondary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
289
290       mTapDetector.Attach( secondary.grabArea );
291       mPanGestureDetector.Attach( secondary.grabArea );
292       secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
293
294       secondary.actor.Add( secondary.grabArea );
295       mActiveLayer.Add( secondary.actor );
296     }
297
298     //SetUpHandlePropertyNotifications(); TODO
299   }
300
301   void OnTap( Actor actor, const TapGesture& tap )
302   {
303     if( actor == mGrabHandle )
304     {
305       // TODO
306     }
307   }
308
309   void OnPan( Actor actor, const PanGesture& gesture )
310   {
311     if( actor == mGrabHandle )
312     {
313       // TODO
314     }
315   }
316
317   bool OnHandleOneTouched( Actor actor, const TouchEvent& touch )
318   {
319     // TODO
320     return false;
321   }
322
323   bool OnHandleTwoTouched( Actor actor, const TouchEvent& touch )
324   {
325     // TODO
326     return false;
327   }
328
329   Internal::Control& mParent;
330   Observer& mObserver;
331
332   Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
333
334   unsigned int mActiveCursor;
335   bool         mActiveGrabHandle;
336   bool         mActiveSelection;
337
338   CursorImpl mCursor[CURSOR_COUNT];
339
340   ImageActor mGrabHandle;
341   Actor mGrabArea;
342
343   SelectionHandleImpl mSelectionHandle[SELECTION_HANDLE_COUNT];
344
345   Image mCursorImage;
346   Image mGrabHandleImage;
347
348   TapGestureDetector mTapDetector;
349   PanGestureDetector mPanGestureDetector;
350
351   float mCursorBlinkInterval;
352   float mCursorBlinkDuration;
353 };
354
355 DecoratorPtr Decorator::New( Internal::Control& parent, Observer& observer )
356 {
357   return DecoratorPtr( new Decorator(parent, observer) );
358 }
359
360 void Decorator::Relayout( const Vector2& size )
361 {
362   mImpl->Relayout( size );
363 }
364
365 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
366 {
367   mImpl->mActiveCursor = activeCursor;
368 }
369
370 unsigned int Decorator::GetActiveCursor() const
371 {
372   return mImpl->mActiveCursor;
373 }
374
375 void Decorator::SetPosition( Cursor cursor, float x, float y, float height )
376 {
377   mImpl->mCursor[cursor].x = x;
378   mImpl->mCursor[cursor].y = y;
379   mImpl->mCursor[cursor].height = height;
380 }
381
382 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& height ) const
383 {
384   x = mImpl->mCursor[cursor].x;
385   y = mImpl->mCursor[cursor].y;
386   height = mImpl->mCursor[cursor].height;
387 }
388
389 void Decorator::SetCursorImage( Dali::Image image )
390 {
391   mImpl->mCursorImage = image;
392 }
393
394 Dali::Image Decorator::GetCursorImage() const
395 {
396   return mImpl->mCursorImage;
397 }
398
399 void Decorator::SetColor( Cursor cursor, const Dali::Vector4& color )
400 {
401   mImpl->mCursor[cursor].color = color;
402 }
403
404 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
405 {
406   return mImpl->mCursor[cursor].color;
407 }
408
409 void Decorator::StartCursorBlink()
410 {
411   // TODO
412 }
413
414 void Decorator::StopCursorBlink()
415 {
416   // TODO
417 }
418
419 void Decorator::SetCursorBlinkInterval( float seconds )
420 {
421   mImpl->mCursorBlinkInterval = seconds;
422 }
423
424 float Decorator::GetCursorBlinkInterval() const
425 {
426   return mImpl->mCursorBlinkInterval;
427 }
428
429 void Decorator::SetCursorBlinkDuration( float seconds )
430 {
431   mImpl->mCursorBlinkDuration = seconds;
432 }
433
434 float Decorator::GetCursorBlinkDuration() const
435 {
436   return mImpl->mCursorBlinkDuration;
437 }
438
439 void Decorator::SetGrabHandleActive( bool active )
440 {
441   mImpl->mActiveGrabHandle = active;
442 }
443
444 bool Decorator::IsGrabHandleActive() const
445 {
446   return mImpl->mActiveGrabHandle;
447 }
448
449 void Decorator::SetGrabHandleImage( Dali::Image image )
450 {
451   mImpl->mGrabHandleImage = image;
452 }
453
454 Dali::Image Decorator::GetGrabHandleImage() const
455 {
456   return mImpl->mGrabHandleImage;
457 }
458
459 void Decorator::SetSelectionActive( bool active )
460 {
461   mImpl->mActiveSelection = active;
462 }
463
464 bool Decorator::IsSelectionActive() const
465 {
466   return mImpl->mActiveSelection;
467 }
468
469 void Decorator::SetPosition( SelectionHandle handle, float x, float y, float height )
470 {
471   mImpl->mSelectionHandle[handle].x = x;
472   mImpl->mSelectionHandle[handle].y = y;
473   mImpl->mSelectionHandle[handle].cursorHeight = height;
474 }
475
476 void Decorator::GetPosition( SelectionHandle handle, float& x, float& y, float& height ) const
477 {
478   x = mImpl->mSelectionHandle[handle].x;
479   y = mImpl->mSelectionHandle[handle].y;
480   height = mImpl->mSelectionHandle[handle].cursorHeight;
481 }
482
483 void Decorator::SetImage( SelectionHandle handle, SelectionHandleState state, Dali::Image image )
484 {
485   if( SELECTION_HANDLE_PRESSED == state )
486   {
487     mImpl->mSelectionHandle[handle].pressedImage = image;
488   }
489   else
490   {
491     mImpl->mSelectionHandle[handle].releasedImage = image;
492   }
493 }
494
495 Dali::Image Decorator::GetImage( SelectionHandle handle, SelectionHandleState state ) const
496 {
497   if( SELECTION_HANDLE_PRESSED == state )
498   {
499     return mImpl->mSelectionHandle[handle].pressedImage;
500   }
501
502   return mImpl->mSelectionHandle[handle].releasedImage;
503 }
504
505 Decorator::~Decorator()
506 {
507   delete mImpl;
508 }
509
510 Decorator::Decorator( Dali::Toolkit::Internal::Control& parent, Observer& observer )
511 : mImpl( NULL )
512 {
513   mImpl = new Decorator::Impl( parent, observer );
514 }
515
516 } // namespace Text
517
518 } // namespace Toolkit
519
520 } // namespace Dali