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/TabBar.qml
ADDED
|
@@ -0,0 +1,673 @@
|
|
|
1
|
+
import QtQuick
|
|
2
|
+
import Quickshell
|
|
3
|
+
import Quickshell.Io
|
|
4
|
+
import "." as Flea
|
|
5
|
+
import "js/DragOut.js" as DragOut
|
|
6
|
+
import "js/Tabs.js" as Tabs
|
|
7
|
+
import "js/TabMove.js" as TabMove
|
|
8
|
+
|
|
9
|
+
// The window's tab strip. Hidden with no height until a second tab exists, so the default window
|
|
10
|
+
// keeps the chrome-to-list layout every existing click test and the first-paint path already have.
|
|
11
|
+
Item {
|
|
12
|
+
id: root
|
|
13
|
+
|
|
14
|
+
property var pane: null
|
|
15
|
+
enabled: root.pane !== null && root.pane.enabled
|
|
16
|
+
|
|
17
|
+
// pane.tabs is a replaced JS object, so these bindings have to read it directly; a helper
|
|
18
|
+
// call alone would not re-run when t opens a second tab.
|
|
19
|
+
readonly property var tabs: pane ? pane.tabs : null
|
|
20
|
+
readonly property string path: pane ? pane.path : ""
|
|
21
|
+
readonly property int tabCount: root.tabs && root.tabs.items && root.tabs.items.length > 0 ? root.tabs.items.length : 1
|
|
22
|
+
readonly property int currentIndex: root.tabs ? root.tabs.index : 0
|
|
23
|
+
readonly property bool open: root.tabCount > 1
|
|
24
|
+
// Tab's strip stop: the keyboard is on the tab at focusedIndex, or on New tab one past the last, which draws its own frame; ui/js/ChromeKeys.js moves it.
|
|
25
|
+
property bool stopFocused: false
|
|
26
|
+
property int focusedIndex: 0
|
|
27
|
+
// The keyboard frame lies inside a tab's box, clear of the rule, the current tab's accent edge and the separator.
|
|
28
|
+
readonly property real frameLeft: Theme.chromeControlInset
|
|
29
|
+
readonly property real frameBottom: Theme.accentEdge + Theme.chromeControlInset
|
|
30
|
+
readonly property int tabWidth: {
|
|
31
|
+
var n = Math.max(1, root.tabCount)
|
|
32
|
+
var avail = Math.max(0, root.width - Theme.hitMin)
|
|
33
|
+
var maxW = Math.round(Theme.font.caption * 12) + Theme.hitMin + 2 * Theme.spacing.rowPaddingX
|
|
34
|
+
var minW = Theme.hitMin * 3
|
|
35
|
+
return Math.round(Math.max(minW, Math.min(maxW, avail / n)))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// How long a drag rests on a tab before the tab is selected: long enough to cross it on the way elsewhere.
|
|
39
|
+
readonly property int hoverSwitchMs: 400
|
|
40
|
+
|
|
41
|
+
// Tabs040 callout 1: a tab drag reorders the strip. dragFrom is the tab
|
|
42
|
+
// held, dropAt the insertion point (0..tabCount) its pointer names.
|
|
43
|
+
property int dragFrom: -1
|
|
44
|
+
property int dropAt: -1
|
|
45
|
+
readonly property int dragTo: root.dropAt > root.dragFrom ? root.dropAt - 1 : root.dropAt
|
|
46
|
+
readonly property var layoutOrder: {
|
|
47
|
+
var order = []
|
|
48
|
+
for (var i = 0; i < root.tabCount; i++) order.push(i)
|
|
49
|
+
if (root.dragFrom >= 0 && root.dropAt >= 0)
|
|
50
|
+
TabMove.reorder(order, root.dragFrom, root.dragTo, root.currentIndex)
|
|
51
|
+
return order
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Gesture state is separate from outstanding lifts awaiting their own ack.
|
|
55
|
+
property var outMime: ({})
|
|
56
|
+
property bool outActive: false
|
|
57
|
+
property bool outOutside: false
|
|
58
|
+
property bool outRefused: false
|
|
59
|
+
property int outIndex: -1
|
|
60
|
+
property string outPath: ""
|
|
61
|
+
property string outPid: ""
|
|
62
|
+
property string outToken: ""
|
|
63
|
+
property bool ownAccepted: false
|
|
64
|
+
property var pendingTab: null
|
|
65
|
+
readonly property int tabDropPeekFirst: 2
|
|
66
|
+
// A receiver cannot wait longer than the source keeps its unacknowledged lift.
|
|
67
|
+
readonly property int tabDropWaitMs: Tabs.ACK_WAIT_MS
|
|
68
|
+
onPaneChanged: root.refuseTabDrop()
|
|
69
|
+
// The lift outlives the drag's own end, until the taken ack or Tabs.ACK_WAIT_MS ends.
|
|
70
|
+
property double ackLiftedAt: 0
|
|
71
|
+
property var takenQueue: []
|
|
72
|
+
property var outstandingLifts: []
|
|
73
|
+
readonly property int ackPollMs: 100 // Settle acknowledged lifts when their captured pane finishes loading.
|
|
74
|
+
// Stage trace, on only with FLEA_TRACE_TABDRAG=1; read once, silent otherwise.
|
|
75
|
+
readonly property bool tabTrace: Quickshell.env("FLEA_TRACE_TABDRAG") === "1"
|
|
76
|
+
function traceTab(stage, detail) { if (root.tabTrace) console.log("TABDRAG " + stage + " pid=" + Quickshell.processId + " " + detail) }
|
|
77
|
+
|
|
78
|
+
Timer {
|
|
79
|
+
id: ackTimer
|
|
80
|
+
interval: root.ackPollMs
|
|
81
|
+
repeat: true
|
|
82
|
+
running: root.outstandingLifts.length > 0
|
|
83
|
+
onTriggered: root.drainLifts()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
Timer {
|
|
87
|
+
id: tabDropDeadline
|
|
88
|
+
interval: root.tabDropWaitMs
|
|
89
|
+
onTriggered: root.refuseTabDrop()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
Process {
|
|
93
|
+
id: takenAck
|
|
94
|
+
onExited: function (exitCode, exitStatus) {
|
|
95
|
+
root.traceTab("taken-exit", "code=" + exitCode)
|
|
96
|
+
root.pumpTaken()
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// QTBUG-64128: a DropArea rejects a drag sourced from its ancestor.
|
|
101
|
+
Item { id: dragOrigin; width: 0; height: 0; visible: false }
|
|
102
|
+
Drag.source: dragOrigin
|
|
103
|
+
Flea.TabDragGeometry { id: sourceGeometry }
|
|
104
|
+
|
|
105
|
+
Drag.dragType: Drag.Automatic
|
|
106
|
+
// Private tab MIME must never offer the folder as a file drag.
|
|
107
|
+
Drag.supportedActions: Qt.MoveAction
|
|
108
|
+
Drag.mimeData: root.outMime
|
|
109
|
+
Drag.onDragFinished: function (dropAction) { root.outFinished(dropAction) }
|
|
110
|
+
|
|
111
|
+
function dragStarted(index) {
|
|
112
|
+
root.dragFrom = index
|
|
113
|
+
root.dropAt = index < 0 ? -1 : index + 1
|
|
114
|
+
}
|
|
115
|
+
function dragMoved(x) {
|
|
116
|
+
if (root.dragFrom < 0)
|
|
117
|
+
return
|
|
118
|
+
root.dropAt = TabMove.insertionAt(x, root.tabWidth, root.tabCount)
|
|
119
|
+
}
|
|
120
|
+
function dragFinished() {
|
|
121
|
+
if (root.dragFrom >= 0 && root.dropAt >= 0 && root.pane) {
|
|
122
|
+
Tabs.move(root.pane, root.dragFrom, root.dragTo)
|
|
123
|
+
}
|
|
124
|
+
root.dragFrom = -1
|
|
125
|
+
root.dropAt = -1
|
|
126
|
+
root.drainLifts()
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// A lift captures the tab payload before the pane can change.
|
|
130
|
+
function tabLiftBegan(index) {
|
|
131
|
+
if (!Tabs.canLift(root.pane)) {
|
|
132
|
+
root.dragFrom = -1
|
|
133
|
+
root.dropAt = -1
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
root.dragStarted(index)
|
|
137
|
+
root.outIndex = index
|
|
138
|
+
var info = Tabs.tabInfo(root.pane, index)
|
|
139
|
+
root.outPath = info ? info.path : ""
|
|
140
|
+
root.outPid = String(Quickshell.processId)
|
|
141
|
+
root.outToken = Tabs.newToken()
|
|
142
|
+
var band = strip.mapToItem(null, 0, 0)
|
|
143
|
+
sourceGeometry.begin(root.outToken, { x: band.x, y: band.y,
|
|
144
|
+
width: root.width - strip.x, height: root.height })
|
|
145
|
+
root.ackLiftedAt = 0
|
|
146
|
+
var lift = Tabs.captureLift(root.pane, index, root.outToken, root.ackLiftedAt)
|
|
147
|
+
if (lift) root.outstandingLifts = root.outstandingLifts.concat([lift])
|
|
148
|
+
root.outMime = Tabs.tabDragMime(root.pane, index, root.outPid, root.outToken)
|
|
149
|
+
root.outOutside = false
|
|
150
|
+
root.outRefused = false
|
|
151
|
+
root.ownAccepted = false
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// A platform drag freezes the strip reorder until the drop answers.
|
|
155
|
+
function tabLiftMoved(stripX, winX, winY) {
|
|
156
|
+
root.dragMoved(stripX)
|
|
157
|
+
if (root.parent)
|
|
158
|
+
root.outOutside = winX < 0 || winY < 0 || winX > root.parent.width || winY > root.parent.height
|
|
159
|
+
if (root.outOutside && !root.outActive && root.outMime[Tabs.TAB_MIME]) {
|
|
160
|
+
var refusal = Tabs.tearRefusal(root.pane)
|
|
161
|
+
if (refusal.length > 0) {
|
|
162
|
+
if (!root.outRefused && root.pane)
|
|
163
|
+
root.pane.message(refusal, false)
|
|
164
|
+
root.outRefused = true
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
root.outActive = true
|
|
168
|
+
root.Drag.active = true
|
|
169
|
+
root.traceTab("drag-start", "index=" + root.outIndex + " path=" + root.outPath + " mime=" + Object.keys(root.outMime).join(",") + " dragType=" + root.Drag.dragType)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// A platform gesture owns its release, so the handler must not reorder again.
|
|
174
|
+
function tabLiftEnded() {
|
|
175
|
+
if (root.outActive || root.ownAccepted) {
|
|
176
|
+
root.dragFrom = -1
|
|
177
|
+
root.dropAt = -1
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
root.dragFinished()
|
|
181
|
+
if (root.ackLiftedAt === 0) root.clearAck()
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// The lift must survive the drag ending until its acknowledgment arrives.
|
|
185
|
+
function holdAck() {
|
|
186
|
+
var lift = Tabs.liftFor(root.outstandingLifts, root.outToken)
|
|
187
|
+
root.ackLiftedAt = Date.now()
|
|
188
|
+
if (lift) lift.liftedAt = root.ackLiftedAt
|
|
189
|
+
}
|
|
190
|
+
function clearAck() {
|
|
191
|
+
root.outstandingLifts = root.outstandingLifts.filter(function (lift) { return lift.token !== root.outToken })
|
|
192
|
+
root.outToken = ""
|
|
193
|
+
root.outIndex = -1
|
|
194
|
+
root.outPath = ""
|
|
195
|
+
root.ackLiftedAt = 0
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function drainLifts() {
|
|
199
|
+
if (root.dragFrom >= 0) return
|
|
200
|
+
var now = Date.now()
|
|
201
|
+
var kept = []
|
|
202
|
+
for (var i = 0; i < root.outstandingLifts.length; i++) {
|
|
203
|
+
var lift = root.outstandingLifts[i]
|
|
204
|
+
if (!lift.taken && !Tabs.ackCloses(lift.token, lift.token, lift.liftedAt, now)) {
|
|
205
|
+
if (root.outToken === lift.token) root.outToken = ""
|
|
206
|
+
continue
|
|
207
|
+
}
|
|
208
|
+
var index = Tabs.resolveMovedTab(lift.pane, lift.identity)
|
|
209
|
+
if (index < 0) continue
|
|
210
|
+
if (lift.taken && !lift.pane.listInFlight) {
|
|
211
|
+
Tabs.closeTabAfterMove(lift.pane, index)
|
|
212
|
+
if (root.outToken === lift.token) root.outToken = ""
|
|
213
|
+
} else kept.push(lift)
|
|
214
|
+
}
|
|
215
|
+
root.outstandingLifts = kept
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// A cross-process drop closes the source tab only after a matching acknowledgment.
|
|
219
|
+
function outFinished(dropAction) {
|
|
220
|
+
root.traceTab("drag-finished", "action=" + dropAction)
|
|
221
|
+
var consumed = root.ownAccepted
|
|
222
|
+
root.dragFrom = -1
|
|
223
|
+
root.dropAt = -1
|
|
224
|
+
root.outActive = false
|
|
225
|
+
root.ownAccepted = false
|
|
226
|
+
if (consumed)
|
|
227
|
+
root.clearAck()
|
|
228
|
+
else if (root.outToken.length > 0)
|
|
229
|
+
root.holdAck()
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Qt's platform Escape filter and explicit cancellation share the same ending.
|
|
233
|
+
function cancelOut() {
|
|
234
|
+
if (!root.outActive)
|
|
235
|
+
return
|
|
236
|
+
root.Drag.cancel()
|
|
237
|
+
if (root.outActive)
|
|
238
|
+
root.outFinished(Qt.IgnoreAction)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function returnAt(at) {
|
|
242
|
+
var lift = Tabs.liftFor(root.outstandingLifts, root.outToken)
|
|
243
|
+
var from = lift ? Tabs.resolveMovedTab(lift.pane, lift.identity) : -1
|
|
244
|
+
if (from >= 0)
|
|
245
|
+
Tabs.move(lift.pane, from, at > from ? at - 1 : at)
|
|
246
|
+
root.ownAccepted = true
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function catcherDrop(x, y) {
|
|
250
|
+
if (!root.outActive)
|
|
251
|
+
return
|
|
252
|
+
var result = Tabs.catcherOutcome(sourceGeometry.rect, sourceGeometry.strip,
|
|
253
|
+
x, y, root.tabWidth, root.tabCount)
|
|
254
|
+
root.traceTab("catcher-drop", "outcome=" + result.outcome + " global=" + x + "," + y
|
|
255
|
+
+ (sourceGeometry.rect ? "" : " reason=unknown-source-rectangle"))
|
|
256
|
+
if (result.outcome === "tearoff")
|
|
257
|
+
root.tearOffAt()
|
|
258
|
+
else if (result.outcome === "return")
|
|
259
|
+
root.returnAt(result.at)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// The new window acknowledges only after listing; a failed spawn keeps the source tab.
|
|
263
|
+
function tearOffAt() {
|
|
264
|
+
if (root.outPath.length === 0)
|
|
265
|
+
return
|
|
266
|
+
var info = Tabs.parseTabMime(root.outMime[Tabs.TAB_MIME])
|
|
267
|
+
Quickshell.execDetached(["env", "FLEA_TAB_SOURCE_PID=" + root.outPid,
|
|
268
|
+
"FLEA_TAB_CURSOR=" + (info ? info.cursor : ""),
|
|
269
|
+
"FLEA_TAB_TOKEN=" + root.outToken, Quickshell.env("FLEA_BIN") || "flea", root.outPath])
|
|
270
|
+
root.outFinished(Qt.IgnoreAction)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// An own-window tab drag reorders its strip instead of opening another tab.
|
|
274
|
+
function tabEnterOk(drag) {
|
|
275
|
+
var ok = Tabs.enterAccepts(drag.formats, drag.getDataAsString(Tabs.TAB_MIME), undefined, Tabs.canReceive(root.pane), root.outActive)
|
|
276
|
+
root.traceTab("enter-strip", "formats=" + String(drag.formats) + " ok=" + ok)
|
|
277
|
+
return ok
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// A foreign tab opens only after its reserved peek confirms a directory.
|
|
281
|
+
function acceptTabDrop(payload, info, at) {
|
|
282
|
+
if (root.pendingTab || !root.pane || !Tabs.canReceive(root.pane)) {
|
|
283
|
+
root.traceTab("drop-skip", "reason=strip-accept-refused")
|
|
284
|
+
return
|
|
285
|
+
}
|
|
286
|
+
root.pendingTab = { payload: payload, pid: info.pid, token: info.token, path: info.path, at: at, pane: root.pane }
|
|
287
|
+
tabDropDeadline.restart()
|
|
288
|
+
root.traceTab("peek-sent", "path=" + info.path + " first=" + root.tabDropPeekFirst + " hidden=false")
|
|
289
|
+
root.pane.backend.peek(info.path, root.tabDropPeekFirst, false, false)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function refuseTabDrop() {
|
|
293
|
+
var pending = root.pendingTab
|
|
294
|
+
if (!pending)
|
|
295
|
+
return
|
|
296
|
+
root.pendingTab = null
|
|
297
|
+
tabDropDeadline.stop()
|
|
298
|
+
var pane = root.pane || pending.pane
|
|
299
|
+
if (pane)
|
|
300
|
+
pane.message("That tab could not be received.", false)
|
|
301
|
+
root.traceTab("receive", "ok=false refused-pending path=" + pending.path)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Send the taken acknowledgment only after the receiving tab opens.
|
|
305
|
+
function sendTaken(pid, token) {
|
|
306
|
+
root.traceTab("taken-sent", "target=" + pid + " token=" + token)
|
|
307
|
+
root.takenQueue = root.takenQueue.concat([{ pid: String(pid), token: String(token) }])
|
|
308
|
+
root.pumpTaken()
|
|
309
|
+
}
|
|
310
|
+
// One Process runs one call, so overlapping acks queue behind it instead of vanishing inside execDetached.
|
|
311
|
+
function pumpTaken() {
|
|
312
|
+
if (takenAck.running || root.takenQueue.length === 0)
|
|
313
|
+
return
|
|
314
|
+
var next = root.takenQueue[0]
|
|
315
|
+
root.takenQueue = root.takenQueue.slice(1)
|
|
316
|
+
takenAck.command = ["qs", "ipc", "--pid", next.pid, "call", "fleatab", "taken", next.token]
|
|
317
|
+
takenAck.running = true
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
Connections {
|
|
321
|
+
target: root.pane ? root.pane.backend : null
|
|
322
|
+
function onPeeked(path, hidden, total, rows, readFailed, mode, hiddenLast, first) {
|
|
323
|
+
var pending = root.pendingTab
|
|
324
|
+
if (!pending || path !== pending.path || hidden !== false || hiddenLast !== false || first !== root.tabDropPeekFirst)
|
|
325
|
+
return
|
|
326
|
+
root.traceTab("peek-answer", "path=" + path + " failed=" + readFailed + " total=" + total)
|
|
327
|
+
root.pendingTab = null
|
|
328
|
+
tabDropDeadline.stop()
|
|
329
|
+
if (readFailed) {
|
|
330
|
+
if (root.pane)
|
|
331
|
+
root.pane.message("That folder is no longer there.", false)
|
|
332
|
+
root.traceTab("receive", "ok=false refused-missing path=" + path)
|
|
333
|
+
return
|
|
334
|
+
}
|
|
335
|
+
var received = root.pane && Tabs.receiveTab(root.pane, pending.payload, pending.at)
|
|
336
|
+
root.traceTab("receive", "ok=" + !!received + " path=" + pending.path + " at=" + pending.at)
|
|
337
|
+
if (received)
|
|
338
|
+
root.sendTaken(pending.pid, pending.token)
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
visible: root.open
|
|
343
|
+
implicitHeight: Theme.chromeHeight
|
|
344
|
+
height: visible ? implicitHeight : 0
|
|
345
|
+
|
|
346
|
+
function itemAt(index) {
|
|
347
|
+
return repeater.itemAt(index)
|
|
348
|
+
}
|
|
349
|
+
// The strip's keyboard frames, so tests/keyreach.qml measures them in their host.
|
|
350
|
+
readonly property Item newTabFrame: addFrame
|
|
351
|
+
|
|
352
|
+
// On glass the strip's ground runs left and right of the current tab's plate, which a dragged tab gives up; see ui/Glass.qml.
|
|
353
|
+
readonly property Item plate: Glass.on && root.dragFrom !== root.currentIndex && repeater.count > root.currentIndex ? root.itemAt(root.currentIndex) : null
|
|
354
|
+
Rectangle {
|
|
355
|
+
width: root.plate ? strip.x + root.plate.x : parent.width
|
|
356
|
+
height: parent.height
|
|
357
|
+
color: Glass.surface
|
|
358
|
+
}
|
|
359
|
+
Rectangle {
|
|
360
|
+
visible: root.plate !== null
|
|
361
|
+
x: root.plate ? strip.x + root.plate.x + root.plate.width : 0
|
|
362
|
+
width: parent.width - x
|
|
363
|
+
height: parent.height
|
|
364
|
+
color: Glass.surface
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
Rectangle {
|
|
368
|
+
anchors.bottom: parent.bottom
|
|
369
|
+
anchors.left: parent.left
|
|
370
|
+
anchors.right: parent.right
|
|
371
|
+
height: Theme.spacing.hairline
|
|
372
|
+
color: Theme.color.foreground
|
|
373
|
+
opacity: 0.12
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
Item {
|
|
377
|
+
id: strip
|
|
378
|
+
anchors.left: parent.left
|
|
379
|
+
anchors.leftMargin: Theme.spacing.rowPaddingX
|
|
380
|
+
anchors.top: parent.top
|
|
381
|
+
anchors.bottom: parent.bottom
|
|
382
|
+
width: root.tabCount * root.tabWidth + Theme.hitMin
|
|
383
|
+
|
|
384
|
+
Repeater {
|
|
385
|
+
id: repeater
|
|
386
|
+
model: root.open ? root.tabCount : 0
|
|
387
|
+
delegate: Item {
|
|
388
|
+
id: tab
|
|
389
|
+
required property int index
|
|
390
|
+
readonly property int slot: root.layoutOrder.indexOf(tab.index)
|
|
391
|
+
x: tab.slot * root.tabWidth
|
|
392
|
+
width: root.tabWidth
|
|
393
|
+
height: strip.height
|
|
394
|
+
// The tab under the pointer draws ghosted while its drag runs.
|
|
395
|
+
opacity: root.dragFrom === tab.index ? Theme.disabledOpacity : 1
|
|
396
|
+
|
|
397
|
+
readonly property bool current: root.currentIndex === tab.index
|
|
398
|
+
readonly property Item focusFrame: tabFrame
|
|
399
|
+
// Every input named, so the label re-reads when a tab opens or the pane navigates.
|
|
400
|
+
readonly property string title: Tabs.label(
|
|
401
|
+
Tabs.pathAt(root.tabs, root.currentIndex, tab.index, root.path),
|
|
402
|
+
pane ? pane.home : "")
|
|
403
|
+
|
|
404
|
+
Accessible.role: Accessible.PageTab
|
|
405
|
+
Accessible.name: tab.title
|
|
406
|
+
Accessible.onPressAction: if (pane) Tabs.selectAt(pane, tab.index)
|
|
407
|
+
|
|
408
|
+
HoverHandler { cursorShape: Qt.PointingHandCursor }
|
|
409
|
+
|
|
410
|
+
// The rule: a drag resting on a tab selects it so the drop can land in that tab's
|
|
411
|
+
// listing, and a drop on the tab itself lands there too, by path, see ui/DropInto.qml.
|
|
412
|
+
Flea.DropInto {
|
|
413
|
+
anchors.fill: parent
|
|
414
|
+
pane: root.pane
|
|
415
|
+
enabled: DragOut.searchTabEnabled(root.pane ? root.pane.searchMode : "", tab.current)
|
|
416
|
+
switchesOnHover: true
|
|
417
|
+
refuseLoading: DragOut.refuseLoading(root.pane && root.pane.listInFlight, true, tab.current)
|
|
418
|
+
// The pane's drop path, not its drawn one: a tab selected by the hover switch is
|
|
419
|
+
// current before its listing lands, and until then pane.path is the tab left behind.
|
|
420
|
+
// Sidebar040: a history is not a directory, so a drop onto the tab standing on it
|
|
421
|
+
// is refused rather than landing in the root it stands on.
|
|
422
|
+
dest: root.pane && root.pane.recentMode.length > 0 && tab.current ? ""
|
|
423
|
+
: Tabs.pathAt(root.tabs, root.currentIndex, tab.index,
|
|
424
|
+
root.pane ? root.pane.dropPath : root.path)
|
|
425
|
+
// Unknown while the listed reply is still out, because dirDev is then the directory a hover switch just left; unknown makes verbFor copy, never a move that turns into a cross-device delete.
|
|
426
|
+
destDev: Tabs.devAt(root.tabs, root.currentIndex, tab.index,
|
|
427
|
+
root.pane && root.pane.backend && !root.pane.listInFlight ? root.pane.backend.dirDev : 0)
|
|
428
|
+
// Only an accepted enter arms the switch: Qt emits entered before it reads accepted,
|
|
429
|
+
// and a refused drag gets no exited, so the timer would otherwise never stop.
|
|
430
|
+
onEntered: function (drag) { if (drag.accepted) hoverSwitch.restart() }
|
|
431
|
+
onExited: hoverSwitch.stop()
|
|
432
|
+
onDropped: hoverSwitch.stop()
|
|
433
|
+
}
|
|
434
|
+
Timer {
|
|
435
|
+
id: hoverSwitch
|
|
436
|
+
interval: root.hoverSwitchMs
|
|
437
|
+
onTriggered: if (root.pane && !tab.current) Tabs.selectAt(root.pane, tab.index)
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
Rectangle {
|
|
441
|
+
anchors.fill: parent
|
|
442
|
+
color: tab.current && (!Glass.on || root.plate === tab) ? Glass.background : "transparent"
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Flush on the strip's own bottom edge, replacing it rather than sitting inside the
|
|
446
|
+
// plate: rendered in Quickshell on a low-chroma theme, an inset edge vanishes.
|
|
447
|
+
Rectangle {
|
|
448
|
+
anchors.bottom: parent.bottom
|
|
449
|
+
anchors.left: parent.left
|
|
450
|
+
anchors.right: parent.right
|
|
451
|
+
height: Theme.accentEdge
|
|
452
|
+
color: Theme.color.accent
|
|
453
|
+
visible: tab.current
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
Rectangle {
|
|
457
|
+
anchors.right: parent.right
|
|
458
|
+
anchors.verticalCenter: parent.verticalCenter
|
|
459
|
+
width: Theme.spacing.hairline
|
|
460
|
+
height: parent.height * 0.45
|
|
461
|
+
color: Theme.color.foreground
|
|
462
|
+
opacity: 0.12
|
|
463
|
+
visible: tab.slot < root.tabCount - 1 && !tab.current
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
Rectangle {
|
|
467
|
+
id: tabFrame
|
|
468
|
+
x: root.frameLeft
|
|
469
|
+
y: root.frameLeft
|
|
470
|
+
width: parent.width - 2 * root.frameLeft
|
|
471
|
+
height: parent.height - root.frameLeft - root.frameBottom
|
|
472
|
+
color: "transparent"
|
|
473
|
+
border.width: Theme.spacing.hairline
|
|
474
|
+
border.color: Theme.color.foreground
|
|
475
|
+
visible: root.stopFocused && root.focusedIndex === tab.index
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
Text {
|
|
479
|
+
id: titleText
|
|
480
|
+
anchors.left: parent.left
|
|
481
|
+
anchors.leftMargin: Theme.spacing.rowPaddingX
|
|
482
|
+
anchors.right: closeHit.left
|
|
483
|
+
anchors.verticalCenter: parent.verticalCenter
|
|
484
|
+
text: tab.title
|
|
485
|
+
color: tab.current ? Theme.color.foreground : Theme.color.muted
|
|
486
|
+
font.family: Theme.font.family
|
|
487
|
+
font.pixelSize: Theme.font.caption
|
|
488
|
+
textFormat: Text.PlainText
|
|
489
|
+
elide: Text.ElideRight
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
Item {
|
|
493
|
+
id: closeHit
|
|
494
|
+
anchors.right: parent.right
|
|
495
|
+
anchors.verticalCenter: parent.verticalCenter
|
|
496
|
+
width: Theme.hitMin
|
|
497
|
+
height: parent.height
|
|
498
|
+
|
|
499
|
+
// The mark never moves and its target never shrinks; only the ink answers, so a
|
|
500
|
+
// crowded strip is no harder to hit than a tidy one.
|
|
501
|
+
Flea.Glyph {
|
|
502
|
+
anchors.centerIn: parent
|
|
503
|
+
width: Theme.chromeMarkSize
|
|
504
|
+
height: Theme.chromeMarkSize
|
|
505
|
+
name: "x"
|
|
506
|
+
color: tab.current || closeHover.hovered ? Theme.color.foreground : Theme.color.mutedFrame
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
HoverHandler { id: closeHover }
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
TapHandler {
|
|
513
|
+
acceptedButtons: Qt.LeftButton | Qt.MiddleButton
|
|
514
|
+
onTapped: function (eventPoint, button) {
|
|
515
|
+
if (!pane)
|
|
516
|
+
return
|
|
517
|
+
if (button === Qt.MiddleButton) {
|
|
518
|
+
Tabs.closeAt(pane, tab.index)
|
|
519
|
+
return
|
|
520
|
+
}
|
|
521
|
+
var local = closeHit.mapFromItem(tab, eventPoint.position.x, eventPoint.position.y)
|
|
522
|
+
if (local.x >= 0 && local.x <= closeHit.width && local.y >= 0 && local.y <= closeHit.height)
|
|
523
|
+
Tabs.closeAt(pane, tab.index)
|
|
524
|
+
else
|
|
525
|
+
Tabs.selectAt(pane, tab.index)
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// A left-button drag reorders; a press without a move still taps above.
|
|
530
|
+
// A file drag never enters here, so the hover switch answers only files.
|
|
531
|
+
DragHandler {
|
|
532
|
+
acceptedButtons: Qt.LeftButton
|
|
533
|
+
target: null
|
|
534
|
+
function updateDrop() {
|
|
535
|
+
// Scene coordinates stay fixed while the preview moves the delegate beneath the pointer.
|
|
536
|
+
var pos = strip.mapFromItem(null, centroid.scenePosition.x, centroid.scenePosition.y)
|
|
537
|
+
var win = root.parent ? root.parent.mapFromItem(null, centroid.scenePosition.x, centroid.scenePosition.y) : pos
|
|
538
|
+
root.tabLiftMoved(pos.x, win.x, win.y)
|
|
539
|
+
}
|
|
540
|
+
onActiveChanged: {
|
|
541
|
+
// The threshold move precedes activation, so its centroid must be sampled here.
|
|
542
|
+
if (active) {
|
|
543
|
+
root.tabLiftBegan(tab.index)
|
|
544
|
+
updateDrop()
|
|
545
|
+
} else if (root.outActive || root.ownAccepted) {
|
|
546
|
+
// A platform drag owns its release, so no grab release reaches this handler to end the lift.
|
|
547
|
+
root.tabLiftEnded()
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
onCentroidChanged: if (active) updateDrop()
|
|
551
|
+
onGrabChanged: function (transition, point) {
|
|
552
|
+
if (transition !== PointerDevice.UngrabExclusive) return
|
|
553
|
+
// Qt deactivates before updating the centroid on release; the event point holds the release position.
|
|
554
|
+
var pos = strip.mapFromItem(null, point.scenePosition.x, point.scenePosition.y)
|
|
555
|
+
root.dragMoved(pos.x)
|
|
556
|
+
root.tabLiftEnded()
|
|
557
|
+
}
|
|
558
|
+
onCanceled: {
|
|
559
|
+
root.dragFrom = -1
|
|
560
|
+
root.dropAt = -1
|
|
561
|
+
if (!root.outActive && root.ackLiftedAt === 0) root.clearAck()
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
Item {
|
|
568
|
+
id: addButton
|
|
569
|
+
x: root.tabCount * root.tabWidth
|
|
570
|
+
width: Theme.hitMin
|
|
571
|
+
height: strip.height
|
|
572
|
+
Accessible.role: Accessible.Button
|
|
573
|
+
Accessible.name: "New tab"
|
|
574
|
+
Accessible.onPressAction: if (pane) Tabs.openNew(pane)
|
|
575
|
+
HoverHandler { cursorShape: Qt.PointingHandCursor }
|
|
576
|
+
|
|
577
|
+
Rectangle {
|
|
578
|
+
id: addFrame
|
|
579
|
+
x: root.frameLeft
|
|
580
|
+
y: root.frameLeft
|
|
581
|
+
width: parent.width - 2 * root.frameLeft
|
|
582
|
+
height: parent.height - root.frameLeft - root.frameBottom
|
|
583
|
+
color: "transparent"
|
|
584
|
+
border.width: Theme.spacing.hairline
|
|
585
|
+
border.color: Theme.color.foreground
|
|
586
|
+
visible: root.stopFocused && root.focusedIndex === root.tabCount
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
Flea.Glyph {
|
|
590
|
+
anchors.centerIn: parent
|
|
591
|
+
width: Theme.chromeMarkSize
|
|
592
|
+
height: Theme.chromeMarkSize
|
|
593
|
+
name: "plus"
|
|
594
|
+
color: root.stopFocused && root.focusedIndex === root.tabCount ? Theme.color.foreground : Theme.color.mutedFrame
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
TapHandler {
|
|
598
|
+
acceptedButtons: Qt.LeftButton
|
|
599
|
+
onTapped: if (pane) Tabs.openNew(pane)
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// The strip catcher accepts tab drags without intercepting pointer input.
|
|
605
|
+
DropArea {
|
|
606
|
+
anchors.left: parent.left
|
|
607
|
+
anchors.leftMargin: Theme.spacing.rowPaddingX
|
|
608
|
+
anchors.top: parent.top
|
|
609
|
+
anchors.bottom: parent.bottom
|
|
610
|
+
anchors.right: parent.right
|
|
611
|
+
keys: [Tabs.TAB_MIME]
|
|
612
|
+
onEntered: function (drag) {
|
|
613
|
+
if (!root.tabEnterOk(drag))
|
|
614
|
+
drag.accepted = false
|
|
615
|
+
}
|
|
616
|
+
onExited: root.traceTab("leave-strip", "")
|
|
617
|
+
// An own drag out and back tracks its insertion while it is over the strip.
|
|
618
|
+
onPositionChanged: function (drag) {
|
|
619
|
+
var info = Tabs.parseTabMime(drag.getDataAsString(Tabs.TAB_MIME))
|
|
620
|
+
if (info && Tabs.isOwnTab(info) && root.outActive)
|
|
621
|
+
root.dragMoved(drag.x)
|
|
622
|
+
}
|
|
623
|
+
onDropped: function (drop) {
|
|
624
|
+
var payload = drop.getDataAsString(Tabs.TAB_MIME)
|
|
625
|
+
root.traceTab("drop-strip", "empty=" + (payload.length === 0) + " len=" + payload.length)
|
|
626
|
+
var info = Tabs.parseTabMime(payload)
|
|
627
|
+
if (!info) {
|
|
628
|
+
root.traceTab("drop-skip", "reason=strip-bad-payload")
|
|
629
|
+
return
|
|
630
|
+
}
|
|
631
|
+
if (Tabs.isOwnTab(info)) {
|
|
632
|
+
// An own-strip return reorders once before the late release clears the gesture.
|
|
633
|
+
if (!root.outActive) {
|
|
634
|
+
root.traceTab("drop-skip", "reason=strip-own-inactive")
|
|
635
|
+
return
|
|
636
|
+
}
|
|
637
|
+
root.returnAt(Tabs.dropIndexAt(drop.x, root.tabWidth, root.tabCount))
|
|
638
|
+
drop.accept(Qt.MoveAction)
|
|
639
|
+
return
|
|
640
|
+
}
|
|
641
|
+
// The take decision answers Move at once; the peek behind it may still refuse, and then no ack goes out.
|
|
642
|
+
if (Tabs.dropDecision(info, undefined, root.outActive, Tabs.canReceive(root.pane)) !== Tabs.DROP_TAKE) {
|
|
643
|
+
root.traceTab("drop-skip", "reason=strip-decision-ignore")
|
|
644
|
+
return
|
|
645
|
+
}
|
|
646
|
+
drop.accept(Qt.MoveAction)
|
|
647
|
+
root.acceptTabDrop(payload, info, Tabs.dropIndexAt(drop.x, root.tabWidth, root.tabCount))
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// The catcher stays loaded only while an outgoing tab drag is active.
|
|
652
|
+
Loader {
|
|
653
|
+
id: tearPanels
|
|
654
|
+
active: root.outActive
|
|
655
|
+
source: "file://" + Quickshell.shellDir + "/tabtearoff.qml"
|
|
656
|
+
onLoaded: {
|
|
657
|
+
item.tabBar = root
|
|
658
|
+
item.tabMime = Tabs.TAB_MIME
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
Component.onCompleted: Tabs.setOwnPid(Quickshell.processId)
|
|
663
|
+
|
|
664
|
+
// Tabs040 callout 1: the accent bar borders the ghost's leading edge through the strip's height.
|
|
665
|
+
Rectangle {
|
|
666
|
+
visible: root.dragFrom >= 0 && root.dropAt >= 0
|
|
667
|
+
x: strip.x + root.dragTo * root.tabWidth - Theme.spacing.hairline
|
|
668
|
+
width: 2 * Theme.spacing.hairline
|
|
669
|
+
anchors.top: parent.top
|
|
670
|
+
anchors.bottom: parent.bottom
|
|
671
|
+
color: Theme.color.accent
|
|
672
|
+
}
|
|
673
|
+
}
|