ModernPatternListPage.qml 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. import QtQuick 2.15
  2. import QtQuick.Controls 2.15
  3. import QtQuick.Layouts 1.15
  4. import "../components"
  5. Page {
  6. id: page
  7. property var patternModel
  8. property var backend
  9. property var stackView
  10. property bool searchExpanded: false
  11. Rectangle {
  12. anchors.fill: parent
  13. color: "#f5f5f5"
  14. }
  15. ColumnLayout {
  16. anchors.fill: parent
  17. spacing: 0
  18. // Header with integrated search
  19. Rectangle {
  20. Layout.fillWidth: true
  21. Layout.preferredHeight: 50
  22. color: "white"
  23. // Bottom border
  24. Rectangle {
  25. anchors.bottom: parent.bottom
  26. width: parent.width
  27. height: 1
  28. color: "#e5e7eb"
  29. }
  30. RowLayout {
  31. anchors.fill: parent
  32. anchors.leftMargin: 15
  33. anchors.rightMargin: 10
  34. ConnectionStatus {
  35. backend: page.backend
  36. Layout.rightMargin: 8
  37. visible: !searchExpanded
  38. }
  39. Label {
  40. text: "Browse Patterns"
  41. font.pixelSize: 18
  42. font.bold: true
  43. color: "#333"
  44. visible: !searchExpanded
  45. }
  46. // Pattern count
  47. Label {
  48. text: patternModel.rowCount() + " patterns"
  49. font.pixelSize: 12
  50. color: "#999"
  51. visible: !searchExpanded
  52. }
  53. Item {
  54. Layout.fillWidth: true
  55. visible: !searchExpanded
  56. }
  57. // Expandable search
  58. Rectangle {
  59. Layout.fillWidth: searchExpanded
  60. Layout.preferredWidth: searchExpanded ? parent.width - 60 : 120
  61. Layout.preferredHeight: 32
  62. radius: 16
  63. color: searchExpanded ? "white" : "#f5f5f5"
  64. border.color: searchExpanded ? "#2563eb" : "#e0e0e0"
  65. border.width: 1
  66. Behavior on Layout.preferredWidth {
  67. NumberAnimation { duration: 200 }
  68. }
  69. RowLayout {
  70. anchors.fill: parent
  71. anchors.leftMargin: 10
  72. anchors.rightMargin: 10
  73. spacing: 5
  74. Text {
  75. text: "⌕"
  76. font.pixelSize: 16
  77. font.family: "sans-serif"
  78. color: searchExpanded ? "#2563eb" : "#6b7280"
  79. }
  80. TextField {
  81. id: searchField
  82. Layout.fillWidth: true
  83. placeholderText: searchExpanded ? "Search patterns... (press Enter)" : "Search"
  84. font.pixelSize: 14
  85. visible: searchExpanded || text.length > 0
  86. property string lastSearchText: ""
  87. property bool hasUnappliedSearch: text !== lastSearchText && text.length > 0
  88. background: Rectangle {
  89. color: "transparent"
  90. border.color: searchField.hasUnappliedSearch ? "#f59e0b" : "transparent"
  91. border.width: searchField.hasUnappliedSearch ? 1 : 0
  92. radius: 4
  93. }
  94. // Remove automatic filtering on text change
  95. // onTextChanged: patternModel.filter(text)
  96. // Only filter when user presses Enter or field loses focus
  97. onAccepted: {
  98. patternModel.filter(text)
  99. lastSearchText = text
  100. Qt.inputMethod.hide()
  101. focus = false
  102. }
  103. // Enable virtual keyboard
  104. activeFocusOnPress: true
  105. selectByMouse: true
  106. inputMethodHints: Qt.ImhNoPredictiveText
  107. // Direct MouseArea for touch events
  108. MouseArea {
  109. anchors.fill: parent
  110. onPressed: {
  111. searchField.forceActiveFocus()
  112. Qt.inputMethod.show()
  113. mouse.accepted = false // Pass through to TextField
  114. }
  115. }
  116. onActiveFocusChanged: {
  117. if (activeFocus) {
  118. searchExpanded = true
  119. // Force virtual keyboard to show
  120. Qt.inputMethod.show()
  121. } else {
  122. // Apply search when focus is lost
  123. if (text !== lastSearchText) {
  124. patternModel.filter(text)
  125. lastSearchText = text
  126. }
  127. }
  128. }
  129. // Handle Enter key - triggers onAccepted
  130. Keys.onReturnPressed: {
  131. // onAccepted will be called automatically
  132. // Just hide keyboard and unfocus
  133. Qt.inputMethod.hide()
  134. focus = false
  135. }
  136. Keys.onEscapePressed: {
  137. // Clear search and hide keyboard
  138. text = ""
  139. lastSearchText = ""
  140. patternModel.filter("")
  141. Qt.inputMethod.hide()
  142. focus = false
  143. }
  144. }
  145. Text {
  146. text: searchExpanded || searchField.text.length > 0 ? "Search" : ""
  147. font.pixelSize: 12
  148. color: "#999"
  149. visible: !searchExpanded && searchField.text.length === 0
  150. }
  151. }
  152. MouseArea {
  153. anchors.fill: parent
  154. enabled: !searchExpanded
  155. onClicked: {
  156. searchExpanded = true
  157. searchField.forceActiveFocus()
  158. Qt.inputMethod.show()
  159. }
  160. }
  161. }
  162. // Close button when expanded
  163. Button {
  164. text: "✕"
  165. font.pixelSize: 18
  166. flat: true
  167. visible: searchExpanded
  168. Layout.preferredWidth: 32
  169. Layout.preferredHeight: 32
  170. onClicked: {
  171. searchExpanded = false
  172. searchField.text = ""
  173. searchField.lastSearchText = ""
  174. searchField.focus = false
  175. // Clear the filter when closing search
  176. patternModel.filter("")
  177. }
  178. }
  179. }
  180. }
  181. // Content - Pattern Grid
  182. GridView {
  183. id: gridView
  184. Layout.fillWidth: true
  185. Layout.fillHeight: true
  186. cellWidth: 200
  187. cellHeight: 220
  188. model: patternModel
  189. clip: true
  190. // Add smooth scrolling
  191. ScrollBar.vertical: ScrollBar {
  192. active: true
  193. policy: ScrollBar.AsNeeded
  194. }
  195. delegate: ModernPatternCard {
  196. width: gridView.cellWidth - 10
  197. height: gridView.cellHeight - 10
  198. name: model.name
  199. preview: model.preview
  200. onClicked: {
  201. if (stackView && backend) {
  202. stackView.push("PatternDetailPage.qml", {
  203. patternName: model.name,
  204. patternPath: model.path,
  205. patternPreview: model.preview,
  206. backend: backend
  207. })
  208. }
  209. }
  210. }
  211. // Add scroll animations
  212. add: Transition {
  213. NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 300 }
  214. NumberAnimation { property: "scale"; from: 0.8; to: 1; duration: 300 }
  215. }
  216. }
  217. // Empty state
  218. Item {
  219. Layout.fillWidth: true
  220. Layout.fillHeight: true
  221. visible: patternModel.rowCount() === 0 && searchField.text !== ""
  222. Column {
  223. anchors.centerIn: parent
  224. spacing: 20
  225. Text {
  226. text: "⌕"
  227. font.pixelSize: 48
  228. anchors.horizontalCenter: parent.horizontalCenter
  229. color: "#ccc"
  230. }
  231. Label {
  232. text: "No patterns found"
  233. anchors.horizontalCenter: parent.horizontalCenter
  234. color: "#999"
  235. font.pixelSize: 18
  236. }
  237. Label {
  238. text: "Try a different search term"
  239. anchors.horizontalCenter: parent.horizontalCenter
  240. color: "#ccc"
  241. font.pixelSize: 14
  242. }
  243. }
  244. }
  245. }
  246. }