flea-qml 0.0.0-stage → 0.1.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.
- package/LICENSE +22 -0
- package/README.md +478 -2
- package/THIRD-PARTY-NOTICES.md +29 -0
- package/demos/App.java +5 -0
- package/demos/Dockerfile +4 -0
- package/demos/Main.kt +3 -0
- package/demos/Program.cs +1 -0
- package/demos/README.md +12 -0
- package/demos/app.dart +3 -0
- package/demos/app.deb +0 -0
- package/demos/app.js +1 -0
- package/demos/backup.tar +0 -0
- package/demos/budget.xlsx +0 -0
- package/demos/build.sh +2 -0
- package/demos/bundle.zip +0 -0
- package/demos/clip.mp4 +0 -0
- package/demos/data.sqlite +0 -0
- package/demos/disk.iso +0 -0
- package/demos/engine.cpp +5 -0
- package/demos/firmware.bin +0 -0
- package/demos/flake.nix +4 -0
- package/demos/folder/inside.txt +1 -0
- package/demos/font.ttf +0 -0
- package/demos/index.html +5 -0
- package/demos/index.php +2 -0
- package/demos/init.lua +1 -0
- package/demos/letter.docx +0 -0
- package/demos/lib.rs +3 -0
- package/demos/main.c +6 -0
- package/demos/main.h +3 -0
- package/demos/math-and-diagrams.md +200 -0
- package/demos/notes.tar.gz +0 -0
- package/demos/notes.txt +3 -0
- package/demos/photo.png +0 -0
- package/demos/program +2 -0
- package/demos/report.pdf +32 -0
- package/demos/script.py +1 -0
- package/demos/server.go +7 -0
- package/demos/settings.json +4 -0
- package/demos/slides.pptx +0 -0
- package/demos/song.mp3 +0 -0
- package/demos/style.css +4 -0
- package/demos/tool.rb +1 -0
- package/demos/types.ts +2 -0
- package/docs/pele.md +23 -0
- package/docs/protocol.md +3420 -0
- package/docs/temml.md +63 -0
- package/index.js +107 -0
- package/package.json +34 -4
- package/src/archive.js +402 -0
- package/src/archiveops.js +697 -0
- package/src/backend.js +369 -0
- package/src/bulkrename.js +95 -0
- package/src/clip-host.js +76 -0
- package/src/clip.js +462 -0
- package/src/code.js +168 -0
- package/src/compare.js +74 -0
- package/src/create.js +128 -0
- package/src/dirsize.js +147 -0
- package/src/figure-svg.js +530 -0
- package/src/figure-thread.js +23 -0
- package/src/figure.js +132 -0
- package/src/find.js +55 -0
- package/src/flea.js +39 -0
- package/src/fsinfo.js +39 -0
- package/src/highlight.js +141 -0
- package/src/html.js +52 -0
- package/src/kinds.js +89 -0
- package/src/listing.js +130 -0
- package/src/markdown-html.js +317 -0
- package/src/mathml.js +186 -0
- package/src/menu.js +180 -0
- package/src/meta.js +129 -0
- package/src/office.js +810 -0
- package/src/order.js +100 -0
- package/src/peek.js +49 -0
- package/src/pele-wrapper.js +695 -0
- package/src/permissions.js +180 -0
- package/src/places.js +313 -0
- package/src/preview-language.js +10 -0
- package/src/transfer.js +669 -0
- package/src/trash-record.js +41 -0
- package/src/trash.js +210 -0
- package/src/trashbrowse.js +326 -0
- package/src/uistate.js +393 -0
- package/src/uistore.js +168 -0
- package/src/vendor/LICENSES/Apache-2.0.txt +202 -0
- package/src/vendor/LICENSES/MIT-katex-2015.txt +27 -0
- package/src/vendor/LICENSES/MIT-katex.txt +21 -0
- package/src/vendor/LICENSES/MIT-mermaid.txt +21 -0
- package/src/vendor/LICENSES/MIT-pele.txt +40 -0
- package/src/vendor/LICENSES/MIT-perseus.txt +18 -0
- package/src/vendor/LICENSES/MIT-temml.txt +21 -0
- package/src/vendor/pele.js +145 -0
- package/src/vendor/temml.mjs +14708 -0
- package/src/watch.js +98 -0
- package/src/wayland/client.js +109 -0
- package/src/wayland/protocol.js +55 -0
- package/src/wayland/protocols/ext-data-control-v1.xml +276 -0
- package/src/wayland/protocols/wayland.xml +3248 -0
- package/src/wayland/protocols/wlr-data-control-unstable-v1.xml +278 -0
- package/src/wayland/resource-io.js +6 -0
- package/src/wayland/socket-discovery.js +26 -0
- package/src/wayland/transport-posix.js +120 -0
- package/src/wayland/transport-win32.js +11 -0
- package/src/wayland/transport.js +3 -0
- package/src/wayland/win32-resources.js +80 -0
- package/src/wayland/win32-wire.js +62 -0
- package/test/archive.test.js +80 -0
- package/test/bench-markdown-scroll.mjs +190 -0
- package/test/clip-host.test.js +113 -0
- package/test/code-preview.test.js +35 -0
- package/test/compare-ui-paths.test.js +24 -0
- package/test/compare.test.js +54 -0
- package/test/delete-trash.test.js +67 -0
- package/test/dirsize.test.js +138 -0
- package/test/file-ops.test.js +241 -0
- package/test/find-contents.test.js +161 -0
- package/test/find-input.test.js +66 -0
- package/test/fixtures/pele-parity.json +1006 -0
- package/test/highlight-launcher.test.js +35 -0
- package/test/html-preview.test.js +22 -0
- package/test/input.test.js +15 -0
- package/test/keymap.test.js +26 -0
- package/test/language-icons.test.js +36 -0
- package/test/launcher.test.js +51 -0
- package/test/manual-code-scroll.mjs +28 -0
- package/test/manual-column-scroll.mjs +24 -0
- package/test/manual-compare-ui.mjs +20 -0
- package/test/manual-find-status.mjs +37 -0
- package/test/manual-html-security.mjs +23 -0
- package/test/manual-html-ui.mjs +30 -0
- package/test/manual-syntax-preview.mjs +26 -0
- package/test/manual-trash-ui.mjs +86 -0
- package/test/markdown-diagrams.test.js +93 -0
- package/test/markdown-html.test.js +105 -0
- package/test/mathml.test.js +92 -0
- package/test/menu-transfer.test.js +57 -0
- package/test/office-preview.test.js +154 -0
- package/test/pele-parity.test.js +28 -0
- package/test/places.test.js +134 -0
- package/test/preview-keys.test.js +60 -0
- package/test/preview-language.test.js +22 -0
- package/test/status-filename.test.js +20 -0
- package/test/transfer-replace-merge.test.js +187 -0
- package/test/trash-address.test.js +23 -0
- package/test/trash-view.test.js +37 -0
- package/test/trashbrowse.test.js +135 -0
- package/ui/AboutFacts.qml +78 -0
- package/ui/ArchiveCards.qml +68 -0
- package/ui/ArchiveCompressDialog.qml +378 -0
- package/ui/ArchivePasswordDialog.qml +257 -0
- package/ui/Backend.qml +771 -0
- package/ui/BulkOldNames.qml +47 -0
- package/ui/BulkRenameDialog.qml +380 -0
- package/ui/CardFrame.qml +67 -0
- package/ui/CardScroll.qml +79 -0
- package/ui/CheckBox.qml +73 -0
- package/ui/ChromeBar.qml +360 -0
- package/ui/ChromeButton.qml +131 -0
- package/ui/ChromeModes.qml +43 -0
- package/ui/CodeLines.qml +256 -0
- package/ui/CodeRefusal.qml +52 -0
- package/ui/CollideConfirm.qml +147 -0
- package/ui/CollideHost.qml +167 -0
- package/ui/ColumnPane.qml +523 -0
- package/ui/ColumnRow.qml +380 -0
- package/ui/ColumnsArea.qml +594 -0
- package/ui/ColumnsLanding.qml +147 -0
- package/ui/CompareView.qml +321 -0
- package/ui/ContextMenu.qml +743 -0
- package/ui/ConvertDialog.qml +456 -0
- package/ui/CountedWorker.qml +7 -0
- package/ui/Crumb.qml +64 -0
- package/ui/CrumbDrop.qml +25 -0
- package/ui/CsvTable.qml +394 -0
- package/ui/CursorMark.qml +41 -0
- package/ui/DefaultClaim.qml +130 -0
- package/ui/DeviceLock.qml +47 -0
- package/ui/DeviceMounts.qml +540 -0
- package/ui/DialogButton.qml +150 -0
- package/ui/DialogField.qml +147 -0
- package/ui/DialogHeader.qml +31 -0
- package/ui/DialogScrim.qml +20 -0
- package/ui/DialogTitle.qml +37 -0
- package/ui/Divider.qml +9 -0
- package/ui/DropInto.qml +126 -0
- package/ui/DropTarget.qml +35 -0
- package/ui/DropboxMark.qml +60 -0
- package/ui/EmptyState.qml +150 -0
- package/ui/EscapeHint.qml +11 -0
- package/ui/FactsTable.qml +54 -0
- package/ui/FastScrollHandler.qml +476 -0
- package/ui/FatNamesConfirm.qml +157 -0
- package/ui/Favourites.qml +143 -0
- package/ui/FigureService.qml +120 -0
- package/ui/FileDrag.qml +267 -0
- package/ui/FilterStrip.qml +133 -0
- package/ui/FitMetrics.qml +22 -0
- package/ui/FlashDriveRows.qml +137 -0
- package/ui/FlashHost.qml +72 -0
- package/ui/FlashImageRow.qml +104 -0
- package/ui/FlashRunner.qml +119 -0
- package/ui/FlashStepRow.qml +172 -0
- package/ui/FlashStrip.qml +38 -0
- package/ui/FlashView.qml +397 -0
- package/ui/FleaMark.qml +88 -0
- package/ui/FlyoutFade.qml +23 -0
- package/ui/FontPane.qml +170 -0
- package/ui/Glass.qml +114 -0
- package/ui/GlassTagger.qml +59 -0
- package/ui/Glyph.qml +64 -0
- package/ui/GlyphSolid.qml +22 -0
- package/ui/GridArea.qml +664 -0
- package/ui/GridTile.qml +325 -0
- package/ui/GvfsBridge.qml +164 -0
- package/ui/Header.qml +433 -0
- package/ui/HoverTimers.qml +39 -0
- package/ui/HtmlClip.qml +30 -0
- package/ui/HtmlPane.qml +63 -0
- package/ui/HyprlandMark.qml +45 -0
- package/ui/Ipc.qml +1005 -0
- package/ui/IpcControls.qml +7 -0
- package/ui/IpcMenuState.qml +48 -0
- package/ui/IpcPreviewState.qml +185 -0
- package/ui/JumpPath.qml +51 -0
- package/ui/KeymapSheet.qml +561 -0
- package/ui/KeymapSheetResult.qml +89 -0
- package/ui/KindStack.qml +40 -0
- package/ui/LangMark.qml +51 -0
- package/ui/LangMarkLoader.qml +26 -0
- package/ui/List.qml +480 -0
- package/ui/LoadingState.qml +54 -0
- package/ui/LocalSend.qml +56 -0
- package/ui/LocalSendMark.qml +73 -0
- package/ui/MarkdownBlockView.qml +311 -0
- package/ui/MarkdownBlocks.qml +35 -0
- package/ui/MarkdownFigure.qml +254 -0
- package/ui/MarkdownImages.qml +72 -0
- package/ui/MarkdownList.qml +81 -0
- package/ui/MarkdownMathsText.qml +97 -0
- package/ui/MarkdownPane.qml +83 -0
- package/ui/MarkdownPictures.qml +41 -0
- package/ui/MarkdownQuote.qml +52 -0
- package/ui/MarkdownRemote.qml +82 -0
- package/ui/MarkdownSource.qml +155 -0
- package/ui/MarkdownTable.qml +222 -0
- package/ui/MarkdownTableScroll.qml +64 -0
- package/ui/MarkdownTableWheel.qml +126 -0
- package/ui/MarkdownText.qml +87 -0
- package/ui/MarkdownWorker.js +132 -0
- package/ui/MatchText.qml +99 -0
- package/ui/MediaSession.qml +38 -0
- package/ui/MediaSound.qml +26 -0
- package/ui/MediaStrip.qml +191 -0
- package/ui/MediaTrack.qml +36 -0
- package/ui/MediaVolume.qml +54 -0
- package/ui/MenuActionDialog.qml +396 -0
- package/ui/MenuEdgeFade.qml +24 -0
- package/ui/MenuHost.qml +227 -0
- package/ui/MenuRow.qml +351 -0
- package/ui/MountListing.qml +100 -0
- package/ui/NetworkDialog.qml +435 -0
- package/ui/NetworkForm.qml +426 -0
- package/ui/NetworkMounts.qml +913 -0
- package/ui/NetworkPlaces.qml +127 -0
- package/ui/OpenWithDialog.qml +613 -0
- package/ui/Opener.qml +115 -0
- package/ui/Pane.qml +1296 -0
- package/ui/PaneMenuActions.qml +527 -0
- package/ui/PanePath.qml +90 -0
- package/ui/PaneRail.qml +239 -0
- package/ui/PaneStates.qml +88 -0
- package/ui/PaneSwap.qml +339 -0
- package/ui/PaneWire.qml +605 -0
- package/ui/ParkedWait.qml +9 -0
- package/ui/PathEntry.qml +186 -0
- package/ui/PathJump.qml +400 -0
- package/ui/PdfViewer.qml +297 -0
- package/ui/PermissionsDialog.qml +658 -0
- package/ui/PermissionsHeadings.qml +31 -0
- package/ui/PhoneMounts.qml +67 -0
- package/ui/PickerAnswers.qml +75 -0
- package/ui/PickerBox.qml +26 -0
- package/ui/PickerChrome.qml +295 -0
- package/ui/PickerFilter.qml +97 -0
- package/ui/PickerGrid.qml +308 -0
- package/ui/PickerHeader.qml +28 -0
- package/ui/PickerLifecycle.qml +27 -0
- package/ui/PickerList.qml +218 -0
- package/ui/PickerListing.qml +149 -0
- package/ui/PickerPathBar.qml +127 -0
- package/ui/PickerPlaces.qml +189 -0
- package/ui/PickerRecent.qml +92 -0
- package/ui/PickerSave.qml +101 -0
- package/ui/PickerSelection.qml +155 -0
- package/ui/PickerWindow.qml +846 -0
- package/ui/PowerSectorsReader.qml +10 -0
- package/ui/PrebuildDriver.qml +146 -0
- package/ui/Preview.qml +1004 -0
- package/ui/PreviewAnimation.qml +24 -0
- package/ui/PreviewArchive.qml +66 -0
- package/ui/PreviewColumn.qml +551 -0
- package/ui/PreviewEdge.qml +63 -0
- package/ui/PreviewFacts.qml +30 -0
- package/ui/PreviewFactsLine.qml +34 -0
- package/ui/PreviewHtml.qml +233 -0
- package/ui/PreviewImage.qml +253 -0
- package/ui/PreviewImagePan.qml +13 -0
- package/ui/PreviewImageSharp.qml +32 -0
- package/ui/PreviewLines.qml +106 -0
- package/ui/PreviewMarkdown.qml +706 -0
- package/ui/PreviewMedia.qml +250 -0
- package/ui/PreviewPdf.qml +482 -0
- package/ui/PreviewScroll.qml +201 -0
- package/ui/PreviewSwap.qml +335 -0
- package/ui/PreviewText.qml +149 -0
- package/ui/ProtocolChip.qml +18 -0
- package/ui/QuickLookArchive.qml +38 -0
- package/ui/QuickLookBar.qml +103 -0
- package/ui/QuickLookPrepare.qml +355 -0
- package/ui/QuickLookSwap.qml +10 -0
- package/ui/QuickLookWarm.qml +65 -0
- package/ui/RailDrop.qml +155 -0
- package/ui/RailHeading.qml +19 -0
- package/ui/RailPlaces.qml +38 -0
- package/ui/RailTrashRow.qml +67 -0
- package/ui/RenameField.qml +353 -0
- package/ui/RendererRetry.qml +11 -0
- package/ui/ResizeHandle.qml +52 -0
- package/ui/Reveal.qml +54 -0
- package/ui/Row.qml +476 -0
- package/ui/RowDrag.qml +213 -0
- package/ui/RowDropTarget.qml +27 -0
- package/ui/RunCard.qml +286 -0
- package/ui/Scripts.qml +65 -0
- package/ui/SearchStrip.qml +232 -0
- package/ui/SelectionBand.qml +140 -0
- package/ui/SelectionPreview.qml +370 -0
- package/ui/SettingsFavourite.qml +122 -0
- package/ui/SettingsGroup.qml +96 -0
- package/ui/SettingsHint.qml +12 -0
- package/ui/SettingsMetrics.qml +29 -0
- package/ui/SettingsPane.qml +178 -0
- package/ui/SettingsPanel.qml +547 -0
- package/ui/SettingsRail.qml +128 -0
- package/ui/SettingsRow.qml +409 -0
- package/ui/SettingsRuler.qml +85 -0
- package/ui/SettingsSegment.qml +85 -0
- package/ui/ShareBrowser.qml +113 -0
- package/ui/ShareLink.qml +67 -0
- package/ui/ShelfPins.qml +101 -0
- package/ui/Sidebar.qml +732 -0
- package/ui/SidebarRow.qml +358 -0
- package/ui/Spinner.qml +60 -0
- package/ui/StableModel.qml +89 -0
- package/ui/StateMessage.qml +84 -0
- package/ui/StatusBar.qml +512 -0
- package/ui/StatusFileName.qml +27 -0
- package/ui/TabBar.qml +673 -0
- package/ui/TabDragGeometry.qml +60 -0
- package/ui/TagMenuMark.qml +52 -0
- package/ui/TagRailSection.qml +53 -0
- package/ui/TagSlot.qml +40 -0
- package/ui/TagState.qml +150 -0
- package/ui/TagSwatch.qml +28 -0
- package/ui/Taildrop.qml +86 -0
- package/ui/TailscaleMark.qml +50 -0
- package/ui/Theme.qml +460 -0
- package/ui/ThemeColors.qml +43 -0
- package/ui/TransferCard.qml +239 -0
- package/ui/TrashConfirm.qml +129 -0
- package/ui/TrashDropHandler.qml +11 -0
- package/ui/TrashHost.qml +202 -0
- package/ui/TrashMonitor.qml +30 -0
- package/ui/TrashToolbar.qml +19 -0
- package/ui/TrashView.qml +492 -0
- package/ui/UnlockDialog.qml +450 -0
- package/ui/UnlockHost.qml +206 -0
- package/ui/UpdateCheck.qml +97 -0
- package/ui/UserDirs.qml +25 -0
- package/ui/ViewState.qml +565 -0
- package/ui/ViewportScrollBar.qml +195 -0
- package/ui/ViewportScrollBars.qml +25 -0
- package/ui/WebPreview.qml +33 -0
- package/ui/WebPreviewView.qml +26 -0
- package/ui/WindowBody.qml +827 -0
- package/ui/WindowDrag.qml +35 -0
- package/ui/boot/Commons/AudioNodes.qml +50 -0
- package/ui/boot/Commons/AudioNodesModel.js +96 -0
- package/ui/boot/Commons/Border.qml +243 -0
- package/ui/boot/Commons/BorderGeometry.js +373 -0
- package/ui/boot/Commons/Color.qml +258 -0
- package/ui/boot/Commons/I18n.qml +118 -0
- package/ui/boot/Commons/InputDevices.qml +112 -0
- package/ui/boot/Commons/InputMethodState.qml +38 -0
- package/ui/boot/Commons/IpcRegistry.qml +77 -0
- package/ui/boot/Commons/Numbers.js +19 -0
- package/ui/boot/Commons/Plural.js +91 -0
- package/ui/boot/Commons/ShellIpc.qml +12 -0
- package/ui/boot/Commons/Style.qml +561 -0
- package/ui/boot/Commons/Translations.qml +100 -0
- package/ui/boot/Commons/UntypedInput.qml +152 -0
- package/ui/boot/Commons/Util.qml +188 -0
- package/ui/boot/Commons/qmldir +13 -0
- package/ui/boot/LICENSE-omarchy +20 -0
- package/ui/boot/Ui/BackgroundMedia.qml +49 -0
- package/ui/boot/Ui/BarIconButton.qml +106 -0
- package/ui/boot/Ui/BarIconContent.qml +38 -0
- package/ui/boot/Ui/BarIndicator.qml +53 -0
- package/ui/boot/Ui/BarWidget.qml +50 -0
- package/ui/boot/Ui/BorderOverlay.qml +54 -0
- package/ui/boot/Ui/BorderSurface.qml +40 -0
- package/ui/boot/Ui/Button.qml +224 -0
- package/ui/boot/Ui/ButtonGroup.qml +131 -0
- package/ui/boot/Ui/ConfirmDialog.qml +134 -0
- package/ui/boot/Ui/CursorSurface.qml +42 -0
- package/ui/boot/Ui/Dropdown.qml +245 -0
- package/ui/boot/Ui/FilterInput.qml +26 -0
- package/ui/boot/Ui/IconContentBounds.qml +141 -0
- package/ui/boot/Ui/KeyboardPanel.qml +453 -0
- package/ui/boot/Ui/MultiSelect.qml +624 -0
- package/ui/boot/Ui/NumberField.qml +86 -0
- package/ui/boot/Ui/OpticalGlyph.qml +68 -0
- package/ui/boot/Ui/OverlayWindow.qml +102 -0
- package/ui/boot/Ui/Panel.qml +60 -0
- package/ui/boot/Ui/PanelActionButton.qml +105 -0
- package/ui/boot/Ui/PanelController.qml +16 -0
- package/ui/boot/Ui/PanelHero.qml +112 -0
- package/ui/boot/Ui/PanelKeyCatcher.qml +100 -0
- package/ui/boot/Ui/PanelSectionHeader.qml +31 -0
- package/ui/boot/Ui/PanelSeparator.qml +19 -0
- package/ui/boot/Ui/PanelSlider.qml +156 -0
- package/ui/boot/Ui/PanelToolTip.qml +50 -0
- package/ui/boot/Ui/PluginBarApi.qml +89 -0
- package/ui/boot/Ui/PointerMoveGate.qml +54 -0
- package/ui/boot/Ui/PopupCard.qml +181 -0
- package/ui/boot/Ui/ScreenMoveRemap.qml +43 -0
- package/ui/boot/Ui/SearchableDropdown.qml +354 -0
- package/ui/boot/Ui/SizedImage.qml +125 -0
- package/ui/boot/Ui/SpeedTestOverlay.qml +412 -0
- package/ui/boot/Ui/TextField.qml +58 -0
- package/ui/boot/Ui/Toggle.qml +118 -0
- package/ui/boot/Ui/ToggleSwitch.qml +115 -0
- package/ui/boot/Ui/WidgetButton.qml +172 -0
- package/ui/boot/Ui/qmldir +41 -0
- package/ui/boot/fleatab.qml +50 -0
- package/ui/boot/picker.qml +39 -0
- package/ui/boot/shell.qml +122 -0
- package/ui/boot/tabtearoff.qml +51 -0
- package/ui/js/Anchor.js +379 -0
- package/ui/js/AnchorHold.js +162 -0
- package/ui/js/Archive.js +157 -0
- package/ui/js/ArchiveCard.js +44 -0
- package/ui/js/ArchiveOps.js +44 -0
- package/ui/js/BackReturn.js +118 -0
- package/ui/js/BulkRename.js +100 -0
- package/ui/js/Buttons.js +54 -0
- package/ui/js/ChromeKeys.js +116 -0
- package/ui/js/ClipMarks.js +73 -0
- package/ui/js/ClipSelection.js +67 -0
- package/ui/js/Clipboard.js +179 -0
- package/ui/js/Cloud.js +97 -0
- package/ui/js/CodeRuns.js +72 -0
- package/ui/js/Collide.js +212 -0
- package/ui/js/ColumnFit.js +28 -0
- package/ui/js/ColumnMenu.js +111 -0
- package/ui/js/Columns.js +298 -0
- package/ui/js/ColumnsCarry.js +103 -0
- package/ui/js/Compare.js +74 -0
- package/ui/js/Contrast.js +157 -0
- package/ui/js/ControlReader.js +42 -0
- package/ui/js/Convert.js +52 -0
- package/ui/js/CopyAs.js +52 -0
- package/ui/js/Crumbs.js +178 -0
- package/ui/js/Csv.js +231 -0
- package/ui/js/Density.js +44 -0
- package/ui/js/Devices.js +440 -0
- package/ui/js/DirSizes.js +180 -0
- package/ui/js/Drag.js +364 -0
- package/ui/js/DragOut.js +166 -0
- package/ui/js/DropDev.js +52 -0
- package/ui/js/Dropbox.js +41 -0
- package/ui/js/Eject.js +217 -0
- package/ui/js/Errors.js +188 -0
- package/ui/js/ExtThumbs.js +115 -0
- package/ui/js/Facts.js +402 -0
- package/ui/js/FigureDate.js +14 -0
- package/ui/js/Filter.js +285 -0
- package/ui/js/Find.js +46 -0
- package/ui/js/Flash.js +184 -0
- package/ui/js/FlashCard.js +79 -0
- package/ui/js/FlashEndings.js +125 -0
- package/ui/js/FlashPane.js +116 -0
- package/ui/js/FleaBin.js +12 -0
- package/ui/js/Focus.js +502 -0
- package/ui/js/FolderSorts.js +108 -0
- package/ui/js/FolderViews.js +44 -0
- package/ui/js/Format.js +305 -0
- package/ui/js/Fuzzy.js +141 -0
- package/ui/js/GlassTag.js +72 -0
- package/ui/js/Grid.js +33 -0
- package/ui/js/GridGeometry.js +90 -0
- package/ui/js/GridNames.js +153 -0
- package/ui/js/GvfsBridge.js +200 -0
- package/ui/js/HostClipboard.js +29 -0
- package/ui/js/Icons.js +218 -0
- package/ui/js/Input.js +50 -0
- package/ui/js/Jump.js +200 -0
- package/ui/js/KeyName.js +64 -0
- package/ui/js/Keymap.js +401 -0
- package/ui/js/Kinds.js +164 -0
- package/ui/js/LICENSES/ISC-lucide.txt +43 -0
- package/ui/js/LangMarkPaths.js +47 -0
- package/ui/js/LangMarks.js +120 -0
- package/ui/js/Lists.js +11 -0
- package/ui/js/LocalSend.js +39 -0
- package/ui/js/LockedMenu.js +63 -0
- package/ui/js/MakeDefault.js +186 -0
- package/ui/js/MarkBox.js +7 -0
- package/ui/js/Markdown.js +119 -0
- package/ui/js/MarkdownLists.js +86 -0
- package/ui/js/MarkdownMaths.js +58 -0
- package/ui/js/MarkdownPictures.js +32 -0
- package/ui/js/MarkdownPrepared.js +139 -0
- package/ui/js/MarkdownTableFit.js +34 -0
- package/ui/js/Marks.js +148 -0
- package/ui/js/Marquee.js +96 -0
- package/ui/js/Match.js +93 -0
- package/ui/js/Material.js +109 -0
- package/ui/js/MdBlocks.js +437 -0
- package/ui/js/MdBreak.js +71 -0
- package/ui/js/MdChunks.js +70 -0
- package/ui/js/MdContainer.js +137 -0
- package/ui/js/MdDocument.js +337 -0
- package/ui/js/MdEmph.js +97 -0
- package/ui/js/MdEntity.js +108 -0
- package/ui/js/MdEntityTable.js +223 -0
- package/ui/js/MdEscape.js +59 -0
- package/ui/js/MdFront.js +37 -0
- package/ui/js/MdHold.js +32 -0
- package/ui/js/MdHtml.js +307 -0
- package/ui/js/MdHtmlBlock.js +290 -0
- package/ui/js/MdHtmlHeading.js +53 -0
- package/ui/js/MdHtmlImage.js +179 -0
- package/ui/js/MdInline.js +154 -0
- package/ui/js/MdItems.js +212 -0
- package/ui/js/MdLeaf.js +309 -0
- package/ui/js/MdLink.js +196 -0
- package/ui/js/MdMath.js +67 -0
- package/ui/js/MdParagraphs.js +138 -0
- package/ui/js/MdRefs.js +232 -0
- package/ui/js/MdResolve.js +226 -0
- package/ui/js/MdRun.js +300 -0
- package/ui/js/MdUrl.js +226 -0
- package/ui/js/Menu.js +514 -0
- package/ui/js/MenuContext.js +85 -0
- package/ui/js/MenuFit.js +13 -0
- package/ui/js/MenuRefresh.js +74 -0
- package/ui/js/MenuWheel.js +35 -0
- package/ui/js/Messages.js +186 -0
- package/ui/js/Motion.js +16 -0
- package/ui/js/Mounts.js +320 -0
- package/ui/js/MouseNav.js +13 -0
- package/ui/js/Nav.js +329 -0
- package/ui/js/NetFs.js +26 -0
- package/ui/js/OpenWith.js +83 -0
- package/ui/js/Ops.js +585 -0
- package/ui/js/Palette.js +33 -0
- package/ui/js/PathBar.js +260 -0
- package/ui/js/Permissions.js +179 -0
- package/ui/js/Phones.js +112 -0
- package/ui/js/Picker.js +353 -0
- package/ui/js/PlaceMenu.js +96 -0
- package/ui/js/Places.js +265 -0
- package/ui/js/PreviewAsk.js +21 -0
- package/ui/js/PreviewKeys.js +155 -0
- package/ui/js/PreviewSettle.js +29 -0
- package/ui/js/PreviewSwap.js +101 -0
- package/ui/js/PropertyFacts.js +144 -0
- package/ui/js/Protocols.js +296 -0
- package/ui/js/QuickLookKinds.js +27 -0
- package/ui/js/QuickLookRect.js +24 -0
- package/ui/js/RailDrop.js +51 -0
- package/ui/js/RailKeys.js +87 -0
- package/ui/js/RailMenu.js +133 -0
- package/ui/js/Recent.js +135 -0
- package/ui/js/RecentMode.js +156 -0
- package/ui/js/Reload.js +60 -0
- package/ui/js/Renderer.js +9 -0
- package/ui/js/RunFile.js +143 -0
- package/ui/js/Scripts.js +28 -0
- package/ui/js/Scroll.js +304 -0
- package/ui/js/ScrollOff.js +69 -0
- package/ui/js/Search.js +478 -0
- package/ui/js/Selection.js +97 -0
- package/ui/js/Settings.js +476 -0
- package/ui/js/SettingsAbout.js +97 -0
- package/ui/js/SettingsShelf.js +52 -0
- package/ui/js/SheetKeys.js +42 -0
- package/ui/js/SheetQuery.js +281 -0
- package/ui/js/ShelfPile.js +27 -0
- package/ui/js/SlowClick.js +53 -0
- package/ui/js/SlowOp.js +6 -0
- package/ui/js/Sort.js +134 -0
- package/ui/js/Startup.js +63 -0
- package/ui/js/StateWords.js +17 -0
- package/ui/js/Status.js +238 -0
- package/ui/js/Swap.js +166 -0
- package/ui/js/TabMove.js +39 -0
- package/ui/js/Tabs.js +736 -0
- package/ui/js/Tags.js +134 -0
- package/ui/js/Taildrop.js +128 -0
- package/ui/js/Tap.js +174 -0
- package/ui/js/TextSize.js +107 -0
- package/ui/js/Thumbs.js +158 -0
- package/ui/js/Transfer.js +172 -0
- package/ui/js/Trash.js +81 -0
- package/ui/js/TrashDates.js +47 -0
- package/ui/js/TrashDrop.js +64 -0
- package/ui/js/UiState.js +438 -0
- package/ui/js/Unlock.js +152 -0
- package/ui/js/Update.js +130 -0
- package/ui/js/UserDirs.js +56 -0
- package/ui/qmldir +228 -0
package/ui/Preview.qml
ADDED
|
@@ -0,0 +1,1004 @@
|
|
|
1
|
+
import QtQuick
|
|
2
|
+
import qs.Commons
|
|
3
|
+
import "." as Flea
|
|
4
|
+
import "js/Icons.js" as Icons
|
|
5
|
+
import "js/Kinds.js" as Kinds
|
|
6
|
+
import "js/Search.js" as Search
|
|
7
|
+
import "js/Thumbs.js" as Thumbs
|
|
8
|
+
import "js/ExtThumbs.js" as ExtThumbs
|
|
9
|
+
import "js/MarkdownPrepared.js" as Prepared
|
|
10
|
+
import "js/Motion.js" as Motion
|
|
11
|
+
import "js/PreviewSettle.js" as PreviewSettle
|
|
12
|
+
import "js/PreviewKeys.js" as PreviewKeys
|
|
13
|
+
import "js/PreviewSwap.js" as PreviewSwap
|
|
14
|
+
import "js/Swap.js" as Swap
|
|
15
|
+
import "js/QuickLookRect.js" as QuickLookRect
|
|
16
|
+
|
|
17
|
+
// The overlay lives inside the Flea window, Finder's Quick Look shape: a second window breaks omarchy-drive focus flea and every test that narrows on it.
|
|
18
|
+
Item {
|
|
19
|
+
id: root
|
|
20
|
+
anchors.fill: parent
|
|
21
|
+
// active flips instantly, so the IPC read never races the close fade; visible outlives it until surface's own opacity finishes.
|
|
22
|
+
visible: root.active || surface.opacity > 0
|
|
23
|
+
z: 1
|
|
24
|
+
|
|
25
|
+
property bool active: false
|
|
26
|
+
// shell.qml wires the pane in: the archive pane asks the backend about the cursor row and nothing else here reads it.
|
|
27
|
+
property var pane: null
|
|
28
|
+
property string path: ""
|
|
29
|
+
property string iconName: ""
|
|
30
|
+
property string kindName: ""
|
|
31
|
+
property real size: 0
|
|
32
|
+
property string kind: ""
|
|
33
|
+
readonly property bool isMedia: root.kind === "audio" || root.kind === "video"
|
|
34
|
+
readonly property bool isPdf: root.kind === "pdf"
|
|
35
|
+
readonly property bool isImage: root.kind === "image"
|
|
36
|
+
readonly property bool isArchive: root.kind === "archive"
|
|
37
|
+
readonly property bool isFont: root.kind === "font"
|
|
38
|
+
readonly property bool fontFailed: root.isFont && fontLoader.item !== null && fontLoader.item.failed
|
|
39
|
+
// RenderedPreviews: a Markdown file keeps the text kind and draws its own bar and pane.
|
|
40
|
+
readonly property bool isMarkdown: root.kind === "text" && root.size <= ExtThumbs.textLimit(root.pane ? root.pane.storageClass : "") && Kinds.isMarkdown(root.path)
|
|
41
|
+
// An office document is converted by the backend, so it needs local storage and has its own size gate.
|
|
42
|
+
readonly property bool isOffice: root.kind === "text" && root.size <= Kinds.OFFICE_LIMIT && Kinds.isOfficeDocument(root.path)
|
|
43
|
+
&& !(root.pane && (root.pane.storageClass === "network" || root.pane.storageClass === "phone"))
|
|
44
|
+
readonly property bool isHtml: root.isOffice || (root.kind === "text" && root.size <= ExtThumbs.textLimit(root.pane ? root.pane.storageClass : "") && Kinds.isHtml(root.path))
|
|
45
|
+
readonly property bool isDocument: root.isMarkdown || root.isHtml
|
|
46
|
+
// Code and plain text share ui/PreviewText.qml; every text kind draws the Quick Look bar.
|
|
47
|
+
readonly property bool isCodeText: root.kind === "text" && !root.isDocument
|
|
48
|
+
readonly property bool hasBar: root.kind === "text"
|
|
49
|
+
// Compare shows two files in this overlay: path is the first and comparePath the second, and one diff is asked per open.
|
|
50
|
+
readonly property bool isCompare: root.kind === "compare"
|
|
51
|
+
property string comparePath: ""
|
|
52
|
+
// r flips the open document Quick Look to Source; close() forgets it, and a move to another file keeps it.
|
|
53
|
+
property bool markdownSource: false
|
|
54
|
+
// The file whose first Markdown read may block the key, decided from its listing row before the path moves; "" when none may.
|
|
55
|
+
property string inlineMarkdownPath: ""
|
|
56
|
+
// Tab put the keyboard on the bar's close mark, Markdown or code; a move off the text kinds or a close lets it go.
|
|
57
|
+
property bool markdownCloseFocus: false
|
|
58
|
+
readonly property bool markdownCloseFocused: root.active && root.hasBar && root.markdownCloseFocus
|
|
59
|
+
onHasBarChanged: if (!root.hasBar) root.markdownCloseFocus = false
|
|
60
|
+
// The backend's meta answer for the open archive, null until it lands; archiveRow is the row it was asked for.
|
|
61
|
+
property var archiveMeta: null
|
|
62
|
+
property int archiveRow: -1
|
|
63
|
+
// MediaPdf rule 6's fourth fact: Qt carries no sample-rate key at all (QMediaMetaData::Key, Qt 6.11), so the number is the backend probe's, asked the way an archive's is.
|
|
64
|
+
property int mediaRate: 0
|
|
65
|
+
property int mediaRow: -1
|
|
66
|
+
readonly property bool archiveFailed: root.isArchive && root.archiveMeta !== null && root.archiveMeta.archiveFailed === true
|
|
67
|
+
// For ui/Ipc.qml: the item drawing this kind's content, whether a player exists, and what the text and archive panes hold.
|
|
68
|
+
function surfaceItem() {
|
|
69
|
+
if (root.isImage) return imageLoader.item
|
|
70
|
+
if (root.isMedia) return mediaLoader.item
|
|
71
|
+
if (root.isPdf) return pdfLoader.item
|
|
72
|
+
if (root.isArchive) return archivePane
|
|
73
|
+
if (root.isCompare) return compareLoader.item
|
|
74
|
+
if (root.kind === "text") return root.isDocument
|
|
75
|
+
? (markdownLoader.item ? markdownLoader.item.bodyItem : null) : textPane.bodyItem
|
|
76
|
+
return null
|
|
77
|
+
}
|
|
78
|
+
function pageBy(direction) {
|
|
79
|
+
if (root.isPdf) { root.turnPage(direction); return }
|
|
80
|
+
var view = null
|
|
81
|
+
if (root.isDocument) view = root.markdownShown ? root.markdownShown.viewport : null
|
|
82
|
+
else if (root.kind === "text") view = textPane.list
|
|
83
|
+
else if (root.isCompare && compareLoader.item) view = compareLoader.item.viewport
|
|
84
|
+
PreviewKeys.pageViewport(view, direction)
|
|
85
|
+
}
|
|
86
|
+
function mediaLoaded() { return mediaLoader.item !== null }
|
|
87
|
+
function textShown() { return root.isDocument
|
|
88
|
+
? (markdownLoader.item ? markdownLoader.item.rawText : "") : textPane.shownText() }
|
|
89
|
+
function markdownView() { return root.markdownShown ? root.markdownShown.shownView : "" }
|
|
90
|
+
function markdownCloseState() { return JSON.stringify(root.markdownShown ? root.markdownShown.closeState() : {}) }
|
|
91
|
+
function markdownEndGap() { return root.markdownShown ? root.markdownShown.endGap() : -1 }
|
|
92
|
+
function markdownScrollY() { return root.markdownShown ? Math.round(root.markdownShown.scrollY) : -1 }
|
|
93
|
+
function archiveNames() { return root.archiveMeta && root.archiveMeta.names ? root.archiveMeta.names.map(function (e) { return e.n }).join("|") : "" }
|
|
94
|
+
readonly property bool pdfExpanded: root.isPdf && pdfLoader.item !== null && pdfLoader.item.expanded
|
|
95
|
+
// The PDF surface, null with no document loaded: ui/Ipc.qml answers "" for that, so an unmeasured state never reads as a value.
|
|
96
|
+
readonly property var pdfItem: pdfLoader.item
|
|
97
|
+
// What the strip actually draws: shell.qml's IPC reads this rather than re-deriving the visible: expression.
|
|
98
|
+
readonly property alias stripVisible: mediaStrip.visible
|
|
99
|
+
// The strip's own mute mark and the flag it draws from, so a test reads and clicks what is there.
|
|
100
|
+
readonly property var muteMark: mediaStrip.muteItem
|
|
101
|
+
readonly property bool muted: Flea.MediaSound.muted
|
|
102
|
+
// fleaWindow.itemRect needs the real Item, the same seam rowCentre already reads through pane.
|
|
103
|
+
readonly property var seekSlider: mediaStrip.seekItem
|
|
104
|
+
readonly property var volumeSlider: mediaStrip.volumeItem
|
|
105
|
+
readonly property string status: {
|
|
106
|
+
if (!root.active) return ""
|
|
107
|
+
// A fresh media item still carries its empty path until onLoaded binds it; its
|
|
108
|
+
// "stopped" there is not a whole preview, so the swap never releases on it.
|
|
109
|
+
if (root.isMedia) return (mediaLoader.item && mediaLoader.item.path === root.path) ? mediaLoader.item.status : "loading"
|
|
110
|
+
if (root.isPdf) return (pdfLoader.item && pdfLoader.item.failed) ? "This file could not be read." : "pdf"
|
|
111
|
+
if (root.isImage) return imageLoader.item ? imageLoader.item.status : "loading"
|
|
112
|
+
if (root.isArchive) return root.archiveMeta === null ? "loading" : (root.archiveFailed ? "This archive could not be read." : "archive")
|
|
113
|
+
if (root.isCompare) return compareLoader.item ? compareLoader.item.status : "loading"
|
|
114
|
+
if (root.kind === "text") return root.isDocument
|
|
115
|
+
? (markdownLoader.item ? markdownLoader.item.status : "loading") : textPane.status
|
|
116
|
+
if (root.isFont) return fontLoader.item ? fontLoader.item.status : "loading"
|
|
117
|
+
return "This file cannot be previewed."
|
|
118
|
+
}
|
|
119
|
+
// The swap's answer: nothing loading, and a PDF with a page on screen or refused.
|
|
120
|
+
// An interim cache thumbnail shown counts as whole, never as a half-built frame.
|
|
121
|
+
readonly property bool lookReady: !root.active || PreviewSwap.lookReady(root.status, root.isPdf,
|
|
122
|
+
root.pdfItem !== null && root.pdfItem.shownPage >= 0 && root.pdfItem.opened === root.path, root.pdfItem !== null && root.pdfItem.failed,
|
|
123
|
+
root.interimShown, (root.isDocument && markdownLoader.item !== null && markdownLoader.item.firstScreen)
|
|
124
|
+
|| (root.isCodeText && textPane.firstScreen)) && !root.sizeWait
|
|
125
|
+
// The cached thumbnail held at open, drawn under the full decode at the final rect; the stamp retires a stale one.
|
|
126
|
+
property string interimThumb: ""
|
|
127
|
+
property string interimStamp: ""
|
|
128
|
+
// The interim's rect off the upright original, null while its pixels are unknown: no
|
|
129
|
+
// interim until then. A cache file is already upright, so no box swap applies here.
|
|
130
|
+
readonly property var interimBox: PreviewSwap.interimRect(panes.width, panes.height - (root.isRawFile ? Theme.chromeHeight : 0),
|
|
131
|
+
root.imageW, root.imageH, root.imageOrient)
|
|
132
|
+
// True once the interim is on screen at the final's rect; lookReady releases on this.
|
|
133
|
+
readonly property bool interimVisible: root.isImage && root.interimThumb.length > 0
|
|
134
|
+
&& root.path === root.interimStamp && root.interimBox !== null
|
|
135
|
+
readonly property bool interimShown: root.interimVisible && imageLoader.item !== null
|
|
136
|
+
&& imageLoader.item.interimReady === true
|
|
137
|
+
// The original's pixels, off the shown row's meta reply; 0 until it lands.
|
|
138
|
+
property int imageW: 0
|
|
139
|
+
property int imageH: 0
|
|
140
|
+
property int imageOrient: 1
|
|
141
|
+
property int imageFrames: 0
|
|
142
|
+
property int imageRow: -1
|
|
143
|
+
// One meta ask per show; onRowsChanged never re-asks behind it.
|
|
144
|
+
property bool imageAsked: false
|
|
145
|
+
// The row whose thumbnail may stand in for an image that cannot decode and had none at open, -1 when none is owed; the sentence waits while it is set.
|
|
146
|
+
property int thumbRow: -1
|
|
147
|
+
// One thumbnail ask per show, sent only once the final decode has failed.
|
|
148
|
+
property bool thumbAsked: false
|
|
149
|
+
// A listing started while the thumbnail is owed, so its map and row indices are the old listing's.
|
|
150
|
+
property bool thumbRelist: false
|
|
151
|
+
readonly property bool imageUndecodable: root.isImage && imageLoader.item !== null && imageLoader.item.pictureFailed
|
|
152
|
+
onImageUndecodableChanged: root.settleThumb()
|
|
153
|
+
// A camera RAW is drawn through the copy of its embedded JPEG the meta reply names; null until it lands or when the RAW holds none.
|
|
154
|
+
readonly property bool isRawFile: root.isImage && Kinds.isRaw(root.path)
|
|
155
|
+
property var rawCopy: null
|
|
156
|
+
property bool rawAnswered: false
|
|
157
|
+
// See ui/js/QuickLookRect.js: shapeNext follows the file shown and its size reply, shape is what the card draws, taken once no swap holds a picture.
|
|
158
|
+
property var shapeNext: ({ kind: "", known: false, w: 0, h: 0, orient: 1 })
|
|
159
|
+
property var shape: root.shapeNext
|
|
160
|
+
readonly property var rectTokens: ({ fraction: Theme.preview.fraction, edge: Theme.preview.edge, bar: Theme.chromeHeight })
|
|
161
|
+
readonly property var rect: QuickLookRect.surface(root.shape, root.width, root.height, root.pdfExpanded, root.rectTokens)
|
|
162
|
+
readonly property var nextRect: QuickLookRect.surface(root.shapeNext, root.width, root.height, root.pdfExpanded, root.rectTokens)
|
|
163
|
+
readonly property bool held: root.swap !== null && (root.swap.holding || root.swap.capturing)
|
|
164
|
+
// Plain, as a binding on its own releases loops: set by show(), cleared by the reply, an early reveal or the swap's release a turn later.
|
|
165
|
+
property bool sizeWait: false
|
|
166
|
+
onHeldChanged: {
|
|
167
|
+
root.commitShape()
|
|
168
|
+
if (!root.held) Qt.callLater(function () { root.sizeWait = false })
|
|
169
|
+
}
|
|
170
|
+
function commitShape() { if (!root.held) root.shape = root.shapeNext }
|
|
171
|
+
// A size reply counts until the card shows or the swap lets go of its picture; a later one leaves the card alone.
|
|
172
|
+
function sizeArrived(w, h, orient, known) {
|
|
173
|
+
if (!root.revealed || root.held) {
|
|
174
|
+
root.shapeNext = { kind: root.kind, known: known, w: w, h: h, orient: orient }
|
|
175
|
+
root.commitShape()
|
|
176
|
+
}
|
|
177
|
+
root.sizeWait = false
|
|
178
|
+
}
|
|
179
|
+
function swapState() {
|
|
180
|
+
if (root.swap) return root.swap.describe()
|
|
181
|
+
return { holding: false, capturing: false, fellBack: false, holds: 0, fallbacks: 0,
|
|
182
|
+
bursts: 0, heldFrames: 0, midFrames: 0, loadingFrames: 0, last: { ms: 0, end: "" } }
|
|
183
|
+
}
|
|
184
|
+
// The 0.3.6 swap additions, null until the first open builds them below; every reader guards it.
|
|
185
|
+
readonly property var swap: swapLoader.item
|
|
186
|
+
readonly property bool swapBuilt: swapLoader.active
|
|
187
|
+
// The memory suite asserts both Markdown loader items are null without a Markdown file.
|
|
188
|
+
readonly property var markdownItem: markdownLoader.item
|
|
189
|
+
// The open Markdown document, null unless Quick Look shows one; the guarded readers share this null.
|
|
190
|
+
readonly property var markdownShown: root.active && root.isDocument ? markdownLoader.item : null
|
|
191
|
+
// Exposes the eager panes for the live swap gate, so it can pin the wiring.
|
|
192
|
+
readonly property var panesItem: panes
|
|
193
|
+
// Synchronous: a local source: URL answers item on the same call that sets active.
|
|
194
|
+
function ensureSwap() {
|
|
195
|
+
if (!swapLoader.active)
|
|
196
|
+
swapLoader.active = true
|
|
197
|
+
return swapLoader.item
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
property string pendingPath: ""
|
|
201
|
+
property string pendingIcon: ""
|
|
202
|
+
property string pendingKind: ""
|
|
203
|
+
property int pendingSize: 0
|
|
204
|
+
property string pendingThumb: ""
|
|
205
|
+
// The row open/follow captured, carried through load/show like the thumb.
|
|
206
|
+
property int pendingImageRow: -1
|
|
207
|
+
// Last distinct follow target, so a held key cannot reload mid-burst: only idleness loads at once.
|
|
208
|
+
property double lastMoveAt: 0
|
|
209
|
+
property string lastMoveKey: ""
|
|
210
|
+
// The settle idiom Pane's own thumbnail request reuses: a held j/k costs zero reloads until the cursor rests.
|
|
211
|
+
readonly property int followSettleMs: 120
|
|
212
|
+
// The same dim ui/SettingsPanel.qml lays over the listing.
|
|
213
|
+
readonly property real groundOpacity: 0.5
|
|
214
|
+
// The card and dim open once the content can draw, or early over the loading state, so no frame shows an empty card.
|
|
215
|
+
readonly property bool revealed: root.landed || root.revealedEarly
|
|
216
|
+
// Set by the cap, or by a press or the wheel during the hold.
|
|
217
|
+
property bool revealedEarly: false
|
|
218
|
+
// When the early loading state first drew, 0 while none stands; content replaces it only after Swap.MIN_SHOW_MS.
|
|
219
|
+
property double loadingSince: 0
|
|
220
|
+
// True once this open's content first drew; later moves are the swap's to count.
|
|
221
|
+
property bool landed: false
|
|
222
|
+
readonly property bool revealedOpen: root.active && root.revealed
|
|
223
|
+
// The hold: the card and dim draw nothing, a reopen inside the close fade included, and a move takes no picture.
|
|
224
|
+
readonly property bool unrevealed: root.active && !root.revealed
|
|
225
|
+
// An early reveal shows the loading state at once, for every kind, until this open's content lands.
|
|
226
|
+
readonly property bool loadingEarly: root.revealedEarly && !root.landed
|
|
227
|
+
// Frames drawn with the card showing during the hold; tests/ui.sh lookopen and tests/quicklook-hold.sh read them.
|
|
228
|
+
property int lookEmptyFrames: 0
|
|
229
|
+
property int revealCaps: 0
|
|
230
|
+
// The cap's start stamp and the sequence number of show()'s return, ordered by openSeq so tests/quicklook-firstkind.qml reads arm-before-show or after with no clock.
|
|
231
|
+
property var revealCapStart: null
|
|
232
|
+
property int openSeq: 0
|
|
233
|
+
property int shownSeq: 0
|
|
234
|
+
// The kinds whose Loader item this window has built once, so a repeat of one is a warm open.
|
|
235
|
+
property var builtKinds: ({})
|
|
236
|
+
function openState() {
|
|
237
|
+
return { revealed: root.revealed, revealedEarly: root.revealedEarly, landed: root.landed, lookEmptyFrames: root.lookEmptyFrames, revealCaps: root.revealCaps, revealCapStart: root.revealCapStart, shownSeq: root.shownSeq }
|
|
238
|
+
}
|
|
239
|
+
// A move still settling lands nothing, and followSettle reruns this once it stops, so what reveals is the cursor's own file.
|
|
240
|
+
// Past the cap, content that is ready waits out the loading state's minimum, so it never blinks for under 100 ms.
|
|
241
|
+
function revealIfReady() {
|
|
242
|
+
if (!root.active || !root.lookReady || followSettle.running)
|
|
243
|
+
return
|
|
244
|
+
var left = root.loadingEarly ? Swap.showLeft(root.loadingSince, Date.now()) : 0
|
|
245
|
+
if (left > 0) {
|
|
246
|
+
minShow.interval = left
|
|
247
|
+
minShow.restart()
|
|
248
|
+
} else {
|
|
249
|
+
root.landed = true
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function showEarly() {
|
|
253
|
+
root.loadingSince = Date.now()
|
|
254
|
+
root.revealedEarly = true
|
|
255
|
+
root.sizeWait = false
|
|
256
|
+
}
|
|
257
|
+
function revealNow() {
|
|
258
|
+
root.revealIfReady()
|
|
259
|
+
if (root.unrevealed)
|
|
260
|
+
root.showEarly()
|
|
261
|
+
}
|
|
262
|
+
// The ready test runs after the open's own turn, because lookReady still reads the closed overlay inside this handler.
|
|
263
|
+
onActiveChanged: {
|
|
264
|
+
root.revealedEarly = false
|
|
265
|
+
root.loadingSince = 0
|
|
266
|
+
root.landed = false
|
|
267
|
+
minShow.stop()
|
|
268
|
+
if (!root.active)
|
|
269
|
+
revealCap.stop()
|
|
270
|
+
else
|
|
271
|
+
Qt.callLater(root.revealIfReady)
|
|
272
|
+
}
|
|
273
|
+
onLookReadyChanged: root.revealIfReady()
|
|
274
|
+
// Armed by load(): before show() for a warm kind, after it for a kind's first build, beside the swap's own cap.
|
|
275
|
+
Timer {
|
|
276
|
+
id: revealCap
|
|
277
|
+
objectName: "revealCap"
|
|
278
|
+
repeat: false
|
|
279
|
+
onTriggered: {
|
|
280
|
+
if (!root.unrevealed)
|
|
281
|
+
return
|
|
282
|
+
root.revealCaps += 1
|
|
283
|
+
root.showEarly()
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// The early loading state has stood its minimum, so ready content lands now.
|
|
287
|
+
Timer {
|
|
288
|
+
id: minShow
|
|
289
|
+
repeat: false
|
|
290
|
+
onTriggered: root.revealIfReady()
|
|
291
|
+
}
|
|
292
|
+
// afterAnimating runs just before each frame is synchronised, so it sees a frame that draws the card or dim with neither content nor the loading state.
|
|
293
|
+
Connections {
|
|
294
|
+
target: root.active && !root.landed && !root.loadingEarly && (surface.opacity > 0 || lookDim.opacity > 0) ? root.Window.window : null
|
|
295
|
+
function onAfterAnimating() { root.lookEmptyFrames += 1 }
|
|
296
|
+
}
|
|
297
|
+
// One fade for the card and dim, so the hold can cut a close fade still running.
|
|
298
|
+
property real fade: root.revealedOpen ? 1 : 0
|
|
299
|
+
Behavior on fade {
|
|
300
|
+
enabled: !Theme.reducedMotion
|
|
301
|
+
NumberAnimation { duration: root.active ? Motion.durMs.open : Motion.durMs.close; easing.type: Easing.OutCubic }
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Read through to PreviewMedia so this file never imports QtMultimedia, and 0 before the loader has an item.
|
|
305
|
+
readonly property int position: (root.isMedia && mediaLoader.item) ? mediaLoader.item.position : 0
|
|
306
|
+
readonly property int duration: (root.isMedia && mediaLoader.item) ? mediaLoader.item.duration : 0
|
|
307
|
+
|
|
308
|
+
// Shown on open, hidden stripHideMs after the last reveal, video only: audio has nothing else to look at.
|
|
309
|
+
property bool stripShown: true
|
|
310
|
+
// StatusBar.messageMs, the OEM's own transient interval, which Sidebar's unmount arm reuses for the same reason.
|
|
311
|
+
property int stripHideMs: 4000
|
|
312
|
+
|
|
313
|
+
function revealStrip() {
|
|
314
|
+
root.stripShown = true
|
|
315
|
+
stripHideTimer.restart()
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function togglePlay() { if (root.isMedia && mediaLoader.item) mediaLoader.item.togglePlay() }
|
|
319
|
+
|
|
320
|
+
// MediaMute rule 3: one session flag, so the column's strip and this one always agree.
|
|
321
|
+
function toggleMute() { if (root.isMedia) Flea.MediaSound.toggle() }
|
|
322
|
+
|
|
323
|
+
// Absolute seek in ms, clamped by PreviewMedia's own seekTo; the slider's onReleased calls this directly.
|
|
324
|
+
function seekTo(ms) { if (root.isMedia && mediaLoader.item) mediaLoader.item.seekTo(ms) }
|
|
325
|
+
|
|
326
|
+
// Relative seek in ms, Left/Right's own shape; seekTo does the clamping.
|
|
327
|
+
function seek(deltaMs) {
|
|
328
|
+
root.seekTo(root.position + deltaMs)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// The PDF viewer's three actions come through this file, so ui/js/Focus.js never learns a Loader item answers them.
|
|
332
|
+
function turnPage(delta) { if (root.isPdf && pdfLoader.item) pdfLoader.item.turn(delta) }
|
|
333
|
+
|
|
334
|
+
function zoomBy(steps) { if (root.isPdf && pdfLoader.item) pdfLoader.item.zoomBy(steps); else if (root.isImage && imageLoader.item) imageLoader.item.zoomBy(steps); else if (root.isHtml && root.markdownShown) root.markdownShown.zoomBy(steps) }
|
|
335
|
+
function zoomFit() { if (root.isImage && imageLoader.item) imageLoader.item.zoomFit(); else if (root.isHtml && root.markdownShown) root.markdownShown.zoomFit() }
|
|
336
|
+
|
|
337
|
+
function toggleExpand() { if (root.isPdf && pdfLoader.item) pdfLoader.item.toggleExpand() }
|
|
338
|
+
|
|
339
|
+
// Space opens on the cursor row; this is immediate, follow() below is the held-key j/k path.
|
|
340
|
+
function open(newPath, newIcon, newSize, newKind, newThumb) {
|
|
341
|
+
followSettle.stop()
|
|
342
|
+
root.lastMoveKey = newPath + "\n" + newIcon + "\n" + newSize + "\n" + newKind
|
|
343
|
+
root.lastMoveAt = Date.now()
|
|
344
|
+
root.load(newPath, newIcon, newSize, newKind, newThumb, root.pane ? root.pane.cursorIndex : -1)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// The Compare overlay for two regular files; iconName is the first file's, which picks the bar's mark.
|
|
348
|
+
function openCompare(pathA, pathB, newIcon) {
|
|
349
|
+
followSettle.stop()
|
|
350
|
+
root.comparePath = pathB
|
|
351
|
+
root.iconName = newIcon || ""
|
|
352
|
+
root.path = pathA
|
|
353
|
+
root.size = 0
|
|
354
|
+
root.kindName = ""
|
|
355
|
+
root.kind = "compare"
|
|
356
|
+
root.active = true
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// y: the patch goes to the clipboard; any other kind has none.
|
|
360
|
+
function copyPatch() { if (root.isCompare && compareLoader.item) compareLoader.item.copyPatch() }
|
|
361
|
+
|
|
362
|
+
// Document Quick Looks open rendered; r flips only the open one.
|
|
363
|
+
function toggleMarkdownView() {
|
|
364
|
+
if (root.isDocument) root.markdownSource = !root.markdownSource
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function toggleMarkdownClose() {
|
|
368
|
+
if (root.hasBar) root.markdownCloseFocus = !root.markdownCloseFocus
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// w wraps the open code or text Quick Look, and a Markdown Source; ui.json keeps the choice for the next one.
|
|
372
|
+
function toggleWrap() {
|
|
373
|
+
if (root.isCodeText || (root.isDocument && root.markdownShown && root.markdownShown.shownView === "source")) ViewState.changeLeaf("preview", { wrap: !ViewState.previewWrap })
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// The picture is taken now, so the settled load below changes the panes under it.
|
|
377
|
+
function follow(newPath, newIcon, newSize, newKind, newThumb) {
|
|
378
|
+
var key = newPath + "\n" + newIcon + "\n" + newSize + "\n" + newKind
|
|
379
|
+
// Pending covers a repeat; a settled revisit of the shown target stays put, anything else reloads.
|
|
380
|
+
var now = Date.now()
|
|
381
|
+
var decision = PreviewSettle.plan(now, root.lastMoveAt, root.followSettleMs, key, root.lastMoveKey, followSettle.running, root.unrested(key))
|
|
382
|
+
if (decision === "same") return
|
|
383
|
+
if (key === root.lastMoveKey && root.isShown(newPath, newIcon, newSize, newKind)) return
|
|
384
|
+
root.lastMoveKey = key
|
|
385
|
+
root.lastMoveAt = now
|
|
386
|
+
root.pendingPath = newPath
|
|
387
|
+
root.pendingIcon = newIcon
|
|
388
|
+
root.pendingSize = newSize
|
|
389
|
+
root.pendingKind = newKind
|
|
390
|
+
root.pendingThumb = newThumb || ""
|
|
391
|
+
root.pendingImageRow = root.pane ? root.pane.cursorIndex : -1
|
|
392
|
+
// During the hold a move takes no picture, and a burst still trails so it asks nothing per row.
|
|
393
|
+
if (root.revealedOpen) {
|
|
394
|
+
var held = root.ensureSwap()
|
|
395
|
+
if (held)
|
|
396
|
+
held.hold(null, newPath)
|
|
397
|
+
}
|
|
398
|
+
if (decision === "now") {
|
|
399
|
+
followSettle.stop()
|
|
400
|
+
root.load(newPath, newIcon, newSize, newKind, newThumb, root.pendingImageRow)
|
|
401
|
+
// Armed once the load's own work returns, work a starved loop can stretch past the settle.
|
|
402
|
+
rested.restart()
|
|
403
|
+
} else {
|
|
404
|
+
followSettle.restart()
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Whether the overlay already shows this target: the duplicate test the key clock cannot make.
|
|
409
|
+
function isShown(newPath, newIcon, newSize, newKind) {
|
|
410
|
+
return root.active && newPath === root.path && newIcon === root.iconName
|
|
411
|
+
&& newSize === root.size && newKind === root.kindName
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Dropping the loader's source is what stops playback: media dies with the loader.
|
|
415
|
+
function close() {
|
|
416
|
+
if (root.swap) root.swap.cancel()
|
|
417
|
+
root.revealCapStart = null
|
|
418
|
+
followSettle.stop()
|
|
419
|
+
root.lastMoveKey = ""
|
|
420
|
+
root.pendingThumb = ""
|
|
421
|
+
root.pendingImageRow = -1
|
|
422
|
+
root.interimThumb = ""
|
|
423
|
+
root.interimStamp = ""
|
|
424
|
+
root.imageW = 0
|
|
425
|
+
root.imageH = 0
|
|
426
|
+
root.imageOrient = 1
|
|
427
|
+
root.imageFrames = 0
|
|
428
|
+
root.imageRow = -1
|
|
429
|
+
root.imageAsked = false
|
|
430
|
+
root.thumbRow = -1
|
|
431
|
+
root.thumbAsked = false
|
|
432
|
+
root.thumbRelist = false
|
|
433
|
+
root.rawCopy = null
|
|
434
|
+
root.rawAnswered = false
|
|
435
|
+
stripHideTimer.stop()
|
|
436
|
+
root.active = false
|
|
437
|
+
root.kind = ""
|
|
438
|
+
root.markdownSource = false
|
|
439
|
+
root.markdownCloseFocus = false
|
|
440
|
+
root.inlineMarkdownPath = ""
|
|
441
|
+
root.comparePath = ""
|
|
442
|
+
mediaLoader.source = ""
|
|
443
|
+
pdfLoader.source = ""
|
|
444
|
+
imageLoader.source = ""
|
|
445
|
+
root.archiveMeta = null
|
|
446
|
+
root.archiveRow = -1
|
|
447
|
+
root.mediaRate = 0
|
|
448
|
+
root.mediaRow = -1
|
|
449
|
+
if (root.pane) root.pane.listArea.forceActiveFocus()
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Under the held picture when a move took one; Space's own open has none and reveals once its content can draw.
|
|
453
|
+
// The swap starts after its mutation: show runs at once or waits for the capture, start runs with it.
|
|
454
|
+
function load(newPath, newIcon, newSize, newKind, newThumb, newRow) {
|
|
455
|
+
var isPdf = Kinds.quickLookKind(newIcon, newPath) === Kinds.PDF
|
|
456
|
+
var swapItem = root.ensureSwap()
|
|
457
|
+
var show = function () {
|
|
458
|
+
// A warm kind counts from the key, before show() builds the Loader items; a kind's first build (a PDF's compile above all) is outside the cap, which starts once show() returns; a move during the hold keeps the open's deadline.
|
|
459
|
+
var kindKey = root.kindFor(newPath, newIcon, newThumb, newRow)
|
|
460
|
+
var arm = (!root.active || !root.revealed) && !revealCap.running
|
|
461
|
+
var warm = root.builtKinds[kindKey] === true
|
|
462
|
+
if (arm && warm) root.armRevealCap(isPdf)
|
|
463
|
+
root.show(newPath, newIcon, newSize, newKind, newThumb, newRow)
|
|
464
|
+
if (swapItem) swapItem.start(isPdf)
|
|
465
|
+
root.shownSeq = ++root.openSeq
|
|
466
|
+
root.builtKinds[kindKey] = true
|
|
467
|
+
if (arm && !warm) root.armRevealCap(isPdf)
|
|
468
|
+
else if (!arm && !revealCap.running) root.revealCapStart = null
|
|
469
|
+
}
|
|
470
|
+
if (swapItem && (swapItem.holding || swapItem.capturing))
|
|
471
|
+
swapItem.hold(show, newPath, true)
|
|
472
|
+
else
|
|
473
|
+
show()
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// The row at index while its name under the listing's base is path, else null: how this file identifies a row.
|
|
477
|
+
function namedRow(index, path) {
|
|
478
|
+
if (!root.pane || index < 0) return null
|
|
479
|
+
var row = root.pane.rowFor(index)
|
|
480
|
+
return row !== null && root.pane.join(Search.baseOf(root.pane), row.n) === path ? row : null
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function kindFor(newPath, newIcon, newThumb, newRow) {
|
|
484
|
+
var row = root.namedRow(newRow, newPath)
|
|
485
|
+
return Kinds.quickLookKind(newIcon, newPath, (row !== null && row.t === true) || (newThumb || "").length > 0)
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function armRevealCap(isPdf) {
|
|
489
|
+
revealCap.interval = PreviewSwap.capMs(isPdf)
|
|
490
|
+
revealCap.start()
|
|
491
|
+
root.revealCapStart = { seq: ++root.openSeq }
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function show(newPath, newIcon, newSize, newKind, newThumb, newRow) {
|
|
495
|
+
var row = root.namedRow(newRow, newPath)
|
|
496
|
+
root.inlineMarkdownPath = row !== null && Prepared.readsInline(row, root.pane.storageClass, root.pane.storageKnown) ? newPath : ""
|
|
497
|
+
var kind = root.kindFor(newPath, newIcon, newThumb, newRow)
|
|
498
|
+
// Gate the live image reader before its path moves, so a follow cannot decode at the previous card size while its dimensions are owed.
|
|
499
|
+
root.sizeWait = kind === "image" && root.pane !== null && newRow >= 0
|
|
500
|
+
root.kind = kind
|
|
501
|
+
// A pane of another kind goes before the path moves, or it tries to open a file it cannot draw: an image
|
|
502
|
+
// pane handed a video logged "Unsupported image format" on every move from a picture to a clip.
|
|
503
|
+
if (!root.isMedia) mediaLoader.source = ""
|
|
504
|
+
if (!root.isPdf) pdfLoader.source = ""
|
|
505
|
+
if (!root.isImage) imageLoader.source = ""
|
|
506
|
+
// The interim holds the path it was read for; a stale one never outlives a move.
|
|
507
|
+
root.interimThumb = newThumb || ""
|
|
508
|
+
root.interimStamp = newPath
|
|
509
|
+
// The meta row is the one captured at open/follow, never the cursor now.
|
|
510
|
+
root.imageRow = root.isImage && newRow !== undefined ? newRow : -1
|
|
511
|
+
root.imageW = 0
|
|
512
|
+
root.imageH = 0
|
|
513
|
+
root.imageOrient = 1
|
|
514
|
+
root.imageFrames = 0
|
|
515
|
+
root.imageAsked = false
|
|
516
|
+
root.thumbAsked = false
|
|
517
|
+
root.thumbRelist = false
|
|
518
|
+
root.rawCopy = null
|
|
519
|
+
root.rawAnswered = false
|
|
520
|
+
root.shapeNext = { kind: root.kind, known: false, w: 0, h: 0, orient: 1 }
|
|
521
|
+
root.commitShape()
|
|
522
|
+
root.path = newPath
|
|
523
|
+
root.thumbRow = root.thumbOwed(newRow)
|
|
524
|
+
root.iconName = newIcon
|
|
525
|
+
root.size = newSize
|
|
526
|
+
// MediaPdf rule 6: the overlay is the bigger surface, so it says at least what the column says, and the kind is the caller's because the backend named it for that row.
|
|
527
|
+
root.kindName = newKind || ""
|
|
528
|
+
root.active = true
|
|
529
|
+
mediaLoader.source = root.isMedia ? "PreviewMedia.qml" : ""
|
|
530
|
+
pdfLoader.source = root.isPdf ? "PdfViewer.qml" : ""
|
|
531
|
+
imageLoader.source = root.isImage ? "PreviewImage.qml" : ""
|
|
532
|
+
root.askArchive()
|
|
533
|
+
root.askMedia()
|
|
534
|
+
root.askImage()
|
|
535
|
+
root.sizeWait = root.isImage && root.imageAsked && root.imageRow >= 0
|
|
536
|
+
root.revealStrip()
|
|
537
|
+
root.maybePrefetch()
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Bounded prefetch: the next row's cache entry only, once per rest, never a decode and never a meta.
|
|
541
|
+
function maybePrefetch() {
|
|
542
|
+
if (!root.active || !root.pane || followSettle.running) return
|
|
543
|
+
if (!ViewState.previewAutomatic) return
|
|
544
|
+
var next = root.pane.cursorIndex + 1
|
|
545
|
+
if (root.pane.thumbState.file[next] !== undefined) return
|
|
546
|
+
var row = root.pane.rowFor(next)
|
|
547
|
+
if (!row || row.d || row.t !== true || !root.thumbClear(row)) return
|
|
548
|
+
var work = { ask: [next], drop: [], cacheOnly: true }
|
|
549
|
+
root.pane.thumbState = Thumbs.applied(root.pane.thumbState, work)
|
|
550
|
+
root.pane.backend.thumb(work.ask, true)
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Whether a thumbnail ask may go out for this row here: the listing settled, the class known, no manual hold, and the row allowed.
|
|
554
|
+
function thumbClear(row) {
|
|
555
|
+
if (!root.pane || root.pane.listInFlight || !root.pane.storageKnown) return false
|
|
556
|
+
if (ExtThumbs.manualHold(root.pane.storageClass, ViewState.preview)) return false
|
|
557
|
+
return Thumbs.allowed(row, ViewState.thumbnailMode)
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// One row, only while an archive is the thing open: the same no-sweep rule the column follows.
|
|
561
|
+
function askArchive() {
|
|
562
|
+
root.archiveMeta = null
|
|
563
|
+
root.archiveRow = root.isArchive && root.pane ? root.pane.cursorIndex : -1
|
|
564
|
+
if (root.archiveRow >= 0)
|
|
565
|
+
root.pane.backend.askMeta(root.archiveRow, false, false, true)
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// The same one row, for the one fact the transport under the overlay cannot report.
|
|
569
|
+
function askMedia() {
|
|
570
|
+
root.mediaRate = 0
|
|
571
|
+
root.mediaRow = root.isMedia && root.pane ? root.pane.cursorIndex : -1
|
|
572
|
+
if (root.mediaRow >= 0)
|
|
573
|
+
root.pane.backend.askMeta(root.mediaRow, false, true, false)
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// The original's pixels, which size the interim and the card; once per show, never while a held key is still bursting.
|
|
577
|
+
function askImage() {
|
|
578
|
+
if (!root.isImage || !root.pane) return
|
|
579
|
+
if (root.imageRow < 0 || root.imageAsked || followSettle.running) return
|
|
580
|
+
// An insert or a re-sort between capture and ask moves another file to the index,
|
|
581
|
+
// so the row must still name the shown file before anything is asked for it.
|
|
582
|
+
root.resolveImageRow()
|
|
583
|
+
root.imageAsked = true
|
|
584
|
+
if (root.imageRow < 0) return
|
|
585
|
+
root.pane.backend.askMeta(root.imageRow, false, false, false)
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function imageRowShown() {
|
|
589
|
+
return root.namedRow(root.imageRow, root.path) !== null
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// The captured index drifted onto another file: take the cursor when it names the
|
|
593
|
+
// shown file instead, else drop the interim for this show with no ask.
|
|
594
|
+
function resolveImageRow() {
|
|
595
|
+
if (root.imageRowShown()) return
|
|
596
|
+
var at = root.pane ? root.pane.cursorIndex : -1
|
|
597
|
+
root.imageRow = root.namedRow(at, root.path) !== null ? at : -1
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// A re-list clears the owed row's entry: take the cursor when it names the shown file, else drop the owed thumbnail with no ask.
|
|
601
|
+
function resolveThumbRow() {
|
|
602
|
+
if (root.namedRow(root.thumbRow, root.path) !== null) return
|
|
603
|
+
var at = root.pane ? root.pane.cursorIndex : -1
|
|
604
|
+
root.thumbRow = root.namedRow(at, root.path) !== null ? at : -1
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// The row whose thumbnail may stand in if this image cannot decode: only with none held, clear to ask, and not refused.
|
|
608
|
+
function thumbOwed(newRow) {
|
|
609
|
+
if (!root.isImage || root.interimThumb.length > 0) return -1
|
|
610
|
+
var at = newRow !== undefined ? newRow : -1
|
|
611
|
+
if (!root.thumbClear(root.namedRow(at, root.path)) || Thumbs.refused(root.pane.thumbState, at)) return -1
|
|
612
|
+
return at
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// After a failed final decode: ask the one row's thumbnail, then bind its reply to the shown file; a refusal or a forgotten ask ends on the sentence.
|
|
616
|
+
function settleThumb() {
|
|
617
|
+
if (root.thumbRow < 0 || !root.imageUndecodable || root.pane.listInFlight) return
|
|
618
|
+
// A re-list cleared the map under the owed row: find the shown file again and ask once on the new listing, or end on the sentence when it is gone.
|
|
619
|
+
if (root.thumbRelist) {
|
|
620
|
+
root.thumbRelist = false
|
|
621
|
+
root.resolveThumbRow()
|
|
622
|
+
root.thumbAsked = false
|
|
623
|
+
if (root.thumbRow < 0) return
|
|
624
|
+
}
|
|
625
|
+
// An insert or a re-sort since the capture moves another file to the index, so the row must still name the shown file.
|
|
626
|
+
var at = root.namedRow(root.thumbRow, root.path) !== null ? root.thumbRow : -1
|
|
627
|
+
var state = root.pane.thumbState
|
|
628
|
+
var file = at >= 0 ? Thumbs.fileFor(state, at) : ""
|
|
629
|
+
// Nothing more is owed once a name landed, the row was refused, or our own ask was forgotten.
|
|
630
|
+
if (at < 0 || file.length > 0 || Thumbs.refused(state, at) || (root.thumbAsked && state.file[at] === undefined)) {
|
|
631
|
+
if (file.length > 0) root.interimThumb = file
|
|
632
|
+
root.thumbRow = -1
|
|
633
|
+
return
|
|
634
|
+
}
|
|
635
|
+
if (root.thumbAsked || Thumbs.pending(state.file[at])) return
|
|
636
|
+
root.thumbAsked = true
|
|
637
|
+
var work = { ask: [at], drop: [], cacheOnly: false }
|
|
638
|
+
root.pane.thumbState = Thumbs.applied(state, work)
|
|
639
|
+
root.pane.backend.thumb(work.ask, false)
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
Connections {
|
|
643
|
+
target: root.pane ? root.pane.backend : null
|
|
644
|
+
function onMeta(row, w, h, orient, durationMs, sampleRate, entries, unpacked, archiveFailed, names, lines, partial, linesFailed, target, targetDir, owner) {
|
|
645
|
+
if (root.isArchive && row === root.archiveRow)
|
|
646
|
+
root.archiveMeta = { entries: entries, unpacked: unpacked, archiveFailed: archiveFailed, names: names }
|
|
647
|
+
if (root.isMedia && row === root.mediaRow)
|
|
648
|
+
root.mediaRate = sampleRate
|
|
649
|
+
if (root.isImage && !root.isRawFile && row === root.imageRow && root.imageRowShown()) {
|
|
650
|
+
root.imageW = w
|
|
651
|
+
root.imageH = h
|
|
652
|
+
root.imageOrient = orient
|
|
653
|
+
root.sizeArrived(w, h, orient, true)
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
// The RAW's own pixels are not what is drawn, so its size and turn come from the copy and not from the file's header.
|
|
657
|
+
function onMetaResult(message) {
|
|
658
|
+
if (!root.isRawFile || message.row !== root.imageRow || !root.imageRowShown()) return
|
|
659
|
+
root.rawCopy = message.raw || null
|
|
660
|
+
root.rawAnswered = true
|
|
661
|
+
if (root.rawCopy === null) {
|
|
662
|
+
root.sizeArrived(0, 0, 1, false)
|
|
663
|
+
return
|
|
664
|
+
}
|
|
665
|
+
root.imageW = root.rawCopy.w
|
|
666
|
+
root.imageH = root.rawCopy.h
|
|
667
|
+
root.imageOrient = root.rawCopy.orient
|
|
668
|
+
root.sizeArrived(root.rawCopy.w, root.rawCopy.h, root.rawCopy.orient, true)
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
Connections {
|
|
673
|
+
target: root.pane ? root.pane.backend : null
|
|
674
|
+
function onMetaResult(message) {
|
|
675
|
+
if (root.isImage && message.row === root.imageRow && root.imageRowShown()) root.imageFrames = message.frames || 0
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// A meta asked across a listing change is answered with silence, so the rows landing re-asks it, the way ui/ColumnsArea.qml does.
|
|
680
|
+
Connections {
|
|
681
|
+
target: root.pane
|
|
682
|
+
function onRowsChanged() {
|
|
683
|
+
if (root.active && root.isArchive && root.archiveMeta === null) root.askArchive()
|
|
684
|
+
if (root.active && root.isMedia && root.mediaRate === 0) root.askMedia()
|
|
685
|
+
if (root.active && root.isImage && root.imageW === 0 && !root.rawAnswered) root.askImage()
|
|
686
|
+
}
|
|
687
|
+
// The cursor and the rows are final once the listing lands, so an owed row is found again there.
|
|
688
|
+
function onListInFlightChanged() {
|
|
689
|
+
if (!root.pane) return
|
|
690
|
+
if (root.pane.listInFlight) root.thumbRelist = root.thumbRelist || root.thumbRow >= 0
|
|
691
|
+
else root.settleThumb()
|
|
692
|
+
}
|
|
693
|
+
function onThumbStateChanged() { root.settleThumb() }
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// The cursor has not rested since its last move: an earlier row's move still trailing, or a load made at once whose rest has not run out.
|
|
697
|
+
function unrested(key) { return (followSettle.running && root.lastMoveKey !== key) || rested.running }
|
|
698
|
+
Timer {
|
|
699
|
+
id: followSettle
|
|
700
|
+
objectName: "followSettle"
|
|
701
|
+
interval: root.followSettleMs
|
|
702
|
+
repeat: false
|
|
703
|
+
onTriggered: root.load(root.pendingPath, root.pendingIcon, root.pendingSize, root.pendingKind, root.pendingThumb, root.pendingImageRow)
|
|
704
|
+
onRunningChanged: if (!followSettle.running) Qt.callLater(root.revealIfReady)
|
|
705
|
+
}
|
|
706
|
+
// A single-shot Timer runs until its posted trigger is handled, so a repeat a starved loop serves first has not rested and trails.
|
|
707
|
+
Timer { id: rested; interval: root.followSettleMs }
|
|
708
|
+
|
|
709
|
+
Timer {
|
|
710
|
+
id: stripHideTimer
|
|
711
|
+
interval: root.stripHideMs
|
|
712
|
+
repeat: false
|
|
713
|
+
// A held seek or volume press is contact too, so the strip waits for the release before it goes.
|
|
714
|
+
onTriggered: {
|
|
715
|
+
if (mediaStrip.scrubbing || (mediaStrip.volumeItem && mediaStrip.volumeItem.pressing)) stripHideTimer.restart()
|
|
716
|
+
else root.stripShown = false
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
MouseArea {
|
|
721
|
+
anchors.fill: parent
|
|
722
|
+
// A shield that outlives the overlay swallows the first click after it and freezes row hover for the window.
|
|
723
|
+
enabled: root.active
|
|
724
|
+
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
725
|
+
hoverEnabled: true
|
|
726
|
+
// A click behind the overlay would silently move the cursor or open the listing's menu on it.
|
|
727
|
+
// A left click outside the surface closes, a rule of 2026-09-11, and a right click is only swallowed.
|
|
728
|
+
onClicked: function (mouse) {
|
|
729
|
+
if (mouse.button === Qt.LeftButton && !surface.contains(surface.mapFromItem(root, mouse.x, mouse.y)))
|
|
730
|
+
root.close()
|
|
731
|
+
}
|
|
732
|
+
onPositionChanged: root.revealStrip()
|
|
733
|
+
// The ground takes the wheel, so a scroll over the overlay never reaches the listing behind it.
|
|
734
|
+
onWheel: function (wheel) { wheel.accepted = true }
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// PdfViewer.html and MediaPlayer.html draw a pane with its own edge: on the surface colour alone the inset vanished into the listing behind it.
|
|
738
|
+
Rectangle {
|
|
739
|
+
id: lookDim
|
|
740
|
+
anchors.fill: parent
|
|
741
|
+
color: Theme.color.background
|
|
742
|
+
opacity: root.unrevealed ? 0 : root.fade * root.groundOpacity
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
Rectangle {
|
|
746
|
+
id: surface
|
|
747
|
+
x: Theme.cardOrigin(parent.width, width)
|
|
748
|
+
y: Theme.cardOrigin(parent.height, height) + surface.rise
|
|
749
|
+
border.width: Theme.spacing.hairline
|
|
750
|
+
border.color: Theme.color.mutedFrame
|
|
751
|
+
// Open rises into place and close only fades, faster, because the translation is enabled: root.active.
|
|
752
|
+
property real rise: root.revealedOpen ? 0 : Motion.translateUpPx
|
|
753
|
+
opacity: root.unrevealed ? 0 : root.fade
|
|
754
|
+
// Tall kinds take the window's height less the edge, and Expand fills the window, which is the whole of the canvas's "expand fills the window".
|
|
755
|
+
width: Theme.cardSpan(root.rect.w, parent.width)
|
|
756
|
+
height: Theme.cardSpan(root.rect.h, parent.height)
|
|
757
|
+
color: Theme.color.surface
|
|
758
|
+
// Mirrors hyprland decoration:rounding; media fills the surface and keeps square corners, a visible corner only shows on text and audio panes.
|
|
759
|
+
radius: Style.cornerRadius
|
|
760
|
+
|
|
761
|
+
Behavior on rise {
|
|
762
|
+
enabled: root.active && !Theme.reducedMotion
|
|
763
|
+
NumberAnimation { duration: Motion.durMs.open; easing.type: Easing.OutCubic }
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// Every kind's pane, eager the way 0.3.5 drew them: the first Space decodes and never
|
|
767
|
+
// builds. Moving to the next file holds this one's picture through the swap below until that one is whole.
|
|
768
|
+
Item {
|
|
769
|
+
id: panes
|
|
770
|
+
// The size the next card has, so a preview built under a held picture is whole at the size it lands on.
|
|
771
|
+
width: Theme.cardSpan(root.nextRect.w, root.width)
|
|
772
|
+
height: Theme.cardSpan(root.nextRect.h, root.height)
|
|
773
|
+
|
|
774
|
+
Flea.PreviewText {
|
|
775
|
+
id: textPane
|
|
776
|
+
anchors.fill: parent
|
|
777
|
+
anchors.margins: Theme.spacing.hairline
|
|
778
|
+
active: root.isCodeText
|
|
779
|
+
path: root.path
|
|
780
|
+
size: root.size
|
|
781
|
+
backend: root.pane ? root.pane.backend : null
|
|
782
|
+
markName: Icons.glyphFor(root.iconName)
|
|
783
|
+
// The column's rule: code gets the gutter, plain text none.
|
|
784
|
+
numbered: Kinds.kindState(root.iconName) === Kinds.CODE || Kinds.isCode(root.path)
|
|
785
|
+
wrap: ViewState.previewWrap
|
|
786
|
+
closeFocused: root.markdownCloseFocused
|
|
787
|
+
onCloseRequested: root.close()
|
|
788
|
+
// ExtThumbs: Quick Look reads at most the first 256 KiB on network and phone storage.
|
|
789
|
+
maxBytes: ExtThumbs.textLimit(root.pane ? root.pane.storageClass : "")
|
|
790
|
+
truncate: root.pane ? (root.pane.storageClass === "network" || root.pane.storageClass === "phone") : false
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// A file-path Loader, never an inline Component, so a window that never shows Markdown never compiles it.
|
|
794
|
+
Loader {
|
|
795
|
+
id: markdownLoader
|
|
796
|
+
anchors.fill: parent
|
|
797
|
+
// The pane lies inside the surface's own frame, as the column's does; media fills it on purpose.
|
|
798
|
+
anchors.margins: Theme.spacing.hairline
|
|
799
|
+
active: root.isDocument
|
|
800
|
+
source: root.isHtml ? "HtmlPane.qml" : "MarkdownPane.qml"
|
|
801
|
+
onLoaded: {
|
|
802
|
+
item.blockPath = Qt.binding(function () { return root.inlineMarkdownPath })
|
|
803
|
+
item.iconName = Qt.binding(function () { return root.iconName })
|
|
804
|
+
item.path = Qt.binding(function () { return root.path })
|
|
805
|
+
item.size = Qt.binding(function () { return root.size })
|
|
806
|
+
item.view = Qt.binding(function () { return root.markdownSource ? "source" : "rendered" })
|
|
807
|
+
item.maxBytes = Qt.binding(function () {
|
|
808
|
+
return root.isOffice ? Kinds.OFFICE_LIMIT : ExtThumbs.textLimit(root.pane ? root.pane.storageClass : "")
|
|
809
|
+
})
|
|
810
|
+
item.truncate = Qt.binding(function () {
|
|
811
|
+
return root.pane ? (root.pane.storageClass === "network"
|
|
812
|
+
|| root.pane.storageClass === "phone") : false
|
|
813
|
+
})
|
|
814
|
+
item.active = Qt.binding(function () { return root.isDocument })
|
|
815
|
+
item.closeFocused = Qt.binding(function () { return root.markdownCloseFocused })
|
|
816
|
+
item.backend = Qt.binding(function () { return root.pane ? root.pane.backend : null })
|
|
817
|
+
item.wrap = Qt.binding(function () { return ViewState.previewWrap })
|
|
818
|
+
item.closeRequested.connect(root.close)
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
Loader {
|
|
823
|
+
id: fontLoader
|
|
824
|
+
anchors.fill: parent
|
|
825
|
+
anchors.margins: Theme.spacing.hairline
|
|
826
|
+
active: root.isFont
|
|
827
|
+
source: "FontPane.qml"
|
|
828
|
+
onLoaded: {
|
|
829
|
+
item.path = Qt.binding(function () { return root.path })
|
|
830
|
+
item.size = Qt.binding(function () { return root.size })
|
|
831
|
+
item.backend = Qt.binding(function () { return root.pane ? root.pane.backend : null })
|
|
832
|
+
item.active = Qt.binding(function () { return root.isFont })
|
|
833
|
+
item.closeRequested.connect(root.close)
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// A file-path Loader, so a window that never compares compiles none of it; the item asks its one diff on load.
|
|
838
|
+
Loader {
|
|
839
|
+
id: compareLoader
|
|
840
|
+
anchors.fill: parent
|
|
841
|
+
active: root.isCompare
|
|
842
|
+
source: "CompareView.qml"
|
|
843
|
+
onLoaded: {
|
|
844
|
+
item.backend = Qt.binding(function () { return root.pane ? root.pane.backend : null })
|
|
845
|
+
item.opener = Qt.binding(function () { return root.pane ? root.pane.opener : null })
|
|
846
|
+
item.pathA = Qt.binding(function () { return root.path })
|
|
847
|
+
item.pathB = Qt.binding(function () { return root.comparePath })
|
|
848
|
+
item.iconName = Qt.binding(function () { return root.iconName })
|
|
849
|
+
item.active = Qt.binding(function () { return root.active })
|
|
850
|
+
item.closeRequested.connect(root.close)
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
Loader {
|
|
855
|
+
id: mediaLoader
|
|
856
|
+
anchors.fill: parent
|
|
857
|
+
onLoaded: {
|
|
858
|
+
item.path = Qt.binding(function () { return root.path })
|
|
859
|
+
item.kind = Qt.binding(function () { return root.kind })
|
|
860
|
+
item.size = Qt.binding(function () { return root.size })
|
|
861
|
+
item.kindName = Qt.binding(function () { return root.kindName })
|
|
862
|
+
item.rate = Qt.binding(function () { return root.mediaRate })
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// source rather than sourceComponent, so a file is decoded only while an image is open and its texture goes with the item.
|
|
867
|
+
Loader {
|
|
868
|
+
id: imageLoader
|
|
869
|
+
anchors.fill: parent
|
|
870
|
+
// With no thumbnail to show, built once the size reply lands or the cap passes, so the picture decodes once at the card's final size.
|
|
871
|
+
active: !root.sizeWait || root.interimThumb.length > 0
|
|
872
|
+
onLoaded: {
|
|
873
|
+
item.path = Qt.binding(function () { return root.path })
|
|
874
|
+
item.origW = Qt.binding(function () { return root.imageW })
|
|
875
|
+
item.origH = Qt.binding(function () { return root.imageH })
|
|
876
|
+
item.thumbPending = Qt.binding(function () { return root.thumbRow >= 0 })
|
|
877
|
+
item.frames = Qt.binding(function () { return root.imageFrames })
|
|
878
|
+
item.origOrient = Qt.binding(function () { return root.imageOrient })
|
|
879
|
+
item.embedded = Qt.binding(function () { return root.rawCopy !== null ? root.rawCopy.file : "" })
|
|
880
|
+
item.embeddedAnswered = Qt.binding(function () { return root.rawAnswered })
|
|
881
|
+
item.interimThumb = Qt.binding(function () { return root.interimThumb })
|
|
882
|
+
item.interimVisible = Qt.binding(function () { return root.interimVisible })
|
|
883
|
+
item.interimX = Qt.binding(function () { return root.interimBox !== null ? root.interimBox.x : 0 })
|
|
884
|
+
item.interimY = Qt.binding(function () { return root.interimBox !== null ? root.interimBox.y : 0 })
|
|
885
|
+
item.interimWidth = Qt.binding(function () { return root.interimBox !== null ? root.interimBox.w : 0 })
|
|
886
|
+
item.interimHeight = Qt.binding(function () { return root.interimBox !== null ? root.interimBox.h : 0 })
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// The canvas's PdfViewer, source not sourceComponent, so QtQuick.Pdf loads on the first PDF and never for a folder without one.
|
|
891
|
+
Loader {
|
|
892
|
+
id: pdfLoader
|
|
893
|
+
anchors.fill: parent
|
|
894
|
+
onLoaded: {
|
|
895
|
+
item.path = Qt.binding(function () { return root.path })
|
|
896
|
+
item.active = true
|
|
897
|
+
// A document on a hangable class loads from the backend's fetched copy.
|
|
898
|
+
item.backend = Qt.binding(function () { return root.pane ? root.pane.backend : null })
|
|
899
|
+
item.fetchFirst = Qt.binding(function () { return root.pane ? ExtThumbs.present(root.pane.storageClass) : false })
|
|
900
|
+
item.viewerSlot = "quicklook"
|
|
901
|
+
item.forceActiveFocus()
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
Connections {
|
|
906
|
+
target: pdfLoader.item
|
|
907
|
+
function onClosed() { root.close() }
|
|
908
|
+
function onPageAspectChanged() { root.sizeArrived(pdfLoader.item.pageAspect, 1, 1, pdfLoader.item.pageAspect > 0) }
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// The canvas's Archive tile at Quick Look size, its own component so this file stays inside its ceiling.
|
|
912
|
+
Flea.QuickLookArchive {
|
|
913
|
+
id: archivePane
|
|
914
|
+
anchors.fill: parent
|
|
915
|
+
anchors.margins: Theme.spacing.rowPaddingX
|
|
916
|
+
visible: root.isArchive && root.archiveMeta !== null && !root.archiveFailed
|
|
917
|
+
path: root.path
|
|
918
|
+
meta: root.archiveMeta
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// Declined, or an archive whose index could not be read: a mark over the sentence, never a bare surface.
|
|
922
|
+
Column {
|
|
923
|
+
anchors.centerIn: parent
|
|
924
|
+
width: parent.width - 2 * Theme.spacing.rowPaddingX
|
|
925
|
+
spacing: Theme.spacing.gap
|
|
926
|
+
visible: false // Unavailable non-text previews remain blank.
|
|
927
|
+
|
|
928
|
+
Flea.Glyph {
|
|
929
|
+
anchors.horizontalCenter: parent.horizontalCenter
|
|
930
|
+
// The overlay declining is a pane state standing alone, which States.dc.html draws at 40.
|
|
931
|
+
maxSize: Theme.stateMarkSize
|
|
932
|
+
width: Theme.stateMarkSize
|
|
933
|
+
height: Theme.stateMarkSize
|
|
934
|
+
name: root.archiveFailed ? "alert" : "file"
|
|
935
|
+
color: root.archiveFailed ? Theme.color.error : Theme.color.mutedFrame
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
Text {
|
|
939
|
+
width: parent.width
|
|
940
|
+
horizontalAlignment: Text.AlignHCenter
|
|
941
|
+
text: root.status
|
|
942
|
+
color: root.archiveFailed ? Theme.color.foreground : Theme.color.muted
|
|
943
|
+
font.family: Theme.font.family
|
|
944
|
+
font.pixelSize: Theme.font.body
|
|
945
|
+
textFormat: Text.PlainText
|
|
946
|
+
wrapMode: Text.Wrap
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// Media still buffering or an image still decoding shows the crawl; LoadingState's hold-off keeps a fast local open from flashing it.
|
|
951
|
+
Flea.LoadingState {
|
|
952
|
+
anchors.fill: parent
|
|
953
|
+
visible: root.loadingEarly || (root.isMedia || root.isImage || root.isArchive || root.isFont) && root.status === "loading"
|
|
954
|
+
heldOff: root.loadingEarly || root.swap !== null && root.swap.fellBack
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// MediaStrip unframed: quiet over the video, permanent on audio, and the column draws the framed form of the same file.
|
|
958
|
+
Flea.MediaStrip {
|
|
959
|
+
id: mediaStrip
|
|
960
|
+
visible: root.isMedia && (root.kind === "audio" || root.stripShown)
|
|
961
|
+
anchors.left: parent.left
|
|
962
|
+
anchors.right: parent.right
|
|
963
|
+
anchors.bottom: parent.bottom
|
|
964
|
+
framed: false
|
|
965
|
+
playing: root.status === "playing"
|
|
966
|
+
position: root.position
|
|
967
|
+
duration: root.duration
|
|
968
|
+
onToggled: root.togglePlay()
|
|
969
|
+
onSeeked: function (ms) { root.seekTo(ms) }
|
|
970
|
+
onTouched: root.revealStrip()
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// The swap wrapper waits for the first open, so launch pays no Loader, compile or picture.
|
|
975
|
+
Loader {
|
|
976
|
+
id: swapLoader
|
|
977
|
+
anchors.fill: parent
|
|
978
|
+
active: false
|
|
979
|
+
source: "QuickLookSwap.qml"
|
|
980
|
+
onLoaded: {
|
|
981
|
+
// The surface's own fill, inside its hairline, so the live border and rounded corners show round the picture.
|
|
982
|
+
item.panesSource = panes
|
|
983
|
+
item.ready = Qt.binding(function () { return root.lookReady })
|
|
984
|
+
item.ground = Qt.binding(function () { return surface.color })
|
|
985
|
+
item.groundInset = Qt.binding(function () { return surface.border.width })
|
|
986
|
+
item.groundRadius = Qt.binding(function () { return Math.max(0, surface.radius - surface.border.width) })
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// During the hold a press or the wheel reveals at once and reaches nothing unseen, so no click closes a card that never drew.
|
|
992
|
+
MouseArea {
|
|
993
|
+
id: holdShield
|
|
994
|
+
anchors.fill: parent
|
|
995
|
+
// Held through its own press, so the release after the reveal clicks nothing either.
|
|
996
|
+
enabled: root.unrevealed || holdShield.pressed
|
|
997
|
+
acceptedButtons: Qt.AllButtons
|
|
998
|
+
onPressed: root.revealNow()
|
|
999
|
+
onWheel: function (wheel) {
|
|
1000
|
+
wheel.accepted = true
|
|
1001
|
+
root.revealNow()
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
}
|