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); _format(style);
} else { } else {
// Otherwise forward to children as it's an inline format update. // 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); assert(index + local != thisLength);
super.retain(index, local, style); 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. /// except newlines.
class ResolveInlineFormatRule extends FormatRule { class ResolveInlineFormatRule extends FormatRule {
const ResolveInlineFormatRule(); const ResolveInlineFormatRule();
@ -176,3 +176,24 @@ class ResolveInlineFormatRule extends FormatRule {
return delta; 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 FormatLinkAtCaretPositionRule(),
const ResolveLineFormatRule(), const ResolveLineFormatRule(),
const ResolveInlineFormatRule(), const ResolveInlineFormatRule(),
const ResolveImageFormatRule(),
const InsertEmbedsRule(), const InsertEmbedsRule(),
const AutoExitBlockRule(), const AutoExitBlockRule(),
const PreserveBlockStyleOnInsertRule(), const PreserveBlockStyleOnInsertRule(),

@ -17,6 +17,32 @@ Map<String, String> parseKeyValuePairs(String s, Set<String> targetKeys) {
return result; 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) { Alignment getAlignment(String? s) {
const _defaultAlignment = Alignment.center; const _defaultAlignment = Alignment.center;
if (s == null) { if (s == null) {

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

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