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,354 @@
1
+ import QtQuick
2
+ import QtQuick.Controls as QQC
3
+ import qs.Commons
4
+ import qs.Commons as Commons
5
+
6
+ // Searchable single-select dropdown. Same trigger shape as Dropdown, but
7
+ // the popup leads with an embedded TextField that filters the option
8
+ // list in real time. Use for pickers with enough options that scanning
9
+ // is friction.
10
+ //
11
+ // Filtering is case-insensitive substring against each option's label.
12
+ // Options can be string[] or [{ value, label, description? }] — the same
13
+ // shape Dropdown accepts. The filter clears whenever the popup closes.
14
+ //
15
+ // Keyboard: Tab to focus the trigger, Enter/Space opens (search focused
16
+ // immediately). Down arrow from the search jumps to the first match;
17
+ // Up from the first match returns to the search. Enter selects, Esc
18
+ // closes (and clears the filter).
19
+ Item {
20
+ id: root
21
+
22
+ property string label: ""
23
+ property string value: ""
24
+ property var options: []
25
+ property string placeholderText: Commons.I18n.t("common.search")
26
+ property string emptyText: Commons.I18n.t("common.noMatches")
27
+ property string triggerLabel: ""
28
+
29
+ property color foreground: Commons.Color.popups.text
30
+ property color background: Commons.Color.popups.background
31
+ property color popupBorder: Commons.Color.popups.border
32
+ property color accent: Commons.Color.accent
33
+ readonly property var popupBorderSpec: Border.localOrSurfaceSpec("popups", "border", popupBorder, Commons.Color.popups.border, Style.normalBorderWidth)
34
+ property string fontFamily: Style.font.family
35
+ property int rowHeight: Style.spacing.controlHeight
36
+ property int popupRowHeight: Style.spacing.popupRowHeight
37
+ property int popupMinHeight: Style.spacing.searchablePopupMinHeight
38
+ property bool showLabel: true
39
+
40
+ // Panel-cursor flag. When true, the trigger renders the shared
41
+ // hover-cursor state. Active Qt focus defaults to the same visuals.
42
+ // Emits `hovered(bool)` on pointer enter/leave so the panel can keep
43
+ // its cursor state in sync with the mouse.
44
+ property bool hasCursor: false
45
+
46
+ // popupOpen + open()/close()/toggle() let a parent panel know when the
47
+ // dropdown owns keys (search field + result list are active) and
48
+ // suspend its own keyCatcher so typing into the filter doesn't drive
49
+ // the panel cursor.
50
+ readonly property bool popupOpen: popup.opened
51
+ function open() { popup.open() }
52
+ function close() { popup.close() }
53
+ function toggle() { popup.opened ? popup.close() : popup.open() }
54
+
55
+ signal changed(string value)
56
+ signal hovered(bool isHovered)
57
+
58
+ function optionValue(o) {
59
+ return (o && typeof o === "object") ? String(o.value) : String(o)
60
+ }
61
+ function optionLabel(o) {
62
+ return (o && typeof o === "object") ? String(o.label) : String(o)
63
+ }
64
+ function optionDescription(o) {
65
+ return (o && typeof o === "object" && o.description) ? String(o.description) : ""
66
+ }
67
+ function currentLabel() {
68
+ for (var i = 0; i < options.length; i++) {
69
+ if (optionValue(options[i]) === value) return optionLabel(options[i])
70
+ }
71
+ return value
72
+ }
73
+
74
+ property var filtered: options
75
+ function recomputeFiltered() {
76
+ var q = searchField.text.toLowerCase()
77
+ if (!q) { filtered = options; return }
78
+ var out = []
79
+ for (var i = 0; i < options.length; i++) {
80
+ var lbl = optionLabel(options[i]).toLowerCase()
81
+ var desc = optionDescription(options[i]).toLowerCase()
82
+ if (lbl.indexOf(q) !== -1 || desc.indexOf(q) !== -1) out.push(options[i])
83
+ }
84
+ filtered = out
85
+ }
86
+
87
+ onOptionsChanged: recomputeFiltered()
88
+
89
+ implicitWidth: Style.spacing.searchableDropdownWidth
90
+ implicitHeight: showLabel && label !== "" ? rowHeight + Style.spacing.huge : rowHeight
91
+
92
+ Column {
93
+ anchors.fill: parent
94
+ spacing: Style.spacing.labelGap
95
+
96
+ Text {
97
+ textFormat: Text.PlainText
98
+ visible: root.showLabel && root.label !== ""
99
+ text: root.label
100
+ color: Qt.darker(root.foreground, 1.4)
101
+ font.family: root.fontFamily
102
+ font.pixelSize: Style.font.caption
103
+ font.bold: true
104
+ }
105
+
106
+ BorderSurface {
107
+ id: trigger
108
+ width: parent.width
109
+ height: root.rowHeight
110
+ radius: Style.cornerRadius
111
+
112
+ readonly property bool _focused: trigger.activeFocus
113
+ readonly property bool _hot: triggerHover.hovered || root.hasCursor
114
+ readonly property var _borderSpec: Border.controlSpec(trigger._focused ? "focus" : (trigger._hot ? "hover-cursor" : "normal"), root.foreground, root.accent)
115
+
116
+ color: Style.controlFill(trigger._focused, trigger._hot, root.foreground, root.accent)
117
+ borderSpec: _borderSpec
118
+
119
+ activeFocusOnTab: true
120
+
121
+ HoverHandler {
122
+ id: triggerHover
123
+ onHoveredChanged: root.hovered(hovered)
124
+ }
125
+
126
+ Keys.onPressed: function(event) {
127
+ if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter
128
+ || event.key === Qt.Key_Space || event.key === Qt.Key_Down) {
129
+ popup.opened ? popup.close() : popup.open()
130
+ event.accepted = true
131
+ } else if (event.key === Qt.Key_Escape && popup.opened) {
132
+ popup.close(); event.accepted = true
133
+ }
134
+ }
135
+
136
+ Text {
137
+ textFormat: Text.PlainText
138
+ anchors.left: parent.left
139
+ anchors.right: chevron.left
140
+ anchors.verticalCenter: parent.verticalCenter
141
+ anchors.leftMargin: trigger.borderLeft + Style.spacing.controlPaddingX
142
+ anchors.rightMargin: trigger.borderRight + Style.spacing.md
143
+ text: root.currentLabel() || root.triggerLabel || root.placeholderText
144
+ color: (root.currentLabel() || root.triggerLabel) ? root.foreground : Qt.darker(root.foreground, 1.5)
145
+ font.family: root.fontFamily
146
+ font.pixelSize: Style.font.body
147
+ elide: Text.ElideRight
148
+ }
149
+
150
+ Text {
151
+ id: chevron
152
+ anchors.right: parent.right
153
+ anchors.verticalCenter: parent.verticalCenter
154
+ anchors.rightMargin: trigger.borderRight + Style.spacing.controlGap
155
+ text: "󰅀"
156
+ color: Qt.darker(root.foreground, 1.2)
157
+ font.family: root.fontFamily
158
+ font.pixelSize: Style.font.body
159
+ }
160
+
161
+ MouseArea {
162
+ anchors.fill: parent
163
+ cursorShape: Qt.PointingHandCursor
164
+ onClicked: {
165
+ trigger.forceActiveFocus()
166
+ popup.opened ? popup.close() : popup.open()
167
+ }
168
+ }
169
+
170
+ QQC.Popup {
171
+ id: popup
172
+ x: 0
173
+ y: trigger.height + Style.spacing.xxs
174
+ width: trigger.width
175
+ implicitHeight: Math.max(root.popupMinHeight,
176
+ Math.min(resultList.contentHeight + Style.space(50),
177
+ root.popupRowHeight * 6 + 5 * Style.spacing.labelGap + Style.space(50)))
178
+ padding: Style.spacing.hairline
179
+ leftPadding: Border.left(root.popupBorderSpec) + Style.spacing.hairline
180
+ rightPadding: Border.right(root.popupBorderSpec) + Style.spacing.hairline
181
+ topPadding: Border.top(root.popupBorderSpec) + Style.spacing.hairline
182
+ bottomPadding: Border.bottom(root.popupBorderSpec) + Style.spacing.hairline
183
+ focus: true
184
+
185
+ background: BorderSurface {
186
+ color: root.background
187
+ borderSpec: root.popupBorderSpec
188
+ radius: Style.cornerRadius
189
+ }
190
+
191
+ onOpened: {
192
+ searchField.text = ""
193
+ root.recomputeFiltered()
194
+ Qt.callLater(function() { searchField.forceActiveFocus() })
195
+ }
196
+ onClosed: searchField.text = ""
197
+
198
+ contentItem: Column {
199
+ spacing: 0
200
+
201
+ Item {
202
+ id: searchHeader
203
+ width: parent.width
204
+ height: root.popupRowHeight + Style.spacing.controlPaddingX
205
+
206
+ TextField {
207
+ id: searchField
208
+ anchors.fill: parent
209
+ anchors.margins: Style.spacing.md
210
+ placeholderText: root.placeholderText
211
+ foreground: root.foreground
212
+ accent: root.accent
213
+ font.family: root.fontFamily
214
+ font.pixelSize: Style.font.body
215
+
216
+ onTextChanged: {
217
+ root.recomputeFiltered()
218
+ if (resultList.count > 0) resultList.currentIndex = 0
219
+ }
220
+
221
+ Keys.onPressed: function(event) {
222
+ if (event.key === Qt.Key_Escape) {
223
+ popup.close(); event.accepted = true
224
+ } else if (event.key === Qt.Key_Down) {
225
+ if (resultList.count > 0) {
226
+ resultList.currentIndex = 0
227
+ resultList.forceActiveFocus()
228
+ }
229
+ event.accepted = true
230
+ } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
231
+ if (resultList.count > 0) {
232
+ resultList.currentIndex = 0
233
+ resultList.selectCurrent()
234
+ }
235
+ event.accepted = true
236
+ }
237
+ }
238
+ }
239
+ }
240
+
241
+ Rectangle {
242
+ width: parent.width
243
+ height: 1
244
+ color: Util.alpha(root.foreground, 0.10)
245
+ }
246
+
247
+ Item {
248
+ width: parent.width
249
+ height: popup.height - searchHeader.height - Style.spacing.xxs - 1
250
+
251
+ Text {
252
+ textFormat: Text.PlainText
253
+ anchors.centerIn: parent
254
+ visible: resultList.count === 0
255
+ text: root.emptyText
256
+ color: Qt.darker(root.foreground, 1.6)
257
+ font.family: root.fontFamily
258
+ font.pixelSize: Style.font.body
259
+ }
260
+
261
+ ListView {
262
+ id: resultList
263
+ anchors.fill: parent
264
+ spacing: Style.spacing.labelGap
265
+ clip: true
266
+ boundsBehavior: Flickable.StopAtBounds
267
+ model: root.filtered
268
+ currentIndex: -1
269
+ keyNavigationEnabled: false
270
+
271
+ function selectCurrent() {
272
+ if (currentIndex < 0 || currentIndex >= root.filtered.length) return
273
+ var v = root.optionValue(root.filtered[currentIndex])
274
+ root.value = v
275
+ root.changed(v)
276
+ popup.close()
277
+ }
278
+
279
+ Keys.priority: Keys.BeforeItem
280
+ Keys.onPressed: function(event) {
281
+ if (event.key === Qt.Key_Escape) {
282
+ popup.close(); event.accepted = true
283
+ } else if (event.key === Qt.Key_Down || event.text === "j") {
284
+ if (resultList.currentIndex >= resultList.count - 1) {
285
+ event.accepted = true; return
286
+ }
287
+ resultList.currentIndex = resultList.currentIndex + 1
288
+ event.accepted = true
289
+ } else if (event.key === Qt.Key_Up || event.text === "k") {
290
+ if (resultList.currentIndex <= 0) {
291
+ searchField.forceActiveFocus()
292
+ event.accepted = true; return
293
+ }
294
+ resultList.currentIndex = resultList.currentIndex - 1
295
+ event.accepted = true
296
+ } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
297
+ resultList.selectCurrent(); event.accepted = true
298
+ }
299
+ }
300
+
301
+ delegate: Rectangle {
302
+ required property var modelData
303
+ required property int index
304
+ width: resultList.width
305
+ height: Math.max(root.popupRowHeight, rowContent.implicitHeight + Style.spacing.rowPaddingX)
306
+ color: index === resultList.currentIndex
307
+ ? Style.hoverFillFor(root.foreground, root.accent)
308
+ : "transparent"
309
+
310
+ Column {
311
+ id: rowContent
312
+ anchors.left: parent.left
313
+ anchors.right: parent.right
314
+ anchors.verticalCenter: parent.verticalCenter
315
+ anchors.leftMargin: Style.spacing.controlPaddingX
316
+ anchors.rightMargin: Style.spacing.controlPaddingX
317
+ spacing: Style.spacing.xxs
318
+
319
+ Text {
320
+ textFormat: Text.PlainText
321
+ text: root.optionLabel(modelData)
322
+ color: index === resultList.currentIndex ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground
323
+ font.family: root.fontFamily
324
+ font.pixelSize: Style.font.body
325
+ elide: Text.ElideRight
326
+ width: parent.width
327
+ }
328
+ Text {
329
+ textFormat: Text.PlainText
330
+ visible: text !== ""
331
+ text: root.optionDescription(modelData)
332
+ color: Qt.darker(root.foreground, 1.5)
333
+ font.family: root.fontFamily
334
+ font.pixelSize: Style.font.caption
335
+ elide: Text.ElideRight
336
+ width: parent.width
337
+ }
338
+ }
339
+
340
+ MouseArea {
341
+ anchors.fill: parent
342
+ hoverEnabled: true
343
+ cursorShape: Qt.PointingHandCursor
344
+ onPositionChanged: resultList.currentIndex = parent.index
345
+ onClicked: resultList.selectCurrent()
346
+ }
347
+ }
348
+ }
349
+ }
350
+ }
351
+ }
352
+ }
353
+ }
354
+ }
@@ -0,0 +1,125 @@
1
+ import QtQuick
2
+
3
+ // An image fitted inside this item, or with `crop` covering it, preserving
4
+ // its aspect ratio, and decoded at about the size it is drawn, in physical
5
+ // pixels, instead of at the file's own size. A 2560x1440 screenshot shown as
6
+ // a 40px thumbnail would otherwise hold a 14 MB bitmap for as long as the
7
+ // thumbnail exists.
8
+ //
9
+ // The inner Image is not PreserveAspectFit or PreserveAspectCrop on purpose.
10
+ // With either, Qt scales the decode to cover sourceSize, which upscales an
11
+ // image narrower than the box: a 400x10 strip in a 400x400 box would decode at
12
+ // 16000x400. Without them, sourceSize is a bound Qt only ever shrinks the
13
+ // image to fit, so the bitmap is never larger than the bound or the file.
14
+ // This item then places the decoded image the way the fill mode would. A crop
15
+ // bounds the decode at twice the box, so images up to 2:1 still cover it at
16
+ // full resolution.
17
+ //
18
+ // That only holds for raster images. Qt renders an SVG at exactly sourceSize
19
+ // when the fill mode is not a preserving one, which would stretch it, and with
20
+ // a preserving one it covers the box like any other image. An SVG therefore
21
+ // keeps Qt's own handling: rendered at its intrinsic size and fitted or
22
+ // cropped on draw.
23
+ //
24
+ // The image waits until the item has been laid out, so nothing is decoded at
25
+ // native size first.
26
+ Item {
27
+ id: root
28
+
29
+ property url url: ""
30
+ property bool crop: false
31
+ property int horizontalAlignment: Image.AlignHCenter
32
+ property int verticalAlignment: Image.AlignVCenter
33
+ // Left and right swap in a mirrored (right-to-left) layout, as they do for
34
+ // a plain Image.
35
+ readonly property int effectiveHorizontalAlignment: !LayoutMirroring.enabled ? horizontalAlignment
36
+ : horizontalAlignment === Image.AlignLeft ? Image.AlignRight
37
+ : horizontalAlignment === Image.AlignRight ? Image.AlignLeft
38
+ : horizontalAlignment
39
+ property bool asynchronous: true
40
+ readonly property alias status: image.status
41
+ readonly property bool sized: width > 0 && height > 0
42
+ // Clipboard captures name the file after the MIME subtype, as in .svg+xml,
43
+ // and Util.fileUrl percent-encodes the plus.
44
+ readonly property bool scalable: /(\.svgz?|\.svg(\+|%2B)xml)([?#].*)?$|^data:image\/svg/i.test(String(url))
45
+ readonly property real reach: crop ? 2 : 1
46
+ // A strip thin enough that fitting it in the bound rounds its short side
47
+ // to nothing decodes empty. It is decoded again covering a single pixel
48
+ // instead: one pixel across and its aspect ratio long, still a sliver of
49
+ // the file however large that is.
50
+ // Remembered by URL rather than as a flag to reset, so switching to another
51
+ // image can't carry the fallback over to it.
52
+ property string thinUrl: ""
53
+ readonly property bool thinDecode: thinUrl !== "" && thinUrl === String(url)
54
+ property string failedUrl: ""
55
+ property string recheckedUrl: ""
56
+ onUrlChanged: {
57
+ failedUrl = ""
58
+ recheckedUrl = ""
59
+ }
60
+
61
+ clip: crop
62
+
63
+ Image {
64
+ id: image
65
+
66
+ readonly property real placedScale: implicitWidth > 0 && implicitHeight > 0
67
+ ? (root.crop ? Math.max(root.width / implicitWidth, root.height / implicitHeight)
68
+ : Math.min(root.width / implicitWidth, root.height / implicitHeight))
69
+ : 0
70
+
71
+ width: root.scalable ? root.width : implicitWidth * placedScale
72
+ height: root.scalable ? root.height : implicitHeight * placedScale
73
+ x: root.scalable || root.effectiveHorizontalAlignment === Image.AlignLeft ? 0
74
+ : root.effectiveHorizontalAlignment === Image.AlignRight ? root.width - width
75
+ : (root.width - width) / 2
76
+ y: root.scalable || root.verticalAlignment === Image.AlignTop ? 0
77
+ : root.verticalAlignment === Image.AlignBottom ? root.height - height
78
+ : (root.height - height) / 2
79
+
80
+ source: root.sized ? root.url : ""
81
+ readonly property bool bounded: root.sized && !root.scalable && !root.thinDecode
82
+ sourceSize.width: bounded ? Math.ceil(root.width * root.reach * Screen.devicePixelRatio) : root.thinDecode ? 1 : 0
83
+ sourceSize.height: bounded ? Math.ceil(root.height * root.reach * Screen.devicePixelRatio) : root.thinDecode ? 1 : 0
84
+ onStatusChanged: {
85
+ var loaded = String(source)
86
+ if (root.thinDecode) {
87
+ if (status === Image.Error) {
88
+ root.thinUrl = ""
89
+ } else if (status === Image.Ready && root.recheckedUrl !== loaded) {
90
+ // The fallback's bitmap has the image's aspect ratio. If fitting
91
+ // that in the bound keeps at least a pixel across, the bounded load
92
+ // failed for another reason, such as art that only appeared later,
93
+ // so load it bounded again. Only once per URL, so a disagreement
94
+ // about rounding can't flip back and forth.
95
+ var boundWidth = Math.ceil(root.width * root.reach * Screen.devicePixelRatio)
96
+ var boundHeight = Math.ceil(root.height * root.reach * Screen.devicePixelRatio)
97
+ var across = Math.min(boundWidth / implicitWidth, boundHeight / implicitHeight) * Math.min(implicitWidth, implicitHeight)
98
+ if (across >= 1) {
99
+ root.recheckedUrl = loaded
100
+ root.thinUrl = ""
101
+ }
102
+ }
103
+ return
104
+ }
105
+ if (!bounded) return
106
+ if (status === Image.Ready && (implicitWidth === 0 || implicitHeight === 0)) {
107
+ root.thinUrl = loaded
108
+ } else if (status === Image.Error && root.failedUrl !== loaded) {
109
+ // Some decoders fail outright where a thin PNG comes back empty. Try
110
+ // the fallback once per URL, so a file that is simply missing does
111
+ // not keep reloading.
112
+ root.failedUrl = loaded
113
+ root.thinUrl = loaded
114
+ }
115
+ }
116
+ // A thin decode covers its 1x1 bound, so its short side is one pixel.
117
+ fillMode: root.scalable || root.thinDecode
118
+ ? (root.crop || root.thinDecode ? Image.PreserveAspectCrop : Image.PreserveAspectFit)
119
+ : Image.Stretch
120
+ horizontalAlignment: root.horizontalAlignment
121
+ verticalAlignment: root.verticalAlignment
122
+ asynchronous: root.asynchronous
123
+ smooth: root.smooth
124
+ }
125
+ }