Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Added `cursorWidth`, `cursorHeight`, `cursorRadius`, `cursorOffset`, and `cursorOpacityAnimates` to `QuillEditorConfig` so high-level editors can configure the caret without using `QuillRawEditor` directly.

### Fixed

- Aligned macOS caret geometry and platform defaults with Flutter, and made caret painting and `getLocalRectForCaret` use the same final rectangle.

- Fixed an issue where bullet points became visually detached from the text body when toggling text direction formatting (RTL) by locking the list leading block to the editor's base text direction.
- Fixed typed text being inserted at the previous caret position on Android after moving the caret with a tap/mouse by keeping the platform IME's editing state in sync with the selection even when the keyboard is hidden.

Expand Down
44 changes: 44 additions & 0 deletions lib/src/editor/config/editor_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ class QuillEditorConfig {
this.textSelectionThemeData,
this.showCursor,
this.paintCursorAboveText,
this.cursorWidth = 2.0,
this.cursorHeight,
this.cursorRadius,
this.cursorOffset,
this.cursorOpacityAnimates,
this.enableInteractiveSelection = true,
this.enableSelectionToolbar = true,
this.scrollBottomInset = 0,
Expand Down Expand Up @@ -248,6 +253,34 @@ class QuillEditorConfig {
final bool? showCursor;
final bool? paintCursorAboveText;

/// How thick the cursor will be.
///
/// Defaults to `2.0`, preserving the editor's existing cursor appearance.
final double cursorWidth;

/// How tall the cursor will be.
///
/// If this property is null, the preferred line height is used.
final double? cursorHeight;

/// How rounded the corners of the cursor should be.
///
/// If this property is null, the platform default is used. Set this to
/// [Radius.zero] to explicitly disable rounded corners on platforms whose
/// default cursor is rounded.
final Radius? cursorRadius;

/// The offset applied to the cursor when it is painted.
///
/// If this property is null, the platform default is used. Set this to
/// [Offset.zero] to explicitly disable a platform cursor offset.
final Offset? cursorOffset;

/// Whether the cursor opacity animates during each blink.
///
/// If this property is null, the platform default is used.
final bool? cursorOpacityAnimates;

/// The [readOnlyMouseCursor] is used for Windows, macOS when [readOnly] is [true]
final MouseCursor readOnlyMouseCursor;

Expand Down Expand Up @@ -504,6 +537,11 @@ class QuillEditorConfig {
KeyEventResult? Function(KeyEvent event, Node? node)? onKeyPressed,
bool? showCursor,
bool? paintCursorAboveText,
double? cursorWidth,
double? cursorHeight,
Radius? cursorRadius,
Offset? cursorOffset,
bool? cursorOpacityAnimates,
MouseCursor? readOnlyMouseCursor,
bool? enableInteractiveSelection,
bool? enableSelectionToolbar,
Expand Down Expand Up @@ -565,6 +603,12 @@ class QuillEditorConfig {
onTapOutside: onTapOutside ?? this.onTapOutside,
showCursor: showCursor ?? this.showCursor,
paintCursorAboveText: paintCursorAboveText ?? this.paintCursorAboveText,
cursorWidth: cursorWidth ?? this.cursorWidth,
cursorHeight: cursorHeight ?? this.cursorHeight,
cursorRadius: cursorRadius ?? this.cursorRadius,
cursorOffset: cursorOffset ?? this.cursorOffset,
cursorOpacityAnimates:
cursorOpacityAnimates ?? this.cursorOpacityAnimates,
readOnlyMouseCursor: readOnlyMouseCursor ?? this.readOnlyMouseCursor,
enableInteractiveSelection:
enableInteractiveSelection ?? this.enableInteractiveSelection,
Expand Down
60 changes: 41 additions & 19 deletions lib/src/editor/editor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -233,37 +233,55 @@ class QuillEditorState extends State<QuillEditor>
config.textSelectionThemeData ?? TextSelectionTheme.of(context);

TextSelectionControls textSelectionControls;
bool paintCursorAboveText;
bool cursorOpacityAnimates;
Offset? cursorOffset;
Color? cursorColor;
Color selectionColor;
Radius? cursorRadius;

if (theme.isCupertino) {
final cupertinoTheme = CupertinoTheme.of(context);
textSelectionControls = cupertinoTextSelectionControls;
paintCursorAboveText = true;
cursorOpacityAnimates = true;
cursorColor ??= selectionTheme.cursorColor ?? cupertinoTheme.primaryColor;
selectionColor =
selectionTheme.selectionColor ??
cupertinoTheme.primaryColor.withValues(alpha: 0.40);
cursorRadius ??= const Radius.circular(2);
cursorOffset = Offset(
iOSHorizontalOffset / MediaQuery.devicePixelRatioOf(context),
0,
);
} else {
textSelectionControls = materialTextSelectionControls;
paintCursorAboveText = false;
cursorOpacityAnimates = false;
cursorColor ??= selectionTheme.cursorColor ?? theme.colorScheme.primary;
selectionColor =
selectionTheme.selectionColor ??
theme.colorScheme.primary.withValues(alpha: 0.40);
}

final bool platformPaintCursorAboveText;
final bool platformCursorOpacityAnimates;
final Radius? platformCursorRadius;
final Offset? platformCursorOffset;
switch (theme.platform) {
case TargetPlatform.iOS:
platformPaintCursorAboveText = true;
platformCursorOpacityAnimates = true;
platformCursorRadius = const Radius.circular(2);
platformCursorOffset = Offset(
iOSHorizontalOffset / MediaQuery.devicePixelRatioOf(context),
0,
);
case TargetPlatform.macOS:
platformPaintCursorAboveText = true;
platformCursorOpacityAnimates = false;
platformCursorRadius = const Radius.circular(2);
platformCursorOffset = Offset(
iOSHorizontalOffset / MediaQuery.devicePixelRatioOf(context),
0,
);
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
platformPaintCursorAboveText = false;
platformCursorOpacityAnimates = false;
platformCursorRadius = null;
platformCursorOffset = null;
}

final showSelectionToolbar =
config.enableInteractiveSelection && config.enableSelectionToolbar;

Expand Down Expand Up @@ -296,11 +314,15 @@ class QuillEditorState extends State<QuillEditor>
cursorStyle: CursorStyle(
color: cursorColor,
backgroundColor: Colors.grey,
width: 2,
radius: cursorRadius,
offset: cursorOffset,
paintAboveText: config.paintCursorAboveText ?? paintCursorAboveText,
opacityAnimates: cursorOpacityAnimates,
platform: theme.platform,
width: config.cursorWidth,
height: config.cursorHeight,
radius: config.cursorRadius ?? platformCursorRadius,
offset: config.cursorOffset ?? platformCursorOffset,
paintAboveText:
config.paintCursorAboveText ?? platformPaintCursorAboveText,
opacityAnimates:
config.cursorOpacityAnimates ?? platformCursorOpacityAnimates,
),
textCapitalization: config.textCapitalization,
minHeight: config.minHeight,
Expand Down Expand Up @@ -1345,7 +1367,7 @@ class RenderEditor extends RenderEditableContainerBox
final childLocalRect = targetChild.getLocalRectForCaret(localPosition);

final boxParentData = targetChild.parentData as BoxParentData;
return childLocalRect.shift(Offset(0, boxParentData.offset.dy));
return childLocalRect.shift(boxParentData.offset);
}

// Start floating cursor
Expand Down
Loading