diff --git a/API_Reference.md b/API_Reference.md
new file mode 100644
index 0000000..e8ad15a
--- /dev/null
+++ b/API_Reference.md
@@ -0,0 +1,807 @@
+# MarketAlly.TouchEffect.Maui API Reference
+
+Complete API documentation for MarketAlly.TouchEffect.Maui v2.0.0.
+
+## Table of Contents
+
+- [TouchEffect Class](#toucheffect-class)
+ - [Attached Properties](#attached-properties)
+ - [Events](#events)
+ - [Static Methods](#static-methods)
+- [TouchBehavior Class](#touchbehavior-class)
+- [TouchEffectBuilder Class](#toucheffectbuilder-class)
+- [TouchEffectPresets Class](#toucheffectpresets-class)
+- [Enumerations](#enumerations)
+- [Interfaces](#interfaces)
+- [Constants](#constants)
+
+---
+
+## TouchEffect Class
+
+The core class that provides touch and hover visual feedback for any `VisualElement`.
+
+**Namespace:** `MarketAlly.TouchEffect.Maui`
+
+**Inheritance:** `RoutingEffect`
+
+### Attached Properties
+
+#### State Properties
+
+| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+| `IsAvailable` | `bool` | `true` | Enables or disables the touch effect. When `false`, no touch feedback occurs. |
+| `ShouldMakeChildrenInputTransparent` | `bool` | `true` | When `true`, child elements become input-transparent to allow touch to pass through. |
+| `Status` | `TouchStatus` | `Completed` | **Read-only.** Current touch status (Started, Completed, Canceled). |
+| `State` | `TouchState` | `Normal` | **Read-only.** Current touch state (Normal, Pressed). |
+| `InteractionStatus` | `TouchInteractionStatus` | `Completed` | **Read-only.** Current interaction status. |
+| `HoverStatus` | `HoverStatus` | `Exited` | **Read-only.** Current hover status (Entered, Exited). |
+| `HoverState` | `HoverState` | `Normal` | **Read-only.** Current hover state (Normal, Hovered). |
+
+**Usage:**
+```xml
+
+
+
+```
+
+#### Command Properties
+
+| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+| `Command` | `ICommand` | `null` | Command executed when touch completes successfully. |
+| `CommandParameter` | `object` | `null` | Parameter passed to the `Command`. |
+| `LongPressCommand` | `ICommand` | `null` | Command executed after long press duration elapses. |
+| `LongPressCommandParameter` | `object` | `null` | Parameter for the `LongPressCommand`. Falls back to `CommandParameter` if null. |
+| `LongPressDuration` | `int` | `500` | Duration in milliseconds before `LongPressCommand` executes. |
+
+**Usage:**
+```xml
+
+
+
+```
+
+#### Background Color Properties
+
+| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+| `NormalBackgroundColor` | `Color` | `Default` | Background color in normal state. |
+| `HoveredBackgroundColor` | `Color` | `Default` | Background color when hovered (desktop platforms). |
+| `PressedBackgroundColor` | `Color` | `Default` | Background color when pressed. |
+
+**Usage:**
+```xml
+
+
+
+```
+
+#### Opacity Properties
+
+| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+| `NormalOpacity` | `double` | `1.0` | Opacity in normal state. |
+| `HoveredOpacity` | `double` | `1.0` | Opacity when hovered. |
+| `PressedOpacity` | `double` | `1.0` | Opacity when pressed. |
+
+**Usage:**
+```xml
+
+
+
+```
+
+#### Scale Properties
+
+| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+| `NormalScale` | `double` | `1.0` | Scale in normal state. |
+| `HoveredScale` | `double` | `1.0` | Scale when hovered. |
+| `PressedScale` | `double` | `1.0` | Scale when pressed. |
+
+**Usage:**
+```xml
+
+
+
+```
+
+#### Translation Properties
+
+| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+| `NormalTranslationX` | `double` | `0.0` | X translation in normal state. |
+| `HoveredTranslationX` | `double` | `0.0` | X translation when hovered. |
+| `PressedTranslationX` | `double` | `0.0` | X translation when pressed. |
+| `NormalTranslationY` | `double` | `0.0` | Y translation in normal state. |
+| `HoveredTranslationY` | `double` | `0.0` | Y translation when hovered. |
+| `PressedTranslationY` | `double` | `0.0` | Y translation when pressed. |
+
+**Usage:**
+```xml
+
+
+
+```
+
+#### Rotation Properties
+
+| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+| `NormalRotation` | `double` | `0.0` | Z-axis rotation in normal state (degrees). |
+| `HoveredRotation` | `double` | `0.0` | Z-axis rotation when hovered. |
+| `PressedRotation` | `double` | `0.0` | Z-axis rotation when pressed. |
+| `NormalRotationX` | `double` | `0.0` | X-axis rotation in normal state. |
+| `HoveredRotationX` | `double` | `0.0` | X-axis rotation when hovered. |
+| `PressedRotationX` | `double` | `0.0` | X-axis rotation when pressed. |
+| `NormalRotationY` | `double` | `0.0` | Y-axis rotation in normal state. |
+| `HoveredRotationY` | `double` | `0.0` | Y-axis rotation when hovered. |
+| `PressedRotationY` | `double` | `0.0` | Y-axis rotation when pressed. |
+
+**Usage:**
+```xml
+
+
+
+```
+
+#### Animation Properties
+
+| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+| `AnimationDuration` | `int` | `0` | Default animation duration in milliseconds. |
+| `AnimationEasing` | `Easing` | `null` | Default easing function for animations. |
+| `PressedAnimationDuration` | `int` | `0` | Animation duration for pressed state transitions. |
+| `PressedAnimationEasing` | `Easing` | `null` | Easing for pressed state transitions. |
+| `NormalAnimationDuration` | `int` | `0` | Animation duration for returning to normal state. |
+| `NormalAnimationEasing` | `Easing` | `null` | Easing for normal state transitions. |
+| `HoveredAnimationDuration` | `int` | `0` | Animation duration for hover state transitions. |
+| `HoveredAnimationEasing` | `Easing` | `null` | Easing for hover state transitions. |
+| `PulseCount` | `int` | `0` | Number of pulse repetitions. Use `-1` for infinite. |
+
+**Usage:**
+```xml
+
+
+
+```
+
+#### Toggle Properties
+
+| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+| `IsToggled` | `bool?` | `null` | Toggle state. `null` disables toggle behavior. Supports two-way binding. |
+| `DisallowTouchThreshold` | `int` | `0` | Movement threshold in pixels before touch is canceled. |
+
+**Usage:**
+```xml
+
+
+
+```
+
+#### Native Animation Properties
+
+| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+| `NativeAnimation` | `bool` | `false` | Enables platform-specific animations (Android ripple, iOS highlight). |
+| `NativeAnimationColor` | `Color` | `Default` | Color for native animation effects. |
+| `NativeAnimationRadius` | `int` | `-1` | Radius for native ripple effect. `-1` uses default. |
+| `NativeAnimationShadowRadius` | `int` | `-1` | Shadow radius for native effects. |
+| `NativeAnimationBorderless` | `bool` | `false` | When `true`, ripple extends beyond view bounds. |
+
+**Usage:**
+```xml
+
+
+
+```
+
+#### Background Image Properties
+
+| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+| `NormalBackgroundImageSource` | `ImageSource` | `null` | Background image in normal state. |
+| `HoveredBackgroundImageSource` | `ImageSource` | `null` | Background image when hovered. |
+| `PressedBackgroundImageSource` | `ImageSource` | `null` | Background image when pressed. |
+| `BackgroundImageAspect` | `Aspect` | `AspectFill` | Default aspect ratio for background images. |
+| `NormalBackgroundImageAspect` | `Aspect` | `AspectFill` | Aspect ratio for normal state image. |
+| `HoveredBackgroundImageAspect` | `Aspect` | `AspectFill` | Aspect ratio for hovered state image. |
+| `PressedBackgroundImageAspect` | `Aspect` | `AspectFill` | Aspect ratio for pressed state image. |
+| `ShouldSetImageOnAnimationEnd` | `bool` | `false` | When `true`, image changes occur after animation completes. |
+
+### Events
+
+| Event | EventArgs | Description |
+|-------|-----------|-------------|
+| `StatusChanged` | `TouchStatusChangedEventArgs` | Fired when touch status changes. |
+| `StateChanged` | `TouchStateChangedEventArgs` | Fired when touch state changes. |
+| `InteractionStatusChanged` | `TouchInteractionStatusChangedEventArgs` | Fired when interaction status changes. |
+| `HoverStatusChanged` | `HoverStatusChangedEventArgs` | Fired when hover status changes. |
+| `HoverStateChanged` | `HoverStateChangedEventArgs` | Fired when hover state changes. |
+| `Completed` | `TouchCompletedEventArgs` | Fired when touch completes successfully. |
+| `LongPressCompleted` | `LongPressCompletedEventArgs` | Fired when long press completes. |
+
+**Usage in Code-Behind:**
+```csharp
+public partial class MyPage : ContentPage
+{
+ protected override void OnAppearing()
+ {
+ base.OnAppearing();
+
+ // Get the effect instance
+ var effect = MyView.Effects.OfType().FirstOrDefault();
+ if (effect != null)
+ {
+ effect.Completed += OnTouchCompleted;
+ effect.StateChanged += OnStateChanged;
+ }
+ }
+
+ private void OnTouchCompleted(object sender, TouchCompletedEventArgs e)
+ {
+ Debug.WriteLine($"Touch completed with parameter: {e.Parameter}");
+ }
+
+ private void OnStateChanged(object sender, TouchStateChangedEventArgs e)
+ {
+ Debug.WriteLine($"State changed to: {e.State}");
+ }
+}
+```
+
+### Static Methods
+
+#### SetLogger
+
+```csharp
+public static void SetLogger(ITouchEffectLogger? logger)
+```
+
+Sets the logger instance for all TouchEffect operations.
+
+**Parameters:**
+- `logger`: The logger implementation. Pass `null` to disable logging.
+
+**Usage:**
+```csharp
+// Enable default console logging
+TouchEffect.SetLogger(new DefaultTouchEffectLogger());
+
+// Disable logging
+TouchEffect.SetLogger(null);
+
+// Custom logging
+TouchEffect.SetLogger(new MyCustomLogger());
+```
+
+---
+
+## TouchBehavior Class
+
+**NEW in v2.0.0**
+
+A Behavior-based alternative to `TouchEffect` for attaching touch feedback to elements. MAUI is moving toward Behaviors over Effects, making this the preferred modern API.
+
+**Namespace:** `MarketAlly.TouchEffect.Maui`
+
+**Inheritance:** `Behavior`
+
+### Properties
+
+| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+| `Command` | `ICommand` | `null` | Command executed on tap. |
+| `CommandParameter` | `object` | `null` | Parameter for the command. |
+| `LongPressCommand` | `ICommand` | `null` | Command for long press. |
+| `LongPressCommandParameter` | `object` | `null` | Parameter for long press command. |
+| `LongPressDuration` | `int` | `500` | Long press duration in ms. |
+| `PressedScale` | `double` | `1.0` | Scale when pressed. |
+| `PressedOpacity` | `double` | `1.0` | Opacity when pressed. |
+| `PressedBackgroundColor` | `Color` | `null` | Background color when pressed. |
+| `HoveredScale` | `double` | `1.0` | Scale when hovered. |
+| `HoveredOpacity` | `double` | `1.0` | Opacity when hovered. |
+| `HoveredBackgroundColor` | `Color` | `null` | Background color when hovered. |
+| `NormalScale` | `double` | `1.0` | Scale in normal state. |
+| `NormalOpacity` | `double` | `1.0` | Opacity in normal state. |
+| `NormalBackgroundColor` | `Color` | `null` | Background color in normal state. |
+| `AnimationDuration` | `int` | `100` | Animation duration in ms. |
+| `AnimationEasing` | `Easing` | `null` | Animation easing function. |
+| `NativeAnimation` | `bool` | `false` | Enable native platform animations. |
+| `NativeAnimationColor` | `Color` | `null` | Color for native animations. |
+| `IsToggled` | `bool?` | `null` | Toggle state (two-way bindable). |
+| `IsAvailable` | `bool` | `true` | Enable/disable the behavior. |
+
+### Usage
+
+**XAML:**
+```xml
+
+```
+
+**C#:**
+```csharp
+var button = new Button { Text = "Click Me" };
+button.Behaviors.Add(new TouchBehavior
+{
+ PressedScale = 0.95,
+ PressedOpacity = 0.8,
+ AnimationDuration = 100,
+ Command = viewModel.TapCommand
+});
+```
+
+---
+
+## TouchEffectBuilder Class
+
+Fluent builder for configuring TouchEffect with a clean, chainable API.
+
+**Namespace:** `MarketAlly.TouchEffect.Maui`
+
+### Static Methods
+
+#### For
+
+```csharp
+public static TouchEffectBuilder For(VisualElement element)
+```
+
+Creates a builder for the specified element.
+
+### Instance Methods
+
+#### Command Configuration
+
+| Method | Description |
+|--------|-------------|
+| `WithCommand(ICommand command, object? parameter = null)` | Sets the tap command. |
+| `WithLongPressCommand(ICommand command, object? parameter = null)` | Sets the long press command. |
+| `WithLongPressDuration(int milliseconds)` | Sets long press duration. |
+
+#### Visual Configuration
+
+| Method | Description |
+|--------|-------------|
+| `WithPressedState(double? opacity, double? scale, Color? backgroundColor)` | Configures pressed state. |
+| `WithHoveredState(double? opacity, double? scale, Color? backgroundColor)` | Configures hovered state. |
+| `WithNormalState(double? opacity, double? scale, Color? backgroundColor)` | Configures normal state. |
+| `WithPressedOpacity(double opacity)` | Sets pressed opacity. |
+| `WithPressedScale(double scale)` | Sets pressed scale. |
+| `WithPressedBackgroundColor(Color color)` | Sets pressed background color. |
+| `WithHoveredScale(double scale)` | Sets hovered scale. |
+
+#### Animation Configuration
+
+| Method | Description |
+|--------|-------------|
+| `WithAnimation(int duration, Easing? easing = null)` | Sets animation duration and easing. |
+| `WithPressedAnimation(int duration, Easing? easing = null)` | Sets pressed state animation. |
+| `WithHoveredAnimation(int duration, Easing? easing = null)` | Sets hovered state animation. |
+| `WithPulse(int count)` | Sets pulse count. |
+| `WithInfinitePulse()` | Enables infinite pulsing. |
+
+#### Native Animation Configuration
+
+| Method | Description |
+|--------|-------------|
+| `WithNativeAnimation(Color? color = null, int radius = -1)` | Enables native platform animations. |
+
+#### Toggle Configuration
+
+| Method | Description |
+|--------|-------------|
+| `AsToggle(bool initialState = false)` | Enables toggle behavior. |
+
+#### Other Configuration
+
+| Method | Description |
+|--------|-------------|
+| `WithDisallowThreshold(int pixels)` | Sets movement cancellation threshold. |
+| `Disable()` | Disables the effect. |
+
+#### Preset Methods
+
+| Method | Description |
+|--------|-------------|
+| `AsButton()` | Applies standard button preset. |
+| `AsCard()` | Applies card preset with scale effect. |
+| `AsListItem()` | Applies list item preset with background color. |
+| `AsFloatingActionButton()` | Applies FAB preset with native animation. |
+
+#### Build Methods
+
+| Method | Returns | Description |
+|--------|---------|-------------|
+| `Build()` | `VisualElement` | Applies configuration and returns the element. |
+| `Apply()` | `TouchEffectBuilder` | Applies configuration and returns builder for chaining. |
+
+### Usage
+
+```csharp
+// Basic usage
+var button = TouchEffectBuilder.For(myFrame)
+ .WithPressedScale(0.95)
+ .WithPressedOpacity(0.8)
+ .WithAnimation(100, Easing.CubicOut)
+ .WithCommand(tapCommand)
+ .Build();
+
+// Using presets
+var card = TouchEffectBuilder.For(myCard)
+ .AsCard()
+ .WithCommand(selectCommand)
+ .Build();
+
+// Complex configuration
+var interactive = TouchEffectBuilder.For(element)
+ .WithPressedState(opacity: 0.7, scale: 0.95, backgroundColor: Colors.LightGray)
+ .WithHoveredState(opacity: 1.0, scale: 1.02, backgroundColor: Colors.White)
+ .WithAnimation(150, Easing.CubicInOut)
+ .WithLongPressCommand(contextMenuCommand)
+ .WithLongPressDuration(800)
+ .AsToggle()
+ .Build();
+```
+
+### Extension Methods
+
+```csharp
+// Create a builder for any VisualElement
+public static TouchEffectBuilder ConfigureTouchEffect(this VisualElement element)
+
+// Quick presets
+public static VisualElement WithButtonEffect(this VisualElement element, ICommand? command = null)
+public static VisualElement WithCardEffect(this VisualElement element, ICommand? command = null)
+```
+
+---
+
+## TouchEffectPresets Class
+
+Predefined TouchEffect configurations for common UI patterns.
+
+**Namespace:** `MarketAlly.TouchEffect.Maui`
+
+### Button Presets
+
+| Method | Description |
+|--------|-------------|
+| `Button.Apply(element)` | Standard button with opacity feedback (0.7 pressed). |
+| `Button.ApplyPrimary(element)` | Primary button with scale (0.95) and opacity (0.8). |
+| `Button.ApplySecondary(element)` | Secondary button with subtle opacity (0.6). |
+| `Button.ApplyText(element)` | Text button with minimal feedback (0.5 opacity, instant). |
+
+### Card Presets
+
+| Method | Description |
+|--------|-------------|
+| `Card.Apply(element)` | Standard card with subtle scale (0.97). |
+| `Card.ApplyElevated(element)` | Elevated card with scale (0.95), opacity (0.9), hover scale (1.02). |
+| `Card.ApplyInteractive(element)` | Interactive card with hover background highlight. |
+
+### ListItem Presets
+
+| Method | Description |
+|--------|-------------|
+| `ListItem.Apply(element)` | Standard list item with background highlight. |
+| `ListItem.ApplySelectable(element)` | Selectable item with toggle behavior. |
+| `ListItem.ApplySwipeable(element)` | Swipeable item with scale feedback. |
+
+### IconButton Presets
+
+| Method | Description |
+|--------|-------------|
+| `IconButton.Apply(element)` | Standard icon button with scale (0.85), spring animation. |
+| `IconButton.ApplyFloatingAction(element)` | FAB with scale (0.9), native animation. |
+| `IconButton.ApplyToolbar(element)` | Toolbar icon with subtle opacity (0.5). |
+
+### Toggle Presets
+
+| Method | Description |
+|--------|-------------|
+| `Toggle.Apply(element)` | Standard toggle with scale effect. |
+| `Toggle.ApplyCheckbox(element)` | Checkbox-style with bounce animation. |
+
+### Image Presets
+
+| Method | Description |
+|--------|-------------|
+| `Image.ApplyThumbnail(element)` | Thumbnail with scale (0.95 pressed, 1.05 hover). |
+| `Image.ApplyGallery(element)` | Gallery image with zoom effect (1.1 hover). |
+| `Image.ApplyAvatar(element)` | Avatar with subtle feedback. |
+
+### Native Presets
+
+| Method | Description |
+|--------|-------------|
+| `Native.ApplyRipple(element, color?)` | Android ripple effect. |
+| `Native.ApplyHaptic(element)` | iOS-style haptic feedback. |
+
+### Special Presets
+
+| Method | Description |
+|--------|-------------|
+| `Special.ApplyPulse(element, count)` | Pulse effect with repeating animation. |
+| `Special.ApplyBounce(element)` | Bounce effect with spring animation. |
+| `Special.ApplyShake(element)` | Shake effect with rotation. |
+| `Special.ApplyDisabled(element)` | Disabled state with no interaction. |
+
+### Extension Methods
+
+```csharp
+element.WithButtonPreset();
+element.WithCardPreset();
+element.WithListItemPreset();
+element.WithIconButtonPreset();
+element.WithNativeEffect(color?);
+```
+
+---
+
+## Enumerations
+
+### TouchStatus
+
+**Namespace:** `MarketAlly.TouchEffect.Maui.Enums`
+
+| Value | Description |
+|-------|-------------|
+| `Started` | Touch has started. |
+| `Completed` | Touch completed successfully. |
+| `Canceled` | Touch was canceled (moved outside, interrupted). |
+
+### TouchState
+
+**Namespace:** `MarketAlly.TouchEffect.Maui.Enums`
+
+| Value | Description |
+|-------|-------------|
+| `Normal` | Element is in normal state. |
+| `Pressed` | Element is being pressed. |
+
+### TouchInteractionStatus
+
+**Namespace:** `MarketAlly.TouchEffect.Maui.Enums`
+
+| Value | Description |
+|-------|-------------|
+| `Started` | User interaction started. |
+| `Completed` | User interaction completed. |
+
+### HoverStatus
+
+**Namespace:** `MarketAlly.TouchEffect.Maui.Enums`
+
+| Value | Description |
+|-------|-------------|
+| `Entered` | Pointer entered the element. |
+| `Exited` | Pointer exited the element. |
+
+### HoverState
+
+**Namespace:** `MarketAlly.TouchEffect.Maui.Enums`
+
+| Value | Description |
+|-------|-------------|
+| `Normal` | Element is not being hovered. |
+| `Hovered` | Element is being hovered. |
+
+---
+
+## Interfaces
+
+### ITouchEffectLogger
+
+**Namespace:** `MarketAlly.TouchEffect.Maui.Interfaces`
+
+Interface for custom logging implementations.
+
+```csharp
+public interface ITouchEffectLogger
+{
+ void LogError(Exception ex, string context, string? additionalInfo = null);
+ void LogWarning(string message, string context);
+ void LogInfo(string message, string context);
+}
+```
+
+### Built-in Implementations
+
+#### DefaultTouchEffectLogger
+
+Logs to `Debug.WriteLine` with formatted output.
+
+```csharp
+TouchEffect.SetLogger(new DefaultTouchEffectLogger());
+```
+
+#### NullTouchEffectLogger
+
+No-op logger that discards all messages. Used by default.
+
+```csharp
+// Singleton instance
+NullTouchEffectLogger.Instance
+```
+
+---
+
+## Constants
+
+### TouchEffectConstants
+
+**Namespace:** `MarketAlly.TouchEffect.Maui`
+
+#### Defaults
+
+| Constant | Value | Description |
+|----------|-------|-------------|
+| `LongPressDuration` | `500` | Default long press duration (ms). |
+| `Opacity` | `1.0` | Default opacity. |
+| `Scale` | `1.0` | Default scale. |
+| `TranslationX` | `0.0` | Default X translation. |
+| `TranslationY` | `0.0` | Default Y translation. |
+| `Rotation` | `0.0` | Default rotation. |
+| `PulseCount` | `0` | Default pulse count. |
+| `DisallowTouchThreshold` | `0` | Default movement threshold. |
+| `NativeAnimationRadius` | `-1` | Default native animation radius. |
+| `NativeAnimationShadowRadius` | `-1` | Default shadow radius. |
+
+#### Animation
+
+| Constant | Value | Description |
+|----------|-------|-------------|
+| `DefaultDuration` | `0` | Default animation duration. |
+| `DefaultProgressDelay` | `10` | Delay between animation frames. |
+| `TargetFrameRate` | `60` | Target animation frame rate. |
+
+#### PresetDurations
+
+| Constant | Value | Description |
+|----------|-------|-------------|
+| `Instant` | `0` | No animation. |
+| `VeryFast` | `50` | Very fast animations (ms). |
+| `Fast` | `100` | Fast animations (ms). |
+| `Normal` | `200` | Normal animations (ms). |
+| `Slow` | `300` | Slow animations (ms). |
+
+#### VisualStates
+
+| Constant | Value |
+|----------|-------|
+| `Unpressed` | `"Unpressed"` |
+| `Pressed` | `"Pressed"` |
+| `Hovered` | `"Hovered"` |
+
+#### Platform.Android
+
+| Constant | Value | Description |
+|----------|-------|-------------|
+| `DefaultRippleColor` | `128` | Default ripple RGB value. |
+| `DefaultRippleAlpha` | `80` | Default ripple alpha. |
+| `MinRippleRadius` | `48` | Minimum ripple radius (dp). |
+
+#### Platform.iOS
+
+| Constant | Value | Description |
+|----------|-------|-------------|
+| `HighlightAlpha` | `0.5f` | Default highlight alpha. |
+
+---
+
+## Event Args Classes
+
+### TouchStatusChangedEventArgs
+
+```csharp
+public class TouchStatusChangedEventArgs : EventArgs
+{
+ public TouchStatus Status { get; }
+}
+```
+
+### TouchStateChangedEventArgs
+
+```csharp
+public class TouchStateChangedEventArgs : EventArgs
+{
+ public TouchState State { get; }
+}
+```
+
+### TouchInteractionStatusChangedEventArgs
+
+```csharp
+public class TouchInteractionStatusChangedEventArgs : EventArgs
+{
+ public TouchInteractionStatus InteractionStatus { get; }
+}
+```
+
+### HoverStatusChangedEventArgs
+
+```csharp
+public class HoverStatusChangedEventArgs : EventArgs
+{
+ public HoverStatus Status { get; }
+}
+```
+
+### HoverStateChangedEventArgs
+
+```csharp
+public class HoverStateChangedEventArgs : EventArgs
+{
+ public HoverState State { get; }
+}
+```
+
+### TouchCompletedEventArgs
+
+```csharp
+public class TouchCompletedEventArgs : EventArgs
+{
+ public object? Parameter { get; }
+}
+```
+
+### LongPressCompletedEventArgs
+
+```csharp
+public class LongPressCompletedEventArgs : EventArgs
+{
+ public object? Parameter { get; }
+}
+```
+
+---
+
+## Migration Guide
+
+### From v1.x to v2.0
+
+1. **Update package reference** to version 2.0.0
+2. **Update target framework** to .NET 10 if needed
+3. **Optional: Use TouchBehavior** instead of attached properties for new code
+4. **Optional: Configure logging** using `TouchEffect.SetLogger()`
+
+No breaking changes - all v1.x code continues to work.
+
+### From Xamarin TouchEffect
+
+1. Update namespace from `Xamarin.CommunityToolkit.Effects` to `MarketAlly.TouchEffect.Maui`
+2. Replace `TouchEff` with `TouchEffect` in XAML
+3. Update `UseMauiApp()` to include `.UseMauiTouchEffect()`
+
+---
+
+*Last updated: January 2025 | Version 2.0.0*
diff --git a/README.md b/README.md
index bf630cd..dc2c62b 100644
--- a/README.md
+++ b/README.md
@@ -3,26 +3,38 @@
[](https://www.nuget.org/packages/MarketAlly.TouchEffect.Maui)
[](https://www.nuget.org/packages/MarketAlly.TouchEffect.Maui)
[](https://opensource.org/licenses/MIT)
+[](https://dotnet.microsoft.com/)
A comprehensive touch effect library for .NET MAUI applications by **MarketAlly**, providing rich interaction feedback and animations across all platforms. MarketAlly.TouchEffect.Maui brings advanced touch handling, hover states, long press detection, and smooth animations to any MAUI view.
+## What's New in v2.0.0
+
+- **.NET 10 Support** - Updated to target .NET 10 with latest MAUI
+- **TouchBehavior** - New Behavior-based API as modern alternative to Effects
+- **Thread-Safe Architecture** - Complete thread-safety overhaul with proper synchronization
+- **Enhanced Logging** - Integrated logging throughout with `ITouchEffectLogger` interface
+- **Improved Code Organization** - TouchEffect split into partial classes for maintainability
+- **Bug Fixes** - Fixed `ForceUpdateStateWithoutAnimation` to actually disable animations
+- **Performance** - Replaced LINQ with for-loops in hot paths to reduce allocations
+
## Features
-### 🎯 Core Capabilities
+### Core Capabilities
- **Universal Touch Feedback** - Consistent touch interactions across iOS, Android, and Windows
- **50+ Customizable Properties** - Fine-grained control over every aspect of the touch experience
- **Hardware-Accelerated Animations** - Smooth, performant transitions using platform-native acceleration
- **Accessibility First** - Full keyboard, screen reader, and assistive technology support
- **Memory Efficient** - WeakEventManager pattern prevents memory leaks
+- **Thread-Safe** - Proper synchronization for animation state management
-### 🎨 Visual Effects
+### Visual Effects
- **Opacity Animations** - Fade effects on touch with customizable values
- **Scale Transformations** - Grow or shrink elements during interaction
- **Color Transitions** - Dynamic background color changes for different states
- **Translation & Rotation** - Move and rotate elements during touch
- **Native Platform Effects** - Android ripple effects and iOS haptic feedback
-### 🔧 Advanced Features
+### Advanced Features
- **Long Press Detection** - Configurable duration with separate command binding
- **Hover Support** - Mouse and stylus hover states on supported platforms
- **Toggle Behavior** - Switch-like functionality with persistent state
@@ -36,24 +48,24 @@ A comprehensive touch effect library for .NET MAUI applications by **MarketAlly*
| iOS | 13.0+ | Full support with haptic feedback |
| Android | API 24+ | Full support with native ripple effects |
| Windows | 10.0.17763+ | Full support with WinUI 3 animations |
-| Mac Catalyst| ❌ | Not currently supported |
-| Tizen | ❌ | Not currently supported |
+| Mac Catalyst| - | Not currently supported |
+| Tizen | - | Not currently supported |
## Installation
### Package Manager
```bash
-Install-Package MarketAlly.TouchEffect.Maui -Version 1.0.0
+Install-Package MarketAlly.TouchEffect.Maui -Version 2.0.0
```
### .NET CLI
```bash
-dotnet add package MarketAlly.TouchEffect.Maui --version 1.0.0
+dotnet add package MarketAlly.TouchEffect.Maui --version 2.0.0
```
### PackageReference
```xml
-
+
```
## Quick Start
@@ -85,7 +97,7 @@ public static class MauiProgram
### 2. Add Touch Effects to Your Views
-#### XAML Approach
+#### XAML Approach (Attached Properties)
```xml
@@ -124,7 +136,28 @@ public static class MauiProgram
```
-#### 🆕 Fluent Builder Approach (New!)
+#### NEW: TouchBehavior Approach (v2.0.0)
+
+```xml
+
+
+
+
+
+```
+
+#### Fluent Builder Approach
```csharp
using MarketAlly.TouchEffect.Maui;
@@ -148,7 +181,7 @@ var listItem = new StackLayout()
.WithListItemPreset();
```
-#### 🆕 Using Presets (New!)
+#### Using Presets
```csharp
// Apply common UI patterns instantly
@@ -163,55 +196,34 @@ myCard.WithCardPreset();
myListItem.WithListItemPreset();
```
-## 🆕 New Features in v1.0.0
+## Logging Configuration
-### Fluent Builder Pattern
-Configure touch effects with a clean, chainable API:
+Configure logging to debug touch effect issues:
```csharp
-element.ConfigureTouchEffect()
- .WithPressedScale(0.95)
- .WithPressedOpacity(0.7)
- .WithAnimation(100, Easing.CubicOut)
- .WithCommand(tapCommand)
- .Build();
-```
+// Use the default console logger
+TouchEffect.SetLogger(new DefaultTouchEffectLogger());
-### Preset Configurations
-Pre-built configurations for common UI patterns:
-
-- **Button Presets**: Primary, Secondary, Text
-- **Card Presets**: Standard, Elevated, Interactive
-- **List Item Presets**: Standard, Selectable, Swipeable
-- **Icon Button Presets**: Standard, FAB, Toolbar
-- **Toggle Presets**: Standard, Checkbox
-- **Image Presets**: Thumbnail, Gallery, Avatar
-- **Native Effects**: Ripple, Haptic
-- **Special Effects**: Pulse, Bounce, Shake
-
-### Centralized Constants
-All magic numbers replaced with semantic constants:
-
-```csharp
-TouchEffectConstants.Defaults.LongPressDuration // 500ms
-TouchEffectConstants.Animation.TargetFrameRate // 60fps
-TouchEffectConstants.Platform.Android.MinRippleRadius // 48dp
-```
-
-### Enhanced Error Handling
-Comprehensive logging interface for debugging:
-
-```csharp
-// Implement custom logging
+// Or implement custom logging
public class MyLogger : ITouchEffectLogger
{
public void LogError(Exception ex, string context, string? info = null)
{
- // Log to your preferred service
+ // Log to your preferred service (App Center, Sentry, etc.)
+ }
+
+ public void LogWarning(string message, string context)
+ {
+ Debug.WriteLine($"[TouchEffect Warning] {context}: {message}");
+ }
+
+ public void LogInfo(string message, string context)
+ {
+ // Optional info logging
}
}
-// Configure in your app
+// Configure in your app startup
TouchEffect.SetLogger(new MyLogger());
```
@@ -249,79 +261,20 @@ TouchEffect.SetLogger(new MyLogger());
touch:TouchEffect.LongPressDuration="500" />
```
-## Properties Reference
+## API Reference
-### State Properties
-| Property | Type | Default | Description |
-|----------|------|---------|-------------|
-| IsAvailable | bool | true | Enables/disables the effect |
-| IsToggled | bool? | null | Toggle state (null = no toggle behavior) |
-| Status | TouchStatus | Completed | Current touch status |
-| State | TouchState | Normal | Current touch state |
+For complete API documentation, see [API_Reference.md](API_Reference.md).
-### Animation Properties
-| Property | Type | Default | Description |
-|----------|------|---------|-------------|
-| AnimationDuration | int | 0 | Animation duration in milliseconds |
-| AnimationEasing | Easing | null | Animation easing function |
-| PulseCount | int | 0 | Number of pulse repetitions (-1 for infinite) |
+### Quick Reference
-### Visual Properties
-| Property | Type | Default | Description |
-|----------|------|---------|-------------|
-| PressedOpacity | double | 1.0 | Opacity when pressed |
-| PressedScale | double | 1.0 | Scale when pressed |
-| PressedBackgroundColor | Color | Default | Background color when pressed |
-| HoveredOpacity | double | 1.0 | Opacity when hovered |
-| HoveredScale | double | 1.0 | Scale when hovered |
-| NormalOpacity | double | 1.0 | Normal state opacity |
-
-### Command Properties
-| Property | Type | Default | Description |
-|----------|------|---------|-------------|
-| Command | ICommand | null | Command to execute on tap |
-| CommandParameter | object | null | Parameter for command |
-| LongPressCommand | ICommand | null | Command for long press |
-| LongPressDuration | int | 500 | Long press duration in ms |
-
-### Platform-Specific Properties
-| Property | Type | Default | Description |
-|----------|------|---------|-------------|
-| NativeAnimation | bool | false | Use platform native animations |
-| NativeAnimationColor | Color | Default | Native animation color |
-| NativeAnimationRadius | int | -1 | Animation radius (Android/iOS) |
-
-## Events
-
-```csharp
-public partial class MyPage : ContentPage
-{
- protected override void OnAppearing()
- {
- base.OnAppearing();
-
- // Subscribe to events
- TouchEffect.SetStatusChanged(MyView, OnTouchStatusChanged);
- TouchEffect.SetStateChanged(MyView, OnTouchStateChanged);
- TouchEffect.SetCompleted(MyView, OnTouchCompleted);
- }
-
- void OnTouchStatusChanged(object sender, TouchStatusChangedEventArgs e)
- {
- Debug.WriteLine($"Touch Status: {e.Status}");
- }
-
- void OnTouchStateChanged(object sender, TouchStateChangedEventArgs e)
- {
- Debug.WriteLine($"Touch State: {e.State}");
- }
-
- void OnTouchCompleted(object sender, TouchCompletedEventArgs e)
- {
- Debug.WriteLine("Touch completed!");
- }
-}
-```
+| Category | Key Properties |
+|----------|---------------|
+| **State** | `IsAvailable`, `IsToggled`, `Status`, `State` |
+| **Commands** | `Command`, `CommandParameter`, `LongPressCommand`, `LongPressDuration` |
+| **Visual** | `PressedOpacity`, `PressedScale`, `PressedBackgroundColor` |
+| **Hover** | `HoveredOpacity`, `HoveredScale`, `HoveredBackgroundColor` |
+| **Animation** | `AnimationDuration`, `AnimationEasing`, `PulseCount` |
+| **Native** | `NativeAnimation`, `NativeAnimationColor`, `NativeAnimationRadius` |
## Performance Tips
@@ -335,10 +288,10 @@ public partial class MyPage : ContentPage
TouchEffect.Maui is fully accessible by default:
-- ✅ **Keyboard Navigation** - Full support for Tab, Enter, and Space keys
-- ✅ **Screen Readers** - Compatible with VoiceOver, TalkBack, and Narrator
-- ✅ **Focus Indicators** - Proper focus visualization
-- ✅ **Touch Exploration** - Support for accessibility touch modes
+- **Keyboard Navigation** - Full support for Tab, Enter, and Space keys
+- **Screen Readers** - Compatible with VoiceOver, TalkBack, and Narrator
+- **Focus Indicators** - Proper focus visualization
+- **Touch Exploration** - Support for accessibility touch modes
```xml
@@ -355,6 +308,7 @@ TouchEffect.Maui is fully accessible by default:
- Verify `IsAvailable` is true
- Check parent view `InputTransparent` settings
- Ensure view has appropriate size (not 0x0)
+- Enable logging to see detailed diagnostics
### Animations Stuttering
- Reduce `AnimationDuration`
@@ -377,7 +331,7 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
```bash
# Clone the repository
-git clone https://github.com/felipebaltazar/TouchEffect.git
+git clone https://github.com/MarketAlly/TouchEffect.git
# Build the project
dotnet build src/Maui.TouchEffect/TouchEffect.Maui.csproj
@@ -391,15 +345,25 @@ dotnet pack src/Maui.TouchEffect/TouchEffect.Maui.csproj
## Changelog
+### Version 2.0.0 (2025-01)
+- **.NET 10 Support**: Updated to target .NET 10 with latest MAUI packages
+- **TouchBehavior**: New `TouchBehavior` class as modern Behavior-based alternative to Effects
+- **Thread-Safety**: Complete overhaul with proper `lock` synchronization in `GestureManager`
+- **Logging Integration**: Full logging throughout codebase via `ITouchEffectLogger`
+- **Code Organization**: `TouchEffect` split into partial classes (Core, Properties, Accessors)
+- **Bug Fix**: `ForceUpdateStateWithoutAnimation` now correctly passes `animated: false`
+- **Performance**: Replaced LINQ with for-loops in hot paths (`HasTouchEffect`, `GetFrom`, `PickFrom`)
+- **CancellationToken Disposal**: Proper null-before-dispose pattern to prevent race conditions
+
### Version 1.0.0 (2024-11)
-- 🆕 **Fluent Builder Pattern**: New intuitive API for configuring touch effects
-- 🆕 **Preset Configurations**: 20+ pre-built configurations for common UI patterns
-- 🆕 **Centralized Constants**: Eliminated magic numbers throughout codebase
-- 🆕 **Logging Interface**: Comprehensive error handling and debugging support
-- 🆕 **Windows Support**: Full Windows platform implementation with WinUI 3
-- ✨ **Code Quality**: Partial classes, improved organization, and documentation
-- 🐛 **Bug Fixes**: Fixed .NET 9 compatibility issues
-- 📝 **Documentation**: Enhanced XML documentation for all public APIs
+- Fluent Builder Pattern: New intuitive API for configuring touch effects
+- Preset Configurations: 20+ pre-built configurations for common UI patterns
+- Centralized Constants: Eliminated magic numbers throughout codebase
+- Logging Interface: Comprehensive error handling and debugging support
+- Windows Support: Full Windows platform implementation with WinUI 3
+- Code Quality: Partial classes, improved organization, and documentation
+- Bug Fixes: Fixed .NET 9 compatibility issues
+- Documentation: Enhanced XML documentation for all public APIs
### Version 8.1.0
- Initial release as MarketAlly.TouchEffect.Maui
@@ -412,18 +376,18 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
## Acknowledgments
-- Based on the original [TouchEffect](https://github.com/felipebaltazar/TouchEffect) by Andrei (MIT License)
+- Based on the original [TouchEffect](https://github.com/AbuMandworking/TouchEffect) by Andrei (MIT License)
- Original [Xamarin Community Toolkit](https://github.com/xamarin/XamarinCommunityToolkit) team
- [.NET MAUI](https://github.com/dotnet/maui) team
- All [contributors](https://github.com/MarketAlly/TouchEffect/graphs/contributors)
## Support
-- 🐛 [Report Issues](https://github.com/MarketAlly/TouchEffect.Maui/issues)
-- ⭐ Star this repository if you find it helpful!
+- [Report Issues](https://github.com/MarketAlly/TouchEffect.Maui/issues)
+- Star this repository if you find it helpful!
---
-Made with ❤️ by **MarketAlly** for the .NET MAUI Community
+Made with care by **MarketAlly** for the .NET MAUI Community
-*Based on the original TouchEffect by Andrei - Used under MIT License*
\ No newline at end of file
+*Based on the original TouchEffect by Andrei - Used under MIT License*
diff --git a/samples/Maui.TouchEffect.Sample/Maui.TouchEffect.Sample/Maui.TouchEffect.Sample.csproj b/samples/Maui.TouchEffect.Sample/Maui.TouchEffect.Sample/Maui.TouchEffect.Sample.csproj
index b1dfca6..82c64a8 100644
--- a/samples/Maui.TouchEffect.Sample/Maui.TouchEffect.Sample/Maui.TouchEffect.Sample.csproj
+++ b/samples/Maui.TouchEffect.Sample/Maui.TouchEffect.Sample/Maui.TouchEffect.Sample.csproj
@@ -1,10 +1,10 @@
- net9.0-android;net9.0-ios
- $(TargetFrameworks);net9.0-windows10.0.19041.0
+ net10.0-android;net10.0-ios
+ $(TargetFrameworks);net10.0-windows10.0.19041.0
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
-
+
+
diff --git a/src/Maui.TouchEffect/Enums/HoverState.cs b/src/Maui.TouchEffect/Enums/HoverState.cs
index 4388883..124f26a 100644
--- a/src/Maui.TouchEffect/Enums/HoverState.cs
+++ b/src/Maui.TouchEffect/Enums/HoverState.cs
@@ -1,4 +1,4 @@
-namespace Maui.TouchEffect.Enums;
+namespace MarketAlly.TouchEffect.Maui.Enums;
public enum HoverState
{
diff --git a/src/Maui.TouchEffect/Enums/HoverStatus.cs b/src/Maui.TouchEffect/Enums/HoverStatus.cs
index 4d32586..0941898 100644
--- a/src/Maui.TouchEffect/Enums/HoverStatus.cs
+++ b/src/Maui.TouchEffect/Enums/HoverStatus.cs
@@ -1,4 +1,4 @@
-namespace Maui.TouchEffect.Enums;
+namespace MarketAlly.TouchEffect.Maui.Enums;
public enum HoverStatus
{
diff --git a/src/Maui.TouchEffect/Enums/TouchInteractionStatus.cs b/src/Maui.TouchEffect/Enums/TouchInteractionStatus.cs
index 7068c5c..13a1bbd 100644
--- a/src/Maui.TouchEffect/Enums/TouchInteractionStatus.cs
+++ b/src/Maui.TouchEffect/Enums/TouchInteractionStatus.cs
@@ -1,4 +1,4 @@
-namespace Maui.TouchEffect.Enums;
+namespace MarketAlly.TouchEffect.Maui.Enums;
public enum TouchInteractionStatus
{
diff --git a/src/Maui.TouchEffect/Enums/TouchState.cs b/src/Maui.TouchEffect/Enums/TouchState.cs
index d345628..9e9ee11 100644
--- a/src/Maui.TouchEffect/Enums/TouchState.cs
+++ b/src/Maui.TouchEffect/Enums/TouchState.cs
@@ -1,4 +1,4 @@
-namespace Maui.TouchEffect.Enums;
+namespace MarketAlly.TouchEffect.Maui.Enums;
public enum TouchState
{
diff --git a/src/Maui.TouchEffect/Enums/TouchStatus.cs b/src/Maui.TouchEffect/Enums/TouchStatus.cs
index 4943e31..f35b1e7 100644
--- a/src/Maui.TouchEffect/Enums/TouchStatus.cs
+++ b/src/Maui.TouchEffect/Enums/TouchStatus.cs
@@ -1,4 +1,4 @@
-namespace Maui.TouchEffect.Enums;
+namespace MarketAlly.TouchEffect.Maui.Enums;
public enum TouchStatus
{
diff --git a/src/Maui.TouchEffect/EventArgs/HoverStateChangedEventArgs.cs b/src/Maui.TouchEffect/EventArgs/HoverStateChangedEventArgs.cs
index 02adb6a..ecc4d9b 100644
--- a/src/Maui.TouchEffect/EventArgs/HoverStateChangedEventArgs.cs
+++ b/src/Maui.TouchEffect/EventArgs/HoverStateChangedEventArgs.cs
@@ -1,5 +1,6 @@
-using Maui.TouchEffect.Enums;
-namespace Maui.TouchEffect;
+using MarketAlly.TouchEffect.Maui.Enums;
+
+namespace MarketAlly.TouchEffect.Maui;
public class HoverStateChangedEventArgs : EventArgs
{
diff --git a/src/Maui.TouchEffect/EventArgs/HoverStatusChangedEventArgs.cs b/src/Maui.TouchEffect/EventArgs/HoverStatusChangedEventArgs.cs
index 6d25f25..a6fc52b 100644
--- a/src/Maui.TouchEffect/EventArgs/HoverStatusChangedEventArgs.cs
+++ b/src/Maui.TouchEffect/EventArgs/HoverStatusChangedEventArgs.cs
@@ -1,5 +1,6 @@
-using Maui.TouchEffect.Enums;
-namespace Maui.TouchEffect;
+using MarketAlly.TouchEffect.Maui.Enums;
+
+namespace MarketAlly.TouchEffect.Maui;
public class HoverStatusChangedEventArgs : EventArgs
{
diff --git a/src/Maui.TouchEffect/EventArgs/LongPressCompletedEventArgs.cs b/src/Maui.TouchEffect/EventArgs/LongPressCompletedEventArgs.cs
index bd36576..d314cae 100644
--- a/src/Maui.TouchEffect/EventArgs/LongPressCompletedEventArgs.cs
+++ b/src/Maui.TouchEffect/EventArgs/LongPressCompletedEventArgs.cs
@@ -1,4 +1,4 @@
-namespace Maui.TouchEffect;
+namespace MarketAlly.TouchEffect.Maui;
public class LongPressCompletedEventArgs : EventArgs
{
diff --git a/src/Maui.TouchEffect/EventArgs/TouchCompletedEventArgs.cs b/src/Maui.TouchEffect/EventArgs/TouchCompletedEventArgs.cs
index 5281325..e1ccd69 100644
--- a/src/Maui.TouchEffect/EventArgs/TouchCompletedEventArgs.cs
+++ b/src/Maui.TouchEffect/EventArgs/TouchCompletedEventArgs.cs
@@ -1,4 +1,4 @@
-namespace Maui.TouchEffect;
+namespace MarketAlly.TouchEffect.Maui;
public class TouchCompletedEventArgs : EventArgs
{
diff --git a/src/Maui.TouchEffect/EventArgs/TouchInteractionStatusChangedEventArgs.cs b/src/Maui.TouchEffect/EventArgs/TouchInteractionStatusChangedEventArgs.cs
index 85d6d9e..714fef5 100644
--- a/src/Maui.TouchEffect/EventArgs/TouchInteractionStatusChangedEventArgs.cs
+++ b/src/Maui.TouchEffect/EventArgs/TouchInteractionStatusChangedEventArgs.cs
@@ -1,5 +1,6 @@
-using Maui.TouchEffect.Enums;
-namespace Maui.TouchEffect;
+using MarketAlly.TouchEffect.Maui.Enums;
+
+namespace MarketAlly.TouchEffect.Maui;
public class TouchInteractionStatusChangedEventArgs : EventArgs
{
diff --git a/src/Maui.TouchEffect/EventArgs/TouchStateChangedEventArgs.cs b/src/Maui.TouchEffect/EventArgs/TouchStateChangedEventArgs.cs
index a3a15d2..517884a 100644
--- a/src/Maui.TouchEffect/EventArgs/TouchStateChangedEventArgs.cs
+++ b/src/Maui.TouchEffect/EventArgs/TouchStateChangedEventArgs.cs
@@ -1,5 +1,6 @@
-using Maui.TouchEffect.Enums;
-namespace Maui.TouchEffect;
+using MarketAlly.TouchEffect.Maui.Enums;
+
+namespace MarketAlly.TouchEffect.Maui;
public class TouchStateChangedEventArgs : EventArgs
{
diff --git a/src/Maui.TouchEffect/EventArgs/TouchStatusChangedEventArgs.cs b/src/Maui.TouchEffect/EventArgs/TouchStatusChangedEventArgs.cs
index 90d76e4..f74e5ce 100644
--- a/src/Maui.TouchEffect/EventArgs/TouchStatusChangedEventArgs.cs
+++ b/src/Maui.TouchEffect/EventArgs/TouchStatusChangedEventArgs.cs
@@ -1,5 +1,6 @@
-using Maui.TouchEffect.Enums;
-namespace Maui.TouchEffect;
+using MarketAlly.TouchEffect.Maui.Enums;
+
+namespace MarketAlly.TouchEffect.Maui;
public class TouchStatusChangedEventArgs : EventArgs
{
diff --git a/src/Maui.TouchEffect/Extensions/SafeFireAndForgetExtension.cs b/src/Maui.TouchEffect/Extensions/SafeFireAndForgetExtension.cs
index 6d1d9ca..47a6a0c 100644
--- a/src/Maui.TouchEffect/Extensions/SafeFireAndForgetExtension.cs
+++ b/src/Maui.TouchEffect/Extensions/SafeFireAndForgetExtension.cs
@@ -1,4 +1,4 @@
-namespace MauiTouchEffect.Extensions;
+namespace MarketAlly.TouchEffect.Maui.Extensions;
///
/// Extension methods for System.Threading.Tasks.Task and System.Threading.Tasks.ValueTask
diff --git a/src/Maui.TouchEffect/Extensions/VisualElementExtension.cs b/src/Maui.TouchEffect/Extensions/VisualElementExtension.cs
index 0de56b4..ed5e890 100644
--- a/src/Maui.TouchEffect/Extensions/VisualElementExtension.cs
+++ b/src/Maui.TouchEffect/Extensions/VisualElementExtension.cs
@@ -1,4 +1,4 @@
-namespace Maui.TouchEffect.Extensions;
+namespace MarketAlly.TouchEffect.Maui.Extensions;
///
/// Extension methods for .
@@ -51,7 +51,7 @@ public static class VisualElementExtension
}
}
- internal static bool TryFindParentElementWithParentOfType(this VisualElement element, out VisualElement result, out T parent) where T : VisualElement
+ internal static bool TryFindParentElementWithParentOfType(this VisualElement? element, out VisualElement? result, out T? parent) where T : VisualElement
{
result = null;
parent = null;
@@ -73,7 +73,7 @@ public static class VisualElementExtension
return false;
}
- internal static bool TryFindParentOfType(this VisualElement element, out T parent) where T : VisualElement
+ internal static bool TryFindParentOfType(this VisualElement element, out T? parent) where T : VisualElement
{
return element.TryFindParentElementWithParentOfType(out _, out parent);
}
diff --git a/src/Maui.TouchEffect/GestureManager.cs b/src/Maui.TouchEffect/GestureManager.cs
index 1183c00..4061a7c 100644
--- a/src/Maui.TouchEffect/GestureManager.cs
+++ b/src/Maui.TouchEffect/GestureManager.cs
@@ -1,16 +1,16 @@
-using Maui.TouchEffect.Enums;
-using Maui.TouchEffect.Extensions;
+using MarketAlly.TouchEffect.Maui.Enums;
+using MarketAlly.TouchEffect.Maui.Extensions;
using static System.Math;
-namespace Maui.TouchEffect;
+namespace MarketAlly.TouchEffect.Maui;
internal sealed class GestureManager
{
- private const int _animationProgressDelay = 10;
+ private readonly object _syncLock = new();
private Color? _defaultBackgroundColor;
private CancellationTokenSource? _longPressTokenSource;
private CancellationTokenSource? _animationTokenSource;
- private Func? _animationTaskFactory;
+ private Func? _animationTaskFactory;
private double? _durationMultiplier;
private double _animationProgress;
private TouchState? _animationState;
@@ -36,8 +36,11 @@ internal sealed class GestureManager
if (status == TouchStatus.Started)
{
- _animationProgress = 0;
- _animationState = state;
+ lock (_syncLock)
+ {
+ _animationProgress = 0;
+ _animationState = state;
+ }
}
var isToggled = sender.IsToggled;
@@ -45,10 +48,13 @@ internal sealed class GestureManager
{
if (status != TouchStatus.Started)
{
- _durationMultiplier = _animationState == TouchState.Pressed && !isToggled.Value ||
- _animationState == TouchState.Normal && isToggled.Value
- ? 1 - _animationProgress
- : _animationProgress;
+ lock (_syncLock)
+ {
+ _durationMultiplier = _animationState == TouchState.Pressed && !isToggled.Value ||
+ _animationState == TouchState.Normal && isToggled.Value
+ ? 1 - _animationProgress
+ : _animationProgress;
+ }
UpdateStatusAndState(sender, status, state);
@@ -117,8 +123,14 @@ internal sealed class GestureManager
var hoverState = sender.HoverState;
AbortAnimations(sender);
- _animationTokenSource = new CancellationTokenSource();
- var token = _animationTokenSource.Token;
+
+ CancellationTokenSource tokenSource;
+ lock (_syncLock)
+ {
+ _animationTokenSource = new CancellationTokenSource();
+ tokenSource = _animationTokenSource;
+ }
+ var token = tokenSource.Token;
var isToggled = sender.IsToggled;
@@ -136,10 +148,14 @@ internal sealed class GestureManager
: TouchState.Normal;
}
- var durationMultiplier = _durationMultiplier;
- _durationMultiplier = null;
+ double? durationMultiplier;
+ lock (_syncLock)
+ {
+ durationMultiplier = _durationMultiplier;
+ _durationMultiplier = null;
+ }
- await RunAnimationTask(sender, state, hoverState, _animationTokenSource.Token, durationMultiplier.GetValueOrDefault()).ConfigureAwait(false);
+ await RunAnimationTask(sender, state, hoverState, token, durationMultiplier.GetValueOrDefault()).ConfigureAwait(false);
return;
}
@@ -156,7 +172,7 @@ internal sealed class GestureManager
: TouchState.Pressed;
}
- await RunAnimationTask(sender, state, hoverState, _animationTokenSource.Token).ConfigureAwait(false);
+ await RunAnimationTask(sender, state, hoverState, token).ConfigureAwait(false);
return;
}
@@ -166,7 +182,7 @@ internal sealed class GestureManager
? TouchState.Normal
: TouchState.Pressed;
- await RunAnimationTask(sender, rippleState, hoverState, _animationTokenSource.Token);
+ await RunAnimationTask(sender, rippleState, hoverState, token);
if (token.IsCancellationRequested)
{
return;
@@ -176,7 +192,7 @@ internal sealed class GestureManager
? TouchState.Pressed
: TouchState.Normal;
- await RunAnimationTask(sender, rippleState, hoverState, _animationTokenSource.Token);
+ await RunAnimationTask(sender, rippleState, hoverState, token);
if (token.IsCancellationRequested)
{
return;
@@ -188,9 +204,7 @@ internal sealed class GestureManager
{
if (sender.State == TouchState.Normal)
{
- _longPressTokenSource?.Cancel();
- _longPressTokenSource?.Dispose();
- _longPressTokenSource = null;
+ CancelAndDisposeLongPressToken();
return;
}
@@ -199,37 +213,66 @@ internal sealed class GestureManager
return;
}
- _longPressTokenSource = new CancellationTokenSource();
- _ = Task.Delay(sender.LongPressDuration, _longPressTokenSource.Token).ContinueWith(t =>
+ CancelAndDisposeLongPressToken();
+
+ var tokenSource = new CancellationTokenSource();
+ lock (_syncLock)
{
- if (t.IsFaulted && t.Exception != null)
- {
- throw t.Exception;
- }
+ _longPressTokenSource = tokenSource;
+ }
- if (t.IsCanceled)
- {
+ _ = HandleLongPressAsync(sender, tokenSource.Token);
+ }
+
+ private async Task HandleLongPressAsync(TouchEffect sender, CancellationToken token)
+ {
+ try
+ {
+ await Task.Delay(sender.LongPressDuration, token).ConfigureAwait(false);
+
+ if (token.IsCancellationRequested)
return;
- }
- var longPressAction = new Action(() =>
+ await MainThread.InvokeOnMainThreadAsync(() =>
{
sender.HandleUserInteraction(TouchInteractionStatus.Completed);
sender.RaiseLongPressCompleted();
- });
-
- if (MainThread.IsMainThread)
- {
- MainThread.BeginInvokeOnMainThread(longPressAction);
- }
- else
- {
- longPressAction.Invoke();
- }
- });
+ }).ConfigureAwait(false);
+ }
+ catch (TaskCanceledException)
+ {
+ // Expected when long press is canceled
+ }
+ catch (Exception ex)
+ {
+ TouchEffect.Logger.LogError(ex, "HandleLongPressAsync", "Error during long press handling");
+ }
}
- internal void SetCustomAnimationTask(Func? animationTaskFactory)
+ private void CancelAndDisposeLongPressToken()
+ {
+ CancellationTokenSource? tokenSource;
+ lock (_syncLock)
+ {
+ tokenSource = _longPressTokenSource;
+ _longPressTokenSource = null;
+ }
+
+ if (tokenSource != null)
+ {
+ try
+ {
+ tokenSource.Cancel();
+ tokenSource.Dispose();
+ }
+ catch (ObjectDisposedException)
+ {
+ // Already disposed
+ }
+ }
+ }
+
+ internal void SetCustomAnimationTask(Func? animationTaskFactory)
{
_animationTaskFactory = animationTaskFactory;
}
@@ -262,7 +305,7 @@ internal sealed class GestureManager
private static void HandleCollectionViewSelection(TouchEffect sender)
{
- if (!sender.Element.TryFindParentElementWithParentOfType(out var result, out CollectionView parent))
+ if (sender.Element == null || !sender.Element.TryFindParentElementWithParentOfType(out var result, out CollectionView? parent))
{
return;
}
@@ -294,9 +337,26 @@ internal sealed class GestureManager
internal void AbortAnimations(TouchEffect sender)
{
- _animationTokenSource?.Cancel();
- _animationTokenSource?.Dispose();
- _animationTokenSource = null;
+ CancellationTokenSource? tokenSource;
+ lock (_syncLock)
+ {
+ tokenSource = _animationTokenSource;
+ _animationTokenSource = null;
+ }
+
+ if (tokenSource != null)
+ {
+ try
+ {
+ tokenSource.Cancel();
+ tokenSource.Dispose();
+ }
+ catch (ObjectDisposedException)
+ {
+ // Already disposed
+ }
+ }
+
var element = sender.Element;
if (element == null)
{
@@ -395,7 +455,7 @@ internal sealed class GestureManager
}
}
- private Task SetBackgroundColor(TouchEffect sender, TouchState touchState, HoverState hoverState, int duration, Easing easing)
+ private Task SetBackgroundColor(TouchEffect sender, TouchState touchState, HoverState hoverState, int duration, Easing? easing)
{
var normalBackgroundColor = sender.NormalBackgroundColor;
var pressedBackgroundColor = sender.PressedBackgroundColor;
@@ -436,7 +496,7 @@ internal sealed class GestureManager
return element.ColorTo(color, (uint)duration, easing);
}
- private static Task? SetOpacity(TouchEffect sender, TouchState touchState, HoverState hoverState, int duration, Easing easing)
+ private static Task? SetOpacity(TouchEffect sender, TouchState touchState, HoverState hoverState, int duration, Easing? easing)
{
var normalOpacity = sender.NormalOpacity;
var pressedOpacity = sender.PressedOpacity;
@@ -471,7 +531,7 @@ internal sealed class GestureManager
return element?.FadeTo(opacity, (uint)Abs(duration), easing);
}
- private Task SetScale(TouchEffect sender, TouchState touchState, HoverState hoverState, int duration, Easing easing)
+ private Task SetScale(TouchEffect sender, TouchState touchState, HoverState hoverState, int duration, Easing? easing)
{
var normalScale = sender.NormalScale;
var pressedScale = sender.PressedScale;
@@ -521,7 +581,7 @@ internal sealed class GestureManager
return animationCompletionSource.Task;
}
- private static Task SetTranslation(TouchEffect sender, TouchState touchState, HoverState hoverState, int duration, Easing easing)
+ private static Task SetTranslation(TouchEffect sender, TouchState touchState, HoverState hoverState, int duration, Easing? easing)
{
var normalTranslationX = sender.NormalTranslationX;
var pressedTranslationX = sender.PressedTranslationX;
@@ -574,7 +634,7 @@ internal sealed class GestureManager
return element?.TranslateTo(translationX, translationY, (uint)Abs(duration), easing) ?? Task.FromResult(false);
}
- private static Task SetRotation(TouchEffect sender, TouchState touchState, HoverState hoverState, int duration, Easing easing)
+ private static Task SetRotation(TouchEffect sender, TouchState touchState, HoverState hoverState, int duration, Easing? easing)
{
var normalRotation = sender.NormalRotation;
var pressedRotation = sender.PressedRotation;
@@ -609,7 +669,7 @@ internal sealed class GestureManager
return element?.RotateTo(rotation, (uint)Abs(duration), easing) ?? Task.FromResult(false);
}
- private static Task SetRotationX(TouchEffect sender, TouchState touchState, HoverState hoverState, int duration, Easing easing)
+ private static Task SetRotationX(TouchEffect sender, TouchState touchState, HoverState hoverState, int duration, Easing? easing)
{
var normalRotationX = sender.NormalRotationX;
var pressedRotationX = sender.PressedRotationX;
@@ -644,7 +704,7 @@ internal sealed class GestureManager
return element?.RotateXTo(rotationX, (uint)Abs(duration), easing) ?? Task.FromResult(false);
}
- private static Task SetRotationY(TouchEffect sender, TouchState touchState, HoverState hoverState, int duration, Easing easing)
+ private static Task SetRotationY(TouchEffect sender, TouchState touchState, HoverState hoverState, int duration, Easing? easing)
{
var normalRotationY = sender.NormalRotationY;
var pressedRotationY = sender.PressedRotationY;
@@ -744,29 +804,48 @@ internal sealed class GestureManager
_animationTaskFactory?.Invoke(sender, touchState, hoverState, duration, easing, token) ?? Task.FromResult(true),
SetBackgroundImageAsync(sender, touchState, hoverState, duration, token),
SetBackgroundColor(sender, touchState, hoverState, duration, easing),
- SetOpacity(sender, touchState, hoverState, duration, easing),
+ SetOpacity(sender, touchState, hoverState, duration, easing) ?? Task.CompletedTask,
SetScale(sender, touchState, hoverState, duration, easing),
SetTranslation(sender, touchState, hoverState, duration, easing),
SetRotation(sender, touchState, hoverState, duration, easing),
SetRotationX(sender, touchState, hoverState, duration, easing),
SetRotationY(sender, touchState, hoverState, duration, easing),
- Task.Run(async () =>
+ TrackAnimationProgressAsync(touchState, duration, token));
+ }
+
+ private async Task TrackAnimationProgressAsync(TouchState touchState, int duration, CancellationToken token)
+ {
+ lock (_syncLock)
+ {
+ _animationProgress = 0;
+ _animationState = touchState;
+ }
+
+ for (var progress = TouchEffectConstants.Animation.DefaultProgressDelay; progress < duration; progress += TouchEffectConstants.Animation.DefaultProgressDelay)
+ {
+ try
{
- _animationProgress = 0;
- _animationState = touchState;
+ await Task.Delay(TouchEffectConstants.Animation.DefaultProgressDelay, token).ConfigureAwait(false);
+ }
+ catch (TaskCanceledException)
+ {
+ return;
+ }
- for (var progress = _animationProgressDelay; progress < duration; progress += _animationProgressDelay)
- {
- await Task.Delay(_animationProgressDelay).ConfigureAwait(false);
- if (token.IsCancellationRequested)
- {
- return;
- }
+ if (token.IsCancellationRequested)
+ {
+ return;
+ }
- _animationProgress = (double)progress / duration;
- }
+ lock (_syncLock)
+ {
+ _animationProgress = (double)progress / duration;
+ }
+ }
- _animationProgress = 1;
- }));
+ lock (_syncLock)
+ {
+ _animationProgress = 1;
+ }
}
}
diff --git a/src/Maui.TouchEffect/Hosting/AppBuilderExtensions.cs b/src/Maui.TouchEffect/Hosting/AppBuilderExtensions.cs
index f392c88..509ecbc 100644
--- a/src/Maui.TouchEffect/Hosting/AppBuilderExtensions.cs
+++ b/src/Maui.TouchEffect/Hosting/AppBuilderExtensions.cs
@@ -1,6 +1,7 @@
using Microsoft.Maui.Controls.Hosting;
using Microsoft.Maui.Hosting;
-namespace Maui.TouchEffect.Hosting;
+
+namespace MarketAlly.TouchEffect.Maui.Hosting;
public static class AppBuilderExtensions
{
diff --git a/src/Maui.TouchEffect/Interfaces/ITouchEffectLogger.cs b/src/Maui.TouchEffect/Interfaces/ITouchEffectLogger.cs
index 6a2467a..72ab9a6 100644
--- a/src/Maui.TouchEffect/Interfaces/ITouchEffectLogger.cs
+++ b/src/Maui.TouchEffect/Interfaces/ITouchEffectLogger.cs
@@ -1,6 +1,4 @@
-using System;
-
-namespace Maui.TouchEffect.Interfaces;
+namespace MarketAlly.TouchEffect.Maui.Interfaces;
///
/// Interface for logging TouchEffect events and errors.
@@ -75,12 +73,6 @@ public class DefaultTouchEffectLogger : ITouchEffectLogger
System.Diagnostics.Debug.WriteLine(message);
System.Diagnostics.Debug.WriteLine($"Stack Trace: {exception.StackTrace}");
-
- // In production, you might want to send this to a crash reporting service
-#if !DEBUG
- // Example: AppCenter, Sentry, Application Insights, etc.
- // CrashReporting.TrackError(exception, new Dictionary { { "Context", context } });
-#endif
}
///
@@ -121,12 +113,6 @@ public class DefaultTouchEffectLogger : ITouchEffectLogger
}
System.Diagnostics.Debug.WriteLine(message);
-
- // In production, you might want to send this to analytics
-#if !DEBUG
- // Example: Application Insights, Google Analytics, etc.
- // Analytics.TrackMetric(operationName, elapsedMilliseconds, additionalMetrics);
-#endif
}
}
@@ -147,4 +133,4 @@ public class NullTouchEffectLogger : ITouchEffectLogger
public void LogInformation(string message, string context) { }
public void LogDebug(string message, string context) { }
public void LogPerformance(string operationName, double elapsedMilliseconds, Dictionary? additionalMetrics = null) { }
-}
\ No newline at end of file
+}
diff --git a/src/Maui.TouchEffect/Platforms/Android/JavaObjectExtensions.cs b/src/Maui.TouchEffect/Platforms/Android/JavaObjectExtensions.cs
index 7e3441a..36886ce 100644
--- a/src/Maui.TouchEffect/Platforms/Android/JavaObjectExtensions.cs
+++ b/src/Maui.TouchEffect/Platforms/Android/JavaObjectExtensions.cs
@@ -1,4 +1,4 @@
-namespace Maui.TouchEffect;
+namespace MarketAlly.TouchEffect.Maui;
internal static class JavaObjectExtensions
{
@@ -13,4 +13,4 @@ internal static class JavaObjectExtensions
public static bool IsAlive(this global::Android.Runtime.IJavaObject obj)
=> obj != null && !obj.IsDisposed();
-}
\ No newline at end of file
+}
diff --git a/src/Maui.TouchEffect/Platforms/Android/PlatformTouchEffect.cs b/src/Maui.TouchEffect/Platforms/Android/PlatformTouchEffect.cs
index f2f69bc..c18645d 100644
--- a/src/Maui.TouchEffect/Platforms/Android/PlatformTouchEffect.cs
+++ b/src/Maui.TouchEffect/Platforms/Android/PlatformTouchEffect.cs
@@ -7,17 +7,21 @@ using Android.Views.Accessibility;
using Android.Widget;
using Microsoft.Maui.Platform;
using System.ComponentModel;
+using MarketAlly.TouchEffect.Maui.Enums;
using AView = Android.Views.View;
using Color = Android.Graphics.Color;
using Mview = Microsoft.Maui.Controls.View;
using Mcolor = Microsoft.Maui.Graphics.Color;
-using Maui.TouchEffect.Enums;
-namespace Maui.TouchEffect;
+namespace MarketAlly.TouchEffect.Maui;
public class PlatformTouchEffect : Microsoft.Maui.Controls.Platform.PlatformEffect
{
- private static readonly Mcolor defaultNativeAnimationColor = new(128, 128, 128, 64);
+ private static readonly Mcolor defaultNativeAnimationColor = new(
+ TouchEffectConstants.Platform.Android.DefaultRippleColor,
+ TouchEffectConstants.Platform.Android.DefaultRippleColor,
+ TouchEffectConstants.Platform.Android.DefaultRippleColor,
+ TouchEffectConstants.Platform.Android.DefaultRippleAlpha);
AccessibilityManager? accessibilityManager;
AccessibilityListener? accessibilityListener;
@@ -28,7 +32,7 @@ public class PlatformTouchEffect : Microsoft.Maui.Controls.Platform.PlatformEffe
float startX;
float startY;
Mcolor? rippleColor;
- int rippleRadius = -1;
+ int rippleRadius = TouchEffectConstants.Defaults.NativeAnimationRadius;
AView view => Control ?? Container;
@@ -139,9 +143,9 @@ public class PlatformTouchEffect : Microsoft.Maui.Controls.Platform.PlatformEffe
ripple?.Dispose();
ripple = null;
}
- catch (ObjectDisposedException)
+ catch (ObjectDisposedException ex)
{
- // Suppress exception
+ TouchEffect.Logger.LogWarning($"Object already disposed during OnDetached: {ex.Message}", "PlatformTouchEffect.Android");
}
isHoverSupported = false;
}
@@ -166,7 +170,7 @@ public class PlatformTouchEffect : Microsoft.Maui.Controls.Platform.PlatformEffe
}
}
- void OnTouch(object sender, AView.TouchEventArgs e)
+ void OnTouch(object? sender, AView.TouchEventArgs e)
{
e.Handled = false;
@@ -223,7 +227,7 @@ public class PlatformTouchEffect : Microsoft.Maui.Controls.Platform.PlatformEffe
void OnTouchCancel()
=> HandleEnd(TouchStatus.Canceled);
- void OnTouchMove(object sender, AView.TouchEventArgs e)
+ void OnTouchMove(object? sender, AView.TouchEventArgs e)
{
if (IsCanceled || e.Event == null)
return;
@@ -273,7 +277,7 @@ public class PlatformTouchEffect : Microsoft.Maui.Controls.Platform.PlatformEffe
effect?.HandleHover(HoverStatus.Exited);
}
- void OnClick(object sender, System.EventArgs args)
+ void OnClick(object? sender, System.EventArgs args)
{
if (effect?.IsDisabled ?? true)
return;
@@ -385,7 +389,7 @@ public class PlatformTouchEffect : Microsoft.Maui.Controls.Platform.PlatformEffe
new[] { (int)nativeAnimationColor.ToPlatform() });
}
- void OnLayoutChange(object sender, AView.LayoutChangeEventArgs e)
+ void OnLayoutChange(object? sender, AView.LayoutChangeEventArgs e)
{
if (sender is not AView layoutView || group == null || rippleView == null)
return;
@@ -398,7 +402,7 @@ public class PlatformTouchEffect : Microsoft.Maui.Controls.Platform.PlatformEffe
AccessibilityManager.IAccessibilityStateChangeListener,
AccessibilityManager.ITouchExplorationStateChangeListener
{
- PlatformTouchEffect platformTouchEffect;
+ PlatformTouchEffect? platformTouchEffect;
internal AccessibilityListener(PlatformTouchEffect platformTouchEffect)
=> this.platformTouchEffect = platformTouchEffect;
@@ -417,4 +421,4 @@ public class PlatformTouchEffect : Microsoft.Maui.Controls.Platform.PlatformEffe
base.Dispose(disposing);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Maui.TouchEffect/Platforms/Windows/PlatformTouchEffect.cs b/src/Maui.TouchEffect/Platforms/Windows/PlatformTouchEffect.cs
index cb29bad..05e554a 100644
--- a/src/Maui.TouchEffect/Platforms/Windows/PlatformTouchEffect.cs
+++ b/src/Maui.TouchEffect/Platforms/Windows/PlatformTouchEffect.cs
@@ -1,5 +1,5 @@
-using Maui.TouchEffect.Enums;
-using MauiTouchEffect.Extensions;
+using MarketAlly.TouchEffect.Maui.Enums;
+using MarketAlly.TouchEffect.Maui.Extensions;
using Microsoft.Maui.Controls.Platform;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input;
@@ -13,7 +13,7 @@ using WinBrush = Microsoft.UI.Xaml.Media.Brush;
using WinSolidColorBrush = Microsoft.UI.Xaml.Media.SolidColorBrush;
using WinPoint = Windows.Foundation.Point;
-namespace Maui.TouchEffect;
+namespace MarketAlly.TouchEffect.Maui;
public class PlatformTouchEffect : Microsoft.Maui.Controls.Platform.PlatformEffect
{
@@ -98,9 +98,9 @@ public class PlatformTouchEffect : Microsoft.Maui.Controls.Platform.PlatformEffe
_effect = null;
_view = null;
}
- catch (Exception)
+ catch (Exception ex)
{
- // Suppress exceptions during cleanup
+ TouchEffect.Logger.LogError(ex, "PlatformTouchEffect.Windows.OnDetached", "Error during cleanup");
}
}
@@ -193,9 +193,9 @@ public class PlatformTouchEffect : Microsoft.Maui.Controls.Platform.PlatformEffe
{
_view.CapturePointer(e.Pointer);
}
- catch
+ catch (Exception ex)
{
- // Some controls may not support pointer capture
+ TouchEffect.Logger.LogWarning($"Failed to capture pointer: {ex.Message}", "PlatformTouchEffect.Windows");
}
// Handle the touch interaction
@@ -224,14 +224,16 @@ public class PlatformTouchEffect : Microsoft.Maui.Controls.Platform.PlatformEffe
{
_view?.ReleasePointerCapture(e.Pointer);
}
- catch
+ catch (Exception ex)
{
- // Ignore capture release errors
+ TouchEffect.Logger.LogWarning($"Failed to release pointer capture: {ex.Message}", "PlatformTouchEffect.Windows");
}
// Determine if this is a completed tap or canceled
var distance = CalculateDistance(_startPoint, pointer.Position);
- var threshold = _effect.DisallowTouchThreshold > 0 ? _effect.DisallowTouchThreshold : 20;
+ var threshold = _effect.DisallowTouchThreshold > 0
+ ? _effect.DisallowTouchThreshold
+ : TouchEffectConstants.Platform.Windows.DefaultMovementThreshold;
if (distance <= threshold)
{
@@ -266,7 +268,9 @@ public class PlatformTouchEffect : Microsoft.Maui.Controls.Platform.PlatformEffe
var pointer = e.GetCurrentPoint(_view);
var distance = CalculateDistance(_startPoint, pointer.Position);
- var threshold = _effect.DisallowTouchThreshold > 0 ? _effect.DisallowTouchThreshold : 20;
+ var threshold = _effect.DisallowTouchThreshold > 0
+ ? _effect.DisallowTouchThreshold
+ : TouchEffectConstants.Platform.Windows.DefaultMovementThreshold;
// Cancel if moved too far
if (distance > threshold)
@@ -368,10 +372,10 @@ public class PlatformTouchEffect : Microsoft.Maui.Controls.Platform.PlatformEffe
// Determine target values based on state
double targetOpacity = _originalOpacity;
- double targetScale = 1.0;
- double targetTranslationX = 0;
- double targetTranslationY = 0;
- double targetRotation = 0;
+ double targetScale = TouchEffectConstants.Defaults.Scale;
+ double targetTranslationX = TouchEffectConstants.Defaults.TranslationX;
+ double targetTranslationY = TouchEffectConstants.Defaults.TranslationY;
+ double targetRotation = TouchEffectConstants.Defaults.Rotation;
WinBrush? targetBrush = _originalBrush;
if (_isPressed && _effect.State == TouchState.Pressed)
@@ -540,7 +544,7 @@ public class PlatformTouchEffect : Microsoft.Maui.Controls.Platform.PlatformEffe
// Handle pulse/ripple count
if (_effect?.PulseCount != 0)
{
- var pulseCount = _effect?.PulseCount ?? 0;
+ var pulseCount = _effect?.PulseCount ?? TouchEffectConstants.Defaults.PulseCount;
if (pulseCount < 0)
{
storyboard.RepeatBehavior = RepeatBehavior.Forever;
@@ -558,7 +562,7 @@ public class PlatformTouchEffect : Microsoft.Maui.Controls.Platform.PlatformEffe
private int GetAnimationDuration()
{
if (_effect == null)
- return 0;
+ return TouchEffectConstants.Animation.DefaultDuration;
// Determine which animation duration to use
if (_isPressed)
@@ -656,35 +660,49 @@ public class PlatformTouchEffect : Microsoft.Maui.Controls.Platform.PlatformEffe
if (_effect == null || _effect.LongPressCommand == null)
return;
- _longPressCts?.Cancel();
+ CancelLongPressDetection();
_longPressCts = new System.Threading.CancellationTokenSource();
try
{
- var duration = _effect.LongPressDuration > 0 ? _effect.LongPressDuration : 500;
+ var duration = _effect.LongPressDuration > 0
+ ? _effect.LongPressDuration
+ : TouchEffectConstants.Defaults.LongPressDuration;
await Task.Delay(duration, _longPressCts.Token);
if (!_longPressCts.Token.IsCancellationRequested && _isPressed)
{
_effect.RaiseLongPressCompleted();
-
- // Optional: Provide haptic feedback if available
- // Windows doesn't have built-in haptic API like mobile platforms
- // Could potentially use Windows.Devices.Haptics if available
}
}
catch (TaskCanceledException)
{
// Expected when gesture is canceled
}
+ catch (Exception ex)
+ {
+ TouchEffect.Logger.LogError(ex, "PlatformTouchEffect.Windows.StartLongPressDetection", "Error during long press detection");
+ }
}
private void CancelLongPressDetection()
{
- _longPressCts?.Cancel();
- _longPressCts?.Dispose();
+ var cts = _longPressCts;
_longPressCts = null;
+
+ if (cts != null)
+ {
+ try
+ {
+ cts.Cancel();
+ cts.Dispose();
+ }
+ catch (ObjectDisposedException)
+ {
+ // Already disposed
+ }
+ }
}
#endregion
-}
\ No newline at end of file
+}
diff --git a/src/Maui.TouchEffect/Platforms/iOS/PlatformTouchEffect.cs b/src/Maui.TouchEffect/Platforms/iOS/PlatformTouchEffect.cs
index d96fecc..0d8543a 100644
--- a/src/Maui.TouchEffect/Platforms/iOS/PlatformTouchEffect.cs
+++ b/src/Maui.TouchEffect/Platforms/iOS/PlatformTouchEffect.cs
@@ -1,21 +1,21 @@
using CoreGraphics;
using Foundation;
-using Maui.TouchEffect.Enums;
-using MauiTouchEffect.Extensions;
+using MarketAlly.TouchEffect.Maui.Enums;
+using MarketAlly.TouchEffect.Maui.Extensions;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Platform;
using UIKit;
-namespace Maui.TouchEffect;
+namespace MarketAlly.TouchEffect.Maui;
public partial class PlatformTouchEffect : PlatformEffect
{
private UIGestureRecognizer? touchGesture;
private UIGestureRecognizer? hoverGesture;
-
+
TouchEffect? effect;
UIView View => Container ?? Control;
-
+
protected override void OnAttached()
{
effect = TouchEffect.PickFrom(Element);
@@ -37,7 +37,7 @@ public partial class PlatformTouchEffect : PlatformEffect
View.AddGestureRecognizer(touchGesture);
- if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
+ if (UIDevice.CurrentDevice.CheckSystemVersion(TouchEffectConstants.Platform.iOS.MinHoverGestureVersion, 0))
{
hoverGesture = new UIHoverGestureRecognizer(OnHover);
View.AddGestureRecognizer(hoverGesture);
@@ -104,7 +104,7 @@ public partial class PlatformTouchEffect : PlatformEffect
internal sealed class TouchUITapGestureRecognizer : UIGestureRecognizer
{
- private TouchEffect touchEffect;
+ private TouchEffect? touchEffect;
private float? defaultRadius;
private float? defaultShadowRadius;
private float? defaultShadowOpacity;
@@ -131,7 +131,8 @@ internal sealed class TouchUITapGestureRecognizer : UIGestureRecognizer
IsCanceled = false;
startPoint = GetTouchPoint(touches);
- HandleTouch(TouchStatus.Started, TouchInteractionStatus.Started).SafeFireAndForget();
+ HandleTouch(TouchStatus.Started, TouchInteractionStatus.Started).SafeFireAndForget(
+ ex => TouchEffect.Logger.LogError(ex, "TouchesBegan", "Error handling touch start"));
base.TouchesBegan(touches, evt);
}
@@ -143,7 +144,8 @@ internal sealed class TouchUITapGestureRecognizer : UIGestureRecognizer
return;
}
- HandleTouch(touchEffect?.Status == TouchStatus.Started ? TouchStatus.Completed : TouchStatus.Canceled, TouchInteractionStatus.Completed).SafeFireAndForget();
+ HandleTouch(touchEffect?.Status == TouchStatus.Started ? TouchStatus.Completed : TouchStatus.Canceled, TouchInteractionStatus.Completed).SafeFireAndForget(
+ ex => TouchEffect.Logger.LogError(ex, "TouchesEnded", "Error handling touch end"));
IsCanceled = true;
@@ -157,7 +159,8 @@ internal sealed class TouchUITapGestureRecognizer : UIGestureRecognizer
return;
}
- HandleTouch(TouchStatus.Canceled, TouchInteractionStatus.Completed).SafeFireAndForget();
+ HandleTouch(TouchStatus.Canceled, TouchInteractionStatus.Completed).SafeFireAndForget(
+ ex => TouchEffect.Logger.LogError(ex, "TouchesCancelled", "Error handling touch cancel"));
IsCanceled = true;
@@ -180,7 +183,8 @@ internal sealed class TouchUITapGestureRecognizer : UIGestureRecognizer
var maxDiff = Math.Max(diffX, diffY);
if (maxDiff > disallowTouchThreshold)
{
- HandleTouch(TouchStatus.Canceled, TouchInteractionStatus.Completed).SafeFireAndForget();
+ HandleTouch(TouchStatus.Canceled, TouchInteractionStatus.Completed).SafeFireAndForget(
+ ex => TouchEffect.Logger.LogError(ex, "TouchesMoved", "Error handling touch cancel from threshold"));
IsCanceled = true;
base.TouchesMoved(touches, evt);
return;
@@ -193,7 +197,8 @@ internal sealed class TouchUITapGestureRecognizer : UIGestureRecognizer
if (touchEffect?.Status != status)
{
- HandleTouch(status).SafeFireAndForget();
+ HandleTouch(status).SafeFireAndForget(
+ ex => TouchEffect.Logger.LogError(ex, "TouchesMoved", "Error handling touch status change"));
}
if (status == TouchStatus.Canceled)
@@ -254,7 +259,7 @@ internal sealed class TouchUITapGestureRecognizer : UIGestureRecognizer
else
{
var backgroundColor = touchEffect.Element.BackgroundColor ?? Colors.Transparent;
-
+
View.Layer.BackgroundColor = (isStarted ? color : backgroundColor).ToCGColor();
}
@@ -273,7 +278,8 @@ internal sealed class TouchUITapGestureRecognizer : UIGestureRecognizer
{
if (disposing)
{
- Delegate.Dispose();
+ Delegate?.Dispose();
+ touchEffect = null;
}
base.Dispose(disposing);
@@ -291,7 +297,8 @@ internal sealed class TouchUITapGestureRecognizer : UIGestureRecognizer
if (gestureRecognizer is TouchUITapGestureRecognizer touchGesture && otherGestureRecognizer is UIPanGestureRecognizer &&
otherGestureRecognizer.State == UIGestureRecognizerState.Began)
{
- touchGesture.HandleTouch(TouchStatus.Canceled, TouchInteractionStatus.Completed).SafeFireAndForget();
+ touchGesture.HandleTouch(TouchStatus.Canceled, TouchInteractionStatus.Completed).SafeFireAndForget(
+ ex => TouchEffect.Logger.LogError(ex, "ShouldRecognizeSimultaneously", "Error handling touch cancel"));
touchGesture.IsCanceled = true;
}
@@ -308,4 +315,4 @@ internal sealed class TouchUITapGestureRecognizer : UIGestureRecognizer
return recognizer.View.Subviews.Any(view => view == touch.View);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Maui.TouchEffect/Properties/AssemblyInfo.cs b/src/Maui.TouchEffect/Properties/AssemblyInfo.cs
index 8467f0d..26bf250 100644
--- a/src/Maui.TouchEffect/Properties/AssemblyInfo.cs
+++ b/src/Maui.TouchEffect/Properties/AssemblyInfo.cs
@@ -1,2 +1,2 @@
-[assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/2021/maui", "Maui.TouchEffect")]
-[assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/2021/maui", "Maui.TouchEffect.Enums")]
\ No newline at end of file
+[assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/2021/maui", "MarketAlly.TouchEffect.Maui")]
+[assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/2021/maui", "MarketAlly.TouchEffect.Maui.Enums")]
diff --git a/src/Maui.TouchEffect/TouchBehavior.cs b/src/Maui.TouchEffect/TouchBehavior.cs
new file mode 100644
index 0000000..10c69fc
--- /dev/null
+++ b/src/Maui.TouchEffect/TouchBehavior.cs
@@ -0,0 +1,312 @@
+using System.Windows.Input;
+using MarketAlly.TouchEffect.Maui.Enums;
+
+namespace MarketAlly.TouchEffect.Maui;
+
+///
+/// A Behavior-based alternative to TouchEffect for attaching touch feedback to elements.
+/// MAUI is moving toward Behaviors over Effects, so this provides a more modern API.
+///
+///
+/// XAML Usage:
+///
+/// <Button>
+/// <Button.Behaviors>
+/// <touch:TouchBehavior PressedScale="0.95" Command="{Binding TapCommand}" />
+/// </Button.Behaviors>
+/// </Button>
+///
+///
+public class TouchBehavior : Behavior
+{
+ private VisualElement? _associatedElement;
+
+ #region Bindable Properties
+
+ public static readonly BindableProperty CommandProperty =
+ BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(TouchBehavior));
+
+ public static readonly BindableProperty CommandParameterProperty =
+ BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(TouchBehavior));
+
+ public static readonly BindableProperty LongPressCommandProperty =
+ BindableProperty.Create(nameof(LongPressCommand), typeof(ICommand), typeof(TouchBehavior));
+
+ public static readonly BindableProperty LongPressCommandParameterProperty =
+ BindableProperty.Create(nameof(LongPressCommandParameter), typeof(object), typeof(TouchBehavior));
+
+ public static readonly BindableProperty LongPressDurationProperty =
+ BindableProperty.Create(nameof(LongPressDuration), typeof(int), typeof(TouchBehavior), TouchEffectConstants.Defaults.LongPressDuration);
+
+ public static readonly BindableProperty PressedScaleProperty =
+ BindableProperty.Create(nameof(PressedScale), typeof(double), typeof(TouchBehavior), TouchEffectConstants.Defaults.Scale);
+
+ public static readonly BindableProperty PressedOpacityProperty =
+ BindableProperty.Create(nameof(PressedOpacity), typeof(double), typeof(TouchBehavior), TouchEffectConstants.Defaults.Opacity);
+
+ public static readonly BindableProperty PressedBackgroundColorProperty =
+ BindableProperty.Create(nameof(PressedBackgroundColor), typeof(Color), typeof(TouchBehavior));
+
+ public static readonly BindableProperty HoveredScaleProperty =
+ BindableProperty.Create(nameof(HoveredScale), typeof(double), typeof(TouchBehavior), TouchEffectConstants.Defaults.Scale);
+
+ public static readonly BindableProperty HoveredOpacityProperty =
+ BindableProperty.Create(nameof(HoveredOpacity), typeof(double), typeof(TouchBehavior), TouchEffectConstants.Defaults.Opacity);
+
+ public static readonly BindableProperty HoveredBackgroundColorProperty =
+ BindableProperty.Create(nameof(HoveredBackgroundColor), typeof(Color), typeof(TouchBehavior));
+
+ public static readonly BindableProperty NormalScaleProperty =
+ BindableProperty.Create(nameof(NormalScale), typeof(double), typeof(TouchBehavior), TouchEffectConstants.Defaults.Scale);
+
+ public static readonly BindableProperty NormalOpacityProperty =
+ BindableProperty.Create(nameof(NormalOpacity), typeof(double), typeof(TouchBehavior), TouchEffectConstants.Defaults.Opacity);
+
+ public static readonly BindableProperty NormalBackgroundColorProperty =
+ BindableProperty.Create(nameof(NormalBackgroundColor), typeof(Color), typeof(TouchBehavior));
+
+ public static readonly BindableProperty AnimationDurationProperty =
+ BindableProperty.Create(nameof(AnimationDuration), typeof(int), typeof(TouchBehavior), TouchEffectConstants.PresetDurations.Fast);
+
+ public static readonly BindableProperty AnimationEasingProperty =
+ BindableProperty.Create(nameof(AnimationEasing), typeof(Easing), typeof(TouchBehavior));
+
+ public static readonly BindableProperty NativeAnimationProperty =
+ BindableProperty.Create(nameof(NativeAnimation), typeof(bool), typeof(TouchBehavior), false);
+
+ public static readonly BindableProperty NativeAnimationColorProperty =
+ BindableProperty.Create(nameof(NativeAnimationColor), typeof(Color), typeof(TouchBehavior));
+
+ public static readonly BindableProperty IsToggledProperty =
+ BindableProperty.Create(nameof(IsToggled), typeof(bool?), typeof(TouchBehavior), null, BindingMode.TwoWay);
+
+ public static readonly BindableProperty IsAvailableProperty =
+ BindableProperty.Create(nameof(IsAvailable), typeof(bool), typeof(TouchBehavior), true);
+
+ #endregion
+
+ #region Properties
+
+ public ICommand? Command
+ {
+ get => (ICommand?)GetValue(CommandProperty);
+ set => SetValue(CommandProperty, value);
+ }
+
+ public object? CommandParameter
+ {
+ get => GetValue(CommandParameterProperty);
+ set => SetValue(CommandParameterProperty, value);
+ }
+
+ public ICommand? LongPressCommand
+ {
+ get => (ICommand?)GetValue(LongPressCommandProperty);
+ set => SetValue(LongPressCommandProperty, value);
+ }
+
+ public object? LongPressCommandParameter
+ {
+ get => GetValue(LongPressCommandParameterProperty);
+ set => SetValue(LongPressCommandParameterProperty, value);
+ }
+
+ public int LongPressDuration
+ {
+ get => (int)GetValue(LongPressDurationProperty);
+ set => SetValue(LongPressDurationProperty, value);
+ }
+
+ public double PressedScale
+ {
+ get => (double)GetValue(PressedScaleProperty);
+ set => SetValue(PressedScaleProperty, value);
+ }
+
+ public double PressedOpacity
+ {
+ get => (double)GetValue(PressedOpacityProperty);
+ set => SetValue(PressedOpacityProperty, value);
+ }
+
+ public Color? PressedBackgroundColor
+ {
+ get => (Color?)GetValue(PressedBackgroundColorProperty);
+ set => SetValue(PressedBackgroundColorProperty, value);
+ }
+
+ public double HoveredScale
+ {
+ get => (double)GetValue(HoveredScaleProperty);
+ set => SetValue(HoveredScaleProperty, value);
+ }
+
+ public double HoveredOpacity
+ {
+ get => (double)GetValue(HoveredOpacityProperty);
+ set => SetValue(HoveredOpacityProperty, value);
+ }
+
+ public Color? HoveredBackgroundColor
+ {
+ get => (Color?)GetValue(HoveredBackgroundColorProperty);
+ set => SetValue(HoveredBackgroundColorProperty, value);
+ }
+
+ public double NormalScale
+ {
+ get => (double)GetValue(NormalScaleProperty);
+ set => SetValue(NormalScaleProperty, value);
+ }
+
+ public double NormalOpacity
+ {
+ get => (double)GetValue(NormalOpacityProperty);
+ set => SetValue(NormalOpacityProperty, value);
+ }
+
+ public Color? NormalBackgroundColor
+ {
+ get => (Color?)GetValue(NormalBackgroundColorProperty);
+ set => SetValue(NormalBackgroundColorProperty, value);
+ }
+
+ public int AnimationDuration
+ {
+ get => (int)GetValue(AnimationDurationProperty);
+ set => SetValue(AnimationDurationProperty, value);
+ }
+
+ public Easing? AnimationEasing
+ {
+ get => (Easing?)GetValue(AnimationEasingProperty);
+ set => SetValue(AnimationEasingProperty, value);
+ }
+
+ public bool NativeAnimation
+ {
+ get => (bool)GetValue(NativeAnimationProperty);
+ set => SetValue(NativeAnimationProperty, value);
+ }
+
+ public Color? NativeAnimationColor
+ {
+ get => (Color?)GetValue(NativeAnimationColorProperty);
+ set => SetValue(NativeAnimationColorProperty, value);
+ }
+
+ public bool? IsToggled
+ {
+ get => (bool?)GetValue(IsToggledProperty);
+ set => SetValue(IsToggledProperty, value);
+ }
+
+ public bool IsAvailable
+ {
+ get => (bool)GetValue(IsAvailableProperty);
+ set => SetValue(IsAvailableProperty, value);
+ }
+
+ #endregion
+
+ #region Behavior Lifecycle
+
+ protected override void OnAttachedTo(VisualElement bindable)
+ {
+ base.OnAttachedTo(bindable);
+ _associatedElement = bindable;
+
+ ApplyTouchEffect();
+ }
+
+ protected override void OnDetachingFrom(VisualElement bindable)
+ {
+ RemoveTouchEffect();
+ _associatedElement = null;
+
+ base.OnDetachingFrom(bindable);
+ }
+
+ protected override void OnPropertyChanged(string? propertyName = null)
+ {
+ base.OnPropertyChanged(propertyName);
+
+ // Reapply effect when properties change
+ if (_associatedElement != null && propertyName != null)
+ {
+ ApplyTouchEffect();
+ }
+ }
+
+ #endregion
+
+ #region Touch Effect Application
+
+ private void ApplyTouchEffect()
+ {
+ if (_associatedElement == null)
+ return;
+
+ // Apply all properties via the attached property system
+ TouchEffect.SetIsAvailable(_associatedElement, IsAvailable);
+
+ if (Command != null)
+ TouchEffect.SetCommand(_associatedElement, Command);
+ if (CommandParameter != null)
+ TouchEffect.SetCommandParameter(_associatedElement, CommandParameter);
+ if (LongPressCommand != null)
+ TouchEffect.SetLongPressCommand(_associatedElement, LongPressCommand);
+ if (LongPressCommandParameter != null)
+ TouchEffect.SetLongPressCommandParameter(_associatedElement, LongPressCommandParameter);
+
+ TouchEffect.SetLongPressDuration(_associatedElement, LongPressDuration);
+
+ // Visual properties
+ TouchEffect.SetPressedScale(_associatedElement, PressedScale);
+ TouchEffect.SetPressedOpacity(_associatedElement, PressedOpacity);
+ if (PressedBackgroundColor != null)
+ TouchEffect.SetPressedBackgroundColor(_associatedElement, PressedBackgroundColor);
+
+ TouchEffect.SetHoveredScale(_associatedElement, HoveredScale);
+ TouchEffect.SetHoveredOpacity(_associatedElement, HoveredOpacity);
+ if (HoveredBackgroundColor != null)
+ TouchEffect.SetHoveredBackgroundColor(_associatedElement, HoveredBackgroundColor);
+
+ TouchEffect.SetNormalScale(_associatedElement, NormalScale);
+ TouchEffect.SetNormalOpacity(_associatedElement, NormalOpacity);
+ if (NormalBackgroundColor != null)
+ TouchEffect.SetNormalBackgroundColor(_associatedElement, NormalBackgroundColor);
+
+ // Animation
+ TouchEffect.SetAnimationDuration(_associatedElement, AnimationDuration);
+ if (AnimationEasing != null)
+ TouchEffect.SetAnimationEasing(_associatedElement, AnimationEasing);
+
+ // Native
+ TouchEffect.SetNativeAnimation(_associatedElement, NativeAnimation);
+ if (NativeAnimationColor != null)
+ TouchEffect.SetNativeAnimationColor(_associatedElement, NativeAnimationColor);
+
+ // Toggle
+ if (IsToggled.HasValue)
+ TouchEffect.SetIsToggled(_associatedElement, IsToggled);
+ }
+
+ private void RemoveTouchEffect()
+ {
+ if (_associatedElement == null)
+ return;
+
+ // Remove any TouchEffect instances
+ var effects = _associatedElement.Effects;
+ for (int i = effects.Count - 1; i >= 0; i--)
+ {
+ if (effects[i] is TouchEffect)
+ {
+ effects.RemoveAt(i);
+ }
+ }
+ }
+
+ #endregion
+}
diff --git a/src/Maui.TouchEffect/TouchEffect.Accessors.cs b/src/Maui.TouchEffect/TouchEffect.Accessors.cs
new file mode 100644
index 0000000..f78ea69
--- /dev/null
+++ b/src/Maui.TouchEffect/TouchEffect.Accessors.cs
@@ -0,0 +1,450 @@
+using System.Windows.Input;
+using MarketAlly.TouchEffect.Maui.Enums;
+
+namespace MarketAlly.TouchEffect.Maui;
+
+///
+/// TouchEffect partial class containing all static Get/Set accessor methods.
+///
+public partial class TouchEffect
+{
+ #region State Accessors
+
+ public static bool GetIsAvailable(BindableObject? bindable)
+ => (bool)(bindable?.GetValue(IsAvailableProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetIsAvailable(BindableObject? bindable, bool value)
+ => bindable?.SetValue(IsAvailableProperty, value);
+
+ public static bool GetShouldMakeChildrenInputTransparent(BindableObject? bindable)
+ => (bool)(bindable?.GetValue(ShouldMakeChildrenInputTransparentProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetShouldMakeChildrenInputTransparent(BindableObject? bindable, bool value)
+ => bindable?.SetValue(ShouldMakeChildrenInputTransparentProperty, value);
+
+ public static TouchStatus GetStatus(BindableObject? bindable)
+ => (TouchStatus)(bindable?.GetValue(StatusProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetStatus(BindableObject? bindable, TouchStatus value)
+ => bindable?.SetValue(StatusProperty, value);
+
+ public static TouchState GetState(BindableObject? bindable)
+ => (TouchState)(bindable?.GetValue(StateProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetState(BindableObject? bindable, TouchState value)
+ => bindable?.SetValue(StateProperty, value);
+
+ public static TouchInteractionStatus GetInteractionStatus(BindableObject? bindable)
+ => (TouchInteractionStatus)(bindable?.GetValue(InteractionStatusProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetInteractionStatus(BindableObject? bindable, TouchInteractionStatus value)
+ => bindable?.SetValue(InteractionStatusProperty, value);
+
+ public static HoverStatus GetHoverStatus(BindableObject? bindable)
+ => (HoverStatus)(bindable?.GetValue(HoverStatusProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetHoverStatus(BindableObject? bindable, HoverStatus value)
+ => bindable?.SetValue(HoverStatusProperty, value);
+
+ public static HoverState GetHoverState(BindableObject? bindable)
+ => (HoverState)(bindable?.GetValue(HoverStateProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetHoverState(BindableObject? bindable, HoverState value)
+ => bindable?.SetValue(HoverStateProperty, value);
+
+ #endregion
+
+ #region Command Accessors
+
+ public static ICommand? GetCommand(BindableObject? bindable)
+ {
+ if (bindable == null) throw new ArgumentNullException(nameof(bindable));
+ return (ICommand?)bindable.GetValue(CommandProperty);
+ }
+
+ public static void SetCommand(BindableObject? bindable, ICommand value)
+ => bindable?.SetValue(CommandProperty, value);
+
+ public static ICommand? GetLongPressCommand(BindableObject? bindable)
+ {
+ if (bindable == null) throw new ArgumentNullException(nameof(bindable));
+ return (ICommand?)bindable.GetValue(LongPressCommandProperty);
+ }
+
+ public static void SetLongPressCommand(BindableObject? bindable, ICommand value)
+ => bindable?.SetValue(LongPressCommandProperty, value);
+
+ public static object? GetCommandParameter(BindableObject? bindable)
+ {
+ if (bindable == null) throw new ArgumentNullException(nameof(bindable));
+ return bindable.GetValue(CommandParameterProperty);
+ }
+
+ public static void SetCommandParameter(BindableObject? bindable, object value)
+ => bindable?.SetValue(CommandParameterProperty, value);
+
+ public static object? GetLongPressCommandParameter(BindableObject? bindable)
+ {
+ if (bindable == null) throw new ArgumentNullException(nameof(bindable));
+ return bindable.GetValue(LongPressCommandParameterProperty);
+ }
+
+ public static void SetLongPressCommandParameter(BindableObject? bindable, object value)
+ => bindable?.SetValue(LongPressCommandParameterProperty, value);
+
+ public static int GetLongPressDuration(BindableObject? bindable)
+ => (int)(bindable?.GetValue(LongPressDurationProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetLongPressDuration(BindableObject? bindable, int value)
+ => bindable?.SetValue(LongPressDurationProperty, value);
+
+ #endregion
+
+ #region Background Color Accessors
+
+ public static Color? GetNormalBackgroundColor(BindableObject? bindable)
+ => bindable?.GetValue(NormalBackgroundColorProperty) as Color;
+
+ public static void SetNormalBackgroundColor(BindableObject? bindable, Color value)
+ => bindable?.SetValue(NormalBackgroundColorProperty, value);
+
+ public static Color? GetHoveredBackgroundColor(BindableObject? bindable)
+ => bindable?.GetValue(HoveredBackgroundColorProperty) as Color;
+
+ public static void SetHoveredBackgroundColor(BindableObject? bindable, Color value)
+ => bindable?.SetValue(HoveredBackgroundColorProperty, value);
+
+ public static Color? GetPressedBackgroundColor(BindableObject? bindable)
+ => bindable?.GetValue(PressedBackgroundColorProperty) as Color;
+
+ public static void SetPressedBackgroundColor(BindableObject? bindable, Color value)
+ => bindable?.SetValue(PressedBackgroundColorProperty, value);
+
+ #endregion
+
+ #region Opacity Accessors
+
+ public static double GetNormalOpacity(BindableObject? bindable)
+ => (double)(bindable?.GetValue(NormalOpacityProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetNormalOpacity(BindableObject? bindable, double value)
+ => bindable?.SetValue(NormalOpacityProperty, value);
+
+ public static double GetHoveredOpacity(BindableObject? bindable)
+ => (double)(bindable?.GetValue(HoveredOpacityProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetHoveredOpacity(BindableObject? bindable, double value)
+ => bindable?.SetValue(HoveredOpacityProperty, value);
+
+ public static double GetPressedOpacity(BindableObject? bindable)
+ => (double)(bindable?.GetValue(PressedOpacityProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetPressedOpacity(BindableObject? bindable, double value)
+ => bindable?.SetValue(PressedOpacityProperty, value);
+
+ #endregion
+
+ #region Scale Accessors
+
+ public static double GetNormalScale(BindableObject? bindable)
+ => (double)(bindable?.GetValue(NormalScaleProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetNormalScale(BindableObject? bindable, double value)
+ => bindable?.SetValue(NormalScaleProperty, value);
+
+ public static double GetHoveredScale(BindableObject? bindable)
+ => (double)(bindable?.GetValue(HoveredScaleProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetHoveredScale(BindableObject? bindable, double value)
+ => bindable?.SetValue(HoveredScaleProperty, value);
+
+ public static double GetPressedScale(BindableObject? bindable)
+ => (double)(bindable?.GetValue(PressedScaleProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetPressedScale(BindableObject? bindable, double value)
+ => bindable?.SetValue(PressedScaleProperty, value);
+
+ #endregion
+
+ #region Translation Accessors
+
+ public static double GetNormalTranslationX(BindableObject? bindable)
+ => (double)(bindable?.GetValue(NormalTranslationXProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetNormalTranslationX(BindableObject? bindable, double value)
+ => bindable?.SetValue(NormalTranslationXProperty, value);
+
+ public static double GetHoveredTranslationX(BindableObject? bindable)
+ => (double)(bindable?.GetValue(HoveredTranslationXProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetHoveredTranslationX(BindableObject? bindable, double value)
+ => bindable?.SetValue(HoveredTranslationXProperty, value);
+
+ public static double GetPressedTranslationX(BindableObject? bindable)
+ => (double)(bindable?.GetValue(PressedTranslationXProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetPressedTranslationX(BindableObject? bindable, double value)
+ => bindable?.SetValue(PressedTranslationXProperty, value);
+
+ public static double GetNormalTranslationY(BindableObject? bindable)
+ => (double)(bindable?.GetValue(NormalTranslationYProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetNormalTranslationY(BindableObject? bindable, double value)
+ => bindable?.SetValue(NormalTranslationYProperty, value);
+
+ public static double GetHoveredTranslationY(BindableObject? bindable)
+ => (double)(bindable?.GetValue(HoveredTranslationYProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetHoveredTranslationY(BindableObject? bindable, double value)
+ => bindable?.SetValue(HoveredTranslationYProperty, value);
+
+ public static double GetPressedTranslationY(BindableObject? bindable)
+ => (double)(bindable?.GetValue(PressedTranslationYProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetPressedTranslationY(BindableObject? bindable, double value)
+ => bindable?.SetValue(PressedTranslationYProperty, value);
+
+ #endregion
+
+ #region Rotation Accessors
+
+ public static double GetNormalRotation(BindableObject? bindable)
+ => (double)(bindable?.GetValue(NormalRotationProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetNormalRotation(BindableObject? bindable, double value)
+ => bindable?.SetValue(NormalRotationProperty, value);
+
+ public static double GetHoveredRotation(BindableObject? bindable)
+ => (double)(bindable?.GetValue(HoveredRotationProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetHoveredRotation(BindableObject? bindable, double value)
+ => bindable?.SetValue(HoveredRotationProperty, value);
+
+ public static double GetPressedRotation(BindableObject? bindable)
+ => (double)(bindable?.GetValue(PressedRotationProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetPressedRotation(BindableObject? bindable, double value)
+ => bindable?.SetValue(PressedRotationProperty, value);
+
+ public static double GetNormalRotationX(BindableObject? bindable)
+ => (double)(bindable?.GetValue(NormalRotationXProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetNormalRotationX(BindableObject? bindable, double value)
+ => bindable?.SetValue(NormalRotationXProperty, value);
+
+ public static double GetHoveredRotationX(BindableObject? bindable)
+ => (double)(bindable?.GetValue(HoveredRotationXProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetHoveredRotationX(BindableObject? bindable, double value)
+ => bindable?.SetValue(HoveredRotationXProperty, value);
+
+ public static double GetPressedRotationX(BindableObject? bindable)
+ => (double)(bindable?.GetValue(PressedRotationXProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetPressedRotationX(BindableObject? bindable, double value)
+ => bindable?.SetValue(PressedRotationXProperty, value);
+
+ public static double GetNormalRotationY(BindableObject? bindable)
+ => (double)(bindable?.GetValue(NormalRotationYProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetNormalRotationY(BindableObject? bindable, double value)
+ => bindable?.SetValue(NormalRotationYProperty, value);
+
+ public static double GetHoveredRotationY(BindableObject? bindable)
+ => (double)(bindable?.GetValue(HoveredRotationYProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetHoveredRotationY(BindableObject? bindable, double value)
+ => bindable?.SetValue(HoveredRotationYProperty, value);
+
+ public static double GetPressedRotationY(BindableObject? bindable)
+ => (double)(bindable?.GetValue(PressedRotationYProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetPressedRotationY(BindableObject? bindable, double value)
+ => bindable?.SetValue(PressedRotationYProperty, value);
+
+ #endregion
+
+ #region Animation Accessors
+
+ public static int GetAnimationDuration(BindableObject? bindable)
+ => (int)(bindable?.GetValue(AnimationDurationProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetAnimationDuration(BindableObject? bindable, int value)
+ => bindable?.SetValue(AnimationDurationProperty, value);
+
+ public static Easing? GetAnimationEasing(BindableObject? bindable)
+ {
+ if (bindable == null) throw new ArgumentNullException(nameof(bindable));
+ return (Easing?)bindable.GetValue(AnimationEasingProperty);
+ }
+
+ public static void SetAnimationEasing(BindableObject? bindable, Easing? value)
+ => bindable?.SetValue(AnimationEasingProperty, value);
+
+ public static int GetPressedAnimationDuration(BindableObject? bindable)
+ => (int)(bindable?.GetValue(PressedAnimationDurationProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetPressedAnimationDuration(BindableObject? bindable, int value)
+ => bindable?.SetValue(PressedAnimationDurationProperty, value);
+
+ public static Easing? GetPressedAnimationEasing(BindableObject? bindable)
+ {
+ if (bindable == null) throw new ArgumentNullException(nameof(bindable));
+ return (Easing?)bindable.GetValue(PressedAnimationEasingProperty);
+ }
+
+ public static void SetPressedAnimationEasing(BindableObject? bindable, Easing? value)
+ => bindable?.SetValue(PressedAnimationEasingProperty, value);
+
+ public static int GetNormalAnimationDuration(BindableObject? bindable)
+ => (int)(bindable?.GetValue(NormalAnimationDurationProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetNormalAnimationDuration(BindableObject? bindable, int value)
+ => bindable?.SetValue(NormalAnimationDurationProperty, value);
+
+ public static Easing? GetNormalAnimationEasing(BindableObject? bindable)
+ {
+ if (bindable == null) throw new ArgumentNullException(nameof(bindable));
+ return (Easing?)bindable.GetValue(NormalAnimationEasingProperty);
+ }
+
+ public static void SetNormalAnimationEasing(BindableObject? bindable, Easing? value)
+ => bindable?.SetValue(NormalAnimationEasingProperty, value);
+
+ public static int GetHoveredAnimationDuration(BindableObject? bindable)
+ => (int)(bindable?.GetValue(HoveredAnimationDurationProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetHoveredAnimationDuration(BindableObject? bindable, int value)
+ => bindable?.SetValue(HoveredAnimationDurationProperty, value);
+
+ public static Easing? GetHoveredAnimationEasing(BindableObject? bindable)
+ {
+ if (bindable == null) throw new ArgumentNullException(nameof(bindable));
+ return (Easing?)bindable.GetValue(HoveredAnimationEasingProperty);
+ }
+
+ public static void SetHoveredAnimationEasing(BindableObject? bindable, Easing? value)
+ => bindable?.SetValue(HoveredAnimationEasingProperty, value);
+
+ public static int GetPulseCount(BindableObject? bindable)
+ => (int)(bindable?.GetValue(PulseCountProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetPulseCount(BindableObject? bindable, int value)
+ => bindable?.SetValue(PulseCountProperty, value);
+
+ #endregion
+
+ #region Toggle and Threshold Accessors
+
+ public static bool? GetIsToggled(BindableObject? bindable)
+ {
+ if (bindable == null) throw new ArgumentNullException(nameof(bindable));
+ return (bool?)bindable.GetValue(IsToggledProperty);
+ }
+
+ public static void SetIsToggled(BindableObject? bindable, bool? value)
+ => bindable?.SetValue(IsToggledProperty, value);
+
+ public static int GetDisallowTouchThreshold(BindableObject? bindable)
+ => (int)(bindable?.GetValue(DisallowTouchThresholdProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetDisallowTouchThreshold(BindableObject? bindable, int value)
+ => bindable?.SetValue(DisallowTouchThresholdProperty, value);
+
+ #endregion
+
+ #region Native Animation Accessors
+
+ public static bool GetNativeAnimation(BindableObject? bindable)
+ => (bool)(bindable?.GetValue(NativeAnimationProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetNativeAnimation(BindableObject? bindable, bool value)
+ => bindable?.SetValue(NativeAnimationProperty, value);
+
+ public static Color? GetNativeAnimationColor(BindableObject? bindable)
+ => bindable?.GetValue(NativeAnimationColorProperty) as Color;
+
+ public static void SetNativeAnimationColor(BindableObject? bindable, Color value)
+ => bindable?.SetValue(NativeAnimationColorProperty, value);
+
+ public static int GetNativeAnimationRadius(BindableObject? bindable)
+ => (int)(bindable?.GetValue(NativeAnimationRadiusProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetNativeAnimationRadius(BindableObject? bindable, int value)
+ => bindable?.SetValue(NativeAnimationRadiusProperty, value);
+
+ public static int GetNativeAnimationShadowRadius(BindableObject? bindable)
+ => (int)(bindable?.GetValue(NativeAnimationShadowRadiusProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetNativeAnimationShadowRadius(BindableObject? bindable, int value)
+ => bindable?.SetValue(NativeAnimationShadowRadiusProperty, value);
+
+ public static bool GetNativeAnimationBorderless(BindableObject? bindable)
+ => (bool)(bindable?.GetValue(NativeAnimationBorderlessProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetNativeAnimationBorderless(BindableObject? bindable, bool value)
+ => bindable?.SetValue(NativeAnimationBorderlessProperty, value);
+
+ #endregion
+
+ #region Background Image Accessors
+
+ public static ImageSource? GetNormalBackgroundImageSource(BindableObject? bindable)
+ {
+ if (bindable == null) throw new ArgumentNullException(nameof(bindable));
+ return (ImageSource?)bindable.GetValue(NormalBackgroundImageSourceProperty);
+ }
+
+ public static void SetNormalBackgroundImageSource(BindableObject? bindable, ImageSource value)
+ => bindable?.SetValue(NormalBackgroundImageSourceProperty, value);
+
+ public static ImageSource? GetHoveredBackgroundImageSource(BindableObject? bindable)
+ {
+ if (bindable == null) throw new ArgumentNullException(nameof(bindable));
+ return (ImageSource?)bindable.GetValue(HoveredBackgroundImageSourceProperty);
+ }
+
+ public static void SetHoveredBackgroundImageSource(BindableObject? bindable, ImageSource value)
+ => bindable?.SetValue(HoveredBackgroundImageSourceProperty, value);
+
+ public static ImageSource? GetPressedBackgroundImageSource(BindableObject? bindable)
+ {
+ if (bindable == null) throw new ArgumentNullException(nameof(bindable));
+ return (ImageSource?)bindable.GetValue(PressedBackgroundImageSourceProperty);
+ }
+
+ public static void SetPressedBackgroundImageSource(BindableObject? bindable, ImageSource value)
+ => bindable?.SetValue(PressedBackgroundImageSourceProperty, value);
+
+ public static Aspect GetBackgroundImageAspect(BindableObject? bindable)
+ => (Aspect)(bindable?.GetValue(BackgroundImageAspectProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetBackgroundImageAspect(BindableObject? bindable, Aspect value)
+ => bindable?.SetValue(BackgroundImageAspectProperty, value);
+
+ public static Aspect GetNormalBackgroundImageAspect(BindableObject? bindable)
+ => (Aspect)(bindable?.GetValue(NormalBackgroundImageAspectProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetNormalBackgroundImageAspect(BindableObject? bindable, Aspect value)
+ => bindable?.SetValue(NormalBackgroundImageAspectProperty, value);
+
+ public static Aspect GetHoveredBackgroundImageAspect(BindableObject? bindable)
+ => (Aspect)(bindable?.GetValue(HoveredBackgroundImageAspectProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetHoveredBackgroundImageAspect(BindableObject? bindable, Aspect value)
+ => bindable?.SetValue(HoveredBackgroundImageAspectProperty, value);
+
+ public static Aspect GetPressedBackgroundImageAspect(BindableObject? bindable)
+ => (Aspect)(bindable?.GetValue(PressedBackgroundImageAspectProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetPressedBackgroundImageAspect(BindableObject? bindable, Aspect value)
+ => bindable?.SetValue(PressedBackgroundImageAspectProperty, value);
+
+ public static bool GetShouldSetImageOnAnimationEnd(BindableObject? bindable)
+ => (bool)(bindable?.GetValue(ShouldSetImageOnAnimationEndProperty) ?? throw new ArgumentNullException(nameof(bindable)));
+
+ public static void SetShouldSetImageOnAnimationEnd(BindableObject? bindable, bool value)
+ => bindable?.SetValue(ShouldSetImageOnAnimationEndProperty, value);
+
+ #endregion
+}
diff --git a/src/Maui.TouchEffect/TouchEffect.Maui.csproj b/src/Maui.TouchEffect/TouchEffect.Maui.csproj
index bb60d04..913dd52 100644
--- a/src/Maui.TouchEffect/TouchEffect.Maui.csproj
+++ b/src/Maui.TouchEffect/TouchEffect.Maui.csproj
@@ -1,7 +1,7 @@
- net9.0-android;net9.0-ios;net9.0-windows10.0.19041.0
+ net10.0-android;net10.0-ios;net10.0-windows10.0.19041.0
true
true
@@ -26,10 +26,10 @@
Advanced touch effects for .NET MAUI applications. Features fluent builder API, 20+ preset configurations, Windows platform support, comprehensive logging, and rich interaction feedback with animations, hover states, and platform-specific effects. Enterprise-ready with zero magic numbers and extensive documentation. A MarketAlly product based on the original TouchEffect.
Copyright © 2025 MarketAlly. Original TouchEffect Copyright © 2018 Andrei
true
- 1.0.0
- 1.0.0
- 1.0.0
- v1.0.0: New fluent builder API, 20+ preset configurations, Windows platform support, centralized constants, enhanced error handling with logging interface, and comprehensive documentation. Fixed .NET 9 compatibility issues.
+ 2.0.0
+ 2.0.0
+ 2.0.0
+ v2.0.0: .NET 10 support, new TouchBehavior class (Behavior-based alternative to Effects), thread-safe architecture with proper synchronization, integrated logging via ITouchEffectLogger, code organization improvements (partial classes), ForceUpdateStateWithoutAnimation bug fix, performance improvements (LINQ replaced with for-loops in hot paths), proper CancellationTokenSource disposal pattern.
icon.png
13.0
@@ -39,8 +39,8 @@
-
-
+
+
true
\
@@ -50,7 +50,7 @@
-
+
diff --git a/src/Maui.TouchEffect/TouchEffect.Properties.cs b/src/Maui.TouchEffect/TouchEffect.Properties.cs
new file mode 100644
index 0000000..415d865
--- /dev/null
+++ b/src/Maui.TouchEffect/TouchEffect.Properties.cs
@@ -0,0 +1,475 @@
+using System.Windows.Input;
+using MarketAlly.TouchEffect.Maui.Enums;
+
+namespace MarketAlly.TouchEffect.Maui;
+
+///
+/// TouchEffect partial class containing all BindableProperty definitions.
+///
+public partial class TouchEffect
+{
+ #region State Properties
+
+ public static readonly BindableProperty IsAvailableProperty = BindableProperty.CreateAttached(
+ nameof(IsAvailable),
+ typeof(bool),
+ typeof(TouchEffect),
+ true,
+ propertyChanged: TryGenerateEffect);
+
+ public static readonly BindableProperty ShouldMakeChildrenInputTransparentProperty = BindableProperty.CreateAttached(
+ nameof(ShouldMakeChildrenInputTransparent),
+ typeof(bool),
+ typeof(TouchEffect),
+ true,
+ propertyChanged: SetChildrenInputTransparentAndTryGenerateEffect);
+
+ public static readonly BindableProperty StatusProperty = BindableProperty.CreateAttached(
+ nameof(Status),
+ typeof(TouchStatus),
+ typeof(TouchEffect),
+ TouchStatus.Completed,
+ BindingMode.OneWayToSource);
+
+ public static readonly BindableProperty StateProperty = BindableProperty.CreateAttached(
+ nameof(State),
+ typeof(TouchState),
+ typeof(TouchEffect),
+ TouchState.Normal,
+ BindingMode.OneWayToSource);
+
+ public static readonly BindableProperty InteractionStatusProperty = BindableProperty.CreateAttached(
+ nameof(InteractionStatus),
+ typeof(TouchInteractionStatus),
+ typeof(TouchEffect),
+ TouchInteractionStatus.Completed,
+ BindingMode.OneWayToSource);
+
+ public static readonly BindableProperty HoverStatusProperty = BindableProperty.CreateAttached(
+ nameof(HoverStatus),
+ typeof(HoverStatus),
+ typeof(TouchEffect),
+ Enums.HoverStatus.Exited,
+ BindingMode.OneWayToSource);
+
+ public static readonly BindableProperty HoverStateProperty = BindableProperty.CreateAttached(
+ nameof(HoverState),
+ typeof(HoverState),
+ typeof(TouchEffect),
+ Enums.HoverState.Normal,
+ BindingMode.OneWayToSource);
+
+ #endregion
+
+ #region Command Properties
+
+ public static readonly BindableProperty CommandProperty = BindableProperty.CreateAttached(
+ nameof(Command),
+ typeof(ICommand),
+ typeof(TouchEffect),
+ default(ICommand),
+ propertyChanged: TryGenerateEffect);
+
+ public static readonly BindableProperty LongPressCommandProperty = BindableProperty.CreateAttached(
+ nameof(LongPressCommand),
+ typeof(ICommand),
+ typeof(TouchEffect),
+ default(ICommand),
+ propertyChanged: TryGenerateEffect);
+
+ public static readonly BindableProperty CommandParameterProperty = BindableProperty.CreateAttached(
+ nameof(CommandParameter),
+ typeof(object),
+ typeof(TouchEffect),
+ default,
+ propertyChanged: TryGenerateEffect);
+
+ public static readonly BindableProperty LongPressCommandParameterProperty = BindableProperty.CreateAttached(
+ nameof(LongPressCommandParameter),
+ typeof(object),
+ typeof(TouchEffect),
+ default,
+ propertyChanged: TryGenerateEffect);
+
+ public static readonly BindableProperty LongPressDurationProperty = BindableProperty.CreateAttached(
+ nameof(LongPressDuration),
+ typeof(int),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.LongPressDuration,
+ propertyChanged: TryGenerateEffect);
+
+ #endregion
+
+ #region Background Color Properties
+
+ public static readonly BindableProperty NormalBackgroundColorProperty = BindableProperty.CreateAttached(
+ nameof(NormalBackgroundColor),
+ typeof(Color),
+ typeof(TouchEffect),
+ KnownColor.Default,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty HoveredBackgroundColorProperty = BindableProperty.CreateAttached(
+ nameof(HoveredBackgroundColor),
+ typeof(Color),
+ typeof(TouchEffect),
+ KnownColor.Default,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty PressedBackgroundColorProperty = BindableProperty.CreateAttached(
+ nameof(PressedBackgroundColor),
+ typeof(Color),
+ typeof(TouchEffect),
+ KnownColor.Default,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ #endregion
+
+ #region Opacity Properties
+
+ public static readonly BindableProperty NormalOpacityProperty = BindableProperty.CreateAttached(
+ nameof(NormalOpacity),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.Opacity,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty HoveredOpacityProperty = BindableProperty.CreateAttached(
+ nameof(HoveredOpacity),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.Opacity,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty PressedOpacityProperty = BindableProperty.CreateAttached(
+ nameof(PressedOpacity),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.Opacity,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ #endregion
+
+ #region Scale Properties
+
+ public static readonly BindableProperty NormalScaleProperty = BindableProperty.CreateAttached(
+ nameof(NormalScale),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.Scale,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty HoveredScaleProperty = BindableProperty.CreateAttached(
+ nameof(HoveredScale),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.Scale,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty PressedScaleProperty = BindableProperty.CreateAttached(
+ nameof(PressedScale),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.Scale,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ #endregion
+
+ #region Translation Properties
+
+ public static readonly BindableProperty NormalTranslationXProperty = BindableProperty.CreateAttached(
+ nameof(NormalTranslationX),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.TranslationX,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty HoveredTranslationXProperty = BindableProperty.CreateAttached(
+ nameof(HoveredTranslationX),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.TranslationX,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty PressedTranslationXProperty = BindableProperty.CreateAttached(
+ nameof(PressedTranslationX),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.TranslationX,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty NormalTranslationYProperty = BindableProperty.CreateAttached(
+ nameof(NormalTranslationY),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.TranslationY,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty HoveredTranslationYProperty = BindableProperty.CreateAttached(
+ nameof(HoveredTranslationY),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.TranslationY,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty PressedTranslationYProperty = BindableProperty.CreateAttached(
+ nameof(PressedTranslationY),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.TranslationY,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ #endregion
+
+ #region Rotation Properties
+
+ public static readonly BindableProperty NormalRotationProperty = BindableProperty.CreateAttached(
+ nameof(NormalRotation),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.Rotation,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty HoveredRotationProperty = BindableProperty.CreateAttached(
+ nameof(HoveredRotation),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.Rotation,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty PressedRotationProperty = BindableProperty.CreateAttached(
+ nameof(PressedRotation),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.Rotation,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty NormalRotationXProperty = BindableProperty.CreateAttached(
+ nameof(NormalRotationX),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.Rotation,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty HoveredRotationXProperty = BindableProperty.CreateAttached(
+ nameof(HoveredRotationX),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.Rotation,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty PressedRotationXProperty = BindableProperty.CreateAttached(
+ nameof(PressedRotationX),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.Rotation,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty NormalRotationYProperty = BindableProperty.CreateAttached(
+ nameof(NormalRotationY),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.Rotation,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty HoveredRotationYProperty = BindableProperty.CreateAttached(
+ nameof(HoveredRotationY),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.Rotation,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty PressedRotationYProperty = BindableProperty.CreateAttached(
+ nameof(PressedRotationY),
+ typeof(double),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.Rotation,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ #endregion
+
+ #region Animation Properties
+
+ public static readonly BindableProperty AnimationDurationProperty = BindableProperty.CreateAttached(
+ nameof(AnimationDuration),
+ typeof(int),
+ typeof(TouchEffect),
+ TouchEffectConstants.Animation.DefaultDuration,
+ propertyChanged: TryGenerateEffect);
+
+ public static readonly BindableProperty AnimationEasingProperty = BindableProperty.CreateAttached(
+ nameof(AnimationEasing),
+ typeof(Easing),
+ typeof(TouchEffect),
+ null,
+ propertyChanged: TryGenerateEffect);
+
+ public static readonly BindableProperty PressedAnimationDurationProperty = BindableProperty.CreateAttached(
+ nameof(PressedAnimationDuration),
+ typeof(int),
+ typeof(TouchEffect),
+ TouchEffectConstants.Animation.DefaultDuration,
+ propertyChanged: TryGenerateEffect);
+
+ public static readonly BindableProperty PressedAnimationEasingProperty = BindableProperty.CreateAttached(
+ nameof(PressedAnimationEasing),
+ typeof(Easing),
+ typeof(TouchEffect),
+ null,
+ propertyChanged: TryGenerateEffect);
+
+ public static readonly BindableProperty NormalAnimationDurationProperty = BindableProperty.CreateAttached(
+ nameof(NormalAnimationDuration),
+ typeof(int),
+ typeof(TouchEffect),
+ TouchEffectConstants.Animation.DefaultDuration,
+ propertyChanged: TryGenerateEffect);
+
+ public static readonly BindableProperty NormalAnimationEasingProperty = BindableProperty.CreateAttached(
+ nameof(NormalAnimationEasing),
+ typeof(Easing),
+ typeof(TouchEffect),
+ null,
+ propertyChanged: TryGenerateEffect);
+
+ public static readonly BindableProperty HoveredAnimationDurationProperty = BindableProperty.CreateAttached(
+ nameof(HoveredAnimationDuration),
+ typeof(int),
+ typeof(TouchEffect),
+ TouchEffectConstants.Animation.DefaultDuration,
+ propertyChanged: TryGenerateEffect);
+
+ public static readonly BindableProperty HoveredAnimationEasingProperty = BindableProperty.CreateAttached(
+ nameof(HoveredAnimationEasing),
+ typeof(Easing),
+ typeof(TouchEffect),
+ null,
+ propertyChanged: TryGenerateEffect);
+
+ public static readonly BindableProperty PulseCountProperty = BindableProperty.CreateAttached(
+ nameof(PulseCount),
+ typeof(int),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.PulseCount,
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ #endregion
+
+ #region Toggle and Threshold Properties
+
+ public static readonly BindableProperty IsToggledProperty = BindableProperty.CreateAttached(
+ nameof(IsToggled),
+ typeof(bool?),
+ typeof(TouchEffect),
+ default(bool?),
+ BindingMode.TwoWay,
+ propertyChanged: ForceUpdateStateWithoutAnimationAndTryGenerateEffect);
+
+ public static readonly BindableProperty DisallowTouchThresholdProperty = BindableProperty.CreateAttached(
+ nameof(DisallowTouchThreshold),
+ typeof(int),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.DisallowTouchThreshold,
+ propertyChanged: TryGenerateEffect);
+
+ #endregion
+
+ #region Native Animation Properties
+
+ public static readonly BindableProperty NativeAnimationProperty = BindableProperty.CreateAttached(
+ nameof(NativeAnimation),
+ typeof(bool),
+ typeof(TouchEffect),
+ false,
+ propertyChanged: TryGenerateEffect);
+
+ public static readonly BindableProperty NativeAnimationColorProperty = BindableProperty.CreateAttached(
+ nameof(NativeAnimationColor),
+ typeof(Color),
+ typeof(TouchEffect),
+ KnownColor.Default,
+ propertyChanged: TryGenerateEffect);
+
+ public static readonly BindableProperty NativeAnimationRadiusProperty = BindableProperty.CreateAttached(
+ nameof(NativeAnimationRadius),
+ typeof(int),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.NativeAnimationRadius,
+ propertyChanged: TryGenerateEffect);
+
+ public static readonly BindableProperty NativeAnimationShadowRadiusProperty = BindableProperty.CreateAttached(
+ nameof(NativeAnimationShadowRadius),
+ typeof(int),
+ typeof(TouchEffect),
+ TouchEffectConstants.Defaults.NativeAnimationShadowRadius,
+ propertyChanged: TryGenerateEffect);
+
+ public static readonly BindableProperty NativeAnimationBorderlessProperty = BindableProperty.CreateAttached(
+ nameof(NativeAnimationBorderless),
+ typeof(bool),
+ typeof(TouchEffect),
+ false,
+ propertyChanged: TryGenerateEffect);
+
+ #endregion
+
+ #region Background Image Properties
+
+ public static readonly BindableProperty NormalBackgroundImageSourceProperty = BindableProperty.CreateAttached(
+ nameof(NormalBackgroundImageSource),
+ typeof(ImageSource),
+ typeof(TouchEffect),
+ default(ImageSource),
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty HoveredBackgroundImageSourceProperty = BindableProperty.CreateAttached(
+ nameof(HoveredBackgroundImageSource),
+ typeof(ImageSource),
+ typeof(TouchEffect),
+ default(ImageSource),
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty PressedBackgroundImageSourceProperty = BindableProperty.CreateAttached(
+ nameof(PressedBackgroundImageSource),
+ typeof(ImageSource),
+ typeof(TouchEffect),
+ default(ImageSource),
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty BackgroundImageAspectProperty = BindableProperty.CreateAttached(
+ nameof(BackgroundImageAspect),
+ typeof(Aspect),
+ typeof(TouchEffect),
+ default(Aspect),
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty NormalBackgroundImageAspectProperty = BindableProperty.CreateAttached(
+ nameof(NormalBackgroundImageAspect),
+ typeof(Aspect),
+ typeof(TouchEffect),
+ default(Aspect),
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty HoveredBackgroundImageAspectProperty = BindableProperty.CreateAttached(
+ nameof(HoveredBackgroundImageAspect),
+ typeof(Aspect),
+ typeof(TouchEffect),
+ default(Aspect),
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty PressedBackgroundImageAspectProperty = BindableProperty.CreateAttached(
+ nameof(PressedBackgroundImageAspect),
+ typeof(Aspect),
+ typeof(TouchEffect),
+ default(Aspect),
+ propertyChanged: ForceUpdateStateAndTryGenerateEffect);
+
+ public static readonly BindableProperty ShouldSetImageOnAnimationEndProperty = BindableProperty.CreateAttached(
+ nameof(ShouldSetImageOnAnimationEnd),
+ typeof(bool),
+ typeof(TouchEffect),
+ default(bool),
+ propertyChanged: TryGenerateEffect);
+
+ #endregion
+}
diff --git a/src/Maui.TouchEffect/TouchEffect.cs b/src/Maui.TouchEffect/TouchEffect.cs
index 87b6771..bf4f04d 100644
--- a/src/Maui.TouchEffect/TouchEffect.cs
+++ b/src/Maui.TouchEffect/TouchEffect.cs
@@ -1,1174 +1,132 @@
-using System.Windows.Input;
-using Maui.TouchEffect.Enums;
-using MauiTouchEffect.Extensions;
-// ReSharper disable MemberCanBePrivate.Global
-namespace Maui.TouchEffect;
+using System.Windows.Input;
+using MarketAlly.TouchEffect.Maui.Enums;
+using MarketAlly.TouchEffect.Maui.Extensions;
+using MarketAlly.TouchEffect.Maui.Interfaces;
+namespace MarketAlly.TouchEffect.Maui;
+
+///
+/// TouchEffect provides touch and hover visual feedback for any VisualElement.
+/// This is the core partial class containing events, instance properties, and logic.
+///
public partial class TouchEffect : RoutingEffect
{
- public const string UnpressedVisualState = "Unpressed";
+ #region Visual State Constants
- public const string PressedVisualState = "Pressed";
+ public const string UnpressedVisualState = TouchEffectConstants.VisualStates.Unpressed;
+ public const string PressedVisualState = TouchEffectConstants.VisualStates.Pressed;
+ public const string HoveredVisualState = TouchEffectConstants.VisualStates.Hovered;
- public const string HoveredVisualState = "Hovered";
+ #endregion
+
+ #region Static Logger
+
+ private static ITouchEffectLogger _logger = NullTouchEffectLogger.Instance;
+
+ ///
+ /// Sets the logger instance for all TouchEffect operations.
+ ///
+ /// The logger to use. Pass null to disable logging.
+ public static void SetLogger(ITouchEffectLogger? logger)
+ {
+ _logger = logger ?? NullTouchEffectLogger.Instance;
+ }
+
+ ///
+ /// Gets the current logger instance.
+ ///
+ internal static ITouchEffectLogger Logger => _logger;
+
+ #endregion
+
+ #region Events
public event EventHandler StatusChanged
{
- add => weakEventManager.AddEventHandler(value);
- remove => weakEventManager.RemoveEventHandler(value);
+ add => _weakEventManager.AddEventHandler(value);
+ remove => _weakEventManager.RemoveEventHandler(value);
}
public event EventHandler StateChanged
{
- add => weakEventManager.AddEventHandler(value);
- remove => weakEventManager.RemoveEventHandler(value);
+ add => _weakEventManager.AddEventHandler(value);
+ remove => _weakEventManager.RemoveEventHandler(value);
}
public event EventHandler InteractionStatusChanged
{
- add => weakEventManager.AddEventHandler(value);
- remove => weakEventManager.RemoveEventHandler(value);
+ add => _weakEventManager.AddEventHandler(value);
+ remove => _weakEventManager.RemoveEventHandler(value);
}
public event EventHandler HoverStatusChanged
{
- add => weakEventManager.AddEventHandler(value);
- remove => weakEventManager.RemoveEventHandler(value);
+ add => _weakEventManager.AddEventHandler(value);
+ remove => _weakEventManager.RemoveEventHandler(value);
}
public event EventHandler HoverStateChanged
{
- add => weakEventManager.AddEventHandler(value);
- remove => weakEventManager.RemoveEventHandler(value);
+ add => _weakEventManager.AddEventHandler(value);
+ remove => _weakEventManager.RemoveEventHandler(value);
}
public event EventHandler Completed
{
- add => weakEventManager.AddEventHandler(value);
- remove => weakEventManager.RemoveEventHandler(value);
+ add => _weakEventManager.AddEventHandler(value);
+ remove => _weakEventManager.RemoveEventHandler(value);
}
public event EventHandler LongPressCompleted
{
- add => weakEventManager.AddEventHandler(value);
- remove => weakEventManager.RemoveEventHandler(value);
+ add => _weakEventManager.AddEventHandler(value);
+ remove => _weakEventManager.RemoveEventHandler(value);
}
- public readonly static BindableProperty IsAvailableProperty = BindableProperty.CreateAttached(
- nameof(IsAvailable),
- typeof(bool),
- typeof(TouchEffect),
- true,
- propertyChanged: TryGenerateEffect);
+ #endregion
- public readonly static BindableProperty ShouldMakeChildrenInputTransparentProperty = BindableProperty.CreateAttached(
- nameof(ShouldMakeChildrenInputTransparent),
- typeof(bool),
- typeof(TouchEffect),
- true,
- propertyChanged: SetChildrenInputTransparentAndTryGenerateEffect);
+ #region Fields
- public readonly static BindableProperty CommandProperty = BindableProperty.CreateAttached(
- nameof(Command),
- typeof(ICommand),
- typeof(TouchEffect),
- default(ICommand),
- propertyChanged: TryGenerateEffect);
+ private readonly GestureManager _gestureManager;
+ private readonly WeakEventManager _weakEventManager;
+ private VisualElement? _element;
- public readonly static BindableProperty LongPressCommandProperty = BindableProperty.CreateAttached(
- nameof(LongPressCommand),
- typeof(ICommand),
- typeof(TouchEffect),
- default(ICommand),
- propertyChanged: TryGenerateEffect);
+ #endregion
- public readonly static BindableProperty CommandParameterProperty = BindableProperty.CreateAttached(
- nameof(CommandParameter),
- typeof(object),
- typeof(TouchEffect),
- default,
- propertyChanged: TryGenerateEffect);
-
- public readonly static BindableProperty LongPressCommandParameterProperty = BindableProperty.CreateAttached(
- nameof(LongPressCommandParameter),
- typeof(object),
- typeof(TouchEffect),
- default,
- propertyChanged: TryGenerateEffect);
-
- public readonly static BindableProperty LongPressDurationProperty = BindableProperty.CreateAttached(
- nameof(LongPressDuration),
- typeof(int),
- typeof(TouchEffect),
- 500,
- propertyChanged: TryGenerateEffect);
-
- public readonly static BindableProperty StatusProperty = BindableProperty.CreateAttached(
- nameof(Status),
- typeof(TouchStatus),
- typeof(TouchEffect),
- TouchStatus.Completed,
- BindingMode.OneWayToSource);
-
- public readonly static BindableProperty StateProperty = BindableProperty.CreateAttached(
- nameof(State),
- typeof(TouchState),
- typeof(TouchEffect),
- TouchState.Normal,
- BindingMode.OneWayToSource);
-
- public readonly static BindableProperty InteractionStatusProperty = BindableProperty.CreateAttached(
- nameof(InteractionStatus),
- typeof(TouchInteractionStatus),
- typeof(TouchEffect),
- TouchInteractionStatus.Completed,
- BindingMode.OneWayToSource);
-
- public readonly static BindableProperty HoverStatusProperty = BindableProperty.CreateAttached(
- nameof(HoverStatus),
- typeof(HoverStatus),
- typeof(TouchEffect),
- HoverStatus.Exited,
- BindingMode.OneWayToSource);
-
- public readonly static BindableProperty HoverStateProperty = BindableProperty.CreateAttached(
- nameof(HoverState),
- typeof(HoverState),
- typeof(TouchEffect),
- HoverState.Normal,
- BindingMode.OneWayToSource);
-
- public readonly static BindableProperty NormalBackgroundColorProperty = BindableProperty.CreateAttached(
- nameof(NormalBackgroundColor),
- typeof(Color),
- typeof(TouchEffect),
- KnownColor.Default,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty HoveredBackgroundColorProperty = BindableProperty.CreateAttached(
- nameof(HoveredBackgroundColor),
- typeof(Color),
- typeof(TouchEffect),
- KnownColor.Default,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty PressedBackgroundColorProperty = BindableProperty.CreateAttached(
- nameof(PressedBackgroundColor),
- typeof(Color),
- typeof(TouchEffect),
- KnownColor.Default,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty NormalOpacityProperty = BindableProperty.CreateAttached(
- nameof(NormalOpacity),
- typeof(double),
- typeof(TouchEffect),
- 1.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty HoveredOpacityProperty = BindableProperty.CreateAttached(
- nameof(HoveredOpacity),
- typeof(double),
- typeof(TouchEffect),
- 1.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty PressedOpacityProperty = BindableProperty.CreateAttached(
- nameof(PressedOpacity),
- typeof(double),
- typeof(TouchEffect),
- 1.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty NormalScaleProperty = BindableProperty.CreateAttached(
- nameof(NormalScale),
- typeof(double),
- typeof(TouchEffect),
- 1.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty HoveredScaleProperty = BindableProperty.CreateAttached(
- nameof(HoveredScale),
- typeof(double),
- typeof(TouchEffect),
- 1.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty PressedScaleProperty = BindableProperty.CreateAttached(
- nameof(PressedScale),
- typeof(double),
- typeof(TouchEffect),
- 1.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty NormalTranslationXProperty = BindableProperty.CreateAttached(
- nameof(NormalTranslationX),
- typeof(double),
- typeof(TouchEffect),
- 0.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty HoveredTranslationXProperty = BindableProperty.CreateAttached(
- nameof(HoveredTranslationX),
- typeof(double),
- typeof(TouchEffect),
- 0.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty PressedTranslationXProperty = BindableProperty.CreateAttached(
- nameof(PressedTranslationX),
- typeof(double),
- typeof(TouchEffect),
- 0.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty NormalTranslationYProperty = BindableProperty.CreateAttached(
- nameof(NormalTranslationY),
- typeof(double),
- typeof(TouchEffect),
- 0.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty HoveredTranslationYProperty = BindableProperty.CreateAttached(
- nameof(HoveredTranslationY),
- typeof(double),
- typeof(TouchEffect),
- 0.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty PressedTranslationYProperty = BindableProperty.CreateAttached(
- nameof(PressedTranslationY),
- typeof(double),
- typeof(TouchEffect),
- 0.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty NormalRotationProperty = BindableProperty.CreateAttached(
- nameof(NormalRotation),
- typeof(double),
- typeof(TouchEffect),
- 0.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty HoveredRotationProperty = BindableProperty.CreateAttached(
- nameof(HoveredRotation),
- typeof(double),
- typeof(TouchEffect),
- 0.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty PressedRotationProperty = BindableProperty.CreateAttached(
- nameof(PressedRotation),
- typeof(double),
- typeof(TouchEffect),
- 0.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty NormalRotationXProperty = BindableProperty.CreateAttached(
- nameof(NormalRotationX),
- typeof(double),
- typeof(TouchEffect),
- 0.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty HoveredRotationXProperty = BindableProperty.CreateAttached(
- nameof(HoveredRotationX),
- typeof(double),
- typeof(TouchEffect),
- 0.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty PressedRotationXProperty = BindableProperty.CreateAttached(
- nameof(PressedRotationX),
- typeof(double),
- typeof(TouchEffect),
- 0.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty NormalRotationYProperty = BindableProperty.CreateAttached(
- nameof(NormalRotationY),
- typeof(double),
- typeof(TouchEffect),
- 0.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty HoveredRotationYProperty = BindableProperty.CreateAttached(
- nameof(HoveredRotationY),
- typeof(double),
- typeof(TouchEffect),
- 0.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty PressedRotationYProperty = BindableProperty.CreateAttached(
- nameof(PressedRotationY),
- typeof(double),
- typeof(TouchEffect),
- 0.0,
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty AnimationDurationProperty = BindableProperty.CreateAttached(
- nameof(AnimationDuration),
- typeof(int),
- typeof(TouchEffect),
- default(int),
- propertyChanged: TryGenerateEffect);
-
- public readonly static BindableProperty AnimationEasingProperty = BindableProperty.CreateAttached(
- nameof(AnimationEasing),
- typeof(Easing),
- typeof(TouchEffect),
- null,
- propertyChanged: TryGenerateEffect);
-
- public readonly static BindableProperty PressedAnimationDurationProperty = BindableProperty.CreateAttached(
- nameof(PressedAnimationDuration),
- typeof(int),
- typeof(TouchEffect),
- default(int),
- propertyChanged: TryGenerateEffect);
-
- public readonly static BindableProperty PressedAnimationEasingProperty = BindableProperty.CreateAttached(
- nameof(PressedAnimationEasing),
- typeof(Easing),
- typeof(TouchEffect),
- null,
- propertyChanged: TryGenerateEffect);
-
- public readonly static BindableProperty NormalAnimationDurationProperty = BindableProperty.CreateAttached(
- nameof(NormalAnimationDuration),
- typeof(int),
- typeof(TouchEffect),
- default(int),
- propertyChanged: TryGenerateEffect);
-
- public readonly static BindableProperty NormalAnimationEasingProperty = BindableProperty.CreateAttached(
- nameof(NormalAnimationEasing),
- typeof(Easing),
- typeof(TouchEffect),
- null,
- propertyChanged: TryGenerateEffect);
-
- public readonly static BindableProperty HoveredAnimationDurationProperty = BindableProperty.CreateAttached(
- nameof(HoveredAnimationDuration),
- typeof(int),
- typeof(TouchEffect),
- default(int),
- propertyChanged: TryGenerateEffect);
-
- public readonly static BindableProperty HoveredAnimationEasingProperty = BindableProperty.CreateAttached(
- nameof(HoveredAnimationEasing),
- typeof(Easing),
- typeof(TouchEffect),
- null,
- propertyChanged: TryGenerateEffect);
-
- public readonly static BindableProperty PulseCountProperty = BindableProperty.CreateAttached(
- nameof(PulseCount),
- typeof(int),
- typeof(TouchEffect),
- default(int),
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty IsToggledProperty = BindableProperty.CreateAttached(
- nameof(IsToggled),
- typeof(bool?),
- typeof(TouchEffect),
- default(bool?),
- BindingMode.TwoWay,
- propertyChanged: ForceUpdateStateWithoutAnimationAndTryGenerateEffect);
-
- public readonly static BindableProperty DisallowTouchThresholdProperty = BindableProperty.CreateAttached(
- nameof(DisallowTouchThreshold),
- typeof(int),
- typeof(TouchEffect),
- default(int),
- propertyChanged: TryGenerateEffect);
-
- public readonly static BindableProperty NativeAnimationProperty = BindableProperty.CreateAttached(
- nameof(NativeAnimation),
- typeof(bool),
- typeof(TouchEffect),
- false,
- propertyChanged: TryGenerateEffect);
-
- public readonly static BindableProperty NativeAnimationColorProperty = BindableProperty.CreateAttached(
- nameof(NativeAnimationColor),
- typeof(Color),
- typeof(TouchEffect),
- KnownColor.Default,
- propertyChanged: TryGenerateEffect);
-
- public readonly static BindableProperty NativeAnimationRadiusProperty = BindableProperty.CreateAttached(
- nameof(NativeAnimationRadius),
- typeof(int),
- typeof(TouchEffect),
- -1,
- propertyChanged: TryGenerateEffect);
-
- public readonly static BindableProperty NativeAnimationShadowRadiusProperty = BindableProperty.CreateAttached(
- nameof(NativeAnimationShadowRadius),
- typeof(int),
- typeof(TouchEffect),
- -1,
- propertyChanged: TryGenerateEffect);
-
- public readonly static BindableProperty NativeAnimationBorderlessProperty = BindableProperty.CreateAttached(
- nameof(NativeAnimationBorderless),
- typeof(bool),
- typeof(TouchEffect),
- false,
- propertyChanged: TryGenerateEffect);
-
- public readonly static BindableProperty NormalBackgroundImageSourceProperty = BindableProperty.CreateAttached(
- nameof(NormalBackgroundImageSource),
- typeof(ImageSource),
- typeof(TouchEffect),
- default(ImageSource),
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty HoveredBackgroundImageSourceProperty = BindableProperty.CreateAttached(
- nameof(HoveredBackgroundImageSource),
- typeof(ImageSource),
- typeof(TouchEffect),
- default(ImageSource),
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty PressedBackgroundImageSourceProperty = BindableProperty.CreateAttached(
- nameof(PressedBackgroundImageSource),
- typeof(ImageSource),
- typeof(TouchEffect),
- default(ImageSource),
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty BackgroundImageAspectProperty = BindableProperty.CreateAttached(
- nameof(BackgroundImageAspect),
- typeof(Aspect),
- typeof(TouchEffect),
- default(Aspect),
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty NormalBackgroundImageAspectProperty = BindableProperty.CreateAttached(
- nameof(NormalBackgroundImageAspect),
- typeof(Aspect),
- typeof(TouchEffect),
- default(Aspect),
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty HoveredBackgroundImageAspectProperty = BindableProperty.CreateAttached(
- nameof(HoveredBackgroundImageAspect),
- typeof(Aspect),
- typeof(TouchEffect),
- default(Aspect),
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty PressedBackgroundImageAspectProperty = BindableProperty.CreateAttached(
- nameof(PressedBackgroundImageAspect),
- typeof(Aspect),
- typeof(TouchEffect),
- default(Aspect),
- propertyChanged: ForceUpdateStateAndTryGenerateEffect);
-
- public readonly static BindableProperty ShouldSetImageOnAnimationEndProperty = BindableProperty.CreateAttached(
- nameof(ShouldSetImageOnAnimationEnd),
- typeof(bool),
- typeof(TouchEffect),
- default(bool),
- propertyChanged: TryGenerateEffect);
-
- private readonly GestureManager gestureManager;
-
- private readonly WeakEventManager weakEventManager;
-
- private VisualElement? element;
+ #region Constructor
public TouchEffect()
{
- gestureManager = new GestureManager();
- weakEventManager = new WeakEventManager();
+ _gestureManager = new GestureManager();
+ _weakEventManager = new WeakEventManager();
}
- public static bool GetIsAvailable(BindableObject? bindable)
- {
- return (bool)(bindable?.GetValue(IsAvailableProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetIsAvailable(BindableObject? bindable, bool value)
- {
- bindable?.SetValue(IsAvailableProperty, value);
- }
-
- public static bool GetShouldMakeChildrenInputTransparent(BindableObject? bindable)
- {
- return (bool)(bindable?.GetValue(ShouldMakeChildrenInputTransparentProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetShouldMakeChildrenInputTransparent(BindableObject? bindable, bool value)
- {
- bindable?.SetValue(ShouldMakeChildrenInputTransparentProperty, value);
- }
-
- public static ICommand? GetCommand(BindableObject? bindable)
- {
- if (bindable == null)
- {
- throw new ArgumentNullException(nameof(bindable));
- }
-
- return (ICommand?)bindable.GetValue(CommandProperty);
- }
-
- public static void SetCommand(BindableObject? bindable, ICommand value)
- {
- bindable?.SetValue(CommandProperty, value);
- }
-
- public static ICommand? GetLongPressCommand(BindableObject? bindable)
- {
- if (bindable == null)
- {
- throw new ArgumentNullException(nameof(bindable));
- }
-
- return (ICommand?)bindable.GetValue(LongPressCommandProperty);
- }
-
- public static void SetLongPressCommand(BindableObject? bindable, ICommand value)
- {
- bindable?.SetValue(LongPressCommandProperty, value);
- }
-
- public static object? GetCommandParameter(BindableObject? bindable)
- {
- if (bindable == null)
- {
- throw new ArgumentNullException(nameof(bindable));
- }
-
- return bindable.GetValue(CommandParameterProperty);
- }
-
- public static void SetCommandParameter(BindableObject? bindable, object value)
- {
- bindable?.SetValue(CommandParameterProperty, value);
- }
-
- public static object? GetLongPressCommandParameter(BindableObject? bindable)
- {
- if (bindable == null)
- {
- throw new ArgumentNullException(nameof(bindable));
- }
-
- return bindable.GetValue(LongPressCommandParameterProperty);
- }
-
- public static void SetLongPressCommandParameter(BindableObject? bindable, object value)
- {
- bindable?.SetValue(LongPressCommandParameterProperty, value);
- }
-
- public static int GetLongPressDuration(BindableObject? bindable)
- {
- return (int)(bindable?.GetValue(LongPressDurationProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetLongPressDuration(BindableObject? bindable, int value)
- {
- bindable?.SetValue(LongPressDurationProperty, value);
- }
-
- public static TouchStatus GetStatus(BindableObject? bindable)
- {
- return (TouchStatus)(bindable?.GetValue(StatusProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetStatus(BindableObject? bindable, TouchStatus value)
- {
- bindable?.SetValue(StatusProperty, value);
- }
-
- public static TouchState GetState(BindableObject? bindable)
- {
- return (TouchState)(bindable?.GetValue(StateProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetState(BindableObject? bindable, TouchState value)
- {
- bindable?.SetValue(StateProperty, value);
- }
-
- public static TouchInteractionStatus GetInteractionStatus(BindableObject? bindable)
- {
- return (TouchInteractionStatus)(bindable?.GetValue(InteractionStatusProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetInteractionStatus(BindableObject? bindable, TouchInteractionStatus value)
- {
- bindable?.SetValue(InteractionStatusProperty, value);
- }
-
- public static HoverStatus GetHoverStatus(BindableObject? bindable)
- {
- return (HoverStatus)(bindable?.GetValue(HoverStatusProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetHoverStatus(BindableObject? bindable, HoverStatus value)
- {
- bindable?.SetValue(HoverStatusProperty, value);
- }
-
- public static HoverState GetHoverState(BindableObject? bindable)
- {
- return (HoverState)(bindable?.GetValue(HoverStateProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetHoverState(BindableObject? bindable, HoverState value)
- {
- bindable?.SetValue(HoverStateProperty, value);
- }
-
- public static Color? GetNormalBackgroundColor(BindableObject? bindable)
- {
- return bindable?.GetValue(NormalBackgroundColorProperty) as Color;
- }
-
- public static void SetNormalBackgroundColor(BindableObject? bindable, Color value)
- {
- bindable?.SetValue(NormalBackgroundColorProperty, value);
- }
-
- public static Color? GetHoveredBackgroundColor(BindableObject? bindable)
- {
- return bindable?.GetValue(HoveredBackgroundColorProperty) as Color;
- }
-
- public static void SetHoveredBackgroundColor(BindableObject? bindable, Color value)
- {
- bindable?.SetValue(HoveredBackgroundColorProperty, value);
- }
-
- public static Color? GetPressedBackgroundColor(BindableObject? bindable)
- {
- return bindable?.GetValue(PressedBackgroundColorProperty) as Color;
- }
-
- public static void SetPressedBackgroundColor(BindableObject? bindable, Color value)
- {
- bindable?.SetValue(PressedBackgroundColorProperty, value);
- }
-
- public static double GetNormalOpacity(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(NormalOpacityProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetNormalOpacity(BindableObject? bindable, double value)
- {
- bindable?.SetValue(NormalOpacityProperty, value);
- }
-
- public static double GetHoveredOpacity(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(HoveredOpacityProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetHoveredOpacity(BindableObject? bindable, double value)
- {
- bindable?.SetValue(HoveredOpacityProperty, value);
- }
-
- public static double GetPressedOpacity(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(PressedOpacityProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetPressedOpacity(BindableObject? bindable, double value)
- {
- bindable?.SetValue(PressedOpacityProperty, value);
- }
-
- public static double GetNormalScale(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(NormalScaleProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetNormalScale(BindableObject? bindable, double value)
- {
- bindable?.SetValue(NormalScaleProperty, value);
- }
-
- public static double GetHoveredScale(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(HoveredScaleProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetHoveredScale(BindableObject? bindable, double value)
- {
- bindable?.SetValue(HoveredScaleProperty, value);
- }
-
- public static double GetPressedScale(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(PressedScaleProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetPressedScale(BindableObject? bindable, double value)
- {
- bindable?.SetValue(PressedScaleProperty, value);
- }
-
- public static double GetNormalTranslationX(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(NormalTranslationXProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetNormalTranslationX(BindableObject? bindable, double value)
- {
- bindable?.SetValue(NormalTranslationXProperty, value);
- }
-
- public static double GetHoveredTranslationX(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(HoveredTranslationXProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetHoveredTranslationX(BindableObject? bindable, double value)
- {
- bindable?.SetValue(HoveredTranslationXProperty, value);
- }
-
- public static double GetPressedTranslationX(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(PressedTranslationXProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetPressedTranslationX(BindableObject? bindable, double value)
- {
- bindable?.SetValue(PressedTranslationXProperty, value);
- }
-
- public static double GetNormalTranslationY(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(NormalTranslationYProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetNormalTranslationY(BindableObject? bindable, double value)
- {
- bindable?.SetValue(NormalTranslationYProperty, value);
- }
-
- public static double GetHoveredTranslationY(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(HoveredTranslationYProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetHoveredTranslationY(BindableObject? bindable, double value)
- {
- bindable?.SetValue(HoveredTranslationYProperty, value);
- }
-
- public static double GetPressedTranslationY(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(PressedTranslationYProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetPressedTranslationY(BindableObject? bindable, double value)
- {
- bindable?.SetValue(PressedTranslationYProperty, value);
- }
-
- public static double GetNormalRotation(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(NormalRotationProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetNormalRotation(BindableObject? bindable, double value)
- {
- bindable?.SetValue(NormalRotationProperty, value);
- }
-
- public static double GetHoveredRotation(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(HoveredRotationProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetHoveredRotation(BindableObject? bindable, double value)
- {
- bindable?.SetValue(HoveredRotationProperty, value);
- }
-
- public static double GetPressedRotation(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(PressedRotationProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetPressedRotation(BindableObject? bindable, double value)
- {
- bindable?.SetValue(PressedRotationProperty, value);
- }
-
- public static double GetNormalRotationX(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(NormalRotationXProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
+ #endregion
- public static void SetNormalRotationX(BindableObject? bindable, double value)
- {
- bindable?.SetValue(NormalRotationXProperty, value);
- }
-
- public static double GetHoveredRotationX(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(HoveredRotationXProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetHoveredRotationX(BindableObject? bindable, double value)
- {
- bindable?.SetValue(HoveredRotationXProperty, value);
- }
-
- public static double GetPressedRotationX(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(PressedRotationXProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetPressedRotationX(BindableObject? bindable, double value)
- {
- bindable?.SetValue(PressedRotationXProperty, value);
- }
-
- public static double GetNormalRotationY(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(NormalRotationYProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetNormalRotationY(BindableObject? bindable, double value)
- {
- bindable?.SetValue(NormalRotationYProperty, value);
- }
-
- public static double GetHoveredRotationY(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(HoveredRotationYProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetHoveredRotationY(BindableObject? bindable, double value)
- {
- bindable?.SetValue(HoveredRotationYProperty, value);
- }
-
- public static double GetPressedRotationY(BindableObject? bindable)
- {
- return (double)(bindable?.GetValue(PressedRotationYProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetPressedRotationY(BindableObject? bindable, double value)
- {
- bindable?.SetValue(PressedRotationYProperty, value);
- }
-
- public static int GetAnimationDuration(BindableObject? bindable)
- {
- return (int)(bindable?.GetValue(AnimationDurationProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetAnimationDuration(BindableObject? bindable, int value)
- {
- bindable?.SetValue(AnimationDurationProperty, value);
- }
-
- public static Easing? GetAnimationEasing(BindableObject? bindable)
- {
- if (bindable == null)
- {
- throw new ArgumentNullException(nameof(bindable));
- }
-
- return (Easing?)bindable.GetValue(AnimationEasingProperty);
- }
-
- public static void SetAnimationEasing(BindableObject? bindable, Easing? value)
- {
- bindable?.SetValue(AnimationEasingProperty, value);
- }
-
- public static int GetPressedAnimationDuration(BindableObject? bindable)
- {
- return (int)(bindable?.GetValue(PressedAnimationDurationProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetPressedAnimationDuration(BindableObject? bindable, int value)
- {
- bindable?.SetValue(PressedAnimationDurationProperty, value);
- }
-
- public static Easing? GetPressedAnimationEasing(BindableObject? bindable)
- {
- if (bindable == null)
- {
- throw new ArgumentNullException(nameof(bindable));
- }
-
- return (Easing?)bindable.GetValue(PressedAnimationEasingProperty);
- }
-
- public static void SetPressedAnimationEasing(BindableObject? bindable, Easing? value)
- {
- bindable?.SetValue(PressedAnimationEasingProperty, value);
- }
-
- public static int GetNormalAnimationDuration(BindableObject? bindable)
- {
- return (int)(bindable?.GetValue(NormalAnimationDurationProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetNormalAnimationDuration(BindableObject? bindable, int value)
- {
- bindable?.SetValue(NormalAnimationDurationProperty, value);
- }
-
- public static Easing? GetNormalAnimationEasing(BindableObject? bindable)
- {
- if (bindable == null)
- {
- throw new ArgumentNullException(nameof(bindable));
- }
-
- return (Easing?)bindable.GetValue(NormalAnimationEasingProperty);
- }
-
- public static void SetNormalAnimationEasing(BindableObject? bindable, Easing? value)
- {
- bindable?.SetValue(NormalAnimationEasingProperty, value);
- }
-
- public static int GetHoveredAnimationDuration(BindableObject? bindable)
- {
- return (int)(bindable?.GetValue(HoveredAnimationDurationProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetHoveredAnimationDuration(BindableObject? bindable, int value)
- {
- bindable?.SetValue(HoveredAnimationDurationProperty, value);
- }
-
- public static Easing? GetHoveredAnimationEasing(BindableObject? bindable)
- {
- if (bindable == null)
- {
- throw new ArgumentNullException(nameof(bindable));
- }
-
- return (Easing?)bindable.GetValue(HoveredAnimationEasingProperty);
- }
-
- public static void SetHoveredAnimationEasing(BindableObject? bindable, Easing? value)
- {
- bindable?.SetValue(HoveredAnimationEasingProperty, value);
- }
-
- public static int GetPulseCount(BindableObject? bindable)
- {
- return (int)(bindable?.GetValue(PulseCountProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetPulseCount(BindableObject? bindable, int value)
- {
- bindable?.SetValue(PulseCountProperty, value);
- }
-
- public static bool? GetIsToggled(BindableObject? bindable)
- {
- if (bindable == null)
- {
- throw new ArgumentNullException(nameof(bindable));
- }
-
- return (bool?)bindable.GetValue(IsToggledProperty);
- }
-
- public static void SetIsToggled(BindableObject? bindable, bool? value)
- {
- bindable?.SetValue(IsToggledProperty, value);
- }
-
- public static int GetDisallowTouchThreshold(BindableObject? bindable)
- {
- return (int)(bindable?.GetValue(DisallowTouchThresholdProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetDisallowTouchThreshold(BindableObject? bindable, int value)
- {
- bindable?.SetValue(DisallowTouchThresholdProperty, value);
- }
-
- public static bool GetNativeAnimation(BindableObject? bindable)
- {
- return (bool)(bindable?.GetValue(NativeAnimationProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetNativeAnimation(BindableObject? bindable, bool value)
- {
- bindable?.SetValue(NativeAnimationProperty, value);
- }
-
- public static Color? GetNativeAnimationColor(BindableObject? bindable)
- {
- return bindable?.GetValue(NativeAnimationColorProperty) as Color;
- }
-
- public static void SetNativeAnimationColor(BindableObject? bindable, Color value)
- {
- bindable?.SetValue(NativeAnimationColorProperty, value);
- }
-
- public static int GetNativeAnimationRadius(BindableObject? bindable)
- {
- return (int)(bindable?.GetValue(NativeAnimationRadiusProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetNativeAnimationRadius(BindableObject? bindable, int value)
- {
- bindable?.SetValue(NativeAnimationRadiusProperty, value);
- }
-
- public static int GetNativeAnimationShadowRadius(BindableObject? bindable)
- {
- return (int)(bindable?.GetValue(NativeAnimationShadowRadiusProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetNativeAnimationShadowRadius(BindableObject? bindable, int value)
- {
- bindable?.SetValue(NativeAnimationShadowRadiusProperty, value);
- }
-
- public static bool GetNativeAnimationBorderless(BindableObject? bindable)
- {
- return (bool)(bindable?.GetValue(NativeAnimationBorderlessProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetNativeAnimationBorderless(BindableObject? bindable, bool value)
- {
- bindable?.SetValue(NativeAnimationBorderlessProperty, value);
- }
-
- public static ImageSource? GetNormalBackgroundImageSource(BindableObject? bindable)
- {
- if (bindable == null)
- {
- throw new ArgumentNullException(nameof(bindable));
- }
-
- return (ImageSource?)bindable.GetValue(NormalBackgroundImageSourceProperty);
- }
-
- public static void SetNormalBackgroundImageSource(BindableObject? bindable, ImageSource value)
- {
- bindable?.SetValue(NormalBackgroundImageSourceProperty, value);
- }
-
- public static ImageSource? GetHoveredBackgroundImageSource(BindableObject? bindable)
- {
- if (bindable == null)
- {
- throw new ArgumentNullException(nameof(bindable));
- }
-
- return (ImageSource?)bindable.GetValue(HoveredBackgroundImageSourceProperty);
- }
-
- public static void SetHoveredBackgroundImageSource(BindableObject? bindable, ImageSource value)
- {
- bindable?.SetValue(HoveredBackgroundImageSourceProperty, value);
- }
-
- public static ImageSource? GetPressedBackgroundImageSource(BindableObject? bindable)
- {
- if (bindable == null)
- {
- throw new ArgumentNullException(nameof(bindable));
- }
-
- return (ImageSource?)bindable.GetValue(PressedBackgroundImageSourceProperty);
- }
-
- public static void SetPressedBackgroundImageSource(BindableObject? bindable, ImageSource value)
- {
- bindable?.SetValue(PressedBackgroundImageSourceProperty, value);
- }
-
- public static Aspect GetBackgroundImageAspect(BindableObject? bindable)
- {
- return (Aspect)(bindable?.GetValue(BackgroundImageAspectProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetBackgroundImageAspect(BindableObject? bindable, Aspect value)
- {
- bindable?.SetValue(BackgroundImageAspectProperty, value);
- }
-
- public static Aspect GetNormalBackgroundImageAspect(BindableObject? bindable)
- {
- return (Aspect)(bindable?.GetValue(NormalBackgroundImageAspectProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetNormalBackgroundImageAspect(BindableObject? bindable, Aspect value)
- {
- bindable?.SetValue(NormalBackgroundImageAspectProperty, value);
- }
-
- public static Aspect GetHoveredBackgroundImageAspect(BindableObject? bindable)
- {
- return (Aspect)(bindable?.GetValue(HoveredBackgroundImageAspectProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetHoveredBackgroundImageAspect(BindableObject? bindable, Aspect value)
- {
- bindable?.SetValue(HoveredBackgroundImageAspectProperty, value);
- }
-
- public static Aspect GetPressedBackgroundImageAspect(BindableObject? bindable)
- {
- return (Aspect)(bindable?.GetValue(PressedBackgroundImageAspectProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetPressedBackgroundImageAspect(BindableObject? bindable, Aspect value)
- {
- bindable?.SetValue(PressedBackgroundImageAspectProperty, value);
- }
-
- public static bool GetShouldSetImageOnAnimationEnd(BindableObject? bindable)
- {
- return (bool)(bindable?.GetValue(ShouldSetImageOnAnimationEndProperty) ?? throw new ArgumentNullException(nameof(bindable)));
- }
-
- public static void SetShouldSetImageOnAnimationEnd(BindableObject? bindable, bool value)
- {
- bindable?.SetValue(ShouldSetImageOnAnimationEndProperty, value);
- }
+ #region Property Changed Handlers
private static void TryGenerateEffect(BindableObject? bindable, object oldValue, object newValue)
{
- if (bindable is not VisualElement view || view.Effects.OfType().Any())
- {
+ if (bindable is not VisualElement view)
return;
- }
- view.Effects.Add(new TouchEffect
- {
- IsAutoGenerated = true,
- });
+ // Optimize: Use cached check instead of LINQ
+ if (HasTouchEffect(view))
+ return;
+
+ view.Effects.Add(new TouchEffect { IsAutoGenerated = true });
}
private static void ForceUpdateStateAndTryGenerateEffect(BindableObject bindable, object oldValue, object newValue)
{
- GetFrom(bindable)?.ForceUpdateState();
+ GetFrom(bindable)?.ForceUpdateState(animated: true);
TryGenerateEffect(bindable, oldValue, newValue);
}
private static void ForceUpdateStateWithoutAnimationAndTryGenerateEffect(BindableObject bindable, object oldValue, object newValue)
{
- GetFrom(bindable)?.ForceUpdateState();
+ // FIX: Pass animated: false to actually disable animation
+ GetFrom(bindable)?.ForceUpdateState(animated: false);
TryGenerateEffect(bindable, oldValue, newValue);
}
@@ -1178,24 +136,57 @@ public partial class TouchEffect : RoutingEffect
TryGenerateEffect(bindable, oldValue, newValue);
}
+ #endregion
+
+ #region Internal Properties
+
internal bool IsDisabled { get; set; }
-
internal bool IsUsed { get; set; }
-
internal bool IsAutoGenerated { get; set; }
+ internal new VisualElement? Element
+ {
+ get => _element;
+ set
+ {
+ if (_element != null)
+ {
+ IsUsed = false;
+ _gestureManager.Reset();
+ SetChildrenInputTransparent(false);
+ }
+
+ _gestureManager.AbortAnimations(this);
+ _element = value;
+
+ if (value != null)
+ {
+ SetChildrenInputTransparent(ShouldMakeChildrenInputTransparent);
+ if (!IsAutoGenerated)
+ {
+ IsUsed = true;
+ DisableOtherTouchEffects(value);
+ }
+
+ ForceUpdateState();
+ }
+ }
+ }
+
+ internal bool CanExecute => IsAvailable
+ && (Element?.IsEnabled ?? false)
+ && (Command?.CanExecute(CommandParameter) ?? true);
+
+ #endregion
+
+ #region Instance Property Accessors
+
public bool IsAvailable => GetIsAvailable(Element);
-
public bool ShouldMakeChildrenInputTransparent => GetShouldMakeChildrenInputTransparent(Element);
-
public ICommand? Command => GetCommand(Element);
-
public ICommand? LongPressCommand => GetLongPressCommand(Element);
-
public object? CommandParameter => GetCommandParameter(Element);
-
public object? LongPressCommandParameter => GetLongPressCommandParameter(Element);
-
public int LongPressDuration => GetLongPressDuration(Element);
public TouchStatus Status
@@ -1229,81 +220,43 @@ public partial class TouchEffect : RoutingEffect
}
public int DisallowTouchThreshold => GetDisallowTouchThreshold(Element);
-
public bool NativeAnimation => GetNativeAnimation(Element);
-
public Color? NativeAnimationColor => GetNativeAnimationColor(Element);
-
public int NativeAnimationRadius => GetNativeAnimationRadius(Element);
-
public int NativeAnimationShadowRadius => GetNativeAnimationShadowRadius(Element);
-
public bool NativeAnimationBorderless => GetNativeAnimationBorderless(Element);
-
public Color? NormalBackgroundColor => GetNormalBackgroundColor(Element);
-
public Color? HoveredBackgroundColor => GetHoveredBackgroundColor(Element);
-
public Color? PressedBackgroundColor => GetPressedBackgroundColor(Element);
-
public double NormalOpacity => GetNormalOpacity(Element);
-
public double HoveredOpacity => GetHoveredOpacity(Element);
-
public double PressedOpacity => GetPressedOpacity(Element);
-
public double NormalScale => GetNormalScale(Element);
-
public double HoveredScale => GetHoveredScale(Element);
-
public double PressedScale => GetPressedScale(Element);
-
public double NormalTranslationX => GetNormalTranslationX(Element);
-
public double HoveredTranslationX => GetHoveredTranslationX(Element);
-
public double PressedTranslationX => GetPressedTranslationX(Element);
-
public double NormalTranslationY => GetNormalTranslationY(Element);
-
public double HoveredTranslationY => GetHoveredTranslationY(Element);
-
public double PressedTranslationY => GetPressedTranslationY(Element);
-
public double NormalRotation => GetNormalRotation(Element);
-
public double HoveredRotation => GetHoveredRotation(Element);
-
public double PressedRotation => GetPressedRotation(Element);
-
public double NormalRotationX => GetNormalRotationX(Element);
-
public double HoveredRotationX => GetHoveredRotationX(Element);
-
public double PressedRotationX => GetPressedRotationX(Element);
-
public double NormalRotationY => GetNormalRotationY(Element);
-
public double HoveredRotationY => GetHoveredRotationY(Element);
-
public double PressedRotationY => GetPressedRotationY(Element);
-
public int AnimationDuration => GetAnimationDuration(Element);
-
public Easing? AnimationEasing => GetAnimationEasing(Element);
-
public int PressedAnimationDuration => GetPressedAnimationDuration(Element);
-
public Easing? PressedAnimationEasing => GetPressedAnimationEasing(Element);
-
public int NormalAnimationDuration => GetNormalAnimationDuration(Element);
-
public Easing? NormalAnimationEasing => GetNormalAnimationEasing(Element);
-
public int HoveredAnimationDuration => GetHoveredAnimationDuration(Element);
-
public Easing? HoveredAnimationEasing => GetHoveredAnimationEasing(Element);
-
public int PulseCount => GetPulseCount(Element);
public bool? IsToggled
@@ -1313,73 +266,109 @@ public partial class TouchEffect : RoutingEffect
}
public ImageSource? NormalBackgroundImageSource => GetNormalBackgroundImageSource(Element);
-
public ImageSource? HoveredBackgroundImageSource => GetHoveredBackgroundImageSource(Element);
-
public ImageSource? PressedBackgroundImageSource => GetPressedBackgroundImageSource(Element);
-
public Aspect BackgroundImageAspect => GetBackgroundImageAspect(Element);
-
public Aspect NormalBackgroundImageAspect => GetNormalBackgroundImageAspect(Element);
-
public Aspect HoveredBackgroundImageAspect => GetHoveredBackgroundImageAspect(Element);
-
public Aspect PressedBackgroundImageAspect => GetPressedBackgroundImageAspect(Element);
-
public bool ShouldSetImageOnAnimationEnd => GetShouldSetImageOnAnimationEnd(Element);
- internal bool CanExecute => IsAvailable
- && (Element?.IsEnabled ?? false)
- && (Command?.CanExecute(CommandParameter) ?? true);
+ #endregion
- internal new VisualElement? Element
+ #region Static Helpers
+
+ ///
+ /// Optimized check for TouchEffect presence without LINQ allocation.
+ ///
+ private static bool HasTouchEffect(VisualElement view)
{
- get => element;
- set
+ var effects = view.Effects;
+ for (int i = 0; i < effects.Count; i++)
{
- if (element != null)
+ if (effects[i] is TouchEffect)
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// Gets the TouchEffect from a bindable object, preferring non-auto-generated effects.
+ /// Optimized to avoid LINQ allocations.
+ ///
+ internal static TouchEffect? GetFrom(BindableObject? bindable)
+ {
+ if (bindable is not VisualElement view)
+ return null;
+
+ var effects = view.Effects;
+ TouchEffect? autoGenerated = null;
+
+ for (int i = 0; i < effects.Count; i++)
+ {
+ if (effects[i] is TouchEffect te)
{
- IsUsed = false;
- gestureManager.Reset();
- SetChildrenInputTransparent(false);
+ if (!te.IsAutoGenerated)
+ return te;
+ autoGenerated ??= te;
}
+ }
- gestureManager.AbortAnimations(this);
- element = value;
- if (value != null)
+ return autoGenerated;
+ }
+
+ ///
+ /// Picks an available TouchEffect for use, preferring unused non-auto-generated effects.
+ /// Optimized to avoid LINQ allocations.
+ ///
+ internal static TouchEffect? PickFrom(BindableObject? bindable)
+ {
+ if (bindable is not VisualElement view)
+ return null;
+
+ var effects = view.Effects;
+ TouchEffect? autoGenerated = null;
+ TouchEffect? anyEffect = null;
+
+ for (int i = 0; i < effects.Count; i++)
+ {
+ if (effects[i] is TouchEffect te)
{
- SetChildrenInputTransparent(ShouldMakeChildrenInputTransparent);
- if (!IsAutoGenerated)
- {
- IsUsed = true;
- foreach (var effect in value.Effects.OfType())
- {
- effect.IsDisabled = effect != this;
- }
- }
+ anyEffect ??= te;
- ForceUpdateState();
+ if (!te.IsAutoGenerated && !te.IsUsed)
+ return te;
+
+ if (te.IsAutoGenerated)
+ autoGenerated ??= te;
+ }
+ }
+
+ return autoGenerated ?? anyEffect;
+ }
+
+ ///
+ /// Disables other TouchEffect instances on the same element.
+ ///
+ private void DisableOtherTouchEffects(VisualElement view)
+ {
+ var effects = view.Effects;
+ for (int i = 0; i < effects.Count; i++)
+ {
+ if (effects[i] is TouchEffect te && te != this)
+ {
+ te.IsDisabled = true;
}
}
}
- internal static TouchEffect? GetFrom(BindableObject? bindable)
- {
- var effects = (bindable as VisualElement)?.Effects?.OfType().ToList();
- return effects?.FirstOrDefault(x => !x.IsAutoGenerated) ?? effects?.FirstOrDefault();
- }
+ #endregion
- internal static TouchEffect? PickFrom(BindableObject? bindable)
- {
- var effects = (bindable as VisualElement)?.Effects?.OfType().ToList();
- return effects?.FirstOrDefault(x => !x.IsAutoGenerated && !x.IsUsed)
- ?? effects?.FirstOrDefault(x => x.IsAutoGenerated)
- ?? effects?.FirstOrDefault();
- }
+ #region Touch Handling
internal void HandleTouch(TouchStatus status)
{
- gestureManager.HandleTouch(this, status);
+ _gestureManager.HandleTouch(this, status);
}
internal void HandleUserInteraction(TouchInteractionStatus interactionStatus)
@@ -1392,125 +381,137 @@ public partial class TouchEffect : RoutingEffect
GestureManager.HandleHover(this, status);
}
+ #endregion
+
+ #region Event Raising
+
internal void RaiseStateChanged()
{
ForceUpdateState();
HandleLongPress();
- if (Element is null)
- {
- return;
- }
-
- weakEventManager.HandleEvent(Element, new TouchStateChangedEventArgs(State), nameof(StateChanged));
+ if (Element is null)
+ return;
+
+ _weakEventManager.HandleEvent(Element, new TouchStateChangedEventArgs(State), nameof(StateChanged));
}
internal void RaiseInteractionStatusChanged()
{
- if (Element is null)
- {
- return;
- }
-
- weakEventManager.HandleEvent(Element, new TouchInteractionStatusChangedEventArgs(InteractionStatus), nameof(InteractionStatusChanged));
+ if (Element is null)
+ return;
+
+ _weakEventManager.HandleEvent(Element, new TouchInteractionStatusChangedEventArgs(InteractionStatus), nameof(InteractionStatusChanged));
}
internal void RaiseStatusChanged()
{
- if (Element is null)
- {
- return;
- }
-
- weakEventManager.HandleEvent(Element, new TouchStatusChangedEventArgs(Status), nameof(StatusChanged));
+ if (Element is null)
+ return;
+
+ _weakEventManager.HandleEvent(Element, new TouchStatusChangedEventArgs(Status), nameof(StatusChanged));
}
internal void RaiseHoverStateChanged()
{
ForceUpdateState();
-
- if (Element is null)
- {
- return;
- }
-
- weakEventManager.HandleEvent(Element, new HoverStateChangedEventArgs(HoverState), nameof(HoverStateChanged));
+
+ if (Element is null)
+ return;
+
+ _weakEventManager.HandleEvent(Element, new HoverStateChangedEventArgs(HoverState), nameof(HoverStateChanged));
}
internal void RaiseHoverStatusChanged()
{
- if (Element is null)
- {
- return;
- }
-
- weakEventManager.HandleEvent(Element, new HoverStatusChangedEventArgs(HoverStatus), nameof(HoverStatusChanged));
+ if (Element is null)
+ return;
+
+ _weakEventManager.HandleEvent(Element, new HoverStatusChangedEventArgs(HoverStatus), nameof(HoverStatusChanged));
}
internal void RaiseCompleted()
{
var cElement = Element;
if (cElement == null)
- {
return;
- }
var parameter = CommandParameter;
- Command?.Execute(parameter);
- weakEventManager.HandleEvent(cElement, new TouchCompletedEventArgs(parameter), nameof(Completed));
+
+ try
+ {
+ Command?.Execute(parameter);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "RaiseCompleted", "Error executing Command");
+ }
+
+ _weakEventManager.HandleEvent(cElement, new TouchCompletedEventArgs(parameter), nameof(Completed));
}
internal void RaiseLongPressCompleted()
{
- var cElement = Element;
- if (cElement == null)
- {
- return;
- }
+ var cElement = Element;
+ if (cElement == null)
+ return;
var parameter = LongPressCommandParameter ?? CommandParameter;
- LongPressCommand?.Execute(parameter);
- weakEventManager.HandleEvent(cElement, new LongPressCompletedEventArgs(parameter), nameof(LongPressCompleted));
+
+ try
+ {
+ LongPressCommand?.Execute(parameter);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "RaiseLongPressCompleted", "Error executing LongPressCommand");
+ }
+
+ _weakEventManager.HandleEvent(cElement, new LongPressCompletedEventArgs(parameter), nameof(LongPressCompleted));
}
+ #endregion
+
+ #region State Management
+
internal void ForceUpdateState(bool animated = true)
{
if (Element == null)
- {
return;
- }
- gestureManager.ChangeStateAsync(this, animated).SafeFireAndForget();
+ _gestureManager.ChangeStateAsync(this, animated).SafeFireAndForget(ex =>
+ _logger.LogError(ex, "ForceUpdateState", "Error during state change animation"));
}
internal void HandleLongPress()
{
if (Element == null)
- {
return;
- }
- gestureManager.HandleLongPress(this);
+ _gestureManager.HandleLongPress(this);
}
+ #endregion
+
+ #region Children Input Transparency
+
private void SetChildrenInputTransparent(bool value)
{
if (Element is not Layout layout)
- {
return;
- }
layout.ChildAdded -= OnLayoutChildAdded;
if (!value)
- {
return;
- }
layout.InputTransparent = false;
- foreach (var view in layout.Children)
+ foreach (var child in layout.Children)
{
- OnLayoutChildAdded(layout, new ElementEventArgs((Element)view));
+ if (child is Element element)
+ {
+ OnLayoutChildAdded(layout, new ElementEventArgs(element));
+ }
}
layout.ChildAdded += OnLayoutChildAdded;
@@ -1519,9 +520,7 @@ public partial class TouchEffect : RoutingEffect
private void OnLayoutChildAdded(object? sender, ElementEventArgs e)
{
if (e.Element is not View view)
- {
return;
- }
if (!ShouldMakeChildrenInputTransparent)
{
@@ -1532,4 +531,6 @@ public partial class TouchEffect : RoutingEffect
var effect = GetFrom(view);
view.InputTransparent = effect?.Element == null || !effect.IsAvailable;
}
+
+ #endregion
}
diff --git a/src/Maui.TouchEffect/TouchEffectBuilder.cs b/src/Maui.TouchEffect/TouchEffectBuilder.cs
index 40c2835..bc2c089 100644
--- a/src/Maui.TouchEffect/TouchEffectBuilder.cs
+++ b/src/Maui.TouchEffect/TouchEffectBuilder.cs
@@ -1,9 +1,6 @@
using System.Windows.Input;
-using Microsoft.Maui.Controls;
-using Microsoft.Maui.Graphics;
-using Maui.TouchEffect;
-namespace Maui.TouchEffect;
+namespace MarketAlly.TouchEffect.Maui;
///
/// Fluent builder for configuring TouchEffect with a clean API.
@@ -15,21 +12,21 @@ public class TouchEffectBuilder
private object? commandParameter;
private ICommand? longPressCommand;
private object? longPressCommandParameter;
- private int longPressDuration = 500; // Default long press duration
+ private int longPressDuration = TouchEffectConstants.Defaults.LongPressDuration;
// Visual properties
- private double pressedOpacity = 1.0;
- private double pressedScale = 1.0;
+ private double pressedOpacity = TouchEffectConstants.Defaults.Opacity;
+ private double pressedScale = TouchEffectConstants.Defaults.Scale;
private Color? pressedBackgroundColor;
- private double hoveredOpacity = 1.0;
- private double hoveredScale = 1.0;
+ private double hoveredOpacity = TouchEffectConstants.Defaults.Opacity;
+ private double hoveredScale = TouchEffectConstants.Defaults.Scale;
private Color? hoveredBackgroundColor;
- private double normalOpacity = 1.0;
- private double normalScale = 1.0;
+ private double normalOpacity = TouchEffectConstants.Defaults.Opacity;
+ private double normalScale = TouchEffectConstants.Defaults.Scale;
private Color? normalBackgroundColor;
// Animation properties
- private int animationDuration = 150;
+ private int animationDuration = TouchEffectConstants.PresetDurations.Normal;
private Easing? animationEasing;
private int pressedAnimationDuration;
private Easing? pressedAnimationEasing;
@@ -40,11 +37,11 @@ public class TouchEffectBuilder
// Native animation properties
private bool nativeAnimation;
private Color? nativeAnimationColor;
- private int nativeAnimationRadius = -1;
+ private int nativeAnimationRadius = TouchEffectConstants.Defaults.NativeAnimationRadius;
// Other properties
private bool? isToggled;
- private int disallowTouchThreshold = 10;
+ private int disallowTouchThreshold = TouchEffectConstants.Defaults.DisallowTouchThreshold;
private bool isAvailable = true;
///
@@ -277,7 +274,7 @@ public class TouchEffectBuilder
public TouchEffectBuilder AsButton()
{
return WithPressedOpacity(0.7)
- .WithAnimation(100, Easing.CubicOut);
+ .WithAnimation(TouchEffectConstants.PresetDurations.Fast, Easing.CubicOut);
}
///
@@ -286,7 +283,7 @@ public class TouchEffectBuilder
public TouchEffectBuilder AsCard()
{
return WithPressedScale(0.97)
- .WithAnimation(150, Easing.CubicInOut)
+ .WithAnimation(TouchEffectConstants.PresetDurations.Normal, Easing.CubicInOut)
.WithHoveredScale(1.02);
}
@@ -296,7 +293,7 @@ public class TouchEffectBuilder
public TouchEffectBuilder AsListItem()
{
return WithPressedBackgroundColor(Colors.LightGray.WithAlpha(0.3f))
- .WithAnimation(50);
+ .WithAnimation(TouchEffectConstants.PresetDurations.VeryFast);
}
///
@@ -306,7 +303,7 @@ public class TouchEffectBuilder
{
return WithPressedScale(0.9)
.WithPressedOpacity(0.8)
- .WithAnimation(100, Easing.SpringOut)
+ .WithAnimation(TouchEffectConstants.PresetDurations.Fast, Easing.SpringOut)
.WithNativeAnimation();
}
@@ -425,4 +422,4 @@ public static class TouchEffectBuilderExtensions
builder.WithCommand(command);
return builder.Build();
}
-}
\ No newline at end of file
+}
diff --git a/src/Maui.TouchEffect/TouchEffectConstants.cs b/src/Maui.TouchEffect/TouchEffectConstants.cs
index 62e77a4..9b1c5c3 100644
--- a/src/Maui.TouchEffect/TouchEffectConstants.cs
+++ b/src/Maui.TouchEffect/TouchEffectConstants.cs
@@ -1,4 +1,4 @@
-namespace Maui.TouchEffect;
+namespace MarketAlly.TouchEffect.Maui;
///
/// Constants used throughout the TouchEffect library.
@@ -212,4 +212,4 @@ public static class TouchEffectConstants
///
public const int VerySlow = 500;
}
-}
\ No newline at end of file
+}
diff --git a/src/Maui.TouchEffect/TouchEffectPresets.cs b/src/Maui.TouchEffect/TouchEffectPresets.cs
index cabdf1f..d957b2a 100644
--- a/src/Maui.TouchEffect/TouchEffectPresets.cs
+++ b/src/Maui.TouchEffect/TouchEffectPresets.cs
@@ -1,7 +1,4 @@
-using Microsoft.Maui.Controls;
-using Microsoft.Maui.Graphics;
-
-namespace Maui.TouchEffect;
+namespace MarketAlly.TouchEffect.Maui;
///
/// Predefined TouchEffect configurations for common UI patterns.
@@ -19,7 +16,7 @@ public static class TouchEffectPresets
public static void Apply(VisualElement element)
{
TouchEffect.SetPressedOpacity(element, 0.7);
- TouchEffect.SetAnimationDuration(element, 100); // Fast
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.Fast);
TouchEffect.SetAnimationEasing(element, Easing.CubicOut);
}
@@ -30,7 +27,7 @@ public static class TouchEffectPresets
{
TouchEffect.SetPressedOpacity(element, 0.8);
TouchEffect.SetPressedScale(element, 0.95);
- TouchEffect.SetAnimationDuration(element, 100); // Fast
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.Fast);
TouchEffect.SetAnimationEasing(element, Easing.CubicOut);
}
@@ -40,7 +37,7 @@ public static class TouchEffectPresets
public static void ApplySecondary(VisualElement element)
{
TouchEffect.SetPressedOpacity(element, 0.6);
- TouchEffect.SetAnimationDuration(element, 50); // Very Fast
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.VeryFast);
}
///
@@ -49,7 +46,7 @@ public static class TouchEffectPresets
public static void ApplyText(VisualElement element)
{
TouchEffect.SetPressedOpacity(element, 0.5);
- TouchEffect.SetAnimationDuration(element, 25); // Instant
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.Instant);
}
}
@@ -64,7 +61,7 @@ public static class TouchEffectPresets
public static void Apply(VisualElement element)
{
TouchEffect.SetPressedScale(element, 0.97);
- TouchEffect.SetAnimationDuration(element, 150); // Normal
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.Normal);
TouchEffect.SetAnimationEasing(element, Easing.CubicInOut);
}
@@ -75,10 +72,10 @@ public static class TouchEffectPresets
{
TouchEffect.SetPressedScale(element, 0.95);
TouchEffect.SetPressedOpacity(element, 0.9);
- TouchEffect.SetAnimationDuration(element, 150); // Normal
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.Normal);
TouchEffect.SetAnimationEasing(element, Easing.CubicInOut);
TouchEffect.SetHoveredScale(element, 1.02);
- TouchEffect.SetHoveredAnimationDuration(element, 200); // Slow
+ TouchEffect.SetHoveredAnimationDuration(element, TouchEffectConstants.PresetDurations.Slow);
}
///
@@ -89,7 +86,7 @@ public static class TouchEffectPresets
TouchEffect.SetPressedScale(element, 0.98);
TouchEffect.SetHoveredScale(element, 1.01);
TouchEffect.SetHoveredBackgroundColor(element, Colors.Gray.WithAlpha(0.1f));
- TouchEffect.SetAnimationDuration(element, 100); // Fast
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.Fast);
TouchEffect.SetAnimationEasing(element, Easing.CubicOut);
}
}
@@ -105,7 +102,7 @@ public static class TouchEffectPresets
public static void Apply(VisualElement element)
{
TouchEffect.SetPressedBackgroundColor(element, Colors.Gray.WithAlpha(0.2f));
- TouchEffect.SetAnimationDuration(element, 50); // Very Fast
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.VeryFast);
}
///
@@ -115,7 +112,7 @@ public static class TouchEffectPresets
{
TouchEffect.SetIsToggled(element, false);
TouchEffect.SetPressedBackgroundColor(element, Colors.Blue.WithAlpha(0.3f));
- TouchEffect.SetAnimationDuration(element, 100); // Fast
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.Fast);
}
///
@@ -125,7 +122,7 @@ public static class TouchEffectPresets
{
TouchEffect.SetPressedScale(element, 0.98);
TouchEffect.SetPressedBackgroundColor(element, Colors.Gray.WithAlpha(0.1f));
- TouchEffect.SetAnimationDuration(element, 50); // Very Fast
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.VeryFast);
}
}
@@ -141,7 +138,7 @@ public static class TouchEffectPresets
{
TouchEffect.SetPressedScale(element, 0.85);
TouchEffect.SetPressedOpacity(element, 0.7);
- TouchEffect.SetAnimationDuration(element, 100); // Fast
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.Fast);
TouchEffect.SetAnimationEasing(element, Easing.SpringOut);
}
@@ -152,7 +149,7 @@ public static class TouchEffectPresets
{
TouchEffect.SetPressedScale(element, 0.9);
TouchEffect.SetPressedOpacity(element, 0.8);
- TouchEffect.SetAnimationDuration(element, 100); // Fast
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.Fast);
TouchEffect.SetAnimationEasing(element, Easing.SpringOut);
TouchEffect.SetNativeAnimation(element, true);
}
@@ -163,7 +160,7 @@ public static class TouchEffectPresets
public static void ApplyToolbar(VisualElement element)
{
TouchEffect.SetPressedOpacity(element, 0.5);
- TouchEffect.SetAnimationDuration(element, 50); // Very Fast
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.VeryFast);
}
}
@@ -179,7 +176,7 @@ public static class TouchEffectPresets
{
TouchEffect.SetIsToggled(element, false);
TouchEffect.SetPressedScale(element, 0.95);
- TouchEffect.SetAnimationDuration(element, 150); // Normal
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.Normal);
TouchEffect.SetAnimationEasing(element, Easing.CubicInOut);
}
@@ -190,7 +187,7 @@ public static class TouchEffectPresets
{
TouchEffect.SetIsToggled(element, false);
TouchEffect.SetPressedScale(element, 0.9);
- TouchEffect.SetAnimationDuration(element, 100); // Fast
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.Fast);
TouchEffect.SetAnimationEasing(element, Easing.BounceOut);
}
}
@@ -207,7 +204,7 @@ public static class TouchEffectPresets
{
TouchEffect.SetPressedScale(element, 0.95);
TouchEffect.SetHoveredScale(element, 1.05);
- TouchEffect.SetAnimationDuration(element, 150); // Normal
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.Normal);
TouchEffect.SetAnimationEasing(element, Easing.CubicInOut);
}
@@ -219,7 +216,7 @@ public static class TouchEffectPresets
TouchEffect.SetPressedScale(element, 0.98);
TouchEffect.SetPressedOpacity(element, 0.8);
TouchEffect.SetHoveredScale(element, 1.1);
- TouchEffect.SetAnimationDuration(element, 200); // Slow
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.Slow);
TouchEffect.SetAnimationEasing(element, Easing.CubicInOut);
}
@@ -230,7 +227,7 @@ public static class TouchEffectPresets
{
TouchEffect.SetPressedScale(element, 0.92);
TouchEffect.SetPressedOpacity(element, 0.7);
- TouchEffect.SetAnimationDuration(element, 100); // Fast
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.Fast);
TouchEffect.SetAnimationEasing(element, Easing.CubicOut);
}
}
@@ -257,7 +254,7 @@ public static class TouchEffectPresets
{
TouchEffect.SetNativeAnimation(element, true);
TouchEffect.SetPressedOpacity(element, 0.8);
- TouchEffect.SetAnimationDuration(element, 50); // Very Fast
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.VeryFast);
}
}
@@ -274,7 +271,7 @@ public static class TouchEffectPresets
TouchEffect.SetPressedScale(element, 1.1);
TouchEffect.SetPressedOpacity(element, 0.7);
TouchEffect.SetPulseCount(element, count);
- TouchEffect.SetAnimationDuration(element, 150); // Normal
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.Normal);
TouchEffect.SetAnimationEasing(element, Easing.SinInOut);
}
@@ -284,7 +281,7 @@ public static class TouchEffectPresets
public static void ApplyBounce(VisualElement element)
{
TouchEffect.SetPressedScale(element, 0.8);
- TouchEffect.SetAnimationDuration(element, 200); // Slow
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.Slow);
TouchEffect.SetAnimationEasing(element, Easing.SpringOut);
}
@@ -295,7 +292,7 @@ public static class TouchEffectPresets
{
TouchEffect.SetPressedRotation(element, 5);
TouchEffect.SetPulseCount(element, 2);
- TouchEffect.SetAnimationDuration(element, 50); // Very Fast
+ TouchEffect.SetAnimationDuration(element, TouchEffectConstants.PresetDurations.VeryFast);
TouchEffect.SetAnimationEasing(element, Easing.BounceOut);
}
@@ -360,4 +357,4 @@ public static class TouchEffectPresetExtensions
TouchEffectPresets.Native.ApplyRipple(element, color);
return element;
}
-}
\ No newline at end of file
+}