diff --git a/.config/quickshell/modules/common/Appearance.qml b/.config/quickshell/modules/common/Appearance.qml index 284aca6de..32133ed7a 100644 --- a/.config/quickshell/modules/common/Appearance.qml +++ b/.config/quickshell/modules/common/Appearance.qml @@ -197,6 +197,7 @@ Singleton { property int barPreferredSideSectionWidth: 400 property int sidebarWidth: 450 property int notificationPopupWidth: 410 + property int searchWidth: 450 property int hyprlandGapsOut: 5 property int elevationMargin: 7 property int fabShadowRadius: 5 diff --git a/.config/quickshell/modules/overview/Overview.qml b/.config/quickshell/modules/overview/Overview.qml index 31f48b468..7a7599166 100644 --- a/.config/quickshell/modules/overview/Overview.qml +++ b/.config/quickshell/modules/overview/Overview.qml @@ -24,6 +24,7 @@ Scope { WlrLayershell.namespace: "quickshell:overview" WlrLayershell.layer: WlrLayer.Overlay + WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive color: "transparent" mask: Region { @@ -40,7 +41,7 @@ Scope { HyprlandFocusGrab { id: grab windows: [ root ] - active: false + active: GlobalStates.overviewOpen onCleared: () => { if (!active) GlobalStates.overviewOpen = false } @@ -80,42 +81,17 @@ Scope { width: 1 // Prevent Wayland protocol error } - TextField { - id: searchInput - + SearchWidget { + panelWindow: root Layout.alignment: Qt.AlignHCenter - padding: 15 - color: activeFocus ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3onSurfaceVariant - selectedTextColor: Appearance.m3colors.m3onSurface - placeholderText: qsTr("Search") - placeholderTextColor: Appearance.m3colors.m3outline - focus: root.visible - - onTextChanged: root.searchingText = text - Connections { - target: root - function onVisibleChanged() { - searchInput.selectAll() - root.searchingText = "" - } - } - - background: Rectangle { - anchors.fill: parent - radius: Appearance.rounding.normal - color: Appearance.colors.colLayer0 - } - - cursorDelegate: Rectangle { - width: 1 - color: searchInput.activeFocus ? Appearance.m3colors.m3primary : "transparent" - radius: 1 + onSearchingTextChanged: (text) => { + root.searchingText = searchingText } } OverviewWidget { + panelWindow: root visible: (root.searchingText == "") - bar: root } } diff --git a/.config/quickshell/modules/overview/OverviewWidget.qml b/.config/quickshell/modules/overview/OverviewWidget.qml index 829d7d653..f586af2e1 100644 --- a/.config/quickshell/modules/overview/OverviewWidget.qml +++ b/.config/quickshell/modules/overview/OverviewWidget.qml @@ -13,8 +13,8 @@ import "./icons.js" as Icons Item { id: root - required property var bar - readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen) + required property var panelWindow + readonly property HyprlandMonitor monitor: Hyprland.monitorFor(panelWindow.screen) readonly property var toplevels: ToplevelManager.toplevels readonly property int workspacesShown: ConfigOptions.overview.numOfRows * ConfigOptions.overview.numOfCols readonly property int workspaceGroup: Math.floor((monitor.activeWorkspace?.id - 1) / workspacesShown) diff --git a/.config/quickshell/modules/overview/SearchItem.qml b/.config/quickshell/modules/overview/SearchItem.qml new file mode 100644 index 000000000..1e67290a8 --- /dev/null +++ b/.config/quickshell/modules/overview/SearchItem.qml @@ -0,0 +1,93 @@ +// pragma NativeMethodBehavior: AcceptThisObject +import "root:/" +import "root:/modules/common" +import "root:/modules/common/widgets" +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import Quickshell.Widgets + +Button { + id: root + property DesktopEntry desktopEntry + property string itemName: desktopEntry?.name + property string itemIcon: desktopEntry?.icon + property var itemExecute: desktopEntry?.execute + property string itemClickActionName: desktopEntry?.clickActionName + + property int horizontalMargin: 10 + property int buttonHorizontalPadding: 10 + property int buttonVerticalPadding: 5 + property bool keyboardDown: false + + anchors.left: parent?.left + anchors.right: parent?.right + implicitHeight: rowLayout.implicitHeight + root.buttonVerticalPadding * 2 + implicitWidth: rowLayout.implicitWidth + root.buttonHorizontalPadding * 2 + + PointingHandInteraction {} + onClicked: { + root.itemExecute() + closeOverview.running = true + } + Keys.onPressed: (event) => { + if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { + root.keyboardDown = true + root.clicked() + event.accepted = true; + } + } + Keys.onReleased: (event) => { + if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { + root.keyboardDown = false + event.accepted = true; + } + } + + background: Rectangle { + anchors.fill: parent + anchors.leftMargin: root.horizontalMargin + anchors.rightMargin: root.horizontalMargin + radius: Appearance.rounding.small + color: (root.down || root.keyboardDown) ? Appearance.colors.colLayer1Active : ((root.hovered || root.focus) ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.m3colors.m3surfaceContainerHigh, 1)) + } + + RowLayout { + id: rowLayout + spacing: 10 + anchors.fill: parent + anchors.leftMargin: root.horizontalMargin + root.buttonHorizontalPadding + anchors.rightMargin: root.horizontalMargin + root.buttonHorizontalPadding + + IconImage { + source: Quickshell.iconPath(root.itemIcon); + width: 35 + height: 35 + } + StyledText { + Layout.fillWidth: true + id: nameText + font.pixelSize: Appearance.font.pixelSize.normal + color: Appearance.m3colors.m3onSurface + horizontalAlignment: Text.AlignLeft + elide: Text.ElideRight + text: root.itemName + } + StyledText { + Layout.fillWidth: false + visible: (root.hovered || root.focus) + id: clickAction + font.pixelSize: Appearance.font.pixelSize.normal + color: Appearance.colors.colSubtext + horizontalAlignment: Text.AlignRight + text: root.itemClickActionName + } + } + + Process { + id: closeOverview + command: ["bash", "-c", "qs ipc call overview close &"] // Somehow has to be async to work? + } +} \ No newline at end of file diff --git a/.config/quickshell/modules/overview/SearchWidget.qml b/.config/quickshell/modules/overview/SearchWidget.qml new file mode 100644 index 000000000..a3cf41d95 --- /dev/null +++ b/.config/quickshell/modules/overview/SearchWidget.qml @@ -0,0 +1,135 @@ +import "root:/" +import "root:/modules/common" +import "root:/modules/common/widgets" +import Qt5Compat.GraphicalEffects +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Io + +Item { // Wrapper + id: root + required property var panelWindow + property string searchingText: "" + property bool showResults: searchingText != "" + property real searchBarHeight: searchBar.height + Appearance.sizes.elevationMargin * 2 + implicitWidth: searchWidgetContent.implicitWidth + Appearance.sizes.elevationMargin * 2 + implicitHeight: searchWidgetContent.implicitHeight + Appearance.sizes.elevationMargin * 2 + + Rectangle { // Background + id: searchWidgetContent + anchors.centerIn: parent + implicitWidth: columnLayout.implicitWidth + implicitHeight: columnLayout.implicitHeight + radius: Appearance.rounding.large + color: Appearance.colors.colLayer0 + + ColumnLayout { + id: columnLayout + anchors.centerIn: parent + spacing: 0 + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: searchWidgetContent.width + height: searchWidgetContent.width + radius: searchWidgetContent.radius + } + } + + RowLayout { + id: searchBar + spacing: 5 + KeyNavigation.down: appResults + MaterialSymbol { + id: searchIcon + Layout.leftMargin: 15 + font.pixelSize: Appearance.font.pixelSize.huge + color: Appearance.m3colors.m3onSurface + text: "search" + } + TextField { // Search box + id: searchInput + + padding: 15 + Layout.rightMargin: 15 + color: activeFocus ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3onSurfaceVariant + selectedTextColor: Appearance.m3colors.m3onSurface + placeholderText: qsTr("Search") + placeholderTextColor: Appearance.m3colors.m3outline + focus: root.panelWindow.visible || GlobalStates.overviewOpen + + implicitWidth: Appearance.sizes.searchWidth + + onTextChanged: root.searchingText = text + Connections { + target: root + function onVisibleChanged() { + searchInput.selectAll() + root.searchingText = "" + } + } + + background: Item {} + + cursorDelegate: Rectangle { + width: 1 + color: searchInput.activeFocus ? Appearance.m3colors.m3primary : "transparent" + radius: 1 + } + } + } + + Rectangle { // Separator + visible: root.showResults + Layout.fillWidth: true + height: 1 + color: Appearance.m3colors.m3outline + } + + ListView { // App results + id: appResults + visible: root.showResults + Layout.fillWidth: true + implicitHeight: 600 + clip: true + topMargin: 10 + bottomMargin: 10 + spacing: 0 + KeyNavigation.up: searchBar + + 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; + }) + } + delegate: SearchItem { + desktopEntry: modelData + // itemName: modelData.name + // itemIcon: modelData.icon + } + } + + } + } + + DropShadow { + id: searchWidgetShadow + anchors.fill: searchWidgetContent + source: searchWidgetContent + radius: Appearance.sizes.elevationMargin + samples: radius * 2 + 1 + color: Appearance.colors.colShadow + verticalOffset: 2 + horizontalOffset: 0 + } +} \ No newline at end of file