ModernPatternCard.qml 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import QtQuick 2.15
  2. import QtQuick.Controls 2.15
  3. import "." as Components
  4. // Pattern card: the preview PNG is a circular sand dish (thr_preview.py
  5. // renders the disc with transparent corners), so it sits directly on the
  6. // card surface — no boxed frame around round art.
  7. Rectangle {
  8. id: card
  9. property string name: ""
  10. property alias preview: previewImage.source
  11. // Clean up the pattern name for display
  12. property string cleanName: {
  13. var cleanedName = name
  14. // Remove path (get everything after the last slash)
  15. var parts = cleanedName.split('/')
  16. cleanedName = parts[parts.length - 1]
  17. // Remove .thr extension
  18. cleanedName = cleanedName.replace('.thr', '')
  19. return cleanedName
  20. }
  21. signal clicked()
  22. color: mouseArea.pressed ? Components.ThemeManager.pressedColor
  23. : Components.ThemeManager.surfaceColor
  24. radius: Components.ThemeManager.radiusMd
  25. border.width: 1
  26. border.color: Components.ThemeManager.borderColor
  27. scale: mouseArea.pressed ? 0.97 : 1.0
  28. Behavior on scale {
  29. NumberAnimation { duration: 100; easing.type: Easing.OutQuad }
  30. }
  31. Column {
  32. anchors.fill: parent
  33. anchors.margins: Components.ThemeManager.spaceSm
  34. spacing: Components.ThemeManager.spaceXs
  35. Item {
  36. width: parent.width
  37. height: parent.height - nameLabel.height - Components.ThemeManager.spaceXs
  38. Image {
  39. id: previewImage
  40. anchors.fill: parent
  41. fillMode: Image.PreserveAspectFit
  42. source: preview ? "file:///" + preview : ""
  43. smooth: true
  44. // Decode off the GUI thread, at grid-cell resolution — a
  45. // full 512px decode per tile is what makes flicking stutter.
  46. asynchronous: true
  47. sourceSize.width: 200
  48. sourceSize.height: 200
  49. opacity: status === Image.Ready ? 1 : 0
  50. Behavior on opacity {
  51. NumberAnimation { duration: 200 }
  52. }
  53. }
  54. // Placeholder dish until the preview is decoded (or on failure)
  55. Rectangle {
  56. anchors.centerIn: parent
  57. width: Math.min(parent.width, parent.height)
  58. height: width
  59. radius: width / 2
  60. color: Components.ThemeManager.cardColor
  61. border.width: 1
  62. border.color: Components.ThemeManager.borderColor
  63. visible: previewImage.status !== Image.Ready
  64. Components.Icon {
  65. anchors.centerIn: parent
  66. name: "radio_unchecked"
  67. size: 28
  68. color: Components.ThemeManager.placeholderText
  69. }
  70. }
  71. }
  72. Label {
  73. id: nameLabel
  74. text: cleanName
  75. width: parent.width
  76. elide: Label.ElideRight
  77. horizontalAlignment: Label.AlignHCenter
  78. font.family: Components.ThemeManager.fontMedium
  79. font.pixelSize: 13
  80. color: Components.ThemeManager.textPrimary
  81. maximumLineCount: 1
  82. }
  83. }
  84. MouseArea {
  85. id: mouseArea
  86. anchors.fill: parent
  87. onClicked: card.clicked()
  88. }
  89. }