From a71e93498e58f9ce807c74d24acdd4cfc79a742f Mon Sep 17 00:00:00 2001 From: Yestay <79095619+yescorp@users.noreply.github.com> Date: Fri, 27 Jan 2023 19:38:33 +0600 Subject: [PATCH] Gutter customization (#167) * WIP * small rearrangements * margin adjustments, example added * variable replaced with getter * example 03 restored * ignores removed * example 4 moved to example 3 * checkbox color changed to transparent white from grey * GutterStyle renamed instead of creating new file * GutterStyle.none added * padding changed * ignores inlined * import fixes * example adjustments * Code style (#159) Co-authored-by: Yestay Tastanov Co-authored-by: Alexey Inkin --- .../03.change_language_theme/home_screen.dart | 43 +++++++++- lib/flutter_code_editor.dart | 2 +- lib/src/code_field/code_field.dart | 46 ++++++----- lib/src/gutter/gutter.dart | 78 +++++++++++++------ ...ne_number_style.dart => gutter_style.dart} | 44 ++++++++--- 5 files changed, 159 insertions(+), 54 deletions(-) rename lib/src/line_numbers/{line_number_style.dart => gutter_style.dart} (51%) diff --git a/example/lib/03.change_language_theme/home_screen.dart b/example/lib/03.change_language_theme/home_screen.dart index fa542a02..47ea1d5c 100644 --- a/example/lib/03.change_language_theme/home_screen.dart +++ b/example/lib/03.change_language_theme/home_screen.dart @@ -9,6 +9,9 @@ import 'widgets/dropdown_selector.dart'; const _defaultLanguage = 'java'; const _defaultTheme = 'monokai-sublime'; +const toggleButtonColor = Color.fromARGB(124, 255, 255, 255); +const toggleButtonActiveColor = Colors.white; + class HomeScreen extends StatefulWidget { @override State createState() => _HomeScreenState(); @@ -17,6 +20,11 @@ class HomeScreen extends StatefulWidget { class _HomeScreenState extends State { String _language = _defaultLanguage; String _theme = _defaultTheme; + + bool _showNumbers = true; + bool _showErrors = true; + bool _showFoldingHandles = true; + final _codeFieldFocusNode = FocusNode(); late final _codeController = CodeController( language: builtinLanguages[_language], @@ -31,6 +39,33 @@ class _HomeScreenState extends State { appBar: AppBar( title: const Text('Code Editor by Akvelon'), actions: [ + // + IconButton( + color: _showNumbers ? toggleButtonActiveColor : toggleButtonColor, + onPressed: () => setState(() { + _showNumbers = !_showNumbers; + }), + icon: const Icon(Icons.numbers), + ), + + IconButton( + color: _showErrors ? toggleButtonActiveColor : toggleButtonColor, + onPressed: () => setState(() { + _showErrors = !_showErrors; + }), + icon: const Icon(Icons.cancel), + ), + + IconButton( + color: _showFoldingHandles + ? toggleButtonActiveColor + : toggleButtonColor, + onPressed: () => setState(() { + _showFoldingHandles = !_showFoldingHandles; + }), + icon: const Icon(Icons.chevron_right), + ), + const SizedBox(width: 20), DropdownSelector( onChanged: _setLanguage, icon: Icons.code, @@ -44,6 +79,7 @@ class _HomeScreenState extends State { value: _theme, values: themeList, ), + const SizedBox(width: 20), ], ), body: ListView( @@ -54,10 +90,13 @@ class _HomeScreenState extends State { focusNode: _codeFieldFocusNode, controller: _codeController, textStyle: const TextStyle(fontFamily: 'SourceCode'), - lineNumberStyle: const LineNumberStyle( - textStyle: TextStyle( + gutterStyle: GutterStyle( + textStyle: const TextStyle( color: Colors.purple, ), + showLineNumbers: _showNumbers, + showErrors: _showErrors, + showFoldingHandles: _showFoldingHandles, ), ), ), diff --git a/lib/flutter_code_editor.dart b/lib/flutter_code_editor.dart index a334c4b6..cab9fc39 100644 --- a/lib/flutter_code_editor.dart +++ b/lib/flutter_code_editor.dart @@ -22,7 +22,7 @@ export 'src/folding/foldable_block_type.dart'; export 'src/folding/invalid_foldable_block.dart'; export 'src/folding/parsers/highlight.dart'; -export 'src/line_numbers/line_number_style.dart'; +export 'src/line_numbers/gutter_style.dart'; export 'src/named_sections/named_section.dart'; export 'src/named_sections/parsers/abstract.dart'; diff --git a/lib/src/code_field/code_field.dart b/lib/src/code_field/code_field.dart index b34bbcc8..fe068dcb 100644 --- a/lib/src/code_field/code_field.dart +++ b/lib/src/code_field/code_field.dart @@ -7,7 +7,7 @@ import 'package:linked_scroll_controller/linked_scroll_controller.dart'; import '../code_theme/code_theme.dart'; import '../gutter/gutter.dart'; -import '../line_numbers/line_number_style.dart'; +import '../line_numbers/gutter_style.dart'; import '../sizes.dart'; import '../wip/autocomplete/popup.dart'; import 'actions/comment_uncomment.dart'; @@ -106,8 +106,8 @@ class CodeField extends StatefulWidget { /// language highlight, themeing and modifiers. final CodeController controller; - /// A LineNumberStyle instance to tweak the line number column styling - final LineNumberStyle lineNumberStyle; + @Deprecated('Use gutterStyle instead') + final GutterStyle lineNumberStyle; /// {@macro flutter.widgets.textField.cursorColor} final Color? cursorColor; @@ -132,7 +132,11 @@ class CodeField extends StatefulWidget { final Decoration? decoration; final TextSelectionThemeData? textSelectionTheme; final FocusNode? focusNode; - final bool lineNumbers; + + @Deprecated('Use gutterStyle instead') + final bool? lineNumbers; + + final GutterStyle gutterStyle; const CodeField({ super.key, @@ -145,7 +149,7 @@ class CodeField extends StatefulWidget { this.decoration, this.textStyle, this.padding = EdgeInsets.zero, - this.lineNumberStyle = const LineNumberStyle(), + GutterStyle? gutterStyle, this.enabled, this.readOnly = false, this.cursorColor, @@ -153,8 +157,15 @@ class CodeField extends StatefulWidget { this.lineNumberBuilder, this.focusNode, this.onChanged, - this.lineNumbers = true, - }); + @Deprecated('Use gutterStyle instead') this.lineNumbers, + @Deprecated('Use gutterStyle instead') + this.lineNumberStyle = const GutterStyle(), + }) : assert( + gutterStyle == null || lineNumbers == null, + 'Can not provide gutterStyle and lineNumbers at the same time. ' + 'Please use gutterStyle and provide necessary columns to show/hide'), + gutterStyle = gutterStyle ?? + ((lineNumbers == false) ? GutterStyle.none : lineNumberStyle); @override State createState() => _CodeFieldState(); @@ -310,26 +321,25 @@ class _CodeFieldState extends State { textStyle = defaultTextStyle.merge(widget.textStyle); final lineNumberSize = textStyle.fontSize; - final lineNumberColor = widget.lineNumberStyle.textStyle?.color ?? - textStyle.color?.withOpacity(.5); + final lineNumberColor = + widget.gutterStyle.textStyle?.color ?? textStyle.color?.withOpacity(.5); final lineNumberTextStyle = - (widget.lineNumberStyle.textStyle ?? textStyle).copyWith( + (widget.gutterStyle.textStyle ?? textStyle).copyWith( color: lineNumberColor, fontFamily: textStyle.fontFamily, fontSize: lineNumberSize, ); - final lineNumberStyle = widget.lineNumberStyle.copyWith( + final gutterStyle = widget.gutterStyle.copyWith( textStyle: lineNumberTextStyle, ); - Widget? numberCol; - - if (widget.lineNumbers) { - numberCol = GutterWidget( + Widget? gutter; + if (gutterStyle.showGutter) { + gutter = GutterWidget( codeController: widget.controller, - style: lineNumberStyle, + style: gutterStyle, ); } @@ -376,11 +386,11 @@ class _CodeFieldState extends State { decoration: widget.decoration, color: backgroundCol, key: _codeFieldKey, - padding: !widget.lineNumbers ? const EdgeInsets.only(left: 8) : null, + padding: const EdgeInsets.only(left: 8), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (widget.lineNumbers && numberCol != null) numberCol, + if (gutter != null) gutter, Expanded( child: Stack( children: [ diff --git a/lib/src/gutter/gutter.dart b/lib/src/gutter/gutter.dart index ef663e3c..0723fa72 100644 --- a/lib/src/gutter/gutter.dart +++ b/lib/src/gutter/gutter.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import '../code_field/code_controller.dart'; -import '../line_numbers/line_number_style.dart'; +import '../line_numbers/gutter_style.dart'; import 'error.dart'; import 'fold_toggle.dart'; @@ -19,7 +19,7 @@ class GutterWidget extends StatelessWidget { }); final CodeController codeController; - final LineNumberStyle style; + final GutterStyle style; @override Widget build(BuildContext context) { @@ -32,32 +32,44 @@ class GutterWidget extends StatelessWidget { Widget _buildOnChange(BuildContext context, Widget? child) { final code = codeController.code; - final tableRows = [ - for (final i in code.hiddenLineRanges.visibleLineNumbers) - TableRow( - children: [ - Text( - '${i + 1}', - style: style.textStyle, - textAlign: style.textAlign, - ), - const SizedBox(), - const SizedBox(), - ], - ), - ]; - - _fillIssues(tableRows); - _fillFoldToggles(tableRows); + final gutterWidth = style.width - + (style.showErrors ? 0 : _issueColumnWidth) - + (style.showFoldingHandles ? 0 : _foldingColumnWidth); + + final issueColumnWidth = style.showErrors ? _issueColumnWidth : 0.0; + final foldingColumnWidth = + style.showFoldingHandles ? _foldingColumnWidth : 0.0; + + final tableRows = List.generate( + code.hiddenLineRanges.visibleLineNumbers.length, + // ignore: prefer_const_constructors + (i) => TableRow( + // ignore: prefer_const_literals_to_create_immutables + children: [ + const SizedBox(), + const SizedBox(), + const SizedBox(), + ], + ), + ); + + _fillLineNumbers(tableRows); + + if (style.showErrors) { + _fillIssues(tableRows); + } + if (style.showFoldingHandles) { + _fillFoldToggles(tableRows); + } return Container( padding: EdgeInsets.only(top: 12, bottom: 12, right: style.margin), - width: style.width, + width: style.showLineNumbers ? gutterWidth : null, child: Table( - columnWidths: const { - _lineNumberColumn: FlexColumnWidth(), - _issueColumn: FixedColumnWidth(_issueColumnWidth), - _foldingColumn: FixedColumnWidth(_foldingColumnWidth), + columnWidths: { + _lineNumberColumn: const FlexColumnWidth(), + _issueColumn: FixedColumnWidth(issueColumnWidth), + _foldingColumn: FixedColumnWidth(foldingColumnWidth), }, defaultVerticalAlignment: TableCellVerticalAlignment.middle, children: tableRows, @@ -65,6 +77,24 @@ class GutterWidget extends StatelessWidget { ); } + void _fillLineNumbers(List tableRows) { + final code = codeController.code; + + for (final i in code.hiddenLineRanges.visibleLineNumbers) { + final lineIndex = _lineIndexToTableRowIndex(i); + + if (lineIndex == null) { + continue; + } + + tableRows[lineIndex].children![_lineNumberColumn] = Text( + style.showLineNumbers ? '${i + 1}' : ' ', + style: style.textStyle, + textAlign: style.textAlign, + ); + } + } + void _fillIssues(List tableRows) { final code = codeController.code; diff --git a/lib/src/line_numbers/line_number_style.dart b/lib/src/line_numbers/gutter_style.dart similarity index 51% rename from lib/src/line_numbers/line_number_style.dart rename to lib/src/line_numbers/gutter_style.dart index 9fff9a24..9f8278b1 100644 --- a/lib/src/line_numbers/line_number_style.dart +++ b/lib/src/line_numbers/gutter_style.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; -// TODO(alexeyinkin): Rename to GutterStyle when we break compatibility. -class LineNumberStyle { +class GutterStyle { /// Width of the line number column. final double width; @@ -18,28 +17,55 @@ class LineNumberStyle { /// half the opacity. final TextStyle? textStyle; - /// Background of the line number column + /// Background of the line number column. final Color? background; /// Central horizontal margin between the numbers and the code. final double margin; - const LineNumberStyle({ - this.width = 80.0, - this.textAlign = TextAlign.right, + /// Whether to show line numbers column. + final bool showLineNumbers; + + /// Whether to show errors column. + final bool showErrors; + + /// Whether to show folding handles column. + final bool showFoldingHandles; + + /// Whether there is any column to show in gutter. + bool get showGutter => showLineNumbers || showErrors || showFoldingHandles; + + const GutterStyle({ this.margin = 10.0, - this.textStyle, + this.textAlign = TextAlign.right, + this.showErrors = true, + this.showFoldingHandles = true, + this.showLineNumbers = true, + this.width = 80.0, this.background, + this.textStyle, }); - LineNumberStyle copyWith({ + static const GutterStyle none = GutterStyle( + showErrors: false, + showFoldingHandles: false, + showLineNumbers: false, + ); + + GutterStyle copyWith({ TextStyle? textStyle, }) => - LineNumberStyle( + GutterStyle( width: width, textAlign: textAlign, textStyle: textStyle ?? this.textStyle, background: background, margin: margin, + showErrors: showErrors, + showFoldingHandles: showFoldingHandles, + showLineNumbers: showLineNumbers, ); } + +@Deprecated('Renamed to GutterStyle') +typedef LineNumberStyle = GutterStyle;