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
states = q->GetStates();
DALI_TEST_EQUALS((int) states[Dali::Accessibility::State::SHOWING], (int) false, TEST_LOCATION);
+ // Make SHOWING parent invisible
+ parentButton.SetProperty(Actor::Property::VISIBLE, false);
+
+ application.SendNotification();
+ application.Render(16);
+
+ q = Dali::Accessibility::Accessible::Get(buttonA);
+ DALI_TEST_CHECK(q);
+ states = q->GetStates();
+ DALI_TEST_EQUALS((int) states[Dali::Accessibility::State::SHOWING], (int) false, TEST_LOCATION);
+
END_TEST;
-}
\ No newline at end of file
+}
}
#include <dali-toolkit/internal/controls/text-controls/text-field-impl.h>
+#include <dali-toolkit/internal/controls/text-controls/text-anchor-impl.h>
+int UtcDaliAccessibilityTextAnchor(void)
+{
+ ToolkitTestApplication application;
+
+ auto textanchor = TextAnchor::New();
+ DALI_TEST_CHECK( textanchor );
+
+ auto textlabel = TextLabel::New();
+ DALI_TEST_CHECK( textlabel );
+
+ Dali::Accessibility::TestEnableSC( true );
+
+ textlabel.Add(textanchor);
+ auto accessible = Dali::Accessibility::Accessible::Get( textanchor );
+ DALI_TEST_CHECK( accessible );
+ auto hyperlink = dynamic_cast< Dali::Accessibility::Hyperlink* >( accessible );
+ DALI_TEST_CHECK( hyperlink );
+ textanchor.SetProperty( Toolkit::TextAnchor::Property::URI, "https://www.tizen.org" );
+ DALI_TEST_EQUALS( hyperlink->IsValid(), true, TEST_LOCATION );
+ auto action = dynamic_cast<Dali::Accessibility::Action*>( accessible );
+ // activation of valid hyperlink
+ DALI_TEST_CHECK( action->DoAction( "accessibilityActivated" ) );
+ // making hyperlink invalid
+ textanchor.SetProperty( Toolkit::TextAnchor::Property::URI, "" );
+ DALI_TEST_EQUALS( hyperlink->IsValid(), false, TEST_LOCATION );
+ DALI_TEST_CHECK( !action->DoAction( "accessibilityActivated" ) );
+
+ Dali::Accessibility::TestEnableSC( false );
+
+ END_TEST;
+}
+
int UtcDaliAccessibilityTextField(void)
{
ToolkitTestApplication application;
DALI_TEST_EQUALS(editabletext->DeleteText(1, 5), true, TEST_LOCATION);
DALI_TEST_EQUALS(text->GetText(0, 2), "af", TEST_LOCATION);
+ auto hypertext = dynamic_cast< Dali::Accessibility::Hypertext* >( accessible );
+ DALI_TEST_CHECK( hypertext );
+ // text without the anchors markup and ENABLE_MARKUP property set (by default) to false
+ DALI_TEST_EQUALS( hypertext->GetLinkCount(), 0, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( -1 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 0 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 5 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLink( -1 ) == nullptr, true, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLink( 0 ) == nullptr, true, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLink( 5 ) == nullptr, true, TEST_LOCATION );
+ // text with the anchors markup and ENABLE_MARKUP property set (by default) to false
+ textfield.SetProperty( Toolkit::TextField::Property::TEXT, "12345<a href = 'https://www.tizen.org'>anchor1</a>12345<a href = 'https://www.tizen.org' >veryveryveryveryveryveryveryverylonganchor2</a>12345<a href = 'https://www.tizen.org'>anchor3</a>12345" );
+ DALI_TEST_EQUALS( hypertext->GetLinkCount(), 0, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( -1 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 0 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 5 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLink( -1 ) == nullptr, true, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLink( 0 ) == nullptr, true, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLink( 5 ) == nullptr, true, TEST_LOCATION );
+ // text with the anchors markup and ENABLE_MARKUP property set to true
+ textfield.SetProperty( Toolkit::TextField::Property::ENABLE_MARKUP, true);
+ DALI_TEST_EQUALS( hypertext->GetLinkCount(), 3, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( -1 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 0 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 5 ), 0, TEST_LOCATION ); //1st anchor index
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 17 ), 1, TEST_LOCATION ); //2nd anchor index
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 66 ), 2, TEST_LOCATION ); //3rd anchor index
+ DALI_TEST_EQUALS( hypertext->GetLink( -1 ) == nullptr, true, TEST_LOCATION );
+ auto hyperlink = hypertext->GetLink( 0 );
+ DALI_TEST_CHECK ( hyperlink );
+ DALI_TEST_EQUALS( hyperlink->GetStartIndex(), 5, TEST_LOCATION );
+ DALI_TEST_EQUALS( hyperlink->GetEndIndex(), 12, TEST_LOCATION );
+ DALI_TEST_EQUALS( hyperlink->GetAnchorCount(), 1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hyperlink->GetAnchorUri( 0 ), "https://www.tizen.org", TEST_LOCATION );
+ auto anchorAccessible = hyperlink->GetAnchorAccessible( 0 );
+ DALI_TEST_EQUALS( hyperlink == anchorAccessible, true, TEST_LOCATION );
+ hyperlink = hypertext->GetLink( 1 );
+ DALI_TEST_CHECK ( hyperlink );
+ DALI_TEST_EQUALS( hyperlink->GetStartIndex(), 17, TEST_LOCATION );
+ DALI_TEST_EQUALS( hyperlink->GetEndIndex(), 60, TEST_LOCATION );
+ hyperlink = hypertext->GetLink( 2 );
+ DALI_TEST_CHECK ( hyperlink );
+ DALI_TEST_EQUALS( hyperlink->GetStartIndex(), 65, TEST_LOCATION );
+ DALI_TEST_EQUALS( hyperlink->GetEndIndex(), 72, TEST_LOCATION );
+
Dali::Accessibility::TestEnableSC( false );
END_TEST;
DALI_TEST_EQUALS(editabletext->DeleteText(1, 5), true, TEST_LOCATION);
DALI_TEST_EQUALS(text->GetText(0, 2), "af", TEST_LOCATION);
+ auto hypertext = dynamic_cast< Dali::Accessibility::Hypertext* >( accessible );
+ DALI_TEST_CHECK( hypertext );
+ // text without the anchors markup and ENABLE_MARKUP property set (by default) to false
+ DALI_TEST_EQUALS( hypertext->GetLinkCount(), 0, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( -1 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 0 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 5 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLink( -1 ) == nullptr, true, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLink( 0 ) == nullptr, true, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLink( 5 ) == nullptr, true, TEST_LOCATION );
+ // text with the anchors markup and ENABLE_MARKUP property set (by default) to false
+ texteditor.SetProperty( Toolkit::TextEditor::Property::TEXT, "12345<a href = 'https://www.tizen.org'>anchor1</a>12345<a href = 'https://www.tizen.org' >veryveryveryveryveryveryveryverylonganchor2</a>12345<a href = 'https://www.tizen.org'>anchor3</a>12345" );
+ DALI_TEST_EQUALS( hypertext->GetLinkCount(), 0, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( -1 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 0 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 5 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLink( -1 ) == nullptr, true, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLink( 0 ) == nullptr, true, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLink( 5 ) == nullptr, true, TEST_LOCATION );
+ // text with the anchors markup and ENABLE_MARKUP property set to true
+ texteditor.SetProperty( Toolkit::TextEditor::Property::ENABLE_MARKUP, true);
+ DALI_TEST_EQUALS( hypertext->GetLinkCount(), 3, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( -1 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 0 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 5 ), 0, TEST_LOCATION ); //1st anchor index
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 17 ), 1, TEST_LOCATION ); //2nd anchor index
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 66 ), 2, TEST_LOCATION ); //3rd anchor index
+ DALI_TEST_EQUALS( hypertext->GetLink( -1 ) == nullptr, true, TEST_LOCATION );
+ auto hyperlink = hypertext->GetLink( 0 );
+ DALI_TEST_CHECK ( hyperlink );
+ DALI_TEST_EQUALS( hyperlink->GetStartIndex(), 5, TEST_LOCATION );
+ DALI_TEST_EQUALS( hyperlink->GetEndIndex(), 12, TEST_LOCATION );
+ DALI_TEST_EQUALS( hyperlink->GetAnchorCount(), 1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hyperlink->GetAnchorUri( 0 ), "https://www.tizen.org", TEST_LOCATION );
+ auto anchorAccessible = hyperlink->GetAnchorAccessible( 0 );
+ DALI_TEST_EQUALS( hyperlink == anchorAccessible, true, TEST_LOCATION );
+ hyperlink = hypertext->GetLink( 1 );
+ DALI_TEST_CHECK ( hyperlink );
+ DALI_TEST_EQUALS( hyperlink->GetStartIndex(), 17, TEST_LOCATION );
+ DALI_TEST_EQUALS( hyperlink->GetEndIndex(), 60, TEST_LOCATION );
+ hyperlink = hypertext->GetLink( 2 );
+ DALI_TEST_CHECK ( hyperlink );
+ DALI_TEST_EQUALS( hyperlink->GetStartIndex(), 65, TEST_LOCATION );
+ DALI_TEST_EQUALS( hyperlink->GetEndIndex(), 72, TEST_LOCATION );
+
Dali::Accessibility::TestEnableSC( false );
END_TEST;
DALI_TEST_EQUALS( text->SetRangeOfSelection( 1, 0, 1 ), false, TEST_LOCATION );
DALI_TEST_EQUALS( text->RemoveSelection( 1 ), false, TEST_LOCATION );
+ auto hypertext = dynamic_cast< Dali::Accessibility::Hypertext* >( accessible );
+ DALI_TEST_CHECK( hypertext );
+ // text without the anchors markup and ENABLE_MARKUP property set (by default) to false
+ DALI_TEST_EQUALS( hypertext->GetLinkCount(), 0, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( -1 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 0 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 5 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLink( -1 ) == nullptr, true, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLink( 0 ) == nullptr, true, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLink( 5 ) == nullptr, true, TEST_LOCATION );
+ // text with the anchors markup and ENABLE_MARKUP property set (by default) to false
+ textlabel.SetProperty( Toolkit::TextLabel::Property::TEXT, "12345<a href = 'https://www.tizen.org'>anchor1</a>12345<a href = 'https://www.tizen.org' >veryveryveryveryveryveryveryverylonganchor2</a>12345<a href = 'https://www.tizen.org'>anchor3</a>12345" );
+ DALI_TEST_EQUALS( hypertext->GetLinkCount(), 0, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( -1 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 0 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 5 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLink( -1 ) == nullptr, true, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLink( 0 ) == nullptr, true, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLink( 5 ) == nullptr, true, TEST_LOCATION );
+ // text with the anchors markup and ENABLE_MARKUP property set to true
+ textlabel.SetProperty( Toolkit::TextLabel::Property::ENABLE_MARKUP, true);
+ DALI_TEST_EQUALS( hypertext->GetLinkCount(), 3, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( -1 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 0 ), -1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 5 ), 0, TEST_LOCATION ); //1st anchor index
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 17 ), 1, TEST_LOCATION ); //2nd anchor index
+ DALI_TEST_EQUALS( hypertext->GetLinkIndex( 66 ), 2, TEST_LOCATION ); //3rd anchor index
+ DALI_TEST_EQUALS( hypertext->GetLink( -1 ) == nullptr, true, TEST_LOCATION );
+ auto hyperlink = hypertext->GetLink( 0 );
+ DALI_TEST_CHECK ( hyperlink );
+ DALI_TEST_EQUALS( hyperlink->GetStartIndex(), 5, TEST_LOCATION );
+ DALI_TEST_EQUALS( hyperlink->GetEndIndex(), 12, TEST_LOCATION );
+ DALI_TEST_EQUALS( hyperlink->GetAnchorCount(), 1, TEST_LOCATION );
+ DALI_TEST_EQUALS( hyperlink->GetAnchorUri( 0 ), "https://www.tizen.org", TEST_LOCATION );
+ auto anchorAccessible = hyperlink->GetAnchorAccessible( 0 );
+ DALI_TEST_EQUALS( hyperlink == anchorAccessible, true, TEST_LOCATION );
+ hyperlink = hypertext->GetLink( 1 );
+ DALI_TEST_CHECK ( hyperlink );
+ DALI_TEST_EQUALS( hyperlink->GetStartIndex(), 17, TEST_LOCATION );
+ DALI_TEST_EQUALS( hyperlink->GetEndIndex(), 60, TEST_LOCATION );
+ hyperlink = hypertext->GetLink( 2 );
+ DALI_TEST_CHECK ( hyperlink );
+ DALI_TEST_EQUALS( hyperlink->GetStartIndex(), 65, TEST_LOCATION );
+ DALI_TEST_EQUALS( hyperlink->GetEndIndex(), 72, TEST_LOCATION );
+
Dali::Accessibility::TestEnableSC( false );
END_TEST;
#include <dali-toolkit/dali-toolkit.h>
#include <dali/devel-api/adaptor-framework/accessibility.h>
+#include <dali-toolkit/internal/controls/text-controls/text-field-impl.h>
#include <dali-toolkit/internal/controls/text-controls/text-editor-impl.h>
#include <automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/dbus-wrapper.h>
DALI_TEST_EQUALS( x->GetText( 0, 0 ), "", TEST_LOCATION );
field.SetProperty( Toolkit::TextField::Property::TEXT, "exemplary_text" );
DALI_TEST_EQUALS( x->GetText( 0, 9 ), "exemplary", TEST_LOCATION );
+
+ Dali::Property::Map hiddenInputSettings;
+ hiddenInputSettings[ Toolkit::HiddenInput::Property::MODE ] = Toolkit::HiddenInput::Mode::HIDE_ALL;
+
+ field.SetProperty( Toolkit::TextField::Property::HIDDEN_INPUT_SETTINGS, hiddenInputSettings );
+
+ DALI_TEST_EQUALS( x->GetName(), "", TEST_LOCATION );
+ DALI_TEST_EQUALS( x->GetText( 0, 9 ), "*********", TEST_LOCATION );
+
+ hiddenInputSettings[ Toolkit::HiddenInput::Property::SUBSTITUTE_CHARACTER ] = 0x23;
+ field.SetProperty( Toolkit::TextField::Property::HIDDEN_INPUT_SETTINGS, hiddenInputSettings );
+
+ DALI_TEST_EQUALS( x->GetName(), "", TEST_LOCATION );
+ DALI_TEST_EQUALS( x->GetText( 0, 9 ), "#########", TEST_LOCATION );
}
END_TEST;
DALI_TEST_EQUALS( range.content, " test sentence", TEST_LOCATION );
DALI_TEST_EQUALS( range.startOffset, 25, TEST_LOCATION );
DALI_TEST_EQUALS( range.endOffset, 39, TEST_LOCATION );
+
+ Dali::Property::Map hiddenInputSettings;
+ hiddenInputSettings[ Toolkit::HiddenInput::Property::MODE ] = Toolkit::HiddenInput::Mode::HIDE_ALL;
+ hiddenInputSettings[ Toolkit::HiddenInput::Property::SUBSTITUTE_CHARACTER ] = 0x23;
+ field.SetProperty( Toolkit::TextField::Property::HIDDEN_INPUT_SETTINGS, hiddenInputSettings );
+ range = x->GetTextAtOffset( 8, Dali::Accessibility::TextBoundary::LINE );
+ DALI_TEST_EQUALS( range.content, "", TEST_LOCATION );
+ DALI_TEST_EQUALS( range.startOffset, 0, TEST_LOCATION );
+ DALI_TEST_EQUALS( range.endOffset, 0, TEST_LOCATION );
+
}
END_TEST;
DALI_TEST_EQUALS( range.startOffset, 4, TEST_LOCATION );
DALI_TEST_EQUALS( range.endOffset, 9, TEST_LOCATION );
DALI_TEST_EQUALS( range.content, "plary", TEST_LOCATION );
+
+ Dali::Property::Map hiddenInputSettings;
+ hiddenInputSettings[ Toolkit::HiddenInput::Property::MODE ] = Toolkit::HiddenInput::Mode::HIDE_ALL;
+ field.SetProperty( Toolkit::TextField::Property::HIDDEN_INPUT_SETTINGS, hiddenInputSettings );
+
+ range = x->GetRangeOfSelection( 0 );
+ DALI_TEST_EQUALS( range.startOffset, 4, TEST_LOCATION );
+ DALI_TEST_EQUALS( range.endOffset, 9, TEST_LOCATION );
+ DALI_TEST_EQUALS( range.content, "*****", TEST_LOCATION );
}
END_TEST;
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;
static bool gAnchorClickedCallBackCalled;
static bool gAnchorClickedCallBackNotCalled;
+static bool gTextFitChangedCallBackCalled;
+
struct CallbackFunctor
{
CallbackFunctor(bool* callbackFlag)
}
}
+static void TestTextFitChangedCallback(TextLabel control)
+{
+ tet_infoline(" TestTextFitChangedCallback");
+ gTextFitChangedCallBackCalled = true;
+}
+
bool DaliTestCheckMaps( const Property::Map& mapGet, const Property::Map& mapSet, const std::vector<std::string>& indexConversionTable = std::vector<std::string>() )
{
const Property::Map::SizeType size = mapGet.Count();
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;
}
label.SetProperty( Actor::Property::SIZE, size );
label.SetProperty( TextLabel::Property::TEXT, "Hello world" );
+ // connect to the text git changed signal.
+ ConnectionTracker* testTracker = new ConnectionTracker();
+ DevelTextLabel::TextFitChangedSignal(label).Connect(&TestTextFitChangedCallback);
+ bool textFitChangedSignal = false;
+ label.ConnectSignal(testTracker, "textFitChanged", CallbackFunctor(&textFitChangedSignal));
+ gTextFitChangedCallBackCalled = false;
+
// check point size
Property::Map textFitMapSet;
textFitMapSet["enable"] = true;
const Vector3 EXPECTED_NATURAL_SIZE( 450.0f, 96.0f, 0.0f );
DALI_TEST_EQUALS( EXPECTED_NATURAL_SIZE, label.GetNaturalSize(), TEST_LOCATION );
+ DALI_TEST_CHECK(gTextFitChangedCallBackCalled);
+ DALI_TEST_CHECK(textFitChangedSignal);
+
// check pixel size
textFitMapSet.Clear();
textFitMapSet["enable"] = true;
END_TEST;
-}
\ No newline at end of file
+}
ALIASES += SINCE_1_4="@since 1.4"
ALIASES += SINCE_1_9="@since 1.9"
ALIASES += SINCE_2_0="@since 2.0"
+ALIASES += SINCE_2_1="@since 2.1"
# Extra tags for Tizen 3.0
ALIASES += SINCE_1_2_2="@since 1.2.2"
ALIASES += DEPRECATED_1_3_51="@deprecated Deprecated since 1.3.51"
ALIASES += DEPRECATED_1_4="@deprecated Deprecated since 1.4"
ALIASES += DEPRECATED_2_0="@deprecated Deprecated since 2.0"
+ALIASES += DEPRECATED_2_1="@deprecated Deprecated since 2.1"
ALIASES += PLATFORM=""
ALIASES += PRIVLEVEL_PLATFORM=""
#ALIASES += SINCE_1_3="\par Since:\n 5.0, DALi version 1.3"
#ALIASES += SINCE_1_4="\par Since:\n 5.5, DALi version 1.4"
#ALIASES += SINCE_1_9="\par Since:\n 6.0, DALi version 1.9"
-#ALIASES += SINCE_2_0="\par Since:\n 6.0, DALi version 2.0"
+#ALIASES += SINCE_2_0="\par Since:\n 6.5, DALi version 2.0"
+#ALIASES += SINCE_2_1="\par Since:\n 7.0, DALi version 2.1"
## Extra tags for Tizen 3.0
#ALIASES += SINCE_1_2_2="\par Since:\n 3.0, DALi version 1.2.2"
#ALIASES += DEPRECATED_1_3_39="@deprecated Deprecated since 5.5, DALi version 1.3.39"
#ALIASES += DEPRECATED_1_3_51="@deprecated Deprecated since 5.5, DALi version 1.3.51"
#ALIASES += DEPRECATED_1_4="@deprecated Deprecated since 5.5, DALi version 1.4"
-#ALIASES += DEPRECATED_2_0="@deprecated Deprecated since 5.5, DALi version 2.0"
+#ALIASES += DEPRECATED_2_0="@deprecated Deprecated since 6.5, DALi version 2.0"
+#ALIASES += DEPRECATED_2_1="@deprecated Deprecated since 7.0, DALi version 2.1"
#ALIASES += PLATFORM="@platform"
#ALIASES += PRIVLEVEL_PLATFORM="\par Privilege Level:\n platform"
namespace Dali::Toolkit::DevelControl
{
-
+namespace
+{
static std::string GetLocaleText(std::string string, const char *domain = "dali-toolkit")
{
#ifdef DGETTEXT_ENABLED
#endif
}
+static Dali::Actor CreateHighlightIndicatorActor()
+{
+ std::string focusBorderImagePath(AssetManager::GetDaliImagePath());
+ focusBorderImagePath += "/keyboard_focus.9.png";
+
+ // Create the default if it hasn't been set and one that's shared by all the
+ // keyboard focusable actors
+ auto actor = Toolkit::ImageView::New(focusBorderImagePath);
+ actor.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
+
+ DevelControl::AppendAccessibilityAttribute(actor, "highlight", std::string());
+ actor.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_HIGHLIGHTABLE, false);
+
+ return actor;
+}
+} // unnamed namespace
+
AccessibleImpl::AccessibleImpl(Dali::Actor self, Dali::Accessibility::Role role, bool modal)
: mSelf(self),
mIsModal(modal)
bool AccessibleImpl::IsShowing()
{
Dali::Actor self = Self();
- if(self.GetProperty(Dali::DevelActor::Property::CULLED).Get<bool>() || !self.GetCurrentProperty<bool>(Actor::Property::VISIBLE))
+ if(!self.GetProperty<bool>(Actor::Property::VISIBLE) || self.GetProperty<Vector4>(Actor::Property::WORLD_COLOR).a == 0 || self.GetProperty<bool>(Dali::DevelActor::Property::CULLED))
{
return false;
}
while(parent)
{
auto control = Dali::Toolkit::Control::DownCast(parent->Self());
+ if(!control.GetProperty<bool>(Actor::Property::VISIBLE))
+ {
+ return false;
+ }
auto clipMode = control.GetProperty(Actor::Property::CLIPPING_MODE).Get<bool>();
auto parentExtent = parent->GetExtents(Dali::Accessibility::CoordinateType::WINDOW);
if ((clipMode != ClippingMode::DISABLED) && !parentExtent.Intersects(childExtent))
state[Dali::Accessibility::State::HIGHLIGHTED] = GetCurrentlyHighlightedActor() == self;
state[Dali::Accessibility::State::ENABLED] = true;
state[Dali::Accessibility::State::SENSITIVE] = true;
- state[Dali::Accessibility::State::VISIBLE] = true;
+ state[Dali::Accessibility::State::VISIBLE] = self.GetProperty<bool>(Actor::Property::VISIBLE);
if(mIsModal)
{
return Toolkit::KeyboardFocusManager::Get().SetCurrentFocusActor(Self());
}
-static Dali::Actor CreateHighlightIndicatorActor()
-{
- std::string focusBorderImagePath(AssetManager::GetDaliImagePath());
- focusBorderImagePath += "/keyboard_focus.9.png";
-
- // Create the default if it hasn't been set and one that's shared by all the
- // keyboard focusable actors
- auto actor = Toolkit::ImageView::New(focusBorderImagePath);
- actor.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
-
- DevelControl::AppendAccessibilityAttribute(actor, "highlight", std::string());
- actor.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_HIGHLIGHTABLE, false);
-
- return actor;
-}
-
void AccessibleImpl::ScrollToSelf()
{
auto* child = this;
bool mIsModal = false;
bool mIsRoot = false;
- Dali::Actor Self()
+ Dali::Actor Self() const
{
auto handle = mSelf.GetHandle();
--- /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/devel-api/controls/text-controls/text-anchor-devel.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/text-controls/text-anchor-impl.h>
+
+using namespace Dali;
+
+namespace Dali
+{
+namespace Toolkit
+{
+TextAnchor TextAnchor::New()
+{
+ return Internal::TextAnchor::New();
+}
+
+TextAnchor::TextAnchor()
+{
+}
+
+TextAnchor::TextAnchor(const TextAnchor& handle)
+: Control(handle)
+{
+}
+
+TextAnchor& TextAnchor::operator=(const TextAnchor& handle)
+{
+ if(&handle != this)
+ {
+ Control::operator=(handle);
+ }
+ return *this;
+}
+
+TextAnchor::~TextAnchor()
+{
+}
+
+TextAnchor TextAnchor::DownCast(BaseHandle handle)
+{
+ return Control::DownCast<TextAnchor, Internal::TextAnchor>(handle);
+}
+
+TextAnchor::TextAnchor(Internal::TextAnchor& implementation)
+: Control(implementation)
+{
+}
+
+TextAnchor::TextAnchor(Dali::Internal::CustomActor* internal)
+: Control(internal)
+{
+ VerifyCustomActorPointer<Internal::TextAnchor>(internal);
+}
+
+} // namespace Toolkit
+
+} // namespace Dali
--- /dev/null
+#ifndef DALI_TOOLKIT_TEXT_ANCHOR_DEVEL_H
+#define DALI_TOOLKIT_TEXT_ANCHOR_DEVEL_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/public-api/controls/control.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal DALI_INTERNAL
+{
+class TextAnchor;
+}
+
+/**
+ * @brief A control which renders anchor (hyperlink) in hypertext.
+ */
+class DALI_TOOLKIT_API TextAnchor : public Control
+{
+public:
+ /**
+ * @brief The start and end property ranges for this control.
+ */
+ enum PropertyRange
+ {
+ PROPERTY_START_INDEX = Control::CONTROL_PROPERTY_END_INDEX + 1,
+ PROPERTY_END_INDEX = PROPERTY_START_INDEX + 1000 ///< Reserve property indices
+ };
+
+ /**
+ * @brief An enumeration of properties belonging to the TextAnchor class.
+ */
+ struct Property
+ {
+ enum
+ {
+ /**
+ * @brief The index of a character in text at which an anchor starts.
+ * @details Name "startCharacterIndex", type INTEGER.
+ */
+ START_CHARACTER_INDEX = PROPERTY_START_INDEX,
+
+ /**
+ * @brief The index of a character in text that stands one position after the anchor's last character.
+ * @details Name "endCharacterIndex", type INTEGER.
+ */
+ END_CHARACTER_INDEX,
+
+ /**
+ * @brief The URI associated with an anchor.
+ * @details Name "uri", type STRING.
+ */
+ URI
+ };
+ };
+
+ /**
+ * @brief Creates the TextAnchor control.
+ * @return A handle to the TextAnchor control.
+ */
+ static TextAnchor New();
+
+ /**
+ * @brief Creates an empty handle.
+ */
+ TextAnchor();
+
+ /**
+ * @brief Copy constructor.
+ *
+ * @param[in] handle The handle to copy from.
+ */
+ TextAnchor(const TextAnchor& handle);
+
+ /**
+ * @brief Assignment operator.
+ *
+ * @param[in] handle The handle to copy from.
+ * @return A reference to this.
+ */
+ TextAnchor& operator=(const TextAnchor& handle);
+
+ /**
+ * @brief Destructor
+ *
+ * This is non-virtual since derived Handle types must not contain data or virtual methods.
+ */
+ ~TextAnchor();
+
+ /**
+ * @brief Downcast a handle to TextAnchor.
+ *
+ * If the BaseHandle points is a TextAnchor the downcast returns a valid handle.
+ * If not the returned handle is left empty.
+ *
+ * @param[in] handle Handle to an object
+ * @return handle to a TextAnchor or an empty handle
+ */
+ static TextAnchor DownCast(BaseHandle handle);
+
+public: // Not intended for application developers
+ /**
+ * @brief Creates a handle using the Toolkit::Internal implementation.
+ *
+ * @param[in] implementation The Control implementation.
+ */
+ DALI_INTERNAL TextAnchor(Internal::TextAnchor& implementation);
+
+ /**
+ * @brief Allows the creation of this Control from an Internal::CustomActor pointer.
+ *
+ * @param[in] internal A pointer to the internal CustomActor.
+ */
+ explicit DALI_INTERNAL TextAnchor(Dali::Internal::CustomActor* internal);
+
+}; // Class TextAnchor
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_ANCHOR_DEVEL_H
return GetImpl(textLabel).AnchorClickedSignal();
}
+TextFitChangedSignalType& TextFitChangedSignal(TextLabel textLabel)
+{
+ return GetImpl(textLabel).TextFitChangedSignal();
+}
+
} // namespace DevelTextLabel
} // namespace Toolkit
using AnchorClickedSignalType = Signal<void(TextLabel, const char*, uint32_t)>;
/**
+ * @brief TextFit property changed signal type.
+ */
+using TextFitChangedSignalType = Signal<void(TextLabel)>;
+
+/**
* @brief This signal is emitted when the anchor is clicked.
*
* A callback of the following type may be connected:
*/
DALI_TOOLKIT_API AnchorClickedSignalType& AnchorClickedSignal(TextLabel textLabel);
+/**
+ * @brief This signal is emitted when the textfit property is changed.
+ *
+ * A callback of the following type may be connected:
+ * @code
+ * void YourCallbackName(TextLabel textLabel);
+ * @endcode
+ * @param[in] textLabel The instance of TextLabel.
+ * @return The signal to connect to.
+ */
+DALI_TOOLKIT_API TextFitChangedSignalType& TextFitChangedSignal(TextLabel textLabel);
+
} // namespace DevelTextLabel
} // namespace Toolkit
${devel_api_src_dir}/controls/shadow-view/shadow-view.cpp
${devel_api_src_dir}/controls/super-blur-view/super-blur-view.cpp
${devel_api_src_dir}/controls/table-view/table-view.cpp
+ ${devel_api_src_dir}/controls/text-controls/text-anchor-devel.cpp
${devel_api_src_dir}/controls/text-controls/text-editor-devel.cpp
${devel_api_src_dir}/controls/text-controls/text-field-devel.cpp
${devel_api_src_dir}/controls/text-controls/text-label-devel.cpp
)
SET( devel_api_text_controls_header_files
+ ${devel_api_src_dir}/controls/text-controls/text-anchor-devel.h
${devel_api_src_dir}/controls/text-controls/text-editor-devel.h
${devel_api_src_dir}/controls/text-controls/text-field-devel.h
${devel_api_src_dir}/controls/text-controls/text-label-devel.h
--- /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;
};
/**
#include <dali/public-api/actors/layer.h>
#include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
+#include <dali-toolkit/devel-api/controls/control-devel.h>
#include <dali-toolkit/internal/controls/text-controls/common-text-utils.h>
#include <dali-toolkit/internal/text/text-view.h>
namespace Dali::Toolkit::Internal
{
+void CommonTextUtils::SynchronizeTextAnchorsInParent(
+ Actor parent,
+ Text::ControllerPtr controller,
+ std::vector<Toolkit::TextAnchor>& anchorActors)
+{
+ for(auto& anchorActor : anchorActors)
+ {
+ parent.Remove(anchorActor);
+ }
+ if(Dali::Accessibility::IsUp())
+ {
+ controller->GetAnchorActors(anchorActors);
+ for(auto& anchorActor : anchorActors)
+ {
+ parent.Add(anchorActor);
+ }
+ }
+}
+
void CommonTextUtils::RenderText(
- Actor textActor,
- Text::RendererPtr renderer,
- Text::ControllerPtr controller,
- Text::DecoratorPtr decorator,
- float alignmentOffset,
- Actor& renderableActor,
- Actor& backgroundActor,
- Toolkit::Control& stencil,
- std::vector<Actor>& clippingDecorationActors,
- Text::Controller::UpdateTextType updateTextType)
+ Actor textActor,
+ Text::RendererPtr renderer,
+ Text::ControllerPtr controller,
+ Text::DecoratorPtr decorator,
+ float& alignmentOffset,
+ Actor& renderableActor,
+ Actor& backgroundActor,
+ Toolkit::Control& stencil,
+ std::vector<Actor>& clippingDecorationActors,
+ std::vector<Toolkit::TextAnchor>& anchorActors,
+ Text::Controller::UpdateTextType updateTextType)
{
Actor newRenderableActor;
backgroundActor.LowerToBottom();
}
}
+ SynchronizeTextAnchorsInParent(textActor, controller, anchorActors);
}
}
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
+#include <dali-toolkit/devel-api/controls/text-controls/text-anchor-devel.h>
#include <dali-toolkit/internal/text/decorator/text-decorator.h>
#include <dali-toolkit/internal/text/rendering/text-renderer.h>
#include <dali-toolkit/internal/text/text-controller.h>
* @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
* @param[in,out] clippingDecorationActors Clipping decoration actors
+ * @param[in,out] anchorActors Anchor actors
* @param[in] updateTextType How the text has been updated
*/
static void RenderText(
Text::RendererPtr renderer,
Text::ControllerPtr controller,
Text::DecoratorPtr decorator,
- float alignmentOffset,
+ float& alignmentOffset,
Actor& renderableActor,
Actor& backgroundActor,
Toolkit::Control& stencil,
std::vector<Actor>& clippingDecorationActors,
+ std::vector<Toolkit::TextAnchor>& anchorActors,
Text::Controller::UpdateTextType updateTextType);
+
+ /**
+ * Common method to synchronize TextAnchor actors with Anchor objects in text's logical model.
+ * @param[in] parent The actor that is a parent of anchor actors
+ * @param[in] controller pointer to the text controller
+ * @param[in,out] anchorActors Anchor actors
+ */
+ static void SynchronizeTextAnchorsInParent(
+ Actor parent,
+ Text::ControllerPtr controller,
+ std::vector<Toolkit::TextAnchor>& anchorActors);
};
} // namespace Dali::Toolkit::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/controls/text-controls/text-anchor-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/actors/actor-devel.h>
+#include <dali/devel-api/object/property-helper-devel.h>
+#include <dali/integration-api/debug.h>
+#include <dali/public-api/common/dali-common.h>
+#include <dali/public-api/object/type-registry-helper.h>
+
+// INTERNAL INCLUDES
+
+// DEVEL INCLUDES
+#include <dali-toolkit/devel-api/controls/control-devel.h>
+
+using namespace Dali::Toolkit::Text;
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+namespace
+{
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
+#endif
+
+// Type registration
+BaseHandle Create()
+{
+ return Toolkit::TextAnchor::New();
+}
+
+// clang-format off
+// Setup properties, signals and actions using the type-registry.
+DALI_TYPE_REGISTRATION_BEGIN(Toolkit::TextAnchor, Toolkit::Control, Create);
+
+DALI_PROPERTY_REGISTRATION(Toolkit, TextAnchor, "startCharacterIndex", INTEGER, START_CHARACTER_INDEX)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextAnchor, "endCharacterIndex", INTEGER, END_CHARACTER_INDEX )
+DALI_PROPERTY_REGISTRATION(Toolkit, TextAnchor, "uri", STRING, URI )
+
+DALI_TYPE_REGISTRATION_END()
+// clang-format on
+
+} // namespace
+
+Toolkit::TextAnchor TextAnchor::New()
+{
+ // Create the implementation, temporarily owned by this handle on stack
+ IntrusivePtr<TextAnchor> impl = new TextAnchor();
+
+ // Pass ownership to CustomActor handle
+ Toolkit::TextAnchor handle(*impl);
+
+ // Second-phase init of the implementation
+ // This can only be done after the CustomActor connection has been made...
+ impl->Initialize();
+
+ return handle;
+}
+
+Property::Value TextAnchor::GetProperty(BaseObject* object, Property::Index index)
+{
+ Property::Value value;
+
+ Toolkit::TextAnchor anchor = Toolkit::TextAnchor::DownCast(Dali::BaseHandle(object));
+
+ if(anchor)
+ {
+ TextAnchor& impl(GetImpl(anchor));
+
+ switch(index)
+ {
+ case Toolkit::TextAnchor::Property::START_CHARACTER_INDEX:
+ {
+ value = impl.mStartCharacterIndex;
+ break;
+ }
+ case Toolkit::TextAnchor::Property::END_CHARACTER_INDEX:
+ {
+ value = impl.mEndCharacterIndex;
+ break;
+ }
+ case Toolkit::TextAnchor::Property::URI:
+ {
+ value = impl.mUri;
+ break;
+ }
+ }
+ }
+
+ return value;
+}
+
+void TextAnchor::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
+{
+ Toolkit::TextAnchor anchor = Toolkit::TextAnchor::DownCast(Dali::BaseHandle(object));
+
+ if(anchor)
+ {
+ TextAnchor& impl(GetImpl(anchor));
+ switch(index)
+ {
+ case Toolkit::TextAnchor::Property::START_CHARACTER_INDEX:
+ {
+ value.Get(impl.mStartCharacterIndex);
+ break;
+ }
+
+ case Toolkit::TextAnchor::Property::END_CHARACTER_INDEX:
+ {
+ value.Get(impl.mEndCharacterIndex);
+ break;
+ }
+
+ case Toolkit::TextAnchor::Property::URI:
+ {
+ value.Get(impl.mUri);
+ break;
+ }
+ }
+ }
+}
+
+void TextAnchor::OnInitialize()
+{
+ Actor self = Self();
+
+ // Enable highlightability
+ self.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_HIGHLIGHTABLE, true);
+
+ DevelControl::SetAccessibilityConstructor(self, [](Dali::Actor actor) {
+ return std::unique_ptr<Dali::Accessibility::Accessible>(
+ new AccessibleImpl(actor, Dali::Accessibility::Role::LINK));
+ });
+}
+
+TextAnchor::TextAnchor()
+: Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT))
+{
+}
+
+TextAnchor::~TextAnchor()
+{
+}
+
+int32_t TextAnchor::AccessibleImpl::GetEndIndex() const
+{
+ auto self = Toolkit::TextAnchor::DownCast(Self());
+ return self.GetProperty(Toolkit::TextAnchor::Property::END_CHARACTER_INDEX).Get<int>();
+}
+
+int32_t TextAnchor::AccessibleImpl::GetStartIndex() const
+{
+ auto self = Toolkit::TextAnchor::DownCast(Self());
+ return self.GetProperty(Toolkit::TextAnchor::Property::START_CHARACTER_INDEX).Get<int>();
+}
+
+int32_t TextAnchor::AccessibleImpl::GetAnchorCount() const
+{
+ return 1;
+}
+
+Dali::Accessibility::Accessible* TextAnchor::AccessibleImpl::GetAnchorAccessible(int32_t anchorIndex) const
+{
+ return Control::Impl::GetAccessibilityObject(Self());
+}
+
+std::string TextAnchor::AccessibleImpl::GetAnchorUri(int32_t anchorIndex) const
+{
+ auto self = Toolkit::TextAnchor::DownCast(Self());
+ return self.GetProperty(Toolkit::TextAnchor::Property::URI).Get<std::string>();
+}
+
+bool TextAnchor::AccessibleImpl::IsValid() const
+{
+ return !GetAnchorUri(0).empty();
+}
+
+bool TextAnchor::OnAccessibilityActivated()
+{
+ auto uri = Self().GetProperty(Toolkit::TextAnchor::Property::URI).Get<std::string>();
+ if(!uri.empty())
+ {
+ Dali::Actor current = Self();
+ Dali::Toolkit::Text::AnchorControlInterface* parentImplementationAnchorInterface = nullptr;
+ while(!current.GetProperty<bool>(Actor::Property::IS_ROOT) && !parentImplementationAnchorInterface)
+ {
+ Dali::Actor parentAsActor = current.GetParent();
+ Dali::CustomActor parentAsCustomActor = Dali::CustomActor::DownCast(parentAsActor);
+ Dali::CustomActorImpl& parentImplementation = parentAsCustomActor.GetImplementation();
+ parentImplementationAnchorInterface = dynamic_cast<Dali::Toolkit::Text::AnchorControlInterface*>(&parentImplementation);
+ current = parentAsActor;
+ }
+ if(parentImplementationAnchorInterface)
+ {
+ parentImplementationAnchorInterface->AnchorClicked(uri);
+ return true;
+ }
+ else
+ {
+ DALI_LOG_ERROR("TextAnchor::OnAccessibilityActivate cannot find ancestor actor implementing Dali::Toolkit::Text::AnchorControlInterface.\n");
+ }
+ }
+ return false;
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
--- /dev/null
+#ifndef DALI_TOOLKIT_INTERNAL_TEXT_ANCHOR_H
+#define DALI_TOOLKIT_INTERNAL_TEXT_ANCHOR_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-toolkit/public-api/controls/control-impl.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/controls/text-controls/text-anchor-devel.h>
+#include <dali-toolkit/internal/controls/control/control-data-impl.h>
+#include <dali-toolkit/internal/text/text-anchor-control-interface.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+/**
+ * @brief A control which renders anchor (hyperlink) in hypertext.
+ */
+class TextAnchor : public Control
+{
+public:
+ /**
+ * @copydoc Dali::Toollkit::TextAnchor::New()
+ */
+ static Toolkit::TextAnchor New();
+
+ // Properties
+
+ /**
+ * @brief Called when a property of an object of this type is set.
+ *
+ * @param[in] object The object whose property is set.
+ * @param[in] index The property index.
+ * @param[in] value The new property value.
+ */
+ static void SetProperty(BaseObject* object, Property::Index index, const Property::Value& value);
+
+ /**
+ * @brief Called to retrieve a property of an object of this type.
+ *
+ * @param[in] object The object whose property is to be retrieved.
+ * @param[in] index The property index.
+ * @return The current value of the property.
+ */
+ static Property::Value GetProperty(BaseObject* object, Property::Index index);
+
+private: // From Control
+ /**
+ * @copydoc Control::OnInitialize()
+ */
+ void OnInitialize() override;
+
+ /**
+ * @copydoc Control::OnPropertySet()
+ */
+ // void OnPropertySet(Property::Index index, const Property::Value& propertyValue) override;
+
+ /**
+ * @copydoc Control::OnAccessibilityActivated()
+ */
+ bool OnAccessibilityActivated() override;
+
+private: // Implementation
+ /**
+ * Construct a new TextAnchor.
+ */
+ TextAnchor();
+
+ /**
+ * A reference counted object may only be deleted by calling Unreference()
+ */
+ virtual ~TextAnchor();
+
+private:
+ // Undefined copy constructor and assignment operators
+ TextAnchor(const TextAnchor&);
+ TextAnchor& operator=(const TextAnchor& rhs);
+
+ //Data
+ int mStartCharacterIndex;
+ int mEndCharacterIndex;
+ std::string mUri;
+
+protected:
+ /**
+ * @brief This structure is to connect TextAnchor with Accessible functions.
+ */
+ struct AccessibleImpl : public DevelControl::AccessibleImpl,
+ public virtual Dali::Accessibility::Hyperlink
+ {
+ using DevelControl::AccessibleImpl::AccessibleImpl;
+ /**
+ * @copydoc Dali::Accessibility::Hyperlink::GetEndIndex()
+ */
+ int32_t GetEndIndex() const override;
+
+ /**
+ * @copydoc Dali::Accessibility::Hyperlink::GetStartIndex()
+ */
+ int32_t GetStartIndex() const override;
+
+ /**
+ * @copydoc Dali::Accessibility::Hyperlink::GetAnchorCount()
+ */
+ int32_t GetAnchorCount() const override;
+
+ /**
+ * @copydoc Dali::Accessibility::Hyperlink::GetAnchorAccessible()
+ */
+ Accessible* GetAnchorAccessible(int32_t anchorIndex) const override;
+
+ /**
+ * @copydoc Dali::Accessibility::Hyperlink::GetAnchorUri()
+ */
+ std::string GetAnchorUri(int32_t anchorIndex) const override;
+
+ /**
+ * @copydoc Dali::Accessibility::Hyperlink::IsValid()
+ */
+ bool IsValid() const override;
+ };
+};
+
+inline Toolkit::Internal::TextAnchor& GetImpl(Toolkit::TextAnchor& textAnchor)
+{
+ DALI_ASSERT_ALWAYS(textAnchor);
+
+ Dali::RefObject& handle = textAnchor.GetImplementation();
+
+ return static_cast<Toolkit::Internal::TextAnchor&>(handle);
+}
+
+inline const Toolkit::Internal::TextAnchor& GetImpl(const Toolkit::TextAnchor& textAnchor)
+{
+ DALI_ASSERT_ALWAYS(textAnchor);
+
+ const Dali::RefObject& handle = textAnchor.GetImplementation();
+
+ return static_cast<const Toolkit::Internal::TextAnchor&>(handle);
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_INTERNAL_TEXT_ANCHOR_H
return mScrollStateChangedSignal;
}
+void TextEditor::OnAccessibilityStatusChanged()
+{
+ CommonTextUtils::SynchronizeTextAnchorsInParent(Self(), mController, mAnchorActors);
+}
+
void TextEditor::OnInitialize()
{
Actor self = Self();
return std::unique_ptr<Dali::Accessibility::Accessible>(
new AccessibleImpl(actor, Dali::Accessibility::Role::ENTRY));
});
+
+ Accessibility::Bridge::EnabledSignal().Connect(this, &TextEditor::OnAccessibilityStatusChanged);
+ Accessibility::Bridge::DisabledSignal().Connect(this, &TextEditor::OnAccessibilityStatusChanged);
}
void TextEditor::OnStyleChange(Toolkit::StyleManager styleManager, StyleChange::Type change)
void TextEditor::RenderText(Text::Controller::UpdateTextType updateTextType)
{
- CommonTextUtils::RenderText(Self(), mRenderer, mController, mDecorator, mAlignmentOffset, mRenderableActor, mBackgroundActor, mStencil, mClippingDecorationActors, updateTextType);
+ CommonTextUtils::RenderText(Self(), mRenderer, mController, mDecorator, mAlignmentOffset, mRenderableActor, mBackgroundActor, mStencil, mClippingDecorationActors, mAnchorActors, updateTextType);
if(mRenderableActor)
{
ApplyScrollPosition();
return true;
}
+int32_t TextEditor::AccessibleImpl::GetLinkCount() const
+{
+ auto self = Toolkit::TextEditor::DownCast(Self());
+ return Dali::Toolkit::GetImpl(self).mAnchorActors.size();
+}
+
+Accessibility::Hyperlink* TextEditor::AccessibleImpl::GetLink(int32_t linkIndex) const
+{
+ if(linkIndex < 0 || linkIndex >= GetLinkCount())
+ {
+ return nullptr;
+ }
+ auto self = Toolkit::TextEditor::DownCast(Self());
+ auto anchorActor = Dali::Toolkit::GetImpl(self).mAnchorActors[linkIndex];
+ return dynamic_cast<Accessibility::Hyperlink*>(Dali::Accessibility::Accessible::Get(anchorActor));
+}
+
+int32_t TextEditor::AccessibleImpl::GetLinkIndex(int32_t characterOffset) const
+{
+ auto self = Toolkit::TextEditor::DownCast(Self());
+ auto controller = Dali::Toolkit::GetImpl(self).GetTextController();
+ return controller->GetAnchorIndex(static_cast<size_t>(characterOffset));
+}
+
} // namespace Internal
} // namespace Toolkit
// Connection needed to re-render text, when a text editor returns to the scene.
void OnSceneConnect(Dali::Actor actor);
+ // Needed to synchronize TextAnchor actors with Anchor objects in text's logical model
+ void OnAccessibilityStatusChanged();
+
private: // Data
// Signals
Toolkit::TextEditor::TextChangedSignalType mTextChangedSignal;
Toolkit::DevelTextEditor::SelectionChangedSignalType mSelectionChangedSignal;
Toolkit::DevelTextEditor::SelectionClearedSignalType mSelectionClearedSignal;
- InputMethodContext mInputMethodContext;
- Text::ControllerPtr mController;
- Text::RendererPtr mRenderer;
- Text::DecoratorPtr mDecorator;
- Text::TextVerticalScrollerPtr mTextVerticalScroller;
- Toolkit::Control mStencil;
- Toolkit::ScrollBar mScrollBar;
- Dali::Animation mAnimation; ///< Scroll indicator Show/Hide Animation.
- Dali::TimePeriod mAnimationPeriod;
- std::vector<Actor> mClippingDecorationActors; ///< Decoration actors which need clipping.
- Dali::InputMethodOptions mInputMethodOptions;
+ InputMethodContext mInputMethodContext;
+ Text::ControllerPtr mController;
+ Text::RendererPtr mRenderer;
+ Text::DecoratorPtr mDecorator;
+ Text::TextVerticalScrollerPtr mTextVerticalScroller;
+ Toolkit::Control mStencil;
+ Toolkit::ScrollBar mScrollBar;
+ Dali::Animation mAnimation; ///< Scroll indicator Show/Hide Animation.
+ Dali::TimePeriod mAnimationPeriod;
+ std::vector<Actor> mClippingDecorationActors; ///< Decoration actors which need clipping.
+ std::vector<Toolkit::TextAnchor> mAnchorActors;
+ Dali::InputMethodOptions mInputMethodOptions;
Actor mRenderableActor;
Actor mActiveLayer;
*/
struct AccessibleImpl : public DevelControl::AccessibleImpl,
public virtual Dali::Accessibility::Text,
- public virtual Dali::Accessibility::EditableText
+ public virtual Dali::Accessibility::EditableText,
+ public virtual Dali::Accessibility::Hypertext
{
using DevelControl::AccessibleImpl::AccessibleImpl;
* @copydoc Dali::Accessibility::EditableText::DeleteText()
*/
bool DeleteText(size_t startPosition, size_t endPosition) override;
+
+ /**
+ * @copydoc Dali::Accessibility::Hypertext::GetLink()
+ */
+ Accessibility::Hyperlink* GetLink(int32_t linkIndex) const override;
+
+ /**
+ * @copydoc Dali::Accessibility::Hypertext::GetLinkIndex()
+ */
+ int32_t GetLinkIndex(int32_t characterOffset) const override;
+
+ /**
+ * @copydoc Dali::Accessibility::Hypertext::GetLinkCount()
+ */
+ int32_t GetLinkCount() const override;
};
};
*/
#include <dali-toolkit/internal/controls/text-controls/text-editor-property-handler.h>
+#include <dali-toolkit/internal/controls/text-controls/common-text-utils.h>
#include <dali-toolkit/devel-api/focus-manager/keyinput-focus-manager.h>
DALI_LOG_INFO(gTextEditorLogFilter, Debug::General, "TextEditor %p ENABLE_MARKUP %d\n", impl.mController.Get(), enableMarkup);
impl.mController->SetMarkupProcessorEnabled(enableMarkup);
+ CommonTextUtils::SynchronizeTextAnchorsInParent(textEditor, impl.mController, impl.mAnchorActors);
break;
}
case Toolkit::TextEditor::Property::INPUT_COLOR:
return fieldInputStyleMask;
}
+bool IsHiddenInput(Toolkit::TextField textField)
+{
+ Property::Map hiddenInputSettings = textField.GetProperty<Property::Map>(Toolkit::TextField::Property::HIDDEN_INPUT_SETTINGS);
+ auto mode = hiddenInputSettings.Find(Toolkit::HiddenInput::Property::MODE);
+ if (mode && (mode->Get<int>() != Toolkit::HiddenInput::Mode::HIDE_NONE))
+ {
+ return true;
+ }
+ return false;
+}
+
+char GetSubstituteCharacter(Toolkit::TextField textField)
+{
+ Property::Map hiddenInputSettings = textField.GetProperty<Property::Map>(Toolkit::TextField::Property::HIDDEN_INPUT_SETTINGS);
+ auto substChar = hiddenInputSettings.Find(Toolkit::HiddenInput::Property::SUBSTITUTE_CHARACTER);
+ if (substChar)
+ {
+ return static_cast<char>(substChar->Get<int>());
+ }
+ return STAR;
+}
+
} // namespace
Toolkit::TextField TextField::New()
return mSelectionClearedSignal;
}
+void TextField::OnAccessibilityStatusChanged()
+{
+ CommonTextUtils::SynchronizeTextAnchorsInParent(Self(), mController, mAnchorActors);
+}
+
void TextField::OnInitialize()
{
Actor self = Self();
return std::unique_ptr<Dali::Accessibility::Accessible>(
new AccessibleImpl(actor, Dali::Accessibility::Role::ENTRY));
});
+
+ Accessibility::Bridge::EnabledSignal().Connect(this, &TextField::OnAccessibilityStatusChanged);
+ Accessibility::Bridge::DisabledSignal().Connect(this, &TextField::OnAccessibilityStatusChanged);
}
void TextField::OnStyleChange(Toolkit::StyleManager styleManager, StyleChange::Type change)
void TextField::RenderText(Text::Controller::UpdateTextType updateTextType)
{
- CommonTextUtils::RenderText(Self(), mRenderer, mController, mDecorator, mAlignmentOffset, mRenderableActor, mBackgroundActor, mStencil, mClippingDecorationActors, updateTextType);
+ CommonTextUtils::RenderText(Self(), mRenderer, mController, mDecorator, mAlignmentOffset, mRenderableActor, mBackgroundActor, mStencil, mClippingDecorationActors, mAnchorActors, updateTextType);
}
void TextField::OnKeyInputFocusGained()
std::string TextField::AccessibleImpl::GetName()
{
auto self = Toolkit::TextField::DownCast(Self());
+ if (IsHiddenInput(self))
+ {
+ return {};
+ }
+
return self.GetProperty(Toolkit::TextField::Property::TEXT).Get<std::string>();
}
{
return {};
}
-
+ if(IsHiddenInput(self))
+ {
+ return std::string(endOffset - startOffset, GetSubstituteCharacter(self));
+ }
return text.substr(startOffset, endOffset - startOffset);
}
size_t offset, Dali::Accessibility::TextBoundary boundary)
{
auto self = Toolkit::TextField::DownCast(Self());
+ auto range = Dali::Accessibility::Range{};
+
+ if(IsHiddenInput(self))
+ {
+ // Returning empty object, as there is no possibility to parse the textfield
+ // when its content is hidden.
+ return range;
+ }
+
auto text = self.GetProperty(Toolkit::TextField::Property::TEXT).Get<std::string>();
auto textSize = text.size();
- auto range = Dali::Accessibility::Range{};
-
switch(boundary)
{
case Dali::Accessibility::TextBoundary::CHARACTER:
return {};
}
- auto self = Toolkit::TextField::DownCast(Self());
- auto controller = Dali::Toolkit::GetImpl(self).GetTextController();
- std::string value{};
- controller->RetrieveSelection(value);
+ auto self = Toolkit::TextField::DownCast(Self());
+ auto controller = Dali::Toolkit::GetImpl(self).GetTextController();
auto indices = controller->GetSelectionIndexes();
- return {static_cast<size_t>(indices.first), static_cast<size_t>(indices.second), value};
+ auto startOffset = static_cast<size_t>(indices.first);
+ auto endOffset = static_cast<size_t>(indices.second);
+
+ if (IsHiddenInput(self))
+ {
+ return {startOffset, endOffset, std::string(endOffset - startOffset, GetSubstituteCharacter(self))};
+ }
+
+ std::string value{};
+ controller->RetrieveSelection(value);
+ return {startOffset, endOffset, value};
}
bool TextField::AccessibleImpl::RemoveSelection(size_t selectionIndex)
return true;
}
+int32_t TextField::AccessibleImpl::GetLinkCount() const
+{
+ auto self = Toolkit::TextField::DownCast(Self());
+ return Dali::Toolkit::GetImpl(self).mAnchorActors.size();
+}
+
+Accessibility::Hyperlink* TextField::AccessibleImpl::GetLink(int32_t linkIndex) const
+{
+ if(linkIndex < 0 || linkIndex >= GetLinkCount())
+ {
+ return nullptr;
+ }
+ auto self = Toolkit::TextField::DownCast(Self());
+ auto anchorActor = Dali::Toolkit::GetImpl(self).mAnchorActors[linkIndex];
+ return dynamic_cast<Accessibility::Hyperlink*>(Dali::Accessibility::Accessible::Get(anchorActor));
+}
+
+int32_t TextField::AccessibleImpl::GetLinkIndex(int32_t characterOffset) const
+{
+ auto self = Toolkit::TextField::DownCast(Self());
+ auto controller = Dali::Toolkit::GetImpl(self).GetTextController();
+ return controller->GetAnchorIndex(static_cast<size_t>(characterOffset));
+}
+
} // namespace Internal
} // namespace Toolkit
// Connection needed to re-render text, when a Text Field returns to the scene.
void OnSceneConnect(Dali::Actor actor);
+ // Needed to synchronize TextAnchor actors with Anchor objects in text's logical model
+ void OnAccessibilityStatusChanged();
+
private: // Data
// Signals
Toolkit::TextField::TextChangedSignalType mTextChangedSignal;
Toolkit::DevelTextField::SelectionChangedSignalType mSelectionChangedSignal;
Toolkit::DevelTextField::SelectionClearedSignalType mSelectionClearedSignal;
- InputMethodContext mInputMethodContext;
- Text::ControllerPtr mController;
- Text::RendererPtr mRenderer;
- Text::DecoratorPtr mDecorator;
- Toolkit::Control mStencil; ///< For EXCEED_POLICY_CLIP
- std::vector<Actor> mClippingDecorationActors; ///< Decoration actors which need clipping.
- Dali::InputMethodOptions mInputMethodOptions;
+ InputMethodContext mInputMethodContext;
+ Text::ControllerPtr mController;
+ Text::RendererPtr mRenderer;
+ Text::DecoratorPtr mDecorator;
+ Toolkit::Control mStencil; ///< For EXCEED_POLICY_CLIP
+ std::vector<Actor> mClippingDecorationActors; ///< Decoration actors which need clipping.
+ std::vector<Toolkit::TextAnchor> mAnchorActors;
+ Dali::InputMethodOptions mInputMethodOptions;
Actor mRenderableActor;
Actor mActiveLayer;
*/
struct AccessibleImpl : public DevelControl::AccessibleImpl,
public virtual Dali::Accessibility::Text,
- public virtual Dali::Accessibility::EditableText
+ public virtual Dali::Accessibility::EditableText,
+ public virtual Dali::Accessibility::Hypertext
{
using DevelControl::AccessibleImpl::AccessibleImpl;
* @copydoc Dali::Accessibility::EditableText::DeleteText()
*/
bool DeleteText(size_t startPosition, size_t endPosition) override;
+
+ /**
+ * @copydoc Dali::Accessibility::Hypertext::GetLink()
+ */
+ Accessibility::Hyperlink* GetLink(int32_t linkIndex) const override;
+
+ /**
+ * @copydoc Dali::Accessibility::Hypertext::GetLinkIndex()
+ */
+ int32_t GetLinkIndex(int32_t characterOffset) const override;
+
+ /**
+ * @copydoc Dali::Accessibility::Hypertext::GetLinkCount()
+ */
+ int32_t GetLinkCount() const override;
};
};
*/
#include <dali-toolkit/internal/controls/text-controls/text-field-property-handler.h>
+#include <dali-toolkit/internal/controls/text-controls/common-text-utils.h>
#include <dali-toolkit/devel-api/focus-manager/keyinput-focus-manager.h>
#include <dali-toolkit/devel-api/text/rendering-backend.h>
DALI_LOG_INFO(gTextFieldLogFilter, Debug::General, "TextField %p ENABLE_MARKUP %d\n", impl.mController.Get(), enableMarkup);
impl.mController->SetMarkupProcessorEnabled(enableMarkup);
+ CommonTextUtils::SynchronizeTextAnchorsInParent(textField, impl.mController, impl.mAnchorActors);
break;
}
case Toolkit::TextField::Property::INPUT_FONT_FAMILY:
if(map)
{
impl.mController->SetHiddenInputOption(*map);
+ auto mode = map->Find(Toolkit::HiddenInput::Property::MODE);
+ if(mode && (mode->Get<int>() != Toolkit::HiddenInput::Mode::HIDE_NONE))
+ {
+ textField.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_ROLE, Accessibility::Role::PASSWORD_TEXT);
+ }
+ else
+ {
+ textField.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_ROLE, Accessibility::Role::ENTRY);
+ }
}
break;
}
#include <dali-toolkit/internal/text/text-effects-style.h>
#include <dali-toolkit/internal/text/text-font-style.h>
#include <dali-toolkit/internal/text/text-view.h>
+#include <dali-toolkit/internal/controls/text-controls/common-text-utils.h>
#include <dali-toolkit/public-api/text/text-enumerations.h>
#include <dali-toolkit/devel-api/controls/control-devel.h>
const std::string TEXT_FIT_MIN_SIZE_KEY("minSize");
const std::string TEXT_FIT_MAX_SIZE_KEY("maxSize");
const std::string TEXT_FIT_STEP_SIZE_KEY("stepSize");
+const std::string TEXT_FIT_FONT_SIZE_KEY("fontSize");
const std::string TEXT_FIT_FONT_SIZE_TYPE_KEY("fontSizeType");
#if defined(DEBUG_ENABLED)
DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, TextLabel, "textColorAlpha", TEXT_COLOR_ALPHA, TEXT_COLOR, 3)
DALI_SIGNAL_REGISTRATION(Toolkit, TextLabel, "anchorClicked", SIGNAL_ANCHOR_CLICKED)
+DALI_SIGNAL_REGISTRATION(Toolkit, TextLabel, "textFitChanged", SIGNAL_TEXT_FIT_CHANGED)
DALI_TYPE_REGISTRATION_END()
// clang-format on
case Toolkit::DevelTextLabel::Property::TEXT_FIT:
{
ParseTextFitProperty(impl.mController, value.GetMap());
+ impl.mController->SetTextFitChanged(true);
break;
}
case Toolkit::DevelTextLabel::Property::MIN_LINE_SIZE:
const float minSize = impl.mController->GetTextFitMinSize();
const float maxSize = impl.mController->GetTextFitMaxSize();
const float stepSize = impl.mController->GetTextFitStepSize();
+ const float pointSize = impl.mController->GetTextFitPointSize();
Property::Map map;
map.Insert(TEXT_FIT_ENABLE_KEY, enabled);
map.Insert(TEXT_FIT_MIN_SIZE_KEY, minSize);
map.Insert(TEXT_FIT_MAX_SIZE_KEY, maxSize);
map.Insert(TEXT_FIT_STEP_SIZE_KEY, stepSize);
+ map.Insert(TEXT_FIT_FONT_SIZE_KEY, pointSize);
map.Insert(TEXT_FIT_FONT_SIZE_TYPE_KEY, "pointSize");
value = map;
labelImpl.AnchorClickedSignal().Connect(tracker, functor);
}
}
+ else if(0 == strcmp(signalName.c_str(), SIGNAL_TEXT_FIT_CHANGED))
+ {
+ if(label)
+ {
+ Internal::TextLabel& labelImpl(GetImpl(label));
+ labelImpl.TextFitChangedSignal().Connect(tracker, functor);
+ }
+ }
else
{
// signalName does not match any signal
return mAnchorClickedSignal;
}
+DevelTextLabel::TextFitChangedSignalType& TextLabel::TextFitChangedSignal()
+{
+ return mTextFitChangedSignal;
+}
+
void TextLabel::OnInitialize()
{
Actor self = Self();
return std::unique_ptr<Dali::Accessibility::Accessible>(
new AccessibleImpl(actor, Dali::Accessibility::Role::LABEL));
});
+
+ Accessibility::Bridge::EnabledSignal().Connect(this, &TextLabel::OnAccessibilityStatusChanged);
+ Accessibility::Bridge::DisabledSignal().Connect(this, &TextLabel::OnAccessibilityStatusChanged);
}
void TextLabel::OnStyleChange(Toolkit::StyleManager styleManager, StyleChange::Type change)
}
break;
}
+ case Toolkit::TextLabel::Property::TEXT:
+ case Toolkit::TextLabel::Property::ENABLE_MARKUP:
+ {
+ CommonTextUtils::SynchronizeTextAnchorsInParent(Self(), mController, mAnchorActors);
+ break;
+ }
default:
{
Control::OnPropertySet(index, propertyValue); // up call to control for non-handled properties
mTextUpdateNeeded = false;
}
+
+ if(mController->IsTextFitChanged())
+ {
+ EmitTextFitChangedSignal();
+ mController->SetTextFitChanged(false);
+ }
}
void TextLabel::RequestTextRelayout()
mController->ChangedLayoutDirection();
}
+void TextLabel::EmitTextFitChangedSignal()
+{
+ Dali::Toolkit::TextLabel handle(GetOwner());
+ mTextFitChangedSignal.Emit(handle);
+}
+
+void TextLabel::OnAccessibilityStatusChanged()
+{
+ CommonTextUtils::SynchronizeTextAnchorsInParent(Self(), mController, mAnchorActors);
+}
+
TextLabel::TextLabel()
: Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT)),
mRenderingBackend(DEFAULT_RENDERING_BACKEND),
Dali::Accessibility::Range TextLabel::AccessibleImpl::GetTextAtOffset(size_t offset, Dali::Accessibility::TextBoundary boundary)
{
- auto self = Toolkit::TextLabel::DownCast(Self());
- auto text = self.GetProperty(Toolkit::TextLabel::Property::TEXT).Get<std::string>();
+ auto self = Toolkit::TextLabel::DownCast(Self());
+ auto text = self.GetProperty(Toolkit::TextLabel::Property::TEXT).Get<std::string>();
auto textSize = text.size();
auto range = Dali::Accessibility::Range{};
case Dali::Accessibility::TextBoundary::LINE:
{
auto textString = text.c_str();
- auto breaks = std::vector<char>(textSize, 0);
+ auto breaks = std::vector<char>(textSize, 0);
if(boundary == Dali::Accessibility::TextBoundary::WORD)
{
Accessibility::Accessible::FindLineSeparationsUtf8(reinterpret_cast<const utf8_t*>(textString), textSize, "", breaks.data());
}
- auto index = 0u;
+ auto index = 0u;
auto counter = 0u;
while(index < textSize && counter <= offset)
{
return {};
}
- auto self = Toolkit::TextLabel::DownCast(Self());
- auto controller = Dali::Toolkit::GetImpl(self).GetTextController();
+ auto self = Toolkit::TextLabel::DownCast(Self());
+ auto controller = Dali::Toolkit::GetImpl(self).GetTextController();
std::string value{};
controller->RetrieveSelection(value);
auto indices = controller->GetSelectionIndexes();
return true;
}
+int32_t TextLabel::AccessibleImpl::GetLinkCount() const
+{
+ auto self = Toolkit::TextLabel::DownCast(Self());
+ return Dali::Toolkit::GetImpl(self).mAnchorActors.size();
+}
+
+Accessibility::Hyperlink* TextLabel::AccessibleImpl::GetLink(int32_t linkIndex) const
+{
+ if(linkIndex < 0 || linkIndex >= GetLinkCount())
+ {
+ return nullptr;
+ }
+ auto self = Toolkit::TextLabel::DownCast(Self());
+ auto anchorActor = Dali::Toolkit::GetImpl(self).mAnchorActors[linkIndex];
+ return dynamic_cast<Accessibility::Hyperlink*>(Dali::Accessibility::Accessible::Get(anchorActor));
+}
+
+int32_t TextLabel::AccessibleImpl::GetLinkIndex(int32_t characterOffset) const
+{
+ auto self = Toolkit::TextLabel::DownCast(Self());
+ auto controller = Dali::Toolkit::GetImpl(self).GetTextController();
+ return controller->GetAnchorIndex(static_cast<size_t>(characterOffset));
+}
+
} // namespace Internal
} // namespace Toolkit
DevelTextLabel::AnchorClickedSignalType& AnchorClickedSignal();
/**
+ * @copydoc Dali::Toollkit::TextLabel::TextFitChangedSignal()
+ */
+ DevelTextLabel::TextFitChangedSignalType& TextFitChangedSignal();
+
+ /**
* Connects a callback function with the object's signals.
* @param[in] object The object providing the signal.
* @param[in] tracker Used to disconnect the signal.
*/
void OnLayoutDirectionChanged(Actor actor, LayoutDirection::Type type);
+ /**
+ * @brief Emits TextFitChanged signal.
+ */
+ void EmitTextFitChangedSignal();
+ void OnAccessibilityStatusChanged();
+
private: // Data
Text::ControllerPtr mController;
Text::TextScrollerPtr mTextScroller;
Toolkit::Visual::Base mVisual;
+ std::vector<Toolkit::TextAnchor> mAnchorActors;
+
// Signals
Toolkit::DevelTextLabel::AnchorClickedSignalType mAnchorClickedSignal;
+ Toolkit::DevelTextLabel::TextFitChangedSignalType mTextFitChangedSignal;
int mRenderingBackend;
bool mTextUpdateNeeded : 1;
* @brief This structure is to connect TextLabel with Accessible functions.
*/
struct AccessibleImpl : public DevelControl::AccessibleImpl,
- public virtual Dali::Accessibility::Text
+ public virtual Dali::Accessibility::Text,
+ public virtual Dali::Accessibility::Hypertext
{
using DevelControl::AccessibleImpl::AccessibleImpl;
* @copydoc Dali::Accessibility::Text::GetNamePropertyIndex()
*/
Property::Index GetNamePropertyIndex() override;
+
+ /**
+ * @copydoc Dali::Accessibility::Hypertext::GetLink()
+ */
+ Accessibility::Hyperlink* GetLink(int32_t linkIndex) const override;
+
+ /**
+ * @copydoc Dali::Accessibility::Hypertext::GetLinkIndex()
+ */
+ int32_t GetLinkIndex(int32_t characterOffset) const override;
+
+ /**
+ * @copydoc Dali::Accessibility::Hypertext::GetLinkCount()
+ */
+ int32_t GetLinkCount() const override;
};
};
${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}/controls/super-blur-view/super-blur-view-impl.cpp
${toolkit_src_dir}/controls/table-view/table-view-impl.cpp
${toolkit_src_dir}/controls/text-controls/common-text-utils.cpp
+ ${toolkit_src_dir}/controls/text-controls/text-anchor-impl.cpp
${toolkit_src_dir}/controls/text-controls/text-editor-impl.cpp
${toolkit_src_dir}/controls/text-controls/text-editor-property-handler.cpp
${toolkit_src_dir}/controls/text-controls/text-field-impl.cpp
${toolkit_src_dir}/text/hyphenator.cpp
${toolkit_src_dir}/text/text-enumerations-impl.cpp
${toolkit_src_dir}/text/text-controller.cpp
+ ${toolkit_src_dir}/text/text-controller-background-actor.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}
if(abs(vPosition.x) < vOptRectSize.x && abs(vPosition.y) < vOptRectSize.y)
{
OUT_COLOR = targetColor;
- return;
}
- PreprocessPotential();
+ else
+ {
+ PreprocessPotential();
#endif
#if !IS_REQUIRED_BLUR && IS_REQUIRED_BORDERLINE
- targetColor = convertBorderlineColor(targetColor);
+ targetColor = convertBorderlineColor(targetColor);
#endif
- OUT_COLOR = targetColor;
+ OUT_COLOR = targetColor;
#if IS_REQUIRED_BLUR
- mediump float opacity = calculateBlurOpacity();
- OUT_COLOR.a *= opacity;
+ mediump float opacity = calculateBlurOpacity();
+ OUT_COLOR.a *= opacity;
#elif IS_REQUIRED_ROUNDED_CORNER
- mediump float opacity = calculateCornerOpacity();
- OUT_COLOR.a *= opacity;
+ mediump float opacity = calculateCornerOpacity();
+ OUT_COLOR.a *= opacity;
+#endif
+
+#if IS_REQUIRED_BLUR || IS_REQUIRED_ROUNDED_CORNER || IS_REQUIRED_BORDERLINE
+ }
#endif
}
if(abs(vPosition.x) < vOptRectSize.x && abs(vPosition.y) < vOptRectSize.y)
{
OUT_COLOR = textureColor;
- return;
}
- PreprocessPotential();
+ else
+ {
+ PreprocessPotential();
#endif
#if IS_REQUIRED_BORDERLINE
- textureColor = convertBorderlineColor(textureColor);
+ textureColor = convertBorderlineColor(textureColor);
#endif
- OUT_COLOR = textureColor;
+ OUT_COLOR = textureColor;
#if IS_REQUIRED_ROUNDED_CORNER
- mediump float opacity = calculateCornerOpacity();
- OUT_COLOR *= opacity;
+ mediump float opacity = calculateCornerOpacity();
+ OUT_COLOR *= opacity;
+#endif
+
+#if IS_REQUIRED_ROUNDED_CORNER || IS_REQUIRED_BORDERLINE
+ }
#endif
}
if(abs(vPosition.x) < vOptRectSize.x && abs(vPosition.y) < vOptRectSize.y)
{
OUT_COLOR = textureColor;
- return;
}
- PreprocessPotential();
+ else
+ {
+ PreprocessPotential();
#endif
#if IS_REQUIRED_BORDERLINE
- textureColor = convertBorderlineColor(textureColor);
+ textureColor = convertBorderlineColor(textureColor);
#endif
- OUT_COLOR = textureColor;
+ OUT_COLOR = textureColor;
#if IS_REQUIRED_ROUNDED_CORNER
- mediump float opacity = calculateCornerOpacity();
- OUT_COLOR.a *= opacity;
- OUT_COLOR.rgb *= mix(1.0, opacity, preMultipliedAlpha);
+ mediump float opacity = calculateCornerOpacity();
+ OUT_COLOR.a *= opacity;
+ OUT_COLOR.rgb *= mix(1.0, opacity, preMultipliedAlpha);
+#endif
+
+#if IS_REQUIRED_ROUNDED_CORNER || IS_REQUIRED_BORDERLINE
+ }
#endif
}
--- /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
using namespace Dali::Toolkit;
-const uint32_t STAR = 0x2A; // Set default substitute character as '*'
const int DEFAULT_SHOW_DURATION = 1000;
namespace Dali
{
namespace Text
{
+
+static constexpr const uint32_t STAR = 0x2A; // Set default substitute character as '*'
+
/**
* Class to handle the hidden text
*/
#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.
+ *
+ */
+
+// HEADER
+#include <dali-toolkit/internal/text/text-controller-background-actor.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/rendering/renderer.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
+#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
+#include <dali-toolkit/internal/text/text-view.h>
+
+namespace Dali::Toolkit::Text
+{
+
+namespace
+{
+struct BackgroundVertex
+{
+ Vector2 mPosition; ///< Vertex posiiton
+ Vector4 mColor; ///< Vertex color
+};
+
+struct BackgroundMesh
+{
+ Vector<BackgroundVertex> mVertices; ///< container of vertices
+ Vector<unsigned short> mIndices; ///< container of indices
+};
+} // unnamed namespace
+
+Actor CreateControllerBackgroundActor(const View& textView, const VisualModelPtr& textVisualModel, Shader& textShaderBackground)
+{
+ // NOTE: Currently we only support background color for left-to-right text.
+
+ Actor actor;
+
+ Length numberOfGlyphs = textView.GetNumberOfGlyphs();
+ if(numberOfGlyphs > 0u)
+ {
+ Vector<GlyphInfo> glyphs;
+ glyphs.Resize(numberOfGlyphs);
+
+ Vector<Vector2> positions;
+ positions.Resize(numberOfGlyphs);
+
+ // Get the line where the glyphs are laid-out.
+ const LineRun* lineRun = textVisualModel->mLines.Begin();
+ float alignmentOffset = lineRun->alignmentOffset;
+ numberOfGlyphs = textView.GetGlyphs(glyphs.Begin(),
+ positions.Begin(),
+ alignmentOffset,
+ 0u,
+ numberOfGlyphs);
+
+ glyphs.Resize(numberOfGlyphs);
+ positions.Resize(numberOfGlyphs);
+
+ const GlyphInfo* const glyphsBuffer = glyphs.Begin();
+ const Vector2* const positionsBuffer = positions.Begin();
+
+ BackgroundMesh mesh;
+ mesh.mVertices.Reserve(4u * glyphs.Size());
+ mesh.mIndices.Reserve(6u * glyphs.Size());
+
+ const Vector2 textSize = textView.GetLayoutSize();
+
+ const float offsetX = alignmentOffset + textSize.width * 0.5f;
+ const float offsetY = textSize.height * 0.5f;
+
+ const Vector4* const backgroundColorsBuffer = textView.GetBackgroundColors();
+ const ColorIndex* const backgroundColorIndicesBuffer = textView.GetBackgroundColorIndices();
+ const Vector4& defaultBackgroundColor = textVisualModel->IsBackgroundEnabled() ? textVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
+
+ Vector4 quad;
+ uint32_t numberOfQuads = 0u;
+ Length yLineOffset = 0;
+ Length prevLineIndex = 0;
+ LineIndex lineIndex;
+ Length numberOfLines;
+
+ for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
+ {
+ const GlyphInfo& glyph = *(glyphsBuffer + i);
+
+ // Get the background color of the character.
+ // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
+ const bool isMarkupBackground = textView.IsMarkupBackgroundColorSet();
+ const ColorIndex backgroundColorIndex = isMarkupBackground ? *(backgroundColorIndicesBuffer + i) : 0u;
+ const bool isDefaultBackgroundColor = (0u == backgroundColorIndex);
+ const Vector4& backgroundColor = isDefaultBackgroundColor ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
+
+ textVisualModel->GetNumberOfLines(i, 1, lineIndex, numberOfLines);
+ Length lineHeight = lineRun[lineIndex].ascender + -(lineRun[lineIndex].descender) + lineRun[lineIndex].lineSpacing;
+
+ if(lineIndex != prevLineIndex)
+ {
+ yLineOffset += lineHeight;
+ }
+
+ // Only create quads for glyphs with a background color
+ if(backgroundColor != Color::TRANSPARENT)
+ {
+ const Vector2 position = *(positionsBuffer + i);
+
+ if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
+ {
+ quad.x = position.x;
+ quad.y = yLineOffset;
+ quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
+ quad.w = lineHeight;
+ }
+ else if((lineIndex != prevLineIndex) || (i == 0u)) // The first glyph in the line
+ {
+ quad.x = position.x;
+ quad.y = yLineOffset;
+ quad.z = quad.x - glyph.xBearing + glyph.advance;
+ quad.w = quad.y + lineHeight;
+ }
+ else if(i == glyphSize - 1u) // The last glyph in the whole text
+ {
+ quad.x = position.x - glyph.xBearing;
+ quad.y = yLineOffset;
+ quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
+ quad.w = quad.y + lineHeight;
+ }
+ else // The glyph in the middle of the text
+ {
+ quad.x = position.x - glyph.xBearing;
+ quad.y = yLineOffset;
+ quad.z = quad.x + glyph.advance;
+ quad.w = quad.y + lineHeight;
+ }
+
+ BackgroundVertex vertex;
+
+ // Top left
+ vertex.mPosition.x = quad.x - offsetX;
+ vertex.mPosition.y = quad.y - offsetY;
+ vertex.mColor = backgroundColor;
+ mesh.mVertices.PushBack(vertex);
+
+ // Top right
+ vertex.mPosition.x = quad.z - offsetX;
+ vertex.mPosition.y = quad.y - offsetY;
+ vertex.mColor = backgroundColor;
+ mesh.mVertices.PushBack(vertex);
+
+ // Bottom left
+ vertex.mPosition.x = quad.x - offsetX;
+ vertex.mPosition.y = quad.w - offsetY;
+ vertex.mColor = backgroundColor;
+ mesh.mVertices.PushBack(vertex);
+
+ // Bottom right
+ vertex.mPosition.x = quad.z - offsetX;
+ vertex.mPosition.y = quad.w - offsetY;
+ vertex.mColor = backgroundColor;
+ mesh.mVertices.PushBack(vertex);
+
+ // Six indices in counter clockwise winding
+ mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
+ mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
+ mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
+ mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
+ mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
+ mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
+
+ numberOfQuads++;
+ }
+
+ if(lineIndex != prevLineIndex)
+ {
+ prevLineIndex = lineIndex;
+ }
+ }
+
+ // Only create the background actor if there are glyphs with background color
+ if(mesh.mVertices.Count() > 0u)
+ {
+ Property::Map quadVertexFormat;
+ quadVertexFormat["aPosition"] = Property::VECTOR2;
+ quadVertexFormat["aColor"] = Property::VECTOR4;
+
+ VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
+ quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
+
+ Geometry quadGeometry = Geometry::New();
+ quadGeometry.AddVertexBuffer(quadVertices);
+ quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
+
+ if(!textShaderBackground)
+ {
+ textShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
+ }
+
+ Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, textShaderBackground);
+ renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
+ renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
+
+ actor = Actor::New();
+ actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
+ actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
+ actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+ actor.SetProperty(Actor::Property::SIZE, textSize);
+ actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
+ actor.AddRenderer(renderer);
+ }
+ }
+
+ return actor;
+}
+
+} // namespace Dali::Toolkit::Text
--- /dev/null
+#ifndef DALI_TOOLKIT_TEXT_CONTROLLER_BACKGROUND_ACTOR_H
+#define DALI_TOOLKIT_TEXT_CONTROLLER_BACKGROUND_ACTOR_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/actors/actor.h>
+#include <dali/public-api/rendering/shader.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/visual-model-impl.h>
+
+namespace Dali::Toolkit::Text
+{
+class View;
+
+/**
+ * @brief Create an actor that renders the text background color
+ *
+ * @return the created actor or an empty handle if no background color needs to be rendered.
+ */
+Actor CreateControllerBackgroundActor(const View& textView, const VisualModelPtr& textVisualModel, Shader& textShaderBackground);
+
+} // namespace Dali::Toolkit::Text
+
+#endif // DALI_TOOLKIT_TEXT_CONTROLLER_BACKGROUND_ACTOR_H
// INTERNAL INCLUDES
#include <dali-toolkit/internal/text/cursor-helper-functions.h>
#include <dali-toolkit/internal/text/text-controller-impl.h>
+#include <dali-toolkit/internal/text/text-controller-placeholder-handler.h>
+#include <dali-toolkit/internal/text/text-controller-text-updater.h>
#include <dali-toolkit/internal/text/text-editable-control-interface.h>
namespace
if(controller.mImpl->IsShowingPlaceholderText())
{
// Show alternative placeholder-text when editing
- controller.ShowPlaceholderText();
+ PlaceholderHandler::ShowPlaceholderText(*controller.mImpl);
}
controller.mImpl->RequestRelayout();
if(!controller.mImpl->IsShowingRealText())
{
// Revert to regular placeholder-text when not editing
- controller.ShowPlaceholderText();
+ PlaceholderHandler::ShowPlaceholderText(*controller.mImpl);
}
}
}
else if((Dali::DALI_KEY_BACKSPACE == keyCode) ||
(Dali::DevelKey::DALI_KEY_DELETE == keyCode))
{
- textChanged = controller.DeleteEvent(keyCode);
+ textChanged = DeleteEvent(controller, keyCode);
// Will request for relayout.
relayoutNeeded = true;
// InputMethodContext is no longer handling key-events
controller.mImpl->ClearPreEditFlag();
- controller.InsertText(refinedKey, COMMIT);
+ TextUpdater::InsertText(controller, refinedKey, COMMIT);
textChanged = true;
if(controller.mImpl->IsShowingPlaceholderText() && !controller.mImpl->IsFocusedPlaceholderAvailable())
{
// Hide placeholder text
- controller.ResetText();
+ TextUpdater::ResetText(controller);
}
if(EventData::INACTIVE == state)
// A (single) replace event should come first, otherwise we wasted time processing NOOP events
DALI_ASSERT_DEBUG(it == events.Begin() && "Unexpected TEXT_REPLACED event");
- controller.TextReplacedEvent();
+ TextReplacedEvent(controller);
}
else if(ModifyEvent::TEXT_INSERTED == event.type)
{
- controller.TextInsertedEvent();
+ TextInsertedEvent(controller);
}
else if(ModifyEvent::TEXT_DELETED == event.type)
{
// Placeholder-text cannot be deleted
if(!controller.mImpl->IsShowingPlaceholderText())
{
- controller.TextDeletedEvent();
+ TextDeletedEvent(controller);
}
}
}
if(EventData::SELECTING == controller.mImpl->mEventData->mState)
{
- removed = controller.RemoveSelectedText();
+ removed = TextUpdater::RemoveSelectedText(controller);
}
else if((controller.mImpl->mEventData->mPrimaryCursorPosition > 0) && (keyCode == Dali::DALI_KEY_BACKSPACE))
{
// Remove the character before the current cursor position
- removed = controller.RemoveText(-1,
- 1,
- UPDATE_INPUT_STYLE);
+ removed = TextUpdater::RemoveText(controller, -1, 1, UPDATE_INPUT_STYLE);
}
else if((controller.mImpl->mEventData->mPrimaryCursorPosition < controller.mImpl->mModel->mLogicalModel->mText.Count()) &&
(keyCode == Dali::DevelKey::DALI_KEY_DELETE))
{
// Remove the character after the current cursor position
- removed = controller.RemoveText(0,
- 1,
- UPDATE_INPUT_STYLE);
+ removed = TextUpdater::RemoveText(controller, 0, 1, UPDATE_INPUT_STYLE);
}
if(removed)
}
else
{
- controller.ShowPlaceholderText();
+ PlaceholderHandler::ShowPlaceholderText(*controller.mImpl);
}
controller.mImpl->mEventData->mUpdateCursorPosition = true;
controller.mImpl->mEventData->mScrollAfterDelete = true;
{
case InputMethodContext::COMMIT:
{
- controller.InsertText(inputMethodContextEvent.predictiveString, Text::Controller::COMMIT);
+ TextUpdater::InsertText(controller, inputMethodContextEvent.predictiveString, Text::Controller::COMMIT);
requestRelayout = true;
retrieveCursor = true;
break;
}
case InputMethodContext::PRE_EDIT:
{
- controller.InsertText(inputMethodContextEvent.predictiveString, Text::Controller::PRE_EDIT);
+ TextUpdater::InsertText(controller, inputMethodContextEvent.predictiveString, Text::Controller::PRE_EDIT);
requestRelayout = true;
retrieveCursor = true;
break;
}
case InputMethodContext::DELETE_SURROUNDING:
{
- const bool textDeleted = controller.RemoveText(inputMethodContextEvent.cursorOffset,
- inputMethodContextEvent.numberOfChars,
- DONT_UPDATE_INPUT_STYLE);
+ const bool textDeleted = TextUpdater::RemoveText(controller,
+ inputMethodContextEvent.cursorOffset,
+ inputMethodContextEvent.numberOfChars,
+ DONT_UPDATE_INPUT_STYLE);
if(textDeleted)
{
}
else
{
- controller.ShowPlaceholderText();
+ PlaceholderHandler::ShowPlaceholderText(*controller.mImpl);
}
controller.mImpl->mEventData->mUpdateCursorPosition = true;
controller.mImpl->mEventData->mScrollAfterDelete = true;
controller.mImpl->SetClipboardHideEnable(false);
// Paste
- controller.PasteText(stringToPaste);
+ TextUpdater::PasteText(controller, stringToPaste);
controller.mImpl->SetClipboardHideEnable(true);
}
if(controller.mImpl->mEventData->mSelectionEnabled)
{
// Creates a SELECT event.
- controller.SelectEvent(currentCursorPosition.x, currentCursorPosition.y, SelectionType::INTERACTIVE);
+ SelectEvent(controller, currentCursorPosition.x, currentCursorPosition.y, SelectionType::INTERACTIVE);
}
break;
}
case Toolkit::TextSelectionPopup::SELECT_ALL:
{
// Creates a SELECT_ALL event
- controller.SelectEvent(0.f, 0.f, SelectionType::ALL);
+ SelectEvent(controller, 0.f, 0.f, SelectionType::ALL);
break;
}
case Toolkit::TextSelectionPopup::CLIPBOARD:
{
struct Controller::EventHandler
{
+ /// @copydoc Text::Controller::KeyboardFocusGainEvent
+ /// @param[in] controller A reference to the controller class
static void KeyboardFocusGainEvent(Controller& controller);
+
+ /// @copydoc Text::Controller::KeyboardFocusLostEvent
+ /// @param[in] controller A reference to the controller class
static void KeyboardFocusLostEvent(Controller& controller);
+
+ /// @copydoc Text::Controller::KeyEvent
+ /// @param[in] controller A reference to the controller class
static bool KeyEvent(Controller& controller, const Dali::KeyEvent& keyEvent);
+
+ /// @copydoc Text::Controller::AnchorEvent
+ /// @param[in] controller A reference to the controller class
static void AnchorEvent(Controller& controller, float x, float y);
+
+ /// @copydoc Text::Controller::TapEvent
+ /// @param[in] controller A reference to the controller class
static void TapEvent(Controller& controller, unsigned int tapCount, float x, float y);
+
+ /// @copydoc Text::Controller::PanEvent
+ /// @param[in] controller A reference to the controller class
static void PanEvent(Controller& controller, GestureState state, const Vector2& displacement);
+
+ /// @copydoc Text::Controller::LongPressEvent
+ /// @param[in] controller A reference to the controller class
static void LongPressEvent(Controller& controller, GestureState state, float x, float y);
+
+ /// @copydoc Text::Controller::SelectEvent
+ /// @param[in] controller A reference to the controller class
static void SelectEvent(Controller& controller, float x, float y, SelectionType selectType);
+
+ /**
+ * @brief Creates a selection event with a selection index.
+ *
+ * It could be called from the SelectText().
+ * The start and end parameters are passed through the event.
+ *
+ * @param[in] controller A reference to the controller class
+ * @param[in] start The start selection position.
+ * @param[in] end The end selection position.
+ * @param[in] selection type like the range.
+ */
static void SelectEvent(Controller& controller, const uint32_t start, const uint32_t end, SelectionType selectType);
+
+ /**
+ * @brief Process queued events which modify the model.
+ * @param[in] controller A reference to the controller class
+ */
static void ProcessModifyEvents(Controller& controller);
+
+ /**
+ * @brief Used to process an event queued from SetText()
+ * @param[in] controller A reference to the controller class
+ */
static void TextReplacedEvent(Controller& controller);
+
+ /**
+ * @brief Used to process an event queued from key events etc.
+ * @param[in] controller A reference to the controller class
+ */
static void TextInsertedEvent(Controller& controller);
+
+ /**
+ * @brief Used to process an event queued from backspace key etc.
+ * @param[in] controller A reference to the controller class
+ */
static void TextDeletedEvent(Controller& controller);
+
+ /**
+ * @brief Helper to KeyEvent() to handle the backspace or delete key case.
+ *
+ * @param[in] controller A reference to the controller class
+ * @param[in] keyCode The keycode for the key pressed
+ * @return True if a character was deleted.
+ */
static bool DeleteEvent(Controller& controller, int keyCode);
static InputMethodContext::CallbackData OnInputMethodContextEvent(Controller& controller,
--- /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/public-api/actors/layer.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/graphics/builtin-shader-extern-gen.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-controller-placeholder-handler.h>
+#include <dali-toolkit/internal/text/text-controller-relayouter.h>
#include <dali-toolkit/internal/text/text-editable-control-interface.h>
#include <dali-toolkit/internal/text/text-enumerations-impl.h>
#include <dali-toolkit/internal/text/text-run-container.h>
Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
#endif
-struct BackgroundVertex
-{
- Vector2 mPosition; ///< Vertex posiiton
- Vector4 mColor; ///< Vertex color
-};
+constexpr float MAX_FLOAT = std::numeric_limits<float>::max();
-struct BackgroundMesh
-{
- Vector<BackgroundVertex> mVertices; ///< container of vertices
- Vector<unsigned short> mIndices; ///< container of indices
-};
+const std::string EMPTY_STRING("");
} // namespace
}
}
}
+
+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)
return numberOfWhiteSpaces;
}
+void Controller::Impl::GetText(std::string& text) const
+{
+ if(!IsShowingPlaceholderText())
+ {
+ // Retrieves the text string.
+ GetText(0u, text);
+ }
+ else
+ {
+ DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::GetText %p empty (but showing placeholder)\n", this);
+ }
+}
+
void Controller::Impl::GetText(CharacterIndex index, std::string& text) const
{
// Get the total number of characters.
}
}
+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>());
+ }
+}
+
+Toolkit::DevelText::TextDirection::Type Controller::Impl::GetTextDirection()
+{
+ if(mUpdateTextDirection)
+ {
+ // Operations that can be done only once until the text changes.
+ const OperationsMask onlyOnceOperations = static_cast<OperationsMask>(CONVERT_TO_UTF32 |
+ GET_SCRIPTS |
+ VALIDATE_FONTS |
+ GET_LINE_BREAKS |
+ BIDI_INFO |
+ SHAPE_TEXT |
+ GET_GLYPH_METRICS);
+
+ // Set the update info to relayout the whole text.
+ mTextUpdateInfo.mParagraphCharacterIndex = 0u;
+ mTextUpdateInfo.mRequestedNumberOfCharacters = mModel->mLogicalModel->mText.Count();
+
+ // Make sure the model is up-to-date before layouting
+ UpdateModel(onlyOnceOperations);
+
+ Vector3 naturalSize;
+ Relayouter::DoRelayout(*this,
+ Size(MAX_FLOAT, MAX_FLOAT),
+ static_cast<OperationsMask>(onlyOnceOperations |
+ LAYOUT | REORDER | UPDATE_DIRECTION),
+ naturalSize.GetVectorXY());
+
+ // Do not do again the only once operations.
+ mOperationsPending = static_cast<OperationsMask>(mOperationsPending & ~onlyOnceOperations);
+
+ // Clear the update info. This info will be set the next time the text is updated.
+ mTextUpdateInfo.Clear();
+
+ // FullRelayoutNeeded should be true because DoRelayout is MAX_FLOAT, MAX_FLOAT.
+ mTextUpdateInfo.mFullRelayoutNeeded = true;
+
+ mUpdateTextDirection = false;
+ }
+
+ return mIsTextDirectionRTL ? Toolkit::DevelText::TextDirection::RIGHT_TO_LEFT : Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT;
+}
+
void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters)
{
mTextUpdateInfo.mParagraphCharacterIndex = 0u;
mTextUpdateInfo.mStartGlyphIndex = *(mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex);
}
-void Controller::Impl::ClearFullModelData(OperationsMask operations)
+void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
{
- if(NO_OPERATION != (GET_LINE_BREAKS & operations))
- {
- mModel->mLogicalModel->mLineBreakInfo.Clear();
- mModel->mLogicalModel->mParagraphInfo.Clear();
- }
+ ControllerImplDataClearer::ClearModelData(*this, startIndex, endIndex, operations);
+}
- if(NO_OPERATION != (GET_SCRIPTS & operations))
- {
- mModel->mLogicalModel->mScriptRuns.Clear();
- }
+bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
+{
+ return ControllerImplModelUpdater::Update(*this, operationsRequired);
+}
- if(NO_OPERATION != (VALIDATE_FONTS & operations))
- {
- mModel->mLogicalModel->mFontRuns.Clear();
- }
+void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
+{
+ SetDefaultInputStyle(inputStyle, mFontDefaults, mTextColor);
+}
- if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
+float Controller::Impl::GetDefaultFontLineHeight()
+{
+ FontId defaultFontId = 0u;
+ if(nullptr == mFontDefaults)
{
- 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();
- }
+ TextAbstraction::FontDescription fontDescription;
+ defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale);
}
-
- if(NO_OPERATION != (SHAPE_TEXT & operations))
+ else
{
- mModel->mVisualModel->mGlyphs.Clear();
- mModel->mVisualModel->mGlyphsToCharacters.Clear();
- mModel->mVisualModel->mCharactersToGlyph.Clear();
- mModel->mVisualModel->mCharactersPerGlyph.Clear();
- mModel->mVisualModel->mGlyphsPerCharacter.Clear();
- mModel->mVisualModel->mGlyphPositions.Clear();
+ defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale);
}
- if(NO_OPERATION != (LAYOUT & operations))
- {
- mModel->mVisualModel->mLines.Clear();
- }
+ Text::FontMetrics fontMetrics;
+ mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
- if(NO_OPERATION != (COLOR & operations))
- {
- mModel->mVisualModel->mColorIndices.Clear();
- mModel->mVisualModel->mBackgroundColorIndices.Clear();
- }
+ return (fontMetrics.ascender - fontMetrics.descender);
}
-void Controller::Impl::ClearCharacterModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
+bool Controller::Impl::SetDefaultLineSpacing(float lineSpacing)
{
- const CharacterIndex endIndexPlusOne = endIndex + 1u;
-
- if(NO_OPERATION != (GET_LINE_BREAKS & operations))
+ if(std::fabs(lineSpacing - mLayoutEngine.GetDefaultLineSpacing()) > Math::MACHINE_EPSILON_1000)
{
- // Clear the line break info.
- LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
+ mLayoutEngine.SetDefaultLineSpacing(lineSpacing);
+ mRecalculateNaturalSize = true;
- mModel->mLogicalModel->mLineBreakInfo.Erase(lineBreakInfoBuffer + startIndex,
- lineBreakInfoBuffer + endIndexPlusOne);
-
- // Clear the paragraphs.
- ClearCharacterRuns(startIndex,
- endIndex,
- mModel->mLogicalModel->mParagraphInfo);
+ RelayoutForNewLineSize();
+ return true;
}
+ return false;
+}
- if(NO_OPERATION != (GET_SCRIPTS & operations))
+bool Controller::Impl::SetDefaultLineSize(float lineSize)
+{
+ if(std::fabs(lineSize - mLayoutEngine.GetDefaultLineSize()) > Math::MACHINE_EPSILON_1000)
{
- // Clear the scripts.
- ClearCharacterRuns(startIndex,
- endIndex,
- mModel->mLogicalModel->mScriptRuns);
- }
+ mLayoutEngine.SetDefaultLineSize(lineSize);
+ mRecalculateNaturalSize = true;
- if(NO_OPERATION != (VALIDATE_FONTS & operations))
- {
- // Clear the fonts.
- ClearCharacterRuns(startIndex,
- endIndex,
- mModel->mLogicalModel->mFontRuns);
+ RelayoutForNewLineSize();
+ return true;
}
+ return false;
+}
- if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
+string Controller::Impl::GetSelectedText()
+{
+ string text;
+ if(EventData::SELECTING == mEventData->mState)
{
- 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);
- }
+ RetrieveSelection(text, false);
}
+ return text;
}
-void Controller::Impl::ClearGlyphModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
+string Controller::Impl::CopyText()
{
- 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);
+ string text;
+ RetrieveSelection(text, false);
+ SendSelectionToClipboard(false); // Text not modified
- // Clear the glyphs buffer.
- GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
- mModel->mVisualModel->mGlyphs.Erase(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
- glyphsBuffer + endGlyphIndexPlusOne);
+ mEventData->mUpdateCursorPosition = true;
- 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;
- }
+ RequestRelayout(); // Cursor, Handles, Selection Highlight, Popup
- // 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);
+ return text;
+}
- // 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);
- }
- }
+string Controller::Impl::CutText()
+{
+ string text;
+ RetrieveSelection(text, false);
- if(NO_OPERATION != (LAYOUT & operations))
+ if(!IsEditable())
{
- // 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);
+ return EMPTY_STRING;
}
- 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);
- }
- }
-}
+ SendSelectionToClipboard(true); // Synchronous call to modify text
+ mOperationsPending = ALL_OPERATIONS;
-void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
-{
- if(mTextUpdateInfo.mClearAll ||
- ((0u == startIndex) &&
- (mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u)))
+ if((0u != mModel->mLogicalModel->mText.Count()) ||
+ !IsPlaceholderAvailable())
{
- ClearFullModelData(operations);
+ QueueModifyEvent(ModifyEvent::TEXT_DELETED);
}
else
{
- // Clear the model data related with characters.
- ClearCharacterModelData(startIndex, endIndex, operations);
-
- // Clear the model data related with glyphs.
- ClearGlyphModelData(startIndex, endIndex, operations);
+ PlaceholderHandler::ShowPlaceholderText(*this);
}
- // 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();
-}
-
-bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
-{
- return ControllerImplModelUpdater::Update(*this, operationsRequired);
-}
+ mEventData->mUpdateCursorPosition = true;
+ mEventData->mScrollAfterDelete = true;
-void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
-{
- SetDefaultInputStyle(inputStyle, mFontDefaults, mTextColor);
-}
+ RequestRelayout();
-float Controller::Impl::GetDefaultFontLineHeight()
-{
- FontId defaultFontId = 0u;
- if(nullptr == mFontDefaults)
- {
- TextAbstraction::FontDescription fontDescription;
- defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale);
- }
- else
+ if(nullptr != mEditableControlInterface)
{
- defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale);
+ mEditableControlInterface->TextChanged(true);
}
-
- Text::FontMetrics fontMetrics;
- mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
-
- return (fontMetrics.ascender - fontMetrics.descender);
+ return text;
}
void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd)
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();
}
}
bool isEditable = IsEditable();
TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
- if(EventData::SELECTING == mEventData->mState)
- {
- buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
- if(isEditable)
- {
- buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
- }
-
- if(!IsClipboardEmpty())
- {
- if(isEditable)
- {
- buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
- }
- buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
- }
-
- if(!mEventData->mAllTextSelected)
- {
- buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
- }
- }
- else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
- {
- if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
- {
- buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
- }
-
- if(!IsClipboardEmpty())
- {
- if(isEditable)
- {
- buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
- }
- buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
- }
- }
- else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
- {
- if(!IsClipboardEmpty())
- {
- if(isEditable)
- {
- buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
- }
- buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
- }
- }
-
- mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
-}
-
-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);
+ if(EventData::SELECTING == mEventData->mState)
+ {
+ buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
+ if(isEditable)
+ {
+ buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
+ }
- 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:
+ if(!IsClipboardEmpty())
+ {
+ if(isEditable)
{
- 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;
+ buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
}
- 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();
- }
+ buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
+ }
- 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->mAllTextSelected)
+ {
+ buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
+ }
+ }
+ else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
+ {
+ if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
+ {
+ buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
+ }
- if(mEventData->mGrabHandlePopupEnabled)
- {
- SetPopupButtons();
- mEventData->mDecorator->SetPopupActive(true);
- }
- mEventData->mDecoratorUpdated = true;
- break;
+ if(!IsClipboardEmpty())
+ {
+ if(isEditable)
+ {
+ buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
}
- case EventData::TEXT_PANNING:
+ buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
+ }
+ }
+ else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
+ {
+ if(!IsClipboardEmpty())
+ {
+ if(isEditable)
{
- 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;
+ buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
}
+ buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
}
}
+
+ mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
+}
+
+void Controller::Impl::ChangeState(EventData::State newState)
+{
+ ChangeTextControllerState(*this, newState);
}
void Controller::Impl::GetCursorPosition(CharacterIndex logical,
}
}
-Actor Controller::Impl::CreateBackgroundActor()
+void Controller::Impl::RelayoutForNewLineSize()
{
- // NOTE: Currently we only support background color for left-to-right text.
+ // relayout all characters
+ mTextUpdateInfo.mCharacterIndex = 0;
+ mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
+ mTextUpdateInfo.mNumberOfCharactersToAdd = mModel->mLogicalModel->mText.Count();
+ mOperationsPending = static_cast<OperationsMask>(mOperationsPending | LAYOUT);
- Actor actor;
-
- Length numberOfGlyphs = mView.GetNumberOfGlyphs();
- if(numberOfGlyphs > 0u)
+ //remove selection
+ if(mEventData && mEventData->mState == EventData::SELECTING)
{
- Vector<GlyphInfo> glyphs;
- glyphs.Resize(numberOfGlyphs);
-
- Vector<Vector2> positions;
- positions.Resize(numberOfGlyphs);
-
- // Get the line where the glyphs are laid-out.
- const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
- float alignmentOffset = lineRun->alignmentOffset;
- numberOfGlyphs = mView.GetGlyphs(glyphs.Begin(),
- positions.Begin(),
- alignmentOffset,
- 0u,
- numberOfGlyphs);
-
- glyphs.Resize(numberOfGlyphs);
- positions.Resize(numberOfGlyphs);
+ ChangeState(EventData::EDITING);
+ }
- const GlyphInfo* const glyphsBuffer = glyphs.Begin();
- const Vector2* const positionsBuffer = positions.Begin();
+ RequestRelayout();
+}
- BackgroundMesh mesh;
- mesh.mVertices.Reserve(4u * glyphs.Size());
- mesh.mIndices.Reserve(6u * glyphs.Size());
+bool Controller::Impl::IsInputStyleChangedSignalsQueueEmpty()
+{
+ return (NULL == mEventData) || (0u == mEventData->mInputStyleChangedQueue.Count());
+}
- const Vector2 textSize = mView.GetLayoutSize();
+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); } );
+ }
- const float offsetX = alignmentOffset + textSize.width * 0.5f;
- const float offsetY = textSize.height * 0.5f;
+ mEventData->mInputStyleChangedQueue.Clear();
+ }
+}
- const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
- const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
- const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
+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;
- Vector4 quad;
- uint32_t numberOfQuads = 0u;
- Length yLineOffset = 0;
- Length prevLineIndex = 0;
- LineIndex lineIndex;
- Length numberOfLines;
+ scroll.x = -scroll.x;
+ scroll.y = -scroll.y;
- for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
+ if(fabs(scroll.x) > Math::MACHINE_EPSILON_0)
{
- const GlyphInfo& glyph = *(glyphsBuffer + i);
-
- // Get the background color of the character.
- // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
- const bool isMarkupBackground = mView.IsMarkupBackgroundColorSet();
- const ColorIndex backgroundColorIndex = isMarkupBackground ? *(backgroundColorIndicesBuffer + i) : 0u;
- const bool isDefaultBackgroundColor = (0u == backgroundColorIndex);
- const Vector4& backgroundColor = isDefaultBackgroundColor ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
-
- mModel->mVisualModel->GetNumberOfLines(i, 1, lineIndex, numberOfLines);
- Length lineHeight = lineRun[lineIndex].ascender + -(lineRun[lineIndex].descender) + lineRun[lineIndex].lineSpacing;
+ mModel->mScrollPosition.x += scroll.x;
+ ClampHorizontalScroll(layoutSize);
+ }
- if(lineIndex != prevLineIndex)
- {
- yLineOffset += lineHeight;
- }
+ if(fabs(scroll.y) > Math::MACHINE_EPSILON_0)
+ {
+ mModel->mScrollPosition.y += scroll.y;
+ ClampVerticalScroll(layoutSize);
+ }
- // Only create quads for glyphs with a background color
- if(backgroundColor != Color::TRANSPARENT)
- {
- const Vector2 position = *(positionsBuffer + i);
+ if(mModel->mScrollPosition != currentScroll)
+ {
+ mEventData->mDecorator->UpdatePositions(mModel->mScrollPosition - currentScroll);
+ RequestRelayout();
+ }
+ }
+}
- if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
- {
- quad.x = position.x;
- quad.y = yLineOffset;
- quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
- quad.w = lineHeight;
- }
- else if((lineIndex != prevLineIndex) || (i == 0u)) // The first glyph in the line
- {
- quad.x = position.x;
- quad.y = yLineOffset;
- quad.z = quad.x - glyph.xBearing + glyph.advance;
- quad.w = quad.y + lineHeight;
- }
- else if(i == glyphSize - 1u) // The last glyph in the whole text
- {
- quad.x = position.x - glyph.xBearing;
- quad.y = yLineOffset;
- quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
- quad.w = quad.y + lineHeight;
- }
- else // The glyph in the middle of the text
- {
- quad.x = position.x - glyph.xBearing;
- quad.y = yLineOffset;
- quad.z = quad.x + glyph.advance;
- quad.w = quad.y + lineHeight;
- }
+float Controller::Impl::GetHorizontalScrollPosition()
+{
+ // Scroll values are negative internally so we convert them to positive numbers
+ return mEventData ? -mModel->mScrollPosition.x : 0.0f;
+}
- BackgroundVertex vertex;
-
- // Top left
- vertex.mPosition.x = quad.x - offsetX;
- vertex.mPosition.y = quad.y - offsetY;
- vertex.mColor = backgroundColor;
- mesh.mVertices.PushBack(vertex);
-
- // Top right
- vertex.mPosition.x = quad.z - offsetX;
- vertex.mPosition.y = quad.y - offsetY;
- vertex.mColor = backgroundColor;
- mesh.mVertices.PushBack(vertex);
-
- // Bottom left
- vertex.mPosition.x = quad.x - offsetX;
- vertex.mPosition.y = quad.w - offsetY;
- vertex.mColor = backgroundColor;
- mesh.mVertices.PushBack(vertex);
-
- // Bottom right
- vertex.mPosition.x = quad.z - offsetX;
- vertex.mPosition.y = quad.w - offsetY;
- vertex.mColor = backgroundColor;
- mesh.mVertices.PushBack(vertex);
-
- // Six indices in counter clockwise winding
- mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
- mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
- mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
- mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
- mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
- mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
-
- numberOfQuads++;
- }
+float Controller::Impl::GetVerticalScrollPosition()
+{
+ // Scroll values are negative internally so we convert them to positive numbers
+ return mEventData ? -mModel->mScrollPosition.y : 0.0f;
+}
- if(lineIndex != prevLineIndex)
- {
- prevLineIndex = lineIndex;
- }
- }
+Vector3 Controller::Impl::GetAnchorPosition(Anchor anchor) const
+{
+ //TODO
+ return Vector3(10.f, 10.f, 10.f);
+}
- // Only create the background actor if there are glyphs with background color
- if(mesh.mVertices.Count() > 0u)
- {
- Property::Map quadVertexFormat;
- quadVertexFormat["aPosition"] = Property::VECTOR2;
- quadVertexFormat["aColor"] = Property::VECTOR4;
+Vector2 Controller::Impl::GetAnchorSize(Anchor anchor) const
+{
+ //TODO
+ return Vector2(10.f, 10.f);
+}
- VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
- quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
+Toolkit::TextAnchor Controller::Impl::CreateAnchorActor(Anchor anchor)
+{
+ auto actor = Toolkit::TextAnchor::New();
+ actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
+ actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+ const Vector3 anchorPosition = GetAnchorPosition(anchor);
+ actor.SetProperty(Actor::Property::POSITION, anchorPosition);
+ const Vector2 anchorSize = GetAnchorSize(anchor);
+ actor.SetProperty(Actor::Property::SIZE, anchorSize);
+ std::string anchorText(mModel->mLogicalModel->mText.Begin() + anchor.startIndex, mModel->mLogicalModel->mText.Begin() + anchor.endIndex);
+ actor.SetProperty(Actor::Property::NAME, anchorText);
+ actor.SetProperty(Toolkit::TextAnchor::Property::URI, std::string(anchor.href));
+ actor.SetProperty(Toolkit::TextAnchor::Property::START_CHARACTER_INDEX, static_cast<int>(anchor.startIndex));
+ actor.SetProperty(Toolkit::TextAnchor::Property::END_CHARACTER_INDEX, static_cast<int>(anchor.endIndex));
+ return actor;
+}
- Geometry quadGeometry = Geometry::New();
- quadGeometry.AddVertexBuffer(quadVertices);
- quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
+void Controller::Impl::GetAnchorActors(std::vector<Toolkit::TextAnchor>& anchorActors)
+{
+ /* TODO: Now actors are created/destroyed in every "RenderText" function call. Even when we add just 1 character,
+ we need to create and destroy potentially many actors. Some optimization can be considered here.
+ Maybe a "dirty" flag in mLogicalModel? */
+ anchorActors.clear();
+ for(auto& anchor : mModel->mLogicalModel->mAnchors)
+ {
+ auto actor = CreateAnchorActor(anchor);
+ anchorActors.push_back(actor);
+ }
+}
- if(!mShaderBackground)
- {
- mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
- }
+int32_t Controller::Impl::GetAnchorIndex(size_t characterOffset) const
+{
+ Vector<Anchor>::Iterator it = mModel->mLogicalModel->mAnchors.Begin();
- Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, mShaderBackground);
- renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
- renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
-
- actor = Actor::New();
- actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
- actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
- actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
- actor.SetProperty(Actor::Property::SIZE, textSize);
- actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
- actor.AddRenderer(renderer);
- }
+ while(it != mModel->mLogicalModel->mAnchors.End() && (it->startIndex > characterOffset || it->endIndex <= characterOffset))
+ {
+ it++;
}
- return actor;
+ return it == mModel->mLogicalModel->mAnchors.End() ? -1 : it - mModel->mLogicalModel->mAnchors.Begin();
}
void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
}
}
+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
mTextFitMinSize(DEFAULT_TEXTFIT_MIN),
mTextFitMaxSize(DEFAULT_TEXTFIT_MAX),
mTextFitStepSize(DEFAULT_TEXTFIT_STEP),
- mTextFitEnabled(false),
mFontSizeScale(DEFAULT_FONT_SIZE_SCALE),
+ mTextFitEnabled(false),
+ mTextFitChanged(false),
mIsLayoutDirectionChanged(false)
{
mModel = Model::New();
Length GetNumberOfWhiteSpaces(CharacterIndex index) const;
/**
+ * @brief Retrieve any text previously set.
+ *
+ * @param[out] text A string of UTF-8 characters.
+ */
+ void GetText(std::string& text) const;
+
+ /**
* @brief Retrieve any text previously set starting from the given @p index.
*
* @param[in] index The character index from where to retrieve the text.
}
/**
- * @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.
- *
- * @param[out] numberOfCharacters The number of characters 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.
+ * @copydoc Controller::GetLayoutDirection()
*/
- void ClearFullModelData(OperationsMask operations);
+ Dali::LayoutDirection::Type GetLayoutDirection(Dali::Actor& actor) const;
/**
- * @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.
+ * @brief Checks text direction.
+ * @return The text direction.
*/
- void ClearCharacterModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations);
+ Toolkit::DevelText::TextDirection::Type GetTextDirection();
/**
- * @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.
+ * @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.
*
- * @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.
+ * @param[out] numberOfCharacters The number of characters to be updated.
*/
- void ClearGlyphModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations);
+ void CalculateTextUpdateIndices(Length& numberOfCharacters);
/**
* @brief Helper to clear the parts of the model specified by the given @p operations and from @p startIndex to @p endIndex.
float GetDefaultFontLineHeight();
/**
+ * @copydoc Controller::SetDefaultLineSpacing
+ */
+ bool SetDefaultLineSpacing(float lineSpacing);
+
+ /**
+ * @copydoc Controller::SetDefaultLineSize
+ */
+ bool SetDefaultLineSize(float lineSize);
+
+ /**
* @copydoc Text::Controller::GetPrimaryCursorPosition()
*/
CharacterIndex GetPrimaryCursorPosition() const;
bool SetPrimaryCursorPosition(CharacterIndex index, bool focused);
/**
+ * @copydoc Text::SelectableControlInterface::GetSelectedText()
+ */
+ string GetSelectedText();
+
+ /**
+ * @copydoc Text::EditableControlInterface::CopyText()
+ */
+ string CopyText();
+
+ /**
+ * @copydoc Text::EditableControlInterface::CutText()
+ */
+ string CutText();
+
+ /**
* @copydoc Text::SelectableControlInterface::SetTextSelectionRange()
*/
void SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEndf);
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.
void ScrollTextToMatchCursor();
/**
- * @brief Create an actor that renders the text background color
+ * @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();
+
+ /**
+ * @brief Resets a provided vector with actors that marks the position of anchors in markup enabled text
+ *
+ * @param[out] anchorActors the vector of actor (empty collection if no anchors available).
+ */
+ void GetAnchorActors(std::vector<Toolkit::TextAnchor>& anchorActors);
+
+ /**
+ * @brief Return an index of first anchor in the anchor vector whose boundaries includes given character offset
+ *
+ * @param[in] characterOffset A position in text coords.
+ *
+ * @return the 0-based index in anchor vector (-1 if an anchor not found)
+ */
+ int32_t GetAnchorIndex(size_t characterOffset) const;
+
+ /**
+ * @brief Return the geometrical position of an anchor relative to the parent origin point.
+ *
+ * @param[in] anchor An anchor.
+ *
+ * @return The x, y, z coordinates of an anchor.
+ */
+ Vector3 GetAnchorPosition(Anchor anchor) const;
+
+ /**
+ * @brief Return the size of an anchor expresed as a vector containing anchor's width and height.
+ *
+ * @param[in] anchor An anchor.
*
- * @return the created actor or an empty handle if no background color needs to be rendered.
+ * @return The width and height of an anchor.
*/
- Actor CreateBackgroundActor();
+ Vector2 GetAnchorSize(Anchor anchor) const;
+
+ /**
+ * @brief Return the actor representing an anchor.
+ *
+ * @param[in] anchor An anchor.
+ *
+ * @return The actor representing an anchor.
+ */
+ Toolkit::TextAnchor CreateAnchorActor(Anchor anchor);
public:
/**
float mTextFitMinSize; ///< Minimum Font Size for text fit. Default 10
float mTextFitMaxSize; ///< Maximum Font Size for text fit. Default 100
float mTextFitStepSize; ///< Step Size for font intervalse. Default 1
- bool mTextFitEnabled : 1; ///< Whether the text's fit is enabled.
float mFontSizeScale; ///< Scale value for Font Size. Default 1.0
+ bool mTextFitEnabled : 1; ///< Whether the text's fit is enabled.
+ bool mTextFitChanged : 1; ///< Whether the text fit property has changed.
bool mIsLayoutDirectionChanged : 1; ///< Whether the layout has changed.
private:
--- /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
if(controller.mImpl->IsShowingPlaceholderText() ||
(0u == controller.mImpl->mModel->mLogicalModel->mText.Count()))
{
- controller.ShowPlaceholderText();
+ ShowPlaceholderText(*controller.mImpl);
}
}
if(controller.mImpl->IsShowingPlaceholderText() ||
(0u == controller.mImpl->mModel->mLogicalModel->mText.Count()))
{
- controller.ShowPlaceholderText();
+ ShowPlaceholderText(*controller.mImpl);
}
}
}
}
}
-void Controller::PlaceholderHandler::ShowPlaceholderText(Controller& controller)
+void Controller::PlaceholderHandler::ShowPlaceholderText(Controller::Impl& impl)
{
- Controller::Impl& impl = *controller.mImpl;
-
if(impl.IsPlaceholderAvailable())
{
EventData*& eventData = impl.mEventData;
static const Vector4& GetPlaceholderTextColor(const Controller& controller);
static void SetPlaceholderProperty(Controller& controller, const Property::Map& map);
static void GetPlaceholderProperty(Controller& controller, Property::Map& map);
- static void ShowPlaceholderText(Controller& controller);
+ static void ShowPlaceholderText(Controller::Impl& impl);
static void CreatePlaceholderFont(Controller& controller);
};
// INTERNAL INCLUDES
#include <dali-toolkit/internal/text/layouts/layout-parameters.h>
+#include <dali-toolkit/internal/text/text-controller-event-handler.h>
#include <dali-toolkit/internal/text/text-controller-impl.h>
namespace
// Store the actual control's size to restore later.
const Size actualControlSize = visualModel->mControlSize;
- DoRelayout(controller,
+ DoRelayout(impl,
requestedControllerSize,
static_cast<OperationsMask>(onlyOnceOperations |
requestedOperationsMask),
Vector3 naturalSizeVec3;
// Make sure the model is up-to-date before layouting
- controller.ProcessModifyEvents();
+ EventHandler::ProcessModifyEvents(controller);
Controller::Impl& impl = *controller.mImpl;
ModelPtr& model = impl.mModel;
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 |
// Make sure the model is up-to-date before layouting
impl.UpdateModel(onlyOnceOperations);
- DoRelayout(controller,
+ DoRelayout(impl,
Size(layoutSize.width, MAX_FLOAT),
static_cast<OperationsMask>(onlyOnceOperations | LAYOUT),
textSize);
float minPointSize = impl.mTextFitMinSize;
float maxPointSize = impl.mTextFitMaxSize;
float pointInterval = impl.mTextFitStepSize;
+ float currentFitPointSize = impl.mFontDefaults->mFitPointSize;
model->mElideEnabled = false;
Vector<float> pointSizeArray;
}
model->mElideEnabled = actualellipsis;
+ if(currentFitPointSize != pointSizeArray[bestSizeIndex])
+ {
+ impl.mTextFitChanged = true;
+ }
impl.mFontDefaults->mFitPointSize = pointSizeArray[bestSizeIndex];
impl.mFontDefaults->sizeDefined = true;
- controller.ClearFontData();
+ impl.ClearFontData();
}
}
DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::GetHeightForWidth %p width %f\n", &controller, width);
// Make sure the model is up-to-date before layouting
- controller.ProcessModifyEvents();
+ EventHandler::ProcessModifyEvents(controller);
Controller::Impl& impl = *controller.mImpl;
ModelPtr& model = impl.mModel;
}
// Make sure the model is up-to-date before layouting.
- controller.ProcessModifyEvents();
+ EventHandler::ProcessModifyEvents(controller);
bool updated = impl.UpdateModel(operationsPending);
// Layout the text.
Size layoutSize;
- updated = DoRelayout(controller, size, operationsPending, layoutSize) || updated;
+ updated = DoRelayout(impl, size, operationsPending, layoutSize) || updated;
if(updated)
{
if(!isEditable || !controller.IsMultiLineEnabled())
{
// After doing the text layout, the vertical offset to place the actor in the desired position can be calculated.
- controller.CalculateVerticalOffset(size);
+ CalculateVerticalOffset(controller, size);
}
if(isEditable)
return updateTextType;
}
-bool Controller::Relayouter::DoRelayout(Controller& controller, const Size& size, OperationsMask operationsRequired, Size& layoutSize)
+bool Controller::Relayouter::DoRelayout(Controller::Impl& impl, const Size& size, OperationsMask operationsRequired, Size& layoutSize)
{
- DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::DoRelayout %p size %f,%f\n", &controller, size.width, size.height);
+ DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::Relayouter::DoRelayout %p size %f,%f\n", &impl, size.width, size.height);
bool viewUpdated(false);
- Controller::Impl& impl = *controller.mImpl;
-
// Calculate the operations to be done.
const OperationsMask operations = static_cast<OperationsMask>(impl.mOperationsPending & operationsRequired);
(lastIndex > charactersToGlyph.Count() && charactersToGlyph.Count() > 0u))
{
std::string currentText;
- controller.GetText(currentText);
+ impl.GetText(currentText);
DALI_LOG_ERROR("Controller::DoRelayout: Attempting to access invalid buffer\n");
DALI_LOG_ERROR("Current text is: %s\n", currentText.c_str());
// Reset the scroll position in inactive state
if(elideTextEnabled && (impl.mEventData->mState == EventData::INACTIVE))
{
- controller.ResetScrollPosition();
+ impl.ResetScrollPosition();
}
}
}
#if defined(DEBUG_ENABLED)
std::string currentText;
- controller.GetText(currentText);
- DALI_LOG_INFO(gLogFilter, Debug::Concise, "Controller::DoRelayout [%p] mImpl->mIsTextDirectionRTL[%s] [%s]\n", &controller, (impl.mIsTextDirectionRTL) ? "true" : "false", currentText.c_str());
+ impl.GetText(currentText);
+ DALI_LOG_INFO(gLogFilter, Debug::Concise, "Controller::Relayouter::DoRelayout [%p] mImpl->mIsTextDirectionRTL[%s] [%s]\n", &impl, (impl.mIsTextDirectionRTL) ? "true" : "false", currentText.c_str());
#endif
- DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", (viewUpdated ? "true" : "false"));
+ DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::Relayouter::DoRelayout, view updated %s\n", (viewUpdated ? "true" : "false"));
return viewUpdated;
}
/**
* @brief Called by the Controller to do certain operations when relayouting.
*
- * @param[in] controller A reference to the controller class
+ * @param[in] impl A reference to the controller impl class
* @param[in] size The size to set
* @param[in] operationsRequired The operations we need to do
* @param[in/out] layoutSize The Layout size which can be updated depending on the result of the performed operations
* @return
*/
- static bool DoRelayout(Controller& controller, const Size& size, OperationsMask operationsRequired, Size& layoutSize);
+
+ static bool DoRelayout(Controller::Impl& impl, const Size& size, OperationsMask operationsRequired, Size& layoutSize);
/**
* @brief Called by the Controller to calculate the veritcal offset give the control size.
#include <dali-toolkit/internal/text/character-set-conversion.h>
#include <dali-toolkit/internal/text/markup-processor.h>
#include <dali-toolkit/internal/text/text-controller-impl.h>
+#include <dali-toolkit/internal/text/text-controller-placeholder-handler.h>
#include <dali-toolkit/internal/text/text-editable-control-interface.h>
namespace
ResetText(controller);
// Remove the style.
- controller.ClearStyleData();
+ impl.ClearStyleData();
CharacterIndex lastCursorIndex = 0u;
}
else
{
- controller.ShowPlaceholderText();
+ PlaceholderHandler::ShowPlaceholderText(impl);
}
unsigned int oldCursorPos = (nullptr != eventData ? eventData->mPrimaryCursorPosition : 0);
controller.ResetCursorPosition(lastCursorIndex);
// Scrolls the text to make the cursor visible.
- controller.ResetScrollPosition();
+ impl.ResetScrollPosition();
impl.RequestRelayout();
impl.IsPlaceholderAvailable())
{
// Show place-holder if empty after removing the pre-edit text
- controller.ShowPlaceholderText();
+ PlaceholderHandler::ShowPlaceholderText(impl);
eventData->mUpdateCursorPosition = true;
impl.ClearPreEditFlag();
}
/// @param[in] controller The controller
static void SetText(Controller& controller, const std::string& text);
- /// @copydoc Text::Contoller::InsertText
- /// @param[in] controller The controller
+ /**
+ * @brief Called by editable UI controls when key events are received.
+ *
+ * @param[in] controller The controller
+ * @param[in] text The text to insert.
+ * @param[in] type Used to distinguish between regular key events and InputMethodContext events.
+ */
static void InsertText(Controller& controller, const std::string& text, Controller::InsertType type);
- /// @copydoc Text::Contoller::PasteText
+ /// @copydoc Text::EditableControlInterface::PasteText()
/// @param[in] controller The controller
static void PasteText(Controller& controller, const std::string& stringToPaste);
- /// @copydoc Text::Contoller::RemoveText
- /// @param[in] controller The controller
+ /**
+ * @brief Remove a given number of characters
+ *
+ * When predictve text is used the pre-edit text is removed and inserted again with the new characters.
+ * The UpdateInputStyleType @type parameter if set to DONT_UPDATE_INPUT_STYLE avoids to update the input
+ * style when pre-edit text is removed.
+ *
+ * @param[in] controller The controller
+ * @param[in] cursorOffset Start position from the current cursor position to start deleting characters.
+ * @param[in] numberOfCharacters The number of characters to delete from the cursorOffset.
+ * @param[in] type Whether to update the input style.
+ * @return True if the remove was successful.
+ */
static bool RemoveText(Controller& controller, int cursorOffset, int numberOfCharacters, UpdateInputStyleType type);
- /// @copydoc Text::Contoller::RemoveSelectedText
- /// @param[in] controller The controller
+ /**
+ * @brief Checks if text is selected and if so removes it.
+ * @param[in] controller The controller
+ * @return true if text was removed
+ */
static bool RemoveSelectedText(Controller& controller);
- /// @copydoc Text::Contoller::ResetText
- /// @param[in] controller The controller
+ /**
+ * @brief Used to remove the text included the placeholder text.
+ * @param[in] controller The controller
+ */
static void ResetText(Controller& controller);
- /// @copydoc Text::Contoller::InsertTextAnchor
- /// @param[in] controller The controller
+ /**
+ * @brief Update anchor position from given number of inserted characters.
+ *
+ * @param[in] controller The controller
+ * @param[in] numberOfCharacters The number of inserted characters.
+ * @param[in] previousCursorIndex A cursor position before event occurs.
+ */
static void InsertTextAnchor(Controller& controller, int numberOfCharacters, CharacterIndex previousCursorIndex);
- /// @copydoc Text::Contoller::RemoveTextAnchor
- /// @param[in] controller The controller
+ /**
+ * @brief Update anchor position from given number of removed characters.
+ *
+ * @param[in] controller The controller
+ * @param[in] cursorOffset Start position from the current cursor position to start deleting characters.
+ * @param[in] numberOfCharacters The number of removed characters.
+ * @param[in] previousCursorIndex A cursor position before event occurs.
+ */
static void RemoveTextAnchor(Controller& controller, int cursorOffset, int numberOfCharacters, CharacterIndex previousCursorIndex);
};
// INTERNAL INCLUDES
#include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
+#include <dali-toolkit/internal/text/text-controller-background-actor.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-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>
Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
#endif
-constexpr float MAX_FLOAT = std::numeric_limits<float>::max();
-
const std::string EMPTY_STRING("");
template<typename Type>
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
Dali::LayoutDirection::Type Controller::GetLayoutDirection(Dali::Actor& actor) const
{
- if(mImpl->mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::LOCALE ||
- (mImpl->mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::INHERIT && !mImpl->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>());
- }
+ return mImpl->GetLayoutDirection(actor);
}
bool Controller::IsShowingRealText() const
void Controller::SetLineWrapMode(Text::LineWrap::Mode lineWrapMode)
{
- if(lineWrapMode != mImpl->mModel->mLineWrapMode)
- {
- // Update Text layout for applying wrap mode
- mImpl->mOperationsPending = static_cast<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
return mImpl->mTextFitEnabled;
}
+void Controller::SetTextFitChanged(bool changed)
+{
+ mImpl->mTextFitChanged = changed;
+}
+
+bool Controller::IsTextFitChanged() const
+{
+ return mImpl->mTextFitChanged;
+}
+
void Controller::SetTextFitMinSize(float minSize, FontSizeType type)
{
mImpl->mTextFitMinSize = (type == POINT_SIZE) ? minSize : ConvertPixelToPoint(minSize);
return mImpl->mTextFitContentSize;
}
+float Controller::GetTextFitPointSize() const
+{
+ return mImpl->mFontDefaults ? mImpl->mFontDefaults->mFitPointSize : 0.0f;
+}
+
void Controller::SetPlaceholderTextElideEnabled(bool enabled)
{
PlaceholderHandler::SetPlaceholderTextElideEnabled(*this, enabled);
void Controller::GetText(std::string& text) const
{
- if(!mImpl->IsShowingPlaceholderText())
- {
- // Retrieves the text string.
- mImpl->GetText(0u, text);
- }
- else
- {
- DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::GetText %p empty (but showing placeholder)\n", this);
- }
+ mImpl->GetText(text);
}
void Controller::SetPlaceholderText(PlaceholderType type, const std::string& text)
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)
Toolkit::DevelText::TextDirection::Type Controller::GetTextDirection()
{
// Make sure the model is up-to-date before layouting
- ProcessModifyEvents();
-
- if(mImpl->mUpdateTextDirection)
- {
- // Operations that can be done only once until the text changes.
- const OperationsMask onlyOnceOperations = static_cast<OperationsMask>(CONVERT_TO_UTF32 |
- GET_SCRIPTS |
- VALIDATE_FONTS |
- GET_LINE_BREAKS |
- BIDI_INFO |
- SHAPE_TEXT |
- GET_GLYPH_METRICS);
-
- // Set the update info to relayout the whole text.
- mImpl->mTextUpdateInfo.mParagraphCharacterIndex = 0u;
- mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters = mImpl->mModel->mLogicalModel->mText.Count();
-
- // Make sure the model is up-to-date before layouting
- mImpl->UpdateModel(onlyOnceOperations);
-
- Vector3 naturalSize;
- DoRelayout(Size(MAX_FLOAT, MAX_FLOAT),
- static_cast<OperationsMask>(onlyOnceOperations |
- LAYOUT | REORDER | UPDATE_DIRECTION),
- naturalSize.GetVectorXY());
-
- // Do not do again the only once operations.
- mImpl->mOperationsPending = static_cast<OperationsMask>(mImpl->mOperationsPending & ~onlyOnceOperations);
-
- // Clear the update info. This info will be set the next time the text is updated.
- mImpl->mTextUpdateInfo.Clear();
-
- // FullRelayoutNeeded should be true because DoRelayout is MAX_FLOAT, MAX_FLOAT.
- mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true;
-
- mImpl->mUpdateTextDirection = false;
- }
+ EventHandler::ProcessModifyEvents(*this);
- return mImpl->mIsTextDirectionRTL ? Toolkit::DevelText::TextDirection::RIGHT_TO_LEFT : Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT;
+ return mImpl->GetTextDirection();
}
Toolkit::DevelText::VerticalLineAlignment::Type Controller::GetVerticalLineAlignment() const
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()
EventHandler::SelectEvent(*this, x, y, selectType);
}
-void Controller::SelectEvent(const uint32_t start, const uint32_t end, SelectionType selectType)
-{
- EventHandler::SelectEvent(*this, start, end, selectType);
-}
-
void Controller::SetTextSelectionRange(const uint32_t* start, const uint32_t* end)
{
if(mImpl->mEventData)
mImpl->mEventData->mIsRightHandleSelected = true;
mImpl->SetTextSelectionRange(start, end);
mImpl->RequestRelayout();
- KeyboardFocusGainEvent();
+ EventHandler::KeyboardFocusGainEvent(*this);
}
}
mImpl->mEventData->mCheckScrollAmount = true;
if(mImpl->SetPrimaryCursorPosition(index, focused) && focused)
{
- KeyboardFocusGainEvent();
+ EventHandler::KeyboardFocusGainEvent(*this);
return true;
}
}
void Controller::SelectWholeText()
{
- SelectEvent(0.f, 0.f, SelectionType::ALL);
+ EventHandler::SelectEvent(*this, 0.f, 0.f, SelectionType::ALL);
}
void Controller::SelectNone()
{
- SelectEvent(0.f, 0.f, SelectionType::NONE);
+ EventHandler::SelectEvent(*this, 0.f, 0.f, SelectionType::NONE);
}
void Controller::SelectText(const uint32_t start, const uint32_t end)
{
- SelectEvent(start, end, SelectionType::RANGE);
+ EventHandler::SelectEvent(*this, start, end, SelectionType::RANGE);
}
string Controller::GetSelectedText() const
{
- string text;
- if(EventData::SELECTING == mImpl->mEventData->mState)
- {
- mImpl->RetrieveSelection(text, false);
- }
- return text;
+ return mImpl->GetSelectedText();
}
string Controller::CopyText()
{
- string text;
- mImpl->RetrieveSelection(text, false);
- mImpl->SendSelectionToClipboard(false); // Text not modified
-
- mImpl->mEventData->mUpdateCursorPosition = true;
-
- mImpl->RequestRelayout(); // Cursor, Handles, Selection Highlight, Popup
-
- return text;
+ return mImpl->CopyText();
}
string Controller::CutText()
{
- string text;
- mImpl->RetrieveSelection(text, false);
-
- if(!IsEditable())
- {
- return EMPTY_STRING;
- }
-
- mImpl->SendSelectionToClipboard(true); // Synchronous call to modify text
- mImpl->mOperationsPending = ALL_OPERATIONS;
-
- if((0u != mImpl->mModel->mLogicalModel->mText.Count()) ||
- !mImpl->IsPlaceholderAvailable())
- {
- mImpl->QueueModifyEvent(ModifyEvent::TEXT_DELETED);
- }
- else
- {
- ShowPlaceholderText();
- }
-
- mImpl->mEventData->mUpdateCursorPosition = true;
- mImpl->mEventData->mScrollAfterDelete = true;
-
- mImpl->RequestRelayout();
-
- if(nullptr != mImpl->mEditableControlInterface)
- {
- mImpl->mEditableControlInterface->TextChanged(true);
- }
- return text;
+ return mImpl->CutText();
}
void Controller::PasteText()
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)
mImpl->RequestRelayout();
}
-void Controller::InsertText(const std::string& text, Controller::InsertType type)
-{
- TextUpdater::InsertText(*this, text, type);
-}
-
-void Controller::PasteText(const std::string& stringToPaste)
-{
- TextUpdater::PasteText(*this, stringToPaste);
-}
-
-bool Controller::RemoveText(int cursorOffset, int numberOfCharacters, UpdateInputStyleType type)
-{
- return TextUpdater::RemoveText(*this, cursorOffset, numberOfCharacters, type);
-}
-
-bool Controller::RemoveSelectedText()
-{
- return TextUpdater::RemoveSelectedText(*this);
-}
-
-void Controller::InsertTextAnchor(int numberOfCharacters, CharacterIndex previousCursorIndex)
-{
- TextUpdater::InsertTextAnchor(*this, numberOfCharacters, previousCursorIndex);
-}
-
-void Controller::RemoveTextAnchor(int cursorOffset, int numberOfCharacters, CharacterIndex previousCursorIndex)
-{
- TextUpdater::RemoveTextAnchor(*this, cursorOffset, numberOfCharacters, previousCursorIndex);
-}
-
-bool Controller::DoRelayout(const Size& size, OperationsMask operationsRequired, Size& layoutSize)
-{
- return Relayouter::DoRelayout(*this, size, operationsRequired, layoutSize);
-}
-
-void Controller::CalculateVerticalOffset(const Size& controlSize)
-{
- Relayouter::CalculateVerticalOffset(*this, controlSize);
-}
-
-void Controller::ProcessModifyEvents()
-{
- EventHandler::ProcessModifyEvents(*this);
-}
-
-void Controller::TextReplacedEvent()
-{
- EventHandler::TextReplacedEvent(*this);
-}
-
-void Controller::TextInsertedEvent()
-{
- EventHandler::TextInsertedEvent(*this);
-}
-
-void Controller::TextDeletedEvent()
-{
- EventHandler::TextDeletedEvent(*this);
-}
-
-bool Controller::DeleteEvent(int keyCode)
-{
- return EventHandler::DeleteEvent(*this, keyCode);
-}
-
-// private : Helpers.
-
-void Controller::ResetText()
-{
- TextUpdater::ResetText(*this);
-}
-
-void Controller::ShowPlaceholderText()
-{
- PlaceholderHandler::ShowPlaceholderText(*this);
-}
-
-void Controller::ClearFontData()
-{
- if(mImpl->mFontDefaults)
- {
- mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
- }
-
- // Set flags to update the model.
- mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
- mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
- mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count();
-
- mImpl->mTextUpdateInfo.mClearAll = true;
- mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true;
- mImpl->mRecalculateNaturalSize = true;
-
- mImpl->mOperationsPending = static_cast<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)
Actor Controller::CreateBackgroundActor()
{
- return mImpl->CreateBackgroundActor();
+ return CreateControllerBackgroundActor(mImpl->mView, mImpl->mModel->mVisualModel, mImpl->mShaderBackground);
+}
+
+void Controller::GetAnchorActors(std::vector<Toolkit::TextAnchor>& anchorActors)
+{
+ mImpl->GetAnchorActors(anchorActors);
+}
+
+int Controller::GetAnchorIndex(size_t characterOffset)
+{
+ return mImpl->GetAnchorIndex(characterOffset);
}
Controller::Controller(ControlInterface* controlInterface,
// INTERNAL INCLUDES
#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/controls/text-controls/text-anchor-devel.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>
Vector2 GetTextFitContentSize() const;
/**
+ * @brief Retrieve the fited point size.
+ *
+ * @return The fited point size.
+ */
+ float GetTextFitPointSize() const;
+
+ /**
+ * @brief Sets whether the text fit properties have changed.
+ *
+ * @param[in] changed Whether to changed the text fit.
+ */
+ void SetTextFitChanged(bool changed);
+
+ /**
+ * @brief Whether the text fit properties are changed or not.
+ *
+ * @return True if the text fit properties are changed
+ */
+ bool IsTextFitChanged() const;
+
+ /**
* @brief Enable or disable the placeholder text elide.
* @param enabled Whether to enable the placeholder text elide.
*/
void SelectEvent(float x, float y, SelectionType selection);
/**
- * @brief Creates a selection event with a selection index.
- *
- * It could be called from the SelectText().
- * The start and end parameters are passed through the event.
- *
- * @param[in] start The start selection position.
- * @param[in] end The end selection position.
- * @param[in] selection type like the range.
- */
- void SelectEvent(const uint32_t start, const uint32_t end, SelectionType selection);
-
- /**
* @copydoc Text::SelectableControlInterface::SetTextSelectionRange()
*/
void SetTextSelectionRange(const uint32_t* start, const uint32_t* end);
*/
CharacterIndex GetCursorPosition();
+ /**
+ * @brief Resets a provided vector with actors that marks the position of anchors in markup enabled text
+ *
+ * @param[out] anchorActors the vector of actor (empty collection if no anchors available).
+ */
+ void GetAnchorActors(std::vector<Toolkit::TextAnchor>& anchorActors);
+
+ /**
+ * @brief Return an index of first anchor in the anchor vector whose boundaries includes given character offset
+ *
+ * @param[in] characterOffset A position in text coords.
+ *
+ * @return the index in anchor vector (-1 if an anchor not found)
+ */
+ int GetAnchorIndex(size_t characterOffset);
+
protected: // Inherit from Text::Decorator::ControllerInterface.
/**
* @copydoc Dali::Toolkit::Text::Decorator::ControllerInterface::GetTargetSize()
*/
void DisplayTimeExpired() override;
-private: // Update.
- /**
- * @brief Called by editable UI controls when key events are received.
- *
- * @param[in] text The text to insert.
- * @param[in] type Used to distinguish between regular key events and InputMethodContext events.
- */
- void InsertText(const std::string& text, InsertType type);
-
- /**
- * @copydoc Text::EditableControlInterface::PasteText()
- */
- void PasteText(const std::string& stringToPaste);
-
- /**
- * @brief Remove a given number of characters
- *
- * When predictve text is used the pre-edit text is removed and inserted again with the new characters.
- * The UpdateInputStyleType @type parameter if set to DONT_UPDATE_INPUT_STYLE avoids to update the input
- * style when pre-edit text is removed.
- *
- * @param[in] cursorOffset Start position from the current cursor position to start deleting characters.
- * @param[in] numberOfCharacters The number of characters to delete from the cursorOffset.
- * @param[in] type Whether to update the input style.
- * @return True if the remove was successful.
- */
- bool RemoveText(int cursorOffset,
- int numberOfCharacters,
- UpdateInputStyleType type);
-
- /**
- * @brief Checks if text is selected and if so removes it.
- * @return true if text was removed
- */
- bool RemoveSelectedText();
-
- /**
- * @brief Update anchor position from given number of inserted characters.
- *
- * @param[in] numberOfCharacters The number of inserted characters.
- * @param[in] previousCursorIndex A cursor position before event occurs.
- */
- void InsertTextAnchor(int numberOfCharacters,
- CharacterIndex previousCursorIndex);
-
- /**
- * @brief Update anchor position from given number of removed characters.
- *
- * @param[in] cursorOffset Start position from the current cursor position to start deleting characters.
- * @param[in] numberOfCharacters The number of removed characters.
- * @param[in] previousCursorIndex A cursor position before event occurs.
- */
- void RemoveTextAnchor(int cursorOffset,
- int numberOfCharacters,
- CharacterIndex previousCursorIndex);
-
-private: // Relayout.
- /**
- * @brief Lays-out the text.
- *
- * GetNaturalSize(), GetHeightForWidth() and Relayout() calls this method.
- *
- * @param[in] size A the size of a bounding box to layout text within.
- * @param[in] operations The layout operations which need to be done.
- * @param[out] layoutSize The size of the laid-out text.
- */
- bool DoRelayout(const Size& size,
- OperationsMask operations,
- Size& layoutSize);
-
- /**
- * @brief Calulates the vertical offset to align the text inside the bounding box.
- *
- * @param[in] size The size of the bounding box.
- */
- void CalculateVerticalOffset(const Size& size);
-
-private: // Events.
- /**
- * @brief Process queued events which modify the model.
- */
- void ProcessModifyEvents();
-
- /**
- * @brief Used to process an event queued from SetText()
- */
- void TextReplacedEvent();
-
- /**
- * @brief Used to process an event queued from key events etc.
- */
- void TextInsertedEvent();
-
- /**
- * @brief Used to process an event queued from backspace key etc.
- */
- void TextDeletedEvent();
-
- /**
- * @brief Helper to KeyEvent() to handle the backspace or delete key case.
- *
- * @param[in] keyCode The keycode for the key pressed
- * @return True if a character was deleted.
- */
- bool DeleteEvent(int keyCode);
-
-private: // Helpers.
- /**
- * @brief Used to remove the text included the placeholder text.
- */
- void ResetText();
-
- /**
- * @brief Helper to show the place holder text..
- */
- 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,
}
break;
}
- case Toolkit::DevelControl::Property::ACCESSIBILITY_ROLE:
- {
- if(Dali::Accessibility::IsUp())
- {
- Dali::Accessibility::Accessible::Get(Self())->Emit(Dali::Accessibility::ObjectPropertyChangeEvent::ROLE);
- }
- break;
- }
}
}
namespace Toolkit
{
const unsigned int TOOLKIT_MAJOR_VERSION = 2;
-const unsigned int TOOLKIT_MINOR_VERSION = 0;
-const unsigned int TOOLKIT_MICRO_VERSION = 53;
+const unsigned int TOOLKIT_MINOR_VERSION = 1;
+const unsigned int TOOLKIT_MICRO_VERSION = 0;
const char* const TOOLKIT_BUILD_DATE = __DATE__ " " __TIME__;
#ifdef DEBUG_ENABLED
Name: dali2-toolkit
Summary: Dali 3D engine Toolkit
-Version: 2.0.53
+Version: 2.1.0
Release: 1
Group: System/Libraries
License: Apache-2.0 and BSD-3-Clause and MIT