.net 10, refactor for better stability
This commit is contained in:
parent
f9ab873c67
commit
2b003b7970
807
API_Reference.md
Normal file
807
API_Reference.md
Normal file
@ -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
|
||||
<Frame touch:TouchEffect.IsAvailable="{Binding CanInteract}"
|
||||
touch:TouchEffect.ShouldMakeChildrenInputTransparent="True">
|
||||
<Label Text="Interactive Content" />
|
||||
</Frame>
|
||||
```
|
||||
|
||||
#### 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
|
||||
<Frame touch:TouchEffect.Command="{Binding TapCommand}"
|
||||
touch:TouchEffect.CommandParameter="{Binding Item}"
|
||||
touch:TouchEffect.LongPressCommand="{Binding ContextMenuCommand}"
|
||||
touch:TouchEffect.LongPressDuration="800">
|
||||
<Label Text="Tap or Long Press" />
|
||||
</Frame>
|
||||
```
|
||||
|
||||
#### 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
|
||||
<Frame touch:TouchEffect.NormalBackgroundColor="White"
|
||||
touch:TouchEffect.HoveredBackgroundColor="LightGray"
|
||||
touch:TouchEffect.PressedBackgroundColor="Gray">
|
||||
<Label Text="Color Feedback" />
|
||||
</Frame>
|
||||
```
|
||||
|
||||
#### 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
|
||||
<Frame touch:TouchEffect.NormalOpacity="1.0"
|
||||
touch:TouchEffect.HoveredOpacity="0.9"
|
||||
touch:TouchEffect.PressedOpacity="0.7">
|
||||
<Label Text="Opacity Feedback" />
|
||||
</Frame>
|
||||
```
|
||||
|
||||
#### 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
|
||||
<Frame touch:TouchEffect.NormalScale="1.0"
|
||||
touch:TouchEffect.HoveredScale="1.02"
|
||||
touch:TouchEffect.PressedScale="0.95">
|
||||
<Label Text="Scale Feedback" />
|
||||
</Frame>
|
||||
```
|
||||
|
||||
#### 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
|
||||
<Frame touch:TouchEffect.PressedTranslationY="2">
|
||||
<Label Text="Moves down when pressed" />
|
||||
</Frame>
|
||||
```
|
||||
|
||||
#### 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
|
||||
<Frame touch:TouchEffect.PressedRotation="5">
|
||||
<Label Text="Tilts when pressed" />
|
||||
</Frame>
|
||||
```
|
||||
|
||||
#### 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
|
||||
<Frame touch:TouchEffect.AnimationDuration="150"
|
||||
touch:TouchEffect.AnimationEasing="{x:Static Easing.CubicOut}"
|
||||
touch:TouchEffect.PressedScale="0.95">
|
||||
<Label Text="Animated Feedback" />
|
||||
</Frame>
|
||||
```
|
||||
|
||||
#### 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
|
||||
<Frame touch:TouchEffect.IsToggled="{Binding IsSelected, Mode=TwoWay}"
|
||||
touch:TouchEffect.PressedBackgroundColor="Blue"
|
||||
touch:TouchEffect.NormalBackgroundColor="Gray">
|
||||
<Label Text="Toggle Button" />
|
||||
</Frame>
|
||||
```
|
||||
|
||||
#### 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
|
||||
<Frame touch:TouchEffect.NativeAnimation="True"
|
||||
touch:TouchEffect.NativeAnimationColor="Blue"
|
||||
touch:TouchEffect.NativeAnimationRadius="100">
|
||||
<Label Text="Native Ripple Effect" />
|
||||
</Frame>
|
||||
```
|
||||
|
||||
#### 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<TouchEffect>().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<VisualElement>`
|
||||
|
||||
### 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
|
||||
<Button Text="Click Me">
|
||||
<Button.Behaviors>
|
||||
<touch:TouchBehavior
|
||||
PressedScale="0.95"
|
||||
PressedOpacity="0.8"
|
||||
AnimationDuration="100"
|
||||
AnimationEasing="{x:Static Easing.CubicOut}"
|
||||
Command="{Binding TapCommand}" />
|
||||
</Button.Behaviors>
|
||||
</Button>
|
||||
```
|
||||
|
||||
**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<App>()` to include `.UseMauiTouchEffect()`
|
||||
|
||||
---
|
||||
|
||||
*Last updated: January 2025 | Version 2.0.0*
|
||||
238
README.md
238
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
|
||||
<PackageReference Include="MarketAlly.TouchEffect.Maui" Version="1.0.0" />
|
||||
<PackageReference Include="MarketAlly.TouchEffect.Maui" Version="2.0.0" />
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
@ -85,7 +97,7 @@ public static class MauiProgram
|
||||
|
||||
### 2. Add Touch Effects to Your Views
|
||||
|
||||
#### XAML Approach
|
||||
#### XAML Approach (Attached Properties)
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
@ -124,7 +136,28 @@ public static class MauiProgram
|
||||
</ContentPage>
|
||||
```
|
||||
|
||||
#### 🆕 Fluent Builder Approach (New!)
|
||||
#### NEW: TouchBehavior Approach (v2.0.0)
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:touch="clr-namespace:MarketAlly.TouchEffect.Maui;assembly=MarketAlly.TouchEffect.Maui"
|
||||
x:Class="YourApp.MainPage">
|
||||
|
||||
<Button Text="Click Me">
|
||||
<Button.Behaviors>
|
||||
<touch:TouchBehavior
|
||||
PressedScale="0.95"
|
||||
PressedOpacity="0.8"
|
||||
AnimationDuration="100"
|
||||
Command="{Binding TapCommand}" />
|
||||
</Button.Behaviors>
|
||||
</Button>
|
||||
</ContentPage>
|
||||
```
|
||||
|
||||
#### 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
|
||||
<!-- Accessible button with semantic properties -->
|
||||
@ -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*
|
||||
*Based on the original TouchEffect by Andrei - Used under MIT License*
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net9.0-android;net9.0-ios</TargetFrameworks>
|
||||
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net9.0-windows10.0.19041.0</TargetFrameworks>
|
||||
<TargetFrameworks>net10.0-android;net10.0-ios</TargetFrameworks>
|
||||
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net10.0-windows10.0.19041.0</TargetFrameworks>
|
||||
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
|
||||
<!-- <TargetFrameworks>$(TargetFrameworks);net9.0-tizen</TargetFrameworks> -->
|
||||
<!-- <TargetFrameworks>$(TargetFrameworks);net10.0-tizen</TargetFrameworks> -->
|
||||
|
||||
<!-- Note for MacCatalyst:
|
||||
The default runtime is maccatalyst-x64, except in Release config, in which case the default is maccatalyst-x64;maccatalyst-arm64.
|
||||
@ -39,25 +39,25 @@
|
||||
|
||||
<ItemGroup>
|
||||
<!-- App Icon -->
|
||||
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4"/>
|
||||
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
|
||||
|
||||
<!-- Splash Screen -->
|
||||
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128"/>
|
||||
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />
|
||||
|
||||
<!-- Images -->
|
||||
<MauiImage Include="Resources\Images\*"/>
|
||||
<MauiImage Update="Resources\Images\dotnet_bot.svg" BaseSize="168,208"/>
|
||||
<MauiImage Include="Resources\Images\*" />
|
||||
<MauiImage Update="Resources\Images\dotnet_bot.svg" BaseSize="168,208" />
|
||||
|
||||
<!-- Custom Fonts -->
|
||||
<MauiFont Include="Resources\Fonts\*"/>
|
||||
<MauiFont Include="Resources\Fonts\*" />
|
||||
|
||||
<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
|
||||
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)"/>
|
||||
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.0"/>
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.82" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="10.0.1" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="10.0.11" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
namespace Maui.TouchEffect.Enums;
|
||||
namespace MarketAlly.TouchEffect.Maui.Enums;
|
||||
|
||||
public enum HoverState
|
||||
{
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
namespace Maui.TouchEffect.Enums;
|
||||
namespace MarketAlly.TouchEffect.Maui.Enums;
|
||||
|
||||
public enum HoverStatus
|
||||
{
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
namespace Maui.TouchEffect.Enums;
|
||||
namespace MarketAlly.TouchEffect.Maui.Enums;
|
||||
|
||||
public enum TouchInteractionStatus
|
||||
{
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
namespace Maui.TouchEffect.Enums;
|
||||
namespace MarketAlly.TouchEffect.Maui.Enums;
|
||||
|
||||
public enum TouchState
|
||||
{
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
namespace Maui.TouchEffect.Enums;
|
||||
namespace MarketAlly.TouchEffect.Maui.Enums;
|
||||
|
||||
public enum TouchStatus
|
||||
{
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using Maui.TouchEffect.Enums;
|
||||
namespace Maui.TouchEffect;
|
||||
using MarketAlly.TouchEffect.Maui.Enums;
|
||||
|
||||
namespace MarketAlly.TouchEffect.Maui;
|
||||
|
||||
public class HoverStateChangedEventArgs : EventArgs
|
||||
{
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using Maui.TouchEffect.Enums;
|
||||
namespace Maui.TouchEffect;
|
||||
using MarketAlly.TouchEffect.Maui.Enums;
|
||||
|
||||
namespace MarketAlly.TouchEffect.Maui;
|
||||
|
||||
public class HoverStatusChangedEventArgs : EventArgs
|
||||
{
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
namespace Maui.TouchEffect;
|
||||
namespace MarketAlly.TouchEffect.Maui;
|
||||
|
||||
public class LongPressCompletedEventArgs : EventArgs
|
||||
{
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
namespace Maui.TouchEffect;
|
||||
namespace MarketAlly.TouchEffect.Maui;
|
||||
|
||||
public class TouchCompletedEventArgs : EventArgs
|
||||
{
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using Maui.TouchEffect.Enums;
|
||||
namespace Maui.TouchEffect;
|
||||
using MarketAlly.TouchEffect.Maui.Enums;
|
||||
|
||||
namespace MarketAlly.TouchEffect.Maui;
|
||||
|
||||
public class TouchInteractionStatusChangedEventArgs : EventArgs
|
||||
{
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using Maui.TouchEffect.Enums;
|
||||
namespace Maui.TouchEffect;
|
||||
using MarketAlly.TouchEffect.Maui.Enums;
|
||||
|
||||
namespace MarketAlly.TouchEffect.Maui;
|
||||
|
||||
public class TouchStateChangedEventArgs : EventArgs
|
||||
{
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using Maui.TouchEffect.Enums;
|
||||
namespace Maui.TouchEffect;
|
||||
using MarketAlly.TouchEffect.Maui.Enums;
|
||||
|
||||
namespace MarketAlly.TouchEffect.Maui;
|
||||
|
||||
public class TouchStatusChangedEventArgs : EventArgs
|
||||
{
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
namespace MauiTouchEffect.Extensions;
|
||||
namespace MarketAlly.TouchEffect.Maui.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for System.Threading.Tasks.Task and System.Threading.Tasks.ValueTask
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
namespace Maui.TouchEffect.Extensions;
|
||||
namespace MarketAlly.TouchEffect.Maui.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="VisualElement" />.
|
||||
@ -51,7 +51,7 @@ public static class VisualElementExtension
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool TryFindParentElementWithParentOfType<T>(this VisualElement element, out VisualElement result, out T parent) where T : VisualElement
|
||||
internal static bool TryFindParentElementWithParentOfType<T>(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<T>(this VisualElement element, out T parent) where T : VisualElement
|
||||
internal static bool TryFindParentOfType<T>(this VisualElement element, out T? parent) where T : VisualElement
|
||||
{
|
||||
return element.TryFindParentElementWithParentOfType(out _, out parent);
|
||||
}
|
||||
|
||||
@ -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<TouchEffect, TouchState, HoverState, int, Easing, CancellationToken, Task>? _animationTaskFactory;
|
||||
private Func<TouchEffect, TouchState, HoverState, int, Easing?, CancellationToken, Task>? _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<TouchEffect, TouchState, HoverState, int, Easing, CancellationToken, Task>? 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<TouchEffect, TouchState, HoverState, int, Easing?, CancellationToken, Task>? 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
using System;
|
||||
|
||||
namespace Maui.TouchEffect.Interfaces;
|
||||
namespace MarketAlly.TouchEffect.Maui.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// 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<string, string> { { "Context", context } });
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@ -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<string, object>? additionalMetrics = null) { }
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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")]
|
||||
[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")]
|
||||
|
||||
312
src/Maui.TouchEffect/TouchBehavior.cs
Normal file
312
src/Maui.TouchEffect/TouchBehavior.cs
Normal file
@ -0,0 +1,312 @@
|
||||
using System.Windows.Input;
|
||||
using MarketAlly.TouchEffect.Maui.Enums;
|
||||
|
||||
namespace MarketAlly.TouchEffect.Maui;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// XAML Usage:
|
||||
/// <code>
|
||||
/// <Button>
|
||||
/// <Button.Behaviors>
|
||||
/// <touch:TouchBehavior PressedScale="0.95" Command="{Binding TapCommand}" />
|
||||
/// </Button.Behaviors>
|
||||
/// </Button>
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class TouchBehavior : Behavior<VisualElement>
|
||||
{
|
||||
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
|
||||
}
|
||||
450
src/Maui.TouchEffect/TouchEffect.Accessors.cs
Normal file
450
src/Maui.TouchEffect/TouchEffect.Accessors.cs
Normal file
@ -0,0 +1,450 @@
|
||||
using System.Windows.Input;
|
||||
using MarketAlly.TouchEffect.Maui.Enums;
|
||||
|
||||
namespace MarketAlly.TouchEffect.Maui;
|
||||
|
||||
/// <summary>
|
||||
/// TouchEffect partial class containing all static Get/Set accessor methods.
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net9.0-android;net9.0-ios;net9.0-windows10.0.19041.0</TargetFrameworks>
|
||||
<TargetFrameworks>net10.0-android;net10.0-ios;net10.0-windows10.0.19041.0</TargetFrameworks>
|
||||
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
|
||||
<UseMaui>true</UseMaui>
|
||||
<SingleProject>true</SingleProject>
|
||||
@ -26,10 +26,10 @@
|
||||
<Description>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.</Description>
|
||||
<Copyright>Copyright © 2025 MarketAlly. Original TouchEffect Copyright © 2018 Andrei</Copyright>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<AssemblyVersion>1.0.0</AssemblyVersion>
|
||||
<AssemblyFileVersion>1.0.0</AssemblyFileVersion>
|
||||
<Version>1.0.0</Version>
|
||||
<PackageReleaseNotes>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.</PackageReleaseNotes>
|
||||
<AssemblyVersion>2.0.0</AssemblyVersion>
|
||||
<AssemblyFileVersion>2.0.0</AssemblyFileVersion>
|
||||
<Version>2.0.0</Version>
|
||||
<PackageReleaseNotes>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.</PackageReleaseNotes>
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">13.0</SupportedOSPlatformVersion>
|
||||
@ -39,8 +39,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\..\README.md" Pack="true" PackagePath="\"/>
|
||||
<None Include="..\..\LICENSE" Pack="true" PackagePath="\"/>
|
||||
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
|
||||
<None Include="..\..\LICENSE" Pack="true" PackagePath="\" />
|
||||
<None Include="icon.png">
|
||||
<Pack>true</Pack>
|
||||
<PackagePath>\</PackagePath>
|
||||
@ -50,7 +50,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.82" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="10.0.11" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
475
src/Maui.TouchEffect/TouchEffect.Properties.cs
Normal file
475
src/Maui.TouchEffect/TouchEffect.Properties.cs
Normal file
@ -0,0 +1,475 @@
|
||||
using System.Windows.Input;
|
||||
using MarketAlly.TouchEffect.Maui.Enums;
|
||||
|
||||
namespace MarketAlly.TouchEffect.Maui;
|
||||
|
||||
/// <summary>
|
||||
/// TouchEffect partial class containing all BindableProperty definitions.
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 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;
|
||||
|
||||
/// <summary>
|
||||
@ -277,7 +274,7 @@ public class TouchEffectBuilder
|
||||
public TouchEffectBuilder AsButton()
|
||||
{
|
||||
return WithPressedOpacity(0.7)
|
||||
.WithAnimation(100, Easing.CubicOut);
|
||||
.WithAnimation(TouchEffectConstants.PresetDurations.Fast, Easing.CubicOut);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
namespace Maui.TouchEffect;
|
||||
namespace MarketAlly.TouchEffect.Maui;
|
||||
|
||||
/// <summary>
|
||||
/// Constants used throughout the TouchEffect library.
|
||||
@ -212,4 +212,4 @@ public static class TouchEffectConstants
|
||||
/// </summary>
|
||||
public const int VerySlow = 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Graphics;
|
||||
|
||||
namespace Maui.TouchEffect;
|
||||
namespace MarketAlly.TouchEffect.Maui;
|
||||
|
||||
/// <summary>
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user