Support resizing image

pull/635/head
X Code 3 years ago
parent 4f36393aea
commit a4b6641b57
  1. 4
      lib/src/models/documents/nodes/line.dart
  2. 23
      lib/src/models/rules/format.dart
  3. 1
      lib/src/models/rules/rule.dart
  4. 26
      lib/src/utils/string.dart
  5. 24
      lib/src/widgets/embeds/default_embed_builder.dart
  6. 28
      lib/src/widgets/embeds/image_resizer.dart

@ -134,7 +134,9 @@ class Line extends Container<Leaf?> {
_format(style);
} else {
// Otherwise forward to children as it's an inline format update.
assert(style.values.every((attr) => attr.scope == AttributeScope.INLINE));
assert(style.values.every((attr) =>
attr.scope == AttributeScope.INLINE ||
attr.scope == AttributeScope.IGNORE));
assert(index + local != thisLength);
super.retain(index, local, style);
}

@ -136,7 +136,7 @@ class FormatLinkAtCaretPositionRule extends FormatRule {
}
}
/// Produces Delta with inline-level attributes applied too all characters
/// Produces Delta with inline-level attributes applied to all characters
/// except newlines.
class ResolveInlineFormatRule extends FormatRule {
const ResolveInlineFormatRule();
@ -176,3 +176,24 @@ class ResolveInlineFormatRule extends FormatRule {
return delta;
}
}
/// Produces Delta with attributes applied to image leaf node
class ResolveImageFormatRule extends FormatRule {
const ResolveImageFormatRule();
@override
Delta? applyRule(Delta document, int index,
{int? len, Object? data, Attribute? attribute}) {
if (attribute == null || attribute.key != Attribute.style.key) {
return null;
}
assert(len == 1 && data == null);
final delta = Delta()
..retain(index)
..retain(1, attribute.toJson());
return delta;
}
}

@ -37,6 +37,7 @@ class Rules {
const FormatLinkAtCaretPositionRule(),
const ResolveLineFormatRule(),
const ResolveInlineFormatRule(),
const ResolveImageFormatRule(),
const InsertEmbedsRule(),
const AutoExitBlockRule(),
const PreserveBlockStyleOnInsertRule(),

@ -17,6 +17,32 @@ Map<String, String> parseKeyValuePairs(String s, Set<String> targetKeys) {
return result;
}
String replaceStyleString(String? s, double width, double height) {
s ??= '';
final result = <String, String>{};
final pairs = s.split(';');
for (final pair in pairs) {
final _index = pair.indexOf(':');
if (_index < 0) {
continue;
}
final _key = pair.substring(0, _index).trim();
result[_key] = pair.substring(_index + 1).trim();
}
result['mobileWidth'] = width.toString();
result['mobileHeight'] = height.toString();
final sb = StringBuffer();
for (final pair in result.entries) {
sb
..write(pair.key)
..write(': ')
..write(pair.value)
..write('; ');
}
return sb.toString();
}
Alignment getAlignment(String? s) {
const _defaultAlignment = Alignment.center;
if (s == null) {

@ -6,7 +6,9 @@ import 'package:flutter/material.dart';
import 'package:gallery_saver/gallery_saver.dart';
import 'package:tuple/tuple.dart';
import '../../models/documents/attribute.dart';
import '../../models/documents/nodes/leaf.dart' as leaf;
import '../../models/documents/style.dart';
import '../../translations/toolbar.i18n.dart';
import '../../utils/platform.dart';
import '../../utils/string.dart';
@ -63,14 +65,28 @@ Widget defaultEmbedBuilder(BuildContext context, QuillController controller,
text: 'Resize'.i18n,
onPressed: () {
Navigator.pop(context);
final res = _getImageNode(controller);
showCupertinoModalPopup<void>(
context: context,
builder: (context) {
final _screenSize = MediaQuery.of(context).size;
final _style = controller
.getAllSelectionStyles()
.firstWhere(
(s) => s.attributes
.containsKey(Attribute.style.key),
orElse: () => Style());
return ImageResizer(
imageNode: res.item2,
offset: res.item1,
onImageResize: (w, h) {
final res = _getImageNode(controller);
final attr = replaceStyleString(
_style.attributes[Attribute.style.key]
?.value,
w,
h);
controller.formatText(
res.item1, 1, StyleAttribute(attr));
},
imageWidth: _widthHeight?.item1,
imageHeight: _widthHeight?.item2,
maxWidth: _screenSize.width,
@ -106,7 +122,7 @@ Widget defaultEmbedBuilder(BuildContext context, QuillController controller,
shape: const RoundedRectangleBorder(
borderRadius:
BorderRadius.all(Radius.circular(10))),
children: [copyOption, removeOption]),
children: [resizeOption, copyOption, removeOption]),
);
});
},

@ -1,7 +1,7 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import '../../models/documents/nodes/leaf.dart';
import '../../translations/toolbar.i18n.dart';
class ImageResizer extends StatefulWidget {
@ -10,8 +10,7 @@ class ImageResizer extends StatefulWidget {
required this.imageHeight,
required this.maxWidth,
required this.maxHeight,
required this.offset,
required this.imageNode,
required this.onImageResize,
Key? key})
: super(key: key);
@ -19,8 +18,7 @@ class ImageResizer extends StatefulWidget {
final double? imageHeight;
final double maxWidth;
final double maxHeight;
final int offset;
final Embed imageNode;
final Function(double, double) onImageResize;
@override
_ImageResizerState createState() => _ImageResizerState();
@ -48,11 +46,12 @@ class _ImageResizerState extends State<ImageResizer> {
child: Slider(
value: _width,
max: widget.maxWidth,
divisions: 100,
divisions: 1000,
label: 'Width'.i18n,
onChanged: (val) {
setState(() {
_width = val;
_resizeImage();
});
},
),
@ -66,11 +65,12 @@ class _ImageResizerState extends State<ImageResizer> {
child: Slider(
value: _height,
max: widget.maxHeight,
divisions: 100,
divisions: 1000,
label: 'Height'.i18n,
onChanged: (val) {
setState(() {
_height = val;
_resizeImage();
});
},
),
@ -78,4 +78,18 @@ class _ImageResizerState extends State<ImageResizer> {
)
]);
}
bool _scheduled = false;
void _resizeImage() {
if (_scheduled) {
return;
}
_scheduled = true;
SchedulerBinding.instance!.addPostFrameCallback((_) {
widget.onImageResize(_width, _height);
_scheduled = false;
});
}
}

Loading…
Cancel
Save