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,370 @@
|
|
|
1
|
+
import QtQuick
|
|
2
|
+
import "." as Flea
|
|
3
|
+
import "js/Columns.js" as Columns
|
|
4
|
+
import "js/Facts.js" as Facts
|
|
5
|
+
import "js/Thumbs.js" as Thumbs
|
|
6
|
+
import "js/ExtThumbs.js" as ExtThumbs
|
|
7
|
+
import "js/Keymap.js" as Keymap
|
|
8
|
+
import "js/PreviewKeys.js" as PreviewKeys
|
|
9
|
+
import "js/Search.js" as Search
|
|
10
|
+
import "js/PreviewSettle.js" as PreviewSettle
|
|
11
|
+
import "js/PreviewAsk.js" as PreviewAsk
|
|
12
|
+
import "js/PreviewSwap.js" as PreviewSwap
|
|
13
|
+
|
|
14
|
+
// Selection loading is independent of column visibility and the separate Quick Look overlay.
|
|
15
|
+
Flea.PreviewColumn {
|
|
16
|
+
id: root
|
|
17
|
+
property var pane: null
|
|
18
|
+
property int loadedIndex: -1
|
|
19
|
+
property string loadedDirectory: ""
|
|
20
|
+
property string loadedIdentity: ""
|
|
21
|
+
property int pendingToken: 0
|
|
22
|
+
// ui/ColumnsArea.qml's third-column swap, which then drives the cursor moves; null leaves every change immediate.
|
|
23
|
+
property var swap: null
|
|
24
|
+
property string settleKey: ""
|
|
25
|
+
// The running settle stands for a move the clock stamped and trailed; ColumnsArea's own arm stands for none.
|
|
26
|
+
property bool settleTrails: false
|
|
27
|
+
// A load queued under the swap's picture, so a duplicate replace queues no clear behind it.
|
|
28
|
+
property string loadQueuedKey: ""
|
|
29
|
+
// The multi-selection the loaded frame was built for; "" for a lone selection, which never keys on the version.
|
|
30
|
+
property string loadedSelection: ""
|
|
31
|
+
// Last distinct cursor move, so a held key cannot decode mid-burst: only idleness loads at once.
|
|
32
|
+
property double lastMoveAt: 0
|
|
33
|
+
property string lastMoveKey: ""
|
|
34
|
+
signal thumbsApplied(var work)
|
|
35
|
+
onExpandRequested: {
|
|
36
|
+
if (!root.row || !root.pane) return
|
|
37
|
+
var look = root.pane.quickLook()
|
|
38
|
+
look.open(root.path, root.row.i, root.row.s, root.kindName)
|
|
39
|
+
look.pdfItem.expandFrom(root.pdfPage(), PreviewKeys.PDF_ZOOM_MIN)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
Keys.onPressed: function(event) {
|
|
43
|
+
var context = root.rowState === Facts.PDF ? "pdf"
|
|
44
|
+
: root.rowState === Facts.VIDEO || root.rowState === Facts.AUDIO ? "media" : "preview"
|
|
45
|
+
var action = Keymap.lookup(event.key, event.text, event.modifiers, context)
|
|
46
|
+
if (action === "escape" || action === "focusPreview") root.pane.listArea.forceActiveFocus()
|
|
47
|
+
else if (PreviewKeys.pageDirection(action)) root.pageBy(PreviewKeys.pageDirection(action))
|
|
48
|
+
else if (action === "loadPreview") root.loadSelection()
|
|
49
|
+
else if (root.rowState === Facts.PDF) PreviewKeys.pdfAction(action, root)
|
|
50
|
+
else if (action === "preview") {
|
|
51
|
+
var strip = root.mediaStripItem()
|
|
52
|
+
if (strip) strip.toggled()
|
|
53
|
+
else if (root.row && !root.row.d) root.pane.quickLook().open(root.path, root.row.i, root.row.s, root.kindName)
|
|
54
|
+
} else if (action === "seekBack" || action === "seekForward") {
|
|
55
|
+
var direction = action === "seekBack" ? -1 : 1
|
|
56
|
+
if (root.rowState === Facts.PDF) root.turnPage(direction)
|
|
57
|
+
else {
|
|
58
|
+
var transport = root.mediaStripItem()
|
|
59
|
+
if (transport) transport.seeked(root.mediaPosition() + direction * PreviewKeys.SEEK_MS)
|
|
60
|
+
}
|
|
61
|
+
} else if (action === "parent") root.turnPage(-1)
|
|
62
|
+
else if (action === "pageForward") root.turnPage(1)
|
|
63
|
+
event.accepted = true
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
readonly property bool canRead: root.visible && root.pane !== null && !root.pane.listInFlight
|
|
67
|
+
overlayOpen: root.pane && root.pane.preview ? root.pane.preview.active : false
|
|
68
|
+
thumb: root.pane ? (root.manualHold ? Thumbs.fileFor(root.pane.thumbState, root.pane.cursorIndex)
|
|
69
|
+
: (root.loadedIndex >= 0 ? Thumbs.fileFor(root.pane.thumbState, root.loadedIndex) : "")) : ""
|
|
70
|
+
noThumbComing: root.row !== null && (root.row.t !== true || !root.pane
|
|
71
|
+
|| Thumbs.refused(root.pane.thumbState, root.loadedIndex))
|
|
72
|
+
// True from a move's clear until the next plain clear, the load's own; only then may the swap's fallback end, and its held-off mark show.
|
|
73
|
+
property bool moved: false
|
|
74
|
+
loadingHeldOff: root.swap !== null && root.swap.fellBack && !root.moved
|
|
75
|
+
|
|
76
|
+
// What the swap waits for: the cursor row's preview whole, or nothing more coming for it.
|
|
77
|
+
// A held frame is whole from the listing alone, cached thumbnail or not.
|
|
78
|
+
readonly property bool ready: {
|
|
79
|
+
if (root.manualHold)
|
|
80
|
+
return !root.pending
|
|
81
|
+
// A move's clear writes pending false and then true, and ready must not read true between the two: that ends the swap's fallback at every step of a held key.
|
|
82
|
+
if (root.pane === null || root.row === null)
|
|
83
|
+
return !root.pending && !root.moved
|
|
84
|
+
if (root.loadedIndex !== root.pane.cursorIndex)
|
|
85
|
+
return false
|
|
86
|
+
return PreviewSwap.columnReady({ state: root.previewState, thumb: root.thumb.length > 0,
|
|
87
|
+
frame: root.frameStatus, noThumbComing: root.noThumbComing, pdfDrawn: root.pdfDrawn,
|
|
88
|
+
pdfFailed: root.pdfFailed, linesLoading: root.textLoading, meta: root.meta !== null })
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ExtThumbs: how much of a text file this frame may read; the column passes it on.
|
|
92
|
+
textLimit: ExtThumbs.textLimit(root.pane ? root.pane.storageClass : "")
|
|
93
|
+
truncateText: root.pane ? (root.pane.storageClass === "network" || root.pane.storageClass === "phone") : false
|
|
94
|
+
|
|
95
|
+
function identity(row) {
|
|
96
|
+
return row ? JSON.stringify([row.n, row.s, row.m, row.p, row.i]) : ""
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function swapKey() { return root.pane.path + "\n" + root.pane.cursorIndex }
|
|
100
|
+
|
|
101
|
+
// The settle runs from the key, and a second call for the same row does not push it back.
|
|
102
|
+
function armSettle() {
|
|
103
|
+
if (!ViewState.previewAutomatic || !root.pane)
|
|
104
|
+
return
|
|
105
|
+
var key = root.swapKey()
|
|
106
|
+
if (settle.running && root.settleKey === key)
|
|
107
|
+
return
|
|
108
|
+
root.settleKey = key
|
|
109
|
+
root.settleTrails = false
|
|
110
|
+
settle.restart()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function clear() {
|
|
114
|
+
settle.stop()
|
|
115
|
+
root.moved = false
|
|
116
|
+
root.clearShown()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// A move's clear: the loading state stands under the swap's picture until the settle loads the new row.
|
|
120
|
+
function clearForMove() {
|
|
121
|
+
root.moved = true
|
|
122
|
+
root.clearShown()
|
|
123
|
+
root.pending = true
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// A folder or null row keeps the old preview: ColumnsArea holds the old column by data until the peek lands.
|
|
127
|
+
function replace() {
|
|
128
|
+
if (!Columns.isFileRow(root.pane ? root.pane.rowFor(root.pane.cursorIndex) : null)) { settle.stop(); return }
|
|
129
|
+
var key = root.swapKey()
|
|
130
|
+
// A true duplicate preserves the frame and timer; a changed identity lets a same-key refresh proceed.
|
|
131
|
+
// A queued load stands for the same key: clearing behind it would wipe the load when it lands.
|
|
132
|
+
if (key === root.lastMoveKey && (root.isShown() || root.loadQueuedKey === key)) return
|
|
133
|
+
// A burst's move for this key already waits on its settle, so a rows landing under it holds nothing again.
|
|
134
|
+
if (key === root.lastMoveKey && root.moved && settle.running && root.settleKey === key) return
|
|
135
|
+
if (root.swap && ViewState.previewAutomatic && root.canRead) {
|
|
136
|
+
// The picture first, the exact contract a deferred load keeps; the scheduler may load under it at once.
|
|
137
|
+
root.swap.hold(root.clearForMove, key)
|
|
138
|
+
root.settleFor(key)
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
if (!root.canRead) { root.clear(); return }
|
|
142
|
+
// No swap takes no picture, so the same scheduler decides after the same duplicate check above.
|
|
143
|
+
root.clear()
|
|
144
|
+
root.settleFor(key)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Whether the loaded frame already names the cursor row: the duplicate test the key clock cannot make.
|
|
148
|
+
function isShown() {
|
|
149
|
+
var current = root.pane.rowFor(root.pane.cursorIndex)
|
|
150
|
+
return root.loadedIndex === root.pane.cursorIndex && root.loadedDirectory === root.pane.path
|
|
151
|
+
&& root.loadedIdentity === root.identity(current)
|
|
152
|
+
&& root.loadedSelection === PreviewSettle.selectionKey(root.pane.selectionCount(), root.pane.selectionVersion)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Refresh-or-new scheduler: a pending timer already covers this key, otherwise the move clock decides.
|
|
156
|
+
function settleFor(key) {
|
|
157
|
+
if (!ViewState.previewAutomatic || !root.pane) return
|
|
158
|
+
var now = Date.now()
|
|
159
|
+
var decision = PreviewSettle.plan(now, root.lastMoveAt, settle.interval, key, root.lastMoveKey,
|
|
160
|
+
settle.running && root.settleKey === key, root.unrested(key))
|
|
161
|
+
if (decision === "same") return
|
|
162
|
+
root.lastMoveKey = key
|
|
163
|
+
root.lastMoveAt = now
|
|
164
|
+
if (decision === "now") {
|
|
165
|
+
settle.stop()
|
|
166
|
+
root.settleKey = key
|
|
167
|
+
root.fireSettle()
|
|
168
|
+
// Armed once the load's own work returns, work a starved loop can stretch past the settle.
|
|
169
|
+
rested.restart()
|
|
170
|
+
} else {
|
|
171
|
+
root.settleKey = key
|
|
172
|
+
root.settleTrails = true
|
|
173
|
+
settle.restart()
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function clearShown() {
|
|
178
|
+
root.loadQueuedKey = ""
|
|
179
|
+
root.loadedSelection = ""
|
|
180
|
+
root.pending = false
|
|
181
|
+
root.pendingToken = 0
|
|
182
|
+
root.manualHold = false
|
|
183
|
+
root.loadedIndex = -1
|
|
184
|
+
root.loadedDirectory = ""
|
|
185
|
+
root.loadedIdentity = ""
|
|
186
|
+
root.row = null
|
|
187
|
+
root.meta = null
|
|
188
|
+
root.path = ""
|
|
189
|
+
root.kindName = ""
|
|
190
|
+
root.selectionCount = 0
|
|
191
|
+
root.selectedRows = []
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function followSelection() {
|
|
195
|
+
if (!root.canRead) return
|
|
196
|
+
var candidate = root.pane.rowFor(root.pane.cursorIndex)
|
|
197
|
+
// Unknown is never held: the class has not named its verdict yet, so the frame waits
|
|
198
|
+
// under the swap's cap and onStorageKnownChanged decides when it lands, never a held mark.
|
|
199
|
+
if (!root.pane.storageKnown) {
|
|
200
|
+
if (root.swap) root.swap.start(false)
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
var hold = ExtThumbs.manualHold(root.pane.storageClass, ViewState.preview)
|
|
204
|
+
if (root.loadedDirectory === root.pane.path && root.loadedIndex === root.pane.cursorIndex
|
|
205
|
+
&& root.loadedIdentity === root.identity(candidate) && root.manualHold === hold) return
|
|
206
|
+
// A folder or null row keeps the old preview until its peek lands; clearing here would blank the held column.
|
|
207
|
+
if (!Columns.isFileRow(candidate)) { settle.stop(); return }
|
|
208
|
+
// An off class holds the frame on the listing's own facts until Ctrl+Space loads it; nothing loads, so no swap.
|
|
209
|
+
if (hold) { root.clear(); root.holdSelection(candidate); return }
|
|
210
|
+
root.replace()
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// The held frame: the row, its path and its facts from the listing alone, no meta and no
|
|
214
|
+
// thumbnail ask. The loaded identity is recorded so rows replies do not clear and re-hold.
|
|
215
|
+
// Space still opens Quick Look, which loads on request.
|
|
216
|
+
function holdSelection(candidate) {
|
|
217
|
+
if (!candidate || candidate.d) return
|
|
218
|
+
root.manualHold = true
|
|
219
|
+
root.loadedIndex = root.pane.cursorIndex
|
|
220
|
+
root.loadedDirectory = root.pane.path
|
|
221
|
+
root.loadedIdentity = root.identity(candidate)
|
|
222
|
+
root.row = Object.assign({}, candidate)
|
|
223
|
+
root.path = root.pane.join(Search.baseOf(root.pane), candidate.n)
|
|
224
|
+
root.kindName = root.pane.kindNames[candidate.k] || ""
|
|
225
|
+
root.selectionCount = root.pane.selectionCount()
|
|
226
|
+
root.loadedSelection = PreviewSettle.selectionKey(root.pane.selectionCount(), root.pane.selectionVersion)
|
|
227
|
+
root.selectedRows = root.pane.selectedIndices().map(function (index) { return root.pane.rowFor(index) }).filter(function (row) { return row !== null })
|
|
228
|
+
if (root.swap) root.swap.start(false)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Ctrl+Space calls this directly; automatic selection reaches it only after selection settles.
|
|
232
|
+
function loadSelection() {
|
|
233
|
+
if (!root.canRead) return
|
|
234
|
+
if (!Columns.isFileRow(root.pane.rowFor(root.pane.cursorIndex))) { settle.stop(); return }
|
|
235
|
+
var key = root.swapKey()
|
|
236
|
+
root.loadQueuedKey = key
|
|
237
|
+
if (root.swap) root.swap.hold(root.load, key, true)
|
|
238
|
+
else root.load()
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// The swap is told the new preview's work has begun, so its cap counts from here, a PDF's after its document settle.
|
|
242
|
+
function load() {
|
|
243
|
+
if (!root.canRead) return
|
|
244
|
+
var pane = root.pane
|
|
245
|
+
var current = pane.rowFor(pane.cursorIndex)
|
|
246
|
+
if (!Columns.isFileRow(current)) { settle.stop(); root.startSwap(false); return }
|
|
247
|
+
root.clear()
|
|
248
|
+
root.loadedIndex = pane.cursorIndex
|
|
249
|
+
root.loadedDirectory = pane.path
|
|
250
|
+
root.loadedIdentity = root.identity(current)
|
|
251
|
+
root.row = Object.assign({}, current)
|
|
252
|
+
root.path = pane.join(Search.baseOf(pane), current.n)
|
|
253
|
+
root.kindName = pane.kindNames[current.k] || ""
|
|
254
|
+
root.selectionCount = pane.selectionCount()
|
|
255
|
+
root.loadedSelection = PreviewSettle.selectionKey(pane.selectionCount(), pane.selectionVersion)
|
|
256
|
+
root.selectedRows = pane.selectedIndices().map(function (index) { return pane.rowFor(index) }).filter(function (row) { return row !== null })
|
|
257
|
+
if (root.selectionCount > 1) { root.startSwap(false); return }
|
|
258
|
+
var kind = Facts.state(current, 1, false, "", root.kindName)
|
|
259
|
+
root.startSwap(kind === Facts.PDF)
|
|
260
|
+
root.pendingToken = pane.backend.askMeta(root.loadedIndex, kind === Facts.TEXT || kind === Facts.CODE,
|
|
261
|
+
kind === Facts.VIDEO || kind === Facts.AUDIO, kind === Facts.ARCHIVE)
|
|
262
|
+
root.askThumb()
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// The single-row ask, held until the storage class lands: unknown reads as generating,
|
|
266
|
+
// so asking early would thumbnail a NAS image in full over the network.
|
|
267
|
+
function askThumb() {
|
|
268
|
+
var pane = root.pane
|
|
269
|
+
if (!pane || root.loadedIndex < 0) return
|
|
270
|
+
if (root.manualHold || !root.isShown()) return
|
|
271
|
+
var current = pane.rowFor(root.loadedIndex)
|
|
272
|
+
var work = PreviewAsk.workFor({ canRead: root.canRead, storageKnown: pane.storageKnown,
|
|
273
|
+
selectionCount: root.selectionCount, index: root.loadedIndex,
|
|
274
|
+
rowT: current ? !!current.t : false,
|
|
275
|
+
asked: pane.thumbState.file[root.loadedIndex] !== undefined,
|
|
276
|
+
storageClass: pane.storageClass, preview: ViewState.preview })
|
|
277
|
+
if (!work) return
|
|
278
|
+
root.thumbsApplied(work)
|
|
279
|
+
pane.backend.thumb(work.ask, work.cacheOnly)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Idle press asks the cursor thumb before the capture, so load's own ask sends nothing twice.
|
|
283
|
+
function askCursorThumb() {
|
|
284
|
+
if (!ViewState.previewAutomatic || !ViewState.previewColumn) return
|
|
285
|
+
var pane = root.pane
|
|
286
|
+
if (!pane) return
|
|
287
|
+
var current = pane.rowFor(pane.cursorIndex)
|
|
288
|
+
if (!Columns.isFileRow(current)) return
|
|
289
|
+
var key = root.swapKey()
|
|
290
|
+
var work = PreviewAsk.earlyWorkFor({ now: Date.now(), lastMoveAt: root.lastMoveAt,
|
|
291
|
+
interval: settle.interval, key: key, lastMoveKey: root.lastMoveKey,
|
|
292
|
+
armed: settle.running && root.settleKey === key, pending: root.unrested(key), canRead: root.canRead,
|
|
293
|
+
storageKnown: pane.storageKnown, selectionCount: pane.selectionCount(),
|
|
294
|
+
index: pane.cursorIndex, rowT: !!current.t,
|
|
295
|
+
asked: pane.thumbState.file[pane.cursorIndex] !== undefined,
|
|
296
|
+
storageClass: pane.storageClass, preview: ViewState.preview })
|
|
297
|
+
if (!work) return
|
|
298
|
+
root.thumbsApplied(work)
|
|
299
|
+
pane.backend.thumb(work.ask, work.cacheOnly)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function startSwap(isPdf) { if (root.swap) root.swap.start(isPdf) }
|
|
303
|
+
|
|
304
|
+
// The settle's whole decision, shared by the timer and the idle fast path, so both spend the same gates.
|
|
305
|
+
function fireSettle() {
|
|
306
|
+
if (!ViewState.previewAutomatic) return
|
|
307
|
+
var hold = root.pane ? (!root.pane.storageKnown || ExtThumbs.manualHold(root.pane.storageClass, ViewState.preview)) : false
|
|
308
|
+
if (hold) root.followSelection()
|
|
309
|
+
else root.loadSelection()
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// The cursor has not rested since its last move: an earlier row's move still trailing, or a load made at once whose rest has not run out.
|
|
313
|
+
function unrested(key) { return (settle.running && root.settleTrails && root.settleKey !== key) || rested.running }
|
|
314
|
+
// A single-shot Timer stays running until its trigger, a posted event, is handled, so a held key's next repeat that a starved loop
|
|
315
|
+
// handles before then has not rested, however late its own load work made it, and trails instead of loading at once again.
|
|
316
|
+
Timer { id: rested; interval: settle.interval }
|
|
317
|
+
Timer {
|
|
318
|
+
id: settle
|
|
319
|
+
interval: root.pane ? root.pane.settleMs : 120
|
|
320
|
+
// The automatic settle holds an off class; Ctrl+Space's direct loadSelection stays full.
|
|
321
|
+
// Unknown waits through followSelection, so the first settle never spends the class fsinfo has not named yet.
|
|
322
|
+
onTriggered: root.fireSettle()
|
|
323
|
+
}
|
|
324
|
+
onCanReadChanged: {
|
|
325
|
+
// A listing going out leaves the preview to Nav.forget's reset, which clears it when held rows go.
|
|
326
|
+
if (!root.canRead && (!root.visible || root.pane === null)) root.clear()
|
|
327
|
+
else if (root.canRead) root.followSelection()
|
|
328
|
+
}
|
|
329
|
+
Connections {
|
|
330
|
+
target: ViewState
|
|
331
|
+
function onPreviewAutomaticChanged() {
|
|
332
|
+
if (ViewState.previewAutomatic) root.followSelection()
|
|
333
|
+
else settle.stop()
|
|
334
|
+
}
|
|
335
|
+
// A class switch moves the frame between held and loaded without a cursor move.
|
|
336
|
+
function onStateChanged() { root.followSelection() }
|
|
337
|
+
}
|
|
338
|
+
Connections {
|
|
339
|
+
target: root.pane
|
|
340
|
+
// With a swap, ui/ColumnsArea.qml calls followSelection after it has put the hold in place.
|
|
341
|
+
function onCursorIndexChanged() { if (!root.swap) root.followSelection() }
|
|
342
|
+
// The class lands with fsinfo, after the rows; a settle fired in between spent local.
|
|
343
|
+
function onStorageClassChanged() { root.followSelection() }
|
|
344
|
+
// A frame loaded before the class lands never asked; the ask runs here, once the gate names it.
|
|
345
|
+
function onStorageKnownChanged() { root.followSelection(); root.askThumb() }
|
|
346
|
+
function onRowsChanged() {
|
|
347
|
+
if (root.loadedIndex >= 0 && root.loadedIdentity !== root.identity(root.pane.rowFor(root.loadedIndex)))
|
|
348
|
+
root.replace()
|
|
349
|
+
else if (!root.swap)
|
|
350
|
+
root.followSelection()
|
|
351
|
+
}
|
|
352
|
+
function onPathChanged() { root.replace() }
|
|
353
|
+
function onSelectionVersionChanged() { root.replace() }
|
|
354
|
+
}
|
|
355
|
+
Connections {
|
|
356
|
+
target: root.pane ? root.pane.backend : null
|
|
357
|
+
function onMetaResult(message) {
|
|
358
|
+
if (!root.canRead || !root.pendingToken || message.token !== root.pendingToken
|
|
359
|
+
|| root.loadedDirectory !== root.pane.path
|
|
360
|
+
|| root.loadedIdentity !== root.identity(root.pane.rowFor(root.loadedIndex))) return
|
|
361
|
+
root.pendingToken = 0
|
|
362
|
+
root.meta = { w: message.w, h: message.h, durationMs: message.ms, sampleRate: message.rate,
|
|
363
|
+
topEntries: typeof message.topEntries === "number" ? message.topEntries : -1, entries: message.entries, unpacked: message.unpacked, archiveFailed: message.afailed,
|
|
364
|
+
names: message.names, lines: message.lines, partial: message.partial,
|
|
365
|
+
linesFailed: message.lfailed, target: message.target, targetDir: message.targetdir,
|
|
366
|
+
owner: message.owner || "", orient: message.orient || 1, frames: message.frames || 0, photo: message.photo || null }
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
Component.onCompleted: root.followSelection()
|
|
370
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import QtQuick
|
|
2
|
+
import "." as Flea
|
|
3
|
+
import "js/Format.js" as Format
|
|
4
|
+
|
|
5
|
+
// Favourite records keep their exact label/path; only the drag handle initiates reordering.
|
|
6
|
+
Item {
|
|
7
|
+
id: root
|
|
8
|
+
property var row: ({})
|
|
9
|
+
signal activated()
|
|
10
|
+
signal moved(int to)
|
|
11
|
+
signal removed()
|
|
12
|
+
readonly property Item dragItem: grip
|
|
13
|
+
readonly property Item removeItem: remove
|
|
14
|
+
implicitHeight: Theme.railRowHeight
|
|
15
|
+
// The remove button's accessible name, filled by Format.fill.
|
|
16
|
+
readonly property string removeWord: "Remove %1"
|
|
17
|
+
|
|
18
|
+
Flea.Glyph {
|
|
19
|
+
id: icon
|
|
20
|
+
anchors.left: parent.left
|
|
21
|
+
anchors.leftMargin: Theme.spacing.rowPaddingX
|
|
22
|
+
anchors.verticalCenter: parent.verticalCenter
|
|
23
|
+
// Sidebar040: the Built in rows' own 19 px mark slot, so every label in the pane starts on one column.
|
|
24
|
+
width: Theme.markSize
|
|
25
|
+
height: width
|
|
26
|
+
name: root.row.glyph || "folder"
|
|
27
|
+
color: root.row.error ? Theme.color.error : Theme.color.mutedFrame
|
|
28
|
+
}
|
|
29
|
+
Text {
|
|
30
|
+
anchors.left: icon.right
|
|
31
|
+
anchors.leftMargin: Theme.spacing.gap
|
|
32
|
+
anchors.right: path.left
|
|
33
|
+
anchors.rightMargin: Theme.spacing.gap
|
|
34
|
+
anchors.verticalCenter: parent.verticalCenter
|
|
35
|
+
text: root.row.label || ""
|
|
36
|
+
font.family: Theme.font.family
|
|
37
|
+
font.pixelSize: Theme.font.body
|
|
38
|
+
color: root.row.error ? Theme.color.error : Theme.color.foreground
|
|
39
|
+
elide: Text.ElideRight
|
|
40
|
+
textFormat: Text.PlainText
|
|
41
|
+
}
|
|
42
|
+
Text {
|
|
43
|
+
id: path
|
|
44
|
+
anchors.right: grip.left
|
|
45
|
+
anchors.rightMargin: Theme.spacing.gap
|
|
46
|
+
anchors.verticalCenter: parent.verticalCenter
|
|
47
|
+
width: Math.min(implicitWidth, root.width * 0.46)
|
|
48
|
+
// Sidebar040: a favourite reads as the person writes it; row.value stays the stored path for every action.
|
|
49
|
+
text: root.row.display !== undefined ? root.row.display : (root.row.value || "")
|
|
50
|
+
font.family: Theme.font.family
|
|
51
|
+
font.pixelSize: Theme.font.caption
|
|
52
|
+
color: root.row.error ? Theme.color.error : Theme.color.muted
|
|
53
|
+
elide: Text.ElideMiddle
|
|
54
|
+
textFormat: Text.PlainText
|
|
55
|
+
}
|
|
56
|
+
// SettingsRest rule 4: an action that acts on one row reads as a mark on that row, where a button
|
|
57
|
+
// under the list leaves its target to be inferred from a cursor somewhere above it.
|
|
58
|
+
// Sidebar040: the handle and the remove mark draw at the mark size inside flush 24 px boxes.
|
|
59
|
+
Item {
|
|
60
|
+
id: remove
|
|
61
|
+
anchors.right: parent.right
|
|
62
|
+
anchors.rightMargin: Theme.spacing.rowPaddingX
|
|
63
|
+
anchors.verticalCenter: parent.verticalCenter
|
|
64
|
+
width: Theme.hitMin
|
|
65
|
+
height: Theme.hitMin
|
|
66
|
+
Accessible.role: Accessible.Button
|
|
67
|
+
Accessible.name: Format.fill(root.removeWord, root.row.label || "")
|
|
68
|
+
Accessible.onPressAction: root.removed()
|
|
69
|
+
HoverHandler { cursorShape: Qt.PointingHandCursor }
|
|
70
|
+
TapHandler {
|
|
71
|
+
acceptedButtons: Qt.LeftButton
|
|
72
|
+
gesturePolicy: TapHandler.ReleaseWithinBounds
|
|
73
|
+
onTapped: root.removed()
|
|
74
|
+
}
|
|
75
|
+
Flea.Glyph {
|
|
76
|
+
anchors.centerIn: parent
|
|
77
|
+
width: Theme.markSize
|
|
78
|
+
height: Theme.markSize
|
|
79
|
+
name: "x"
|
|
80
|
+
color: Theme.color.mutedFrame
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
Item {
|
|
85
|
+
id: grip
|
|
86
|
+
anchors.right: remove.left
|
|
87
|
+
anchors.rightMargin: 0
|
|
88
|
+
anchors.verticalCenter: parent.verticalCenter
|
|
89
|
+
width: Theme.hitMin
|
|
90
|
+
height: Theme.hitMin
|
|
91
|
+
Flea.Glyph {
|
|
92
|
+
anchors.centerIn: parent
|
|
93
|
+
width: Theme.markSize
|
|
94
|
+
height: Theme.markSize
|
|
95
|
+
name: "list"
|
|
96
|
+
color: Theme.color.mutedFrame
|
|
97
|
+
}
|
|
98
|
+
DragHandler {
|
|
99
|
+
id: drag
|
|
100
|
+
target: null
|
|
101
|
+
xAxis.enabled: false
|
|
102
|
+
property real startY: 0
|
|
103
|
+
onActiveChanged: {
|
|
104
|
+
if (active) { startY = persistentTranslation.y; return }
|
|
105
|
+
// A pin is a favourite-shaped row ordered inside the shelf's own pile, so the drag
|
|
106
|
+
// counts rows in whichever list this one came from.
|
|
107
|
+
var from = root.row.pinIndex !== undefined ? root.row.pinIndex : root.row.favouriteIndex
|
|
108
|
+
var last = (root.row.pinIndex !== undefined ? root.row.pinCount : Favourites.records.length) - 1
|
|
109
|
+
// Qt clears active translation before this release callback.
|
|
110
|
+
var to = Math.max(0, Math.min(last,
|
|
111
|
+
from + Math.round((persistentTranslation.y - startY) / Theme.railRowHeight)))
|
|
112
|
+
if (to !== from) root.moved(to)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
TapHandler {
|
|
117
|
+
// Exclusive on press so the listing row under the card cannot tap as well; the remove mark
|
|
118
|
+
// owns its corner, so the row still reads where the press landed before it opens anything.
|
|
119
|
+
gesturePolicy: TapHandler.ReleaseWithinBounds
|
|
120
|
+
onTapped: function (point) { if (point.position.x < remove.x) root.activated() }
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import QtQuick
|
|
2
|
+
import "." as Flea
|
|
3
|
+
|
|
4
|
+
// One settings heading. Where the group has a master, the heading is also that master's control:
|
|
5
|
+
// SettingsMenus rule 3 puts the tri-state box and the group's own enabled count on the heading it
|
|
6
|
+
// governs, so neither is a row of its own and every group answers the question it raises.
|
|
7
|
+
Item {
|
|
8
|
+
id: root
|
|
9
|
+
|
|
10
|
+
// {label, master, state, value} from ui/js/Settings.js, the group row itself.
|
|
11
|
+
property var row: ({})
|
|
12
|
+
property bool first: false
|
|
13
|
+
|
|
14
|
+
// Settings headings scale their resolved 12/15/4/8 insets once from the boards' bodySmall 13 anchor.
|
|
15
|
+
readonly property real insetScale: Theme.font.bodySmall / 13
|
|
16
|
+
readonly property real gap: root.first ? 0 : Math.round(8 * root.insetScale)
|
|
17
|
+
readonly property real paddingTop: Math.round((root.first ? 12 : 15) * root.insetScale)
|
|
18
|
+
readonly property real paddingBottom: Math.round(4 * root.insetScale)
|
|
19
|
+
readonly property bool hasMaster: root.row.master === true
|
|
20
|
+
// SettingsRest rule 3: a group whose one action governs the whole list carries it here instead.
|
|
21
|
+
readonly property bool hasAction: (root.row.action || "") !== ""
|
|
22
|
+
|
|
23
|
+
// Where the heading's own ink starts, under the rule and the padding above its label.
|
|
24
|
+
readonly property real inkTop: label.y
|
|
25
|
+
implicitHeight: label.y + Math.max(label.height, root.hasMaster || root.hasAction ? box.implicitHeight : 0) + root.paddingBottom
|
|
26
|
+
|
|
27
|
+
Text {
|
|
28
|
+
id: label
|
|
29
|
+
anchors.left: parent.left
|
|
30
|
+
anchors.leftMargin: Theme.spacing.rowPaddingX
|
|
31
|
+
y: root.gap + (root.first ? 0 : Theme.spacing.hairline) + root.paddingTop
|
|
32
|
+
height: Theme.font.caption * 1.6 + topPadding
|
|
33
|
+
topPadding: Math.ceil(font.pixelSize * 0.15)
|
|
34
|
+
verticalAlignment: Text.AlignVCenter
|
|
35
|
+
text: root.row.label || ""
|
|
36
|
+
color: Theme.color.muted
|
|
37
|
+
font.family: Theme.font.family
|
|
38
|
+
font.pixelSize: Theme.font.caption
|
|
39
|
+
font.bold: true
|
|
40
|
+
// The canvas sets every group eyebrow in small caps, the same treatment the rail's own headings take.
|
|
41
|
+
font.capitalization: Font.AllUppercase
|
|
42
|
+
font.letterSpacing: Theme.font.caption * 0.14
|
|
43
|
+
textFormat: Text.PlainText
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
Row {
|
|
47
|
+
visible: root.hasAction
|
|
48
|
+
anchors.right: parent.right
|
|
49
|
+
anchors.rightMargin: Theme.spacing.rowPaddingX
|
|
50
|
+
anchors.verticalCenter: label.verticalCenter
|
|
51
|
+
spacing: Theme.spacing.gap - Theme.spacing.hairline
|
|
52
|
+
|
|
53
|
+
Flea.Glyph {
|
|
54
|
+
anchors.verticalCenter: parent.verticalCenter
|
|
55
|
+
width: Theme.font.caption
|
|
56
|
+
height: width
|
|
57
|
+
name: "plus"
|
|
58
|
+
color: Theme.color.foreground
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
Text {
|
|
62
|
+
height: box.implicitHeight
|
|
63
|
+
verticalAlignment: Text.AlignVCenter
|
|
64
|
+
text: root.row.value || ""
|
|
65
|
+
color: Theme.color.foreground
|
|
66
|
+
font.family: Theme.font.family
|
|
67
|
+
font.pixelSize: Theme.font.caption
|
|
68
|
+
textFormat: Text.PlainText
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
Row {
|
|
73
|
+
visible: root.hasMaster
|
|
74
|
+
anchors.right: parent.right
|
|
75
|
+
anchors.rightMargin: Theme.spacing.rowPaddingX
|
|
76
|
+
anchors.verticalCenter: label.verticalCenter
|
|
77
|
+
spacing: Theme.spacing.gap
|
|
78
|
+
|
|
79
|
+
Text {
|
|
80
|
+
height: box.implicitHeight
|
|
81
|
+
verticalAlignment: Text.AlignVCenter
|
|
82
|
+
text: root.row.value || ""
|
|
83
|
+
color: Theme.color.muted
|
|
84
|
+
font.family: Theme.font.family
|
|
85
|
+
font.pixelSize: Theme.font.caption
|
|
86
|
+
font.letterSpacing: Theme.font.caption * 0.14
|
|
87
|
+
textFormat: Text.PlainText
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// The tri-state the group reads: every id on is a tick, some on a bar, none an empty box.
|
|
91
|
+
Flea.CheckBox {
|
|
92
|
+
id: box
|
|
93
|
+
value: root.row.state === "all" ? "on" : (root.row.state === "some" ? "some" : "off")
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import QtQuick
|
|
2
|
+
|
|
3
|
+
// A settings hint's type: the caption face, and for a board hint the board's line box of 1.5 times the caption, so a wrapped hint keeps the board's rhythm; the pane's footer line keeps the font's own line.
|
|
4
|
+
Text {
|
|
5
|
+
property bool footer: false
|
|
6
|
+
|
|
7
|
+
font.family: Theme.font.family
|
|
8
|
+
font.pixelSize: Theme.font.caption
|
|
9
|
+
textFormat: Text.PlainText
|
|
10
|
+
lineHeightMode: footer ? Text.ProportionalHeight : Text.FixedHeight
|
|
11
|
+
lineHeight: footer ? 1 : SettingsMetrics.hintLine
|
|
12
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
pragma Singleton
|
|
2
|
+
|
|
3
|
+
import Quickshell
|
|
4
|
+
import QtQuick
|
|
5
|
+
|
|
6
|
+
// The Settings boards' row geometry that CSS derives instead of stating. The boards are drawn at base-size 14, where bodySmall is 13 and the caption 12, so each figure scales with that token as Theme.settings.railPaddingY does.
|
|
7
|
+
Singleton {
|
|
8
|
+
id: root
|
|
9
|
+
|
|
10
|
+
// Tabs040.html data-flea="hint": padding 4px 14px 2px 42px, 12px type, line-height 1.5.
|
|
11
|
+
readonly property int boardHintTop: 4
|
|
12
|
+
readonly property int boardHintBottom: 2
|
|
13
|
+
readonly property real boardHintLineRatio: 1.5
|
|
14
|
+
readonly property int boardBodySmall: 13
|
|
15
|
+
|
|
16
|
+
readonly property int hintTop: Math.round(root.boardHintTop * Theme.font.bodySmall / root.boardBodySmall)
|
|
17
|
+
readonly property int hintBottom: Math.round(root.boardHintBottom * Theme.font.bodySmall / root.boardBodySmall)
|
|
18
|
+
readonly property int hintLine: Math.round(Theme.font.caption * root.boardHintLineRatio)
|
|
19
|
+
|
|
20
|
+
FontMetrics { id: bodyFace; font.family: Theme.font.family; font.pixelSize: Theme.font.body }
|
|
21
|
+
FontMetrics { id: captionFace; font.family: Theme.font.family; font.pixelSize: Theme.font.caption }
|
|
22
|
+
|
|
23
|
+
// A browser puts half of a line's leftover above its text, where a fixed Qt line puts all of it below, so the text box starts this far inside the band and the band's own bottom pad shrinks by it.
|
|
24
|
+
readonly property int hintLead: Math.round((root.hintLine - Math.round(captionFace.ascent) - Math.round(captionFace.descent)) / 2)
|
|
25
|
+
|
|
26
|
+
// A browser centres each line box on the row with its ascent and descent each rounded to whole pixels, so a baseline sits half of ascent less descent below the centre; the caption's baseline therefore sits this far from its label's, 1 px above at 14 over 12 (ClickAndRefresh F7).
|
|
27
|
+
readonly property int captionShift: Math.round(((Math.round(captionFace.ascent) - Math.round(captionFace.descent))
|
|
28
|
+
- (Math.round(bodyFace.ascent) - Math.round(bodyFace.descent))) / 2)
|
|
29
|
+
}
|