Supply stylesheet using Application constructor
[platform/core/uifw/dali-demo.git] / examples / builder / examples.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 //------------------------------------------------------------------------------
19 //
20 //
21 //------------------------------------------------------------------------------
22
23 #include <dali/dali.h>
24 #include <dali-toolkit/dali-toolkit.h>
25 #include <dali-toolkit/devel-api/builder/builder.h>
26 #include <dali-toolkit/devel-api/builder/tree-node.h>
27 #include <dali-toolkit/devel-api/builder/json-parser.h>
28 #include <dali-toolkit/devel-api/controls/popup/popup.h>
29 #include <map>
30 #include <string>
31 #include <fstream>
32 #include <streambuf>
33 #include <sstream>
34 #include <dirent.h>
35 #include <stdio.h>
36 #include <iostream>
37
38 #include "sys/stat.h"
39 #include <ctime>
40 #include <cstring>
41
42 #include <dali/integration-api/debug.h>
43 #include "shared/view.h"
44
45 #define TOKEN_STRING(x) #x
46
47 using namespace Dali;
48 using namespace Dali::Toolkit;
49
50 namespace
51 {
52
53 const char* BACKGROUND_IMAGE( "" );
54 const char* TOOLBAR_IMAGE( DALI_IMAGE_DIR "top-bar.png" );
55 const char* EDIT_IMAGE( DALI_IMAGE_DIR "icon-change.png" );
56
57 std::string USER_DIRECTORY;
58
59 std::string JSON_BROKEN("                                      \
60 {                                                              \
61   'stage':                                                     \
62   [                                                            \
63     {                                                          \
64       'type':'TextLabel',                                      \
65       'size': [50,50,1],                                       \
66       'parent-origin': 'CENTER',                               \
67       'text':'COULD NOT LOAD JSON FILE'                        \
68     }                                                          \
69   ]                                                            \
70 }                                                              \
71 ");
72
73 std::string ReplaceQuotes(const std::string &single_quoted)
74 {
75   std::string s(single_quoted);
76
77   // wrong as no embedded quote but had regex link problems
78   std::replace(s.begin(), s.end(), '\'', '"');
79
80   return s;
81 }
82
83 std::string GetFileContents(const std::string &fn)
84 {
85   std::ifstream t(fn.c_str());
86   return std::string((std::istreambuf_iterator<char>(t)),
87                      std::istreambuf_iterator<char>());
88 };
89
90 typedef std::vector<std::string> FileList;
91
92 void DirectoryFileList(const std::string& directory, FileList& files)
93 {
94   DIR           *d;
95   struct dirent *dir;
96   d = opendir(directory.c_str());
97   if (d)
98   {
99     while ((dir = readdir(d)) != NULL)
100     {
101       if (dir->d_type == DT_REG)
102       {
103         files.push_back( directory + std::string(dir->d_name) );
104       }
105     }
106
107     closedir(d);
108   }
109 }
110
111 void DirectoryFilesByType(const std::string& dir, const std::string& fileType /* ie "json" */, FileList& files)
112 {
113   typedef FileList Collection;
114   typedef FileList::iterator Iter;
115
116   Collection allFiles;
117   DirectoryFileList(dir, allFiles);
118
119   for(Iter iter = allFiles.begin(); iter != allFiles.end(); ++iter)
120   {
121     size_t pos = (*iter).rfind( '.' );
122     if( pos != std::string::npos )
123     {
124       if( (*iter).substr( pos+1 ) == fileType )
125       {
126         files.push_back( (*iter) );
127       }
128     }
129   }
130 }
131
132 const std::string ShortName( const std::string& name )
133 {
134   size_t pos = name.rfind( '/' );
135
136   if( pos != std::string::npos )
137   {
138     return name.substr( pos );
139   }
140   else
141   {
142     return name;
143   }
144 }
145
146 //------------------------------------------------------------------------------
147 //
148 //
149 //
150 //------------------------------------------------------------------------------
151 class FileWatcher
152 {
153 public:
154   FileWatcher(void);
155   ~FileWatcher(void);
156   explicit FileWatcher(const std::string &fn) { SetFilename(fn) ; };
157
158   void SetFilename(const std::string &fn);
159   std::string GetFilename() const;
160
161   bool FileHasChanged(void);
162   std::string GetFileContents(void) const { return ::GetFileContents(mstringPath) ; };
163
164 private:
165   // compiler does
166   // FileWatcher(const FileWatcher&);
167   // FileWatcher &operator=(const FileWatcher &);
168
169   std::time_t mLastTime;
170   std::string mstringPath;
171
172 };
173
174 FileWatcher::FileWatcher(void) : mLastTime(0)
175 {
176 }
177
178 bool FileWatcher::FileHasChanged(void)
179 {
180   struct stat buf;
181
182   if(0 != stat(mstringPath.c_str(), &buf))
183   {
184     return false;
185   }
186   else
187   {
188     if(buf.st_mtime > mLastTime)
189     {
190       mLastTime = buf.st_mtime;
191       return true;
192     }
193     else
194     {
195       mLastTime = buf.st_mtime;
196       return false;
197     }
198   }
199
200   return false;
201 }
202
203 FileWatcher::~FileWatcher()
204 {
205 }
206
207 void FileWatcher::SetFilename(const std::string &fn)
208 {
209   mstringPath = fn;
210   FileHasChanged(); // update last time
211 }
212
213 std::string FileWatcher::GetFilename(void) const
214 {
215   return mstringPath;
216 }
217
218
219 } // anon namespace
220
221
222 //------------------------------------------------------------------------------
223 //
224 //
225 //
226 //------------------------------------------------------------------------------
227 class ExampleApp : public ConnectionTracker, public Toolkit::ItemFactory
228 {
229 public:
230   ExampleApp(Application &app) : mApp(app)
231   {
232     app.InitSignal().Connect(this, &ExampleApp::Create);
233   }
234
235   ~ExampleApp() {}
236
237 public:
238
239   void SetTitle(const std::string& title)
240   {
241     if(!mTitleActor)
242     {
243       mTitleActor = DemoHelper::CreateToolBarLabel( "" );
244       // Add title to the tool bar.
245       mToolBar.AddControl( mTitleActor, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarTitlePercentage, Alignment::HorizontalCenter );
246     }
247
248     mTitleActor.SetProperty( TextLabel::Property::TEXT, title );
249   }
250
251   bool OnToolSelectLayout( Toolkit::Button button )
252   {
253     bool on = mItemView.IsVisible();
254
255     if( on )
256     {
257       LeaveSelection();
258     }
259     else
260     {
261       EnterSelection();
262     }
263
264     return true;
265   }
266
267   void LeaveSelection()
268   {
269
270   }
271
272   void EnterSelection()
273   {
274     Stage stage = Stage::GetCurrent();
275     stage.SetBackgroundColor( Color::WHITE );
276
277     mTapDetector = TapGestureDetector::New();
278     mTapDetector.DetectedSignal().Connect( this, &ExampleApp::OnTap );
279
280     if( mItemView )
281     {
282       stage.Remove( mItemView );
283     }
284
285     mFiles.clear();
286
287     mItemView = ItemView::New(*this);
288     stage.Add( mItemView );
289     mItemView.SetParentOrigin(ParentOrigin::CENTER);
290     mItemView.SetAnchorPoint(AnchorPoint::CENTER);
291     mLayout = DefaultItemLayout::New( DefaultItemLayout::LIST );
292
293     mLayout->SetItemSize( Vector3( stage.GetSize().width, 50, 1 ) );
294
295     mItemView.AddLayout( *mLayout );
296
297     mItemView.SetKeyboardFocusable( true );
298
299     mFiles.clear();
300     FileList files;
301
302     if( USER_DIRECTORY.size() )
303     {
304       DirectoryFilesByType( USER_DIRECTORY, "json", files );
305     }
306     else
307     {
308       DirectoryFilesByType( DALI_SCRIPT_DIR, "json", files );
309     }
310
311     std::sort(files.begin(), files.end());
312
313     ItemId itemId = 0;
314     for(FileList::iterator iter = files.begin(); iter != files.end(); ++iter)
315     {
316       JsonParser parser = JsonParser::New();
317
318       std::string data( GetFileContents( *iter ) );
319
320       parser.Parse( data );
321
322       if( parser.ParseError() )
323       {
324         std::cout << "Parser Error:" << *iter << std::endl;
325         std::cout << parser.GetErrorLineNumber() << "(" << parser.GetErrorColumn() << "):" << parser.GetErrorDescription() << std::endl;
326         exit(1);
327       }
328
329       if( parser.GetRoot() )
330       {
331         if( const TreeNode* node = parser.GetRoot()->Find("stage") )
332         {
333           // only those with a stage section
334           if( node->Size() )
335           {
336             mFiles.push_back( *iter );
337
338             mItemView.InsertItem( Item(itemId,
339                                        MenuItem( ShortName( *iter ) ) ),
340                                   0.5f );
341
342             itemId++;
343           }
344           else
345           {
346             std::cout << "Ignored file (stage has no nodes?):" << *iter << std::endl;
347           }
348         }
349         else
350         {
351           std::cout << "Ignored file (no stage section):" << *iter << std::endl;
352         }
353       }
354     }
355
356     // Display item view on the stage
357     stage.Add( mItemView );
358
359     mItemView.SetVisible( true );
360     mBuilderLayer.SetVisible( false );
361
362     SetTitle("Select");
363
364     // Activate the layout
365     Vector3 size(stage.GetSize());
366     mItemView.ActivateLayout(0, size, 0.0f/*immediate*/);
367   }
368
369   void ExitSelection()
370   {
371     mTapDetector.Reset();
372
373     mItemView.SetVisible( false );
374     mBuilderLayer.SetVisible( true );
375
376     SetTitle("View");
377   }
378
379   void OnTap( Actor actor, const TapGesture& tap )
380   {
381     ItemId id = mItemView.GetItemId( actor );
382
383     LoadFromFileList( id );
384   }
385
386   Actor MenuItem(const std::string& text)
387   {
388     TextLabel label = TextLabel::New( ShortName( text ) );
389     label.SetProperty( Dali::Toolkit::Control::Property::STYLE_NAME, "builderlabel" );
390     label.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
391
392     // Hook up tap detector
393     mTapDetector.Attach( label );
394
395     return label;
396   }
397
398   bool OnTimer()
399   {
400     if( mFileWatcher.FileHasChanged() )
401     {
402       LoadFromFile( mFileWatcher.GetFilename() );
403     }
404
405     return true;
406   }
407
408   void ReloadJsonFile(const std::string& filename, Builder& builder, Layer& layer)
409   {
410     Stage stage = Stage::GetCurrent();
411
412     builder = Builder::New();
413     builder.QuitSignal().Connect( this, &ExampleApp::OnBuilderQuit );
414
415     Property::Map defaultDirs;
416     defaultDirs[ TOKEN_STRING(DALI_IMAGE_DIR) ]  = DALI_IMAGE_DIR;
417     defaultDirs[ TOKEN_STRING(DALI_MODEL_DIR) ]  = DALI_MODEL_DIR;
418     defaultDirs[ TOKEN_STRING(DALI_SCRIPT_DIR) ] = DALI_SCRIPT_DIR;
419
420     builder.AddConstants( defaultDirs );
421
422     // render tasks may have been setup last load so remove them
423     RenderTaskList taskList = stage.GetRenderTaskList();
424     if( taskList.GetTaskCount() > 1 )
425     {
426       typedef std::vector<RenderTask> Collection;
427       typedef Collection::iterator ColIter;
428       Collection tasks;
429
430       for(unsigned int i = 1; i < taskList.GetTaskCount(); ++i)
431       {
432         tasks.push_back( taskList.GetTask(i) );
433       }
434
435       for(ColIter iter = tasks.begin(); iter != tasks.end(); ++iter)
436       {
437         taskList.RemoveTask(*iter);
438       }
439
440       RenderTask defaultTask = taskList.GetTask(0);
441       defaultTask.SetSourceActor( stage.GetRootLayer() );
442       defaultTask.SetTargetFrameBuffer( FrameBufferImage() );
443     }
444
445     unsigned int numChildren = layer.GetChildCount();
446
447     for(unsigned int i=0; i<numChildren; ++i)
448     {
449       layer.Remove( layer.GetChildAt(0) );
450     }
451
452     std::string data(GetFileContents(filename));
453
454     try
455     {
456       builder.LoadFromString(data);
457     }
458     catch(...)
459     {
460       builder.LoadFromString(ReplaceQuotes(JSON_BROKEN));
461     }
462
463     builder.AddActors( layer );
464   }
465
466
467   void LoadFromFileList( size_t index )
468   {
469     if( index < mFiles.size())
470     {
471       const std::string& name = mFiles[index];
472       mFileWatcher.SetFilename( name );
473       LoadFromFile( name );
474     }
475   }
476
477   void LoadFromFile( const std::string& name )
478   {
479     ReloadJsonFile( name, mBuilder, mBuilderLayer );
480
481     // do this here as GetCurrentSize()
482     mBuilderLayer.SetParentOrigin(ParentOrigin::CENTER);
483     mBuilderLayer.SetAnchorPoint(AnchorPoint::CENTER);
484     Dali::Vector3 size = Stage::GetCurrent().GetRootLayer().GetCurrentSize();
485     size.y -= DemoHelper::DEFAULT_VIEW_STYLE.mToolBarHeight;
486     mBuilderLayer.SetSize( size );
487
488     mBuilderLayer.LowerToBottom();
489     Stage::GetCurrent().GetRootLayer().RaiseToTop();
490
491     ExitSelection();
492   }
493
494   void Create(Application& app)
495   {
496     Stage stage = Stage::GetCurrent();
497
498     Stage::GetCurrent().KeyEventSignal().Connect(this, &ExampleApp::OnKeyEvent);
499
500     Layer contents = DemoHelper::CreateView( app,
501                                              mView,
502                                              mToolBar,
503                                              BACKGROUND_IMAGE,
504                                              TOOLBAR_IMAGE,
505                                              "" );
506
507     SetTitle("Builder");
508
509     mBuilderLayer = Layer::New();
510     stage.GetRootLayer().Add(mBuilderLayer);
511
512
513     // Create an edit mode button. (left of toolbar)
514     Toolkit::PushButton editButton = Toolkit::PushButton::New();
515     editButton.SetBackgroundImage( ResourceImage::New( EDIT_IMAGE ) );
516     editButton.ClickedSignal().Connect( this, &ExampleApp::OnToolSelectLayout);
517     editButton.SetLeaveRequired( true );
518     mToolBar.AddControl( editButton, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HorizontalLeft, DemoHelper::DEFAULT_MODE_SWITCH_PADDING  );
519
520     EnterSelection();
521
522     mTimer = Timer::New( 500 ); // ms
523     mTimer.TickSignal().Connect( this, &ExampleApp::OnTimer);
524     mTimer.Start();
525
526   } // Create(app)
527
528   virtual unsigned int GetNumberOfItems()
529   {
530     return mFiles.size();
531   }
532
533   virtual Actor NewItem(unsigned int itemId)
534   {
535     DALI_ASSERT_DEBUG( itemId < mFiles.size() );
536     return MenuItem( ShortName( mFiles[itemId] ) );
537   }
538
539   /**
540    * Main key event handler
541    */
542   void OnKeyEvent(const KeyEvent& event)
543   {
544     if(event.state == KeyEvent::Down)
545     {
546       if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
547       {
548         if ( mItemView.IsVisible() )
549         {
550           mApp.Quit();
551         }
552         else
553         {
554           EnterSelection();
555         }
556       }
557     }
558   }
559
560   /**
561    * Event handler when Builder wants to quit (we only want to close the shown json unless we're at the top-level)
562    */
563   void OnBuilderQuit()
564   {
565     if ( mItemView.IsVisible() )
566     {
567       mApp.Quit();
568     }
569     else
570     {
571       EnterSelection();
572     }
573   }
574
575 private:
576   Application& mApp;
577
578   ItemLayoutPtr mLayout;
579   ItemView mItemView;
580
581   Toolkit::Control mView;
582   unsigned int mOrientation;
583
584   Toolkit::ToolBar mToolBar;
585   TextLabel mTitleActor;             ///< The Toolbar's Title.
586
587   Layer mBuilderLayer;
588
589   Toolkit::Popup mMenu;
590
591   TapGestureDetector mTapDetector;
592
593   // builder
594   Builder mBuilder;
595
596   FileList mFiles;
597
598   FileWatcher mFileWatcher;
599   Timer mTimer;
600
601
602 };
603
604 //------------------------------------------------------------------------------
605 //
606 //
607 //
608 //------------------------------------------------------------------------------
609 int main(int argc, char **argv)
610 {
611   if(argc > 2)
612   {
613     if(strcmp(argv[1], "-f") == 0)
614     {
615       USER_DIRECTORY = argv[2];
616     }
617   }
618
619   Application app = Application::New(&argc, &argv, DALI_DEMO_THEME_PATH);
620
621   ExampleApp dali_app(app);
622
623   app.MainLoop();
624
625   return 0;
626 }