mirror of
https://github.com/end-4/dots-hyprland.git
synced 2026-06-05 14:59:27 -05:00
overview: search
This commit is contained in:
@@ -17,15 +17,106 @@ Item { // Wrapper
|
||||
implicitWidth: searchWidgetContent.implicitWidth + Appearance.sizes.elevationMargin * 2
|
||||
implicitHeight: searchWidgetContent.implicitHeight + Appearance.sizes.elevationMargin * 2
|
||||
|
||||
Keys.onPressed: {
|
||||
// Only handle printable characters (ignore modifiers, arrows, etc.)
|
||||
if (event.text && event.text.length === 1 && event.key !== Qt.Key_Enter && event.key !== Qt.Key_Return) {
|
||||
property string mathResult: ""
|
||||
|
||||
Timer {
|
||||
id: nonAppResultsTimer
|
||||
interval: ConfigOptions.search.nonAppResultDelay
|
||||
onTriggered: {
|
||||
mathProcess.calculateExpression(root.searchingText);
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: mathProcess
|
||||
property list<string> baseCommand: ["qalc", "-t"]
|
||||
function calculateExpression(expression) {
|
||||
// mathProcess.running = false
|
||||
mathProcess.command = baseCommand.concat(expression)
|
||||
mathProcess.running = true
|
||||
}
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
root.mathResult = data
|
||||
if (searchInput.focus) appResults.currentIndex = 0; // Focus the first item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: copyText
|
||||
property list<string> baseCommand: ["wl-copy"]
|
||||
function copyTextToClipboard(text) {
|
||||
copyText.running = false
|
||||
copyText.command = baseCommand.concat(text)
|
||||
copyText.running = true
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: webSearch
|
||||
property list<string> baseCommand: ["xdg-open"]
|
||||
function search(query) {
|
||||
webSearch.running = false
|
||||
let url = ConfigOptions.search.engineBaseUrl + query
|
||||
for (let site of ConfigOptions.search.excludedSites) {
|
||||
url += ` -site:${site}`;
|
||||
}
|
||||
webSearch.command = baseCommand.concat(url)
|
||||
webSearch.running = true
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
// Prevent Esc and Backspace from registering
|
||||
if (event.key === Qt.Key_Escape) return;
|
||||
|
||||
// Handle Backspace: focus and delete character if not focused
|
||||
if (event.key === Qt.Key_Backspace) {
|
||||
if (!searchInput.activeFocus) {
|
||||
searchInput.forceActiveFocus();
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
// Delete word before cursor
|
||||
let text = searchInput.text;
|
||||
let pos = searchInput.cursorPosition;
|
||||
if (pos > 0) {
|
||||
// Find the start of the previous word
|
||||
let left = text.slice(0, pos);
|
||||
let match = left.match(/(\s*\S+)\s*$/);
|
||||
let deleteLen = match ? match[0].length : 1;
|
||||
searchInput.text = text.slice(0, pos - deleteLen) + text.slice(pos);
|
||||
searchInput.cursorPosition = pos - deleteLen;
|
||||
}
|
||||
} else {
|
||||
// Delete character before cursor if any
|
||||
if (searchInput.cursorPosition > 0) {
|
||||
searchInput.text = searchInput.text.slice(0, searchInput.cursorPosition - 1) +
|
||||
searchInput.text.slice(searchInput.cursorPosition);
|
||||
searchInput.cursorPosition -= 1;
|
||||
}
|
||||
}
|
||||
// Always move cursor to end after programmatic edit
|
||||
searchInput.cursorPosition = searchInput.text.length;
|
||||
event.accepted = true;
|
||||
}
|
||||
// If already focused, let TextField handle it
|
||||
return;
|
||||
}
|
||||
|
||||
// Only handle visible printable characters (ignore control chars, arrows, etc.)
|
||||
if (
|
||||
event.text &&
|
||||
event.text.length === 1 &&
|
||||
event.key !== Qt.Key_Enter &&
|
||||
event.key !== Qt.Key_Return &&
|
||||
event.text.charCodeAt(0) >= 0x20 // ignore control chars like Backspace, Tab, etc.
|
||||
) {
|
||||
if (!searchInput.activeFocus) {
|
||||
searchInput.forceActiveFocus();
|
||||
// Insert the character at the cursor position
|
||||
searchInput.text = searchInput.text.slice(0, searchInput.cursorPosition) +
|
||||
event.text +
|
||||
searchInput.text.slice(searchInput.cursorPosition);
|
||||
event.text +
|
||||
searchInput.text.slice(searchInput.cursorPosition);
|
||||
searchInput.cursorPosition += 1;
|
||||
event.accepted = true;
|
||||
}
|
||||
@@ -57,7 +148,12 @@ Item { // Wrapper
|
||||
RowLayout {
|
||||
id: searchBar
|
||||
spacing: 5
|
||||
KeyNavigation.down: appResults
|
||||
KeyNavigation.down: {
|
||||
if (appResults.count > 1) {
|
||||
appResults.currentIndex = 1;
|
||||
appResults.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
MaterialSymbol {
|
||||
id: searchIcon
|
||||
Layout.leftMargin: 15
|
||||
@@ -75,8 +171,14 @@ Item { // Wrapper
|
||||
selectedTextColor: Appearance.m3colors.m3onSurface
|
||||
placeholderText: qsTr("Search")
|
||||
placeholderTextColor: Appearance.m3colors.m3outline
|
||||
implicitWidth: Appearance.sizes.searchWidth
|
||||
implicitWidth: root.searchingText == "" ? Appearance.sizes.searchWidthCollapsed : Appearance.sizes.searchWidth
|
||||
|
||||
Behavior on implicitWidth {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementDecelFast.duration
|
||||
easing.type: Appearance.animation.elementDecelFast.type
|
||||
}
|
||||
}
|
||||
|
||||
onTextChanged: root.searchingText = text
|
||||
Connections {
|
||||
@@ -125,20 +227,65 @@ Item { // Wrapper
|
||||
spacing: 0
|
||||
KeyNavigation.up: searchBar
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onSearchingTextChanged() {
|
||||
if (appResults.count > 0)
|
||||
appResults.currentIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
id: model
|
||||
values: DesktopEntries.applications.values
|
||||
.filter((entry) => {
|
||||
if (root.searchingText == "") return false
|
||||
return entry.name.toLowerCase().includes(root.searchingText.toLowerCase())
|
||||
})
|
||||
.map((entry) => {
|
||||
entry.clickActionName = "Launch";
|
||||
return entry;
|
||||
})
|
||||
values: {
|
||||
if(root.searchingText == "") return [];
|
||||
|
||||
// Start math and other non-app stuff
|
||||
nonAppResultsTimer.restart();
|
||||
|
||||
// Init result array
|
||||
let result = [];
|
||||
|
||||
// Add filtered application entries
|
||||
result = result.concat(
|
||||
DesktopEntries.applications.values
|
||||
.filter((entry) => {
|
||||
if (root.searchingText == "") return false
|
||||
return entry.name.toLowerCase().includes(root.searchingText.toLowerCase())
|
||||
})
|
||||
.map((entry) => {
|
||||
entry.clickActionName = "Launch";
|
||||
entry.type = "App"
|
||||
return entry;
|
||||
})
|
||||
);
|
||||
|
||||
// Add non-app results
|
||||
result.push({
|
||||
name: root.mathResult,
|
||||
clickActionName: "Copy",
|
||||
type: qsTr("Math result"),
|
||||
fontType: "monospace",
|
||||
materialSymbol: 'calculate',
|
||||
execute: () => {
|
||||
copyText.copyTextToClipboard(root.mathResult);
|
||||
}
|
||||
});
|
||||
result.push({
|
||||
name: root.searchingText,
|
||||
clickActionName: "Search",
|
||||
type: "Search the web",
|
||||
materialSymbol: 'travel_explore',
|
||||
execute: () => {
|
||||
webSearch.search(root.searchingText);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
delegate: SearchItem {
|
||||
desktopEntry: modelData
|
||||
entry: modelData
|
||||
// itemName: modelData.name
|
||||
// itemIcon: modelData.icon
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user