PageComp.xaml.vb 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. Imports System.Windows.Markup
  2. <ContentProperty("SearchTags")>
  3. Public Class PageComp
  4. #Region "属性"
  5. ''' <summary>
  6. ''' 用于 XAML 快速设置的 Tag 下拉框列表。
  7. ''' </summary>
  8. Public ReadOnly Property SearchTags As ItemCollection
  9. Get
  10. Return ComboSearchTag.Items
  11. End Get
  12. End Property
  13. ''' <summary>
  14. ''' 英文前后不含空格的可读资源类型名,例如 "Mod"、"整合包"。
  15. ''' </summary>
  16. Public Property TypeName As String
  17. Get
  18. Return _TypeName
  19. End Get
  20. Set(Value As String)
  21. If _TypeName = Value Then Return
  22. _TypeName = Value
  23. Loader.Name = $"社区资源获取:{Value}"
  24. End Set
  25. End Property
  26. Private _TypeName As String = ""
  27. ''' <summary>
  28. ''' 英文前后含一个空格的可读资源类型名,例如 " Mod "、"整合包"。
  29. ''' </summary>
  30. Public Property TypeNameSpaced As String
  31. Get
  32. Return _TypeNameSpaced
  33. End Get
  34. Set(Value As String)
  35. If _TypeNameSpaced = Value Then Return
  36. _TypeNameSpaced = Value
  37. PanAlways.Title = $"搜索{Value}"
  38. Load.Text = $"正在获取{Value}列表"
  39. End Set
  40. End Property
  41. Private _TypeNameSpaced As String = ""
  42. ''' <summary>
  43. ''' 该页面对应的资源类型。
  44. ''' </summary>
  45. Public Property PageType As CompType
  46. Get
  47. Return _Type
  48. End Get
  49. Set(Value As CompType)
  50. If _Type = Value Then Return
  51. _Type = Value
  52. BtnSearchInstallModPack.Visibility = If(Value = CompType.ModPack, Visibility.Visible, Visibility.Collapsed)
  53. End Set
  54. End Property
  55. Private _Type As CompType = -1
  56. #End Region
  57. #Region "加载"
  58. ''' <summary>
  59. ''' 在切换到页面时,应自动将筛选项设置为与该目标 MC 版本和加载器相同。
  60. ''' </summary>
  61. Public Shared TargetInstance As McInstance = Nothing
  62. ''' <summary>
  63. ''' 在切换到该页面时自动设置的搜索框内的内容。
  64. ''' </summary>
  65. Public Shared TargetName As String = Nothing
  66. '在点击 MyCompItem 时会获取 Loader 的输入,以使资源详情页面可以应用相同的筛选项
  67. Public Loader As New LoaderTask(Of CompProjectRequest, Integer)("社区资源获取:XXX", AddressOf CompProjectsGet, AddressOf LoaderInput) With {.ReloadTimeout = 60 * 1000}
  68. Private Sub PageCompControls_Inited(sender As Object, e As EventArgs) Handles Me.Loaded
  69. Static IsFirstLoaded As Boolean = False
  70. '不知道从 Initialized 改成 Loaded 会不会有问题,但用 Initialized 会导致初始的筛选器修改被覆盖回默认值
  71. If TargetInstance IsNot Nothing Then
  72. '设置目标
  73. ResetFilter() '重置筛选器
  74. TextSearchVersion.Text = TargetInstance.Version.VanillaName
  75. Dim GetTargetItemByName =
  76. Function(Name As String) As MyComboBoxItem
  77. For Each Item As MyComboBoxItem In ComboSearchLoader.Items
  78. If Item.Content = Name Then Return Item
  79. Next
  80. Return ComboSearchLoader.Items(0)
  81. End Function
  82. If TargetInstance.Version.HasForge Then
  83. ComboSearchLoader.SelectedItem = GetTargetItemByName("Forge")
  84. ElseIf TargetInstance.Version.HasFabric Then
  85. ComboSearchLoader.SelectedItem = GetTargetItemByName("Fabric")
  86. ElseIf TargetInstance.Version.HasNeoForge Then
  87. ComboSearchLoader.SelectedItem = GetTargetItemByName("NeoForge")
  88. End If
  89. TargetInstance = Nothing
  90. If TargetName IsNot Nothing Then
  91. TextSearchName.Text = TargetName
  92. TargetName = Nothing
  93. End If
  94. '如果已经完成请求,则重新开始
  95. If IsFirstLoaded Then StartNewSearch()
  96. ScrollToHome()
  97. End If
  98. '加载器初始化
  99. If IsFirstLoaded Then Return
  100. IsFirstLoaded = True
  101. CType(Parent, MyPageRight).PageLoaderInit(Load, PanLoad, PanContent, PanAlways, Loader, AddressOf Load_OnFinish, AddressOf LoaderInput)
  102. '将最高 Drop 加入筛选
  103. If AllDrops?.Any AndAlso AllDrops.First > 250 Then
  104. Dim HighestVersion As String = McVersion.DropToVersion(AllDrops.First)
  105. If CType(TextSearchVersion.Items(1), MyComboBoxItem).Content.ToString <> HighestVersion Then '0 是全部
  106. TextSearchVersion.Items.Insert(1, New MyComboBoxItem With {.Content = HighestVersion})
  107. End If
  108. End If
  109. End Sub
  110. Private Function LoaderInput() As CompProjectRequest
  111. Dim Request As New CompProjectRequest(PageType, Storage, (Page + 1) * PAGE_SIZE)
  112. Dim GameVersion As String = If(TextSearchVersion.Text = "全部 (也可自行输入)", Nothing,
  113. If(TextSearchVersion.Text.Contains(".") OrElse TextSearchVersion.Text.Contains("w"), TextSearchVersion.Text, Nothing))
  114. With Request
  115. .SearchText = TextSearchName.Text
  116. .GameVersion = GameVersion
  117. .Tag = ComboSearchTag.SelectedItem.Tag
  118. .ModLoader = If(PageType = CompType.Mod, Val(ComboSearchLoader.SelectedItem.Tag), CompModLoaderType.Any)
  119. .Source = CType(Val(ComboSearchSource.SelectedItem.Tag), CompSourceType)
  120. End With
  121. Return Request
  122. End Function
  123. #End Region
  124. Public Storage As New CompProjectStorage
  125. ''' <summary>
  126. ''' 每页展示的结果数量。
  127. ''' </summary>
  128. Public Const PAGE_SIZE = 40
  129. Public Page As Integer = 0
  130. '结果 UI 化
  131. Private Sub Load_OnFinish()
  132. Try
  133. Log($"[Comp] 开始可视化{TypeNameSpaced}列表,已储藏 {Storage.Results.Count} 个结果,当前在第 {Page + 1} 页")
  134. '列表项
  135. PanProjects.Children.Clear()
  136. Dim Index As Integer = Math.Min(Page * PAGE_SIZE, Storage.Results.Count - 1)
  137. For Each Result In Storage.Results.GetRange(Index, Math.Min(Storage.Results.Count - Index, PAGE_SIZE))
  138. PanProjects.Children.Add(Result.ToCompItem(
  139. ShowMcVersionDesc:=Loader.Input.GameVersion Is Nothing,
  140. ShowLoaderDesc:=Loader.Input.ModLoader = CompModLoaderType.Any AndAlso (PageType = CompType.Mod OrElse PageType = CompType.ModPack)))
  141. Next
  142. '页码
  143. CardPages.Visibility = If(Storage.Results.Count > 40 OrElse
  144. Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal,
  145. Visibility.Visible, Visibility.Collapsed)
  146. LabPage.Text = Page + 1
  147. BtnPageFirst.IsEnabled = Page > 1
  148. BtnPageFirst.Opacity = If(Page > 1, 1, 0.2)
  149. BtnPageLeft.IsEnabled = Page > 0
  150. BtnPageLeft.Opacity = If(Page > 0, 1, 0.2)
  151. Dim IsRightEnabled As Boolean = '由于 WPF 的未知 bug,读取到的 IsEnabled 可能是错误的值(#3319)
  152. Storage.Results.Count > PAGE_SIZE * (Page + 1) OrElse
  153. Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal
  154. BtnPageRight.IsEnabled = IsRightEnabled
  155. BtnPageRight.Opacity = If(IsRightEnabled, 1, 0.2)
  156. '错误信息
  157. If Storage.ErrorMessage Is Nothing Then
  158. HintError.Visibility = Visibility.Collapsed
  159. Else
  160. HintError.Visibility = Visibility.Visible
  161. HintError.Text = Storage.ErrorMessage
  162. End If
  163. '强制返回顶部
  164. ScrollToTop()
  165. Catch ex As Exception
  166. Log(ex, $"可视化{TypeNameSpaced}列表出错", LogLevel.Feedback)
  167. End Try
  168. End Sub
  169. '自动重试
  170. Private Sub Load_State(sender As Object, state As MyLoading.MyLoadingState, oldState As MyLoading.MyLoadingState) Handles Load.StateChanged
  171. Select Case Loader.State
  172. Case LoadState.Failed
  173. Dim ErrorMessage As String = ""
  174. If Loader.Error IsNot Nothing Then ErrorMessage = Loader.Error.Message
  175. If ErrorMessage.Contains("不是有效的 json 文件") Then
  176. Log($"[Download] 下载的{TypeNameSpaced}列表 json 文件损坏,已自动重试", LogLevel.Debug)
  177. CType(Parent, MyPageRight).PageLoaderRestart()
  178. End If
  179. End Select
  180. End Sub
  181. '切换页码
  182. Private Sub BtnPageFirst_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageFirst.Click
  183. ChangePage(0)
  184. End Sub
  185. Private Sub BtnPageLeft_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageLeft.Click
  186. ChangePage(Page - 1)
  187. End Sub
  188. Private Sub BtnPageRight_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageRight.Click
  189. ChangePage(Page + 1)
  190. End Sub
  191. Private Sub ChangePage(NewPage As Integer)
  192. CardPages.IsEnabled = False
  193. Page = NewPage
  194. FrmMain.BackToTop()
  195. Log($"[Download] {TypeName}:切换到第 {Page + 1} 页")
  196. RunInThread(
  197. Sub()
  198. Thread.Sleep(100) '等待向上滚的动画结束
  199. RunInUi(Sub() CardPages.IsEnabled = True)
  200. Loader.Start()
  201. End Sub)
  202. End Sub
  203. #Region "搜索"
  204. '搜索按钮
  205. Private Sub StartNewSearch() Handles BtnSearchRun.Click
  206. Page = 0
  207. If Loader.ShouldStart(LoaderInput()) Then Storage = New CompProjectStorage '避免连续搜索两次使得 CompProjectStorage 引用丢失(#1311)
  208. Loader.Start()
  209. End Sub
  210. Private Sub EnterTrigger(sender As Object, e As KeyEventArgs) Handles TextSearchName.KeyDown, TextSearchVersion.KeyDown
  211. If e.Key = Key.Enter Then StartNewSearch()
  212. End Sub
  213. '重置按钮
  214. Private Sub ResetFilter() Handles BtnSearchReset.Click
  215. TextSearchName.Text = ""
  216. TextSearchVersion.Text = "全部 (也可自行输入)"
  217. TextSearchVersion.SelectedIndex = 0
  218. ComboSearchSource.SelectedIndex = 0
  219. ComboSearchTag.SelectedIndex = 0
  220. ComboSearchLoader.SelectedIndex = 0
  221. Loader.LastFinishedTime = 0 '要求强制重新开始
  222. End Sub
  223. '版本选择
  224. '#3067:当下拉菜单展开时,程序会被 WPF 挂起,因而无法更新 Grid 布局,所以必须延迟到下拉菜单收起后才能更新
  225. Private Sub TextSearchVersion_TextChanged() Handles TextSearchVersion.TextChanged
  226. If Not TextSearchVersion.IsDropDownOpen Then UpdateSearchLoaderVisibility()
  227. End Sub
  228. Private Sub UpdateSearchLoaderVisibility() Handles TextSearchVersion.DropDownClosed
  229. If PageType = CompType.Mod AndAlso (TextSearchVersion.Text.Contains(".") OrElse TextSearchVersion.Text.Contains("w")) Then
  230. ComboSearchLoader.Visibility = Visibility.Visible
  231. Grid.SetColumnSpan(TextSearchVersion, 1)
  232. Else
  233. ComboSearchLoader.Visibility = Visibility.Collapsed
  234. Grid.SetColumnSpan(TextSearchVersion, 2)
  235. ComboSearchLoader.SelectedIndex = 0
  236. End If
  237. End Sub
  238. #End Region
  239. '安装已有整合包按钮
  240. Private Sub BtnSearchInstallModPack_Click(sender As Object, e As EventArgs) Handles BtnSearchInstallModPack.Click
  241. ModpackInstall()
  242. End Sub
  243. End Class