mirror of
https://github.com/2dust/v2rayN.git
synced 2025-07-04 14:02:11 +00:00
Compare commits
No commits in common. "master" and "7.12.6" have entirely different histories.
52 changed files with 682 additions and 1153 deletions
14
.github/workflows/winget-publish.yml
vendored
14
.github/workflows/winget-publish.yml
vendored
|
@ -22,18 +22,10 @@ jobs:
|
|||
$github = Invoke-RestMethod -uri "https://api.github.com/repos/2dust/v2rayN/releases"
|
||||
|
||||
$targetRelease = $github | Where-Object -Property prerelease -match 'False' | Select -First 1
|
||||
|
||||
$x64InstallerUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'v2rayN-windows-64\.zip' | Select -ExpandProperty browser_download_url
|
||||
$arm64InstallerUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'v2rayN-windows-arm64\.zip' | Select -ExpandProperty browser_download_url
|
||||
|
||||
$installerUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'v2rayN-windows-64\.zip*' | Select -ExpandProperty browser_download_url
|
||||
|
||||
$ver = $targetRelease.tag_name
|
||||
|
||||
# getting latest wingetcreate file
|
||||
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
|
||||
|
||||
Write-Host "Updating with both x64 and arm64 installers"
|
||||
Write-Host "Version: $ver"
|
||||
Write-Host "x64 URL: $x64InstallerUrl"
|
||||
Write-Host "arm64 URL: $arm64InstallerUrl"
|
||||
|
||||
.\wingetcreate.exe update $wingetPackage -s -v $ver -u "$x64InstallerUrl|x64" "$arm64InstallerUrl|arm64" -t $gitToken
|
||||
.\wingetcreate.exe update $wingetPackage -s -v $ver -u "$installerUrl|x64" -t $gitToken
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>7.12.7</Version>
|
||||
<Version>7.12.6</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
|
100
v2rayN/ServiceLib/Common/AesUtils.cs
Normal file
100
v2rayN/ServiceLib/Common/AesUtils.cs
Normal file
|
@ -0,0 +1,100 @@
|
|||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Common;
|
||||
|
||||
public class AesUtils
|
||||
{
|
||||
private const int KeySize = 256; // AES-256
|
||||
private const int IvSize = 16; // AES block size
|
||||
private const int Iterations = 10000;
|
||||
private static readonly byte[] Salt = Encoding.ASCII.GetBytes("saltysalt".PadRight(16, ' '));
|
||||
private static readonly string DefaultPassword = Utils.GetMd5(Utils.GetHomePath() + "AesUtils");
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt
|
||||
/// </summary>
|
||||
/// <param name="text">Plain text</param>
|
||||
/// <param name="password">Password for key derivation or direct key in ASCII bytes</param>
|
||||
/// <returns>Base64 encoded cipher text with IV</returns>
|
||||
public static string Encrypt(string text, string? password = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return string.Empty;
|
||||
|
||||
var plaintext = Encoding.UTF8.GetBytes(text);
|
||||
var key = GetKey(password);
|
||||
var iv = GenerateIv();
|
||||
|
||||
using var aes = Aes.Create();
|
||||
aes.Key = key;
|
||||
aes.IV = iv;
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
ms.Write(iv, 0, iv.Length);
|
||||
|
||||
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
|
||||
{
|
||||
cs.Write(plaintext, 0, plaintext.Length);
|
||||
cs.FlushFinalBlock();
|
||||
}
|
||||
|
||||
var cipherTextWithIv = ms.ToArray();
|
||||
return Convert.ToBase64String(cipherTextWithIv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt
|
||||
/// </summary>
|
||||
/// <param name="cipherTextWithIv">Base64 encoded cipher text with IV</param>
|
||||
/// <param name="password">Password for key derivation or direct key in ASCII bytes</param>
|
||||
/// <returns>Plain text</returns>
|
||||
public static string Decrypt(string cipherTextWithIv, string? password = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(cipherTextWithIv))
|
||||
return string.Empty;
|
||||
|
||||
var cipherTextWithIvBytes = Convert.FromBase64String(cipherTextWithIv);
|
||||
var key = GetKey(password);
|
||||
|
||||
var iv = new byte[IvSize];
|
||||
Buffer.BlockCopy(cipherTextWithIvBytes, 0, iv, 0, IvSize);
|
||||
|
||||
var cipherText = new byte[cipherTextWithIvBytes.Length - IvSize];
|
||||
Buffer.BlockCopy(cipherTextWithIvBytes, IvSize, cipherText, 0, cipherText.Length);
|
||||
|
||||
using var aes = Aes.Create();
|
||||
aes.Key = key;
|
||||
aes.IV = iv;
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
|
||||
{
|
||||
cs.Write(cipherText, 0, cipherText.Length);
|
||||
cs.FlushFinalBlock();
|
||||
}
|
||||
|
||||
var plainText = ms.ToArray();
|
||||
return Encoding.UTF8.GetString(plainText);
|
||||
}
|
||||
|
||||
private static byte[] GetKey(string? password)
|
||||
{
|
||||
if (password.IsNullOrEmpty())
|
||||
{
|
||||
password = DefaultPassword;
|
||||
}
|
||||
|
||||
using var pbkdf2 = new Rfc2898DeriveBytes(password, Salt, Iterations, HashAlgorithmName.SHA256);
|
||||
return pbkdf2.GetBytes(KeySize / 8);
|
||||
}
|
||||
|
||||
private static byte[] GenerateIv()
|
||||
{
|
||||
var randomNumber = new byte[IvSize];
|
||||
|
||||
using var rng = RandomNumberGenerator.Create();
|
||||
rng.GetBytes(randomNumber);
|
||||
return randomNumber;
|
||||
}
|
||||
}
|
74
v2rayN/ServiceLib/Common/DesUtils.cs
Normal file
74
v2rayN/ServiceLib/Common/DesUtils.cs
Normal file
|
@ -0,0 +1,74 @@
|
|||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Common;
|
||||
|
||||
public class DesUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Encrypt
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// /// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public static string Encrypt(string? text, string? key = null)
|
||||
{
|
||||
if (text.IsNullOrEmpty())
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
GetKeyIv(key ?? GetDefaultKey(), out var rgbKey, out var rgbIv);
|
||||
var dsp = DES.Create();
|
||||
using var memStream = new MemoryStream();
|
||||
using var cryStream = new CryptoStream(memStream, dsp.CreateEncryptor(rgbKey, rgbIv), CryptoStreamMode.Write);
|
||||
using var sWriter = new StreamWriter(cryStream);
|
||||
sWriter.Write(text);
|
||||
sWriter.Flush();
|
||||
cryStream.FlushFinalBlock();
|
||||
memStream.Flush();
|
||||
return Convert.ToBase64String(memStream.GetBuffer(), 0, (int)memStream.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt
|
||||
/// </summary>
|
||||
/// <param name="encryptText"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public static string Decrypt(string? encryptText, string? key = null)
|
||||
{
|
||||
if (encryptText.IsNullOrEmpty())
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
GetKeyIv(key ?? GetDefaultKey(), out var rgbKey, out var rgbIv);
|
||||
var dsp = DES.Create();
|
||||
var buffer = Convert.FromBase64String(encryptText);
|
||||
|
||||
using var memStream = new MemoryStream();
|
||||
using var cryStream = new CryptoStream(memStream, dsp.CreateDecryptor(rgbKey, rgbIv), CryptoStreamMode.Write);
|
||||
cryStream.Write(buffer, 0, buffer.Length);
|
||||
cryStream.FlushFinalBlock();
|
||||
return Encoding.UTF8.GetString(memStream.ToArray());
|
||||
}
|
||||
|
||||
private static void GetKeyIv(string key, out byte[] rgbKey, out byte[] rgbIv)
|
||||
{
|
||||
if (key.IsNullOrEmpty())
|
||||
{
|
||||
throw new ArgumentNullException("The key cannot be null");
|
||||
}
|
||||
if (key.Length <= 8)
|
||||
{
|
||||
throw new ArgumentNullException("The key length cannot be less than 8 characters.");
|
||||
}
|
||||
|
||||
rgbKey = Encoding.ASCII.GetBytes(key.Substring(0, 8));
|
||||
rgbIv = Encoding.ASCII.GetBytes(key.Insert(0, "w").Substring(0, 8));
|
||||
}
|
||||
|
||||
private static string GetDefaultKey()
|
||||
{
|
||||
return Utils.GetMd5(Utils.GetHomePath() + "DesUtils");
|
||||
}
|
||||
}
|
|
@ -101,7 +101,6 @@ public class ConfigHandler
|
|||
EnableAutoAdjustMainLvColWidth = true
|
||||
};
|
||||
config.UiItem.MainColumnItem ??= new();
|
||||
config.UiItem.WindowSizeItem ??= new();
|
||||
|
||||
if (config.UiItem.CurrentLanguage.IsNullOrEmpty())
|
||||
{
|
||||
|
@ -247,7 +246,6 @@ public class ConfigHandler
|
|||
item.ShortId = profileItem.ShortId;
|
||||
item.SpiderX = profileItem.SpiderX;
|
||||
item.Extra = profileItem.Extra;
|
||||
item.MuxEnabled = profileItem.MuxEnabled;
|
||||
}
|
||||
|
||||
var ret = item.ConfigType switch
|
||||
|
@ -1981,7 +1979,7 @@ public class ConfigHandler
|
|||
items = await AppHandler.Instance.RoutingItems();
|
||||
}
|
||||
|
||||
if (!blImportAdvancedRules && items.Count > 0)
|
||||
if (!blImportAdvancedRules && items.Where(t => t.Remarks.StartsWith(ver)).ToList().Count > 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -2175,44 +2173,4 @@ public class ConfigHandler
|
|||
}
|
||||
|
||||
#endregion Regional Presets
|
||||
|
||||
#region UIItem
|
||||
|
||||
public static WindowSizeItem? GetWindowSizeItem(Config config, string typeName)
|
||||
{
|
||||
var sizeItem = config?.UiItem?.WindowSizeItem?.FirstOrDefault(t => t.TypeName == typeName);
|
||||
if (sizeItem == null || sizeItem.Width <= 0 || sizeItem.Height <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return sizeItem;
|
||||
}
|
||||
|
||||
public static int SaveWindowSizeItem(Config config, string typeName, double width, double height)
|
||||
{
|
||||
var sizeItem = config?.UiItem?.WindowSizeItem?.FirstOrDefault(t => t.TypeName == typeName);
|
||||
if (sizeItem == null)
|
||||
{
|
||||
sizeItem = new WindowSizeItem { TypeName = typeName };
|
||||
config.UiItem.WindowSizeItem.Add(sizeItem);
|
||||
}
|
||||
|
||||
sizeItem.Width = (int)width;
|
||||
sizeItem.Height = (int)height;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int SaveMainGirdHeight(Config config, double height1, double height2)
|
||||
{
|
||||
var uiItem = config.UiItem ?? new();
|
||||
|
||||
uiItem.MainGirdHeight1 = (int)(height1 + 0.1);
|
||||
uiItem.MainGirdHeight2 = (int)(height2 + 0.1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endregion UIItem
|
||||
}
|
||||
|
|
|
@ -89,8 +89,10 @@ public class UIItem
|
|||
{
|
||||
public bool EnableAutoAdjustMainLvColWidth { get; set; }
|
||||
public bool EnableUpdateSubOnlyRemarksExist { get; set; }
|
||||
public int MainGirdHeight1 { get; set; }
|
||||
public int MainGirdHeight2 { get; set; }
|
||||
public double MainWidth { get; set; }
|
||||
public double MainHeight { get; set; }
|
||||
public double MainGirdHeight1 { get; set; }
|
||||
public double MainGirdHeight2 { get; set; }
|
||||
public EGirdOrientation MainGirdOrientation { get; set; } = EGirdOrientation.Vertical;
|
||||
public string? ColorPrimaryName { get; set; }
|
||||
public string? CurrentTheme { get; set; }
|
||||
|
@ -101,10 +103,8 @@ public class UIItem
|
|||
public bool DoubleClick2Activate { get; set; }
|
||||
public bool AutoHideStartup { get; set; }
|
||||
public bool Hide2TrayWhenClose { get; set; }
|
||||
public bool ShowInTaskbar { get; set; }
|
||||
public bool MacOSShowInDock { get; set; }
|
||||
public List<ColumnItem> MainColumnItem { get; set; }
|
||||
public List<WindowSizeItem> WindowSizeItem { get; set; }
|
||||
public bool ShowInTaskbar { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
@ -245,11 +245,3 @@ public class Fragment4RayItem
|
|||
public string? Length { get; set; }
|
||||
public string? Interval { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class WindowSizeItem
|
||||
{
|
||||
public string TypeName { get; set; }
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ internal class IPAPIInfo
|
|||
{
|
||||
public string? ip { get; set; }
|
||||
public string? clientIp { get; set; }
|
||||
public string? ip_addr { get; set; }
|
||||
public string? ip_addr { get; set; }
|
||||
public string? query { get; set; }
|
||||
public string? country { get; set; }
|
||||
public string? country_name { get; set; }
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
using ReactiveUI;
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
[Serializable]
|
||||
public class ProfileItem : ReactiveObject
|
||||
public class ProfileItem
|
||||
{
|
||||
public ProfileItem()
|
||||
{
|
||||
|
@ -94,5 +93,4 @@ public class ProfileItem : ReactiveObject
|
|||
public string ShortId { get; set; }
|
||||
public string SpiderX { get; set; }
|
||||
public string Extra { get; set; }
|
||||
public bool? MuxEnabled { get; set; }
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
[Serializable]
|
||||
|
@ -7,28 +5,13 @@ public class ProfileItemModel : ProfileItem
|
|||
{
|
||||
public bool IsActive { get; set; }
|
||||
public string SubRemarks { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public int Delay { get; set; }
|
||||
|
||||
public decimal Speed { get; set; }
|
||||
public int Sort { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string DelayVal { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string SpeedVal { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string TodayUp { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string TodayDown { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string TotalUp { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string TotalDown { get; set; }
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@
|
|||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="BatchExportURLSuccessfully" xml:space="preserve">
|
||||
<value>匯出分享連結至剪貼簿成功</value>
|
||||
<value>匯出分享链接至剪貼簿成功</value>
|
||||
</data>
|
||||
<data name="CheckServerSettings" xml:space="preserve">
|
||||
<value>請先檢查設定檔設定</value>
|
||||
|
@ -295,7 +295,7 @@
|
|||
<value>成功從剪貼簿匯入 {0} 個設定檔</value>
|
||||
</data>
|
||||
<data name="SuccessfullyImportedServerViaScan" xml:space="preserve">
|
||||
<value>掃描匯入分享連結成功</value>
|
||||
<value>掃描匯入分享链接成功</value>
|
||||
</data>
|
||||
<data name="TestMeOutput" xml:space="preserve">
|
||||
<value>目前延遲: {0} ms,{1}</value>
|
||||
|
@ -481,7 +481,7 @@
|
|||
<value>語言 (需重啟)</value>
|
||||
</data>
|
||||
<data name="menuAddServerViaClipboard" xml:space="preserve">
|
||||
<value>從剪貼簿導入分享連結 (Ctrl+V)</value>
|
||||
<value>從剪貼簿導入分享鏈接 (Ctrl+V)</value>
|
||||
</data>
|
||||
<data name="menuAddServerViaScan" xml:space="preserve">
|
||||
<value>掃描螢幕上的二維碼 (Ctrl+S)</value>
|
||||
|
@ -517,7 +517,7 @@
|
|||
<value>匯出所選設定檔完整設定</value>
|
||||
</data>
|
||||
<data name="menuExport2ShareUrl" xml:space="preserve">
|
||||
<value>匯出分享連結至剪貼簿 (多選) (Ctrl+C)</value>
|
||||
<value>匯出分享链接至剪貼簿 (多選) (Ctrl+C)</value>
|
||||
</data>
|
||||
<data name="menuAddCustomServer" xml:space="preserve">
|
||||
<value>新增自訂設定設定檔</value>
|
||||
|
@ -892,7 +892,7 @@
|
|||
<value>規則詳細說明檔案</value>
|
||||
</data>
|
||||
<data name="TbDnsObjectDoc" xml:space="preserve">
|
||||
<value>支援填寫 DnsObject,JSON 格式,點擊查看說明</value>
|
||||
<value>支援填寫 DnsObject,JSON 格式,點擊查看説明</value>
|
||||
</data>
|
||||
<data name="SubUrlTips" xml:space="preserve">
|
||||
<value>普通分組此處請留空</value>
|
||||
|
@ -1003,7 +1003,7 @@
|
|||
<value>啟用硬體加速 (需重啟)</value>
|
||||
</data>
|
||||
<data name="SpeedtestingWait" xml:space="preserve">
|
||||
<value>等待測試中(按 ESC 終止)...</value>
|
||||
<value>等待测试中(按 ESC 终止)...</value>
|
||||
</data>
|
||||
<data name="TipDisplayLog" xml:space="preserve">
|
||||
<value>當有異常斷流時請關閉</value>
|
||||
|
@ -1102,10 +1102,10 @@
|
|||
<value>混淆密碼 (obfs password)</value>
|
||||
</data>
|
||||
<data name="TbRuleMatchingTips" xml:space="preserve">
|
||||
<value>(Domain 或 IP 或 行程名) 與 Port 與 Protocol 與 InboundTag => OutboundTag</value>
|
||||
<value>(Domain 或 IP 或 行程名) 与 Port 与 Protocol 与 InboundTag => OutboundTag</value>
|
||||
</data>
|
||||
<data name="TbAutoScrollToEnd" xml:space="preserve">
|
||||
<value>自動滾動到末尾</value>
|
||||
<value>自动滚动到末尾</value>
|
||||
</data>
|
||||
<data name="TbSettingsSpeedPingTestUrl" xml:space="preserve">
|
||||
<value>真連線測試位址</value>
|
||||
|
@ -1201,7 +1201,7 @@
|
|||
<value>全局</value>
|
||||
</data>
|
||||
<data name="menuModeNothing" xml:space="preserve">
|
||||
<value>隨原配置</value>
|
||||
<value>随原配置</value>
|
||||
</data>
|
||||
<data name="menuModeRule" xml:space="preserve">
|
||||
<value>規則</value>
|
||||
|
@ -1231,7 +1231,7 @@
|
|||
<value>自動調整列寬</value>
|
||||
</data>
|
||||
<data name="menuExport2ShareUrlBase64" xml:space="preserve">
|
||||
<value>匯出分享連結至剪貼簿 (多選) Base64 編碼</value>
|
||||
<value>匯出分享链接至剪貼簿 (多選) Base64 编码</value>
|
||||
</data>
|
||||
<data name="menuExport2ClientConfigClipboard" xml:space="preserve">
|
||||
<value>匯出所選設定檔完整設定至剪貼簿</value>
|
||||
|
@ -1267,7 +1267,7 @@
|
|||
<value>WebDav 伺服器位址</value>
|
||||
</data>
|
||||
<data name="LvWebDavUserName" xml:space="preserve">
|
||||
<value>WebDav 帳戶</value>
|
||||
<value>WebDav 賬戶</value>
|
||||
</data>
|
||||
<data name="LvWebDavPassword" xml:space="preserve">
|
||||
<value>WebDav 密碼</value>
|
||||
|
@ -1336,7 +1336,7 @@
|
|||
<value>系統的 sudo 密碼</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
||||
<value>輸入的密碼無法校驗,所以請確保輸入正確。如果因為輸入錯誤導致無法正常運作時,請重新啟動本應用。 密碼不會儲存,每次重啟後都需要再次輸入。</value>
|
||||
<value>輸入的密碼無法校驗,所以請確保輸入正確。如果因為輸入錯誤導致無法正常運作時,請重新啟動本應用。 密碼不會存儲,每次重啟後都需要再次輸入。</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
|
||||
<value>請先在 Tun 模式設定中設定 sudo 密碼</value>
|
||||
|
@ -1366,7 +1366,7 @@
|
|||
<value>開啟第二個本機監聽埠</value>
|
||||
</data>
|
||||
<data name="TbRoutingInboundTagTips" xml:space="preserve">
|
||||
<value>socks:本地埠,socks2:第二個本地埠,socks3:區域網路埠</value>
|
||||
<value>socks:本地端口,socks2:第二個本地端口,socks3:區域網路端口</value>
|
||||
</data>
|
||||
<data name="TbSettingsTheme" xml:space="preserve">
|
||||
<value>主題</value>
|
||||
|
@ -1387,10 +1387,10 @@
|
|||
<value>移除無效測試結果 {0} 個。</value>
|
||||
</data>
|
||||
<data name="TbPorts7" xml:space="preserve">
|
||||
<value>跳躍埠範圍</value>
|
||||
<value>跳躍端口範圍</value>
|
||||
</data>
|
||||
<data name="TbPorts7Tips" xml:space="preserve">
|
||||
<value>會覆蓋埠,多組時用逗號 (,) 隔開</value>
|
||||
<value>會覆蓋端口,多組時用逗號 (,) 隔開</value>
|
||||
</data>
|
||||
<data name="menuSetDefaultMultipleServer" xml:space="preserve">
|
||||
<value>多設定檔產生自訂配置 (多選)</value>
|
||||
|
|
|
@ -336,7 +336,7 @@ public class CoreConfigSingboxService
|
|||
await GenExperimental(singboxConfig);
|
||||
singboxConfig.outbounds.RemoveAt(0);
|
||||
|
||||
var proxyProfiles = new List<ProfileItem>();
|
||||
var tagProxy = new List<string>();
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
|
@ -370,18 +370,42 @@ public class CoreConfigSingboxService
|
|||
}
|
||||
|
||||
//outbound
|
||||
proxyProfiles.Add(item);
|
||||
var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||
await GenOutbound(item, outbound);
|
||||
outbound.tag = $"{Global.ProxyTag}-{tagProxy.Count + 1}";
|
||||
singboxConfig.outbounds.Insert(0, outbound);
|
||||
tagProxy.Add(outbound.tag);
|
||||
}
|
||||
if (proxyProfiles.Count <= 0)
|
||||
if (tagProxy.Count <= 0)
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
await GenOutboundsList(proxyProfiles, singboxConfig);
|
||||
|
||||
await GenDns(null, singboxConfig);
|
||||
await ConvertGeo2Ruleset(singboxConfig);
|
||||
|
||||
//add urltest outbound
|
||||
var outUrltest = new Outbound4Sbox
|
||||
{
|
||||
type = "urltest",
|
||||
tag = $"{Global.ProxyTag}-auto",
|
||||
outbounds = tagProxy,
|
||||
interrupt_exist_connections = false,
|
||||
};
|
||||
singboxConfig.outbounds.Insert(0, outUrltest);
|
||||
|
||||
//add selector outbound
|
||||
var outSelector = new Outbound4Sbox
|
||||
{
|
||||
type = "selector",
|
||||
tag = Global.ProxyTag,
|
||||
outbounds = JsonUtils.DeepCopy(tagProxy),
|
||||
interrupt_exist_connections = false,
|
||||
};
|
||||
outSelector.outbounds.Insert(0, outUrltest.tag);
|
||||
singboxConfig.outbounds.Insert(0, outSelector);
|
||||
|
||||
ret.Success = true;
|
||||
ret.Data = JsonUtils.Serialize(singboxConfig);
|
||||
return ret;
|
||||
|
@ -751,8 +775,7 @@ public class CoreConfigSingboxService
|
|||
{
|
||||
try
|
||||
{
|
||||
var muxEnabled = node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled;
|
||||
if (muxEnabled && _config.Mux4SboxItem.Protocol.IsNotEmpty())
|
||||
if (_config.CoreBasicItem.MuxEnabled && _config.Mux4SboxItem.Protocol.IsNotEmpty())
|
||||
{
|
||||
var mux = new Multiplex4Sbox()
|
||||
{
|
||||
|
@ -918,192 +941,37 @@ public class CoreConfigSingboxService
|
|||
|
||||
//Previous proxy
|
||||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
string? prevOutboundTag = null;
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom)
|
||||
{
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
prevOutboundTag = $"prev-{Global.ProxyTag}";
|
||||
prevOutbound.tag = prevOutboundTag;
|
||||
prevOutbound.tag = $"{Global.ProxyTag}2";
|
||||
singboxConfig.outbounds.Add(prevOutbound);
|
||||
}
|
||||
var nextOutbound = await GenChainOutbounds(subItem, outbound, prevOutboundTag);
|
||||
|
||||
if (nextOutbound is not null)
|
||||
{
|
||||
singboxConfig.outbounds.Insert(0, nextOutbound);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenOutboundsList(List<ProfileItem> nodes, SingboxConfig singboxConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get outbound template and initialize lists
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
|
||||
if (txtOutbound.IsNullOrEmpty())
|
||||
{
|
||||
return 0;
|
||||
outbound.detour = prevOutbound.tag;
|
||||
}
|
||||
|
||||
var resultOutbounds = new List<Outbound4Sbox>();
|
||||
var prevOutbounds = new List<Outbound4Sbox>(); // Separate list for prev outbounds
|
||||
var proxyTags = new List<string>(); // For selector and urltest outbounds
|
||||
|
||||
// Cache for chain proxies to avoid duplicate generation
|
||||
var nextProxyCache = new Dictionary<string, Outbound4Sbox?>();
|
||||
var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag
|
||||
int prevIndex = 0; // Index for prev outbounds
|
||||
|
||||
// Process each node
|
||||
int index = 0;
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
index++;
|
||||
|
||||
// Handle proxy chain
|
||||
string? prevTag = null;
|
||||
var currentOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||
var nextOutbound = nextProxyCache.GetValueOrDefault(node.Subid, null);
|
||||
if (nextOutbound != null)
|
||||
{
|
||||
nextOutbound = JsonUtils.DeepCopy(nextOutbound);
|
||||
}
|
||||
|
||||
var subItem = await AppHandler.Instance.GetSubItem(node.Subid);
|
||||
|
||||
// current proxy
|
||||
await GenOutbound(node, currentOutbound);
|
||||
currentOutbound.tag = $"{Global.ProxyTag}-{index}";
|
||||
proxyTags.Add(currentOutbound.tag);
|
||||
|
||||
if (!node.Subid.IsNullOrEmpty())
|
||||
{
|
||||
if (prevProxyTags.TryGetValue(node.Subid, out var value))
|
||||
{
|
||||
prevTag = value; // maybe null
|
||||
}
|
||||
else
|
||||
{
|
||||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom)
|
||||
{
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}";
|
||||
prevOutbound.tag = prevTag;
|
||||
prevOutbounds.Add(prevOutbound);
|
||||
}
|
||||
prevProxyTags[node.Subid] = prevTag;
|
||||
}
|
||||
|
||||
nextOutbound = await GenChainOutbounds(subItem, currentOutbound, prevTag, nextOutbound);
|
||||
if (!nextProxyCache.ContainsKey(node.Subid))
|
||||
{
|
||||
nextProxyCache[node.Subid] = nextOutbound;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextOutbound is not null)
|
||||
{
|
||||
resultOutbounds.Add(nextOutbound);
|
||||
}
|
||||
resultOutbounds.Add(currentOutbound);
|
||||
}
|
||||
|
||||
// Add urltest outbound (auto selection based on latency)
|
||||
if (proxyTags.Count > 0)
|
||||
{
|
||||
var outUrltest = new Outbound4Sbox
|
||||
{
|
||||
type = "urltest",
|
||||
tag = $"{Global.ProxyTag}-auto",
|
||||
outbounds = proxyTags,
|
||||
interrupt_exist_connections = false,
|
||||
};
|
||||
|
||||
// Add selector outbound (manual selection)
|
||||
var outSelector = new Outbound4Sbox
|
||||
{
|
||||
type = "selector",
|
||||
tag = Global.ProxyTag,
|
||||
outbounds = JsonUtils.DeepCopy(proxyTags),
|
||||
interrupt_exist_connections = false,
|
||||
};
|
||||
outSelector.outbounds.Insert(0, outUrltest.tag);
|
||||
|
||||
// Insert these at the beginning
|
||||
resultOutbounds.Insert(0, outUrltest);
|
||||
resultOutbounds.Insert(0, outSelector);
|
||||
}
|
||||
|
||||
// Merge results: first the selector/urltest/proxies, then other outbounds, and finally prev outbounds
|
||||
resultOutbounds.AddRange(prevOutbounds);
|
||||
resultOutbounds.AddRange(singboxConfig.outbounds);
|
||||
singboxConfig.outbounds = resultOutbounds;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a chained outbound configuration for the given subItem and outbound.
|
||||
/// The outbound's tag must be set before calling this method.
|
||||
/// Returns the next proxy's outbound configuration, which may be null if no next proxy exists.
|
||||
/// </summary>
|
||||
/// <param name="subItem">The subscription item containing proxy chain information.</param>
|
||||
/// <param name="outbound">The current outbound configuration. Its tag must be set before calling this method.</param>
|
||||
/// <param name="prevOutboundTag">The tag of the previous outbound in the chain, if any.</param>
|
||||
/// <param name="nextOutbound">The outbound for the next proxy in the chain, if already created. If null, will be created inside.</param>
|
||||
/// <returns>
|
||||
/// The outbound configuration for the next proxy in the chain, or null if no next proxy exists.
|
||||
/// </returns>
|
||||
private async Task<Outbound4Sbox?> GenChainOutbounds(SubItem subItem, Outbound4Sbox outbound, string? prevOutboundTag, Outbound4Sbox? nextOutbound = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
|
||||
|
||||
if (!prevOutboundTag.IsNullOrEmpty())
|
||||
{
|
||||
outbound.detour = prevOutboundTag;
|
||||
}
|
||||
|
||||
// Next proxy
|
||||
//Next proxy
|
||||
var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode is not null
|
||||
&& nextNode.ConfigType != EConfigType.Custom)
|
||||
{
|
||||
if (nextOutbound == null)
|
||||
{
|
||||
nextOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||
await GenOutbound(nextNode, nextOutbound);
|
||||
}
|
||||
nextOutbound.tag = outbound.tag;
|
||||
var nextOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||
await GenOutbound(nextNode, nextOutbound);
|
||||
nextOutbound.tag = Global.ProxyTag;
|
||||
singboxConfig.outbounds.Insert(0, nextOutbound);
|
||||
|
||||
outbound.tag = $"mid-{outbound.tag}";
|
||||
outbound.tag = $"{Global.ProxyTag}1";
|
||||
nextOutbound.detour = outbound.tag;
|
||||
}
|
||||
return nextOutbound;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return null;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenRouting(SingboxConfig singboxConfig)
|
||||
|
|
|
@ -113,7 +113,7 @@ public class CoreConfigV2rayService
|
|||
await GenStatistic(v2rayConfig);
|
||||
v2rayConfig.outbounds.RemoveAt(0);
|
||||
|
||||
var proxyProfiles = new List<ProfileItem>();
|
||||
var tagProxy = new List<string>();
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
|
@ -151,14 +151,17 @@ public class CoreConfigV2rayService
|
|||
}
|
||||
|
||||
//outbound
|
||||
proxyProfiles.Add(item);
|
||||
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(item, outbound);
|
||||
outbound.tag = $"{Global.ProxyTag}-{tagProxy.Count + 1}";
|
||||
v2rayConfig.outbounds.Insert(0, outbound);
|
||||
tagProxy.Add(outbound.tag);
|
||||
}
|
||||
if (proxyProfiles.Count <= 0)
|
||||
if (tagProxy.Count <= 0)
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
await GenOutboundsList(proxyProfiles, v2rayConfig);
|
||||
|
||||
//add balancers
|
||||
await GenBalancer(v2rayConfig, multipleLoad);
|
||||
|
@ -631,7 +634,7 @@ public class CoreConfigV2rayService
|
|||
{
|
||||
try
|
||||
{
|
||||
var muxEnabled = node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled;
|
||||
var muxEnabled = _config.CoreBasicItem.MuxEnabled;
|
||||
switch (node.ConfigType)
|
||||
{
|
||||
case EConfigType.VMess:
|
||||
|
@ -1318,7 +1321,6 @@ public class CoreConfigV2rayService
|
|||
|
||||
//Previous proxy
|
||||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
string? prevOutboundTag = null;
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom
|
||||
&& prevNode.ConfigType != EConfigType.Hysteria2
|
||||
|
@ -1326,168 +1328,40 @@ public class CoreConfigV2rayService
|
|||
{
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
prevOutboundTag = $"prev-{Global.ProxyTag}";
|
||||
prevOutbound.tag = prevOutboundTag;
|
||||
prevOutbound.tag = $"{Global.ProxyTag}2";
|
||||
v2rayConfig.outbounds.Add(prevOutbound);
|
||||
}
|
||||
var nextOutbound = await GenChainOutbounds(subItem, outbound, prevOutboundTag);
|
||||
|
||||
if (nextOutbound is not null)
|
||||
{
|
||||
v2rayConfig.outbounds.Insert(0, nextOutbound);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenOutboundsList(List<ProfileItem> nodes, V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get template and initialize list
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||
if (txtOutbound.IsNullOrEmpty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var resultOutbounds = new List<Outbounds4Ray>();
|
||||
var prevOutbounds = new List<Outbounds4Ray>(); // Separate list for prev outbounds and fragment
|
||||
|
||||
// Cache for chain proxies to avoid duplicate generation
|
||||
var nextProxyCache = new Dictionary<string, Outbounds4Ray?>();
|
||||
var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag
|
||||
int prevIndex = 0; // Index for prev outbounds
|
||||
|
||||
// Process nodes
|
||||
int index = 0;
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
index++;
|
||||
|
||||
// Handle proxy chain
|
||||
string? prevTag = null;
|
||||
var currentOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
var nextOutbound = nextProxyCache.GetValueOrDefault(node.Subid, null);
|
||||
if (nextOutbound != null)
|
||||
{
|
||||
nextOutbound = JsonUtils.DeepCopy(nextOutbound);
|
||||
}
|
||||
|
||||
var subItem = await AppHandler.Instance.GetSubItem(node.Subid);
|
||||
|
||||
// current proxy
|
||||
await GenOutbound(node, currentOutbound);
|
||||
currentOutbound.tag = $"{Global.ProxyTag}-{index}";
|
||||
|
||||
if (!node.Subid.IsNullOrEmpty())
|
||||
{
|
||||
if (prevProxyTags.TryGetValue(node.Subid, out var value))
|
||||
{
|
||||
prevTag = value; // maybe null
|
||||
}
|
||||
else
|
||||
{
|
||||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom
|
||||
&& prevNode.ConfigType != EConfigType.Hysteria2
|
||||
&& prevNode.ConfigType != EConfigType.TUIC)
|
||||
{
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}";
|
||||
prevOutbound.tag = prevTag;
|
||||
prevOutbounds.Add(prevOutbound);
|
||||
}
|
||||
prevProxyTags[node.Subid] = prevTag;
|
||||
}
|
||||
|
||||
nextOutbound = await GenChainOutbounds(subItem, currentOutbound, prevTag, nextOutbound);
|
||||
if (!nextProxyCache.ContainsKey(node.Subid))
|
||||
{
|
||||
nextProxyCache[node.Subid] = nextOutbound;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextOutbound is not null)
|
||||
{
|
||||
resultOutbounds.Add(nextOutbound);
|
||||
}
|
||||
resultOutbounds.Add(currentOutbound);
|
||||
}
|
||||
|
||||
// Merge results: first the main chain outbounds, then other outbounds, and finally utility outbounds
|
||||
resultOutbounds.AddRange(prevOutbounds);
|
||||
resultOutbounds.AddRange(v2rayConfig.outbounds);
|
||||
v2rayConfig.outbounds = resultOutbounds;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a chained outbound configuration for the given subItem and outbound.
|
||||
/// The outbound's tag must be set before calling this method.
|
||||
/// Returns the next proxy's outbound configuration, which may be null if no next proxy exists.
|
||||
/// </summary>
|
||||
/// <param name="subItem">The subscription item containing proxy chain information.</param>
|
||||
/// <param name="outbound">The current outbound configuration. Its tag must be set before calling this method.</param>
|
||||
/// <param name="prevOutboundTag">The tag of the previous outbound in the chain, if any.</param>
|
||||
/// <param name="nextOutbound">The outbound for the next proxy in the chain, if already created. If null, will be created inside.</param>
|
||||
/// <returns>
|
||||
/// The outbound configuration for the next proxy in the chain, or null if no next proxy exists.
|
||||
/// </returns>
|
||||
private async Task<Outbounds4Ray?> GenChainOutbounds(SubItem subItem, Outbounds4Ray outbound, string? prevOutboundTag, Outbounds4Ray? nextOutbound = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||
|
||||
if (!prevOutboundTag.IsNullOrEmpty())
|
||||
{
|
||||
outbound.streamSettings.sockopt = new()
|
||||
{
|
||||
dialerProxy = prevOutboundTag
|
||||
dialerProxy = prevOutbound.tag
|
||||
};
|
||||
}
|
||||
|
||||
// Next proxy
|
||||
//Next proxy
|
||||
var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode is not null
|
||||
&& nextNode.ConfigType != EConfigType.Custom
|
||||
&& nextNode.ConfigType != EConfigType.Hysteria2
|
||||
&& nextNode.ConfigType != EConfigType.TUIC)
|
||||
{
|
||||
if (nextOutbound == null)
|
||||
{
|
||||
nextOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(nextNode, nextOutbound);
|
||||
}
|
||||
nextOutbound.tag = outbound.tag;
|
||||
var nextOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(nextNode, nextOutbound);
|
||||
nextOutbound.tag = Global.ProxyTag;
|
||||
v2rayConfig.outbounds.Insert(0, nextOutbound);
|
||||
|
||||
outbound.tag = $"mid-{outbound.tag}";
|
||||
outbound.tag = $"{Global.ProxyTag}1";
|
||||
nextOutbound.streamSettings.sockopt = new()
|
||||
{
|
||||
dialerProxy = outbound.tag
|
||||
};
|
||||
}
|
||||
return nextOutbound;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return null;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenBalancer(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad)
|
||||
|
|
|
@ -196,7 +196,6 @@ public class SpeedtestService
|
|||
{
|
||||
return false;
|
||||
}
|
||||
await Task.Delay(1000);
|
||||
|
||||
var downloadHandle = new DownloadService();
|
||||
|
||||
|
@ -256,13 +255,9 @@ public class SpeedtestService
|
|||
try
|
||||
{
|
||||
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(it);
|
||||
if (pid < 0)
|
||||
if (pid > 0)
|
||||
{
|
||||
UpdateFunc(it.IndexId, "", ResUI.FailedToRunCore);
|
||||
}
|
||||
else
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
await Task.Delay(500);
|
||||
var delay = await DoRealPing(downloadHandle, it);
|
||||
if (blSpeedTest)
|
||||
{
|
||||
|
@ -276,6 +271,10 @@ public class SpeedtestService
|
|||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateFunc(it.IndexId, "", ResUI.FailedToRunCore);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
@ -552,7 +552,6 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
{
|
||||
await LoadCore();
|
||||
await SysProxyHandler.UpdateSysProxy(_config, false);
|
||||
await Task.Delay(1000);
|
||||
});
|
||||
Locator.Current.GetService<StatusBarViewModel>()?.TestServerAvailability();
|
||||
|
||||
|
|
|
@ -304,7 +304,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
{
|
||||
item.SpeedVal = result.Speed ?? string.Empty;
|
||||
}
|
||||
//_profileItems.Replace(item, JsonUtils.DeepCopy(item));
|
||||
_profileItems.Replace(item, JsonUtils.DeepCopy(item));
|
||||
}
|
||||
|
||||
public void UpdateStatistics(ServerSpeedItem update)
|
||||
|
@ -319,16 +319,16 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
item.TotalDown = Utils.HumanFy(update.TotalDown);
|
||||
item.TotalUp = Utils.HumanFy(update.TotalUp);
|
||||
|
||||
//if (SelectedProfile?.IndexId == item.IndexId)
|
||||
//{
|
||||
// var temp = JsonUtils.DeepCopy(item);
|
||||
// _profileItems.Replace(item, temp);
|
||||
// SelectedProfile = temp;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// _profileItems.Replace(item, JsonUtils.DeepCopy(item));
|
||||
//}
|
||||
if (SelectedProfile?.IndexId == item.IndexId)
|
||||
{
|
||||
var temp = JsonUtils.DeepCopy(item);
|
||||
_profileItems.Replace(item, temp);
|
||||
SelectedProfile = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
_profileItems.Replace(item, JsonUtils.DeepCopy(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
|
|
|
@ -500,16 +500,8 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
{
|
||||
try
|
||||
{
|
||||
if (_config.IsRunningCore(ECoreType.sing_box))
|
||||
{
|
||||
SpeedProxyDisplay = string.Format(ResUI.SpeedDisplayText, EInboundProtocol.mixed, Utils.HumanFy(update.ProxyUp), Utils.HumanFy(update.ProxyDown));
|
||||
SpeedDirectDisplay = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
SpeedProxyDisplay = string.Format(ResUI.SpeedDisplayText, Global.ProxyTag, Utils.HumanFy(update.ProxyUp), Utils.HumanFy(update.ProxyDown));
|
||||
SpeedDirectDisplay = string.Format(ResUI.SpeedDisplayText, Global.DirectTag, Utils.HumanFy(update.DirectUp), Utils.HumanFy(update.DirectDown));
|
||||
}
|
||||
SpeedProxyDisplay = string.Format(ResUI.SpeedDisplayText, Global.ProxyTag, Utils.HumanFy(update.ProxyUp), Utils.HumanFy(update.ProxyDown));
|
||||
SpeedDirectDisplay = string.Format(ResUI.SpeedDisplayText, Global.DirectTag, Utils.HumanFy(update.DirectUp), Utils.HumanFy(update.DirectDown));
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:semi="https://irihi.tech/semi"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
Name="v2rayN"
|
||||
x:DataType="vms:StatusBarViewModel"
|
||||
Name="v2rayN"
|
||||
RequestedThemeVariant="Default">
|
||||
<Application.Styles>
|
||||
<semi:SemiTheme />
|
||||
|
@ -32,8 +32,6 @@
|
|||
ToolTipText="{Binding RunningServerToolTipText}">
|
||||
<TrayIcon.Menu>
|
||||
<NativeMenu>
|
||||
<NativeMenuItem Command="{Binding NotifyLeftClickCmd}" Header="{x:Static resx:ResUI.menuShowOrHideMainWindow}" />
|
||||
<NativeMenuItemSeparator />
|
||||
<NativeMenuItem
|
||||
Command="{Binding SystemProxyClearCmd}"
|
||||
Header="{x:Static resx:ResUI.menuSystemProxyClear}"
|
||||
|
@ -57,11 +55,13 @@
|
|||
ToggleType="Radio" />
|
||||
<NativeMenuItemSeparator />
|
||||
<NativeMenuItem Click="MenuAddServerViaClipboardClick" Header="{x:Static resx:ResUI.menuAddServerViaClipboard}" />
|
||||
<NativeMenuItem Header="{x:Static resx:ResUI.menuAddServerViaScan}" IsVisible="False" />
|
||||
<NativeMenuItem Command="{Binding SubUpdateCmd}" Header="{x:Static resx:ResUI.menuSubUpdate}" />
|
||||
<NativeMenuItem Command="{Binding SubUpdateViaProxyCmd}" Header="{x:Static resx:ResUI.menuSubUpdateViaProxy}" />
|
||||
<NativeMenuItemSeparator />
|
||||
<NativeMenuItem Command="{Binding CopyProxyCmdToClipboardCmd}" Header="{x:Static resx:ResUI.menuCopyProxyCmdToClipboard}" />
|
||||
<NativeMenuItemSeparator />
|
||||
<NativeMenuItem Command="{Binding NotifyLeftClickCmd}" Header="{x:Static resx:ResUI.menuShowOrHideMainWindow}" />
|
||||
<NativeMenuItem Click="MenuExit_Click" Header="{x:Static resx:ResUI.menuExit}" />
|
||||
</NativeMenu>
|
||||
</TrayIcon.Menu>
|
||||
|
|
|
@ -11,6 +11,11 @@ public partial class App : Application
|
|||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
if (!AppHandler.Instance.InitApp())
|
||||
{
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
}
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace v2rayN.Desktop.Base;
|
||||
|
||||
public class WindowBase<TViewModel> : ReactiveWindow<TViewModel> where TViewModel : class
|
||||
{
|
||||
public WindowBase()
|
||||
{
|
||||
Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
private void ReactiveWindowBase_Closed(object? sender, EventArgs e)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected virtual void OnLoaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sizeItem = ConfigHandler.GetWindowSizeItem(AppHandler.Instance.Config, GetType().Name);
|
||||
if (sizeItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Width = sizeItem.Width;
|
||||
Height = sizeItem.Height;
|
||||
|
||||
var workingArea = (Screens.ScreenFromWindow(this) ?? Screens.Primary).WorkingArea;
|
||||
var scaling = (Utils.IsOSX() ? null : VisualRoot?.RenderScaling) ?? 1.0;
|
||||
|
||||
var x = workingArea.X + ((workingArea.Width - (Width * scaling)) / 2);
|
||||
var y = workingArea.Y + ((workingArea.Height - (Height * scaling)) / 2);
|
||||
|
||||
Position = new PixelPoint((int)x, (int)y);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
base.OnClosed(e);
|
||||
try
|
||||
{
|
||||
ConfigHandler.SaveWindowSizeItem(AppHandler.Instance.Config, GetType().Name, Width, Height);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
|
@ -14,17 +14,13 @@ internal class Program
|
|||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
if (OnStartup(args) == false)
|
||||
{
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
}
|
||||
OnStartup(args);
|
||||
|
||||
BuildAvaloniaApp()
|
||||
.StartWithClassicDesktopLifetime(args);
|
||||
}
|
||||
|
||||
private static bool OnStartup(string[]? Args)
|
||||
private static void OnStartup(string[]? Args)
|
||||
{
|
||||
if (Utils.IsWindows())
|
||||
{
|
||||
|
@ -34,7 +30,8 @@ internal class Program
|
|||
if (!rebootas && !bCreatedNew)
|
||||
{
|
||||
ProgramStarted.Set();
|
||||
return false;
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -42,30 +39,19 @@ internal class Program
|
|||
_ = new Mutex(true, "v2rayN", out var bOnlyOneInstance);
|
||||
if (!bOnlyOneInstance)
|
||||
{
|
||||
return false;
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!AppHandler.Instance.InitApp())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
{
|
||||
return AppBuilder.Configure<App>()
|
||||
.UsePlatformDetect()
|
||||
//.WithInterFont()
|
||||
.WithFontByDefault()
|
||||
.LogToTrace()
|
||||
#if OS_OSX
|
||||
.UseReactiveUI()
|
||||
.With(new MacOSPlatformOptions { ShowInDock = AppHandler.Instance.Config.UiItem.MacOSShowInDock });
|
||||
#else
|
||||
.UseReactiveUI();
|
||||
#endif
|
||||
}
|
||||
=> AppBuilder.Configure<App>()
|
||||
.UsePlatformDetect()
|
||||
//.WithInterFont()
|
||||
.WithFontByDefault()
|
||||
.LogToTrace()
|
||||
.UseReactiveUI()
|
||||
.With(new MacOSPlatformOptions { ShowInDock = false });
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
using System.Reactive.Disposables;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
using v2rayN.Desktop.Common;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class AddServer2Window : WindowBase<AddServer2ViewModel>
|
||||
public partial class AddServer2Window : ReactiveWindow<AddServer2ViewModel>
|
||||
{
|
||||
public AddServer2Window()
|
||||
{
|
||||
|
|
|
@ -105,7 +105,7 @@
|
|||
Grid.Row="2"
|
||||
ColumnDefinitions="180,Auto,Auto"
|
||||
IsVisible="False"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||
RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
|
@ -152,26 +152,13 @@
|
|||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsMuxEnabled}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togmuxEnabled"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridSs"
|
||||
Grid.Row="2"
|
||||
ColumnDefinitions="180,Auto"
|
||||
IsVisible="False"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||
RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
|
@ -198,19 +185,6 @@
|
|||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsMuxEnabled}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togmuxEnabled3"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridSocks"
|
||||
|
@ -250,7 +224,7 @@
|
|||
Grid.Row="2"
|
||||
ColumnDefinitions="180,Auto,Auto"
|
||||
IsVisible="False"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||
RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
|
@ -297,26 +271,13 @@
|
|||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsMuxEnabled}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togmuxEnabled5"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridTrojan"
|
||||
Grid.Row="2"
|
||||
ColumnDefinitions="180,Auto"
|
||||
IsVisible="False"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||
RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
|
@ -343,19 +304,6 @@
|
|||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsMuxEnabled}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togmuxEnabled6"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridHysteria2"
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
using System.Reactive.Disposables;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||
public partial class AddServerWindow : ReactiveWindow<AddServerViewModel>
|
||||
{
|
||||
public AddServerWindow()
|
||||
{
|
||||
|
@ -150,13 +150,11 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
|||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Shadowsocks:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId3.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity3.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.SOCKS:
|
||||
|
@ -169,13 +167,11 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
|||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId5.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow5.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity5.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Trojan:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId6.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow6.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Hysteria2:
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
IsCancel="True" />
|
||||
</StackPanel>
|
||||
|
||||
<TabControl HorizontalContentAlignment="Stretch">
|
||||
<TabControl HorizontalContentAlignment="Left">
|
||||
<TabItem Header="{x:Static resx:ResUI.TbSettingsCoreDns}">
|
||||
<DockPanel Margin="{StaticResource Margin8}">
|
||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
|
||||
|
@ -90,18 +90,16 @@
|
|||
</StackPanel>
|
||||
</WrapPanel>
|
||||
|
||||
<HeaderedContentControl
|
||||
Margin="{StaticResource Margin4}"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
Header="HTTP/SOCKS">
|
||||
<Grid Margin="{StaticResource Margin4}">
|
||||
<TextBox
|
||||
Name="txtnormalDNS"
|
||||
x:Name="txtnormalDNS"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Stretch"
|
||||
BorderThickness="1"
|
||||
Classes="TextArea"
|
||||
MinLines="10"
|
||||
TextWrapping="Wrap" />
|
||||
</HeaderedContentControl>
|
||||
TextWrapping="Wrap"
|
||||
Watermark="HTTP/SOCKS" />
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
|
||||
|
@ -146,34 +144,31 @@
|
|||
|
||||
<Grid Margin="{StaticResource Margin4}" ColumnDefinitions="*,10,*">
|
||||
|
||||
<HeaderedContentControl
|
||||
<TextBox
|
||||
x:Name="txtnormalDNS2"
|
||||
Grid.Column="0"
|
||||
BorderBrush="Gray"
|
||||
Width="400"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
BorderThickness="1"
|
||||
Header="HTTP/SOCKS">
|
||||
<TextBox
|
||||
Name="txtnormalDNS2"
|
||||
VerticalAlignment="Stretch"
|
||||
Classes="TextArea"
|
||||
MinLines="10"
|
||||
TextWrapping="Wrap" />
|
||||
</HeaderedContentControl>
|
||||
Classes="TextArea"
|
||||
Margin="{StaticResource Margin4}"
|
||||
TextWrapping="Wrap"
|
||||
Watermark="HTTP/SOCKS" />
|
||||
|
||||
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" />
|
||||
|
||||
<HeaderedContentControl
|
||||
<TextBox
|
||||
x:Name="txttunDNS2"
|
||||
Grid.Column="2"
|
||||
BorderBrush="Gray"
|
||||
Width="400"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
BorderThickness="1"
|
||||
Header="{x:Static resx:ResUI.TbSettingsTunMode}">
|
||||
<TextBox
|
||||
Name="txttunDNS2"
|
||||
VerticalAlignment="Stretch"
|
||||
Classes="TextArea"
|
||||
MinLines="10"
|
||||
TextWrapping="Wrap" />
|
||||
</HeaderedContentControl>
|
||||
|
||||
Classes="TextArea"
|
||||
Margin="{StaticResource Margin4}"
|
||||
TextWrapping="Wrap"
|
||||
Watermark="{x:Static resx:ResUI.TbSettingsTunMode}" />
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
using System.Reactive.Disposables;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
|
||||
public partial class DNSSettingWindow : ReactiveWindow<DNSSettingViewModel>
|
||||
{
|
||||
private static Config _config;
|
||||
|
||||
|
|
|
@ -3,13 +3,13 @@ using System.Text;
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
using v2rayN.Desktop.Handler;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class GlobalHotkeySettingWindow : WindowBase<GlobalHotkeySettingViewModel>
|
||||
public partial class GlobalHotkeySettingWindow : ReactiveWindow<GlobalHotkeySettingViewModel>
|
||||
{
|
||||
private readonly List<object> _textBoxKeyEventItem = new();
|
||||
|
||||
|
|
|
@ -5,18 +5,18 @@ using Avalonia.Controls.ApplicationLifetimes;
|
|||
using Avalonia.Controls.Notifications;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using Avalonia.Threading;
|
||||
using DialogHostAvalonia;
|
||||
using MsBox.Avalonia.Enums;
|
||||
using ReactiveUI;
|
||||
using Splat;
|
||||
using v2rayN.Desktop.Base;
|
||||
using v2rayN.Desktop.Common;
|
||||
using v2rayN.Desktop.Handler;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class MainWindow : WindowBase<MainWindowViewModel>
|
||||
public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
|
||||
{
|
||||
private static Config _config;
|
||||
private WindowNotificationManager? _manager;
|
||||
|
@ -154,6 +154,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
}
|
||||
menuAddServerViaScan.IsVisible = false;
|
||||
|
||||
RestoreUI();
|
||||
AddHelpMenuItem();
|
||||
MessageBus.Current.Listen<string>(EMsgCommand.AppExit.ToString()).Subscribe(StorageUI);
|
||||
}
|
||||
|
@ -435,14 +436,14 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
_config.UiItem.ShowInTaskbar = bl;
|
||||
}
|
||||
|
||||
protected override void OnLoaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
base.OnLoaded(sender, e);
|
||||
RestoreUI();
|
||||
}
|
||||
|
||||
private void RestoreUI()
|
||||
{
|
||||
if (_config.UiItem.MainWidth > 0 && _config.UiItem.MainHeight > 0)
|
||||
{
|
||||
Width = _config.UiItem.MainWidth;
|
||||
Height = _config.UiItem.MainHeight;
|
||||
}
|
||||
|
||||
if (_config.UiItem.MainGirdHeight1 > 0 && _config.UiItem.MainGirdHeight2 > 0)
|
||||
{
|
||||
if (_config.UiItem.MainGirdOrientation == EGirdOrientation.Horizontal)
|
||||
|
@ -460,15 +461,18 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
|
||||
private void StorageUI(string? n = null)
|
||||
{
|
||||
ConfigHandler.SaveWindowSizeItem(_config, GetType().Name, Width, Height);
|
||||
_config.UiItem.MainWidth = this.Width;
|
||||
_config.UiItem.MainHeight = this.Height;
|
||||
|
||||
if (_config.UiItem.MainGirdOrientation == EGirdOrientation.Horizontal)
|
||||
{
|
||||
ConfigHandler.SaveMainGirdHeight(_config, gridMain.ColumnDefinitions[0].ActualWidth, gridMain.ColumnDefinitions[2].ActualWidth);
|
||||
_config.UiItem.MainGirdHeight1 = Math.Ceiling(gridMain.ColumnDefinitions[0].ActualWidth + 0.1);
|
||||
_config.UiItem.MainGirdHeight2 = Math.Ceiling(gridMain.ColumnDefinitions[2].ActualWidth + 0.1);
|
||||
}
|
||||
else if (_config.UiItem.MainGirdOrientation == EGirdOrientation.Vertical)
|
||||
{
|
||||
ConfigHandler.SaveMainGirdHeight(_config, gridMain1.RowDefinitions[0].ActualHeight, gridMain1.RowDefinitions[2].ActualHeight);
|
||||
_config.UiItem.MainGirdHeight1 = Math.Ceiling(gridMain1.RowDefinitions[0].ActualHeight + 0.1);
|
||||
_config.UiItem.MainGirdHeight2 = Math.Ceiling(gridMain1.RowDefinitions[2].ActualHeight + 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -575,9 +575,9 @@
|
|||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsIPAPIUrl}" />
|
||||
<ComboBox
|
||||
x:Name="cmbIPAPIUrl"
|
||||
Text="{x:Static resx:ResUI.TbSettingsSubConvert}" />
|
||||
<ctrls:AutoCompleteBox
|
||||
x:Name="cmbSubConvertUrl"
|
||||
Grid.Row="20"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
|
@ -588,41 +588,28 @@
|
|||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsSubConvert}" />
|
||||
<ctrls:AutoCompleteBox
|
||||
x:Name="cmbSubConvertUrl"
|
||||
Grid.Row="21"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="22"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsMainGirdOrientation}" />
|
||||
<ComboBox
|
||||
x:Name="cmbMainGirdOrientation"
|
||||
Grid.Row="22"
|
||||
Grid.Row="21"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="23"
|
||||
Grid.Row="22"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsGeoFilesSource}" />
|
||||
<ComboBox
|
||||
x:Name="cmbGetFilesSourceUrl"
|
||||
Grid.Row="23"
|
||||
Grid.Row="22"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
<TextBlock
|
||||
Grid.Row="23"
|
||||
Grid.Row="22"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
|
@ -630,13 +617,33 @@
|
|||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="24"
|
||||
Grid.Row="23"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsSrsFilesSource}" />
|
||||
<ComboBox
|
||||
x:Name="cmbSrsFilesSourceUrl"
|
||||
Grid.Row="23"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
<TextBlock
|
||||
Grid.Row="23"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsChinaUserTip}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="24"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsRoutingRulesSource}" />
|
||||
<ComboBox
|
||||
x:Name="cmbRoutingRulesSourceUrl"
|
||||
Grid.Row="24"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
|
@ -654,20 +661,13 @@
|
|||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsRoutingRulesSource}" />
|
||||
Text="{x:Static resx:ResUI.TbSettingsIPAPIUrl}" />
|
||||
<ComboBox
|
||||
x:Name="cmbRoutingRulesSourceUrl"
|
||||
x:Name="cmbIPAPIUrl"
|
||||
Grid.Row="25"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
<TextBlock
|
||||
Grid.Row="25"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsChinaUserTip}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
using System.Reactive.Disposables;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
||||
public partial class OptionSettingWindow : ReactiveWindow<OptionSettingViewModel>
|
||||
{
|
||||
private static Config _config;
|
||||
|
||||
|
|
|
@ -215,7 +215,7 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
|||
await ViewModel.RefreshServersBiz();
|
||||
}
|
||||
|
||||
if (lstProfiles.SelectedIndex >= 0)
|
||||
if (lstProfiles.SelectedIndex > 0)
|
||||
{
|
||||
lstProfiles.ScrollIntoView(lstProfiles.SelectedItem, null);
|
||||
}
|
||||
|
@ -347,24 +347,6 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
|||
{
|
||||
try
|
||||
{
|
||||
//First scroll horizontally to the initial position to avoid the control crash bug
|
||||
if (lstProfiles.SelectedIndex >= 0)
|
||||
{
|
||||
lstProfiles.ScrollIntoView(lstProfiles.SelectedItem, lstProfiles.Columns[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
var model = lstProfiles.ItemsSource.Cast<ProfileItemModel>();
|
||||
if (model.Any())
|
||||
{
|
||||
lstProfiles.ScrollIntoView(model.First(), lstProfiles.Columns[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var it in lstProfiles.Columns)
|
||||
{
|
||||
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
using System.Reactive.Disposables;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsViewModel>
|
||||
public partial class RoutingRuleDetailsWindow : ReactiveWindow<RoutingRuleDetailsViewModel>
|
||||
{
|
||||
public RoutingRuleDetailsWindow()
|
||||
{
|
||||
|
|
|
@ -2,14 +2,14 @@ using System.Reactive.Disposables;
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using MsBox.Avalonia.Enums;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
using v2rayN.Desktop.Common;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class RoutingRuleSettingWindow : WindowBase<RoutingRuleSettingViewModel>
|
||||
public partial class RoutingRuleSettingWindow : ReactiveWindow<RoutingRuleSettingViewModel>
|
||||
{
|
||||
public RoutingRuleSettingWindow()
|
||||
{
|
||||
|
|
|
@ -24,11 +24,28 @@
|
|||
<MenuItem x:Name="menuRoutingAdvancedAdd2" Header="{x:Static resx:ResUI.menuRoutingAdvancedAdd}" />
|
||||
<MenuItem x:Name="menuRoutingAdvancedImportRules2" Header="{x:Static resx:ResUI.menuRoutingAdvancedImportRules}" />
|
||||
</Menu>
|
||||
|
||||
<TextBlock VerticalAlignment="Center">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkdomainStrategy_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbdomainStrategy}" />
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<ComboBox x:Name="cmbdomainStrategy" Width="110" />
|
||||
<Separator />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Static resx:ResUI.TbdomainMatcher}" />
|
||||
<ComboBox x:Name="cmbdomainMatcher" Width="60" />
|
||||
<Separator />
|
||||
<TextBlock VerticalAlignment="Center">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkdomainStrategy4Singbox_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbdomainStrategy4Singbox}" />
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<ComboBox x:Name="cmbdomainStrategy4Singbox" Width="100" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="{StaticResource Margin4}"
|
||||
DockPanel.Dock="Bottom"
|
||||
Orientation="Horizontal">
|
||||
<StackPanel
|
||||
|
@ -52,112 +69,58 @@
|
|||
IsCancel="True" />
|
||||
</StackPanel>
|
||||
|
||||
<Grid
|
||||
Margin="{StaticResource Margin4}"
|
||||
ColumnDefinitions="Auto,Auto"
|
||||
DockPanel.Dock="Top"
|
||||
RowDefinitions="Auto,Auto,Auto">
|
||||
<DockPanel>
|
||||
<TabControl x:Name="tabAdvanced">
|
||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.TbRoutingTabRuleList}">
|
||||
<DataGrid
|
||||
x:Name="lstRoutings"
|
||||
AutoGenerateColumns="False"
|
||||
BorderThickness="1"
|
||||
CanUserResizeColumns="True"
|
||||
GridLinesVisibility="All"
|
||||
HeadersVisibility="Column"
|
||||
IsReadOnly="True"
|
||||
ItemsSource="{Binding RoutingItems}">
|
||||
<DataGrid.KeyBindings>
|
||||
<KeyBinding Command="{Binding RoutingAdvancedSetDefaultCmd}" Gesture="Enter" />
|
||||
</DataGrid.KeyBindings>
|
||||
<DataGrid.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem x:Name="menuRoutingAdvancedAdd" Header="{x:Static resx:ResUI.menuRoutingAdvancedAdd}" />
|
||||
<MenuItem x:Name="menuRoutingAdvancedRemove" Header="{x:Static resx:ResUI.menuRoutingAdvancedRemove}" />
|
||||
<MenuItem x:Name="menuRoutingAdvancedSelectAll" Header="{x:Static resx:ResUI.menuSelectAll}" />
|
||||
<MenuItem x:Name="menuRoutingAdvancedSetDefault" Header="{x:Static resx:ResUI.menuRoutingAdvancedSetDefault}" />
|
||||
<Separator />
|
||||
<MenuItem x:Name="menuRoutingAdvancedImportRules" Header="{x:Static resx:ResUI.menuRoutingAdvancedImportRules}" />
|
||||
</ContextMenu>
|
||||
</DataGrid.ContextMenu>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkdomainStrategy_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbdomainStrategy}" />
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<ComboBox
|
||||
x:Name="cmbdomainStrategy"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbdomainMatcher}" />
|
||||
<ComboBox
|
||||
x:Name="cmbdomainMatcher"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkdomainStrategy4Singbox_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbdomainStrategy4Singbox}" />
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<ComboBox
|
||||
x:Name="cmbdomainStrategy4Singbox"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
|
||||
<TabControl x:Name="tabAdvanced">
|
||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.TbRoutingTabRuleList}">
|
||||
<DataGrid
|
||||
x:Name="lstRoutings"
|
||||
AutoGenerateColumns="False"
|
||||
BorderThickness="1"
|
||||
CanUserResizeColumns="True"
|
||||
GridLinesVisibility="All"
|
||||
HeadersVisibility="Column"
|
||||
IsReadOnly="True"
|
||||
ItemsSource="{Binding RoutingItems}">
|
||||
<DataGrid.KeyBindings>
|
||||
<KeyBinding Command="{Binding RoutingAdvancedSetDefaultCmd}" Gesture="Enter" />
|
||||
</DataGrid.KeyBindings>
|
||||
<DataGrid.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem x:Name="menuRoutingAdvancedAdd" Header="{x:Static resx:ResUI.menuRoutingAdvancedAdd}" />
|
||||
<MenuItem x:Name="menuRoutingAdvancedRemove" Header="{x:Static resx:ResUI.menuRoutingAdvancedRemove}" />
|
||||
<MenuItem x:Name="menuRoutingAdvancedSelectAll" Header="{x:Static resx:ResUI.menuSelectAll}" />
|
||||
<MenuItem x:Name="menuRoutingAdvancedSetDefault" Header="{x:Static resx:ResUI.menuRoutingAdvancedSetDefault}" />
|
||||
<Separator />
|
||||
<MenuItem x:Name="menuRoutingAdvancedImportRules" Header="{x:Static resx:ResUI.menuRoutingAdvancedImportRules}" />
|
||||
</ContextMenu>
|
||||
</DataGrid.ContextMenu>
|
||||
|
||||
<DataGrid.Columns>
|
||||
<DataGridCheckBoxColumn Width="40" Binding="{Binding IsActive}" />
|
||||
<DataGridTextColumn
|
||||
Width="*"
|
||||
Binding="{Binding Remarks}"
|
||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<DataGridTextColumn
|
||||
Width="60"
|
||||
Binding="{Binding RuleNum}"
|
||||
Header="{x:Static resx:ResUI.LvCount}" />
|
||||
<DataGridTextColumn
|
||||
Width="60"
|
||||
Binding="{Binding Sort}"
|
||||
Header="{x:Static resx:ResUI.LvSort}" />
|
||||
<DataGridTextColumn
|
||||
Width="*"
|
||||
Binding="{Binding Url}"
|
||||
Header="{x:Static resx:ResUI.LvUrl}" />
|
||||
<DataGridTextColumn
|
||||
Width="300"
|
||||
Binding="{Binding CustomIcon}"
|
||||
Header="{x:Static resx:ResUI.LvCustomIcon}" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
<DataGrid.Columns>
|
||||
<DataGridCheckBoxColumn Width="40" Binding="{Binding IsActive}" />
|
||||
<DataGridTextColumn
|
||||
Width="*"
|
||||
Binding="{Binding Remarks}"
|
||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<DataGridTextColumn
|
||||
Width="60"
|
||||
Binding="{Binding RuleNum}"
|
||||
Header="{x:Static resx:ResUI.LvCount}" />
|
||||
<DataGridTextColumn
|
||||
Width="60"
|
||||
Binding="{Binding Sort}"
|
||||
Header="{x:Static resx:ResUI.LvSort}" />
|
||||
<DataGridTextColumn
|
||||
Width="*"
|
||||
Binding="{Binding Url}"
|
||||
Header="{x:Static resx:ResUI.LvUrl}" />
|
||||
<DataGridTextColumn
|
||||
Width="300"
|
||||
Binding="{Binding CustomIcon}"
|
||||
Header="{x:Static resx:ResUI.LvCustomIcon}" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
|
|
|
@ -2,14 +2,14 @@ using System.Reactive.Disposables;
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using MsBox.Avalonia.Enums;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
using v2rayN.Desktop.Common;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
|
||||
public partial class RoutingSettingWindow : ReactiveWindow<RoutingSettingViewModel>
|
||||
{
|
||||
private bool _manualClose = false;
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
using System.Reactive.Disposables;
|
||||
using Avalonia;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class SubEditWindow : WindowBase<SubEditViewModel>
|
||||
public partial class SubEditWindow : ReactiveWindow<SubEditViewModel>
|
||||
{
|
||||
public SubEditWindow()
|
||||
{
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
using System.Reactive.Disposables;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using DialogHostAvalonia;
|
||||
using DynamicData;
|
||||
using MsBox.Avalonia.Enums;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
using v2rayN.Desktop.Common;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class SubSettingWindow : WindowBase<SubSettingViewModel>
|
||||
public partial class SubSettingWindow : ReactiveWindow<SubSettingViewModel>
|
||||
{
|
||||
private bool _manualClose = false;
|
||||
|
||||
|
|
|
@ -215,7 +215,7 @@
|
|||
<Setter Property="Background" Value="{DynamicResource MaterialDesignPaper}" />
|
||||
<Setter Property="TextElement.FontFamily" Value="{x:Static conv:MaterialDesignFonts.MyFont}" />
|
||||
<Setter Property="FontFamily" Value="{x:Static conv:MaterialDesignFonts.MyFont}" />
|
||||
<Setter Property="ResizeMode" Value="CanResize" />
|
||||
<Setter Property="ResizeMode" Value="NoResize" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="ViewGlobal"
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
using System.Windows;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace v2rayN.Base;
|
||||
|
||||
public class WindowBase<TViewModel> : ReactiveWindow<TViewModel> where TViewModel : class
|
||||
{
|
||||
public WindowBase()
|
||||
{
|
||||
Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
protected virtual void OnLoaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sizeItem = ConfigHandler.GetWindowSizeItem(AppHandler.Instance.Config, GetType().Name);
|
||||
if (sizeItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Width = Math.Min(sizeItem.Width, SystemParameters.WorkArea.Width);
|
||||
Height = Math.Min(sizeItem.Height, SystemParameters.WorkArea.Height);
|
||||
|
||||
Left = SystemParameters.WorkArea.Left + ((SystemParameters.WorkArea.Width - Width) / 2);
|
||||
Top = SystemParameters.WorkArea.Top + ((SystemParameters.WorkArea.Height - Height) / 2);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
base.OnClosed(e);
|
||||
|
||||
try
|
||||
{
|
||||
ConfigHandler.SaveWindowSizeItem(AppHandler.Instance.Config, GetType().Name, Width, Height);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
<base:WindowBase
|
||||
<reactiveui:ReactiveWindow
|
||||
x:Class="v2rayN.Views.AddServer2Window"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:base="clr-namespace:v2rayN.Base"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
|
@ -199,4 +198,4 @@
|
|||
</Grid>
|
||||
</ScrollViewer>
|
||||
</DockPanel>
|
||||
</base:WindowBase>
|
||||
</reactiveui:ReactiveWindow>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<base:WindowBase
|
||||
<reactiveui:ReactiveWindow
|
||||
x:Class="v2rayN.Views.AddServerWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:base="clr-namespace:v2rayN.Base"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
|
@ -156,7 +155,6 @@
|
|||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="180" />
|
||||
|
@ -216,20 +214,6 @@
|
|||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsMuxEnabled}" />
|
||||
<ToggleButton
|
||||
x:Name="togmuxEnabled"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridSs"
|
||||
|
@ -240,7 +224,6 @@
|
|||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="180" />
|
||||
|
@ -276,20 +259,6 @@
|
|||
Width="300"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsMuxEnabled}" />
|
||||
<ToggleButton
|
||||
x:Name="togmuxEnabled3"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridSocks"
|
||||
|
@ -345,7 +314,6 @@
|
|||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="180" />
|
||||
|
@ -405,20 +373,6 @@
|
|||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsMuxEnabled}" />
|
||||
<ToggleButton
|
||||
x:Name="togmuxEnabled5"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridTrojan"
|
||||
|
@ -429,7 +383,6 @@
|
|||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="180" />
|
||||
|
@ -465,20 +418,6 @@
|
|||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsMuxEnabled}" />
|
||||
<ToggleButton
|
||||
x:Name="togmuxEnabled6"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridHysteria2"
|
||||
|
@ -1072,4 +1011,4 @@
|
|||
</Grid>
|
||||
</ScrollViewer>
|
||||
</DockPanel>
|
||||
</base:WindowBase>
|
||||
</reactiveui:ReactiveWindow>
|
||||
|
|
|
@ -144,13 +144,11 @@ public partial class AddServerWindow
|
|||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Shadowsocks:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId3.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity3.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.SOCKS:
|
||||
|
@ -163,13 +161,11 @@ public partial class AddServerWindow
|
|||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId5.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow5.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity5.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Trojan:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId6.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow6.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Hysteria2:
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<base:WindowBase
|
||||
<reactiveui:ReactiveWindow
|
||||
x:Class="v2rayN.Views.DNSSettingWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:base="clr-namespace:v2rayN.Base"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
|
@ -205,4 +204,4 @@
|
|||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</base:WindowBase>
|
||||
</reactiveui:ReactiveWindow>
|
|
@ -1,8 +1,7 @@
|
|||
<base:WindowBase
|
||||
<reactiveui:ReactiveWindow
|
||||
x:Class="v2rayN.Views.GlobalHotkeySettingWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:base="clr-namespace:v2rayN.Base"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
|
@ -170,4 +169,4 @@
|
|||
</Grid>
|
||||
</ScrollViewer>
|
||||
</DockPanel>
|
||||
</base:WindowBase>
|
||||
</reactiveui:ReactiveWindow>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<base:WindowBase
|
||||
<reactiveui:ReactiveWindow
|
||||
x:Class="v2rayN.Views.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:base="clr-namespace:v2rayN.Base"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
|
@ -41,8 +40,8 @@
|
|||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
ClipToBounds="True"
|
||||
KeyboardNavigation.TabNavigation="Continue"
|
||||
Style="{StaticResource MaterialDesignToolBar}">
|
||||
Style="{StaticResource MaterialDesignToolBar}"
|
||||
KeyboardNavigation.TabNavigation="Continue">
|
||||
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
|
||||
<MenuItem Padding="8,0" AutomationProperties.Name="{x:Static resx:ResUI.menuServers}">
|
||||
<MenuItem.Header>
|
||||
|
@ -433,4 +432,4 @@
|
|||
</DockPanel>
|
||||
</Grid>
|
||||
</materialDesign:DialogHost>
|
||||
</base:WindowBase>
|
||||
</reactiveui:ReactiveWindow>
|
||||
|
|
|
@ -139,6 +139,7 @@ public partial class MainWindow
|
|||
RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly;
|
||||
}
|
||||
|
||||
RestoreUI();
|
||||
AddHelpMenuItem();
|
||||
WindowsHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, null);
|
||||
MessageBus.Current.Listen<string>(EMsgCommand.AppExit.ToString()).Subscribe(StorageUI);
|
||||
|
@ -394,14 +395,20 @@ public partial class MainWindow
|
|||
_config.UiItem.ShowInTaskbar = bl;
|
||||
}
|
||||
|
||||
protected override void OnLoaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
base.OnLoaded(sender, e);
|
||||
RestoreUI();
|
||||
}
|
||||
|
||||
private void RestoreUI()
|
||||
{
|
||||
if (_config.UiItem.MainWidth > 0 && _config.UiItem.MainHeight > 0)
|
||||
{
|
||||
Width = _config.UiItem.MainWidth;
|
||||
Height = _config.UiItem.MainHeight;
|
||||
}
|
||||
|
||||
var maxWidth = SystemParameters.WorkArea.Width;
|
||||
var maxHeight = SystemParameters.WorkArea.Height;
|
||||
if (Width > maxWidth)
|
||||
Width = maxWidth;
|
||||
if (Height > maxHeight)
|
||||
Height = maxHeight;
|
||||
if (_config.UiItem.MainGirdHeight1 > 0 && _config.UiItem.MainGirdHeight2 > 0)
|
||||
{
|
||||
if (_config.UiItem.MainGirdOrientation == EGirdOrientation.Horizontal)
|
||||
|
@ -419,15 +426,18 @@ public partial class MainWindow
|
|||
|
||||
private void StorageUI(string? n = null)
|
||||
{
|
||||
ConfigHandler.SaveWindowSizeItem(_config, GetType().Name, Width, Height);
|
||||
_config.UiItem.MainWidth = this.Width;
|
||||
_config.UiItem.MainHeight = this.Height;
|
||||
|
||||
if (_config.UiItem.MainGirdOrientation == EGirdOrientation.Horizontal)
|
||||
{
|
||||
ConfigHandler.SaveMainGirdHeight(_config, gridMain.ColumnDefinitions[0].ActualWidth, gridMain.ColumnDefinitions[2].ActualWidth);
|
||||
_config.UiItem.MainGirdHeight1 = Math.Ceiling(gridMain.ColumnDefinitions[0].ActualWidth + 0.1);
|
||||
_config.UiItem.MainGirdHeight2 = Math.Ceiling(gridMain.ColumnDefinitions[2].ActualWidth + 0.1);
|
||||
}
|
||||
else if (_config.UiItem.MainGirdOrientation == EGirdOrientation.Vertical)
|
||||
{
|
||||
ConfigHandler.SaveMainGirdHeight(_config, gridMain1.RowDefinitions[0].ActualHeight, gridMain1.RowDefinitions[2].ActualHeight);
|
||||
_config.UiItem.MainGirdHeight1 = Math.Ceiling(gridMain1.RowDefinitions[0].ActualHeight + 0.1);
|
||||
_config.UiItem.MainGirdHeight2 = Math.Ceiling(gridMain1.RowDefinitions[2].ActualHeight + 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<base:WindowBase
|
||||
<reactiveui:ReactiveWindow
|
||||
x:Class="v2rayN.Views.OptionSettingWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:base="clr-namespace:v2rayN.Base"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
|
@ -847,26 +846,10 @@
|
|||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsIPAPIUrl}" />
|
||||
<ComboBox
|
||||
x:Name="cmbIPAPIUrl"
|
||||
Grid.Row="20"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin8}"
|
||||
IsEditable="True"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="21"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsSubConvert}" />
|
||||
<ComboBox
|
||||
x:Name="cmbSubConvertUrl"
|
||||
Grid.Row="21"
|
||||
Grid.Row="20"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin8}"
|
||||
|
@ -875,7 +858,7 @@
|
|||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="22"
|
||||
Grid.Row="21"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
|
@ -883,14 +866,14 @@
|
|||
Text="{x:Static resx:ResUI.TbSettingsMainGirdOrientation}" />
|
||||
<ComboBox
|
||||
x:Name="cmbMainGirdOrientation"
|
||||
Grid.Row="22"
|
||||
Grid.Row="21"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="23"
|
||||
Grid.Row="22"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
|
@ -898,14 +881,14 @@
|
|||
Text="{x:Static resx:ResUI.TbSettingsGeoFilesSource}" />
|
||||
<ComboBox
|
||||
x:Name="cmbGetFilesSourceUrl"
|
||||
Grid.Row="23"
|
||||
Grid.Row="22"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin8}"
|
||||
IsEditable="True"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
<TextBlock
|
||||
Grid.Row="23"
|
||||
Grid.Row="22"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
|
@ -914,7 +897,7 @@
|
|||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="24"
|
||||
Grid.Row="23"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
|
@ -922,6 +905,30 @@
|
|||
Text="{x:Static resx:ResUI.TbSettingsSrsFilesSource}" />
|
||||
<ComboBox
|
||||
x:Name="cmbSrsFilesSourceUrl"
|
||||
Grid.Row="23"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin8}"
|
||||
IsEditable="True"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
<TextBlock
|
||||
Grid.Row="23"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsChinaUserTip}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="24"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsRoutingRulesSource}" />
|
||||
<ComboBox
|
||||
x:Name="cmbRoutingRulesSourceUrl"
|
||||
Grid.Row="24"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
|
@ -943,23 +950,15 @@
|
|||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsRoutingRulesSource}" />
|
||||
Text="{x:Static resx:ResUI.TbSettingsIPAPIUrl}" />
|
||||
<ComboBox
|
||||
x:Name="cmbRoutingRulesSourceUrl"
|
||||
x:Name="cmbIPAPIUrl"
|
||||
Grid.Row="25"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin8}"
|
||||
IsEditable="True"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
<TextBlock
|
||||
Grid.Row="25"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsChinaUserTip}"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
@ -1230,4 +1229,4 @@
|
|||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</base:WindowBase>
|
||||
</reactiveui:ReactiveWindow>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<base:WindowBase
|
||||
<reactiveui:ReactiveWindow
|
||||
x:Class="v2rayN.Views.RoutingRuleDetailsWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:base="clr-namespace:v2rayN.Base"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
|
@ -243,4 +242,4 @@
|
|||
</GroupBox>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</base:WindowBase>
|
||||
</reactiveui:ReactiveWindow>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<base:WindowBase
|
||||
<reactiveui:ReactiveWindow
|
||||
x:Class="v2rayN.Views.RoutingRuleSettingWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:base="clr-namespace:v2rayN.Base"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
|
@ -335,4 +334,4 @@
|
|||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</base:WindowBase>
|
||||
</reactiveui:ReactiveWindow>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<base:WindowBase
|
||||
<reactiveui:ReactiveWindow
|
||||
x:Class="v2rayN.Views.RoutingSettingWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:base="clr-namespace:v2rayN.Base"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
|
@ -30,25 +29,68 @@
|
|||
VerticalAlignment="Center"
|
||||
ClipToBounds="True"
|
||||
Style="{StaticResource MaterialDesignToolBar}">
|
||||
<Button x:Name="menuRoutingAdvancedAdd2">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon
|
||||
Margin="{StaticResource MarginRight8}"
|
||||
VerticalAlignment="Center"
|
||||
Kind="Plus" />
|
||||
<TextBlock Style="{StaticResource ToolbarTextBlock}" Text="{x:Static resx:ResUI.menuRoutingAdvancedAdd}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
|
||||
<MenuItem x:Name="menuRoutingAdvanced" Padding="8,0">
|
||||
<MenuItem.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon
|
||||
Margin="{StaticResource MarginRight8}"
|
||||
VerticalAlignment="Center"
|
||||
Kind="Routes" />
|
||||
<TextBlock Text="{x:Static resx:ResUI.menuRoutingAdvanced}" />
|
||||
</StackPanel>
|
||||
</MenuItem.Header>
|
||||
<MenuItem
|
||||
x:Name="menuRoutingAdvancedAdd2"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuRoutingAdvancedAdd}" />
|
||||
<MenuItem
|
||||
x:Name="menuRoutingAdvancedImportRules2"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuRoutingAdvancedImportRules}" />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<Separator />
|
||||
<Button x:Name="menuRoutingAdvancedImportRules2">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon
|
||||
Margin="{StaticResource MarginRight8}"
|
||||
VerticalAlignment="Center"
|
||||
Kind="Import" />
|
||||
<TextBlock Style="{StaticResource ToolbarTextBlock}" Text="{x:Static resx:ResUI.menuRoutingAdvancedImportRules}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<TextBlock
|
||||
Margin="{StaticResource MarginLeft8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}">
|
||||
<Hyperlink Click="linkdomainStrategy_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbdomainStrategy}" />
|
||||
<materialDesign:PackIcon Kind="Link" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<ComboBox
|
||||
x:Name="cmbdomainStrategy"
|
||||
Width="110"
|
||||
Margin="{StaticResource MarginLeft8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
<Separator />
|
||||
<TextBlock
|
||||
Margin="{StaticResource MarginLeft8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbdomainMatcher}" />
|
||||
<ComboBox
|
||||
x:Name="cmbdomainMatcher"
|
||||
Width="60"
|
||||
Margin="{StaticResource MarginLeft8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
<Separator />
|
||||
<TextBlock
|
||||
Margin="{StaticResource MarginLeft8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}">
|
||||
<Hyperlink Click="linkdomainStrategy4Singbox_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbdomainStrategy4Singbox}" />
|
||||
<materialDesign:PackIcon Kind="Link" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<ComboBox
|
||||
x:Name="cmbdomainStrategy4Singbox"
|
||||
Width="100"
|
||||
Margin="{StaticResource MarginLeft8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
</ToolBar>
|
||||
</ToolBarTray>
|
||||
|
||||
|
@ -80,145 +122,82 @@
|
|||
Style="{StaticResource DefButton}" />
|
||||
</StackPanel>
|
||||
|
||||
<Grid Margin="{StaticResource Margin8}" DockPanel.Dock="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}">
|
||||
<Hyperlink Click="linkdomainStrategy_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbdomainStrategy}" />
|
||||
<materialDesign:PackIcon Kind="Link" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<ComboBox
|
||||
x:Name="cmbdomainStrategy"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbdomainMatcher}" />
|
||||
<ComboBox
|
||||
x:Name="cmbdomainMatcher"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}">
|
||||
<Hyperlink Click="linkdomainStrategy4Singbox_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbdomainStrategy4Singbox}" />
|
||||
<materialDesign:PackIcon Kind="Link" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<ComboBox
|
||||
x:Name="cmbdomainStrategy4Singbox"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
</Grid>
|
||||
|
||||
<TabControl x:Name="tabAdvanced">
|
||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.TbRoutingTabRuleList}">
|
||||
<DataGrid
|
||||
x:Name="lstRoutings"
|
||||
AutoGenerateColumns="False"
|
||||
BorderThickness="1"
|
||||
CanUserAddRows="False"
|
||||
CanUserResizeRows="False"
|
||||
CanUserSortColumns="False"
|
||||
EnableRowVirtualization="True"
|
||||
GridLinesVisibility="All"
|
||||
HeadersVisibility="Column"
|
||||
IsReadOnly="True"
|
||||
Style="{StaticResource DefDataGrid}">
|
||||
<DataGrid.ContextMenu>
|
||||
<ContextMenu Style="{StaticResource DefContextMenu}">
|
||||
<MenuItem
|
||||
x:Name="menuRoutingAdvancedAdd"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuRoutingAdvancedAdd}" />
|
||||
<MenuItem
|
||||
x:Name="menuRoutingAdvancedRemove"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuRoutingAdvancedRemove}" />
|
||||
<MenuItem
|
||||
x:Name="menuRoutingAdvancedSelectAll"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuSelectAll}" />
|
||||
<MenuItem
|
||||
x:Name="menuRoutingAdvancedSetDefault"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuRoutingAdvancedSetDefault}" />
|
||||
<Separator />
|
||||
<MenuItem
|
||||
x:Name="menuRoutingAdvancedImportRules"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuRoutingAdvancedImportRules}" />
|
||||
</ContextMenu>
|
||||
</DataGrid.ContextMenu>
|
||||
<DataGrid.Resources>
|
||||
<Style BasedOn="{StaticResource MaterialDesignDataGridCell}" TargetType="DataGridCell">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsActive}" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource MaterialDesign.Brush.Primary.Light}" />
|
||||
<Setter Property="Foreground" Value="Black" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource MaterialDesign.Brush.Primary.Light}" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</DataGrid.Resources>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn
|
||||
Width="*"
|
||||
Binding="{Binding Remarks}"
|
||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<DataGridTextColumn
|
||||
Width="60"
|
||||
Binding="{Binding RuleNum}"
|
||||
Header="{x:Static resx:ResUI.LvCount}" />
|
||||
<DataGridTextColumn
|
||||
Width="60"
|
||||
Binding="{Binding Sort}"
|
||||
Header="{x:Static resx:ResUI.LvSort}" />
|
||||
<DataGridTextColumn
|
||||
Width="*"
|
||||
Binding="{Binding Url}"
|
||||
Header="{x:Static resx:ResUI.LvUrl}" />
|
||||
<DataGridTextColumn
|
||||
Width="300"
|
||||
Binding="{Binding CustomIcon}"
|
||||
Header="{x:Static resx:ResUI.LvCustomIcon}" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
<DockPanel>
|
||||
<TabControl x:Name="tabAdvanced">
|
||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.TbRoutingTabRuleList}">
|
||||
<DataGrid
|
||||
x:Name="lstRoutings"
|
||||
AutoGenerateColumns="False"
|
||||
BorderThickness="1"
|
||||
CanUserAddRows="False"
|
||||
CanUserResizeRows="False"
|
||||
CanUserSortColumns="False"
|
||||
EnableRowVirtualization="True"
|
||||
GridLinesVisibility="All"
|
||||
HeadersVisibility="Column"
|
||||
IsReadOnly="True"
|
||||
Style="{StaticResource DefDataGrid}">
|
||||
<DataGrid.ContextMenu>
|
||||
<ContextMenu Style="{StaticResource DefContextMenu}">
|
||||
<MenuItem
|
||||
x:Name="menuRoutingAdvancedAdd"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuRoutingAdvancedAdd}" />
|
||||
<MenuItem
|
||||
x:Name="menuRoutingAdvancedRemove"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuRoutingAdvancedRemove}" />
|
||||
<MenuItem
|
||||
x:Name="menuRoutingAdvancedSelectAll"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuSelectAll}" />
|
||||
<MenuItem
|
||||
x:Name="menuRoutingAdvancedSetDefault"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuRoutingAdvancedSetDefault}" />
|
||||
<Separator />
|
||||
<MenuItem
|
||||
x:Name="menuRoutingAdvancedImportRules"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuRoutingAdvancedImportRules}" />
|
||||
</ContextMenu>
|
||||
</DataGrid.ContextMenu>
|
||||
<DataGrid.Resources>
|
||||
<Style BasedOn="{StaticResource MaterialDesignDataGridCell}" TargetType="DataGridCell">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsActive}" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource MaterialDesign.Brush.Primary.Light}" />
|
||||
<Setter Property="Foreground" Value="Black" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource MaterialDesign.Brush.Primary.Light}" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</DataGrid.Resources>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn
|
||||
Width="*"
|
||||
Binding="{Binding Remarks}"
|
||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<DataGridTextColumn
|
||||
Width="60"
|
||||
Binding="{Binding RuleNum}"
|
||||
Header="{x:Static resx:ResUI.LvCount}" />
|
||||
<DataGridTextColumn
|
||||
Width="60"
|
||||
Binding="{Binding Sort}"
|
||||
Header="{x:Static resx:ResUI.LvSort}" />
|
||||
<DataGridTextColumn
|
||||
Width="*"
|
||||
Binding="{Binding Url}"
|
||||
Header="{x:Static resx:ResUI.LvUrl}" />
|
||||
<DataGridTextColumn
|
||||
Width="300"
|
||||
Binding="{Binding CustomIcon}"
|
||||
Header="{x:Static resx:ResUI.LvCustomIcon}" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</DockPanel>
|
||||
</base:WindowBase>
|
||||
</reactiveui:ReactiveWindow>
|
|
@ -92,7 +92,7 @@
|
|||
AutomationProperties.Name="{x:Static resx:ResUI.menuRouting}"
|
||||
DisplayMemberPath="Remarks"
|
||||
FontSize="{DynamicResource StdFontSize}"
|
||||
Style="{StaticResource MaterialDesignFloatingHintComboBox}">
|
||||
Style="{StaticResource MaterialDesignFloatingHintComboBox}">
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
|
||||
|
@ -185,7 +185,7 @@
|
|||
AutomationProperties.Name="{x:Static resx:ResUI.menuRouting}"
|
||||
DisplayMemberPath="Remarks"
|
||||
FontSize="{DynamicResource StdFontSize}"
|
||||
Style="{StaticResource MaterialDesignFilledComboBox}">
|
||||
Style="{StaticResource MaterialDesignFilledComboBox}">
|
||||
</ComboBox>
|
||||
</DockPanel>
|
||||
</MenuItem.Header>
|
||||
|
@ -200,7 +200,7 @@
|
|||
AutomationProperties.Name="{x:Static resx:ResUI.menuServers}"
|
||||
DisplayMemberPath="Text"
|
||||
FontSize="{DynamicResource StdFontSize}"
|
||||
Style="{StaticResource MaterialDesignFilledComboBox}">
|
||||
Style="{StaticResource MaterialDesignFilledComboBox}">
|
||||
</ComboBox>
|
||||
</DockPanel>
|
||||
</MenuItem.Header>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<base:WindowBase
|
||||
<reactiveui:ReactiveWindow
|
||||
x:Class="v2rayN.Views.SubEditWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:base="clr-namespace:v2rayN.Base"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
|
@ -316,4 +315,4 @@
|
|||
</Grid>
|
||||
</ScrollViewer>
|
||||
</DockPanel>
|
||||
</base:WindowBase>
|
||||
</reactiveui:ReactiveWindow>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<base:WindowBase
|
||||
<reactiveui:ReactiveWindow
|
||||
x:Class="v2rayN.Views.SubSettingWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:base="clr-namespace:v2rayN.Base"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
|
@ -121,4 +120,4 @@
|
|||
</DataGrid>
|
||||
</DockPanel>
|
||||
</materialDesign:DialogHost>
|
||||
</base:WindowBase>
|
||||
</reactiveui:ReactiveWindow>
|
||||
|
|
Loading…
Reference in a new issue