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