diff --git a/lib/pangea/choreographer/controllers/igc_controller.dart b/lib/pangea/choreographer/controllers/igc_controller.dart index a14d2ef41..2db0663e8 100644 --- a/lib/pangea/choreographer/controllers/igc_controller.dart +++ b/lib/pangea/choreographer/controllers/igc_controller.dart @@ -244,6 +244,7 @@ class IgcController { choreographer.chatController.inputFocus.unfocus(); OverlayUtil.showPositionedCard( + overlayKey: "span_card_overlay_$firstMatchIndex", context: context, cardToShow: SpanCard( scm: SpanCardModel( @@ -265,6 +266,7 @@ class IgcController { maxHeight: match.isITStart ? 260 : 350, maxWidth: 350, transformTargetId: choreographer.inputTransformTargetKey, + onDismiss: () => choreographer.setState(), ); } diff --git a/lib/pangea/choreographer/models/igc_text_data_model.dart b/lib/pangea/choreographer/models/igc_text_data_model.dart index 4f6d46ceb..05baa063e 100644 --- a/lib/pangea/choreographer/models/igc_text_data_model.dart +++ b/lib/pangea/choreographer/models/igc_text_data_model.dart @@ -16,6 +16,7 @@ import 'package:fluffychat/pangea/events/event_wrappers/pangea_representation_ev import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/events/models/representation_content_model.dart'; import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; +import 'package:fluffychat/widgets/matrix.dart'; // import 'package:language_tool/language_tool.dart'; @@ -358,6 +359,21 @@ class IGCTextData { ); } + int? get _openMatchIndex { + final RegExp pattern = RegExp(r'span_card_overlay_\d+'); + final String? matchingKeys = + MatrixState.pAnyState.getMatchingOverlayKeys(pattern).firstOrNull; + if (matchingKeys == null) return null; + final int? index = int.tryParse(matchingKeys.split("_").last); + if (index == null || + matches.length <= index || + matches[index].status != PangeaMatchStatus.open) { + return null; + } + + return index; + } + //PTODO - handle multitoken spans /// Returns a list of [TextSpan]s used to display the text in the input field /// with the appropriate styling for each error match. @@ -410,7 +426,11 @@ class IGCTextData { getSpanItem( start: match.match.offset, end: match.match.offset + match.match.length, - style: match.textStyle(defaultStyle), + style: match.textStyle( + matchIndex, + _openMatchIndex, + defaultStyle, + ), ), ); diff --git a/lib/pangea/choreographer/models/pangea_match_model.dart b/lib/pangea/choreographer/models/pangea_match_model.dart index f56900a42..ca22cd558 100644 --- a/lib/pangea/choreographer/models/pangea_match_model.dart +++ b/lib/pangea/choreographer/models/pangea_match_model.dart @@ -127,9 +127,26 @@ class PangeaMatch { } } - TextStyle textStyle(TextStyle? existingStyle) => - existingStyle?.merge(IGCTextData.underlineStyle(underlineColor)) ?? - IGCTextData.underlineStyle(underlineColor); + TextStyle textStyle( + int matchIndex, + int? openMatchIndex, + TextStyle? existingStyle, + ) { + double opacityFactor = 1.0; + if (openMatchIndex != null && openMatchIndex != matchIndex) { + opacityFactor = 0.5; + } + + final int alpha = (255 * opacityFactor).round(); + return existingStyle?.merge( + IGCTextData.underlineStyle( + underlineColor.withAlpha(alpha), + ), + ) ?? + IGCTextData.underlineStyle( + underlineColor.withAlpha(alpha), + ); + } PangeaMatch get copyWith => PangeaMatch.fromJson(toJson()); diff --git a/lib/pangea/choreographer/widgets/igc/pangea_text_controller.dart b/lib/pangea/choreographer/widgets/igc/pangea_text_controller.dart index 804243d0f..386e4c42c 100644 --- a/lib/pangea/choreographer/widgets/igc/pangea_text_controller.dart +++ b/lib/pangea/choreographer/widgets/igc/pangea_text_controller.dart @@ -120,6 +120,7 @@ class PangeaTextController extends TextEditingController { if (cardToShow != null) { OverlayUtil.showPositionedCard( + overlayKey: matchIndex != -1 ? "span_card_overlay_$matchIndex" : null, context: context, maxHeight: matchIndex != -1 && choreographer.igc.igcTextData!.matches[matchIndex].isITStart @@ -128,6 +129,7 @@ class PangeaTextController extends TextEditingController { maxWidth: 350, cardToShow: cardToShow, transformTargetId: choreographer.inputTransformTargetKey, + onDismiss: () => choreographer.setState(), ); } } diff --git a/lib/pangea/choreographer/widgets/igc/span_card.dart b/lib/pangea/choreographer/widgets/igc/span_card.dart index 1de869c41..bd889930e 100644 --- a/lib/pangea/choreographer/widgets/igc/span_card.dart +++ b/lib/pangea/choreographer/widgets/igc/span_card.dart @@ -269,6 +269,7 @@ class WordMatchContent extends StatelessWidget { botExpression: controller.error == null ? controller.currentExpression : BotExpression.addled, + onClose: () => controller.widget.scm.choreographer.setState(), ), Scrollbar( controller: scrollController, diff --git a/lib/pangea/common/utils/any_state_holder.dart b/lib/pangea/common/utils/any_state_holder.dart index 2cc03e67d..d7b563900 100644 --- a/lib/pangea/common/utils/any_state_holder.dart +++ b/lib/pangea/common/utils/any_state_holder.dart @@ -107,6 +107,15 @@ class PangeaAnyState { bool isOverlayOpen(String overlayKey) { return entries.any((element) => element.key == overlayKey); } + + List getMatchingOverlayKeys(RegExp regex) { + return entries + .where((e) => e.key != null) + .where((element) => regex.hasMatch(element.key!)) + .map((e) => e.key) + .whereType() + .toList(); + } } class LayerLinkAndKey { diff --git a/lib/pangea/common/utils/overlay.dart b/lib/pangea/common/utils/overlay.dart index d5a4fbaac..a08efdf8d 100644 --- a/lib/pangea/common/utils/overlay.dart +++ b/lib/pangea/common/utils/overlay.dart @@ -27,7 +27,7 @@ class OverlayUtil { Color? borderColor, Color? backgroundColor, bool closePrevOverlay = true, - Function? onDismiss, + VoidCallback? onDismiss, OverlayPositionEnum position = OverlayPositionEnum.transform, Offset? offset, String? overlayKey, @@ -117,6 +117,7 @@ class OverlayUtil { String? overlayKey, bool isScrollable = true, bool addBorder = true, + VoidCallback? onDismiss, }) { try { final LayerLinkAndKey layerLinkAndKey = @@ -192,6 +193,7 @@ class OverlayUtil { hasTopOverflow ? Alignment.bottomCenter : Alignment.topCenter, followerAnchor: hasTopOverflow ? Alignment.topCenter : Alignment.bottomCenter, + onDismiss: onDismiss, ); } catch (err, stack) { debugger(when: kDebugMode); @@ -208,7 +210,7 @@ class OverlayUtil { class TransparentBackdrop extends StatefulWidget { final Color? backgroundColor; - final Function? onDismiss; + final VoidCallback? onDismiss; final bool blurBackground; const TransparentBackdrop({