Default theme is back. Container selection functionality etc.
This commit is contained in:
@@ -47,6 +47,7 @@
|
|||||||
<PackageVersion Include="SkiaSharp" Version="3.119.1" />
|
<PackageVersion Include="SkiaSharp" Version="3.119.1" />
|
||||||
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
|
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
|
||||||
<PackageVersion Include="SqlKata" Version="4.0.1" />
|
<PackageVersion Include="SqlKata" Version="4.0.1" />
|
||||||
|
<PackageVersion Include="System.Drawing.Common" Version="9.0.10" />
|
||||||
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
|
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
|
||||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.10" />
|
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.10" />
|
||||||
<PackageVersion Include="System.Text.Json" Version="9.0.10" />
|
<PackageVersion Include="System.Text.Json" Version="9.0.10" />
|
||||||
@@ -67,4 +68,4 @@
|
|||||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="2.0.250930001-experimental1" />
|
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="2.0.250930001-experimental1" />
|
||||||
<PackageVersion Include="WinUIEx" Version="2.9.0" />
|
<PackageVersion Include="WinUIEx" Version="2.9.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -531,6 +531,9 @@
|
|||||||
"SettingsDiscord_Title": "Discord Channel",
|
"SettingsDiscord_Title": "Discord Channel",
|
||||||
"SettingsEditLinkedInbox_Description": "Add / remove accounts, rename or break the link between accounts.",
|
"SettingsEditLinkedInbox_Description": "Add / remove accounts, rename or break the link between accounts.",
|
||||||
"SettingsEditLinkedInbox_Title": "Edit Linked Inbox",
|
"SettingsEditLinkedInbox_Title": "Edit Linked Inbox",
|
||||||
|
"SettingsWindowBackdrop_Title": "Window Backdrop",
|
||||||
|
"SettingsWindowBackdrop_Description": "Select a backdrop effect for Wino windows.",
|
||||||
|
"SettingsWindowBackdrop_Disabled": "Window backdrop selection is disabled when application theme is selected other than Default.",
|
||||||
"SettingsElementTheme_Description": "Select a Windows theme for Wino",
|
"SettingsElementTheme_Description": "Select a Windows theme for Wino",
|
||||||
"SettingsElementTheme_Title": "Element Theme",
|
"SettingsElementTheme_Title": "Element Theme",
|
||||||
"SettingsElementThemeSelectionDisabled": "Element theme selection is disabled when application theme is selected other than Default.",
|
"SettingsElementThemeSelectionDisabled": "Element theme selection is disabled when application theme is selected other than Default.",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:xaml="using:Microsoft.UI.Xaml">
|
xmlns:xaml="using:Microsoft.UI.Xaml">
|
||||||
|
|
||||||
<x:String x:Key="ThemeName">Mica</x:String>
|
<x:String x:Key="ThemeName">Default</x:String>
|
||||||
|
|
||||||
<SolidColorBrush x:Key="WinoApplicationBackgroundColor">Transparent</SolidColorBrush>
|
<SolidColorBrush x:Key="WinoApplicationBackgroundColor">Transparent</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="AppBarBackgroundColor">Transparent</SolidColorBrush>
|
<SolidColorBrush x:Key="AppBarBackgroundColor">Transparent</SolidColorBrush>
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
# NewThemeService Implementation Summary
|
|
||||||
|
|
||||||
## What's Been Created
|
|
||||||
|
|
||||||
### 🏗️ Core Implementation
|
|
||||||
|
|
||||||
1. **WindowBackdropType Enum** (`Wino.Core.Domain\Enums\WindowBackdropType.cs`)
|
|
||||||
- Defines all supported backdrop types (Mica, MicaAlt, DesktopAcrylic, etc.)
|
|
||||||
|
|
||||||
2. **INewThemeService Interface** (`Wino.Core.Domain\Interfaces\INewThemeService.cs`)
|
|
||||||
- Extended interface with backdrop management and enhanced accent color support
|
|
||||||
- Backward compatible with existing IThemeService functionality
|
|
||||||
|
|
||||||
3. **NewThemeService Implementation** (`Wino.Core.WinUI\Services\NewThemeService.cs`)
|
|
||||||
- Full WinUI-optimized theme service
|
|
||||||
- Window backdrop management with WindowEx support
|
|
||||||
- Enhanced accent color management with theme preservation
|
|
||||||
- Proper initialization sequence for backdrop application
|
|
||||||
- Event system for backdrop, theme, and accent color changes
|
|
||||||
|
|
||||||
### 🔧 Integration Updates
|
|
||||||
|
|
||||||
4. **CoreUWPContainerSetup.cs** - Updated DI registration
|
|
||||||
- Registers both old and new theme services
|
|
||||||
- Maintains backward compatibility
|
|
||||||
|
|
||||||
5. **WinoApplication.cs** - Enhanced base application class
|
|
||||||
- Added NewThemeService property and initialization
|
|
||||||
- Updated service initialization order
|
|
||||||
- Proper backdrop application timing
|
|
||||||
|
|
||||||
6. **App.xaml.cs** - Updated application launch sequence
|
|
||||||
- Loads backdrop settings before window creation
|
|
||||||
- Applies backdrop immediately after window creation
|
|
||||||
- Maintains proper initialization flow
|
|
||||||
|
|
||||||
7. **ShellWindow.xaml** - Removed hardcoded backdrop
|
|
||||||
- Allows NewThemeService to control backdrop dynamically
|
|
||||||
|
|
||||||
### 📚 Documentation & Examples
|
|
||||||
|
|
||||||
8. **Usage Example** (`Examples\NewThemeServiceExampleViewModel.cs`)
|
|
||||||
- Complete ViewModel showing all features
|
|
||||||
- Event handling patterns
|
|
||||||
- Error handling best practices
|
|
||||||
- UI binding examples
|
|
||||||
|
|
||||||
9. **Comprehensive Documentation** (`README_NewThemeService.md`)
|
|
||||||
- Feature overview and benefits
|
|
||||||
- Usage examples and patterns
|
|
||||||
- Configuration and best practices
|
|
||||||
- Service registration details
|
|
||||||
|
|
||||||
10. **Migration Guide** (`MIGRATION_NewThemeService.md`)
|
|
||||||
- Step-by-step migration instructions
|
|
||||||
- Before/after code comparisons
|
|
||||||
- Common issues and solutions
|
|
||||||
- Testing checklist
|
|
||||||
|
|
||||||
## Key Features Delivered
|
|
||||||
|
|
||||||
### ✨ Window Backdrop Management
|
|
||||||
- **Dynamic Backdrop Control**: Switch between Mica, Acrylic, and other backdrops at runtime
|
|
||||||
- **Persistent Settings**: Backdrop preferences saved and restored across app sessions
|
|
||||||
- **Initialization Support**: Backdrop applied before window is shown to user
|
|
||||||
- **Multiple Backdrop Types**:
|
|
||||||
- `Mica` - Standard translucent material
|
|
||||||
- `MicaAlt` - Alternative Mica variant
|
|
||||||
- `DesktopAcrylic` - Semi-transparent acrylic
|
|
||||||
- `AcrylicBase` & `AcrylicThin` - Acrylic variants
|
|
||||||
- `None` - No backdrop for solid backgrounds
|
|
||||||
|
|
||||||
### 🎨 Enhanced Accent Color Management
|
|
||||||
- **Theme-Preserving Color Changes**: Change accent color without switching themes
|
|
||||||
- **System Integration**: Automatic system accent color detection
|
|
||||||
- **Improved API**: `SetAccentColorAsync()` with better control options
|
|
||||||
- **Backward Compatibility**: Works with existing accent color code
|
|
||||||
|
|
||||||
### 🖼️ Custom Wallpaper Support
|
|
||||||
- **Existing Functionality**: Polished implementation from old service
|
|
||||||
- **Thumbnail Generation**: Automatic preview image creation
|
|
||||||
- **Persistent Storage**: Themes saved in local app data
|
|
||||||
- **Metadata Management**: JSON-based theme configuration
|
|
||||||
|
|
||||||
### ⚡ Better Initialization
|
|
||||||
- **Startup Performance**: Backdrop settings loaded early in app lifecycle
|
|
||||||
- **Window Creation Flow**: Proper timing between window creation and backdrop application
|
|
||||||
- **Service Dependencies**: Correct initialization order with other services
|
|
||||||
- **Configuration Persistence**: All settings properly saved and restored
|
|
||||||
|
|
||||||
### 🔄 Backward Compatibility
|
|
||||||
- **Dual Service Support**: Both old and new services available
|
|
||||||
- **Gradual Migration**: Can migrate features incrementally
|
|
||||||
- **API Compatibility**: All existing theme functionality preserved
|
|
||||||
- **Zero Breaking Changes**: Existing code continues to work
|
|
||||||
|
|
||||||
## Technical Implementation Details
|
|
||||||
|
|
||||||
### Service Registration Pattern
|
|
||||||
```csharp
|
|
||||||
// Both services registered for compatibility
|
|
||||||
services.AddSingleton<IThemeService, ThemeService>();
|
|
||||||
services.AddSingleton<INewThemeService, NewThemeService>();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Initialization Sequence
|
|
||||||
1. App constructs services (including NewThemeService)
|
|
||||||
2. OnLaunched loads saved backdrop type from configuration
|
|
||||||
3. Window is created (ShellWindow)
|
|
||||||
4. Backdrop is applied immediately via ApplyBackdropAsync()
|
|
||||||
5. Service initialization completes (InitializeServicesAsync)
|
|
||||||
6. Window is activated and shown to user
|
|
||||||
|
|
||||||
### Configuration Keys
|
|
||||||
- `WindowBackdropTypeKey`: Backdrop type preference
|
|
||||||
- `AccentColorKey`: Custom accent color
|
|
||||||
- `CurrentApplicationThemeKey`: Selected theme ID
|
|
||||||
- `SelectedAppThemeKey`: Element theme (Light/Dark/Default)
|
|
||||||
|
|
||||||
### Event System
|
|
||||||
- `BackdropChanged`: Fired when backdrop type changes
|
|
||||||
- `ElementThemeChanged`: Existing theme change events
|
|
||||||
- `AccentColorChanged`: Existing accent color events
|
|
||||||
|
|
||||||
## Benefits Over Original ThemeService
|
|
||||||
|
|
||||||
### 🎯 WinUI Optimized
|
|
||||||
- **WindowEx Integration**: Proper SystemBackdrop property usage
|
|
||||||
- **Modern Backdrop Types**: Support for latest WinUI backdrop materials
|
|
||||||
- **Performance**: Optimized for WinUI rendering pipeline
|
|
||||||
|
|
||||||
### 💡 Enhanced User Experience
|
|
||||||
- **Visual Polish**: Professional backdrop effects (Mica, Acrylic)
|
|
||||||
- **Smooth Transitions**: Proper backdrop switching without flicker
|
|
||||||
- **System Integration**: Respects system theme and accent preferences
|
|
||||||
|
|
||||||
### 🔧 Developer Experience
|
|
||||||
- **Better APIs**: Async methods with proper error handling
|
|
||||||
- **Event System**: Rich event notifications for UI updates
|
|
||||||
- **Clear Separation**: Theme vs backdrop vs accent color management
|
|
||||||
- **Documentation**: Comprehensive guides and examples
|
|
||||||
|
|
||||||
### 🚀 Future Ready
|
|
||||||
- **Extensible**: Easy to add new backdrop types
|
|
||||||
- **Modern Patterns**: Async/await, proper DI integration
|
|
||||||
- **Maintainable**: Clean separation of concerns
|
|
||||||
- **Testable**: Interface-based design with dependency injection
|
|
||||||
|
|
||||||
## Deployment Notes
|
|
||||||
|
|
||||||
### Required Steps for Integration
|
|
||||||
1. ✅ All code files created and properly structured
|
|
||||||
2. ✅ Service registration updated in DI container
|
|
||||||
3. ✅ WinoApplication updated with NewThemeService support
|
|
||||||
4. ✅ App launch sequence updated for proper backdrop initialization
|
|
||||||
5. ✅ Hardcoded backdrop removed from XAML
|
|
||||||
6. ✅ Backward compatibility maintained
|
|
||||||
7. ✅ Documentation and examples provided
|
|
||||||
|
|
||||||
### Testing Recommendations
|
|
||||||
- Test all backdrop types on different system themes
|
|
||||||
- Verify backdrop persistence across app restarts
|
|
||||||
- Test accent color changes with different themes
|
|
||||||
- Verify custom theme creation still works
|
|
||||||
- Test on different Windows versions and hardware
|
|
||||||
|
|
||||||
### Migration Path
|
|
||||||
- Services can coexist during transition period
|
|
||||||
- Teams can migrate features incrementally
|
|
||||||
- No breaking changes to existing functionality
|
|
||||||
- Full backward compatibility maintained
|
|
||||||
|
|
||||||
This implementation provides a solid foundation for modern WinUI theming with professional backdrop effects while maintaining all existing functionality.
|
|
||||||
@@ -1,258 +0,0 @@
|
|||||||
# NewThemeService Migration Guide
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This guide helps you migrate from the original `ThemeService` to the new `NewThemeService` with enhanced backdrop management and improved accent color handling.
|
|
||||||
|
|
||||||
## Quick Migration Steps
|
|
||||||
|
|
||||||
### 1. Update Service Usage
|
|
||||||
|
|
||||||
**Before (Old ThemeService):**
|
|
||||||
```csharp
|
|
||||||
var themeService = Services.GetService<IThemeService>();
|
|
||||||
themeService.RootTheme = ApplicationElementTheme.Dark;
|
|
||||||
themeService.AccentColor = "#FF5722";
|
|
||||||
```
|
|
||||||
|
|
||||||
**After (NewThemeService):**
|
|
||||||
```csharp
|
|
||||||
var newThemeService = Services.GetService<INewThemeService>();
|
|
||||||
newThemeService.RootTheme = ApplicationElementTheme.Dark;
|
|
||||||
await newThemeService.SetAccentColorAsync("#FF5722", preserveTheme: true);
|
|
||||||
|
|
||||||
// Plus new backdrop management
|
|
||||||
await newThemeService.ApplyBackdropAsync(WindowBackdropType.Mica);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Remove Hardcoded Backdrops
|
|
||||||
|
|
||||||
**Before:**
|
|
||||||
```xaml
|
|
||||||
<winuiex:WindowEx.SystemBackdrop>
|
|
||||||
<MicaBackdrop />
|
|
||||||
</winuiex:WindowEx.SystemBackdrop>
|
|
||||||
```
|
|
||||||
|
|
||||||
**After:**
|
|
||||||
```xaml
|
|
||||||
<!-- SystemBackdrop will be set by NewThemeService -->
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Update App Initialization
|
|
||||||
|
|
||||||
**Before:**
|
|
||||||
```csharp
|
|
||||||
protected override async void OnLaunched(LaunchActivatedEventArgs args)
|
|
||||||
{
|
|
||||||
MainWindow = new ShellWindow();
|
|
||||||
await InitializeServicesAsync();
|
|
||||||
MainWindow.Activate();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**After:**
|
|
||||||
```csharp
|
|
||||||
protected override async void OnLaunched(LaunchActivatedEventArgs args)
|
|
||||||
{
|
|
||||||
// Load backdrop settings before creating window
|
|
||||||
var newThemeService = Services.GetService<INewThemeService>();
|
|
||||||
var configService = Services.GetService<IConfigurationService>();
|
|
||||||
var savedBackdropType = (WindowBackdropType)configService.Get("WindowBackdropTypeKey", (int)WindowBackdropType.Mica);
|
|
||||||
|
|
||||||
MainWindow = new ShellWindow();
|
|
||||||
|
|
||||||
// Apply backdrop immediately after window creation
|
|
||||||
if (newThemeService != null)
|
|
||||||
{
|
|
||||||
await newThemeService.ApplyBackdropAsync(savedBackdropType);
|
|
||||||
}
|
|
||||||
|
|
||||||
await InitializeServicesAsync();
|
|
||||||
MainWindow.Activate();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Update WinoApplication Services
|
|
||||||
|
|
||||||
**Before:**
|
|
||||||
```csharp
|
|
||||||
public IEnumerable<IInitializeAsync> GetActivationServices()
|
|
||||||
{
|
|
||||||
yield return DatabaseService;
|
|
||||||
yield return TranslationService;
|
|
||||||
yield return ThemeService; // Old service
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**After:**
|
|
||||||
```csharp
|
|
||||||
public IEnumerable<IInitializeAsync> GetActivationServices()
|
|
||||||
{
|
|
||||||
yield return DatabaseService;
|
|
||||||
yield return TranslationService;
|
|
||||||
yield return NewThemeService; // New service
|
|
||||||
// yield return ThemeService; // Keep for backward compatibility but don't initialize
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## New Features Available After Migration
|
|
||||||
|
|
||||||
### 1. Backdrop Management
|
|
||||||
```csharp
|
|
||||||
// All available backdrop types
|
|
||||||
await newThemeService.ApplyBackdropAsync(WindowBackdropType.Mica);
|
|
||||||
await newThemeService.ApplyBackdropAsync(WindowBackdropType.MicaAlt);
|
|
||||||
await newThemeService.ApplyBackdropAsync(WindowBackdropType.DesktopAcrylic);
|
|
||||||
await newThemeService.ApplyBackdropAsync(WindowBackdropType.AcrylicBase);
|
|
||||||
await newThemeService.ApplyBackdropAsync(WindowBackdropType.AcrylicThin);
|
|
||||||
await newThemeService.ApplyBackdropAsync(WindowBackdropType.None);
|
|
||||||
|
|
||||||
// Persistent backdrop setting
|
|
||||||
newThemeService.CurrentBackdropType = WindowBackdropType.MicaAlt;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Enhanced Accent Color Management
|
|
||||||
```csharp
|
|
||||||
// Set accent color without changing theme
|
|
||||||
await newThemeService.SetAccentColorAsync("#FF5722", preserveTheme: true);
|
|
||||||
|
|
||||||
// Get system accent color
|
|
||||||
var systemColor = newThemeService.GetSystemAccentColorHex();
|
|
||||||
await newThemeService.SetAccentColorAsync(systemColor);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Event Handling
|
|
||||||
```csharp
|
|
||||||
// New backdrop change events
|
|
||||||
newThemeService.BackdropChanged += (sender, backdropType) => {
|
|
||||||
// Handle backdrop changes
|
|
||||||
UpdateUI(backdropType);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Existing events still work
|
|
||||||
newThemeService.ElementThemeChanged += (sender, theme) => {
|
|
||||||
// Handle theme changes
|
|
||||||
};
|
|
||||||
|
|
||||||
newThemeService.AccentColorChanged += (sender, color) => {
|
|
||||||
// Handle accent color changes
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Backward Compatibility
|
|
||||||
|
|
||||||
### Both Services Available
|
|
||||||
- `IThemeService` (original) - still registered and functional
|
|
||||||
- `INewThemeService` (new) - enhanced version with additional features
|
|
||||||
|
|
||||||
### Choosing Which to Use
|
|
||||||
```csharp
|
|
||||||
// For new code - use NewThemeService
|
|
||||||
var newThemeService = Services.GetService<INewThemeService>();
|
|
||||||
|
|
||||||
// For existing code that needs compatibility - keep using ThemeService
|
|
||||||
var oldThemeService = Services.GetService<IThemeService>();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Gradual Migration Strategy
|
|
||||||
1. **Phase 1**: Register both services, initialize NewThemeService
|
|
||||||
2. **Phase 2**: Update new features to use NewThemeService
|
|
||||||
3. **Phase 3**: Migrate existing code gradually
|
|
||||||
4. **Phase 4**: Eventually phase out old ThemeService (optional)
|
|
||||||
|
|
||||||
## Common Migration Issues
|
|
||||||
|
|
||||||
### Issue 1: Window Backdrop Not Applied
|
|
||||||
**Problem**: Backdrop doesn't appear after migration
|
|
||||||
**Solution**: Ensure backdrop is applied after window creation but before activation
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
MainWindow = new ShellWindow();
|
|
||||||
await newThemeService.ApplyBackdropAsync(WindowBackdropType.Mica); // Add this
|
|
||||||
await InitializeServicesAsync();
|
|
||||||
MainWindow.Activate();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Issue 2: Accent Color Changes Don't Persist
|
|
||||||
**Problem**: Accent color resets after app restart
|
|
||||||
**Solution**: Use the enhanced SetAccentColorAsync method
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Old way - might not persist properly
|
|
||||||
newThemeService.AccentColor = "#FF5722";
|
|
||||||
|
|
||||||
// New way - properly persisted
|
|
||||||
await newThemeService.SetAccentColorAsync("#FF5722", preserveTheme: true);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Issue 3: Multiple Service Initialization
|
|
||||||
**Problem**: Both services being initialized causing conflicts
|
|
||||||
**Solution**: Only initialize NewThemeService in GetActivationServices()
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
public IEnumerable<IInitializeAsync> GetActivationServices()
|
|
||||||
{
|
|
||||||
yield return DatabaseService;
|
|
||||||
yield return TranslationService;
|
|
||||||
yield return NewThemeService; // Only this one
|
|
||||||
// Don't yield ThemeService here
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing Your Migration
|
|
||||||
|
|
||||||
### 1. Backdrop Functionality
|
|
||||||
- [ ] App starts with saved backdrop type
|
|
||||||
- [ ] Backdrop changes are applied immediately
|
|
||||||
- [ ] Backdrop changes persist after app restart
|
|
||||||
- [ ] All backdrop types work correctly
|
|
||||||
|
|
||||||
### 2. Theme Functionality
|
|
||||||
- [ ] Light/Dark theme changes work
|
|
||||||
- [ ] Custom themes still function
|
|
||||||
- [ ] Theme changes persist after restart
|
|
||||||
|
|
||||||
### 3. Accent Color Management
|
|
||||||
- [ ] Custom accent colors apply correctly
|
|
||||||
- [ ] System accent color detection works
|
|
||||||
- [ ] Accent color changes persist
|
|
||||||
- [ ] Theme preservation works with accent changes
|
|
||||||
|
|
||||||
### 4. Backward Compatibility
|
|
||||||
- [ ] Existing custom themes still work
|
|
||||||
- [ ] Old theme-related code continues to function
|
|
||||||
- [ ] No regression in existing functionality
|
|
||||||
|
|
||||||
## Performance Considerations
|
|
||||||
|
|
||||||
### Initialization Order
|
|
||||||
- NewThemeService initializes backdrop settings from saved configuration
|
|
||||||
- Window creation happens before service initialization for best performance
|
|
||||||
- Backdrop is applied immediately after window creation
|
|
||||||
|
|
||||||
### Runtime Performance
|
|
||||||
- Backdrop changes are async operations
|
|
||||||
- Don't change backdrop frequently (e.g., during animations)
|
|
||||||
- Cache backdrop type to avoid unnecessary changes
|
|
||||||
|
|
||||||
## Complete Migration Checklist
|
|
||||||
|
|
||||||
- [ ] Update DI container registration
|
|
||||||
- [ ] Update WinoApplication service initialization
|
|
||||||
- [ ] Remove hardcoded SystemBackdrop from XAML
|
|
||||||
- [ ] Update app launch sequence
|
|
||||||
- [ ] Update settings/preferences UI for backdrop options
|
|
||||||
- [ ] Test all backdrop types
|
|
||||||
- [ ] Test theme and accent color functionality
|
|
||||||
- [ ] Verify persistence across app restarts
|
|
||||||
- [ ] Update documentation and comments
|
|
||||||
- [ ] Train team on new features
|
|
||||||
|
|
||||||
## Need Help?
|
|
||||||
|
|
||||||
If you encounter issues during migration:
|
|
||||||
1. Check the complete example in `NewThemeServiceExampleViewModel`
|
|
||||||
2. Review the full documentation in `README_NewThemeService.md`
|
|
||||||
3. Ensure proper initialization order in your app
|
|
||||||
4. Verify all required using statements are included
|
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
# NewThemeService Documentation
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
The `NewThemeService` is an enhanced theme management service designed specifically for WinUI applications. It extends the capabilities of the original `ThemeService` with advanced features like backdrop management, improved accent color handling, and better window initialization support.
|
|
||||||
|
|
||||||
## Key Features
|
|
||||||
|
|
||||||
### 🎨 Window Backdrop Management
|
|
||||||
- **Mica**: Modern translucent material with system-aware tinting
|
|
||||||
- **MicaAlt**: Alternative Mica variant with different tinting behavior
|
|
||||||
- **DesktopAcrylic**: Semi-transparent acrylic background
|
|
||||||
- **AcrylicBase & AcrylicThin**: Different acrylic variants
|
|
||||||
- **None**: No backdrop (solid background)
|
|
||||||
|
|
||||||
### 🌈 Enhanced Accent Color Management
|
|
||||||
- Set custom accent colors without changing themes
|
|
||||||
- Preserve theme while changing accent colors
|
|
||||||
- Automatic system accent color detection
|
|
||||||
- Backward-compatible color management
|
|
||||||
|
|
||||||
### 🖼️ Custom Wallpaper Support
|
|
||||||
- Create custom themes with user wallpapers
|
|
||||||
- Automatic thumbnail generation
|
|
||||||
- Persistent theme storage
|
|
||||||
- Reuses existing custom theme functionality
|
|
||||||
|
|
||||||
### ⚡ Improved Initialization
|
|
||||||
- Backdrop settings applied before window creation
|
|
||||||
- Persistent settings restoration
|
|
||||||
- Proper service initialization order
|
|
||||||
|
|
||||||
## Usage Examples
|
|
||||||
|
|
||||||
### Basic Setup
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Service is automatically registered in DI container
|
|
||||||
var newThemeService = WinoApplication.Current.Services.GetService<INewThemeService>();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Changing Window Backdrop
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Apply different backdrop types
|
|
||||||
await newThemeService.ApplyBackdropAsync(WindowBackdropType.Mica);
|
|
||||||
await newThemeService.ApplyBackdropAsync(WindowBackdropType.DesktopAcrylic);
|
|
||||||
await newThemeService.ApplyBackdropAsync(WindowBackdropType.None);
|
|
||||||
|
|
||||||
// Persist the setting
|
|
||||||
newThemeService.CurrentBackdropType = WindowBackdropType.MicaAlt;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Accent Color
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Set accent color while preserving theme
|
|
||||||
await newThemeService.SetAccentColorAsync("#FF6B47", preserveTheme: true);
|
|
||||||
|
|
||||||
// Reset to system accent color
|
|
||||||
var systemAccent = newThemeService.GetSystemAccentColorHex();
|
|
||||||
await newThemeService.SetAccentColorAsync(systemAccent);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Event Handling
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
newThemeService.BackdropChanged += (sender, backdropType) => {
|
|
||||||
Debug.WriteLine($"Backdrop changed to: {backdropType}");
|
|
||||||
};
|
|
||||||
|
|
||||||
newThemeService.ElementThemeChanged += (sender, theme) => {
|
|
||||||
Debug.WriteLine($"Theme changed to: {theme}");
|
|
||||||
};
|
|
||||||
|
|
||||||
newThemeService.AccentColorChanged += (sender, color) => {
|
|
||||||
Debug.WriteLine($"Accent color changed to: {color}");
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Creating Custom Themes
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Create custom theme with wallpaper
|
|
||||||
var wallpaperData = await GetWallpaperBytesAsync(); // Your implementation
|
|
||||||
var customTheme = await newThemeService.CreateNewCustomThemeAsync(
|
|
||||||
"My Theme",
|
|
||||||
"#FF5722",
|
|
||||||
wallpaperData);
|
|
||||||
|
|
||||||
// Apply the custom theme
|
|
||||||
newThemeService.CurrentApplicationThemeId = customTheme.Id;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Service Registration
|
|
||||||
|
|
||||||
The service is automatically registered in the DI container:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// In CoreUWPContainerSetup.cs
|
|
||||||
services.AddSingleton<INewThemeService, NewThemeService>();
|
|
||||||
```
|
|
||||||
|
|
||||||
## Initialization Order
|
|
||||||
|
|
||||||
The service is initialized during app startup:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// In WinoApplication.cs
|
|
||||||
public IEnumerable<IInitializeAsync> GetActivationServices()
|
|
||||||
{
|
|
||||||
yield return DatabaseService;
|
|
||||||
yield return TranslationService;
|
|
||||||
yield return NewThemeService; // Initializes before window activation
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Backdrop Application Flow
|
|
||||||
|
|
||||||
1. **App Launch**: Saved backdrop type is loaded from configuration
|
|
||||||
2. **Window Creation**: Window is created without hardcoded backdrop
|
|
||||||
3. **Service Initialization**: NewThemeService initializes and applies saved backdrop
|
|
||||||
4. **Runtime Changes**: User can change backdrop which is immediately applied and persisted
|
|
||||||
|
|
||||||
## Backward Compatibility
|
|
||||||
|
|
||||||
- The original `IThemeService` is still registered and functional
|
|
||||||
- All existing theme functionality is preserved
|
|
||||||
- Custom theme creation and management works as before
|
|
||||||
- Accent color management is enhanced but compatible
|
|
||||||
|
|
||||||
## Configuration Keys
|
|
||||||
|
|
||||||
The service uses these persistent configuration keys:
|
|
||||||
|
|
||||||
- `WindowBackdropTypeKey`: Stores the selected backdrop type (int)
|
|
||||||
- `AccentColorKey`: Stores custom accent color (string)
|
|
||||||
- `CurrentApplicationThemeKey`: Stores selected theme ID (Guid)
|
|
||||||
- `SelectedAppThemeKey`: Stores element theme preference (ApplicationElementTheme)
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
### ✅ Do
|
|
||||||
- Initialize the service early in app lifecycle
|
|
||||||
- Use async methods for backdrop changes
|
|
||||||
- Handle backdrop change errors gracefully
|
|
||||||
- Subscribe to events for UI updates
|
|
||||||
- Preserve themes when changing accent colors
|
|
||||||
|
|
||||||
### ❌ Don't
|
|
||||||
- Set hardcoded SystemBackdrop in XAML
|
|
||||||
- Change backdrop on every frame/animation
|
|
||||||
- Ignore initialization errors
|
|
||||||
- Forget to unsubscribe from events
|
|
||||||
|
|
||||||
## Migration from Old ThemeService
|
|
||||||
|
|
||||||
1. **Update DI Registration**: Both services are registered, choose which to use
|
|
||||||
2. **Update Initialization**: Change `GetActivationServices()` to use `NewThemeService`
|
|
||||||
3. **Remove Hardcoded Backdrops**: Remove `<MicaBackdrop />` from XAML
|
|
||||||
4. **Add Backdrop Management**: Use `ApplyBackdropAsync()` for backdrop changes
|
|
||||||
5. **Enhanced Accent Colors**: Use `SetAccentColorAsync()` for better accent color management
|
|
||||||
|
|
||||||
## See Also
|
|
||||||
|
|
||||||
- `INewThemeService` interface documentation
|
|
||||||
- `WindowBackdropType` enumeration
|
|
||||||
- `NewThemeServiceExampleViewModel` for complete usage example
|
|
||||||
- Original `ThemeService` for backward compatibility
|
|
||||||
@@ -38,6 +38,7 @@ public class NewThemeService : INewThemeService
|
|||||||
{
|
{
|
||||||
public const string CustomThemeFolderName = "CustomThemes";
|
public const string CustomThemeFolderName = "CustomThemes";
|
||||||
|
|
||||||
|
private static string _defaultThemeId = "00000000-0000-0000-0000-000000000000";
|
||||||
private static string _cloudsThemeId = "3b621cc2-e270-4a76-8477-737917cccda0";
|
private static string _cloudsThemeId = "3b621cc2-e270-4a76-8477-737917cccda0";
|
||||||
private static string _forestThemeId = "8bc89b37-a7c5-4049-86e2-de1ae8858dbd";
|
private static string _forestThemeId = "8bc89b37-a7c5-4049-86e2-de1ae8858dbd";
|
||||||
private static string _nightyThemeId = "5b65e04e-fd7e-4c2d-8221-068d3e02d23a";
|
private static string _nightyThemeId = "5b65e04e-fd7e-4c2d-8221-068d3e02d23a";
|
||||||
@@ -64,6 +65,7 @@ public class NewThemeService : INewThemeService
|
|||||||
|
|
||||||
private List<AppThemeBase> preDefinedThemes { get; set; } = new List<AppThemeBase>()
|
private List<AppThemeBase> preDefinedThemes { get; set; } = new List<AppThemeBase>()
|
||||||
{
|
{
|
||||||
|
new SystemAppTheme("Default", Guid.Parse(_defaultThemeId)),
|
||||||
new PreDefinedAppTheme("Nighty", Guid.Parse(_nightyThemeId), "#e1b12c", ApplicationElementTheme.Dark),
|
new PreDefinedAppTheme("Nighty", Guid.Parse(_nightyThemeId), "#e1b12c", ApplicationElementTheme.Dark),
|
||||||
new PreDefinedAppTheme("Forest", Guid.Parse(_forestThemeId), "#16a085", ApplicationElementTheme.Dark),
|
new PreDefinedAppTheme("Forest", Guid.Parse(_forestThemeId), "#16a085", ApplicationElementTheme.Dark),
|
||||||
new PreDefinedAppTheme("Clouds", Guid.Parse(_cloudsThemeId), "#0984e3", ApplicationElementTheme.Light),
|
new PreDefinedAppTheme("Clouds", Guid.Parse(_cloudsThemeId), "#0984e3", ApplicationElementTheme.Light),
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
<Content Include="AppThemes\Garden.xaml">
|
<Content Include="AppThemes\Garden.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="AppThemes\Mica.xaml">
|
<Content Include="AppThemes\Default.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="AppThemes\Nighty.xaml">
|
<Content Include="AppThemes\Nighty.xaml">
|
||||||
@@ -78,6 +78,7 @@
|
|||||||
<PackageReference Include="CommunityToolkit.Diagnostics" />
|
<PackageReference Include="CommunityToolkit.Diagnostics" />
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" />
|
<PackageReference Include="CommunityToolkit.Mvvm" />
|
||||||
<PackageReference Include="EmailValidation" />
|
<PackageReference Include="EmailValidation" />
|
||||||
|
<PackageReference Include="System.Drawing.Common" />
|
||||||
<PackageReference Include="WinUIEx" />
|
<PackageReference Include="WinUIEx" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -636,6 +636,8 @@ public partial class GroupedEmailCollection : ObservableObject, IRecipient<Prope
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int IndexOf(object item) => Items.IndexOf(item);
|
||||||
|
|
||||||
private void RefreshThreadInUI(ThreadMailItemViewModel expander)
|
private void RefreshThreadInUI(ThreadMailItemViewModel expander)
|
||||||
{
|
{
|
||||||
// Remove thread completely from UI
|
// Remove thread completely from UI
|
||||||
@@ -646,6 +648,53 @@ public partial class GroupedEmailCollection : ObservableObject, IRecipient<Prope
|
|||||||
AddThreadToUI(expander, groupKey);
|
AddThreadToUI(expander, groupKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MailItemContainer GetMailItemContainer(Guid uniqueId)
|
||||||
|
{
|
||||||
|
// First, search in standalone mail items (not displayed in threads)
|
||||||
|
var standaloneMailItem = _sourceItems.FirstOrDefault(item =>
|
||||||
|
item.MailCopy.UniqueId == uniqueId && !item.IsDisplayedInThread);
|
||||||
|
|
||||||
|
if (standaloneMailItem != null)
|
||||||
|
{
|
||||||
|
// Check if the standalone item is visible in the UI
|
||||||
|
var isItemVisible = Items.Contains(standaloneMailItem);
|
||||||
|
|
||||||
|
return new MailItemContainer(standaloneMailItem)
|
||||||
|
{
|
||||||
|
IsItemVisible = isItemVisible,
|
||||||
|
IsThreadVisible = false // Not a threaded item
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search in thread expanders for threaded mail items
|
||||||
|
foreach (var threadExpander in _threadExpanders.Values)
|
||||||
|
{
|
||||||
|
if (threadExpander.HasUniqueId(uniqueId))
|
||||||
|
{
|
||||||
|
// Find the specific mail item within the thread
|
||||||
|
var threadMailItem = threadExpander.ThreadEmails.FirstOrDefault(email =>
|
||||||
|
email.MailCopy.UniqueId == uniqueId);
|
||||||
|
|
||||||
|
if (threadMailItem != null)
|
||||||
|
{
|
||||||
|
// Check visibility: thread expander must be visible, and for individual item visibility,
|
||||||
|
// the thread must be expanded and the item must be in the visible Items collection
|
||||||
|
var isThreadVisible = Items.Contains(threadExpander);
|
||||||
|
var isItemVisible = isThreadVisible && threadExpander.IsThreadExpanded && Items.Contains(threadMailItem);
|
||||||
|
|
||||||
|
return new MailItemContainer(threadMailItem, threadExpander)
|
||||||
|
{
|
||||||
|
IsItemVisible = isItemVisible,
|
||||||
|
IsThreadVisible = isThreadVisible
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Item not found
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private void AddThreadToUI(ThreadMailItemViewModel expander, string groupKey)
|
private void AddThreadToUI(ThreadMailItemViewModel expander, string groupKey)
|
||||||
{
|
{
|
||||||
var groupHeader = GetOrCreateGroupHeader(groupKey);
|
var groupHeader = GetOrCreateGroupHeader(groupKey);
|
||||||
|
|||||||
@@ -6,6 +6,25 @@ public class MailItemContainer
|
|||||||
{
|
{
|
||||||
public MailItemViewModel ItemViewModel { get; set; }
|
public MailItemViewModel ItemViewModel { get; set; }
|
||||||
public ThreadMailItemViewModel ThreadViewModel { get; set; }
|
public ThreadMailItemViewModel ThreadViewModel { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the mail item is currently visible in the UI's Items collection.
|
||||||
|
/// For threaded items, this indicates if the individual mail item is visible (thread must be expanded).
|
||||||
|
/// </summary>
|
||||||
|
public bool IsItemVisible { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the thread expander (if applicable) is currently visible in the UI's Items collection.
|
||||||
|
/// Only relevant when ThreadViewModel is not null.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsThreadVisible { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the container can be successfully navigated to in the UI.
|
||||||
|
/// For standalone items: true if IsItemVisible is true.
|
||||||
|
/// For threaded items: true if IsThreadVisible is true (the thread expander can be navigated to).
|
||||||
|
/// </summary>
|
||||||
|
public bool CanNavigate => ThreadViewModel != null ? IsThreadVisible : IsItemVisible;
|
||||||
|
|
||||||
public MailItemContainer(MailItemViewModel itemViewModel, ThreadMailItemViewModel threadViewModel) : this(itemViewModel)
|
public MailItemContainer(MailItemViewModel itemViewModel, ThreadMailItemViewModel threadViewModel) : this(itemViewModel)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -110,4 +110,9 @@ public partial class ThreadMailItemViewModel : ObservableRecipient, IDisposable
|
|||||||
NotifyPropertyChanges();
|
NotifyPropertyChanges();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this thread contains an email with the specified unique ID
|
||||||
|
/// </summary>
|
||||||
|
public bool HasUniqueId(Guid uniqueId) => _threadEmails.Any(email => email.MailCopy.UniqueId == uniqueId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<ResourceDictionary Source="Styles/WebViewEditorControl.xaml" />
|
<ResourceDictionary Source="Styles/WebViewEditorControl.xaml" />
|
||||||
|
|
||||||
<styles:WinoExpanderStyle />
|
<styles:WinoExpanderStyle />
|
||||||
<ResourceDictionary Source="/Wino.Core.WinUI/AppThemes/Mica.xaml" />
|
<ResourceDictionary Source="/Wino.Core.WinUI/AppThemes/Default.xaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</uwp:WinoApplication.Resources>
|
</uwp:WinoApplication.Resources>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using CommunityToolkit.WinUI;
|
using CommunityToolkit.WinUI;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Wino.Mail.ViewModels.Data;
|
||||||
|
|
||||||
namespace Wino.Mail.WinUI.Controls.Advanced;
|
namespace Wino.Mail.WinUI.Controls.Advanced;
|
||||||
|
|
||||||
@@ -43,4 +44,44 @@ public partial class WinoItemsView : ItemsView
|
|||||||
// Trigger when scrolled past 90% of total height
|
// Trigger when scrolled past 90% of total height
|
||||||
if (progress >= 0.9) LoadMoreCommand?.Execute(null);
|
if (progress >= 0.9) LoadMoreCommand?.Execute(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool SelectMailItemContainer(MailItemViewModel mailItemViewModel)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recursively clears all selections except the given mail.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="exceptViewModel">Exceptional mail item to be not unselected.</param>
|
||||||
|
/// <param name="preserveThreadExpanding">Whether expansion states of thread containers should stay as it is or not.</param>
|
||||||
|
public void ClearSelections(MailItemViewModel? exceptViewModel = null, bool preserveThreadExpanding = false)
|
||||||
|
{
|
||||||
|
if (CastedItemsSource == null) return;
|
||||||
|
|
||||||
|
foreach (var item in CastedItemsSource)
|
||||||
|
{
|
||||||
|
if (item is MailItemViewModel mailItemViewModel)
|
||||||
|
{
|
||||||
|
if (mailItemViewModel != exceptViewModel)
|
||||||
|
{
|
||||||
|
mailItemViewModel.IsSelected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (item is ThreadMailItemViewModel threadMailItemViewModel)
|
||||||
|
{
|
||||||
|
threadMailItemViewModel.IsSelected = false;
|
||||||
|
|
||||||
|
if (!preserveThreadExpanding) threadMailItemViewModel.IsThreadExpanded = false;
|
||||||
|
|
||||||
|
foreach (var childMail in threadMailItemViewModel.ThreadEmails)
|
||||||
|
{
|
||||||
|
if (childMail != exceptViewModel)
|
||||||
|
{
|
||||||
|
childMail.IsSelected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,16 +12,17 @@
|
|||||||
|
|
||||||
<!-- SystemBackdrop will be set by NewThemeService -->
|
<!-- SystemBackdrop will be set by NewThemeService -->
|
||||||
|
|
||||||
<Grid Background="{ThemeResource WinoApplicationBackgroundColor}">
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid Grid.RowSpan="2" Background="{ThemeResource WinoApplicationBackgroundColor}" />
|
||||||
<TitleBar
|
<TitleBar
|
||||||
x:Name="ShellTitleBar"
|
x:Name="ShellTitleBar"
|
||||||
Title="{x:Bind StatePersistanceService.CoreWindowTitle, Mode=OneWay}"
|
Title="{x:Bind StatePersistanceService.CoreWindowTitle, Mode=OneWay}"
|
||||||
Margin="-3,-3,0,0"
|
MinHeight="48"
|
||||||
HorizontalContentAlignment="Stretch"
|
HorizontalContentAlignment="Stretch"
|
||||||
VerticalContentAlignment="Stretch"
|
VerticalContentAlignment="Stretch"
|
||||||
BackRequested="BackButtonClicked"
|
BackRequested="BackButtonClicked"
|
||||||
|
|||||||
@@ -127,10 +127,7 @@
|
|||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate x:Key="DateGroupHeaderTemplate" x:DataType="data:DateGroupHeader">
|
<DataTemplate x:Key="DateGroupHeaderTemplate" x:DataType="data:DateGroupHeader">
|
||||||
<ItemContainer
|
<ItemContainer CanUserSelect="UserCannotSelect" IsHitTestVisible="False">
|
||||||
CanUserSelect="UserCannotSelect"
|
|
||||||
IsEnabled="False"
|
|
||||||
IsHitTestVisible="False">
|
|
||||||
<Grid Padding="12,8" Background="{ThemeResource CardBackgroundFillColorDefaultBrush}">
|
<Grid Padding="12,8" Background="{ThemeResource CardBackgroundFillColorDefaultBrush}">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
FontSize="14"
|
FontSize="14"
|
||||||
@@ -197,6 +194,7 @@
|
|||||||
|
|
||||||
<AutoSuggestBox
|
<AutoSuggestBox
|
||||||
x:Name="SearchBar"
|
x:Name="SearchBar"
|
||||||
|
Width="400"
|
||||||
Margin="2,0,-2,0"
|
Margin="2,0,-2,0"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
BorderBrush="Transparent"
|
BorderBrush="Transparent"
|
||||||
|
|||||||
@@ -98,10 +98,7 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
|||||||
SelectAllCheckbox.Unchecked += SelectAllCheckboxUnchecked;
|
SelectAllCheckbox.Unchecked += SelectAllCheckboxUnchecked;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SelectionModeToggleChecked(object sender, RoutedEventArgs e)
|
private void SelectionModeToggleChecked(object sender, RoutedEventArgs e) => ChangeSelectionMode(ItemsViewSelectionMode.Multiple);
|
||||||
{
|
|
||||||
ChangeSelectionMode(ListViewSelectionMode.Multiple);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FolderPivotChanged(object sender, SelectionChangedEventArgs e)
|
private void FolderPivotChanged(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
@@ -130,20 +127,19 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
|||||||
ViewModel.SelectedPivotChangedCommand.Execute(null);
|
ViewModel.SelectedPivotChangedCommand.Execute(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ChangeSelectionMode(ListViewSelectionMode mode)
|
private void ChangeSelectionMode(ItemsViewSelectionMode mode)
|
||||||
{
|
{
|
||||||
// ItemsView doesn't have a ChangeSelectionMode method like ListView
|
MailListView.SelectionMode = mode;
|
||||||
// The selection mode is set in XAML and doesn't need to change dynamically for ItemsView
|
|
||||||
|
|
||||||
if (ViewModel?.PivotFolders != null)
|
if (ViewModel?.PivotFolders != null)
|
||||||
{
|
{
|
||||||
ViewModel.PivotFolders.ForEach(a => a.IsExtendedMode = mode == ListViewSelectionMode.Extended);
|
ViewModel.PivotFolders.ForEach(a => a.IsExtendedMode = mode == ItemsViewSelectionMode.Extended);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SelectionModeToggleUnchecked(object sender, RoutedEventArgs e)
|
private void SelectionModeToggleUnchecked(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
ChangeSelectionMode(ListViewSelectionMode.Extended);
|
ChangeSelectionMode(ItemsViewSelectionMode.Extended);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SelectAllCheckboxChecked(object sender, RoutedEventArgs e)
|
private void SelectAllCheckboxChecked(object sender, RoutedEventArgs e)
|
||||||
@@ -306,46 +302,54 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
|||||||
{
|
{
|
||||||
if (message.SelectedMailViewModel == null) return;
|
if (message.SelectedMailViewModel == null) return;
|
||||||
|
|
||||||
//await ViewModel.ExecuteUIThread(async () =>
|
await ViewModel.ExecuteUIThread(async () =>
|
||||||
//{
|
{
|
||||||
// MailListView.ClearSelections(message.SelectedMailViewModel, true);
|
MailListView.ClearSelections(message.SelectedMailViewModel, true);
|
||||||
|
|
||||||
// int retriedSelectionCount = 0;
|
int retriedSelectionCount = 0;
|
||||||
//trySelection:
|
trySelection:
|
||||||
|
|
||||||
// bool isSelected = MailListView.SelectMailItemContainer(message.SelectedMailViewModel);
|
bool isSelected = MailListView.SelectMailItemContainer(message.SelectedMailViewModel);
|
||||||
|
|
||||||
// if (!isSelected)
|
if (!isSelected)
|
||||||
// {
|
{
|
||||||
// for (int i = retriedSelectionCount; i < 5;)
|
for (int i = retriedSelectionCount; i < 5;)
|
||||||
// {
|
{
|
||||||
// // Retry with delay until the container is realized. Max 1 second.
|
// Retry with delay until the container is realized. Max 1 second.
|
||||||
// await Task.Delay(200);
|
await Task.Delay(200);
|
||||||
|
|
||||||
// retriedSelectionCount++;
|
retriedSelectionCount++;
|
||||||
|
|
||||||
// goto trySelection;
|
goto trySelection;
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// // Automatically scroll to the selected item.
|
// Automatically scroll to the selected item.
|
||||||
// // This is useful when creating draft.
|
// This is useful when creating draft.
|
||||||
// if (isSelected && message.ScrollToItem)
|
|
||||||
// {
|
|
||||||
// var collectionContainer = ViewModel.MailCollection.GetMailItemContainer(message.SelectedMailViewModel.UniqueId);
|
|
||||||
|
|
||||||
// // Scroll to thread if available.
|
if (isSelected && message.ScrollToItem)
|
||||||
// if (collectionContainer.ThreadViewModel != null)
|
{
|
||||||
// {
|
var collectionContainer = ViewModel.MailCollection.GetMailItemContainer(message.SelectedMailViewModel.MailCopy.UniqueId);
|
||||||
// MailListView.StartBringItemIntoView(collectionContainer.ThreadViewModel, new BringIntoViewOptions());
|
|
||||||
// }
|
|
||||||
// else if (collectionContainer.ItemViewModel != null)
|
|
||||||
// {
|
|
||||||
// MailListView.StartBringItemIntoView(collectionContainer.ItemViewModel, new BringIntoViewOptions());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// }
|
// Scroll to thread if available.
|
||||||
//});
|
// Find the item index on the UI. This is different than ListView.
|
||||||
|
|
||||||
|
int scrollIndex = -1;
|
||||||
|
if (collectionContainer.ThreadViewModel != null)
|
||||||
|
{
|
||||||
|
scrollIndex = ViewModel.MailCollection.IndexOf(collectionContainer.ThreadViewModel);
|
||||||
|
}
|
||||||
|
else if (collectionContainer.ItemViewModel != null)
|
||||||
|
{
|
||||||
|
scrollIndex = ViewModel.MailCollection.IndexOf(collectionContainer.ItemViewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scrollIndex >= 0)
|
||||||
|
{
|
||||||
|
MailListView.StartBringItemIntoView(scrollIndex, new BringIntoViewOptions() { AnimationDesired = true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SearchBoxFocused(object sender, RoutedEventArgs e)
|
private void SearchBoxFocused(object sender, RoutedEventArgs e)
|
||||||
@@ -501,8 +505,6 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
|||||||
private void DeleteAllInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
|
private void DeleteAllInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
|
||||||
=> ViewModel.ExecuteMailOperationCommand.Execute(MailOperation.SoftDelete);
|
=> ViewModel.ExecuteMailOperationCommand.Execute(MailOperation.SoftDelete);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Animates the rotation using high-performance Composition APIs
|
/// Animates the rotation using high-performance Composition APIs
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -539,6 +541,26 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
|||||||
{
|
{
|
||||||
UpdateSelectAllButtonStatus();
|
UpdateSelectAllButtonStatus();
|
||||||
UpdateAdaptiveness();
|
UpdateAdaptiveness();
|
||||||
|
|
||||||
|
SynchronizeSelectedItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object _selectedItemsLock = new object();
|
||||||
|
private void SynchronizeSelectedItems()
|
||||||
|
{
|
||||||
|
lock (_selectedItemsLock)
|
||||||
|
{
|
||||||
|
ViewModel.SelectedItems.Clear();
|
||||||
|
|
||||||
|
foreach (var item in MailListView.SelectedItems)
|
||||||
|
{
|
||||||
|
if (item is MailItemViewModel mailItem)
|
||||||
|
{
|
||||||
|
if (!mailItem.IsSelected) mailItem.IsSelected = true;
|
||||||
|
if (!ViewModel.SelectedItems.Contains(mailItem)) ViewModel.SelectedItems.Add(mailItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThreadContainerRightTapped(object sender, RightTappedRoutedEventArgs e)
|
private void ThreadContainerRightTapped(object sender, RightTappedRoutedEventArgs e)
|
||||||
|
|||||||
@@ -151,8 +151,8 @@
|
|||||||
|
|
||||||
<!-- Backdrop Selection -->
|
<!-- Backdrop Selection -->
|
||||||
<controls:SettingsCard
|
<controls:SettingsCard
|
||||||
Description="Choose the backdrop effect for your app window"
|
Description="{x:Bind domain:Translator.SettingsWindowBackdrop_Description}"
|
||||||
Header="Window Backdrop"
|
Header="{x:Bind domain:Translator.SettingsWindowBackdrop_Title}"
|
||||||
IsEnabled="{x:Bind ViewModel.CanSelectElementTheme, Mode=OneWay}">
|
IsEnabled="{x:Bind ViewModel.CanSelectElementTheme, Mode=OneWay}">
|
||||||
<controls:SettingsCard.HeaderIcon>
|
<controls:SettingsCard.HeaderIcon>
|
||||||
<PathIcon Data="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z" />
|
<PathIcon Data="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z" />
|
||||||
@@ -172,6 +172,13 @@
|
|||||||
</controls:SettingsCard.Content>
|
</controls:SettingsCard.Content>
|
||||||
</controls:SettingsCard>
|
</controls:SettingsCard>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
x:Name="WindowBackdropSelectionDisabledTextBlock"
|
||||||
|
Margin="4,0,0,0"
|
||||||
|
x:Load="{x:Bind ViewModel.CanSelectElementTheme, Mode=OneWay, Converter={StaticResource ReverseBooleanConverter}}"
|
||||||
|
Foreground="{ThemeResource InfoBarWarningSeverityIconBackground}"
|
||||||
|
Text="{x:Bind domain:Translator.SettingsWindowBackdrop_Disabled}" />
|
||||||
|
|
||||||
<!-- Mail spacing. -->
|
<!-- Mail spacing. -->
|
||||||
<controls:SettingsExpander Description="{x:Bind domain:Translator.SettingsMailSpacing_Description}" Header="{x:Bind domain:Translator.SettingsMailSpacing_Title}">
|
<controls:SettingsExpander Description="{x:Bind domain:Translator.SettingsMailSpacing_Description}" Header="{x:Bind domain:Translator.SettingsMailSpacing_Title}">
|
||||||
<controls:SettingsExpander.HeaderIcon>
|
<controls:SettingsExpander.HeaderIcon>
|
||||||
|
|||||||
@@ -112,6 +112,7 @@
|
|||||||
<PackageReference Include="Sentry.Serilog" />
|
<PackageReference Include="Sentry.Serilog" />
|
||||||
<PackageReference Include="sqlite-net-pcl" />
|
<PackageReference Include="sqlite-net-pcl" />
|
||||||
<PackageReference Include="EmailValidation" />
|
<PackageReference Include="EmailValidation" />
|
||||||
|
<PackageReference Include="System.Drawing.Common" />
|
||||||
<PackageReference Include="WinUIEx" />
|
<PackageReference Include="WinUIEx" />
|
||||||
<PackageReference Include="H.NotifyIcon.WinUI" />
|
<PackageReference Include="H.NotifyIcon.WinUI" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
Reference in New Issue
Block a user