PageVersionMod.xaml.vb 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  1. Public Class PageVersionMod
  2. Implements IRefreshable
  3. #Region "初始化"
  4. Private IsLoad As Boolean = False
  5. Public Sub PageOther_Loaded() Handles Me.Loaded
  6. If FrmMain.PageLast.Page <> FormMain.PageType.CompDetail Then PanBack.ScrollToHome()
  7. AniControlEnabled += 1
  8. SelectedMods.Clear()
  9. ReloadModList()
  10. ChangeAllSelected(False)
  11. AniControlEnabled -= 1
  12. '非重复加载部分
  13. If IsLoad Then Return
  14. IsLoad = True
  15. AddHandler FrmMain.KeyDown, AddressOf FrmMain_KeyDown
  16. '调整按钮边距(这玩意儿没法从 XAML 改)
  17. For Each Btn As MyRadioButton In PanFilter.Children
  18. Btn.LabText.Margin = New Thickness(-2, 0, 8, 0)
  19. Next
  20. BtnManageModUpdate.Text = If(Setup.Get("VersionCanModUpdate", Version:=PageVersionLeft.Version), "禁用 Mod 更新", "启用 Mod 更新")
  21. #If DEBUG Then
  22. BtnManageCheck.Visibility = Visibility.Visible
  23. #End If
  24. End Sub
  25. ''' <summary>
  26. ''' 刷新 Mod 列表。
  27. ''' </summary>
  28. Public Sub ReloadModList(Optional ForceReload As Boolean = False)
  29. If LoaderRun(If(ForceReload, LoaderFolderRunType.ForceRun, LoaderFolderRunType.RunOnUpdated)) Then
  30. Log("[System] 已刷新 Mod 列表")
  31. Filter = FilterType.All
  32. PanBack.ScrollToHome()
  33. SearchBox.Text = ""
  34. End If
  35. End Sub
  36. '强制刷新
  37. Private Sub RefreshSelf() Implements IRefreshable.Refresh
  38. Refresh()
  39. End Sub
  40. Public Shared Sub Refresh()
  41. '强制刷新
  42. Try
  43. CompProjectCache.Clear()
  44. CompFilesCache.Clear()
  45. File.Delete(PathTemp & "Cache\LocalMod.json")
  46. Log("[Mod] 由于点击刷新按钮,清理本地 Mod 信息缓存")
  47. Catch ex As Exception
  48. Log(ex, "强制刷新时清理本地 Mod 信息缓存失败")
  49. End Try
  50. If FrmVersionMod IsNot Nothing Then FrmVersionMod.ReloadModList(True) '无需 Else,还没加载刷个鬼的新
  51. FrmVersionLeft.ItemMod.Checked = True
  52. Hint("正在刷新……", Log:=False)
  53. End Sub
  54. Private Sub LoaderInit() Handles Me.Initialized
  55. PageLoaderInit(Load, PanLoad, PanAllBack, Nothing, McModLoader, AddressOf LoadUIFromLoaderOutput, AutoRun:=False)
  56. End Sub
  57. Private Sub Load_Click(sender As Object, e As MouseButtonEventArgs) Handles Load.Click
  58. If McModLoader.State = LoadState.Failed Then
  59. LoaderRun(LoaderFolderRunType.ForceRun)
  60. End If
  61. End Sub
  62. Public Function LoaderRun(Type As LoaderFolderRunType) As Boolean
  63. Return LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", Type)
  64. End Function
  65. #End Region
  66. #Region "UI 化"
  67. ''' <summary>
  68. ''' 已加载的 Mod UI 缓存,不确保按显示顺序排列。Key 为 Mod 的 RawFileName。
  69. ''' </summary>
  70. Public ModItems As New Dictionary(Of String, MyLocalModItem)
  71. ''' <summary>
  72. ''' 将加载器结果的 Mod 列表加载为 UI。
  73. ''' </summary>
  74. Private Sub LoadUIFromLoaderOutput()
  75. Try
  76. '判断应该显示哪一个页面
  77. If McModLoader.Output.Any() Then
  78. PanBack.Visibility = Visibility.Visible
  79. PanEmpty.Visibility = Visibility.Collapsed
  80. Else
  81. PanEmpty.Visibility = Visibility.Visible
  82. PanBack.Visibility = Visibility.Collapsed
  83. Return
  84. End If
  85. '修改缓存
  86. ModItems.Clear()
  87. For Each ModEntity As McMod In McModLoader.Output
  88. ModItems(ModEntity.RawFileName) = McModListItem(ModEntity)
  89. Next
  90. '显示结果
  91. Filter = FilterType.All
  92. SearchBox.Text = "" '这会触发结果刷新,所以需要在 ModItems 更新之后,详见 #3124 的视频
  93. RefreshUI()
  94. Catch ex As Exception
  95. Log(ex, "加载 Mod 列表 UI 失败", LogLevel.Feedback)
  96. End Try
  97. End Sub
  98. Private Function McModListItem(Entry As McMod) As MyLocalModItem
  99. AniControlEnabled += 1
  100. Dim NewItem As New MyLocalModItem With {.SnapsToDevicePixels = True, .Entry = Entry,
  101. .ButtonHandler = AddressOf McModContent, .Checked = SelectedMods.Contains(Entry.RawFileName)}
  102. AddHandler Entry.OnCompUpdate, AddressOf NewItem.Refresh
  103. NewItem.Refresh()
  104. AniControlEnabled -= 1
  105. Return NewItem
  106. End Function
  107. Private Sub McModContent(sender As MyLocalModItem, e As EventArgs)
  108. '点击事件
  109. AddHandler sender.Changed, AddressOf CheckChanged
  110. AddHandler sender.Click, Sub(ss As MyLocalModItem, ee As EventArgs) ss.Checked = Not ss.Checked
  111. '图标按钮
  112. Dim BtnOpen As New MyIconButton With {.LogoScale = 1.05, .Logo = Logo.IconButtonOpen, .Tag = sender}
  113. BtnOpen.ToolTip = "打开文件位置"
  114. ToolTipService.SetPlacement(BtnOpen, Primitives.PlacementMode.Center)
  115. ToolTipService.SetVerticalOffset(BtnOpen, 30)
  116. ToolTipService.SetHorizontalOffset(BtnOpen, 2)
  117. AddHandler BtnOpen.Click, AddressOf Open_Click
  118. Dim BtnCont As New MyIconButton With {.LogoScale = 1, .Logo = Logo.IconButtonInfo, .Tag = sender}
  119. BtnCont.ToolTip = "详情"
  120. ToolTipService.SetPlacement(BtnCont, Primitives.PlacementMode.Center)
  121. ToolTipService.SetVerticalOffset(BtnCont, 30)
  122. ToolTipService.SetHorizontalOffset(BtnCont, 2)
  123. AddHandler BtnCont.Click, AddressOf Info_Click
  124. AddHandler sender.MouseRightButtonUp, AddressOf Info_Click
  125. Dim BtnDelete As New MyIconButton With {.LogoScale = 1, .Logo = Logo.IconButtonDelete, .Tag = sender}
  126. BtnDelete.ToolTip = "删除"
  127. ToolTipService.SetPlacement(BtnDelete, Primitives.PlacementMode.Center)
  128. ToolTipService.SetVerticalOffset(BtnDelete, 30)
  129. ToolTipService.SetHorizontalOffset(BtnDelete, 2)
  130. AddHandler BtnDelete.Click, AddressOf Delete_Click
  131. Dim BtnED As New MyIconButton With {.LogoScale = 1, .Logo = If(sender.Entry.State = McMod.McModState.Fine, Logo.IconButtonStop, Logo.IconButtonCheck), .Tag = sender}
  132. BtnED.ToolTip = If(sender.Entry.State = McMod.McModState.Fine, "禁用", "启用")
  133. ToolTipService.SetPlacement(BtnED, Primitives.PlacementMode.Center)
  134. ToolTipService.SetVerticalOffset(BtnED, 30)
  135. ToolTipService.SetHorizontalOffset(BtnED, 2)
  136. AddHandler BtnED.Click, AddressOf ED_Click
  137. If sender.Entry.State = McMod.McModState.Unavailable Then
  138. sender.Buttons = {BtnCont, BtnOpen, BtnDelete}
  139. Else
  140. sender.Buttons = {BtnCont, BtnOpen, BtnED, BtnDelete}
  141. End If
  142. End Sub
  143. ''' <summary>
  144. ''' 刷新整个 UI。
  145. ''' </summary>
  146. Public Sub RefreshUI()
  147. If PanList Is Nothing Then Return
  148. Dim ShowingMods = If(IsSearching, SearchResult, If(McModLoader.Output, New List(Of McMod))).Where(Function(m) CanPassFilter(m)).ToList
  149. '重新列出列表
  150. AniControlEnabled += 1
  151. If ShowingMods.Any() Then
  152. PanList.Visibility = Visibility.Visible
  153. PanList.Children.Clear()
  154. For Each TargetMod In ShowingMods
  155. Dim Item As MyLocalModItem = ModItems(TargetMod.RawFileName)
  156. Item.Checked = SelectedMods.Contains(TargetMod.RawFileName) '更新选中状态
  157. PanList.Children.Add(Item)
  158. Next
  159. Else
  160. PanList.Visibility = Visibility.Collapsed
  161. End If
  162. AniControlEnabled -= 1
  163. SelectedMods = SelectedMods.Where(Function(m) ShowingMods.Any(Function(s) s.RawFileName = m)).ToList '取消选中已经不显示的 Mod
  164. RefreshBars()
  165. End Sub
  166. ''' <summary>
  167. ''' 刷新顶栏和底栏显示。
  168. ''' </summary>
  169. Public Sub RefreshBars()
  170. '-----------------
  171. ' 顶部栏
  172. '-----------------
  173. '计数
  174. Dim AnyCount As Integer = 0
  175. Dim EnabledCount As Integer = 0
  176. Dim DisabledCount As Integer = 0
  177. Dim UpdateCount As Integer = 0
  178. Dim UnavalialeCount As Integer = 0
  179. For Each ModItem In If(IsSearching, SearchResult, If(McModLoader.Output, New List(Of McMod)))
  180. AnyCount += 1
  181. If ModItem.CanUpdate Then UpdateCount += 1
  182. If ModItem.State.Equals(McMod.McModState.Fine) Then EnabledCount += 1
  183. If ModItem.State.Equals(McMod.McModState.Disabled) Then DisabledCount += 1
  184. If ModItem.State.Equals(McMod.McModState.Unavailable) Then UnavalialeCount += 1
  185. Next
  186. '显示
  187. BtnFilterAll.Text = If(IsSearching, "搜索结果", "全部") & $" ({AnyCount})"
  188. BtnFilterCanUpdate.Text = $"可更新 ({UpdateCount})"
  189. BtnFilterCanUpdate.Visibility = If(Filter = FilterType.CanUpdate OrElse UpdateCount > 0, Visibility.Visible, Visibility.Collapsed)
  190. BtnFilterEnabled.Text = $"启用 ({EnabledCount})"
  191. BtnFilterEnabled.Visibility = If(Filter = FilterType.Enabled OrElse (EnabledCount > 0 AndAlso EnabledCount < AnyCount), Visibility.Visible, Visibility.Collapsed)
  192. BtnFilterDisabled.Text = $"禁用 ({DisabledCount})"
  193. BtnFilterDisabled.Visibility = If(Filter = FilterType.Disabled OrElse DisabledCount > 0, Visibility.Visible, Visibility.Collapsed)
  194. BtnFilterError.Text = $"错误 ({UnavalialeCount})"
  195. BtnFilterError.Visibility = If(Filter = FilterType.Unavailable OrElse UnavalialeCount > 0, Visibility.Visible, Visibility.Collapsed)
  196. '-----------------
  197. ' 底部栏
  198. '-----------------
  199. '计数
  200. Dim NewCount As Integer = SelectedMods.Count
  201. Dim Selected = NewCount > 0
  202. If Selected Then LabSelect.Text = $"已选择 {NewCount} 个文件" '取消所有选择时不更新数字
  203. '按钮可用性
  204. If Selected Then
  205. Dim HasUpdate As Boolean = False
  206. Dim HasEnabled As Boolean = False
  207. Dim HasDisabled As Boolean = False
  208. For Each ModEntity In McModLoader.Output
  209. If SelectedMods.Contains(ModEntity.RawFileName) Then
  210. If ModEntity.CanUpdate Then HasUpdate = True
  211. If ModEntity.State = McMod.McModState.Fine Then
  212. HasEnabled = True
  213. ElseIf ModEntity.State = McMod.McModState.Disabled Then
  214. HasDisabled = True
  215. End If
  216. End If
  217. Next
  218. BtnSelectDisable.IsEnabled = HasEnabled
  219. BtnSelectEnable.IsEnabled = HasDisabled
  220. BtnSelectUpdate.IsEnabled = HasUpdate
  221. End If
  222. '更新显示状态
  223. If AniControlEnabled = 0 Then
  224. PanListBack.Margin = New Thickness(0, 0, 0, If(Selected, 95, 15))
  225. If Selected Then
  226. '仅在数量增加时播放出现/跳跃动画
  227. If BottomBarShownCount >= NewCount Then
  228. BottomBarShownCount = NewCount
  229. Return
  230. Else
  231. BottomBarShownCount = NewCount
  232. End If
  233. '出现/跳跃动画
  234. CardSelect.Visibility = Visibility.Visible
  235. AniStart({
  236. AaOpacity(CardSelect, 1 - CardSelect.Opacity, 60),
  237. AaTranslateY(CardSelect, -27 - TransSelect.Y, 120, Ease:=New AniEaseOutFluent(AniEasePower.Weak)),
  238. AaTranslateY(CardSelect, 3, 150, 120, Ease:=New AniEaseInoutFluent(AniEasePower.Weak)),
  239. AaTranslateY(CardSelect, -1, 90, 270, Ease:=New AniEaseInoutFluent(AniEasePower.Weak))
  240. }, "Mod Sidebar")
  241. Else
  242. '不重复播放隐藏动画
  243. If BottomBarShownCount = 0 Then Return
  244. BottomBarShownCount = 0
  245. '隐藏动画
  246. AniStart({
  247. AaOpacity(CardSelect, -CardSelect.Opacity, 90),
  248. AaTranslateY(CardSelect, -10 - TransSelect.Y, 90, Ease:=New AniEaseInFluent(AniEasePower.Weak)),
  249. AaCode(Sub() CardSelect.Visibility = Visibility.Collapsed, After:=True)
  250. }, "Mod Sidebar")
  251. End If
  252. Else
  253. AniStop("Mod Sidebar")
  254. BottomBarShownCount = NewCount
  255. If Selected Then
  256. CardSelect.Visibility = Visibility.Visible
  257. CardSelect.Opacity = 1
  258. TransSelect.Y = -25
  259. Else
  260. CardSelect.Visibility = Visibility.Collapsed
  261. CardSelect.Opacity = 0
  262. TransSelect.Y = -10
  263. End If
  264. End If
  265. End Sub
  266. Private BottomBarShownCount As Integer = 0
  267. #End Region
  268. #Region "管理"
  269. ''' <summary>
  270. ''' 打开 Mods 文件夹。
  271. ''' </summary>
  272. Private Sub BtnManageOpen_Click(sender As Object, e As EventArgs) Handles BtnManageOpen.Click, BtnHintOpen.Click
  273. Try
  274. Directory.CreateDirectory(PageVersionLeft.Version.PathIndie & "mods\")
  275. OpenExplorer(PageVersionLeft.Version.PathIndie & "mods\")
  276. Catch ex As Exception
  277. Log(ex, "打开 Mods 文件夹失败", LogLevel.Msgbox)
  278. End Try
  279. End Sub
  280. #If DEBUG Then
  281. ''' <summary>
  282. ''' 检查 Mod。
  283. ''' </summary>
  284. Private Sub BtnManageCheck_Click(sender As Object, e As EventArgs) Handles BtnManageCheck.Click
  285. Try
  286. Dim Result = McModCheck(PageVersionLeft.Version, McModLoader.Output)
  287. If Result.Any Then
  288. MyMsgBox(Join(Result, vbCrLf & vbCrLf), "Mod 检查结果")
  289. Else
  290. Hint("Mod 检查完成,未发现任何问题!", HintType.Finish)
  291. End If
  292. Catch ex As Exception
  293. Log(ex, "进行 Mod 检查时出错", LogLevel.Feedback)
  294. End Try
  295. End Sub
  296. #End If
  297. ''' <summary>
  298. ''' 全选。
  299. ''' </summary>
  300. Private Sub BtnManageSelectAll_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnManageSelectAll.Click
  301. ChangeAllSelected(SelectedMods.Count < PanList.Children.Count)
  302. End Sub
  303. ''' <summary>
  304. ''' 安装 Mod。
  305. ''' </summary>
  306. Private Sub BtnManageInstall_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnManageInstall.Click, BtnHintInstall.Click
  307. Dim FileList = SelectFiles("Mod 文件(*.jar;*.litemod;*.disabled;*.old)|*.jar;*.litemod;*.disabled;*.old", "选择要安装的 Mod")
  308. If Not FileList.Any Then Return
  309. InstallMods(FileList)
  310. End Sub
  311. ''' <summary>
  312. ''' 尝试安装 Mod。
  313. ''' 返回输入的文件是否为一个 Mod 文件,仅用于判断拖拽行为。
  314. ''' </summary>
  315. Public Shared Function InstallMods(FilePathList As IEnumerable(Of String)) As Boolean
  316. Dim Extension As String = FilePathList.First.AfterLast(".").ToLower
  317. '检查文件扩展名
  318. If Not {"jar", "litemod", "disabled", "old"}.Any(Function(t) t = Extension) Then Return False
  319. Log("[System] 文件为 jar/litemod 格式,尝试作为 Mod 安装")
  320. '检查回收站:回收站中的文件有错误的文件名
  321. If FilePathList.First.Contains(":\$RECYCLE.BIN\") Then
  322. Hint("请先将文件从回收站还原,再尝试安装!", HintType.Critical)
  323. Return True
  324. End If
  325. '获取并检查目标版本
  326. Dim TargetVersion As McVersion = McVersionCurrent
  327. If FrmMain.PageCurrent = FormMain.PageType.VersionSetup Then TargetVersion = PageVersionLeft.Version
  328. If FrmMain.PageCurrent = FormMain.PageType.VersionSelect OrElse TargetVersion Is Nothing OrElse Not TargetVersion.Modable Then
  329. '正在选择版本,或当前版本不能安装 Mod
  330. Hint("若要安装 Mod,请先选择一个可以安装 Mod 的版本!")
  331. ElseIf Not (FrmMain.PageCurrent = FormMain.PageType.VersionSetup AndAlso FrmMain.PageCurrentSub = FormMain.PageSubType.VersionMod) Then
  332. '未处于 Mod 管理页面
  333. If MyMsgBox($"是否要将这{If(FilePathList.Count = 1, "个", "些")}文件作为 Mod 安装到 {TargetVersion.Name}?", "Mod 安装确认", "确定", "取消") = 1 Then GoTo Install
  334. Else
  335. '处于 Mod 管理页面
  336. Install:
  337. Try
  338. For Each ModFile In FilePathList
  339. Dim NewFileName = GetFileNameFromPath(ModFile).Replace(".disabled", "").Replace(".old", "")
  340. If Not NewFileName.Contains(".") Then NewFileName += ".jar" '#4227
  341. CopyFile(ModFile, TargetVersion.PathIndie & "mods\" & NewFileName)
  342. Next
  343. If FilePathList.Count = 1 Then
  344. Hint($"已安装 {GetFileNameFromPath(FilePathList.First).Replace(".disabled", "").Replace(".old", "")}!", HintType.Finish)
  345. Else
  346. Hint($"已安装 {FilePathList.Count} 个 Mod!", HintType.Finish)
  347. End If
  348. '刷新列表
  349. If FrmMain.PageCurrent = FormMain.PageType.VersionSetup AndAlso FrmMain.PageCurrentSub = FormMain.PageSubType.VersionMod Then
  350. LoaderFolderRun(McModLoader, TargetVersion.PathIndie & "mods\", LoaderFolderRunType.ForceRun)
  351. End If
  352. Catch ex As Exception
  353. Log(ex, "复制 Mod 文件失败", LogLevel.Msgbox)
  354. End Try
  355. End If
  356. Return True
  357. End Function
  358. ''' <summary>
  359. ''' 下载 Mod。
  360. ''' </summary>
  361. Private Sub BtnManageDownload_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnManageDownload.Click, BtnHintDownload.Click
  362. PageComp.TargetVersion = PageVersionLeft.Version '将当前版本设置为筛选器
  363. FrmMain.PageChange(FormMain.PageType.Download, FormMain.PageSubType.DownloadMod)
  364. End Sub
  365. ''' <summary>
  366. ''' 禁用/启用 Mod 更新
  367. ''' </summary>
  368. Private Sub BtnManageModUpdate_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnManageModUpdate.Click
  369. Setup.Set("VersionCanModUpdate", Not Setup.Get("VersionCanModUpdate", Version:=PageVersionLeft.Version), Version:=PageVersionLeft.Version)
  370. BtnManageModUpdate.Text = If(Setup.Get("VersionCanModUpdate", Version:=PageVersionLeft.Version), "禁用 Mod 更新", "启用 Mod 更新")
  371. RefreshBars()
  372. End Sub
  373. #End Region
  374. #Region "选择"
  375. '选择的 Mod 的路径(不含 .disabled 和 .old)
  376. Public SelectedMods As New List(Of String)
  377. '单项切换选择状态
  378. Public Sub CheckChanged(sender As MyLocalModItem, e As RouteEventArgs)
  379. If AniControlEnabled <> 0 Then Return
  380. '更新选择了的内容
  381. Dim SelectedKey As String = sender.Entry.RawFileName
  382. If sender.Checked Then
  383. If Not SelectedMods.Contains(SelectedKey) Then SelectedMods.Add(SelectedKey)
  384. Else
  385. SelectedMods.Remove(SelectedKey)
  386. End If
  387. RefreshBars()
  388. End Sub
  389. '切换所有项的选择状态
  390. Private Sub ChangeAllSelected(Value As Boolean)
  391. AniControlEnabled += 1
  392. SelectedMods.Clear()
  393. For Each Item As MyLocalModItem In ModItems.Values
  394. '#4992,Mod 从过滤器看可能不应在列表中,但因为刚切换状态所以依然保留在列表中,所以应该从列表 UI 判断,而非从过滤器判断
  395. Dim ShouldSelected As Boolean = Value AndAlso PanList.Children.Contains(Item)
  396. Item.Checked = ShouldSelected
  397. If ShouldSelected Then SelectedMods.Add(Item.Entry.RawFileName)
  398. Next
  399. AniControlEnabled -= 1
  400. RefreshBars()
  401. End Sub
  402. Private Sub UnselectedAllWithAnimation() Handles Load.StateChanged, Me.PageExit
  403. Dim CacheAniControlEnabled = AniControlEnabled
  404. AniControlEnabled = 0
  405. ChangeAllSelected(False)
  406. AniControlEnabled += CacheAniControlEnabled
  407. End Sub
  408. Private Sub FrmMain_KeyDown(sender As Object, e As KeyEventArgs) '监听自己的事件的话进入页面后不点击右侧控件就没办法监听到事件 (#4311)
  409. If FrmMain.PageRight IsNot Me Then Return
  410. If My.Computer.Keyboard.CtrlKeyDown AndAlso e.Key = Key.A Then ChangeAllSelected(True)
  411. End Sub
  412. #End Region
  413. #Region "筛选"
  414. Private _Filter As FilterType = FilterType.All
  415. Public Property Filter As FilterType
  416. Get
  417. Return _Filter
  418. End Get
  419. Set(value As FilterType)
  420. If _Filter = value Then Return
  421. _Filter = value
  422. Select Case value
  423. Case FilterType.All
  424. BtnFilterAll.Checked = True
  425. Case FilterType.Enabled
  426. BtnFilterEnabled.Checked = True
  427. Case FilterType.Disabled
  428. BtnFilterDisabled.Checked = True
  429. Case FilterType.CanUpdate
  430. BtnFilterCanUpdate.Checked = True
  431. Case Else
  432. BtnFilterError.Checked = True
  433. End Select
  434. RefreshUI()
  435. End Set
  436. End Property
  437. Public Enum FilterType As Integer
  438. All = 0
  439. Enabled = 1
  440. Disabled = 2
  441. CanUpdate = 3
  442. Unavailable = 4
  443. End Enum
  444. ''' <summary>
  445. ''' 检查该 Mod 项是否符合当前筛选的类别。
  446. ''' </summary>
  447. Private Function CanPassFilter(CheckingMod As McMod) As Boolean
  448. Select Case Filter
  449. Case FilterType.All
  450. Return True
  451. Case FilterType.Enabled
  452. Return CheckingMod.State = McMod.McModState.Fine
  453. Case FilterType.Disabled
  454. Return CheckingMod.State = McMod.McModState.Disabled
  455. Case FilterType.CanUpdate
  456. Return CheckingMod.CanUpdate
  457. Case FilterType.Unavailable
  458. Return CheckingMod.State = McMod.McModState.Unavailable
  459. Case Else
  460. Return False
  461. End Select
  462. End Function
  463. '点击筛选项触发的改变
  464. Private Sub ChangeFilter(sender As MyRadioButton, raiseByMouse As Boolean) Handles BtnFilterAll.Check, BtnFilterCanUpdate.Check, BtnFilterDisabled.Check, BtnFilterEnabled.Check, BtnFilterError.Check
  465. Filter = sender.Tag
  466. RefreshUI()
  467. End Sub
  468. #End Region
  469. #Region "下边栏"
  470. '启用 / 禁用
  471. Private Sub BtnSelectED_Click(sender As MyIconTextButton, e As RouteEventArgs) Handles BtnSelectEnable.Click, BtnSelectDisable.Click
  472. EDMods(McModLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawFileName)),
  473. Not sender.Equals(BtnSelectDisable))
  474. ChangeAllSelected(False)
  475. End Sub
  476. Private Sub EDMods(ModList As IEnumerable(Of McMod), IsEnable As Boolean)
  477. Dim IsSuccessful As Boolean = True
  478. For Each ModE In ModList.ToList
  479. Dim ModEntity = ModE '仅用于去除迭代变量无法修改的限制
  480. Dim NewPath As String = Nothing
  481. If ModEntity.State = McMod.McModState.Fine AndAlso Not IsEnable Then
  482. '禁用
  483. NewPath = ModEntity.Path & If(File.Exists(ModEntity.Path & ".old"), ".old", ".disabled")
  484. ElseIf ModEntity.State = McMod.McModState.Disabled AndAlso IsEnable Then
  485. '启用
  486. NewPath = ModEntity.RawPath
  487. Else
  488. Continue For
  489. End If
  490. '重命名
  491. Try
  492. If File.Exists(NewPath) Then
  493. If File.Exists(ModEntity.Path) Then
  494. '同时存在两个名称的 Mod
  495. If GetFileMD5(ModEntity.Path) <> GetFileMD5(NewPath) Then
  496. MyMsgBox($"目前同时存在启用和禁用的两个 Mod 文件:{vbCrLf} - {NewPath}{vbCrLf} - {ModEntity.Path}{vbCrLf}{vbCrLf}注意,这两个文件的内容并不相同。{vbCrLf}在手动删除或重命名其中一个文件后,才能继续操作。", "存在文件冲突")
  497. Continue For
  498. End If
  499. Else
  500. '已经重命名过了
  501. Log("[Mod] Mod 的状态已被切换", LogLevel.Debug)
  502. Continue For
  503. End If
  504. End If
  505. File.Delete(NewPath)
  506. FileSystem.Rename(ModEntity.Path, NewPath)
  507. Catch ex As FileNotFoundException
  508. Log(ex, $"未找到需要重命名的 Mod({If(ModEntity.Path, "null")})", LogLevel.Feedback)
  509. ReloadModList(True)
  510. Return
  511. Catch ex As Exception
  512. Log(ex, $"重命名 Mod 失败({If(ModEntity.Path, "null")})")
  513. IsSuccessful = False
  514. End Try
  515. '更改 Loader 中的列表
  516. Dim NewModEntity As New McMod(NewPath)
  517. NewModEntity.FromJson(ModEntity.ToJson)
  518. If McModLoader.Output.Contains(ModEntity) Then
  519. Dim IndexOfLoader As Integer = McModLoader.Output.IndexOf(ModEntity)
  520. McModLoader.Output.RemoveAt(IndexOfLoader)
  521. McModLoader.Output.Insert(IndexOfLoader, NewModEntity)
  522. End If
  523. If SearchResult IsNot Nothing AndAlso SearchResult.Contains(ModEntity) Then '#4862
  524. Dim IndexOfResult As Integer = SearchResult.IndexOf(ModEntity)
  525. SearchResult.Remove(ModEntity)
  526. SearchResult.Insert(IndexOfResult, NewModEntity)
  527. End If
  528. '更改 UI 中的列表
  529. Dim NewItem As MyLocalModItem = McModListItem(NewModEntity)
  530. ModItems(ModEntity.RawFileName) = NewItem
  531. Dim IndexOfUi As Integer = PanList.Children.IndexOf(PanList.Children.OfType(Of MyLocalModItem).FirstOrDefault(Function(i) i.Entry Is ModEntity))
  532. If IndexOfUi = -1 Then Continue For '因为未知原因 Mod 的状态已经切换完了
  533. PanList.Children.RemoveAt(IndexOfUi)
  534. PanList.Children.Insert(IndexOfUi, NewItem)
  535. Next
  536. If IsSuccessful Then
  537. RefreshBars()
  538. Else
  539. Hint("由于文件被占用,Mod 的状态切换失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical)
  540. ReloadModList(True)
  541. End If
  542. LoaderRun(LoaderFolderRunType.UpdateOnly)
  543. End Sub
  544. '更新
  545. Private Sub BtnSelectUpdate_Click() Handles BtnSelectUpdate.Click
  546. Dim UpdateList As List(Of McMod) = McModLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawFileName) AndAlso m.CanUpdate).ToList()
  547. If Not UpdateList.Any() Then Return
  548. UpdateMods(UpdateList)
  549. ChangeAllSelected(False)
  550. End Sub
  551. ''' <summary>
  552. ''' 记录正在进行 Mod 更新的 mods 文件夹路径。
  553. ''' </summary>
  554. Public Shared UpdatingVersions As New List(Of String)
  555. Public Sub UpdateMods(ModList As IEnumerable(Of McMod))
  556. '更新前警告
  557. If Not Setup.Get("HintUpdateMod") OrElse ModList.Count >= 15 Then
  558. If MyMsgBox($"新版本 Mod 可能不兼容旧存档或者其他 Mod,这可能导致游戏崩溃,甚至永久损坏存档!{vbCrLf}如果你在游玩整合包,请千万不要自行更新 Mod!{vbCrLf}{vbCrLf}在更新前,请先备份存档,并检查 Mod 的更新日志。{vbCrLf}如果更新后出现问题,你也可以在回收站找回更新前的 Mod。", "Mod 更新警告", "我已了解风险,继续更新", "取消", IsWarn:=True) = 1 Then
  559. Setup.Set("HintUpdateMod", True)
  560. Else
  561. Return
  562. End If
  563. End If
  564. Try
  565. '构造下载信息
  566. ModList = ModList.ToList() '防止刷新影响迭代器
  567. Dim FileList As New List(Of NetFile)
  568. Dim FileCopyList As New Dictionary(Of String, String)
  569. For Each Entry As McMod In ModList
  570. Dim File As CompFile = Entry.UpdateFile
  571. If Not File.Available Then Continue For
  572. '确认更新后的文件名
  573. Dim CurrentReplaceName = Entry.CompFile.FileName.Replace(".jar", "").Replace(".old", "").Replace(".disabled", "")
  574. Dim NewestReplaceName = Entry.UpdateFile.FileName.Replace(".jar", "").Replace(".old", "").Replace(".disabled", "")
  575. Dim CurrentSegs = CurrentReplaceName.Split("-"c).ToList()
  576. Dim NewestSegs = NewestReplaceName.Split("-"c).ToList()
  577. Dim Shortened As Boolean = False
  578. Do While True '移除前导相同部分(不能移除所有相同项,这会导致例如 1.2-forge-2 和 1.3-forge-3 中间的 forge 被去掉,导致尝试替换 1.2-2)
  579. If Not CurrentSegs.Any() OrElse Not NewestSegs.Any() Then Exit Do
  580. If CurrentSegs.First <> NewestSegs.First Then Exit Do
  581. CurrentSegs.RemoveAt(0)
  582. NewestSegs.RemoveAt(0)
  583. Shortened = True
  584. Loop
  585. Do While True '移除后导相同部分
  586. If Not CurrentSegs.Any() OrElse Not NewestSegs.Any() Then Exit Do
  587. If CurrentSegs.Last <> NewestSegs.Last Then Exit Do
  588. CurrentSegs.RemoveAt(CurrentSegs.Count - 1)
  589. NewestSegs.RemoveAt(NewestSegs.Count - 1)
  590. Shortened = True
  591. Loop
  592. If Shortened AndAlso CurrentSegs.Any() AndAlso NewestSegs.Any() Then
  593. CurrentReplaceName = Join(CurrentSegs, "-")
  594. NewestReplaceName = Join(NewestSegs, "-")
  595. End If
  596. '添加到下载列表
  597. Dim TempAddress As String = PathTemp & "DownloadedMods\" & Entry.FileName.Replace(CurrentReplaceName, NewestReplaceName)
  598. Dim RealAddress As String = GetPathFromFullPath(Entry.Path) & Entry.FileName.Replace(CurrentReplaceName, NewestReplaceName)
  599. FileList.Add(File.ToNetFile(TempAddress))
  600. FileCopyList(TempAddress) = RealAddress
  601. Next
  602. '构造加载器
  603. Dim InstallLoaders As New List(Of LoaderBase)
  604. Dim FinishedFileNames As New List(Of String)
  605. InstallLoaders.Add(New LoaderDownload("下载新版 Mod 文件", FileList) With {.ProgressWeight = ModList.Count * 1.5}) '每个 Mod 需要 1.5s
  606. InstallLoaders.Add(New LoaderTask(Of Integer, Integer)("替换旧版 Mod 文件",
  607. Sub()
  608. Try
  609. For Each Entry As McMod In ModList
  610. If File.Exists(Entry.Path) Then
  611. My.Computer.FileSystem.DeleteFile(Entry.Path, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin)
  612. Else
  613. Log($"[Mod] 未找到更新前的 Mod 文件,跳过对它的删除:{Entry.Path}", LogLevel.Debug)
  614. End If
  615. Next
  616. For Each Entry As KeyValuePair(Of String, String) In FileCopyList
  617. If File.Exists(Entry.Value) Then
  618. My.Computer.FileSystem.DeleteFile(Entry.Value, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin)
  619. Log($"[Mod] 更新后的 Mod 文件已存在,将会把它放入回收站:{Entry.Value}", LogLevel.Debug)
  620. End If
  621. If Directory.Exists(GetPathFromFullPath(Entry.Value)) Then
  622. File.Move(Entry.Key, Entry.Value)
  623. FinishedFileNames.Add(GetFileNameFromPath(Entry.Value))
  624. Else
  625. Log($"[Mod] 更新后的目标文件夹已被删除:{Entry.Value}", LogLevel.Debug)
  626. End If
  627. Next
  628. Catch ex As OperationCanceledException
  629. Log(ex, "替换旧版 Mod 文件时被主动取消")
  630. End Try
  631. End Sub))
  632. '结束处理
  633. Dim Loader As New LoaderCombo(Of IEnumerable(Of McMod))("Mod 更新:" & PageVersionLeft.Version.Name, InstallLoaders)
  634. Dim PathMods As String = PageVersionLeft.Version.PathIndie & "mods\"
  635. Loader.OnStateChanged =
  636. Sub()
  637. '结果提示
  638. Select Case Loader.State
  639. Case LoadState.Finished
  640. Select Case FinishedFileNames.Count
  641. Case 0 '一般是由于 Mod 文件被占用,然后玩家主动取消
  642. Log($"[Mod] 没有 Mod 被成功更新")
  643. Case 1
  644. Hint($"已成功更新 {FinishedFileNames.Single}!", HintType.Finish)
  645. Case Else
  646. Hint($"已成功更新 {FinishedFileNames.Count} 个 Mod!", HintType.Finish)
  647. End Select
  648. Case LoadState.Failed
  649. Hint("Mod 更新失败:" & GetExceptionSummary(Loader.Error), HintType.Critical)
  650. Case LoadState.Aborted
  651. Hint("Mod 更新已中止!", HintType.Info)
  652. Case Else
  653. Return
  654. End Select
  655. Log($"[Mod] 已从正在进行 Mod 更新的文件夹列表移除:{PathMods}")
  656. UpdatingVersions.Remove(PathMods)
  657. '清理缓存
  658. RunInNewThread(
  659. Sub()
  660. Try
  661. For Each TempFile In FileCopyList.Keys
  662. If File.Exists(TempFile) Then File.Delete(TempFile)
  663. Next
  664. Catch ex As Exception
  665. Log(ex, "清理 Mod 更新缓存失败")
  666. End Try
  667. End Sub, "Clean Mod Update Cache", ThreadPriority.BelowNormal)
  668. End Sub
  669. '启动加载器
  670. Log($"[Mod] 开始更新 {ModList.Count} 个 Mod:{PathMods}")
  671. UpdatingVersions.Add(PathMods)
  672. Loader.Start()
  673. LoaderTaskbarAdd(Loader)
  674. FrmMain.BtnExtraDownload.ShowRefresh()
  675. FrmMain.BtnExtraDownload.Ribble()
  676. ReloadModList(True)
  677. Catch ex As Exception
  678. Log(ex, "初始化 Mod 更新失败")
  679. End Try
  680. End Sub
  681. '删除
  682. Private Sub BtnSelectDelete_Click() Handles BtnSelectDelete.Click
  683. DeleteMods(McModLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawFileName)))
  684. ChangeAllSelected(False)
  685. End Sub
  686. Private Sub DeleteMods(ModList As IEnumerable(Of McMod))
  687. Try
  688. Dim IsSuccessful As Boolean = True
  689. Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown
  690. '确认需要删除的文件
  691. ModList = ModList.SelectMany(
  692. Function(Target As McMod)
  693. If Target.State = McMod.McModState.Fine Then
  694. Return {Target.Path, Target.Path & If(File.Exists(Target.Path & ".old"), ".old", ".disabled")}
  695. Else
  696. Return {Target.Path, Target.RawPath}
  697. End If
  698. End Function).Distinct.Where(Function(m) File.Exists(m)).Select(Function(m) New McMod(m)).ToList()
  699. '实际删除文件
  700. For Each ModEntity In ModList
  701. '删除
  702. Try
  703. If IsShiftPressed Then
  704. File.Delete(ModEntity.Path)
  705. Else
  706. My.Computer.FileSystem.DeleteFile(ModEntity.Path, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin)
  707. End If
  708. Catch ex As OperationCanceledException
  709. Log(ex, "删除 Mod 被主动取消")
  710. ReloadModList(True)
  711. Return
  712. Catch ex As Exception
  713. Log(ex, $"删除 Mod 失败({ModEntity.Path})", LogLevel.Msgbox)
  714. IsSuccessful = False
  715. End Try
  716. '取消选中
  717. SelectedMods.Remove(ModEntity.RawFileName)
  718. '更改 Loader 和 UI 中的列表
  719. McModLoader.Output.Remove(ModEntity)
  720. SearchResult?.Remove(ModEntity)
  721. ModItems.Remove(ModEntity.RawFileName)
  722. Dim IndexOfUi As Integer = PanList.Children.IndexOf(PanList.Children.OfType(Of MyLocalModItem).FirstOrDefault(Function(i) i.Entry.Equals(ModEntity)))
  723. If IndexOfUi >= 0 Then PanList.Children.RemoveAt(IndexOfUi)
  724. Next
  725. RefreshBars()
  726. If Not IsSuccessful Then
  727. Hint("由于文件被占用,Mod 删除失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical)
  728. ReloadModList(True)
  729. ElseIf PanList.Children.Count = 0 Then
  730. ReloadModList(True) '删除了全部文件
  731. Else
  732. RefreshBars()
  733. End If
  734. '显示结果提示
  735. If Not IsSuccessful Then Return
  736. If IsShiftPressed Then
  737. If ModList.Count = 1 Then
  738. Hint($"已彻底删除 {ModList.Single.FileName}!", HintType.Finish)
  739. Else
  740. Hint($"已彻底删除 {ModList.Count} 个文件!", HintType.Finish)
  741. End If
  742. Else
  743. If ModList.Count = 1 Then
  744. Hint($"已将 {ModList.Single.FileName} 删除到回收站!", HintType.Finish)
  745. Else
  746. Hint($"已将 {ModList.Count} 个文件删除到回收站!", HintType.Finish)
  747. End If
  748. End If
  749. Catch ex As OperationCanceledException
  750. Log(ex, "删除 Mod 被主动取消")
  751. ReloadModList(True)
  752. Catch ex As Exception
  753. Log(ex, "删除 Mod 出现未知错误", LogLevel.Feedback)
  754. ReloadModList(True)
  755. End Try
  756. LoaderRun(LoaderFolderRunType.UpdateOnly)
  757. End Sub
  758. '取消选择
  759. Private Sub BtnSelectCancel_Click() Handles BtnSelectCancel.Click
  760. ChangeAllSelected(False)
  761. End Sub
  762. #End Region
  763. #Region "单个 Mod 项"
  764. '详情
  765. Public Sub Info_Click(sender As Object, e As EventArgs)
  766. Try
  767. Dim ModEntry As McMod = CType(If(TypeOf sender Is MyIconButton, sender.Tag, sender), MyLocalModItem).Entry
  768. '加载失败信息
  769. If ModEntry.State = McMod.McModState.Unavailable Then
  770. MyMsgBox("无法读取此 Mod 的信息。" & vbCrLf & vbCrLf & "详细的错误信息:" & GetExceptionDetail(ModEntry.FileUnavailableReason), "Mod 读取失败")
  771. Return
  772. End If
  773. If ModEntry.Comp IsNot Nothing Then
  774. '跳转到 Mod 下载页面
  775. FrmMain.PageChange(New FormMain.PageStackData With {.Page = FormMain.PageType.CompDetail,
  776. .Additional = {ModEntry.Comp, New List(Of String), PageVersionLeft.Version.Version.McName,
  777. If(PageVersionLeft.Version.Version.HasForge, CompModLoaderType.Forge,
  778. If(PageVersionLeft.Version.Version.HasNeoForge, CompModLoaderType.NeoForge,
  779. If(PageVersionLeft.Version.Version.HasFabric, CompModLoaderType.Fabric, CompModLoaderType.Any))),
  780. CompType.Mod}})
  781. Else
  782. '获取信息
  783. Dim ContentLines As New List(Of String)
  784. If ModEntry.Description IsNot Nothing Then ContentLines.Add(ModEntry.Description & vbCrLf)
  785. If ModEntry.Authors IsNot Nothing Then ContentLines.Add("作者:" & ModEntry.Authors)
  786. ContentLines.Add("文件:" & ModEntry.FileName & "(" & GetString(New FileInfo(ModEntry.Path).Length) & ")")
  787. If ModEntry.Version IsNot Nothing Then ContentLines.Add("版本:" & ModEntry.Version)
  788. Dim DebugInfo As New List(Of String)
  789. If ModEntry.ModId IsNot Nothing Then
  790. DebugInfo.Add("Mod ID:" & ModEntry.ModId)
  791. End If
  792. If ModEntry.Dependencies.Any Then
  793. DebugInfo.Add("依赖于:")
  794. For Each Dep In ModEntry.Dependencies
  795. DebugInfo.Add(" - " & Dep.Key & If(Dep.Value Is Nothing, "", ",版本:" & Dep.Value))
  796. Next
  797. End If
  798. If DebugInfo.Any Then
  799. ContentLines.Add("")
  800. ContentLines.AddRange(DebugInfo)
  801. End If
  802. '获取用于搜索的 Mod 名称
  803. Dim ModOriginalName As String = ModEntry.Name.Replace(" ", "+")
  804. Dim ModSearchName As String = ModOriginalName.Substring(0, 1)
  805. For i = 1 To ModOriginalName.Count - 1
  806. Dim IsLastLower As Boolean = ModOriginalName(i - 1).ToString.ToLower.Equals(ModOriginalName(i - 1).ToString)
  807. Dim IsCurrentLower As Boolean = ModOriginalName(i).ToString.ToLower.Equals(ModOriginalName(i).ToString)
  808. If IsLastLower AndAlso Not IsCurrentLower Then
  809. '上一个字母为小写,这一个字母为大写
  810. ModSearchName += "+"
  811. End If
  812. ModSearchName += ModOriginalName(i)
  813. Next
  814. ModSearchName = ModSearchName.Replace("++", "+").Replace("pti+Fine", "ptiFine")
  815. '显示
  816. If ModEntry.Url Is Nothing Then
  817. If MyMsgBox(Join(ContentLines, vbCrLf), ModEntry.Name, "百科搜索", "返回") = 1 Then
  818. OpenWebsite("https://www.mcmod.cn/s?key=" & ModSearchName & "&site=all&filter=0")
  819. End If
  820. Else
  821. Select Case MyMsgBox(Join(ContentLines, vbCrLf), ModEntry.Name, "打开官网", "百科搜索", "返回")
  822. Case 1
  823. OpenWebsite(ModEntry.Url)
  824. Case 2
  825. OpenWebsite("https://www.mcmod.cn/s?key=" & ModSearchName & "&site=all&filter=0")
  826. End Select
  827. End If
  828. End If
  829. Catch ex As Exception
  830. Log(ex, "获取 Mod 详情失败", LogLevel.Feedback)
  831. End Try
  832. End Sub
  833. '打开文件所在的位置
  834. Public Sub Open_Click(sender As MyIconButton, e As EventArgs)
  835. Try
  836. Dim ListItem As MyLocalModItem = sender.Tag
  837. OpenExplorer(ListItem.Entry.Path)
  838. Catch ex As Exception
  839. Log(ex, "打开 Mod 文件位置失败", LogLevel.Feedback)
  840. End Try
  841. End Sub
  842. '删除
  843. Public Sub Delete_Click(sender As MyIconButton, e As EventArgs)
  844. Dim ListItem As MyLocalModItem = sender.Tag
  845. DeleteMods({ListItem.Entry})
  846. End Sub
  847. '启用 / 禁用
  848. Public Sub ED_Click(sender As MyIconButton, e As EventArgs)
  849. Dim ListItem As MyLocalModItem = sender.Tag
  850. EDMods({ListItem.Entry}, ListItem.Entry.State = McMod.McModState.Disabled)
  851. End Sub
  852. #End Region
  853. #Region "搜索"
  854. Public ReadOnly Property IsSearching As Boolean
  855. Get
  856. Return Not String.IsNullOrWhiteSpace(SearchBox.Text)
  857. End Get
  858. End Property
  859. Private SearchResult As List(Of McMod)
  860. Public Sub SearchRun() Handles SearchBox.TextChanged
  861. If IsSearching Then
  862. '构造请求
  863. Dim QueryList As New List(Of SearchEntry(Of McMod))
  864. For Each Entry As McMod In McModLoader.Output
  865. Dim SearchSource As New List(Of KeyValuePair(Of String, Double))
  866. SearchSource.Add(New KeyValuePair(Of String, Double)(Entry.Name, 1))
  867. SearchSource.Add(New KeyValuePair(Of String, Double)(Entry.FileName, 1))
  868. If Entry.Version IsNot Nothing Then
  869. SearchSource.Add(New KeyValuePair(Of String, Double)(Entry.Version, 0.2))
  870. End If
  871. If Entry.Description IsNot Nothing AndAlso Entry.Description <> "" Then
  872. SearchSource.Add(New KeyValuePair(Of String, Double)(Entry.Description, 0.4))
  873. End If
  874. If Entry.Comp IsNot Nothing Then
  875. If Entry.Comp.RawName <> Entry.Name Then SearchSource.Add(New KeyValuePair(Of String, Double)(Entry.Comp.RawName, 1))
  876. If Entry.Comp.TranslatedName <> Entry.Comp.RawName Then SearchSource.Add(New KeyValuePair(Of String, Double)(Entry.Comp.TranslatedName, 1))
  877. If Entry.Comp.Description <> Entry.Description Then SearchSource.Add(New KeyValuePair(Of String, Double)(Entry.Comp.Description, 0.4))
  878. SearchSource.Add(New KeyValuePair(Of String, Double)(String.Join("", Entry.Comp.Tags), 0.2))
  879. End If
  880. QueryList.Add(New SearchEntry(Of McMod) With {.Item = Entry, .SearchSource = SearchSource})
  881. Next
  882. '进行搜索
  883. SearchResult = Search(QueryList, SearchBox.Text, MaxBlurCount:=6, MinBlurSimilarity:=0.35).Select(Function(r) r.Item).ToList
  884. End If
  885. RefreshUI()
  886. End Sub
  887. #End Region
  888. End Class