From: joogab yun Date: Mon, 22 Nov 2021 06:40:15 +0000 (+0000) Subject: Merge "Add a callback to get textfitted font size." into devel/master X-Git-Tag: dali_2.1.0~8 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=c039ebb7a115f5516aa792c1aa3bd6370e61acfd;hp=c2a64c99bfb51070915cdb15559bd3be5d2f9998 Merge "Add a callback to get textfitted font size." into devel/master --- diff --git a/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.cpp b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.cpp index 64a8473..701977d 100755 --- a/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.cpp +++ b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.cpp @@ -460,6 +460,47 @@ void ConfigureTextEditor( ControllerPtr controller ) controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS); } + +Vector CreateSingleFontDescription( + const CharacterRun& characterRun, + const std::string fontFamilyName, + const FontWeight weight, + const FontWidth width, + const FontSlant slant, + const PointSize26Dot6 size, + const bool familyDefined, + const bool weightDefined, + const bool widthDefined, + const bool slantDefined, + const bool sizeDefined) +{ + + FontDescriptionRun fontDescriptionRun = + { + characterRun, + nullptr, + 0u, + weight, + width, + slant, + size, + familyDefined, + weightDefined, + widthDefined, + slantDefined, + sizeDefined + }; + + fontDescriptionRun.familyLength = fontFamilyName.size(); + fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength]; + memcpy( fontDescriptionRun.familyName, fontFamilyName.c_str(), fontDescriptionRun.familyLength ); + + Vector fontDescriptionRuns; + fontDescriptionRuns.PushBack(fontDescriptionRun); + + return fontDescriptionRuns; +} + } // namespace Text } // namespace Toolkit diff --git a/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.h b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.h index ccfbe98..0b9f592 100644 --- a/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.h +++ b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.h @@ -93,6 +93,37 @@ void ConfigureTextField( ControllerPtr controller ); */ void ConfigureTextEditor( ControllerPtr controller ); + +/** + * @brief Creates one FontDescriptionRun then add it to FontDescription list. + * + * @param[in] characterRun The initial character index and the number of characters of the run. + * @param[in] fontFamilyName The font's family name. + * @param[in] weight The font's weight. + * @param[in] width The font's width. + * @param[in] slant The font's slant. + * @param[in] size Whether the font's family is defined. + * @param[in] familyDefined Whether the font's weight is defined. + * @param[in] weightDefined Whether the font's width is defined. + * @param[in] widthDefined Whether the ellipsis layout option is enabled. + * @param[in] slantDefined Whether the font's slant is defined. + * @param[in] sizeDefined Whether the font's size is defined. + +* @return vector contains one FontDescriptionRun. + */ +Vector CreateSingleFontDescription( + const CharacterRun& characterRun, + const std::string fontFamilyName, + const FontWeight weight, + const FontWidth width, + const FontSlant slant, + const PointSize26Dot6 size, + const bool familyDefined, + const bool weightDefined, + const bool widthDefined, + const bool slantDefined, + const bool sizeDefined); + } // namespace Text } // namespace Toolkit diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Text.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Text.cpp index 6b77f00..eea6fa6 100644 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Text.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Text.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -221,6 +222,20 @@ int utcDaliAccessibilityTextFieldGetText(void) DALI_TEST_EQUALS( x->GetText( 0, 0 ), "", TEST_LOCATION ); field.SetProperty( Toolkit::TextField::Property::TEXT, "exemplary_text" ); DALI_TEST_EQUALS( x->GetText( 0, 9 ), "exemplary", TEST_LOCATION ); + + Dali::Property::Map hiddenInputSettings; + hiddenInputSettings[ Toolkit::HiddenInput::Property::MODE ] = Toolkit::HiddenInput::Mode::HIDE_ALL; + + field.SetProperty( Toolkit::TextField::Property::HIDDEN_INPUT_SETTINGS, hiddenInputSettings ); + + DALI_TEST_EQUALS( x->GetName(), "", TEST_LOCATION ); + DALI_TEST_EQUALS( x->GetText( 0, 9 ), "*********", TEST_LOCATION ); + + hiddenInputSettings[ Toolkit::HiddenInput::Property::SUBSTITUTE_CHARACTER ] = 0x23; + field.SetProperty( Toolkit::TextField::Property::HIDDEN_INPUT_SETTINGS, hiddenInputSettings ); + + DALI_TEST_EQUALS( x->GetName(), "", TEST_LOCATION ); + DALI_TEST_EQUALS( x->GetText( 0, 9 ), "#########", TEST_LOCATION ); } END_TEST; @@ -294,6 +309,16 @@ int utcDaliAccessibilityTextFieldGetTextAtOffset(void) DALI_TEST_EQUALS( range.content, " test sentence", TEST_LOCATION ); DALI_TEST_EQUALS( range.startOffset, 25, TEST_LOCATION ); DALI_TEST_EQUALS( range.endOffset, 39, TEST_LOCATION ); + + Dali::Property::Map hiddenInputSettings; + hiddenInputSettings[ Toolkit::HiddenInput::Property::MODE ] = Toolkit::HiddenInput::Mode::HIDE_ALL; + hiddenInputSettings[ Toolkit::HiddenInput::Property::SUBSTITUTE_CHARACTER ] = 0x23; + field.SetProperty( Toolkit::TextField::Property::HIDDEN_INPUT_SETTINGS, hiddenInputSettings ); + range = x->GetTextAtOffset( 8, Dali::Accessibility::TextBoundary::LINE ); + DALI_TEST_EQUALS( range.content, "", TEST_LOCATION ); + DALI_TEST_EQUALS( range.startOffset, 0, TEST_LOCATION ); + DALI_TEST_EQUALS( range.endOffset, 0, TEST_LOCATION ); + } END_TEST; @@ -321,6 +346,15 @@ int utcDaliAccessibilityTextFieldGetSetRangeOfSelection(void) DALI_TEST_EQUALS( range.startOffset, 4, TEST_LOCATION ); DALI_TEST_EQUALS( range.endOffset, 9, TEST_LOCATION ); DALI_TEST_EQUALS( range.content, "plary", TEST_LOCATION ); + + Dali::Property::Map hiddenInputSettings; + hiddenInputSettings[ Toolkit::HiddenInput::Property::MODE ] = Toolkit::HiddenInput::Mode::HIDE_ALL; + field.SetProperty( Toolkit::TextField::Property::HIDDEN_INPUT_SETTINGS, hiddenInputSettings ); + + range = x->GetRangeOfSelection( 0 ); + DALI_TEST_EQUALS( range.startOffset, 4, TEST_LOCATION ); + DALI_TEST_EQUALS( range.endOffset, 9, TEST_LOCATION ); + DALI_TEST_EQUALS( range.content, "*****", TEST_LOCATION ); } END_TEST; diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Shaping.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Shaping.cpp index 8c056dd..272db31 100755 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Shaping.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Shaping.cpp @@ -119,6 +119,7 @@ struct ShapeInfoData Length* charactersPerGlyph; ///< The characters per glyph. uint32_t expectedNumberOfNewParagraphGlyphs; ///< The expected number of glyphs. GlyphIndex* newParagraphGlyphs; ///< Indices to the new paragraphs glyphs. + bool markupProcessorEnabled; //< Enable markup processor to use markup text. Vector fontDescriptions; ///< Fonts which is used for text. }; @@ -140,7 +141,7 @@ bool ShapeInfoTest( const ShapeInfoData& data ) layoutSize, textModel, metrics, - false, + data.markupProcessorEnabled, LineWrap::WORD, false, Toolkit::DevelText::EllipsisPosition::END ); @@ -337,6 +338,34 @@ void LoadSoftwareStylingFonts() fontClient.GetFontId( pathName + DEFAULT_FONT_DIR + "/roboto/Roboto-BoldItalic.ttf" ); } +void LoadEmojiFonts() +{ + TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get(); + fontClient.ClearCache(); + fontClient.SetDpi( 96u, 96u ); + + char* pathNamePtr = get_current_dir_name(); + const std::string pathName( pathNamePtr ); + free( pathNamePtr ); + + + TextAbstraction::FontDescription fontDescriptionText; + fontDescriptionText.path = ""; + fontDescriptionText.family = "DejaVuSans"; + fontDescriptionText.width = TextAbstraction::FontWidth::NONE; + fontDescriptionText.weight = TextAbstraction::FontWeight::NORMAL; + fontDescriptionText.slant = TextAbstraction::FontSlant::NONE; + fontClient.GetFontId(fontDescriptionText, TextAbstraction::FontClient::DEFAULT_POINT_SIZE); + + TextAbstraction::FontDescription fontDescriptionEmoji; + fontDescriptionEmoji.path = ""; + fontDescriptionEmoji.family = "NotoColorEmoji"; + fontDescriptionEmoji.width = TextAbstraction::FontWidth::NONE; + fontDescriptionEmoji.weight = TextAbstraction::FontWeight::NORMAL; + fontDescriptionEmoji.slant = TextAbstraction::FontSlant::NONE; + fontClient.GetFontId(fontDescriptionEmoji, TextAbstraction::FontClient::DEFAULT_POINT_SIZE); +} + } // namespace ////////////////////////////////////////////////////////// @@ -671,7 +700,8 @@ int UtcDaliTextShape(void) nullptr, nullptr, 0u, - nullptr + nullptr, + false, }, { "Latin script", @@ -684,6 +714,7 @@ int UtcDaliTextShape(void) charactersPerGlyph02, 0u, nullptr, + false, fontDescriptions01 }, { @@ -697,6 +728,7 @@ int UtcDaliTextShape(void) charactersPerGlyph03, 2u, newParagraphGlyphs03, + false, fontDescriptions02 }, { @@ -710,6 +742,7 @@ int UtcDaliTextShape(void) charactersPerGlyph04, 0u, nullptr, + false, fontDescriptions03 }, { @@ -723,7 +756,8 @@ int UtcDaliTextShape(void) charactersPerGlyph05, 1u, newParagraphGlyphs05, - fontDescriptions04 + false, + fontDescriptions04, }, { "Latin script with some paragraphs. Update mid paragraph.", @@ -736,6 +770,7 @@ int UtcDaliTextShape(void) charactersPerGlyph05, 1u, newParagraphGlyphs06, + false, fontDescriptions05 }, { @@ -749,6 +784,7 @@ int UtcDaliTextShape(void) charactersPerGlyph05, 1u, newParagraphGlyphs07, + false, fontDescriptions06 }, }; @@ -927,6 +963,7 @@ int UtcDaliTextSoftwareStyling(void) charactersPerGlyph, 0u, nullptr, + false, fontDescriptions01 }, { @@ -940,6 +977,7 @@ int UtcDaliTextSoftwareStyling(void) charactersPerGlyph, 0u, nullptr, + false, fontDescriptions02 } }; @@ -960,3 +998,142 @@ int UtcDaliTextSoftwareStyling(void) tet_result(TET_PASS); END_TEST; } + + +int UtcDaliTextShapeEmojiSequences(void) +{ + + ToolkitTestApplication application; + + tet_infoline(" UtcDaliTextShapeEmojiSequences"); + + const std::string colorFontFamily( "NotoColorEmoji" ); + const std::string textFontFamily( "DejaVuSans" ); + + LoadEmojiFonts(); + + //Common attributes for font Descriptions + CharacterRun characterRun = {0u, 2u}; + FontWeight weight = TextAbstraction::FontWeight::NORMAL; + FontWidth width = TextAbstraction::FontWidth::NORMAL; + FontSlant slant = TextAbstraction::FontSlant::ITALIC; + PointSize26Dot6 size = TextAbstraction::FontClient::DEFAULT_POINT_SIZE; + + bool familyDefined = true; + bool weightDefined = false; + bool widthDefined = false; + bool slantDefined = false; + bool sizeDefined = false; + + + // variation selector 16 (Emoji) + struct GlyphInfoData glyphsVS16[] = + { + { 2u, 74u, 0.f, 0.f, 0.f, 0.f, 39.0f, 0.f, false, false }, + }; + CharacterIndex characterIndicesVS16[] = { 0u, 1u}; + Length charactersPerGlyphVS16[] = { 2u }; + + + + // variation selector 15 (Text) + struct GlyphInfoData glyphsVS15[] = + { + { 1u, 3842u, 0.f, 0.f, 0.f, 0.f, 14.0f, 0.f, false, false }, + { 1u, 8203u, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, false, false }, + }; + CharacterIndex characterIndicesVS15[] = { 0u, 0u}; + Length charactersPerGlyphVS15[] = { 0u, 2u }; + + //Font Descriptions + Vector fontDescriptionsColorVS16 = + CreateSingleFontDescription (characterRun, colorFontFamily, weight, width, + slant, size, familyDefined, weightDefined, widthDefined, slantDefined, sizeDefined); + + Vector fontDescriptionsColorVS15 = + CreateSingleFontDescription (characterRun, colorFontFamily, weight, width, + slant, size, familyDefined, weightDefined, widthDefined, slantDefined, sizeDefined); + + Vector fontDescriptionsTextVS16 = + CreateSingleFontDescription (characterRun, textFontFamily, weight, width, + slant, size, familyDefined, weightDefined, widthDefined, slantDefined, sizeDefined); + + Vector fontDescriptionsTextVS15 = + CreateSingleFontDescription (characterRun, textFontFamily, weight, width, + slant, size, familyDefined, weightDefined, widthDefined, slantDefined, sizeDefined); + + + struct ShapeInfoData data[] = + { + { + "EMOJI Sequence: Color Font with VS16", + "☪️", + 0u, + 2u, + 1u, + glyphsVS16, + characterIndicesVS16, + charactersPerGlyphVS16, + 0u, + nullptr, + true, + fontDescriptionsColorVS16 + }, + { + "EMOJI Sequence: Color Font with VS15", + "☪︎", + 0u, + 2u, + 2u, + glyphsVS15, + characterIndicesVS15, + charactersPerGlyphVS15, + 0u, + nullptr, + true, + fontDescriptionsColorVS15 + }, + { + "EMOJI Sequence: Text Font with VS16", + "☪️", + 0u, + 2u, + 1u, + glyphsVS16, + characterIndicesVS16, + charactersPerGlyphVS16, + 0u, + nullptr, + true, + fontDescriptionsTextVS16 + }, + { + "EMOJI Sequence: Text Font with VS15", + "☪︎", + 0u, + 2u, + 2u, + glyphsVS15, + characterIndicesVS15, + charactersPerGlyphVS15, + 0u, + nullptr, + true, + fontDescriptionsTextVS15 + }, + }; + + const unsigned int numberOfTests = 4u; + + for( unsigned int index = 0u; index < numberOfTests; ++index ) + { + tet_infoline( data[index].description.c_str()); + if( !ShapeInfoTest( data[index] ) ) + { + tet_result(TET_FAIL); + } + } + + tet_result(TET_PASS); + END_TEST; +} diff --git a/automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp b/automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp index cd834df..51b9923 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp @@ -422,7 +422,6 @@ int UtcDaliImageVisualRemoteImageLoad(void) END_TEST; } - int UtcDaliImageVisualWithNativeImage(void) { ToolkitTestApplication application; @@ -468,6 +467,71 @@ int UtcDaliImageVisualWithNativeImage(void) END_TEST; } +int UtcDaliImageVisualWithNativeImageCustomShader(void) +{ + ToolkitTestApplication application; + tet_infoline( "Use Native Image as url and Use custom shader" ); + + NativeImageSourcePtr nativeImageSource = NativeImageSource::New(500, 500, NativeImageSource::COLOR_DEPTH_DEFAULT); + ImageUrl imageUrl = Dali::Toolkit::Image::GenerateUrl(nativeImageSource); + std::string url = imageUrl.GetUrl(); + + VisualFactory factory = VisualFactory::Get(); + DALI_TEST_CHECK( factory ); + + Property::Map propertyMap; + Property::Map shaderMap; + const std::string customVertexShaderSource = "Foobar"; + const std::string customFragmentShaderSource = "Foobar"; + shaderMap[Toolkit::Visual::Shader::Property::FRAGMENT_SHADER] = customFragmentShaderSource; + shaderMap[Toolkit::Visual::Shader::Property::VERTEX_SHADER] = customVertexShaderSource; + + propertyMap.Insert( Toolkit::Visual::Property::TYPE, Visual::IMAGE ); + propertyMap.Insert( Toolkit::Visual::Property::SHADER, shaderMap ); + propertyMap.Insert( ImageVisual::Property::URL, url ); + + Visual::Base visual = factory.CreateVisual( propertyMap ); + DALI_TEST_CHECK( visual ); + + DummyControl actor = DummyControl::New(); + DummyControlImpl& dummyImpl = static_cast(actor.GetImplementation()); + dummyImpl.RegisterVisual( Control::CONTROL_PROPERTY_END_INDEX + 1, visual ); + + actor.SetProperty( Actor::Property::SIZE, Vector2( 200.f, 200.f ) ); + actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER ); + + DALI_TEST_EQUALS( actor.GetRendererCount(), 0u, TEST_LOCATION ); + + application.GetScene().Add( actor ); + + DALI_TEST_EQUALS( actor.GetRendererCount(), 1u, TEST_LOCATION ); + + application.SendNotification(); + application.Render(16); + + Renderer renderer = actor.GetRendererAt(0); + Shader shader = renderer.GetShader(); + + Property::Value value = shader.GetProperty(Shader::Property::PROGRAM); + DALI_TEST_CHECK(value.GetType() == Property::MAP); + const Property::Map* outMap = value.GetMap(); + std::string fragmentShaderSource = (*outMap)["fragment"].Get(); + std::string vertexShaderSource = (*outMap)["vertex"].Get(); + + // Compare vertex shader is equal + DALI_TEST_EQUALS( customVertexShaderSource, vertexShaderSource, TEST_LOCATION ); + + // Check fragment shader changed + const char* fragmentPrefix = Dali::NativeImageSourceTest::GetCustomFragmentPrefix(); + size_t pos = fragmentShaderSource.find(fragmentPrefix); + + DALI_TEST_EQUALS( pos != std::string::npos, true, TEST_LOCATION ); + + DALI_TEST_EQUALS( std::string(fragmentPrefix) + customFragmentShaderSource, fragmentShaderSource, TEST_LOCATION ); + + END_TEST; +} + int UtcDaliImageVisualWithNativeImageRemoved(void) { ToolkitTestApplication application; diff --git a/automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp b/automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp index aa7f5bd..b0f9764 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp @@ -851,6 +851,37 @@ int UtcDaliToolkitTextLabelEmojisP(void) application.SendNotification(); application.Render(); + // EMOJI Sequences case for coverage. + std::string emojiSequences = + "Text VS15 ☪︎\n" //text presentation sequence and selector + "Color VS16 ☪️\n" //emoji presentation sequence and selector + "Default ☪ \n" //default presentation + "FamilyManWomanGirlBoy 👨‍👩‍👧‍👦\n" // emoji multi zwj sequence + "WomanScientist 👩‍🔬\n" // emoji zwj sequence + "WomanScientistLightSkinTone👩🏻‍🔬 \n" //emoji modifier sequence: skin tone & JWZ + "LeftRightArrowText↔︎\n" //text presentation sequence and selector + "LeftRightArrowEmoji↔️\n" //emoji presentation sequence and selector + "SouthKoreaFlag🇰🇷\n" //emoji flag sequence + "JordanFlag🇯🇴\n" // emoji flag sequence + "EnglandFlag🏴󠁧󠁢󠁥󠁮󠁧󠁿\n" //emoji tag sequence like England flag + "Runner 🏃‍➡️ \n" + "VictoryHandMediumLightSkinTone:✌️🏼\n" //emoji modifier sequence: skin tone + "RainbowFlag:🏳️‍🌈 \n" //emoji zwj sequence: Rainbow Flag + "keycap# #️⃣ \n" // fully-qualified emoji keycap sequence + "keycap#_text #⃣ \n" // unqualified emoji keycap sequence + "keycap3 3️⃣ \n" // fully-qualified emoji keycap sequence + "keycap3_text 3⃣ \n" // unqualified emoji keycap sequence + "two adjacent glyphs ☪️️️☪️\n" //This line should be rendered as two adjacent glyphs + "Digit 8️ 8︎ 8\n" // should be rendered according to selector + "Surfing Medium Skin Female: 🏄🏼‍♀️"; // Person Surfing + Medium Skin Tone +? Zero Width Joiner + Female Sign + + label.SetProperty( TextLabel::Property::TEXT, emojiSequences ); + label.SetProperty( TextLabel::Property::ENABLE_MARKUP, true ); + label.SetProperty( TextLabel::Property::MULTI_LINE, true); + label.SetProperty( TextLabel::Property::ELLIPSIS, false); + + application.SendNotification(); + application.Render(); END_TEST; } diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl-property-handler.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl-property-handler.cpp new file mode 100644 index 0000000..9c1272c --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl-property-handler.cpp @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2021 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. + * + */ + +// CLASS HEADER +#include + +// INTERNAL INCLUDES +#include +#include + +namespace Dali::Toolkit::Internal +{ + +void ScrollViewPropertyHandler::Set(BaseObject* object, Property::Index index, const Property::Value& value) +{ + Toolkit::ScrollView scrollView = Toolkit::ScrollView::DownCast(Dali::BaseHandle(object)); + + if(scrollView) + { + ScrollView& scrollViewImpl(GetImpl(scrollView)); + switch(index) + { + case Toolkit::ScrollView::Property::WRAP_ENABLED: + { + scrollViewImpl.SetWrapMode(value.Get()); + break; + } + case Toolkit::ScrollView::Property::PANNING_ENABLED: + { + scrollViewImpl.SetScrollSensitive(value.Get()); + break; + } + case Toolkit::ScrollView::Property::AXIS_AUTO_LOCK_ENABLED: + { + scrollViewImpl.SetAxisAutoLock(value.Get()); + break; + } + case Toolkit::ScrollView::Property::WHEEL_SCROLL_DISTANCE_STEP: + { + scrollViewImpl.SetWheelScrollDistanceStep(value.Get()); + break; + } + case Toolkit::ScrollView::Property::SCROLL_MODE: + { + const Property::Map* map = value.GetMap(); + if(map) + { + SetScrollMode(scrollViewImpl, *map); + } + } + } + } +} + +Property::Value ScrollViewPropertyHandler::Get(BaseObject* object, Property::Index index) +{ + Property::Value value; + + Toolkit::ScrollView scrollView = Toolkit::ScrollView::DownCast(Dali::BaseHandle(object)); + + if(scrollView) + { + ScrollView& scrollViewImpl(GetImpl(scrollView)); + switch(index) + { + case Toolkit::ScrollView::Property::WRAP_ENABLED: + { + value = scrollViewImpl.GetWrapMode(); + break; + } + case Toolkit::ScrollView::Property::PANNING_ENABLED: + { + value = scrollViewImpl.GetScrollSensitive(); + break; + } + case Toolkit::ScrollView::Property::AXIS_AUTO_LOCK_ENABLED: + { + value = scrollViewImpl.GetAxisAutoLock(); + break; + } + case Toolkit::ScrollView::Property::WHEEL_SCROLL_DISTANCE_STEP: + { + value = scrollViewImpl.GetWheelScrollDistanceStep(); + break; + } + } + } + + return value; +} + +void ScrollViewPropertyHandler::SetScrollMode(ScrollView& scrollView, const Property::Map& scrollModeMap) +{ + Toolkit::RulerPtr rulerX, rulerY; + + // Check the scroll mode in the X axis + bool xAxisScrollEnabled = true; + Property::Value* valuePtr = scrollModeMap.Find(Toolkit::ScrollMode::X_AXIS_SCROLL_ENABLED, "xAxisScrollEnabled"); + if(valuePtr && valuePtr->GetType() == Property::BOOLEAN) + { + valuePtr->Get(xAxisScrollEnabled); + } + + if(!xAxisScrollEnabled) + { + // Default ruler and disabled + rulerX = new Toolkit::DefaultRuler(); + rulerX->Disable(); + } + else + { + valuePtr = scrollModeMap.Find(Toolkit::ScrollMode::X_AXIS_SNAP_TO_INTERVAL, "xAxisSnapToInterval"); + float xAxisSnapToInterval = 0.0f; + if(valuePtr && valuePtr->Get(xAxisSnapToInterval)) + { + // Fixed ruler and enabled + rulerX = new Toolkit::FixedRuler(xAxisSnapToInterval); + } + else + { + // Default ruler and enabled + rulerX = new Toolkit::DefaultRuler(); + } + + valuePtr = scrollModeMap.Find(Toolkit::ScrollMode::X_AXIS_SCROLL_BOUNDARY, "xAxisScrollBoundary"); + float xAxisScrollBoundary = 0.0f; + if(valuePtr && valuePtr->Get(xAxisScrollBoundary)) + { + // By default ruler domain is disabled unless set + rulerX->SetDomain(Toolkit::RulerDomain(0, xAxisScrollBoundary, true)); + } + } + + // Check the scroll mode in the Y axis + bool yAxisScrollEnabled = true; + valuePtr = scrollModeMap.Find(Toolkit::ScrollMode::Y_AXIS_SCROLL_ENABLED, "yAxisScrollEnabled"); + if(valuePtr && valuePtr->GetType() == Property::BOOLEAN) + { + valuePtr->Get(yAxisScrollEnabled); + } + + if(!yAxisScrollEnabled) + { + // Default ruler and disabled + rulerY = new Toolkit::DefaultRuler(); + rulerY->Disable(); + } + else + { + valuePtr = scrollModeMap.Find(Toolkit::ScrollMode::Y_AXIS_SNAP_TO_INTERVAL, "yAxisSnapToInterval"); + float yAxisSnapToInterval = 0.0f; + if(valuePtr && valuePtr->Get(yAxisSnapToInterval)) + { + // Fixed ruler and enabled + rulerY = new Toolkit::FixedRuler(yAxisSnapToInterval); + } + else + { + // Default ruler and enabled + rulerY = new Toolkit::DefaultRuler(); + } + + valuePtr = scrollModeMap.Find(Toolkit::ScrollMode::Y_AXIS_SCROLL_BOUNDARY, "yAxisScrollBoundary"); + float yAxisScrollBoundary = 0.0f; + if(valuePtr && valuePtr->Get(yAxisScrollBoundary)) + { + // By default ruler domain is disabled unless set + rulerY->SetDomain(Toolkit::RulerDomain(0, yAxisScrollBoundary, true)); + } + } + + scrollView.SetRulerX(rulerX); + scrollView.SetRulerY(rulerY); +} + +void ScrollViewPropertyHandler::UpdatePropertyDomain(ScrollView& scrollView) +{ + Actor self = scrollView.Self(); + Vector3 size = self.GetTargetSize(); + Vector2 min = scrollView.mMinScroll; + Vector2 max = scrollView.mMaxScroll; + bool scrollPositionChanged = false; + bool domainChanged = false; + + bool canScrollVertical = false; + bool canScrollHorizontal = false; + scrollView.UpdateLocalScrollProperties(); + if(scrollView.mRulerX->IsEnabled()) + { + const Toolkit::RulerDomain& rulerDomain = scrollView.mRulerX->GetDomain(); + if(fabsf(min.x - rulerDomain.min) > Math::MACHINE_EPSILON_100 || fabsf(max.x - rulerDomain.max) > Math::MACHINE_EPSILON_100) + { + domainChanged = true; + min.x = rulerDomain.min; + max.x = rulerDomain.max; + + // make sure new scroll value is within new domain + if(scrollView.mScrollPrePosition.x < min.x || scrollView.mScrollPrePosition.x > max.x) + { + scrollPositionChanged = true; + scrollView.mScrollPrePosition.x = Clamp(scrollView.mScrollPrePosition.x, -(max.x - size.x), -min.x); + } + } + if((fabsf(rulerDomain.max - rulerDomain.min) - size.x) > Math::MACHINE_EPSILON_100) + { + canScrollHorizontal = true; + } + } + else if(fabs(min.x) > Math::MACHINE_EPSILON_100 || fabs(max.x) > Math::MACHINE_EPSILON_100) + { + // need to reset to 0 + domainChanged = true; + min.x = 0.0f; + max.x = 0.0f; + canScrollHorizontal = false; + } + + if(scrollView.mRulerY->IsEnabled()) + { + const Toolkit::RulerDomain& rulerDomain = scrollView.mRulerY->GetDomain(); + if(fabsf(min.y - rulerDomain.min) > Math::MACHINE_EPSILON_100 || fabsf(max.y - rulerDomain.max) > Math::MACHINE_EPSILON_100) + { + domainChanged = true; + min.y = rulerDomain.min; + max.y = rulerDomain.max; + + // make sure new scroll value is within new domain + if(scrollView.mScrollPrePosition.y < min.y || scrollView.mScrollPrePosition.y > max.y) + { + scrollPositionChanged = true; + scrollView.mScrollPrePosition.y = Clamp(scrollView.mScrollPrePosition.y, -(max.y - size.y), -min.y); + } + } + if((fabsf(rulerDomain.max - rulerDomain.min) - size.y) > Math::MACHINE_EPSILON_100) + { + canScrollVertical = true; + } + } + else if(fabs(min.y) > Math::MACHINE_EPSILON_100 || fabs(max.y) > Math::MACHINE_EPSILON_100) + { + // need to reset to 0 + domainChanged = true; + min.y = 0.0f; + max.y = 0.0f; + canScrollVertical = false; + } + + // avoid setting properties if possible, otherwise this will cause an entire update as well as triggering constraints using each property we update + if(scrollView.mCanScrollVertical != canScrollVertical) + { + scrollView.mCanScrollVertical = canScrollVertical; + self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL, canScrollVertical); + } + if(scrollView.mCanScrollHorizontal != canScrollHorizontal) + { + scrollView.mCanScrollHorizontal = canScrollHorizontal; + self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL, canScrollHorizontal); + } + if(scrollPositionChanged) + { + self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, scrollView.mScrollPrePosition); + } + if(domainChanged) + { + scrollView.mMinScroll = min; + scrollView.mMaxScroll = max; + self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MIN, scrollView.mMinScroll); + self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, scrollView.mMaxScroll); + } +} + +} // namespace Dali::Toolkit::Internal diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl-property-handler.h b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl-property-handler.h new file mode 100644 index 0000000..1923cfc --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl-property-handler.h @@ -0,0 +1,69 @@ +#ifndef DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_IMPL_PROPERTY_HANDLER_H +#define DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_IMPL_PROPERTY_HANDLER_H + +/* + * Copyright (c) 2021 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. + * + */ + +// EXTERNAL INCLUDES +#include +#include + +namespace Dali::Toolkit::Internal +{ +class ScrollView; + +/// Handles the properties in scroll view calling the appropriate scroll view methods +struct ScrollViewPropertyHandler +{ + /** + * @brief Sets the property on the given scroll-view object. + * + * @param object The scrollview object + * @param index The index to set + * @param value The value to set + */ + static void Set(BaseObject* object, Property::Index index, const Property::Value& value); + + /** + * @brief Retrieves the value of a scroll-view property. + * + * @param object The scrollview object + * @param index The index whose value is to be retrieved + * @return + */ + static Property::Value Get(BaseObject* object, Property::Index index); + + /** + * Set up default rulers using a property map + * @param[in] scrollView The scroll view to apply this on + * @param[in] scrollModeMap A map defining the characteristics of X and Y scrolling + * using either FixedRuler or DefaultRuler. + */ + static void SetScrollMode(ScrollView& scrollView, const Property::Map& scrollModeMap); + + /** + * This is called whenever the Scroll Rulers are modified. + * + * This will update the properties: 'scrollPositionMin' * and 'scrollPositionMax' to reflect the changes. + * @param scrollView The Scroll View to modify + */ + static void UpdatePropertyDomain(ScrollView& scrollView); +}; + +} // namespace Dali::Toolkit::Internal + +#endif // DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_IMPL_PROPERTY_HANDLER_H diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp index 86cfc56..712cb17 100644 --- a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -673,7 +674,7 @@ void ScrollView::OnInitialize() self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL, mCanScrollVertical); self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL, mCanScrollHorizontal); - UpdatePropertyDomain(); + ScrollViewPropertyHandler::UpdatePropertyDomain(*this); mConstraints.SetInternalConstraints(*this); // Connect wheel event @@ -794,7 +795,7 @@ void ScrollView::SetRulerX(RulerPtr ruler) { mRulerX = ruler; - UpdatePropertyDomain(); + ScrollViewPropertyHandler::UpdatePropertyDomain(*this); mConstraints.UpdateMainInternalConstraint(*this); } @@ -802,107 +803,10 @@ void ScrollView::SetRulerY(RulerPtr ruler) { mRulerY = ruler; - UpdatePropertyDomain(); + ScrollViewPropertyHandler::UpdatePropertyDomain(*this); mConstraints.UpdateMainInternalConstraint(*this); } -void ScrollView::UpdatePropertyDomain() -{ - Actor self = Self(); - Vector3 size = self.GetTargetSize(); - Vector2 min = mMinScroll; - Vector2 max = mMaxScroll; - bool scrollPositionChanged = false; - bool domainChanged = false; - - bool canScrollVertical = false; - bool canScrollHorizontal = false; - UpdateLocalScrollProperties(); - if(mRulerX->IsEnabled()) - { - const Toolkit::RulerDomain& rulerDomain = mRulerX->GetDomain(); - if(fabsf(min.x - rulerDomain.min) > Math::MACHINE_EPSILON_100 || fabsf(max.x - rulerDomain.max) > Math::MACHINE_EPSILON_100) - { - domainChanged = true; - min.x = rulerDomain.min; - max.x = rulerDomain.max; - - // make sure new scroll value is within new domain - if(mScrollPrePosition.x < min.x || mScrollPrePosition.x > max.x) - { - scrollPositionChanged = true; - mScrollPrePosition.x = Clamp(mScrollPrePosition.x, -(max.x - size.x), -min.x); - } - } - if((fabsf(rulerDomain.max - rulerDomain.min) - size.x) > Math::MACHINE_EPSILON_100) - { - canScrollHorizontal = true; - } - } - else if(fabs(min.x) > Math::MACHINE_EPSILON_100 || fabs(max.x) > Math::MACHINE_EPSILON_100) - { - // need to reset to 0 - domainChanged = true; - min.x = 0.0f; - max.x = 0.0f; - canScrollHorizontal = false; - } - - if(mRulerY->IsEnabled()) - { - const Toolkit::RulerDomain& rulerDomain = mRulerY->GetDomain(); - if(fabsf(min.y - rulerDomain.min) > Math::MACHINE_EPSILON_100 || fabsf(max.y - rulerDomain.max) > Math::MACHINE_EPSILON_100) - { - domainChanged = true; - min.y = rulerDomain.min; - max.y = rulerDomain.max; - - // make sure new scroll value is within new domain - if(mScrollPrePosition.y < min.y || mScrollPrePosition.y > max.y) - { - scrollPositionChanged = true; - mScrollPrePosition.y = Clamp(mScrollPrePosition.y, -(max.y - size.y), -min.y); - } - } - if((fabsf(rulerDomain.max - rulerDomain.min) - size.y) > Math::MACHINE_EPSILON_100) - { - canScrollVertical = true; - } - } - else if(fabs(min.y) > Math::MACHINE_EPSILON_100 || fabs(max.y) > Math::MACHINE_EPSILON_100) - { - // need to reset to 0 - domainChanged = true; - min.y = 0.0f; - max.y = 0.0f; - canScrollVertical = false; - } - - // avoid setting properties if possible, otherwise this will cause an entire update as well as triggering constraints using each property we update - if(mCanScrollVertical != canScrollVertical) - { - mCanScrollVertical = canScrollVertical; - self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL, canScrollVertical); - } - if(mCanScrollHorizontal != canScrollHorizontal) - { - mCanScrollHorizontal = canScrollHorizontal; - self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL, canScrollHorizontal); - } - if(scrollPositionChanged) - { - DALI_LOG_SCROLL_STATE("[0x%X] Domain Changed, setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y); - self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition); - } - if(domainChanged) - { - mMinScroll = min; - mMaxScroll = max; - self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MIN, mMinScroll); - self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, mMaxScroll); - } -} - void ScrollView::SetScrollSensitive(bool sensitive) { Actor self = Self(); @@ -1482,7 +1386,7 @@ bool ScrollView::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface* void ScrollView::OnSizeAnimation(Animation& animation, const Vector3& targetSize) { // need to update domain properties for new size - UpdatePropertyDomain(); + ScrollViewPropertyHandler::UpdatePropertyDomain(*this); } void ScrollView::OnSizeSet(const Vector3& size) @@ -1497,7 +1401,7 @@ void ScrollView::OnSizeSet(const Vector3& size) mMaxOvershoot = mUserMaxOvershoot; } } - UpdatePropertyDomain(); + ScrollViewPropertyHandler::UpdatePropertyDomain(*this); mConstraints.UpdateMainInternalConstraint(*this); if(IsOvershootEnabled()) { @@ -2180,45 +2084,6 @@ void ScrollView::FinishTransform() } } -Vector2 ScrollView::GetOvershoot(Vector2& position) const -{ - Vector3 size = Self().GetCurrentProperty(Actor::Property::SIZE); - Vector2 overshoot; - - const RulerDomain rulerDomainX = mRulerX->GetDomain(); - const RulerDomain rulerDomainY = mRulerY->GetDomain(); - - if(mRulerX->IsEnabled() && rulerDomainX.enabled) - { - const float left = rulerDomainX.min - position.x; - const float right = size.width - rulerDomainX.max - position.x; - if(left < 0) - { - overshoot.x = left; - } - else if(right > 0) - { - overshoot.x = right; - } - } - - if(mRulerY->IsEnabled() && rulerDomainY.enabled) - { - const float top = rulerDomainY.min - position.y; - const float bottom = size.height - rulerDomainY.max - position.y; - if(top < 0) - { - overshoot.y = top; - } - else if(bottom > 0) - { - overshoot.y = bottom; - } - } - - return overshoot; -} - bool ScrollView::OnAccessibilityPan(PanGesture gesture) { // Keep track of whether this is an AccessibilityPan @@ -2263,164 +2128,12 @@ void ScrollView::WrapPosition(Vector2& position) const void ScrollView::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value) { - Toolkit::ScrollView scrollView = Toolkit::ScrollView::DownCast(Dali::BaseHandle(object)); - - if(scrollView) - { - ScrollView& scrollViewImpl(GetImpl(scrollView)); - switch(index) - { - case Toolkit::ScrollView::Property::WRAP_ENABLED: - { - scrollViewImpl.SetWrapMode(value.Get()); - break; - } - case Toolkit::ScrollView::Property::PANNING_ENABLED: - { - scrollViewImpl.SetScrollSensitive(value.Get()); - break; - } - case Toolkit::ScrollView::Property::AXIS_AUTO_LOCK_ENABLED: - { - scrollViewImpl.SetAxisAutoLock(value.Get()); - break; - } - case Toolkit::ScrollView::Property::WHEEL_SCROLL_DISTANCE_STEP: - { - scrollViewImpl.SetWheelScrollDistanceStep(value.Get()); - break; - } - case Toolkit::ScrollView::Property::SCROLL_MODE: - { - const Property::Map* map = value.GetMap(); - if(map) - { - scrollViewImpl.SetScrollMode(*map); - } - } - } - } + ScrollViewPropertyHandler::Set(object, index, value); } Property::Value ScrollView::GetProperty(BaseObject* object, Property::Index index) { - Property::Value value; - - Toolkit::ScrollView scrollView = Toolkit::ScrollView::DownCast(Dali::BaseHandle(object)); - - if(scrollView) - { - ScrollView& scrollViewImpl(GetImpl(scrollView)); - switch(index) - { - case Toolkit::ScrollView::Property::WRAP_ENABLED: - { - value = scrollViewImpl.GetWrapMode(); - break; - } - case Toolkit::ScrollView::Property::PANNING_ENABLED: - { - value = scrollViewImpl.GetScrollSensitive(); - break; - } - case Toolkit::ScrollView::Property::AXIS_AUTO_LOCK_ENABLED: - { - value = scrollViewImpl.GetAxisAutoLock(); - break; - } - case Toolkit::ScrollView::Property::WHEEL_SCROLL_DISTANCE_STEP: - { - value = scrollViewImpl.GetWheelScrollDistanceStep(); - break; - } - } - } - - return value; -} - -void ScrollView::SetScrollMode(const Property::Map& scrollModeMap) -{ - Toolkit::RulerPtr rulerX, rulerY; - - // Check the scroll mode in the X axis - bool xAxisScrollEnabled = true; - Property::Value* valuePtr = scrollModeMap.Find(Toolkit::ScrollMode::X_AXIS_SCROLL_ENABLED, "xAxisScrollEnabled"); - if(valuePtr && valuePtr->GetType() == Property::BOOLEAN) - { - valuePtr->Get(xAxisScrollEnabled); - } - - if(!xAxisScrollEnabled) - { - // Default ruler and disabled - rulerX = new Toolkit::DefaultRuler(); - rulerX->Disable(); - } - else - { - valuePtr = scrollModeMap.Find(Toolkit::ScrollMode::X_AXIS_SNAP_TO_INTERVAL, "xAxisSnapToInterval"); - float xAxisSnapToInterval = 0.0f; - if(valuePtr && valuePtr->Get(xAxisSnapToInterval)) - { - // Fixed ruler and enabled - rulerX = new Toolkit::FixedRuler(xAxisSnapToInterval); - } - else - { - // Default ruler and enabled - rulerX = new Toolkit::DefaultRuler(); - } - - valuePtr = scrollModeMap.Find(Toolkit::ScrollMode::X_AXIS_SCROLL_BOUNDARY, "xAxisScrollBoundary"); - float xAxisScrollBoundary = 0.0f; - if(valuePtr && valuePtr->Get(xAxisScrollBoundary)) - { - // By default ruler domain is disabled unless set - rulerX->SetDomain(Toolkit::RulerDomain(0, xAxisScrollBoundary, true)); - } - } - - // Check the scroll mode in the Y axis - bool yAxisScrollEnabled = true; - valuePtr = scrollModeMap.Find(Toolkit::ScrollMode::Y_AXIS_SCROLL_ENABLED, "yAxisScrollEnabled"); - if(valuePtr && valuePtr->GetType() == Property::BOOLEAN) - { - valuePtr->Get(yAxisScrollEnabled); - } - - if(!yAxisScrollEnabled) - { - // Default ruler and disabled - rulerY = new Toolkit::DefaultRuler(); - rulerY->Disable(); - } - else - { - valuePtr = scrollModeMap.Find(Toolkit::ScrollMode::Y_AXIS_SNAP_TO_INTERVAL, "yAxisSnapToInterval"); - float yAxisSnapToInterval = 0.0f; - if(valuePtr && valuePtr->Get(yAxisSnapToInterval)) - { - // Fixed ruler and enabled - rulerY = new Toolkit::FixedRuler(yAxisSnapToInterval); - } - else - { - // Default ruler and enabled - rulerY = new Toolkit::DefaultRuler(); - } - - valuePtr = scrollModeMap.Find(Toolkit::ScrollMode::Y_AXIS_SCROLL_BOUNDARY, "yAxisScrollBoundary"); - float yAxisScrollBoundary = 0.0f; - if(valuePtr && valuePtr->Get(yAxisScrollBoundary)) - { - // By default ruler domain is disabled unless set - rulerY->SetDomain(Toolkit::RulerDomain(0, yAxisScrollBoundary, true)); - } - } - - SetRulerX(rulerX); - SetRulerY(rulerY); + return ScrollViewPropertyHandler::Get(object, index); } ScrollView::LockAxis GetLockAxis(const Vector2& panDelta, ScrollView::LockAxis currentLockAxis, float lockGradient) diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h index 4d871a7..4061727 100644 --- a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h @@ -47,6 +47,8 @@ typedef IntrusivePtr ScrollInternalConstraintsPtr; class ScrollOvershootIndicator; typedef IntrusivePtr ScrollOvershootIndicatorPtr; +class ScrollViewPropertyHandler; + /** * @copydoc Toolkit::ScrollView */ @@ -774,13 +776,6 @@ private: void SnapInternalYTo(float position); /** - * This is called internally whenever the Scroll Rulers are - * modified. This will update the properties: 'scrollPositionMin' - * and 'scrollPositionMax' to reflect the changes. - */ - void UpdatePropertyDomain(); - - /** * Called when the gesture starts. */ void GestureStarted(); @@ -821,20 +816,6 @@ private: void FinishTransform(); /** - * Returns overshoot vector based on current position - * - * Overshoot vector is defined as how far outside of bounds - * the viewport is trying to view (prior to being clamped). - * - * an overshoot of (100,50), means user is in bottom right corner, - * trying to pan +100 to the right, and +50 below. This can be used - * to determine an effect, such as stretching. - * - * @param[in] position The position for which you wish to obtain overshoot vector - */ - Vector2 GetOvershoot(Vector2& position) const; - - /** * Clamps position within the domain set up by X/Y Rulers * * @param[in,out] position The position you wish to clamp @@ -924,13 +905,6 @@ private: */ void OnScrollUpdateNotification(Dali::PropertyNotification& source); - /** - * Set up default rulers using a property map - * @param[in] scrollModeMap A map defining the characteristics of X and Y scrolling - * using either FixedRuler or DefaultRuler. - */ - void SetScrollMode(const Property::Map& scrollModeMap); - private: // Undefined ScrollView(const ScrollView&); @@ -1020,6 +994,7 @@ private: bool mTransientScrollBar : 1; ///< True if scroll-bar should be automatically show/hidden during/after panning friend ScrollViewConstraints; + friend ScrollViewPropertyHandler; }; /** diff --git a/dali-toolkit/internal/controls/text-controls/common-text-utils.cpp b/dali-toolkit/internal/controls/text-controls/common-text-utils.cpp index 7331f3e..7033e6d 100644 --- a/dali-toolkit/internal/controls/text-controls/common-text-utils.cpp +++ b/dali-toolkit/internal/controls/text-controls/common-text-utils.cpp @@ -27,7 +27,7 @@ void CommonTextUtils::RenderText( Text::RendererPtr renderer, Text::ControllerPtr controller, Text::DecoratorPtr decorator, - float alignmentOffset, + float& alignmentOffset, Actor& renderableActor, Actor& backgroundActor, Toolkit::Control& stencil, diff --git a/dali-toolkit/internal/controls/text-controls/common-text-utils.h b/dali-toolkit/internal/controls/text-controls/common-text-utils.h index f2eaba7..f9069a2 100644 --- a/dali-toolkit/internal/controls/text-controls/common-text-utils.h +++ b/dali-toolkit/internal/controls/text-controls/common-text-utils.h @@ -37,7 +37,7 @@ public: * @param[in] renderer pointer to the text renderer * @param[in] controller pointer to the text controller * @param[in] decorator pointer to the text decorator - * @param[in] alignmentOffset Alignment offset + * @param[in,out] alignmentOffset Alignment offset * @param[in,out] renderableActor Actor for rendering text * @param[in,out] backgroundActor Actor for rendering background * @param[in,out] stencil Clipping actor @@ -49,7 +49,7 @@ public: Text::RendererPtr renderer, Text::ControllerPtr controller, Text::DecoratorPtr decorator, - float alignmentOffset, + float& alignmentOffset, Actor& renderableActor, Actor& backgroundActor, Toolkit::Control& stencil, diff --git a/dali-toolkit/internal/controls/text-controls/text-field-impl.cpp b/dali-toolkit/internal/controls/text-controls/text-field-impl.cpp index fe01e46..b48faa0 100644 --- a/dali-toolkit/internal/controls/text-controls/text-field-impl.cpp +++ b/dali-toolkit/internal/controls/text-controls/text-field-impl.cpp @@ -201,6 +201,28 @@ Toolkit::TextField::InputStyle::Mask ConvertInputStyle(Text::InputStyle::Mask in return fieldInputStyleMask; } +bool IsHiddenInput(Toolkit::TextField textField) +{ + Property::Map hiddenInputSettings = textField.GetProperty(Toolkit::TextField::Property::HIDDEN_INPUT_SETTINGS); + auto mode = hiddenInputSettings.Find(Toolkit::HiddenInput::Property::MODE); + if (mode && (mode->Get() != Toolkit::HiddenInput::Mode::HIDE_NONE)) + { + return true; + } + return false; +} + +char GetSubstituteCharacter(Toolkit::TextField textField) +{ + Property::Map hiddenInputSettings = textField.GetProperty(Toolkit::TextField::Property::HIDDEN_INPUT_SETTINGS); + auto substChar = hiddenInputSettings.Find(Toolkit::HiddenInput::Property::SUBSTITUTE_CHARACTER); + if (substChar) + { + return static_cast(substChar->Get()); + } + return STAR; +} + } // namespace Toolkit::TextField TextField::New() @@ -1101,6 +1123,11 @@ TextField::~TextField() std::string TextField::AccessibleImpl::GetName() { auto self = Toolkit::TextField::DownCast(Self()); + if (IsHiddenInput(self)) + { + return {}; + } + return self.GetProperty(Toolkit::TextField::Property::TEXT).Get(); } @@ -1118,7 +1145,10 @@ std::string TextField::AccessibleImpl::GetText(size_t startOffset, size_t endOff { return {}; } - + if(IsHiddenInput(self)) + { + return std::string(endOffset - startOffset, GetSubstituteCharacter(self)); + } return text.substr(startOffset, endOffset - startOffset); } @@ -1156,11 +1186,18 @@ Dali::Accessibility::Range TextField::AccessibleImpl::GetTextAtOffset( size_t offset, Dali::Accessibility::TextBoundary boundary) { auto self = Toolkit::TextField::DownCast(Self()); + auto range = Dali::Accessibility::Range{}; + + if(IsHiddenInput(self)) + { + // Returning empty object, as there is no possibility to parse the textfield + // when its content is hidden. + return range; + } + auto text = self.GetProperty(Toolkit::TextField::Property::TEXT).Get(); auto textSize = text.size(); - auto range = Dali::Accessibility::Range{}; - switch(boundary) { case Dali::Accessibility::TextBoundary::CHARACTER: @@ -1252,13 +1289,21 @@ Dali::Accessibility::Range TextField::AccessibleImpl::GetRangeOfSelection(size_t return {}; } - auto self = Toolkit::TextField::DownCast(Self()); - auto controller = Dali::Toolkit::GetImpl(self).GetTextController(); - std::string value{}; - controller->RetrieveSelection(value); + auto self = Toolkit::TextField::DownCast(Self()); + auto controller = Dali::Toolkit::GetImpl(self).GetTextController(); auto indices = controller->GetSelectionIndexes(); - return {static_cast(indices.first), static_cast(indices.second), value}; + auto startOffset = static_cast(indices.first); + auto endOffset = static_cast(indices.second); + + if (IsHiddenInput(self)) + { + return {startOffset, endOffset, std::string(endOffset - startOffset, GetSubstituteCharacter(self))}; + } + + std::string value{}; + controller->RetrieveSelection(value); + return {startOffset, endOffset, value}; } bool TextField::AccessibleImpl::RemoveSelection(size_t selectionIndex) diff --git a/dali-toolkit/internal/controls/text-controls/text-field-property-handler.cpp b/dali-toolkit/internal/controls/text-controls/text-field-property-handler.cpp index 04a5082..db08d3ae 100644 --- a/dali-toolkit/internal/controls/text-controls/text-field-property-handler.cpp +++ b/dali-toolkit/internal/controls/text-controls/text-field-property-handler.cpp @@ -507,6 +507,15 @@ void TextField::PropertyHandler::SetProperty(Toolkit::TextField textField, Prope if(map) { impl.mController->SetHiddenInputOption(*map); + auto mode = map->Find(Toolkit::HiddenInput::Property::MODE); + if(mode && (mode->Get() != Toolkit::HiddenInput::Mode::HIDE_NONE)) + { + textField.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_ROLE, Accessibility::Role::PASSWORD_TEXT); + } + else + { + textField.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_ROLE, Accessibility::Role::ENTRY); + } } break; } diff --git a/dali-toolkit/internal/file.list b/dali-toolkit/internal/file.list index 3229006..855df54 100644 --- a/dali-toolkit/internal/file.list +++ b/dali-toolkit/internal/file.list @@ -93,6 +93,7 @@ SET( toolkit_src_files ${toolkit_src_dir}/controls/scrollable/scroll-view/scroll-view-effect-impl.cpp ${toolkit_src_dir}/controls/scrollable/scroll-view/scroll-view-impl.cpp ${toolkit_src_dir}/controls/scrollable/scroll-view/scroll-view-impl-constraints.cpp + ${toolkit_src_dir}/controls/scrollable/scroll-view/scroll-view-impl-property-handler.cpp ${toolkit_src_dir}/controls/scrollable/scroll-view/scroll-view-page-path-effect-impl.cpp ${toolkit_src_dir}/controls/scene3d-view/scene3d-view-impl.cpp ${toolkit_src_dir}/controls/scene3d-view/gltf-loader.cpp @@ -158,9 +159,11 @@ SET( toolkit_src_files ${toolkit_src_dir}/text/text-controller.cpp ${toolkit_src_dir}/text/text-controller-event-handler.cpp ${toolkit_src_dir}/text/text-controller-impl.cpp + ${toolkit_src_dir}/text/text-controller-impl-data-clearer.cpp ${toolkit_src_dir}/text/text-controller-impl-event-handler.cpp ${toolkit_src_dir}/text/text-controller-impl-model-updater.cpp ${toolkit_src_dir}/text/text-controller-input-font-handler.cpp + ${toolkit_src_dir}/text/text-controller-input-properties.cpp ${toolkit_src_dir}/text/text-controller-placeholder-handler.cpp ${toolkit_src_dir}/text/text-controller-relayouter.cpp ${toolkit_src_dir}/text/text-controller-text-updater.cpp @@ -202,6 +205,7 @@ SET( toolkit_src_files ${toolkit_src_dir}/transition-effects/cube-transition-wave-effect-impl.cpp ${toolkit_src_dir}/text/xhtml-entities.cpp ${toolkit_src_dir}/drag-drop-detector/drag-and-drop-detector-impl.cpp + ${toolkit_src_dir}/text/emoji-helper.cpp ) SET( SOURCES ${SOURCES} diff --git a/dali-toolkit/internal/text/emoji-helper.cpp b/dali-toolkit/internal/text/emoji-helper.cpp new file mode 100644 index 0000000..eec4ee4 --- /dev/null +++ b/dali-toolkit/internal/text/emoji-helper.cpp @@ -0,0 +1,224 @@ + +/* + * Copyright (c) 2021 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. + * + */ + +// FILE HEADER +#include + +namespace Dali +{ +namespace Toolkit +{ +namespace Text +{ +bool IsTextPresentationSequence(const TextAbstraction::Script& currentRunScript, + const TextAbstraction::Character& character) +{ + return (IsSymbolOrEmojiOrTextScript(currentRunScript) && TextAbstraction::IsTextPresentationSelector(character)); +} + +bool IsEmojiPresentationSequence(const TextAbstraction::Script& currentRunScript, + const TextAbstraction::Character& character) +{ + return ((IsSymbolOrEmojiScript(currentRunScript) || IsEmojiColorScript(currentRunScript)) && + TextAbstraction::IsEmojiPresentationSelector(character)); +} + +bool IsEmojiSequence(const TextAbstraction::Script& currentRunScript, + const TextAbstraction::Character& character, + const TextAbstraction::Script& characterScript) +{ + return (IsOneOfEmojiScripts(currentRunScript) && + (IsOneOfEmojiScripts(characterScript) || + TextAbstraction::IsZeroWidthJoiner(character) || + TextAbstraction::IsZeroWidthNonJoiner(character) || + TextAbstraction::IsEmojiItem(character) || + TextAbstraction::IsMiscellaneousSymbolsAndArrowsEmoji(character) || + TextAbstraction::IsDingbatsEmoji(character))); +} + +bool IsNewSequence(const Character* const textBuffer, + const TextAbstraction::Script& currentRunScript, + const Length& currentCharacterIndex, + const Length& lastCharacterIndex, + TextAbstraction::Script& currentCharacterScript) +{ + // Until now we have two cases : VariationSelector & Keycap + // In-case there are more cases then should be added in this function + + return IsNewKeycapSequence(textBuffer, currentCharacterIndex, lastCharacterIndex, currentCharacterScript) || + IsNewVariationSelectorSequence(textBuffer, currentRunScript, currentCharacterIndex, lastCharacterIndex, currentCharacterScript); +} + +// + +bool IsNewKeycapSequence(const Character* const textBuffer, + const Length& currentCharacterIndex, + const Length& lastCharacterIndex, + TextAbstraction::Script& currentCharacterScript) +{ + // Ref: https://www.unicode.org/Public/emoji/14.0/emoji-sequences.txt Search on "Emoji_Keycap_Sequence" + // Ref: https://www.unicode.org/Public/emoji/14.0/emoji-test.txt Search on "subgroup: keycap" + + // Default initialization does not keycap sequence + bool isNewKeycapSequence = false; + + if(currentCharacterIndex < lastCharacterIndex) + { + Character currentCharacter = *(textBuffer + currentCharacterIndex); + if(IsStartForKeycapSequence(currentCharacter)) + { + if(!isNewKeycapSequence && currentCharacterIndex + 2 <= lastCharacterIndex) + { + Character characterOne = *(textBuffer + currentCharacterIndex + 1); + Character characterTwo = *(textBuffer + currentCharacterIndex + 2); + + if(TextAbstraction::IsEmojiPresentationSelector(characterOne) && + TextAbstraction::IsCombiningEnclosingKeycap(characterTwo)) + { + isNewKeycapSequence = true; + currentCharacterScript = TextAbstraction::EMOJI_COLOR; + } + } // if(!isNewKeycapSequence && currentCharacterIndex + 2 <= lastCharacterIndex) + } // if(IsStartForKeycapSequence(currentCharacter)) + } // if(currentCharacterIndex < lastCharacterIndex) + + return isNewKeycapSequence; +} + +bool IsNewVariationSelectorSequence(const Character* const textBuffer, + const TextAbstraction::Script& currentRunScript, + const Length& currentCharacterIndex, + const Length& lastCharacterIndex, + TextAbstraction::Script& currentCharacterScript) +{ + // Ref: Emoji and Text Presentation Selectors: https://www.unicode.org/reports/tr51/#Emoji_Variation_Selectors + // Ref: Emoji Variation Sequences for UTS #51: https://www.unicode.org/Public/14.0.0/ucd/emoji/emoji-variation-sequences.txt + + // Default initialization does not VariationSelector sequence + bool isNewVariationSelectorSequence = false; + + if(currentCharacterIndex < lastCharacterIndex) + { + Character currentCharacter = *(textBuffer + currentCharacterIndex); + if(TextAbstraction::IsEmojiVariationSequences(currentCharacter)) + { + if(!isNewVariationSelectorSequence && currentCharacterIndex + 1 <= lastCharacterIndex) + { + Character characterVS = *(textBuffer + currentCharacterIndex + 1); + + if(TextAbstraction::IsEmojiPresentationSelector(characterVS)) + { + isNewVariationSelectorSequence = currentRunScript != TextAbstraction::EMOJI_COLOR; + currentCharacterScript = TextAbstraction::EMOJI_COLOR; + } + else if(TextAbstraction::IsTextPresentationSelector(characterVS)) + { + isNewVariationSelectorSequence = currentRunScript != TextAbstraction::EMOJI_TEXT; + currentCharacterScript = TextAbstraction::EMOJI_TEXT; + } + + } // if(!isNewVariationSelectorSequence && currentCharacterIndex + 1 <= lastCharacterIndex) + } // if(TextAbstraction::IsEmojiVariationSequences(currentCharacter)) + } // if(currentCharacterIndex < lastCharacterIndex) + + return isNewVariationSelectorSequence; +} + +bool IsStartForKeycapSequence(const TextAbstraction::Character& character) +{ + return (TextAbstraction::IsASCIIDigits(character) || + TextAbstraction::CHAR_NUMBER_SIGN == character || + TextAbstraction::CHAR_ASTERISK == character); +} + +bool IsScriptChangedToFollowSequence(const TextAbstraction::Script& currentRunScript, + const TextAbstraction::Character& character, + TextAbstraction::Script& currentCharacterScript) +{ + bool isUpdated = false; + + // Keycap cases + if(TextAbstraction::IsCombiningEnclosingKeycap(character)) + { + if(TextAbstraction::EMOJI == currentRunScript) + { + // Keycap and unqualified + // Emoji request a default presentation for an emoji character. + isUpdated = (currentCharacterScript != TextAbstraction::EMOJI); + currentCharacterScript = TextAbstraction::EMOJI; + } + else if(TextAbstraction::EMOJI_COLOR == currentRunScript) + { + // Keycap and fully-qualified + // Emoji request an emoji presentation for an emoji character. + isUpdated = (currentCharacterScript != TextAbstraction::EMOJI_COLOR); + currentCharacterScript = TextAbstraction::EMOJI_COLOR; + } + } + // Emoji(Text) Presentation cases + else if(IsTextPresentationSequence(currentRunScript, character)) + { + // Emoji request a text presentation for an emoji character. + isUpdated = (currentCharacterScript != TextAbstraction::EMOJI_TEXT); + currentCharacterScript = TextAbstraction::EMOJI_TEXT; + } + // Emoji(Color) Presentation cases + else if(IsEmojiPresentationSequence(currentRunScript, character)) + { + // Emoji request an emoji presentation for an emoji character. + isUpdated = (currentCharacterScript != TextAbstraction::EMOJI_COLOR); + currentCharacterScript = TextAbstraction::EMOJI_COLOR; + } + // Default Emoji + else if(IsEmojiScript(currentRunScript) && IsEmojiScript(currentCharacterScript)) + { + // Emoji request an emoji presentation for an emoji character. + isUpdated = (currentCharacterScript != TextAbstraction::EMOJI); + currentCharacterScript = TextAbstraction::EMOJI; + } + // Emoji sequences + else if(IsEmojiSequence(currentRunScript, character, currentCharacterScript)) + { + // Emoji request an emoji presentation for an emoji character. + isUpdated = (currentCharacterScript != TextAbstraction::EMOJI_COLOR); + currentCharacterScript = TextAbstraction::EMOJI_COLOR; + } + + return isUpdated; +} + +Character GetVariationSelectorByScript(const TextAbstraction::Script& script) +{ + Character character = 0u; + if(TextAbstraction::EMOJI_COLOR == script) + { + character = TextAbstraction::CHAR_VARIATION_SELECTOR_16; + } + else if(TextAbstraction::EMOJI_TEXT == script) + { + character = TextAbstraction::CHAR_VARIATION_SELECTOR_15; + } + + return character; +} + +} // namespace Text + +} // namespace Toolkit + +} // namespace Dali \ No newline at end of file diff --git a/dali-toolkit/internal/text/emoji-helper.h b/dali-toolkit/internal/text/emoji-helper.h new file mode 100644 index 0000000..95efd9f --- /dev/null +++ b/dali-toolkit/internal/text/emoji-helper.h @@ -0,0 +1,170 @@ +#ifndef DALI_TOOLKIT_TEXT_EMOJI_HELPER_H +#define DALI_TOOLKIT_TEXT_EMOJI_HELPER_H + +/* + * Copyright (c) 2021 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. + * + */ + +// INTERNAL INCLUDES +#include + +// EXTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ +namespace Toolkit +{ +namespace Text +{ +/** + * @brief Whether the sequence is a variation sequence consisting of an emoji character followed by a text presentation selector. + * + * @param[in] currentRunScript The script of current run. + * @param[in] character The next character. + * + * @return @e true if the sequence is text presentation sequence. + */ +bool IsTextPresentationSequence(const TextAbstraction::Script& currentRunScript, const TextAbstraction::Character& character); + +/** + * @brief Whether the sequence is a variation sequence consisting of an emoji character followed by a emoji presentation selector. + * + * @param[in] currentRunScript The script of current run. + * @param[in] character The next character. + * + * @return @e true if the sequence is emoji presentation sequence. + */ +bool IsEmojiPresentationSequence(const TextAbstraction::Script& currentRunScript, + const TextAbstraction::Character& character); + +/** + * @brief Whether the sequence is an emoji sequence. + * + * @param[in] currentRunScript The script of current run. + * @param[in] character The next character. + * @param[in] characterScript The script of the next character. + * + * @return @e true if the sequence is an emoji sequence. + */ +bool IsEmojiSequence(const TextAbstraction::Script& currentRunScript, + const TextAbstraction::Character& character, + const TextAbstraction::Script& characterScript); + +/** + * @brief Whether the sequence is a keycap sequence and set script according to the case. + * + * @param[in] textBuffer The text. + * @param[in] currentCharacterIndex The index of current character. + * @param[in] lastCharacterIndex The index of last character. + * @param[out] currentCharacterScript The current character script to update it in-case it's the Keycap sequence. + * + * @return @e true if @p currentRunScript is ASCII_DIGITS and @p character is COMBINING_ENCLOSING_KEYCAP + */ +bool IsNewKeycapSequence(const Character* const textBuffer, + const Length& currentCharacterIndex, + const Length& lastCharacterIndex, + TextAbstraction::Script& currentCharacterScript); + +/** + * @brief Whether the sequence is a variation selector sequence and set script according to the case. + * + * @param[in] textBuffer The text. + * @param[in] currentRunScript The script of current run. + * @param[in] currentCharacterIndex The index of current character. + * @param[in] lastCharacterIndex The index of last character. + * @param[out] currentCharacterScript The current character script to update it in-case it's the VariationSelector sequence. + * + * @return @e true if @p currentRunScript is ASCII_DIGITS and @p character is COMBINING_ENCLOSING_KEYCAP + */ +bool IsNewVariationSelectorSequence(const Character* const textBuffer, + const TextAbstraction::Script& currentRunScript, + const Length& currentCharacterIndex, + const Length& lastCharacterIndex, + TextAbstraction::Script& currentCharacterScript); + +/** + * @brief Whether the case is a new sequence and set script according to the case. + * + * @param[in] textBuffer The text. + * @param[in] currentRunScript The script of current run. + * @param[in] currentCharacterIndex The index of current character. + * @param[in] lastCharacterIndex The index of last character. + * @param[out] currentCharacterScript The current character script to update according to sequence. + * + * @return @e true the case is a new sequence + */ +bool IsNewSequence(const Character* const textBuffer, + const TextAbstraction::Script& currentRunScript, + const Length& currentCharacterIndex, + const Length& lastCharacterIndex, + TextAbstraction::Script& currentCharacterScript); + +/** + * @brief Whether the character is ASCII digits | # Number Sign | * Asterisk. + * + * @param[in] character The character. + * + * @return @e true if the character is ASCII digits | # Number Sign | * Asterisk. + */ +bool IsStartForKeycapSequence(const TextAbstraction::Character& character); + +/** + * @brief Check sequence case and update script of character if needed. + * + * @param[in] currentRunScript The script of current run. + * @param[in] character The next character. + * @param[out] script The current character script to update according to sequence. + * + * @return @e true if the script is changed + */ +bool IsScriptChangedToFollowSequence(const TextAbstraction::Script& currentRunScript, + const TextAbstraction::Character& character, + TextAbstraction::Script& script); + +/** + * @brief Check sequence case and update script of character if needed. + * + * @param[in] currentRunScript The script of current run. + * @param[in] character The next character. + * @param[out] script The current character script to update according to sequence. + * + * @return @e true if the script is changed + */ +bool HandleEmojiSequence(const Character* const textBuffer, + const Length& currentCharacterIndex, + const Length& lastCharacterIndex, + const TextAbstraction::Script& currentRunScript, + TextAbstraction::Script& currentCharacterScript); + +/** + * @brief Determine the Variation Selector according to script. + * + * @param[in] script The script. + * + * @return CHAR_VARIATION_SELECTOR_15 in-case EMOJI_TEXT or CHAR_VARIATION_SELECTOR_16 in-case EMOJI_COLOR or 0 otherwise + */ +Character GetVariationSelectorByScript(const TextAbstraction::Script& script); + +} // namespace Text + +} // namespace Toolkit + +} // namespace Dali + +#endif // DALI_TOOLKIT_TEXT_EMOJI_HELPER_H \ No newline at end of file diff --git a/dali-toolkit/internal/text/hidden-text.cpp b/dali-toolkit/internal/text/hidden-text.cpp index 1adbe83..ab2aba5 100644 --- a/dali-toolkit/internal/text/hidden-text.cpp +++ b/dali-toolkit/internal/text/hidden-text.cpp @@ -23,7 +23,6 @@ using namespace Dali::Toolkit; -const uint32_t STAR = 0x2A; // Set default substitute character as '*' const int DEFAULT_SHOW_DURATION = 1000; namespace Dali diff --git a/dali-toolkit/internal/text/hidden-text.h b/dali-toolkit/internal/text/hidden-text.h index 70d4a82..b13c263 100644 --- a/dali-toolkit/internal/text/hidden-text.h +++ b/dali-toolkit/internal/text/hidden-text.h @@ -32,6 +32,9 @@ namespace Toolkit { namespace Text { + +static constexpr const uint32_t STAR = 0x2A; // Set default substitute character as '*' + /** * Class to handle the hidden text */ diff --git a/dali-toolkit/internal/text/multi-language-support-impl.cpp b/dali-toolkit/internal/text/multi-language-support-impl.cpp index 82470f8..dbd110b 100644 --- a/dali-toolkit/internal/text/multi-language-support-impl.cpp +++ b/dali-toolkit/internal/text/multi-language-support-impl.cpp @@ -24,6 +24,7 @@ #include // INTERNAL INCLUDES +#include #include namespace Dali @@ -97,11 +98,11 @@ MultilanguageSupport::MultilanguageSupport() { // 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. - mDefaultFontPerScriptCache.Resize(TextAbstraction::UNKNOWN + 1, NULL); + mDefaultFontPerScriptCache.Resize(TextAbstraction::GetNumberOfScripts(), NULL); // Initializes the valid fonts cache to NULL (no valid fonts). // Reserves space to cache the valid fonts and access them with the script as an index. - mValidFontsPerScriptCache.Resize(TextAbstraction::UNKNOWN + 1, NULL); + mValidFontsPerScriptCache.Resize(TextAbstraction::GetNumberOfScripts(), NULL); } MultilanguageSupport::~MultilanguageSupport() @@ -205,6 +206,7 @@ void MultilanguageSupport::SetScripts(const Vector& text, // Traverse all characters and set the scripts. const Length lastCharacter = startIndex + numberOfCharacters; + for(Length index = startIndex; index < lastCharacter; ++index) { Character character = *(textBuffer + index); @@ -222,26 +224,40 @@ void MultilanguageSupport::SetScripts(const Vector& text, // Skip those characters valid for many scripts like white spaces or '\n'. bool endOfText = index == lastCharacter; + + //Handle all Emoji Sequence cases + if(IsNewSequence(textBuffer, currentScriptRun.script, index, lastCharacter, script)) + { + AddCurrentScriptAndCreatNewScript(script, + false, + false, + currentScriptRun, + numberOfAllScriptCharacters, + scripts, + scriptIndex); + } + else if(IsScriptChangedToFollowSequence(currentScriptRun.script, character, script)) + { + currentScriptRun.script = script; + } + else if(IsOneOfEmojiScripts(currentScriptRun.script) && (TextAbstraction::COMMON == script)) + { + // Emojis doesn't mix well with characters common to all scripts. Insert the emoji run. + AddCurrentScriptAndCreatNewScript(TextAbstraction::UNKNOWN, + false, + false, + currentScriptRun, + numberOfAllScriptCharacters, + scripts, + scriptIndex); + } + while(!endOfText && (TextAbstraction::COMMON == script)) { // Check if whether is right to left markup and Keeps true if the previous value was true. currentScriptRun.isRightToLeft = currentScriptRun.isRightToLeft || TextAbstraction::IsRightToLeftMark(character); - // ZWJ, ZWNJ between emojis should be treated as EMOJI. - if(TextAbstraction::EMOJI == currentScriptRun.script && !(TextAbstraction::IsZeroWidthJoiner(character) || TextAbstraction::IsZeroWidthNonJoiner(character))) - { - // Emojis doesn't mix well with characters common to all scripts. Insert the emoji run. - scripts.Insert(scripts.Begin() + scriptIndex, currentScriptRun); - ++scriptIndex; - - // Initialize the new one. - currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters; - currentScriptRun.characterRun.numberOfCharacters = 0u; - currentScriptRun.script = TextAbstraction::UNKNOWN; - numberOfAllScriptCharacters = 0u; - } - // Count all these characters to be added into a script. ++numberOfAllScriptCharacters; @@ -253,20 +269,13 @@ void MultilanguageSupport::SetScripts(const Vector& text, // the same direction than the first script of the paragraph. isFirstScriptToBeSet = true; - // Characters common to all scripts at the end of the paragraph are added to the last script. - currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; - - // Store the script run. - scripts.Insert(scripts.Begin() + scriptIndex, currentScriptRun); - ++scriptIndex; - - // Initialize the new one. - currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters; - currentScriptRun.characterRun.numberOfCharacters = 0u; - currentScriptRun.script = TextAbstraction::UNKNOWN; - numberOfAllScriptCharacters = 0u; - // Initialize whether is right to left direction - currentScriptRun.isRightToLeft = false; + AddCurrentScriptAndCreatNewScript(TextAbstraction::UNKNOWN, + false, + false, + currentScriptRun, + numberOfAllScriptCharacters, + scripts, + scriptIndex); } // Get the next character. @@ -276,6 +285,22 @@ void MultilanguageSupport::SetScripts(const Vector& text, { character = *(textBuffer + index); script = TextAbstraction::GetCharacterScript(character); + + //Handle all Emoji Sequence cases + if(IsNewSequence(textBuffer, currentScriptRun.script, index, lastCharacter, script)) + { + AddCurrentScriptAndCreatNewScript(script, + false, + false, + currentScriptRun, + numberOfAllScriptCharacters, + scripts, + scriptIndex); + } + else if(IsScriptChangedToFollowSequence(currentScriptRun.script, character, script)) + { + currentScriptRun.script = script; + } } } // end while( !endOfText && ( TextAbstraction::COMMON == script ) ) @@ -290,7 +315,10 @@ void MultilanguageSupport::SetScripts(const Vector& text, if(isFirstScriptToBeSet && (TextAbstraction::UNKNOWN != script) && (TextAbstraction::COMMON != script) && - (TextAbstraction::EMOJI != script)) + (TextAbstraction::EMOJI != script) && + (TextAbstraction::EMOJI_TEXT != script) && + (TextAbstraction::EMOJI_COLOR != script) && + (!TextAbstraction::IsSymbolScript(script))) { // Sets the direction of the first valid script. isParagraphRTL = currentScriptRun.isRightToLeft || TextAbstraction::IsRightToLeftScript(script); @@ -319,26 +347,21 @@ void MultilanguageSupport::SetScripts(const Vector& text, numberOfAllScriptCharacters = 0u; } else if((TextAbstraction::UNKNOWN == currentScriptRun.script) && - (TextAbstraction::EMOJI == script)) + (TextAbstraction::IsSymbolOrEmojiOrTextScript(script))) { currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; numberOfAllScriptCharacters = 0u; } - if(0u != currentScriptRun.characterRun.numberOfCharacters) - { - // Store the script run. - scripts.Insert(scripts.Begin() + scriptIndex, currentScriptRun); - ++scriptIndex; - } - - // Initialize the new one. - currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters; - currentScriptRun.characterRun.numberOfCharacters = numberOfAllScriptCharacters + 1u; // Adds the white spaces which are at the begining of the script. - currentScriptRun.script = script; - numberOfAllScriptCharacters = 0u; - // Check if whether is right to left script. - currentScriptRun.isRightToLeft = TextAbstraction::IsRightToLeftScript(currentScriptRun.script); + // Adds the white spaces which are at the begining of the script. + numberOfAllScriptCharacters++; + AddCurrentScriptAndCreatNewScript(script, + TextAbstraction::IsRightToLeftScript(script), + true, + currentScriptRun, + numberOfAllScriptCharacters, + scripts, + scriptIndex); } else { @@ -445,8 +468,8 @@ void MultilanguageSupport::ValidateFonts(const Vector& Vector::ConstIterator scriptRunEndIt = scripts.End(); bool isNewParagraphCharacter = false; - bool isPreviousEmojiScript = false; - FontId previousEmojiFontId = 0u; + FontId previousEmojiFontId = 0u; + TextAbstraction::Script previousScript = TextAbstraction::UNKNOWN; CharacterIndex lastCharacter = startIndex + numberOfCharacters; for(Length index = startIndex; index < lastCharacter; ++index) @@ -518,28 +541,11 @@ void MultilanguageSupport::ValidateFonts(const Vector& } bool isCommonScript = false; - bool isEmojiScript = TextAbstraction::EMOJI == script; - - if(isEmojiScript && !isPreviousEmojiScript) - { - if(0u != currentFontRun.characterRun.numberOfCharacters) - { - // Store the font run. - fonts.Insert(fonts.Begin() + fontIndex, currentFontRun); - ++fontIndex; - } - - // Initialize the new one. - currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters; - currentFontRun.characterRun.numberOfCharacters = 0u; - currentFontRun.fontId = fontId; - currentFontRun.isItalicRequired = false; - currentFontRun.isBoldRequired = false; - } + bool isEmojiScript = TextAbstraction::IsEmojiScript(script) || TextAbstraction::IsEmojiColorScript(script) || TextAbstraction::IsEmojiTextScript(script); - // ZWJ, ZWNJ between emojis should use the previous emoji font. - if(isEmojiScript && (TextAbstraction::IsZeroWidthJoiner(character) || TextAbstraction::IsZeroWidthNonJoiner(character))) + if(isEmojiScript && (previousScript == script)) { + // Emoji sequence should use the previous emoji font. if(0u != previousEmojiFontId) { fontId = previousEmojiFontId; @@ -567,7 +573,7 @@ void MultilanguageSupport::ValidateFonts(const Vector& // // Many fonts support 'white spaces' so probably the font set by the user or the platform's default // supports the 'white space'. However, that font may not support the DEVANAGARI script. - isCommonScript = TextAbstraction::IsCommonScript(character); + isCommonScript = TextAbstraction::IsCommonScript(character) || TextAbstraction::IsEmojiPresentationSelector(character); // Check in the valid fonts cache. ValidateFontsPerScript* validateFontsPerScript = *(validFontsPerScriptCacheBuffer + script); @@ -659,14 +665,55 @@ void MultilanguageSupport::ValidateFonts(const Vector& } // !isValidFont (2) } // !isValidFont (1) + if(isEmojiScript && (previousScript != script)) + { + //New Emoji sequence should select font according to the variation selector (VS15 or VS16). + if(0u != currentFontRun.characterRun.numberOfCharacters) + { + // Store the font run. + fonts.Insert(fonts.Begin() + fontIndex, currentFontRun); + ++fontIndex; + } + + // Initialize the new one. + currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters; + currentFontRun.characterRun.numberOfCharacters = 0u; + currentFontRun.fontId = fontId; + currentFontRun.isItalicRequired = false; + currentFontRun.isBoldRequired = false; + + if(TextAbstraction::IsEmojiColorScript(script) || TextAbstraction::IsEmojiTextScript(script)) + { + bool isModifiedByVariationSelector = false; + GlyphIndex glyphIndexChar = fontClient.GetGlyphIndex(fontId, character); + GlyphIndex glyphIndexCharByVS = fontClient.GetGlyphIndex(fontId, character, Text::GetVariationSelectorByScript(script)); + + isModifiedByVariationSelector = glyphIndexChar != glyphIndexCharByVS; + + if(isModifiedByVariationSelector) + { + FontId requestedFontId = fontClient.FindDefaultFont(character, currentFontPointSize, IsEmojiColorScript(script)); + if(0u != requestedFontId) + { + currentFontRun.fontId = fontId = requestedFontId; + isValidFont = true; + } + } + } + } + // Store the font id when the first character is an emoji. - if(isEmojiScript && !isPreviousEmojiScript) + if(isEmojiScript) { - if(0u != fontId) + if(0u != fontId && previousScript != script) { previousEmojiFontId = fontId; } } + else + { + previousEmojiFontId = 0u; + } #ifdef DEBUG_ENABLED { @@ -715,7 +762,7 @@ void MultilanguageSupport::ValidateFonts(const Vector& // Whether the current character is a new paragraph character. isNewParagraphCharacter = TextAbstraction::IsNewParagraph(character); - isPreviousEmojiScript = isEmojiScript; + previousScript = script; } // end traverse characters. if(0u != currentFontRun.characterRun.numberOfCharacters) @@ -746,6 +793,34 @@ void MultilanguageSupport::ValidateFonts(const Vector& DALI_LOG_INFO(gLogFilter, Debug::General, "<--MultilanguageSupport::ValidateFonts\n"); } +void MultilanguageSupport::AddCurrentScriptAndCreatNewScript(const Script requestedScript, + const bool isRightToLeft, + const bool addScriptCharactersToNewScript, + ScriptRun& currentScriptRun, + Length& numberOfAllScriptCharacters, + Vector& scripts, + ScriptRunIndex& scriptIndex) +{ + // Add the pending characters to the current script + currentScriptRun.characterRun.numberOfCharacters += (addScriptCharactersToNewScript ? 0u : numberOfAllScriptCharacters); + + // In-case the current script is empty then no need to add it for scripts + if(0u != currentScriptRun.characterRun.numberOfCharacters) + { + // Store the script run. + scripts.Insert(scripts.Begin() + scriptIndex, currentScriptRun); + ++scriptIndex; + } + + // Initialize the new one by the requested script + currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters; + currentScriptRun.characterRun.numberOfCharacters = (addScriptCharactersToNewScript ? numberOfAllScriptCharacters : 0u); + currentScriptRun.script = requestedScript; + numberOfAllScriptCharacters = 0u; + // Initialize whether is right to left direction + currentScriptRun.isRightToLeft = isRightToLeft; +} + } // namespace Internal } // namespace Text diff --git a/dali-toolkit/internal/text/multi-language-support-impl.h b/dali-toolkit/internal/text/multi-language-support-impl.h index de44e19..cda767a 100644 --- a/dali-toolkit/internal/text/multi-language-support-impl.h +++ b/dali-toolkit/internal/text/multi-language-support-impl.h @@ -160,6 +160,28 @@ public: private: Vector mDefaultFontPerScriptCache; ///< Caches default fonts for a script. Vector mValidFontsPerScriptCache; ///< Caches valid fonts for a script. + + //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. + * + */ + void AddCurrentScriptAndCreatNewScript(const Script requestedScript, + const bool isRightToLeft, + const bool addScriptCharactersToNewScript, + ScriptRun& currentScriptRun, + Length& numberOfAllScriptCharacters, + Vector& scripts, + ScriptRunIndex& scriptIndex); }; } // namespace Internal diff --git a/dali-toolkit/internal/text/text-controller-impl-data-clearer.cpp b/dali-toolkit/internal/text/text-controller-impl-data-clearer.cpp new file mode 100644 index 0000000..87099da --- /dev/null +++ b/dali-toolkit/internal/text/text-controller-impl-data-clearer.cpp @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2021 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. + * + */ + +// CLASS HEADER +#include + +// INTERNAL INCLUDES +#include +#include + +namespace Dali::Toolkit::Text +{ + +void ControllerImplDataClearer::ClearFullModelData(Controller::Impl& impl, Controller::OperationsMask operations) +{ + ModelPtr& model = impl.mModel; + + if(Controller::NO_OPERATION != (Controller::GET_LINE_BREAKS & operations)) + { + model->mLogicalModel->mLineBreakInfo.Clear(); + model->mLogicalModel->mParagraphInfo.Clear(); + } + + if(Controller::NO_OPERATION != (Controller::GET_SCRIPTS & operations)) + { + model->mLogicalModel->mScriptRuns.Clear(); + } + + if(Controller::NO_OPERATION != (Controller::VALIDATE_FONTS & operations)) + { + model->mLogicalModel->mFontRuns.Clear(); + } + + if(0u != model->mLogicalModel->mBidirectionalParagraphInfo.Count()) + { + if(Controller::NO_OPERATION != (Controller::BIDI_INFO & operations)) + { + model->mLogicalModel->mBidirectionalParagraphInfo.Clear(); + model->mLogicalModel->mCharacterDirections.Clear(); + } + + if(Controller::NO_OPERATION != (Controller::REORDER & operations)) + { + // Free the allocated memory used to store the conversion table in the bidirectional line info run. + for(Vector::Iterator it = model->mLogicalModel->mBidirectionalLineInfo.Begin(), + endIt = model->mLogicalModel->mBidirectionalLineInfo.End(); + it != endIt; + ++it) + { + BidirectionalLineInfoRun& bidiLineInfo = *it; + + free(bidiLineInfo.visualToLogicalMap); + bidiLineInfo.visualToLogicalMap = NULL; + } + model->mLogicalModel->mBidirectionalLineInfo.Clear(); + } + } + + if(Controller::NO_OPERATION != (Controller::SHAPE_TEXT & operations)) + { + model->mVisualModel->mGlyphs.Clear(); + model->mVisualModel->mGlyphsToCharacters.Clear(); + model->mVisualModel->mCharactersToGlyph.Clear(); + model->mVisualModel->mCharactersPerGlyph.Clear(); + model->mVisualModel->mGlyphsPerCharacter.Clear(); + model->mVisualModel->mGlyphPositions.Clear(); + } + + if(Controller::NO_OPERATION != (Controller::LAYOUT & operations)) + { + model->mVisualModel->mLines.Clear(); + } + + if(Controller::NO_OPERATION != (Controller::COLOR & operations)) + { + model->mVisualModel->mColorIndices.Clear(); + model->mVisualModel->mBackgroundColorIndices.Clear(); + } +} + +void ControllerImplDataClearer::ClearCharacterModelData(Controller::Impl& impl, CharacterIndex startIndex, CharacterIndex endIndex, Controller::OperationsMask operations) +{ + const CharacterIndex endIndexPlusOne = endIndex + 1u; + ModelPtr& model = impl.mModel; + + if(Controller::NO_OPERATION != (Controller::GET_LINE_BREAKS & operations)) + { + // Clear the line break info. + LineBreakInfo* lineBreakInfoBuffer = model->mLogicalModel->mLineBreakInfo.Begin(); + + model->mLogicalModel->mLineBreakInfo.Erase(lineBreakInfoBuffer + startIndex, + lineBreakInfoBuffer + endIndexPlusOne); + + // Clear the paragraphs. + ClearCharacterRuns(startIndex, + endIndex, + model->mLogicalModel->mParagraphInfo); + } + + if(Controller::NO_OPERATION != (Controller::GET_SCRIPTS & operations)) + { + // Clear the scripts. + ClearCharacterRuns(startIndex, + endIndex, + model->mLogicalModel->mScriptRuns); + } + + if(Controller::NO_OPERATION != (Controller::VALIDATE_FONTS & operations)) + { + // Clear the fonts. + ClearCharacterRuns(startIndex, + endIndex, + model->mLogicalModel->mFontRuns); + } + + if(0u != model->mLogicalModel->mBidirectionalParagraphInfo.Count()) + { + if(Controller::NO_OPERATION != (Controller::BIDI_INFO & operations)) + { + // Clear the bidirectional paragraph info. + ClearCharacterRuns(startIndex, + endIndex, + model->mLogicalModel->mBidirectionalParagraphInfo); + + // Clear the character's directions. + CharacterDirection* characterDirectionsBuffer = model->mLogicalModel->mCharacterDirections.Begin(); + + model->mLogicalModel->mCharacterDirections.Erase(characterDirectionsBuffer + startIndex, + characterDirectionsBuffer + endIndexPlusOne); + } + + if(Controller::NO_OPERATION != (Controller::REORDER & operations)) + { + uint32_t startRemoveIndex = model->mLogicalModel->mBidirectionalLineInfo.Count(); + uint32_t endRemoveIndex = startRemoveIndex; + ClearCharacterRuns(startIndex, + endIndex, + model->mLogicalModel->mBidirectionalLineInfo, + startRemoveIndex, + endRemoveIndex); + + BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = model->mLogicalModel->mBidirectionalLineInfo.Begin(); + + // Free the allocated memory used to store the conversion table in the bidirectional line info run. + for(Vector::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex, + endIt = bidirectionalLineInfoBuffer + endRemoveIndex; + it != endIt; + ++it) + { + BidirectionalLineInfoRun& bidiLineInfo = *it; + + free(bidiLineInfo.visualToLogicalMap); + bidiLineInfo.visualToLogicalMap = NULL; + } + + model->mLogicalModel->mBidirectionalLineInfo.Erase(bidirectionalLineInfoBuffer + startRemoveIndex, + bidirectionalLineInfoBuffer + endRemoveIndex); + } + } +} + +void ControllerImplDataClearer::ClearGlyphModelData(Controller::Impl& impl, CharacterIndex startIndex, CharacterIndex endIndex, Controller::OperationsMask operations) +{ + const CharacterIndex endIndexPlusOne = endIndex + 1u; + const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex; + ModelPtr& model = impl.mModel; + TextUpdateInfo& textUpdateInfo = impl.mTextUpdateInfo; + + + // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers. + GlyphIndex* charactersToGlyphBuffer = model->mVisualModel->mCharactersToGlyph.Begin(); + Length* glyphsPerCharacterBuffer = model->mVisualModel->mGlyphsPerCharacter.Begin(); + + const GlyphIndex endGlyphIndexPlusOne = *(charactersToGlyphBuffer + endIndex) + *(glyphsPerCharacterBuffer + endIndex); + const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - textUpdateInfo.mStartGlyphIndex; + + if(Controller::NO_OPERATION != (Controller::SHAPE_TEXT & operations)) + { + // Update the character to glyph indices. + for(Vector::Iterator it = charactersToGlyphBuffer + endIndexPlusOne, + endIt = charactersToGlyphBuffer + model->mVisualModel->mCharactersToGlyph.Count(); + it != endIt; + ++it) + { + CharacterIndex& index = *it; + index -= numberOfGlyphsRemoved; + } + + // Clear the character to glyph conversion table. + model->mVisualModel->mCharactersToGlyph.Erase(charactersToGlyphBuffer + startIndex, + charactersToGlyphBuffer + endIndexPlusOne); + + // Clear the glyphs per character table. + model->mVisualModel->mGlyphsPerCharacter.Erase(glyphsPerCharacterBuffer + startIndex, + glyphsPerCharacterBuffer + endIndexPlusOne); + + // Clear the glyphs buffer. + GlyphInfo* glyphsBuffer = model->mVisualModel->mGlyphs.Begin(); + model->mVisualModel->mGlyphs.Erase(glyphsBuffer + textUpdateInfo.mStartGlyphIndex, + glyphsBuffer + endGlyphIndexPlusOne); + + CharacterIndex* glyphsToCharactersBuffer = model->mVisualModel->mGlyphsToCharacters.Begin(); + + // Update the glyph to character indices. + for(Vector::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne, + endIt = glyphsToCharactersBuffer + model->mVisualModel->mGlyphsToCharacters.Count(); + it != endIt; + ++it) + { + CharacterIndex& index = *it; + index -= numberOfCharactersRemoved; + } + + // Clear the glyphs to characters buffer. + model->mVisualModel->mGlyphsToCharacters.Erase(glyphsToCharactersBuffer + textUpdateInfo.mStartGlyphIndex, + glyphsToCharactersBuffer + endGlyphIndexPlusOne); + + // Clear the characters per glyph buffer. + Length* charactersPerGlyphBuffer = model->mVisualModel->mCharactersPerGlyph.Begin(); + model->mVisualModel->mCharactersPerGlyph.Erase(charactersPerGlyphBuffer + textUpdateInfo.mStartGlyphIndex, + charactersPerGlyphBuffer + endGlyphIndexPlusOne); + + // Should pass if mGlyphPositions has already been cleared in Controller::Relayouter::Relayout + if(0u != model->mVisualModel->mGlyphPositions.Count()) + { + // Clear the positions buffer. + Vector2* positionsBuffer = model->mVisualModel->mGlyphPositions.Begin(); + model->mVisualModel->mGlyphPositions.Erase(positionsBuffer + textUpdateInfo.mStartGlyphIndex, + positionsBuffer + endGlyphIndexPlusOne); + } + } + + if(Controller::NO_OPERATION != (Controller::LAYOUT & operations)) + { + // Clear the lines. + uint32_t startRemoveIndex = model->mVisualModel->mLines.Count(); + uint32_t endRemoveIndex = startRemoveIndex; + ClearCharacterRuns(startIndex, + endIndex, + model->mVisualModel->mLines, + startRemoveIndex, + endRemoveIndex); + + // Will update the glyph runs. + startRemoveIndex = model->mVisualModel->mLines.Count(); + endRemoveIndex = startRemoveIndex; + ClearGlyphRuns(textUpdateInfo.mStartGlyphIndex, + endGlyphIndexPlusOne - 1u, + model->mVisualModel->mLines, + startRemoveIndex, + endRemoveIndex); + + // Set the line index from where to insert the new laid-out lines. + textUpdateInfo.mStartLineIndex = startRemoveIndex; + + LineRun* linesBuffer = model->mVisualModel->mLines.Begin(); + model->mVisualModel->mLines.Erase(linesBuffer + startRemoveIndex, + linesBuffer + endRemoveIndex); + } + + if(Controller::NO_OPERATION != (Controller::COLOR & operations)) + { + if(0u != model->mVisualModel->mColorIndices.Count()) + { + ColorIndex* colorIndexBuffer = model->mVisualModel->mColorIndices.Begin(); + model->mVisualModel->mColorIndices.Erase(colorIndexBuffer + textUpdateInfo.mStartGlyphIndex, + colorIndexBuffer + endGlyphIndexPlusOne); + } + + if(0u != model->mVisualModel->mBackgroundColorIndices.Count()) + { + ColorIndex* backgroundColorIndexBuffer = model->mVisualModel->mBackgroundColorIndices.Begin(); + model->mVisualModel->mBackgroundColorIndices.Erase(backgroundColorIndexBuffer + textUpdateInfo.mStartGlyphIndex, + backgroundColorIndexBuffer + endGlyphIndexPlusOne); + } + } +} + +void ControllerImplDataClearer::ClearModelData(Controller::Impl& impl, CharacterIndex startIndex, CharacterIndex endIndex, Controller::OperationsMask operations) +{ + TextUpdateInfo& textUpdateInfo = impl.mTextUpdateInfo; + + if(textUpdateInfo.mClearAll || + ((0u == startIndex) && + (textUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u))) + { + ClearFullModelData(impl, operations); + } + else + { + // Clear the model data related with characters. + ClearCharacterModelData(impl, startIndex, endIndex, operations); + + // Clear the model data related with glyphs. + ClearGlyphModelData(impl, startIndex, endIndex, operations); + } + + ModelPtr& model = impl.mModel; + + // The estimated number of lines. Used to avoid reallocations when layouting. + textUpdateInfo.mEstimatedNumberOfLines = std::max(model->mVisualModel->mLines.Count(), model->mLogicalModel->mParagraphInfo.Count()); + + model->mVisualModel->ClearCaches(); +} + +} // namespace Dali::Toolkit::Text diff --git a/dali-toolkit/internal/text/text-controller-impl-data-clearer.h b/dali-toolkit/internal/text/text-controller-impl-data-clearer.h new file mode 100644 index 0000000..87d6e24 --- /dev/null +++ b/dali-toolkit/internal/text/text-controller-impl-data-clearer.h @@ -0,0 +1,81 @@ +#ifndef DALI_TOOLKIT_TEXT_CONTROLLER_IMPL_DATA_CLEARER_H +#define DALI_TOOLKIT_TEXT_CONTROLLER_IMPL_DATA_CLEARER_H + +/* + * Copyright (c) 2021 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. + * + */ + +// INTERNAL INCLUDES +#include + +namespace Dali::Toolkit::Text +{ + +/// Provides methods to clear some of the model data in the Text::Controller::Impl +struct ControllerImplDataClearer +{ + + /** + * @brief Helper to clear completely the parts of the model specified by the given @p operations. + * + * @note It never clears the text stored in utf32. + * + * @param[in] impl The text controller impl. + * @param[in] operations The operations required. + */ + static void ClearFullModelData(Controller::Impl& impl, Controller::OperationsMask operations); + + /** + * @brief Helper to clear completely the parts of the model related with the characters specified by the given @p operations. + * + * @note It never clears the text stored in utf32. + * + * @param[in] impl The text controller impl. + * @param[in] startIndex Index to the first character to be cleared. + * @param[in] endIndex Index to the last character to be cleared. + * @param[in] operations The operations required. + */ + static void ClearCharacterModelData(Controller::Impl& impl, CharacterIndex startIndex, CharacterIndex endIndex, Controller::OperationsMask operations); + + /** + * @brief Helper to clear completely the parts of the model related with the glyphs specified by the given @p operations. + * + * @note It never clears the text stored in utf32. + * @note Character indices are transformed to glyph indices. + * + * @param[in] impl The text controller impl. + * @param[in] startIndex Index to the first character to be cleared. + * @param[in] endIndex Index to the last character to be cleared. + * @param[in] operations The operations required. + */ + static void ClearGlyphModelData(Controller::Impl& impl, CharacterIndex startIndex, CharacterIndex endIndex, Controller::OperationsMask operations); + + /** + * @brief Helper to clear the parts of the model specified by the given @p operations and from @p startIndex to @p endIndex. + * + * @note It never clears the text stored in utf32. + * + * @param[in] impl The text controller impl. + * @param[in] startIndex Index to the first character to be cleared. + * @param[in] endIndex Index to the last character to be cleared. + * @param[in] operations The operations required. + */ + static void ClearModelData(Controller::Impl& impl, CharacterIndex startIndex, CharacterIndex endIndex, Controller::OperationsMask operations); +}; + +} // namespace Dali::Toolkit::Text + +#endif // DALI_TOOLKIT_TEXT_CONTROLLER_IMPL_DATA_CLEARER_H diff --git a/dali-toolkit/internal/text/text-controller-impl.cpp b/dali-toolkit/internal/text/text-controller-impl.cpp index 2edca46..8a1dd76 100644 --- a/dali-toolkit/internal/text/text-controller-impl.cpp +++ b/dali-toolkit/internal/text/text-controller-impl.cpp @@ -19,8 +19,10 @@ #include // EXTERNAL INCLUDES +#include #include #include +#include // INTERNAL INCLUDES #include @@ -28,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -129,6 +132,243 @@ void SetDefaultInputStyle(InputStyle& inputStyle, const FontDefaults* const font } } } + +void ChangeTextControllerState(Controller::Impl& impl, EventData::State newState) +{ + EventData* eventData = impl.mEventData; + + if(nullptr == eventData) + { + // Nothing to do if there is no text input. + return; + } + + DecoratorPtr& decorator = eventData->mDecorator; + if(!decorator) + { + // Nothing to do if there is no decorator. + return; + } + + DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", eventData->mState, newState); + + if(eventData->mState != newState) + { + eventData->mPreviousState = eventData->mState; + eventData->mState = newState; + + switch(eventData->mState) + { + case EventData::INACTIVE: + { + decorator->SetActiveCursor(ACTIVE_CURSOR_NONE); + decorator->StopCursorBlink(); + decorator->SetHandleActive(GRAB_HANDLE, false); + decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); + decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); + decorator->SetHighlightActive(false); + decorator->SetPopupActive(false); + eventData->mDecoratorUpdated = true; + break; + } + + case EventData::INTERRUPTED: + { + decorator->SetHandleActive(GRAB_HANDLE, false); + decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); + decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); + decorator->SetHighlightActive(false); + decorator->SetPopupActive(false); + eventData->mDecoratorUpdated = true; + break; + } + + case EventData::SELECTING: + { + decorator->SetActiveCursor(ACTIVE_CURSOR_NONE); + decorator->StopCursorBlink(); + decorator->SetHandleActive(GRAB_HANDLE, false); + if(eventData->mGrabHandleEnabled) + { + decorator->SetHandleActive(LEFT_SELECTION_HANDLE, true); + decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true); + } + decorator->SetHighlightActive(true); + if(eventData->mGrabHandlePopupEnabled) + { + impl.SetPopupButtons(); + decorator->SetPopupActive(true); + } + eventData->mDecoratorUpdated = true; + break; + } + + case EventData::EDITING: + { + decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); + if(eventData->mCursorBlinkEnabled) + { + decorator->StartCursorBlink(); + } + // Grab handle is not shown until a tap is received whilst EDITING + decorator->SetHandleActive(GRAB_HANDLE, false); + decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); + decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); + decorator->SetHighlightActive(false); + if(eventData->mGrabHandlePopupEnabled) + { + decorator->SetPopupActive(false); + } + eventData->mDecoratorUpdated = true; + break; + } + case EventData::EDITING_WITH_POPUP: + { + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState); + + decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); + if(eventData->mCursorBlinkEnabled) + { + decorator->StartCursorBlink(); + } + if(eventData->mSelectionEnabled) + { + decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); + decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); + decorator->SetHighlightActive(false); + } + else if(eventData->mGrabHandleEnabled) + { + decorator->SetHandleActive(GRAB_HANDLE, true); + } + if(eventData->mGrabHandlePopupEnabled) + { + impl.SetPopupButtons(); + decorator->SetPopupActive(true); + } + eventData->mDecoratorUpdated = true; + break; + } + case EventData::EDITING_WITH_GRAB_HANDLE: + { + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState); + + decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); + if(eventData->mCursorBlinkEnabled) + { + decorator->StartCursorBlink(); + } + // Grab handle is not shown until a tap is received whilst EDITING + if(eventData->mGrabHandleEnabled) + { + decorator->SetHandleActive(GRAB_HANDLE, true); + } + decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); + decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); + decorator->SetHighlightActive(false); + if(eventData->mGrabHandlePopupEnabled) + { + decorator->SetPopupActive(false); + } + eventData->mDecoratorUpdated = true; + break; + } + + case EventData::SELECTION_HANDLE_PANNING: + { + decorator->SetActiveCursor(ACTIVE_CURSOR_NONE); + decorator->StopCursorBlink(); + decorator->SetHandleActive(GRAB_HANDLE, false); + if(eventData->mGrabHandleEnabled) + { + decorator->SetHandleActive(LEFT_SELECTION_HANDLE, true); + decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true); + } + decorator->SetHighlightActive(true); + if(eventData->mGrabHandlePopupEnabled) + { + decorator->SetPopupActive(false); + } + eventData->mDecoratorUpdated = true; + break; + } + + case EventData::GRAB_HANDLE_PANNING: + { + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState); + + decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); + if(eventData->mCursorBlinkEnabled) + { + decorator->StartCursorBlink(); + } + if(eventData->mGrabHandleEnabled) + { + decorator->SetHandleActive(GRAB_HANDLE, true); + } + decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); + decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); + decorator->SetHighlightActive(false); + if(eventData->mGrabHandlePopupEnabled) + { + decorator->SetPopupActive(false); + } + eventData->mDecoratorUpdated = true; + break; + } + + case EventData::EDITING_WITH_PASTE_POPUP: + { + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState); + + decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); + if(eventData->mCursorBlinkEnabled) + { + decorator->StartCursorBlink(); + } + + if(eventData->mGrabHandleEnabled) + { + decorator->SetHandleActive(GRAB_HANDLE, true); + } + decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); + decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); + decorator->SetHighlightActive(false); + + if(eventData->mGrabHandlePopupEnabled) + { + impl.SetPopupButtons(); + decorator->SetPopupActive(true); + } + eventData->mDecoratorUpdated = true; + break; + } + + case EventData::TEXT_PANNING: + { + decorator->SetActiveCursor(ACTIVE_CURSOR_NONE); + decorator->StopCursorBlink(); + decorator->SetHandleActive(GRAB_HANDLE, false); + if(eventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) || + decorator->IsHandleActive(RIGHT_SELECTION_HANDLE)) + { + decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); + decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); + decorator->SetHighlightActive(true); + } + + if(eventData->mGrabHandlePopupEnabled) + { + decorator->SetPopupActive(false); + } + + eventData->mDecoratorUpdated = true; + break; + } + } + } +} + } // unnamed Namespace EventData::EventData(DecoratorPtr decorator, InputMethodContext& inputMethodContext) @@ -268,6 +508,19 @@ void Controller::Impl::GetText(CharacterIndex index, std::string& text) const } } +Dali::LayoutDirection::Type Controller::Impl::GetLayoutDirection(Dali::Actor& actor) const +{ + if(mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::LOCALE || + (mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::INHERIT && !mIsLayoutDirectionChanged)) + { + return static_cast(DevelWindow::Get(actor).GetRootLayer().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get()); + } + else + { + return static_cast(actor.GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get()); + } +} + void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters) { mTextUpdateInfo.mParagraphCharacterIndex = 0u; @@ -353,286 +606,9 @@ void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters) mTextUpdateInfo.mStartGlyphIndex = *(mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex); } -void Controller::Impl::ClearFullModelData(OperationsMask operations) -{ - if(NO_OPERATION != (GET_LINE_BREAKS & operations)) - { - mModel->mLogicalModel->mLineBreakInfo.Clear(); - mModel->mLogicalModel->mParagraphInfo.Clear(); - } - - if(NO_OPERATION != (GET_SCRIPTS & operations)) - { - mModel->mLogicalModel->mScriptRuns.Clear(); - } - - if(NO_OPERATION != (VALIDATE_FONTS & operations)) - { - mModel->mLogicalModel->mFontRuns.Clear(); - } - - if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count()) - { - if(NO_OPERATION != (BIDI_INFO & operations)) - { - mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear(); - mModel->mLogicalModel->mCharacterDirections.Clear(); - } - - if(NO_OPERATION != (REORDER & operations)) - { - // Free the allocated memory used to store the conversion table in the bidirectional line info run. - for(Vector::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(), - endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End(); - it != endIt; - ++it) - { - BidirectionalLineInfoRun& bidiLineInfo = *it; - - free(bidiLineInfo.visualToLogicalMap); - bidiLineInfo.visualToLogicalMap = NULL; - } - mModel->mLogicalModel->mBidirectionalLineInfo.Clear(); - } - } - - if(NO_OPERATION != (SHAPE_TEXT & operations)) - { - mModel->mVisualModel->mGlyphs.Clear(); - mModel->mVisualModel->mGlyphsToCharacters.Clear(); - mModel->mVisualModel->mCharactersToGlyph.Clear(); - mModel->mVisualModel->mCharactersPerGlyph.Clear(); - mModel->mVisualModel->mGlyphsPerCharacter.Clear(); - mModel->mVisualModel->mGlyphPositions.Clear(); - } - - if(NO_OPERATION != (LAYOUT & operations)) - { - mModel->mVisualModel->mLines.Clear(); - } - - if(NO_OPERATION != (COLOR & operations)) - { - mModel->mVisualModel->mColorIndices.Clear(); - mModel->mVisualModel->mBackgroundColorIndices.Clear(); - } -} - -void Controller::Impl::ClearCharacterModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations) -{ - const CharacterIndex endIndexPlusOne = endIndex + 1u; - - if(NO_OPERATION != (GET_LINE_BREAKS & operations)) - { - // Clear the line break info. - LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin(); - - mModel->mLogicalModel->mLineBreakInfo.Erase(lineBreakInfoBuffer + startIndex, - lineBreakInfoBuffer + endIndexPlusOne); - - // Clear the paragraphs. - ClearCharacterRuns(startIndex, - endIndex, - mModel->mLogicalModel->mParagraphInfo); - } - - if(NO_OPERATION != (GET_SCRIPTS & operations)) - { - // Clear the scripts. - ClearCharacterRuns(startIndex, - endIndex, - mModel->mLogicalModel->mScriptRuns); - } - - if(NO_OPERATION != (VALIDATE_FONTS & operations)) - { - // Clear the fonts. - ClearCharacterRuns(startIndex, - endIndex, - mModel->mLogicalModel->mFontRuns); - } - - if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count()) - { - if(NO_OPERATION != (BIDI_INFO & operations)) - { - // Clear the bidirectional paragraph info. - ClearCharacterRuns(startIndex, - endIndex, - mModel->mLogicalModel->mBidirectionalParagraphInfo); - - // Clear the character's directions. - CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin(); - - mModel->mLogicalModel->mCharacterDirections.Erase(characterDirectionsBuffer + startIndex, - characterDirectionsBuffer + endIndexPlusOne); - } - - if(NO_OPERATION != (REORDER & operations)) - { - uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count(); - uint32_t endRemoveIndex = startRemoveIndex; - ClearCharacterRuns(startIndex, - endIndex, - mModel->mLogicalModel->mBidirectionalLineInfo, - startRemoveIndex, - endRemoveIndex); - - BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(); - - // Free the allocated memory used to store the conversion table in the bidirectional line info run. - for(Vector::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex, - endIt = bidirectionalLineInfoBuffer + endRemoveIndex; - it != endIt; - ++it) - { - BidirectionalLineInfoRun& bidiLineInfo = *it; - - free(bidiLineInfo.visualToLogicalMap); - bidiLineInfo.visualToLogicalMap = NULL; - } - - mModel->mLogicalModel->mBidirectionalLineInfo.Erase(bidirectionalLineInfoBuffer + startRemoveIndex, - bidirectionalLineInfoBuffer + endRemoveIndex); - } - } -} - -void Controller::Impl::ClearGlyphModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations) -{ - const CharacterIndex endIndexPlusOne = endIndex + 1u; - const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex; - - // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers. - GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin(); - Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin(); - - const GlyphIndex endGlyphIndexPlusOne = *(charactersToGlyphBuffer + endIndex) + *(glyphsPerCharacterBuffer + endIndex); - const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex; - - if(NO_OPERATION != (SHAPE_TEXT & operations)) - { - // Update the character to glyph indices. - for(Vector::Iterator it = charactersToGlyphBuffer + endIndexPlusOne, - endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count(); - it != endIt; - ++it) - { - CharacterIndex& index = *it; - index -= numberOfGlyphsRemoved; - } - - // Clear the character to glyph conversion table. - mModel->mVisualModel->mCharactersToGlyph.Erase(charactersToGlyphBuffer + startIndex, - charactersToGlyphBuffer + endIndexPlusOne); - - // Clear the glyphs per character table. - mModel->mVisualModel->mGlyphsPerCharacter.Erase(glyphsPerCharacterBuffer + startIndex, - glyphsPerCharacterBuffer + endIndexPlusOne); - - // Clear the glyphs buffer. - GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin(); - mModel->mVisualModel->mGlyphs.Erase(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, - glyphsBuffer + endGlyphIndexPlusOne); - - CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin(); - - // Update the glyph to character indices. - for(Vector::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne, - endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count(); - it != endIt; - ++it) - { - CharacterIndex& index = *it; - index -= numberOfCharactersRemoved; - } - - // Clear the glyphs to characters buffer. - mModel->mVisualModel->mGlyphsToCharacters.Erase(glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex, - glyphsToCharactersBuffer + endGlyphIndexPlusOne); - - // Clear the characters per glyph buffer. - Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin(); - mModel->mVisualModel->mCharactersPerGlyph.Erase(charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex, - charactersPerGlyphBuffer + endGlyphIndexPlusOne); - - // Should pass if mGlyphPositions has already been cleared in Controller::Relayouter::Relayout - if(0u != mModel->mVisualModel->mGlyphPositions.Count()) - { - // Clear the positions buffer. - Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin(); - mModel->mVisualModel->mGlyphPositions.Erase(positionsBuffer + mTextUpdateInfo.mStartGlyphIndex, - positionsBuffer + endGlyphIndexPlusOne); - } - } - - if(NO_OPERATION != (LAYOUT & operations)) - { - // Clear the lines. - uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count(); - uint32_t endRemoveIndex = startRemoveIndex; - ClearCharacterRuns(startIndex, - endIndex, - mModel->mVisualModel->mLines, - startRemoveIndex, - endRemoveIndex); - - // Will update the glyph runs. - startRemoveIndex = mModel->mVisualModel->mLines.Count(); - endRemoveIndex = startRemoveIndex; - ClearGlyphRuns(mTextUpdateInfo.mStartGlyphIndex, - endGlyphIndexPlusOne - 1u, - mModel->mVisualModel->mLines, - startRemoveIndex, - endRemoveIndex); - - // Set the line index from where to insert the new laid-out lines. - mTextUpdateInfo.mStartLineIndex = startRemoveIndex; - - LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin(); - mModel->mVisualModel->mLines.Erase(linesBuffer + startRemoveIndex, - linesBuffer + endRemoveIndex); - } - - if(NO_OPERATION != (COLOR & operations)) - { - if(0u != mModel->mVisualModel->mColorIndices.Count()) - { - ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin(); - mModel->mVisualModel->mColorIndices.Erase(colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex, - colorIndexBuffer + endGlyphIndexPlusOne); - } - - if(0u != mModel->mVisualModel->mBackgroundColorIndices.Count()) - { - ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin(); - mModel->mVisualModel->mBackgroundColorIndices.Erase(backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex, - backgroundColorIndexBuffer + endGlyphIndexPlusOne); - } - } -} - void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations) { - if(mTextUpdateInfo.mClearAll || - ((0u == startIndex) && - (mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u))) - { - ClearFullModelData(operations); - } - else - { - // Clear the model data related with characters. - ClearCharacterModelData(startIndex, endIndex, operations); - - // Clear the model data related with glyphs. - ClearGlyphModelData(startIndex, endIndex, operations); - } - - // The estimated number of lines. Used to avoid reallocations when layouting. - mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count()); - - mModel->mVisualModel->ClearCaches(); + ControllerImplDataClearer::ClearModelData(*this, startIndex, endIndex, operations); } bool Controller::Impl::UpdateModel(OperationsMask operationsRequired) @@ -664,6 +640,32 @@ float Controller::Impl::GetDefaultFontLineHeight() return (fontMetrics.ascender - fontMetrics.descender); } +bool Controller::Impl::SetDefaultLineSpacing(float lineSpacing) +{ + if(std::fabs(lineSpacing - mLayoutEngine.GetDefaultLineSpacing()) > Math::MACHINE_EPSILON_1000) + { + mLayoutEngine.SetDefaultLineSpacing(lineSpacing); + mRecalculateNaturalSize = true; + + RelayoutForNewLineSize(); + return true; + } + return false; +} + +bool Controller::Impl::SetDefaultLineSize(float lineSize) +{ + if(std::fabs(lineSize - mLayoutEngine.GetDefaultLineSize()) > Math::MACHINE_EPSILON_1000) + { + mLayoutEngine.SetDefaultLineSize(lineSize); + mRecalculateNaturalSize = true; + + RelayoutForNewLineSize(); + return true; + } + return false; +} + void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd) { if(nullptr == mEventData) @@ -783,6 +785,26 @@ void Controller::Impl::SetEditable(bool editable) if(mEventData) { mEventData->mEditingEnabled = editable; + + if(mEventData->mDecorator) + { + mEventData->mDecorator->SetEditable(editable); + } + } +} + +void Controller::Impl::UpdateAfterFontChange(const std::string& newDefaultFont) +{ + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::UpdateAfterFontChange\n"); + + if(!mFontDefaults->familyDefined) // If user defined font then should not update when system font changes + { + DALI_LOG_INFO(gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange newDefaultFont(%s)\n", newDefaultFont.c_str()); + mFontDefaults->mFontDescription.family = newDefaultFont; + + ClearFontData(); + + RequestRelayout(); } } @@ -1003,222 +1025,7 @@ void Controller::Impl::SetPopupButtons() void Controller::Impl::ChangeState(EventData::State newState) { - if(nullptr == mEventData) - { - // Nothing to do if there is no text input. - return; - } - - DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState); - - if(mEventData->mState != newState) - { - mEventData->mPreviousState = mEventData->mState; - mEventData->mState = newState; - - switch(mEventData->mState) - { - case EventData::INACTIVE: - { - mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE); - mEventData->mDecorator->StopCursorBlink(); - mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false); - mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHighlightActive(false); - mEventData->mDecorator->SetPopupActive(false); - mEventData->mDecoratorUpdated = true; - break; - } - case EventData::INTERRUPTED: - { - mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false); - mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHighlightActive(false); - mEventData->mDecorator->SetPopupActive(false); - mEventData->mDecoratorUpdated = true; - break; - } - case EventData::SELECTING: - { - mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE); - mEventData->mDecorator->StopCursorBlink(); - mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false); - if(mEventData->mGrabHandleEnabled) - { - mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true); - mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true); - } - mEventData->mDecorator->SetHighlightActive(true); - if(mEventData->mGrabHandlePopupEnabled) - { - SetPopupButtons(); - mEventData->mDecorator->SetPopupActive(true); - } - mEventData->mDecoratorUpdated = true; - break; - } - case EventData::EDITING: - { - mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); - if(mEventData->mCursorBlinkEnabled) - { - mEventData->mDecorator->StartCursorBlink(); - } - // Grab handle is not shown until a tap is received whilst EDITING - mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false); - mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHighlightActive(false); - if(mEventData->mGrabHandlePopupEnabled) - { - mEventData->mDecorator->SetPopupActive(false); - } - mEventData->mDecoratorUpdated = true; - break; - } - case EventData::EDITING_WITH_POPUP: - { - DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState); - - mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); - if(mEventData->mCursorBlinkEnabled) - { - mEventData->mDecorator->StartCursorBlink(); - } - if(mEventData->mSelectionEnabled) - { - mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHighlightActive(false); - } - else if(mEventData->mGrabHandleEnabled) - { - mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true); - } - if(mEventData->mGrabHandlePopupEnabled) - { - SetPopupButtons(); - mEventData->mDecorator->SetPopupActive(true); - } - mEventData->mDecoratorUpdated = true; - break; - } - case EventData::EDITING_WITH_GRAB_HANDLE: - { - DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState); - - mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); - if(mEventData->mCursorBlinkEnabled) - { - mEventData->mDecorator->StartCursorBlink(); - } - // Grab handle is not shown until a tap is received whilst EDITING - if(mEventData->mGrabHandleEnabled) - { - mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true); - } - mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHighlightActive(false); - if(mEventData->mGrabHandlePopupEnabled) - { - mEventData->mDecorator->SetPopupActive(false); - } - mEventData->mDecoratorUpdated = true; - break; - } - case EventData::SELECTION_HANDLE_PANNING: - { - mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE); - mEventData->mDecorator->StopCursorBlink(); - mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false); - if(mEventData->mGrabHandleEnabled) - { - mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true); - mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true); - } - mEventData->mDecorator->SetHighlightActive(true); - if(mEventData->mGrabHandlePopupEnabled) - { - mEventData->mDecorator->SetPopupActive(false); - } - mEventData->mDecoratorUpdated = true; - break; - } - case EventData::GRAB_HANDLE_PANNING: - { - DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState); - - mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); - if(mEventData->mCursorBlinkEnabled) - { - mEventData->mDecorator->StartCursorBlink(); - } - if(mEventData->mGrabHandleEnabled) - { - mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true); - } - mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHighlightActive(false); - if(mEventData->mGrabHandlePopupEnabled) - { - mEventData->mDecorator->SetPopupActive(false); - } - mEventData->mDecoratorUpdated = true; - break; - } - case EventData::EDITING_WITH_PASTE_POPUP: - { - DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState); - - mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); - if(mEventData->mCursorBlinkEnabled) - { - mEventData->mDecorator->StartCursorBlink(); - } - - if(mEventData->mGrabHandleEnabled) - { - mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true); - } - mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHighlightActive(false); - - if(mEventData->mGrabHandlePopupEnabled) - { - SetPopupButtons(); - mEventData->mDecorator->SetPopupActive(true); - } - mEventData->mDecoratorUpdated = true; - break; - } - case EventData::TEXT_PANNING: - { - mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE); - mEventData->mDecorator->StopCursorBlink(); - mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false); - if(mEventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) || - mEventData->mDecorator->IsHandleActive(RIGHT_SELECTION_HANDLE)) - { - mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHighlightActive(true); - } - - if(mEventData->mGrabHandlePopupEnabled) - { - mEventData->mDecorator->SetPopupActive(false); - } - - mEventData->mDecoratorUpdated = true; - break; - } - } - } + ChangeTextControllerState(*this, newState); } void Controller::Impl::GetCursorPosition(CharacterIndex logical, @@ -1732,6 +1539,86 @@ Actor Controller::Impl::CreateBackgroundActor() return actor; } +void Controller::Impl::RelayoutForNewLineSize() +{ + // relayout all characters + mTextUpdateInfo.mCharacterIndex = 0; + mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters; + mTextUpdateInfo.mNumberOfCharactersToAdd = mModel->mLogicalModel->mText.Count(); + mOperationsPending = static_cast(mOperationsPending | LAYOUT); + + //remove selection + if(mEventData && mEventData->mState == EventData::SELECTING) + { + ChangeState(EventData::EDITING); + } + + RequestRelayout(); +} + +bool Controller::Impl::IsInputStyleChangedSignalsQueueEmpty() +{ + return (NULL == mEventData) || (0u == mEventData->mInputStyleChangedQueue.Count()); +} + +void Controller::Impl::ProcessInputStyleChangedSignals() +{ + if(mEventData) + { + if(mEditableControlInterface) + { + // Emit the input style changed signal for each mask + std::for_each(mEventData->mInputStyleChangedQueue.begin(), + mEventData->mInputStyleChangedQueue.end(), + [&](const auto mask) { mEditableControlInterface->InputStyleChanged(mask); } ); + } + + mEventData->mInputStyleChangedQueue.Clear(); + } +} + +void Controller::Impl::ScrollBy(Vector2 scroll) +{ + if(mEventData && (fabs(scroll.x) > Math::MACHINE_EPSILON_0 || fabs(scroll.y) > Math::MACHINE_EPSILON_0)) + { + const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize(); + const Vector2 currentScroll = mModel->mScrollPosition; + + scroll.x = -scroll.x; + scroll.y = -scroll.y; + + if(fabs(scroll.x) > Math::MACHINE_EPSILON_0) + { + mModel->mScrollPosition.x += scroll.x; + ClampHorizontalScroll(layoutSize); + } + + if(fabs(scroll.y) > Math::MACHINE_EPSILON_0) + { + mModel->mScrollPosition.y += scroll.y; + ClampVerticalScroll(layoutSize); + } + + if(mModel->mScrollPosition != currentScroll) + { + mEventData->mDecorator->UpdatePositions(mModel->mScrollPosition - currentScroll); + RequestRelayout(); + } + } +} + +float Controller::Impl::GetHorizontalScrollPosition() +{ + // Scroll values are negative internally so we convert them to positive numbers + return mEventData ? -mModel->mScrollPosition.x : 0.0f; +} + +float Controller::Impl::GetVerticalScrollPosition() +{ + // Scroll values are negative internally so we convert them to positive numbers + return mEventData ? -mModel->mScrollPosition.y : 0.0f; +} + void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns) { //Underlined character runs for markup-processor @@ -1758,4 +1645,197 @@ void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearP } } +void Controller::Impl::SetAutoScrollEnabled(bool enable) +{ + if(mLayoutEngine.GetLayout() == Layout::Engine::SINGLE_LINE_BOX) + { + mOperationsPending = static_cast(mOperationsPending | + LAYOUT | + ALIGN | + UPDATE_LAYOUT_SIZE | + REORDER); + + if(enable) + { + DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled for SINGLE_LINE_BOX\n"); + mOperationsPending = static_cast(mOperationsPending | UPDATE_DIRECTION); + } + else + { + DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled Disabling autoscroll\n"); + } + + mIsAutoScrollEnabled = enable; + RequestRelayout(); + } + else + { + DALI_LOG_WARNING("Attempted AutoScrolling on a non SINGLE_LINE_BOX, request ignored\n"); + mIsAutoScrollEnabled = false; + } +} + +void Controller::Impl::SetEnableCursorBlink(bool enable) +{ + DALI_ASSERT_DEBUG(NULL != mEventData && "TextInput disabled"); + + if(mEventData) + { + mEventData->mCursorBlinkEnabled = enable; + + if(!enable && mEventData->mDecorator) + { + mEventData->mDecorator->StopCursorBlink(); + } + } +} + +void Controller::Impl::SetMultiLineEnabled(bool enable) +{ + const Layout::Engine::Type layout = enable ? Layout::Engine::MULTI_LINE_BOX : Layout::Engine::SINGLE_LINE_BOX; + + if(layout != mLayoutEngine.GetLayout()) + { + // Set the layout type. + mLayoutEngine.SetLayout(layout); + + // Set the flags to redo the layout operations + const OperationsMask layoutOperations = static_cast(LAYOUT | + UPDATE_LAYOUT_SIZE | + ALIGN | + REORDER); + + mTextUpdateInfo.mFullRelayoutNeeded = true; + mOperationsPending = static_cast(mOperationsPending | layoutOperations); + + // Need to recalculate natural size + mRecalculateNaturalSize = true; + + RequestRelayout(); + } +} + +void Controller::Impl::SetHorizontalAlignment(Text::HorizontalAlignment::Type alignment) +{ + if(alignment != mModel->mHorizontalAlignment) + { + // Set the alignment. + mModel->mHorizontalAlignment = alignment; + + // Set the flag to redo the alignment operation. + mOperationsPending = static_cast(mOperationsPending | ALIGN); + + if(mEventData) + { + mEventData->mUpdateAlignment = true; + + // Update the cursor if it's in editing mode + if(EventData::IsEditingState(mEventData->mState)) + { + ChangeState(EventData::EDITING); + mEventData->mUpdateCursorPosition = true; + } + } + + RequestRelayout(); + } +} + +void Controller::Impl::SetVerticalAlignment(VerticalAlignment::Type alignment) +{ + if(alignment != mModel->mVerticalAlignment) + { + // Set the alignment. + mModel->mVerticalAlignment = alignment; + mOperationsPending = static_cast(mOperationsPending | ALIGN); + RequestRelayout(); + } +} + +void Controller::Impl::SetLineWrapMode(Text::LineWrap::Mode lineWrapMode) +{ + if(lineWrapMode != mModel->mLineWrapMode) + { + // Update Text layout for applying wrap mode + mOperationsPending = static_cast(mOperationsPending | + ALIGN | + LAYOUT | + UPDATE_LAYOUT_SIZE | + REORDER); + + if((mModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) || (lineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) || + (mModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED) || (lineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED)) // hyphen is treated as line break + { + mOperationsPending = static_cast(mOperationsPending | GET_LINE_BREAKS); + } + + // Set the text wrap mode. + mModel->mLineWrapMode = lineWrapMode; + + mTextUpdateInfo.mCharacterIndex = 0u; + mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters; + mTextUpdateInfo.mNumberOfCharactersToAdd = mModel->mLogicalModel->mText.Count(); + + // Request relayout + RequestRelayout(); + } +} + +void Controller::Impl::SetDefaultColor(const Vector4& color) +{ + mTextColor = color; + + if(!IsShowingPlaceholderText()) + { + mModel->mVisualModel->SetTextColor(color); + mModel->mLogicalModel->mColorRuns.Clear(); + mOperationsPending = static_cast(mOperationsPending | COLOR); + RequestRelayout(); + } +} + +void Controller::Impl::ClearFontData() +{ + if(mFontDefaults) + { + mFontDefaults->mFontId = 0u; // Remove old font ID + } + + // Set flags to update the model. + mTextUpdateInfo.mCharacterIndex = 0u; + mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters; + mTextUpdateInfo.mNumberOfCharactersToAdd = mModel->mLogicalModel->mText.Count(); + + mTextUpdateInfo.mClearAll = true; + mTextUpdateInfo.mFullRelayoutNeeded = true; + mRecalculateNaturalSize = true; + + mOperationsPending = static_cast(mOperationsPending | + VALIDATE_FONTS | + SHAPE_TEXT | + BIDI_INFO | + GET_GLYPH_METRICS | + LAYOUT | + UPDATE_LAYOUT_SIZE | + REORDER | + ALIGN); +} + +void Controller::Impl::ClearStyleData() +{ + mModel->mLogicalModel->mColorRuns.Clear(); + mModel->mLogicalModel->ClearFontDescriptionRuns(); +} + + +void Controller::Impl::ResetScrollPosition() +{ + if(mEventData) + { + // Reset the scroll position. + mModel->mScrollPosition = Vector2::ZERO; + mEventData->mScrollAfterUpdatePosition = true; + } +} + } // namespace Dali::Toolkit::Text diff --git a/dali-toolkit/internal/text/text-controller-impl.h b/dali-toolkit/internal/text/text-controller-impl.h index 51e0438..814f3a8 100644 --- a/dali-toolkit/internal/text/text-controller-impl.h +++ b/dali-toolkit/internal/text/text-controller-impl.h @@ -545,6 +545,11 @@ struct Controller::Impl } /** + * @copydoc Controller::GetLayoutDirection() + */ + Dali::LayoutDirection::Type GetLayoutDirection(Dali::Actor& actor) const; + + /** * @brief Calculates the start character index of the first paragraph to be updated and * the end character index of the last paragraph to be updated. * @@ -553,36 +558,6 @@ struct Controller::Impl void CalculateTextUpdateIndices(Length& numberOfCharacters); /** - * @brief Helper to clear completely the parts of the model specified by the given @p operations. - * - * @note It never clears the text stored in utf32. - */ - void ClearFullModelData(OperationsMask operations); - - /** - * @brief Helper to clear completely the parts of the model related with the characters specified by the given @p operations. - * - * @note It never clears the text stored in utf32. - * - * @param[in] startIndex Index to the first character to be cleared. - * @param[in] endIndex Index to the last character to be cleared. - * @param[in] operations The operations required. - */ - void ClearCharacterModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations); - - /** - * @brief Helper to clear completely the parts of the model related with the glyphs specified by the given @p operations. - * - * @note It never clears the text stored in utf32. - * @note Character indices are transformed to glyph indices. - * - * @param[in] startIndex Index to the first character to be cleared. - * @param[in] endIndex Index to the last character to be cleared. - * @param[in] operations The operations required. - */ - void ClearGlyphModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations); - - /** * @brief Helper to clear the parts of the model specified by the given @p operations and from @p startIndex to @p endIndex. * * @note It never clears the text stored in utf32. @@ -620,6 +595,16 @@ struct Controller::Impl float GetDefaultFontLineHeight(); /** + * @copydoc Controller::SetDefaultLineSpacing + */ + bool SetDefaultLineSpacing(float lineSpacing); + + /** + * @copydoc Controller::SetDefaultLineSize + */ + bool SetDefaultLineSize(float lineSize); + + /** * @copydoc Text::Controller::GetPrimaryCursorPosition() */ CharacterIndex GetPrimaryCursorPosition() const; @@ -650,6 +635,11 @@ struct Controller::Impl void SetEditable(bool editable); /** + * @copydoc Controller::UpdateAfterFontChange + */ + void UpdateAfterFontChange(const std::string& newDefaultFont); + + /** * @brief Retrieves the selected text. It removes the text if the @p deleteAfterRetrieval parameter is @e true. * * @param[out] selectedText The selected text encoded in utf8. @@ -771,6 +761,86 @@ struct Controller::Impl */ Actor CreateBackgroundActor(); + /** + * @brief fill needed relayout parameters when line size is changed & request relayout. + */ + void RelayoutForNewLineSize(); + + /** + * @copydoc Controller::IsInputStyleChangedSignalsQueueEmpty + */ + bool IsInputStyleChangedSignalsQueueEmpty(); + + /** + * @copydoc Controller::ProcessInputStyleChangedSignals + */ + void ProcessInputStyleChangedSignals(); + + /** + * @copydoc Controller::ScrollBy() + */ + void ScrollBy(Vector2 scroll); + + /** + * @copydoc Controller::GetHorizontalScrollPosition() + */ + float GetHorizontalScrollPosition(); + + /** + * @copydoc Controller::GetVerticalScrollPosition() + */ + float GetVerticalScrollPosition(); + + /** + * @copydoc Controller::SetAutoScrollEnabled() + */ + void SetAutoScrollEnabled(bool enable); + + /** + * @copydoc Controller::SetEnableCursorBlink() + */ + void SetEnableCursorBlink(bool enable); + + /** + * @copydoc Controller::SetMultiLineEnabled() + */ + void SetMultiLineEnabled(bool enable); + + /** + * @copydoc Controller::SetHorizontalAlignment() + */ + void SetHorizontalAlignment(HorizontalAlignment::Type alignment); + + /** + * @copydoc Controller::SetVerticalAlignment() + */ + void SetVerticalAlignment(VerticalAlignment::Type alignment); + + /** + * @copydoc Controller::SetLineWrapMode() + */ + void SetLineWrapMode(Text::LineWrap::Mode textWarpMode); + + /** + * @copydoc Controller::SetDefaultColor() + */ + void SetDefaultColor(const Vector4& color); + + /** + * @brief Helper to clear font-specific data (only). + */ + void ClearFontData(); + + /** + * @brief Helper to clear text's style data. + */ + void ClearStyleData(); + + /** + * @brief Used to reset the scroll position after setting a new text. + */ + void ResetScrollPosition(); + public: /** * @brief Gets implementation from the controller handle. diff --git a/dali-toolkit/internal/text/text-controller-input-properties.cpp b/dali-toolkit/internal/text/text-controller-input-properties.cpp new file mode 100644 index 0000000..99ef15a --- /dev/null +++ b/dali-toolkit/internal/text/text-controller-input-properties.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2021 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. + * + */ + +// CLASS HEADER +#include + +// EXTERNAL INCLUDES +//#include +//#include +//#include +#include +#include +#include + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ +const std::string EMPTY_STRING(""); +} + +namespace Dali::Toolkit::Text +{ + +void Controller::InputProperties::SetInputColor(Controller& controller, const Vector4& color) +{ + if(controller.mImpl->mEventData) + { + controller.mImpl->mEventData->mInputStyle.textColor = color; + controller.mImpl->mEventData->mInputStyle.isDefaultColor = false; + + if(EventData::SELECTING == controller.mImpl->mEventData->mState || EventData::EDITING == controller.mImpl->mEventData->mState || EventData::INACTIVE == controller.mImpl->mEventData->mState) + { + if(EventData::SELECTING == controller.mImpl->mEventData->mState) + { + const bool handlesCrossed = controller.mImpl->mEventData->mLeftSelectionPosition > controller.mImpl->mEventData->mRightSelectionPosition; + + // Get start and end position of selection + const CharacterIndex startOfSelectedText = handlesCrossed ? controller.mImpl->mEventData->mRightSelectionPosition : controller.mImpl->mEventData->mLeftSelectionPosition; + const Length lengthOfSelectedText = (handlesCrossed ? controller.mImpl->mEventData->mLeftSelectionPosition : controller.mImpl->mEventData->mRightSelectionPosition) - startOfSelectedText; + + // Add the color run. + const VectorBase::SizeType numberOfRuns = controller.mImpl->mModel->mLogicalModel->mColorRuns.Count(); + controller.mImpl->mModel->mLogicalModel->mColorRuns.Resize(numberOfRuns + 1u); + + ColorRun& colorRun = *(controller.mImpl->mModel->mLogicalModel->mColorRuns.Begin() + numberOfRuns); + colorRun.color = color; + colorRun.characterRun.characterIndex = startOfSelectedText; + colorRun.characterRun.numberOfCharacters = lengthOfSelectedText; + + controller.mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText; + controller.mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText; + controller.mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText; + } + else + { + controller.mImpl->mTextUpdateInfo.mCharacterIndex = 0; + controller.mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = controller.mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters; + controller.mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = controller.mImpl->mModel->mLogicalModel->mText.Count(); + } + + // Request to relayout. + controller.mImpl->mOperationsPending = static_cast(controller.mImpl->mOperationsPending | COLOR); + controller.mImpl->RequestRelayout(); + } + } +} + +const Vector4& Controller::InputProperties::GetInputColor(const Controller& controller) +{ + // Return event text input color if we have it, otherwise just return the default text's color + return controller.mImpl->mEventData ? controller.mImpl->mEventData->mInputStyle.textColor : controller.mImpl->mTextColor; +} + +void Controller::InputProperties::SetInputLineSpacing(Controller& controller, float lineSpacing) +{ + if(controller.mImpl->mEventData) + { + controller.mImpl->mEventData->mInputStyle.lineSpacing = lineSpacing; + controller.mImpl->mEventData->mInputStyle.isLineSpacingDefined = true; + } +} + +float Controller::InputProperties::GetInputLineSpacing(const Controller& controller) +{ + return controller.mImpl->mEventData ? controller.mImpl->mEventData->mInputStyle.lineSpacing : 0.0f; +} + +void Controller::InputProperties::SetInputShadowProperties(Controller& controller, const std::string& shadowProperties) +{ + if(controller.mImpl->mEventData) + { + controller.mImpl->mEventData->mInputStyle.shadowProperties = shadowProperties; + } +} + +const std::string& Controller::InputProperties::GetInputShadowProperties(const Controller& controller) +{ + return controller.mImpl->mEventData ? controller.mImpl->mEventData->mInputStyle.shadowProperties : EMPTY_STRING; +} + +void Controller::InputProperties::SetInputUnderlineProperties(Controller& controller, const std::string& underlineProperties) +{ + if(controller.mImpl->mEventData) + { + controller.mImpl->mEventData->mInputStyle.underlineProperties = underlineProperties; + } +} + +const std::string& Controller::InputProperties::GetInputUnderlineProperties(const Controller& controller) +{ + return controller.mImpl->mEventData ? controller.mImpl->mEventData->mInputStyle.underlineProperties : EMPTY_STRING; +} + +void Controller::InputProperties::SetInputEmbossProperties(Controller& controller, const std::string& embossProperties) +{ + if(controller.mImpl->mEventData) + { + controller.mImpl->mEventData->mInputStyle.embossProperties = embossProperties; + } +} + +const std::string& Controller::InputProperties::GetInputEmbossProperties(const Controller& controller) +{ + return controller.mImpl->mEventData ? controller.mImpl->mEventData->mInputStyle.embossProperties : controller.GetDefaultEmbossProperties(); +} + +void Controller::InputProperties::SetInputOutlineProperties(Controller& controller, const std::string& outlineProperties) +{ + if(controller.mImpl->mEventData) + { + controller.mImpl->mEventData->mInputStyle.outlineProperties = outlineProperties; + } +} + +const std::string& Controller::InputProperties::GetInputOutlineProperties(const Controller& controller) +{ + return controller.mImpl->mEventData ? controller.mImpl->mEventData->mInputStyle.outlineProperties : controller.GetDefaultOutlineProperties(); +} + +void Controller::InputProperties::SetInputModePassword(Controller& controller, bool passwordInput) +{ + if(controller.mImpl->mEventData) + { + controller.mImpl->mEventData->mPasswordInput = passwordInput; + } +} + +bool Controller::InputProperties::IsInputModePassword(Controller& controller) +{ + return controller.mImpl->mEventData && controller.mImpl->mEventData->mPasswordInput; +} + +} // namespace Dali::Toolkit::Text diff --git a/dali-toolkit/internal/text/text-controller-input-properties.h b/dali-toolkit/internal/text/text-controller-input-properties.h new file mode 100644 index 0000000..d453b5f --- /dev/null +++ b/dali-toolkit/internal/text/text-controller-input-properties.h @@ -0,0 +1,75 @@ +#ifndef DALI_TOOLKIT_TEXT_CONTROLLER_INPUT_PROPERTIES_H +#define DALI_TOOLKIT_TEXT_CONTROLLER_INPUT_PROPERTIES_H + +/* + * Copyright (c) 2021 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. + * + */ + +// EXTERNAL INCLUDES +#include +#include + +// INTERNAL INCLUDES +#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include + +namespace Dali::Toolkit::Text +{ +struct Controller::InputProperties +{ + static void SetInputColor(Controller& controller, const Vector4& color); + + static const Vector4& GetInputColor(const Controller& controller); + + static void SetInputLineSpacing(Controller& controller, float lineSpacing); + + static float GetInputLineSpacing(const Controller& controller); + + static void SetInputShadowProperties(Controller& controller, const std::string& shadowProperties); + + static const std::string& GetInputShadowProperties(const Controller& controller); + + static void SetInputUnderlineProperties(Controller& controller, const std::string& underlineProperties); + + static const std::string& GetInputUnderlineProperties(const Controller& controller); + + static void SetInputEmbossProperties(Controller& controller, const std::string& embossProperties); + + static const std::string& GetInputEmbossProperties(const Controller& controller); + + static void SetInputOutlineProperties(Controller& controller, const std::string& outlineProperties); + + static const std::string& GetInputOutlineProperties(const Controller& controller); + + static void SetInputModePassword(Controller& controller, bool passwordInput); + + static bool IsInputModePassword(Controller& controller); +}; + + +} // namespace Dali::Toolkit::Text + +#endif // DALI_TOOLKIT_TEXT_CONTROLLER_INPUT_PROPERTIES_H diff --git a/dali-toolkit/internal/text/text-controller-relayouter.cpp b/dali-toolkit/internal/text/text-controller-relayouter.cpp index ed0ddc6..c38f92e 100644 --- a/dali-toolkit/internal/text/text-controller-relayouter.cpp +++ b/dali-toolkit/internal/text/text-controller-relayouter.cpp @@ -170,7 +170,7 @@ bool Controller::Relayouter::CheckForTextFit(Controller& controller, float point TextUpdateInfo& textUpdateInfo = impl.mTextUpdateInfo; impl.mFontDefaults->mFitPointSize = pointSize; impl.mFontDefaults->sizeDefined = true; - controller.ClearFontData(); + impl.ClearFontData(); // Operations that can be done only once until the text changes. const OperationsMask onlyOnceOperations = static_cast(CONVERT_TO_UTF32 | @@ -262,7 +262,7 @@ void Controller::Relayouter::FitPointSizeforLayout(Controller& controller, const } impl.mFontDefaults->mFitPointSize = pointSizeArray[bestSizeIndex]; impl.mFontDefaults->sizeDefined = true; - controller.ClearFontData(); + impl.ClearFontData(); } } @@ -566,7 +566,7 @@ bool Controller::Relayouter::DoRelayout(Controller& controller, const Size& size // Reset the scroll position in inactive state if(elideTextEnabled && (impl.mEventData->mState == EventData::INACTIVE)) { - controller.ResetScrollPosition(); + impl.ResetScrollPosition(); } } diff --git a/dali-toolkit/internal/text/text-controller-text-updater.cpp b/dali-toolkit/internal/text/text-controller-text-updater.cpp index 75ea73c..8a19786 100644 --- a/dali-toolkit/internal/text/text-controller-text-updater.cpp +++ b/dali-toolkit/internal/text/text-controller-text-updater.cpp @@ -55,7 +55,7 @@ void Controller::TextUpdater::SetText(Controller& controller, const std::string& ResetText(controller); // Remove the style. - controller.ClearStyleData(); + impl.ClearStyleData(); CharacterIndex lastCursorIndex = 0u; @@ -150,7 +150,7 @@ void Controller::TextUpdater::SetText(Controller& controller, const std::string& controller.ResetCursorPosition(lastCursorIndex); // Scrolls the text to make the cursor visible. - controller.ResetScrollPosition(); + impl.ResetScrollPosition(); impl.RequestRelayout(); diff --git a/dali-toolkit/internal/text/text-controller.cpp b/dali-toolkit/internal/text/text-controller.cpp index 832f09a..4ec306b 100644 --- a/dali-toolkit/internal/text/text-controller.cpp +++ b/dali-toolkit/internal/text/text-controller.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -137,7 +138,7 @@ void Controller::SetGlyphType(TextAbstraction::GlyphType glyphType) mImpl->mMetrics->SetGlyphType(glyphType); // Clear the font-specific data - ClearFontData(); + mImpl->ClearFontData(); mImpl->RequestRelayout(); } @@ -169,39 +170,12 @@ bool Controller::HasAnchors() const void Controller::SetAutoScrollEnabled(bool enable) { DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled[%s] SingleBox[%s]-> [%p]\n", (enable) ? "true" : "false", (mImpl->mLayoutEngine.GetLayout() == Layout::Engine::SINGLE_LINE_BOX) ? "true" : "false", this); - - if(mImpl->mLayoutEngine.GetLayout() == Layout::Engine::SINGLE_LINE_BOX) - { - mImpl->mOperationsPending = static_cast(mImpl->mOperationsPending | - LAYOUT | - ALIGN | - UPDATE_LAYOUT_SIZE | - REORDER); - - if(enable) - { - DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled for SINGLE_LINE_BOX\n"); - mImpl->mOperationsPending = static_cast(mImpl->mOperationsPending | UPDATE_DIRECTION); - } - else - { - DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled Disabling autoscroll\n"); - } - - mImpl->mIsAutoScrollEnabled = enable; - mImpl->RequestRelayout(); - } - else - { - DALI_LOG_WARNING("Attempted AutoScrolling on a non SINGLE_LINE_BOX, request ignored\n"); - mImpl->mIsAutoScrollEnabled = false; - } + mImpl->SetAutoScrollEnabled(enable); } bool Controller::IsAutoScrollEnabled() const { DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::IsAutoScrollEnabled[%s]\n", mImpl->mIsAutoScrollEnabled ? "true" : "false"); - return mImpl->mIsAutoScrollEnabled; } @@ -271,17 +245,7 @@ int Controller::GetMaximumNumberOfCharacters() void Controller::SetEnableCursorBlink(bool enable) { - DALI_ASSERT_DEBUG(NULL != mImpl->mEventData && "TextInput disabled"); - - if(mImpl->mEventData) - { - mImpl->mEventData->mCursorBlinkEnabled = enable; - - if(!enable && mImpl->mEventData->mDecorator) - { - mImpl->mEventData->mDecorator->StopCursorBlink(); - } - } + mImpl->SetEnableCursorBlink(enable); } bool Controller::GetEnableCursorBlink() const @@ -291,27 +255,7 @@ bool Controller::GetEnableCursorBlink() const void Controller::SetMultiLineEnabled(bool enable) { - const Layout::Engine::Type layout = enable ? Layout::Engine::MULTI_LINE_BOX : Layout::Engine::SINGLE_LINE_BOX; - - if(layout != mImpl->mLayoutEngine.GetLayout()) - { - // Set the layout type. - mImpl->mLayoutEngine.SetLayout(layout); - - // Set the flags to redo the layout operations - const OperationsMask layoutOperations = static_cast(LAYOUT | - UPDATE_LAYOUT_SIZE | - ALIGN | - REORDER); - - mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true; - mImpl->mOperationsPending = static_cast(mImpl->mOperationsPending | layoutOperations); - - // Need to recalculate natural size - mImpl->mRecalculateNaturalSize = true; - - mImpl->RequestRelayout(); - } + mImpl->SetMultiLineEnabled(enable); } bool Controller::IsMultiLineEnabled() const @@ -321,28 +265,7 @@ bool Controller::IsMultiLineEnabled() const void Controller::SetHorizontalAlignment(Text::HorizontalAlignment::Type alignment) { - if(alignment != mImpl->mModel->mHorizontalAlignment) - { - // Set the alignment. - mImpl->mModel->mHorizontalAlignment = alignment; - - // Set the flag to redo the alignment operation. - mImpl->mOperationsPending = static_cast(mImpl->mOperationsPending | ALIGN); - - if(mImpl->mEventData) - { - mImpl->mEventData->mUpdateAlignment = true; - - // Update the cursor if it's in editing mode - if(EventData::IsEditingState(mImpl->mEventData->mState)) - { - mImpl->ChangeState(EventData::EDITING); - mImpl->mEventData->mUpdateCursorPosition = true; - } - } - - mImpl->RequestRelayout(); - } + mImpl->SetHorizontalAlignment(alignment); } Text::HorizontalAlignment::Type Controller::GetHorizontalAlignment() const @@ -352,13 +275,7 @@ Text::HorizontalAlignment::Type Controller::GetHorizontalAlignment() const void Controller::SetVerticalAlignment(VerticalAlignment::Type alignment) { - if(alignment != mImpl->mModel->mVerticalAlignment) - { - // Set the alignment. - mImpl->mModel->mVerticalAlignment = alignment; - mImpl->mOperationsPending = static_cast(mImpl->mOperationsPending | ALIGN); - mImpl->RequestRelayout(); - } + mImpl->SetVerticalAlignment(alignment); } VerticalAlignment::Type Controller::GetVerticalAlignment() const @@ -416,31 +333,7 @@ bool Controller::IsShowingRealText() const void Controller::SetLineWrapMode(Text::LineWrap::Mode lineWrapMode) { - if(lineWrapMode != mImpl->mModel->mLineWrapMode) - { - // Update Text layout for applying wrap mode - mImpl->mOperationsPending = static_cast(mImpl->mOperationsPending | - ALIGN | - LAYOUT | - UPDATE_LAYOUT_SIZE | - REORDER); - - if((mImpl->mModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) || (lineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) || - (mImpl->mModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED) || (lineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED)) // hyphen is treated as line break - { - mImpl->mOperationsPending = static_cast(mImpl->mOperationsPending | GET_LINE_BREAKS); - } - - // Set the text wrap mode. - mImpl->mModel->mLineWrapMode = lineWrapMode; - - mImpl->mTextUpdateInfo.mCharacterIndex = 0u; - mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters; - mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count(); - - // Request relayout - mImpl->RequestRelayout(); - } + mImpl->SetLineWrapMode(lineWrapMode); } Text::LineWrap::Mode Controller::GetLineWrapMode() const @@ -604,17 +497,7 @@ void Controller::GetPlaceholderText(PlaceholderType type, std::string& text) con void Controller::UpdateAfterFontChange(const std::string& newDefaultFont) { - DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::UpdateAfterFontChange\n"); - - if(!mImpl->mFontDefaults->familyDefined) // If user defined font then should not update when system font changes - { - DALI_LOG_INFO(gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange newDefaultFont(%s)\n", newDefaultFont.c_str()); - mImpl->mFontDefaults->mFontDescription.family = newDefaultFont; - - ClearFontData(); - - mImpl->RequestRelayout(); - } + mImpl->UpdateAfterFontChange(newDefaultFont); } void Controller::RetrieveSelection(std::string& selectedText) const @@ -654,7 +537,7 @@ void Controller::SetDefaultFontFamily(const std::string& defaultFontFamily) UpdateCursorPosition(mImpl->mEventData); // Clear the font-specific data - ClearFontData(); + mImpl->ClearFontData(); mImpl->RequestRelayout(); } @@ -685,7 +568,7 @@ void Controller::SetDefaultFontWeight(FontWeight weight) UpdateCursorPosition(mImpl->mEventData); // Clear the font-specific data - ClearFontData(); + mImpl->ClearFontData(); mImpl->RequestRelayout(); } @@ -726,7 +609,7 @@ void Controller::SetDefaultFontWidth(FontWidth width) UpdateCursorPosition(mImpl->mEventData); // Clear the font-specific data - ClearFontData(); + mImpl->ClearFontData(); mImpl->RequestRelayout(); } @@ -767,7 +650,7 @@ void Controller::SetDefaultFontSlant(FontSlant slant) UpdateCursorPosition(mImpl->mEventData); // Clear the font-specific data - ClearFontData(); + mImpl->ClearFontData(); mImpl->RequestRelayout(); } @@ -805,7 +688,7 @@ void Controller::SetFontSizeScale(float scale) UpdateCursorPosition(mImpl->mEventData); // Clear the font-specific data - ClearFontData(); + mImpl->ClearFontData(); mImpl->RequestRelayout(); } @@ -826,7 +709,7 @@ void Controller::SetDefaultFontSize(float fontSize, FontSizeType type) UpdateCursorPosition(mImpl->mEventData); // Clear the font-specific data - ClearFontData(); + mImpl->ClearFontData(); mImpl->RequestRelayout(); } @@ -852,15 +735,7 @@ float Controller::GetPlaceholderTextFontSize(FontSizeType type) const void Controller::SetDefaultColor(const Vector4& color) { - mImpl->mTextColor = color; - - if(!mImpl->IsShowingPlaceholderText()) - { - mImpl->mModel->mVisualModel->SetTextColor(color); - mImpl->mModel->mLogicalModel->mColorRuns.Clear(); - mImpl->mOperationsPending = static_cast(mImpl->mOperationsPending | COLOR); - mImpl->RequestRelayout(); - } + mImpl->SetDefaultColor(color); } const Vector4& Controller::GetDefaultColor() const @@ -1013,34 +888,9 @@ const std::string& Controller::GetDefaultOutlineProperties() const return mImpl->mOutlineDefaults ? mImpl->mOutlineDefaults->properties : EMPTY_STRING; } -void Controller::RelayoutForNewLineSize() -{ - // relayout all characters - mImpl->mTextUpdateInfo.mCharacterIndex = 0; - mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters; - mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count(); - mImpl->mOperationsPending = static_cast(mImpl->mOperationsPending | LAYOUT); - - //remove selection - if(mImpl->mEventData && mImpl->mEventData->mState == EventData::SELECTING) - { - mImpl->ChangeState(EventData::EDITING); - } - - mImpl->RequestRelayout(); -} - bool Controller::SetDefaultLineSpacing(float lineSpacing) { - if(std::fabs(lineSpacing - mImpl->mLayoutEngine.GetDefaultLineSpacing()) > Math::MACHINE_EPSILON_1000) - { - mImpl->mLayoutEngine.SetDefaultLineSpacing(lineSpacing); - mImpl->mRecalculateNaturalSize = true; - - RelayoutForNewLineSize(); - return true; - } - return false; + return mImpl->SetDefaultLineSpacing(lineSpacing); } float Controller::GetDefaultLineSpacing() const @@ -1050,15 +900,7 @@ float Controller::GetDefaultLineSpacing() const bool Controller::SetDefaultLineSize(float lineSize) { - if(std::fabs(lineSize - mImpl->mLayoutEngine.GetDefaultLineSize()) > Math::MACHINE_EPSILON_1000) - { - mImpl->mLayoutEngine.SetDefaultLineSize(lineSize); - mImpl->mRecalculateNaturalSize = true; - - RelayoutForNewLineSize(); - return true; - } - return false; + return mImpl->SetDefaultLineSize(lineSize); } float Controller::GetDefaultLineSize() const @@ -1068,52 +910,12 @@ float Controller::GetDefaultLineSize() const void Controller::SetInputColor(const Vector4& color) { - if(mImpl->mEventData) - { - mImpl->mEventData->mInputStyle.textColor = color; - mImpl->mEventData->mInputStyle.isDefaultColor = false; - - if(EventData::SELECTING == mImpl->mEventData->mState || EventData::EDITING == mImpl->mEventData->mState || EventData::INACTIVE == mImpl->mEventData->mState) - { - if(EventData::SELECTING == mImpl->mEventData->mState) - { - const bool handlesCrossed = mImpl->mEventData->mLeftSelectionPosition > mImpl->mEventData->mRightSelectionPosition; - - // Get start and end position of selection - const CharacterIndex startOfSelectedText = handlesCrossed ? mImpl->mEventData->mRightSelectionPosition : mImpl->mEventData->mLeftSelectionPosition; - const Length lengthOfSelectedText = (handlesCrossed ? mImpl->mEventData->mLeftSelectionPosition : mImpl->mEventData->mRightSelectionPosition) - startOfSelectedText; - - // Add the color run. - const VectorBase::SizeType numberOfRuns = mImpl->mModel->mLogicalModel->mColorRuns.Count(); - mImpl->mModel->mLogicalModel->mColorRuns.Resize(numberOfRuns + 1u); - - ColorRun& colorRun = *(mImpl->mModel->mLogicalModel->mColorRuns.Begin() + numberOfRuns); - colorRun.color = color; - colorRun.characterRun.characterIndex = startOfSelectedText; - colorRun.characterRun.numberOfCharacters = lengthOfSelectedText; - - mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText; - mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText; - mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText; - } - else - { - mImpl->mTextUpdateInfo.mCharacterIndex = 0; - mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters; - mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count(); - } - - // Request to relayout. - mImpl->mOperationsPending = static_cast(mImpl->mOperationsPending | COLOR); - mImpl->RequestRelayout(); - } - } + InputProperties::SetInputColor(*this, color); } const Vector4& Controller::GetInputColor() const { - // Return event text input color if we have it, otherwise just return the default text's color - return mImpl->mEventData ? mImpl->mEventData->mInputStyle.textColor : mImpl->mTextColor; + return InputProperties::GetInputColor(*this); } void Controller::SetInputFontFamily(const std::string& fontFamily) @@ -1183,81 +985,62 @@ float Controller::GetInputFontPointSize() const void Controller::SetInputLineSpacing(float lineSpacing) { - if(mImpl->mEventData) - { - mImpl->mEventData->mInputStyle.lineSpacing = lineSpacing; - mImpl->mEventData->mInputStyle.isLineSpacingDefined = true; - } + InputProperties::SetInputLineSpacing(*this, lineSpacing); } float Controller::GetInputLineSpacing() const { - return mImpl->mEventData ? mImpl->mEventData->mInputStyle.lineSpacing : 0.0f; + return InputProperties::GetInputLineSpacing(*this); } void Controller::SetInputShadowProperties(const std::string& shadowProperties) { - if(mImpl->mEventData) - { - mImpl->mEventData->mInputStyle.shadowProperties = shadowProperties; - } + InputProperties::SetInputShadowProperties(*this, shadowProperties); } const std::string& Controller::GetInputShadowProperties() const { - return mImpl->mEventData ? mImpl->mEventData->mInputStyle.shadowProperties : EMPTY_STRING; + return InputProperties::GetInputShadowProperties(*this); } void Controller::SetInputUnderlineProperties(const std::string& underlineProperties) { - if(mImpl->mEventData) - { - mImpl->mEventData->mInputStyle.underlineProperties = underlineProperties; - } + InputProperties::SetInputUnderlineProperties(*this, underlineProperties); } const std::string& Controller::GetInputUnderlineProperties() const { - return mImpl->mEventData ? mImpl->mEventData->mInputStyle.underlineProperties : EMPTY_STRING; + return InputProperties::GetInputUnderlineProperties(*this); } void Controller::SetInputEmbossProperties(const std::string& embossProperties) { - if(mImpl->mEventData) - { - mImpl->mEventData->mInputStyle.embossProperties = embossProperties; - } + InputProperties::SetInputEmbossProperties(*this, embossProperties); } const std::string& Controller::GetInputEmbossProperties() const { - return mImpl->mEventData ? mImpl->mEventData->mInputStyle.embossProperties : GetDefaultEmbossProperties(); + return InputProperties::GetInputEmbossProperties(*this); } void Controller::SetInputOutlineProperties(const std::string& outlineProperties) { - if(mImpl->mEventData) - { - mImpl->mEventData->mInputStyle.outlineProperties = outlineProperties; - } + InputProperties::SetInputOutlineProperties(*this, outlineProperties); } const std::string& Controller::GetInputOutlineProperties() const { - return mImpl->mEventData ? mImpl->mEventData->mInputStyle.outlineProperties : GetDefaultOutlineProperties(); + return InputProperties::GetInputOutlineProperties(*this); } void Controller::SetInputModePassword(bool passwordInput) { - if(mImpl->mEventData) - { - mImpl->mEventData->mPasswordInput = passwordInput; - } + InputProperties::SetInputModePassword(*this, passwordInput); } bool Controller::IsInputModePassword() { - return mImpl->mEventData && mImpl->mEventData->mPasswordInput; + return InputProperties::IsInputModePassword(*this); } void Controller::SetNoTextDoubleTapAction(NoTextTap::Action action) @@ -1506,23 +1289,12 @@ void Controller::RequestRelayout() bool Controller::IsInputStyleChangedSignalsQueueEmpty() { - return (NULL == mImpl->mEventData) || (0u == mImpl->mEventData->mInputStyleChangedQueue.Count()); + return mImpl->IsInputStyleChangedSignalsQueueEmpty(); } void Controller::ProcessInputStyleChangedSignals() { - if(mImpl->mEventData) - { - if(mImpl->mEditableControlInterface) - { - // Emit the input style changed signal for each mask - std::for_each(mImpl->mEventData->mInputStyleChangedQueue.begin(), - mImpl->mEventData->mInputStyleChangedQueue.end(), - [&](const auto mask) { mImpl->mEditableControlInterface->InputStyleChanged(mask); } ); - } - - mImpl->mEventData->mInputStyleChangedQueue.Clear(); - } + mImpl->ProcessInputStyleChangedSignals(); } void Controller::KeyboardFocusGainEvent() @@ -1719,52 +1491,21 @@ bool Controller::IsEditable() const void Controller::SetEditable(bool editable) { mImpl->SetEditable(editable); - if(mImpl->mEventData && mImpl->mEventData->mDecorator) - { - mImpl->mEventData->mDecorator->SetEditable(editable); - } } void Controller::ScrollBy(Vector2 scroll) { - if(mImpl->mEventData && (fabs(scroll.x) > Math::MACHINE_EPSILON_0 || fabs(scroll.y) > Math::MACHINE_EPSILON_0)) - { - const Vector2& layoutSize = mImpl->mModel->mVisualModel->GetLayoutSize(); - const Vector2 currentScroll = mImpl->mModel->mScrollPosition; - - scroll.x = -scroll.x; - scroll.y = -scroll.y; - - if(fabs(scroll.x) > Math::MACHINE_EPSILON_0) - { - mImpl->mModel->mScrollPosition.x += scroll.x; - mImpl->ClampHorizontalScroll(layoutSize); - } - - if(fabs(scroll.y) > Math::MACHINE_EPSILON_0) - { - mImpl->mModel->mScrollPosition.y += scroll.y; - mImpl->ClampVerticalScroll(layoutSize); - } - - if(mImpl->mModel->mScrollPosition != currentScroll) - { - mImpl->mEventData->mDecorator->UpdatePositions(mImpl->mModel->mScrollPosition - currentScroll); - mImpl->RequestRelayout(); - } - } + mImpl->ScrollBy(scroll); } float Controller::GetHorizontalScrollPosition() { - // Scroll values are negative internally so we convert them to positive numbers - return mImpl->mEventData ? -mImpl->mModel->mScrollPosition.x : 0.0f; + return mImpl->GetHorizontalScrollPosition(); } float Controller::GetVerticalScrollPosition() { - // Scroll values are negative internally so we convert them to positive numbers - return mImpl->mEventData ? -mImpl->mModel->mScrollPosition.y : 0.0f; + return mImpl->GetVerticalScrollPosition(); } void Controller::DecorationEvent(HandleType handleType, HandleState state, float x, float y) @@ -1863,39 +1604,6 @@ void Controller::ShowPlaceholderText() PlaceholderHandler::ShowPlaceholderText(*this); } -void Controller::ClearFontData() -{ - if(mImpl->mFontDefaults) - { - mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID - } - - // Set flags to update the model. - mImpl->mTextUpdateInfo.mCharacterIndex = 0u; - mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters; - mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count(); - - mImpl->mTextUpdateInfo.mClearAll = true; - mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true; - mImpl->mRecalculateNaturalSize = true; - - mImpl->mOperationsPending = static_cast(mImpl->mOperationsPending | - VALIDATE_FONTS | - SHAPE_TEXT | - BIDI_INFO | - GET_GLYPH_METRICS | - LAYOUT | - UPDATE_LAYOUT_SIZE | - REORDER | - ALIGN); -} - -void Controller::ClearStyleData() -{ - mImpl->mModel->mLogicalModel->mColorRuns.Clear(); - mImpl->mModel->mLogicalModel->ClearFontDescriptionRuns(); -} - void Controller::ResetCursorPosition(CharacterIndex cursorIndex) { // Reset the cursor position @@ -1913,20 +1621,7 @@ void Controller::ResetCursorPosition(CharacterIndex cursorIndex) CharacterIndex Controller::GetCursorPosition() { - if(!mImpl->mEventData) - return 0; - - return mImpl->mEventData->mPrimaryCursorPosition; -} - -void Controller::ResetScrollPosition() -{ - if(mImpl->mEventData) - { - // Reset the scroll position. - mImpl->mModel->mScrollPosition = Vector2::ZERO; - mImpl->mEventData->mScrollAfterUpdatePosition = true; - } + return mImpl->mEventData ? mImpl->mEventData->mPrimaryCursorPosition : 0; } void Controller::SetControlInterface(ControlInterface* controlInterface) diff --git a/dali-toolkit/internal/text/text-controller.h b/dali-toolkit/internal/text/text-controller.h index 149e788..a5bc8b4 100644 --- a/dali-toolkit/internal/text/text-controller.h +++ b/dali-toolkit/internal/text/text-controller.h @@ -1902,26 +1902,6 @@ private: // Helpers. */ void ShowPlaceholderText(); - /** - * @brief Helper to clear font-specific data (only). - */ - void ClearFontData(); - - /** - * @brief Helper to clear text's style data. - */ - void ClearStyleData(); - - /** - * @brief Used to reset the scroll position after setting a new text. - */ - void ResetScrollPosition(); - - /** - * @brief fill needed relayout parameters when line size is changed & request relayout. - */ - void RelayoutForNewLineSize(); - private: // Private contructors & copy operator. /** * @brief Private constructor. @@ -1962,6 +1942,7 @@ public: private: struct EventHandler; struct InputFontHandler; + struct InputProperties; struct PlaceholderHandler; struct Relayouter; struct TextUpdater; diff --git a/dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp b/dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp index d8d61d3..da7fb09 100644 --- a/dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp +++ b/dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp @@ -833,10 +833,11 @@ Shader AnimatedImageVisual::GenerateShader() const Shader shader; shader = mImageVisualShaderFactory.GetShader( mFactoryCache, - TextureAtlas::DISABLED, - defaultWrapMode ? DefaultTextureWrapMode::APPLY : DefaultTextureWrapMode::DO_NOT_APPLY, - IsRoundedCornerRequired() ? RoundedCorner::ENABLED : RoundedCorner::DISABLED, - IsBorderlineRequired() ? Borderline::ENABLED : Borderline::DISABLED); + ImageVisualShaderFeature::FeatureBuilder() + .ApplyDefaultTextureWrapMode(defaultWrapMode) + .EnableRoundedCorner(IsRoundedCornerRequired()) + .EnableBorderline(IsBorderlineRequired()) + ); return shader; } diff --git a/dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.cpp b/dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.cpp index 26495bf..75b71f2 100644 --- a/dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.cpp +++ b/dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.cpp @@ -626,10 +626,9 @@ Shader AnimatedVectorImageVisual::GenerateShader() const { shader = mImageVisualShaderFactory.GetShader( mFactoryCache, - TextureAtlas::DISABLED, - DefaultTextureWrapMode::APPLY, - IsRoundedCornerRequired() ? RoundedCorner::ENABLED : RoundedCorner::DISABLED, - IsBorderlineRequired() ? Borderline::ENABLED : Borderline::DISABLED + ImageVisualShaderFeature::FeatureBuilder() + .EnableRoundedCorner(IsRoundedCornerRequired()) + .EnableBorderline(IsBorderlineRequired()) ); } return shader; diff --git a/dali-toolkit/internal/visuals/image-visual-shader-factory.cpp b/dali-toolkit/internal/visuals/image-visual-shader-factory.cpp index 7f2cac7..f5753fa 100644 --- a/dali-toolkit/internal/visuals/image-visual-shader-factory.cpp +++ b/dali-toolkit/internal/visuals/image-visual-shader-factory.cpp @@ -18,6 +18,7 @@ #include // EXTERNAL INCLUDES +#include // INTERNAL INCLUDES #include @@ -40,9 +41,41 @@ static std::string gVertexShader; // global string variable to caching complate fragment shader (no atlas) static std::string gFragmentShaderNoAtlas; +const int NATIVE_SHADER_TYPE_OFFSET = VisualFactoryCache::ShaderType::NATIVE_IMAGE_SHADER - VisualFactoryCache::ShaderType::IMAGE_SHADER; + } // unnamed namespace +namespace ImageVisualShaderFeature +{ +FeatureBuilder& FeatureBuilder::EnableTextureAtlas(bool enableAtlas) +{ + mTextureAtlas = (enableAtlas ? TextureAtlas::ENABLED : TextureAtlas::DISABLED); + return *this; +} +FeatureBuilder& FeatureBuilder::ApplyDefaultTextureWrapMode(bool applyDefaultTextureWrapMode) +{ + mDefaultTextureWrapMode = (applyDefaultTextureWrapMode ? DefaultTextureWrapMode::APPLY : DefaultTextureWrapMode::DO_NOT_APPLY); + return *this; +} +FeatureBuilder& FeatureBuilder::EnableRoundedCorner(bool enableRoundedCorner) +{ + mRoundedCorner = (enableRoundedCorner ? RoundedCorner::ENABLED : RoundedCorner::DISABLED); + return *this; +} +FeatureBuilder& FeatureBuilder::EnableBorderline(bool enableBorderline) +{ + mBorderline = (enableBorderline ? Borderline::ENABLED : Borderline::DISABLED); + return *this; +} +FeatureBuilder& FeatureBuilder::SetTextureForFragmentShaderCheck(const Dali::Texture& texture) +{ + mTexture = texture; + return *this; +} +} // namespace ImageVisualShaderFeature + ImageVisualShaderFactory::ImageVisualShaderFactory() +: mFragmentShaderNeedChange(ImageVisualShaderFeature::ChangeFragmentShader::UNDECIDED) { } @@ -50,13 +83,22 @@ ImageVisualShaderFactory::~ImageVisualShaderFactory() { } -Shader ImageVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, TextureAtlas atlasing, DefaultTextureWrapMode defaultTextureWrapping, RoundedCorner roundedCorner, Borderline borderline) +Shader ImageVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, const ImageVisualShaderFeature::FeatureBuilder& featureBuilder) { Shader shader; VisualFactoryCache::ShaderType shaderType = VisualFactoryCache::IMAGE_SHADER; - if(atlasing == TextureAtlas::ENABLED) + + const auto& atlasing = featureBuilder.mTextureAtlas; + const auto& defaultTextureWrapping = featureBuilder.mDefaultTextureWrapMode; + const auto& roundedCorner = featureBuilder.mRoundedCorner; + const auto& borderline = featureBuilder.mBorderline; + const auto& changeFragmentShader = (featureBuilder.mTexture && DevelTexture::IsNative(featureBuilder.mTexture)) + ? ImageVisualShaderFeature::ChangeFragmentShader::NEED_CHANGE + : ImageVisualShaderFeature::ChangeFragmentShader::DONT_CHANGE; + + if(atlasing == ImageVisualShaderFeature::TextureAtlas::ENABLED) { - if(defaultTextureWrapping == DefaultTextureWrapMode::APPLY) + if(defaultTextureWrapping == ImageVisualShaderFeature::DefaultTextureWrapMode::APPLY) { shaderType = VisualFactoryCache::IMAGE_SHADER_ATLAS_DEFAULT_WRAP; } @@ -67,9 +109,9 @@ Shader ImageVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, Tex } else { - if(roundedCorner == RoundedCorner::ENABLED) + if(roundedCorner == ImageVisualShaderFeature::RoundedCorner::ENABLED) { - if(borderline == Borderline::ENABLED) + if(borderline == ImageVisualShaderFeature::Borderline::ENABLED) { shaderType = VisualFactoryCache::IMAGE_SHADER_ROUNDED_BORDERLINE; } @@ -80,21 +122,28 @@ Shader ImageVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, Tex } else { - if(borderline == Borderline::ENABLED) + if(borderline == ImageVisualShaderFeature::Borderline::ENABLED) { shaderType = VisualFactoryCache::IMAGE_SHADER_BORDERLINE; } } } + if(changeFragmentShader == ImageVisualShaderFeature::ChangeFragmentShader::NEED_CHANGE && + (mFragmentShaderNeedChange == ImageVisualShaderFeature::ChangeFragmentShader::UNDECIDED || + mFragmentShaderNeedChange == ImageVisualShaderFeature::ChangeFragmentShader::NEED_CHANGE)) + { + shaderType = static_cast(static_cast(shaderType) + NATIVE_SHADER_TYPE_OFFSET); + } + shader = factoryCache.GetShader(shaderType); if(!shader) { std::string vertexShaderPrefixList; std::string fragmentShaderPrefixList; - if(atlasing == TextureAtlas::ENABLED) + if(atlasing == ImageVisualShaderFeature::TextureAtlas::ENABLED) { - if(defaultTextureWrapping == DefaultTextureWrapMode::APPLY) + if(defaultTextureWrapping == ImageVisualShaderFeature::DefaultTextureWrapMode::APPLY) { fragmentShaderPrefixList += "#define ATLAS_DEFAULT_WARP 1\n"; } @@ -105,20 +154,70 @@ Shader ImageVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, Tex } else { - if(roundedCorner == RoundedCorner::ENABLED) + if(roundedCorner == ImageVisualShaderFeature::RoundedCorner::ENABLED) { vertexShaderPrefixList += "#define IS_REQUIRED_ROUNDED_CORNER 1\n"; fragmentShaderPrefixList += "#define IS_REQUIRED_ROUNDED_CORNER 1\n"; } - if(borderline == Borderline::ENABLED) + if(borderline == ImageVisualShaderFeature::Borderline::ENABLED) { vertexShaderPrefixList += "#define IS_REQUIRED_BORDERLINE 1\n"; fragmentShaderPrefixList += "#define IS_REQUIRED_BORDERLINE 1\n"; } } - shader = Shader::New(Dali::Shader::GetVertexShaderPrefix() + vertexShaderPrefixList + SHADER_IMAGE_VISUAL_SHADER_VERT.data(), - Dali::Shader::GetFragmentShaderPrefix() + fragmentShaderPrefixList + SHADER_IMAGE_VISUAL_SHADER_FRAG.data()); + std::string vertexShader = std::string(Dali::Shader::GetVertexShaderPrefix() + vertexShaderPrefixList + SHADER_IMAGE_VISUAL_SHADER_VERT.data()); + std::string fragmentShader = std::string(Dali::Shader::GetFragmentShaderPrefix() + fragmentShaderPrefixList + SHADER_IMAGE_VISUAL_SHADER_FRAG.data()); + + if(changeFragmentShader == ImageVisualShaderFeature::ChangeFragmentShader::NEED_CHANGE) + { + if(DALI_UNLIKELY(mFragmentShaderNeedChange == ImageVisualShaderFeature::ChangeFragmentShader::UNDECIDED)) + { + // NOTE : This routine will run exist one times. + // + // First, we will run ApplyNativeFragmentShader + // - If fragment shader is modified, then current platform allow to change fragment shader. + // We cache this result mFragmentShaderNeedChange = ChangeFragmentShader::NEED_CHANGE. + // - If fragment shader is not modified, then current platform will always don't change fragment shader. + // We cache this result mFragmentShaderNeedChange = ChangeFragmentShader::DONT_CHANGE. + // And change current shaderType into normal image range. + // After cached the result, shaderType never become NATIVE_IMAGE_SHADER anymore. + // Second, save shader result. + + // Try to apply fragmentShader + bool modified = DevelTexture::ApplyNativeFragmentShader(featureBuilder.mTexture, fragmentShader); + if(modified) + { + // Now we know that fragment shader need to change. + mFragmentShaderNeedChange = ImageVisualShaderFeature::ChangeFragmentShader::NEED_CHANGE; + } + else + { + // Now we know that fragment shader even don't need to change. + // We can skip ApplyNativeFragmentShader routine after now. + mFragmentShaderNeedChange = ImageVisualShaderFeature::ChangeFragmentShader::DONT_CHANGE; + + // Now we need normal shader type + // So decrease NATIVE_SHADER_TYPE_OFFSET. + shaderType = static_cast(static_cast(shaderType) - NATIVE_SHADER_TYPE_OFFSET); + + // If we already compiled this type already, just use that cached shader. + // Else, just go forward. + shader = factoryCache.GetShader(shaderType); + if(shader) + { + return shader; + } + } + } + else if(mFragmentShaderNeedChange == ImageVisualShaderFeature::ChangeFragmentShader::NEED_CHANGE) + { + // Always need to apply fragmentShader + bool modified = DevelTexture::ApplyNativeFragmentShader(featureBuilder.mTexture, fragmentShader); + DALI_ASSERT_ALWAYS(modified && "NativeImageTexture need to change fragment shader. But DALI default image shader doesn't changed!"); + } + } + shader = Shader::New(vertexShader, fragmentShader); shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT); factoryCache.SaveShader(shaderType, shader); } diff --git a/dali-toolkit/internal/visuals/image-visual-shader-factory.h b/dali-toolkit/internal/visuals/image-visual-shader-factory.h index 604b49b..1459614 100644 --- a/dali-toolkit/internal/visuals/image-visual-shader-factory.h +++ b/dali-toolkit/internal/visuals/image-visual-shader-factory.h @@ -29,41 +29,101 @@ namespace Toolkit { namespace Internal { + +/** + * ImageVisualShaderFeature contains feature lists what image visual shader need to know. + */ +namespace ImageVisualShaderFeature +{ +namespace TextureAtlas +{ /** * @brief Whether use texture with atlas, or not */ -enum class TextureAtlas +enum Type { - DISABLED = 0, ///< Image visual use ATLAS - ENABLED ///< Image visual doesn't use ATLAS + DISABLED = 0, ///< Image visual doesn't use ATLAS + ENABLED ///< Image visual uses ATLAS }; +} // namespace TextureAtlas +namespace DefaultTextureWrapMode +{ /** * @brief Whether apply to texture wraping in default, or not */ -enum class DefaultTextureWrapMode +enum Type { - DO_NOT_APPLY = 0, ///< Image visual doesn't apply to wraping texture in default - APPLY ///< Image visual apply to wraping texture in default + APPLY = 0, ///< Image visual applies to wraping texture in default + DO_NOT_APPLY ///< Image visual doesn't apply to wraping texture in default }; +} // namespace DefaultTextureWrapMode +namespace RoundedCorner +{ /** * @brief Whether use rounded corner, or not */ -enum class RoundedCorner +enum Type { DISABLED = 0, ///< Image visual doesn't use rounded corner - ENABLED ///< Image visual use rounded corner + ENABLED ///< Image visual uses rounded corner }; +} // namespace RoundedCorner +namespace Borderline +{ /** * @brief Whether use borderline, or not */ -enum class Borderline +enum Type { DISABLED = 0, ///< Image visual doesn't use borderline - ENABLED ///< Image visual use borderline + ENABLED ///< Image visual uses borderline }; +} // namespace Borderline + +namespace ChangeFragmentShader +{ +/** + * @brief Whether native image change the default fragment shader, or not + */ +enum Type +{ + DONT_CHANGE = 0, ///< Native image doesn't change default fragment shader. + NEED_CHANGE, ///< Native image changes default fragment shader. We need another shader cache. + UNDECIDED, ///< Undecided. +}; +} // namespace ChangeFragmentShader + +/** + * @brief Collection of current image visual feature. Only use for ImageVisualShaderFactory::GetShader() + */ +struct FeatureBuilder +{ + FeatureBuilder() + : mTextureAtlas(TextureAtlas::DISABLED), + mDefaultTextureWrapMode(DefaultTextureWrapMode::APPLY), + mRoundedCorner(RoundedCorner::DISABLED), + mBorderline(Borderline::DISABLED), + mTexture() + { + } + + FeatureBuilder& EnableTextureAtlas(bool enableTextureAtlas); + FeatureBuilder& ApplyDefaultTextureWrapMode(bool applyDefaultTextureWrapMode); + FeatureBuilder& EnableRoundedCorner(bool enableRoundedCorner); + FeatureBuilder& EnableBorderline(bool enableBorderline); + FeatureBuilder& SetTextureForFragmentShaderCheck(const Dali::Texture& texture); + + TextureAtlas::Type mTextureAtlas : 2; ///< Whether use texture with atlas, or not. default as TextureAtlas::DISABLED + DefaultTextureWrapMode::Type mDefaultTextureWrapMode : 2; ///< Whether apply to texture wraping in default, or not. default as DefaultTextureWrapMode::APPLY + RoundedCorner::Type mRoundedCorner : 2; ///< Whether use rounded corner, or not. default as RoundedCorner::DISABLED + Borderline::Type mBorderline : 2; ///< Whether use borderline, or not. default as Borderline::DISABLED + Dali::Texture mTexture; ///< Texture to check whether we need to change fragment shader or not +}; + +} // namespace ImageVisualShaderFactoryFeature /** * ImageVisualShaderFactory is an object that provides and shares shaders between image visuals @@ -83,23 +143,21 @@ public: ~ImageVisualShaderFactory(); /** - * Get the standard image rendering shader. + * @brief Get the standard image rendering shader. * @param[in] factoryCache A pointer pointing to the VisualFactoryCache object - * @param[in] atlasing Whether texture atlasing is applied. - * @param[in] defaultTextureWrapping Whether the default texture wrap mode is applied. - * @param[in] roundedCorner Whether the rounded corder is applied. - * @param[in] borderline Whether the borderline of visual is applied. + * @param[in] featureBuilder Collection of current image shader's features + * @return The standard image rendering shader with features. */ - Shader GetShader(VisualFactoryCache& factoryCache, TextureAtlas atlasing, DefaultTextureWrapMode defaultTextureWrapping, RoundedCorner roundedCorner, Borderline borderline); + Shader GetShader(VisualFactoryCache& factoryCache, const ImageVisualShaderFeature::FeatureBuilder& featureBuilder); /** - * Request the default vertex shader source. + * @brief Request the default vertex shader source. * @return The default vertex shader source. */ std::string_view GetVertexShaderSource(); /** - * Request the default fragment shader source. + * @brief Request the default fragment shader source. * @return The default fragment shader source. */ std::string_view GetFragmentShaderSource(); @@ -116,6 +174,19 @@ protected: ImageVisualShaderFactory& operator=(const ImageVisualShaderFactory& rhs); private: + + /** + * @brief Cached information whether native image should change fragment shader. + * Default it is ChangeFragmentShader::UNDECIDED. + * If we have any chance to check native image source apply fragment shader, + * this vaule will be changed one of these : ChangeFragmentShader::DONT_CHANGE or ChangeFragmentShader::NEED_CHANGE + * + * After result cached, this value will not be changed. + * + * If value is DONT_CHANGE, ImageVisualShaderFactory::GetShader never call ApplyNativeFragmentShader. + * Else, ImageVisualShaderFactory::GetShader will call ApplyNativeFragmentShader if native image source texture come. + */ + ImageVisualShaderFeature::ChangeFragmentShader::Type mFragmentShaderNeedChange : 3; }; } // namespace Internal diff --git a/dali-toolkit/internal/visuals/image/image-visual.cpp b/dali-toolkit/internal/visuals/image/image-visual.cpp index 7e404ca..650c508 100644 --- a/dali-toolkit/internal/visuals/image/image-visual.cpp +++ b/dali-toolkit/internal/visuals/image/image-visual.cpp @@ -974,59 +974,68 @@ Shader ImageVisual::GenerateShader() const { Shader shader; - std::string_view vertexShaderView; - bool usesWholeTexture = true; - if(mImpl->mCustomShader && !mImpl->mCustomShader->mVertexShader.empty()) - { - vertexShaderView = mImpl->mCustomShader->mVertexShader; - usesWholeTexture = false; // Impossible to tell. - } - else - { - vertexShaderView = mImageVisualShaderFactory.GetVertexShaderSource(); - } + bool usesWholeTexture = true; + const bool useStandardShader = !mImpl->mCustomShader; + const bool useNativeImage = (mTextures && DevelTexture::IsNative(mTextures.GetTexture(0))); - std::string_view fragmentShaderView; - if(mImpl->mCustomShader && !mImpl->mCustomShader->mFragmentShader.empty()) - { - fragmentShaderView = mImpl->mCustomShader->mFragmentShader; - } - else - { - fragmentShaderView = mImageVisualShaderFactory.GetFragmentShaderSource(); - } - - // If the texture is native, we may need to change prefix and sampler in - // the fragment shader - bool modifiedFragmentShader = false; - std::string fragmentShaderString; - if(mTextures && DevelTexture::IsNative(mTextures.GetTexture(0))) - { - Texture nativeTexture = mTextures.GetTexture(0); - fragmentShaderString = std::string(fragmentShaderView); - modifiedFragmentShader = DevelTexture::ApplyNativeFragmentShader(nativeTexture, fragmentShaderString); - fragmentShaderView = fragmentShaderString; - } - - const bool useStandardShader = !mImpl->mCustomShader && !modifiedFragmentShader; if(useStandardShader) { // Create and cache the standard shader shader = mImageVisualShaderFactory.GetShader( mFactoryCache, - mImpl->mFlags & Impl::IS_ATLASING_APPLIED ? TextureAtlas::ENABLED : TextureAtlas::DISABLED, - mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE ? DefaultTextureWrapMode::APPLY : DefaultTextureWrapMode::DO_NOT_APPLY, - IsRoundedCornerRequired() ? RoundedCorner::ENABLED : RoundedCorner::DISABLED, - IsBorderlineRequired() ? Borderline::ENABLED : Borderline::DISABLED + ImageVisualShaderFeature::FeatureBuilder() + .EnableTextureAtlas(mImpl->mFlags & Impl::IS_ATLASING_APPLIED && !useNativeImage) + .ApplyDefaultTextureWrapMode(mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE) + .EnableRoundedCorner(IsRoundedCornerRequired()) + .EnableBorderline(IsBorderlineRequired()) + .SetTextureForFragmentShaderCheck(useNativeImage ? mTextures.GetTexture(0) : Dali::Texture()) ); } - else if(mImpl->mCustomShader) - { - shader = Shader::New(vertexShaderView, fragmentShaderView, mImpl->mCustomShader->mHints); - } else { - shader = Shader::New(vertexShaderView, fragmentShaderView); + std::string_view vertexShaderView; + std::string_view fragmentShaderView; + + if(mImpl->mCustomShader && !mImpl->mCustomShader->mVertexShader.empty()) + { + vertexShaderView = mImpl->mCustomShader->mVertexShader; + usesWholeTexture = false; // Impossible to tell. + } + else + { + vertexShaderView = mImageVisualShaderFactory.GetVertexShaderSource(); + } + + if(mImpl->mCustomShader && !mImpl->mCustomShader->mFragmentShader.empty()) + { + fragmentShaderView = mImpl->mCustomShader->mFragmentShader; + } + else + { + fragmentShaderView = mImageVisualShaderFactory.GetFragmentShaderSource(); + } + + // If the texture is native, we may need to change prefix and sampler in + // the fragment shader + if(useNativeImage) + { + bool modifiedFragmentShader = false; + Texture nativeTexture = mTextures.GetTexture(0); + std::string fragmentShaderString = std::string(fragmentShaderView); + + modifiedFragmentShader = DevelTexture::ApplyNativeFragmentShader(nativeTexture, fragmentShaderString); + if(modifiedFragmentShader) + { + fragmentShaderView = fragmentShaderString; + } + + // Create shader here cause fragmentShaderString scope issue + shader = Shader::New(vertexShaderView, fragmentShaderView, mImpl->mCustomShader->mHints); + } + else + { + shader = Shader::New(vertexShaderView, fragmentShaderView, mImpl->mCustomShader->mHints); + } } if(usesWholeTexture) diff --git a/dali-toolkit/internal/visuals/npatch/npatch-visual.cpp b/dali-toolkit/internal/visuals/npatch/npatch-visual.cpp index e87e244..1f48eca 100644 --- a/dali-toolkit/internal/visuals/npatch/npatch-visual.cpp +++ b/dali-toolkit/internal/visuals/npatch/npatch-visual.cpp @@ -378,10 +378,7 @@ void NPatchVisual::OnInitialize() Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY); Shader shader = mImageVisualShaderFactory.GetShader( mFactoryCache, - TextureAtlas::DISABLED, - DefaultTextureWrapMode::APPLY, - RoundedCorner::DISABLED, - Borderline::DISABLED + ImageVisualShaderFeature::FeatureBuilder() ); mImpl->mRenderer = Renderer::New(geometry, shader); diff --git a/dali-toolkit/internal/visuals/svg/svg-visual.cpp b/dali-toolkit/internal/visuals/svg/svg-visual.cpp index 5d1fff1..25e84fd 100644 --- a/dali-toolkit/internal/visuals/svg/svg-visual.cpp +++ b/dali-toolkit/internal/visuals/svg/svg-visual.cpp @@ -378,10 +378,10 @@ Shader SvgVisual::GenerateShader() const { shader = mImageVisualShaderFactory.GetShader( mFactoryCache, - mAttemptAtlasing ? TextureAtlas::ENABLED : TextureAtlas::DISABLED, - DefaultTextureWrapMode::APPLY, - IsRoundedCornerRequired() ? RoundedCorner::ENABLED : RoundedCorner::DISABLED, - IsBorderlineRequired() ? Borderline::ENABLED : Borderline::DISABLED + ImageVisualShaderFeature::FeatureBuilder() + .EnableTextureAtlas(mAttemptAtlasing) + .EnableRoundedCorner(IsRoundedCornerRequired()) + .EnableBorderline(IsBorderlineRequired()) ); } else diff --git a/dali-toolkit/internal/visuals/visual-factory-cache.h b/dali-toolkit/internal/visuals/visual-factory-cache.h index b23c010..deae0b0 100644 --- a/dali-toolkit/internal/visuals/visual-factory-cache.h +++ b/dali-toolkit/internal/visuals/visual-factory-cache.h @@ -81,11 +81,15 @@ public: GRADIENT_SHADER_RADIAL_USER_SPACE_BORDERLINE, GRADIENT_SHADER_RADIAL_USER_SPACE_ROUNDED_BORDERLINE, IMAGE_SHADER, - IMAGE_SHADER_ATLAS_DEFAULT_WRAP, - IMAGE_SHADER_ATLAS_CUSTOM_WRAP, IMAGE_SHADER_ROUNDED_CORNER, IMAGE_SHADER_BORDERLINE, IMAGE_SHADER_ROUNDED_BORDERLINE, + IMAGE_SHADER_ATLAS_DEFAULT_WRAP, + IMAGE_SHADER_ATLAS_CUSTOM_WRAP, + NATIVE_IMAGE_SHADER, + NATIVE_IMAGE_SHADER_ROUNDED_CORNER, + NATIVE_IMAGE_SHADER_BORDERLINE, + NATIVE_IMAGE_SHADER_ROUNDED_BORDERLINE, NINE_PATCH_SHADER, NINE_PATCH_MASK_SHADER, TEXT_SHADER_MULTI_COLOR_TEXT, diff --git a/dali-toolkit/public-api/dali-toolkit-version.cpp b/dali-toolkit/public-api/dali-toolkit-version.cpp index 5f6856e..396764e 100644 --- a/dali-toolkit/public-api/dali-toolkit-version.cpp +++ b/dali-toolkit/public-api/dali-toolkit-version.cpp @@ -29,7 +29,7 @@ namespace Toolkit { const unsigned int TOOLKIT_MAJOR_VERSION = 2; const unsigned int TOOLKIT_MINOR_VERSION = 0; -const unsigned int TOOLKIT_MICRO_VERSION = 52; +const unsigned int TOOLKIT_MICRO_VERSION = 53; const char* const TOOLKIT_BUILD_DATE = __DATE__ " " __TIME__; #ifdef DEBUG_ENABLED diff --git a/packaging/dali-toolkit.spec b/packaging/dali-toolkit.spec index d9a1c86..c9d2a96 100644 --- a/packaging/dali-toolkit.spec +++ b/packaging/dali-toolkit.spec @@ -1,6 +1,6 @@ Name: dali2-toolkit Summary: Dali 3D engine Toolkit -Version: 2.0.52 +Version: 2.0.53 Release: 1 Group: System/Libraries License: Apache-2.0 and BSD-3-Clause and MIT