flea-qml 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (637) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +511 -2
  3. package/THIRD-PARTY-NOTICES.md +29 -0
  4. package/demos/App.java +5 -0
  5. package/demos/Dockerfile +4 -0
  6. package/demos/Main.kt +3 -0
  7. package/demos/Program.cs +1 -0
  8. package/demos/README.md +12 -0
  9. package/demos/app.dart +3 -0
  10. package/demos/app.deb +0 -0
  11. package/demos/app.js +1 -0
  12. package/demos/backup.tar +0 -0
  13. package/demos/budget.xlsx +0 -0
  14. package/demos/build.sh +2 -0
  15. package/demos/bundle.zip +0 -0
  16. package/demos/clip.mp4 +0 -0
  17. package/demos/data.sqlite +0 -0
  18. package/demos/disk.iso +0 -0
  19. package/demos/engine.cpp +5 -0
  20. package/demos/firmware.bin +0 -0
  21. package/demos/flake.nix +4 -0
  22. package/demos/folder/inside.txt +1 -0
  23. package/demos/font.ttf +0 -0
  24. package/demos/index.html +5 -0
  25. package/demos/index.php +2 -0
  26. package/demos/init.lua +1 -0
  27. package/demos/letter.docx +0 -0
  28. package/demos/lib.rs +3 -0
  29. package/demos/main.c +6 -0
  30. package/demos/main.h +3 -0
  31. package/demos/math-and-diagrams.md +200 -0
  32. package/demos/notes.tar.gz +0 -0
  33. package/demos/notes.txt +3 -0
  34. package/demos/photo.png +0 -0
  35. package/demos/program +2 -0
  36. package/demos/report.pdf +32 -0
  37. package/demos/script.py +1 -0
  38. package/demos/server.go +7 -0
  39. package/demos/settings.json +4 -0
  40. package/demos/slides.pptx +0 -0
  41. package/demos/song.mp3 +0 -0
  42. package/demos/style.css +4 -0
  43. package/demos/tool.rb +1 -0
  44. package/demos/types.ts +2 -0
  45. package/docs/pele.md +23 -0
  46. package/docs/protocol.md +3420 -0
  47. package/docs/temml.md +63 -0
  48. package/index.js +107 -0
  49. package/package.json +34 -4
  50. package/src/archive.js +407 -0
  51. package/src/archiveops.js +728 -0
  52. package/src/backend.js +369 -0
  53. package/src/bulkrename.js +95 -0
  54. package/src/clip-host.js +76 -0
  55. package/src/clip.js +462 -0
  56. package/src/code.js +168 -0
  57. package/src/compare.js +74 -0
  58. package/src/create.js +128 -0
  59. package/src/dirsize.js +147 -0
  60. package/src/figure-svg.js +530 -0
  61. package/src/figure-thread.js +23 -0
  62. package/src/figure.js +132 -0
  63. package/src/find.js +55 -0
  64. package/src/flea.js +39 -0
  65. package/src/fsinfo.js +39 -0
  66. package/src/highlight.js +141 -0
  67. package/src/html.js +52 -0
  68. package/src/kinds.js +89 -0
  69. package/src/listing.js +130 -0
  70. package/src/markdown-html.js +317 -0
  71. package/src/mathml.js +186 -0
  72. package/src/menu.js +180 -0
  73. package/src/meta.js +135 -0
  74. package/src/office.js +810 -0
  75. package/src/order.js +100 -0
  76. package/src/peek.js +49 -0
  77. package/src/pele-wrapper.js +695 -0
  78. package/src/permissions.js +180 -0
  79. package/src/places.js +313 -0
  80. package/src/preview-language.js +10 -0
  81. package/src/terminal-server.js +95 -0
  82. package/src/terminal-shell.js +26 -0
  83. package/src/transfer.js +669 -0
  84. package/src/trash-record.js +41 -0
  85. package/src/trash.js +210 -0
  86. package/src/trashbrowse.js +327 -0
  87. package/src/uistate.js +393 -0
  88. package/src/uistore.js +168 -0
  89. package/src/vendor/LICENSES/Apache-2.0.txt +202 -0
  90. package/src/vendor/LICENSES/MIT-katex-2015.txt +27 -0
  91. package/src/vendor/LICENSES/MIT-katex.txt +21 -0
  92. package/src/vendor/LICENSES/MIT-mermaid.txt +21 -0
  93. package/src/vendor/LICENSES/MIT-pele.txt +40 -0
  94. package/src/vendor/LICENSES/MIT-perseus.txt +18 -0
  95. package/src/vendor/LICENSES/MIT-temml.txt +21 -0
  96. package/src/vendor/pele.js +145 -0
  97. package/src/vendor/temml.mjs +14708 -0
  98. package/src/watch.js +98 -0
  99. package/src/wayland/client.js +109 -0
  100. package/src/wayland/protocol.js +55 -0
  101. package/src/wayland/protocols/ext-data-control-v1.xml +276 -0
  102. package/src/wayland/protocols/wayland.xml +3248 -0
  103. package/src/wayland/protocols/wlr-data-control-unstable-v1.xml +278 -0
  104. package/src/wayland/resource-io.js +6 -0
  105. package/src/wayland/socket-discovery.js +26 -0
  106. package/src/wayland/transport-posix.js +120 -0
  107. package/src/wayland/transport-win32.js +11 -0
  108. package/src/wayland/transport.js +3 -0
  109. package/src/wayland/win32-resources.js +80 -0
  110. package/src/wayland/win32-wire.js +62 -0
  111. package/test/archive.test.js +80 -0
  112. package/test/bench-markdown-scroll.mjs +190 -0
  113. package/test/clip-host.test.js +113 -0
  114. package/test/code-preview.test.js +35 -0
  115. package/test/compare-ui-paths.test.js +24 -0
  116. package/test/compare.test.js +54 -0
  117. package/test/delete-trash.test.js +67 -0
  118. package/test/dirsize.test.js +138 -0
  119. package/test/file-ops.test.js +307 -0
  120. package/test/file-url.test.js +24 -0
  121. package/test/find-contents.test.js +161 -0
  122. package/test/find-input.test.js +66 -0
  123. package/test/fixtures/pele-parity.json +1006 -0
  124. package/test/highlight-launcher.test.js +35 -0
  125. package/test/html-preview.test.js +22 -0
  126. package/test/input.test.js +15 -0
  127. package/test/keymap.test.js +26 -0
  128. package/test/language-icons.test.js +36 -0
  129. package/test/launcher.test.js +51 -0
  130. package/test/manual-archive-here-ui.mjs +47 -0
  131. package/test/manual-code-scroll.mjs +28 -0
  132. package/test/manual-column-scroll.mjs +24 -0
  133. package/test/manual-compare-ui.mjs +20 -0
  134. package/test/manual-find-status.mjs +37 -0
  135. package/test/manual-folder-focus.mjs +63 -0
  136. package/test/manual-html-security.mjs +23 -0
  137. package/test/manual-html-ui.mjs +30 -0
  138. package/test/manual-syntax-preview.mjs +26 -0
  139. package/test/manual-terminal-lifecycle.mjs +42 -0
  140. package/test/manual-terminal-navigation.mjs +48 -0
  141. package/test/manual-terminal-ui.mjs +47 -0
  142. package/test/manual-trash-ui.mjs +86 -0
  143. package/test/markdown-diagrams.test.js +93 -0
  144. package/test/markdown-html.test.js +105 -0
  145. package/test/mathml.test.js +92 -0
  146. package/test/menu-transfer.test.js +57 -0
  147. package/test/office-preview.test.js +154 -0
  148. package/test/pele-parity.test.js +28 -0
  149. package/test/places.test.js +134 -0
  150. package/test/preview-keys.test.js +60 -0
  151. package/test/preview-language.test.js +22 -0
  152. package/test/status-filename.test.js +20 -0
  153. package/test/transfer-replace-merge.test.js +187 -0
  154. package/test/trash-address.test.js +23 -0
  155. package/test/trash-view.test.js +37 -0
  156. package/test/trashbrowse.test.js +135 -0
  157. package/ui/AboutFacts.qml +78 -0
  158. package/ui/ArchiveCards.qml +95 -0
  159. package/ui/ArchiveCompressDialog.qml +378 -0
  160. package/ui/ArchiveHereConfirm.qml +127 -0
  161. package/ui/ArchivePasswordDialog.qml +257 -0
  162. package/ui/Backend.qml +775 -0
  163. package/ui/BulkOldNames.qml +47 -0
  164. package/ui/BulkRenameDialog.qml +380 -0
  165. package/ui/CardFrame.qml +67 -0
  166. package/ui/CardScroll.qml +79 -0
  167. package/ui/CheckBox.qml +73 -0
  168. package/ui/ChromeBar.qml +360 -0
  169. package/ui/ChromeButton.qml +131 -0
  170. package/ui/ChromeModes.qml +43 -0
  171. package/ui/CodeLines.qml +256 -0
  172. package/ui/CodeRefusal.qml +52 -0
  173. package/ui/CollideConfirm.qml +147 -0
  174. package/ui/CollideHost.qml +167 -0
  175. package/ui/ColumnPane.qml +525 -0
  176. package/ui/ColumnRow.qml +380 -0
  177. package/ui/ColumnsArea.qml +594 -0
  178. package/ui/ColumnsLanding.qml +147 -0
  179. package/ui/CompareView.qml +321 -0
  180. package/ui/ContextMenu.qml +743 -0
  181. package/ui/ConvertDialog.qml +456 -0
  182. package/ui/CountedWorker.qml +7 -0
  183. package/ui/Crumb.qml +64 -0
  184. package/ui/CrumbDrop.qml +25 -0
  185. package/ui/CsvTable.qml +394 -0
  186. package/ui/CursorMark.qml +41 -0
  187. package/ui/DefaultClaim.qml +130 -0
  188. package/ui/DeviceLock.qml +47 -0
  189. package/ui/DeviceMounts.qml +540 -0
  190. package/ui/DialogButton.qml +150 -0
  191. package/ui/DialogField.qml +147 -0
  192. package/ui/DialogHeader.qml +31 -0
  193. package/ui/DialogScrim.qml +20 -0
  194. package/ui/DialogTitle.qml +37 -0
  195. package/ui/Divider.qml +9 -0
  196. package/ui/DropInto.qml +126 -0
  197. package/ui/DropTarget.qml +35 -0
  198. package/ui/DropboxMark.qml +60 -0
  199. package/ui/EmptyState.qml +150 -0
  200. package/ui/EscapeHint.qml +11 -0
  201. package/ui/FactsTable.qml +54 -0
  202. package/ui/FastScrollHandler.qml +476 -0
  203. package/ui/FatNamesConfirm.qml +157 -0
  204. package/ui/Favourites.qml +143 -0
  205. package/ui/FigureService.qml +120 -0
  206. package/ui/FileDrag.qml +267 -0
  207. package/ui/FilterStrip.qml +133 -0
  208. package/ui/FitMetrics.qml +22 -0
  209. package/ui/FlashDriveRows.qml +137 -0
  210. package/ui/FlashHost.qml +72 -0
  211. package/ui/FlashImageRow.qml +104 -0
  212. package/ui/FlashRunner.qml +119 -0
  213. package/ui/FlashStepRow.qml +172 -0
  214. package/ui/FlashStrip.qml +38 -0
  215. package/ui/FlashView.qml +397 -0
  216. package/ui/FleaMark.qml +88 -0
  217. package/ui/FlyoutFade.qml +23 -0
  218. package/ui/FontPane.qml +170 -0
  219. package/ui/Glass.qml +114 -0
  220. package/ui/GlassTagger.qml +59 -0
  221. package/ui/Glyph.qml +64 -0
  222. package/ui/GlyphSolid.qml +22 -0
  223. package/ui/GridArea.qml +664 -0
  224. package/ui/GridTile.qml +325 -0
  225. package/ui/GvfsBridge.qml +164 -0
  226. package/ui/Header.qml +433 -0
  227. package/ui/HoverTimers.qml +39 -0
  228. package/ui/HtmlClip.qml +30 -0
  229. package/ui/HtmlPane.qml +63 -0
  230. package/ui/HyprlandMark.qml +45 -0
  231. package/ui/Ipc.qml +1005 -0
  232. package/ui/IpcControls.qml +7 -0
  233. package/ui/IpcMenuState.qml +48 -0
  234. package/ui/IpcPreviewState.qml +185 -0
  235. package/ui/JumpPath.qml +51 -0
  236. package/ui/KeymapSheet.qml +561 -0
  237. package/ui/KeymapSheetResult.qml +89 -0
  238. package/ui/KindStack.qml +40 -0
  239. package/ui/LangMark.qml +51 -0
  240. package/ui/LangMarkLoader.qml +26 -0
  241. package/ui/List.qml +480 -0
  242. package/ui/LoadingState.qml +54 -0
  243. package/ui/LocalSend.qml +56 -0
  244. package/ui/LocalSendMark.qml +73 -0
  245. package/ui/MarkdownBlockView.qml +311 -0
  246. package/ui/MarkdownBlocks.qml +35 -0
  247. package/ui/MarkdownFigure.qml +254 -0
  248. package/ui/MarkdownImages.qml +72 -0
  249. package/ui/MarkdownList.qml +81 -0
  250. package/ui/MarkdownMathsText.qml +97 -0
  251. package/ui/MarkdownPane.qml +83 -0
  252. package/ui/MarkdownPictures.qml +41 -0
  253. package/ui/MarkdownQuote.qml +52 -0
  254. package/ui/MarkdownRemote.qml +82 -0
  255. package/ui/MarkdownSource.qml +155 -0
  256. package/ui/MarkdownTable.qml +222 -0
  257. package/ui/MarkdownTableScroll.qml +64 -0
  258. package/ui/MarkdownTableWheel.qml +126 -0
  259. package/ui/MarkdownText.qml +87 -0
  260. package/ui/MarkdownWorker.js +132 -0
  261. package/ui/MatchText.qml +99 -0
  262. package/ui/MediaSession.qml +38 -0
  263. package/ui/MediaSound.qml +26 -0
  264. package/ui/MediaStrip.qml +191 -0
  265. package/ui/MediaTrack.qml +36 -0
  266. package/ui/MediaVolume.qml +54 -0
  267. package/ui/MenuActionDialog.qml +396 -0
  268. package/ui/MenuEdgeFade.qml +24 -0
  269. package/ui/MenuHost.qml +227 -0
  270. package/ui/MenuRow.qml +351 -0
  271. package/ui/MountListing.qml +100 -0
  272. package/ui/NetworkDialog.qml +435 -0
  273. package/ui/NetworkForm.qml +426 -0
  274. package/ui/NetworkMounts.qml +913 -0
  275. package/ui/NetworkPlaces.qml +127 -0
  276. package/ui/OpenWithDialog.qml +613 -0
  277. package/ui/Opener.qml +95 -0
  278. package/ui/Pane.qml +1320 -0
  279. package/ui/PaneMenuActions.qml +527 -0
  280. package/ui/PanePath.qml +90 -0
  281. package/ui/PaneRail.qml +268 -0
  282. package/ui/PaneStates.qml +88 -0
  283. package/ui/PaneSwap.qml +339 -0
  284. package/ui/PaneWire.qml +601 -0
  285. package/ui/ParkedWait.qml +9 -0
  286. package/ui/PathEntry.qml +186 -0
  287. package/ui/PathJump.qml +400 -0
  288. package/ui/PdfViewer.qml +297 -0
  289. package/ui/PermissionsDialog.qml +658 -0
  290. package/ui/PermissionsHeadings.qml +31 -0
  291. package/ui/PhoneMounts.qml +67 -0
  292. package/ui/PickerAnswers.qml +75 -0
  293. package/ui/PickerBox.qml +26 -0
  294. package/ui/PickerChrome.qml +295 -0
  295. package/ui/PickerFilter.qml +97 -0
  296. package/ui/PickerGrid.qml +308 -0
  297. package/ui/PickerHeader.qml +28 -0
  298. package/ui/PickerLifecycle.qml +27 -0
  299. package/ui/PickerList.qml +218 -0
  300. package/ui/PickerListing.qml +149 -0
  301. package/ui/PickerPathBar.qml +127 -0
  302. package/ui/PickerPlaces.qml +189 -0
  303. package/ui/PickerRecent.qml +92 -0
  304. package/ui/PickerSave.qml +101 -0
  305. package/ui/PickerSelection.qml +155 -0
  306. package/ui/PickerWindow.qml +846 -0
  307. package/ui/PowerSectorsReader.qml +10 -0
  308. package/ui/PrebuildDriver.qml +146 -0
  309. package/ui/Preview.qml +1005 -0
  310. package/ui/PreviewAnimation.qml +24 -0
  311. package/ui/PreviewArchive.qml +91 -0
  312. package/ui/PreviewColumn.qml +553 -0
  313. package/ui/PreviewEdge.qml +63 -0
  314. package/ui/PreviewFacts.qml +30 -0
  315. package/ui/PreviewFactsLine.qml +34 -0
  316. package/ui/PreviewHtml.qml +233 -0
  317. package/ui/PreviewImage.qml +253 -0
  318. package/ui/PreviewImagePan.qml +13 -0
  319. package/ui/PreviewImageSharp.qml +32 -0
  320. package/ui/PreviewLines.qml +106 -0
  321. package/ui/PreviewMarkdown.qml +706 -0
  322. package/ui/PreviewMedia.qml +250 -0
  323. package/ui/PreviewPdf.qml +482 -0
  324. package/ui/PreviewScroll.qml +201 -0
  325. package/ui/PreviewSwap.qml +335 -0
  326. package/ui/PreviewText.qml +149 -0
  327. package/ui/ProtocolChip.qml +18 -0
  328. package/ui/QuickLookArchive.qml +41 -0
  329. package/ui/QuickLookBar.qml +103 -0
  330. package/ui/QuickLookPrepare.qml +355 -0
  331. package/ui/QuickLookSwap.qml +10 -0
  332. package/ui/QuickLookWarm.qml +65 -0
  333. package/ui/RailDrop.qml +155 -0
  334. package/ui/RailHeading.qml +19 -0
  335. package/ui/RailPlaces.qml +38 -0
  336. package/ui/RailTrashRow.qml +67 -0
  337. package/ui/RenameField.qml +353 -0
  338. package/ui/RendererRetry.qml +11 -0
  339. package/ui/ResizeHandle.qml +52 -0
  340. package/ui/Reveal.qml +54 -0
  341. package/ui/Row.qml +476 -0
  342. package/ui/RowDrag.qml +213 -0
  343. package/ui/RowDropTarget.qml +27 -0
  344. package/ui/RunCard.qml +286 -0
  345. package/ui/Scripts.qml +65 -0
  346. package/ui/SearchStrip.qml +232 -0
  347. package/ui/SelectionBand.qml +140 -0
  348. package/ui/SelectionPreview.qml +370 -0
  349. package/ui/SettingsFavourite.qml +122 -0
  350. package/ui/SettingsGroup.qml +96 -0
  351. package/ui/SettingsHint.qml +12 -0
  352. package/ui/SettingsMetrics.qml +29 -0
  353. package/ui/SettingsPane.qml +178 -0
  354. package/ui/SettingsPanel.qml +547 -0
  355. package/ui/SettingsRail.qml +128 -0
  356. package/ui/SettingsRow.qml +409 -0
  357. package/ui/SettingsRuler.qml +85 -0
  358. package/ui/SettingsSegment.qml +85 -0
  359. package/ui/ShareBrowser.qml +113 -0
  360. package/ui/ShareLink.qml +67 -0
  361. package/ui/ShelfPins.qml +101 -0
  362. package/ui/Sidebar.qml +728 -0
  363. package/ui/SidebarRow.qml +358 -0
  364. package/ui/Spinner.qml +60 -0
  365. package/ui/StableModel.qml +89 -0
  366. package/ui/StateMessage.qml +84 -0
  367. package/ui/StatusBar.qml +512 -0
  368. package/ui/StatusFileName.qml +27 -0
  369. package/ui/TabBar.qml +673 -0
  370. package/ui/TabDragGeometry.qml +60 -0
  371. package/ui/TagMenuMark.qml +52 -0
  372. package/ui/TagRailSection.qml +53 -0
  373. package/ui/TagSlot.qml +40 -0
  374. package/ui/TagState.qml +150 -0
  375. package/ui/TagSwatch.qml +28 -0
  376. package/ui/Taildrop.qml +86 -0
  377. package/ui/TailscaleMark.qml +50 -0
  378. package/ui/TerminalService.qml +69 -0
  379. package/ui/TerminalTab.qml +48 -0
  380. package/ui/TerminalTabs.qml +27 -0
  381. package/ui/Theme.qml +460 -0
  382. package/ui/ThemeColors.qml +43 -0
  383. package/ui/TransferCard.qml +239 -0
  384. package/ui/TrashConfirm.qml +129 -0
  385. package/ui/TrashDropHandler.qml +11 -0
  386. package/ui/TrashHost.qml +202 -0
  387. package/ui/TrashMonitor.qml +30 -0
  388. package/ui/TrashToolbar.qml +19 -0
  389. package/ui/TrashView.qml +492 -0
  390. package/ui/UnlockDialog.qml +450 -0
  391. package/ui/UnlockHost.qml +206 -0
  392. package/ui/UpdateCheck.qml +97 -0
  393. package/ui/UserDirs.qml +25 -0
  394. package/ui/ViewState.qml +565 -0
  395. package/ui/ViewportScrollBar.qml +195 -0
  396. package/ui/ViewportScrollBars.qml +25 -0
  397. package/ui/WebPreview.qml +33 -0
  398. package/ui/WebPreviewView.qml +26 -0
  399. package/ui/WindowBody.qml +829 -0
  400. package/ui/WindowDrag.qml +35 -0
  401. package/ui/boot/Commons/AudioNodes.qml +50 -0
  402. package/ui/boot/Commons/AudioNodesModel.js +96 -0
  403. package/ui/boot/Commons/Border.qml +243 -0
  404. package/ui/boot/Commons/BorderGeometry.js +373 -0
  405. package/ui/boot/Commons/Color.qml +258 -0
  406. package/ui/boot/Commons/I18n.qml +118 -0
  407. package/ui/boot/Commons/InputDevices.qml +112 -0
  408. package/ui/boot/Commons/InputMethodState.qml +38 -0
  409. package/ui/boot/Commons/IpcRegistry.qml +77 -0
  410. package/ui/boot/Commons/Numbers.js +19 -0
  411. package/ui/boot/Commons/Plural.js +91 -0
  412. package/ui/boot/Commons/ShellIpc.qml +12 -0
  413. package/ui/boot/Commons/Style.qml +561 -0
  414. package/ui/boot/Commons/Translations.qml +100 -0
  415. package/ui/boot/Commons/UntypedInput.qml +152 -0
  416. package/ui/boot/Commons/Util.qml +188 -0
  417. package/ui/boot/Commons/qmldir +13 -0
  418. package/ui/boot/LICENSE-omarchy +20 -0
  419. package/ui/boot/Ui/BackgroundMedia.qml +49 -0
  420. package/ui/boot/Ui/BarIconButton.qml +106 -0
  421. package/ui/boot/Ui/BarIconContent.qml +38 -0
  422. package/ui/boot/Ui/BarIndicator.qml +53 -0
  423. package/ui/boot/Ui/BarWidget.qml +50 -0
  424. package/ui/boot/Ui/BorderOverlay.qml +54 -0
  425. package/ui/boot/Ui/BorderSurface.qml +40 -0
  426. package/ui/boot/Ui/Button.qml +224 -0
  427. package/ui/boot/Ui/ButtonGroup.qml +131 -0
  428. package/ui/boot/Ui/ConfirmDialog.qml +134 -0
  429. package/ui/boot/Ui/CursorSurface.qml +42 -0
  430. package/ui/boot/Ui/Dropdown.qml +245 -0
  431. package/ui/boot/Ui/FilterInput.qml +26 -0
  432. package/ui/boot/Ui/IconContentBounds.qml +141 -0
  433. package/ui/boot/Ui/KeyboardPanel.qml +453 -0
  434. package/ui/boot/Ui/MultiSelect.qml +624 -0
  435. package/ui/boot/Ui/NumberField.qml +86 -0
  436. package/ui/boot/Ui/OpticalGlyph.qml +68 -0
  437. package/ui/boot/Ui/OverlayWindow.qml +102 -0
  438. package/ui/boot/Ui/Panel.qml +60 -0
  439. package/ui/boot/Ui/PanelActionButton.qml +105 -0
  440. package/ui/boot/Ui/PanelController.qml +16 -0
  441. package/ui/boot/Ui/PanelHero.qml +112 -0
  442. package/ui/boot/Ui/PanelKeyCatcher.qml +100 -0
  443. package/ui/boot/Ui/PanelSectionHeader.qml +31 -0
  444. package/ui/boot/Ui/PanelSeparator.qml +19 -0
  445. package/ui/boot/Ui/PanelSlider.qml +156 -0
  446. package/ui/boot/Ui/PanelToolTip.qml +50 -0
  447. package/ui/boot/Ui/PluginBarApi.qml +89 -0
  448. package/ui/boot/Ui/PointerMoveGate.qml +54 -0
  449. package/ui/boot/Ui/PopupCard.qml +181 -0
  450. package/ui/boot/Ui/ScreenMoveRemap.qml +43 -0
  451. package/ui/boot/Ui/SearchableDropdown.qml +354 -0
  452. package/ui/boot/Ui/SizedImage.qml +125 -0
  453. package/ui/boot/Ui/SpeedTestOverlay.qml +412 -0
  454. package/ui/boot/Ui/TextField.qml +58 -0
  455. package/ui/boot/Ui/Toggle.qml +118 -0
  456. package/ui/boot/Ui/ToggleSwitch.qml +115 -0
  457. package/ui/boot/Ui/WidgetButton.qml +172 -0
  458. package/ui/boot/Ui/qmldir +41 -0
  459. package/ui/boot/fleatab.qml +50 -0
  460. package/ui/boot/picker.qml +39 -0
  461. package/ui/boot/shell.qml +131 -0
  462. package/ui/boot/tabtearoff.qml +51 -0
  463. package/ui/js/Anchor.js +379 -0
  464. package/ui/js/AnchorHold.js +162 -0
  465. package/ui/js/Archive.js +157 -0
  466. package/ui/js/ArchiveCard.js +44 -0
  467. package/ui/js/ArchiveOps.js +44 -0
  468. package/ui/js/BackReturn.js +118 -0
  469. package/ui/js/BulkRename.js +100 -0
  470. package/ui/js/Buttons.js +54 -0
  471. package/ui/js/ChromeKeys.js +116 -0
  472. package/ui/js/ClipMarks.js +73 -0
  473. package/ui/js/ClipSelection.js +67 -0
  474. package/ui/js/Clipboard.js +179 -0
  475. package/ui/js/Cloud.js +97 -0
  476. package/ui/js/CodeRuns.js +72 -0
  477. package/ui/js/Collide.js +212 -0
  478. package/ui/js/ColumnFit.js +28 -0
  479. package/ui/js/ColumnMenu.js +111 -0
  480. package/ui/js/Columns.js +298 -0
  481. package/ui/js/ColumnsCarry.js +103 -0
  482. package/ui/js/Compare.js +74 -0
  483. package/ui/js/Contrast.js +157 -0
  484. package/ui/js/ControlReader.js +42 -0
  485. package/ui/js/Convert.js +52 -0
  486. package/ui/js/CopyAs.js +52 -0
  487. package/ui/js/Crumbs.js +178 -0
  488. package/ui/js/Csv.js +231 -0
  489. package/ui/js/Density.js +44 -0
  490. package/ui/js/Devices.js +440 -0
  491. package/ui/js/DirSizes.js +180 -0
  492. package/ui/js/Drag.js +360 -0
  493. package/ui/js/DragOut.js +166 -0
  494. package/ui/js/DropDev.js +52 -0
  495. package/ui/js/Dropbox.js +41 -0
  496. package/ui/js/Eject.js +217 -0
  497. package/ui/js/Errors.js +188 -0
  498. package/ui/js/ExtThumbs.js +115 -0
  499. package/ui/js/Facts.js +402 -0
  500. package/ui/js/FigureDate.js +14 -0
  501. package/ui/js/Filter.js +285 -0
  502. package/ui/js/Find.js +46 -0
  503. package/ui/js/Flash.js +184 -0
  504. package/ui/js/FlashCard.js +79 -0
  505. package/ui/js/FlashEndings.js +125 -0
  506. package/ui/js/FlashPane.js +116 -0
  507. package/ui/js/FleaBin.js +12 -0
  508. package/ui/js/Focus.js +504 -0
  509. package/ui/js/FolderSorts.js +108 -0
  510. package/ui/js/FolderViews.js +44 -0
  511. package/ui/js/Format.js +314 -0
  512. package/ui/js/Fuzzy.js +141 -0
  513. package/ui/js/GlassTag.js +72 -0
  514. package/ui/js/Grid.js +33 -0
  515. package/ui/js/GridGeometry.js +90 -0
  516. package/ui/js/GridNames.js +153 -0
  517. package/ui/js/GvfsBridge.js +200 -0
  518. package/ui/js/HostClipboard.js +29 -0
  519. package/ui/js/Icons.js +218 -0
  520. package/ui/js/Input.js +50 -0
  521. package/ui/js/Jump.js +200 -0
  522. package/ui/js/KeyName.js +64 -0
  523. package/ui/js/Keymap.js +401 -0
  524. package/ui/js/Kinds.js +164 -0
  525. package/ui/js/LICENSES/ISC-lucide.txt +43 -0
  526. package/ui/js/LangMarkPaths.js +47 -0
  527. package/ui/js/LangMarks.js +120 -0
  528. package/ui/js/Lists.js +11 -0
  529. package/ui/js/LocalSend.js +39 -0
  530. package/ui/js/LockedMenu.js +63 -0
  531. package/ui/js/MakeDefault.js +186 -0
  532. package/ui/js/MarkBox.js +7 -0
  533. package/ui/js/Markdown.js +119 -0
  534. package/ui/js/MarkdownLists.js +86 -0
  535. package/ui/js/MarkdownMaths.js +58 -0
  536. package/ui/js/MarkdownPictures.js +32 -0
  537. package/ui/js/MarkdownPrepared.js +139 -0
  538. package/ui/js/MarkdownTableFit.js +34 -0
  539. package/ui/js/Marks.js +148 -0
  540. package/ui/js/Marquee.js +96 -0
  541. package/ui/js/Match.js +93 -0
  542. package/ui/js/Material.js +109 -0
  543. package/ui/js/MdBlocks.js +437 -0
  544. package/ui/js/MdBreak.js +71 -0
  545. package/ui/js/MdChunks.js +70 -0
  546. package/ui/js/MdContainer.js +137 -0
  547. package/ui/js/MdDocument.js +337 -0
  548. package/ui/js/MdEmph.js +97 -0
  549. package/ui/js/MdEntity.js +108 -0
  550. package/ui/js/MdEntityTable.js +223 -0
  551. package/ui/js/MdEscape.js +59 -0
  552. package/ui/js/MdFront.js +37 -0
  553. package/ui/js/MdHold.js +32 -0
  554. package/ui/js/MdHtml.js +307 -0
  555. package/ui/js/MdHtmlBlock.js +290 -0
  556. package/ui/js/MdHtmlHeading.js +53 -0
  557. package/ui/js/MdHtmlImage.js +179 -0
  558. package/ui/js/MdInline.js +154 -0
  559. package/ui/js/MdItems.js +212 -0
  560. package/ui/js/MdLeaf.js +309 -0
  561. package/ui/js/MdLink.js +196 -0
  562. package/ui/js/MdMath.js +67 -0
  563. package/ui/js/MdParagraphs.js +138 -0
  564. package/ui/js/MdRefs.js +232 -0
  565. package/ui/js/MdResolve.js +226 -0
  566. package/ui/js/MdRun.js +300 -0
  567. package/ui/js/MdUrl.js +226 -0
  568. package/ui/js/Menu.js +514 -0
  569. package/ui/js/MenuContext.js +85 -0
  570. package/ui/js/MenuFit.js +13 -0
  571. package/ui/js/MenuRefresh.js +74 -0
  572. package/ui/js/MenuWheel.js +35 -0
  573. package/ui/js/Messages.js +186 -0
  574. package/ui/js/Motion.js +16 -0
  575. package/ui/js/Mounts.js +320 -0
  576. package/ui/js/MouseNav.js +13 -0
  577. package/ui/js/Nav.js +329 -0
  578. package/ui/js/NetFs.js +26 -0
  579. package/ui/js/OpenWith.js +83 -0
  580. package/ui/js/Ops.js +585 -0
  581. package/ui/js/Palette.js +33 -0
  582. package/ui/js/PathBar.js +260 -0
  583. package/ui/js/Permissions.js +179 -0
  584. package/ui/js/Phones.js +112 -0
  585. package/ui/js/Picker.js +353 -0
  586. package/ui/js/PlaceMenu.js +96 -0
  587. package/ui/js/Places.js +265 -0
  588. package/ui/js/PreviewAsk.js +21 -0
  589. package/ui/js/PreviewKeys.js +155 -0
  590. package/ui/js/PreviewSettle.js +29 -0
  591. package/ui/js/PreviewSwap.js +101 -0
  592. package/ui/js/PropertyFacts.js +144 -0
  593. package/ui/js/Protocols.js +296 -0
  594. package/ui/js/QuickLookKinds.js +27 -0
  595. package/ui/js/QuickLookRect.js +24 -0
  596. package/ui/js/RailDrop.js +51 -0
  597. package/ui/js/RailKeys.js +87 -0
  598. package/ui/js/RailMenu.js +133 -0
  599. package/ui/js/Recent.js +135 -0
  600. package/ui/js/RecentMode.js +156 -0
  601. package/ui/js/Reload.js +60 -0
  602. package/ui/js/Renderer.js +9 -0
  603. package/ui/js/RunFile.js +143 -0
  604. package/ui/js/Scripts.js +28 -0
  605. package/ui/js/Scroll.js +304 -0
  606. package/ui/js/ScrollOff.js +69 -0
  607. package/ui/js/Search.js +478 -0
  608. package/ui/js/Selection.js +97 -0
  609. package/ui/js/Settings.js +476 -0
  610. package/ui/js/SettingsAbout.js +97 -0
  611. package/ui/js/SettingsShelf.js +52 -0
  612. package/ui/js/SheetKeys.js +42 -0
  613. package/ui/js/SheetQuery.js +281 -0
  614. package/ui/js/ShelfPile.js +27 -0
  615. package/ui/js/SlowClick.js +53 -0
  616. package/ui/js/SlowOp.js +6 -0
  617. package/ui/js/Sort.js +134 -0
  618. package/ui/js/Startup.js +63 -0
  619. package/ui/js/StateWords.js +17 -0
  620. package/ui/js/Status.js +238 -0
  621. package/ui/js/Swap.js +166 -0
  622. package/ui/js/TabMove.js +39 -0
  623. package/ui/js/Tabs.js +751 -0
  624. package/ui/js/Tags.js +134 -0
  625. package/ui/js/Taildrop.js +128 -0
  626. package/ui/js/Tap.js +180 -0
  627. package/ui/js/TextSize.js +107 -0
  628. package/ui/js/Thumbs.js +158 -0
  629. package/ui/js/Transfer.js +172 -0
  630. package/ui/js/Trash.js +84 -0
  631. package/ui/js/TrashDates.js +47 -0
  632. package/ui/js/TrashDrop.js +64 -0
  633. package/ui/js/UiState.js +438 -0
  634. package/ui/js/Unlock.js +152 -0
  635. package/ui/js/Update.js +130 -0
  636. package/ui/js/UserDirs.js +56 -0
  637. package/ui/qmldir +230 -0
@@ -0,0 +1,624 @@
1
+ import QtQuick
2
+ import QtQuick.Controls as QQC
3
+ import QtQuick.Window
4
+ import Quickshell.Io
5
+ import qs.Commons
6
+ import qs.Commons as Commons
7
+
8
+ // Searchable multi-select dropdown. Trigger shape matches Dropdown /
9
+ // SearchableDropdown; the popup shows a search field, an optional refresh
10
+ // button, and a checkbox list. Click rows to toggle. Use when callers
11
+ // need to pick zero or more items from a (possibly long, possibly
12
+ // dynamic) list.
13
+ //
14
+ // Options are either:
15
+ // - static `options`: string[] or [{ value, label, description? }]
16
+ // - dynamic `optionsCommand`: argv array. The command's stdout is
17
+ // parsed as JSON when it trims to start with `[`, otherwise as
18
+ // one option per non-empty newline. Re-runs whenever the popup opens
19
+ // and via the refresh button.
20
+ //
21
+ // `values` is the persisted selection — always an array of strings.
22
+ // Emits `changed(values)` whenever the selection mutates.
23
+ Item {
24
+ id: root
25
+
26
+ property string label: ""
27
+ property var values: []
28
+ property var options: []
29
+ property var optionsCommand: []
30
+ property string optionsCommandCwd: ""
31
+ property string placeholderText: Commons.I18n.t("common.search")
32
+ property string emptyText: Commons.I18n.t("common.noOptions")
33
+ property string noSelectionText: Commons.I18n.t("common.noneSelected")
34
+ property string triggerLabel: ""
35
+ property bool showLabel: true
36
+
37
+ property color foreground: Commons.Color.popups.text
38
+ property color background: Commons.Color.popups.background
39
+ property color popupBorder: Commons.Color.popups.border
40
+ property color accent: Commons.Color.accent
41
+ readonly property var popupBorderSpec: Border.localOrSurfaceSpec("popups", "border", popupBorder, Commons.Color.popups.border, Style.normalBorderWidth)
42
+ property string fontFamily: Style.font.family
43
+ property int rowHeight: Style.spacing.controlHeight
44
+ property int popupRowHeight: Style.spacing.popupRowHeight
45
+ property int popupMinHeight: Style.spacing.searchablePopupMinHeight
46
+
47
+ property bool hasCursor: false
48
+
49
+ readonly property bool popupOpen: popup.opened
50
+ function open() { popup.open() }
51
+ function close() { popup.close() }
52
+ function toggle() { popup.opened ? popup.close() : popup.open() }
53
+
54
+ signal changed(var values)
55
+ signal hovered(bool isHovered)
56
+
57
+ // Loaded options after merging static + dynamic. Always normalized
58
+ // into the [{ value, label, description }] shape for delegate use.
59
+ property var resolvedOptions: []
60
+ property bool loadingOptions: false
61
+ property string optionsError: ""
62
+
63
+ function normalizeOption(o) {
64
+ if (o && typeof o === "object") {
65
+ return {
66
+ value: String(o.value),
67
+ label: String(o.label !== undefined ? o.label : o.value),
68
+ description: o.description ? String(o.description) : ""
69
+ }
70
+ }
71
+ var s = String(o)
72
+ return { value: s, label: s, description: "" }
73
+ }
74
+
75
+ // QML schema arrays sometimes arrive as JSValue lists that fail
76
+ // `Array.isArray`. arrayFrom accepts anything array-like (`.length`
77
+ // numeric) and returns a real JS array so the rest of the component can
78
+ // rely on standard array operations.
79
+ function arrayFrom(v) {
80
+ if (!v || typeof v.length !== "number" || typeof v === "string") return []
81
+ var out = []
82
+ for (var i = 0; i < v.length; i++) out.push(v[i])
83
+ return out
84
+ }
85
+
86
+ function normalizeAll(arr) {
87
+ var out = []
88
+ var src = arrayFrom(arr)
89
+ var seen = ({})
90
+ for (var i = 0; i < src.length; i++) {
91
+ var n = normalizeOption(src[i])
92
+ if (!n.value) continue
93
+ if (seen[n.value]) continue
94
+ seen[n.value] = true
95
+ out.push(n)
96
+ }
97
+ return out
98
+ }
99
+
100
+ function valueSet() {
101
+ var set = ({})
102
+ var arr = arrayFrom(values)
103
+ for (var i = 0; i < arr.length; i++) set[String(arr[i])] = true
104
+ return set
105
+ }
106
+
107
+ function isSelected(value) {
108
+ var set = valueSet()
109
+ return !!set[String(value)]
110
+ }
111
+
112
+ function toggleValue(value) {
113
+ var v = String(value)
114
+ var arr = arrayFrom(values)
115
+ var idx = arr.indexOf(v)
116
+ if (idx === -1) arr.push(v)
117
+ else arr.splice(idx, 1)
118
+ root.values = arr
119
+ root.changed(arr)
120
+ }
121
+
122
+ function selectionLabel() {
123
+ var arr = arrayFrom(values)
124
+ if (arr.length === 0) return ""
125
+ var labels = []
126
+ var byValue = ({})
127
+ for (var i = 0; i < resolvedOptions.length; i++)
128
+ byValue[resolvedOptions[i].value] = resolvedOptions[i].label
129
+ for (var j = 0; j < arr.length; j++)
130
+ labels.push(byValue[String(arr[j])] || String(arr[j]))
131
+ if (labels.length <= 3) return labels.join(", ")
132
+ return Commons.I18n.tn("multiSelect.selectedCount", arr.length).arg(Commons.I18n.formatNumber(arr.length))
133
+ }
134
+
135
+ property var filtered: resolvedOptions
136
+ function recomputeFiltered() {
137
+ var q = searchField.text.toLowerCase()
138
+ if (!q) { filtered = resolvedOptions; return }
139
+ var out = []
140
+ for (var i = 0; i < resolvedOptions.length; i++) {
141
+ var o = resolvedOptions[i]
142
+ if (o.label.toLowerCase().indexOf(q) !== -1
143
+ || o.description.toLowerCase().indexOf(q) !== -1
144
+ || o.value.toLowerCase().indexOf(q) !== -1) out.push(o)
145
+ }
146
+ filtered = out
147
+ }
148
+
149
+ function rebuildFromStatic() {
150
+ resolvedOptions = normalizeAll(options)
151
+ recomputeFiltered()
152
+ }
153
+
154
+ // Parse stdout from a dynamic optionsCommand into `{ options, error }`.
155
+ // Output starting with `[` is parsed strictly as JSON — a malformed array
156
+ // surfaces as an error rather than silently falling back to newline
157
+ // parsing, which would render the broken text as a literal option label.
158
+ // Output not starting with `[` is treated as one value per non-empty line.
159
+ function parseCommandOutput(text) {
160
+ var raw = String(text || "").trim()
161
+ if (raw === "") return { options: [], error: "" }
162
+ if (raw.charAt(0) === "[") {
163
+ try {
164
+ var parsed = JSON.parse(raw)
165
+ return { options: parsed, error: "" }
166
+ } catch (e) {
167
+ return { options: [], error: Commons.I18n.t("multiSelect.invalidJson") }
168
+ }
169
+ }
170
+ var out = []
171
+ var lines = raw.split(/\r?\n/)
172
+ for (var i = 0; i < lines.length; i++) {
173
+ var line = lines[i].trim()
174
+ if (line !== "") out.push(line)
175
+ }
176
+ return { options: out, error: "" }
177
+ }
178
+
179
+ // Monotonic request id so stale stdout/exit signals from a previous
180
+ // refresh can't clobber the resolvedOptions of a newer refresh, and
181
+ // a runaway command can be detected after a timeout.
182
+ property int refreshSeq: 0
183
+ readonly property int refreshTimeoutMs: 6000
184
+
185
+ function refresh() {
186
+ var cmd = arrayFrom(optionsCommand)
187
+ if (cmd.length === 0) {
188
+ rebuildFromStatic()
189
+ return
190
+ }
191
+ refreshSeq++
192
+ loadingOptions = true
193
+ optionsError = ""
194
+ optionsProcess.command = cmd
195
+ optionsProcess.workingDirectory = optionsCommandCwd
196
+ optionsProcess.running = false
197
+ optionsProcess.running = true
198
+ refreshTimeoutTimer.restart()
199
+ }
200
+
201
+ Timer {
202
+ id: refreshTimeoutTimer
203
+ interval: root.refreshTimeoutMs
204
+ repeat: false
205
+ onTriggered: {
206
+ if (!root.loadingOptions) return
207
+ optionsProcess.running = false
208
+ root.loadingOptions = false
209
+ root.optionsError = Commons.I18n.t("multiSelect.timedOut")
210
+ }
211
+ }
212
+
213
+ onOptionsChanged: if (arrayFrom(optionsCommand).length === 0) rebuildFromStatic()
214
+ onOptionsCommandChanged: refresh()
215
+ Component.onCompleted: refresh()
216
+
217
+ Process {
218
+ id: optionsProcess
219
+ running: false
220
+ command: []
221
+
222
+ property int seq: 0
223
+
224
+ stdout: StdioCollector {
225
+ waitForEnd: true
226
+ onStreamFinished: {
227
+ if (optionsProcess.seq !== root.refreshSeq) return
228
+ var result = root.parseCommandOutput(text)
229
+ if (result.error) {
230
+ root.optionsError = result.error
231
+ root.resolvedOptions = root.normalizeAll(root.options)
232
+ } else {
233
+ var combined = root.arrayFrom(root.options)
234
+ for (var j = 0; j < result.options.length; j++) combined.push(result.options[j])
235
+ root.resolvedOptions = root.normalizeAll(combined)
236
+ }
237
+ root.recomputeFiltered()
238
+ root.loadingOptions = false
239
+ refreshTimeoutTimer.stop()
240
+ }
241
+ }
242
+
243
+ onRunningChanged: if (running) seq = root.refreshSeq
244
+
245
+ onExited: function(exitCode, exitStatus) {
246
+ if (seq !== root.refreshSeq) return
247
+ refreshTimeoutTimer.stop()
248
+ if (exitCode !== 0) {
249
+ root.loadingOptions = false
250
+ root.optionsError = Commons.I18n.t("multiSelect.exited").arg(Commons.I18n.formatNumber(exitCode))
251
+ }
252
+ }
253
+ }
254
+
255
+ implicitWidth: Style.spacing.searchableDropdownWidth
256
+ implicitHeight: showLabel && label !== "" ? rowHeight + Style.spacing.huge : rowHeight
257
+
258
+ Column {
259
+ anchors.fill: parent
260
+ spacing: Style.spacing.labelGap
261
+
262
+ Text {
263
+ textFormat: Text.PlainText
264
+ visible: root.showLabel && root.label !== ""
265
+ text: root.label
266
+ color: Qt.darker(root.foreground, 1.4)
267
+ font.family: root.fontFamily
268
+ font.pixelSize: Style.font.caption
269
+ font.bold: true
270
+ }
271
+
272
+ BorderSurface {
273
+ id: trigger
274
+ width: parent.width
275
+ height: root.rowHeight
276
+ radius: Style.cornerRadius
277
+
278
+ readonly property bool _focused: trigger.activeFocus
279
+ readonly property bool _hot: triggerHover.hovered || root.hasCursor
280
+ readonly property var _borderSpec: Border.controlSpec(trigger._focused ? "focus" : (trigger._hot ? "hover-cursor" : "normal"), root.foreground, root.accent)
281
+
282
+ color: Style.controlFill(trigger._focused, trigger._hot, root.foreground, root.accent)
283
+ borderSpec: _borderSpec
284
+
285
+ activeFocusOnTab: true
286
+
287
+ HoverHandler {
288
+ id: triggerHover
289
+ onHoveredChanged: root.hovered(hovered)
290
+ }
291
+
292
+ Keys.onPressed: function(event) {
293
+ if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter
294
+ || event.key === Qt.Key_Space || event.key === Qt.Key_Down) {
295
+ popup.opened ? popup.close() : popup.open()
296
+ event.accepted = true
297
+ } else if (event.key === Qt.Key_Escape && popup.opened) {
298
+ popup.close(); event.accepted = true
299
+ }
300
+ }
301
+
302
+ Text {
303
+ textFormat: Text.PlainText
304
+ anchors.left: parent.left
305
+ anchors.right: chevron.left
306
+ anchors.verticalCenter: parent.verticalCenter
307
+ anchors.leftMargin: trigger.borderLeft + Style.spacing.controlPaddingX
308
+ anchors.rightMargin: trigger.borderRight + Style.spacing.md
309
+ text: root.selectionLabel() || root.triggerLabel || root.noSelectionText
310
+ color: root.selectionLabel() ? root.foreground : Qt.darker(root.foreground, 1.5)
311
+ font.family: root.fontFamily
312
+ font.pixelSize: Style.font.body
313
+ elide: Text.ElideRight
314
+ }
315
+
316
+ Text {
317
+ id: chevron
318
+ anchors.right: parent.right
319
+ anchors.verticalCenter: parent.verticalCenter
320
+ anchors.rightMargin: trigger.borderRight + Style.spacing.controlGap
321
+ text: "󰅀"
322
+ color: Qt.darker(root.foreground, 1.2)
323
+ font.family: root.fontFamily
324
+ font.pixelSize: Style.font.body
325
+ }
326
+
327
+ MouseArea {
328
+ anchors.fill: parent
329
+ cursorShape: Qt.PointingHandCursor
330
+ onClicked: {
331
+ trigger.forceActiveFocus()
332
+ popup.opened ? popup.close() : popup.open()
333
+ }
334
+ }
335
+
336
+ QQC.Popup {
337
+ id: popup
338
+ // Reparent to the window's content item so the popup is free of any
339
+ // clipping ancestor. Position
340
+ // and available height are recomputed on open and any time the
341
+ // trigger's geometry changes, since a binding on mapToItem alone
342
+ // won't reliably re-evaluate when ancestors scroll or resize.
343
+ parent: trigger.Window.window ? trigger.Window.window.contentItem : trigger
344
+ property real _anchorX: 0
345
+ property real _anchorY: 0
346
+ property real _availableBelow: 0
347
+ readonly property real _windowHeight: parent ? parent.height : 0
348
+ readonly property real _idealContent: resultList.contentHeight + Style.space(50)
349
+ readonly property real _maxRowsHeight: root.popupRowHeight * 6 + 5 * Style.spacing.labelGap + Style.space(50)
350
+
351
+ function reposition() {
352
+ if (!parent) return
353
+ var p = trigger.mapToItem(parent, 0, trigger.height + Style.spacing.xxs)
354
+ _anchorX = p.x
355
+ _anchorY = p.y
356
+ _availableBelow = Math.max(0, _windowHeight - _anchorY - Style.space(12))
357
+ }
358
+
359
+ x: _anchorX
360
+ y: _anchorY
361
+ width: trigger.width
362
+ // Clamp to whatever fits below the trigger; don't force popupMinHeight
363
+ // when there isn't room, otherwise the popup overflows the window.
364
+ implicitHeight: Math.min(_availableBelow, _idealContent, _maxRowsHeight)
365
+ padding: Style.spacing.hairline
366
+ leftPadding: Border.left(root.popupBorderSpec) + Style.spacing.hairline
367
+ rightPadding: Border.right(root.popupBorderSpec) + Style.spacing.hairline
368
+ topPadding: Border.top(root.popupBorderSpec) + Style.spacing.hairline
369
+ bottomPadding: Border.bottom(root.popupBorderSpec) + Style.spacing.hairline
370
+ focus: true
371
+
372
+ Connections {
373
+ target: trigger
374
+ function onXChanged() { popup.reposition() }
375
+ function onYChanged() { popup.reposition() }
376
+ function onWidthChanged() { popup.reposition() }
377
+ function onHeightChanged() { popup.reposition() }
378
+ }
379
+
380
+ background: BorderSurface {
381
+ color: root.background
382
+ borderSpec: root.popupBorderSpec
383
+ radius: Style.cornerRadius
384
+ }
385
+
386
+ onOpened: {
387
+ reposition()
388
+ searchField.text = ""
389
+ root.refresh()
390
+ root.recomputeFiltered()
391
+ Qt.callLater(function() { searchField.forceActiveFocus() })
392
+ }
393
+ onClosed: searchField.text = ""
394
+
395
+ contentItem: Column {
396
+ spacing: 0
397
+
398
+ Item {
399
+ id: searchHeader
400
+ width: parent.width
401
+ height: root.popupRowHeight + Style.spacing.controlPaddingX
402
+
403
+ Row {
404
+ anchors.fill: parent
405
+ anchors.margins: Style.spacing.md
406
+ spacing: Style.spacing.rowGap
407
+
408
+ TextField {
409
+ id: searchField
410
+ width: parent.width - refreshButton.width - parent.spacing
411
+ height: parent.height
412
+ placeholderText: root.placeholderText
413
+ foreground: root.foreground
414
+ accent: root.accent
415
+ font.family: root.fontFamily
416
+ font.pixelSize: Style.font.body
417
+
418
+ onTextChanged: {
419
+ root.recomputeFiltered()
420
+ if (resultList.count > 0) resultList.currentIndex = 0
421
+ }
422
+
423
+ Keys.onPressed: function(event) {
424
+ if (event.key === Qt.Key_Escape) {
425
+ popup.close(); event.accepted = true
426
+ } else if (event.key === Qt.Key_Down) {
427
+ if (resultList.count > 0) {
428
+ resultList.currentIndex = 0
429
+ resultList.forceActiveFocus()
430
+ }
431
+ event.accepted = true
432
+ } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
433
+ if (resultList.count > 0) {
434
+ resultList.currentIndex = 0
435
+ resultList.toggleCurrent()
436
+ }
437
+ event.accepted = true
438
+ }
439
+ }
440
+ }
441
+
442
+ BorderSurface {
443
+ id: refreshButton
444
+ visible: root.arrayFrom(root.optionsCommand).length > 0
445
+ enabled: !root.loadingOptions
446
+ width: parent.height
447
+ height: parent.height
448
+ radius: Style.cornerRadius
449
+ color: refreshHover.hovered
450
+ ? Style.hoverFillFor(root.foreground, root.accent)
451
+ : Style.normalFillFor(root.foreground, root.accent)
452
+ borderSpec: refreshHover.hovered
453
+ ? Border.controlSpec("hover-cursor", root.foreground, root.accent)
454
+ : Border.controlSpec("normal", root.foreground, root.accent)
455
+
456
+ Text {
457
+ textFormat: Text.PlainText
458
+ anchors.centerIn: parent
459
+ text: root.loadingOptions ? "󰦖" : "󰑐"
460
+ color: root.foreground
461
+ font.family: root.fontFamily
462
+ font.pixelSize: Style.font.body
463
+
464
+ RotationAnimator on rotation {
465
+ running: root.loadingOptions && !Style.reduceMotion
466
+ from: 0; to: 360
467
+ duration: 800
468
+ loops: Animation.Infinite
469
+ }
470
+ }
471
+
472
+ HoverHandler { id: refreshHover }
473
+ MouseArea {
474
+ anchors.fill: parent
475
+ cursorShape: Qt.PointingHandCursor
476
+ onClicked: root.refresh()
477
+ }
478
+ }
479
+ }
480
+ }
481
+
482
+ Rectangle {
483
+ width: parent.width
484
+ height: 1
485
+ color: Util.alpha(root.foreground, 0.10)
486
+ }
487
+
488
+ Item {
489
+ width: parent.width
490
+ height: popup.height - searchHeader.height - Style.spacing.xxs - 1
491
+
492
+ Text {
493
+ textFormat: Text.PlainText
494
+ anchors.centerIn: parent
495
+ visible: resultList.count === 0
496
+ text: root.loadingOptions ? Commons.I18n.t("common.loading") : (root.optionsError !== "" ? root.optionsError : root.emptyText)
497
+ color: Qt.darker(root.foreground, 1.6)
498
+ font.family: root.fontFamily
499
+ font.pixelSize: Style.font.body
500
+ }
501
+
502
+ ListView {
503
+ id: resultList
504
+ anchors.fill: parent
505
+ spacing: Style.spacing.labelGap
506
+ clip: true
507
+ boundsBehavior: Flickable.StopAtBounds
508
+ model: root.filtered
509
+ currentIndex: -1
510
+ keyNavigationEnabled: false
511
+
512
+ function toggleCurrent() {
513
+ if (currentIndex < 0 || currentIndex >= root.filtered.length) return
514
+ root.toggleValue(root.filtered[currentIndex].value)
515
+ }
516
+
517
+ Keys.priority: Keys.BeforeItem
518
+ Keys.onPressed: function(event) {
519
+ if (event.key === Qt.Key_Escape) {
520
+ popup.close(); event.accepted = true
521
+ } else if (event.key === Qt.Key_Down || event.text === "j") {
522
+ if (resultList.currentIndex >= resultList.count - 1) {
523
+ event.accepted = true; return
524
+ }
525
+ resultList.currentIndex = resultList.currentIndex + 1
526
+ event.accepted = true
527
+ } else if (event.key === Qt.Key_Up || event.text === "k") {
528
+ if (resultList.currentIndex <= 0) {
529
+ searchField.forceActiveFocus()
530
+ event.accepted = true; return
531
+ }
532
+ resultList.currentIndex = resultList.currentIndex - 1
533
+ event.accepted = true
534
+ } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter
535
+ || event.key === Qt.Key_Space) {
536
+ resultList.toggleCurrent(); event.accepted = true
537
+ }
538
+ }
539
+
540
+ delegate: Rectangle {
541
+ required property var modelData
542
+ required property int index
543
+
544
+ readonly property bool selected: root.isSelected(modelData.value)
545
+
546
+ width: resultList.width
547
+ height: Math.max(root.popupRowHeight, rowContent.implicitHeight + Style.spacing.rowPaddingX)
548
+ color: index === resultList.currentIndex
549
+ ? Style.hoverFillFor(root.foreground, root.accent)
550
+ : "transparent"
551
+
552
+ Row {
553
+ id: rowContent
554
+ anchors.left: parent.left
555
+ anchors.right: parent.right
556
+ anchors.verticalCenter: parent.verticalCenter
557
+ anchors.leftMargin: Style.spacing.controlPaddingX
558
+ anchors.rightMargin: Style.spacing.controlPaddingX
559
+ spacing: Style.spacing.rowGap
560
+
561
+ BorderSurface {
562
+ id: checkbox
563
+ width: Style.space(16)
564
+ height: Style.space(16)
565
+ radius: Math.max(2, Style.cornerRadius / 2)
566
+ anchors.verticalCenter: parent.verticalCenter
567
+ color: selected ? Style.selectedFillFor(root.foreground, root.accent) : "transparent"
568
+ borderSpec: selected
569
+ ? Border.controlSpec("selected", root.foreground, root.accent)
570
+ : Border.controlSpec("normal", root.foreground, root.accent)
571
+
572
+ Text {
573
+ anchors.centerIn: parent
574
+ visible: selected
575
+ text: "✓"
576
+ color: Style.selectedStateColor(root.foreground, root.accent)
577
+ font.family: root.fontFamily
578
+ font.pixelSize: Math.round(checkbox.height * 0.85)
579
+ font.bold: true
580
+ }
581
+ }
582
+
583
+ Column {
584
+ width: parent.width - checkbox.width - parent.spacing
585
+ anchors.verticalCenter: parent.verticalCenter
586
+ spacing: Style.spacing.xxs
587
+
588
+ Text {
589
+ textFormat: Text.PlainText
590
+ text: modelData.label
591
+ color: index === resultList.currentIndex ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground
592
+ font.family: root.fontFamily
593
+ font.pixelSize: Style.font.body
594
+ elide: Text.ElideRight
595
+ width: parent.width
596
+ }
597
+ Text {
598
+ textFormat: Text.PlainText
599
+ visible: text !== ""
600
+ text: modelData.description
601
+ color: Qt.darker(root.foreground, 1.5)
602
+ font.family: root.fontFamily
603
+ font.pixelSize: Style.font.caption
604
+ elide: Text.ElideRight
605
+ width: parent.width
606
+ }
607
+ }
608
+ }
609
+
610
+ MouseArea {
611
+ anchors.fill: parent
612
+ hoverEnabled: true
613
+ cursorShape: Qt.PointingHandCursor
614
+ onPositionChanged: resultList.currentIndex = parent.index
615
+ onClicked: root.toggleValue(modelData.value)
616
+ }
617
+ }
618
+ }
619
+ }
620
+ }
621
+ }
622
+ }
623
+ }
624
+ }
@@ -0,0 +1,86 @@
1
+ import QtQuick
2
+ import QtQuick.Controls as QQC
3
+ import qs.Commons
4
+ import qs.Commons as Commons
5
+
6
+ Column {
7
+ id: root
8
+
9
+ property string label: ""
10
+ property int value: 0
11
+ property int from: 0
12
+ property int to: 100
13
+ property int stepSize: 1
14
+ property color foreground: Commons.Color.foreground
15
+ property color accent: Commons.Color.accent
16
+ property string fontFamily: Style.font.family
17
+ property real fontSize: Style.font.body
18
+ property real fieldWidth: Style.spacing.numberFieldWidth
19
+ property bool hasCursor: false
20
+ property bool _hovered: false
21
+ property alias field: spin
22
+
23
+ signal modified(int value)
24
+ signal hovered(bool on)
25
+
26
+ spacing: Style.spacing.md
27
+
28
+ Text {
29
+ textFormat: Text.PlainText
30
+ visible: root.label !== ""
31
+ text: root.label
32
+ color: Qt.darker(root.foreground, 1.4)
33
+ font.family: root.fontFamily
34
+ font.pixelSize: Style.font.bodySmall
35
+ }
36
+
37
+ QQC.SpinBox {
38
+ id: spin
39
+ width: root.fieldWidth
40
+ implicitHeight: Math.max(Style.spacing.controlHeight, root.fontSize + Style.spacing.controlPaddingY * 2)
41
+ from: root.from
42
+ to: root.to
43
+ stepSize: root.stepSize
44
+ value: root.value
45
+ editable: true
46
+ font.family: root.fontFamily
47
+ font.pixelSize: root.fontSize
48
+
49
+ readonly property bool _focused: spin.activeFocus
50
+ readonly property bool _hot: root._hovered || root.hasCursor
51
+ readonly property var _borderSpec: Border.controlSpec(_focused ? "focus" : (_hot ? "hover-cursor" : "normal"), root.foreground, root.accent)
52
+
53
+ leftPadding: Border.left(_borderSpec) + Style.spacing.controlPaddingX
54
+ rightPadding: Border.right(_borderSpec) + Style.spacing.controlPaddingX
55
+ topPadding: Border.top(_borderSpec)
56
+ bottomPadding: Border.bottom(_borderSpec)
57
+
58
+ onValueModified: root.modified(value)
59
+
60
+ background: BorderSurface {
61
+ color: Style.controlFill(spin._focused, spin._hot, root.foreground, root.accent)
62
+ borderSpec: spin._borderSpec
63
+ radius: Style.cornerRadius
64
+
65
+ HoverHandler {
66
+ onHoveredChanged: {
67
+ root._hovered = hovered
68
+ root.hovered(hovered)
69
+ }
70
+ }
71
+ }
72
+
73
+ contentItem: TextInput {
74
+ text: spin.displayText
75
+ font: spin.font
76
+ color: root.foreground
77
+ selectionColor: Style.selectionFillFor(root.foreground, root.accent)
78
+ selectedTextColor: root.foreground
79
+ horizontalAlignment: Qt.AlignHCenter
80
+ verticalAlignment: Qt.AlignVCenter
81
+ readOnly: !spin.editable
82
+ validator: spin.validator
83
+ inputMethodHints: Qt.ImhFormattedNumbersOnly
84
+ }
85
+ }
86
+ }