Compare commits

..

3 Commits

  1. 55
      CHANGELOG.md
  2. 2
      CHANGELOG_DATA.json
  3. 63
      README.md
  4. 55
      dart_quill_delta/CHANGELOG.md
  5. 2
      dart_quill_delta/pubspec.yaml
  6. 13
      example/lib/screens/home/widgets/home_screen.dart
  7. 25
      example/lib/screens/quill/quill_screen.dart
  8. 55
      flutter_quill_extensions/CHANGELOG.md
  9. 25
      flutter_quill_extensions/lib/flutter_quill_extensions.dart
  10. 65
      flutter_quill_extensions/lib/src/editor/spell_checker/simple_spell_checker_service.dart
  11. 5
      flutter_quill_extensions/pubspec.yaml
  12. 55
      flutter_quill_test/CHANGELOG.md
  13. 2
      flutter_quill_test/pubspec.yaml
  14. 2
      lib/flutter_quill.dart
  15. 13
      lib/src/common/utils/color.dart
  16. 6
      lib/src/controller/quill_controller.dart
  17. 21
      lib/src/document/document.dart
  18. 27
      lib/src/document/nodes/line.dart
  19. 6
      lib/src/editor/config/editor_configurations.dart
  20. 46
      lib/src/editor/editor.dart
  21. 2
      lib/src/editor/raw_editor/raw_editor.dart
  22. 87
      lib/src/editor/raw_editor/raw_editor_actions.dart
  23. 27
      lib/src/editor/raw_editor/raw_editor_state.dart
  24. 13
      lib/src/editor/raw_editor/raw_editor_state_text_input_client_mixin.dart
  25. 37
      lib/src/editor/spellchecker/default_spellchecker_service.dart
  26. 39
      lib/src/editor/spellchecker/spellchecker_service.dart
  27. 36
      lib/src/editor/spellchecker/spellchecker_service_provider.dart
  28. 5
      lib/src/editor/widgets/default_styles.dart
  29. 684
      lib/src/editor/widgets/delegate.dart
  30. 25
      lib/src/editor/widgets/text/text_line.dart
  31. 397
      lib/src/editor/widgets/text/text_selection.dart
  32. 7
      lib/src/l10n/generated/quill_localizations.dart
  33. 8
      lib/src/l10n/generated/quill_localizations_ar.dart
  34. 8
      lib/src/l10n/generated/quill_localizations_bg.dart
  35. 8
      lib/src/l10n/generated/quill_localizations_bn.dart
  36. 26
      lib/src/l10n/generated/quill_localizations_ca.dart
  37. 8
      lib/src/l10n/generated/quill_localizations_cs.dart
  38. 8
      lib/src/l10n/generated/quill_localizations_da.dart
  39. 8
      lib/src/l10n/generated/quill_localizations_de.dart
  40. 12
      lib/src/l10n/generated/quill_localizations_en.dart
  41. 8
      lib/src/l10n/generated/quill_localizations_es.dart
  42. 8
      lib/src/l10n/generated/quill_localizations_fa.dart
  43. 22
      lib/src/l10n/generated/quill_localizations_fr.dart
  44. 8
      lib/src/l10n/generated/quill_localizations_he.dart
  45. 8
      lib/src/l10n/generated/quill_localizations_hi.dart
  46. 8
      lib/src/l10n/generated/quill_localizations_id.dart
  47. 14
      lib/src/l10n/generated/quill_localizations_it.dart
  48. 8
      lib/src/l10n/generated/quill_localizations_ja.dart
  49. 304
      lib/src/l10n/generated/quill_localizations_km.dart
  50. 6
      lib/src/l10n/generated/quill_localizations_ko.dart
  51. 12
      lib/src/l10n/generated/quill_localizations_ku.dart
  52. 8
      lib/src/l10n/generated/quill_localizations_ms.dart
  53. 8
      lib/src/l10n/generated/quill_localizations_ne.dart
  54. 8
      lib/src/l10n/generated/quill_localizations_nl.dart
  55. 8
      lib/src/l10n/generated/quill_localizations_no.dart
  56. 8
      lib/src/l10n/generated/quill_localizations_pl.dart
  57. 12
      lib/src/l10n/generated/quill_localizations_pt.dart
  58. 12
      lib/src/l10n/generated/quill_localizations_ro.dart
  59. 8
      lib/src/l10n/generated/quill_localizations_ru.dart
  60. 8
      lib/src/l10n/generated/quill_localizations_sk.dart
  61. 8
      lib/src/l10n/generated/quill_localizations_sr.dart
  62. 8
      lib/src/l10n/generated/quill_localizations_sv.dart
  63. 8
      lib/src/l10n/generated/quill_localizations_sw.dart
  64. 4
      lib/src/l10n/generated/quill_localizations_tk.dart
  65. 10
      lib/src/l10n/generated/quill_localizations_tr.dart
  66. 8
      lib/src/l10n/generated/quill_localizations_uk.dart
  67. 8
      lib/src/l10n/generated/quill_localizations_ur.dart
  68. 8
      lib/src/l10n/generated/quill_localizations_vi.dart
  69. 16
      lib/src/l10n/generated/quill_localizations_zh.dart
  70. 112
      lib/src/l10n/quill_km.arb
  71. 21
      lib/src/rules/insert.dart
  72. 4
      pubspec.yaml
  73. 48
      test/document/document_test.dart
  74. 83
      test/document/line_test.dart

@ -4,61 +4,6 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## 10.4.1
* Chore: improve Spell checker API to the example by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2133
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.4.0...v10.4.1
## 10.4.0
* Copy TapAndPanGestureRecognizer from TextField by @demoYang in https://github.com/singerdmx/flutter-quill/pull/2128
* enhance stringToColor with a custom defined palette from `DefaultStyles` by @vishna in https://github.com/singerdmx/flutter-quill/pull/2095
* Feat: include spell checker for example app by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2127
## New Contributors
* @vishna made their first contribution in https://github.com/singerdmx/flutter-quill/pull/2095
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.3.3...v10.4.0
## 10.3.2
* Fix: Loss of style when backspace by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2125
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.3.1...v10.3.2
## 10.3.1
* Chore: Move spellchecker service to extensions by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2120
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.3.0...v10.3.1
## 10.3.0
* Feat: Spellchecker for Flutter Quill by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2118
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.2.1...v10.3.0
## 10.2.1
* Fix: context menu is visible even when selection is collapsed by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2116
* Fix: unsafe operation while getting overlayEntry in text_selection by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2117
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.2.0...v10.2.1
## 10.2.0
* refactor!: restructure project into modular architecture for flutter_quill_extensions by @EchoEllet in https://github.com/singerdmx/flutter-quill/pull/2106
* Fix: Link selection and editing by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2114
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.10...v10.2.0
## 10.1.10 ## 10.1.10
* Fix(example): image_cropper outdated version by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2100 * Fix(example): image_cropper outdated version by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2100

File diff suppressed because one or more lines are too long

@ -64,7 +64,6 @@ You can join our [Slack Group] for discussion.
- [🛠 Using the embed blocks from `flutter_quill_extensions`](#-using-the-embed-blocks-from-flutter_quill_extensions) - [🛠 Using the embed blocks from `flutter_quill_extensions`](#-using-the-embed-blocks-from-flutter_quill_extensions)
- [🔗 Links](#-links-2) - [🔗 Links](#-links-2)
- [🔄 Conversion to HTML](#-conversion-to-html) - [🔄 Conversion to HTML](#-conversion-to-html)
- [📝 Spelling checker](#-Spelling-checker)
- [🌐 Translation](#-translation) - [🌐 Translation](#-translation)
- [🧪 Testing](#-testing) - [🧪 Testing](#-testing)
- [👥 Contributors](#-contributors) - [👥 Contributors](#-contributors)
@ -137,7 +136,7 @@ and attach the `QuillController` to them:
```dart ```dart
QuillSimpleToolbar( QuillSimpleToolbar(
controller: _controller, controller: _controller,
configurations: const QuillSimpleToolbarConfigurations(), configurations: QuillSimpleToolbarConfigurations(),
), ),
Expanded( Expanded(
child: QuillEditor.basic( child: QuillEditor.basic(
@ -283,66 +282,6 @@ The following packages can be used:
3. [`flutter_quill_to_pdf`](https://pub.dev/packages/flutter_quill_to_pdf): To convert **Delta** To **PDF**. 3. [`flutter_quill_to_pdf`](https://pub.dev/packages/flutter_quill_to_pdf): To convert **Delta** To **PDF**.
4. [`markdown_quill`](https://pub.dev/packages/markdown_quill): To convert **Markdown** To **Delta** and vice versa. 4. [`markdown_quill`](https://pub.dev/packages/markdown_quill): To convert **Markdown** To **Delta** and vice versa.
## 📝 Spelling checker
A spell checker is a software tool or feature integrated into various text processing applications that automatically identifies and corrects spelling errors in a written document. It works by comparing the words in the text against a built-in dictionary. If a word isn't found in the dictionary or doesn't match any known word patterns, the spell checker highlights it as a potential error.
#### Benefits of a spell checker include:
* Improved Accuracy: It helps writers avoid common spelling mistakes, ensuring that the text is free of errors.
* Time-Saving: Automatically detecting errors reduces the time needed for manual proofreading.
* Enhanced Professionalism: Correctly spelled words contribute to the overall professionalism of documents, which is crucial in academic, business, and formal writing.
* Multilingual Support: Many spell checkers support multiple languages, making it easier for users to write accurately in different languages.
> [!IMPORTANT]
> The spell checker usually does not work as expected in most cases. **Many translations are not supported** such as: `Chinese`, `Japanese`, `Korean`, `Hebrew`, `Arabic`, `Russian`, etc. For now it is a purely **experimental** feature that may have **code that will be modified** in future versions.
#### The translations supported so far are:
* **German** - `de` (may contain errors or missing words)
* **English** - `en` (currently adding missing translations)
* **Spanish** - `es` (currently adding missing translations)
* **French** - `fr` (may contain errors or missing words)
* **Italian** - `it` (currently adding missing translations)
* **Norwegian** - `no` (may contain errors or missing words)
* **Portuguese** - `pt` (may contain errors or missing words)
* **Swedish** - `sv` (may contain errors or missing words)
_**Note**: If you have knowledge about any of these available languages or the unsupported ones, you can make a pull request to add support or add words that are not currently in [simple_spell_checker](https://github.com/CatHood0/simple_spell_checker)_.
In order to activate this functionality you can use the following code:
```dart
// you can use the language of your preference or directly select the language of the operating system
final language = 'en'; // or Localizations.localeOf(context).languageCode
FlutterQuillExtensions.useSpellCheckerService(language);
```
When you no longer need to have the Spell checker activated you can simply use `dispose()` of the `SpellCheckerServiceProvider` class:
```dart
// dispose all service and it cannot be used after this
SpellCheckerServiceProvider.dispose();
```
If what we want is to **close the StreamControllers** without deleting the values that are already stored in it, we can set `onlyPartial` to `true`.
```dart
// it can be still used by the editor
SpellCheckerServiceProvider.dispose(onlyPartial: true);
```
One use of this would be having the opportunity to **activate and deactivate** the service when we want, we can see this in the example that we have in this package, in which you can see that on each screen, we have a button that dynamically activates and deactivates the service. To do this is pretty simple:
```dart
SpellCheckerServiceProvider.toggleState();
// use isServiceActive to get the state of the service
SpellCheckerServiceProvider.isServiceActive();
setState(() {});
```
Open this [page](https://pub.dev/packages/simple_spell_checker) for more information.
## 🌐 Translation ## 🌐 Translation
The package offers translations for the quill toolbar and editor, it will follow the system locale unless you set your The package offers translations for the quill toolbar and editor, it will follow the system locale unless you set your

@ -4,61 +4,6 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## 10.4.1
* Chore: improve Spell checker API to the example by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2133
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.4.0...v10.4.1
## 10.4.0
* Copy TapAndPanGestureRecognizer from TextField by @demoYang in https://github.com/singerdmx/flutter-quill/pull/2128
* enhance stringToColor with a custom defined palette from `DefaultStyles` by @vishna in https://github.com/singerdmx/flutter-quill/pull/2095
* Feat: include spell checker for example app by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2127
## New Contributors
* @vishna made their first contribution in https://github.com/singerdmx/flutter-quill/pull/2095
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.3.3...v10.4.0
## 10.3.2
* Fix: Loss of style when backspace by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2125
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.3.1...v10.3.2
## 10.3.1
* Chore: Move spellchecker service to extensions by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2120
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.3.0...v10.3.1
## 10.3.0
* Feat: Spellchecker for Flutter Quill by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2118
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.2.1...v10.3.0
## 10.2.1
* Fix: context menu is visible even when selection is collapsed by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2116
* Fix: unsafe operation while getting overlayEntry in text_selection by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2117
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.2.0...v10.2.1
## 10.2.0
* refactor!: restructure project into modular architecture for flutter_quill_extensions by @EchoEllet in https://github.com/singerdmx/flutter-quill/pull/2106
* Fix: Link selection and editing by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2114
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.10...v10.2.0
## 10.1.10 ## 10.1.10
* Fix(example): image_cropper outdated version by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2100 * Fix(example): image_cropper outdated version by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2100

@ -1,6 +1,6 @@
name: dart_quill_delta name: dart_quill_delta
description: A port of quill-js-delta from typescript to dart description: A port of quill-js-delta from typescript to dart
version: 10.4.1 version: 10.1.10
homepage: https://github.com/singerdmx/flutter-quill/tree/master/dart_quill_delta/ homepage: https://github.com/singerdmx/flutter-quill/tree/master/dart_quill_delta/
repository: https://github.com/singerdmx/flutter-quill/tree/master/dart_quill_delta/ repository: https://github.com/singerdmx/flutter-quill/tree/master/dart_quill_delta/
issue_tracker: https://github.com/singerdmx/flutter-quill/issues/ issue_tracker: https://github.com/singerdmx/flutter-quill/issues/

@ -15,22 +15,11 @@ import '../../quill/samples/quill_videos_sample.dart';
import '../../settings/widgets/settings_screen.dart'; import '../../settings/widgets/settings_screen.dart';
import 'example_item.dart'; import 'example_item.dart';
class HomeScreen extends StatefulWidget { class HomeScreen extends StatelessWidget {
const HomeScreen({super.key}); const HomeScreen({super.key});
static const routeName = '/home'; static const routeName = '/home';
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
void dispose() {
SpellCheckerServiceProvider.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(

@ -3,10 +3,7 @@ import 'dart:convert' show jsonEncode;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart'; import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart' import 'package:flutter_quill_extensions/flutter_quill_extensions.dart'
show show FlutterQuillEmbeds, QuillSharedExtensionsConfigurations;
FlutterQuillEmbeds,
FlutterQuillExtensions,
QuillSharedExtensionsConfigurations;
import 'package:share_plus/share_plus.dart' show Share; import 'package:share_plus/share_plus.dart' show Share;
import '../../extensions/scaffold_messenger.dart'; import '../../extensions/scaffold_messenger.dart';
@ -14,8 +11,6 @@ import '../shared/widgets/home_screen_button.dart';
import 'my_quill_editor.dart'; import 'my_quill_editor.dart';
import 'my_quill_toolbar.dart'; import 'my_quill_toolbar.dart';
var _isSpellcheckerActive = false;
@immutable @immutable
class QuillScreenArgs { class QuillScreenArgs {
const QuillScreenArgs({required this.document}); const QuillScreenArgs({required this.document});
@ -61,28 +56,10 @@ class _QuillScreenState extends State<QuillScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
_controller.readOnly = _isReadOnly; _controller.readOnly = _isReadOnly;
if (!_isSpellcheckerActive) {
_isSpellcheckerActive = true;
FlutterQuillExtensions.useSpellCheckerService(
Localizations.localeOf(context).languageCode);
}
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Flutter Quill'), title: const Text('Flutter Quill'),
actions: [ actions: [
IconButton(
tooltip: 'Spell-checker',
onPressed: () {
SpellCheckerServiceProvider.toggleState();
setState(() {});
},
icon: Icon(
Icons.document_scanner,
color: SpellCheckerServiceProvider.isServiceActive()
? Colors.red.withOpacity(0.5)
: null,
),
),
IconButton( IconButton(
tooltip: 'Share', tooltip: 'Share',
onPressed: () { onPressed: () {

@ -4,61 +4,6 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## 10.4.1
* Chore: improve Spell checker API to the example by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2133
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.4.0...v10.4.1
## 10.4.0
* Copy TapAndPanGestureRecognizer from TextField by @demoYang in https://github.com/singerdmx/flutter-quill/pull/2128
* enhance stringToColor with a custom defined palette from `DefaultStyles` by @vishna in https://github.com/singerdmx/flutter-quill/pull/2095
* Feat: include spell checker for example app by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2127
## New Contributors
* @vishna made their first contribution in https://github.com/singerdmx/flutter-quill/pull/2095
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.3.3...v10.4.0
## 10.3.2
* Fix: Loss of style when backspace by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2125
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.3.1...v10.3.2
## 10.3.1
* Chore: Move spellchecker service to extensions by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2120
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.3.0...v10.3.1
## 10.3.0
* Feat: Spellchecker for Flutter Quill by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2118
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.2.1...v10.3.0
## 10.2.1
* Fix: context menu is visible even when selection is collapsed by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2116
* Fix: unsafe operation while getting overlayEntry in text_selection by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2117
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.2.0...v10.2.1
## 10.2.0
* refactor!: restructure project into modular architecture for flutter_quill_extensions by @EchoEllet in https://github.com/singerdmx/flutter-quill/pull/2106
* Fix: Link selection and editing by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2114
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.10...v10.2.0
## 10.1.10 ## 10.1.10
* Fix(example): image_cropper outdated version by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2100 * Fix(example): image_cropper outdated version by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2100

@ -1,12 +1,9 @@
library flutter_quill_extensions; library flutter_quill_extensions;
// ignore: implementation_imports
import 'package:flutter_quill/src/editor/spellchecker/spellchecker_service_provider.dart';
// ignore: implementation_imports // ignore: implementation_imports
import 'package:flutter_quill/src/editor_toolbar_controller_shared/clipboard/clipboard_service_provider.dart'; import 'package:flutter_quill/src/editor_toolbar_controller_shared/clipboard/clipboard_service_provider.dart';
import 'package:meta/meta.dart' show immutable; import 'package:meta/meta.dart' show immutable;
import 'src/editor/spell_checker/simple_spell_checker_service.dart';
import 'src/editor_toolbar_controller_shared/clipboard/super_clipboard_service.dart'; import 'src/editor_toolbar_controller_shared/clipboard/super_clipboard_service.dart';
export 'src/common/extensions/controller_ext.dart'; export 'src/common/extensions/controller_ext.dart';
@ -16,7 +13,6 @@ export 'src/editor/image/image_embed_types.dart';
export 'src/editor/image/image_web_embed.dart'; export 'src/editor/image/image_web_embed.dart';
export 'src/editor/image/models/image_configurations.dart'; export 'src/editor/image/models/image_configurations.dart';
export 'src/editor/image/models/image_web_configurations.dart'; export 'src/editor/image/models/image_web_configurations.dart';
export 'src/editor/spell_checker/simple_spell_checker_service.dart';
export 'src/editor/table/table_cell_embed.dart'; export 'src/editor/table/table_cell_embed.dart';
export 'src/editor/table/table_embed.dart'; export 'src/editor/table/table_embed.dart';
export 'src/editor/table/table_models.dart'; export 'src/editor/table/table_models.dart';
@ -39,29 +35,12 @@ export 'src/toolbar/video/models/video.dart';
export 'src/toolbar/video/models/video_configurations.dart'; export 'src/toolbar/video/models/video_configurations.dart';
export 'src/toolbar/video/video_button.dart'; export 'src/toolbar/video/video_button.dart';
// TODO: Refactor flutter_quill_extensions to match the structure of flutter_quill
@immutable @immutable
class FlutterQuillExtensions { class FlutterQuillExtensions {
const FlutterQuillExtensions._(); const FlutterQuillExtensions._();
/// override the default implementation of [SpellCheckerServiceProvider]
/// to allow a `flutter quill` support a better check spelling
///
/// # !WARNING
/// To avoid memory leaks, ensure to use [dispose()] method to
/// close stream controllers that used by this custom implementation
/// when them no longer needed
///
/// Example:
///
///```dart
///// set partial true if you only need to close the controllers
///SpellCheckerServiceProvider.dispose(onlyPartial: false);
///```
static void useSpellCheckerService(String language) {
SpellCheckerServiceProvider.setNewCheckerService(
SimpleSpellCheckerService(language: language));
}
/// Override default implementation of [ClipboardServiceProvider.instance] /// Override default implementation of [ClipboardServiceProvider.instance]
/// to allow `flutter_quill` package to use `super_clipboard` plugin /// to allow `flutter_quill` package to use `super_clipboard` plugin
/// to support rich text features, gif and images. /// to support rich text features, gif and images.

@ -1,65 +0,0 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:simple_spell_checker/simple_spell_checker.dart';
/// SimpleSpellChecker is a simple spell checker for get
/// all words divide on different objects if them are wrong or not
class SimpleSpellCheckerService
extends SpellCheckerService<LanguageIdentifier> {
SimpleSpellCheckerService({required super.language})
: checker = SimpleSpellChecker(
language: language,
safeDictionaryLoad: true,
);
/// [SimpleSpellChecker] comes from the package [simple_spell_checker]
/// that give us all necessary methods for get our spans with highlighting
/// where needed
final SimpleSpellChecker checker;
@override
List<TextSpan>? checkSpelling(
String text, {
LongPressGestureRecognizer Function(String word)?
customLongPressRecognizerOnWrongSpan,
}) {
return checker.check(
text,
customLongPressRecognizerOnWrongSpan:
customLongPressRecognizerOnWrongSpan,
);
}
@override
void toggleChecker() => checker.toggleChecker();
@override
bool isServiceActive() => checker.isCheckerActive();
@override
void dispose({bool onlyPartial = false}) {
if (onlyPartial) {
checker.disposeControllers();
return;
}
checker.dispose();
}
@override
void addCustomLanguage({required languageIdentifier}) {
checker
..registerLanguage(languageIdentifier.language)
..addCustomLanguage(languageIdentifier);
}
@override
void setNewLanguageState({required String language}) {
checker.setNewLanguageToState(language);
}
@override
void updateCustomLanguageIfExist({required languageIdentifier}) {
checker.updateCustomLanguageIfExist(languageIdentifier);
}
}

@ -1,6 +1,6 @@
name: flutter_quill_extensions name: flutter_quill_extensions
description: Embed extensions for flutter_quill including image, video, formula and etc. description: Embed extensions for flutter_quill including image, video, formula and etc.
version: 10.4.1 version: 10.1.10
homepage: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions/ homepage: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions/
repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions/ repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions/
issue_tracker: https://github.com/singerdmx/flutter-quill/issues/ issue_tracker: https://github.com/singerdmx/flutter-quill/issues/
@ -35,13 +35,12 @@ dependencies:
universal_html: ^2.2.4 universal_html: ^2.2.4
cross_file: ^0.3.3+6 cross_file: ^0.3.3+6
flutter_quill: ^10.3.0 flutter_quill: ^10.0.0
photo_view: ^0.15.0 photo_view: ^0.15.0
youtube_explode_dart: ^2.2.1 youtube_explode_dart: ^2.2.1
# Plugins # Plugins
video_player: ^2.8.1 video_player: ^2.8.1
simple_spell_checker: ^1.1.6
youtube_player_flutter: ^9.0.1 youtube_player_flutter: ^9.0.1
url_launcher: ^6.2.1 url_launcher: ^6.2.1
super_clipboard: ^0.8.15 super_clipboard: ^0.8.15

@ -4,61 +4,6 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## 10.4.1
* Chore: improve Spell checker API to the example by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2133
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.4.0...v10.4.1
## 10.4.0
* Copy TapAndPanGestureRecognizer from TextField by @demoYang in https://github.com/singerdmx/flutter-quill/pull/2128
* enhance stringToColor with a custom defined palette from `DefaultStyles` by @vishna in https://github.com/singerdmx/flutter-quill/pull/2095
* Feat: include spell checker for example app by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2127
## New Contributors
* @vishna made their first contribution in https://github.com/singerdmx/flutter-quill/pull/2095
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.3.3...v10.4.0
## 10.3.2
* Fix: Loss of style when backspace by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2125
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.3.1...v10.3.2
## 10.3.1
* Chore: Move spellchecker service to extensions by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2120
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.3.0...v10.3.1
## 10.3.0
* Feat: Spellchecker for Flutter Quill by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2118
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.2.1...v10.3.0
## 10.2.1
* Fix: context menu is visible even when selection is collapsed by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2116
* Fix: unsafe operation while getting overlayEntry in text_selection by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2117
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.2.0...v10.2.1
## 10.2.0
* refactor!: restructure project into modular architecture for flutter_quill_extensions by @EchoEllet in https://github.com/singerdmx/flutter-quill/pull/2106
* Fix: Link selection and editing by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2114
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.10...v10.2.0
## 10.1.10 ## 10.1.10
* Fix(example): image_cropper outdated version by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2100 * Fix(example): image_cropper outdated version by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2100

@ -1,6 +1,6 @@
name: flutter_quill_test name: flutter_quill_test
description: Test utilities for flutter_quill which includes methods to simplify interacting with the editor in test cases. description: Test utilities for flutter_quill which includes methods to simplify interacting with the editor in test cases.
version: 10.4.1 version: 10.1.10
homepage: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_test/ homepage: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_test/
repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_test/ repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_test/
issue_tracker: https://github.com/singerdmx/flutter-quill/issues/ issue_tracker: https://github.com/singerdmx/flutter-quill/issues/

@ -24,8 +24,6 @@ export 'src/editor/raw_editor/config/raw_editor_configurations.dart';
export 'src/editor/raw_editor/quill_single_child_scroll_view.dart'; export 'src/editor/raw_editor/quill_single_child_scroll_view.dart';
export 'src/editor/raw_editor/raw_editor.dart'; export 'src/editor/raw_editor/raw_editor.dart';
export 'src/editor/raw_editor/raw_editor_state.dart'; export 'src/editor/raw_editor/raw_editor_state.dart';
export 'src/editor/spellchecker/spellchecker_service.dart';
export 'src/editor/spellchecker/spellchecker_service_provider.dart';
export 'src/editor/style_widgets/style_widgets.dart'; export 'src/editor/style_widgets/style_widgets.dart';
export 'src/editor/widgets/cursor.dart'; export 'src/editor/widgets/cursor.dart';
export 'src/editor/widgets/default_styles.dart'; export 'src/editor/widgets/default_styles.dart';

@ -1,17 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../editor/widgets/default_styles.dart'; Color stringToColor(String? s, [Color? originalColor]) {
Color stringToColor(String? s,
[Color? originalColor, DefaultStyles? defaultStyles]) {
final palette = defaultStyles?.palette;
if (s != null && palette != null) {
final maybeColor = palette[s];
if (maybeColor != null) {
return maybeColor;
}
}
switch (s) { switch (s) {
case 'transparent': case 'transparent':
return Colors.transparent; return Colors.transparent;

@ -124,12 +124,6 @@ class QuillController extends ChangeNotifier {
/// It gets reset after each format action within the [document]. /// It gets reset after each format action within the [document].
Style toggledStyle = const Style(); Style toggledStyle = const Style();
/// [raw_editor_actions] handling of backspace event may need to force the style displayed in the toolbar
void forceToggledStyle(Style style) {
toggledStyle = style;
notifyListeners();
}
bool ignoreFocusOnTextChange = false; bool ignoreFocusOnTextChange = false;
/// Skip requestKeyboard being called in /// Skip requestKeyboard being called in

@ -192,12 +192,11 @@ class Document {
while ((res.node as Line).length == 1 && index > 0) { while ((res.node as Line).length == 1 && index > 0) {
res = queryChild(--index); res = queryChild(--index);
} }
// Get inline attributes from previous line (link does not cross line breaks) // Get inline attributes from previous line
final prev = (res.node as Line).collectStyle(res.offset, 0); final prev = (res.node as Line).collectStyle(res.offset, 0);
final attributes = <String, Attribute>{}; final attributes = <String, Attribute>{};
for (final attr in prev.attributes.values) { for (final attr in prev.attributes.values) {
if (attr.scope == AttributeScope.inline && if (attr.scope == AttributeScope.inline) {
attr.key != Attribute.link.key) {
attributes[attr.key] = attr; attributes[attr.key] = attr;
} }
} }
@ -212,15 +211,13 @@ class Document {
// //
final style = (res.node as Line).collectStyle(res.offset - 1, 0); final style = (res.node as Line).collectStyle(res.offset - 1, 0);
final linkAttribute = style.attributes[Attribute.link.key]; final linkAttribute = style.attributes[Attribute.link.key];
if (linkAttribute != null) { if ((linkAttribute != null) &&
if ((res.node!.length - 1 == res.offset) || (linkAttribute.value !=
(linkAttribute.value != (res.node as Line)
(res.node as Line) .collectStyle(res.offset, len)
.collectStyle(res.offset, len) .attributes[Attribute.link.key]
.attributes[Attribute.link.key] ?.value)) {
?.value)) { return style.removeAll({linkAttribute});
return style.removeAll({linkAttribute});
}
} }
return style; return style;
} }

@ -383,34 +383,15 @@ base class Line extends QuillContainer<Leaf?> {
pos += node.length; pos += node.length;
} }
} }
result = result.mergeAll(style);
/// Blank lines do not have style and must get the active style from prior line
if (isEmpty) {
var prevLine = previous;
while (prevLine is Block && prevLine.isNotEmpty) {
prevLine = prevLine.children.last;
}
if (prevLine is Line) {
result = result.mergeAll(prevLine.collectStyle(prevLine.length - 1, 1));
}
} else {
result = result.mergeAll(style);
}
if (parent is Block) { if (parent is Block) {
final block = parent as Block; final block = parent as Block;
result = result.mergeAll(block.style); result = result.mergeAll(block.style);
} }
var remaining = len - local; final remaining = len - local;
var nxt = nextLine; if (remaining > 0 && nextLine != null) {
final rest = nextLine!.collectStyle(0, remaining);
/// Skip over empty lines that have no attributes
while (remaining > 0 && nxt != null && nxt.isEmpty) {
remaining--;
nxt = nxt.nextLine;
}
if (remaining > 0 && nxt != null) {
final rest = nxt.collectStyle(0, remaining);
handle(rest); handle(rest);
} }

@ -1,6 +1,5 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart' show Brightness, Uint8List, immutable; import 'package:flutter/foundation.dart' show Brightness, Uint8List, immutable;
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart' import 'package:flutter/material.dart'
show TextCapitalization, TextInputAction, TextSelectionThemeData; show TextCapitalization, TextInputAction, TextSelectionThemeData;
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@ -259,12 +258,11 @@ class QuillEditorConfigurations extends Equatable {
// Returns whether gesture is handled // Returns whether gesture is handled
final bool Function( final bool Function(
TapDragDownDetails details, TextPosition Function(Offset offset))? TapDownDetails details, TextPosition Function(Offset offset))? onTapDown;
onTapDown;
// Returns whether gesture is handled // Returns whether gesture is handled
final bool Function( final bool Function(
TapDragUpDetails details, TextPosition Function(Offset offset))? onTapUp; TapUpDetails details, TextPosition Function(Offset offset))? onTapUp;
// Returns whether gesture is handled // Returns whether gesture is handled
final bool Function( final bool Function(

@ -4,13 +4,7 @@ import 'package:flutter/cupertino.dart'
show CupertinoTheme, cupertinoTextSelectionControls; show CupertinoTheme, cupertinoTextSelectionControls;
import 'package:flutter/foundation.dart' import 'package:flutter/foundation.dart'
show ValueListenable, defaultTargetPlatform; show ValueListenable, defaultTargetPlatform;
import 'package:flutter/gestures.dart' import 'package:flutter/gestures.dart' show PointerDeviceKind;
show
PointerDeviceKind,
TapDragDownDetails,
TapDragEndDetails,
TapDragStartDetails,
TapDragUpDetails;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -494,7 +488,7 @@ class _QuillEditorSelectionGestureDetectorBuilder
editor?.updateMagnifier(details.globalPosition); editor?.updateMagnifier(details.globalPosition);
} }
bool _isPositionSelected(TapDragUpDetails details) { bool _isPositionSelected(TapUpDetails details) {
if (_state.controller.document.isEmpty()) { if (_state.controller.document.isEmpty()) {
return false; return false;
} }
@ -517,7 +511,7 @@ class _QuillEditorSelectionGestureDetectorBuilder
} }
@override @override
void onTapDown(TapDragDownDetails details) { void onTapDown(TapDownDetails details) {
if (_state.configurations.onTapDown != null) { if (_state.configurations.onTapDown != null) {
if (renderEditor != null && if (renderEditor != null &&
_state.configurations.onTapDown!( _state.configurations.onTapDown!(
@ -538,7 +532,7 @@ class _QuillEditorSelectionGestureDetectorBuilder
} }
@override @override
void onSingleTapUp(TapDragUpDetails details) { void onSingleTapUp(TapUpDetails details) {
if (_state.configurations.onTapUp != null && if (_state.configurations.onTapUp != null &&
renderEditor != null && renderEditor != null &&
_state.configurations.onTapUp!( _state.configurations.onTapUp!(
@ -744,7 +738,6 @@ class RenderEditor extends RenderEditableContainerBox
Document document; Document document;
TextSelection selection; TextSelection selection;
bool _hasFocus = false; bool _hasFocus = false;
bool get hasFocus => _hasFocus;
LayerLink _startHandleLayerLink; LayerLink _startHandleLayerLink;
LayerLink _endHandleLayerLink; LayerLink _endHandleLayerLink;
@ -918,22 +911,11 @@ class RenderEditor extends RenderEditableContainerBox
final extentNode = _container.queryChild(textSelection.end, false).node; final extentNode = _container.queryChild(textSelection.end, false).node;
RenderEditableBox? extentChild = baseChild; RenderEditableBox? extentChild = baseChild;
while (extentChild != null) {
/// Trap shortening the text of a link which can cause selection to extend off end of line if (extentChild.container == extentNode) {
if (extentNode == null) { break;
while (true) {
final next = childAfter(extentChild);
if (next == null) {
break;
}
}
} else {
while (extentChild != null) {
if (extentChild.container == extentNode) {
break;
}
extentChild = childAfter(extentChild);
} }
extentChild = childAfter(extentChild);
} }
assert(extentChild != null); assert(extentChild != null);
@ -951,20 +933,12 @@ class RenderEditor extends RenderEditableContainerBox
} }
Offset? _lastTapDownPosition; Offset? _lastTapDownPosition;
Offset? _lastSecondaryTapDownPosition;
Offset? get lastSecondaryTapDownPosition => _lastSecondaryTapDownPosition;
// Used on Desktop (mouse and keyboard enabled platforms) as base offset // Used on Desktop (mouse and keyboard enabled platforms) as base offset
// for extending selection, either with combination of `Shift` + Click or // for extending selection, either with combination of `Shift` + Click or
// by dragging // by dragging
TextSelection? _extendSelectionOrigin; TextSelection? _extendSelectionOrigin;
void handleSecondaryTapDown(TapDownDetails details) {
_lastTapDownPosition = details.globalPosition;
_lastSecondaryTapDownPosition = details.globalPosition;
}
@override @override
void handleTapDown(TapDownDetails details) { void handleTapDown(TapDownDetails details) {
_lastTapDownPosition = details.globalPosition; _lastTapDownPosition = details.globalPosition;
@ -972,7 +946,7 @@ class RenderEditor extends RenderEditableContainerBox
bool _isDragging = false; bool _isDragging = false;
void handleDragStart(TapDragStartDetails details) { void handleDragStart(DragStartDetails details) {
_isDragging = true; _isDragging = true;
final newSelection = selectPositionAt( final newSelection = selectPositionAt(
@ -985,7 +959,7 @@ class RenderEditor extends RenderEditableContainerBox
_extendSelectionOrigin = newSelection; _extendSelectionOrigin = newSelection;
} }
void handleDragEnd(TapDragEndDetails details) { void handleDragEnd(DragEndDetails details) {
_isDragging = false; _isDragging = false;
onSelectionCompleted(); onSelectionCompleted();
} }

@ -100,6 +100,4 @@ abstract class EditorState extends State<QuillRawEditor>
void updateMagnifier(Offset positionToShow); void updateMagnifier(Offset positionToShow);
void hideMagnifier(); void hideMagnifier();
void toggleToolbar([bool hideHandles = true]);
} }

@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import '../../../translations.dart'; import '../../../translations.dart';
import '../../document/attribute.dart'; import '../../document/attribute.dart';
import '../../document/style.dart';
import '../../toolbar/buttons/link_style2_button.dart'; import '../../toolbar/buttons/link_style2_button.dart';
import '../../toolbar/buttons/search/search_dialog.dart'; import '../../toolbar/buttons/search/search_dialog.dart';
import '../editor.dart'; import '../editor.dart';
@ -39,72 +38,42 @@ class QuillEditorDeleteTextAction<T extends DirectionalTextEditingIntent>
final selection = state.textEditingValue.selection; final selection = state.textEditingValue.selection;
assert(selection.isValid); assert(selection.isValid);
Object? execute() { if (!selection.isCollapsed) {
if (!selection.isCollapsed) {
return Actions.invoke(
context!,
ReplaceTextIntent(
state.textEditingValue,
'',
_expandNonCollapsedRange(state.textEditingValue),
SelectionChangedCause.keyboard),
);
}
final textBoundary = getTextBoundariesForIntent(intent);
if (!textBoundary.textEditingValue.selection.isValid) {
return null;
}
if (!textBoundary.textEditingValue.selection.isCollapsed) {
return Actions.invoke(
context!,
ReplaceTextIntent(
state.textEditingValue,
'',
_expandNonCollapsedRange(textBoundary.textEditingValue),
SelectionChangedCause.keyboard),
);
}
return Actions.invoke( return Actions.invoke(
context!, context!,
ReplaceTextIntent( ReplaceTextIntent(
textBoundary.textEditingValue, state.textEditingValue,
'', '',
textBoundary _expandNonCollapsedRange(state.textEditingValue),
.getTextBoundaryAt(textBoundary.textEditingValue.selection.base), SelectionChangedCause.keyboard),
SelectionChangedCause.keyboard,
),
); );
} }
/// Backspace event needs to 'remember' the style of the deleted text. final textBoundary = getTextBoundariesForIntent(intent);
/// Example: enter styled text, backspace to erase and reenter - expects to use the same style and not reset to default. if (!textBoundary.textEditingValue.selection.isValid) {
/// Also must handle situations where text is selected and deleted by backspace. return null;
/// Note: This implementation is the same as that used by word processors.
/// Backspace events are handled differently from selection replacement or using the delete key.
Style? postStyle;
if (!intent.forward) {
final start = selection.start + (selection.isCollapsed ? 0 : 1);
var target = state.controller.document.collectStyle(start, 0);
if (start > 0) {
final style = state.controller.document.collectStyle(start - 1, 0);
for (final key in style.attributes.keys) {
if (Attribute.inlineKeys.contains(key)) {
if (!target.containsKey(key)) {
target = target.put(Attribute(key, AttributeScope.inline, null));
}
}
}
}
postStyle = target;
} }
// if (!textBoundary.textEditingValue.selection.isCollapsed) {
final result = execute(); return Actions.invoke(
if (postStyle != null) { context!,
state.controller.forceToggledStyle(postStyle); ReplaceTextIntent(
state.textEditingValue,
'',
_expandNonCollapsedRange(textBoundary.textEditingValue),
SelectionChangedCause.keyboard),
);
} }
return result;
return Actions.invoke(
context!,
ReplaceTextIntent(
textBoundary.textEditingValue,
'',
textBoundary
.getTextBoundaryAt(textBoundary.textEditingValue.selection.base),
SelectionChangedCause.keyboard,
),
);
} }
@override @override

@ -906,7 +906,7 @@ class QuillRawEditorState extends EditorState
_selectionOverlay?.handlesVisible = _shouldShowSelectionHandles(); _selectionOverlay?.handlesVisible = _shouldShowSelectionHandles();
_selectionOverlay?.showHandles(); _selectionOverlay?.showHandles();
if (!_hasFocus) { if (!_keyboardVisible) {
// This will show the keyboard for all selection changes on the // This will show the keyboard for all selection changes on the
// editor, not just changes triggered by user gestures. // editor, not just changes triggered by user gestures.
requestKeyboard(); requestKeyboard();
@ -1421,9 +1421,6 @@ class QuillRawEditorState extends EditorState
if (_selectionOverlay != null) { if (_selectionOverlay != null) {
if (_hasFocus) { if (_hasFocus) {
_selectionOverlay!.update(textEditingValue); _selectionOverlay!.update(textEditingValue);
} else {
_selectionOverlay!.dispose();
_selectionOverlay = null;
} }
} else if (_hasFocus) { } else if (_hasFocus) {
_selectionOverlay = _createSelectionOverlay(); _selectionOverlay = _createSelectionOverlay();
@ -1601,16 +1598,6 @@ class QuillRawEditorState extends EditorState
return true; return true;
} }
@override
void toggleToolbar([bool hideHandles = true]) {
final selectionOverlay = _selectionOverlay ??= _createSelectionOverlay();
if (selectionOverlay.handlesVisible) {
hideToolbar(hideHandles);
} else {
showToolbar();
}
}
void _replaceText(ReplaceTextIntent intent) { void _replaceText(ReplaceTextIntent intent) {
userUpdateTextEditingValue( userUpdateTextEditingValue(
intent.currentTextEditingValue intent.currentTextEditingValue
@ -1845,19 +1832,15 @@ class QuillRawEditorState extends EditorState
@override @override
void showMagnifier(ui.Offset positionToShow) { void showMagnifier(ui.Offset positionToShow) {
if (_hasFocus == false) return;
if (_selectionOverlay == null) return; if (_selectionOverlay == null) return;
final position = renderEditor.getPositionForOffset(positionToShow); final position = renderEditor.getPositionForOffset(positionToShow);
if (_selectionOverlay!.magnifierIsVisible) { _selectionOverlay?.showMagnifier(position, positionToShow, renderEditor);
_selectionOverlay!
.updateMagnifier(position, positionToShow, renderEditor);
} else {
_selectionOverlay!.showMagnifier(position, positionToShow, renderEditor);
}
} }
@override @override
void updateMagnifier(ui.Offset positionToShow) { void updateMagnifier(ui.Offset positionToShow) {
showMagnifier(positionToShow); _updateOrDisposeSelectionOverlayIfNeeded();
final position = renderEditor.getPositionForOffset(positionToShow);
_selectionOverlay?.updateMagnifier(position, positionToShow, renderEditor);
} }
} }

@ -79,19 +79,6 @@ mixin RawEditorStateTextInputClientMixin on EditorState
_updateComposingRectIfNeeded(); _updateComposingRectIfNeeded();
//update IME position for Macos //update IME position for Macos
_updateCaretRectIfNeeded(); _updateCaretRectIfNeeded();
/// Trap selection extends off end of document
if (_lastKnownRemoteTextEditingValue != null) {
if (_lastKnownRemoteTextEditingValue!.selection.end >
_lastKnownRemoteTextEditingValue!.text.length) {
_lastKnownRemoteTextEditingValue = _lastKnownRemoteTextEditingValue!
.copyWith(
selection: _lastKnownRemoteTextEditingValue!.selection
.copyWith(
extentOffset:
_lastKnownRemoteTextEditingValue!.text.length));
}
}
_textInputConnection!.setEditingState(_lastKnownRemoteTextEditingValue!); _textInputConnection!.setEditingState(_lastKnownRemoteTextEditingValue!);
} }
_textInputConnection!.show(); _textInputConnection!.show();

@ -1,37 +0,0 @@
import 'package:flutter/gestures.dart' show LongPressGestureRecognizer;
import 'package:flutter/material.dart' show TextSpan;
import 'spellchecker_service.dart' show SpellCheckerService;
/// A default implementation of the [SpellcheckerService]
/// that always will return null since Spell checking
/// is not a standard feature
class DefaultSpellCheckerService extends SpellCheckerService<Object?> {
DefaultSpellCheckerService() : super(language: 'en');
@override
void dispose({bool onlyPartial = false}) {}
@override
List<TextSpan>? checkSpelling(
String text, {
LongPressGestureRecognizer Function(String p1)?
customLongPressRecognizerOnWrongSpan,
}) {
return null;
}
@override
void addCustomLanguage({languageIdentifier}) {}
@override
void setNewLanguageState({required String language}) {}
@override
void updateCustomLanguageIfExist({languageIdentifier}) {}
@override
bool isServiceActive() => false;
@override
void toggleChecker() {}
}

@ -1,39 +0,0 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
/// A representation a custom SpellCheckService.
abstract class SpellCheckerService<T> {
SpellCheckerService({required this.language});
final String language;
/// Decide if the service should be activate or deactivate
/// without dispose the service
void toggleChecker();
bool isServiceActive();
/// dispose all the resources used for SpellcheckerService
///
/// if [onlyPartial] is true just dispose a part of the SpellcheckerService
/// (this comes from the implementation)
///
/// if [onlyPartial] is false dispose all resources
void dispose({bool onlyPartial = false});
/// set a new language state used for SpellcheckerService
void setNewLanguageState({required String language});
/// set a new language state used for SpellcheckerService
void updateCustomLanguageIfExist({required T languageIdentifier});
/// set a new custom language for SpellcheckerService
void addCustomLanguage({required T languageIdentifier});
/// Facilitates a spell check request.
///
/// Returns a [List<TextSpan>] with all misspelled words divide from the right words.
List<TextSpan>? checkSpelling(String text,
{LongPressGestureRecognizer Function(String)?
customLongPressRecognizerOnWrongSpan});
}

@ -1,36 +0,0 @@
import 'package:flutter/foundation.dart' show immutable;
import 'default_spellchecker_service.dart';
import 'spellchecker_service.dart';
@immutable
class SpellCheckerServiceProvider {
const SpellCheckerServiceProvider._();
static SpellCheckerService _instance = DefaultSpellCheckerService();
static SpellCheckerService get instance => _instance;
static void setNewCheckerService(SpellCheckerService service) {
_instance = service;
}
static void dispose({bool onlyPartial = false}) {
_instance.dispose(onlyPartial: onlyPartial);
}
static void toggleState() {
_instance.toggleChecker();
}
static bool isServiceActive() {
return _instance.isServiceActive();
}
static void setNewLanguageState({required String language}) {
assert(language.isNotEmpty);
_instance.setNewLanguageState(language: language);
}
static void turnOffService() {
_instance = DefaultSpellCheckerService();
}
}

@ -200,7 +200,6 @@ class DefaultStyles {
this.sizeSmall, this.sizeSmall,
this.sizeLarge, this.sizeLarge,
this.sizeHuge, this.sizeHuge,
this.palette,
}); });
final DefaultTextBlockStyle? h1; final DefaultTextBlockStyle? h1;
@ -237,9 +236,6 @@ class DefaultStyles {
final DefaultTextBlockStyle? align; final DefaultTextBlockStyle? align;
final DefaultTextBlockStyle? leading; final DefaultTextBlockStyle? leading;
/// Custom palette of colors
final Map<String, Color>? palette;
static DefaultStyles getInstance(BuildContext context) { static DefaultStyles getInstance(BuildContext context) {
final themeData = Theme.of(context); final themeData = Theme.of(context);
final defaultTextStyle = DefaultTextStyle.of(context); final defaultTextStyle = DefaultTextStyle.of(context);
@ -522,7 +518,6 @@ class DefaultStyles {
sizeSmall: other.sizeSmall ?? sizeSmall, sizeSmall: other.sizeSmall ?? sizeSmall,
sizeLarge: other.sizeLarge ?? sizeLarge, sizeLarge: other.sizeLarge ?? sizeLarge,
sizeHuge: other.sizeHuge ?? sizeHuge, sizeHuge: other.sizeHuge ?? sizeHuge,
palette: other.palette ?? palette,
); );
} }
} }

@ -1,9 +1,7 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import '../../common/utils/platform.dart'; import '../../common/utils/platform.dart';
import '../../document/attribute.dart'; import '../../document/attribute.dart';
@ -109,138 +107,6 @@ class EditorTextSelectionGestureDetectorBuilder {
@protected @protected
RenderEditor? get renderEditor => editor?.renderEditor; RenderEditor? get renderEditor => editor?.renderEditor;
/// Whether the Shift key was pressed when the most recent [PointerDownEvent]
/// was tracked by the [BaseTapAndDragGestureRecognizer].
bool _isShiftPressed = false;
/// The viewport offset pixels of any [Scrollable] containing the
/// [RenderEditable] at the last drag start.
double _dragStartScrollOffset = 0;
/// The viewport offset pixels of the [RenderEditable] at the last drag start.
double _dragStartViewportOffset = 0;
double get _scrollPosition {
final scrollableState = delegate.editableTextKey.currentContext == null
? null
: Scrollable.maybeOf(delegate.editableTextKey.currentContext!);
return scrollableState == null ? 0.0 : scrollableState.position.pixels;
}
// For tap + drag gesture on iOS, whether the position where the drag started
// was on the previous TextSelection. iOS uses this value to determine if
// the cursor should move on drag update.
//
TextSelection? _dragStartSelection;
// If the drag started on the previous selection then the cursor will move on
// drag update. If the drag did not start on the previous selection then the
// cursor will not move on drag update.
bool? _dragBeganOnPreviousSelection;
/// Returns true if lastSecondaryTapDownPosition was on selection.
bool get _lastSecondaryTapWasOnSelection {
assert(renderEditor?.lastSecondaryTapDownPosition != null);
if (renderEditor?.selection == null) {
return false;
}
renderEditor?.lastSecondaryTapDownPosition;
final textPosition = renderEditor?.getPositionForOffset(
renderEditor!.lastSecondaryTapDownPosition!,
);
if (textPosition == null) return false;
return renderEditor!.selection.start <= textPosition.offset &&
renderEditor!.selection.end >= textPosition.offset;
}
/// Returns true if position was on selection.
bool _positionOnSelection(Offset position, TextSelection? targetSelection) {
if (targetSelection == null) return false;
final textPosition = renderEditor?.getPositionForOffset(position);
if (textPosition == null) return false;
return targetSelection.start <= textPosition.offset &&
targetSelection.end >= textPosition.offset;
}
// Expand the selection to the given global position.
//
// Either base or extent will be moved to the last tapped position, whichever
// is closest. The selection will never shrink or pivot, only grow.
//
// If fromSelection is given, will expand from that selection instead of the
// current selection in renderEditable.
//
// See also:
//
// * [_extendSelection], which is similar but pivots the selection around
// the base.
void _expandSelection(Offset offset, SelectionChangedCause cause,
[TextSelection? fromSelection]) {
final tappedPosition = renderEditor!.getPositionForOffset(offset);
final selection = fromSelection ?? renderEditor!.selection;
final baseIsCloser = (tappedPosition.offset - selection.baseOffset).abs() <
(tappedPosition.offset - selection.extentOffset).abs();
final nextSelection = selection.copyWith(
baseOffset: baseIsCloser ? selection.extentOffset : selection.baseOffset,
extentOffset: tappedPosition.offset,
);
editor?.userUpdateTextEditingValue(
editor!.textEditingValue.copyWith(selection: nextSelection), cause);
}
// Extend the selection to the given global position.
//
// Holds the base in place and moves the extent.
//
// See also:
//
// * [_expandSelection], which is similar but always increases the size of
// the selection.
void _extendSelection(Offset offset, SelectionChangedCause cause) {
assert(renderEditor?.selection.baseOffset != null);
final tappedPosition = renderEditor!.getPositionForOffset(offset);
final selection = renderEditor!.selection;
final nextSelection = selection.copyWith(
extentOffset: tappedPosition.offset,
);
editor?.userUpdateTextEditingValue(
editor!.textEditingValue.copyWith(selection: nextSelection), cause);
}
/// Handler for [TextSelectionGestureDetector.onTapTrackStart].
///
/// See also:
///
/// * [TextSelectionGestureDetector.onTapTrackStart], which triggers this
/// callback.
@protected
void onTapTrackStart() {
_isShiftPressed = HardwareKeyboard.instance.logicalKeysPressed
.intersection(<LogicalKeyboardKey>{
LogicalKeyboardKey.shiftLeft,
LogicalKeyboardKey.shiftRight
}).isNotEmpty;
}
/// Handler for [TextSelectionGestureDetector.onTapTrackReset].
///
/// See also:
///
/// * [TextSelectionGestureDetector.onTapTrackReset], which triggers this
/// callback.
@protected
void onTapTrackReset() {
_isShiftPressed = false;
}
/// Handler for [EditorTextSelectionGestureDetector.onTapDown]. /// Handler for [EditorTextSelectionGestureDetector.onTapDown].
/// ///
/// By default, it forwards the tap to [RenderEditable.handleTapDown] and sets /// By default, it forwards the tap to [RenderEditable.handleTapDown] and sets
@ -252,45 +118,19 @@ class EditorTextSelectionGestureDetectorBuilder {
/// * [EditorTextSelectionGestureDetector.onTapDown], /// * [EditorTextSelectionGestureDetector.onTapDown],
/// which triggers this callback. /// which triggers this callback.
@protected @protected
void onTapDown(TapDragDownDetails details) { void onTapDown(TapDownDetails details) {
if (!delegate.selectionEnabled) return; renderEditor!.handleTapDown(details);
renderEditor! // The selection overlay should only be shown when the user is interacting
.handleTapDown(TapDownDetails(globalPosition: details.globalPosition)); // through a touch screen (via either a finger or a stylus).
final kind = details.kind; // A mouse shouldn't trigger the selection overlay.
// For backwards-compatibility, we treat a null kind the same as touch.
kind = details.kind;
shouldShowSelectionToolbar = kind == null || shouldShowSelectionToolbar = kind == null ||
kind ==
PointerDeviceKind
.mouse || // Enable word selection by mouse double tap
kind == PointerDeviceKind.touch || kind == PointerDeviceKind.touch ||
kind == PointerDeviceKind.stylus; kind == PointerDeviceKind.stylus;
final isShiftPressedValid =
_isShiftPressed && renderEditor?.selection.baseOffset != null;
switch (defaultTargetPlatform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
editor?.hideToolbar(false);
case TargetPlatform.iOS:
// On mobile platforms the selection is set on tap up.
break;
case TargetPlatform.macOS:
editor?.hideToolbar();
// On macOS, a shift-tapped unfocused field expands from 0, not from the
// previous selection.
if (isShiftPressedValid) {
final fromSelection = renderEditor?.hasFocus == true
? null
: const TextSelection.collapsed(offset: 0);
_expandSelection(
details.globalPosition, SelectionChangedCause.tap, fromSelection);
return;
}
renderEditor?.selectPosition(cause: SelectionChangedCause.tap);
case TargetPlatform.linux:
case TargetPlatform.windows:
editor?.hideToolbar();
if (isShiftPressedValid) {
_extendSelection(details.globalPosition, SelectionChangedCause.tap);
return;
}
renderEditor?.selectPosition(cause: SelectionChangedCause.tap);
}
} }
/// Handler for [EditorTextSelectionGestureDetector.onForcePressStart]. /// Handler for [EditorTextSelectionGestureDetector.onForcePressStart].
@ -341,27 +181,6 @@ class EditorTextSelectionGestureDetectorBuilder {
} }
} }
/// Whether the provided [onUserTap] callback should be dispatched on every
/// tap or only non-consecutive taps.
///
/// Defaults to false.
@protected
bool get onUserTapAlwaysCalled => false;
/// Handler for [TextSelectionGestureDetector.onUserTap].
///
/// By default, it serves as placeholder to enable subclass override.
///
/// See also:
///
/// * [TextSelectionGestureDetector.onUserTap], which triggers this
/// callback.
/// * [TextSelectionGestureDetector.onUserTapAlwaysCalled], which controls
/// whether this callback is called only on the first tap in a series
/// of taps.
@protected
void onUserTap() {/* Subclass should override this method if needed. */}
/// Handler for [EditorTextSelectionGestureDetector.onSingleTapUp]. /// Handler for [EditorTextSelectionGestureDetector.onSingleTapUp].
/// ///
/// By default, it selects word edge if selection is enabled. /// By default, it selects word edge if selection is enabled.
@ -371,7 +190,7 @@ class EditorTextSelectionGestureDetectorBuilder {
/// * [EditorTextSelectionGestureDetector.onSingleTapUp], which triggers /// * [EditorTextSelectionGestureDetector.onSingleTapUp], which triggers
/// this callback. /// this callback.
@protected @protected
void onSingleTapUp(TapDragUpDetails details) { void onSingleTapUp(TapUpDetails details) {
if (delegate.selectionEnabled) { if (delegate.selectionEnabled) {
renderEditor!.selectWordEdge(SelectionChangedCause.tap); renderEditor!.selectWordEdge(SelectionChangedCause.tap);
} }
@ -450,64 +269,6 @@ class EditorTextSelectionGestureDetectorBuilder {
if (checkSelectionToolbarShouldShow(isAdditionalAction: false)) { if (checkSelectionToolbarShouldShow(isAdditionalAction: false)) {
editor!.showToolbar(); editor!.showToolbar();
} }
// Q: why ?
// A: cannot access QuillRawEditorState.updateFloatingCursor
//
// if (defaultTargetPlatform == TargetPlatform.iOS &&
// delegate.selectionEnabled &&
// editor?.textEditingValue.selection.isCollapsed == true) {
// // Update the floating cursor.
// final cursorPoint =
// RawFloatingCursorPoint(state: FloatingCursorDragState.End);
// // !.updateFloatingCursor(cursorPoint);
// (editor as QuillRawEditorState?)?.updateFloatingCursor(cursorPoint);
// }
}
/// Handler for [TextSelectionGestureDetector.onSecondaryTap].
///
/// By default, selects the word if possible and shows the toolbar.
@protected
void onSecondaryTap() {
if (!delegate.selectionEnabled) {
return;
}
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
if (!_lastSecondaryTapWasOnSelection ||
renderEditor?.hasFocus == false) {
renderEditor?.selectWord(SelectionChangedCause.tap);
}
if (shouldShowSelectionToolbar) {
editor?.hideToolbar();
editor?.showToolbar();
}
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
if (renderEditor?.hasFocus == false) {
renderEditor?.selectPosition(cause: SelectionChangedCause.tap);
}
editor?.toggleToolbar();
}
}
/// Handler for [TextSelectionGestureDetector.onDoubleTapDown].
///
/// By default, it selects a word through [RenderEditable.selectWord] if
/// selectionEnabled and shows toolbar if necessary.
///
/// See also:
///
/// * [TextSelectionGestureDetector.onDoubleTapDown], which triggers this
/// callback.
@protected
void onSecondaryTapDown(TapDownDetails details) {
renderEditor?.handleSecondaryTapDown(
TapDownDetails(globalPosition: details.globalPosition));
shouldShowSelectionToolbar = true;
} }
/// Handler for [EditorTextSelectionGestureDetector.onDoubleTapDown]. /// Handler for [EditorTextSelectionGestureDetector.onDoubleTapDown].
@ -520,7 +281,7 @@ class EditorTextSelectionGestureDetectorBuilder {
/// * [EditorTextSelectionGestureDetector.onDoubleTapDown], /// * [EditorTextSelectionGestureDetector.onDoubleTapDown],
/// which triggers this callback. /// which triggers this callback.
@protected @protected
void onDoubleTapDown(TapDragDownDetails details) { void onDoubleTapDown(TapDownDetails details) {
if (delegate.selectionEnabled) { if (delegate.selectionEnabled) {
renderEditor!.selectWord(SelectionChangedCause.tap); renderEditor!.selectWord(SelectionChangedCause.tap);
// allow the selection to get updated before trying to bring up // allow the selection to get updated before trying to bring up
@ -537,105 +298,6 @@ class EditorTextSelectionGestureDetectorBuilder {
} }
} }
// Selects the set of paragraphs in a document that intersect a given range of
// global positions.
void _selectParagraphsInRange(
{required Offset from, Offset? to, SelectionChangedCause? cause}) {
final TextBoundary paragraphBoundary =
ParagraphBoundary(editor!.textEditingValue.text);
_selectTextBoundariesInRange(
boundary: paragraphBoundary, from: from, to: to, cause: cause);
}
// Selects the set of lines in a document that intersect a given range of
// global positions.
void _selectLinesInRange(
{required Offset from, Offset? to, SelectionChangedCause? cause}) {
final TextBoundary lineBoundary = LineBoundary(renderEditor!);
_selectTextBoundariesInRange(
boundary: lineBoundary, from: from, to: to, cause: cause);
}
// Returns the location of a text boundary at `extent`. When `extent` is at
// the end of the text, returns the previous text boundary's location.
TextRange _moveToTextBoundary(
TextPosition extent, TextBoundary textBoundary) {
assert(extent.offset >= 0);
final start = textBoundary.getLeadingTextBoundaryAt(
extent.offset == editor!.textEditingValue.text.length
? extent.offset - 1
: extent.offset) ??
0;
final end = textBoundary.getTrailingTextBoundaryAt(extent.offset) ??
editor!.textEditingValue.text.length;
return TextRange(start: start, end: end);
}
// Selects the set of text boundaries in a document that intersect a given
// range of global positions.
//
// The set of text boundaries selected are not strictly bounded by the range
// of global positions.
//
// The first and last endpoints of the selection will always be at the
// beginning and end of a text boundary respectively.
void _selectTextBoundariesInRange(
{required TextBoundary boundary,
required Offset from,
Offset? to,
SelectionChangedCause? cause}) {
final fromPosition = renderEditor!.getPositionForOffset(from);
final fromRange = _moveToTextBoundary(fromPosition, boundary);
final toPosition =
to == null ? fromPosition : renderEditor!.getPositionForOffset(to);
final toRange = toPosition == fromPosition
? fromRange
: _moveToTextBoundary(toPosition, boundary);
final isFromBoundaryBeforeToBoundary = fromRange.start < toRange.end;
final newSelection = isFromBoundaryBeforeToBoundary
? TextSelection(baseOffset: fromRange.start, extentOffset: toRange.end)
: TextSelection(baseOffset: fromRange.end, extentOffset: toRange.start);
editor!.userUpdateTextEditingValue(
editor!.textEditingValue.copyWith(selection: newSelection),
cause ?? SelectionChangedCause.drag);
}
/// Handler for [TextSelectionGestureDetector.onTripleTapDown].
///
/// By default, it selects a paragraph if
/// [TextSelectionGestureDetectorBuilderDelegate.selectionEnabled] is true
/// and shows the toolbar if necessary.
///
/// See also:
///
/// * [TextSelectionGestureDetector.onTripleTapDown], which triggers this
/// callback.
@protected
void onTripleTapDown(TapDragDownDetails details) {
if (!delegate.selectionEnabled) {
return;
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.iOS:
case TargetPlatform.macOS:
case TargetPlatform.windows:
_selectParagraphsInRange(
from: details.globalPosition, cause: SelectionChangedCause.tap);
case TargetPlatform.linux:
_selectLinesInRange(
from: details.globalPosition, cause: SelectionChangedCause.tap);
}
if (shouldShowSelectionToolbar) {
editor?.showToolbar();
}
}
/// Handler for [EditorTextSelectionGestureDetector.onDragSelectionStart]. /// Handler for [EditorTextSelectionGestureDetector.onDragSelectionStart].
/// ///
/// By default, it selects a text position specified in [details]. /// By default, it selects a text position specified in [details].
@ -645,106 +307,8 @@ class EditorTextSelectionGestureDetectorBuilder {
/// * [EditorTextSelectionGestureDetector.onDragSelectionStart], /// * [EditorTextSelectionGestureDetector.onDragSelectionStart],
/// which triggers this callback. /// which triggers this callback.
@protected @protected
void onDragSelectionStart(TapDragStartDetails details) { void onDragSelectionStart(DragStartDetails details) {
if (delegate.selectionEnabled == false) return; renderEditor!.handleDragStart(details);
// underline show open on ios and android,
// when has isCollapsed, show not reposonse to tapdarg gesture
// so that will not change texteditingvalue,
// and same issue to TextField, tap selection area, will lost selection,
// if (editor?.textEditingValue.selection.isCollapsed == false) return;
final kind = details.kind;
shouldShowSelectionToolbar = kind == null ||
kind == PointerDeviceKind.touch ||
kind == PointerDeviceKind.stylus;
_dragStartSelection = renderEditor?.selection;
_dragStartScrollOffset = _scrollPosition;
_dragStartViewportOffset = renderEditor?.offset?.pixels ?? 0.0;
_dragBeganOnPreviousSelection =
_positionOnSelection(details.globalPosition, _dragStartSelection);
if (EditorTextSelectionGestureDetector.getEffectiveConsecutiveTapCount(
details.consecutiveTapCount) >
1) {
// Do not set the selection on a consecutive tap and drag.
return;
}
if (_isShiftPressed &&
renderEditor?.selection != null &&
renderEditor?.selection.isValid == true) {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
renderEditor?.extendSelection(details.globalPosition,
cause: SelectionChangedCause.drag);
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
renderEditor?.extendSelection(details.globalPosition,
cause: SelectionChangedCause.drag);
}
} else {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
switch (details.kind) {
case PointerDeviceKind.mouse:
case PointerDeviceKind.trackpad:
renderEditor?.selectPositionAt(
from: details.globalPosition,
cause: SelectionChangedCause.drag,
);
case PointerDeviceKind.stylus:
case PointerDeviceKind.invertedStylus:
case PointerDeviceKind.touch:
case PointerDeviceKind.unknown:
// For iOS platforms, a touch drag does not initiate unless the
// editable has focus and the drag began on the previous selection.
assert(_dragBeganOnPreviousSelection != null);
if (renderEditor?.hasFocus == true &&
_dragBeganOnPreviousSelection!) {
renderEditor?.selectPositionAt(
from: details.globalPosition,
cause: SelectionChangedCause.drag,
);
editor?.showMagnifier(details.globalPosition);
}
case null:
}
case TargetPlatform.android:
case TargetPlatform.fuchsia:
switch (details.kind) {
case PointerDeviceKind.mouse:
case PointerDeviceKind.trackpad:
renderEditor?.selectPositionAt(
from: details.globalPosition,
cause: SelectionChangedCause.drag,
);
case PointerDeviceKind.stylus:
case PointerDeviceKind.invertedStylus:
case PointerDeviceKind.touch:
case PointerDeviceKind.unknown:
// For Android, Fucshia, and iOS platforms, a touch drag
// does not initiate unless the editable has focus.
if (renderEditor?.hasFocus == true) {
renderEditor?.selectPositionAt(
from: details.globalPosition,
cause: SelectionChangedCause.drag,
);
editor?.showMagnifier(details.globalPosition);
}
case null:
}
case TargetPlatform.linux:
case TargetPlatform.macOS:
case TargetPlatform.windows:
renderEditor?.selectPositionAt(
from: details.globalPosition,
cause: SelectionChangedCause.drag,
);
}
}
} }
/// Handler for [EditorTextSelectionGestureDetector.onDragSelectionUpdate]. /// Handler for [EditorTextSelectionGestureDetector.onDragSelectionUpdate].
@ -757,206 +321,13 @@ class EditorTextSelectionGestureDetectorBuilder {
/// * [EditorTextSelectionGestureDetector.onDragSelectionUpdate], /// * [EditorTextSelectionGestureDetector.onDragSelectionUpdate],
/// which triggers this callback./lib/src/material/text_field.dart /// which triggers this callback./lib/src/material/text_field.dart
@protected @protected
void onDragSelectionUpdate(TapDragUpdateDetails updateDetails) { void onDragSelectionUpdate(
if (delegate.selectionEnabled == false) return; //DragStartDetails startDetails,
// if (editor?.textEditingValue.selection.isCollapsed == false) return; DragUpdateDetails updateDetails) {
if (!_isShiftPressed) { renderEditor!.extendSelection(
// Adjust the drag start offset for possible viewport offset changes. updateDetails.globalPosition,
final editableOffset = cause: SelectionChangedCause.drag,
Offset(0, renderEditor!.offset!.pixels - _dragStartViewportOffset); );
final scrollableOffset =
Offset(0, _scrollPosition - _dragStartScrollOffset);
final dragStartGlobalPosition =
updateDetails.globalPosition - updateDetails.offsetFromOrigin;
// Select word by word.
if (EditorTextSelectionGestureDetector.getEffectiveConsecutiveTapCount(
updateDetails.consecutiveTapCount) ==
2) {
renderEditor?.selectWordsInRange(
dragStartGlobalPosition - editableOffset - scrollableOffset,
updateDetails.globalPosition,
SelectionChangedCause.drag,
);
switch (updateDetails.kind) {
case PointerDeviceKind.stylus:
case PointerDeviceKind.invertedStylus:
case PointerDeviceKind.touch:
case PointerDeviceKind.unknown:
return editor?.updateMagnifier(updateDetails.globalPosition);
case PointerDeviceKind.mouse:
case PointerDeviceKind.trackpad:
case null:
return;
}
}
// Select paragraph-by-paragraph.
if (EditorTextSelectionGestureDetector.getEffectiveConsecutiveTapCount(
updateDetails.consecutiveTapCount) ==
3) {
switch (defaultTargetPlatform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.iOS:
switch (updateDetails.kind) {
case PointerDeviceKind.mouse:
case PointerDeviceKind.trackpad:
return _selectParagraphsInRange(
from: dragStartGlobalPosition -
editableOffset -
scrollableOffset,
to: updateDetails.globalPosition,
cause: SelectionChangedCause.drag,
);
case PointerDeviceKind.stylus:
case PointerDeviceKind.invertedStylus:
case PointerDeviceKind.touch:
case PointerDeviceKind.unknown:
case null:
// Triple tap to drag is not present on these platforms when using
// non-precise pointer devices at the moment.
break;
}
return;
case TargetPlatform.linux:
return _selectLinesInRange(
from: dragStartGlobalPosition - editableOffset - scrollableOffset,
to: updateDetails.globalPosition,
cause: SelectionChangedCause.drag,
);
case TargetPlatform.windows:
case TargetPlatform.macOS:
return _selectParagraphsInRange(
from: dragStartGlobalPosition - editableOffset - scrollableOffset,
to: updateDetails.globalPosition,
cause: SelectionChangedCause.drag,
);
}
}
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
// With a touch device, nothing should happen, unless there was a double tap, or
// there was a collapsed selection, and the tap/drag position is at the collapsed selection.
// In that case the caret should move with the drag position.
//
// With a mouse device, a drag should select the range from the origin of the drag
// to the current position of the drag.
switch (updateDetails.kind) {
case PointerDeviceKind.mouse:
case PointerDeviceKind.trackpad:
renderEditor?.selectPositionAt(
from:
dragStartGlobalPosition - editableOffset - scrollableOffset,
to: updateDetails.globalPosition,
cause: SelectionChangedCause.drag,
);
return;
case PointerDeviceKind.stylus:
case PointerDeviceKind.invertedStylus:
case PointerDeviceKind.touch:
case PointerDeviceKind.unknown:
assert(_dragBeganOnPreviousSelection != null);
if (renderEditor?.hasFocus == true &&
_dragStartSelection!.isCollapsed &&
_dragBeganOnPreviousSelection!) {
renderEditor?.selectPositionAt(
from: updateDetails.globalPosition,
cause: SelectionChangedCause.drag,
);
return editor?.updateMagnifier(updateDetails.globalPosition);
}
case null:
break;
}
return;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
// With a precise pointer device, such as a mouse, trackpad, or stylus,
// the drag will select the text spanning the origin of the drag to the end of the drag.
// With a touch device, the cursor should move with the drag.
switch (updateDetails.kind) {
case PointerDeviceKind.mouse:
case PointerDeviceKind.trackpad:
case PointerDeviceKind.stylus:
case PointerDeviceKind.invertedStylus:
renderEditor?.selectPositionAt(
from:
dragStartGlobalPosition - editableOffset - scrollableOffset,
to: updateDetails.globalPosition,
cause: SelectionChangedCause.drag,
);
return;
case PointerDeviceKind.touch:
case PointerDeviceKind.unknown:
if (renderEditor?.hasFocus == true) {
renderEditor?.selectPositionAt(
from: updateDetails.globalPosition,
cause: SelectionChangedCause.drag,
);
return editor?.updateMagnifier(updateDetails.globalPosition);
}
case null:
break;
}
return;
case TargetPlatform.macOS:
case TargetPlatform.linux:
case TargetPlatform.windows:
renderEditor?.selectPositionAt(
from: dragStartGlobalPosition - editableOffset - scrollableOffset,
to: updateDetails.globalPosition,
cause: SelectionChangedCause.drag,
);
}
}
if (_dragStartSelection!.isCollapsed ||
(defaultTargetPlatform != TargetPlatform.iOS &&
defaultTargetPlatform != TargetPlatform.macOS)) {
return _extendSelection(
updateDetails.globalPosition, SelectionChangedCause.drag);
}
// If the drag inverts the selection, Mac and iOS revert to the initial
// selection.
final selection = renderEditor!.selection;
final nextExtent =
renderEditor!.getPositionForOffset(updateDetails.globalPosition);
final isShiftTapDragSelectionForward =
_dragStartSelection!.baseOffset < _dragStartSelection!.extentOffset;
final isInverted = isShiftTapDragSelectionForward
? nextExtent.offset < _dragStartSelection!.baseOffset
: nextExtent.offset > _dragStartSelection!.baseOffset;
if (isInverted && selection.baseOffset == _dragStartSelection!.baseOffset) {
editor?.userUpdateTextEditingValue(
editor!.textEditingValue.copyWith(
selection: TextSelection(
baseOffset: _dragStartSelection!.extentOffset,
extentOffset: nextExtent.offset,
),
),
SelectionChangedCause.drag,
);
} else if (!isInverted &&
nextExtent.offset != _dragStartSelection!.baseOffset &&
selection.baseOffset != _dragStartSelection!.baseOffset) {
editor?.userUpdateTextEditingValue(
editor!.textEditingValue.copyWith(
selection: TextSelection(
baseOffset: _dragStartSelection!.baseOffset,
extentOffset: nextExtent.offset,
),
),
SelectionChangedCause.drag,
);
} else {
_extendSelection(
updateDetails.globalPosition, SelectionChangedCause.drag);
}
} }
/// Handler for [EditorTextSelectionGestureDetector.onDragSelectionEnd]. /// Handler for [EditorTextSelectionGestureDetector.onDragSelectionEnd].
@ -968,8 +339,7 @@ class EditorTextSelectionGestureDetectorBuilder {
/// * [EditorTextSelectionGestureDetector.onDragSelectionEnd], /// * [EditorTextSelectionGestureDetector.onDragSelectionEnd],
/// which triggers this callback. /// which triggers this callback.
@protected @protected
void onDragSelectionEnd(TapDragEndDetails details) { void onDragSelectionEnd(DragEndDetails details) {
// if (editor?.textEditingValue.selection.isCollapsed == false) return;
renderEditor!.handleDragEnd(details); renderEditor!.handleDragEnd(details);
if (isDesktop(supportWeb: true) && if (isDesktop(supportWeb: true) &&
delegate.selectionEnabled && delegate.selectionEnabled &&
@ -977,7 +347,6 @@ class EditorTextSelectionGestureDetectorBuilder {
// added to show selection copy/paste toolbar after drag to select // added to show selection copy/paste toolbar after drag to select
editor!.showToolbar(); editor!.showToolbar();
} }
editor?.hideMagnifier();
} }
/// Returns a [EditorTextSelectionGestureDetector] configured with /// Returns a [EditorTextSelectionGestureDetector] configured with
@ -992,26 +361,21 @@ class EditorTextSelectionGestureDetectorBuilder {
}) { }) {
return EditorTextSelectionGestureDetector( return EditorTextSelectionGestureDetector(
key: key, key: key,
onTapTrackStart: onTapTrackStart,
onTapTrackReset: onTapTrackReset,
onTapDown: onTapDown, onTapDown: onTapDown,
onForcePressStart: delegate.forcePressEnabled ? onForcePressStart : null, onForcePressStart: delegate.forcePressEnabled ? onForcePressStart : null,
onForcePressEnd: delegate.forcePressEnabled ? onForcePressEnd : null, onForcePressEnd: delegate.forcePressEnabled ? onForcePressEnd : null,
onSecondaryTap: onSecondaryTap,
onSecondaryTapDown: onSecondaryTapDown,
onSingleTapUp: onSingleTapUp, onSingleTapUp: onSingleTapUp,
onSingleTapCancel: onSingleTapCancel, onSingleTapCancel: onSingleTapCancel,
onUserTap: onUserTap,
onSingleLongTapStart: onSingleLongTapStart, onSingleLongTapStart: onSingleLongTapStart,
onSingleLongTapMoveUpdate: onSingleLongTapMoveUpdate, onSingleLongTapMoveUpdate: onSingleLongTapMoveUpdate,
onSingleLongTapEnd: onSingleLongTapEnd, onSingleLongTapEnd: onSingleLongTapEnd,
onDoubleTapDown: onDoubleTapDown, onDoubleTapDown: onDoubleTapDown,
onTripleTapDown: onTripleTapDown, onSecondarySingleTapUp: onSecondarySingleTapUp,
onDragSelectionStart: onDragSelectionStart, onDragSelectionStart: onDragSelectionStart,
onDragSelectionUpdate: onDragSelectionUpdate, onDragSelectionUpdate: onDragSelectionUpdate,
onDragSelectionEnd: onDragSelectionEnd, onDragSelectionEnd: onDragSelectionEnd,
onUserTapAlwaysCalled: onUserTapAlwaysCalled,
behavior: behavior, behavior: behavior,
detectWordBoundary: detectWordBoundary,
child: child, child: child,
); );
} }

@ -391,8 +391,10 @@ class _TextLineState extends State<TextLine> {
final nodeStyle = textNode.style; final nodeStyle = textNode.style;
final isLink = nodeStyle.containsKey(Attribute.link.key) && final isLink = nodeStyle.containsKey(Attribute.link.key) &&
nodeStyle.attributes[Attribute.link.key]!.value != null; nodeStyle.attributes[Attribute.link.key]!.value != null;
final style = final style =
_getInlineTextStyle(nodeStyle, defaultStyles, lineStyle, isLink); _getInlineTextStyle(nodeStyle, defaultStyles, lineStyle, isLink);
if (widget.controller.configurations.requireScriptFontFeatures == false && if (widget.controller.configurations.requireScriptFontFeatures == false &&
textNode.value.isNotEmpty) { textNode.value.isNotEmpty) {
if (nodeStyle.containsKey(Attribute.script.key)) { if (nodeStyle.containsKey(Attribute.script.key)) {
@ -404,22 +406,6 @@ class _TextLineState extends State<TextLine> {
} }
} }
if (!isLink &&
!widget.readOnly &&
!widget.line.style.attributes.containsKey('code-block') &&
!widget.line.style.attributes.containsKey('placeholder') &&
!kIsWeb) {
final service = SpellCheckerServiceProvider.instance;
final spellcheckedSpans = service.checkSpelling(textNode.value);
if (spellcheckedSpans != null && spellcheckedSpans.isNotEmpty) {
return TextSpan(
children: spellcheckedSpans,
style: style,
mouseCursor: null,
);
}
}
final recognizer = _getRecognizer(node, isLink); final recognizer = _getRecognizer(node, isLink);
return TextSpan( return TextSpan(
text: textNode.value, text: textNode.value,
@ -446,7 +432,7 @@ class _TextLineState extends State<TextLine> {
if (k == Attribute.underline.key || k == Attribute.strikeThrough.key) { if (k == Attribute.underline.key || k == Attribute.strikeThrough.key) {
var textColor = defaultStyles.color; var textColor = defaultStyles.color;
if (color?.value is String) { if (color?.value is String) {
textColor = stringToColor(color?.value, textColor, defaultStyles); textColor = stringToColor(color?.value, textColor);
} }
res = _merge(res.copyWith(decorationColor: textColor), res = _merge(res.copyWith(decorationColor: textColor),
s!.copyWith(decorationColor: textColor)); s!.copyWith(decorationColor: textColor));
@ -500,7 +486,7 @@ class _TextLineState extends State<TextLine> {
if (color != null && color.value != null) { if (color != null && color.value != null) {
var textColor = defaultStyles.color; var textColor = defaultStyles.color;
if (color.value is String) { if (color.value is String) {
textColor = stringToColor(color.value, null, defaultStyles); textColor = stringToColor(color.value);
} }
if (textColor != null) { if (textColor != null) {
res = res.merge(TextStyle(color: textColor)); res = res.merge(TextStyle(color: textColor));
@ -509,8 +495,7 @@ class _TextLineState extends State<TextLine> {
final background = nodeStyle.attributes[Attribute.background.key]; final background = nodeStyle.attributes[Attribute.background.key];
if (background != null && background.value != null) { if (background != null && background.value != null) {
final backgroundColor = final backgroundColor = stringToColor(background.value);
stringToColor(background.value, null, defaultStyles);
res = res.merge(TextStyle(backgroundColor: backgroundColor)); res = res.merge(TextStyle(backgroundColor: backgroundColor));
} }

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -186,8 +187,6 @@ class EditorTextSelectionOverlay {
final MagnifierController _magnifierController = MagnifierController(); final MagnifierController _magnifierController = MagnifierController();
bool get magnifierIsVisible => _magnifierController.shown;
final TextMagnifierConfiguration magnifierConfiguration; final TextMagnifierConfiguration magnifierConfiguration;
final ValueNotifier<MagnifierInfo> _magnifierInfo = final ValueNotifier<MagnifierInfo> _magnifierInfo =
@ -434,7 +433,7 @@ class EditorTextSelectionOverlay {
context: context, context: context,
below: magnifierConfiguration.shouldDisplayHandlesInMagnifier below: magnifierConfiguration.shouldDisplayHandlesInMagnifier
? null ? null
: _handles?.elementAtOrNull(0), : _handles![0],
builder: (_) => builtMagnifier, builder: (_) => builtMagnifier,
); );
} }
@ -791,39 +790,31 @@ class EditorTextSelectionGestureDetector extends StatefulWidget {
/// The [child] parameter must not be null. /// The [child] parameter must not be null.
const EditorTextSelectionGestureDetector({ const EditorTextSelectionGestureDetector({
required this.child, required this.child,
super.key,
this.onTapTrackStart,
this.onTapTrackReset,
this.onTapDown, this.onTapDown,
this.onForcePressStart, this.onForcePressStart,
this.onForcePressEnd, this.onForcePressEnd,
this.onSecondaryTap,
this.onSecondaryTapDown,
this.onSingleTapUp, this.onSingleTapUp,
this.onSingleTapCancel, this.onSingleTapCancel,
this.onUserTap, this.onSecondaryTapDown,
this.onSecondarySingleTapUp,
this.onSecondarySingleTapCancel,
this.onSecondaryDoubleTapDown,
this.onSingleLongTapStart, this.onSingleLongTapStart,
this.onSingleLongTapMoveUpdate, this.onSingleLongTapMoveUpdate,
this.onSingleLongTapEnd, this.onSingleLongTapEnd,
this.onDoubleTapDown, this.onDoubleTapDown,
this.onTripleTapDown,
this.onDragSelectionStart, this.onDragSelectionStart,
this.onDragSelectionUpdate, this.onDragSelectionUpdate,
this.onDragSelectionEnd, this.onDragSelectionEnd,
this.onUserTapAlwaysCalled = false,
this.behavior, this.behavior,
this.detectWordBoundary = true,
super.key,
}); });
/// {@macro flutter.gestures.selectionrecognizers.BaseTapAndDragGestureRecognizer.onTapTrackStart}
final VoidCallback? onTapTrackStart;
/// {@macro flutter.gestures.selectionrecognizers.BaseTapAndDragGestureRecognizer.onTapTrackReset}
final VoidCallback? onTapTrackReset;
/// Called for every tap down including every tap down that's part of a /// Called for every tap down including every tap down that's part of a
/// double click or a long press, except touches that include enough movement /// double click or a long press, except touches that include enough movement
/// to not qualify as taps (e.g. pans and flings). /// to not qualify as taps (e.g. pans and flings).
final GestureTapDragDownCallback? onTapDown; final GestureTapDownCallback? onTapDown;
/// Called when a pointer has tapped down and the force of the pointer has /// Called when a pointer has tapped down and the force of the pointer has
/// just become greater than [ForcePressGestureRecognizer.startPressure]. /// just become greater than [ForcePressGestureRecognizer.startPressure].
@ -833,31 +824,28 @@ class EditorTextSelectionGestureDetector extends StatefulWidget {
/// lifted off the screen. /// lifted off the screen.
final GestureForcePressEndCallback? onForcePressEnd; final GestureForcePressEndCallback? onForcePressEnd;
/// Called for a tap event with the secondary mouse button. /// Called for each distinct tap except for every second tap of a double tap.
final GestureTapCallback? onSecondaryTap;
/// Called for a tap down event with the secondary mouse button.
final GestureTapDownCallback? onSecondaryTapDown;
/// Called for the first tap in a series of taps, consecutive taps do not call
/// this method.
///
/// For example, if the detector was configured with [onTapDown] and /// For example, if the detector was configured with [onTapDown] and
/// [onDoubleTapDown], three quick taps would be recognized as a single tap /// [onDoubleTapDown], three quick taps would be recognized as a single tap
/// down, followed by a tap up, then a double tap down, followed by a single tap down. /// down, followed by a double tap down, followed by a single tap down.
final GestureTapDragUpCallback? onSingleTapUp; final GestureTapUpCallback? onSingleTapUp;
/// Called for each touch that becomes recognized as a gesture that is not a /// Called for each touch that becomes recognized as a gesture that is not a
/// short tap, such as a long tap or drag. It is called at the moment when /// short tap, such as a long tap or drag. It is called at the moment when
/// another gesture from the touch is recognized. /// another gesture from the touch is recognized.
final GestureCancelCallback? onSingleTapCancel; final GestureTapCancelCallback? onSingleTapCancel;
/// Called for the first tap in a series of taps when [onUserTapAlwaysCalled] is /// onTapDown for mouse right click
/// disabled, which is the default behavior. final GestureTapDownCallback? onSecondaryTapDown;
///
/// When [onUserTapAlwaysCalled] is enabled, this is called for every tap, /// onTapUp for mouse right click
/// including consecutive taps. final GestureTapUpCallback? onSecondarySingleTapUp;
final GestureTapCallback? onUserTap;
/// onTapCancel for mouse right click
final GestureTapCancelCallback? onSecondarySingleTapCancel;
/// onDoubleTap for mouse right click
final GestureTapDownCallback? onSecondaryDoubleTapDown;
/// Called for a single long tap that's sustained for longer than /// Called for a single long tap that's sustained for longer than
/// [kLongPressTimeout] but not necessarily lifted. Not called for a /// [kLongPressTimeout] but not necessarily lifted. Not called for a
@ -872,25 +860,20 @@ class EditorTextSelectionGestureDetector extends StatefulWidget {
/// Called after a momentary hold or a short tap that is close in space and /// Called after a momentary hold or a short tap that is close in space and
/// time (within [kDoubleTapTimeout]) to a previous short tap. /// time (within [kDoubleTapTimeout]) to a previous short tap.
final GestureTapDragDownCallback? onDoubleTapDown; final GestureTapDownCallback? onDoubleTapDown;
/// Called after a momentary hold or a short tap that is close in space and
/// time (within [kDoubleTapTimeout]) to a previous double-tap.
final GestureTapDragDownCallback? onTripleTapDown;
/// Called when a mouse starts dragging to select text. /// Called when a mouse starts dragging to select text.
final GestureTapDragStartCallback? onDragSelectionStart; final GestureDragStartCallback? onDragSelectionStart;
/// Called repeatedly as a mouse moves while dragging. /// Called repeatedly as a mouse moves while dragging.
final GestureTapDragUpdateCallback? onDragSelectionUpdate; ///
/// The frequency of calls is throttled to avoid excessive text layout
/// operations in text fields. The throttling is controlled by the constant
/// [_kDragSelectionUpdateThrottle].
final GestureDragUpdateCallback? onDragSelectionUpdate;
/// Called when a mouse that was previously dragging is released. /// Called when a mouse that was previously dragging is released.
final GestureTapDragEndCallback? onDragSelectionEnd; final GestureDragEndCallback? onDragSelectionEnd;
/// Whether [onUserTap] will be called for all taps including consecutive taps.
///
/// Defaults to false, so [onUserTap] is only called for each distinct tap.
final bool onUserTapAlwaysCalled;
/// How this gesture detector should behave during hit testing. /// How this gesture detector should behave during hit testing.
/// ///
@ -900,145 +883,210 @@ class EditorTextSelectionGestureDetector extends StatefulWidget {
/// Child below this widget. /// Child below this widget.
final Widget child; final Widget child;
final bool detectWordBoundary;
@override @override
State<StatefulWidget> createState() => State<StatefulWidget> createState() =>
_EditorTextSelectionGestureDetectorState(); _EditorTextSelectionGestureDetectorState();
static int getEffectiveConsecutiveTapCount(int rawCount) {
switch (defaultTargetPlatform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
// From observation, these platform's reset their tap count to 0 when
// the number of consecutive taps exceeds 3. For example on Debian Linux
// with GTK, when going past a triple click, on the fourth click the
// selection is moved to the precise click position, on the fifth click
// the word at the position is selected, and on the sixth click the
// paragraph at the position is selected.
return rawCount <= 3
? rawCount
: (rawCount % 3 == 0 ? 3 : rawCount % 3);
case TargetPlatform.iOS:
case TargetPlatform.macOS:
// From observation, these platform's either hold their tap count at 3.
// For example on macOS, when going past a triple click, the selection
// should be retained at the paragraph that was first selected on triple
// click.
return math.min(rawCount, 3);
case TargetPlatform.windows:
// From observation, this platform's consecutive tap actions alternate
// between double click and triple click actions. For example, after a
// triple click has selected a paragraph, on the next click the word at
// the clicked position will be selected, and on the next click the
// paragraph at the position is selected.
return rawCount < 2 ? rawCount : 2 + rawCount % 2;
}
}
} }
class _EditorTextSelectionGestureDetectorState class _EditorTextSelectionGestureDetectorState
extends State<EditorTextSelectionGestureDetector> { extends State<EditorTextSelectionGestureDetector> {
// Converts the details.consecutiveTapCount from a TapAndDrag*Details object, // Counts down for a short duration after a previous tap. Null otherwise.
// which can grow to be infinitely large, to a value between 1 and 3. The value Timer? _doubleTapTimer;
// that the raw count is converted to is based on the default observed behavior Offset? _lastTapOffset;
// on the native platforms.
// // True if a second tap down of a double tap is detected. Used to discard
// This method should be used in all instances when details.consecutiveTapCount // subsequent tap up / tap hold of the same tap.
// would be used. bool _isDoubleTap = false;
void _handleTapTrackStart() {
widget.onTapTrackStart?.call();
}
void _handleTapTrackReset() { // _isDoubleTap for mouse right click
widget.onTapTrackReset?.call(); bool _isSecondaryDoubleTap = false;
@override
void dispose() {
_doubleTapTimer?.cancel();
_dragUpdateThrottleTimer?.cancel();
super.dispose();
} }
// The down handler is force-run on success of a single tap and optimistically // The down handler is force-run on success of a single tap and optimistically
// run before a long press success. // run before a long press success.
void _handleTapDown(TapDragDownDetails details) { void _handleTapDown(TapDownDetails details) {
widget.onTapDown?.call(details); widget.onTapDown?.call(details);
// This isn't detected as a double tap gesture in the gesture recognizer
// because it's 2 single taps, each of which may do different things depending
// on whether it's a single tap, the first tap of a double tap, the second
// tap held down, a clean double tap etc.
if (EditorTextSelectionGestureDetector.getEffectiveConsecutiveTapCount(
details.consecutiveTapCount) ==
2) {
return widget.onDoubleTapDown?.call(details);
}
if (EditorTextSelectionGestureDetector.getEffectiveConsecutiveTapCount( // This isn't detected as a double tap gesture in the gesture recognizer
details.consecutiveTapCount) == // because it's 2 single taps, each of which may do different things
3) { // depending on whether it's a single tap, the first tap of a double tap,
return widget.onTripleTapDown?.call(details); // the second tap held down, a clean double tap etc.
if (_doubleTapTimer != null &&
_isWithinDoubleTapTolerance(details.globalPosition)) {
// If there was already a previous tap, the second down hold/tap is a
// double tap down.
widget.onDoubleTapDown?.call(details);
_doubleTapTimer!.cancel();
_doubleTapTimeout();
_isDoubleTap = true;
} }
} }
void _handleTapUp(TapDragUpDetails details) { void _handleTapUp(TapUpDetails details) {
if (EditorTextSelectionGestureDetector.getEffectiveConsecutiveTapCount( if (!_isDoubleTap) {
details.consecutiveTapCount) ==
1) {
widget.onSingleTapUp?.call(details); widget.onSingleTapUp?.call(details);
widget.onUserTap?.call(); _lastTapOffset = details.globalPosition;
} else if (widget.onUserTapAlwaysCalled) { _doubleTapTimer = Timer(kDoubleTapTimeout, _doubleTapTimeout);
widget.onUserTap?.call();
} }
_isDoubleTap = false;
} }
void _handleTapCancel() { void _handleTapCancel() {
widget.onSingleTapCancel?.call(); widget.onSingleTapCancel?.call();
} }
void _handleDragStart(TapDragStartDetails details) { // added secondary tap function for mouse right click to show toolbar
void _handleSecondaryTapDown(TapDownDetails details) {
if (widget.onSecondaryTapDown != null) {
widget.onSecondaryTapDown?.call(details);
}
if (_doubleTapTimer != null &&
_isWithinDoubleTapTolerance(details.globalPosition)) {
widget.onSecondaryDoubleTapDown?.call(details);
_doubleTapTimer!.cancel();
_doubleTapTimeout();
_isDoubleTap = true;
}
}
void _handleSecondaryTapUp(TapUpDetails details) {
if (!_isSecondaryDoubleTap) {
widget.onSecondarySingleTapUp?.call(details);
_lastTapOffset = details.globalPosition;
_doubleTapTimer = Timer(kDoubleTapTimeout, _doubleTapTimeout);
}
_isSecondaryDoubleTap = false;
}
void _handleSecondaryTapCancel() {
widget.onSecondarySingleTapCancel?.call();
}
DragStartDetails? _lastDragStartDetails;
DragUpdateDetails? _lastDragUpdateDetails;
Timer? _dragUpdateThrottleTimer;
void _handleDragStart(DragStartDetails details) {
assert(_lastDragStartDetails == null);
_lastDragStartDetails = details;
widget.onDragSelectionStart?.call(details); widget.onDragSelectionStart?.call(details);
} }
void _handleDragUpdate(TapDragUpdateDetails details) { void _handleDragUpdate(DragUpdateDetails details) {
widget.onDragSelectionUpdate?.call(details); _lastDragUpdateDetails = details;
_dragUpdateThrottleTimer ??= Timer(
const Duration(milliseconds: 50),
_handleDragUpdateThrottled,
);
} }
void _handleDragEnd(TapDragEndDetails details) { /// Drag updates are being throttled to avoid excessive text layouts in text
/// fields. The frequency of invocations is controlled by the constant
/// [_kDragSelectionUpdateThrottle].
///
/// Once the drag gesture ends, any pending drag update will be fired
/// immediately. See [_handleDragEnd].
void _handleDragUpdateThrottled() {
assert(_lastDragStartDetails != null);
assert(_lastDragUpdateDetails != null);
if (widget.onDragSelectionUpdate != null) {
widget.onDragSelectionUpdate!(
//_lastDragStartDetails!,
_lastDragUpdateDetails!);
}
_dragUpdateThrottleTimer = null;
_lastDragUpdateDetails = null;
}
void _handleDragEnd(DragEndDetails details) {
assert(_lastDragStartDetails != null);
if (_dragUpdateThrottleTimer != null) {
// If there's already an update scheduled, trigger it immediately and
// cancel the timer.
_dragUpdateThrottleTimer!.cancel();
_handleDragUpdateThrottled();
}
widget.onDragSelectionEnd?.call(details); widget.onDragSelectionEnd?.call(details);
_dragUpdateThrottleTimer = null;
_lastDragStartDetails = null;
_lastDragUpdateDetails = null;
} }
void _forcePressStarted(ForcePressDetails details) { void _forcePressStarted(ForcePressDetails details) {
_doubleTapTimer?.cancel();
_doubleTapTimer = null;
widget.onForcePressStart?.call(details); widget.onForcePressStart?.call(details);
} }
void _forcePressEnded(ForcePressDetails details) { void _forcePressEnded(ForcePressDetails details) {
widget.onForcePressEnd?.call(details); if (widget.onForcePressEnd != null) {
widget.onForcePressEnd?.call(details);
}
} }
void _handleLongPressStart(LongPressStartDetails details) { void _handleLongPressStart(LongPressStartDetails details) {
if (widget.onSingleLongTapStart != null) { if (!_isDoubleTap) {
widget.onSingleLongTapStart!(details); widget.onSingleLongTapStart?.call(details);
} }
} }
void _handleLongPressMoveUpdate(LongPressMoveUpdateDetails details) { void _handleLongPressMoveUpdate(LongPressMoveUpdateDetails details) {
if (widget.onSingleLongTapMoveUpdate != null) { if (!_isDoubleTap) {
widget.onSingleLongTapMoveUpdate!(details); widget.onSingleLongTapMoveUpdate?.call(details);
} }
} }
void _handleLongPressEnd(LongPressEndDetails details) { void _handleLongPressEnd(LongPressEndDetails details) {
if (widget.onSingleLongTapEnd != null) { if (!_isDoubleTap) {
widget.onSingleLongTapEnd!(details); widget.onSingleLongTapEnd?.call(details);
} }
_isDoubleTap = false;
}
void _doubleTapTimeout() {
_doubleTapTimer = null;
_lastTapOffset = null;
}
bool _isWithinDoubleTapTolerance(Offset secondTapOffset) {
if (_lastTapOffset == null) {
return false;
}
return (secondTapOffset - _lastTapOffset!).distance <= kDoubleTapSlop;
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final gestures = <Type, GestureRecognizerFactory>{}; final gestures = <Type, GestureRecognizerFactory>{};
gestures[TapGestureRecognizer] = // Use _TransparentTapGestureRecognizer so that TextSelectionGestureDetector
GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>( // can receive the same tap events that a selection handle placed visually
() => TapGestureRecognizer(debugOwner: this), // on top of it also receives.
gestures[_TransparentTapGestureRecognizer] =
GestureRecognizerFactoryWithHandlers<_TransparentTapGestureRecognizer>(
() => _TransparentTapGestureRecognizer(debugOwner: this),
(instance) { (instance) {
instance instance
..onSecondaryTap = widget.onSecondaryTap ..onTapDown = _handleTapDown
..onSecondaryTapDown = widget.onSecondaryTapDown; ..onTapUp = _handleTapUp
..onTapCancel = _handleTapCancel
..onSecondaryTapDown = _handleSecondaryTapDown
..onSecondaryTapUp = _handleSecondaryTapUp
..onSecondaryTapCancel = _handleSecondaryTapCancel;
}, },
); );
@ -1062,51 +1110,21 @@ class _EditorTextSelectionGestureDetectorState
if (widget.onDragSelectionStart != null || if (widget.onDragSelectionStart != null ||
widget.onDragSelectionUpdate != null || widget.onDragSelectionUpdate != null ||
widget.onDragSelectionEnd != null) { widget.onDragSelectionEnd != null) {
switch (defaultTargetPlatform) { gestures[HorizontalDragGestureRecognizer] =
case TargetPlatform.android: GestureRecognizerFactoryWithHandlers<HorizontalDragGestureRecognizer>(
case TargetPlatform.fuchsia: () => HorizontalDragGestureRecognizer(
case TargetPlatform.iOS: debugOwner: this,
gestures[TapAndHorizontalDragGestureRecognizer] = supportedDevices: <PointerDeviceKind>{PointerDeviceKind.mouse}),
GestureRecognizerFactoryWithHandlers< (instance) {
TapAndHorizontalDragGestureRecognizer>( // Text selection should start from the position of the first pointer
() => TapAndHorizontalDragGestureRecognizer(debugOwner: this), // down event.
(instance) { instance
instance ..dragStartBehavior = DragStartBehavior.down
// Text selection should start from the position of the first pointer ..onStart = _handleDragStart
// down event. ..onUpdate = _handleDragUpdate
..dragStartBehavior = DragStartBehavior.down ..onEnd = _handleDragEnd;
..onTapTrackStart = _handleTapTrackStart },
..onTapTrackReset = _handleTapTrackReset );
..onTapDown = _handleTapDown
..onDragStart = _handleDragStart
..onDragUpdate = _handleDragUpdate
..onDragEnd = _handleDragEnd
..onTapUp = _handleTapUp
..onCancel = _handleTapCancel;
},
);
case TargetPlatform.linux:
case TargetPlatform.macOS:
case TargetPlatform.windows:
gestures[TapAndPanGestureRecognizer] =
GestureRecognizerFactoryWithHandlers<TapAndPanGestureRecognizer>(
() => TapAndPanGestureRecognizer(debugOwner: this),
(instance) {
instance
// Text selection should start from the position of the first pointer
// down event.
..dragStartBehavior = DragStartBehavior.down
..onTapTrackStart = _handleTapTrackStart
..onTapTrackReset = _handleTapTrackReset
..onTapDown = _handleTapDown
..onDragStart = _handleDragStart
..onDragUpdate = _handleDragUpdate
..onDragEnd = _handleDragEnd
..onTapUp = _handleTapUp
..onCancel = _handleTapCancel;
},
);
}
} }
if (widget.onForcePressStart != null || widget.onForcePressEnd != null) { if (widget.onForcePressStart != null || widget.onForcePressEnd != null) {
@ -1130,3 +1148,32 @@ class _EditorTextSelectionGestureDetectorState
); );
} }
} }
// A TapGestureRecognizer which allows other GestureRecognizers to win in the
// GestureArena. This means both _TransparentTapGestureRecognizer and other
// GestureRecognizers can handle the same event.
//
// This enables proper handling of events on both the selection handle and the
// underlying input, since there is significant overlap between the two given
// the handle's padded hit area. For example, the selection handle needs to
// handle single taps on itself, but double taps need to be handled by the
// underlying input.
class _TransparentTapGestureRecognizer extends TapGestureRecognizer {
_TransparentTapGestureRecognizer({
super.debugOwner,
});
@override
void rejectGesture(int pointer) {
// Accept new gestures that another recognizer has already won.
// Specifically, this needs to accept taps on the text selection handle on
// behalf of the text field in order to handle double tap to select. It must
// not accept other gestures like longpresses and drags that end outside of
// the text field.
if (state == GestureRecognizerState.ready) {
acceptGesture(pointer);
} else {
super.rejectGesture(pointer);
}
}
}

@ -21,7 +21,6 @@ import 'quill_localizations_hi.dart';
import 'quill_localizations_id.dart'; import 'quill_localizations_id.dart';
import 'quill_localizations_it.dart'; import 'quill_localizations_it.dart';
import 'quill_localizations_ja.dart'; import 'quill_localizations_ja.dart';
import 'quill_localizations_km.dart';
import 'quill_localizations_ko.dart'; import 'quill_localizations_ko.dart';
import 'quill_localizations_ku.dart'; import 'quill_localizations_ku.dart';
import 'quill_localizations_ms.dart'; import 'quill_localizations_ms.dart';
@ -43,8 +42,6 @@ import 'quill_localizations_ur.dart';
import 'quill_localizations_vi.dart'; import 'quill_localizations_vi.dart';
import 'quill_localizations_zh.dart'; import 'quill_localizations_zh.dart';
// ignore_for_file: type=lint
/// Callers can lookup localized strings with an instance of FlutterQuillLocalizations /// Callers can lookup localized strings with an instance of FlutterQuillLocalizations
/// returned by `FlutterQuillLocalizations.of(context)`. /// returned by `FlutterQuillLocalizations.of(context)`.
/// ///
@ -147,7 +144,6 @@ abstract class FlutterQuillLocalizations {
Locale('id'), Locale('id'),
Locale('it'), Locale('it'),
Locale('ja'), Locale('ja'),
Locale('km'),
Locale('ko'), Locale('ko'),
Locale('ku'), Locale('ku'),
Locale('ku', 'CKB'), Locale('ku', 'CKB'),
@ -786,7 +782,6 @@ class _FlutterQuillLocalizationsDelegate
'id', 'id',
'it', 'it',
'ja', 'ja',
'km',
'ko', 'ko',
'ku', 'ku',
'ms', 'ms',
@ -894,8 +889,6 @@ FlutterQuillLocalizations lookupFlutterQuillLocalizations(Locale locale) {
return FlutterQuillLocalizationsIt(); return FlutterQuillLocalizationsIt();
case 'ja': case 'ja':
return FlutterQuillLocalizationsJa(); return FlutterQuillLocalizationsJa();
case 'km':
return FlutterQuillLocalizationsKm();
case 'ko': case 'ko':
return FlutterQuillLocalizationsKo(); return FlutterQuillLocalizationsKo();
case 'ku': case 'ku':

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Arabic (`ar`). /// The translations for Arabic (`ar`).
class FlutterQuillLocalizationsAr extends FlutterQuillLocalizations { class FlutterQuillLocalizationsAr extends FlutterQuillLocalizations {
FlutterQuillLocalizationsAr([String locale = 'ar']) : super(locale); FlutterQuillLocalizationsAr([super.locale = 'ar']);
@override @override
String get pasteLink => 'نسخ الرابط'; String get pasteLink => 'نسخ الرابط';
@ -237,10 +235,10 @@ class FlutterQuillLocalizationsAr extends FlutterQuillLocalizations {
String get errorWhileSavingImage => 'حدث خطأ أثناء حفظ الصورة'; String get errorWhileSavingImage => 'حدث خطأ أثناء حفظ الصورة';
@override @override
String get pleaseEnterTextForYourLink => 'مثال: \'تعلم المزيد\''; String get pleaseEnterTextForYourLink => "مثال: 'تعلم المزيد'";
@override @override
String get pleaseEnterTheLinkURL => 'مثال: \'https://example.com\''; String get pleaseEnterTheLinkURL => "مثال: 'https://example.com'";
@override @override
String get pleaseEnterAValidImageURL => 'الرجاء إدخال عنوان URL صحيح للصورة'; String get pleaseEnterAValidImageURL => 'الرجاء إدخال عنوان URL صحيح للصورة';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Bulgarian (`bg`). /// The translations for Bulgarian (`bg`).
class FlutterQuillLocalizationsBg extends FlutterQuillLocalizations { class FlutterQuillLocalizationsBg extends FlutterQuillLocalizations {
FlutterQuillLocalizationsBg([String locale = 'bg']) : super(locale); FlutterQuillLocalizationsBg([super.locale = 'bg']);
@override @override
String get pasteLink => 'Поставете връзка'; String get pasteLink => 'Поставете връзка';
@ -237,10 +235,10 @@ class FlutterQuillLocalizationsBg extends FlutterQuillLocalizations {
String get errorWhileSavingImage => 'Error while saving image'; String get errorWhileSavingImage => 'Error while saving image';
@override @override
String get pleaseEnterTextForYourLink => 'Например, \'Научете повече\''; String get pleaseEnterTextForYourLink => "Например, 'Научете повече'";
@override @override
String get pleaseEnterTheLinkURL => 'Например, \'https://example.com\''; String get pleaseEnterTheLinkURL => "Например, 'https://example.com'";
@override @override
String get pleaseEnterAValidImageURL => String get pleaseEnterAValidImageURL =>

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Bengali Bangla (`bn`). /// The translations for Bengali Bangla (`bn`).
class FlutterQuillLocalizationsBn extends FlutterQuillLocalizations { class FlutterQuillLocalizationsBn extends FlutterQuillLocalizations {
FlutterQuillLocalizationsBn([String locale = 'bn']) : super(locale); FlutterQuillLocalizationsBn([super.locale = 'bn']);
@override @override
String get pasteLink => 'িক পট কর'; String get pasteLink => 'িক পট কর';
@ -238,11 +236,11 @@ class FlutterQuillLocalizationsBn extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'আপনর লির জনয একটিসট লিন (উদ \'আরও জ\')'; "আপনর লির জনয একটিসট লিন (উদ 'আরও জন')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'দয করিক URL লিন (উদ \'https://example.com\')'; "দয করিক URL লিন (উদ 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => 'দয কর একটিধ চির URL লি'; String get pleaseEnterAValidImageURL => 'দয কর একটিধ চির URL লি';

@ -1,16 +1,14 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Catalan Valencian (`ca`). /// The translations for Catalan Valencian (`ca`).
class FlutterQuillLocalizationsCa extends FlutterQuillLocalizations { class FlutterQuillLocalizationsCa extends FlutterQuillLocalizations {
FlutterQuillLocalizationsCa([String locale = 'ca']) : super(locale); FlutterQuillLocalizationsCa([super.locale = 'ca']);
@override @override
String get pasteLink => 'Enganxa un enllaç'; String get pasteLink => 'Enganxa un enllaç';
@override @override
String get ok => 'D\'acord'; String get ok => "D'acord";
@override @override
String get selectColor => 'Selecciona el color'; String get selectColor => 'Selecciona el color';
@ -121,7 +119,7 @@ class FlutterQuillLocalizationsCa extends FlutterQuillLocalizations {
String get clearFormat => 'Neteja format'; String get clearFormat => 'Neteja format';
@override @override
String get alignLeft => 'Alinea a l\'esquerra'; String get alignLeft => "Alinea a l'esquerra";
@override @override
String get alignCenter => 'Alinea al centre'; String get alignCenter => 'Alinea al centre';
@ -133,7 +131,7 @@ class FlutterQuillLocalizationsCa extends FlutterQuillLocalizations {
String get alignJustify => 'Justifica'; String get alignJustify => 'Justifica';
@override @override
String get justifyWinWidth => 'Justifica l\'amplada de la finestra'; String get justifyWinWidth => "Justifica l'amplada de la finestra";
@override @override
String get textDirection => 'Direcció del text'; String get textDirection => 'Direcció del text';
@ -187,10 +185,10 @@ class FlutterQuillLocalizationsCa extends FlutterQuillLocalizations {
String get insertURL => 'Insereix URL'; String get insertURL => 'Insereix URL';
@override @override
String get visitLink => 'Visita l\'enllaç'; String get visitLink => "Visita l'enllaç";
@override @override
String get enterLink => 'Introdueix l\'enllaç'; String get enterLink => "Introdueix l'enllaç";
@override @override
String get enterMedia => 'Introdueix mitjans'; String get enterMedia => 'Introdueix mitjans';
@ -217,7 +215,7 @@ class FlutterQuillLocalizationsCa extends FlutterQuillLocalizations {
String get findText => 'Troba text'; String get findText => 'Troba text';
@override @override
String get moveToPreviousOccurrence => 'Mou-te a l\'ocurrència anterior'; String get moveToPreviousOccurrence => "Mou-te a l'ocurrència anterior";
@override @override
String get moveToNextOccurrence => 'Mou-te a la següent ocurrència'; String get moveToNextOccurrence => 'Mou-te a la següent ocurrència';
@ -226,11 +224,11 @@ class FlutterQuillLocalizationsCa extends FlutterQuillLocalizations {
String get savedUsingTheNetwork => 'Desat utilitzant la xarxa'; String get savedUsingTheNetwork => 'Desat utilitzant la xarxa';
@override @override
String get savedUsingLocalStorage => 'Desat en l\'emmagatzematge local'; String get savedUsingLocalStorage => "Desat en l'emmagatzematge local";
@override @override
String theImageHasBeenSavedAt(String imagePath) { String theImageHasBeenSavedAt(String imagePath) {
return 'La imatge s\'ha desat a: $imagePath'; return "La imatge s'ha desat a: $imagePath";
} }
@override @override
@ -238,15 +236,15 @@ class FlutterQuillLocalizationsCa extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'Si us plau, introdueix un text per al teu enllaç (p. ex., \'Saber més\')'; "Si us plau, introdueix un text per al teu enllaç (p. ex., 'Saber més')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'Si us plau, introdueix l\'URL de l\'enllaç (p. ex., \'https://example.com\')'; "Si us plau, introdueix l'URL de l'enllaç (p. ex., 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => String get pleaseEnterAValidImageURL =>
'Si us plau, introdueix un URL d\'imatge vàlid'; "Si us plau, introdueix un URL d'imatge vàlid";
@override @override
String get pleaseEnterAValidVideoURL => String get pleaseEnterAValidVideoURL =>

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Czech (`cs`). /// The translations for Czech (`cs`).
class FlutterQuillLocalizationsCs extends FlutterQuillLocalizations { class FlutterQuillLocalizationsCs extends FlutterQuillLocalizations {
FlutterQuillLocalizationsCs([String locale = 'cs']) : super(locale); FlutterQuillLocalizationsCs([super.locale = 'cs']);
@override @override
String get pasteLink => 'Vložit odkaz'; String get pasteLink => 'Vložit odkaz';
@ -238,11 +236,11 @@ class FlutterQuillLocalizationsCs extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'Zadejte text pro váš odkaz (např., \'Dozvědět se více\')'; "Zadejte text pro váš odkaz (např., 'Dozvědět se více')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'Zadejte URL odkazu (např., \'https://example.com\')'; "Zadejte URL odkazu (např., 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => 'Zadejte platnou URL adresu obrázku'; String get pleaseEnterAValidImageURL => 'Zadejte platnou URL adresu obrázku';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Danish (`da`). /// The translations for Danish (`da`).
class FlutterQuillLocalizationsDa extends FlutterQuillLocalizations { class FlutterQuillLocalizationsDa extends FlutterQuillLocalizations {
FlutterQuillLocalizationsDa([String locale = 'da']) : super(locale); FlutterQuillLocalizationsDa([super.locale = 'da']);
@override @override
String get pasteLink => 'Indsæt link'; String get pasteLink => 'Indsæt link';
@ -237,10 +235,10 @@ class FlutterQuillLocalizationsDa extends FlutterQuillLocalizations {
String get errorWhileSavingImage => 'Error while saving image'; String get errorWhileSavingImage => 'Error while saving image';
@override @override
String get pleaseEnterTextForYourLink => 'e.g., \'Learn more\''; String get pleaseEnterTextForYourLink => "e.g., 'Learn more'";
@override @override
String get pleaseEnterTheLinkURL => 'e.g., \'https://example.com\''; String get pleaseEnterTheLinkURL => "e.g., 'https://example.com'";
@override @override
String get pleaseEnterAValidImageURL => 'Please enter a valid image URL'; String get pleaseEnterAValidImageURL => 'Please enter a valid image URL';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for German (`de`). /// The translations for German (`de`).
class FlutterQuillLocalizationsDe extends FlutterQuillLocalizations { class FlutterQuillLocalizationsDe extends FlutterQuillLocalizations {
FlutterQuillLocalizationsDe([String locale = 'de']) : super(locale); FlutterQuillLocalizationsDe([super.locale = 'de']);
@override @override
String get pasteLink => 'Link hinzufügen'; String get pasteLink => 'Link hinzufügen';
@ -237,10 +235,10 @@ class FlutterQuillLocalizationsDe extends FlutterQuillLocalizations {
String get errorWhileSavingImage => 'Fehler beim Speichern des Bildes'; String get errorWhileSavingImage => 'Fehler beim Speichern des Bildes';
@override @override
String get pleaseEnterTextForYourLink => 'z.B. \'Mehr erfahren\''; String get pleaseEnterTextForYourLink => "z.B. 'Mehr erfahren'";
@override @override
String get pleaseEnterTheLinkURL => 'z.B. \'https://example.com\''; String get pleaseEnterTheLinkURL => "z.B. 'https://example.com'";
@override @override
String get pleaseEnterAValidImageURL => String get pleaseEnterAValidImageURL =>

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for English (`en`). /// The translations for English (`en`).
class FlutterQuillLocalizationsEn extends FlutterQuillLocalizations { class FlutterQuillLocalizationsEn extends FlutterQuillLocalizations {
FlutterQuillLocalizationsEn([String locale = 'en']) : super(locale); FlutterQuillLocalizationsEn([super.locale = 'en']);
@override @override
String get pasteLink => 'Paste a link'; String get pasteLink => 'Paste a link';
@ -238,11 +236,11 @@ class FlutterQuillLocalizationsEn extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'Please enter a text for your link (e.g., \'Learn more\')'; "Please enter a text for your link (e.g., 'Learn more')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'Please enter the link URL (e.g., \'https://example.com\')'; "Please enter the link URL (e.g., 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => 'Please enter a valid image URL'; String get pleaseEnterAValidImageURL => 'Please enter a valid image URL';
@ -510,11 +508,11 @@ class FlutterQuillLocalizationsEnUs extends FlutterQuillLocalizationsEn {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'Please enter a text for your link (e.g., \'Learn more\')'; "Please enter a text for your link (e.g., 'Learn more')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'Please enter the link URL (e.g., \'https://example.com\')'; "Please enter the link URL (e.g., 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => 'Please enter a valid image URL'; String get pleaseEnterAValidImageURL => 'Please enter a valid image URL';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Spanish Castilian (`es`). /// The translations for Spanish Castilian (`es`).
class FlutterQuillLocalizationsEs extends FlutterQuillLocalizations { class FlutterQuillLocalizationsEs extends FlutterQuillLocalizations {
FlutterQuillLocalizationsEs([String locale = 'es']) : super(locale); FlutterQuillLocalizationsEs([super.locale = 'es']);
@override @override
String get pasteLink => 'Pega un enlace'; String get pasteLink => 'Pega un enlace';
@ -237,10 +235,10 @@ class FlutterQuillLocalizationsEs extends FlutterQuillLocalizations {
String get errorWhileSavingImage => 'Error al guardar imagen'; String get errorWhileSavingImage => 'Error al guardar imagen';
@override @override
String get pleaseEnterTextForYourLink => 'p.ej., \'Aprende más\''; String get pleaseEnterTextForYourLink => "p.ej., 'Aprende más'";
@override @override
String get pleaseEnterTheLinkURL => 'p.ej., \'https://example.com\''; String get pleaseEnterTheLinkURL => "p.ej., 'https://example.com'";
@override @override
String get pleaseEnterAValidImageURL => String get pleaseEnterAValidImageURL =>

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Persian (`fa`). /// The translations for Persian (`fa`).
class FlutterQuillLocalizationsFa extends FlutterQuillLocalizations { class FlutterQuillLocalizationsFa extends FlutterQuillLocalizations {
FlutterQuillLocalizationsFa([String locale = 'fa']) : super(locale); FlutterQuillLocalizationsFa([super.locale = 'fa']);
@override @override
String get pasteLink => 'جایگذاری لینک'; String get pasteLink => 'جایگذاری لینک';
@ -239,11 +237,11 @@ class FlutterQuillLocalizationsFa extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'لطفاً متن لینک خود را وارد کنید (مثال: \'بیشتر بدانید\')'; "لطفاً متن لینک خود را وارد کنید (مثال: 'بیشتر بدانید')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'لطفاً URL لینک را وارد کنید (مثال: \'https://example.com\')'; "لطفاً URL لینک را وارد کنید (مثال: 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => 'لطفاً یک URL تصویر معتبر وارد کنید'; String get pleaseEnterAValidImageURL => 'لطفاً یک URL تصویر معتبر وارد کنید';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for French (`fr`). /// The translations for French (`fr`).
class FlutterQuillLocalizationsFr extends FlutterQuillLocalizations { class FlutterQuillLocalizationsFr extends FlutterQuillLocalizations {
FlutterQuillLocalizationsFr([String locale = 'fr']) : super(locale); FlutterQuillLocalizationsFr([super.locale = 'fr']);
@override @override
String get pasteLink => 'Coller un lien'; String get pasteLink => 'Coller un lien';
@ -139,7 +137,7 @@ class FlutterQuillLocalizationsFr extends FlutterQuillLocalizations {
String get textDirection => 'Direction du texte'; String get textDirection => 'Direction du texte';
@override @override
String get headerStyle => 'Style d\'en-tête'; String get headerStyle => "Style d'en-tête";
@override @override
String get normal => 'Normal'; String get normal => 'Normal';
@ -217,10 +215,10 @@ class FlutterQuillLocalizationsFr extends FlutterQuillLocalizations {
String get findText => 'Rechercher du texte'; String get findText => 'Rechercher du texte';
@override @override
String get moveToPreviousOccurrence => 'Aller à l\'occurrence précédente'; String get moveToPreviousOccurrence => "Aller à l'occurrence précédente";
@override @override
String get moveToNextOccurrence => 'Aller à l\'occurrence suivante'; String get moveToNextOccurrence => "Aller à l'occurrence suivante";
@override @override
String get savedUsingTheNetwork => 'Enregistré via le réseau'; String get savedUsingTheNetwork => 'Enregistré via le réseau';
@ -236,17 +234,17 @@ class FlutterQuillLocalizationsFr extends FlutterQuillLocalizations {
@override @override
String get errorWhileSavingImage => String get errorWhileSavingImage =>
'Erreur lors de l\'enregistrement de l\'image'; "Erreur lors de l'enregistrement de l'image";
@override @override
String get pleaseEnterTextForYourLink => 'par exemple, \'En savoir plus\''; String get pleaseEnterTextForYourLink => "par exemple, 'En savoir plus'";
@override @override
String get pleaseEnterTheLinkURL => 'par exemple, \'https://example.com\''; String get pleaseEnterTheLinkURL => "par exemple, 'https://example.com'";
@override @override
String get pleaseEnterAValidImageURL => String get pleaseEnterAValidImageURL =>
'Veuillez saisir une URL d\'image valide'; "Veuillez saisir une URL d'image valide";
@override @override
String get pleaseEnterAValidVideoURL => String get pleaseEnterAValidVideoURL =>
@ -280,7 +278,7 @@ class FlutterQuillLocalizationsFr extends FlutterQuillLocalizations {
'Prendre une photo avec votre appareil photo'; 'Prendre une photo avec votre appareil photo';
@override @override
String get pasteAPhotoUsingALink => 'Coller une photo à l\'aide d\'un lien'; String get pasteAPhotoUsingALink => "Coller une photo à l'aide d'un lien";
@override @override
String get pickAVideoFromYourGallery => String get pickAVideoFromYourGallery =>
@ -291,7 +289,7 @@ class FlutterQuillLocalizationsFr extends FlutterQuillLocalizations {
'Enregistrez une vidéo en utilisant votre caméra'; 'Enregistrez une vidéo en utilisant votre caméra';
@override @override
String get pasteAVideoUsingALink => 'Coller une vidéo à l\'aide d\'un lien'; String get pasteAVideoUsingALink => "Coller une vidéo à l'aide d'un lien";
@override @override
String get close => 'Close'; String get close => 'Close';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Hebrew (`he`). /// The translations for Hebrew (`he`).
class FlutterQuillLocalizationsHe extends FlutterQuillLocalizations { class FlutterQuillLocalizationsHe extends FlutterQuillLocalizations {
FlutterQuillLocalizationsHe([String locale = 'he']) : super(locale); FlutterQuillLocalizationsHe([super.locale = 'he']);
@override @override
String get pasteLink => 'הדבק את הלינק'; String get pasteLink => 'הדבק את הלינק';
@ -238,11 +236,11 @@ class FlutterQuillLocalizationsHe extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'אנא הזן טקסט לקישור שלך (לדוגמה, \'מידע נוסף\')'; "אנא הזן טקסט לקישור שלך (לדוגמה, 'מידע נוסף')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'אנא הזן את כתובת ה-URL של הקישור (לדוגמה, \'https://example.com\')'; "אנא הזן את כתובת ה-URL של הקישור (לדוגמה, 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => 'אנא הזן כתובת URL תקינה של תמונה'; String get pleaseEnterAValidImageURL => 'אנא הזן כתובת URL תקינה של תמונה';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Hindi (`hi`). /// The translations for Hindi (`hi`).
class FlutterQuillLocalizationsHi extends FlutterQuillLocalizations { class FlutterQuillLocalizationsHi extends FlutterQuillLocalizations {
FlutterQuillLocalizationsHi([String locale = 'hi']) : super(locale); FlutterQuillLocalizationsHi([super.locale = 'hi']);
@override @override
String get pasteLink => 'िक पट कर'; String get pasteLink => 'िक पट कर';
@ -239,11 +237,11 @@ class FlutterQuillLocalizationsHi extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'पय अपनिक किए एक पठ दरज कर (उदहरण: \'और अधिक ज\')'; "पय अपनिक किए एक पठ दरज कर (उदहरण: 'और अधिक ज')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'पयिक URL दरज कर (उदहरण: \'https://example.com\')'; "पयिक URL दरज कर (उदहरण: 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => 'पय एक वध चिर URL दरज कर'; String get pleaseEnterAValidImageURL => 'पय एक वध चिर URL दरज कर';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Indonesian (`id`). /// The translations for Indonesian (`id`).
class FlutterQuillLocalizationsId extends FlutterQuillLocalizations { class FlutterQuillLocalizationsId extends FlutterQuillLocalizations {
FlutterQuillLocalizationsId([String locale = 'id']) : super(locale); FlutterQuillLocalizationsId([super.locale = 'id']);
@override @override
String get pasteLink => 'Tempel tautan'; String get pasteLink => 'Tempel tautan';
@ -239,11 +237,11 @@ class FlutterQuillLocalizationsId extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'Harap masukkan teks untuk tautan Anda (contoh: \'Pelajari lebih lanjut\')'; "Harap masukkan teks untuk tautan Anda (contoh: 'Pelajari lebih lanjut')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'Harap masukkan URL tautan (contoh: \'https://example.com\')'; "Harap masukkan URL tautan (contoh: 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => String get pleaseEnterAValidImageURL =>

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Italian (`it`). /// The translations for Italian (`it`).
class FlutterQuillLocalizationsIt extends FlutterQuillLocalizations { class FlutterQuillLocalizationsIt extends FlutterQuillLocalizations {
FlutterQuillLocalizationsIt([String locale = 'it']) : super(locale); FlutterQuillLocalizationsIt([super.locale = 'it']);
@override @override
String get pasteLink => 'Incolla un collegamento'; String get pasteLink => 'Incolla un collegamento';
@ -217,10 +215,10 @@ class FlutterQuillLocalizationsIt extends FlutterQuillLocalizations {
String get findText => 'Trova testo'; String get findText => 'Trova testo';
@override @override
String get moveToPreviousOccurrence => 'Vai all\'occorrenza precedente'; String get moveToPreviousOccurrence => "Vai all'occorrenza precedente";
@override @override
String get moveToNextOccurrence => 'Vai all\'occorrenza successiva'; String get moveToNextOccurrence => "Vai all'occorrenza successiva";
@override @override
String get savedUsingTheNetwork => 'Salvato utilizzando la rete'; String get savedUsingTheNetwork => 'Salvato utilizzando la rete';
@ -236,15 +234,15 @@ class FlutterQuillLocalizationsIt extends FlutterQuillLocalizations {
@override @override
String get errorWhileSavingImage => String get errorWhileSavingImage =>
'Errore durante il salvataggio dell\'immagine'; "Errore durante il salvataggio dell'immagine";
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'Inserisci un testo per il tuo link (ad esempio, \'Per saperne di più\')'; "Inserisci un testo per il tuo link (ad esempio, 'Per saperne di più')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'Inserisci l\'URL del link (ad esempio, \'https://example.com\')'; "Inserisci l'URL del link (ad esempio, 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => 'Inserisci un URL di immagine valido'; String get pleaseEnterAValidImageURL => 'Inserisci un URL di immagine valido';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Japanese (`ja`). /// The translations for Japanese (`ja`).
class FlutterQuillLocalizationsJa extends FlutterQuillLocalizations { class FlutterQuillLocalizationsJa extends FlutterQuillLocalizations {
FlutterQuillLocalizationsJa([String locale = 'ja']) : super(locale); FlutterQuillLocalizationsJa([super.locale = 'ja']);
@override @override
String get pasteLink => 'リンクをペースト'; String get pasteLink => 'リンクをペースト';
@ -237,10 +235,10 @@ class FlutterQuillLocalizationsJa extends FlutterQuillLocalizations {
String get errorWhileSavingImage => '画像の保存中にエラーが発生しました'; String get errorWhileSavingImage => '画像の保存中にエラーが発生しました';
@override @override
String get pleaseEnterTextForYourLink => '例: \'Learn more\''; String get pleaseEnterTextForYourLink => "例: 'Learn more'";
@override @override
String get pleaseEnterTheLinkURL => '例: \'https://example.com\''; String get pleaseEnterTheLinkURL => "例: 'https://example.com'";
@override @override
String get pleaseEnterAValidImageURL => '有効な画像URLを入力してください'; String get pleaseEnterAValidImageURL => '有効な画像URLを入力してください';

@ -1,304 +0,0 @@
import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Khmer Central Khmer (`km`).
class FlutterQuillLocalizationsKm extends FlutterQuillLocalizations {
FlutterQuillLocalizationsKm([String locale = 'km']) : super(locale);
@override
String get pasteLink => 'Paste a link';
@override
String get ok => 'Ok';
@override
String get selectColor => 'Select Color';
@override
String get gallery => 'Gallery';
@override
String get link => 'Link';
@override
String get open => 'Open';
@override
String get copy => 'Copy';
@override
String get remove => 'Remove';
@override
String get save => 'Save';
@override
String get zoom => 'Zoom';
@override
String get saved => 'Saved';
@override
String get text => 'Text';
@override
String get resize => 'Resize';
@override
String get width => 'Width';
@override
String get height => 'Height';
@override
String get size => 'Size';
@override
String get small => 'Small';
@override
String get large => 'Large';
@override
String get huge => 'Huge';
@override
String get clear => 'Clear';
@override
String get font => 'Font';
@override
String get search => 'Search';
@override
String get camera => 'Camera';
@override
String get video => 'Video';
@override
String get undo => 'Undo';
@override
String get redo => 'Redo';
@override
String get fontFamily => 'Font family';
@override
String get fontSize => 'Font size';
@override
String get bold => 'Bold';
@override
String get subscript => 'Subscript';
@override
String get superscript => 'Superscript';
@override
String get italic => 'Italic';
@override
String get underline => 'Underline';
@override
String get strikeThrough => 'Strike through';
@override
String get inlineCode => 'Inline code';
@override
String get fontColor => 'Font color';
@override
String get backgroundColor => 'Background color';
@override
String get clearFormat => 'Clear format';
@override
String get alignLeft => 'Align left';
@override
String get alignCenter => 'Align center';
@override
String get alignRight => 'Align right';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Justify win width';
@override
String get textDirection => 'Text direction';
@override
String get headerStyle => 'Header style';
@override
String get normal => 'Normal';
@override
String get heading1 => 'Heading 1';
@override
String get heading2 => 'Heading 2';
@override
String get heading3 => 'Heading 3';
@override
String get heading4 => 'Heading 4';
@override
String get heading5 => 'Heading 5';
@override
String get heading6 => 'Heading 6';
@override
String get numberedList => 'Numbered list';
@override
String get bulletList => 'Bullet list';
@override
String get checkedList => 'Checked list';
@override
String get codeBlock => 'Code block';
@override
String get quote => 'Quote';
@override
String get increaseIndent => 'Increase indent';
@override
String get decreaseIndent => 'Decrease indent';
@override
String get insertURL => 'Insert URL';
@override
String get visitLink => 'Visit link';
@override
String get enterLink => 'Enter link';
@override
String get enterMedia => 'Enter media';
@override
String get edit => 'Edit';
@override
String get apply => 'Apply';
@override
String get hex => 'Hex';
@override
String get material => 'Material';
@override
String get color => 'Color';
@override
String get lineheight => 'Line height';
@override
String get findText => 'Find text';
@override
String get moveToPreviousOccurrence => 'Move to previous occurrence';
@override
String get moveToNextOccurrence => 'Move to next occurrence';
@override
String get savedUsingTheNetwork => 'Saved using the network';
@override
String get savedUsingLocalStorage => 'Saved using the local storage';
@override
String theImageHasBeenSavedAt(String imagePath) {
return 'The image has been saved at: $imagePath';
}
@override
String get errorWhileSavingImage => 'Error while saving image';
@override
String get pleaseEnterTextForYourLink =>
'Please enter a text for your link (e.g., \'Learn more\')';
@override
String get pleaseEnterTheLinkURL =>
'Please enter the link URL (e.g., \'https://example.com\')';
@override
String get pleaseEnterAValidImageURL => 'Please enter a valid image URL';
@override
String get pleaseEnterAValidVideoURL => 'Please enter a valid video url';
@override
String get photo => 'Photo';
@override
String get image => 'Image';
@override
String get caseSensitivityAndWholeWordSearch =>
'Case sensitivity and whole word search';
@override
String get caseSensitive => 'Case sensitive';
@override
String get wholeWord => 'Whole word';
@override
String get insertImage => 'Insert image';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera => 'Take a photo using your camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera => 'Record a video using your camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
@override
String get close => 'Close';
@override
String get searchSettings => 'Search settings';
@override
String get cut => 'Cut';
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Korean (`ko`). /// The translations for Korean (`ko`).
class FlutterQuillLocalizationsKo extends FlutterQuillLocalizations { class FlutterQuillLocalizationsKo extends FlutterQuillLocalizations {
FlutterQuillLocalizationsKo([String locale = 'ko']) : super(locale); FlutterQuillLocalizationsKo([super.locale = 'ko']);
@override @override
String get pasteLink => '링크를 붙여 넣어 주세요'; String get pasteLink => '링크를 붙여 넣어 주세요';
@ -240,7 +238,7 @@ class FlutterQuillLocalizationsKo extends FlutterQuillLocalizations {
String get pleaseEnterTextForYourLink => '링크 제목 입력'; String get pleaseEnterTextForYourLink => '링크 제목 입력';
@override @override
String get pleaseEnterTheLinkURL => '예시) \'https://example.com\''; String get pleaseEnterTheLinkURL => "예시) 'https://example.com'";
@override @override
String get pleaseEnterAValidImageURL => '유효한 이미지 URL을 입력하세요'; String get pleaseEnterAValidImageURL => '유효한 이미지 URL을 입력하세요';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Kurdish (`ku`). /// The translations for Kurdish (`ku`).
class FlutterQuillLocalizationsKu extends FlutterQuillLocalizations { class FlutterQuillLocalizationsKu extends FlutterQuillLocalizations {
FlutterQuillLocalizationsKu([String locale = 'ku']) : super(locale); FlutterQuillLocalizationsKu([super.locale = 'ku']);
@override @override
String get pasteLink => 'لینک دابنێ'; String get pasteLink => 'لینک دابنێ';
@ -238,11 +236,11 @@ class FlutterQuillLocalizationsKu extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'تکایە دەقێک بۆ بەستەرەکەت دابنێ (بۆ نموونە، \'زیاتر بزانە\')'; "تکایە دەقێک بۆ بەستەرەکەت دابنێ (بۆ نموونە، 'زیاتر بزانە')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'تکایە لینکێک بۆ بەستەرەکە دابنێ (بۆ نموونە، \'https://example.com\')'; "تکایە لینکێک بۆ بەستەرەکە دابنێ (بۆ نموونە، 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => 'تکایە لینکی وێنەی دروست دابنێ'; String get pleaseEnterAValidImageURL => 'تکایە لینکی وێنەی دروست دابنێ';
@ -540,11 +538,11 @@ class FlutterQuillLocalizationsKuCkb extends FlutterQuillLocalizationsKu {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'تکایە دەقێک بۆ بەستەرەکەت دابنێ (بۆ نموونە، \'زیاتر بزانە\')'; "تکایە دەقێک بۆ بەستەرەکەت دابنێ (بۆ نموونە، 'زیاتر بزانە')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'تکایە لینکێک بۆ بەستەرەکە دابنێ (بۆ نموونە، \'https://example.com\')'; "تکایە لینکێک بۆ بەستەرەکە دابنێ (بۆ نموونە، 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => 'تکایە لینکی وێنەی دروست دابنێ'; String get pleaseEnterAValidImageURL => 'تکایە لینکی وێنەی دروست دابنێ';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Malay (`ms`). /// The translations for Malay (`ms`).
class FlutterQuillLocalizationsMs extends FlutterQuillLocalizations { class FlutterQuillLocalizationsMs extends FlutterQuillLocalizations {
FlutterQuillLocalizationsMs([String locale = 'ms']) : super(locale); FlutterQuillLocalizationsMs([super.locale = 'ms']);
@override @override
String get pasteLink => 'Tampal Pautan'; String get pasteLink => 'Tampal Pautan';
@ -238,11 +236,11 @@ class FlutterQuillLocalizationsMs extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'Sila masukkan teks untuk pautan anda (contoh, \'Ketahui lebih lanjut\')'; "Sila masukkan teks untuk pautan anda (contoh, 'Ketahui lebih lanjut')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'Sila masukkan URL pautan (contoh, \'https://example.com\')'; "Sila masukkan URL pautan (contoh, 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => 'Sila masukkan URL imej yang sah'; String get pleaseEnterAValidImageURL => 'Sila masukkan URL imej yang sah';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Nepali (`ne`). /// The translations for Nepali (`ne`).
class FlutterQuillLocalizationsNe extends FlutterQuillLocalizations { class FlutterQuillLocalizationsNe extends FlutterQuillLocalizations {
FlutterQuillLocalizationsNe([String locale = 'ne']) : super(locale); FlutterQuillLocalizationsNe([super.locale = 'ne']);
@override @override
String get pasteLink => 'िक पट गर'; String get pasteLink => 'िक पट गर';
@ -238,11 +236,11 @@ class FlutterQuillLocalizationsNe extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'पय आफिककिठ परविट गर (जस, \'थप ज\')'; "पय आफिककिठ परविट गर (जस, 'थप ज')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'पयिक URL परविट गर (जस, \'https://example.com\')'; "पयिक URL परविट गर (जस, 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => String get pleaseEnterAValidImageURL =>

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Dutch Flemish (`nl`). /// The translations for Dutch Flemish (`nl`).
class FlutterQuillLocalizationsNl extends FlutterQuillLocalizations { class FlutterQuillLocalizationsNl extends FlutterQuillLocalizations {
FlutterQuillLocalizationsNl([String locale = 'nl']) : super(locale); FlutterQuillLocalizationsNl([super.locale = 'nl']);
@override @override
String get pasteLink => 'Plak een link'; String get pasteLink => 'Plak een link';
@ -238,11 +236,11 @@ class FlutterQuillLocalizationsNl extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'Voer tekst in voor uw link (bijvoorbeeld \'Meer weten\')'; "Voer tekst in voor uw link (bijvoorbeeld 'Meer weten')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'Voer de URL van de link in (bijvoorbeeld \'https://example.com\')'; "Voer de URL van de link in (bijvoorbeeld 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => String get pleaseEnterAValidImageURL =>

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Norwegian (`no`). /// The translations for Norwegian (`no`).
class FlutterQuillLocalizationsNo extends FlutterQuillLocalizations { class FlutterQuillLocalizationsNo extends FlutterQuillLocalizations {
FlutterQuillLocalizationsNo([String locale = 'no']) : super(locale); FlutterQuillLocalizationsNo([super.locale = 'no']);
@override @override
String get pasteLink => 'Lim inn lenke'; String get pasteLink => 'Lim inn lenke';
@ -238,11 +236,11 @@ class FlutterQuillLocalizationsNo extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'Vennligst skriv inn tekst for lenken din (for eksempel \'Lær mer\')'; "Vennligst skriv inn tekst for lenken din (for eksempel 'Lær mer')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'Vennligst skriv inn lenkens URL (for eksempel \'https://example.com\')'; "Vennligst skriv inn lenkens URL (for eksempel 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => String get pleaseEnterAValidImageURL =>

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Polish (`pl`). /// The translations for Polish (`pl`).
class FlutterQuillLocalizationsPl extends FlutterQuillLocalizations { class FlutterQuillLocalizationsPl extends FlutterQuillLocalizations {
FlutterQuillLocalizationsPl([String locale = 'pl']) : super(locale); FlutterQuillLocalizationsPl([super.locale = 'pl']);
@override @override
String get pasteLink => 'Wklej link'; String get pasteLink => 'Wklej link';
@ -237,10 +235,10 @@ class FlutterQuillLocalizationsPl extends FlutterQuillLocalizations {
String get errorWhileSavingImage => 'Error while saving image'; String get errorWhileSavingImage => 'Error while saving image';
@override @override
String get pleaseEnterTextForYourLink => 'e.g., \'Learn more\''; String get pleaseEnterTextForYourLink => "e.g., 'Learn more'";
@override @override
String get pleaseEnterTheLinkURL => 'e.g., \'https://example.com\''; String get pleaseEnterTheLinkURL => "e.g., 'https://example.com'";
@override @override
String get pleaseEnterAValidImageURL => 'Please enter a valid image URL'; String get pleaseEnterAValidImageURL => 'Please enter a valid image URL';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Portuguese (`pt`). /// The translations for Portuguese (`pt`).
class FlutterQuillLocalizationsPt extends FlutterQuillLocalizations { class FlutterQuillLocalizationsPt extends FlutterQuillLocalizations {
FlutterQuillLocalizationsPt([String locale = 'pt']) : super(locale); FlutterQuillLocalizationsPt([super.locale = 'pt']);
@override @override
String get pasteLink => 'Colar um link'; String get pasteLink => 'Colar um link';
@ -238,10 +236,10 @@ class FlutterQuillLocalizationsPt extends FlutterQuillLocalizations {
String get errorWhileSavingImage => 'Erro a gravar imagem'; String get errorWhileSavingImage => 'Erro a gravar imagem';
@override @override
String get pleaseEnterTextForYourLink => 'e.g., \'Learn more\''; String get pleaseEnterTextForYourLink => "e.g., 'Learn more'";
@override @override
String get pleaseEnterTheLinkURL => 'e.g., \'https://example.com\''; String get pleaseEnterTheLinkURL => "e.g., 'https://example.com'";
@override @override
String get pleaseEnterAValidImageURL => 'Please enter a valid image URL'; String get pleaseEnterAValidImageURL => 'Please enter a valid image URL';
@ -509,10 +507,10 @@ class FlutterQuillLocalizationsPtBr extends FlutterQuillLocalizationsPt {
String get errorWhileSavingImage => 'Error while saving image'; String get errorWhileSavingImage => 'Error while saving image';
@override @override
String get pleaseEnterTextForYourLink => 'e.g., \'Learn more\''; String get pleaseEnterTextForYourLink => "e.g., 'Learn more'";
@override @override
String get pleaseEnterTheLinkURL => 'e.g., \'https://example.com\''; String get pleaseEnterTheLinkURL => "e.g., 'https://example.com'";
@override @override
String get pleaseEnterAValidImageURL => 'Please enter a valid image URL'; String get pleaseEnterAValidImageURL => 'Please enter a valid image URL';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Romanian Moldavian Moldovan (`ro`). /// The translations for Romanian Moldavian Moldovan (`ro`).
class FlutterQuillLocalizationsRo extends FlutterQuillLocalizations { class FlutterQuillLocalizationsRo extends FlutterQuillLocalizations {
FlutterQuillLocalizationsRo([String locale = 'ro']) : super(locale); FlutterQuillLocalizationsRo([super.locale = 'ro']);
@override @override
String get pasteLink => 'Lipește un link'; String get pasteLink => 'Lipește un link';
@ -238,11 +236,11 @@ class FlutterQuillLocalizationsRo extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'Vă rugăm să introduceți un text pentru link-ul dvs. (de exemplu, \'Aflați mai multe\')'; "Vă rugăm să introduceți un text pentru link-ul dvs. (de exemplu, 'Aflați mai multe')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'Vă rugăm să introduceți URL-ul link-ului (de exemplu, \'https://example.com\')'; "Vă rugăm să introduceți URL-ul link-ului (de exemplu, 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => String get pleaseEnterAValidImageURL =>
@ -534,11 +532,11 @@ class FlutterQuillLocalizationsRoRo extends FlutterQuillLocalizationsRo {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'Vă rugăm să introduceți un text pentru link-ul dvs. (de exemplu, \'Aflați mai multe\')'; "Vă rugăm să introduceți un text pentru link-ul dvs. (de exemplu, 'Aflați mai multe')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'Vă rugăm să introduceți URL-ul link-ului (de exemplu, \'https://example.com\')'; "Vă rugăm să introduceți URL-ul link-ului (de exemplu, 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => String get pleaseEnterAValidImageURL =>

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Russian (`ru`). /// The translations for Russian (`ru`).
class FlutterQuillLocalizationsRu extends FlutterQuillLocalizations { class FlutterQuillLocalizationsRu extends FlutterQuillLocalizations {
FlutterQuillLocalizationsRu([String locale = 'ru']) : super(locale); FlutterQuillLocalizationsRu([super.locale = 'ru']);
@override @override
String get pasteLink => 'Вставить ссылку'; String get pasteLink => 'Вставить ссылку';
@ -238,10 +236,10 @@ class FlutterQuillLocalizationsRu extends FlutterQuillLocalizations {
String get errorWhileSavingImage => 'Ошибка при сохранении изображения'; String get errorWhileSavingImage => 'Ошибка при сохранении изображения';
@override @override
String get pleaseEnterTextForYourLink => 'например, \'Узнать больше\''; String get pleaseEnterTextForYourLink => "например, 'Узнать больше'";
@override @override
String get pleaseEnterTheLinkURL => 'например, \'https://example.com\''; String get pleaseEnterTheLinkURL => "например, 'https://example.com'";
@override @override
String get pleaseEnterAValidImageURL => String get pleaseEnterAValidImageURL =>

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Slovak (`sk`). /// The translations for Slovak (`sk`).
class FlutterQuillLocalizationsSk extends FlutterQuillLocalizations { class FlutterQuillLocalizationsSk extends FlutterQuillLocalizations {
FlutterQuillLocalizationsSk([String locale = 'sk']) : super(locale); FlutterQuillLocalizationsSk([super.locale = 'sk']);
@override @override
String get pasteLink => 'Vložiť odkaz'; String get pasteLink => 'Vložiť odkaz';
@ -238,11 +236,11 @@ class FlutterQuillLocalizationsSk extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'Prosím zadajte text pre váš odkaz (napr. \'Ďalšie informácie\')'; "Prosím zadajte text pre váš odkaz (napr. 'Ďalšie informácie')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'Prosím zadajte URL odkazu (napr. \'https://example.com\')'; "Prosím zadajte URL odkazu (napr. 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => String get pleaseEnterAValidImageURL =>

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Serbian (`sr`). /// The translations for Serbian (`sr`).
class FlutterQuillLocalizationsSr extends FlutterQuillLocalizations { class FlutterQuillLocalizationsSr extends FlutterQuillLocalizations {
FlutterQuillLocalizationsSr([String locale = 'sr']) : super(locale); FlutterQuillLocalizationsSr([super.locale = 'sr']);
@override @override
String get pasteLink => 'Nalepi vezu'; String get pasteLink => 'Nalepi vezu';
@ -239,11 +237,11 @@ class FlutterQuillLocalizationsSr extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'Unesite tekst za svoj link (na primer, \'Saznajte više\')'; "Unesite tekst za svoj link (na primer, 'Saznajte više')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'Unesite URL linka (na primer, \'https://example.com\')'; "Unesite URL linka (na primer, 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => 'Unesite važeći URL slike'; String get pleaseEnterAValidImageURL => 'Unesite važeći URL slike';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Swedish (`sv`). /// The translations for Swedish (`sv`).
class FlutterQuillLocalizationsSv extends FlutterQuillLocalizations { class FlutterQuillLocalizationsSv extends FlutterQuillLocalizations {
FlutterQuillLocalizationsSv([String locale = 'sv']) : super(locale); FlutterQuillLocalizationsSv([super.locale = 'sv']);
@override @override
String get pasteLink => 'Klistra in länk'; String get pasteLink => 'Klistra in länk';
@ -238,11 +236,11 @@ class FlutterQuillLocalizationsSv extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'Ange text för din länk (t.ex. \'Lär dig mer\')'; "Ange text för din länk (t.ex. 'Lär dig mer')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'Ange URL för länken (t.ex. \'https://example.com\')'; "Ange URL för länken (t.ex. 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => 'Ange en giltig bild-URL'; String get pleaseEnterAValidImageURL => 'Ange en giltig bild-URL';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Swahili (`sw`). /// The translations for Swahili (`sw`).
class FlutterQuillLocalizationsSw extends FlutterQuillLocalizations { class FlutterQuillLocalizationsSw extends FlutterQuillLocalizations {
FlutterQuillLocalizationsSw([String locale = 'sw']) : super(locale); FlutterQuillLocalizationsSw([super.locale = 'sw']);
@override @override
String get pasteLink => 'Bandika Kiungo'; String get pasteLink => 'Bandika Kiungo';
@ -237,10 +235,10 @@ class FlutterQuillLocalizationsSw extends FlutterQuillLocalizations {
String get errorWhileSavingImage => 'Hitilafu Wakati wa Kuhifadhi Picha'; String get errorWhileSavingImage => 'Hitilafu Wakati wa Kuhifadhi Picha';
@override @override
String get pleaseEnterTextForYourLink => 'Kwa mfano, \'Jifunze zaidi\''; String get pleaseEnterTextForYourLink => "Kwa mfano, 'Jifunze zaidi'";
@override @override
String get pleaseEnterTheLinkURL => 'Kwa mfano, \'https://example.com\''; String get pleaseEnterTheLinkURL => "Kwa mfano, 'https://example.com'";
@override @override
String get pleaseEnterAValidImageURL => String get pleaseEnterAValidImageURL =>

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Turkmen (`tk`). /// The translations for Turkmen (`tk`).
class FlutterQuillLocalizationsTk extends FlutterQuillLocalizations { class FlutterQuillLocalizationsTk extends FlutterQuillLocalizations {
FlutterQuillLocalizationsTk([String locale = 'tk']) : super(locale); FlutterQuillLocalizationsTk([super.locale = 'tk']);
@override @override
String get pasteLink => 'Baglanyşygy goýuň'; String get pasteLink => 'Baglanyşygy goýuň';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Turkish (`tr`). /// The translations for Turkish (`tr`).
class FlutterQuillLocalizationsTr extends FlutterQuillLocalizations { class FlutterQuillLocalizationsTr extends FlutterQuillLocalizations {
FlutterQuillLocalizationsTr([String locale = 'tr']) : super(locale); FlutterQuillLocalizationsTr([super.locale = 'tr']);
@override @override
String get pasteLink => 'Bağlantıyı Yapıştır'; String get pasteLink => 'Bağlantıyı Yapıştır';
@ -237,17 +235,17 @@ class FlutterQuillLocalizationsTr extends FlutterQuillLocalizations {
String get errorWhileSavingImage => 'Error while saving image'; String get errorWhileSavingImage => 'Error while saving image';
@override @override
String get pleaseEnterTextForYourLink => 'e.g., \'Learn more\''; String get pleaseEnterTextForYourLink => "e.g., 'Learn more'";
@override @override
String get pleaseEnterTheLinkURL => 'e.g., \'https://example.com\''; String get pleaseEnterTheLinkURL => "e.g., 'https://example.com'";
@override @override
String get pleaseEnterAValidImageURL => 'Please enter a valid image URL'; String get pleaseEnterAValidImageURL => 'Please enter a valid image URL';
@override @override
String get pleaseEnterAValidVideoURL => String get pleaseEnterAValidVideoURL =>
'Lütfen geçerli bir video URL\'si girin'; "Lütfen geçerli bir video URL'si girin";
@override @override
String get photo => 'Fotoğraf'; String get photo => 'Fotoğraf';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Ukrainian (`uk`). /// The translations for Ukrainian (`uk`).
class FlutterQuillLocalizationsUk extends FlutterQuillLocalizations { class FlutterQuillLocalizationsUk extends FlutterQuillLocalizations {
FlutterQuillLocalizationsUk([String locale = 'uk']) : super(locale); FlutterQuillLocalizationsUk([super.locale = 'uk']);
@override @override
String get pasteLink => 'Вставити посилання'; String get pasteLink => 'Вставити посилання';
@ -238,10 +236,10 @@ class FlutterQuillLocalizationsUk extends FlutterQuillLocalizations {
String get errorWhileSavingImage => 'Помилка при збереженні зображення'; String get errorWhileSavingImage => 'Помилка при збереженні зображення';
@override @override
String get pleaseEnterTextForYourLink => 'Наприклад, \'Дізнатися більше\''; String get pleaseEnterTextForYourLink => "Наприклад, 'Дізнатися більше'";
@override @override
String get pleaseEnterTheLinkURL => 'Наприклад, \'https://example.com\''; String get pleaseEnterTheLinkURL => "Наприклад, 'https://example.com'";
@override @override
String get pleaseEnterAValidImageURL => String get pleaseEnterAValidImageURL =>

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Urdu (`ur`). /// The translations for Urdu (`ur`).
class FlutterQuillLocalizationsUr extends FlutterQuillLocalizations { class FlutterQuillLocalizationsUr extends FlutterQuillLocalizations {
FlutterQuillLocalizationsUr([String locale = 'ur']) : super(locale); FlutterQuillLocalizationsUr([super.locale = 'ur']);
@override @override
String get pasteLink => 'لنک پیسٹ کریں'; String get pasteLink => 'لنک پیسٹ کریں';
@ -239,11 +237,11 @@ class FlutterQuillLocalizationsUr extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'براہ کرم اپنے لنک کے لیے متن درج کریں (مثال کے طور پر، \'مزید جانیں\')'; "براہ کرم اپنے لنک کے لیے متن درج کریں (مثال کے طور پر، 'مزید جانیں')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'براہ کرم لنک کا URL درج کریں (مثال کے طور پر، \'https://example.com\')'; "براہ کرم لنک کا URL درج کریں (مثال کے طور پر، 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => String get pleaseEnterAValidImageURL =>

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Vietnamese (`vi`). /// The translations for Vietnamese (`vi`).
class FlutterQuillLocalizationsVi extends FlutterQuillLocalizations { class FlutterQuillLocalizationsVi extends FlutterQuillLocalizations {
FlutterQuillLocalizationsVi([String locale = 'vi']) : super(locale); FlutterQuillLocalizationsVi([super.locale = 'vi']);
@override @override
String get pasteLink => 'Chèn liên kết'; String get pasteLink => 'Chèn liên kết';
@ -238,11 +236,11 @@ class FlutterQuillLocalizationsVi extends FlutterQuillLocalizations {
@override @override
String get pleaseEnterTextForYourLink => String get pleaseEnterTextForYourLink =>
'Vui lòng nhập văn bản cho liên kết của bạn (ví dụ: \'Tìm hiểu thêm\')'; "Vui lòng nhập văn bản cho liên kết của bạn (ví dụ: 'Tìm hiểu thêm')";
@override @override
String get pleaseEnterTheLinkURL => String get pleaseEnterTheLinkURL =>
'Vui lòng nhập URL của liên kết (ví dụ: \'https://example.com\')'; "Vui lòng nhập URL của liên kết (ví dụ: 'https://example.com')";
@override @override
String get pleaseEnterAValidImageURL => 'Vui lòng nhập URL hình ảnh hợp lệ'; String get pleaseEnterAValidImageURL => 'Vui lòng nhập URL hình ảnh hợp lệ';

@ -1,10 +1,8 @@
import 'quill_localizations.dart'; import 'quill_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Chinese (`zh`). /// The translations for Chinese (`zh`).
class FlutterQuillLocalizationsZh extends FlutterQuillLocalizations { class FlutterQuillLocalizationsZh extends FlutterQuillLocalizations {
FlutterQuillLocalizationsZh([String locale = 'zh']) : super(locale); FlutterQuillLocalizationsZh([super.locale = 'zh']);
@override @override
String get pasteLink => '粘贴链接'; String get pasteLink => '粘贴链接';
@ -237,10 +235,10 @@ class FlutterQuillLocalizationsZh extends FlutterQuillLocalizations {
String get errorWhileSavingImage => '保存图像时发生错误'; String get errorWhileSavingImage => '保存图像时发生错误';
@override @override
String get pleaseEnterTextForYourLink => '\'了解更多\''; String get pleaseEnterTextForYourLink => "如'了解更多'";
@override @override
String get pleaseEnterTheLinkURL => '\'https://example.com\''; String get pleaseEnterTheLinkURL => "如'https://example.com'";
@override @override
String get pleaseEnterAValidImageURL => '请输入有效的图像URL'; String get pleaseEnterAValidImageURL => '请输入有效的图像URL';
@ -527,10 +525,10 @@ class FlutterQuillLocalizationsZhCn extends FlutterQuillLocalizationsZh {
String get errorWhileSavingImage => '保存图像时发生错误'; String get errorWhileSavingImage => '保存图像时发生错误';
@override @override
String get pleaseEnterTextForYourLink => '\'了解更多\''; String get pleaseEnterTextForYourLink => "如'了解更多'";
@override @override
String get pleaseEnterTheLinkURL => '\'https://example.com\''; String get pleaseEnterTheLinkURL => "如'https://example.com'";
@override @override
String get pleaseEnterAValidImageURL => '请输入有效的图像URL'; String get pleaseEnterAValidImageURL => '请输入有效的图像URL';
@ -775,10 +773,10 @@ class FlutterQuillLocalizationsZhHk extends FlutterQuillLocalizationsZh {
String get errorWhileSavingImage => '保存圖像時發生錯誤'; String get errorWhileSavingImage => '保存圖像時發生錯誤';
@override @override
String get pleaseEnterTextForYourLink => '例如,\'了解更多\''; String get pleaseEnterTextForYourLink => "例如,'了解更多'";
@override @override
String get pleaseEnterTheLinkURL => '例如,\'https://example.com\''; String get pleaseEnterTheLinkURL => "例如,'https://example.com'";
@override @override
String get pleaseEnterAValidImageURL => '請輸入有效的圖像URL'; String get pleaseEnterAValidImageURL => '請輸入有效的圖像URL';

@ -1,112 +0,0 @@
{
"@@locale": "km",
"pasteLink": "Paste a link",
"ok": "Ok",
"selectColor": "Select Color",
"gallery": "Gallery",
"link": "Link",
"open": "Open",
"copy": "Copy",
"remove": "Remove",
"save": "Save",
"zoom": "Zoom",
"saved": "Saved",
"text": "Text",
"resize": "Resize",
"width": "Width",
"height": "Height",
"size": "Size",
"small": "Small",
"large": "Large",
"huge": "Huge",
"clear": "Clear",
"font": "Font",
"search": "Search",
"camera": "Camera",
"video": "Video",
"undo": "Undo",
"redo": "Redo",
"fontFamily": "Font family",
"fontSize": "Font size",
"bold": "Bold",
"subscript": "Subscript",
"superscript": "Superscript",
"italic": "Italic",
"underline": "Underline",
"strikeThrough": "Strike through",
"inlineCode": "Inline code",
"fontColor": "Font color",
"backgroundColor": "Background color",
"clearFormat": "Clear format",
"alignLeft": "Align left",
"alignCenter": "Align center",
"alignRight": "Align right",
"alignJustify": "Align justify",
"@alignJustify": {
"description": "Justify the text over the full window width"
},
"justifyWinWidth": "Justify win width",
"textDirection": "Text direction",
"headerStyle": "Header style",
"normal": "Normal",
"heading1": "Heading 1",
"heading2": "Heading 2",
"heading3": "Heading 3",
"heading4": "Heading 4",
"heading5": "Heading 5",
"heading6": "Heading 6",
"numberedList": "Numbered list",
"bulletList": "Bullet list",
"checkedList": "Checked list",
"codeBlock": "Code block",
"quote": "Quote",
"increaseIndent": "Increase indent",
"decreaseIndent": "Decrease indent",
"insertURL": "Insert URL",
"visitLink": "Visit link",
"enterLink": "Enter link",
"enterMedia": "Enter media",
"edit": "Edit",
"apply": "Apply",
"hex": "Hex",
"material": "Material",
"color": "Color",
"lineheight": "Line height",
"findText": "Find text",
"moveToPreviousOccurrence": "Move to previous occurrence",
"moveToNextOccurrence": "Move to next occurrence",
"savedUsingTheNetwork": "Saved using the network",
"savedUsingLocalStorage": "Saved using the local storage",
"theImageHasBeenSavedAt": "The image has been saved at: {imagePath}",
"@theImageHasBeenSavedAt": {
"description": "A message with a single parameter",
"placeholders": {
"imagePath": {
"type": "String",
"example": "path/to/location"
}
}
},
"errorWhileSavingImage": "Error while saving image",
"pleaseEnterTextForYourLink": "Please enter a text for your link (e.g., 'Learn more')",
"pleaseEnterTheLinkURL": "Please enter the link URL (e.g., 'https://example.com')",
"pleaseEnterAValidImageURL": "Please enter a valid image URL",
"pleaseEnterAValidVideoURL": "Please enter a valid video url",
"photo": "Photo",
"image": "Image",
"caseSensitivityAndWholeWordSearch": "Case sensitivity and whole word search",
"caseSensitive": "Case sensitive",
"wholeWord": "Whole word",
"insertImage": "Insert image",
"pickAPhotoFromYourGallery": "Pick a photo from your gallery",
"takeAPhotoUsingYourCamera": "Take a photo using your camera",
"pasteAPhotoUsingALink": "Paste a photo using a link",
"pickAVideoFromYourGallery": "Pick a video from your gallery",
"recordAVideoUsingYourCamera": "Record a video using your camera",
"pasteAVideoUsingALink": "Paste a video using a link",
"close": "Close",
"searchSettings": "Search settings",
"cut": "Cut",
"paste": "Paste",
"insertTable": "Insert table"
}

@ -560,21 +560,16 @@ class PreserveInlineStylesRule extends InsertRule {
final itr = DeltaIterator(documentDelta); final itr = DeltaIterator(documentDelta);
len ??= 0; len ??= 0;
var prev = itr.skip(len == 0 ? index : index + 1); var prev = itr.skip(len == 0 ? index : index + 1);
var excludeLink = false; var excludeLinkAtLineStart = false;
/// Process simple insertions at start of line /// Process simple insertions at start of line
if (len == 0) { if (len == 0) {
final currLine = itr.next(); final currLine = itr.next();
/// Prevent links extending beyond the link's text label. /// Trap for previous is not text with attributes
excludeLink =
currLine.attributes?.containsKey(Attribute.link.key) != true &&
prev?.attributes?.containsKey(Attribute.link.key) == true;
/// Trap for previous is not text
if (prev?.data is! String) { if (prev?.data is! String) {
prev = currLine; prev = currLine;
excludeLink = true; excludeLinkAtLineStart = true;
} else { } else {
final prevData = prev!.data as String; final prevData = prev!.data as String;
if (prevData.endsWith('\n')) { if (prevData.endsWith('\n')) {
@ -585,17 +580,13 @@ class PreserveInlineStylesRule extends InsertRule {
if (prevData.trimRight().isEmpty) { if (prevData.trimRight().isEmpty) {
final back = final back =
DeltaIterator(documentDelta).skip(index - prevData.length); DeltaIterator(documentDelta).skip(index - prevData.length);
if (back != null && back.data is String) {
/// Prevent link attribute from propagating over line break
if (back != null &&
back.data is String &&
back.attributes?.containsKey(Attribute.link.key) != true) {
prev = back; prev = back;
} }
} }
} else { } else {
prev = currLine; prev = currLine;
excludeLink = true; excludeLinkAtLineStart = true;
} }
} }
} }
@ -613,7 +604,7 @@ class PreserveInlineStylesRule extends InsertRule {
return null; return null;
} }
if (excludeLink) { if (excludeLinkAtLineStart) {
attributes.remove(Attribute.link.key); attributes.remove(Attribute.link.key);
} }
return Delta() return Delta()

@ -1,6 +1,6 @@
name: flutter_quill name: flutter_quill
description: A rich text editor built for the modern Android, iOS, web and desktop platforms. It is the WYSIWYG editor and a Quill component for Flutter. description: A rich text editor built for the modern Android, iOS, web and desktop platforms. It is the WYSIWYG editor and a Quill component for Flutter.
version: 10.4.1 version: 10.1.10
homepage: https://1o24bbs.com/c/bulletjournal/108/ homepage: https://1o24bbs.com/c/bulletjournal/108/
repository: https://github.com/singerdmx/flutter-quill/ repository: https://github.com/singerdmx/flutter-quill/
issue_tracker: https://github.com/singerdmx/flutter-quill/issues/ issue_tracker: https://github.com/singerdmx/flutter-quill/issues/
@ -54,7 +54,7 @@ dependencies:
flutter_colorpicker: ^1.1.0 flutter_colorpicker: ^1.1.0
# For converting HTML to Quill delta # For converting HTML to Quill delta
flutter_quill_delta_from_html: ^1.4.0 flutter_quill_delta_from_html: ^1.3.13
markdown: ^7.2.1 markdown: ^7.2.1
charcode: ^1.3.1 charcode: ^1.3.1

@ -4,32 +4,6 @@ import 'package:test/test.dart';
void main() { void main() {
group('collectStyle', () { group('collectStyle', () {
test('No selection', () {
final delta = Delta()
..insert('plain\n')
..insert('bold\n', <String, dynamic>{'bold': true})
..insert('italic\n', <String, dynamic>{'italic': true});
final document = Document.fromDelta(delta);
//
expect(
document.getPlainText(0, document.length), 'plain\nbold\nitalic\n');
expect(document.length, 18);
//
for (var index = 0; index < 6; index++) {
expect(const Style(), document.collectStyle(index, 0));
}
//
for (var index = 6; index < 11; index++) {
expect(const Style.attr({'bold': Attribute.bold}),
document.collectStyle(index, 0));
}
//
for (var index = 11; index < document.length; index++) {
expect(const Style.attr({'italic': Attribute.italic}),
document.collectStyle(index, 0));
}
});
/// Lists and alignments have the same block attribute key but can have different values. /// Lists and alignments have the same block attribute key but can have different values.
/// Changing the format value updates the document but must also update the toolbar button state /// Changing the format value updates the document but must also update the toolbar button state
/// by ensuring the collectStyles method returns the attribute selected for the newly entered line. /// by ensuring the collectStyles method returns the attribute selected for the newly entered line.
@ -159,27 +133,5 @@ void main() {
// //
expect(const Style(), document.collectStyle(3, 3)); expect(const Style(), document.collectStyle(3, 3));
}); });
/// Links do not cross a line boundary
/// Enter key inserts newline as plain text without inline styles.
/// collectStyle needs to retrieve style of preceding line
test('Links and line boundaries', () {
final delta = Delta()
..insert('A link ')
..insert('home page', <String, dynamic>{'link': 'https://unknown.com'})
..insert('\n\nplain\n');
final document = Document.fromDelta(delta);
//
const linkStyle =
Style.attr({'link': LinkAttribute('https://unknown.com')});
//
expect(document.collectStyle(15, 0), linkStyle, reason: 'Within Link');
expect(document.collectStyle(16, 0), const Style(),
reason: 'At end of link');
expect(document.collectStyle(17, 0), const Style(),
reason: 'start of blank line');
expect(document.collectStyle(18, 0), const Style(),
reason: 'start of blank line');
});
}); });
} }

@ -1,83 +0,0 @@
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill/quill_delta.dart';
import 'package:test/test.dart';
void main() {
group('collectStyle', () {
test('Simple', () {
final delta = Delta()
..insert('First\nSecond ')
..insert('Bold', <String, dynamic>{'bold': true})
..insert('\n\nplain\n');
final document = Document.fromDelta(delta);
//
final line = document.queryChild(6).node as Line;
expect(line.getPlainText(0, line.length), 'Second Bold\n');
expect(line.length, 12);
//
expect(line.collectStyle(0, line.length), const Style());
expect(
line.collectStyle(7, 4), const Style.attr({'bold': Attribute.bold}));
expect(
line.collectStyle(7, 5), const Style.attr({'bold': Attribute.bold}),
reason: 'Include trailing NL');
expect(
line.collectStyle(7, 6), const Style.attr({'bold': Attribute.bold}),
reason: 'Spans next NL');
expect(line.collectStyle(7, 7), const Style(),
reason: 'Spans into plain text');
//
final line2 = document.queryChild(18).node as Line;
expect(line2.length, 1);
expect(
line2.collectStyle(0, 1), const Style.attr({'bold': Attribute.bold}),
reason: 'Empty line gets style from previous line');
});
test('Block', () {
final delta = Delta()
..insert('first', {'bold': true})
..insert('\n', {'list': Attribute.ol})
..insert('second', {'bold': true})
..insert('\n', {'list': Attribute.ol})
..insert('third', {'italic': true})
..insert('\n', {'list': Attribute.ol})
..insert('\nplain\n');
final document = Document.fromDelta(delta);
//
const orderedList = Attribute('list', AttributeScope.block, Attribute.ol);
expect(document.collectStyle(0, 4),
const Style.attr({'bold': Attribute.bold, 'list': orderedList}));
//
final first = document.queryChild(1).node as Line;
expect(first.getPlainText(0, first.length), 'first\n');
expect(first.length, 6);
expect(first.collectStyle(0, 2),
const Style.attr({'bold': Attribute.bold, 'list': orderedList}));
//
final second = document.queryChild(6).node as Line;
expect(second.getPlainText(0, second.length), 'second\n');
expect(second.length, 7);
expect(second.collectStyle(2, 4),
const Style.attr({'bold': Attribute.bold, 'list': orderedList}));
//
expect(first.collectStyle(3, 5),
const Style.attr({'bold': Attribute.bold, 'list': orderedList}),
reason: 'spans first and second list entry');
expect(second.collectStyle(3, 6), const Style.attr({'list': orderedList}),
reason: 'spans second and third list entry');
//
final plain = document.queryChild(20).node as Line;
expect(plain.getPlainText(0, plain.length), 'plain\n');
expect(plain.length, 6);
expect(plain.collectStyle(2, 4), const Style());
//
final blank = document.queryChild(19).node as Line;
expect(blank.getPlainText(0, blank.length), '\n');
expect(blank.length, 1);
expect(blank.getPlainText(0, 1), '\n');
expect(blank.collectStyle(0, 1),
const Style.attr({'italic': Attribute.italic, 'list': orderedList}));
});
});
}
Loading…
Cancel
Save