Compare commits

...

6 commits

Author SHA1 Message Date
DHR60
0788b1a752
Merge 4301415b4c into a452bbe140 2025-10-04 20:17:13 +08:00
2dust
a452bbe140 Fix
Some checks are pending
release Linux / build (Release) (push) Waiting to run
release macOS / build (Release) (push) Waiting to run
release Windows desktop (Avalonia UI) / build (Release) (push) Waiting to run
release Windows / build (Release) (push) Waiting to run
https://github.com/2dust/v2rayN/issues/8061
2025-10-04 19:54:15 +08:00
DHR60
185c5e4bfb
Fix (#8057) 2025-10-04 16:17:39 +08:00
2dust
bbe64aa970 Remove AutoCompleteBox
https://github.com/2dust/v2rayN/pull/8067
2025-10-04 16:16:32 +08:00
DHR60
513662d89a
Use editable ComboBox instead of AutoCompleteBox (#8067)
Some checks are pending
release Linux / build (Release) (push) Waiting to run
release macOS / build (Release) (push) Waiting to run
release Windows desktop (Avalonia UI) / build (Release) (push) Waiting to run
release Windows / build (Release) (push) Waiting to run
* Update Avalonia

* Use editable ComboBox instead of AutoCompleteBox
2025-10-04 15:18:37 +08:00
DHR60
4301415b4c Add rule type selection to routing rules 2025-09-23 17:59:18 +08:00
34 changed files with 260 additions and 249 deletions

View file

@ -6,10 +6,10 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.3.0" /> <PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.3.0" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.6" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.7" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.6" /> <PackageVersion Include="Avalonia.Desktop" Version="11.3.7" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.6" /> <PackageVersion Include="Avalonia.Diagnostics" Version="11.3.7" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.6" /> <PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.7" />
<PackageVersion Include="CliWrap" Version="3.9.0" /> <PackageVersion Include="CliWrap" Version="3.9.0" />
<PackageVersion Include="Downloader" Version="4.0.3" /> <PackageVersion Include="Downloader" Version="4.0.3" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" /> <PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" />
@ -19,9 +19,9 @@
<PackageVersion Include="ReactiveUI" Version="20.4.1" /> <PackageVersion Include="ReactiveUI" Version="20.4.1" />
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" /> <PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageVersion Include="ReactiveUI.WPF" Version="20.4.1" /> <PackageVersion Include="ReactiveUI.WPF" Version="20.4.1" />
<PackageVersion Include="Semi.Avalonia" Version="11.2.1.10" /> <PackageVersion Include="Semi.Avalonia" Version="11.3.7" />
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" /> <PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.10" /> <PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7" />
<PackageVersion Include="NLog" Version="6.0.4" /> <PackageVersion Include="NLog" Version="6.0.4" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" /> <PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="TaskScheduler" Version="2.12.2" /> <PackageVersion Include="TaskScheduler" Version="2.12.2" />

View file

@ -87,6 +87,8 @@ public class Global
public const string SingboxFinalResolverTag = "final_resolver"; public const string SingboxFinalResolverTag = "final_resolver";
public const string SingboxHostsDNSTag = "hosts_dns"; public const string SingboxHostsDNSTag = "hosts_dns";
public const string SingboxFakeDNSTag = "fake_dns"; public const string SingboxFakeDNSTag = "fake_dns";
public const string RoutingRuleType = "Routing";
public const string DNSRuleType = "DNS";
public static readonly List<string> IEProxyProtocols = public static readonly List<string> IEProxyProtocols =
[ [
@ -479,6 +481,12 @@ public class Global
"tcp,udp" "tcp,udp"
]; ];
public static readonly List<string> RuleTypes =
[
RoutingRuleType,
DNSRuleType
];
public static readonly List<string> destOverrideProtocols = public static readonly List<string> destOverrideProtocols =
[ [
"http", "http",

View file

@ -155,61 +155,60 @@ public class BaseFmt
protected static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item) protected static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item)
{ {
item.Flow = query["flow"] ?? ""; item.Flow = GetQueryValue(query, "flow");
item.StreamSecurity = query["security"] ?? ""; item.StreamSecurity = GetQueryValue(query, "security");
item.Sni = query["sni"] ?? ""; item.Sni = GetQueryValue(query, "sni");
item.Alpn = Utils.UrlDecode(query["alpn"] ?? ""); item.Alpn = GetQueryDecoded(query, "alpn");
item.Fingerprint = Utils.UrlDecode(query["fp"] ?? ""); item.Fingerprint = GetQueryDecoded(query, "fp");
item.PublicKey = Utils.UrlDecode(query["pbk"] ?? ""); item.PublicKey = GetQueryDecoded(query, "pbk");
item.ShortId = Utils.UrlDecode(query["sid"] ?? ""); item.ShortId = GetQueryDecoded(query, "sid");
item.SpiderX = Utils.UrlDecode(query["spx"] ?? ""); item.SpiderX = GetQueryDecoded(query, "spx");
item.Mldsa65Verify = Utils.UrlDecode(query["pqv"] ?? ""); item.Mldsa65Verify = GetQueryDecoded(query, "pqv");
item.AllowInsecure = (query["allowInsecure"] ?? "") == "1" ? "true" : ""; item.AllowInsecure = new[] { "allowInsecure", "allow_insecure", "insecure" }.Any(k => (query[k] ?? "") == "1") ? "true" : "";
item.Network = query["type"] ?? nameof(ETransport.tcp); item.Network = GetQueryValue(query, "type", nameof(ETransport.tcp));
switch (item.Network) switch (item.Network)
{ {
case nameof(ETransport.tcp): case nameof(ETransport.tcp):
item.HeaderType = query["headerType"] ?? Global.None; item.HeaderType = GetQueryValue(query, "headerType", Global.None);
item.RequestHost = Utils.UrlDecode(query["host"] ?? ""); item.RequestHost = GetQueryDecoded(query, "host");
break; break;
case nameof(ETransport.kcp): case nameof(ETransport.kcp):
item.HeaderType = query["headerType"] ?? Global.None; item.HeaderType = GetQueryValue(query, "headerType", Global.None);
item.Path = Utils.UrlDecode(query["seed"] ?? ""); item.Path = GetQueryDecoded(query, "seed");
break; break;
case nameof(ETransport.ws): case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade): case nameof(ETransport.httpupgrade):
item.RequestHost = Utils.UrlDecode(query["host"] ?? ""); item.RequestHost = GetQueryDecoded(query, "host");
item.Path = Utils.UrlDecode(query["path"] ?? "/"); item.Path = GetQueryDecoded(query, "path", "/");
break; break;
case nameof(ETransport.xhttp): case nameof(ETransport.xhttp):
item.RequestHost = Utils.UrlDecode(query["host"] ?? ""); item.RequestHost = GetQueryDecoded(query, "host");
item.Path = Utils.UrlDecode(query["path"] ?? "/"); item.Path = GetQueryDecoded(query, "path", "/");
item.HeaderType = Utils.UrlDecode(query["mode"] ?? ""); item.HeaderType = GetQueryDecoded(query, "mode");
item.Extra = Utils.UrlDecode(query["extra"] ?? ""); item.Extra = GetQueryDecoded(query, "extra");
break; break;
case nameof(ETransport.http): case nameof(ETransport.http):
case nameof(ETransport.h2): case nameof(ETransport.h2):
item.Network = nameof(ETransport.h2); item.Network = nameof(ETransport.h2);
item.RequestHost = Utils.UrlDecode(query["host"] ?? ""); item.RequestHost = GetQueryDecoded(query, "host");
item.Path = Utils.UrlDecode(query["path"] ?? "/"); item.Path = GetQueryDecoded(query, "path", "/");
break; break;
case nameof(ETransport.quic): case nameof(ETransport.quic):
item.HeaderType = query["headerType"] ?? Global.None; item.HeaderType = GetQueryValue(query, "headerType", Global.None);
item.RequestHost = query["quicSecurity"] ?? Global.None; item.RequestHost = GetQueryValue(query, "quicSecurity", Global.None);
item.Path = Utils.UrlDecode(query["key"] ?? ""); item.Path = GetQueryDecoded(query, "key");
break; break;
case nameof(ETransport.grpc): case nameof(ETransport.grpc):
item.RequestHost = Utils.UrlDecode(query["authority"] ?? ""); item.RequestHost = GetQueryDecoded(query, "authority");
item.Path = Utils.UrlDecode(query["serviceName"] ?? ""); item.Path = GetQueryDecoded(query, "serviceName");
item.HeaderType = Utils.UrlDecode(query["mode"] ?? Global.GrpcGunMode); item.HeaderType = GetQueryDecoded(query, "mode", Global.GrpcGunMode);
break; break;
default: default:
@ -239,4 +238,14 @@ public class BaseFmt
var url = $"{Utils.UrlEncode(userInfo)}@{GetIpv6(address)}:{port}"; var url = $"{Utils.UrlEncode(userInfo)}@{GetIpv6(address)}:{port}";
return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}"; return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}";
} }
protected static string GetQueryValue(NameValueCollection query, string key, string defaultValue = "")
{
return query[key] ?? defaultValue;
}
protected static string GetQueryDecoded(NameValueCollection query, string key, string defaultValue = "")
{
return Utils.UrlDecode(GetQueryValue(query, key, defaultValue));
}
} }

View file

@ -21,10 +21,10 @@ public class Hysteria2Fmt : BaseFmt
var query = Utils.ParseQueryString(url.Query); var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item); ResolveStdTransport(query, ref item);
item.Path = Utils.UrlDecode(query["obfs-password"] ?? ""); item.Path = GetQueryDecoded(query, "obfs-password");
item.AllowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false"; item.AllowInsecure = GetQueryValue(query, "insecure") == "1" ? "true" : "false";
item.Ports = Utils.UrlDecode(query["mport"] ?? ""); item.Ports = GetQueryDecoded(query, "mport");
return item; return item;
} }

View file

@ -30,7 +30,7 @@ public class TuicFmt : BaseFmt
var query = Utils.ParseQueryString(url.Query); var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item); ResolveStdTransport(query, ref item);
item.HeaderType = query["congestion_control"] ?? ""; item.HeaderType = GetQueryValue(query, "congestion_control");
return item; return item;
} }

View file

@ -24,8 +24,8 @@ public class VLESSFmt : BaseFmt
item.Id = Utils.UrlDecode(url.UserInfo); item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query); var query = Utils.ParseQueryString(url.Query);
item.Security = query["encryption"] ?? Global.None; item.Security = GetQueryValue(query, "encryption", Global.None);
item.StreamSecurity = query["security"] ?? ""; item.StreamSecurity = GetQueryValue(query, "security");
_ = ResolveStdTransport(query, ref item); _ = ResolveStdTransport(query, ref item);
return item; return item;

View file

@ -24,10 +24,10 @@ public class WireguardFmt : BaseFmt
var query = Utils.ParseQueryString(url.Query); var query = Utils.ParseQueryString(url.Query);
item.PublicKey = Utils.UrlDecode(query["publickey"] ?? ""); item.PublicKey = GetQueryDecoded(query, "publickey");
item.Path = Utils.UrlDecode(query["reserved"] ?? ""); item.Path = GetQueryDecoded(query, "reserved");
item.RequestHost = Utils.UrlDecode(query["address"] ?? ""); item.RequestHost = GetQueryDecoded(query, "address");
item.ShortId = Utils.UrlDecode(query["mtu"] ?? ""); item.ShortId = GetQueryDecoded(query, "mtu");
return item; return item;
} }

View file

@ -15,4 +15,5 @@ public class RulesItem
public List<string>? Process { get; set; } public List<string>? Process { get; set; }
public bool Enabled { get; set; } = true; public bool Enabled { get; set; } = true;
public string? Remarks { get; set; } public string? Remarks { get; set; }
public List<string>? RuleTypes { get; set; }
} }

View file

@ -2932,16 +2932,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 The sing-box DoH resolution server can be overwritten 的本地化字符串。 /// 查找类似 Resolve DNS server domains, requires IP 的本地化字符串。
/// </summary>
public static string TbSBDoHOverride {
get {
return ResourceManager.GetString("TbSBDoHOverride", resourceCulture);
}
}
/// <summary>
/// 查找类似 Fallback DNS Resolution, Require IP 的本地化字符串。
/// </summary> /// </summary>
public static string TbSBFallbackDNSResolve { public static string TbSBFallbackDNSResolve {
get { get {

View file

@ -1429,7 +1429,7 @@
<value>Bootstrap DNS (sing-box)</value> <value>Bootstrap DNS (sing-box)</value>
</data> </data>
<data name="TbSBFallbackDNSResolve" xml:space="preserve"> <data name="TbSBFallbackDNSResolve" xml:space="preserve">
<value>Fallback DNS Resolution, Require IP</value> <value>Resolve DNS server domains, requires IP</value>
</data> </data>
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve"> <data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
<value>xray Freedom Resolution Strategy</value> <value>xray Freedom Resolution Strategy</value>
@ -1443,9 +1443,6 @@
<data name="TbAddCommonDNSHosts" xml:space="preserve"> <data name="TbAddCommonDNSHosts" xml:space="preserve">
<value>Add Common DNS Hosts</value> <value>Add Common DNS Hosts</value>
</data> </data>
<data name="TbSBDoHOverride" xml:space="preserve">
<value>The sing-box DoH resolution server can be overwritten</value>
</data>
<data name="TbFakeIP" xml:space="preserve"> <data name="TbFakeIP" xml:space="preserve">
<value>FakeIP</value> <value>FakeIP</value>
</data> </data>

View file

@ -1429,7 +1429,7 @@
<value>Bootstrap DNS (sing-box)</value> <value>Bootstrap DNS (sing-box)</value>
</data> </data>
<data name="TbSBFallbackDNSResolve" xml:space="preserve"> <data name="TbSBFallbackDNSResolve" xml:space="preserve">
<value>Fallback DNS Resolution, Require IP</value> <value>Resolve DNS server domains, requires IP</value>
</data> </data>
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve"> <data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
<value>xray Freedom Resolution Strategy</value> <value>xray Freedom Resolution Strategy</value>
@ -1443,9 +1443,6 @@
<data name="TbAddCommonDNSHosts" xml:space="preserve"> <data name="TbAddCommonDNSHosts" xml:space="preserve">
<value>Add Common DNS Hosts</value> <value>Add Common DNS Hosts</value>
</data> </data>
<data name="TbSBDoHOverride" xml:space="preserve">
<value>The sing-box DoH resolution server can be overwritten</value>
</data>
<data name="TbFakeIP" xml:space="preserve"> <data name="TbFakeIP" xml:space="preserve">
<value>FakeIP</value> <value>FakeIP</value>
</data> </data>

View file

@ -1429,7 +1429,7 @@
<value>Bootstrap DNS (sing-box)</value> <value>Bootstrap DNS (sing-box)</value>
</data> </data>
<data name="TbSBFallbackDNSResolve" xml:space="preserve"> <data name="TbSBFallbackDNSResolve" xml:space="preserve">
<value>Fallback DNS Resolution, Require IP</value> <value>Resolve DNS server domains, requires IP</value>
</data> </data>
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve"> <data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
<value>xray Freedom Resolution Strategy</value> <value>xray Freedom Resolution Strategy</value>
@ -1443,9 +1443,6 @@
<data name="TbAddCommonDNSHosts" xml:space="preserve"> <data name="TbAddCommonDNSHosts" xml:space="preserve">
<value>Add Common DNS Hosts</value> <value>Add Common DNS Hosts</value>
</data> </data>
<data name="TbSBDoHOverride" xml:space="preserve">
<value>The sing-box DoH resolution server can be overwritten</value>
</data>
<data name="TbFakeIP" xml:space="preserve"> <data name="TbFakeIP" xml:space="preserve">
<value>FakeIP</value> <value>FakeIP</value>
</data> </data>

View file

@ -1429,7 +1429,7 @@
<value>Bootstrap DNS (sing-box)</value> <value>Bootstrap DNS (sing-box)</value>
</data> </data>
<data name="TbSBFallbackDNSResolve" xml:space="preserve"> <data name="TbSBFallbackDNSResolve" xml:space="preserve">
<value>Fallback DNS Resolution, Require IP</value> <value>Resolve DNS server domains, requires IP</value>
</data> </data>
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve"> <data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
<value>Стратегия резолвинга Freedom (Xray)</value> <value>Стратегия резолвинга Freedom (Xray)</value>
@ -1443,9 +1443,6 @@
<data name="TbAddCommonDNSHosts" xml:space="preserve"> <data name="TbAddCommonDNSHosts" xml:space="preserve">
<value>Добавить стандартные записи hosts (DNS)</value> <value>Добавить стандартные записи hosts (DNS)</value>
</data> </data>
<data name="TbSBDoHOverride" xml:space="preserve">
<value>Сервер DoH-резолвера sing-box можно переопределить</value>
</data>
<data name="TbFakeIP" xml:space="preserve"> <data name="TbFakeIP" xml:space="preserve">
<value>FakeIP</value> <value>FakeIP</value>
</data> </data>

View file

@ -1426,7 +1426,7 @@
<value>Bootstrap DNS (sing-box)</value> <value>Bootstrap DNS (sing-box)</value>
</data> </data>
<data name="TbSBFallbackDNSResolve" xml:space="preserve"> <data name="TbSBFallbackDNSResolve" xml:space="preserve">
<value>回退 DNS 解析,需指定为 IP</value> <value>解析 DNS 服务器域名,需指定为 IP</value>
</data> </data>
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve"> <data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
<value>xray freedom 解析策略</value> <value>xray freedom 解析策略</value>
@ -1440,9 +1440,6 @@
<data name="TbAddCommonDNSHosts" xml:space="preserve"> <data name="TbAddCommonDNSHosts" xml:space="preserve">
<value>添加常用 DNS Hosts</value> <value>添加常用 DNS Hosts</value>
</data> </data>
<data name="TbSBDoHOverride" xml:space="preserve">
<value>开启后可覆盖 sing-box DoH 解析服务器</value>
</data>
<data name="TbFakeIP" xml:space="preserve"> <data name="TbFakeIP" xml:space="preserve">
<value>FakeIP</value> <value>FakeIP</value>
</data> </data>

View file

@ -1426,7 +1426,7 @@
<value>Bootstrap DNS (sing-box)</value> <value>Bootstrap DNS (sing-box)</value>
</data> </data>
<data name="TbSBFallbackDNSResolve" xml:space="preserve"> <data name="TbSBFallbackDNSResolve" xml:space="preserve">
<value>Fallback DNS Resolution, Require IP</value> <value>Resolve DNS server domains, requires IP</value>
</data> </data>
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve"> <data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
<value>xray Freedom Resolution Strategy</value> <value>xray Freedom Resolution Strategy</value>
@ -1440,9 +1440,6 @@
<data name="TbAddCommonDNSHosts" xml:space="preserve"> <data name="TbAddCommonDNSHosts" xml:space="preserve">
<value>Add Common DNS Hosts</value> <value>Add Common DNS Hosts</value>
</data> </data>
<data name="TbSBDoHOverride" xml:space="preserve">
<value>The sing-box DoH resolution server can be overwritten</value>
</data>
<data name="TbFakeIP" xml:space="preserve"> <data name="TbFakeIP" xml:space="preserve">
<value>FakeIP</value> <value>FakeIP</value>
</data> </data>

View file

@ -253,6 +253,11 @@ public partial class CoreConfigSingboxService
continue; continue;
} }
if ((item.RuleTypes?.Count ?? 0) > 0 && !item.RuleTypes.Contains(Global.DNSRuleType))
{
continue;
}
var rule = new Rule4Sbox(); var rule = new Rule4Sbox();
var validDomains = item.Domain.Count(it => ParseV2Domain(it, rule)); var validDomains = item.Domain.Count(it => ParseV2Domain(it, rule));
if (validDomains <= 0) if (validDomains <= 0)

View file

@ -136,13 +136,21 @@ public partial class CoreConfigSingboxService
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet); var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
foreach (var item1 in rules ?? []) foreach (var item1 in rules ?? [])
{ {
if (item1.Enabled) if (!item1.Enabled)
{ {
await GenRoutingUserRule(item1, singboxConfig); continue;
if (item1.Ip != null && item1.Ip.Count > 0) }
{
ipRules.Add(item1); if ((item1.RuleTypes?.Count ?? 0) > 0 && !item1.RuleTypes.Contains(Global.RoutingRuleType))
} {
continue;
}
await GenRoutingUserRule(item1, singboxConfig);
if (item1.Ip?.Count > 0)
{
ipRules.Add(item1);
} }
} }
} }

View file

@ -186,7 +186,7 @@ public partial class CoreConfigV2rayService(Config config)
ret.Success = true; ret.Success = true;
ret.Data = await ApplyFullConfigTemplate(v2rayConfig, true); ret.Data = await ApplyFullConfigTemplate(v2rayConfig);
return ret; return ret;
} }
catch (Exception ex) catch (Exception ex)

View file

@ -4,7 +4,7 @@ namespace ServiceLib.Services.CoreConfig;
public partial class CoreConfigV2rayService public partial class CoreConfigV2rayService
{ {
private async Task<string> ApplyFullConfigTemplate(V2rayConfig v2rayConfig, bool handleBalancerAndRules = false) private async Task<string> ApplyFullConfigTemplate(V2rayConfig v2rayConfig)
{ {
var fullConfigTemplate = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.Xray); var fullConfigTemplate = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.Xray);
if (fullConfigTemplate == null || !fullConfigTemplate.Enabled || fullConfigTemplate.Config.IsNullOrEmpty()) if (fullConfigTemplate == null || !fullConfigTemplate.Enabled || fullConfigTemplate.Config.IsNullOrEmpty())
@ -19,7 +19,7 @@ public partial class CoreConfigV2rayService
} }
// Handle balancer and rules modifications (for multiple load scenarios) // Handle balancer and rules modifications (for multiple load scenarios)
if (handleBalancerAndRules && v2rayConfig.routing?.balancers?.Count > 0) if (v2rayConfig.routing?.balancers?.Count > 0)
{ {
var balancer = v2rayConfig.routing.balancers.First(); var balancer = v2rayConfig.routing.balancers.First();
@ -60,6 +60,34 @@ public partial class CoreConfigV2rayService
} }
} }
if (v2rayConfig.observatory != null)
{
if (fullConfigTemplateNode["observatory"] == null)
{
fullConfigTemplateNode["observatory"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.observatory));
}
else
{
var subjectSelector = v2rayConfig.observatory.subjectSelector;
subjectSelector.AddRange(fullConfigTemplateNode["observatory"]?["subjectSelector"]?.AsArray()?.Select(x => x?.GetValue<string>()) ?? []);
fullConfigTemplateNode["observatory"]["subjectSelector"] = JsonNode.Parse(JsonUtils.Serialize(subjectSelector.Distinct().ToList()));
}
}
if (v2rayConfig.burstObservatory != null)
{
if (fullConfigTemplateNode["burstObservatory"] == null)
{
fullConfigTemplateNode["burstObservatory"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.burstObservatory));
}
else
{
var subjectSelector = v2rayConfig.burstObservatory.subjectSelector;
subjectSelector.AddRange(fullConfigTemplateNode["burstObservatory"]?["subjectSelector"]?.AsArray()?.Select(x => x?.GetValue<string>()) ?? []);
fullConfigTemplateNode["burstObservatory"]["subjectSelector"] = JsonNode.Parse(JsonUtils.Serialize(subjectSelector.Distinct().ToList()));
}
}
// Handle outbounds - append instead of override // Handle outbounds - append instead of override
var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray(); var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray();
foreach (var outbound in v2rayConfig.outbounds) foreach (var outbound in v2rayConfig.outbounds)

View file

@ -142,6 +142,11 @@ public partial class CoreConfigV2rayService
continue; continue;
} }
if ((item.RuleTypes?.Count ?? 0) > 0 && !item.RuleTypes.Contains(Global.DNSRuleType))
{
continue;
}
foreach (var domain in item.Domain) foreach (var domain in item.Domain)
{ {
if (domain.StartsWith('#')) if (domain.StartsWith('#'))

View file

@ -20,11 +20,18 @@ public partial class CoreConfigV2rayService
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet); var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
foreach (var item in rules) foreach (var item in rules)
{ {
if (item.Enabled) if (!item.Enabled)
{ {
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item)); continue;
await GenRoutingUserRule(item2, v2rayConfig);
} }
if ((item.RuleTypes?.Count ?? 0) > 0 && !item.RuleTypes.Contains(Global.RoutingRuleType))
{
continue;
}
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item));
await GenRoutingUserRule(item2, v2rayConfig);
} }
} }
} }

View file

@ -21,6 +21,8 @@ public class RoutingRuleDetailsViewModel : MyReactiveObject
[Reactive] [Reactive]
public string Process { get; set; } public string Process { get; set; }
public IList<string> Types { get; set; }
[Reactive] [Reactive]
public bool AutoSort { get; set; } public bool AutoSort { get; set; }
@ -51,6 +53,11 @@ public class RoutingRuleDetailsViewModel : MyReactiveObject
Domain = Utils.List2String(SelectedSource.Domain, true); Domain = Utils.List2String(SelectedSource.Domain, true);
IP = Utils.List2String(SelectedSource.Ip, true); IP = Utils.List2String(SelectedSource.Ip, true);
Process = Utils.List2String(SelectedSource.Process, true); Process = Utils.List2String(SelectedSource.Process, true);
Types = SelectedSource.RuleTypes?.ToList();
if (Types == null || Types.Count == 0)
{
Types = Global.RuleTypes;
}
} }
private async Task SaveRulesAsync() private async Task SaveRulesAsync()
@ -73,6 +80,7 @@ public class RoutingRuleDetailsViewModel : MyReactiveObject
} }
SelectedSource.Protocol = ProtocolItems?.ToList(); SelectedSource.Protocol = ProtocolItems?.ToList();
SelectedSource.InboundTag = InboundTagItems?.ToList(); SelectedSource.InboundTag = InboundTagItems?.ToList();
SelectedSource.RuleTypes = Types?.ToList();
var hasRule = SelectedSource.Domain?.Count > 0 var hasRule = SelectedSource.Domain?.Count > 0
|| SelectedSource.Ip?.Count > 0 || SelectedSource.Ip?.Count > 0

View file

@ -20,7 +20,6 @@
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="Assets/GlobalResources.axaml" /> <ResourceInclude Source="Assets/GlobalResources.axaml" />
<ResourceInclude Source="Controls/AutoCompleteBox.axaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
</ResourceDictionary> </ResourceDictionary>
</Application.Resources> </Application.Resources>

View file

@ -1,6 +1,7 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Platform; using Avalonia.Platform;
@ -18,7 +19,7 @@ internal class AvaUtils
return null; return null;
} }
return await clipboard.GetTextAsync(); return await clipboard.TryGetTextAsync();
} }
catch catch
{ {
@ -33,9 +34,7 @@ internal class AvaUtils
var clipboard = TopLevel.GetTopLevel(visual)?.Clipboard; var clipboard = TopLevel.GetTopLevel(visual)?.Clipboard;
if (clipboard == null) if (clipboard == null)
return; return;
var dataObject = new DataObject(); await clipboard.SetTextAsync(strData);
dataObject.Set(DataFormats.Text, strData);
await clipboard.SetDataObjectAsync(dataObject);
} }
catch catch
{ {

View file

@ -1,48 +0,0 @@
<ResourceDictionary
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:v2rayN.Desktop.Controls">
<!-- Add Resources Here -->
<ControlTheme x:Key="{x:Type controls:AutoCompleteBox}" TargetType="controls:AutoCompleteBox">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="MinHeight" Value="{DynamicResource AutoCompleteBoxDefaultHeight}" />
<Setter Property="MaxDropDownHeight" Value="{DynamicResource AutoCompleteMaxDropdownHeight}" />
<Setter Property="Template">
<ControlTemplate TargetType="AutoCompleteBox">
<Panel>
<TextBox
Name="PART_TextBox"
MinHeight="{TemplateBinding MinHeight}"
VerticalAlignment="Stretch"
DataValidationErrors.Errors="{TemplateBinding (DataValidationErrors.Errors)}"
InnerLeftContent="{TemplateBinding InnerLeftContent}"
InnerRightContent="{TemplateBinding InnerRightContent}"
Watermark="{TemplateBinding Watermark}" />
<Popup
Name="PART_Popup"
MaxHeight="{TemplateBinding MaxDropDownHeight}"
IsLightDismissEnabled="True"
PlacementTarget="{TemplateBinding}">
<Border
MinWidth="{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}"
Margin="{DynamicResource AutoCompleteBoxPopupMargin}"
Padding="{DynamicResource AutoCompleteBoxPopupPadding}"
HorizontalAlignment="Stretch"
Background="{DynamicResource AutoCompleteBoxPopupBackground}"
BorderBrush="{DynamicResource AutoCompleteBoxPopupBorderBrush}"
BorderThickness="{DynamicResource AutoCompleteBoxPopupBorderThickness}"
BoxShadow="{DynamicResource AutoCompleteBoxPopupBoxShadow}"
CornerRadius="{DynamicResource AutoCompleteBoxPopupCornerRadius}">
<ListBox
Name="PART_SelectingItemsControl"
Foreground="{TemplateBinding Foreground}"
ItemTemplate="{TemplateBinding ItemTemplate}"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto" />
</Border>
</Popup>
</Panel>
</ControlTemplate>
</Setter>
</ControlTheme>
</ResourceDictionary>

View file

@ -1,40 +0,0 @@
using Avalonia.Input;
using Avalonia.Interactivity;
namespace v2rayN.Desktop.Controls;
public class AutoCompleteBox : Avalonia.Controls.AutoCompleteBox
{
static AutoCompleteBox()
{
MinimumPrefixLengthProperty.OverrideDefaultValue<AutoCompleteBox>(0);
}
public AutoCompleteBox()
{
AddHandler(PointerPressedEvent, OnBoxPointerPressed, RoutingStrategies.Tunnel);
}
private void OnBoxPointerPressed(object? sender, PointerPressedEventArgs e)
{
if (Equals(sender, this) && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
{
SetCurrentValue(IsDropDownOpenProperty, true);
}
}
protected override void OnGotFocus(GotFocusEventArgs e)
{
base.OnGotFocus(e);
if (IsDropDownOpen)
{
return;
}
SetCurrentValue(IsDropDownOpenProperty, true);
}
public void Clear()
{
SetCurrentValue(SelectedItemProperty, null);
}
}

View file

@ -2,7 +2,6 @@
x:Class="v2rayN.Desktop.Views.DNSSettingWindow" x:Class="v2rayN.Desktop.Views.DNSSettingWindow"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ctrls="clr-namespace:v2rayN.Desktop.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
@ -56,13 +55,13 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbDomesticDNS}" /> Text="{x:Static resx:ResUI.TbDomesticDNS}" />
<ctrls:AutoCompleteBox <ComboBox
x:Name="cmbDirectDNS" x:Name="cmbDirectDNS"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="300" Width="300"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
Text="{Binding DirectDNS, Mode=TwoWay}" /> IsEditable="True" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
@ -70,13 +69,13 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRemoteDNS}" /> Text="{x:Static resx:ResUI.TbRemoteDNS}" />
<ctrls:AutoCompleteBox <ComboBox
x:Name="cmbRemoteDNS" x:Name="cmbRemoteDNS"
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Width="300" Width="300"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
Text="{Binding RemoteDNS, Mode=TwoWay}" /> IsEditable="True" />
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
@ -84,13 +83,13 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSBOutboundsResolverDNS}" /> Text="{x:Static resx:ResUI.TbSBOutboundsResolverDNS}" />
<ctrls:AutoCompleteBox <ComboBox
x:Name="cmbSBResolverDNS" x:Name="cmbSBResolverDNS"
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
Width="300" Width="300"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
Text="{Binding SingboxOutboundsResolveDNS, Mode=TwoWay}" /> IsEditable="True" />
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
Grid.Column="2" Grid.Column="2"
@ -105,13 +104,13 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSBBootstrapDNS}" /> Text="{x:Static resx:ResUI.TbSBBootstrapDNS}" />
<ctrls:AutoCompleteBox <ComboBox
x:Name="cmbSBFinalResolverDNS" x:Name="cmbSBFinalResolverDNS"
Grid.Row="4" Grid.Row="4"
Grid.Column="1" Grid.Column="1"
Width="300" Width="300"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
Text="{Binding SingboxFinalResolveDNS, Mode=TwoWay}" /> IsEditable="True" />
<TextBlock <TextBlock
Grid.Row="4" Grid.Row="4"
Grid.Column="2" Grid.Column="2"
@ -174,13 +173,6 @@
Grid.Column="1" Grid.Column="1"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" /> HorizontalAlignment="Left" />
<TextBlock
Grid.Row="8"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSBDoHOverride}"
TextWrapping="Wrap" />
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</TabItem> </TabItem>
@ -260,13 +252,13 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbValidateDirectExpectedIPs}" /> Text="{x:Static resx:ResUI.TbValidateDirectExpectedIPs}" />
<ctrls:AutoCompleteBox <ComboBox
x:Name="cmbDirectExpectedIPs" x:Name="cmbDirectExpectedIPs"
Grid.Row="4" Grid.Row="4"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
Text="{Binding DirectExpectedIPs, Mode=TwoWay}" /> IsEditable="True" />
<TextBlock <TextBlock
Grid.Row="4" Grid.Row="4"
Grid.Column="2" Grid.Column="2"
@ -363,11 +355,11 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsDomainDNSAddress}" /> Text="{x:Static resx:ResUI.TbSettingsDomainDNSAddress}" />
<ctrls:AutoCompleteBox <ComboBox
x:Name="cmbdomainDNSAddressCompatible" x:Name="cmbdomainDNSAddressCompatible"
Width="150" Width="150"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
Text="{Binding DomainDNSAddressCompatible, Mode=TwoWay}" /> IsEditable="True" />
</StackPanel> </StackPanel>
</WrapPanel> </WrapPanel>
@ -435,11 +427,11 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsDomainDNSAddress}" /> Text="{x:Static resx:ResUI.TbSettingsDomainDNSAddress}" />
<ctrls:AutoCompleteBox <ComboBox
x:Name="cmbdomainDNSAddress2Compatible" x:Name="cmbdomainDNSAddress2Compatible"
Width="150" Width="150"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
Text="{Binding DomainDNSAddress2Compatible, Mode=TwoWay}" /> IsEditable="True" />
</StackPanel> </StackPanel>
</WrapPanel> </WrapPanel>

View file

@ -40,15 +40,15 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
this.Bind(ViewModel, vm => vm.AddCommonHosts, v => v.togAddCommonHosts.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.AddCommonHosts, v => v.togAddCommonHosts.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.FakeIP, v => v.togFakeIP.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.FakeIP, v => v.togFakeIP.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.BlockBindingQuery, v => v.togBlockBindingQuery.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.BlockBindingQuery, v => v.togBlockBindingQuery.IsChecked).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.Text).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.SingboxOutboundsResolveDNS, v => v.cmbSBResolverDNS.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SingboxOutboundsResolveDNS, v => v.cmbSBResolverDNS.Text).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.SingboxFinalResolveDNS, v => v.cmbSBFinalResolverDNS.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SingboxFinalResolveDNS, v => v.cmbSBFinalResolverDNS.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RayStrategy4Freedom, v => v.cmbRayFreedomDNSStrategy.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.RayStrategy4Freedom, v => v.cmbRayFreedomDNSStrategy.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SingboxStrategy4Direct, v => v.cmbSBDirectDNSStrategy.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SingboxStrategy4Direct, v => v.cmbSBDirectDNSStrategy.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SingboxStrategy4Proxy, v => v.cmbSBRemoteDNSStrategy.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SingboxStrategy4Proxy, v => v.cmbSBRemoteDNSStrategy.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
@ -57,11 +57,11 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
this.Bind(ViewModel, vm => vm.UseSystemHostsCompatible, v => v.togUseSystemHostsCompatible.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.UseSystemHostsCompatible, v => v.togUseSystemHostsCompatible.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DomainStrategy4FreedomCompatible, v => v.cmbdomainStrategy4FreedomCompatible.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DomainStrategy4FreedomCompatible, v => v.cmbdomainStrategy4FreedomCompatible.SelectedItem).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.DomainDNSAddressCompatible, v => v.cmbdomainDNSAddressCompatible.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DomainDNSAddressCompatible, v => v.cmbdomainDNSAddressCompatible.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.NormalDNSCompatible, v => v.txtnormalDNSCompatible.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.NormalDNSCompatible, v => v.txtnormalDNSCompatible.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DomainStrategy4Freedom2Compatible, v => v.cmbdomainStrategy4OutCompatible.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DomainStrategy4Freedom2Compatible, v => v.cmbdomainStrategy4OutCompatible.SelectedItem).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.DomainDNSAddress2Compatible, v => v.cmbdomainDNSAddress2Compatible.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DomainDNSAddress2Compatible, v => v.cmbdomainDNSAddress2Compatible.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.NormalDNS2Compatible, v => v.txtnormalDNS2Compatible.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.NormalDNS2Compatible, v => v.txtnormalDNS2Compatible.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunDNS2Compatible, v => v.txttunDNS2Compatible.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunDNS2Compatible, v => v.txttunDNS2Compatible.Text).DisposeWith(disposables);

View file

@ -2,7 +2,6 @@
x:Class="v2rayN.Desktop.Views.OptionSettingWindow" x:Class="v2rayN.Desktop.Views.OptionSettingWindow"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ctrls="clr-namespace:v2rayN.Desktop.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
@ -502,12 +501,13 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamily}" /> Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamily}" />
<ctrls:AutoCompleteBox <ComboBox
x:Name="cmbcurrentFontFamily" x:Name="cmbcurrentFontFamily"
Grid.Row="15" Grid.Row="15"
Grid.Column="1" Grid.Column="1"
Width="300" Width="300"
Margin="{StaticResource Margin4}" /> Margin="{StaticResource Margin4}"
IsEditable="True" />
<TextBlock <TextBlock
Grid.Row="15" Grid.Row="15"
Grid.Column="2" Grid.Column="2"
@ -548,12 +548,13 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsSpeedTestUrl}" /> Text="{x:Static resx:ResUI.TbSettingsSpeedTestUrl}" />
<ctrls:AutoCompleteBox <ComboBox
Name="cmbSpeedTestUrl" Name="cmbSpeedTestUrl"
Grid.Row="18" Grid.Row="18"
Grid.Column="1" Grid.Column="1"
Width="300" Width="300"
Margin="{StaticResource Margin4}" /> Margin="{StaticResource Margin4}"
IsEditable="True" />
<TextBlock <TextBlock
Grid.Row="19" Grid.Row="19"
@ -561,12 +562,13 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsSpeedPingTestUrl}" /> Text="{x:Static resx:ResUI.TbSettingsSpeedPingTestUrl}" />
<ctrls:AutoCompleteBox <ComboBox
x:Name="cmbSpeedPingTestUrl" x:Name="cmbSpeedPingTestUrl"
Grid.Row="19" Grid.Row="19"
Grid.Column="1" Grid.Column="1"
Width="300" Width="300"
Margin="{StaticResource Margin4}" /> Margin="{StaticResource Margin4}"
IsEditable="True" />
<TextBlock <TextBlock
Grid.Row="20" Grid.Row="20"
@ -587,12 +589,13 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsSubConvert}" /> Text="{x:Static resx:ResUI.TbSettingsSubConvert}" />
<ctrls:AutoCompleteBox <ComboBox
x:Name="cmbSubConvertUrl" x:Name="cmbSubConvertUrl"
Grid.Row="21" Grid.Row="21"
Grid.Column="1" Grid.Column="1"
Width="300" Width="300"
Margin="{StaticResource Margin4}" /> Margin="{StaticResource Margin4}"
IsEditable="True" />
<TextBlock <TextBlock
Grid.Row="22" Grid.Row="22"
@ -618,7 +621,8 @@
Grid.Row="23" Grid.Row="23"
Grid.Column="1" Grid.Column="1"
Width="300" Width="300"
Margin="{StaticResource Margin4}" /> Margin="{StaticResource Margin4}"
IsEditable="True" />
<TextBlock <TextBlock
Grid.Row="23" Grid.Row="23"
Grid.Column="2" Grid.Column="2"
@ -638,7 +642,8 @@
Grid.Row="24" Grid.Row="24"
Grid.Column="1" Grid.Column="1"
Width="300" Width="300"
Margin="{StaticResource Margin4}" /> Margin="{StaticResource Margin4}"
IsEditable="True" />
<TextBlock <TextBlock
Grid.Row="24" Grid.Row="24"
Grid.Column="2" Grid.Column="2"
@ -658,7 +663,8 @@
Grid.Row="25" Grid.Row="25"
Grid.Column="1" Grid.Column="1"
Width="300" Width="300"
Margin="{StaticResource Margin4}" /> Margin="{StaticResource Margin4}"
IsEditable="True" />
<TextBlock <TextBlock
Grid.Row="25" Grid.Row="25"
Grid.Column="2" Grid.Column="2"

View file

@ -2,7 +2,6 @@
x:Class="v2rayN.Desktop.Views.RoutingRuleDetailsWindow" x:Class="v2rayN.Desktop.Views.RoutingRuleDetailsWindow"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ctrls="clr-namespace:v2rayN.Desktop.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
@ -33,13 +32,25 @@
Width="300" Width="300"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" /> HorizontalAlignment="Left" />
<ToggleSwitch <StackPanel
x:Name="togEnabled"
Grid.Row="0" Grid.Row="0"
Grid.Column="2" Grid.Column="2"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" /> VerticalAlignment="Center"
Orientation="Horizontal">
<ToggleSwitch
x:Name="togEnabled"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
VerticalAlignment="Center" />
<ListBox
x:Name="clbRuleTypes"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
SelectionMode="Multiple,Toggle"
Theme="{DynamicResource CardCheckGroupListBox}" />
</StackPanel>
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
@ -47,28 +58,26 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="outboundTag" /> Text="outboundTag" />
<ctrls:AutoCompleteBox <ComboBox
Name="cmbOutboundTag" Name="cmbOutboundTag"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="300" Width="300"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
Text="{Binding SelectedSource.OutboundTag, Mode=TwoWay}" /> IsEditable="True" />
<StackPanel <StackPanel
Grid.Row="1" Grid.Row="1"
Grid.Column="2" Grid.Column="2"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
Orientation="Horizontal"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center"> VerticalAlignment="Center"
Orientation="Horizontal">
<Button <Button
x:Name="btnSelectProfile" x:Name="btnSelectProfile"
Margin="0,0,8,0" Margin="0,0,8,0"
Content="{x:Static resx:ResUI.TbSelectProfile}" Click="BtnSelectProfile_Click"
Click="BtnSelectProfile_Click" /> Content="{x:Static resx:ResUI.TbSelectProfile}" />
<TextBlock <TextBlock VerticalAlignment="Center" Text="{x:Static resx:ResUI.TbRuleOutboundTagTip}" />
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRuleOutboundTagTip}" />
</StackPanel> </StackPanel>
<TextBlock <TextBlock

View file

@ -29,6 +29,16 @@ public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsVie
clbInboundTag.ItemsSource = Global.InboundTags; clbInboundTag.ItemsSource = Global.InboundTags;
cmbNetwork.ItemsSource = Global.RuleNetworks; cmbNetwork.ItemsSource = Global.RuleNetworks;
clbRuleTypes.SelectionChanged += ClbRuleTypes_SelectionChanged;
clbRuleTypes.ItemsSource = Global.RuleTypes;
if (ViewModel.Types != null)
{
foreach (var it in ViewModel.Types)
{
clbRuleTypes.SelectedItems.Add(it);
}
}
if (!rulesItem.Id.IsNullOrEmpty()) if (!rulesItem.Id.IsNullOrEmpty())
{ {
rulesItem.Protocol?.ForEach(it => rulesItem.Protocol?.ForEach(it =>
@ -43,6 +53,7 @@ public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsVie
this.WhenActivated(disposables => this.WhenActivated(disposables =>
{ {
this.Bind(ViewModel, vm => vm.SelectedSource.OutboundTag, v => v.cmbOutboundTag.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.OutboundTag, v => v.cmbOutboundTag.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.OutboundTag, v => v.cmbOutboundTag.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Port, v => v.txtPort.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Port, v => v.txtPort.Text).DisposeWith(disposables);
@ -108,4 +119,12 @@ public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsVie
} }
} }
} }
private void ClbRuleTypes_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (ViewModel != null)
{
ViewModel.Types = clbRuleTypes.SelectedItems.Cast<string>().ToList();
}
}
} }

View file

@ -210,13 +210,6 @@
Grid.Column="1" Grid.Column="1"
Margin="{StaticResource Margin8}" Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" /> HorizontalAlignment="Left" />
<TextBlock
Grid.Row="8"
Grid.Column="3"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSBDoHOverride}" />
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</TabItem> </TabItem>

View file

@ -48,13 +48,25 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Style="{StaticResource DefTextBox}" /> Style="{StaticResource DefTextBox}" />
<ToggleButton <StackPanel
x:Name="togEnabled"
Grid.Row="0" Grid.Row="0"
Grid.Column="2" Grid.Column="2"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" /> VerticalAlignment="Center"
Orientation="Horizontal">
<ToggleButton
x:Name="togEnabled"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
VerticalAlignment="Center" />
<ListBox
x:Name="clbRuleTypes"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
FontSize="{DynamicResource StdFontSize}"
Style="{StaticResource MaterialDesignFilterChipPrimaryListBox}" />
</StackPanel>
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"

View file

@ -22,6 +22,16 @@ public partial class RoutingRuleDetailsWindow
clbInboundTag.ItemsSource = Global.InboundTags; clbInboundTag.ItemsSource = Global.InboundTags;
cmbNetwork.ItemsSource = Global.RuleNetworks; cmbNetwork.ItemsSource = Global.RuleNetworks;
clbRuleTypes.SelectionChanged += ClbRuleTypes_SelectionChanged;
clbRuleTypes.ItemsSource = Global.RuleTypes;
if (ViewModel.Types != null)
{
foreach (var it in ViewModel.Types)
{
clbRuleTypes.SelectedItems.Add(it);
}
}
if (!rulesItem.Id.IsNullOrEmpty()) if (!rulesItem.Id.IsNullOrEmpty())
{ {
rulesItem.Protocol?.ForEach(it => rulesItem.Protocol?.ForEach(it =>
@ -101,4 +111,12 @@ public partial class RoutingRuleDetailsWindow
} }
} }
} }
private void ClbRuleTypes_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
if (ViewModel != null)
{
ViewModel.Types = clbRuleTypes.SelectedItems.Cast<string>().ToList();
}
}
} }