Compare commits

...

74 Commits

Author SHA1 Message Date
2dust
a7de149fd7 up 7.10.0 2025-02-28 09:46:43 +08:00
2dust
ae38be36f5 Checkout submodules 2025-02-27 20:53:29 +08:00
2dust
a20e989211 Update build-windows-desktop.yml 2025-02-27 20:50:52 +08:00
2dust
579f47ba0d Create GlobalHotKeys 2025-02-27 20:25:20 +08:00
2dust
24cad87954 Bug fix
https://github.com/2dust/v2rayN/issues/6812
2025-02-27 20:12:53 +08:00
2dust
84d72cd110 Use project to implement Windows global hotkey
https://github.com/2dust/GlobalHotKeys
2025-02-27 20:12:07 +08:00
2dust
c0cd46a5aa Optimize HotkeyHandler 2025-02-27 17:50:54 +08:00
2dust
98613c43ca Fix tun mtu setting 2025-02-26 20:46:31 +08:00
2dust
555960e210 Optimize and improve code 2025-02-26 17:01:57 +08:00
2dust
a18ae5582b Jump to the selected item when refreshing the server list
https://github.com/2dust/v2rayN/issues/6800
2025-02-26 16:36:36 +08:00
2dust
6d6894591c Update Global.cs 2025-02-26 15:51:47 +08:00
2dust
984b97fc14 Optimize latency and IP address information testing
If the first test fails, it will be tested again after 500ms.
2025-02-26 14:30:10 +08:00
2dust
a7f35d4495 Windows desktop version add global hotkey function 2025-02-26 10:52:36 +08:00
2dust
add92cfa7c Update desktop global hotkey setting 2025-02-25 16:14:50 +08:00
2dust
6079e76be5 Improved global hotkey setting 2025-02-25 14:26:12 +08:00
2dust
166c7cb2f5 Fix window title 2025-02-25 14:15:54 +08:00
nayeko
dbd3ca44c2 Fix Package AppImage script (#6794)
Co-authored-by: nayeko <nayeko@users.noreply.github.com>
2025-02-25 10:06:33 +08:00
dependabot[bot]
ae495dde54 Bump actions/upload-artifact from 4.6.0 to 4.6.1 (#6797)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.0 to 4.6.1.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4.6.0...v4.6.1)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-25 09:46:08 +08:00
2dust
4ae25b2f34 Improved global hotkey setting 2025-02-24 19:53:38 +08:00
2dust
cdfb621c59 Update MsgView.axaml 2025-02-24 19:07:05 +08:00
2dust
29e8df7d2e When set the linux system proxy, use shell scripts instead of command lines 2025-02-24 11:02:51 +08:00
2dust
72ff947d95 Bug fix for ProxySettingOSX 2025-02-23 19:59:01 +08:00
2dust
3edaac5739 When set the macOS system proxy, use shell scripts instead of command lines 2025-02-23 19:52:13 +08:00
2dust
5777a97119 Create proxy_set_osx_sh 2025-02-23 19:41:34 +08:00
2dust
aeddbc1dcc CreateLinuxShellFile in the binConfigs folder 2025-02-23 17:19:45 +08:00
2dust
2f3e409487 Add linux bash param 2025-02-23 16:59:22 +08:00
2dust
aa133bb50b Update SpeedtestService.cs 2025-02-23 16:34:40 +08:00
2dust
3bc79a4ba1 Update 01_bug_report.yml 2025-02-23 12:05:51 +08:00
2dust
064a04fbad up 7.9.3 2025-02-21 17:47:30 +08:00
2dust
39ed13cf8a Create build-all.yml 2025-02-21 17:38:47 +08:00
2dust
a1edacb196 Update UpdateService.cs 2025-02-21 17:37:36 +08:00
2dust
c9f79e4b47 Added update function for Country.mmdb, geoip-only-cn-private.dat, geoip.metadb
https://github.com/2dust/v2rayN/issues/6752
2025-02-21 15:38:11 +08:00
2dust
40c1498226 When testing speed, skip items with incorrect latency 2025-02-21 14:33:43 +08:00
2dust
390061f9bd Fix the desktop UI bug 2025-02-21 12:40:45 +08:00
2dust
42324a2c9e Adjust controls margin 2025-02-20 18:34:25 +08:00
2dust
b9b4ca6360 The desktop version use SemiFontFamilyRegular, Fall back to built-in fonts 2025-02-20 16:23:46 +08:00
2dust
565697bc0b The desktop version unified icon size 2025-02-20 14:32:38 +08:00
2dust
2e6c82c851 Adjust UI
https://github.com/2dust/v2rayN/issues/6751
2025-02-20 14:07:12 +08:00
2dust
ee75dd37af The desktop version add wrap to the subscription group 2025-02-20 11:08:28 +08:00
2dust
4859dcda08 up 7.9.2 2025-02-19 21:10:47 +08:00
2dust
2e962e555d Use AutoCompleteBox instead of editable Combobox for desktop
Thanks to https://github.com/irihitech/Ursa.Avalonia
2025-02-19 17:41:58 +08:00
2dust
ab73e5acb2 Micro-adjustment interface 2025-02-19 17:39:47 +08:00
2dust
4e3e5ce130 Add CanUserReorderColumns for desktop 2025-02-19 15:03:29 +08:00
2dust
bb8eef3bf5 Removed the Hide to tray when closing the window feature for Windows version
https://github.com/2dust/v2rayN/issues/6726
2025-02-19 10:42:31 +08:00
2dust
eee87ded29 Fix cache.db storage location
https://github.com/2dust/v2rayN/issues/6731
2025-02-19 10:06:47 +08:00
2dust
e0f005bd96 up 7.9.1 2025-02-18 18:58:19 +08:00
2dust
2cacc372ad Optimize desktop DataGrid RowHeight adjusts with font size 2025-02-18 18:48:29 +08:00
2dust
0b1b681655 Add system proxy pac to the Windows desktop version 2025-02-18 14:38:08 +08:00
2dust
deafd73306 Optimize RemoveInvalidServerResult 2025-02-17 20:00:28 +08:00
2dust
317a5da120 up build 2025-02-17 14:38:26 +08:00
bonjour
071cefc511 Revert "Fix Package AppImage script (#6681)" (#6714)
This reverts commit 41cc260b5c.
2025-02-17 13:59:36 +08:00
2dust
32a5cc8aa3 Check for avalonia desktop windows version 2025-02-17 12:27:00 +08:00
2dust
c41378a085 Update build-windows-desktop.yml 2025-02-17 12:24:55 +08:00
2dust
6910e03ef4 up 7.9.0 2025-02-16 20:34:14 +08:00
2dust
5060a358db Clear resx 2025-02-16 19:12:58 +08:00
2dust
b3e9a957c4 Remove invalid by test results 2025-02-16 15:07:09 +08:00
2dust
f6dbfc2dac up PackageVersion 2025-02-16 15:05:10 +08:00
2dust
2966a34e63 Update Directory.Build.props 2025-02-16 11:15:32 +08:00
2dust
50959951ae The number of concurrent during multi-test 2025-02-15 20:27:20 +08:00
2dust
c2e1cf7bdb Fix windows TaskbarIcon 2025-02-15 17:26:20 +08:00
2dust
51ac7cc8be Improved and optimized speedtest 2025-02-15 11:21:35 +08:00
2dust
3144f1d1c2 Remove SpeedTestPageSize 2025-02-14 15:00:23 +08:00
2dust
a176e7b912 Optimize the prompt function of speed test
Save friendly prompt information during speed test.
2025-02-14 14:55:43 +08:00
2dust
1198ec0f74 Improved and optimized speedtest
When testing server speed, start the Core for each server and generate the same configuration files as when using the server.
Add a folder binConfigs to store temporary configuration files for Core.
2025-02-13 19:46:51 +08:00
2dust
4104964e38 up 7.8.3 2025-02-12 20:04:29 +08:00
2dust
3bbd1edf06 Update V2rayConfig.cs 2025-02-12 19:18:59 +08:00
lyranico
41cc260b5c Fix Package AppImage script (#6681) 2025-02-12 14:33:02 +08:00
2dust
6cd5063c9b Fix call core file
https://github.com/2dust/v2rayN/issues/6680
2025-02-12 11:23:22 +08:00
2dust
e544df6d01 up 7.8.2 2025-02-11 16:30:16 +08:00
2dust
f952d2383c Remove IncludeNativeLibrariesForSelfExtract 2025-02-11 10:16:19 +08:00
2dust
8a29e147d3 Added Enable Mixin parameter 2025-02-10 20:04:47 +08:00
Alphax-Hue3682
8a19128e7f Update Persian translate (#6651)
* Update Persian translate

* Update ResUI.fa-Ir.resx
2025-02-10 17:40:21 +08:00
2dust
7dc9fbd8ff Improved and optimized speedtest 2025-02-10 10:08:03 +08:00
2dust
31a179e647 Code clean 2025-02-09 20:17:56 +08:00
106 changed files with 2689 additions and 2047 deletions

View File

@@ -3,6 +3,13 @@ description: 在提出问题前请先自行排除服务器端问题和升级到
title: "[Bug]: " title: "[Bug]: "
labels: ["bug"] labels: ["bug"]
body: body:
- type: input
id: "os-version"
attributes:
label: "操作系统和版本"
description: "操作系统和版本"
validations:
required: true
- type: input - type: input
id: "expectation" id: "expectation"
attributes: attributes:

69
.github/workflows/build-all.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
name: release all platforms
on:
workflow_dispatch:
inputs:
release_tag:
required: false
type: string
jobs:
update:
runs-on: ubuntu-latest
steps:
- name: Trigger build windows
if: github.event.inputs.release_tag != ''
run: |
curl -X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
https://api.github.com/repos/${{ github.repository }}/actions/workflows/build-windows.yml/dispatches \
-d "{
\"ref\": \"master\",
\"inputs\": {
\"release_tag\": \"${{ github.event.inputs.release_tag }}\"
}
}"
- name: Trigger build linux
if: github.event.inputs.release_tag != ''
run: |
curl -X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
https://api.github.com/repos/${{ github.repository }}/actions/workflows/build-linux.yml/dispatches \
-d "{
\"ref\": \"master\",
\"inputs\": {
\"release_tag\": \"${{ github.event.inputs.release_tag }}\"
}
}"
- name: Trigger build osx
if: github.event.inputs.release_tag != ''
run: |
curl -X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
https://api.github.com/repos/${{ github.repository }}/actions/workflows/build-osx.yml/dispatches \
-d "{
\"ref\": \"master\",
\"inputs\": {
\"release_tag\": \"${{ github.event.inputs.release_tag }}\"
}
}"
- name: Trigger build windows desktop
if: github.event.inputs.release_tag != ''
run: |
curl -X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
https://api.github.com/repos/${{ github.repository }}/actions/workflows/build-windows-desktop.yml/dispatches \
-d "{
\"ref\": \"master\",
\"inputs\": {
\"release_tag\": \"${{ github.event.inputs.release_tag }}\"
}
}"

View File

@@ -27,6 +27,9 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.2.2
with:
submodules: 'recursive'
fetch-depth: '0'
- name: Setup - name: Setup
uses: actions/setup-dotnet@v4.3.0 uses: actions/setup-dotnet@v4.3.0
@@ -36,13 +39,13 @@ jobs:
- name: Build - name: Build
run: | run: |
cd v2rayN cd v2rayN
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPath64 dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 --self-contained=true -o $OutputPath64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPathArm64 dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 --self-contained=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPath64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 --self-contained=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPathArm64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4.6.0 uses: actions/upload-artifact@v4.6.1
with: with:
name: v2rayN-linux name: v2rayN-linux
path: | path: |
@@ -68,9 +71,8 @@ jobs:
- name: Package AppImage - name: Package AppImage
if: github.event.inputs.release_tag != '' if: github.event.inputs.release_tag != ''
run: | run: |
chmod 755 package-appimage.sh chmod a+x package-appimage.sh
./package-appimage.sh $OutputArch $OutputPath64 ${{ github.event.inputs.release_tag }} ./package-appimage.sh
./package-appimage.sh $OutputArchArm $OutputPathArm64 ${{ github.event.inputs.release_tag }}
- name: Upload AppImage to release - name: Upload AppImage to release
uses: svenstaro/upload-release-action@v2 uses: svenstaro/upload-release-action@v2

View File

@@ -27,6 +27,9 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.2.2
with:
submodules: 'recursive'
fetch-depth: '0'
- name: Setup - name: Setup
uses: actions/setup-dotnet@v4.3.0 uses: actions/setup-dotnet@v4.3.0
@@ -36,13 +39,13 @@ jobs:
- name: Build - name: Build
run: | run: |
cd v2rayN cd v2rayN
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-x64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPath64 dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-x64 --self-contained=true -o $OutputPath64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPathArm64 dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-arm64 --self-contained=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-x64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPath64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-x64 --self-contained=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPathArm64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4.6.0 uses: actions/upload-artifact@v4.6.1
with: with:
name: v2rayN-macos name: v2rayN-macos
path: | path: |

View File

@@ -27,6 +27,9 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.2.2
with:
submodules: 'recursive'
fetch-depth: '0'
- name: Setup - name: Setup
uses: actions/setup-dotnet@v4.3.0 uses: actions/setup-dotnet@v4.3.0
@@ -36,15 +39,33 @@ jobs:
- name: Build - name: Build
run: | run: |
cd v2rayN cd v2rayN
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r win-x64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPath64 dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r win-arm64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPathArm64 dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -p:EnableWindowsTargeting=true -o $OutputPath64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -p:EnableWindowsTargeting=true -o $OutputPathArm64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4.6.0 uses: actions/upload-artifact@v4.6.1
with: with:
name: v2rayN-windows-desktop name: v2rayN-windows-desktop
path: | path: |
${{ github.workspace }}/v2rayN/Release/windows* ${{ github.workspace }}/v2rayN/Release/windows*
# release zip archive
- name: Package release zip archive
if: github.event.inputs.release_tag != ''
run: |
chmod 755 package-release-zip.sh
./package-release-zip.sh $OutputArch $OutputPath64
mv "v2rayN-${OutputArch}.zip" "v2rayN-${OutputArch}-desktop.zip"
./package-release-zip.sh $OutputArchArm $OutputPathArm64
mv "v2rayN-${OutputArchArm}.zip" "v2rayN-${OutputArchArm}-desktop.zip"
- name: Upload zip archive to release
uses: svenstaro/upload-release-action@v2
if: github.event.inputs.release_tag != ''
with:
file: ${{ github.workspace }}/v2rayN*.zip
tag: ${{ github.event.inputs.release_tag }}
file_glob: true
prerelease: true

View File

@@ -37,16 +37,16 @@ jobs:
- name: Build - name: Build
run: | run: |
cd v2rayN cd v2rayN
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained=false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPath64 dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-arm64 --self-contained=false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPathArm64 dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-arm64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPath64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPathArm64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64Sc
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4.6.0 uses: actions/upload-artifact@v4.6.1
with: with:
name: v2rayN-windows name: v2rayN-windows
path: | path: |

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "v2rayN/GlobalHotKeys"]
path = v2rayN/GlobalHotKeys
url = https://github.com/2dust/GlobalHotKeys

View File

@@ -1,71 +1,14 @@
#!/bin/bash #!/bin/bash
Arch="$1" sudo apt update -y
OutputPath="$2"
Version="$3"
FileName="v2rayN-${Arch}.zip"
wget -nv -O $FileName "https://github.com/2dust/v2rayN-core-bin/raw/refs/heads/master/$FileName"
7z x $FileName -aoa
cp -rf v2rayN-${Arch}/* $OutputPath
PackagePath="v2rayN-Package-${Arch}"
mkdir -p "${PackagePath}/AppDir/opt"
cp -rf $OutputPath "${PackagePath}/AppDir/opt/v2rayN"
echo "When this file exists, app will not store configs under this folder" >"${PackagePath}/AppDir/opt/v2rayN/NotStoreConfigHere.txt"
if [ $Arch = "linux-64" ]; then
Arch2="x86_64"
Arch3="amd64"
else
Arch2="aarch64"
Arch3="arm64"
fi
echo $Arch2
# basic
cat >"${PackagePath}/AppDir/AppRun" <<-EOF
#!/bin/sh
HERE="\$(dirname "\$(readlink -f "\${0}")")"
export PATH="\${HERE}"/opt/v2rayN/:"\${PATH}"
export LD_LIBRARY_PATH="\${HERE}"/opt/v2rayN/:"\${LD_LIBRARY_PATH}"
exec "\${HERE}/opt/v2rayN/v2rayN" \$@
EOF
cat >"${PackagePath}/AppDir/v2rayN.desktop" <<-EOF
[Desktop Entry]
Name=v2rayN
Comment=A GUI client for Windows and Linux, support Xray core and sing-box-core and others
Exec=v2rayN
Icon=v2rayN
Terminal=false
Type=Application
Categories=Network;
EOF
sudo cp "${PackagePath}/AppDir/opt/v2rayN/v2rayN.png" "${PackagePath}/AppDir/v2rayN.png"
sudo dpkg --add-architecture ${Arch3}
mkdir deb_folder
cd deb_folder
apt download libicu74:${Arch3}
apt download libfontconfig1:${Arch3} || true
apt download libfontconfig:${Arch3} || true
mkdir ../output_folder
for deb in *.deb; do
dpkg-deb -x "$deb" ../output_folder/
done
cd ..
find output_folder -type f -name "*.so*" -exec cp {} ${PackagePath}/AppDir/opt/v2rayN/ \;
find output_folder -type l -name "*.so*" -exec cp {} ${PackagePath}/AppDir/opt/v2rayN/ \;
rm -rf deb_folder output_folder
sudo chmod 0755 "${PackagePath}/AppDir/opt/v2rayN/v2rayN"
sudo chmod 0755 "${PackagePath}/AppDir/AppRun"
# desktop && PATH
wget "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
chmod a+x appimagetool-x86_64.AppImage
sudo apt install -y libfuse2 sudo apt install -y libfuse2
sudo ./appimagetool-x86_64.AppImage "${PackagePath}/AppDir" wget -O pkg2appimage https://github.com/AppImageCommunity/pkg2appimage/releases/download/continuous/pkg2appimage-1eceb30-x86_64.AppImage
sudo mv "v2rayN-${Arch2}.AppImage" "v2rayN-${Arch}.AppImage" chmod a+x pkg2appimage
export AppImageOutputArch=$OutputArch
export OutputPath=$OutputPath64
./pkg2appimage ./pkg2appimage.yml
mv out/*.AppImage v2rayN-${AppImageOutputArch}.AppImage
export AppImageOutputArch=$OutputArchArm
export OutputPath=$OutputPathArm64
./pkg2appimage ./pkg2appimage.yml
mv out/*.AppImage v2rayN-${AppImageOutputArch}.AppImage

37
pkg2appimage.yml Normal file
View File

@@ -0,0 +1,37 @@
app: v2rayN
binpatch: true
ingredients:
script:
- export FileName="v2rayN-${AppImageOutputArch}.zip"
- wget -nv -O $FileName "https://github.com/2dust/v2rayN-core-bin/raw/refs/heads/master/${FileName}"
- 7z x $FileName -aoa
- cp -rf v2rayN-${AppImageOutputArch}/* $OutputPath
script:
- mkdir -p usr/bin usr/lib
- cp -rf $OutputPath usr/lib/v2rayN
- echo "When this file exists, app will not store configs under this folder" > usr/lib/v2rayN/NotStoreConfigHere.txt
- ln -sf usr/lib/v2rayN/v2rayN usr/bin/v2rayN
- chmod a+x usr/lib/v2rayN/v2rayN
- find usr -type f -exec sh -c 'file "{}" | grep -qi "executable" && chmod +x "{}"' \;
- install -Dm644 usr/lib/v2rayN/v2rayN.png v2rayN.png
- install -Dm644 usr/lib/v2rayN/v2rayN.png usr/share/pixmaps/v2rayN.png
- cat > v2rayN.desktop <<EOF
- [Desktop Entry]
- Name=v2rayN
- Comment=A GUI client for Windows and Linux, support Xray core and sing-box-core and others
- Exec=v2rayN
- Icon=v2rayN
- Terminal=false
- Type=Application
- Categories=Network;
- EOF
- install -Dm644 v2rayN.desktop usr/share/applications/v2rayN.desktop
- cat > AppRun <<\EOF
- #!/bin/sh
- HERE="$(dirname "$(readlink -f "${0}")")"
- cd ${HERE}/usr/lib/v2rayN
- exec ${HERE}/usr/lib/v2rayN/v2rayN $@
- EOF
- chmod a+x AppRun

View File

@@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>7.8.1</Version> <Version>7.10.0</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
@@ -28,6 +28,6 @@
<UseSystemResourceKeys>true</UseSystemResourceKeys> <UseSystemResourceKeys>true</UseSystemResourceKeys>
<PublishSingleFile>true</PublishSingleFile> <PublishSingleFile>true</PublishSingleFile>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract> <PublishReadyToRun>false</PublishReadyToRun>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@@ -5,11 +5,11 @@
<CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled> <CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.2.3" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.2.4" />
<PackageVersion Include="Avalonia.Desktop" Version="11.2.3" /> <PackageVersion Include="Avalonia.Desktop" Version="11.2.4" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.2.3" /> <PackageVersion Include="Avalonia.Diagnostics" Version="11.2.4" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.2.3" /> <PackageVersion Include="Avalonia.ReactiveUI" Version="11.2.4" />
<PackageVersion Include="CliWrap" Version="3.7.1" /> <PackageVersion Include="CliWrap" Version="3.8.1" />
<PackageVersion Include="Downloader" Version="3.3.3" /> <PackageVersion Include="Downloader" Version="3.3.3" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.2.0" /> <PackageVersion Include="H.NotifyIcon.Wpf" Version="2.2.0" />
<PackageVersion Include="MaterialDesignThemes" Version="5.2.1" /> <PackageVersion Include="MaterialDesignThemes" Version="5.2.1" />
@@ -22,9 +22,9 @@
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.4" /> <PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.4" />
<PackageVersion Include="Splat.NLog" Version="15.3.1" /> <PackageVersion Include="Splat.NLog" Version="15.3.1" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" /> <PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="TaskScheduler" Version="2.11.0" /> <PackageVersion Include="TaskScheduler" Version="2.12.0" />
<PackageVersion Include="WebDav.Client" Version="2.8.0" /> <PackageVersion Include="WebDav.Client" Version="2.8.0" />
<PackageVersion Include="YamlDotNet" Version="16.3.0" /> <PackageVersion Include="YamlDotNet" Version="16.3.0" />
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" /> <PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" />
</ItemGroup> </ItemGroup>
</Project> </Project>

1
v2rayN/GlobalHotKeys Submodule

Submodule v2rayN/GlobalHotKeys added at 35901e2eaa

View File

@@ -20,9 +20,13 @@ public static class ProcUtils
try try
{ {
if (fileName.Contains(' ')) if (fileName.Contains(' '))
{
fileName = fileName.AppendQuotes(); fileName = fileName.AppendQuotes();
}
if (arguments.Contains(' ')) if (arguments.Contains(' '))
{
arguments = arguments.AppendQuotes(); arguments = arguments.AppendQuotes();
}
Process proc = new() Process proc = new()
{ {
@@ -86,17 +90,43 @@ public static class ProcUtils
GetProcessKeyInfo(proc, review, out var procId, out var fileName, out var processName); GetProcessKeyInfo(proc, review, out var procId, out var fileName, out var processName);
try try
{ proc?.Kill(true); } {
catch (Exception ex) { Logging.SaveLog(_tag, ex); } if (Utils.IsNonWindows())
{
proc?.Kill(true);
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
try try
{ proc?.Kill(); } {
catch (Exception ex) { Logging.SaveLog(_tag, ex); } proc?.Kill();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
try try
{ proc?.Close(); } {
catch (Exception ex) { Logging.SaveLog(_tag, ex); } proc?.Close();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
try try
{ proc?.Dispose(); } {
catch (Exception ex) { Logging.SaveLog(_tag, ex); } proc?.Dispose();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
await Task.Delay(300); await Task.Delay(300);
await ProcessKillByKeyInfo(review, procId, fileName, processName); await ProcessKillByKeyInfo(review, procId, fileName, processName);

View File

@@ -433,10 +433,22 @@ namespace ServiceLib.Common
{ {
try try
{ {
var ipProperties = IPGlobalProperties.GetIPGlobalProperties(); List<IPEndPoint> lstIpEndPoints = new();
var ipEndPoints = ipProperties.GetActiveTcpListeners(); List<TcpConnectionInformation> lstTcpConns = new();
//var lstIpEndPoints = new List<IPEndPoint>(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners());
return ipEndPoints.Any(endPoint => endPoint.Port == port); lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners());
lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners());
lstTcpConns.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections());
if (lstIpEndPoints?.FindIndex(it => it.Port == port) >= 0)
{
return true;
}
if (lstTcpConns?.FindIndex(it => it.LocalEndPoint.Port == port) >= 0)
{
return true;
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -786,6 +798,24 @@ namespace ServiceLib.Common
} }
} }
public static string GetBinConfigPath(string filename = "")
{
var tempPath = Path.Combine(StartupPath(), "binConfigs");
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
}
if (Utils.IsNullOrEmpty(filename))
{
return tempPath;
}
else
{
return Path.Combine(tempPath, filename);
}
}
#endregion TempPath #endregion TempPath
#region Platform #region Platform
@@ -827,7 +857,7 @@ namespace ServiceLib.Common
private static async Task<string?> GetLinuxUserId() private static async Task<string?> GetLinuxUserId()
{ {
var arg = new List<string>() { "-c", "id -u" }; var arg = new List<string>() { "-c", "id -u" };
return await GetCliWrapOutput("/bin/bash", arg); return await GetCliWrapOutput(Global.LinuxBash, arg);
} }
public static async Task<string?> SetLinuxChmod(string? fileName) public static async Task<string?> SetLinuxChmod(string? fileName)
@@ -838,14 +868,14 @@ namespace ServiceLib.Common
fileName = fileName.AppendQuotes(); fileName = fileName.AppendQuotes();
//File.SetUnixFileMode(fileName, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute); //File.SetUnixFileMode(fileName, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
var arg = new List<string>() { "-c", $"chmod +x {fileName}" }; var arg = new List<string>() { "-c", $"chmod +x {fileName}" };
return await GetCliWrapOutput("/bin/bash", arg); return await GetCliWrapOutput(Global.LinuxBash, arg);
} }
public static async Task<string?> GetLinuxFontFamily(string lang) public static async Task<string?> GetLinuxFontFamily(string lang)
{ {
// var arg = new List<string>() { "-c", $"fc-list :lang={lang} family" }; // var arg = new List<string>() { "-c", $"fc-list :lang={lang} family" };
var arg = new List<string>() { "-c", $"fc-list : family" }; var arg = new List<string>() { "-c", $"fc-list : family" };
return await GetCliWrapOutput("/bin/bash", arg); return await GetCliWrapOutput(Global.LinuxBash, arg);
} }
public static string? GetHomePath() public static string? GetHomePath()
@@ -855,12 +885,6 @@ namespace ServiceLib.Common
: Environment.GetEnvironmentVariable("HOME"); : Environment.GetEnvironmentVariable("HOME");
} }
public static async Task<string?> GetListNetworkServices()
{
var arg = new List<string>() { "-c", $"networksetup -listallnetworkservices" };
return await GetCliWrapOutput("/bin/bash", arg);
}
#endregion Platform #endregion Platform
} }
} }

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums
{ {
public enum EMsgCommand public enum EMsgCommand
{ {
@@ -6,7 +6,6 @@
SendMsgView, SendMsgView,
SendSnackMsg, SendSnackMsg,
RefreshProfiles, RefreshProfiles,
StopSpeedtest,
AppExit AppExit
} }
} }

View File

@@ -16,7 +16,7 @@ namespace ServiceLib
public const string ConfigFileName = "guiNConfig.json"; public const string ConfigFileName = "guiNConfig.json";
public const string CoreConfigFileName = "config.json"; public const string CoreConfigFileName = "config.json";
public const string CorePreConfigFileName = "configPre.json"; public const string CorePreConfigFileName = "configPre.json";
public const string CoreSpeedtestConfigFileName = "configSpeedtest.json"; public const string CoreSpeedtestConfigFileName = "configTest{0}.json";
public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json"; public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json";
public const string ClashMixinConfigFileName = "Mixin.yaml"; public const string ClashMixinConfigFileName = "Mixin.yaml";
@@ -38,6 +38,8 @@ namespace ServiceLib
public const string ClashTunYaml = NamespaceSample + "clash_tun_yaml"; public const string ClashTunYaml = NamespaceSample + "clash_tun_yaml";
public const string LinuxAutostartConfig = NamespaceSample + "linux_autostart_config"; public const string LinuxAutostartConfig = NamespaceSample + "linux_autostart_config";
public const string PacFileName = NamespaceSample + "pac"; public const string PacFileName = NamespaceSample + "pac";
public const string ProxySetOSXShellFileName = NamespaceSample + "proxy_set_osx_sh";
public const string ProxySetLinuxShellFileName = NamespaceSample + "proxy_set_linux_sh";
public const string DefaultSecurity = "auto"; public const string DefaultSecurity = "auto";
public const string DefaultNetwork = "tcp"; public const string DefaultNetwork = "tcp";
@@ -64,15 +66,14 @@ namespace ServiceLib
public const string GrpcGunMode = "gun"; public const string GrpcGunMode = "gun";
public const string GrpcMultiMode = "multi"; public const string GrpcMultiMode = "multi";
public const int MaxPort = 65536; public const int MaxPort = 65536;
public const string DelayUnit = "";
public const string SpeedUnit = "";
public const int MinFontSize = 8; public const int MinFontSize = 8;
public const string RebootAs = "rebootas"; public const string RebootAs = "rebootas";
public const string AvaAssets = "avares://v2rayN/Assets/"; public const string AvaAssets = "avares://v2rayN/Assets/";
public const string LocalAppData = "V2RAYN_LOCAL_APPLICATION_DATA"; public const string LocalAppData = "V2RAYN_LOCAL_APPLICATION_DATA_V2";
public const string V2RayLocalAsset = "V2RAY_LOCATION_ASSET"; public const string V2RayLocalAsset = "V2RAY_LOCATION_ASSET";
public const string XrayLocalAsset = "XRAY_LOCATION_ASSET"; public const string XrayLocalAsset = "XRAY_LOCATION_ASSET";
public const int SpeedTestPageSize = 1000; public const int SpeedTestPageSize = 1000;
public const string LinuxBash = "/bin/bash";
public static readonly List<string> IEProxyProtocols = public static readonly List<string> IEProxyProtocols =
[ [
@@ -105,10 +106,10 @@ namespace ServiceLib
public static readonly List<string> SpeedTestUrls = public static readonly List<string> SpeedTestUrls =
[ [
@"https://speed.cloudflare.com/__down?bytes=100000000", @"https://cachefly.cachefly.net/50mb.test",
@"https://speed.cloudflare.com/__down?bytes=50000000",
@"https://speed.cloudflare.com/__down?bytes=10000000", @"https://speed.cloudflare.com/__down?bytes=10000000",
@"https://cachefly.cachefly.net/50mb.test" @"https://speed.cloudflare.com/__down?bytes=50000000",
@"https://speed.cloudflare.com/__down?bytes=100000000",
]; ];
public static readonly List<string> SpeedPingTestUrls = public static readonly List<string> SpeedPingTestUrls =
@@ -428,12 +429,12 @@ namespace ServiceLib
"fakedns+others" "fakedns+others"
]; ];
public static readonly List<string> TunMtus = public static readonly List<int> TunMtus =
[ [
"1280", 1280,
"1408", 1408,
"1500", 1500,
"9000" 9000
]; ];
public static readonly List<string> TunStacks = public static readonly List<string> TunStacks =
@@ -509,6 +510,13 @@ namespace ServiceLib
{ ECoreType.v2rayN, "2dust/v2rayN" }, { ECoreType.v2rayN, "2dust/v2rayN" },
}; };
public static readonly List<string> OtherGeoUrls =
[
@"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/geoip-only-cn-private.dat",
@"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb",
@"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb"
];
#endregion const #endregion const
} }
} }

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Handler namespace ServiceLib.Handler
{ {
public sealed class AppHandler public sealed class AppHandler
{ {
@@ -98,6 +98,7 @@
{ {
FileManager.DeleteExpiredFiles(Utils.GetLogPath(), DateTime.Now.AddMonths(-1)); FileManager.DeleteExpiredFiles(Utils.GetLogPath(), DateTime.Now.AddMonths(-1));
FileManager.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1)); FileManager.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1));
FileManager.DeleteExpiredFiles(Utils.GetBinConfigPath(), DateTime.Now.AddHours(-1));
}); });
} }
@@ -252,4 +253,4 @@
#endregion Core Type #endregion Core Type
} }
} }

View File

@@ -177,7 +177,7 @@ namespace ServiceLib.Handler
if (File.Exists(launchAgentPath)) if (File.Exists(launchAgentPath))
{ {
var args = new[] { "-c", $"launchctl unload -w \"{launchAgentPath}\"" }; var args = new[] { "-c", $"launchctl unload -w \"{launchAgentPath}\"" };
await Utils.GetCliWrapOutput("/bin/bash", args); await Utils.GetCliWrapOutput(Global.LinuxBash, args);
File.Delete(launchAgentPath); File.Delete(launchAgentPath);
} }
@@ -197,7 +197,7 @@ namespace ServiceLib.Handler
await File.WriteAllTextAsync(launchAgentPath, plistContent); await File.WriteAllTextAsync(launchAgentPath, plistContent);
var args = new[] { "-c", $"launchctl load -w \"{launchAgentPath}\"" }; var args = new[] { "-c", $"launchctl load -w \"{launchAgentPath}\"" };
await Utils.GetCliWrapOutput("/bin/bash", args); await Utils.GetCliWrapOutput(Global.LinuxBash, args);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -1,4 +1,4 @@
using static ServiceLib.Models.ClashProxies; using static ServiceLib.Models.ClashProxies;
namespace ServiceLib.Handler namespace ServiceLib.Handler
{ {
@@ -38,63 +38,63 @@ namespace ServiceLib.Handler
public void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Action<ClashProxyModel?, string> updateFunc) public void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Action<ClashProxyModel?, string> updateFunc)
{ {
Task.Run(() => Task.Run(() =>
{
if (blAll)
{ {
for (int i = 0; i < 5; i++) if (blAll)
{ {
if (_proxies != null) for (var i = 0; i < 5; i++)
{ {
break; if (_proxies != null)
{
break;
}
Task.Delay(5000).Wait();
}
if (_proxies == null)
{
return;
}
lstProxy = new List<ClashProxyModel>();
foreach (KeyValuePair<string, ProxiesItem> kv in _proxies)
{
if (Global.notAllowTestType.Contains(kv.Value.type.ToLower()))
{
continue;
}
lstProxy.Add(new ClashProxyModel()
{
Name = kv.Value.name,
Type = kv.Value.type.ToLower(),
});
} }
Task.Delay(5000).Wait();
} }
if (_proxies == null)
if (lstProxy == null)
{ {
return; return;
} }
lstProxy = new List<ClashProxyModel>(); var urlBase = $"{GetApiUrl()}/proxies";
foreach (KeyValuePair<string, ProxiesItem> kv in _proxies) urlBase += @"/{0}/delay?timeout=10000&url=" + AppHandler.Instance.Config.SpeedTestItem.SpeedPingTestUrl;
var tasks = new List<Task>();
foreach (var it in lstProxy)
{ {
if (Global.notAllowTestType.Contains(kv.Value.type.ToLower())) if (Global.notAllowTestType.Contains(it.Type.ToLower()))
{ {
continue; continue;
} }
lstProxy.Add(new ClashProxyModel() var name = it.Name;
var url = string.Format(urlBase, name);
tasks.Add(Task.Run(async () =>
{ {
Name = kv.Value.name, var result = await HttpClientHelper.Instance.TryGetAsync(url);
Type = kv.Value.type.ToLower(), updateFunc?.Invoke(it, result);
}); }));
} }
} Task.WaitAll(tasks.ToArray());
if (lstProxy == null) Task.Delay(1000).Wait();
{ updateFunc?.Invoke(null, "");
return; });
}
var urlBase = $"{GetApiUrl()}/proxies";
urlBase += @"/{0}/delay?timeout=10000&url=" + AppHandler.Instance.Config.SpeedTestItem.SpeedPingTestUrl;
List<Task> tasks = new List<Task>();
foreach (var it in lstProxy)
{
if (Global.notAllowTestType.Contains(it.Type.ToLower()))
{
continue;
}
var name = it.Name;
var url = string.Format(urlBase, name);
tasks.Add(Task.Run(async () =>
{
var result = await HttpClientHelper.Instance.TryGetAsync(url);
updateFunc?.Invoke(it, result);
}));
}
Task.WaitAll(tasks.ToArray());
Task.Delay(1000).Wait();
updateFunc?.Invoke(null, "");
});
} }
public List<ProxiesItem>? GetClashProxyGroups() public List<ProxiesItem>? GetClashProxyGroups()
@@ -120,7 +120,7 @@ namespace ServiceLib.Handler
try try
{ {
var url = $"{GetApiUrl()}/proxies/{name}"; var url = $"{GetApiUrl()}/proxies/{name}";
Dictionary<string, string> headers = new Dictionary<string, string>(); var headers = new Dictionary<string, string>();
headers.Add("name", nameNode); headers.Add("name", nameNode);
await HttpClientHelper.Instance.PutAsync(url, headers); await HttpClientHelper.Instance.PutAsync(url, headers);
} }
@@ -148,7 +148,7 @@ namespace ServiceLib.Handler
try try
{ {
var url = $"{GetApiUrl()}/configs?force=true"; var url = $"{GetApiUrl()}/configs?force=true";
Dictionary<string, string> headers = new Dictionary<string, string>(); var headers = new Dictionary<string, string>();
headers.Add("path", filePath); headers.Add("path", filePath);
await HttpClientHelper.Instance.PutAsync(url, headers); await HttpClientHelper.Instance.PutAsync(url, headers);
} }
@@ -194,4 +194,4 @@ namespace ServiceLib.Handler
return $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort2}"; return $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort2}";
} }
} }
} }

View File

@@ -125,6 +125,10 @@ namespace ServiceLib.Handler
{ {
config.SpeedTestItem.SpeedPingTestUrl = Global.SpeedPingTestUrl; config.SpeedTestItem.SpeedPingTestUrl = Global.SpeedPingTestUrl;
} }
if (config.SpeedTestItem.MixedConcurrencyCount < 1)
{
config.SpeedTestItem.MixedConcurrencyCount = 5;
}
config.Mux4RayItem ??= new() config.Mux4RayItem ??= new()
{ {
@@ -154,6 +158,7 @@ namespace ServiceLib.Handler
Length = "100-200", Length = "100-200",
Interval = "10-20" Interval = "10-20"
}; };
config.GlobalHotkeys ??= new();
if (config.SystemProxyItem.SystemProxyExceptions.IsNullOrEmpty()) if (config.SystemProxyItem.SystemProxyExceptions.IsNullOrEmpty())
{ {
@@ -290,7 +295,7 @@ namespace ServiceLib.Handler
/// <param name="config"></param> /// <param name="config"></param>
/// <param name="indexes"></param> /// <param name="indexes"></param>
/// <returns></returns> /// <returns></returns>
public static async Task<int> RemoveServer(Config config, List<ProfileItem> indexes) public static async Task<int> RemoveServers(Config config, List<ProfileItem> indexes)
{ {
var subid = "TempRemoveSubId"; var subid = "TempRemoveSubId";
foreach (var item in indexes) foreach (var item in indexes)
@@ -299,7 +304,7 @@ namespace ServiceLib.Handler
} }
await SQLiteHelper.Instance.UpdateAllAsync(indexes); await SQLiteHelper.Instance.UpdateAllAsync(indexes);
await RemoveServerViaSubid(config, subid, false); await RemoveServersViaSubid(config, subid, false);
return 0; return 0;
} }
@@ -720,7 +725,7 @@ namespace ServiceLib.Handler
profileItem.Network = string.Empty; profileItem.Network = string.Empty;
if (profileItem.ShortId.IsNullOrEmpty()) if (profileItem.ShortId.IsNullOrEmpty())
{ {
profileItem.ShortId = Global.TunMtus.FirstOrDefault(); profileItem.ShortId = Global.TunMtus.First().ToString();
} }
if (profileItem.Id.IsNullOrEmpty()) if (profileItem.Id.IsNullOrEmpty())
@@ -754,9 +759,9 @@ namespace ServiceLib.Handler
Security = t.Security, Security = t.Security,
Network = t.Network, Network = t.Network,
StreamSecurity = t.StreamSecurity, StreamSecurity = t.StreamSecurity,
Delay = t33 == null ? 0 : t33.Delay, Delay = t33?.Delay ?? 0,
Speed = t33 == null ? 0 : t33.Speed, Speed = t33?.Speed ?? 0,
Sort = t33 == null ? 0 : t33.Sort Sort = t33?.Sort ?? 0
}).ToList(); }).ToList();
Enum.TryParse(colName, true, out EServerColName name); Enum.TryParse(colName, true, out EServerColName name);
@@ -882,7 +887,7 @@ namespace ServiceLib.Handler
lstRemove.Add(item); lstRemove.Add(item);
} }
} }
await RemoveServer(config, lstRemove); await RemoveServers(config, lstRemove);
return new Tuple<int, int>(lstProfile.Count, lstKeep.Count); return new Tuple<int, int>(lstProfile.Count, lstKeep.Count);
} }
@@ -1047,6 +1052,24 @@ namespace ServiceLib.Handler
return itemSocks; return itemSocks;
} }
public static async Task<int> RemoveInvalidServerResult(Config config, string subid)
{
var lstModel = await AppHandler.Instance.ProfileItems(subid, "");
if (lstModel is { Count: <= 0 })
{
return -1;
}
var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs();
var lstProfile = (from t in lstModel
join t2 in lstProfileExs on t.IndexId equals t2.IndexId
where t2.Delay == -1
select t).ToList();
await RemoveServers(config, JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(lstProfile)));
return lstProfile.Count;
}
#endregion Server #endregion Server
#region Batch add servers #region Batch add servers
@@ -1069,7 +1092,7 @@ namespace ServiceLib.Handler
//remove sub items //remove sub items
if (isSub && Utils.IsNotEmpty(subid)) if (isSub && Utils.IsNotEmpty(subid))
{ {
await RemoveServerViaSubid(config, subid, isSub); await RemoveServersViaSubid(config, subid, isSub);
subFilter = (await AppHandler.Instance.GetSubItem(subid))?.Filter ?? ""; subFilter = (await AppHandler.Instance.GetSubItem(subid))?.Filter ?? "";
} }
@@ -1163,7 +1186,7 @@ namespace ServiceLib.Handler
{ {
if (isSub && Utils.IsNotEmpty(subid)) if (isSub && Utils.IsNotEmpty(subid))
{ {
await RemoveServerViaSubid(config, subid, isSub); await RemoveServersViaSubid(config, subid, isSub);
} }
int count = 0; int count = 0;
foreach (var it in lstProfiles) foreach (var it in lstProfiles)
@@ -1219,7 +1242,7 @@ namespace ServiceLib.Handler
if (isSub && Utils.IsNotEmpty(subid)) if (isSub && Utils.IsNotEmpty(subid))
{ {
await RemoveServerViaSubid(config, subid, isSub); await RemoveServersViaSubid(config, subid, isSub);
} }
profileItem.Subid = subid; profileItem.Subid = subid;
@@ -1244,7 +1267,7 @@ namespace ServiceLib.Handler
if (isSub && Utils.IsNotEmpty(subid)) if (isSub && Utils.IsNotEmpty(subid))
{ {
await RemoveServerViaSubid(config, subid, isSub); await RemoveServersViaSubid(config, subid, isSub);
} }
var lstSsServer = ShadowsocksFmt.ResolveSip008(strData); var lstSsServer = ShadowsocksFmt.ResolveSip008(strData);
@@ -1431,7 +1454,7 @@ namespace ServiceLib.Handler
/// <param name="config"></param> /// <param name="config"></param>
/// <param name="subid"></param> /// <param name="subid"></param>
/// <returns></returns> /// <returns></returns>
public static async Task<int> RemoveServerViaSubid(Config config, string subid, bool isSub) public static async Task<int> RemoveServersViaSubid(Config config, string subid, bool isSub)
{ {
if (Utils.IsNullOrEmpty(subid)) if (Utils.IsNullOrEmpty(subid))
{ {
@@ -1462,7 +1485,7 @@ namespace ServiceLib.Handler
return 0; return 0;
} }
await SQLiteHelper.Instance.DeleteAsync(item); await SQLiteHelper.Instance.DeleteAsync(item);
await RemoveServerViaSubid(config, id, false); await RemoveServersViaSubid(config, id, false);
return 0; return 0;
} }

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Handler namespace ServiceLib.Handler
{ {
/// <summary> /// <summary>
/// Core configuration file processing class /// Core configuration file processing class
@@ -109,6 +109,30 @@
return result; return result;
} }
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, ProfileItem node, ServerTestItem testItem, string fileName)
{
var result = new RetResult();
var initPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest);
var port = Utils.GetFreePort(initPort + testItem.QueueNum);
testItem.Port = port;
if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
{
result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(node, port);
}
else
{
result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(node, port);
}
if (result.Success != true)
{
return result;
}
await File.WriteAllTextAsync(fileName, result.Data.ToString());
return result;
}
public static async Task<RetResult> GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, ECoreType coreType) public static async Task<RetResult> GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, ECoreType coreType)
{ {
var result = new RetResult(); var result = new RetResult();
@@ -129,4 +153,4 @@
return result; return result;
} }
} }
} }

View File

@@ -70,7 +70,7 @@ namespace ServiceLib.Handler
return; return;
} }
var fileName = Utils.GetConfigPath(Global.CoreConfigFileName); var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName);
var result = await CoreConfigHandler.GenerateClientConfig(node, fileName); var result = await CoreConfigHandler.GenerateClientConfig(node, fileName);
if (result.Success != true) if (result.Success != true)
{ {
@@ -101,7 +101,8 @@ namespace ServiceLib.Handler
public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds) public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
{ {
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard) ? ECoreType.sing_box : ECoreType.Xray; var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard) ? ECoreType.sing_box : ECoreType.Xray;
var configPath = Utils.GetConfigPath(Global.CoreSpeedtestConfigFileName); var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
var configPath = Utils.GetBinConfigPath(fileName);
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType); var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
UpdateFunc(false, result.Msg); UpdateFunc(false, result.Msg);
if (result.Success != true) if (result.Success != true)
@@ -113,7 +114,34 @@ namespace ServiceLib.Handler
UpdateFunc(false, configPath); UpdateFunc(false, configPath);
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var proc = await RunProcess(coreInfo, Global.CoreSpeedtestConfigFileName, true, false); var proc = await RunProcess(coreInfo, fileName, true, false);
if (proc is null)
{
return -1;
}
return proc.Id;
}
public async Task<int> LoadCoreConfigSpeedtest(ServerTestItem testItem)
{
var node = await AppHandler.Instance.GetProfileItem(testItem.IndexId);
if (node is null)
{
return -1;
}
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
var configPath = Utils.GetBinConfigPath(fileName);
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, node, testItem, configPath);
if (result.Success != true)
{
return -1;
}
var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var proc = await RunProcess(coreInfo, fileName, true, false);
if (proc is null) if (proc is null)
{ {
return -1; return -1;
@@ -175,7 +203,7 @@ namespace ServiceLib.Handler
if (itemSocks != null) if (itemSocks != null)
{ {
var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box; var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box;
var fileName = Utils.GetConfigPath(Global.CorePreConfigFileName); var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName);
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName); var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName);
if (result.Success) if (result.Success)
{ {
@@ -225,8 +253,8 @@ namespace ServiceLib.Handler
StartInfo = new() StartInfo = new()
{ {
FileName = fileName, FileName = fileName,
Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetConfigPath(configPath) : configPath), Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath),
WorkingDirectory = Utils.GetConfigPath(), WorkingDirectory = Utils.GetBinConfigPath(),
UseShellExecute = false, UseShellExecute = false,
RedirectStandardOutput = displayLog, RedirectStandardOutput = displayLog,
RedirectStandardError = displayLog, RedirectStandardError = displayLog,
@@ -298,7 +326,7 @@ namespace ServiceLib.Handler
private async Task RunProcessAsLinuxSudo(Process proc, string fileName, CoreInfo coreInfo, string configPath) private async Task RunProcessAsLinuxSudo(Process proc, string fileName, CoreInfo coreInfo, string configPath)
{ {
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetConfigPath(configPath).AppendQuotes())}"; var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh"); var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
proc.StartInfo.FileName = shFilePath; proc.StartInfo.FileName = shFilePath;
@@ -352,7 +380,7 @@ namespace ServiceLib.Handler
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName) private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
{ {
//Shell scripts //Shell scripts
var shFilePath = Utils.GetBinPath(AppHandler.Instance.IsAdministrator ? "root_" + fileName : fileName); var shFilePath = Utils.GetBinConfigPath(AppHandler.Instance.IsAdministrator ? "root_" + fileName : fileName);
File.Delete(shFilePath); File.Delete(shFilePath);
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendLine("#!/bin/sh"); sb.AppendLine("#!/bin/sh");

View File

@@ -75,7 +75,7 @@ namespace ServiceLib.Handler
new CoreInfo new CoreInfo
{ {
CoreType = ECoreType.v2fly, CoreType = ECoreType.v2fly,
CoreExes = ["wv2ray", "v2ray"], CoreExes = ["v2ray"],
Arguments = "{0}", Arguments = "{0}",
Url = GetCoreUrl(ECoreType.v2fly), Url = GetCoreUrl(ECoreType.v2fly),
Match = "V2Ray", Match = "V2Ray",
@@ -95,7 +95,7 @@ namespace ServiceLib.Handler
new CoreInfo new CoreInfo
{ {
CoreType = ECoreType.Xray, CoreType = ECoreType.Xray,
CoreExes = ["wxray","xray"], CoreExes = ["xray"],
Arguments = "run -c {0}", Arguments = "run -c {0}",
Url = GetCoreUrl(ECoreType.Xray), Url = GetCoreUrl(ECoreType.Xray),
ReleaseApiUrl = urlXray.Replace(Global.GithubUrl, Global.GithubApiUrl), ReleaseApiUrl = urlXray.Replace(Global.GithubUrl, Global.GithubApiUrl),

View File

@@ -1,4 +1,4 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
//using System.Reactive.Linq; //using System.Reactive.Linq;
@@ -8,7 +8,7 @@ namespace ServiceLib.Handler
{ {
private static readonly Lazy<ProfileExHandler> _instance = new(() => new()); private static readonly Lazy<ProfileExHandler> _instance = new(() => new());
private ConcurrentBag<ProfileExItem> _lstProfileEx = []; private ConcurrentBag<ProfileExItem> _lstProfileEx = [];
private Queue<string> _queIndexIds = new(); private readonly Queue<string> _queIndexIds = new();
public static ProfileExHandler Instance => _instance.Value; public static ProfileExHandler Instance => _instance.Value;
private static readonly string _tag = "ProfileExHandler"; private static readonly string _tag = "ProfileExHandler";
@@ -59,7 +59,7 @@ namespace ServiceLib.Handler
List<ProfileExItem> lstInserts = []; List<ProfileExItem> lstInserts = [];
List<ProfileExItem> lstUpdates = []; List<ProfileExItem> lstUpdates = [];
for (int i = 0; i < cnt; i++) for (var i = 0; i < cnt; i++)
{ {
var id = _queIndexIds.Dequeue(); var id = _queIndexIds.Dequeue();
var item = lstExists.FirstOrDefault(t => t.IndexId == id); var item = lstExists.FirstOrDefault(t => t.IndexId == id);
@@ -78,13 +78,18 @@ namespace ServiceLib.Handler
lstInserts.Add(itemNew); lstInserts.Add(itemNew);
} }
} }
try try
{ {
if (lstInserts.Count() > 0) if (lstInserts.Count > 0)
{
await SQLiteHelper.Instance.InsertAllAsync(lstInserts); await SQLiteHelper.Instance.InsertAllAsync(lstInserts);
}
if (lstUpdates.Count() > 0) if (lstUpdates.Count > 0)
{
await SQLiteHelper.Instance.UpdateAllAsync(lstUpdates); await SQLiteHelper.Instance.UpdateAllAsync(lstUpdates);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -93,17 +98,24 @@ namespace ServiceLib.Handler
} }
} }
private void AddProfileEx(string indexId, ref ProfileExItem? profileEx) private ProfileExItem AddProfileEx(string indexId)
{ {
profileEx = new() var profileEx = new ProfileExItem()
{ {
IndexId = indexId, IndexId = indexId,
Delay = 0, Delay = 0,
Speed = 0, Speed = 0,
Sort = 0 Sort = 0,
Message = string.Empty
}; };
_lstProfileEx.Add(profileEx); _lstProfileEx.Add(profileEx);
IndexIdEnqueue(indexId); IndexIdEnqueue(indexId);
return profileEx;
}
private ProfileExItem GetProfileExItem(string? indexId)
{
return _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId) ?? AddProfileEx(indexId);
} }
public async Task ClearAll() public async Task ClearAll()
@@ -124,39 +136,34 @@ namespace ServiceLib.Handler
} }
} }
public void SetTestDelay(string indexId, string delayVal) public void SetTestDelay(string indexId, int delay)
{ {
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId); var profileEx = GetProfileExItem(indexId);
if (profileEx == null)
{
AddProfileEx(indexId, ref profileEx);
}
int.TryParse(delayVal, out int delay);
profileEx.Delay = delay; profileEx.Delay = delay;
IndexIdEnqueue(indexId); IndexIdEnqueue(indexId);
} }
public void SetTestSpeed(string indexId, string speedVal) public void SetTestSpeed(string indexId, decimal speed)
{ {
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId); var profileEx = GetProfileExItem(indexId);
if (profileEx == null)
{
AddProfileEx(indexId, ref profileEx);
}
decimal.TryParse(speedVal, out decimal speed);
profileEx.Speed = speed; profileEx.Speed = speed;
IndexIdEnqueue(indexId); IndexIdEnqueue(indexId);
} }
public void SetTestMessage(string indexId, string message)
{
var profileEx = GetProfileExItem(indexId);
profileEx.Message = message;
IndexIdEnqueue(indexId);
}
public void SetSort(string indexId, int sort) public void SetSort(string indexId, int sort)
{ {
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId); var profileEx = GetProfileExItem(indexId);
if (profileEx == null)
{
AddProfileEx(indexId, ref profileEx);
}
profileEx.Sort = sort; profileEx.Sort = sort;
IndexIdEnqueue(indexId); IndexIdEnqueue(indexId);
} }
@@ -180,4 +187,4 @@ namespace ServiceLib.Handler
return _lstProfileEx.Max(t => t == null ? 0 : t.Sort); return _lstProfileEx.Max(t => t == null ? 0 : t.Sort);
} }
} }
} }

View File

@@ -1,202 +1,33 @@
namespace ServiceLib.Handler.SysProxy namespace ServiceLib.Handler.SysProxy
{ {
public class ProxySettingLinux public class ProxySettingLinux
{ {
private static readonly string _proxySetFileName = $"{Global.ProxySetLinuxShellFileName.Replace(Global.NamespaceSample, "")}.sh";
public static async Task SetProxy(string host, int port, string exceptions) public static async Task SetProxy(string host, int port, string exceptions)
{ {
var lstCmd = GetSetCmds(host, port, exceptions); List<string> args = ["manual", host, port.ToString(), exceptions];
await ExecCmd(args);
await ExecCmd(lstCmd);
} }
public static async Task UnsetProxy() public static async Task UnsetProxy()
{ {
var lstCmd = GetUnsetCmds(); List<string> args = ["none"];
await ExecCmd(args);
await ExecCmd(lstCmd);
} }
private static async Task ExecCmd(List<CmdItem> lstCmd) private static async Task ExecCmd(List<string> args)
{ {
foreach (var cmd in lstCmd) var fileName = Utils.GetBinConfigPath(_proxySetFileName);
if (!File.Exists(fileName))
{ {
if (cmd is null || cmd.Cmd.IsNullOrEmpty() || cmd.Arguments is null) var contents = EmbedUtils.GetEmbedText(Global.ProxySetLinuxShellFileName);
{ await File.AppendAllTextAsync(fileName, contents);
continue;
}
await Task.Delay(10);
await Utils.GetCliWrapOutput(cmd.Cmd, cmd.Arguments);
}
}
private static List<CmdItem> GetSetCmds(string host, int port, string exceptions) await Utils.SetLinuxChmod(fileName);
{
var isKde = IsKde(out var configDir);
List<string> lstType = ["", "http", "https", "socks", "ftp"];
List<CmdItem> lstCmd = [];
//GNOME
foreach (var type in lstType)
{
lstCmd.AddRange(GetSetCmd4Gnome(type, host, port));
}
if (exceptions.IsNotEmpty())
{
lstCmd.AddRange(GetSetCmd4Gnome("exceptions", exceptions, 0));
} }
if (isKde) await Utils.GetCliWrapOutput(fileName, args);
{
foreach (var type in lstType)
{
lstCmd.AddRange(GetSetCmd4Kde(type, host, port, configDir));
}
if (exceptions.IsNotEmpty())
{
lstCmd.AddRange(GetSetCmd4Kde("exceptions", exceptions, 0, configDir));
}
// Notify system to reload
lstCmd.Add(new CmdItem()
{
Cmd = "dbus-send",
Arguments = ["--type=signal", "/KIO/Scheduler", "org.kde.KIO.Scheduler.reparseSlaveConfiguration", "string:''"]
});
}
return lstCmd;
}
private static List<CmdItem> GetUnsetCmds()
{
var isKde = IsKde(out var configDir);
List<CmdItem> lstCmd = [];
//GNOME
lstCmd.Add(new CmdItem()
{
Cmd = "gsettings",
Arguments = ["set", "org.gnome.system.proxy", "mode", "none"]
});
if (isKde)
{
lstCmd.Add(new CmdItem()
{
Cmd = GetKdeVersion(),
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "0"]
});
// Notify system to reload
lstCmd.Add(new CmdItem()
{
Cmd = "dbus-send",
Arguments = ["--type=signal", "/KIO/Scheduler", "org.kde.KIO.Scheduler.reparseSlaveConfiguration", "string:''"]
});
}
return lstCmd;
}
private static List<CmdItem> GetSetCmd4Kde(string type, string host, int port, string configDir)
{
List<CmdItem> lstCmd = [];
var cmd = GetKdeVersion();
if (type.IsNullOrEmpty())
{
lstCmd.Add(new()
{
Cmd = cmd,
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "1"]
});
}
else if (type == "exceptions")
{
lstCmd.Add(new()
{
Cmd = cmd,
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "NoProxyFor", host]
});
}
else
{
var type2 = type.Equals("https") ? "http" : type;
lstCmd.Add(new CmdItem()
{
Cmd = cmd,
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", $"{type}Proxy", $"{type2}://{host}:{port}"]
});
}
return lstCmd;
}
private static List<CmdItem> GetSetCmd4Gnome(string type, string host, int port)
{
List<CmdItem> lstCmd = [];
if (type.IsNullOrEmpty())
{
lstCmd.Add(new()
{
Cmd = "gsettings",
Arguments = ["set", "org.gnome.system.proxy", "mode", "manual"]
});
}
else if (type == "exceptions")
{
lstCmd.Add(new()
{
Cmd = "gsettings",
Arguments = ["set", $"org.gnome.system.proxy", "ignore-hosts", JsonUtils.Serialize(host.Split(','), false)]
});
}
else
{
lstCmd.Add(new()
{
Cmd = "gsettings",
Arguments = ["set", $"org.gnome.system.proxy.{type}", "host", host]
});
lstCmd.Add(new()
{
Cmd = "gsettings",
Arguments = ["set", $"org.gnome.system.proxy.{type}", "port", $"{port}"]
});
}
return lstCmd;
}
private static bool IsKde(out string configDir)
{
configDir = "/home";
var desktop = Environment.GetEnvironmentVariable("XDG_CURRENT_DESKTOP");
var desktop2 = Environment.GetEnvironmentVariable("XDG_SESSION_DESKTOP");
var isKde = string.Equals(desktop, "KDE", StringComparison.OrdinalIgnoreCase)
|| string.Equals(desktop, "plasma", StringComparison.OrdinalIgnoreCase)
|| string.Equals(desktop2, "KDE", StringComparison.OrdinalIgnoreCase)
|| string.Equals(desktop2, "plasma", StringComparison.OrdinalIgnoreCase);
if (isKde)
{
var homeDir = Environment.GetEnvironmentVariable("HOME");
if (homeDir != null)
{
configDir = Path.Combine(homeDir, ".config");
}
}
return isKde;
}
private static string GetKdeVersion()
{
var ver = Environment.GetEnvironmentVariable("KDE_SESSION_VERSION") ?? "0";
return ver switch
{
"6" => "kwriteconfig6",
_ => "kwriteconfig5"
};
} }
} }
} }

View File

@@ -1,101 +1,38 @@
namespace ServiceLib.Handler.SysProxy namespace ServiceLib.Handler.SysProxy
{ {
public class ProxySettingOSX public class ProxySettingOSX
{ {
/// <summary> private static readonly string _proxySetFileName = $"{Global.ProxySetOSXShellFileName.Replace(Global.NamespaceSample, "")}.sh";
/// 应用接口类型
/// </summary>
private static readonly List<string> LstInterface = ["Ethernet", "Wi-Fi", "Thunderbolt Bridge", "USB 10/100/1000 LAN"];
/// <summary>
/// 代理类型,对应 http,https,socks
/// </summary>
private static readonly List<string> LstTypes = ["setwebproxy", "setsecurewebproxy", "setsocksfirewallproxy"];
public static async Task SetProxy(string host, int port, string exceptions) public static async Task SetProxy(string host, int port, string exceptions)
{ {
var lstInterface = await GetListNetworkServices(); List<string> args = ["set", host, port.ToString()];
var lstCmd = GetSetCmds(lstInterface, host, port, exceptions); if (exceptions.IsNotEmpty())
await ExecCmd(lstCmd); {
args.AddRange(exceptions.Split(','));
}
await ExecCmd(args);
} }
public static async Task UnsetProxy() public static async Task UnsetProxy()
{ {
var lstInterface = await GetListNetworkServices(); List<string> args = ["clear"];
var lstCmd = GetUnsetCmds(lstInterface); await ExecCmd(args);
await ExecCmd(lstCmd);
} }
private static async Task ExecCmd(List<CmdItem> lstCmd) private static async Task ExecCmd(List<string> args)
{ {
foreach (var cmd in lstCmd) var fileName = Utils.GetBinConfigPath(_proxySetFileName);
if (!File.Exists(fileName))
{ {
if (cmd is null || cmd.Cmd.IsNullOrEmpty() || cmd.Arguments is null) var contents = EmbedUtils.GetEmbedText(Global.ProxySetOSXShellFileName);
{ await File.AppendAllTextAsync(fileName, contents);
continue;
}
await Task.Delay(10); await Utils.SetLinuxChmod(fileName);
await Utils.GetCliWrapOutput(cmd.Cmd, cmd.Arguments);
}
}
private static List<CmdItem> GetSetCmds(List<string> lstInterface, string host, int port, string exceptions)
{
List<CmdItem> lstCmd = [];
foreach (var interf in lstInterface)
{
foreach (var type in LstTypes)
{
lstCmd.Add(new CmdItem()
{
Cmd = "networksetup",
Arguments = [$"-{type}", interf, host, port.ToString()]
});
}
if (exceptions.IsNotEmpty())
{
List<string> args = [$"-setproxybypassdomains", interf];
args.AddRange(exceptions.Split(','));
lstCmd.Add(new CmdItem()
{
Cmd = "networksetup",
Arguments = args
});
}
} }
return lstCmd; await Utils.GetCliWrapOutput(fileName, args);
}
private static List<CmdItem> GetUnsetCmds(List<string> lstInterface)
{
List<CmdItem> lstCmd = [];
foreach (var interf in lstInterface)
{
foreach (var type in LstTypes)
{
lstCmd.Add(new CmdItem()
{
Cmd = "networksetup",
Arguments = [$"-{type}state", interf, "off"]
});
}
}
return lstCmd;
}
public static async Task<List<string>> GetListNetworkServices()
{
var services = await Utils.GetListNetworkServices();
if (services.IsNullOrEmpty())
{
return LstInterface;
}
var lst = services.Split(Environment.NewLine).Where(t => t.Length > 0 && t.Contains('*') == false);
return lst.ToList();
} }
} }
} }

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Models namespace ServiceLib.Models
{ {
[Serializable] [Serializable]
public class CoreBasicItem public class CoreBasicItem
@@ -156,7 +156,7 @@
public int SpeedTestTimeout { get; set; } public int SpeedTestTimeout { get; set; }
public string SpeedTestUrl { get; set; } public string SpeedTestUrl { get; set; }
public string SpeedPingTestUrl { get; set; } public string SpeedPingTestUrl { get; set; }
public int SpeedTestPageSize { get; set; } public int MixedConcurrencyCount { get; set; }
} }
[Serializable] [Serializable]
@@ -244,4 +244,4 @@
public string? Length { get; set; } public string? Length { get; set; }
public string? Interval { get; set; } public string? Interval { get; set; }
} }
} }

View File

@@ -1,4 +1,4 @@
using SQLite; using SQLite;
namespace ServiceLib.Models namespace ServiceLib.Models
{ {
@@ -11,5 +11,6 @@ namespace ServiceLib.Models
public int Delay { get; set; } public int Delay { get; set; }
public decimal Speed { get; set; } public decimal Speed { get; set; }
public int Sort { get; set; } public int Sort { get; set; }
public string? Message { get; set; }
} }
} }

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Models namespace ServiceLib.Models
{ {
[Serializable] [Serializable]
public class ServerTestItem public class ServerTestItem
@@ -8,6 +8,6 @@
public int Port { get; set; } public int Port { get; set; }
public EConfigType ConfigType { get; set; } public EConfigType ConfigType { get; set; }
public bool AllowTest { get; set; } public bool AllowTest { get; set; }
public int Delay { get; set; } public int QueueNum { get; set; }
} }
} }

View File

@@ -4,23 +4,15 @@ namespace ServiceLib.Models
{ {
public class V2rayConfig public class V2rayConfig
{ {
public string? remarks { get; set; }
public Log4Ray log { get; set; } public Log4Ray log { get; set; }
public List<Inbounds4Ray> inbounds { get; set; }
public List<Outbounds4Ray> outbounds { get; set; }
public Stats4Ray? stats { get; set; }
public Metrics4Ray? metrics { get; set; }
public Policy4Ray? policy { get; set; }
public object dns { get; set; } public object dns { get; set; }
public List<Inbounds4Ray> inbounds { get; set; }
public List<Outbounds4Ray> outbounds { get; set; }
public Routing4Ray routing { get; set; } public Routing4Ray routing { get; set; }
public Metrics4Ray? metrics { get; set; }
public Policy4Ray? policy { get; set; }
public Stats4Ray? stats { get; set; }
public string? remarks { get; set; }
} }
public class Stats4Ray public class Stats4Ray
@@ -398,4 +390,4 @@ namespace ServiceLib.Models
public string? length { get; set; } public string? length { get; set; }
public string? interval { get; set; } public string? interval { get; set; }
} }
} }

View File

@@ -61,7 +61,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Do you want to append rules? Choose yes to append, choose otherwise to replace 的本地化字符串。 /// 查找类似 Do you want to append rules? Choose yes to append, choose otherwise to replace. 的本地化字符串。
/// </summary> /// </summary>
public static string AddBatchRoutingRulesYesNo { public static string AddBatchRoutingRulesYesNo {
get { get {
@@ -196,7 +196,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Failed to run Core, please see the log 的本地化字符串。 /// 查找类似 Failed to run Core, please check the prompt information 的本地化字符串。
/// </summary> /// </summary>
public static string FailedToRunCore { public static string FailedToRunCore {
get { get {
@@ -1311,6 +1311,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Remove invalid by test results 的本地化字符串。
/// </summary>
public static string menuRemoveInvalidServerResult {
get {
return ResourceManager.GetString("menuRemoveInvalidServerResult", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Remove selected servers (Delete) 的本地化字符串。 /// 查找类似 Remove selected servers (Delete) 的本地化字符串。
/// </summary> /// </summary>
@@ -1680,6 +1689,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 By test result 的本地化字符串。
/// </summary>
public static string menuTestServerResult {
get {
return ResourceManager.GetString("menuTestServerResult", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 {0} Website 的本地化字符串。 /// 查找类似 {0} Website 的本地化字符串。
/// </summary> /// </summary>
@@ -1689,15 +1707,6 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Clear original subscription content 的本地化字符串。
/// </summary>
public static string MsgClearSubscription {
get {
return ResourceManager.GetString("MsgClearSubscription", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Download GeoFile: {0} successfully 的本地化字符串。 /// 查找类似 Download GeoFile: {0} successfully 的本地化字符串。
/// </summary> /// </summary>
@@ -2004,6 +2013,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Removed {0} invalid test results. 的本地化字符串。
/// </summary>
public static string RemoveInvalidServerResultTip {
get {
return ResourceManager.GetString("RemoveInvalidServerResultTip", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Are you sure to remove the rules? 的本地化字符串。 /// 查找类似 Are you sure to remove the rules? 的本地化字符串。
/// </summary> /// </summary>
@@ -2204,7 +2222,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 AutoRefresh 的本地化字符串。 /// 查找类似 Auto refresh 的本地化字符串。
/// </summary> /// </summary>
public static string TbAutoRefresh { public static string TbAutoRefresh {
get { get {
@@ -2213,7 +2231,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Auto ScrollToEnd 的本地化字符串。 /// 查找类似 Auto scroll to end 的本地化字符串。
/// </summary> /// </summary>
public static string TbAutoScrollToEnd { public static string TbAutoScrollToEnd {
get { get {
@@ -2861,7 +2879,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Install the font to the system and restart the settings 的本地化字符串。 /// 查找类似 Install the font to the system, select or fill in the font name, restart the settings 的本地化字符串。
/// </summary> /// </summary>
public static string TbSettingsCurrentFontFamilyLinuxTip { public static string TbSettingsCurrentFontFamilyLinuxTip {
get { get {
@@ -3112,15 +3130,6 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 HTTP Port 的本地化字符串。
/// </summary>
public static string TbSettingsHttpPort {
get {
return ResourceManager.GetString("TbSettingsHttpPort", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Hysteria Max bandwidth (Up/Dw) 的本地化字符串。 /// 查找类似 Hysteria Max bandwidth (Up/Dw) 的本地化字符串。
/// </summary> /// </summary>
@@ -3176,7 +3185,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 The password is encrypted and stored only in local files. 的本地化字符串。 /// 查找类似 The password is encrypted and stored only in local files 的本地化字符串。
/// </summary> /// </summary>
public static string TbSettingsLinuxSudoPasswordTip { public static string TbSettingsLinuxSudoPasswordTip {
get { get {
@@ -3220,6 +3229,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 The number of concurrent during multi-test 的本地化字符串。
/// </summary>
public static string TbSettingsMixedConcurrencyCount {
get {
return ResourceManager.GetString("TbSettingsMixedConcurrencyCount", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 sing-box Mux Protocol 的本地化字符串。 /// 查找类似 sing-box Mux Protocol 的本地化字符串。
/// </summary> /// </summary>
@@ -3355,15 +3373,6 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Number per time for auto batch during speedtest(max 1000) 的本地化字符串。
/// </summary>
public static string TbSettingsSpeedTestPageSize {
get {
return ResourceManager.GetString("TbSettingsSpeedTestPageSize", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 SpeedTest Single Timeout Value 的本地化字符串。 /// 查找类似 SpeedTest Single Timeout Value 的本地化字符串。
/// </summary> /// </summary>
@@ -3877,15 +3886,6 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 You are currently running a standalone package, please manually download the SelfContained.7z file to unzip and overwrite it! 的本地化字符串。
/// </summary>
public static string UpdateStandalonePackageTip {
get {
return ResourceManager.GetString("UpdateStandalonePackageTip", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 UpgradeApp does not exist 的本地化字符串。 /// 查找类似 UpgradeApp does not exist 的本地化字符串。
/// </summary> /// </summary>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@@ -210,9 +210,6 @@
<data name="LvTransportProtocol" xml:space="preserve"> <data name="LvTransportProtocol" xml:space="preserve">
<value>جابجایی</value> <value>جابجایی</value>
</data> </data>
<data name="MsgClearSubscription" xml:space="preserve">
<value>محتوای اشتراک اصلی را پاک کنید</value>
</data>
<data name="MsgDownloadV2rayCoreSuccessfully" xml:space="preserve"> <data name="MsgDownloadV2rayCoreSuccessfully" xml:space="preserve">
<value>دانلود Core با موفقیت</value> <value>دانلود Core با موفقیت</value>
</data> </data>
@@ -344,10 +341,10 @@
<value>لطفاً DNS سفارشی صحیح را پر کنید</value> <value>لطفاً DNS سفارشی صحیح را پر کنید</value>
</data> </data>
<data name="TransportPathTip1" xml:space="preserve"> <data name="TransportPathTip1" xml:space="preserve">
<value>*ws path</value> <value>* مسیر ws</value>
</data> </data>
<data name="TransportPathTip2" xml:space="preserve"> <data name="TransportPathTip2" xml:space="preserve">
<value>*h2 path</value> <value>* مسیر h2</value>
</data> </data>
<data name="TransportPathTip3" xml:space="preserve"> <data name="TransportPathTip3" xml:space="preserve">
<value>*QUIC key/Kcp seed</value> <value>*QUIC key/Kcp seed</value>
@@ -356,13 +353,13 @@
<value>*grpc serviceName</value> <value>*grpc serviceName</value>
</data> </data>
<data name="TransportRequestHostTip1" xml:space="preserve"> <data name="TransportRequestHostTip1" xml:space="preserve">
<value>*http host Separated by commas (,)</value> <value>*هاست http جدا شده با کاما (،)</value>
</data> </data>
<data name="TransportRequestHostTip2" xml:space="preserve"> <data name="TransportRequestHostTip2" xml:space="preserve">
<value>*ws host</value> <value>*هاست ws</value>
</data> </data>
<data name="TransportRequestHostTip3" xml:space="preserve"> <data name="TransportRequestHostTip3" xml:space="preserve">
<value>*h2 host Separated by commas (,)</value> <value>*هاست h2 با کاما (،) جدا شده است</value>
</data> </data>
<data name="TransportRequestHostTip4" xml:space="preserve"> <data name="TransportRequestHostTip4" xml:space="preserve">
<value>*QUIC securty</value> <value>*QUIC securty</value>
@@ -377,7 +374,7 @@
<value>*QUIC camouflage type</value> <value>*QUIC camouflage type</value>
</data> </data>
<data name="TransportHeaderTypeTip4" xml:space="preserve"> <data name="TransportHeaderTypeTip4" xml:space="preserve">
<value>*grpc mode</value> <value>*حالت grpc</value>
</data> </data>
<data name="LvTLS" xml:space="preserve"> <data name="LvTLS" xml:space="preserve">
<value>TLS</value> <value>TLS</value>
@@ -404,7 +401,7 @@
<value>درحال تست کردن...</value> <value>درحال تست کردن...</value>
</data> </data>
<data name="LabLAN" xml:space="preserve"> <data name="LabLAN" xml:space="preserve">
<value>LAN</value> <value>شبکه محلی</value>
</data> </data>
<data name="LabLocal" xml:space="preserve"> <data name="LabLocal" xml:space="preserve">
<value>محلی</value> <value>محلی</value>
@@ -715,9 +712,6 @@
<data name="TbSettingsExceptionTip" xml:space="preserve"> <data name="TbSettingsExceptionTip" xml:space="preserve">
<value>استثناها: از سرور پروکسی برای آدرس هایی که با موارد زیر شروع می شوند استفاده نکنید. برای جدا کردن ورودی ها از نقطه ویرگول (;) استفاده کنید.</value> <value>استثناها: از سرور پروکسی برای آدرس هایی که با موارد زیر شروع می شوند استفاده نکنید. برای جدا کردن ورودی ها از نقطه ویرگول (;) استفاده کنید.</value>
</data> </data>
<data name="TbSettingsHttpPort" xml:space="preserve">
<value>پورت Http</value>
</data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve"> <data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>نمایش سرعت واقعی (نیاز به راه اندازی مجدد)</value> <value>نمایش سرعت واقعی (نیاز به راه اندازی مجدد)</value>
</data> </data>
@@ -1051,9 +1045,6 @@
<data name="menuShowOrHideMainWindow" xml:space="preserve"> <data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>نمایش یا پنهان کردن پنجره اصلی</value> <value>نمایش یا پنهان کردن پنجره اصلی</value>
</data> </data>
<data name="UpdateStandalonePackageTip" xml:space="preserve">
<value>شما در حال حاضر در حال اجرای یک بسته مستقل هستید، لطفاً فایل SelfContained.7z را به صورت دستی دانلود کنید تا آن را از حالت فشرده خارج کرده و بازنویسی کنید!</value>
</data>
<data name="TbPreSocksPort4Sub" xml:space="preserve"> <data name="TbPreSocksPort4Sub" xml:space="preserve">
<value>پیکربندی سفارشی ساکس پورت</value> <value>پیکربندی سفارشی ساکس پورت</value>
</data> </data>
@@ -1372,8 +1363,8 @@
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve"> <data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>هنگام بستن پنجره در سینی پنهان شوید</value> <value>هنگام بستن پنجره در سینی پنهان شوید</value>
</data> </data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve"> <data name="TbSettingsMixedConcurrencyCount" xml:space="preserve">
<value>تعداد در هر زمان برای دسته خودکار در طول تست سرعت (حداکثر 1000)</value> <value>The number of concurrent during multi-test</value>
</data> </data>
<data name="TbSettingsExceptionTip2" xml:space="preserve"> <data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>موارد استثنا: از سرور پروکسی برای آدرس های زیر استفاده نکنید. برای جدا کردن ورودی ها از کاما (،) استفاده کنید.</value> <value>موارد استثنا: از سرور پروکسی برای آدرس های زیر استفاده نکنید. برای جدا کردن ورودی ها از کاما (،) استفاده کنید.</value>
@@ -1394,6 +1385,15 @@
<value>کپی کردن دستور پروکسی در کلیپ بورد</value> <value>کپی کردن دستور پروکسی در کلیپ بورد</value>
</data> </data>
<data name="SpeedtestingTestFailedPart" xml:space="preserve"> <data name="SpeedtestingTestFailedPart" xml:space="preserve">
<value>Starting retesting failed parts, {0} remaining. Press ESC to terminate...</value> <value>شروع آزمایش مجدد قطعات ناموفق، {0} باقی مانده است. برای خاتمه ESC را فشار دهید...</value>
</data>
<data name="menuTestServerResult" xml:space="preserve">
<value>By test result</value>
</data>
<data name="menuRemoveInvalidServerResult" xml:space="preserve">
<value>Remove invalid by test results</value>
</data>
<data name="RemoveInvalidServerResultTip" xml:space="preserve">
<value>Removed {0} invalid test results.</value>
</data> </data>
</root> </root>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@@ -210,9 +210,6 @@
<data name="LvTransportProtocol" xml:space="preserve"> <data name="LvTransportProtocol" xml:space="preserve">
<value>Szállítás</value> <value>Szállítás</value>
</data> </data>
<data name="MsgClearSubscription" xml:space="preserve">
<value>Eredeti előfizetési tartalom törlése</value>
</data>
<data name="MsgDownloadV2rayCoreSuccessfully" xml:space="preserve"> <data name="MsgDownloadV2rayCoreSuccessfully" xml:space="preserve">
<value>A Core letöltése sikerült</value> <value>A Core letöltése sikerült</value>
</data> </data>
@@ -715,9 +712,6 @@
<data name="TbSettingsExceptionTip" xml:space="preserve"> <data name="TbSettingsExceptionTip" xml:space="preserve">
<value>Kivétel. Ne használjon proxy szervert a címek esetében, amelyek pontosan itt kezdődnek, használjon pontosvesszőt (;)</value> <value>Kivétel. Ne használjon proxy szervert a címek esetében, amelyek pontosan itt kezdődnek, használjon pontosvesszőt (;)</value>
</data> </data>
<data name="TbSettingsHttpPort" xml:space="preserve">
<value>HTTP Port</value>
</data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve"> <data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>Display real-time speed</value> <value>Display real-time speed</value>
</data> </data>
@@ -1255,9 +1249,6 @@
<data name="menuShowOrHideMainWindow" xml:space="preserve"> <data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>Fő ablak megjelenítése vagy elrejtése</value> <value>Fő ablak megjelenítése vagy elrejtése</value>
</data> </data>
<data name="UpdateStandalonePackageTip" xml:space="preserve">
<value>Jelenleg egy önálló csomagot futtatsz, kérlek, töltsd le manuálisan a SelfContained.7z fájlt, bontsd ki és írd felül!</value>
</data>
<data name="TbPreSocksPort4Sub" xml:space="preserve"> <data name="TbPreSocksPort4Sub" xml:space="preserve">
<value>Testreszabott konfigurációs socks port</value> <value>Testreszabott konfigurációs socks port</value>
</data> </data>
@@ -1372,8 +1363,8 @@
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve"> <data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>Minimálás tálcára ablak zárásakor</value> <value>Minimálás tálcára ablak zárásakor</value>
</data> </data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve"> <data name="TbSettingsMixedConcurrencyCount" xml:space="preserve">
<value>Szám / időszak az automatikus batch során a sebességvizsgálat során (maximum 1000)</value> <value>The number of concurrent during multi-test</value>
</data> </data>
<data name="TbSettingsExceptionTip2" xml:space="preserve"> <data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>Kivétel. Ne használj proxy szervert a címeknél, évezz pontosvesszőt (,)</value> <value>Kivétel. Ne használj proxy szervert a címeknél, évezz pontosvesszőt (,)</value>
@@ -1396,4 +1387,13 @@
<data name="SpeedtestingTestFailedPart" xml:space="preserve"> <data name="SpeedtestingTestFailedPart" xml:space="preserve">
<value>Starting retesting failed parts, {0} remaining. Press ESC to terminate...</value> <value>Starting retesting failed parts, {0} remaining. Press ESC to terminate...</value>
</data> </data>
<data name="menuTestServerResult" xml:space="preserve">
<value>By test result</value>
</data>
<data name="menuRemoveInvalidServerResult" xml:space="preserve">
<value>Remove invalid by test results</value>
</data>
<data name="RemoveInvalidServerResultTip" xml:space="preserve">
<value>Removed {0} invalid test results.</value>
</data>
</root> </root>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@@ -210,9 +210,6 @@
<data name="LvTransportProtocol" xml:space="preserve"> <data name="LvTransportProtocol" xml:space="preserve">
<value>Transport</value> <value>Transport</value>
</data> </data>
<data name="MsgClearSubscription" xml:space="preserve">
<value>Clear original subscription content</value>
</data>
<data name="MsgDownloadV2rayCoreSuccessfully" xml:space="preserve"> <data name="MsgDownloadV2rayCoreSuccessfully" xml:space="preserve">
<value>Download Core successfully</value> <value>Download Core successfully</value>
</data> </data>
@@ -329,7 +326,7 @@
<value>Please fill in the URL</value> <value>Please fill in the URL</value>
</data> </data>
<data name="AddBatchRoutingRulesYesNo" xml:space="preserve"> <data name="AddBatchRoutingRulesYesNo" xml:space="preserve">
<value>Do you want to append rules? Choose yes to append, choose otherwise to replace</value> <value>Do you want to append rules? Choose yes to append, choose otherwise to replace.</value>
</data> </data>
<data name="MsgDownloadGeoFileSuccessfully" xml:space="preserve"> <data name="MsgDownloadGeoFileSuccessfully" xml:space="preserve">
<value>Download GeoFile: {0} successfully</value> <value>Download GeoFile: {0} successfully</value>
@@ -715,9 +712,6 @@
<data name="TbSettingsExceptionTip" xml:space="preserve"> <data name="TbSettingsExceptionTip" xml:space="preserve">
<value>Exclusions: Do not use proxy server for addresses beginning with the following. Use semicolon (;) to separate entries.</value> <value>Exclusions: Do not use proxy server for addresses beginning with the following. Use semicolon (;) to separate entries.</value>
</data> </data>
<data name="TbSettingsHttpPort" xml:space="preserve">
<value>HTTP Port</value>
</data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve"> <data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>Display real-time speed (requires restart)</value> <value>Display real-time speed (requires restart)</value>
</data> </data>
@@ -926,7 +920,7 @@
<value>Speed(M/s)</value> <value>Speed(M/s)</value>
</data> </data>
<data name="FailedToRunCore" xml:space="preserve"> <data name="FailedToRunCore" xml:space="preserve">
<value>Failed to run Core, please see the log</value> <value>Failed to run Core, please check the prompt information</value>
</data> </data>
<data name="LvFilter" xml:space="preserve"> <data name="LvFilter" xml:space="preserve">
<value>Remarks regular filter</value> <value>Remarks regular filter</value>
@@ -950,7 +944,7 @@
<value>Enable sorting servers by drag-n-drop (requires restart)</value> <value>Enable sorting servers by drag-n-drop (requires restart)</value>
</data> </data>
<data name="TbAutoRefresh" xml:space="preserve"> <data name="TbAutoRefresh" xml:space="preserve">
<value>AutoRefresh</value> <value>Auto refresh</value>
</data> </data>
<data name="SpeedtestingSkip" xml:space="preserve"> <data name="SpeedtestingSkip" xml:space="preserve">
<value>Skip test</value> <value>Skip test</value>
@@ -1115,7 +1109,7 @@
<value>(Domain or IP or ProcName) and Port and Protocol and InboundTag =&gt; OutboundTag</value> <value>(Domain or IP or ProcName) and Port and Protocol and InboundTag =&gt; OutboundTag</value>
</data> </data>
<data name="TbAutoScrollToEnd" xml:space="preserve"> <data name="TbAutoScrollToEnd" xml:space="preserve">
<value>Auto ScrollToEnd</value> <value>Auto scroll to end</value>
</data> </data>
<data name="TbSettingsSpeedPingTestUrl" xml:space="preserve"> <data name="TbSettingsSpeedPingTestUrl" xml:space="preserve">
<value>Speed Ping Test URL</value> <value>Speed Ping Test URL</value>
@@ -1255,9 +1249,6 @@
<data name="menuShowOrHideMainWindow" xml:space="preserve"> <data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>Show or hide the main window</value> <value>Show or hide the main window</value>
</data> </data>
<data name="UpdateStandalonePackageTip" xml:space="preserve">
<value>You are currently running a standalone package, please manually download the SelfContained.7z file to unzip and overwrite it!</value>
</data>
<data name="TbPreSocksPort4Sub" xml:space="preserve"> <data name="TbPreSocksPort4Sub" xml:space="preserve">
<value>Custom config socks port</value> <value>Custom config socks port</value>
</data> </data>
@@ -1343,7 +1334,7 @@
<value>Please do not use the insecure HTTP protocol subscription address</value> <value>Please do not use the insecure HTTP protocol subscription address</value>
</data> </data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve"> <data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>Install the font to the system and restart the settings</value> <value>Install the font to the system, select or fill in the font name, restart the settings</value>
</data> </data>
<data name="menuExitTips" xml:space="preserve"> <data name="menuExitTips" xml:space="preserve">
<value>Are you sure to exit?</value> <value>Are you sure to exit?</value>
@@ -1355,7 +1346,7 @@
<value>System sudo password</value> <value>System sudo password</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>The password is encrypted and stored only in local files.</value> <value>The password is encrypted and stored only in local files</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>Please set the sudo password in Tun mode settings first</value> <value>Please set the sudo password in Tun mode settings first</value>
@@ -1372,8 +1363,8 @@
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve"> <data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>Hide to tray when closing the window</value> <value>Hide to tray when closing the window</value>
</data> </data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve"> <data name="TbSettingsMixedConcurrencyCount" xml:space="preserve">
<value>Number per time for auto batch during speedtest(max 1000)</value> <value>The number of concurrent during multi-test</value>
</data> </data>
<data name="TbSettingsExceptionTip2" xml:space="preserve"> <data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>Exclusions: Do not use proxy server for the following addresses. Use comma (,) to separate entries.</value> <value>Exclusions: Do not use proxy server for the following addresses. Use comma (,) to separate entries.</value>
@@ -1396,4 +1387,13 @@
<data name="SpeedtestingTestFailedPart" xml:space="preserve"> <data name="SpeedtestingTestFailedPart" xml:space="preserve">
<value>Starting retesting failed parts, {0} remaining. Press ESC to terminate...</value> <value>Starting retesting failed parts, {0} remaining. Press ESC to terminate...</value>
</data> </data>
<data name="menuTestServerResult" xml:space="preserve">
<value>By test result</value>
</data>
<data name="menuRemoveInvalidServerResult" xml:space="preserve">
<value>Remove invalid by test results</value>
</data>
<data name="RemoveInvalidServerResultTip" xml:space="preserve">
<value>Removed {0} invalid test results.</value>
</data>
</root> </root>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@@ -210,9 +210,6 @@
<data name="LvTransportProtocol" xml:space="preserve"> <data name="LvTransportProtocol" xml:space="preserve">
<value>Протокол</value> <value>Протокол</value>
</data> </data>
<data name="MsgClearSubscription" xml:space="preserve">
<value>Очистить контент оригинальной подписки</value>
</data>
<data name="MsgDownloadV2rayCoreSuccessfully" xml:space="preserve"> <data name="MsgDownloadV2rayCoreSuccessfully" xml:space="preserve">
<value>Ядро успешно загружено</value> <value>Ядро успешно загружено</value>
</data> </data>
@@ -721,9 +718,6 @@
<data name="TbSettingsExceptionTip" xml:space="preserve"> <data name="TbSettingsExceptionTip" xml:space="preserve">
<value>Исключение. Не используйте прокси-сервер для адресов, начинающихся с (,), используйте точку с запятой (;)</value> <value>Исключение. Не используйте прокси-сервер для адресов, начинающихся с (,), используйте точку с запятой (;)</value>
</data> </data>
<data name="TbSettingsHttpPort" xml:space="preserve">
<value>HTTP порт</value>
</data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve"> <data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>Display real-time speed</value> <value>Display real-time speed</value>
</data> </data>
@@ -1210,9 +1204,6 @@
<data name="menuAddTuicServer" xml:space="preserve"> <data name="menuAddTuicServer" xml:space="preserve">
<value>Add [TUIC] server</value> <value>Add [TUIC] server</value>
</data> </data>
<data name="UpdateStandalonePackageTip" xml:space="preserve">
<value>You are currently running a standalone package, please manually download the SelfContained.7z file to unzip and overwrite it!</value>
</data>
<data name="TbSettingsEnableIPv6Address" xml:space="preserve"> <data name="TbSettingsEnableIPv6Address" xml:space="preserve">
<value>Enable IPv6 Address</value> <value>Enable IPv6 Address</value>
</data> </data>
@@ -1372,8 +1363,8 @@
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve"> <data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>Hide to tray when closing the window</value> <value>Hide to tray when closing the window</value>
</data> </data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve"> <data name="TbSettingsMixedConcurrencyCount" xml:space="preserve">
<value>Number per time for auto batch during speedtest(max 1000)</value> <value>The number of concurrent during multi-test</value>
</data> </data>
<data name="TbSettingsExceptionTip2" xml:space="preserve"> <data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>Exception. Do not use proxy server for addresses,with a comma (,)</value> <value>Exception. Do not use proxy server for addresses,with a comma (,)</value>
@@ -1396,4 +1387,13 @@
<data name="SpeedtestingTestFailedPart" xml:space="preserve"> <data name="SpeedtestingTestFailedPart" xml:space="preserve">
<value>Starting retesting failed parts, {0} remaining. Press ESC to terminate...</value> <value>Starting retesting failed parts, {0} remaining. Press ESC to terminate...</value>
</data> </data>
<data name="menuTestServerResult" xml:space="preserve">
<value>By test result</value>
</data>
<data name="menuRemoveInvalidServerResult" xml:space="preserve">
<value>Remove invalid by test results</value>
</data>
<data name="RemoveInvalidServerResultTip" xml:space="preserve">
<value>Removed {0} invalid test results.</value>
</data>
</root> </root>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@@ -175,10 +175,10 @@
<value>初始化配置</value> <value>初始化配置</value>
</data> </data>
<data name="IsLatestCore" xml:space="preserve"> <data name="IsLatestCore" xml:space="preserve">
<value>{0} {1} 已是最新版本</value> <value>{0} {1} 已是最新版本</value>
</data> </data>
<data name="IsLatestN" xml:space="preserve"> <data name="IsLatestN" xml:space="preserve">
<value>{0} {1} 已是最新版本</value> <value>{0} {1} 已是最新版本</value>
</data> </data>
<data name="LvAddress" xml:space="preserve"> <data name="LvAddress" xml:space="preserve">
<value>地址</value> <value>地址</value>
@@ -210,9 +210,6 @@
<data name="LvTransportProtocol" xml:space="preserve"> <data name="LvTransportProtocol" xml:space="preserve">
<value>传输协议</value> <value>传输协议</value>
</data> </data>
<data name="MsgClearSubscription" xml:space="preserve">
<value>清除原订阅内容</value>
</data>
<data name="MsgDownloadV2rayCoreSuccessfully" xml:space="preserve"> <data name="MsgDownloadV2rayCoreSuccessfully" xml:space="preserve">
<value>下载Core成功</value> <value>下载Core成功</value>
</data> </data>
@@ -277,7 +274,7 @@
<value>请先选择服务器</value> <value>请先选择服务器</value>
</data> </data>
<data name="RemoveDuplicateServerResult" xml:space="preserve"> <data name="RemoveDuplicateServerResult" xml:space="preserve">
<value>服务器去重完成。原数量: {0},现数量: {1}</value> <value>服务器去重完成。原数量: {0},现数量: {1}</value>
</data> </data>
<data name="RemoveServer" xml:space="preserve"> <data name="RemoveServer" xml:space="preserve">
<value>是否确定移除服务器?</value> <value>是否确定移除服务器?</value>
@@ -715,9 +712,6 @@
<data name="TbSettingsExceptionTip" xml:space="preserve"> <data name="TbSettingsExceptionTip" xml:space="preserve">
<value>例外:对于下列字符开头的地址,不使用代理配置文件。使用分号(;)分隔。</value> <value>例外:对于下列字符开头的地址,不使用代理配置文件。使用分号(;)分隔。</value>
</data> </data>
<data name="TbSettingsHttpPort" xml:space="preserve">
<value>本地http监听端口</value>
</data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve"> <data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>显示实时速度(需重启)</value> <value>显示实时速度(需重启)</value>
</data> </data>
@@ -926,7 +920,7 @@
<value>速度(M/s)</value> <value>速度(M/s)</value>
</data> </data>
<data name="FailedToRunCore" xml:space="preserve"> <data name="FailedToRunCore" xml:space="preserve">
<value>运行Core失败请查看日志</value> <value>运行 Core 失败,请查看提示信息</value>
</data> </data>
<data name="LvFilter" xml:space="preserve"> <data name="LvFilter" xml:space="preserve">
<value>别名正则过滤</value> <value>别名正则过滤</value>
@@ -1252,9 +1246,6 @@
<data name="menuShowOrHideMainWindow" xml:space="preserve"> <data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>显示或隐藏主界面</value> <value>显示或隐藏主界面</value>
</data> </data>
<data name="UpdateStandalonePackageTip" xml:space="preserve">
<value>您当前运行的是独立包,请手动下载 SelfContained.7z文件解压覆盖!</value>
</data>
<data name="TbPreSocksPort4Sub" xml:space="preserve"> <data name="TbPreSocksPort4Sub" xml:space="preserve">
<value>自定义配置的Socks端口</value> <value>自定义配置的Socks端口</value>
</data> </data>
@@ -1340,7 +1331,7 @@
<value>请不要使用不安全的HTTP协议订阅地址</value> <value>请不要使用不安全的HTTP协议订阅地址</value>
</data> </data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve"> <data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>安装字体到系统中,重启设置</value> <value>安装字体到系统中,选择或填入字体名称,重启设置</value>
</data> </data>
<data name="menuExitTips" xml:space="preserve"> <data name="menuExitTips" xml:space="preserve">
<value>是否确定退出?</value> <value>是否确定退出?</value>
@@ -1369,8 +1360,8 @@
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve"> <data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>关闭窗口时隐藏至托盘</value> <value>关闭窗口时隐藏至托盘</value>
</data> </data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve"> <data name="TbSettingsMixedConcurrencyCount" xml:space="preserve">
<value>测试时自动分批的每批数量最大1000</value> <value>多线程测试时的并发数量</value>
</data> </data>
<data name="TbSettingsExceptionTip2" xml:space="preserve"> <data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>例外:对于下列地址不使用代理配置文件。使用逗号(,)分隔。</value> <value>例外:对于下列地址不使用代理配置文件。使用逗号(,)分隔。</value>
@@ -1393,4 +1384,13 @@
<data name="SpeedtestingTestFailedPart" xml:space="preserve"> <data name="SpeedtestingTestFailedPart" xml:space="preserve">
<value>开始对失败部分进行重新测试,剩余 {0} 个。可按 ESC 终止...</value> <value>开始对失败部分进行重新测试,剩余 {0} 个。可按 ESC 终止...</value>
</data> </data>
<data name="menuTestServerResult" xml:space="preserve">
<value>按测试结果</value>
</data>
<data name="menuRemoveInvalidServerResult" xml:space="preserve">
<value>按测试结果移除无效</value>
</data>
<data name="RemoveInvalidServerResultTip" xml:space="preserve">
<value>移除无效测试结果 {0} 个。</value>
</data>
</root> </root>

View File

@@ -175,10 +175,10 @@
<value>初始化設定</value> <value>初始化設定</value>
</data> </data>
<data name="IsLatestCore" xml:space="preserve"> <data name="IsLatestCore" xml:space="preserve">
<value>{0} {1} 已是最新版本</value> <value>{0} {1} 已是最新版本</value>
</data> </data>
<data name="IsLatestN" xml:space="preserve"> <data name="IsLatestN" xml:space="preserve">
<value>{0} {1} 已是最新版本</value> <value>{0} {1} 已是最新版本</value>
</data> </data>
<data name="LvAddress" xml:space="preserve"> <data name="LvAddress" xml:space="preserve">
<value>位址</value> <value>位址</value>
@@ -210,9 +210,6 @@
<data name="LvTransportProtocol" xml:space="preserve"> <data name="LvTransportProtocol" xml:space="preserve">
<value>傳輸協定</value> <value>傳輸協定</value>
</data> </data>
<data name="MsgClearSubscription" xml:space="preserve">
<value>清除原訂閱內容</value>
</data>
<data name="MsgDownloadV2rayCoreSuccessfully" xml:space="preserve"> <data name="MsgDownloadV2rayCoreSuccessfully" xml:space="preserve">
<value>下載Core成功</value> <value>下載Core成功</value>
</data> </data>
@@ -277,7 +274,7 @@
<value>請先選擇伺服器</value> <value>請先選擇伺服器</value>
</data> </data>
<data name="RemoveDuplicateServerResult" xml:space="preserve"> <data name="RemoveDuplicateServerResult" xml:space="preserve">
<value>伺服器去重完成。原數量: {0},現數量: {1}</value> <value>伺服器去重完成。原數量: {0},現數量: {1}</value>
</data> </data>
<data name="RemoveServer" xml:space="preserve"> <data name="RemoveServer" xml:space="preserve">
<value>是否確定移除伺服器?</value> <value>是否確定移除伺服器?</value>
@@ -716,9 +713,6 @@
<data name="TbSettingsExceptionTip" xml:space="preserve"> <data name="TbSettingsExceptionTip" xml:space="preserve">
<value>例外:對於下列字元開頭的位址,不使用代理設定檔。使用分號(;)分隔。</value> <value>例外:對於下列字元開頭的位址,不使用代理設定檔。使用分號(;)分隔。</value>
</data> </data>
<data name="TbSettingsHttpPort" xml:space="preserve">
<value>本機HTTP偵聽埠</value>
</data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve"> <data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>顯示即時速度(需重啟)</value> <value>顯示即時速度(需重啟)</value>
</data> </data>
@@ -927,7 +921,7 @@
<value>速度(M/s)</value> <value>速度(M/s)</value>
</data> </data>
<data name="FailedToRunCore" xml:space="preserve"> <data name="FailedToRunCore" xml:space="preserve">
<value>執行Core失敗請查閲日誌</value> <value>執行Core失敗請查看提示訊息</value>
</data> </data>
<data name="LvFilter" xml:space="preserve"> <data name="LvFilter" xml:space="preserve">
<value>別名正則過濾</value> <value>別名正則過濾</value>
@@ -1133,9 +1127,6 @@
<data name="menuShowOrHideMainWindow" xml:space="preserve"> <data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>顯示或隱藏主介面</value> <value>顯示或隱藏主介面</value>
</data> </data>
<data name="UpdateStandalonePackageTip" xml:space="preserve">
<value>您目前運行的是獨立包,請手動下載 SelfContained.7z檔案解壓縮覆蓋!</value>
</data>
<data name="TbPreSocksPort4Sub" xml:space="preserve"> <data name="TbPreSocksPort4Sub" xml:space="preserve">
<value>自訂設定的Socks連接埠</value> <value>自訂設定的Socks連接埠</value>
</data> </data>
@@ -1221,7 +1212,7 @@
<value>請不要使用不安全的HTTP協定訂閱位址</value> <value>請不要使用不安全的HTTP協定訂閱位址</value>
</data> </data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve"> <data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>安裝字到系統中,重新啟動設定</value> <value>安裝字到系統中,選擇或填入字體名稱,重新啟動設定</value>
</data> </data>
<data name="menuExitTips" xml:space="preserve"> <data name="menuExitTips" xml:space="preserve">
<value>是否確定退出?</value> <value>是否確定退出?</value>
@@ -1370,8 +1361,8 @@
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve"> <data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>關閉視窗時隱藏至托盤</value> <value>關閉視窗時隱藏至托盤</value>
</data> </data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve"> <data name="TbSettingsMixedConcurrencyCount" xml:space="preserve">
<value>測試時自動分批的每批數量最大1000</value> <value>多執行緒測試時的並發數量</value>
</data> </data>
<data name="TbSettingsExceptionTip2" xml:space="preserve"> <data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>例外:對於下列位址不使用代理設定檔,使用逗號(,)分隔。</value> <value>例外:對於下列位址不使用代理設定檔,使用逗號(,)分隔。</value>
@@ -1394,4 +1385,13 @@
<data name="SpeedtestingTestFailedPart" xml:space="preserve"> <data name="SpeedtestingTestFailedPart" xml:space="preserve">
<value>開始對失敗部分進行重新測試,剩餘 {0} 個。可按 ESC 終止...</value> <value>開始對失敗部分進行重新測試,剩餘 {0} 個。可按 ESC 終止...</value>
</data> </data>
<data name="menuTestServerResult" xml:space="preserve">
<value>按測試結果</value>
</data>
<data name="menuRemoveInvalidServerResult" xml:space="preserve">
<value>按測試結果移除無效</value>
</data>
<data name="RemoveInvalidServerResultTip" xml:space="preserve">
<value>移除無效測試結果 {0} 個。</value>
</data>
</root> </root>

View File

@@ -0,0 +1,118 @@
#!/bin/bash
# Function to set proxy for GNOME
set_gnome_proxy() {
local MODE=$1
local PROXY_IP=$2
local PROXY_PORT=$3
local IGNORE_HOSTS=$4
# Set the proxy mode
gsettings set org.gnome.system.proxy mode "$MODE"
if [ "$MODE" == "manual" ]; then
# List of protocols
local PROTOCOLS=("http" "https" "ftp" "socks")
# Loop through protocols to set the proxy
for PROTOCOL in "${PROTOCOLS[@]}"; do
gsettings set org.gnome.system.proxy.$PROTOCOL host "$PROXY_IP"
gsettings set org.gnome.system.proxy.$PROTOCOL port "$PROXY_PORT"
done
# Set ignored hosts
gsettings set org.gnome.system.proxy ignore-hosts "['$IGNORE_HOSTS']"
echo "GNOME: Manual proxy settings applied."
echo "Proxy IP: $PROXY_IP"
echo "Proxy Port: $PROXY_PORT"
echo "Ignored Hosts: $IGNORE_HOSTS"
elif [ "$MODE" == "none" ]; then
echo "GNOME: Proxy disabled."
else
echo "GNOME: Invalid mode. Use 'none' or 'manual'."
exit 1
fi
}
# Function to set proxy for KDE
set_kde_proxy() {
local MODE=$1
local PROXY_IP=$2
local PROXY_PORT=$3
local IGNORE_HOSTS=$4
# Determine the correct kwriteconfig command based on KDE_SESSION_VERSION
if [ "$KDE_SESSION_VERSION" == "6" ]; then
KWRITECONFIG="kwriteconfig6"
else
KWRITECONFIG="kwriteconfig5"
fi
# KDE uses kwriteconfig to modify proxy settings
if [ "$MODE" == "manual" ]; then
# Set proxy for all protocols
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key ProxyType 1
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key httpProxy "http://$PROXY_IP:$PROXY_PORT"
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key httpsProxy "http://$PROXY_IP:$PROXY_PORT"
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key ftpProxy "http://$PROXY_IP:$PROXY_PORT"
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key socksProxy "http://$PROXY_IP:$PROXY_PORT"
# Set ignored hosts
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key NoProxyFor "$IGNORE_HOSTS"
echo "KDE: Manual proxy settings applied."
echo "Proxy IP: $PROXY_IP"
echo "Proxy Port: $PROXY_PORT"
echo "Ignored Hosts: $IGNORE_HOSTS"
elif [ "$MODE" == "none" ]; then
# Disable proxy
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key ProxyType 0
echo "KDE: Proxy disabled."
else
echo "KDE: Invalid mode. Use 'none' or 'manual'."
exit 1
fi
# Apply changes by restarting KDE's network settings
dbus-send --type=signal /KIO/Scheduler org.kde.KIO.Scheduler.reparseSlaveConfiguration string:""
}
# Detect the current desktop environment
detect_desktop_environment() {
if [ "$XDG_CURRENT_DESKTOP" == "GNOME" ] || [ "$XDG_CURRENT_DESKTOP" == "ubuntu:GNOME" ] || [ "$XDG_SESSION_DESKTOP" == "GNOME" ] || [ "$XDG_SESSION_DESKTOP" == "ubuntu:GNOME" ]; then
echo "gnome"
elif [ "$XDG_CURRENT_DESKTOP" == "KDE" ] || [ "$XDG_CURRENT_DESKTOP" == "plasma" ] || [ "$XDG_SESSION_DESKTOP" == "KDE" ] || [ "$XDG_SESSION_DESKTOP" == "plasma" ]; then
echo "kde"
else
echo "unsupported"
fi
}
# Main script logic
if [ "$#" -lt 1 ]; then
echo "Usage: $0 <mode> [proxy_ip proxy_port ignore_hosts]"
echo " mode: 'none' or 'manual'"
echo " If mode is 'manual', provide proxy IP, port, and ignore hosts."
exit 1
fi
# Get the mode
MODE=$1
PROXY_IP=$2
PROXY_PORT=$3
IGNORE_HOSTS=$4
# Detect desktop environment
DE=$(detect_desktop_environment)
# Apply settings based on the desktop environment
if [ "$DE" == "gnome" ]; then
set_gnome_proxy "$MODE" "$PROXY_IP" "$PROXY_PORT" "$IGNORE_HOSTS"
elif [ "$DE" == "kde" ]; then
set_gnome_proxy "$MODE" "$PROXY_IP" "$PROXY_PORT" "$IGNORE_HOSTS"
set_kde_proxy "$MODE" "$PROXY_IP" "$PROXY_PORT" "$IGNORE_HOSTS"
else
echo "Unsupported desktop environment: $DE"
exit 1
fi

View File

@@ -0,0 +1,74 @@
#!/bin/bash
# Function to set proxy
set_proxy() {
PROXY_IP=$1
PROXY_PORT=$2
shift 2
BYPASS_DOMAINS=("$@")
# If no bypass domains are provided, set it to empty by default
if [ ${#BYPASS_DOMAINS[@]} -eq 0 ]; then
BYPASS_DOMAINS=("")
fi
# Get all network service names
SERVICES=$(networksetup -listallnetworkservices | grep -v '*')
# Loop through each network service
echo "$SERVICES" | while read -r SERVICE; do
echo "Setting proxy for network service '$SERVICE'..."
# Set HTTP proxy
networksetup -setwebproxy "$SERVICE" "$PROXY_IP" "$PROXY_PORT"
# Set HTTPS proxy
networksetup -setsecurewebproxy "$SERVICE" "$PROXY_IP" "$PROXY_PORT"
# Set SOCKS proxy
networksetup -setsocksfirewallproxy "$SERVICE" "$PROXY_IP" "$PROXY_PORT"
# Set bypass domains
networksetup -setproxybypassdomains "$SERVICE" "${BYPASS_DOMAINS[@]}"
echo "Proxy for network service '$SERVICE' has been set to $PROXY_IP:$PROXY_PORT"
done
echo "Proxy settings for all network services are complete!"
}
# Function to disable proxy
clear_proxy() {
# Get all network service names
SERVICES=$(networksetup -listallnetworkservices | grep -v '*')
# Loop through each network service
echo "$SERVICES" | while read -r SERVICE; do
echo "Disabling proxy and clearing bypass domains for network service '$SERVICE'..."
# Disable HTTP proxy
networksetup -setwebproxystate "$SERVICE" off
# Disable HTTPS proxy
networksetup -setsecurewebproxystate "$SERVICE" off
# Disable SOCKS proxy
networksetup -setsocksfirewallproxystate "$SERVICE" off
echo "Proxy for network service '$SERVICE' has been disabled"
done
echo "Proxy for all network services has been disabled!"
}
# Main script logic
if [ "$1" == "set" ]; then
# Check if enough parameters are passed for setting proxy
if [ "$#" -lt 3 ]; then
echo "Usage: $0 set <IP Address> <Port> [Bypass Domain 1 Bypass Domain 2 ...]"
exit 1
fi
set_proxy "$2" "$3" "${@:4}"
elif [ "$1" == "clear" ]; then
clear_proxy
else
echo "Usage:"
echo " To set proxy: $0 set <IP Address> <Port> [Bypass Domain 1 Bypass Domain 2 ...]"
echo " To clear proxy: $0 clear"
exit 1
fi

View File

@@ -29,6 +29,8 @@
<EmbeddedResource Include="Sample\dns_singbox_normal" /> <EmbeddedResource Include="Sample\dns_singbox_normal" />
<EmbeddedResource Include="Sample\dns_v2ray_normal" /> <EmbeddedResource Include="Sample\dns_v2ray_normal" />
<EmbeddedResource Include="Sample\pac" /> <EmbeddedResource Include="Sample\pac" />
<EmbeddedResource Include="Sample\proxy_set_linux_sh" />
<EmbeddedResource Include="Sample\proxy_set_osx_sh" />
<EmbeddedResource Include="Sample\SampleClientConfig" /> <EmbeddedResource Include="Sample\SampleClientConfig" />
<EmbeddedResource Include="Sample\SampleHttpRequest" /> <EmbeddedResource Include="Sample\SampleHttpRequest" />
<EmbeddedResource Include="Sample\SampleHttpResponse" /> <EmbeddedResource Include="Sample\SampleHttpResponse" />

View File

@@ -116,7 +116,7 @@ namespace ServiceLib.Services.CoreConfig
//enable tun mode //enable tun mode
if (_config.TunModeItem.EnableTun) if (_config.TunModeItem.EnableTun)
{ {
string tun = EmbedUtils.GetEmbedText(Global.ClashTunYaml); var tun = EmbedUtils.GetEmbedText(Global.ClashTunYaml);
if (Utils.IsNotEmpty(tun)) if (Utils.IsNotEmpty(tun))
{ {
var tunContent = YamlUtils.FromYaml<Dictionary<string, object>>(tun); var tunContent = YamlUtils.FromYaml<Dictionary<string, object>>(tun);
@@ -128,7 +128,7 @@ namespace ServiceLib.Services.CoreConfig
//Mixin //Mixin
try try
{ {
MixinContent(fileContent, node); await MixinContent(fileContent, node);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -158,20 +158,21 @@ namespace ServiceLib.Services.CoreConfig
} }
} }
private void MixinContent(Dictionary<string, object> fileContent, ProfileItem node) private async Task MixinContent(Dictionary<string, object> fileContent, ProfileItem node)
{ {
//if (!_config.clashUIItem.enableMixinContent) if (!_config.ClashUIItem.EnableMixinContent)
//{
// return;
//}
var path = Utils.GetConfigPath(Global.ClashMixinConfigFileName);
if (!File.Exists(path))
{ {
return; return;
} }
var txtFile = File.ReadAllText(Utils.GetConfigPath(Global.ClashMixinConfigFileName)); var path = Utils.GetConfigPath(Global.ClashMixinConfigFileName);
if (!File.Exists(path))
{
var mixin = EmbedUtils.GetEmbedText(Global.ClashMixinYaml);
await File.AppendAllTextAsync(path, mixin);
}
var txtFile = await File.ReadAllTextAsync(Utils.GetConfigPath(Global.ClashMixinConfigFileName));
var mixinContent = YamlUtils.FromYaml<Dictionary<string, object>>(txtFile); var mixinContent = YamlUtils.FromYaml<Dictionary<string, object>>(txtFile);
if (mixinContent == null) if (mixinContent == null)
@@ -269,4 +270,4 @@ namespace ServiceLib.Services.CoreConfig
} }
} }
} }
} }

View File

@@ -242,6 +242,66 @@ namespace ServiceLib.Services.CoreConfig
} }
} }
public async Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port)
{
var ret = new RetResult();
try
{
if (node is not { Port: > 0 })
{
ret.Msg = ResUI.CheckServerSettings;
return ret;
}
if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
{
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
return ret;
}
ret.Msg = ResUI.InitialConfiguration;
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
if (Utils.IsNullOrEmpty(result))
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
return ret;
}
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
if (singboxConfig == null)
{
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
await GenLog(singboxConfig);
await GenOutbound(node, singboxConfig.outbounds.First());
await GenMoreOutbounds(node, singboxConfig);
await GenDnsDomains(null, singboxConfig, null);
singboxConfig.route.rules.Clear();
singboxConfig.inbounds.Clear();
singboxConfig.inbounds.Add(new()
{
tag = $"{EInboundProtocol.mixed}{port}",
listen = Global.Loopback,
listen_port = port,
type = EInboundProtocol.mixed.ToString(),
});
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
ret.Success = true;
ret.Data = JsonUtils.Serialize(singboxConfig);
return ret;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
}
public async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds) public async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds)
{ {
var ret = new RetResult(); var ret = new RetResult();
@@ -539,7 +599,7 @@ namespace ServiceLib.Services.CoreConfig
{ {
if (_config.TunModeItem.Mtu <= 0) if (_config.TunModeItem.Mtu <= 0)
{ {
_config.TunModeItem.Mtu = Utils.ToInt(Global.TunMtus.First()); _config.TunModeItem.Mtu = Global.TunMtus.First();
} }
if (Utils.IsNullOrEmpty(_config.TunModeItem.Stack)) if (Utils.IsNullOrEmpty(_config.TunModeItem.Stack))
{ {
@@ -685,7 +745,7 @@ namespace ServiceLib.Services.CoreConfig
outbound.peer_public_key = node.PublicKey; outbound.peer_public_key = node.PublicKey;
outbound.reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList(); outbound.reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList();
outbound.local_address = Utils.String2List(node.RequestHost); outbound.local_address = Utils.String2List(node.RequestHost);
outbound.mtu = Utils.ToInt(node.ShortId.IsNullOrEmpty() ? Global.TunMtus.FirstOrDefault() : node.ShortId); outbound.mtu = Utils.ToInt(node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId);
break; break;
} }
} }
@@ -1264,7 +1324,7 @@ namespace ServiceLib.Services.CoreConfig
singboxConfig.experimental.cache_file = new CacheFile4Sbox() singboxConfig.experimental.cache_file = new CacheFile4Sbox()
{ {
enabled = true, enabled = true,
path = Utils.GetConfigPath("cache.db") path = Utils.GetBinPath("cache.db")
}; };
} }

View File

@@ -353,6 +353,64 @@ namespace ServiceLib.Services.CoreConfig
} }
} }
public async Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port)
{
var ret = new RetResult();
try
{
if (node is not { Port: > 0 })
{
ret.Msg = ResUI.CheckServerSettings;
return ret;
}
if (node.GetNetwork() is nameof(ETransport.quic))
{
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
return ret;
}
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
if (Utils.IsNullOrEmpty(result))
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
return ret;
}
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
if (v2rayConfig == null)
{
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
await GenLog(v2rayConfig);
await GenOutbound(node, v2rayConfig.outbounds.First());
await GenMoreOutbounds(node, v2rayConfig);
v2rayConfig.routing.rules.Clear();
v2rayConfig.inbounds.Clear();
v2rayConfig.inbounds.Add(new()
{
tag = $"{EInboundProtocol.socks}{port}",
listen = Global.Loopback,
port = port,
protocol = EInboundProtocol.socks.ToString(),
});
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
ret.Success = true;
ret.Data = JsonUtils.Serialize(v2rayConfig);
return ret;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
}
#endregion public gen function #endregion public gen function
#region private gen function #region private gen function

View File

@@ -1,4 +1,4 @@
using System.Diagnostics; using System.Diagnostics;
using System.Net; using System.Net;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Net.Sockets; using System.Net.Sockets;
@@ -222,20 +222,20 @@ namespace ServiceLib.Services
public async Task<int> RunAvailabilityCheck(IWebProxy? webProxy) public async Task<int> RunAvailabilityCheck(IWebProxy? webProxy)
{ {
var responseTime = -1;
try try
{ {
webProxy ??= await GetWebProxy(true); webProxy ??= await GetWebProxy(true);
var config = AppHandler.Instance.Config;
try for (var i = 0; i < 2; i++)
{ {
var config = AppHandler.Instance.Config; responseTime = await GetRealPingTime(config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
var responseTime = await GetRealPingTime(config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10); if (responseTime > 0)
return responseTime; {
} break;
catch (Exception ex) }
{ await Task.Delay(500);
Logging.SaveLog(_tag, ex);
return -1;
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -243,6 +243,7 @@ namespace ServiceLib.Services
Logging.SaveLog(_tag, ex); Logging.SaveLog(_tag, ex);
return -1; return -1;
} }
return responseTime;
} }
public async Task<int> GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout) public async Task<int> GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout)
@@ -319,4 +320,4 @@ namespace ServiceLib.Services
ServicePointManager.DefaultConnectionLimit = 256; ServicePointManager.DefaultConnectionLimit = 256;
} }
} }
} }

View File

@@ -1,76 +1,69 @@
using System.Collections.Concurrent;
using System.Diagnostics; using System.Diagnostics;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using ReactiveUI;
namespace ServiceLib.Services namespace ServiceLib.Services
{ {
public class SpeedtestService public class SpeedtestService
{ {
private static readonly string _tag = "SpeedtestService";
private Config? _config; private Config? _config;
private Action<SpeedTestResult>? _updateFunc; private Action<SpeedTestResult>? _updateFunc;
private static readonly ConcurrentBag<string> _lstExitLoop = new();
private bool _exitLoop = false; public SpeedtestService(Config config, Action<SpeedTestResult> updateFunc)
private static readonly string _tag = "SpeedtestService";
public SpeedtestService(Config config, ESpeedActionType actionType, List<ProfileItem> selecteds, Action<SpeedTestResult> updateFunc)
{ {
_config = config; _config = config;
_updateFunc = updateFunc; _updateFunc = updateFunc;
}
MessageBus.Current.Listen<string>(EMsgCommand.StopSpeedtest.ToString()).Subscribe(ExitLoop); public void RunLoop(ESpeedActionType actionType, List<ProfileItem> selecteds)
{
Task.Run(async () => Task.Run(async () =>
{ {
var lstSelected = GetClearItem(actionType, selecteds); await RunAsync(actionType, selecteds);
await RunAsync(actionType, lstSelected); await ProfileExHandler.Instance.SaveTo();
UpdateFunc("", ResUI.SpeedtestingCompleted); UpdateFunc("", ResUI.SpeedtestingCompleted);
FileManager.DeleteExpiredFiles(Utils.GetBinConfigPath(), DateTime.Now.AddHours(-1));
}); });
} }
private async Task RunAsync(ESpeedActionType actionType, List<ServerTestItem> lstSelected, int pageSize = 0) public void ExitLoop()
{ {
if (actionType == ESpeedActionType.Tcping) if (_lstExitLoop.Count > 0)
{ {
await RunTcpingAsync(lstSelected); UpdateFunc("", ResUI.SpeedtestingStop);
return;
_lstExitLoop.Clear();
} }
}
if (pageSize <= 0) private async Task RunAsync(ESpeedActionType actionType, List<ProfileItem> selecteds)
{
var exitLoopKey = Utils.GetGuid(false);
_lstExitLoop.Add(exitLoopKey);
var lstSelected = GetClearItem(actionType, selecteds);
switch (actionType)
{ {
pageSize = lstSelected.Count < Global.SpeedTestPageSize ? lstSelected.Count : Global.SpeedTestPageSize; case ESpeedActionType.Tcping:
} await RunTcpingAsync(lstSelected);
var lstTest = GetTestBatchItem(lstSelected, pageSize); break;
List<ServerTestItem> lstFailed = new(); case ESpeedActionType.Realping:
foreach (var lst in lstTest) await RunRealPingBatchAsync(lstSelected, exitLoopKey);
{ break;
var ret = actionType switch
{
ESpeedActionType.Realping => await RunRealPingAsync(lst),
ESpeedActionType.Speedtest => await RunSpeedTestAsync(lst),
ESpeedActionType.Mixedtest => await RunMixedTestAsync(lst),
_ => true
};
if (ret == false)
{
lstFailed.AddRange(lst);
}
await Task.Delay(100);
}
//Retest the failed part case ESpeedActionType.Speedtest:
var pageSizeNext = pageSize / 2; await RunMixedTestAsync(lstSelected, 1, true, exitLoopKey);
if (lstFailed.Count > 0 && pageSizeNext > 0) break;
{
if (_exitLoop)
{
UpdateFunc("", ResUI.SpeedtestingSkip);
return;
}
UpdateFunc("", string.Format(ResUI.SpeedtestingTestFailedPart, lstFailed.Count)); case ESpeedActionType.Mixedtest:
await RunAsync(actionType, lstFailed, pageSizeNext); await RunMixedTestAsync(lstSelected, _config.SpeedTestItem.MixedConcurrencyCount, true, exitLoopKey);
break;
} }
} }
@@ -94,7 +87,8 @@ namespace ServiceLib.Services
IndexId = it.IndexId, IndexId = it.IndexId,
Address = it.Address, Address = it.Address,
Port = it.Port, Port = it.Port,
ConfigType = it.ConfigType ConfigType = it.ConfigType,
QueueNum = selecteds.IndexOf(it)
}); });
} }
@@ -106,18 +100,18 @@ namespace ServiceLib.Services
case ESpeedActionType.Tcping: case ESpeedActionType.Tcping:
case ESpeedActionType.Realping: case ESpeedActionType.Realping:
UpdateFunc(it.IndexId, ResUI.Speedtesting, ""); UpdateFunc(it.IndexId, ResUI.Speedtesting, "");
ProfileExHandler.Instance.SetTestDelay(it.IndexId, "0"); ProfileExHandler.Instance.SetTestDelay(it.IndexId, 0);
break; break;
case ESpeedActionType.Speedtest: case ESpeedActionType.Speedtest:
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingWait); UpdateFunc(it.IndexId, "", ResUI.SpeedtestingWait);
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, "0"); ProfileExHandler.Instance.SetTestSpeed(it.IndexId, 0);
break; break;
case ESpeedActionType.Mixedtest: case ESpeedActionType.Mixedtest:
UpdateFunc(it.IndexId, ResUI.Speedtesting, ResUI.SpeedtestingWait); UpdateFunc(it.IndexId, ResUI.Speedtesting, ResUI.SpeedtestingWait);
ProfileExHandler.Instance.SetTestDelay(it.IndexId, "0"); ProfileExHandler.Instance.SetTestDelay(it.IndexId, 0);
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, "0"); ProfileExHandler.Instance.SetTestSpeed(it.IndexId, 0);
break; break;
} }
} }
@@ -125,72 +119,77 @@ namespace ServiceLib.Services
return lstSelected; return lstSelected;
} }
private List<List<ServerTestItem>> GetTestBatchItem(List<ServerTestItem> lstSelected, int pageSize)
{
List<List<ServerTestItem>> lstTest = new();
var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard)).ToList();
var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard).ToList();
for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++)
{
lstTest.Add(lst1.Skip(num * pageSize).Take(pageSize).ToList());
}
for (var num = 0; num < (int)Math.Ceiling(lst2.Count * 1.0 / pageSize); num++)
{
lstTest.Add(lst2.Skip(num * pageSize).Take(pageSize).ToList());
}
return lstTest;
}
private void ExitLoop(string x)
{
if (_exitLoop)
return;
_exitLoop = true;
UpdateFunc("", ResUI.SpeedtestingStop);
}
private async Task RunTcpingAsync(List<ServerTestItem> selecteds) private async Task RunTcpingAsync(List<ServerTestItem> selecteds)
{ {
try List<Task> tasks = [];
foreach (var it in selecteds)
{ {
List<Task> tasks = []; if (it.ConfigType == EConfigType.Custom)
foreach (var it in selecteds)
{ {
if (it.ConfigType == EConfigType.Custom) continue;
{
continue;
}
tasks.Add(Task.Run(async () =>
{
try
{
var time = await GetTcpingTime(it.Address, it.Port);
var output = FormatOut(time, Global.DelayUnit);
ProfileExHandler.Instance.SetTestDelay(it.IndexId, output);
UpdateFunc(it.IndexId, output);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}));
} }
Task.WaitAll([.. tasks]); tasks.Add(Task.Run(async () =>
{
try
{
var responseTime = await GetTcpingTime(it.Address, it.Port);
ProfileExHandler.Instance.SetTestDelay(it.IndexId, responseTime);
UpdateFunc(it.IndexId, responseTime.ToString());
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}));
} }
catch (Exception ex) Task.WaitAll([.. tasks]);
await Task.CompletedTask;
}
private async Task RunRealPingBatchAsync(List<ServerTestItem> lstSelected, string exitLoopKey, int pageSize = 0)
{
if (pageSize <= 0)
{ {
Logging.SaveLog(_tag, ex); pageSize = lstSelected.Count < Global.SpeedTestPageSize ? lstSelected.Count : Global.SpeedTestPageSize;
} }
finally var lstTest = GetTestBatchItem(lstSelected, pageSize);
List<ServerTestItem> lstFailed = new();
foreach (var lst in lstTest)
{ {
await ProfileExHandler.Instance.SaveTo(); var ret = await RunRealPingAsync(lst, exitLoopKey);
if (ret == false)
{
lstFailed.AddRange(lst);
}
await Task.Delay(100);
}
//Retest the failed part
var pageSizeNext = pageSize / 2;
if (lstFailed.Count > 0 && pageSizeNext > 0)
{
if (_lstExitLoop.Any(p => p == exitLoopKey) == false)
{
UpdateFunc("", ResUI.SpeedtestingSkip);
return;
}
UpdateFunc("", string.Format(ResUI.SpeedtestingTestFailedPart, lstFailed.Count));
if (pageSizeNext > _config.SpeedTestItem.MixedConcurrencyCount)
{
await RunRealPingBatchAsync(lstFailed, exitLoopKey, pageSizeNext);
}
else
{
await RunMixedTestAsync(lstSelected, _config.SpeedTestItem.MixedConcurrencyCount, false, exitLoopKey);
}
} }
} }
private async Task<bool> RunRealPingAsync(List<ServerTestItem> selecteds) private async Task<bool> RunRealPingAsync(List<ServerTestItem> selecteds, string exitLoopKey)
{ {
var pid = -1; var pid = -1;
try try
@@ -216,20 +215,7 @@ namespace ServiceLib.Services
} }
tasks.Add(Task.Run(async () => tasks.Add(Task.Run(async () =>
{ {
try await DoRealPing(downloadHandle, it);
{
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
var output = await GetRealPingTime(downloadHandle, webProxy);
ProfileExHandler.Instance.SetTestDelay(it.IndexId, output);
UpdateFunc(it.IndexId, output);
int.TryParse(output, out var delay);
it.Delay = delay;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
})); }));
} }
Task.WaitAll(tasks.ToArray()); Task.WaitAll(tasks.ToArray());
@@ -244,160 +230,98 @@ namespace ServiceLib.Services
{ {
await ProcUtils.ProcessKill(pid); await ProcUtils.ProcessKill(pid);
} }
await ProfileExHandler.Instance.SaveTo();
} }
return true; return true;
} }
private async Task<bool> RunSpeedTestAsync(List<ServerTestItem> selecteds) private async Task RunMixedTestAsync(List<ServerTestItem> selecteds, int concurrencyCount, bool blSpeedTest, string exitLoopKey)
{ {
var pid = -1; using var concurrencySemaphore = new SemaphoreSlim(concurrencyCount);
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds); var downloadHandle = new DownloadService();
if (pid < 0) List<Task> tasks = new();
{
return false;
}
var url = _config.SpeedTestItem.SpeedTestUrl;
var timeout = _config.SpeedTestItem.SpeedTestTimeout;
DownloadService downloadHandle = new();
foreach (var it in selecteds) foreach (var it in selecteds)
{ {
if (_exitLoop) if (_lstExitLoop.Any(p => p == exitLoopKey) == false)
{ {
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip); UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
continue; continue;
} }
if (!it.AllowTest)
{
continue;
}
if (it.ConfigType == EConfigType.Custom) if (it.ConfigType == EConfigType.Custom)
{ {
continue; continue;
} }
//if (it.delay < 0) await concurrencySemaphore.WaitAsync();
//{
// UpdateFunc(it.indexId, "", ResUI.SpeedtestingSkip);
// continue;
//}
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, "-1");
UpdateFunc(it.IndexId, "", ResUI.Speedtesting);
var item = await AppHandler.Instance.GetProfileItem(it.IndexId); tasks.Add(Task.Run(async () =>
if (item is null)
continue;
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
await downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
{ {
decimal.TryParse(msg, out var dec); var pid = -1;
if (dec > 0) try
{ {
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg); pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(it);
if (pid > 0)
{
await Task.Delay(500);
var delay = await DoRealPing(downloadHandle, it);
if (blSpeedTest)
{
if (delay > 0)
{
await DoSpeedTest(downloadHandle, it);
}
else
{
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
}
}
}
else
{
UpdateFunc(it.IndexId, "", ResUI.FailedToRunCore);
}
} }
UpdateFunc(it.IndexId, "", msg); catch (Exception ex)
});
}
if (pid > 0)
{
await ProcUtils.ProcessKill(pid);
}
await ProfileExHandler.Instance.SaveTo();
return true;
}
private async Task<bool> RunSpeedTestMulti(List<ServerTestItem> selecteds)
{
var pid = -1;
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
if (pid < 0)
{
return false;
}
var url = _config.SpeedTestItem.SpeedTestUrl;
var timeout = _config.SpeedTestItem.SpeedTestTimeout;
DownloadService downloadHandle = new();
foreach (var it in selecteds)
{
if (_exitLoop)
{
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
continue;
}
if (!it.AllowTest)
{
continue;
}
if (it.ConfigType == EConfigType.Custom)
{
continue;
}
if (it.Delay < 0)
{
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
continue;
}
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, "-1");
UpdateFunc(it.IndexId, "", ResUI.Speedtesting);
var item = await AppHandler.Instance.GetProfileItem(it.IndexId);
if (item is null)
continue;
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
_ = downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
{
decimal.TryParse(msg, out var dec);
if (dec > 0)
{ {
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg); Logging.SaveLog(_tag, ex);
} }
UpdateFunc(it.IndexId, "", msg); finally
}); {
await Task.Delay(2000); if (pid > 0)
{
await ProcUtils.ProcessKill(pid);
}
concurrencySemaphore.Release();
}
}));
} }
Task.WaitAll(tasks.ToArray());
await Task.Delay((timeout + 2) * 1000);
if (pid > 0)
{
await ProcUtils.ProcessKill(pid);
}
await ProfileExHandler.Instance.SaveTo();
return true;
} }
private async Task<bool> RunMixedTestAsync(List<ServerTestItem> selecteds) private async Task<int> DoRealPing(DownloadService downloadHandle, ServerTestItem it)
{
var ret = await RunRealPingAsync(selecteds);
if (ret == false)
{
return false;
}
await Task.Delay(1000);
var ret2 = await RunSpeedTestMulti(selecteds);
if (ret2 == false)
{
return false;
}
return true;
}
private async Task<string> GetRealPingTime(DownloadService downloadHandle, IWebProxy webProxy)
{ {
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
var responseTime = await downloadHandle.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10); var responseTime = await downloadHandle.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
return FormatOut(responseTime, Global.DelayUnit);
ProfileExHandler.Instance.SetTestDelay(it.IndexId, responseTime);
UpdateFunc(it.IndexId, responseTime.ToString());
return responseTime;
}
private async Task DoSpeedTest(DownloadService downloadHandle, ServerTestItem it)
{
UpdateFunc(it.IndexId, "", ResUI.Speedtesting);
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
var url = _config.SpeedTestItem.SpeedTestUrl;
var timeout = _config.SpeedTestItem.SpeedTestTimeout;
await downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
{
decimal.TryParse(msg, out var dec);
if (dec > 0)
{
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, dec);
}
UpdateFunc(it.IndexId, "", msg);
});
} }
private async Task<int> GetTcpingTime(string url, int port) private async Task<int> GetTcpingTime(string url, int port)
@@ -412,18 +336,19 @@ namespace ServiceLib.Services
ipAddress = ipHostInfo.AddressList.First(); ipAddress = ipHostInfo.AddressList.First();
} }
var timer = Stopwatch.StartNew();
IPEndPoint endPoint = new(ipAddress, port); IPEndPoint endPoint = new(ipAddress, port);
using Socket clientSocket = new(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); using Socket clientSocket = new(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
var timer = Stopwatch.StartNew();
var result = clientSocket.BeginConnect(endPoint, null, null); var result = clientSocket.BeginConnect(endPoint, null, null);
if (!result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5))) if (!result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5)))
{
throw new TimeoutException("connect timeout (5s): " + url); throw new TimeoutException("connect timeout (5s): " + url);
clientSocket.EndConnect(result); }
timer.Stop(); timer.Stop();
responseTime = (int)timer.Elapsed.TotalMilliseconds; responseTime = (int)timer.Elapsed.TotalMilliseconds;
clientSocket.EndConnect(result);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -432,14 +357,31 @@ namespace ServiceLib.Services
return responseTime; return responseTime;
} }
private string FormatOut(object time, string unit) private List<List<ServerTestItem>> GetTestBatchItem(List<ServerTestItem> lstSelected, int pageSize)
{ {
return $"{time}"; List<List<ServerTestItem>> lstTest = new();
var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard)).ToList();
var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard).ToList();
for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++)
{
lstTest.Add(lst1.Skip(num * pageSize).Take(pageSize).ToList());
}
for (var num = 0; num < (int)Math.Ceiling(lst2.Count * 1.0 / pageSize); num++)
{
lstTest.Add(lst2.Skip(num * pageSize).Take(pageSize).ToList());
}
return lstTest;
} }
private void UpdateFunc(string indexId, string delay, string speed = "") private void UpdateFunc(string indexId, string delay, string speed = "")
{ {
_updateFunc?.Invoke(new() { IndexId = indexId, Delay = delay, Speed = speed }); _updateFunc?.Invoke(new() { IndexId = indexId, Delay = delay, Speed = speed });
if (indexId.IsNotEmpty() && speed.IsNotEmpty())
{
ProfileExHandler.Instance.SetTestMessage(indexId, speed);
}
} }
} }
} }

View File

@@ -21,7 +21,7 @@ namespace ServiceLib.Services.Statistics
Task.Run(Run); Task.Run(Run);
} }
private async void Init() private async Task Init()
{ {
await Task.Delay(5000); await Task.Delay(5000);
@@ -53,9 +53,9 @@ namespace ServiceLib.Services.Statistics
} }
} }
private async void Run() private async Task Run()
{ {
Init(); await Init();
while (!_exitFlag) while (!_exitFlag)
{ {
@@ -73,7 +73,7 @@ namespace ServiceLib.Services.Statistics
{ {
webSocket.Abort(); webSocket.Abort();
webSocket = null; webSocket = null;
Init(); await Init();
continue; continue;
} }

View File

@@ -23,7 +23,7 @@ namespace ServiceLib.Services.Statistics
_exitFlag = true; _exitFlag = true;
} }
private async void Run() private async Task Run()
{ {
while (!_exitFlag) while (!_exitFlag)
{ {

View File

@@ -237,8 +237,8 @@ namespace ServiceLib.Services
public async Task UpdateGeoFileAll(Config config, Action<bool, string> updateFunc) public async Task UpdateGeoFileAll(Config config, Action<bool, string> updateFunc)
{ {
await UpdateGeoFile("geosite", config, updateFunc); await UpdateGeoFiles(config, updateFunc);
await UpdateGeoFile("geoip", config, updateFunc); await UpdateOtherFiles(config, updateFunc);
await UpdateSrsFileAll(config, updateFunc); await UpdateSrsFileAll(config, updateFunc);
_updateFunc?.Invoke(true, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, "geo")); _updateFunc?.Invoke(true, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, "geo"));
} }
@@ -427,22 +427,32 @@ namespace ServiceLib.Services
{ {
if (Utils.IsWindows()) if (Utils.IsWindows())
{ {
//Check for standalone windows .Net version var url = RuntimeInformation.ProcessArchitecture switch
if (coreInfo?.CoreType == ECoreType.v2rayN && RuntimeInformation.ProcessArchitecture == Architecture.X64)
{
var runtimes = await Utils.GetCliWrapOutput("dotnet", "--list-runtimes");
if (runtimes == null || runtimes.Contains("Microsoft.WindowsDesktop.App 8") == false)
{
return coreInfo?.DownloadUrlWin64?.Replace(".zip", "-SelfContained.zip");
}
}
return RuntimeInformation.ProcessArchitecture switch
{ {
Architecture.Arm64 => coreInfo?.DownloadUrlWinArm64, Architecture.Arm64 => coreInfo?.DownloadUrlWinArm64,
Architecture.X64 => coreInfo?.DownloadUrlWin64, Architecture.X64 => coreInfo?.DownloadUrlWin64,
_ => null, _ => null,
}; };
if (coreInfo?.CoreType != ECoreType.v2rayN)
{
return url;
}
//Check for standalone windows .Net version
if (File.Exists(Path.Combine(Utils.GetBaseDirectory(), "wpfgfx_cor3.dll"))
&& File.Exists(Path.Combine(Utils.GetBaseDirectory(), "D3DCompiler_47_cor3.dll")))
{
return url?.Replace(".zip", "-SelfContained.zip");
}
//Check for avalonia desktop windows version
if (File.Exists(Path.Combine(Utils.GetBaseDirectory(), "libHarfBuzzSharp.dll")))
{
return url?.Replace(".zip", "-desktop.zip");
}
return url;
} }
else if (Utils.IsLinux()) else if (Utils.IsLinux())
{ {
@@ -462,14 +472,14 @@ namespace ServiceLib.Services
_ => null, _ => null,
}; };
} }
return null; return await Task.FromResult("");
} }
#endregion CheckUpdate private #endregion CheckUpdate private
#region Geo private #region Geo private
private async Task UpdateGeoFile(string geoName, Config config, Action<bool, string> updateFunc) private async Task UpdateGeoFiles(Config config, Action<bool, string> updateFunc)
{ {
_updateFunc = updateFunc; _updateFunc = updateFunc;
@@ -477,11 +487,28 @@ namespace ServiceLib.Services
? Global.GeoUrl ? Global.GeoUrl
: config.ConstItem.GeoSourceUrl; : config.ConstItem.GeoSourceUrl;
var fileName = $"{geoName}.dat"; List<string> files = ["geosite", "geoip"];
var targetPath = Utils.GetBinPath($"{fileName}"); foreach (var geoName in files)
var url = string.Format(geoUrl, geoName); {
var fileName = $"{geoName}.dat";
var targetPath = Utils.GetBinPath($"{fileName}");
var url = string.Format(geoUrl, geoName);
await DownloadGeoFile(url, fileName, targetPath, updateFunc); await DownloadGeoFile(url, fileName, targetPath, updateFunc);
}
}
private async Task UpdateOtherFiles(Config config, Action<bool, string> updateFunc)
{
_updateFunc = updateFunc;
foreach (var url in Global.OtherGeoUrls)
{
var fileName = Path.GetFileName(url);
var targetPath = Utils.GetBinPath($"{fileName}");
await DownloadGeoFile(url, fileName, targetPath, updateFunc);
}
} }
private async Task UpdateSrsFileAll(Config config, Action<bool, string> updateFunc) private async Task UpdateSrsFileAll(Config config, Action<bool, string> updateFunc)

View File

@@ -173,7 +173,7 @@ namespace ServiceLib.ViewModels
var configDirZipTemp = Utils.GetTempPath($"v2rayN_{DateTime.Now:yyyyMMddHHmmss}"); var configDirZipTemp = Utils.GetTempPath($"v2rayN_{DateTime.Now:yyyyMMddHHmmss}");
var configDirTemp = Path.Combine(configDirZipTemp, _guiConfigs); var configDirTemp = Path.Combine(configDirZipTemp, _guiConfigs);
FileManager.CopyDirectory(configDir, configDirTemp, false, true, "cache.db"); FileManager.CopyDirectory(configDir, configDirTemp, false, true, "");
var ret = FileManager.CreateFromDirectory(configDirZipTemp, fileName); var ret = FileManager.CreateFromDirectory(configDirZipTemp, fileName);
Directory.Delete(configDirZipTemp, true); Directory.Delete(configDirZipTemp, true);
return await Task.FromResult(ret); return await Task.FromResult(ret);

View File

@@ -0,0 +1,65 @@
using System.Reactive;
using ReactiveUI;
namespace ServiceLib.ViewModels
{
public class GlobalHotkeySettingViewModel : MyReactiveObject
{
private readonly List<KeyEventItem> _globalHotkeys;
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public GlobalHotkeySettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
{
_config = AppHandler.Instance.Config;
_updateView = updateView;
_globalHotkeys = JsonUtils.DeepCopy(_config.GlobalHotkeys);
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SaveSettingAsync();
});
}
public KeyEventItem GetKeyEventItem(EGlobalHotkey eg)
{
var item = _globalHotkeys.FirstOrDefault((it) => it.EGlobalHotkey == eg);
if (item != null)
{
return item;
}
item = new()
{
EGlobalHotkey = eg,
Control = false,
Alt = false,
Shift = false,
KeyCode = null
};
_globalHotkeys.Add(item);
return item;
}
public void ResetKeyEventItem()
{
_globalHotkeys.Clear();
}
private async Task SaveSettingAsync()
{
_config.GlobalHotkeys = _globalHotkeys;
if (await ConfigHandler.SaveConfig(_config) == 0)
{
_updateView?.Invoke(EViewAction.CloseWindow, null);
}
else
{
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
}
}
}
}

View File

@@ -63,7 +63,7 @@ namespace ServiceLib.ViewModels
[Reactive] public int SpeedTestTimeout { get; set; } [Reactive] public int SpeedTestTimeout { get; set; }
[Reactive] public string SpeedTestUrl { get; set; } [Reactive] public string SpeedTestUrl { get; set; }
[Reactive] public string SpeedPingTestUrl { get; set; } [Reactive] public string SpeedPingTestUrl { get; set; }
//[Reactive] public int SpeedTestPageSize { get; set; } [Reactive] public int MixedConcurrencyCount { get; set; }
[Reactive] public bool EnableHWA { get; set; } [Reactive] public bool EnableHWA { get; set; }
[Reactive] public string SubConvertUrl { get; set; } [Reactive] public string SubConvertUrl { get; set; }
[Reactive] public int MainGirdOrientation { get; set; } [Reactive] public int MainGirdOrientation { get; set; }
@@ -178,7 +178,7 @@ namespace ServiceLib.ViewModels
CurrentFontFamily = _config.UiItem.CurrentFontFamily; CurrentFontFamily = _config.UiItem.CurrentFontFamily;
SpeedTestTimeout = _config.SpeedTestItem.SpeedTestTimeout; SpeedTestTimeout = _config.SpeedTestItem.SpeedTestTimeout;
SpeedTestUrl = _config.SpeedTestItem.SpeedTestUrl; SpeedTestUrl = _config.SpeedTestItem.SpeedTestUrl;
//SpeedTestPageSize = _config.SpeedTestItem.SpeedTestPageSize; MixedConcurrencyCount = _config.SpeedTestItem.MixedConcurrencyCount;
SpeedPingTestUrl = _config.SpeedTestItem.SpeedPingTestUrl; SpeedPingTestUrl = _config.SpeedTestItem.SpeedPingTestUrl;
EnableHWA = _config.GuiItem.EnableHWA; EnableHWA = _config.GuiItem.EnableHWA;
SubConvertUrl = _config.ConstItem.SubConvertUrl; SubConvertUrl = _config.ConstItem.SubConvertUrl;
@@ -332,7 +332,7 @@ namespace ServiceLib.ViewModels
_config.GuiItem.TrayMenuServersLimit = TrayMenuServersLimit; _config.GuiItem.TrayMenuServersLimit = TrayMenuServersLimit;
_config.UiItem.CurrentFontFamily = CurrentFontFamily; _config.UiItem.CurrentFontFamily = CurrentFontFamily;
_config.SpeedTestItem.SpeedTestTimeout = SpeedTestTimeout; _config.SpeedTestItem.SpeedTestTimeout = SpeedTestTimeout;
//_config.SpeedTestItem.SpeedTestPageSize = SpeedTestPageSize; _config.SpeedTestItem.MixedConcurrencyCount = MixedConcurrencyCount;
_config.SpeedTestItem.SpeedTestUrl = SpeedTestUrl; _config.SpeedTestItem.SpeedTestUrl = SpeedTestUrl;
_config.SpeedTestItem.SpeedPingTestUrl = SpeedPingTestUrl; _config.SpeedTestItem.SpeedPingTestUrl = SpeedPingTestUrl;
_config.GuiItem.EnableHWA = EnableHWA; _config.GuiItem.EnableHWA = EnableHWA;

View File

@@ -16,6 +16,7 @@ namespace ServiceLib.ViewModels
private List<ProfileItem> _lstProfile; private List<ProfileItem> _lstProfile;
private string _serverFilter = string.Empty; private string _serverFilter = string.Empty;
private Dictionary<string, bool> _dicHeaderSort = new(); private Dictionary<string, bool> _dicHeaderSort = new();
private SpeedtestService? _speedtestService;
#endregion private prop #endregion private prop
@@ -78,6 +79,7 @@ namespace ServiceLib.ViewModels
public ReactiveCommand<Unit, Unit> RealPingServerCmd { get; } public ReactiveCommand<Unit, Unit> RealPingServerCmd { get; }
public ReactiveCommand<Unit, Unit> SpeedServerCmd { get; } public ReactiveCommand<Unit, Unit> SpeedServerCmd { get; }
public ReactiveCommand<Unit, Unit> SortServerResultCmd { get; } public ReactiveCommand<Unit, Unit> SortServerResultCmd { get; }
public ReactiveCommand<Unit, Unit> RemoveInvalidServerResultCmd { get; }
//servers export //servers export
public ReactiveCommand<Unit, Unit> Export2ClientConfigCmd { get; } public ReactiveCommand<Unit, Unit> Export2ClientConfigCmd { get; }
@@ -196,6 +198,10 @@ namespace ServiceLib.ViewModels
{ {
await SortServer(EServerColName.DelayVal.ToString()); await SortServer(EServerColName.DelayVal.ToString());
}); });
RemoveInvalidServerResultCmd = ReactiveCommand.CreateFromTask(async () =>
{
await RemoveInvalidServerResult();
});
//servers export //servers export
Export2ClientConfigCmd = ReactiveCommand.CreateFromTask(async () => Export2ClientConfigCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
@@ -268,20 +274,22 @@ namespace ServiceLib.ViewModels
return; return;
} }
var item = _profileItems.FirstOrDefault(it => it.IndexId == result.IndexId); var item = _profileItems.FirstOrDefault(it => it.IndexId == result.IndexId);
if (item != null) if (item == null)
{ {
if (Utils.IsNotEmpty(result.Delay)) return;
{
int.TryParse(result.Delay, out int temp);
item.Delay = temp;
item.DelayVal = $"{result.Delay} {Global.DelayUnit}";
}
if (Utils.IsNotEmpty(result.Speed))
{
item.SpeedVal = $"{result.Speed} {Global.SpeedUnit}";
}
_profileItems.Replace(item, JsonUtils.DeepCopy(item));
} }
if (Utils.IsNotEmpty(result.Delay))
{
int.TryParse(result.Delay, out var temp);
item.Delay = temp;
item.DelayVal = result.Delay ?? string.Empty;
}
if (Utils.IsNotEmpty(result.Speed))
{
item.SpeedVal = result.Speed ?? string.Empty;
}
_profileItems.Replace(item, JsonUtils.DeepCopy(item));
} }
public void UpdateStatistics(ServerSpeedItem update) public void UpdateStatistics(ServerSpeedItem update)
@@ -420,10 +428,11 @@ namespace ServiceLib.ViewModels
Subid = t.Subid, Subid = t.Subid,
SubRemarks = t.SubRemarks, SubRemarks = t.SubRemarks,
IsActive = t.IndexId == _config.IndexId, IsActive = t.IndexId == _config.IndexId,
Sort = t33 == null ? 0 : t33.Sort, Sort = t33?.Sort ?? 0,
Delay = t33 == null ? 0 : t33.Delay, Delay = t33?.Delay ?? 0,
DelayVal = t33?.Delay != 0 ? $"{t33?.Delay} {Global.DelayUnit}" : string.Empty, Speed = t33?.Speed ?? 0,
SpeedVal = t33?.Speed != 0 ? $"{t33?.Speed} {Global.SpeedUnit}" : string.Empty, DelayVal = t33?.Delay != 0 ? $"{t33?.Delay}" : string.Empty,
SpeedVal = t33?.Speed > 0 ? $"{t33?.Speed}" : t33?.Message ?? string.Empty,
TodayDown = t22 == null ? "" : Utils.HumanFy(t22.TodayDown), TodayDown = t22 == null ? "" : Utils.HumanFy(t22.TodayDown),
TodayUp = t22 == null ? "" : Utils.HumanFy(t22.TodayUp), TodayUp = t22 == null ? "" : Utils.HumanFy(t22.TodayUp),
TotalDown = t22 == null ? "" : Utils.HumanFy(t22.TotalDown), TotalDown = t22 == null ? "" : Utils.HumanFy(t22.TotalDown),
@@ -511,7 +520,7 @@ namespace ServiceLib.ViewModels
} }
var exists = lstSelecteds.Exists(t => t.IndexId == _config.IndexId); var exists = lstSelecteds.Exists(t => t.IndexId == _config.IndexId);
await ConfigHandler.RemoveServer(_config, lstSelecteds); await ConfigHandler.RemoveServers(_config, lstSelecteds);
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
if (lstSelecteds.Count == _profileItems.Count) if (lstSelecteds.Count == _profileItems.Count)
{ {
@@ -655,6 +664,13 @@ namespace ServiceLib.ViewModels
RefreshServers(); RefreshServers();
} }
public async Task RemoveInvalidServerResult()
{
var count = await ConfigHandler.RemoveInvalidServerResult(_config, _config.SubIndexId);
RefreshServers();
NoticeHandler.Instance.Enqueue(string.Format(ResUI.RemoveInvalidServerResultTip, count));
}
//move server //move server
private async Task MoveToGroup(bool c) private async Task MoveToGroup(bool c)
{ {
@@ -722,15 +738,13 @@ namespace ServiceLib.ViewModels
return; return;
} }
_ = new SpeedtestService(_config, actionType, lstSelecteds, (SpeedTestResult result) => _speedtestService ??= new SpeedtestService(_config, (SpeedTestResult result) => _updateView?.Invoke(EViewAction.DispatcherSpeedTest, result));
{ _speedtestService?.RunLoop(actionType, lstSelecteds);
_updateView?.Invoke(EViewAction.DispatcherSpeedTest, result);
});
} }
public void ServerSpeedtestStop() public void ServerSpeedtestStop()
{ {
MessageBus.Current.SendMessage("", EMsgCommand.StopSpeedtest.ToString()); _speedtestService?.ExitLoop();
} }
private async Task Export2ClientConfigAsync(bool blClipboard) private async Task Export2ClientConfigAsync(bool blClipboard)

View File

@@ -60,6 +60,9 @@ namespace ServiceLib.ViewModels
[Reactive] [Reactive]
public int SystemProxySelected { get; set; } public int SystemProxySelected { get; set; }
[Reactive]
public bool BlSystemProxyPacVisible { get; set; }
#endregion System Proxy #endregion System Proxy
#region UI #region UI
@@ -96,6 +99,7 @@ namespace ServiceLib.ViewModels
SelectedRouting = new(); SelectedRouting = new();
SelectedServer = new(); SelectedServer = new();
RunningServerToolTipText = "-"; RunningServerToolTipText = "-";
BlSystemProxyPacVisible = Utils.IsWindows();
if (_config.TunModeItem.EnableTun && AllowEnableTun()) if (_config.TunModeItem.EnableTun && AllowEnableTun())
{ {
@@ -312,6 +316,8 @@ namespace ServiceLib.ViewModels
return; return;
} }
_updateView?.Invoke(EViewAction.DispatcherServerAvailability, ResUI.Speedtesting);
var msg = await (new UpdateService()).RunAvailabilityCheck(); var msg = await (new UpdateService()).RunAvailabilityCheck();
NoticeHandler.Instance.SendMessageEx(msg); NoticeHandler.Instance.SendMessageEx(msg);

View File

@@ -19,6 +19,7 @@
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="Assets/GlobalResources.axaml" /> <ResourceInclude Source="Assets/GlobalResources.axaml" />
<ResourceInclude Source="Controls/AutoCompleteBox.axaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
</ResourceDictionary> </ResourceDictionary>
</Application.Resources> </Application.Resources>
@@ -46,6 +47,12 @@
Header="{x:Static resx:ResUI.menuSystemProxyNothing}" Header="{x:Static resx:ResUI.menuSystemProxyNothing}"
IsChecked="{Binding BlSystemProxyNothing}" IsChecked="{Binding BlSystemProxyNothing}"
ToggleType="Radio" /> ToggleType="Radio" />
<NativeMenuItem
Command="{Binding SystemProxyPacCmd}"
Header="{x:Static resx:ResUI.menuSystemProxyPac}"
IsChecked="{Binding BlSystemProxyPac}"
IsVisible="{Binding BlSystemProxyPacVisible}"
ToggleType="Radio" />
<NativeMenuItemSeparator /> <NativeMenuItemSeparator />
<NativeMenuItem Click="MenuAddServerViaClipboardClick" Header="{x:Static resx:ResUI.menuAddServerViaClipboard}" /> <NativeMenuItem Click="MenuAddServerViaClipboardClick" Header="{x:Static resx:ResUI.menuAddServerViaClipboard}" />
<NativeMenuItem Header="{x:Static resx:ResUI.menuAddServerViaScan}" IsVisible="False" /> <NativeMenuItem Header="{x:Static resx:ResUI.menuAddServerViaScan}" IsVisible="False" />
@@ -61,4 +68,4 @@
</TrayIcon> </TrayIcon>
</TrayIcons> </TrayIcons>
</TrayIcon.Icons> </TrayIcon.Icons>
</Application> </Application>

View File

@@ -8,4 +8,13 @@
<StreamGeometry x:Key="building_refresh">M849.652671 679.144788l111.007233-174.965917-50.615794 0C905.498584 274.107915 717.720873 88.965218 486.575446 88.965218c-233.998405 0-423.716304 189.698456-423.716304 423.707095 0 233.998405 189.716876 423.715281 423.716304 423.715281 113.936959 0 217.278605-45.079708 293.440216-118.235868l-62.46568-108.306728c-55.750745 65.205071-138.455375 106.709347-230.974535 106.709347-167.843706 0-303.882032-136.039349-303.882032-303.883055S318.732763 208.788234 486.575446 208.788234c164.951843 0 298.899554 131.522476 303.44508 295.389614l-51.357691 0L849.652671 679.144788z</StreamGeometry> <StreamGeometry x:Key="building_refresh">M849.652671 679.144788l111.007233-174.965917-50.615794 0C905.498584 274.107915 717.720873 88.965218 486.575446 88.965218c-233.998405 0-423.716304 189.698456-423.716304 423.707095 0 233.998405 189.716876 423.715281 423.716304 423.715281 113.936959 0 217.278605-45.079708 293.440216-118.235868l-62.46568-108.306728c-55.750745 65.205071-138.455375 106.709347-230.974535 106.709347-167.843706 0-303.882032-136.039349-303.882032-303.883055S318.732763 208.788234 486.575446 208.788234c164.951843 0 298.899554 131.522476 303.44508 295.389614l-51.357691 0L849.652671 679.144788z</StreamGeometry>
<StreamGeometry x:Key="building_ping">M273.28 899.328c-6.4 6.4-16 9.6-25.6 9.6-6.4 0-12.8-3.2-18.56-6.4-102.4-85.76-162.56-209.92-162.56-343.68 0-245.12 200.32-445.44 445.44-445.44s445.44 200.32 445.44 445.44c0 133.76-56.96 257.92-162.56 343.68-12.16 12.8-34.56 9.6-44.16-3.2-12.8-12.8-9.6-35.2 3.2-44.8a377.152 377.152 0 0 0 136.96-292.48c0-209.92-172.16-382.08-382.08-382.08-206.72-3.2-378.88 168.96-378.88 378.88 0 114.56 51.2 222.72 140.16 295.68 12.8 12.8 16 32 3.2 44.8z m394.88-540.8c12.8-12.8 31.36-12.8 44.16 0 12.8 12.8 12.8 32 0 44.8l-138.88 138.88c1.28 5.12 2.56 10.88 2.56 16.64 0 35.2-28.8 64-64 64-5.76 0-11.52-1.28-16.64-2.56l-24.32 24.32c-6.4 6.4-12.8 9.6-22.4 9.6-9.6 0-16-3.2-22.4-9.6-12.8-12.8-12.8-31.36 0-44.16l24.32-24.96a69.76 69.76 0 0 1-1.92-16.64c0-35.2 28.16-63.36 63.36-63.36 5.76 0 11.52 0.64 16.64 1.92z</StreamGeometry> <StreamGeometry x:Key="building_ping">M273.28 899.328c-6.4 6.4-16 9.6-25.6 9.6-6.4 0-12.8-3.2-18.56-6.4-102.4-85.76-162.56-209.92-162.56-343.68 0-245.12 200.32-445.44 445.44-445.44s445.44 200.32 445.44 445.44c0 133.76-56.96 257.92-162.56 343.68-12.16 12.8-34.56 9.6-44.16-3.2-12.8-12.8-9.6-35.2 3.2-44.8a377.152 377.152 0 0 0 136.96-292.48c0-209.92-172.16-382.08-382.08-382.08-206.72-3.2-378.88 168.96-378.88 378.88 0 114.56 51.2 222.72 140.16 295.68 12.8 12.8 16 32 3.2 44.8z m394.88-540.8c12.8-12.8 31.36-12.8 44.16 0 12.8 12.8 12.8 32 0 44.8l-138.88 138.88c1.28 5.12 2.56 10.88 2.56 16.64 0 35.2-28.8 64-64 64-5.76 0-11.52-1.28-16.64-2.56l-24.32 24.32c-6.4 6.4-12.8 9.6-22.4 9.6-9.6 0-16-3.2-22.4-9.6-12.8-12.8-12.8-31.36 0-44.16l24.32-24.96a69.76 69.76 0 0 1-1.92-16.64c0-35.2 28.16-63.36 63.36-63.36 5.76 0 11.52 0.64 16.64 1.92z</StreamGeometry>
<x:Double x:Key="IconButtonWidth">32</x:Double>
<x:Double x:Key="IconButtonHeight">32</x:Double>
<Thickness x:Key="Margin2">2</Thickness>
<Thickness x:Key="MarginLr4">4,0</Thickness>
<Thickness x:Key="Margin4">4</Thickness>
<Thickness x:Key="MarginLr8">8,0</Thickness>
<Thickness x:Key="MarginTb8">0,8</Thickness>
<Thickness x:Key="Margin8">8</Thickness>
</ResourceDictionary> </ResourceDictionary>

View File

@@ -5,19 +5,12 @@
</Border> </Border>
</Design.PreviewWith> </Design.PreviewWith>
<Style Selector="TextBlock.Margin8"> <Style Selector="DataGrid">
<Setter Property="Margin" Value="8" /> <Setter Property="RowHeight" Value="24" />
</Style> </Style>
<Style Selector="StackPanel.Margin8">
<Setter Property="Margin" Value="8" /> <Style Selector="PathIcon">
<Setter Property="Width" Value="16" />
<Setter Property="Height" Value="16" />
</Style> </Style>
<Style Selector="DockPanel.Margin8"> </Styles>
<Setter Property="Margin" Value="8" />
</Style>
<Style Selector="WrapPanel.Margin8">
<Setter Property="Margin" Value="8" />
</Style>
<Style Selector="Grid.Margin8">
<Setter Property="Margin" Value="8" />
</Style>
</Styles>

View File

@@ -1,4 +1,4 @@
using Avalonia; using Avalonia;
using Avalonia.Media; using Avalonia.Media;
namespace v2rayN.Desktop.Common namespace v2rayN.Desktop.Common
@@ -10,9 +10,9 @@ namespace v2rayN.Desktop.Common
var uri = Path.Combine(Global.AvaAssets, "Fonts#Noto Sans SC"); var uri = Path.Combine(Global.AvaAssets, "Fonts#Noto Sans SC");
return appBuilder.With(new FontManagerOptions() return appBuilder.With(new FontManagerOptions()
{ {
DefaultFamilyName = uri, //DefaultFamilyName = uri,
FontFallbacks = new[] { new FontFallback { FontFamily = new FontFamily(uri) } } FontFallbacks = new[] { new FontFallback { FontFamily = new FontFamily(uri) } }
}); });
} }
} }
} }

View File

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

View File

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

View File

@@ -0,0 +1,91 @@
using System.Reactive.Linq;
using Avalonia.Input;
using Avalonia.ReactiveUI;
using Avalonia.Win32.Input;
using GlobalHotKeys;
namespace v2rayN.Desktop.Handler
{
public sealed class HotkeyHandler
{
private static readonly Lazy<HotkeyHandler> _instance = new(() => new());
public static HotkeyHandler Instance = _instance.Value;
private readonly Dictionary<int, EGlobalHotkey> _hotkeyTriggerDic = new();
private HotKeyManager? _hotKeyManager;
private Config? _config;
private event Action<EGlobalHotkey>? _updateFunc;
public bool IsPause { get; set; } = false;
public void Init(Config config, Action<EGlobalHotkey> updateFunc)
{
_config = config;
_updateFunc = updateFunc;
Register();
}
public void Dispose()
{
_hotKeyManager?.Dispose();
}
private void Register()
{
if (_config.GlobalHotkeys.Any(t => t.KeyCode > 0) == false)
{
return;
}
_hotKeyManager ??= new GlobalHotKeys.HotKeyManager();
_hotkeyTriggerDic.Clear();
foreach (var item in _config.GlobalHotkeys)
{
if (item.KeyCode is null or 0)
{
continue;
}
var vKey = KeyInterop.VirtualKeyFromKey((Key)item.KeyCode);
var modifiers = Modifiers.NoRepeat;
if (item.Control)
{
modifiers |= Modifiers.Control;
}
if (item.Shift)
{
modifiers |= Modifiers.Shift;
}
if (item.Alt)
{
modifiers |= Modifiers.Alt;
}
var result = _hotKeyManager?.Register((VirtualKeyCode)vKey, modifiers);
if (result?.IsSuccessful == true)
{
_hotkeyTriggerDic.Add(result.Id, item.EGlobalHotkey);
}
}
_hotKeyManager?.HotKeyPressed
.ObserveOn(AvaloniaScheduler.Instance)
.Subscribe(OnNext);
}
private void OnNext(HotKey key)
{
if (_updateFunc == null || IsPause)
{
return;
}
if (_hotkeyTriggerDic.TryGetValue(key.Id, out var value))
{
_updateFunc?.Invoke(value);
}
}
}
}

View File

@@ -119,6 +119,17 @@ namespace v2rayN.Desktop.ViewModels
Value = size, Value = size,
}); });
Application.Current?.Styles.Add(style); Application.Current?.Styles.Add(style);
ModifyFontSizeEx(size);
}
private void ModifyFontSizeEx(double size)
{
//DataGrid
var rowHeight = 20 + (size / 2);
var style = new Style(x => x.OfType<DataGrid>());
style.Add(new Setter(DataGrid.RowHeightProperty, rowHeight));
Application.Current?.Styles.Add(style);
} }
private void ModifyFontFamily() private void ModifyFontFamily()

View File

@@ -1,4 +1,4 @@
<Window <Window
x:Class="v2rayN.Desktop.Views.AddServer2Window" x:Class="v2rayN.Desktop.Views.AddServer2Window"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -6,17 +6,17 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib" xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="v2rayN" Title="{x:Static resx:ResUI.menuAddCustomServer}"
Width="700" Width="700"
Height="500" Height="500"
x:DataType="vms:AddServer2ViewModel" x:DataType="vms:AddServer2ViewModel"
ShowInTaskbar="False" ShowInTaskbar="False"
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
mc:Ignorable="d"> mc:Ignorable="d">
<DockPanel Classes="Margin8"> <DockPanel Margin="{StaticResource Margin8}">
<StackPanel <StackPanel
Margin="{StaticResource Margin4}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Classes="Margin8"
DockPanel.Dock="Bottom" DockPanel.Dock="Bottom"
Orientation="Horizontal"> Orientation="Horizontal">
<Button <Button
@@ -28,7 +28,7 @@
<Button <Button
x:Name="btnCancel" x:Name="btnCancel"
Width="100" Width="100"
Margin="8,0" Margin="{StaticResource MarginLr8}"
Content="{x:Static resx:ResUI.TbCancel}" Content="{x:Static resx:ResUI.TbCancel}"
Cursor="Hand" Cursor="Hand"
IsCancel="True" /> IsCancel="True" />
@@ -39,14 +39,14 @@
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.menuServers}" /> Text="{x:Static resx:ResUI.menuServers}" />
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbRemarks}" /> Text="{x:Static resx:ResUI.TbRemarks}" />
<TextBox <TextBox
@@ -54,24 +54,24 @@
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center" />
Classes="Margin8" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbAddress}" /> Text="{x:Static resx:ResUI.TbAddress}" />
<TextBox <TextBox
x:Name="txtAddress" x:Name="txtAddress"
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
IsReadOnly="True" /> IsReadOnly="True" />
<StackPanel <StackPanel
Grid.Row="2" Grid.Row="2"
@@ -80,46 +80,46 @@
Orientation="Horizontal"> Orientation="Horizontal">
<Button <Button
x:Name="btnBrowse" x:Name="btnBrowse"
Margin="2,0" Margin="{StaticResource MarginLr4}"
Content="{x:Static resx:ResUI.TbBrowse}" /> Content="{x:Static resx:ResUI.TbBrowse}" />
<Button <Button
x:Name="btnEdit" x:Name="btnEdit"
Margin="2,0" Margin="{StaticResource MarginLr4}"
Content="{x:Static resx:ResUI.TbEdit}" /> Content="{x:Static resx:ResUI.TbEdit}" />
</StackPanel> </StackPanel>
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbCoreType}" /> Text="{x:Static resx:ResUI.TbCoreType}" />
<ComboBox <ComboBox
x:Name="cmbCoreType" x:Name="cmbCoreType"
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Classes="Margin8"
MaxDropDownHeight="1000" /> MaxDropDownHeight="1000" />
<TextBlock <TextBlock
Grid.Row="4" Grid.Row="4"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbDisplayLog}" /> Text="{x:Static resx:ResUI.TbDisplayLog}" />
<StackPanel <StackPanel
Grid.Row="4" Grid.Row="4"
Grid.Column="1" Grid.Column="1"
Classes="Margin8" Margin="{StaticResource Margin4}"
Orientation="Horizontal"> Orientation="Horizontal">
<ToggleSwitch <ToggleSwitch
x:Name="togDisplayLog" x:Name="togDisplayLog"
HorizontalAlignment="Left" Margin="{StaticResource Margin4}"
Classes="Margin8" /> HorizontalAlignment="Left" />
<TextBlock <TextBlock
Margin="8,0" Margin="{StaticResource MarginLr8}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TipDisplayLog}" /> Text="{x:Static resx:ResUI.TipDisplayLog}" />
</StackPanel> </StackPanel>
@@ -127,21 +127,21 @@
<TextBlock <TextBlock
Grid.Row="5" Grid.Row="5"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbPreSocksPort}" /> Text="{x:Static resx:ResUI.TbPreSocksPort}" />
<TextBox <TextBox
x:Name="txtPreSocksPort" x:Name="txtPreSocksPort"
Grid.Row="5" Grid.Row="5"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
HorizontalAlignment="Left" Margin="{StaticResource Margin4}"
Classes="Margin8" /> HorizontalAlignment="Left" />
<StackPanel <StackPanel
Grid.Row="6" Grid.Row="6"
Grid.Column="1" Grid.Column="1"
Grid.ColumnSpan="2" Grid.ColumnSpan="2"
Classes="Margin8"> Margin="{StaticResource Margin4}">
<TextBlock <TextBlock
Width="500" Width="500"
VerticalAlignment="Center" VerticalAlignment="Center"
@@ -149,7 +149,7 @@
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock <TextBlock
Width="500" Width="500"
Margin="8,0" Margin="{StaticResource MarginLr8}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.CustomServerTips}" Text="{x:Static resx:ResUI.CustomServerTips}"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
@@ -157,4 +157,4 @@
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</DockPanel> </DockPanel>
</Window> </Window>

View File

@@ -1,4 +1,4 @@
<Window <Window
x:Class="v2rayN.Desktop.Views.AddServerWindow" x:Class="v2rayN.Desktop.Views.AddServerWindow"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -13,10 +13,10 @@
ShowInTaskbar="False" ShowInTaskbar="False"
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
mc:Ignorable="d"> mc:Ignorable="d">
<DockPanel Classes="Margin8"> <DockPanel Margin="{StaticResource Margin8}">
<StackPanel <StackPanel
Margin="{StaticResource Margin4}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Classes="Margin8"
DockPanel.Dock="Bottom" DockPanel.Dock="Bottom"
Orientation="Horizontal"> Orientation="Horizontal">
<Button <Button
@@ -28,7 +28,7 @@
<Button <Button
x:Name="btnCancel" x:Name="btnCancel"
Width="100" Width="100"
Margin="8,0" Margin="{StaticResource MarginLr8}"
Content="{x:Static resx:ResUI.TbCancel}" Content="{x:Static resx:ResUI.TbCancel}"
Cursor="Hand" Cursor="Hand"
IsCancel="True" /> IsCancel="True" />
@@ -44,7 +44,7 @@
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.menuServers}" /> Text="{x:Static resx:ResUI.menuServers}" />
<StackPanel <StackPanel
Grid.Row="0" Grid.Row="0"
@@ -53,52 +53,52 @@
<ComboBox <ComboBox
x:Name="cmbCoreType" x:Name="cmbCoreType"
Width="100" Width="100"
Classes="Margin8" Margin="{StaticResource Margin4}"
ToolTip.Tip="{x:Static resx:ResUI.TbCoreType}" /> ToolTip.Tip="{x:Static resx:ResUI.TbCoreType}" />
</StackPanel> </StackPanel>
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbRemarks}" /> Text="{x:Static resx:ResUI.TbRemarks}" />
<TextBox <TextBox
x:Name="txtRemarks" x:Name="txtRemarks"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbAddress}" /> Text="{x:Static resx:ResUI.TbAddress}" />
<TextBox <TextBox
x:Name="txtAddress" x:Name="txtAddress"
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbPort}" /> Text="{x:Static resx:ResUI.TbPort}" />
<TextBox <TextBox
x:Name="txtPort" x:Name="txtPort"
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
HorizontalAlignment="Left" Margin="{StaticResource Margin4}"
Classes="Margin8" /> HorizontalAlignment="Left" />
</Grid> </Grid>
<Separator Grid.Row="1" Margin="0,10" /> <Separator Grid.Row="1" Margin="{StaticResource MarginTb8}" />
<Grid <Grid
x:Name="gridVMess" x:Name="gridVMess"
@@ -110,48 +110,48 @@
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbId}" /> Text="{x:Static resx:ResUI.TbId}" />
<TextBox <TextBox
x:Name="txtId" x:Name="txtId"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<Button <Button
x:Name="btnGUID" x:Name="btnGUID"
Grid.Row="1" Grid.Row="1"
Grid.Column="2" Grid.Column="2"
Margin="4,0" Margin="{StaticResource MarginLr4}"
Content="{x:Static resx:ResUI.TbGUID}" /> Content="{x:Static resx:ResUI.TbGUID}" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbAlterId}" /> Text="{x:Static resx:ResUI.TbAlterId}" />
<TextBox <TextBox
x:Name="txtAlterId" x:Name="txtAlterId"
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
HorizontalAlignment="Left" Margin="{StaticResource Margin4}"
Classes="Margin8" /> HorizontalAlignment="Left" />
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSecurity}" /> Text="{x:Static resx:ResUI.TbSecurity}" />
<ComboBox <ComboBox
x:Name="cmbSecurity" x:Name="cmbSecurity"
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
</Grid> </Grid>
<Grid <Grid
x:Name="gridSs" x:Name="gridSs"
@@ -163,28 +163,28 @@
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbId3}" /> Text="{x:Static resx:ResUI.TbId3}" />
<TextBox <TextBox
x:Name="txtId3" x:Name="txtId3"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSecurity3}" /> Text="{x:Static resx:ResUI.TbSecurity3}" />
<ComboBox <ComboBox
x:Name="cmbSecurity3" x:Name="cmbSecurity3"
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Width="300" Width="300"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
</Grid> </Grid>
<Grid <Grid
x:Name="gridSocks" x:Name="gridSocks"
@@ -196,28 +196,28 @@
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSecurity4}" /> Text="{x:Static resx:ResUI.TbSecurity4}" />
<TextBox <TextBox
x:Name="txtSecurity4" x:Name="txtSecurity4"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbId4}" /> Text="{x:Static resx:ResUI.TbId4}" />
<TextBox <TextBox
x:Name="txtId4" x:Name="txtId4"
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
</Grid> </Grid>
<Grid <Grid
x:Name="gridVLESS" x:Name="gridVLESS"
@@ -229,48 +229,48 @@
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbId5}" /> Text="{x:Static resx:ResUI.TbId5}" />
<TextBox <TextBox
x:Name="txtId5" x:Name="txtId5"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<Button <Button
x:Name="btnGUID5" x:Name="btnGUID5"
Grid.Row="1" Grid.Row="1"
Grid.Column="2" Grid.Column="2"
Margin="4,0" Margin="{StaticResource MarginLr4}"
Content="{x:Static resx:ResUI.TbGUID}" /> Content="{x:Static resx:ResUI.TbGUID}" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbFlow5}" /> Text="{x:Static resx:ResUI.TbFlow5}" />
<ComboBox <ComboBox
x:Name="cmbFlow5" x:Name="cmbFlow5"
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSecurity5}" /> Text="{x:Static resx:ResUI.TbSecurity5}" />
<TextBox <TextBox
x:Name="txtSecurity5" x:Name="txtSecurity5"
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
HorizontalAlignment="Left" Margin="{StaticResource Margin4}"
Classes="Margin8" /> HorizontalAlignment="Left" />
</Grid> </Grid>
<Grid <Grid
x:Name="gridTrojan" x:Name="gridTrojan"
@@ -282,28 +282,28 @@
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbId3}" /> Text="{x:Static resx:ResUI.TbId3}" />
<TextBox <TextBox
x:Name="txtId6" x:Name="txtId6"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbFlow5}" /> Text="{x:Static resx:ResUI.TbFlow5}" />
<ComboBox <ComboBox
x:Name="cmbFlow6" x:Name="cmbFlow6"
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
</Grid> </Grid>
<Grid <Grid
x:Name="gridHysteria2" x:Name="gridHysteria2"
@@ -315,28 +315,28 @@
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbId3}" /> Text="{x:Static resx:ResUI.TbId3}" />
<TextBox <TextBox
x:Name="txtId7" x:Name="txtId7"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbPath7}" /> Text="{x:Static resx:ResUI.TbPath7}" />
<TextBox <TextBox
x:Name="txtPath7" x:Name="txtPath7"
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
</Grid> </Grid>
<Grid <Grid
x:Name="gridTuic" x:Name="gridTuic"
@@ -348,41 +348,41 @@
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbId}" /> Text="{x:Static resx:ResUI.TbId}" />
<TextBox <TextBox
x:Name="txtId8" x:Name="txtId8"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbId3}" /> Text="{x:Static resx:ResUI.TbId3}" />
<TextBox <TextBox
x:Name="txtSecurity8" x:Name="txtSecurity8"
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbHeaderType8}" /> Text="{x:Static resx:ResUI.TbHeaderType8}" />
<ComboBox <ComboBox
x:Name="cmbHeaderType8" x:Name="cmbHeaderType8"
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
</Grid> </Grid>
<Grid <Grid
x:Name="gridWireguard" x:Name="gridWireguard"
@@ -394,75 +394,75 @@
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbPrivateKey}" /> Text="{x:Static resx:ResUI.TbPrivateKey}" />
<TextBox <TextBox
x:Name="txtId9" x:Name="txtId9"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbPublicKey}" /> Text="{x:Static resx:ResUI.TbPublicKey}" />
<TextBox <TextBox
x:Name="txtPublicKey9" x:Name="txtPublicKey9"
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
HorizontalAlignment="Left" Margin="{StaticResource Margin4}"
Classes="Margin8" /> HorizontalAlignment="Left" />
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbReserved}" /> Text="{x:Static resx:ResUI.TbReserved}" />
<TextBox <TextBox
x:Name="txtPath9" x:Name="txtPath9"
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="4" Grid.Row="4"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbLocalAddress}" /> Text="{x:Static resx:ResUI.TbLocalAddress}" />
<TextBox <TextBox
x:Name="txtRequestHost9" x:Name="txtRequestHost9"
Grid.Row="4" Grid.Row="4"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="5" Grid.Row="5"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="Mtu" /> Text="Mtu" />
<TextBox <TextBox
x:Name="txtShortId9" x:Name="txtShortId9"
Grid.Row="5" Grid.Row="5"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
HorizontalAlignment="Left" Margin="{StaticResource Margin4}"
Classes="Margin8" /> HorizontalAlignment="Left" />
</Grid> </Grid>
<Separator <Separator
x:Name="sepa2" x:Name="sepa2"
Grid.Row="3" Grid.Row="3"
Margin="0,10" /> Margin="{StaticResource MarginTb8}" />
<Grid <Grid
x:Name="gridTransport" x:Name="gridTransport"
@@ -473,34 +473,34 @@
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"
Grid.ColumnSpan="2" Grid.ColumnSpan="2"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.GbTransport}" /> Text="{x:Static resx:ResUI.GbTransport}" />
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbNetwork}" /> Text="{x:Static resx:ResUI.TbNetwork}" />
<ComboBox <ComboBox
x:Name="cmbNetwork" x:Name="cmbNetwork"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="2" Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TipNetwork}" /> Text="{x:Static resx:ResUI.TipNetwork}" />
<TextBlock <TextBlock
x:Name="labHeaderType" x:Name="labHeaderType"
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbHeaderType}" /> Text="{x:Static resx:ResUI.TbHeaderType}" />
<StackPanel <StackPanel
Grid.Row="2" Grid.Row="2"
@@ -510,35 +510,32 @@
<ComboBox <ComboBox
x:Name="cmbHeaderType" x:Name="cmbHeaderType"
Width="200" Width="200"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<Button <Button
x:Name="btnExtra" x:Name="btnExtra"
Width="30" Width="{StaticResource IconButtonWidth}"
Height="30" Height="{StaticResource IconButtonHeight}"
Margin="10,0" Margin="{StaticResource MarginLr8}"
Theme="{DynamicResource BorderlessButton}"> Theme="{DynamicResource BorderlessButton}">
<Button.Content> <Button.Content>
<PathIcon <PathIcon Data="{StaticResource building_more}" Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
Width="20"
Height="20"
Data="{StaticResource building_more}"
Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
</Button.Content> </Button.Content>
<Button.Flyout> <Button.Flyout>
<Flyout> <Flyout>
<StackPanel> <StackPanel>
<TextBlock <TextBlock
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TransportExtraTip}" /> Text="{x:Static resx:ResUI.TransportExtraTip}" />
<TextBox <TextBox
x:Name="txtExtra" x:Name="txtExtra"
Width="400" Width="400"
MinHeight="100" MinHeight="100"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="TextArea Margin8" Classes="TextArea"
MinLines="6" MinLines="6"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
</StackPanel> </StackPanel>
@@ -550,52 +547,52 @@
x:Name="tipHeaderType" x:Name="tipHeaderType"
Grid.Row="2" Grid.Row="2"
Grid.Column="2" Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbHeaderType}" /> Text="{x:Static resx:ResUI.TbHeaderType}" />
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbRequestHost}" /> Text="{x:Static resx:ResUI.TbRequestHost}" />
<TextBox <TextBox
x:Name="txtRequestHost" x:Name="txtRequestHost"
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
x:Name="tipRequestHost" x:Name="tipRequestHost"
Grid.Row="3" Grid.Row="3"
Grid.Column="2" Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbRequestHost}" /> Text="{x:Static resx:ResUI.TbRequestHost}" />
<TextBlock <TextBlock
Grid.Row="4" Grid.Row="4"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbPath}" /> Text="{x:Static resx:ResUI.TbPath}" />
<TextBox <TextBox
x:Name="txtPath" x:Name="txtPath"
Grid.Row="4" Grid.Row="4"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
x:Name="tipPath" x:Name="tipPath"
Grid.Row="4" Grid.Row="4"
Grid.Column="2" Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbPath}" /> Text="{x:Static resx:ResUI.TbPath}" />
</Grid> </Grid>
<Separator Grid.Row="5" Margin="0,10" /> <Separator Grid.Row="5" Margin="{StaticResource MarginTb8}" />
<Grid <Grid
x:Name="gridTls" x:Name="gridTls"
@@ -606,15 +603,15 @@
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbStreamSecurity}" /> Text="{x:Static resx:ResUI.TbStreamSecurity}" />
<ComboBox <ComboBox
x:Name="cmbStreamSecurity" x:Name="cmbStreamSecurity"
Grid.Row="0" Grid.Row="0"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
</Grid> </Grid>
<Grid <Grid
x:Name="gridTlsMore" x:Name="gridTlsMore"
@@ -626,55 +623,55 @@
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSNI}" /> Text="{x:Static resx:ResUI.TbSNI}" />
<TextBox <TextBox
x:Name="txtSNI" x:Name="txtSNI"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
HorizontalAlignment="Left" Margin="{StaticResource Margin4}"
Classes="Margin8" /> HorizontalAlignment="Left" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbFingerprint}" /> Text="{x:Static resx:ResUI.TbFingerprint}" />
<ComboBox <ComboBox
x:Name="cmbFingerprint" x:Name="cmbFingerprint"
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbAlpn}" /> Text="{x:Static resx:ResUI.TbAlpn}" />
<ComboBox <ComboBox
x:Name="cmbAlpn" x:Name="cmbAlpn"
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="4" Grid.Row="4"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbAllowInsecure}" /> Text="{x:Static resx:ResUI.TbAllowInsecure}" />
<ComboBox <ComboBox
x:Name="cmbAllowInsecure" x:Name="cmbAllowInsecure"
Grid.Row="4" Grid.Row="4"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
</Grid> </Grid>
<Grid <Grid
x:Name="gridRealityMore" x:Name="gridRealityMore"
@@ -686,74 +683,74 @@
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSNI}" /> Text="{x:Static resx:ResUI.TbSNI}" />
<TextBox <TextBox
x:Name="txtSNI2" x:Name="txtSNI2"
Grid.Row="0" Grid.Row="0"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
HorizontalAlignment="Left" Margin="{StaticResource Margin4}"
Classes="Margin8" /> HorizontalAlignment="Left" />
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbFingerprint}" /> Text="{x:Static resx:ResUI.TbFingerprint}" />
<ComboBox <ComboBox
x:Name="cmbFingerprint2" x:Name="cmbFingerprint2"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbPublicKey}" /> Text="{x:Static resx:ResUI.TbPublicKey}" />
<TextBox <TextBox
x:Name="txtPublicKey" x:Name="txtPublicKey"
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
HorizontalAlignment="Left" Margin="{StaticResource Margin4}"
Classes="Margin8" /> HorizontalAlignment="Left" />
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbShortId}" /> Text="{x:Static resx:ResUI.TbShortId}" />
<TextBox <TextBox
x:Name="txtShortId" x:Name="txtShortId"
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
HorizontalAlignment="Left" Margin="{StaticResource Margin4}"
Classes="Margin8" /> HorizontalAlignment="Left" />
<TextBlock <TextBlock
Grid.Row="4" Grid.Row="4"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSpiderX}" /> Text="{x:Static resx:ResUI.TbSpiderX}" />
<TextBox <TextBox
x:Name="txtSpiderX" x:Name="txtSpiderX"
Grid.Row="4" Grid.Row="4"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
HorizontalAlignment="Left" Margin="{StaticResource Margin4}"
Classes="Margin8" /> HorizontalAlignment="Left" />
</Grid> </Grid>
<Separator Grid.Row="8" Margin="0,10" /> <Separator Grid.Row="8" Margin="{StaticResource MarginTb8}" />
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</DockPanel> </DockPanel>
</Window> </Window>

View File

@@ -1,4 +1,4 @@
<UserControl <UserControl
x:Class="v2rayN.Desktop.Views.BackupAndRestoreView" x:Class="v2rayN.Desktop.Views.BackupAndRestoreView"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -14,90 +14,87 @@
</Style> </Style>
</UserControl.Styles> </UserControl.Styles>
<DockPanel Classes="Margin8"> <DockPanel Margin="{StaticResource Margin8}">
<StackPanel Classes="Margin8" DockPanel.Dock="Bottom"> <StackPanel Margin="{StaticResource Margin4}" DockPanel.Dock="Bottom">
<TextBlock <TextBlock
Name="txtMsg" Name="txtMsg"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<Border <Border
Margin="4" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Theme="{DynamicResource CardBorder}"> Theme="{DynamicResource CardBorder}">
<Grid <Grid
Classes="Margin8" Margin="{StaticResource Margin4}"
ColumnDefinitions="300,200" ColumnDefinitions="300,200"
RowDefinitions="Auto,Auto,Auto,Auto"> RowDefinitions="Auto,Auto,Auto,Auto">
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.menuLocalBackupAndRestore}" /> Text="{x:Static resx:ResUI.menuLocalBackupAndRestore}" />
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.menuLocalBackup}" /> Text="{x:Static resx:ResUI.menuLocalBackup}" />
<Button <Button
Name="menuLocalBackup" Name="menuLocalBackup"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Content="{x:Static resx:ResUI.menuLocalBackup}" /> Content="{x:Static resx:ResUI.menuLocalBackup}" />
<Separator <Separator
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Grid.ColumnSpan="2" /> Grid.ColumnSpan="2"
Margin="{StaticResource MarginTb8}" />
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.menuLocalRestore}" /> Text="{x:Static resx:ResUI.menuLocalRestore}" />
<Button <Button
Name="menuLocalRestore" Name="menuLocalRestore"
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Content="{x:Static resx:ResUI.menuLocalRestore}" /> Content="{x:Static resx:ResUI.menuLocalRestore}" />
</Grid> </Grid>
</Border> </Border>
<Border <Border
Margin="4" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Theme="{DynamicResource CardBorder}"> Theme="{DynamicResource CardBorder}">
<Grid <Grid
Classes="Margin8" Margin="{StaticResource Margin4}"
ColumnDefinitions="300,200" ColumnDefinitions="300,200"
RowDefinitions="Auto,Auto,Auto,Auto,Auto"> RowDefinitions="Auto,Auto,Auto,Auto,Auto">
<StackPanel <StackPanel
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0"
Orientation="Horizontal"> Orientation="Horizontal">
<TextBlock Classes="Margin8" Text="{x:Static resx:ResUI.menuRemoteBackupAndRestore}" /> <TextBlock Margin="{StaticResource Margin4}" Text="{x:Static resx:ResUI.menuRemoteBackupAndRestore}" />
<Button <Button
Width="30" Width="{StaticResource IconButtonWidth}"
Height="30" Height="{StaticResource IconButtonHeight}"
MinWidth="30" MinWidth="{StaticResource IconButtonWidth}"
Margin="10,0" Margin="{StaticResource MarginLr8}"
Theme="{DynamicResource BorderlessButton}"> Theme="{DynamicResource BorderlessButton}">
<Button.Content> <Button.Content>
<PathIcon <PathIcon Data="{StaticResource building_more}" Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
Width="20"
Height="20"
Data="{StaticResource building_more}"
Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
</Button.Content> </Button.Content>
<Button.Flyout> <Button.Flyout>
<Flyout> <Flyout>
@@ -107,7 +104,7 @@
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.LvWebDavUrl}" /> Text="{x:Static resx:ResUI.LvWebDavUrl}" />
<TextBox <TextBox
@@ -115,14 +112,14 @@
Grid.Row="0" Grid.Row="0"
Grid.Column="1" Grid.Column="1"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.LvWebDavUserName}" /> Text="{x:Static resx:ResUI.LvWebDavUserName}" />
<TextBox <TextBox
@@ -130,13 +127,13 @@
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.LvWebDavPassword}" /> Text="{x:Static resx:ResUI.LvWebDavPassword}" />
<TextBox <TextBox
@@ -144,13 +141,13 @@
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.LvWebDavDirName}" /> Text="{x:Static resx:ResUI.LvWebDavDirName}" />
<TextBox <TextBox
@@ -158,7 +155,7 @@
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<Button <Button
x:Name="menuWebDavCheck" x:Name="menuWebDavCheck"
@@ -166,7 +163,7 @@
Grid.Column="1" Grid.Column="1"
HorizontalAlignment="Right" HorizontalAlignment="Right"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Content="{x:Static resx:ResUI.LvWebDavCheck}" /> Content="{x:Static resx:ResUI.LvWebDavCheck}" />
</Grid> </Grid>
</StackPanel> </StackPanel>
@@ -180,35 +177,36 @@
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.menuRemoteBackup}" /> Text="{x:Static resx:ResUI.menuRemoteBackup}" />
<Button <Button
Name="menuRemoteBackup" Name="menuRemoteBackup"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Content="{x:Static resx:ResUI.menuRemoteBackup}" /> Content="{x:Static resx:ResUI.menuRemoteBackup}" />
<Separator <Separator
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Grid.ColumnSpan="2" /> Grid.ColumnSpan="2"
Margin="{StaticResource MarginTb8}" />
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.menuRemoteRestore}" /> Text="{x:Static resx:ResUI.menuRemoteRestore}" />
<Button <Button
Name="menuRemoteRestore" Name="menuRemoteRestore"
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Content="{x:Static resx:ResUI.menuRemoteRestore}" /> Content="{x:Static resx:ResUI.menuRemoteRestore}" />
</Grid> </Grid>
</Border> </Border>
</StackPanel> </StackPanel>
</DockPanel> </DockPanel>
</UserControl> </UserControl>

View File

@@ -1,4 +1,4 @@
<UserControl <UserControl
x:Class="v2rayN.Desktop.Views.CheckUpdateView" x:Class="v2rayN.Desktop.Views.CheckUpdateView"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -11,40 +11,45 @@
x:DataType="vms:CheckUpdateViewModel" x:DataType="vms:CheckUpdateViewModel"
mc:Ignorable="d"> mc:Ignorable="d">
<DockPanel Classes="Margin8"> <DockPanel Margin="{StaticResource Margin8}">
<StackPanel <Border
HorizontalAlignment="Center" Margin="{StaticResource Margin4}"
Classes="Margin8" VerticalAlignment="Center"
DockPanel.Dock="Bottom" DockPanel.Dock="Bottom"
Orientation="Horizontal"> Theme="{DynamicResource CardBorder}">
<TextBlock <StackPanel
HorizontalAlignment="Left" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" HorizontalAlignment="Center"
Classes="Margin8" Orientation="Horizontal">
Text="{x:Static resx:ResUI.TbSettingsEnableCheckPreReleaseUpdate}" />
<ToggleSwitch
x:Name="togEnableCheckPreReleaseUpdate"
HorizontalAlignment="Left"
Classes="Margin8" />
<Button <TextBlock
x:Name="btnCheckUpdate" Margin="{StaticResource Margin4}"
Width="100" HorizontalAlignment="Left"
Classes="Margin8" VerticalAlignment="Center"
Content="{x:Static resx:ResUI.menuCheckUpdate}" /> Text="{x:Static resx:ResUI.TbSettingsEnableCheckPreReleaseUpdate}" />
</StackPanel> <ToggleSwitch
x:Name="togEnableCheckPreReleaseUpdate"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<Button
x:Name="btnCheckUpdate"
Width="100"
Margin="{StaticResource Margin4}"
Content="{x:Static resx:ResUI.menuCheckUpdate}" />
</StackPanel>
</Border>
<Border <Border
Margin="4" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Theme="{DynamicResource CardBorder}"> Theme="{DynamicResource CardBorder}">
<ListBox <ListBox
x:Name="lstCheckUpdates" x:Name="lstCheckUpdates"
BorderThickness="1" BorderThickness="1"
ItemsSource="{Binding CheckUpdateModels}" ItemsSource="{Binding CheckUpdateModels}">
Theme="{StaticResource ButtonRadioGroupListBox}">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<StackPanel Orientation="Vertical" /> <StackPanel Orientation="Vertical" />
@@ -57,7 +62,7 @@
<ToggleSwitch <ToggleSwitch
x:Name="togAutoRefresh" x:Name="togAutoRefresh"
Grid.Column="0" Grid.Column="0"
Margin="8" Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
IsChecked="{Binding IsSelected}" /> IsChecked="{Binding IsSelected}" />
@@ -79,4 +84,4 @@
</ListBox> </ListBox>
</Border> </Border>
</DockPanel> </DockPanel>
</UserControl> </UserControl>

View File

@@ -14,59 +14,51 @@
<DockPanel Margin="2"> <DockPanel Margin="2">
<WrapPanel <WrapPanel
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
DockPanel.Dock="Top" DockPanel.Dock="Top"
Orientation="Horizontal"> Orientation="Horizontal">
<TextBox <TextBox
x:Name="txtHostFilter" x:Name="txtHostFilter"
Width="200" Width="200"
Margin="8,0"
VerticalContentAlignment="Center" VerticalContentAlignment="Center"
Margin="{StaticResource MarginLr8}"
Watermark="{x:Static resx:ResUI.ConnectionsHostFilterTitle}" /> Watermark="{x:Static resx:ResUI.ConnectionsHostFilterTitle}" />
<Button <Button
x:Name="btnConnectionCloseAll" x:Name="btnConnectionCloseAll"
Width="30" Width="{StaticResource IconButtonWidth}"
Height="30" Height="{StaticResource IconButtonHeight}"
Margin="8,0"
Classes="Success" Classes="Success"
Margin="{StaticResource MarginLr8}"
Theme="{DynamicResource BorderlessButton}" Theme="{DynamicResource BorderlessButton}"
ToolTip.Tip="{x:Static resx:ResUI.menuConnectionCloseAll}"> ToolTip.Tip="{x:Static resx:ResUI.menuConnectionCloseAll}">
<Button.Content> <Button.Content>
<PathIcon <PathIcon Data="{StaticResource building_delete}" Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
Width="20"
Height="20"
Data="{StaticResource building_delete}"
Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
</Button.Content> </Button.Content>
</Button> </Button>
<Button <Button
x:Name="btnAutofitColumnWidth" x:Name="btnAutofitColumnWidth"
Width="30" Width="{StaticResource IconButtonWidth}"
Height="30" Height="{StaticResource IconButtonHeight}"
Margin="8,0"
Classes="Success" Classes="Success"
Margin="{StaticResource MarginLr8}"
Theme="{DynamicResource BorderlessButton}" Theme="{DynamicResource BorderlessButton}"
ToolTip.Tip="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}"> ToolTip.Tip="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}">
<Button.Content> <Button.Content>
<PathIcon <PathIcon Data="{StaticResource building_fit}" Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
Width="20"
Height="20"
Data="{StaticResource building_fit}"
Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
</Button.Content> </Button.Content>
</Button> </Button>
<TextBlock <TextBlock
Margin="8,0"
VerticalAlignment="Center" VerticalAlignment="Center"
Margin="{StaticResource MarginLr8}"
Text="{x:Static resx:ResUI.TbAutoRefresh}" /> Text="{x:Static resx:ResUI.TbAutoRefresh}" />
<ToggleSwitch <ToggleSwitch
x:Name="togAutoRefresh" x:Name="togAutoRefresh"
Margin="8,0" HorizontalAlignment="Left"
HorizontalAlignment="Left" /> Margin="{StaticResource MarginLr8}" />
</WrapPanel> </WrapPanel>
<DataGrid <DataGrid
@@ -108,4 +100,4 @@
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
</DockPanel> </DockPanel>
</UserControl> </UserControl>

View File

@@ -17,32 +17,32 @@
<DockPanel Margin="2"> <DockPanel Margin="2">
<WrapPanel <WrapPanel
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
DockPanel.Dock="Top" DockPanel.Dock="Top"
Orientation="Horizontal"> Orientation="Horizontal">
<TextBlock <TextBlock
Margin="8,0" Margin="{StaticResource MarginLr8}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.menuRulemode}" /> Text="{x:Static resx:ResUI.menuRulemode}" />
<ComboBox <ComboBox
x:Name="cmbRulemode" x:Name="cmbRulemode"
Width="100" Width="100"
Margin="8,0"> Margin="{StaticResource MarginLr8}">
<ComboBoxItem Content="{x:Static resx:ResUI.menuModeRule}" /> <ComboBoxItem Content="{x:Static resx:ResUI.menuModeRule}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuModeGlobal}" /> <ComboBoxItem Content="{x:Static resx:ResUI.menuModeGlobal}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuModeDirect}" /> <ComboBoxItem Content="{x:Static resx:ResUI.menuModeDirect}" />
</ComboBox> </ComboBox>
<TextBlock <TextBlock
Margin="8,0" Margin="{StaticResource MarginLr8}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSorting}" /> Text="{x:Static resx:ResUI.TbSorting}" />
<ComboBox <ComboBox
x:Name="cmbSorting" x:Name="cmbSorting"
Width="100" Width="100"
Margin="8,0"> Margin="{StaticResource MarginLr8}">
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDelay}" /> <ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDelay}" />
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingName}" /> <ComboBoxItem Content="{x:Static resx:ResUI.TbSortingName}" />
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDefault}" /> <ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDefault}" />
@@ -50,53 +50,44 @@
<Button <Button
x:Name="menuProxiesReload" x:Name="menuProxiesReload"
Width="30" Width="{StaticResource IconButtonWidth}"
Height="30" Height="{StaticResource IconButtonHeight}"
Margin="8,0" Margin="{StaticResource MarginLr8}"
Classes="Success" Classes="Success"
Theme="{DynamicResource BorderlessButton}" Theme="{DynamicResource BorderlessButton}"
ToolTip.Tip="{x:Static resx:ResUI.menuProxiesReload}"> ToolTip.Tip="{x:Static resx:ResUI.menuProxiesReload}">
<Button.Content> <Button.Content>
<PathIcon <PathIcon Data="{StaticResource building_refresh}" Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
Width="20"
Height="20"
Data="{StaticResource building_refresh}"
Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
</Button.Content> </Button.Content>
</Button> </Button>
<Button <Button
x:Name="menuProxiesDelaytest" x:Name="menuProxiesDelaytest"
Width="30" Width="{StaticResource IconButtonWidth}"
Height="30" Height="{StaticResource IconButtonHeight}"
Margin="8,0" Margin="{StaticResource MarginLr8}"
Classes="Success" Classes="Success"
Theme="{DynamicResource BorderlessButton}" Theme="{DynamicResource BorderlessButton}"
ToolTip.Tip="{x:Static resx:ResUI.menuProxiesDelaytest}"> ToolTip.Tip="{x:Static resx:ResUI.menuProxiesDelaytest}">
<Button.Content> <Button.Content>
<PathIcon <PathIcon Data="{StaticResource building_ping}" Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
Width="20"
Height="20"
Data="{StaticResource building_ping}"
Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
</Button.Content> </Button.Content>
</Button> </Button>
<TextBlock <TextBlock
Margin="8,0" Margin="{StaticResource MarginLr8}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbAutoRefresh}" /> Text="{x:Static resx:ResUI.TbAutoRefresh}" />
<ToggleSwitch <ToggleSwitch
x:Name="togAutoRefresh" x:Name="togAutoRefresh"
Margin="8,0" Margin="{StaticResource MarginLr8}"
HorizontalAlignment="Left" /> HorizontalAlignment="Left" />
</WrapPanel> </WrapPanel>
<DockPanel> <DockPanel>
<ListBox <ListBox
x:Name="lstProxyGroups" x:Name="lstProxyGroups"
DockPanel.Dock="Left" DockPanel.Dock="Left"
ItemsSource="{Binding ProxyGroups}" ItemsSource="{Binding ProxyGroups}">
Theme="{StaticResource ButtonRadioGroupListBox}">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<StackPanel Orientation="Vertical" /> <StackPanel Orientation="Vertical" />
@@ -104,27 +95,24 @@
</ItemsControl.ItemsPanel> </ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<Border <Label
Width="160" Width="160"
Margin="-8,-4" Margin="-4,-6"
Padding="0" Padding="0"
Theme="{StaticResource CardBorder}"> Theme="{DynamicResource TagLabel}">
<Grid Classes="Margin8" RowDefinitions="1*,8,1*"> <Grid Margin="{StaticResource Margin4}" RowDefinitions="1*,8,1*">
<DockPanel Grid.Row="0"> <DockPanel Grid.Row="0">
<TextBlock DockPanel.Dock="Right" Text="{Binding Type}" /> <TextBlock DockPanel.Dock="Right" Text="{Binding Type}" />
<TextBlock Text="{Binding Name}" /> <TextBlock Text="{Binding Name}" />
</DockPanel> </DockPanel>
<TextBlock Grid.Row="2" Text="{Binding Now}" /> <TextBlock Grid.Row="2" Text="{Binding Now}" />
</Grid> </Grid>
</Border> </Label>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ListBox> </ListBox>
<ListBox <ListBox x:Name="lstProxyDetails" ItemsSource="{Binding ProxyDetails}">
x:Name="lstProxyDetails"
ItemsSource="{Binding ProxyDetails}"
Theme="{StaticResource ButtonRadioGroupListBox}">
<ItemsControl.ContextMenu> <ItemsControl.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem x:Name="menuProxiesDelaytestPart" Header="{x:Static resx:ResUI.menuProxiesDelaytestPart}" /> <MenuItem x:Name="menuProxiesDelaytestPart" Header="{x:Static resx:ResUI.menuProxiesDelaytestPart}" />
@@ -138,11 +126,11 @@
</ItemsControl.ItemsPanel> </ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<Border <Label
Width="160" Width="160"
Margin="-12,-4" Margin="-10,-6"
Padding="0" Padding="0"
Theme="{StaticResource CardBorder}"> Theme="{DynamicResource TagLabel}">
<DockPanel> <DockPanel>
<Border <Border
Width="5" Width="5"
@@ -151,7 +139,7 @@
CornerRadius="4" CornerRadius="4"
DockPanel.Dock="Left" DockPanel.Dock="Left"
IsVisible="{Binding IsActive}" /> IsVisible="{Binding IsActive}" />
<Grid Classes="Margin8" RowDefinitions="1*,8,1*"> <Grid Margin="{StaticResource Margin4}" RowDefinitions="1*,8,1*">
<TextBlock Grid.Row="0" Text="{Binding Name}" /> <TextBlock Grid.Row="0" Text="{Binding Name}" />
<DockPanel Grid.Row="2"> <DockPanel Grid.Row="2">
<TextBlock <TextBlock
@@ -162,10 +150,10 @@
</DockPanel> </DockPanel>
</Grid> </Grid>
</DockPanel> </DockPanel>
</Border> </Label>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ListBox> </ListBox>
</DockPanel> </DockPanel>
</DockPanel> </DockPanel>
</UserControl> </UserControl>

View File

@@ -1,4 +1,4 @@
<Window <Window
x:Class="v2rayN.Desktop.Views.DNSSettingWindow" x:Class="v2rayN.Desktop.Views.DNSSettingWindow"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -13,10 +13,10 @@
ShowInTaskbar="False" ShowInTaskbar="False"
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
mc:Ignorable="d"> mc:Ignorable="d">
<DockPanel Classes="Margin8"> <DockPanel Margin="{StaticResource Margin8}">
<StackPanel <StackPanel
Margin="{StaticResource Margin4}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Classes="Margin8"
DockPanel.Dock="Bottom" DockPanel.Dock="Bottom"
Orientation="Horizontal"> Orientation="Horizontal">
<Button <Button
@@ -28,7 +28,7 @@
<Button <Button
x:Name="btnCancel" x:Name="btnCancel"
Width="100" Width="100"
Margin="8,0" Margin="{StaticResource MarginLr8}"
Content="{x:Static resx:ResUI.TbCancel}" Content="{x:Static resx:ResUI.TbCancel}"
Cursor="Hand" Cursor="Hand"
IsCancel="True" /> IsCancel="True" />
@@ -36,21 +36,21 @@
<TabControl HorizontalContentAlignment="Left"> <TabControl HorizontalContentAlignment="Left">
<TabItem Header="{x:Static resx:ResUI.TbSettingsCoreDns}"> <TabItem Header="{x:Static resx:ResUI.TbSettingsCoreDns}">
<DockPanel Classes="Margin8"> <DockPanel Margin="{StaticResource Margin8}">
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal"> <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<TextBlock <TextBlock
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSettingsRemoteDNS}" /> Text="{x:Static resx:ResUI.TbSettingsRemoteDNS}" />
<TextBlock VerticalAlignment="Center" Classes="Margin8"> <TextBlock Margin="{StaticResource Margin4}" VerticalAlignment="Center">
<HyperlinkButton Classes="WithIcon" Click="linkDnsObjectDoc_Click"> <HyperlinkButton Classes="WithIcon" Click="linkDnsObjectDoc_Click">
<TextBlock Text="{x:Static resx:ResUI.TbDnsObjectDoc}" /> <TextBlock Text="{x:Static resx:ResUI.TbDnsObjectDoc}" />
</HyperlinkButton> </HyperlinkButton>
</TextBlock> </TextBlock>
<Button <Button
x:Name="btnImportDefConfig4V2ray" x:Name="btnImportDefConfig4V2ray"
Classes="Margin8" Margin="{StaticResource Margin4}"
Content="{x:Static resx:ResUI.TbSettingDnsImportDefConfig}" Content="{x:Static resx:ResUI.TbSettingDnsImportDefConfig}"
Cursor="Hand" /> Cursor="Hand" />
</StackPanel> </StackPanel>
@@ -58,44 +58,45 @@
<WrapPanel DockPanel.Dock="Bottom" Orientation="Horizontal"> <WrapPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock <TextBlock
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSettingsUseSystemHosts}" /> Text="{x:Static resx:ResUI.TbSettingsUseSystemHosts}" />
<ToggleSwitch <ToggleSwitch
x:Name="togUseSystemHosts" x:Name="togUseSystemHosts"
HorizontalAlignment="Left" Margin="{StaticResource Margin4}"
Classes="Margin8" /> HorizontalAlignment="Left" />
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock <TextBlock
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSettingsDomainStrategy4Freedom}" /> Text="{x:Static resx:ResUI.TbSettingsDomainStrategy4Freedom}" />
<ComboBox <ComboBox
x:Name="cmbdomainStrategy4Freedom" x:Name="cmbdomainStrategy4Freedom"
Width="150" Width="150"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock <TextBlock
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSettingsDomainDNSAddress}" /> Text="{x:Static resx:ResUI.TbSettingsDomainDNSAddress}" />
<ComboBox <ComboBox
x:Name="cmbdomainDNSAddress" x:Name="cmbdomainDNSAddress"
Width="150" Width="150"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
</StackPanel> </StackPanel>
</WrapPanel> </WrapPanel>
<Grid Classes="Margin8"> <Grid Margin="{StaticResource Margin4}">
<TextBox <TextBox
x:Name="txtnormalDNS" x:Name="txtnormalDNS"
Margin="{StaticResource Margin4}"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
BorderThickness="1" BorderThickness="1"
Classes="TextArea Margin8" Classes="TextArea"
TextWrapping="Wrap" TextWrapping="Wrap"
Watermark="HTTP/SOCKS" /> Watermark="HTTP/SOCKS" />
</Grid> </Grid>
@@ -103,16 +104,16 @@
</TabItem> </TabItem>
<TabItem Header="{x:Static resx:ResUI.TbSettingsCoreDnsSingbox}"> <TabItem Header="{x:Static resx:ResUI.TbSettingsCoreDnsSingbox}">
<DockPanel Classes="Margin8"> <DockPanel Margin="{StaticResource Margin8}">
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal"> <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Classes="Margin8"> <TextBlock Margin="{StaticResource Margin4}" VerticalAlignment="Center">
<HyperlinkButton Classes="WithIcon" Click="linkDnsSingboxObjectDoc_Click"> <HyperlinkButton Classes="WithIcon" Click="linkDnsSingboxObjectDoc_Click">
<TextBlock Text="{x:Static resx:ResUI.TbDnsSingboxObjectDoc}" /> <TextBlock Text="{x:Static resx:ResUI.TbDnsSingboxObjectDoc}" />
</HyperlinkButton> </HyperlinkButton>
</TextBlock> </TextBlock>
<Button <Button
x:Name="btnImportDefConfig4Singbox" x:Name="btnImportDefConfig4Singbox"
Classes="Margin8" Margin="{StaticResource Margin4}"
Content="{x:Static resx:ResUI.TbSettingDnsImportDefConfig}" Content="{x:Static resx:ResUI.TbSettingDnsImportDefConfig}"
Cursor="Hand" /> Cursor="Hand" />
</StackPanel> </StackPanel>
@@ -120,28 +121,28 @@
<WrapPanel DockPanel.Dock="Bottom" Orientation="Horizontal"> <WrapPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock <TextBlock
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSettingsDomainStrategy4Out}" /> Text="{x:Static resx:ResUI.TbSettingsDomainStrategy4Out}" />
<ComboBox <ComboBox
x:Name="cmbdomainStrategy4Out" x:Name="cmbdomainStrategy4Out"
Width="150" Width="150"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock <TextBlock
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSettingsDomainDNSAddress}" /> Text="{x:Static resx:ResUI.TbSettingsDomainDNSAddress}" />
<ComboBox <ComboBox
x:Name="cmbdomainDNSAddress2" x:Name="cmbdomainDNSAddress2"
Width="150" Width="150"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
</StackPanel> </StackPanel>
</WrapPanel> </WrapPanel>
<Grid Classes="Margin8" ColumnDefinitions="*,10,*"> <Grid Margin="{StaticResource Margin4}" ColumnDefinitions="*,10,*">
<TextBox <TextBox
x:Name="txtnormalDNS2" x:Name="txtnormalDNS2"
@@ -150,7 +151,8 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
BorderThickness="1" BorderThickness="1"
Classes="TextArea Margin8" Classes="TextArea"
Margin="{StaticResource Margin4}"
TextWrapping="Wrap" TextWrapping="Wrap"
Watermark="HTTP/SOCKS" /> Watermark="HTTP/SOCKS" />
@@ -163,7 +165,8 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
BorderThickness="1" BorderThickness="1"
Classes="TextArea Margin8" Classes="TextArea"
Margin="{StaticResource Margin4}"
TextWrapping="Wrap" TextWrapping="Wrap"
Watermark="{x:Static resx:ResUI.TbSettingsTunMode}" /> Watermark="{x:Static resx:ResUI.TbSettingsTunMode}" />
</Grid> </Grid>
@@ -171,4 +174,4 @@
</TabItem> </TabItem>
</TabControl> </TabControl>
</DockPanel> </DockPanel>
</Window> </Window>

View File

@@ -1,4 +1,4 @@
<Window <Window
x:Class="v2rayN.Desktop.Views.GlobalHotkeySettingWindow" x:Class="v2rayN.Desktop.Views.GlobalHotkeySettingWindow"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -6,23 +6,23 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib" xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="{x:Static resx:ResUI.menuSetting}" Title="{x:Static resx:ResUI.menuGlobalHotkeySetting}"
Width="700" Width="700"
Height="500" Height="500"
x:DataType="vms:SubEditViewModel" x:DataType="vms:GlobalHotkeySettingViewModel"
ShowInTaskbar="False" ShowInTaskbar="False"
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
mc:Ignorable="d"> mc:Ignorable="d">
<DockPanel Classes="Margin8"> <DockPanel Margin="{StaticResource Margin8}">
<StackPanel <StackPanel
Margin="{StaticResource Margin4}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Classes="Margin8"
DockPanel.Dock="Bottom" DockPanel.Dock="Bottom"
Orientation="Horizontal"> Orientation="Horizontal">
<Button <Button
x:Name="btnReset" x:Name="btnReset"
Width="100" Width="100"
Margin="8,0" Margin="{StaticResource MarginLr8}"
Content="{x:Static resx:ResUI.TbReset}" /> Content="{x:Static resx:ResUI.TbReset}" />
<Button <Button
x:Name="btnSave" x:Name="btnSave"
@@ -33,7 +33,7 @@
<Button <Button
x:Name="btnCancel" x:Name="btnCancel"
Width="100" Width="100"
Margin="8,0" Margin="{StaticResource MarginLr8}"
Content="{x:Static resx:ResUI.TbCancel}" Content="{x:Static resx:ResUI.TbCancel}"
Cursor="Hand" Cursor="Hand"
IsCancel="True" /> IsCancel="True" />
@@ -49,85 +49,85 @@
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.TbGlobalHotkeySetting}" /> Text="{x:Static resx:ResUI.TbGlobalHotkeySetting}" />
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbDisplayGUI}" /> Text="{x:Static resx:ResUI.TbDisplayGUI}" />
<TextBox <TextBox
x:Name="txtGlobalHotkey0" x:Name="txtGlobalHotkey0"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
IsReadOnly="True" /> IsReadOnly="True" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbClearSystemProxy}" /> Text="{x:Static resx:ResUI.TbClearSystemProxy}" />
<TextBox <TextBox
x:Name="txtGlobalHotkey1" x:Name="txtGlobalHotkey1"
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
IsReadOnly="True" /> IsReadOnly="True" />
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSetSystemProxy}" /> Text="{x:Static resx:ResUI.TbSetSystemProxy}" />
<TextBox <TextBox
x:Name="txtGlobalHotkey2" x:Name="txtGlobalHotkey2"
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
IsReadOnly="True" /> IsReadOnly="True" />
<TextBlock <TextBlock
Grid.Row="4" Grid.Row="4"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbNotChangeSystemProxy}" /> Text="{x:Static resx:ResUI.TbNotChangeSystemProxy}" />
<TextBox <TextBox
x:Name="txtGlobalHotkey3" x:Name="txtGlobalHotkey3"
Grid.Row="4" Grid.Row="4"
Grid.Column="1" Grid.Column="1"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
IsReadOnly="True" /> IsReadOnly="True" />
<TextBlock <TextBlock
Grid.Row="5" Grid.Row="5"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSystemProxyPac}" /> Text="{x:Static resx:ResUI.TbSystemProxyPac}" />
<TextBox <TextBox
x:Name="txtGlobalHotkey4" x:Name="txtGlobalHotkey4"
Grid.Row="5" Grid.Row="5"
Grid.Column="1" Grid.Column="1"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
IsReadOnly="True" /> IsReadOnly="True" />
</Grid> </Grid>
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbGlobalHotkeySettingTip}" /> Text="{x:Static resx:ResUI.TbGlobalHotkeySettingTip}" />
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</DockPanel> </DockPanel>
</Window> </Window>

View File

@@ -1,129 +1,138 @@
using Avalonia.Controls; using System.Reactive.Disposables;
using System.Text;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.ReactiveUI;
using ReactiveUI;
using v2rayN.Desktop.Handler;
namespace v2rayN.Desktop.Views namespace v2rayN.Desktop.Views
{ {
public partial class GlobalHotkeySettingWindow : Window public partial class GlobalHotkeySettingWindow : ReactiveWindow<GlobalHotkeySettingViewModel>
{ {
private static Config _config = default!; private readonly List<object> _textBoxKeyEventItem = new();
private Dictionary<object, KeyEventItem> _TextBoxKeyEventItem = default!;
public GlobalHotkeySettingWindow() public GlobalHotkeySettingWindow()
{ {
InitializeComponent(); InitializeComponent();
ViewModel = new GlobalHotkeySettingViewModel(UpdateViewHandler);
btnReset.Click += btnReset_Click;
HotkeyHandler.Instance.IsPause = true;
this.Closing += (s, e) => HotkeyHandler.Instance.IsPause = false;
btnCancel.Click += (s, e) => this.Close(); btnCancel.Click += (s, e) => this.Close();
_config = AppHandler.Instance.Config;
//_config.globalHotkeys ??= new List<KeyEventItem>();
//txtGlobalHotkey0.KeyDown += TxtGlobalHotkey_PreviewKeyDown; this.WhenActivated(disposables =>
//txtGlobalHotkey1.KeyDown += TxtGlobalHotkey_PreviewKeyDown; {
//txtGlobalHotkey2.KeyDown += TxtGlobalHotkey_PreviewKeyDown; this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
//txtGlobalHotkey3.KeyDown += TxtGlobalHotkey_PreviewKeyDown; });
//txtGlobalHotkey4.KeyDown += TxtGlobalHotkey_PreviewKeyDown;
//HotkeyHandler.Instance.IsPause = true; Init();
//this.Closing += (s, e) => HotkeyHandler.Instance.IsPause = false; BindingData();
//InitData();
} }
//private void InitData() private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
//{ {
// _TextBoxKeyEventItem = new() switch (action)
// { {
// { txtGlobalHotkey0,GetKeyEventItemByEGlobalHotkey(_config.globalHotkeys,EGlobalHotkey.ShowForm) }, case EViewAction.CloseWindow:
// { txtGlobalHotkey1,GetKeyEventItemByEGlobalHotkey(_config.globalHotkeys,EGlobalHotkey.SystemProxyClear) }, this.Close(true);
// { txtGlobalHotkey2,GetKeyEventItemByEGlobalHotkey(_config.globalHotkeys,EGlobalHotkey.SystemProxySet) }, break;
// { txtGlobalHotkey3,GetKeyEventItemByEGlobalHotkey(_config.globalHotkeys,EGlobalHotkey.SystemProxyUnchanged)}, }
// { txtGlobalHotkey4,GetKeyEventItemByEGlobalHotkey(_config.globalHotkeys,EGlobalHotkey.SystemProxyPac)} return await Task.FromResult(true);
// }; }
// BindingData();
//}
//private void TxtGlobalHotkey_PreviewKeyDown(object? sender, KeyEventArgs e) private void Init()
//{ {
// e.Handled = true; _textBoxKeyEventItem.Add(txtGlobalHotkey0);
// var _ModifierKeys = new Key[] { Key.LeftCtrl, Key.RightCtrl, Key.LeftShift, _textBoxKeyEventItem.Add(txtGlobalHotkey1);
// Key.RightShift, Key.LeftAlt, Key.RightAlt, Key.LWin, Key.RWin}; _textBoxKeyEventItem.Add(txtGlobalHotkey2);
// _TextBoxKeyEventItem[sender].KeyCode = (int)(e.Key == Key.System ? (_ModifierKeys.Contains(e.SystemKey) ? Key.None : e.SystemKey) : (_ModifierKeys.Contains(e.Key) ? Key.None : e.Key)); _textBoxKeyEventItem.Add(txtGlobalHotkey3);
// _TextBoxKeyEventItem[sender].Alt = (Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt; _textBoxKeyEventItem.Add(txtGlobalHotkey4);
// _TextBoxKeyEventItem[sender].Control = (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control;
// _TextBoxKeyEventItem[sender].Shift = (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift;
// (sender as TextBox)!.Text = KeyEventItemToString(_TextBoxKeyEventItem[sender]);
//}
//private KeyEventItem GetKeyEventItemByEGlobalHotkey(List<KeyEventItem> KEList, EGlobalHotkey eg) for (var index = 0; index < _textBoxKeyEventItem.Count; index++)
//{ {
// return JsonUtils.DeepCopy(KEList.Find((it) => it.eGlobalHotkey == eg) ?? new() var sender = _textBoxKeyEventItem[index];
// { if (sender is not TextBox txtBox)
// eGlobalHotkey = eg, {
// Control = false, continue;
// Alt = false, }
// Shift = false, txtBox.Tag = (EGlobalHotkey)index;
// KeyCode = null txtBox.KeyDown += TxtGlobalHotkey_PreviewKeyDown;
// }); }
//} }
//private string KeyEventItemToString(KeyEventItem item) private void TxtGlobalHotkey_PreviewKeyDown(object? sender, KeyEventArgs e)
//{ {
// var res = new StringBuilder(); e.Handled = true;
if (sender is not TextBox txtBox)
{
return;
}
// if (item.Control) res.Append($"{ModifierKeys.Control}+"); var item = ViewModel?.GetKeyEventItem((EGlobalHotkey)txtBox.Tag);
// if (item.Shift) res.Append($"{ModifierKeys.Shift}+"); var modifierKeys = new Key[] { Key.LeftCtrl, Key.RightCtrl, Key.LeftShift, Key.RightShift, Key.LeftAlt, Key.RightAlt, Key.LWin, Key.RWin };
// if (item.Alt) res.Append($"{ModifierKeys.Alt}+");
// if (item.KeyCode != null && (Key)item.KeyCode != Key.None)
// res.Append($"{(Key)item.KeyCode}");
// return res.ToString(); item.KeyCode = (int)(e.Key == Key.System ? modifierKeys.Contains(Key.System) ? Key.None : Key.System : modifierKeys.Contains(e.Key) ? Key.None : e.Key);
//} item.Alt = (e.KeyModifiers & KeyModifiers.Alt) == KeyModifiers.Alt;
item.Control = (e.KeyModifiers & KeyModifiers.Control) == KeyModifiers.Control;
item.Shift = (e.KeyModifiers & KeyModifiers.Shift) == KeyModifiers.Shift;
//private void BindingData() txtBox.Text = KeyEventItemToString(item);
//{ }
// foreach (var item in _TextBoxKeyEventItem)
// {
// if (item.Value.KeyCode != null && (Key)item.Value.KeyCode != Key.None)
// {
// (item.Key as TextBox)!.Text = KeyEventItemToString(item.Value);
// }
// else
// {
// (item.Key as TextBox)!.Text = string.Empty;
// }
// }
//}
//private void btnSave_Click(object? sender, RoutedEventArgs e) private void BindingData()
//{ {
// _config.globalHotkeys = _TextBoxKeyEventItem.Values.ToList(); foreach (var sender in _textBoxKeyEventItem)
{
if (sender is not TextBox txtBox)
{
continue;
}
// if (ConfigHandler.SaveConfig(_config, false) == 0) var item = ViewModel?.GetKeyEventItem((EGlobalHotkey)txtBox.Tag);
// { txtBox.Text = KeyEventItemToString(item);
// HotkeyHandler.Instance.ReLoad(); }
// this.Close(); }
// }
// else
// {
// UI.Show(ResUI.OperationFailed);
// }
//}
//private void btnReset_Click(object? sender, RoutedEventArgs e) private void btnReset_Click(object sender, RoutedEventArgs e)
//{ {
// foreach (var k in _TextBoxKeyEventItem.Keys) ViewModel?.ResetKeyEventItem();
// { BindingData();
// _TextBoxKeyEventItem[k].Alt = false; }
// _TextBoxKeyEventItem[k].Control = false;
// _TextBoxKeyEventItem[k].Shift = false;
// _TextBoxKeyEventItem[k].KeyCode = (int)Key.None;
// }
// BindingData();
//}
//private void GlobalHotkeySettingWindow_KeyDown(object? sender, KeyEventArgs e) private string KeyEventItemToString(KeyEventItem? item)
//{ {
// if (e.Key == Key.Escape) if (item == null)
// { {
// this.Close(); return string.Empty;
// } }
//} var res = new StringBuilder();
if (item.Control)
{
res.Append($"{KeyModifiers.Control} +");
}
if (item.Shift)
{
res.Append($"{KeyModifiers.Shift} +");
}
if (item.Alt)
{
res.Append($"{KeyModifiers.Alt} +");
}
if (item.KeyCode != null && (Key)item.KeyCode != Key.None)
{
res.Append($"{(Key)item.KeyCode}");
}
return res.ToString();
}
} }
} }

View File

@@ -9,7 +9,7 @@
xmlns:view="using:v2rayN.Desktop.Views" xmlns:view="using:v2rayN.Desktop.Views"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib" xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="v2rayN" Title="v2rayN"
Width="900" Width="1000"
Height="600" Height="600"
MinWidth="900" MinWidth="900"
x:DataType="vms:MainWindowViewModel" x:DataType="vms:MainWindowViewModel"
@@ -22,7 +22,7 @@
CloseOnClickAway="True" CloseOnClickAway="True"
DisableOpeningAnimation="True"> DisableOpeningAnimation="True">
<DockPanel> <DockPanel>
<DockPanel Classes="Margin8" DockPanel.Dock="Top"> <DockPanel Margin="{StaticResource Margin8}" DockPanel.Dock="Top">
<ContentControl x:Name="conTheme" DockPanel.Dock="Right" /> <ContentControl x:Name="conTheme" DockPanel.Dock="Right" />
<Menu Margin="0,1"> <Menu Margin="0,1">
<MenuItem Padding="8,0"> <MenuItem Padding="8,0">
@@ -166,4 +166,4 @@
</Grid> </Grid>
</DockPanel> </DockPanel>
</dialogHost:DialogHost> </dialogHost:DialogHost>
</Window> </Window>

View File

@@ -12,6 +12,7 @@ using MsBox.Avalonia.Enums;
using ReactiveUI; using ReactiveUI;
using Splat; using Splat;
using v2rayN.Desktop.Common; using v2rayN.Desktop.Common;
using v2rayN.Desktop.Handler;
namespace v2rayN.Desktop.Views namespace v2rayN.Desktop.Views
{ {
@@ -138,8 +139,7 @@ namespace v2rayN.Desktop.Views
if (Utils.IsWindows()) if (Utils.IsWindows())
{ {
ThreadPool.RegisterWaitForSingleObject(Program.ProgramStarted, OnProgramStarted, null, -1, false); ThreadPool.RegisterWaitForSingleObject(Program.ProgramStarted, OnProgramStarted, null, -1, false);
HotkeyHandler.Instance.Init(_config, OnHotkeyHandler);
menuGlobalHotkeySetting.IsVisible = false;
} }
else else
{ {
@@ -156,7 +156,6 @@ namespace v2rayN.Desktop.Views
RestoreUI(); RestoreUI();
AddHelpMenuItem(); AddHelpMenuItem();
//WindowsHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, null);
MessageBus.Current.Listen<string>(EMsgCommand.AppExit.ToString()).Subscribe(StorageUI); MessageBus.Current.Listen<string>(EMsgCommand.AppExit.ToString()).Subscribe(StorageUI);
} }
@@ -233,6 +232,7 @@ namespace v2rayN.Desktop.Views
StorageUI(); StorageUI();
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
HotkeyHandler.Instance.Dispose();
desktop.Shutdown(); desktop.Shutdown();
} }
break; break;
@@ -247,7 +247,10 @@ namespace v2rayN.Desktop.Views
case EViewAction.AddServerViaClipboard: case EViewAction.AddServerViaClipboard:
var clipboardData = await AvaUtils.GetClipboardData(this); var clipboardData = await AvaUtils.GetClipboardData(this);
ViewModel?.AddServerViaClipboardAsync(clipboardData); if (ViewModel != null)
{
await ViewModel.AddServerViaClipboardAsync(clipboardData);
}
break; break;
case EViewAction.AdjustMainLvColWidth: case EViewAction.AdjustMainLvColWidth:
@@ -268,21 +271,12 @@ namespace v2rayN.Desktop.Views
ShowHideWindow(null); ShowHideWindow(null);
break; break;
//case EGlobalHotkey.SystemProxyClear: case EGlobalHotkey.SystemProxyClear:
// ViewModel?.SetListenerType(ESysProxyType.ForcedClear); case EGlobalHotkey.SystemProxySet:
// break; case EGlobalHotkey.SystemProxyUnchanged:
case EGlobalHotkey.SystemProxyPac:
//case EGlobalHotkey.SystemProxySet: Locator.Current.GetService<StatusBarViewModel>()?.SetListenerType((ESysProxyType)((int)e - 1));
// ViewModel?.SetListenerType(ESysProxyType.ForcedChange); break;
// break;
//case EGlobalHotkey.SystemProxyUnchanged:
// ViewModel?.SetListenerType(ESysProxyType.Unchanged);
// break;
//case EGlobalHotkey.SystemProxyPac:
// ViewModel?.SetListenerType(ESysProxyType.Pac);
// break;
} }
} }
@@ -303,7 +297,10 @@ namespace v2rayN.Desktop.Views
break; break;
case WindowCloseReason.ApplicationShutdown or WindowCloseReason.OSShutdown: case WindowCloseReason.ApplicationShutdown or WindowCloseReason.OSShutdown:
await ViewModel?.MyAppExitAsync(true); if (ViewModel != null)
{
await ViewModel.MyAppExitAsync(true);
}
break; break;
} }
@@ -318,7 +315,10 @@ namespace v2rayN.Desktop.Views
{ {
case Key.V: case Key.V:
var clipboardData = await AvaUtils.GetClipboardData(this); var clipboardData = await AvaUtils.GetClipboardData(this);
ViewModel?.AddServerViaClipboardAsync(clipboardData); if (ViewModel != null)
{
await ViewModel.AddServerViaClipboardAsync(clipboardData);
}
break; break;
case Key.S: case Key.S:
@@ -367,7 +367,11 @@ namespace v2rayN.Desktop.Views
{ {
return; return;
} }
await ViewModel?.ScanImageResult(fileName);
if (ViewModel != null)
{
await ViewModel.ScanImageResult(fileName);
}
} }
private void MenuCheckUpdate_Click(object? sender, RoutedEventArgs e) private void MenuCheckUpdate_Click(object? sender, RoutedEventArgs e)
@@ -391,8 +395,10 @@ namespace v2rayN.Desktop.Views
_blCloseByUser = true; _blCloseByUser = true;
StorageUI(); StorageUI();
if (ViewModel != null)
await ViewModel?.MyAppExitAsync(false); {
await ViewModel.MyAppExitAsync(false);
}
} }
#endregion Event #endregion Event
@@ -414,18 +420,17 @@ namespace v2rayN.Desktop.Views
} }
else else
{ {
if (Utils.IsOSX() || _config.UiItem.Hide2TrayWhenClose) if (Utils.IsLinux() && _config.UiItem.Hide2TrayWhenClose == false)
{
foreach (var ownedWindow in this.OwnedWindows)
{
ownedWindow.Close();
}
this.Hide();
}
else
{ {
this.WindowState = WindowState.Minimized; this.WindowState = WindowState.Minimized;
return;
} }
foreach (var ownedWindow in this.OwnedWindows)
{
ownedWindow.Close();
}
this.Hide();
} }
_config.UiItem.ShowInTaskbar = bl; _config.UiItem.ShowInTaskbar = bl;

View File

@@ -10,80 +10,72 @@
mc:Ignorable="d"> mc:Ignorable="d">
<DockPanel Margin="2"> <DockPanel Margin="2">
<WrapPanel <WrapPanel
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
DockPanel.Dock="Top" DockPanel.Dock="Top"
Orientation="Horizontal"> Orientation="Horizontal">
<TextBox <TextBox
x:Name="cmbMsgFilter" x:Name="cmbMsgFilter"
Width="200" Width="200"
Margin="8,0" Margin="{StaticResource MarginLr8}"
VerticalContentAlignment="Center" VerticalContentAlignment="Center"
Watermark="{x:Static resx:ResUI.MsgFilterTitle}" /> Watermark="{x:Static resx:ResUI.MsgFilterTitle}" />
<Button <Button
x:Name="btnCopy" x:Name="btnCopy"
Width="30" Width="{StaticResource IconButtonWidth}"
Height="30" Height="{StaticResource IconButtonHeight}"
Margin="8,0" Margin="{StaticResource MarginLr8}"
Classes="Success" Classes="Success"
Click="menuMsgViewCopyAll_Click" Click="menuMsgViewCopyAll_Click"
Theme="{DynamicResource BorderlessButton}" Theme="{DynamicResource BorderlessButton}"
ToolTip.Tip="{x:Static resx:ResUI.menuMsgViewCopyAll}"> ToolTip.Tip="{x:Static resx:ResUI.menuMsgViewCopyAll}">
<Button.Content> <Button.Content>
<PathIcon <PathIcon Data="{StaticResource building_copy}" Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
Width="20"
Height="20"
Data="{StaticResource building_copy}"
Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
</Button.Content> </Button.Content>
</Button> </Button>
<Button <Button
x:Name="btnClear" x:Name="btnClear"
Width="30" Width="{StaticResource IconButtonWidth}"
Height="30" Height="{StaticResource IconButtonHeight}"
Margin="8,0" Margin="{StaticResource MarginLr8}"
Classes="Success" Classes="Success"
Click="menuMsgViewClear_Click" Click="menuMsgViewClear_Click"
Theme="{DynamicResource BorderlessButton}" Theme="{DynamicResource BorderlessButton}"
ToolTip.Tip="{x:Static resx:ResUI.menuMsgViewClear}"> ToolTip.Tip="{x:Static resx:ResUI.menuMsgViewClear}">
<Button.Content> <Button.Content>
<PathIcon <PathIcon Data="{StaticResource building_delete}" Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
Width="20"
Height="20"
Data="{StaticResource building_delete}"
Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
</Button.Content> </Button.Content>
</Button> </Button>
<TextBlock <TextBlock
Margin="8,0" Margin="{StaticResource MarginLr8}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbAutoRefresh}" /> Text="{x:Static resx:ResUI.TbAutoRefresh}" />
<ToggleSwitch <ToggleSwitch
x:Name="togAutoRefresh" x:Name="togAutoRefresh"
Margin="8,0" Margin="{StaticResource MarginLr8}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
IsChecked="True" IsChecked="True"
Theme="{DynamicResource SimpleToggleSwitch}" /> Theme="{DynamicResource SimpleToggleSwitch}" />
<TextBlock <TextBlock
Margin="8,0" Margin="{StaticResource MarginLr8}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbAutoScrollToEnd}" /> Text="{x:Static resx:ResUI.TbAutoScrollToEnd}" />
<ToggleSwitch <ToggleSwitch
x:Name="togScrollToEnd" x:Name="togScrollToEnd"
Margin="8,0" Margin="{StaticResource MarginLr8}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
IsChecked="True" IsChecked="True"
Theme="{DynamicResource SimpleToggleSwitch}" /> Theme="{DynamicResource SimpleToggleSwitch}" />
</WrapPanel> </WrapPanel>
<TextBox <TextBox
Name="txtMsg" Name="txtMsg"
VerticalAlignment="Stretch"
BorderThickness="0" BorderThickness="0"
Classes="TextArea" Classes="TextArea"
IsReadOnly="True" IsReadOnly="True"
TextAlignment="Left" TextAlignment="Left"
VerticalAlignment="Stretch"
TextWrapping="Wrap"> TextWrapping="Wrap">
<TextBox.ContextMenu> <TextBox.ContextMenu>
<ContextMenu> <ContextMenu>
@@ -107,4 +99,4 @@
</TextBox.ContextMenu> </TextBox.ContextMenu>
</TextBox> </TextBox>
</DockPanel> </DockPanel>
</UserControl> </UserControl>

File diff suppressed because it is too large Load Diff

View File

@@ -66,22 +66,19 @@ namespace v2rayN.Desktop.Views
cmbCoreType6.Items.Add(it); cmbCoreType6.Items.Add(it);
}); });
for (int i = 2; i <= 6; i++) for (var i = 2; i <= 8; i++)
{
cmbMixedConcurrencyCount.Items.Add(i);
}
for (var i = 2; i <= 6; i++)
{ {
cmbSpeedTestTimeout.Items.Add(i * 5); cmbSpeedTestTimeout.Items.Add(i * 5);
} }
Global.SpeedTestUrls.ForEach(it =>
{ cmbSpeedTestUrl.ItemsSource = Global.SpeedTestUrls;
cmbSpeedTestUrl.Items.Add(it); cmbSpeedPingTestUrl.ItemsSource = Global.SpeedPingTestUrls;
}); cmbSubConvertUrl.ItemsSource = Global.SubConvertUrls;
Global.SpeedPingTestUrls.ForEach(it =>
{
cmbSpeedPingTestUrl.Items.Add(it);
});
Global.SubConvertUrls.ForEach(it =>
{
cmbSubConvertUrl.Items.Add(it);
});
Global.GeoFilesSources.ForEach(it => Global.GeoFilesSources.ForEach(it =>
{ {
cmbGetFilesSourceUrl.Items.Add(it); cmbGetFilesSourceUrl.Items.Add(it);
@@ -135,12 +132,12 @@ namespace v2rayN.Desktop.Views
this.Bind(ViewModel, vm => vm.Hide2TrayWhenClose, v => v.togHide2TrayWhenClose.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Hide2TrayWhenClose, v => v.togHide2TrayWhenClose.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DoubleClick2Activate, v => v.togDoubleClick2Activate.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DoubleClick2Activate, v => v.togDoubleClick2Activate.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoUpdateInterval, v => v.txtautoUpdateInterval.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.AutoUpdateInterval, v => v.txtautoUpdateInterval.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CurrentFontFamily, v => v.cmbcurrentFontFamily.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CurrentFontFamily, v => v.cmbcurrentFontFamily.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SpeedTestTimeout, v => v.cmbSpeedTestTimeout.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SpeedTestTimeout, v => v.cmbSpeedTestTimeout.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SpeedTestUrl, v => v.cmbSpeedTestUrl.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SpeedTestUrl, v => v.cmbSpeedTestUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SpeedPingTestUrl, v => v.cmbSpeedPingTestUrl.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SpeedPingTestUrl, v => v.cmbSpeedPingTestUrl.Text).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.SpeedTestPageSize, v => v.txtSpeedTestPageSize.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.MixedConcurrencyCount, v => v.cmbMixedConcurrencyCount.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SubConvertUrl, v => v.cmbSubConvertUrl.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SubConvertUrl, v => v.cmbSubConvertUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.MainGirdOrientation, v => v.cmbMainGirdOrientation.SelectedIndex).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.MainGirdOrientation, v => v.cmbMainGirdOrientation.SelectedIndex).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SrsFileSourceUrl, v => v.cmbSrsFilesSourceUrl.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SrsFileSourceUrl, v => v.cmbSrsFilesSourceUrl.SelectedValue).DisposeWith(disposables);
@@ -170,12 +167,27 @@ namespace v2rayN.Desktop.Views
if (Utils.IsWindows()) if (Utils.IsWindows())
{ {
txbSettingsExceptionTip2.IsVisible = false; txbSettingsExceptionTip2.IsVisible = false;
txtLinuxSudoPassword.IsVisible = false;
labLinuxSudoPassword.IsVisible = false;
labLinuxSudoPasswordTip.IsVisible = false;
labHide2TrayWhenClose.IsVisible = false;
togHide2TrayWhenClose.IsVisible = false;
} }
else else if (Utils.IsLinux())
{ {
txbSettingsExceptionTip.IsVisible = false; txbSettingsExceptionTip.IsVisible = false;
panSystemProxyAdvanced.IsVisible = false; panSystemProxyAdvanced.IsVisible = false;
} }
else if (Utils.IsOSX())
{
txbSettingsExceptionTip.IsVisible = false;
panSystemProxyAdvanced.IsVisible = false;
labHide2TrayWhenClose.IsVisible = false;
togHide2TrayWhenClose.IsVisible = false;
}
} }
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj) private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
@@ -196,8 +208,9 @@ namespace v2rayN.Desktop.Views
private async Task InitSettingFont() private async Task InitSettingFont()
{ {
var lstFonts = await GetFonts(); var lstFonts = await GetFonts();
lstFonts.ForEach(it => { cmbcurrentFontFamily.Items.Add(it); });
cmbcurrentFontFamily.Items.Add(string.Empty); lstFonts.Add(string.Empty);
cmbcurrentFontFamily.ItemsSource = lstFonts;
} }
private async Task<List<string>> GetFonts() private async Task<List<string>> GetFonts()
@@ -235,7 +248,10 @@ namespace v2rayN.Desktop.Views
private void ClbdestOverride_SelectionChanged(object? sender, SelectionChangedEventArgs e) private void ClbdestOverride_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{ {
ViewModel.destOverride = clbdestOverride.SelectedItems.Cast<string>().ToList(); if (ViewModel != null)
{
ViewModel.destOverride = clbdestOverride.SelectedItems.Cast<string>().ToList();
}
} }
} }
} }

View File

@@ -19,65 +19,59 @@
<WrapPanel Margin="2" DockPanel.Dock="Top"> <WrapPanel Margin="2" DockPanel.Dock="Top">
<ListBox <ListBox
x:Name="lstGroup" x:Name="lstGroup"
MaxHeight="200" Margin="{StaticResource MarginLr4}"
DisplayMemberBinding="{Binding Remarks}" DisplayMemberBinding="{Binding Remarks}"
ItemsSource="{Binding SubItems}" ItemsSource="{Binding SubItems}"
Theme="{DynamicResource PureCardRadioGroupListBox}" /> Theme="{DynamicResource PureCardRadioGroupListBox}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
<Button <Button
x:Name="btnEditSub" x:Name="btnEditSub"
Width="30" Width="{StaticResource IconButtonWidth}"
Height="30" Height="{StaticResource IconButtonHeight}"
Margin="4,0" Margin="{StaticResource MarginLr4}"
Classes="Success" Classes="Success"
Theme="{DynamicResource BorderlessButton}" Theme="{DynamicResource BorderlessButton}"
ToolTip.Tip="{x:Static resx:ResUI.menuSubEdit}"> ToolTip.Tip="{x:Static resx:ResUI.menuSubEdit}">
<Button.Content> <Button.Content>
<PathIcon <PathIcon Data="{StaticResource building_edit}" Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
Width="24"
Height="24"
Data="{StaticResource building_edit}"
Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
</Button.Content> </Button.Content>
</Button> </Button>
<Button <Button
x:Name="btnAddSub" x:Name="btnAddSub"
Width="30" Width="{StaticResource IconButtonWidth}"
Height="30" Height="{StaticResource IconButtonHeight}"
Margin="4,0" Margin="{StaticResource MarginLr4}"
Classes="Success" Classes="Success"
Theme="{DynamicResource BorderlessButton}" Theme="{DynamicResource BorderlessButton}"
ToolTip.Tip="{x:Static resx:ResUI.menuSubAdd}"> ToolTip.Tip="{x:Static resx:ResUI.menuSubAdd}">
<Button.Content> <Button.Content>
<PathIcon <PathIcon Data="{StaticResource building_add}" Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
Width="24"
Height="24"
Data="{StaticResource building_add}"
Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
</Button.Content> </Button.Content>
</Button> </Button>
<Button <Button
x:Name="btnAutofitColumnWidth" x:Name="btnAutofitColumnWidth"
Width="30" Width="{StaticResource IconButtonWidth}"
Height="30" Height="{StaticResource IconButtonHeight}"
Margin="4,0" Margin="{StaticResource MarginLr4}"
Classes="Success" Classes="Success"
Theme="{DynamicResource BorderlessButton}" Theme="{DynamicResource BorderlessButton}"
ToolTip.Tip="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}"> ToolTip.Tip="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}">
<Button.Content> <Button.Content>
<PathIcon <PathIcon Data="{StaticResource building_fit}" Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
Width="24"
Height="24"
Data="{StaticResource building_fit}"
Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
</Button.Content> </Button.Content>
</Button> </Button>
<TextBox <TextBox
x:Name="txtServerFilter" x:Name="txtServerFilter"
Width="200" Width="200"
Margin="4,0" Margin="{StaticResource MarginLr4}"
VerticalContentAlignment="Center" VerticalContentAlignment="Center"
Watermark="{x:Static resx:ResUI.MsgServerTitle}" /> Watermark="{x:Static resx:ResUI.MsgServerTitle}" />
</WrapPanel> </WrapPanel>
@@ -85,11 +79,13 @@
x:Name="lstProfiles" x:Name="lstProfiles"
AutoGenerateColumns="False" AutoGenerateColumns="False"
BorderThickness="1" BorderThickness="1"
CanUserReorderColumns="True"
CanUserResizeColumns="True" CanUserResizeColumns="True"
GridLinesVisibility="All" GridLinesVisibility="All"
HeadersVisibility="All" HeadersVisibility="All"
IsReadOnly="True" IsReadOnly="True"
ItemsSource="{Binding ProfileItems}"> ItemsSource="{Binding ProfileItems}"
ScrollViewer.AllowAutoHide="False">
<DataGrid.KeyBindings> <DataGrid.KeyBindings>
<KeyBinding Command="{Binding Export2ShareUrlCmd}" Gesture="Ctrl+C" /> <KeyBinding Command="{Binding Export2ShareUrlCmd}" Gesture="Ctrl+C" />
<KeyBinding Command="{Binding SetDefaultServerCmd}" Gesture="Enter" /> <KeyBinding Command="{Binding SetDefaultServerCmd}" Gesture="Enter" />
@@ -104,14 +100,16 @@
<MenuItem x:Name="menuShareServer" Header="{x:Static resx:ResUI.menuShareServer}" /> <MenuItem x:Name="menuShareServer" Header="{x:Static resx:ResUI.menuShareServer}" />
<Separator /> <Separator />
<MenuItem x:Name="menuSetDefaultMultipleServer" Header="{x:Static resx:ResUI.menuSetDefaultMultipleServer}" /> <MenuItem x:Name="menuSetDefaultMultipleServer" Header="{x:Static resx:ResUI.menuSetDefaultMultipleServer}" />
<MenuItem x:Name="menuSetDefaultLoadBalanceServer" Header="{x:Static resx:ResUI.menuSetDefaultLoadBalanceServer}" /> <MenuItem x:Name="menuSetDefaultLoadBalanceServer" Header="{x:Static resx:ResUI.menuSetDefaultLoadBalanceServer}" />
<Separator /> <Separator />
<MenuItem x:Name="menuMixedTestServer" Header="{x:Static resx:ResUI.menuMixedTestServer}" /> <MenuItem x:Name="menuMixedTestServer" Header="{x:Static resx:ResUI.menuMixedTestServer}" />
<MenuItem x:Name="menuTcpingServer" Header="{x:Static resx:ResUI.menuTcpingServer}" /> <MenuItem x:Name="menuTcpingServer" Header="{x:Static resx:ResUI.menuTcpingServer}" />
<MenuItem x:Name="menuRealPingServer" Header="{x:Static resx:ResUI.menuRealPingServer}" /> <MenuItem x:Name="menuRealPingServer" Header="{x:Static resx:ResUI.menuRealPingServer}" />
<MenuItem x:Name="menuSpeedServer" Header="{x:Static resx:ResUI.menuSpeedServer}" /> <MenuItem x:Name="menuSpeedServer" Header="{x:Static resx:ResUI.menuSpeedServer}" />
<MenuItem x:Name="menuSortServerResult" Header="{x:Static resx:ResUI.menuSortServerResult}" /> <MenuItem Header="{x:Static resx:ResUI.menuTestServerResult}">
<MenuItem x:Name="menuSortServerResult" Header="{x:Static resx:ResUI.menuSortServerResult}" />
<MenuItem x:Name="menuRemoveInvalidServerResult" Header="{x:Static resx:ResUI.menuRemoveInvalidServerResult}" />
</MenuItem>
<Separator /> <Separator />
<MenuItem x:Name="menuMoveToGroup" Header="{x:Static resx:ResUI.menuMoveToGroup}"> <MenuItem x:Name="menuMoveToGroup" Header="{x:Static resx:ResUI.menuMoveToGroup}">
<MenuItem> <MenuItem>
@@ -159,9 +157,9 @@
</DataGridTemplateColumn.Header> </DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<StackPanel Margin="8,0" Orientation="Horizontal"> <StackPanel Margin="{StaticResource MarginLr8}" Orientation="Horizontal">
<Label <Label
Margin="0,0,8,0" Margin="{StaticResource MarginLr4}"
Classes="Solid Red" Classes="Solid Red"
Content="{x:Static resx:ResUI.TipActiveServer}" Content="{x:Static resx:ResUI.TipActiveServer}"
IsVisible="{Binding IsActive}" IsVisible="{Binding IsActive}"
@@ -207,7 +205,7 @@
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<TextBlock <TextBlock
Margin="8,0" Margin="{StaticResource MarginLr8}"
HorizontalAlignment="Right" HorizontalAlignment="Right"
VerticalAlignment="Center" VerticalAlignment="Center"
Foreground="{Binding Delay, Converter={StaticResource DelayColorConverter}}" Foreground="{Binding Delay, Converter={StaticResource DelayColorConverter}}"
@@ -245,4 +243,4 @@
</DataGrid> </DataGrid>
</DockPanel> </DockPanel>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@@ -85,6 +85,7 @@ namespace v2rayN.Desktop.Views
this.BindCommand(ViewModel, vm => vm.RealPingServerCmd, v => v.menuRealPingServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.RealPingServerCmd, v => v.menuRealPingServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SpeedServerCmd, v => v.menuSpeedServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SpeedServerCmd, v => v.menuSpeedServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SortServerResultCmd, v => v.menuSortServerResult).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SortServerResultCmd, v => v.menuSortServerResult).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RemoveInvalidServerResultCmd, v => v.menuRemoveInvalidServerResult).DisposeWith(disposables);
//servers export //servers export
this.BindCommand(ViewModel, vm => vm.Export2ClientConfigCmd, v => v.menuExport2ClientConfig).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.Export2ClientConfigCmd, v => v.menuExport2ClientConfig).DisposeWith(disposables);
@@ -101,11 +102,16 @@ namespace v2rayN.Desktop.Views
private async void LstProfiles_Sorting(object? sender, DataGridColumnEventArgs e) private async void LstProfiles_Sorting(object? sender, DataGridColumnEventArgs e)
{ {
e.Handled = true; e.Handled = true;
await ViewModel?.SortServer(e.Column.Tag.ToString());
if (ViewModel != null && e.Column?.Tag?.ToString() != null)
{
await ViewModel.SortServer(e.Column.Tag.ToString());
}
e.Handled = false; e.Handled = false;
} }
//#region Event #region Event
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj) private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{ {
@@ -178,7 +184,9 @@ namespace v2rayN.Desktop.Views
case EViewAction.DispatcherRefreshServersBiz: case EViewAction.DispatcherRefreshServersBiz:
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
ViewModel?.RefreshServersBiz(), {
_ = RefreshServersBiz();
},
DispatcherPriority.Default); DispatcherPriority.Default);
break; break;
} }
@@ -197,9 +205,25 @@ namespace v2rayN.Desktop.Views
await DialogHost.Show(dialog); await DialogHost.Show(dialog);
} }
public async Task RefreshServersBiz()
{
if (ViewModel != null)
{
await ViewModel.RefreshServersBiz();
}
if (lstProfiles.SelectedIndex > 0)
{
lstProfiles.ScrollIntoView(lstProfiles.SelectedItem, null);
}
}
private void lstProfiles_SelectionChanged(object? sender, SelectionChangedEventArgs e) private void lstProfiles_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{ {
ViewModel.SelectedProfiles = lstProfiles.SelectedItems.Cast<ProfileItemModel>().ToList(); if (ViewModel != null)
{
ViewModel.SelectedProfiles = lstProfiles.SelectedItems.Cast<ProfileItemModel>().ToList();
}
} }
private void LstProfiles_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e) private void LstProfiles_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e)
@@ -332,9 +356,9 @@ namespace v2rayN.Desktop.Views
} }
} }
//#endregion Event #endregion Event
//#region UI #region UI
private void RestoreUI() private void RestoreUI()
{ {
@@ -371,9 +395,8 @@ namespace v2rayN.Desktop.Views
private void StorageUI(string? n = null) private void StorageUI(string? n = null)
{ {
List<ColumnItem> lvColumnItem = new(); List<ColumnItem> lvColumnItem = new();
for (int k = 0; k < lstProfiles.Columns.Count; k++) foreach (var item2 in lstProfiles.Columns)
{ {
var item2 = lstProfiles.Columns[k];
if (item2.Tag == null) if (item2.Tag == null)
{ {
continue; continue;
@@ -388,9 +411,9 @@ namespace v2rayN.Desktop.Views
_config.UiItem.MainColumnItem = lvColumnItem; _config.UiItem.MainColumnItem = lvColumnItem;
} }
//#endregion UI #endregion UI
//#region Drag and Drop #region Drag and Drop
//private Point startPoint = new(); //private Point startPoint = new();
//private int startIndex = -1; //private int startIndex = -1;
@@ -480,6 +503,6 @@ namespace v2rayN.Desktop.Views
// } // }
//} //}
//#endregion Drag and Drop #endregion Drag and Drop
} }
} }

View File

@@ -1,4 +1,4 @@
<UserControl <UserControl
x:Class="v2rayN.Desktop.Views.QrcodeView" x:Class="v2rayN.Desktop.Views.QrcodeView"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -7,7 +7,7 @@
d:DesignHeight="480" d:DesignHeight="480"
d:DesignWidth="400" d:DesignWidth="400"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid Margin="30" RowDefinitions="Auto,Auto"> <Grid Margin="32" RowDefinitions="Auto,Auto">
<Image <Image
Name="imgQrcode" Name="imgQrcode"
Width="300" Width="300"
@@ -17,10 +17,10 @@
x:Name="txtContent" x:Name="txtContent"
Grid.Row="1" Grid.Row="1"
Width="300" Width="300"
Margin="0,8" Margin="{StaticResource MarginTb8}"
VerticalAlignment="Center" VerticalAlignment="Center"
IsReadOnly="True" IsReadOnly="True"
MaxLines="1" /> MaxLines="1" />
</Grid> </Grid>
</UserControl> </UserControl>

View File

@@ -1,4 +1,4 @@
<Window <Window
x:Class="v2rayN.Desktop.Views.RoutingRuleDetailsWindow" x:Class="v2rayN.Desktop.Views.RoutingRuleDetailsWindow"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -15,7 +15,7 @@
mc:Ignorable="d"> mc:Ignorable="d">
<DockPanel> <DockPanel>
<Grid <Grid
Classes="Margin8" Margin="{StaticResource Margin4}"
ColumnDefinitions="Auto,Auto,Auto" ColumnDefinitions="Auto,Auto,Auto"
DockPanel.Dock="Top" DockPanel.Dock="Top"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto"> RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
@@ -23,7 +23,7 @@
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.LvRemarks}" /> Text="{x:Static resx:ResUI.LvRemarks}" />
<TextBox <TextBox
x:Name="txtRemarks" x:Name="txtRemarks"
@@ -31,39 +31,39 @@
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<ToggleSwitch <ToggleSwitch
x:Name="togEnabled" x:Name="togEnabled"
Grid.Row="0" Grid.Row="0"
Grid.Column="2" Grid.Column="2"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="outboundTag" /> Text="outboundTag" />
<ComboBox <ComboBox
x:Name="cmbOutboundTag" x:Name="cmbOutboundTag"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Classes="Margin8" Margin="{StaticResource Margin4}"
MaxDropDownHeight="1000" /> MaxDropDownHeight="1000" />
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="2" Grid.Column="2"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.TbRuleMatchingTips}" /> Text="{x:Static resx:ResUI.TbRuleMatchingTips}" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="port" /> Text="port" />
<TextBox <TextBox
x:Name="txtPort" x:Name="txtPort"
@@ -71,12 +71,12 @@
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="2" Grid.Column="2"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"> Margin="{StaticResource Margin4}">
<HyperlinkButton Classes="WithIcon" Click="linkRuleobjectDoc_Click"> <HyperlinkButton Classes="WithIcon" Click="linkRuleobjectDoc_Click">
<TextBlock Text="{x:Static resx:ResUI.TbRuleobjectDoc}" /> <TextBlock Text="{x:Static resx:ResUI.TbRuleobjectDoc}" />
</HyperlinkButton> </HyperlinkButton>
@@ -86,14 +86,14 @@
Grid.Row="3" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="protocol" /> Text="protocol" />
<ListBox <ListBox
x:Name="clbProtocol" x:Name="clbProtocol"
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Classes="Margin8" Margin="{StaticResource Margin4}"
SelectionMode="Multiple,Toggle" SelectionMode="Multiple,Toggle"
Theme="{DynamicResource CardCheckGroupListBox}" /> Theme="{DynamicResource CardCheckGroupListBox}" />
@@ -101,20 +101,20 @@
Grid.Row="4" Grid.Row="4"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="inboundTag" /> Text="inboundTag" />
<ListBox <ListBox
x:Name="clbInboundTag" x:Name="clbInboundTag"
Grid.Row="4" Grid.Row="4"
Grid.Column="1" Grid.Column="1"
Classes="Margin8" Margin="{StaticResource Margin4}"
SelectionMode="Multiple,Toggle" SelectionMode="Multiple,Toggle"
Theme="{DynamicResource CardCheckGroupListBox}" /> Theme="{DynamicResource CardCheckGroupListBox}" />
<TextBlock <TextBlock
Grid.Row="4" Grid.Row="4"
Grid.Column="2" Grid.Column="2"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.TbRoutingInboundTagTips}" /> Text="{x:Static resx:ResUI.TbRoutingInboundTagTips}" />
@@ -122,26 +122,26 @@
Grid.Row="5" Grid.Row="5"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="network" /> Text="network" />
<ComboBox <ComboBox
x:Name="cmbNetwork" x:Name="cmbNetwork"
Grid.Row="5" Grid.Row="5"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Classes="Margin8" Margin="{StaticResource Margin4}"
MaxDropDownHeight="1000" /> MaxDropDownHeight="1000" />
<TextBlock <TextBlock
Grid.Row="5" Grid.Row="5"
Grid.Column="2" Grid.Column="2"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.TbRoutingTips}" /> Text="{x:Static resx:ResUI.TbRoutingTips}" />
</Grid> </Grid>
<StackPanel <StackPanel
HorizontalAlignment="Right" HorizontalAlignment="Right"
Classes="Margin8" Margin="{StaticResource Margin4}"
DockPanel.Dock="Bottom" DockPanel.Dock="Bottom"
Orientation="Horizontal"> Orientation="Horizontal">
<StackPanel <StackPanel
@@ -161,13 +161,13 @@
<Button <Button
x:Name="btnCancel" x:Name="btnCancel"
Width="100" Width="100"
Margin="8,0" Margin="{StaticResource MarginLr8}"
Content="{x:Static resx:ResUI.TbCancel}" Content="{x:Static resx:ResUI.TbCancel}"
Cursor="Hand" Cursor="Hand"
IsCancel="True" /> IsCancel="True" />
</StackPanel> </StackPanel>
<Grid Classes="Margin8" ColumnDefinitions="1*,10,1*,10,1*"> <Grid Margin="{StaticResource Margin4}" ColumnDefinitions="1*,10,1*,10,1*">
<HeaderedContentControl <HeaderedContentControl
Grid.Column="0" Grid.Column="0"
BorderBrush="Gray" BorderBrush="Gray"
@@ -208,4 +208,4 @@
</HeaderedContentControl> </HeaderedContentControl>
</Grid> </Grid>
</DockPanel> </DockPanel>
</Window> </Window>

View File

@@ -85,12 +85,18 @@ namespace v2rayN.Desktop.Views
private void ClbProtocol_SelectionChanged(object? sender, SelectionChangedEventArgs e) private void ClbProtocol_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{ {
ViewModel.ProtocolItems = clbProtocol.SelectedItems.Cast<string>().ToList(); if (ViewModel != null)
{
ViewModel.ProtocolItems = clbProtocol.SelectedItems.Cast<string>().ToList();
}
} }
private void ClbInboundTag_SelectionChanged(object? sender, SelectionChangedEventArgs e) private void ClbInboundTag_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{ {
ViewModel.InboundTagItems = clbInboundTag.SelectedItems.Cast<string>().ToList(); if (ViewModel != null)
{
ViewModel.InboundTagItems = clbInboundTag.SelectedItems.Cast<string>().ToList();
}
} }
private void linkRuleobjectDoc_Click(object? sender, RoutedEventArgs e) private void linkRuleobjectDoc_Click(object? sender, RoutedEventArgs e)

View File

@@ -1,4 +1,4 @@
<Window <Window
x:Class="v2rayN.Desktop.Views.RoutingRuleSettingWindow" x:Class="v2rayN.Desktop.Views.RoutingRuleSettingWindow"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -15,7 +15,7 @@
mc:Ignorable="d"> mc:Ignorable="d">
<DockPanel> <DockPanel>
<StackPanel <StackPanel
Classes="Margin8" Margin="{StaticResource Margin4}"
DockPanel.Dock="Top" DockPanel.Dock="Top"
Orientation="Horizontal"> Orientation="Horizontal">
<Menu> <Menu>
@@ -28,7 +28,7 @@
<StackPanel <StackPanel
HorizontalAlignment="Right" HorizontalAlignment="Right"
Classes="Margin8" Margin="{StaticResource Margin4}"
DockPanel.Dock="Bottom" DockPanel.Dock="Bottom"
Orientation="Horizontal"> Orientation="Horizontal">
<Button <Button
@@ -40,14 +40,14 @@
<Button <Button
x:Name="btnCancel" x:Name="btnCancel"
Width="100" Width="100"
Margin="8,0" Margin="{StaticResource MarginLr8}"
Content="{x:Static resx:ResUI.TbCancel}" Content="{x:Static resx:ResUI.TbCancel}"
Cursor="Hand" Cursor="Hand"
IsCancel="True" /> IsCancel="True" />
</StackPanel> </StackPanel>
<Grid <Grid
Classes="Margin8" Margin="{StaticResource Margin4}"
ColumnDefinitions="Auto,Auto,Auto" ColumnDefinitions="Auto,Auto,Auto"
DockPanel.Dock="Top" DockPanel.Dock="Top"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto"> RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
@@ -55,7 +55,7 @@
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.LvRemarks}" /> Text="{x:Static resx:ResUI.LvRemarks}" />
<StackPanel <StackPanel
Grid.Row="0" Grid.Row="0"
@@ -69,25 +69,25 @@
Width="300" Width="300"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock <TextBlock
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.LvSort}" /> Text="{x:Static resx:ResUI.LvSort}" />
<TextBox <TextBox
x:Name="txtSort" x:Name="txtSort"
Width="100" Width="100"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
</StackPanel> </StackPanel>
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.TbdomainStrategy}" /> Text="{x:Static resx:ResUI.TbdomainStrategy}" />
<StackPanel <StackPanel
Grid.Row="1" Grid.Row="1"
@@ -96,22 +96,22 @@
<ComboBox <ComboBox
x:Name="cmbdomainStrategy" x:Name="cmbdomainStrategy"
Width="200" Width="200"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
<TextBlock <TextBlock
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.TbdomainStrategy4Singbox}" /> Text="{x:Static resx:ResUI.TbdomainStrategy4Singbox}" />
<ComboBox <ComboBox
x:Name="cmbdomainStrategy4Singbox" x:Name="cmbdomainStrategy4Singbox"
Width="200" Width="200"
Classes="Margin8" /> Margin="{StaticResource Margin4}" />
</StackPanel> </StackPanel>
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.LvUrl}" /> Text="{x:Static resx:ResUI.LvUrl}" />
<TextBox <TextBox
x:Name="txtUrl" x:Name="txtUrl"
@@ -120,7 +120,7 @@
Width="600" Width="600"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<!-- <!--
@@ -128,7 +128,7 @@
Grid.Row="3" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.LvCustomIcon}" /> Text="{x:Static resx:ResUI.LvCustomIcon}" />
<TextBox <TextBox
x:Name="txtCustomIcon" x:Name="txtCustomIcon"
@@ -137,13 +137,13 @@
Width="600" Width="600"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<Button <Button
x:Name="btnBrowseCustomIcon" x:Name="btnBrowseCustomIcon"
Grid.Row="3" Grid.Row="3"
Grid.Column="2" Grid.Column="2"
Classes="Margin8" Margin="{StaticResource Margin4}"
Content="{x:Static resx:ResUI.TbBrowse}" /> Content="{x:Static resx:ResUI.TbBrowse}" />
--> -->
@@ -151,7 +151,7 @@
Grid.Row="4" Grid.Row="4"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"> Margin="{StaticResource Margin4}">
<HyperlinkButton Classes="WithIcon" Click="linkCustomRulesetPath4Singbox"> <HyperlinkButton Classes="WithIcon" Click="linkCustomRulesetPath4Singbox">
<TextBlock Text="{x:Static resx:ResUI.LvCustomRulesetPath4Singbox}" /> <TextBlock Text="{x:Static resx:ResUI.LvCustomRulesetPath4Singbox}" />
</HyperlinkButton> </HyperlinkButton>
@@ -163,13 +163,13 @@
Width="600" Width="600"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<Button <Button
x:Name="btnBrowseCustomRulesetPath4Singbox" x:Name="btnBrowseCustomRulesetPath4Singbox"
Grid.Row="4" Grid.Row="4"
Grid.Column="2" Grid.Column="2"
Classes="Margin8" Margin="{StaticResource Margin4}"
Content="{x:Static resx:ResUI.TbBrowse}" /> Content="{x:Static resx:ResUI.TbBrowse}" />
</Grid> </Grid>
@@ -242,4 +242,4 @@
</TabItem> </TabItem>
</TabControl> </TabControl>
</DockPanel> </DockPanel>
</Window> </Window>

View File

@@ -167,7 +167,10 @@ namespace v2rayN.Desktop.Views
private void lstRules_SelectionChanged(object? sender, SelectionChangedEventArgs e) private void lstRules_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{ {
ViewModel.SelectedSources = lstRules.SelectedItems.Cast<RulesItemModel>().ToList(); if (ViewModel != null)
{
ViewModel.SelectedSources = lstRules.SelectedItems.Cast<RulesItemModel>().ToList();
}
} }
private void LstRules_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e) private void LstRules_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e)

View File

@@ -1,4 +1,4 @@
<Window <Window
x:Class="v2rayN.Desktop.Views.RoutingSettingWindow" x:Class="v2rayN.Desktop.Views.RoutingSettingWindow"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -16,7 +16,7 @@
<DockPanel> <DockPanel>
<StackPanel <StackPanel
Classes="Margin8" Margin="{StaticResource Margin4}"
DockPanel.Dock="Top" DockPanel.Dock="Top"
Orientation="Horizontal" Orientation="Horizontal"
Spacing="4"> Spacing="4">
@@ -45,7 +45,7 @@
<StackPanel <StackPanel
HorizontalAlignment="Right" HorizontalAlignment="Right"
Classes="Margin8" Margin="{StaticResource Margin4}"
DockPanel.Dock="Bottom" DockPanel.Dock="Bottom"
Orientation="Horizontal"> Orientation="Horizontal">
<StackPanel <StackPanel
@@ -63,7 +63,7 @@
<Button <Button
x:Name="btnCancel" x:Name="btnCancel"
Width="100" Width="100"
Margin="8,0" Margin="{StaticResource MarginLr8}"
Content="{x:Static resx:ResUI.TbCancel}" Content="{x:Static resx:ResUI.TbCancel}"
Cursor="Hand" Cursor="Hand"
IsCancel="True" /> IsCancel="True" />
@@ -123,4 +123,4 @@
</TabControl> </TabControl>
</DockPanel> </DockPanel>
</DockPanel> </DockPanel>
</Window> </Window>

View File

@@ -108,7 +108,10 @@ namespace v2rayN.Desktop.Views
private void lstRoutings_SelectionChanged(object? sender, SelectionChangedEventArgs e) private void lstRoutings_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{ {
ViewModel.SelectedSources = lstRoutings.SelectedItems.Cast<RoutingItemModel>().ToList(); if (ViewModel != null)
{
ViewModel.SelectedSources = lstRoutings.SelectedItems.Cast<RoutingItemModel>().ToList();
}
} }
private void LstRoutings_DoubleTapped(object? sender, TappedEventArgs e) private void LstRoutings_DoubleTapped(object? sender, TappedEventArgs e)

View File

@@ -11,10 +11,10 @@
x:DataType="vms:StatusBarViewModel" x:DataType="vms:StatusBarViewModel"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid> <Grid>
<DockPanel Margin="4"> <DockPanel Margin="{StaticResource Margin8}">
<StackPanel <StackPanel
Margin="8,0"
VerticalAlignment="Center" VerticalAlignment="Center"
Margin="{StaticResource MarginLr8}"
DockPanel.Dock="Right"> DockPanel.Dock="Right">
<TextBlock x:Name="txtSpeedProxyDisplay" HorizontalAlignment="Right" /> <TextBlock x:Name="txtSpeedProxyDisplay" HorizontalAlignment="Right" />
<Border Margin="1" /> <Border Margin="1" />
@@ -22,8 +22,8 @@
</StackPanel> </StackPanel>
<StackPanel <StackPanel
Margin="8,0"
VerticalAlignment="Center" VerticalAlignment="Center"
Margin="{StaticResource MarginLr8}"
DockPanel.Dock="Left"> DockPanel.Dock="Left">
<TextBlock x:Name="txtInboundDisplay" /> <TextBlock x:Name="txtInboundDisplay" />
<Border Margin="1" /> <Border Margin="1" />
@@ -32,50 +32,51 @@
<StackPanel <StackPanel
x:Name="spEnableTun" x:Name="spEnableTun"
Margin="8,0"
VerticalAlignment="Center" VerticalAlignment="Center"
Margin="{StaticResource MarginLr8}"
DockPanel.Dock="Left" DockPanel.Dock="Left"
Orientation="Horizontal"> Orientation="Horizontal">
<TextBlock <TextBlock
Margin="8,0"
VerticalAlignment="Center" VerticalAlignment="Center"
Margin="{StaticResource MarginLr8}"
Text="{x:Static resx:ResUI.TbEnableTunAs}" /> Text="{x:Static resx:ResUI.TbEnableTunAs}" />
<ToggleSwitch <ToggleSwitch
x:Name="togEnableTun" x:Name="togEnableTun"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Classes="Margin8" Margin="{StaticResource Margin4}"
Theme="{DynamicResource SimpleToggleSwitch}" /> Theme="{DynamicResource SimpleToggleSwitch}" />
</StackPanel> </StackPanel>
<StackPanel <StackPanel
Margin="8,0"
VerticalAlignment="Center" VerticalAlignment="Center"
Margin="{StaticResource MarginLr8}"
DockPanel.Dock="Left" DockPanel.Dock="Left"
Orientation="Horizontal"> Orientation="Horizontal">
<ComboBox <ComboBox
x:Name="cmbSystemProxy" x:Name="cmbSystemProxy"
Width="160" Width="160"
Margin="8,0" Margin="{StaticResource MarginLr8}"
ToolTip.Tip="{x:Static resx:ResUI.menuSystemproxy}"> ToolTip.Tip="{x:Static resx:ResUI.menuSystemproxy}">
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyClear}" /> <ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyClear}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxySet}" /> <ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxySet}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyNothing}" /> <ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyNothing}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyPac}" />
</ComboBox> </ComboBox>
<ComboBox <ComboBox
x:Name="cmbRoutings2" x:Name="cmbRoutings2"
Width="160" Width="160"
Margin="8,0" Margin="{StaticResource MarginLr8}"
DisplayMemberBinding="{Binding Remarks}" DisplayMemberBinding="{Binding Remarks}"
ItemsSource="{Binding RoutingItems}" ItemsSource="{Binding RoutingItems}"
ToolTip.Tip="{x:Static resx:ResUI.menuRouting}" /> ToolTip.Tip="{x:Static resx:ResUI.menuRouting}" />
</StackPanel> </StackPanel>
<StackPanel Margin="8,0" VerticalAlignment="Center"> <StackPanel VerticalAlignment="Center" Margin="{StaticResource MarginLr8}">
<TextBlock x:Name="txtRunningServerDisplay" /> <TextBlock x:Name="txtRunningServerDisplay" />
<Border Margin="1" /> <Border Margin="1" />
<TextBlock x:Name="txtRunningInfoDisplay" /> <TextBlock x:Name="txtRunningInfoDisplay" />
</StackPanel> </StackPanel>
</DockPanel> </DockPanel>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@@ -43,6 +43,11 @@ namespace v2rayN.Desktop.Views
}); });
//spEnableTun.IsVisible = (Utils.IsWindows() || AppHandler.Instance.IsAdministrator); //spEnableTun.IsVisible = (Utils.IsWindows() || AppHandler.Instance.IsAdministrator);
if (Utils.IsNonWindows() && cmbSystemProxy.Items.IsReadOnly == false)
{
cmbSystemProxy.Items.RemoveAt(cmbSystemProxy.Items.Count - 1);
}
} }
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj) private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)

View File

@@ -12,10 +12,10 @@
ShowInTaskbar="False" ShowInTaskbar="False"
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
mc:Ignorable="d"> mc:Ignorable="d">
<DockPanel Classes="Margin8"> <DockPanel Margin="{StaticResource Margin8}">
<StackPanel <StackPanel
Margin="{StaticResource Margin4}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Classes="Margin8"
DockPanel.Dock="Bottom" DockPanel.Dock="Bottom"
Orientation="Horizontal"> Orientation="Horizontal">
<Button <Button
@@ -27,7 +27,7 @@
<Button <Button
x:Name="btnCancel" x:Name="btnCancel"
Width="100" Width="100"
Margin="8,0" Margin="{StaticResource MarginLr8}"
Content="{x:Static resx:ResUI.TbCancel}" Content="{x:Static resx:ResUI.TbCancel}"
Cursor="Hand" Cursor="Hand"
IsCancel="True" /> IsCancel="True" />
@@ -39,66 +39,63 @@
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0"
Classes="Margin8" Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.menuSubscription}" /> Text="{x:Static resx:ResUI.menuSubscription}" />
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.LvRemarks}" /> Text="{x:Static resx:ResUI.LvRemarks}" />
<TextBox <TextBox
x:Name="txtRemarks" x:Name="txtRemarks"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.LvUrl}" /> Text="{x:Static resx:ResUI.LvUrl}" />
<TextBox <TextBox
x:Name="txtUrl" x:Name="txtUrl"
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
TextWrapping="Wrap" TextWrapping="Wrap"
Watermark="{x:Static resx:ResUI.SubUrlTips}" /> Watermark="{x:Static resx:ResUI.SubUrlTips}" />
<Button <Button
Grid.Row="2" Grid.Row="2"
Grid.Column="2" Grid.Column="2"
Width="30" Width="{StaticResource IconButtonWidth}"
Height="30" Height="{StaticResource IconButtonHeight}"
Margin="10,0" Margin="{StaticResource MarginLr8}"
Theme="{DynamicResource BorderlessButton}"> Theme="{DynamicResource BorderlessButton}">
<Button.Content> <Button.Content>
<PathIcon <PathIcon Data="{StaticResource building_more}" Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
Width="20"
Height="20"
Data="{StaticResource building_more}"
Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
</Button.Content> </Button.Content>
<Button.Flyout> <Button.Flyout>
<Flyout> <Flyout>
<StackPanel> <StackPanel>
<TextBlock <TextBlock
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.LvMoreUrl}" /> Text="{x:Static resx:ResUI.LvMoreUrl}" />
<TextBox <TextBox
x:Name="txtMoreUrl" x:Name="txtMoreUrl"
Width="400" Width="400"
MinHeight="100" MinHeight="100"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="TextArea Margin8" Classes="TextArea"
MinLines="4" MinLines="4"
TextWrapping="Wrap" TextWrapping="Wrap"
Watermark="{x:Static resx:ResUI.SubUrlTips}" /> Watermark="{x:Static resx:ResUI.SubUrlTips}" />
@@ -110,150 +107,148 @@
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.LvEnabled}" /> Text="{x:Static resx:ResUI.LvEnabled}" />
<DockPanel <DockPanel
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
Classes="Margin8"> Margin="{StaticResource Margin4}">
<ToggleSwitch <ToggleSwitch
x:Name="togEnable" x:Name="togEnable"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Classes="Margin8"
DockPanel.Dock="Left" /> DockPanel.Dock="Left" />
<TextBox <TextBox
x:Name="txtAutoUpdateInterval" x:Name="txtAutoUpdateInterval"
Width="100" Width="100"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
DockPanel.Dock="Right" DockPanel.Dock="Right"
Watermark="{x:Static resx:ResUI.SubUrlTips}" /> Watermark="{x:Static resx:ResUI.SubUrlTips}" />
<TextBlock <TextBlock
Margin="{StaticResource Margin4}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.LvAutoUpdateInterval}" /> Text="{x:Static resx:ResUI.LvAutoUpdateInterval}" />
</DockPanel> </DockPanel>
<TextBlock <TextBlock
Grid.Row="5" Grid.Row="5"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.LvFilter}" /> Text="{x:Static resx:ResUI.LvFilter}" />
<TextBox <TextBox
x:Name="txtFilter" x:Name="txtFilter"
Grid.Row="5" Grid.Row="5"
Grid.Column="1" Grid.Column="1"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Watermark="{x:Static resx:ResUI.SubUrlTips}" /> Watermark="{x:Static resx:ResUI.SubUrlTips}" />
<TextBlock <TextBlock
Grid.Row="6" Grid.Row="6"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.LvConvertTarget}" /> Text="{x:Static resx:ResUI.LvConvertTarget}" />
<ComboBox <ComboBox
x:Name="cmbConvertTarget" x:Name="cmbConvertTarget"
Grid.Row="6" Grid.Row="6"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Classes="Margin8" Margin="{StaticResource Margin4}"
ToolTip.Tip="{x:Static resx:ResUI.LvConvertTargetTip}" /> ToolTip.Tip="{x:Static resx:ResUI.LvConvertTargetTip}" />
<TextBlock <TextBlock
Grid.Row="7" Grid.Row="7"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.LvUserAgent}" /> Text="{x:Static resx:ResUI.LvUserAgent}" />
<TextBox <TextBox
x:Name="txtUserAgent" x:Name="txtUserAgent"
Grid.Row="7" Grid.Row="7"
Grid.Column="1" Grid.Column="1"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
TextWrapping="Wrap" TextWrapping="Wrap"
Watermark="{x:Static resx:ResUI.SubUrlTips}" /> Watermark="{x:Static resx:ResUI.SubUrlTips}" />
<TextBlock <TextBlock
Grid.Row="8" Grid.Row="8"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.LvSort}" /> Text="{x:Static resx:ResUI.LvSort}" />
<TextBox <TextBox
x:Name="txtSort" x:Name="txtSort"
Grid.Row="8" Grid.Row="8"
Grid.Column="1" Grid.Column="1"
Width="100" Width="100"
HorizontalAlignment="Left" Margin="{StaticResource Margin4}"
Classes="Margin8" /> HorizontalAlignment="Left" />
<TextBlock <TextBlock
Grid.Row="9" Grid.Row="9"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.LvPrevProfile}" /> Text="{x:Static resx:ResUI.LvPrevProfile}" />
<TextBox <TextBox
x:Name="txtPrevProfile" x:Name="txtPrevProfile"
Grid.Row="9" Grid.Row="9"
Grid.Column="1" Grid.Column="1"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Watermark="{x:Static resx:ResUI.LvPrevProfileTip}" /> Watermark="{x:Static resx:ResUI.LvPrevProfileTip}" />
<TextBlock <TextBlock
Grid.Row="10" Grid.Row="10"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.LvNextProfile}" /> Text="{x:Static resx:ResUI.LvNextProfile}" />
<TextBox <TextBox
x:Name="txtNextProfile" x:Name="txtNextProfile"
Grid.Row="10" Grid.Row="10"
Grid.Column="1" Grid.Column="1"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Watermark="{x:Static resx:ResUI.LvPrevProfileTip}" /> Watermark="{x:Static resx:ResUI.LvPrevProfileTip}" />
<TextBlock <TextBlock
Grid.Row="11" Grid.Row="11"
Grid.Column="0" Grid.Column="0"
Margin="4" Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbPreSocksPort4Sub}" /> Text="{x:Static resx:ResUI.TbPreSocksPort4Sub}" />
<TextBox <TextBox
x:Name="txtPreSocksPort" x:Name="txtPreSocksPort"
Grid.Row="11" Grid.Row="11"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Margin="4" Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Classes="Margin8"
ToolTip.Tip="{x:Static resx:ResUI.TipPreSocksPort}" ToolTip.Tip="{x:Static resx:ResUI.TipPreSocksPort}"
Watermark="{x:Static resx:ResUI.TipPreSocksPort}" /> Watermark="{x:Static resx:ResUI.TipPreSocksPort}" />
<TextBlock <TextBlock
Grid.Row="12" Grid.Row="12"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.LvMemo}" /> Text="{x:Static resx:ResUI.LvMemo}" />
<TextBox <TextBox
x:Name="txtMemo" x:Name="txtMemo"
Grid.Row="12" Grid.Row="12"
Grid.Column="1" Grid.Column="1"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
</Grid> </Grid>

View File

@@ -1,4 +1,4 @@
<Window <Window
x:Class="v2rayN.Desktop.Views.SubSettingWindow" x:Class="v2rayN.Desktop.Views.SubSettingWindow"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -19,9 +19,9 @@
CloseOnClickAway="True" CloseOnClickAway="True"
DisableOpeningAnimation="True" DisableOpeningAnimation="True"
Identifier="dialogHostSub"> Identifier="dialogHostSub">
<DockPanel Classes="Margin8"> <DockPanel Margin="{StaticResource Margin8}">
<StackPanel <StackPanel
Classes="Margin8" Margin="{StaticResource Margin4}"
DockPanel.Dock="Top" DockPanel.Dock="Top"
Orientation="Horizontal"> Orientation="Horizontal">
<Menu> <Menu>

View File

@@ -84,7 +84,10 @@ namespace v2rayN.Desktop.Views
private void LstSubscription_SelectionChanged(object? sender, SelectionChangedEventArgs e) private void LstSubscription_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{ {
ViewModel.SelectedSources = lstSubscription.SelectedItems.Cast<SubItem>().ToList(); if (ViewModel != null)
{
ViewModel.SelectedSources = lstSubscription.SelectedItems.Cast<SubItem>().ToList();
}
} }
private void menuClose_Click(object? sender, RoutedEventArgs e) private void menuClose_Click(object? sender, RoutedEventArgs e)

View File

@@ -1,4 +1,4 @@
<UserControl <UserControl
x:Class="v2rayN.Desktop.Views.ThemeSettingView" x:Class="v2rayN.Desktop.Views.ThemeSettingView"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -20,52 +20,48 @@
</UserControl.Styles> </UserControl.Styles>
<Button <Button
Width="30" Width="{StaticResource IconButtonWidth}"
Height="30" Height="{StaticResource IconButtonHeight}"
Margin="10,0" Margin="{StaticResource MarginLr8}"
Theme="{DynamicResource BorderlessButton}"> Theme="{DynamicResource BorderlessButton}">
<Button.Content> <Button.Content>
<PathIcon <PathIcon Data="{StaticResource building_more}" Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
Width="20"
Height="20"
Data="{StaticResource building_more}"
Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
</Button.Content> </Button.Content>
<Button.Flyout> <Button.Flyout>
<Flyout> <Flyout>
<StackPanel Margin="0,12"> <StackPanel Margin="{StaticResource MarginTb8}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock <TextBlock
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSettingsTheme}" /> Text="{x:Static resx:ResUI.TbSettingsTheme}" />
<ComboBox <ComboBox
x:Name="cmbCurrentTheme" x:Name="cmbCurrentTheme"
HorizontalAlignment="Center" Margin="{StaticResource Margin4}"
Classes="Margin8" /> HorizontalAlignment="Center" />
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock <TextBlock
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSettingsFontSize}" /> Text="{x:Static resx:ResUI.TbSettingsFontSize}" />
<ComboBox <ComboBox
x:Name="cmbCurrentFontSize" x:Name="cmbCurrentFontSize"
HorizontalAlignment="Center" Margin="{StaticResource Margin4}"
Classes="Margin8" /> HorizontalAlignment="Center" />
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock <TextBlock
Margin="{StaticResource Margin4}"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSettingsLanguage}" /> Text="{x:Static resx:ResUI.TbSettingsLanguage}" />
<ComboBox <ComboBox
x:Name="cmbCurrentLanguage" x:Name="cmbCurrentLanguage"
HorizontalAlignment="Center" Margin="{StaticResource Margin4}"
Classes="Margin8" /> HorizontalAlignment="Center" />
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</Flyout> </Flyout>
</Button.Flyout> </Button.Flyout>
</Button> </Button>
</UserControl> </UserControl>

View File

@@ -31,6 +31,7 @@
<ItemGroup> <ItemGroup>
<ProjectCapability Include="Avalonia" /> <ProjectCapability Include="Avalonia" />
<AvaloniaResource Include="Assets\**" /> <AvaloniaResource Include="Assets\**" />
<ProjectReference Include="..\GlobalHotKeys\src\GlobalHotKeys\GlobalHotKeys.csproj" />
<ProjectReference Include="..\ServiceLib\ServiceLib.csproj" /> <ProjectReference Include="..\ServiceLib\ServiceLib.csproj" />
</ItemGroup> </ItemGroup>

View File

@@ -11,6 +11,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "v2rayN.Desktop", "v2rayN.De
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AmazTool", "AmazTool\AmazTool.csproj", "{47D68B1C-601C-4C69-873B-FFF0DC13EC97}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AmazTool", "AmazTool\AmazTool.csproj", "{47D68B1C-601C-4C69-873B-FFF0DC13EC97}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GlobalHotKeys", "GlobalHotKeys\src\GlobalHotKeys\GlobalHotKeys.csproj", "{CB3DE54F-3A26-AE02-1299-311132C32156}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -33,6 +35,10 @@ Global
{47D68B1C-601C-4C69-873B-FFF0DC13EC97}.Debug|Any CPU.Build.0 = Debug|Any CPU {47D68B1C-601C-4C69-873B-FFF0DC13EC97}.Debug|Any CPU.Build.0 = Debug|Any CPU
{47D68B1C-601C-4C69-873B-FFF0DC13EC97}.Release|Any CPU.ActiveCfg = Release|Any CPU {47D68B1C-601C-4C69-873B-FFF0DC13EC97}.Release|Any CPU.ActiveCfg = Release|Any CPU
{47D68B1C-601C-4C69-873B-FFF0DC13EC97}.Release|Any CPU.Build.0 = Release|Any CPU {47D68B1C-601C-4C69-873B-FFF0DC13EC97}.Release|Any CPU.Build.0 = Release|Any CPU
{CB3DE54F-3A26-AE02-1299-311132C32156}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CB3DE54F-3A26-AE02-1299-311132C32156}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB3DE54F-3A26-AE02-1299-311132C32156}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB3DE54F-3A26-AE02-1299-311132C32156}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -11,15 +11,8 @@ namespace v2rayN.Handler
{ {
private static readonly Lazy<HotkeyHandler> _instance = new(() => new()); private static readonly Lazy<HotkeyHandler> _instance = new(() => new());
public static HotkeyHandler Instance = _instance.Value; public static HotkeyHandler Instance = _instance.Value;
private const int WmHotkey = 0x0312; private const int WmHotkey = 0x0312;
private readonly Dictionary<int, List<EGlobalHotkey>> _hotkeyTriggerDic = new();
private Config _config
{
get => AppHandler.Instance.Config;
}
private Dictionary<int, List<EGlobalHotkey>> _hotkeyTriggerDic;
public bool IsPause { get; set; } = false; public bool IsPause { get; set; } = false;
@@ -29,7 +22,6 @@ namespace v2rayN.Handler
public HotkeyHandler() public HotkeyHandler()
{ {
_hotkeyTriggerDic = new();
ComponentDispatcher.ThreadPreprocessMessage += OnThreadPreProcessMessage; ComponentDispatcher.ThreadPreprocessMessage += OnThreadPreProcessMessage;
Init(); Init();
} }
@@ -37,20 +29,27 @@ namespace v2rayN.Handler
private void Init() private void Init()
{ {
_hotkeyTriggerDic.Clear(); _hotkeyTriggerDic.Clear();
if (_config.GlobalHotkeys == null) foreach (var item in AppHandler.Instance.Config.GlobalHotkeys)
return;
foreach (var item in _config.GlobalHotkeys)
{ {
if (item.KeyCode != null && (Key)item.KeyCode != Key.None) if (item.KeyCode != null && (Key)item.KeyCode != Key.None)
{ {
int key = KeyInterop.VirtualKeyFromKey((Key)item.KeyCode); var key = KeyInterop.VirtualKeyFromKey((Key)item.KeyCode);
KeyModifiers modifiers = KeyModifiers.None; var modifiers = KeyModifiers.None;
if (item.Control) if (item.Control)
{
modifiers |= KeyModifiers.Ctrl; modifiers |= KeyModifiers.Ctrl;
}
if (item.Shift) if (item.Shift)
{
modifiers |= KeyModifiers.Shift; modifiers |= KeyModifiers.Shift;
}
if (item.Alt) if (item.Alt)
{
modifiers |= KeyModifiers.Alt; modifiers |= KeyModifiers.Alt;
}
key = (key << 16) | (int)modifiers; key = (key << 16) | (int)modifiers;
if (!_hotkeyTriggerDic.ContainsKey(key)) if (!_hotkeyTriggerDic.ContainsKey(key))
{ {
@@ -59,7 +58,9 @@ namespace v2rayN.Handler
else else
{ {
if (!_hotkeyTriggerDic[key].Contains(item.EGlobalHotkey)) if (!_hotkeyTriggerDic[key].Contains(item.EGlobalHotkey))
{
_hotkeyTriggerDic[key].Add(item.EGlobalHotkey); _hotkeyTriggerDic[key].Add(item.EGlobalHotkey);
}
} }
} }
} }
@@ -70,8 +71,8 @@ namespace v2rayN.Handler
foreach (var _hotkeyCode in _hotkeyTriggerDic.Keys) foreach (var _hotkeyCode in _hotkeyTriggerDic.Keys)
{ {
var hotkeyInfo = GetHotkeyInfo(_hotkeyCode); var hotkeyInfo = GetHotkeyInfo(_hotkeyCode);
bool isSuccess = false; var isSuccess = false;
string msg; var msg = string.Empty;
Application.Current?.Dispatcher.Invoke(() => Application.Current?.Dispatcher.Invoke(() =>
{ {
@@ -106,29 +107,38 @@ namespace v2rayN.Handler
Load(); Load();
} }
private (int fsModifiers, int vKey, string hotkeyStr, List<string> Names) GetHotkeyInfo(int hotkeycode) private (int fsModifiers, int vKey, string hotkeyStr, List<string> Names) GetHotkeyInfo(int hotkeyCode)
{ {
var _fsModifiers = hotkeycode & 0xffff; var fsModifiers = hotkeyCode & 0xffff;
var _vkey = (hotkeycode >> 16) & 0xffff; var vKey = (hotkeyCode >> 16) & 0xffff;
var _hotkeyStr = new StringBuilder(); var hotkeyStr = new StringBuilder();
var _names = new List<string>(); var names = new List<string>();
var mdif = (KeyModifiers)_fsModifiers; var modify = (KeyModifiers)fsModifiers;
var key = KeyInterop.KeyFromVirtualKey(_vkey); var key = KeyInterop.KeyFromVirtualKey(vKey);
if ((mdif & KeyModifiers.Ctrl) == KeyModifiers.Ctrl) if ((modify & KeyModifiers.Ctrl) == KeyModifiers.Ctrl)
_hotkeyStr.Append($"{KeyModifiers.Ctrl}+");
if ((mdif & KeyModifiers.Alt) == KeyModifiers.Alt)
_hotkeyStr.Append($"{KeyModifiers.Alt}+");
if ((mdif & KeyModifiers.Shift) == KeyModifiers.Shift)
_hotkeyStr.Append($"{KeyModifiers.Shift}+");
_hotkeyStr.Append(key.ToString());
foreach (var name in _hotkeyTriggerDic[hotkeycode])
{ {
_names.Add(name.ToString()); hotkeyStr.Append($"{KeyModifiers.Ctrl}+");
} }
return (_fsModifiers, _vkey, _hotkeyStr.ToString(), _names); if ((modify & KeyModifiers.Alt) == KeyModifiers.Alt)
{
hotkeyStr.Append($"{KeyModifiers.Alt}+");
}
if ((modify & KeyModifiers.Shift) == KeyModifiers.Shift)
{
hotkeyStr.Append($"{KeyModifiers.Shift}+");
}
hotkeyStr.Append(key.ToString());
foreach (var name in _hotkeyTriggerDic[hotkeyCode])
{
names.Add(name.ToString());
}
return (fsModifiers, vKey, hotkeyStr.ToString(), names);
} }
private void OnThreadPreProcessMessage(ref MSG msg, ref bool handled) private void OnThreadPreProcessMessage(ref MSG msg, ref bool handled)
@@ -138,21 +148,18 @@ namespace v2rayN.Handler
return; return;
} }
handled = true; handled = true;
var _hotKeyCode = (int)msg.lParam; var hotKeyCode = (int)msg.lParam;
if (IsPause) if (IsPause)
{ {
Application.Current?.Dispatcher.Invoke(() => Application.Current?.Dispatcher.Invoke(() =>
{ {
UIElement? element = Keyboard.FocusedElement as UIElement; if (Keyboard.FocusedElement is UIElement element)
if (element != null)
{ {
var _keyEventArgs = new KeyEventArgs(Keyboard.PrimaryDevice, var keyEventArgs = new KeyEventArgs(Keyboard.PrimaryDevice, PresentationSource.FromVisual(element), 0, KeyInterop.KeyFromVirtualKey(GetHotkeyInfo(hotKeyCode).vKey))
PresentationSource.FromVisual(element), 0,
KeyInterop.KeyFromVirtualKey(GetHotkeyInfo(_hotKeyCode).vKey))
{ {
RoutedEvent = UIElement.KeyDownEvent RoutedEvent = UIElement.KeyDownEvent
}; };
element.RaiseEvent(_keyEventArgs); element.RaiseEvent(keyEventArgs);
} }
}); });
} }

View File

@@ -1,4 +1,4 @@
<reactiveui:ReactiveWindow <reactiveui:ReactiveWindow
x:Class="v2rayN.Views.AddServer2Window" x:Class="v2rayN.Views.AddServer2Window"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -8,7 +8,7 @@
xmlns:reactiveui="http://reactiveui.net" xmlns:reactiveui="http://reactiveui.net"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib" xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="v2rayN" Title="{x:Static resx:ResUI.menuAddCustomServer}"
Width="700" Width="700"
Height="500" Height="500"
x:TypeArguments="vms:AddServer2ViewModel" x:TypeArguments="vms:AddServer2ViewModel"
@@ -198,4 +198,4 @@
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</DockPanel> </DockPanel>
</reactiveui:ReactiveWindow> </reactiveui:ReactiveWindow>

View File

@@ -1,18 +1,17 @@
<reactiveui:ReactiveWindow <reactiveui:ReactiveWindow
x:Class="v2rayN.Views.GlobalHotkeySettingWindow" x:Class="v2rayN.Views.GlobalHotkeySettingWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:reactiveui="http://reactiveui.net"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
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" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveui="http://reactiveui.net"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib" xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="{x:Static resx:ResUI.menuSetting}" Title="{x:Static resx:ResUI.menuGlobalHotkeySetting}"
Width="700" Width="700"
Height="500" Height="500"
x:TypeArguments="vms:SubEditViewModel" x:TypeArguments="vms:GlobalHotkeySettingViewModel"
KeyDown="GlobalHotkeySettingWindow_KeyDown"
ShowInTaskbar="False" ShowInTaskbar="False"
Style="{StaticResource WindowGlobal}" Style="{StaticResource WindowGlobal}"
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
@@ -92,7 +91,6 @@
VerticalAlignment="Center" VerticalAlignment="Center"
AcceptsReturn="True" AcceptsReturn="True"
IsReadOnly="True" IsReadOnly="True"
PreviewKeyDown="TxtGlobalHotkey_PreviewKeyDown"
Style="{StaticResource MyOutlinedTextBox}" /> Style="{StaticResource MyOutlinedTextBox}" />
<TextBlock <TextBlock
@@ -110,7 +108,6 @@
VerticalAlignment="Center" VerticalAlignment="Center"
AcceptsReturn="True" AcceptsReturn="True"
IsReadOnly="True" IsReadOnly="True"
PreviewKeyDown="TxtGlobalHotkey_PreviewKeyDown"
Style="{StaticResource MyOutlinedTextBox}" /> Style="{StaticResource MyOutlinedTextBox}" />
<TextBlock <TextBlock
@@ -128,7 +125,6 @@
VerticalAlignment="Center" VerticalAlignment="Center"
AcceptsReturn="True" AcceptsReturn="True"
IsReadOnly="True" IsReadOnly="True"
PreviewKeyDown="TxtGlobalHotkey_PreviewKeyDown"
Style="{StaticResource MyOutlinedTextBox}" /> Style="{StaticResource MyOutlinedTextBox}" />
<TextBlock <TextBlock
Grid.Row="4" Grid.Row="4"
@@ -145,7 +141,6 @@
VerticalAlignment="Center" VerticalAlignment="Center"
AcceptsReturn="True" AcceptsReturn="True"
IsReadOnly="True" IsReadOnly="True"
PreviewKeyDown="TxtGlobalHotkey_PreviewKeyDown"
Style="{StaticResource MyOutlinedTextBox}" /> Style="{StaticResource MyOutlinedTextBox}" />
<TextBlock <TextBlock
Grid.Row="5" Grid.Row="5"
@@ -162,7 +157,6 @@
VerticalAlignment="Center" VerticalAlignment="Center"
AcceptsReturn="True" AcceptsReturn="True"
IsReadOnly="True" IsReadOnly="True"
PreviewKeyDown="TxtGlobalHotkey_PreviewKeyDown"
Style="{StaticResource MyOutlinedTextBox}" /> Style="{StaticResource MyOutlinedTextBox}" />
</Grid> </Grid>

View File

@@ -1,140 +1,139 @@
using System.Reactive.Disposables;
using System.Text; using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using ReactiveUI;
using v2rayN.Handler; using v2rayN.Handler;
namespace v2rayN.Views namespace v2rayN.Views
{ {
public partial class GlobalHotkeySettingWindow public partial class GlobalHotkeySettingWindow
{ {
private static Config _config = default!; private readonly List<object> _textBoxKeyEventItem = new();
private Dictionary<object, KeyEventItem> _TextBoxKeyEventItem = default!;
public GlobalHotkeySettingWindow() public GlobalHotkeySettingWindow()
{ {
InitializeComponent(); InitializeComponent();
this.Owner = Application.Current.MainWindow; this.Owner = Application.Current.MainWindow;
_config = AppHandler.Instance.Config;
_config.GlobalHotkeys ??= new List<KeyEventItem>(); ViewModel = new GlobalHotkeySettingViewModel(UpdateViewHandler);
btnReset.Click += btnReset_Click; btnReset.Click += btnReset_Click;
btnSave.Click += btnSave_ClickAsync;
txtGlobalHotkey0.KeyDown += TxtGlobalHotkey_PreviewKeyDown;
txtGlobalHotkey1.KeyDown += TxtGlobalHotkey_PreviewKeyDown;
txtGlobalHotkey2.KeyDown += TxtGlobalHotkey_PreviewKeyDown;
txtGlobalHotkey3.KeyDown += TxtGlobalHotkey_PreviewKeyDown;
txtGlobalHotkey4.KeyDown += TxtGlobalHotkey_PreviewKeyDown;
HotkeyHandler.Instance.IsPause = true; HotkeyHandler.Instance.IsPause = true;
this.Closing += (s, e) => HotkeyHandler.Instance.IsPause = false; this.Closing += (s, e) => HotkeyHandler.Instance.IsPause = false;
WindowsUtils.SetDarkBorder(this, _config.UiItem.CurrentTheme);
InitData();
}
private void InitData() this.WhenActivated(disposables =>
{
_TextBoxKeyEventItem = new()
{ {
{ txtGlobalHotkey0,GetKeyEventItemByEGlobalHotkey(_config.GlobalHotkeys,EGlobalHotkey.ShowForm) }, this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
{ txtGlobalHotkey1,GetKeyEventItemByEGlobalHotkey(_config.GlobalHotkeys,EGlobalHotkey.SystemProxyClear) }, });
{ txtGlobalHotkey2,GetKeyEventItemByEGlobalHotkey(_config.GlobalHotkeys,EGlobalHotkey.SystemProxySet) }, WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme);
{ txtGlobalHotkey3,GetKeyEventItemByEGlobalHotkey(_config.GlobalHotkeys,EGlobalHotkey.SystemProxyUnchanged)},
{ txtGlobalHotkey4,GetKeyEventItemByEGlobalHotkey(_config.GlobalHotkeys,EGlobalHotkey.SystemProxyPac)} Init();
};
BindingData(); BindingData();
} }
private void TxtGlobalHotkey_PreviewKeyDown(object sender, KeyEventArgs e) private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
break;
}
return await Task.FromResult(true);
}
private void Init()
{
_textBoxKeyEventItem.Add(txtGlobalHotkey0);
_textBoxKeyEventItem.Add(txtGlobalHotkey1);
_textBoxKeyEventItem.Add(txtGlobalHotkey2);
_textBoxKeyEventItem.Add(txtGlobalHotkey3);
_textBoxKeyEventItem.Add(txtGlobalHotkey4);
for (var index = 0; index < _textBoxKeyEventItem.Count; index++)
{
var sender = _textBoxKeyEventItem[index];
if (sender is not TextBox txtBox)
{
continue;
}
txtBox.Tag = (EGlobalHotkey)index;
txtBox.PreviewKeyDown += TxtGlobalHotkey_PreviewKeyDown;
}
}
private void TxtGlobalHotkey_PreviewKeyDown(object? sender, KeyEventArgs e)
{ {
e.Handled = true; e.Handled = true;
var _ModifierKeys = new Key[] { Key.LeftCtrl, Key.RightCtrl, Key.LeftShift, if (sender is not TextBox txtBox)
Key.RightShift, Key.LeftAlt, Key.RightAlt, Key.LWin, Key.RWin};
_TextBoxKeyEventItem[sender].KeyCode = (int)(e.Key == Key.System ? (_ModifierKeys.Contains(e.SystemKey) ? Key.None : e.SystemKey) : (_ModifierKeys.Contains(e.Key) ? Key.None : e.Key));
_TextBoxKeyEventItem[sender].Alt = (Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt;
_TextBoxKeyEventItem[sender].Control = (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control;
_TextBoxKeyEventItem[sender].Shift = (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift;
(sender as TextBox)!.Text = KeyEventItemToString(_TextBoxKeyEventItem[sender]);
}
private KeyEventItem GetKeyEventItemByEGlobalHotkey(List<KeyEventItem> KEList, EGlobalHotkey eg)
{
return JsonUtils.DeepCopy(KEList.Find((it) => it.EGlobalHotkey == eg) ?? new()
{ {
EGlobalHotkey = eg, return;
Control = false, }
Alt = false,
Shift = false,
KeyCode = null
});
}
private string KeyEventItemToString(KeyEventItem item) var item = ViewModel?.GetKeyEventItem((EGlobalHotkey)txtBox.Tag);
{ var modifierKeys = new Key[] { Key.LeftCtrl, Key.RightCtrl, Key.LeftShift, Key.RightShift, Key.LeftAlt, Key.RightAlt, Key.LWin, Key.RWin };
var res = new StringBuilder();
if (item.Control) item.KeyCode = (int)(e.Key == Key.System ? (modifierKeys.Contains(e.SystemKey) ? Key.None : e.SystemKey) : (modifierKeys.Contains(e.Key) ? Key.None : e.Key));
res.Append($"{ModifierKeys.Control}+"); item.Alt = (Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt;
if (item.Shift) item.Control = (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control;
res.Append($"{ModifierKeys.Shift}+"); item.Shift = (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift;
if (item.Alt)
res.Append($"{ModifierKeys.Alt}+");
if (item.KeyCode != null && (Key)item.KeyCode != Key.None)
res.Append($"{(Key)item.KeyCode}");
return res.ToString(); txtBox.Text = KeyEventItemToString(item);
} }
private void BindingData() private void BindingData()
{ {
foreach (var item in _TextBoxKeyEventItem) foreach (var sender in _textBoxKeyEventItem)
{ {
if (item.Value.KeyCode != null && (Key)item.Value.KeyCode != Key.None) if (sender is not TextBox txtBox)
{ {
(item.Key as TextBox)!.Text = KeyEventItemToString(item.Value); continue;
} }
else
{
(item.Key as TextBox)!.Text = string.Empty;
}
}
}
private async void btnSave_ClickAsync(object sender, RoutedEventArgs e) var item = ViewModel?.GetKeyEventItem((EGlobalHotkey)txtBox.Tag);
{ txtBox.Text = KeyEventItemToString(item);
_config.GlobalHotkeys = _TextBoxKeyEventItem.Values.ToList();
if (await ConfigHandler.SaveConfig(_config) == 0)
{
HotkeyHandler.Instance.ReLoad();
this.DialogResult = true;
}
else
{
UI.Show(ResUI.OperationFailed);
} }
} }
private void btnReset_Click(object sender, RoutedEventArgs e) private void btnReset_Click(object sender, RoutedEventArgs e)
{ {
foreach (var k in _TextBoxKeyEventItem.Keys) ViewModel?.ResetKeyEventItem();
{
_TextBoxKeyEventItem[k].Alt = false;
_TextBoxKeyEventItem[k].Control = false;
_TextBoxKeyEventItem[k].Shift = false;
_TextBoxKeyEventItem[k].KeyCode = (int)Key.None;
}
BindingData(); BindingData();
} }
private void GlobalHotkeySettingWindow_KeyDown(object sender, KeyEventArgs e) private string KeyEventItemToString(KeyEventItem? item)
{ {
if (e.Key == Key.Escape) if (item == null)
{ {
this.Close(); return string.Empty;
} }
var res = new StringBuilder();
if (item.Control)
{
res.Append($"{ModifierKeys.Control} +");
}
if (item.Shift)
{
res.Append($"{ModifierKeys.Shift} +");
}
if (item.Alt)
{
res.Append($"{ModifierKeys.Alt} +");
}
if (item.KeyCode != null && (Key)item.KeyCode != Key.None)
{
res.Append($"{(Key)item.KeyCode}");
}
return res.ToString();
} }
} }
} }

View File

@@ -764,7 +764,7 @@
x:Name="cmbcurrentFontFamily" x:Name="cmbcurrentFontFamily"
Grid.Row="15" Grid.Row="15"
Grid.Column="1" Grid.Column="1"
Width="200" Width="300"
Margin="{StaticResource Margin8}" Margin="{StaticResource Margin8}"
MaxDropDownHeight="1000" MaxDropDownHeight="1000"
Style="{StaticResource DefComboBox}" /> Style="{StaticResource DefComboBox}" />
@@ -776,23 +776,22 @@
Style="{StaticResource ToolbarTextBlock}" Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamilyTip}" Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamilyTip}"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<!--
<TextBlock <TextBlock
Grid.Row="16" Grid.Row="16"
Grid.Column="0" Grid.Column="0"
Margin="{StaticResource Margin8}" Margin="{StaticResource Margin8}"
VerticalAlignment="Center" VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}" Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSettingsSpeedTestPageSize}" /> Text="{x:Static resx:ResUI.TbSettingsMixedConcurrencyCount}" />
<TextBox <ComboBox
x:Name="txtSpeedTestPageSize" x:Name="cmbMixedConcurrencyCount"
Grid.Row="16" Grid.Row="16"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Margin="{StaticResource Margin8}" Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" Style="{StaticResource DefComboBox}" />
Style="{StaticResource DefTextBox}" />
-->
<TextBlock <TextBlock
Grid.Row="17" Grid.Row="17"
Grid.Column="0" Grid.Column="0"
@@ -868,7 +867,7 @@
x:Name="cmbMainGirdOrientation" x:Name="cmbMainGirdOrientation"
Grid.Row="21" Grid.Row="21"
Grid.Column="1" Grid.Column="1"
Width="300" Width="200"
Margin="{StaticResource Margin8}" Margin="{StaticResource Margin8}"
Style="{StaticResource DefComboBox}" /> Style="{StaticResource DefComboBox}" />

View File

@@ -68,7 +68,11 @@ namespace v2rayN.Views
cmbCoreType6.Items.Add(it); cmbCoreType6.Items.Add(it);
}); });
for (int i = 2; i <= 6; i++) for (var i = 2; i <= 8; i++)
{
cmbMixedConcurrencyCount.Items.Add(i);
}
for (var i = 2; i <= 6; i++)
{ {
cmbSpeedTestTimeout.Items.Add(i * 5); cmbSpeedTestTimeout.Items.Add(i * 5);
} }
@@ -150,7 +154,7 @@ namespace v2rayN.Views
this.Bind(ViewModel, vm => vm.SpeedTestTimeout, v => v.cmbSpeedTestTimeout.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SpeedTestTimeout, v => v.cmbSpeedTestTimeout.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SpeedTestUrl, v => v.cmbSpeedTestUrl.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SpeedTestUrl, v => v.cmbSpeedTestUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SpeedPingTestUrl, v => v.cmbSpeedPingTestUrl.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SpeedPingTestUrl, v => v.cmbSpeedPingTestUrl.Text).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.SpeedTestPageSize, v => v.txtSpeedTestPageSize.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.MixedConcurrencyCount, v => v.cmbMixedConcurrencyCount.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableHWA, v => v.togEnableHWA.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.EnableHWA, v => v.togEnableHWA.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SubConvertUrl, v => v.cmbSubConvertUrl.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SubConvertUrl, v => v.cmbSubConvertUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.MainGirdOrientation, v => v.cmbMainGirdOrientation.SelectedIndex).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.MainGirdOrientation, v => v.cmbMainGirdOrientation.SelectedIndex).DisposeWith(disposables);
@@ -255,7 +259,10 @@ namespace v2rayN.Views
private void ClbdestOverride_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) private void ClbdestOverride_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{ {
ViewModel.destOverride = clbdestOverride.SelectedItems.Cast<string>().ToList(); if (ViewModel != null)
{
ViewModel.destOverride = clbdestOverride.SelectedItems.Cast<string>().ToList();
}
} }
} }
} }

View File

@@ -146,10 +146,16 @@
x:Name="menuSpeedServer" x:Name="menuSpeedServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSpeedServer}" /> Header="{x:Static resx:ResUI.menuSpeedServer}" />
<MenuItem <MenuItem Header="{x:Static resx:ResUI.menuTestServerResult}">
x:Name="menuSortServerResult" <MenuItem
Height="{StaticResource MenuItemHeight}" x:Name="menuSortServerResult"
Header="{x:Static resx:ResUI.menuSortServerResult}" /> Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSortServerResult}" />
<MenuItem
x:Name="menuRemoveInvalidServerResult"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuRemoveInvalidServerResult}" />
</MenuItem>
<Separator /> <Separator />
<MenuItem <MenuItem
x:Name="menuMoveToGroup" x:Name="menuMoveToGroup"
@@ -321,4 +327,4 @@
</DataGrid> </DataGrid>
</DockPanel> </DockPanel>
</Grid> </Grid>
</reactiveui:ReactiveUserControl> </reactiveui:ReactiveUserControl>

View File

@@ -79,6 +79,7 @@ namespace v2rayN.Views
this.BindCommand(ViewModel, vm => vm.RealPingServerCmd, v => v.menuRealPingServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.RealPingServerCmd, v => v.menuRealPingServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SpeedServerCmd, v => v.menuSpeedServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SpeedServerCmd, v => v.menuSpeedServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SortServerResultCmd, v => v.menuSortServerResult).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SortServerResultCmd, v => v.menuSortServerResult).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RemoveInvalidServerResultCmd, v => v.menuRemoveInvalidServerResult).DisposeWith(disposables);
//servers export //servers export
this.BindCommand(ViewModel, vm => vm.Export2ClientConfigCmd, v => v.menuExport2ClientConfig).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.Export2ClientConfigCmd, v => v.menuExport2ClientConfig).DisposeWith(disposables);
@@ -165,7 +166,7 @@ namespace v2rayN.Views
case EViewAction.DispatcherRefreshServersBiz: case EViewAction.DispatcherRefreshServersBiz:
Application.Current?.Dispatcher.Invoke((() => Application.Current?.Dispatcher.Invoke((() =>
{ {
ViewModel?.RefreshServersBiz(); _ = RefreshServersBiz();
}), DispatcherPriority.Normal); }), DispatcherPriority.Normal);
break; break;
} }
@@ -185,9 +186,25 @@ namespace v2rayN.Views
await DialogHost.Show(dialog, "RootDialog"); await DialogHost.Show(dialog, "RootDialog");
} }
public async Task RefreshServersBiz()
{
if (ViewModel != null)
{
await ViewModel.RefreshServersBiz();
}
if (lstProfiles.SelectedIndex > 0)
{
lstProfiles.ScrollIntoView(lstProfiles.SelectedItem, null);
}
}
private void lstProfiles_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) private void lstProfiles_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{ {
ViewModel.SelectedProfiles = lstProfiles.SelectedItems.Cast<ProfileItemModel>().ToList(); if (ViewModel != null)
{
ViewModel.SelectedProfiles = lstProfiles.SelectedItems.Cast<ProfileItemModel>().ToList();
}
} }
private void LstProfiles_LoadingRow(object? sender, DataGridRowEventArgs e) private void LstProfiles_LoadingRow(object? sender, DataGridRowEventArgs e)
@@ -352,9 +369,9 @@ namespace v2rayN.Views
private void StorageUI(string? n = null) private void StorageUI(string? n = null)
{ {
List<ColumnItem> lvColumnItem = new(); List<ColumnItem> lvColumnItem = new();
for (int k = 0; k < lstProfiles.Columns.Count; k++) foreach (var t in lstProfiles.Columns)
{ {
var item2 = (MyDGTextColumn)lstProfiles.Columns[k]; var item2 = (MyDGTextColumn)t;
lvColumnItem.Add(new() lvColumnItem.Add(new()
{ {
Name = item2.ExName, Name = item2.ExName,

Some files were not shown because too many files have changed in this diff Show More