list button implementation

This commit is contained in:
Filip Krawczyk 2022-07-06 11:28:06 +02:00
parent 062a53fdd9
commit 3c295552df
3 changed files with 163 additions and 7 deletions

View File

@ -1,16 +1,25 @@
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
extension on String { extension Utilities on String {
int getBeginningOfPreviousLine(int startingIndex) { int getBeginningOfTheLine(int startingIndex) {
if (startingIndex <= 0) return 0;
for (var i = startingIndex; i >= 0; i--) { for (var i = startingIndex; i >= 0; i--) {
if (this[i] == '\n') return i + 1; if (this[i] == '\n') return i + 1;
} }
return 0; return 0;
} }
int getEndOfTheLine(int from) {
for (var i = from; i < length; i++) {
if (this[i] == '\n') return i;
}
return length - 1;
}
// returns the line that ends at endingIndex // returns the line that ends at endingIndex
String lineBefore(int endingIndex) { String lineBefore(int endingIndex) {
return substring(getBeginningOfPreviousLine(endingIndex), endingIndex + 1); return substring(getBeginningOfTheLine(endingIndex), endingIndex + 1);
} }
} }

View File

@ -0,0 +1,89 @@
import 'package:flutter/widgets.dart';
/// utililty class for traversing through multiline text
class TextLinesIterator extends Iterator {
String text;
int beg;
int end;
TextSelection? selection;
TextLinesIterator(this.text, {this.selection})
: end = -1,
beg = -1;
factory TextLinesIterator.fromController(TextEditingController controller) =>
TextLinesIterator(controller.text, selection: controller.selection);
bool get isWithinSelection {
final selection = this.selection;
if (selection == null || beg == -1) {
return false;
} else {
return (selection.end >= beg && beg >= selection.start) ||
(selection.end >= end && end >= selection.start) ||
(end >= selection.start && selection.start >= beg) ||
(end >= selection.end && selection.end >= beg) ||
(beg <= selection.start &&
selection.start <= end &&
beg <= selection.end &&
selection.end <= end);
}
}
@override
String get current {
return text.substring(beg, end);
}
set current(String newVal) {
final selected = isWithinSelection;
text = text.replaceRange(beg, end, newVal);
final wordLen = end - beg;
final dif = newVal.length - wordLen;
end += dif;
final selection = this.selection;
if (selection == null) return;
if (selected || selection.baseOffset > end) {
this.selection =
selection.copyWith(extentOffset: selection.extentOffset + dif);
}
}
void reset() {
end = -1;
beg = -1;
}
@override
bool moveNext() {
if (end == text.length) {
return false;
}
if (beg == -1) {
end = 0;
beg = 0;
} else {
end += 1;
beg = end;
}
for (; end < text.length; end++) {
if (text[end] == '\n') {
return true;
}
}
end = text.length;
return true;
}
/// returns the lines as a list but also moves the pointer to the back
List<String> get asList {
reset();
final list = <String>[];
while (moveNext()) {
list.add(current);
}
return list;
}
}

View File

@ -1,7 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import '../../formatter.dart';
import '../../util/extensions/spaced.dart'; import '../../util/extensions/spaced.dart';
import '../../util/text_lines_iterator.dart';
class Reformat { class Reformat {
final String text; final String text;
@ -43,6 +45,54 @@ extension on TextEditingController {
)); ));
} }
String get firstSelectedLine {
if (text.isEmpty) return '';
return text.substring(text.getBeginningOfTheLine(selection.start - 1),
text.getEndOfTheLine(selection.end));
}
void removeAtBeginningOfEverySelectedLine(String s) {
final lines = TextLinesIterator.fromController(this);
var linesCount = 0;
while (lines.moveNext()) {
if (lines.isWithinSelection) {
if (lines.current.startsWith(RegExp.escape(s))) {
lines.current = lines.current.substring(s.length);
linesCount++;
}
}
}
value = value.copyWith(
text: lines.text,
selection: selection.copyWith(
baseOffset: selection.baseOffset - s.length,
extentOffset: selection.extentOffset - s.length * linesCount,
),
);
}
void insertAtBeginningOfEverySelectedLine(String s) {
final lines = TextLinesIterator.fromController(this);
var linesCount = 0;
while (lines.moveNext()) {
if (lines.isWithinSelection) {
if (!lines.current.startsWith(RegExp.escape(s))) {
lines.current = '$s${lines.current}';
linesCount++;
}
}
}
value = value.copyWith(
text: lines.text,
selection: selection.copyWith(
baseOffset: selection.baseOffset + s.length,
extentOffset: selection.extentOffset + s.length * linesCount,
),
);
}
void reformat(Reformat Function(String selection) reformatter) { void reformat(Reformat Function(String selection) reformatter) {
final beg = beforeSelectionText; final beg = beforeSelectionText;
final mid = selectionText; final mid = selectionText;
@ -101,9 +151,7 @@ class Toolbar extends HookWidget {
icon: const Icon(Icons.person), icon: const Icon(Icons.person),
), ),
IconButton( IconButton(
onPressed: () { onPressed: () {},
//
},
icon: const Icon(Icons.home), icon: const Icon(Icons.home),
), ),
IconButton( IconButton(
@ -119,7 +167,17 @@ class Toolbar extends HookWidget {
icon: const Icon(Icons.format_quote), icon: const Icon(Icons.format_quote),
), ),
IconButton( IconButton(
onPressed: () {}, onPressed: () {
final line = controller.firstSelectedLine;
if (line.startsWith(RegExp.escape('* '))) {
controller.removeAtBeginningOfEverySelectedLine('* ');
} else if (line.startsWith('- ')) {
controller.removeAtBeginningOfEverySelectedLine('- ');
} else {
controller.insertAtBeginningOfEverySelectedLine('- ');
}
},
icon: const Icon(Icons.format_list_bulleted)), icon: const Icon(Icons.format_list_bulleted)),
IconButton( IconButton(
onPressed: () => controller.surround('`'), onPressed: () => controller.surround('`'),