|
| 1 | +# WARP.md |
| 2 | + |
| 3 | +This file provides guidance to WARP (warp.dev) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +Sharpnado.Shadows is a .NET MAUI library (version 2.0+) that adds customizable shadows to any view across Android, iOS, Windows, and MacCatalyst platforms. The library enables multiple shadows per view with configurable properties (Color, Opacity, BlurRadius, Offset, CornerRadius) and supports Neumorphism design patterns. |
| 8 | + |
| 9 | +**Version 2.0.0** is a complete rewrite for .NET 9 MAUI with modern handler architecture. Legacy Xamarin.Forms support (v1.x) has been moved to a separate branch. |
| 10 | + |
| 11 | +## Repository Structure |
| 12 | + |
| 13 | +``` |
| 14 | +Sharpnado.Shadows/ |
| 15 | +├── Maui.Shadows/ # .NET 9 MAUI library (main project) |
| 16 | +│ ├── Shade.cs # Core shadow configuration (BindableObject) |
| 17 | +│ ├── Shadows.cs # Main ContentView with weak events |
| 18 | +│ ├── *Extension.cs # XAML markup extensions |
| 19 | +│ ├── MauiAppBuilderExtensions.cs # MAUI initialization (UseSharpnadoShadows) |
| 20 | +│ ├── InternalLogger.cs # Logging infrastructure |
| 21 | +│ ├── Maui.Shadows.csproj # Multi-targeted .NET 9 project |
| 22 | +│ └── Platforms/ # Platform-specific implementations |
| 23 | +│ ├── Android/ # Android handlers and controllers |
| 24 | +│ │ ├── ShadowsHandler.cs # MAUI ViewHandler |
| 25 | +│ │ ├── AndroidShadowsController.cs # Shadow lifecycle management |
| 26 | +│ │ ├── BitmapCache.cs # Global bitmap caching |
| 27 | +│ │ ├── GpuBlurHelper.cs # GPU-based blur (RenderScript/RenderEffect) |
| 28 | +│ │ └── StackBlurHelper.cs # CPU-based StackBlur fallback |
| 29 | +│ ├── iOS/ # iOS/MacCatalyst handlers |
| 30 | +│ │ ├── ShadowsHandler.cs # MAUI ViewHandler with UIView |
| 31 | +│ │ └── iOSShadowsController.cs # CALayer-based shadows |
| 32 | +│ └── Windows/ # Windows handlers |
| 33 | +│ ├── ShadowsHandler.cs # MAUI ViewHandler with Grid |
| 34 | +│ └── WindowsShadowsController.cs # WinUI 3 Composition API |
| 35 | +├── MauiSample/ # .NET MAUI sample application |
| 36 | +│ └── ShadowsSample.Maui/ # Sample app demonstrating library features |
| 37 | +├── Docs/ # Documentation assets (images, diagrams) |
| 38 | +└── README.md # Main documentation |
| 39 | +``` |
| 40 | + |
| 41 | +## Build Commands |
| 42 | + |
| 43 | +### Building the MAUI library (.NET 9) |
| 44 | + |
| 45 | +```bash |
| 46 | +# From repository root |
| 47 | +cd Maui.Shadows |
| 48 | + |
| 49 | +# Build all target frameworks |
| 50 | +dotnet build |
| 51 | + |
| 52 | +# Build for specific platform |
| 53 | +dotnet build -f net9.0-android |
| 54 | +dotnet build -f net9.0-ios |
| 55 | +dotnet build -f net9.0-maccatalyst |
| 56 | +dotnet build -f net9.0-windows10.0.19041.0 |
| 57 | + |
| 58 | +# Build in Release mode |
| 59 | +dotnet build -c Release |
| 60 | + |
| 61 | +# Clean build |
| 62 | +dotnet clean && dotnet build |
| 63 | +``` |
| 64 | + |
| 65 | +### Building the sample application |
| 66 | + |
| 67 | +```bash |
| 68 | +# From repository root |
| 69 | +cd MauiSample/ShadowsSample.Maui |
| 70 | + |
| 71 | +# Build for all platforms |
| 72 | +dotnet build |
| 73 | + |
| 74 | +# Run on specific platform |
| 75 | +dotnet build -t:Run -f net9.0-android |
| 76 | +dotnet build -t:Run -f net9.0-ios |
| 77 | +``` |
| 78 | + |
| 79 | +### Creating NuGet package |
| 80 | + |
| 81 | +```bash |
| 82 | +# From Maui.Shadows directory |
| 83 | +dotnet pack -c Release |
| 84 | + |
| 85 | +# Package will be created in bin/Release/ |
| 86 | +# Sharpnado.Maui.Shadows.2.0.0.nupkg |
| 87 | +``` |
| 88 | + |
| 89 | +### Running tests (if available) |
| 90 | + |
| 91 | +```bash |
| 92 | +# From repository root |
| 93 | +dotnet test |
| 94 | +``` |
| 95 | + |
| 96 | +## Architecture |
| 97 | + |
| 98 | +### Core Concepts |
| 99 | + |
| 100 | +**Shadows Component**: A .NET MAUI `ContentView` that wraps any view and applies one or more shadow layers (called "Shades"). |
| 101 | + |
| 102 | +**Shade**: Represents an individual shadow with five main properties: |
| 103 | +- `Point Offset`: Shadow offset from the source (X, Y) |
| 104 | +- `Color Color`: Shadow color (MAUI Color) |
| 105 | +- `double Opacity`: Shadow opacity (0.0 to 1.0) |
| 106 | +- `double BlurRadius`: Blur amount in pixels |
| 107 | +- `float CornerRadius`: Corner radius matching (set on Shadows, not individual Shade) |
| 108 | + |
| 109 | +**BlurType** (Android only): Choose rendering algorithm: |
| 110 | +- `Gpu` (default): Hardware-accelerated blur using RenderScript/RenderEffect |
| 111 | +- `StackBlur`: CPU-based StackBlur algorithm |
| 112 | + |
| 113 | +### Platform-Specific Implementations (.NET MAUI Handlers) |
| 114 | + |
| 115 | +Each platform uses modern MAUI handlers with optimized rendering: |
| 116 | + |
| 117 | +**Android** (`Maui.Shadows/Platforms/Android/`): |
| 118 | +- **Handler**: `ShadowsHandler` (ViewHandler<Shadows, FrameLayout>) |
| 119 | +- **Controller**: `AndroidShadowsController` manages shadow lifecycle |
| 120 | +- **Blur Rendering**: |
| 121 | + - `GpuBlurHelper`: RenderScript (API < 31) or RenderEffect (API 31+) |
| 122 | + - `StackBlurHelper`: CPU fallback for compatibility |
| 123 | +- **Caching**: `BitmapCache` singleton with hash-based bitmap reuse |
| 124 | +- **Memory Management**: WeakEvents, proper disposal, cached bitmaps |
| 125 | +- **Key Classes**: `ShadowsHandler`, `AndroidShadowsController`, `BitmapCache`, `GpuBlurHelper`, `StackBlurHelper` |
| 126 | + |
| 127 | +**iOS/MacCatalyst** (`Maui.Shadows/Platforms/iOS/`): |
| 128 | +- **Handler**: `ShadowsHandler` (ViewHandler<Shadows, UIView>) |
| 129 | +- **Controller**: `iOSShadowsController` manages CALayer sublayers |
| 130 | +- **Rendering**: Native `CALayer` with shadowColor, shadowOpacity, shadowRadius, shadowOffset |
| 131 | +- **Hardware Acceleration**: Full Core Animation GPU acceleration |
| 132 | +- **Memory**: UIView container with proper event unsubscription |
| 133 | +- **Key Classes**: `ShadowsHandler`, `iOSShadowsController`, `ShadeExtensions` |
| 134 | + |
| 135 | +**Windows** (`Maui.Shadows/Platforms/Windows/`): |
| 136 | +- **Handler**: `ShadowsHandler` (ViewHandler<Shadows, Grid>) |
| 137 | +- **Controller**: `WindowsShadowsController` manages SpriteVisual shadows |
| 138 | +- **Rendering**: WinUI 3 Composition API with DropShadow |
| 139 | +- **Structure**: Grid container with Canvas for shadows + content view |
| 140 | +- **Hardware Acceleration**: GPU-accelerated via Windows Composition |
| 141 | +- **Fixed**: Memory leak (SizeChanged event) from original UWP renderer |
| 142 | +- **Key Classes**: `ShadowsHandler`, `WindowsShadowsController` |
| 143 | + |
| 144 | +### Core Library Structure |
| 145 | + |
| 146 | +The .NET 9 MAUI library (`Maui.Shadows/`) contains: |
| 147 | + |
| 148 | +**Core Components**: |
| 149 | +- **Shadows.cs**: Main `ContentView` with weak event pattern for memory safety |
| 150 | +- **Shade.cs**: `BindableObject` with properties for shadow configuration |
| 151 | +- **MauiAppBuilderExtensions.cs**: Modern MAUI initialization with `UseSharpnadoShadows()` |
| 152 | +- **InternalLogger.cs**: Logging infrastructure with Action-based delegates |
| 153 | + |
| 154 | +**XAML Markup Extensions** (all implement `IMarkupExtension`): |
| 155 | +- **ImmutableShadesExtension**: Creates `ReadOnlyCollection<Shade>` for static resources |
| 156 | +- **ShadeStackExtension**: Creates `ObservableCollection<Shade>` for dynamic changes |
| 157 | +- **SingleShadeExtension**: Convenience for single shadow (returns `ReadOnlyCollection<Shade>`) |
| 158 | +- **NeumorphismShadesExtension**: Pre-configured two-shadow neumorphism effect |
| 159 | + |
| 160 | +**Platform Handlers** (see Platform-Specific Implementations section above) |
| 161 | + |
| 162 | +### Key Design Patterns |
| 163 | + |
| 164 | +**Shade Collections**: |
| 165 | +- `ReadOnlyCollection<Shade>`: Immutable, shades are cloned to prevent leaks. **Use for static ResourceDictionary definitions.** |
| 166 | +- `ObservableCollection<Shade>`: For dynamic addition/removal of shades during view lifetime |
| 167 | +- `IEnumerable<Shade>`: For modifying shade properties during view lifetime |
| 168 | + |
| 169 | +**Important**: Non-ReadOnly shades MUST be declared as transient instances, never as static resources. |
| 170 | + |
| 171 | +**Weak Events**: The library uses `ThomasLevesque.WeakEvent` pattern to prevent memory leaks: |
| 172 | +- `WeakCollectionChanged`: For shade collection changes |
| 173 | +- `WeakPropertyChanged`: For individual shade property changes |
| 174 | +- Prevents handlers from keeping views alive after disposal |
| 175 | + |
| 176 | +**MAUI Handler Pattern**: Each platform implements a `ViewHandler<Shadows, TNative>` that: |
| 177 | +1. **CreatePlatformView()**: Creates native container (FrameLayout/UIView/Grid) |
| 178 | +2. **ConnectHandler()**: Sets up shadow controller, subscribes to events |
| 179 | +3. **DisconnectHandler()**: Unsubscribes events, disposes controller, cleans up resources |
| 180 | +4. **PropertyMapper**: Maps `CornerRadius` and `Shades` property changes |
| 181 | +5. **Controller**: Separate class manages shadow lifecycle (create, update, destroy) |
| 182 | + |
| 183 | +**Memory Management**: |
| 184 | +- All handlers properly unsubscribe from events in `DisconnectHandler()` |
| 185 | +- Controllers dispose native resources (bitmaps, layers, visuals) |
| 186 | +- Android: Global bitmap cache with weak references |
| 187 | +- iOS: CALayer sublayers removed from superlayer |
| 188 | +- Windows: SpriteVisual and DropShadow properly disposed |
| 189 | + |
| 190 | +## Initialization Requirements (.NET MAUI) |
| 191 | + |
| 192 | +In `MauiProgram.cs`: |
| 193 | + |
| 194 | +```csharp |
| 195 | +using Sharpnado.Shades; |
| 196 | + |
| 197 | +public static class MauiProgram |
| 198 | +{ |
| 199 | + public static MauiApp CreateMauiApp() |
| 200 | + { |
| 201 | + var builder = MauiApp.CreateBuilder(); |
| 202 | + builder |
| 203 | + .UseMauiApp<App>() |
| 204 | + .ConfigureFonts(fonts => |
| 205 | + { |
| 206 | + fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); |
| 207 | + }) |
| 208 | + .UseSharpnadoShadows(loggerEnable: false); // Add this line |
| 209 | +
|
| 210 | + return builder.Build(); |
| 211 | + } |
| 212 | +} |
| 213 | +``` |
| 214 | + |
| 215 | +**That's it!** No platform-specific initialization required. Handlers are automatically registered via `MauiAppBuilderExtensions.UseSharpnadoShadows()`. |
| 216 | + |
| 217 | +**Logging**: Set `loggerEnable: true` to enable internal logging for debugging. |
| 218 | + |
| 219 | +## Performance Considerations |
| 220 | + |
| 221 | +### Android |
| 222 | +- **BlurType="Gpu"** (default): Best performance with hardware acceleration |
| 223 | + - API < 31: Uses RenderScript (deprecated but functional) |
| 224 | + - API 31+: Uses RenderEffect (modern API) |
| 225 | +- **BlurType="StackBlur"**: CPU fallback, use if GPU has issues on specific devices |
| 226 | +- **Bitmap Caching**: Shadows are cached globally by hash (color, size, blur) |
| 227 | +- **Avoid Animating**: Don't animate BlurRadius, Color, or Opacity (creates new bitmaps) |
| 228 | +- **View Animations OK**: Rotation, scale, translation don't recreate bitmaps |
| 229 | +- **Size Animations**: Temporarily set `Shades` to empty collection, animate, then restore |
| 230 | + |
| 231 | +### iOS/MacCatalyst |
| 232 | +- **CALayer Hardware Acceleration**: All shadow rendering is GPU-accelerated |
| 233 | +- **Animate Freely**: All properties (color, blur, opacity, offset) can be animated efficiently |
| 234 | +- **No Bitmap Overhead**: Native CALayer shadows don't use bitmaps |
| 235 | + |
| 236 | +### Windows |
| 237 | +- **Composition API**: Hardware-accelerated via WinUI 3 Composition |
| 238 | +- **SpriteVisual**: Lightweight drop shadows with GPU rendering |
| 239 | +- **Animate Freely**: All properties can be animated efficiently |
| 240 | + |
| 241 | +### General |
| 242 | +- **Weak Events**: Prevent memory leaks, no performance overhead |
| 243 | +- **Shade Collections**: Use `ReadOnlyCollection<Shade>` in ResourceDictionary for best performance |
| 244 | +- **Multiple Shadows**: Each shade adds rendering cost, but caching mitigates on Android |
| 245 | + |
| 246 | +## Dependencies |
| 247 | + |
| 248 | +- **Microsoft.Maui.Controls** (9.0.110): .NET MAUI framework |
| 249 | +- **ThomasLevesque.WeakEvent** (4.1.0): Weak event pattern implementation for memory leak prevention |
| 250 | + |
| 251 | +## Code Style |
| 252 | + |
| 253 | +- **Language Version**: `latest` (C# 12 features enabled) |
| 254 | +- **Nullable Reference Types**: Enabled throughout project |
| 255 | +- **ImplicitUsings**: Enabled for common namespaces |
| 256 | +- **Target Framework**: .NET 9 with multi-targeting |
| 257 | +- **Coding Patterns**: Modern C# (pattern matching, target-typed new, null-coalescing) |
| 258 | +- **Async/Await**: Used where appropriate (not in this project) |
| 259 | +- **Disposal**: Proper `IDisposable` implementation in all controllers |
| 260 | + |
| 261 | +## Namespace Convention |
| 262 | + |
| 263 | +**Assembly Name**: `Sharpnado.Maui.Shadows` |
| 264 | +**Root Namespace**: `Sharpnado.Shades` |
| 265 | +**XAML Namespace**: `xmlns:sh="clr-namespace:Sharpnado.Shades;assembly=Sharpnado.Maui.Shadows"` |
| 266 | + |
| 267 | +This is intentional - "Shades" is the conceptual name (a shadow is made of multiple shades), while "Shadows" is the product name. |
| 268 | + |
| 269 | +## Version History |
| 270 | + |
| 271 | +### Version 2.0.0 (Current - .NET MAUI) |
| 272 | +- Complete rewrite for .NET 9 MAUI |
| 273 | +- Modern ViewHandler architecture for all platforms |
| 274 | +- Android: New `BlurType` property (Gpu/StackBlur) |
| 275 | +- Fixed memory leaks (Windows SizeChanged event, proper event unsubscription) |
| 276 | +- Enhanced null safety with nullable reference types |
| 277 | +- Improved logging and debugging |
| 278 | +- **Breaking Changes**: See README.md for migration guide |
| 279 | + |
| 280 | +### Version 1.x (Legacy - Xamarin.Forms) |
| 281 | +- Original Xamarin.Forms implementation |
| 282 | +- Supported: Android, iOS, UWP, Tizen |
| 283 | +- Deprecated: No longer maintained |
| 284 | +- Code moved to separate branch |
| 285 | + |
| 286 | +## NuGet Package |
| 287 | + |
| 288 | +- **Package ID**: `Sharpnado.Maui.Shadows` |
| 289 | +- **Version**: 2.0.0 |
| 290 | +- **License**: MIT |
| 291 | +- **Target Frameworks**: net9.0, net9.0-android, net9.0-ios, net9.0-maccatalyst, net9.0-windows10.0.19041.0 |
| 292 | +- **Minimum OS Versions**: |
| 293 | + - Android: API 21 (Android 5.0) |
| 294 | + - iOS: 12.2 |
| 295 | + - MacCatalyst: 15.0 |
| 296 | + - Windows: 10.0.17763.0 |
0 commit comments