Supports Thai word line breaks using ICU dictionary.
Environment variables have been added. The default is 0.
export DALI_TEXT_ENABLE_ICU=1
It is currently only used for Thai language, but will be used in many cases in the future.
Change-Id: I93ced35d8d8e9c3f69a076920165dd4670c3e3db
Signed-off-by: Bowon Ryu <bowon.ryu@samsung.com>
utc-Dali-Text-CharacterSpacingSpan.cpp
utc-Dali-Text-FontSpan.cpp
utc-Dali-Text-BoldSpan.cpp
+ utc-Dali-Text-ICU.cpp
utc-Dali-Text-ItalicSpan.cpp
utc-Dali-Text-BackgroundColorSpan.cpp
utc-Dali-Text-Range.cpp
dali-toolkit-test-utils/toolkit-direct-rendering-egl.cpp
dali-toolkit-test-utils/toolkit-event-thread-callback.cpp
dali-toolkit-test-utils/toolkit-environment-variable.cpp
+ dali-toolkit-test-utils/toolkit-icu.cpp
dali-toolkit-test-utils/toolkit-input-method-context.cpp
dali-toolkit-test-utils/toolkit-input-method-options.cpp
dali-toolkit-test-utils/toolkit-lifecycle-controller.cpp
--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali/devel-api/text-abstraction/icu.h>
+#include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
+#include <dali/public-api/dali-adaptor-common.h>
+#include <dali/public-api/object/base-handle.h>
+#include <dali/public-api/object/base-object.h>
+#include <toolkit-application.h>
+#include <toolkit-icu.h>
+#include <dali-toolkit-test-suite-utils.h>
+
+namespace Dali
+{
+namespace TextAbstraction
+{
+namespace Internal
+{
+namespace
+{
+Dali::Vector<Dali::TextAbstraction::Internal::ICU*> gICUs;
+uint32_t gInitializedCount = 0u;
+}
+
+class ICU: public Dali::BaseObject
+{
+public:
+
+ICU()
+: mInitialized(false)
+{
+}
+
+~ICU()
+{
+}
+
+void Initialize()
+{
+ // Try once.
+ mInitialized = true;
+ tet_printf("ICU::Initialize:%d\n", mInitialized);
+}
+
+void UpdateLineBreakInfoByLocale(const std::string& text,
+ Length numberOfCharacters,
+ const char* locale,
+ LineBreakInfo* breakInfo)
+{
+ tet_printf("ICU::UpdateLineBreakInfoByLocale\n");
+ if(!mInitialized)
+ {
+ Initialize();
+ }
+}
+
+// Test method
+void ResetInitialize()
+{
+ mInitialized = false;
+}
+
+// Test method
+bool IsInitialized()
+{
+ return mInitialized;
+}
+
+private:
+ bool mInitialized : 1; ///< Whether Initialize() has been called, prevents dlopen from being attempted again.
+};
+
+inline ICU& GetImplementation(TextAbstraction::ICU& icu)
+{
+ DALI_ASSERT_ALWAYS(icu && "icu handle is empty");
+ BaseObject& handle = icu.GetBaseObject();
+ return static_cast<Internal::ICU&>(handle);
+}
+
+inline const ICU& GetImplementation(const TextAbstraction::ICU& icu)
+{
+ DALI_ASSERT_ALWAYS(icu && "icu handle is empty");
+ const BaseObject& handle = icu.GetBaseObject();
+ return static_cast<const Internal::ICU&>(handle);
+}
+
+} // namespace Internal
+
+/********************************************************************************/
+/********************************* PUBLIC CLASS *******************************/
+/********************************************************************************/
+
+ICU::ICU()
+{
+}
+
+ICU::~ICU()
+{
+}
+
+ICU::ICU(Internal::ICU* impl)
+: BaseHandle(impl)
+{
+}
+
+ICU ICU::New()
+{
+ auto icuImpl = new Internal::ICU();
+ Dali::TextAbstraction::Internal::gICUs.PushBack(icuImpl);
+ return ICU(icuImpl);
+}
+
+void ICU::UpdateLineBreakInfoByLocale(const std::string& text,
+ Length numberOfCharacters,
+ const char* locale,
+ LineBreakInfo* breakInfo)
+{
+ Internal::GetImplementation(*this).UpdateLineBreakInfoByLocale(text, numberOfCharacters, locale, breakInfo);
+}
+
+} // namespace TextAbstraction
+} // namespace Dali;
+
+namespace Test
+{
+namespace ICU
+{
+void ResetInitializeFlag()
+{
+ for(auto& icu : Dali::TextAbstraction::Internal::gICUs)
+ {
+ icu->ResetInitialize();
+ }
+ Dali::TextAbstraction::Internal::gInitializedCount = 0u;
+}
+
+uint32_t GetInitializedCount()
+{
+ Dali::TextAbstraction::Internal::gInitializedCount = 0u;
+ for(auto& icu : Dali::TextAbstraction::Internal::gICUs)
+ {
+ if(icu->IsInitialized())
+ {
+ Dali::TextAbstraction::Internal::gInitializedCount++;
+ }
+ }
+ return Dali::TextAbstraction::Internal::gInitializedCount;
+}
+
+} // namespace ICU
+} // namespace Test
--- /dev/null
+#ifndef DALI_TOOLKIT_TEST_ICU_H
+#define DALI_TOOLKIT_TEST_ICU_H
+
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <cstdint>
+
+namespace Test
+{
+namespace ICU
+{
+void ResetInitializeFlag();
+uint32_t GetInitializedCount();
+
+} // namespace ICU
+} // namespace Test
+
+#endif // DALI_TOOLKIT_TEST_ICU_H
--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <toolkit-environment-variable.h>
+#include <toolkit-event-thread-callback.h>
+#include <toolkit-icu.h>
+
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/dali-toolkit.h>
+#include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
+#include <dali-toolkit/internal/text/async-text/async-text-manager.h>
+#include <dali-toolkit/internal/text/multi-language-support.h>
+#include <locale.h>
+#include <iostream>
+
+using namespace Dali;
+using namespace Toolkit;
+
+namespace
+{
+static int ASYNC_TEXT_THREAD_TIMEOUT = 5;
+static bool gAsyncTextRenderedCalled;
+static float gAsyncTextRenderedWidth;
+static float gAsyncTextRenderedHeight;
+
+struct CallbackFunctor
+{
+ CallbackFunctor(bool* callbackFlag)
+ : mCallbackFlag(callbackFlag)
+ {
+ }
+
+ void operator()()
+ {
+ *mCallbackFlag = true;
+ }
+ bool* mCallbackFlag;
+};
+
+static void TestAsyncTextRendered(TextLabel control, float width, float height)
+{
+ tet_infoline(" TestAsyncTextRendered");
+ gAsyncTextRenderedCalled = true;
+ gAsyncTextRenderedWidth = width;
+ gAsyncTextRenderedHeight = height;
+}
+
+} // namespace
+
+void dali_text_icu_startup(void)
+{
+ EnvironmentVariable::SetTestEnvironmentVariable("DALI_TEXT_ENABLE_ICU", "1");
+
+ test_return_value = TET_UNDEF;
+}
+
+void dali_text_icu_cleanup(void)
+{
+ EnvironmentVariable::SetTestEnvironmentVariable("DALI_TEXT_ENABLE_ICU", "0");
+
+ test_return_value = TET_PASS;
+}
+
+int UtcDaliToolkitTextICU(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliToolkitTextICU");
+
+ // Avoid a crash when core load gl resources.
+ application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+ // Create MultilanguageSupport & AsyncTextManager to get LocaleChangedSignal.
+ Text::MultilanguageSupport::Get();
+ Text::AsyncTextManager::Get();
+
+ Adaptor &adaptor = application.GetAdaptor();
+ std::string locale = "en_US";
+ adaptor.LocaleChangedSignal().Emit(locale);
+
+ TextLabel label = TextLabel::New();
+ DALI_TEST_CHECK(label);
+
+ float expectedWidth = 300.0f;
+ float expectedHeight = 300.0f;
+
+ // Sync test.
+ label.SetProperty(TextLabel::Property::TEXT, "ฉันเป็นเอาต์บอกความร้อนของสิ่งที่ถูกหล่อหลอม ฉันไม่สามารถพูดภาษาไทยได้จริงๆ แต่ฉันสามารถช่วยเหลือคุณในการสร้างคำบรรยายสำหรับภาษาไทยได้ แต่ฉันจะต้องพึ่งความช่วยเหลือของ");
+ label.SetProperty(Actor::Property::SIZE, Vector2(expectedWidth, expectedHeight));
+ label.SetProperty(TextLabel::Property::POINT_SIZE, 12);
+ label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+ application.GetScene().Add(label);
+
+ label.SetProperty(TextLabel::Property::LINE_WRAP_MODE, "WORD");
+ DALI_TEST_EQUALS(static_cast<int>(Text::LineWrap::WORD), label.GetProperty<int>(TextLabel::Property::LINE_WRAP_MODE), TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(0u, Test::ICU::GetInitializedCount(), TEST_LOCATION);
+
+ // Locale changed
+ std::string newLocale = "th_TH";
+ adaptor.LocaleChangedSignal().Emit(newLocale);
+
+ label.SetProperty(TextLabel::Property::TEXT, "ฉันเป็นเอาต์บอกความร้อนของสิ่งที่ถูกหล่อหลอม ฉันไม่สามารถพูดภาษาไทยได้จริงๆ แต่ฉันสามารถช่วยเหลือคุณในการสร้างคำบรรยายสำหรับภาษาไทยได้ แต่ฉันจะต้องพึ่งความช่วยเหลือของ");
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(1u, Test::ICU::GetInitializedCount(), TEST_LOCATION);
+
+ // Async test.
+ label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_AUTO);
+
+ // Connect to the async text rendered signal.
+ ConnectionTracker* testTracker = new ConnectionTracker();
+ DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+ bool asyncTextRendered = false;
+ label.ConnectSignal(testTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+
+ gAsyncTextRenderedCalled = false;
+ gAsyncTextRenderedWidth = 0.0f;
+ gAsyncTextRenderedHeight = 0.0f;
+
+ // Request render automatically.
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+ DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+ DALI_TEST_CHECK(asyncTextRendered);
+
+ DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+ DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+ DALI_TEST_EQUALS(false, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+ DALI_TEST_EQUALS(2u, Test::ICU::GetInitializedCount(), TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render();
+
+ Test::ICU::ResetInitializeFlag();
+ DALI_TEST_EQUALS(0u, Test::ICU::GetInitializedCount(), TEST_LOCATION);
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
/*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
: mModule(),
mTextModel(),
mMetrics(),
+ mLocale(),
mNumberOfCharacters(0u),
mFitActualEllipsis(true),
mIsTextDirectionRTL(false),
mIsTextMirrored(false),
mModuleClearNeeded(false),
+ mLocaleUpdateNeeded(false),
mMutex()
{
mModule = Dali::Toolkit::Text::AsyncTextModule::New();
// Use this to access FontClient i.e. to get down-scaled Emoji metrics.
mMetrics = Metrics::New(mModule.GetFontClient());
mLayoutEngine.SetMetrics(mMetrics);
+
+ mLocale = TextAbstraction::GetLocaleFull();
}
AsyncTextLoader::~AsyncTextLoader()
{
}
+void AsyncTextLoader::SetLocale(const std::string& locale)
+{
+ mLocale = locale;
+ mModule.GetMultilanguageSupport().SetLocale(mLocale);
+}
+
+void AsyncTextLoader::SetLocaleUpdateNeeded(bool update)
+{
+ Dali::Mutex::ScopedLock lock(mMutex);
+ mLocaleUpdateNeeded = update;
+}
+
+bool AsyncTextLoader::IsLocaleUpdateNeeded()
+{
+ return mLocaleUpdateNeeded;
+}
+
void AsyncTextLoader::ClearModule()
{
mModule.ClearCache();
lineBreakInfo.Resize(numberOfCharacters, TextAbstraction::LINE_NO_BREAK);
SetLineBreakInfo(mModule.GetSegmentation(), utf32Characters, 0u, numberOfCharacters, lineBreakInfo);
+ // Check if an ICU-based line break update is required.
+ if(mModule.GetMultilanguageSupport().IsICULineBreakNeeded())
+ {
+ std::string currentText;
+ Utf32ToUtf8(mTextModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText);
+ mModule.GetMultilanguageSupport().UpdateICULineBreak(currentText, numberOfCharacters, lineBreakInfo.Begin());
+ }
+
// Hyphenation
if(parameters.lineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
parameters.lineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
#define DALI_TOOLKIT_TEXT_ASYNC_TEXT_LOADER_IMPL_H
/*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
~AsyncTextLoader();
+ /**
+ * @copydoc Dali::AsyncTextLoader::SetLocale()
+ */
+ void SetLocale(const std::string& locale);
+
+ /**
+ * @copydoc Dali::AsyncTextLoader::SetLocaleUpdateNeeded()
+ */
+ void SetLocaleUpdateNeeded(bool update);
+
+ /**
+ * @copydoc Dali::AsyncTextLoader::IsLocaleUpdateNeeded()
+ */
+ bool IsLocaleUpdateNeeded();
+
/**
* @copydoc Dali::AsyncTextLoader::ClearModule()
*/
MetricsPtr mMetrics;
Text::Layout::Engine mLayoutEngine;
Text::TypesetterPtr mTypesetter;
+ std::string mLocale;
Length mNumberOfCharacters;
- bool mFitActualEllipsis : 1; // Used to store actual ellipses during TextFit calculations. Do not use it in other sections.
- bool mIsTextDirectionRTL : 1; // The direction of the first line after layout completion.
- bool mIsTextMirrored : 1;
- bool mModuleClearNeeded : 1;
+ bool mFitActualEllipsis : 1; // Used to store actual ellipses during TextFit calculations. Do not use it in other sections.
+ bool mIsTextDirectionRTL : 1; // The direction of the first line after layout completion.
+ bool mIsTextMirrored : 1;
+ bool mModuleClearNeeded : 1;
+ bool mLocaleUpdateNeeded : 1;
Mutex mMutex;
}; // class AsyncTextLoader
/*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
}
+void AsyncTextLoader::SetLocale(const std::string& locale)
+{
+ GetImplementation(*this).SetLocale(locale);
+}
+
+void AsyncTextLoader::SetLocaleUpdateNeeded(bool update)
+{
+ GetImplementation(*this).SetLocaleUpdateNeeded(update);
+}
+
+bool AsyncTextLoader::IsLocaleUpdateNeeded()
+{
+ return GetImplementation(*this).IsLocaleUpdateNeeded();
+}
+
void AsyncTextLoader::ClearModule()
{
GetImplementation(*this).ClearModule();
#define DALI_TOOLKIT_TEXT_ASYNC_TEXT_LOADER_H
/*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
static AsyncTextLoader New();
+ /**
+ * @brief Sets the locale.
+ *
+ * @param[in] locale The locale.
+ */
+ void SetLocale(const std::string& locale);
+
+ /**
+ * @brief Sets a flag indicating that module's locale updating is needed.
+ *
+ * When the async text loader is available, update is processed on the main thread.
+ *
+ * @param[in] update Whether to update the locale or not.
+ */
+ void SetLocaleUpdateNeeded(bool update);
+
+ /**
+ * @brief Whether module's locale updating is needed.
+ *
+ * @return A flag that indicates whether the locale should be updated or not.
+ */
+ bool IsLocaleUpdateNeeded();
+
/**
* @brief Clear the cache of the async text module.
*/
/*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
for(auto& loader : mAvailableLoaders)
{
loader.ClearModule();
+ loader.SetLocale(mLocale);
}
// When the Loader is in running state, just set the flag and clear it when it becomes available.
for(auto& loader : mRunningLoaders)
{
loader.SetModuleClearNeeded(true);
+ loader.SetLocaleUpdateNeeded(true);
}
}
}
loader.ClearModule();
loader.SetModuleClearNeeded(false);
}
+ if(loader.IsLocaleUpdateNeeded())
+ {
+ loader.SetLocale(mLocale);
+ loader.SetLocaleUpdateNeeded(false);
+ }
mAvailableLoaders.pop_back();
mRunningLoaders.push_back(loader);
/*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
TextAbstraction::Segmentation segmentation = TextAbstraction::Segmentation::Get();
SetLineBreakInfo(segmentation, utf32Characters, startIndex, requestedNumberOfCharacters, lineBreakInfo);
+ MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
+
+ // Check if an ICU-based line break update is required.
+ if(multilanguageSupport.IsICULineBreakNeeded())
+ {
+ std::string currentText;
+ Utf32ToUtf8(impl.mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText);
+ multilanguageSupport.UpdateICULineBreak(currentText, numberOfCharacters, lineBreakInfo.Begin());
+ }
+
if(impl.mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
impl.mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
{
/*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <dali-toolkit/internal/text/multi-language-support-impl.h>
// EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/environment-variable.h>
#include <dali/devel-api/common/singleton-service.h>
#include <dali/integration-api/adaptor-framework/adaptor.h>
#include <dali/integration-api/debug.h>
const Dali::Toolkit::Text::Character UTF32_A = 0x0041;
const Dali::Toolkit::Text::Character UTF32_COLON = 0x3A;
+
+const char* DALI_TEXT_ENABLE_ICU("DALI_TEXT_ENABLE_ICU");
+const int DEFAULT_ENABLE_ICU = 0;
+const char* THAILAND_LOCALE_CODE = "th_TH";
+
} // namespace
namespace Text
}
MultilanguageSupport::MultilanguageSupport(bool connectLocaleChangedSignal)
-: mDefaultFontPerScriptCache(),
+: mICU(),
+ mDefaultFontPerScriptCache(),
mValidFontsPerScriptCache(),
- mLocale()
+ mLocale(),
+ mIsICUEnabled(false),
+ mIsICULineBreakNeededForLocale(false)
{
// Initializes the default font cache to zero (invalid font).
// Reserves space to cache the default fonts and access them with the script as an index.
mLocale = TextAbstraction::GetLocaleFull();
+ // Check environment variable for DALI_TEXT_ENABLE_ICU.
+ auto enableICUstring = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_TEXT_ENABLE_ICU);
+ int enableICU = enableICUstring ? std::atoi(enableICUstring) : DEFAULT_ENABLE_ICU;
+ if(enableICU == 1)
+ {
+ mIsICUEnabled = true;
+ mIsICULineBreakNeededForLocale = IsICULineBreakNeededForLocale();
+ mICU = TextAbstraction::ICU::New();
+ }
+
if(connectLocaleChangedSignal && Dali::Adaptor::IsAvailable())
{
Dali::Adaptor::Get().LocaleChangedSignal().Connect(this, &MultilanguageSupport::OnLocaleChanged);
{
if(mLocale != locale)
{
- mLocale = locale;
+ SetLocale(locale);
ClearCache();
}
}
mValidFontsPerScriptCache.Resize(TextAbstraction::GetNumberOfScripts(), NULL);
}
-std::string MultilanguageSupport::GetLocale()
+const std::string& MultilanguageSupport::GetLocale()
{
return mLocale;
}
+void MultilanguageSupport::SetLocale(const std::string& locale)
+{
+ mLocale = locale;
+ mIsICULineBreakNeededForLocale = IsICULineBreakNeededForLocale();
+}
+
+bool MultilanguageSupport::IsICULineBreakNeeded()
+{
+ return mIsICUEnabled && mIsICULineBreakNeededForLocale;
+}
+
+void MultilanguageSupport::UpdateICULineBreak(const std::string& text,
+ TextAbstraction::Length numberOfCharacters,
+ TextAbstraction::LineBreakInfo* breakInfo)
+{
+ if(mIsICUEnabled && mICU)
+ {
+ mICU.UpdateLineBreakInfoByLocale(text, numberOfCharacters, mLocale.c_str(), breakInfo);
+ }
+}
+
Text::MultilanguageSupport MultilanguageSupport::Get()
{
Text::MultilanguageSupport multilanguageSupportHandle;
currentScriptRun.isRightToLeft = isRightToLeft;
}
+bool MultilanguageSupport::IsICULineBreakNeededForLocale()
+{
+ return mLocale == THAILAND_LOCALE_CODE;
+}
+
} // namespace Internal
} // namespace Text
#define DALI_TOOLKIT_TEXT_MULTI_LANGUAGE_SUPPORT_IMPL_H
/*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
// EXTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/icu.h>
#include <dali/public-api/object/base-object.h>
#include <dali/public-api/signals/connection-tracker.h>
*/
void OnLocaleChanged(std::string locale);
+ /**
+ * @copydoc Dali::MultilanguageSupport::GetLocale()
+ */
+ const std::string& GetLocale();
+
+ /**
+ * @copydoc Dali::MultilanguageSupport::SetLocale()
+ */
+ void SetLocale(const std::string& locale);
+
/**
* @copydoc Dali::MultilanguageSupport::ClearCache()
*/
void ClearCache();
/**
- * @brief Gets the locale.
+ * @copydoc Dali::MultilanguageSupport::IsICULineBreakNeeded()
*/
- std::string GetLocale();
+ bool IsICULineBreakNeeded();
+
+ /**
+ * @copydoc Dali::MultilanguageSupport::UpdateICULineBreak()
+ */
+ void UpdateICULineBreak(const std::string& text,
+ TextAbstraction::Length numberOfCharacters,
+ TextAbstraction::LineBreakInfo* breakInfo);
private:
+ TextAbstraction::ICU mICU; ///< Handle to the dali ICU.
Vector<DefaultFonts*> mDefaultFontPerScriptCache; ///< Caches default fonts for a script.
Vector<ValidateFontsPerScript*> mValidFontsPerScriptCache; ///< Caches valid fonts for a script.
std::string mLocale;
+ bool mIsICUEnabled : 1;
+ bool mIsICULineBreakNeededForLocale : 1;
//Methods
/**
- * @brief Add the current script to scripts and create new script.
- *
- * @param[in] requestedScript The script of the new script run.
- * @param[in] isRightToLeft The direction of the new script run.
- * @param[in] addScriptCharactersToNewScript Whether to add the pending characters to the new script run or to the current script run.
- * @param[inout] currentScriptRun The current character script run and it will be updated it to the new script run.
- * @param[inout] numberOfAllScriptCharacters The pending characters.
- * @param[inout] scripts The list of scripts.
- * @param[inout] scriptIndex The current index of scripts.
- *
- */
+ * @brief Add the current script to scripts and create new script.
+ *
+ * @param[in] requestedScript The script of the new script run.
+ * @param[in] isRightToLeft The direction of the new script run.
+ * @param[in] addScriptCharactersToNewScript Whether to add the pending characters to the new script run or to the current script run.
+ * @param[inout] currentScriptRun The current character script run and it will be updated it to the new script run.
+ * @param[inout] numberOfAllScriptCharacters The pending characters.
+ * @param[inout] scripts The list of scripts.
+ * @param[inout] scriptIndex The current index of scripts.
+ *
+ */
void AddCurrentScriptAndCreatNewScript(const Script requestedScript,
const bool isRightToLeft,
const bool addScriptCharactersToNewScript,
Length& numberOfAllScriptCharacters,
Vector<ScriptRun>& scripts,
ScriptRunIndex& scriptIndex);
+
+ /**
+ * @brief Checks if an ICU-based line break update is required for the current locale.
+ * @note Use only when there is a change in locale to avoid useless calculations.
+ * @return If true, icu-based line breaks are required or possible.
+ */
+ bool IsICULineBreakNeededForLocale();
+
};
} // namespace Internal
/*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
fonts);
}
+const std::string& MultilanguageSupport::GetLocale()
+{
+ return GetImplementation(*this).GetLocale();
+}
+
+void MultilanguageSupport::SetLocale(const std::string& locale)
+{
+ GetImplementation(*this).SetLocale(locale);
+}
+
void MultilanguageSupport::ClearCache()
{
GetImplementation(*this).ClearCache();
}
+bool MultilanguageSupport::IsICULineBreakNeeded()
+{
+ return GetImplementation(*this).IsICULineBreakNeeded();
+}
+
+void MultilanguageSupport::UpdateICULineBreak(const std::string& text,
+ TextAbstraction::Length numberOfCharacters,
+ TextAbstraction::LineBreakInfo* breakInfo)
+{
+ GetImplementation(*this).UpdateICULineBreak(text, numberOfCharacters, breakInfo);
+}
+
} // namespace Text
} // namespace Toolkit
#define DALI_TOOLKIT_TEXT_MULTI_LANGUAGE_SUPPORT_H
/*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Length numberOfCharacters,
Vector<FontRun>& fonts);
+ /**
+ * @brief Gets the locale.
+ */
+ const std::string& GetLocale();
+
+ /**
+ * @brief Sets the locale.
+ * @param[in] locale The locale.
+ */
+ void SetLocale(const std::string& locale);
+
/**
* @brief Clear font caches when locale changed.
*/
void ClearCache();
+ /**
+ * @brief Checks if an ICU-based line break update is required for the current locale.
+ * @return If true, icu-based line breaks are required or possible.
+ */
+ bool IsICULineBreakNeeded();
+
+ /**
+ * @brief Update line break information by ICU.
+ * @remark Updates given line break information with ICU dictionary-based word wrap information that unibreak does not support.
+ * @param[in] text A string of UTF-8 characters.
+ * @param[in] numberOfCharacters The number of characters.
+ * @param[out] breakInfo The unibreak line break information buffer.
+ */
+ void UpdateICULineBreak(const std::string& text,
+ TextAbstraction::Length numberOfCharacters,
+ TextAbstraction::LineBreakInfo* breakInfo);
+
public:
// Default copy and move operator
MultilanguageSupport(const MultilanguageSupport& rhs) = default;