super_editor icon indicating copy to clipboard operation
super_editor copied to clipboard

When hovering & scrolling, the editor should consume available scroll space before parent can scroll

Open roughike opened this issue 4 years ago • 12 comments
trafficstars

Current behavior: When the mouse cursor is over the editor, the editor has scrollable content, and the page is being scrolled by the mouse wheel, the page and editor scroll at the same time. It's not possible to use the mouse scroll wheel to scroll to the bottom of the editor, because the editor moves at the same time while its inner content is scrolling.

scrolling-actual

The above example uses a SingleChildScrollView and a Column. I also tried to share a ScrollController between the Editor and SingleChildScrollView, but there was no difference in behavior.

Reproducible sample

import 'package:flutter/material.dart';
import 'package:super_editor/super_editor.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  ScrollController _scrollController;
  Document _doc;
  DocumentEditor _docEditor;

  @override
  void initState() {
    super.initState();
    _scrollController = ScrollController();
    _doc = _createInitialDocument();
    _docEditor = DocumentEditor(document: _doc);
  }

  @override
  void dispose() {
    _doc.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.blue,
      body: Scrollbar(
        child: SingleChildScrollView(
          controller: _scrollController,
          child: Column(
            children: [
              Center(
                child: ConstrainedBox(
                  constraints: BoxConstraints(maxWidth: 800),
                  child: Row(
                    children: [
                      Expanded(
                        child: Text(
                          'A supercharged rich text editor for Flutter',
                          style: TextStyle(fontSize: 40),
                        ),
                      ),
                      Expanded(
                        child: Container(
                          color: Colors.red,
                          height: 320,
                          child: Editor.standard(
                            scrollController: _scrollController,
                            editor: _docEditor,
                            padding: const EdgeInsets.symmetric(
                                vertical: 56, horizontal: 24),
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 16),
              for (var i = 0; i < 200; i++) ...[
                const SizedBox(height: 16),
                Text('blah blah blah blah blah blah blah blah blah blah blah')
              ]
            ],
          ),
        ),
      ),
    );
  }
}

Document _createInitialDocument() {
  return MutableDocument(
    nodes: [
      ParagraphNode(
        id: DocumentEditor.createNodeId(),
        text: AttributedText(
          text: 'Try Me! I’m live!!!!',
        ),
        metadata: {
          'blockType': 'header1',
        },
      ),
      ParagraphNode(
        id: DocumentEditor.createNodeId(),
        text: AttributedText(
          text: 'Try typing here and editing this text.',
        ),
      ),
    ],
  );
}

Expected behavior: When hovering the editor and scrolling, the page does not scroll if the editor has "unconsumed" scrollable content.

scrolling-expected

roughike avatar Apr 13 '21 12:04 roughike

Possibly related to #106

matthew-carroll avatar Apr 23 '21 22:04 matthew-carroll

@roughike do you know how this is typically handled in a Flutter UI? I imagine that the parent scroll view has to do something to make this possible, even if we also need changes to the editor UI. Any thoughts?

matthew-carroll avatar May 05 '21 07:05 matthew-carroll

Flutter UI, as in on mobile, desktop, or in general?

I tried to find an example of this scenario on a touch screen device, in native apps and Flutter apps, but I didn't find any. All of the examples of a multiline text field were fixed on the bottom of the screen, or there was no restrictions on the number of lines.

I made a sample with the Material TextField widget that I think has the behavior as I would expect it to have:

scrolling_mobile

The same thing works out of the box roughly in the way I would expect it to work on the web. It's not 100% there, but I would say pretty close and more than acceptable:

scrolling_web

Sample code

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('scrolly mcscrollface'),
      ),
      body: SingleChildScrollView(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            for (var i = 0; i < 75; i++) const Text('some filler content'),
            TextField(maxLines: 5),
            for (var i = 0; i < 75; i++) const Text('some filler content'),
          ],
        ),
      ),
    );
  }
}

DartPad link

roughike avatar May 10 '21 06:05 roughike

@rutvik110 would you like to give this ticket a try? If so, let me know, and I'll assign it to you.

matthew-carroll avatar Sep 03 '23 19:09 matthew-carroll

yah, I'll check on this issue.

rutvik110 avatar Sep 08 '23 02:09 rutvik110

@roughike This issue doesn't seem to be present any longer. Testing this on master, and the scrolling behaviour's working as you wished it should when the editor is present within an parent scrollable.

Here's a latest demo where you can see the editor's scrollable content if any is consumed if it's in focus/we're hovering over it before parent scrollable is scrolled. Also tested this with mouse scroll wheel, works fine.

Latest example

import 'package:example/marketing_video/main_marketing_video.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:super_editor/super_editor.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late ScrollController _scrollController;
  late MutableDocument _doc;
  late MutableDocumentComposer _composer;
  late Editor _docEditor;

  @override
  void initState() {
    super.initState();
    _scrollController = ScrollController();
    _doc = _createInitialDocument();
    _composer = MutableDocumentComposer();

    _docEditor = createDefaultDocumentEditor(document: _doc, composer: _composer);
  }

  @override
  void dispose() {
    _doc.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.blue,
      body: Scrollbar(
        child: SingleChildScrollView(
          controller: _scrollController,
          child: Column(
            children: [
              const SizedBox(height: 16),
              for (var i = 0; i < 50; i++) ...[
                const SizedBox(height: 16),
                Text('blah blah blah blah blah blah blah blah blah blah blah')
              ],
              Center(
                child: ConstrainedBox(
                  constraints: BoxConstraints(maxWidth: 800),
                  child: Row(
                    children: [
                      Expanded(
                        child: Text(
                          'A supercharged rich text editor for Flutter',
                          style: TextStyle(fontSize: 40),
                        ),
                      ),
                      Expanded(
                        child: SizedBox(
                          height: 320,
                          child: ListView(
                            children: [
                              ColoredBox(
                                color: Colors.red,
                                child: SuperEditor(
                                  document: _doc,
                                  composer: _composer,
                                  scrollController: _scrollController,
                                  editor: _docEditor,
                                ),
                              ),
                            ],
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 16),
              for (var i = 0; i < 200; i++) ...[
                const SizedBox(height: 16),
                Text('blah blah blah blah blah blah blah blah blah blah blah')
              ]
            ],
          ),
        ),
      ),
    );
  }
}

MutableDocument _createInitialDocument() {
  return MutableDocument(
      nodes: List.generate(
    20,
    (index) => ParagraphNode(
        id: Editor.createNodeId(),
        text: AttributedText(
          "${index + 1} Scrollable content",
        )),
  ));
}

https://github.com/superlistapp/super_editor/assets/65209850/93260d80-9413-45ab-bb29-b5b82fd5f9bf

rutvik110 avatar Dec 28 '23 16:12 rutvik110

@matthew-carroll The mentioned issue is no longer present within super_editor. Likely resolved when #106 got fixed.

Though right now we seem to be lacking tests for this scrolling behaviour. If you would like I can go ahead and add tests relevant to it to super_editor.

rutvik110 avatar Dec 28 '23 17:12 rutvik110

@rutvik110 I'm curious how this could have been solved by #106 when this issue is #120?

In any event, yes, let's add tests for this.

matthew-carroll avatar Dec 28 '23 19:12 matthew-carroll

@rutvik110 I'm curious how this could have been solved by #106 when this issue is #120?

I assumed it may've been part of #106 as it was mentioned earlier in this issue. That may've been totally another thing though.

rutvik110 avatar Dec 29 '23 05:12 rutvik110

@matthew-carroll I think we can close down this issue as the behaviour is as expected in super_editor as @roughike mentioned.

Beside this, I'm working on some tests for locking down this behaviour in #1733 PR but that could go on separately.

rutvik110 avatar Jan 03 '24 06:01 rutvik110

If #1733 is about locking down behavior for this issue ticket then we can consider that to be a PR to close this issue. Let's get that work finished and merged and have it close this issue.

matthew-carroll avatar Jan 03 '24 06:01 matthew-carroll

We spoke offline. Apparently the web scrolling behavior for this ticket works as expected, but the desktop (Mac, Windows, Linux) scrolling doesn't work as expected. The goal right now is for @rutvik110 to find out why there's a difference. Once we know exactly why things work on one platform and not another platform, we'll decide what to do in terms of changes/fixes/tests.

matthew-carroll avatar Mar 01 '24 03:03 matthew-carroll

@matthew-carroll Rutvik's sample seem to work both on desktop and web. This sample works because the editor is wrapped with a ListView. Therefore it's just like the editor was inside a CustomScrollView, where the editor scrolls the ancestor scrollable.

If wrapping the editor with a ListView is acceptable, I think we should just close this ticket.

angelosilvestre avatar Jun 25 '24 22:06 angelosilvestre

Ok. We can close this. If others have related issues, please file a new ticket with clear reproduction steps and desired behaviors.

matthew-carroll avatar Jun 27 '24 21:06 matthew-carroll