Files
dots-hyprland/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml
T
2025-12-06 23:14:08 +01:00

127 lines
4.7 KiB
QML

pragma ComponentBehavior: Bound
import Qt.labs.synchronizer
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
import qs.modules.waffle.startMenu.startPage
import qs.modules.waffle.startMenu.searchPage
WBarAttachedPanelContent {
id: root
property bool searching: false
property string searchText: LauncherSearch.query
StartMenuContext {
id: context
}
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) {
searchBar.forceFocus();
if (event.modifiers & Qt.ControlModifier) {
// Delete word before cursor
let text = searchBar.text;
let pos = searchBar.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;
searchBar.text = text.slice(0, pos - deleteLen) + text.slice(pos);
searchBar.searchInput.cursorPosition = pos - deleteLen;
}
} else {
// Delete character before cursor if any
if (searchBar.searchInput.cursorPosition > 0) {
searchBar.text = searchBar.text.slice(0, searchBar.searchInput.cursorPosition - 1) + searchBar.text.slice(searchBar.searchInput.cursorPosition);
searchBar.searchInput.cursorPosition -= 1;
}
}
// Always move cursor to end after programmatic edit
searchBar.searchInput.cursorPosition = searchBar.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.key !== Qt.Key_Delete && event.text.charCodeAt(0) >= 0x20) // ignore control chars like Backspace, Tab, etc.
{
if (!searchBar.searchInput.activeFocus) {
searchBar.forceFocus();
// Insert the character at the cursor position
searchBar.text = searchBar.text.slice(0, searchBar.searchInput.cursorPosition) + event.text + searchBar.text.slice(searchBar.searchInput.cursorPosition);
searchBar.searchInput.cursorPosition += 1;
event.accepted = true;
context.setCurrentIndex(0);
}
}
// Arrow keys for item navigation
if (event.key === Qt.Key_Down) {
let maxIndex = Math.max(0, LauncherSearch.results.length - 1);
context.setCurrentIndex(Math.min(context.currentIndex + 1, maxIndex));
event.accepted = true;
} else if (event.key === Qt.Key_Up) {
context.setCurrentIndex(Math.max(context.currentIndex - 1, 0));
event.accepted = true;
}
}
contentItem: WPane {
contentItem: WPanelPageColumn {
SearchBar {
id: searchBar
Layout.fillWidth: true
implicitWidth: 832 // TODO: Make sizes naturally inferred
horizontalPadding: root.searching ? 24 : 32
// verticalPadding: root.searching ? 32 : 16 // TODO: make this not nuke the panel
Synchronizer on searching {
property alias target: root.searching
}
focus: true
text: root.searchText
onTextChanged: {
LauncherSearch.query = text;
}
onAccepted: {
context.accepted();
}
}
Item {
implicitHeight: root.searching ? 800 : 800 // TODO: Make sizes naturally inferred
Layout.fillWidth: true
Loader {
id: pageContentLoader
anchors.fill: parent
sourceComponent: root.searching ? searchPageComp : startPageComp
}
}
}
}
Component {
id: searchPageComp
SearchPageContent {
context: context
}
}
Component {
id: startPageComp
StartPageContent {}
}
}