1
0

ModernPatternCard.qml 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import QtQuick 2.15
  2. import QtQuick.Controls 2.15
  3. import QtQuick.Effects
  4. import "." as Components
  5. Rectangle {
  6. property string name: ""
  7. property alias preview: previewImage.source
  8. // Clean up the pattern name for display
  9. property string cleanName: {
  10. var cleanedName = name
  11. // Remove path (get everything after the last slash)
  12. var parts = cleanedName.split('/')
  13. cleanedName = parts[parts.length - 1]
  14. // Remove .thr extension
  15. cleanedName = cleanedName.replace('.thr', '')
  16. return cleanedName
  17. }
  18. signal clicked()
  19. color: Components.ThemeManager.surfaceColor
  20. radius: 12
  21. // Drop shadow effect
  22. layer.enabled: true
  23. layer.effect: MultiEffect {
  24. shadowEnabled: true
  25. shadowColor: "#20000000"
  26. shadowBlur: 0.8
  27. shadowVerticalOffset: 2
  28. shadowHorizontalOffset: 0
  29. }
  30. // Hover/press animation
  31. scale: mouseArea.pressed ? 0.95 : (mouseArea.containsMouse ? 1.02 : 1.0)
  32. Behavior on scale {
  33. NumberAnimation { duration: 150; easing.type: Easing.OutQuad }
  34. }
  35. Column {
  36. anchors.fill: parent
  37. anchors.margins: 8
  38. spacing: 6
  39. // Preview image container
  40. Rectangle {
  41. width: parent.width
  42. height: parent.height - nameLabel.height - 12
  43. radius: 8
  44. color: Components.ThemeManager.previewBackground
  45. clip: true
  46. Image {
  47. id: previewImage
  48. anchors.fill: parent
  49. fillMode: Image.PreserveAspectFit
  50. source: preview ? "file:///" + preview : ""
  51. smooth: true
  52. // Loading animation
  53. opacity: status === Image.Ready ? 1 : 0
  54. Behavior on opacity {
  55. NumberAnimation { duration: 200 }
  56. }
  57. }
  58. // Placeholder when no preview
  59. Rectangle {
  60. anchors.fill: parent
  61. color: Components.ThemeManager.placeholderBackground
  62. visible: previewImage.status === Image.Error || previewImage.source == ""
  63. radius: 8
  64. Column {
  65. anchors.centerIn: parent
  66. spacing: 8
  67. Text {
  68. text: "◻"
  69. font.pixelSize: 32
  70. anchors.horizontalCenter: parent.horizontalCenter
  71. color: Components.ThemeManager.placeholderText
  72. }
  73. Text {
  74. text: "No Preview"
  75. anchors.horizontalCenter: parent.horizontalCenter
  76. color: Components.ThemeManager.textTertiary
  77. font.pixelSize: 12
  78. }
  79. }
  80. }
  81. }
  82. // Pattern name
  83. Label {
  84. id: nameLabel
  85. text: cleanName
  86. width: parent.width
  87. elide: Label.ElideRight
  88. horizontalAlignment: Label.AlignHCenter
  89. font.pixelSize: 13
  90. font.weight: Font.Medium
  91. color: Components.ThemeManager.textPrimary
  92. wrapMode: Text.Wrap
  93. maximumLineCount: 2
  94. }
  95. }
  96. // Click area
  97. MouseArea {
  98. id: mouseArea
  99. anchors.fill: parent
  100. hoverEnabled: true
  101. onClicked: parent.clicked()
  102. // Ripple effect on click
  103. Rectangle {
  104. id: ripple
  105. width: 0
  106. height: 0
  107. radius: width / 2
  108. color: "#20000000"
  109. anchors.centerIn: parent
  110. NumberAnimation {
  111. id: rippleAnimation
  112. target: ripple
  113. property: "width"
  114. from: 0
  115. to: mouseArea.width * 1.5
  116. duration: 300
  117. easing.type: Easing.OutQuad
  118. onFinished: {
  119. ripple.width = 0
  120. ripple.height = 0
  121. }
  122. }
  123. Connections {
  124. target: mouseArea
  125. function onPressed() {
  126. ripple.height = ripple.width
  127. rippleAnimation.start()
  128. }
  129. }
  130. }
  131. }
  132. }