Merge branch 'end-4:main' into parallax

This commit is contained in:
Ivan Rosinskii
2025-12-05 16:44:05 +01:00
committed by GitHub
67 changed files with 839 additions and 125 deletions
@@ -19,11 +19,14 @@ QtObject {
}
property var actions: []
// Stuff needed for DesktopEntry objects
// Stuff needed for DesktopEntry
property string id: ""
property bool shown: true
property string comment: ""
property bool runInTerminal: false
property string genericName: ""
property list<string> keywords: []
// Extra stuff to allow for more flexibility
property string category: type
}
@@ -9,6 +9,7 @@ ScrollBar {
policy: ScrollBar.AsNeeded
topPadding: Appearance.rounding.normal
bottomPadding: Appearance.rounding.normal
active: hovered || pressed
contentItem: Rectangle {
implicitWidth: 4
@@ -72,7 +72,7 @@ Scope {
}
// Unlock the keyring if configured to do so
if (Config.options.lock.security.unlockKeyring) root.unlockKeyring();
if (Config.options.lock.security.unlockKeyring) root.unlockKeyring(); // Async
// Unlock the screen before exiting, or the compositor will display a
// fallback lock you can't interact with.
@@ -83,6 +83,12 @@ Scope {
// Reset
lockContext.reset();
// Post-unlock actions
if (lockContext.alsoInhibitIdle) {
lockContext.alsoInhibitIdle = false;
Idle.toggleInhibit(true);
}
}
}
@@ -21,6 +21,7 @@ Scope {
property bool showFailure: false
property bool fingerprintsConfigured: false
property var targetAction: LockContext.ActionEnum.Unlock
property bool alsoInhibitIdle: false
function resetTargetAction() {
root.targetAction = LockContext.ActionEnum.Unlock;
@@ -58,7 +59,8 @@ Scope {
passwordClearTimer.restart();
}
function tryUnlock() {
function tryUnlock(alsoInhibitIdle = false) {
root.alsoInhibitIdle = alsoInhibitIdle;
root.unlockInProgress = true;
pam.start();
}
@@ -59,10 +59,20 @@ MouseArea {
}
// Key presses
property bool ctrlHeld: false
Keys.onPressed: event => {
root.context.resetClearTimer();
if (event.key === Qt.Key_Control) {
root.ctrlHeld = true;
}
if (event.key === Qt.Key_Escape) { // Esc to clear
root.context.currentText = "";
}
forceFieldFocus();
}
Keys.onReleased: event => {
if (event.key === Qt.Key_Control) {
root.ctrlHeld = false;
}
forceFieldFocus();
}
@@ -133,7 +143,9 @@ MouseArea {
// Synchronizing (across monitors) and unlocking
onTextChanged: root.context.currentText = this.text
onAccepted: root.context.tryUnlock()
onAccepted: {
root.context.tryUnlock(ctrlHeld);
}
Connections {
target: root.context
function onCurrentTextChanged() {
@@ -202,7 +214,7 @@ MouseArea {
iconSize: 24
text: {
if (root.context.targetAction === LockContext.ActionEnum.Unlock) {
return "arrow_right_alt";
return root.ctrlHeld ? "emoji_food_beverage" : "arrow_right_alt";
} else if (root.context.targetAction === LockContext.ActionEnum.Poweroff) {
return "power_settings_new";
} else if (root.context.targetAction === LockContext.ActionEnum.Reboot) {
@@ -26,7 +26,7 @@ PanelWindow {
bottom: true
}
// TODO: Ask: sidebar AI; Ocr: tesseract
// TODO: Ask: sidebar AI
enum SnipAction { Copy, Edit, Search, CharRecognition, Record, RecordWithSound }
enum SelectionMode { RectCorners, Circle }
property var action: RegionSelection.SnipAction.Copy
@@ -30,6 +30,12 @@ ColumnLayout {
required property var modelData
node: modelData
}
PagePlaceholder {
icon: "widgets"
title: Translation.tr("No applications")
shown: !root.hasApps
shape: MaterialShape.Shape.Cookie7Sided
}
}
StyledComboBox {
@@ -17,8 +17,8 @@ Singleton {
property string iconsPath: `${Directories.assetsPath}/icons/fluent`
property bool dark: Appearance.m3colors.darkmode
property real backgroundTransparency: 0.13
property real panelBackgroundTransparency: 0.12
property real backgroundTransparency: 0.16
property real panelBackgroundTransparency: 0.14
property real panelLayerTransparency: root.dark ? 0.9 : 0.7
property real contentTransparency: root.dark ? 0.87 : 0.5
function applyBackgroundTransparency(col) {
@@ -152,6 +152,7 @@ Singleton {
property real normal: 11
property real large: 13
property real larger: 15
property real xlarger: 17
}
}
@@ -48,7 +48,7 @@ Singleton {
}
property string batteryLevelIcon: {
const discreteLevel = Math.ceil(Battery.percentage * 10)
const discreteLevel = Math.ceil(Battery.percentage * 10);
return `battery-${discreteLevel > 9 ? "full" : discreteLevel}`;
}
@@ -107,7 +107,8 @@ Singleton {
function audioAppIcon(node) {
let icon;
icon = AppSearch.guessIcon(node?.properties["application.icon-name"] ?? "");
if (AppSearch.iconExists(icon)) return icon;
if (AppSearch.iconExists(icon))
return icon;
icon = AppSearch.guessIcon(node?.properties["node.name"] ?? "");
return icon;
}
@@ -127,4 +128,60 @@ Singleton {
return "bluetooth";
}
function fluentFromMaterial(icon) {
switch (icon) {
case "calculate":
return "calculator";
case "keyboard_return":
return "arrow-enter-left";
case "open_in_new":
return "open";
case "settings_suggest":
return "wand";
case "terminal":
return "app-generic";
case "travel_explore":
return "globe-search";
case "keep":
return "pin";
case "keep_off":
return "pin-off";
default:
return "apps";
}
}
function guessIconForName(name) {
const lowerName = name.toLowerCase();
if (lowerName.includes("app") || lowerName.includes("desktop"))
return "apps";
if (lowerName.includes("news"))
return "news";
if (lowerName.includes("new") || lowerName.includes("create") || lowerName.includes("add"))
return "add";
if (lowerName.includes("open"))
return "open";
if (lowerName.includes("friends") || lowerName.includes("contact") || lowerName.includes("family"))
return "people";
if (lowerName.includes("community"))
return "people-team";
if (lowerName.includes("library"))
return "library";
if (lowerName.includes("setting"))
return "settings";
if (lowerName.includes("gallery"))
return "image-copy";
if (lowerName.includes("server"))
return "server";
if (lowerName.includes("picture") || lowerName.includes("photo") || lowerName.includes("image"))
return "image";
if (lowerName.includes("store") || lowerName.includes("shop"))
return "store-microsoft";
if (lowerName.includes("record") || lowerName.includes("capture"))
return "record";
if (lowerName.includes("screen") || lowerName.includes("display") || lowerName.includes("monitor") || lowerName.includes("desktop"))
return "desktop";
return "apps";
}
}
@@ -0,0 +1,10 @@
import qs.modules.common
import qs.modules.common.widgets
import QtQuick
import QtQuick.Controls
ListView {
id: root
ScrollBar.vertical: WScrollBar {}
}
@@ -0,0 +1,25 @@
import QtQuick
import QtQuick.Controls
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
ScrollBar {
id: root
policy: ScrollBar.AsNeeded
active: hovered || pressed
property color color: Looks.colors.controlBg
contentItem: Rectangle {
implicitWidth: root.active ? 4 : 2
implicitHeight: root.visualSize
radius: 9999
color: root.color
opacity: root.policy === ScrollBar.AlwaysOn || (root.active && root.size < 1.0) ? 0.5 : 0
Behavior on opacity {
animation: Looks.transition.opacity.createObject(this)
}
}
}
@@ -15,10 +15,16 @@ FooterRectangle {
property real horizontalPadding: 32
property real verticalPadding: 16
property bool searching: text.length > 0
property alias searchInput: searchInput
property alias text: searchInput.text
implicitHeight: outline.implicitHeight + verticalPadding * 2
Component.onCompleted: searchInput.forceActiveFocus()
signal accepted()
Component.onCompleted: forceFocus()
function forceFocus() {
searchInput.forceActiveFocus();
}
focus: true
color: searching ? Looks.colors.bgPanelBody : Looks.colors.bgPanelFooter
@@ -81,6 +87,10 @@ FooterRectangle {
visible: searchInput.text.length === 0
font.pixelSize: Looks.font.pixelSize.large
}
onAccepted: {
root.accepted();
}
}
}
}
@@ -0,0 +1,48 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.models
import qs.modules.common.functions
import qs.modules.common.widgets
import qs.modules.waffle.looks
Item {
id: root
required property LauncherSearchResult entry
property int iconSize: 24
implicitWidth: Math.max(iconSize, textIconLoader.implicitWidth)
implicitHeight: iconSize
Loader {
anchors.centerIn: parent
active: root.entry.iconType === LauncherSearchResult.IconType.System && root.entry.iconName !== ""
sourceComponent: WAppIcon {
implicitSize: root.iconSize
iconName: root.entry.iconName
tryCustomIcon: false
animated: false
}
}
Loader {
id: textIconLoader
anchors.centerIn: parent
active: root.entry.iconType === LauncherSearchResult.IconType.Text
sourceComponent: WText {
text: root.entry.iconName
font.pixelSize: root.iconSize
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
Loader {
anchors.centerIn: parent
active: root.entry.iconType === LauncherSearchResult.IconType.Material || root.entry.iconType === LauncherSearchResult.IconType.None || root.entry.iconName === ""
sourceComponent: FluentIcon {
icon: root.entry.iconName ? WIcons.fluentFromMaterial(root.entry.iconName) : WIcons.guessIconForName(root.entry.name)
implicitSize: root.iconSize
animated: false
}
}
}
@@ -12,7 +12,9 @@ import qs.modules.waffle.looks
BodyRectangle {
id: root
property alias context: searchResults.context
property string searchText: LauncherSearch.query
property alias currentIndex: searchResults.currentIndex
ColumnLayout {
anchors {
@@ -24,11 +26,13 @@ BodyRectangle {
spacing: 12
TagStrip {
context: root.context
Layout.fillWidth: true
Layout.fillHeight: false
}
SearchResults {
id: searchResults
Layout.fillWidth: true
Layout.fillHeight: true
}
@@ -1,19 +1,44 @@
pragma ComponentBehavior: Bound
import qs
import qs.services
import qs.modules.common
import qs.modules.waffle.looks
import qs.modules.common.functions
import qs.modules.common
import qs.services
import qs
import qs.modules.common.models
import Quickshell
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick
pragma ComponentBehavior: Bound
RowLayout {
id: root
property int maxResultsPerCategory: 4
property StartMenuContext context
property int currentIndex: context.currentIndex
onCurrentIndexChanged: {
forceCurrentIndex(currentIndex);
}
function focusFirstItem() {
resultList.currentIndex = 0;
forceCurrentIndex(0);
}
function forceCurrentIndex(index) {
context.currentIndex = index;
// Somehow this hack is needed
if (index === 0) {
resultList.incrementCurrentIndex();
resultList.decrementCurrentIndex();
} else {
resultList.decrementCurrentIndex();
resultList.incrementCurrentIndex();
}
}
Connections {
target: context
function onAccepted() {
resultList.currentItem?.execute();
}
}
ResultList {
@@ -25,23 +50,74 @@ RowLayout {
Layout.preferredWidth: 386
Layout.leftMargin: 1
Layout.rightMargin: 1
entry: resultList.model[resultList.currentIndex] ?? searchResultComp.createObject()
}
component ResultList: ListView {
component ResultList: WListView {
id: resultListView
section {
criteria: ViewSection.FullString
property: "type"
property: "category" // This is "type" with tweaks to make it match more closely
labelPositioning: ViewSection.InlineLabels
delegate: Item {
id: sectionButton
required property string section
implicitHeight: sectionChoiceButton.implicitHeight + resultListView.spacing
width: ListView.view?.width
WChoiceButton {
id: sectionChoiceButton
anchors {
left: parent.left
right: parent.right
top: parent.top
}
implicitHeight: 38
contentItem: WText {
text: sectionButton.section
font.pixelSize: Looks.font.pixelSize.large
font.weight: Looks.font.weight.strong
}
onClicked: {
root.context.selectCategory(sectionButton.section);
}
}
}
}
clip: true
spacing: 4
model: ScriptModel {
values: {
// TODO: categorize and have max per category
LauncherSearch.results.slice(0, 10)
}
onValuesChanged: {
root.focusFirstItem();
currentIndex: root.currentIndex
// We can't use a ScriptModel here because it would mess up sections
model: {
const allResults = LauncherSearch.results;
// Find categories
var categories = new Set();
for (let i = 0; i < allResults.length; i++) {
categories.add(allResults[i].type);
}
// Collect max 4 per category
var categorizedResults = [];
categories.forEach(category => {
let count = 0;
for (let i = 0; i < allResults.length; i++) {
if (allResults[i].type === category) {
const entry = allResults[i];
const tweakedEntry = searchResultComp.createObject(null, Object.assign({}, entry));
tweakedEntry.category = categorizedResults.length === 0 ? Translation.tr("Best match") : entry.type
categorizedResults.push(tweakedEntry); // Section header
count++;
if (count >= root.maxResultsPerCategory) {
break;
}
}
}
});
// print(JSON.stringify(categorizedResults, null, 2));
return categorizedResults;
}
onModelChanged: {
root.focusFirstItem();
}
delegate: WSearchResultButton {
required property int index
@@ -49,12 +125,120 @@ RowLayout {
entry: modelData
firstEntry: index === 0
width: ListView.view?.width
checked: resultListView.currentIndex === index
onRequestFocus: {
root.forceCurrentIndex(index);
}
}
}
component ResultPreview: Rectangle {
id: resultPreview
property LauncherSearchResult entry // LauncherSearchResult
Layout.fillHeight: true
color: Looks.colors.bg1
radius: Looks.radius.large
ColumnLayout {
anchors.fill: parent
anchors.margins: 22
spacing: 13
ColumnLayout {
id: mainInfoColumn
Layout.alignment: Qt.AlignHCenter
SearchEntryIcon {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 10
Layout.bottomMargin: 12
entry: resultPreview.entry
iconSize: 64
}
WText {
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
wrapMode: Text.Wrap
maximumLineCount: 2
text: resultPreview.entry?.name || ""
font.pixelSize: Looks.font.pixelSize.xlarger
}
WText {
Layout.alignment: Qt.AlignHCenter
text: resultPreview.entry?.type || ""
color: Looks.colors.accentUnfocused
font.pixelSize: Looks.font.pixelSize.normal
}
}
Rectangle {
id: resultSeparator
implicitHeight: 2
Layout.topMargin: 16
Layout.fillWidth: true
color: Looks.colors.bg2Hover
}
WListView {
id: actionsColumn
Layout.fillHeight: true
Layout.fillWidth: true
clip: true
spacing: 2
model: {
const isAppEntry = resultPreview.entry.type === Translation.tr("App");
const appId = isAppEntry ? resultPreview.entry.id : "";
const pinned = isAppEntry ? (Config.options.dock.pinnedApps.includes(appId)) : false;
var result = [
searchResultComp.createObject(null, {
name: resultPreview.entry.verb,
iconName: isAppEntry ? "open_in_new" : "keyboard_return",
iconType: LauncherSearchResult.IconType.Material,
execute: () => {
resultPreview.entry.execute();
}
}),
...(isAppEntry ? [
searchResultComp.createObject(null, {
name: pinned ? Translation.tr("Unpin from taskbar") : Translation.tr("Pin to taskbar"),
iconName: pinned ? "keep_off" : "keep",
iconType: LauncherSearchResult.IconType.Material,
execute: () => {
TaskbarApps.togglePin(appId);
}
})
] : [])
];
result = result.concat(resultPreview.entry.actions);
return result;
}
delegate: WButton {
id: actionButton
required property var modelData
width: ListView.view?.width
icon.name: modelData.iconName
text: modelData.name
onClicked: modelData.execute();
contentItem: RowLayout {
spacing: 11
SearchEntryIcon {
entry: actionButton.modelData
iconSize: 16
}
WText {
Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft
text: actionButton.text
}
}
}
}
}
}
Component {
id: searchResultComp
LauncherSearchResult {}
}
}
@@ -16,10 +16,72 @@ WBarAttachedPanelContent {
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 {
focus: true
id: searchBar
Layout.fillWidth: true
implicitWidth: 832 // TODO: Make sizes naturally inferred
horizontalPadding: root.searching ? 24 : 32
@@ -27,10 +89,14 @@ WBarAttachedPanelContent {
Synchronizer on searching {
property alias target: root.searching
}
focus: true
text: root.searchText
onTextChanged: {
LauncherSearch.query = text;
}
onAccepted: {
context.accepted();
}
}
Item {
implicitHeight: root.searching ? 736 : 736 // TODO: Make sizes naturally inferred
@@ -46,7 +112,9 @@ WBarAttachedPanelContent {
Component {
id: searchPageComp
SearchPageContent {}
SearchPageContent {
context: context
}
}
Component {
@@ -0,0 +1,64 @@
import QtQuick
import Quickshell
import Quickshell.Io
import qs
import qs.modules.common
import qs.services
Scope {
id: root
signal accepted
property int currentIndex: 0
function setCurrentIndex(index) {
if (index == currentIndex)
return;
currentIndex = index;
}
function selectCategory(category) {
for (let i = 0; i < root.categories.length; i++) {
const thisCategoryName = root.categories[i].name;
if (thisCategoryName.startsWith(category) || category.startsWith(thisCategoryName)) {
LauncherSearch.ensurePrefix(root.categories[i].prefix);
return;
}
}
}
property list<var> categories: [
{
name: Translation.tr("All"),
prefix: ""
},
{
name: Translation.tr("Apps"),
prefix: Config.options.search.prefix.app
},
{
name: Translation.tr("Actions"),
prefix: Config.options.search.prefix.action
},
{
name: Translation.tr("Clipboard"),
prefix: Config.options.search.prefix.clipboard
},
{
name: Translation.tr("Emojis"),
prefix: Config.options.search.prefix.emojis
},
{
name: Translation.tr("Math"),
prefix: Config.options.search.prefix.math
},
{
name: Translation.tr("Commands"),
prefix: Config.options.search.prefix.shellCommand
},
{
name: Translation.tr("Web"),
prefix: Config.options.search.prefix.webSearch
},
]
}
@@ -10,6 +10,9 @@ import qs.modules.common.functions
import qs.modules.waffle.looks
RowLayout {
id: root
property StartMenuContext context
WPanelIconButton {
implicitWidth: 36
implicitHeight: 36
@@ -23,40 +26,8 @@ RowLayout {
Layout.fillHeight: true
orientation: Qt.Horizontal
spacing: 4
model: [
{
name: Translation.tr("All"),
prefix: ""
},
{
name: Translation.tr("Apps"),
prefix: Config.options.search.prefix.app
},
{
name: Translation.tr("Actions"),
prefix: Config.options.search.prefix.action
},
{
name: Translation.tr("Clipboard"),
prefix: Config.options.search.prefix.clipboard
},
{
name: Translation.tr("Emojis"),
prefix: Config.options.search.prefix.emojis
},
{
name: Translation.tr("Math"),
prefix: Config.options.search.prefix.math
},
{
name: Translation.tr("Commands"),
prefix: Config.options.search.prefix.shellCommand
},
{
name: Translation.tr("Web"),
prefix: Config.options.search.prefix.webSearch
},
]
model: root.context.categories
clip: true
delegate: WBorderedButton {
id: tagButton
required property var modelData
@@ -68,7 +39,7 @@ RowLayout {
if (modelData.prefix != "") {
return LauncherSearch.query.startsWith(modelData.prefix);
} else {
return !tagListView.model.some(i => (i.prefix != "" && LauncherSearch.query.startsWith(i.prefix)))
return !tagListView.model.some(i => (i.prefix != "" && LauncherSearch.query.startsWith(i.prefix)));
}
}
contentItem: Item {
@@ -84,9 +55,27 @@ RowLayout {
}
}
WPanelIconButton {
id: optionsButton
implicitWidth: 36
implicitHeight: 36
iconSize: 24
iconName: "more-horizontal"
onClicked: accountsMenu.open()
WMenu {
id: accountsMenu
x: -accountsMenu.implicitWidth + optionsButton.implicitWidth
y: optionsButton.height + 10
downDirection: true
Action {
icon.name: "people-settings"
text: Translation.tr("Manage accounts")
onTriggered: {
Quickshell.execDetached(["bash", "-c", Config.options.apps.manageUser])
GlobalStates.searchOpen = false;
}
}
}
}
}
@@ -11,51 +11,84 @@ import qs.modules.waffle.looks
WChoiceButton {
id: root
required property LauncherSearchResult entry
property bool firstEntry: false
signal requestFocus()
checked: focus
animateChoiceHighlight: false
implicitWidth: contentLayout.implicitWidth + leftPadding + rightPadding
implicitHeight: contentLayout.implicitHeight + topPadding + bottomPadding
onClicked: {
GlobalStates.searchOpen = false
root.entry.execute()
}
contentItem: RowLayout {
id: contentLayout
spacing: 8
EntryIcon {}
EntryNameColumn {
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
}
execute();
}
component EntryIcon: Item {
implicitWidth: 24
implicitHeight: 24
Loader {
anchors.centerIn: parent
active: root.entry.iconType === LauncherSearchResult.IconType.System
sourceComponent: WAppIcon {
implicitSize: 24
tryCustomIcon: false
iconName: root.entry.iconName
function execute() {
GlobalStates.searchOpen = false;
root.entry.execute();
}
horizontalPadding: 0
verticalPadding: 0
contentItem: RowLayout {
id: contentLayout
spacing: 0
WButton {
id: launchButton
Layout.fillWidth: true
Layout.fillHeight: true
horizontalPadding: 10
verticalPadding: 11
implicitHeight: root.firstEntry ? 62 : 36
implicitWidth: entryContentRow.implicitWidth + leftPadding + rightPadding
topRightRadius: 0
bottomRightRadius: 0
onClicked: root.click()
contentItem: Item {
RowLayout {
id: entryContentRow
anchors {
left: parent.left
verticalCenter: parent.verticalCenter
}
spacing: 8
SearchEntryIcon {
entry: root.entry
iconSize: 24
}
EntryNameColumn {
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
}
}
}
}
Loader {
anchors.centerIn: parent
active: root.entry.iconType === LauncherSearchResult.IconType.Text
sourceComponent: WText {
text: root.entry.iconName
font.pixelSize: 24
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
Rectangle {
id: separator
opacity: (root.hovered && !root.checked) ? 1 : 0
Layout.fillHeight: true
implicitWidth: 1
color: ColorUtils.transparentize(Looks.colors.fg, 0.75)
}
WButton {
visible: !root.checked
Layout.fillHeight: true
implicitWidth: 47
topLeftRadius: 0
bottomLeftRadius: 0
onClicked: root.requestFocus()
contentItem: Item {
FluentIcon {
anchors.centerIn: parent
icon: "chevron-right"
implicitSize: 14
}
}
}
}
@@ -78,4 +111,11 @@ WChoiceButton {
color: Looks.colors.accentUnfocused
}
}
MouseArea {
anchors.fill: parent
// hoverEnabled: true
acceptedButtons: Qt.NoButton
cursorShape: Qt.PointingHandCursor
}
}
@@ -15,8 +15,10 @@ Scope {
target: GlobalStates
function onSearchOpenChanged() {
if (GlobalStates.searchOpen)
if (GlobalStates.searchOpen) {
LauncherSearch.query = "";
panelLoader.active = true;
}
}
}
@@ -62,6 +64,7 @@ Scope {
onClosed: {
GlobalStates.searchOpen = false;
panelLoader.active = false;
LauncherSearch.query = "";
}
}
}