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.
- package/LICENSE +22 -0
- package/README.md +511 -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 +407 -0
- package/src/archiveops.js +728 -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 +135 -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/terminal-server.js +95 -0
- package/src/terminal-shell.js +26 -0
- package/src/transfer.js +669 -0
- package/src/trash-record.js +41 -0
- package/src/trash.js +210 -0
- package/src/trashbrowse.js +327 -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 +307 -0
- package/test/file-url.test.js +24 -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-archive-here-ui.mjs +47 -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-folder-focus.mjs +63 -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-terminal-lifecycle.mjs +42 -0
- package/test/manual-terminal-navigation.mjs +48 -0
- package/test/manual-terminal-ui.mjs +47 -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 +95 -0
- package/ui/ArchiveCompressDialog.qml +378 -0
- package/ui/ArchiveHereConfirm.qml +127 -0
- package/ui/ArchivePasswordDialog.qml +257 -0
- package/ui/Backend.qml +775 -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 +525 -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 +95 -0
- package/ui/Pane.qml +1320 -0
- package/ui/PaneMenuActions.qml +527 -0
- package/ui/PanePath.qml +90 -0
- package/ui/PaneRail.qml +268 -0
- package/ui/PaneStates.qml +88 -0
- package/ui/PaneSwap.qml +339 -0
- package/ui/PaneWire.qml +601 -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 +1005 -0
- package/ui/PreviewAnimation.qml +24 -0
- package/ui/PreviewArchive.qml +91 -0
- package/ui/PreviewColumn.qml +553 -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 +41 -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 +728 -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/TerminalService.qml +69 -0
- package/ui/TerminalTab.qml +48 -0
- package/ui/TerminalTabs.qml +27 -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 +829 -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 +131 -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 +360 -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 +504 -0
- package/ui/js/FolderSorts.js +108 -0
- package/ui/js/FolderViews.js +44 -0
- package/ui/js/Format.js +314 -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 +751 -0
- package/ui/js/Tags.js +134 -0
- package/ui/js/Taildrop.js +128 -0
- package/ui/js/Tap.js +180 -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 +84 -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 +230 -0
|
@@ -0,0 +1,846 @@
|
|
|
1
|
+
// The pragmas that name this window's app id and shell id live in ui/boot/picker.qml, which is
|
|
2
|
+
// the Quickshell entry; this document is loaded from there by file: URL so that Qt caches it.
|
|
3
|
+
|
|
4
|
+
import Quickshell
|
|
5
|
+
import Quickshell.Io
|
|
6
|
+
import QtQuick
|
|
7
|
+
import qs.Commons
|
|
8
|
+
import "." as Flea
|
|
9
|
+
import "js/Picker.js" as Picker
|
|
10
|
+
import "js/Keymap.js" as Keymap
|
|
11
|
+
import "js/Thumbs.js" as Thumbs
|
|
12
|
+
// ControlReader.js loads with IpcControls.qml on the first cohesionControls call, never with the picker's first frame.
|
|
13
|
+
|
|
14
|
+
// One portal request, one window: the org.freedesktop.impl.portal.FileChooser dialog every caller on
|
|
15
|
+
// the box gets, opened by flea --pick and answered through the reply file tools/flea-portal reads.
|
|
16
|
+
// The same Backend, Row, Theme and places the browser window draws with, and none of its operations:
|
|
17
|
+
// a chooser that can rename or delete is a file manager wearing a dialog's clothes.
|
|
18
|
+
ShellRoot {
|
|
19
|
+
property alias pickerWin: win
|
|
20
|
+
FloatingWindow {
|
|
21
|
+
id: win
|
|
22
|
+
|
|
23
|
+
readonly property var req: Picker.request(Quickshell.env("FLEA_PICKER"))
|
|
24
|
+
readonly property string home: Quickshell.env("HOME")
|
|
25
|
+
|
|
26
|
+
title: Picker.title(win.req)
|
|
27
|
+
implicitWidth: Math.round(Theme.space(640) * Theme.dialogWidthRatio)
|
|
28
|
+
implicitHeight: Theme.space(440)
|
|
29
|
+
color: Theme.color.background
|
|
30
|
+
|
|
31
|
+
// Where the list is standing, what it holds of the listing, and where the cursor is in it.
|
|
32
|
+
property string path: ""
|
|
33
|
+
property int total: 0
|
|
34
|
+
property int held: 0
|
|
35
|
+
property var rows: []
|
|
36
|
+
// The per-response kind dictionary ui/Row.qml's Kind column indexes into.
|
|
37
|
+
property var kindNames: []
|
|
38
|
+
property int cursorIndex: 0
|
|
39
|
+
property string listingState: "loading"
|
|
40
|
+
// A refused scan leaves the worker holding the previous folder, which "empty" alone cannot say, so only a new listing clears it.
|
|
41
|
+
property bool listingFailed: false
|
|
42
|
+
// The one statement of when the listing may be reordered; the header and the keys both read it.
|
|
43
|
+
readonly property bool sortable: !win.backendUnavailable && !win.recent && !win.submitting && !win.listingFailed
|
|
44
|
+
&& win.listingState !== "loading" && !(shares.item && shares.item.active)
|
|
45
|
+
// The order the listing is actually in, read through the picker because the backend id is out of scope.
|
|
46
|
+
readonly property string sortBy: backend.sortBy
|
|
47
|
+
readonly property bool sortDesc: backend.sortDesc
|
|
48
|
+
property int pendingListings: 0
|
|
49
|
+
property bool receivingLatestListing: false
|
|
50
|
+
property bool backendUnavailable: false
|
|
51
|
+
property string message: ""
|
|
52
|
+
|
|
53
|
+
// The checked identities, each a path and its size, so Back and Parent cannot rebind one.
|
|
54
|
+
property var marks: []
|
|
55
|
+
// Which chip is active: an index into the caller's filters, or -1 for All files.
|
|
56
|
+
property int filterIndex: Picker.currentChip(win.req)
|
|
57
|
+
readonly property var filter: win.filterIndex >= 0 ? win.req.filters[win.filterIndex] : null
|
|
58
|
+
readonly property var shown: null
|
|
59
|
+
readonly property int shownTotal: win.total
|
|
60
|
+
onFilterChanged: if (win.path.length) win.openWithoutHistory(win.path)
|
|
61
|
+
|
|
62
|
+
// The view the listing draws in; a launch reads pickerView and only setView writes it.
|
|
63
|
+
property string viewMode: "list"
|
|
64
|
+
function setView(mode) {
|
|
65
|
+
var before = win.actionFocus()
|
|
66
|
+
var next = Picker.viewSwitch(win.viewMode, mode)
|
|
67
|
+
if (!next) { win.restoreActionFocus(before); return }
|
|
68
|
+
win.viewMode = next
|
|
69
|
+
// The user's own switch, and only that, is what the next launch reopens: a launch
|
|
70
|
+
// reads, and cursor moves never owe the file anything.
|
|
71
|
+
ViewState.changeKey("pickerView", next)
|
|
72
|
+
// A reshow owns its window: the shown view moves to the cursor and refetches there.
|
|
73
|
+
var at = Math.max(0, Math.min(win.cursorIndex, win.total - 1))
|
|
74
|
+
if (next === "grid") grid.reshow(at)
|
|
75
|
+
else list.reshow(at)
|
|
76
|
+
win.restoreActionFocus(before)
|
|
77
|
+
}
|
|
78
|
+
function viewItem() { return win.viewMode === "grid" ? grid : list }
|
|
79
|
+
function focusView() { win.viewItem().forceActiveFocus() }
|
|
80
|
+
function actionFocus() {
|
|
81
|
+
var view = win.viewItem()
|
|
82
|
+
if (view.activeFocus) return view
|
|
83
|
+
var field = save.fieldItem
|
|
84
|
+
return field.activeFocus ? { field: field, cursor: field.cursorPosition,
|
|
85
|
+
anchor: field.cursorPosition === field.selectionStart ? field.selectionEnd : field.selectionStart } : null
|
|
86
|
+
}
|
|
87
|
+
function restoreActionFocus(before) {
|
|
88
|
+
if (before === list || before === grid) win.focusView()
|
|
89
|
+
else if (before) {
|
|
90
|
+
var field = before.field
|
|
91
|
+
if (!field.activeFocus) {
|
|
92
|
+
field.forceActiveFocus()
|
|
93
|
+
field.select(before.anchor, before.cursor)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else win.focusDefault()
|
|
97
|
+
}
|
|
98
|
+
// Automatic returns keep a save on Filename; Tab and row clicks can still focus the listing.
|
|
99
|
+
function focusDefault() {
|
|
100
|
+
if (win.saving) save.selectStem()
|
|
101
|
+
else win.focusView()
|
|
102
|
+
}
|
|
103
|
+
// Whether the listing shows the directory's dotfiles. The worker never sends what a
|
|
104
|
+
// request did not ask for, so the window re-reads the standing directory when this flips.
|
|
105
|
+
property bool showHidden: false
|
|
106
|
+
// The grid's visible-only thumbnail plan, owned here so both views share one map the way
|
|
107
|
+
// the main pane owns its views' states; only the grid ever asks.
|
|
108
|
+
property var thumbState: Thumbs.empty()
|
|
109
|
+
// Seven screens of history at the picker's row height, the main pane's own bound.
|
|
110
|
+
readonly property int thumbCap: 240
|
|
111
|
+
// The directory's storage class beside its fsinfo line, never per row; unknown holds the
|
|
112
|
+
// first settle the way the main pane holds its own, see ui/js/ExtThumbs.js.
|
|
113
|
+
property string storageClass: ""
|
|
114
|
+
property bool storageKnown: false
|
|
115
|
+
|
|
116
|
+
// Where Back goes, and it only ever goes back: Parent is its own button and pushes here too.
|
|
117
|
+
property var history: []
|
|
118
|
+
// The save mode's own name. It starts empty and Component.onCompleted seeds it from the caller's name.
|
|
119
|
+
property string saveName: ""
|
|
120
|
+
onSaveNameChanged: win.invalidateSave()
|
|
121
|
+
property int nextCheck: 0
|
|
122
|
+
property int markRequest: 0
|
|
123
|
+
property bool acceptMarks: false
|
|
124
|
+
property bool marksDirty: false
|
|
125
|
+
property int saveRequest: 0
|
|
126
|
+
property int reviewRequest: 0
|
|
127
|
+
property string probeKey: ""
|
|
128
|
+
property var saveReview: ({})
|
|
129
|
+
property string saveError: ""
|
|
130
|
+
readonly property string saveKey: JSON.stringify([win.path, win.saveName])
|
|
131
|
+
readonly property bool saveReady: win.saveReview.key === win.saveKey
|
|
132
|
+
readonly property bool saveCollision: win.saveReady && win.saveReview.collision
|
|
133
|
+
// Return asked once for a name that exists; the review stands until the name, folder or listing changes.
|
|
134
|
+
property bool reviewAsked: false
|
|
135
|
+
// The review is the asked review of a name the backend still reports as taken: Replace, its error line and its role read this alone.
|
|
136
|
+
readonly property bool collisionReview: win.reviewAsked && win.saveCollision
|
|
137
|
+
readonly property bool submitting: win.acceptMarks || win.reviewRequest > 0
|
|
138
|
+
// Submission keeps Cancel reachable and restores its initiating control only on refusal.
|
|
139
|
+
onSubmittingChanged: chrome.submissionChanged()
|
|
140
|
+
readonly property bool marksAllowed: win.req.multiple && !win.saving
|
|
141
|
+
readonly property var cursorRow: win.rowFor(win.cursorIndex)
|
|
142
|
+
readonly property bool cursorFile: win.listingState === "ready" && !win.listingFailed && win.cursorRow !== null && !Picker.directory(win.cursorRow)
|
|
143
|
+
readonly property bool canAccept: !win.backendUnavailable && !win.submitting && !win.markRequest && (win.saving
|
|
144
|
+
? win.saveReady : win.marks.length > 0 || (!win.folderMode && win.cursorFile) || (win.folderMode && !win.recent && win.listingState !== "loading"))
|
|
145
|
+
|
|
146
|
+
// SendPicker.html draws every rule and control frame in one ink, a lift over whatever plane
|
|
147
|
+
// it sits on. Theme.color.surface is a drop on these palettes and vanishes against the chrome
|
|
148
|
+
// strips, so the picker takes the OEM's own resting border alpha, which lifts on both.
|
|
149
|
+
readonly property color edge: Style.hoverBorderColor
|
|
150
|
+
|
|
151
|
+
// Recent is a location and not a directory: the rail's own row opens it and the listing it
|
|
152
|
+
// builds comes from the desktop's history rather than a scan; see AGENTS.md "Recent, and why".
|
|
153
|
+
readonly property bool recent: Picker.isRecent(win.path)
|
|
154
|
+
|
|
155
|
+
readonly property bool saving: win.req.mode === "save"
|
|
156
|
+
readonly property bool folderMode: win.req.directory || win.req.mode === "savefiles"
|
|
157
|
+
// Twice the wider view's screen plus slack, so the screen still fits after the quarter lead.
|
|
158
|
+
readonly property int windowSize: Picker.windowSize(list.visibleRows, grid.visibleTileRows, grid.columns)
|
|
159
|
+
// Both views refetch through one interval and lead, the main views' own 16 ms and quarter window.
|
|
160
|
+
readonly property int coalesceMs: 16
|
|
161
|
+
readonly property real windowLead: 0.25
|
|
162
|
+
|
|
163
|
+
// Exactly one answer leaves this window, whichever way it is asked for.
|
|
164
|
+
property bool answered: false
|
|
165
|
+
// The typed-path probe: the mark that has the backend refuse a typed file in a save or folder dialog.
|
|
166
|
+
property int pathProbe: 0
|
|
167
|
+
|
|
168
|
+
function rowFor(index) {
|
|
169
|
+
var at = index - win.held
|
|
170
|
+
return at >= 0 && at < win.rows.length ? win.rows[at] : null
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function open(next) {
|
|
174
|
+
if (win.submitting || next === win.path) return
|
|
175
|
+
if (win.path.length > 0) win.history = win.history.concat([win.path])
|
|
176
|
+
win.openWithoutHistory(next)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function openWithoutHistory(next) {
|
|
180
|
+
if (win.backendUnavailable) return
|
|
181
|
+
listing.clear()
|
|
182
|
+
if (shares.item) shares.item.close()
|
|
183
|
+
win.path = next
|
|
184
|
+
win.listingFailed = false
|
|
185
|
+
win.clearListing()
|
|
186
|
+
win.invalidateSave()
|
|
187
|
+
win.validateMarks(false)
|
|
188
|
+
if (Picker.isRecent(next)) {
|
|
189
|
+
recents.refresh()
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
backend.resetSort(next)
|
|
193
|
+
win.requestListing(backend.listRequest(next, win.windowSize, win.showHidden))
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function requestListing(request) {
|
|
197
|
+
win.pendingListings = 1
|
|
198
|
+
listing.request(Object.assign(request, win.filterRequest()))
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Rows are named by index, so a replaced listing is dropped whole; emptying the model returns the viewport to the top.
|
|
202
|
+
function clearListing() {
|
|
203
|
+
selection.reset()
|
|
204
|
+
win.total = 0
|
|
205
|
+
win.held = 0
|
|
206
|
+
win.rows = []
|
|
207
|
+
win.cursorIndex = 0
|
|
208
|
+
win.listingState = "loading"
|
|
209
|
+
win.receivingLatestListing = false
|
|
210
|
+
// A replaced listing renumbers every row, so no thumb answer may outlive it.
|
|
211
|
+
win.thumbState = Thumbs.empty()
|
|
212
|
+
win.storageClass = ""
|
|
213
|
+
win.storageKnown = false
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Sort reorders the worker's listing, marks stay; remembers folder sort and default sort in ui.json.
|
|
217
|
+
function requestSort(order) {
|
|
218
|
+
var before = win.actionFocus()
|
|
219
|
+
if (!order || !win.sortable || (backend.sortBy === order.key && backend.sortDesc === order.desc))
|
|
220
|
+
return
|
|
221
|
+
backend.sortBy = order.key
|
|
222
|
+
backend.sortDesc = order.desc
|
|
223
|
+
// A user sort writes its folder only while remembering is on; the default follows every sort.
|
|
224
|
+
if (!win.recent && win.path.length > 0 && ViewState.state.rememberSort !== false)
|
|
225
|
+
backend.rememberFolderSort(win.path, order.key, order.desc)
|
|
226
|
+
ViewState.changeLeaf("sort", { key: order.key === "mtime" ? "date" : order.key, reverse: order.desc })
|
|
227
|
+
win.clearListing()
|
|
228
|
+
win.pendingListings = 1
|
|
229
|
+
listing.sort(order.key, order.desc, ViewState.state.foldersFirst !== false, ViewState.state.groupByKind === true, ViewState.state.hiddenLast === true)
|
|
230
|
+
// sort answers a listed line and no rows of its own, so the reordered window is asked for.
|
|
231
|
+
listing.window(0, win.windowSize)
|
|
232
|
+
win.restoreActionFocus(before)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function filterRequest() {
|
|
236
|
+
return {pickerGlobs: win.filter ? win.filter.globs : [], pickerMimes: win.filter ? win.filter.mimes : []}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function check(request) {
|
|
240
|
+
request.c = "picker"
|
|
241
|
+
request.id = ++win.nextCheck
|
|
242
|
+
backend.send(request)
|
|
243
|
+
return request.id
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function validateMarks(accepting) { selection.validate(accepting) }
|
|
247
|
+
|
|
248
|
+
function invalidateSave() {
|
|
249
|
+
win.saveReview = ({})
|
|
250
|
+
win.reviewAsked = false
|
|
251
|
+
win.saveError = ""
|
|
252
|
+
if (win.saving) Qt.callLater(win.probeSave)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function probeSave() {
|
|
256
|
+
if (win.backendUnavailable || !win.saving || win.saveRequest || !win.path.length || !Picker.validName(win.saveName)) return
|
|
257
|
+
win.probeKey = win.saveKey
|
|
258
|
+
win.saveRequest = win.check({op: "save", folder: win.path, name: win.saveName})
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Reassign history so property var notifies.
|
|
262
|
+
function goBack() {
|
|
263
|
+
if (shares.item && shares.item.active) { shares.item.close(); return }
|
|
264
|
+
if (win.submitting || win.history.length === 0)
|
|
265
|
+
return
|
|
266
|
+
var target = win.history[win.history.length - 1]
|
|
267
|
+
win.history = win.history.slice(0, win.history.length - 1)
|
|
268
|
+
win.openWithoutHistory(target)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function goUp() {
|
|
272
|
+
// The board's own rule: Parent is unavailable in Recent, because a history has no parent.
|
|
273
|
+
if (win.recent) {
|
|
274
|
+
return
|
|
275
|
+
}
|
|
276
|
+
var up = Picker.parentOf(win.path)
|
|
277
|
+
if (up !== win.path)
|
|
278
|
+
win.open(up)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function toggleMark(index) { selection.toggle(index) }
|
|
282
|
+
function markRange(was, index) { selection.range(was, index) }
|
|
283
|
+
function selectAll() { selection.all() }
|
|
284
|
+
function endRange() { selection.endRange() }
|
|
285
|
+
|
|
286
|
+
// Enter walks folders and submits the checked files, or the cursor file when none are checked.
|
|
287
|
+
function activate(index) {
|
|
288
|
+
var row = win.rowFor(index)
|
|
289
|
+
if (!row)
|
|
290
|
+
return
|
|
291
|
+
if (Picker.directory(row)) {
|
|
292
|
+
win.open(Picker.rowPath(win.path, row.n))
|
|
293
|
+
return
|
|
294
|
+
}
|
|
295
|
+
win.accept()
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// File double clicks toggle marks in several-file requests; one-file requests mark when needed, then accept.
|
|
299
|
+
function doubleActivate(index, rowPath, firstPath) {
|
|
300
|
+
var row = win.rowFor(index)
|
|
301
|
+
var choice = Picker.doubleAction(win.req, row, rowPath, firstPath, win.marks)
|
|
302
|
+
if (choice === Picker.DOUBLE_OPEN) {
|
|
303
|
+
win.open(Picker.rowPath(win.path, row.n))
|
|
304
|
+
return
|
|
305
|
+
}
|
|
306
|
+
if (choice === Picker.DOUBLE_MARK) {
|
|
307
|
+
win.toggleMark(index)
|
|
308
|
+
return
|
|
309
|
+
}
|
|
310
|
+
if (choice !== Picker.DOUBLE_ACCEPT && choice !== Picker.DOUBLE_MARK_ACCEPT) {
|
|
311
|
+
return
|
|
312
|
+
}
|
|
313
|
+
if (win.backendUnavailable || win.markRequest || win.submitting) {
|
|
314
|
+
win.say("Selection is still being checked.")
|
|
315
|
+
return
|
|
316
|
+
}
|
|
317
|
+
if (choice === Picker.DOUBLE_ACCEPT) {
|
|
318
|
+
win.accept()
|
|
319
|
+
return
|
|
320
|
+
}
|
|
321
|
+
win.acceptMarks = true
|
|
322
|
+
win.markRequest = win.check({op: "mark", path: rowPath, directory: false, multiple: win.req.multiple})
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function accept() {
|
|
326
|
+
if (win.backendUnavailable || win.submitting || win.markRequest) return
|
|
327
|
+
if (win.saving) {
|
|
328
|
+
if (win.saveName.length === 0) {
|
|
329
|
+
win.say("Name the file before saving it")
|
|
330
|
+
return
|
|
331
|
+
}
|
|
332
|
+
// The answer has to name the folder the user was shown, so a typed separator is
|
|
333
|
+
// refused here rather than rewritten: a rewrite would send a path nobody approved.
|
|
334
|
+
if (!Picker.validName(win.saveName)) {
|
|
335
|
+
win.say(Picker.NAME_REFUSED)
|
|
336
|
+
return
|
|
337
|
+
}
|
|
338
|
+
if (!win.saveReady) { win.probeSave(); return }
|
|
339
|
+
// Return, the field and a file row only ask: the review starts on a taken name and stays until Replace answers.
|
|
340
|
+
if (win.saveCollision) {
|
|
341
|
+
if (!win.reviewAsked) { win.reviewAsked = true; save.selectStem() }
|
|
342
|
+
return
|
|
343
|
+
}
|
|
344
|
+
win.submitSave()
|
|
345
|
+
return
|
|
346
|
+
}
|
|
347
|
+
// A folder request with nothing checked takes the directory the window is standing in,
|
|
348
|
+
// which is what the board's Choose folder button does with no row marked.
|
|
349
|
+
if (win.marks.length === 0 && win.folderMode) {
|
|
350
|
+
// Recent is not a directory, so there is nothing here to hand back unasked.
|
|
351
|
+
if (win.recent) {
|
|
352
|
+
win.say("Press Space to select a folder first")
|
|
353
|
+
return
|
|
354
|
+
}
|
|
355
|
+
win.acceptMarks = true
|
|
356
|
+
win.markRequest = win.check({op: "mark", path: win.path, directory: true, multiple: false})
|
|
357
|
+
return
|
|
358
|
+
}
|
|
359
|
+
if (win.marks.length === 0) {
|
|
360
|
+
if (!win.cursorFile) return
|
|
361
|
+
win.acceptMarks = true
|
|
362
|
+
win.markRequest = win.check({op: "mark", path: Picker.rowPath(win.path, win.cursorRow.n), directory: false, multiple: false})
|
|
363
|
+
return
|
|
364
|
+
}
|
|
365
|
+
win.validateMarks(true)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Replace is the only answer that overwrites; Enter or Space on it, or a click, arrives here and nothing else does.
|
|
369
|
+
function replace() {
|
|
370
|
+
if (win.backendUnavailable || win.submitting || win.markRequest || !win.collisionReview) return
|
|
371
|
+
win.submitSave()
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function submitSave() { win.reviewRequest = win.check({op: "review", review: win.saveReview.review}) }
|
|
375
|
+
|
|
376
|
+
function cancel() {
|
|
377
|
+
win.finish(Picker.RESPONSE_CANCELLED, [])
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// ":" and Ctrl+L are the main window's own pathBar rows; keys.toml is not edited.
|
|
381
|
+
function openPathBar() { chrome.startEdit() }
|
|
382
|
+
|
|
383
|
+
// The portal reads this reply after exit, so its atomic write must finish before teardown.
|
|
384
|
+
function finish(response, list) {
|
|
385
|
+
if (win.answered)
|
|
386
|
+
return
|
|
387
|
+
// Built before the flag is set, so a throw here leaves the window answerable rather than shut.
|
|
388
|
+
var text = Picker.reply(response, list, win.filterIndex)
|
|
389
|
+
win.answered = true
|
|
390
|
+
if (!win.backendUnavailable) backend.send({c: "picker", op: "close"})
|
|
391
|
+
replyFile.setText(text)
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
property bool messageError: false
|
|
395
|
+
function say(text, error) {
|
|
396
|
+
win.message = text
|
|
397
|
+
win.messageError = error === true
|
|
398
|
+
messageLife.stop()
|
|
399
|
+
if (!win.messageError) messageLife.restart()
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
Timer {
|
|
403
|
+
id: messageLife
|
|
404
|
+
interval: 4000
|
|
405
|
+
onTriggered: win.message = ""
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
FileView {
|
|
409
|
+
id: replyFile
|
|
410
|
+
path: Quickshell.env("FLEA_PICKER_REPLY")
|
|
411
|
+
atomicWrites: true
|
|
412
|
+
// The reply file does not exist until this window writes it, and a preload read of a
|
|
413
|
+
// path that is not there is not an error worth a line; onSaveFailed below is.
|
|
414
|
+
printErrors: false
|
|
415
|
+
// Sequenced on saved(), never on setText() returning: the reply is on disk before both children are reaped.
|
|
416
|
+
onSaved: lifecycle.quit()
|
|
417
|
+
onSaveFailed: {
|
|
418
|
+
console.warn("the portal reply could not be written, so the request fails rather than reporting a refusal")
|
|
419
|
+
lifecycle.quit()
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// A window-manager close takes the same refusal path as Cancel.
|
|
424
|
+
Connections {
|
|
425
|
+
target: Quickshell
|
|
426
|
+
function onLastWindowClosed() {
|
|
427
|
+
if (win.answered)
|
|
428
|
+
return
|
|
429
|
+
win.finish(Picker.RESPONSE_CANCELLED, [])
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// The saved reply can leave once both chooser-owned read processes have exited.
|
|
434
|
+
Flea.PickerLifecycle {
|
|
435
|
+
id: lifecycle; checks: backend; listing: listing
|
|
436
|
+
onStopped: Quickshell.execDetached(["kill", String(Quickshell.processId)])
|
|
437
|
+
}
|
|
438
|
+
Flea.PickerListing {
|
|
439
|
+
id: listing
|
|
440
|
+
onFailed: function(reason) { backend.failed("scan", win.path, reason, 0) }
|
|
441
|
+
onMessage: function(message) { backend.receive(JSON.stringify(message)) }
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
Flea.Backend {
|
|
445
|
+
id: backend
|
|
446
|
+
pickerOnly: true
|
|
447
|
+
// Each listing resolves its folder explicitly; between requests the mark must keep describing the held rows.
|
|
448
|
+
preserveSort: true
|
|
449
|
+
|
|
450
|
+
onListed: function (n, readMs, sortMs) {
|
|
451
|
+
if (win.backendUnavailable) return
|
|
452
|
+
win.pendingListings = 0
|
|
453
|
+
win.receivingLatestListing = true
|
|
454
|
+
win.total = n
|
|
455
|
+
win.listingState = n === 0 ? "empty" : "ready"
|
|
456
|
+
// Grid needs the storage class, so ask fsinfo except on Recent.
|
|
457
|
+
if (!win.recent)
|
|
458
|
+
listing.fsinfo()
|
|
459
|
+
}
|
|
460
|
+
onRows: function (start, items, ms, kinds) {
|
|
461
|
+
if (!win.receivingLatestListing) return
|
|
462
|
+
win.held = start
|
|
463
|
+
win.rows = items
|
|
464
|
+
win.kindNames = kinds
|
|
465
|
+
}
|
|
466
|
+
// A thumbed line for the previous listing is still in the pipe when clearListing
|
|
467
|
+
// emptied the map, so only the live listing's answers land.
|
|
468
|
+
onThumbed: function (row, file) {
|
|
469
|
+
if (!win.receivingLatestListing) return
|
|
470
|
+
win.thumbState = Thumbs.remember(win.thumbState, row, file, win.thumbCap)
|
|
471
|
+
}
|
|
472
|
+
// The worker stats its own base after the rows, so the class lands after them; a
|
|
473
|
+
// settle fired in between held on unknown, and restarts now that it is named.
|
|
474
|
+
onFsInfo: function (fs, free, path, storageClass) {
|
|
475
|
+
if (path.length > 0 && path !== win.path) return
|
|
476
|
+
win.storageClass = storageClass || ""
|
|
477
|
+
win.storageKnown = true
|
|
478
|
+
grid.restartSettle()
|
|
479
|
+
}
|
|
480
|
+
onFailed: function (where, input, msg, mode) {
|
|
481
|
+
if (where === "backend") {
|
|
482
|
+
win.backendUnavailable = true
|
|
483
|
+
win.pendingListings = 0
|
|
484
|
+
win.receivingLatestListing = false
|
|
485
|
+
win.markRequest = 0
|
|
486
|
+
win.saveRequest = 0
|
|
487
|
+
win.reviewRequest = 0
|
|
488
|
+
win.acceptMarks = false
|
|
489
|
+
win.marksDirty = false
|
|
490
|
+
win.saveReview = ({})
|
|
491
|
+
win.saveError = "Flea stopped responding."
|
|
492
|
+
win.say("Flea stopped responding. Cancel and reopen this request.", true)
|
|
493
|
+
win.stepFocus(null, false)
|
|
494
|
+
return
|
|
495
|
+
}
|
|
496
|
+
if (where === "scan" || where === "sort") {
|
|
497
|
+
selection.reset()
|
|
498
|
+
win.pendingListings = 0
|
|
499
|
+
win.listingFailed = true
|
|
500
|
+
}
|
|
501
|
+
// A worker lost over held rows keeps them and the footer error; only a listing that holds nothing reads as empty.
|
|
502
|
+
if (win.total === 0) win.listingState = "empty"
|
|
503
|
+
win.say(msg, true)
|
|
504
|
+
}
|
|
505
|
+
onChanged: function (path) {
|
|
506
|
+
if (path === win.path && !win.submitting) win.openWithoutHistory(win.path)
|
|
507
|
+
}
|
|
508
|
+
onPickerResult: function (message) {
|
|
509
|
+
if (win.answered || win.backendUnavailable) return
|
|
510
|
+
if (message.id === win.markRequest || message.id === win.pathProbe) {
|
|
511
|
+
selection.received(message)
|
|
512
|
+
} else if (message.id === win.saveRequest) {
|
|
513
|
+
win.saveRequest = 0
|
|
514
|
+
if (win.probeKey !== win.saveKey) { win.probeSave(); return }
|
|
515
|
+
if (!message.ok) { win.saveError = message.error; win.say(message.error, true); return }
|
|
516
|
+
win.saveReview = Object.assign({key: win.saveKey}, message)
|
|
517
|
+
} else if (message.id === win.reviewRequest) {
|
|
518
|
+
win.reviewRequest = 0
|
|
519
|
+
if (!message.ok || !win.saveReady) {
|
|
520
|
+
win.say(message.error || "The save location changed; review it again.", true)
|
|
521
|
+
win.invalidateSave()
|
|
522
|
+
return
|
|
523
|
+
}
|
|
524
|
+
win.finish(Picker.RESPONSE_OK, [message.path])
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
Flea.PickerSelection {
|
|
530
|
+
id: selection
|
|
531
|
+
picker: win
|
|
532
|
+
listing: listing
|
|
533
|
+
backend: backend
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// The history the Recent location lists, read only when that location is opened. The listing
|
|
537
|
+
// is the client's own order, so the backend is asked for these paths and never to sort them.
|
|
538
|
+
Flea.PickerRecent {
|
|
539
|
+
id: recents
|
|
540
|
+
onRefreshed: if (win.recent) win.requestListing({c: "listpaths", paths: recents.paths, first: win.windowSize})
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
Rectangle {
|
|
544
|
+
anchors.fill: parent
|
|
545
|
+
color: Theme.color.background
|
|
546
|
+
focus: true
|
|
547
|
+
// The save field takes the keyboard from the list, and Escape has to refuse from there too.
|
|
548
|
+
Keys.onEscapePressed: win.cancel()
|
|
549
|
+
|
|
550
|
+
Flea.PickerChrome {
|
|
551
|
+
id: chrome
|
|
552
|
+
anchors.left: parent.left
|
|
553
|
+
anchors.right: parent.right
|
|
554
|
+
anchors.top: parent.top
|
|
555
|
+
picker: win
|
|
556
|
+
listing: listing
|
|
557
|
+
backend: backend
|
|
558
|
+
onBackRequested: { win.focusDefault(); win.goBack() }
|
|
559
|
+
onUpRequested: { win.focusDefault(); win.goUp() }
|
|
560
|
+
onViewChosen: function (mode) { win.setView(mode) }
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
Flea.PickerPlaces {
|
|
564
|
+
id: places
|
|
565
|
+
anchors.left: parent.left
|
|
566
|
+
anchors.top: chrome.bottom
|
|
567
|
+
anchors.bottom: save.top
|
|
568
|
+
home: win.home
|
|
569
|
+
picker: win
|
|
570
|
+
current: win.path
|
|
571
|
+
edge: win.edge
|
|
572
|
+
offerRecent: !win.saving
|
|
573
|
+
onChosen: function (path) { win.open(path); win.focusDefault() }
|
|
574
|
+
onNetworkCompleted: function(requestId, uri, success, reason, connectFailed) {
|
|
575
|
+
if (networkDialog.item) networkDialog.item.mountFinished(requestId, uri, success, reason, connectFailed)
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
Flea.PickerHeader {
|
|
580
|
+
id: header
|
|
581
|
+
anchors.left: places.right
|
|
582
|
+
anchors.right: parent.right
|
|
583
|
+
anchors.top: chrome.bottom
|
|
584
|
+
picker: win
|
|
585
|
+
backend: backend
|
|
586
|
+
leadingSlot: win.marksAllowed ? list.checkSize + Theme.spacing.gap : 0
|
|
587
|
+
// Only the list heads columns; the save form keeps its old room, so the header yields when it and one row do not fit.
|
|
588
|
+
visible: win.viewMode === "list" && save.y - chrome.height >= implicitHeight + Theme.rowHeight
|
|
589
|
+
height: visible ? implicitHeight : 0
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
Flea.PickerList {
|
|
593
|
+
id: list
|
|
594
|
+
visible: win.viewMode === "list"
|
|
595
|
+
anchors.left: places.right
|
|
596
|
+
anchors.right: parent.right
|
|
597
|
+
anchors.top: header.bottom
|
|
598
|
+
anchors.bottom: save.top
|
|
599
|
+
picker: win
|
|
600
|
+
backend: listing
|
|
601
|
+
clip: true
|
|
602
|
+
focus: win.viewMode === "list"
|
|
603
|
+
enabled: !win.submitting && !win.backendUnavailable
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// The grid draws the same rows through the main window's tiles, with its own
|
|
607
|
+
// visible-only thumbnail plan; see ui/PickerGrid.qml.
|
|
608
|
+
Flea.PickerGrid {
|
|
609
|
+
id: grid
|
|
610
|
+
visible: win.viewMode === "grid"
|
|
611
|
+
anchors.left: places.right
|
|
612
|
+
anchors.right: parent.right
|
|
613
|
+
anchors.top: header.bottom
|
|
614
|
+
anchors.bottom: save.top
|
|
615
|
+
picker: win
|
|
616
|
+
backend: listing
|
|
617
|
+
clip: true
|
|
618
|
+
focus: win.viewMode === "grid"
|
|
619
|
+
enabled: !win.submitting && !win.backendUnavailable
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
Connections {
|
|
623
|
+
target: grid
|
|
624
|
+
function onThumbsApplied(work) { win.thumbState = Thumbs.applied(win.thumbState, work) }
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// The same empty hero the browser window draws, over the list area alone.
|
|
628
|
+
Flea.EmptyState {
|
|
629
|
+
id: hero
|
|
630
|
+
x: list.x
|
|
631
|
+
y: list.y
|
|
632
|
+
width: list.width
|
|
633
|
+
height: list.height
|
|
634
|
+
visible: win.listingState === "empty"
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
FocusScope {
|
|
638
|
+
id: shareFocus
|
|
639
|
+
x: list.x
|
|
640
|
+
y: list.y
|
|
641
|
+
width: list.width
|
|
642
|
+
height: list.height
|
|
643
|
+
visible: shares.item !== null && shares.item.visible
|
|
644
|
+
Keys.onPressed: function(event) {
|
|
645
|
+
var action = Keymap.lookup(event.key, event.text, event.modifiers, "rail")
|
|
646
|
+
event.accepted = true
|
|
647
|
+
if (event.key === Qt.Key_Escape || action === "parent" || action === "historyBack") shares.item.close()
|
|
648
|
+
else if (action === "cursorDown") shares.item.moveCursor(1)
|
|
649
|
+
else if (action === "cursorUp") shares.item.moveCursor(-1)
|
|
650
|
+
else if (action === "open" || event.key === Qt.Key_Space) shares.item.activateCursor()
|
|
651
|
+
else if (event.key === Qt.Key_Tab) win.stepFocus(list, (event.modifiers & Qt.ShiftModifier) !== 0)
|
|
652
|
+
else if (event.key === Qt.Key_Backtab) win.stepFocus(list, true)
|
|
653
|
+
}
|
|
654
|
+
Loader {
|
|
655
|
+
id: shares
|
|
656
|
+
anchors.fill: parent
|
|
657
|
+
active: false
|
|
658
|
+
source: "ShareBrowser.qml"
|
|
659
|
+
}
|
|
660
|
+
Connections {
|
|
661
|
+
target: shares.item
|
|
662
|
+
function onClosed() { win.focusDefault() }
|
|
663
|
+
function onActivated(uri, label) { places.openChild(uri, label) }
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
Flea.PickerSave {
|
|
668
|
+
id: save
|
|
669
|
+
keyTarget: win.viewItem()
|
|
670
|
+
anchors.left: parent.left
|
|
671
|
+
anchors.right: parent.right
|
|
672
|
+
anchors.bottom: answers.top
|
|
673
|
+
maximumHeight: Math.max(0, answers.y - chrome.height - Theme.rowHeight)
|
|
674
|
+
picker: win
|
|
675
|
+
onNameEdited: function (text) { win.saveName = text }
|
|
676
|
+
onAccepted: win.accept()
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// The answer row stands under the listing and the save area in every mode; the footer is under it.
|
|
680
|
+
Flea.PickerAnswers { id: answers; anchors.bottom: status.top; picker: win }
|
|
681
|
+
|
|
682
|
+
// The footer: what is checked on the left, the keys that act on it on the right.
|
|
683
|
+
Item {
|
|
684
|
+
id: status
|
|
685
|
+
anchors.left: parent.left
|
|
686
|
+
anchors.right: parent.right
|
|
687
|
+
anchors.bottom: parent.bottom
|
|
688
|
+
height: Theme.chromeHeight
|
|
689
|
+
|
|
690
|
+
// The footer takes the chrome plane, the same strip the ask above it stands on.
|
|
691
|
+
Rectangle {
|
|
692
|
+
anchors.fill: parent
|
|
693
|
+
color: Theme.color.surface
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
Rectangle {
|
|
697
|
+
anchors.top: parent.top
|
|
698
|
+
width: parent.width
|
|
699
|
+
height: Theme.spacing.hairline
|
|
700
|
+
color: win.edge
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
Text {
|
|
704
|
+
id: statusMessage
|
|
705
|
+
anchors.left: parent.left
|
|
706
|
+
anchors.leftMargin: Theme.spacing.rowPaddingX
|
|
707
|
+
anchors.right: statusHints.left
|
|
708
|
+
anchors.rightMargin: Theme.spacing.gap
|
|
709
|
+
anchors.verticalCenter: parent.verticalCenter
|
|
710
|
+
text: win.message.length > 0 ? win.message : !win.marksAllowed ? "" : Picker.statusLine(win.marks.length, Picker.totalBytes(win.marks))
|
|
711
|
+
color: win.messageError ? Theme.color.errorOnSurface : Theme.color.foreground
|
|
712
|
+
font.family: Theme.font.family
|
|
713
|
+
font.pixelSize: Theme.font.caption
|
|
714
|
+
textFormat: Text.PlainText
|
|
715
|
+
elide: Text.ElideRight
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
Text {
|
|
719
|
+
id: statusHints
|
|
720
|
+
anchors.right: parent.right
|
|
721
|
+
anchors.rightMargin: Theme.spacing.rowPaddingX
|
|
722
|
+
width: Math.min(implicitWidth, Math.max(0, parent.width - 2 * Theme.spacing.rowPaddingX
|
|
723
|
+
- Theme.spacing.gap - Math.min(statusMessage.implicitWidth, parent.width / 2)))
|
|
724
|
+
anchors.verticalCenter: parent.verticalCenter
|
|
725
|
+
text: win.backendUnavailable ? "Esc cancel" : Picker.hints(win.req)
|
|
726
|
+
color: Theme.color.foreground
|
|
727
|
+
font.family: Theme.font.family
|
|
728
|
+
font.pixelSize: Theme.font.caption
|
|
729
|
+
textFormat: Text.PlainText
|
|
730
|
+
elide: Text.ElideRight
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
Loader {
|
|
735
|
+
id: networkDialog
|
|
736
|
+
anchors.fill: parent
|
|
737
|
+
active: false
|
|
738
|
+
sourceComponent: Component {
|
|
739
|
+
Flea.NetworkDialog {
|
|
740
|
+
onClosed: win.focusDefault()
|
|
741
|
+
onMountRequested: function(requestId, uri, label, password) { places.retry(requestId, uri, label, password) }
|
|
742
|
+
onCancelRequested: function(requestId) { places.cancelNetwork(requestId) }
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
function showShares(uri, label, names) {
|
|
749
|
+
shares.active = true
|
|
750
|
+
shares.item.open(uri, label, names)
|
|
751
|
+
shareFocus.forceActiveFocus()
|
|
752
|
+
}
|
|
753
|
+
function retryNetwork(uri, label, password, reason, failed) {
|
|
754
|
+
networkDialog.active = true
|
|
755
|
+
networkDialog.item.openLocation(uri, label, password, reason, failed)
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
Component.onCompleted: {
|
|
759
|
+
// Only an absolute path is a folder, so a caller cannot name the Recent token, or any
|
|
760
|
+
// other text, as the directory this window opens on.
|
|
761
|
+
var start = win.req.folder.charAt(0) === "/" ? win.req.folder : win.home
|
|
762
|
+
win.showHidden = typeof win.req.hidden === "boolean" ? win.req.hidden : ViewState.state.hidden === true
|
|
763
|
+
win.viewMode = Picker.rememberedView(ViewState.pickerView)
|
|
764
|
+
// A one-time seed of a filename only: a binding would read first inside saveReady, and flea-portal passes current_name verbatim.
|
|
765
|
+
win.saveName = Picker.validName(win.req.name) ? win.req.name : ""
|
|
766
|
+
win.openWithoutHistory(start)
|
|
767
|
+
// Seed focus explicitly: saves start on Filename, other requests on the listing.
|
|
768
|
+
win.focusDefault()
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// The seam tests/picker.sh drives, the same read-only shape ui/Ipc.qml has for the window.
|
|
772
|
+
IpcHandler {
|
|
773
|
+
target: "fleapicker"
|
|
774
|
+
function ready(): bool { return true }
|
|
775
|
+
function path(): string { return win.path }
|
|
776
|
+
function total(): int { return win.total }
|
|
777
|
+
function shownTotal(): int { return win.shownTotal }
|
|
778
|
+
function cursor(): int { return win.cursorIndex }
|
|
779
|
+
function marks(): string { return Picker.paths(win.marks).join(",") }
|
|
780
|
+
function rowAt(index: int): string { var row = win.rowFor(index); return row ? row.n : "" }
|
|
781
|
+
function cursorName(): string { return win.rowFor(win.cursorIndex) ? win.rowFor(win.cursorIndex).n : "" }
|
|
782
|
+
function state(): string { return win.listingState }
|
|
783
|
+
function recent(): bool { return win.recent }
|
|
784
|
+
function accept(): string { return Picker.acceptLabel(win.req, win.marks.length) }
|
|
785
|
+
function chip(): int { return win.filterIndex }
|
|
786
|
+
function saveName(): string { return win.saveName }
|
|
787
|
+
function message(): string { return win.message }
|
|
788
|
+
function pathBarOpen(): bool { return chrome.editing }
|
|
789
|
+
function checks(): string { return JSON.stringify({marksBusy: win.markRequest > 0, saveBusy: win.saveRequest > 0, submitting: win.submitting, canAccept: win.canAccept, saveReady: win.saveReady, collision: win.saveCollision, review: win.saveReview.review || 0}) }
|
|
790
|
+
function markedUris(): string { return JSON.stringify(win.marks.map(function(mark) { return mark.uri })) }
|
|
791
|
+
function controls(): string { return JSON.stringify(chrome.controls().concat(answers.controls(), header.controls(), save.controls(), places.controls())) }
|
|
792
|
+
function rowCentre(index: int): string { return win.centre(win.viewItem().itemAtIndex(index)) }
|
|
793
|
+
function saveState(): string { return JSON.stringify({name: win.saveName, path: win.saveReview.path || "", collision: win.saveCollision, error: win.saveError, field: win.centre(save.fieldItem)}) }
|
|
794
|
+
// The cohesion captures' reader over the picker, see ui/js/ControlReader.js; its answer and chrome marks are inline components, and its filter is PickerFilter.
|
|
795
|
+
function cohesionControls(): string {
|
|
796
|
+
var kinds = /^(DialogButton|ChromeButton|SidebarRow|Answer|Mark|PickerFilter)(?:_QML|\()/
|
|
797
|
+
var alias = {Answer: "DialogButton", Mark: "ChromeButton"}
|
|
798
|
+
return Qt.createComponent("IpcControls.qml").createObject(null).read(win.contentItem ? win.contentItem.Window.window : null, win.contentItem, kinds, alias)
|
|
799
|
+
}
|
|
800
|
+
function snapshot(): string {
|
|
801
|
+
// One reader answers path, field and Jump together, so a poll reads one instant.
|
|
802
|
+
var jumpItem = chrome.jump
|
|
803
|
+
var jumpFrame = jumpItem ? jumpItem.dropItem : null
|
|
804
|
+
var jumpVisible = jumpFrame ? Math.floor((jumpFrame.height - 2 * Theme.spacing.rowPaddingY) / Theme.rowHeight) : 0
|
|
805
|
+
return JSON.stringify({path: win.path, total: win.total, held: win.held, rows: win.rows,
|
|
806
|
+
pathBarOpen: chrome.editing, pathFieldText: chrome.jump ? chrome.jump.query : "",
|
|
807
|
+
jump: {open: !!jumpItem && jumpItem.shown, rows: jumpItem ? jumpItem.entries.length : 0, visibleRows: jumpVisible, contentY: jumpFrame ? jumpFrame.scrollY : 0, cursor: jumpItem ? jumpItem.cursor : -1, paths: jumpItem ? jumpItem.entries.map(function(entry) { return entry.path }) : []},
|
|
808
|
+
cursor: win.cursorIndex, cursorName: win.rowFor(win.cursorIndex) ? win.rowFor(win.cursorIndex).n : "",
|
|
809
|
+
marks: win.marks, state: win.listingState, emptyHero: hero.visible, emptyRest: hero.atRest, listingFailed: win.listingFailed, sortBy: backend.sortBy, sortDesc: backend.sortDesc, sortable: win.sortable, filter: win.filterIndex, history: win.history,
|
|
810
|
+
view: win.viewMode, thumbPending: Object.keys(win.thumbState.file).filter(function(index) { return win.thumbState.file[index] === null || win.thumbState.file[index] === "cache-asked" }).length,
|
|
811
|
+
marksBusy: win.markRequest > 0, saveBusy: win.saveRequest > 0, submitting: win.submitting, backendUnavailable: win.backendUnavailable,
|
|
812
|
+
canAccept: win.canAccept, saveReady: win.saveReady, collision: win.saveCollision, collisionReview: win.collisionReview,
|
|
813
|
+
saveName: win.saveName, saveError: win.saveError, message: win.message, messageError: win.messageError, hints: statusHints.text,
|
|
814
|
+
controls: chrome.controls().concat(answers.controls(), header.controls(), save.controls(), places.controls()), headerMark: header.sortBy, listFocus: list.activeFocus, gridFocus: grid.activeFocus,
|
|
815
|
+
railFocus: places.focusItem.activeFocus, preset: Flea.ViewState.keysPreset,
|
|
816
|
+
bodySmall: Theme.font.bodySmall, body: Theme.font.body, width: win.width, height: win.height,
|
|
817
|
+
themeLoaded: Theme.ready, themeForeground: String(Theme.color.foreground),
|
|
818
|
+
geometry: {chrome: chrome.height, header: header.height, rail: places.width, row: Theme.rowHeight, footer: status.height,
|
|
819
|
+
save: save.height, list: list.height, saveViewport: win.bounds(save.scrollItem), saveScroll: save.scrollItem.contentY}, title: win.title, app: win.req.app})
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function centre(item) {
|
|
824
|
+
if (!item) return ""
|
|
825
|
+
var point = item.mapToItem(win.contentItem, item.width / 2, item.height / 2)
|
|
826
|
+
return Math.round(point.x) + " " + Math.round(point.y)
|
|
827
|
+
}
|
|
828
|
+
function control(name, item, available) {
|
|
829
|
+
return {name: name, visible: item.visible, enabled: available, focused: item.activeFocus, centre: win.centre(item), bounds: win.bounds(item)}
|
|
830
|
+
}
|
|
831
|
+
function bounds(item) {
|
|
832
|
+
if (!item) return []
|
|
833
|
+
var point = item.mapToItem(win.contentItem, 0, 0)
|
|
834
|
+
return [point.x, point.y, item.width, item.height]
|
|
835
|
+
}
|
|
836
|
+
function stepFocus(from, back) {
|
|
837
|
+
var items = Picker.tabOrder({chrome: chrome.focusItems(), rail: places.focusItem, view: win.viewItem(), save: save.focusItems(), answers: answers.focusItems()})
|
|
838
|
+
if (!items.length) return
|
|
839
|
+
var at = items.indexOf(from)
|
|
840
|
+
// No origin lands on Cancel: a submission or a lost backend disables Filter and Open, and Filter is still enabled when this runs.
|
|
841
|
+
var next = from ? items[(at + (back ? -1 : 1) + items.length) % items.length] : (items.indexOf(answers.cancelItem) >= 0 ? answers.cancelItem : items[0])
|
|
842
|
+
if (next === list && shares.item && shares.item.active) shareFocus.forceActiveFocus(Qt.TabFocusReason)
|
|
843
|
+
else next.forceActiveFocus(Qt.TabFocusReason)
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
}
|