fix: sync lyric romanization and clear stale selection menus

Preserve romanization and translation metadata through Rust exports and cache, match rounded line times, and retain word endings. Highlight romanization with the main lyric clock in Material and Mornye.

Clear selection overlay ownership safely when collection screens are disposed. Add parser, rendering, cache and overlay lifecycle regression coverage.
This commit is contained in:
zarzet
2026-09-23 12:42:08 +07:00
parent ebcd1fd877
commit ba44bafa92
14 changed files with 1286 additions and 67 deletions
+20 -4
View File
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/scheduler.dart';
import 'package:spotiflac_android/theme/app_tokens.dart';
import 'package:spotiflac_android/theme/mornye_theme.dart';
import 'package:spotiflac_android/widgets/mornye_chrome.dart';
@@ -29,21 +30,36 @@ class _SelectionOverlayHostState extends State<SelectionOverlayHost> {
_owner == owner && _builder != null;
void show(SelectionOverlayController owner, WidgetBuilder builder) {
if (!mounted) return;
setState(() {
_update(() {
_owner = owner;
_builder = builder;
});
}
void hide(SelectionOverlayController owner) {
if (!mounted || _owner != owner) return;
setState(() {
if (_owner != owner) return;
_update(() {
_owner = null;
_builder = null;
});
}
/// A screen can remove its bar from dispose while Flutter has locked the
/// tree. Clear ownership immediately, then rebuild after that frame so the
/// bar cannot outlive the screen or retain its disposed callbacks.
void _update(VoidCallback change) {
if (!mounted) return;
if (SchedulerBinding.instance.schedulerPhase !=
SchedulerPhase.persistentCallbacks) {
setState(change);
return;
}
change();
SchedulerBinding.instance.addPostFrameCallback((_) {
if (mounted) setState(() {});
});
}
@override
Widget build(BuildContext context) {
final owner = _owner;