From 9175a05fd1dcd4087172699e6ff6e7f2825c2b0a Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Mon, 28 Apr 2025 11:13:06 -0400 Subject: [PATCH] chore: move morph editting into morph popup (#2565) --- lib/pangea/lemmas/construct_xp_widget.dart | 19 +- lib/pangea/morphs/edit_morph_widget.dart | 188 +++++++++------- .../widgets/reading_assistance_content.dart | 5 - .../word_zoom/morphological_list_item.dart | 208 +++++++++++------- .../widgets/word_zoom/word_zoom_widget.dart | 119 ++++------ 5 files changed, 295 insertions(+), 244 deletions(-) diff --git a/lib/pangea/lemmas/construct_xp_widget.dart b/lib/pangea/lemmas/construct_xp_widget.dart index e4a5ebd92..105a3a4bf 100644 --- a/lib/pangea/lemmas/construct_xp_widget.dart +++ b/lib/pangea/lemmas/construct_xp_widget.dart @@ -15,11 +15,13 @@ import 'package:fluffychat/widgets/matrix.dart'; class ConstructXpWidget extends StatefulWidget { final ConstructIdentifier id; final VoidCallback? onTap; + final double size; const ConstructXpWidget({ super.key, required this.id, this.onTap, + this.size = 24.0, }); @override @@ -100,12 +102,17 @@ class ConstructXpWidgetState extends State @override Widget build(BuildContext context) { - return GestureDetector( - onTap: widget.onTap, - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: Opacity( - opacity: constructLemmaCategory == null ? 0.2 : 1.0, + if (constructLemmaCategory == null) { + return const SizedBox(); + } + + return SizedBox( + width: widget.size, + height: widget.size, + child: GestureDetector( + onTap: widget.onTap, + child: MouseRegion( + cursor: SystemMouseCursors.click, child: Stack( alignment: Alignment.center, children: [ diff --git a/lib/pangea/morphs/edit_morph_widget.dart b/lib/pangea/morphs/edit_morph_widget.dart index ee471af8a..bad16ceb6 100644 --- a/lib/pangea/morphs/edit_morph_widget.dart +++ b/lib/pangea/morphs/edit_morph_widget.dart @@ -139,93 +139,119 @@ class EditMorphWidgetState extends State { @override Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - spacing: 8.0, + return Stack( children: [ - Text( - "${L10n.of(context).pangeaBotIsFallible} ${L10n.of(context).chooseCorrectLabel}", - textAlign: TextAlign.center, - style: const TextStyle(fontStyle: FontStyle.italic), - ), - if (_availableMorphTags == null || _availableMorphTags!.isEmpty) - const CircularProgressIndicator() - else - Wrap( - alignment: WrapAlignment.center, - children: _availableMorphTags!.map((tag) { - return Container( - margin: const EdgeInsets.all(2), - padding: EdgeInsets.zero, - decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(10), - ), - border: Border.all( - color: _selectedMorphTag == tag - ? Theme.of(context).colorScheme.primary - : Colors.transparent, - style: BorderStyle.solid, - width: 2.0, - ), - ), - child: TextButton( - style: ButtonStyle( - padding: WidgetStateProperty.all( - const EdgeInsets.symmetric( - horizontal: 7, - ), - ), - backgroundColor: WidgetStateProperty.all( - _selectedMorphTag == tag - ? Theme.of(context).colorScheme.primary.withAlpha(50) - : Colors.transparent, - ), - shape: WidgetStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - ), - ), - onPressed: () => setState(() => _selectedMorphTag = tag), - child: Text( - getGrammarCopy( - category: widget.morphFeature.name, - lemma: tag, - context: context, - ) ?? - tag, - textAlign: TextAlign.center, - ), - ), - ); - }).toList(), + Padding( + padding: const EdgeInsets.only( + top: 16.0, + bottom: 48.0, ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - spacing: 10, - children: [ - ElevatedButton( - style: ElevatedButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10.0), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + spacing: 8.0, + children: [ + Text( + "${L10n.of(context).pangeaBotIsFallible} ${L10n.of(context).chooseCorrectLabel}", + textAlign: TextAlign.center, + style: const TextStyle(fontStyle: FontStyle.italic), ), - padding: const EdgeInsets.symmetric(horizontal: 10), - ), - onPressed: widget.onClose, - child: Text(L10n.of(context).cancel), + if (_availableMorphTags == null || _availableMorphTags!.isEmpty) + const CircularProgressIndicator() + else + Wrap( + alignment: WrapAlignment.center, + children: _availableMorphTags!.map((tag) { + return Container( + margin: const EdgeInsets.all(2), + padding: EdgeInsets.zero, + decoration: BoxDecoration( + borderRadius: const BorderRadius.all( + Radius.circular(10), + ), + border: Border.all( + color: _selectedMorphTag == tag + ? Theme.of(context).colorScheme.primary + : Colors.transparent, + style: BorderStyle.solid, + width: 2.0, + ), + ), + child: TextButton( + style: ButtonStyle( + padding: WidgetStateProperty.all( + const EdgeInsets.symmetric( + horizontal: 7, + ), + ), + backgroundColor: WidgetStateProperty.all( + _selectedMorphTag == tag + ? Theme.of(context) + .colorScheme + .primary + .withAlpha(50) + : Colors.transparent, + ), + shape: WidgetStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + ), + onPressed: () => + setState(() => _selectedMorphTag = tag), + child: Text( + getGrammarCopy( + category: widget.morphFeature.name, + lemma: tag, + context: context, + ) ?? + tag, + textAlign: TextAlign.center, + ), + ), + ); + }).toList(), + ), + ], ), - ElevatedButton( - style: ElevatedButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10.0), + ), + ), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 10, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + padding: const EdgeInsets.symmetric(horizontal: 10), + ), + onPressed: widget.onClose, + child: Text(L10n.of(context).cancel), ), - padding: const EdgeInsets.symmetric(horizontal: 10), - ), - onPressed: _canSaveChanges ? _saveChanges : null, - child: Text(L10n.of(context).saveChanges), + ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + padding: const EdgeInsets.symmetric(horizontal: 10), + ), + onPressed: _canSaveChanges ? _saveChanges : null, + child: Text(L10n.of(context).saveChanges), + ), + ], ), - ], + ), ), ], ); diff --git a/lib/pangea/toolbar/widgets/reading_assistance_content.dart b/lib/pangea/toolbar/widgets/reading_assistance_content.dart index 01c249418..facdb86e5 100644 --- a/lib/pangea/toolbar/widgets/reading_assistance_content.dart +++ b/lib/pangea/toolbar/widgets/reading_assistance_content.dart @@ -8,7 +8,6 @@ import 'package:matrix/matrix_api_lite/model/message_types.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; -import 'package:fluffychat/pangea/morphs/morph_features_enum.dart'; import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart'; import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart'; import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart'; @@ -39,8 +38,6 @@ class ReadingAssistanceContent extends StatefulWidget { } class ReadingAssistanceContentState extends State { - MorphFeaturesEnum? _selectedEditMorphFeature; - TtsController get ttsController => widget.overlayController.widget.chatController.choreographer.tts; @@ -128,8 +125,6 @@ class ReadingAssistanceContentState extends State { messageEvent: widget.overlayController.pangeaMessageEvent!, tts: ttsController, overlayController: widget.overlayController, - editMorph: (m) => setState(() => _selectedEditMorphFeature = m), - selectedEditMorphFeature: _selectedEditMorphFeature, ); } } diff --git a/lib/pangea/toolbar/widgets/word_zoom/morphological_list_item.dart b/lib/pangea/toolbar/widgets/word_zoom/morphological_list_item.dart index b9d220784..f7e4e1038 100644 --- a/lib/pangea/toolbar/widgets/word_zoom/morphological_list_item.dart +++ b/lib/pangea/toolbar/widgets/word_zoom/morphological_list_item.dart @@ -12,8 +12,10 @@ import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/common/utils/overlay.dart'; import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; +import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/lemmas/construct_xp_widget.dart'; +import 'package:fluffychat/pangea/morphs/edit_morph_widget.dart'; import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart'; import 'package:fluffychat/pangea/morphs/morph_features_enum.dart'; import 'package:fluffychat/pangea/morphs/morph_icon.dart'; @@ -29,13 +31,13 @@ class MorphologicalListItem extends StatelessWidget { final MorphFeaturesEnum morphFeature; final PangeaToken token; final MessageOverlayController overlayController; - final VoidCallback editMorph; + // final VoidCallback editMorph; const MorphologicalListItem({ required this.morphFeature, required this.token, required this.overlayController, - required this.editMorph, + // required this.editMorph, super.key, }); @@ -64,15 +66,26 @@ class MorphologicalListItem extends StatelessWidget { void _openDefintionPopup(BuildContext context) async { const width = 300.0; - const height = 150.0; + const height = 200.0; try { + if (overlayController.pangeaMessageEvent == null) { + return; + } + OverlayUtil.showPositionedCard( context: context, cardToShow: MorphMeaningPopup( + token: token, + pangeaMessageEvent: overlayController.pangeaMessageEvent!, cId: cId, width: width, height: height, + refresh: () { + overlayController.onMorphActivitySelect( + MorphSelection(token, morphFeature), + ); + }, ), transformTargetId: cId.string, backDropToDismiss: true, @@ -114,11 +127,6 @@ class MorphologicalListItem extends StatelessWidget { .onMorphActivitySelect(MorphSelection(token, morphFeature)); _openDefintionPopup(context); }, - onLongPress: () { - overlayController - .onMorphActivitySelect(MorphSelection(token, morphFeature)); - editMorph(); - }, tooltip: shouldDoActivity ? morphFeature.getDisplayCopy(context) : getGrammarCopy( @@ -134,15 +142,21 @@ class MorphologicalListItem extends StatelessWidget { } class MorphMeaningPopup extends StatefulWidget { + final PangeaToken token; + final PangeaMessageEvent pangeaMessageEvent; final ConstructIdentifier cId; final double width; final double height; + final VoidCallback refresh; const MorphMeaningPopup({ super.key, + required this.token, + required this.pangeaMessageEvent, required this.cId, required this.width, required this.height, + required this.refresh, }); @override @@ -154,9 +168,10 @@ class MorphMeaningPopupState extends State { MorphFeaturesEnumExtension.fromString(widget.cId.category); String get _morphTag => widget.cId.lemma; - String? _defintion; + bool _isEditMode = false; + @override void initState() { super.initState(); @@ -193,84 +208,121 @@ class MorphMeaningPopupState extends State { } } + void _setEditMode(bool editing) { + setState(() => _isEditMode = editing); + } + @override Widget build(BuildContext context) { return Material( borderRadius: BorderRadius.circular(AppConfig.borderRadius), - child: Container( - padding: const EdgeInsets.all(8), - height: widget.height, - width: widget.width, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - borderRadius: BorderRadius.circular(AppConfig.borderRadius), - boxShadow: [ - BoxShadow( - color: Theme.of(context).colorScheme.onSurface.withAlpha(50), - blurRadius: 4, - offset: const Offset(0, 2), + child: Stack( + children: [ + Container( + padding: const EdgeInsets.all(8), + height: widget.height, + width: widget.width, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + boxShadow: [ + BoxShadow( + color: Theme.of(context).colorScheme.onSurface.withAlpha(50), + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], ), - ], - ), - alignment: Alignment.center, - child: SingleChildScrollView( - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - spacing: 16.0, - children: [ - SizedBox( - width: 24.0, - height: 24.0, - child: MorphIcon( - morphFeature: _morphFeature, - morphTag: _morphTag, - ), - ), - Text( - getGrammarCopy( - category: _morphFeature.name, - lemma: _morphTag, - context: context, - ) ?? - _morphTag, - style: Theme.of(context).textTheme.titleMedium, - ), - SizedBox( - width: 24.0, - height: 24.0, - child: ConstructXpWidget( - id: widget.cId, - onTap: () => showDialog( - context: context, - builder: (context) => AnalyticsPopupWrapper( - constructZoom: widget.cId, - view: ConstructTypeEnum.morph, - backButtonOverride: IconButton( - icon: const Icon(Icons.close), - onPressed: () => Navigator.of(context).pop(), - ), + alignment: Alignment.center, + child: _isEditMode + ? EditMorphWidget( + token: widget.token, + pangeaMessageEvent: widget.pangeaMessageEvent, + morphFeature: _morphFeature, + onClose: () { + _setEditMode(false); + _fetchDefinition(); + widget.refresh(); + }, + ) + : SingleChildScrollView( + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 16.0, + children: [ + SizedBox( + width: 24.0, + height: 24.0, + child: MorphIcon( + morphFeature: _morphFeature, + morphTag: _morphTag, + ), + ), + Text( + getGrammarCopy( + category: _morphFeature.name, + lemma: _morphTag, + context: context, + ) ?? + _morphTag, + style: Theme.of(context).textTheme.titleMedium, + ), + if (MatrixState.pangeaController.getAnalytics + .constructListModel + .getConstructUses(widget.cId) != + null) + ConstructXpWidget( + id: widget.cId, + onTap: () => showDialog( + context: context, + builder: (context) => AnalyticsPopupWrapper( + constructZoom: widget.cId, + view: ConstructTypeEnum.morph, + backButtonOverride: IconButton( + icon: const Icon(Icons.close), + onPressed: () => + Navigator.of(context).pop(), + ), + ), + ), + ), + ], ), - ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: _defintion != null + ? Text( + _defintion!, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.bodyMedium, + ) + : const LinearProgressIndicator(), + ), + ], ), ), - ], - ), - Padding( - padding: const EdgeInsets.only(top: 16.0), - child: _defintion != null - ? Text( - _defintion!, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodyMedium, - ) - : const LinearProgressIndicator(), - ), - ], ), - ), + if (!_isEditMode) + Positioned( + top: 12.0, + right: 12.0, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + child: Icon( + Icons.edit_outlined, + size: 20.0, + color: Theme.of(context).disabledColor, + ), + onTap: () { + _setEditMode(true); + }, + ), + ), + ), + ], ), ); } diff --git a/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart b/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart index 19e63ce2b..158598125 100644 --- a/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart +++ b/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart @@ -8,7 +8,6 @@ import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; import 'package:fluffychat/pangea/lemmas/construct_xp_widget.dart'; import 'package:fluffychat/pangea/lemmas/lemma_emoji_row.dart'; -import 'package:fluffychat/pangea/morphs/edit_morph_widget.dart'; import 'package:fluffychat/pangea/morphs/morph_features_enum.dart'; import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart'; import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart'; @@ -25,9 +24,6 @@ class WordZoomWidget extends StatelessWidget { final PangeaMessageEvent messageEvent; final TtsController tts; final MessageOverlayController overlayController; - final Function(MorphFeaturesEnum?) editMorph; - - final MorphFeaturesEnum? selectedEditMorphFeature; const WordZoomWidget({ super.key, @@ -35,8 +31,6 @@ class WordZoomWidget extends StatelessWidget { required this.messageEvent, required this.tts, required this.overlayController, - required this.editMorph, - required this.selectedEditMorphFeature, }); PangeaToken get _selectedToken => overlayController.selectedToken!; @@ -111,68 +105,56 @@ class WordZoomWidget extends StatelessWidget { const SizedBox( height: 8.0, ), - if (selectedEditMorphFeature == null) - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - constraints: const BoxConstraints( - minHeight: 40, - ), - alignment: Alignment.center, - child: LemmaEmojiRow( - cId: _selectedToken.vocabConstructID, - onTapOverride: overlayController.hideWordCardContent && - hasEmojiActivity - ? () => overlayController.updateToolbarMode( - MessageMode.wordEmoji, - ) - : null, - isSelected: overlayController.toolbarMode == - MessageMode.wordEmoji, - emojiSetCallback: () => overlayController.setState(() {}), - shouldShowEmojis: !hasEmojiActivity, - ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + constraints: const BoxConstraints( + minHeight: 40, ), - ], - ), + alignment: Alignment.center, + child: LemmaEmojiRow( + cId: _selectedToken.vocabConstructID, + onTapOverride: overlayController.hideWordCardContent && + hasEmojiActivity + ? () => overlayController.updateToolbarMode( + MessageMode.wordEmoji, + ) + : null, + isSelected: + overlayController.toolbarMode == MessageMode.wordEmoji, + emojiSetCallback: () => overlayController.setState(() {}), + shouldShowEmojis: !hasEmojiActivity, + ), + ), + ], + ), const SizedBox( height: 8.0, ), - if (selectedEditMorphFeature == null) - Container( - constraints: const BoxConstraints( - minHeight: 40, - ), - alignment: Alignment.center, - child: Wrap( - alignment: WrapAlignment.center, - runAlignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - spacing: 8, - children: [ - LemmaMeaningWidget( - constructUse: token.vocabConstructID.constructUses, - langCode: MatrixState.pangeaController.languageController - .userL2?.langCodeShort ?? - LanguageKeys.defaultLanguage, - token: overlayController.selectedToken!, - controller: overlayController, - style: Theme.of(context).textTheme.bodyLarge, - ), - ], - ), - ) - else - EditMorphWidget( - token: token, - pangeaMessageEvent: overlayController.pangeaMessageEvent!, - morphFeature: selectedEditMorphFeature!, - onClose: () { - editMorph(null); - overlayController.setState(() {}); - }, + Container( + constraints: const BoxConstraints( + minHeight: 40, ), + alignment: Alignment.center, + child: Wrap( + alignment: WrapAlignment.center, + runAlignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + spacing: 8, + children: [ + LemmaMeaningWidget( + constructUse: token.vocabConstructID.constructUses, + langCode: MatrixState.pangeaController.languageController + .userL2?.langCodeShort ?? + LanguageKeys.defaultLanguage, + token: overlayController.selectedToken!, + controller: overlayController, + style: Theme.of(context).textTheme.bodyLarge, + ), + ], + ), + ), const SizedBox( height: 8.0, ), @@ -215,21 +197,10 @@ class WordZoomWidget extends StatelessWidget { ), token: _selectedToken, overlayController: overlayController, - editMorph: () => editMorph( - MorphFeaturesEnumExtension.fromString(cId.category), - ), ), ), ], ), - // if (_selectedMorphFeature != null) - // MorphologicalCenterWidget( - // token: token, - // morphFeature: _selectedMorphFeature!, - // pangeaMessageEvent: messageEvent, - // overlayController: overlayController, - // onEditDone: onEditDone, - // ), ], ), ),