From c5a40382a9428fa63dadc4870896a74eb0139fd2 Mon Sep 17 00:00:00 2001 From: Takashi Kawasaki Date: Fri, 30 Aug 2024 15:32:28 +0900 Subject: [PATCH] Reduce frequent onTextSelectionChange calls --- lib/src/pdf_api.dart | 2 +- lib/src/widgets/pdf_page_text_overlay.dart | 13 ++++++++-- lib/src/widgets/pdf_viewer.dart | 29 ++++++++++------------ 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/lib/src/pdf_api.dart b/lib/src/pdf_api.dart index bf5050c..77788ca 100644 --- a/lib/src/pdf_api.dart +++ b/lib/src/pdf_api.dart @@ -625,7 +625,7 @@ class PdfTextRanges { bool get isEmpty => ranges.isEmpty; /// Determine whether the text ranges are *NOT* empty. - bool get isNotEmpty => !isEmpty; + bool get isNotEmpty => ranges.isNotEmpty; /// Page number of the text ranges. int get pageNumber => pageText.pageNumber; diff --git a/lib/src/widgets/pdf_page_text_overlay.dart b/lib/src/widgets/pdf_page_text_overlay.dart index cee2baa..64e0505 100644 --- a/lib/src/widgets/pdf_page_text_overlay.dart +++ b/lib/src/widgets/pdf_page_text_overlay.dart @@ -1,3 +1,5 @@ +import 'dart:collection'; + import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; @@ -28,7 +30,7 @@ class PdfPageTextOverlay extends StatefulWidget { super.key, }); - final Map selectables; + final SplayTreeMap selectables; final bool enabled; final PdfPage page; final Rect pageRect; @@ -197,8 +199,12 @@ class _PdfTextWidget extends LeafRenderObjectWidget { } } +mixin PdfPageTextSelectable implements Selectable { + PdfTextRanges get selectedRanges; +} + /// The code is based on the code on [Making a widget selectable](https://api.flutter.dev/flutter/widgets/SelectableRegion-class.html#widgets).SelectableRegion.2] -class _PdfTextRenderBox extends RenderBox with Selectable, SelectionRegistrant { +class _PdfTextRenderBox extends RenderBox with PdfPageTextSelectable, Selectable, SelectionRegistrant { _PdfTextRenderBox( this._selectionColor, this._textWidget, @@ -277,6 +283,9 @@ class _PdfTextRenderBox extends RenderBox with Selectable, SelectionRegistrant { late PdfTextRanges _selectedRanges = PdfTextRanges.createEmpty(_textWidget._state._pageText!); + @override + PdfTextRanges get selectedRanges => _selectedRanges; + void _notifySelectionChange() { _textWidget._state._notifySelectionChange(_selectedRanges); } diff --git a/lib/src/widgets/pdf_viewer.dart b/lib/src/widgets/pdf_viewer.dart index fdb462d..0e7ab54 100644 --- a/lib/src/widgets/pdf_viewer.dart +++ b/lib/src/widgets/pdf_viewer.dart @@ -228,10 +228,11 @@ class _PdfViewerState extends State // Changes to the stream rebuilds the viewer final _updateStream = BehaviorSubject(); - final _selectionHandlers = {}; + final _selectionHandlers = SplayTreeMap(); + Timer? _selectionChangedThrottleTimer; - final _textSelections = SplayTreeSet( - (a, b) => a.pageNumber.compareTo(b.pageNumber)); + Timer? _interactionEndedTimer; + bool _isInteractionGoingOn = false; @override void initState() { @@ -306,6 +307,7 @@ class _PdfViewerState extends State void _onDocumentChanged() async { _layout = null; + _selectionChangedThrottleTimer?.cancel(); _stopInteraction(); _releaseAllImages(); _canvasLinkPainter.resetAll(); @@ -347,6 +349,7 @@ class _PdfViewerState extends State @override void dispose() { + _selectionChangedThrottleTimer?.cancel(); _stopInteraction(); _cancelAllPendingRenderings(); _animController.dispose(); @@ -484,9 +487,6 @@ class _PdfViewerState extends State }); } - Timer? _interactionEndedTimer; - bool _isInteractionGoingOn = false; - void _startInteraction() { _interactionEndedTimer?.cancel(); _interactionEndedTimer = null; @@ -866,16 +866,13 @@ class _PdfViewerState extends State } void _onSelectionChange(PdfTextRanges selection) { - if (selection.isEmpty) { - if (!_textSelections.contains(selection)) return; - _textSelections.remove(selection); - } else { - if (_textSelections.contains(selection)) { - _textSelections.remove(selection); - } - _textSelections.add(selection); - } - widget.params.onTextSelectionChange?.call(_textSelections.toList()); + _selectionChangedThrottleTimer?.cancel(); + _selectionChangedThrottleTimer = Timer(const Duration(milliseconds: 300), () { + if (!mounted || !_selectionHandlers.containsKey(selection.pageNumber)) return; + widget.params.onTextSelectionChange?.call( + _selectionHandlers.values.map((s) => s.selectedRanges).where((s) => s.isNotEmpty).toList() + ); + }); } Rect _getCacheExtentRect() {