summaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/IListView.qml34
-rw-r--r--components/IMouseArea.qml56
-rw-r--r--components/IRect.qml26
-rw-r--r--components/ISpringAnimation.qml6
-rw-r--r--components/IText.qml106
-rw-r--r--components/ITextEdit.qml97
-rw-r--r--components/IWindow.qml14
-rw-r--r--components/Icon.qml40
-rw-r--r--components/Searchable.qml510
9 files changed, 461 insertions, 428 deletions
diff --git a/components/IListView.qml b/components/IListView.qml
new file mode 100644
index 0000000..d9155f1
--- /dev/null
+++ b/components/IListView.qml
@@ -0,0 +1,34 @@
+import QtQuick
+
+ListView {
+ id: root
+
+ property PropertyAnimation scrollAnim: ISpringAnimation {}
+
+ function scrollToIndex(idx, alignment = ListView.Contain) {
+ root.scrollAnim.stop();
+ let y = root.contentY;
+
+ root.positionViewAtIndex(idx, alignment);
+ let dsty = root.contentY;
+
+ root.contentY = y;
+
+ if (root.scrollAnim) {
+ root.scrollAnim.target = root;
+ root.scrollAnim.property = "contentY";
+ root.scrollAnim.from = y;
+ root.scrollAnim.to = dsty;
+ root.scrollAnim.start();
+ } else
+ root.contentY = dsty;
+ }
+
+ Connections {
+ target: root.scrollAnim
+
+ function onStopped() {
+ // root.positionViewAtIndex(targetIndex, alignment);
+ }
+ }
+}
diff --git a/components/IMouseArea.qml b/components/IMouseArea.qml
index 4c360ed..e817bca 100644
--- a/components/IMouseArea.qml
+++ b/components/IMouseArea.qml
@@ -4,42 +4,42 @@
import QtQuick
MouseArea {
- hoverEnabled: true
+ hoverEnabled: true
- property real gx: Number.MAX_VALUE
- property real gy: Number.MAX_VALUE
+ property real gx: Number.MAX_VALUE
+ property real gy: Number.MAX_VALUE
- property bool hovered: false
+ property bool hovered: false
- signal entered_
- signal positionChanged_(var mouse)
+ signal entered_
+ signal positionChanged_(var mouse)
- onPositionChanged: mouse => {
- let gpos = mapToItem(null, mouse.x, mouse.y);
+ onPositionChanged: mouse => {
+ let gpos = mapToItem(null, mouse.x, mouse.y);
- if (gx === Number.MAX_VALUE && gy === Number.MAX_VALUE) {
- gx = gpos.x;
- gy = gpos.y;
- return;
- }
+ if (gx === Number.MAX_VALUE && gy === Number.MAX_VALUE) {
+ gx = gpos.x;
+ gy = gpos.y;
+ return;
+ }
- if (gpos.x === gx && gpos.y === gy)
- return;
+ if (gpos.x === gx && gpos.y === gy)
+ return;
- gx = gpos.x;
- gy = gpos.y;
+ gx = gpos.x;
+ gy = gpos.y;
- if (!hovered) {
- hovered = true;
- entered_();
- }
+ if (!hovered) {
+ hovered = true;
+ entered_();
+ }
- positionChanged_(mouse);
- }
+ positionChanged_(mouse);
+ }
- onExited: {
- hovered = false;
- gx = Number.MAX_VALUE;
- gy = Number.MAX_VALUE;
- }
+ onExited: {
+ hovered = false;
+ gx = Number.MAX_VALUE;
+ gy = Number.MAX_VALUE;
+ }
}
diff --git a/components/IRect.qml b/components/IRect.qml
index a72b564..5f9a86f 100644
--- a/components/IRect.qml
+++ b/components/IRect.qml
@@ -2,18 +2,18 @@ import QtQuick
import qs.config
Rectangle {
- Behavior on color {
- ColorAnimation {
- duration: General.animationDuration
- easing.type: Easing.InOutQuad
- }
- }
+ Behavior on color {
+ ColorAnimation {
+ duration: General.animationDuration
+ easing.type: Easing.InOutQuad
+ }
+ }
- // TODO: use visible instead of opacity for perf, but we need to animate the opacity then set visible
- Behavior on opacity {
- NumberAnimation {
- duration: General.animationDuration
- easing.type: Easing.InOutQuad
- }
- }
+ // TODO: use visible instead of opacity for perf, but we need to animate the opacity then set visible
+ Behavior on opacity {
+ NumberAnimation {
+ duration: General.animationDuration
+ easing.type: Easing.InOutQuad
+ }
+ }
}
diff --git a/components/ISpringAnimation.qml b/components/ISpringAnimation.qml
index 0d13b59..cac1138 100644
--- a/components/ISpringAnimation.qml
+++ b/components/ISpringAnimation.qml
@@ -2,8 +2,8 @@ import QtQuick
import qs.config
SpringAnimation {
- property real speed: 1.0
+ property real speed: 1.0
- spring: General.springAnimationSpring * (speed * speed)
- damping: General.springAnimationDamping * (speed)
+ spring: General.springAnimationSpring * (speed * speed)
+ damping: General.springAnimationDamping * (speed)
}
diff --git a/components/IText.qml b/components/IText.qml
index c3eb817..54878c5 100644
--- a/components/IText.qml
+++ b/components/IText.qml
@@ -5,66 +5,66 @@ import QtQuick
import qs.config
Text {
- id: root
+ id: root
- property bool animate: false
- property real animateFrom: 0
- property real animateTo: 1
- property real multiplier: 1.0
- property real fontSize: Style.font.size.larger
+ property bool animate: false
+ property real animateFrom: 0
+ property real animateTo: 1
+ property real multiplier: 1.0
+ property real fontSize: Style.font.size.larger
- renderType: Text.NativeRendering // or Text.CurveRendering(much more expansive) or Text.QtRendering for faster
- textFormat: Text.PlainText
- elide: Text.ElideRight
- color: Colors.foreground1
- smooth: true
+ renderType: Text.NativeRendering // or Text.CurveRendering(much more expansive) or Text.QtRendering for faster
+ textFormat: Text.PlainText
+ elide: Text.ElideRight
+ color: Colors.foreground1
+ smooth: true
- wrapMode: TextEdit.Wrap
+ wrapMode: TextEdit.Wrap
- font {
- family: Style.font.family.iosevka
- // pointSize: root.pixelSize
- pixelSize: fontSize
- }
+ font {
+ family: Style.font.family.iosevka
+ // pointSize: root.pixelSize
+ pixelSize: fontSize
+ }
- onTextChanged: {
- if (animate) {
- root.scale = animateFrom;
- root.opacity = animateFrom;
- scaleAnim.to = animateTo;
- opacityAnim.to = animateTo;
- scaleAnim.easing.bezierCurve = Style.anim.curves.standardAccel;
- opacityAnim.easing.bezierCurve = Style.anim.curves.standardAccel;
- scaleAnim.start();
- opacityAnim.start();
- }
- }
+ onTextChanged: {
+ if (animate) {
+ root.scale = animateFrom;
+ root.opacity = animateFrom;
+ scaleAnim.to = animateTo;
+ opacityAnim.to = animateTo;
+ scaleAnim.easing.bezierCurve = Style.anim.curves.standardAccel;
+ opacityAnim.easing.bezierCurve = Style.anim.curves.standardAccel;
+ scaleAnim.start();
+ opacityAnim.start();
+ }
+ }
- onLinkActivated: link => {
- Qt.openUrlExternally(link);
- }
+ onLinkActivated: link => {
+ Qt.openUrlExternally(link);
+ }
- Behavior on color {
- ColorAnimation {
- duration: Style.anim.durations.normal
- easing.type: Easing.BezierSpline
- easing.bezierCurve: Style.anim.curves.standard
- }
- }
+ Behavior on color {
+ ColorAnimation {
+ duration: Style.anim.durations.normal
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Style.anim.curves.standard
+ }
+ }
- NumberAnimation {
- id: scaleAnim
- target: root
- property: "scale"
- duration: General.animationDuration / 4 / root.multiplier
- easing.type: Easing.BezierSpline
- }
+ NumberAnimation {
+ id: scaleAnim
+ target: root
+ property: "scale"
+ duration: General.animationDuration / 4 / root.multiplier
+ easing.type: Easing.BezierSpline
+ }
- NumberAnimation {
- id: opacityAnim
- target: root
- property: "opacity"
- duration: General.animationDuration / 4 / root.multiplier
- easing.type: Easing.BezierSpline
- }
+ NumberAnimation {
+ id: opacityAnim
+ target: root
+ property: "opacity"
+ duration: General.animationDuration / 4 / root.multiplier
+ easing.type: Easing.BezierSpline
+ }
}
diff --git a/components/ITextEdit.qml b/components/ITextEdit.qml
index f05c8c7..7c6dd9e 100644
--- a/components/ITextEdit.qml
+++ b/components/ITextEdit.qml
@@ -5,53 +5,52 @@ import QtQuick
import qs.config
TextEdit {
- id: root
-
- property bool animate: false
- property real animateFrom: 0
- property real animateTo: 1
- property real fontSize: Style.font.size.larger
-
- renderType: Text.NativeRendering // or Text.CurveRendering(much more expansive) or Text.QtRendering for faster
- textFormat: Text.PlainText
- color: Colors.foreground1
- smooth: true
-
- wrapMode: TextEdit.Wrap
-
- font {
- family: Style.font.family.iosevka
- // pointSize: root.pixelSize
- pixelSize: fontSize
- }
-
- onTextChanged: {
- if (animate) {
- root.scale = animateFrom;
- scaleAnim.to = animateTo;
- scaleAnim.easing.bezierCurve = Style.anim.curves.standardAccel;
- scaleAnim.start();
- }
- }
-
- onLinkActivated: (link) => {
- Qt.openUrlExternally(link)
- }
-
- Behavior on color {
- ColorAnimation {
- duration: Style.anim.durations.normal
- easing.type: Easing.BezierSpline
- easing.bezierCurve: Style.anim.curves.standard
- }
- }
-
- NumberAnimation {
- id: scaleAnim
- target: root
- property: "scale"
- duration: General.animationDuration / 4
- easing.type: Easing.BezierSpline
- }
-
+ id: root
+
+ property bool animate: false
+ property real animateFrom: 0
+ property real animateTo: 1
+ property real fontSize: Style.font.size.larger
+
+ renderType: Text.NativeRendering // or Text.CurveRendering(much more expansive) or Text.QtRendering for faster
+ textFormat: Text.PlainText
+ color: Colors.foreground1
+ smooth: true
+
+ wrapMode: TextEdit.Wrap
+
+ font {
+ family: Style.font.family.iosevka
+ // pointSize: root.pixelSize
+ pixelSize: fontSize
+ }
+
+ onTextChanged: {
+ if (animate) {
+ root.scale = animateFrom;
+ scaleAnim.to = animateTo;
+ scaleAnim.easing.bezierCurve = Style.anim.curves.standardAccel;
+ scaleAnim.start();
+ }
+ }
+
+ onLinkActivated: link => {
+ Qt.openUrlExternally(link);
+ }
+
+ Behavior on color {
+ ColorAnimation {
+ duration: Style.anim.durations.normal
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Style.anim.curves.standard
+ }
+ }
+
+ NumberAnimation {
+ id: scaleAnim
+ target: root
+ property: "scale"
+ duration: General.animationDuration / 4
+ easing.type: Easing.BezierSpline
+ }
}
diff --git a/components/IWindow.qml b/components/IWindow.qml
index b048282..4933e14 100644
--- a/components/IWindow.qml
+++ b/components/IWindow.qml
@@ -3,12 +3,12 @@ import Quickshell.Wayland
import QtQuick
PanelWindow {
- required property string name
- property ShellScreen modelData
- property var layer: WlrLayer.Top
+ required property string name
+ property ShellScreen modelData
+ property var layer: WlrLayer.Top
- screen: modelData
- WlrLayershell.namespace: `${name}`
- WlrLayershell.layer: layer
- color: "transparent"
+ screen: modelData
+ WlrLayershell.namespace: `${name}`
+ WlrLayershell.layer: layer
+ color: "transparent"
}
diff --git a/components/Icon.qml b/components/Icon.qml
index a23f6da..3f70807 100644
--- a/components/Icon.qml
+++ b/components/Icon.qml
@@ -4,24 +4,24 @@ import QtQuick.Layouts
import qs.config
IText {
- id: root
- property real iconSize: General.iconSize
- property real fill: 0
- property real truncatedFill: Math.round(fill * 100) / 100 // Reduce memory consumption spikes from constant font remapping
- renderType: Text.NativeRendering
- color: Colors.foreground2
- font {
- // hintingPreference: Font.PreferFullHinting
- hintingPreference: Font.PreferNoHinting // so the icons dont just offset away
- family: Style.font.family.symbols ?? "Symbols Nerd Font"
- pixelSize: iconSize
- weight: Font.Normal + (Font.DemiBold - Font.Normal) * fill
- variableAxes: {
- "FILL": truncatedFill,
- // "wght": font.weight,
- // "GRAD": 0,
- "opsz": iconSize
- }
- }
- verticalAlignment: Text.AlignVCenter
+ id: root
+ property real iconSize: General.iconSize
+ property real fill: 0
+ property real truncatedFill: Math.round(fill * 100) / 100 // Reduce memory consumption spikes from constant font remapping
+ renderType: Text.NativeRendering
+ color: Colors.foreground2
+ font {
+ // hintingPreference: Font.PreferFullHinting
+ hintingPreference: Font.PreferNoHinting // so the icons dont just offset away
+ family: Style.font.family.symbols ?? "Symbols Nerd Font"
+ pixelSize: iconSize
+ weight: Font.Normal + (Font.DemiBold - Font.Normal) * fill
+ variableAxes: {
+ "FILL": truncatedFill,
+ // "wght": font.weight,
+ // "GRAD": 0,
+ "opsz": iconSize
+ }
+ }
+ verticalAlignment: Text.AlignVCenter
}
diff --git a/components/Searchable.qml b/components/Searchable.qml
index a3f1034..666e2d9 100644
--- a/components/Searchable.qml
+++ b/components/Searchable.qml
@@ -6,259 +6,259 @@ import "../utils/fuzzysort.js" as Fuzzy
import "../utils/levendist.js" as Levendist
Scope {
- required property var list
- property string key: "name"
-
- enum SearchAlgorithm {
- Levendist,
- Fzf,
- Fuzzysort,
- Include
- }
-
- property int algorithm: SearchAlgorithm.Levendist
- property int scoreThreshold: 50
- property var extraOpts: ({})
- property list<string> keys: [key]
- property list<real> weights: [1]
- property var queryCache: ({})
-
- readonly property var fzf: algorithm === Searchable.SearchAlgorithm.Fzf ? new Fzf.Finder(list, Object.assign({
- selector
- }, extraOpts)) : null
-
- readonly property list<var> fzfFinders: algorithm === Searchable.SearchAlgorithm.Fzf ? keys.map(k => {
- return new Fzf.Finder(list, Object.assign({
- selector: item => fieldString(item, k)
- }, extraOpts));
- }) : []
-
- readonly property list<var> fuzzyPrepped: algorithm === Searchable.SearchAlgorithm.Fuzzysort ? list.map(e => {
- const obj = {
- _item: e
- };
- for (const k of keys)
- obj[k] = Fuzzy.prepare(String(e[k] ?? ""));
- return obj;
- }) : []
-
- function transformSearch(search: string): string {
- return search;
- }
-
- function fieldString(item: var, k: string): string {
- const v = item[k];
- return v == null ? "" : String(v);
- }
-
- function selector(item: var): string {
- return fieldString(item, keys[0]);
- }
-
- function getCachedSource(search) {
- if (queryCache[search])
- return {
- hit: true,
- data: queryCache[search]
- };
- let source = list;
- let previousLists = Object.keys(queryCache).filter(key => key && search.startsWith(key));
- if (previousLists.length > 0) {
- var longest = previousLists.reduce((a, b) => a.length >= b.length ? a : b);
- source = queryCache[longest];
- }
- return {
- hit: false,
- data: source
- };
- }
-
- function levendistQuery(search: string): list<var> {
- const prefixGroups = keys.map(() => []);
- const fuzzyGroups = keys.map(() => []);
-
- for (const s of list) {
- let best = null;
- for (let i = 0; i < keys.length; i++) {
- const t = fieldString(s, keys[i]).toLowerCase();
- if (!t)
- continue;
-
- if (t.startsWith(search)) {
- if (!best || i < best.index || best.type !== "prefix") {
- best = {
- type: "prefix",
- index: i,
- score: 0
- };
- if (best.index === 0)
- break;
- }
- continue;
- }
-
- let pos = 0;
- for (const c of search) {
- pos = t.indexOf(c, pos);
- if (pos < 0) {
- pos = 0;
- break;
- }
- pos++;
- }
- if (pos) {
- const d = Levendist.distance(search, t);
- if (d <= scoreThreshold) {
- if (!best || i < best.index || (best.type === "fuzzy" && i === best.index && d < best.score) || best.type === "prefix") {
- if (!best || i < best.index || d < best.score) {
- best = {
- type: "fuzzy",
- index: i,
- score: d
- };
- }
- }
- }
- }
- }
-
- if (best) {
- if (best.type === "prefix")
- prefixGroups[best.index].push(s);
- else
- fuzzyGroups[best.index].push({
- s,
- score: best.score
- });
- }
- }
-
- const results = [];
- const seen = new Set();
- for (let i = 0; i < keys.length; i++) {
- for (const item of prefixGroups[i]) {
- if (!seen.has(item)) {
- results.push(item);
- seen.add(item);
- }
- }
- fuzzyGroups[i].sort((a, b) => a.score - b.score).forEach(({
- s
- }) => {
- if (!seen.has(s)) {
- results.push(s);
- seen.add(s);
- }
- });
- }
-
- return results;
- }
-
- function fuzzysortQuery(search: string): list<var> {
- const res = Fuzzy.go(search, fuzzyPrepped, Object.assign({
- all: true,
- keys,
- scoreFn: r => weights.reduce((a, w, i) => a + r[i].score * w, 0)
- }, extraOpts));
-
- const groups = keys.map(() => []);
- const noMatch = [];
- for (const r of res) {
- let idx = -1;
- for (let i = 0; i < keys.length; i++) {
- const kr = r[i];
- if (kr && kr.score !== -Infinity) {
- idx = i;
- break;
- }
- }
- if (idx === -1)
- noMatch.push(r);
- else
- groups[idx].push(r);
- }
-
- const ordered = [];
- for (let i = 0; i < groups.length; i++) {
- for (const r of groups[i])
- ordered.push(r.obj._item);
- }
- for (const r of noMatch)
- ordered.push(r.obj._item);
-
- return ordered;
- }
-
- function fzfQuery(search: string): list<var> {
- let results;
- if (keys.length <= 1) {
- results = fzf.find(search).sort((a, b) => {
- if (a.score === b.score)
- return (selector(a.item).trim().length - selector(b.item).trim().length);
- return b.score - a.score;
- }).map(r => r.item);
- } else {
- const groups = keys.map((_, i) => {
- const arr = fzfFinders[i].find(search);
- arr.sort((a, b) => {
- if (a.score === b.score) {
- const la = fieldString(a.item, keys[i]).trim().length;
- const lb = fieldString(b.item, keys[i]).trim().length;
- return la - lb;
- }
- return b.score - a.score;
- });
- return arr;
- });
-
- const seen = new Set();
- const out = [];
- for (let i = 0; i < groups.length; i++) {
- for (const r of groups[i]) {
- const it = r.item;
- if (!seen.has(it)) {
- out.push(it);
- seen.add(it);
- }
- }
- }
- results = out;
- }
-
- return results;
- }
-
- function includeQuery(search: string): list<var> {
- return list.filter(item => keys.some(k => transformSearch(fieldString(item, k)).includes(search)));
- }
-
- function query(search: string): list<var> {
- search = transformSearch(search);
- if (!search)
- return list;
-
- const cacheCheck = getCachedSource(search);
- if (cacheCheck.hit)
- return cacheCheck.data;
-
- let result;
-
- if (algorithm === Searchable.SearchAlgorithm.Levendist)
- result = levendistQuery(search.toLowerCase());
-
- if (algorithm === Searchable.SearchAlgorithm.Fuzzysort)
- result = fuzzysortQuery(search);
-
- if (algorithm === Searchable.SearchAlgorithm.Fzf)
- result = fzfQuery(search);
-
- if (algorithm === Searchable.SearchAlgorithm.Include)
- result = includeQuery(search);
-
- queryCache[search] = result;
-
- return result;
- return [];
- }
+ required property var list
+ property string key: "name"
+
+ enum SearchAlgorithm {
+ Levendist,
+ Fzf,
+ Fuzzysort,
+ Include
+ }
+
+ property int algorithm: SearchAlgorithm.Levendist
+ property int scoreThreshold: 50
+ property var extraOpts: ({})
+ property list<string> keys: [key]
+ property list<real> weights: [1]
+ property var queryCache: ({})
+
+ readonly property var fzf: algorithm === Searchable.SearchAlgorithm.Fzf ? new Fzf.Finder(list, Object.assign({
+ selector
+ }, extraOpts)) : null
+
+ readonly property list<var> fzfFinders: algorithm === Searchable.SearchAlgorithm.Fzf ? keys.map(k => {
+ return new Fzf.Finder(list, Object.assign({
+ selector: item => fieldString(item, k)
+ }, extraOpts));
+ }) : []
+
+ readonly property list<var> fuzzyPrepped: algorithm === Searchable.SearchAlgorithm.Fuzzysort ? list.map(e => {
+ const obj = {
+ _item: e
+ };
+ for (const k of keys)
+ obj[k] = Fuzzy.prepare(String(e[k] ?? ""));
+ return obj;
+ }) : []
+
+ function transformSearch(search: string): string {
+ return search;
+ }
+
+ function fieldString(item: var, k: string): string {
+ const v = item[k];
+ return v == null ? "" : String(v);
+ }
+
+ function selector(item: var): string {
+ return fieldString(item, keys[0]);
+ }
+
+ function getCachedSource(search) {
+ if (queryCache[search])
+ return {
+ hit: true,
+ data: queryCache[search]
+ };
+ let source = list;
+ let previousLists = Object.keys(queryCache).filter(key => key && search.startsWith(key));
+ if (previousLists.length > 0) {
+ var longest = previousLists.reduce((a, b) => a.length >= b.length ? a : b);
+ source = queryCache[longest];
+ }
+ return {
+ hit: false,
+ data: source
+ };
+ }
+
+ function levendistQuery(search: string): list<var> {
+ const prefixGroups = keys.map(() => []);
+ const fuzzyGroups = keys.map(() => []);
+
+ for (const s of list) {
+ let best = null;
+ for (let i = 0; i < keys.length; i++) {
+ const t = fieldString(s, keys[i]).toLowerCase();
+ if (!t)
+ continue;
+
+ if (t.startsWith(search)) {
+ if (!best || i < best.index || best.type !== "prefix") {
+ best = {
+ type: "prefix",
+ index: i,
+ score: 0
+ };
+ if (best.index === 0)
+ break;
+ }
+ continue;
+ }
+
+ let pos = 0;
+ for (const c of search) {
+ pos = t.indexOf(c, pos);
+ if (pos < 0) {
+ pos = 0;
+ break;
+ }
+ pos++;
+ }
+ if (pos) {
+ const d = Levendist.distance(search, t);
+ if (d <= scoreThreshold) {
+ if (!best || i < best.index || (best.type === "fuzzy" && i === best.index && d < best.score) || best.type === "prefix") {
+ if (!best || i < best.index || d < best.score) {
+ best = {
+ type: "fuzzy",
+ index: i,
+ score: d
+ };
+ }
+ }
+ }
+ }
+ }
+
+ if (best) {
+ if (best.type === "prefix")
+ prefixGroups[best.index].push(s);
+ else
+ fuzzyGroups[best.index].push({
+ s,
+ score: best.score
+ });
+ }
+ }
+
+ const results = [];
+ const seen = new Set();
+ for (let i = 0; i < keys.length; i++) {
+ for (const item of prefixGroups[i]) {
+ if (!seen.has(item)) {
+ results.push(item);
+ seen.add(item);
+ }
+ }
+ fuzzyGroups[i].sort((a, b) => a.score - b.score).forEach(({
+ s
+ }) => {
+ if (!seen.has(s)) {
+ results.push(s);
+ seen.add(s);
+ }
+ });
+ }
+
+ return results;
+ }
+
+ function fuzzysortQuery(search: string): list<var> {
+ const res = Fuzzy.go(search, fuzzyPrepped, Object.assign({
+ all: true,
+ keys,
+ scoreFn: r => weights.reduce((a, w, i) => a + r[i].score * w, 0)
+ }, extraOpts));
+
+ const groups = keys.map(() => []);
+ const noMatch = [];
+ for (const r of res) {
+ let idx = -1;
+ for (let i = 0; i < keys.length; i++) {
+ const kr = r[i];
+ if (kr && kr.score !== -Infinity) {
+ idx = i;
+ break;
+ }
+ }
+ if (idx === -1)
+ noMatch.push(r);
+ else
+ groups[idx].push(r);
+ }
+
+ const ordered = [];
+ for (let i = 0; i < groups.length; i++) {
+ for (const r of groups[i])
+ ordered.push(r.obj._item);
+ }
+ for (const r of noMatch)
+ ordered.push(r.obj._item);
+
+ return ordered;
+ }
+
+ function fzfQuery(search: string): list<var> {
+ let results;
+ if (keys.length <= 1) {
+ results = fzf.find(search).sort((a, b) => {
+ if (a.score === b.score)
+ return (selector(a.item).trim().length - selector(b.item).trim().length);
+ return b.score - a.score;
+ }).map(r => r.item);
+ } else {
+ const groups = keys.map((_, i) => {
+ const arr = fzfFinders[i].find(search);
+ arr.sort((a, b) => {
+ if (a.score === b.score) {
+ const la = fieldString(a.item, keys[i]).trim().length;
+ const lb = fieldString(b.item, keys[i]).trim().length;
+ return la - lb;
+ }
+ return b.score - a.score;
+ });
+ return arr;
+ });
+
+ const seen = new Set();
+ const out = [];
+ for (let i = 0; i < groups.length; i++) {
+ for (const r of groups[i]) {
+ const it = r.item;
+ if (!seen.has(it)) {
+ out.push(it);
+ seen.add(it);
+ }
+ }
+ }
+ results = out;
+ }
+
+ return results;
+ }
+
+ function includeQuery(search: string): list<var> {
+ return list.filter(item => keys.some(k => transformSearch(fieldString(item, k)).includes(search)));
+ }
+
+ function query(search: string): list<var> {
+ search = transformSearch(search);
+ if (!search)
+ return list;
+
+ const cacheCheck = getCachedSource(search);
+ if (cacheCheck.hit)
+ return cacheCheck.data;
+
+ let result;
+
+ if (algorithm === Searchable.SearchAlgorithm.Levendist)
+ result = levendistQuery(search.toLowerCase());
+
+ if (algorithm === Searchable.SearchAlgorithm.Fuzzysort)
+ result = fuzzysortQuery(search);
+
+ if (algorithm === Searchable.SearchAlgorithm.Fzf)
+ result = fzfQuery(search);
+
+ if (algorithm === Searchable.SearchAlgorithm.Include)
+ result = includeQuery(search);
+
+ queryCache[search] = result;
+
+ return result;
+ return [];
+ }
}