[dali_2.3.20] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-placeholder-handler.cpp
1 /*
2  * Copyright (c) 2021 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 <dali-toolkit/internal/text/text-controller-placeholder-handler.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23
24 // INTERNAL INCLUDES
25 #include <dali-toolkit/internal/text/character-set-conversion.h>
26 #include <dali-toolkit/internal/text/text-controller-impl.h>
27 #include <dali-toolkit/internal/text/text-font-style.h>
28 #include <dali-toolkit/public-api/controls/text-controls/placeholder-properties.h>
29
30 namespace
31 {
32 #if defined(DEBUG_ENABLED)
33 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
34 #endif
35
36 const std::string EMPTY_STRING("");
37
38 const char* const PLACEHOLDER_TEXT         = "text";
39 const char* const PLACEHOLDER_TEXT_FOCUSED = "textFocused";
40 const char* const PLACEHOLDER_COLOR        = "color";
41 const char* const PLACEHOLDER_FONT_FAMILY  = "fontFamily";
42 const char* const PLACEHOLDER_FONT_STYLE   = "fontStyle";
43 const char* const PLACEHOLDER_POINT_SIZE   = "pointSize";
44 const char* const PLACEHOLDER_PIXEL_SIZE   = "pixelSize";
45 const char* const PLACEHOLDER_ELLIPSIS     = "ellipsis";
46
47 } // namespace
48
49 namespace Dali
50 {
51 namespace Toolkit
52 {
53 namespace Text
54 {
55 void Controller::PlaceholderHandler::SetPlaceholderTextElideEnabled(Controller& controller, bool enabled)
56 {
57   controller.mImpl->mEventData->mIsPlaceholderElideEnabled = enabled;
58   controller.mImpl->mEventData->mPlaceholderEllipsisFlag   = true;
59
60   // Update placeholder if there is no text
61   if(controller.mImpl->IsShowingPlaceholderText() ||
62      (0u == controller.mImpl->mModel->mLogicalModel->mText.Count()))
63   {
64     controller.ShowPlaceholderText();
65   }
66 }
67
68 bool Controller::PlaceholderHandler::IsPlaceholderTextElideEnabled(const Controller& controller)
69 {
70   return controller.mImpl->mEventData->mIsPlaceholderElideEnabled;
71 }
72
73 void Controller::PlaceholderHandler::SetPlaceholderText(Controller& controller, PlaceholderType type, const std::string& text)
74 {
75   if(NULL != controller.mImpl->mEventData)
76   {
77     if(PLACEHOLDER_TYPE_INACTIVE == type)
78     {
79       controller.mImpl->mEventData->mPlaceholderTextInactive = text;
80     }
81     else
82     {
83       controller.mImpl->mEventData->mPlaceholderTextActive = text;
84     }
85
86     // Update placeholder if there is no text
87     if(controller.mImpl->IsShowingPlaceholderText() ||
88        (0u == controller.mImpl->mModel->mLogicalModel->mText.Count()))
89     {
90       controller.ShowPlaceholderText();
91     }
92   }
93 }
94
95 void Controller::PlaceholderHandler::GetPlaceholderText(const Controller& controller, PlaceholderType type, std::string& text)
96 {
97   if(NULL != controller.mImpl->mEventData)
98   {
99     if(PLACEHOLDER_TYPE_INACTIVE == type)
100     {
101       text = controller.mImpl->mEventData->mPlaceholderTextInactive;
102     }
103     else
104     {
105       text = controller.mImpl->mEventData->mPlaceholderTextActive;
106     }
107   }
108 }
109
110 void Controller::PlaceholderHandler::SetPlaceholderFontFamily(Controller& controller, const std::string& placeholderTextFontFamily)
111 {
112   if(NULL != controller.mImpl->mEventData)
113   {
114     if(NULL == controller.mImpl->mEventData->mPlaceholderFont)
115     {
116       controller.mImpl->mEventData->mPlaceholderFont = new FontDefaults();
117     }
118
119     controller.mImpl->mEventData->mPlaceholderFont->mFontDescription.family = placeholderTextFontFamily;
120     DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::SetPlaceholderFontFamily %s\n", placeholderTextFontFamily.c_str());
121     controller.mImpl->mEventData->mPlaceholderFont->familyDefined = !placeholderTextFontFamily.empty();
122
123     controller.mImpl->RequestRelayout();
124   }
125 }
126
127 const std::string& Controller::PlaceholderHandler::GetPlaceholderFontFamily(const Controller& controller)
128 {
129   if((NULL != controller.mImpl->mEventData) && (NULL != controller.mImpl->mEventData->mPlaceholderFont))
130   {
131     return controller.mImpl->mEventData->mPlaceholderFont->mFontDescription.family;
132   }
133
134   return EMPTY_STRING;
135 }
136
137 void Controller::PlaceholderHandler::SetPlaceholderTextFontWeight(Controller& controller, FontWeight weight)
138 {
139   if(NULL != controller.mImpl->mEventData)
140   {
141     if(NULL == controller.mImpl->mEventData->mPlaceholderFont)
142     {
143       controller.mImpl->mEventData->mPlaceholderFont = new FontDefaults();
144     }
145
146     controller.mImpl->mEventData->mPlaceholderFont->mFontDescription.weight = weight;
147     controller.mImpl->mEventData->mPlaceholderFont->weightDefined           = true;
148
149     controller.mImpl->RequestRelayout();
150   }
151 }
152
153 bool Controller::PlaceholderHandler::IsPlaceholderTextFontWeightDefined(const Controller& controller)
154 {
155   if((NULL != controller.mImpl->mEventData) && (NULL != controller.mImpl->mEventData->mPlaceholderFont))
156   {
157     return controller.mImpl->mEventData->mPlaceholderFont->weightDefined;
158   }
159   return false;
160 }
161
162 FontWeight Controller::PlaceholderHandler::GetPlaceholderTextFontWeight(const Controller& controller)
163 {
164   if((NULL != controller.mImpl->mEventData) && (NULL != controller.mImpl->mEventData->mPlaceholderFont))
165   {
166     return controller.mImpl->mEventData->mPlaceholderFont->mFontDescription.weight;
167   }
168
169   return TextAbstraction::FontWeight::NORMAL;
170 }
171
172 void Controller::PlaceholderHandler::SetPlaceholderTextFontWidth(Controller& controller, FontWidth width)
173 {
174   if(NULL != controller.mImpl->mEventData)
175   {
176     if(NULL == controller.mImpl->mEventData->mPlaceholderFont)
177     {
178       controller.mImpl->mEventData->mPlaceholderFont = new FontDefaults();
179     }
180
181     controller.mImpl->mEventData->mPlaceholderFont->mFontDescription.width = width;
182     controller.mImpl->mEventData->mPlaceholderFont->widthDefined           = true;
183
184     controller.mImpl->RequestRelayout();
185   }
186 }
187
188 bool Controller::PlaceholderHandler::IsPlaceholderTextFontWidthDefined(const Controller& controller)
189 {
190   if((NULL != controller.mImpl->mEventData) && (NULL != controller.mImpl->mEventData->mPlaceholderFont))
191   {
192     return controller.mImpl->mEventData->mPlaceholderFont->widthDefined;
193   }
194   return false;
195 }
196
197 FontWidth Controller::PlaceholderHandler::GetPlaceholderTextFontWidth(const Controller& controller)
198 {
199   if((NULL != controller.mImpl->mEventData) && (NULL != controller.mImpl->mEventData->mPlaceholderFont))
200   {
201     return controller.mImpl->mEventData->mPlaceholderFont->mFontDescription.width;
202   }
203
204   return TextAbstraction::FontWidth::NORMAL;
205 }
206
207 void Controller::PlaceholderHandler::SetPlaceholderTextFontSlant(Controller& controller, FontSlant slant)
208 {
209   if(NULL != controller.mImpl->mEventData)
210   {
211     if(NULL == controller.mImpl->mEventData->mPlaceholderFont)
212     {
213       controller.mImpl->mEventData->mPlaceholderFont = new FontDefaults();
214     }
215
216     controller.mImpl->mEventData->mPlaceholderFont->mFontDescription.slant = slant;
217     controller.mImpl->mEventData->mPlaceholderFont->slantDefined           = true;
218
219     controller.mImpl->RequestRelayout();
220   }
221 }
222
223 bool Controller::PlaceholderHandler::IsPlaceholderTextFontSlantDefined(const Controller& controller)
224 {
225   if((NULL != controller.mImpl->mEventData) && (NULL != controller.mImpl->mEventData->mPlaceholderFont))
226   {
227     return controller.mImpl->mEventData->mPlaceholderFont->slantDefined;
228   }
229   return false;
230 }
231
232 FontSlant Controller::PlaceholderHandler::GetPlaceholderTextFontSlant(const Controller& controller)
233 {
234   if((NULL != controller.mImpl->mEventData) && (NULL != controller.mImpl->mEventData->mPlaceholderFont))
235   {
236     return controller.mImpl->mEventData->mPlaceholderFont->mFontDescription.slant;
237   }
238
239   return TextAbstraction::FontSlant::NORMAL;
240 }
241
242 void Controller::PlaceholderHandler::SetPlaceholderTextFontSize(Controller& controller, float fontSize, FontSizeType type)
243 {
244   if(NULL != controller.mImpl->mEventData)
245   {
246     if(NULL == controller.mImpl->mEventData->mPlaceholderFont)
247     {
248       controller.mImpl->mEventData->mPlaceholderFont = new FontDefaults();
249     }
250
251     switch(type)
252     {
253       case POINT_SIZE:
254       {
255         controller.mImpl->mEventData->mPlaceholderFont->mDefaultPointSize = fontSize;
256         controller.mImpl->mEventData->mPlaceholderFont->sizeDefined       = true;
257         controller.mImpl->mEventData->mIsPlaceholderPixelSize             = false; // Font size flag
258         break;
259       }
260       case PIXEL_SIZE:
261       {
262         // Point size = Pixel size * 72.f / DPI
263         unsigned int                horizontalDpi = 0u;
264         unsigned int                verticalDpi   = 0u;
265         TextAbstraction::FontClient fontClient    = TextAbstraction::FontClient::Get();
266         fontClient.GetDpi(horizontalDpi, verticalDpi);
267
268         controller.mImpl->mEventData->mPlaceholderFont->mDefaultPointSize = (fontSize * 72.f) / static_cast<float>(horizontalDpi);
269         controller.mImpl->mEventData->mPlaceholderFont->sizeDefined       = true;
270         controller.mImpl->mEventData->mIsPlaceholderPixelSize             = true; // Font size flag
271         break;
272       }
273     }
274
275     controller.mImpl->RequestRelayout();
276   }
277 }
278
279 float Controller::PlaceholderHandler::GetPlaceholderTextFontSize(const Controller& controller, FontSizeType type)
280 {
281   float value = 0.0f;
282   if(NULL != controller.mImpl->mEventData)
283   {
284     switch(type)
285     {
286       case POINT_SIZE:
287       {
288         if(NULL != controller.mImpl->mEventData->mPlaceholderFont)
289         {
290           value = controller.mImpl->mEventData->mPlaceholderFont->mDefaultPointSize;
291         }
292         else
293         {
294           // If the placeholder text font size is not set, then return the default font size.
295           value = controller.GetDefaultFontSize(POINT_SIZE);
296         }
297         break;
298       }
299       case PIXEL_SIZE:
300       {
301         if(NULL != controller.mImpl->mEventData->mPlaceholderFont)
302         {
303           // Pixel size = Point size * DPI / 72.f
304           unsigned int                horizontalDpi = 0u;
305           unsigned int                verticalDpi   = 0u;
306           TextAbstraction::FontClient fontClient    = TextAbstraction::FontClient::Get();
307           fontClient.GetDpi(horizontalDpi, verticalDpi);
308
309           value = controller.mImpl->mEventData->mPlaceholderFont->mDefaultPointSize * static_cast<float>(horizontalDpi) / 72.f;
310         }
311         else
312         {
313           // If the placeholder text font size is not set, then return the default font size.
314           value = controller.GetDefaultFontSize(PIXEL_SIZE);
315         }
316         break;
317       }
318     }
319     return value;
320   }
321
322   return value;
323 }
324
325 void Controller::PlaceholderHandler::SetPlaceholderTextColor(Controller& controller, const Vector4& textColor)
326 {
327   if(NULL != controller.mImpl->mEventData)
328   {
329     controller.mImpl->mEventData->mPlaceholderTextColor = textColor;
330   }
331
332   if(controller.mImpl->IsShowingPlaceholderText())
333   {
334     controller.mImpl->mModel->mVisualModel->SetTextColor(textColor);
335     controller.mImpl->RequestRelayout();
336   }
337 }
338
339 const Vector4& Controller::PlaceholderHandler::GetPlaceholderTextColor(const Controller& controller)
340 {
341   if(NULL != controller.mImpl->mEventData)
342   {
343     return controller.mImpl->mEventData->mPlaceholderTextColor;
344   }
345
346   return Color::BLACK;
347 }
348
349 void Controller::PlaceholderHandler::SetPlaceholderProperty(Controller& controller, const Property::Map& map)
350 {
351   const Property::Map::SizeType count = map.Count();
352
353   for(Property::Map::SizeType position = 0; position < count; ++position)
354   {
355     KeyValuePair     keyValue = map.GetKeyValue(position);
356     Property::Key&   key      = keyValue.first;
357     Property::Value& value    = keyValue.second;
358
359     if(key == Toolkit::Text::PlaceHolder::Property::TEXT || key == PLACEHOLDER_TEXT)
360     {
361       std::string text = "";
362       value.Get(text);
363       SetPlaceholderText(controller, Controller::PLACEHOLDER_TYPE_INACTIVE, text);
364     }
365     else if(key == Toolkit::Text::PlaceHolder::Property::TEXT_FOCUSED || key == PLACEHOLDER_TEXT_FOCUSED)
366     {
367       std::string text = "";
368       value.Get(text);
369       SetPlaceholderText(controller, Controller::PLACEHOLDER_TYPE_ACTIVE, text);
370     }
371     else if(key == Toolkit::Text::PlaceHolder::Property::COLOR || key == PLACEHOLDER_COLOR)
372     {
373       Vector4 textColor;
374       value.Get(textColor);
375       if(GetPlaceholderTextColor(controller) != textColor)
376       {
377         SetPlaceholderTextColor(controller, textColor);
378       }
379     }
380     else if(key == Toolkit::Text::PlaceHolder::Property::FONT_FAMILY || key == PLACEHOLDER_FONT_FAMILY)
381     {
382       std::string fontFamily = "";
383       value.Get(fontFamily);
384       SetPlaceholderFontFamily(controller, fontFamily);
385     }
386     else if(key == Toolkit::Text::PlaceHolder::Property::FONT_STYLE || key == PLACEHOLDER_FONT_STYLE)
387     {
388       SetFontStyleProperty(&controller, value, Text::FontStyle::PLACEHOLDER);
389     }
390     else if(key == Toolkit::Text::PlaceHolder::Property::POINT_SIZE || key == PLACEHOLDER_POINT_SIZE)
391     {
392       float pointSize;
393       value.Get(pointSize);
394       if(!Equals(GetPlaceholderTextFontSize(controller, Text::Controller::POINT_SIZE), pointSize))
395       {
396         SetPlaceholderTextFontSize(controller, pointSize, Text::Controller::POINT_SIZE);
397       }
398     }
399     else if(key == Toolkit::Text::PlaceHolder::Property::PIXEL_SIZE || key == PLACEHOLDER_PIXEL_SIZE)
400     {
401       float pixelSize;
402       value.Get(pixelSize);
403       if(!Equals(GetPlaceholderTextFontSize(controller, Text::Controller::PIXEL_SIZE), pixelSize))
404       {
405         SetPlaceholderTextFontSize(controller, pixelSize, Text::Controller::PIXEL_SIZE);
406       }
407     }
408     else if(key == Toolkit::Text::PlaceHolder::Property::ELLIPSIS || key == PLACEHOLDER_ELLIPSIS)
409     {
410       bool ellipsis;
411       value.Get(ellipsis);
412       SetPlaceholderTextElideEnabled(controller, ellipsis);
413     }
414   }
415 }
416
417 void Controller::PlaceholderHandler::GetPlaceholderProperty(Controller& controller, Property::Map& map)
418 {
419   if(NULL != controller.mImpl->mEventData)
420   {
421     if(!controller.mImpl->mEventData->mPlaceholderTextActive.empty())
422     {
423       map[Text::PlaceHolder::Property::TEXT_FOCUSED] = controller.mImpl->mEventData->mPlaceholderTextActive;
424     }
425     if(!controller.mImpl->mEventData->mPlaceholderTextInactive.empty())
426     {
427       map[Text::PlaceHolder::Property::TEXT] = controller.mImpl->mEventData->mPlaceholderTextInactive;
428     }
429
430     map[Text::PlaceHolder::Property::COLOR]       = controller.mImpl->mEventData->mPlaceholderTextColor;
431     map[Text::PlaceHolder::Property::FONT_FAMILY] = GetPlaceholderFontFamily(controller);
432
433     Property::Value fontStyleMapGet;
434     GetFontStyleProperty(&controller, fontStyleMapGet, Text::FontStyle::PLACEHOLDER);
435     map[Text::PlaceHolder::Property::FONT_STYLE] = fontStyleMapGet;
436
437     // Choose font size : POINT_SIZE or PIXEL_SIZE
438     if(!controller.mImpl->mEventData->mIsPlaceholderPixelSize)
439     {
440       map[Text::PlaceHolder::Property::POINT_SIZE] = GetPlaceholderTextFontSize(controller, Text::Controller::POINT_SIZE);
441     }
442     else
443     {
444       map[Text::PlaceHolder::Property::PIXEL_SIZE] = GetPlaceholderTextFontSize(controller, Text::Controller::PIXEL_SIZE);
445     }
446
447     if(controller.mImpl->mEventData->mPlaceholderEllipsisFlag)
448     {
449       map[Text::PlaceHolder::Property::ELLIPSIS] = IsPlaceholderTextElideEnabled(controller);
450     }
451   }
452 }
453
454 void Controller::PlaceholderHandler::ShowPlaceholderText(Controller& controller)
455 {
456   Controller::Impl& impl = *controller.mImpl;
457
458   if(impl.IsPlaceholderAvailable())
459   {
460     EventData*& eventData = impl.mEventData;
461     DALI_ASSERT_DEBUG(eventData && "No placeholder text available");
462
463     if(NULL == eventData)
464     {
465       return;
466     }
467
468     eventData->mIsShowingPlaceholderText = true;
469
470     // Disable handles when showing place-holder text
471     DecoratorPtr& decorator = eventData->mDecorator;
472     decorator->SetHandleActive(GRAB_HANDLE, false);
473     decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
474     decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
475
476     const char* text(NULL);
477     size_t      size(0);
478
479     // TODO - Switch Placeholder text when changing state
480     std::string& placeholderTextActive = eventData->mPlaceholderTextActive;
481     if((EventData::INACTIVE != eventData->mState) &&
482        (0u != placeholderTextActive.c_str()))
483     {
484       text = placeholderTextActive.c_str();
485       size = placeholderTextActive.size();
486     }
487     else
488     {
489       std::string& placeholderTextInactive = eventData->mPlaceholderTextInactive;
490       text                                 = placeholderTextInactive.c_str();
491       size                                 = placeholderTextInactive.size();
492     }
493
494     TextUpdateInfo& textUpdateInfo             = impl.mTextUpdateInfo;
495     textUpdateInfo.mCharacterIndex             = 0u;
496     textUpdateInfo.mNumberOfCharactersToRemove = textUpdateInfo.mPreviousNumberOfCharacters;
497
498     // Reset model for showing placeholder.
499     ModelPtr&        model        = impl.mModel;
500     LogicalModelPtr& logicalModel = model->mLogicalModel;
501     logicalModel->mText.Clear();
502     model->mVisualModel->SetTextColor(eventData->mPlaceholderTextColor);
503
504     // Convert text into UTF-32
505     Vector<Character>& utf32Characters = logicalModel->mText;
506     utf32Characters.Resize(size);
507
508     // This is a bit horrible but std::string returns a (signed) char*
509     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>(text);
510
511     // Transform a text array encoded in utf8 into an array encoded in utf32.
512     // It returns the actual number of characters.
513     const Length characterCount = Utf8ToUtf32(utf8, size, utf32Characters.Begin());
514     utf32Characters.Resize(characterCount);
515
516     // The characters to be added.
517     textUpdateInfo.mNumberOfCharactersToAdd = characterCount;
518
519     // Reset the cursor position
520     eventData->mPrimaryCursorPosition = 0;
521
522     // The natural size needs to be re-calculated.
523     impl.mRecalculateNaturalSize = true;
524
525     // The text direction needs to be updated.
526     impl.mUpdateTextDirection = true;
527
528     // Apply modifications to the model
529     impl.mOperationsPending = ALL_OPERATIONS;
530
531     // Update the rest of the model during size negotiation
532     impl.QueueModifyEvent(ModifyEvent::TEXT_REPLACED);
533   }
534 }
535
536 } // namespace Text
537
538 } // namespace Toolkit
539
540 } // namespace Dali