Fix block multi-line selection style (#1876)

* toggle_style_button : calls to options.afterButtonPressed replaced by call to class function afterButtonPressed to allow default call to base button settings
quill_icon_button: L26 build for isSelected updated to call afterButtonPressed = same as if not selected
QuillController _updateSelection removed param=source because not used; added new param insertNewline when true set tog to style of preceding char (last entered); updated replaceText to call _updateSelection for NL
document collectStyle:  Selecting the start of a line, user expects the style to be the visible style of the line including inline styles

* color_button calls afterButtonPressed
insert at start of line uses style for line

* Remove comments

* Fix formatting issue

* Fix FontFamily and Size button actions

* Fix FontFamily and Size button actions

* Value setting Stateful toolbar buttons derive from base class

* Rename base class as QuillToolbarBaseValueButton

* Fixes for before_push script

* Removed deprecated functions

* Move clipboard actions to QuillController

* Fix: collectAllIndividualStylesAndEmbed for result span

* Add: Clipboard toolbar buttons

* export: Clipboard toolbar buttons

* Fix: Dividers not shown in toolbar when multiRowsDisplay.
Fix: Toolbar drop buttons clipped when !multiRowsDisplay

* Add: test for QuillController clipboard
Dart Formatted

* Localizations updated

* QuillControllerConfigurations and clipboard paste

* Fix: CheckList action

* Fix: Multiline selection  and refactor toolbar buttons

* Add tests: Multiline selection

---------

Co-authored-by: Douglas Ward <dward@scied.com>
pull/1877/head
AtlasAutocode 11 months ago committed by GitHub
parent bcade13db8
commit fcc92da115
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      lib/src/models/documents/nodes/line.dart
  2. 8
      lib/src/models/rules/insert.dart
  3. 1
      lib/src/widgets/editor/editor.dart
  4. 8
      lib/src/widgets/quill/quill_controller.dart
  5. 81
      lib/src/widgets/toolbar/base_button/base_value_button.dart
  6. 7
      lib/src/widgets/toolbar/base_button/stateless_base_button.dart
  7. 2
      lib/src/widgets/toolbar/buttons/clear_format_button.dart
  8. 65
      lib/src/widgets/toolbar/buttons/color/color_button.dart
  9. 13
      lib/src/widgets/toolbar/buttons/font_family_button.dart
  10. 4
      lib/src/widgets/toolbar/buttons/font_size_button.dart
  11. 76
      lib/src/widgets/toolbar/buttons/hearder_style/select_header_style_buttons.dart
  12. 64
      lib/src/widgets/toolbar/buttons/hearder_style/select_header_style_dropdown_button.dart
  13. 56
      lib/src/widgets/toolbar/buttons/history_button.dart
  14. 68
      lib/src/widgets/toolbar/buttons/indent_button.dart
  15. 19
      lib/src/widgets/toolbar/buttons/link_style2_button.dart
  16. 65
      lib/src/widgets/toolbar/buttons/link_style_button.dart
  17. 4
      lib/src/widgets/toolbar/buttons/toggle_check_list_button.dart
  18. 56
      test/widgets/controller_test.dart

@ -372,7 +372,7 @@ base class Line extends QuillContainer<Leaf?> {
final data = queryChild(offset, true);
var node = data.node as Leaf?;
if (node != null) {
result = result.mergeAll(node.style);
result = node.style;
var pos = node.length - data.offset;
while (!node!.isLast && pos < local) {
node = node.next as Leaf;
@ -380,7 +380,6 @@ base class Line extends QuillContainer<Leaf?> {
pos += node.length;
}
}
result = result.mergeAll(style);
if (parent is Block) {
final block = parent as Block;
@ -390,7 +389,6 @@ base class Line extends QuillContainer<Leaf?> {
final remaining = len - local;
if (remaining > 0 && nextLine != null) {
final rest = nextLine!.collectStyle(0, remaining);
result = result.mergeAll(rest);
handle(rest);
}

@ -564,8 +564,12 @@ class PreserveInlineStylesRule extends InsertRule {
if (prev == null || prev.data is! String) return null;
if ((prev.data as String).endsWith('\n')) {
if (prev.attributes?.containsKey(Attribute.list.key) == true) {
return null;
if (prev.attributes != null) {
for (final key in prev.attributes!.keys) {
if (Attribute.blockKeys.contains(key)) {
return null;
}
}
}
prev = itr
.next(); // at the start of a line, apply the style for the current line and not the style for the preceding line

@ -172,6 +172,7 @@ class QuillEditorState extends State<QuillEditor>
@override
void initState() {
super.initState();
widget.configurations.controller.editorFocusNode ??= widget.focusNode;
_editorKey = configurations.editorKey ?? GlobalKey<EditorState>();
_selectionGestureDetectorBuilder =
_QuillEditorSelectionGestureDetectorBuilder(

@ -25,14 +25,17 @@ class QuillController extends ChangeNotifier {
this.onSelectionCompleted,
this.onSelectionChanged,
this.readOnly = false,
this.editorFocusNode,
}) : _document = document,
_selection = selection;
factory QuillController.basic(
{QuillControllerConfigurations configurations =
const QuillControllerConfigurations()}) {
const QuillControllerConfigurations(),
FocusNode? editorFocusNode}) {
return QuillController(
configurations: configurations,
editorFocusNode: editorFocusNode,
document: Document(),
selection: const TextSelection.collapsed(offset: 0),
);
@ -485,6 +488,9 @@ class QuillController extends ChangeNotifier {
List<OffsetValue> get pasteStyleAndEmbed => _pasteStyleAndEmbed;
bool readOnly;
/// Used to give focus to the editor following a toolbar action
FocusNode? editorFocusNode;
ImageUrl? _copiedImageUrl;
ImageUrl? get copiedImageUrl => _copiedImageUrl;

@ -4,10 +4,10 @@ import '../../../../flutter_quill.dart';
/// The [T] is the options for the button
/// The [E] is the extra options for the button
abstract class QuillToolbarBaseValueButton<
abstract class QuillToolbarBaseButton<
T extends QuillToolbarBaseButtonOptions<T, E>,
E extends QuillToolbarBaseButtonExtraOptions> extends StatefulWidget {
const QuillToolbarBaseValueButton(
const QuillToolbarBaseButton(
{required this.controller, required this.options, super.key});
final T options;
@ -16,16 +16,46 @@ abstract class QuillToolbarBaseValueButton<
}
/// The [W] is the widget that creates this State
/// The [V] is the type of the currentValue
abstract class QuillToolbarBaseValueButtonState<
W extends QuillToolbarBaseValueButton<T, E>,
abstract class QuillToolbarCommonButtonState<
W extends QuillToolbarBaseButton<T, E>,
T extends QuillToolbarBaseButtonOptions<T, E>,
E extends QuillToolbarBaseButtonExtraOptions,
V> extends State<W> {
E extends QuillToolbarBaseButtonExtraOptions> extends State<W> {
T get options => widget.options;
QuillController get controller => widget.controller;
QuillToolbarBaseButtonOptions? get baseButtonExtraOptions =>
context.quillToolbarBaseButtonOptions;
String get defaultTooltip;
String get tooltip =>
options.tooltip ?? baseButtonExtraOptions?.tooltip ?? defaultTooltip;
double get iconSize =>
options.iconSize ?? baseButtonExtraOptions?.iconSize ?? kDefaultIconSize;
double get iconButtonFactor =>
options.iconButtonFactor ??
baseButtonExtraOptions?.iconButtonFactor ??
kDefaultIconButtonFactor;
QuillIconTheme? get iconTheme =>
options.iconTheme ?? baseButtonExtraOptions?.iconTheme;
VoidCallback? get afterButtonPressed =>
options.afterButtonPressed ??
baseButtonExtraOptions?.afterButtonPressed ??
() => controller.editorFocusNode?.requestFocus();
}
/// The [W] is the widget that creates this State
/// The [V] is the type of the currentValue
abstract class QuillToolbarBaseButtonState<
W extends QuillToolbarBaseButton<T, E>,
T extends QuillToolbarBaseButtonOptions<T, E>,
E extends QuillToolbarBaseButtonExtraOptions,
V> extends QuillToolbarCommonButtonState<W, T, E> {
V? _currentValue;
V get currentValue => _currentValue!;
set currentValue(V value) => _currentValue = value;
@ -72,46 +102,13 @@ abstract class QuillToolbarBaseValueButtonState<
/// Extra listeners allow a subclass to listen to an external event that can affect its currentValue
void addExtraListener() {}
void removeExtraListener(covariant W oldWidget) {}
String get defaultTooltip;
String get tooltip {
return options.tooltip ??
context.quillToolbarBaseButtonOptions?.tooltip ??
defaultTooltip;
}
double get iconSize {
final baseFontSize = baseButtonExtraOptions?.iconSize;
final iconSize = options.iconSize;
return iconSize ?? baseFontSize ?? kDefaultIconSize;
}
double get iconButtonFactor {
final baseIconFactor = baseButtonExtraOptions?.iconButtonFactor;
final iconButtonFactor = options.iconButtonFactor;
return iconButtonFactor ?? baseIconFactor ?? kDefaultIconButtonFactor;
}
QuillIconTheme? get iconTheme {
return options.iconTheme ?? baseButtonExtraOptions?.iconTheme;
}
QuillToolbarBaseButtonOptions? get baseButtonExtraOptions {
return context.quillToolbarBaseButtonOptions;
}
VoidCallback? get afterButtonPressed {
return options.afterButtonPressed ??
baseButtonExtraOptions?.afterButtonPressed;
}
}
typedef QuillToolbarToggleStyleBaseButton = QuillToolbarBaseValueButton<
typedef QuillToolbarToggleStyleBaseButton = QuillToolbarBaseButton<
QuillToolbarToggleStyleButtonOptions,
QuillToolbarToggleStyleButtonExtraOptions>;
typedef QuillToolbarToggleStyleBaseButtonState<
W extends QuillToolbarToggleStyleBaseButton>
= QuillToolbarBaseValueButtonState<W, QuillToolbarToggleStyleButtonOptions,
= QuillToolbarBaseButtonState<W, QuillToolbarToggleStyleButtonOptions,
QuillToolbarToggleStyleButtonExtraOptions, bool>;

@ -33,7 +33,8 @@ abstract class QuillToolbarBaseButton<T, I> extends StatelessWidget {
VoidCallback? afterButtonPressed(BuildContext context) {
return options?.afterButtonPressed ??
baseButtonExtraOptions(context)?.afterButtonPressed;
baseButtonExtraOptions(context)?.afterButtonPressed ??
() => controller.editorFocusNode?.requestFocus();
}
QuillIconTheme? iconTheme(BuildContext context) {
@ -53,11 +54,11 @@ abstract class QuillToolbarBaseButton<T, I> extends StatelessWidget {
String tooltip(BuildContext context) {
return options?.tooltip ??
baseButtonExtraOptions(context)?.tooltip ??
getDefaultIconSize(context);
getDefaultTooltip(context);
}
abstract final IconData Function(BuildContext context) getDefaultIconData;
abstract final String Function(BuildContext context) getDefaultIconSize;
abstract final String Function(BuildContext context) getDefaultTooltip;
Widget buildButton(BuildContext context);
Widget? buildCustomChildBuilder(

@ -59,6 +59,6 @@ class QuillToolbarClearFormatButton extends QuillToolbarBaseButton {
(context) => Icons.format_clear;
@override
String Function(BuildContext context) get getDefaultIconSize =>
String Function(BuildContext context) get getDefaultTooltip =>
(context) => context.loc.clearFormat;
}

@ -5,39 +5,47 @@ import '../../../../l10n/extensions/localizations.dart';
import '../../../../l10n/widgets/localizations.dart';
import '../../../../models/documents/attribute.dart';
import '../../../../models/documents/style.dart';
import '../../../../models/themes/quill_icon_theme.dart';
import '../../../../utils/color.dart';
import '../../../quill/quill_controller.dart';
import '../../base_button/base_value_button.dart';
import '../../base_toolbar.dart';
import 'color_dialog.dart';
typedef QuillToolbarColorBaseButton = QuillToolbarBaseButton<
QuillToolbarColorButtonOptions, QuillToolbarColorButtonExtraOptions>;
typedef QuillToolbarColorBaseButtonState<W extends QuillToolbarColorButton>
= QuillToolbarCommonButtonState<W, QuillToolbarColorButtonOptions,
QuillToolbarColorButtonExtraOptions>;
/// Controls color styles.
///
/// When pressed, this button displays overlay toolbar with
/// buttons for each color.
class QuillToolbarColorButton extends StatefulWidget {
class QuillToolbarColorButton extends QuillToolbarColorBaseButton {
const QuillToolbarColorButton({
required this.controller,
required super.controller,
required this.isBackground,
this.options = const QuillToolbarColorButtonOptions(),
super.options = const QuillToolbarColorButtonOptions(),
super.key,
});
/// Is this background color button or font color
final bool isBackground;
final QuillController controller;
final QuillToolbarColorButtonOptions options;
@override
QuillToolbarColorButtonState createState() => QuillToolbarColorButtonState();
}
class QuillToolbarColorButtonState extends State<QuillToolbarColorButton> {
class QuillToolbarColorButtonState extends QuillToolbarColorBaseButtonState {
late bool _isToggledColor;
late bool _isToggledBackground;
late bool _isWhite;
late bool _isWhiteBackground;
@override
String get defaultTooltip =>
widget.isBackground ? context.loc.backgroundColor : context.loc.fontColor;
Style get _selectionStyle => widget.controller.getSelectionStyle();
void _didChangeEditingValue() {
@ -95,53 +103,12 @@ class QuillToolbarColorButtonState extends State<QuillToolbarColorButton> {
super.dispose();
}
QuillToolbarColorButtonOptions get options {
return widget.options;
}
QuillController get controller {
return widget.controller;
}
double get iconSize {
final baseFontSize = baseButtonExtraOptions?.iconSize;
final iconSize = options.iconSize;
return iconSize ?? baseFontSize ?? kDefaultIconSize;
}
double get iconButtonFactor {
final baseIconFactor = baseButtonExtraOptions?.iconButtonFactor;
final iconButtonFactor = options.iconButtonFactor;
return iconButtonFactor ?? baseIconFactor ?? kDefaultIconButtonFactor;
}
VoidCallback? get afterButtonPressed {
return options.afterButtonPressed ??
baseButtonExtraOptions?.afterButtonPressed;
}
QuillIconTheme? get iconTheme {
return options.iconTheme ?? baseButtonExtraOptions?.iconTheme;
}
QuillToolbarBaseButtonOptions? get baseButtonExtraOptions {
return context.quillToolbarBaseButtonOptions;
}
IconData get iconData {
return options.iconData ??
baseButtonExtraOptions?.iconData ??
(widget.isBackground ? Icons.format_color_fill : Icons.color_lens);
}
String get tooltip {
return options.tooltip ??
baseButtonExtraOptions?.tooltip ??
(widget.isBackground
? context.loc.backgroundColor
: context.loc.fontColor);
}
@override
Widget build(BuildContext context) {
final iconColor = _isToggledColor && !widget.isBackground && !_isWhite

@ -7,7 +7,7 @@ import '../../../models/documents/attribute.dart';
import '../base_button/base_value_button.dart';
import '../base_toolbar.dart';
class QuillToolbarFontFamilyButton extends QuillToolbarBaseValueButton<
class QuillToolbarFontFamilyButton extends QuillToolbarBaseButton<
QuillToolbarFontFamilyButtonOptions,
QuillToolbarFontFamilyButtonExtraOptions> {
QuillToolbarFontFamilyButton({
@ -28,12 +28,11 @@ class QuillToolbarFontFamilyButton extends QuillToolbarBaseValueButton<
QuillToolbarFontFamilyButtonState();
}
class QuillToolbarFontFamilyButtonState
extends QuillToolbarBaseValueButtonState<
QuillToolbarFontFamilyButton,
QuillToolbarFontFamilyButtonOptions,
QuillToolbarFontFamilyButtonExtraOptions,
String> {
class QuillToolbarFontFamilyButtonState extends QuillToolbarBaseButtonState<
QuillToolbarFontFamilyButton,
QuillToolbarFontFamilyButtonOptions,
QuillToolbarFontFamilyButtonExtraOptions,
String> {
@override
String get currentStateValue {
final attribute =

@ -9,7 +9,7 @@ import '../../../utils/font.dart';
import '../base_button/base_value_button.dart';
import '../base_toolbar.dart';
class QuillToolbarFontSizeButton extends QuillToolbarBaseValueButton<
class QuillToolbarFontSizeButton extends QuillToolbarBaseButton<
QuillToolbarFontSizeButtonOptions, QuillToolbarFontSizeButtonExtraOptions> {
QuillToolbarFontSizeButton({
required super.controller,
@ -28,7 +28,7 @@ class QuillToolbarFontSizeButton extends QuillToolbarBaseValueButton<
QuillToolbarFontSizeButtonState();
}
class QuillToolbarFontSizeButtonState extends QuillToolbarBaseValueButtonState<
class QuillToolbarFontSizeButtonState extends QuillToolbarBaseButtonState<
QuillToolbarFontSizeButton,
QuillToolbarFontSizeButtonOptions,
QuillToolbarFontSizeButtonExtraOptions,

@ -5,29 +5,40 @@ import '../../../../extensions/quill_configurations_ext.dart';
import '../../../../l10n/extensions/localizations.dart';
import '../../../../models/documents/attribute.dart';
import '../../../../models/documents/style.dart';
import '../../../../models/themes/quill_icon_theme.dart';
import '../../../quill/quill_controller.dart';
import '../../base_button/base_value_button.dart';
import '../../base_toolbar.dart';
class QuillToolbarSelectHeaderStyleButtons extends StatefulWidget {
typedef QuillToolbarSelectHeaderStyleBaseButtons = QuillToolbarBaseButton<
QuillToolbarSelectHeaderStyleButtonsOptions,
QuillToolbarSelectHeaderStyleButtonsExtraOptions>;
typedef QuillToolbarSelectHeaderStyleBaseButtonsState<
W extends QuillToolbarSelectHeaderStyleBaseButtons>
= QuillToolbarCommonButtonState<
W,
QuillToolbarSelectHeaderStyleButtonsOptions,
QuillToolbarSelectHeaderStyleButtonsExtraOptions>;
class QuillToolbarSelectHeaderStyleButtons
extends QuillToolbarSelectHeaderStyleBaseButtons {
const QuillToolbarSelectHeaderStyleButtons({
required this.controller,
this.options = const QuillToolbarSelectHeaderStyleButtonsOptions(),
required super.controller,
super.options = const QuillToolbarSelectHeaderStyleButtonsOptions(),
super.key,
});
final QuillController controller;
final QuillToolbarSelectHeaderStyleButtonsOptions options;
@override
QuillToolbarSelectHeaderStyleButtonsState createState() =>
QuillToolbarSelectHeaderStyleButtonsState();
}
class QuillToolbarSelectHeaderStyleButtonsState
extends State<QuillToolbarSelectHeaderStyleButtons> {
extends QuillToolbarSelectHeaderStyleBaseButtonsState {
Attribute? _selectedAttribute;
@override
String get defaultTooltip => context.loc.headerStyle;
Style get _selectionStyle => controller.getSelectionStyle();
final _valueToText = <Attribute, String>{
@ -46,45 +57,6 @@ class QuillToolbarSelectHeaderStyleButtonsState
controller.addListener(_didChangeEditingValue);
}
QuillToolbarSelectHeaderStyleButtonsOptions get options {
return widget.options;
}
QuillController get controller {
return widget.controller;
}
double get iconSize {
final baseFontSize = baseButtonExtraOptions?.iconSize;
final iconSize = options.iconSize;
return iconSize ?? baseFontSize ?? kDefaultIconSize;
}
double get iconButtonFactor {
final baseIconFactor = baseButtonExtraOptions?.iconButtonFactor;
final iconButtonFactor = options.iconButtonFactor;
return iconButtonFactor ?? baseIconFactor ?? kDefaultIconButtonFactor;
}
VoidCallback? get afterButtonPressed {
return options.afterButtonPressed ??
baseButtonExtraOptions?.afterButtonPressed;
}
QuillIconTheme? get iconTheme {
return options.iconTheme ?? baseButtonExtraOptions?.iconTheme;
}
QuillToolbarBaseButtonOptions? get baseButtonExtraOptions {
return context.quillToolbarBaseButtonOptions;
}
String get tooltip {
return options.tooltip ??
baseButtonExtraOptions?.tooltip ??
context.loc.headerStyle;
}
Axis get axis {
return options.axis ??
context.quillSimpleToolbarConfigurations?.axis ??
@ -99,7 +71,7 @@ class QuillToolbarSelectHeaderStyleButtonsState
afterButtonPressed?.call();
}
List<Attribute> get _attrbuites {
List<Attribute> get _attributes {
return options.attributes ??
const [
Attribute.header,
@ -112,7 +84,7 @@ class QuillToolbarSelectHeaderStyleButtonsState
@override
Widget build(BuildContext context) {
assert(
_attrbuites.every(
_attributes.every(
(element) => _valueToText.keys.contains(element),
),
'All attributes must be one of them: header, h1, h2 or h3',
@ -126,7 +98,7 @@ class QuillToolbarSelectHeaderStyleButtonsState
final childBuilder =
options.childBuilder ?? baseButtonExtraOptions?.childBuilder;
final children = _attrbuites.map((attribute) {
final children = _attributes.map((attribute) {
if (childBuilder != null) {
return childBuilder(
options,
@ -149,7 +121,7 @@ class QuillToolbarSelectHeaderStyleButtonsState
icon: Text(
_valueToText[attribute] ??
(throw ArgumentError.notNull(
'attrbuite',
'attribute',
)),
style: style.copyWith(
color: isSelected

@ -3,27 +3,38 @@ import 'package:flutter/material.dart';
import '../../../../../translations.dart';
import '../../../../extensions/quill_configurations_ext.dart';
import '../../../../models/documents/attribute.dart';
import '../../../../models/themes/quill_icon_theme.dart';
import '../../../quill/quill_controller.dart';
import '../../base_button/base_value_button.dart';
import '../../base_toolbar.dart';
class QuillToolbarSelectHeaderStyleDropdownButton extends StatefulWidget {
typedef QuillToolbarSelectHeaderStyleDropdownBaseButton
= QuillToolbarBaseButton<QuillToolbarSelectHeaderStyleDropdownButtonOptions,
QuillToolbarSelectHeaderStyleDropdownButtonExtraOptions>;
typedef QuillToolbarSelectHeaderStyleDropdownBaseButtonsState<
W extends QuillToolbarSelectHeaderStyleDropdownButton>
= QuillToolbarCommonButtonState<
W,
QuillToolbarSelectHeaderStyleDropdownButtonOptions,
QuillToolbarSelectHeaderStyleDropdownButtonExtraOptions>;
class QuillToolbarSelectHeaderStyleDropdownButton
extends QuillToolbarSelectHeaderStyleDropdownBaseButton {
const QuillToolbarSelectHeaderStyleDropdownButton({
required this.controller,
this.options = const QuillToolbarSelectHeaderStyleDropdownButtonOptions(),
required super.controller,
super.options = const QuillToolbarSelectHeaderStyleDropdownButtonOptions(),
super.key,
});
final QuillController controller;
final QuillToolbarSelectHeaderStyleDropdownButtonOptions options;
@override
State<QuillToolbarSelectHeaderStyleDropdownButton> createState() =>
QuillToolbarSelectHeaderStyleDropdownBaseButtonsState createState() =>
_QuillToolbarSelectHeaderStyleDropdownButtonState();
}
class _QuillToolbarSelectHeaderStyleDropdownButtonState
extends State<QuillToolbarSelectHeaderStyleDropdownButton> {
extends QuillToolbarSelectHeaderStyleDropdownBaseButtonsState {
@override
String get defaultTooltip => context.loc.headerStyle;
Attribute<dynamic> _selectedItem = Attribute.header;
final _menuController = MenuController();
@ -89,24 +100,6 @@ class _QuillToolbarSelectHeaderStyleDropdownButtonState
return label;
}
double get iconSize {
final baseFontSize = context.quillToolbarBaseButtonOptions?.iconSize;
final iconSize = widget.options.iconSize;
return iconSize ?? baseFontSize ?? kDefaultIconSize;
}
double get iconButtonFactor {
final baseIconFactor =
context.quillToolbarBaseButtonOptions?.iconButtonFactor;
final iconButtonFactor = widget.options.iconButtonFactor;
return iconButtonFactor ?? baseIconFactor ?? kDefaultIconButtonFactor;
}
QuillIconTheme? get iconTheme {
return widget.options.iconTheme ??
context.quillToolbarBaseButtonOptions?.iconTheme;
}
List<Attribute<int?>> get headerAttributes {
return widget.options.attributes ??
[
@ -117,21 +110,6 @@ class _QuillToolbarSelectHeaderStyleDropdownButtonState
];
}
String get tooltip {
return widget.options.tooltip ??
context.quillToolbarBaseButtonOptions?.tooltip ??
context.loc.headerStyle;
}
QuillToolbarBaseButtonOptions? get baseButtonExtraOptions {
return context.quillToolbarBaseButtonOptions;
}
VoidCallback? get afterButtonPressed {
return widget.options.afterButtonPressed ??
baseButtonExtraOptions?.afterButtonPressed;
}
void _onPressed(Attribute<int?> e) {
setState(() => _selectedItem = e);
widget.controller.formatSelection(_selectedItem);

@ -1,15 +1,21 @@
import 'package:flutter/material.dart';
import '../../../extensions/quill_configurations_ext.dart';
import '../../../l10n/extensions/localizations.dart';
import '../../quill/quill_controller.dart';
import '../base_button/base_value_button.dart';
import '../base_toolbar.dart';
class QuillToolbarHistoryButton extends StatefulWidget {
typedef QuillToolbarHistoryBaseButton = QuillToolbarBaseButton<
QuillToolbarHistoryButtonOptions, QuillToolbarHistoryButtonExtraOptions>;
typedef QuillToolbarHistoryBaseButtonState<W extends QuillToolbarHistoryButton>
= QuillToolbarCommonButtonState<W, QuillToolbarHistoryButtonOptions,
QuillToolbarHistoryButtonExtraOptions>;
class QuillToolbarHistoryButton extends QuillToolbarHistoryBaseButton {
const QuillToolbarHistoryButton({
required this.controller,
required super.controller,
required this.isUndo,
this.options = const QuillToolbarHistoryButtonOptions(),
super.options = const QuillToolbarHistoryButtonOptions(),
super.key,
});
@ -17,25 +23,18 @@ class QuillToolbarHistoryButton extends StatefulWidget {
/// otherwise it will be redo
final bool isUndo;
final QuillToolbarHistoryButtonOptions options;
final QuillController controller;
@override
QuillToolbarHistoryButtonState createState() =>
QuillToolbarHistoryButtonState();
}
class QuillToolbarHistoryButtonState extends State<QuillToolbarHistoryButton> {
late ThemeData theme;
class QuillToolbarHistoryButtonState
extends QuillToolbarHistoryBaseButtonState {
var _canPressed = false;
QuillToolbarHistoryButtonOptions get options {
return widget.options;
}
QuillController get controller {
return widget.controller;
}
@override
String get defaultTooltip =>
widget.isUndo ? context.loc.undo : context.loc.redo;
@override
void initState() {
@ -54,25 +53,11 @@ class QuillToolbarHistoryButtonState extends State<QuillToolbarHistoryButton> {
@override
Widget build(BuildContext context) {
final baseButtonConfigurations = context.quillToolbarBaseButtonOptions;
final tooltip = options.tooltip ??
baseButtonConfigurations?.tooltip ??
(widget.isUndo ? context.loc.undo : context.loc.redo);
final iconData = options.iconData ??
baseButtonConfigurations?.iconData ??
baseButtonExtraOptions?.iconData ??
(widget.isUndo ? Icons.undo_outlined : Icons.redo_outlined);
final childBuilder =
options.childBuilder ?? baseButtonConfigurations?.childBuilder;
final iconSize = options.iconSize ??
baseButtonConfigurations?.iconSize ??
kDefaultIconSize;
final iconButtonFactor = options.iconButtonFactor ??
baseButtonConfigurations?.iconButtonFactor ??
kDefaultIconButtonFactor;
final iconTheme = options.iconTheme ?? baseButtonConfigurations?.iconTheme;
final afterButtonPressed = options.afterButtonPressed ??
baseButtonConfigurations?.afterButtonPressed;
options.childBuilder ?? baseButtonExtraOptions?.childBuilder;
if (childBuilder != null) {
return childBuilder(
@ -89,7 +74,6 @@ class QuillToolbarHistoryButtonState extends State<QuillToolbarHistoryButton> {
);
}
theme = Theme.of(context);
return QuillToolbarIconButton(
tooltip: tooltip,
icon: Icon(
@ -122,13 +106,13 @@ class QuillToolbarHistoryButtonState extends State<QuillToolbarHistoryButton> {
if (controller.hasUndo) {
controller.undo();
}
// _updateCanPressed(); // We are already listeneting for the changes
// _updateCanPressed(); // We are already listening for the changes
return;
}
if (controller.hasRedo) {
controller.redo();
// _updateCanPressed(); // We are already listeneting for the changes
// _updateCanPressed(); // We are already listening for the changes
}
}
}

@ -1,63 +1,37 @@
import 'package:flutter/material.dart';
import '../../../extensions/quill_configurations_ext.dart';
import '../../../l10n/extensions/localizations.dart';
import '../../../models/config/toolbar/simple_toolbar_configurations.dart';
import '../../../models/themes/quill_icon_theme.dart';
import '../../quill/quill_controller.dart';
import '../base_toolbar.dart'
show QuillToolbarBaseButtonOptions, QuillToolbarIconButton;
import '../base_button/base_value_button.dart';
import '../base_toolbar.dart' show QuillToolbarIconButton;
class QuillToolbarIndentButton extends StatefulWidget {
typedef QuillToolbarIndentBaseButton = QuillToolbarBaseButton<
QuillToolbarIndentButtonOptions, QuillToolbarIndentButtonExtraOptions>;
typedef QuillToolbarIndentBaseButtonState<W extends QuillToolbarIndentButton>
= QuillToolbarCommonButtonState<W, QuillToolbarIndentButtonOptions,
QuillToolbarIndentButtonExtraOptions>;
class QuillToolbarIndentButton extends QuillToolbarIndentBaseButton {
const QuillToolbarIndentButton({
required this.controller,
required super.controller,
required this.isIncrease,
this.options = const QuillToolbarIndentButtonOptions(),
super.options = const QuillToolbarIndentButtonOptions(),
super.key,
});
final QuillController controller;
final bool isIncrease;
final QuillToolbarIndentButtonOptions options;
@override
QuillToolbarIndentButtonState createState() =>
QuillToolbarIndentButtonState();
}
class QuillToolbarIndentButtonState extends State<QuillToolbarIndentButton> {
QuillToolbarIndentButtonOptions get options {
return widget.options;
}
QuillController get controller {
return widget.controller;
}
double get iconSize {
final baseFontSize = baseButtonExtraOptions?.iconSize;
final iconSize = options.iconSize;
return iconSize ?? baseFontSize ?? kDefaultIconSize;
}
double get iconButtonFactor {
final baseIconFactor = baseButtonExtraOptions?.iconButtonFactor;
final iconButtonFactor = options.iconButtonFactor;
return iconButtonFactor ?? baseIconFactor ?? kDefaultIconButtonFactor;
}
VoidCallback? get afterButtonPressed {
return options.afterButtonPressed ??
baseButtonExtraOptions?.afterButtonPressed;
}
QuillIconTheme? get iconTheme {
return options.iconTheme ?? baseButtonExtraOptions?.iconTheme;
}
QuillToolbarBaseButtonOptions? get baseButtonExtraOptions {
return context.quillToolbarBaseButtonOptions;
}
class QuillToolbarIndentButtonState extends QuillToolbarIndentBaseButtonState {
@override
String get defaultTooltip => widget.isIncrease
? context.loc.increaseIndent
: context.loc.decreaseIndent;
IconData get iconData {
return options.iconData ??
@ -67,14 +41,6 @@ class QuillToolbarIndentButtonState extends State<QuillToolbarIndentButton> {
: Icons.format_indent_decrease);
}
String get tooltip {
return options.tooltip ??
baseButtonExtraOptions?.tooltip ??
(widget.isIncrease
? context.loc.increaseIndent
: context.loc.decreaseIndent);
}
void _sharedOnPressed() {
widget.controller.indentSelection(widget.isIncrease);
}

@ -12,15 +12,25 @@ import '../../../models/themes/quill_dialog_theme.dart';
import '../../../models/themes/quill_icon_theme.dart';
import '../../others/link.dart';
import '../../quill/quill_controller.dart';
import '../base_button/base_value_button.dart';
import '../base_toolbar.dart';
typedef QuillToolbarLinkStyleBaseButton2 = QuillToolbarBaseButton<
QuillToolbarLinkStyleButton2Options,
QuillToolbarLinkStyleButton2ExtraOptions>;
typedef QuillToolbarLinkStyleBaseButton2State<
W extends QuillToolbarLinkStyleBaseButton2>
= QuillToolbarCommonButtonState<W, QuillToolbarLinkStyleButton2Options,
QuillToolbarLinkStyleButton2ExtraOptions>;
/// Alternative version of [QuillToolbarLinkStyleButton]. This widget has more
/// customization
/// and uses dialog similar to one which is used on [http://quilljs.com].
class QuillToolbarLinkStyleButton2 extends StatefulWidget {
class QuillToolbarLinkStyleButton2 extends QuillToolbarLinkStyleBaseButton2 {
QuillToolbarLinkStyleButton2({
required this.controller,
this.options = const QuillToolbarLinkStyleButton2Options(),
required super.controller,
super.options = const QuillToolbarLinkStyleButton2Options(),
super.key,
}) : assert(options.addLinkLabel == null ||
(options.addLinkLabel?.isNotEmpty ?? true)),
@ -30,9 +40,6 @@ class QuillToolbarLinkStyleButton2 extends StatefulWidget {
assert(options.validationMessage == null ||
(options.validationMessage?.isNotEmpty ?? true));
final QuillController controller;
final QuillToolbarLinkStyleButton2Options options;
@override
State<QuillToolbarLinkStyleButton2> createState() =>
_QuillToolbarLinkStyleButton2State();

@ -7,28 +7,36 @@ import '../../../models/documents/attribute.dart';
import '../../../models/rules/insert.dart';
import '../../../models/structs/link_dialog_action.dart';
import '../../../models/themes/quill_dialog_theme.dart';
import '../../../models/themes/quill_icon_theme.dart';
import '../../others/link.dart';
import '../../quill/quill_controller.dart';
import '../base_button/base_value_button.dart';
import '../base_toolbar.dart';
class QuillToolbarLinkStyleButton extends StatefulWidget {
typedef QuillToolbarLinkStyleBaseButton = QuillToolbarBaseButton<
QuillToolbarLinkStyleButtonOptions,
QuillToolbarLinkStyleButtonExtraOptions>;
typedef QuillToolbarLinkStyleBaseButtonState<
W extends QuillToolbarLinkStyleBaseButton>
= QuillToolbarCommonButtonState<W, QuillToolbarLinkStyleButtonOptions,
QuillToolbarLinkStyleButtonExtraOptions>;
class QuillToolbarLinkStyleButton extends QuillToolbarLinkStyleBaseButton {
const QuillToolbarLinkStyleButton({
required this.controller,
this.options = const QuillToolbarLinkStyleButtonOptions(),
required super.controller,
super.options = const QuillToolbarLinkStyleButtonOptions(),
super.key,
});
final QuillController controller;
final QuillToolbarLinkStyleButtonOptions options;
@override
QuillToolbarLinkStyleButtonState createState() =>
QuillToolbarLinkStyleButtonState();
}
class QuillToolbarLinkStyleButtonState
extends State<QuillToolbarLinkStyleButton> {
extends QuillToolbarLinkStyleBaseButtonState {
@override
String get defaultTooltip => context.loc.insertURL;
void _didChangeSelection() {
setState(() {});
}
@ -54,45 +62,6 @@ class QuillToolbarLinkStyleButtonState
controller.removeListener(_didChangeSelection);
}
QuillController get controller {
return widget.controller;
}
QuillToolbarLinkStyleButtonOptions get options {
return widget.options;
}
double get iconSize {
final baseFontSize = baseButtonExtraOptions?.iconSize;
final iconSize = options.iconSize;
return iconSize ?? baseFontSize ?? kDefaultIconSize;
}
double get iconButtonFactor {
final baseIconFactor = baseButtonExtraOptions?.iconButtonFactor;
final iconButtonFactor = options.iconButtonFactor;
return iconButtonFactor ?? baseIconFactor ?? kDefaultIconButtonFactor;
}
VoidCallback? get afterButtonPressed {
return options.afterButtonPressed ??
baseButtonExtraOptions?.afterButtonPressed;
}
QuillIconTheme? get iconTheme {
return options.iconTheme ?? baseButtonExtraOptions?.iconTheme;
}
QuillToolbarBaseButtonOptions? get baseButtonExtraOptions {
return context.quillToolbarBaseButtonOptions;
}
String get tooltip {
return options.tooltip ??
baseButtonExtraOptions?.tooltip ??
context.loc.insertURL;
}
IconData get iconData {
return options.iconData ?? baseButtonExtraOptions?.iconData ?? Icons.link;
}

@ -7,7 +7,7 @@ import '../../../utils/widgets.dart';
import '../base_button/base_value_button.dart';
import '../base_toolbar.dart';
class QuillToolbarToggleCheckListButton extends QuillToolbarBaseValueButton<
class QuillToolbarToggleCheckListButton extends QuillToolbarBaseButton<
QuillToolbarToggleCheckListButtonOptions,
QuillToolbarToggleCheckListButtonExtraOptions> {
const QuillToolbarToggleCheckListButton({
@ -22,7 +22,7 @@ class QuillToolbarToggleCheckListButton extends QuillToolbarBaseValueButton<
}
class QuillToolbarToggleCheckListButtonState
extends QuillToolbarBaseValueButtonState<
extends QuillToolbarBaseButtonState<
QuillToolbarToggleCheckListButton,
QuillToolbarToggleCheckListButtonOptions,
QuillToolbarToggleCheckListButtonExtraOptions,

@ -355,5 +355,61 @@ void main() {
expect(controller.document.length, 6,
reason: 'Cut not permitted on readOnly document');
});
test('blockSelectionStyles', () {
Style select(int start, int end) {
controller.updateSelection(
TextSelection(baseOffset: start, extentOffset: end),
ChangeSource.local);
return controller.getSelectionStyle();
}
Attribute fromKey(String key) => switch (key) {
'header' => Attribute.h1,
'list' => Attribute.ol,
'align' => Attribute.centerAlignment,
'code-block' => Attribute.codeBlock,
'blockquote' => Attribute.blockQuote,
'indent' => Attribute.indentL2,
'direction' => Attribute.rtl,
String() => throw UnimplementedError(key)
};
for (final blockKey in Attribute.blockKeys) {
final blockAttribute = fromKey(blockKey);
controller
..clear()
..replaceText(0, 0, 'line 1\nLine 2\nLine 3', null)
..formatText(0, 0, blockAttribute) // first 2 lines
..formatText(
4, 6, Attribute.bold) // spans end of line 1 and start of line 2
..formatText(7, 0, blockAttribute);
expect(select(2, 5), const Style().put(blockAttribute),
reason: 'line 1 block, plain and bold');
expect(
select(5, 6), const Style().put(Attribute.bold).put(blockAttribute),
reason: 'line 1 block, bold');
expect(
select(4, 8), const Style().put(Attribute.bold).put(blockAttribute),
reason: 'spans line1 and 2, selection is all bold');
expect(select(4, 11), const Style().put(blockAttribute),
reason: 'selection expands into non-bold text');
expect(select(2, 11), const Style().put(blockAttribute),
reason:
'selection starts in non-bold text extends into plain on next line');
expect(select(2, 8), const Style().put(blockAttribute),
reason:
'selection starts in non-bold text, extends into bold on next line');
expect(
select(7, 8), const Style().put(Attribute.bold).put(blockAttribute),
reason: 'line 2 block, bold');
expect(select(7, 11), const Style().put(blockAttribute),
reason: 'line 2 block, selection extends into plain text');
expect(select(4, 16), const Style(),
reason: 'line 1 extends into line3 which is not block');
}
});
});
}

Loading…
Cancel
Save