Skip to content

Commit 226afa0

Browse files
committed
trying to improve text selection without setting line height
1 parent 8568519 commit 226afa0

File tree

4 files changed

+102
-29
lines changed

4 files changed

+102
-29
lines changed
Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
// Use different implementations for web vs non-web platforms
2+
import 'package:flutter/rendering.dart';
23
import 'package:flutter_code_editor/src/code_field/browser_detection_web.dart'
34
if (dart.library.io) 'browser_detection_io.dart' as detection;
45

5-
// Function to determine the line height based on platform
6+
// Function to determine if we need to use specific Chrome text selection fixes
7+
Map<String, dynamic> getChromeTextSelectionFixes() {
8+
return detection.getChromeTextSelectionFixes();
9+
}
10+
11+
// Legacy function kept for backward compatibility
12+
// Now returns null to use the default line height
13+
@deprecated
614
double? getChromeLineHeight() {
7-
return detection.isChromeBrowser() ? 1.18 : null;
15+
return null;
816
}
917

10-
// Function to get appropriate top padding based on line height
18+
// Function to get appropriate top padding based on browser
1119
double getGutterTopPadding() {
12-
return getChromeLineHeight() == 1.18 ? 13.0 : 16.0;
20+
return 16.0;
1321
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
import 'package:flutter/rendering.dart';
2+
13
bool isChromeBrowser() {
24
return false;
35
}
6+
7+
Map<String, dynamic> getChromeTextSelectionFixes() {
8+
return {
9+
'useCustomSelectionStyle': false,
10+
};
11+
}

lib/src/code_field/browser_detection_web.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// ignore: deprecated_member_use
22
import 'dart:js' as js;
3+
import 'dart:ui';
34

45
import 'package:flutter/foundation.dart';
6+
import 'package:flutter/rendering.dart';
57

68
// Returns true if the browser is Chrome
79
bool isChromeBrowser() {
@@ -19,3 +21,20 @@ bool isChromeBrowser() {
1921
}
2022
return false;
2123
}
24+
25+
// Optimizes text selection in Chrome by adjusting selection behavior
26+
// instead of modifying line height
27+
Map<String, dynamic> getChromeTextSelectionFixes() {
28+
if (!isChromeBrowser()) {
29+
return {
30+
'useCustomSelectionStyle': false,
31+
};
32+
}
33+
34+
return {
35+
'useCustomSelectionStyle': true,
36+
'selectionHeightFix': true,
37+
'leadingDistribution': TextLeadingDistribution.even,
38+
'selectionAlignmentFix': true,
39+
};
40+
}

lib/src/code_field/code_field.dart

Lines changed: 63 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'dart:math';
22

3+
import 'package:flutter/foundation.dart';
34
import 'package:flutter/gestures.dart';
45
import 'package:flutter/material.dart';
56
import 'package:flutter/services.dart';
@@ -365,11 +366,16 @@ class _CodeFieldState extends State<CodeField> {
365366

366367
textStyle = defaultTextStyle.merge(widget.textStyle);
367368

368-
// Adjust textStyle to have consistent line height
369-
// This is a key fix for Chrome selection issues
369+
// Get Chrome-specific text selection fixes
370+
final chromeTextSelectionFixes = getChromeTextSelectionFixes();
371+
final useCustomSelectionStyle = chromeTextSelectionFixes['useCustomSelectionStyle'] as bool;
372+
373+
// Adjust textStyle to have consistent line height and Chrome-specific fixes
370374
final adjustedTextStyle = textStyle.copyWith(
371-
height: getLineHeight(), // Use the function instead of constant
372-
leadingDistribution: TextLeadingDistribution.even, // Added for consistent text baselines
375+
height: getLineHeight(),
376+
leadingDistribution: useCustomSelectionStyle
377+
? chromeTextSelectionFixes['leadingDistribution'] as TextLeadingDistribution
378+
: TextLeadingDistribution.proportional,
373379
);
374380

375381
final codeField = TextField(
@@ -382,16 +388,18 @@ class _CodeFieldState extends State<CodeField> {
382388
expands: widget.expands,
383389
scrollController: _codeScroll,
384390
keyboardType: widget.keyboardType,
385-
selectionControls: MaterialTextSelectionControls(),
386-
decoration: const InputDecoration(
391+
selectionControls: kIsWeb ? DesktopTextSelectionControls() : MaterialTextSelectionControls(),
392+
decoration: InputDecoration(
387393
isCollapsed: true,
388-
// Add more vertical padding to help with line selection
389-
contentPadding: EdgeInsets.symmetric(vertical: 16),
394+
// Add vertical padding - adjust based on selection fixes
395+
contentPadding: EdgeInsets.symmetric(
396+
vertical: useCustomSelectionStyle ? 14.0 : 16.0,
397+
),
390398
disabledBorder: InputBorder.none,
391399
border: InputBorder.none,
392400
focusedBorder: InputBorder.none,
393401
),
394-
textAlignVertical: TextAlignVertical.top, // Align text at the top for better matching
402+
textAlignVertical: TextAlignVertical.top,
395403
cursorColor: widget.cursorColor ?? defaultTextStyle.color,
396404
cursorHeight: widget.cursorHeight,
397405
autocorrect: false,
@@ -413,14 +421,7 @@ class _CodeFieldState extends State<CodeField> {
413421

414422
final editingField = Theme(
415423
data: Theme.of(context).copyWith(
416-
textSelectionTheme: widget.textSelectionTheme ??
417-
TextSelectionThemeData(
418-
// Use a more pronounced and distinguishable selection color
419-
// This helps with Chrome's selection rendering
420-
selectionColor: Theme.of(context).colorScheme.primary.withOpacity(0.5),
421-
cursorColor: widget.cursorColor ?? defaultTextStyle.color,
422-
selectionHandleColor: Theme.of(context).colorScheme.primary,
423-
),
424+
textSelectionTheme: _getTextSelectionTheme(context),
424425
),
425426
child: LayoutBuilder(
426427
builder: (BuildContext context, BoxConstraints constraints) {
@@ -457,14 +458,20 @@ class _CodeFieldState extends State<CodeField> {
457458
final lineNumberSize = textStyle.fontSize;
458459
final lineNumberColor = widget.gutterStyle.textStyle?.color ?? textStyle.color?.withOpacity(.5);
459460

461+
// Get Chrome-specific text selection fixes
462+
final chromeTextSelectionFixes = getChromeTextSelectionFixes();
463+
final useCustomSelectionStyle = chromeTextSelectionFixes['useCustomSelectionStyle'] as bool;
464+
460465
// Ensure same text style properties for consistent line height
461466
final lineNumberTextStyle = (widget.gutterStyle.textStyle ?? textStyle).copyWith(
462467
color: lineNumberColor,
463468
fontFamily: textStyle.fontFamily,
464469
fontSize: lineNumberSize,
465-
height: getLineHeight(), // Use the function instead of constant
470+
height: getLineHeight(),
466471
// Add additional properties to ensure metrics consistency
467-
leadingDistribution: TextLeadingDistribution.even,
472+
leadingDistribution: useCustomSelectionStyle
473+
? chromeTextSelectionFixes['leadingDistribution'] as TextLeadingDistribution
474+
: TextLeadingDistribution.proportional,
468475
);
469476

470477
final gutterStyle = widget.gutterStyle.copyWith(
@@ -548,9 +555,11 @@ class _CodeFieldState extends State<CodeField> {
548555
return renderBox.localToGlobal(Offset.zero);
549556
}
550557

551-
// Instead of calculating line position based on newlines,
552-
// we'll use the TextPainter's getOffsetForCaret method which
553-
// properly accounts for text wrapping
558+
// Get Chrome-specific text selection fixes
559+
final chromeTextSelectionFixes = getChromeTextSelectionFixes();
560+
final useCustomSelectionStyle = chromeTextSelectionFixes['useCustomSelectionStyle'] as bool;
561+
final selectionHeightFix = useCustomSelectionStyle ?
562+
(chromeTextSelectionFixes['selectionHeightFix'] as bool? ?? false) : false;
554563

555564
// Create a text painter for the entire text
556565
final fullTextPainter = TextPainter(
@@ -576,15 +585,21 @@ class _CodeFieldState extends State<CodeField> {
576585

577586
// Apply scroll offsets
578587
final double adjustedX = rawOffset.dx - horizontalScrollOffset;
579-
final double adjustedY = rawOffset.dy - scrollY;
588+
double adjustedY = rawOffset.dy - scrollY;
589+
590+
// Apply Chrome-specific vertical adjustment if needed
591+
if (selectionHeightFix) {
592+
// This small offset helps Chrome render selections correctly without changing line height
593+
adjustedY += 2.0;
594+
}
580595

581596
// Convert to global coordinates
582597
return renderBox.localToGlobal(Offset(adjustedX, adjustedY));
583598
}
584599

585600
double getLineHeight() {
586-
// Default line height multiple if not specified in the style
587-
return getChromeLineHeight() ?? (textStyle.height ?? 1.2);
601+
// Use a consistent line height that looks good (not the special Chrome value)
602+
return textStyle.height ?? 1.2;
588603
}
589604

590605
void _onPopupStateChanged() {
@@ -639,4 +654,27 @@ class _CodeFieldState extends State<CodeField> {
639654
}
640655
}
641656
}
657+
658+
TextSelectionThemeData _getTextSelectionTheme(BuildContext context) {
659+
final chromeTextSelectionFixes = getChromeTextSelectionFixes();
660+
final useCustomSelectionStyle = chromeTextSelectionFixes['useCustomSelectionStyle'] as bool;
661+
662+
// Create a more optimized selection style for Chrome
663+
if (useCustomSelectionStyle) {
664+
return widget.textSelectionTheme ??
665+
TextSelectionThemeData(
666+
selectionColor: Theme.of(context).colorScheme.primary.withOpacity(0.3),
667+
cursorColor: widget.cursorColor ?? textStyle.color,
668+
selectionHandleColor: Theme.of(context).colorScheme.primary,
669+
);
670+
}
671+
672+
// Default selection style for other browsers
673+
return widget.textSelectionTheme ??
674+
TextSelectionThemeData(
675+
selectionColor: Theme.of(context).colorScheme.primary.withOpacity(0.5),
676+
cursorColor: widget.cursorColor ?? textStyle.color,
677+
selectionHandleColor: Theme.of(context).colorScheme.primary,
678+
);
679+
}
642680
}

0 commit comments

Comments
 (0)