PageVersionMod.xaml.vb 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. Public Class PageVersionMod
  2. #Region "初始化"
  3. Private IsLoad As Boolean = False
  4. Public Sub PageOther_Loaded() Handles Me.Loaded
  5. PanBack.ScrollToHome()
  6. AniControlEnabled += 1
  7. SelectedMods.Clear()
  8. RefreshList()
  9. ChangeAllSelected(False)
  10. AniControlEnabled -= 1
  11. '非重复加载部分
  12. If IsLoad Then Exit Sub
  13. IsLoad = True
  14. #If DEBUG Then
  15. BtnManageCheck.Visibility = Visibility.Visible
  16. #End If
  17. End Sub
  18. ''' <summary>
  19. ''' 刷新 Mod 列表。
  20. ''' </summary>
  21. Public Sub RefreshList(Optional ForceReload As Boolean = False)
  22. If McModLoader.State = LoadState.Loading Then Exit Sub
  23. If LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", If(ForceReload, LoaderFolderRunType.ForceRun, LoaderFolderRunType.RunOnUpdated)) Then
  24. Log("[System] 已刷新 Mod 列表")
  25. PanBack.ScrollToHome()
  26. SearchBox.Text = ""
  27. End If
  28. End Sub
  29. Private Sub LoaderInit() Handles Me.Initialized
  30. PageLoaderInit(Load, PanLoad, PanAllBack, Nothing, McModLoader, AddressOf Load_Finish, AutoRun:=False)
  31. End Sub
  32. Private Sub Load_Click(sender As Object, e As MouseButtonEventArgs) Handles Load.Click
  33. If McModLoader.State = LoadState.Failed Then
  34. LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.ForceRun)
  35. End If
  36. End Sub
  37. #End Region
  38. #Region "UI 化"
  39. Private PanItems As StackPanel
  40. ''' <summary>
  41. ''' 将 Mod 列表加载为 UI。
  42. ''' </summary>
  43. Private Sub Load_Finish(Loader As LoaderTask(Of String, List(Of McMod)))
  44. Dim List As List(Of McMod) = Loader.Output
  45. Try
  46. PanList.Children.Clear()
  47. '判断应该显示哪一个页面
  48. If List.Count = 0 Then
  49. PanEmpty.Visibility = Visibility.Visible
  50. PanBack.Visibility = Visibility.Collapsed
  51. Exit Sub
  52. Else
  53. PanBack.Visibility = Visibility.Visible
  54. PanEmpty.Visibility = Visibility.Collapsed
  55. End If
  56. SearchBox.Text = ""
  57. '建立 StackPanel
  58. PanItems = New StackPanel With {.Margin = New Thickness(18, MyCard.SwapedHeight, 18, If(List.Count > 0, 20, 0)), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0)}
  59. For Each ModEntity As McMod In List
  60. PanItems.Children.Add(McModListItem(ModEntity))
  61. Next
  62. '建立 MyCard
  63. Dim NewCard As New MyCard With {.Title = McModGetTitle(List), .Margin = New Thickness(0, 0, 0, 15)}
  64. NewCard.Children.Add(PanItems)
  65. PanList.Children.Add(NewCard)
  66. Catch ex As Exception
  67. Log(ex, "加载 Mod 列表 UI 失败", LogLevel.Feedback)
  68. End Try
  69. End Sub
  70. ''' <summary>
  71. ''' 获取 Card 的标题。
  72. ''' </summary>
  73. Private Function McModGetTitle(List As List(Of McMod)) As String
  74. Dim Counter = {0, 0, 0}
  75. For Each ModEntity As McMod In List
  76. Counter(ModEntity.State) += 1
  77. Next
  78. If List.Count = 0 Then Return "未找到任何 Mod"
  79. Dim TypeList As New List(Of String)
  80. If Counter(McMod.McModState.Fine) > 0 Then TypeList.Add("启用 " & Counter(McMod.McModState.Fine))
  81. If Counter(McMod.McModState.Disabled) > 0 Then TypeList.Add("禁用 " & Counter(McMod.McModState.Disabled))
  82. If Counter(McMod.McModState.Unavaliable) > 0 Then TypeList.Add("错误 " & Counter(McMod.McModState.Unavaliable))
  83. Return "Mod 列表(" & Join(TypeList, ",") & ")"
  84. End Function
  85. Private Function McModListItem(Entry As McMod) As MyListItem
  86. '图标
  87. Dim Logo As String
  88. Select Case Entry.State
  89. Case McMod.McModState.Fine
  90. Logo = "pack://application:,,,/images/Blocks/RedstoneLampOn.png"
  91. Case McMod.McModState.Disabled
  92. Logo = "pack://application:,,,/images/Blocks/RedstoneLampOff.png"
  93. Case Else '出错
  94. Logo = "pack://application:,,,/images/Blocks/RedstoneBlock.png"
  95. End Select
  96. '文本
  97. Dim Title As String
  98. If Entry.State = McMod.McModState.Disabled Then
  99. Title = GetFileNameWithoutExtentionFromPath(Entry.Path.Substring(0, Entry.Path.Count - ".disabled".Count)) & "(已禁用)"
  100. Else
  101. Title = GetFileNameWithoutExtentionFromPath(Entry.Path)
  102. End If
  103. Dim Desc As String
  104. If Entry.Version Is Nothing OrElse Entry.Description IsNot Nothing Then
  105. Desc = Entry.Name & If(Entry.Version Is Nothing, "", " (" & Entry.Version & ")") & " : " & If(Entry.Description, Entry.Path)
  106. ElseIf Entry.IsFileAvailable Then
  107. Desc = Entry.Path
  108. Else
  109. Desc = "存在错误 : " & Entry.Path
  110. End If
  111. 'Desc = If(Entry.ModId, "无可用名称") & " (" & If(Entry.Version, "无可用版本") & ")"
  112. 'If Entry.Dependencies.Count > 0 Then
  113. ' Dim DepList As New List(Of String)
  114. ' For Each Dep In Entry.Dependencies
  115. ' DepList.Add(Dep.Key & If(Dep.Value Is Nothing, "", "@" & Dep.Value))
  116. ' Next
  117. ' Desc += " : " & Join(DepList, ", ")
  118. 'End If
  119. '实例化
  120. AniControlEnabled += 1
  121. Dim NewItem As New MyListItem With {
  122. .LogoClickable = True, .Logo = Logo, .SnapsToDevicePixels = True, .Title = Title, .Info = Desc, .Height = 42, .Tag = Entry,
  123. .Type = MyListItem.CheckType.CheckBox, .IsScaleAnimationEnabled = False,
  124. .PaddingRight = 73,
  125. .ContentHandler = AddressOf McModContent, .Checked = SelectedMods.Contains(Entry.RawFileName)
  126. }
  127. AniControlEnabled -= 1
  128. Return NewItem
  129. End Function
  130. Private Sub McModContent(sender As MyListItem, e As EventArgs)
  131. Dim ModEntity As McMod = sender.Tag
  132. '注册点击事件
  133. AddHandler sender.Changed, AddressOf CheckChanged
  134. AddHandler sender.LogoClick, AddressOf ItemLogo_Click
  135. '图标按钮
  136. Dim BtnDel As New MyIconButton With {.LogoScale = 1.1, .Logo = Logo.IconButtonDelete, .Tag = sender}
  137. BtnDel.ToolTip = "删除"
  138. ToolTipService.SetPlacement(BtnDel, Primitives.PlacementMode.Center)
  139. ToolTipService.SetVerticalOffset(BtnDel, 30)
  140. ToolTipService.SetHorizontalOffset(BtnDel, 2)
  141. AddHandler BtnDel.Click, AddressOf Delete_Click
  142. Dim BtnOpen As New MyIconButton With {.LogoScale = 1.15, .Logo = Logo.IconButtonOpen, .Tag = sender}
  143. BtnOpen.ToolTip = "打开文件位置"
  144. ToolTipService.SetPlacement(BtnOpen, Primitives.PlacementMode.Center)
  145. ToolTipService.SetVerticalOffset(BtnOpen, 30)
  146. ToolTipService.SetHorizontalOffset(BtnOpen, 2)
  147. AddHandler BtnOpen.Click, AddressOf Open_Click
  148. Dim BtnCont As New MyIconButton With {.LogoScale = 1.05, .Logo = Logo.IconButtonInfo, .Tag = sender}
  149. BtnCont.ToolTip = "详情"
  150. ToolTipService.SetPlacement(BtnCont, Primitives.PlacementMode.Center)
  151. ToolTipService.SetVerticalOffset(BtnCont, 30)
  152. ToolTipService.SetHorizontalOffset(BtnCont, 2)
  153. AddHandler BtnCont.Click, AddressOf Info_Click
  154. AddHandler sender.MouseRightButtonDown, AddressOf Info_Click
  155. sender.Buttons = {BtnCont, BtnOpen, BtnDel}
  156. End Sub
  157. #End Region
  158. #Region "管理"
  159. ''' <summary>
  160. ''' 打开 Mods 文件夹。
  161. ''' </summary>
  162. Private Sub BtnManageOpen_Click(sender As Object, e As EventArgs) Handles BtnManageOpen.Click, BtnHintOpen.Click
  163. Try
  164. Directory.CreateDirectory(PageVersionLeft.Version.PathIndie & "mods\")
  165. OpenExplorer("""" & PageVersionLeft.Version.PathIndie & "mods\""")
  166. Catch ex As Exception
  167. Log(ex, "打开 Mods 文件夹失败", LogLevel.Msgbox)
  168. End Try
  169. End Sub
  170. #If DEBUG Then
  171. ''' <summary>
  172. ''' 检查 Mod。
  173. ''' </summary>
  174. Private Sub BtnManageCheck_Click(sender As Object, e As EventArgs) Handles BtnManageCheck.Click
  175. Try
  176. Dim Result = McModCheck(PageVersionLeft.Version, McModLoader.Output)
  177. If Result.Count > 0 Then
  178. MyMsgBox(Join(Result, vbCrLf & vbCrLf), "Mod 检查结果")
  179. Else
  180. Hint("Mod 检查完成,未发现任何问题!", HintType.Finish)
  181. End If
  182. Catch ex As Exception
  183. Log(ex, "进行 Mod 检查时出错", LogLevel.Feedback)
  184. End Try
  185. End Sub
  186. #End If
  187. ''' <summary>
  188. ''' 全选。
  189. ''' </summary>
  190. Private Sub BtnManageSelectAll_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnManageSelectAll.Click
  191. ChangeAllSelected(True)
  192. End Sub
  193. #End Region
  194. #Region "选择"
  195. '选择的 Mod 的路径(不含 .disabled)
  196. Public SelectedMods As New List(Of String)
  197. '单项切换选择状态
  198. Public Sub CheckChanged(sender As MyListItem, e As RouteEventArgs)
  199. If AniControlEnabled <> 0 Then Return
  200. '更新选择了的内容
  201. Dim SelectedKey As String = CType(sender.Tag, McMod).RawFileName
  202. If sender.Checked Then
  203. If Not SelectedMods.Contains(SelectedKey) Then SelectedMods.Add(SelectedKey)
  204. Else
  205. SelectedMods.Remove(SelectedKey)
  206. End If
  207. '更新下边栏 UI
  208. RefreshBottomBar()
  209. End Sub
  210. '改变下边栏状态
  211. Private ShownCount As Integer = 0
  212. Private Sub RefreshBottomBar()
  213. '计数
  214. Dim NewCount As Integer = SelectedMods.Count
  215. Dim Selected = NewCount > 0
  216. If Selected Then LabSelect.Text = $"已选择 {NewCount} 个文件" '取消所有选择时不更新数字
  217. '按钮可用性
  218. If Selected Then
  219. Dim HasEnabled As Boolean = False
  220. Dim HasDisabled As Boolean = False
  221. For Each ModEntity In McModLoader.Output
  222. If SelectedMods.Contains(ModEntity.RawFileName) Then
  223. If ModEntity.State = McMod.McModState.Fine Then
  224. HasEnabled = True
  225. ElseIf ModEntity.State = McMod.McModState.Disabled Then
  226. HasDisabled = True
  227. End If
  228. End If
  229. Next
  230. BtnSelectDisable.IsEnabled = HasEnabled
  231. BtnSelectEnable.IsEnabled = HasDisabled
  232. End If
  233. '更新显示状态
  234. CardSelect.IsHitTestVisible = Selected
  235. If AniControlEnabled = 0 Then
  236. If Selected Then
  237. '仅在数量增加时播放出现/跳跃动画
  238. If ShownCount >= NewCount Then
  239. ShownCount = NewCount
  240. Return
  241. Else
  242. ShownCount = NewCount
  243. End If
  244. '出现/跳跃动画
  245. CardSelect.Visibility = Visibility.Visible
  246. AniStart({
  247. AaOpacity(CardSelect, 1 - CardSelect.Opacity, 60),
  248. AaTranslateY(CardSelect, -27 - TransSelect.Y, 120, Ease:=New AniEaseOutFluent(AniEasePower.Weak)),
  249. AaTranslateY(CardSelect, 3, 150, 120, Ease:=New AniEaseInoutFluent(AniEasePower.Weak)),
  250. AaTranslateY(CardSelect, -1, 90, 270, Ease:=New AniEaseInoutFluent(AniEasePower.Weak))
  251. }, "Mod Sidebar")
  252. Else
  253. '不重复播放隐藏动画
  254. If ShownCount = 0 Then Return
  255. ShownCount = 0
  256. '隐藏动画
  257. AniStart({
  258. AaOpacity(CardSelect, -CardSelect.Opacity, 90),
  259. AaTranslateY(CardSelect, -10 - TransSelect.Y, 90, Ease:=New AniEaseInFluent(AniEasePower.Weak)),
  260. AaCode(Sub() CardSelect.Visibility = Visibility.Collapsed, After:=True)
  261. }, "Mod Sidebar")
  262. End If
  263. Else
  264. AniStop("Mod Sidebar")
  265. ShownCount = NewCount
  266. If Selected Then
  267. CardSelect.Visibility = Visibility.Visible
  268. CardSelect.Opacity = 1
  269. TransSelect.Y = -25
  270. Else
  271. CardSelect.Visibility = Visibility.Collapsed
  272. CardSelect.Opacity = 0
  273. TransSelect.Y = -10
  274. End If
  275. End If
  276. End Sub
  277. '切换所有项的选择状态
  278. Private Sub ChangeAllSelected(Value As Boolean)
  279. AniControlEnabled += 1
  280. SelectedMods.Clear()
  281. If IsSearching Then
  282. '搜索中
  283. For Each Item As MyListItem In PanSearchList.Children
  284. Item.Checked = Value
  285. If Value Then SelectedMods.Add(CType(Item.Tag, McMod).RawFileName)
  286. Next
  287. If Not Value Then '只取消选择
  288. If PanItems IsNot Nothing Then
  289. For Each Item As MyListItem In PanItems.Children
  290. Item.Checked = Value
  291. Next
  292. End If
  293. End If
  294. Else
  295. '非搜索中
  296. If PanItems IsNot Nothing Then
  297. For Each Item As MyListItem In PanItems.Children
  298. Item.Checked = Value
  299. If Value Then SelectedMods.Add(CType(Item.Tag, McMod).RawFileName)
  300. Next
  301. End If
  302. End If
  303. AniControlEnabled -= 1
  304. '更新下边栏 UI
  305. RefreshBottomBar()
  306. End Sub
  307. Private Sub UnselectedAllWithAnimation() Handles Load.StateChanged, Me.PageExit
  308. Dim CacheAniControlEnabled = AniControlEnabled
  309. AniControlEnabled = 0
  310. ChangeAllSelected(False)
  311. AniControlEnabled += CacheAniControlEnabled
  312. End Sub
  313. Private Sub PageVersionMod_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
  314. If My.Computer.Keyboard.CtrlKeyDown AndAlso e.Key = Key.A Then ChangeAllSelected(True)
  315. End Sub
  316. #End Region
  317. #Region "下边栏"
  318. '启用 / 禁用
  319. Private Sub BtnSelectEorD_Click(sender As MyIconTextButton, e As RouteEventArgs) Handles BtnSelectEnable.Click, BtnSelectDisable.Click
  320. Dim IsSuccessful As Boolean = True
  321. Dim IsDisable As Boolean = sender.Equals(BtnSelectDisable)
  322. For Each ModEntity In McModLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawFileName)).ToList()
  323. Dim NewPath As String = Nothing
  324. If ModEntity.State = McMod.McModState.Fine And IsDisable Then
  325. '禁用
  326. NewPath = ModEntity.Path & ".disabled"
  327. ElseIf ModEntity.State = McMod.McModState.Disabled AndAlso Not IsDisable Then
  328. '启用
  329. NewPath = ModEntity.Path.Substring(0, ModEntity.Path.Count - ".disabled".Count)
  330. Else
  331. Continue For
  332. End If
  333. '重命名
  334. Try
  335. If File.Exists(NewPath) AndAlso Not File.Exists(ModEntity.Path) Then Continue For '因为未知原因 Mod 的状态已经切换完了
  336. File.Delete(NewPath)
  337. FileSystem.Rename(ModEntity.Path, NewPath)
  338. Catch ex As FileNotFoundException
  339. Log(ex, $"未找到需要重命名的 Mod({If(ModEntity.Path, "null")})", LogLevel.Feedback)
  340. RefreshList(True)
  341. Return
  342. Catch ex As Exception
  343. Log(ex, $"批量重命名 Mod 失败({If(ModEntity.Path, "null")})")
  344. IsSuccessful = False
  345. End Try
  346. '更改 Loader 和 UI 中的列表
  347. Dim NewModEntity As New McMod(NewPath)
  348. Dim IndexOfLoader As Integer = McModLoader.Output.IndexOf(ModEntity)
  349. McModLoader.Output.RemoveAt(IndexOfLoader)
  350. McModLoader.Output.Insert(IndexOfLoader, NewModEntity)
  351. Dim Parent As StackPanel = If(IsSearching, PanSearchList, PanItems)
  352. Dim IndexOfUi As Integer = Parent.Children.IndexOf(Parent.Children.OfType(Of MyListItem).First(Function(i) CType(i.Tag, McMod) Is ModEntity))
  353. Parent.Children.RemoveAt(IndexOfUi)
  354. Parent.Children.Insert(IndexOfUi, McModListItem(NewModEntity))
  355. Next
  356. If Not IsSearching Then
  357. '改变禁用数量的显示
  358. CType(PanItems.Parent, MyCard).Title = McModGetTitle(McModLoader.Output)
  359. '更新加载器状态
  360. LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.UpdateOnly)
  361. End If
  362. If Not IsSuccessful Then
  363. Hint("由于文件被占用,部分 Mod 的状态切换失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical)
  364. RefreshList(True)
  365. Else
  366. ChangeAllSelected(False)
  367. End If
  368. End Sub
  369. '删除
  370. Private Sub BtnSelectDelete_Click() Handles BtnSelectDelete.Click
  371. Dim IsSuccessful As Boolean = True
  372. Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown
  373. Dim DeleteList As List(Of McMod) = McModLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawFileName)).ToList()
  374. For Each ModEntity In DeleteList
  375. '删除
  376. Try
  377. If IsShiftPressed Then
  378. File.Delete(ModEntity.Path)
  379. Else
  380. My.Computer.FileSystem.DeleteFile(ModEntity.Path, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin)
  381. End If
  382. Catch ex As OperationCanceledException
  383. Log(ex, "批量删除 Mod 被主动取消")
  384. RefreshList(True)
  385. Return
  386. Catch ex As Exception
  387. Log(ex, $"批量删除 Mod 失败({ModEntity.Path})", LogLevel.Msgbox)
  388. IsSuccessful = False
  389. End Try
  390. '更改 Loader 和 UI 中的列表
  391. McModLoader.Output.Remove(ModEntity)
  392. Dim Parent As StackPanel = If(IsSearching, PanSearchList, PanItems)
  393. Dim IndexOfUi As Integer = Parent.Children.IndexOf(Parent.Children.OfType(Of MyListItem).First(Function(i) CType(i.Tag, McMod) Is ModEntity))
  394. Parent.Children.RemoveAt(IndexOfUi)
  395. Next
  396. If Not IsSearching Then
  397. '改变禁用数量的显示
  398. CType(PanItems.Parent, MyCard).Title = McModGetTitle(McModLoader.Output)
  399. '更新加载器状态
  400. LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.UpdateOnly)
  401. End If
  402. If Not IsSuccessful Then
  403. Hint("由于文件被占用,部分 Mod 删除失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical)
  404. RefreshList(True)
  405. ElseIf If(IsSearching, PanSearchList, PanItems).Children.Count = 0 Then
  406. RefreshList(True)
  407. Else
  408. ChangeAllSelected(False)
  409. End If
  410. If IsSuccessful Then
  411. If IsShiftPressed Then
  412. Hint($"已彻底删除 {DeleteList.Count} 个文件!", HintType.Finish)
  413. Else
  414. Hint($"已将 {DeleteList.Count} 个文件删除到回收站!", HintType.Finish)
  415. End If
  416. End If
  417. End Sub
  418. '取消选择
  419. Private Sub BtnSelectCancel_Click() Handles BtnSelectCancel.Click
  420. ChangeAllSelected(False)
  421. End Sub
  422. #End Region
  423. #Region "单个 Mod 项"
  424. '点击 Logo:切换可用状态 / 显示错误原因
  425. Public Sub ItemLogo_Click(sender As MyListItem, e As EventArgs)
  426. Try
  427. Dim ModEntity As McMod = sender.Tag
  428. Dim NewPath As String = Nothing
  429. Select Case ModEntity.State
  430. Case McMod.McModState.Fine
  431. '前置检测警告
  432. If ModEntity.IsPresetMod Then
  433. If MyMsgBox("该 Mod 可能为其他 Mod 的前置,如果禁用可能导致其他 Mod 无法使用。" & vbCrLf & "你确定要继续禁用吗?", "警告", "禁用", "取消") = 2 Then Exit Sub
  434. End If
  435. NewPath = ModEntity.Path & ".disabled"
  436. Case McMod.McModState.Disabled
  437. NewPath = ModEntity.Path.Substring(0, ModEntity.Path.Count - ".disabled".Count)
  438. Case McMod.McModState.Unavaliable
  439. MyMsgBox("无法读取此 Mod 的信息。" & vbCrLf & vbCrLf & "详细的错误信息:" & GetExceptionDetail(ModEntity.FileUnavailableReason), "Mod 读取失败")
  440. Exit Sub
  441. End Select
  442. '重命名
  443. Dim NewModEntity As New McMod(NewPath)
  444. Try
  445. File.Delete(NewPath)
  446. FileSystem.Rename(ModEntity.Path, NewPath)
  447. Catch ex As FileNotFoundException
  448. Log(ex, "未找到理应存在的 Mod 文件(" & ModEntity.Path & ")")
  449. FileSystem.Rename(NewPath, ModEntity.Path)
  450. NewModEntity = New McMod(ModEntity.Path)
  451. End Try
  452. '更改 Loader 中的列表
  453. Dim IndexOfLoader As Integer = McModLoader.Output.IndexOf(ModEntity)
  454. McModLoader.Output.RemoveAt(IndexOfLoader)
  455. McModLoader.Output.Insert(IndexOfLoader, NewModEntity)
  456. '更改 UI 中的列表
  457. Dim Parent As StackPanel = sender.Parent
  458. Dim IndexOfUi As Integer = Parent.Children.IndexOf(sender)
  459. Parent.Children.RemoveAt(IndexOfUi)
  460. Parent.Children.Insert(IndexOfUi, McModListItem(NewModEntity))
  461. '仅在非搜索页面才执行,以确保搜索页内外显示一致
  462. If Not IsSearching Then
  463. '改变禁用数量的显示
  464. CType(Parent.Parent, MyCard).Title = McModGetTitle(McModLoader.Output)
  465. '更新加载器状态
  466. LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.UpdateOnly)
  467. End If
  468. RefreshBottomBar()
  469. Catch ex As Exception
  470. Log(ex, "单个状态改变中重命名 Mod 失败")
  471. Hint("切换 Mod 状态失败,请尝试关闭正在运行的游戏后再试!")
  472. LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.UpdateOnly)
  473. End Try
  474. End Sub
  475. '删除
  476. Public Sub Delete_Click(sender As MyIconButton, e As EventArgs)
  477. Try
  478. Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown
  479. Dim ListItem As MyListItem = sender.Tag
  480. Dim ModEntity As McMod = ListItem.Tag
  481. '前置检测警告
  482. If ModEntity.IsPresetMod Then
  483. If MyMsgBox("该 Mod 可能为其他 Mod 的前置,如果删除可能导致其他 Mod 无法使用。" & vbCrLf & "你确定要继续删除吗?", "警告", "删除", "取消", IsWarn:=True) = 2 Then Exit Sub
  484. End If
  485. '删除
  486. If File.Exists(ModEntity.Path) Then
  487. If IsShiftPressed Then
  488. File.Delete(ModEntity.Path)
  489. Else
  490. My.Computer.FileSystem.DeleteFile(ModEntity.Path, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin)
  491. End If
  492. Else
  493. '在不明原因下删除的文件可能还在列表里,如果玩家又点了一次就会出错,总之加个判断也不碍事
  494. Log("[System] 需要删除的 Mod 文件不存在(" & ModEntity.Path & ")", LogLevel.Hint)
  495. Exit Sub
  496. End If
  497. '更改 Loader 中的列表
  498. McModLoader.Output.Remove(ModEntity)
  499. '更改 UI 中的列表
  500. Dim Parent As StackPanel = ListItem.Parent
  501. Parent.Children.Remove(ListItem)
  502. If Parent.Children.Count = 0 Then
  503. RefreshList(True)
  504. Else
  505. CType(Parent.Parent, MyCard).Title = McModGetTitle(McModLoader.Output)
  506. End If
  507. '显示提示
  508. If IsShiftPressed Then
  509. Hint("已删除 " & ModEntity.FileName & "!", HintType.Finish)
  510. Else
  511. Hint("已将 " & ModEntity.FileName & " 删除到回收站!", HintType.Finish)
  512. End If
  513. LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.UpdateOnly)
  514. Catch ex As OperationCanceledException
  515. Log(ex, "删除 Mod 被主动取消")
  516. Catch ex As Exception
  517. Log(ex, "删除 Mod 失败", LogLevel.Feedback)
  518. End Try
  519. End Sub
  520. '详情
  521. Public Sub Info_Click(sender As Object, e As EventArgs)
  522. Try
  523. Dim ListItem As MyListItem
  524. ListItem = If(TypeOf sender Is MyIconButton, sender.Tag, sender)
  525. Dim ModEntity As McMod = ListItem.Tag
  526. '加载失败信息
  527. If ModEntity.State = McMod.McModState.Unavaliable Then
  528. MyMsgBox("无法读取此 Mod 的信息。" & vbCrLf & vbCrLf & "详细的错误信息:" & GetExceptionDetail(ModEntity.FileUnavailableReason), "Mod 读取失败")
  529. Return
  530. End If
  531. '获取信息
  532. Dim ContentLines As New List(Of String)
  533. If ModEntity.Description IsNot Nothing Then ContentLines.Add(ModEntity.Description & vbCrLf)
  534. If ModEntity.Authors IsNot Nothing Then ContentLines.Add("作者:" & ModEntity.Authors)
  535. ContentLines.Add("文件:" & ModEntity.FileName & "(" & GetString(New FileInfo(ModEntity.Path).Length) & ")")
  536. If ModEntity.Version IsNot Nothing Then ContentLines.Add("版本:" & ModEntity.Version)
  537. Dim DebugInfo As New List(Of String)
  538. If ModEntity.ModId IsNot Nothing Then
  539. DebugInfo.Add("Mod ID:" & ModEntity.ModId)
  540. End If
  541. If ModEntity.Dependencies.Count > 0 Then
  542. DebugInfo.Add("依赖于:")
  543. For Each Dep In ModEntity.Dependencies
  544. DebugInfo.Add(" - " & Dep.Key & If(Dep.Value Is Nothing, "", ",版本:" & Dep.Value))
  545. Next
  546. End If
  547. If DebugInfo.Count > 0 Then
  548. ContentLines.Add("")
  549. ContentLines.AddRange(DebugInfo)
  550. End If
  551. '获取用于搜索的 Mod 名称
  552. Dim ModOriginalName As String = ModEntity.Name.Replace(" ", "+")
  553. Dim ModSearchName As String = ModOriginalName.Substring(0, 1)
  554. For i = 1 To ModOriginalName.Count - 1
  555. Dim IsLastLower As Boolean = ModOriginalName(i - 1).ToString.ToLower.Equals(ModOriginalName(i - 1).ToString)
  556. Dim IsCurrentLower As Boolean = ModOriginalName(i).ToString.ToLower.Equals(ModOriginalName(i).ToString)
  557. If IsLastLower AndAlso Not IsCurrentLower Then
  558. '上一个字母为小写,这一个字母为大写
  559. ModSearchName += "+"
  560. End If
  561. ModSearchName += ModOriginalName(i)
  562. Next
  563. ModSearchName = ModSearchName.Replace("++", "+").Replace("pti+Fine", "ptiFine")
  564. '显示
  565. If ModEntity.Url Is Nothing Then
  566. If MyMsgBox(Join(ContentLines, vbCrLf), ModEntity.Name, "百科搜索", "返回") = 1 Then
  567. OpenWebsite("https://www.mcmod.cn/s?key=" & ModSearchName & "&site=all&filter=0")
  568. End If
  569. Else
  570. Select Case MyMsgBox(Join(ContentLines, vbCrLf), ModEntity.Name, "打开官网", "百科搜索", "返回")
  571. Case 1
  572. OpenWebsite(ModEntity.Url)
  573. Case 2
  574. OpenWebsite("https://www.mcmod.cn/s?key=" & ModSearchName & "&site=all&filter=0")
  575. End Select
  576. End If
  577. Catch ex As Exception
  578. Log(ex, "获取 Mod 详情失败", LogLevel.Feedback)
  579. End Try
  580. End Sub
  581. '打开文件所在的位置
  582. Public Sub Open_Click(sender As MyIconButton, e As EventArgs)
  583. Try
  584. Dim ListItem As MyListItem = sender.Tag
  585. Dim ModEntity As McMod = ListItem.Tag
  586. OpenExplorer("/select,""" & ModEntity.Path & """")
  587. Catch ex As Exception
  588. Log(ex, "打开 Mod 文件位置失败", LogLevel.Feedback)
  589. End Try
  590. End Sub
  591. #End Region
  592. Public ReadOnly Property IsSearching As Boolean
  593. Get
  594. Return Not String.IsNullOrWhiteSpace(SearchBox.Text)
  595. End Get
  596. End Property
  597. Public Sub SearchRun() Handles SearchBox.TextChanged
  598. ChangeAllSelected(False)
  599. If IsSearching Then
  600. '构造请求
  601. Dim QueryList As New List(Of SearchEntry(Of McMod))
  602. For Each Entry As McMod In McModLoader.Output
  603. QueryList.Add(New SearchEntry(Of McMod) With {
  604. .Item = Entry,
  605. .SearchSource = New List(Of KeyValuePair(Of String, Double)) From {
  606. New KeyValuePair(Of String, Double)(Entry.Name, 1),
  607. New KeyValuePair(Of String, Double)(Entry.FileName, 1),
  608. New KeyValuePair(Of String, Double)(If(Entry.Description, ""), 0.5)
  609. }
  610. })
  611. Next
  612. '进行搜索,构造列表
  613. Dim SearchResult = Search(QueryList, SearchBox.Text, MaxBlurCount:=5, MinBlurSimilarity:=0.35)
  614. PanSearchList.Children.Clear()
  615. If SearchResult.Count = 0 Then
  616. PanSearch.Title = "无搜索结果"
  617. PanSearchList.Visibility = Visibility.Collapsed
  618. Else
  619. PanSearch.Title = "搜索结果"
  620. For Each Result In SearchResult
  621. Dim Item = McModListItem(Result.Item)
  622. If ModeDebug Then Item.Info = If(Result.AbsoluteRight, "完全匹配,", "") & "相似度:" & Math.Round(Result.Similarity, 3) & "," & Item.Info
  623. PanSearchList.Children.Add(Item)
  624. Next
  625. PanSearchList.Visibility = Visibility.Visible
  626. End If
  627. '显示
  628. AniStart({
  629. AaOpacity(PanList, -PanList.Opacity, 100),
  630. AaCode(Sub()
  631. PanList.Visibility = Visibility.Collapsed
  632. PanSearch.Visibility = Visibility.Visible
  633. PanSearch.TriggerForceResize()
  634. End Sub,, True),
  635. AaOpacity(PanSearch, 1 - PanSearch.Opacity, 200, 60)
  636. }, "FrmVersionMod Search Switch", True)
  637. Else
  638. '隐藏
  639. LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.RunOnUpdated)
  640. AniStart({
  641. AaOpacity(PanSearch, -PanSearch.Opacity, 100),
  642. AaCode(Sub()
  643. PanSearch.Height = 0
  644. PanSearch.Visibility = Visibility.Collapsed
  645. PanList.Visibility = Visibility.Visible
  646. End Sub,, True),
  647. AaOpacity(PanList, 1 - PanList.Opacity, 150, 30)
  648. }, "FrmVersionMod Search Switch", True)
  649. End If
  650. End Sub
  651. End Class