Fixing text-label demo mix color bug
[platform/core/uifw/dali-demo.git] / examples / text-label / text-label-example.cpp
1 /*
2  * Copyright (c) 2022 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  * @file text-label-example.cpp
20  * @brief Usage of TextLabel control with style application.
21  */
22
23 // EXTERNAL INCLUDES
24 #include <dali-toolkit/dali-toolkit.h>
25 #include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
26 #include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
27 #include <dali/devel-api/actors/actor-devel.h>
28 #include <dali/devel-api/object/handle-devel.h>
29 #include <iostream>
30
31 // INTERNAL INCLUDES
32 #include "expanding-buttons.h"
33 #include "shared/multi-language-strings.h"
34 #include "shared/view.h"
35
36 using namespace Dali;
37 using namespace Dali::Toolkit;
38 using namespace MultiLanguageStrings;
39
40 namespace
41 {
42 const char* const BACKGROUND_IMAGE     = DEMO_IMAGE_DIR "grab-handle.png";
43 const char* const STYLE_SELECTED_IMAGE = DEMO_IMAGE_DIR "FontStyleButton_OK_03.png";
44
45 const char* BUTTON_IMAGES[] =
46   {
47     DEMO_IMAGE_DIR "FontStyleButton_Colour.png",
48     DEMO_IMAGE_DIR "FontStyleButton_Outline.png",
49     DEMO_IMAGE_DIR "FontStyleButton_Shadow.png",
50     DEMO_IMAGE_DIR "FontStyleButton_Background.png"};
51
52 const unsigned int KEY_ZERO  = 10;
53 const unsigned int KEY_ONE   = 11;
54 const unsigned int KEY_A     = 38;
55 const unsigned int KEY_F     = 41;
56 const unsigned int KEY_H     = 43;
57 const unsigned int KEY_U     = 30;
58 const unsigned int KEY_V     = 55;
59 const unsigned int KEY_M     = 58;
60 const unsigned int KEY_L     = 46;
61 const unsigned int KEY_S     = 39;
62 const unsigned int KEY_PLUS  = 21;
63 const unsigned int KEY_MINUS = 20;
64
65 const char* H_ALIGNMENT_STRING_TABLE[] =
66   {
67     "BEGIN",
68     "CENTER",
69     "END"};
70
71 const unsigned int H_ALIGNMENT_STRING_COUNT = sizeof(H_ALIGNMENT_STRING_TABLE) / sizeof(H_ALIGNMENT_STRING_TABLE[0u]);
72
73 const char* V_ALIGNMENT_STRING_TABLE[] =
74   {
75     "TOP",
76     "CENTER",
77     "BOTTOM"};
78
79 const unsigned int V_ALIGNMENT_STRING_COUNT = sizeof(V_ALIGNMENT_STRING_TABLE) / sizeof(V_ALIGNMENT_STRING_TABLE[0u]);
80
81 enum StyleType
82 {
83   TEXT_COLOR = 0,
84   OUTLINE,
85   SHADOW,
86   BACKGROUND,
87   NUMBER_OF_STYLES
88 };
89
90 const Vector4 AVAILABLE_COLORS[] =
91   {
92     Color::GREEN,
93     Color::BLUE,
94     Color::RED,
95     Color::CYAN,
96     Color::WHITE // Used as clear
97 };
98
99 const unsigned int NUMBER_OF_COLORS = sizeof(AVAILABLE_COLORS) / sizeof(AVAILABLE_COLORS[0u]);
100
101 int ConvertToEven(int value)
102 {
103   return (value % 2 == 0) ? value : (value + 1);
104 }
105
106 struct HSVColorConstraint
107 {
108   HSVColorConstraint(float hueParam, float saturationParam, float valueParam)
109   : hue(hueParam),
110     saturation(saturationParam),
111     value(valueParam)
112   {
113   }
114
115   void operator()(Vector3& current, const PropertyInputContainer& inputs)
116   {
117     current = hsv2rgb(Vector3(inputs[0]->GetFloat(), saturation, value));
118   }
119
120   Vector3 hsv2rgb(Vector3 colorHSV)
121   {
122     float r = colorHSV.z * (1 + colorHSV.y * (cos(colorHSV.x) - 1));
123     float g = colorHSV.z * (1 + colorHSV.y * (cos(colorHSV.x - 2.09439) - 1));
124     float b = colorHSV.z * (1 + colorHSV.y * (cos(colorHSV.x + 2.09439) - 1));
125     return Vector3(r, g, b);
126   }
127   float hue;
128   float saturation;
129   float value;
130 };
131
132 const float   STYLE_BUTTON_POSTION_RELATIVE_TO_WINDOW = 0.9f;
133 const float   BUTTON_SIZE_RATIO_TO_WINDOW             = 0.1f;
134 const float   OUTLINE_WIDTH                           = 2.0f;
135 const Vector2 SHADOW_OFFSET                           = Vector2(2.0f, 2.0f);
136 const int     GAP_BETWEEN_BUTTONS                     = 3;
137
138 } // anonymous namespace
139
140 /**
141  * @brief The main class of the demo.
142  */
143 class TextLabelExample : public ConnectionTracker
144 {
145 public:
146   TextLabelExample(Application& application)
147   : mApplication(application),
148     mLabel(),
149     mSelectedColor(AVAILABLE_COLORS[0]),
150     mStyleActivatedForColor(NUMBER_OF_STYLES),
151     mContainer(),
152     mGrabCorner(),
153     mBorder(),
154     mPanGestureDetector(),
155     mLayoutSize(),
156     mLanguageId(0u),
157     mAlignment(0u),
158     mHueAngleIndex(Property::INVALID_INDEX),
159     mOverrideMixColorIndex(Property::INVALID_INDEX),
160     mColorButtonsHidden(true),
161     mCollapseColorsAndStyles(false)
162   {
163     // Connect to the Application's Init signal
164     mApplication.InitSignal().Connect(this, &TextLabelExample::Create);
165
166     // Set Style flags to inactive
167     for(unsigned int i = TEXT_COLOR; i < NUMBER_OF_STYLES; i++)
168     {
169       mStyleActiveState[i]  = false;
170       mCurrentStyleColor[i] = AVAILABLE_COLORS[NUMBER_OF_COLORS - 1];
171     }
172   }
173
174   ~TextLabelExample()
175   {
176     // Nothing to do here.
177   }
178
179   // Clicking the expanding button shows the registered style buttons.
180   void SetUpExpandingStyleButtons(Vector2 position)
181   {
182     mExpandingButtons = Demo::ExpandingButtons::New();
183     mExpandingButtons.SetProperty(Actor::Property::POSITION, Vector2(mButtonSize.width, mWindowSize.height * STYLE_BUTTON_POSTION_RELATIVE_TO_WINDOW));
184     mExpandingButtons.CollapsingSignal().Connect(this, &TextLabelExample::OnExpandingButtonCollapsing);
185     mExpandingButtons.SetProperty(Actor::Property::SIZE, mButtonSize);
186     // Creates the buttons to be expanded
187     CreateStyleButtons();
188
189     // Register the created buttons with the ExpandingButtons.
190     for(unsigned int index = 0; index < NUMBER_OF_STYLES; index++)
191     {
192       mExpandingButtons.RegisterButton(mStyleButtons[index]);
193     }
194   }
195
196   /**
197    * One-time setup in response to Application InitSignal.
198    */
199   void Create(Application& application)
200   {
201     Window window = application.GetWindow();
202
203     window.KeyEventSignal().Connect(this, &TextLabelExample::OnKeyEvent);
204     mWindowSize = window.GetSize();
205     mButtonSize = Size(mWindowSize.height * 0.1, mWindowSize.height * 0.1); // Button size 1/10 of window height
206
207     mContainer = Control::New();
208     mContainer.SetProperty(Dali::Actor::Property::NAME, "Container");
209     mContainer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
210     mLayoutSize = Vector2(mWindowSize.width * 0.6f, mWindowSize.width * 0.6f);
211     mContainer.SetProperty(Actor::Property::SIZE, mLayoutSize);
212     window.Add(mContainer);
213
214     // Resize the center layout when the corner is grabbed
215     mGrabCorner = ImageView::New(BACKGROUND_IMAGE);
216     mGrabCorner.SetProperty(Dali::Actor::Property::NAME, "GrabCorner");
217     mGrabCorner.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
218     mGrabCorner.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_RIGHT);
219     mGrabCorner.SetResizePolicy(ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS);
220     mContainer.Add(mGrabCorner);
221
222     mPanGestureDetector = PanGestureDetector::New();
223     mPanGestureDetector.Attach(mGrabCorner);
224     mPanGestureDetector.DetectedSignal().Connect(this, &TextLabelExample::OnPan);
225
226     mLabel = TextLabel::New("\xF0\x9F\x98\x89 A Quick Brown Fox Jumps Over The Lazy Dog");
227
228     mLabel.SetProperty(Dali::Actor::Property::NAME, "TextLabel");
229     mLabel.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
230     mLabel.SetProperty(Actor::Property::SIZE, mLayoutSize);
231     mLabel.SetProperty(TextLabel::Property::MULTI_LINE, true);
232     mLabel.SetProperty(TextLabel::Property::TEXT_COLOR, Color::GREEN);
233     mLabel.SetBackgroundColor(Color::WHITE);
234     mContainer.Add(mLabel);
235
236     // Clicking ExpandingButton shows the Registered Style buttons, clicking again hides them.
237     Vector2 expandingButtonPosition(mButtonSize.width, mWindowSize.height * STYLE_BUTTON_POSTION_RELATIVE_TO_WINDOW);
238     SetUpExpandingStyleButtons(expandingButtonPosition);
239     window.Add(mExpandingButtons);
240
241     // Add a border for the container so you can see the container is being resized while grabbing the handle.
242     mBorder = Control::New();
243     mBorder.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
244     mBorder.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH);
245     mBorder.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT);
246
247     Dali::Property::Map border;
248     border.Insert(Toolkit::Visual::Property::TYPE, Visual::BORDER);
249     border.Insert(BorderVisual::Property::COLOR, Color::WHITE);
250     border.Insert(BorderVisual::Property::SIZE, 3.f);
251     mBorder.SetProperty(Control::Property::BACKGROUND, border);
252     mContainer.Add(mBorder);
253     mBorder.SetProperty(Actor::Property::VISIBLE, false);
254     mGrabCorner.RaiseToTop();
255
256     mHueAngleIndex      = mLabel.RegisterProperty("hue", 0.0f);
257     Renderer bgRenderer = mLabel.GetRendererAt(0);
258
259     Constraint constraint = Constraint::New<Vector3>(bgRenderer, VisualRenderer::Property::VISUAL_MIX_COLOR, HSVColorConstraint(0.0f, 0.5f, 0.8f));
260     constraint.AddSource(Source(mLabel, mHueAngleIndex));
261     constraint.SetRemoveAction(Constraint::DISCARD);
262     constraint.Apply();
263
264     Animation anim = Animation::New(50.0f);
265     anim.AnimateTo(Property(mLabel, mHueAngleIndex), 6.28318f);
266     anim.SetLooping(true);
267     anim.Play();
268
269     mContainer.RaiseToTop();
270     mGrabCorner.RaiseToTop();
271
272     Property::Value labelText = mLabel.GetProperty(TextLabel::Property::TEXT);
273     std::cout << "Displaying text: \"" << labelText.Get<std::string>() << "\"" << std::endl;
274   }
275
276   // If the styling buttons should colapse (hide) then the color buttons should also hide.
277   bool OnExpandingButtonCollapsing(Demo::ExpandingButtons button)
278   {
279     mCollapseColorsAndStyles = true;
280     HideColorButtons();
281     return true;
282   }
283
284   // Get the style type from the given button
285   StyleType GetStyleTypeFromButton(Toolkit::Button button)
286   {
287     StyleType style = StyleType::TEXT_COLOR;
288
289     if(button == mStyleButtons[StyleType::OUTLINE])
290     {
291       style = StyleType::OUTLINE;
292     }
293     else if(button == mStyleButtons[StyleType::SHADOW])
294     {
295       style = StyleType::SHADOW;
296     }
297     else if(button == mStyleButtons[StyleType::BACKGROUND])
298     {
299       style = StyleType::BACKGROUND;
300     }
301     return style;
302   }
303
304   // Style selected, show color buttons
305   bool OnStyleButtonClicked(Toolkit::Button button)
306   {
307     StyleType selectedStyle = GetStyleTypeFromButton(button);
308     if(mStyleActivatedForColor == selectedStyle)
309     {
310       HideColorButtons();
311     }
312     else
313     {
314       ResetColorButtons(mColorButtons, NUMBER_OF_COLORS);
315       ShowColorButtons(selectedStyle);
316     }
317     return true;
318   }
319
320   // Set style to selected color
321   bool OnColorSelected(Toolkit::Button button)
322   {
323     for(unsigned int index = 0; index < NUMBER_OF_COLORS; index++)
324     {
325       if(mColorButtons[index] == button)
326       {
327         mSelectedColor = AVAILABLE_COLORS[index];
328       }
329     }
330
331     switch(mStyleActivatedForColor)
332     {
333       case TEXT_COLOR:
334       {
335         Animation animation = Animation::New(1.f);
336         animation.AnimateTo(Property(mLabel, TextLabel::Property::TEXT_COLOR), mSelectedColor, AlphaFunction::LINEAR);
337         mCurrentStyleColor[TEXT_COLOR] = mSelectedColor;
338         animation.Play();
339         break;
340       }
341       case OUTLINE:
342       {
343         Property::Map outlineMap;
344         float         outlineWidth = OUTLINE_WIDTH;
345
346         if(mStyleActiveState[OUTLINE])
347         {
348           outlineWidth = (Color::WHITE == mSelectedColor) ? 0.0f : OUTLINE_WIDTH; // toggles outline on/off
349         }
350         mStyleActiveState[OUTLINE] = (outlineWidth > 0.0f) ? true : false;
351
352         outlineMap["color"]         = mSelectedColor;
353         outlineMap["width"]         = outlineWidth;
354         mCurrentStyleColor[OUTLINE] = mSelectedColor;
355         mLabel.SetProperty(TextLabel::Property::OUTLINE, outlineMap);
356         break;
357       }
358       case SHADOW:
359       {
360         Vector2         shadowOffset(SHADOW_OFFSET); // Will be set to zeros if color already set
361         Property::Value value = mLabel.GetProperty(TextLabel::Property::SHADOW);
362         Vector4         currentShadowColor;
363         value.Get(currentShadowColor);
364
365         if(mStyleActiveState[SHADOW])
366         {
367           // toggle shadow off ( zero offset ) if color is already set
368           shadowOffset = (Color::WHITE == mSelectedColor) ? Vector2::ZERO : Vector2(SHADOW_OFFSET);
369         }
370
371         mStyleActiveState[SHADOW]  = (shadowOffset == Vector2::ZERO) ? false : true;
372         mCurrentStyleColor[SHADOW] = mSelectedColor;
373
374         Property::Map shadowMap;
375         shadowMap.Insert("offset", shadowOffset);
376         shadowMap.Insert("color", mSelectedColor);
377         mLabel.SetProperty(TextLabel::Property::SHADOW, shadowMap);
378
379         break;
380       }
381       case BACKGROUND:
382       {
383         Property::Map backgroundMap;
384         auto          backgroundEnabled(true);
385
386         if(mStyleActiveState[BACKGROUND])
387         {
388           backgroundEnabled = (Color::WHITE != mSelectedColor); // toggles background on/off
389         }
390         mStyleActiveState[BACKGROUND] = backgroundEnabled;
391
392         backgroundMap["color"]         = mSelectedColor;
393         backgroundMap["enable"]        = backgroundEnabled;
394         mCurrentStyleColor[BACKGROUND] = mSelectedColor;
395         mLabel.SetProperty(DevelTextLabel::Property::BACKGROUND, backgroundMap);
396
397         break;
398       }
399       default:
400         break;
401     }
402
403     return true;
404   }
405
406   // Set the inital color button that should be be selected.
407   // If the style already has a color set then that should be used
408   void SetInitialSelectedColorButton(StyleType styleButtonIndex)
409   {
410     Vector4 selectedColor = mCurrentStyleColor[styleButtonIndex];
411
412     for(unsigned int i = 0; i < NUMBER_OF_COLORS; i++)
413     {
414       if(AVAILABLE_COLORS[i] == selectedColor)
415       {
416         if(mColorButtons[i])
417         {
418           mColorButtons[i].SetProperty(Toolkit::Button::Property::SELECTED, true);
419         }
420         break;
421       }
422     }
423   }
424
425   // Create a bar of color buttons that the user can select.
426   void ShowColorButtons(StyleType styleButtonIndex)
427   {
428     mCollapseColorsAndStyles = false; // Request to show colors so reset flag
429     mStyleActivatedForColor  = styleButtonIndex;
430
431     for(unsigned int index = 0; index < NUMBER_OF_COLORS; index++)
432     {
433       if(!mColorButtonsAnimation)
434       {
435         mColorButtonsAnimation = Animation::New(0.15f);
436         mColorButtonsAnimation.FinishedSignal().Connect(this, &TextLabelExample::OnColorButtonAnimationFinished);
437       }
438
439       // Create a color button
440       if(!mColorButtons[index])
441       {
442         mColorButtons[index] = RadioButton::New();
443         mColorButtons[index].SetProperty(Actor::Property::SIZE, mButtonSize);
444         mColorButtons[index].ClickedSignal().Connect(this, &TextLabelExample::OnColorSelected);
445         mColorButtons[index].SetProperty(Button::Property::TOGGLABLE, true);
446         Property::Map propertyMap;
447         propertyMap.Insert(Visual::Property::TYPE, Visual::COLOR);
448         propertyMap.Insert(ColorVisual::Property::MIX_COLOR, AVAILABLE_COLORS[index]);
449         mColorButtons[index].SetProperty(Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, propertyMap);
450         mColorButtons[index].SetProperty(Toolkit::Button::Property::UNSELECTED_VISUAL, propertyMap);
451         mColorButtons[index].SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
452         mColorButtons[index].SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_CENTER);
453
454         propertyMap.Insert(Visual::Property::TYPE, Visual::COLOR);
455         propertyMap.Insert(ColorVisual::Property::MIX_COLOR, AVAILABLE_COLORS[index]);
456         mColorButtons[index].SetProperty(Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, propertyMap);
457
458         mColorButtons[index].SetProperty(Toolkit::Button::Property::SELECTED_VISUAL,
459                                          Property::Map().Add(Visual::Property::TYPE, Visual::BORDER).Add(BorderVisual::Property::COLOR, Color::WHITE).Add(BorderVisual::Property::SIZE, 4.0f).Add(BorderVisual::Property::ANTI_ALIASING, true));
460
461         // Use a white button with 50% transparency as a clear color button
462         if(Color::WHITE == AVAILABLE_COLORS[index] && styleButtonIndex != StyleType::TEXT_COLOR)
463         {
464           mColorButtons[index].SetProperty(Actor::Property::OPACITY, 0.5f);
465
466           mColorButtons[index].SetProperty(Toolkit::Button::Property::LABEL,
467                                            Property::Map().Add(Toolkit::Visual::Property::TYPE, Toolkit::Visual::TEXT).Add(Toolkit::TextVisual::Property::HORIZONTAL_ALIGNMENT, HorizontalAlignment::CENTER).Add(Toolkit::TextVisual::Property::TEXT, "off"));
468         }
469       }
470
471       SetInitialSelectedColorButton(mStyleActivatedForColor);
472
473       mColorButtons[index].Unparent();
474
475       mStyleButtons[styleButtonIndex].Add(mColorButtons[index]);
476       mColorButtons[index].Lower();
477
478       // Position button using nice animation
479       mColorButtons[index].SetProperty(Actor::Property::POSITION_Y, -GAP_BETWEEN_BUTTONS);
480       float         desiredPosition      = -(mButtonSize.height + GAP_BETWEEN_BUTTONS) * (index);
481       AlphaFunction focusedAlphaFunction = AlphaFunction(Vector2(0.32f, 0.08f), Vector2(0.38f, 1.72f));
482       mColorButtonsAnimation.AnimateBy(Property(mColorButtons[index], Actor::Property::POSITION_Y), desiredPosition, focusedAlphaFunction);
483     }
484
485     mColorButtonsHidden = false;
486     mColorButtonsAnimation.Play();
487   }
488
489   // Remove the color buttons when not being shown.
490   void ResetColorButtons(Button buttons[], unsigned int numberOfButtons)
491   {
492     for(unsigned int index = 0; index < numberOfButtons; index++)
493     {
494       UnparentAndReset(buttons[index]);
495     }
496   }
497
498   void OnColorButtonAnimationFinished(Animation& animation)
499   {
500     animation.Clear();
501     if(mColorButtonsHidden)
502     {
503       ResetColorButtons(mColorButtons, NUMBER_OF_COLORS);
504       animation.Reset(); // Handle reset
505       if(mCollapseColorsAndStyles)
506       {
507         mExpandingButtons.Collapse();
508       }
509     }
510   }
511
512   // Create the style buttons that will expand from the expanding button.
513   void CreateStyleButtons()
514   {
515     for(unsigned int index = 0; index < NUMBER_OF_STYLES; index++)
516     {
517       if(!mStyleButtons[index])
518       {
519         mStyleButtons[index] = PushButton::New();
520         mStyleButtons[index].SetProperty(Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, BUTTON_IMAGES[index]);
521         mStyleButtons[index].SetProperty(Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, STYLE_SELECTED_IMAGE);
522         mStyleButtons[index].SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
523         mStyleButtons[index].SetProperty(Actor::Property::SIZE, mButtonSize);
524         mStyleButtons[index].ClickedSignal().Connect(this, &TextLabelExample::OnStyleButtonClicked);
525       }
526     }
527   }
528
529   // Animate away the color bar.
530   void HideColorButtons()
531   {
532     if(!mColorButtonsHidden)
533     {
534       for(unsigned int index = 0; index < NUMBER_OF_COLORS; index++)
535       {
536         mColorButtonsAnimation.AnimateTo(Property(mColorButtons[index], Actor::Property::POSITION_Y), 0.0f);
537       }
538       mColorButtonsHidden = true;
539       mColorButtonsAnimation.Play();
540     }
541     mStyleActivatedForColor = NUMBER_OF_STYLES;
542   }
543
544   //  Request the expanding button to collapse.
545   void HideStyleAndColorButtons()
546   {
547     mCollapseColorsAndStyles = true;
548     if(mColorButtonsHidden)
549     {
550       mExpandingButtons.Collapse();
551     }
552     else
553     {
554       HideColorButtons();
555     }
556   }
557
558   // Resize the text-label with pan gesture
559   void OnPan(Actor actor, const PanGesture& gesture)
560   {
561     // Reset mLayoutSize when the pan starts
562     GestureState state = gesture.GetState();
563     if(state == GestureState::STARTED)
564     {
565       if(mLayoutSize.x < 2.0f)
566       {
567         mLayoutSize.x = 2.0f;
568       }
569
570       if(mLayoutSize.y < 2.0f)
571       {
572         mLayoutSize.y = 2.0f;
573       }
574
575       // Only show the border during the panning
576       mBorder.SetProperty(Actor::Property::VISIBLE, true);
577
578       HideStyleAndColorButtons();
579     }
580
581     const Vector2& displacement = gesture.GetDisplacement();
582     mLayoutSize.x += displacement.x * 2.0f;
583     mLayoutSize.y += displacement.y * 2.0f;
584
585     if(mLayoutSize.x >= 2.0f ||
586        mLayoutSize.y >= 2.0f)
587     {
588       mLayoutSize.x = std::min(mLayoutSize.x, mWindowSize.width);
589       mLayoutSize.y = std::min(mLayoutSize.y, mWindowSize.height * .9f);
590
591       // Avoid pixel mis-alignment issue
592       Vector2 clampedSize = Vector2(std::max(ConvertToEven(static_cast<int>(mLayoutSize.x)), 2),
593                                     std::max(ConvertToEven(static_cast<int>(mLayoutSize.y)), 2));
594
595       mContainer.SetProperty(Actor::Property::SIZE, clampedSize);
596     }
597
598     if(state == GestureState::CANCELLED || state == GestureState::FINISHED)
599     {
600       // Resize the text label to match the container size when panning is finished
601       mLabel.SetProperty(Actor::Property::SIZE, mLayoutSize);
602       mBorder.SetProperty(Actor::Property::VISIBLE, false);
603     }
604   }
605
606   /**
607    * Main key event handler
608    */
609   void OnKeyEvent(const KeyEvent& event)
610   {
611     if(event.GetState() == KeyEvent::DOWN)
612     {
613       if(IsKey(event, DALI_KEY_ESCAPE) || IsKey(event, DALI_KEY_BACK))
614       {
615         mApplication.Quit();
616       }
617       else if(event.IsCtrlModifier())
618       {
619         switch(event.GetKeyCode())
620         {
621           // Select rendering back-end
622           case KEY_ZERO: // fall through
623           case KEY_ONE:
624           {
625             mLabel.SetProperty(DevelTextLabel::Property::RENDERING_BACKEND, event.GetKeyCode() - 10);
626             break;
627           }
628           case KEY_A: // Animate text colour
629           {
630             Animation animation = Animation::New(2.f);
631             animation.AnimateTo(Property(mLabel, TextLabel::Property::TEXT_COLOR), Color::RED, AlphaFunction::SIN);
632             animation.SetLoopCount(3);
633             animation.Play();
634             break;
635           }
636           case KEY_F: // Fill vertically
637           {
638             if(ResizePolicy::DIMENSION_DEPENDENCY == mLabel.GetResizePolicy(Dimension::HEIGHT))
639             {
640               mLabel.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT);
641             }
642             else
643             {
644               mLabel.SetResizePolicy(ResizePolicy::DIMENSION_DEPENDENCY, Dimension::HEIGHT);
645             }
646             break;
647           }
648           case KEY_H: // Horizontal alignment
649           {
650             if(++mAlignment >= H_ALIGNMENT_STRING_COUNT)
651             {
652               mAlignment = 0u;
653             }
654
655             mLabel.SetProperty(TextLabel::Property::HORIZONTAL_ALIGNMENT, H_ALIGNMENT_STRING_TABLE[mAlignment]);
656             break;
657           }
658           case KEY_V: // Vertical alignment
659           {
660             if(++mAlignment >= V_ALIGNMENT_STRING_COUNT)
661             {
662               mAlignment = 0u;
663             }
664
665             mLabel.SetProperty(TextLabel::Property::VERTICAL_ALIGNMENT, V_ALIGNMENT_STRING_TABLE[mAlignment]);
666             break;
667           }
668           case KEY_M: // Multi-line
669           {
670             bool multiLine = mLabel.GetProperty<bool>(TextLabel::Property::MULTI_LINE);
671             mLabel.SetProperty(TextLabel::Property::MULTI_LINE, !multiLine);
672             break;
673           }
674           case KEY_L: // Language
675           {
676             const Language& language = LANGUAGES[mLanguageId];
677
678             mLabel.SetProperty(TextLabel::Property::TEXT, language.text);
679
680             if(++mLanguageId >= NUMBER_OF_LANGUAGES)
681             {
682               mLanguageId = 0u;
683             }
684             break;
685           }
686           case KEY_S: // Shadow color
687           {
688             Property::Value value = mLabel.GetProperty(TextLabel::Property::SHADOW);
689             Vector4         shadowColor;
690             value.Get(shadowColor);
691             Property::Map shadowMap;
692             if(Color::BLACK == shadowColor)
693             {
694               shadowMap.Insert("color", Color::RED);
695               mLabel.SetProperty(TextLabel::Property::SHADOW, shadowMap);
696             }
697             else
698             {
699               shadowMap.Insert("color", Color::BLACK);
700               mLabel.SetProperty(TextLabel::Property::SHADOW, shadowMap);
701             }
702             break;
703           }
704           case KEY_U: // Markup
705           {
706             mLabel.SetProperty(TextLabel::Property::ENABLE_MARKUP, true);
707             mLabel.SetProperty(TextLabel::Property::TEXT, "<font family='DejaVuSerif' size='18'>H<color value='blue'>ello</color> <font weight='bold'>world</font> demo</font>");
708             break;
709           }
710           case KEY_PLUS: // Increase shadow offset
711           {
712             Property::Value value = mLabel.GetProperty(TextLabel::Property::SHADOW);
713             Vector2         shadowOffset;
714             value.Get(shadowOffset);
715             shadowOffset += Vector2(1.0f, 1.0f);
716
717             Property::Map shadowMap;
718             shadowMap.Insert("offset", shadowOffset);
719             mLabel.SetProperty(TextLabel::Property::SHADOW, shadowMap);
720             break;
721           }
722           case KEY_MINUS: // Decrease shadow offset
723           {
724             Property::Value value = mLabel.GetProperty(TextLabel::Property::SHADOW);
725             Vector2         shadowOffset;
726             value.Get(shadowOffset);
727             shadowOffset -= Vector2(1.0f, 1.0f);
728
729             Property::Map shadowMap;
730             shadowMap.Insert("offset", shadowOffset);
731             mLabel.SetProperty(TextLabel::Property::SHADOW, shadowMap);
732             break;
733           }
734         }
735       }
736     }
737   }
738
739 private:
740   Application& mApplication;
741
742   TextLabel mLabel;
743
744   Demo::ExpandingButtons mExpandingButtons;
745   PushButton             mStyleButtons[NUMBER_OF_STYLES];
746   bool                   mStyleActiveState[NUMBER_OF_STYLES];
747
748   Vector4 mCurrentStyleColor[NUMBER_OF_STYLES];
749
750   Vector4 mSelectedColor;
751
752   Button mColorButtons[NUMBER_OF_COLORS];
753
754   StyleType mStyleActivatedForColor; // The style that the color bar is connected to
755
756   Control mContainer;
757   Control mGrabCorner;
758   Control mBorder;
759
760   PanGestureDetector mPanGestureDetector;
761
762   Vector2 mLayoutSize;
763
764   Animation mColorButtonsAnimation;
765
766   Size mWindowSize;
767   Size mButtonSize;
768
769   unsigned int    mLanguageId;
770   unsigned int    mAlignment;
771   Property::Index mHueAngleIndex;
772   Property::Index mOverrideMixColorIndex;
773
774   bool mColorButtonsHidden;
775   bool mCollapseColorsAndStyles;
776 };
777
778 int DALI_EXPORT_API main(int argc, char** argv)
779 {
780   Application      application = Application::New(&argc, &argv, DEMO_THEME_PATH);
781   TextLabelExample test(application);
782   application.MainLoop();
783   return 0;
784 }