controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
}
+
+Vector<FontDescriptionRun> 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<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack(fontDescriptionRun);
+
+ return fontDescriptionRuns;
+}
+
} // namespace Text
} // namespace Toolkit
*/
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<FontDescriptionRun> 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
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<FontDescriptionRun> fontDescriptions; ///< Fonts which is used for text.
};
layoutSize,
textModel,
metrics,
- false,
+ data.markupProcessorEnabled,
LineWrap::WORD,
false,
Toolkit::DevelText::EllipsisPosition::END );
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
//////////////////////////////////////////////////////////
nullptr,
nullptr,
0u,
- nullptr
+ nullptr,
+ false,
},
{
"Latin script",
charactersPerGlyph02,
0u,
nullptr,
+ false,
fontDescriptions01
},
{
charactersPerGlyph03,
2u,
newParagraphGlyphs03,
+ false,
fontDescriptions02
},
{
charactersPerGlyph04,
0u,
nullptr,
+ false,
fontDescriptions03
},
{
charactersPerGlyph05,
1u,
newParagraphGlyphs05,
- fontDescriptions04
+ false,
+ fontDescriptions04,
},
{
"Latin script with some paragraphs. Update mid paragraph.",
charactersPerGlyph05,
1u,
newParagraphGlyphs06,
+ false,
fontDescriptions05
},
{
charactersPerGlyph05,
1u,
newParagraphGlyphs07,
+ false,
fontDescriptions06
},
};
charactersPerGlyph,
0u,
nullptr,
+ false,
fontDescriptions01
},
{
charactersPerGlyph,
0u,
nullptr,
+ false,
fontDescriptions02
}
};
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<FontDescriptionRun> fontDescriptionsColorVS16 =
+ CreateSingleFontDescription (characterRun, colorFontFamily, weight, width,
+ slant, size, familyDefined, weightDefined, widthDefined, slantDefined, sizeDefined);
+
+ Vector<FontDescriptionRun> fontDescriptionsColorVS15 =
+ CreateSingleFontDescription (characterRun, colorFontFamily, weight, width,
+ slant, size, familyDefined, weightDefined, widthDefined, slantDefined, sizeDefined);
+
+ Vector<FontDescriptionRun> fontDescriptionsTextVS16 =
+ CreateSingleFontDescription (characterRun, textFontFamily, weight, width,
+ slant, size, familyDefined, weightDefined, widthDefined, slantDefined, sizeDefined);
+
+ Vector<FontDescriptionRun> 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;
+}
END_TEST;
}
-
int UtcDaliImageVisualWithNativeImage(void)
{
ToolkitTestApplication application;
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<DummyControlImpl&>(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>();
+ std::string vertexShaderSource = (*outMap)["vertex"].Get<std::string>();
+
+ // 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;
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;
}
--- /dev/null
+/*
+ * 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 <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl-property-handler.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h>
+#include <dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-mode.h>
+
+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<bool>());
+ break;
+ }
+ case Toolkit::ScrollView::Property::PANNING_ENABLED:
+ {
+ scrollViewImpl.SetScrollSensitive(value.Get<bool>());
+ break;
+ }
+ case Toolkit::ScrollView::Property::AXIS_AUTO_LOCK_ENABLED:
+ {
+ scrollViewImpl.SetAxisAutoLock(value.Get<bool>());
+ break;
+ }
+ case Toolkit::ScrollView::Property::WHEEL_SCROLL_DISTANCE_STEP:
+ {
+ scrollViewImpl.SetWheelScrollDistanceStep(value.Get<Vector2>());
+ 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
--- /dev/null
+#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 <dali/public-api/object/base-object.h>
+#include <dali/public-api/object/property.h>
+
+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
#include <dali-toolkit/devel-api/controls/scroll-bar/scroll-bar.h>
#include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.h>
#include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-effect-impl.h>
+#include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl-property-handler.h>
#include <dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-mode.h>
#include <dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-constraints.h>
#include <dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.h>
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
{
mRulerX = ruler;
- UpdatePropertyDomain();
+ ScrollViewPropertyHandler::UpdatePropertyDomain(*this);
mConstraints.UpdateMainInternalConstraint(*this);
}
{
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();
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)
mMaxOvershoot = mUserMaxOvershoot;
}
}
- UpdatePropertyDomain();
+ ScrollViewPropertyHandler::UpdatePropertyDomain(*this);
mConstraints.UpdateMainInternalConstraint(*this);
if(IsOvershootEnabled())
{
}
}
-Vector2 ScrollView::GetOvershoot(Vector2& position) const
-{
- Vector3 size = Self().GetCurrentProperty<Vector3>(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
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<bool>());
- break;
- }
- case Toolkit::ScrollView::Property::PANNING_ENABLED:
- {
- scrollViewImpl.SetScrollSensitive(value.Get<bool>());
- break;
- }
- case Toolkit::ScrollView::Property::AXIS_AUTO_LOCK_ENABLED:
- {
- scrollViewImpl.SetAxisAutoLock(value.Get<bool>());
- break;
- }
- case Toolkit::ScrollView::Property::WHEEL_SCROLL_DISTANCE_STEP:
- {
- scrollViewImpl.SetWheelScrollDistanceStep(value.Get<Vector2>());
- 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)
class ScrollOvershootIndicator;
typedef IntrusivePtr<ScrollOvershootIndicator> ScrollOvershootIndicatorPtr;
+class ScrollViewPropertyHandler;
+
/**
* @copydoc Toolkit::ScrollView
*/
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();
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
*/
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&);
bool mTransientScrollBar : 1; ///< True if scroll-bar should be automatically show/hidden during/after panning
friend ScrollViewConstraints;
+ friend ScrollViewPropertyHandler;
};
/**
Text::RendererPtr renderer,
Text::ControllerPtr controller,
Text::DecoratorPtr decorator,
- float alignmentOffset,
+ float& alignmentOffset,
Actor& renderableActor,
Actor& backgroundActor,
Toolkit::Control& stencil,
* @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
Text::RendererPtr renderer,
Text::ControllerPtr controller,
Text::DecoratorPtr decorator,
- float alignmentOffset,
+ float& alignmentOffset,
Actor& renderableActor,
Actor& backgroundActor,
Toolkit::Control& stencil,
${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
${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
${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}
--- /dev/null
+
+/*
+ * 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 <dali-toolkit/internal/text/emoji-helper.h>
+
+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
--- /dev/null
+#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 <dali-toolkit/internal/text/text-definitions.h>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/emoji-character-properties.h>
+#include <dali/devel-api/text-abstraction/emoji-helper.h>
+#include <dali/devel-api/text-abstraction/script.h>
+
+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
#include <dali/integration-api/debug.h>
// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/emoji-helper.h>
#include <dali-toolkit/internal/text/multi-language-helper-functions.h>
namespace Dali
{
// 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()
// Traverse all characters and set the scripts.
const Length lastCharacter = startIndex + numberOfCharacters;
+
for(Length index = startIndex; index < lastCharacter; ++index)
{
Character character = *(textBuffer + index);
// 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;
// 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.
{
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 ) )
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);
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
{
Vector<ScriptRun>::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)
}
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;
//
// 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);
} // !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
{
// 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)
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<ScriptRun>& 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
private:
Vector<DefaultFonts*> mDefaultFontPerScriptCache; ///< Caches default fonts for a script.
Vector<ValidateFontsPerScript*> 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<ScriptRun>& scripts,
+ ScriptRunIndex& scriptIndex);
};
} // namespace Internal
--- /dev/null
+/*
+ * 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 <dali-toolkit/internal/text/text-controller-impl-data-clearer.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/text-controller-impl.h>
+#include <dali-toolkit/internal/text/text-run-container.h>
+
+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<BidirectionalLineInfoRun>::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<BidirectionalLineInfoRun>::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<GlyphIndex>::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<CharacterIndex>::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
--- /dev/null
+#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 <dali-toolkit/internal/text/text-controller.h>
+
+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
#include <dali-toolkit/internal/text/text-controller-impl.h>
// EXTERNAL INCLUDES
+#include <cmath>
#include <dali/integration-api/debug.h>
#include <dali/public-api/rendering/renderer.h>
+#include <dali/devel-api/adaptor-framework/window-devel.h>
// INTERNAL INCLUDES
#include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
#include <dali-toolkit/internal/text/character-set-conversion.h>
#include <dali-toolkit/internal/text/cursor-helper-functions.h>
#include <dali-toolkit/internal/text/text-control-interface.h>
+#include <dali-toolkit/internal/text/text-controller-impl-data-clearer.h>
#include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
#include <dali-toolkit/internal/text/text-controller-impl-model-updater.h>
#include <dali-toolkit/internal/text/text-editable-control-interface.h>
}
}
}
+
+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)
}
}
+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<Dali::LayoutDirection::Type>(DevelWindow::Get(actor).GetRootLayer().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>());
+ }
+ else
+ {
+ return static_cast<Dali::LayoutDirection::Type>(actor.GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>());
+ }
+}
+
void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters)
{
mTextUpdateInfo.mParagraphCharacterIndex = 0u;
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<BidirectionalLineInfoRun>::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<BidirectionalLineInfoRun>::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<GlyphIndex>::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<CharacterIndex>::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)
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)
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();
}
}
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,
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<OperationsMask>(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
}
}
+void Controller::Impl::SetAutoScrollEnabled(bool enable)
+{
+ if(mLayoutEngine.GetLayout() == Layout::Engine::SINGLE_LINE_BOX)
+ {
+ mOperationsPending = static_cast<OperationsMask>(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<OperationsMask>(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<OperationsMask>(LAYOUT |
+ UPDATE_LAYOUT_SIZE |
+ ALIGN |
+ REORDER);
+
+ mTextUpdateInfo.mFullRelayoutNeeded = true;
+ mOperationsPending = static_cast<OperationsMask>(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<OperationsMask>(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<OperationsMask>(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<OperationsMask>(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<OperationsMask>(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<OperationsMask>(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<OperationsMask>(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
}
/**
+ * @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.
*
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.
float GetDefaultFontLineHeight();
/**
+ * @copydoc Controller::SetDefaultLineSpacing
+ */
+ bool SetDefaultLineSpacing(float lineSpacing);
+
+ /**
+ * @copydoc Controller::SetDefaultLineSize
+ */
+ bool SetDefaultLineSize(float lineSize);
+
+ /**
* @copydoc Text::Controller::GetPrimaryCursorPosition()
*/
CharacterIndex GetPrimaryCursorPosition() const;
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.
*/
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.
--- /dev/null
+/*
+ * 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 <dali-toolkit/internal/text/text-controller-input-properties.h>
+
+// EXTERNAL INCLUDES
+//#include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
+//#include <dali/devel-api/adaptor-framework/window-devel.h>
+//#include <dali/integration-api/debug.h>
+#include <memory.h>
+#include <cmath>
+#include <limits>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
+#include <dali-toolkit/internal/text/text-controller-event-handler.h>
+#include <dali-toolkit/internal/text/text-controller-impl.h>
+#include <dali-toolkit/internal/text/text-controller-input-font-handler.h>
+#include <dali-toolkit/internal/text/text-controller-placeholder-handler.h>
+#include <dali-toolkit/internal/text/text-controller-relayouter.h>
+#include <dali-toolkit/internal/text/text-controller-text-updater.h>
+#include <dali-toolkit/internal/text/text-editable-control-interface.h>
+
+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<OperationsMask>(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
--- /dev/null
+#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 <dali/devel-api/adaptor-framework/input-method-context.h>
+#include <dali/public-api/events/gesture.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/text-controller.h>
+//#include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
+//#include <dali-toolkit/devel-api/controls/text-controls/text-selection-popup-callback-interface.h>
+//#include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
+//#include <dali-toolkit/internal/text/decorator/text-decorator.h>
+//#include <dali-toolkit/internal/text/hidden-text.h>
+//#include <dali-toolkit/internal/text/input-filter.h>
+//#include <dali-toolkit/internal/text/layouts/layout-engine.h>
+//#include <dali-toolkit/internal/text/text-anchor-control-interface.h>
+//#include <dali-toolkit/internal/text/text-model-interface.h>
+//#include <dali-toolkit/internal/text/text-selectable-control-interface.h>
+//#include <dali-toolkit/public-api/text/text-enumerations.h>
+
+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
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<OperationsMask>(CONVERT_TO_UTF32 |
model->mElideEnabled = actualellipsis;
impl.mFontDefaults->mFitPointSize = pointSizeArray[bestSizeIndex];
impl.mFontDefaults->sizeDefined = true;
- controller.ClearFontData();
+ impl.ClearFontData();
}
}
// Reset the scroll position in inactive state
if(elideTextEnabled && (impl.mEventData->mState == EventData::INACTIVE))
{
- controller.ResetScrollPosition();
+ impl.ResetScrollPosition();
}
}
ResetText(controller);
// Remove the style.
- controller.ClearStyleData();
+ impl.ClearStyleData();
CharacterIndex lastCursorIndex = 0u;
controller.ResetCursorPosition(lastCursorIndex);
// Scrolls the text to make the cursor visible.
- controller.ResetScrollPosition();
+ impl.ResetScrollPosition();
impl.RequestRelayout();
#include <dali-toolkit/internal/text/text-controller-event-handler.h>
#include <dali-toolkit/internal/text/text-controller-impl.h>
#include <dali-toolkit/internal/text/text-controller-input-font-handler.h>
+#include <dali-toolkit/internal/text/text-controller-input-properties.h>
#include <dali-toolkit/internal/text/text-controller-placeholder-handler.h>
#include <dali-toolkit/internal/text/text-controller-relayouter.h>
#include <dali-toolkit/internal/text/text-controller-text-updater.h>
mImpl->mMetrics->SetGlyphType(glyphType);
// Clear the font-specific data
- ClearFontData();
+ mImpl->ClearFontData();
mImpl->RequestRelayout();
}
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<OperationsMask>(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<OperationsMask>(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;
}
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
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<OperationsMask>(LAYOUT |
- UPDATE_LAYOUT_SIZE |
- ALIGN |
- REORDER);
-
- mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true;
- mImpl->mOperationsPending = static_cast<OperationsMask>(mImpl->mOperationsPending | layoutOperations);
-
- // Need to recalculate natural size
- mImpl->mRecalculateNaturalSize = true;
-
- mImpl->RequestRelayout();
- }
+ mImpl->SetMultiLineEnabled(enable);
}
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<OperationsMask>(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
void Controller::SetVerticalAlignment(VerticalAlignment::Type alignment)
{
- if(alignment != mImpl->mModel->mVerticalAlignment)
- {
- // Set the alignment.
- mImpl->mModel->mVerticalAlignment = alignment;
- mImpl->mOperationsPending = static_cast<OperationsMask>(mImpl->mOperationsPending | ALIGN);
- mImpl->RequestRelayout();
- }
+ mImpl->SetVerticalAlignment(alignment);
}
VerticalAlignment::Type Controller::GetVerticalAlignment() const
void Controller::SetLineWrapMode(Text::LineWrap::Mode lineWrapMode)
{
- if(lineWrapMode != mImpl->mModel->mLineWrapMode)
- {
- // Update Text layout for applying wrap mode
- mImpl->mOperationsPending = static_cast<OperationsMask>(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<OperationsMask>(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
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
UpdateCursorPosition(mImpl->mEventData);
// Clear the font-specific data
- ClearFontData();
+ mImpl->ClearFontData();
mImpl->RequestRelayout();
}
UpdateCursorPosition(mImpl->mEventData);
// Clear the font-specific data
- ClearFontData();
+ mImpl->ClearFontData();
mImpl->RequestRelayout();
}
UpdateCursorPosition(mImpl->mEventData);
// Clear the font-specific data
- ClearFontData();
+ mImpl->ClearFontData();
mImpl->RequestRelayout();
}
UpdateCursorPosition(mImpl->mEventData);
// Clear the font-specific data
- ClearFontData();
+ mImpl->ClearFontData();
mImpl->RequestRelayout();
}
UpdateCursorPosition(mImpl->mEventData);
// Clear the font-specific data
- ClearFontData();
+ mImpl->ClearFontData();
mImpl->RequestRelayout();
}
UpdateCursorPosition(mImpl->mEventData);
// Clear the font-specific data
- ClearFontData();
+ mImpl->ClearFontData();
mImpl->RequestRelayout();
}
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<OperationsMask>(mImpl->mOperationsPending | COLOR);
- mImpl->RequestRelayout();
- }
+ mImpl->SetDefaultColor(color);
}
const Vector4& Controller::GetDefaultColor() 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<OperationsMask>(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
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
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<OperationsMask>(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)
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)
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()
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)
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<OperationsMask>(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
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)
*/
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.
private:
struct EventHandler;
struct InputFontHandler;
+ struct InputProperties;
struct PlaceholderHandler;
struct Relayouter;
struct TextUpdater;
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;
}
{
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;
#include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
// EXTERNAL INCLUDES
+#include <dali/devel-api/rendering/texture-devel.h>
// INTERNAL INCLUDES
#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
// 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)
{
}
{
}
-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;
}
}
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;
}
}
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<VisualFactoryCache::ShaderType>(static_cast<int>(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";
}
}
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<VisualFactoryCache::ShaderType>(static_cast<int>(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);
}
{
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
~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();
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
{
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)
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);
{
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
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,
{
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
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