Adding run targeting to text value ranges!

Ability to target an individual run with the modifier range:
<img width="1577" alt="CleanShot 2023-07-24 at 16 25 12@2x" src="https://github.com/rive-app/rive/assets/454182/48bca39b-057c-4ed5-b04e-cada62c1f190">

Diffs=
215d11c86 Adding run targeting to text value ranges! (#5660)

Co-authored-by: Alex Gibson <agibson.uk@gmail.com>
Co-authored-by: Luigi Rosso <luigi-rosso@users.noreply.github.com>
This commit is contained in:
luigi-rosso
2023-07-25 22:26:38 +00:00
parent 810730f00c
commit 8dfb05c64d
5 changed files with 124 additions and 26 deletions

View File

@ -1 +1 @@
24aaadf9aeb0d6beb3faa1dec9f2d490d5d67afa
215d11c864cc550adbcfc9a47691cf9583fa7f25

View File

@ -1391,6 +1391,11 @@ class RiveCoreContext {
object.offset = value;
}
break;
case TextModifierRangeBase.runIdPropertyKey:
if (object is TextModifierRangeBase && value is int) {
object.runId = value;
}
break;
case TextStyleFeatureBase.tagPropertyKey:
if (object is TextStyleFeatureBase && value is int) {
object.tag = value;
@ -1656,6 +1661,7 @@ class RiveCoreContext {
case TextModifierRangeBase.unitsValuePropertyKey:
case TextModifierRangeBase.typeValuePropertyKey:
case TextModifierRangeBase.modeValuePropertyKey:
case TextModifierRangeBase.runIdPropertyKey:
case TextStyleFeatureBase.tagPropertyKey:
case TextStyleFeatureBase.featureValuePropertyKey:
case TextVariationModifierBase.axisTagPropertyKey:
@ -1996,6 +2002,8 @@ class RiveCoreContext {
return (object as TextModifierRangeBase).typeValue;
case TextModifierRangeBase.modeValuePropertyKey:
return (object as TextModifierRangeBase).modeValue;
case TextModifierRangeBase.runIdPropertyKey:
return (object as TextModifierRangeBase).runId;
case TextStyleFeatureBase.tagPropertyKey:
return (object as TextStyleFeatureBase).tag;
case TextStyleFeatureBase.featureValuePropertyKey:
@ -2753,6 +2761,11 @@ class RiveCoreContext {
object.modeValue = value;
}
break;
case TextModifierRangeBase.runIdPropertyKey:
if (object is TextModifierRangeBase) {
object.runId = value;
}
break;
case TextStyleFeatureBase.tagPropertyKey:
if (object is TextStyleFeatureBase) {
object.tag = value;

View File

@ -238,6 +238,30 @@ abstract class TextModifierRangeBase extends ContainerComponent {
void offsetChanged(double from, double to);
/// --------------------------------------------------------------------------
/// RunId field with key 378.
static const int runIdInitialValue = -1;
int _runId = runIdInitialValue;
static const int runIdPropertyKey = 378;
/// Identifier used to which run should be targeted.
int get runId => _runId;
/// Change the [_runId] field value.
/// [runIdChanged] will be invoked only if the field's value has changed.
set runId(int value) {
if (_runId == value) {
return;
}
int from = _runId;
_runId = value;
if (hasValidated) {
runIdChanged(from, value);
}
}
void runIdChanged(int from, int to);
@override
void copy(covariant TextModifierRangeBase source) {
super.copy(source);
@ -251,5 +275,6 @@ abstract class TextModifierRangeBase extends ContainerComponent {
_falloffFrom = source._falloffFrom;
_falloffTo = source._falloffTo;
_offset = source._offset;
_runId = source._runId;
}
}

View File

@ -1,13 +1,14 @@
import 'dart:math';
import 'dart:typed_data';
import 'package:collection/collection.dart';
import 'package:rive/src/core/core.dart';
import 'package:rive/src/generated/text/text_modifier_range_base.dart';
import 'package:rive/src/rive_core/animation/cubic_interpolator_component.dart';
import 'package:rive/src/rive_core/animation/interpolator.dart';
import 'package:rive/src/rive_core/component.dart';
import 'package:rive/src/rive_core/component_dirt.dart';
import 'package:rive/src/rive_core/text/text_modifier_group.dart';
import 'package:rive/src/rive_core/text/text_value_run.dart';
import 'package:rive_common/rive_text.dart';
export 'package:rive/src/generated/text/text_modifier_range_base.dart';
@ -110,25 +111,36 @@ class TextModifierRange extends TextModifierRangeBase {
if (_rangeMapper != null) {
return;
}
int start = 0;
int end = text.length;
if (run != null) {
start = run!.offset;
end = start + run!.text.length;
}
switch (units) {
case TextRangeUnits.charactersExcludingSpaces:
_rangeMapper = RangeMapper.fromCharacters(
text,
start,
end,
glyphLookup,
withoutSpaces: true,
);
break;
case TextRangeUnits.words:
_rangeMapper = RangeMapper.fromWords(text);
_rangeMapper = RangeMapper.fromWords(text, start, end);
break;
case TextRangeUnits.lines:
if (shape != null && lines != null) {
_rangeMapper = RangeMapper.fromLines(text, shape, lines, glyphLookup);
_rangeMapper = RangeMapper.fromLines(
text, start, end, shape, lines, glyphLookup);
}
break;
default:
_rangeMapper = RangeMapper.fromCharacters(
text,
start,
end,
glyphLookup,
);
break;
@ -247,6 +259,30 @@ class TextModifierRange extends TextModifierRangeBase {
@override
void strengthChanged(double from, double to) => modifierGroup?.rangeChanged();
@override
void runIdChanged(int from, int to) {
run = to == Core.missingId ? null : context.resolve(to);
modifierGroup?.rangeTypeChanged();
}
@override
void onAddedDirty() {
run = context.resolve(runId);
super.onAddedDirty();
}
TextValueRun? _run;
TextValueRun? get run => _run;
set run(TextValueRun? value) {
if (_run == value) {
return;
}
_run = value;
}
}
// See word indices and word lengths implementation above, we basically want the
@ -270,7 +306,7 @@ class RangeMapper {
unitLengths = Uint32List.fromList(lengths);
/// Build a RangeMapper from the words in [text].
static RangeMapper? fromWords(String text) {
static RangeMapper? fromWords(String text, int start, int end) {
if (text.isEmpty) {
return null;
}
@ -280,12 +316,21 @@ class RangeMapper {
int characterCount = 0;
int index = 0;
int indexFrom = 0;
for (final unit in text.codeUnits) {
if (wantWhiteSpace == isWhiteSpace(unit)) {
if (!wantWhiteSpace) {
indices.add(index);
indexFrom = index;
} else {
lengths.add(characterCount);
var indexTo = indexFrom + characterCount;
if (indexTo > start && end > indexFrom) {
var actualStart = max(start, indexFrom);
int selected = min(end, indexTo) - actualStart;
if (selected > 0) {
indices.add(actualStart);
lengths.add(selected);
}
}
characterCount = 0;
}
@ -296,23 +341,20 @@ class RangeMapper {
}
index++;
}
if (characterCount != 0) {
lengths.add(characterCount);
indices.add(index);
}
indices.add(end);
return RangeMapper(indices, lengths);
}
/// Build a RangeMapper from the words in [text].
static RangeMapper? fromCharacters(String text, GlyphLookup glyphLookup,
static RangeMapper? fromCharacters(
String text, int start, int end, GlyphLookup glyphLookup,
{bool withoutSpaces = false}) {
if (text.isEmpty) {
return null;
}
List<int> indices = [];
List<int> lengths = [];
for (int i = 0; i < text.length;) {
for (int i = start; i < end;) {
var unit = text.codeUnits[i];
if (withoutSpaces && isWhiteSpace(unit)) {
i++;
@ -325,12 +367,12 @@ class RangeMapper {
i += codePoints;
}
indices.add(text.length);
indices.add(end);
return RangeMapper(indices, lengths);
}
static RangeMapper? fromLines(String text, TextShapeResult shape,
BreakLinesResult lines, GlyphLookup glyphLookup) {
static RangeMapper? fromLines(String text, int start, int end,
TextShapeResult shape, BreakLinesResult lines, GlyphLookup glyphLookup) {
if (text.isEmpty) {
return null;
}
@ -342,23 +384,25 @@ class RangeMapper {
final paragraph = shape.paragraphs[paragraphIndex++];
var glyphRuns = paragraph.runs;
for (final line in paragraphLines) {
// if (line.endIndex == 0) {
// // Empty line.
// continue;
// }
var rf = glyphRuns[line.startRun];
var indexFrom = rf.textIndexAt(line.startIndex);
var rt = glyphRuns[line.endRun];
var endGlyphIndex = max(
0, line.endIndex - 1); //min(rt.glyphCount - 1, line.endIndex - 1);
var endGlyphIndex = max(0, line.endIndex - 1);
var indexTo = rt.textIndexAt(endGlyphIndex);
indexTo += glyphLookup.count(indexTo);
indices.add(indexFrom);
lengths.add(indexTo - indexFrom);
if (indexTo > start && end > indexFrom) {
var actualStart = max(start, indexFrom);
int selected = min(end, indexTo) - actualStart;
if (selected > 0) {
indices.add(actualStart);
lengths.add(selected);
}
}
}
}
indices.add(text.length);
indices.add(end);
return RangeMapper(indices, lengths);
}

View File

@ -21,6 +21,22 @@ class TextValueRun extends TextValueRunBase {
_style?.ref(this);
}
/// Returns the offset of this run within the text.
int get offset {
var text = textComponent;
if (text == null) {
return 0;
}
int value = 0;
for (final run in text.runs) {
if (run == this) {
break;
}
value += run.text.length;
}
return value;
}
void markShapeDirty() => textComponent?.markShapeDirty();
@override