buninu-browser 0.2.4 → 0.4.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/LICENSES/jsmdcui.txt +22 -0
- package/README.md +102 -2
- package/frontends/wayland/LICENSE-land +21 -0
- package/frontends/wayland/README.md +123 -0
- package/frontends/wayland/casty/LICENSE +21 -0
- package/frontends/wayland/casty/bookmarks.js +32 -0
- package/frontends/wayland/casty/click.js +96 -0
- package/frontends/wayland/casty/commands.js +61 -0
- package/frontends/wayland/casty/config.js +45 -0
- package/frontends/wayland/casty/download-directory.js +28 -0
- package/frontends/wayland/casty/download.js +49 -0
- package/frontends/wayland/casty/hints.js +334 -0
- package/frontends/wayland/casty/keys.js +56 -0
- package/frontends/wayland/casty/page-commands.js +80 -0
- package/frontends/wayland/casty/prompt-width.js +148 -0
- package/frontends/wayland/casty/urlbar.js +606 -0
- package/frontends/wayland/client.js +88 -0
- package/frontends/wayland/clipboard.js +80 -0
- package/frontends/wayland/index.js +116 -0
- package/frontends/wayland/input.js +56 -0
- package/frontends/wayland/protocol.js +55 -0
- package/frontends/wayland/protocols/wayland.xml +3248 -0
- package/frontends/wayland/protocols/xdg-shell.xml +1415 -0
- package/frontends/wayland/transport.js +120 -0
- package/frontends/wayland/urlbar.js +158 -0
- package/index.js +28 -1
- package/lib/clipboard-command.js +10 -0
- package/lib/clipboard-powershell.js +22 -0
- package/lib/clipboard.js +57 -0
- package/lib/document-types.js +11 -0
- package/lib/headless-shell.js +78 -13
- package/lib/layout/text-layout.js +17 -9
- package/lib/paint/display-list.js +5 -1
- package/lib/paint/frame.js +35 -3
- package/lib/paint/raw-frames.js +47 -0
- package/lib/qml/README.md +971 -0
- package/lib/qml/builtins.js +700 -0
- package/lib/qml/canvas-dom.js +115 -0
- package/lib/qml/document.js +1176 -0
- package/lib/qml/format.js +9 -0
- package/lib/qml/host.js +324 -0
- package/lib/qml/images.js +199 -0
- package/lib/qml/launcher.js +132 -0
- package/lib/qml/loader.js +517 -0
- package/lib/qml/media-renderer.js +49 -0
- package/lib/qml/media.js +86 -0
- package/lib/qml/models.js +622 -0
- package/lib/qml/parser.js +381 -0
- package/lib/qml/qs +5 -0
- package/lib/qml/qscasty +3 -0
- package/lib/qml/qsheadless +3 -0
- package/lib/qml/qswayland +3 -0
- package/lib/qml/reactive.js +159 -0
- package/lib/qml/runtime.js +3381 -0
- package/lib/qml/shapes.js +243 -0
- package/lib/qml/text.js +109 -0
- package/lib/qml/textures.js +123 -0
- package/lib/qml/wayland-clipboard.js +32 -0
- package/lib/qml/worker-thread.js +70 -0
- package/lib/render-tree/index.js +16 -1
- package/lib/renderer/bindings.js +11 -0
- package/lib/renderer/canvas-2d.js +13 -0
- package/lib/renderer/compositor-channel.js +103 -0
- package/lib/renderer/compositor.js +25 -33
- package/lib/renderer/flags.js +1 -1
- package/lib/renderer/host.js +128 -92
- package/lib/renderer/main.js +38 -4
- package/lib/renderer/page-realm.js +32 -1
- package/lib/renderer/page-renderer.js +250 -21
- package/lib/renderer/text-input-paint.js +54 -0
- package/lib/renderer/text-input.js +57 -0
- package/lib/style/computed-style.js +2 -2
- package/package.json +1 -1
- package/test/clipboard.test.js +54 -0
- package/test/fixtures/browser-raw-demo.html +36 -0
- package/test/fixtures/flea-helpers/Dockerfile +36 -0
- package/test/fixtures/flea-helpers/bat.mjs +43 -0
- package/test/fixtures/flea-helpers/bin/bat +3 -0
- package/test/fixtures/flea-helpers/bin/bwrap +10 -0
- package/test/fixtures/flea-helpers/bin/bwrap-real-filter +9 -0
- package/test/fixtures/flea-helpers/bin/glow +3 -0
- package/test/fixtures/flea-helpers/bin/qs +3 -0
- package/test/fixtures/flea-helpers/bin/unsupported +3 -0
- package/test/fixtures/flea-helpers/check-highlighting.mjs +47 -0
- package/test/fixtures/flea-helpers/check-media.mjs +29 -0
- package/test/fixtures/flea-helpers/check-preview.mjs +27 -0
- package/test/fixtures/flea-helpers/check.mjs +36 -0
- package/test/fixtures/flea-helpers/highlight-cases.json +227 -0
- package/test/fixtures/flea-helpers/source-mime.xml +28 -0
- package/test/fixtures/flea-helpers/syntax-compat.mjs +23 -0
- package/test/fixtures/flea-thumbnail-stub/flea-placeholder.thumbnailer +4 -0
- package/test/fixtures/flea-thumbnail-stub/flea-thumbnail-stub +3 -0
- package/test/fixtures/qml/flea/EscapeHint.qml +11 -0
- package/test/fixtures/qml/flea/LICENSE +21 -0
- package/test/fixtures/qml/flea/README.md +25 -0
- package/test/fixtures/qml/flea/Theme.qml +8 -0
- package/test/fixtures/qml/flea/demo.qml +24 -0
- package/test/fixtures/qml/flea/qmldir +3 -0
- package/test/fixtures/qml/flea-theme/LICENSE-Flea +21 -0
- package/test/fixtures/qml/flea-theme/LICENSE-Omarchy +20 -0
- package/test/fixtures/qml/flea-theme/README.md +70 -0
- package/test/fixtures/qml/flea-theme/button.qml +17 -0
- package/test/fixtures/qml/flea-theme/demo.qml +27 -0
- package/test/fixtures/qml/flea-theme/field.qml +21 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/BorderGeometry.js +373 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Color.qml +258 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/I18n.qml +118 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Numbers.js +19 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Plural.js +91 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Style.qml +561 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Util.qml +188 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/qmldir +13 -0
- package/test/fixtures/qml/flea-theme/segment.qml +17 -0
- package/test/fixtures/qml/flea-theme/settings.qml +58 -0
- package/test/fixtures/qml/flea-theme/text-metrics.qml +10 -0
- package/test/fixtures/qml/flea-theme/ui/DialogButton.qml +150 -0
- package/test/fixtures/qml/flea-theme/ui/DialogField.qml +147 -0
- package/test/fixtures/qml/flea-theme/ui/EscapeHint.qml +11 -0
- package/test/fixtures/qml/flea-theme/ui/Glass.qml +114 -0
- package/test/fixtures/qml/flea-theme/ui/Glyph.qml +54 -0
- package/test/fixtures/qml/flea-theme/ui/GlyphSolid.qml +22 -0
- package/test/fixtures/qml/flea-theme/ui/Reveal.qml +54 -0
- package/test/fixtures/qml/flea-theme/ui/SettingsRuler.qml +85 -0
- package/test/fixtures/qml/flea-theme/ui/SettingsSegment.qml +85 -0
- package/test/fixtures/qml/flea-theme/ui/StableModel.qml +89 -0
- package/test/fixtures/qml/flea-theme/ui/Theme.qml +460 -0
- package/test/fixtures/qml/flea-theme/ui/ThemeColors.qml +43 -0
- package/test/fixtures/qml/flea-theme/ui/ViewState.qml +565 -0
- package/test/fixtures/qml/flea-theme/ui/js/Anchor.js +379 -0
- package/test/fixtures/qml/flea-theme/ui/js/AnchorHold.js +162 -0
- package/test/fixtures/qml/flea-theme/ui/js/Archive.js +157 -0
- package/test/fixtures/qml/flea-theme/ui/js/BackReturn.js +110 -0
- package/test/fixtures/qml/flea-theme/ui/js/Buttons.js +54 -0
- package/test/fixtures/qml/flea-theme/ui/js/ColumnMenu.js +111 -0
- package/test/fixtures/qml/flea-theme/ui/js/Columns.js +298 -0
- package/test/fixtures/qml/flea-theme/ui/js/Contrast.js +157 -0
- package/test/fixtures/qml/flea-theme/ui/js/Density.js +44 -0
- package/test/fixtures/qml/flea-theme/ui/js/DirSizes.js +129 -0
- package/test/fixtures/qml/flea-theme/ui/js/ExtThumbs.js +115 -0
- package/test/fixtures/qml/flea-theme/ui/js/Filter.js +284 -0
- package/test/fixtures/qml/flea-theme/ui/js/FolderSorts.js +108 -0
- package/test/fixtures/qml/flea-theme/ui/js/Format.js +305 -0
- package/test/fixtures/qml/flea-theme/ui/js/Icons.js +206 -0
- package/test/fixtures/qml/flea-theme/ui/js/Input.js +39 -0
- package/test/fixtures/qml/flea-theme/ui/js/KeyName.js +64 -0
- package/test/fixtures/qml/flea-theme/ui/js/Keymap.js +370 -0
- package/test/fixtures/qml/flea-theme/ui/js/Kinds.js +145 -0
- package/test/fixtures/qml/flea-theme/ui/js/Lists.js +11 -0
- package/test/fixtures/qml/flea-theme/ui/js/MakeDefault.js +186 -0
- package/test/fixtures/qml/flea-theme/ui/js/Match.js +27 -0
- package/test/fixtures/qml/flea-theme/ui/js/Material.js +109 -0
- package/test/fixtures/qml/flea-theme/ui/js/Menu.js +513 -0
- package/test/fixtures/qml/flea-theme/ui/js/Motion.js +16 -0
- package/test/fixtures/qml/flea-theme/ui/js/Mounts.js +320 -0
- package/test/fixtures/qml/flea-theme/ui/js/MouseNav.js +13 -0
- package/test/fixtures/qml/flea-theme/ui/js/Nav.js +328 -0
- package/test/fixtures/qml/flea-theme/ui/js/Palette.js +33 -0
- package/test/fixtures/qml/flea-theme/ui/js/Places.js +236 -0
- package/test/fixtures/qml/flea-theme/ui/js/Protocols.js +296 -0
- package/test/fixtures/qml/flea-theme/ui/js/RecentMode.js +154 -0
- package/test/fixtures/qml/flea-theme/ui/js/Search.js +396 -0
- package/test/fixtures/qml/flea-theme/ui/js/Settings.js +476 -0
- package/test/fixtures/qml/flea-theme/ui/js/SettingsAbout.js +99 -0
- package/test/fixtures/qml/flea-theme/ui/js/SettingsShelf.js +52 -0
- package/test/fixtures/qml/flea-theme/ui/js/Sort.js +127 -0
- package/test/fixtures/qml/flea-theme/ui/js/StateWords.js +42 -0
- package/test/fixtures/qml/flea-theme/ui/js/Swap.js +166 -0
- package/test/fixtures/qml/flea-theme/ui/js/TextSize.js +107 -0
- package/test/fixtures/qml/flea-theme/ui/js/Thumbs.js +158 -0
- package/test/fixtures/qml/flea-theme/ui/js/UiState.js +438 -0
- package/test/fixtures/qml/flea-theme/ui/js/Update.js +132 -0
- package/test/fixtures/qml/flea-theme/ui/js/UserDirs.js +56 -0
- package/test/fixtures/qml/flea-theme/ui/qmldir +227 -0
- package/test/fixtures/qml/hello.qml +26 -0
- package/test/fixtures/qml/imports/Demo/Toolkit/VersionLabel.qml +2 -0
- package/test/fixtures/qml/imports/Demo/Toolkit/VersionLabelNew.qml +2 -0
- package/test/fixtures/qml/imports/Demo/Toolkit/qmldir +3 -0
- package/test/fixtures/qml/imports/components/Badge.qml +15 -0
- package/test/fixtures/qml/imports/logic/base.js +2 -0
- package/test/fixtures/qml/imports/logic/counter.js +4 -0
- package/test/fixtures/qml/imports/logic/math.js +3 -0
- package/test/fixtures/qml/imports/logic/shared.js +3 -0
- package/test/fixtures/qml/imports/main.qml +21 -0
- package/test/fixtures/qml/imports/theme/Theme.qml +8 -0
- package/test/fixtures/qml/imports/theme/qmldir +2 -0
- package/test/fixtures/qml/multiply.qml +61 -0
- package/test/fixtures/qml-canvas-dom.js +90 -0
- package/test/fixtures/qml-media-audio.js +39 -0
- package/test/fixtures/raw-capture-benchmark.json +76 -0
- package/test/helpers/flea-sandbox.js +48 -0
- package/test/helpers/quickshell-fixture.js +125 -0
- package/test/html-clipboard-wayland.test.js +47 -0
- package/test/index.test.js +13 -0
- package/test/manual-casty-without-qml.js +87 -0
- package/test/manual-flea-application.js +49 -0
- package/test/manual-flea-binary.js +62 -0
- package/test/manual-flea-qml.js +9 -0
- package/test/manual-flea-settings.js +40 -0
- package/test/manual-flea-theme.js +40 -0
- package/test/manual-qml-removal.js +46 -0
- package/test/manual-without-qml.js +45 -0
- package/test/qml-canvas-dom.test.js +37 -0
- package/test/qml-clipboard-bridge.test.js +25 -0
- package/test/qml-clipboard-wayland.test.js +40 -0
- package/test/qml-connections-timing.test.js +73 -0
- package/test/qml-dynamic.test.js +401 -0
- package/test/qml-flea-app.test.js +146 -0
- package/test/qml-flea-backend.test.js +78 -0
- package/test/qml-flea-columns-backend.test.js +38 -0
- package/test/qml-flea-compile.test.js +78 -0
- package/test/qml-flea-launcher.test.js +88 -0
- package/test/qml-flea-markdown-backend.test.js +31 -0
- package/test/qml-flea-media.test.js +28 -0
- package/test/qml-flea-navigation-backend.test.js +35 -0
- package/test/qml-flea-navigation-timing.test.js +147 -0
- package/test/qml-flea-preview-backend.test.js +53 -0
- package/test/qml-flea-previews.test.js +163 -0
- package/test/qml-flea-rows.test.js +101 -0
- package/test/qml-flea-textures.test.js +59 -0
- package/test/qml-flea-view-switch.test.js +33 -0
- package/test/qml-flea-wayland.test.js +68 -0
- package/test/qml-flea.test.js +34 -0
- package/test/qml-host.test.js +238 -0
- package/test/qml-imports.test.js +102 -0
- package/test/qml-inherited-handlers.test.js +51 -0
- package/test/qml-input-selection.test.js +66 -0
- package/test/qml-interactions.test.js +309 -0
- package/test/qml-language.test.js +167 -0
- package/test/qml-launcher.test.js +54 -0
- package/test/qml-layout-text.test.js +205 -0
- package/test/qml-media.test.js +63 -0
- package/test/qml-persisted-delegates.test.js +45 -0
- package/test/qml-quickshell-controls.test.js +65 -0
- package/test/qml-quickshell-settings.test.js +62 -0
- package/test/qml-repeater.test.js +72 -0
- package/test/qml-style-updates.test.js +124 -0
- package/test/qml-textures.test.js +66 -0
- package/test/qml-views.test.js +332 -0
- package/test/qml-worker.test.js +69 -0
- package/test/qml.test.js +83 -0
- package/test/raw-capture.test.js +152 -0
- package/test/renderer-host.test.js +11 -0
- package/test/text-input-pointer.test.js +31 -0
- package/test/text-input-scroll.test.js +29 -0
- package/test/text-input.test.js +47 -0
- package/test/wayland-frontend.test.js +83 -0
- package/test/wayland-urlbar.test.js +38 -0
- package/test/window-resize.test.js +36 -0
- package/tools/bench-raw-capture.js +32 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
.import "BackReturn.js" as BackReturn
|
|
4
|
+
.import "DirSizes.js" as DirSizes
|
|
5
|
+
.import "Nav.js" as Nav
|
|
6
|
+
.import "Search.js" as Search
|
|
7
|
+
.import "Thumbs.js" as Thumbs
|
|
8
|
+
|
|
9
|
+
// Recent lists the history's paths under "/", newest first via mtime desc.
|
|
10
|
+
var OFF = ""
|
|
11
|
+
var RESULTS = "results"
|
|
12
|
+
|
|
13
|
+
// One refusal for both paste routes: the menu reaches past key dispatch, so it shares this.
|
|
14
|
+
function refusePaste(pane) {
|
|
15
|
+
if (pane.recentMode.length > 0) {
|
|
16
|
+
pane.message("Recent cannot take a paste. Open a folder first", false)
|
|
17
|
+
return true
|
|
18
|
+
}
|
|
19
|
+
return false
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Open keeps where the pane stood and asks the history's paths after the bounded jump; spanning mounts leaves fsinfo unknown.
|
|
23
|
+
function run(pane, paths, visits) {
|
|
24
|
+
// The history takes the place of a listing out; its answer is dropped by the folder it names, see ui/js/Swap.js onListed.
|
|
25
|
+
if (pane.listInFlight) {
|
|
26
|
+
pane.swap.stopWaiting()
|
|
27
|
+
BackReturn.dropOwed(pane)
|
|
28
|
+
}
|
|
29
|
+
// The standing order is kept for the way back, not the mtime it replaces.
|
|
30
|
+
if (pane.recentMode.length === 0 && pane.backend) {
|
|
31
|
+
pane.recentSortBy = pane.backend.sortBy
|
|
32
|
+
pane.recentSortDesc = pane.backend.sortDesc === true
|
|
33
|
+
}
|
|
34
|
+
if (pane.recentFrom.length === 0) {
|
|
35
|
+
pane.recentFrom = Nav.place(pane)
|
|
36
|
+
}
|
|
37
|
+
// A history replaces the standing listing, so a live session closes first.
|
|
38
|
+
Search.leaveWalk(pane)
|
|
39
|
+
// The paths this listing stands on, so a refresh with the rail hidden still re-asks them.
|
|
40
|
+
pane.recentPaths = paths || []
|
|
41
|
+
pane.recentVisits = visits || ({})
|
|
42
|
+
pane.listInFlight = true
|
|
43
|
+
pane.listedSeen = false
|
|
44
|
+
pane.path = "/"
|
|
45
|
+
pane.recentMode = RESULTS
|
|
46
|
+
pane.listingPath = "/"
|
|
47
|
+
pane.storageClass = ""
|
|
48
|
+
// Recent rows span filesystems, so the Tags row stays and the backend refuses each filesystem that cannot hold tags by name.
|
|
49
|
+
pane.canTag = true
|
|
50
|
+
pane.fsName = ""
|
|
51
|
+
pane.fsFree = 0
|
|
52
|
+
// Recent asks for no figures, so its empty slot is an answer.
|
|
53
|
+
pane.fsAnswered = true
|
|
54
|
+
var ask = {}
|
|
55
|
+
// A walk closed above keeps its rows and its strip until the history lands.
|
|
56
|
+
if (pane.searchLeaving === true) ask.holdWalk = true
|
|
57
|
+
Nav.holdOrForget(pane, ask)
|
|
58
|
+
pane.appliedListingPreferences = pane.listingPreferences
|
|
59
|
+
pane.backend.listPaths(pane.recentPaths, pane.windowSize)
|
|
60
|
+
// A deep anchored refresh asks its window only after the history's listpaths request.
|
|
61
|
+
var anchor = pane.wire ? pane.wire.anchor : null
|
|
62
|
+
if (anchor && anchor.path === pane.path && anchor.start > 0)
|
|
63
|
+
pane.backend.window(anchor.start, pane.windowSize)
|
|
64
|
+
pane.backend.sortBy = "mtime"
|
|
65
|
+
pane.backend.sortDesc = true
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Leaving Recent re-lists the folder it was opened over, pushing no history entry.
|
|
69
|
+
function close(pane) {
|
|
70
|
+
var back = pane.recentFrom.length > 0 ? pane.recentFrom : "/"
|
|
71
|
+
leave(pane)
|
|
72
|
+
pane.openWithoutHistory(back, { supersede: true })
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// The order run() replaced, handed back before the folder re-lists.
|
|
76
|
+
function restoreSort(pane) {
|
|
77
|
+
if (pane.backend && typeof pane.recentSortBy === "string" && pane.recentSortBy.length > 0) {
|
|
78
|
+
pane.backend.sortBy = pane.recentSortBy
|
|
79
|
+
pane.backend.sortDesc = pane.recentSortDesc === true
|
|
80
|
+
}
|
|
81
|
+
pane.recentSortBy = ""
|
|
82
|
+
pane.recentSortDesc = false
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Every way out of Recent, a plain hop, Escape, a tab switch and reveal, leaves through this one step, so each restores the same order.
|
|
86
|
+
function leave(pane) {
|
|
87
|
+
if (pane.recentMode.length > 0) {
|
|
88
|
+
restoreSort(pane)
|
|
89
|
+
}
|
|
90
|
+
pane.recentMode = OFF
|
|
91
|
+
pane.recentFrom = ""
|
|
92
|
+
pane.recentPaths = []
|
|
93
|
+
pane.recentVisits = ({})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Refresh re-reads history after an operation, never re-listing "/" over it; an unloaded sidebar re-asks the standing paths.
|
|
97
|
+
function refresh(pane, selectPath) {
|
|
98
|
+
// A re-ask of the history is no navigation, so it never takes the place of a listing out.
|
|
99
|
+
if (pane.listInFlight) {
|
|
100
|
+
pane.message("A directory is already loading.", false)
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
pane.pendingSelect = selectPath ? selectPath : ""
|
|
104
|
+
pane.pendingMenu = false
|
|
105
|
+
if (pane.sidebar) {
|
|
106
|
+
pane.sidebar.readRecent(pane)
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
run(pane, pane.recentPaths || [], pane.recentVisits)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Only the held viewport rows gain a Used stamp; filesystem metadata remains intact.
|
|
113
|
+
function stampRows(pane, rows) {
|
|
114
|
+
if (pane.recentMode !== RESULTS) return rows
|
|
115
|
+
return rows.map(function (row) {
|
|
116
|
+
var visit = (pane.recentVisits || {})[pane.join(pane.path, row.n)]
|
|
117
|
+
return Object.assign({}, row, { used: visit === undefined ? null : visit })
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// The folder a tab records on a history, "" elsewhere, so it lands where it stood.
|
|
122
|
+
function restingPath(pane) {
|
|
123
|
+
return pane.recentMode === RESULTS && pane.recentFrom.length > 0 ? pane.recentFrom : ""
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// A tab switch drops the mode and says whether it dropped one.
|
|
127
|
+
function dropOverlay(pane) {
|
|
128
|
+
if (pane.recentMode.length === 0) {
|
|
129
|
+
return false
|
|
130
|
+
}
|
|
131
|
+
leave(pane)
|
|
132
|
+
return true
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// o opens the row's own folder; Enter opens the file itself, so the two cannot disagree.
|
|
136
|
+
function reveal(pane) {
|
|
137
|
+
if (pane.listInFlight) {
|
|
138
|
+
pane.message("A directory is already loading.", false)
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
var row = pane.rowFor(pane.cursorIndex)
|
|
142
|
+
if (!row) {
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
var full = pane.join(pane.path, row.n)
|
|
146
|
+
var cut = full.lastIndexOf("/")
|
|
147
|
+
if (cut < 0) {
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
var dir = cut === 0 ? "/" : full.substring(0, cut)
|
|
151
|
+
leave(pane)
|
|
152
|
+
pane.pendingSelect = full
|
|
153
|
+
pane.openWithoutHistory(dir)
|
|
154
|
+
}
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
.import "Input.js" as Input
|
|
3
|
+
.import "Format.js" as Format
|
|
4
|
+
|
|
5
|
+
// The status bar's two search lines, each filled by Format.fill.
|
|
6
|
+
var WORDS = {
|
|
7
|
+
searching: "%1 found · Searching, %2 scanned",
|
|
8
|
+
landed: "%1 found in %2 s"
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Live subtree search: the strip opens a session, each keystroke sends its query, rows replace folder rows with no Enter.
|
|
12
|
+
var OFF = ""
|
|
13
|
+
var TYPING = "typing"
|
|
14
|
+
var RESULTS = "results"
|
|
15
|
+
|
|
16
|
+
// f opens the session with no rows touched; from Recent it leaves first, so the scope and rows are the real folder.
|
|
17
|
+
function start(root) {
|
|
18
|
+
if (root.searchMode === RESULTS) {
|
|
19
|
+
root.searchMode = TYPING
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
if ((root.searchMode || "").length > 0) {
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
// The cursor's file rides pendingSelect until Esc; mid-session landings skip it while the query stands.
|
|
26
|
+
var under = root.rowFor(root.cursorIndex)
|
|
27
|
+
root.pendingSelect = under && under.n ? root.join(root.path, under.n) : ""
|
|
28
|
+
var fromRecent = root.recentMode.length > 0
|
|
29
|
+
if (fromRecent) {
|
|
30
|
+
// The history rows keep their own base until the folder's rows, asked below, replace them.
|
|
31
|
+
root.searchBase = root.path
|
|
32
|
+
if (root.recentFrom.length > 0) {
|
|
33
|
+
root.path = root.recentFrom
|
|
34
|
+
}
|
|
35
|
+
root.recentMode = ""
|
|
36
|
+
root.recentFrom = ""
|
|
37
|
+
root.recentPaths = []
|
|
38
|
+
root.restoreRecentSort()
|
|
39
|
+
}
|
|
40
|
+
root.searchFrom = root.path
|
|
41
|
+
root.searchMode = TYPING
|
|
42
|
+
openScope(root)
|
|
43
|
+
// Recent's rows name paths under "/", so the folder's own rows are asked for now and Esc re-lists it.
|
|
44
|
+
if (fromRecent) {
|
|
45
|
+
sendQuery(root)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// The backend session for the pane's scope, with the drawn state of a fresh walk; f and a tag place both open through here.
|
|
50
|
+
function openScope(root) {
|
|
51
|
+
// Gens keep rising across sessions, so a closed session's late line never matches; the first answer parks on row 0.
|
|
52
|
+
root.searchOpenedGen = root.searchGen
|
|
53
|
+
if (root.swap) { root.swap.landedGen = 0; root.swap.answeredQuery = "" }
|
|
54
|
+
root.searchQuery = ""
|
|
55
|
+
root.searchRunning = true
|
|
56
|
+
root.searchCancelled = false
|
|
57
|
+
root.searchScanned = 0
|
|
58
|
+
root.backend.searchOpen(scopeRoot(root.path, root.home || "", root.searchHere), root.path, root.showHidden)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Universal search: home when the pane stands inside it, else the pane's directory (backend has no home notion). Issue 30: here=true stays; abort()/reveal() leave it; nothing persists.
|
|
62
|
+
function scopeRoot(path, home, here) {
|
|
63
|
+
if (here === true) {
|
|
64
|
+
return path
|
|
65
|
+
}
|
|
66
|
+
if (home.length === 0) {
|
|
67
|
+
return path
|
|
68
|
+
}
|
|
69
|
+
if (path === home || String(path).indexOf(home + "/") === 0) {
|
|
70
|
+
return home
|
|
71
|
+
}
|
|
72
|
+
return path
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Keystrokes send rising gens with old rows held and the cursor standing for the landing to keep.
|
|
76
|
+
function sendQuery(root) {
|
|
77
|
+
root.searchGen += 1
|
|
78
|
+
root.searchKeyAt = Date.now()
|
|
79
|
+
root.searchFirstRowsAt = 0
|
|
80
|
+
root.searchSettledAt = 0
|
|
81
|
+
root.backend.searchQuery(root.searchQuery, root.searchGen, root.windowSize, root.sizeWish)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function extend(root, character) {
|
|
85
|
+
root.searchQuery += character
|
|
86
|
+
sendQuery(root)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Backspace to empty sends "" and the folder rows come back, with the strip still open.
|
|
90
|
+
function shrink(root) {
|
|
91
|
+
if (root.searchQuery.length === 0) {
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
root.searchQuery = root.searchQuery.substring(0, root.searchQuery.length - 1)
|
|
95
|
+
sendQuery(root)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Enter keeps the results and moves the keys to them, with no new request.
|
|
99
|
+
function commit(root) {
|
|
100
|
+
if ((root.searchQuery || "").length === 0) {
|
|
101
|
+
abort(root)
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
root.searchMode = RESULTS
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// A leaving session takes no query from outside, so a caller that clears its own state first asks here.
|
|
108
|
+
function takesQuery(root) {
|
|
109
|
+
return root.searchLeaving !== true
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// A query set from outside the strip lands as results over the Home scope, from a fresh session, a standing one or a cancelled one; a leaving session takes none.
|
|
113
|
+
// Sample input: (pane, "tag:Red") with the pane browsing, in Recent, or typing or showing results at either scope.
|
|
114
|
+
function openQuery(root, query) {
|
|
115
|
+
if (!takesQuery(root)) {
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
if ((root.searchMode || "").length === 0) {
|
|
119
|
+
root.searchHere = false
|
|
120
|
+
start(root)
|
|
121
|
+
} else if (root.searchCancelled === true) {
|
|
122
|
+
// A cancelled walk answers nothing more, so the place opens a fresh Home session with the drawn state of a new walk.
|
|
123
|
+
root.searchHere = false
|
|
124
|
+
openScope(root)
|
|
125
|
+
} else if (root.searchHere) {
|
|
126
|
+
// The old query is dropped first, so the rescope sends nothing and the new query is the one request.
|
|
127
|
+
root.searchQuery = ""
|
|
128
|
+
rescope(root)
|
|
129
|
+
}
|
|
130
|
+
root.searchQuery = query
|
|
131
|
+
sendQuery(root)
|
|
132
|
+
commit(root)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Tab flips the scope, opens the new scope at once and re-sends the standing query.
|
|
136
|
+
function rescope(root) {
|
|
137
|
+
root.searchHere = !root.searchHere
|
|
138
|
+
var scope = scopeRoot(root.path, root.home || "", root.searchHere)
|
|
139
|
+
root.backend.searchOpen(scope, root.path, root.showHidden)
|
|
140
|
+
if ((root.searchQuery || "").length > 0) {
|
|
141
|
+
sendQuery(root)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// The first Esc while the walk runs stops it and keeps the results on screen.
|
|
146
|
+
function cancel(root) {
|
|
147
|
+
if (root.backend) {
|
|
148
|
+
root.backend.searchCancel()
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Esc leaves at once when empty or landed, or re-lists the folder it came from otherwise.
|
|
153
|
+
function abort(root) {
|
|
154
|
+
// A second Esc lands nowhere: the first already closed the backend and owes the landing.
|
|
155
|
+
if (root.searchLeaving === true) {
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
if (root.searchRunning && (root.searchQuery || "").length > 0) {
|
|
159
|
+
cancel(root)
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
var back = (root.searchFrom || "").length > 0 ? root.searchFrom : root.path
|
|
163
|
+
if (!owesRelist(root)) {
|
|
164
|
+
closeSession(root)
|
|
165
|
+
root.pendingSelect = ""
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
// The backend closes at the request; the drawn state waits for the folder to land.
|
|
169
|
+
leaveWalk(root)
|
|
170
|
+
root.openWithoutHistory(back, { holdWalk: true, relist: true })
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Any gen this session sent may have drawn rows the folder does not own, so leaving it re-lists the folder.
|
|
174
|
+
function owesRelist(root) {
|
|
175
|
+
return (root.searchMode || "").length > 0 && root.searchGen > (root.searchOpenedGen || 0)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// A live re-answer keeps the cursor's file when the new window still names it, else row 0.
|
|
179
|
+
function keptIndex(name, items, start) {
|
|
180
|
+
if (name === null || name === undefined) {
|
|
181
|
+
return start
|
|
182
|
+
}
|
|
183
|
+
for (var i = 0; i < items.length; i++) {
|
|
184
|
+
if (items[i].n === name) {
|
|
185
|
+
return start + i
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return start
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// A navigation drops the session without re-listing, so the next listing lands on clean state.
|
|
192
|
+
function leaveWalk(root) {
|
|
193
|
+
if ((root.searchMode || "").length === 0) {
|
|
194
|
+
return false
|
|
195
|
+
}
|
|
196
|
+
// One close per exit: tabs close the walk before the navigation closes it again.
|
|
197
|
+
if (root.searchLeaving === true) {
|
|
198
|
+
return true
|
|
199
|
+
}
|
|
200
|
+
// Results on screen keep their strip until the next listing lands; the backend closes now.
|
|
201
|
+
if (live(root)) {
|
|
202
|
+
if (root.backend) {
|
|
203
|
+
root.backend.searchClose()
|
|
204
|
+
}
|
|
205
|
+
root.searchLeaving = true
|
|
206
|
+
return true
|
|
207
|
+
}
|
|
208
|
+
return closeSession(root)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Closing the session is one searchclose; a list closes it too, so a doubled close is silence.
|
|
212
|
+
function closeSession(root) {
|
|
213
|
+
if ((root.searchMode || "").length === 0) {
|
|
214
|
+
return false
|
|
215
|
+
}
|
|
216
|
+
if (root.backend) {
|
|
217
|
+
root.backend.searchClose()
|
|
218
|
+
}
|
|
219
|
+
root.searchMode = OFF
|
|
220
|
+
root.searchQuery = ""
|
|
221
|
+
root.searchRunning = false
|
|
222
|
+
root.searchCancelled = false
|
|
223
|
+
root.searchScanned = 0
|
|
224
|
+
root.searchFrom = ""
|
|
225
|
+
root.searchBase = ""
|
|
226
|
+
root.listingState = listingState(root, root.total)
|
|
227
|
+
root.stateMessage = ""
|
|
228
|
+
return true
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// A terminal line answers only the newest gen this session sent; older gens drop with anything gen-less.
|
|
232
|
+
function accepts(root, gen) {
|
|
233
|
+
return (root.searchMode || "").length > 0 && gen === root.searchGen && gen > (root.searchOpenedGen || 0)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// An exit landing clears the drawn search state in the same turn its rows do; the backend already closed.
|
|
237
|
+
function closeLeaving(root) {
|
|
238
|
+
root.searchLeaving = false
|
|
239
|
+
root.searchMode = OFF
|
|
240
|
+
root.searchQuery = ""
|
|
241
|
+
root.searchRunning = false
|
|
242
|
+
root.searchCancelled = false
|
|
243
|
+
root.searchScanned = 0
|
|
244
|
+
root.searchFrom = ""
|
|
245
|
+
root.searchBase = ""
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// The answered counts land; the first window rides its own rows line, so nothing is asked for.
|
|
249
|
+
function searched(root, total, scanned, ms, cancelled, gen, walking) {
|
|
250
|
+
// Answers for a session closed at its exit's request never land.
|
|
251
|
+
if (root.searchLeaving === true) {
|
|
252
|
+
return
|
|
253
|
+
}
|
|
254
|
+
if (!accepts(root, gen)) {
|
|
255
|
+
return
|
|
256
|
+
}
|
|
257
|
+
root.searchRunning = walking === true
|
|
258
|
+
root.searchCancelled = cancelled
|
|
259
|
+
if (walking !== true) {
|
|
260
|
+
root.searchSettledAt = Date.now()
|
|
261
|
+
}
|
|
262
|
+
root.total = total
|
|
263
|
+
root.searchScanned = scanned
|
|
264
|
+
root.searchMs = ms
|
|
265
|
+
root.listingState = listingState(root, total)
|
|
266
|
+
root.listArea.restartSettle()
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Query-line keys: enter keeps, escape leaves, tab rescopes, printables extend; cursor motions run through Focus.
|
|
270
|
+
function typeKey(event, root) {
|
|
271
|
+
// Keys struck while the exit is out wait for the folder, the way any held listing holds them.
|
|
272
|
+
if (root.searchLeaving === true) {
|
|
273
|
+
return true
|
|
274
|
+
}
|
|
275
|
+
// Ctrl+V and Shift+Insert extend the query by the clipboard's first line, one generation (issue 287).
|
|
276
|
+
if (Input.isPasteKey(event))
|
|
277
|
+
return Input.pasteInto(root, function (text) { extend(root, text) })
|
|
278
|
+
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
|
279
|
+
commit(root)
|
|
280
|
+
return true
|
|
281
|
+
}
|
|
282
|
+
if (event.key === Qt.Key_Escape) {
|
|
283
|
+
abort(root)
|
|
284
|
+
return true
|
|
285
|
+
}
|
|
286
|
+
if (event.key === Qt.Key_Backspace) {
|
|
287
|
+
shrink(root)
|
|
288
|
+
return true
|
|
289
|
+
}
|
|
290
|
+
// Issue 30's control: tab flips the scope between the whole home directory and the one the pane
|
|
291
|
+
// is standing in. It is the only writer, and nothing resets it, so the flip holds for the window;
|
|
292
|
+
// ui/SearchStrip.qml draws "in <scope>" under the caret, so every search says which one it took.
|
|
293
|
+
if (event.key === Qt.Key_Tab || event.key === Qt.Key_Backtab) {
|
|
294
|
+
rescope(root)
|
|
295
|
+
return true
|
|
296
|
+
}
|
|
297
|
+
if (Input.isPrintable(event.text)) {
|
|
298
|
+
extend(root, event.text)
|
|
299
|
+
return true
|
|
300
|
+
}
|
|
301
|
+
return true
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// o on a result opens the directory that holds it and puts the cursor on the row, the design's reveal.
|
|
305
|
+
function reveal(root) {
|
|
306
|
+
var row = root.rowFor(root.cursorIndex)
|
|
307
|
+
if (!row) {
|
|
308
|
+
return
|
|
309
|
+
}
|
|
310
|
+
var full = root.join(baseOf(root), row.n)
|
|
311
|
+
var cut = full.lastIndexOf("/")
|
|
312
|
+
if (cut <= 0) {
|
|
313
|
+
return
|
|
314
|
+
}
|
|
315
|
+
root.recentMode = ""
|
|
316
|
+
root.recentFrom = ""
|
|
317
|
+
root.recentPaths = []
|
|
318
|
+
// Through the pane: importing RecentMode.js here would close a Nav import cycle.
|
|
319
|
+
root.restoreRecentSort()
|
|
320
|
+
root.pendingSelect = full
|
|
321
|
+
root.open(full.substring(0, cut))
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Read-only stopwatch for the newest gen; per-keystroke latency is measured off it.
|
|
325
|
+
function stamps(root) {
|
|
326
|
+
return { gen: root.searchGen, keyAt: root.searchKeyAt || 0,
|
|
327
|
+
firstRowsAt: root.searchFirstRowsAt || 0, settledAt: root.searchSettledAt || 0 }
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Rows on screen belong to a standing query: results in, folder out, whatever mode holds them.
|
|
331
|
+
function live(root) {
|
|
332
|
+
return (root.searchMode || "").length > 0 && (root.searchQuery || "").length > 0
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// What activating a row means right now. On a search result it takes
|
|
336
|
+
// you to the file instead of opening it; the canvas keeps enter on open and o on reveal, so it is
|
|
337
|
+
// only the pointer that asks this. ui/js/Tap.js is the one caller.
|
|
338
|
+
function activateAction(root) {
|
|
339
|
+
return live(root) ? "reveal" : "open"
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// A walk with no matches yet is still working, so the list area keeps the crawl rather than
|
|
343
|
+
// flashing the empty state at every directory that happens to hold nothing.
|
|
344
|
+
function listingState(root, total) {
|
|
345
|
+
if (total > 0) {
|
|
346
|
+
return "ready"
|
|
347
|
+
}
|
|
348
|
+
return root.searchRunning ? "loading" : "empty"
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Strip note as whole templates: the count with its crawl while the walk runs, alone landed.
|
|
352
|
+
var NOTE = { walking: "%1 found · still scanning", landed: "%1 found" }
|
|
353
|
+
function note(total, running, empty) {
|
|
354
|
+
if (empty === true) {
|
|
355
|
+
return ""
|
|
356
|
+
}
|
|
357
|
+
if (running) {
|
|
358
|
+
return NOTE.walking.replace("%1", Format.count(total))
|
|
359
|
+
}
|
|
360
|
+
return NOTE.landed.replace("%1", Format.count(total))
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Empty-state caption for the query the shown answer is for; the function replacer keeps "$&" and "$$" in it literal.
|
|
364
|
+
function nothingMatches(answered) {
|
|
365
|
+
if (answered.length === 0) {
|
|
366
|
+
return ""
|
|
367
|
+
}
|
|
368
|
+
return "Nothing matches %1".replace("%1", function () { return answered })
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Strip right edge as whole templates: walking esc stops the walk, any other esc leaves.
|
|
372
|
+
var WAY = { walking: "esc cancels, then returns", done: "esc returns" }
|
|
373
|
+
function wayOut(running, empty) {
|
|
374
|
+
if (running && empty !== true) {
|
|
375
|
+
return WAY.walking
|
|
376
|
+
}
|
|
377
|
+
return WAY.done
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Row facilities join a row against the listing's base, the scope or the folder, never pane.path mid-session.
|
|
381
|
+
function baseOf(root) {
|
|
382
|
+
return (root.searchBase || "").length > 0 ? root.searchBase : root.path
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// The rule itself lives in Format.tilde, because the window chrome draws a path through the same one.
|
|
386
|
+
function scope(path, home) {
|
|
387
|
+
return Format.tilde(path, home)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// The status bar's own left half while a search is up, the two lines the canvas draws.
|
|
391
|
+
function statusLine(running, total, scanned, ms) {
|
|
392
|
+
if (running) {
|
|
393
|
+
return Format.fill(WORDS.searching, Format.count(total), Format.count(scanned))
|
|
394
|
+
}
|
|
395
|
+
return Format.fill(WORDS.landed, Format.count(total), (ms / 1000).toFixed(1))
|
|
396
|
+
}
|