// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Controls;
namespace Microsoft.Maui.Platform;
///
/// Base class for state triggers that automatically activate visual states.
///
public abstract class SkiaStateTriggerBase
{
private bool _isActive;
private SkiaVisualState? _ownerState;
private SkiaView? _ownerView;
///
/// Gets whether this trigger is currently active.
///
public bool IsActive
{
get => _isActive;
protected set
{
if (_isActive != value)
{
_isActive = value;
OnIsActiveChanged();
}
}
}
///
/// Gets or sets the visual state this trigger belongs to.
///
internal SkiaVisualState? OwnerState
{
get => _ownerState;
set => _ownerState = value;
}
///
/// Gets or sets the view this trigger is attached to.
///
internal SkiaView? OwnerView
{
get => _ownerView;
set
{
_ownerView = value;
OnAttached();
}
}
///
/// Called when the trigger is attached to a view.
///
protected virtual void OnAttached()
{
}
///
/// Called when IsActive changes.
///
protected virtual void OnIsActiveChanged()
{
if (_isActive && _ownerState != null && _ownerView != null)
{
SkiaVisualStateManager.GoToState(_ownerView, _ownerState.Name);
}
}
}
///
/// A trigger that activates based on a boolean property.
/// Maps to MAUI StateTrigger.
///
public class SkiaStateTrigger : SkiaStateTriggerBase
{
private bool _isActiveValue;
///
/// Gets or sets whether this trigger should be active.
///
public bool IsActiveValue
{
get => _isActiveValue;
set
{
_isActiveValue = value;
IsActive = value;
}
}
}
///
/// A trigger that activates based on window size thresholds.
/// Maps to MAUI AdaptiveTrigger.
///
public class SkiaAdaptiveTrigger : SkiaStateTriggerBase
{
private double _minWindowWidth = -1;
private double _minWindowHeight = -1;
///
/// Gets or sets the minimum window width for this trigger to activate.
///
public double MinWindowWidth
{
get => _minWindowWidth;
set
{
_minWindowWidth = value;
UpdateIsActive();
}
}
///
/// Gets or sets the minimum window height for this trigger to activate.
///
public double MinWindowHeight
{
get => _minWindowHeight;
set
{
_minWindowHeight = value;
UpdateIsActive();
}
}
protected override void OnAttached()
{
base.OnAttached();
// Subscribe to window size changes if needed
UpdateIsActive();
}
private void UpdateIsActive()
{
if (OwnerView == null)
{
IsActive = false;
return;
}
// Get current window size from the view's bounds
var width = OwnerView.Bounds.Width;
var height = OwnerView.Bounds.Height;
bool widthMet = _minWindowWidth < 0 || width >= _minWindowWidth;
bool heightMet = _minWindowHeight < 0 || height >= _minWindowHeight;
IsActive = widthMet && heightMet;
}
}
///
/// A trigger that activates when a property equals a specific value.
/// Maps to MAUI CompareStateTrigger.
///
public class SkiaCompareStateTrigger : SkiaStateTriggerBase
{
private object? _property;
private object? _value;
///
/// Gets or sets the property value to compare.
///
public object? Property
{
get => _property;
set
{
_property = value;
UpdateIsActive();
}
}
///
/// Gets or sets the value to compare against.
///
public object? Value
{
get => _value;
set
{
_value = value;
UpdateIsActive();
}
}
private void UpdateIsActive()
{
if (_property == null && _value == null)
{
IsActive = true;
return;
}
if (_property == null || _value == null)
{
IsActive = _property == _value;
return;
}
// Try to compare values
IsActive = _property.Equals(_value);
}
}
///
/// A trigger that activates based on device idiom (Desktop, Phone, Tablet, etc.).
///
public class SkiaDeviceStateTrigger : SkiaStateTriggerBase
{
private string _deviceType = "";
///
/// Gets or sets the device type to match (Desktop, Phone, Tablet, Watch, TV).
///
public string DeviceType
{
get => _deviceType;
set
{
_deviceType = value;
UpdateIsActive();
}
}
protected override void OnAttached()
{
base.OnAttached();
UpdateIsActive();
}
private void UpdateIsActive()
{
// On Linux, we're always Desktop
IsActive = string.Equals(_deviceType, "Desktop", StringComparison.OrdinalIgnoreCase);
}
}
///
/// A trigger that activates based on orientation (Portrait or Landscape).
///
public class SkiaOrientationStateTrigger : SkiaStateTriggerBase
{
private SkiaDisplayOrientation _orientation = SkiaDisplayOrientation.Portrait;
///
/// Gets or sets the orientation to match.
///
public SkiaDisplayOrientation Orientation
{
get => _orientation;
set
{
_orientation = value;
UpdateIsActive();
}
}
protected override void OnAttached()
{
base.OnAttached();
UpdateIsActive();
}
private void UpdateIsActive()
{
if (OwnerView == null)
{
IsActive = false;
return;
}
var width = OwnerView.Bounds.Width;
var height = OwnerView.Bounds.Height;
var currentOrientation = width > height
? SkiaDisplayOrientation.Landscape
: SkiaDisplayOrientation.Portrait;
IsActive = currentOrientation == _orientation;
}
}
///
/// Display orientation values for state triggers.
///
public enum SkiaDisplayOrientation
{
Portrait,
Landscape
}