Add in the ability to make custom overrides.
Fixed issues with the CSS in the property inspector Make it so i can actually write unit tests if i want to
@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.2.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FocusVolumeControl\FocusVolumeControl.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
168
src/FocusVolumeControl.UnitTests/OverrideParserTests.cs
Normal file
@ -0,0 +1,168 @@
|
||||
using FocusVolumeControl.Overrides;
|
||||
|
||||
namespace FocusVolumeControl.UnitTests
|
||||
{
|
||||
public class OverrideParserTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
[InlineData("\n")]
|
||||
public void BlankReturnsEmpty(string str)
|
||||
{
|
||||
//arrange
|
||||
|
||||
//act
|
||||
var overrides = OverrideParser.Parse(str);
|
||||
|
||||
//assert
|
||||
Assert.Empty(overrides);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HelldiversParses()
|
||||
{
|
||||
//arrange
|
||||
var str =
|
||||
"""
|
||||
eq: HELLDIVERS™ 2
|
||||
helldivers2
|
||||
""";
|
||||
|
||||
//act
|
||||
var overrides = OverrideParser.Parse(str);
|
||||
|
||||
//assert
|
||||
Assert.Single(overrides);
|
||||
Assert.Equal(MatchType.Equal, overrides[0].MatchType);
|
||||
Assert.Equal("HELLDIVERS™ 2", overrides[0].WindowQuery);
|
||||
Assert.Equal("helldivers2", overrides[0].AudioProcessName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultipleOverridesParse()
|
||||
{
|
||||
//arrange
|
||||
var str =
|
||||
"""
|
||||
eq: HELLDIVERS™ 2
|
||||
helldivers2
|
||||
|
||||
start: Task
|
||||
Steam
|
||||
""";
|
||||
|
||||
//act
|
||||
var overrides = OverrideParser.Parse(str);
|
||||
|
||||
//assert
|
||||
Assert.Equal(MatchType.Equal, overrides[0].MatchType);
|
||||
Assert.Equal("HELLDIVERS™ 2", overrides[0].WindowQuery);
|
||||
Assert.Equal("helldivers2", overrides[0].AudioProcessName);
|
||||
Assert.Equal(MatchType.StartsWith, overrides[1].MatchType);
|
||||
Assert.Equal("Task", overrides[1].WindowQuery);
|
||||
Assert.Equal("Steam", overrides[1].AudioProcessName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IncompleteMatchesAreSkipped()
|
||||
{
|
||||
//arrange
|
||||
var str =
|
||||
"""
|
||||
eq: HELLDIVERS™ 2
|
||||
|
||||
start: Task
|
||||
Steam
|
||||
""";
|
||||
|
||||
//act
|
||||
var overrides = OverrideParser.Parse(str);
|
||||
|
||||
//assert
|
||||
Assert.Single(overrides);
|
||||
Assert.Equal(MatchType.StartsWith, overrides[0].MatchType);
|
||||
Assert.Equal("Task", overrides[0].WindowQuery);
|
||||
Assert.Equal("Steam", overrides[0].AudioProcessName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidMatchesAreSkipped()
|
||||
{
|
||||
//arrange
|
||||
var str =
|
||||
"""
|
||||
equal: Chrome
|
||||
chrome
|
||||
|
||||
end: Task
|
||||
Steam
|
||||
""";
|
||||
|
||||
//act
|
||||
var overrides = OverrideParser.Parse(str);
|
||||
|
||||
//assert
|
||||
Assert.Single(overrides);
|
||||
Assert.Equal(MatchType.EndsWith, overrides[0].MatchType);
|
||||
Assert.Equal("Task", overrides[0].WindowQuery);
|
||||
Assert.Equal("Steam", overrides[0].AudioProcessName);
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAreCaseInsensitive()
|
||||
{
|
||||
//arrange
|
||||
var str =
|
||||
"""
|
||||
Eq: 0
|
||||
0
|
||||
|
||||
eNd: 1
|
||||
1
|
||||
|
||||
StArT: 2
|
||||
2
|
||||
|
||||
Regex: 3
|
||||
3
|
||||
""";
|
||||
|
||||
//act
|
||||
var overrides = OverrideParser.Parse(str);
|
||||
|
||||
//assert
|
||||
Assert.Equal(MatchType.Equal, overrides[0].MatchType);
|
||||
Assert.Equal(MatchType.EndsWith, overrides[1].MatchType);
|
||||
Assert.Equal(MatchType.StartsWith, overrides[2].MatchType);
|
||||
Assert.Equal(MatchType.Regex, overrides[3].MatchType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CommentsAreSkipped()
|
||||
{
|
||||
//arrange
|
||||
var str =
|
||||
"""
|
||||
//Eq: 0
|
||||
//0
|
||||
|
||||
end: 1
|
||||
1
|
||||
""";
|
||||
|
||||
//act
|
||||
var overrides = OverrideParser.Parse(str);
|
||||
|
||||
//assert
|
||||
Assert.Single(overrides);
|
||||
Assert.Equal(MatchType.EndsWith, overrides[0].MatchType);
|
||||
Assert.Equal("1", overrides[0].WindowQuery);
|
||||
Assert.Equal("1", overrides[0].AudioProcessName);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
1
src/FocusVolumeControl.UnitTests/Usings.cs
Normal file
@ -0,0 +1 @@
|
||||
global using Xunit;
|
@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FocusVolumeControl", "Focus
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SoundBrowser", "SoundBrowser\SoundBrowser.csproj", "{0E8AB334-82F1-4DBC-9BDA-B6F9714A1847}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FocusVolumeControl.UnitTests", "FocusVolumeControl.UnitTests\FocusVolumeControl.UnitTests.csproj", "{322E16C9-C96E-45DF-912F-DB6366170645}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -21,6 +23,10 @@ Global
|
||||
{0E8AB334-82F1-4DBC-9BDA-B6F9714A1847}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0E8AB334-82F1-4DBC-9BDA-B6F9714A1847}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0E8AB334-82F1-4DBC-9BDA-B6F9714A1847}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{322E16C9-C96E-45DF-912F-DB6366170645}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{322E16C9-C96E-45DF-912F-DB6366170645}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{322E16C9-C96E-45DF-912F-DB6366170645}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{322E16C9-C96E-45DF-912F-DB6366170645}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -1,8 +1,10 @@
|
||||
using FocusVolumeControl.AudioSessions;
|
||||
using FocusVolumeControl.Overrides;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FocusVolumeControl.AudioHelpers;
|
||||
|
||||
@ -14,6 +16,8 @@ public class AudioHelper
|
||||
int[] _currentProcesses;
|
||||
int _retryFallbackCount = 0;
|
||||
|
||||
public List<Override> Overrides { get; set; }
|
||||
|
||||
public IAudioSession Current { get; private set; }
|
||||
|
||||
public void ResetCache()
|
||||
@ -113,7 +117,12 @@ public class AudioHelper
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var processes = GetPossibleProcesses();
|
||||
var processes = TryGetProcessFromOverrides();
|
||||
|
||||
if(processes == null)
|
||||
{
|
||||
processes = GetPossibleProcesses();
|
||||
}
|
||||
var processIds = processes?.Select(x => x.Id).ToArray();
|
||||
|
||||
//_currentProcesses null - first time getting sessions
|
||||
@ -175,27 +184,18 @@ public class AudioHelper
|
||||
|
||||
Native.GetWindowThreadProcessId(handle, out var pid);
|
||||
|
||||
if(ids.Count == 0 && pid == 0)
|
||||
{
|
||||
foreach(var p in Process.GetProcesses())
|
||||
{
|
||||
if(p.MainWindowHandle == handle)
|
||||
{
|
||||
if(p.MainWindowTitle == "HELLDIVERS™ 2")
|
||||
{
|
||||
ids = FindAudioSessionByProcessName("helldivers2");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if(pid != 0)
|
||||
{
|
||||
ids.Insert(0, pid);
|
||||
}
|
||||
|
||||
if(ids.Count == 0)
|
||||
{
|
||||
return new List<Process>();
|
||||
}
|
||||
|
||||
var processes = ids.Distinct()
|
||||
.Select(x => Process.GetProcessById(x))
|
||||
.Select(Process.GetProcessById)
|
||||
.ToList();
|
||||
|
||||
if(processes.FirstOrDefault()?.ProcessName == "explorer")
|
||||
@ -328,6 +328,47 @@ public class AudioHelper
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<Process> TryGetProcessFromOverrides(IntPtr? handleOverride = null)
|
||||
{
|
||||
var handle = handleOverride ?? Native.GetForegroundWindow();
|
||||
|
||||
if (Overrides?.Any() == true)
|
||||
{
|
||||
Process tmp = null;
|
||||
foreach (var p in Process.GetProcesses())
|
||||
{
|
||||
if (p.MainWindowHandle == handle)
|
||||
{
|
||||
tmp = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tmp != null)
|
||||
{
|
||||
foreach (var o in Overrides)
|
||||
{
|
||||
if (
|
||||
(o.MatchType == MatchType.Equal && tmp.MainWindowTitle.Equals(o.WindowQuery, StringComparison.InvariantCultureIgnoreCase))
|
||||
|| (o.MatchType == MatchType.StartsWith && tmp.MainWindowTitle.StartsWith(o.WindowQuery, StringComparison.OrdinalIgnoreCase))
|
||||
|| (o.MatchType == MatchType.EndsWith && tmp.MainWindowTitle.EndsWith(o.WindowQuery, StringComparison.OrdinalIgnoreCase))
|
||||
|| (o.MatchType == MatchType.Regex && Regex.IsMatch(tmp.MainWindowTitle, o.WindowQuery))
|
||||
)
|
||||
{
|
||||
var ids = FindAudioSessionByProcessName(o.AudioProcessName);
|
||||
if (ids?.Count > 0)
|
||||
{
|
||||
return ids.Distinct()
|
||||
.Select(Process.GetProcessById)
|
||||
.ToList();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<int> FindAudioSessionByProcessName(string processName)
|
||||
{
|
||||
@ -338,7 +379,6 @@ public class AudioHelper
|
||||
}
|
||||
|
||||
var results = new List<int>();
|
||||
Process bestProcessMatch = null;
|
||||
|
||||
var deviceEnumerator = (IMMDeviceEnumerator)new MMDeviceEnumerator();
|
||||
|
||||
@ -356,8 +396,6 @@ public class AudioHelper
|
||||
|
||||
manager.GetSessionEnumerator(out var sessionEnumerator);
|
||||
|
||||
var currentIndex = int.MaxValue;
|
||||
|
||||
sessionEnumerator.GetCount(out var count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
@ -367,7 +405,13 @@ public class AudioHelper
|
||||
|
||||
var audioProcess = GetProcessById(sessionProcessId);
|
||||
|
||||
if(audioProcess != null && audioProcess.ProcessName == processName)
|
||||
if(audioProcess == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var audioProcessName = _nameAndIconHelper.TryGetProcessNameWithoutIcon(audioProcess);
|
||||
if(audioProcess.ProcessName == processName || displayName == processName || processName == audioProcessName)
|
||||
{
|
||||
results.Add(sessionProcessId);
|
||||
}
|
||||
|
@ -83,6 +83,41 @@ public class NameAndIconHelper
|
||||
}
|
||||
}
|
||||
|
||||
public string TryGetProcessNameWithoutIcon(Process process)
|
||||
{
|
||||
try
|
||||
{
|
||||
//appx packages are installed from the windows store. eg, itunes
|
||||
var appx = AppxPackage.FromProcess(process);
|
||||
if (appx == null)
|
||||
{
|
||||
|
||||
//if the display name is already set, then it came from the display name of the audio session
|
||||
if(!string.IsNullOrEmpty(process.MainWindowTitle))
|
||||
{
|
||||
return process.MainWindowTitle;
|
||||
}
|
||||
|
||||
//using process.MainModule.FileVersionInfo sometimes throws permission exceptions
|
||||
//we get the file version info with a limited query flag to avoid that
|
||||
var fileVersionInfo = GetFileVersionInfo(process);
|
||||
if(!string.IsNullOrEmpty(fileVersionInfo?.FileDescription))
|
||||
{
|
||||
return fileVersionInfo.FileDescription;
|
||||
}
|
||||
|
||||
return process.ProcessName;
|
||||
}
|
||||
else
|
||||
{
|
||||
return appx.DisplayName ?? process.ProcessName;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return process.ProcessName;
|
||||
}
|
||||
|
||||
|
||||
FileVersionInfo GetFileVersionInfo(Process process)
|
||||
{
|
||||
var path = GetExecutablePathWithPInvoke(process);
|
||||
|
@ -10,9 +10,11 @@ using FocusVolumeControl.AudioSession;
|
||||
|
||||
namespace FocusVolumeControl.AudioSessions;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public sealed class ActiveAudioSessionWrapper : IAudioSession
|
||||
{
|
||||
public string DisplayName { get; set; }
|
||||
public string DisplayName { get; set; } = null!;
|
||||
private List<IAudioSessionControl2> Sessions { get; } = new List<IAudioSessionControl2>();
|
||||
private IEnumerable<ISimpleAudioVolume> Volume => Sessions.Cast<ISimpleAudioVolume>();
|
||||
|
||||
@ -102,3 +104,4 @@ public sealed class ActiveAudioSessionWrapper : IAudioSession
|
||||
return VolumeHelpers.GetVolumePercentage(level);
|
||||
}
|
||||
}
|
||||
#nullable restore
|
||||
|
@ -4,6 +4,8 @@ using FocusVolumeControl.UI;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace FocusVolumeControl.AudioSession
|
||||
{
|
||||
public abstract class IconWrapper
|
||||
@ -101,3 +103,4 @@ namespace FocusVolumeControl.AudioSession
|
||||
}
|
||||
|
||||
}
|
||||
#nullable restore
|
||||
|
@ -2,6 +2,7 @@
|
||||
using BarRaider.SdTools.Payloads;
|
||||
using FocusVolumeControl.AudioHelpers;
|
||||
using FocusVolumeControl.AudioSessions;
|
||||
using FocusVolumeControl.Overrides;
|
||||
using FocusVolumeControl.UI;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@ -15,6 +16,12 @@ namespace FocusVolumeControl;
|
||||
[PluginActionId("com.dlprows.focusvolumecontrol.dialaction")]
|
||||
public class DialAction : EncoderBase
|
||||
{
|
||||
private const string DefaultOverrides =
|
||||
"""
|
||||
//eq: HELLDIVERS™ 2
|
||||
//helldivers2
|
||||
""";
|
||||
|
||||
private class PluginSettings
|
||||
{
|
||||
[JsonProperty("fallbackBehavior")]
|
||||
@ -23,11 +30,15 @@ public class DialAction : EncoderBase
|
||||
[JsonProperty("stepSize")]
|
||||
public int StepSize { get; set; }
|
||||
|
||||
[JsonProperty("overrides")]
|
||||
public string Overrides { get; set; }
|
||||
|
||||
public static PluginSettings CreateDefaultSettings()
|
||||
{
|
||||
PluginSettings instance = new PluginSettings();
|
||||
instance.FallbackBehavior = FallbackBehavior.SystemSounds;
|
||||
instance.StepSize = 1;
|
||||
instance.Overrides = DefaultOverrides;
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
@ -46,12 +57,18 @@ public class DialAction : EncoderBase
|
||||
else
|
||||
{
|
||||
settings = payload.Settings.ToObject<PluginSettings>();
|
||||
if(string.IsNullOrEmpty(settings.Overrides))
|
||||
{
|
||||
settings.Overrides = DefaultOverrides;
|
||||
_ = SaveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
WindowChangedEventLoop.Instance.WindowChanged += WindowChanged;
|
||||
|
||||
try
|
||||
{
|
||||
_audioHelper.Overrides = OverrideParser.Parse(settings.Overrides);
|
||||
//just in case we fail to get the active session, don't prevent the plugin from launching
|
||||
var session = _audioHelper.GetActiveSession(settings.FallbackBehavior);
|
||||
_ = UpdateStateIfNeeded(session);
|
||||
@ -220,7 +237,8 @@ public class DialAction : EncoderBase
|
||||
try
|
||||
{
|
||||
Tools.AutoPopulateSettings(settings, payload.Settings);
|
||||
_ = SaveSettings();
|
||||
_audioHelper.Overrides = OverrideParser.Parse(settings.Overrides);
|
||||
//_ = SaveSettings();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -66,6 +66,10 @@
|
||||
<Compile Include="AudioSessions\IAudioSession.cs" />
|
||||
<Compile Include="FallbackBehavior.cs" />
|
||||
<Compile Include="AudioHelpers\NameAndIconHelper.cs" />
|
||||
<Compile Include="InternalsVisibleTo.cs" />
|
||||
<Compile Include="Overrides\Override.cs" />
|
||||
<Compile Include="Overrides\MatchType.cs" />
|
||||
<Compile Include="Overrides\OverrideParser.cs" />
|
||||
<Compile Include="UI\IconExtraction.cs" />
|
||||
<Compile Include="UI\ISDConnectionExtensions.cs" />
|
||||
<Compile Include="Native.cs" />
|
||||
@ -91,7 +95,7 @@
|
||||
<Content Include="Images\**\*.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="PropertyInspector\**\*.js;PropertyInspector\**\*.css">
|
||||
<Content Include="PropertyInspector\**\*.js;PropertyInspector\**\*.css;PropertyInspector\assets\*.*">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="PropertyInspector\PluginActionPI.html">
|
||||
|
4
src/FocusVolumeControl/InternalsVisibleTo.cs
Normal file
@ -0,0 +1,4 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("FocusVolumeControl.UnitTests")]
|
||||
[assembly: InternalsVisibleTo("SoundBrowser")]
|
10
src/FocusVolumeControl/Overrides/MatchType.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace FocusVolumeControl.Overrides
|
||||
{
|
||||
public enum MatchType
|
||||
{
|
||||
Equal,
|
||||
StartsWith,
|
||||
EndsWith,
|
||||
Regex,
|
||||
}
|
||||
}
|
15
src/FocusVolumeControl/Overrides/Override.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FocusVolumeControl.Overrides
|
||||
{
|
||||
public class Override
|
||||
{
|
||||
public MatchType MatchType { get; set; }
|
||||
public string WindowQuery { get; set; }
|
||||
public string AudioProcessName { get; set; }
|
||||
}
|
||||
}
|
78
src/FocusVolumeControl/Overrides/OverrideParser.cs
Normal file
@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FocusVolumeControl.Overrides
|
||||
{
|
||||
internal class OverrideParser
|
||||
{
|
||||
public static List<Override> Parse(string raw)
|
||||
{
|
||||
var overrides = new List<Override>();
|
||||
|
||||
if (raw == null)
|
||||
{
|
||||
return overrides;
|
||||
}
|
||||
|
||||
var lines = raw.Split(new[] { "\r\n", "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
|
||||
Override tmp = null;
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (string.IsNullOrEmpty(line) || line.StartsWith("//"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var split = line.Split(':');
|
||||
if (split.Length > 1)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(tmp?.WindowQuery) && !string.IsNullOrEmpty(tmp?.AudioProcessName))
|
||||
{
|
||||
overrides.Add(tmp);
|
||||
}
|
||||
tmp = new Override();
|
||||
|
||||
if (string.Equals(split[0], "eq", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
tmp.MatchType = MatchType.Equal;
|
||||
}
|
||||
else if (string.Equals(split[0], "start", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
tmp.MatchType = MatchType.StartsWith;
|
||||
}
|
||||
else if (string.Equals(split[0], "end", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
tmp.MatchType = MatchType.EndsWith;
|
||||
}
|
||||
else if (string.Equals(split[0], "regex", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
tmp.MatchType = MatchType.Regex;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
tmp.WindowQuery = split[1].Trim();
|
||||
}
|
||||
else if (tmp != null)
|
||||
{
|
||||
tmp.AudioProcessName = split[0].Trim();
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(tmp?.WindowQuery) && !string.IsNullOrEmpty(tmp?.AudioProcessName))
|
||||
{
|
||||
overrides.Add(tmp);
|
||||
}
|
||||
|
||||
|
||||
return overrides;
|
||||
}
|
||||
}
|
||||
}
|
@ -6,8 +6,8 @@
|
||||
<meta name=apple-mobile-web-app-capable content=yes>
|
||||
<meta name=apple-mobile-web-app-status-bar-style content=black>
|
||||
<title>FocusVolumeControl Settings</title>
|
||||
<link rel="stylesheet" href="./lib/sdpi.css">
|
||||
<link rel="sytlesheet" href="./lib/rangeTooltip.css">
|
||||
<link rel="stylesheet" href="./css/sdpi.css">
|
||||
<link rel="sytlesheet" href="./css/rangeTooltip.css">
|
||||
<script src="lib/sdtools.common.js"></script>
|
||||
<script src="lib/rangeTooltip.js"></script>
|
||||
</head>
|
||||
@ -16,13 +16,20 @@
|
||||
|
||||
<div class="sdpi-item">
|
||||
<div class="sdpi-item-label">Fallback</div>
|
||||
<select class="sdpi-item-value sdProperty" id="fallbackBehavior" oninput="setSettings()">
|
||||
<select class="sdpi-item-value select sdProperty" id="fallbackBehavior" oninput="setSettings()">
|
||||
<option value="0">System Sounds</option>
|
||||
<option value="1">Previous App</option>
|
||||
<option value="2">Default Output Device Volume</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<details>
|
||||
<summary>Fallback Details</summary>
|
||||
<p>If you look at windows volume mixer, you will see that not all applications can have their volume controlled. The fallback behavior controls what happens when you are in an application that doesn't show up in the volume mixer</p>
|
||||
<p>* System Sounds - Switch to system sounds. This will control windows sound effects such as when an error sound plays. If you're in an application that is making beeping sounds, this will often allow you to control those sounds while leaving things like your music/videos alone</p>
|
||||
<p>* Previous App - Use the last app that had a volume control. This can result in the stream deck not changing after you have quit an application.</p>
|
||||
<p>* Default Output Device Volume - Switch to the main volume control for the default output device. This will change the volume of the default output device. This is usually volume for all applications, unless you override the output device for specific applications.</p>
|
||||
</details>
|
||||
|
||||
<div type="range" class="sdpi-item sdShowTooltip">
|
||||
<div class="sdpi-item-label">Step Size</div>
|
||||
@ -32,15 +39,30 @@
|
||||
<span class="clickable" value="1">10</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sdpi-info-label hidden" style="top: -1000;" value="">Tooltip</div>
|
||||
<div class="sdpi-info-label hidden" style="z-index: 999;" value="">Tooltip</div>
|
||||
|
||||
<div type="textarea" class="sdpi-item">
|
||||
<div class="sdpi-item-label">Overrides</div>
|
||||
<span class="sdpi-item-value" textarea>
|
||||
<textarea type="textarea" class="sdProperty" id="overrides" oninput="setSettings()"></textarea>
|
||||
<label for="overrides">window title => volume process</label>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<details>
|
||||
<p>If you look at windows volume mixer, you will see that not all applications can have their volume controlled. The fallback behavior controls what happens when you are in an application that doesn't show up in the volume mixer</p>
|
||||
<p>* System Sounds - Switch to system sounds. This will control windows sound effects such as when an error sound plays. If you're in an application that is making beeping sounds, this will often allow you to control those sounds while leaving things like your music/videos alone</p>
|
||||
<p>* Previous App - Use the last app that had a volume control. This can result in the stream deck not changing after you have quit an application.</p>
|
||||
<p>* Default Output Device Volume - Switch to the main volume control for the default output device. This will change the volume of the default output device. This is usually volume for all applications, unless you override the output device for specific applications.</p>
|
||||
<summary>Override Details</summary>
|
||||
<p>Some games use anti-cheat software that interferes with the ability to know what application is running, and pair it to the appropriate audio process.</p>
|
||||
<p>There is nothing I can do about this.</p>
|
||||
<p>In order to work around this, the overrides mechanism is there for you to manually map an application window to an audio process.</p>
|
||||
<p>They synax for this is</p>
|
||||
<p>[matching]: Window Title <br />Audio Process</p>
|
||||
<p>Blank lines can be used for spacing</p>
|
||||
<p>[matching] can be eq, start or end. Which will perform an exact match, starts with, or ends with respectively.</p>
|
||||
<p>Example:<br />eq: Task Manager<br />Discord</p>
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="6" viewBox="0 0 12 6">
|
||||
<polygon fill="#8E8E92" fill-rule="evenodd" points="5 4 9 0 10 1 5 6 0 1 1 0"/>
|
||||
</svg>
|
After Width: | Height: | Size: 173 B |
@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="7" height="12" viewBox="0 0 7 12">
|
||||
<g fill="#8E8E92" transform="translate(4, 5) rotate(-90) translate(-4, -5) translate(-2, 2)">
|
||||
<polygon id="Path" points="5 4 9 0 10 1 5 6 0 1 1 0"></polygon>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 268 B |
BIN
src/FocusVolumeControl/PropertyInspector/assets/check.png
Normal file
After Width: | Height: | Size: 234 B |
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="10" viewBox="0 0 12 10">
|
||||
<polygon fill="#FFF" points="7.2 7.5 7.2 -1.3 8.7 -1.3 8.6 9.1 2.7 8.7 2.7 7.2" transform="rotate(37 5.718 3.896)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 212 B |
@ -0,0 +1,25 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path fill="#9C9C9C" fill-rule="nonzero"
|
||||
d="M1,5 L1,14 L14,14 L14,5 L1,5 Z M0,1 L15,1 L15,15 L0,15 L0,1 Z M14,4 L14,2 L1,2 L1,4 L14,4 Z"/>
|
||||
<rect width="1" height="1" x="2" fill="#9C9C9C" fill-rule="nonzero"/>
|
||||
<rect width="1" height="1" x="12" fill="#9C9C9C" fill-rule="nonzero"/>
|
||||
<g transform="translate(3 7)">
|
||||
<rect width="1" height="1" x="2" fill="#9C9C9C"/>
|
||||
<rect width="1" height="1" fill="#666"/>
|
||||
<rect width="1" height="1" x="4" fill="#9C9C9C"/>
|
||||
<rect width="1" height="1" x="6" fill="#9C9C9C"/>
|
||||
<rect width="1" height="1" x="8" fill="#9C9C9C"/>
|
||||
<rect width="1" height="1" y="2" fill="#9C9C9C"/>
|
||||
<rect width="1" height="1" x="2" y="2" fill="#9C9C9C"/>
|
||||
<rect width="1" height="1" x="4" y="2" fill="#9C9C9C"/>
|
||||
<rect width="1" height="1" x="6" y="2" fill="#9C9C9C"/>
|
||||
<rect width="1" height="1" x="8" y="2" fill="#9C9C9C"/>
|
||||
<rect width="1" height="1" y="4" fill="#9C9C9C"/>
|
||||
<rect width="1" height="1" x="2" y="4" fill="#9C9C9C"/>
|
||||
<rect width="1" height="1" x="4" y="4" fill="#9C9C9C"/>
|
||||
<rect width="1" height="1" x="6" y="4" fill="#9C9C9C"/>
|
||||
<rect width="1" height="1" x="8" y="4" fill="#666"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="#9C9C9C">
|
||||
<path d="M15,15 L1.77635684e-15,15 L1.77635684e-15,1 L15,1 L15,15 Z M5,7 L5,8 L6,8 L6,7 L5,7 Z M3,7 L3,8 L4,8 L4,7 L3,7 Z M7,7 L7,8 L8,8 L8,7 L7,7 Z M9,7 L9,8 L10,8 L10,7 L9,7 Z M11,7 L11,8 L12,8 L12,7 L11,7 Z M3,9 L3,10 L4,10 L4,9 L3,9 Z M5,9 L5,10 L6,10 L6,9 L5,9 Z M7,9 L7,10 L8,10 L8,9 L7,9 Z M9,9 L9,10 L10,10 L10,9 L9,9 Z M11,9 L11,10 L12,10 L12,9 L11,9 Z M3,11 L3,12 L4,12 L4,11 L3,11 Z M5,11 L5,12 L6,12 L6,11 L5,11 Z M7,11 L7,12 L8,12 L8,11 L7,11 Z M9,11 L9,12 L10,12 L10,11 L9,11 Z M11,11 L11,12 L12,12 L12,11 L11,11 Z M14,4 L14,2 L1,2 L1,4 L14,4 Z"/>
|
||||
<rect width="1" height="1" x="2"/>
|
||||
<rect width="1" height="1" x="12"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 780 B |
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="6" height="6" viewBox="0 0 6 6">
|
||||
<circle cx="3" cy="3" r="3" fill="#FFF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 133 B |
3
src/FocusVolumeControl/PropertyInspector/assets/tick.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1" height="8" viewBox="0 0 1 8">
|
||||
<rect id="Rectangle" width="1" height="6" x="0" y="1" fill="#555"/>
|
||||
</svg>
|
After Width: | Height: | Size: 159 B |
@ -1,65 +1,3 @@
|
||||
.linkspan {
|
||||
cursor: pointer;
|
||||
color: #7397d2;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.titleAlignedSmall {
|
||||
font-size: 9pt;
|
||||
padding-left: 33px !important;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: 9pt !important;
|
||||
}
|
||||
|
||||
.leftMargin10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.leftMargin0 {
|
||||
margin-left: 0px !important;
|
||||
}
|
||||
|
||||
.leftPadding3 {
|
||||
padding-left: 3px !important;
|
||||
}
|
||||
|
||||
.leftPadding0 {
|
||||
padding-left: 0px !important;
|
||||
}
|
||||
|
||||
.bright {
|
||||
color: #d8d8d8;
|
||||
}
|
||||
|
||||
.iconLeft {
|
||||
background-position: 0px 4px !important;
|
||||
}
|
||||
|
||||
.summaryIconPadding {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.subMenu {
|
||||
border-left: 1px dotted gray;
|
||||
padding-left: 15px;
|
||||
max-width: 96%;
|
||||
background-color: #323232;
|
||||
}
|
||||
|
||||
:root {
|
||||
--sdpi-bgcolor: #2D2D2D;
|
||||
--sdpi-background: #3D3D3D;
|
||||
--sdpi-color: #d8d8d8;
|
||||
--sdpi-bordercolor: #3a3a3a;
|
||||
--sdpi-buttonbordercolor: #969696;
|
||||
--sdpi-borderradius: 0px;
|
||||
--sdpi-width: 224px;
|
||||
--sdpi-fontweight: 600;
|
||||
--sdpi-letterspacing: -0.25pt;
|
||||
}
|
||||
|
||||
html {
|
||||
--sdpi-bgcolor: #2D2D2D;
|
||||
--sdpi-background: #3D3D3D;
|
||||
@ -70,6 +8,16 @@ html {
|
||||
--sdpi-width: 224px;
|
||||
--sdpi-fontweight: 600;
|
||||
--sdpi-letterspacing: -0.25pt;
|
||||
--sdpi-tab-color: #969696;
|
||||
--sdpi-tab-left-margin: 1px;
|
||||
--sdpi-tab-top-offset: 1px;
|
||||
--sdpi-tab-selected-color: #333333;
|
||||
--sdpi-tab-selected-top-offset: 0px;
|
||||
--sdpi-tab-font-size: 9pt;
|
||||
--sdpi-tab-container-left-offset: 5px;
|
||||
--sdpi-tab-padding-horizontal: 12px;
|
||||
--sdpi-tab-padding-vertical: 5px;
|
||||
--sdpi-linecolor: #454545;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
@ -119,6 +67,14 @@ hr2,
|
||||
margin: 8px 0px;
|
||||
}
|
||||
|
||||
|
||||
h1 {
|
||||
font-size: 1.3em;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.sdpi-heading::before,
|
||||
.sdpi-heading::after {
|
||||
content: "";
|
||||
@ -225,6 +181,9 @@ progress {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
|
||||
/* TABS */
|
||||
|
||||
.tabs {
|
||||
/**
|
||||
* Setting display to flex makes this container lay
|
||||
@ -232,19 +191,91 @@ progress {
|
||||
* as in the above "Stepper input" example.
|
||||
*/
|
||||
display: flex;
|
||||
border-bottom: 1px solid #D7DBDD;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.0);
|
||||
flex-wrap: nowrap;
|
||||
white-space: nowrap;
|
||||
overflow-x: auto;
|
||||
text-transform: capitalize;
|
||||
background-color: transparent;
|
||||
margin-left: var(--sdpi-tab-container-left-offset);
|
||||
}
|
||||
|
||||
.tabs::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tabs::-webkit-scrollbar-track {
|
||||
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
||||
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.tabs::-webkit-scrollbar-thumb {
|
||||
background-color: #444;
|
||||
outline: 1px solid #444;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.tab-separator {
|
||||
margin-left: 100px;
|
||||
max-width: 234px;
|
||||
margin-bottom: 20px;
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
.tab {
|
||||
cursor: pointer;
|
||||
padding: 5px 30px;
|
||||
color: #16a2d7;
|
||||
font-size: 9pt;
|
||||
border-bottom: 2px solid transparent;
|
||||
padding: var(--sdpi-tab-padding-vertical) var(--sdpi-tab-padding-horizontal);
|
||||
color: var(--sdpi-tab-color);
|
||||
font-size: var(--sdpi-tab-font-size);
|
||||
font-weight: var(--title-font-weight);
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
margin: 0px;
|
||||
margin-top: var(--sdpi-tab-top-offset);
|
||||
margin-left: var(--sdpi-tab-left-margin);
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-bottom: 1px solid var(--sdpi-linecolor);
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.tab.is-tab-selected {
|
||||
border-bottom-color: #4ebbe4;
|
||||
.tab:first-child {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.tab-container {
|
||||
margin-top: -14px;
|
||||
}
|
||||
|
||||
.tab-container > hr {
|
||||
margin-left: 100px;
|
||||
max-width: 234px;
|
||||
}
|
||||
|
||||
.tabs + hr {
|
||||
margin-left: 0px;
|
||||
max-width: 234px;
|
||||
margin-top: -6px;
|
||||
}
|
||||
|
||||
.tab.selected {
|
||||
color: white;
|
||||
background-color: var(--sdpi-tab-selected-color);
|
||||
border-bottom: 2px solid var(--sdpi-tab-selected-color);
|
||||
margin-top: var(--sdpi-tab-selected-top-offset);
|
||||
}
|
||||
|
||||
.sdpi-item.tabgroup {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.istab {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
margin-bottom: 20px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
select {
|
||||
@ -306,18 +337,30 @@ option {
|
||||
.sdpi-wrapper {
|
||||
overflow-x: hidden;
|
||||
height: 100%;
|
||||
margin-right: 1px; /* ensure scroller thumb is not clipped */
|
||||
}
|
||||
|
||||
.sdpi-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
min-height: 32px;
|
||||
align-items: center;
|
||||
min-height: 30px;
|
||||
align-items: first baseline;
|
||||
margin-top: 2px;
|
||||
max-width: 344px;
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
|
||||
.sdpi-item[type="textarea"],
|
||||
.sdpi-item[type="color"],
|
||||
.sdpi-item[type="canvas"],
|
||||
.sdpi-item .aligncenter {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sdpi-item[type="color"] > .sdpi-item-label {
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.sdpi-item:first-child {
|
||||
margin-top: -1px;
|
||||
}
|
||||
@ -412,7 +455,7 @@ table > caption {
|
||||
padding-right: 5px;
|
||||
font-weight: 600;
|
||||
-webkit-user-select: none;
|
||||
line-height: 24px;
|
||||
line-height: normal;
|
||||
margin-left: -1px;
|
||||
}
|
||||
|
||||
@ -486,8 +529,8 @@ ol.sdpi-item-value,
|
||||
margin-left: 5px;
|
||||
margin-right: 12px;
|
||||
padding: 4px !important;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* display: flex;
|
||||
flex-direction: column; */
|
||||
}
|
||||
|
||||
.two-items li {
|
||||
@ -632,6 +675,16 @@ summary {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sdpi-item.details {
|
||||
align-items: first baseline;
|
||||
}
|
||||
|
||||
/* needs Chromium update 2023
|
||||
.sdpi-item:has(>details) {
|
||||
align-items: first baseline;
|
||||
}
|
||||
*/
|
||||
|
||||
details * {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
@ -654,21 +707,21 @@ details.message {
|
||||
}
|
||||
|
||||
details.message > summary:first-of-type {
|
||||
/*line-height: 48px;*/
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
details.message h1 {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
details:not(.pointer) > summary {
|
||||
/* details:not(.pointer)>summary {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
details > summary::-webkit-details-marker
|
||||
details > summary::-webkit-details-marker,
|
||||
.message > summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
} */
|
||||
|
||||
.info20,
|
||||
.question,
|
||||
@ -702,7 +755,6 @@ details > summary::-webkit-details-marker
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,18 C5.581722,18 2,14.418278 2,10 C2,5.581722 5.581722,2 10,2 C14.418278,2 18,5.581722 18,10 C18,14.418278 14.418278,18 10,18 Z M6.77783203,7.65332031 C6.77783203,7.84798274 6.85929281,8.02888914 7.0222168,8.19604492 C7.18514079,8.36320071 7.38508996,8.44677734 7.62207031,8.44677734 C8.02409055,8.44677734 8.29703704,8.20768468 8.44091797,7.72949219 C8.59326248,7.27245865 8.77945854,6.92651485 8.99951172,6.69165039 C9.2195649,6.45678594 9.56233491,6.33935547 10.027832,6.33935547 C10.4256205,6.33935547 10.7006836,6.37695313 11.0021973,6.68847656 C11.652832,7.53271484 10.942627,8.472229 10.3750916,9.1321106 C9.80755615,9.79199219 8.29492188,11.9897461 10.027832,12.1347656 C10.4498423,12.1700818 10.7027991,11.9147157 10.7832031,11.4746094 C11.0021973,9.59857178 13.1254883,8.82415771 13.1254883,7.53271484 C13.1254883,7.07568131 12.9974785,6.65250846 12.7414551,6.26318359 C12.4854317,5.87385873 12.1225609,5.56600048 11.652832,5.33959961 C11.1831031,5.11319874 10.6414419,5 10.027832,5 C9.36767248,5 8.79004154,5.13541531 8.29492187,5.40625 C7.79980221,5.67708469 7.42317837,6.01879677 7.16503906,6.43139648 C6.90689975,6.8439962 6.77783203,7.25130007 6.77783203,7.65332031 Z M10.0099668,15 C10.2713191,15 10.5016601,14.9108147 10.7009967,14.7324415 C10.9003332,14.5540682 11,14.3088087 11,13.9966555 C11,13.7157177 10.9047629,13.4793767 10.7142857,13.2876254 C10.5238086,13.0958742 10.2890379,13 10.0099668,13 C9.72646591,13 9.48726565,13.0958742 9.2923588,13.2876254 C9.09745196,13.4793767 9,13.7157177 9,13.9966555 C9,14.313268 9.10077419,14.5596424 9.30232558,14.735786 C9.50387698,14.9119295 9.73975502,15 10.0099668,15 Z'/%3E%3C/svg%3E%0A");
|
||||
}
|
||||
|
||||
|
||||
.sdpi-more-info {
|
||||
position: fixed;
|
||||
left: 0px;
|
||||
@ -731,7 +783,6 @@ details > summary::-webkit-details-marker
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
|
||||
.sdpi-bottom-bar {
|
||||
display: flex;
|
||||
align-self: right;
|
||||
@ -759,7 +810,6 @@ details a {
|
||||
padding-right: 28px;
|
||||
}
|
||||
|
||||
|
||||
input:not([type="range"]),
|
||||
textarea {
|
||||
-webkit-appearance: none;
|
||||
@ -827,7 +877,6 @@ input[type="checkbox"] {
|
||||
margin-top: -2px;
|
||||
min-width: 8px;
|
||||
text-align: right;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
@ -840,6 +889,10 @@ input[type="checkbox"] {
|
||||
|
||||
span + input[type="range"] {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
span + .range-container > input[type="range"],
|
||||
span + input[type="range"] {
|
||||
max-width: 168px;
|
||||
}
|
||||
|
||||
@ -1028,7 +1081,6 @@ textarea {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
.card-carousel-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -1091,7 +1143,8 @@ textarea {
|
||||
margin: 0 5px;
|
||||
cursor: pointer;
|
||||
/* box-shadow: 0 4px 15px 0 rgba(40, 44, 53, 0.06), 0 2px 2px 0 rgba(40, 44, 53, 0.08); */
|
||||
background-color: #fff;
|
||||
/* background-color: #fff; */
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
z-index: 3;
|
||||
}
|
||||
@ -1114,6 +1167,7 @@ textarea {
|
||||
|
||||
.card-carousel-cards .card-carousel--card img:hover {
|
||||
opacity: 0.5;
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
.card-carousel-cards .card-carousel--card--footer {
|
||||
@ -1141,44 +1195,8 @@ textarea {
|
||||
color: #666a73;
|
||||
}
|
||||
|
||||
|
||||
h1 {
|
||||
font-size: 1.3em;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
background: url(../assets/elg_calendar_inv.svg) no-repeat left center;
|
||||
padding-right: 1em;
|
||||
padding-left: 25px;
|
||||
background-position: 4px 0px;
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit-fields-wrapper {
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit-text {
|
||||
padding: 0 0.3em;
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit-month-field {
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit-day-field {
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit-year-field {
|
||||
}
|
||||
|
||||
::-webkit-inner-spin-button {
|
||||
/* display: none; */
|
||||
}
|
||||
|
||||
::-webkit-calendar-picker-indicator {
|
||||
background: transparent;
|
||||
background: url(../assets/elg_calendar_inv.svg) no-repeat center;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
@ -1186,53 +1204,30 @@ h1 {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
input[type="text"]::-webkit-calendar-picker-indicator {
|
||||
background: transparent;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
input[type="date"] {
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
display: -webkit-inline-flex;
|
||||
font-family: monospace;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
-webkit-padding-start: 1px;
|
||||
}
|
||||
|
||||
input::-webkit-datetime-edit {
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
-webkit-user-modify: read-only !important;
|
||||
user-modify: read-only !important;
|
||||
display: inline-block;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
padding: 4px;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
}
|
||||
|
||||
/*
|
||||
input::-webkit-datetime-edit-fields-wrapper {
|
||||
-webkit-user-modify: read-only !important;
|
||||
display: inline-block;
|
||||
padding: 1px 0;
|
||||
white-space: pre;
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
input[type="date"] {
|
||||
background-color: red;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type="date"]::-webkit-clear-button {
|
||||
font-size: 18px;
|
||||
height: 30px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
input[type="date"]::-webkit-inner-spin-button {
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
input[type="date"]::-webkit-calendar-picker-indicator {
|
||||
font-size: 15px;
|
||||
} */
|
||||
|
||||
input[type="file"] {
|
||||
opacity: 0;
|
||||
display: none;
|
||||
@ -1308,7 +1303,6 @@ input:required:valid {
|
||||
background-color: var(--sdpi-background);
|
||||
}
|
||||
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
@ -1330,23 +1324,72 @@ a {
|
||||
color: #7397d2;
|
||||
}
|
||||
|
||||
.testcontainer {
|
||||
display: flex;
|
||||
background-color: #0000ff20;
|
||||
max-width: 400px;
|
||||
height: 200px;
|
||||
align-content: space-evenly;
|
||||
input[type="week"] {
|
||||
-webkit-appearance: auto !important;
|
||||
appearance: auto !important;
|
||||
}
|
||||
|
||||
input[type=range] {
|
||||
-webkit-appearance: none;
|
||||
/* background-color: green; */
|
||||
input[type="month"] + datalist,
|
||||
input[type="day"] + datalist,
|
||||
input[type="week"] + datalist,
|
||||
input[type=text] + datalist {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
-webkit-appearance: auto;
|
||||
appearance: auto;
|
||||
height: 6px;
|
||||
margin-top: 12px;
|
||||
z-index: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
input[type="range"]::-webkit-slider-runnable-track {
|
||||
border: 0px solid transparent;
|
||||
}
|
||||
|
||||
.sdpi-item[type="range"] .sdpi-item-value.datalist {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
datalist {
|
||||
--sdpi-datalist-margin: 7px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 0px;
|
||||
padding-top: 0px;
|
||||
font-size: 12px;
|
||||
margin-left: var(--sdpi-datalist-margin);
|
||||
width: calc(100% - calc(var(--sdpi-datalist-margin) * 2.5));
|
||||
}
|
||||
|
||||
datalist > option {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: end;
|
||||
/* background-image: url(../assets/tick.svg); */
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1' height='8' viewBox='0 0 1 8'%3E%3Crect width='1' height='6' x='0' y='1' fill='%23555'/%3E%3C/svg%3E%0A");
|
||||
padding: 0;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
color: #9A9A99;
|
||||
width: 1px;
|
||||
height: 30px;
|
||||
z-index: 1;
|
||||
margin-top: -6px;
|
||||
background-position: top 6px right 5px;
|
||||
background-repeat: repeat no-repeat; /* fallback */
|
||||
background-repeat-y: no-repeat;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
[role="spinbutton"] {
|
||||
-webkit-appearance: auto;
|
||||
appearance: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
@ -1595,7 +1638,8 @@ input[type="range"].colortemperature::-webkit-slider-runnable-track {
|
||||
background-image: linear-gradient(to right, #94d0ec, #ffb165);
|
||||
}
|
||||
|
||||
input[type="range"].colorbrightness::-webkit-slider-runnable-track {
|
||||
input[type="range"].colorbrightness.greyscale::-webkit-slider-runnable-track,
|
||||
input[type="range"].colorbrightness.grayscale::-webkit-slider-runnable-track {
|
||||
background-color: #efefef;
|
||||
background-image: linear-gradient(to right, black, rgba(0, 0, 0, 0));
|
||||
}
|
||||
@ -1648,3 +1692,54 @@ select {
|
||||
-webkit-appearance: media-slider;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/*--------- context menu ----------*/
|
||||
|
||||
.context-menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
padding: 12px 0;
|
||||
width: 120px;
|
||||
background-color: #3D3D3D;
|
||||
border: solid 1px #dfdfdf;
|
||||
box-shadow: 1px 1px 2px #cfcfcf;
|
||||
}
|
||||
|
||||
.context-menu--active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.context-menu__items {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.context-menu__item {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
background-color: #3D3D3D !important;
|
||||
}
|
||||
|
||||
.context-menu__item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.context-menu__link {
|
||||
display: block;
|
||||
padding: 4px 12px;
|
||||
color: #ffff;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.context-menu__link:hover {
|
||||
color: #fff;
|
||||
background-color: #0066aa;
|
||||
}
|
||||
|
||||
.context-menu_message {
|
||||
cursor: default;
|
||||
}
|
@ -29,6 +29,7 @@ function calcRangeLabel(elem) {
|
||||
return tooltipValue + outputType;
|
||||
}
|
||||
|
||||
/*
|
||||
function setElementLabel(elem, str) {
|
||||
// Try to set this for the rangeLabel class, if it exists
|
||||
let label = elem.querySelector('.rangeLabel');
|
||||
@ -39,6 +40,7 @@ function setElementLabel(elem, str) {
|
||||
console.log('setElementLabel ERROR! No .rangeLabel found', elem);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
function setRangeTooltips() {
|
||||
console.log("Loading setRangeTooltips");
|
||||
@ -68,7 +70,7 @@ function setRangeTooltips() {
|
||||
tooltip.style.top = (rangeRect.top - 32) + 'px';
|
||||
}
|
||||
|
||||
setElementLabel(elem, labelStr)
|
||||
//setElementLabel(elem, labelStr)
|
||||
};
|
||||
|
||||
rangeSelector.addEventListener(
|
||||
@ -100,7 +102,7 @@ function setRangeTooltips() {
|
||||
console.log('rangeTooltip settingsUpdated called');
|
||||
window.setTimeout(function () {
|
||||
let str = calcRangeLabel(rangeSelector);
|
||||
setElementLabel(elem, str);
|
||||
//setElementLabel(elem, str);
|
||||
}, 500);
|
||||
},
|
||||
false
|
||||
@ -112,7 +114,7 @@ function setRangeTooltips() {
|
||||
console.log('rangeTooltip websocketCreate called');
|
||||
window.setTimeout(function () {
|
||||
let str = calcRangeLabel(rangeSelector);
|
||||
setElementLabel(elem, str);
|
||||
//setElementLabel(elem, str);
|
||||
}, 500);
|
||||
},
|
||||
false
|
||||
|
@ -7,6 +7,8 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace FocusVolumeControl.UI
|
||||
{
|
||||
internal class JavaIconExtractor
|
||||
@ -94,3 +96,5 @@ namespace FocusVolumeControl.UI
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#nullable restore
|
||||
|
@ -8,6 +8,8 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Threading;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace FocusVolumeControl
|
||||
{
|
||||
internal class WindowChangedEventLoop
|
||||
@ -16,10 +18,10 @@ namespace FocusVolumeControl
|
||||
public static WindowChangedEventLoop Instance => _lazy.Value;
|
||||
|
||||
readonly Thread _thread;
|
||||
Dispatcher _dispatcher;
|
||||
Dispatcher? _dispatcher;
|
||||
|
||||
IntPtr _foregroundWindowChangedEvent;
|
||||
Native.WinEventDelegate _delegate;
|
||||
Native.WinEventDelegate? _delegate;
|
||||
|
||||
private WindowChangedEventLoop()
|
||||
{
|
||||
@ -37,7 +39,7 @@ namespace FocusVolumeControl
|
||||
_thread.Start();
|
||||
}
|
||||
|
||||
public event Action WindowChanged;
|
||||
public event Action? WindowChanged;
|
||||
|
||||
CancellationTokenSource? _cancellationTokenSource = null;
|
||||
|
||||
@ -64,3 +66,5 @@ namespace FocusVolumeControl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#nullable restore
|
||||
|
@ -33,7 +33,7 @@
|
||||
"Name": "Focused Application Volume",
|
||||
"Description": "Control the volume of the focused application",
|
||||
"URL": "https://github.com/dlprows/FocusVolumeControl",
|
||||
"Version": "1.2.0",
|
||||
"Version": "1.3.0",
|
||||
"CodePath": "FocusVolumeControl",
|
||||
"Category": "Volume Control [dlprows]",
|
||||
"Icon": "Images/pluginIcon",
|
||||
|