forked from Shinonome/dots-hyprland
Merge branch 'main' into main
This commit is contained in:
@@ -154,6 +154,7 @@ Singleton {
|
||||
property JsonObject apps: JsonObject {
|
||||
property string bluetooth: "kcmshell6 kcm_bluetooth"
|
||||
property string network: "kcmshell6 kcm_networkmanagement"
|
||||
property string manageUser: "kcmshell6 kcm_users"
|
||||
property string networkEthernet: "kcmshell6 kcm_networkmanagement"
|
||||
property string taskManager: "plasma-systemmonitor --page-name Processes"
|
||||
property string terminal: "kitty -1" // This is only for shell actions
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.services
|
||||
import qs.modules.common.functions
|
||||
import QtCore
|
||||
import QtQuick
|
||||
@@ -46,6 +47,9 @@ Singleton {
|
||||
property string aiChats: FileUtils.trimFileProtocol(`${Directories.state}/user/ai/chats`)
|
||||
property string aiTranslationScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/ai/gemini-translate.sh`)
|
||||
property string recordScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/videos/record.sh`)
|
||||
property string userAvatarPathAccountsService: FileUtils.trimFileProtocol(`/var/lib/AccountsService/icons/${SystemInfo.username}`)
|
||||
property string userAvatarPathRicersAndWeirdSystems: FileUtils.trimFileProtocol(`${Directories.home}.face`)
|
||||
property string userAvatarPathRicersAndWeirdSystems2: FileUtils.trimFileProtocol(`${Directories.home}.face.icon`)
|
||||
// Cleanup on init
|
||||
Component.onCompleted: {
|
||||
Quickshell.execDetached(["mkdir", "-p", `${shellConfig}`])
|
||||
|
||||
@@ -84,4 +84,28 @@ Singleton {
|
||||
// Older dates
|
||||
return Qt.formatDateTime(messageTime, "MMMM dd");
|
||||
}
|
||||
|
||||
function processNotificationBody(body, appName) {
|
||||
let processedBody = body
|
||||
|
||||
// Clean Chromium-based browsers notifications - remove first line
|
||||
if (appName) {
|
||||
const lowerApp = appName.toLowerCase()
|
||||
const chromiumBrowsers = [
|
||||
"brave", "chrome", "chromium", "vivaldi", "opera", "microsoft edge"
|
||||
]
|
||||
|
||||
if (chromiumBrowsers.some(name => lowerApp.includes(name))) {
|
||||
const lines = body.split('\n\n')
|
||||
|
||||
if (lines.length > 1 && lines[0].startsWith('<a')) {
|
||||
processedBody = lines.slice(1).join('\n\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processedBody = processedBody.replace(/<img/gi, '\n\n<img');
|
||||
|
||||
return processedBody
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,19 @@ Process {
|
||||
|
||||
running: true
|
||||
command: ["bash", "-c",
|
||||
`mkdir -p $(dirname '${processFilePath(filePath)}'); [ -f '${processFilePath(filePath)}' ] || curl -sSL '${sourceUrl}' -o '${processFilePath(filePath)}' && magick identify -format '%w %h' '${processFilePath(filePath)}'[0]`
|
||||
`mkdir -p $(dirname '${processFilePath()}'); [ -f '${processFilePath()}' ] || curl -sSL '${sourceUrl}' -o '${processFilePath()}' && file '${processFilePath()}'`
|
||||
]
|
||||
stdout: StdioCollector {
|
||||
id: imageSizeOutputCollector
|
||||
onStreamFinished: {
|
||||
const output = imageSizeOutputCollector.text.trim();
|
||||
const [width, height] = output.split(" ").map(Number);
|
||||
root.done(root.filePath, width, height);
|
||||
const match = output.match(/(\d+)\s*x\s*(\d+)/);
|
||||
|
||||
if (match) {
|
||||
const width = Number(match[1]);
|
||||
const height = Number(match[2]);
|
||||
root.done(root.filePath, width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,28 +31,6 @@ Item { // Notification item area
|
||||
|
||||
implicitHeight: background.implicitHeight
|
||||
|
||||
function processNotificationBody(body, appName) {
|
||||
let processedBody = body
|
||||
|
||||
// Clean Chromium-based browsers notifications - remove first line
|
||||
if (appName) {
|
||||
const lowerApp = appName.toLowerCase()
|
||||
const chromiumBrowsers = [
|
||||
"brave", "chrome", "chromium", "vivaldi", "opera", "microsoft edge"
|
||||
]
|
||||
|
||||
if (chromiumBrowsers.some(name => lowerApp.includes(name))) {
|
||||
const lines = body.split('\n\n')
|
||||
|
||||
if (lines.length > 1 && lines[0].startsWith('<a')) {
|
||||
processedBody = lines.slice(1).join('\n\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return processedBody
|
||||
}
|
||||
|
||||
function destroyWithAnimation(left = false) {
|
||||
root.qmlParent.resetDrag()
|
||||
background.anchors.leftMargin = background.anchors.leftMargin; // Break binding
|
||||
@@ -196,12 +174,13 @@ Item { // Notification item area
|
||||
maximumLineCount: 1
|
||||
textFormat: Text.StyledText
|
||||
text: {
|
||||
return processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "<br/>")
|
||||
return NotificationUtils.processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "<br/>")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout { // Expanded content
|
||||
id: expandedContentColumn
|
||||
Layout.fillWidth: true
|
||||
opacity: root.expanded ? 1 : 0
|
||||
visible: opacity > 0
|
||||
@@ -218,8 +197,8 @@ Item { // Notification item area
|
||||
elide: Text.ElideRight
|
||||
textFormat: Text.RichText
|
||||
text: {
|
||||
return `<style>img{max-width:${300 /* binding to notificationBodyText.width would cause a binding loop */}px;}</style>` +
|
||||
`${processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "<br/>")}`
|
||||
return `<style>img{max-width:${expandedContentColumn.width}px;}</style>` +
|
||||
`${NotificationUtils.processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "<br/>")}`
|
||||
}
|
||||
|
||||
onLinkActivated: (link) => {
|
||||
@@ -293,6 +272,8 @@ Item { // Notification item area
|
||||
id: actionRepeater
|
||||
model: notificationObject.actions
|
||||
NotificationActionButton {
|
||||
id: notifAction
|
||||
required property var modelData
|
||||
Layout.fillWidth: true
|
||||
buttonText: modelData.text
|
||||
urgency: notificationObject.urgency
|
||||
|
||||
@@ -12,4 +12,14 @@ Image {
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
property list<string> fallbacks: []
|
||||
property int currentFallbackIndex: 0
|
||||
|
||||
onStatusChanged: {
|
||||
if (status === Image.Error && currentFallbackIndex < fallbacks.length) {
|
||||
source = fallbacks[currentFallbackIndex];
|
||||
currentFallbackIndex += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ ToolTip {
|
||||
hintingPreference: Font.PreferNoHinting // Prevent shaky text
|
||||
}
|
||||
|
||||
delay: 0
|
||||
visible: internalVisibleCondition
|
||||
|
||||
contentItem: StyledToolTipContent {
|
||||
|
||||
@@ -162,7 +162,7 @@ Scope {
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "overview"
|
||||
target: "search"
|
||||
|
||||
function toggle() {
|
||||
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
|
||||
@@ -185,8 +185,8 @@ Scope {
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "overviewToggle"
|
||||
description: "Toggles overview on press"
|
||||
name: "searchToggle"
|
||||
description: "Toggles search on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
|
||||
@@ -201,16 +201,8 @@ Scope {
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "overviewClose"
|
||||
description: "Closes overview"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.overviewOpen = false;
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "overviewToggleRelease"
|
||||
description: "Toggles overview on release"
|
||||
name: "searchToggleRelease"
|
||||
description: "Toggles search on release"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.superReleaseMightTrigger = true;
|
||||
@@ -225,8 +217,8 @@ Scope {
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "overviewToggleReleaseInterrupt"
|
||||
description: "Interrupts possibility of overview being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything."
|
||||
name: "searchToggleReleaseInterrupt"
|
||||
description: "Interrupts possibility of search being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything."
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.superReleaseMightTrigger = false;
|
||||
|
||||
@@ -81,7 +81,7 @@ RowLayout {
|
||||
}
|
||||
}
|
||||
|
||||
onTextChanged: root.searchingText = text
|
||||
onTextChanged: LauncherSearch.query = text
|
||||
|
||||
onAccepted: {
|
||||
if (appResults.count > 0) {
|
||||
|
||||
@@ -14,81 +14,11 @@ import Quickshell.Io
|
||||
Item { // Wrapper
|
||||
id: root
|
||||
readonly property string xdgConfigHome: Directories.config
|
||||
property string searchingText: ""
|
||||
property string searchingText: LauncherSearch.query
|
||||
property bool showResults: searchingText != ""
|
||||
implicitWidth: searchWidgetContent.implicitWidth + Appearance.sizes.elevationMargin * 2
|
||||
implicitHeight: searchBar.implicitHeight + searchBar.verticalPadding * 2 + Appearance.sizes.elevationMargin * 2
|
||||
|
||||
property string mathResult: ""
|
||||
property bool clipboardWorkSafetyActive: {
|
||||
const enabled = Config.options.workSafety.enable.clipboard;
|
||||
const sensitiveNetwork = (StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords))
|
||||
return enabled && sensitiveNetwork;
|
||||
}
|
||||
|
||||
property var searchActions: [
|
||||
{
|
||||
action: "accentcolor",
|
||||
execute: args => {
|
||||
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--noswitch", "--color", ...(args != '' ? [`${args}`] : [])]);
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "dark",
|
||||
execute: () => {
|
||||
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "dark", "--noswitch"]);
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "konachanwallpaper",
|
||||
execute: () => {
|
||||
Quickshell.execDetached([Quickshell.shellPath("scripts/colors/random/random_konachan_wall.sh")]);
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "light",
|
||||
execute: () => {
|
||||
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "light", "--noswitch"]);
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "superpaste",
|
||||
execute: args => {
|
||||
if (!/^(\d+)/.test(args.trim())) { // Invalid if doesn't start with numbers
|
||||
Quickshell.execDetached([
|
||||
"notify-send",
|
||||
Translation.tr("Superpaste"),
|
||||
Translation.tr("Usage: <tt>%1superpaste NUM_OF_ENTRIES[i]</tt>\nSupply <tt>i</tt> when you want images\nExamples:\n<tt>%1superpaste 4i</tt> for the last 4 images\n<tt>%1superpaste 7</tt> for the last 7 entries").arg(Config.options.search.prefix.action),
|
||||
"-a", "Shell"
|
||||
]);
|
||||
return;
|
||||
}
|
||||
const syntaxMatch = /^(?:(\d+)(i)?)/.exec(args.trim());
|
||||
const count = syntaxMatch[1] ? parseInt(syntaxMatch[1]) : 1;
|
||||
const isImage = !!syntaxMatch[2];
|
||||
Cliphist.superpaste(count, isImage);
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "todo",
|
||||
execute: args => {
|
||||
Todo.addTask(args);
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "wallpaper",
|
||||
execute: () => {
|
||||
GlobalStates.wallpaperSelectorOpen = true;
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "wipeclipboard",
|
||||
execute: () => {
|
||||
Cliphist.wipe();
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
function focusFirstItem() {
|
||||
appResults.currentIndex = 0;
|
||||
}
|
||||
@@ -103,13 +33,13 @@ Item { // Wrapper
|
||||
|
||||
function cancelSearch() {
|
||||
searchBar.searchInput.selectAll();
|
||||
root.searchingText = "";
|
||||
LauncherSearch.query = "";
|
||||
searchBar.animateWidth = true;
|
||||
}
|
||||
|
||||
function setSearchingText(text) {
|
||||
searchBar.searchInput.text = text;
|
||||
root.searchingText = text;
|
||||
LauncherSearch.query = text;
|
||||
}
|
||||
|
||||
function containsUnsafeLink(entry) {
|
||||
@@ -118,34 +48,6 @@ Item { // Wrapper
|
||||
return StringUtils.stringListContainsSubstring(entry.toLowerCase(), unsafeKeywords);
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: nonAppResultsTimer
|
||||
interval: Config.options.search.nonAppResultDelay
|
||||
onTriggered: {
|
||||
let expr = root.searchingText;
|
||||
if (expr.startsWith(Config.options.search.prefix.math)) {
|
||||
expr = expr.slice(Config.options.search.prefix.math.length);
|
||||
}
|
||||
mathProcess.calculateExpression(expr);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
root.focusFirstItem();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
// Prevent Esc and Backspace from registering
|
||||
if (event.key === Qt.Key_Escape)
|
||||
@@ -285,167 +187,9 @@ Item { // Wrapper
|
||||
model: ScriptModel {
|
||||
id: model
|
||||
objectProp: "key"
|
||||
values: {
|
||||
// Search results are handled here
|
||||
////////////////// Skip? //////////////////
|
||||
if (root.searchingText == "")
|
||||
return [];
|
||||
|
||||
///////////// Special cases ///////////////
|
||||
if (root.searchingText.startsWith(Config.options.search.prefix.clipboard)) {
|
||||
// Clipboard
|
||||
const searchString = StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.clipboard);
|
||||
return Cliphist.fuzzyQuery(searchString).map((entry, index, array) => {
|
||||
const mightBlurImage = Cliphist.entryIsImage(entry) && root.clipboardWorkSafetyActive;
|
||||
let shouldBlurImage = mightBlurImage;
|
||||
if (mightBlurImage) {
|
||||
shouldBlurImage = shouldBlurImage && (containsUnsafeLink(array[index - 1]) || containsUnsafeLink(array[index + 1]));
|
||||
}
|
||||
const type = `#${entry.match(/^\s*(\S+)/)?.[1] || ""}`
|
||||
return {
|
||||
key: type,
|
||||
cliphistRawString: entry,
|
||||
name: StringUtils.cleanCliphistEntry(entry),
|
||||
clickActionName: "",
|
||||
type: type,
|
||||
execute: () => {
|
||||
Cliphist.copy(entry)
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
name: "Copy",
|
||||
materialIcon: "content_copy",
|
||||
execute: () => {
|
||||
Cliphist.copy(entry);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Delete",
|
||||
materialIcon: "delete",
|
||||
execute: () => {
|
||||
Cliphist.deleteEntry(entry);
|
||||
}
|
||||
}
|
||||
],
|
||||
blurImage: shouldBlurImage,
|
||||
blurImageText: Translation.tr("Work safety")
|
||||
};
|
||||
}).filter(Boolean);
|
||||
}
|
||||
else if (root.searchingText.startsWith(Config.options.search.prefix.emojis)) {
|
||||
// Clipboard
|
||||
const searchString = StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.emojis);
|
||||
return Emojis.fuzzyQuery(searchString).map(entry => {
|
||||
const emoji = entry.match(/^\s*(\S+)/)?.[1] || ""
|
||||
return {
|
||||
key: emoji,
|
||||
cliphistRawString: entry,
|
||||
bigText: emoji,
|
||||
name: entry.replace(/^\s*\S+\s+/, ""),
|
||||
clickActionName: "",
|
||||
type: "Emoji",
|
||||
execute: () => {
|
||||
Quickshell.clipboardText = entry.match(/^\s*(\S+)/)?.[1];
|
||||
}
|
||||
};
|
||||
}).filter(Boolean);
|
||||
}
|
||||
|
||||
////////////////// Init ///////////////////
|
||||
nonAppResultsTimer.restart();
|
||||
const mathResultObject = {
|
||||
key: `Math result: ${root.mathResult}`,
|
||||
name: root.mathResult,
|
||||
clickActionName: Translation.tr("Copy"),
|
||||
type: Translation.tr("Math result"),
|
||||
fontType: "monospace",
|
||||
materialSymbol: 'calculate',
|
||||
execute: () => {
|
||||
Quickshell.clipboardText = root.mathResult;
|
||||
}
|
||||
};
|
||||
const appResultObjects = AppSearch.fuzzyQuery(StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.app)).map(entry => {
|
||||
entry.clickActionName = Translation.tr("Launch");
|
||||
entry.type = Translation.tr("App");
|
||||
entry.key = entry.execute
|
||||
return entry;
|
||||
})
|
||||
const commandResultObject = {
|
||||
key: `cmd ${root.searchingText}`,
|
||||
name: StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.shellCommand).replace("file://", ""),
|
||||
clickActionName: Translation.tr("Run"),
|
||||
type: Translation.tr("Run command"),
|
||||
fontType: "monospace",
|
||||
materialSymbol: 'terminal',
|
||||
execute: () => {
|
||||
let cleanedCommand = root.searchingText.replace("file://", "");
|
||||
cleanedCommand = StringUtils.cleanPrefix(cleanedCommand, Config.options.search.prefix.shellCommand);
|
||||
if (cleanedCommand.startsWith(Config.options.search.prefix.shellCommand)) {
|
||||
cleanedCommand = cleanedCommand.slice(Config.options.search.prefix.shellCommand.length);
|
||||
}
|
||||
Quickshell.execDetached(["bash", "-c", searchingText.startsWith('sudo') ? `${Config.options.apps.terminal} fish -C '${cleanedCommand}'` : cleanedCommand]);
|
||||
}
|
||||
};
|
||||
const webSearchResultObject = {
|
||||
key: `website ${root.searchingText}`,
|
||||
name: StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.webSearch),
|
||||
clickActionName: Translation.tr("Search"),
|
||||
type: Translation.tr("Search the web"),
|
||||
materialSymbol: 'travel_explore',
|
||||
execute: () => {
|
||||
let query = StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.webSearch);
|
||||
let url = Config.options.search.engineBaseUrl + query;
|
||||
for (let site of Config.options.search.excludedSites) {
|
||||
url += ` -site:${site}`;
|
||||
}
|
||||
Qt.openUrlExternally(url);
|
||||
}
|
||||
}
|
||||
const launcherActionObjects = root.searchActions.map(action => {
|
||||
const actionString = `${Config.options.search.prefix.action}${action.action}`;
|
||||
if (actionString.startsWith(root.searchingText) || root.searchingText.startsWith(actionString)) {
|
||||
return {
|
||||
key: `Action ${actionString}`,
|
||||
name: root.searchingText.startsWith(actionString) ? root.searchingText : actionString,
|
||||
clickActionName: Translation.tr("Run"),
|
||||
type: Translation.tr("Action"),
|
||||
materialSymbol: 'settings_suggest',
|
||||
execute: () => {
|
||||
action.execute(root.searchingText.split(" ").slice(1).join(" "));
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}).filter(Boolean);
|
||||
|
||||
//////// Prioritized by prefix /////////
|
||||
let result = [];
|
||||
const startsWithNumber = /^\d/.test(root.searchingText);
|
||||
const startsWithMathPrefix = root.searchingText.startsWith(Config.options.search.prefix.math);
|
||||
const startsWithShellCommandPrefix = root.searchingText.startsWith(Config.options.search.prefix.shellCommand);
|
||||
const startsWithWebSearchPrefix = root.searchingText.startsWith(Config.options.search.prefix.webSearch);
|
||||
if (startsWithNumber || startsWithMathPrefix) {
|
||||
result.push(mathResultObject);
|
||||
} else if (startsWithShellCommandPrefix) {
|
||||
result.push(commandResultObject);
|
||||
} else if (startsWithWebSearchPrefix) {
|
||||
result.push(webSearchResultObject);
|
||||
}
|
||||
|
||||
//////////////// Apps //////////////////
|
||||
result = result.concat(appResultObjects);
|
||||
|
||||
////////// Launcher actions ////////////
|
||||
result = result.concat(launcherActionObjects);
|
||||
|
||||
/// Math result, command, web search ///
|
||||
if (Config.options.search.prefix.showDefaultActionsWithoutPrefix) {
|
||||
if (!startsWithShellCommandPrefix) result.push(commandResultObject);
|
||||
if (!startsWithNumber && !startsWithMathPrefix) result.push(mathResultObject);
|
||||
if (!startsWithWebSearchPrefix) result.push(webSearchResultObject);
|
||||
}
|
||||
|
||||
return result;
|
||||
values: LauncherSearch.results
|
||||
onValuesChanged: {
|
||||
root.focusFirstItem();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ BarButton {
|
||||
}
|
||||
}
|
||||
|
||||
AppIcon {
|
||||
WAppIcon {
|
||||
id: iconWidget
|
||||
anchors.centerIn: parent
|
||||
iconName: root.iconName
|
||||
|
||||
@@ -5,17 +5,12 @@ import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WButton {
|
||||
AcrylicButton {
|
||||
id: root
|
||||
|
||||
property var altAction: () => {}
|
||||
property var middleClickAction: () => {}
|
||||
|
||||
colBackground: ColorUtils.transparentize(Looks.colors.bg1)
|
||||
colBackgroundHover: Looks.colors.bg1Hover
|
||||
colBackgroundActive: Looks.colors.bg1Active
|
||||
property color colBackgroundBorder
|
||||
property color color
|
||||
Layout.fillHeight: true
|
||||
topInset: 4
|
||||
bottomInset: 4
|
||||
@@ -23,16 +18,7 @@ WButton {
|
||||
rightInset: 0
|
||||
horizontalPadding: 8
|
||||
|
||||
colBackgroundBorder: ColorUtils.transparentize(Looks.colors.bg1Border, (root.checked || root.hovered) ? Looks.backgroundTransparency : 1)
|
||||
color: {
|
||||
if (root.down) {
|
||||
return root.colBackgroundActive
|
||||
} else if ((root.hovered && !root.down) || root.checked) {
|
||||
return root.colBackgroundHover
|
||||
} else {
|
||||
return root.colBackground
|
||||
}
|
||||
}
|
||||
colBackground: ColorUtils.transparentize(Looks.colors.bg1)
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
@@ -50,15 +36,4 @@ WButton {
|
||||
}
|
||||
}
|
||||
|
||||
background: AcrylicRectangle {
|
||||
shiny: ((root.hovered && !root.down) || root.checked)
|
||||
color: root.color
|
||||
radius: Looks.radius.medium
|
||||
border.width: 1
|
||||
border.color: root.colBackgroundBorder
|
||||
|
||||
Behavior on border.color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,16 @@ import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
// TODO: Replace the icon with QMLized svg (with /usr/lib/qt6/bin/svgtoqml) for proper micro-animation
|
||||
AppButton {
|
||||
id: root
|
||||
|
||||
leftInset: Config.options.waffles.bar.leftAlignApps ? 12 : 0
|
||||
iconName: down ? "start-here-pressed" : "start-here"
|
||||
|
||||
checked: GlobalStates.searchOpen
|
||||
onClicked: {
|
||||
GlobalStates.overviewOpen = !GlobalStates.overviewOpen; // For now...
|
||||
GlobalStates.searchOpen = !GlobalStates.searchOpen;
|
||||
}
|
||||
|
||||
BarToolTip {
|
||||
|
||||
@@ -42,7 +42,7 @@ AppButton {
|
||||
}
|
||||
spacing: 6
|
||||
|
||||
AppIcon {
|
||||
WAppIcon {
|
||||
id: iconWidget
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: root.iconName
|
||||
|
||||
@@ -43,7 +43,7 @@ Button {
|
||||
Layout.fillHeight: false
|
||||
spacing: 8
|
||||
|
||||
AppIcon {
|
||||
WAppIcon {
|
||||
id: appIcon
|
||||
Layout.leftMargin: Looks.radius.large - root.padding + 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WButton {
|
||||
id: root
|
||||
|
||||
colBackground: Looks.colors.bg1
|
||||
colBackgroundHover: Looks.colors.bg1Hover
|
||||
colBackgroundActive: Looks.colors.bg1Active
|
||||
property color colBackgroundBorder
|
||||
property color color
|
||||
property alias border: background.border
|
||||
property alias shinyColor: background.borderColor
|
||||
|
||||
colBackgroundBorder: ColorUtils.transparentize(color, (root.checked || root.hovered) ? Looks.backgroundTransparency : 0)
|
||||
color: {
|
||||
if (root.down) {
|
||||
return root.colBackgroundActive
|
||||
} else if ((root.hovered && !root.down) || root.checked) {
|
||||
return root.colBackgroundHover
|
||||
} else {
|
||||
return root.colBackground
|
||||
}
|
||||
}
|
||||
|
||||
background: AcrylicRectangle {
|
||||
id: background
|
||||
shiny: ((root.hovered && !root.down) || root.checked)
|
||||
color: root.color
|
||||
radius: Looks.radius.medium
|
||||
border.width: 1
|
||||
border.color: root.colBackgroundBorder
|
||||
|
||||
Behavior on border.color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,17 @@ Rectangle {
|
||||
id: root
|
||||
|
||||
property bool shiny: true // Top border
|
||||
property color borderColor: ColorUtils.transparentize(Looks.colors.bg2Border, shiny ? 0.5 : 1)
|
||||
property color borderColor: ColorUtils.transparentize(Looks.colors.bg1Hover, 0.7)
|
||||
property color internalBorderColor: ColorUtils.transparentize(borderColor, shiny ? 0.0 : 1)
|
||||
color: Looks.colors.bg1Hover
|
||||
radius: Looks.radius.medium
|
||||
Behavior on color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
Behavior on borderColor {
|
||||
Behavior on internalBorderColor {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
onBorderColorChanged: {
|
||||
onInternalBorderColorChanged: {
|
||||
borderCanvas.requestPaint();
|
||||
}
|
||||
|
||||
@@ -32,7 +33,7 @@ Rectangle {
|
||||
var ctx = getContext("2d");
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
|
||||
var borderColor = root.borderColor;
|
||||
var borderColor = root.internalBorderColor;
|
||||
|
||||
var r = root.radius;
|
||||
var fadeLength = Math.max(1, r);
|
||||
|
||||
@@ -53,6 +53,8 @@ Singleton {
|
||||
property color controlBgHover: '#57575B'
|
||||
property color controlFg: "#FFFFFF"
|
||||
property color accentUnfocused: "#848484"
|
||||
property color link: "#235CCF"
|
||||
property color inputBg: ColorUtils.transparentize(bg0, 0.4)
|
||||
}
|
||||
darkColors: QtObject {
|
||||
id: darkColors
|
||||
@@ -70,7 +72,7 @@ Singleton {
|
||||
property color bg2: '#8a8a8a'
|
||||
property color bg2Hover: '#b1b1b1'
|
||||
property color bg2Active: '#919191'
|
||||
property color bg2Border: '#c4c4c4'
|
||||
property color bg2Border: '#bdbdbd'
|
||||
property color subfg: "#CED1D7"
|
||||
property color fg: "#FFFFFF"
|
||||
property color fg1: "#D1D1D1"
|
||||
@@ -80,6 +82,8 @@ Singleton {
|
||||
property color controlBgHover: "#CFCED1"
|
||||
property color controlFg: "#454545"
|
||||
property color accentUnfocused: "#989898"
|
||||
property color link: "#A7C9FC"
|
||||
property color inputBg: ColorUtils.transparentize(darkColors.bg0, 0.5)
|
||||
}
|
||||
colors: QtObject {
|
||||
id: colors
|
||||
@@ -110,6 +114,8 @@ Singleton {
|
||||
property color controlBg: root.dark ? root.darkColors.controlBg : root.lightColors.controlBg
|
||||
property color controlBgHover: root.dark ? root.darkColors.controlBgHover : root.lightColors.controlBgHover
|
||||
property color controlFg: root.dark ? root.darkColors.controlFg : root.lightColors.controlFg
|
||||
property color inputBg: root.dark ? root.darkColors.inputBg : root.lightColors.inputBg
|
||||
property color link: root.dark ? root.darkColors.link : root.lightColors.link
|
||||
property color danger: "#C42B1C"
|
||||
property color dangerActive: "#B62D1F"
|
||||
property color warning: "#FF9900"
|
||||
@@ -118,6 +124,7 @@ Singleton {
|
||||
property color accentActive: Appearance.colors.colPrimaryActive
|
||||
property color accentUnfocused: root.dark ? root.darkColors.accentUnfocused : root.lightColors.accentUnfocused
|
||||
property color accentFg: ColorUtils.isDark(accent) ? "#FFFFFF" : "#000000"
|
||||
property color selection: Appearance.colors.colPrimaryContainer
|
||||
}
|
||||
|
||||
radius: QtObject {
|
||||
|
||||
-1
@@ -2,7 +2,6 @@ import QtQuick
|
||||
import org.kde.kirigami as Kirigami
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Kirigami.Icon {
|
||||
id: root
|
||||
@@ -53,10 +53,11 @@ Button {
|
||||
// Hover stuff
|
||||
signal hoverTimedOut
|
||||
property bool shouldShowTooltip: false
|
||||
ToolTip.delay: 400
|
||||
property Timer hoverTimer: Timer {
|
||||
id: hoverTimer
|
||||
running: root.hovered
|
||||
interval: 400
|
||||
interval: root.ToolTip.delay
|
||||
onTriggered: {
|
||||
root.hoverTimedOut();
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@ import qs.services
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
function pathForName(iconName) {
|
||||
return Quickshell.shellPath(`assets/icons/fluent/${iconName}.svg`);
|
||||
}
|
||||
|
||||
function wifiIconForStrength(strength) {
|
||||
if (strength > 75)
|
||||
return "wifi-1";
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Menu {
|
||||
id: root
|
||||
|
||||
property bool downDirection: false
|
||||
property bool hasIcons: false // TODO: implement
|
||||
|
||||
implicitWidth: background.implicitWidth + root.padding * 2
|
||||
implicitHeight: background.implicitHeight + root.padding * 2
|
||||
padding: 3
|
||||
property real sourceEdgeMargin: -implicitHeight
|
||||
clip: true
|
||||
|
||||
enter: Transition {
|
||||
NumberAnimation {
|
||||
property: "sourceEdgeMargin"
|
||||
from: -root.implicitHeight
|
||||
to: root.padding
|
||||
duration: 200
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
}
|
||||
exit: Transition {
|
||||
NumberAnimation {
|
||||
property: "sourceEdgeMargin"
|
||||
from: root.padding
|
||||
to: -root.implicitHeight
|
||||
duration: 150
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut
|
||||
}
|
||||
}
|
||||
|
||||
background: WPane {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: root.downDirection ? parent.top : undefined
|
||||
bottom: root.downDirection ? undefined : parent.bottom
|
||||
margins: root.padding
|
||||
topMargin: root.downDirection ? root.sourceEdgeMargin : root.padding
|
||||
bottomMargin: root.downDirection ? root.padding : root.sourceEdgeMargin
|
||||
}
|
||||
contentItem: Rectangle {
|
||||
color: Looks.colors.bg1Base
|
||||
implicitWidth: menuListView.implicitWidth + root.padding * 2
|
||||
implicitHeight: root.contentItem.implicitHeight + root.padding * 2
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: ListView {
|
||||
id: menuListView
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: root.downDirection ? parent.top : undefined
|
||||
bottom: root.downDirection ? undefined : parent.bottom
|
||||
margins: root.padding * 2
|
||||
topMargin: root.downDirection ? root.sourceEdgeMargin : root.padding
|
||||
bottomMargin: root.downDirection ? root.padding : root.sourceEdgeMargin
|
||||
}
|
||||
implicitHeight: contentHeight
|
||||
implicitWidth: Array.from({
|
||||
length: count
|
||||
}, (_, i) => itemAtIndex(i)?.implicitWidth ?? 0).reduce((a, b) => a > b ? a : b)
|
||||
|
||||
model: root.contentModel
|
||||
}
|
||||
|
||||
delegate: WMenuItem {
|
||||
id: menuItemDelegate
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
MenuItem {
|
||||
id: root
|
||||
|
||||
property color colBackground: ColorUtils.transparentize(Looks.colors.bg1)
|
||||
property color colBackgroundHover: Looks.colors.bg2Hover
|
||||
property color colBackgroundActive: Looks.colors.bg2Active
|
||||
property color colBackgroundToggled: Looks.colors.accent
|
||||
property color colBackgroundToggledHover: Looks.colors.accentHover
|
||||
property color colBackgroundToggledActive: Looks.colors.accentActive
|
||||
property color colForeground: Looks.colors.fg
|
||||
property color colForegroundToggled: Looks.colors.accentFg
|
||||
property color colForegroundDisabled: ColorUtils.transparentize(Looks.colors.subfg, 0.4)
|
||||
property color color: {
|
||||
if (!root.enabled)
|
||||
return colBackground;
|
||||
if (root.checked) {
|
||||
if (root.down) {
|
||||
return root.colBackgroundToggledActive;
|
||||
} else if (root.hovered) {
|
||||
return root.colBackgroundToggledHover;
|
||||
} else {
|
||||
return root.colBackgroundToggled;
|
||||
}
|
||||
}
|
||||
if (root.down) {
|
||||
return root.colBackgroundActive;
|
||||
} else if (root.hovered) {
|
||||
return root.colBackgroundHover;
|
||||
} else {
|
||||
return root.colBackground;
|
||||
}
|
||||
}
|
||||
property color fgColor: {
|
||||
if (root.checked)
|
||||
return root.colForegroundToggled;
|
||||
if (root.enabled)
|
||||
return root.colForeground;
|
||||
return root.colForegroundDisabled;
|
||||
}
|
||||
|
||||
property real inset: 2
|
||||
topInset: inset
|
||||
bottomInset: inset
|
||||
leftInset: inset
|
||||
rightInset: inset
|
||||
horizontalPadding: 11
|
||||
|
||||
background: Rectangle {
|
||||
id: backgroundRect
|
||||
radius: Looks.radius.medium
|
||||
color: root.color
|
||||
Behavior on color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
implicitHeight: Math.max(28, contentItem.implicitHeight) + topInset + bottomInset
|
||||
implicitWidth: contentItem.implicitWidth + leftInset + rightInset + leftPadding + rightPadding
|
||||
|
||||
contentItem: RowLayout {
|
||||
id: contentLayout
|
||||
spacing: 12
|
||||
FluentIcon {
|
||||
id: buttonIcon
|
||||
monochrome: true
|
||||
implicitSize: 20
|
||||
Layout.fillWidth: false
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: root.fgColor
|
||||
visible: root.icon.name !== "";
|
||||
icon: root.icon.name
|
||||
}
|
||||
WText {
|
||||
id: buttonText
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||
text: root.text
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
color: root.fgColor
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import qs.modules.waffle.looks
|
||||
|
||||
Item {
|
||||
id: root
|
||||
required property Item contentItem
|
||||
property Item contentItem
|
||||
property real radius: Looks.radius.large
|
||||
property alias border: borderRect
|
||||
property alias borderColor: borderRect.border.color
|
||||
|
||||
@@ -8,8 +8,11 @@ Text {
|
||||
color: Looks.colors.fg
|
||||
|
||||
font {
|
||||
hintingPreference: Font.PreferFullHinting
|
||||
family: Looks.font.family.ui
|
||||
pixelSize: Looks.font.pixelSize.normal
|
||||
weight: Looks.font.weight.regular
|
||||
}
|
||||
|
||||
linkColor: Looks.colors.link
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
TextInput {
|
||||
id: root
|
||||
renderType: Text.NativeRendering
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: Looks.colors.fg
|
||||
|
||||
font {
|
||||
hintingPreference: Font.PreferFullHinting
|
||||
family: Looks.font.family.ui
|
||||
pixelSize: Looks.font.pixelSize.large
|
||||
weight: Looks.font.weight.regular
|
||||
}
|
||||
|
||||
selectionColor: Looks.colors.selection
|
||||
}
|
||||
@@ -25,6 +25,8 @@ StyledToolTip {
|
||||
verticalPadding: 8
|
||||
horizontalPadding: 10
|
||||
|
||||
delay: 400
|
||||
|
||||
contentItem: WToolTipContent {
|
||||
id: tooltipContent
|
||||
realContentItem: root.realContentItem
|
||||
|
||||
@@ -6,6 +6,7 @@ Item {
|
||||
id: root
|
||||
anchors.centerIn: parent
|
||||
required property Item realContentItem
|
||||
property alias radius: realContent.radius
|
||||
property real verticalPadding: 8
|
||||
property real horizontalPadding: 10
|
||||
implicitWidth: realContent.implicitWidth + 2 * 2
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
StyledImage {
|
||||
id: avatar
|
||||
Layout.alignment: Qt.AlignTop
|
||||
sourceSize: Qt.size(32, 32)
|
||||
source: Directories.userAvatarPathAccountsService
|
||||
fallbacks: [Directories.userAvatarPathRicersAndWeirdSystems, Directories.userAvatarPathRicersAndWeirdSystems2]
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Circle {
|
||||
diameter: avatar.height
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
-3
@@ -20,6 +20,7 @@ WBarAttachedPanelContent {
|
||||
property bool collapsed: false
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
id: contentLayout
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: parent.top
|
||||
@@ -41,9 +42,24 @@ WBarAttachedPanelContent {
|
||||
}
|
||||
contentItem: NotificationPaneContent {
|
||||
implicitWidth: calendarColumnLayout.implicitWidth
|
||||
implicitHeight: Notifications.list.length > 0 ? (notificationArea.height - notificationPane.borderWidth * 2) : 230
|
||||
implicitHeight: {
|
||||
if (Notifications.list.length > 0) {
|
||||
return ((contentLayout.height - calendarPane.height - contentLayout.spacing) - notificationPane.borderWidth * 2)
|
||||
}
|
||||
return 230;
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: enableTimer
|
||||
interval: Config.options.hacks.arbitraryRaceConditionDelay
|
||||
onTriggered: heightBehavior.enabled = true;
|
||||
}
|
||||
Behavior on implicitHeight {
|
||||
id: heightBehavior
|
||||
enabled: false
|
||||
Component.onCompleted: {
|
||||
enableTimer.restart();
|
||||
}
|
||||
animation: Looks.transition.enter.createObject(this)
|
||||
}
|
||||
}
|
||||
@@ -51,9 +67,9 @@ WBarAttachedPanelContent {
|
||||
}
|
||||
|
||||
WPane {
|
||||
contentItem: ColumnLayout {
|
||||
id: calendarPane
|
||||
contentItem: WPanelPageColumn {
|
||||
id: calendarColumnLayout
|
||||
spacing: 0
|
||||
DateHeader {
|
||||
Layout.fillWidth: true
|
||||
Synchronizer on collapsed {
|
||||
|
||||
+12
-6
@@ -8,18 +8,24 @@ import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WBorderlessButton {
|
||||
id: headerButton
|
||||
id: root
|
||||
Layout.fillWidth: false
|
||||
implicitWidth: 16
|
||||
implicitHeight: 16
|
||||
property real implicitSize: 16
|
||||
implicitWidth: implicitSize
|
||||
implicitHeight: implicitSize
|
||||
color: "transparent"
|
||||
colForeground: root.hovered && !root.pressed ? Looks.colors.fg : Looks.colors.fg1
|
||||
|
||||
Behavior on colForeground {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
FluentIcon {
|
||||
anchors.centerIn: parent
|
||||
implicitSize: 16
|
||||
icon: headerButton.icon.name
|
||||
color: headerButton.hovered && !headerButton.pressed ? Looks.colors.fg : Looks.colors.fg1
|
||||
implicitSize: root.implicitSize
|
||||
icon: root.icon.name
|
||||
color: root.colForeground
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-1
@@ -2,18 +2,27 @@ import QtQuick
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
SmallBorderedIconButton {
|
||||
AcrylicButton {
|
||||
id: root
|
||||
|
||||
property bool iconVisible: true
|
||||
property string iconName: ""
|
||||
property bool iconFilled: true
|
||||
|
||||
colBackground: Looks.colors.bg2
|
||||
colBackgroundHover: Looks.colors.bg2Hover
|
||||
colBackgroundActive: Looks.colors.bg2Active
|
||||
property color colBorder: Looks.colors.bg2Border
|
||||
property color colBorderToggled: Looks.colors.accent
|
||||
border.color: checked ? colBorderToggled : colBorder
|
||||
|
||||
leftPadding: 12
|
||||
rightPadding: 12
|
||||
implicitWidth: focusButtonContent.implicitWidth + leftPadding + rightPadding
|
||||
implicitHeight: 24
|
||||
|
||||
contentItem: Row {
|
||||
id: focusButtonContent
|
||||
|
||||
+14
-1
@@ -7,11 +7,13 @@ import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
// TODO: Swipe to dismiss
|
||||
MouseArea {
|
||||
id: root
|
||||
|
||||
required property var notificationGroup
|
||||
readonly property var notifications: notificationGroup?.notifications ?? []
|
||||
property bool expanded: false
|
||||
|
||||
implicitWidth: contentLayout.implicitWidth
|
||||
implicitHeight: contentLayout.implicitHeight
|
||||
@@ -34,12 +36,23 @@ MouseArea {
|
||||
interactive: false
|
||||
spacing: 4
|
||||
model: ScriptModel {
|
||||
values: root.notifications.slice().reverse()
|
||||
values: root.expanded ? root.notifications.slice().reverse() : root.notifications.slice(-1)
|
||||
objectProp: "notificationId"
|
||||
}
|
||||
delegate: WSingleNotification {
|
||||
required property int index
|
||||
required property var modelData
|
||||
width: ListView.view.width
|
||||
notification: modelData
|
||||
groupExpandControlMessage: {
|
||||
if (root.notifications.length <= 1) return "";
|
||||
if (!root.expanded) return Translation.tr("+%1 notifications").arg(root.notifications.length - 1);
|
||||
if (index === root.notifications.length - 1) return Translation.tr("See fewer");
|
||||
return "";
|
||||
}
|
||||
onGroupExpandToggle: {
|
||||
root.expanded = !root.expanded;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+194
-15
@@ -1,3 +1,4 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
@@ -12,11 +13,18 @@ MouseArea {
|
||||
id: root
|
||||
|
||||
required property var notification
|
||||
property bool expanded: false
|
||||
property bool expanded: notification.actions.length > 0
|
||||
property string groupExpandControlMessage: ""
|
||||
signal groupExpandToggle
|
||||
hoverEnabled: true
|
||||
|
||||
implicitHeight: contentItem.implicitHeight
|
||||
implicitWidth: contentItem.implicitWidth
|
||||
|
||||
Behavior on implicitHeight {
|
||||
animation: Looks.transition.enter.createObject(this)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: contentItem
|
||||
anchors.fill: parent
|
||||
@@ -26,34 +34,205 @@ MouseArea {
|
||||
implicitHeight: notificationContent.implicitHeight + padding * 2
|
||||
implicitWidth: notificationContent.implicitWidth + padding * 2
|
||||
border.width: 1
|
||||
border.color: Looks.applyContentTransparency(Looks.colors.ambientShadow)
|
||||
border.color: ColorUtils.applyAlpha(Looks.colors.ambientShadow, 0.1)
|
||||
|
||||
ColumnLayout {
|
||||
id: notificationContent
|
||||
anchors.fill: parent
|
||||
anchors.margins: contentItem.padding
|
||||
spacing: 19
|
||||
|
||||
RowLayout {
|
||||
// Header
|
||||
SingleNotificationHeader {
|
||||
Layout.fillWidth: true
|
||||
WText {
|
||||
text: NotificationUtils.getFriendlyNotifTimeString(root.notification?.time)
|
||||
}
|
||||
|
||||
// Content
|
||||
Item {
|
||||
id: actualContent
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
property real spacing: 16
|
||||
implicitHeight: Math.max(contentColumn.implicitHeight, imageLoader.height)
|
||||
implicitWidth: contentColumn.implicitWidth
|
||||
|
||||
Loader {
|
||||
id: imageLoader
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
}
|
||||
active: root.notification.image != ""
|
||||
sourceComponent: StyledImage {
|
||||
readonly property int size: 48
|
||||
width: size
|
||||
height: size
|
||||
sourceSize.width: size
|
||||
sourceSize.height: size
|
||||
source: root.notification.image
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
spacing: 3
|
||||
|
||||
SummaryText {
|
||||
id: summaryText
|
||||
Layout.leftMargin: imageLoader.active ? imageLoader.width + actualContent.spacing : 0
|
||||
}
|
||||
BodyText {
|
||||
Layout.leftMargin: imageLoader.active ? imageLoader.width + actualContent.spacing : 0
|
||||
// onLineLaidOut: (line) => {
|
||||
// if (!imageLoader.active) return;
|
||||
// const dodgeDistance = imageLoader.width + actualContent.spacing;
|
||||
// // print(line.y, dodgeDistance)
|
||||
// if (summaryText.height + line.y > dodgeDistance) {
|
||||
// line.x -= dodgeDistance;
|
||||
// line.width += dodgeDistance;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
// Actions
|
||||
ActionsRow {
|
||||
Layout.fillWidth: true
|
||||
WText {
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
text: root.notification.summary
|
||||
}
|
||||
WText {
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
// "+1 notifications" button
|
||||
GroupExpandButton {
|
||||
Layout.bottomMargin: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component SingleNotificationHeader: RowLayout {
|
||||
ExpandButton {
|
||||
Layout.topMargin: -2
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NotificationHeaderButton {
|
||||
Layout.rightMargin: 4
|
||||
opacity: root.containsMouse ? 1 : 0
|
||||
icon.name: "dismiss"
|
||||
implicitSize: 12
|
||||
onClicked: {
|
||||
Qt.callLater(() => {
|
||||
Notifications.discardNotification(root.notification?.notificationId);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component ActionsRow: RowLayout {
|
||||
visible: root.expanded && root.notification.actions.length > 0
|
||||
uniformCellSizes: true
|
||||
Repeater {
|
||||
id: actionRepeater
|
||||
model: root.notification.actions
|
||||
delegate: WBorderedButton {
|
||||
id: actionButton
|
||||
Layout.fillHeight: true
|
||||
required property var modelData
|
||||
Layout.fillWidth: true
|
||||
verticalPadding: 16
|
||||
horizontalPadding: 12
|
||||
text: modelData.text
|
||||
implicitHeight: actionButtonText.implicitHeight + verticalPadding * 2
|
||||
contentItem: WText {
|
||||
id: actionButtonText
|
||||
text: actionButton.text
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.Wrap
|
||||
maximumLineCount: root.expanded ? 100 : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component SummaryText: WText {
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
text: root.notification?.summary
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
}
|
||||
|
||||
component BodyText: WText {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignTop
|
||||
wrapMode: Text.Wrap
|
||||
maximumLineCount: root.expanded ? 100 : 1
|
||||
text: {
|
||||
if (root.expanded)
|
||||
return `<style>img{max-width:${summaryText.width}px; align: right}</style>` + `${NotificationUtils.processNotificationBody(root.notification.body, root.notification.appName || root.notification.summary).replace(/\n/g, "<br/>")}`;
|
||||
return NotificationUtils.processNotificationBody(root.notification.body, root.notification.appName || root.notification.summary).replace(/\n/g, "<br/>");
|
||||
}
|
||||
color: Looks.colors.subfg
|
||||
textFormat: root.expanded ? Text.RichText : Text.StyledText
|
||||
onLinkActivated: link => {
|
||||
Qt.openUrlExternally(link);
|
||||
GlobalStates.sidebarRightOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
component ExpandButton: NotificationHeaderButton {
|
||||
id: expandButton
|
||||
implicitWidth: expandButtonContent.implicitWidth
|
||||
onClicked: root.expanded = !root.expanded
|
||||
|
||||
contentItem: Item {
|
||||
id: expandButtonContent
|
||||
implicitWidth: expandButtonRow.implicitWidth
|
||||
implicitHeight: expandButtonRow.implicitHeight
|
||||
RowLayout {
|
||||
id: expandButtonRow
|
||||
anchors.centerIn: parent
|
||||
spacing: 8
|
||||
WText {
|
||||
color: expandButton.colForeground
|
||||
text: NotificationUtils.getFriendlyNotifTimeString(root.notification?.time)
|
||||
}
|
||||
FluentIcon {
|
||||
Layout.rightMargin: 12
|
||||
icon: "chevron-down"
|
||||
implicitSize: 18
|
||||
rotation: root.expanded ? -180 : 0
|
||||
color: expandButton.colForeground
|
||||
Behavior on rotation {
|
||||
animation: Looks.transition.rotate.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component GroupExpandButton: AcrylicButton {
|
||||
id: groupExpandButton
|
||||
visible: root.groupExpandControlMessage !== ""
|
||||
horizontalPadding: 10
|
||||
implicitHeight: 24
|
||||
implicitWidth: expandButtonText.implicitWidth + horizontalPadding * 2
|
||||
onClicked: root.groupExpandToggle()
|
||||
contentItem: Item {
|
||||
WText {
|
||||
id: expandButtonText
|
||||
anchors.centerIn: parent
|
||||
text: root.groupExpandControlMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
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
|
||||
|
||||
FooterRectangle {
|
||||
id: root
|
||||
|
||||
property bool searching: text.length > 0
|
||||
property alias text: searchInput.text
|
||||
|
||||
Component.onCompleted: searchInput.forceActiveFocus()
|
||||
|
||||
focus: true
|
||||
color: searching ? Looks.colors.bgPanelBody : Looks.colors.bgPanelFooter
|
||||
|
||||
implicitWidth: 832 // TODO: Make sizes naturally inferred
|
||||
implicitHeight: 63
|
||||
|
||||
Rectangle {
|
||||
id: outline
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: 32
|
||||
rightMargin: 32
|
||||
topMargin: 16
|
||||
bottomMargin: 15
|
||||
}
|
||||
color: "transparent"
|
||||
radius: height / 2
|
||||
border.width: 1
|
||||
border.color: Looks.colors.bg2Border
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: searchInputBg
|
||||
anchors.fill: outline
|
||||
anchors.margins: 1
|
||||
radius: height / 2
|
||||
color: Looks.colors.inputBg
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 11
|
||||
|
||||
WAppIcon {
|
||||
Layout.leftMargin: 14
|
||||
iconName: "system-search-checked"
|
||||
separateLightDark: true
|
||||
implicitSize: 18
|
||||
}
|
||||
|
||||
WTextInput {
|
||||
id: searchInput
|
||||
focus: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
WText {
|
||||
anchors {
|
||||
left: parent.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
color: Looks.colors.accentUnfocused
|
||||
text: Translation.tr("Search for apps") // should also have "", settings, and documents" but we don't have those
|
||||
visible: searchInput.text.length === 0
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.IBeamCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
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
|
||||
|
||||
BodyRectangle {
|
||||
id: root
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
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
|
||||
|
||||
WBarAttachedPanelContent {
|
||||
id: root
|
||||
|
||||
property bool searching: false
|
||||
property string searchText: ""
|
||||
|
||||
contentItem: WPane {
|
||||
contentItem: WPanelPageColumn {
|
||||
SearchBar {
|
||||
focus: true
|
||||
Layout.fillWidth: true
|
||||
Synchronizer on searching {
|
||||
property alias target: root.searching
|
||||
}
|
||||
Synchronizer on text {
|
||||
property alias source: root.searchText
|
||||
}
|
||||
}
|
||||
Loader {
|
||||
id: pageContentLoader
|
||||
Layout.fillWidth: true
|
||||
source: root.searching ? "SearchPageContent.qml" : "StartPageContent.qml"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WPanelPageColumn {
|
||||
id: root
|
||||
|
||||
WPanelSeparator {}
|
||||
|
||||
BodyRectangle {
|
||||
implicitHeight: 736 // TODO: Make sizes naturally inferred
|
||||
}
|
||||
|
||||
WPanelSeparator {}
|
||||
|
||||
StartFooter {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
component StartFooter: FooterRectangle {
|
||||
implicitHeight: 63
|
||||
|
||||
UserButton {
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: 52
|
||||
bottom: parent.bottom
|
||||
bottomMargin: 12
|
||||
}
|
||||
}
|
||||
|
||||
PowerButton {
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: 52
|
||||
bottom: parent.bottom
|
||||
bottomMargin: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component UserButton: WBorderlessButton {
|
||||
id: userButton
|
||||
implicitWidth: userButtonRow.implicitWidth + 12 * 2
|
||||
implicitHeight: 40
|
||||
|
||||
contentItem: Item {
|
||||
RowLayout {
|
||||
id: userButtonRow
|
||||
anchors.centerIn: parent
|
||||
spacing: 12
|
||||
|
||||
WUserAvatar {
|
||||
sourceSize: Qt.size(32, 32)
|
||||
}
|
||||
WText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: SystemInfo.username
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
userMenu.open();
|
||||
}
|
||||
|
||||
WToolTip {
|
||||
text: SystemInfo.username
|
||||
}
|
||||
|
||||
Popup {
|
||||
id: userMenu
|
||||
x: -51
|
||||
y: -userMenu.implicitHeight + userButton.implicitHeight / 2 - 10
|
||||
|
||||
background: null
|
||||
|
||||
WToolTipContent {
|
||||
id: popupContent
|
||||
horizontalPadding: 10
|
||||
verticalPadding: 7
|
||||
radius: Looks.radius.large
|
||||
realContentItem: Item {
|
||||
implicitWidth: userMenuContentLayout.implicitWidth
|
||||
implicitHeight: userMenuContentLayout.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: userMenuContentLayout
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: popupContent.horizontalPadding
|
||||
rightMargin: popupContent.horizontalPadding
|
||||
topMargin: popupContent.verticalPadding
|
||||
bottomMargin: popupContent.verticalPadding
|
||||
}
|
||||
spacing: 5
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 6
|
||||
FluentIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
implicitSize: 22
|
||||
icon: "corporation"
|
||||
monochrome: false
|
||||
}
|
||||
WText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: "Megahard"
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
font.weight: Looks.font.weight.strong
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
WBorderlessButton {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
implicitHeight: 36
|
||||
implicitWidth: textItem.implicitWidth + 10 * 2
|
||||
contentItem: WText {
|
||||
id: textItem
|
||||
text: Translation.tr("Sign out")
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
}
|
||||
onClicked: Session.logout()
|
||||
}
|
||||
}
|
||||
Item { // Force min width 360 (using min on the item somehow doesn't work)
|
||||
implicitWidth: 334
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 7
|
||||
Layout.leftMargin: 6
|
||||
spacing: 12
|
||||
WUserAvatar {
|
||||
sourceSize: Qt.size(58, 58)
|
||||
}
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: 2
|
||||
WText {
|
||||
text: SystemInfo.username
|
||||
font.pixelSize: Looks.font.pixelSize.larger
|
||||
font.weight: Looks.font.weight.strong
|
||||
}
|
||||
WText {
|
||||
color: Looks.colors.fg1
|
||||
text: Translation.tr("Local account")
|
||||
}
|
||||
WText {
|
||||
color: Looks.colors.accent
|
||||
text: Translation.tr("Manage my account")
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Quickshell.execDetached(["bash", "-c", Config.options.apps.manageUser])
|
||||
GlobalStates.searchOpen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component PowerButton: WBorderlessButton {
|
||||
id: powerButton
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
contentItem: Item {
|
||||
FluentIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: "power"
|
||||
implicitSize: 20
|
||||
}
|
||||
}
|
||||
|
||||
WToolTip {
|
||||
extraVisibleCondition: !powerMenu.visible
|
||||
text: qsTr("Power")
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
powerMenu.open()
|
||||
}
|
||||
|
||||
WMenu {
|
||||
id: powerMenu
|
||||
x: -powerMenu.implicitWidth / 2 + powerButton.implicitWidth / 2
|
||||
y: -powerMenu.implicitHeight - 4
|
||||
Action {
|
||||
icon.name: "lock-closed"
|
||||
text: Translation.tr("Lock")
|
||||
onTriggered: Session.lock()
|
||||
}
|
||||
Action {
|
||||
icon.name: "weather-moon"
|
||||
text: Translation.tr("Sleep")
|
||||
onTriggered: Session.suspend()
|
||||
}
|
||||
Action {
|
||||
icon.name: "power"
|
||||
text: Translation.tr("Shut down")
|
||||
onTriggered: Session.poweroff()
|
||||
}
|
||||
Action {
|
||||
icon.name: "arrow-counterclockwise"
|
||||
text: Translation.tr("Restart")
|
||||
onTriggered: Session.reboot()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
|
||||
function onSearchOpenChanged() {
|
||||
if (GlobalStates.searchOpen)
|
||||
panelLoader.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: panelLoader
|
||||
active: GlobalStates.searchOpen
|
||||
sourceComponent: PanelWindow {
|
||||
id: panelWindow
|
||||
exclusiveZone: 0
|
||||
WlrLayershell.namespace: "quickshell:wStartMenu"
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
color: "transparent"
|
||||
|
||||
anchors {
|
||||
bottom: Config.options.waffles.bar.bottom
|
||||
top: !Config.options.waffles.bar.bottom
|
||||
left: Config.options.waffles.bar.leftAlignApps
|
||||
}
|
||||
|
||||
implicitWidth: content.implicitWidth
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
HyprlandFocusGrab {
|
||||
id: focusGrab
|
||||
active: true
|
||||
windows: [panelWindow]
|
||||
onCleared: content.close()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onSearchOpenChanged() {
|
||||
if (!GlobalStates.searchOpen)
|
||||
content.close();
|
||||
}
|
||||
}
|
||||
|
||||
StartMenuContent {
|
||||
id: content
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
|
||||
onClosed: {
|
||||
GlobalStates.searchOpen = false;
|
||||
panelLoader.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "search"
|
||||
|
||||
function toggle() {
|
||||
GlobalStates.searchOpen = !GlobalStates.searchOpen;
|
||||
}
|
||||
function close() {
|
||||
GlobalStates.searchOpen = false;
|
||||
}
|
||||
function open() {
|
||||
GlobalStates.searchOpen = true;
|
||||
}
|
||||
function toggleReleaseInterrupt() {
|
||||
GlobalStates.superReleaseMightTrigger = false;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "searchToggle"
|
||||
description: "Toggles search on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.searchOpen = !GlobalStates.searchOpen;
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "searchToggleRelease"
|
||||
description: "Toggles search on release"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.superReleaseMightTrigger = true;
|
||||
}
|
||||
|
||||
onReleased: {
|
||||
if (!GlobalStates.superReleaseMightTrigger) {
|
||||
GlobalStates.superReleaseMightTrigger = true;
|
||||
return;
|
||||
}
|
||||
GlobalStates.searchOpen = !GlobalStates.searchOpen;
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "searchToggleReleaseInterrupt"
|
||||
description: "Interrupts possibility of search being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything."
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.superReleaseMightTrigger = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user