diff --git a/.config/quickshell/modules/common/functions/string_utils.js b/.config/quickshell/modules/common/functions/string_utils.js new file mode 100644 index 000000000..82cae4e72 --- /dev/null +++ b/.config/quickshell/modules/common/functions/string_utils.js @@ -0,0 +1,5 @@ +function format(str, ...args) { + return str.replace(/{(\d+)}/g, (match, index) => + typeof args[index] !== 'undefined' ? args[index] : match + ); +} diff --git a/.config/quickshell/modules/sidebarLeft/Anime.qml b/.config/quickshell/modules/sidebarLeft/Anime.qml index e00a684f0..c092b5315 100644 --- a/.config/quickshell/modules/sidebarLeft/Anime.qml +++ b/.config/quickshell/modules/sidebarLeft/Anime.qml @@ -3,6 +3,7 @@ import "root:/services" import "root:/modules/common" import "root:/modules/common/widgets" import "root:/modules/common/functions/fuzzysort.js" as Fuzzy +import "root:/modules/common/functions/string_utils.js" as StringUtils import "./anime/" import Qt.labs.platform import QtQuick @@ -55,36 +56,29 @@ Item { } }, { - name: "nsfw", - description: qsTr("Toggle NSFW mode"), + name: "next", + description: qsTr("Get the next page of results"), execute: () => { - ConfigOptions.sidebar.booru.allowNsfw = !ConfigOptions.sidebar.booru.allowNsfw; + if (Booru.responses.length > 0) { + const lastResponse = Booru.responses[Booru.responses.length - 1]; + root.handleInput(`${lastResponse.tags.join(" ")} ${parseInt(lastResponse.page) + 1}`); + } } }, { name: "safe", - description: qsTr("Set NSFW mode to false"), + description: qsTr("Disable NSFW content"), execute: () => { ConfigOptions.sidebar.booru.allowNsfw = false; } }, { name: "lewd", - description: qsTr("Set NSFW mode to true"), + description: qsTr("Allow NSFW content"), execute: () => { ConfigOptions.sidebar.booru.allowNsfw = true; } }, - { - name: "next", - description: qsTr("Get the next page of results"), - execute: () => { - if (Booru.responses.length > 0) { - const lastResponse = Booru.responses[Booru.responses.length - 1]; - root.handleInput(lastResponse.tags.join(" ") + ` ${parseInt(lastResponse.page) + 1}`); - } - } - } ] function handleInput(inputText) { @@ -233,10 +227,47 @@ Item { } } + Item { // Tag suggestion description + opacity: tagDescriptionText.text.length > 0 ? 1 : 0 + visible: opacity > 0 + Layout.fillWidth: true + implicitHeight: tagDescriptionBackground.implicitHeight + + Behavior on opacity { + NumberAnimation { + duration: Appearance.animation.elementMoveFast.duration + easing.type: Appearance.animation.elementMoveFast.type + easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve + } + } + + Rectangle { + id: tagDescriptionBackground + color: Appearance.colors.colTooltip + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + implicitHeight: tagDescriptionText.implicitHeight + 5 * 2 + radius: Appearance.rounding.verysmall + + StyledText { + id: tagDescriptionText + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: 10 + anchors.rightMargin: 10 + anchors.verticalCenter: parent.verticalCenter + font.pixelSize: Appearance.font.pixelSize.smaller + color: Appearance.colors.colOnTooltip + wrapMode: Text.Wrap + text: root.suggestionList[tagSuggestions.selectedIndex]?.description ?? "" + } + } + } + Flow { // Tag suggestions id: tagSuggestions - visible: root.suggestionList.length > 0 && - tagInputField.text.length > 0 + visible: root.suggestionList.length > 0 && tagInputField.text.length > 0 property int selectedIndex: 0 Layout.fillWidth: true spacing: 5 @@ -249,7 +280,7 @@ Item { } delegate: BooruTagButton { id: tagButton - // buttonText: `${modelData.name}_{${modelData.count}}` + background: Rectangle { radius: Appearance.rounding.small color: tagSuggestions.selectedIndex === index ? Appearance.colors.colLayer2Hover : @@ -270,7 +301,7 @@ Item { StyledText { font.pixelSize: Appearance.font.pixelSize.small color: Appearance.m3colors.m3onSurface - text: modelData.name + text: modelData.displayName ?? modelData.name } StyledText { visible: modelData.count !== undefined @@ -279,6 +310,12 @@ Item { text: modelData.count ?? "" } } + + onHoveredChanged: { + if (tagButton.hovered) { + tagSuggestions.selectedIndex = index; + } + } onClicked: { tagSuggestions.acceptTag(modelData.name) } @@ -344,7 +381,7 @@ Item { renderType: Text.NativeRendering selectedTextColor: Appearance.m3colors.m3onPrimary selectionColor: Appearance.m3colors.m3primary - placeholderText: qsTr("Enter tags") + placeholderText: StringUtils.format(qsTr('Enter tags, or "{0}" for commands'), root.commandPrefix) placeholderTextColor: Appearance.m3colors.m3outline background: Item {} @@ -379,10 +416,11 @@ Item { all: true, key: "name" }) - console.log(JSON.stringify(providerResults)) root.suggestionList = providerResults.map(provider => { return { name: `${tagInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "mode ") : ""}${provider.target}`, + displayName: `${Booru.providers[provider.target].name}`, + description: `${Booru.providers[provider.target].description}`, } }) searchTimer.stop(); @@ -393,6 +431,7 @@ Item { root.suggestionList = root.allCommands.filter(cmd => cmd.name.startsWith(tagInputField.text.substring(1))).map(cmd => { return { name: `${root.commandPrefix}${cmd.name}`, + description: `${cmd.description}`, } }) searchTimer.stop(); @@ -517,6 +556,7 @@ Item { } StyledToolTip { id: toolTip + extraVisibleCondition: false alternativeVisibleCondition: mouseArea.containsMouse // Show tooltip when hovered content: qsTr("The current API used. Endpoint: ") + Booru.providers[Booru.currentProvider].url + qsTr("\nSet with /mode PROVIDER") } @@ -534,7 +574,7 @@ Item { text: "•" } - Rectangle { + Rectangle { // NSFW toggle implicitWidth: switchesRow.implicitWidth RowLayout { @@ -542,13 +582,22 @@ Item { spacing: 5 anchors.centerIn: parent + MouseArea { + anchors.fill: parent + hoverEnabled: true + PointingHandInteraction {} + onClicked: { + nsfwSwitch.checked = !nsfwSwitch.checked + } + } + StyledText { Layout.fillHeight: true Layout.leftMargin: 10 Layout.alignment: Qt.AlignVCenter font.pixelSize: Appearance.font.pixelSize.smaller - color: Appearance.colors.colOnLayer1 - text: qsTr("NSFW") + color: nsfwSwitch.enabled ? Appearance.colors.colOnLayer1 : Appearance.m3colors.m3outline + text: qsTr("Allow NSFW") } StyledSwitch { id: nsfwSwitch diff --git a/.config/quickshell/services/Booru.qml b/.config/quickshell/services/Booru.qml index 2d8518abd..9246d6633 100644 --- a/.config/quickshell/services/Booru.qml +++ b/.config/quickshell/services/Booru.qml @@ -35,6 +35,7 @@ Singleton { "name": "yande.re", "url": "https://yande.re", "api": "https://yande.re/post.json", + "description": "All-rounder | Good quality, decent quantity", "mapFunc": (response) => { return response.map(item => { return { @@ -68,6 +69,7 @@ Singleton { "name": "Konachan", "url": "https://konachan.com", "api": "https://konachan.com/post.json", + "description": "For desktop wallpapers | Good quality", "mapFunc": (response) => { return response.map(item => { return { @@ -101,6 +103,7 @@ Singleton { "name": "Zerochan", "url": "https://www.zerochan.net", "api": "https://www.zerochan.net/?json", + "description": "Clean stuff | Excellent quality, no NSFW", "mapFunc": (response) => { response = response.items return response.map(item => { @@ -127,6 +130,7 @@ Singleton { "name": "Danbooru", "url": "https://danbooru.donmai.us", "api": "https://danbooru.donmai.us/posts.json", + "description": "The popular one | Best quantity, but quality can vary wildly", "mapFunc": (response) => { return response.map(item => { return { @@ -161,6 +165,7 @@ Singleton { "name": "Gelbooru", "url": "https://gelbooru.com", "api": "https://gelbooru.com/index.php?page=dapi&s=post&q=index&json=1", + "description": "The hentai one | Great quantity, a lot of NSFW, quality varies wildly", "mapFunc": (response) => { response = response.post return response.map(item => { @@ -195,6 +200,7 @@ Singleton { "name": "waifu.im", "url": "https://waifu.im", "api": "https://api.waifu.im/search", + "description": "Waifus only | Excellent quality, limited quantity", "mapFunc": (response) => { response = response.images return response.map(item => { @@ -226,6 +232,7 @@ Singleton { property var currentProvider: ConfigOptions.sidebar.booru.defaultProvider function setProvider(provider) { + provider = provider.toLowerCase() if (providerList.indexOf(provider) !== -1) { currentProvider = provider root.addSystemMessage(qsTr("Provider set to ") + providers[provider].name @@ -254,7 +261,10 @@ Singleton { var baseUrl = provider.api var tagString = tags.join(" ") if (!nsfw && !(["zerochan", "waifu.im"].includes(currentProvider))) { - tagString += " rating:safe" + if (currentProvider == "gelbooru") + tagString += " rating:general"; + else + tagString += " rating:safe"; } var params = [] // Tags & limit