Skip to content

Latest commit

 

History

History
79 lines (63 loc) · 5.37 KB

selection.md

File metadata and controls

79 lines (63 loc) · 5.37 KB

Selection

Text can be selected on screen. Additional widgets can be rendered to control the text actions. One might expect that the copy paste menus are implemented by the system. Actually these are fully controller by Visual Editor. We currently have 2 ways of customizing the selection menu:

  • Attaching To Markers (new) - When the selection callbacks have emitted we can use the rectangles data to place any attachment anywhere. Recommended when you want to place atypical looking markers related to the lines of selected text.
  • TextSelectionControls (old) - Standard flutter procedure using a custom TextSelectionControls. Recommended when you want to display standard selection menu with custom buttons.

Architecture Overview

This is a breakdown of the various parts that are used to execute updates of the selection on screen.

  • TextGestures - Starting and updating the selection is triggered from the TextGestures widget. The TextGestures widget is used as a wrapper over the document text in the main build() method. It adds a gesture detector which interprets the various touchscreen signals into potential gestures.
  • TextGesturesService - Controls the selection of text after tapDown and tapUp events.
  • SelectionService - Once the raw events are parsed then we send higher level calls to the SelectionService.
    • selectWordsInRange() - One highly interesting method in the codebase is selectWordsInRange(). This methods teaches you a lot about how the document model and editor work together. This method makes use of selectWordsInRange() and getWordAtPosition(). These methods are highly useful for computing coordinates on screens. They are now exposed to the public in the EditorController.
  • State Store - Once the selection range is detected it's then passed to the state store.
  • controller.updateSelection() - Once a gesture is detected in the callback executed, then the controller.updateSelection() is called which triggers the runBuild() which triggers a build() in main.
  • build() -Remember, the main build() contains the widgets generated by the documentBlocsAndLines(). These are a list of editable text lines and blocks.
  • EditableTextLine - Once build() is triggered each EditableTextLine widget, then calls on the renderer object methods to update information. If the renderer callbacks notice that the selection is changed and within it's bounds then it triggers a paint() cycle.
  • paint(), Rectangles - A new paint() cycle will "render" the selection rectangles vector data on top of the text. Thanks to the rectangles coordinates the client devs have total freedom to place their attachments wherever needed.
  • onSelectionCompleted() - This callback is invoked after the full build cycle is completed. This ensures that we have acces to the rectangles data. The rectangles data is computed during the paint() phase.

Attaching Widgets To Rectangles

This is a general overview of setting up a selection menu or custom widgets when the text selection has changed. To view a complete sample go to the SelectionMenuPage and inspect the code. Looking at the demo code one can believe that the current API for managing selection menu is rather complex. However, our goal is not to provide a minified toolbars as the selection menu. Our goal is to provide an API that is versatile enough to be used for any menu, in any position triggered by any conditions you can think of. This gives maximum flexibility to implement any kind of UX interactions you might need for your particular app. Even the positioning logic was left open for the client developers to best decide what fits them best. If all you need is to setup a new button in the custom menu, then try using the custom controls option.

Widget build(BuildContext context) => Stack(
  children: [
    DemoPageScaffold(
      child: _controller != null
          ? _col(
              children: [
                _editor(),
                _toolbar(),
              ],
            )
          : Loading(),
    ),
    
    // Has to be a Positioned Widget (anything you need)
    if (_isQuickMenuVisible) _quickMenu(),
  ],
);

Init the editor with callbacks hooked to the selection events.

Widget _editor() => VisualEditor(
  controller: _controller,
  scrollController: _scrollController,
  focusNode: _focusNode,
  config: EditorConfigM(
    onScroll: _updateQuickMenuPosition,
    
    // Hide menu while the selection is changing
    onSelectionChanged: (selection, rectangles) {
      _hideQuickMenu();
    },
    
    // Trigger the rendering of the attached menu or custom widgets
    onSelectionCompleted: (markers) {
      final isCollapsed = _controller?.selection.isCollapsed ?? true;
      
      // Don't render menu for selections that are collapsed (zero chars selected)
      if (!isCollapsed) {
      
        // Use your own logic for rendering and positioning the attached widget(s)
        _displayQuickMenuOnTextSelection(markers);
      }
    },
  ),
);

TextSelectionControls (WIP)

  • Selection Handles - On mobile the copy paste actions are executed via the context menu. These are controlled by the SelectionHandlesController. This controller is also in charge of rendering the options menu overlaid on top of the text.
  • Flutter Custom Selection - Flutter custom selection toolbar