mirror of
https://github.com/rive-app/rive-flutter.git
synced 2025-08-06 16:40:27 +08:00
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:
@ -1 +1 @@
|
|||||||
24aaadf9aeb0d6beb3faa1dec9f2d490d5d67afa
|
215d11c864cc550adbcfc9a47691cf9583fa7f25
|
||||||
|
@ -1391,6 +1391,11 @@ class RiveCoreContext {
|
|||||||
object.offset = value;
|
object.offset = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TextModifierRangeBase.runIdPropertyKey:
|
||||||
|
if (object is TextModifierRangeBase && value is int) {
|
||||||
|
object.runId = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case TextStyleFeatureBase.tagPropertyKey:
|
case TextStyleFeatureBase.tagPropertyKey:
|
||||||
if (object is TextStyleFeatureBase && value is int) {
|
if (object is TextStyleFeatureBase && value is int) {
|
||||||
object.tag = value;
|
object.tag = value;
|
||||||
@ -1656,6 +1661,7 @@ class RiveCoreContext {
|
|||||||
case TextModifierRangeBase.unitsValuePropertyKey:
|
case TextModifierRangeBase.unitsValuePropertyKey:
|
||||||
case TextModifierRangeBase.typeValuePropertyKey:
|
case TextModifierRangeBase.typeValuePropertyKey:
|
||||||
case TextModifierRangeBase.modeValuePropertyKey:
|
case TextModifierRangeBase.modeValuePropertyKey:
|
||||||
|
case TextModifierRangeBase.runIdPropertyKey:
|
||||||
case TextStyleFeatureBase.tagPropertyKey:
|
case TextStyleFeatureBase.tagPropertyKey:
|
||||||
case TextStyleFeatureBase.featureValuePropertyKey:
|
case TextStyleFeatureBase.featureValuePropertyKey:
|
||||||
case TextVariationModifierBase.axisTagPropertyKey:
|
case TextVariationModifierBase.axisTagPropertyKey:
|
||||||
@ -1996,6 +2002,8 @@ class RiveCoreContext {
|
|||||||
return (object as TextModifierRangeBase).typeValue;
|
return (object as TextModifierRangeBase).typeValue;
|
||||||
case TextModifierRangeBase.modeValuePropertyKey:
|
case TextModifierRangeBase.modeValuePropertyKey:
|
||||||
return (object as TextModifierRangeBase).modeValue;
|
return (object as TextModifierRangeBase).modeValue;
|
||||||
|
case TextModifierRangeBase.runIdPropertyKey:
|
||||||
|
return (object as TextModifierRangeBase).runId;
|
||||||
case TextStyleFeatureBase.tagPropertyKey:
|
case TextStyleFeatureBase.tagPropertyKey:
|
||||||
return (object as TextStyleFeatureBase).tag;
|
return (object as TextStyleFeatureBase).tag;
|
||||||
case TextStyleFeatureBase.featureValuePropertyKey:
|
case TextStyleFeatureBase.featureValuePropertyKey:
|
||||||
@ -2753,6 +2761,11 @@ class RiveCoreContext {
|
|||||||
object.modeValue = value;
|
object.modeValue = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TextModifierRangeBase.runIdPropertyKey:
|
||||||
|
if (object is TextModifierRangeBase) {
|
||||||
|
object.runId = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case TextStyleFeatureBase.tagPropertyKey:
|
case TextStyleFeatureBase.tagPropertyKey:
|
||||||
if (object is TextStyleFeatureBase) {
|
if (object is TextStyleFeatureBase) {
|
||||||
object.tag = value;
|
object.tag = value;
|
||||||
|
@ -238,6 +238,30 @@ abstract class TextModifierRangeBase extends ContainerComponent {
|
|||||||
|
|
||||||
void offsetChanged(double from, double to);
|
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
|
@override
|
||||||
void copy(covariant TextModifierRangeBase source) {
|
void copy(covariant TextModifierRangeBase source) {
|
||||||
super.copy(source);
|
super.copy(source);
|
||||||
@ -251,5 +275,6 @@ abstract class TextModifierRangeBase extends ContainerComponent {
|
|||||||
_falloffFrom = source._falloffFrom;
|
_falloffFrom = source._falloffFrom;
|
||||||
_falloffTo = source._falloffTo;
|
_falloffTo = source._falloffTo;
|
||||||
_offset = source._offset;
|
_offset = source._offset;
|
||||||
|
_runId = source._runId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
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/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/cubic_interpolator_component.dart';
|
||||||
import 'package:rive/src/rive_core/animation/interpolator.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.dart';
|
||||||
import 'package:rive/src/rive_core/component_dirt.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_modifier_group.dart';
|
||||||
|
import 'package:rive/src/rive_core/text/text_value_run.dart';
|
||||||
import 'package:rive_common/rive_text.dart';
|
import 'package:rive_common/rive_text.dart';
|
||||||
|
|
||||||
export 'package:rive/src/generated/text/text_modifier_range_base.dart';
|
export 'package:rive/src/generated/text/text_modifier_range_base.dart';
|
||||||
@ -110,25 +111,36 @@ class TextModifierRange extends TextModifierRangeBase {
|
|||||||
if (_rangeMapper != null) {
|
if (_rangeMapper != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
int start = 0;
|
||||||
|
int end = text.length;
|
||||||
|
if (run != null) {
|
||||||
|
start = run!.offset;
|
||||||
|
end = start + run!.text.length;
|
||||||
|
}
|
||||||
switch (units) {
|
switch (units) {
|
||||||
case TextRangeUnits.charactersExcludingSpaces:
|
case TextRangeUnits.charactersExcludingSpaces:
|
||||||
_rangeMapper = RangeMapper.fromCharacters(
|
_rangeMapper = RangeMapper.fromCharacters(
|
||||||
text,
|
text,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
glyphLookup,
|
glyphLookup,
|
||||||
withoutSpaces: true,
|
withoutSpaces: true,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case TextRangeUnits.words:
|
case TextRangeUnits.words:
|
||||||
_rangeMapper = RangeMapper.fromWords(text);
|
_rangeMapper = RangeMapper.fromWords(text, start, end);
|
||||||
break;
|
break;
|
||||||
case TextRangeUnits.lines:
|
case TextRangeUnits.lines:
|
||||||
if (shape != null && lines != null) {
|
if (shape != null && lines != null) {
|
||||||
_rangeMapper = RangeMapper.fromLines(text, shape, lines, glyphLookup);
|
_rangeMapper = RangeMapper.fromLines(
|
||||||
|
text, start, end, shape, lines, glyphLookup);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
_rangeMapper = RangeMapper.fromCharacters(
|
_rangeMapper = RangeMapper.fromCharacters(
|
||||||
text,
|
text,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
glyphLookup,
|
glyphLookup,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
@ -247,6 +259,30 @@ class TextModifierRange extends TextModifierRangeBase {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void strengthChanged(double from, double to) => modifierGroup?.rangeChanged();
|
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
|
// See word indices and word lengths implementation above, we basically want the
|
||||||
@ -270,7 +306,7 @@ class RangeMapper {
|
|||||||
unitLengths = Uint32List.fromList(lengths);
|
unitLengths = Uint32List.fromList(lengths);
|
||||||
|
|
||||||
/// Build a RangeMapper from the words in [text].
|
/// 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) {
|
if (text.isEmpty) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -280,12 +316,21 @@ class RangeMapper {
|
|||||||
|
|
||||||
int characterCount = 0;
|
int characterCount = 0;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
int indexFrom = 0;
|
||||||
for (final unit in text.codeUnits) {
|
for (final unit in text.codeUnits) {
|
||||||
if (wantWhiteSpace == isWhiteSpace(unit)) {
|
if (wantWhiteSpace == isWhiteSpace(unit)) {
|
||||||
if (!wantWhiteSpace) {
|
if (!wantWhiteSpace) {
|
||||||
indices.add(index);
|
indexFrom = index;
|
||||||
} else {
|
} 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;
|
characterCount = 0;
|
||||||
}
|
}
|
||||||
@ -296,23 +341,20 @@ class RangeMapper {
|
|||||||
}
|
}
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
indices.add(end);
|
||||||
if (characterCount != 0) {
|
|
||||||
lengths.add(characterCount);
|
|
||||||
indices.add(index);
|
|
||||||
}
|
|
||||||
return RangeMapper(indices, lengths);
|
return RangeMapper(indices, lengths);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a RangeMapper from the words in [text].
|
/// 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}) {
|
{bool withoutSpaces = false}) {
|
||||||
if (text.isEmpty) {
|
if (text.isEmpty) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
List<int> indices = [];
|
List<int> indices = [];
|
||||||
List<int> lengths = [];
|
List<int> lengths = [];
|
||||||
for (int i = 0; i < text.length;) {
|
for (int i = start; i < end;) {
|
||||||
var unit = text.codeUnits[i];
|
var unit = text.codeUnits[i];
|
||||||
if (withoutSpaces && isWhiteSpace(unit)) {
|
if (withoutSpaces && isWhiteSpace(unit)) {
|
||||||
i++;
|
i++;
|
||||||
@ -325,12 +367,12 @@ class RangeMapper {
|
|||||||
i += codePoints;
|
i += codePoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
indices.add(text.length);
|
indices.add(end);
|
||||||
return RangeMapper(indices, lengths);
|
return RangeMapper(indices, lengths);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RangeMapper? fromLines(String text, TextShapeResult shape,
|
static RangeMapper? fromLines(String text, int start, int end,
|
||||||
BreakLinesResult lines, GlyphLookup glyphLookup) {
|
TextShapeResult shape, BreakLinesResult lines, GlyphLookup glyphLookup) {
|
||||||
if (text.isEmpty) {
|
if (text.isEmpty) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -342,23 +384,25 @@ class RangeMapper {
|
|||||||
final paragraph = shape.paragraphs[paragraphIndex++];
|
final paragraph = shape.paragraphs[paragraphIndex++];
|
||||||
var glyphRuns = paragraph.runs;
|
var glyphRuns = paragraph.runs;
|
||||||
for (final line in paragraphLines) {
|
for (final line in paragraphLines) {
|
||||||
// if (line.endIndex == 0) {
|
|
||||||
// // Empty line.
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
var rf = glyphRuns[line.startRun];
|
var rf = glyphRuns[line.startRun];
|
||||||
var indexFrom = rf.textIndexAt(line.startIndex);
|
var indexFrom = rf.textIndexAt(line.startIndex);
|
||||||
|
|
||||||
var rt = glyphRuns[line.endRun];
|
var rt = glyphRuns[line.endRun];
|
||||||
var endGlyphIndex = max(
|
var endGlyphIndex = max(0, line.endIndex - 1);
|
||||||
0, line.endIndex - 1); //min(rt.glyphCount - 1, line.endIndex - 1);
|
|
||||||
var indexTo = rt.textIndexAt(endGlyphIndex);
|
var indexTo = rt.textIndexAt(endGlyphIndex);
|
||||||
indexTo += glyphLookup.count(indexTo);
|
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);
|
return RangeMapper(indices, lengths);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,22 @@ class TextValueRun extends TextValueRunBase {
|
|||||||
_style?.ref(this);
|
_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();
|
void markShapeDirty() => textComponent?.markShapeDirty();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
Reference in New Issue
Block a user