2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/text/controller/text-controller-placeholder-handler.h>
22 #include <dali/integration-api/debug.h>
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>
32 #if defined(DEBUG_ENABLED)
33 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
36 const char* EMPTY_STRING = "";
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";
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
52 static Dali::Property::Index GetIntKey(const Dali::Property::Key& key)
54 if(key.type == Dali::Property::Key::INDEX)
59 if(key.stringKey == PLACEHOLDER_TEXT)
61 return Dali::Toolkit::Text::PlaceHolder::Property::TEXT;
63 else if(key.stringKey == PLACEHOLDER_TEXT_FOCUSED)
65 return Dali::Toolkit::Text::PlaceHolder::Property::TEXT_FOCUSED;
67 else if(key.stringKey == PLACEHOLDER_COLOR)
69 return Dali::Toolkit::Text::PlaceHolder::Property::COLOR;
71 else if(key.stringKey == PLACEHOLDER_FONT_FAMILY)
73 return Dali::Toolkit::Text::PlaceHolder::Property::FONT_FAMILY;
75 else if(key.stringKey == PLACEHOLDER_FONT_STYLE)
77 return Dali::Toolkit::Text::PlaceHolder::Property::FONT_STYLE;
79 else if(key.stringKey == PLACEHOLDER_POINT_SIZE)
81 return Dali::Toolkit::Text::PlaceHolder::Property::POINT_SIZE;
83 else if(key.stringKey == PLACEHOLDER_PIXEL_SIZE)
85 return Dali::Toolkit::Text::PlaceHolder::Property::PIXEL_SIZE;
87 else if(key.stringKey == PLACEHOLDER_ELLIPSIS)
89 return Dali::Toolkit::Text::PlaceHolder::Property::ELLIPSIS;
92 return Dali::Property::INVALID_INDEX;
103 void Controller::PlaceholderHandler::SetPlaceholderTextElideEnabled(Controller& controller, bool enabled)
105 controller.mImpl->mEventData->mIsPlaceholderElideEnabled = enabled;
106 controller.mImpl->mEventData->mPlaceholderEllipsisFlag = true;
108 // Update placeholder if there is no text
109 if(controller.mImpl->IsShowingPlaceholderText() ||
110 (0u == controller.mImpl->mModel->mLogicalModel->mText.Count()))
112 ShowPlaceholderText(*controller.mImpl);
116 bool Controller::PlaceholderHandler::IsPlaceholderTextElideEnabled(const Controller& controller)
118 return controller.mImpl->mEventData->mIsPlaceholderElideEnabled;
121 void Controller::PlaceholderHandler::SetPlaceholderText(Controller& controller, PlaceholderType type, const std::string& text)
123 if(NULL != controller.mImpl->mEventData)
125 if(PLACEHOLDER_TYPE_INACTIVE == type)
127 controller.mImpl->mEventData->mPlaceholderTextInactive = text;
131 controller.mImpl->mEventData->mPlaceholderTextActive = text;
134 // Update placeholder if there is no text
135 if(controller.mImpl->IsShowingPlaceholderText() ||
136 (0u == controller.mImpl->mModel->mLogicalModel->mText.Count()))
138 ShowPlaceholderText(*controller.mImpl);
143 void Controller::PlaceholderHandler::GetPlaceholderText(const Controller& controller, PlaceholderType type, std::string& text)
145 if(NULL != controller.mImpl->mEventData)
147 if(PLACEHOLDER_TYPE_INACTIVE == type)
149 text = controller.mImpl->mEventData->mPlaceholderTextInactive;
153 text = controller.mImpl->mEventData->mPlaceholderTextActive;
158 void Controller::PlaceholderHandler::SetPlaceholderFontFamily(Controller& controller, const std::string& placeholderTextFontFamily)
160 if(NULL != controller.mImpl->mEventData)
162 // if mPlaceholderFont is null, create an instance.
163 CreatePlaceholderFont(controller);
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();
169 controller.mImpl->RequestRelayout();
173 std::string Controller::PlaceholderHandler::GetPlaceholderFontFamily(const Controller& controller)
175 if((NULL != controller.mImpl->mEventData) && (NULL != controller.mImpl->mEventData->mPlaceholderFont))
177 return controller.mImpl->mEventData->mPlaceholderFont->mFontDescription.family;
183 void Controller::PlaceholderHandler::SetPlaceholderTextFontWeight(Controller& controller, FontWeight weight)
185 if(NULL != controller.mImpl->mEventData)
187 // if mPlaceholderFont is null, create an instance.
188 CreatePlaceholderFont(controller);
190 controller.mImpl->mEventData->mPlaceholderFont->mFontDescription.weight = weight;
191 controller.mImpl->mEventData->mPlaceholderFont->weightDefined = true;
193 controller.mImpl->RequestRelayout();
197 bool Controller::PlaceholderHandler::IsPlaceholderTextFontWeightDefined(const Controller& controller)
199 if((NULL != controller.mImpl->mEventData) && (NULL != controller.mImpl->mEventData->mPlaceholderFont))
201 return controller.mImpl->mEventData->mPlaceholderFont->weightDefined;
206 FontWeight Controller::PlaceholderHandler::GetPlaceholderTextFontWeight(const Controller& controller)
208 if((NULL != controller.mImpl->mEventData) && (NULL != controller.mImpl->mEventData->mPlaceholderFont))
210 return controller.mImpl->mEventData->mPlaceholderFont->mFontDescription.weight;
213 return TextAbstraction::FontWeight::NORMAL;
216 void Controller::PlaceholderHandler::SetPlaceholderTextFontWidth(Controller& controller, FontWidth width)
218 if(NULL != controller.mImpl->mEventData)
220 // if mPlaceholderFont is null, create an instance.
221 CreatePlaceholderFont(controller);
223 controller.mImpl->mEventData->mPlaceholderFont->mFontDescription.width = width;
224 controller.mImpl->mEventData->mPlaceholderFont->widthDefined = true;
226 controller.mImpl->RequestRelayout();
230 bool Controller::PlaceholderHandler::IsPlaceholderTextFontWidthDefined(const Controller& controller)
232 if((NULL != controller.mImpl->mEventData) && (NULL != controller.mImpl->mEventData->mPlaceholderFont))
234 return controller.mImpl->mEventData->mPlaceholderFont->widthDefined;
239 FontWidth Controller::PlaceholderHandler::GetPlaceholderTextFontWidth(const Controller& controller)
241 if((NULL != controller.mImpl->mEventData) && (NULL != controller.mImpl->mEventData->mPlaceholderFont))
243 return controller.mImpl->mEventData->mPlaceholderFont->mFontDescription.width;
246 return TextAbstraction::FontWidth::NORMAL;
249 void Controller::PlaceholderHandler::SetPlaceholderTextFontSlant(Controller& controller, FontSlant slant)
251 if(NULL != controller.mImpl->mEventData)
253 // if mPlaceholderFont is null, create an instance.
254 CreatePlaceholderFont(controller);
256 controller.mImpl->mEventData->mPlaceholderFont->mFontDescription.slant = slant;
257 controller.mImpl->mEventData->mPlaceholderFont->slantDefined = true;
259 controller.mImpl->RequestRelayout();
263 bool Controller::PlaceholderHandler::IsPlaceholderTextFontSlantDefined(const Controller& controller)
265 if((NULL != controller.mImpl->mEventData) && (NULL != controller.mImpl->mEventData->mPlaceholderFont))
267 return controller.mImpl->mEventData->mPlaceholderFont->slantDefined;
272 FontSlant Controller::PlaceholderHandler::GetPlaceholderTextFontSlant(const Controller& controller)
274 if((NULL != controller.mImpl->mEventData) && (NULL != controller.mImpl->mEventData->mPlaceholderFont))
276 return controller.mImpl->mEventData->mPlaceholderFont->mFontDescription.slant;
279 return TextAbstraction::FontSlant::NORMAL;
282 void Controller::PlaceholderHandler::SetPlaceholderTextFontSize(Controller& controller, float fontSize, FontSizeType type)
284 if(NULL != controller.mImpl->mEventData)
286 // if mPlaceholderFont is null, create an instance.
287 CreatePlaceholderFont(controller);
293 controller.mImpl->mEventData->mPlaceholderFont->mDefaultPointSize = fontSize;
294 controller.mImpl->mEventData->mPlaceholderFont->sizeDefined = true;
295 controller.mImpl->mEventData->mIsPlaceholderPixelSize = false; // Font size flag
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);
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
313 controller.mImpl->RequestRelayout();
317 float Controller::PlaceholderHandler::GetPlaceholderTextFontSize(const Controller& controller, FontSizeType type)
320 if(NULL != controller.mImpl->mEventData)
326 if(NULL != controller.mImpl->mEventData->mPlaceholderFont)
328 value = controller.mImpl->mEventData->mPlaceholderFont->mDefaultPointSize;
332 // If the placeholder text font size is not set, then return the default font size.
333 value = controller.GetDefaultFontSize(POINT_SIZE);
339 if(NULL != controller.mImpl->mEventData->mPlaceholderFont)
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);
347 value = controller.mImpl->mEventData->mPlaceholderFont->mDefaultPointSize * static_cast<float>(horizontalDpi) / 72.f;
351 // If the placeholder text font size is not set, then return the default font size.
352 value = controller.GetDefaultFontSize(PIXEL_SIZE);
363 void Controller::PlaceholderHandler::SetPlaceholderTextColor(Controller& controller, const Vector4& textColor)
365 if(NULL != controller.mImpl->mEventData)
367 controller.mImpl->mEventData->mPlaceholderTextColor = textColor;
370 if(controller.mImpl->IsShowingPlaceholderText())
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();
379 const Vector4& Controller::PlaceholderHandler::GetPlaceholderTextColor(const Controller& controller)
381 if(NULL != controller.mImpl->mEventData)
383 return controller.mImpl->mEventData->mPlaceholderTextColor;
389 void Controller::PlaceholderHandler::SetPlaceholderProperty(Controller& controller, const Property::Map& map)
391 const Property::Map::SizeType count = map.Count();
393 for(Property::Map::SizeType position = 0; position < count; ++position)
395 const KeyValuePair& keyValue = map.GetKeyValue(position);
396 const Property::Key& key = keyValue.first;
397 const Property::Value& value = keyValue.second;
399 Property::Index indexKey = GetIntKey(key);
403 case Toolkit::Text::PlaceHolder::Property::TEXT:
404 case Toolkit::Text::PlaceHolder::Property::TEXT_FOCUSED:
406 PlaceholderType placeHolderType = (indexKey == PlaceHolder::Property::TEXT) ? Controller::PLACEHOLDER_TYPE_INACTIVE : Controller::PLACEHOLDER_TYPE_ACTIVE;
408 std::string text = "";
411 SetPlaceholderText(controller, placeHolderType, text);
415 case Toolkit::Text::PlaceHolder::Property::COLOR:
418 if(value.Get(textColor))
420 if(GetPlaceholderTextColor(controller) != textColor)
422 SetPlaceholderTextColor(controller, textColor);
427 case Toolkit::Text::PlaceHolder::Property::FONT_FAMILY:
429 std::string fontFamily = "";
430 if(value.Get(fontFamily))
432 SetPlaceholderFontFamily(controller, fontFamily);
436 case Toolkit::Text::PlaceHolder::Property::FONT_STYLE:
438 SetFontStyleProperty(&controller, value, Text::FontStyle::PLACEHOLDER);
441 case Toolkit::Text::PlaceHolder::Property::POINT_SIZE:
442 case Toolkit::Text::PlaceHolder::Property::PIXEL_SIZE:
444 FontSizeType fontSizeType = (indexKey == PlaceHolder::Property::POINT_SIZE) ? Text::Controller::POINT_SIZE : Text::Controller::PIXEL_SIZE;
446 float fontSizeValue = 0.0f;
447 if(value.Get(fontSizeValue))
449 if(!Equals(GetPlaceholderTextFontSize(controller, fontSizeType), fontSizeValue))
451 SetPlaceholderTextFontSize(controller, fontSizeValue, fontSizeType);
456 case Toolkit::Text::PlaceHolder::Property::ELLIPSIS:
458 bool ellipsis = false;
459 if(value.Get(ellipsis))
461 SetPlaceholderTextElideEnabled(controller, ellipsis);
469 void Controller::PlaceholderHandler::GetPlaceholderProperty(Controller& controller, Property::Map& map)
471 if(NULL != controller.mImpl->mEventData)
473 if(!controller.mImpl->mEventData->mPlaceholderTextActive.empty())
475 map[Text::PlaceHolder::Property::TEXT_FOCUSED] = controller.mImpl->mEventData->mPlaceholderTextActive;
477 if(!controller.mImpl->mEventData->mPlaceholderTextInactive.empty())
479 map[Text::PlaceHolder::Property::TEXT] = controller.mImpl->mEventData->mPlaceholderTextInactive;
482 map[Text::PlaceHolder::Property::COLOR] = controller.mImpl->mEventData->mPlaceholderTextColor;
483 map[Text::PlaceHolder::Property::FONT_FAMILY] = GetPlaceholderFontFamily(controller);
485 Property::Value fontStyleMapGet;
486 GetFontStyleProperty(&controller, fontStyleMapGet, Text::FontStyle::PLACEHOLDER);
487 map[Text::PlaceHolder::Property::FONT_STYLE] = fontStyleMapGet;
489 // Choose font size : POINT_SIZE or PIXEL_SIZE
490 if(!controller.mImpl->mEventData->mIsPlaceholderPixelSize)
492 map[Text::PlaceHolder::Property::POINT_SIZE] = GetPlaceholderTextFontSize(controller, Text::Controller::POINT_SIZE);
496 map[Text::PlaceHolder::Property::PIXEL_SIZE] = GetPlaceholderTextFontSize(controller, Text::Controller::PIXEL_SIZE);
499 if(controller.mImpl->mEventData->mPlaceholderEllipsisFlag)
501 map[Text::PlaceHolder::Property::ELLIPSIS] = IsPlaceholderTextElideEnabled(controller);
506 void Controller::PlaceholderHandler::ShowPlaceholderText(Controller::Impl& impl)
508 if(impl.IsPlaceholderAvailable())
510 EventData*& eventData = impl.mEventData;
511 DALI_ASSERT_DEBUG(eventData && "No placeholder text available");
513 if(NULL == eventData)
518 eventData->mIsShowingPlaceholderText = true;
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);
526 const char* text(NULL);
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()))
534 text = placeholderTextActive.c_str();
535 size = placeholderTextActive.size();
539 std::string& placeholderTextInactive = eventData->mPlaceholderTextInactive;
540 text = placeholderTextInactive.c_str();
541 size = placeholderTextInactive.size();
544 TextUpdateInfo& textUpdateInfo = impl.mTextUpdateInfo;
545 textUpdateInfo.mCharacterIndex = 0u;
546 textUpdateInfo.mNumberOfCharactersToRemove = textUpdateInfo.mPreviousNumberOfCharacters;
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);
554 // Convert text into UTF-32
555 Vector<Character>& utf32Characters = logicalModel->mText;
556 utf32Characters.Resize(size);
558 // This is a bit horrible but std::string returns a (signed) char*
559 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>(text);
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);
566 // The characters to be added.
567 textUpdateInfo.mNumberOfCharactersToAdd = characterCount;
569 // Reset the cursor position
570 eventData->mPrimaryCursorPosition = 0;
572 // The natural size needs to be re-calculated.
573 impl.mRecalculateNaturalSize = true;
575 // The text direction needs to be updated.
576 impl.mUpdateTextDirection = true;
578 // Apply modifications to the model
579 impl.mOperationsPending = ALL_OPERATIONS;
581 // Update the rest of the model during size negotiation
582 impl.QueueModifyEvent(ModifyEvent::TEXT_REPLACED);
586 void Controller::PlaceholderHandler::CreatePlaceholderFont(Controller& controller)
588 if(nullptr == controller.mImpl->mEventData->mPlaceholderFont)
590 controller.mImpl->mEventData->mPlaceholderFont = std::unique_ptr<FontDefaults>(new FontDefaults());
596 } // namespace Toolkit