Skip to content
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

【feature request】support line wrap #6

Open
dreamer2q opened this issue Mar 12, 2021 · 7 comments
Open

【feature request】support line wrap #6

dreamer2q opened this issue Mar 12, 2021 · 7 comments
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@dreamer2q
Copy link

This package is really awesome !

But somehow, it displays lines without wrap, which means you have to scroll this widget horizontally to view it all.

Can anyone help me with this feature? (I think it really important!)

@baderouaich baderouaich added the enhancement New feature or request label Mar 12, 2021
@baderouaich
Copy link
Owner

baderouaich commented Mar 12, 2021

Hello @dreamer2q, thank you for submitting this issue.
i will see what i can do to add this feature as soon as possible.

TODO List:

  • Wrap text instead of scrolling horizontally
  • Make some tests for compatibility
  • Fix issue of line numbers get messed up in wrong order when enabling softWrap in RichText (the lines will be broken in the view but text will remain the same, leaving '\n'.allMatches unusefull in this case...)

@baderouaich
Copy link
Owner

Adding softWrap: true to the RichText seem to resolve your issue, but its not compatible with line numbers. as to why i dont think i will add this one to the master branch yet.
although you can modify it in your side until there is a better solution, here are the changes:


flutter_syntax_view.dart

import 'package:flutter/material.dart';

import 'syntax/index.dart';

class SyntaxView extends StatefulWidget {
  SyntaxView(
      {@required this.code,
      @required this.syntax,
      this.syntaxTheme,
      this.withZoom = true,
      this.withLinesCount = false,
      this.fontSize = 12.0,
      this.expanded = false,
      this.softWrap = true});

  /// Code text
  final String code;

  /// Syntax/Langauge (Dart, C, C++...)
  final Syntax syntax;

  /// Enable/Disable touch zooming  (default: true)
  final bool withZoom;

  /// Enable/Disable line number in left  (default: true)
  final bool withLinesCount;

  /// Theme of syntax view example SyntaxTheme.dracula()
  final SyntaxTheme syntaxTheme;

  /// Font Size with a default value of 12.0
  final double fontSize;

  /// Expansion which allows the SyntaxView to be used inside a Column or a ListView...  (default: false)
  final bool expanded;

  /// Wrap text Whether the code text should break at soft line breaks.  (default: true)
  final bool softWrap;

  @override
  State<StatefulWidget> createState() => SyntaxViewState();
}

class SyntaxViewState extends State<SyntaxView> {
  @override
  void initState() {
    super.initState();
    _fontSize = widget.fontSize;
  }

  /// Zoom Controls
  static const double FONT_SIZE_MIN = 0.5;

  /// Minimum zoom font size
  static const double FONT_SIZE_MAX = 6.0;

  /// Maximum zoom font size
  static const double FONT_BASE_SCALE = 12.0;

  /// Font size & scale
  double _fontSize;
  double _fontScale = 1.0;
  double _fontScaleFactor = 1.0;

  @override
  Widget build(BuildContext context) {
    assert(widget.code != null,
        "Code Content must not be null.\n===| if you are loading a String from assets, make sure you declare it in pubspec.yaml |===");
    assert(widget.syntax != null,
        "Syntax must not be null. select a Syntax by calling Syntax.(Language)");

    return GestureDetector(
        onScaleStart: (final ScaleStartDetails ascaleStartDetails) {
          /// Don't apply zooming if disabled
          if (!widget.withZoom) {
            return;
          }
          _fontScaleFactor = _fontScale;
        },
        onScaleUpdate: (final ScaleUpdateDetails scaleUpdateDetails) {
          /// Don't apply zooming if disabled
          if (!widget.withZoom) {
            return;
          }

          /// Don't update the UI if the scale didn't change
          if (scaleUpdateDetails.scale == 1.0) {
            return;
          }

          if (mounted) {
            setState(() {
              _fontScale = (_fontScaleFactor * scaleUpdateDetails.scale)
                  .clamp(FONT_SIZE_MIN, FONT_SIZE_MAX);
              _fontSize = _fontScale * FONT_BASE_SCALE;
            });
          }
        },
        child: Container(
          padding: (widget.withLinesCount ?? true)
              ? const EdgeInsets.only(left: 5, top: 10, right: 10, bottom: 10)
              : const EdgeInsets.all(10),
          color: (widget.syntaxTheme ?? SyntaxTheme.dracula()).backgroundColor,
          constraints: widget.expanded ? BoxConstraints.expand() : null,
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              /// Lines count
              /// TODO: when softWrap enabled, lines count get messed up
              if (widget.withLinesCount) _linesCountWidget(),
              if (widget.withLinesCount) VerticalDivider(width: 5),

              /// Code text view
              Expanded(child: _codeTextWidget())
            ],
          ),
        ));
  }

  /// Widgets parts ///

  /// Lines number to the left
  Widget _linesCountWidget() {
    final int numLines = '\n'.allMatches(widget.code).length + 1;
    return Column(children: <Widget>[
      for (int i = 1; i <= numLines; i++)
        RichText(
            textScaleFactor: _fontScaleFactor,
            text: TextSpan(
                style: TextStyle(
                    fontFamily: 'monospace',
                    fontSize: _fontSize,
                    color: (widget.syntaxTheme ?? SyntaxTheme.dracula())
                        .linesCountColor),
                text: "$i"))
    ]);
  }

  // Code text
  Widget _codeTextWidget() {
    if (widget.softWrap) {
      return RichText(
        softWrap: true,
        overflow: TextOverflow.clip,
        textScaleFactor: _fontScaleFactor,
        text: TextSpan(
          style: TextStyle(fontFamily: 'monospace', fontSize: _fontSize),
          children: <TextSpan>[
            getSyntax(widget.syntax, widget.syntaxTheme).format(widget.code)
          ],
        ),
      );
    } else {
      return SingleChildScrollView(
          child: SingleChildScrollView(
              scrollDirection: Axis.horizontal,
              child: RichText(
                softWrap: false,
                textScaleFactor: _fontScaleFactor,
                text: TextSpan(
                  style:
                      TextStyle(fontFamily: 'monospace', fontSize: _fontSize),
                  children: <TextSpan>[
                    getSyntax(widget.syntax, widget.syntaxTheme)
                        .format(widget.code)
                  ],
                ),
              )));
    }
  }
}

@baderouaich
Copy link
Owner

Hi @rodydavis, i hope you are doing fine.
we could use your advice to add this feature.
thank you

@baderouaich baderouaich added the help wanted Extra attention is needed label Mar 13, 2021
@dreamer2q
Copy link
Author

dreamer2q commented Mar 14, 2021

@BaderEddineOuaich Since softWrap set to true, there is no need to scroll, so my idea is that, we can add line number in

getSyntax(widget.syntax, widget.syntaxTheme).format(widget.code)

In this way, line number is messed in code viewer widget, it might corrupt copy & paste features.

@rodydavis
Copy link
Contributor

I solved this issue by changing the logic for painting line numbers. I draw the paragraph to guess at how many lines there would be with a soft wrap. I maintain my own fork since https://widget.studio depends on it

@baderouaich
Copy link
Owner

Thanks @rodydavis for mentioning that.
I tried guessing numbers in your way of TextPainter and ui.LineMetrics when softwrap is enabled (instead of just counting '\n') matches. now it seem to work just fine in my side.
I need someone to confirm changes so i can publish with version 4.2.2.
@dreamer2q can you give the last changes in this repository a test to see if it fulfills yours needs before publishing?
Thank you guys!

@rodydavis
Copy link
Contributor

Sure thing! 👍🏼

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

3 participants