ModernPatternCard.qml 4.3 KB

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