Skip to content

Commit

Permalink
All proposed changes have been made.
Browse files Browse the repository at this point in the history
  • Loading branch information
nazarski committed Apr 27, 2023
1 parent 4a18686 commit 8af6194
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 74 deletions.
157 changes: 102 additions & 55 deletions lib/core/controllers/colored_text_editing_controller.dart
Original file line number Diff line number Diff line change
@@ -1,107 +1,154 @@
import 'dart:developer';

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:languagetool_textfield/core/enums/mistake_type.dart';
import 'package:languagetool_textfield/domain/highlight_colors.dart';
import 'package:languagetool_textfield/domain/mistake.dart';

/// A TextEditingController with overrides buildTextSpan for building
/// marked TextSpans with tap recognizer
class ColoredTextEditingController extends TextEditingController {
/// Color scheme to highlight mistakes
final HighlightColors? highlightColorScheme;

/// List which contains Mistake objects spans are built from
List<Mistake> _mistakes = [];

final double _backGroundOpacity =
final double _backgroundOpacity =
0.2; // background opacity for mistake TextSpan

final double _mistakeLineThickness =
1.5; // mistake TextSpan underline thickness

/// A method sets new list of Mistake and triggers buildTextSpan
void setMistakes(List<Mistake> list) {
_mistakes = list;
notifyListeners();
@override
set value(TextEditingValue newValue) {
_handleTextChange(newValue.text);
super.value = newValue;
}

/// builds TextSpan from Mistake list
/// Controller constructor
ColoredTextEditingController({this.highlightColorScheme});

/// Generates TextSpan from Mistake list
@override
TextSpan buildTextSpan({
required BuildContext context,
TextStyle? style,
required bool withComposing,
}) {
int currentOffset = 0; // enter index
final List<TextSpan> spans = []; // List of TextSpan
final int textLength = text.length; // Length of text to be built

/// Iterates _mistakes and adds TextSpans from Mistake offset and length
for (final Mistake mistake in _mistakes) {
/// Breaks the loop if iterated Mistake offset is bigger than text length.
if (mistake.offset > textLength ||
mistake.offset + mistake.length > textLength) {
break;
}

/// TextSpan before mistake
spans.add(
TextSpan(
final int textLength = text.length;

/// Generator function to create TextSpan instances
Iterable<TextSpan> generateSpans() sync* {
for (final Mistake mistake in _mistakes) {
/// Breaks the loop if iterated Mistake offset is bigger than text
/// length.
if (mistake.offset > textLength ||
mistake.offset + mistake.length > textLength) {
break;
}

/// TextSpan before mistake
yield TextSpan(
text: text.substring(
currentOffset,
mistake.offset,
),
style: style,
),
);
);

/// Setting color of the mistake by its type
final Color mistakeColor = _getMistakeColor(mistake.type);
/// Get a highlight color
final Color mistakeColor = _getMistakeColor(mistake.type);

/// The mistake TextSpan
spans.add(
TextSpan(
/// Mistake highlighted TextSpan
yield TextSpan(
text: text.substring(mistake.offset, mistake.offset + mistake.length),
mouseCursor: MaterialStateMouseCursor.clickable,
recognizer: TapGestureRecognizer()
..onTapDown = _callOverlay, // calls overlay with mistakes details
style: style?.copyWith(
backgroundColor: mistakeColor.withOpacity(_backGroundOpacity),
backgroundColor: mistakeColor.withOpacity(_backgroundOpacity),
decoration: TextDecoration.underline,
decorationColor: mistakeColor,
decorationThickness: _mistakeLineThickness,
),
),
);
);

/// Changing enter index position for the next iteration
currentOffset = mistake.offset + mistake.length;
}
currentOffset = mistake.offset + mistake.length;
}

/// TextSpan after mistake
spans.add(
TextSpan(
/// TextSpan after mistake
yield TextSpan(
text: text.substring(currentOffset),
style: style,
),
);
);
}

/// Returns TextSpan
return TextSpan(children: spans);
return TextSpan(children: generateSpans().toList());
}

void _callOverlay(TapDownDetails details) {
log(details.globalPosition.toString());
/// Apply changes to Mistake list while new data being fetched
void _handleTextChange(String newText) {
final int deltaLength = newText.length - text.length;

/// Update the _mistakes list in-place based on the text modifications
_mistakes = _mistakes
.map((mistake) {
int newOffset = mistake.offset;
int newLength = mistake.length;

/// If the text modification starts within the mistake
if (selection.start >= mistake.offset &&
selection.start <= mistake.offset + mistake.length) {
newLength += deltaLength;
}

/// If the text modification starts before the mistake
else if (selection.start < mistake.offset) {
newOffset += deltaLength;
}

/// Return the updated mistake (if the length is greater than 0)
return newLength > 0
? Mistake(
message: mistake.message,
type: mistake.type,
offset: newOffset,
length: newLength,
replacements: mistake.replacements,
)
: null;
})
.whereType<Mistake>()
.toList();

/// Notify listeners to rebuild the widget
notifyListeners();
}

/// A method sets new list of Mistake and triggers buildTextSpan
void highlightMistakes(List<Mistake> list) {
_mistakes = list;
notifyListeners();
}

/// Returns color for mistake TextSpan style
Color _getMistakeColor(String type) {
Color _getMistakeColor(MistakeType type) {
switch (type) {
case 'misspelling':
return Colors.red;
case 'style':
return Colors.blue;
case 'uncategorized':
return Colors.amber;
default:
return Colors.green;
case MistakeType.misspelling:
return highlightColorScheme?.misspellingMistakeColor ?? Colors.red;
case MistakeType.typographical:
return highlightColorScheme?.typographicalMistakeColor ?? Colors.green;
case MistakeType.grammar:
return highlightColorScheme?.grammarMistakeColor ?? Colors.amber;
case MistakeType.uncategorized:
return highlightColorScheme?.uncategorizedMistakeColor ?? Colors.blue;
case MistakeType.nonConformance:
return highlightColorScheme?.nonConformanceMistakeColor ??
Colors.greenAccent;
case MistakeType.style:
return highlightColorScheme?.styleMistakeColor ??
Colors.deepPurpleAccent;
case MistakeType.other:
return highlightColorScheme?.otherMistakeColor ?? Colors.white60;
}
}
}
23 changes: 23 additions & 0 deletions lib/core/enums/mistake_type.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
///Enumerate several mistake types
enum MistakeType {
/// Misspelling mistake type
misspelling,

/// Typographical mistake type
typographical,

/// Grammar mistake type
grammar,

/// Uncategorized mistake type
uncategorized,

/// NonConformance mistake type
nonConformance,

/// Style mistake type
style,

/// Any other mistake type
other,
}
36 changes: 36 additions & 0 deletions lib/domain/highlight_colors.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'dart:ui';

/// Class creates color scheme for highlighting mistakes
class HighlightColors {
/// Misspelling mistake highlight color
final Color? misspellingMistakeColor;

/// Misspelling mistake highlight color
final Color? typographicalMistakeColor;

/// Typographical mistake highlight color
final Color? grammarMistakeColor;

/// Uncategorized mistake highlight color
final Color? uncategorizedMistakeColor;

/// NonConformance mistake highlight color
final Color? nonConformanceMistakeColor;

/// Style mistake highlight color
final Color? styleMistakeColor;

/// Other mistake highlight color
final Color? otherMistakeColor;

///Color scheme constructor
HighlightColors(
this.misspellingMistakeColor,
this.typographicalMistakeColor,
this.grammarMistakeColor,
this.uncategorizedMistakeColor,
this.nonConformanceMistakeColor,
this.styleMistakeColor,
this.otherMistakeColor,
);
}
10 changes: 3 additions & 7 deletions lib/domain/mistake.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import 'package:languagetool_textfield/core/enums/mistake_type.dart';

/// A data model class that stores information about a single writing mistake.
class Mistake {
/// A brief description of the mistake.
final String message;

/// A type of this mistake.
final String type;
final MistakeType type;

/// A position of the beginning of this mistake.
final int offset;
Expand All @@ -25,10 +27,4 @@ class Mistake {
required this.length,
this.replacements = const [],
});

@override
String toString() {
return 'Mistake{message: $message, type: $type, offset: $offset, '
'length: $length, replacements: $replacements}';
}
}
24 changes: 23 additions & 1 deletion lib/implementations/lang_tool_service.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:language_tool/language_tool.dart';
import 'package:languagetool_textfield/core/enums/mistake_type.dart';
import 'package:languagetool_textfield/domain/language_check_service.dart';
import 'package:languagetool_textfield/domain/mistake.dart';

Expand All @@ -16,7 +17,9 @@ class LangToolService extends LanguageCheckService {
final mistakes = writingMistakes.map(
(m) => Mistake(
message: m.message,
type: m.issueType,
type: _stringToMistakeType(
m.issueType,
),
offset: m.offset,
length: m.length,
replacements: m.replacements,
Expand All @@ -25,4 +28,23 @@ class LangToolService extends LanguageCheckService {

return mistakes.toList();
}

MistakeType _stringToMistakeType(String issueType) {
switch (issueType.toLowerCase()) {
case 'misspelling':
return MistakeType.misspelling;
case 'typographical':
return MistakeType.typographical;
case 'grammar':
return MistakeType.grammar;
case 'uncategorized':
return MistakeType.uncategorized;
case 'non-conformance':
return MistakeType.nonConformance;
case 'style':
return MistakeType.style;
default:
return MistakeType.other;
}
}
}
1 change: 1 addition & 0 deletions lib/languagetool_textfield.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ library languagetool_textfield;

export 'package:language_tool/language_tool.dart';

export 'domain/highlight_colors.dart';
export 'domain/language_check_service.dart';
export 'domain/mistake.dart';
export 'implementations/debounce_lang_tool_service.dart';
Expand Down
Loading

0 comments on commit 8af6194

Please sign in to comment.