MyLocalModItem.xaml.vb 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. Imports System.Windows.Forms
  2. Public Class MyLocalModItem
  3. #Region "基础属性"
  4. Public Uuid As Integer = GetUuid()
  5. 'Logo
  6. Private _Logo As String = ""
  7. Public Property Logo As String
  8. Get
  9. Return _Logo
  10. End Get
  11. Set(value As String)
  12. If _Logo = value OrElse value Is Nothing Then Exit Property
  13. _Logo = value
  14. Dim FileAddress = PathTemp & "CompLogo\" & GetHash(_Logo) & ".png"
  15. Try
  16. If _Logo.StartsWithF("http", True) Then
  17. '网络图片
  18. If File.Exists(FileAddress) Then
  19. PathLogo.Source = New MyBitmap(FileAddress)
  20. ElseIf _Logo.EndsWithF(".webp", True) Then 'Modrinth 林业 Mod 使用了不支持的 WebP 格式 Logo
  21. Log($"[LocalModItem] 发现不支持的 WebP 格式图标,已更改为默认图标:{_Logo}")
  22. PathLogo.Source = New MyBitmap(PathImage & "Icons/NoIcon.png")
  23. Else
  24. PathLogo.Source = New MyBitmap(PathImage & "Icons/NoIcon.png")
  25. RunInNewThread(Sub() LogoLoader(FileAddress), "Comp Logo Loader " & Uuid & "#", ThreadPriority.BelowNormal)
  26. End If
  27. Else
  28. '位图
  29. PathLogo.Source = New MyBitmap(_Logo)
  30. End If
  31. Catch ex As IOException
  32. Log(ex, "加载本地 Mod 图标时读取失败(" & FileAddress & ")")
  33. Catch ex As ArgumentException
  34. '考虑缓存的图片本身可能有误
  35. Log(ex, "可视化本地 Mod 图标失败(" & FileAddress & ")")
  36. Try
  37. File.Delete(FileAddress)
  38. Log("[LocalModItem] 已清理损坏的本地 Mod 图标:" & FileAddress)
  39. Catch exx As Exception
  40. Log(exx, "清理损坏的本地 Mod 图标缓存失败(" & FileAddress & ")", LogLevel.Hint)
  41. End Try
  42. Catch ex As Exception
  43. Log(ex, "加载本地 Mod 图标失败(" & value & ")")
  44. End Try
  45. End Set
  46. End Property
  47. '后台加载 Logo
  48. Private Sub LogoLoader(LocalFileAddress As String)
  49. Dim Retried As Boolean = False
  50. Dim DownloadEnd As String = GetUuid()
  51. RetryStart:
  52. Try
  53. 'CurseForge 图片使用缩略图
  54. Dim Url As String = _Logo
  55. If Url.Contains("/256/256/") AndAlso GetPixelSize(1) <= 1.25 AndAlso Not Retried Then
  56. Url = Url.Replace("/256/256/", "/64/64/") '#3075:部分 Mod 不存在 64x64 图标,所以重试时不再缩小
  57. End If
  58. '下载图片
  59. NetDownload(Url, LocalFileAddress & DownloadEnd, True)
  60. Dim LoadError As Exception = Nothing
  61. RunInUiWait(
  62. Sub()
  63. Try
  64. '在地址更换时取消加载
  65. If LocalFileAddress <> $"{PathTemp}CompLogo\{GetHash(_Logo)}.png" Then Exit Sub
  66. '在完成正常加载后才保存缓存图片
  67. PathLogo.Source = New MyBitmap(LocalFileAddress & DownloadEnd)
  68. Catch ex As Exception
  69. Log(ex, "读取本地 Mod 图标失败(" & LocalFileAddress & ")")
  70. File.Delete(LocalFileAddress & DownloadEnd)
  71. LoadError = ex
  72. End Try
  73. End Sub)
  74. If LoadError IsNot Nothing Then Throw LoadError
  75. If File.Exists(LocalFileAddress) Then
  76. File.Delete(LocalFileAddress & DownloadEnd)
  77. Else
  78. FileIO.FileSystem.MoveFile(LocalFileAddress & DownloadEnd, LocalFileAddress)
  79. End If
  80. Catch ex As Exception
  81. If Not Retried Then
  82. Retried = True
  83. GoTo RetryStart
  84. Else
  85. Log(ex, $"下载本地 Mod 图标失败({_Logo})")
  86. RunInUi(Sub() PathLogo.Source = New MyBitmap(PathImage & "Icons/NoIcon.png"))
  87. End If
  88. End Try
  89. End Sub
  90. '标题
  91. Private _Title As String
  92. Public Property Title As String
  93. Get
  94. Return _Title
  95. End Get
  96. Set(value As String)
  97. Dim RawValue = value
  98. Select Case Entry.State
  99. Case McMod.McModState.Fine
  100. LabTitle.TextDecorations = Nothing
  101. Case McMod.McModState.Disabled
  102. LabTitle.TextDecorations = TextDecorations.Strikethrough
  103. Case McMod.McModState.Unavaliable
  104. LabTitle.TextDecorations = TextDecorations.Strikethrough
  105. value &= " [错误]"
  106. End Select
  107. If LabTitle.Text = value Then Exit Property
  108. LabTitle.Text = value
  109. _Title = RawValue
  110. End Set
  111. End Property
  112. '副标题
  113. Public Property SubTitle As String
  114. Get
  115. Return If(LabTitleRaw?.Text, "")
  116. End Get
  117. Set(value As String)
  118. If LabTitleRaw.Text = value Then Exit Property
  119. LabTitleRaw.Text = value
  120. LabTitleRaw.Visibility = If(value = "", Visibility.Collapsed, Visibility.Visible)
  121. End Set
  122. End Property
  123. '描述
  124. Public Property Description As String
  125. Get
  126. Return LabInfo.Text
  127. End Get
  128. Set(value As String)
  129. If LabInfo.Text = value Then Exit Property
  130. LabInfo.Text = value
  131. End Set
  132. End Property
  133. 'Tag
  134. Public WriteOnly Property Tags As List(Of String)
  135. Set(value As List(Of String))
  136. PanTags.Children.Clear()
  137. PanTags.Visibility = If(value.Any(), Visibility.Visible, Visibility.Collapsed)
  138. For Each TagText In value
  139. Dim NewTag = GetObjectFromXML(
  140. "<Border xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
  141. Background=""#11000000"" Padding=""3,1"" CornerRadius=""3"" Margin=""0,0,3,0""
  142. SnapsToDevicePixels=""True"" UseLayoutRounding=""False"">
  143. <TextBlock Text=""" & TagText & """ Foreground=""#868686"" FontSize=""11"" />
  144. </Border>")
  145. PanTags.Children.Add(NewTag)
  146. Next
  147. End Set
  148. End Property
  149. '相关联的 Mod
  150. Public Property Entry As McMod
  151. Get
  152. Return Tag
  153. End Get
  154. Set(value As McMod)
  155. Tag = value
  156. End Set
  157. End Property
  158. #End Region
  159. #Region "点击与勾选"
  160. '触发点击事件
  161. Public Event Click(sender As Object, e As MouseButtonEventArgs)
  162. Private Sub Button_MouseUp(sender As Object, e As MouseButtonEventArgs) Handles Me.PreviewMouseLeftButtonUp
  163. If IsMouseDown Then
  164. RaiseEvent Click(sender, e)
  165. If e.Handled Then Exit Sub
  166. Log("[Control] 按下本地 Mod 列表项:" & LabTitle.Text)
  167. End If
  168. End Sub
  169. '鼠标点击判定
  170. Private IsMouseDown As Boolean = False
  171. Private Sub Button_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles Me.PreviewMouseLeftButtonDown
  172. If Not IsMouseDirectlyOver Then Exit Sub
  173. IsMouseDown = True
  174. If ButtonStack IsNot Nothing Then ButtonStack.IsHitTestVisible = False
  175. End Sub
  176. Private Sub Button_MouseLeave(sender As Object, e As Object) Handles Me.MouseLeave, Me.PreviewMouseLeftButtonUp
  177. IsMouseDown = False
  178. If ButtonStack IsNot Nothing Then ButtonStack.IsHitTestVisible = True
  179. End Sub
  180. '滑动选中
  181. Private Shared SwipeStart As Integer, SwipeEnd As Integer
  182. Private Shared Swiping As Boolean = False
  183. Private Shared SwipToState As Boolean '被滑动到的目标应将 Checked 改为此值
  184. Private Sub Button_MouseSwipeStart(sender As Object, e As Object) Handles Me.MouseLeftButtonDown
  185. If Parent Is Nothing Then Exit Sub 'Mod 可能已被删除(#3824)
  186. '开始滑动
  187. Dim Index = CType(Parent, StackPanel).Children.IndexOf(Me)
  188. SwipeStart = Index
  189. SwipeEnd = Index
  190. Swiping = True
  191. SwipToState = Not Checked
  192. FrmVersionMod.CardSelect.IsHitTestVisible = False '暂时禁用下边栏
  193. End Sub
  194. Private Sub Button_MouseSwipe(sender As Object, e As Object) Handles Me.MouseEnter, Me.MouseLeave, Me.MouseLeftButtonUp
  195. If Parent Is Nothing Then Exit Sub 'Mod 可能已被删除(#3824)
  196. '结束滑动
  197. If Mouse.LeftButton <> MouseButtonState.Pressed OrElse Not Swiping Then
  198. Swiping = False
  199. FrmVersionMod.CardSelect.IsHitTestVisible = True
  200. Exit Sub
  201. End If
  202. '计算滑动范围
  203. Dim Index = CType(Parent, StackPanel).Children.IndexOf(Me)
  204. SwipeStart = Math.Min(SwipeStart, Index)
  205. SwipeEnd = Math.Max(SwipeEnd, Index)
  206. '勾选所有范围中的项
  207. If SwipeStart = SwipeEnd Then Exit Sub
  208. For i = SwipeStart To SwipeEnd
  209. Dim Item As MyLocalModItem = CType(Parent, StackPanel).Children(i)
  210. Item.InitLate(Item, e)
  211. Item.Checked = SwipToState
  212. Next
  213. End Sub
  214. '勾选状态
  215. Public Event Check(sender As Object, e As RouteEventArgs)
  216. Public Event Changed(sender As Object, e As RouteEventArgs)
  217. Private _Checked As Boolean = False
  218. Public Property Checked As Boolean
  219. Get
  220. Return _Checked
  221. End Get
  222. Set(value As Boolean)
  223. Try
  224. '触发属性值修改
  225. Dim RawValue = _Checked
  226. If value = _Checked Then Exit Property
  227. _Checked = value
  228. Dim ChangedEventArgs As New RouteEventArgs(False)
  229. If IsInitialized Then
  230. RaiseEvent Changed(Me, ChangedEventArgs)
  231. If ChangedEventArgs.Handled Then
  232. _Checked = RawValue
  233. Exit Property
  234. End If
  235. End If
  236. If value Then
  237. Dim CheckEventArgs As New RouteEventArgs(False)
  238. RaiseEvent Check(Me, CheckEventArgs)
  239. If CheckEventArgs.Handled Then Exit Property
  240. End If
  241. '更改动画
  242. If IsVisibleInForm() Then
  243. Dim Anim As New List(Of AniData)
  244. If Checked Then
  245. '由无变有
  246. Dim Delta = 32 - RectCheck.ActualHeight
  247. Anim.Add(AaHeight(RectCheck, Delta * 0.4, 200,, New AniEaseOutFluent(AniEasePower.Weak)))
  248. Anim.Add(AaHeight(RectCheck, Delta * 0.6, 300,, New AniEaseOutBack(AniEasePower.Weak)))
  249. Anim.Add(AaOpacity(RectCheck, 1 - RectCheck.Opacity, 30))
  250. RectCheck.VerticalAlignment = VerticalAlignment.Center
  251. RectCheck.Margin = New Thickness(-3, 0, 0, 0)
  252. Anim.Add(AaColor(LabTitle, TextBlock.ForegroundProperty, If(Entry.State = McMod.McModState.Fine, "ColorBrush2", "ColorBrush5"), 200))
  253. Else
  254. '由有变无
  255. Anim.Add(AaHeight(RectCheck, -RectCheck.ActualHeight, 120,, New AniEaseInFluent(AniEasePower.Weak)))
  256. Anim.Add(AaOpacity(RectCheck, -RectCheck.Opacity, 70, 40))
  257. RectCheck.VerticalAlignment = VerticalAlignment.Center
  258. Anim.Add(AaColor(LabTitle, TextBlock.ForegroundProperty, If(LabTitle.TextDecorations Is Nothing, "ColorBrush1", "ColorBrushGray4"), 120))
  259. End If
  260. AniStart(Anim, "MyLocalModItem Checked " & Uuid)
  261. Else
  262. '不在窗口上时直接设置
  263. RectCheck.VerticalAlignment = VerticalAlignment.Center
  264. RectCheck.Margin = New Thickness(-3, 0, 0, 0)
  265. If Checked Then
  266. RectCheck.Height = 32
  267. RectCheck.Opacity = 1
  268. LabTitle.SetResourceReference(TextBlock.ForegroundProperty, If(Entry.State = McMod.McModState.Fine, "ColorBrush2", "ColorBrush5"))
  269. Else
  270. RectCheck.Height = 0
  271. RectCheck.Opacity = 0
  272. LabTitle.SetResourceReference(TextBlock.ForegroundProperty, If(Entry.State = McMod.McModState.Fine, "ColorBrush1", "ColorBrushGray4"))
  273. End If
  274. AniStop("MyLocalModItem Checked " & Uuid)
  275. End If
  276. Catch ex As Exception
  277. Log(ex, "设置 Checked 失败")
  278. End Try
  279. End Set
  280. End Property
  281. #End Region
  282. #Region "后加载内容"
  283. '右下角状态指示图标
  284. Private ImgState As Image
  285. '指向背景
  286. Private _RectBack As Border = Nothing
  287. Public ReadOnly Property RectBack As Border
  288. Get
  289. If _RectBack Is Nothing Then
  290. Dim Rect As New Border With {
  291. .Name = "RectBack",
  292. .CornerRadius = New CornerRadius(3),
  293. .RenderTransform = New ScaleTransform(0.8, 0.8),
  294. .RenderTransformOrigin = New Point(0.5, 0.5),
  295. .BorderThickness = New Thickness(GetWPFSize(1)),
  296. .SnapsToDevicePixels = True,
  297. .IsHitTestVisible = False,
  298. .Opacity = 0
  299. }
  300. Rect.SetResourceReference(Border.BackgroundProperty, "ColorBrush7")
  301. Rect.SetResourceReference(Border.BorderBrushProperty, "ColorBrush6")
  302. SetColumnSpan(Rect, 999)
  303. SetRowSpan(Rect, 999)
  304. Children.Insert(0, Rect)
  305. _RectBack = Rect
  306. '<!--<Border x:Name = "RectBack" CornerRadius="3" RenderTransformOrigin="0.5,0.5" SnapsToDevicePixels="True"
  307. 'IsHitTestVisible = "False" Opacity="0" BorderThickness="1"
  308. 'Grid.ColumnSpan = "4" Background="{DynamicResource ColorBrush7}" BorderBrush="{DynamicResource ColorBrush6}"/>-->
  309. End If
  310. Return _RectBack
  311. End Get
  312. End Property
  313. '按钮
  314. Public ButtonHandler As Action(Of MyLocalModItem, EventArgs)
  315. Public ButtonStack As FrameworkElement
  316. Private _Buttons As IEnumerable(Of MyIconButton)
  317. Public Property Buttons As IEnumerable(Of MyIconButton)
  318. Get
  319. Return _Buttons
  320. End Get
  321. Set(value As IEnumerable(Of MyIconButton))
  322. _Buttons = value
  323. '移除原 Stack
  324. If ButtonStack IsNot Nothing Then
  325. Children.Remove(ButtonStack)
  326. ButtonStack = Nothing
  327. End If
  328. If Not value.Any() Then Exit Property
  329. '添加新 Stack
  330. ButtonStack = New StackPanel With {.Opacity = 0, .Margin = New Thickness(0, 0, 5, 0), .SnapsToDevicePixels = False, .Orientation = Orientation.Horizontal,
  331. .HorizontalAlignment = HorizontalAlignment.Right, .VerticalAlignment = VerticalAlignment.Center, .UseLayoutRounding = False}
  332. SetColumnSpan(ButtonStack, 10) : SetRowSpan(ButtonStack, 10)
  333. '构造按钮
  334. For Each Btn As MyIconButton In value
  335. If Btn.Height.Equals(Double.NaN) Then Btn.Height = 25
  336. If Btn.Width.Equals(Double.NaN) Then Btn.Width = 25
  337. CType(ButtonStack, StackPanel).Children.Add(Btn)
  338. Next
  339. Children.Add(ButtonStack)
  340. End Set
  341. End Property
  342. '勾选条
  343. Private _RectCheck As Border
  344. Public ReadOnly Property RectCheck As Border
  345. Get
  346. If _RectCheck Is Nothing Then
  347. _RectCheck = New Border With {.Width = 5, .Height = If(Checked, Double.NaN, 0), .CornerRadius = New CornerRadius(2, 2, 2, 2),
  348. .VerticalAlignment = If(Checked, VerticalAlignment.Stretch, VerticalAlignment.Center),
  349. .HorizontalAlignment = HorizontalAlignment.Left, .UseLayoutRounding = False, .SnapsToDevicePixels = False,
  350. .Margin = If(Checked, New Thickness(-3, 6, 0, 6), New Thickness(-3, 0, 0, 0))}
  351. _RectCheck.SetResourceReference(Border.BackgroundProperty, "ColorBrush3")
  352. SetRowSpan(_RectCheck, 10)
  353. Children.Add(_RectCheck)
  354. End If
  355. Return _RectCheck
  356. End Get
  357. End Property
  358. #End Region
  359. Public Sub Refresh()
  360. RunInUi(
  361. Sub()
  362. '更新
  363. If Entry.CanUpdate Then
  364. BtnUpdate.Visibility = Visibility.Visible
  365. Dim CurrentName = Entry.CompFile.FileName.Replace(".jar", "")
  366. Dim NewestName = Entry.UpdateFile.FileName.Replace(".jar", "")
  367. '简化名称对比
  368. Dim CurrentSegs = CurrentName.Split("-"c).ToList()
  369. Dim NewestSegs = NewestName.Split("-"c).ToList()
  370. Dim Shortened As Boolean = False
  371. For Each Seg In CurrentSegs.ToList()
  372. If Not NewestSegs.Contains(Seg) Then Continue For
  373. CurrentSegs.Remove(Seg)
  374. NewestSegs.Remove(Seg)
  375. Shortened = True
  376. Next
  377. If Shortened AndAlso CurrentSegs.Any() AndAlso NewestSegs.Any() Then
  378. CurrentName = Join(CurrentSegs, "-")
  379. NewestName = Join(NewestSegs, "-")
  380. Entry._Version = CurrentName '使用网络信息作为显示的版本号
  381. End If
  382. BtnUpdate.ToolTip = $"当前版本:{CurrentName} ({Entry.CompFile.ReleaseDate:yyyy/MM/dd HH:mm:ss}){vbCrLf}最新版本:{NewestName} ({Entry.UpdateFile.ReleaseDate:yyyy/MM/dd HH:mm:ss}){vbCrLf}点击以更新,右键查看更新日志。"
  383. Else
  384. BtnUpdate.Visibility = Visibility.Collapsed
  385. End If
  386. '标题
  387. If Entry.Comp Is Nothing Then
  388. Title = Entry.Name
  389. SubTitle = If(Entry.Version Is Nothing, "", " | " & Entry.Version)
  390. Else
  391. Dim Titles = Entry.Comp.GetControlTitle(False)
  392. Title = Titles.Key
  393. SubTitle = Titles.Value & If(Entry.Version Is Nothing, "", " | " & Entry.Version)
  394. End If
  395. If Checked Then
  396. LabTitle.SetResourceReference(TextBlock.ForegroundProperty, If(Entry.State = McMod.McModState.Fine, "ColorBrush2", "ColorBrush5"))
  397. Else
  398. LabTitle.SetResourceReference(TextBlock.ForegroundProperty, If(Entry.State = McMod.McModState.Fine, "ColorBrush1", "ColorBrushGray4"))
  399. End If
  400. '描述
  401. Dim NewDescription As String
  402. Select Case Entry.State
  403. Case McMod.McModState.Fine
  404. NewDescription = GetFileNameWithoutExtentionFromPath(Entry.Path)
  405. Case McMod.McModState.Disabled
  406. NewDescription = GetFileNameWithoutExtentionFromPath(Entry.Path.Replace(".disabled", "").Replace(".old", ""))
  407. Case Else 'McMod.McModState.Unavaliable
  408. NewDescription = GetFileNameFromPath(Entry.Path)
  409. End Select
  410. If Entry.Comp IsNot Nothing Then
  411. NewDescription += ": " & Entry.Comp.Description.Replace(vbCr, "").Replace(vbLf, "")
  412. ElseIf Entry.Description IsNot Nothing Then
  413. NewDescription += ": " & Entry.Description.Replace(vbCr, "").Replace(vbLf, "")
  414. ElseIf Not Entry.IsFileAvailable Then
  415. NewDescription += ": " & "存在错误,无法获取信息"
  416. End If
  417. Description = NewDescription
  418. '主 Logo
  419. Logo = If(Entry.Comp Is Nothing, PathImage & "Icons/NoIcon.png", Entry.Comp.GetControlLogo())
  420. '图标右下角的 Logo
  421. If Entry.State = McMod.McModState.Fine Then
  422. If ImgState IsNot Nothing Then
  423. Children.Remove(ImgState)
  424. ImgState = Nothing
  425. End If
  426. Else
  427. If ImgState Is Nothing Then
  428. ImgState = New Image With {
  429. .Width = 20, .Height = 20, .Margin = New Thickness(0, 0, -5, -3), .IsHitTestVisible = False,
  430. .HorizontalAlignment = HorizontalAlignment.Right, .VerticalAlignment = VerticalAlignment.Bottom
  431. }
  432. RenderOptions.SetBitmapScalingMode(ImgState, BitmapScalingMode.HighQuality)
  433. SetColumn(ImgState, 1) : SetRow(ImgState, 1) : SetRowSpan(ImgState, 2)
  434. Children.Add(ImgState)
  435. '<Image x:Name="ImgState" RenderOptions.BitmapScalingMode="HighQuality" Width="16" Height="16" Margin="0,0,-3,-1"
  436. ' Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" IsHitTestVisible="False"
  437. ' HorizontalAlignment="Right" VerticalAlignment="Bottom"
  438. ' Source="/Images/Icons/Unavaliable.png" />
  439. End If
  440. ImgState.Source = New MyBitmap(PathImage & $"Icons/{Entry.State}.png")
  441. End If
  442. '标签
  443. If Entry.Comp IsNot Nothing Then Tags = Entry.Comp.Tags
  444. End Sub)
  445. End Sub
  446. Public Sub RefreshColor(sender As Object, e As EventArgs) Handles Me.MouseEnter, Me.MouseLeave, Me.MouseLeftButtonDown, Me.MouseLeftButtonUp, Me.Changed
  447. InitLate(sender, e)
  448. '触发颜色动画
  449. Dim Time As Integer = If(IsMouseOver, 120, 180)
  450. Dim Ani As New List(Of AniData)
  451. 'ButtonStack
  452. If ButtonStack IsNot Nothing Then
  453. If IsMouseOver Then
  454. Ani.Add(AaOpacity(ButtonStack, 1 - ButtonStack.Opacity, Time * 0.7, Time * 0.3))
  455. Ani.Add(AaDouble(Sub(i) ColumnPaddingRight.Width = New GridLength(Math.Max(0, ColumnPaddingRight.Width.Value + i)),
  456. 5 + Buttons.Count * 25 - ColumnPaddingRight.Width.Value, Time * 0.3, Time * 0.7))
  457. Else
  458. Ani.Add(AaOpacity(ButtonStack, -ButtonStack.Opacity, Time * 0.4))
  459. Ani.Add(AaDouble(Sub(i) ColumnPaddingRight.Width = New GridLength(Math.Max(0, ColumnPaddingRight.Width.Value + i)),
  460. 4 - ColumnPaddingRight.Width.Value, Time * 0.4))
  461. End If
  462. End If
  463. 'RectBack
  464. If IsMouseOver OrElse Checked Then
  465. Ani.AddRange({
  466. AaColor(RectBack, Border.BackgroundProperty, If(IsMouseDown, "ColorBrush6", "ColorBrushBg1"), Time),
  467. AaOpacity(RectBack, 1 - RectBack.Opacity, Time,, New AniEaseOutFluent)
  468. })
  469. If IsMouseDown Then
  470. Ani.Add(AaScaleTransform(RectBack, 0.996 - CType(RectBack.RenderTransform, ScaleTransform).ScaleX, Time * 1.2,, New AniEaseOutFluent))
  471. Else
  472. Ani.Add(AaScaleTransform(RectBack, 1 - CType(RectBack.RenderTransform, ScaleTransform).ScaleX, Time * 1.2,, New AniEaseOutFluent))
  473. End If
  474. Else
  475. Ani.AddRange({
  476. AaOpacity(RectBack, -RectBack.Opacity, Time),
  477. AaScaleTransform(RectBack, 0.996 - CType(RectBack.RenderTransform, ScaleTransform).ScaleX, Time,, New AniEaseOutFluent),
  478. AaScaleTransform(RectBack, -0.196, 1,,, True)
  479. })
  480. End If
  481. AniStart(Ani, "LocalModItem Color " & Uuid)
  482. End Sub
  483. '触发虚拟化内容
  484. Private Sub InitLate(sender As Object, e As EventArgs)
  485. If ButtonHandler IsNot Nothing Then
  486. ButtonHandler(sender, e)
  487. ButtonHandler = Nothing
  488. End If
  489. End Sub
  490. '显示更新日志
  491. Private Sub ShowUpdateLog(sender As Object, e As MouseButtonEventArgs) Handles BtnUpdate.PreviewMouseRightButtonUp
  492. e.Handled = True
  493. Dim CurseForgeUrl As String = Entry.ChangelogUrls.FirstOrDefault(Function(x) x.Contains("curseforge.com"))
  494. Dim ModrinthUrl As String = Entry.ChangelogUrls.FirstOrDefault(Function(x) x.Contains("modrinth.com"))
  495. If CurseForgeUrl Is Nothing OrElse ModrinthUrl Is Nothing Then
  496. OpenWebsite(Entry.ChangelogUrls.First)
  497. Else
  498. Select Case MyMsgBox("要在哪个网站上查看更新日志?", "查看更新日志", "Modrinth", "CurseForge", "取消")
  499. Case 1
  500. OpenWebsite(ModrinthUrl)
  501. Case 2
  502. OpenWebsite(CurseForgeUrl)
  503. End Select
  504. End If
  505. End Sub
  506. '触发更新
  507. Private Sub BtnUpdate_Click(sender As Object, e As EventArgs) Handles BtnUpdate.Click
  508. FrmVersionMod.UpdateMods({Entry})
  509. End Sub
  510. End Class