diff options
Diffstat (limited to 'components')
| -rw-r--r-- | components/IListView.qml | 34 | ||||
| -rw-r--r-- | components/IMouseArea.qml | 56 | ||||
| -rw-r--r-- | components/IRect.qml | 26 | ||||
| -rw-r--r-- | components/ISpringAnimation.qml | 6 | ||||
| -rw-r--r-- | components/IText.qml | 106 | ||||
| -rw-r--r-- | components/ITextEdit.qml | 97 | ||||
| -rw-r--r-- | components/IWindow.qml | 14 | ||||
| -rw-r--r-- | components/Icon.qml | 40 | ||||
| -rw-r--r-- | components/Searchable.qml | 510 |
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 []; + } } |
