fullscreen: refresh overlay & reset info metadata on metadata change
This commit is contained in:
parent
7aa50e7880
commit
bb23e7a939
6 changed files with 158 additions and 100 deletions
|
@ -196,14 +196,20 @@ class ImageEntry {
|
|||
}
|
||||
}
|
||||
|
||||
bool get portrait => rotationDegrees % 180 == 90;
|
||||
bool get isPortrait => rotationDegrees % 180 == 90;
|
||||
|
||||
String get resolutionText {
|
||||
final w = width ?? '?';
|
||||
final h = height ?? '?';
|
||||
return isPortrait ? '$h × $w' : '$w × $h';
|
||||
}
|
||||
|
||||
double get displayAspectRatio {
|
||||
if (width == 0 || height == 0) return 1;
|
||||
return portrait ? height / width : width / height;
|
||||
return isPortrait ? height / width : width / height;
|
||||
}
|
||||
|
||||
Size get displaySize => portrait ? Size(height.toDouble(), width.toDouble()) : Size(width.toDouble(), height.toDouble());
|
||||
Size get displaySize => isPortrait ? Size(height.toDouble(), width.toDouble()) : Size(width.toDouble(), height.toDouble());
|
||||
|
||||
int get megaPixels => width != null && height != null ? (width * height / 1000000).round() : null;
|
||||
|
||||
|
@ -371,7 +377,10 @@ class ImageEntry {
|
|||
final contentId = newFields['contentId'];
|
||||
if (contentId is int) this.contentId = contentId;
|
||||
final sourceTitle = newFields['title'];
|
||||
if (sourceTitle is String) this.sourceTitle = sourceTitle;
|
||||
if (sourceTitle is String) {
|
||||
this.sourceTitle = sourceTitle;
|
||||
_bestTitle = null;
|
||||
}
|
||||
final dateModifiedSecs = newFields['dateModifiedSecs'];
|
||||
if (dateModifiedSecs is int) this.dateModifiedSecs = dateModifiedSecs;
|
||||
final rotationDegrees = newFields['rotationDegrees'];
|
||||
|
@ -381,6 +390,8 @@ class ImageEntry {
|
|||
|
||||
await metadataDb.saveEntries({this});
|
||||
await metadataDb.saveMetadata({catalogMetadata});
|
||||
|
||||
metadataChangeNotifier.notifyListeners();
|
||||
}
|
||||
|
||||
Future<bool> rename(String newName) async {
|
||||
|
@ -390,8 +401,6 @@ class ImageEntry {
|
|||
if (newFields.isEmpty) return false;
|
||||
|
||||
await _applyNewFields(newFields);
|
||||
_bestTitle = null;
|
||||
metadataChangeNotifier.notifyListeners();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ class _ThumbnailRasterImageState extends State<ThumbnailRasterImage> {
|
|||
}
|
||||
|
||||
void _unregisterWidget(ThumbnailRasterImage widget) {
|
||||
widget.entry.imageChangeNotifier?.removeListener(_onImageChanged);
|
||||
widget.entry.imageChangeNotifier.removeListener(_onImageChanged);
|
||||
_pauseProvider();
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ class FullscreenDebugPage extends StatelessWidget {
|
|||
'sourceRotationDegrees': '${entry.sourceRotationDegrees}',
|
||||
'rotationDegrees': '${entry.rotationDegrees}',
|
||||
'isFlipped': '${entry.isFlipped}',
|
||||
'portrait': '${entry.portrait}',
|
||||
'portrait': '${entry.isPortrait}',
|
||||
'displayAspectRatio': '${entry.displayAspectRatio}',
|
||||
'displaySize': '${entry.displaySize}',
|
||||
}),
|
||||
|
|
|
@ -31,7 +31,7 @@ class BasicSection extends StatelessWidget {
|
|||
final date = entry.bestDate;
|
||||
final dateText = date != null ? '${DateFormat.yMMMd().format(date)} • ${DateFormat.Hm().format(date)}' : Constants.unknown;
|
||||
final showMegaPixels = entry.isPhoto && entry.megaPixels != null && entry.megaPixels > 0;
|
||||
final resolutionText = '${entry.width ?? '?'} × ${entry.height ?? '?'}${showMegaPixels ? ' (${entry.megaPixels} MP)' : ''}';
|
||||
final resolutionText = '${entry.resolutionText}${showMegaPixels ? ' (${entry.megaPixels} MP)' : ''}';
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
|
|
@ -36,48 +36,13 @@ class InfoPage extends StatefulWidget {
|
|||
class InfoPageState extends State<InfoPage> {
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
bool _scrollStartFromTop = false;
|
||||
List<MetadataDirectory> _metadata = [];
|
||||
String _loadedMetadataUri;
|
||||
|
||||
CollectionLens get collection => widget.collection;
|
||||
|
||||
ImageEntry get entry => widget.entryNotifier.value;
|
||||
|
||||
bool get isVisible => widget.visibleNotifier.value;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_registerWidget(widget);
|
||||
_getMetadata();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(InfoPage oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
_unregisterWidget(oldWidget);
|
||||
_registerWidget(widget);
|
||||
_getMetadata();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_unregisterWidget(widget);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _registerWidget(InfoPage widget) {
|
||||
widget.visibleNotifier.addListener(_getMetadata);
|
||||
}
|
||||
|
||||
void _unregisterWidget(InfoPage widget) {
|
||||
widget.visibleNotifier.removeListener(_getMetadata);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const horizontalPadding = EdgeInsets.symmetric(horizontal: 8);
|
||||
|
||||
final appBar = SliverAppBar(
|
||||
leading: IconButton(
|
||||
key: Key('back-button'),
|
||||
|
@ -99,64 +64,20 @@ class InfoPageState extends State<InfoPage> {
|
|||
builder: (c, mq, child) {
|
||||
final mqWidth = mq.item1;
|
||||
final mqViewInsetsBottom = mq.item2;
|
||||
final split = mqWidth > 400;
|
||||
|
||||
return ValueListenableBuilder<ImageEntry>(
|
||||
valueListenable: widget.entryNotifier,
|
||||
builder: (context, entry, child) {
|
||||
if (entry == null) return SizedBox.shrink();
|
||||
|
||||
final locationAtTop = split && entry.hasGps;
|
||||
final locationSection = LocationSection(
|
||||
return entry != null
|
||||
? _InfoPageContent(
|
||||
collection: collection,
|
||||
entry: entry,
|
||||
showTitle: !locationAtTop,
|
||||
visibleNotifier: widget.visibleNotifier,
|
||||
onFilter: _goToCollection,
|
||||
);
|
||||
final basicAndLocationSliver = locationAtTop
|
||||
? SliverToBoxAdapter(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(child: BasicSection(entry: entry, collection: collection, onFilter: _goToCollection)),
|
||||
SizedBox(width: 8),
|
||||
Expanded(child: locationSection),
|
||||
],
|
||||
),
|
||||
scrollController: _scrollController,
|
||||
appBar: appBar,
|
||||
split: mqWidth > 400,
|
||||
mqViewInsetsBottom: mqViewInsetsBottom,
|
||||
)
|
||||
: SliverList(
|
||||
delegate: SliverChildListDelegate.fixed(
|
||||
[
|
||||
BasicSection(entry: entry, collection: collection, onFilter: _goToCollection),
|
||||
locationSection,
|
||||
],
|
||||
),
|
||||
);
|
||||
final metadataSliver = MetadataSectionSliver(
|
||||
entry: entry,
|
||||
metadata: _metadata,
|
||||
);
|
||||
|
||||
return AnimationLimiter(
|
||||
// we update the limiter key after fetching the metadata of a new entry,
|
||||
// in order to restart the staggered animation of the metadata section
|
||||
key: Key(_loadedMetadataUri),
|
||||
child: CustomScrollView(
|
||||
controller: _scrollController,
|
||||
slivers: [
|
||||
appBar,
|
||||
SliverPadding(
|
||||
padding: horizontalPadding + EdgeInsets.only(top: 8),
|
||||
sliver: basicAndLocationSliver,
|
||||
),
|
||||
SliverPadding(
|
||||
padding: horizontalPadding + EdgeInsets.only(bottom: 8 + mqViewInsetsBottom),
|
||||
sliver: metadataSliver,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
: SizedBox.shrink();
|
||||
},
|
||||
);
|
||||
},
|
||||
|
@ -198,12 +119,141 @@ class InfoPageState extends State<InfoPage> {
|
|||
curve: Curves.easeInOut,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _InfoPageContent extends StatefulWidget {
|
||||
final CollectionLens collection;
|
||||
final ImageEntry entry;
|
||||
final ValueNotifier<bool> visibleNotifier;
|
||||
final ScrollController scrollController;
|
||||
final SliverAppBar appBar;
|
||||
final bool split;
|
||||
final double mqViewInsetsBottom;
|
||||
|
||||
const _InfoPageContent({
|
||||
Key key,
|
||||
@required this.collection,
|
||||
@required this.entry,
|
||||
@required this.visibleNotifier,
|
||||
@required this.scrollController,
|
||||
@required this.appBar,
|
||||
@required this.split,
|
||||
@required this.mqViewInsetsBottom,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_InfoPageContentState createState() => _InfoPageContentState();
|
||||
}
|
||||
|
||||
class _InfoPageContentState extends State<_InfoPageContent> {
|
||||
List<MetadataDirectory> _metadata = [];
|
||||
String _loadedMetadataUri;
|
||||
|
||||
static const horizontalPadding = EdgeInsets.symmetric(horizontal: 8);
|
||||
|
||||
CollectionLens get collection => widget.collection;
|
||||
|
||||
ImageEntry get entry => widget.entry;
|
||||
|
||||
bool get isVisible => widget.visibleNotifier.value;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_registerWidget(widget);
|
||||
_getMetadata();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(_InfoPageContent oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
_unregisterWidget(oldWidget);
|
||||
_registerWidget(widget);
|
||||
_getMetadata();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_unregisterWidget(widget);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _registerWidget(_InfoPageContent widget) {
|
||||
widget.visibleNotifier.addListener(_getMetadata);
|
||||
widget.entry.metadataChangeNotifier.addListener(_onMetadataChanged);
|
||||
}
|
||||
|
||||
void _unregisterWidget(_InfoPageContent widget) {
|
||||
widget.visibleNotifier.removeListener(_getMetadata);
|
||||
widget.entry.metadataChangeNotifier.removeListener(_onMetadataChanged);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final locationAtTop = widget.split && entry.hasGps;
|
||||
final locationSection = LocationSection(
|
||||
collection: collection,
|
||||
entry: entry,
|
||||
showTitle: !locationAtTop,
|
||||
visibleNotifier: widget.visibleNotifier,
|
||||
onFilter: _goToCollection,
|
||||
);
|
||||
final basicAndLocationSliver = locationAtTop
|
||||
? SliverToBoxAdapter(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(child: BasicSection(entry: entry, collection: collection, onFilter: _goToCollection)),
|
||||
SizedBox(width: 8),
|
||||
Expanded(child: locationSection),
|
||||
],
|
||||
),
|
||||
)
|
||||
: SliverList(
|
||||
delegate: SliverChildListDelegate.fixed(
|
||||
[
|
||||
BasicSection(entry: entry, collection: collection, onFilter: _goToCollection),
|
||||
locationSection,
|
||||
],
|
||||
),
|
||||
);
|
||||
final metadataSliver = MetadataSectionSliver(
|
||||
entry: entry,
|
||||
metadata: _metadata,
|
||||
);
|
||||
|
||||
return AnimationLimiter(
|
||||
// we update the limiter key after fetching the metadata of a new entry,
|
||||
// in order to restart the staggered animation of the metadata section
|
||||
key: Key(_loadedMetadataUri),
|
||||
child: CustomScrollView(
|
||||
controller: widget.scrollController,
|
||||
slivers: [
|
||||
widget.appBar,
|
||||
SliverPadding(
|
||||
padding: horizontalPadding + EdgeInsets.only(top: 8),
|
||||
sliver: basicAndLocationSliver,
|
||||
),
|
||||
SliverPadding(
|
||||
padding: horizontalPadding + EdgeInsets.only(bottom: 8 + widget.mqViewInsetsBottom),
|
||||
sliver: metadataSliver,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _goToCollection(CollectionFilter filter) {
|
||||
if (collection == null) return;
|
||||
FilterNotification(filter).dispatch(context);
|
||||
}
|
||||
|
||||
void _onMetadataChanged() {
|
||||
_metadata = [];
|
||||
_loadedMetadataUri = null;
|
||||
_getMetadata();
|
||||
}
|
||||
|
||||
// fetch and hold metadata in the page widget and not in the section sliver,
|
||||
// so that we can refresh and limit the staggered animation of the metadata section
|
||||
Future<void> _getMetadata() async {
|
||||
|
|
|
@ -229,13 +229,12 @@ class _DateRow extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
final date = entry.bestDate;
|
||||
final dateText = date != null ? '${DateFormat.yMMMd().format(date)} • ${DateFormat.Hm().format(date)}' : Constants.unknown;
|
||||
final resolution = '${entry.width ?? '?'} × ${entry.height ?? '?'}';
|
||||
return Row(
|
||||
children: [
|
||||
DecoratedIcon(AIcons.date, shadows: [Constants.embossShadow], size: _iconSize),
|
||||
SizedBox(width: _iconPadding),
|
||||
Expanded(flex: 3, child: Text(dateText, strutStyle: Constants.overflowStrutStyle)),
|
||||
if (!entry.isSvg) Expanded(flex: 2, child: Text(resolution, strutStyle: Constants.overflowStrutStyle)),
|
||||
if (!entry.isSvg) Expanded(flex: 2, child: Text(entry.resolutionText, strutStyle: Constants.overflowStrutStyle)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue