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