├── .gitignore ├── LICENSE ├── README.md └── RGBSync+ ├── App.config ├── App.xaml ├── App.xaml.cs ├── ApplicationManager.cs ├── Attached ├── SliderValue.cs └── SliderValueAdorner.cs ├── Brushes └── SyncBrush.cs ├── Configuration ├── AbstractConfiguration.cs ├── ColorSerializer.cs ├── IConfiguration.cs ├── Legacy │ └── ConfigurationUpdates.cs └── Settings.cs ├── Controls ├── BlurredDecorationWindow.cs ├── ColorSelector.cs ├── Form.cs ├── GradientEditor.cs └── ImageButton.cs ├── Converter ├── BoolToVisibilityConverter.cs ├── EqualsToBoolConverter.cs ├── NullToVisibilityConverter.cs ├── ScrollOffsetToOpacityMaskConverter.cs └── ScrollOffsetToVisibilityConverter.cs ├── Helper ├── ActionCommand.cs ├── ExceptionExtension.cs ├── MathHelper.cs └── RGBNetExtension.cs ├── Model ├── SyncGroup.cs └── SyncLed.cs ├── NuGet.Config ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings ├── RGBSync+.csproj ├── RGBSync+.sln ├── Resources ├── RGBSync+.xaml ├── argebee.ico ├── arrow_down.png ├── arrow_up.png ├── background.png ├── close.png ├── font.ttf ├── minimize.png └── navigation.ttf ├── Styles ├── BlurredDecorationWindow.xaml ├── Button.xaml ├── CachedResourceDictionary.cs ├── ColorSelector.xaml ├── ComboBox.xaml ├── Form.xaml ├── FrameworkElement.xaml ├── GradientEditor.xaml ├── GroupBox.xaml ├── ImageButton.xaml ├── ListBox.xaml ├── Navigation.xaml ├── Slider.xaml ├── TextBox.xaml ├── Theme.xaml └── ToolTip.xaml └── UI ├── ConfigurationViewModel.cs ├── ConfigurationWindow.xaml └── ConfigurationWindow.xaml.cs /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Since [RGB.NET](https://github.com/DarthAffe/RGB.NET) no longer supports reading colors from devices, syncing is no longer possible and this example no longer needed. 2 | 3 | If you're looking for a software to control RGB-devices of different brands together consider checking out [Artemis](https://github.com/Artemis-RGB/Artemis). 4 | 5 | # RGBSyncPlus 6 | Tool to syncronize RGB devices 7 | 8 | If you've issues with the configuration window not opening when clicking the Tray-Icon you can disable Tray-Minimization by changing ```"MinimizeToTray":true``` to ```"MinimizeToTray":false``` in _Settings.json_ in the installation directory. (If this entry isn't already included delete the file and start/close the program once.) 9 | -------------------------------------------------------------------------------- /RGBSync+/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /RGBSync+/App.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /RGBSync+/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Windows; 4 | using System.Windows.Controls; 5 | using Hardcodet.Wpf.TaskbarNotification; 6 | using Newtonsoft.Json; 7 | using RGBSyncPlus.Configuration; 8 | using RGBSyncPlus.Configuration.Legacy; 9 | using RGBSyncPlus.Helper; 10 | 11 | namespace RGBSyncPlus 12 | { 13 | public partial class App : Application 14 | { 15 | #region Constants 16 | 17 | private const string PATH_SETTINGS = "Settings.json"; 18 | 19 | #endregion 20 | 21 | #region Properties & Fields 22 | 23 | private TaskbarIcon _taskbarIcon; 24 | 25 | #endregion 26 | 27 | #region Methods 28 | 29 | protected override void OnStartup(StartupEventArgs e) 30 | { 31 | base.OnStartup(e); 32 | 33 | try 34 | { 35 | ToolTipService.ShowDurationProperty.OverrideMetadata(typeof(DependencyObject), new FrameworkPropertyMetadata(int.MaxValue)); 36 | 37 | _taskbarIcon = (TaskbarIcon)FindResource("TaskbarIcon"); 38 | _taskbarIcon.DoubleClickCommand = ApplicationManager.Instance.OpenConfigurationCommand; 39 | 40 | Settings settings = null; 41 | try { settings = JsonConvert.DeserializeObject(File.ReadAllText(PATH_SETTINGS), new ColorSerializer()); } 42 | catch (Exception ex) 43 | { 44 | Console.WriteLine(ex.Message); 45 | /* File doesn't exist or is corrupt - just create a new one. */ 46 | } 47 | 48 | if (settings == null) 49 | { 50 | settings = new Settings { Version = Settings.CURRENT_VERSION }; 51 | _taskbarIcon.ShowBalloonTip("RGBSync+ is starting in the tray!", "Click on the icon to open the configuration.", BalloonIcon.Info); 52 | } 53 | else if (settings.Version != Settings.CURRENT_VERSION) 54 | ConfigurationUpdates.PerformOn(settings); 55 | 56 | ApplicationManager.Instance.Settings = settings; 57 | ApplicationManager.Instance.Initialize(); 58 | 59 | if (!settings.MinimizeToTray) //HACK DarthAffe 02.12.2018: Workaround to create the window 60 | { 61 | ApplicationManager.Instance.OpenConfigurationCommand.Execute(null); 62 | ApplicationManager.Instance.HideConfigurationCommand.Execute(null); 63 | } 64 | } 65 | catch (Exception ex) 66 | { 67 | File.WriteAllText("error.log", $"[{DateTime.Now:G}] Exception!\r\n\r\nMessage:\r\n{ex.GetFullMessage()}\r\n\r\nStackTrace:\r\n{ex.StackTrace}\r\n\r\n"); 68 | MessageBox.Show("An error occured while starting RGBSync+.\r\nMore information can be found in the error.log file in the application directory.", "Can't start RGBSync+."); 69 | 70 | try { ApplicationManager.Instance.ExitCommand.Execute(null); } 71 | catch { Environment.Exit(0); } 72 | } 73 | } 74 | 75 | protected override void OnExit(ExitEventArgs e) 76 | { 77 | base.OnExit(e); 78 | 79 | File.WriteAllText(PATH_SETTINGS, JsonConvert.SerializeObject(ApplicationManager.Instance.Settings, new ColorSerializer())); 80 | } 81 | 82 | #endregion 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /RGBSync+/ApplicationManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Specialized; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Windows; 8 | using RGB.NET.Core; 9 | using RGB.NET.Groups; 10 | using RGBSyncPlus.Brushes; 11 | using RGBSyncPlus.Configuration; 12 | using RGBSyncPlus.Helper; 13 | using RGBSyncPlus.Model; 14 | using RGBSyncPlus.UI; 15 | 16 | namespace RGBSyncPlus 17 | { 18 | public class ApplicationManager 19 | { 20 | #region Constants 21 | 22 | private const string DEVICEPROVIDER_DIRECTORY = "DeviceProvider"; 23 | 24 | #endregion 25 | 26 | #region Properties & Fields 27 | 28 | public static ApplicationManager Instance { get; } = new ApplicationManager(); 29 | 30 | private ConfigurationWindow _configurationWindow; 31 | 32 | public Settings Settings { get; set; } 33 | public TimerUpdateTrigger UpdateTrigger { get; private set; } 34 | 35 | #endregion 36 | 37 | #region Commands 38 | 39 | private ActionCommand _openConfiguration; 40 | public ActionCommand OpenConfigurationCommand => _openConfiguration ?? (_openConfiguration = new ActionCommand(OpenConfiguration)); 41 | 42 | private ActionCommand _hideConfiguration; 43 | public ActionCommand HideConfigurationCommand => _hideConfiguration ?? (_hideConfiguration = new ActionCommand(HideConfiguration)); 44 | 45 | private ActionCommand _exitCommand; 46 | public ActionCommand ExitCommand => _exitCommand ?? (_exitCommand = new ActionCommand(Exit)); 47 | 48 | #endregion 49 | 50 | #region Constructors 51 | 52 | private ApplicationManager() { } 53 | 54 | #endregion 55 | 56 | #region Methods 57 | 58 | public void Initialize() 59 | { 60 | RGBSurface surface = RGBSurface.Instance; 61 | LoadDeviceProviders(); 62 | surface.AlignDevices(); 63 | 64 | foreach (IRGBDevice device in surface.Devices) 65 | device.UpdateMode = DeviceUpdateMode.Sync | DeviceUpdateMode.SyncBack; 66 | 67 | UpdateTrigger = new TimerUpdateTrigger { UpdateFrequency = 1.0 / MathHelper.Clamp(Settings.UpdateRate, 1, 100) }; 68 | surface.RegisterUpdateTrigger(UpdateTrigger); 69 | UpdateTrigger.Start(); 70 | 71 | foreach (SyncGroup syncGroup in Settings.SyncGroups) 72 | RegisterSyncGroup(syncGroup); 73 | } 74 | 75 | private void LoadDeviceProviders() 76 | { 77 | string deviceProvierDir = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) ?? string.Empty, DEVICEPROVIDER_DIRECTORY); 78 | if (!Directory.Exists(deviceProvierDir)) return; 79 | 80 | foreach (string file in Directory.GetFiles(deviceProvierDir, "*.dll")) 81 | { 82 | try 83 | { 84 | Assembly assembly = Assembly.LoadFrom(file); 85 | foreach (Type loaderType in assembly.GetTypes().Where(t => !t.IsAbstract && !t.IsInterface && t.IsClass 86 | && typeof(IRGBDeviceProviderLoader).IsAssignableFrom(t))) 87 | { 88 | if (Activator.CreateInstance(loaderType) is IRGBDeviceProviderLoader deviceProviderLoader) 89 | { 90 | //TODO DarthAffe 03.06.2018: Support Initialization 91 | if (deviceProviderLoader.RequiresInitialization) continue; 92 | 93 | RGBSurface.Instance.LoadDevices(deviceProviderLoader); 94 | } 95 | } 96 | } 97 | catch { /* #sadprogrammer */ } 98 | } 99 | } 100 | 101 | public void AddSyncGroup(SyncGroup syncGroup) 102 | { 103 | Settings.SyncGroups.Add(syncGroup); 104 | RegisterSyncGroup(syncGroup); 105 | } 106 | 107 | private void RegisterSyncGroup(SyncGroup syncGroup) 108 | { 109 | syncGroup.LedGroup = new ListLedGroup(syncGroup.Leds.GetLeds()) { Brush = new SyncBrush(syncGroup) }; 110 | syncGroup.LedsChangedEventHandler = (sender, args) => UpdateLedGroup(syncGroup.LedGroup, args); 111 | syncGroup.Leds.CollectionChanged += syncGroup.LedsChangedEventHandler; 112 | } 113 | 114 | public void RemoveSyncGroup(SyncGroup syncGroup) 115 | { 116 | Settings.SyncGroups.Remove(syncGroup); 117 | syncGroup.Leds.CollectionChanged -= syncGroup.LedsChangedEventHandler; 118 | syncGroup.LedGroup.Detach(); 119 | syncGroup.LedGroup = null; 120 | } 121 | 122 | private void UpdateLedGroup(ListLedGroup group, NotifyCollectionChangedEventArgs args) 123 | { 124 | if (args.Action == NotifyCollectionChangedAction.Reset) 125 | { 126 | List leds = group.GetLeds().ToList(); 127 | group.RemoveLeds(leds); 128 | } 129 | else 130 | { 131 | if (args.NewItems != null) 132 | group.AddLeds(args.NewItems.Cast().GetLeds()); 133 | 134 | if (args.OldItems != null) 135 | group.RemoveLeds(args.OldItems.Cast().GetLeds()); 136 | } 137 | } 138 | 139 | private void HideConfiguration() 140 | { 141 | if (Settings.MinimizeToTray) 142 | { 143 | if (_configurationWindow.IsVisible) 144 | _configurationWindow.Hide(); 145 | } 146 | else 147 | _configurationWindow.WindowState = WindowState.Minimized; 148 | } 149 | 150 | private void OpenConfiguration() 151 | { 152 | if (_configurationWindow == null) _configurationWindow = new ConfigurationWindow(); 153 | 154 | if (!_configurationWindow.IsVisible) 155 | _configurationWindow.Show(); 156 | 157 | if (_configurationWindow.WindowState == WindowState.Minimized) 158 | _configurationWindow.WindowState = WindowState.Normal; 159 | } 160 | 161 | private void Exit() 162 | { 163 | try { RGBSurface.Instance?.Dispose(); } catch { /* well, we're shuting down anyway ... */ } 164 | Application.Current.Shutdown(); 165 | } 166 | 167 | #endregion 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /RGBSync+/Attached/SliderValue.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using System.Windows.Documents; 5 | using System.Windows.Input; 6 | using System.Windows.Media; 7 | 8 | namespace RGBSyncPlus.Attached 9 | { 10 | public static class SliderValue 11 | { 12 | #region Properties & Fields 13 | // ReSharper disable InconsistentNaming 14 | 15 | public static readonly DependencyProperty UnitProperty = DependencyProperty.RegisterAttached( 16 | "Unit", typeof(string), typeof(SliderValue), new PropertyMetadata(default(string))); 17 | 18 | public static void SetUnit(DependencyObject element, string value) => element.SetValue(UnitProperty, value); 19 | public static string GetUnit(DependencyObject element) => (string)element.GetValue(UnitProperty); 20 | 21 | public static readonly DependencyProperty IsShownProperty = DependencyProperty.RegisterAttached( 22 | "IsShown", typeof(bool), typeof(SliderValue), new PropertyMetadata(default(bool), IsShownChanged)); 23 | 24 | public static void SetIsShown(DependencyObject element, bool value) => element.SetValue(IsShownProperty, value); 25 | public static bool GetIsShown(DependencyObject element) => (bool)element.GetValue(IsShownProperty); 26 | 27 | public static readonly DependencyProperty BorderBrushProperty = DependencyProperty.RegisterAttached( 28 | "BorderBrush", typeof(Brush), typeof(SliderValue), new PropertyMetadata(default(Brush))); 29 | 30 | public static void SetBorderBrush(DependencyObject element, Brush value) => element.SetValue(BorderBrushProperty, value); 31 | public static Brush GetBorderBrush(DependencyObject element) => (Brush)element.GetValue(BorderBrushProperty); 32 | 33 | public static readonly DependencyProperty BackgroundProperty = DependencyProperty.RegisterAttached( 34 | "Background", typeof(Brush), typeof(SliderValue), new PropertyMetadata(default(Brush))); 35 | 36 | public static void SetBackground(DependencyObject element, Brush value) => element.SetValue(BackgroundProperty, value); 37 | public static Brush GetBackground(DependencyObject element) => (Brush)element.GetValue(BackgroundProperty); 38 | 39 | public static readonly DependencyProperty ForegroundProperty = DependencyProperty.RegisterAttached( 40 | "Foreground", typeof(Brush), typeof(SliderValue), new PropertyMetadata(default(Brush))); 41 | 42 | public static void SetForeground(DependencyObject element, Brush value) => element.SetValue(ForegroundProperty, value); 43 | public static Brush GetForeground(DependencyObject element) => (Brush)element.GetValue(ForegroundProperty); 44 | 45 | public static readonly DependencyProperty FontProperty = DependencyProperty.RegisterAttached( 46 | "Font", typeof(FontFamily), typeof(SliderValue), new PropertyMetadata(default(FontFamily))); 47 | 48 | public static void SetFont(DependencyObject element, FontFamily value) => element.SetValue(FontProperty, value); 49 | public static FontFamily GetFont(DependencyObject element) => (FontFamily)element.GetValue(FontProperty); 50 | 51 | public static readonly DependencyProperty FontSizeProperty = DependencyProperty.RegisterAttached( 52 | "FontSize", typeof(double), typeof(SliderValue), new PropertyMetadata(default(double))); 53 | 54 | public static void SetFontSize(DependencyObject element, double value) => element.SetValue(FontSizeProperty, value); 55 | public static double GetFontSize(DependencyObject element) => (double)element.GetValue(FontSizeProperty); 56 | 57 | // ReSharper enable InconsistentNaming 58 | #endregion 59 | 60 | #region Methods 61 | 62 | private static void IsShownChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) 63 | { 64 | if (!(dependencyObject is Slider slider)) return; 65 | 66 | if (dependencyPropertyChangedEventArgs.NewValue as bool? == true) 67 | { 68 | slider.MouseEnter += SliderOnMouseEnter; 69 | slider.MouseLeave += SliderOnMouseLeave; 70 | } 71 | else 72 | { 73 | slider.MouseEnter -= SliderOnMouseEnter; 74 | slider.MouseLeave -= SliderOnMouseLeave; 75 | RemoveAdorner(slider); 76 | } 77 | } 78 | 79 | private static void SliderOnMouseEnter(object sender, MouseEventArgs mouseEventArgs) 80 | { 81 | if (!(sender is Slider slider)) return; 82 | AdornerLayer.GetAdornerLayer(slider)?.Add(new SliderValueAdorner(slider, GetUnit(slider)) 83 | { 84 | BorderBrush = GetBorderBrush(slider), 85 | Background = GetBackground(slider), 86 | Foreground = GetForeground(slider), 87 | Font = GetFont(slider), 88 | FontSize = GetFontSize(slider) 89 | }); 90 | } 91 | 92 | private static void SliderOnMouseLeave(object sender, MouseEventArgs mouseEventArgs) 93 | { 94 | if (!(sender is Slider slider)) return; 95 | RemoveAdorner(slider); 96 | } 97 | 98 | private static void RemoveAdorner(Slider slider) 99 | { 100 | AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(slider); 101 | Adorner adorner = adornerLayer?.GetAdorners(slider)?.FirstOrDefault(x => x is SliderValueAdorner); 102 | if (adorner != null) 103 | { 104 | adornerLayer.Remove(adorner); 105 | (adorner as SliderValueAdorner)?.Cleanup(); 106 | } 107 | } 108 | 109 | #endregion 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /RGBSync+/Attached/SliderValueAdorner.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using System.Windows.Controls.Primitives; 5 | using System.Windows.Media; 6 | using Point = System.Windows.Point; 7 | 8 | namespace RGBSyncPlus.Attached 9 | { 10 | public class SliderValueAdorner : System.Windows.Documents.Adorner 11 | { 12 | #region Properties & Fields 13 | 14 | private readonly string _unit; 15 | private readonly Slider _slider; 16 | private readonly Thumb _thumb; 17 | private readonly RepeatButton _decreaseRepeatButton; 18 | 19 | public Brush BorderBrush { get; set; } = System.Windows.Media.Brushes.Black; 20 | public Brush Background { get; set; } = System.Windows.Media.Brushes.Black; 21 | public Brush Foreground { get; set; } = System.Windows.Media.Brushes.White; 22 | public FontFamily Font { get; set; } = new FontFamily("Verdana"); 23 | public double FontSize { get; set; } = 14; 24 | 25 | #endregion 26 | 27 | #region Constructors 28 | 29 | public SliderValueAdorner(UIElement adornedElement, string unit) 30 | : base(adornedElement) 31 | { 32 | this._unit = unit; 33 | 34 | _slider = (Slider)adornedElement; 35 | Track track = (Track)_slider.Template.FindName("PART_Track", _slider); 36 | 37 | _thumb = track.Thumb; 38 | _decreaseRepeatButton = track.DecreaseRepeatButton; 39 | _decreaseRepeatButton.SizeChanged += OnButtonSizeChanged; 40 | } 41 | 42 | #endregion 43 | 44 | #region Methods 45 | 46 | public void Cleanup() 47 | { 48 | _decreaseRepeatButton.SizeChanged -= OnButtonSizeChanged; 49 | } 50 | 51 | private void OnButtonSizeChanged(object sender, SizeChangedEventArgs sizeChangedEventArgs) => InvalidateVisual(); 52 | 53 | protected override void OnRender(DrawingContext drawingContext) 54 | { 55 | double offset = _decreaseRepeatButton.ActualWidth + (_thumb.ActualWidth / 2.0); 56 | 57 | FormattedText text = new FormattedText(GetText(), CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface(Font, FontStyles.Normal, FontWeights.Normal, FontStretches.Normal), FontSize, Foreground); 58 | Geometry border = CreateBorder(offset, text.Width, text.Height); 59 | 60 | drawingContext.DrawGeometry(Background, new Pen(BorderBrush, 1), border); 61 | drawingContext.DrawText(text, new Point(offset - (text.Width / 2.0), -26)); 62 | } 63 | 64 | private string GetText() 65 | { 66 | string valueText = _slider.Value.ToString(); 67 | if (!string.IsNullOrWhiteSpace(_unit)) 68 | valueText += " " + _unit; 69 | 70 | return valueText; 71 | } 72 | 73 | private Geometry CreateBorder(double offset, double width, double height) 74 | { 75 | double halfWidth = width / 2.0; 76 | 77 | PathGeometry borderGeometry = new PathGeometry(); 78 | PathFigure border = new PathFigure 79 | { 80 | StartPoint = new Point(offset, 0), 81 | IsClosed = true, 82 | IsFilled = true 83 | }; 84 | 85 | border.Segments.Add(new LineSegment(new Point(offset + 4, -6), true)); 86 | border.Segments.Add(new LineSegment(new Point(offset + 4 + halfWidth, -6), true)); 87 | border.Segments.Add(new LineSegment(new Point(offset + 4 + halfWidth, -10 - height), true)); 88 | border.Segments.Add(new LineSegment(new Point(offset - 4 - halfWidth, -10 - height), true)); 89 | border.Segments.Add(new LineSegment(new Point(offset - 4 - halfWidth, -6), true)); 90 | border.Segments.Add(new LineSegment(new Point(offset - 4, -6), true)); 91 | 92 | borderGeometry.Figures.Add(border); 93 | 94 | return borderGeometry; 95 | } 96 | 97 | #endregion 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /RGBSync+/Brushes/SyncBrush.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using RGB.NET.Core; 3 | using RGBSyncPlus.Helper; 4 | using RGBSyncPlus.Model; 5 | 6 | namespace RGBSyncPlus.Brushes 7 | { 8 | public class SyncBrush : AbstractBrush 9 | { 10 | #region Properties & Fields 11 | 12 | private readonly SyncGroup _syncGroup; 13 | 14 | private Led _syncLed; 15 | 16 | #endregion 17 | 18 | #region Constructors 19 | 20 | public SyncBrush(SyncGroup syncGroup) 21 | { 22 | this._syncGroup = syncGroup; 23 | 24 | syncGroup.PropertyChanged += SyncGroupOnPropertyChanged; 25 | _syncLed = syncGroup.SyncLed?.GetLed(); 26 | } 27 | 28 | #endregion 29 | 30 | #region Methods 31 | 32 | private void SyncGroupOnPropertyChanged(object sender, PropertyChangedEventArgs e) 33 | { 34 | if (e.PropertyName == nameof(SyncGroup.SyncLed)) 35 | _syncLed = _syncGroup.SyncLed?.GetLed(); 36 | } 37 | 38 | protected override Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget) 39 | { 40 | if(_syncLed == null) 41 | _syncLed = _syncGroup.SyncLed?.GetLed(); 42 | 43 | if (renderTarget.Led == _syncLed) 44 | return Color.Transparent; 45 | 46 | return _syncLed?.Color ?? Color.Transparent; 47 | } 48 | 49 | #endregion 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /RGBSync+/Configuration/AbstractConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Runtime.CompilerServices; 4 | using RGB.NET.Core; 5 | 6 | namespace RGBSyncPlus.Configuration 7 | { 8 | public class AbstractConfiguration : AbstractBindable, IConfiguration, INotifyPropertyChanged 9 | { 10 | #region Methods 11 | 12 | protected override bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null) 13 | { 14 | if ((typeof(T) == typeof(double)) || (typeof(T) == typeof(float))) 15 | { 16 | if (Math.Abs((double)(object)storage - (double)(object)value) < 0.000001) return false; 17 | } 18 | else 19 | { 20 | if (Equals(storage, value)) return false; 21 | } 22 | 23 | storage = value; 24 | // ReSharper disable once ExplicitCallerInfoArgument 25 | OnPropertyChanged(propertyName); 26 | return true; 27 | } 28 | 29 | #endregion 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /RGBSync+/Configuration/ColorSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | using RGB.NET.Core; 5 | 6 | namespace RGBSyncPlus.Configuration 7 | { 8 | public class ColorSerializer : JsonConverter 9 | { 10 | #region Methods 11 | 12 | public override bool CanConvert(Type objectType) => objectType == typeof(Color); 13 | 14 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 15 | { 16 | if (!(value is Color color)) return; 17 | 18 | writer.WriteStartObject(); 19 | writer.WritePropertyName("A"); 20 | writer.WriteValue(color.A); 21 | writer.WritePropertyName("R"); 22 | writer.WriteValue(color.R); 23 | writer.WritePropertyName("G"); 24 | writer.WriteValue(color.G); 25 | writer.WritePropertyName("B"); 26 | writer.WriteValue(color.B); 27 | writer.WriteEndObject(); 28 | } 29 | 30 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 31 | { 32 | JObject jsonObject = JObject.Load(reader); 33 | return new Color(jsonObject.Property("A").Value.ToObject(), 34 | jsonObject.Property("R").Value.ToObject(), 35 | jsonObject.Property("G").Value.ToObject(), 36 | jsonObject.Property("B").Value.ToObject()); 37 | } 38 | 39 | #endregion 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /RGBSync+/Configuration/IConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace RGBSyncPlus.Configuration 4 | { 5 | public interface IConfiguration : INotifyPropertyChanged 6 | { } 7 | } 8 | -------------------------------------------------------------------------------- /RGBSync+/Configuration/Legacy/ConfigurationUpdates.cs: -------------------------------------------------------------------------------- 1 | namespace RGBSyncPlus.Configuration.Legacy 2 | { 3 | public static class ConfigurationUpdates 4 | { 5 | #region Methods 6 | 7 | public static void PerformOn(Settings settings) 8 | { } 9 | 10 | #endregion 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /RGBSync+/Configuration/Settings.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using RGBSyncPlus.Model; 3 | 4 | namespace RGBSyncPlus.Configuration 5 | { 6 | public class Settings 7 | { 8 | #region Constants 9 | 10 | public const int CURRENT_VERSION = 1; 11 | 12 | #endregion 13 | 14 | #region Properties & Fields 15 | 16 | public int Version { get; set; } = 0; 17 | 18 | public double UpdateRate { get; set; } = 30.0; 19 | 20 | public bool MinimizeToTray { get; set; } = true; 21 | 22 | public List SyncGroups { get; set; } = new List(); 23 | 24 | #endregion 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /RGBSync+/Controls/BlurredDecorationWindow.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | using System.Windows.Input; 4 | using System.Windows.Media; 5 | 6 | namespace RGBSyncPlus.Controls 7 | { 8 | [TemplatePart(Name = "PART_Decoration", Type = typeof(FrameworkElement))] 9 | [TemplatePart(Name = "PART_Content", Type = typeof(FrameworkElement))] 10 | [TemplatePart(Name = "PART_CloseButton", Type = typeof(Button))] 11 | [TemplatePart(Name = "PART_MinimizeButton", Type = typeof(Button))] 12 | [TemplatePart(Name = "PART_IconButton", Type = typeof(Button))] 13 | public class BlurredDecorationWindow : Window 14 | { 15 | #region DependencyProperties 16 | // ReSharper disable InconsistentNaming 17 | 18 | public static readonly DependencyProperty BackgroundImageProperty = DependencyProperty.Register( 19 | "BackgroundImage", typeof(ImageSource), typeof(BlurredDecorationWindow), new PropertyMetadata(default(ImageSource))); 20 | 21 | public ImageSource BackgroundImage 22 | { 23 | get => (ImageSource)GetValue(BackgroundImageProperty); 24 | set => SetValue(BackgroundImageProperty, value); 25 | } 26 | 27 | public static readonly DependencyProperty DecorationHeightProperty = DependencyProperty.Register( 28 | "DecorationHeight", typeof(double), typeof(BlurredDecorationWindow), new PropertyMetadata(20.0)); 29 | 30 | public double DecorationHeight 31 | { 32 | get => (double)GetValue(DecorationHeightProperty); 33 | set => SetValue(DecorationHeightProperty, value); 34 | } 35 | 36 | public static readonly DependencyProperty IconToolTipProperty = DependencyProperty.Register( 37 | "IconToolTip", typeof(string), typeof(BlurredDecorationWindow), new PropertyMetadata(default(string))); 38 | 39 | public string IconToolTip 40 | { 41 | get => (string)GetValue(IconToolTipProperty); 42 | set => SetValue(IconToolTipProperty, value); 43 | } 44 | 45 | public static readonly DependencyProperty IconCommandProperty = DependencyProperty.Register( 46 | "IconCommand", typeof(ICommand), typeof(BlurredDecorationWindow), new PropertyMetadata(default(ICommand))); 47 | 48 | public ICommand IconCommand 49 | { 50 | get => (ICommand)GetValue(IconCommandProperty); 51 | set => SetValue(IconCommandProperty, value); 52 | } 53 | 54 | // ReSharper restore InconsistentNaming 55 | #endregion 56 | 57 | #region Constructors 58 | 59 | static BlurredDecorationWindow() 60 | { 61 | DefaultStyleKeyProperty.OverrideMetadata(typeof(BlurredDecorationWindow), new FrameworkPropertyMetadata(typeof(BlurredDecorationWindow))); 62 | } 63 | 64 | #endregion 65 | 66 | #region Methods 67 | 68 | public override void OnApplyTemplate() 69 | { 70 | base.OnApplyTemplate(); 71 | 72 | if (GetTemplateChild("PART_Decoration") is FrameworkElement decoration) 73 | decoration.MouseLeftButtonDown += (sender, args) => DragMove(); 74 | 75 | if (GetTemplateChild("PART_CloseButton") is Button closeButton) 76 | closeButton.Click += (sender, args) => ApplicationManager.Instance.ExitCommand.Execute(null); 77 | 78 | if (GetTemplateChild("PART_MinimizeButton") is Button minimizeButton) 79 | minimizeButton.Click += (sender, args) => ApplicationManager.Instance.HideConfigurationCommand.Execute(null); //HACK DarthAffe 02.12.2018: This is a really dirty hack - hopefully this will never get more than one window ... 80 | 81 | if (GetTemplateChild("PART_IconButton") is Button iconButton) 82 | iconButton.Click += (sender, args) => IconCommand?.Execute(null); 83 | } 84 | 85 | #endregion 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /RGBSync+/Controls/Form.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | 5 | namespace RGBSyncPlus.Controls 6 | { 7 | public class Form : Panel 8 | { 9 | #region DependencyProperties 10 | // ReSharper disable InconsistentNaming 11 | 12 | public static readonly DependencyProperty RowHeightProperty = DependencyProperty.Register("RowHeight", typeof(double), typeof(Form), 13 | new FrameworkPropertyMetadata(24.0, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure)); 14 | 15 | public double RowHeight 16 | { 17 | get => (double)GetValue(RowHeightProperty); 18 | set 19 | { 20 | if (value < 0) throw new ArgumentOutOfRangeException(nameof(RowHeight), "Row height can't be negative"); 21 | SetValue(RowHeightProperty, value); 22 | } 23 | } 24 | 25 | public static readonly DependencyProperty LabelWidthProperty = DependencyProperty.Register("LabelWidth", typeof(double), typeof(Form), 26 | new FrameworkPropertyMetadata(100.0, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure)); 27 | 28 | public double LabelWidth 29 | { 30 | get => (double)GetValue(LabelWidthProperty); 31 | set 32 | { 33 | if (value < 0) throw new ArgumentOutOfRangeException(nameof(RowHeight), "Label width can't be negative"); 34 | SetValue(LabelWidthProperty, value); 35 | } 36 | } 37 | 38 | public static readonly DependencyProperty ElementSpacingProperty = DependencyProperty.Register("ElementSpacing", typeof(double), typeof(Form), 39 | new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure)); 40 | 41 | public double ElementSpacing 42 | { 43 | get => (double)GetValue(ElementSpacingProperty); 44 | set => SetValue(ElementSpacingProperty, value); 45 | } 46 | 47 | public static readonly DependencyProperty RowSpacingProperty = DependencyProperty.Register("RowSpacing", typeof(double), typeof(Form), 48 | new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure)); 49 | 50 | public double RowSpacing 51 | { 52 | get => (double)GetValue(RowSpacingProperty); 53 | set => SetValue(RowSpacingProperty, value); 54 | } 55 | 56 | // ReSharper restore InconsistentNaming 57 | #endregion 58 | 59 | #region AttachedProperties 60 | // ReSharper disable InconsistentNaming 61 | 62 | public static readonly DependencyProperty IsLabelProperty = DependencyProperty.RegisterAttached("IsLabel", typeof(bool), typeof(Form), 63 | new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure)); 64 | 65 | public static void SetIsLabel(UIElement element, bool value) => element.SetValue(IsLabelProperty, value); 66 | public static bool GetIsLabel(UIElement element) => (bool)element.GetValue(IsLabelProperty); 67 | 68 | public static readonly DependencyProperty LineBreaksProperty = DependencyProperty.RegisterAttached("LineBreaks", typeof(int), typeof(Form), 69 | new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure)); 70 | 71 | public static void SetLineBreaks(UIElement element, int value) => element.SetValue(LineBreaksProperty, value); 72 | public static int GetLineBreaks(UIElement element) => (int)element.GetValue(LineBreaksProperty); 73 | 74 | public static readonly DependencyProperty RowSpanProperty = DependencyProperty.RegisterAttached("RowSpan", typeof(int), typeof(Form), 75 | new FrameworkPropertyMetadata(1, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure)); 76 | 77 | public static void SetRowSpan(DependencyObject element, int value) => element.SetValue(RowSpanProperty, value); 78 | public static int GetRowSpan(DependencyObject element) => (int)element.GetValue(RowSpanProperty); 79 | 80 | public static readonly DependencyProperty FillProperty = DependencyProperty.RegisterAttached("Fill", typeof(bool), typeof(Form), 81 | new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure)); 82 | 83 | public static void SetFill(DependencyObject element, bool value) => element.SetValue(FillProperty, value); 84 | public static bool GetFill(DependencyObject element) => (bool)element.GetValue(FillProperty); 85 | 86 | // ReSharper restore InconsistentNaming 87 | #endregion 88 | 89 | #region Methods 90 | 91 | protected override Size MeasureOverride(Size availableSize) 92 | { 93 | if (InternalChildren.Count == 0) return new Size(0, 0); 94 | 95 | FormLayout layout = new FormLayout(RowHeight, LabelWidth, ElementSpacing, RowSpacing); 96 | 97 | foreach (UIElement child in InternalChildren) 98 | { 99 | child.Measure(availableSize); 100 | layout.AddElement(child, 0); 101 | } 102 | 103 | return new Size(layout.Width, layout.Height); 104 | } 105 | 106 | protected override Size ArrangeOverride(Size finalSize) 107 | { 108 | if (InternalChildren.Count == 0) return new Size(0, 0); 109 | 110 | FormLayout layout = new FormLayout(RowHeight, LabelWidth, ElementSpacing, RowSpacing); 111 | 112 | foreach (UIElement child in InternalChildren) 113 | child.Arrange(layout.AddElement(child, finalSize.Width)); 114 | 115 | return new Size(finalSize.Width, layout.Height); 116 | } 117 | 118 | #endregion 119 | 120 | #region Data 121 | 122 | private class FormLayout 123 | { 124 | #region Properties & Fields 125 | 126 | private readonly double _rowHeight; 127 | private readonly double _labelWidth; 128 | private readonly double _elementSpacing; 129 | private readonly double _rowSpacing; 130 | 131 | private double _currentRowWidth; 132 | 133 | private int _newRows = 0; 134 | private int _rows = -1; 135 | private double _currentMaxWidth; 136 | public double Width => Math.Max((Math.Max(_currentMaxWidth, _currentRowWidth) - _elementSpacing), 0); 137 | public double Height => ((_rows + 1) * _rowHeight) + (_rows * _rowSpacing); 138 | 139 | #endregion 140 | 141 | #region Constructors 142 | 143 | public FormLayout(double rowHeight, double labelWidth, double elementSpacing, double rowSpacing) 144 | { 145 | this._rowHeight = rowHeight; 146 | this._labelWidth = labelWidth; 147 | this._elementSpacing = elementSpacing; 148 | this._rowSpacing = rowSpacing; 149 | } 150 | 151 | #endregion 152 | 153 | #region Methods 154 | 155 | public Rect AddElement(UIElement element, double targetWidth) 156 | { 157 | bool isLabel = GetIsLabel(element); 158 | int lineBreaks = GetLineBreaks(element); 159 | int rowSpan = GetRowSpan(element); 160 | 161 | double elementWidth = isLabel ? _labelWidth : element.DesiredSize.Width; 162 | double height = _rowHeight; 163 | 164 | if (_newRows > 0) 165 | { 166 | AddLineBreaks(_newRows); 167 | _newRows = 0; 168 | } 169 | 170 | if (lineBreaks > 0) AddLineBreaks(lineBreaks); 171 | else if (isLabel) AddLineBreaks(1); 172 | else if (_rows < 0) _rows = 0; 173 | 174 | if (!isLabel && (_currentRowWidth < _labelWidth)) 175 | _currentRowWidth = _labelWidth + _elementSpacing; 176 | 177 | if (rowSpan > 1) 178 | { 179 | height = (rowSpan * _rowHeight) + ((rowSpan - 1) * _rowSpacing); 180 | _newRows = Math.Max(_newRows, rowSpan - 1); 181 | } 182 | 183 | if (element is FrameworkElement fe) 184 | fe.MaxHeight = height; 185 | 186 | double width = elementWidth; 187 | if ((targetWidth >= 1) && GetFill(element)) 188 | width = targetWidth - _currentRowWidth; 189 | 190 | Rect rect = new Rect(new Point(_currentRowWidth, (_rows * _rowHeight) + (_rows * _rowSpacing)), new Size(width, height)); 191 | 192 | _currentRowWidth += width + _elementSpacing; 193 | 194 | return rect; 195 | } 196 | 197 | private void AddLineBreaks(int count) 198 | { 199 | if (count <= 0) return; 200 | 201 | _currentMaxWidth = Math.Max(_currentMaxWidth, _currentRowWidth); 202 | 203 | _currentRowWidth = 0; 204 | _rows += count; 205 | } 206 | 207 | #endregion 208 | } 209 | 210 | #endregion 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /RGBSync+/Controls/ImageButton.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | using System.Windows.Media; 4 | 5 | namespace RGBSyncPlus.Controls 6 | { 7 | public class ImageButton : Button 8 | { 9 | #region Properties & Fields 10 | // ReSharper disable InconsistentNaming 11 | 12 | public static readonly DependencyProperty ImageProperty = DependencyProperty.Register( 13 | "Image", typeof(ImageSource), typeof(ImageButton), new PropertyMetadata(default(ImageSource))); 14 | 15 | public ImageSource Image 16 | { 17 | get => (ImageSource)GetValue(ImageProperty); 18 | set => SetValue(ImageProperty, value); 19 | } 20 | 21 | public static readonly DependencyProperty HoverImageProperty = DependencyProperty.Register( 22 | "HoverImage", typeof(ImageSource), typeof(ImageButton), new PropertyMetadata(default(ImageSource))); 23 | 24 | public ImageSource HoverImage 25 | { 26 | get => (ImageSource)GetValue(HoverImageProperty); 27 | set => SetValue(HoverImageProperty, value); 28 | } 29 | 30 | public static readonly DependencyProperty PressedImageProperty = DependencyProperty.Register( 31 | "PressedImage", typeof(ImageSource), typeof(ImageButton), new PropertyMetadata(default(ImageSource))); 32 | 33 | public ImageSource PressedImage 34 | { 35 | get => (ImageSource)GetValue(PressedImageProperty); 36 | set => SetValue(PressedImageProperty, value); 37 | } 38 | 39 | // ReSharper restore InconsistentNaming 40 | #endregion 41 | 42 | #region Constructors 43 | 44 | static ImageButton() 45 | { 46 | DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton))); 47 | } 48 | 49 | #endregion 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /RGBSync+/Converter/BoolToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace RGBSyncPlus.Converter 7 | { 8 | [ValueConversion(typeof(bool), typeof(Visibility))] 9 | public class BoolToVisibilityConverter : IValueConverter 10 | { 11 | #region Methods 12 | 13 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 14 | => (value as bool?) == true ? Visibility.Visible 15 | : (string.Equals(parameter?.ToString(), "true", StringComparison.OrdinalIgnoreCase) ? Visibility.Hidden : Visibility.Collapsed); 16 | 17 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => value as Visibility? == Visibility.Visible; 18 | 19 | #endregion 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /RGBSync+/Converter/EqualsToBoolConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace RGBSyncPlus.Converter 6 | { 7 | [ValueConversion(typeof(object), typeof(bool))] 8 | public class EqualsToBoolConverter : IValueConverter 9 | { 10 | #region Methods 11 | 12 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => Equals(value, parameter); 13 | 14 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException(); 15 | 16 | #endregion 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /RGBSync+/Converter/NullToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace RGBSyncPlus.Converter 7 | { 8 | [ValueConversion(typeof(object), typeof(Visibility))] 9 | public class NullToVisibilityConverter : IValueConverter 10 | { 11 | #region Methods 12 | 13 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 14 | => (value == null) == (string.Equals(parameter?.ToString(), "true", StringComparison.OrdinalIgnoreCase)) ? Visibility.Visible : Visibility.Hidden; 15 | 16 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException(); 17 | 18 | #endregion 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /RGBSync+/Converter/ScrollOffsetToOpacityMaskConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | using System.Windows.Media; 6 | 7 | namespace RGBSyncPlus.Converter 8 | { 9 | public class ScrollOffsetToOpacityMaskConverter : IMultiValueConverter 10 | { 11 | #region Constants 12 | 13 | private static readonly Color TRANSPARENT = Color.FromArgb(0, 0, 0, 0); 14 | private static readonly Color OPAQUE = Color.FromArgb(255, 0, 0, 0); 15 | 16 | #endregion 17 | 18 | #region Methods 19 | 20 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 21 | { 22 | double offset = double.Parse(values[0].ToString()); 23 | double maxHeight = double.Parse(values[1].ToString()); 24 | double height = double.Parse(values[2].ToString()); 25 | 26 | double transparencyHeight = double.Parse(parameter.ToString()); 27 | double transparencyFactor = (transparencyHeight - 6) / height; 28 | double transparencyFadeFactor = (transparencyHeight + 4) / height; 29 | 30 | bool top = !(Math.Abs(offset) < float.Epsilon); 31 | bool bot = !(Math.Abs(offset - maxHeight) < float.Epsilon); 32 | 33 | if (!top && !bot) return new SolidColorBrush(OPAQUE); 34 | 35 | GradientStopCollection gradientStops = new GradientStopCollection(); 36 | if (top) 37 | { 38 | gradientStops.Add(new GradientStop(TRANSPARENT, 0.0)); 39 | gradientStops.Add(new GradientStop(TRANSPARENT, transparencyFactor)); 40 | gradientStops.Add(new GradientStop(OPAQUE, transparencyFadeFactor)); 41 | } 42 | else 43 | gradientStops.Add(new GradientStop(OPAQUE, 0.0)); 44 | 45 | if (bot) 46 | { 47 | gradientStops.Add(new GradientStop(OPAQUE, 1.0 - transparencyFadeFactor)); 48 | gradientStops.Add(new GradientStop(TRANSPARENT, 1.0 - transparencyFactor)); 49 | gradientStops.Add(new GradientStop(TRANSPARENT, 1.0)); 50 | } 51 | else 52 | gradientStops.Add(new GradientStop(OPAQUE, 1.0)); 53 | 54 | return new LinearGradientBrush(gradientStops, new Point(0.5, 0.0), new Point(0.5, 1.0)); 55 | } 56 | 57 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotSupportedException(); 58 | 59 | #endregion 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /RGBSync+/Converter/ScrollOffsetToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace RGBSyncPlus.Converter 7 | { 8 | // Based on: http://stackoverflow.com/a/28679767 9 | public class ScrollOffsetToVisibilityConverter : IMultiValueConverter 10 | { 11 | #region Methods 12 | 13 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 14 | { 15 | bool top = "top".Equals(parameter?.ToString(), StringComparison.OrdinalIgnoreCase); 16 | 17 | double offset = double.Parse(values[0].ToString()); 18 | double maxHeight = double.Parse(values[1].ToString()); 19 | 20 | return (top && Math.Abs(offset) < float.Epsilon) || (!top && Math.Abs(offset - maxHeight) < float.Epsilon) 21 | ? Visibility.Collapsed 22 | : Visibility.Visible; 23 | } 24 | 25 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotSupportedException(); 26 | 27 | #endregion 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /RGBSync+/Helper/ActionCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Input; 3 | 4 | namespace RGBSyncPlus.Helper 5 | { 6 | public class ActionCommand : ICommand 7 | { 8 | #region Properties & Fields 9 | 10 | private readonly Func _canExecute; 11 | private readonly Action _command; 12 | 13 | #endregion 14 | 15 | #region Events 16 | 17 | public event EventHandler CanExecuteChanged; 18 | 19 | #endregion 20 | 21 | #region Constructors 22 | 23 | public ActionCommand(Action command, Func canExecute = null) 24 | { 25 | this._command = command; 26 | this._canExecute = canExecute; 27 | } 28 | 29 | #endregion 30 | 31 | #region Methods 32 | 33 | public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true; 34 | 35 | public void Execute(object parameter) => _command?.Invoke(); 36 | 37 | public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, new EventArgs()); 38 | 39 | #endregion 40 | } 41 | 42 | public class ActionCommand : ICommand 43 | { 44 | #region Properties & Fields 45 | 46 | private readonly Func _canExecute; 47 | private readonly Action _command; 48 | 49 | #endregion 50 | 51 | #region Events 52 | 53 | public event EventHandler CanExecuteChanged; 54 | 55 | #endregion 56 | 57 | #region Constructors 58 | 59 | public ActionCommand(Action command, Func canExecute = null) 60 | { 61 | this._command = command; 62 | this._canExecute = canExecute; 63 | } 64 | 65 | #endregion 66 | 67 | #region Methods 68 | 69 | public bool CanExecute(object parameter) => _canExecute?.Invoke((T)parameter) ?? true; 70 | 71 | public void Execute(object parameter) => _command?.Invoke((T)parameter); 72 | 73 | public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, new EventArgs()); 74 | 75 | #endregion 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /RGBSync+/Helper/ExceptionExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RGBSyncPlus.Helper 4 | { 5 | public static class ExceptionExtension 6 | { 7 | #region Methods 8 | 9 | public static string GetFullMessage(this Exception ex, string message = "") 10 | { 11 | if (ex == null) return string.Empty; 12 | 13 | message += ex.Message; 14 | 15 | if (ex.InnerException != null) 16 | message += "\r\nInnerException: " + GetFullMessage(ex.InnerException); 17 | 18 | return message; 19 | } 20 | 21 | #endregion 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /RGBSync+/Helper/MathHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RGBSyncPlus.Helper 4 | { 5 | public static class MathHelper 6 | { 7 | #region Methods 8 | 9 | public static double Clamp(double value, double min, double max) => Math.Max(min, Math.Min(max, value)); 10 | public static float Clamp(float value, float min, float max) => (float)Clamp((double)value, min, max); 11 | public static int Clamp(int value, int min, int max) => Math.Max(min, Math.Min(max, value)); 12 | 13 | #endregion 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /RGBSync+/Helper/RGBNetExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using RGB.NET.Core; 4 | using RGBSyncPlus.Model; 5 | 6 | namespace RGBSyncPlus.Helper 7 | { 8 | public static class RGBNetExtension 9 | { 10 | public static string GetDeviceName(this IRGBDevice device) => $"{device.DeviceInfo.DeviceName} ({device.DeviceInfo.DeviceType})"; 11 | 12 | public static IEnumerable GetLeds(this IEnumerable syncLeds) 13 | => syncLeds.Select(GetLed).Where(led => led != null); 14 | 15 | public static Led GetLed(this SyncLed syncLed) 16 | { 17 | if (syncLed == null) return null; 18 | return RGBSurface.Instance.Leds.FirstOrDefault(l => (l.Id == syncLed.LedId) && (l.Device.GetDeviceName() == syncLed.Device)); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /RGBSync+/Model/SyncGroup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using System.Collections.Specialized; 3 | using Newtonsoft.Json; 4 | using RGB.NET.Core; 5 | using RGB.NET.Groups; 6 | 7 | namespace RGBSyncPlus.Model 8 | { 9 | public class SyncGroup : AbstractBindable 10 | { 11 | #region Properties & Fields 12 | 13 | public string DisplayName => string.IsNullOrWhiteSpace(Name) ? "(unnamed)" : Name; 14 | 15 | private string _name; 16 | public string Name 17 | { 18 | get => _name; 19 | set 20 | { 21 | if (SetProperty(ref _name, value)) 22 | OnPropertyChanged(nameof(DisplayName)); 23 | } 24 | } 25 | 26 | private SyncLed _syncLed; 27 | public SyncLed SyncLed 28 | { 29 | get => _syncLed; 30 | set => SetProperty(ref _syncLed, value); 31 | } 32 | 33 | private ObservableCollection _leds = new ObservableCollection(); 34 | public ObservableCollection Leds 35 | { 36 | get => _leds; 37 | set => SetProperty(ref _leds, value); 38 | } 39 | 40 | [JsonIgnore] 41 | public ListLedGroup LedGroup { get; set; } 42 | 43 | [JsonIgnore] 44 | public NotifyCollectionChangedEventHandler LedsChangedEventHandler { get; set; } 45 | 46 | #endregion 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /RGBSync+/Model/SyncLed.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using RGB.NET.Core; 3 | using RGBSyncPlus.Helper; 4 | 5 | namespace RGBSyncPlus.Model 6 | { 7 | public class SyncLed : AbstractBindable 8 | { 9 | #region Properties & Fields 10 | 11 | private string _device; 12 | public string Device 13 | { 14 | get => _device; 15 | set => SetProperty(ref _device, value); 16 | } 17 | 18 | private LedId _ledId; 19 | public LedId LedId 20 | { 21 | get => _ledId; 22 | set => SetProperty(ref _ledId, value); 23 | } 24 | 25 | private Led _led; 26 | [JsonIgnore] 27 | public Led Led 28 | { 29 | get => _led; 30 | set => SetProperty(ref _led, value); 31 | } 32 | 33 | #endregion 34 | 35 | #region Constructors 36 | 37 | public SyncLed() 38 | { } 39 | 40 | public SyncLed(string device, LedId ledId) 41 | { 42 | this.Device = device; 43 | this.LedId = ledId; 44 | } 45 | 46 | public SyncLed(Led led) 47 | { 48 | this.Device = led.Device.GetDeviceName(); 49 | this.LedId = led.Id; 50 | this.Led = led; 51 | } 52 | 53 | #endregion 54 | 55 | #region Methods 56 | 57 | protected bool Equals(SyncLed other) => string.Equals(_device, other._device) && (_ledId == other._ledId); 58 | 59 | public override bool Equals(object obj) 60 | { 61 | if (ReferenceEquals(null, obj)) return false; 62 | if (ReferenceEquals(this, obj)) return true; 63 | if (obj.GetType() != this.GetType()) return false; 64 | return Equals((SyncLed)obj); 65 | } 66 | 67 | public override int GetHashCode() 68 | { 69 | unchecked 70 | { 71 | return ((_device != null ? _device.GetHashCode() : 0) * 397) ^ (int)_ledId; 72 | } 73 | } 74 | 75 | public static bool operator ==(SyncLed left, SyncLed right) => Equals(left, right); 76 | public static bool operator !=(SyncLed left, SyncLed right) => !Equals(left, right); 77 | 78 | #endregion 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /RGBSync+/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RGBSync+/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("RGBSync+")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("RGBSync+")] 15 | [assembly: AssemblyCopyright("Copyright © 2018")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.3")] 55 | [assembly: AssemblyFileVersion("1.0.0.3")] 56 | -------------------------------------------------------------------------------- /RGBSync+/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace RGBSyncPlus.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RGBSyncPlus.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /RGBSync+/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /RGBSync+/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace RGBSyncPlus.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /RGBSync+/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /RGBSync+/RGBSync+.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C6BF4357-07D9-496B-9630-A26568D30723} 8 | WinExe 9 | RGBSyncPlus 10 | RGBSync+ 11 | v4.5 12 | 512 13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 4 15 | 16 | 17 | x86 18 | true 19 | full 20 | false 21 | ..\bin\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | x86 28 | pdbonly 29 | true 30 | ..\bin\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | RGBSyncPlus.App 37 | 38 | 39 | Resources\argebee.ico 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 4.0 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | MSBuild:Compile 61 | Designer 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | ConfigurationWindow.xaml 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | App.xaml 95 | Code 96 | 97 | 98 | MSBuild:Compile 99 | Designer 100 | 101 | 102 | MSBuild:Compile 103 | Designer 104 | 105 | 106 | MSBuild:Compile 107 | Designer 108 | 109 | 110 | MSBuild:Compile 111 | Designer 112 | 113 | 114 | MSBuild:Compile 115 | Designer 116 | 117 | 118 | MSBuild:Compile 119 | Designer 120 | 121 | 122 | MSBuild:Compile 123 | Designer 124 | 125 | 126 | MSBuild:Compile 127 | Designer 128 | 129 | 130 | MSBuild:Compile 131 | Designer 132 | 133 | 134 | MSBuild:Compile 135 | Designer 136 | 137 | 138 | MSBuild:Compile 139 | Designer 140 | 141 | 142 | MSBuild:Compile 143 | Designer 144 | 145 | 146 | MSBuild:Compile 147 | Designer 148 | 149 | 150 | MSBuild:Compile 151 | Designer 152 | 153 | 154 | MSBuild:Compile 155 | Designer 156 | 157 | 158 | MSBuild:Compile 159 | Designer 160 | 161 | 162 | MSBuild:Compile 163 | Designer 164 | 165 | 166 | 167 | 168 | Code 169 | 170 | 171 | True 172 | True 173 | Resources.resx 174 | 175 | 176 | True 177 | Settings.settings 178 | True 179 | 180 | 181 | ResXFileCodeGenerator 182 | Resources.Designer.cs 183 | 184 | 185 | SettingsSingleFileGenerator 186 | Settings.Designer.cs 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 1.1.0 197 | 198 | 199 | 1.0.8 200 | 201 | 202 | 12.0.1 203 | 204 | 205 | 0.1.23 206 | 207 | 208 | 0.1.23 209 | 210 | 211 | 0.1.23 212 | 213 | 214 | 0.1.23 215 | 216 | 217 | 4.5.0 218 | 219 | 220 | 1.0.0 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | -------------------------------------------------------------------------------- /RGBSync+/RGBSync+.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2018 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RGBSync+", "RGBSync+.csproj", "{C6BF4357-07D9-496B-9630-A26568D30723}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {C6BF4357-07D9-496B-9630-A26568D30723}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {C6BF4357-07D9-496B-9630-A26568D30723}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {C6BF4357-07D9-496B-9630-A26568D30723}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {C6BF4357-07D9-496B-9630-A26568D30723}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {3AD1163D-9F77-43AB-A3BA-38C1B9CCBA87} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /RGBSync+/Resources/RGBSync+.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 20 | 21 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /RGBSync+/Styles/Button.xaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | 6 | 7 | 8 | 9 | 10 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /RGBSync+/Styles/CachedResourceDictionary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Windows; 5 | 6 | namespace RGBSyncPlus.Styles 7 | { 8 | public class CachedResourceDictionary : ResourceDictionary 9 | { 10 | #region Properties & Fields 11 | 12 | // ReSharper disable InconsistentNaming 13 | private static readonly List _cachedDictionaries = new List(); 14 | private static readonly ResourceDictionary _innerDictionary = new ResourceDictionary(); 15 | // ReSharper restore 16 | 17 | public new Uri Source 18 | { 19 | get => null; 20 | set 21 | { 22 | lock (_innerDictionary) 23 | { 24 | UpdateCache(value); 25 | 26 | MergedDictionaries.Clear(); 27 | MergedDictionaries.Add(_innerDictionary); 28 | } 29 | } 30 | } 31 | 32 | #endregion 33 | 34 | #region Methods 35 | 36 | private static void UpdateCache(Uri source) 37 | { 38 | string uriPath = source.OriginalString; 39 | if (_cachedDictionaries.Contains(uriPath)) return; 40 | 41 | _cachedDictionaries.Add(uriPath); 42 | 43 | ResourceDictionary newDictionary = new ResourceDictionary { Source = new Uri(uriPath, source.IsAbsoluteUri ? UriKind.Absolute : UriKind.Relative) }; 44 | CopyDictionaryEntries(newDictionary, _innerDictionary); 45 | } 46 | 47 | private static void CopyDictionaryEntries(IDictionary source, IDictionary target) 48 | { 49 | foreach (object key in source.Keys) 50 | if (!target.Contains(key)) 51 | target.Add(key, source[key]); 52 | } 53 | 54 | #endregion 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /RGBSync+/Styles/ColorSelector.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 10 | 11 | 13 | 14 | 15 | 16 | 20 | 21 | 23 | 24 | 25 | 26 | 31 | 32 | 36 | 37 | 57 | 58 | 111 | 112 | 303 | 32 | 33 | 35 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 71 | 74 | 75 | 76 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /RGBSync+/Styles/Form.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | 10 | 19 | 20 | 46 | 47 | 55 | 56 | 79 | 80 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /RGBSync+/Styles/FrameworkElement.xaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /RGBSync+/Styles/GradientEditor.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | 10 | 42 | 43 | 91 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /RGBSync+/Styles/ImageButton.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | 10 | 68 | 69 | 105 | 106 | 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /RGBSync+/Styles/ListBox.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 147 | 148 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /RGBSync+/Styles/Navigation.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 98 | 99 | 175 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /RGBSync+/Styles/TextBox.xaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | 6 | 7 | 8 | 9 | 10 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /RGBSync+/Styles/Theme.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | #FFDCDCDC 7 | #FF2A2A2A 8 | #B82A2A2A 9 | #111111 10 | #B8111111 11 | #60111111 12 | #50000000 13 | #FFE135 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | #60111111 53 | #B8111111 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 40 69 | 70 | 71 | 72 | 14 73 | 14 74 | 22 75 | 76 | -------------------------------------------------------------------------------- /RGBSync+/Styles/ToolTip.xaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | 6 | 7 | 8 | 9 | 10 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /RGBSync+/UI/ConfigurationViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Collections.ObjectModel; 5 | using System.ComponentModel; 6 | using System.Diagnostics; 7 | using System.Linq; 8 | using System.Reflection; 9 | using System.Windows; 10 | using System.Windows.Data; 11 | using GongSolutions.Wpf.DragDrop; 12 | using RGB.NET.Core; 13 | using RGBSyncPlus.Helper; 14 | using RGBSyncPlus.Model; 15 | 16 | namespace RGBSyncPlus.UI 17 | { 18 | public sealed class ConfigurationViewModel : AbstractBindable, IDropTarget 19 | { 20 | #region Properties & Fields 21 | 22 | public Version Version => Assembly.GetEntryAssembly().GetName().Version; 23 | 24 | public double UpdateRate 25 | { 26 | get => 1.0 / ApplicationManager.Instance.UpdateTrigger.UpdateFrequency; 27 | set 28 | { 29 | double val = MathHelper.Clamp(value, 1, 100); 30 | ApplicationManager.Instance.Settings.UpdateRate = val; 31 | ApplicationManager.Instance.UpdateTrigger.UpdateFrequency = 1.0 / val; 32 | OnPropertyChanged(); 33 | } 34 | } 35 | 36 | private ObservableCollection _syncGroups; 37 | public ObservableCollection SyncGroups 38 | { 39 | get => _syncGroups; 40 | set => SetProperty(ref _syncGroups, value); 41 | } 42 | 43 | private SyncGroup _selectedSyncGroup; 44 | public SyncGroup SelectedSyncGroup 45 | { 46 | get => _selectedSyncGroup; 47 | set 48 | { 49 | if (SetProperty(ref _selectedSyncGroup, value)) 50 | UpdateLedLists(); 51 | } 52 | } 53 | 54 | private ListCollectionView _availableSyncLeds; 55 | public ListCollectionView AvailableSyncLeds 56 | { 57 | get => _availableSyncLeds; 58 | set => SetProperty(ref _availableSyncLeds, value); 59 | } 60 | 61 | private ListCollectionView _availableLeds; 62 | public ListCollectionView AvailableLeds 63 | { 64 | get => _availableLeds; 65 | set => SetProperty(ref _availableLeds, value); 66 | } 67 | 68 | private ListCollectionView _synchronizedLeds; 69 | public ListCollectionView SynchronizedLeds 70 | { 71 | get => _synchronizedLeds; 72 | set => SetProperty(ref _synchronizedLeds, value); 73 | } 74 | 75 | #endregion 76 | 77 | #region Commands 78 | 79 | private ActionCommand _openHomepageCommand; 80 | public ActionCommand OpenHomepageCommand => _openHomepageCommand ?? (_openHomepageCommand = new ActionCommand(OpenHomepage)); 81 | 82 | private ActionCommand _addSyncGroupCommand; 83 | public ActionCommand AddSyncGroupCommand => _addSyncGroupCommand ?? (_addSyncGroupCommand = new ActionCommand(AddSyncGroup)); 84 | 85 | private ActionCommand _removeSyncGroupCommand; 86 | public ActionCommand RemoveSyncGroupCommand => _removeSyncGroupCommand ?? (_removeSyncGroupCommand = new ActionCommand(RemoveSyncGroup)); 87 | 88 | #endregion 89 | 90 | #region Constructors 91 | 92 | public ConfigurationViewModel() 93 | { 94 | SyncGroups = new ObservableCollection(ApplicationManager.Instance.Settings.SyncGroups); 95 | 96 | AvailableSyncLeds = GetGroupedLedList(RGBSurface.Instance.Leds.Where(x => x.Device.DeviceInfo.SupportsSyncBack)); 97 | OnPropertyChanged(nameof(AvailableSyncLeds)); 98 | } 99 | 100 | #endregion 101 | 102 | #region Methods 103 | 104 | private ListCollectionView GetGroupedLedList(IEnumerable leds) => GetGroupedLedList(leds.Select(led => new SyncLed(led)).ToList()); 105 | 106 | private ListCollectionView GetGroupedLedList(IList syncLeds) 107 | { 108 | ListCollectionView collectionView = new ListCollectionView(syncLeds); 109 | collectionView.GroupDescriptions.Add(new PropertyGroupDescription(nameof(SyncLed.Device))); 110 | collectionView.SortDescriptions.Add(new SortDescription(nameof(SyncLed.Device), ListSortDirection.Ascending)); 111 | collectionView.SortDescriptions.Add(new SortDescription(nameof(SyncLed.LedId), ListSortDirection.Ascending)); 112 | collectionView.Refresh(); 113 | return collectionView; 114 | } 115 | 116 | private void UpdateLedLists() 117 | { 118 | SynchronizedLeds = GetGroupedLedList(SelectedSyncGroup.Leds); 119 | OnPropertyChanged(nameof(SynchronizedLeds)); 120 | 121 | AvailableLeds = GetGroupedLedList(RGBSurface.Instance.Leds.Where(led => !SelectedSyncGroup.Leds.Any(sc => (sc.LedId == led.Id) && (sc.Device == led.Device.GetDeviceName())))); 122 | OnPropertyChanged(nameof(AvailableLeds)); 123 | } 124 | 125 | private void OpenHomepage() => Process.Start("https://github.com/DarthAffe/RGBSyncPlus"); 126 | 127 | private void AddSyncGroup() 128 | { 129 | SyncGroup syncGroup = new SyncGroup(); 130 | SyncGroups.Add(syncGroup); 131 | ApplicationManager.Instance.AddSyncGroup(syncGroup); 132 | } 133 | 134 | private void RemoveSyncGroup(SyncGroup syncGroup) 135 | { 136 | if (syncGroup == null) return; 137 | 138 | if (MessageBox.Show($"Are you sure that you want to delete the group '{syncGroup.DisplayName}'", "Remove Sync-Group", MessageBoxButton.YesNo) == MessageBoxResult.No) 139 | return; 140 | 141 | SyncGroups.Remove(syncGroup); 142 | ApplicationManager.Instance.RemoveSyncGroup(syncGroup); 143 | } 144 | 145 | void IDropTarget.DragOver(IDropInfo dropInfo) 146 | { 147 | if ((dropInfo.Data is SyncLed || dropInfo.Data is IEnumerable) && (dropInfo.TargetCollection is ListCollectionView)) 148 | { 149 | dropInfo.DropTargetAdorner = DropTargetAdorners.Highlight; 150 | dropInfo.Effects = DragDropEffects.Copy; 151 | } 152 | } 153 | 154 | void IDropTarget.Drop(IDropInfo dropInfo) 155 | { 156 | if (!(dropInfo.TargetCollection is ListCollectionView targetList)) return; 157 | 158 | //HACK DarthAffe 04.06.2018: Super ugly hack - I've no idea how to do this correctly ... 159 | ListCollectionView sourceList = targetList == AvailableLeds ? SynchronizedLeds : AvailableLeds; 160 | 161 | if (dropInfo.Data is SyncLed syncLed) 162 | { 163 | targetList.AddNewItem(syncLed); 164 | sourceList.Remove(syncLed); 165 | 166 | targetList.CommitNew(); 167 | sourceList.CommitEdit(); 168 | } 169 | else if (dropInfo.Data is IEnumerable syncLeds) 170 | { 171 | foreach (SyncLed led in syncLeds) 172 | { 173 | targetList.AddNewItem(led); 174 | sourceList.Remove(led); 175 | } 176 | targetList.CommitNew(); 177 | sourceList.CommitEdit(); 178 | } 179 | } 180 | 181 | #endregion 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /RGBSync+/UI/ConfigurationWindow.xaml: -------------------------------------------------------------------------------- 1 |  19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 76 | 77 | 78 | 79 | 80 | 81 |