diff --git a/BetterMultiview/ObsMultiview/Controls/SceneSlot.xaml b/BetterMultiview/ObsMultiview/Controls/SceneSlot.xaml
index c404e4c..c1a3058 100644
--- a/BetterMultiview/ObsMultiview/Controls/SceneSlot.xaml
+++ b/BetterMultiview/ObsMultiview/Controls/SceneSlot.xaml
@@ -15,13 +15,16 @@
d:DesignHeight="450" d:DesignWidth="800">
-
+
-
-
-
-
+
+
+
+
+
+
@@ -36,6 +39,15 @@
Background="#83000000" VerticalAlignment="Top" Padding="5" FontSize="20"
Visibility="{Binding Name, Converter={StaticResource StringToVis}}" />
-
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BetterMultiview/ObsMultiview/Controls/SceneSlot.xaml.cs b/BetterMultiview/ObsMultiview/Controls/SceneSlot.xaml.cs
index d94c9ea..4a0d3b8 100644
--- a/BetterMultiview/ObsMultiview/Controls/SceneSlot.xaml.cs
+++ b/BetterMultiview/ObsMultiview/Controls/SceneSlot.xaml.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using System.Printing;
using System.Windows;
using System.Windows.Controls;
@@ -67,6 +68,14 @@ public partial class SceneSlot : UserControl {
set { SetValue(SlotConfiguringProperty, value); }
}
+ public static readonly DependencyProperty SetProperty = DependencyProperty.Register(
+ nameof(Set), typeof(Set), typeof(SceneSlot), new PropertyMetadata(default(Set)));
+
+ public Set Set {
+ get { return (Set)GetValue(SetProperty); }
+ set { SetValue(SetProperty, value); }
+ }
+
public SceneSlot(UserProfile.DSlot slot, StreamView owner) {
_slot = slot;
_owner = owner;
@@ -113,10 +122,13 @@ public partial class SceneSlot : UserControl {
private void LoadSlot() {
Unconfigured = string.IsNullOrEmpty(_slot.Obs.Scene);
Name = _slot.Name;
+ Set = _profile.ActiveProfile?.SceneView?.Sets.FirstOrDefault(x => x.Id == _slot.SetId);
}
private void SceneSlot_OnMouseRightButtonUp(object sender, MouseButtonEventArgs e) {
- var config = new SlotConfig(_slot);
+ if (_profile.ActiveProfile == null) return;
+
+ var config = new SlotConfig(_slot, _profile.ActiveProfile.SceneView);
config.Owner = Window.GetWindow(this);
_plugins.PausePlugins(null, true);
diff --git a/BetterMultiview/ObsMultiview/Converters/StringToVis.cs b/BetterMultiview/ObsMultiview/Converters/StringToVis.cs
index 364f4e1..ba51a8c 100644
--- a/BetterMultiview/ObsMultiview/Converters/StringToVis.cs
+++ b/BetterMultiview/ObsMultiview/Converters/StringToVis.cs
@@ -8,7 +8,7 @@ namespace ObsMultiview.Converters {
///
/// Convert a string to visibility (null or whitespace => hidden)
///
- public class StringToVis : IValueConverter {
+ public class NullToVis : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (value is string s) {
return string.IsNullOrWhiteSpace(s) ? Visibility.Collapsed : Visibility.Visible;
diff --git a/BetterMultiview/ObsMultiview/Data/IPluginSettingsProvider.cs b/BetterMultiview/ObsMultiview/Data/IPluginSettingsProvider.cs
new file mode 100644
index 0000000..1a33a88
--- /dev/null
+++ b/BetterMultiview/ObsMultiview/Data/IPluginSettingsProvider.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+using Newtonsoft.Json.Linq;
+
+namespace ObsMultiview.Data {
+ public interface IPluginSettingsProvider {
+ JObject GetPluginSettings(string pluginId);
+
+ void SetPluginSettings(string pluginId, JObject pluginSettings);
+
+ Guid Id { get; }
+
+ string Name { get; set; }
+ }
+}
diff --git a/BetterMultiview/ObsMultiview/Data/Set.cs b/BetterMultiview/ObsMultiview/Data/Set.cs
new file mode 100644
index 0000000..808df38
--- /dev/null
+++ b/BetterMultiview/ObsMultiview/Data/Set.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media;
+using JetBrains.Annotations;
+using Newtonsoft.Json.Linq;
+
+namespace ObsMultiview.Data {
+ public class Set : INotifyPropertyChanged, IPluginSettingsProvider {
+ private Color _color;
+ public string Name { get; set; }
+
+ ///
+ /// Plugin configs
+ ///
+ public Dictionary PluginConfigs { get; set; } = new();
+
+ public void SetPluginSettings(string pluginId, JObject pluginSettings) {
+ if (pluginSettings == null) {
+ PluginConfigs.Remove(pluginId);
+ } else {
+ PluginConfigs[pluginId] = pluginSettings;
+ }
+ }
+
+ public Guid? Id { get; set; }
+
+ Guid IPluginSettingsProvider.Id => Id ?? Guid.Empty;
+
+ public Color Color {
+ get => _color;
+ set {
+ if (value.Equals(_color)) return;
+ _color = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public Set() {
+ Id = Guid.NewGuid();
+ PluginConfigs = new Dictionary();
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [NotifyPropertyChangedInvocator]
+ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ public JObject GetPluginSettings(string pluginId) {
+ if (PluginConfigs.ContainsKey(pluginId)) {
+ return PluginConfigs[pluginId];
+ } else {
+ return null;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/BetterMultiview/ObsMultiview/Data/UserProfile.cs b/BetterMultiview/ObsMultiview/Data/UserProfile.cs
index 39d7621..5f780a6 100644
--- a/BetterMultiview/ObsMultiview/Data/UserProfile.cs
+++ b/BetterMultiview/ObsMultiview/Data/UserProfile.cs
@@ -1,5 +1,11 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.Serialization;
+using System.Windows.Media;
+using JetBrains.Annotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -11,11 +17,28 @@ public class UserProfile {
///
/// Config options for a single slot
///
- public class DSlot {
+ public class DSlot : INotifyPropertyChanged, IPluginSettingsProvider {
+ private string _name;
+
///
/// Name of the slot
///
- public string Name { get; set; }
+ public string Name {
+ get => _name;
+ set {
+ if (value == _name) return;
+ _name = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public void SetPluginSettings(string pluginId, JObject pluginSettings) {
+ if (pluginSettings == null) {
+ PluginConfigs.Remove(pluginId);
+ } else {
+ PluginConfigs[pluginId] = pluginSettings;
+ }
+ }
///
/// internal id of the slot. Not persistent during restarts
@@ -23,6 +46,11 @@ public class DSlot {
[JsonIgnore]
public Guid Id { get; }
+ ///
+ /// The id of the set this slot belongs to
+ ///
+ public Guid? SetId { get; set; }
+
///
/// OBS config
///
@@ -36,6 +64,12 @@ public class DSlot {
public DSlot() {
Obs = new DSlotObs();
Id = Guid.NewGuid();
+ PluginConfigs = new();
+ }
+
+ [OnDeserialized]
+ private void OnDeserialized(StreamingContext context) {
+ if (PluginConfigs == null) PluginConfigs = new();
}
public static bool operator ==(DSlot a, DSlot b) {
@@ -45,6 +79,21 @@ public class DSlot {
public static bool operator !=(DSlot a, DSlot b) {
return !(a == b);
}
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [NotifyPropertyChangedInvocator]
+ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ public JObject GetPluginSettings(string pluginId) {
+ if (PluginConfigs.ContainsKey(pluginId)) {
+ return PluginConfigs[pluginId];
+ } else {
+ return null;
+ }
+ }
}
///
@@ -72,9 +121,21 @@ public class DSceneViewConfig {
public int Columns { get; set; } = 6;
public List Slots { get; set; }
+ public List Sets { get; set; }
public DSceneViewConfig() {
Slots = new List();
+ Sets = new List();
+ Sets.Add(new Set {
+ Color = Colors.DarkGray,
+ Name = "Neutral",
+ Id = Guid.Empty
+ });
+ }
+
+ [OnDeserialized]
+ internal void OnDeserialized(StreamingContext context) {
+ Sets = Sets.DistinctBy(x => x.Id).ToList();
}
}
@@ -102,7 +163,7 @@ public class DObsProfile {
public DObsProfile(string id, int rows, int columns) {
Id = id;
- SceneView = new DSceneViewConfig {Rows = rows, Columns = columns};
+ SceneView = new DSceneViewConfig { Rows = rows, Columns = columns };
}
}
diff --git a/BetterMultiview/ObsMultiview/Dialogs/SetConfig.xaml b/BetterMultiview/ObsMultiview/Dialogs/SetConfig.xaml
new file mode 100644
index 0000000..cff2cc2
--- /dev/null
+++ b/BetterMultiview/ObsMultiview/Dialogs/SetConfig.xaml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BetterMultiview/ObsMultiview/Dialogs/SetConfig.xaml.cs b/BetterMultiview/ObsMultiview/Dialogs/SetConfig.xaml.cs
new file mode 100644
index 0000000..b1589d0
--- /dev/null
+++ b/BetterMultiview/ObsMultiview/Dialogs/SetConfig.xaml.cs
@@ -0,0 +1,186 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Data;
+using Autofac;
+using JetBrains.Annotations;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using ObsMultiview.Data;
+using ObsMultiview.Plugins;
+using ObsMultiview.Services;
+
+namespace ObsMultiview.Dialogs {
+ ///
+ /// Config Dialog for a scene slot
+ ///
+ public partial class SetConfig : Window {
+ private readonly ILogger _logger;
+
+ public static readonly DependencyProperty SetProperty = DependencyProperty.Register(
+ nameof(Set), typeof(Set), typeof(SetConfig), new PropertyMetadata(default(Set)));
+
+ public Set Set {
+ get { return (Set)GetValue(SetProperty); }
+ set { SetValue(SetProperty, value); }
+ }
+
+ public static readonly DependencyProperty AvailableScenesProperty = DependencyProperty.Register(
+ nameof(AvailableScenes), typeof(ObservableCollection), typeof(SetConfig),
+ new PropertyMetadata(default(ObservableCollection)));
+
+ public ObservableCollection AvailableScenes {
+ get { return (ObservableCollection)GetValue(AvailableScenesProperty); }
+ set { SetValue(AvailableScenesProperty, value); }
+ }
+ public static readonly DependencyProperty PluginStateProperty = DependencyProperty.Register(
+ nameof(PluginState), typeof(Dictionary), typeof(SetConfig),
+ new PropertyMetadata(default(Dictionary)));
+
+ public Dictionary PluginState {
+ get { return (Dictionary)GetValue(PluginStateProperty); }
+ set { SetValue(PluginStateProperty, value); }
+ }
+
+ private string _originalConfig;
+ private readonly ObsWatchService _obs;
+ private readonly PluginService _plugins;
+ private readonly List<(PluginBase plugin, SettingsControl settings)> _pluginSettings = new();
+
+ public SetConfig(Set set) {
+ _originalConfig = JsonConvert.SerializeObject(set);
+ Set = set;
+
+ InitializeComponent();
+
+ _obs = App.Container.Resolve();
+ _plugins = App.Container.Resolve();
+ _logger = App.Container.Resolve>();
+
+ var scenes = _obs.WebSocket.GetSceneList().Scenes.Select(x => x.Name)
+ .Where(x => x != "multiview" && x != "preview");
+ AvailableScenes = new ObservableCollection(scenes);
+ PluginState = new Dictionary();
+
+ if (Set.PluginConfigs == null)
+ Set.PluginConfigs = new Dictionary();
+
+ // load config controls for all active plugins
+ foreach (var plugin in _plugins.Plugins.Where(x => x.Active && x.Plugin.HasSlotSettings)) {
+ PluginState.Add(plugin.Plugin.Name, Set.PluginConfigs.ContainsKey(plugin.Plugin.Name));
+
+ var expander = new Expander();
+
+ var title = new CheckBox();
+ title.Content = new TextBlock {
+ Text = plugin.Plugin.Name,
+ Style = TryFindResource("Title") as Style,
+ HorizontalAlignment = HorizontalAlignment.Stretch
+ };
+ title.HorizontalAlignment = HorizontalAlignment.Stretch;
+ var bind = new Binding();
+ bind.Path = new PropertyPath($"PluginState[{plugin.Plugin.Name}].Value");
+ bind.Mode = BindingMode.TwoWay;
+ bind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
+ BindingOperations.SetBinding(title, ToggleButton.IsCheckedProperty, bind);
+ expander.Header = title;
+ expander.IsExpanded = PluginState[plugin.Plugin.Name];
+
+ var slotSettings = plugin.Plugin.GetSlotSettings(Set.Id);
+ if (slotSettings == null) return;
+
+ slotSettings.FetchSettings();
+ slotSettings.Margin = new Thickness(0, 0, 0, 10);
+ bind = new Binding();
+ bind.Source = this;
+ bind.Path = new PropertyPath($"PluginState[{plugin.Plugin.Name}].Value");
+ bind.Mode = BindingMode.OneWay;
+ BindingOperations.SetBinding(slotSettings, UserControl.IsEnabledProperty, bind);
+
+ _pluginSettings.Add((plugin.Plugin, slotSettings));
+ expander.Content = slotSettings;
+
+ ConfigPanel.Children.Add(expander);
+ }
+
+ input.Focus();
+ }
+
+ private void Ok_OnClick(object sender, RoutedEventArgs e) {
+ DialogResult = true;
+
+ foreach (var item in _pluginSettings) {
+ try {
+ item.settings.WriteSettings();
+ } catch (Exception ex) {
+ _logger.LogError(ex, "Failed to write slot settings for " + item.plugin.Name);
+ }
+ }
+
+ foreach (var plugin in PluginState.Where(x => !x.Value).Select(x => x.Key)) {
+ Set.PluginConfigs.Remove(plugin);
+ }
+
+ Close();
+ }
+
+ private void Cancel_OnClick(object sender, RoutedEventArgs e) {
+ DialogResult = false;
+ JsonConvert.PopulateObject(_originalConfig, Set,
+ new() { ObjectCreationHandling = ObjectCreationHandling.Replace });
+
+ Close();
+ }
+
+ private void SlotConfig_OnClosing(object sender, CancelEventArgs e) {
+ if (DialogResult == null) {
+ DialogResult = false;
+ JsonConvert.PopulateObject(_originalConfig, Set,
+ new() { ObjectCreationHandling = ObjectCreationHandling.Replace });
+ }
+ }
+
+ private void Unlink_OnClick(object sender, RoutedEventArgs e) {
+ // Reset this slot & delete all configs
+ JsonConvert.PopulateObject(JsonConvert.SerializeObject(new UserProfile.DSlot()), Set,
+ new() { ObjectCreationHandling = ObjectCreationHandling.Replace });
+ DialogResult = true;
+ Close();
+ }
+
+ public class ObservableBoolean : INotifyPropertyChanged {
+ private bool _value;
+
+ public bool Value {
+ get => _value;
+ set {
+ if (value == _value) return;
+ _value = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [NotifyPropertyChangedInvocator]
+ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ public static implicit operator ObservableBoolean(bool value) {
+ return new ObservableBoolean { Value = value };
+ }
+
+ public static implicit operator bool(ObservableBoolean value) {
+ return value.Value;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/BetterMultiview/ObsMultiview/Dialogs/SetEditor.xaml b/BetterMultiview/ObsMultiview/Dialogs/SetEditor.xaml
new file mode 100644
index 0000000..abecfed
--- /dev/null
+++ b/BetterMultiview/ObsMultiview/Dialogs/SetEditor.xaml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BetterMultiview/ObsMultiview/Dialogs/SetEditor.xaml.cs b/BetterMultiview/ObsMultiview/Dialogs/SetEditor.xaml.cs
new file mode 100644
index 0000000..a6b47e0
--- /dev/null
+++ b/BetterMultiview/ObsMultiview/Dialogs/SetEditor.xaml.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Windows;
+using System.Windows.Input;
+using ObsMultiview.Data;
+
+namespace ObsMultiview.Dialogs {
+ ///
+ /// Interaktionslogik für SetEditor.xaml
+ ///
+ public partial class SetEditor : Window {
+ public static readonly DependencyProperty SetsProperty = DependencyProperty.Register(
+ nameof(Sets), typeof(ObservableCollection), typeof(SetEditor),
+ new PropertyMetadata(default(ObservableCollection)));
+
+ public ObservableCollection Sets {
+ get { return (ObservableCollection)GetValue(SetsProperty); }
+ set { SetValue(SetsProperty, value); }
+ }
+
+ public static RoutedUICommand EditSet { get; }
+
+ static SetEditor() {
+ EditSet = new RoutedUICommand();
+ }
+
+ public SetEditor() {
+ InitializeComponent();
+ }
+
+ private void Ok_OnClick(object sender, RoutedEventArgs e) {
+ DialogResult = true;
+ Close();
+ }
+
+ private void Cancel_OnClick(object sender, RoutedEventArgs e) {
+ DialogResult = false;
+ Close();
+ }
+
+ private void EditSet_OnExecuted(object sender, ExecutedRoutedEventArgs e) {
+ var set = e.Parameter as Set;
+
+ if (set != null) {
+ var editor = new SetConfig(set);
+ editor.Owner = this;
+ editor.ShowDialog();
+ }
+ }
+
+ private void EditSet_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) {
+ var set = e.Parameter as Set;
+
+ if (set != null) {
+ e.CanExecute = set.Id != null && set.Id != Guid.Empty;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/BetterMultiview/ObsMultiview/Dialogs/SlotConfig.xaml b/BetterMultiview/ObsMultiview/Dialogs/SlotConfig.xaml
index bf7cda8..6f3e6af 100644
--- a/BetterMultiview/ObsMultiview/Dialogs/SlotConfig.xaml
+++ b/BetterMultiview/ObsMultiview/Dialogs/SlotConfig.xaml
@@ -5,6 +5,8 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ObsMultiview"
xmlns:lex="http://wpflocalizeextension.codeplex.com"
+ xmlns:data="clr-namespace:ObsMultiview.Data"
+ xmlns:converters="clr-namespace:ObsMultiview.Converters"
mc:Ignorable="d"
Icon="pack://application:,,,/ObsMultiview;component/Images/Icon.png"
Closing="SlotConfig_OnClosing"
@@ -17,6 +19,8 @@
Title="{lex:Loc SlotConfig}" Height="450" Width="345">
+
+
@@ -46,6 +50,14 @@
OBS
+
+
+
+
+
+
+
+
diff --git a/BetterMultiview/ObsMultiview/Dialogs/SlotConfig.xaml.cs b/BetterMultiview/ObsMultiview/Dialogs/SlotConfig.xaml.cs
index 373d0a7..1b5b54e 100644
--- a/BetterMultiview/ObsMultiview/Dialogs/SlotConfig.xaml.cs
+++ b/BetterMultiview/ObsMultiview/Dialogs/SlotConfig.xaml.cs
@@ -3,13 +3,18 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
+using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Data;
using Autofac;
+using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using ObsMultiview.Data;
+using ObsMultiview.Extensions;
using ObsMultiview.Plugins;
using ObsMultiview.Services;
@@ -25,7 +30,7 @@ public partial class SlotConfig : Window {
new PropertyMetadata(default(UserProfile.DSlot)));
public UserProfile.DSlot Slot {
- get { return (UserProfile.DSlot) GetValue(SlotProperty); }
+ get { return (UserProfile.DSlot)GetValue(SlotProperty); }
set { SetValue(SlotProperty, value); }
}
@@ -34,18 +39,38 @@ public partial class SlotConfig : Window {
new PropertyMetadata(default(ObservableCollection)));
public ObservableCollection AvailableScenes {
- get { return (ObservableCollection) GetValue(AvailableScenesProperty); }
+ get { return (ObservableCollection)GetValue(AvailableScenesProperty); }
set { SetValue(AvailableScenesProperty, value); }
}
+ public static readonly DependencyProperty SetsProperty = DependencyProperty.Register(
+ nameof(Sets), typeof(ObservableCollection), typeof(SlotConfig),
+ new PropertyMetadata(default(ObservableCollection)));
+
+ public ObservableCollection Sets {
+ get { return (ObservableCollection)GetValue(SetsProperty); }
+ set { SetValue(SetsProperty, value); }
+ }
+
+ public static readonly DependencyProperty PluginStateProperty = DependencyProperty.Register(
+ nameof(PluginState), typeof(Dictionary), typeof(SlotConfig),
+ new PropertyMetadata(default(Dictionary)));
+
+ public Dictionary PluginState {
+ get { return (Dictionary)GetValue(PluginStateProperty); }
+ set { SetValue(PluginStateProperty, value); }
+ }
+
private string _originalConfig;
private readonly ObsWatchService _obs;
private readonly PluginService _plugins;
private readonly List<(PluginBase plugin, SettingsControl settings)> _pluginSettings = new();
- public SlotConfig(UserProfile.DSlot slot) {
+ public SlotConfig(UserProfile.DSlot slot, UserProfile.DSceneViewConfig config) {
_originalConfig = JsonConvert.SerializeObject(slot);
Slot = slot;
+ Sets = new ObservableCollection(config.Sets);
+ Sets.Insert(0, new Set { Name = Localizer.Localize("Dialogs","NoSet"), Id = null });
InitializeComponent();
@@ -56,25 +81,42 @@ public partial class SlotConfig : Window {
var scenes = _obs.WebSocket.GetSceneList().Scenes.Select(x => x.Name)
.Where(x => x != "multiview" && x != "preview");
AvailableScenes = new ObservableCollection(scenes);
+ PluginState = new Dictionary();
if (Slot.PluginConfigs == null)
Slot.PluginConfigs = new Dictionary();
// load config controls for all active plugins
foreach (var plugin in _plugins.Plugins.Where(x => x.Active && x.Plugin.HasSlotSettings)) {
+ PluginState.Add(plugin.Plugin.Name, Slot.PluginConfigs.ContainsKey(plugin.Plugin.Name));
+
var expander = new Expander();
- var title = new TextBlock();
- title.Text = plugin.Plugin.Name;
- title.Style = TryFindResource("Title") as Style;
+ var title = new CheckBox();
+ title.Content = new TextBlock {
+ Text = plugin.Plugin.Name,
+ Style = TryFindResource("Title") as Style,
+ HorizontalAlignment = HorizontalAlignment.Stretch
+ };
title.HorizontalAlignment = HorizontalAlignment.Stretch;
+ var bind = new Binding();
+ bind.Path = new PropertyPath($"PluginState[{plugin.Plugin.Name}].Value");
+ bind.Mode = BindingMode.TwoWay;
+ bind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
+ BindingOperations.SetBinding(title, ToggleButton.IsCheckedProperty, bind);
expander.Header = title;
+ expander.IsExpanded = PluginState[plugin.Plugin.Name];
var slotSettings = plugin.Plugin.GetSlotSettings(slot.Id);
if (slotSettings == null) return;
slotSettings.FetchSettings();
slotSettings.Margin = new Thickness(0, 0, 0, 10);
+ bind = new Binding();
+ bind.Source = this;
+ bind.Path = new PropertyPath($"PluginState[{plugin.Plugin.Name}].Value");
+ bind.Mode = BindingMode.OneWay;
+ BindingOperations.SetBinding(slotSettings, UserControl.IsEnabledProperty, bind);
_pluginSettings.Add((plugin.Plugin, slotSettings));
expander.Content = slotSettings;
@@ -96,27 +138,63 @@ public partial class SlotConfig : Window {
}
}
+ foreach (var plugin in PluginState.Where(x => !x.Value).Select(x => x.Key)) {
+ Slot.PluginConfigs.Remove(plugin);
+ }
+
Close();
}
private void Cancel_OnClick(object sender, RoutedEventArgs e) {
DialogResult = false;
- JsonConvert.PopulateObject(_originalConfig, Slot);
+ JsonConvert.PopulateObject(_originalConfig, Slot,
+ new() { ObjectCreationHandling = ObjectCreationHandling.Replace });
+
Close();
}
private void SlotConfig_OnClosing(object sender, CancelEventArgs e) {
if (DialogResult == null) {
DialogResult = false;
- JsonConvert.PopulateObject(_originalConfig, Slot);
+ JsonConvert.PopulateObject(_originalConfig, Slot,
+ new() { ObjectCreationHandling = ObjectCreationHandling.Replace });
}
}
private void Unlink_OnClick(object sender, RoutedEventArgs e) {
// Reset this slot & delete all configs
- JsonConvert.PopulateObject(JsonConvert.SerializeObject(new UserProfile.DSlot()), Slot);
+ JsonConvert.PopulateObject(JsonConvert.SerializeObject(new UserProfile.DSlot()), Slot,
+ new() { ObjectCreationHandling = ObjectCreationHandling.Replace });
DialogResult = true;
Close();
}
+
+ public class ObservableBoolean : INotifyPropertyChanged {
+ private bool _value;
+
+ public bool Value {
+ get => _value;
+ set {
+ if (value == _value) return;
+ _value = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [NotifyPropertyChangedInvocator]
+ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ public static implicit operator ObservableBoolean(bool value) {
+ return new ObservableBoolean { Value = value };
+ }
+
+ public static implicit operator bool(ObservableBoolean value) {
+ return value.Value;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/BetterMultiview/ObsMultiview/MainWindow.xaml b/BetterMultiview/ObsMultiview/MainWindow.xaml
index 20e77b3..60c6f12 100644
--- a/BetterMultiview/ObsMultiview/MainWindow.xaml
+++ b/BetterMultiview/ObsMultiview/MainWindow.xaml
@@ -17,7 +17,7 @@
Title="Stream Deck" Height="450" Width="800">
-
+
diff --git a/BetterMultiview/ObsMultiview/ObsMultiview.csproj b/BetterMultiview/ObsMultiview/ObsMultiview.csproj
index ee2226a..7e0cfba 100644
--- a/BetterMultiview/ObsMultiview/ObsMultiview.csproj
+++ b/BetterMultiview/ObsMultiview/ObsMultiview.csproj
@@ -4,8 +4,9 @@
WinExe
net6.0-windows
true
- 1.0.0.0
+ 2.0.0.0
Icon.ico
+ 2.0.0.0
@@ -22,6 +23,7 @@
+
@@ -43,6 +45,9 @@
+
+ Code
+
True
True
@@ -72,4 +77,11 @@
+
+
+ $(DefaultXamlRuntime)
+ Designer
+
+
+
diff --git a/BetterMultiview/ObsMultiview/Services/PluginService.cs b/BetterMultiview/ObsMultiview/Services/PluginService.cs
index fb5e8cd..05d58c1 100644
--- a/BetterMultiview/ObsMultiview/Services/PluginService.cs
+++ b/BetterMultiview/ObsMultiview/Services/PluginService.cs
@@ -235,11 +235,17 @@ private class CommandFacadeBound : CommandFacade {
_logger.LogDebug($"Requesting slot config for slot {guid}");
var slot = _profile.ActiveProfile.SceneView.Slots.FirstOrDefault(x => x.Id == guid);
- if (slot != null && slot.PluginConfigs != null) {
- return slot.PluginConfigs.FirstOrDefault(x => x.Key == _plugin.Name).Value;
+ if (slot != null && slot.PluginConfigs.ContainsKey(_plugin.Name)) {
+ return slot.PluginConfigs[_plugin.Name];
} else {
- return new JObject();
+ var set = _profile.ActiveProfile.SceneView.Sets.FirstOrDefault(x => x.Id == guid);
+
+ if (set != null && set.PluginConfigs.ContainsKey(_plugin.Name)) {
+ return set.PluginConfigs[_plugin.Name];
+ }
}
+
+ return null;
}
protected override IEnumerable<(Guid id, JObject config)> RequestSlotSettings() {
@@ -254,10 +260,13 @@ private class CommandFacadeBound : CommandFacade {
var slot = _profile.ActiveProfile.SceneView.Slots.FirstOrDefault(x => x.Id == guid);
if (slot != null) {
- if (slot.PluginConfigs == null)
- slot.PluginConfigs = new Dictionary();
-
slot.PluginConfigs[_plugin.Name] = config;
+ } else {
+ var set = _profile.ActiveProfile.SceneView.Sets.FirstOrDefault(x => x.Id == guid);
+
+ if (set != null) {
+ set.PluginConfigs[_plugin.Name] = config;
+ }
}
}
}
diff --git a/BetterMultiview/ObsMultiview/Services/SceneService.cs b/BetterMultiview/ObsMultiview/Services/SceneService.cs
index e326d20..c9f89cd 100644
--- a/BetterMultiview/ObsMultiview/Services/SceneService.cs
+++ b/BetterMultiview/ObsMultiview/Services/SceneService.cs
@@ -1,6 +1,8 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
+using Newtonsoft.Json.Linq;
using ObsMultiview.Data;
using ObsMultiview.Plugins;
@@ -16,6 +18,7 @@ public class SceneService {
private UserProfile.DSlot _previewScene;
private UserProfile.DSlot _liveScene;
+ private Dictionary _activeSettings = new();
///
/// Fired when the active preview changes
@@ -93,15 +96,18 @@ public class SceneService {
_logger.LogDebug($"Unapplying scene {slot?.Name} to {next?.Name}");
foreach (var plugin in
- _plugins.Plugins.Where(x =>
- (slot?.PluginConfigs?.ContainsKey(x.Plugin.Name) ?? false) &&
- x.Plugin.TriggerType == PluginTriggerType.Change)) {
-
+ _plugins.Plugins.Where(x => x.Active && x.Plugin.TriggerType == PluginTriggerType.Change)) {
if (plugin.Plugin is ChangePluginBase cplug) {
- try {
- cplug.OnSlotExit(slot.Id, next?.Id);
- } catch (Exception ex) {
- _logger.LogError(ex,$"Failed to trigger SlotExit for plugin {cplug.Name}");
+ var current = ResolveActiveSettings(cplug, slot);
+ var nextS = ResolveActiveSettings(cplug, next);
+
+ if (_activeSettings[cplug] != nextS) {
+ try {
+ cplug.OnSlotExit(current.GetPluginSettings(cplug.Name),
+ nextS.GetPluginSettings(cplug.Name));
+ } catch (Exception ex) {
+ _logger.LogError(ex, $"Failed to trigger SlotExit for plugin {cplug.Name}");
+ }
}
}
}
@@ -123,30 +129,66 @@ public class SceneService {
}
foreach (var plugin in
- _plugins.Plugins.Where(x => (slot?.PluginConfigs?.ContainsKey(x.Plugin.Name) ?? false)
- && (x.Plugin.TriggerType == PluginTriggerType.Change))) {
+ _plugins.Plugins.Where(x => x.Active && x.Plugin.TriggerType == PluginTriggerType.Change)) {
if (plugin.Plugin is ChangePluginBase cplug) {
- try {
- cplug.OnSlotEnter(slot.Id, prev?.Id);
- } catch (Exception ex) {
- _logger.LogError(ex,$"Failed to trigger SlotEnter for plugin {cplug.Name}");
+ var next = ResolveActiveSettings(cplug, slot);
+ var prevS = ResolveActiveSettings(cplug, prev);
+
+ if (_activeSettings[cplug] != next) {
+ try {
+ cplug.OnSlotEnter(next.GetPluginSettings(cplug.Name), prevS.GetPluginSettings(cplug.Name));
+ } catch (Exception ex) {
+ _logger.LogError(ex, $"Failed to trigger SlotEnter for plugin {cplug.Name}");
+ }
+
+ _activeSettings[cplug] = next;
}
}
}
foreach (var plugin in
- _plugins.Plugins.Where(x => (slot?.PluginConfigs?.ContainsKey(x.Plugin.Name) ?? false)
- && (x.Plugin.TriggerType == PluginTriggerType.State))) {
+ _plugins.Plugins.Where(x => x.Active && x.Plugin.TriggerType == PluginTriggerType.State)) {
if (plugin.Plugin is StatePluginBase splug) {
- try {
- splug.ActiveSlotChanged(slot.Id);
- } catch (Exception ex) {
- _logger.LogError(ex,$"Failed to apply slot state for plugin {splug.Name}");
+ var config = ResolveActiveSettings(splug, slot);
+
+ if (_activeSettings[splug] != config) {
+ try {
+ splug.ActiveSettingsChanged(config.GetPluginSettings(splug.Name));
+ } catch (Exception ex) {
+ _logger.LogError(ex, $"Failed to apply slot state for plugin {splug.Name}");
+ }
+
+ _activeSettings[splug] = config;
}
}
}
}
+ private IPluginSettingsProvider ResolveActiveSettings(PluginBase plugin, UserProfile.DSlot slot) {
+ if (slot == null) return null;
+
+ if (!_activeSettings.ContainsKey(plugin))
+ _activeSettings.Add(plugin, null);
+
+ if (slot.PluginConfigs.ContainsKey(plugin.Name) && slot.PluginConfigs[plugin.Name] != null) {
+ return slot;
+ } else if (slot.SetId != null) {
+ var set = _profile.ActiveProfile.SceneView.Sets.FirstOrDefault(x => x.Id == slot.SetId);
+
+ if (set != null && set.Id == Guid.Empty) {
+ _activeSettings.TryGetValue(plugin, out var config);
+ return config;
+ } else if (set != null && set.PluginConfigs.ContainsKey(plugin.Name) &&
+ set.PluginConfigs[plugin.Name] != null) {
+ return set;
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
public void ClearPreview() {
OnPreviewChanged(null);
}
diff --git a/BetterMultiview/ObsMultiview/StreamView.xaml b/BetterMultiview/ObsMultiview/StreamView.xaml
index 5add5ba..880a9b7 100644
--- a/BetterMultiview/ObsMultiview/StreamView.xaml
+++ b/BetterMultiview/ObsMultiview/StreamView.xaml
@@ -92,6 +92,10 @@
FontSize="16" FontFamily="Segoe MDL2 Assets" Margin="5" Click="ProfileSettings_OnClick">
+
diff --git a/BetterMultiview/ObsMultiview/StreamView.xaml.cs b/BetterMultiview/ObsMultiview/StreamView.xaml.cs
index 49d79d3..75e4b21 100644
--- a/BetterMultiview/ObsMultiview/StreamView.xaml.cs
+++ b/BetterMultiview/ObsMultiview/StreamView.xaml.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Numerics;
@@ -290,5 +291,25 @@ public partial class StreamView : Window {
private void ReplaceRunningConfig(UserProfile.DSceneViewConfig config) {
_watcher.ReplaceProfile(config);
}
+
+ private void ConfigSets_OnClick(object sender, RoutedEventArgs e) {
+ if (_watcher?.ActiveProfile == null) return;
+
+ var dlg = new SetEditor();
+ dlg.Owner = this;
+ dlg.Sets = new ObservableCollection(_watcher.ActiveProfile.SceneView.Sets ?? new List());
+
+ if (dlg.ShowDialog() == true) {
+ _watcher.ActiveProfile.SceneView.Sets = dlg.Sets?.ToList();
+
+ foreach (var slot in _watcher.ActiveProfile.SceneView.Slots) {
+ if (slot.SetId != null && !dlg.Sets.Any(x=>x.Id == slot.SetId.Value)) {
+ slot.SetId = null;
+ }
+ }
+
+ SceneCollectionChanged(_watcher.ActiveProfile);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/BetterMultiview/ObsMultiview/Strings/Dialogs.Designer.cs b/BetterMultiview/ObsMultiview/Strings/Dialogs.Designer.cs
index ceba6e2..02ecc18 100644
--- a/BetterMultiview/ObsMultiview/Strings/Dialogs.Designer.cs
+++ b/BetterMultiview/ObsMultiview/Strings/Dialogs.Designer.cs
@@ -105,6 +105,15 @@ internal class Dialogs {
}
}
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Set ähnelt.
+ ///
+ internal static string Group {
+ get {
+ return ResourceManager.GetString("Group", resourceCulture);
+ }
+ }
+
///
/// Sucht eine lokalisierte Zeichenfolge, die Import ähnelt.
///
@@ -123,6 +132,15 @@ internal class Dialogs {
}
}
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die No Set ähnelt.
+ ///
+ internal static string NoSet {
+ get {
+ return ResourceManager.GetString("NoSet", resourceCulture);
+ }
+ }
+
///
/// Sucht eine lokalisierte Zeichenfolge, die Ok ähnelt.
///
@@ -204,6 +222,24 @@ internal class Dialogs {
}
}
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Set Config ähnelt.
+ ///
+ internal static string SetConfig {
+ get {
+ return ResourceManager.GetString("SetConfig", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Edit Sets ähnelt.
+ ///
+ internal static string SetEditor {
+ get {
+ return ResourceManager.GetString("SetEditor", resourceCulture);
+ }
+ }
+
///
/// Sucht eine lokalisierte Zeichenfolge, die Slot Config ähnelt.
///
diff --git a/BetterMultiview/ObsMultiview/Strings/Dialogs.de-DE.resx b/BetterMultiview/ObsMultiview/Strings/Dialogs.de-DE.resx
index a0ff3cd..c4bf141 100644
--- a/BetterMultiview/ObsMultiview/Strings/Dialogs.de-DE.resx
+++ b/BetterMultiview/ObsMultiview/Strings/Dialogs.de-DE.resx
@@ -168,4 +168,16 @@
Exportieren
+
+ Gruppen Bearbeiten
+
+
+ Gruppe Konfigurieren
+
+
+ Gruppe
+
+
+ Keine Gruppe
+
\ No newline at end of file
diff --git a/BetterMultiview/ObsMultiview/Strings/Dialogs.resx b/BetterMultiview/ObsMultiview/Strings/Dialogs.resx
index c3b54b7..191fc01 100644
--- a/BetterMultiview/ObsMultiview/Strings/Dialogs.resx
+++ b/BetterMultiview/ObsMultiview/Strings/Dialogs.resx
@@ -148,4 +148,16 @@
Export
+
+ Edit Sets
+
+
+ Set Config
+
+
+ Set
+
+
+ No Set
+
\ No newline at end of file
diff --git a/BetterMultiview/Plugins/CommandFacade.cs b/BetterMultiview/Plugins/CommandFacade.cs
new file mode 100644
index 0000000..06a0c92
--- /dev/null
+++ b/BetterMultiview/Plugins/CommandFacade.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json.Linq;
+
+namespace ObsMultiview.Plugins {
+ ///
+ /// A facade to interface with the normal application
+ ///
+ public abstract class CommandFacade {
+ ///
+ /// Fired when the settings have changed
+ ///
+ public event Action SettingsChanged;
+
+ ///
+ /// Fired when a slot config has changed
+ ///
+ public event Action SlotConfigChanged;
+
+ ///
+ /// The logging instance for this plugin
+ ///
+ public ILogger Logger { get; protected set; }
+
+ ///
+ /// Request settings
+ ///
+ /// The settings class
+ /// The subtype to fetch or null for the default settings
+ ///
+ public T RequestSettings(string subtype = null) {
+ var json = RequestSettings(subtype);
+
+ if (json != null) {
+ return json.ToObject();
+ } else {
+ return new JObject().ToObject();
+ }
+ }
+
+ ///
+ /// Save settings
+ ///
+ /// The settings class
+ /// The global settings
+ /// The subtype of the settings
+ public void WriteSettings(T settings, string subtype = null) {
+ var json = JObject.FromObject(settings);
+ WriteSettings(json, subtype);
+ OnSettingsChanged(subtype);
+ }
+
+ ///
+ /// Request settings for a slot
+ ///
+ /// The settings class
+ /// The slot id
+ ///
+ public T RequestSlotSetting(Guid? slot) {
+ if (slot == null) return default(T);
+
+ var json = RequestSlotSetting(slot.Value);
+
+ if (json != null) {
+ return json.ToObject();
+ } else {
+ return new JObject().ToObject();
+ }
+ }
+
+ ///
+ /// Request all slot settings with their IDs
+ ///
+ /// The settings class
+ ///
+ public IEnumerable<(Guid id, T config)> RequestSlotSettings() {
+ foreach (var item in RequestSlotSettings()) {
+ if (item.Item2 != null) {
+ yield return (item.Item1, item.Item2.ToObject());
+ }
+ }
+ }
+
+ ///
+ /// Save slot settings
+ ///
+ /// The settings class
+ /// The slot ID
+ /// The slot settings
+ public void WriteSlotSettings(Guid id, T config) {
+ WriteSlotSettings(id, JObject.FromObject(config));
+ OnSlotConfigChanged(id);
+ }
+
+ ///
+ /// Activate a scene in preview mode
+ ///
+ /// The scene ID
+ public abstract void ActivateScene(Guid scene);
+
+ ///
+ /// Switch the currently live and preview scenes
+ ///
+ public abstract void SwitchLive();
+
+ ///
+ protected abstract JObject RequestSettings(string subtype = null);
+
+ ///
+ protected abstract void WriteSettings(JObject settings, string subtype = null);
+
+ ///
+ protected abstract JObject RequestSlotSetting(Guid slot);
+
+ ///
+ protected abstract IEnumerable<(Guid id, JObject config)> RequestSlotSettings();
+
+ ///
+ protected abstract void WriteSlotSettings(Guid id, JObject config);
+
+ private void OnSettingsChanged(string obj) {
+ SettingsChanged?.Invoke(obj);
+ }
+
+ private void OnSlotConfigChanged(Guid obj) {
+ SlotConfigChanged?.Invoke(obj);
+ }
+ }
+}
\ No newline at end of file
diff --git a/BetterMultiview/Plugins/KNX/KnxPlugin.cs b/BetterMultiview/Plugins/KNX/KnxPlugin.cs
index d2d3225..45fb58e 100644
--- a/BetterMultiview/Plugins/KNX/KnxPlugin.cs
+++ b/BetterMultiview/Plugins/KNX/KnxPlugin.cs
@@ -8,7 +8,7 @@ namespace ObsMultiview.Plugins.KNX {
///
/// A plugin to talk to a KNX/IP Interface
///
- public class KnxPlugin : ChangePluginBase {
+ public class KnxPlugin : ChangePluginBase {
public override string Name => "KNX";
public override string Author => "Nathanael Schneider";
@@ -65,21 +65,17 @@ public class KnxPlugin : ChangePluginBase {
return new GlobalSettings(CommandFacade);
}
- public override SettingsControl GetSlotSettings(Guid slot) {
+ public override SettingsControl GetSlotSettings(Guid? slot) {
return new SlotSettings(this, CommandFacade, slot);
}
- public override void OnSlotExit(Guid slot, Guid? next) {
- var config = CommandFacade.RequestSlotSetting(slot);
-
+ protected override void OnSlotExit(KnxSlotSettings config, KnxSlotSettings? next) {
foreach (var group in config.Groups.Where(x => x.OnExit != null && x.OnExit.Length > 0)) {
_knx.Action(group.Group.GroupAddress, group.OnExit);
}
}
- public override void OnSlotEnter(Guid slot, Guid? previous) {
- var config = CommandFacade.RequestSlotSetting(slot);
-
+ protected override void OnSlotEnter(KnxSlotSettings config, KnxSlotSettings? previous) {
foreach (var group in config.Groups.Where(x => x.OnEntry != null && x.OnEntry.Length > 0)) {
_knx.Action(group.Group.GroupAddress, group.OnEntry);
}
diff --git a/BetterMultiview/Plugins/KNX/SlotSettings.xaml.cs b/BetterMultiview/Plugins/KNX/SlotSettings.xaml.cs
index 64eb74a..7c9ac15 100644
--- a/BetterMultiview/Plugins/KNX/SlotSettings.xaml.cs
+++ b/BetterMultiview/Plugins/KNX/SlotSettings.xaml.cs
@@ -16,7 +16,7 @@ public partial class SlotSettings : SlotSettingsControl {
set { SetValue(PluginProperty, value); }
}
- public SlotSettings(KnxPlugin plugin, CommandFacade commandFacade, Guid slotID) : base(commandFacade, slotID) {
+ public SlotSettings(KnxPlugin plugin, CommandFacade commandFacade, Guid? slotID) : base(commandFacade, slotID) {
Plugin = plugin;
InitializeComponent();
}
diff --git a/BetterMultiview/Plugins/Keyboard/KeyboardPlugin.cs b/BetterMultiview/Plugins/Keyboard/KeyboardPlugin.cs
index 0f70cbd..6cd50d8 100644
--- a/BetterMultiview/Plugins/Keyboard/KeyboardPlugin.cs
+++ b/BetterMultiview/Plugins/Keyboard/KeyboardPlugin.cs
@@ -131,7 +131,7 @@ public class KeyboardPlugin : TriggerPluginBase {
return new GlobalSettings(CommandFacade);
}
- public override SettingsControl GetSlotSettings(Guid id) {
+ public override SettingsControl GetSlotSettings(Guid? id) {
return new SlotSettings(CommandFacade, id);
}
}
diff --git a/BetterMultiview/Plugins/Keyboard/SlotSettings.xaml.cs b/BetterMultiview/Plugins/Keyboard/SlotSettings.xaml.cs
index 3cd4285..144bf4b 100644
--- a/BetterMultiview/Plugins/Keyboard/SlotSettings.xaml.cs
+++ b/BetterMultiview/Plugins/Keyboard/SlotSettings.xaml.cs
@@ -6,7 +6,7 @@ namespace ObsMultiview.Plugins.Keyboard {
///
public partial class SlotSettings : SlotSettingsControl {
- public SlotSettings(CommandFacade commandFacade, Guid slotID) : base(commandFacade, slotID) {
+ public SlotSettings(CommandFacade commandFacade, Guid? slotID) : base(commandFacade, slotID) {
InitializeComponent();
Grabber.SetCommandFacade(commandFacade);
}
diff --git a/BetterMultiview/Plugins/ObsMultiview.Plugins.csproj b/BetterMultiview/Plugins/ObsMultiview.Plugins.csproj
index 7aed0b2..3d5ecc0 100644
--- a/BetterMultiview/Plugins/ObsMultiview.Plugins.csproj
+++ b/BetterMultiview/Plugins/ObsMultiview.Plugins.csproj
@@ -3,6 +3,8 @@
net6.0-windows
True
+ 2.0.0.0
+ 2.0.0.0
diff --git a/BetterMultiview/Plugins/PelcoD/PelcoPlugin.cs b/BetterMultiview/Plugins/PelcoD/PelcoPlugin.cs
index f79ed94..e0bc3ef 100644
--- a/BetterMultiview/Plugins/PelcoD/PelcoPlugin.cs
+++ b/BetterMultiview/Plugins/PelcoD/PelcoPlugin.cs
@@ -7,7 +7,7 @@
using Microsoft.Extensions.Logging;
namespace ObsMultiview.Plugins.PelcoD {
- public class PelcoPlugin : StatePluginBase {
+ public class PelcoPlugin : StatePluginBase {
public override string Name => "Pelco-D";
public override string Author => "Nathanael Schneider";
@@ -47,13 +47,11 @@ public class PelcoPlugin : StatePluginBase {
return new GlobalSettings(CommandFacade);
}
- public override SettingsControl GetSlotSettings(Guid slot) {
+ public override SettingsControl GetSlotSettings(Guid? slot) {
return new SlotSettings(CommandFacade, slot);
}
- public override void ActiveSlotChanged(Guid slot) {
- var settings = CommandFacade.RequestSlotSetting(slot);
-
+ protected override void ActiveSettingsChanged(PelcoSlotSettings settings) {
if (settings.Presets != null && _port.IsOpen) {
foreach (var preset in settings.Presets) {
if (preset.CameraID > 0) {
@@ -71,5 +69,9 @@ public class PelcoPlugin : StatePluginBase {
}
}
}
+
+ protected override void PrepareSettings(PelcoSlotSettings preview, PelcoSlotSettings live) {
+
+ }
}
}
\ No newline at end of file
diff --git a/BetterMultiview/Plugins/PelcoD/SlotSettings.xaml.cs b/BetterMultiview/Plugins/PelcoD/SlotSettings.xaml.cs
index 6eea4d5..6b780c4 100644
--- a/BetterMultiview/Plugins/PelcoD/SlotSettings.xaml.cs
+++ b/BetterMultiview/Plugins/PelcoD/SlotSettings.xaml.cs
@@ -32,7 +32,7 @@ public partial class SlotSettings : SlotSettingsControl {
set { SetValue(PresetsProperty, value); }
}
- public SlotSettings(CommandFacade commandFacade, Guid slotID) : base(commandFacade, slotID) {
+ public SlotSettings(CommandFacade commandFacade, Guid? slotID) : base(commandFacade, slotID) {
Presets = new List();
var settings = commandFacade.RequestSettings();
diff --git a/BetterMultiview/Plugins/PluginBase.cs b/BetterMultiview/Plugins/PluginBase.cs
index 2593b01..b578756 100644
--- a/BetterMultiview/Plugins/PluginBase.cs
+++ b/BetterMultiview/Plugins/PluginBase.cs
@@ -6,6 +6,7 @@
using System.Windows.Controls;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace ObsMultiview.Plugins {
@@ -27,10 +28,12 @@ public enum PluginTriggerType {
/// Is not triggered by slots but instead triggers a slot
///
Trigger,
+
///
/// Gets triggered when a slot is activated
///
State,
+
///
/// Gets triggered when a slot gets activated or deactivated
///
@@ -64,7 +67,7 @@ public abstract class SettingsControl : SettingsControl {
/// The global settings of this plugin
///
public T Settings {
- get { return (T) GetValue(SettingsProperty); }
+ get { return (T)GetValue(SettingsProperty); }
set { SetValue(SettingsProperty, value); }
}
@@ -97,7 +100,7 @@ public abstract class SlotSettingsControl : SettingsControl {
nameof(Settings), typeof(T), typeof(SlotSettingsControl), new PropertyMetadata(default(T)));
public T Settings {
- get { return (T) GetValue(SettingsProperty); }
+ get { return (T)GetValue(SettingsProperty); }
set { SetValue(SettingsProperty, value); }
}
@@ -112,9 +115,11 @@ public abstract class SlotSettingsControl : SettingsControl {
/// ID is not persistent through restarts
protected Guid SlotID { get; }
- protected SlotSettingsControl(CommandFacade commandFacade, Guid slotID) {
+ protected SlotSettingsControl(CommandFacade commandFacade, Guid? slotID) {
+ if(slotID == null) throw new ArgumentNullException(nameof(slotID));
+
CommandFacade = commandFacade;
- SlotID = slotID;
+ SlotID = slotID.Value;
}
///
@@ -232,7 +237,7 @@ public abstract class PluginBase : INotifyPropertyChanged {
///
/// The slot id
///
- public abstract SettingsControl GetSlotSettings(Guid slot);
+ public abstract SettingsControl GetSlotSettings(Guid? slot);
///
/// Set the command facade for this plugin. Can only be set once (during initialization)
@@ -267,158 +272,68 @@ public abstract class TriggerPluginBase : PluginBase {
public sealed override PluginTriggerType TriggerType => PluginTriggerType.Trigger;
}
- ///
- /// Plugin base for State-Type plugins
- ///
public abstract class StatePluginBase : PluginBase {
public sealed override PluginTriggerType TriggerType => PluginTriggerType.State;
- ///
- /// Gets triggered when the live-slot changes
- ///
- /// The name of the slot
- public abstract void ActiveSlotChanged(Guid slot);
- }
+ public abstract void ActiveSettingsChanged(JObject settings);
- public abstract class ChangePluginBase : PluginBase {
- public sealed override PluginTriggerType TriggerType => PluginTriggerType.Change;
-
- ///
- /// Triggered when exiting a live slot
- ///
- ///
- ///
- public abstract void OnSlotExit(Guid slot, Guid? next);
-
- ///
- /// Triggered when entering a new slot
- ///
- ///
- ///
- public abstract void OnSlotEnter(Guid slot, Guid? previous);
+ public abstract void PrepareSettings(JObject preview, JObject live);
}
///
- /// A facade to interface with the normal application
+ /// Plugin base for State-Type plugins
///
- public abstract class CommandFacade {
- ///
- /// Fired when the settings have changed
- ///
- public event Action SettingsChanged;
-
- ///
- /// Fired when a slot config has changed
- ///
- public event Action SlotConfigChanged;
-
- ///
- /// The logging instance for this plugin
- ///
- public ILogger Logger { get; protected set; }
-
- ///
- /// Request settings
- ///
- /// The settings class
- /// The subtype to fetch or null for the default settings
- ///
- public T RequestSettings(string subtype = null) {
- var json = RequestSettings(subtype);
+ public abstract class StatePluginBase : StatePluginBase where T : class {
+ public override void ActiveSettingsChanged(JObject settings) {
+ ActiveSettingsChanged(settings?.ToObject());
+ }
- if (json != null) {
- return json.ToObject();
- } else {
- return new JObject().ToObject();
- }
+ public override void PrepareSettings(JObject preview, JObject live) {
+ PrepareSettings(preview?.ToObject(), live?.ToObject());
}
///
- /// Save settings
+ /// Gets triggered when the live-slot changes
///
- /// The settings class
- /// The global settings
- /// The subtype of the settings
- public void WriteSettings(T settings, string subtype = null) {
- var json = JObject.FromObject(settings);
- WriteSettings(json, subtype);
- OnSettingsChanged(subtype);
- }
+ /// The name of the slot
+ protected abstract void ActiveSettingsChanged(T slot);
///
- /// Request settings for a slot
+ /// Gets triggered when the preview slot changes to prepare for transitions
///
- /// The settings class
- /// The slot id
- ///
- public T RequestSlotSetting(Guid? slot) {
- if (slot == null) return default(T);
+ ///
+ ///
+ protected abstract void PrepareSettings(T preview, T live);
+ }
- var json = RequestSlotSetting(slot.Value);
+ public abstract class ChangePluginBase : PluginBase {
+ public sealed override PluginTriggerType TriggerType => PluginTriggerType.Change;
- if (json != null) {
- return json.ToObject();
- } else {
- return new JObject().ToObject();
- }
- }
+ public abstract void OnSlotExit(JObject slot, JObject next);
+ public abstract void OnSlotEnter(JObject slot, JObject previous);
+ }
- ///
- /// Request all slot settings with their IDs
- ///
- /// The settings class
- ///
- public IEnumerable<(Guid id, T config)> RequestSlotSettings() {
- foreach (var item in RequestSlotSettings()) {
- if (item.Item2 != null) {
- yield return (item.Item1, item.Item2.ToObject());
- }
- }
+ public abstract class ChangePluginBase : ChangePluginBase where T : class {
+ public sealed override void OnSlotExit(JObject slot, JObject next) {
+ OnSlotExit(slot?.ToObject(), next?.ToObject());
}
- ///
- /// Save slot settings
- ///
- /// The settings class
- /// The slot ID
- /// The slot settings
- public void WriteSlotSettings(Guid id, T config) {
- WriteSlotSettings(id, JObject.FromObject(config));
- OnSlotConfigChanged(id);
+ public sealed override void OnSlotEnter(JObject slot, JObject previous) {
+ OnSlotEnter(slot?.ToObject(), previous?.ToObject());
}
///
- /// Activate a scene in preview mode
+ /// Triggered when exiting a live slot
///
- /// The scene ID
- public abstract void ActivateScene(Guid scene);
+ ///
+ ///
+ protected abstract void OnSlotExit(T slot, T next);
///
- /// Switch the currently live and preview scenes
+ /// Triggered when entering a new slot
///
- public abstract void SwitchLive();
-
- ///
- protected abstract JObject RequestSettings(string subtype = null);
-
- ///
- protected abstract void WriteSettings(JObject settings, string subtype = null);
-
- ///
- protected abstract JObject RequestSlotSetting(Guid slot);
-
- ///
- protected abstract IEnumerable<(Guid id, JObject config)> RequestSlotSettings();
-
- ///
- protected abstract void WriteSlotSettings(Guid id, JObject config);
-
- private void OnSettingsChanged(string obj) {
- SettingsChanged?.Invoke(obj);
- }
-
- private void OnSlotConfigChanged(Guid obj) {
- SlotConfigChanged?.Invoke(obj);
- }
+ ///
+ ///
+ protected abstract void OnSlotEnter(T slot, T previous);
}
}
\ No newline at end of file
diff --git a/BetterMultiview/Plugins/qlc/QlcPlugin.cs b/BetterMultiview/Plugins/qlc/QlcPlugin.cs
index c9fb5f0..8adec9f 100644
--- a/BetterMultiview/Plugins/qlc/QlcPlugin.cs
+++ b/BetterMultiview/Plugins/qlc/QlcPlugin.cs
@@ -20,22 +20,17 @@ namespace ObsMultiview.Plugins.qlc {
/// - Can't easily fetch button state, because it's a) async and b) the response has no identifier as to which control it belongs to
/// - Can't easily fetch widget type, because a) there is no identifier for the control that is associated with the response and b) they are localized (wtf?)
///
- public class QlcPlugin : ChangePluginBase {
- public override void OnSlotExit(Guid slot, Guid? next) {
- var settings = CommandFacade.RequestSlotSetting(slot);
- var nextSettings = CommandFacade.RequestSlotSetting(next);
-
+ public class QlcPlugin : ChangePluginBase {
+ protected override void OnSlotExit(QlcSlotSettings settings, QlcSlotSettings? next) {
foreach (var fkt in settings.ExitFunctions) {
// Only turn off functions that don't get triggered in the next scene
- if (!(nextSettings?.EntryFunctions.Any(x =>
+ if (!(next?.EntryFunctions.Any(x =>
x.Function.Type == fkt.Function.Type && x.Function.ID == fkt.Function.ID) ?? false))
SetFkt(fkt.Function, fkt.Value);
}
}
- public override void OnSlotEnter(Guid slot, Guid? previous) {
- var settings = CommandFacade.RequestSlotSetting(slot);
-
+ protected override void OnSlotEnter(QlcSlotSettings settings, QlcSlotSettings? previous) {
foreach (var fkt in settings.EntryFunctions) {
SetFkt(fkt.Function, fkt.Value);
}
@@ -168,7 +163,7 @@ public class QlcPlugin : ChangePluginBase {
return new GlobalSettings(CommandFacade);
}
- public override SettingsControl GetSlotSettings(Guid slot) {
+ public override SettingsControl GetSlotSettings(Guid? slot) {
return new SlotSettings(this, CommandFacade, slot);
}
diff --git a/BetterMultiview/Plugins/qlc/SlotSettings.xaml.cs b/BetterMultiview/Plugins/qlc/SlotSettings.xaml.cs
index 4ad4ef2..848366a 100644
--- a/BetterMultiview/Plugins/qlc/SlotSettings.xaml.cs
+++ b/BetterMultiview/Plugins/qlc/SlotSettings.xaml.cs
@@ -16,7 +16,7 @@ public partial class SlotSettings : SlotSettingsControl {
set { SetValue(PluginProperty, value); }
}
- public SlotSettings(QlcPlugin plugin, CommandFacade commandFacade, Guid slotID) : base(commandFacade, slotID) {
+ public SlotSettings(QlcPlugin plugin, CommandFacade commandFacade, Guid? slotID) : base(commandFacade, slotID) {
Plugin = plugin;
plugin.FetchInfo();
InitializeComponent();