PageVersionMod.xaml.vb 44 KB

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