diff --git a/CHANGELOG.md b/CHANGELOG.md index 25b9eb357..59a85be17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. - Cataloguing: identify Apple variant of HDR images - Collection: allow using hash (md5/sha1/sha256) when bulk renaming +- Info: color palette - option to force using western arabic numerals for dates ### Changed diff --git a/lib/theme/icons.dart b/lib/theme/icons.dart index bf2dc065b..df80978eb 100644 --- a/lib/theme/icons.dart +++ b/lib/theme/icons.dart @@ -47,6 +47,7 @@ class AIcons { static const mainStorage = Icons.smartphone_outlined; static const mimeType = Icons.code_outlined; static const opacity = Icons.opacity; + static const palette = Icons.palette_outlined; static final privacy = MdiIcons.shieldAccountOutline; static const rating = Icons.star_border_outlined; static const ratingFull = Icons.star; diff --git a/lib/widgets/viewer/info/color_section.dart b/lib/widgets/viewer/info/color_section.dart new file mode 100644 index 000000000..ca8d669e2 --- /dev/null +++ b/lib/widgets/viewer/info/color_section.dart @@ -0,0 +1,87 @@ +import 'dart:async'; +import 'dart:math'; + +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/images.dart'; +import 'package:aves/theme/durations.dart'; +import 'package:aves/theme/icons.dart'; +import 'package:aves/widgets/common/basic/color_indicator.dart'; +import 'package:aves/widgets/viewer/info/common.dart'; +import 'package:flex_color_picker/flex_color_picker.dart' as flex; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; +import 'package:palette_generator/palette_generator.dart'; +import 'package:provider/provider.dart'; + +class ColorSectionSliver extends StatefulWidget { + final AvesEntry entry; + + const ColorSectionSliver({super.key, required this.entry}); + + @override + State createState() => _ColorSectionSliverState(); +} + +class _ColorSectionSliverState extends State { + late final Future _paletteLoader; + + @override + void initState() { + super.initState(); + final provider = widget.entry.getThumbnail(extent: min(200, widget.entry.displaySize.longestSide)); + _paletteLoader = PaletteGenerator.fromImageProvider( + provider, + maximumColorCount: 10, + // do not use the default palette filter + filters: [], + ); + } + + @override + Widget build(BuildContext context) { + return SliverToBoxAdapter( + child: FutureBuilder( + future: _paletteLoader, + builder: (context, snapshot) { + final colors = snapshot.data?.paletteColors; + if (colors == null || colors.isEmpty) return const SizedBox(); + + final durations = context.watch(); + return Wrap( + alignment: WrapAlignment.center, + children: AnimationConfiguration.toStaggeredList( + duration: durations.staggeredAnimation, + delay: durations.staggeredAnimationDelay * timeDilation, + childAnimationBuilder: (child) => SlideAnimation( + verticalOffset: 50.0, + child: FadeInAnimation( + child: child, + ), + ), + children: [ + const SectionRow(icon: AIcons.palette), + ...colors.map( + (v) => Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + ColorIndicator(value: v.color), + const SizedBox(width: 8), + SelectableText( + '#${v.color.hex}', + style: const TextStyle(fontFamily: 'monospace'), + ), + ], + ), + ), + ), + ], + ), + ); + }, + ), + ); + } +} diff --git a/lib/widgets/viewer/info/info_page.dart b/lib/widgets/viewer/info/info_page.dart index 1f3708606..1ef8fd5da 100644 --- a/lib/widgets/viewer/info/info_page.dart +++ b/lib/widgets/viewer/info/info_page.dart @@ -17,6 +17,7 @@ import 'package:aves/widgets/viewer/info/basic_section.dart'; import 'package:aves/widgets/viewer/info/embedded/embedded_data_opener.dart'; import 'package:aves/widgets/viewer/info/info_app_bar.dart'; import 'package:aves/widgets/viewer/info/location_section.dart'; +import 'package:aves/widgets/viewer/info/color_section.dart'; import 'package:aves/widgets/viewer/info/metadata/metadata_dir.dart'; import 'package:aves/widgets/viewer/info/metadata/metadata_section.dart'; import 'package:aves/widgets/viewer/multipage/conductor.dart'; @@ -233,10 +234,6 @@ class _InfoPageContentState extends State<_InfoPageContent> { ], ), ); - final metadataSliver = MetadataSectionSliver( - entry: entry, - metadataNotifier: _metadataNotifier, - ); return NotificationListener( onNotification: (notification) { @@ -262,7 +259,14 @@ class _InfoPageContentState extends State<_InfoPageContent> { ), SliverPadding( padding: horizontalPadding + const EdgeInsets.only(bottom: 8), - sliver: metadataSliver, + sliver: MetadataSectionSliver( + entry: entry, + metadataNotifier: _metadataNotifier, + ), + ), + SliverPadding( + padding: horizontalPadding + const EdgeInsets.only(bottom: 8), + sliver: ColorSectionSliver(entry: entry), ), const BottomPaddingSliver(), ],