mirror of
https://github.com/end-4/dots-hyprland.git
synced 2026-06-05 23:09:26 -05:00
Merge branch 'main' into featOverlay/MangoHud-Fps-Limiter
This commit is contained in:
@@ -23,3 +23,5 @@ exec-once = wl-paste --type image --watch bash -c 'cliphist store && qs -c $qsCo
|
||||
# Cursor
|
||||
exec-once = hyprctl setcursor Bibata-Modern-Classic 24
|
||||
|
||||
# Fix dock pinned apps not launching properly (https://github.com/end-4/dots-hyprland/issues/2200)
|
||||
exec-once = sleep 3.5 && hyprctl reload && sleep 0.5 && touch ~/.config/quickshell/ii/shell.qml
|
||||
|
||||
@@ -13,121 +13,60 @@ StyledPopup {
|
||||
spacing: 4
|
||||
|
||||
// Header
|
||||
Row {
|
||||
id: header
|
||||
spacing: 5
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
fill: 0
|
||||
font.weight: Font.Medium
|
||||
text: "battery_android_full"
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Battery"
|
||||
font {
|
||||
weight: Font.Medium
|
||||
pixelSize: Appearance.font.pixelSize.normal
|
||||
}
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
StyledPopupHeaderRow {
|
||||
icon: "battery_android_full"
|
||||
label: Translation.tr("Battery")
|
||||
}
|
||||
|
||||
// This row is hidden when the battery is full.
|
||||
RowLayout {
|
||||
spacing: 5
|
||||
Layout.fillWidth: true
|
||||
property bool rowVisible: {
|
||||
StyledPopupValueRow {
|
||||
visible: {
|
||||
let timeValue = Battery.isCharging ? Battery.timeToFull : Battery.timeToEmpty;
|
||||
let power = Battery.energyRate;
|
||||
return !(Battery.chargeState == 4 || timeValue <= 0 || power <= 0.01);
|
||||
}
|
||||
visible: rowVisible
|
||||
opacity: rowVisible ? 1 : 0
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 500
|
||||
icon: "schedule"
|
||||
label: Battery.isCharging ? Translation.tr("Time to full:") : Translation.tr("Time to empty:")
|
||||
value: {
|
||||
function formatTime(seconds) {
|
||||
var h = Math.floor(seconds / 3600);
|
||||
var m = Math.floor((seconds % 3600) / 60);
|
||||
if (h > 0)
|
||||
return `${h}h, ${m}m`;
|
||||
else
|
||||
return `${m}m`;
|
||||
}
|
||||
if (Battery.isCharging)
|
||||
return formatTime(Battery.timeToFull);
|
||||
else
|
||||
return formatTime(Battery.timeToEmpty);
|
||||
}
|
||||
}
|
||||
|
||||
StyledPopupValueRow {
|
||||
visible: !(Battery.chargeState != 4 && Battery.energyRate == 0)
|
||||
icon: "bolt"
|
||||
label: {
|
||||
if (Battery.chargeState == 4) {
|
||||
return Translation.tr("Fully charged");
|
||||
} else if (Battery.chargeState == 1) {
|
||||
return Translation.tr("Charging:");
|
||||
} else {
|
||||
return Translation.tr("Discharging:");
|
||||
}
|
||||
}
|
||||
|
||||
MaterialSymbol {
|
||||
text: "schedule"
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
}
|
||||
StyledText {
|
||||
text: Battery.isCharging ? Translation.tr("Time to full:") : Translation.tr("Time to empty:")
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
text: {
|
||||
function formatTime(seconds) {
|
||||
var h = Math.floor(seconds / 3600);
|
||||
var m = Math.floor((seconds % 3600) / 60);
|
||||
if (h > 0)
|
||||
return `${h}h, ${m}m`;
|
||||
else
|
||||
return `${m}m`;
|
||||
}
|
||||
if (Battery.isCharging)
|
||||
return formatTime(Battery.timeToFull);
|
||||
else
|
||||
return formatTime(Battery.timeToEmpty);
|
||||
value: {
|
||||
if (Battery.chargeState == 4) {
|
||||
return "";
|
||||
} else {
|
||||
return `${Battery.energyRate.toFixed(2)}W`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 5
|
||||
Layout.fillWidth: true
|
||||
|
||||
property bool rowVisible: !(Battery.chargeState != 4 && Battery.energyRate == 0)
|
||||
visible: rowVisible
|
||||
opacity: rowVisible ? 1 : 0
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 500
|
||||
}
|
||||
}
|
||||
|
||||
MaterialSymbol {
|
||||
text: "bolt"
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (Battery.chargeState == 4) {
|
||||
return Translation.tr("Fully charged");
|
||||
} else if (Battery.chargeState == 1) {
|
||||
return Translation.tr("Charging:");
|
||||
} else {
|
||||
return Translation.tr("Discharging:");
|
||||
}
|
||||
}
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
text: {
|
||||
if (Battery.chargeState == 4) {
|
||||
return "";
|
||||
} else {
|
||||
return `${Battery.energyRate.toFixed(2)}W`;
|
||||
}
|
||||
}
|
||||
}
|
||||
StyledPopupValueRow {
|
||||
icon: "heart_check"
|
||||
label: Translation.tr("Health:")
|
||||
value: `${(Battery.health).toFixed(1)}%`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ Item {
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
|
||||
ClockWidgetTooltip {
|
||||
ClockWidgetPopup {
|
||||
hoverTarget: mouseArea
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
StyledPopup {
|
||||
id: root
|
||||
property string formattedDate: Qt.locale().toString(DateTime.clock.date, "dddd, MMMM dd, yyyy")
|
||||
property string formattedTime: DateTime.time
|
||||
property string formattedUptime: DateTime.uptime
|
||||
property string todosSection: getUpcomingTodos()
|
||||
|
||||
function getUpcomingTodos() {
|
||||
const unfinishedTodos = Todo.list.filter(function (item) {
|
||||
return !item.done;
|
||||
});
|
||||
if (unfinishedTodos.length === 0) {
|
||||
return Translation.tr("No pending tasks");
|
||||
}
|
||||
|
||||
// Limit to first 5 todos to keep popup manageable
|
||||
const limitedTodos = unfinishedTodos.slice(0, 5);
|
||||
let todoText = limitedTodos.map(function (item, index) {
|
||||
return ` ${index + 1}. ${item.content}`;
|
||||
}).join('\n');
|
||||
|
||||
if (unfinishedTodos.length > 5) {
|
||||
todoText += `\n ${Translation.tr("... and %1 more").arg(unfinishedTodos.length - 5)}`;
|
||||
}
|
||||
|
||||
return todoText;
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
anchors.centerIn: parent
|
||||
spacing: 4
|
||||
|
||||
StyledPopupHeaderRow {
|
||||
icon: "calendar_month"
|
||||
label: root.formattedDate
|
||||
}
|
||||
|
||||
StyledPopupValueRow {
|
||||
icon: "timelapse"
|
||||
label: Translation.tr("System uptime:")
|
||||
value: root.formattedUptime
|
||||
}
|
||||
|
||||
// Tasks
|
||||
Column {
|
||||
spacing: 0
|
||||
Layout.fillWidth: true
|
||||
|
||||
StyledPopupValueRow {
|
||||
icon: "checklist"
|
||||
label: Translation.tr("To Do:")
|
||||
value: ""
|
||||
}
|
||||
|
||||
StyledText {
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
wrapMode: Text.Wrap
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
text: root.todosSection
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
StyledPopup {
|
||||
id: root
|
||||
property string formattedDate: Qt.locale().toString(DateTime.clock.date, "dddd, MMMM dd, yyyy")
|
||||
property string formattedTime: DateTime.time
|
||||
property string formattedUptime: DateTime.uptime
|
||||
property string todosSection: getUpcomingTodos()
|
||||
|
||||
function getUpcomingTodos() {
|
||||
const unfinishedTodos = Todo.list.filter(function (item) {
|
||||
return !item.done;
|
||||
});
|
||||
if (unfinishedTodos.length === 0) {
|
||||
return Translation.tr("No pending tasks");
|
||||
}
|
||||
|
||||
// Limit to first 5 todos to keep popup manageable
|
||||
const limitedTodos = unfinishedTodos.slice(0, 5);
|
||||
let todoText = limitedTodos.map(function (item, index) {
|
||||
return `${index + 1}. ${item.content}`;
|
||||
}).join('\n');
|
||||
|
||||
if (unfinishedTodos.length > 5) {
|
||||
todoText += `\n${Translation.tr("... and %1 more").arg(unfinishedTodos.length - 5)}`;
|
||||
}
|
||||
|
||||
return todoText;
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
anchors.centerIn: parent
|
||||
spacing: 4
|
||||
|
||||
// Date + Time row
|
||||
Row {
|
||||
spacing: 5
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
fill: 0
|
||||
font.weight: Font.Medium
|
||||
text: "calendar_month"
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
text: `${root.formattedDate}`
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
}
|
||||
|
||||
// Uptime row
|
||||
RowLayout {
|
||||
spacing: 5
|
||||
Layout.fillWidth: true
|
||||
MaterialSymbol {
|
||||
text: "timelapse"
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
font.pixelSize: Appearance.font.pixelSize.large
|
||||
}
|
||||
StyledText {
|
||||
text: Translation.tr("System uptime:")
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
text: root.formattedUptime
|
||||
}
|
||||
}
|
||||
|
||||
// Tasks
|
||||
Column {
|
||||
spacing: 0
|
||||
Layout.fillWidth: true
|
||||
|
||||
Row {
|
||||
spacing: 4
|
||||
MaterialSymbol {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "checklist"
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
font.pixelSize: Appearance.font.pixelSize.large
|
||||
}
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: Translation.tr("To Do:")
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
wrapMode: Text.Wrap
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
text: root.todosSection
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,57 +12,6 @@ StyledPopup {
|
||||
return (kb / (1024 * 1024)).toFixed(1) + " GB";
|
||||
}
|
||||
|
||||
component ResourceItem: RowLayout {
|
||||
id: resourceItem
|
||||
required property string icon
|
||||
required property string label
|
||||
required property string value
|
||||
spacing: 4
|
||||
|
||||
MaterialSymbol {
|
||||
text: resourceItem.icon
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
}
|
||||
StyledText {
|
||||
text: resourceItem.label
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
visible: resourceItem.value !== ""
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
text: resourceItem.value
|
||||
}
|
||||
}
|
||||
|
||||
component ResourceHeaderItem: Row {
|
||||
id: headerItem
|
||||
required property var icon
|
||||
required property var label
|
||||
spacing: 5
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
fill: 0
|
||||
font.weight: Font.Medium
|
||||
text: headerItem.icon
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: headerItem.label
|
||||
font {
|
||||
weight: Font.Medium
|
||||
pixelSize: Appearance.font.pixelSize.normal
|
||||
}
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: 12
|
||||
@@ -71,23 +20,23 @@ StyledPopup {
|
||||
anchors.top: parent.top
|
||||
spacing: 8
|
||||
|
||||
ResourceHeaderItem {
|
||||
StyledPopupHeaderRow {
|
||||
icon: "memory"
|
||||
label: "RAM"
|
||||
}
|
||||
Column {
|
||||
spacing: 4
|
||||
ResourceItem {
|
||||
StyledPopupValueRow {
|
||||
icon: "clock_loader_60"
|
||||
label: Translation.tr("Used:")
|
||||
value: root.formatKB(ResourceUsage.memoryUsed)
|
||||
}
|
||||
ResourceItem {
|
||||
StyledPopupValueRow {
|
||||
icon: "check_circle"
|
||||
label: Translation.tr("Free:")
|
||||
value: root.formatKB(ResourceUsage.memoryFree)
|
||||
}
|
||||
ResourceItem {
|
||||
StyledPopupValueRow {
|
||||
icon: "empty_dashboard"
|
||||
label: Translation.tr("Total:")
|
||||
value: root.formatKB(ResourceUsage.memoryTotal)
|
||||
@@ -100,23 +49,23 @@ StyledPopup {
|
||||
anchors.top: parent.top
|
||||
spacing: 8
|
||||
|
||||
ResourceHeaderItem {
|
||||
StyledPopupHeaderRow {
|
||||
icon: "swap_horiz"
|
||||
label: "Swap"
|
||||
}
|
||||
Column {
|
||||
spacing: 4
|
||||
ResourceItem {
|
||||
StyledPopupValueRow {
|
||||
icon: "clock_loader_60"
|
||||
label: Translation.tr("Used:")
|
||||
value: root.formatKB(ResourceUsage.swapUsed)
|
||||
}
|
||||
ResourceItem {
|
||||
StyledPopupValueRow {
|
||||
icon: "check_circle"
|
||||
label: Translation.tr("Free:")
|
||||
value: root.formatKB(ResourceUsage.swapFree)
|
||||
}
|
||||
ResourceItem {
|
||||
StyledPopupValueRow {
|
||||
icon: "empty_dashboard"
|
||||
label: Translation.tr("Total:")
|
||||
value: root.formatKB(ResourceUsage.swapTotal)
|
||||
@@ -128,16 +77,16 @@ StyledPopup {
|
||||
anchors.top: parent.top
|
||||
spacing: 8
|
||||
|
||||
ResourceHeaderItem {
|
||||
StyledPopupHeaderRow {
|
||||
icon: "planner_review"
|
||||
label: "CPU"
|
||||
}
|
||||
Column {
|
||||
spacing: 4
|
||||
ResourceItem {
|
||||
StyledPopupValueRow {
|
||||
icon: "bolt"
|
||||
label: Translation.tr("Load:")
|
||||
value: (ResourceUsage.cpuUsage > 0.8 ? Translation.tr("High") : ResourceUsage.cpuUsage > 0.4 ? Translation.tr("Medium") : Translation.tr("Low")) + ` (${Math.round(ResourceUsage.cpuUsage * 100)}%)`
|
||||
value: `${Math.round(ResourceUsage.cpuUsage * 100)}%`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
Row {
|
||||
id: root
|
||||
required property var icon
|
||||
required property var label
|
||||
spacing: 5
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
fill: 0
|
||||
font.weight: Font.DemiBold
|
||||
text: root.icon
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.label
|
||||
font {
|
||||
weight: Font.DemiBold
|
||||
pixelSize: Appearance.font.pixelSize.normal
|
||||
}
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
required property string icon
|
||||
required property string label
|
||||
required property string value
|
||||
spacing: 4
|
||||
|
||||
MaterialSymbol {
|
||||
text: root.icon
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
}
|
||||
StyledText {
|
||||
text: root.label
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
visible: root.value !== ""
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
text: root.value
|
||||
}
|
||||
}
|
||||
@@ -104,7 +104,6 @@ Item {
|
||||
id: overflowPopup
|
||||
hoverTarget: trayOverflowButton
|
||||
active: root.trayOverflowOpen && root.unpinnedItems.length > 0
|
||||
popupBackgroundMargin: 300 // This should be plenty... makes sure tooltips don't get cutoff (easily)
|
||||
|
||||
GridLayout {
|
||||
id: trayOverflowLayout
|
||||
|
||||
@@ -25,7 +25,7 @@ Item {
|
||||
visible: Config.options.bar.utilButtons.showScreenSnip
|
||||
sourceComponent: CircleUtilButton {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
onClicked: Hyprland.dispatch("global quickshell:regionScreenshot")
|
||||
onClicked: Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "screenshot"]);
|
||||
MaterialSymbol {
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
fill: 1
|
||||
|
||||
@@ -14,9 +14,50 @@ Item {
|
||||
property real padding: 4
|
||||
implicitWidth: row.implicitWidth + padding * 2
|
||||
implicitHeight: row.implicitHeight + padding * 2
|
||||
// Excellent symbol explaination and source :
|
||||
// http://xahlee.info/comp/unicode_computing_symbols.html
|
||||
// https://www.nerdfonts.com/cheat-sheet
|
||||
property var macSymbolMap: ({
|
||||
"Ctrl": "",
|
||||
"Alt": "",
|
||||
"Shift": "",
|
||||
"Space": "",
|
||||
"Tab": "↹",
|
||||
"Equal": "",
|
||||
"Minus": "",
|
||||
"Print": "",
|
||||
"BackSpace": "",
|
||||
"Delete": "⌦",
|
||||
"Return": "",
|
||||
"Period": ".",
|
||||
"Escape": "⎋"
|
||||
})
|
||||
property var functionSymbolMap: ({
|
||||
"F1": "",
|
||||
"F2": "",
|
||||
"F3": "",
|
||||
"F4": "",
|
||||
"F5": "",
|
||||
"F6": "",
|
||||
"F7": "",
|
||||
"F8": "",
|
||||
"F9": "",
|
||||
"F10": "",
|
||||
"F11": "",
|
||||
"F12": "",
|
||||
})
|
||||
|
||||
property var mouseSymbolMap: ({
|
||||
"mouse_up": "",
|
||||
"mouse_down": "",
|
||||
"mouse:272": "L",
|
||||
"mouse:273": "R",
|
||||
"Scroll ↑/↓": "",
|
||||
"Page_↑/↓": "⇞/⇟",
|
||||
})
|
||||
|
||||
property var keyBlacklist: ["Super_L"]
|
||||
property var keySubstitutions: ({
|
||||
property var keySubstitutions: Object.assign({
|
||||
"Super": "",
|
||||
"mouse_up": "Scroll ↓", // ikr, weird
|
||||
"mouse_down": "Scroll ↑", // trust me bro
|
||||
@@ -27,7 +68,14 @@ Item {
|
||||
"Hash": "#",
|
||||
"Return": "Enter",
|
||||
// "Shift": "",
|
||||
})
|
||||
},
|
||||
!!Config.options.cheatsheet.superKey ? {
|
||||
"Super": Config.options.cheatsheet.superKey,
|
||||
}: {},
|
||||
Config.options.cheatsheet.useMacSymbol ? macSymbolMap : {},
|
||||
Config.options.cheatsheet.useFnSymbol ? functionSymbolMap : {},
|
||||
Config.options.cheatsheet.useMouseSymbol ? mouseSymbolMap : {},
|
||||
)
|
||||
|
||||
Row { // Keybind columns
|
||||
id: row
|
||||
@@ -77,6 +125,17 @@ Item {
|
||||
var result = [];
|
||||
for (var i = 0; i < keybindSection.modelData.keybinds.length; i++) {
|
||||
const keybind = keybindSection.modelData.keybinds[i];
|
||||
|
||||
if (!Config.options.cheatsheet.splitButtons) {
|
||||
|
||||
for (var j = 0; j < keybind.mods.length; j++) {
|
||||
keybind.mods[j] = keySubstitutions[keybind.mods[j]] || keybind.mods[j];
|
||||
}
|
||||
keybind.mods = [keybind.mods.join(' ') ]
|
||||
keybind.mods[0] += !keyBlacklist.includes(keybind.key) && keybind.mods[0].length ? ' ' : ''
|
||||
keybind.mods[0] += !keyBlacklist.includes(keybind.key) ? (keySubstitutions[keybind.key] || keybind.key) : ''
|
||||
}
|
||||
|
||||
result.push({
|
||||
"type": "keys",
|
||||
"mods": keybind.mods,
|
||||
@@ -108,17 +167,19 @@ Item {
|
||||
delegate: KeyboardKey {
|
||||
required property var modelData
|
||||
key: keySubstitutions[modelData] || modelData
|
||||
pixelSize: Config.options.cheatsheet.fontSize.key
|
||||
}
|
||||
}
|
||||
StyledText {
|
||||
id: keybindPlus
|
||||
visible: !keyBlacklist.includes(modelData.key) && modelData.mods.length > 0
|
||||
visible: Config.options.cheatsheet.splitButtons && !keyBlacklist.includes(modelData.key) && modelData.mods.length > 0
|
||||
text: "+"
|
||||
}
|
||||
KeyboardKey {
|
||||
id: keybindKey
|
||||
visible: !keyBlacklist.includes(modelData.key)
|
||||
visible: Config.options.cheatsheet.splitButtons && !keyBlacklist.includes(modelData.key)
|
||||
key: keySubstitutions[modelData.key] || modelData.key
|
||||
pixelSize: Config.options.cheatsheet.fontSize.key
|
||||
color: Appearance.colors.colOnLayer0
|
||||
}
|
||||
}
|
||||
@@ -134,7 +195,7 @@ Item {
|
||||
StyledText {
|
||||
id: commentText
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
font.pixelSize: Config.options.cheatsheet.fontSize.comment || Appearance.font.pixelSize.smaller
|
||||
text: modelData.comment
|
||||
}
|
||||
}
|
||||
@@ -152,4 +213,4 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,6 +267,22 @@ Singleton {
|
||||
property int suspend: 3
|
||||
}
|
||||
|
||||
property JsonObject cheatsheet: JsonObject {
|
||||
// Use a nerdfont to see the icons
|
||||
// 0: | 1: | 2: | 3: | 4:
|
||||
// 5: | 6: | 7: | 8: | 9:
|
||||
// 10: | 11: | 12: | 13: | 14:
|
||||
property string superKey: ""
|
||||
property bool useMacSymbol: false
|
||||
property bool splitButtons: true
|
||||
property bool useMouseSymbol: false
|
||||
property bool useFnSymbol: false
|
||||
property JsonObject fontSize: JsonObject {
|
||||
property int key: Appearance.font.pixelSize.smaller
|
||||
property int comment: Appearance.font.pixelSize.smaller
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject conflictKiller: JsonObject {
|
||||
property bool autoKillNotificationDaemons: false
|
||||
property bool autoKillTrays: false
|
||||
@@ -395,6 +411,7 @@ Singleton {
|
||||
|
||||
property JsonObject resources: JsonObject {
|
||||
property int updateInterval: 3000
|
||||
property int historyLength: 60
|
||||
}
|
||||
|
||||
property JsonObject musicRecognition: JsonObject {
|
||||
@@ -475,6 +492,14 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject screenRecord: JsonObject {
|
||||
property string savePath: Directories.videos.replace("file://","") // strip "file://"
|
||||
}
|
||||
|
||||
property JsonObject screenSnip: JsonObject {
|
||||
property string savePath: "" // only copy to clipboard when empty
|
||||
}
|
||||
|
||||
property JsonObject sounds: JsonObject {
|
||||
property bool battery: false
|
||||
property bool pomodoro: false
|
||||
|
||||
@@ -80,18 +80,31 @@ Singleton {
|
||||
}
|
||||
|
||||
property JsonObject overlay: JsonObject {
|
||||
property list<string> open: ["crosshair"]
|
||||
property list<string> open: ["crosshair", "recorder", "volumeMixer", "resources"]
|
||||
property JsonObject crosshair: JsonObject {
|
||||
property bool pinned: false
|
||||
property bool clickthrough: true
|
||||
property real x: 100
|
||||
property real y: 100
|
||||
property real x: 835
|
||||
property real y: 490
|
||||
}
|
||||
property JsonObject recorder: JsonObject {
|
||||
property bool pinned: false
|
||||
property bool clickthrough: false
|
||||
property real x: 80
|
||||
property real y: 80
|
||||
}
|
||||
property JsonObject resources: JsonObject {
|
||||
property bool pinned: false
|
||||
property bool clickthrough: true
|
||||
property real x: 1500
|
||||
property real y: 770
|
||||
property int tabIndex: 0
|
||||
}
|
||||
property JsonObject volumeMixer: JsonObject {
|
||||
property bool pinned: false
|
||||
property bool clickthrough: false
|
||||
property real x: 55
|
||||
property real y: 188
|
||||
property real x: 80
|
||||
property real y: 280
|
||||
}
|
||||
property JsonObject fpsLimiter: JsonObject {
|
||||
property bool pinned: false
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import QtQuick
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
|
||||
/*
|
||||
* Simple one value line graph
|
||||
*/
|
||||
Canvas {
|
||||
id: root
|
||||
|
||||
enum Alignment { Left, Right }
|
||||
|
||||
required property list<real> values
|
||||
property int points: values.length
|
||||
property color color: Appearance.colors.colPrimary
|
||||
property real fillOpacity: 0.5
|
||||
property var alignment: Graph.Alignment.Left
|
||||
|
||||
onValuesChanged: root.requestPaint()
|
||||
onPaint: {
|
||||
var ctx = getContext("2d")
|
||||
ctx.clearRect(0, 0, width, height)
|
||||
if (!root.values || root.values.length < 2)
|
||||
return
|
||||
|
||||
var n = root.points
|
||||
var dx = width / (n - 1)
|
||||
ctx.strokeStyle = root.color
|
||||
ctx.fillStyle = ColorUtils.transparentize(root.color, 1 - root.fillOpacity)
|
||||
ctx.lineWidth = 2
|
||||
ctx.beginPath()
|
||||
for (var i = 0; i < n; ++i) {
|
||||
var valueIndex = (root.alignment === Graph.Alignment.Right) ? root.values.length - n + i : i
|
||||
if (valueIndex < 0 || valueIndex >= root.values.length) {
|
||||
continue; // No data for this point
|
||||
}
|
||||
var x = i * dx
|
||||
var norm = root.values[valueIndex] // already in 0-1 range
|
||||
var y = height - norm * height
|
||||
if (valueIndex === 0) {
|
||||
ctx.moveTo(x, height)
|
||||
ctx.lineTo(x, y)
|
||||
} else {
|
||||
ctx.lineTo(x, y)
|
||||
}
|
||||
}
|
||||
ctx.stroke()
|
||||
ctx.lineTo(width, height)
|
||||
ctx.fill()
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ Rectangle {
|
||||
property real extraBottomBorderWidth: 2
|
||||
property color borderColor: Appearance.colors.colOnLayer0
|
||||
property real borderRadius: 5
|
||||
property real pixelSize: Appearance.font.pixelSize.smaller
|
||||
property color keyColor: Appearance.m3colors.m3surfaceContainerLow
|
||||
implicitWidth: keyFace.implicitWidth + borderWidth * 2
|
||||
implicitHeight: keyFace.implicitHeight + borderWidth * 2 + extraBottomBorderWidth
|
||||
@@ -35,7 +36,7 @@ Rectangle {
|
||||
id: keyText
|
||||
anchors.centerIn: parent
|
||||
font.family: Appearance.font.family.monospace
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
font.pixelSize: root.pixelSize
|
||||
text: key
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
import qs.modules.common.models
|
||||
|
||||
TabBar {
|
||||
id: root
|
||||
property real indicatorPadding: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
background: Item {
|
||||
WheelHandler {
|
||||
onWheel: (event) => {
|
||||
if (event.angleDelta.y < 0) root.incrementCurrentIndex();
|
||||
else if (event.angleDelta.y > 0) root.decrementCurrentIndex();
|
||||
}
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: activeIndicator
|
||||
z: 9999
|
||||
anchors.bottom: parent.bottom
|
||||
topLeftRadius: height
|
||||
topRightRadius: height
|
||||
bottomLeftRadius: 0
|
||||
bottomRightRadius: 0
|
||||
color: Appearance.colors.colPrimary
|
||||
// Animation
|
||||
property real baseWidth: root.width / root.count
|
||||
AnimatedTabIndexPair {
|
||||
id: idxPair
|
||||
index: root.currentIndex
|
||||
}
|
||||
height: 3
|
||||
x: Math.min(idxPair.idx1, idxPair.idx2) * baseWidth + root.indicatorPadding
|
||||
width: ((Math.max(idxPair.idx1, idxPair.idx2) + 1) * baseWidth - root.indicatorPadding) - x
|
||||
}
|
||||
|
||||
Rectangle { // Tabbar bottom border
|
||||
id: tabBarBottomBorder
|
||||
z: 9998
|
||||
anchors.bottom: parent.bottom
|
||||
height: 1
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
color: Appearance.colors.colOutlineVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
@@ -10,14 +10,12 @@ TabButton {
|
||||
id: root
|
||||
property string buttonText
|
||||
property string buttonIcon
|
||||
property bool selected: false
|
||||
property int rippleDuration: 1200
|
||||
height: buttonBackground.height
|
||||
property int tabContentWidth: buttonBackground.width - buttonBackground.radius*2
|
||||
|
||||
property color colBackground: ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)
|
||||
property color colBackgroundHover: Appearance.colors.colLayer1Hover
|
||||
property color colRipple: Appearance.colors.colLayer1Active
|
||||
property color colBackground: ColorUtils.transparentize(Appearance.colors.colSurfaceContainer)
|
||||
property color colBackgroundHover: ColorUtils.transparentize(Appearance.colors.colOnSurface, root.checked ? 1 : 0.95)
|
||||
property color colRipple: ColorUtils.transparentize(Appearance.colors.colOnSurface, 0.95)
|
||||
|
||||
PointingHandInteraction {}
|
||||
|
||||
@@ -91,8 +89,12 @@ TabButton {
|
||||
|
||||
background: Rectangle {
|
||||
id: buttonBackground
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: 3
|
||||
}
|
||||
radius: Appearance?.rounding.normal
|
||||
implicitHeight: 37
|
||||
implicitHeight: 42
|
||||
color: (root.hovered ? root.colBackgroundHover : root.colBackground)
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
@@ -156,8 +158,8 @@ TabButton {
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: buttonIcon
|
||||
iconSize: Appearance.font.pixelSize.huge
|
||||
fill: selected ? 1 : 0
|
||||
color: selected ? Appearance.colors.colPrimary : Appearance.colors.colOnLayer1
|
||||
fill: root.checked ? 1 : 0
|
||||
color: root.checked ? Appearance.colors.colPrimary : Appearance.colors.colOnLayer1
|
||||
Behavior on color {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
@@ -167,7 +169,7 @@ TabButton {
|
||||
id: buttonTextWidget
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: selected ? Appearance.colors.colPrimary : Appearance.colors.colOnLayer1
|
||||
color: root.checked ? Appearance.colors.colPrimary : Appearance.colors.colOnLayer1
|
||||
text: buttonText
|
||||
Behavior on color {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
|
||||
@@ -128,11 +128,19 @@ Scope {
|
||||
}
|
||||
}
|
||||
|
||||
function lock() {
|
||||
if (Config.options.lock.useHyprlock) {
|
||||
Quickshell.execDetached(["bash", "-c", "pidof hyprlock || hyprlock"]);
|
||||
return;
|
||||
}
|
||||
GlobalStates.screenLocked = true;
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "lock"
|
||||
|
||||
function activate(): void {
|
||||
GlobalStates.screenLocked = true;
|
||||
root.lock();
|
||||
}
|
||||
function focus(): void {
|
||||
lockContext.shouldReFocus();
|
||||
@@ -144,11 +152,7 @@ Scope {
|
||||
description: "Locks the screen"
|
||||
|
||||
onPressed: {
|
||||
if (Config.options.lock.useHyprlock) {
|
||||
Quickshell.execDetached(["bash", "-c", "pidof hyprlock || hyprlock"]);
|
||||
return;
|
||||
}
|
||||
GlobalStates.screenLocked = true;
|
||||
root.lock()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +169,7 @@ Scope {
|
||||
function initIfReady() {
|
||||
if (!Config.ready || !Persistent.ready) return;
|
||||
if (Config.options.lock.launchOnStartup && Persistent.isNewHyprlandInstance) {
|
||||
Hyprland.dispatch("global quickshell:lock")
|
||||
root.lock();
|
||||
} else {
|
||||
KeyringStorage.fetchKeyringData();
|
||||
}
|
||||
|
||||
@@ -6,9 +6,11 @@ Singleton {
|
||||
id: root
|
||||
|
||||
readonly property list<var> availableWidgets: [
|
||||
{ identifier: "recorder", materialSymbol: "screen_record" },
|
||||
{ identifier: "volumeMixer", materialSymbol: "volume_up" },
|
||||
{ identifier: "crosshair", materialSymbol: "point_scan" },
|
||||
{ identifier: "fpsLimiter", materialSymbol: "animation" },
|
||||
{ identifier: "volumeMixer", materialSymbol: "volume_up" }
|
||||
{ identifier: "resources", materialSymbol: "browse_activity" }
|
||||
]
|
||||
|
||||
readonly property bool hasPinnedWidgets: root.pinnedWidgetIdentifiers.length > 0
|
||||
|
||||
@@ -9,6 +9,8 @@ import Quickshell.Bluetooth
|
||||
import qs.modules.overlay.crosshair
|
||||
import qs.modules.overlay.volumeMixer
|
||||
import qs.modules.overlay.fpsLimiter
|
||||
import qs.modules.overlay.recorder
|
||||
import qs.modules.overlay.resources
|
||||
|
||||
DelegateChooser {
|
||||
id: root
|
||||
@@ -17,4 +19,6 @@ DelegateChooser {
|
||||
DelegateChoice { roleValue: "crosshair"; Crosshair {} }
|
||||
DelegateChoice { roleValue: "volumeMixer"; VolumeMixer {} }
|
||||
DelegateChoice { roleValue: "fpsLimiter"; FpsLimiter {} }
|
||||
DelegateChoice { roleValue: "recorder"; Recorder {} }
|
||||
DelegateChoice { roleValue: "resources"; Resources {} }
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ AbstractOverlayWidget {
|
||||
id: root
|
||||
|
||||
required property Item contentItem
|
||||
property bool fancyBorders: true
|
||||
|
||||
required property var modelData
|
||||
readonly property string identifier: modelData.identifier
|
||||
@@ -29,6 +30,8 @@ AbstractOverlayWidget {
|
||||
property var persistentStateEntry: Persistent.states.overlay[identifier]
|
||||
property real radius: Appearance.rounding.windowRounding
|
||||
property real minWidth: 250
|
||||
property real padding: 6
|
||||
property real contentRadius: radius - padding
|
||||
|
||||
draggable: GlobalStates.overlayOpen
|
||||
x: Math.round(persistentStateEntry.x) // Round or it'll be blurry
|
||||
@@ -96,11 +99,15 @@ AbstractOverlayWidget {
|
||||
Rectangle {
|
||||
id: border
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
color: (root.fancyBorders && GlobalStates.overlayOpen) ? Appearance.colors.colLayer1 : "transparent"
|
||||
radius: root.radius
|
||||
border.color: ColorUtils.transparentize(Appearance.colors.colOutlineVariant, GlobalStates.overlayOpen ? 0 : 1)
|
||||
border.width: 1
|
||||
|
||||
Behavior on color {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
layer.enabled: GlobalStates.overlayOpen
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
@@ -110,25 +117,23 @@ AbstractOverlayWidget {
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
z: -1
|
||||
z: root.fancyBorders ? 0 : -1
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
// Title bar
|
||||
Rectangle {
|
||||
id: titleBar
|
||||
opacity: GlobalStates.overlayOpen ? 1 : 0
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
property real padding: 2
|
||||
Layout.fillWidth: true
|
||||
property real padding: 6
|
||||
implicitWidth: titleBarRow.implicitWidth + padding * 2
|
||||
implicitHeight: titleBarRow.implicitHeight + padding * 2
|
||||
color: Appearance.m3colors.m3surfaceContainer
|
||||
border.color: Appearance.colors.colOutlineVariant
|
||||
border.width: 1
|
||||
color: root.fancyBorders ? "transparent" : Appearance.colors.colLayer1
|
||||
// border.color: Appearance.colors.colOutlineVariant
|
||||
// border.width: 1
|
||||
|
||||
RowLayout {
|
||||
id: titleBarRow
|
||||
@@ -191,7 +196,10 @@ AbstractOverlayWidget {
|
||||
// Content
|
||||
Item {
|
||||
id: contentContainer
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.margins: root.fancyBorders ? root.padding : 0
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
implicitWidth: root.contentItem.implicitWidth
|
||||
implicitHeight: root.contentItem.implicitHeight
|
||||
children: [root.contentItem]
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.modules.common
|
||||
import qs.modules.overlay
|
||||
|
||||
StyledOverlayWidget {
|
||||
id: root
|
||||
contentItem: CrosshairContent {}
|
||||
fancyBorders: false // Crosshair should be see-through
|
||||
contentItem: CrosshairContent {
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.overlay
|
||||
|
||||
StyledOverlayWidget {
|
||||
id: root
|
||||
|
||||
contentItem: Rectangle {
|
||||
id: contentItem
|
||||
anchors.centerIn: parent
|
||||
radius: root.contentRadius
|
||||
color: Appearance.m3colors.m3surfaceContainer
|
||||
property real padding: 8
|
||||
implicitHeight: contentColumn.implicitHeight + padding * 2
|
||||
implicitWidth: 350
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: parent.padding
|
||||
}
|
||||
spacing: 10
|
||||
|
||||
Row {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: 10
|
||||
|
||||
BigRecorderButton {
|
||||
materialSymbol: "screenshot_region"
|
||||
name: "Screenshot region"
|
||||
onClicked: {
|
||||
GlobalStates.overlayOpen = false;
|
||||
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "screenshot"]);
|
||||
}
|
||||
}
|
||||
|
||||
BigRecorderButton {
|
||||
materialSymbol: "photo_camera"
|
||||
name: "Screenshot"
|
||||
onClicked: {
|
||||
GlobalStates.overlayOpen = false;
|
||||
Quickshell.execDetached(["bash", "-c", "grim - | wl-copy"]);
|
||||
}
|
||||
}
|
||||
|
||||
BigRecorderButton {
|
||||
materialSymbol: "screen_record"
|
||||
name: "Record region"
|
||||
onClicked: {
|
||||
GlobalStates.overlayOpen = false;
|
||||
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "recordWithSound"]);
|
||||
}
|
||||
}
|
||||
|
||||
BigRecorderButton {
|
||||
materialSymbol: "capture"
|
||||
name: "Record screen"
|
||||
onClicked: {
|
||||
GlobalStates.overlayOpen = false;
|
||||
Quickshell.execDetached([Directories.recordScriptPath, "--fullscreen", "--sound"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RippleButton {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: false
|
||||
buttonRadius: height / 2
|
||||
colBackground: Appearance.colors.colLayer3
|
||||
colBackgroundHover: Appearance.colors.colLayer3Hover
|
||||
colRipple: Appearance.colors.colLayer3Active
|
||||
onClicked: {
|
||||
GlobalStates.overlayOpen = false;
|
||||
Qt.openUrlExternally(Directories.videos);
|
||||
}
|
||||
contentItem: Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: 6
|
||||
MaterialSymbol {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "animated_images"
|
||||
iconSize: 20
|
||||
}
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: qsTr("Open recordings folder")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component BigRecorderButton: RippleButton {
|
||||
id: bigButton
|
||||
required property string materialSymbol
|
||||
required property string name
|
||||
implicitHeight: 66
|
||||
implicitWidth: 66
|
||||
buttonRadius: height / 2
|
||||
|
||||
colBackground: Appearance.colors.colLayer3
|
||||
colBackgroundHover: Appearance.colors.colLayer3Hover
|
||||
colRipple: Appearance.colors.colLayer3Active
|
||||
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: bigButton.materialSymbol
|
||||
iconSize: 28
|
||||
}
|
||||
|
||||
StyledToolTip {
|
||||
text: bigButton.name
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Qt.labs.synchronizer
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.overlay
|
||||
|
||||
StyledOverlayWidget {
|
||||
id: root
|
||||
property list<var> resources: [
|
||||
{
|
||||
"icon": "planner_review",
|
||||
"name": Translation.tr("CPU"),
|
||||
"history": ResourceUsage.cpuUsageHistory,
|
||||
"maxAvailableString": ResourceUsage.maxAvailableCpuString
|
||||
},
|
||||
{
|
||||
"icon": "memory",
|
||||
"name": Translation.tr("RAM"),
|
||||
"history": ResourceUsage.memoryUsageHistory,
|
||||
"maxAvailableString": ResourceUsage.maxAvailableMemoryString
|
||||
},
|
||||
{
|
||||
"icon": "swap_horiz",
|
||||
"name": Translation.tr("Swap"),
|
||||
"history": ResourceUsage.swapUsageHistory,
|
||||
"maxAvailableString": ResourceUsage.maxAvailableSwapString
|
||||
},
|
||||
]
|
||||
|
||||
contentItem: Rectangle {
|
||||
id: contentItem
|
||||
anchors.centerIn: parent
|
||||
color: Appearance.m3colors.m3surfaceContainer
|
||||
radius: root.contentRadius
|
||||
property real padding: 4
|
||||
implicitWidth: 350
|
||||
implicitHeight: 200
|
||||
// implicitHeight: contentColumn.implicitHeight + padding * 2
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: parent.padding
|
||||
}
|
||||
spacing: 10
|
||||
|
||||
SecondaryTabBar {
|
||||
id: tabBar
|
||||
|
||||
currentIndex: Persistent.states.overlay.resources.tabIndex
|
||||
onCurrentIndexChanged: {
|
||||
Persistent.states.overlay.resources.tabIndex = tabBar.currentIndex;
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.resources.length
|
||||
delegate: SecondaryTabButton {
|
||||
required property int index
|
||||
property var modelData: root.resources[index]
|
||||
buttonIcon: modelData.icon
|
||||
buttonText: modelData.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResourceSummary {
|
||||
Layout.margins: 8
|
||||
history: root.resources[tabBar.currentIndex]?.history ?? []
|
||||
maxAvailableString: root.resources[tabBar.currentIndex]?.maxAvailableString ?? "--"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component ResourceSummary: RowLayout {
|
||||
id: resourceSummary
|
||||
required property list<real> history
|
||||
required property string maxAvailableString
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
spacing: 12
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 2
|
||||
StyledText {
|
||||
text: (resourceSummary.history[resourceSummary.history.length - 1] * 100).toFixed(1) + "%"
|
||||
font {
|
||||
family: Appearance.font.family.numbers
|
||||
variableAxes: Appearance.font.variableAxes.numbers
|
||||
pixelSize: Appearance.font.pixelSize.huge
|
||||
}
|
||||
}
|
||||
StyledText {
|
||||
text: Translation.tr("of %1").arg(resourceSummary.maxAvailableString)
|
||||
font {
|
||||
// family: Appearance.font.family.numbers
|
||||
// variableAxes: Appearance.font.variableAxes.numbers
|
||||
pixelSize: Appearance.font.pixelSize.smallie
|
||||
}
|
||||
color: Appearance.colors.colSubtext
|
||||
}
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
id: graphBg
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
radius: Appearance.rounding.small
|
||||
color: Appearance.colors.colSecondaryContainer
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: graphBg.width
|
||||
height: graphBg.height
|
||||
radius: graphBg.radius
|
||||
}
|
||||
}
|
||||
Graph {
|
||||
anchors.fill: parent
|
||||
values: root.resources[tabBar.currentIndex]?.history ?? []
|
||||
points: ResourceUsage.historyLength
|
||||
alignment: Graph.Alignment.Right
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,10 @@ StyledOverlayWidget {
|
||||
contentItem: Rectangle {
|
||||
anchors.centerIn: parent
|
||||
color: Appearance.m3colors.m3surfaceContainer
|
||||
radius: root.contentRadius
|
||||
property real padding: 16
|
||||
implicitHeight: 700
|
||||
implicitWidth: 400
|
||||
implicitHeight: 600
|
||||
implicitWidth: 350
|
||||
|
||||
VolumeDialogContent {
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -99,7 +99,7 @@ RowLayout {
|
||||
Layout.bottomMargin: 4
|
||||
onClicked: {
|
||||
GlobalStates.overviewOpen = false;
|
||||
Hyprland.dispatch("global quickshell:regionSearch")
|
||||
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "search"]);
|
||||
}
|
||||
text: "image_search"
|
||||
StyledToolTip {
|
||||
|
||||
@@ -33,6 +33,10 @@ PanelWindow {
|
||||
property var selectionMode: RegionSelection.SelectionMode.RectCorners
|
||||
signal dismiss()
|
||||
|
||||
property string saveScreenshotDir: Config.options.screenSnip.savePath !== ""
|
||||
? Config.options.screenSnip.savePath
|
||||
: ""
|
||||
|
||||
property string screenshotDir: Directories.screenshotTemp
|
||||
property string imageSearchEngineBaseUrl: Config.options.search.imageSearch.imageSearchEngineBaseUrl
|
||||
property string fileUploadApiEndpoint: "https://uguu.se/upload"
|
||||
@@ -259,7 +263,23 @@ PanelWindow {
|
||||
}
|
||||
switch (root.action) {
|
||||
case RegionSelection.SnipAction.Copy:
|
||||
snipProc.command = ["bash", "-c", `${cropToStdout} | wl-copy && ${cleanup}`]
|
||||
if (saveScreenshotDir === "") {
|
||||
// not saving the screenshot, just copy to clipboard
|
||||
snipProc.command = ["bash", "-c", `${cropToStdout} | wl-copy && ${cleanup}`]
|
||||
break;
|
||||
}
|
||||
|
||||
const savePathBase = root.saveScreenshotDir
|
||||
|
||||
snipProc.command = [
|
||||
"bash", "-c",
|
||||
`mkdir -p '${StringUtils.shellSingleQuoteEscape(savePathBase)}' && \
|
||||
saveFileName="screenshot-$(date '+%Y-%m-%d_%H.%M.%S').png" && \
|
||||
savePath="${savePathBase}/$saveFileName" && \
|
||||
${cropToStdout} | tee >(wl-copy) > "$savePath" && \
|
||||
${cleanup}`
|
||||
]
|
||||
|
||||
break;
|
||||
case RegionSelection.SnipAction.Edit:
|
||||
snipProc.command = ["bash", "-c", `${cropToStdout} | swappy -f - && ${cleanup}`]
|
||||
|
||||
@@ -90,4 +90,6 @@ ContentPage {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -321,7 +321,7 @@ ContentPage {
|
||||
value: '[]'
|
||||
},
|
||||
{
|
||||
displayName: Translation.tr("Japanese"),
|
||||
displayName: Translation.tr("Han chars"),
|
||||
icon: "square_dot",
|
||||
value: '["一","二","三","四","五","六","七","八","九","十","十一","十二","十三","十四","十五","十六","十七","十八","十九","二十"]'
|
||||
},
|
||||
|
||||
@@ -7,6 +7,98 @@ import qs.modules.common.widgets
|
||||
ContentPage {
|
||||
forceWidth: true
|
||||
|
||||
ContentSection {
|
||||
icon: "keyboard"
|
||||
title: Translation.tr("Cheat sheet")
|
||||
|
||||
ContentSubsection {
|
||||
title: Translation.tr("Super key symbol")
|
||||
tooltip: Translation.tr("You can also manually edit cheatsheet.superKey")
|
||||
ConfigSelectionArray {
|
||||
currentValue: Config.options.cheatsheet.superKey
|
||||
onSelected: newValue => {
|
||||
Config.options.cheatsheet.superKey = newValue;
|
||||
}
|
||||
// Use a nerdfont to see the icons
|
||||
options: ([
|
||||
"", "", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "⌘", "", "", ""
|
||||
]).map(icon => { return {
|
||||
displayName: icon,
|
||||
value: icon
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
ConfigSwitch {
|
||||
buttonIcon: ""
|
||||
text: Translation.tr("Use macOS-like symbols for mods keys")
|
||||
checked: Config.options.cheatsheet.useMacSymbol
|
||||
onCheckedChanged: {
|
||||
Config.options.cheatsheet.useMacSymbol = checked;
|
||||
}
|
||||
StyledToolTip {
|
||||
text: Translation.tr("e.g. for Ctrl, for Alt, for Shift, etc")
|
||||
}
|
||||
}
|
||||
|
||||
ConfigSwitch {
|
||||
buttonIcon: ""
|
||||
text: Translation.tr("Use symbols for function keys")
|
||||
checked: Config.options.cheatsheet.useFnSymbol
|
||||
onCheckedChanged: {
|
||||
Config.options.cheatsheet.useFnSymbol = checked;
|
||||
}
|
||||
StyledToolTip {
|
||||
text: Translation.tr("e.g. for F1, for F12")
|
||||
}
|
||||
}
|
||||
ConfigSwitch {
|
||||
buttonIcon: ""
|
||||
text: Translation.tr("Use symbols for mouse")
|
||||
checked: Config.options.cheatsheet.useMouseSymbol
|
||||
onCheckedChanged: {
|
||||
Config.options.cheatsheet.useMouseSymbol = checked;
|
||||
}
|
||||
StyledToolTip {
|
||||
text: Translation.tr("Replace for \"Scroll ↓\", \"Scroll ↑\", L \"LMB\", R \"RMB\", \"Scroll ↑/↓\" and ⇞/⇟ for \"Page_↑/↓\"")
|
||||
}
|
||||
}
|
||||
ConfigSwitch {
|
||||
buttonIcon: "highlight_keyboard_focus"
|
||||
text: Translation.tr("Split buttons")
|
||||
checked: Config.options.cheatsheet.splitButtons
|
||||
onCheckedChanged: {
|
||||
Config.options.cheatsheet.splitButtons = checked;
|
||||
}
|
||||
StyledToolTip {
|
||||
text: Translation.tr("Display modifiers and keys in multiple keycap (e.g., \"Ctrl + A\" instead of \"Ctrl A\" or \" + A\" instead of \" A\")")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ConfigSpinBox {
|
||||
text: Translation.tr("Keybind font size")
|
||||
value: Config.options.cheatsheet.fontSize.key
|
||||
from: 8
|
||||
to: 30
|
||||
stepSize: 1
|
||||
onValueChanged: {
|
||||
Config.options.cheatsheet.fontSize.key = value;
|
||||
}
|
||||
}
|
||||
ConfigSpinBox {
|
||||
text: Translation.tr("Description font size")
|
||||
value: Config.options.cheatsheet.fontSize.comment
|
||||
from: 8
|
||||
to: 30
|
||||
stepSize: 1
|
||||
onValueChanged: {
|
||||
Config.options.cheatsheet.fontSize.comment = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
ContentSection {
|
||||
icon: "call_to_action"
|
||||
title: Translation.tr("Dock")
|
||||
@@ -647,4 +739,5 @@ ContentPage {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -85,6 +85,31 @@ ContentPage {
|
||||
|
||||
}
|
||||
|
||||
ContentSection {
|
||||
icon: "file_open"
|
||||
title: Translation.tr("Save paths")
|
||||
|
||||
MaterialTextArea {
|
||||
Layout.fillWidth: true
|
||||
placeholderText: Translation.tr("Video Recording Path")
|
||||
text: Config.options.screenRecord.savePath
|
||||
wrapMode: TextEdit.Wrap
|
||||
onTextChanged: {
|
||||
Config.options.screenRecord.savePath = text;
|
||||
}
|
||||
}
|
||||
|
||||
MaterialTextArea {
|
||||
Layout.fillWidth: true
|
||||
placeholderText: Translation.tr("Screenshot Path (leave empty to just copy)")
|
||||
text: Config.options.screenSnip.savePath
|
||||
wrapMode: TextEdit.Wrap
|
||||
onTextChanged: {
|
||||
Config.options.screenSnip.savePath = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSection {
|
||||
icon: "search"
|
||||
title: Translation.tr("Search")
|
||||
|
||||
@@ -7,7 +7,6 @@ import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property int currentTab: 0
|
||||
property var tabButtonList: [
|
||||
{"name": Translation.tr("Pomodoro"), "icon": "search_activity"},
|
||||
{"name": Translation.tr("Stopwatch"), "icon": "timer"}
|
||||
@@ -17,20 +16,20 @@ Item {
|
||||
Keys.onPressed: (event) => {
|
||||
if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) && event.modifiers === Qt.NoModifier) { // Switch tabs
|
||||
if (event.key === Qt.Key_PageDown) {
|
||||
currentTab = Math.min(currentTab + 1, root.tabButtonList.length - 1)
|
||||
tabBar.incrementCurrentIndex();
|
||||
} else if (event.key === Qt.Key_PageUp) {
|
||||
currentTab = Math.max(currentTab - 1, 0)
|
||||
tabBar.decrementCurrentIndex();
|
||||
}
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_Space || event.key === Qt.Key_S) { // Pause/resume with Space or S
|
||||
if (currentTab === 0) {
|
||||
if (tabBar.currentIndex === 0) {
|
||||
TimerService.togglePomodoro()
|
||||
} else {
|
||||
TimerService.toggleStopwatch()
|
||||
}
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_R) { // Reset with R
|
||||
if (currentTab === 0) {
|
||||
if (tabBar.currentIndex === 0) {
|
||||
TimerService.resetPomodoro()
|
||||
} else {
|
||||
TimerService.stopwatchReset()
|
||||
@@ -46,82 +45,19 @@ Item {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
TabBar {
|
||||
SecondaryTabBar {
|
||||
id: tabBar
|
||||
Layout.fillWidth: true
|
||||
currentIndex: currentTab
|
||||
onCurrentIndexChanged: currentTab = currentIndex
|
||||
|
||||
background: Item {
|
||||
WheelHandler {
|
||||
onWheel: (event) => {
|
||||
if (event.angleDelta.y < 0)
|
||||
tabBar.currentIndex = Math.min(tabBar.currentIndex + 1, root.tabButtonList.length - 1)
|
||||
else if (event.angleDelta.y > 0)
|
||||
tabBar.currentIndex = Math.max(tabBar.currentIndex - 1, 0)
|
||||
}
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
}
|
||||
}
|
||||
currentIndex: swipeView.currentIndex
|
||||
|
||||
Repeater {
|
||||
model: root.tabButtonList
|
||||
delegate: SecondaryTabButton {
|
||||
selected: (index == currentTab)
|
||||
buttonText: modelData.name
|
||||
buttonIcon: modelData.icon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { // Tab indicator
|
||||
id: tabIndicator
|
||||
Layout.fillWidth: true
|
||||
height: 3
|
||||
property bool enableIndicatorAnimation: false
|
||||
Connections {
|
||||
target: root
|
||||
function onCurrentTabChanged() {
|
||||
tabIndicator.enableIndicatorAnimation = true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: indicator
|
||||
property int tabCount: root.tabButtonList.length
|
||||
property real fullTabSize: root.width / tabCount;
|
||||
property real targetWidth: tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth
|
||||
|
||||
implicitWidth: targetWidth
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
}
|
||||
|
||||
x: tabBar.currentIndex * fullTabSize + (fullTabSize - targetWidth) / 2
|
||||
|
||||
color: Appearance.colors.colPrimary
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
Behavior on x {
|
||||
enabled: tabIndicator.enableIndicatorAnimation
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
enabled: tabIndicator.enableIndicatorAnimation
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // Tabbar bottom border
|
||||
id: tabBarBottomBorder
|
||||
Layout.fillWidth: true
|
||||
height: 1
|
||||
color: Appearance.colors.colOutlineVariant
|
||||
}
|
||||
|
||||
SwipeView {
|
||||
id: swipeView
|
||||
Layout.topMargin: 10
|
||||
@@ -129,11 +65,7 @@ Item {
|
||||
Layout.fillHeight: true
|
||||
spacing: 10
|
||||
clip: true
|
||||
currentIndex: currentTab
|
||||
onCurrentIndexChanged: {
|
||||
tabIndicator.enableIndicatorAnimation = true
|
||||
currentTab = currentIndex
|
||||
}
|
||||
currentIndex: tabBar.currentIndex
|
||||
|
||||
// Tabs
|
||||
PomodoroTimer {}
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ AndroidQuickToggleButton {
|
||||
interval: 300
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
Hyprland.dispatch("global quickshell:regionScreenshot")
|
||||
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "screenshot"]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property int currentTab: 0
|
||||
property var tabButtonList: [{"icon": "checklist", "name": Translation.tr("Unfinished")}, {"name": Translation.tr("Done"), "icon": "check_circle"}]
|
||||
property bool showAddDialog: false
|
||||
property int dialogMargins: 20
|
||||
@@ -17,9 +16,9 @@ Item {
|
||||
Keys.onPressed: (event) => {
|
||||
if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) && event.modifiers === Qt.NoModifier) {
|
||||
if (event.key === Qt.Key_PageDown) {
|
||||
currentTab = Math.min(currentTab + 1, root.tabButtonList.length - 1)
|
||||
tabBar.incrementCurrentIndex();
|
||||
} else if (event.key === Qt.Key_PageUp) {
|
||||
currentTab = Math.max(currentTab - 1, 0)
|
||||
tabBar.decrementCurrentIndex();
|
||||
}
|
||||
event.accepted = true;
|
||||
}
|
||||
@@ -39,82 +38,19 @@ Item {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
TabBar {
|
||||
SecondaryTabBar {
|
||||
id: tabBar
|
||||
Layout.fillWidth: true
|
||||
currentIndex: currentTab
|
||||
onCurrentIndexChanged: currentTab = currentIndex
|
||||
|
||||
background: Item {
|
||||
WheelHandler {
|
||||
onWheel: (event) => {
|
||||
if (event.angleDelta.y < 0)
|
||||
tabBar.currentIndex = Math.min(tabBar.currentIndex + 1, root.tabButtonList.length - 1)
|
||||
else if (event.angleDelta.y > 0)
|
||||
tabBar.currentIndex = Math.max(tabBar.currentIndex - 1, 0)
|
||||
}
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
}
|
||||
}
|
||||
currentIndex: swipeView.currentIndex
|
||||
|
||||
Repeater {
|
||||
model: root.tabButtonList
|
||||
delegate: SecondaryTabButton {
|
||||
selected: (index == currentTab)
|
||||
buttonText: modelData.name
|
||||
buttonIcon: modelData.icon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { // Tab indicator
|
||||
id: tabIndicator
|
||||
Layout.fillWidth: true
|
||||
height: 3
|
||||
property bool enableIndicatorAnimation: false
|
||||
Connections {
|
||||
target: root
|
||||
function onCurrentTabChanged() {
|
||||
tabIndicator.enableIndicatorAnimation = true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: indicator
|
||||
property int tabCount: root.tabButtonList.length
|
||||
property real fullTabSize: root.width / tabCount;
|
||||
property real targetWidth: tabBar?.contentItem?.children[0]?.children[tabBar.currentIndex]?.tabContentWidth ?? 0
|
||||
|
||||
implicitWidth: targetWidth
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
}
|
||||
|
||||
x: tabBar.currentIndex * fullTabSize + (fullTabSize - targetWidth) / 2
|
||||
|
||||
color: Appearance.colors.colPrimary
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
Behavior on x {
|
||||
enabled: tabIndicator.enableIndicatorAnimation
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
enabled: tabIndicator.enableIndicatorAnimation
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // Tabbar bottom border
|
||||
id: tabBarBottomBorder
|
||||
Layout.fillWidth: true
|
||||
height: 1
|
||||
color: Appearance.colors.colOutlineVariant
|
||||
}
|
||||
|
||||
SwipeView {
|
||||
id: swipeView
|
||||
Layout.topMargin: 10
|
||||
@@ -122,11 +58,7 @@ Item {
|
||||
Layout.fillHeight: true
|
||||
spacing: 10
|
||||
clip: true
|
||||
currentIndex: currentTab
|
||||
onCurrentIndexChanged: {
|
||||
tabIndicator.enableIndicatorAnimation = true
|
||||
currentTab = currentIndex
|
||||
}
|
||||
currentIndex: tabBar.currentIndex
|
||||
|
||||
// To Do tab
|
||||
TaskList {
|
||||
@@ -215,7 +147,7 @@ Item {
|
||||
Todo.addTask(todoInput.text)
|
||||
todoInput.text = ""
|
||||
root.showAddDialog = false
|
||||
root.currentTab = 0 // Show unfinished tasks
|
||||
tabBar.setCurrentIndex(0) // Show unfinished tasks
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ Item {
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
|
||||
Bar.ClockWidgetTooltip {
|
||||
Bar.ClockWidgetPopup {
|
||||
hoverTarget: mouseArea
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,30 +74,13 @@ MouseArea {
|
||||
anchors.centerIn: parent
|
||||
spacing: 4
|
||||
|
||||
Row {
|
||||
spacing: 4
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
fill: 0
|
||||
font.weight: Font.Medium
|
||||
text: "music_note"
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Media"
|
||||
font {
|
||||
weight: Font.Medium
|
||||
pixelSize: Appearance.font.pixelSize.normal
|
||||
}
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
Bar.StyledPopupHeaderRow {
|
||||
icon: "music_note"
|
||||
label: Translation.tr("Media")
|
||||
}
|
||||
|
||||
StyledText {
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
text: `${cleanedTitle}${activePlayer?.trackArtist ? '\n' + activePlayer.trackArtist : ''}`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
CONFIG_FILE="$HOME/.config/illogical-impulse/config.json"
|
||||
JSON_PATH=".screenRecord.savePath"
|
||||
|
||||
CUSTOM_PATH=$(jq -r "$JSON_PATH" "$CONFIG_FILE" 2>/dev/null)
|
||||
|
||||
RECORDING_DIR=""
|
||||
|
||||
if [[ -n "$CUSTOM_PATH" ]]; then
|
||||
RECORDING_DIR="$CUSTOM_PATH"
|
||||
else
|
||||
RECORDING_DIR="$HOME/Videos" # Use default path
|
||||
fi
|
||||
|
||||
getdate() {
|
||||
date '+%Y-%m-%d_%H.%M.%S'
|
||||
}
|
||||
@@ -10,12 +23,8 @@ getactivemonitor() {
|
||||
hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name'
|
||||
}
|
||||
|
||||
xdgvideo="$(xdg-user-dir VIDEOS)"
|
||||
if [[ $xdgvideo = "$HOME" ]]; then
|
||||
unset xdgvideo
|
||||
fi
|
||||
mkdir -p "${xdgvideo:-$HOME/Videos}"
|
||||
cd "${xdgvideo:-$HOME/Videos}" || exit
|
||||
mkdir -p "$RECORDING_DIR"
|
||||
cd "$RECORDING_DIR" || exit
|
||||
|
||||
# parse --region <value> without modifying $@ so other flags like --fullscreen still work
|
||||
ARGS=("$@")
|
||||
@@ -66,4 +75,4 @@ else
|
||||
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -31,6 +31,25 @@ Singleton {
|
||||
property real timeToEmpty: UPower.displayDevice.timeToEmpty
|
||||
property real timeToFull: UPower.displayDevice.timeToFull
|
||||
|
||||
property real health: (function() {
|
||||
const devList = UPower.devices.values;
|
||||
for (let i = 0; i < devList.length; ++i) {
|
||||
const dev = devList[i];
|
||||
if (dev.isLaptopBattery && dev.healthSupported) {
|
||||
const health = dev.healthPercentage;
|
||||
if (health === 0) {
|
||||
return 0.01;
|
||||
} else if (health < 1) {
|
||||
return health * 100;
|
||||
} else {
|
||||
return health;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
})()
|
||||
|
||||
|
||||
onIsLowAndNotChargingChanged: {
|
||||
if (!root.available || !isLowAndNotCharging) return;
|
||||
Quickshell.execDetached([
|
||||
|
||||
@@ -10,17 +10,55 @@ import Quickshell.Io
|
||||
* Simple polled resource usage service with RAM, Swap, and CPU usage.
|
||||
*/
|
||||
Singleton {
|
||||
property double memoryTotal: 1
|
||||
property double memoryFree: 1
|
||||
property double memoryUsed: memoryTotal - memoryFree
|
||||
property double memoryUsedPercentage: memoryUsed / memoryTotal
|
||||
property double swapTotal: 1
|
||||
property double swapFree: 1
|
||||
property double swapUsed: swapTotal - swapFree
|
||||
property double swapUsedPercentage: swapTotal > 0 ? (swapUsed / swapTotal) : 0
|
||||
property double cpuUsage: 0
|
||||
id: root
|
||||
property real memoryTotal: 1
|
||||
property real memoryFree: 0
|
||||
property real memoryUsed: memoryTotal - memoryFree
|
||||
property real memoryUsedPercentage: memoryUsed / memoryTotal
|
||||
property real swapTotal: 1
|
||||
property real swapFree: 0
|
||||
property real swapUsed: swapTotal - swapFree
|
||||
property real swapUsedPercentage: swapTotal > 0 ? (swapUsed / swapTotal) : 0
|
||||
property real cpuUsage: 0
|
||||
property var previousCpuStats
|
||||
|
||||
property string maxAvailableMemoryString: kbToGbString(ResourceUsage.memoryTotal)
|
||||
property string maxAvailableSwapString: kbToGbString(ResourceUsage.swapTotal)
|
||||
property string maxAvailableCpuString: "--"
|
||||
|
||||
readonly property int historyLength: Config?.options.resources.historyLength ?? 60
|
||||
property list<real> cpuUsageHistory: []
|
||||
property list<real> memoryUsageHistory: []
|
||||
property list<real> swapUsageHistory: []
|
||||
|
||||
function kbToGbString(kb) {
|
||||
return (kb / (1024 * 1024)).toFixed(1) + " GB";
|
||||
}
|
||||
|
||||
function updateMemoryUsageHistory() {
|
||||
memoryUsageHistory = [...memoryUsageHistory, memoryUsedPercentage]
|
||||
if (memoryUsageHistory.length > historyLength) {
|
||||
memoryUsageHistory.shift()
|
||||
}
|
||||
}
|
||||
function updateSwapUsageHistory() {
|
||||
swapUsageHistory = [...swapUsageHistory, swapUsedPercentage]
|
||||
if (swapUsageHistory.length > historyLength) {
|
||||
swapUsageHistory.shift()
|
||||
}
|
||||
}
|
||||
function updateCpuUsageHistory() {
|
||||
cpuUsageHistory = [...cpuUsageHistory, cpuUsage]
|
||||
if (cpuUsageHistory.length > historyLength) {
|
||||
cpuUsageHistory.shift()
|
||||
}
|
||||
}
|
||||
function updateHistories() {
|
||||
updateMemoryUsageHistory()
|
||||
updateSwapUsageHistory()
|
||||
updateCpuUsageHistory()
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 1
|
||||
running: true
|
||||
@@ -53,10 +91,24 @@ Singleton {
|
||||
|
||||
previousCpuStats = { total, idle }
|
||||
}
|
||||
|
||||
root.updateHistories()
|
||||
interval = Config.options?.resources?.updateInterval ?? 3000
|
||||
}
|
||||
}
|
||||
|
||||
FileView { id: fileMeminfo; path: "/proc/meminfo" }
|
||||
FileView { id: fileStat; path: "/proc/stat" }
|
||||
|
||||
Process {
|
||||
id: findCpuMaxFreqProc
|
||||
command: ["bash", "-c", "lscpu | grep 'CPU max MHz' | awk '{print $4}'"]
|
||||
running: true
|
||||
stdout: StdioCollector {
|
||||
id: outputCollector
|
||||
onStreamFinished: {
|
||||
root.maxAvailableCpuString = (parseFloat(outputCollector.text) / 1000).toFixed(0) + " GHz"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user