-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor focusing to contextLine #24674
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,8 @@ | |
* limitations under the License. | ||
*/ | ||
|
||
import 'dart:math'; | ||
|
||
import 'package:flutter/widgets.dart'; | ||
import 'package:flutter_code_editor/flutter_code_editor.dart'; | ||
import 'package:get_it/get_it.dart'; | ||
|
@@ -77,6 +79,7 @@ class SnippetEditingController extends ChangeNotifier { | |
codeController.removeListener(_onCodeControllerChanged); | ||
setSource(example.source); | ||
_applyViewOptions(viewOptions); | ||
_toStartOfContextLineIfAny(); | ||
codeController.addListener(_onCodeControllerChanged); | ||
|
||
notifyListeners(); | ||
|
@@ -100,6 +103,31 @@ class SnippetEditingController extends ChangeNotifier { | |
} | ||
} | ||
|
||
void _toStartOfContextLineIfAny() { | ||
final contextLine1Based = selectedExample?.contextLine; | ||
|
||
if (contextLine1Based == null) { | ||
return; | ||
} | ||
|
||
_toStartOfFullLine(max(contextLine1Based - 1, 0)); | ||
} | ||
|
||
void _toStartOfFullLine(int line) { | ||
if (line >= codeController.code.lines.length) { | ||
return; | ||
} | ||
|
||
final fullPosition = codeController.code.lines.lines[line].textRange.start; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a check. |
||
final visiblePosition = codeController.code.hiddenRanges.cutPosition( | ||
fullPosition, | ||
); | ||
|
||
codeController.selection = TextSelection.collapsed( | ||
offset: visiblePosition, | ||
); | ||
} | ||
|
||
Example? get selectedExample => _selectedExample; | ||
|
||
ExampleLoadingDescriptor? get descriptor => _descriptor; | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,31 +16,120 @@ | |
* limitations under the License. | ||
*/ | ||
|
||
import 'package:flutter/widgets.dart'; | ||
import 'dart:math'; | ||
|
||
import 'package:flutter/material.dart'; | ||
import 'package:flutter/scheduler.dart'; | ||
import 'package:flutter_code_editor/flutter_code_editor.dart'; | ||
|
||
import '../controllers/snippet_editing_controller.dart'; | ||
import 'editor_textarea.dart'; | ||
import '../theme/theme.dart'; | ||
|
||
class SnippetEditor extends StatelessWidget { | ||
class SnippetEditor extends StatefulWidget { | ||
final SnippetEditingController controller; | ||
final bool isEditable; | ||
final bool goToContextLine; | ||
|
||
const SnippetEditor({ | ||
SnippetEditor({ | ||
required this.controller, | ||
required this.isEditable, | ||
required this.goToContextLine, | ||
}); | ||
}) : super( | ||
// When the example is changed, will scroll to the context line again. | ||
key: ValueKey(controller.selectedExample), | ||
); | ||
|
||
@override | ||
State<SnippetEditor> createState() => _SnippetEditorState(); | ||
} | ||
|
||
class _SnippetEditorState extends State<SnippetEditor> { | ||
bool _didAutoFocus = false; | ||
final _focusNode = FocusNode(); | ||
final _scrollController = ScrollController(); | ||
|
||
@override | ||
void didChangeDependencies() { | ||
super.didChangeDependencies(); | ||
|
||
if (!_didAutoFocus) { | ||
_didAutoFocus = true; | ||
SchedulerBinding.instance.addPostFrameCallback((_) { | ||
if (mounted) { | ||
_scrollSoCursorIsOnTop(); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
void _scrollSoCursorIsOnTop() { | ||
_focusNode.requestFocus(); | ||
|
||
final position = max(widget.controller.codeController.selection.start, 0); | ||
final characterOffset = _getLastCharacterOffset( | ||
text: widget.controller.codeController.text.substring(0, position), | ||
style: kLightTheme.extension<BeamThemeExtension>()!.codeRootStyle, | ||
); | ||
|
||
_scrollController.jumpTo( | ||
min( | ||
characterOffset.dy, | ||
_scrollController.position.maxScrollExtent, | ||
), | ||
); | ||
} | ||
|
||
@override | ||
void dispose() { | ||
_focusNode.dispose(); | ||
super.dispose(); | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return EditorTextArea( | ||
codeController: controller.codeController, | ||
sdk: controller.sdk, | ||
enabled: !(controller.selectedExample?.isMultiFile ?? false), | ||
example: controller.selectedExample, | ||
isEditable: isEditable, | ||
goToContextLine: goToContextLine, | ||
final ext = Theme.of(context).extension<BeamThemeExtension>()!; | ||
final isMultiFile = widget.controller.selectedExample?.isMultiFile ?? false; | ||
final isEnabled = widget.isEditable && !isMultiFile; | ||
|
||
return Semantics( | ||
container: true, | ||
enabled: isEnabled, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sort? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
label: 'widgets.codeEditor.label', | ||
multiline: true, | ||
readOnly: isEnabled, | ||
textField: true, | ||
child: FocusScope( | ||
node: FocusScopeNode(canRequestFocus: isEnabled), | ||
child: CodeTheme( | ||
data: ext.codeTheme, | ||
child: Container( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It requires non-nullable color, ours is nullable. |
||
color: ext.codeTheme.styles['root']?.backgroundColor, | ||
child: SingleChildScrollView( | ||
controller: _scrollController, | ||
child: CodeField( | ||
key: ValueKey(widget.controller.codeController), | ||
controller: widget.controller.codeController, | ||
enabled: isEnabled, | ||
focusNode: _focusNode, | ||
textStyle: ext.codeRootStyle, | ||
), | ||
), | ||
), | ||
), | ||
), | ||
); | ||
} | ||
} | ||
|
||
Offset _getLastCharacterOffset({ | ||
required String text, | ||
required TextStyle style, | ||
}) { | ||
final textPainter = TextPainter( | ||
textDirection: TextDirection.ltr, | ||
text: TextSpan(text: text, style: style), | ||
)..layout(); | ||
|
||
return textPainter.getOffsetForCaret( | ||
TextPosition(offset: text.length), | ||
Rect.zero, | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_moveCursorToStartOfContextLineIfAny
,_selectStartOfContextLineIfAny
or_focusOnStartOfContextLineIfAny
may be more clear.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rather keep this.