042fe35ad5c2eee0c4d967f87df94e11bd8de895
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / navigation-frame / navigation-control-impl.cpp
1 /*
2  * Copyright (c) 2014 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 "navigation-control-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <cstring> // for strcmp
23 #include <dali/public-api/animation/animation.h>
24 #include <dali/public-api/events/key-event.h>
25 #include <dali/public-api/object/type-registry.h>
26 #include <dali/public-api/object/type-registry-helper.h>
27 #include <dali/public-api/size-negotiation/relayout-container.h>
28
29 // INTERNAL INCLUDES
30 #include <dali-toolkit/public-api/focus-manager/focus-manager.h>
31 #include <dali-toolkit/internal/controls/navigation-frame/navigation-tool-bar.h>
32 #include <dali-toolkit/internal/controls/navigation-frame/navigation-title-bar.h>
33 #include <dali-toolkit/public-api/focus-manager/focus-manager.h>
34
35 namespace Dali
36 {
37
38 namespace Toolkit
39 {
40
41 namespace Internal
42 {
43
44 namespace // to register type
45 {
46
47 BaseHandle Create()
48 {
49   return Toolkit::NavigationControl::New();
50 }
51
52 // Setup properties, signals and actions using the type-registry.
53 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::NavigationControl, Toolkit::Control, Create )
54
55 DALI_ACTION_REGISTRATION( Toolkit, NavigationControl, "push", ACTION_PUSH )
56 DALI_ACTION_REGISTRATION( Toolkit, NavigationControl, "pop",  ACTION_POP  )
57
58 DALI_TYPE_REGISTRATION_END()
59
60 }
61
62 NavigationControl::NavigationControl()
63 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS ) ),
64   mToolBar(NULL),
65   mTitleBar(NULL),
66   mOrientationAngle( 0 ),
67   mOrientationAnimationDuration( 1.0f ),
68   mOrientationAnimationAlphaFunc( AlphaFunction::EASE_OUT ),
69   mItemPositionCoefficient( Vector3( 0.0f, 1.0f, 0.0f) ),
70   mItemPushedSignal( ),
71   mItemPoppedSignal( )
72 {
73 }
74
75 NavigationControl::~NavigationControl()
76 {
77   // Clear all the items in the stack, forces their destruction before NavigationControl is destroyed.
78   mItemStack.clear();
79 }
80
81 void NavigationControl::OnInitialize()
82 {
83   //create layers for display background, current item, and bars respectively
84   mBackgroundLayer = CreateLayer();
85   mContentLayer = CreateLayer();
86   mBarLayer = CreateLayer();
87   mPopupLayer = CreateLayer();
88 }
89
90 void NavigationControl::OnControlChildAdd( Actor& child )
91 {
92   Toolkit::Page page = Toolkit::Page::DownCast(child);
93
94   // If it's a page then store it locally, Off stage.
95   if(page)
96   {
97     mUnpushedItems.push_back(page);
98
99     // Orphan it until needed later during "push".
100     Self().Remove( child );
101   }
102 }
103
104 Toolkit::NavigationControl NavigationControl::New()
105 {
106   // Create the implementation, temporarily owned by this handle on stack
107   IntrusivePtr< NavigationControl > internalNavigationControl = new NavigationControl();
108
109   // Pass ownership to CustomActor handle
110   Toolkit::NavigationControl navigationControl( *internalNavigationControl );
111
112   // Second-phase init of the implementation
113   // This can only be done after the CustomActor connection has been made...
114   internalNavigationControl->Initialize();
115
116   return navigationControl;
117 }
118
119 void NavigationControl::OnStageConnection()
120 {
121   //only works when navigation control is already on stage!
122   mContentLayer.RaiseAbove( mBackgroundLayer );
123   mBarLayer.RaiseAbove( mContentLayer );
124   mPopupLayer.RaiseAbove( mBarLayer );
125   Self().SetSensitive(true);
126   SetKeyInputFocus();
127 }
128
129 void NavigationControl::PushItem( Toolkit::Page page )
130 {
131   // check the uninitialized item
132   // check the duplicated push for the top item
133   if(!page || page == mCurrentItem)
134   {
135     return;
136   }
137
138   if( mCurrentItem )
139   {
140     mContentLayer.Remove( mCurrentItem );
141   }
142
143   //push the new item into the stack and show it
144   mItemStack.push_back(page);
145   mCurrentItem = page;
146   mContentLayer.Add(page);
147   mCurrentItem.SetPositionInheritanceMode(USE_PARENT_POSITION_PLUS_LOCAL_POSITION);
148
149   //set up the popup menu which would response to the KEY_MENU
150   SetupPopupMenu();
151
152   //Emit singal
153   Toolkit::NavigationControl handle( GetOwner() );
154   mItemPushedSignal.Emit(handle, page);
155 }
156
157 Toolkit::Page NavigationControl::PopItem()
158 {
159   // cannot pop out the bottom-most item
160   Toolkit::Page poppedItem;
161   if(mItemStack.size() > 1)
162   {
163     // pop out the top item of the stack and show the new item right under the old one.
164     mContentLayer.Remove(mCurrentItem);
165     poppedItem = mItemStack.back();
166     mItemStack.pop_back();
167     mCurrentItem = mItemStack.back();
168     mContentLayer.Add(mCurrentItem);
169
170     //set up the popup menu which would response to the KEY_MENU
171     SetupPopupMenu();
172   }
173
174   //Emit signal
175   Toolkit::NavigationControl handle( GetOwner() );
176   mItemPoppedSignal.Emit(handle, poppedItem);
177
178   return poppedItem;
179 }
180
181 size_t NavigationControl::GetItemCount() const
182 {
183   return mItemStack.size();
184 }
185
186 Toolkit::Page NavigationControl::GetItem(std::size_t index) const
187 {
188   DALI_ASSERT_ALWAYS( index < mItemStack.size() );
189   return mItemStack[index];
190 }
191
192 Toolkit::Page NavigationControl::GetCurrentItem() const
193 {
194   return mCurrentItem;
195 }
196
197 void NavigationControl::SetBackground( Actor background)
198 {
199   // It removes the old background
200   if( mBackground )
201   {
202     mBackgroundLayer.Remove( mBackground );
203   }
204   mBackgroundLayer.Add( background );
205   mBackground = background;
206   mBackground.SetSize( mControlSize );
207 }
208
209 void NavigationControl::CreateNavigationToolBar(Toolkit::NaviToolBarStyle toolBarStylePortrait,
210                                                 Toolkit::NaviToolBarStyle toolBarStyleLandscape )
211 {
212   // Set a navigation tool bar at the bottom of the navigation frame
213   // the controls on the tool bar will update automatically when item is pushed or popped by responding to the signals
214   mToolBar = new NavigationToolBar(*this, toolBarStylePortrait, toolBarStyleLandscape);
215 }
216
217 void NavigationControl::CreateNavigationTitleBar(Toolkit::NaviTitleBarStyle titleBarStylePortrait,
218                                                  Toolkit::NaviTitleBarStyle titleBarStyleLandscape)
219 {
220   // Set a navigation title bar at the top of the navigation frame
221   // the tile/subtitle/titl icon/buttons will update automatically when item is pushed or popped by responding to the signals
222   mTitleBar = new NavigationTitleBar(*this, titleBarStylePortrait, titleBarStyleLandscape);
223 }
224
225 void NavigationControl::OrientationChanged( int angle )
226 {
227   if( mOrientationAngle != angle )
228   {
229     Vector2 targetSize = Vector2(GetSizeSet());
230
231     // checking to see if changing from landscape -> portrait, or portrait -> landscape
232     if( mOrientationAngle%180 != angle%180 )
233     {
234       targetSize = Vector2( targetSize.height, targetSize.width );
235     }
236
237     mOrientationAngle = angle;
238
239     switch(angle)
240     {
241       case 0:
242       {
243         mItemPositionCoefficient = Vector3(0.0f, 1.0f, 0.0f);
244         break;
245       }
246       case 90:
247       {
248         mItemPositionCoefficient = Vector3(1.0f, 0.0f, 0.0f);
249         break;
250       }
251       case 180:
252       {
253         mItemPositionCoefficient = Vector3(0.0f, -1.0f, 0.0f);
254         break;
255       }
256       case 270:
257       {
258         mItemPositionCoefficient = Vector3(-1.0f, 0.0f, 0.0f);
259         break;
260       }
261       default:
262       {
263         DALI_ASSERT_ALWAYS(false);
264         break;
265       }
266     }
267
268     Actor self = Self();
269     Animation animation = Animation::New( mOrientationAnimationDuration );
270     animation.AnimateTo( Property( self, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree( -angle ) ), Vector3::ZAXIS ), mOrientationAnimationAlphaFunc );
271     animation.Play();
272
273     self.SetSize( targetSize );
274
275     RelayoutRequest();
276   }
277 }
278
279 void NavigationControl::SetOrientationRotateAnimation( float duration, AlphaFunction alphaFunc)
280 {
281   mOrientationAnimationDuration = duration;
282   mOrientationAnimationAlphaFunc = alphaFunc;
283 }
284
285 Layer NavigationControl::GetBarLayer() const
286 {
287   return mBarLayer;
288 }
289
290 void NavigationControl::OnRelayout( const Vector2& size, RelayoutContainer& container )
291 {
292   const Vector2 setSize( size );
293
294   if( mCurrentItem )
295   {
296     // always set the current item to fully occupy navigationControl space apart from the bars,
297     // here the bars might be hidden if the current item does not need them
298     float positionOffset = 0.0f;
299     float sizeShrink = 0.0f;
300     if(mTitleBar)
301     {
302       positionOffset += mTitleBar->GetBarHeight()*0.5f;
303       sizeShrink += mTitleBar->GetBarHeight();
304     }
305     if(mToolBar)
306     {
307       positionOffset -= mToolBar->GetBarHeight()*0.5f;
308       sizeShrink += mToolBar->GetBarHeight();
309     }
310     mCurrentItem.SetPosition( mItemPositionCoefficient * positionOffset);
311     Vector2 itemSize( setSize.x, setSize.y-sizeShrink );
312
313     container.Add( mCurrentItem, itemSize );
314   }
315
316   container.Add( mBarLayer, setSize );
317   container.Add( mPopupLayer, setSize );
318 }
319
320 void NavigationControl::OnControlSizeSet( const Vector3& size )
321 {
322   if( mControlSize == Vector2(size) )
323   {
324     return;
325   }
326   mControlSize = Vector2(size);
327
328   mBarLayer.SetSize(mControlSize);
329   mPopupLayer.SetSize(mControlSize);
330
331   if( mBackground )
332   {
333     mBackground.SetSize( mControlSize );
334   }
335   if( mToolBar )
336   {
337     mToolBar->ScaleStyleUpdate( mControlSize, mOrientationAngle );
338   }
339   if( mTitleBar )
340   {
341     mTitleBar->ScaleStyleUpdate( mControlSize, mOrientationAngle );
342   }
343 }
344
345 bool NavigationControl::OnKeyEvent( const KeyEvent& event )
346 {
347   bool consumed = false;
348
349   if(event.state == KeyEvent::Down)
350   {
351     if(event.keyCode == 96 ) // F12 == for test
352     //if( event.keyCode == Dali::DALI_KEY_BACK || event.keyCode == Dali::DALI_KEY_ESCAPE )
353     {
354       if( mPopupMenu && mPopupMenu.IsSensitive() ) // State:POPUP_SHOW
355       {
356         mPopupMenu.Hide();
357         consumed = true;
358       }
359       else if(PopItem())
360       {
361         consumed = true;
362       }
363     }
364
365     if( mPopupMenu && event.keyCode == 9)
366     //if( mPopupMenu && ( event.keyCode == Dali::DALI_KEY_MENU  || event.keyCode == Dali::DALI_KEY_SEND ) )
367     //Todo: replace with dali key enum after the mapping between X key definition and dali key enum is implemented in dali-adapto
368     //if( mPopupMenu && event.keyPressedName == "XF86Send" )
369     {
370       if( !mPopupMenu.IsSensitive() ) // State: POPUP_HIDE
371       {
372         mPopupMenu.Show();
373       }
374       else // State:POPUP_SHOW
375       {
376         mPopupMenu.Hide();
377       }
378       consumed = true;
379     }
380   }
381
382   return consumed;
383 }
384
385 Layer NavigationControl::CreateLayer()
386 {
387   Layer layer = Layer::New();
388   layer.SetPositionInheritanceMode(USE_PARENT_POSITION);
389   Self().Add(layer);
390   return layer;
391 }
392
393 void NavigationControl::SetupPopupMenu()
394 {
395   if(mPopupMenu)
396   {
397     mPopupLayer.Remove( mPopupMenu );
398   }
399   mPopupMenu = mCurrentItem.GetPopupMenu();
400   if( mPopupMenu )
401   {
402     mPopupLayer.Add( mPopupMenu );
403     mPopupMenu.OutsideTouchedSignal().Connect(this, &NavigationControl::OnPopupTouchedOutside);
404   }
405 }
406
407 void NavigationControl::OnPopupTouchedOutside()
408 {
409   if( mPopupMenu )
410   {
411     mPopupMenu.Hide();
412   }
413 }
414
415 Toolkit::NavigationControl::ItemPushedSignalType& NavigationControl::ItemPushedSignal()
416 {
417   return mItemPushedSignal;
418 }
419
420 Toolkit::NavigationControl::ItemPoppedSignalType& NavigationControl::ItemPoppedSignal()
421 {
422   return mItemPoppedSignal;
423 }
424
425 bool NavigationControl::DoAction( BaseObject* object, const std::string& actionName, const PropertyValueContainer& attributes )
426 {
427   bool ret = false;
428
429   Dali::BaseHandle handle( object );
430   Toolkit::NavigationControl control = Toolkit::NavigationControl::DownCast( handle );
431   DALI_ASSERT_ALWAYS( control );
432
433   if( 0 == strcmp( actionName.c_str(), ACTION_PUSH ) )
434   {
435     for( PropertyValueConstIter iter = attributes.begin(); iter != attributes.end(); ++iter )
436     {
437       const Property::Value& value = *iter;
438
439       DALI_ASSERT_ALWAYS( value.GetType() == Property::STRING );
440       std::string itemName = value.Get<std::string>();
441
442       for( std::list<Toolkit::Page>::iterator itemsIter = GetImpl( control ).mUnpushedItems.begin(); itemsIter != GetImpl( control ).mUnpushedItems.end(); ++itemsIter )
443       {
444         Toolkit::Page page = *itemsIter;
445         if( page.GetName() == itemName )
446         {
447           GetImpl( control ).PushItem( page );
448           ret = true;
449           break;
450         }
451       }
452     }
453   }
454   else if( 0 == strcmp( actionName.c_str(), ACTION_POP ) )
455   {
456     GetImpl( control ).PopItem();
457
458     ret = true;
459   }
460
461   return ret;
462 }
463
464 } // namespace Internal
465
466 } // namespace Toolkit
467
468 } // namespace Dali