Fix text clustering issue 12/319612/4
authorBowon Ryu <bowon.ryu@samsung.com>
Thu, 13 Feb 2025 11:48:08 +0000 (20:48 +0900)
committerBowon Ryu <bowon.ryu@samsung.com>
Fri, 14 Feb 2025 04:40:36 +0000 (13:40 +0900)
Prevent ligature breaks in unicode combinations containing combining diacritical marks.

Change-Id: I26618ba35b955b4434f87dff5c8b3b28e7a4ee1e
Signed-off-by: Bowon Ryu <bowon.ryu@samsung.com>
automated-tests/src/dali-toolkit/utc-Dali-TextEditor.cpp
dali-toolkit/internal/text/controller/text-controller-impl.cpp
dali-toolkit/internal/text/controller/text-controller-text-updater.cpp
dali-toolkit/internal/text/cursor-helper-functions.cpp

index b4ca2efe13a5e36d96b658321fc6c6581d7fba93..d83c506e90acec768f2ded426559a3d45eb2d872 100644 (file)
@@ -2733,6 +2733,37 @@ int utcDaliTextEditorEvent06(void)
 
   DALI_TEST_EQUALS("Hello\nworld\nHello world", editor.GetProperty<std::string>(TextEditor::Property::TEXT), TEST_LOCATION);
 
+  editor.SetProperty(TextEditor::Property::TEXT, "ảffiff");
+  editor.SetProperty(DevelTextEditor::Property::PRIMARY_CURSOR_POSITION, 0);
+  application.SendNotification();
+  application.Render();
+
+  application.ProcessEvent(GenerateKey("", "", "", DALI_KEY_CURSOR_RIGHT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+  application.ProcessEvent(GenerateKey("", "", "", DALI_KEY_CURSOR_RIGHT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+  application.ProcessEvent(GenerateKey("", "", "", DALI_KEY_CURSOR_RIGHT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+  application.ProcessEvent(GenerateKey("", "", "", DALI_KEY_CURSOR_RIGHT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+  application.ProcessEvent(GenerateKey("", "", "", DALI_KEY_CURSOR_RIGHT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+  application.ProcessEvent(GenerateKey("", "", "", DALI_KEY_CURSOR_RIGHT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(editor.GetProperty(DevelTextEditor::Property::PRIMARY_CURSOR_POSITION).Get<int>(), 7, TEST_LOCATION);
+
+  application.ProcessEvent(GenerateKey("", "", "", DALI_KEY_CURSOR_LEFT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+  application.ProcessEvent(GenerateKey("", "", "", DALI_KEY_CURSOR_LEFT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+  application.ProcessEvent(GenerateKey("", "", "", DALI_KEY_CURSOR_LEFT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+  application.ProcessEvent(GenerateKey("", "", "", DALI_KEY_CURSOR_LEFT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+  application.ProcessEvent(GenerateKey("", "", "", DALI_KEY_CURSOR_LEFT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+  application.ProcessEvent(GenerateKey("", "", "", DALI_KEY_CURSOR_LEFT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(editor.GetProperty(DevelTextEditor::Property::PRIMARY_CURSOR_POSITION).Get<int>(), 0, TEST_LOCATION);
+
+  application.ProcessEvent(GenerateKey("", "", "", Dali::DevelKey::DALI_KEY_DELETE, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS("ffiff", editor.GetProperty<std::string>(TextEditor::Property::TEXT), TEST_LOCATION);
+
+
   // For coverage
   editor.SetProperty(TextEditor::Property::TEXT, "الاخيرالسطر1\nالاخيرالسطر2\nالاخيرالسطر3\nالاخيرالسطر4");
   application.SendNotification();
index 5e7a5b3d9fca347b09c7ebad865fa40b079b04c4..2ddb46267b5a2e5365de2b5289ad4d56be68ac4d 100644 (file)
@@ -1376,8 +1376,26 @@ CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) c
     const Script script = mModel->mLogicalModel->GetScript(index);
     if(HasLigatureMustBreak(script))
     {
-      // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ, ...
-      numberOfCharacters = 1u;
+      if(numberOfCharacters == 2u)
+      {
+        const Character* const textBuffer = mModel->mLogicalModel->mText.Begin();
+        Character              character  = *(textBuffer + index);
+
+        CharacterIndex nextIndex          = index + 1u;
+        bool           isCurrentCombining = TextAbstraction::IsCombiningDiacriticalMarks(character);
+        bool           isNextValid        = nextIndex < mModel->mLogicalModel->mText.Count();
+        bool           isNextCombining    = isNextValid && TextAbstraction::IsCombiningDiacriticalMarks(*(textBuffer + nextIndex));
+
+        if(!isCurrentCombining && !isNextCombining)
+        {
+          numberOfCharacters = 1u;
+        }
+      }
+      else
+      {
+        // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ, ...
+        numberOfCharacters = 1u;
+      }
     }
   }
   else
index 5a906a5e1c58ac8f1044d9ca47e4c7c44723dd21..0cb702a6ca95d2b5fe8f7ee13cbc4dede6e1f6a3 100644 (file)
@@ -553,6 +553,16 @@ bool Controller::TextUpdater::RemoveText(
       numberOfCharacters = actualNumberOfCharacters;
     }
 
+    if(HasLigatureMustBreak(script) && cursorOffset == 0) // delete key.
+    {
+      GlyphIndex glyphIndex               = *(visualModel->mCharactersToGlyph.Begin() + cursorIndex);
+      Length     actualNumberOfCharacters = *(visualModel->mCharactersPerGlyph.Begin() + glyphIndex);
+      if(actualNumberOfCharacters == 2u && TextAbstraction::IsCombiningDiacriticalMarks(*(currentText.Begin() + cursorIndex + 1u)))
+      {
+        numberOfCharacters = 2u;
+      }
+    }
+
     if((cursorIndex + numberOfCharacters) > currentText.Count())
     {
       numberOfCharacters = currentText.Count() - cursorIndex;
index 5dfebde9e65cce8c33dd94c7630dce6e4cceb057..38354d150453e4b995ee13b4e22a23160077cc02 100644 (file)
@@ -359,9 +359,17 @@ CharacterIndex GetClosestCursorIndex(VisualModelPtr         visualModel,
       }
 
       // Get the script of the character.
-      const Script script = logicalModel->GetScript(characterLogicalOrderIndex);
+      const Script script                         = logicalModel->GetScript(characterLogicalOrderIndex);
+      const bool   hasLigatureMustBreak           = HasLigatureMustBreak(script);
+      bool         isCombiningDiacriticalSequence = false;
 
-      const bool   isInterglyphIndex = (numberOfCharacters > numberOfGlyphs) && HasLigatureMustBreak(script);
+      if(hasLigatureMustBreak && numberOfCharacters == 2u)
+      {
+        // Second character is combining diacritical mark.
+        isCombiningDiacriticalSequence = TextAbstraction::IsCombiningDiacriticalMarks(*(logicalModel->mText.Begin() + characterLogicalOrderIndex)) ? true : false;
+      }
+
+      const bool   isInterglyphIndex = (numberOfCharacters > numberOfGlyphs) && (hasLigatureMustBreak && !isCombiningDiacriticalSequence);
       const Length numberOfBlocks    = isInterglyphIndex ? numberOfCharacters : 1u;
       const float  glyphAdvance      = glyphMetrics.advance / static_cast<float>(numberOfBlocks);