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