Skip to content

Commit dbbb0b8

Browse files
committed
feat(editor): allow auto deciding single side length
This commit added permissive image size auto-deciding process. Allow single size to be `0` and auto decided when rendering.
1 parent be577e8 commit dbbb0b8

File tree

6 files changed

+142
-47
lines changed

6 files changed

+142
-47
lines changed

lib/features/editor/widgets/image_dialog.dart

Lines changed: 110 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import 'package:tsdm_client/shared/providers/image_cache_provider/image_cache_pr
1414
import 'package:tsdm_client/shared/providers/image_cache_provider/models/models.dart';
1515
import 'package:tsdm_client/utils/logger.dart';
1616
import 'package:tsdm_client/widgets/section_switch_list_tile.dart';
17+
import 'package:tsdm_client/widgets/tips.dart';
1718

1819
/// Show a picture dialog to add picture into editor.
1920
Future<BBCodeImageInfo?> showImagePicker(BuildContext context, {String? url, int? width, int? height}) async =>
@@ -72,6 +73,20 @@ class _ImageDialogState extends State<_ImageDialog> with LoggerMixin, SingleTick
7273
/// So fill with original image size is fine.
7374
bool autoFillSize = true;
7475

76+
/// Automatically determine the width.
77+
///
78+
/// Fill a zero value in bbcode.
79+
///
80+
/// This can not be true when [autoScaleHeight] is true.
81+
bool autoScaleWidth = false;
82+
83+
/// Automatically determine the height.
84+
///
85+
/// Fill a zero value in bbcode.
86+
///
87+
/// This can not be true when [autoScaleWidth] is true.
88+
bool autoScaleHeight = false;
89+
7590
/// Flag indicating in auto-fill-size progress.
7691
bool fillingSize = false;
7792

@@ -284,41 +299,94 @@ class _ImageDialogState extends State<_ImageDialog> with LoggerMixin, SingleTick
284299
// _buildSmmsField(context),
285300
],
286301
),
287-
TextFormField(
288-
controller: widthController,
289-
keyboardType: TextInputType.number,
290-
inputFormatters: [FilteringTextInputFormatter.allow(RegExp('[0-9]+'))],
291-
decoration: InputDecoration(
292-
prefixIcon: const Icon(Icons.horizontal_distribute_outlined),
293-
labelText: tr.width,
294-
),
295-
validator: (v) {
296-
if (v == null || v.trim().isEmpty) {
297-
return tr.errorEmpty;
298-
}
299-
final vv = double.tryParse(v);
300-
if (vv == null || vv <= 0) {
301-
return tr.errorInvalidNumber;
302-
}
303-
return null;
304-
},
302+
Row(
303+
children: [
304+
Expanded(
305+
child: TextFormField(
306+
controller: widthController,
307+
enabled: !autoScaleWidth,
308+
keyboardType: TextInputType.number,
309+
inputFormatters: [FilteringTextInputFormatter.allow(RegExp('[0-9]+'))],
310+
decoration: InputDecoration(
311+
prefixIcon: const Icon(Icons.horizontal_distribute_outlined),
312+
labelText: tr.width,
313+
),
314+
validator: (v) {
315+
if (autoScaleWidth) {
316+
return null;
317+
}
318+
if (v == null || v.trim().isEmpty) {
319+
return tr.errorEmpty;
320+
}
321+
final vv = double.tryParse(v);
322+
if (vv == null || vv <= 0) {
323+
return tr.errorInvalidNumber;
324+
}
325+
return null;
326+
},
327+
),
328+
),
329+
Checkbox(
330+
value: autoScaleWidth,
331+
onChanged: (v) {
332+
if (v == null) {
333+
return;
334+
}
335+
336+
setState(() {
337+
if (autoScaleHeight) {
338+
autoScaleHeight = false;
339+
}
340+
autoScaleWidth = v;
341+
});
342+
},
343+
),
344+
Text(tr.auto),
345+
],
305346
),
306-
TextFormField(
307-
controller: heightController,
308-
keyboardType: TextInputType.number,
309-
inputFormatters: [FilteringTextInputFormatter.allow(RegExp('[0-9]+'))],
310-
decoration: InputDecoration(prefixIcon: const Icon(Icons.add), labelText: tr.height),
311-
validator: (v) {
312-
if (v == null || v.trim().isEmpty) {
313-
return tr.errorEmpty;
314-
}
315-
final vv = double.tryParse(v);
316-
if (vv == null || vv <= 0) {
317-
return tr.errorInvalidNumber;
318-
}
319-
return null;
320-
},
347+
Row(
348+
children: [
349+
Expanded(
350+
child: TextFormField(
351+
controller: heightController,
352+
enabled: !autoScaleHeight,
353+
keyboardType: TextInputType.number,
354+
inputFormatters: [FilteringTextInputFormatter.allow(RegExp('[0-9]+'))],
355+
decoration: InputDecoration(prefixIcon: const Icon(Icons.add), labelText: tr.height),
356+
validator: (v) {
357+
if (autoScaleHeight) {
358+
return null;
359+
}
360+
if (v == null || v.trim().isEmpty) {
361+
return tr.errorEmpty;
362+
}
363+
final vv = double.tryParse(v);
364+
if (vv == null || vv <= 0) {
365+
return tr.errorInvalidNumber;
366+
}
367+
return null;
368+
},
369+
),
370+
),
371+
Checkbox(
372+
value: autoScaleHeight,
373+
onChanged: (v) {
374+
if (v == null) {
375+
return;
376+
}
377+
378+
setState(() {
379+
if (autoScaleWidth) {
380+
autoScaleWidth = false;
381+
}
382+
autoScaleHeight = v;
383+
});
384+
},
385+
),
386+
Text(tr.auto),
387+
],
321388
),
389+
Tips(tr.autoSingleDirectionSize),
322390
SectionSwitchListTile(
323391
title: Text(tr.autoFillSize),
324392
subtitle: Text(tr.autoFillSizeDetail),
@@ -342,12 +410,16 @@ class _ImageDialogState extends State<_ImageDialog> with LoggerMixin, SingleTick
342410
return;
343411
}
344412

345-
final width = int.parse(widthController.text);
346-
final height = int.parse(heightController.text);
347-
assert(width != 0, 'image width should >= zero');
348-
assert(height != 0, 'image height should >= zero');
413+
final width = int.tryParse(widthController.text);
414+
final height = int.tryParse(heightController.text);
349415

350-
context.pop(BBCodeImageInfo(urlController.text, width: width, height: height));
416+
context.pop(
417+
BBCodeImageInfo(
418+
urlController.text,
419+
width: autoScaleWidth ? 0 : width,
420+
height: autoScaleHeight ? 0 : height,
421+
),
422+
);
351423
},
352424
),
353425
],

lib/features/editor/widgets/rich_editor.dart

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,35 @@ class RichEditor extends StatelessWidget {
6060
autoFocus: autoFocus,
6161
scrollController: scrollController,
6262
imageProvider: (context, url, width, height) {
63+
// Requirements:
64+
//
65+
// 1. If width is not larger than max width, keep the original width and height.
66+
// 2. If width is larger than max width, set width to max width and scale height down to the same ratio.
67+
// 3. Width and height can not be 0 at the same time.
68+
6369
final w = width?.toDouble();
6470
final h = height?.toDouble();
6571
double? maxHeight;
66-
if (w != null && h != null && w > _imageMaxWidth) {
67-
maxHeight = h * (_imageMaxWidth / w);
72+
if (w != null && h != null) {
73+
if (w == 0) {
74+
// Auto width, do not limit max height.
75+
maxHeight = h;
76+
} else if (w > _imageMaxWidth && h != 0) {
77+
// Width too large, it will be set to max allowed width, scale down the height.
78+
maxHeight = h * (_imageMaxWidth / w);
79+
} else if (h == 0) {
80+
// Auto height.
81+
maxHeight = double.infinity;
82+
} else {
83+
// Normal height.
84+
maxHeight = h;
85+
}
6886
}
87+
6988
return CachedImage(
7089
url,
71-
width: w,
72-
height: maxHeight == null ? h : null,
90+
width: (w == null || w <= 0) ? null : w,
91+
height: maxHeight == null ? maxHeight : null,
7392
maxWidth: _imageMaxWidth,
7493
maxHeight: maxHeight,
7594
);

lib/features/notification/view/notification_page.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,11 @@ import 'dart:math' as math;
33
import 'package:easy_refresh/easy_refresh.dart';
44
import 'package:flutter/material.dart';
55
import 'package:flutter_bloc/flutter_bloc.dart';
6-
import 'package:go_router/go_router.dart';
76
import 'package:tsdm_client/constants/layout.dart';
87
import 'package:tsdm_client/features/notification/bloc/auto_notification_cubit.dart';
98
import 'package:tsdm_client/features/notification/bloc/notification_bloc.dart';
109
import 'package:tsdm_client/features/notification/bloc/notification_state_cubit.dart';
1110
import 'package:tsdm_client/i18n/strings.g.dart';
12-
import 'package:tsdm_client/routes/screen_paths.dart';
1311
import 'package:tsdm_client/shared/models/notification_type.dart';
1412
import 'package:tsdm_client/utils/logger.dart';
1513
import 'package:tsdm_client/utils/retry_button.dart';

lib/i18n/en.i18n.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,9 @@
704704
"height": "Height",
705705
"errorInvalidNumber": "Invalid number",
706706
"autoFillSize": "Auto fill size",
707-
"autoFillSizeDetail": "Fill size with image real size"
707+
"autoFillSizeDetail": "Fill size with image real size",
708+
"auto": "Auto",
709+
"autoSingleDirectionSize": "If would like a auto decided width or height according to image size, check boxes right to width or height above."
708710
},
709711
"userMention": {
710712
"title": "Mention Someone",

lib/i18n/zh-CN.i18n.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,9 @@
704704
"height": "高度",
705705
"errorInvalidNumber": "无效的数字",
706706
"autoFillSize": "自动填写大小",
707-
"autoFillSizeDetail": "填写图片的实际大小"
707+
"autoFillSizeDetail": "填写图片的实际大小",
708+
"auto": "自动",
709+
"autoSingleDirectionSize": "如果想根据图片大小比例自动设置图片的长或宽,勾选长或宽右边的方框"
708710
},
709711
"userMention": {
710712
"title": "提醒用户(@)",

lib/i18n/zh-TW.i18n.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,9 @@
704704
"height": "高度",
705705
"errorInvalidNumber": "無效的數字",
706706
"autoFillSize": "自動填入大小",
707-
"autoFillSizeDetail": "填寫圖片的實際大小"
707+
"autoFillSizeDetail": "填寫圖片的實際大小",
708+
"auto": "自動",
709+
"autoSingleDirectionSize": "如果想根據圖片大小比例自動設定圖片的長或寬,勾選長或寬右邊的方框"
708710
},
709711
"userMention": {
710712
"title": "提醒使用者(@)",

0 commit comments

Comments
 (0)