Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
Closed="OnClosed"
Icon="/ActionEditor/GenericButtonEditor.ico"
Title="{Binding NameForTitle}"
Height="800" Width="1000" MinHeight="750" MinWidth="800">
Height="810" Width="1000" MinHeight="810" MinWidth="800">

<Window.Resources>
<ResourceDictionary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (C) 2026 Martin Renner
// LGPL-3.0-or-later (see file COPYING and COPYING.LESSER)

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.IO;
Expand All @@ -13,6 +14,7 @@
using StreamDeckSimHub.Plugin.ActionEditor.Tools;
using StreamDeckSimHub.Plugin.ActionEditor.Views.Controls;
using StreamDeckSimHub.Plugin.Actions.GenericButton.Model;
using StreamDeckSimHub.Plugin.Actions.GenericButton.Model.Modifiers;
using StreamDeckSimHub.Plugin.Tools;
using Color = SixLabors.ImageSharp.Color;
using Point = SixLabors.ImageSharp.Point;
Expand All @@ -23,13 +25,20 @@
/// <summary>
/// Base ViewModel for all DisplayItems
/// </summary>
public abstract partial class DisplayItemViewModel(DisplayItem model, IViewModel parentViewModel, byte? _)

Check warning on line 28 in StreamDeckSimHub.Plugin/ActionEditor/ViewModels/DisplayItemViewModels.cs

View workflow job for this annotation

GitHub Actions / build

Parameter '_' is unread.

Check warning on line 28 in StreamDeckSimHub.Plugin/ActionEditor/ViewModels/DisplayItemViewModels.cs

View workflow job for this annotation

GitHub Actions / build

Parameter '_' is unread.

Check warning on line 28 in StreamDeckSimHub.Plugin/ActionEditor/ViewModels/DisplayItemViewModels.cs

View workflow job for this annotation

GitHub Actions / build

Parameter '_' is unread.

Check warning on line 28 in StreamDeckSimHub.Plugin/ActionEditor/ViewModels/DisplayItemViewModels.cs

View workflow job for this annotation

GitHub Actions / build

Parameter '_' is unread.
: ItemViewModel(model, parentViewModel), IDataErrorInfo
{
protected DisplayItemViewModel(DisplayItem model, IViewModel parentViewModel) : this(model, parentViewModel, null)
{
Modifiers = new ObservableCollection<ModifierViewModel>(model.Modifiers.Select(ModifierToViewModel));

if (model is IAcceptsModifierBlink) AvailableModifiers.Add(ModifierBlink.UiName);
if (model is IAcceptsModifierColor) AvailableModifiers.Add(ModifierColor.UiName);
CanAddModifier = AvailableModifiers.Count > 0;
}

[ObservableProperty] private int _selectedTabIndex;

#region Element Data

[ObservableProperty] private float _transparency = model.DisplayParameters.Transparency;
Expand Down Expand Up @@ -101,6 +110,92 @@

#endregion

#region Modifiers

public ObservableCollection<ModifierViewModel> Modifiers { get; } = [];

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsModifierSelected))]
private ModifierViewModel? _selectedModifier;

public bool IsModifierSelected => SelectedModifier != null;

public ObservableCollection<string> AvailableModifiers { get; } = [];

[ObservableProperty] private bool _canAddModifier;

[RelayCommand]
private void AddModifier(string type)
{
switch (type)
{
case ModifierBlink.UiName:
AddModifier(ModifierBlink.Create());
break;
case ModifierColor.UiName:
AddModifier(ModifierColor.Create());
break;
}
}

private void AddModifier(Modifier modifier)
{
model.Modifiers.Add(modifier);
var vm = ModifierToViewModel(modifier);
Modifiers.Add(vm);
SelectedModifier = vm;
}

private ModifierViewModel ModifierToViewModel(Modifier modifier)
{
return modifier switch
{
ModifierBlink modifierBlink => new ModifierBlinkViewModel(modifierBlink, ParentViewModel),
ModifierColor colorModifier => new ModifierColorViewModel(colorModifier, ParentViewModel),
_ => throw new InvalidOperationException($"Unknown Modifier type: {modifier.GetType().FullName}")
};
}

public void RemoveModifier(ModifierViewModel item)
{
// Remove from the underlying model
var modifier = item.GetModel();
model.Modifiers.Remove(modifier);

// Remove from the ViewModel collection
Modifiers.Remove(item);

// Clear selection if this was the selected item
if (SelectedModifier == item)
{
SelectedModifier = null;
}

}

#endregion

#region DragDrop

/// <summary>
/// Updates the underlying model when Modifiers are reordered
/// </summary>
public void UpdateModifiersOrder()
{
// Update the underlying model's Modifiers list to match the order in the ViewModel
// We'll create a new list with the same items but in the new order
var newList = Modifiers.Select(modifierVm => modifierVm.GetModel()).ToList();

// Clear and repopulate the original list to maintain the reference
model.Modifiers.Clear();
foreach (var item in newList)
{
model.Modifiers.Add(item);
}
}

#endregion

public string Error => string.Empty;

public string this[string columnName]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright (C) 2026 Martin Renner
// LGPL-3.0-or-later (see file COPYING and COPYING.LESSER)

using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using StreamDeckSimHub.Plugin.ActionEditor.Tools;
using StreamDeckSimHub.Plugin.ActionEditor.Views.Controls;
using StreamDeckSimHub.Plugin.Actions.GenericButton.Model.Modifiers;
using StreamDeckSimHub.Plugin.SimHub.ShakeIt;
using Color = SixLabors.ImageSharp.Color;

namespace StreamDeckSimHub.Plugin.ActionEditor.ViewModels;

public abstract partial class ModifierViewModel : ObservableObject
{
private readonly Modifier _model;
private readonly IViewModel _rootViewModel;

protected ModifierViewModel(Modifier model, IViewModel rootViewModel)
{
this._model = model;
_rootViewModel = rootViewModel;
_expressionControlConditionViewModel = new ExpressionControlViewModel(model.NCalcConditionHolder)
{
ExpressionLabel = "Condition:",
ExpressionToolTip = "Please enter a valid NCalc expression, that returns true or false or a number",
Example="[DataCorePlugin.Computed.Fuel_RemainingLaps] <= 2",
FetchShakeItProfilesCallback = FetchShakeItProfilesCallback
};
}

public abstract ImageSource? Icon { get; }

public string DisplayName => _model.DisplayName;

[ObservableProperty] private ExpressionControlViewModel _expressionControlConditionViewModel;

public Modifier GetModel() => _model;

private Func<string, Task<IList<Profile>>> FetchShakeItProfilesCallback => FetchShakeItProfiles;

private async Task<IList<Profile>> FetchShakeItProfiles(string type)
{
return type == "Bass"
? await _rootViewModel.FetchShakeItBassProfiles()
: await _rootViewModel.FetchShakeItMotorsProfiles();
}
}

public partial class ModifierBlinkViewModel(ModifierBlink model, IViewModel rootViewModel)
: ModifierViewModel(model, rootViewModel)
{
public override ImageSource? Icon => null;

[ObservableProperty] private int? _durationOn = model.DurationOn;
[ObservableProperty] private int? _durationOff = model.DurationOff;


partial void OnDurationOnChanged(int? value)
{
model.DurationOn = value;
}

partial void OnDurationOffChanged(int? value)
{
model.DurationOff = value;
}
}

public partial class ModifierColorViewModel(ModifierColor model, IViewModel rootViewModel)
: ModifierViewModel(model, rootViewModel), IColorSelectable
{
public override ImageSource? Icon => null;

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ColorHex))]
[NotifyPropertyChangedFor(nameof(ColorAsWpf))]
private Color _imageSharpColor = model.Color;

public string ColorHex => $"#{model.Color.ToHexWithoutAlpha()}";

public System.Windows.Media.Color ColorAsWpf => ImageSharpColor.ToWpfColor();

partial void OnImageSharpColorChanged(Color value)
{
model.Color = value;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<UserControl x:Class="StreamDeckSimHub.Plugin.ActionEditor.Views.Controls.DisplayElementControl"
<UserControl x:Class="StreamDeckSimHub.Plugin.ActionEditor.Views.Controls.DisplayItemControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Expand All @@ -10,8 +10,7 @@
d:DesignHeight="600" d:DesignWidth="750">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <!-- Name -->
<RowDefinition Height="Auto" /> <!-- Detail data (see DataTemplates above) -->
<RowDefinition Height="Auto" /> <!-- Detail data -->
<RowDefinition Height="Auto" /> <!-- Display Parameters -->
<RowDefinition Height="Auto" /> <!-- Conditions -->
</Grid.RowDefinitions>
Expand All @@ -20,23 +19,17 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<Label Grid.Row="0" Grid.Column="0">Item Name:</Label>
<TextBox Grid.Row="0" Grid.Column="1" MinWidth="150"
VerticalContentAlignment="Center"
Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
ToolTip="Is only used for the list on the left side!" />

<ContentControl Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Content="{Binding}">
<ContentControl Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Content="{Binding}">
<d:ContentControl.Content>
<Border Padding="10" BorderBrush="Gray" BorderThickness="1" Margin="0,5">
<TextBlock Text="Placeholder for concrete DisplayItem" />
</Border>
</d:ContentControl.Content>
</ContentControl>

<local:DisplayParametersControl Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,20,0,0" />
<local:DisplayParametersControl Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,20,0,0" />

<local:ExpressionControl Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,20,0,0"
DataContext="{Binding ExpressionControlConditionViewModel}" />
<local:ExpressionControl Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,20,0,0"
DataContext="{Binding ExpressionControlConditionViewModel}" />
</Grid>
</UserControl>
</UserControl>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (C) 2026 Martin Renner
// LGPL-3.0-or-later (see file COPYING and COPYING.LESSER)

using System.Windows.Controls;

namespace StreamDeckSimHub.Plugin.ActionEditor.Views.Controls;

public partial class DisplayItemControl : UserControl
{
public DisplayItemControl()
{
InitializeComponent();
}
}
Loading
Loading