diff --git a/packages/neon/neon/lib/l10n/en.arb b/packages/neon/neon/lib/l10n/en.arb index 53515aa9f32..c8ba84ae498 100644 --- a/packages/neon/neon/lib/l10n/en.arb +++ b/packages/neon/neon/lib/l10n/en.arb @@ -132,6 +132,7 @@ "globalOptionsThemeModeAutomatic": "Automatic", "globalOptionsThemeOLEDAsDark": "OLED theme as dark theme", "globalOptionsThemeKeepOriginalAccentColor": "Keep the original accent color", + "globalOptionsThemeCustomBackground": "Custom background", "globalOptionsPushNotificationsEnabled": "Enabled", "globalOptionsPushNotificationsEnabledDisabledNotice": "No UnifiedPush distributor could be found or you denied the permission for showing notifications. Please go to the app settings and allow notifications and go to https://unifiedpush.org/users/distributors and setup any of the listed distributors. Then re-open this app and you should be able to enable notifications", "globalOptionsPushNotificationsDistributor": "UnifiedPush Distributor", diff --git a/packages/neon/neon/lib/l10n/localizations.dart b/packages/neon/neon/lib/l10n/localizations.dart index ce9f982a632..67fb18b207a 100644 --- a/packages/neon/neon/lib/l10n/localizations.dart +++ b/packages/neon/neon/lib/l10n/localizations.dart @@ -515,6 +515,12 @@ abstract class NeonLocalizations { /// **'Keep the original accent color'** String get globalOptionsThemeKeepOriginalAccentColor; + /// No description provided for @globalOptionsThemeCustomBackground. + /// + /// In en, this message translates to: + /// **'Custom background'** + String get globalOptionsThemeCustomBackground; + /// No description provided for @globalOptionsPushNotificationsEnabled. /// /// In en, this message translates to: diff --git a/packages/neon/neon/lib/l10n/localizations_en.dart b/packages/neon/neon/lib/l10n/localizations_en.dart index e1aba5d66e1..075823b273e 100644 --- a/packages/neon/neon/lib/l10n/localizations_en.dart +++ b/packages/neon/neon/lib/l10n/localizations_en.dart @@ -254,6 +254,9 @@ class NeonLocalizationsEn extends NeonLocalizations { @override String get globalOptionsThemeKeepOriginalAccentColor => 'Keep the original accent color'; + @override + String get globalOptionsThemeCustomBackground => 'Custom background'; + @override String get globalOptionsPushNotificationsEnabled => 'Enabled'; diff --git a/packages/neon/neon/lib/src/app.dart b/packages/neon/neon/lib/src/app.dart index 1726389efa3..05d555cfefd 100644 --- a/packages/neon/neon/lib/src/app.dart +++ b/packages/neon/neon/lib/src/app.dart @@ -21,6 +21,7 @@ import 'package:neon/src/utils/global_options.dart'; import 'package:neon/src/utils/localizations.dart'; import 'package:neon/src/utils/provider.dart'; import 'package:neon/src/utils/push_utils.dart'; +import 'package:neon/src/widgets/custom_background.dart'; import 'package:neon/src/widgets/options_collection_builder.dart'; import 'package:nextcloud/core.dart' as core; import 'package:nextcloud/nextcloud.dart'; @@ -287,10 +288,12 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra ? _accountsBloc.getCapabilitiesBlocFor(activeAccountSnapshot.data!).capabilities : null, builder: (final context, final capabilitiesSnapshot) { + final nextcloudTheme = capabilitiesSnapshot.data?.capabilities.themingPublicCapabilities?.theming; final appTheme = AppTheme( - capabilitiesSnapshot.data?.capabilities.themingPublicCapabilities?.theming, + nextcloudTheme, keepOriginalAccentColor: options.themeKeepOriginalAccentColor.value, oledAsDark: options.themeOLEDAsDark.value, + customBackground: options.themeCustomBackground.value, appThemes: _appImplementations.map((final a) => a.theme).whereNotNull(), neonTheme: widget.neonTheme, ); @@ -308,6 +311,10 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra theme: appTheme.lightTheme, darkTheme: appTheme.darkTheme, routerConfig: _routerDelegate, + builder: (final context, final child) => NeonCustomBackground( + theme: options.themeCustomBackground.value ? nextcloudTheme : null, + child: child, + ), ); }, ); diff --git a/packages/neon/neon/lib/src/pages/home.dart b/packages/neon/neon/lib/src/pages/home.dart index 30d2e759ec2..807ddde1240 100644 --- a/packages/neon/neon/lib/src/pages/home.dart +++ b/packages/neon/neon/lib/src/pages/home.dart @@ -179,10 +179,7 @@ class _HomePageState extends State { if (drawerAlwaysVisible) { return Row( children: [ - ColoredBox( - color: Theme.of(context).colorScheme.background, - child: drawer, - ), + drawer, Expanded( child: body, ), diff --git a/packages/neon/neon/lib/src/pages/settings.dart b/packages/neon/neon/lib/src/pages/settings.dart index 7dc7185240d..4db97e3bdef 100644 --- a/packages/neon/neon/lib/src/pages/settings.dart +++ b/packages/neon/neon/lib/src/pages/settings.dart @@ -128,6 +128,9 @@ class _SettingsPageState extends State { ToggleSettingsTile( option: globalOptions.themeKeepOriginalAccentColor, ), + ToggleSettingsTile( + option: globalOptions.themeCustomBackground, + ), ], ), SettingsCategory( diff --git a/packages/neon/neon/lib/src/theme/theme.dart b/packages/neon/neon/lib/src/theme/theme.dart index a2692d61e75..4e89fa0ca89 100644 --- a/packages/neon/neon/lib/src/theme/theme.dart +++ b/packages/neon/neon/lib/src/theme/theme.dart @@ -13,12 +13,14 @@ class AppTheme { required this.neonTheme, final bool keepOriginalAccentColor = false, this.oledAsDark = false, + this.customBackground = false, this.appThemes, }) : keepOriginalAccentColor = nextcloudTheme == null || keepOriginalAccentColor; final core.ThemingPublicCapabilities_Theming? nextcloudTheme; final bool keepOriginalAccentColor; final bool oledAsDark; + final bool customBackground; final Iterable? appThemes; final NeonTheme neonTheme; @@ -40,7 +42,7 @@ class AppTheme { ThemeData _getTheme(final Brightness brightness) { final colorScheme = _buildColorScheme(brightness); - return ThemeData( + final theme = ThemeData( useMaterial3: true, colorScheme: colorScheme, scaffoldBackgroundColor: colorScheme.background, @@ -54,6 +56,25 @@ class AppTheme { ...?appThemes, ], ); + + if (customBackground) { + return theme.copyWith( + scaffoldBackgroundColor: Colors.transparent, + cardColor: Colors.transparent, + drawerTheme: const DrawerThemeData( + backgroundColor: Colors.transparent, + ), + appBarTheme: const AppBarTheme( + backgroundColor: Colors.transparent, + ), + bottomNavigationBarTheme: const BottomNavigationBarThemeData( + backgroundColor: Colors.transparent, + elevation: 0, + ), + ); + } + + return theme; } ThemeData get lightTheme => _getTheme(Brightness.light); diff --git a/packages/neon/neon/lib/src/utils/global_options.dart b/packages/neon/neon/lib/src/utils/global_options.dart index 99e190d9286..e5584ddd28e 100644 --- a/packages/neon/neon/lib/src/utils/global_options.dart +++ b/packages/neon/neon/lib/src/utils/global_options.dart @@ -70,6 +70,7 @@ class GlobalOptions extends OptionsCollection { themeMode, themeOLEDAsDark, themeKeepOriginalAccentColor, + themeCustomBackground, pushNotificationsEnabled, pushNotificationsDistributor, startupMinimized, @@ -142,6 +143,13 @@ class GlobalOptions extends OptionsCollection { defaultValue: false, ); + late final themeCustomBackground = ToggleOption( + storage: storage, + key: GlobalOptionKeys.themeCustomBackground, + label: (final context) => NeonLocalizations.of(context).globalOptionsThemeCustomBackground, + defaultValue: false, + ); + late final pushNotificationsEnabled = ToggleOption( storage: storage, key: GlobalOptionKeys.pushNotificationsEnabled, @@ -223,6 +231,7 @@ enum GlobalOptionKeys implements Storable { themeMode._('theme-mode'), themeOLEDAsDark._('theme-oled-as-dark'), themeKeepOriginalAccentColor._('theme-keep-original-accent-color'), + themeCustomBackground._('theme-custom-background'), pushNotificationsEnabled._('push-notifications-enabled'), pushNotificationsDistributor._('push-notifications-distributor'), startupMinimized._('startup-minimized'), diff --git a/packages/neon/neon/lib/src/widgets/custom_background.dart b/packages/neon/neon/lib/src/widgets/custom_background.dart new file mode 100644 index 00000000000..179138f16d4 --- /dev/null +++ b/packages/neon/neon/lib/src/widgets/custom_background.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:meta/meta.dart'; +import 'package:neon/src/utils/hex_color.dart'; +import 'package:neon/src/widgets/image.dart'; +import 'package:nextcloud/core.dart' as core; + +@internal +class NeonCustomBackground extends StatelessWidget { + const NeonCustomBackground({ + required this.theme, + required this.child, + super.key, + }); + + final core.ThemingPublicCapabilities_Theming? theme; + final Widget? child; + + double _opacity(final BuildContext context) => Theme.of(context).brightness == Brightness.light ? 0.2 : 0.1; + + @override + Widget build(final BuildContext context) { + if (theme == null) { + return ColoredBox( + color: Theme.of(context).colorScheme.background, + child: child, + ); + } + + if (theme?.backgroundPlain ?? true) { + return ColoredBox( + color: + Color.lerp(HexColor(theme!.background), Theme.of(context).colorScheme.background, 1 - _opacity(context))!, + child: child, + ); + } + + final image = NeonUrlImage( + url: theme!.background, + fit: BoxFit.fill, + ); + return ColoredBox( + color: Theme.of(context).colorScheme.background, + child: Stack( + children: [ + Positioned.fill( + child: Opacity( + opacity: _opacity(context), + child: image, + ), + ), + if (child != null) child!, + ], + ), + ); + } +}