Files
flutter_lua_runtime/lib/runtime/commands/command_toast.dart
2026-06-07 22:53:58 +08:00

97 lines
2.6 KiB
Dart

part of 'command_executor.dart';
extension _CommandExecutorToast on CommandExecutor {
Future<_CommandResult> _toast(
RuntimeCommand command,
_CommandContext context,
RuntimeCommandHandle? handle,
) {
final text = _toastText(command);
final duration = _duration(command, defaultValue: 1.8);
final scope = _scopeFor(command, context, defaultTarget: false);
final scopeEpoch = _scopeEpochFor(scope, context);
final task = _registerTask(scope, handle);
final toastId = 'runtime_toast_${++_toastSerial}';
final toastTextId = '${toastId}_text';
_renderTree.apply(
NodeDiff(
creates: _toastNodes(id: toastId, textId: toastTextId, text: text),
),
);
task.addCancelCallback(() => _renderTree.removeById(toastId));
_schedule(duration, task, () {
if ((handle?.isCancelled ?? false) || !_scopeIsAlive(scope)) {
_renderTree.removeById(toastId);
task.cancel();
return;
}
_renderTree.removeById(toastId);
_emitCommandCompletion(
command,
context.copyWith(scope: scope, scopeEpoch: scopeEpoch),
);
task.complete(_CommandResult.completed);
});
return task.future;
}
List<RuntimeNode> _toastNodes({
required String id,
required String textId,
required String text,
}) {
const width = 360.0;
const minHeight = 38.0;
final lineCount = text.split('\n').length;
final height = (minHeight + (lineCount - 1) * 16).clamp(38.0, 92.0);
final x = ((_overlaySize.x - width) / 2).clamp(12.0, _overlaySize.x);
final y = (_overlaySize.y - height - 58).clamp(12.0, _overlaySize.y);
return [
RuntimeNode(
id: id,
type: RuntimeNodeType.panel,
x: x,
y: y,
width: width,
height: height,
color: const Color(0xee020617),
radius: 12,
layer: 10000,
),
RuntimeNode(
id: textId,
type: RuntimeNodeType.text,
parent: id,
text: text,
x: 14,
y: 0,
width: width - 28,
height: height,
color: const Color(0xfff8fafc),
fontSize: 13,
textAlign: RuntimeTextAlignValue.center,
layer: 10001,
),
];
}
String _toastText(RuntimeCommand command) {
final text = _optionalString(command.payload['text'], 'toast.text');
final message = _optionalString(
command.payload['message'],
'toast.message',
);
if (text != null) {
return text;
}
if (message != null) {
return message;
}
throw const FormatException('toast.text or toast.message is required');
}
}