Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 88a8d8fa4b | |||
| bf5c4ab188 | |||
| 14d314e0a2 | |||
| 324d514f10 | |||
| e11e1989ad | |||
| a5b99c25ed | |||
| 3c908feb95 | |||
| 840eca88c8 | |||
| fa7caf3592 | |||
| 654e15f845 | |||
| 7a81344aea | |||
| 803d267687 | |||
| c8e4d18364 | |||
| aeecca1260 | |||
| 49aaa2d58b | |||
| dd22b119b7 | |||
| 42984ea5fa | |||
| ce0147091a | |||
| 884abf5b0e | |||
| 5cc1c7093a | |||
| 87aae93f4b | |||
| 77002b59b2 | |||
| 5601f72d59 | |||
| afcc8050ce | |||
| 17c043668c | |||
| 2495fc7b26 | |||
| 61404ff852 | |||
| a62ae3c6e0 | |||
| 28f65b3790 | |||
| ce4b83dbe0 | |||
| 7a24faece1 | |||
| 22555b835b | |||
| b2648fecac | |||
| 15a6799d7b | |||
| 13b24aeb7c | |||
| 674ce28420 | |||
| 60c07c3738 | |||
| 2b7f3c1f73 | |||
| 04ff617e3c | |||
| 5ee247d423 | |||
| 0bd2c4eff7 | |||
| 0408729a1d | |||
| 61e9a456af | |||
| 93f89e7a41 | |||
| bb26c9ce55 | |||
| bd2107378b | |||
| 0b0714c166 | |||
| 696ed84f4c | |||
| c8a467af9d | |||
| 8f6383cbe6 | |||
| 25e64dd6f8 | |||
| 745bc65379 | |||
| a4868215f2 | |||
| 8e2d8045c0 | |||
| 5b290ef5c0 | |||
| b68a50431f | |||
| 4f8c7d66e1 | |||
| 48bf15457d | |||
| d85bea7872 | |||
| 02fe33da5a | |||
| 3f17cc705b | |||
| 8078d0f137 | |||
| 51ed642c35 | |||
| 842e1c0d6c | |||
| f76cfe648f | |||
| 1b66473ae0 | |||
| f0bae76b78 | |||
| ff58675069 | |||
| b3cb53b482 | |||
| 4eb9773398 | |||
| 748ab0fff2 | |||
| ae22162020 | |||
| 7390e59ab0 | |||
| 57e0331c58 | |||
| 862374bec4 | |||
| 266c5c0dc8 | |||
| 9fee9a1d6a | |||
| f65341d906 | |||
| c11ca7a3f7 | |||
| b7f3a38826 | |||
| 6ebe50e71b | |||
| 6ce2aea000 | |||
| 60c341927f | |||
| ed03d40ff3 | |||
| 6c616f2b6b | |||
| 84f026f1dd | |||
| 89c9c0d9ef | |||
| 9d631cc980 | |||
| 786d4fc719 | |||
| 9c68a5350b | |||
| e26bf83bb0 | |||
| 52583a6092 | |||
| de9056739b | |||
| 9c611698c7 | |||
| a6357abc15 | |||
| b83facb14e | |||
| 2dc16c13bc |
@@ -1,12 +1,15 @@
|
||||
name: Bug 报告 | Bug Report
|
||||
description: 反馈软件缺陷或异常 | Report a bug to help us improve
|
||||
labels: [bug]
|
||||
type: Bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢你的反馈!请详细填写以下内容,便于我们定位问题。
|
||||
Thank you for your feedback! Please fill out the following information to help us locate the issue.
|
||||
|
||||
在报告问题之前,请确保你的软件已经更新到最新Beta版本,否则我们可能会无条件直接关闭该Issue,感谢配合!
|
||||
Before reporting the issue, please make sure your software has been updated to the latest Beta version. Otherwise, we may unconditionally close this Issue without any further notice. Thank you for your cooperation!
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
@@ -33,7 +36,7 @@ body:
|
||||
id: steps
|
||||
attributes:
|
||||
label: 复现步骤 | Steps to Reproduce
|
||||
description: 如何复现该问题?如有必要可附截图/录屏 | How to reproduce this bug? Screenshots/recordings if needed
|
||||
description: 如何复现该问题?如有必要可附截图/录屏/触发该问题的文件 | How to reproduce this bug? Screenshots/recordings/specific files if needed
|
||||
placeholder: |
|
||||
1.
|
||||
2.
|
||||
@@ -51,6 +54,6 @@ body:
|
||||
id: extra
|
||||
attributes:
|
||||
label: 其他补充信息 | Additional Info
|
||||
description: 其他相关信息(如日志、配置、特殊环境等)| Any other context, logs, configs, special environment, etc.
|
||||
description: 其他相关信息(如日志文件、崩溃日志文件、配置文件、特殊环境等)| Any other context, logs, crash logs, configs, special environment, etc.
|
||||
validations:
|
||||
required: false
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
name: 功能请求 | Feature Request
|
||||
description: 提出你对本项目的功能建议 | Suggest an idea for this project
|
||||
labels: [enhancement]
|
||||
type: Feature
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
name: (Markdown Template) Bug 报告 | Bug Report
|
||||
about: 反馈软件缺陷或异常 | Report a bug to help us improve
|
||||
title: "[Version x.x.x] <your title>"
|
||||
type: Bug
|
||||
---
|
||||
|
||||
<!---请注意,你正在使用Markdown格式的Issue模板,如果你删除该模板的框架、更改问题的tag/类型/受理人或者不按照规范填写,你的Issue可能被直接关闭,如果你对Markdown不熟悉,请使用位于该选项下方的反馈入口继续反馈,感谢配合!-->
|
||||
|
||||
<!---感谢你的反馈!请详细填写以下内容,便于我们定位问题。-->
|
||||
<!---Thank you for your feedback! Please fill out the following information to help us locate the issue.-->
|
||||
|
||||
<!---在报告问题之前,请确保你的软件已经更新到最新Beta版本,否则我们可能会无条件直接关闭该Issue,感谢配合!-->
|
||||
<!---Before reporting the issue, please make sure your software has been updated to the latest Beta version. Otherwise, we may unconditionally close this Issue without any further notice. Thank you for your cooperation!-->
|
||||
|
||||
### 软件版本 | App Version (必填 | Required)
|
||||
<!---可在设置中的"关于"界面查看 | You can find it on the "About" interface in the settings-->
|
||||
<!---例如 v1.2.3 | e.g. v1.2.3-->
|
||||
|
||||
### 操作系统及版本 | OS & Version (必填 | Required)
|
||||
<!---例如 Windows 10 22H2 64位 | e.g. Windows 10 22H2 64bit-->
|
||||
|
||||
### 问题描述 | Description (必填 | Required)
|
||||
<!---简要描述遇到的问题 | Briefly describe the problem-->
|
||||
|
||||
### 复现步骤 | Steps to Reproduce (必填 | Required)
|
||||
<!---如何复现该问题?如有必要可附截图/录屏/触发该问题的文件 | How to reproduce this bug? Screenshots/recordings/specific files if needed-->
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
### 期望结果 | Expected Behavior (必填 | Required)
|
||||
<!---你期望的正确行为或结果 | What did you expect to happen?-->
|
||||
|
||||
### 其他补充信息 | Additional Info (可选 | Optional)
|
||||
<!---其他相关信息(如日志文件、崩溃日志文件、配置文件、特殊环境等)| Any other context, logs, crash logs, configs, special environment, etc.-->
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: (Markdown Template) 功能请求 | Feature Request
|
||||
about: 提出你对本项目的功能建议 | Suggest an idea for this project
|
||||
title: "[Feature Request] "
|
||||
type: Feature
|
||||
---
|
||||
|
||||
<!---请注意,你正在使用Markdown格式的Issue模板,如果你删除该模板的框架、更改问题的tag/类型/受理人或者不按照规范填写,你的Issue可能被直接关闭,如果你对Markdown不熟悉,请使用位于该选项下方的反馈入口继续反馈,感谢配合!-->
|
||||
|
||||
<!---感谢你的建议!请详细描述你的需求。-->
|
||||
<!---Thank you for your suggestion! Please describe your needs in detail.-->
|
||||
|
||||
### 功能描述 | Description (必填 | Required)
|
||||
<!---请描述你希望添加的功能 | Describe the feature you want-->
|
||||
|
||||
### 需求动机 | Motivation (必填 | Required)
|
||||
<!---为什么需要这个功能?| Why do you need this feature?-->
|
||||
|
||||
### 期望设计 | Expected Design (可选 | Optional)
|
||||
<!---描述或画出你期望的界面或交互 | Describe or sketch the expected UI/UX-->
|
||||
|
||||
### 其他补充信息 | Additional Info (可选 | Optional)
|
||||
<!---其他补充说明或建议 | Any other context or suggestions-->
|
||||
@@ -1,4 +1,4 @@
|
||||
name: .NET Build & PR Check
|
||||
name: .NET Build & Package
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -8,15 +8,6 @@ on:
|
||||
branches: [ main, beta ]
|
||||
paths-ignore:
|
||||
- '**/*.md'
|
||||
- 'docs/**'
|
||||
- 'Images/**'
|
||||
- 'Manual.md'
|
||||
- 'README.md'
|
||||
- 'UpdateLog.md'
|
||||
- 'CODE_OF_CONDUCT.md'
|
||||
- 'LICENSE'
|
||||
- 'privacy.txt'
|
||||
- 'icc.png'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
@@ -25,191 +16,44 @@ concurrency:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
find-or-create-pr-comment:
|
||||
name: Find or Create PR Comment
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
outputs:
|
||||
comment_id: ${{ steps.find-comment.outputs.comment_id }}
|
||||
steps:
|
||||
- name: Find existing bot comment
|
||||
id: find-comment
|
||||
run: |
|
||||
# 查找包含特定标记的现有评论
|
||||
COMMENTS=$(gh api \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
"/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" \
|
||||
--jq '.[] | select(.body | contains("<!-- github-action-pr-build -->")) | .id' | head -1)
|
||||
|
||||
if [ -n "$COMMENTS" ]; then
|
||||
echo "📝 找到现有评论 ID: $COMMENTS"
|
||||
echo "comment_id=$COMMENTS" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "📝 未找到现有评论,将创建新评论"
|
||||
echo "comment_id=" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
pr-preview-comment:
|
||||
name: PR Preview (Building)
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
needs: find-or-create-pr-comment
|
||||
permissions:
|
||||
pull-requests: write
|
||||
outputs:
|
||||
comment_id: ${{ steps.create-preview-comment.outputs.comment-id || '' }}
|
||||
steps:
|
||||
- name: Prepare Preview Comment
|
||||
id: prepare-preview
|
||||
env:
|
||||
GH_REPO: ${{ github.repository }}
|
||||
GH_RUN_ID: ${{ github.run_id }}
|
||||
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
IS_READY_FOR_REVIEW: ${{ github.event.action == 'ready_for_review' }}
|
||||
run: |
|
||||
# 构建预览评论内容
|
||||
{
|
||||
if [ "$IS_READY_FOR_REVIEW" = "true" ]; then
|
||||
echo "# 🚀 PR 准备审查 - 构建预览"
|
||||
echo ""
|
||||
echo "**状态:** 🟡 正在构建(准备审查状态)..."
|
||||
else
|
||||
echo "# 🏗️ PR 构建预览"
|
||||
echo ""
|
||||
echo "**状态:** 🟡 正在构建..."
|
||||
fi
|
||||
|
||||
echo "**分支提交:** \`$PR_HEAD_SHA\`"
|
||||
echo "**操作:** [查看运行详情](https://github.com/$GH_REPO/actions/runs/$GH_RUN_ID)"
|
||||
echo ""
|
||||
|
||||
if [ "$IS_READY_FOR_REVIEW" = "true" ]; then
|
||||
echo "> 📋 此 PR 已标记为 **准备审查**,正在进行构建验证"
|
||||
fi
|
||||
|
||||
echo "---"
|
||||
echo "<!-- github-action-pr-build -->"
|
||||
echo "<!-- build-id: $GH_RUN_ID -->"
|
||||
echo "<!-- event-type: ${{ github.event.action }} -->"
|
||||
echo "<!-- pr-head-sha: $PR_HEAD_SHA -->"
|
||||
echo "<!-- merge-sha: ${{ github.sha }} -->"
|
||||
echo ""
|
||||
echo "🤖 构建完成后,状态将自动更新"
|
||||
} > preview_comment.txt
|
||||
|
||||
preview_content=$(cat preview_comment.txt)
|
||||
echo "preview_body<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "$preview_content" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Post/Update Preview Comment
|
||||
id: create-preview-comment
|
||||
uses: peter-evans/create-or-update-comment@v5
|
||||
continue-on-error: true # 即使评论失败也继续执行
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-id: ${{ needs.find-or-create-pr-comment.outputs.comment_id }}
|
||||
body: ${{ steps.prepare-preview.outputs.preview_body }}
|
||||
edit-mode: replace
|
||||
|
||||
build-and-package:
|
||||
name: Build & Package
|
||||
runs-on: windows-latest
|
||||
outputs:
|
||||
archive_name: ${{ steps.create-archive.outputs.archive_name }}
|
||||
build_result: ${{ steps.check-exe.outputs.build_success }}
|
||||
artifact_url: ${{ steps.upload-artifact.outputs.artifact-url }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup NuGet
|
||||
uses: NuGet/setup-nuget@v2.0.1
|
||||
|
||||
|
||||
- name: Setup MSBuild
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
|
||||
- name: Cache NuGet global packages
|
||||
id: cache-nuget
|
||||
uses: actions/cache@v4
|
||||
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
path: |
|
||||
~/.nuget/packages
|
||||
~\AppData\Local\NuGet\Cache
|
||||
key: ${{ runner.os }}-nuget-v2-${{ hashFiles('**/*.csproj', '**/packages.config', '**/*.sln', '**/nuget.config') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-nuget-v2-
|
||||
|
||||
|
||||
|
||||
# Removed caching of obj/ folders to avoid cross-version incremental build issues
|
||||
# NuGet packages will still be cached; always run restore to ensure packages are present
|
||||
- name: Restore NuGet packages
|
||||
run: |
|
||||
Write-Host "📥 正在恢复 NuGet 包(始终运行以确保依赖可用)..." -ForegroundColor Yellow
|
||||
|
||||
# 恢复解决方案级别的包
|
||||
nuget restore "Ink Canvas.sln" -Verbosity minimal
|
||||
|
||||
# 恢复项目级别的包(兼容 packages.config)
|
||||
msbuild -t:restore "Ink Canvas/InkCanvasForClass.csproj" /p:GitFlow="Github Action" /p:RestorePackagesConfig=true /verbosity:minimal
|
||||
|
||||
Write-Host "✅ NuGet 包恢复完成" -ForegroundColor Green
|
||||
|
||||
dotnet-version: '10.x'
|
||||
cache: true
|
||||
cache-dependency-path: '**/packages.lock.json'
|
||||
|
||||
- name: Restore Package
|
||||
run: dotnet restore "Ink Canvas.sln" --locked-mode
|
||||
|
||||
- name: Build the Solution
|
||||
run: |
|
||||
Write-Host "🔨 正在构建项目..." -ForegroundColor Cyan
|
||||
|
||||
# 如果是 ready_for_review 事件,添加特殊标记
|
||||
if ("${{ github.event.action }}" -eq "ready_for_review") {
|
||||
Write-Host "🚀 PR 准备审查状态构建 - 进行完整验证" -ForegroundColor Magenta
|
||||
$GITFLOW = "Github Action - Ready For Review"
|
||||
} else {
|
||||
$GITFLOW = "Github Action"
|
||||
}
|
||||
|
||||
# 执行构建
|
||||
msbuild /p:platform="AnyCPU" /p:configuration="Debug" /p:GitFlow="$GITFLOW" "Ink Canvas/InkCanvasForClass.csproj" /m /p:UseMultiToolTask=true /p:EnforceProcessCountAcrossBuilds=true /verbosity:minimal
|
||||
|
||||
Write-Host "🏗️ 构建命令执行完成" -ForegroundColor Cyan
|
||||
run: msbuild /p:platform="AnyCPU" /p:configuration="Debug" /p:GitFlow="$GITFLOW" "Ink Canvas/InkCanvasForClass.csproj" /m /p:UseMultiToolTask=true /p:EnforceProcessCountAcrossBuilds=true /verbosity:minimal
|
||||
|
||||
- name: Check if exe file is generated
|
||||
id: check-exe
|
||||
run: |
|
||||
Write-Host "🔍 检查是否生成可执行文件..." -ForegroundColor Cyan
|
||||
|
||||
$exePath = "Ink Canvas\bin\Debug\net472\InkCanvasForClass.exe"
|
||||
|
||||
if (Test-Path $exePath) {
|
||||
Write-Host "✅ 找到可执行文件: $exePath" -ForegroundColor Green
|
||||
$fileInfo = Get-Item $exePath
|
||||
Write-Host " 文件大小: $($fileInfo.Length) 字节" -ForegroundColor Gray
|
||||
Write-Host " 创建时间: $($fileInfo.CreationTime)" -ForegroundColor Gray
|
||||
echo "build_success=true" >> $env:GITHUB_OUTPUT
|
||||
} else {
|
||||
Write-Host "❌ 未找到可执行文件: $exePath" -ForegroundColor Red
|
||||
Write-Host " 检查目录内容:" -ForegroundColor Yellow
|
||||
if (Test-Path "Ink Canvas\bin\Debug\net472\") {
|
||||
Get-ChildItem "Ink Canvas\bin\Debug\net472\" -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
Write-Host " - $($_.Name)" -ForegroundColor Gray
|
||||
}
|
||||
} else {
|
||||
Write-Host " bin\Debug\net472 目录不存在" -ForegroundColor Red
|
||||
}
|
||||
echo "build_success=false" >> $env:GITHUB_OUTPUT
|
||||
|
||||
# 如果是直接触发,则抛出错误
|
||||
if ("${{ github.event_name }}" -eq "workflow_dispatch") {
|
||||
Write-Host "🚨 工作流手动触发 - 构建失败,抛出错误!" -ForegroundColor Red -BackgroundColor Black
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
@@ -221,179 +65,40 @@ jobs:
|
||||
GITHUB_SHA: ${{ github.sha }}
|
||||
GITHUB_RUN_NUMBER: ${{ github.run_number }}
|
||||
run: |
|
||||
# 使用合并提交的短哈希(因为构建使用的是合并后的代码)
|
||||
$shortSha = $env:GITHUB_SHA.Substring(0, 7)
|
||||
$version = "debug-$shortSha-$env:GITHUB_RUN_NUMBER"
|
||||
$archiveName = "InkCanvasForClass.CE.$version.zip"
|
||||
Write-Host "📦 正在创建归档包: $archiveName" -ForegroundColor Cyan
|
||||
Write-Host " 使用合并提交哈希: $env:GITHUB_SHA" -ForegroundColor Gray
|
||||
Compress-Archive -Path "Ink Canvas\bin\Debug\net472\*" -DestinationPath $archiveName -Force
|
||||
echo "archive_name=$archiveName" >> $env:GITHUB_OUTPUT
|
||||
Write-Host "✅ 已创建归档包: $archiveName" -ForegroundColor Green
|
||||
echo "archive_name=$version" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Upload Artifact (if build succeeded)
|
||||
id: upload-artifact
|
||||
if: steps.check-exe.outputs.build_success == 'true'
|
||||
uses: actions/upload-artifact@v4.5.0
|
||||
with:
|
||||
name: app-package
|
||||
path: "*.zip"
|
||||
name: InkCanvasForClass.CE.debug
|
||||
path: "Ink Canvas/bin/Debug/net472/*"
|
||||
|
||||
pr-check-comment:
|
||||
name: PR Check & Comment (Final)
|
||||
needs: [build-and-package, pr-preview-comment]
|
||||
if: always() && github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Prepare Final Comment Content
|
||||
id: prepare-final-comment
|
||||
- name: Create Summary
|
||||
if: always()
|
||||
shell: bash
|
||||
run: |
|
||||
# 从构建作业获取构建结果
|
||||
BUILD_SUCCESS="${{ needs.build-and-package.outputs.build_result }}"
|
||||
ARTIFACT_URL="${{ needs.build-and-package.outputs.artifact_url }}"
|
||||
echo "# Build Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# 使用 PR 分支的实际提交哈希
|
||||
PR_HEAD_SHA="${{ github.event.pull_request.head.sha }}"
|
||||
|
||||
# 确定构建状态
|
||||
if [ "$BUILD_SUCCESS" = "true" ]; then
|
||||
STATUS_ICON="✅"
|
||||
STATUS_TEXT="构建成功"
|
||||
COLOR="#00d26a"
|
||||
if [ "${{ steps.check-exe.outputs.build_success }}" = "true" ]; then
|
||||
echo "## ✅ Build Successful" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Commit:** \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Run:** #${{ github.run_number }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Version:** ${{ steps.create-archive.outputs.archive_name }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "[Download Artifact](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "[Nightly.link Download](https://nightly.link/${{ github.repository }}/actions/runs/${{ github.run_id }}/InkCanvasForClass.CE.debug.zip) \([GhProxy Fastly Mirror](https://cdn.gh-proxy.com/nightly.link/${{ github.repository }}/actions/runs/${{ github.run_id }}/InkCanvasForClass.CE.debug.zip) / [GhProxy Mirror](https://gh-proxy.com/nightly.link/${{ github.repository }}/actions/runs/${{ github.run_id }}/InkCanvasForClass.CE.debug.zip)\)" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
STATUS_ICON="❌"
|
||||
STATUS_TEXT="构建失败"
|
||||
COLOR="#f85149"
|
||||
echo "## ❌ Build Failed" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Event:** ${{ github.event_name }} (${{ github.event.action || 'N/A' }})" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Commit:** \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Run:** #${{ github.run_number }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Check build logs for details." >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# 检查是否是 ready_for_review 事件
|
||||
READY_FOR_REVIEW="${{ github.event.action == 'ready_for_review' }}"
|
||||
|
||||
# 构建最终评论内容
|
||||
{
|
||||
if [ "$READY_FOR_REVIEW" = "true" ]; then
|
||||
echo "# 🚀 PR 准备审查 - 构建结果"
|
||||
echo ""
|
||||
echo "> 📋 此 PR 已标记为 **准备审查**,构建验证完成"
|
||||
echo ""
|
||||
else
|
||||
echo "# 📋 构建结果摘要"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo "## 本次构建状态"
|
||||
echo ""
|
||||
echo "| 项目 | 结果 |"
|
||||
echo "|------|------|"
|
||||
echo "| 状态 | $STATUS_ICON **$STATUS_TEXT** |"
|
||||
echo "| 事件类型 | \`${{ github.event.action }}\` |"
|
||||
echo "| 分支提交 | \`$PR_HEAD_SHA\` |"
|
||||
echo "| 工作流 | [运行 #${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) |"
|
||||
|
||||
# 如果有构建产物,显示下载链接
|
||||
if [ "$BUILD_SUCCESS" = "true" ]; then
|
||||
NIGHTLY_LINK="https://hk.gh-proxy.com/https://nightly.link/${{ github.repository }}/actions/runs/${{ github.run_id }}/app-package.zip"
|
||||
echo ""
|
||||
echo "## 构建产物"
|
||||
if [ -n "$ARTIFACT_URL" ]; then
|
||||
echo "- 📦 [下载构建产物]($ARTIFACT_URL)"
|
||||
else
|
||||
echo "- 📦 [下载构建产物](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
|
||||
fi
|
||||
echo "- 🌙 [直链下载 (nightly.link)]($NIGHTLY_LINK)"
|
||||
|
||||
if [ "$READY_FOR_REVIEW" = "true" ]; then
|
||||
echo ""
|
||||
echo "## 🎉 审查建议"
|
||||
echo "- ✅ 构建验证通过,代码可以正常编译"
|
||||
echo "- 🔍 请进行代码审查"
|
||||
echo "- 🧪 建议测试构建产物功能"
|
||||
fi
|
||||
else
|
||||
if [ "$READY_FOR_REVIEW" = "true" ]; then
|
||||
echo ""
|
||||
echo "## ⚠️ 审查阻塞"
|
||||
echo "- ❌ 构建失败,需要修复后才能继续审查"
|
||||
echo "- 🔧 请检查构建错误并修复"
|
||||
echo "- 📝 修复后重新标记为准备审查"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "---"
|
||||
echo "<!-- github-action-pr-build -->"
|
||||
echo "<!-- build-id: ${{ github.run_id }} -->"
|
||||
echo "<!-- event-type: ${{ github.event.action }} -->"
|
||||
echo "<!-- pr-head-sha: $PR_HEAD_SHA -->"
|
||||
echo "<!-- merge-sha: ${{ github.sha }} -->"
|
||||
echo ""
|
||||
echo "<sub>🤖 GitHub Actions 自动生成 • 最后更新: $(date -u +'%Y-%m-%d %H:%M:%S UTC')</sub>"
|
||||
} > final_comment.txt
|
||||
|
||||
# 输出多行内容
|
||||
final_content=$(cat final_comment.txt)
|
||||
echo "final_body<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "$final_content" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
echo "最终评论内容已生成"
|
||||
|
||||
- name: Write Final Comment to GitHub Summary
|
||||
run: |
|
||||
echo "# 🚀 最终构建结果 (GitHub Actions Summary)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "> ⚠️ 注意:由于权限限制,评论可能无法发布到 PR。这里是在 GitHub Actions 中的构建结果:" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
cat final_comment.txt >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "---" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**工作流信息:**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- 事件类型: ${{ github.event_name }} (${{ github.event.action }})" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- 运行编号: #${{ github.run_number }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- 运行 ID: ${{ github.run_id }}" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Update Final Comment
|
||||
uses: peter-evans/create-or-update-comment@v5
|
||||
continue-on-error: true # 即使评论失败也继续执行
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-id: ${{ needs.pr-preview-comment.outputs.comment_id }}
|
||||
body: ${{ steps.prepare-final-comment.outputs.final_body }}
|
||||
edit-mode: replace
|
||||
|
||||
final-check:
|
||||
name: Final Check (Manual Trigger)
|
||||
if: always() && github.event_name == 'workflow_dispatch'
|
||||
needs: [build-and-package]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Build Result
|
||||
id: check-build
|
||||
run: |
|
||||
BUILD_SUCCESS="${{ needs.build-and-package.outputs.build_result }}"
|
||||
|
||||
if [ "$BUILD_SUCCESS" = "true" ]; then
|
||||
echo "✅ 构建成功 - 工作流完成"
|
||||
echo "status=success" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "❌ 构建失败 - 抛出错误"
|
||||
echo "status=failure" >> $GITHUB_OUTPUT
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Create Summary (if successful)
|
||||
if: steps.check-build.outputs.status == 'success'
|
||||
run: |
|
||||
echo "# 🎉 手动构建完成" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**构建状态:** ✅ 成功" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**提交:** \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**运行编号:** #${{ github.run_number }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "[📦 下载构建产物](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**直链下载 (nightly.link):**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "[🌙 nightly.link 下载链接](https://hk.gh-proxy.com/https://nightly.link/${{ github.repository }}/actions/runs/${{ github.run_id }}/app-package.zip)" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -204,78 +204,59 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- name: Setup NuGet
|
||||
uses: NuGet/setup-nuget@v2.0.1
|
||||
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup MSBuild
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: '10.x'
|
||||
cache: true
|
||||
cache-dependency-path: '**/packages.lock.json'
|
||||
|
||||
- name: Restore Package
|
||||
run: dotnet restore "Ink Canvas.sln" --locked-mode
|
||||
|
||||
- name: Build the Solution (Release)
|
||||
run: |
|
||||
msbuild /p:platform="AnyCPU" /p:configuration="Release" /p:GitFlow="Github Action" "Ink Canvas/InkCanvasForClass.csproj" /m /p:UseMultiToolTask=true /p:EnforceProcessCountAcrossBuilds=true /verbosity:minimal
|
||||
|
||||
- name: Check if exe file is generated
|
||||
id: check-exe
|
||||
run: |
|
||||
$exePath = "Ink Canvas/bin/Release/net472/InkCanvasForClass.exe"
|
||||
|
||||
if (Test-Path $exePath) {
|
||||
echo "build_success=true" >> $env:GITHUB_OUTPUT
|
||||
} else {
|
||||
echo "build_success=false" >> $env:GITHUB_OUTPUT
|
||||
exit 1
|
||||
}
|
||||
|
||||
- name: Install Inno Setup Unofficial Language Files
|
||||
if: steps.check-exe.outputs.build_success == 'true'
|
||||
run: |
|
||||
# 创建临时目录用于下载文件
|
||||
New-Item -ItemType Directory -Path "temp_lang" -Force
|
||||
|
||||
# 下载英语英国版语言文件
|
||||
Invoke-WebRequest -Uri "https://github.com/jrsoftware/issrc/raw/refs/heads/main/Files/Languages/Unofficial/EnglishBritish.isl" -OutFile "temp_lang\EnglishBritish.isl"
|
||||
Invoke-WebRequest -Uri "https://github.com/jrsoftware/issrc/raw/refs/heads/main/Files/Languages/Unofficial/EnglishBritish.isl" -OutFile "temp_lang/EnglishBritish.isl"
|
||||
|
||||
# 下载简体中文版语言文件
|
||||
Invoke-WebRequest -Uri "https://github.com/jrsoftware/issrc/raw/refs/heads/main/Files/Languages/Unofficial/ChineseSimplified.isl" -OutFile "temp_lang\ChineseSimplified.isl"
|
||||
Invoke-WebRequest -Uri "https://github.com/jrsoftware/issrc/raw/refs/heads/main/Files/Languages/Unofficial/ChineseSimplified.isl" -OutFile "temp_lang/ChineseSimplified.isl"
|
||||
|
||||
# 将文件移动到 Inno Setup 的语言目录
|
||||
Move-Item -Path "temp_lang\EnglishBritish.isl" -Destination "C:\Program Files (x86)\Inno Setup 6\Languages\EnglishBritish.isl" -Force
|
||||
Move-Item -Path "temp_lang\ChineseSimplified.isl" -Destination "C:\Program Files (x86)\Inno Setup 6\Languages\ChineseSimplified.isl" -Force
|
||||
Move-Item -Path "temp_lang/EnglishBritish.isl" -Destination "C:/Program Files (x86)/Inno Setup 6/Languages/EnglishBritish.isl" -Force
|
||||
Move-Item -Path "temp_lang/ChineseSimplified.isl" -Destination "C:/Program Files (x86)/Inno Setup 6/Languages/ChineseSimplified.isl" -Force
|
||||
|
||||
# 清理临时目录
|
||||
Remove-Item -Path "temp_lang" -Recurse -Force
|
||||
|
||||
Write-Host "✅ Inno Setup unofficial language files installed successfully" -ForegroundColor Green
|
||||
|
||||
- name: Cache NuGet packages and obj files
|
||||
id: cache-nuget
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
# NuGet 全局包缓存(已下载的包)
|
||||
~/.nuget/packages
|
||||
# NuGet 缓存目录(包索引)
|
||||
~\AppData\Local\NuGet\Cache
|
||||
# 项目 obj 目录(包含 project.assets.json)
|
||||
Ink Canvas/obj/
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/packages.config', '**/*.sln') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-nuget-
|
||||
|
||||
- name: Restore NuGet packages (if cache missed)
|
||||
if: steps.cache-nuget.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
Write-Host "📥 缓存未命中,正在恢复 NuGet 包..." -ForegroundColor Yellow
|
||||
|
||||
# 恢复解决方案级别的包
|
||||
nuget restore "Ink Canvas.sln" -Verbosity minimal
|
||||
|
||||
# 恢复项目级别的包
|
||||
msbuild -t:restore "Ink Canvas/InkCanvasForClass.csproj" /p:GitFlow="Github Action" /p:RestorePackagesConfig=true /verbosity:minimal
|
||||
|
||||
Write-Host "✅ NuGet 包恢复完成" -ForegroundColor Green
|
||||
|
||||
- name: Display version info
|
||||
run: |
|
||||
echo "Building version: ${{ needs.prepare.outputs.version }}"
|
||||
echo "Tag: ${{ needs.prepare.outputs.tag_name }}"
|
||||
echo "Release type: ${{ needs.prepare.outputs.is_prerelease == 'true' && 'Pre-release' || 'Release' }}"
|
||||
|
||||
- name: Build the Solution
|
||||
run: |
|
||||
Write-Host "🔨 正在构建项目..." -ForegroundColor Cyan
|
||||
msbuild /p:platform="AnyCPU" /p:configuration="Release" /p:GitFlow="Github Action" "Ink Canvas/InkCanvasForClass.csproj" /m /p:UseMultiToolTask=true /p:EnforceProcessCountAcrossBuilds=true /verbosity:minimal
|
||||
|
||||
Write-Host "🏗️ 构建命令执行完成" -ForegroundColor Cyan
|
||||
|
||||
- name: Create Release Archive
|
||||
id: create_archive
|
||||
if: steps.check-exe.outputs.build_success == 'true'
|
||||
run: |
|
||||
$version = "${{ needs.prepare.outputs.version }}"
|
||||
$archiveName = "InkCanvasForClass.CE.$version.zip"
|
||||
@@ -284,51 +265,50 @@ jobs:
|
||||
New-Item -ItemType Directory -Path "release" -Force
|
||||
|
||||
# 复制发布文件
|
||||
Copy-Item "Ink Canvas\bin\Release\net472\*" "release\" -Recurse -Force
|
||||
Copy-Item "Ink Canvas/bin/Release/net472/*" "release/" -Recurse -Force
|
||||
|
||||
# 创建压缩包
|
||||
Compress-Archive -Path "release\*" -DestinationPath $archiveName -Force
|
||||
Compress-Archive -Path "release/*" -DestinationPath $archiveName -Force
|
||||
|
||||
echo "archive_name=$archiveName" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Prepare Inno Setup script
|
||||
if: steps.check-exe.outputs.build_success == 'true'
|
||||
run: |
|
||||
$version = "${{ needs.prepare.outputs.version }}"
|
||||
|
||||
# 更新 ISS 文件中的版本信息
|
||||
$issPath = "build\InkCanvasForClass CE.iss"
|
||||
$issPath = "build/InkCanvasForClass CE.iss"
|
||||
$issContent = Get-Content -Path $issPath -Raw
|
||||
|
||||
# 替换版本信息
|
||||
$issContent = $issContent -replace '#define MyAppVersion ".*"', "#define MyAppVersion `"$version`""
|
||||
|
||||
# 替换源文件路径为相对路径(考虑到ISS文件在build目录下,需要返回上级目录)
|
||||
$issContent = $issContent -replace 'Source: ".*\\{#MyAppExeName}";', 'Source: "..\release\{#MyAppExeName}";'
|
||||
$issContent = $issContent -replace 'Source: ".*\\InkCanvasForClass.exe.config";', 'Source: "..\release\InkCanvasForClass.exe.config";'
|
||||
$issContent = $issContent -replace 'Source: ".*\\{#MyAppExeName}";', 'Source: "../release/{#MyAppExeName}";'
|
||||
$issContent = $issContent -replace 'Source: ".*\\InkCanvasForClass.exe.config";', 'Source: "../release/InkCanvasForClass.exe.config";'
|
||||
|
||||
# 更新输出目录为当前目录
|
||||
$issContent = $issContent -replace 'OutputDir=.*', 'OutputDir=.'
|
||||
|
||||
# 更新默认安装目录
|
||||
$issContent = $issContent -replace 'DefaultDirName=.*', 'DefaultDirName={autopf}\{#MyAppName}'
|
||||
$issContent = $issContent -replace 'DefaultDirName=.*', 'DefaultDirName={autopf}/{#MyAppName}'
|
||||
|
||||
# 更新许可证文件路径为相对路径(考虑到ISS文件在build目录下,需要返回上级目录)
|
||||
$issContent = $issContent -replace 'LicenseFile=.*', 'LicenseFile=..\LICENSE'
|
||||
$issContent = $issContent -replace 'LicenseFile=.*', 'LicenseFile=../LICENSE'
|
||||
|
||||
# 保存修改后的 ISS 文件
|
||||
$issContent | Set-Content -Path $issPath -Encoding UTF8
|
||||
|
||||
# 显示修改后的 ISS 文件内容
|
||||
Write-Host "Modified ISS file content:"
|
||||
Write-Host $issContent
|
||||
|
||||
- name: Build MSI installer with Inno Setup
|
||||
if: steps.check-exe.outputs.build_success == 'true'
|
||||
uses: Minionguyjpro/Inno-Setup-Action@v1.2.2
|
||||
with:
|
||||
path: build\InkCanvasForClass CE.iss
|
||||
path: build/InkCanvasForClass CE.iss
|
||||
options: /O.
|
||||
|
||||
- name: Rename installer file
|
||||
if: steps.check-exe.outputs.build_success == 'true'
|
||||
run: |
|
||||
$version = "${{ needs.prepare.outputs.version }}"
|
||||
$setupFile = "InkCanvasForClass CE Setup.exe"
|
||||
@@ -336,13 +316,13 @@ jobs:
|
||||
|
||||
if (Test-Path $setupFile) {
|
||||
Rename-Item -Path $setupFile -NewName $newSetupName
|
||||
Write-Host "Renamed setup file to: $newSetupName"
|
||||
} else {
|
||||
Write-Host "Setup file not found: $setupFile"
|
||||
Write-Error "Setup file not found: $setupFile"
|
||||
}
|
||||
|
||||
- name: Calculate archive size
|
||||
id: calculate_size
|
||||
if: steps.check-exe.outputs.build_success == 'true'
|
||||
run: |
|
||||
$version = "${{ needs.prepare.outputs.version }}"
|
||||
$archiveName = "InkCanvasForClass.CE.$version.zip"
|
||||
@@ -352,10 +332,9 @@ jobs:
|
||||
|
||||
echo "zip_size=$fileSize" >> $env:GITHUB_OUTPUT
|
||||
|
||||
echo "Archive size: $fileSize bytes"
|
||||
|
||||
- name: Calculate installer size
|
||||
id: calculate_installer_size
|
||||
if: steps.check-exe.outputs.build_success == 'true'
|
||||
run: |
|
||||
$version = "${{ needs.prepare.outputs.version }}"
|
||||
$installerName = "InkCanvasForClass.CE.$version.Setup.exe"
|
||||
@@ -365,13 +344,12 @@ jobs:
|
||||
$fileSize = (Get-Item $installerName).Length
|
||||
|
||||
echo "installer_size=$fileSize" >> $env:GITHUB_OUTPUT
|
||||
|
||||
echo "Installer size: $fileSize bytes"
|
||||
} else {
|
||||
echo "Installer file not found: $installerName"
|
||||
Write-Error "Installer file not found: $installerName"
|
||||
}
|
||||
|
||||
- name: Upload Build Artifacts
|
||||
if: steps.check-exe.outputs.build_success == 'true'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-files-${{ needs.prepare.outputs.version }}
|
||||
@@ -379,13 +357,50 @@ jobs:
|
||||
InkCanvasForClass.CE.${{ needs.prepare.outputs.version }}.zip
|
||||
InkCanvasForClass.CE.${{ needs.prepare.outputs.version }}.Setup.exe
|
||||
|
||||
- name: Create Build Summary
|
||||
if: always()
|
||||
shell: bash
|
||||
run: |
|
||||
echo "# Release Build Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [ "${{ steps.check-exe.outputs.build_success }}" = "true" ]; then
|
||||
echo "## ✅ Release Build Successful" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Version:** ${{ needs.prepare.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Tag:** \`${{ needs.prepare.outputs.tag_name }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Release Type:** ${{ needs.prepare.outputs.is_prerelease == 'true' && 'Pre-release' || 'Release' }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Commit:** \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Run:** #${{ github.run_number }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [ -n "${{ steps.calculate_size.outputs.zip_size }}" ]; then
|
||||
echo "**Archive Size:** ${{ steps.calculate_size.outputs.zip_size }} bytes" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [ -n "${{ steps.calculate_installer_size.outputs.installer_size }}" ]; then
|
||||
echo "**Installer Size:** ${{ steps.calculate_installer_size.outputs.installer_size }} bytes" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
else
|
||||
echo "## ❌ Release Build Failed" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Version:** ${{ needs.prepare.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Tag:** \`${{ needs.prepare.outputs.tag_name }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Event:** ${{ github.event_name }} (${{ github.event.action || 'N/A' }})" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Commit:** \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Run:** #${{ github.run_number }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Check build logs for details." >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
sign:
|
||||
needs: [prepare, build]
|
||||
if: success()
|
||||
runs-on: ubuntu-latest # 改为 Ubuntu 以使用 Python 签名工具
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write # 需要这个权限来验证签名
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Download Build Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -505,4 +520,4 @@ jobs:
|
||||
InkCanvasForClass.CE.${{ needs.prepare.outputs.version }}.Setup.exe.sigstore.json
|
||||
fail_on_unmatched_files: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
+5
-1
@@ -425,4 +425,8 @@ FodyWeavers.xsd
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
*.msp
|
||||
|
||||
# Telemetry DSN configuration file (contains sensitive information)
|
||||
telemetry_dsn.txt
|
||||
**/telemetry_dsn.txt
|
||||
+141
-22
@@ -20,6 +20,7 @@ using Application = System.Windows.Application;
|
||||
using MessageBox = System.Windows.MessageBox;
|
||||
using SplashScreen = Ink_Canvas.Windows.SplashScreen;
|
||||
using Timer = System.Threading.Timer;
|
||||
using Sentry;
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
@@ -72,6 +73,26 @@ namespace Ink_Canvas
|
||||
{
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var dsn = GetDlassTelemetryDsn();
|
||||
if (!string.IsNullOrWhiteSpace(dsn))
|
||||
{
|
||||
SentrySdk.Init(options =>
|
||||
{
|
||||
options.Dsn = dsn;
|
||||
options.Debug = false;
|
||||
options.SendDefaultPii = true;
|
||||
options.TracesSampleRate = 1.0;
|
||||
options.IsGlobalModeEnabled = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"初始化 Dlass 遥测失败: {ex}", LogHelper.LogType.Warning);
|
||||
}
|
||||
|
||||
// 配置TLS协议以支持Windows 7
|
||||
ConfigureTlsForWindows7();
|
||||
|
||||
@@ -642,8 +663,8 @@ namespace Ink_Canvas
|
||||
|
||||
async void App_Startup(object sender, StartupEventArgs e)
|
||||
{
|
||||
// 初始化应用启动时间
|
||||
appStartTime = DateTime.Now;
|
||||
appStartupStartTime = DateTime.Now;
|
||||
|
||||
// 根据设置决定是否显示启动画面
|
||||
if (ShouldShowSplashScreen())
|
||||
@@ -929,6 +950,22 @@ namespace Ink_Canvas
|
||||
LogHelper.WriteLogToFile("通过IPC发送展开浮动栏命令失败", LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
// 检查是否有URI参数
|
||||
else if (e.Args.Any(a => a.StartsWith("icc:", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
string uriArg = e.Args.FirstOrDefault(a => a.StartsWith("icc:", StringComparison.OrdinalIgnoreCase));
|
||||
LogHelper.WriteLogToFile($"检测到已运行实例且有URI参数: {uriArg}", LogHelper.LogType.Event);
|
||||
|
||||
// 尝试通过IPC发送URI命令给已运行实例
|
||||
if (FileAssociationManager.TrySendUriCommandToExistingInstance(uriArg))
|
||||
{
|
||||
LogHelper.WriteLogToFile("URI命令已通过IPC发送给已运行实例", LogHelper.LogType.Event);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile("通过IPC发送URI命令失败", LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile("检测到已运行实例,但无文件参数", LogHelper.LogType.Event);
|
||||
@@ -1021,6 +1058,21 @@ namespace Ink_Canvas
|
||||
|
||||
mainWindow.Show();
|
||||
|
||||
// 处理启动时的URI参数
|
||||
string startupUriArg = e.Args.FirstOrDefault(a => a.StartsWith("icc:", StringComparison.OrdinalIgnoreCase));
|
||||
if (!string.IsNullOrEmpty(startupUriArg))
|
||||
{
|
||||
LogHelper.WriteLogToFile($"App | 处理启动URI参数: {startupUriArg}", LogHelper.LogType.Event);
|
||||
// 延迟一点执行,确保窗口初始化完成
|
||||
Task.Delay(1000).ContinueWith(_ =>
|
||||
{
|
||||
mainWindow.Dispatcher.Invoke(() =>
|
||||
{
|
||||
mainWindow.HandleUriCommand(startupUriArg);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 注册.icstk文件关联
|
||||
try
|
||||
{
|
||||
@@ -1078,40 +1130,42 @@ namespace Ink_Canvas
|
||||
private static bool isStartupComplete = false;
|
||||
private static DateTime startupCompleteHeartbeat = DateTime.MinValue;
|
||||
private static DateTime splashScreenStartTime = DateTime.MinValue;
|
||||
private static DateTime appStartupStartTime = DateTime.MinValue;
|
||||
|
||||
private void StartHeartbeatMonitor()
|
||||
{
|
||||
heartbeatTimer = new Timer(_ => lastHeartbeat = DateTime.Now, null, 0, 1000);
|
||||
watchdogTimer = new Timer(_ =>
|
||||
{
|
||||
if (_isSplashScreenShown && splashScreenStartTime != DateTime.MinValue)
|
||||
if (!isStartupComplete && appStartupStartTime != DateTime.MinValue)
|
||||
{
|
||||
if (!isStartupComplete)
|
||||
DateTime startTime = _isSplashScreenShown && splashScreenStartTime != DateTime.MinValue
|
||||
? splashScreenStartTime
|
||||
: appStartupStartTime;
|
||||
TimeSpan elapsedSinceStart = DateTime.Now - startTime;
|
||||
if (elapsedSinceStart.TotalMinutes >= 2)
|
||||
{
|
||||
TimeSpan elapsedSinceSplashStart = DateTime.Now - splashScreenStartTime;
|
||||
if (elapsedSinceSplashStart.TotalMinutes >= 2)
|
||||
string timeType = _isSplashScreenShown ? "启动画面已显示" : "应用启动开始";
|
||||
LogHelper.WriteLogToFile($"检测到启动假死:{timeType}{elapsedSinceStart.TotalMinutes:F2}分钟,但未收到启动完成心跳,自动重启。", LogHelper.LogType.Error);
|
||||
SyncCrashActionFromSettings();
|
||||
if (CrashAction == CrashActionType.SilentRestart)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"检测到启动假死:启动画面已显示{elapsedSinceSplashStart.TotalMinutes:F2}分钟,但未收到启动完成心跳,自动重启。", LogHelper.LogType.Error);
|
||||
SyncCrashActionFromSettings();
|
||||
if (CrashAction == CrashActionType.SilentRestart)
|
||||
StartupCount.Increment();
|
||||
if (StartupCount.GetCount() >= 5)
|
||||
{
|
||||
StartupCount.Increment();
|
||||
if (StartupCount.GetCount() >= 5)
|
||||
{
|
||||
MessageBox.Show("检测到程序已连续重启5次,已停止自动重启。请联系开发者或检查系统环境。", "重启次数过多", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
StartupCount.Reset();
|
||||
Environment.Exit(1);
|
||||
}
|
||||
try
|
||||
{
|
||||
string exePath = Process.GetCurrentProcess().MainModule.FileName;
|
||||
Process.Start(exePath);
|
||||
}
|
||||
catch { }
|
||||
MessageBox.Show("检测到程序已连续重启5次,已停止自动重启。请联系开发者或检查系统环境。", "重启次数过多", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
StartupCount.Reset();
|
||||
Environment.Exit(1);
|
||||
}
|
||||
return;
|
||||
try
|
||||
{
|
||||
string exePath = Process.GetCurrentProcess().MainModule.FileName;
|
||||
Process.Start(exePath);
|
||||
}
|
||||
catch { }
|
||||
Environment.Exit(1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1205,6 +1259,71 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GetDlassTelemetryDsn()
|
||||
{
|
||||
try
|
||||
{
|
||||
var envDsn = Environment.GetEnvironmentVariable("DLASS_SENTRY_DSN");
|
||||
if (!string.IsNullOrWhiteSpace(envDsn))
|
||||
{
|
||||
return envDsn;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var resourceName = "Ink_Canvas.telemetry_dsn.txt";
|
||||
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
|
||||
{
|
||||
if (stream != null)
|
||||
{
|
||||
using (StreamReader reader = new StreamReader(stream, System.Text.Encoding.UTF8))
|
||||
{
|
||||
string dsn = reader.ReadToEnd().Trim();
|
||||
if (!string.IsNullOrWhiteSpace(dsn))
|
||||
{
|
||||
return dsn;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"从程序集资源读取遥测 DSN 失败: {ex.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
|
||||
string assemblyLocation = Assembly.GetExecutingAssembly().Location;
|
||||
string currentDir = Path.GetDirectoryName(assemblyLocation);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
string dsnFilePath = Path.Combine(currentDir, "telemetry_dsn.txt");
|
||||
if (File.Exists(dsnFilePath))
|
||||
{
|
||||
string dsn = File.ReadAllText(dsnFilePath, System.Text.Encoding.UTF8).Trim();
|
||||
if (!string.IsNullOrWhiteSpace(dsn))
|
||||
{
|
||||
return dsn;
|
||||
}
|
||||
}
|
||||
|
||||
DirectoryInfo parentDir = Directory.GetParent(currentDir);
|
||||
if (parentDir == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
currentDir = parentDir.FullName;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private void App_Exit(object sender, ExitEventArgs e)
|
||||
{
|
||||
// 仅在软件内主动退出时关闭看门狗,并写入退出信号
|
||||
|
||||
@@ -49,5 +49,5 @@ using System.Windows;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.7.18.5")]
|
||||
[assembly: AssemblyFileVersion("1.7.18.5")]
|
||||
[assembly: AssemblyVersion("1.7.18.6")]
|
||||
[assembly: AssemblyFileVersion("1.7.18.6")]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -46,20 +46,40 @@ namespace Ink_Canvas.Helpers
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. 尝试从主文件读取设备ID
|
||||
string deviceId = LoadDeviceIdFromFile(DeviceIdFilePath);
|
||||
if (!string.IsNullOrEmpty(deviceId))
|
||||
// 计算当前设备的硬件指纹
|
||||
string currentHardwareFingerprint = GenerateHardwareFingerprint();
|
||||
|
||||
// 1. 尝试从主文件读取设备ID及其硬件指纹
|
||||
var storedInfo = LoadDeviceIdFromFile(DeviceIdFilePath);
|
||||
if (storedInfo != null && !string.IsNullOrEmpty(storedInfo.DeviceId) && IsValidDeviceId(storedInfo.DeviceId))
|
||||
{
|
||||
LogHelper.WriteLogToFile($"DeviceIdentifier | 从主文件读取设备ID: {deviceId}");
|
||||
return deviceId;
|
||||
if (!string.IsNullOrEmpty(storedInfo.HardwareFingerprint))
|
||||
{
|
||||
if (storedInfo.HardwareFingerprint == currentHardwareFingerprint)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"DeviceIdentifier | 从主文件读取设备ID且硬件信息一致: {storedInfo.DeviceId}");
|
||||
return storedInfo.DeviceId;
|
||||
}
|
||||
|
||||
LogHelper.WriteLogToFile("DeviceIdentifier | 检测到当前硬件信息与保存的设备ID不一致,将重新生成设备ID");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile("DeviceIdentifier | 检测到旧格式设备ID文件(无硬件信息),将基于当前硬件重新生成设备ID并升级文件格式");
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 生成新的设备ID
|
||||
string newDeviceId = GenerateDeviceId();
|
||||
// 2. 基于当前硬件指纹生成新的设备ID
|
||||
string newDeviceId = GenerateDeviceIdFromFingerprint(currentHardwareFingerprint);
|
||||
LogHelper.WriteLogToFile($"DeviceIdentifier | 生成新设备ID: {newDeviceId}");
|
||||
|
||||
// 3. 保存到主文件
|
||||
SaveDeviceIdToFile(DeviceIdFilePath, newDeviceId);
|
||||
// 3. 保存到主文件(包含硬件指纹)
|
||||
var newInfo = new DeviceIdInfo
|
||||
{
|
||||
DeviceId = newDeviceId,
|
||||
HardwareFingerprint = currentHardwareFingerprint
|
||||
};
|
||||
SaveDeviceIdToFile(DeviceIdFilePath, newInfo);
|
||||
|
||||
return newDeviceId;
|
||||
}
|
||||
@@ -79,143 +99,9 @@ namespace Ink_Canvas.Helpers
|
||||
{
|
||||
try
|
||||
{
|
||||
// 收集硬件信息
|
||||
var hardwareInfo = new StringBuilder();
|
||||
|
||||
// 使用反射获取硬件信息,避免直接引用System.Management
|
||||
try
|
||||
{
|
||||
// 尝试加载System.Management程序集
|
||||
var assembly = Assembly.Load("System.Management");
|
||||
if (assembly != null)
|
||||
{
|
||||
// CPU信息
|
||||
try
|
||||
{
|
||||
var searcherType = assembly.GetType("System.Management.ManagementObjectSearcher");
|
||||
var searcher = Activator.CreateInstance(searcherType, "SELECT ProcessorId FROM Win32_Processor");
|
||||
var getMethod = searcherType.GetMethod("Get");
|
||||
var enumerator = getMethod.Invoke(searcher, null);
|
||||
|
||||
var moveNextMethod = enumerator.GetType().GetMethod("MoveNext");
|
||||
var currentProperty = enumerator.GetType().GetProperty("Current");
|
||||
|
||||
if ((bool)moveNextMethod.Invoke(enumerator, null))
|
||||
{
|
||||
var obj = currentProperty.GetValue(enumerator);
|
||||
var indexer = obj.GetType().GetProperty("Item", new[] { typeof(string) });
|
||||
var processorId = indexer.GetValue(obj, new object[] { "ProcessorId" });
|
||||
hardwareInfo.Append(processorId?.ToString() ?? "");
|
||||
}
|
||||
|
||||
var disposeMethod = searcher.GetType().GetMethod("Dispose");
|
||||
disposeMethod?.Invoke(searcher, null);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// 主板序列号
|
||||
try
|
||||
{
|
||||
var searcherType = assembly.GetType("System.Management.ManagementObjectSearcher");
|
||||
var searcher = Activator.CreateInstance(searcherType, "SELECT SerialNumber FROM Win32_BaseBoard");
|
||||
var getMethod = searcherType.GetMethod("Get");
|
||||
var enumerator = getMethod.Invoke(searcher, null);
|
||||
|
||||
var moveNextMethod = enumerator.GetType().GetMethod("MoveNext");
|
||||
var currentProperty = enumerator.GetType().GetProperty("Current");
|
||||
|
||||
if ((bool)moveNextMethod.Invoke(enumerator, null))
|
||||
{
|
||||
var obj = currentProperty.GetValue(enumerator);
|
||||
var indexer = obj.GetType().GetProperty("Item", new[] { typeof(string) });
|
||||
var serialNumber = indexer.GetValue(obj, new object[] { "SerialNumber" });
|
||||
hardwareInfo.Append(serialNumber?.ToString() ?? "");
|
||||
}
|
||||
|
||||
var disposeMethod = searcher.GetType().GetMethod("Dispose");
|
||||
disposeMethod?.Invoke(searcher, null);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// BIOS序列号
|
||||
try
|
||||
{
|
||||
var searcherType = assembly.GetType("System.Management.ManagementObjectSearcher");
|
||||
var searcher = Activator.CreateInstance(searcherType, "SELECT SerialNumber FROM Win32_BIOS");
|
||||
var getMethod = searcherType.GetMethod("Get");
|
||||
var enumerator = getMethod.Invoke(searcher, null);
|
||||
|
||||
var moveNextMethod = enumerator.GetType().GetMethod("MoveNext");
|
||||
var currentProperty = enumerator.GetType().GetProperty("Current");
|
||||
|
||||
if ((bool)moveNextMethod.Invoke(enumerator, null))
|
||||
{
|
||||
var obj = currentProperty.GetValue(enumerator);
|
||||
var indexer = obj.GetType().GetProperty("Item", new[] { typeof(string) });
|
||||
var serialNumber = indexer.GetValue(obj, new object[] { "SerialNumber" });
|
||||
hardwareInfo.Append(serialNumber?.ToString() ?? "");
|
||||
}
|
||||
|
||||
var disposeMethod = searcher.GetType().GetMethod("Dispose");
|
||||
disposeMethod?.Invoke(searcher, null);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// 主硬盘序列号
|
||||
try
|
||||
{
|
||||
var searcherType = assembly.GetType("System.Management.ManagementObjectSearcher");
|
||||
var searcher = Activator.CreateInstance(searcherType, "SELECT SerialNumber FROM Win32_DiskDrive WHERE MediaType='Fixed hard disk media'");
|
||||
var getMethod = searcherType.GetMethod("Get");
|
||||
var enumerator = getMethod.Invoke(searcher, null);
|
||||
|
||||
var moveNextMethod = enumerator.GetType().GetMethod("MoveNext");
|
||||
var currentProperty = enumerator.GetType().GetProperty("Current");
|
||||
|
||||
if ((bool)moveNextMethod.Invoke(enumerator, null))
|
||||
{
|
||||
var obj = currentProperty.GetValue(enumerator);
|
||||
var indexer = obj.GetType().GetProperty("Item", new[] { typeof(string) });
|
||||
var serialNumber = indexer.GetValue(obj, new object[] { "SerialNumber" });
|
||||
hardwareInfo.Append(serialNumber?.ToString() ?? "");
|
||||
}
|
||||
|
||||
var disposeMethod = searcher.GetType().GetMethod("Dispose");
|
||||
disposeMethod?.Invoke(searcher, null);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// 如果硬件信息不足,添加系统信息
|
||||
if (hardwareInfo.Length < 10)
|
||||
{
|
||||
hardwareInfo.Append(Environment.MachineName);
|
||||
hardwareInfo.Append(Environment.UserName);
|
||||
hardwareInfo.Append(Environment.OSVersion);
|
||||
}
|
||||
|
||||
// 生成哈希
|
||||
using (var sha256 = SHA256.Create())
|
||||
{
|
||||
byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(hardwareInfo.ToString()));
|
||||
string hashString = BitConverter.ToString(hashBytes).Replace("-", "");
|
||||
|
||||
// 取前25个字符,确保唯一性
|
||||
string deviceId = hashString.Substring(0, 25);
|
||||
|
||||
// 添加校验位(第25位)
|
||||
int checksum = 0;
|
||||
for (int i = 0; i < 24; i++)
|
||||
{
|
||||
checksum += Convert.ToInt32(deviceId[i]);
|
||||
}
|
||||
checksum %= 36; // 0-9, A-Z
|
||||
char checksumChar = checksum < 10 ? (char)(checksum + '0') : (char)(checksum - 10 + 'A');
|
||||
|
||||
return deviceId.Substring(0, 24) + checksumChar;
|
||||
}
|
||||
// 基于当前硬件指纹生成设备ID
|
||||
string hardwareFingerprint = GenerateHardwareFingerprint();
|
||||
return GenerateDeviceIdFromFingerprint(hardwareFingerprint);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -224,6 +110,157 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成当前设备的硬件指纹字符串(用于生成和校验设备ID)
|
||||
/// </summary>
|
||||
private static string GenerateHardwareFingerprint()
|
||||
{
|
||||
// 收集硬件信息
|
||||
var hardwareInfo = new StringBuilder();
|
||||
|
||||
try
|
||||
{
|
||||
var assembly = Assembly.Load("System.Management");
|
||||
if (assembly != null)
|
||||
{
|
||||
// CPU信息
|
||||
try
|
||||
{
|
||||
var searcherType = assembly.GetType("System.Management.ManagementObjectSearcher");
|
||||
var searcher = Activator.CreateInstance(searcherType, "SELECT ProcessorId FROM Win32_Processor");
|
||||
var getMethod = searcherType.GetMethod("Get");
|
||||
var enumerator = getMethod.Invoke(searcher, null);
|
||||
|
||||
var moveNextMethod = enumerator.GetType().GetMethod("MoveNext");
|
||||
var currentProperty = enumerator.GetType().GetProperty("Current");
|
||||
|
||||
if ((bool)moveNextMethod.Invoke(enumerator, null))
|
||||
{
|
||||
var obj = currentProperty.GetValue(enumerator);
|
||||
var indexer = obj.GetType().GetProperty("Item", new[] { typeof(string) });
|
||||
var processorId = indexer.GetValue(obj, new object[] { "ProcessorId" });
|
||||
hardwareInfo.Append(processorId?.ToString() ?? "");
|
||||
}
|
||||
|
||||
var disposeMethod = searcher.GetType().GetMethod("Dispose");
|
||||
disposeMethod?.Invoke(searcher, null);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// 主板序列号
|
||||
try
|
||||
{
|
||||
var searcherType = assembly.GetType("System.Management.ManagementObjectSearcher");
|
||||
var searcher = Activator.CreateInstance(searcherType, "SELECT SerialNumber FROM Win32_BaseBoard");
|
||||
var getMethod = searcherType.GetMethod("Get");
|
||||
var enumerator = getMethod.Invoke(searcher, null);
|
||||
|
||||
var moveNextMethod = enumerator.GetType().GetMethod("MoveNext");
|
||||
var currentProperty = enumerator.GetType().GetProperty("Current");
|
||||
|
||||
if ((bool)moveNextMethod.Invoke(enumerator, null))
|
||||
{
|
||||
var obj = currentProperty.GetValue(enumerator);
|
||||
var indexer = obj.GetType().GetProperty("Item", new[] { typeof(string) });
|
||||
var serialNumber = indexer.GetValue(obj, new object[] { "SerialNumber" });
|
||||
hardwareInfo.Append(serialNumber?.ToString() ?? "");
|
||||
}
|
||||
|
||||
var disposeMethod = searcher.GetType().GetMethod("Dispose");
|
||||
disposeMethod?.Invoke(searcher, null);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// BIOS序列号
|
||||
try
|
||||
{
|
||||
var searcherType = assembly.GetType("System.Management.ManagementObjectSearcher");
|
||||
var searcher = Activator.CreateInstance(searcherType, "SELECT SerialNumber FROM Win32_BIOS");
|
||||
var getMethod = searcherType.GetMethod("Get");
|
||||
var enumerator = getMethod.Invoke(searcher, null);
|
||||
|
||||
var moveNextMethod = enumerator.GetType().GetMethod("MoveNext");
|
||||
var currentProperty = enumerator.GetType().GetProperty("Current");
|
||||
|
||||
if ((bool)moveNextMethod.Invoke(enumerator, null))
|
||||
{
|
||||
var obj = currentProperty.GetValue(enumerator);
|
||||
var indexer = obj.GetType().GetProperty("Item", new[] { typeof(string) });
|
||||
var serialNumber = indexer.GetValue(obj, new object[] { "SerialNumber" });
|
||||
hardwareInfo.Append(serialNumber?.ToString() ?? "");
|
||||
}
|
||||
|
||||
var disposeMethod = searcher.GetType().GetMethod("Dispose");
|
||||
disposeMethod?.Invoke(searcher, null);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// 主硬盘序列号
|
||||
try
|
||||
{
|
||||
var searcherType = assembly.GetType("System.Management.ManagementObjectSearcher");
|
||||
var searcher = Activator.CreateInstance(searcherType, "SELECT SerialNumber FROM Win32_DiskDrive WHERE MediaType='Fixed hard disk media'");
|
||||
var getMethod = searcherType.GetMethod("Get");
|
||||
var enumerator = getMethod.Invoke(searcher, null);
|
||||
|
||||
var moveNextMethod = enumerator.GetType().GetMethod("MoveNext");
|
||||
var currentProperty = enumerator.GetType().GetProperty("Current");
|
||||
|
||||
if ((bool)moveNextMethod.Invoke(enumerator, null))
|
||||
{
|
||||
var obj = currentProperty.GetValue(enumerator);
|
||||
var indexer = obj.GetType().GetProperty("Item", new[] { typeof(string) });
|
||||
var serialNumber = indexer.GetValue(obj, new object[] { "SerialNumber" });
|
||||
hardwareInfo.Append(serialNumber?.ToString() ?? "");
|
||||
}
|
||||
|
||||
var disposeMethod = searcher.GetType().GetMethod("Dispose");
|
||||
disposeMethod?.Invoke(searcher, null);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
if (hardwareInfo.Length < 10)
|
||||
{
|
||||
hardwareInfo.Append(Environment.MachineName);
|
||||
hardwareInfo.Append(Environment.UserName);
|
||||
hardwareInfo.Append(Environment.OSVersion);
|
||||
}
|
||||
|
||||
return hardwareInfo.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基于硬件指纹生成25字符的设备ID
|
||||
/// </summary>
|
||||
private static string GenerateDeviceIdFromFingerprint(string hardwareFingerprint)
|
||||
{
|
||||
// 生成哈希
|
||||
using (var sha256 = SHA256.Create())
|
||||
{
|
||||
byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(hardwareFingerprint ?? string.Empty));
|
||||
string hashString = BitConverter.ToString(hashBytes).Replace("-", "");
|
||||
|
||||
// 取前25个字符,确保唯一性
|
||||
string deviceId = hashString.Substring(0, 25);
|
||||
|
||||
// 添加校验位(第25位)
|
||||
int checksum = 0;
|
||||
for (int i = 0; i < 24; i++)
|
||||
{
|
||||
checksum += Convert.ToInt32(deviceId[i]);
|
||||
}
|
||||
checksum %= 36; // 0-9, A-Z
|
||||
char checksumChar = checksum < 10 ? (char)(checksum + '0') : (char)(checksum - 10 + 'A');
|
||||
|
||||
return deviceId.Substring(0, 24) + checksumChar;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成备用设备ID(基于时间戳)
|
||||
/// </summary>
|
||||
@@ -282,16 +319,33 @@ namespace Ink_Canvas.Helpers
|
||||
/// <summary>
|
||||
/// 从文件加载设备ID
|
||||
/// </summary>
|
||||
private static string LoadDeviceIdFromFile(string filePath)
|
||||
private static DeviceIdInfo LoadDeviceIdFromFile(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
string content = File.ReadAllText(filePath).Trim();
|
||||
|
||||
try
|
||||
{
|
||||
var info = JsonConvert.DeserializeObject<DeviceIdInfo>(content);
|
||||
if (info != null && !string.IsNullOrEmpty(info.DeviceId) && IsValidDeviceId(info.DeviceId))
|
||||
{
|
||||
return info;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
if (IsValidDeviceId(content))
|
||||
{
|
||||
return content;
|
||||
return new DeviceIdInfo
|
||||
{
|
||||
DeviceId = content,
|
||||
HardwareFingerprint = null
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,7 +359,7 @@ namespace Ink_Canvas.Helpers
|
||||
/// <summary>
|
||||
/// 保存设备ID到文件
|
||||
/// </summary>
|
||||
private static void SaveDeviceIdToFile(string filePath, string deviceId)
|
||||
private static void SaveDeviceIdToFile(string filePath, DeviceIdInfo info)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -316,7 +370,8 @@ namespace Ink_Canvas.Helpers
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
File.WriteAllText(filePath, deviceId);
|
||||
string json = JsonConvert.SerializeObject(info, Formatting.Indented);
|
||||
File.WriteAllText(filePath, json);
|
||||
|
||||
LogHelper.WriteLogToFile($"DeviceIdentifier | 设备ID已保存到: {filePath}");
|
||||
}
|
||||
@@ -326,6 +381,14 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
private class DeviceIdInfo
|
||||
{
|
||||
[JsonProperty("deviceId")]
|
||||
public string DeviceId { get; set; }
|
||||
|
||||
[JsonProperty("hardwareFingerprint")]
|
||||
public string HardwareFingerprint { get; set; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
@@ -363,6 +426,9 @@ namespace Ink_Canvas.Helpers
|
||||
[JsonProperty("lastModified")]
|
||||
public DateTime LastModified { get; set; }
|
||||
|
||||
[JsonProperty("updateChannel")]
|
||||
public Ink_Canvas.UpdateChannel UpdateChannel { get; set; } = Ink_Canvas.UpdateChannel.Release;
|
||||
|
||||
// 每周统计数据(秒级精度)
|
||||
[JsonProperty("weeklyLaunchCount")]
|
||||
public int WeeklyLaunchCount { get; set; }
|
||||
@@ -940,6 +1006,40 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateUsageChannel(Ink_Canvas.UpdateChannel channel)
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (fileLock)
|
||||
{
|
||||
var stats = LoadUsageStats();
|
||||
if (stats == null)
|
||||
{
|
||||
stats = new UsageStats
|
||||
{
|
||||
DeviceId = DeviceId,
|
||||
LastLaunchTime = DateTime.Now,
|
||||
LaunchCount = 0,
|
||||
TotalUsageSeconds = 0,
|
||||
AverageSessionSeconds = 0,
|
||||
LastUpdateCheck = DateTime.MinValue,
|
||||
UpdatePriority = UpdatePriority.Medium,
|
||||
UsageFrequency = UsageFrequency.Medium
|
||||
};
|
||||
}
|
||||
|
||||
stats.UpdateChannel = channel;
|
||||
SaveUsageStats(stats);
|
||||
|
||||
LogHelper.WriteLogToFile($"DeviceIdentifier | 更新使用统计中的通道信息: {channel}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"DeviceIdentifier | 更新通道信息到使用统计失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录更新检查时间
|
||||
/// </summary>
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Ink_Canvas.Helpers
|
||||
private const string IpcFilePrefix = "InkCanvasFileAssociation_";
|
||||
private const string IpcBoardModePrefix = "InkCanvasBoardMode_";
|
||||
private const string IpcShowModePrefix = "InkCanvasShowMode_";
|
||||
private const string IpcUriCommandPrefix = "InkCanvasUriCommand_";
|
||||
private const int IpcTimeout = 5000; // 5秒超时
|
||||
|
||||
/// <summary>
|
||||
@@ -361,6 +362,57 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试通过IPC将URI命令发送给已运行的实例
|
||||
/// </summary>
|
||||
/// <param name="uri">URI命令</param>
|
||||
/// <returns>是否成功发送</returns>
|
||||
public static bool TrySendUriCommandToExistingInstance(string uri)
|
||||
{
|
||||
try
|
||||
{
|
||||
LogHelper.WriteLogToFile($"尝试通过IPC发送URI命令给已运行实例: {uri}", LogHelper.LogType.Event);
|
||||
|
||||
// 创建IPC文件
|
||||
string tempDir = Path.GetTempPath();
|
||||
string ipcFileName = IpcUriCommandPrefix + Guid.NewGuid().ToString("N") + ".tmp";
|
||||
string ipcFilePath = Path.Combine(tempDir, ipcFileName);
|
||||
|
||||
// 写入URI命令到IPC文件
|
||||
File.WriteAllText(ipcFilePath, uri, Encoding.UTF8);
|
||||
|
||||
// 创建事件通知已运行实例
|
||||
using (EventWaitHandle ipcEvent = new EventWaitHandle(false, EventResetMode.ManualReset, IpcEventName))
|
||||
{
|
||||
ipcEvent.Set();
|
||||
}
|
||||
|
||||
// 等待一段时间让已运行实例处理命令
|
||||
Thread.Sleep(1000);
|
||||
|
||||
// 清理IPC文件
|
||||
try
|
||||
{
|
||||
if (File.Exists(ipcFilePath))
|
||||
{
|
||||
File.Delete(ipcFilePath);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"清理IPC文件失败: {ex.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
|
||||
LogHelper.WriteLogToFile("IPC URI命令发送完成", LogHelper.LogType.Event);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"通过IPC发送URI命令失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动IPC监听器,等待其他实例发送文件路径
|
||||
/// </summary>
|
||||
@@ -576,6 +628,56 @@ namespace Ink_Canvas.Helpers
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
// 处理URI命令IPC文件
|
||||
string[] uriCommandFiles = Directory.GetFiles(tempDir, IpcUriCommandPrefix + "*.tmp");
|
||||
foreach (string ipcFile in uriCommandFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 读取命令内容
|
||||
string uri = File.ReadAllText(ipcFile, Encoding.UTF8);
|
||||
|
||||
if (!string.IsNullOrEmpty(uri))
|
||||
{
|
||||
LogHelper.WriteLogToFile($"IPC接收到URI命令: {uri}", LogHelper.LogType.Event);
|
||||
|
||||
// 在UI线程中处理URI命令
|
||||
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 获取主窗口并处理URI命令
|
||||
if (Application.Current.MainWindow is MainWindow mainWindow)
|
||||
{
|
||||
mainWindow.HandleUriCommand(uri);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"IPC处理URI命令失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// 删除IPC文件
|
||||
File.Delete(ipcFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"处理URI命令IPC文件失败: {ex.Message}", LogHelper.LogType.Warning);
|
||||
|
||||
// 尝试删除损坏的IPC文件
|
||||
try
|
||||
{
|
||||
if (File.Exists(ipcFile))
|
||||
{
|
||||
File.Delete(ipcFile);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using Microsoft.Office.Interop.PowerPoint;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
public interface IPPTLinkManager : IDisposable
|
||||
{
|
||||
event Action<object> SlideShowBegin;
|
||||
event Action<object> SlideShowNextSlide;
|
||||
event Action<object> SlideShowEnd;
|
||||
event Action<object> PresentationOpen;
|
||||
event Action<object> PresentationClose;
|
||||
event Action<bool> PPTConnectionChanged;
|
||||
event Action<bool> SlideShowStateChanged;
|
||||
|
||||
bool IsConnected { get; }
|
||||
bool IsInSlideShow { get; }
|
||||
bool IsSupportWPS { get; set; }
|
||||
int SlidesCount { get; }
|
||||
|
||||
object PPTApplication { get; }
|
||||
|
||||
// 生命周期管理
|
||||
void StartMonitoring();
|
||||
void StopMonitoring();
|
||||
|
||||
// 放映控制
|
||||
bool TryStartSlideShow();
|
||||
bool TryEndSlideShow();
|
||||
|
||||
// 导航控制
|
||||
bool TryNavigateToSlide(int slideNumber);
|
||||
bool TryNavigateNext();
|
||||
bool TryNavigatePrevious();
|
||||
|
||||
// 查询
|
||||
int GetCurrentSlideNumber();
|
||||
string GetPresentationName();
|
||||
bool TryShowSlideNavigation();
|
||||
object GetCurrentActivePresentation();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -63,13 +63,19 @@ namespace Ink_Canvas.Helpers
|
||||
{
|
||||
try
|
||||
{
|
||||
object bestApp = GetAnyActivePowerPoint(null, out int bestPriority, out _, isSupportWPS);
|
||||
object bestApp = GetAnyActivePowerPoint(null, out int bestPriority, out _);
|
||||
|
||||
if (bestApp != null && bestPriority > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
Microsoft.Office.Interop.PowerPoint.Application pptApp = bestApp as Microsoft.Office.Interop.PowerPoint.Application;
|
||||
Type appType = typeof(Microsoft.Office.Interop.PowerPoint.Application);
|
||||
Microsoft.Office.Interop.PowerPoint.Application pptApp = null;
|
||||
|
||||
if (appType.IsInstanceOfType(bestApp))
|
||||
{
|
||||
pptApp = (Microsoft.Office.Interop.PowerPoint.Application)bestApp;
|
||||
}
|
||||
|
||||
if (pptApp != null)
|
||||
{
|
||||
@@ -112,8 +118,8 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private static object GetAnyActivePowerPoint(object targetApp, out int bestPriority, out int targetPriority, bool isSupportWPS)
|
||||
#region Public Methods
|
||||
public static object GetAnyActivePowerPoint(object targetApp, out int bestPriority, out int targetPriority)
|
||||
{
|
||||
IRunningObjectTable rot = null;
|
||||
IEnumMoniker enumMoniker = null;
|
||||
@@ -169,10 +175,7 @@ namespace Ink_Canvas.Helpers
|
||||
object appObj = comObject.GetType().InvokeMember("Application", BindingFlags.GetProperty, null, comObject, null);
|
||||
candidateApp = appObj;
|
||||
}
|
||||
catch
|
||||
{
|
||||
candidateApp = comObject;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
bool isDuplicate = false;
|
||||
@@ -243,7 +246,7 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsSlideShowWindowActive(ssWindow, isSupportWPS))
|
||||
if (IsSlideShowWindowActive(ssWindow))
|
||||
{
|
||||
currentPriority = 3;
|
||||
}
|
||||
@@ -265,6 +268,8 @@ namespace Ink_Canvas.Helpers
|
||||
|
||||
if (currentPriority > 0)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"ROT扫描: {displayName}: priority={currentPriority}", LogHelper.LogType.Trace);
|
||||
|
||||
if (currentPriority > highestPriority)
|
||||
{
|
||||
highestPriority = currentPriority;
|
||||
@@ -294,6 +299,15 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
|
||||
bestPriority = highestPriority;
|
||||
|
||||
if (bestApp != null)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"ROT扫描完成: 找到最佳应用, priority={bestPriority}", LogHelper.LogType.Trace);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile($"ROT扫描完成: 未找到可用应用", LogHelper.LogType.Trace);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -320,7 +334,7 @@ namespace Ink_Canvas.Helpers
|
||||
return bestApp;
|
||||
}
|
||||
|
||||
private static bool AreComObjectsEqual(object o1, object o2)
|
||||
public static bool AreComObjectsEqual(object o1, object o2)
|
||||
{
|
||||
if (o1 == null || o2 == null) return false;
|
||||
if (ReferenceEquals(o1, o2)) return true;
|
||||
@@ -355,10 +369,12 @@ namespace Ink_Canvas.Helpers
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsSlideShowWindowActive(object sswObj, bool isSupportWPS)
|
||||
public static bool IsSlideShowWindowActive(object sswObj)
|
||||
{
|
||||
try
|
||||
{
|
||||
dynamic ssw = sswObj;
|
||||
|
||||
IntPtr foregroundHwnd = GetForegroundWindow();
|
||||
if (foregroundHwnd == IntPtr.Zero) return false;
|
||||
|
||||
@@ -371,15 +387,13 @@ namespace Ink_Canvas.Helpers
|
||||
sswHwnd = GetPptHwndFromSlideShowWindow(sswObj);
|
||||
}
|
||||
catch { return false; }
|
||||
|
||||
if (sswHwnd == IntPtr.Zero) return false;
|
||||
|
||||
uint sswPid;
|
||||
GetWindowThreadProcessId(sswHwnd, out sswPid);
|
||||
|
||||
if (fgPid == sswPid) return true;
|
||||
if (isSupportWPS)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
using (Process fgProc = Process.GetProcessById((int)fgPid))
|
||||
@@ -395,7 +409,6 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -407,31 +420,23 @@ namespace Ink_Canvas.Helpers
|
||||
|
||||
private static IntPtr GetPptHwndFromSlideShowWindow(object pptSlideShowWindowObj)
|
||||
{
|
||||
IntPtr hwnd = IntPtr.Zero;
|
||||
if (pptSlideShowWindowObj == null) return IntPtr.Zero;
|
||||
|
||||
try
|
||||
{
|
||||
dynamic ssw = pptSlideShowWindowObj;
|
||||
object hwndObj = ssw.HWND;
|
||||
|
||||
if (hwndObj is int)
|
||||
{
|
||||
return new IntPtr((int)hwndObj);
|
||||
}
|
||||
else if (hwndObj is IntPtr)
|
||||
{
|
||||
return (IntPtr)hwndObj;
|
||||
}
|
||||
Microsoft.Office.Interop.PowerPoint.SlideShowWindow slideWindow = (Microsoft.Office.Interop.PowerPoint.SlideShowWindow)pptSlideShowWindowObj;
|
||||
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
int hwndVal = slideWindow.HWND;
|
||||
|
||||
hwnd = new IntPtr(hwndVal);
|
||||
}
|
||||
catch { }
|
||||
|
||||
return hwnd;
|
||||
}
|
||||
|
||||
private static void SafeReleaseComObject(object comObj)
|
||||
public static void SafeReleaseComObject(object comObj)
|
||||
{
|
||||
if (comObj == null) return;
|
||||
|
||||
@@ -454,6 +459,35 @@ namespace Ink_Canvas.Helpers
|
||||
if (bindCtx != null)
|
||||
Marshal.ReleaseComObject(bindCtx);
|
||||
}
|
||||
|
||||
public static int GetSlideShowWindowsCount(Microsoft.Office.Interop.PowerPoint.Application pptApp)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (pptApp == null) return 0;
|
||||
return pptApp.SlideShowWindows.Count;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsValidSlideShowWindow(object pptSlideShowWindow)
|
||||
{
|
||||
if (pptSlideShowWindow == null) return false;
|
||||
|
||||
try
|
||||
{
|
||||
dynamic ssw = pptSlideShowWindow;
|
||||
var _ = ssw.Active;
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,168 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Sentry;
|
||||
using Sentry.Protocol;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// 遥测上传:根据用户设置,通过 Sentry 上传 usage_stats 和 Crashes 目录的摘要信息。
|
||||
/// </summary>
|
||||
internal static class TelemetryUploader
|
||||
{
|
||||
/// <summary>
|
||||
/// 根据当前设置决定是否上传遥测数据。
|
||||
/// 在主窗口加载完成后调用一次即可。
|
||||
/// </summary>
|
||||
public static Task UploadTelemetryIfNeededAsync()
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = MainWindow.Settings;
|
||||
if (settings == null || settings.Startup == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var level = settings.Startup.TelemetryUploadLevel;
|
||||
if (level == TelemetryUploadLevel.None)
|
||||
{
|
||||
return; // 用户未开启
|
||||
}
|
||||
|
||||
// 获取并校验设备ID
|
||||
string deviceId = DeviceIdentifier.GetDeviceId();
|
||||
if (string.IsNullOrWhiteSpace(deviceId) || deviceId.Length < 5)
|
||||
{
|
||||
LogHelper.WriteLogToFile("TelemetryUploader | 设备ID无效,取消遥测上传", LogHelper.LogType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 可选:Crashes 目录下的崩溃日志
|
||||
List<object> crashFiles = null;
|
||||
if (level == TelemetryUploadLevel.Extended)
|
||||
{
|
||||
try
|
||||
{
|
||||
string crashDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Crashes");
|
||||
if (Directory.Exists(crashDir))
|
||||
{
|
||||
crashFiles = new List<object>();
|
||||
var files = Directory.GetFiles(crashDir);
|
||||
|
||||
FileInfo latestInfo = null;
|
||||
string latestContent = null;
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
try
|
||||
{
|
||||
var info = new FileInfo(file);
|
||||
|
||||
// 避免一次上传过大,单文件限制为 8192KB
|
||||
if (info.Length > 8192 * 1024)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string content = File.ReadAllText(file);
|
||||
|
||||
if (latestInfo == null || info.LastWriteTime > latestInfo.LastWriteTime)
|
||||
{
|
||||
latestInfo = info;
|
||||
latestContent = content;
|
||||
}
|
||||
}
|
||||
catch (Exception exFile)
|
||||
{
|
||||
LogHelper.WriteLogToFile(
|
||||
$"TelemetryUploader | 读取崩溃日志失败: {exFile.Message}",
|
||||
LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
if (latestInfo != null && latestContent != null)
|
||||
{
|
||||
crashFiles.Add(new
|
||||
{
|
||||
file_name = latestInfo.Name,
|
||||
content = latestContent
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile(
|
||||
$"TelemetryUploader | 收集崩溃日志失败: {ex.Message}",
|
||||
LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
var telemetryData = new
|
||||
{
|
||||
telemetry_level = level.ToString(),
|
||||
device_id = deviceId,
|
||||
update_channel = settings.Startup.UpdateChannel.ToString(),
|
||||
app_version = Assembly.GetExecutingAssembly().GetName().Version.ToString(),
|
||||
os_version = Environment.OSVersion.VersionString,
|
||||
crash_files = crashFiles
|
||||
};
|
||||
|
||||
// 通过 Sentry 上报一个包含遥测信息的事件
|
||||
string userName = Environment.UserName;
|
||||
SentrySdk.ConfigureScope(scope =>
|
||||
{
|
||||
scope.User = new SentryUser
|
||||
{
|
||||
Id = deviceId,
|
||||
Username = userName,
|
||||
Email = $"{userName}",
|
||||
IpAddress = "{{auto}}"
|
||||
};
|
||||
});
|
||||
|
||||
var evt = new SentryEvent
|
||||
{
|
||||
Message = "ICC CE Telemetry",
|
||||
Level = SentryLevel.Info
|
||||
};
|
||||
|
||||
evt.User = new SentryUser
|
||||
{
|
||||
Id = deviceId,
|
||||
Username = userName,
|
||||
Email = $"{userName}",
|
||||
IpAddress = "{{auto}}"
|
||||
};
|
||||
|
||||
evt.SetTag("telemetry_level", level.ToString());
|
||||
evt.SetTag("device_id", deviceId);
|
||||
evt.SetTag("update_channel", settings.Startup.UpdateChannel.ToString());
|
||||
evt.SetTag("app_version", Assembly.GetExecutingAssembly().GetName().Version.ToString());
|
||||
evt.SetTag("os_version", Environment.OSVersion.VersionString);
|
||||
evt.SetExtra("telemetry_data", telemetryData);
|
||||
|
||||
if (crashFiles != null)
|
||||
{
|
||||
evt.SetExtra("crash_files", crashFiles);
|
||||
}
|
||||
|
||||
SentrySdk.CaptureEvent(evt);
|
||||
LogHelper.WriteLogToFile("TelemetryUploader | 遥测数据已通过 Sentry 上报", LogHelper.LogType.Event);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"TelemetryUploader | 遥测上传失败: {ex.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Security;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
public static class UriSchemeHelper
|
||||
{
|
||||
private const string SchemeName = "icc";
|
||||
private const string FriendlyName = "URL:Ink Canvas Protocol";
|
||||
|
||||
public static bool RegisterUriScheme()
|
||||
{
|
||||
try
|
||||
{
|
||||
string exePath = Process.GetCurrentProcess().MainModule.FileName;
|
||||
|
||||
// 使用 CurrentUser\Software\Classes 代替 ClassesRoot,无需管理员权限
|
||||
using (RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Classes\" + SchemeName))
|
||||
{
|
||||
key.SetValue("", FriendlyName);
|
||||
key.SetValue("URL Protocol", "");
|
||||
|
||||
using (RegistryKey defaultIconKey = key.CreateSubKey("DefaultIcon"))
|
||||
{
|
||||
// 修正引号转义
|
||||
defaultIconKey.SetValue("", "\"" + exePath + "\",1");
|
||||
}
|
||||
|
||||
using (RegistryKey shellKey = key.CreateSubKey("shell"))
|
||||
using (RegistryKey openKey = shellKey.CreateSubKey("open"))
|
||||
using (RegistryKey commandKey = openKey.CreateSubKey("command"))
|
||||
{
|
||||
// 修正引号转义
|
||||
commandKey.SetValue("", "\"" + exePath + "\" \"%1\"");
|
||||
}
|
||||
}
|
||||
LogHelper.WriteLogToFile($"成功注册URI Scheme: {SchemeName}://", LogHelper.LogType.Event);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"注册URI Scheme失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool UnregisterUriScheme()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用 CurrentUser\Software\Classes
|
||||
Registry.CurrentUser.DeleteSubKeyTree(@"Software\Classes\" + SchemeName, false);
|
||||
LogHelper.WriteLogToFile($"成功注销URI Scheme: {SchemeName}://", LogHelper.LogType.Event);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"注销URI Scheme失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsUriSchemeRegistered()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用 CurrentUser\Software\Classes
|
||||
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Classes\" + SchemeName))
|
||||
{
|
||||
if (key == null) return false;
|
||||
// 修正反斜杠路径
|
||||
using (RegistryKey shellKey = key.OpenSubKey(@"shell\open\command"))
|
||||
{
|
||||
if (shellKey == null) return false;
|
||||
string command = shellKey.GetValue("") as string;
|
||||
if (string.IsNullOrEmpty(command)) return false;
|
||||
|
||||
// 提取第一个标记作为可执行文件路径(处理带引号的情况)
|
||||
string registeredExePath = "";
|
||||
if (command.StartsWith("\""))
|
||||
{
|
||||
int nextQuote = command.IndexOf("\"", 1);
|
||||
if (nextQuote > 1)
|
||||
{
|
||||
registeredExePath = command.Substring(1, nextQuote - 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int firstSpace = command.IndexOf(" ");
|
||||
registeredExePath = firstSpace > 0 ? command.Substring(0, firstSpace) : command;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(registeredExePath)) return false;
|
||||
|
||||
string currentExePath = Process.GetCurrentProcess().MainModule.FileName;
|
||||
|
||||
try
|
||||
{
|
||||
string normalizedRegisteredPath = System.IO.Path.GetFullPath(registeredExePath);
|
||||
string normalizedCurrentPath = System.IO.Path.GetFullPath(currentExePath);
|
||||
return string.Equals(normalizedRegisteredPath, normalizedCurrentPath, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return string.Equals(registeredExePath, currentExePath, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
<RootNamespace>Ink_Canvas</RootNamespace>
|
||||
<AssemblyName>InkCanvasForClass</AssemblyName>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
@@ -155,6 +156,8 @@
|
||||
<PackageReference Include="MicrosoftOfficeCore" Version="15.0.0" />
|
||||
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
|
||||
<PackageReference Include="Microsoft.International.Converters.PinYinConverter" Version="1.0.0" />
|
||||
<PackageReference Include="Sentry" Version="6.0.0" />
|
||||
<PackageReference Include="Sentry.Profiling" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
@@ -294,6 +297,12 @@
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Icons-Fluent\ic_fluent_delete_24_regular.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="privacy.txt" />
|
||||
<EmbeddedResource Include="telemetry_dsn.txt" Condition="Exists('telemetry_dsn.txt')">
|
||||
<Link>telemetry_dsn.txt</Link>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Icons-Fluent\ic_fluent_cursorWITHdelete_24_regular.png" />
|
||||
</ItemGroup>
|
||||
|
||||
+225
-170
@@ -851,6 +851,16 @@
|
||||
<TextBlock Foreground="#fafafa" Text="{Binding ElementName=InkFadeTimeSlider, Path=Value, StringFormat={}{0:0}ms}"
|
||||
VerticalAlignment="Center" FontSize="14" Margin="16,0,0,0" />
|
||||
</ui:SimpleStackPanel>
|
||||
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0" Stroke="#3f3f46"
|
||||
StrokeThickness="1" Margin="0,4,0,4" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="在笔工具菜单中隐藏墨迹渐隐控制" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchHideInkFadeControlInPenMenu"
|
||||
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchHideInkFadeControlInPenMenu_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<TextBlock Text="# 开启后,主工具栏上点击笔工具后弹出的上下文菜单中将不显示墨迹渐隐控制开关" TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
|
||||
</ui:SimpleStackPanel>
|
||||
</GroupBox>
|
||||
@@ -1446,12 +1456,21 @@
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<Image Source="/Resources/Icons-png/Powerpoint.png" Margin="0,0,6,0" Width="28"
|
||||
Height="28" VerticalAlignment="Center" />
|
||||
<TextBlock Foreground="#fafafa" Text="PowerPoint 联动增强(目前已不推荐使用)" VerticalAlignment="Center"
|
||||
<TextBlock Foreground="#fafafa" Text="PowerPoint 联动增强" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchPowerPointEnhancement"
|
||||
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchPowerPointEnhancement_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<Image Source="/Resources/Icons-png/Powerpoint.png" Margin="0,0,6,0" Width="28"
|
||||
Height="28" VerticalAlignment="Center" />
|
||||
<TextBlock Foreground="#fafafa" Text="使用 ROT 联动(需要重启)" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchUseRotPptLink"
|
||||
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchUseRotPptLink_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<Image Source="/Resources/Icons-png/WPS.png" Margin="0,0,6,0" Width="28"
|
||||
Height="28" VerticalAlignment="Center" />
|
||||
@@ -2407,6 +2426,11 @@
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchIsSpecialScreen"
|
||||
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchIsSpecialScreen_OnToggled" />
|
||||
<ui:ToggleSwitch OnContent="开" OffContent="关" Name="ToggleSwitchIsEnableUriScheme"
|
||||
Visibility="Collapsed" IsOn="False"
|
||||
AutomationProperties.Name="外部协议调用 (icc://)"
|
||||
ToolTip="通过 icc:// 协议从外部控制软件"
|
||||
Toggled="ToggleSwitchIsEnableUriScheme_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock Foreground="#fafafa" Text="触摸倍数" VerticalAlignment="Center"
|
||||
@@ -2572,7 +2596,7 @@
|
||||
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold" />
|
||||
</ui:SimpleStackPanel>
|
||||
<TextBlock TextWrapping="Wrap" Foreground="#a1a1aa"
|
||||
Text="# 避免画布全屏,会导致左侧或顶部有AppBar(Dock栏软件)时导致浮动工具栏偏移,重启icc后生效。" />
|
||||
Text="# 避免画布全屏,应该可解决任务栏非置顶和Win11任务栏无法点击的问题,会导致左侧或顶部有AppBar(Dock栏软件)时导致浮动工具栏偏移,重启icc后生效。" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
|
||||
<TextBlock Foreground="#fafafa" Text="启用EdgeGestureUtil"
|
||||
@@ -3870,6 +3894,35 @@
|
||||
<Button x:Name="RefreshDeviceInfoButton" Content="刷新设备信息"
|
||||
Width="120" Height="30" Margin="0,8,0,0"
|
||||
Click="RefreshDeviceInfo_Click" />
|
||||
|
||||
<Line X1="0" Y1="0" X2="460" Y2="0" Stroke="#3f3f46"
|
||||
StrokeThickness="1" Margin="0,6,0,6" />
|
||||
|
||||
<CheckBox x:Name="CheckBoxTelemetryPrivacyAccepted"
|
||||
Margin="0,0,0,2"
|
||||
Foreground="#a1a1aa"
|
||||
FontSize="12"
|
||||
Checked="CheckBoxTelemetryPrivacyAccepted_Checked"
|
||||
Unchecked="CheckBoxTelemetryPrivacyAccepted_Checked">
|
||||
<TextBlock>
|
||||
<Run Text="我已阅读并同意 " />
|
||||
<Run Text="privacy" FontWeight="Bold" />
|
||||
<Run Text=" 中的隐私说明" />
|
||||
</TextBlock>
|
||||
</CheckBox>
|
||||
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,2,0,0">
|
||||
<TextBlock Text="匿名使用数据上传:" Foreground="#a1a1aa" FontSize="12"
|
||||
VerticalAlignment="Center" />
|
||||
<ComboBox x:Name="ComboBoxTelemetryUploadLevel"
|
||||
Width="230"
|
||||
Margin="8,0,0,0"
|
||||
SelectionChanged="ComboBoxTelemetryUploadLevel_SelectionChanged">
|
||||
<ComboBoxItem Tag="0">关闭(不上传)</ComboBoxItem>
|
||||
<ComboBoxItem Tag="1">上传基础数据</ComboBoxItem>
|
||||
<ComboBoxItem Tag="2">上传基础 + 可选数据</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</ui:SimpleStackPanel>
|
||||
</ui:SimpleStackPanel>
|
||||
</Border>
|
||||
|
||||
@@ -4726,7 +4779,7 @@
|
||||
BorderThickness="1,1,0,1" BorderBrush="{DynamicResource BoardFloatBarBorderBrush}"
|
||||
MouseUp="BtnWhiteBoardSwitchPrevious_Click" CornerRadius="5,0,0,5"
|
||||
Background="{DynamicResource BoardFloatBarBackground}"
|
||||
Opacity="0.95">
|
||||
Opacity="1">
|
||||
<Grid Margin="6,6,6,4">
|
||||
<Image VerticalAlignment="Top"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="20" Width="20">
|
||||
@@ -4750,7 +4803,7 @@
|
||||
<Border Width="75" Height="50" MouseUp="BtnWhiteBoardPageIndex_Click"
|
||||
Name="BtnLeftPageListWB"
|
||||
BorderThickness="1,1,1,1" BorderBrush="{DynamicResource BoardFloatBarBorderBrush}" Background="{DynamicResource BoardFloatBarBackground}"
|
||||
Opacity="0.95">
|
||||
Opacity="1">
|
||||
<Grid Margin="6,6,6,4">
|
||||
<TextBlock HorizontalAlignment="Center"
|
||||
Text="{Binding ElementName=TextBlockWhiteBoardIndexInfo, Path=Text}"
|
||||
@@ -4816,7 +4869,7 @@
|
||||
CornerRadius="0,5,5,0"
|
||||
IsEnabled="{Binding ElementName=BtnWhiteBoardSwitchNext, Path=IsEnabled}"
|
||||
BorderThickness="0,1,1,1" BorderBrush="{DynamicResource BoardFloatBarBorderBrush}"
|
||||
Background="{DynamicResource BoardFloatBarBackground}" Opacity="0.95">
|
||||
Background="{DynamicResource BoardFloatBarBackground}" Opacity="1">
|
||||
<Grid Margin="6,6,6,4">
|
||||
<Image VerticalAlignment="Top"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="20" Width="20">
|
||||
@@ -4842,7 +4895,7 @@
|
||||
<Border Width="60" Height="50"
|
||||
BorderThickness="1" BorderBrush="{DynamicResource BoardFloatBarBorderBrush}"
|
||||
MouseUp="BtnWhiteBoardAdd_Click" CornerRadius="5" Background="{DynamicResource BoardFloatBarBackground}"
|
||||
Opacity="0.95">
|
||||
Opacity="1">
|
||||
<Grid Margin="6,6,6,4">
|
||||
<Image VerticalAlignment="Top"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="20" Width="20">
|
||||
@@ -4902,156 +4955,158 @@
|
||||
HorizontalAlignment="Center" FontSize="12" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<Grid Width="0" Margin="0,0,0,5">
|
||||
<Border ClipToBounds="True" Name="BoardTwoFingerGestureBorder"
|
||||
<Border>
|
||||
<Grid Width="0" Margin="0,0,0,5" RenderTransformOrigin="0,1">
|
||||
<Border ClipToBounds="True" x:Name="BoardTwoFingerGestureBorder"
|
||||
Margin="-115,-161,-55,50"
|
||||
CornerRadius="8"
|
||||
Background="{DynamicResource FloatBarBackground}" Opacity="1" BorderBrush="#2563eb"
|
||||
BorderThickness="1">
|
||||
<ui:SimpleStackPanel Margin="0">
|
||||
<Border BorderBrush="#1e3a8a" BorderThickness="0,0,0,1"
|
||||
<ui:SimpleStackPanel Margin="0">
|
||||
<Border BorderBrush="#1e3a8a" BorderThickness="0,0,0,1"
|
||||
CornerRadius="8,8,0,0"
|
||||
Background="#2563eb" Margin="-1,-1,-1,1">
|
||||
<Canvas Height="36" ClipToBounds="True">
|
||||
<TextBlock Text="手势选项" Canvas.Left="12" Foreground="White"
|
||||
Padding="0,7"
|
||||
FontSize="17" FontWeight="Bold"
|
||||
TextAlignment="Center" />
|
||||
<Image Margin="144,10,0,0"
|
||||
Source="/Resources/new-icons/close-white.png"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Height="16"
|
||||
Width="16" MouseDown="Border_MouseDown"
|
||||
MouseUp="CloseBordertools_MouseUp" />
|
||||
</Canvas>
|
||||
</Border>
|
||||
<Viewbox Margin="8,0,8,0">
|
||||
<ui:SimpleStackPanel Margin="0">
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" Spacing="4"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0,3,0,0">
|
||||
<Image Source="{DynamicResource MultiTouchIcon}"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Height="16"
|
||||
Width="16" />
|
||||
<Label Content="多指书写" FontSize="10"
|
||||
VerticalAlignment="Center" />
|
||||
<Viewbox Width="36" Height="18" Margin="4,0,0,0">
|
||||
<ui:ToggleSwitch MinWidth="0" Margin="0,-6,0,-6"
|
||||
Name="BoardToggleSwitchEnableMultiTouchMode"
|
||||
FontFamily="Microsoft YaHei UI"
|
||||
IsOn="False"
|
||||
OnContent=""
|
||||
OffContent=""
|
||||
Toggled="ToggleSwitchEnableMultiTouchMode_Toggled"
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<ui:ToggleSwitch.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform />
|
||||
<SkewTransform />
|
||||
<RotateTransform />
|
||||
<TranslateTransform X="8" />
|
||||
</TransformGroup>
|
||||
</ui:ToggleSwitch.RenderTransform>
|
||||
</ui:ToggleSwitch>
|
||||
</Viewbox>
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel
|
||||
Opacity="{Binding ElementName=TwoFingerGestureSimpleStackPanel, Path=Opacity}"
|
||||
Orientation="Horizontal" Spacing="4"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center">
|
||||
<Image Source="{DynamicResource HandMoveIcon}"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Height="16"
|
||||
Width="16" />
|
||||
<Label Content="双指移动" FontSize="10"
|
||||
VerticalAlignment="Center" />
|
||||
<Viewbox Width="36" Height="18" Margin="4,0,0,0">
|
||||
<ui:ToggleSwitch MinWidth="0" Margin="0,-6,0,-6"
|
||||
Name="BoardToggleSwitchEnableTwoFingerTranslate"
|
||||
FontFamily="Microsoft YaHei UI"
|
||||
IsOn="False"
|
||||
OnContent=""
|
||||
OffContent=""
|
||||
Toggled="ToggleSwitchEnableTwoFingerTranslate_Toggled"
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<ui:ToggleSwitch.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform />
|
||||
<SkewTransform />
|
||||
<RotateTransform />
|
||||
<TranslateTransform X="8" />
|
||||
</TransformGroup>
|
||||
</ui:ToggleSwitch.RenderTransform>
|
||||
</ui:ToggleSwitch>
|
||||
</Viewbox>
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel
|
||||
Opacity="{Binding ElementName=TwoFingerGestureSimpleStackPanel, Path=Opacity}"
|
||||
Orientation="Horizontal" Spacing="4"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center">
|
||||
<Image Source="{DynamicResource ZoomIcon}"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Height="16"
|
||||
Width="16" />
|
||||
<Label Content="双指缩放" FontSize="10"
|
||||
VerticalAlignment="Center" />
|
||||
<Viewbox Width="36" Height="18" Margin="4,0,0,0">
|
||||
<ui:ToggleSwitch MinWidth="0" Margin="0,-6,0,-6"
|
||||
FontFamily="Microsoft YaHei UI"
|
||||
IsOn="False" OnContent="" OffContent=""
|
||||
Name="BoardToggleSwitchEnableTwoFingerZoom"
|
||||
Toggled="ToggleSwitchEnableTwoFingerZoom_Toggled"
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<ui:ToggleSwitch.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform />
|
||||
<SkewTransform />
|
||||
<RotateTransform />
|
||||
<TranslateTransform X="8" />
|
||||
</TransformGroup>
|
||||
</ui:ToggleSwitch.RenderTransform>
|
||||
</ui:ToggleSwitch>
|
||||
</Viewbox>
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel
|
||||
Opacity="{Binding ElementName=TwoFingerGestureSimpleStackPanel, Path=Opacity}"
|
||||
Orientation="Horizontal" Spacing="4"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center">
|
||||
<Image Source="{DynamicResource RotateIcon}"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Height="16"
|
||||
Width="16" />
|
||||
<Label Content="双指旋转" FontSize="10"
|
||||
VerticalAlignment="Center" />
|
||||
<Viewbox Width="36" Height="18" Margin="4,0,0,0">
|
||||
<ui:ToggleSwitch MinWidth="0" Margin="0,-6,0,-6"
|
||||
FontFamily="Microsoft YaHei UI"
|
||||
IsOn="False" OnContent="" OffContent=""
|
||||
Name="BoardToggleSwitchEnableTwoFingerRotation"
|
||||
Toggled="ToggleSwitchEnableTwoFingerRotation_Toggled"
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<ui:ToggleSwitch.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform />
|
||||
<SkewTransform />
|
||||
<RotateTransform />
|
||||
<TranslateTransform X="8" />
|
||||
</TransformGroup>
|
||||
</ui:ToggleSwitch.RenderTransform>
|
||||
</ui:ToggleSwitch>
|
||||
</Viewbox>
|
||||
<Canvas Height="36" ClipToBounds="True">
|
||||
<TextBlock Text="手势选项" Canvas.Left="12" Foreground="White"
|
||||
Padding="0,7"
|
||||
FontSize="17" FontWeight="Bold"
|
||||
TextAlignment="Center" />
|
||||
<Image Margin="144,10,0,0"
|
||||
Source="/Resources/new-icons/close-white.png"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Height="16"
|
||||
Width="16" MouseDown="Border_MouseDown"
|
||||
MouseUp="CloseBordertools_MouseUp" />
|
||||
</Canvas>
|
||||
</Border>
|
||||
<Viewbox Margin="8,0,8,0">
|
||||
<ui:SimpleStackPanel Margin="0">
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" Spacing="4"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0,3,0,0">
|
||||
<Image Source="{DynamicResource MultiTouchIcon}"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Height="16"
|
||||
Width="16" />
|
||||
<Label Content="多指书写" FontSize="10"
|
||||
VerticalAlignment="Center" />
|
||||
<Viewbox Width="36" Height="18" Margin="4,0,0,0">
|
||||
<ui:ToggleSwitch MinWidth="0" Margin="0,-6,0,-6"
|
||||
x:Name="BoardToggleSwitchEnableMultiTouchMode"
|
||||
FontFamily="Microsoft YaHei UI"
|
||||
IsOn="False"
|
||||
OnContent=""
|
||||
OffContent=""
|
||||
Toggled="ToggleSwitchEnableMultiTouchMode_Toggled"
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<ui:ToggleSwitch.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform />
|
||||
<SkewTransform />
|
||||
<RotateTransform />
|
||||
<TranslateTransform X="8" />
|
||||
</TransformGroup>
|
||||
</ui:ToggleSwitch.RenderTransform>
|
||||
</ui:ToggleSwitch>
|
||||
</Viewbox>
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel
|
||||
Opacity="{Binding Opacity, ElementName=TwoFingerGestureSimpleStackPanel}"
|
||||
Orientation="Horizontal" Spacing="4"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center">
|
||||
<Image Source="{DynamicResource HandMoveIcon}"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Height="16"
|
||||
Width="16" />
|
||||
<Label Content="双指移动" FontSize="10"
|
||||
VerticalAlignment="Center" />
|
||||
<Viewbox Width="36" Height="18" Margin="4,0,0,0">
|
||||
<ui:ToggleSwitch MinWidth="0" Margin="0,-6,0,-6"
|
||||
x:Name="BoardToggleSwitchEnableTwoFingerTranslate"
|
||||
FontFamily="Microsoft YaHei UI"
|
||||
IsOn="False"
|
||||
OnContent=""
|
||||
OffContent=""
|
||||
Toggled="ToggleSwitchEnableTwoFingerTranslate_Toggled"
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<ui:ToggleSwitch.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform />
|
||||
<SkewTransform />
|
||||
<RotateTransform />
|
||||
<TranslateTransform X="8" />
|
||||
</TransformGroup>
|
||||
</ui:ToggleSwitch.RenderTransform>
|
||||
</ui:ToggleSwitch>
|
||||
</Viewbox>
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel
|
||||
Opacity="{Binding Opacity, ElementName=TwoFingerGestureSimpleStackPanel}"
|
||||
Orientation="Horizontal" Spacing="4"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center">
|
||||
<Image Source="{DynamicResource ZoomIcon}"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Height="16"
|
||||
Width="16" />
|
||||
<Label Content="双指缩放" FontSize="10"
|
||||
VerticalAlignment="Center" />
|
||||
<Viewbox Width="36" Height="18" Margin="4,0,0,0">
|
||||
<ui:ToggleSwitch MinWidth="0" Margin="0,-6,0,-6"
|
||||
FontFamily="Microsoft YaHei UI"
|
||||
IsOn="False" OnContent="" OffContent=""
|
||||
x:Name="BoardToggleSwitchEnableTwoFingerZoom"
|
||||
Toggled="ToggleSwitchEnableTwoFingerZoom_Toggled"
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<ui:ToggleSwitch.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform />
|
||||
<SkewTransform />
|
||||
<RotateTransform />
|
||||
<TranslateTransform X="8" />
|
||||
</TransformGroup>
|
||||
</ui:ToggleSwitch.RenderTransform>
|
||||
</ui:ToggleSwitch>
|
||||
</Viewbox>
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel
|
||||
Opacity="{Binding Opacity, ElementName=TwoFingerGestureSimpleStackPanel}"
|
||||
Orientation="Horizontal" Spacing="4"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center">
|
||||
<Image Source="{DynamicResource RotateIcon}"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Height="16"
|
||||
Width="16" />
|
||||
<Label Content="双指旋转" FontSize="10"
|
||||
VerticalAlignment="Center" />
|
||||
<Viewbox Width="36" Height="18" Margin="4,0,0,0">
|
||||
<ui:ToggleSwitch MinWidth="0" Margin="0,-6,0,-6"
|
||||
FontFamily="Microsoft YaHei UI"
|
||||
IsOn="False" OnContent="" OffContent=""
|
||||
x:Name="BoardToggleSwitchEnableTwoFingerRotation"
|
||||
Toggled="ToggleSwitchEnableTwoFingerRotation_Toggled"
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<ui:ToggleSwitch.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform />
|
||||
<SkewTransform />
|
||||
<RotateTransform />
|
||||
<TranslateTransform X="8" />
|
||||
</TransformGroup>
|
||||
</ui:ToggleSwitch.RenderTransform>
|
||||
</ui:ToggleSwitch>
|
||||
</Viewbox>
|
||||
|
||||
</ui:SimpleStackPanel>
|
||||
</ui:SimpleStackPanel>
|
||||
</ui:SimpleStackPanel>
|
||||
</Viewbox>
|
||||
</ui:SimpleStackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Viewbox>
|
||||
</ui:SimpleStackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border Width="60" Height="50" MouseDown="Border_MouseDown"
|
||||
MouseUp="BoardChangeBackgroundColorBtn_MouseUp" CornerRadius="0,5,5,0"
|
||||
Background="{DynamicResource BoardFloatBarBackground}" Opacity="1" BorderThickness="0,1,1,1"
|
||||
@@ -5083,7 +5138,7 @@
|
||||
BorderThickness="1,1,0,1" BorderBrush="{DynamicResource BoardFloatBarBorderBrush}"
|
||||
MouseUp="SymbolIconSelect_MouseUp" CornerRadius="5,0,0,5"
|
||||
Background="{DynamicResource BoardFloatBarBackground}"
|
||||
Opacity="0.95">
|
||||
Opacity="1">
|
||||
<Grid Margin="6,6,6,4">
|
||||
<Image VerticalAlignment="Top"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="20" Width="20">
|
||||
@@ -5106,7 +5161,7 @@
|
||||
</Border>
|
||||
<Border x:Name="BoardPen" Width="60" Height="50" MouseDown="Border_MouseDown"
|
||||
BorderThickness="0,1,0,1" BorderBrush="{DynamicResource BoardFloatBarBorderBrush}"
|
||||
MouseUp="PenIcon_Click" Background="{DynamicResource BoardFloatBarBackground}" Opacity="0.95">
|
||||
MouseUp="PenIcon_Click" Background="{DynamicResource BoardFloatBarBackground}" Opacity="1">
|
||||
<Grid Margin="6,6,6,4">
|
||||
<Image x:Name="BoardPenIcon" VerticalAlignment="Top"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="20" Width="20">
|
||||
@@ -5141,7 +5196,7 @@
|
||||
</Grid.RenderTransform>
|
||||
<Border x:Name="BoardPenPalette" Visibility="Visible"
|
||||
Background="{DynamicResource FloatBarBackground}"
|
||||
Opacity="1" BorderBrush="{DynamicResource FloatBarBorderBrush}"
|
||||
Opacity="1" BorderBrush="#2563eb"
|
||||
BorderThickness="1" CornerRadius="8">
|
||||
<ui:SimpleStackPanel Margin="0,-20,0,0">
|
||||
<Border BorderBrush="#1e3a8a" Height="32"
|
||||
@@ -5294,7 +5349,7 @@
|
||||
IsOn="{Binding ElementName=ToggleSwitchEnableInkToShape, Path=IsOn}" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal"
|
||||
Width="100">
|
||||
Width="100" x:Name="InkFadeControlPanel1">
|
||||
<Label Margin="0,0,10,0" Content="墨迹渐隐"
|
||||
Foreground="{DynamicResource FloatBarForeground}"
|
||||
FontSize="12"
|
||||
@@ -6012,7 +6067,7 @@
|
||||
</Border>
|
||||
<Border x:Name="BoardEraser" Width="60" Height="50" MouseDown="Border_MouseDown"
|
||||
BorderThickness="0,1,1,1" BorderBrush="{DynamicResource BoardFloatBarBorderBrush}"
|
||||
MouseUp="BoardEraserIcon_Click" Background="{DynamicResource BoardFloatBarBackground}" Opacity="0.95">
|
||||
MouseUp="BoardEraserIcon_Click" Background="{DynamicResource BoardFloatBarBackground}" Opacity="1">
|
||||
<Grid Margin="6,6,6,4">
|
||||
<Image VerticalAlignment="Top"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="20" Width="20">
|
||||
@@ -6234,7 +6289,7 @@
|
||||
MouseDown="Border_MouseDown"
|
||||
Visibility="Collapsed"
|
||||
MouseUp="BoardEraserIconByStrokes_Click" Background="{DynamicResource BoardFloatBarBackground}"
|
||||
Opacity="0.85">
|
||||
Opacity="1">
|
||||
<Grid Margin="6,6,6,4">
|
||||
<Image VerticalAlignment="Top"
|
||||
Source="/Resources/Icons-png/eraser-line.png"
|
||||
@@ -6245,7 +6300,7 @@
|
||||
</Border>
|
||||
<Border x:Name="BoardGeometry" Width="60" Height="50" MouseDown="Border_MouseDown"
|
||||
BorderThickness="0,1,1,1" BorderBrush="{DynamicResource BoardFloatBarBorderBrush}"
|
||||
MouseUp="ImageDrawShape_MouseUp" Background="{DynamicResource BoardFloatBarBackground}" Opacity="0.95">
|
||||
MouseUp="ImageDrawShape_MouseUp" Background="{DynamicResource BoardFloatBarBackground}" Opacity="1">
|
||||
<Grid Margin="6,6,6,4">
|
||||
<Image VerticalAlignment="Top"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="20" Width="20">
|
||||
@@ -6271,7 +6326,7 @@
|
||||
<Border Width="0">
|
||||
<Border x:Name="BoardBorderDrawShape" Visibility="Visible"
|
||||
Background="{DynamicResource FloatBarBackground}"
|
||||
Opacity="0.85" BorderBrush="{DynamicResource FloatBarBorderBrush}" BorderThickness="1" CornerRadius="5"
|
||||
Opacity="1" BorderBrush="{DynamicResource FloatBarBorderBrush}" BorderThickness="1" CornerRadius="5"
|
||||
Margin="-147,-286,-89,55">
|
||||
<Viewbox>
|
||||
<ui:SimpleStackPanel Spacing="-8" Orientation="Vertical">
|
||||
@@ -6418,7 +6473,7 @@
|
||||
</Border>
|
||||
<Border x:Name="BoardInsertImage" Width="60" Height="50" MouseDown="Border_MouseDown"
|
||||
BorderThickness="0,1,0,1" BorderBrush="{DynamicResource BoardFloatBarBorderBrush}"
|
||||
MouseUp="InsertImageOptions_MouseUp" Background="{DynamicResource BoardFloatBarBackground}" Opacity="0.95">
|
||||
MouseUp="InsertImageOptions_MouseUp" Background="{DynamicResource BoardFloatBarBackground}" Opacity="1">
|
||||
<Grid Margin="6,6,6,4">
|
||||
<Image VerticalAlignment="Top"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="20" Width="20">
|
||||
@@ -6439,7 +6494,7 @@
|
||||
</Border>
|
||||
<Border>
|
||||
<!-- Image Insertion Options Panel -->
|
||||
<Grid RenderTransformOrigin="0,1" Margin="-133,-172,13,55">
|
||||
<Grid RenderTransformOrigin="0,1" Margin="-110,-92,13,55">
|
||||
<Grid.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform ScaleX="1.5" ScaleY="1.5" />
|
||||
@@ -6448,12 +6503,12 @@
|
||||
<TranslateTransform />
|
||||
</TransformGroup>
|
||||
</Grid.RenderTransform>
|
||||
<Border Name="BoardImageOptionsPanel" Visibility="Collapsed" ClipToBounds="True" CornerRadius="5" Background="{DynamicResource FloatBarBackground}" Opacity="1" BorderThickness="1" BorderBrush="#2563eb" Margin="0,76,0,0">
|
||||
<Border Name="BoardImageOptionsPanel" Visibility="Visible" ClipToBounds="True" CornerRadius="5" Background="{DynamicResource FloatBarBackground}" Opacity="1" BorderThickness="1" BorderBrush="#2563eb">
|
||||
<ui:SimpleStackPanel Margin="0">
|
||||
<Border BorderBrush="#1e3a8a" BorderThickness="0,0,0,1" Margin="-1,-1,-1,1" CornerRadius="6,6,0,0" Background="#2563eb">
|
||||
<Canvas Height="24" ClipToBounds="True">
|
||||
<TextBlock Text="选择图片" Foreground="White" Padding="0,5" FontSize="11" FontWeight="Bold" Canvas.Left="8" TextAlignment="Center" />
|
||||
<Image Margin="100,4,0,0" Source="/Resources/new-icons/close-white.png" RenderOptions.BitmapScalingMode="HighQuality" Height="12" Width="12" MouseDown="Border_MouseDown" MouseUp="CloseImageOptionsPanel_MouseUp" />
|
||||
<Image Margin="77,4,0,0" Source="/Resources/new-icons/close-white.png" RenderOptions.BitmapScalingMode="HighQuality" Height="16" Width="16" MouseDown="Border_MouseDown" MouseUp="CloseImageOptionsPanel_MouseUp" />
|
||||
</Canvas>
|
||||
</Border>
|
||||
<ui:SimpleStackPanel Margin="6,4,6,4" Spacing="2">
|
||||
@@ -6522,7 +6577,7 @@
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border x:Name="BoardUndo" Width="60" Height="50" MouseDown="Border_MouseDown" MouseUp="SymbolIconUndo_MouseUp" BorderThickness="0,1,0,1" BorderBrush="{DynamicResource BoardFloatBarBorderBrush}" IsEnabled="{Binding ElementName=BtnUndo, Path=IsEnabled}" Background="{DynamicResource BoardFloatBarBackground}" Opacity="0.95">
|
||||
<Border x:Name="BoardUndo" Width="60" Height="50" MouseDown="Border_MouseDown" MouseUp="SymbolIconUndo_MouseUp" BorderThickness="0,1,0,1" BorderBrush="{DynamicResource BoardFloatBarBorderBrush}" IsEnabled="{Binding ElementName=BtnUndo, Path=IsEnabled}" Background="{DynamicResource BoardFloatBarBackground}" Opacity="1">
|
||||
<Grid Margin="6,6,6,4">
|
||||
<Image VerticalAlignment="Top"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="20" Width="20" Opacity="{Binding ElementName=BtnUndo, Path=IsEnabled, Converter={StaticResource IsEnabledToOpacityConverter}}">
|
||||
@@ -6540,7 +6595,7 @@
|
||||
<TextBlock Text="撤销" Foreground="{DynamicResource FloatBarForeground}" VerticalAlignment="Bottom" HorizontalAlignment="Center" FontSize="12" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border x:Name="BoardRedo" Width="60" Height="50" CornerRadius="0,5,5,0" MouseDown="Border_MouseDown" MouseUp="SymbolIconRedo_MouseUp" BorderThickness="0,1,1,1" BorderBrush="{DynamicResource BoardFloatBarBorderBrush}" IsEnabled="{Binding ElementName=BtnRedo, Path=IsEnabled}" Background="{DynamicResource BoardFloatBarBackground}" Opacity="0.95">
|
||||
<Border x:Name="BoardRedo" Width="60" Height="50" CornerRadius="0,5,5,0" MouseDown="Border_MouseDown" MouseUp="SymbolIconRedo_MouseUp" BorderThickness="0,1,1,1" BorderBrush="{DynamicResource BoardFloatBarBorderBrush}" IsEnabled="{Binding ElementName=BtnRedo, Path=IsEnabled}" Background="{DynamicResource BoardFloatBarBackground}" Opacity="1">
|
||||
<Grid Margin="6,6,6,4">
|
||||
<Image VerticalAlignment="Top" RenderOptions.BitmapScalingMode="HighQuality" Height="20" Width="20" Opacity="{Binding ElementName=BtnRedo, Path=IsEnabled, Converter={StaticResource IsEnabledToOpacityConverter}}">
|
||||
<Image.Source>
|
||||
@@ -6564,7 +6619,7 @@
|
||||
<Border CornerRadius="5,5,5,5" Background="{DynamicResource BoardFloatBarBackground}">
|
||||
<ui:SimpleStackPanel Orientation="Horizontal">
|
||||
<Border Width="60" Height="50" MouseDown="Border_MouseDown"
|
||||
MouseUp="SymbolIconTools_MouseUp" Background="{DynamicResource BoardFloatBarBackground}" Opacity="0.95"
|
||||
MouseUp="SymbolIconTools_MouseUp" Background="{DynamicResource BoardFloatBarBackground}" Opacity="1"
|
||||
BorderThickness="1,1,0,1" BorderBrush="{DynamicResource BoardFloatBarBorderBrush}"
|
||||
CornerRadius="5,0,0,5">
|
||||
<Grid Margin="6,6,6,4">
|
||||
@@ -6817,7 +6872,7 @@
|
||||
</Border>
|
||||
<Border Width="60" Height="50" MouseDown="Border_MouseDown"
|
||||
MouseUp="ImageBlackboard_MouseUp"
|
||||
CornerRadius="0,5,5,0" Background="{DynamicResource BoardFloatBarBackground}" Opacity="0.95"
|
||||
CornerRadius="0,5,5,0" Background="{DynamicResource BoardFloatBarBackground}" Opacity="1"
|
||||
BorderThickness="0,1,1,1"
|
||||
BorderBrush="{DynamicResource BoardFloatBarBorderBrush}">
|
||||
<Grid Margin="6,6,6,4">
|
||||
@@ -6853,7 +6908,7 @@
|
||||
<Border Width="60" Height="50"
|
||||
BorderThickness="1" BorderBrush="{DynamicResource BoardFloatBarBorderBrush}"
|
||||
MouseUp="BtnWhiteBoardAdd_Click" CornerRadius="5" Background="{DynamicResource BoardFloatBarBackground}"
|
||||
Opacity="0.95">
|
||||
Opacity="1">
|
||||
<Grid Margin="6,6,6,4">
|
||||
<Image VerticalAlignment="Top"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="20" Width="20">
|
||||
@@ -6879,7 +6934,7 @@
|
||||
BorderThickness="1,1,0,1" BorderBrush="{DynamicResource BoardFloatBarBorderBrush}"
|
||||
MouseUp="BtnWhiteBoardSwitchPrevious_Click" CornerRadius="5,0,0,5"
|
||||
Background="{DynamicResource BoardFloatBarBackground}"
|
||||
Opacity="0.95">
|
||||
Opacity="1">
|
||||
<Grid Margin="6,6,6,4">
|
||||
<Image VerticalAlignment="Top"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="20" Width="20">
|
||||
@@ -6903,7 +6958,7 @@
|
||||
<Border Width="75" Height="50" MouseUp="BtnWhiteBoardPageIndex_Click"
|
||||
Name="BtnRightPageListWB"
|
||||
BorderThickness="1,1,1,1" BorderBrush="{DynamicResource BoardFloatBarBorderBrush}" Background="{DynamicResource BoardFloatBarBackground}"
|
||||
Opacity="0.95">
|
||||
Opacity="1">
|
||||
<Grid Margin="6,6,6,4">
|
||||
<TextBlock HorizontalAlignment="Center"
|
||||
Text="{Binding ElementName=TextBlockWhiteBoardIndexInfo, Path=Text}"
|
||||
@@ -6969,7 +7024,7 @@
|
||||
IsEnabled="{Binding ElementName=BtnWhiteBoardSwitchNext, Path=IsEnabled}"
|
||||
CornerRadius="0,5,5,0"
|
||||
BorderThickness="0,1,1,1" BorderBrush="{DynamicResource BoardFloatBarBorderBrush}"
|
||||
Background="{DynamicResource BoardFloatBarBackground}" Opacity="0.95">
|
||||
Background="{DynamicResource BoardFloatBarBackground}" Opacity="1">
|
||||
<Grid Margin="6,6,6,4">
|
||||
<Image VerticalAlignment="Top"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="20" Width="20">
|
||||
@@ -7502,7 +7557,7 @@
|
||||
|
||||
<Grid Name="FloatingbarUIForInkReplay">
|
||||
<Viewbox Name="ViewboxFloatingBar" Margin="100,5,0,0" Cursor="Arrow"
|
||||
HorizontalAlignment="Left" Height="58" VerticalAlignment="Top" MinWidth="200" MaxWidth="1200"
|
||||
HorizontalAlignment="Left" Height="58" VerticalAlignment="Top" Width="1200"
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<Viewbox.LayoutTransform>
|
||||
<ScaleTransform x:Name="ViewboxFloatingBarScaleTransform" ScaleX="1" ScaleY="1" />
|
||||
@@ -8318,7 +8373,7 @@
|
||||
IsOn="{Binding ElementName=ToggleSwitchEnableInkToShape, Path=IsOn}" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal"
|
||||
Width="100">
|
||||
Width="100" x:Name="InkFadeControlPanel2">
|
||||
<Label Margin="0,0,10,0" Content="墨迹渐隐"
|
||||
Foreground="{DynamicResource FloatBarForeground}"
|
||||
FontSize="12" VerticalAlignment="Center" />
|
||||
|
||||
@@ -530,10 +530,13 @@ namespace Ink_Canvas
|
||||
//加载设置
|
||||
LoadSettings(true);
|
||||
AutoBackupManager.Initialize(Settings);
|
||||
CheckUpdateChannelAndTelemetryConsistency();
|
||||
|
||||
// 初始化Dlass上传队列(恢复上次的上传队列)
|
||||
DlassNoteUploader.InitializeQueue();
|
||||
|
||||
_ = TelemetryUploader.UploadTelemetryIfNeededAsync();
|
||||
|
||||
// 检查保存路径是否可用,不可用则修正
|
||||
try
|
||||
{
|
||||
@@ -2692,6 +2695,57 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在笔工具菜单中隐藏墨迹渐隐控制开关切换事件处理
|
||||
/// </summary>
|
||||
private void ToggleSwitchHideInkFadeControlInPenMenu_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (isLoaded)
|
||||
{
|
||||
Settings.Canvas.HideInkFadeControlInPenMenu = ToggleSwitchHideInkFadeControlInPenMenu.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
// 立即更新墨迹渐隐控制开关的可见性
|
||||
UpdateInkFadeControlVisibility();
|
||||
|
||||
LogHelper.WriteLogToFile($"在笔工具菜单中隐藏墨迹渐隐控制开关已{(Settings.Canvas.HideInkFadeControlInPenMenu ? "启用" : "禁用")}", LogHelper.LogType.Event);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"切换在笔工具菜单中隐藏墨迹渐隐控制开关时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新墨迹渐隐控制开关的可见性
|
||||
/// </summary>
|
||||
private void UpdateInkFadeControlVisibility()
|
||||
{
|
||||
try
|
||||
{
|
||||
bool isHidden = Settings.Canvas.HideInkFadeControlInPenMenu;
|
||||
|
||||
// 控制 InkFadeControlPanel1(批注子面板中)的可见性
|
||||
if (InkFadeControlPanel1 != null)
|
||||
{
|
||||
InkFadeControlPanel1.Visibility = isHidden ? Visibility.Collapsed : Visibility.Visible;
|
||||
}
|
||||
|
||||
// 控制 InkFadeControlPanel2(普通画笔面板中)的可见性
|
||||
if (InkFadeControlPanel2 != null)
|
||||
{
|
||||
InkFadeControlPanel2.Visibility = isHidden ? Visibility.Collapsed : Visibility.Visible;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"更新墨迹渐隐控制面板可见性时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PPT放映模式显示手势按钮开关切换事件处理
|
||||
/// </summary>
|
||||
@@ -2957,6 +3011,12 @@ namespace Ink_Canvas
|
||||
{
|
||||
try
|
||||
{
|
||||
// 如果切换到非橡皮擦模式,禁用橡皮擦覆盖层并重置橡皮擦状态
|
||||
if (newMode != InkCanvasEditingMode.EraseByPoint && newMode != InkCanvasEditingMode.EraseByStroke)
|
||||
{
|
||||
DisableEraserOverlay();
|
||||
}
|
||||
|
||||
// 执行模式切换
|
||||
inkCanvas.EditingMode = newMode;
|
||||
|
||||
|
||||
@@ -54,7 +54,10 @@ namespace Ink_Canvas
|
||||
if (sender == Fold_Icon && lastBorderMouseDownObject != Fold_Icon) isShouldRejectAction = true;
|
||||
});
|
||||
|
||||
if (isShouldRejectAction) return;
|
||||
if (isShouldRejectAction)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// FloatingBarIcons_MouseUp_New(sender);
|
||||
if (sender == null)
|
||||
@@ -65,7 +68,10 @@ namespace Ink_Canvas
|
||||
|
||||
if (isFloatingBarFolded) return;
|
||||
|
||||
if (isFloatingBarChangingHideMode) return;
|
||||
if (isFloatingBarChangingHideMode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
@@ -105,6 +111,15 @@ namespace Ink_Canvas
|
||||
HideSubPanels("cursor");
|
||||
SidePannelMarginAnimation(-10);
|
||||
});
|
||||
|
||||
// 新增:如果开启了彻底隐藏,则隐藏主窗口
|
||||
if (Settings.Automation.ThoroughlyHideWhenFolded)
|
||||
{
|
||||
await Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
this.Visibility = Visibility.Hidden;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async void LeftUnFoldButtonDisplayQuickPanel_MouseUp(object sender, MouseButtonEventArgs e)
|
||||
@@ -230,6 +245,15 @@ namespace Ink_Canvas
|
||||
|
||||
public async Task UnFoldFloatingBar(object sender)
|
||||
{
|
||||
// 新增:如果之前彻底隐藏了,先恢复显示
|
||||
if (this.Visibility != Visibility.Visible)
|
||||
{
|
||||
await Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
this.Visibility = Visibility.Visible;
|
||||
});
|
||||
}
|
||||
|
||||
await Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
LeftUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
|
||||
@@ -241,8 +265,12 @@ namespace Ink_Canvas
|
||||
unfoldFloatingBarByUser = true;
|
||||
foldFloatingBarByUser = false;
|
||||
|
||||
if (isFloatingBarChangingHideMode) return;
|
||||
if (isFloatingBarChangingHideMode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
await Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
isFloatingBarChangingHideMode = true;
|
||||
|
||||
@@ -40,31 +40,12 @@ namespace Ink_Canvas
|
||||
{
|
||||
if (TwoFingerGestureBorder.Visibility == Visibility.Visible)
|
||||
{
|
||||
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(PenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
|
||||
}
|
||||
else
|
||||
{
|
||||
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(PenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
HideSubPanels();
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(TwoFingerGestureBorder);
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardTwoFingerGestureBorder);
|
||||
}
|
||||
@@ -285,9 +266,16 @@ namespace Ink_Canvas
|
||||
BoardBorderLeftPageListView.Visibility = Visibility.Collapsed;
|
||||
BoardBorderRightPageListView.Visibility = Visibility.Collapsed;
|
||||
BoardImageOptionsPanel.Visibility = Visibility.Collapsed;
|
||||
TwoFingerGestureBorder.Visibility = Visibility.Collapsed;
|
||||
BoardTwoFingerGestureBorder.Visibility = Visibility.Collapsed;
|
||||
// 添加隐藏图形工具的二级菜单面板
|
||||
BorderDrawShape.Visibility = Visibility.Collapsed;
|
||||
BoardBorderDrawShape.Visibility = Visibility.Collapsed;
|
||||
|
||||
if (LogicalTreeHelper.FindLogicalNode(this, "BackgroundPalette") is Border bgPalette)
|
||||
{
|
||||
bgPalette.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -361,6 +349,8 @@ namespace Ink_Canvas
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderLeftPageListView);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderRightPageListView);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
|
||||
|
||||
// 隐藏背景设置面板
|
||||
if (LogicalTreeHelper.FindLogicalNode(this, "BackgroundPalette") is Border bgPalette)
|
||||
@@ -1567,37 +1557,16 @@ namespace Ink_Canvas
|
||||
|
||||
if (BorderTools.Visibility == Visibility.Visible)
|
||||
{
|
||||
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(PenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
|
||||
}
|
||||
else
|
||||
{
|
||||
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(PenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
|
||||
HideSubPanels();
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(BorderTools);
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardBorderTools);
|
||||
}
|
||||
|
||||
|
||||
if (sender == ToolsFloatingBarBtn)
|
||||
{
|
||||
lastBorderMouseDownObject = null;
|
||||
@@ -2257,33 +2226,12 @@ namespace Ink_Canvas
|
||||
|
||||
if (PenPalette.Visibility == Visibility.Visible)
|
||||
{
|
||||
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(PenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
|
||||
}
|
||||
else
|
||||
{
|
||||
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
|
||||
HideSubPanels();
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(PenPalette);
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardPenPalette);
|
||||
}
|
||||
@@ -3127,7 +3075,9 @@ namespace Ink_Canvas
|
||||
ViewboxFloatingBar.Visibility = Visibility.Visible;
|
||||
|
||||
// 退出白板时自动收纳功能 - 等待浮动栏完全展开后再收纳
|
||||
if (Settings.Automation.IsAutoFoldWhenExitWhiteboard && !isFloatingBarFolded)
|
||||
// 当处于PPT放映模式时,不自动收纳
|
||||
if (Settings.Automation.IsAutoFoldWhenExitWhiteboard && !isFloatingBarFolded &&
|
||||
BtnPPTSlideShowEnd.Visibility != Visibility.Visible)
|
||||
{
|
||||
// 使用异步延迟,等待浮动栏展开动画完成后再收纳
|
||||
Task.Run(async () =>
|
||||
@@ -3416,7 +3366,7 @@ namespace Ink_Canvas
|
||||
else
|
||||
{
|
||||
// Panel was hidden, so hide other panels and show this one
|
||||
HideSubPanelsImmediately();
|
||||
HideSubPanels();
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardImageOptionsPanel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,9 +70,9 @@ namespace Ink_Canvas
|
||||
|
||||
#region PPT Application Variables
|
||||
public static Microsoft.Office.Interop.PowerPoint.Application pptApplication;
|
||||
public static Presentation presentation;
|
||||
public static Slides slides;
|
||||
public static Slide slide;
|
||||
public static dynamic presentation;
|
||||
public static dynamic slides;
|
||||
public static dynamic slide;
|
||||
public static int slidescount;
|
||||
#endregion
|
||||
|
||||
@@ -105,14 +105,13 @@ namespace Ink_Canvas
|
||||
#endregion
|
||||
|
||||
#region PPT Managers
|
||||
private PPTManager _pptManager;
|
||||
private IPPTLinkManager _pptManager;
|
||||
private PPTInkManager _singlePPTInkManager;
|
||||
private PPTUIManager _pptUIManager;
|
||||
|
||||
/// <summary>
|
||||
/// 获取PPT管理器实例
|
||||
/// </summary>
|
||||
public PPTManager PPTManager => _pptManager;
|
||||
private bool IsUsingRotPptLink => Settings.PowerPointSettings.UseRotPptLink;
|
||||
|
||||
public IPPTLinkManager PPTManager => _pptManager;
|
||||
#endregion
|
||||
|
||||
#region PPT Manager Initialization
|
||||
@@ -120,11 +119,25 @@ namespace Ink_Canvas
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
_pptManager?.StopMonitoring();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
// 初始化长按定时器
|
||||
InitializeLongPressTimer();
|
||||
|
||||
// 初始化PPT管理器
|
||||
_pptManager = new PPTManager();
|
||||
if (IsUsingRotPptLink)
|
||||
{
|
||||
_pptManager = new PPTManager();
|
||||
}
|
||||
else
|
||||
{
|
||||
_pptManager = new ComPPTManager();
|
||||
}
|
||||
_pptManager.IsSupportWPS = Settings.PowerPointSettings.IsSupportWPS;
|
||||
|
||||
// 注册事件
|
||||
@@ -133,7 +146,6 @@ namespace Ink_Canvas
|
||||
_pptManager.SlideShowNextSlide += OnPPTSlideShowNextSlide;
|
||||
_pptManager.SlideShowEnd += OnPPTSlideShowEnd;
|
||||
_pptManager.PresentationOpen += OnPPTPresentationOpen;
|
||||
_pptManager.PresentationClose += OnPPTPresentationClose;
|
||||
_pptManager.SlideShowStateChanged += OnPPTSlideShowStateChanged;
|
||||
|
||||
_singlePPTInkManager = new PPTInkManager();
|
||||
@@ -172,8 +184,17 @@ namespace Ink_Canvas
|
||||
|
||||
private void StopPPTMonitoring()
|
||||
{
|
||||
_pptManager?.StopMonitoring();
|
||||
LogHelper.WriteLogToFile("PPT监控已停止", LogHelper.LogType.Event);
|
||||
try
|
||||
{
|
||||
_pptManager?.StopMonitoring();
|
||||
_pptManager?.Dispose();
|
||||
_pptManager = null;
|
||||
LogHelper.WriteLogToFile("PPT监控已停止并释放当前 PPT 管理器实例", LogHelper.LogType.Event);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"停止PPT监控或释放PPT管理器失败: {ex}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
#region PowerPoint Application Management
|
||||
@@ -520,10 +541,11 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPPTPresentationOpen(Presentation pres)
|
||||
private void OnPPTPresentationOpen(object pres)
|
||||
{
|
||||
try
|
||||
{
|
||||
dynamic presObj = pres;
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
// 在初始化墨迹管理器之前,先清理画布上的所有墨迹
|
||||
@@ -535,26 +557,26 @@ namespace Ink_Canvas
|
||||
TimeMachineHistories[0] = null;
|
||||
}
|
||||
|
||||
_singlePPTInkManager?.InitializePresentation(pres);
|
||||
_singlePPTInkManager?.InitializePresentation(presObj as Presentation);
|
||||
|
||||
// 处理跳转到首页或上次播放页的逻辑
|
||||
HandlePresentationOpenNavigation(pres);
|
||||
HandlePresentationOpenNavigation(presObj);
|
||||
|
||||
// 检查隐藏幻灯片
|
||||
if (Settings.PowerPointSettings.IsNotifyHiddenPage)
|
||||
{
|
||||
CheckAndNotifyHiddenSlides(pres);
|
||||
CheckAndNotifyHiddenSlides(presObj);
|
||||
}
|
||||
|
||||
// 检查自动播放设置
|
||||
if (Settings.PowerPointSettings.IsNotifyAutoPlayPresentation)
|
||||
{
|
||||
CheckAndNotifyAutoPlaySettings(pres);
|
||||
CheckAndNotifyAutoPlaySettings(presObj);
|
||||
}
|
||||
|
||||
_pptUIManager?.UpdateConnectionStatus(true);
|
||||
|
||||
LogHelper.WriteLogToFile($"已打开新演示文稿: {pres.Name},墨迹状态已清理", LogHelper.LogType.Event);
|
||||
LogHelper.WriteLogToFile($"已打开新演示文稿: {presObj.Name},墨迹状态已清理", LogHelper.LogType.Event);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -610,10 +632,11 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnPPTSlideShowBegin(SlideShowWindow wn)
|
||||
private async void OnPPTSlideShowBegin(object wn)
|
||||
{
|
||||
try
|
||||
{
|
||||
dynamic wnObj = wn;
|
||||
if (Settings.Automation.IsAutoFoldInPPTSlideShow)
|
||||
{
|
||||
if (!isFloatingBarFolded)
|
||||
@@ -631,14 +654,14 @@ namespace Ink_Canvas
|
||||
|
||||
await Application.Current.Dispatcher.InvokeAsync(async () =>
|
||||
{
|
||||
Presentation activePresentation = null;
|
||||
dynamic activePresentation = null;
|
||||
int currentSlide = 0;
|
||||
int totalSlides = 0;
|
||||
|
||||
if (wn?.View != null && wn.Presentation != null)
|
||||
if (wnObj?.View != null && wnObj.Presentation != null)
|
||||
{
|
||||
activePresentation = wn.Presentation;
|
||||
currentSlide = wn.View.CurrentShowPosition;
|
||||
activePresentation = wnObj.Presentation;
|
||||
currentSlide = wnObj.View.CurrentShowPosition;
|
||||
totalSlides = activePresentation.Slides.Count;
|
||||
// 初始化当前播放页码跟踪
|
||||
_currentSlideShowPosition = currentSlide;
|
||||
@@ -658,7 +681,7 @@ namespace Ink_Canvas
|
||||
{
|
||||
try
|
||||
{
|
||||
_singlePPTInkManager.InitializePresentation(activePresentation);
|
||||
_singlePPTInkManager.InitializePresentation(activePresentation as Presentation);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -784,19 +807,20 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPPTSlideShowNextSlide(SlideShowWindow wn)
|
||||
private void OnPPTSlideShowNextSlide(object wn)
|
||||
{
|
||||
try
|
||||
{
|
||||
dynamic wnObj = wn;
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
if (wn?.View == null || wn.Presentation == null)
|
||||
if (wnObj?.View == null || wnObj.Presentation == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var currentSlide = wn.View.CurrentShowPosition;
|
||||
var activePresentation = wn.Presentation;
|
||||
var currentSlide = wnObj.View.CurrentShowPosition;
|
||||
dynamic activePresentation = wnObj.Presentation;
|
||||
var totalSlides = activePresentation.Slides.Count;
|
||||
|
||||
// 更新当前播放页码
|
||||
@@ -813,10 +837,11 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnPPTSlideShowEnd(Presentation pres)
|
||||
private async void OnPPTSlideShowEnd(object pres)
|
||||
{
|
||||
try
|
||||
{
|
||||
dynamic presObj = pres;
|
||||
if (Settings.Automation.IsAutoFoldAfterPPTSlideShow && !isFloatingBarFolded)
|
||||
{
|
||||
FoldFloatingBar_MouseUp(new object(), null);
|
||||
@@ -838,9 +863,9 @@ namespace Ink_Canvas
|
||||
// 如果无法获取,尝试从演示文稿的SlideShowWindow获取
|
||||
try
|
||||
{
|
||||
if (pres.SlideShowWindow != null && pres.SlideShowWindow.View != null)
|
||||
if (presObj.SlideShowWindow != null && presObj.SlideShowWindow.View != null)
|
||||
{
|
||||
currentPage = pres.SlideShowWindow.View.CurrentShowPosition;
|
||||
currentPage = presObj.SlideShowWindow.View.CurrentShowPosition;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
@@ -848,7 +873,7 @@ namespace Ink_Canvas
|
||||
}
|
||||
|
||||
// 保存墨迹和位置信息
|
||||
_singlePPTInkManager?.SaveAllStrokesToFile(pres, currentPage);
|
||||
_singlePPTInkManager?.SaveAllStrokesToFile(presObj as Presentation, currentPage);
|
||||
|
||||
await Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
@@ -948,7 +973,7 @@ namespace Ink_Canvas
|
||||
#endregion
|
||||
|
||||
#region Helper Methods
|
||||
private void HandlePresentationOpenNavigation(Presentation pres)
|
||||
private void HandlePresentationOpenNavigation(dynamic pres)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -967,7 +992,7 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowPreviousPageNotification(Presentation pres)
|
||||
private void ShowPreviousPageNotification(dynamic pres)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -981,7 +1006,7 @@ namespace Ink_Canvas
|
||||
|
||||
if (!File.Exists(positionFile)) return;
|
||||
|
||||
if (int.TryParse(File.ReadAllText(positionFile), out var page) && page > 0)
|
||||
if (int.TryParse(File.ReadAllText(positionFile), out int page) && page > 0)
|
||||
{
|
||||
_lastPlaybackPage = page;
|
||||
new YesOrNoNotificationWindow($"上次播放到了第 {page} 页, 是否立即跳转", () =>
|
||||
@@ -990,7 +1015,8 @@ namespace Ink_Canvas
|
||||
{
|
||||
if (_pptManager?.PPTApplication != null)
|
||||
{
|
||||
if (_pptManager.PPTApplication.SlideShowWindows.Count >= 1)
|
||||
dynamic pptApp = _pptManager.PPTApplication;
|
||||
if (pptApp.SlideShowWindows.Count >= 1)
|
||||
{
|
||||
pres.SlideShowWindow.View.GotoSlide(page);
|
||||
}
|
||||
@@ -1013,14 +1039,14 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckAndNotifyHiddenSlides(Presentation pres)
|
||||
private void CheckAndNotifyHiddenSlides(dynamic pres)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool hasHiddenSlides = false;
|
||||
if (pres?.Slides != null)
|
||||
{
|
||||
foreach (Slide slide in pres.Slides)
|
||||
foreach (dynamic slide in pres.Slides)
|
||||
{
|
||||
if (slide.SlideShowTransition.Hidden == MsoTriState.msoTrue)
|
||||
{
|
||||
@@ -1040,7 +1066,7 @@ namespace Ink_Canvas
|
||||
{
|
||||
if (pres?.Slides != null)
|
||||
{
|
||||
foreach (Slide slide in pres.Slides)
|
||||
foreach (dynamic slide in pres.Slides)
|
||||
{
|
||||
if (slide.SlideShowTransition.Hidden == MsoTriState.msoTrue)
|
||||
slide.SlideShowTransition.Hidden = MsoTriState.msoFalse;
|
||||
@@ -1066,7 +1092,7 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckAndNotifyAutoPlaySettings(Presentation pres)
|
||||
private void CheckAndNotifyAutoPlaySettings(dynamic pres)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -1075,7 +1101,7 @@ namespace Ink_Canvas
|
||||
bool hasSlideTimings = false;
|
||||
if (pres?.Slides != null)
|
||||
{
|
||||
foreach (Slide slide in pres.Slides)
|
||||
foreach (dynamic slide in pres.Slides)
|
||||
{
|
||||
if (slide.SlideShowTransition.AdvanceOnTime == MsoTriState.msoTrue &&
|
||||
slide.SlideShowTransition.AdvanceTime > 0)
|
||||
@@ -1628,31 +1654,25 @@ namespace Ink_Canvas
|
||||
});
|
||||
}
|
||||
|
||||
// 结束放映
|
||||
if (_pptManager?.TryEndSlideShow() == true)
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
// 如果成功结束放映,等待OnPPTSlideShowEnd事件处理收纳状态恢复
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile("结束幻灯片放映失败", LogHelper.LogType.Warning);
|
||||
CursorIcon_Click(null, null);
|
||||
});
|
||||
|
||||
// 手动更新UI状态,防止事件未触发
|
||||
await Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
await Task.Delay(100);
|
||||
await Application.Current.Dispatcher.InvokeAsync(() => { }, System.Windows.Threading.DispatcherPriority.Background);
|
||||
|
||||
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
_pptUIManager?.UpdateSlideShowStatus(false);
|
||||
_pptUIManager?.UpdateSidebarExitButtons(false);
|
||||
LogHelper.WriteLogToFile("手动更新放映结束UI状态", LogHelper.LogType.Trace);
|
||||
});
|
||||
|
||||
// 手动处理自动收纳,因为OnPPTSlideShowEnd事件可能未触发
|
||||
await HandleManualSlideShowEnd();
|
||||
}
|
||||
|
||||
HideSubPanels("cursor");
|
||||
SetCurrentToolMode(InkCanvasEditingMode.None);
|
||||
|
||||
await Task.Delay(150);
|
||||
_pptManager?.TryEndSlideShow();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"结束放映时发生异常: {ex}", LogHelper.LogType.Error);
|
||||
}
|
||||
}), System.Windows.Threading.DispatcherPriority.Normal);
|
||||
if (!isFloatingBarFolded)
|
||||
{
|
||||
PureViewboxFloatingBarMarginAnimationInDesktopMode();
|
||||
@@ -6,6 +6,8 @@ using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
@@ -30,6 +32,523 @@ namespace Ink_Canvas
|
||||
{
|
||||
#region Behavior
|
||||
|
||||
private bool _isChangingUpdateChannelInternally;
|
||||
private bool _isChangingTelemetryInternally;
|
||||
private bool _isChangingTelemetryPrivacyInternally;
|
||||
|
||||
private static bool PrivacyFileExists()
|
||||
{
|
||||
try
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var resourceName = "Ink_Canvas.privacy.txt";
|
||||
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
|
||||
{
|
||||
return stream != null;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static string FindPrivacyFile()
|
||||
{
|
||||
// 先尝试从文件系统读取
|
||||
string assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
string[] searchPaths = new string[]
|
||||
{
|
||||
App.RootPath,
|
||||
assemblyDir,
|
||||
Path.GetDirectoryName(assemblyDir) // One level up from assembly directory
|
||||
};
|
||||
|
||||
string foundPath = null;
|
||||
foreach (string searchPath in searchPaths)
|
||||
{
|
||||
if (string.IsNullOrEmpty(searchPath)) continue;
|
||||
|
||||
string testPathTxt = Path.Combine(searchPath, "privacy.txt");
|
||||
string testPathNoExt = Path.Combine(searchPath, "privacy");
|
||||
|
||||
if (File.Exists(testPathTxt))
|
||||
{
|
||||
foundPath = testPathTxt;
|
||||
break;
|
||||
}
|
||||
else if (File.Exists(testPathNoExt))
|
||||
{
|
||||
foundPath = testPathNoExt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundPath != null && File.Exists(foundPath))
|
||||
{
|
||||
return foundPath;
|
||||
}
|
||||
|
||||
// 回退到嵌入资源
|
||||
try
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var resourceName = "Ink_Canvas.privacy.txt";
|
||||
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
|
||||
{
|
||||
if (stream != null)
|
||||
{
|
||||
// 返回特殊字符串表示来自嵌入资源
|
||||
return "embedded";
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"从程序集资源查找隐私说明失败: {ex.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void CheckUpdateChannelAndTelemetryConsistency()
|
||||
{
|
||||
var currentChannel = Settings.Startup.UpdateChannel;
|
||||
bool isTestChannel = currentChannel == UpdateChannel.Preview || currentChannel == UpdateChannel.Beta;
|
||||
|
||||
if (!isTestChannel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!Settings.Startup.HasAcceptedTelemetryPrivacy)
|
||||
{
|
||||
var result = MessageBox.Show(
|
||||
$"检测到您当前处于 {currentChannel} 通道,但尚未同意隐私说明。\n\n" +
|
||||
"使用预览/测试通道需要同意隐私说明并启用匿名使用数据上传。\n\n" +
|
||||
"是否现在同意隐私说明并启用基础数据上传?\n" +
|
||||
"(选择“否”将自动切换回正式通道)",
|
||||
"更新通道与隐私协议不匹配",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Warning);
|
||||
|
||||
if (result == MessageBoxResult.Yes)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用 FindPrivacyFile 方法,支持文件系统和嵌入资源
|
||||
string privacyPath = FindPrivacyFile();
|
||||
|
||||
if (string.IsNullOrEmpty(privacyPath))
|
||||
{
|
||||
MessageBox.Show(
|
||||
"未找到隐私说明文件(privacy.txt 或 privacy)。\n\n将切换回正式通道。",
|
||||
"隐私文件缺失",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
var privacyWindow = new PrivacyAgreementWindow();
|
||||
privacyWindow.Owner = this;
|
||||
bool? dialogResult = privacyWindow.ShowDialog();
|
||||
|
||||
if (dialogResult == true && privacyWindow.UserAccepted)
|
||||
{
|
||||
// 用户同意,保存设置
|
||||
Settings.Startup.HasAcceptedTelemetryPrivacy = true;
|
||||
Settings.Startup.TelemetryUploadLevel = TelemetryUploadLevel.Basic;
|
||||
|
||||
// 更新UI,即使 isLoaded 为 false(启动时)
|
||||
// 使用标志避免触发事件处理程序
|
||||
// 使用 Dispatcher 确保在 UI 线程上更新
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
_isChangingTelemetryPrivacyInternally = true;
|
||||
_isChangingTelemetryInternally = true;
|
||||
try
|
||||
{
|
||||
if (CheckBoxTelemetryPrivacyAccepted != null)
|
||||
{
|
||||
CheckBoxTelemetryPrivacyAccepted.IsChecked = true;
|
||||
}
|
||||
if (ComboBoxTelemetryUploadLevel != null)
|
||||
{
|
||||
ComboBoxTelemetryUploadLevel.SelectedIndex = 1;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isChangingTelemetryPrivacyInternally = false;
|
||||
_isChangingTelemetryInternally = false;
|
||||
}
|
||||
}), DispatcherPriority.Normal);
|
||||
|
||||
SaveSettingsToFile();
|
||||
DeviceIdentifier.UpdateUsageChannel(currentChannel);
|
||||
LogHelper.WriteLogToFile($"启动检测 | 用户同意隐私协议并启用基础遥测,保持 {currentChannel} 通道");
|
||||
return; // 用户同意,直接返回,不执行后面的切换通道逻辑
|
||||
}
|
||||
else
|
||||
{
|
||||
// 用户取消或关闭窗口,切换回正式通道
|
||||
LogHelper.WriteLogToFile($"启动检测 | 用户取消隐私协议,将切换回 Release 通道");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"启动检测 | 读取隐私文件失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show(
|
||||
"读取隐私说明文件时出错。\n\n将切换回正式通道。",
|
||||
"读取隐私文件失败",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 用户选择"否",切换回正式通道
|
||||
LogHelper.WriteLogToFile($"启动检测 | 用户选择不同意隐私协议,将切换回 Release 通道");
|
||||
}
|
||||
|
||||
// 只有在用户不同意或取消的情况下,才切换回正式通道
|
||||
Settings.Startup.UpdateChannel = UpdateChannel.Release;
|
||||
DeviceIdentifier.UpdateUsageChannel(UpdateChannel.Release);
|
||||
SaveSettingsToFile();
|
||||
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
_isChangingUpdateChannelInternally = true;
|
||||
try
|
||||
{
|
||||
if (isLoaded && UpdateChannelSelector != null)
|
||||
{
|
||||
foreach (var item in UpdateChannelSelector.Items)
|
||||
{
|
||||
if (item is RadioButton rb && rb.Tag != null &&
|
||||
string.Equals(rb.Tag.ToString(), "Release", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
rb.IsChecked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isChangingUpdateChannelInternally = false;
|
||||
}
|
||||
}), DispatcherPriority.Normal);
|
||||
|
||||
LogHelper.WriteLogToFile($"启动检测 | 用户未同意隐私协议,已切换回 Release 通道");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Settings.Startup.TelemetryUploadLevel == TelemetryUploadLevel.None)
|
||||
{
|
||||
var result = MessageBox.Show(
|
||||
$"检测到您当前处于 {currentChannel} 通道,但匿名使用数据上传已关闭。\n\n" +
|
||||
"使用预览/测试通道需要启用匿名使用数据上传。\n\n" +
|
||||
"是否现在启用基础数据上传?\n" +
|
||||
"(选择“否”将自动切换回正式通道)",
|
||||
"更新通道与遥测状态不匹配",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Warning);
|
||||
|
||||
if (result == MessageBoxResult.Yes)
|
||||
{
|
||||
Settings.Startup.TelemetryUploadLevel = TelemetryUploadLevel.Basic;
|
||||
|
||||
if (isLoaded && ComboBoxTelemetryUploadLevel != null)
|
||||
{
|
||||
ComboBoxTelemetryUploadLevel.SelectedIndex = 1;
|
||||
}
|
||||
|
||||
SaveSettingsToFile();
|
||||
DeviceIdentifier.UpdateUsageChannel(currentChannel);
|
||||
LogHelper.WriteLogToFile($"启动检测 | 用户启用基础遥测,保持 {currentChannel} 通道");
|
||||
}
|
||||
else
|
||||
{
|
||||
Settings.Startup.UpdateChannel = UpdateChannel.Release;
|
||||
DeviceIdentifier.UpdateUsageChannel(UpdateChannel.Release);
|
||||
SaveSettingsToFile();
|
||||
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
_isChangingUpdateChannelInternally = true;
|
||||
try
|
||||
{
|
||||
if (isLoaded && UpdateChannelSelector != null)
|
||||
{
|
||||
foreach (var item in UpdateChannelSelector.Items)
|
||||
{
|
||||
if (item is RadioButton rb && rb.Tag != null &&
|
||||
string.Equals(rb.Tag.ToString(), "Release", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
rb.IsChecked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isChangingUpdateChannelInternally = false;
|
||||
}
|
||||
}), DispatcherPriority.Normal);
|
||||
|
||||
LogHelper.WriteLogToFile($"启动检测 | 用户未启用遥测,已切换回 Release 通道");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ComboBoxTelemetryUploadLevel_SelectionChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
if (_isChangingTelemetryInternally) return;
|
||||
var oldLevel = Settings.Startup.TelemetryUploadLevel;
|
||||
var item = ComboBoxTelemetryUploadLevel?.SelectedItem as ComboBoxItem;
|
||||
if (item == null) return;
|
||||
|
||||
var tag = item.Tag?.ToString() ?? "0";
|
||||
var newLevel = TelemetryUploadLevel.None;
|
||||
switch (tag)
|
||||
{
|
||||
case "1":
|
||||
newLevel = TelemetryUploadLevel.Basic;
|
||||
break;
|
||||
case "2":
|
||||
newLevel = TelemetryUploadLevel.Extended;
|
||||
break;
|
||||
default:
|
||||
newLevel = TelemetryUploadLevel.None;
|
||||
break;
|
||||
}
|
||||
|
||||
if (newLevel == TelemetryUploadLevel.None &&
|
||||
oldLevel != TelemetryUploadLevel.None &&
|
||||
Settings.Startup.UpdateChannel != UpdateChannel.Release)
|
||||
{
|
||||
var result = MessageBox.Show(
|
||||
"关闭匿名使用数据上传后,将无法继续使用预览/测试通道,系统会自动切换回正式通道(Release)。\n\n是否确认关闭?",
|
||||
"确认关闭遥测",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Warning);
|
||||
|
||||
if (result != MessageBoxResult.Yes)
|
||||
{
|
||||
_isChangingTelemetryInternally = true;
|
||||
try
|
||||
{
|
||||
int idx = 0;
|
||||
switch (oldLevel)
|
||||
{
|
||||
case TelemetryUploadLevel.Basic:
|
||||
idx = 1;
|
||||
break;
|
||||
case TelemetryUploadLevel.Extended:
|
||||
idx = 2;
|
||||
break;
|
||||
default:
|
||||
idx = 0;
|
||||
break;
|
||||
}
|
||||
ComboBoxTelemetryUploadLevel.SelectedIndex = idx;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isChangingTelemetryInternally = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_isChangingUpdateChannelInternally = true;
|
||||
try
|
||||
{
|
||||
Settings.Startup.UpdateChannel = UpdateChannel.Release;
|
||||
DeviceIdentifier.UpdateUsageChannel(UpdateChannel.Release);
|
||||
|
||||
if (UpdateChannelSelector != null)
|
||||
{
|
||||
foreach (var u in UpdateChannelSelector.Items)
|
||||
{
|
||||
var rb = u as RadioButton;
|
||||
if (rb != null && rb.Tag != null && rb.Tag.ToString() == "Release")
|
||||
{
|
||||
rb.IsChecked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isChangingUpdateChannelInternally = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (newLevel != TelemetryUploadLevel.None && !Settings.Startup.HasAcceptedTelemetryPrivacy)
|
||||
{
|
||||
MessageBox.Show(
|
||||
"在开启匿名使用数据上传前,请先阅读并勾选上方的隐私说明。",
|
||||
"需要同意隐私说明",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Warning);
|
||||
|
||||
_isChangingTelemetryInternally = true;
|
||||
try
|
||||
{
|
||||
Settings.Startup.TelemetryUploadLevel = TelemetryUploadLevel.None;
|
||||
if (ComboBoxTelemetryUploadLevel != null)
|
||||
{
|
||||
ComboBoxTelemetryUploadLevel.SelectedIndex = 0;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isChangingTelemetryInternally = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Settings.Startup.TelemetryUploadLevel = newLevel;
|
||||
|
||||
SaveSettingsToFile();
|
||||
ShowNotification("匿名使用数据上传设置已保存");
|
||||
}
|
||||
|
||||
private void CheckBoxTelemetryPrivacyAccepted_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
if (_isChangingTelemetryPrivacyInternally) return;
|
||||
|
||||
bool isChecked = CheckBoxTelemetryPrivacyAccepted.IsChecked == true;
|
||||
|
||||
if (isChecked)
|
||||
{
|
||||
if (!PrivacyFileExists())
|
||||
{
|
||||
MessageBox.Show(
|
||||
"未找到隐私说明文件(privacy / privacy.txt),暂时无法启用匿名使用数据上传。",
|
||||
"隐私说明缺失",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Warning);
|
||||
|
||||
_isChangingTelemetryPrivacyInternally = true;
|
||||
try
|
||||
{
|
||||
CheckBoxTelemetryPrivacyAccepted.IsChecked = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isChangingTelemetryPrivacyInternally = false;
|
||||
}
|
||||
|
||||
Settings.Startup.HasAcceptedTelemetryPrivacy = false;
|
||||
SaveSettingsToFile();
|
||||
return;
|
||||
}
|
||||
|
||||
var privacyWindow = new PrivacyAgreementWindow();
|
||||
privacyWindow.Owner = this;
|
||||
bool? dialogResult = privacyWindow.ShowDialog();
|
||||
|
||||
if (dialogResult == true && privacyWindow.UserAccepted)
|
||||
{
|
||||
Settings.Startup.HasAcceptedTelemetryPrivacy = true;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
else
|
||||
{
|
||||
_isChangingTelemetryPrivacyInternally = true;
|
||||
try
|
||||
{
|
||||
CheckBoxTelemetryPrivacyAccepted.IsChecked = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isChangingTelemetryPrivacyInternally = false;
|
||||
}
|
||||
|
||||
Settings.Startup.HasAcceptedTelemetryPrivacy = false;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 用户主动取消勾选,提示会关闭遥测并切回正式通道
|
||||
var result = MessageBox.Show(
|
||||
"取消同意隐私说明后,将关闭匿名使用数据上传,并切回正式通道(Release)。\n\n是否确认?",
|
||||
"确认取消隐私同意",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Warning);
|
||||
|
||||
if (result != MessageBoxResult.Yes)
|
||||
{
|
||||
// 撤销取消操作,恢复为已勾选
|
||||
_isChangingTelemetryPrivacyInternally = true;
|
||||
try
|
||||
{
|
||||
CheckBoxTelemetryPrivacyAccepted.IsChecked = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isChangingTelemetryPrivacyInternally = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 关闭遥测等级
|
||||
_isChangingTelemetryInternally = true;
|
||||
try
|
||||
{
|
||||
Settings.Startup.TelemetryUploadLevel = TelemetryUploadLevel.None;
|
||||
if (ComboBoxTelemetryUploadLevel != null)
|
||||
{
|
||||
ComboBoxTelemetryUploadLevel.SelectedIndex = 0;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isChangingTelemetryInternally = false;
|
||||
}
|
||||
|
||||
// 2. 若当前不是 Release 通道,则切回 Release
|
||||
if (Settings.Startup.UpdateChannel != UpdateChannel.Release)
|
||||
{
|
||||
_isChangingUpdateChannelInternally = true;
|
||||
try
|
||||
{
|
||||
Settings.Startup.UpdateChannel = UpdateChannel.Release;
|
||||
DeviceIdentifier.UpdateUsageChannel(UpdateChannel.Release);
|
||||
|
||||
if (UpdateChannelSelector != null)
|
||||
{
|
||||
foreach (var u in UpdateChannelSelector.Items)
|
||||
{
|
||||
var rb = u as RadioButton;
|
||||
if (rb != null && rb.Tag != null && rb.Tag.ToString() == "Release")
|
||||
{
|
||||
rb.IsChecked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isChangingUpdateChannelInternally = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 最后真正取消隐私同意并保存
|
||||
Settings.Startup.HasAcceptedTelemetryPrivacy = false;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleSwitchIsAutoUpdate_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
@@ -140,6 +659,46 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleSwitchUseRotPptLink_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
|
||||
Settings.PowerPointSettings.UseRotPptLink = ToggleSwitchUseRotPptLink.IsOn;
|
||||
SaveSettingsToFile();
|
||||
|
||||
try
|
||||
{
|
||||
StopPPTMonitoring();
|
||||
if (Settings.PowerPointSettings.UseRotPptLink &&
|
||||
Settings.PowerPointSettings.EnablePowerPointEnhancement)
|
||||
{
|
||||
Settings.PowerPointSettings.EnablePowerPointEnhancement = false;
|
||||
if (ToggleSwitchPowerPointEnhancement != null)
|
||||
{
|
||||
ToggleSwitchPowerPointEnhancement.IsOn = false;
|
||||
}
|
||||
StopPowerPointProcessMonitoring();
|
||||
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
InitializePPTManagers();
|
||||
|
||||
if (Settings.PowerPointSettings.PowerPointSupport)
|
||||
{
|
||||
StartPPTMonitoring();
|
||||
}
|
||||
|
||||
LogHelper.WriteLogToFile(
|
||||
$"已切换 PPT 联动架构为 {(Settings.PowerPointSettings.UseRotPptLink ? "ROT" : "COM")}",
|
||||
LogHelper.LogType.Event);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"切换 PPT 联动架构失败: {ex}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleSwitchShowCanvasAtNewSlideShow_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
@@ -2765,7 +3324,7 @@ namespace Ink_Canvas
|
||||
isLoaded = false;
|
||||
SetSettingsToRecommendation();
|
||||
SaveSettingsToFile();
|
||||
LoadSettings();
|
||||
LoadSettings(isStartup: false, skipAutoUpdateCheck: true);
|
||||
isLoaded = true;
|
||||
|
||||
ToggleSwitchRunAtStartup.IsOn = false;
|
||||
@@ -2786,7 +3345,7 @@ namespace Ink_Canvas
|
||||
Settings.Automation.AutoDelSavedFilesDaysThreshold = 15;
|
||||
SetAutoSavedStrokesLocationToDiskDButton_Click(null, null);
|
||||
SaveSettingsToFile();
|
||||
LoadSettings();
|
||||
LoadSettings(isStartup: false, skipAutoUpdateCheck: true);
|
||||
isLoaded = true;
|
||||
}
|
||||
catch { }
|
||||
@@ -2853,6 +3412,60 @@ namespace Ink_Canvas
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchIsEnableUriScheme_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
|
||||
bool newState = ToggleSwitchIsEnableUriScheme.IsOn;
|
||||
bool success = false;
|
||||
|
||||
try
|
||||
{
|
||||
if (newState)
|
||||
{
|
||||
if (!UriSchemeHelper.IsUriSchemeRegistered())
|
||||
{
|
||||
success = UriSchemeHelper.RegisterUriScheme();
|
||||
}
|
||||
else
|
||||
{
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (UriSchemeHelper.IsUriSchemeRegistered())
|
||||
{
|
||||
success = UriSchemeHelper.UnregisterUriScheme();
|
||||
}
|
||||
else
|
||||
{
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"切换URI Scheme状态失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
Settings.Advanced.IsEnableUriScheme = newState;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 回滚 UI 状态
|
||||
isLoaded = false;
|
||||
ToggleSwitchIsEnableUriScheme.IsOn = !newState;
|
||||
isLoaded = true;
|
||||
|
||||
ShowNotification("设置外部协议失败,请检查权限或日志");
|
||||
}
|
||||
}
|
||||
|
||||
private void TouchMultiplierSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
@@ -3823,9 +4436,11 @@ namespace Ink_Canvas
|
||||
private async void UpdateChannelSelector_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
if (_isChangingUpdateChannelInternally) return;
|
||||
var radioButton = sender as RadioButton;
|
||||
if (radioButton != null)
|
||||
{
|
||||
var oldChannel = Settings.Startup.UpdateChannel;
|
||||
string channel = radioButton.Tag.ToString();
|
||||
UpdateChannel newChannel = channel == "Beta" ? UpdateChannel.Beta
|
||||
: channel == "Preview" ? UpdateChannel.Preview
|
||||
@@ -3837,7 +4452,100 @@ namespace Ink_Canvas
|
||||
return;
|
||||
}
|
||||
|
||||
bool isTestChannel = newChannel == UpdateChannel.Preview || newChannel == UpdateChannel.Beta;
|
||||
if (isTestChannel && !Settings.Startup.HasAcceptedTelemetryPrivacy)
|
||||
{
|
||||
MessageBox.Show(
|
||||
"加入预览 / 测试通道前,请先在关于页面勾选“我已阅读并同意 privacy 中的隐私说明”。",
|
||||
"需要同意隐私说明",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Warning);
|
||||
|
||||
Settings.Startup.UpdateChannel = oldChannel;
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
_isChangingUpdateChannelInternally = true;
|
||||
try
|
||||
{
|
||||
if (UpdateChannelSelector != null)
|
||||
{
|
||||
string oldChannelTag = oldChannel.ToString();
|
||||
foreach (var item in UpdateChannelSelector.Items)
|
||||
{
|
||||
if (item is RadioButton rb && rb.Tag != null &&
|
||||
string.Equals(rb.Tag.ToString(), oldChannelTag, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
rb.IsChecked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isChangingUpdateChannelInternally = false;
|
||||
}
|
||||
}), DispatcherPriority.Normal);
|
||||
|
||||
SaveSettingsToFile();
|
||||
LogHelper.WriteLogToFile("Settings | User not accepted privacy, reverted update channel");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isTestChannel && Settings.Startup.TelemetryUploadLevel == TelemetryUploadLevel.None)
|
||||
{
|
||||
var result = MessageBox.Show(
|
||||
"加入预览 / 测试通道需要开启匿名基础数据上传。\n\n是否立即开启匿名基础数据上传?",
|
||||
"需要开启匿名使用数据上传",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Warning);
|
||||
|
||||
if (result == MessageBoxResult.Yes)
|
||||
{
|
||||
Settings.Startup.TelemetryUploadLevel = TelemetryUploadLevel.Basic;
|
||||
if (ComboBoxTelemetryUploadLevel != null)
|
||||
{
|
||||
ComboBoxTelemetryUploadLevel.SelectedIndex = 1;
|
||||
}
|
||||
SaveSettingsToFile();
|
||||
LogHelper.WriteLogToFile("Settings | Telemetry enabled (Basic) for preview/beta update channel");
|
||||
}
|
||||
else
|
||||
{
|
||||
Settings.Startup.UpdateChannel = oldChannel;
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
_isChangingUpdateChannelInternally = true;
|
||||
try
|
||||
{
|
||||
if (UpdateChannelSelector != null)
|
||||
{
|
||||
string oldChannelTag = oldChannel.ToString();
|
||||
foreach (var item in UpdateChannelSelector.Items)
|
||||
{
|
||||
if (item is RadioButton rb && rb.Tag != null &&
|
||||
string.Equals(rb.Tag.ToString(), oldChannelTag, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
rb.IsChecked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isChangingUpdateChannelInternally = false;
|
||||
}
|
||||
}), DispatcherPriority.Normal);
|
||||
|
||||
SaveSettingsToFile();
|
||||
LogHelper.WriteLogToFile("Settings | User declined telemetry, reverted update channel");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Settings.Startup.UpdateChannel = newChannel;
|
||||
DeviceIdentifier.UpdateUsageChannel(newChannel);
|
||||
LogHelper.WriteLogToFile($"Settings | Update channel changed to {Settings.Startup.UpdateChannel}");
|
||||
SaveSettingsToFile();
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Ink_Canvas
|
||||
{
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private void LoadSettings(bool isStartup = false)
|
||||
private void LoadSettings(bool isStartup = false, bool skipAutoUpdateCheck = false)
|
||||
{
|
||||
AppVersionTextBlock.Text = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
try
|
||||
@@ -142,6 +142,42 @@ namespace Ink_Canvas
|
||||
CursorIcon_Click(null, null);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (Settings?.Startup != null)
|
||||
{
|
||||
if (ComboBoxTelemetryUploadLevel != null)
|
||||
{
|
||||
int idx = 0;
|
||||
switch (Settings.Startup.TelemetryUploadLevel)
|
||||
{
|
||||
case TelemetryUploadLevel.None:
|
||||
idx = 0;
|
||||
break;
|
||||
case TelemetryUploadLevel.Basic:
|
||||
idx = 1;
|
||||
break;
|
||||
case TelemetryUploadLevel.Extended:
|
||||
idx = 2;
|
||||
break;
|
||||
default:
|
||||
idx = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
ComboBoxTelemetryUploadLevel.SelectedIndex = idx;
|
||||
}
|
||||
|
||||
if (CheckBoxTelemetryPrivacyAccepted != null)
|
||||
{
|
||||
CheckBoxTelemetryPrivacyAccepted.IsChecked = Settings.Startup.HasAcceptedTelemetryPrivacy;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.Startup) +
|
||||
@@ -193,7 +229,7 @@ namespace Ink_Canvas
|
||||
ToggleSwitchIsAutoUpdate.IsOn = Settings.Startup.IsAutoUpdate;
|
||||
|
||||
// 只有在启用了自动更新功能时才检查更新
|
||||
if (Settings.Startup.IsAutoUpdate)
|
||||
if (Settings.Startup.IsAutoUpdate && !skipAutoUpdateCheck)
|
||||
{
|
||||
if (isStartup)
|
||||
{
|
||||
@@ -469,6 +505,11 @@ namespace Ink_Canvas
|
||||
|
||||
ToggleSwitchShowCanvasAtNewSlideShow.IsOn = Settings.PowerPointSettings.IsShowCanvasAtNewSlideShow;
|
||||
|
||||
if (ToggleSwitchUseRotPptLink != null)
|
||||
{
|
||||
ToggleSwitchUseRotPptLink.IsOn = Settings.PowerPointSettings.UseRotPptLink;
|
||||
}
|
||||
|
||||
ToggleSwitchEnableTwoFingerGestureInPresentationMode.IsOn =
|
||||
Settings.PowerPointSettings.IsEnableTwoFingerGestureInPresentationMode;
|
||||
|
||||
@@ -857,6 +898,7 @@ namespace Ink_Canvas
|
||||
ToggleSwitchIsSecondConfimeWhenShutdownApp.IsOn = Settings.Advanced.IsSecondConfirmWhenShutdownApp;
|
||||
ToggleSwitchWindowMode.IsOn = Settings.Advanced.WindowMode;
|
||||
ToggleSwitchIsSpecialScreen.IsOn = Settings.Advanced.IsSpecialScreen;
|
||||
ToggleSwitchIsEnableUriScheme.IsOn = Settings.Advanced.IsEnableUriScheme;
|
||||
ToggleSwitchIsQuadIR.IsOn = Settings.Advanced.IsQuadIR;
|
||||
ToggleSwitchEraserBindTouchMultiplier.IsOn = Settings.Advanced.EraserBindTouchMultiplier;
|
||||
ToggleSwitchIsEnableFullScreenHelper.IsOn = Settings.Advanced.IsEnableFullScreenHelper;
|
||||
@@ -1205,6 +1247,15 @@ namespace Ink_Canvas
|
||||
_inkFadeManager.UpdateFadeTime(Settings.Canvas.InkFadeTime);
|
||||
}
|
||||
|
||||
// 同步在笔工具菜单中隐藏墨迹渐隐控制开关的设置
|
||||
if (ToggleSwitchHideInkFadeControlInPenMenu != null)
|
||||
{
|
||||
ToggleSwitchHideInkFadeControlInPenMenu.IsOn = Settings.Canvas.HideInkFadeControlInPenMenu;
|
||||
}
|
||||
|
||||
// 根据设置更新墨迹渐隐控制开关的可见性
|
||||
UpdateInkFadeControlVisibility();
|
||||
|
||||
LogHelper.WriteLogToFile("墨迹渐隐设置已加载", LogHelper.LogType.Event);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -30,32 +30,12 @@ namespace Ink_Canvas
|
||||
// FloatingBarIcons_MouseUp_New(sender);
|
||||
if (BorderDrawShape.Visibility == Visibility.Visible)
|
||||
{
|
||||
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(PenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
|
||||
}
|
||||
else
|
||||
{
|
||||
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(PenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
|
||||
HideSubPanels();
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(BorderDrawShape);
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardBorderDrawShape);
|
||||
}
|
||||
|
||||
@@ -824,16 +824,29 @@ namespace Ink_Canvas
|
||||
if (isFloatingBarChangingHideMode) return;
|
||||
try
|
||||
{
|
||||
if (HasFullScreenWindowOfAutoFoldApps())
|
||||
{
|
||||
if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasFullScreen = HasFullScreenWindowOfAutoFoldApps();
|
||||
bool shouldAutoFold = CheckShouldAutoFoldByWindowPreview();
|
||||
var windowProcessName = ForegroundWindowInfo.ProcessName();
|
||||
var windowTitle = ForegroundWindowInfo.WindowTitle();
|
||||
|
||||
Thickness currentMargin = new Thickness();
|
||||
Dispatcher.Invoke(() => {
|
||||
currentMargin = ViewboxFloatingBar.Margin;
|
||||
});
|
||||
|
||||
if (hasFullScreen)
|
||||
{
|
||||
if (!isFloatingBarFolded)
|
||||
{
|
||||
FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
else if (currentMargin.Left > -50 && !isFloatingBarChangingHideMode)
|
||||
{
|
||||
FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldAutoFold)
|
||||
{
|
||||
if (windowProcessName == "EasiNote")
|
||||
@@ -842,285 +855,59 @@ namespace Ink_Canvas
|
||||
{
|
||||
var versionInfo = FileVersionInfo.GetVersionInfo(ForegroundWindowInfo.ProcessPath());
|
||||
string version = versionInfo.FileVersion;
|
||||
string prodName = versionInfo.ProductName;
|
||||
|
||||
if (version.StartsWith("5.") && Settings.Automation.IsAutoFoldInEasiNote)
|
||||
{
|
||||
bool isAnnotationWindow = windowTitle.Length == 0 && ForegroundWindowInfo.WindowRect().Height < 500;
|
||||
if (Settings.Automation.IsAutoFoldInEasiNoteIgnoreDesktopAnno && isAnnotationWindow)
|
||||
{
|
||||
if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
if (!isFloatingBarFolded)
|
||||
{
|
||||
FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
}
|
||||
else if (!isAnnotationWindow)
|
||||
{
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded)
|
||||
{
|
||||
FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
else if (unfoldFloatingBarByUser)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (version.StartsWith("3.") && Settings.Automation.IsAutoFoldInEasiNote3)
|
||||
{
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
else if (prodName.Contains("3C") && Settings.Automation.IsAutoFoldInEasiNote3C &&
|
||||
ForegroundWindowInfo.WindowRect().Height >= SystemParameters.WorkArea.Height - 16 &&
|
||||
ForegroundWindowInfo.WindowRect().Width >= SystemParameters.WorkArea.Width - 16)
|
||||
{
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 处理其他需要检测批注窗口的情况
|
||||
else if ((Settings.Automation.IsAutoFoldInEasiCamera && windowProcessName == "EasiCamera") ||
|
||||
(Settings.Automation.IsAutoFoldInSeewoPincoTeacher && (windowProcessName == "BoardService" || windowProcessName == "seewoPincoTeacher")) ||
|
||||
(Settings.Automation.IsAutoFoldInHiteCamera && windowProcessName == "HiteCamera") ||
|
||||
(Settings.Automation.IsAutoFoldInHiteTouchPro && windowProcessName == "HiteTouchPro") ||
|
||||
(Settings.Automation.IsAutoFoldInHiteLightBoard && windowProcessName == "HiteLightBoard"))
|
||||
// 处理其他目标软件
|
||||
else if (!unfoldFloatingBarByUser && !isFloatingBarFolded)
|
||||
{
|
||||
if (IsAnnotationWindow())
|
||||
{
|
||||
if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
}
|
||||
// 处理其他普通情况
|
||||
else
|
||||
{
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//LogHelper.WriteLogToFile("windowTitle | " + windowTitle + " | windowProcessName | " + windowProcessName);
|
||||
|
||||
if (windowProcessName == "EasiNote")
|
||||
if (!WinTabWindowsChecker.IsWindowExisted("幻灯片放映", false))
|
||||
{
|
||||
// 检测到有可能是EasiNote5或者EasiNote3/3C
|
||||
if (ForegroundWindowInfo.ProcessPath() != "Unknown")
|
||||
if (isFloatingBarFolded && !foldFloatingBarByUser)
|
||||
{
|
||||
var versionInfo = FileVersionInfo.GetVersionInfo(ForegroundWindowInfo.ProcessPath());
|
||||
string version = versionInfo.FileVersion;
|
||||
string prodName = versionInfo.ProductName;
|
||||
Trace.WriteLine(ForegroundWindowInfo.ProcessPath());
|
||||
Trace.WriteLine(version);
|
||||
Trace.WriteLine(prodName);
|
||||
if (version.StartsWith("5.") && Settings.Automation.IsAutoFoldInEasiNote)
|
||||
{ // EasiNote5
|
||||
// 检查是否是桌面批注窗口
|
||||
bool isAnnotationWindow = windowTitle.Length == 0 && ForegroundWindowInfo.WindowRect().Height < 500;
|
||||
|
||||
// 如果启用了忽略桌面批注窗口功能,且当前是批注窗口
|
||||
if (Settings.Automation.IsAutoFoldInEasiNoteIgnoreDesktopAnno && isAnnotationWindow)
|
||||
{
|
||||
// 强制保持收纳状态
|
||||
if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
else if (!isAnnotationWindow)
|
||||
{
|
||||
// 非批注窗口时正常收纳
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
// 检查是否启用了软件退出后保持收纳模式
|
||||
if (Settings.Automation.KeepFoldAfterSoftwareExit)
|
||||
{
|
||||
unfoldFloatingBarByUser = false;
|
||||
}
|
||||
else if (version.StartsWith("3.") && Settings.Automation.IsAutoFoldInEasiNote3)
|
||||
{ // EasiNote3
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
else
|
||||
{
|
||||
UnFoldFloatingBar_MouseUp(new object(), null);
|
||||
unfoldFloatingBarByUser = false;
|
||||
}
|
||||
else if (prodName.Contains("3C") && Settings.Automation.IsAutoFoldInEasiNote3C &&
|
||||
ForegroundWindowInfo.WindowRect().Height >= SystemParameters.WorkArea.Height - 16 &&
|
||||
ForegroundWindowInfo.WindowRect().Width >= SystemParameters.WorkArea.Width - 16)
|
||||
{ // EasiNote3C
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
}
|
||||
// EasiCamera
|
||||
}
|
||||
else if (Settings.Automation.IsAutoFoldInEasiCamera && windowProcessName == "EasiCamera" &&
|
||||
ForegroundWindowInfo.WindowRect().Height >= SystemParameters.WorkArea.Height - 16 &&
|
||||
ForegroundWindowInfo.WindowRect().Width >= SystemParameters.WorkArea.Width - 16)
|
||||
{
|
||||
// 检测到批注窗口时保持收纳状态
|
||||
if (IsAnnotationWindow())
|
||||
{
|
||||
// 批注窗口打开时,如果当前是展开状态则收纳
|
||||
if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 非批注窗口时正常处理
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
// EasiNote5C
|
||||
}
|
||||
else if (Settings.Automation.IsAutoFoldInEasiNote5C && windowProcessName == "EasiNote5C" &&
|
||||
ForegroundWindowInfo.WindowRect().Height >= SystemParameters.WorkArea.Height - 16 &&
|
||||
ForegroundWindowInfo.WindowRect().Width >= SystemParameters.WorkArea.Width - 16)
|
||||
{
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
// SeewoPinco
|
||||
}
|
||||
else if (Settings.Automation.IsAutoFoldInSeewoPincoTeacher && (windowProcessName == "BoardService" || windowProcessName == "seewoPincoTeacher"))
|
||||
{
|
||||
// 检测到希沃白板五的批注窗口时保持收纳状态
|
||||
if (IsAnnotationWindow())
|
||||
{
|
||||
// 批注窗口打开时,如果当前是展开状态则收纳
|
||||
if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 非批注窗口时正常处理
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
// HiteCamera
|
||||
}
|
||||
else if (Settings.Automation.IsAutoFoldInHiteCamera && windowProcessName == "HiteCamera" &&
|
||||
ForegroundWindowInfo.WindowRect().Height >= SystemParameters.WorkArea.Height - 16 &&
|
||||
ForegroundWindowInfo.WindowRect().Width >= SystemParameters.WorkArea.Width - 16)
|
||||
{
|
||||
// 检测到批注窗口时保持收纳状态
|
||||
if (IsAnnotationWindow())
|
||||
{
|
||||
// 批注窗口打开时,如果当前是展开状态则收纳
|
||||
if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 非批注窗口时正常处理
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
// HiteTouchPro
|
||||
}
|
||||
else if (Settings.Automation.IsAutoFoldInHiteTouchPro && windowProcessName == "HiteTouchPro" &&
|
||||
ForegroundWindowInfo.WindowRect().Height >= SystemParameters.WorkArea.Height - 16 &&
|
||||
ForegroundWindowInfo.WindowRect().Width >= SystemParameters.WorkArea.Width - 16)
|
||||
{
|
||||
// 检测到批注窗口时保持收纳状态
|
||||
if (IsAnnotationWindow())
|
||||
{
|
||||
// 批注窗口打开时,如果当前是展开状态则收纳
|
||||
if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 非批注窗口时正常处理
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
// WxBoardMain
|
||||
}
|
||||
else if (Settings.Automation.IsAutoFoldInWxBoardMain && windowProcessName == "WxBoardMain" &&
|
||||
ForegroundWindowInfo.WindowRect().Height >= SystemParameters.WorkArea.Height - 16 &&
|
||||
ForegroundWindowInfo.WindowRect().Width >= SystemParameters.WorkArea.Width - 16)
|
||||
{
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
// MSWhiteboard
|
||||
}
|
||||
else if (Settings.Automation.IsAutoFoldInMSWhiteboard && (windowProcessName == "MicrosoftWhiteboard" ||
|
||||
windowProcessName == "msedgewebview2"))
|
||||
{
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
// OldZyBoard
|
||||
}
|
||||
else if (Settings.Automation.IsAutoFoldInOldZyBoard && // 中原旧白板
|
||||
(WinTabWindowsChecker.IsWindowExisted("WhiteBoard - DrawingWindow")
|
||||
|| WinTabWindowsChecker.IsWindowExisted("InstantAnnotationWindow")))
|
||||
{
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
// HiteLightBoard
|
||||
}
|
||||
else if (Settings.Automation.IsAutoFoldInHiteLightBoard && windowProcessName == "HiteLightBoard" &&
|
||||
ForegroundWindowInfo.WindowRect().Height >= SystemParameters.WorkArea.Height - 16 &&
|
||||
ForegroundWindowInfo.WindowRect().Width >= SystemParameters.WorkArea.Width - 16)
|
||||
{
|
||||
// 检测到批注窗口时保持收纳状态
|
||||
if (IsAnnotationWindow())
|
||||
{
|
||||
// 批注窗口打开时,如果当前是展开状态则收纳
|
||||
if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 非批注窗口时正常处理
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
// AdmoxWhiteboard
|
||||
}
|
||||
else if (Settings.Automation.IsAutoFoldInAdmoxWhiteboard && windowProcessName == "Amdox.WhiteBoard" &&
|
||||
ForegroundWindowInfo.WindowRect().Height >= SystemParameters.WorkArea.Height - 16 &&
|
||||
ForegroundWindowInfo.WindowRect().Width >= SystemParameters.WorkArea.Width - 16)
|
||||
{
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
// AdmoxBooth
|
||||
}
|
||||
else if (Settings.Automation.IsAutoFoldInAdmoxBooth && windowProcessName == "Amdox.Booth" &&
|
||||
ForegroundWindowInfo.WindowRect().Height >= SystemParameters.WorkArea.Height - 16 &&
|
||||
ForegroundWindowInfo.WindowRect().Width >= SystemParameters.WorkArea.Width - 16)
|
||||
{
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
// QPoint
|
||||
}
|
||||
else if (Settings.Automation.IsAutoFoldInQPoint && windowProcessName == "QPoint" &&
|
||||
ForegroundWindowInfo.WindowRect().Height >= SystemParameters.WorkArea.Height - 16 &&
|
||||
ForegroundWindowInfo.WindowRect().Width >= SystemParameters.WorkArea.Width - 16)
|
||||
{
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
// YiYunVisualPresenter
|
||||
}
|
||||
else if (Settings.Automation.IsAutoFoldInYiYunVisualPresenter && windowProcessName == "YiYunVisualPresenter" &&
|
||||
ForegroundWindowInfo.WindowRect().Height >= SystemParameters.WorkArea.Height - 16 &&
|
||||
ForegroundWindowInfo.WindowRect().Width >= SystemParameters.WorkArea.Width - 16)
|
||||
{
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
// MaxHubWhiteboard
|
||||
}
|
||||
else if (Settings.Automation.IsAutoFoldInMaxHubWhiteboard && windowProcessName == "WhiteBoard" &&
|
||||
WinTabWindowsChecker.IsWindowExisted("白板书写") &&
|
||||
ForegroundWindowInfo.WindowRect().Height >= SystemParameters.WorkArea.Height - 16 &&
|
||||
ForegroundWindowInfo.WindowRect().Width >= SystemParameters.WorkArea.Width - 16)
|
||||
{
|
||||
if (ForegroundWindowInfo.ProcessPath() != "Unknown")
|
||||
{
|
||||
var versionInfo = FileVersionInfo.GetVersionInfo(ForegroundWindowInfo.ProcessPath());
|
||||
var version = versionInfo.FileVersion; var prodName = versionInfo.ProductName;
|
||||
if (version.StartsWith("6.") && prodName == "WhiteBoard") if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
}
|
||||
else if (WinTabWindowsChecker.IsWindowExisted("幻灯片放映", false))
|
||||
{
|
||||
// 处于幻灯片放映状态
|
||||
if (HasFullScreenWindowOfAutoFoldApps())
|
||||
{
|
||||
if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Settings.Automation.IsAutoFoldInPPTSlideShow && isFloatingBarFolded && !foldFloatingBarByUser)
|
||||
UnFoldFloatingBar_MouseUp(new object(), null);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (HasFullScreenWindowOfAutoFoldApps())
|
||||
{
|
||||
if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
unfoldFloatingBarByUser = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否启用了软件退出后保持收纳模式
|
||||
if (Settings.Automation.KeepFoldAfterSoftwareExit)
|
||||
{
|
||||
// 如果启用了保持收纳模式,则不自动展开浮动栏
|
||||
unfoldFloatingBarByUser = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 原有的逻辑:软件退出后自动展开浮动栏
|
||||
if (isFloatingBarFolded && !foldFloatingBarByUser) UnFoldFloatingBar_MouseUp(new object(), null);
|
||||
unfoldFloatingBarByUser = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"[AutoFold Error] 定时检测发生异常: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void timerCheckAutoUpdateWithSilence_Elapsed(object sender, ElapsedEventArgs e)
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using System;
|
||||
using System.Windows;
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
public void HandleUriCommand(string uri)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(uri)) return;
|
||||
|
||||
// 检查是否启用了外部协议
|
||||
if (!Settings.Advanced.IsEnableUriScheme)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"URI协议已禁用,忽略请求: {uri}", LogHelper.LogType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
LogHelper.WriteLogToFile($"正在处理URI命令: {uri}", LogHelper.LogType.Event);
|
||||
|
||||
// 解析URI
|
||||
// 格式: icc://command?param=value
|
||||
// 如果URI以icc:开头但不是标准URI格式,尝试手动解析
|
||||
string command = "";
|
||||
|
||||
if (Uri.TryCreate(uri, UriKind.Absolute, out Uri uriObj))
|
||||
{
|
||||
command = uriObj.Host.ToLower();
|
||||
// 处理像 icc:fold 这样 Host 可能为空的情况
|
||||
if (string.IsNullOrEmpty(command))
|
||||
{
|
||||
command = uriObj.AbsolutePath.Trim('/').ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
// 如果解析失败且是 icc: 协议,则手动处理
|
||||
if (string.IsNullOrEmpty(command) && uri.StartsWith("icc:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// 简单的手动解析: icc:fold
|
||||
string path = uri.Substring(4);
|
||||
// 移除可能的斜杠
|
||||
command = path.Trim('/').ToLower();
|
||||
}
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case "fold":
|
||||
if (!isFloatingBarFolded)
|
||||
{
|
||||
FoldFloatingBar_MouseUp(new object(), null);
|
||||
ShowNotification("已进入收纳模式");
|
||||
}
|
||||
break;
|
||||
|
||||
case "unfold":
|
||||
case "show": // 兼容旧习惯
|
||||
if (isFloatingBarFolded)
|
||||
{
|
||||
UnFoldFloatingBar_MouseUp(new object(), null);
|
||||
ShowNotification("已退出收纳模式");
|
||||
}
|
||||
break;
|
||||
|
||||
case "toggle":
|
||||
if (isFloatingBarFolded)
|
||||
{
|
||||
UnFoldFloatingBar_MouseUp(new object(), null);
|
||||
ShowNotification("已退出收纳模式");
|
||||
}
|
||||
else
|
||||
{
|
||||
FoldFloatingBar_MouseUp(new object(), null);
|
||||
ShowNotification("已进入收纳模式");
|
||||
}
|
||||
break;
|
||||
|
||||
case "thoroughhideon":
|
||||
Settings.Automation.ThoroughlyHideWhenFolded = true;
|
||||
SaveSettingsToFile();
|
||||
ShowNotification("已开启:收起时彻底隐藏");
|
||||
// 如果当前已经是在收纳模式,立即隐藏
|
||||
if (isFloatingBarFolded)
|
||||
{
|
||||
this.Visibility = Visibility.Hidden;
|
||||
}
|
||||
break;
|
||||
|
||||
case "thoroughhideoff":
|
||||
Settings.Automation.ThoroughlyHideWhenFolded = false;
|
||||
SaveSettingsToFile();
|
||||
ShowNotification("已关闭:收起时彻底隐藏");
|
||||
// 确保窗口可见
|
||||
this.Visibility = Visibility.Visible;
|
||||
break;
|
||||
|
||||
case "thoroughhidetoggle":
|
||||
Settings.Automation.ThoroughlyHideWhenFolded = !Settings.Automation.ThoroughlyHideWhenFolded;
|
||||
SaveSettingsToFile();
|
||||
ShowNotification(Settings.Automation.ThoroughlyHideWhenFolded ? "已开启:收起时彻底隐藏" : "已关闭:收起时彻底隐藏");
|
||||
if (isFloatingBarFolded)
|
||||
{
|
||||
this.Visibility = Settings.Automation.ThoroughlyHideWhenFolded ? Visibility.Hidden : Visibility.Visible;
|
||||
}
|
||||
break;
|
||||
|
||||
case "randone":
|
||||
SymbolIconRandOne_MouseUp(null, null);
|
||||
break;
|
||||
|
||||
case "rand":
|
||||
SymbolIconRand_MouseUp(null, null);
|
||||
break;
|
||||
|
||||
case "timer":
|
||||
ImageCountdownTimer_MouseUp(null, null);
|
||||
break;
|
||||
|
||||
case "whiteboard":
|
||||
case "board":
|
||||
ImageBlackboard_MouseUp(null, null);
|
||||
break;
|
||||
|
||||
default:
|
||||
LogHelper.WriteLogToFile($"未知的URI命令: {command}", LogHelper.LogType.Warning);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"处理URI命令时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,5 +49,5 @@ using System.Windows;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.7.18.5")]
|
||||
[assembly: AssemblyFileVersion("1.7.18.5")]
|
||||
[assembly: AssemblyVersion("1.7.18.6")]
|
||||
[assembly: AssemblyFileVersion("1.7.18.6")]
|
||||
|
||||
@@ -106,6 +106,8 @@ namespace Ink_Canvas
|
||||
public bool EnableInkFade { get; set; } = false;
|
||||
[JsonProperty("inkFadeTime")]
|
||||
public int InkFadeTime { get; set; } = 3000; // 墨迹渐隐时间(毫秒)
|
||||
[JsonProperty("hideInkFadeControlInPenMenu")]
|
||||
public bool HideInkFadeControlInPenMenu { get; set; } = false; // 是否在笔工具菜单中隐藏墨迹渐隐控制开关
|
||||
|
||||
}
|
||||
|
||||
@@ -144,6 +146,25 @@ namespace Ink_Canvas
|
||||
Beta
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 遥测上传等级
|
||||
/// </summary>
|
||||
public enum TelemetryUploadLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// 不上传任何匿名使用数据
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// 仅上传基础数据
|
||||
/// </summary>
|
||||
Basic = 1,
|
||||
/// <summary>
|
||||
/// 上传基础数据 + 可选数据
|
||||
/// </summary>
|
||||
Extended = 2
|
||||
}
|
||||
|
||||
public class Startup
|
||||
{
|
||||
[JsonProperty("isAutoUpdate")]
|
||||
@@ -166,6 +187,10 @@ namespace Ink_Canvas
|
||||
public bool IsFoldAtStartup { get; set; }
|
||||
[JsonProperty("crashAction")]
|
||||
public int CrashAction { get; set; }
|
||||
[JsonProperty("telemetryUploadLevel")]
|
||||
public TelemetryUploadLevel TelemetryUploadLevel { get; set; } = TelemetryUploadLevel.None;
|
||||
[JsonProperty("hasAcceptedTelemetryPrivacy")]
|
||||
public bool HasAcceptedTelemetryPrivacy { get; set; } = false;
|
||||
}
|
||||
|
||||
public class Appearance
|
||||
@@ -338,6 +363,8 @@ namespace Ink_Canvas
|
||||
public bool EnablePPTTimeCapsule { get; set; } = true;
|
||||
[JsonProperty("pptTimeCapsulePosition")]
|
||||
public int PPTTimeCapsulePosition { get; set; } = 1;
|
||||
[JsonProperty("useRotPptLink")]
|
||||
public bool UseRotPptLink { get; set; } = false;
|
||||
}
|
||||
|
||||
public class Automation
|
||||
@@ -494,6 +521,9 @@ namespace Ink_Canvas
|
||||
[JsonProperty("autoSaveStrokesIntervalMinutes")]
|
||||
public int AutoSaveStrokesIntervalMinutes { get; set; } = 5;
|
||||
|
||||
[JsonProperty("thoroughlyHideWhenFolded")]
|
||||
public bool ThoroughlyHideWhenFolded { get; set; } = false;
|
||||
|
||||
[JsonProperty("floatingWindowInterceptor")]
|
||||
public FloatingWindowInterceptorSettings FloatingWindowInterceptor { get; set; } = new FloatingWindowInterceptorSettings();
|
||||
}
|
||||
@@ -627,6 +657,9 @@ namespace Ink_Canvas
|
||||
[JsonProperty("enableUIAccessTopMost")]
|
||||
public bool EnableUIAccessTopMost { get; set; } = false;
|
||||
|
||||
[JsonProperty("isEnableUriScheme")]
|
||||
public bool IsEnableUriScheme { get; set; } = false;
|
||||
|
||||
[JsonProperty("windowMode")]
|
||||
public bool WindowMode { get; set; } = true;
|
||||
}
|
||||
|
||||
@@ -275,7 +275,7 @@ namespace Ink_Canvas.Windows
|
||||
var presentationOpenEvent = pptManager.GetType().GetEvent("PresentationOpen");
|
||||
if (presentationOpenEvent != null)
|
||||
{
|
||||
var openHandler = new Action<Microsoft.Office.Interop.PowerPoint.Presentation>(OnPPTPresentationOpen);
|
||||
var openHandler = new Action<object>(OnPPTPresentationOpen);
|
||||
presentationOpenEvent.AddEventHandler(pptManager, openHandler);
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ namespace Ink_Canvas.Windows
|
||||
var slideShowBeginEvent = pptManager.GetType().GetEvent("SlideShowBegin");
|
||||
if (slideShowBeginEvent != null)
|
||||
{
|
||||
var beginHandler = new Action<Microsoft.Office.Interop.PowerPoint.SlideShowWindow>(OnPPTSlideShowBegin);
|
||||
var beginHandler = new Action<object>(OnPPTSlideShowBegin);
|
||||
slideShowBeginEvent.AddEventHandler(pptManager, beginHandler);
|
||||
}
|
||||
|
||||
@@ -291,7 +291,7 @@ namespace Ink_Canvas.Windows
|
||||
var slideShowNextSlideEvent = pptManager.GetType().GetEvent("SlideShowNextSlide");
|
||||
if (slideShowNextSlideEvent != null)
|
||||
{
|
||||
var handler = new Action<Microsoft.Office.Interop.PowerPoint.SlideShowWindow>(OnPPTSlideChanged);
|
||||
var handler = new Action<object>(OnPPTSlideChanged);
|
||||
slideShowNextSlideEvent.AddEventHandler(pptManager, handler);
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ namespace Ink_Canvas.Windows
|
||||
var slideShowEndEvent = pptManager.GetType().GetEvent("SlideShowEnd");
|
||||
if (slideShowEndEvent != null)
|
||||
{
|
||||
var handler = new Action<Microsoft.Office.Interop.PowerPoint.Presentation>(OnPPTSlideShowEnd);
|
||||
var handler = new Action<object>(OnPPTSlideShowEnd);
|
||||
slideShowEndEvent.AddEventHandler(pptManager, handler);
|
||||
}
|
||||
}
|
||||
@@ -310,7 +310,7 @@ namespace Ink_Canvas.Windows
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPPTPresentationOpen(Microsoft.Office.Interop.PowerPoint.Presentation presentation)
|
||||
private void OnPPTPresentationOpen(object presentation)
|
||||
{
|
||||
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
@@ -326,7 +326,7 @@ namespace Ink_Canvas.Windows
|
||||
}), DispatcherPriority.Normal);
|
||||
}
|
||||
|
||||
private void OnPPTSlideShowBegin(Microsoft.Office.Interop.PowerPoint.SlideShowWindow window)
|
||||
private void OnPPTSlideShowBegin(object window)
|
||||
{
|
||||
Application.Current.Dispatcher.BeginInvoke(new Action(async () =>
|
||||
{
|
||||
@@ -369,7 +369,7 @@ namespace Ink_Canvas.Windows
|
||||
}), DispatcherPriority.Normal);
|
||||
}
|
||||
|
||||
private void OnPPTSlideChanged(Microsoft.Office.Interop.PowerPoint.SlideShowWindow window)
|
||||
private void OnPPTSlideChanged(object window)
|
||||
{
|
||||
Application.Current.Dispatcher.BeginInvoke(new Action(async () =>
|
||||
{
|
||||
@@ -420,7 +420,7 @@ namespace Ink_Canvas.Windows
|
||||
}), DispatcherPriority.Normal);
|
||||
}
|
||||
|
||||
private void OnPPTSlideShowEnd(Microsoft.Office.Interop.PowerPoint.Presentation presentation)
|
||||
private void OnPPTSlideShowEnd(object presentation)
|
||||
{
|
||||
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
@@ -1389,23 +1389,104 @@ namespace Ink_Canvas.Windows
|
||||
|
||||
if (pptManager != null)
|
||||
{
|
||||
// 获取当前演示文稿
|
||||
var getCurrentActivePresentationMethod = pptManager.GetType().GetMethod("GetCurrentActivePresentation");
|
||||
var presentation = getCurrentActivePresentationMethod?.Invoke(pptManager, null) as Microsoft.Office.Interop.PowerPoint.Presentation;
|
||||
// 尝试获取当前演示文稿,使用重试机制
|
||||
Microsoft.Office.Interop.PowerPoint.Presentation presentation = null;
|
||||
|
||||
// 首先尝试使用 CurrentPresentation 属性(可能是缓存的)
|
||||
try
|
||||
{
|
||||
var currentPresentationProperty = pptManager.GetType().GetProperty("CurrentPresentation",
|
||||
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
|
||||
presentation = currentPresentationProperty?.GetValue(pptManager) as Microsoft.Office.Interop.PowerPoint.Presentation;
|
||||
}
|
||||
catch { }
|
||||
|
||||
// 如果 CurrentPresentation 不可用,尝试 GetCurrentActivePresentation
|
||||
if (presentation == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var getCurrentActivePresentationMethod = pptManager.GetType().GetMethod("GetCurrentActivePresentation");
|
||||
presentation = getCurrentActivePresentationMethod?.Invoke(pptManager, null) as Microsoft.Office.Interop.PowerPoint.Presentation;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if (presentation != null)
|
||||
{
|
||||
// 生成演示文稿ID(与PPTInkManager一致)
|
||||
string presentationId = GeneratePresentationId(presentation);
|
||||
return Path.Combine(autoSaveLocation, "Auto Saved - Presentations", presentationId);
|
||||
try
|
||||
{
|
||||
// 立即检查COM对象是否仍然有效
|
||||
if (!System.Runtime.InteropServices.Marshal.IsComObject(presentation))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 尝试访问对象以验证其有效性
|
||||
try
|
||||
{
|
||||
var _ = System.Runtime.InteropServices.Marshal.GetIUnknownForObject(presentation);
|
||||
System.Runtime.InteropServices.Marshal.Release(_);
|
||||
}
|
||||
catch (System.Runtime.InteropServices.InvalidComObjectException)
|
||||
{
|
||||
// COM对象已失效,静默返回
|
||||
return null;
|
||||
}
|
||||
|
||||
// 立即生成演示文稿ID(在对象失效前)
|
||||
string presentationId = GeneratePresentationId(presentation);
|
||||
if (string.IsNullOrEmpty(presentationId) || presentationId.StartsWith("unknown_") || presentationId.StartsWith("invalid_") || presentationId.StartsWith("com_error_"))
|
||||
{
|
||||
// 生成ID失败,返回null而不是抛出异常
|
||||
return null;
|
||||
}
|
||||
|
||||
return Path.Combine(autoSaveLocation, "Auto Saved - Presentations", presentationId);
|
||||
}
|
||||
catch (System.Runtime.InteropServices.InvalidComObjectException)
|
||||
{
|
||||
// COM对象已失效,静默返回(不记录错误日志)
|
||||
return null;
|
||||
}
|
||||
catch (System.Runtime.InteropServices.COMException)
|
||||
{
|
||||
// COM异常,对象可能已失效,静默返回(不记录错误日志)
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 其他异常,只记录非COM相关异常
|
||||
if (!(ex is System.Runtime.InteropServices.InvalidComObjectException) &&
|
||||
!(ex is System.Runtime.InteropServices.COMException))
|
||||
{
|
||||
LogHelper.WriteLogToFile($"获取PPT文件夹路径时发生非COM异常: {ex.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.Runtime.InteropServices.InvalidComObjectException)
|
||||
{
|
||||
// COM对象已失效,静默返回(不记录错误日志)
|
||||
return null;
|
||||
}
|
||||
catch (System.Runtime.InteropServices.COMException)
|
||||
{
|
||||
// COM异常,对象可能已失效,静默返回(不记录错误日志)
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"获取PPT文件夹路径失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
// 只记录非COM相关异常
|
||||
if (!(ex is System.Runtime.InteropServices.InvalidComObjectException) &&
|
||||
!(ex is System.Runtime.InteropServices.COMException))
|
||||
{
|
||||
LogHelper.WriteLogToFile($"获取PPT文件夹路径失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -1416,11 +1497,87 @@ namespace Ink_Canvas.Windows
|
||||
/// </summary>
|
||||
private string GeneratePresentationId(Microsoft.Office.Interop.PowerPoint.Presentation presentation)
|
||||
{
|
||||
if (presentation == null)
|
||||
{
|
||||
return $"unknown_{DateTime.Now.Ticks}";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var presentationPath = presentation.FullName;
|
||||
// 检查COM对象是否仍然有效
|
||||
if (!System.Runtime.InteropServices.Marshal.IsComObject(presentation))
|
||||
{
|
||||
return $"unknown_{DateTime.Now.Ticks}";
|
||||
}
|
||||
|
||||
// 验证COM对象有效性
|
||||
try
|
||||
{
|
||||
var _ = System.Runtime.InteropServices.Marshal.GetIUnknownForObject(presentation);
|
||||
System.Runtime.InteropServices.Marshal.Release(_);
|
||||
}
|
||||
catch (System.Runtime.InteropServices.InvalidComObjectException)
|
||||
{
|
||||
// COM对象已失效
|
||||
return $"unknown_{DateTime.Now.Ticks}";
|
||||
}
|
||||
|
||||
// 逐个访问属性,每个都进行异常处理
|
||||
string presentationName = null;
|
||||
string presentationPath = null;
|
||||
int slidesCount = 0;
|
||||
|
||||
try
|
||||
{
|
||||
presentationName = presentation.Name;
|
||||
}
|
||||
catch (System.Runtime.InteropServices.InvalidComObjectException)
|
||||
{
|
||||
return $"unknown_{DateTime.Now.Ticks}";
|
||||
}
|
||||
catch (System.Runtime.InteropServices.COMException)
|
||||
{
|
||||
return $"unknown_{DateTime.Now.Ticks}";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
presentationPath = presentation.FullName;
|
||||
}
|
||||
catch (System.Runtime.InteropServices.InvalidComObjectException)
|
||||
{
|
||||
return $"unknown_{DateTime.Now.Ticks}";
|
||||
}
|
||||
catch (System.Runtime.InteropServices.COMException)
|
||||
{
|
||||
return $"unknown_{DateTime.Now.Ticks}";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
slidesCount = presentation.Slides.Count;
|
||||
}
|
||||
catch (System.Runtime.InteropServices.InvalidComObjectException)
|
||||
{
|
||||
return $"unknown_{DateTime.Now.Ticks}";
|
||||
}
|
||||
catch (System.Runtime.InteropServices.COMException)
|
||||
{
|
||||
return $"unknown_{DateTime.Now.Ticks}";
|
||||
}
|
||||
|
||||
var fileHash = GetFileHash(presentationPath);
|
||||
return $"{presentation.Name}_{presentation.Slides.Count}_{fileHash}";
|
||||
return $"{presentationName}_{slidesCount}_{fileHash}";
|
||||
}
|
||||
catch (System.Runtime.InteropServices.InvalidComObjectException)
|
||||
{
|
||||
// COM对象已失效
|
||||
return $"unknown_{DateTime.Now.Ticks}";
|
||||
}
|
||||
catch (System.Runtime.InteropServices.COMException)
|
||||
{
|
||||
// COM异常,对象可能已失效
|
||||
return $"unknown_{DateTime.Now.Ticks}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<Window x:Class="Ink_Canvas.PrivacyAgreementWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Ink_Canvas"
|
||||
mc:Ignorable="d" FontFamily="Microsoft YaHei UI" ui:WindowHelper.UseModernWindowStyle="True"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" Topmost="True"
|
||||
Title="隐私说明 - Ink Canvas" Height="600" Width="600"
|
||||
Loaded="Window_Loaded" Closing="Window_Closing">
|
||||
<Window.Resources>
|
||||
<SolidColorBrush x:Key="PrivacyAgreementWindowBackground" Color="White"/>
|
||||
<SolidColorBrush x:Key="PrivacyAgreementWindowForeground" Color="Black"/>
|
||||
<SolidColorBrush x:Key="PrivacyAgreementWindowButtonBackground" Color="#F4F4F5"/>
|
||||
<SolidColorBrush x:Key="PrivacyAgreementWindowButtonForeground" Color="Black"/>
|
||||
<SolidColorBrush x:Key="PrivacyAgreementWindowBorderBrush" Color="#E4E4E7"/>
|
||||
<SolidColorBrush x:Key="PrivacyAgreementWindowButtonAcceptBackground" Color="#3584e4"/>
|
||||
<SolidColorBrush x:Key="PrivacyAgreementWindowButtonAcceptForeground" Color="White"/>
|
||||
</Window.Resources>
|
||||
<Grid Background="{DynamicResource PrivacyAgreementWindowBackground}">
|
||||
<Label Content="隐私说明"
|
||||
Margin="10,10,10,0"
|
||||
VerticalAlignment="Top"
|
||||
Foreground="{DynamicResource PrivacyAgreementWindowForeground}"
|
||||
FontFamily="Microsoft YaHei UI"
|
||||
FontSize="18"
|
||||
FontWeight="Bold"/>
|
||||
<ScrollViewer Margin="10,45,10,60"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled">
|
||||
<TextBox Name="TextBoxPrivacyContent"
|
||||
FontFamily="Microsoft YaHei UI"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
AcceptsReturn="True"
|
||||
IsReadOnly="True"
|
||||
TextWrapping="Wrap"
|
||||
Background="{DynamicResource PrivacyAgreementWindowBackground}"
|
||||
Foreground="{DynamicResource PrivacyAgreementWindowForeground}"
|
||||
BorderBrush="{DynamicResource PrivacyAgreementWindowBorderBrush}"
|
||||
Padding="10"/>
|
||||
</ScrollViewer>
|
||||
<StackPanel Orientation="Horizontal"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Margin="10">
|
||||
<Button Name="ButtonCancel"
|
||||
Margin="0,0,10,0"
|
||||
Content="取消"
|
||||
FontFamily="Microsoft YaHei UI"
|
||||
Width="100"
|
||||
Height="35"
|
||||
Click="ButtonCancel_Click"
|
||||
Background="{DynamicResource PrivacyAgreementWindowButtonBackground}"
|
||||
Foreground="{DynamicResource PrivacyAgreementWindowButtonForeground}"
|
||||
BorderBrush="{DynamicResource PrivacyAgreementWindowBorderBrush}"/>
|
||||
<Button Name="ButtonAccept"
|
||||
Content="同意"
|
||||
FontFamily="Microsoft YaHei UI"
|
||||
Width="100"
|
||||
Height="35"
|
||||
Click="ButtonAccept_Click"
|
||||
Background="{DynamicResource PrivacyAgreementWindowButtonAcceptBackground}"
|
||||
Foreground="{DynamicResource PrivacyAgreementWindowButtonAcceptForeground}"
|
||||
BorderBrush="{DynamicResource PrivacyAgreementWindowBorderBrush}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
public partial class PrivacyAgreementWindow : Window
|
||||
{
|
||||
public bool UserAccepted { get; private set; } = false;
|
||||
private bool wasSettingsPanelVisible = false;
|
||||
|
||||
public PrivacyAgreementWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
this.Topmost = true;
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(this, 0.25);
|
||||
ApplyTheme();
|
||||
HideSettingsPanel();
|
||||
}
|
||||
|
||||
private void HideSettingsPanel()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Application.Current.MainWindow is MainWindow mainWindow)
|
||||
{
|
||||
var borderSettings = mainWindow.FindName("BorderSettings") as Border;
|
||||
var borderSettingsMask = mainWindow.FindName("BorderSettingsMask") as Border;
|
||||
|
||||
if (borderSettings != null)
|
||||
{
|
||||
wasSettingsPanelVisible = borderSettings.Visibility == Visibility.Visible;
|
||||
if (wasSettingsPanelVisible)
|
||||
{
|
||||
borderSettings.Visibility = Visibility.Hidden;
|
||||
}
|
||||
}
|
||||
|
||||
if (borderSettingsMask != null)
|
||||
{
|
||||
if (wasSettingsPanelVisible)
|
||||
{
|
||||
borderSettingsMask.Visibility = Visibility.Hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"隐藏设置面板失败: {ex.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.Topmost = true;
|
||||
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
this.Activate();
|
||||
this.Focus();
|
||||
this.Topmost = true;
|
||||
SetForegroundWindow(new WindowInteropHelper(this).Handle);
|
||||
}), DispatcherPriority.Loaded);
|
||||
|
||||
string privacyText = null;
|
||||
|
||||
try
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var resourceName = "Ink_Canvas.privacy.txt";
|
||||
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
|
||||
{
|
||||
if (stream != null)
|
||||
{
|
||||
using (StreamReader reader = new StreamReader(stream, System.Text.Encoding.UTF8))
|
||||
{
|
||||
privacyText = reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"读取隐私说明失败: {ex.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(privacyText))
|
||||
{
|
||||
privacyText = "未找到隐私说明文件(privacy.txt)。";
|
||||
}
|
||||
|
||||
TextBoxPrivacyContent.Text = privacyText;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"读取隐私说明失败: {ex.Message}", LogHelper.LogType.Warning);
|
||||
TextBoxPrivacyContent.Text = "读取隐私说明文件时出错。";
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
private void Window_Closing(object sender, CancelEventArgs e)
|
||||
{
|
||||
if (wasSettingsPanelVisible)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Application.Current.MainWindow is MainWindow mainWindow)
|
||||
{
|
||||
var borderSettings = mainWindow.FindName("BorderSettings") as Border;
|
||||
var borderSettingsMask = mainWindow.FindName("BorderSettingsMask") as Border;
|
||||
|
||||
if (borderSettings != null)
|
||||
{
|
||||
borderSettings.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
if (borderSettingsMask != null)
|
||||
{
|
||||
borderSettingsMask.Visibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"恢复设置面板失败: {ex.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ButtonCancel_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
UserAccepted = false;
|
||||
DialogResult = false;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void ButtonAccept_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
UserAccepted = true;
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void ApplyTheme()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (MainWindow.Settings != null)
|
||||
{
|
||||
ApplyTheme(MainWindow.Settings);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"应用隐私说明窗口主题出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyTheme(Settings settings)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (settings.Appearance.Theme == 0)
|
||||
{
|
||||
iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Light);
|
||||
ApplyThemeResources("Light");
|
||||
}
|
||||
else if (settings.Appearance.Theme == 1)
|
||||
{
|
||||
iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Dark);
|
||||
ApplyThemeResources("Dark");
|
||||
}
|
||||
else
|
||||
{
|
||||
bool isSystemLight = IsSystemThemeLight();
|
||||
if (isSystemLight)
|
||||
{
|
||||
iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Light);
|
||||
ApplyThemeResources("Light");
|
||||
}
|
||||
else
|
||||
{
|
||||
iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Dark);
|
||||
ApplyThemeResources("Dark");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"应用隐私说明窗口主题出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyThemeResources(string theme)
|
||||
{
|
||||
try
|
||||
{
|
||||
var resources = this.Resources;
|
||||
|
||||
if (theme == "Light")
|
||||
{
|
||||
resources["PrivacyAgreementWindowBackground"] = new SolidColorBrush(Color.FromRgb(255, 255, 255));
|
||||
resources["PrivacyAgreementWindowForeground"] = new SolidColorBrush(Color.FromRgb(24, 24, 27));
|
||||
resources["PrivacyAgreementWindowButtonBackground"] = new SolidColorBrush(Color.FromRgb(244, 244, 245));
|
||||
resources["PrivacyAgreementWindowButtonForeground"] = new SolidColorBrush(Color.FromRgb(24, 24, 27));
|
||||
resources["PrivacyAgreementWindowBorderBrush"] = new SolidColorBrush(Color.FromRgb(228, 228, 231));
|
||||
resources["PrivacyAgreementWindowButtonAcceptBackground"] = new SolidColorBrush(Color.FromRgb(53, 132, 228));
|
||||
resources["PrivacyAgreementWindowButtonAcceptForeground"] = new SolidColorBrush(Colors.White);
|
||||
}
|
||||
else
|
||||
{
|
||||
resources["PrivacyAgreementWindowBackground"] = new SolidColorBrush(Color.FromRgb(31, 31, 31));
|
||||
resources["PrivacyAgreementWindowForeground"] = new SolidColorBrush(Colors.White);
|
||||
resources["PrivacyAgreementWindowButtonBackground"] = new SolidColorBrush(Color.FromRgb(42, 42, 42));
|
||||
resources["PrivacyAgreementWindowButtonForeground"] = new SolidColorBrush(Colors.White);
|
||||
resources["PrivacyAgreementWindowBorderBrush"] = new SolidColorBrush(Color.FromRgb(224, 224, 224));
|
||||
resources["PrivacyAgreementWindowButtonAcceptBackground"] = new SolidColorBrush(Color.FromRgb(53, 132, 228));
|
||||
resources["PrivacyAgreementWindowButtonAcceptForeground"] = new SolidColorBrush(Colors.White);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"应用隐私说明窗口主题资源出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsSystemThemeLight()
|
||||
{
|
||||
var light = false;
|
||||
try
|
||||
{
|
||||
var registryKey = Microsoft.Win32.Registry.CurrentUser;
|
||||
var themeKey = registryKey.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize");
|
||||
if (themeKey != null)
|
||||
{
|
||||
var value = themeKey.GetValue("AppsUseLightTheme");
|
||||
if (value != null)
|
||||
{
|
||||
light = (int)value == 1;
|
||||
}
|
||||
themeKey.Close();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
light = true;
|
||||
}
|
||||
return light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,8 +417,8 @@
|
||||
</Grid>
|
||||
<Border Height="1" Background="#ebebeb" Margin="0,8,0,8"/>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,8,0,0">
|
||||
<Button x:Name="BtnManualBackup" Content="手动备份" Padding="12,6" Margin="0,0,12,0" Background="#2563eb" Foreground="White"/>
|
||||
<Button x:Name="BtnRestoreBackup" Content="还原备份" Padding="12,6" Background="#2563eb" Foreground="White"/>
|
||||
<Button x:Name="BtnManualBackup" Tag="manual_backup" Content="手动备份" Padding="12,6" Margin="0,0,12,0" Background="#2563eb" Foreground="White" Click="Button_Click"/>
|
||||
<Button x:Name="BtnRestoreBackup" Tag="restore_backup" Content="还原备份" Padding="12,6" Background="#2563eb" Foreground="White" Click="Button_Click"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
@@ -430,12 +430,37 @@
|
||||
<TextBlock Text="管理.icstk文件的关联设置,双击.icstk文件可直接在Ink Canvas中打开"
|
||||
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11" Margin="0,0,0,12"/>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,8,0,0">
|
||||
<Button x:Name="BtnUnregisterFileAssociation" Content="取消文件关联" Padding="15,5" Margin="0,0,8,0" Background="#2563eb" Foreground="White"/>
|
||||
<Button x:Name="BtnCheckFileAssociation" Content="检查关联状态" Padding="15,5" Margin="0,0,8,0" Background="#2563eb" Foreground="White"/>
|
||||
<Button x:Name="BtnRegisterFileAssociation" Content="重新注册关联" Padding="15,5" Background="#2563eb" Foreground="White"/>
|
||||
<Button x:Name="BtnUnregisterFileAssociation" Tag="unregister" Content="取消文件关联" Padding="15,5" Margin="0,0,8,0" Background="#2563eb" Foreground="White" Click="Button_Click"/>
|
||||
<Button x:Name="BtnCheckFileAssociation" Tag="check" Content="检查关联状态" Padding="15,5" Margin="0,0,8,0" Background="#2563eb" Foreground="White" Click="Button_Click"/>
|
||||
<Button x:Name="BtnRegisterFileAssociation" Tag="register" Content="重新注册关联" Padding="15,5" Background="#2563eb" Foreground="White" Click="Button_Click"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 外部协议调用 -->
|
||||
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
|
||||
<StackPanel Orientation="Vertical" Margin="18,18,18,18">
|
||||
<TextBlock Text="外部协议调用" FontWeight="Bold" Foreground="#2e3436" FontSize="18" Margin="0,0,0,12"/>
|
||||
<TextBlock Text="允许通过 icc:// 协议调用软件,支持从浏览器或其他应用快速控制软件"
|
||||
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11" Margin="0,0,0,12"/>
|
||||
<Grid Height="54">
|
||||
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="启用外部协议 (icc://)" HorizontalAlignment="Left"/>
|
||||
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后可通过 icc://fold (收纳) 或 icc://unfold (展开) 等命令控制" HorizontalAlignment="Left"/>
|
||||
</StackPanel>
|
||||
<Border x:Name="ToggleSwitchIsEnableUriScheme" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="IsEnableUriScheme" MouseLeftButtonDown="ToggleSwitch_Click"
|
||||
Focusable="True" KeyboardNavigation.IsTabStop="True" KeyDown="ToggleSwitch_KeyDown"
|
||||
AutomationProperties.Name="启用外部协议 (icc://)"
|
||||
AutomationProperties.HelpText="开启后可通过 icc://fold (收纳) 或 icc://unfold (展开) 等命令控制">
|
||||
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
|
||||
</Border.Effect>
|
||||
</Border>
|
||||
</Border>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 悬浮窗拦截 -->
|
||||
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
|
||||
@@ -636,7 +661,7 @@
|
||||
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
|
||||
<StackPanel Orientation="Vertical" Margin="18,18,18,18">
|
||||
<TextBlock Text="Dlass设置管理" FontWeight="Bold" Foreground="#2e3436" FontSize="18" Margin="0,0,0,12"/>
|
||||
<Button x:Name="BtnDlassSettingsManage" Content="Dlass设置管理" Padding="15,5" HorizontalAlignment="Left" Background="#2563eb" Foreground="White"/>
|
||||
<Button x:Name="BtnDlassSettingsManage" Tag="dlass_settings" Content="Dlass设置管理" Padding="15,5" HorizontalAlignment="Left" Background="#2563eb" Foreground="White" Click="Button_Click"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
@@ -141,6 +141,9 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchIsAutoBackupEnabled"), advanced.IsAutoBackupEnabled);
|
||||
SetOptionButtonState("AutoBackupInterval", advanced.AutoBackupIntervalDays);
|
||||
|
||||
// 外部协议
|
||||
SetToggleSwitchState(FindToggleSwitch("ToggleSwitchIsEnableUriScheme"), advanced.IsEnableUriScheme);
|
||||
|
||||
// 悬浮窗拦截
|
||||
// 注意:IsEnableFloatingWindowInterception 可能不在 Advanced 类中,需要确认
|
||||
// 这里先假设它在 Advanced 类中,如果不在,需要调整
|
||||
@@ -169,11 +172,12 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
if (toggleSwitch == null) return;
|
||||
toggleSwitch.Background = isOn
|
||||
? new SolidColorBrush(Color.FromRgb(53, 132, 228))
|
||||
: new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
: (ThemeHelper.IsDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(225, 225, 225)));
|
||||
var innerBorder = toggleSwitch.Child as Border;
|
||||
if (innerBorder != null)
|
||||
{
|
||||
innerBorder.HorizontalAlignment = isOn ? HorizontalAlignment.Right : HorizontalAlignment.Left;
|
||||
innerBorder.Background = new SolidColorBrush(Colors.White);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,7 +200,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
var button = this.FindDescendantByName($"{group}{buttonName}Border") as Border;
|
||||
if (button != null)
|
||||
{
|
||||
// 清除同组其他按钮的选中状态
|
||||
bool isDarkTheme = ThemeHelper.IsDarkTheme;
|
||||
var selectedBrush = isDarkTheme ? new SolidColorBrush(Color.FromRgb(25, 25, 25)) : new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
var unselectedBrush = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
var parent = button.Parent as Panel;
|
||||
if (parent != null)
|
||||
{
|
||||
@@ -207,27 +214,77 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
string childTag = childBorder.Tag?.ToString();
|
||||
if (!string.IsNullOrEmpty(childTag) && childTag.StartsWith(group + "_"))
|
||||
{
|
||||
childBorder.Background = new SolidColorBrush(Colors.Transparent);
|
||||
childBorder.Background = unselectedBrush;
|
||||
var textBlock = childBorder.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置当前按钮为选中状态
|
||||
button.Background = new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
button.Background = selectedBrush;
|
||||
var currentTextBlock = button.Child as TextBlock;
|
||||
if (currentTextBlock != null)
|
||||
{
|
||||
currentTextBlock.FontWeight = FontWeights.Bold;
|
||||
currentTextBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetCurrentSettingValue(string tag)
|
||||
{
|
||||
if (MainWindow.Settings?.Advanced == null) return false;
|
||||
|
||||
try
|
||||
{
|
||||
var advanced = MainWindow.Settings.Advanced;
|
||||
switch (tag)
|
||||
{
|
||||
case "IsSpecialScreen":
|
||||
return advanced.IsSpecialScreen;
|
||||
case "EraserBindTouchMultiplier":
|
||||
return advanced.EraserBindTouchMultiplier;
|
||||
case "IsQuadIR":
|
||||
return advanced.IsQuadIR;
|
||||
case "IsLogEnabled":
|
||||
return advanced.IsLogEnabled;
|
||||
case "IsSaveLogByDate":
|
||||
return advanced.IsSaveLogByDate;
|
||||
case "IsSecondConfirmWhenShutdownApp":
|
||||
return advanced.IsSecondConfirmWhenShutdownApp;
|
||||
case "IsEnableFullScreenHelper":
|
||||
return advanced.IsEnableFullScreenHelper;
|
||||
case "IsEnableAvoidFullScreenHelper":
|
||||
return advanced.IsEnableAvoidFullScreenHelper;
|
||||
case "IsEnableEdgeGestureUtil":
|
||||
return advanced.IsEnableEdgeGestureUtil;
|
||||
case "IsEnableForceFullScreen":
|
||||
return advanced.IsEnableForceFullScreen;
|
||||
case "IsEnableDPIChangeDetection":
|
||||
return advanced.IsEnableDPIChangeDetection;
|
||||
case "IsEnableResolutionChangeDetection":
|
||||
return advanced.IsEnableResolutionChangeDetection;
|
||||
case "IsAutoBackupBeforeUpdate":
|
||||
return advanced.IsAutoBackupBeforeUpdate;
|
||||
case "IsAutoBackupEnabled":
|
||||
return advanced.IsAutoBackupEnabled;
|
||||
case "IsEnableUriScheme":
|
||||
return advanced.IsEnableUriScheme;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ToggleSwitch点击事件处理
|
||||
/// </summary>
|
||||
@@ -238,13 +295,13 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
var border = sender as Border;
|
||||
if (border == null) return;
|
||||
|
||||
bool isOn = border.Background.ToString() == "#FF3584E4";
|
||||
bool newState = !isOn;
|
||||
SetToggleSwitchState(border, newState);
|
||||
|
||||
string tag = border.Tag?.ToString();
|
||||
if (string.IsNullOrEmpty(tag)) return;
|
||||
|
||||
bool currentState = GetCurrentSettingValue(tag);
|
||||
bool newState = !currentState;
|
||||
SetToggleSwitchState(border, newState);
|
||||
|
||||
var advanced = MainWindow.Settings.Advanced;
|
||||
if (advanced == null) return;
|
||||
|
||||
@@ -319,6 +376,23 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
// 调用 MainWindow 中的方法
|
||||
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsAutoBackupEnabled", newState);
|
||||
break;
|
||||
|
||||
case "IsEnableUriScheme":
|
||||
// 调用 MainWindow 中的方法
|
||||
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsEnableUriScheme", newState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ToggleSwitch键盘事件处理
|
||||
/// </summary>
|
||||
private void ToggleSwitch_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == System.Windows.Input.Key.Space || e.Key == System.Windows.Input.Key.Enter)
|
||||
{
|
||||
ToggleSwitch_Click(sender, new RoutedEventArgs());
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,7 +454,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
string group = parts[0];
|
||||
string value = parts[1];
|
||||
|
||||
// 清除同组其他按钮的选中状态
|
||||
bool isDarkTheme = ThemeHelper.IsDarkTheme;
|
||||
var selectedBrush = isDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
var unselectedBrush = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
var parent = border.Parent as Panel;
|
||||
if (parent != null)
|
||||
{
|
||||
@@ -391,23 +468,24 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
string childTag = childBorder.Tag?.ToString();
|
||||
if (!string.IsNullOrEmpty(childTag) && childTag.StartsWith(group + "_"))
|
||||
{
|
||||
childBorder.Background = new SolidColorBrush(Colors.Transparent);
|
||||
childBorder.Background = unselectedBrush;
|
||||
var textBlock = childBorder.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置当前按钮为选中状态
|
||||
border.Background = new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
border.Background = selectedBrush;
|
||||
var currentTextBlock = border.Child as TextBlock;
|
||||
if (currentTextBlock != null)
|
||||
{
|
||||
currentTextBlock.FontWeight = FontWeights.Bold;
|
||||
currentTextBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
|
||||
if (MainWindow.Settings.Advanced == null) return;
|
||||
@@ -458,31 +536,39 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
var button = sender as Button;
|
||||
if (button == null) return;
|
||||
|
||||
string name = button.Name;
|
||||
string action = button.Tag?.ToString();
|
||||
if (string.IsNullOrEmpty(action)) action = button.Name;
|
||||
|
||||
// 这些按钮的功能可能需要调用 MainWindow 中的方法
|
||||
// 暂时先留空,后续可以根据需要实现
|
||||
switch (name)
|
||||
switch (action)
|
||||
{
|
||||
case "manual_backup":
|
||||
case "BtnManualBackup":
|
||||
// TODO: 调用 MainWindow 的备份方法
|
||||
break;
|
||||
|
||||
case "restore_backup":
|
||||
case "BtnRestoreBackup":
|
||||
// TODO: 调用 MainWindow 的还原方法
|
||||
break;
|
||||
|
||||
case "unregister":
|
||||
case "BtnUnregisterFileAssociation":
|
||||
// TODO: 调用 MainWindow 的取消文件关联方法
|
||||
break;
|
||||
|
||||
case "check":
|
||||
case "BtnCheckFileAssociation":
|
||||
// TODO: 调用 MainWindow 的检查文件关联方法
|
||||
// TODO: 调用 MainWindow 的检查文件关联状态方法
|
||||
break;
|
||||
|
||||
case "register":
|
||||
case "BtnRegisterFileAssociation":
|
||||
// TODO: 调用 MainWindow 的注册文件关联方法
|
||||
break;
|
||||
|
||||
case "dlass_settings":
|
||||
case "BtnDlassSettingsManage":
|
||||
// TODO: 调用 MainWindow 的 Dlass 设置管理方法
|
||||
break;
|
||||
@@ -497,6 +583,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
try
|
||||
{
|
||||
ThemeHelper.ApplyThemeToControl(this);
|
||||
if (_isLoaded)
|
||||
{
|
||||
LoadSettings();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -288,7 +288,7 @@
|
||||
<Image Source="/Resources/Icons-png/PPTTools.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动查杀希沃"PPT 小工具"" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<Border x:Name="ToggleSwitchAutoKillPptService" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
|
||||
<Border x:Name="ToggleSwitchAutoKillPptService" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
|
||||
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
|
||||
@@ -304,7 +304,7 @@
|
||||
<Image Source="/Resources/Icons-png/EasiNote.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动查杀 希沃白板5" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<Border x:Name="ToggleSwitchAutoKillEasiNote" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
|
||||
<Border x:Name="ToggleSwitchAutoKillEasiNote" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
|
||||
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
|
||||
@@ -318,7 +318,7 @@
|
||||
<Image Source="/Resources/Icons-png/HiteAnnotation.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动查杀 鸿合屏幕书写" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<Border x:Name="ToggleSwitchAutoKillHiteAnnotation" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
|
||||
<Border x:Name="ToggleSwitchAutoKillHiteAnnotation" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
|
||||
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
|
||||
@@ -332,7 +332,7 @@
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="鸿合屏幕书写查杀后自动进入批注" HorizontalAlignment="Left"/>
|
||||
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="查杀鸿合屏幕书写后自动进入批注模式" HorizontalAlignment="Left"/>
|
||||
</StackPanel>
|
||||
<Border x:Name="ToggleSwitchAutoEnterAnnotationAfterKillHite" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1">
|
||||
<Border x:Name="ToggleSwitchAutoEnterAnnotationAfterKillHite" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1" MouseLeftButtonDown="ToggleSwitch_Click">
|
||||
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
|
||||
@@ -346,7 +346,7 @@
|
||||
<Image Source="/Resources/Icons-png/VComYouJiao.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动查杀 优教授课端" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<Border x:Name="ToggleSwitchAutoKillVComYouJiao" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
|
||||
<Border x:Name="ToggleSwitchAutoKillVComYouJiao" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
|
||||
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
|
||||
@@ -360,7 +360,7 @@
|
||||
<Image Source="/Resources/Icons-png/Seewo2Annotation.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动查杀 希沃桌面2.0 桌面批注" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<Border x:Name="ToggleSwitchAutoKillSeewoLauncher2DesktopAnnotation" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
|
||||
<Border x:Name="ToggleSwitchAutoKillSeewoLauncher2DesktopAnnotation" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
|
||||
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
|
||||
@@ -377,7 +377,7 @@
|
||||
<Image Source="/Resources/Icons-png/InkCanvas.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动查杀 Ink Canvas 和 IC+" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<Border x:Name="ToggleSwitchAutoKillInkCanvas" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
|
||||
<Border x:Name="ToggleSwitchAutoKillInkCanvas" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
|
||||
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
|
||||
@@ -391,7 +391,7 @@
|
||||
<Image Source="/Resources/Icons-png/ica.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动查杀ICA(新版旧版通杀)" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<Border x:Name="ToggleSwitchAutoKillICA" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
|
||||
<Border x:Name="ToggleSwitchAutoKillICA" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
|
||||
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
|
||||
@@ -405,7 +405,7 @@
|
||||
<Image Source="/Resources/Icons-png/idt.png" Margin="0,0,6,0" Width="28" Height="28" VerticalAlignment="Center"/>
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动查杀 智绘教Inkeys(仅限新版)" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<Border x:Name="ToggleSwitchAutoKillIDT" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4">
|
||||
<Border x:Name="ToggleSwitchAutoKillIDT" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" MouseLeftButtonDown="ToggleSwitch_Click">
|
||||
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using Ink_Canvas.Windows.SettingsViews;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Ink_Canvas.Windows.SettingsViews
|
||||
{
|
||||
@@ -41,7 +42,7 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
|
||||
var automation = MainWindow.Settings.Automation;
|
||||
|
||||
// 设置所有 ToggleSwitch 的状态
|
||||
// 自动收纳相关
|
||||
SetToggleSwitchState("ToggleSwitchAutoFoldInEasiNote", automation.IsAutoFoldInEasiNote);
|
||||
SetToggleSwitchState("ToggleSwitchAutoFoldInEasiCamera", automation.IsAutoFoldInEasiCamera);
|
||||
SetToggleSwitchState("ToggleSwitchAutoFoldInHiteTouchPro", automation.IsAutoFoldInHiteTouchPro);
|
||||
@@ -62,10 +63,24 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
SetToggleSwitchState("ToggleSwitchAutoFoldInEasiNoteIgnoreDesktopAnno", automation.IsAutoFoldInEasiNoteIgnoreDesktopAnno);
|
||||
SetToggleSwitchState("ToggleSwitchAutoFoldInOldZyBoard", automation.IsAutoFoldInOldZyBoard);
|
||||
SetToggleSwitchState("ToggleSwitchKeepFoldAfterSoftwareExit", automation.KeepFoldAfterSoftwareExit);
|
||||
|
||||
// 自动查杀相关
|
||||
SetToggleSwitchState("ToggleSwitchAutoKillPptService", automation.IsAutoKillPptService);
|
||||
SetToggleSwitchState("ToggleSwitchAutoKillEasiNote", automation.IsAutoKillEasiNote);
|
||||
SetToggleSwitchState("ToggleSwitchAutoKillHiteAnnotation", automation.IsAutoKillHiteAnnotation);
|
||||
SetToggleSwitchState("ToggleSwitchAutoEnterAnnotationAfterKillHite", automation.IsAutoEnterAnnotationAfterKillHite);
|
||||
SetToggleSwitchState("ToggleSwitchAutoKillVComYouJiao", automation.IsAutoKillVComYouJiao);
|
||||
SetToggleSwitchState("ToggleSwitchAutoKillSeewoLauncher2DesktopAnnotation", automation.IsAutoKillSeewoLauncher2DesktopAnnotation);
|
||||
SetToggleSwitchState("ToggleSwitchAutoKillInkCanvas", automation.IsAutoKillInkCanvas);
|
||||
SetToggleSwitchState("ToggleSwitchAutoKillICA", automation.IsAutoKillICA);
|
||||
SetToggleSwitchState("ToggleSwitchAutoKillIDT", automation.IsAutoKillIDT);
|
||||
|
||||
_isLoaded = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"AutomationPanel 加载设置时出错: {ex.Message}");
|
||||
_isLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,19 +91,19 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
{
|
||||
try
|
||||
{
|
||||
var border = FindName(name) as System.Windows.Controls.Border;
|
||||
if (border != null)
|
||||
var border = FindName(name) as Border;
|
||||
if (border == null) return;
|
||||
|
||||
border.Background = isOn
|
||||
? new SolidColorBrush(Color.FromRgb(0x35, 0x84, 0xE4))
|
||||
: (ThemeHelper.IsDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(0xE1, 0xE1, 0xE1)));
|
||||
|
||||
var innerBorder = border.Child as Border;
|
||||
if (innerBorder != null)
|
||||
{
|
||||
bool currentState = border.Background.ToString().Contains("3584e4");
|
||||
if (currentState != isOn)
|
||||
{
|
||||
border.Background = isOn ? new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(0x35, 0x84, 0xe4)) : new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(0xe1, 0xe1, 0xe1));
|
||||
var innerBorder = border.Child as System.Windows.Controls.Border;
|
||||
if (innerBorder != null)
|
||||
{
|
||||
innerBorder.HorizontalAlignment = isOn ? HorizontalAlignment.Right : HorizontalAlignment.Left;
|
||||
}
|
||||
}
|
||||
innerBorder.HorizontalAlignment = isOn ? HorizontalAlignment.Right : HorizontalAlignment.Left;
|
||||
// 保持滑块为白色(主题切换时也更稳定)
|
||||
innerBorder.Background = new SolidColorBrush(Colors.White);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -97,6 +112,27 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsToggleSwitchOn(Border border)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (border?.Background is SolidColorBrush scb)
|
||||
{
|
||||
return scb.Color.A == 0xFF &&
|
||||
scb.Color.R == 0x35 &&
|
||||
scb.Color.G == 0x84 &&
|
||||
scb.Color.B == 0xE4;
|
||||
}
|
||||
|
||||
var s = border?.Background?.ToString();
|
||||
return string.Equals(s, "#FF3584E4", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ToggleSwitch 点击事件处理
|
||||
/// </summary>
|
||||
@@ -104,11 +140,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
{
|
||||
if (!_isLoaded) return;
|
||||
|
||||
var border = sender as System.Windows.Controls.Border;
|
||||
var border = sender as Border;
|
||||
if (border == null) return;
|
||||
|
||||
bool currentState = border.Background.ToString().Contains("3584e4");
|
||||
bool newState = !currentState;
|
||||
bool newState = !IsToggleSwitchOn(border);
|
||||
SetToggleSwitchState(border.Name, newState);
|
||||
|
||||
// 通过 MainWindowSettingsHelper 调用 MainWindow 中的方法
|
||||
|
||||
@@ -249,6 +249,20 @@
|
||||
<TextBlock x:Name="InkFadeTimeText" Text="3000ms" VerticalAlignment="Center" FontSize="14" Margin="12,0,0,0" Foreground="#2e3436"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Border Height="1" Background="#ebebeb"/>
|
||||
<Grid Height="54">
|
||||
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="在笔工具菜单中隐藏墨迹渐隐控制" HorizontalAlignment="Left"/>
|
||||
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="开启后,主工具栏上点击笔工具后弹出的上下文菜单中将不显示墨迹渐隐控制开关" HorizontalAlignment="Left"/>
|
||||
</StackPanel>
|
||||
<Border x:Name="ToggleSwitchHideInkFadeControlInPenMenu" Style="{StaticResource ToggleSwitchStyle}" Background="#e1e1e1" Tag="HideInkFadeControlInPenMenu" MouseLeftButtonDown="ToggleSwitch_Click">
|
||||
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Left" VerticalAlignment="Center">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
|
||||
</Border.Effect>
|
||||
</Border>
|
||||
</Border>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
|
||||
@@ -184,11 +184,12 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
if (toggleSwitch == null) return;
|
||||
toggleSwitch.Background = isOn
|
||||
? new SolidColorBrush(Color.FromRgb(53, 132, 228))
|
||||
: new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
: (ThemeHelper.IsDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(225, 225, 225)));
|
||||
var innerBorder = toggleSwitch.Child as Border;
|
||||
if (innerBorder != null)
|
||||
{
|
||||
innerBorder.HorizontalAlignment = isOn ? HorizontalAlignment.Right : HorizontalAlignment.Left;
|
||||
innerBorder.Background = new SolidColorBrush(Colors.White);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,6 +203,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
|
||||
string[] buttonNames = group == "EraserSize" ? buttons : hyperbolaButtons;
|
||||
|
||||
bool isDarkTheme = ThemeHelper.IsDarkTheme;
|
||||
var selectedBrush = isDarkTheme ? new SolidColorBrush(Color.FromRgb(25, 25, 25)) : new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
var unselectedBrush = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
for (int i = 0; i < buttonNames.Length && i <= selectedIndex; i++)
|
||||
{
|
||||
var button = this.FindDescendantByName($"{group}{buttonNames[i]}") as Border;
|
||||
@@ -209,26 +214,89 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
{
|
||||
if (i == selectedIndex)
|
||||
{
|
||||
button.Background = new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
button.Background = selectedBrush;
|
||||
var textBlock = button.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = FontWeights.Bold;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
button.Background = new SolidColorBrush(Colors.Transparent);
|
||||
button.Background = unselectedBrush;
|
||||
var textBlock = button.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetCurrentSettingValue(string tag)
|
||||
{
|
||||
if (MainWindow.Settings?.Canvas == null) return false;
|
||||
|
||||
try
|
||||
{
|
||||
var canvas = MainWindow.Settings.Canvas;
|
||||
switch (tag)
|
||||
{
|
||||
case "ShowCursor":
|
||||
return canvas.IsShowCursor;
|
||||
case "EnablePressureTouchMode":
|
||||
return canvas.EnablePressureTouchMode;
|
||||
case "DisablePressure":
|
||||
return canvas.DisablePressure;
|
||||
case "HideStrokeWhenSelecting":
|
||||
return canvas.HideStrokeWhenSelecting;
|
||||
case "ClearCanvasAndClearTimeMachine":
|
||||
return canvas.ClearCanvasAndClearTimeMachine;
|
||||
case "ClearCanvasAlsoClearImages":
|
||||
return canvas.ClearCanvasAlsoClearImages;
|
||||
case "CompressPicturesUploaded":
|
||||
return canvas.IsCompressPicturesUploaded;
|
||||
case "ShowCircleCenter":
|
||||
return canvas.ShowCircleCenter;
|
||||
case "FitToCurve":
|
||||
return canvas.FitToCurve && !canvas.UseAdvancedBezierSmoothing;
|
||||
case "AdvancedBezierSmoothing":
|
||||
return canvas.UseAdvancedBezierSmoothing;
|
||||
case "UseAsyncInkSmoothing":
|
||||
return canvas.UseAsyncInkSmoothing;
|
||||
case "UseHardwareAcceleration":
|
||||
return canvas.UseHardwareAcceleration;
|
||||
case "AutoStraightenLine":
|
||||
return canvas.AutoStraightenLine;
|
||||
case "HighPrecisionLineStraighten":
|
||||
return canvas.HighPrecisionLineStraighten;
|
||||
case "LineEndpointSnapping":
|
||||
return canvas.LineEndpointSnapping;
|
||||
case "EnableInkFade":
|
||||
return canvas.EnableInkFade;
|
||||
case "HideInkFadeControlInPenMenu":
|
||||
return canvas.HideInkFadeControlInPenMenu;
|
||||
case "EnableAutoSaveStrokes":
|
||||
return MainWindow.Settings.Automation?.IsEnableAutoSaveStrokes ?? false;
|
||||
case "SaveFullPageStrokes":
|
||||
return MainWindow.Settings.Automation?.IsSaveFullPageStrokes ?? false;
|
||||
case "SaveStrokesAsXML":
|
||||
return MainWindow.Settings.Automation?.IsSaveStrokesAsXML ?? false;
|
||||
case "AutoSaveStrokesInPowerPoint":
|
||||
return MainWindow.Settings.PowerPointSettings?.IsAutoSaveStrokesInPowerPoint ?? false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ToggleSwitch点击事件处理
|
||||
/// </summary>
|
||||
@@ -239,13 +307,13 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
var border = sender as Border;
|
||||
if (border == null) return;
|
||||
|
||||
bool isOn = border.Background.ToString() == "#FF3584E4";
|
||||
bool newState = !isOn;
|
||||
SetToggleSwitchState(border, newState);
|
||||
|
||||
string tag = border.Tag?.ToString();
|
||||
if (string.IsNullOrEmpty(tag)) return;
|
||||
|
||||
bool currentState = GetCurrentSettingValue(tag);
|
||||
bool newState = !currentState;
|
||||
SetToggleSwitchState(border, newState);
|
||||
|
||||
var canvas = MainWindow.Settings.Canvas;
|
||||
if (canvas == null) return;
|
||||
|
||||
@@ -360,6 +428,11 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
}
|
||||
break;
|
||||
|
||||
case "HideInkFadeControlInPenMenu":
|
||||
// 调用 MainWindow 中的方法
|
||||
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchHideInkFadeControlInPenMenu", newState);
|
||||
break;
|
||||
|
||||
case "EnableAutoSaveStrokes":
|
||||
// 调用 MainWindow 中的方法
|
||||
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchEnableAutoSaveStrokes", newState);
|
||||
@@ -401,7 +474,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
string group = parts[0];
|
||||
string value = parts[1];
|
||||
|
||||
// 清除同组其他按钮的选中状态
|
||||
bool isDarkTheme = ThemeHelper.IsDarkTheme;
|
||||
var selectedBrush = isDarkTheme ? new SolidColorBrush(Color.FromRgb(25, 25, 25)) : new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
var unselectedBrush = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
var parent = border.Parent as Panel;
|
||||
if (parent != null)
|
||||
{
|
||||
@@ -412,23 +488,24 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
string childTag = childBorder.Tag?.ToString();
|
||||
if (!string.IsNullOrEmpty(childTag) && childTag.StartsWith(group + "_"))
|
||||
{
|
||||
childBorder.Background = new SolidColorBrush(Colors.Transparent);
|
||||
childBorder.Background = unselectedBrush;
|
||||
var textBlock = childBorder.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置当前按钮为选中状态
|
||||
border.Background = new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
border.Background = selectedBrush;
|
||||
var currentTextBlock = border.Child as TextBlock;
|
||||
if (currentTextBlock != null)
|
||||
{
|
||||
currentTextBlock.FontWeight = FontWeights.Bold;
|
||||
currentTextBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
|
||||
var canvas = MainWindow.Settings.Canvas;
|
||||
@@ -596,6 +673,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
try
|
||||
{
|
||||
ThemeHelper.ApplyThemeToControl(this);
|
||||
if (_isLoaded)
|
||||
{
|
||||
LoadSettings();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -122,11 +122,12 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
if (toggleSwitch == null) return;
|
||||
toggleSwitch.Background = isOn
|
||||
? new SolidColorBrush(Color.FromRgb(53, 132, 228))
|
||||
: new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
: (ThemeHelper.IsDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(225, 225, 225)));
|
||||
var innerBorder = toggleSwitch.Child as Border;
|
||||
if (innerBorder != null)
|
||||
{
|
||||
innerBorder.HorizontalAlignment = isOn ? HorizontalAlignment.Right : HorizontalAlignment.Left;
|
||||
innerBorder.Background = new SolidColorBrush(Colors.White);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,6 +145,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
|
||||
string[] buttonNames = buttons[group];
|
||||
|
||||
bool isDarkTheme = ThemeHelper.IsDarkTheme;
|
||||
var selectedBrush = isDarkTheme ? new SolidColorBrush(Color.FromRgb(25, 25, 25)) : new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
var unselectedBrush = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
for (int i = 0; i < buttonNames.Length; i++)
|
||||
{
|
||||
var button = this.FindDescendantByName($"{group}{buttonNames[i]}Border") as Border;
|
||||
@@ -151,26 +156,52 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
{
|
||||
if (i == selectedIndex)
|
||||
{
|
||||
button.Background = new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
button.Background = selectedBrush;
|
||||
var textBlock = button.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = FontWeights.Bold;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
button.Background = new SolidColorBrush(Colors.Transparent);
|
||||
button.Background = unselectedBrush;
|
||||
var textBlock = button.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetCurrentSettingValue(string tag)
|
||||
{
|
||||
if (MainWindow.Settings == null) return false;
|
||||
|
||||
try
|
||||
{
|
||||
switch (tag)
|
||||
{
|
||||
case "AutoSwitchTwoFingerGesture":
|
||||
return MainWindow.Settings.Gesture?.AutoSwitchTwoFingerGesture ?? false;
|
||||
case "EnableTwoFingerRotationOnSelection":
|
||||
return MainWindow.Settings.Gesture?.IsEnableTwoFingerRotationOnSelection ?? false;
|
||||
case "EnablePalmEraser":
|
||||
return MainWindow.Settings.Canvas?.EnablePalmEraser ?? false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ToggleSwitch点击事件处理
|
||||
/// </summary>
|
||||
@@ -181,13 +212,13 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
var border = sender as Border;
|
||||
if (border == null) return;
|
||||
|
||||
bool isOn = border.Background.ToString() == "#FF3584E4";
|
||||
bool newState = !isOn;
|
||||
SetToggleSwitchState(border, newState);
|
||||
|
||||
string tag = border.Tag?.ToString();
|
||||
if (string.IsNullOrEmpty(tag)) return;
|
||||
|
||||
bool currentState = GetCurrentSettingValue(tag);
|
||||
bool newState = !currentState;
|
||||
SetToggleSwitchState(border, newState);
|
||||
|
||||
switch (tag)
|
||||
{
|
||||
case "AutoSwitchTwoFingerGesture":
|
||||
@@ -231,7 +262,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
string group = parts[0];
|
||||
string value = parts[1];
|
||||
|
||||
// 清除同组其他按钮的选中状态
|
||||
bool isDarkTheme = ThemeHelper.IsDarkTheme;
|
||||
var selectedBrush = isDarkTheme ? new SolidColorBrush(Color.FromRgb(25, 25, 25)) : new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
var unselectedBrush = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
var parent = border.Parent as Panel;
|
||||
if (parent != null)
|
||||
{
|
||||
@@ -242,23 +276,24 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
string childTag = childBorder.Tag?.ToString();
|
||||
if (!string.IsNullOrEmpty(childTag) && childTag.StartsWith(group + "_"))
|
||||
{
|
||||
childBorder.Background = new SolidColorBrush(Colors.Transparent);
|
||||
childBorder.Background = unselectedBrush;
|
||||
var textBlock = childBorder.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置当前按钮为选中状态
|
||||
border.Background = new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
border.Background = selectedBrush;
|
||||
var currentTextBlock = border.Child as TextBlock;
|
||||
if (currentTextBlock != null)
|
||||
{
|
||||
currentTextBlock.FontWeight = FontWeights.Bold;
|
||||
currentTextBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
|
||||
switch (group)
|
||||
@@ -315,6 +350,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
try
|
||||
{
|
||||
ThemeHelper.ApplyThemeToControl(this);
|
||||
if (_isLoaded)
|
||||
{
|
||||
LoadSettings();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -169,11 +169,12 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
if (toggleSwitch == null) return;
|
||||
toggleSwitch.Background = isOn
|
||||
? new SolidColorBrush(Color.FromRgb(53, 132, 228))
|
||||
: new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
: (ThemeHelper.IsDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(225, 225, 225)));
|
||||
var innerBorder = toggleSwitch.Child as Border;
|
||||
if (innerBorder != null)
|
||||
{
|
||||
innerBorder.HorizontalAlignment = isOn ? HorizontalAlignment.Right : HorizontalAlignment.Left;
|
||||
innerBorder.Background = new SolidColorBrush(Colors.White);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,6 +193,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
|
||||
string[] buttonNames = buttons[group];
|
||||
|
||||
bool isDarkTheme = ThemeHelper.IsDarkTheme;
|
||||
var selectedBrush = isDarkTheme ? new SolidColorBrush(Color.FromRgb(25, 25, 25)) : new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
var unselectedBrush = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
for (int i = 0; i < buttonNames.Length; i++)
|
||||
{
|
||||
var button = this.FindDescendantByName($"{group}{buttonNames[i]}Border") as Border;
|
||||
@@ -199,26 +204,59 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
{
|
||||
if (i == selectedIndex)
|
||||
{
|
||||
button.Background = new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
button.Background = selectedBrush;
|
||||
var textBlock = button.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = FontWeights.Bold;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
button.Background = new SolidColorBrush(Colors.Transparent);
|
||||
button.Background = unselectedBrush;
|
||||
var textBlock = button.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetCurrentSettingValue(string tag)
|
||||
{
|
||||
if (MainWindow.Settings?.RandSettings == null) return false;
|
||||
|
||||
try
|
||||
{
|
||||
var randSettings = MainWindow.Settings.RandSettings;
|
||||
switch (tag)
|
||||
{
|
||||
case "DisplayRandWindowNamesInputBtn":
|
||||
return randSettings.DisplayRandWindowNamesInputBtn;
|
||||
case "ShowRandomAndSingleDraw":
|
||||
return randSettings.ShowRandomAndSingleDraw;
|
||||
case "EnableQuickDraw":
|
||||
return randSettings.EnableQuickDraw;
|
||||
case "ExternalCaller":
|
||||
return randSettings.DirectCallCiRand;
|
||||
case "UseNewRollCallUI":
|
||||
return randSettings.UseNewRollCallUI;
|
||||
case "EnableMLAvoidance":
|
||||
return randSettings.EnableMLAvoidance;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ToggleSwitch点击事件处理
|
||||
/// </summary>
|
||||
@@ -229,13 +267,13 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
var border = sender as Border;
|
||||
if (border == null) return;
|
||||
|
||||
bool isOn = border.Background.ToString() == "#FF3584E4";
|
||||
bool newState = !isOn;
|
||||
SetToggleSwitchState(border, newState);
|
||||
|
||||
string tag = border.Tag?.ToString();
|
||||
if (string.IsNullOrEmpty(tag)) return;
|
||||
|
||||
bool currentState = GetCurrentSettingValue(tag);
|
||||
bool newState = !currentState;
|
||||
SetToggleSwitchState(border, newState);
|
||||
|
||||
var randSettings = MainWindow.Settings.RandSettings;
|
||||
if (randSettings == null) return;
|
||||
|
||||
@@ -292,7 +330,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
string group = parts[0];
|
||||
string value = parts[1];
|
||||
|
||||
// 清除同组其他按钮的选中状态
|
||||
bool isDarkTheme = ThemeHelper.IsDarkTheme;
|
||||
var selectedBrush = isDarkTheme ? new SolidColorBrush(Color.FromRgb(25, 25, 25)) : new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
var unselectedBrush = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
var parent = border.Parent as Panel;
|
||||
if (parent != null)
|
||||
{
|
||||
@@ -303,23 +344,24 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
string childTag = childBorder.Tag?.ToString();
|
||||
if (!string.IsNullOrEmpty(childTag) && childTag.StartsWith(group + "_"))
|
||||
{
|
||||
childBorder.Background = new SolidColorBrush(Colors.Transparent);
|
||||
childBorder.Background = unselectedBrush;
|
||||
var textBlock = childBorder.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置当前按钮为选中状态
|
||||
border.Background = new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
border.Background = selectedBrush;
|
||||
var currentTextBlock = border.Child as TextBlock;
|
||||
if (currentTextBlock != null)
|
||||
{
|
||||
currentTextBlock.FontWeight = FontWeights.Bold;
|
||||
currentTextBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
|
||||
var randSettings = MainWindow.Settings.RandSettings;
|
||||
@@ -434,6 +476,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
try
|
||||
{
|
||||
ThemeHelper.ApplyThemeToControl(this);
|
||||
if (_isLoaded)
|
||||
{
|
||||
LoadSettings();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -10,9 +10,6 @@ using Media = System.Windows.Media;
|
||||
|
||||
namespace Ink_Canvas.Windows.SettingsViews
|
||||
{
|
||||
/// <summary>
|
||||
/// 辅助类:用于在新设置面板中调用 MainWindow 中已有的设置处理方法
|
||||
/// </summary>
|
||||
public static class MainWindowSettingsHelper
|
||||
{
|
||||
private static MainWindow GetMainWindow()
|
||||
@@ -446,6 +443,7 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
{ "ToggleSwitchIsEnableResolutionChangeDetection", "AdvancedPanel" },
|
||||
{ "ToggleSwitchIsAutoBackupBeforeUpdate", "AdvancedPanel" },
|
||||
{ "ToggleSwitchIsAutoBackupEnabled", "AdvancedPanel" },
|
||||
{ "ToggleSwitchIsEnableUriScheme", "AdvancedPanel" },
|
||||
|
||||
// LuckyRandomPanel
|
||||
{ "ToggleSwitchDisplayRandWindowNamesInputBtn", "LuckyRandomPanel" },
|
||||
|
||||
@@ -261,11 +261,12 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
if (toggleSwitch == null) return;
|
||||
toggleSwitch.Background = isOn
|
||||
? new SolidColorBrush(Color.FromRgb(53, 132, 228))
|
||||
: new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
: (ThemeHelper.IsDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(225, 225, 225)));
|
||||
var innerBorder = toggleSwitch.Child as Border;
|
||||
if (innerBorder != null)
|
||||
{
|
||||
innerBorder.HorizontalAlignment = isOn ? HorizontalAlignment.Right : HorizontalAlignment.Left;
|
||||
innerBorder.Background = new SolidColorBrush(Colors.White);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,6 +284,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
|
||||
string[] buttonNames = buttons[group];
|
||||
|
||||
bool isDarkTheme = ThemeHelper.IsDarkTheme;
|
||||
var selectedBrush = isDarkTheme ? new SolidColorBrush(Color.FromRgb(25, 25, 25)) : new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
var unselectedBrush = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
for (int i = 0; i < buttonNames.Length; i++)
|
||||
{
|
||||
var button = this.FindDescendantByName($"{group}{buttonNames[i]}Border") as Border;
|
||||
@@ -290,26 +295,81 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
{
|
||||
if (i == selectedIndex)
|
||||
{
|
||||
button.Background = new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
button.Background = selectedBrush;
|
||||
var textBlock = button.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = FontWeights.Bold;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
button.Background = new SolidColorBrush(Colors.Transparent);
|
||||
button.Background = unselectedBrush;
|
||||
var textBlock = button.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetCurrentSettingValue(string tag)
|
||||
{
|
||||
if (MainWindow.Settings?.PowerPointSettings == null) return false;
|
||||
|
||||
try
|
||||
{
|
||||
var pptSettings = MainWindow.Settings.PowerPointSettings;
|
||||
switch (tag)
|
||||
{
|
||||
case "SupportPowerPoint":
|
||||
return pptSettings.PowerPointSupport;
|
||||
case "PowerPointEnhancement":
|
||||
return pptSettings.EnablePowerPointEnhancement;
|
||||
case "SupportWPS":
|
||||
return pptSettings.IsSupportWPS;
|
||||
case "EnableWppProcessKill":
|
||||
return pptSettings.EnableWppProcessKill;
|
||||
case "ShowPPTButton":
|
||||
return pptSettings.ShowPPTButton;
|
||||
case "EnablePPTButtonPageClickable":
|
||||
return pptSettings.EnablePPTButtonPageClickable;
|
||||
case "EnablePPTButtonLongPressPageTurn":
|
||||
return pptSettings.EnablePPTButtonLongPressPageTurn;
|
||||
case "SkipAnimationsWhenGoNext":
|
||||
return pptSettings.SkipAnimationsWhenGoNext;
|
||||
case "ShowCanvasAtNewSlideShow":
|
||||
return pptSettings.IsShowCanvasAtNewSlideShow;
|
||||
case "EnableTwoFingerGestureInPresentationMode":
|
||||
return pptSettings.IsEnableTwoFingerGestureInPresentationMode;
|
||||
case "EnableFingerGestureSlideShowControl":
|
||||
return pptSettings.IsEnableFingerGestureSlideShowControl;
|
||||
case "ShowGestureButtonInSlideShow":
|
||||
return pptSettings.ShowGestureButtonInSlideShow;
|
||||
case "EnablePPTTimeCapsule":
|
||||
return pptSettings.EnablePPTTimeCapsule;
|
||||
case "NotifyPreviousPage":
|
||||
return pptSettings.IsNotifyPreviousPage;
|
||||
case "AlwaysGoToFirstPageOnReenter":
|
||||
return pptSettings.IsAlwaysGoToFirstPageOnReenter;
|
||||
case "NotifyHiddenPage":
|
||||
return pptSettings.IsNotifyHiddenPage;
|
||||
case "NotifyAutoPlayPresentation":
|
||||
return pptSettings.IsNotifyAutoPlayPresentation;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ToggleSwitch点击事件处理
|
||||
/// </summary>
|
||||
@@ -320,13 +380,13 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
var border = sender as Border;
|
||||
if (border == null) return;
|
||||
|
||||
bool isOn = border.Background.ToString() == "#FF3584E4";
|
||||
bool newState = !isOn;
|
||||
SetToggleSwitchState(border, newState);
|
||||
|
||||
string tag = border.Tag?.ToString();
|
||||
if (string.IsNullOrEmpty(tag)) return;
|
||||
|
||||
bool currentState = GetCurrentSettingValue(tag);
|
||||
bool newState = !currentState;
|
||||
SetToggleSwitchState(border, newState);
|
||||
|
||||
var pptSettings = MainWindow.Settings.PowerPointSettings;
|
||||
if (pptSettings == null) return;
|
||||
|
||||
@@ -443,7 +503,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
string group = parts[0];
|
||||
string value = parts[1];
|
||||
|
||||
// 清除同组其他按钮的选中状态
|
||||
bool isDarkTheme = ThemeHelper.IsDarkTheme;
|
||||
var selectedBrush = isDarkTheme ? new SolidColorBrush(Color.FromRgb(25, 25, 25)) : new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
var unselectedBrush = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
var parent = border.Parent as Panel;
|
||||
if (parent != null)
|
||||
{
|
||||
@@ -454,23 +517,24 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
string childTag = childBorder.Tag?.ToString();
|
||||
if (!string.IsNullOrEmpty(childTag) && childTag.StartsWith(group + "_"))
|
||||
{
|
||||
childBorder.Background = new SolidColorBrush(Colors.Transparent);
|
||||
childBorder.Background = unselectedBrush;
|
||||
var textBlock = childBorder.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置当前按钮为选中状态
|
||||
border.Background = new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
border.Background = selectedBrush;
|
||||
var currentTextBlock = border.Child as TextBlock;
|
||||
if (currentTextBlock != null)
|
||||
{
|
||||
currentTextBlock.FontWeight = FontWeights.Bold;
|
||||
currentTextBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
|
||||
var pptSettings = MainWindow.Settings.PowerPointSettings;
|
||||
@@ -638,6 +702,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
try
|
||||
{
|
||||
ThemeHelper.ApplyThemeToControl(this);
|
||||
if (_isLoaded)
|
||||
{
|
||||
LoadSettings();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -182,11 +182,12 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
if (toggleSwitch == null) return;
|
||||
toggleSwitch.Background = isOn
|
||||
? new SolidColorBrush(Color.FromRgb(53, 132, 228))
|
||||
: new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
: (ThemeHelper.IsDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(225, 225, 225)));
|
||||
var innerBorder = toggleSwitch.Child as Border;
|
||||
if (innerBorder != null)
|
||||
{
|
||||
innerBorder.HorizontalAlignment = isOn ? HorizontalAlignment.Right : HorizontalAlignment.Left;
|
||||
innerBorder.Background = new SolidColorBrush(Colors.White);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,27 +247,27 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置当前按钮为选中状态
|
||||
border.Background = new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
bool isDarkTheme = ThemeHelper.IsDarkTheme;
|
||||
var selectedBrush = isDarkTheme ? new SolidColorBrush(Color.FromRgb(25, 25, 25)) : new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
border.Background = selectedBrush;
|
||||
var currentTextBlock = border.Child as TextBlock;
|
||||
if (currentTextBlock != null)
|
||||
{
|
||||
currentTextBlock.FontWeight = FontWeights.Bold;
|
||||
currentTextBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
|
||||
HandleOptionChange(group, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理选项变化 - 子类需要实现
|
||||
/// </summary>
|
||||
protected abstract void HandleOptionChange(string group, string value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,11 +160,45 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
if (toggleSwitch == null) return;
|
||||
toggleSwitch.Background = isOn
|
||||
? new SolidColorBrush(Color.FromRgb(53, 132, 228))
|
||||
: new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
: (ThemeHelper.IsDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(225, 225, 225)));
|
||||
var innerBorder = toggleSwitch.Child as Border;
|
||||
if (innerBorder != null)
|
||||
{
|
||||
innerBorder.HorizontalAlignment = isOn ? System.Windows.HorizontalAlignment.Right : System.Windows.HorizontalAlignment.Left;
|
||||
innerBorder.Background = new SolidColorBrush(Colors.White);
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetCurrentSettingValue(string tag)
|
||||
{
|
||||
if (MainWindow.Settings == null) return false;
|
||||
|
||||
try
|
||||
{
|
||||
switch (tag)
|
||||
{
|
||||
case "AutoSaveStrokesAtClear":
|
||||
return MainWindow.Settings.Automation?.IsAutoSaveStrokesAtClear ?? false;
|
||||
|
||||
case "SaveScreenshotsInDateFolders":
|
||||
return MainWindow.Settings.Automation?.IsSaveScreenshotsInDateFolders ?? false;
|
||||
|
||||
case "AutoSaveStrokesAtScreenshot":
|
||||
return MainWindow.Settings.Automation?.IsAutoSaveStrokesAtScreenshot ?? false;
|
||||
|
||||
case "AutoSaveScreenShotInPowerPoint":
|
||||
return MainWindow.Settings.PowerPointSettings?.IsAutoSaveScreenShotInPowerPoint ?? false;
|
||||
|
||||
case "AutoDelSavedFiles":
|
||||
return MainWindow.Settings.Automation?.AutoDelSavedFiles ?? false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,13 +212,14 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
var border = sender as Border;
|
||||
if (border == null) return;
|
||||
|
||||
bool isOn = border.Background.ToString() == "#FF3584E4";
|
||||
bool newState = !isOn;
|
||||
SetToggleSwitchState(border, newState);
|
||||
|
||||
string tag = border.Tag?.ToString();
|
||||
if (string.IsNullOrEmpty(tag)) return;
|
||||
|
||||
bool currentState = GetCurrentSettingValue(tag);
|
||||
bool newState = !currentState;
|
||||
|
||||
SetToggleSwitchState(border, newState);
|
||||
|
||||
switch (tag)
|
||||
{
|
||||
case "AutoSaveStrokesAtClear":
|
||||
@@ -365,6 +400,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
try
|
||||
{
|
||||
ThemeHelper.ApplyThemeToControl(this);
|
||||
if (_isLoaded)
|
||||
{
|
||||
LoadSettings();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -88,165 +88,6 @@
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 更新设置 -->
|
||||
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Grid Height="54">
|
||||
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动检查更新" HorizontalAlignment="Left"/>
|
||||
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="定期检查是否有新版本可用" HorizontalAlignment="Left"/>
|
||||
</StackPanel>
|
||||
<Border x:Name="ToggleSwitchIsAutoUpdate" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="IsAutoUpdate" MouseLeftButtonDown="ToggleSwitch_Click">
|
||||
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
|
||||
</Border.Effect>
|
||||
</Border>
|
||||
</Border>
|
||||
</Grid>
|
||||
<Border Height="1" Background="#ebebeb"/>
|
||||
<Grid Height="54">
|
||||
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="静默更新" HorizontalAlignment="Left"/>
|
||||
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="在软件不使用时自动安装更新,无需手动操作" HorizontalAlignment="Left"/>
|
||||
</StackPanel>
|
||||
<Border x:Name="ToggleSwitchIsAutoUpdateWithSilence" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="IsAutoUpdateWithSilence" MouseLeftButtonDown="ToggleSwitch_Click">
|
||||
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
|
||||
</Border.Effect>
|
||||
</Border>
|
||||
</Border>
|
||||
</Grid>
|
||||
<Border Height="1" Background="#ebebeb"/>
|
||||
<Grid Height="54">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left" Grid.Column="0" MaxWidth="350">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="更新通道" HorizontalAlignment="Left"/>
|
||||
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="选择更新通道:稳定版提供可靠更新,测试版提供新功能抢先体验" HorizontalAlignment="Left" TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0" Grid.Column="1">
|
||||
<Border x:Name="UpdateChannelReleaseBorder" Padding="13,7" CornerRadius="8" Background="#e1e1e1" Cursor="Hand" Tag="UpdateChannel_Release" Margin="0,0,8,8" MouseLeftButtonDown="OptionButton_Click">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14" FontWeight="Bold" Text="稳定版"/>
|
||||
</Border>
|
||||
<Border x:Name="UpdateChannelPreviewBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="UpdateChannel_Preview" Margin="0,0,8,8" MouseLeftButtonDown="OptionButton_Click">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14" Text="预览版"/>
|
||||
</Border>
|
||||
<Border x:Name="UpdateChannelBetaBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="UpdateChannel_Beta" Margin="0,0,8,8" MouseLeftButtonDown="OptionButton_Click">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14" Text="测试版"/>
|
||||
</Border>
|
||||
</WrapPanel>
|
||||
</Grid>
|
||||
<Border Height="1" Background="#ebebeb"/>
|
||||
<Grid Height="54">
|
||||
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="手动更新" HorizontalAlignment="Left"/>
|
||||
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="点击后立即检查并下载最新版本" HorizontalAlignment="Left"/>
|
||||
</StackPanel>
|
||||
<Button x:Name="ManualUpdateButton" Content="手动更新" Width="120" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0" Padding="12,6" Click="ManualUpdateButton_Click"/>
|
||||
</Grid>
|
||||
<Border Height="1" Background="#ebebeb"/>
|
||||
<Grid Height="54">
|
||||
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="版本修复" HorizontalAlignment="Left"/>
|
||||
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="根据当前选择的通道下载最新版本并执行安装,可用于修复损坏的安装" HorizontalAlignment="Left"/>
|
||||
</StackPanel>
|
||||
<Button x:Name="FixVersionButton" Content="版本修复" Width="120" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0" Padding="12,6" Click="FixVersionButton_Click"/>
|
||||
</Grid>
|
||||
<Border Height="1" Background="#ebebeb"/>
|
||||
<Grid Height="54">
|
||||
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="历史版本回滚" HorizontalAlignment="Left"/>
|
||||
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="回滚到之前的版本" HorizontalAlignment="Left"/>
|
||||
</StackPanel>
|
||||
<Button x:Name="HistoryRollbackButton" Content="历史版本回滚" Width="120" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0" Padding="12,6" Click="HistoryRollbackButton_Click"/>
|
||||
</Grid>
|
||||
<Border Height="1" Background="#ebebeb"/>
|
||||
<Grid Margin="18,12,15,12" x:Name="AutoUpdateTimePeriodBlock">
|
||||
<StackPanel>
|
||||
<TextBlock Text="静默更新时间段" FontSize="15" FontWeight="Bold" Foreground="#2e3436" Margin="0,0,0,12"/>
|
||||
<StackPanel>
|
||||
<StackPanel Orientation="Vertical" Margin="0,0,0,12">
|
||||
<TextBlock Text="起始时间" FontSize="14" Foreground="#2e3436" Margin="0,0,0,8"/>
|
||||
<ComboBox x:Name="AutoUpdateWithSilenceStartTimeComboBox"
|
||||
Style="{StaticResource ComboBoxStyle}"
|
||||
ItemContainerStyle="{StaticResource ComboBoxItemStyle}"
|
||||
HorizontalAlignment="Left"
|
||||
Width="150"
|
||||
SelectedIndex="6"
|
||||
SelectionChanged="AutoUpdateWithSilenceStartTimeComboBox_SelectionChanged">
|
||||
<ComboBoxItem Content="00:00" Tag="00"/>
|
||||
<ComboBoxItem Content="01:00" Tag="01"/>
|
||||
<ComboBoxItem Content="02:00" Tag="02"/>
|
||||
<ComboBoxItem Content="03:00" Tag="03"/>
|
||||
<ComboBoxItem Content="04:00" Tag="04"/>
|
||||
<ComboBoxItem Content="05:00" Tag="05"/>
|
||||
<ComboBoxItem Content="06:00" Tag="06" IsSelected="True"/>
|
||||
<ComboBoxItem Content="07:00" Tag="07"/>
|
||||
<ComboBoxItem Content="08:00" Tag="08"/>
|
||||
<ComboBoxItem Content="09:00" Tag="09"/>
|
||||
<ComboBoxItem Content="10:00" Tag="10"/>
|
||||
<ComboBoxItem Content="11:00" Tag="11"/>
|
||||
<ComboBoxItem Content="12:00" Tag="12"/>
|
||||
<ComboBoxItem Content="13:00" Tag="13"/>
|
||||
<ComboBoxItem Content="14:00" Tag="14"/>
|
||||
<ComboBoxItem Content="15:00" Tag="15"/>
|
||||
<ComboBoxItem Content="16:00" Tag="16"/>
|
||||
<ComboBoxItem Content="17:00" Tag="17"/>
|
||||
<ComboBoxItem Content="18:00" Tag="18"/>
|
||||
<ComboBoxItem Content="19:00" Tag="19"/>
|
||||
<ComboBoxItem Content="20:00" Tag="20"/>
|
||||
<ComboBoxItem Content="21:00" Tag="21"/>
|
||||
<ComboBoxItem Content="22:00" Tag="22"/>
|
||||
<ComboBoxItem Content="23:00" Tag="23"/>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical" Margin="0,0,0,0">
|
||||
<TextBlock Text="终止时间" FontSize="14" Foreground="#2e3436" Margin="0,0,0,8"/>
|
||||
<ComboBox x:Name="AutoUpdateWithSilenceEndTimeComboBox"
|
||||
Style="{StaticResource ComboBoxStyle}"
|
||||
ItemContainerStyle="{StaticResource ComboBoxItemStyle}"
|
||||
HorizontalAlignment="Left"
|
||||
Width="150"
|
||||
SelectedIndex="22"
|
||||
SelectionChanged="AutoUpdateWithSilenceEndTimeComboBox_SelectionChanged">
|
||||
<ComboBoxItem Content="00:00" Tag="00"/>
|
||||
<ComboBoxItem Content="01:00" Tag="01"/>
|
||||
<ComboBoxItem Content="02:00" Tag="02"/>
|
||||
<ComboBoxItem Content="03:00" Tag="03"/>
|
||||
<ComboBoxItem Content="04:00" Tag="04"/>
|
||||
<ComboBoxItem Content="05:00" Tag="05"/>
|
||||
<ComboBoxItem Content="06:00" Tag="06"/>
|
||||
<ComboBoxItem Content="07:00" Tag="07"/>
|
||||
<ComboBoxItem Content="08:00" Tag="08"/>
|
||||
<ComboBoxItem Content="09:00" Tag="09"/>
|
||||
<ComboBoxItem Content="10:00" Tag="10"/>
|
||||
<ComboBoxItem Content="11:00" Tag="11"/>
|
||||
<ComboBoxItem Content="12:00" Tag="12"/>
|
||||
<ComboBoxItem Content="13:00" Tag="13"/>
|
||||
<ComboBoxItem Content="14:00" Tag="14"/>
|
||||
<ComboBoxItem Content="15:00" Tag="15"/>
|
||||
<ComboBoxItem Content="16:00" Tag="16"/>
|
||||
<ComboBoxItem Content="17:00" Tag="17"/>
|
||||
<ComboBoxItem Content="18:00" Tag="18"/>
|
||||
<ComboBoxItem Content="19:00" Tag="19"/>
|
||||
<ComboBoxItem Content="20:00" Tag="20"/>
|
||||
<ComboBoxItem Content="21:00" Tag="21"/>
|
||||
<ComboBoxItem Content="22:00" Tag="22" IsSelected="True"/>
|
||||
<ComboBoxItem Content="23:00" Tag="23"/>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<TextBlock Margin="0,8,0,0" Text="若终止时间小于起始时间,即将终止时间视为第二天的时间。若起始时间与终止时间相同,即视为全天候时间。"
|
||||
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 启动设置 -->
|
||||
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
|
||||
<StackPanel Orientation="Vertical">
|
||||
|
||||
@@ -86,54 +86,6 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
|
||||
try
|
||||
{
|
||||
// 自动更新设置
|
||||
var toggleSwitchIsAutoUpdate = FindToggleSwitch("ToggleSwitchIsAutoUpdate");
|
||||
if (toggleSwitchIsAutoUpdate != null)
|
||||
{
|
||||
bool isAutoUpdate = MainWindow.Settings.Startup.IsAutoUpdate;
|
||||
SetToggleSwitchState(toggleSwitchIsAutoUpdate, isAutoUpdate);
|
||||
}
|
||||
|
||||
// 静默更新设置
|
||||
var toggleSwitchIsAutoUpdateWithSilence = FindToggleSwitch("ToggleSwitchIsAutoUpdateWithSilence");
|
||||
if (toggleSwitchIsAutoUpdateWithSilence != null)
|
||||
{
|
||||
bool isAutoUpdateWithSilence = MainWindow.Settings.Startup.IsAutoUpdateWithSilence;
|
||||
SetToggleSwitchState(toggleSwitchIsAutoUpdateWithSilence, isAutoUpdateWithSilence);
|
||||
toggleSwitchIsAutoUpdateWithSilence.Visibility = MainWindow.Settings.Startup.IsAutoUpdate ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
// 静默更新时间段
|
||||
if (AutoUpdateTimePeriodBlock != null)
|
||||
{
|
||||
AutoUpdateTimePeriodBlock.Visibility =
|
||||
(MainWindow.Settings.Startup.IsAutoUpdateWithSilence && MainWindow.Settings.Startup.IsAutoUpdate) ?
|
||||
Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
// 设置时间选择器
|
||||
if (AutoUpdateWithSilenceStartTimeComboBox != null)
|
||||
{
|
||||
var startTime = MainWindow.Settings.Startup.AutoUpdateWithSilenceStartTime ?? "06:00";
|
||||
var startItem = AutoUpdateWithSilenceStartTimeComboBox.Items.Cast<ComboBoxItem>()
|
||||
.FirstOrDefault(item => item.Tag?.ToString() == startTime.Replace(":", ""));
|
||||
if (startItem != null)
|
||||
{
|
||||
AutoUpdateWithSilenceStartTimeComboBox.SelectedItem = startItem;
|
||||
}
|
||||
}
|
||||
|
||||
if (AutoUpdateWithSilenceEndTimeComboBox != null)
|
||||
{
|
||||
var endTime = MainWindow.Settings.Startup.AutoUpdateWithSilenceEndTime ?? "22:00";
|
||||
var endItem = AutoUpdateWithSilenceEndTimeComboBox.Items.Cast<ComboBoxItem>()
|
||||
.FirstOrDefault(item => item.Tag?.ToString() == endTime.Replace(":", ""));
|
||||
if (endItem != null)
|
||||
{
|
||||
AutoUpdateWithSilenceEndTimeComboBox.SelectedItem = endItem;
|
||||
}
|
||||
}
|
||||
|
||||
// 开机时运行
|
||||
var toggleSwitchRunAtStartup = FindToggleSwitch("ToggleSwitchRunAtStartup");
|
||||
if (toggleSwitchRunAtStartup != null)
|
||||
@@ -179,20 +131,6 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
SetToggleSwitchState(toggleSwitchUIAccessTopMost, MainWindow.Settings.Advanced.EnableUIAccessTopMost);
|
||||
}
|
||||
|
||||
// 更新通道
|
||||
if (MainWindow.Settings.Startup.UpdateChannel == UpdateChannel.Release)
|
||||
{
|
||||
UpdateUpdateChannelButtons(UpdateChannel.Release);
|
||||
}
|
||||
else if (MainWindow.Settings.Startup.UpdateChannel == UpdateChannel.Preview)
|
||||
{
|
||||
UpdateUpdateChannelButtons(UpdateChannel.Preview);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateUpdateChannelButtons(UpdateChannel.Beta);
|
||||
}
|
||||
|
||||
// 仅PPT模式
|
||||
var toggleSwitchMode = FindToggleSwitch("ToggleSwitchMode");
|
||||
if (toggleSwitchMode != null && MainWindow.Settings.ModeSettings != null)
|
||||
@@ -222,11 +160,48 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
private void SetToggleSwitchState(Border toggleSwitch, bool isOn)
|
||||
{
|
||||
if (toggleSwitch == null) return;
|
||||
toggleSwitch.Background = isOn ? new SolidColorBrush(Color.FromRgb(53, 132, 228)) : new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
toggleSwitch.Background = isOn
|
||||
? new SolidColorBrush(Color.FromRgb(53, 132, 228))
|
||||
: (ThemeHelper.IsDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(225, 225, 225)));
|
||||
var innerBorder = toggleSwitch.Child as Border;
|
||||
if (innerBorder != null)
|
||||
{
|
||||
innerBorder.HorizontalAlignment = isOn ? HorizontalAlignment.Right : HorizontalAlignment.Left;
|
||||
innerBorder.Background = new SolidColorBrush(Colors.White);
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetCurrentSettingValue(string tag)
|
||||
{
|
||||
if (MainWindow.Settings == null) return false;
|
||||
|
||||
try
|
||||
{
|
||||
switch (tag)
|
||||
{
|
||||
case "RunAtStartup":
|
||||
// 检查启动项是否存在
|
||||
return System.IO.File.Exists(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.Startup) + "\\Ink Canvas Annotation.lnk");
|
||||
case "FoldAtStartup":
|
||||
return MainWindow.Settings.Startup?.IsFoldAtStartup ?? false;
|
||||
case "NoFocusMode":
|
||||
return MainWindow.Settings.Advanced?.IsNoFocusMode ?? false;
|
||||
case "WindowMode":
|
||||
return MainWindow.Settings.Advanced?.WindowMode ?? false;
|
||||
case "AlwaysOnTop":
|
||||
return MainWindow.Settings.Advanced?.IsAlwaysOnTop ?? false;
|
||||
case "UIAccessTopMost":
|
||||
return MainWindow.Settings.Advanced?.EnableUIAccessTopMost ?? false;
|
||||
case "Mode":
|
||||
return MainWindow.Settings.ModeSettings?.IsPPTOnlyMode ?? false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,42 +215,15 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
var border = sender as Border;
|
||||
if (border == null) return;
|
||||
|
||||
bool isOn = border.Background.ToString() == "#FF3584E4";
|
||||
bool newState = !isOn;
|
||||
SetToggleSwitchState(border, newState);
|
||||
|
||||
string tag = border.Tag?.ToString();
|
||||
if (string.IsNullOrEmpty(tag)) return;
|
||||
|
||||
bool currentState = GetCurrentSettingValue(tag);
|
||||
bool newState = !currentState;
|
||||
SetToggleSwitchState(border, newState);
|
||||
|
||||
switch (tag)
|
||||
{
|
||||
case "IsAutoUpdate":
|
||||
// 直接调用 MainWindow 中的方法
|
||||
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsAutoUpdate", newState);
|
||||
// 更新UI状态
|
||||
var toggleSwitchIsAutoUpdateWithSilence = FindToggleSwitch("ToggleSwitchIsAutoUpdateWithSilence");
|
||||
if (toggleSwitchIsAutoUpdateWithSilence != null)
|
||||
{
|
||||
toggleSwitchIsAutoUpdateWithSilence.Visibility = newState ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
if (AutoUpdateTimePeriodBlock != null)
|
||||
{
|
||||
AutoUpdateTimePeriodBlock.Visibility =
|
||||
(MainWindow.Settings.Startup.IsAutoUpdateWithSilence && MainWindow.Settings.Startup.IsAutoUpdate) ?
|
||||
Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
break;
|
||||
|
||||
case "IsAutoUpdateWithSilence":
|
||||
// 直接调用 MainWindow 中的方法
|
||||
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsAutoUpdateWithSilence", newState);
|
||||
// 更新UI状态
|
||||
if (AutoUpdateTimePeriodBlock != null)
|
||||
{
|
||||
AutoUpdateTimePeriodBlock.Visibility = newState ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
break;
|
||||
|
||||
case "RunAtStartup":
|
||||
// 直接调用 MainWindow 中的方法
|
||||
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchRunAtStartup", newState);
|
||||
@@ -335,156 +283,6 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 选项按钮点击事件处理
|
||||
/// </summary>
|
||||
private void OptionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!_isLoaded) return;
|
||||
|
||||
var border = sender as Border;
|
||||
if (border == null) return;
|
||||
|
||||
string tag = border.Tag?.ToString();
|
||||
if (string.IsNullOrEmpty(tag)) return;
|
||||
|
||||
switch (tag)
|
||||
{
|
||||
case "UpdateChannel_Release":
|
||||
// 选择稳定版
|
||||
MainWindowSettingsHelper.UpdateSettingDirectly(() =>
|
||||
{
|
||||
MainWindow.Settings.Startup.UpdateChannel = UpdateChannel.Release;
|
||||
}, "UpdateChannelSelector");
|
||||
// 调用 MainWindow 中的方法
|
||||
MainWindowSettingsHelper.InvokeMainWindowMethod("UpdateChannelSelector_Checked",
|
||||
new System.Windows.Controls.RadioButton { Tag = "Release" }, e);
|
||||
// 更新UI状态
|
||||
UpdateUpdateChannelButtons(UpdateChannel.Release);
|
||||
break;
|
||||
|
||||
case "UpdateChannel_Preview":
|
||||
// 选择预览版
|
||||
MainWindowSettingsHelper.UpdateSettingDirectly(() =>
|
||||
{
|
||||
MainWindow.Settings.Startup.UpdateChannel = UpdateChannel.Preview;
|
||||
}, "UpdateChannelSelector");
|
||||
// 调用 MainWindow 中的方法
|
||||
MainWindowSettingsHelper.InvokeMainWindowMethod("UpdateChannelSelector_Checked",
|
||||
new System.Windows.Controls.RadioButton { Tag = "Preview" }, e);
|
||||
// 更新UI状态
|
||||
UpdateUpdateChannelButtons(UpdateChannel.Preview);
|
||||
break;
|
||||
|
||||
case "UpdateChannel_Beta":
|
||||
// 选择测试版
|
||||
MainWindowSettingsHelper.UpdateSettingDirectly(() =>
|
||||
{
|
||||
MainWindow.Settings.Startup.UpdateChannel = UpdateChannel.Beta;
|
||||
}, "UpdateChannelSelector");
|
||||
// 调用 MainWindow 中的方法
|
||||
MainWindowSettingsHelper.InvokeMainWindowMethod("UpdateChannelSelector_Checked",
|
||||
new System.Windows.Controls.RadioButton { Tag = "Beta" }, e);
|
||||
// 更新UI状态
|
||||
UpdateUpdateChannelButtons(UpdateChannel.Beta);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新更新通道按钮状态
|
||||
/// </summary>
|
||||
private void UpdateUpdateChannelButtons(UpdateChannel selectedChannel)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool isDarkTheme = ThemeHelper.IsDarkTheme;
|
||||
var selectedBrush = isDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
var unselectedBrush = isDarkTheme ? new SolidColorBrush(Color.FromRgb(35, 35, 35)) : new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
if (UpdateChannelReleaseBorder != null)
|
||||
{
|
||||
bool isSelected = selectedChannel == UpdateChannel.Release;
|
||||
UpdateChannelReleaseBorder.Background = isSelected ? selectedBrush : unselectedBrush;
|
||||
var textBlock = UpdateChannelReleaseBorder.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = isSelected ? FontWeights.Bold : FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
|
||||
if (UpdateChannelPreviewBorder != null)
|
||||
{
|
||||
bool isSelected = selectedChannel == UpdateChannel.Preview;
|
||||
UpdateChannelPreviewBorder.Background = isSelected ? selectedBrush : unselectedBrush;
|
||||
var textBlock = UpdateChannelPreviewBorder.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = isSelected ? FontWeights.Bold : FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
|
||||
if (UpdateChannelBetaBorder != null)
|
||||
{
|
||||
bool isSelected = selectedChannel == UpdateChannel.Beta;
|
||||
UpdateChannelBetaBorder.Background = isSelected ? selectedBrush : unselectedBrush;
|
||||
var textBlock = UpdateChannelBetaBorder.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = isSelected ? FontWeights.Bold : FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"更新更新通道按钮状态时出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 手动更新按钮点击事件
|
||||
/// </summary>
|
||||
private async void ManualUpdateButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
MainWindowSettingsHelper.InvokeMainWindowMethod("ManualUpdateButton_Click", sender, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 版本修复按钮点击事件
|
||||
/// </summary>
|
||||
private async void FixVersionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
MainWindowSettingsHelper.InvokeMainWindowMethod("FixVersionButton_Click", sender, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 历史版本回滚按钮点击事件
|
||||
/// </summary>
|
||||
private void HistoryRollbackButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// 查找 MainWindow 中的历史版本回滚方法
|
||||
MainWindowSettingsHelper.InvokeMainWindowMethod("HistoryRollbackButton_Click", sender, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ComboBox选择变化事件处理
|
||||
/// </summary>
|
||||
private void AutoUpdateWithSilenceStartTimeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (!_isLoaded) return;
|
||||
// 直接调用 MainWindow 中的方法
|
||||
MainWindowSettingsHelper.InvokeComboBoxSelectionChanged("AutoUpdateWithSilenceStartTimeComboBox", AutoUpdateWithSilenceStartTimeComboBox?.SelectedItem);
|
||||
}
|
||||
|
||||
private void AutoUpdateWithSilenceEndTimeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (!_isLoaded) return;
|
||||
// 直接调用 MainWindow 中的方法
|
||||
MainWindowSettingsHelper.InvokeComboBoxSelectionChanged("AutoUpdateWithSilenceEndTimeComboBox", AutoUpdateWithSilenceEndTimeComboBox?.SelectedItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用主题
|
||||
@@ -493,62 +291,11 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
{
|
||||
try
|
||||
{
|
||||
bool isDarkTheme = ThemeHelper.IsDarkTheme;
|
||||
|
||||
// 更新更新通道按钮
|
||||
if (UpdateChannelReleaseBorder != null)
|
||||
{
|
||||
UpdateChannelReleaseBorder.Background = isDarkTheme
|
||||
? ThemeHelper.GetButtonBackgroundBrush()
|
||||
: new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
var textBlock = UpdateChannelReleaseBorder.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
if (UpdateChannelPreviewBorder != null)
|
||||
{
|
||||
UpdateChannelPreviewBorder.Background = isDarkTheme
|
||||
? ThemeHelper.GetButtonBackgroundBrush()
|
||||
: new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
var textBlock = UpdateChannelPreviewBorder.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
if (UpdateChannelBetaBorder != null)
|
||||
{
|
||||
UpdateChannelBetaBorder.Background = isDarkTheme
|
||||
? ThemeHelper.GetButtonBackgroundBrush()
|
||||
: new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
var textBlock = UpdateChannelBetaBorder.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
|
||||
// 更新按钮
|
||||
if (ManualUpdateButton != null)
|
||||
{
|
||||
ManualUpdateButton.Background = ThemeHelper.GetButtonBackgroundBrush();
|
||||
ManualUpdateButton.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
if (FixVersionButton != null)
|
||||
{
|
||||
FixVersionButton.Background = ThemeHelper.GetButtonBackgroundBrush();
|
||||
FixVersionButton.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
if (HistoryRollbackButton != null)
|
||||
{
|
||||
HistoryRollbackButton.Background = ThemeHelper.GetButtonBackgroundBrush();
|
||||
HistoryRollbackButton.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
|
||||
// 使用 ThemeHelper 递归更新其他元素
|
||||
ThemeHelper.ApplyThemeToControl(this);
|
||||
if (_isLoaded)
|
||||
{
|
||||
LoadSettings();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -8,6 +8,35 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
/// </summary>
|
||||
public static class ThemeHelper
|
||||
{
|
||||
private static bool IsToggleSwitchThumb(System.Windows.Controls.Border border)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (border == null) return false;
|
||||
|
||||
// ToggleSwitch thumb 常见尺寸:19x19,圆角 10,父级为 48x25 的开关背景
|
||||
if (border.Width < 16 || border.Width > 24 || border.Height < 16 || border.Height > 24)
|
||||
return false;
|
||||
|
||||
if (border.CornerRadius.TopLeft < 8) return false;
|
||||
|
||||
if (border.Parent is System.Windows.Controls.Border parent)
|
||||
{
|
||||
if (parent.Width >= 40 && parent.Width <= 60 &&
|
||||
parent.Height >= 20 && parent.Height <= 35 &&
|
||||
parent.CornerRadius.TopLeft >= 10)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 检查当前是否为深色主题
|
||||
/// </summary>
|
||||
@@ -180,8 +209,12 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
}
|
||||
else
|
||||
{
|
||||
// 其他白色背景(如搜索框)
|
||||
border.Background = GetTextBoxBackgroundBrush();
|
||||
// ToggleSwitch thumb 应保持白色,不参与主题背景替换
|
||||
if (!IsToggleSwitchThumb(border))
|
||||
{
|
||||
// 其他白色背景(如搜索框)
|
||||
border.Background = GetTextBoxBackgroundBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (color.R == 250 && color.G == 250 && color.B == 250) // #fafafa - 主背景
|
||||
|
||||
@@ -230,11 +230,50 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
if (toggleSwitch == null) return;
|
||||
toggleSwitch.Background = isOn
|
||||
? new SolidColorBrush(Color.FromRgb(53, 132, 228))
|
||||
: new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
: (ThemeHelper.IsDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(225, 225, 225)));
|
||||
var innerBorder = toggleSwitch.Child as Border;
|
||||
if (innerBorder != null)
|
||||
{
|
||||
innerBorder.HorizontalAlignment = isOn ? HorizontalAlignment.Right : HorizontalAlignment.Left;
|
||||
innerBorder.Background = new SolidColorBrush(Colors.White);
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetCurrentSettingValue(string tag)
|
||||
{
|
||||
if (MainWindow.Settings == null) return false;
|
||||
|
||||
try
|
||||
{
|
||||
switch (tag)
|
||||
{
|
||||
case "EnableSplashScreen":
|
||||
return MainWindow.Settings.Appearance?.EnableSplashScreen ?? false;
|
||||
case "EnableDisPlayNibModeToggle":
|
||||
return MainWindow.Settings.Appearance?.IsEnableDisPlayNibModeToggler ?? false;
|
||||
case "EnableTrayIcon":
|
||||
return MainWindow.Settings.Appearance?.EnableTrayIcon ?? false;
|
||||
case "EnableViewboxBlackBoardScaleTransform":
|
||||
return MainWindow.Settings.Appearance?.EnableViewboxBlackBoardScaleTransform ?? false;
|
||||
case "EnableTimeDisplayInWhiteboardMode":
|
||||
return MainWindow.Settings.Appearance?.EnableTimeDisplayInWhiteboardMode ?? false;
|
||||
case "EnableChickenSoupInWhiteboardMode":
|
||||
return MainWindow.Settings.Appearance?.EnableChickenSoupInWhiteboardMode ?? false;
|
||||
case "EnableQuickPanel":
|
||||
return MainWindow.Settings.Appearance?.IsShowQuickPanel ?? false;
|
||||
case "AutoEnterAnnotationModeWhenExitFoldMode":
|
||||
return MainWindow.Settings.Automation?.IsAutoEnterAnnotationModeWhenExitFoldMode ?? false;
|
||||
case "AutoFoldAfterPPTSlideShow":
|
||||
return MainWindow.Settings.Automation?.IsAutoFoldAfterPPTSlideShow ?? false;
|
||||
case "AutoFoldWhenExitWhiteboard":
|
||||
return MainWindow.Settings.Automation?.IsAutoFoldWhenExitWhiteboard ?? false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,13 +287,13 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
var border = sender as Border;
|
||||
if (border == null) return;
|
||||
|
||||
bool isOn = border.Background.ToString() == "#FF3584E4";
|
||||
bool newState = !isOn;
|
||||
SetToggleSwitchState(border, newState);
|
||||
|
||||
string tag = border.Tag?.ToString();
|
||||
if (string.IsNullOrEmpty(tag)) return;
|
||||
|
||||
bool currentState = GetCurrentSettingValue(tag);
|
||||
bool newState = !currentState;
|
||||
SetToggleSwitchState(border, newState);
|
||||
|
||||
var appearance = MainWindow.Settings.Appearance;
|
||||
if (appearance == null) return;
|
||||
|
||||
@@ -453,9 +492,6 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置选项按钮状态
|
||||
/// </summary>
|
||||
private void SetOptionButtonState(string group, int selectedIndex)
|
||||
{
|
||||
var buttons = new Dictionary<string, string[]>
|
||||
@@ -470,6 +506,9 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
if (!buttons.ContainsKey(group)) return;
|
||||
|
||||
string[] buttonNames = buttons[group];
|
||||
bool isDarkTheme = ThemeHelper.IsDarkTheme;
|
||||
var selectedBrush = isDarkTheme ? new SolidColorBrush(Color.FromRgb(25, 25, 25)) : new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
var unselectedBrush = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
for (int i = 0; i < buttonNames.Length; i++)
|
||||
{
|
||||
@@ -478,20 +517,22 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
{
|
||||
if (i == selectedIndex)
|
||||
{
|
||||
button.Background = new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
button.Background = selectedBrush;
|
||||
var textBlock = button.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = FontWeights.Bold;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
button.Background = new SolidColorBrush(Colors.Transparent);
|
||||
button.Background = unselectedBrush;
|
||||
var textBlock = button.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -517,7 +558,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
string group = parts[0];
|
||||
string value = parts[1];
|
||||
|
||||
// 清除同组其他按钮的选中状态
|
||||
bool isDarkTheme = ThemeHelper.IsDarkTheme;
|
||||
var selectedBrush = isDarkTheme ? new SolidColorBrush(Color.FromRgb(25, 25, 25)) : new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
var unselectedBrush = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
var parent = border.Parent as Panel;
|
||||
if (parent != null)
|
||||
{
|
||||
@@ -528,23 +572,24 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
string childTag = childBorder.Tag?.ToString();
|
||||
if (!string.IsNullOrEmpty(childTag) && childTag.StartsWith(group + "_"))
|
||||
{
|
||||
childBorder.Background = new SolidColorBrush(Colors.Transparent);
|
||||
childBorder.Background = unselectedBrush;
|
||||
var textBlock = childBorder.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置当前按钮为选中状态
|
||||
border.Background = new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
border.Background = selectedBrush;
|
||||
var currentTextBlock = border.Child as TextBlock;
|
||||
if (currentTextBlock != null)
|
||||
{
|
||||
currentTextBlock.FontWeight = FontWeights.Bold;
|
||||
currentTextBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
|
||||
var appearance = MainWindow.Settings.Appearance;
|
||||
@@ -797,6 +842,10 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
try
|
||||
{
|
||||
ThemeHelper.ApplyThemeToControl(this);
|
||||
if (_isLoaded)
|
||||
{
|
||||
LoadSettings();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -5,124 +5,333 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Ink_Canvas.Windows.SettingsViews"
|
||||
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
|
||||
xmlns:mdxam="clr-namespace:MdXaml;assembly=MdXaml"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="950" d:DesignWidth="640">
|
||||
<UserControl.Resources>
|
||||
<Style x:Key="ToggleSwitchStyle" TargetType="Border">
|
||||
<Setter Property="Width" Value="48"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
<Setter Property="CornerRadius" Value="12"/>
|
||||
<Setter Property="Padding" Value="3,0"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
<Setter Property="Margin" Value="0,0,15,0"/>
|
||||
</Style>
|
||||
<Style x:Key="ScrollBarThumb" TargetType="{x:Type Thumb}">
|
||||
<Setter Property="OverridesDefaultStyle" Value="true"/>
|
||||
<Setter Property="IsTabStop" Value="false"/>
|
||||
<EventSetter Event="PreviewMouseDown" Handler="ScrollbarThumb_MouseDown"/>
|
||||
<EventSetter Event="PreviewMouseUp" Handler="ScrollbarThumb_MouseUp"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Thumb}">
|
||||
<Border Name="ScrollbarThumbEx"
|
||||
SnapsToDevicePixels="True"
|
||||
Background="#c3c3c3"
|
||||
Opacity="0.5"
|
||||
CornerRadius="1.5"
|
||||
Height="{TemplateBinding Height}"
|
||||
Width="3" HorizontalAlignment="Center"/>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type ScrollBar}">
|
||||
<EventSetter Event="Scroll" Handler="ScrollBar_Scroll"/>
|
||||
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false"/>
|
||||
<Setter Property="Stylus.IsFlicksEnabled" Value="false"/>
|
||||
<Setter Property="Width" Value="8"/>
|
||||
<Setter Property="Margin" Value="-6 3 0 0" />
|
||||
<Setter Property="MinWidth" Value="{Binding Height, RelativeSource={RelativeSource Self}}"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ScrollBar}">
|
||||
<Grid x:Name="ScrollbarGrid" SnapsToDevicePixels="true">
|
||||
<Border Width="3" CornerRadius="1.5" Background="#e0e0e0" Opacity="0" IsHitTestVisible="False" Margin="0 4 -2 4" x:Name="ScrollBarBorderTrackBackground"/>
|
||||
<Border Padding="0 4" Background="Transparent" MouseEnter="ScrollBarTrack_MouseEnter"
|
||||
MouseLeave="ScrollBarTrack_MouseLeave">
|
||||
<Track x:Name="PART_Track"
|
||||
IsDirectionReversed="true"
|
||||
IsEnabled="True"
|
||||
Width="6"
|
||||
Margin="0,0,0,0"
|
||||
HorizontalAlignment="Right">
|
||||
<Track.DecreaseRepeatButton>
|
||||
<RepeatButton Opacity="0" Command="{x:Static ScrollBar.PageUpCommand}" />
|
||||
</Track.DecreaseRepeatButton>
|
||||
<Track.IncreaseRepeatButton>
|
||||
<RepeatButton Opacity="0" Command="{x:Static ScrollBar.PageDownCommand}" />
|
||||
</Track.IncreaseRepeatButton>
|
||||
<Track.Thumb>
|
||||
<Thumb Style="{StaticResource ScrollBarThumb }" />
|
||||
</Track.Thumb>
|
||||
</Track>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ComboBoxStyles.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<Style x:Key="ToggleSwitchStyle" TargetType="Border">
|
||||
<Setter Property="Width" Value="48"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
<Setter Property="CornerRadius" Value="12"/>
|
||||
<Setter Property="Padding" Value="3,0"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
<Setter Property="Margin" Value="0,0,15,0"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<ScrollViewer ScrollChanged="ScrollViewerEx_ScrollChanged" IsManipulationEnabled="True" Name="UpdateCenterScrollViewerEx" IsDeferredScrollingEnabled="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled" IsTabStop="False" TabIndex="-1" Margin="0,0,2,2">
|
||||
<StackPanel Margin="60,24,60,24">
|
||||
<TextBlock Foreground="#2e3436" FontSize="28" FontWeight="Bold" Text="InkCanvasForClass 更新" Margin="0,0,0,24"/>
|
||||
<StackPanel Margin="60,12,60,24">
|
||||
<TextBlock Foreground="#2e3436" FontSize="28" FontWeight="Bold" Text="更新中心" Margin="0,0,0,24"/>
|
||||
|
||||
<Border BorderBrush="#e6e6e6" BorderThickness="1" CornerRadius="8" Padding="20" Background="#FAFAFA" Margin="0,0,0,20">
|
||||
<StackPanel>
|
||||
<TextBlock Name="UpdateStatusText" Foreground="#2e3436" FontSize="18" FontWeight="SemiBold" Text="正在检查更新..." TextWrapping="Wrap" Margin="0,0,0,8"/>
|
||||
<TextBlock Name="LastCheckTimeText" Foreground="#878787" FontSize="14" Text="上次检查时间: 从未" TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,24" VerticalAlignment="Center">
|
||||
<TextBlock Name="CurrentVersionText" Foreground="#878787" FontSize="14" Text="当前版本: 正在获取..." TextWrapping="Wrap" VerticalAlignment="Center"/>
|
||||
<ui:ProgressRing Name="LoadingSpinner" Width="16" Height="16" IsActive="False" Margin="12,0,0,0" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,24">
|
||||
<Button Name="CheckUpdateButton" Content="检查更新" HorizontalAlignment="Left" Padding="20,10" FontSize="14" Cursor="Hand" Click="CheckUpdateButton_Click" Background="#F3F3F3" Foreground="#2e3436" BorderThickness="1" BorderBrush="#E1E1E1" Margin="0,0,12,0">
|
||||
<Button.Template>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="4" Padding="{TemplateBinding Padding}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<Path Name="ChevronIcon" Data="M 0 0 L 4 4 L 0 8" Stroke="{TemplateBinding Foreground}" StrokeThickness="1.5" Margin="8,0,0,0" VerticalAlignment="Center" Visibility="Collapsed"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Button.Template>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Name="UpdateAvailablePanel" Visibility="Collapsed" Margin="0,0,0,24">
|
||||
<Border BorderBrush="#e6e6e6" BorderThickness="1" CornerRadius="8" Padding="16" Background="#F9F9F9">
|
||||
<StackPanel>
|
||||
<TextBlock Foreground="#2e3436" FontSize="16" FontWeight="SemiBold" Text="可用更新" Margin="0,0,0,8"/>
|
||||
<TextBlock Name="LatestVersionText" Foreground="#878787" FontSize="14" Text="" TextWrapping="Wrap" Margin="0,0,0,16"/>
|
||||
<Button Name="UpdateNowButton" Content="立即更新" HorizontalAlignment="Left" Padding="20,10" FontSize="14" Cursor="Hand" Click="UpdateNowButton_Click" Background="#0078D4" Foreground="White" BorderThickness="0">
|
||||
<Button.Template>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}" CornerRadius="4" Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Button.Template>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,20">
|
||||
<Border x:Name="TabUpdate" Padding="20,12" CornerRadius="8,8,0,0" Cursor="Hand" MouseLeftButtonDown="Tab_Click" Tag="Update" Background="#e1e1e1">
|
||||
<TextBlock Foreground="#2e3436" FontSize="16" FontWeight="Bold" Text="更新"/>
|
||||
</Border>
|
||||
<Border x:Name="TabRollback" Padding="20,12" CornerRadius="8,8,0,0" Cursor="Hand" MouseLeftButtonDown="Tab_Click" Tag="Rollback" Background="Transparent" Margin="2,0,0,0">
|
||||
<TextBlock Foreground="#2e3436" FontSize="16" Text="回滚"/>
|
||||
</Border>
|
||||
<Border x:Name="TabSettings" Padding="20,12" CornerRadius="8,8,0,0" Cursor="Hand" MouseLeftButtonDown="Tab_Click" Tag="Settings" Background="Transparent" Margin="2,0,0,0">
|
||||
<TextBlock Foreground="#2e3436" FontSize="16" Text="设置"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<Border BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="0,8,8,8" Background="#FAFAFA">
|
||||
<StackPanel>
|
||||
<StackPanel x:Name="UpdateTabContent" Margin="18,18,18,18">
|
||||
<Border BorderBrush="#e6e6e6" BorderThickness="1" CornerRadius="8" Padding="20" Background="#FFFFFF" Margin="0,0,0,20">
|
||||
<StackPanel>
|
||||
<TextBlock Name="UpdateStatusText" Foreground="#2e3436" FontSize="18" FontWeight="SemiBold" Text="正在检查更新..." TextWrapping="Wrap" Margin="0,0,0,8"/>
|
||||
<TextBlock Name="LastCheckTimeText" Foreground="#878787" FontSize="14" Text="上次检查时间: 从未" TextWrapping="Wrap" Margin="0,0,0,8"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
|
||||
<TextBlock Name="CurrentVersionText" Foreground="#878787" FontSize="14" Text="当前版本: 正在获取..." TextWrapping="Wrap" VerticalAlignment="Center"/>
|
||||
<ui:ProgressRing Name="LoadingSpinner" Width="16" Height="16" IsActive="False" Margin="12,0,0,0" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<Button Name="CheckUpdateButton" Content="检查更新" HorizontalAlignment="Left" Padding="20,10" FontSize="14" Cursor="Hand" Click="CheckUpdateButton_Click" Background="#F3F3F3" Foreground="#2e3436" BorderThickness="1" BorderBrush="#E1E1E1" Margin="0,8,0,0">
|
||||
<Button.Template>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="4" Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Button.Template>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<StackPanel Name="UpdateAvailablePanel" Visibility="Collapsed" Margin="0,0,0,20">
|
||||
<Border x:Name="UpdateAvailableBorder" BorderThickness="1" CornerRadius="8" Padding="16">
|
||||
<StackPanel>
|
||||
<TextBlock Foreground="#2e3436" FontSize="16" FontWeight="SemiBold" Text="可用更新" Margin="0,0,0,8"/>
|
||||
<TextBlock Name="LatestVersionText" Foreground="#878787" FontSize="14" Text="" TextWrapping="Wrap" Margin="0,0,0,16"/>
|
||||
<ui:SimpleStackPanel Orientation="Vertical" Spacing="12">
|
||||
<Button Name="UpdateNowButton" Content="立即下载并安装" HorizontalAlignment="Left" Padding="20,10" FontSize="14" FontWeight="SemiBold" Cursor="Hand" Click="UpdateNowButton_Click" Background="#2563eb" Foreground="White" BorderThickness="0" Height="40" MinWidth="200">
|
||||
<Button.Template>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}" CornerRadius="6" Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#1d4ed8"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background" Value="#1e40af"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Background" Value="#9ca3af"/>
|
||||
<Setter Property="Foreground" Value="#d1d5db"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Button.Template>
|
||||
</Button>
|
||||
<Button Name="UpdateLaterButton" Content="下载并在软件关闭时安装" HorizontalAlignment="Left" Padding="20,10" FontSize="14" FontWeight="Medium" Cursor="Hand" Click="UpdateLaterButton_Click" BorderThickness="1" Height="40" MinWidth="200">
|
||||
<Button.Template>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}" CornerRadius="6" Padding="{TemplateBinding Padding}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background">
|
||||
<Setter.Value>
|
||||
<SolidColorBrush Color="#2563eb" Opacity="0.1"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background">
|
||||
<Setter.Value>
|
||||
<SolidColorBrush Color="#2563eb" Opacity="0.2"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Button.Template>
|
||||
</Button>
|
||||
<Button Name="SkipVersionButton" Content="跳过该版本" HorizontalAlignment="Left" Padding="20,10" FontSize="14" FontWeight="Medium" Cursor="Hand" Click="SkipVersionButton_Click" Background="Transparent" BorderThickness="0" Height="40" MinWidth="200">
|
||||
<Button.Template>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}" CornerRadius="6" Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background">
|
||||
<Setter.Value>
|
||||
<SolidColorBrush Color="#2563eb" Opacity="0.1"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background">
|
||||
<Setter.Value>
|
||||
<SolidColorBrush Color="#2563eb" Opacity="0.2"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Button.Template>
|
||||
</Button>
|
||||
</ui:SimpleStackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<Border BorderBrush="#e6e6e6" BorderThickness="1" CornerRadius="8" Padding="16" Background="#FFFFFF" Margin="0,0,0,20">
|
||||
<StackPanel>
|
||||
<TextBlock Foreground="#2e3436" FontSize="16" FontWeight="SemiBold" Text="更新日志" Margin="0,0,0,12"/>
|
||||
<ScrollViewer MaxHeight="300" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<mdxam:MarkdownScrollViewer x:Name="UpdateLogViewer" Markdown="暂无更新日志" />
|
||||
</ScrollViewer>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Grid Height="54">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Orientation="Vertical" Margin="0,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left" Grid.Column="0" MaxWidth="350">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="更新通道" HorizontalAlignment="Left"/>
|
||||
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="选择更新通道:稳定版提供可靠更新,预览版提供新功能体验,测试版提供新功能抢先体验" HorizontalAlignment="Left" TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0" Grid.Column="1">
|
||||
<Border x:Name="UpdateChannelReleaseBorder" Padding="13,7" CornerRadius="8" Background="#e1e1e1" Cursor="Hand" Tag="UpdateChannel_Release" Margin="0,0,8,8" MouseLeftButtonDown="OptionButton_Click">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14" FontWeight="Bold" Text="稳定版"/>
|
||||
</Border>
|
||||
<Border x:Name="UpdateChannelPreviewBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="UpdateChannel_Preview" Margin="0,0,8,8" MouseLeftButtonDown="OptionButton_Click">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14" Text="预览版"/>
|
||||
</Border>
|
||||
<Border x:Name="UpdateChannelBetaBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="UpdateChannel_Beta" Margin="0,0,8,8" MouseLeftButtonDown="OptionButton_Click">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14" Text="测试版"/>
|
||||
</Border>
|
||||
</WrapPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel x:Name="RollbackTabContent" Visibility="Collapsed" Margin="18,18,18,18">
|
||||
<Border BorderBrush="#e6e6e6" BorderThickness="1" CornerRadius="8" Padding="16" Background="#FFFFFF" Margin="0,0,0,20">
|
||||
<StackPanel>
|
||||
<TextBlock Foreground="#2e3436" FontSize="16" FontWeight="SemiBold" Text="历史版本" Margin="0,0,0,12"/>
|
||||
<ScrollViewer MaxHeight="300" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<mdxam:MarkdownScrollViewer x:Name="HistoryLogViewer" Markdown="正在加载历史版本..." />
|
||||
</ScrollViewer>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Grid Height="54" Margin="0,0,0,20">
|
||||
<StackPanel Orientation="Vertical" Margin="0,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="选择回滚版本" HorizontalAlignment="Left"/>
|
||||
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="选择要回滚到的历史版本" HorizontalAlignment="Left"/>
|
||||
</StackPanel>
|
||||
<ComboBox x:Name="RollbackVersionComboBox"
|
||||
Style="{StaticResource ComboBoxStyle}"
|
||||
ItemContainerStyle="{StaticResource ComboBoxItemStyle}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Width="200"
|
||||
Margin="0,0,15,0"
|
||||
SelectionChanged="RollbackVersionComboBox_SelectionChanged"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Height="54" Margin="0,0,0,20">
|
||||
<StackPanel Orientation="Vertical" Margin="0,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="回滚到选中版本" HorizontalAlignment="Left"/>
|
||||
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="回滚到下拉框中选择的版本" HorizontalAlignment="Left"/>
|
||||
</StackPanel>
|
||||
<Button x:Name="RollbackButton" Content="回滚" Width="120" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0" Padding="12,6" Click="RollbackButton_Click"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Height="54">
|
||||
<StackPanel Orientation="Vertical" Margin="0,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="版本修复" HorizontalAlignment="Left"/>
|
||||
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="根据当前选择的通道下载最新版本并执行安装,可用于修复损坏的安装" HorizontalAlignment="Left"/>
|
||||
</StackPanel>
|
||||
<Button x:Name="FixVersionButton" Content="版本修复" Width="120" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0" Padding="12,6" Click="FixVersionButton_Click"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel x:Name="SettingsTabContent" Visibility="Collapsed" Margin="18,18,18,18">
|
||||
<Grid Height="54">
|
||||
<StackPanel Orientation="Vertical" Margin="0,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="自动检查更新" HorizontalAlignment="Left"/>
|
||||
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="定期检查是否有新版本可用" HorizontalAlignment="Left"/>
|
||||
</StackPanel>
|
||||
<Border x:Name="ToggleSwitchIsAutoUpdate" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="IsAutoUpdate" MouseLeftButtonDown="ToggleSwitch_Click">
|
||||
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
|
||||
</Border.Effect>
|
||||
</Border>
|
||||
</Border>
|
||||
</Grid>
|
||||
<Border Height="1" Background="#ebebeb" Margin="0,0,0,0"/>
|
||||
<Grid Height="54" x:Name="AutoUpdateWithSilencePanel">
|
||||
<StackPanel Orientation="Vertical" Margin="0,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="静默更新" HorizontalAlignment="Left"/>
|
||||
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="在软件不使用时自动安装更新,无需手动操作" HorizontalAlignment="Left"/>
|
||||
</StackPanel>
|
||||
<Border x:Name="ToggleSwitchIsAutoUpdateWithSilence" Style="{StaticResource ToggleSwitchStyle}" Background="#3584e4" Tag="IsAutoUpdateWithSilence" MouseLeftButtonDown="ToggleSwitch_Click">
|
||||
<Border Width="19" Height="19" Background="White" CornerRadius="10" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="4" Direction="-45" Color="Black" Opacity="0.3" ShadowDepth="0"/>
|
||||
</Border.Effect>
|
||||
</Border>
|
||||
</Border>
|
||||
</Grid>
|
||||
<Border Height="1" Background="#ebebeb" Margin="0,0,0,0" x:Name="AutoUpdateTimePeriodSeparator"/>
|
||||
<Grid Margin="0,12,0,12" x:Name="AutoUpdateTimePeriodBlock">
|
||||
<StackPanel>
|
||||
<TextBlock Text="静默更新时间段" FontSize="15" FontWeight="Bold" Foreground="#2e3436" Margin="0,0,0,12"/>
|
||||
<StackPanel>
|
||||
<StackPanel Orientation="Vertical" Margin="0,0,0,12">
|
||||
<TextBlock Text="起始时间" FontSize="14" Foreground="#2e3436" Margin="0,0,0,8"/>
|
||||
<ComboBox x:Name="AutoUpdateWithSilenceStartTimeComboBox"
|
||||
Style="{StaticResource ComboBoxStyle}"
|
||||
ItemContainerStyle="{StaticResource ComboBoxItemStyle}"
|
||||
HorizontalAlignment="Left"
|
||||
Width="150"
|
||||
SelectedIndex="6"
|
||||
SelectionChanged="AutoUpdateWithSilenceStartTimeComboBox_SelectionChanged">
|
||||
<ComboBoxItem Content="00:00" Tag="00"/>
|
||||
<ComboBoxItem Content="01:00" Tag="01"/>
|
||||
<ComboBoxItem Content="02:00" Tag="02"/>
|
||||
<ComboBoxItem Content="03:00" Tag="03"/>
|
||||
<ComboBoxItem Content="04:00" Tag="04"/>
|
||||
<ComboBoxItem Content="05:00" Tag="05"/>
|
||||
<ComboBoxItem Content="06:00" Tag="06" IsSelected="True"/>
|
||||
<ComboBoxItem Content="07:00" Tag="07"/>
|
||||
<ComboBoxItem Content="08:00" Tag="08"/>
|
||||
<ComboBoxItem Content="09:00" Tag="09"/>
|
||||
<ComboBoxItem Content="10:00" Tag="10"/>
|
||||
<ComboBoxItem Content="11:00" Tag="11"/>
|
||||
<ComboBoxItem Content="12:00" Tag="12"/>
|
||||
<ComboBoxItem Content="13:00" Tag="13"/>
|
||||
<ComboBoxItem Content="14:00" Tag="14"/>
|
||||
<ComboBoxItem Content="15:00" Tag="15"/>
|
||||
<ComboBoxItem Content="16:00" Tag="16"/>
|
||||
<ComboBoxItem Content="17:00" Tag="17"/>
|
||||
<ComboBoxItem Content="18:00" Tag="18"/>
|
||||
<ComboBoxItem Content="19:00" Tag="19"/>
|
||||
<ComboBoxItem Content="20:00" Tag="20"/>
|
||||
<ComboBoxItem Content="21:00" Tag="21"/>
|
||||
<ComboBoxItem Content="22:00" Tag="22"/>
|
||||
<ComboBoxItem Content="23:00" Tag="23"/>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical" Margin="0,0,0,0">
|
||||
<TextBlock Text="终止时间" FontSize="14" Foreground="#2e3436" Margin="0,0,0,8"/>
|
||||
<ComboBox x:Name="AutoUpdateWithSilenceEndTimeComboBox"
|
||||
Style="{StaticResource ComboBoxStyle}"
|
||||
ItemContainerStyle="{StaticResource ComboBoxItemStyle}"
|
||||
HorizontalAlignment="Left"
|
||||
Width="150"
|
||||
SelectedIndex="22"
|
||||
SelectionChanged="AutoUpdateWithSilenceEndTimeComboBox_SelectionChanged">
|
||||
<ComboBoxItem Content="00:00" Tag="00"/>
|
||||
<ComboBoxItem Content="01:00" Tag="01"/>
|
||||
<ComboBoxItem Content="02:00" Tag="02"/>
|
||||
<ComboBoxItem Content="03:00" Tag="03"/>
|
||||
<ComboBoxItem Content="04:00" Tag="04"/>
|
||||
<ComboBoxItem Content="05:00" Tag="05"/>
|
||||
<ComboBoxItem Content="06:00" Tag="06"/>
|
||||
<ComboBoxItem Content="07:00" Tag="07"/>
|
||||
<ComboBoxItem Content="08:00" Tag="08"/>
|
||||
<ComboBoxItem Content="09:00" Tag="09"/>
|
||||
<ComboBoxItem Content="10:00" Tag="10"/>
|
||||
<ComboBoxItem Content="11:00" Tag="11"/>
|
||||
<ComboBoxItem Content="12:00" Tag="12"/>
|
||||
<ComboBoxItem Content="13:00" Tag="13"/>
|
||||
<ComboBoxItem Content="14:00" Tag="14"/>
|
||||
<ComboBoxItem Content="15:00" Tag="15"/>
|
||||
<ComboBoxItem Content="16:00" Tag="16"/>
|
||||
<ComboBoxItem Content="17:00" Tag="17"/>
|
||||
<ComboBoxItem Content="18:00" Tag="18"/>
|
||||
<ComboBoxItem Content="19:00" Tag="19"/>
|
||||
<ComboBoxItem Content="20:00" Tag="20"/>
|
||||
<ComboBoxItem Content="21:00" Tag="21"/>
|
||||
<ComboBoxItem Content="22:00" Tag="22" IsSelected="True"/>
|
||||
<ComboBoxItem Content="23:00" Tag="23"/>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<TextBlock Margin="0,8,0,0" Text="若终止时间小于起始时间,即将终止时间视为第二天的时间。若起始时间与终止时间相同,即视为全天候时间。"
|
||||
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
using iNKORE.UI.WPF.Helpers;
|
||||
using iNKORE.UI.WPF.Modern.Controls;
|
||||
using Ink_Canvas.Helpers;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
|
||||
using static Ink_Canvas.Helpers.AutoUpdateHelper;
|
||||
|
||||
namespace Ink_Canvas.Windows.SettingsViews
|
||||
{
|
||||
public partial class UpdateCenterPanel : UserControl
|
||||
{
|
||||
private bool isLoaded = false;
|
||||
private bool _isLoaded = false;
|
||||
private string _currentTab = "Update";
|
||||
private List<(string version, string downloadUrl, string releaseNotes)> _historyVersions = new List<(string, string, string)>();
|
||||
private string _availableVersion = null;
|
||||
private UpdateLineGroup _availableLineGroup = null;
|
||||
|
||||
public UpdateCenterPanel()
|
||||
{
|
||||
@@ -24,10 +30,11 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
|
||||
private void UpdateCenterPanel_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded)
|
||||
if (!_isLoaded)
|
||||
{
|
||||
isLoaded = true;
|
||||
_isLoaded = true;
|
||||
LoadSettings();
|
||||
SwitchTab("Update");
|
||||
CheckUpdateStatus();
|
||||
ApplyTheme();
|
||||
}
|
||||
@@ -35,6 +42,8 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
|
||||
private void LoadSettings()
|
||||
{
|
||||
if (MainWindow.Settings == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
var version = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
@@ -42,6 +51,64 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
CurrentVersionText.Text = $"当前版本: v{version} | {platform}";
|
||||
|
||||
LoadLastCheckTime();
|
||||
|
||||
var toggleSwitchIsAutoUpdate = FindToggleSwitch("ToggleSwitchIsAutoUpdate");
|
||||
if (toggleSwitchIsAutoUpdate != null)
|
||||
{
|
||||
bool isAutoUpdate = MainWindow.Settings.Startup.IsAutoUpdate;
|
||||
SetToggleSwitchState(toggleSwitchIsAutoUpdate, isAutoUpdate);
|
||||
}
|
||||
|
||||
var toggleSwitchIsAutoUpdateWithSilence = FindToggleSwitch("ToggleSwitchIsAutoUpdateWithSilence");
|
||||
if (toggleSwitchIsAutoUpdateWithSilence != null)
|
||||
{
|
||||
bool isAutoUpdateWithSilence = MainWindow.Settings.Startup.IsAutoUpdateWithSilence;
|
||||
SetToggleSwitchState(toggleSwitchIsAutoUpdateWithSilence, isAutoUpdateWithSilence);
|
||||
AutoUpdateWithSilencePanel.Visibility = MainWindow.Settings.Startup.IsAutoUpdate ? Visibility.Visible : Visibility.Collapsed;
|
||||
AutoUpdateTimePeriodSeparator.Visibility = MainWindow.Settings.Startup.IsAutoUpdate ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
if (AutoUpdateTimePeriodBlock != null)
|
||||
{
|
||||
AutoUpdateTimePeriodBlock.Visibility =
|
||||
(MainWindow.Settings.Startup.IsAutoUpdateWithSilence && MainWindow.Settings.Startup.IsAutoUpdate) ?
|
||||
Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
if (AutoUpdateWithSilenceStartTimeComboBox != null)
|
||||
{
|
||||
var startTime = MainWindow.Settings.Startup.AutoUpdateWithSilenceStartTime ?? "06:00";
|
||||
var startItem = AutoUpdateWithSilenceStartTimeComboBox.Items.Cast<ComboBoxItem>()
|
||||
.FirstOrDefault(item => item.Tag?.ToString() == startTime.Replace(":", ""));
|
||||
if (startItem != null)
|
||||
{
|
||||
AutoUpdateWithSilenceStartTimeComboBox.SelectedItem = startItem;
|
||||
}
|
||||
}
|
||||
|
||||
if (AutoUpdateWithSilenceEndTimeComboBox != null)
|
||||
{
|
||||
var endTime = MainWindow.Settings.Startup.AutoUpdateWithSilenceEndTime ?? "22:00";
|
||||
var endItem = AutoUpdateWithSilenceEndTimeComboBox.Items.Cast<ComboBoxItem>()
|
||||
.FirstOrDefault(item => item.Tag?.ToString() == endTime.Replace(":", ""));
|
||||
if (endItem != null)
|
||||
{
|
||||
AutoUpdateWithSilenceEndTimeComboBox.SelectedItem = endItem;
|
||||
}
|
||||
}
|
||||
|
||||
if (MainWindow.Settings.Startup.UpdateChannel == UpdateChannel.Release)
|
||||
{
|
||||
UpdateUpdateChannelButtons(UpdateChannel.Release);
|
||||
}
|
||||
else if (MainWindow.Settings.Startup.UpdateChannel == UpdateChannel.Preview)
|
||||
{
|
||||
UpdateUpdateChannelButtons(UpdateChannel.Preview);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateUpdateChannelButtons(UpdateChannel.Beta);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -94,28 +161,226 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
}
|
||||
}
|
||||
|
||||
private void Tab_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var border = sender as Border;
|
||||
if (border == null) return;
|
||||
|
||||
string tag = border.Tag?.ToString();
|
||||
if (string.IsNullOrEmpty(tag)) return;
|
||||
|
||||
SwitchTab(tag);
|
||||
}
|
||||
|
||||
private void SwitchTab(string tabName)
|
||||
{
|
||||
_currentTab = tabName;
|
||||
bool isDarkTheme = ThemeHelper.IsDarkTheme;
|
||||
var selectedBrush = isDarkTheme ? new SolidColorBrush(Color.FromRgb(25, 25, 25)) : new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
var unselectedBrush = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
TabUpdate.Background = tabName == "Update" ? selectedBrush : unselectedBrush;
|
||||
var updateText = TabUpdate.Child as TextBlock;
|
||||
if (updateText != null)
|
||||
{
|
||||
updateText.FontWeight = tabName == "Update" ? FontWeights.Bold : FontWeights.Normal;
|
||||
updateText.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
|
||||
TabRollback.Background = tabName == "Rollback" ? selectedBrush : unselectedBrush;
|
||||
var rollbackText = TabRollback.Child as TextBlock;
|
||||
if (rollbackText != null)
|
||||
{
|
||||
rollbackText.FontWeight = tabName == "Rollback" ? FontWeights.Bold : FontWeights.Normal;
|
||||
rollbackText.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
|
||||
TabSettings.Background = tabName == "Settings" ? selectedBrush : unselectedBrush;
|
||||
var settingsText = TabSettings.Child as TextBlock;
|
||||
if (settingsText != null)
|
||||
{
|
||||
settingsText.FontWeight = tabName == "Settings" ? FontWeights.Bold : FontWeights.Normal;
|
||||
settingsText.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
|
||||
UpdateTabContent.Visibility = tabName == "Update" ? Visibility.Visible : Visibility.Collapsed;
|
||||
RollbackTabContent.Visibility = tabName == "Rollback" ? Visibility.Visible : Visibility.Collapsed;
|
||||
SettingsTabContent.Visibility = tabName == "Settings" ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
if (tabName == "Rollback" && _historyVersions.Count == 0)
|
||||
{
|
||||
LoadHistoryVersions();
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckUpdateButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CheckUpdateStatus(true);
|
||||
}
|
||||
|
||||
private void UpdateNowButton_Click(object sender, RoutedEventArgs e)
|
||||
private async void UpdateNowButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_availableVersion))
|
||||
{
|
||||
MessageBox.Show("没有可用的更新版本。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var mainWindow = Application.Current.MainWindow as MainWindow;
|
||||
if (mainWindow != null)
|
||||
UpdateNowButton.IsEnabled = false;
|
||||
UpdateLaterButton.IsEnabled = false;
|
||||
SkipVersionButton.IsEnabled = false;
|
||||
|
||||
var updateChannel = UpdateChannel.Release;
|
||||
if (MainWindow.Settings?.Startup != null)
|
||||
{
|
||||
var method = typeof(MainWindow).GetMethod("CheckForUpdates", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
if (method != null)
|
||||
updateChannel = MainWindow.Settings.Startup.UpdateChannel;
|
||||
}
|
||||
|
||||
var groups = _availableLineGroup != null ? new List<UpdateLineGroup> { _availableLineGroup } : AutoUpdateHelper.ChannelLineGroups[updateChannel];
|
||||
|
||||
bool downloadSuccess = await AutoUpdateHelper.DownloadSetupFileWithFallback(_availableVersion, groups, (percent, text) =>
|
||||
{
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
method.Invoke(mainWindow, null);
|
||||
}
|
||||
System.Diagnostics.Debug.WriteLine($"下载进度: {percent}% - {text}");
|
||||
}));
|
||||
});
|
||||
|
||||
if (downloadSuccess)
|
||||
{
|
||||
AutoUpdateHelper.InstallNewVersionApp(_availableVersion, true);
|
||||
App.IsAppExitByUser = true;
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
Application.Current.Shutdown();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("下载失败,请检查网络连接后重试。", "下载失败", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
UpdateNowButton.IsEnabled = true;
|
||||
UpdateLaterButton.IsEnabled = true;
|
||||
SkipVersionButton.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"立即更新失败: {ex.Message}");
|
||||
MessageBox.Show($"更新过程中发生错误:{ex.Message}", "更新错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
UpdateNowButton.IsEnabled = true;
|
||||
UpdateLaterButton.IsEnabled = true;
|
||||
SkipVersionButton.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async void UpdateLaterButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_availableVersion))
|
||||
{
|
||||
MessageBox.Show("没有可用的更新版本。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
UpdateNowButton.IsEnabled = false;
|
||||
UpdateLaterButton.IsEnabled = false;
|
||||
SkipVersionButton.IsEnabled = false;
|
||||
|
||||
var updateChannel = UpdateChannel.Release;
|
||||
if (MainWindow.Settings?.Startup != null)
|
||||
{
|
||||
updateChannel = MainWindow.Settings.Startup.UpdateChannel;
|
||||
}
|
||||
|
||||
var groups = _availableLineGroup != null ? new List<AutoUpdateHelper.UpdateLineGroup> { _availableLineGroup } : AutoUpdateHelper.ChannelLineGroups[updateChannel];
|
||||
|
||||
bool downloadSuccess = await AutoUpdateHelper.DownloadSetupFileWithFallback(_availableVersion, groups, (percent, text) =>
|
||||
{
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"下载进度: {percent}% - {text}");
|
||||
}));
|
||||
});
|
||||
|
||||
if (downloadSuccess)
|
||||
{
|
||||
MainWindow.Settings.Startup.IsAutoUpdate = true;
|
||||
MainWindow.Settings.Startup.IsAutoUpdateWithSilence = true;
|
||||
MainWindow.SaveSettingsToFile();
|
||||
|
||||
var mainWindow = Application.Current.MainWindow as MainWindow;
|
||||
if (mainWindow != null)
|
||||
{
|
||||
var field = typeof(MainWindow).GetField("AvailableLatestVersion", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
if (field != null)
|
||||
{
|
||||
field.SetValue(mainWindow, _availableVersion);
|
||||
}
|
||||
|
||||
var timerField = typeof(MainWindow).GetField("timerCheckAutoUpdateWithSilence", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
if (timerField != null)
|
||||
{
|
||||
var timer = timerField.GetValue(mainWindow);
|
||||
if (timer != null)
|
||||
{
|
||||
var startMethod = timer.GetType().GetMethod("Start");
|
||||
if (startMethod != null)
|
||||
{
|
||||
startMethod.Invoke(timer, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MessageBox.Show("更新已下载完成,将在软件关闭时自动安装。", "更新已准备就绪", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("下载失败,请检查网络连接后重试。", "下载失败", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
|
||||
UpdateNowButton.IsEnabled = true;
|
||||
UpdateLaterButton.IsEnabled = true;
|
||||
SkipVersionButton.IsEnabled = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"稍后更新失败: {ex.Message}");
|
||||
MessageBox.Show($"更新过程中发生错误:{ex.Message}", "更新错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
UpdateNowButton.IsEnabled = true;
|
||||
UpdateLaterButton.IsEnabled = true;
|
||||
SkipVersionButton.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void SkipVersionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_availableVersion))
|
||||
{
|
||||
MessageBox.Show("没有可用的更新版本。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
MainWindow.Settings.Startup.SkippedVersion = _availableVersion;
|
||||
MainWindow.SaveSettingsToFile();
|
||||
|
||||
UpdateAvailablePanel.Visibility = Visibility.Collapsed;
|
||||
UpdateStatusText.Text = "已跳过该版本";
|
||||
|
||||
MessageBox.Show($"已设置跳过版本 {_availableVersion},在下次发布新版本之前不会再提示更新。",
|
||||
"已跳过此版本",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Information);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"跳过版本失败: {ex.Message}");
|
||||
MessageBox.Show($"跳过版本时发生错误:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +403,19 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
|
||||
var (remoteVersion, lineGroup, releaseNotes) = await AutoUpdateHelper.CheckForUpdates(updateChannel, manualCheck, false);
|
||||
|
||||
if (manualCheck)
|
||||
{
|
||||
var mainWindow = Application.Current.MainWindow as MainWindow;
|
||||
if (mainWindow != null)
|
||||
{
|
||||
var field = typeof(MainWindow).GetField("AvailableLatestVersion", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
if (field != null)
|
||||
{
|
||||
field.SetValue(mainWindow, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
StopLoadingAnimation();
|
||||
@@ -156,17 +434,32 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
UpdateStatusText.Text = "有可用更新";
|
||||
LatestVersionText.Text = $"版本 {remoteVersion} 现已可用";
|
||||
UpdateAvailablePanel.Visibility = Visibility.Visible;
|
||||
_availableVersion = remoteVersion;
|
||||
_availableLineGroup = lineGroup;
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateStatusText.Text = "你使用的是最新版本";
|
||||
UpdateAvailablePanel.Visibility = Visibility.Collapsed;
|
||||
_availableVersion = null;
|
||||
_availableLineGroup = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateStatusText.Text = "你使用的是最新版本";
|
||||
UpdateAvailablePanel.Visibility = Visibility.Collapsed;
|
||||
_availableVersion = null;
|
||||
_availableLineGroup = null;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(releaseNotes))
|
||||
{
|
||||
UpdateLogViewer.Markdown = releaseNotes;
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadUpdateLogAsFallback(updateChannel);
|
||||
}
|
||||
|
||||
if (manualCheck)
|
||||
@@ -191,6 +484,470 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadUpdateLogAsFallback(UpdateChannel channel)
|
||||
{
|
||||
try
|
||||
{
|
||||
var updateLog = await AutoUpdateHelper.GetUpdateLog(channel);
|
||||
if (!string.IsNullOrEmpty(updateLog))
|
||||
{
|
||||
UpdateLogViewer.Markdown = updateLog;
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateLogViewer.Markdown = "暂无更新日志";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"加载更新日志失败: {ex.Message}");
|
||||
UpdateLogViewer.Markdown = "加载更新日志失败";
|
||||
}
|
||||
}
|
||||
|
||||
private async void LoadUpdateLogWithPriority(UpdateChannel channel)
|
||||
{
|
||||
try
|
||||
{
|
||||
var (remoteVersion, lineGroup, releaseNotes) = await AutoUpdateHelper.CheckForUpdates(channel, false, false);
|
||||
|
||||
if (!string.IsNullOrEmpty(releaseNotes))
|
||||
{
|
||||
UpdateLogViewer.Markdown = releaseNotes;
|
||||
}
|
||||
else
|
||||
{
|
||||
await LoadUpdateLogAsFallback(channel);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"加载更新日志失败: {ex.Message}");
|
||||
await LoadUpdateLogAsFallback(channel);
|
||||
}
|
||||
}
|
||||
|
||||
private async void LoadHistoryVersions()
|
||||
{
|
||||
try
|
||||
{
|
||||
HistoryLogViewer.Markdown = "正在加载历史版本...";
|
||||
RollbackVersionComboBox.Items.Clear();
|
||||
|
||||
var updateChannel = UpdateChannel.Release;
|
||||
if (MainWindow.Settings?.Startup != null)
|
||||
{
|
||||
updateChannel = MainWindow.Settings.Startup.UpdateChannel;
|
||||
}
|
||||
|
||||
_historyVersions = await AutoUpdateHelper.GetAllGithubReleases(updateChannel);
|
||||
|
||||
if (_historyVersions.Count > 0)
|
||||
{
|
||||
var markdown = new System.Text.StringBuilder();
|
||||
markdown.AppendLine("# 历史版本列表\n");
|
||||
|
||||
foreach (var (version, downloadUrl, releaseNotes) in _historyVersions)
|
||||
{
|
||||
markdown.AppendLine($"## {version}\n");
|
||||
if (!string.IsNullOrEmpty(releaseNotes))
|
||||
{
|
||||
var notes = releaseNotes.Length > 200 ? releaseNotes.Substring(0, 200) + "..." : releaseNotes;
|
||||
markdown.AppendLine(notes);
|
||||
}
|
||||
markdown.AppendLine("\n---\n");
|
||||
}
|
||||
|
||||
HistoryLogViewer.Markdown = markdown.ToString();
|
||||
|
||||
foreach (var versionInfo in _historyVersions)
|
||||
{
|
||||
var item = new ComboBoxItem
|
||||
{
|
||||
Content = versionInfo.version,
|
||||
Tag = versionInfo
|
||||
};
|
||||
RollbackVersionComboBox.Items.Add(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HistoryLogViewer.Markdown = "未获取到历史版本信息。";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"加载历史版本失败: {ex.Message}");
|
||||
HistoryLogViewer.Markdown = "加载历史版本失败";
|
||||
}
|
||||
}
|
||||
|
||||
private void RollbackVersionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (RollbackVersionComboBox.SelectedItem is ComboBoxItem selectedItem && selectedItem.Tag is ValueTuple<string, string, string> versionInfo)
|
||||
{
|
||||
var version = versionInfo.Item1;
|
||||
var downloadUrl = versionInfo.Item2;
|
||||
var releaseNotes = versionInfo.Item3;
|
||||
|
||||
if (!string.IsNullOrEmpty(releaseNotes))
|
||||
{
|
||||
HistoryLogViewer.Markdown = $"# {version}\n\n{releaseNotes}";
|
||||
}
|
||||
else
|
||||
{
|
||||
HistoryLogViewer.Markdown = $"# {version}\n\n无更新日志";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void RollbackButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (RollbackVersionComboBox.SelectedItem is ComboBoxItem selectedItem && selectedItem.Tag is ValueTuple<string, string, string> versionInfo)
|
||||
{
|
||||
var version = versionInfo.Item1;
|
||||
var downloadUrl = versionInfo.Item2;
|
||||
var releaseNotes = versionInfo.Item3;
|
||||
|
||||
var updateChannel = UpdateChannel.Release;
|
||||
if (MainWindow.Settings?.Startup != null)
|
||||
{
|
||||
updateChannel = MainWindow.Settings.Startup.UpdateChannel;
|
||||
}
|
||||
|
||||
var dialog = new ContentDialog
|
||||
{
|
||||
Title = "暂停自动更新",
|
||||
PrimaryButtonText = "确定",
|
||||
SecondaryButtonText = "取消"
|
||||
};
|
||||
|
||||
var panel = new iNKORE.UI.WPF.Modern.Controls.SimpleStackPanel
|
||||
{
|
||||
Spacing = 16,
|
||||
Margin = new Thickness(0, 10, 0, 0)
|
||||
};
|
||||
|
||||
var textBlock = new TextBlock
|
||||
{
|
||||
Text = "请选择在回滚后多久不再接收自动更新:",
|
||||
FontSize = 14,
|
||||
Foreground = ThemeHelper.GetTextPrimaryBrush()
|
||||
};
|
||||
|
||||
var daysComboBox = new ComboBox
|
||||
{
|
||||
Width = 200,
|
||||
Height = 36,
|
||||
HorizontalAlignment = HorizontalAlignment.Left
|
||||
};
|
||||
|
||||
for (int i = 0; i <= 7; i++)
|
||||
{
|
||||
daysComboBox.Items.Add(new ComboBoxItem
|
||||
{
|
||||
Content = $"{i} 天",
|
||||
Tag = i
|
||||
});
|
||||
}
|
||||
|
||||
daysComboBox.SelectedIndex = 0;
|
||||
|
||||
panel.Children.Add(textBlock);
|
||||
panel.Children.Add(daysComboBox);
|
||||
dialog.Content = panel;
|
||||
|
||||
var dialogResult = await dialog.ShowAsync();
|
||||
|
||||
if (dialogResult == ContentDialogResult.Primary)
|
||||
{
|
||||
int days = 0;
|
||||
if (daysComboBox.SelectedItem is ComboBoxItem selectedItemCombo &&
|
||||
selectedItemCombo.Tag != null &&
|
||||
int.TryParse(selectedItemCombo.Tag.ToString(), out int selectedDays))
|
||||
{
|
||||
days = selectedDays;
|
||||
}
|
||||
|
||||
if (days == 0)
|
||||
{
|
||||
MainWindow.Settings.Startup.AutoUpdatePauseUntilDate = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
DateTime pauseUntilDate = DateTime.Now.AddDays(days);
|
||||
MainWindow.Settings.Startup.AutoUpdatePauseUntilDate = pauseUntilDate.ToString("yyyy-MM-dd");
|
||||
LogHelper.WriteLogToFile($"UpdateCenter | 用户选择暂停自动更新 {days} 天,截止日期: {pauseUntilDate:yyyy-MM-dd}");
|
||||
}
|
||||
|
||||
MainWindow.SaveSettingsToFile();
|
||||
LogHelper.WriteLogToFile($"UpdateCenter | 用户确认回滚,目标版本: {version}");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile("UpdateCenter | 用户取消了回滚操作");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
RollbackButton.IsEnabled = false;
|
||||
RollbackVersionComboBox.IsEnabled = false;
|
||||
|
||||
try
|
||||
{
|
||||
bool downloadSuccess = await AutoUpdateHelper.StartManualDownloadAndInstall(
|
||||
version,
|
||||
updateChannel,
|
||||
(percent, text) =>
|
||||
{
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"回滚进度: {percent}% - {text}");
|
||||
}));
|
||||
}
|
||||
);
|
||||
|
||||
if (!downloadSuccess)
|
||||
{
|
||||
MessageBox.Show(
|
||||
"回滚失败,请检查网络连接后重试。",
|
||||
"回滚失败",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
RollbackButton.IsEnabled = true;
|
||||
RollbackVersionComboBox.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(
|
||||
$"回滚过程中发生错误:{ex.Message}",
|
||||
"回滚错误",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
RollbackButton.IsEnabled = true;
|
||||
RollbackVersionComboBox.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show(
|
||||
"请先选择一个要回滚的版本。",
|
||||
"提示",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Information);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"回滚操作失败: {ex.Message}");
|
||||
MessageBox.Show(
|
||||
$"回滚操作失败:{ex.Message}",
|
||||
"错误",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void FixVersionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
MainWindowSettingsHelper.InvokeMainWindowMethod("FixVersionButton_Click", sender, e);
|
||||
}
|
||||
|
||||
private void OptionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!_isLoaded) return;
|
||||
|
||||
var border = sender as Border;
|
||||
if (border == null) return;
|
||||
|
||||
string tag = border.Tag?.ToString();
|
||||
if (string.IsNullOrEmpty(tag)) return;
|
||||
|
||||
switch (tag)
|
||||
{
|
||||
case "UpdateChannel_Release":
|
||||
MainWindowSettingsHelper.UpdateSettingDirectly(() =>
|
||||
{
|
||||
MainWindow.Settings.Startup.UpdateChannel = UpdateChannel.Release;
|
||||
}, "UpdateChannelSelector");
|
||||
MainWindowSettingsHelper.InvokeMainWindowMethod("UpdateChannelSelector_Checked",
|
||||
new System.Windows.Controls.RadioButton { Tag = "Release" }, e);
|
||||
UpdateUpdateChannelButtons(UpdateChannel.Release);
|
||||
LoadHistoryVersions();
|
||||
LoadUpdateLogWithPriority(UpdateChannel.Release);
|
||||
break;
|
||||
|
||||
case "UpdateChannel_Preview":
|
||||
MainWindowSettingsHelper.UpdateSettingDirectly(() =>
|
||||
{
|
||||
MainWindow.Settings.Startup.UpdateChannel = UpdateChannel.Preview;
|
||||
}, "UpdateChannelSelector");
|
||||
MainWindowSettingsHelper.InvokeMainWindowMethod("UpdateChannelSelector_Checked",
|
||||
new System.Windows.Controls.RadioButton { Tag = "Preview" }, e);
|
||||
UpdateUpdateChannelButtons(UpdateChannel.Preview);
|
||||
LoadHistoryVersions();
|
||||
LoadUpdateLogWithPriority(UpdateChannel.Preview);
|
||||
break;
|
||||
|
||||
case "UpdateChannel_Beta":
|
||||
MainWindowSettingsHelper.UpdateSettingDirectly(() =>
|
||||
{
|
||||
MainWindow.Settings.Startup.UpdateChannel = UpdateChannel.Beta;
|
||||
}, "UpdateChannelSelector");
|
||||
MainWindowSettingsHelper.InvokeMainWindowMethod("UpdateChannelSelector_Checked",
|
||||
new System.Windows.Controls.RadioButton { Tag = "Beta" }, e);
|
||||
UpdateUpdateChannelButtons(UpdateChannel.Beta);
|
||||
LoadHistoryVersions();
|
||||
LoadUpdateLogWithPriority(UpdateChannel.Beta);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateUpdateChannelButtons(UpdateChannel selectedChannel)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool isDarkTheme = ThemeHelper.IsDarkTheme;
|
||||
var selectedBrush = isDarkTheme ? new SolidColorBrush(Color.FromRgb(25, 25, 25)) : new SolidColorBrush(Color.FromRgb(225, 225, 225));
|
||||
var unselectedBrush = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
if (UpdateChannelReleaseBorder != null)
|
||||
{
|
||||
bool isSelected = selectedChannel == UpdateChannel.Release;
|
||||
UpdateChannelReleaseBorder.Background = isSelected ? selectedBrush : unselectedBrush;
|
||||
var textBlock = UpdateChannelReleaseBorder.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = isSelected ? FontWeights.Bold : FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
|
||||
if (UpdateChannelPreviewBorder != null)
|
||||
{
|
||||
bool isSelected = selectedChannel == UpdateChannel.Preview;
|
||||
UpdateChannelPreviewBorder.Background = isSelected ? selectedBrush : unselectedBrush;
|
||||
var textBlock = UpdateChannelPreviewBorder.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = isSelected ? FontWeights.Bold : FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
|
||||
if (UpdateChannelBetaBorder != null)
|
||||
{
|
||||
bool isSelected = selectedChannel == UpdateChannel.Beta;
|
||||
UpdateChannelBetaBorder.Background = isSelected ? selectedBrush : unselectedBrush;
|
||||
var textBlock = UpdateChannelBetaBorder.Child as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
textBlock.FontWeight = isSelected ? FontWeights.Bold : FontWeights.Normal;
|
||||
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"更新更新通道按钮状态时出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleSwitch_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!_isLoaded) return;
|
||||
|
||||
var border = sender as Border;
|
||||
if (border == null) return;
|
||||
|
||||
string tag = border.Tag?.ToString();
|
||||
if (string.IsNullOrEmpty(tag)) return;
|
||||
|
||||
bool currentState = GetCurrentSettingValue(tag);
|
||||
bool newState = !currentState;
|
||||
SetToggleSwitchState(border, newState);
|
||||
|
||||
switch (tag)
|
||||
{
|
||||
case "IsAutoUpdate":
|
||||
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsAutoUpdate", newState);
|
||||
AutoUpdateWithSilencePanel.Visibility = newState ? Visibility.Visible : Visibility.Collapsed;
|
||||
AutoUpdateTimePeriodSeparator.Visibility = newState ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (AutoUpdateTimePeriodBlock != null)
|
||||
{
|
||||
AutoUpdateTimePeriodBlock.Visibility =
|
||||
(MainWindow.Settings.Startup.IsAutoUpdateWithSilence && MainWindow.Settings.Startup.IsAutoUpdate) ?
|
||||
Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
break;
|
||||
|
||||
case "IsAutoUpdateWithSilence":
|
||||
MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchIsAutoUpdateWithSilence", newState);
|
||||
if (AutoUpdateTimePeriodBlock != null)
|
||||
{
|
||||
AutoUpdateTimePeriodBlock.Visibility = newState ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetCurrentSettingValue(string tag)
|
||||
{
|
||||
if (MainWindow.Settings == null) return false;
|
||||
|
||||
try
|
||||
{
|
||||
switch (tag)
|
||||
{
|
||||
case "IsAutoUpdate":
|
||||
return MainWindow.Settings.Startup?.IsAutoUpdate ?? false;
|
||||
case "IsAutoUpdateWithSilence":
|
||||
return MainWindow.Settings.Startup?.IsAutoUpdateWithSilence ?? false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Border FindToggleSwitch(string name)
|
||||
{
|
||||
return this.FindDescendantByName(name) as Border;
|
||||
}
|
||||
|
||||
private void SetToggleSwitchState(Border toggleSwitch, bool isOn)
|
||||
{
|
||||
if (toggleSwitch == null) return;
|
||||
toggleSwitch.Background = isOn
|
||||
? new SolidColorBrush(Color.FromRgb(53, 132, 228))
|
||||
: (ThemeHelper.IsDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(225, 225, 225)));
|
||||
var innerBorder = toggleSwitch.Child as Border;
|
||||
if (innerBorder != null)
|
||||
{
|
||||
innerBorder.HorizontalAlignment = isOn ? HorizontalAlignment.Right : HorizontalAlignment.Left;
|
||||
innerBorder.Background = new SolidColorBrush(Colors.White);
|
||||
}
|
||||
}
|
||||
|
||||
private void AutoUpdateWithSilenceStartTimeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (!_isLoaded) return;
|
||||
MainWindowSettingsHelper.InvokeComboBoxSelectionChanged("AutoUpdateWithSilenceStartTimeComboBox", AutoUpdateWithSilenceStartTimeComboBox?.SelectedItem);
|
||||
}
|
||||
|
||||
private void AutoUpdateWithSilenceEndTimeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (!_isLoaded) return;
|
||||
MainWindowSettingsHelper.InvokeComboBoxSelectionChanged("AutoUpdateWithSilenceEndTimeComboBox", AutoUpdateWithSilenceEndTimeComboBox?.SelectedItem);
|
||||
}
|
||||
|
||||
public event EventHandler<RoutedEventArgs> IsTopBarNeedShadowEffect;
|
||||
public event EventHandler<RoutedEventArgs> IsTopBarNeedNoShadowEffect;
|
||||
|
||||
@@ -207,63 +964,18 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
}
|
||||
}
|
||||
|
||||
private void ScrollBar_Scroll(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var scrollbar = (ScrollBar)sender;
|
||||
var scrollviewer = scrollbar.FindAscendant<ScrollViewer>();
|
||||
if (scrollviewer != null) scrollviewer.ScrollToVerticalOffset(scrollbar.Track.Value);
|
||||
}
|
||||
|
||||
private void ScrollBarTrack_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
|
||||
{
|
||||
var border = (Border)sender;
|
||||
if (border.Child is Track track)
|
||||
{
|
||||
track.Width = 16;
|
||||
track.Margin = new Thickness(0, 0, -2, 0);
|
||||
var scrollbar = track.FindAscendant<ScrollBar>();
|
||||
if (scrollbar != null) scrollbar.Width = 16;
|
||||
}
|
||||
}
|
||||
|
||||
private void ScrollBarTrack_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
|
||||
{
|
||||
var border = (Border)sender;
|
||||
if (border.Child is Track track)
|
||||
{
|
||||
track.Width = 6;
|
||||
track.Margin = new Thickness(0, 0, 0, 0);
|
||||
var scrollbar = track.FindAscendant<ScrollBar>();
|
||||
if (scrollbar != null) scrollbar.Width = 6;
|
||||
}
|
||||
}
|
||||
|
||||
private void ScrollbarThumb_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
var thumb = (System.Windows.Controls.Primitives.Thumb)sender;
|
||||
var border = thumb.Template.FindName("ScrollbarThumbEx", thumb);
|
||||
if (border is Border borderElement)
|
||||
{
|
||||
borderElement.Background = new SolidColorBrush(Color.FromRgb(95, 95, 95));
|
||||
}
|
||||
}
|
||||
|
||||
private void ScrollbarThumb_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
var thumb = (System.Windows.Controls.Primitives.Thumb)sender;
|
||||
var border = thumb.Template.FindName("ScrollbarThumbEx", thumb);
|
||||
if (border is Border borderElement)
|
||||
{
|
||||
borderElement.Background = new SolidColorBrush(Color.FromRgb(195, 195, 195));
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyTheme()
|
||||
{
|
||||
try
|
||||
{
|
||||
ThemeHelper.ApplyThemeToControl(this);
|
||||
|
||||
if (UpdateAvailableBorder != null)
|
||||
{
|
||||
UpdateAvailableBorder.Background = ThemeHelper.GetBackgroundPrimaryBrush();
|
||||
UpdateAvailableBorder.BorderBrush = ThemeHelper.GetBorderPrimaryBrush();
|
||||
}
|
||||
|
||||
if (CheckUpdateButton != null)
|
||||
{
|
||||
CheckUpdateButton.Background = ThemeHelper.GetButtonBackgroundBrush();
|
||||
@@ -273,9 +985,37 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
|
||||
if (UpdateNowButton != null)
|
||||
{
|
||||
UpdateNowButton.Background = new SolidColorBrush(Color.FromRgb(0, 120, 212));
|
||||
UpdateNowButton.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235));
|
||||
UpdateNowButton.Foreground = Brushes.White;
|
||||
}
|
||||
|
||||
if (UpdateLaterButton != null)
|
||||
{
|
||||
UpdateLaterButton.Background = ThemeHelper.GetButtonBackgroundBrush();
|
||||
UpdateLaterButton.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
UpdateLaterButton.BorderBrush = ThemeHelper.GetBorderPrimaryBrush();
|
||||
}
|
||||
|
||||
if (SkipVersionButton != null)
|
||||
{
|
||||
SkipVersionButton.Background = Brushes.Transparent;
|
||||
SkipVersionButton.Foreground = ThemeHelper.GetTextSecondaryBrush();
|
||||
SkipVersionButton.BorderThickness = new Thickness(0);
|
||||
}
|
||||
|
||||
if (RollbackButton != null)
|
||||
{
|
||||
RollbackButton.Background = ThemeHelper.GetButtonBackgroundBrush();
|
||||
RollbackButton.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
|
||||
if (FixVersionButton != null)
|
||||
{
|
||||
FixVersionButton.Background = ThemeHelper.GetButtonBackgroundBrush();
|
||||
FixVersionButton.Foreground = ThemeHelper.GetTextPrimaryBrush();
|
||||
}
|
||||
|
||||
SwitchTab(_currentTab);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -314,3 +1054,4 @@ namespace Ink_Canvas.Windows.SettingsViews
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -198,8 +198,10 @@
|
||||
</DrawingImage>
|
||||
<DrawingImage x:Key="UpdateCenterIcon">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<GeometryDrawing Brush="#FF222222" Geometry="F1 M16,16z M0,0z M8,0C3.58172,0 0,3.58172 0,8 0,12.4183 3.58172,16 8,16 12.4183,16 16,12.4183 16,8 16,3.58172 12.4183,0 8,0z M8,14C4.68629,14 2,11.3137 2,8 2,4.68629 4.68629,2 8,2 11.3137,2 14,4.68629 14,8 14,11.3137 11.3137,14 8,14z M8,4C5.79086,4 4,5.79086 4,8 4,10.2091 5.79086,12 8,12 10.2091,12 12,10.2091 12,8 12,5.79086 10.2091,4 8,4z M8,10C6.89543,10 6,9.10457 6,8 6,6.89543 6.89543,6 8,6 9.10457,6 10,6.89543 10,8 10,9.10457 9.10457,10 8,10z"/>
|
||||
<DrawingGroup ClipGeometry="M0,0 V16 H16 V0 H0 Z">
|
||||
<GeometryDrawing Brush="#FF222222" Geometry="F1 M16,16z M0,0z M10.2929,8.70711L7.29288,5.70711H8.70712L5.70712,8.70711C5.51959,8.89465 5.26522,9.00001 5,9.00001C4.73478,9.00001 4.48044,8.89465 4.29291,8.70711C4.10537,8.51958 4,8.26522 4,8.00001C4,7.73479 4.10534,7.48043 4.29288,7.2929L7.29288,4.2929C7.38574,4.20004 7.49598,4.12638 7.61731,4.07612C7.73864,4.02587 7.86868,4 8,4C8.13132,4 8.26136,4.02587 8.38269,4.07612C8.50402,4.12638 8.61426,4.20004 8.70712,4.2929L11.7071,7.2929C11.8947,7.48043 12,7.73479 12,8.00001C12,8.26522 11.8946,8.51958 11.7071,8.70711C11.5196,8.89465 11.2652,9.00001 11,9.00001C10.7348,9.00001 10.4804,8.89465 10.2929,8.70711z"/>
|
||||
<GeometryDrawing Brush="#FF222222" Geometry="F1 M16,16z M0,0z M9,5V11C9,11.2652 8.89463,11.5196 8.70709,11.7071C8.51956,11.8946 8.26522,12 8,12C7.73478,12 7.48044,11.8946 7.29291,11.7071C7.10537,11.5196 7,11.2652 7,11V5C7,4.73478 7.10537,4.48043 7.29291,4.29289C7.48044,4.10536 7.73478,4 8,4C8.26522,4 8.51956,4.10536 8.70709,4.29289C8.89463,4.48043 9,4.73478 9,5z"/>
|
||||
<GeometryDrawing Brush="#FF222222" Geometry="F1 M16,16z M0,0z M2,8C2,11.3087 4.69134,14 8,14C11.3087,14 14,11.3087 14,8C14,4.69133 11.3087,2 8,2C4.69134,2 2,4.69133 2,8ZM16,8C16,12.4233 12.4233,16 8,16C3.57668,16 0,12.4233 0,8C0,3.57668 3.57668,0 8,0C12.4233,0 16,3.57668 16,8z"/>
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
".NETFramework,Version=v4.7.2": {
|
||||
"AForge.Video": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.2.5, )",
|
||||
"resolved": "2.2.5",
|
||||
"contentHash": "XqzcOXtBUagEPRqg/00oayxlCPmxP4284SdM62mVotsNoD03fs19BrzdMBfhUOOYPyd0B/IXH7tEWnSDmc2gxA==",
|
||||
"dependencies": {
|
||||
"AForge": "2.2.5"
|
||||
}
|
||||
},
|
||||
"AForge.Video.DirectShow": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.2.5, )",
|
||||
"resolved": "2.2.5",
|
||||
"contentHash": "pEch6felU/RGAbl0A7yjaQjsGxwiRFU9R+qBqR92wQo++XhzPLeQaZHnAPIBYaG7MfoqtjgCDkK4z3Tra4VQ3w==",
|
||||
"dependencies": {
|
||||
"AForge.Video": "2.2.5"
|
||||
}
|
||||
},
|
||||
"Costura.Fody": {
|
||||
"type": "Direct",
|
||||
"requested": "[6.0.0, )",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "3Uriu9GJABMivG0wXMJs6NQ7FNE3pylir1gZEBAWDvpii3cnrmxXnOG44MMDuIVOIk/Xhef7WZFsaCNV+py9qA==",
|
||||
"dependencies": {
|
||||
"Fody": "6.8.2"
|
||||
}
|
||||
},
|
||||
"Hardcodet.NotifyIcon.Wpf": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.1.0, )",
|
||||
"resolved": "1.1.0",
|
||||
"contentHash": "FFEkdRSidMrbMfQVwhCzq6KtSyR1qQojqtiUrkCyjnOOuMf7W3TNQkzFnBBxYxFe7uLk5mz7M38wFh1qdWyfvg=="
|
||||
},
|
||||
"iNKORE.UI.WPF.Modern": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.9.27, )",
|
||||
"resolved": "0.9.27",
|
||||
"contentHash": "Mt7KEDjYMdq7hOdPQ0accBN/X6Eb1bkgvyqs+qTGpfx9GKTVhtShbBslWplrqtKvvLsC9/6uU+bTa6rKBcO+kA==",
|
||||
"dependencies": {
|
||||
"System.ValueTuple": "4.5.0"
|
||||
}
|
||||
},
|
||||
"MdXaml": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.27.0, )",
|
||||
"resolved": "1.27.0",
|
||||
"contentHash": "VWhqhCeKVkJe8vkPmXuGZlRX01WDrTugOLeUvJn18jH/8DrGGVBvtgIlJoELHD2f1DiEWqF3lxxjV55vnzE7Tg==",
|
||||
"dependencies": {
|
||||
"AvalonEdit": "6.3.0.90",
|
||||
"MdXaml.Plugins": "1.27.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.International.Converters.PinYinConverter": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.0, )",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "hEs/VPwGFeVNLf2Wc6k2bMYF71zE6x+lW4MfebRAjkVbAbaa4DbEmdFRfSBymvGLtdsCUvXo2aa/yFKYSCYmEQ=="
|
||||
},
|
||||
"Microsoft.Office.Interop.PowerPoint": {
|
||||
"type": "Direct",
|
||||
"requested": "[15.0.4420.1018, )",
|
||||
"resolved": "15.0.4420.1018",
|
||||
"contentHash": "3XkJAqbYq93MqqSOPusNE+6Tve5yBkbE92n9W8f+k8Z9AzXeoTMqMYqhZMrzVOp4saHhMMbOD78JkCjvk+qIzw=="
|
||||
},
|
||||
"Microsoft.Toolkit.Uwp.Notifications": {
|
||||
"type": "Direct",
|
||||
"requested": "[7.1.3, )",
|
||||
"resolved": "7.1.3",
|
||||
"contentHash": "A1dglAzb24gjehmb7DwGd07mfyZ1gacAK7ObE0KwDlRc3mayH2QW7cSOy3TkkyELjLg19OQBuhPOj4SpXET9lg==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": "1.0.0",
|
||||
"Microsoft.Windows.SDK.Contracts": "10.0.19041.1",
|
||||
"System.ValueTuple": "4.5.0"
|
||||
}
|
||||
},
|
||||
"MicrosoftOfficeCore": {
|
||||
"type": "Direct",
|
||||
"requested": "[15.0.0, )",
|
||||
"resolved": "15.0.0",
|
||||
"contentHash": "/KgYw8uvv0Ut8FPvyEa1XJ/TrLSRgvTv3JgC2IT0/8NwoICzbEJWfmfwsE6Dwi8WH0jVkTEO7zRvGvaKqUOhqQ=="
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Direct",
|
||||
"requested": "[13.0.3, )",
|
||||
"resolved": "13.0.3",
|
||||
"contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ=="
|
||||
},
|
||||
"NHotkey.Wpf": {
|
||||
"type": "Direct",
|
||||
"requested": "[3.0.0, )",
|
||||
"resolved": "3.0.0",
|
||||
"contentHash": "BIUKlhTG5KtFf9OQzWvkmVmktt5/FFj6AOEgag8Uf0R2YdZt5ajUzs3sVskcJcT2TztWlEHKQr1jFj3KQ0D9Nw==",
|
||||
"dependencies": {
|
||||
"NHotkey": "3.0.0"
|
||||
}
|
||||
},
|
||||
"OSVersionExt": {
|
||||
"type": "Direct",
|
||||
"requested": "[3.0.0, )",
|
||||
"resolved": "3.0.0",
|
||||
"contentHash": "G+3FGoEFhjSMx24UInvqRVb7dQ7K0coC8E+EiZWhnQGnDe8IJa14AZ0IDqz5YkwUFHOAcVUnoMWDqLH2RPR0wA=="
|
||||
},
|
||||
"Sentry": {
|
||||
"type": "Direct",
|
||||
"requested": "[6.0.0, )",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "KomDnmKInN30NIaf52UsRYCK/QS8cBe1jV+JslxBzys30wPH0Wv0U20YQ2oJ65P0+XLv0JqNHmgAjN1zDy/fzQ==",
|
||||
"dependencies": {
|
||||
"System.Reflection.Metadata": "5.0.0",
|
||||
"System.Runtime.InteropServices.RuntimeInformation": "4.3.0",
|
||||
"System.Text.Json": "8.0.5"
|
||||
}
|
||||
},
|
||||
"Sentry.Profiling": {
|
||||
"type": "Direct",
|
||||
"requested": "[6.0.0, )",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "UR5lOlSANE9vFDWoIyq0+nLy14FX3tUxfa9sXu8EaylZHogYnXnLnXKb3mmYVp+r+MAhxbHmTOF6pxeiamBnvQ=="
|
||||
},
|
||||
"AForge": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.5",
|
||||
"contentHash": "clkumhM9DggqIzEXAHgVLeWO4arG5YfoPr7J4jfjJx35AoeEIJSSm49J25bwp/9mXQYLwi7y1Wunc8qgYJsGxg=="
|
||||
},
|
||||
"AvalonEdit": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.3.0.90",
|
||||
"contentHash": "WVTb5MxwGqKdeasd3nG5udlV4t6OpvkFanziwI133K0/QJ5FvZmfzRQgpAjGTJhQfIA8GP7AzKQ3sTY9JOFk8Q=="
|
||||
},
|
||||
"Fody": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.8.2",
|
||||
"contentHash": "sjGHrtGS1+kcrv99WXCvujOFBTQp4zCH3ZC9wo2LAtVaJkuLpHghQx3y4k1Q8ZKuDAbEw+HE6ZjPUJQK3ejepQ=="
|
||||
},
|
||||
"MdXaml.Plugins": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.27.0",
|
||||
"contentHash": "We7LtBdoukRg9mqTfa1f5n8z/GQPMKBRj3URk9DiMuqzIHkW1lTgK5njVPSScxsRt4YzW22423tSnLWNm2MJKg=="
|
||||
},
|
||||
"Microsoft.Bcl.AsyncInterfaces": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
|
||||
"dependencies": {
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "7D2TMufjGiowmt0E941kVoTIS+GTNzaPopuzM1/1LSaJAdJdBrVP0SkZW7AgDd0a2U1DjsIeaKG1wxGVBNLDMw==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net472": "1.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net472": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "VI0f22cH/xOlXLUVY7t6vr2Gi7cg0vzwpspNGz5isRusyspZblF7jJMw49E8mF1m071JyeVmZVkIK9c5kbiyFA=="
|
||||
},
|
||||
"Microsoft.Windows.SDK.Contracts": {
|
||||
"type": "Transitive",
|
||||
"resolved": "10.0.19041.1",
|
||||
"contentHash": "sgDwuoyubbLFNJR/BINbvfSNRiglF91D+Q0uEAkU4ZTO5Hgbnu8+gA4TCc65S56e1kK7gvR1+H4kphkDTr+9bw==",
|
||||
"dependencies": {
|
||||
"System.Runtime.WindowsRuntime": "4.6.0",
|
||||
"System.Runtime.WindowsRuntime.UI.Xaml": "4.6.0"
|
||||
}
|
||||
},
|
||||
"NHotkey": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.0.0",
|
||||
"contentHash": "IEghs0QqWsQYH0uUmvIl0Ye6RaebWRh38eB6ToOkDnQucTYRGFOgtig0gSxlwCszTilYFz3n1ZuY762x+kDR3A=="
|
||||
},
|
||||
"System.Buffers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
|
||||
},
|
||||
"System.Collections.Immutable": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.4"
|
||||
}
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.5",
|
||||
"contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.5.1",
|
||||
"System.Numerics.Vectors": "4.5.0",
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
|
||||
}
|
||||
},
|
||||
"System.Numerics.Vectors": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
|
||||
},
|
||||
"System.Reflection.Metadata": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==",
|
||||
"dependencies": {
|
||||
"System.Collections.Immutable": "5.0.0"
|
||||
}
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
|
||||
},
|
||||
"System.Runtime.InteropServices.RuntimeInformation": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw=="
|
||||
},
|
||||
"System.Runtime.WindowsRuntime": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.6.0",
|
||||
"contentHash": "IWrs1TmbxP65ZZjIglNyvDkFNoV5q2Pofg5WO7I8RKQOpLdFprQSh3xesOoClBqR4JHr4nEB1Xk1MqLPW1jPuQ=="
|
||||
},
|
||||
"System.Runtime.WindowsRuntime.UI.Xaml": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.6.0",
|
||||
"contentHash": "r4tNw5v5kqRJ9HikWpcyNf3suGw7DjX93svj9iBjtdeLqL8jt9Z+7f+s4wrKZJr84u8IMsrIjt8K6jYvkRqMSg==",
|
||||
"dependencies": {
|
||||
"System.Runtime.WindowsRuntime": "4.6.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Encodings.Web": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.5.1",
|
||||
"System.Memory": "4.5.5",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.5",
|
||||
"contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "8.0.0",
|
||||
"System.Buffers": "4.5.1",
|
||||
"System.Memory": "4.5.5",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0",
|
||||
"System.Text.Encodings.Web": "8.0.0",
|
||||
"System.Threading.Tasks.Extensions": "4.5.4",
|
||||
"System.ValueTuple": "4.5.0"
|
||||
}
|
||||
},
|
||||
"System.Threading.Tasks.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.4",
|
||||
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
|
||||
"dependencies": {
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
|
||||
}
|
||||
},
|
||||
"System.ValueTuple": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.7.2/win": {
|
||||
"System.Runtime.InteropServices.RuntimeInformation": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.7.2/win-arm64": {
|
||||
"System.Runtime.InteropServices.RuntimeInformation": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.7.2/win-x64": {
|
||||
"System.Runtime.InteropServices.RuntimeInformation": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.7.2/win-x86": {
|
||||
"System.Runtime.InteropServices.RuntimeInformation": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw=="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
隐私政策
|
||||
|
||||
本软件指 InkCanvasForClass Community Edition(以下称"本软件"或"我们")。
|
||||
|
||||
本软件重视用户隐私,尊重并保护所有使用服务用户的个人隐私权。为了给您提供更准确、更有个性化的服务,本软件会按照本隐私政策的规定使用和披露您的个人信息。
|
||||
|
||||
本隐私政策说明了我们如何收集、使用、存储、传输和保护您的个人信息,以及您对您的个人信息享有的权利。请您仔细阅读本隐私政策,在充分理解并同意后使用本软件。
|
||||
|
||||
一、我们收集的个人信息
|
||||
|
||||
当您使用本软件时,我们可能收集以下个人信息:
|
||||
|
||||
1. 设备标识信息
|
||||
- 设备ID:基于硬件特征生成的唯一设备标识符(25位字符)
|
||||
- 设备硬件指纹:包括但不限于处理器ID、主板序列号、硬盘序列号等硬件特征信息
|
||||
|
||||
2. 使用统计信息(仅在您同意启用匿名使用数据上传时收集)
|
||||
- 应用启动次数
|
||||
- 总使用时长(秒级精度)
|
||||
- 平均会话时长
|
||||
- 使用频率(高频/中频/低频)
|
||||
- 更新优先级
|
||||
- 最后使用时间
|
||||
- 更新通道选择(正式版/预览版/测试版)
|
||||
|
||||
3. 应用信息
|
||||
- 应用版本号
|
||||
- 操作系统版本信息
|
||||
- 系统环境信息
|
||||
|
||||
4. 崩溃日志信息(仅在您选择"上传基础 + 可选数据"时收集)
|
||||
- 崩溃报告文件内容
|
||||
- 崩溃发生时间
|
||||
- 崩溃相关技术信息
|
||||
|
||||
5. 其他信息
|
||||
- 您在使用本软件过程中主动提供的其他信息
|
||||
|
||||
我们不会收集您的姓名、手机号、身份证号、银行卡号、位置信息、通讯录、短信、通话记录等敏感个人信息。
|
||||
|
||||
二、个人信息的使用目的
|
||||
|
||||
我们收集您的个人信息用于以下目的:
|
||||
|
||||
1. 提供和改进服务:分析软件使用情况,优化软件功能和性能
|
||||
2. 问题诊断:通过崩溃日志分析软件问题,修复错误
|
||||
3. 更新推送:根据使用频率和优先级,向您推送合适的软件更新
|
||||
4. 统计分析:进行匿名化的数据统计和分析,了解用户使用习惯
|
||||
5. 法律合规:遵守适用的法律法规要求
|
||||
|
||||
三、个人信息的存储和传输
|
||||
|
||||
1. 本地存储
|
||||
- 您的个人信息主要存储在您设备本地(device_id.dat、usage_stats.enc 等文件)
|
||||
- 我们采用加密方式存储敏感信息
|
||||
|
||||
2. 数据传输
|
||||
- 仅在您明确同意并启用匿名使用数据上传功能时,我们才会将匿名化的使用统计信息和崩溃日志传输至第三方服务提供商
|
||||
- 数据传输采用加密传输协议(HTTPS)
|
||||
|
||||
3. 跨境传输
|
||||
- 本软件使用的第三方服务提供商 Sentry(GlitchTip)的服务器位于境外
|
||||
- 当您启用匿名使用数据上传功能时,您的匿名化数据将被传输至 Sentry 服务器
|
||||
- 我们已与 Sentry 签署数据处理协议,确保其按照适用的数据保护法律处理您的数据
|
||||
- 您有权随时关闭匿名使用数据上传功能,停止数据传输
|
||||
|
||||
四、第三方服务提供商(SDK)
|
||||
|
||||
本软件集成了以下第三方服务:
|
||||
|
||||
| 第三方服务名称 | 服务提供商 | 收集目的 | 收集的信息类型 | 隐私政策链接 |
|
||||
|--------------|----------|---------|--------------|------------|
|
||||
| Sentry (GlitchTip) | Sentry, Inc. | 错误监控和性能分析 | 设备ID、应用版本、操作系统版本、使用统计、崩溃日志 | https://sentry.io/privacy/ |
|
||||
|
||||
我们要求第三方服务提供商严格遵守相关法律法规,采取适当的安全措施保护您的个人信息,并仅将您的个人信息用于我们明确告知的目的。
|
||||
|
||||
五、信息披露
|
||||
|
||||
除以下情况外,我们不会向第三方披露您的个人信息:
|
||||
|
||||
1. 获得您的明确同意
|
||||
2. 法律法规要求:根据法律、法规、司法程序或政府主管部门的要求
|
||||
3. 紧急情况:为保护本软件、用户或公众的权利、财产或安全
|
||||
4. 业务转让:如发生合并、收购或资产转让,我们将提前通知您
|
||||
|
||||
我们不会向任何无关第三方提供、出售、出租、分享或交易您的个人信息。
|
||||
|
||||
六、您的权利
|
||||
|
||||
根据相关法律法规,您对自己的个人信息享有以下权利:
|
||||
|
||||
1. 知情权:您有权了解我们收集、使用您个人信息的情况
|
||||
2. 访问权:您有权访问我们收集的您的个人信息
|
||||
3. 更正权:您有权要求更正不准确的个人信息
|
||||
4. 删除权:您有权要求删除您的个人信息
|
||||
5. 撤回同意权:您有权随时撤回对个人信息处理的同意
|
||||
- 您可以在软件设置中关闭"匿名使用数据上传"功能
|
||||
- 关闭后,我们将停止收集和上传您的使用数据
|
||||
6. 注销权:您有权注销账号(如适用)
|
||||
- 本软件为本地应用,不涉及账号系统,您可以直接卸载软件
|
||||
- 卸载软件后,本地存储的个人信息文件将被删除
|
||||
|
||||
七、如何行使您的权利
|
||||
|
||||
如您需要行使上述权利,请通过以下方式联系我们:
|
||||
|
||||
1. GitHub Issues(推荐方式)
|
||||
- 项目仓库:https://github.com/InkCanvasForClass/community
|
||||
- 功能需求与错误报告:https://github.com/InkCanvasForClass/community/issues
|
||||
- 隐私相关请求:请在提交时标注"隐私政策"或"个人信息"标签
|
||||
|
||||
2. 在线反馈:通过软件内的反馈功能提交请求
|
||||
|
||||
3. 响应时间:我们将在收到您的请求后15个工作日内予以回复
|
||||
|
||||
八、信息安全
|
||||
|
||||
我们采取以下安全措施保护您的个人信息:
|
||||
|
||||
1. 数据加密:对敏感信息进行加密存储和传输
|
||||
2. 访问控制:限制对个人信息的访问权限
|
||||
3. 安全审计:定期进行安全审计和风险评估
|
||||
4. 安全事件响应:建立安全事件应急响应机制
|
||||
|
||||
尽管我们采取了合理的安全措施,但请注意,任何信息传输都无法保证100%安全。请您妥善保管您的设备,避免个人信息泄露。
|
||||
|
||||
九、Cookie 和类似技术
|
||||
|
||||
本软件为桌面应用程序,不使用 Cookie 技术。
|
||||
|
||||
十、隐私政策的更新
|
||||
|
||||
我们可能会不时更新本隐私政策。更新后的隐私政策将在本软件中公布,并通过适当方式通知您。请您定期查看本隐私政策,了解最新的隐私保护措施。
|
||||
|
||||
本隐私政策的重大变更(如收集信息类型、使用目的、第三方服务提供商等发生重大变化)将提前30天通知您。
|
||||
|
||||
十一、联系我们
|
||||
|
||||
如您对本隐私政策有任何疑问、意见或建议,或需要行使您的权利,请通过以下方式联系我们:
|
||||
|
||||
- GitHub Issues:https://github.com/InkCanvasForClass/community/issues
|
||||
- 我们将在收到您的请求后15个工作日内予以回复
|
||||
|
||||
十二、适用法律
|
||||
|
||||
本隐私政策的解释和执行适用中华人民共和国法律法规。如本隐私政策的任何条款与适用的法律法规相冲突,应以适用的法律法规为准。
|
||||
|
||||
十三、同意与授权
|
||||
|
||||
请您在使用本软件前仔细阅读本隐私政策。您使用本软件即表示您已充分理解并同意本隐私政策的全部内容。
|
||||
|
||||
您可以在软件设置中随时查看、修改或撤回对匿名使用数据上传的同意。撤回同意不影响撤回前基于您的同意已进行的个人信息处理活动的效力。
|
||||
|
||||
生效日期:2026年2月6日
|
||||
最后更新:2026年2月7日
|
||||
+15
-4
@@ -13,12 +13,23 @@ body = """
|
||||
{% endif %}\
|
||||
{% for group, commits in commits | group_by(attribute="group") %}
|
||||
### {{ group | striptags | trim | upper_first }}
|
||||
{% for commit in commits %}
|
||||
- {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
|
||||
{% if commit.breaking %}[**breaking**] {% endif %}\
|
||||
{{ commit.message | upper_first }} ({{commit.id}} by {{commit.author.name}})\
|
||||
{% for commit in commits | unique(attribute="message") %}
|
||||
* {{ commit.message | split(pat="\n") | first | trim }}\
|
||||
{% if commit.remote.username %} by @{{ commit.remote.username }}{%- endif %}\
|
||||
{% if commit.remote.pr_number %} in #{{ commit.remote.pr_number }}{%- endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
- 该版本有 {{ statistics.commit_count }} 次提交
|
||||
- 在提交中检测到 {{ statistics.links | length }} 个关联议题
|
||||
{%- if statistics.links | length > 0 %}
|
||||
{%- for link in statistics.links %}
|
||||
{{ " " }}- [{{ link.text }}]({{ link.href }}) (referenced {{ link.count }} time(s))
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
|
||||
感谢 {% for contributor in github.contributors %} @{{ contributor.username }} {%- endfor -%} 为本版本做出了贡献
|
||||
|
||||
"""
|
||||
# Remove leading and trailing whitespaces from the changelog's body.
|
||||
trim = true
|
||||
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
隐私政策
|
||||
本软件指 InkCanvasForClass Community Edition(以下称本软件)。
|
||||
本软件重视用户隐私,本软件尊重并保护所有使用服务用户的个人隐私权。为了给您提供更准确、更有个性化的服务,本软件会按照本隐私权政策的规定使用和披露您的个人信息。
|
||||
除本隐私权政策另有规定外,在未征得您事先许可的情况下,本软件不会将这些信息对外披露或向第三方提供。本软件会不时更新本隐私权政策。您在同意本软件服务使用协议之时,即视为您已经同意本隐私权政策全部内容。本隐私权政策属于本软件服务使用协议不可分割的一部分。
|
||||
1. 适用范围
|
||||
1)本软件收集到的您在本软件发布的有关信息数据,包括但不限于参与活动、成交信息及评价详情;
|
||||
2)违反法律规定或违反本软件规则行为及本软件已对您采取的措施。
|
||||
2.信息使用
|
||||
a)本软件不会向任何无关第三方提供、出售、出租、分享或交易您的个人信息,除非事先得到您的许可,或该第三方和本软件(含本软件关联公司)单独或共同为您提供服务,且在该服务结束后,其将被禁止访问包括其以前能够访问的所有这些资料。
|
||||
b)本软件亦不允许任何第三方以任何手段收集、编辑、出售或者无偿传播您的个人信息。任何本软件平台用户如从事上述活动,一经发现,本软件有权立即终止与该用户的服务协议。
|
||||
c)为服务用户的目的,本软件可能通过使用您的个人信息,向您提供您感兴趣的信息,包括但不限于向您发出产品和服务信息,或者与本软件合作伙伴共享信息以便他们向您发送有关其产品和服务的信息(后者需要您的事先同意)。
|
||||
3.信息披露
|
||||
在如下情况下,本软件将依据您的个人意愿或法律的规定全部或部分的披露您的个人信息:
|
||||
1)经您事先同意,向第三方披露;
|
||||
2)为提供您所要求的产品和服务,而必须和第三方分享您的个人信息;
|
||||
3)根据法律的有关规定,或者行政或司法机构的要求,向第三方或者行政、司法机构披露;
|
||||
4)如您出现违反中国有关法律、法规或者本软件服务协议或相关规则的情况,需要向第三方披露;
|
||||
5)如您是适格的知识产权投诉人并已提起投诉,应被投诉人要求,向被投诉人披露,以便双方处理可能的权利纠纷;
|
||||
6)在本软件平台上创建的某一交易中,如交易任何一方履行或部分履行了交易义务并提出信息披露请求的,本软件有权决定向该用户提供其交易对方的联络方式等必要信息,以促成交易的完成或纠纷的解决。
|
||||
7)其它本软件根据法律、法规或者网站政策认为合适的披露。
|
||||
4. 信息存储和交换
|
||||
本软件收集的有关您的信息和资料将保存在本软件及(或)其关联公司的服务器上,这些信息和资料可能传送至您所在国家、地区或本软件收集信息和资料所在地的境外并在境外被访问、存储和展示。
|
||||
5. Cookie的使用
|
||||
a)在您未拒绝接受Cookies的情况下,本软件会在您的计算机上设定或取用Cookies
|
||||
b)您有权选择接受或拒绝接受Cookies。您可以通过修改浏览器设置的方式拒绝接受Cookies。但如果您选择拒绝接受Cookies,则您可能无法登录或使用依赖于Cookies的本软件网络服务或功能。
|
||||
c)通过本软件所设Cookies所取得的有关信息,将适用本政策。
|
||||
6. 信息安全
|
||||
a) 本软件帐号均有安全保护功能,请妥善保管您的用户名及密码信息。本软件将通过对用户密码进行加密等安全措施确保您的信息不丢失,不被滥用和变造。尽管有前述安全措施,但同时也请您注意在信息网络上不存在“完善的安全措施”。
|
||||
b) 在使用本软件网络服务进行网上交易时,您不可避免的要向交易对方或潜在的交易对方披露自己的个人信息,如联络方式或者邮政地址。请您妥善保护自己的个人信息,仅在必要的情形下向他人提供。如您发现自己的个人信息泄密,尤其是本软件用户名及密码发生泄露,请您立即联络本软件客服,以便本软件采取相应措施。
|
||||
|
||||
Reference in New Issue
Block a user