Commit 6ca36a94 authored by Matt Perry's avatar Matt Perry

Add a text selection handle for the collapsed case. (#3467)

Also simplify handle drawing. All 3 cases are the same, just rotated.

Also fix selection changes on iOS.
parent cd084005
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:math' as math;
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
...@@ -248,22 +250,27 @@ class _InputState extends State<Input> { ...@@ -248,22 +250,27 @@ class _InputState extends State<Input> {
height: _kTextSelectionHandleSize, height: _kTextSelectionHandleSize,
child: new CustomPaint( child: new CustomPaint(
painter: new _TextSelectionHandlePainter( painter: new _TextSelectionHandlePainter(
type: type,
color: Theme.of(context).textSelectionHandleColor color: Theme.of(context).textSelectionHandleColor
) )
) )
); );
// [handle] is a circle, with a rectangle in the top left quadrant of that
// circle (an onion pointing to 10:30). We rotate [handle] to point
// straight up or up-right depending on the handle type.
switch (type) { switch (type) {
case TextSelectionHandleType.left: case TextSelectionHandleType.left: // points up-right
// Shift the child left by 100% of its width, so its top-right corner return new Transform(
// touches the selection endpoint. transform: new Matrix4.identity().rotateZ(math.PI / 2.0),
return new FractionalTranslation(
translation: const FractionalOffset(-1.0, 0.0),
child: handle child: handle
); );
case TextSelectionHandleType.right: case TextSelectionHandleType.right: // points up-left
return handle; return handle;
case TextSelectionHandleType.collapsed: // points up
return new Transform(
transform: new Matrix4.identity().rotateZ(math.PI / 4.0),
child: handle
);
} }
} }
} }
...@@ -304,36 +311,20 @@ class _FormFieldData { ...@@ -304,36 +311,20 @@ class _FormFieldData {
/// Draws a single text selection handle. The [type] determines where the handle /// Draws a single text selection handle. The [type] determines where the handle
/// points (e.g. the [left] handle points up and to the right). /// points (e.g. the [left] handle points up and to the right).
class _TextSelectionHandlePainter extends CustomPainter { class _TextSelectionHandlePainter extends CustomPainter {
_TextSelectionHandlePainter({this.type, this.color}); _TextSelectionHandlePainter({ this.color });
final TextSelectionHandleType type;
final Color color; final Color color;
@override @override
void paint(Canvas canvas, Size size) { void paint(Canvas canvas, Size size) {
Paint paint = new Paint()..color = color; Paint paint = new Paint()..color = color;
// Each handle is a circle, with a rectangle in the top quadrant of that
// circle in the direction it's pointing. [rect] here is the size of the
// corner rect, e.g. half the diameter of the circle.
double radius = size.width/2.0; double radius = size.width/2.0;
canvas.drawCircle(new Point(radius, radius), radius, paint); canvas.drawCircle(new Point(radius, radius), radius, paint);
canvas.drawRect(new Rect.fromLTWH(0.0, 0.0, radius, radius), paint);
Rect rect;
switch (type) {
case TextSelectionHandleType.left:
rect = new Rect.fromLTWH(radius, 0.0, radius, radius);
break;
case TextSelectionHandleType.right:
rect = new Rect.fromLTWH(0.0, 0.0, radius, radius);
break;
}
canvas.drawRect(rect, paint);
} }
@override @override
bool shouldRepaint(_TextSelectionHandlePainter oldPainter) { bool shouldRepaint(_TextSelectionHandlePainter oldPainter) {
return type != oldPainter.type || return color != oldPainter.color;
color != oldPainter.color;
} }
} }
...@@ -289,10 +289,6 @@ class RawInputLineState extends ScrollableState<RawInputLine> { ...@@ -289,10 +289,6 @@ class RawInputLineState extends ScrollableState<RawInputLine> {
if (_keyboardClient.inputValue.text != config.value.text) { if (_keyboardClient.inputValue.text != config.value.text) {
_selectionHandles?.hide(); _selectionHandles?.hide();
_selectionHandles = null; _selectionHandles = null;
} else {
// If the text is unchanged, this was probably called for a selection
// change.
_selectionHandles?.update(_keyboardClient.inputValue.selection);
} }
} }
...@@ -310,6 +306,11 @@ class RawInputLineState extends ScrollableState<RawInputLine> { ...@@ -310,6 +306,11 @@ class RawInputLineState extends ScrollableState<RawInputLine> {
if (config.onChanged != null) if (config.onChanged != null)
config.onChanged(_keyboardClient.inputValue.copyWith(selection: selection)); config.onChanged(_keyboardClient.inputValue.copyWith(selection: selection));
if (_selectionHandles != null) {
_selectionHandles.hide();
_selectionHandles = null;
}
if (_selectionHandles == null && if (_selectionHandles == null &&
_keyboardClient.inputValue.text.isNotEmpty && _keyboardClient.inputValue.text.isNotEmpty &&
config.selectionHandleBuilder != null) { config.selectionHandleBuilder != null) {
......
...@@ -19,7 +19,7 @@ import 'overlay.dart'; ...@@ -19,7 +19,7 @@ import 'overlay.dart';
/// mixed text: '<the nwor<b quick fox' /// mixed text: '<the nwor<b quick fox'
/// Here 'the b' is selected, but 'brown' is RTL. Both are drawn with the /// Here 'the b' is selected, but 'brown' is RTL. Both are drawn with the
/// [left] type. /// [left] type.
enum TextSelectionHandleType { left, right } enum TextSelectionHandleType { left, right, collapsed }
/// Builds a handle of the given type. /// Builds a handle of the given type.
typedef Widget TextSelectionHandleBuilder(BuildContext context, TextSelectionHandleType type); typedef Widget TextSelectionHandleBuilder(BuildContext context, TextSelectionHandleType type);
...@@ -190,8 +190,10 @@ class _TextSelectionHandleOverlayState extends State<_TextSelectionHandleOverlay ...@@ -190,8 +190,10 @@ class _TextSelectionHandleOverlayState extends State<_TextSelectionHandleOverlay
TextSelectionHandleType ltrType, TextSelectionHandleType ltrType,
TextSelectionHandleType rtlType TextSelectionHandleType rtlType
) { ) {
// [direction] is null when it doesn't matter. if (config.selection.isCollapsed)
switch (endpoint.direction ?? TextDirection.ltr) { return TextSelectionHandleType.collapsed;
switch (endpoint.direction) {
case TextDirection.ltr: case TextDirection.ltr:
return ltrType; return ltrType;
case TextDirection.rtl: case TextDirection.rtl:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment