diff --git a/lib/core/editor/querya_code_editor.dart b/lib/core/editor/querya_code_editor.dart index 0d0741e6..0728a6d1 100644 --- a/lib/core/editor/querya_code_editor.dart +++ b/lib/core/editor/querya_code_editor.dart @@ -175,6 +175,13 @@ class _QueryaCodeEditorState extends State { ); } + /// A filling editor starts at the top, like every code editor; an + /// explicit [QueryaCodeEditor.textAlignVertical] still wins. (The field's own + /// default centres the text of a field that expands.) + material.TextAlignVertical? get _verticalAlignment => + widget.textAlignVertical ?? + (widget.expands ? material.TextAlignVertical.top : null); + Widget? _resolvedPlaceholder() { if (widget.placeholder != null) return widget.placeholder; return switch (widget.language) { @@ -202,7 +209,7 @@ class _QueryaCodeEditorState extends State { maxLines: widget.expands ? null : widget.maxLines, expands: widget.expands, style: style, - textAlignVertical: widget.textAlignVertical, + textAlignVertical: _verticalAlignment, decoration: material.InputDecoration( border: material.InputBorder.none, hintText: widget.hintText, @@ -221,6 +228,7 @@ class _QueryaCodeEditorState extends State { maxLines: widget.expands ? null : widget.maxLines, expands: widget.expands, style: style, + textAlignVertical: _verticalAlignment, placeholder: placeholder, onChanged: widget.onChanged, ); diff --git a/test/core/editor/querya_code_editor_alignment_test.dart b/test/core/editor/querya_code_editor_alignment_test.dart new file mode 100644 index 00000000..65cde133 --- /dev/null +++ b/test/core/editor/querya_code_editor_alignment_test.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart' as material; +import 'package:flutter/painting.dart' show TextPosition; +import 'package:flutter_test/flutter_test.dart'; +import 'package:querya_desktop/core/editor/querya_code_editor.dart'; +import 'package:querya_desktop/core/editor/querya_code_language.dart'; + +import '../../support/querya_theme_test_shell.dart'; + +void main() { + Future pump( + WidgetTester tester, { + QueryaCodeEditorVariant variant = QueryaCodeEditorVariant.shadcn, + material.TextAlignVertical? align, + }) async { + await tester.pumpWidget( + queryaThemeTestShell( + child: material.Center( + child: material.SizedBox( + width: 480, + height: 360, + child: QueryaCodeEditor( + language: QueryaCodeLanguage.plain, + variant: variant, + enableHighlighting: false, + textAlignVertical: align, + ), + ), + ), + ), + ); + await tester.pump(); + } + + Future textOffsetFromTop(WidgetTester tester) async { + await tester.enterText(find.byType(material.EditableText), 'select 1'); + await tester.pump(); + final field = tester.getRect(find.byType(QueryaCodeEditor)); + // The widget fills the field either way; the caret shows where the text is. + final render = tester + .state(find.byType(material.EditableText)) + .renderEditable; + final caret = render.getLocalRectForCaret(const TextPosition(offset: 0)); + return render.localToGlobal(caret.topLeft).dy - field.top; + } + + testWidgets('the shadcn editor starts its text at the top', (tester) async { + await pump(tester); + + expect(await textOffsetFromTop(tester), lessThan(60)); + }); + + testWidgets('the material editor starts its text at the top', + (tester) async { + await pump(tester, variant: QueryaCodeEditorVariant.material); + + expect(await textOffsetFromTop(tester), lessThan(60)); + }); + + testWidgets('an explicit alignment still wins', (tester) async { + await pump( + tester, + variant: QueryaCodeEditorVariant.material, + align: material.TextAlignVertical.center, + ); + + expect(await textOffsetFromTop(tester), greaterThan(100)); + }); +}