forked from Shinonome/dots-hyprland
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
|
# Cursor
|
||||||
exec-once = hyprctl setcursor Bibata-Modern-Classic 24
|
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
|
spacing: 4
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
Row {
|
StyledPopupHeaderRow {
|
||||||
id: header
|
icon: "battery_android_full"
|
||||||
spacing: 5
|
label: Translation.tr("Battery")
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This row is hidden when the battery is full.
|
StyledPopupValueRow {
|
||||||
RowLayout {
|
visible: {
|
||||||
spacing: 5
|
|
||||||
Layout.fillWidth: true
|
|
||||||
property bool rowVisible: {
|
|
||||||
let timeValue = Battery.isCharging ? Battery.timeToFull : Battery.timeToEmpty;
|
let timeValue = Battery.isCharging ? Battery.timeToFull : Battery.timeToEmpty;
|
||||||
let power = Battery.energyRate;
|
let power = Battery.energyRate;
|
||||||
return !(Battery.chargeState == 4 || timeValue <= 0 || power <= 0.01);
|
return !(Battery.chargeState == 4 || timeValue <= 0 || power <= 0.01);
|
||||||
}
|
}
|
||||||
visible: rowVisible
|
icon: "schedule"
|
||||||
opacity: rowVisible ? 1 : 0
|
label: Battery.isCharging ? Translation.tr("Time to full:") : Translation.tr("Time to empty:")
|
||||||
Behavior on opacity {
|
value: {
|
||||||
NumberAnimation {
|
function formatTime(seconds) {
|
||||||
duration: 500
|
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:");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
value: {
|
||||||
MaterialSymbol {
|
if (Battery.chargeState == 4) {
|
||||||
text: "schedule"
|
return "";
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
} else {
|
||||||
iconSize: Appearance.font.pixelSize.large
|
return `${Battery.energyRate.toFixed(2)}W`;
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
StyledPopupValueRow {
|
||||||
spacing: 5
|
icon: "heart_check"
|
||||||
Layout.fillWidth: true
|
label: Translation.tr("Health:")
|
||||||
|
value: `${(Battery.health).toFixed(1)}%`
|
||||||
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`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ Item {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
acceptedButtons: Qt.NoButton
|
acceptedButtons: Qt.NoButton
|
||||||
|
|
||||||
ClockWidgetTooltip {
|
ClockWidgetPopup {
|
||||||
hoverTarget: mouseArea
|
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";
|
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 {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: 12
|
spacing: 12
|
||||||
@@ -71,23 +20,23 @@ StyledPopup {
|
|||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
ResourceHeaderItem {
|
StyledPopupHeaderRow {
|
||||||
icon: "memory"
|
icon: "memory"
|
||||||
label: "RAM"
|
label: "RAM"
|
||||||
}
|
}
|
||||||
Column {
|
Column {
|
||||||
spacing: 4
|
spacing: 4
|
||||||
ResourceItem {
|
StyledPopupValueRow {
|
||||||
icon: "clock_loader_60"
|
icon: "clock_loader_60"
|
||||||
label: Translation.tr("Used:")
|
label: Translation.tr("Used:")
|
||||||
value: root.formatKB(ResourceUsage.memoryUsed)
|
value: root.formatKB(ResourceUsage.memoryUsed)
|
||||||
}
|
}
|
||||||
ResourceItem {
|
StyledPopupValueRow {
|
||||||
icon: "check_circle"
|
icon: "check_circle"
|
||||||
label: Translation.tr("Free:")
|
label: Translation.tr("Free:")
|
||||||
value: root.formatKB(ResourceUsage.memoryFree)
|
value: root.formatKB(ResourceUsage.memoryFree)
|
||||||
}
|
}
|
||||||
ResourceItem {
|
StyledPopupValueRow {
|
||||||
icon: "empty_dashboard"
|
icon: "empty_dashboard"
|
||||||
label: Translation.tr("Total:")
|
label: Translation.tr("Total:")
|
||||||
value: root.formatKB(ResourceUsage.memoryTotal)
|
value: root.formatKB(ResourceUsage.memoryTotal)
|
||||||
@@ -100,23 +49,23 @@ StyledPopup {
|
|||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
ResourceHeaderItem {
|
StyledPopupHeaderRow {
|
||||||
icon: "swap_horiz"
|
icon: "swap_horiz"
|
||||||
label: "Swap"
|
label: "Swap"
|
||||||
}
|
}
|
||||||
Column {
|
Column {
|
||||||
spacing: 4
|
spacing: 4
|
||||||
ResourceItem {
|
StyledPopupValueRow {
|
||||||
icon: "clock_loader_60"
|
icon: "clock_loader_60"
|
||||||
label: Translation.tr("Used:")
|
label: Translation.tr("Used:")
|
||||||
value: root.formatKB(ResourceUsage.swapUsed)
|
value: root.formatKB(ResourceUsage.swapUsed)
|
||||||
}
|
}
|
||||||
ResourceItem {
|
StyledPopupValueRow {
|
||||||
icon: "check_circle"
|
icon: "check_circle"
|
||||||
label: Translation.tr("Free:")
|
label: Translation.tr("Free:")
|
||||||
value: root.formatKB(ResourceUsage.swapFree)
|
value: root.formatKB(ResourceUsage.swapFree)
|
||||||
}
|
}
|
||||||
ResourceItem {
|
StyledPopupValueRow {
|
||||||
icon: "empty_dashboard"
|
icon: "empty_dashboard"
|
||||||
label: Translation.tr("Total:")
|
label: Translation.tr("Total:")
|
||||||
value: root.formatKB(ResourceUsage.swapTotal)
|
value: root.formatKB(ResourceUsage.swapTotal)
|
||||||
@@ -128,16 +77,16 @@ StyledPopup {
|
|||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
ResourceHeaderItem {
|
StyledPopupHeaderRow {
|
||||||
icon: "planner_review"
|
icon: "planner_review"
|
||||||
label: "CPU"
|
label: "CPU"
|
||||||
}
|
}
|
||||||
Column {
|
Column {
|
||||||
spacing: 4
|
spacing: 4
|
||||||
ResourceItem {
|
StyledPopupValueRow {
|
||||||
icon: "bolt"
|
icon: "bolt"
|
||||||
label: Translation.tr("Load:")
|
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
|
id: overflowPopup
|
||||||
hoverTarget: trayOverflowButton
|
hoverTarget: trayOverflowButton
|
||||||
active: root.trayOverflowOpen && root.unpinnedItems.length > 0
|
active: root.trayOverflowOpen && root.unpinnedItems.length > 0
|
||||||
popupBackgroundMargin: 300 // This should be plenty... makes sure tooltips don't get cutoff (easily)
|
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
id: trayOverflowLayout
|
id: trayOverflowLayout
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Item {
|
|||||||
visible: Config.options.bar.utilButtons.showScreenSnip
|
visible: Config.options.bar.utilButtons.showScreenSnip
|
||||||
sourceComponent: CircleUtilButton {
|
sourceComponent: CircleUtilButton {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
onClicked: Hyprland.dispatch("global quickshell:regionScreenshot")
|
onClicked: Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "screenshot"]);
|
||||||
MaterialSymbol {
|
MaterialSymbol {
|
||||||
horizontalAlignment: Qt.AlignHCenter
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
fill: 1
|
fill: 1
|
||||||
|
|||||||
@@ -14,9 +14,50 @@ Item {
|
|||||||
property real padding: 4
|
property real padding: 4
|
||||||
implicitWidth: row.implicitWidth + padding * 2
|
implicitWidth: row.implicitWidth + padding * 2
|
||||||
implicitHeight: row.implicitHeight + 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 keyBlacklist: ["Super_L"]
|
||||||
property var keySubstitutions: ({
|
property var keySubstitutions: Object.assign({
|
||||||
"Super": "",
|
"Super": "",
|
||||||
"mouse_up": "Scroll ↓", // ikr, weird
|
"mouse_up": "Scroll ↓", // ikr, weird
|
||||||
"mouse_down": "Scroll ↑", // trust me bro
|
"mouse_down": "Scroll ↑", // trust me bro
|
||||||
@@ -27,7 +68,14 @@ Item {
|
|||||||
"Hash": "#",
|
"Hash": "#",
|
||||||
"Return": "Enter",
|
"Return": "Enter",
|
||||||
// "Shift": "",
|
// "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
|
Row { // Keybind columns
|
||||||
id: row
|
id: row
|
||||||
@@ -77,6 +125,17 @@ Item {
|
|||||||
var result = [];
|
var result = [];
|
||||||
for (var i = 0; i < keybindSection.modelData.keybinds.length; i++) {
|
for (var i = 0; i < keybindSection.modelData.keybinds.length; i++) {
|
||||||
const keybind = keybindSection.modelData.keybinds[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({
|
result.push({
|
||||||
"type": "keys",
|
"type": "keys",
|
||||||
"mods": keybind.mods,
|
"mods": keybind.mods,
|
||||||
@@ -108,17 +167,19 @@ Item {
|
|||||||
delegate: KeyboardKey {
|
delegate: KeyboardKey {
|
||||||
required property var modelData
|
required property var modelData
|
||||||
key: keySubstitutions[modelData] || modelData
|
key: keySubstitutions[modelData] || modelData
|
||||||
|
pixelSize: Config.options.cheatsheet.fontSize.key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StyledText {
|
StyledText {
|
||||||
id: keybindPlus
|
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: "+"
|
text: "+"
|
||||||
}
|
}
|
||||||
KeyboardKey {
|
KeyboardKey {
|
||||||
id: keybindKey
|
id: keybindKey
|
||||||
visible: !keyBlacklist.includes(modelData.key)
|
visible: Config.options.cheatsheet.splitButtons && !keyBlacklist.includes(modelData.key)
|
||||||
key: keySubstitutions[modelData.key] || modelData.key
|
key: keySubstitutions[modelData.key] || modelData.key
|
||||||
|
pixelSize: Config.options.cheatsheet.fontSize.key
|
||||||
color: Appearance.colors.colOnLayer0
|
color: Appearance.colors.colOnLayer0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +195,7 @@ Item {
|
|||||||
StyledText {
|
StyledText {
|
||||||
id: commentText
|
id: commentText
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
font.pixelSize: Config.options.cheatsheet.fontSize.comment || Appearance.font.pixelSize.smaller
|
||||||
text: modelData.comment
|
text: modelData.comment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -267,6 +267,22 @@ Singleton {
|
|||||||
property int suspend: 3
|
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 JsonObject conflictKiller: JsonObject {
|
||||||
property bool autoKillNotificationDaemons: false
|
property bool autoKillNotificationDaemons: false
|
||||||
property bool autoKillTrays: false
|
property bool autoKillTrays: false
|
||||||
@@ -395,6 +411,7 @@ Singleton {
|
|||||||
|
|
||||||
property JsonObject resources: JsonObject {
|
property JsonObject resources: JsonObject {
|
||||||
property int updateInterval: 3000
|
property int updateInterval: 3000
|
||||||
|
property int historyLength: 60
|
||||||
}
|
}
|
||||||
|
|
||||||
property JsonObject musicRecognition: JsonObject {
|
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 JsonObject sounds: JsonObject {
|
||||||
property bool battery: false
|
property bool battery: false
|
||||||
property bool pomodoro: false
|
property bool pomodoro: false
|
||||||
|
|||||||
@@ -80,18 +80,31 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property JsonObject overlay: JsonObject {
|
property JsonObject overlay: JsonObject {
|
||||||
property list<string> open: ["crosshair"]
|
property list<string> open: ["crosshair", "recorder", "volumeMixer", "resources"]
|
||||||
property JsonObject crosshair: JsonObject {
|
property JsonObject crosshair: JsonObject {
|
||||||
property bool pinned: false
|
property bool pinned: false
|
||||||
property bool clickthrough: true
|
property bool clickthrough: true
|
||||||
property real x: 100
|
property real x: 835
|
||||||
property real y: 100
|
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 JsonObject volumeMixer: JsonObject {
|
||||||
property bool pinned: false
|
property bool pinned: false
|
||||||
property bool clickthrough: false
|
property bool clickthrough: false
|
||||||
property real x: 55
|
property real x: 80
|
||||||
property real y: 188
|
property real y: 280
|
||||||
}
|
}
|
||||||
property JsonObject fpsLimiter: JsonObject {
|
property JsonObject fpsLimiter: JsonObject {
|
||||||
property bool pinned: false
|
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 real extraBottomBorderWidth: 2
|
||||||
property color borderColor: Appearance.colors.colOnLayer0
|
property color borderColor: Appearance.colors.colOnLayer0
|
||||||
property real borderRadius: 5
|
property real borderRadius: 5
|
||||||
|
property real pixelSize: Appearance.font.pixelSize.smaller
|
||||||
property color keyColor: Appearance.m3colors.m3surfaceContainerLow
|
property color keyColor: Appearance.m3colors.m3surfaceContainerLow
|
||||||
implicitWidth: keyFace.implicitWidth + borderWidth * 2
|
implicitWidth: keyFace.implicitWidth + borderWidth * 2
|
||||||
implicitHeight: keyFace.implicitHeight + borderWidth * 2 + extraBottomBorderWidth
|
implicitHeight: keyFace.implicitHeight + borderWidth * 2 + extraBottomBorderWidth
|
||||||
@@ -35,7 +36,7 @@ Rectangle {
|
|||||||
id: keyText
|
id: keyText
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
font.family: Appearance.font.family.monospace
|
font.family: Appearance.font.family.monospace
|
||||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
font.pixelSize: root.pixelSize
|
||||||
text: key
|
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
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.modules.common.functions
|
import qs.modules.common.functions
|
||||||
|
import qs.modules.common.widgets
|
||||||
import Qt5Compat.GraphicalEffects
|
import Qt5Compat.GraphicalEffects
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
@@ -10,14 +10,12 @@ TabButton {
|
|||||||
id: root
|
id: root
|
||||||
property string buttonText
|
property string buttonText
|
||||||
property string buttonIcon
|
property string buttonIcon
|
||||||
property bool selected: false
|
|
||||||
property int rippleDuration: 1200
|
property int rippleDuration: 1200
|
||||||
height: buttonBackground.height
|
|
||||||
property int tabContentWidth: buttonBackground.width - buttonBackground.radius*2
|
property int tabContentWidth: buttonBackground.width - buttonBackground.radius*2
|
||||||
|
|
||||||
property color colBackground: ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)
|
property color colBackground: ColorUtils.transparentize(Appearance.colors.colSurfaceContainer)
|
||||||
property color colBackgroundHover: Appearance.colors.colLayer1Hover
|
property color colBackgroundHover: ColorUtils.transparentize(Appearance.colors.colOnSurface, root.checked ? 1 : 0.95)
|
||||||
property color colRipple: Appearance.colors.colLayer1Active
|
property color colRipple: ColorUtils.transparentize(Appearance.colors.colOnSurface, 0.95)
|
||||||
|
|
||||||
PointingHandInteraction {}
|
PointingHandInteraction {}
|
||||||
|
|
||||||
@@ -91,8 +89,12 @@ TabButton {
|
|||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
id: buttonBackground
|
id: buttonBackground
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 3
|
||||||
|
}
|
||||||
radius: Appearance?.rounding.normal
|
radius: Appearance?.rounding.normal
|
||||||
implicitHeight: 37
|
implicitHeight: 42
|
||||||
color: (root.hovered ? root.colBackgroundHover : root.colBackground)
|
color: (root.hovered ? root.colBackgroundHover : root.colBackground)
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
layer.effect: OpacityMask {
|
layer.effect: OpacityMask {
|
||||||
@@ -156,8 +158,8 @@ TabButton {
|
|||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
text: buttonIcon
|
text: buttonIcon
|
||||||
iconSize: Appearance.font.pixelSize.huge
|
iconSize: Appearance.font.pixelSize.huge
|
||||||
fill: selected ? 1 : 0
|
fill: root.checked ? 1 : 0
|
||||||
color: selected ? Appearance.colors.colPrimary : Appearance.colors.colOnLayer1
|
color: root.checked ? Appearance.colors.colPrimary : Appearance.colors.colOnLayer1
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
}
|
}
|
||||||
@@ -167,7 +169,7 @@ TabButton {
|
|||||||
id: buttonTextWidget
|
id: buttonTextWidget
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
font.pixelSize: Appearance.font.pixelSize.small
|
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
|
text: buttonText
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
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 {
|
IpcHandler {
|
||||||
target: "lock"
|
target: "lock"
|
||||||
|
|
||||||
function activate(): void {
|
function activate(): void {
|
||||||
GlobalStates.screenLocked = true;
|
root.lock();
|
||||||
}
|
}
|
||||||
function focus(): void {
|
function focus(): void {
|
||||||
lockContext.shouldReFocus();
|
lockContext.shouldReFocus();
|
||||||
@@ -144,11 +152,7 @@ Scope {
|
|||||||
description: "Locks the screen"
|
description: "Locks the screen"
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
if (Config.options.lock.useHyprlock) {
|
root.lock()
|
||||||
Quickshell.execDetached(["bash", "-c", "pidof hyprlock || hyprlock"]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
GlobalStates.screenLocked = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +169,7 @@ Scope {
|
|||||||
function initIfReady() {
|
function initIfReady() {
|
||||||
if (!Config.ready || !Persistent.ready) return;
|
if (!Config.ready || !Persistent.ready) return;
|
||||||
if (Config.options.lock.launchOnStartup && Persistent.isNewHyprlandInstance) {
|
if (Config.options.lock.launchOnStartup && Persistent.isNewHyprlandInstance) {
|
||||||
Hyprland.dispatch("global quickshell:lock")
|
root.lock();
|
||||||
} else {
|
} else {
|
||||||
KeyringStorage.fetchKeyringData();
|
KeyringStorage.fetchKeyringData();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ Singleton {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property list<var> availableWidgets: [
|
readonly property list<var> availableWidgets: [
|
||||||
|
{ identifier: "recorder", materialSymbol: "screen_record" },
|
||||||
|
{ identifier: "volumeMixer", materialSymbol: "volume_up" },
|
||||||
{ identifier: "crosshair", materialSymbol: "point_scan" },
|
{ identifier: "crosshair", materialSymbol: "point_scan" },
|
||||||
{ identifier: "fpsLimiter", materialSymbol: "animation" },
|
{ identifier: "fpsLimiter", materialSymbol: "animation" },
|
||||||
{ identifier: "volumeMixer", materialSymbol: "volume_up" }
|
{ identifier: "resources", materialSymbol: "browse_activity" }
|
||||||
]
|
]
|
||||||
|
|
||||||
readonly property bool hasPinnedWidgets: root.pinnedWidgetIdentifiers.length > 0
|
readonly property bool hasPinnedWidgets: root.pinnedWidgetIdentifiers.length > 0
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import Quickshell.Bluetooth
|
|||||||
import qs.modules.overlay.crosshair
|
import qs.modules.overlay.crosshair
|
||||||
import qs.modules.overlay.volumeMixer
|
import qs.modules.overlay.volumeMixer
|
||||||
import qs.modules.overlay.fpsLimiter
|
import qs.modules.overlay.fpsLimiter
|
||||||
|
import qs.modules.overlay.recorder
|
||||||
|
import qs.modules.overlay.resources
|
||||||
|
|
||||||
DelegateChooser {
|
DelegateChooser {
|
||||||
id: root
|
id: root
|
||||||
@@ -17,4 +19,6 @@ DelegateChooser {
|
|||||||
DelegateChoice { roleValue: "crosshair"; Crosshair {} }
|
DelegateChoice { roleValue: "crosshair"; Crosshair {} }
|
||||||
DelegateChoice { roleValue: "volumeMixer"; VolumeMixer {} }
|
DelegateChoice { roleValue: "volumeMixer"; VolumeMixer {} }
|
||||||
DelegateChoice { roleValue: "fpsLimiter"; FpsLimiter {} }
|
DelegateChoice { roleValue: "fpsLimiter"; FpsLimiter {} }
|
||||||
|
DelegateChoice { roleValue: "recorder"; Recorder {} }
|
||||||
|
DelegateChoice { roleValue: "resources"; Resources {} }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ AbstractOverlayWidget {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property Item contentItem
|
required property Item contentItem
|
||||||
|
property bool fancyBorders: true
|
||||||
|
|
||||||
required property var modelData
|
required property var modelData
|
||||||
readonly property string identifier: modelData.identifier
|
readonly property string identifier: modelData.identifier
|
||||||
@@ -29,6 +30,8 @@ AbstractOverlayWidget {
|
|||||||
property var persistentStateEntry: Persistent.states.overlay[identifier]
|
property var persistentStateEntry: Persistent.states.overlay[identifier]
|
||||||
property real radius: Appearance.rounding.windowRounding
|
property real radius: Appearance.rounding.windowRounding
|
||||||
property real minWidth: 250
|
property real minWidth: 250
|
||||||
|
property real padding: 6
|
||||||
|
property real contentRadius: radius - padding
|
||||||
|
|
||||||
draggable: GlobalStates.overlayOpen
|
draggable: GlobalStates.overlayOpen
|
||||||
x: Math.round(persistentStateEntry.x) // Round or it'll be blurry
|
x: Math.round(persistentStateEntry.x) // Round or it'll be blurry
|
||||||
@@ -96,11 +99,15 @@ AbstractOverlayWidget {
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
id: border
|
id: border
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "transparent"
|
color: (root.fancyBorders && GlobalStates.overlayOpen) ? Appearance.colors.colLayer1 : "transparent"
|
||||||
radius: root.radius
|
radius: root.radius
|
||||||
border.color: ColorUtils.transparentize(Appearance.colors.colOutlineVariant, GlobalStates.overlayOpen ? 0 : 1)
|
border.color: ColorUtils.transparentize(Appearance.colors.colOutlineVariant, GlobalStates.overlayOpen ? 0 : 1)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
|
||||||
layer.enabled: GlobalStates.overlayOpen
|
layer.enabled: GlobalStates.overlayOpen
|
||||||
layer.effect: OpacityMask {
|
layer.effect: OpacityMask {
|
||||||
maskSource: Rectangle {
|
maskSource: Rectangle {
|
||||||
@@ -110,25 +117,23 @@ AbstractOverlayWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
ColumnLayout {
|
||||||
id: contentColumn
|
id: contentColumn
|
||||||
z: -1
|
z: root.fancyBorders ? 0 : -1
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
// Title bar
|
// Title bar
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: titleBar
|
id: titleBar
|
||||||
opacity: GlobalStates.overlayOpen ? 1 : 0
|
opacity: GlobalStates.overlayOpen ? 1 : 0
|
||||||
anchors {
|
Layout.fillWidth: true
|
||||||
left: parent.left
|
property real padding: 6
|
||||||
right: parent.right
|
|
||||||
}
|
|
||||||
property real padding: 2
|
|
||||||
implicitWidth: titleBarRow.implicitWidth + padding * 2
|
implicitWidth: titleBarRow.implicitWidth + padding * 2
|
||||||
implicitHeight: titleBarRow.implicitHeight + padding * 2
|
implicitHeight: titleBarRow.implicitHeight + padding * 2
|
||||||
color: Appearance.m3colors.m3surfaceContainer
|
color: root.fancyBorders ? "transparent" : Appearance.colors.colLayer1
|
||||||
border.color: Appearance.colors.colOutlineVariant
|
// border.color: Appearance.colors.colOutlineVariant
|
||||||
border.width: 1
|
// border.width: 1
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: titleBarRow
|
id: titleBarRow
|
||||||
@@ -191,7 +196,10 @@ AbstractOverlayWidget {
|
|||||||
// Content
|
// Content
|
||||||
Item {
|
Item {
|
||||||
id: contentContainer
|
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
|
implicitWidth: root.contentItem.implicitWidth
|
||||||
implicitHeight: root.contentItem.implicitHeight
|
implicitHeight: root.contentItem.implicitHeight
|
||||||
children: [root.contentItem]
|
children: [root.contentItem]
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
import qs.modules.overlay
|
import qs.modules.overlay
|
||||||
|
|
||||||
StyledOverlayWidget {
|
StyledOverlayWidget {
|
||||||
id: root
|
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 {
|
contentItem: Rectangle {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
color: Appearance.m3colors.m3surfaceContainer
|
color: Appearance.m3colors.m3surfaceContainer
|
||||||
|
radius: root.contentRadius
|
||||||
property real padding: 16
|
property real padding: 16
|
||||||
implicitHeight: 700
|
implicitHeight: 600
|
||||||
implicitWidth: 400
|
implicitWidth: 350
|
||||||
|
|
||||||
VolumeDialogContent {
|
VolumeDialogContent {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ RowLayout {
|
|||||||
Layout.bottomMargin: 4
|
Layout.bottomMargin: 4
|
||||||
onClicked: {
|
onClicked: {
|
||||||
GlobalStates.overviewOpen = false;
|
GlobalStates.overviewOpen = false;
|
||||||
Hyprland.dispatch("global quickshell:regionSearch")
|
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "search"]);
|
||||||
}
|
}
|
||||||
text: "image_search"
|
text: "image_search"
|
||||||
StyledToolTip {
|
StyledToolTip {
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ PanelWindow {
|
|||||||
property var selectionMode: RegionSelection.SelectionMode.RectCorners
|
property var selectionMode: RegionSelection.SelectionMode.RectCorners
|
||||||
signal dismiss()
|
signal dismiss()
|
||||||
|
|
||||||
|
property string saveScreenshotDir: Config.options.screenSnip.savePath !== ""
|
||||||
|
? Config.options.screenSnip.savePath
|
||||||
|
: ""
|
||||||
|
|
||||||
property string screenshotDir: Directories.screenshotTemp
|
property string screenshotDir: Directories.screenshotTemp
|
||||||
property string imageSearchEngineBaseUrl: Config.options.search.imageSearch.imageSearchEngineBaseUrl
|
property string imageSearchEngineBaseUrl: Config.options.search.imageSearch.imageSearchEngineBaseUrl
|
||||||
property string fileUploadApiEndpoint: "https://uguu.se/upload"
|
property string fileUploadApiEndpoint: "https://uguu.se/upload"
|
||||||
@@ -259,7 +263,23 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
switch (root.action) {
|
switch (root.action) {
|
||||||
case RegionSelection.SnipAction.Copy:
|
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;
|
break;
|
||||||
case RegionSelection.SnipAction.Edit:
|
case RegionSelection.SnipAction.Edit:
|
||||||
snipProc.command = ["bash", "-c", `${cropToStdout} | swappy -f - && ${cleanup}`]
|
snipProc.command = ["bash", "-c", `${cropToStdout} | swappy -f - && ${cleanup}`]
|
||||||
|
|||||||
@@ -90,4 +90,6 @@ ContentPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ ContentPage {
|
|||||||
value: '[]'
|
value: '[]'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: Translation.tr("Japanese"),
|
displayName: Translation.tr("Han chars"),
|
||||||
icon: "square_dot",
|
icon: "square_dot",
|
||||||
value: '["一","二","三","四","五","六","七","八","九","十","十一","十二","十三","十四","十五","十六","十七","十八","十九","二十"]'
|
value: '["一","二","三","四","五","六","七","八","九","十","十一","十二","十三","十四","十五","十六","十七","十八","十九","二十"]'
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,6 +7,98 @@ import qs.modules.common.widgets
|
|||||||
ContentPage {
|
ContentPage {
|
||||||
forceWidth: true
|
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 {
|
ContentSection {
|
||||||
icon: "call_to_action"
|
icon: "call_to_action"
|
||||||
title: Translation.tr("Dock")
|
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 {
|
ContentSection {
|
||||||
icon: "search"
|
icon: "search"
|
||||||
title: Translation.tr("Search")
|
title: Translation.tr("Search")
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import QtQuick.Layouts
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
property int currentTab: 0
|
|
||||||
property var tabButtonList: [
|
property var tabButtonList: [
|
||||||
{"name": Translation.tr("Pomodoro"), "icon": "search_activity"},
|
{"name": Translation.tr("Pomodoro"), "icon": "search_activity"},
|
||||||
{"name": Translation.tr("Stopwatch"), "icon": "timer"}
|
{"name": Translation.tr("Stopwatch"), "icon": "timer"}
|
||||||
@@ -17,20 +16,20 @@ Item {
|
|||||||
Keys.onPressed: (event) => {
|
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 || event.key === Qt.Key_PageUp) && event.modifiers === Qt.NoModifier) { // Switch tabs
|
||||||
if (event.key === Qt.Key_PageDown) {
|
if (event.key === Qt.Key_PageDown) {
|
||||||
currentTab = Math.min(currentTab + 1, root.tabButtonList.length - 1)
|
tabBar.incrementCurrentIndex();
|
||||||
} else if (event.key === Qt.Key_PageUp) {
|
} else if (event.key === Qt.Key_PageUp) {
|
||||||
currentTab = Math.max(currentTab - 1, 0)
|
tabBar.decrementCurrentIndex();
|
||||||
}
|
}
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key === Qt.Key_Space || event.key === Qt.Key_S) { // Pause/resume with Space or S
|
} 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()
|
TimerService.togglePomodoro()
|
||||||
} else {
|
} else {
|
||||||
TimerService.toggleStopwatch()
|
TimerService.toggleStopwatch()
|
||||||
}
|
}
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key === Qt.Key_R) { // Reset with R
|
} else if (event.key === Qt.Key_R) { // Reset with R
|
||||||
if (currentTab === 0) {
|
if (tabBar.currentIndex === 0) {
|
||||||
TimerService.resetPomodoro()
|
TimerService.resetPomodoro()
|
||||||
} else {
|
} else {
|
||||||
TimerService.stopwatchReset()
|
TimerService.stopwatchReset()
|
||||||
@@ -46,82 +45,19 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
TabBar {
|
SecondaryTabBar {
|
||||||
id: tabBar
|
id: tabBar
|
||||||
Layout.fillWidth: true
|
currentIndex: swipeView.currentIndex
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: root.tabButtonList
|
model: root.tabButtonList
|
||||||
delegate: SecondaryTabButton {
|
delegate: SecondaryTabButton {
|
||||||
selected: (index == currentTab)
|
|
||||||
buttonText: modelData.name
|
buttonText: modelData.name
|
||||||
buttonIcon: modelData.icon
|
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 {
|
SwipeView {
|
||||||
id: swipeView
|
id: swipeView
|
||||||
Layout.topMargin: 10
|
Layout.topMargin: 10
|
||||||
@@ -129,11 +65,7 @@ Item {
|
|||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
spacing: 10
|
spacing: 10
|
||||||
clip: true
|
clip: true
|
||||||
currentIndex: currentTab
|
currentIndex: tabBar.currentIndex
|
||||||
onCurrentIndexChanged: {
|
|
||||||
tabIndicator.enableIndicatorAnimation = true
|
|
||||||
currentTab = currentIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tabs
|
// Tabs
|
||||||
PomodoroTimer {}
|
PomodoroTimer {}
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ AndroidQuickToggleButton {
|
|||||||
interval: 300
|
interval: 300
|
||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
Hyprland.dispatch("global quickshell:regionScreenshot")
|
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "screenshot"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import QtQuick.Layouts
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
property int currentTab: 0
|
|
||||||
property var tabButtonList: [{"icon": "checklist", "name": Translation.tr("Unfinished")}, {"name": Translation.tr("Done"), "icon": "check_circle"}]
|
property var tabButtonList: [{"icon": "checklist", "name": Translation.tr("Unfinished")}, {"name": Translation.tr("Done"), "icon": "check_circle"}]
|
||||||
property bool showAddDialog: false
|
property bool showAddDialog: false
|
||||||
property int dialogMargins: 20
|
property int dialogMargins: 20
|
||||||
@@ -17,9 +16,9 @@ Item {
|
|||||||
Keys.onPressed: (event) => {
|
Keys.onPressed: (event) => {
|
||||||
if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) && event.modifiers === Qt.NoModifier) {
|
if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) && event.modifiers === Qt.NoModifier) {
|
||||||
if (event.key === Qt.Key_PageDown) {
|
if (event.key === Qt.Key_PageDown) {
|
||||||
currentTab = Math.min(currentTab + 1, root.tabButtonList.length - 1)
|
tabBar.incrementCurrentIndex();
|
||||||
} else if (event.key === Qt.Key_PageUp) {
|
} else if (event.key === Qt.Key_PageUp) {
|
||||||
currentTab = Math.max(currentTab - 1, 0)
|
tabBar.decrementCurrentIndex();
|
||||||
}
|
}
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
@@ -39,82 +38,19 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
TabBar {
|
SecondaryTabBar {
|
||||||
id: tabBar
|
id: tabBar
|
||||||
Layout.fillWidth: true
|
currentIndex: swipeView.currentIndex
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: root.tabButtonList
|
model: root.tabButtonList
|
||||||
delegate: SecondaryTabButton {
|
delegate: SecondaryTabButton {
|
||||||
selected: (index == currentTab)
|
|
||||||
buttonText: modelData.name
|
buttonText: modelData.name
|
||||||
buttonIcon: modelData.icon
|
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 {
|
SwipeView {
|
||||||
id: swipeView
|
id: swipeView
|
||||||
Layout.topMargin: 10
|
Layout.topMargin: 10
|
||||||
@@ -122,11 +58,7 @@ Item {
|
|||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
spacing: 10
|
spacing: 10
|
||||||
clip: true
|
clip: true
|
||||||
currentIndex: currentTab
|
currentIndex: tabBar.currentIndex
|
||||||
onCurrentIndexChanged: {
|
|
||||||
tabIndicator.enableIndicatorAnimation = true
|
|
||||||
currentTab = currentIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
// To Do tab
|
// To Do tab
|
||||||
TaskList {
|
TaskList {
|
||||||
@@ -215,7 +147,7 @@ Item {
|
|||||||
Todo.addTask(todoInput.text)
|
Todo.addTask(todoInput.text)
|
||||||
todoInput.text = ""
|
todoInput.text = ""
|
||||||
root.showAddDialog = false
|
root.showAddDialog = false
|
||||||
root.currentTab = 0 // Show unfinished tasks
|
tabBar.setCurrentIndex(0) // Show unfinished tasks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ Item {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
acceptedButtons: Qt.NoButton
|
acceptedButtons: Qt.NoButton
|
||||||
|
|
||||||
Bar.ClockWidgetTooltip {
|
Bar.ClockWidgetPopup {
|
||||||
hoverTarget: mouseArea
|
hoverTarget: mouseArea
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,30 +74,13 @@ MouseArea {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
|
||||||
Row {
|
Bar.StyledPopupHeaderRow {
|
||||||
spacing: 4
|
icon: "music_note"
|
||||||
|
label: Translation.tr("Media")
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
|
color: Appearance.colors.colOnSurfaceVariant
|
||||||
text: `${cleanedTitle}${activePlayer?.trackArtist ? '\n' + activePlayer.trackArtist : ''}`
|
text: `${cleanedTitle}${activePlayer?.trackArtist ? '\n' + activePlayer.trackArtist : ''}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,18 @@
|
|||||||
#!/usr/bin/env bash
|
#!/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() {
|
getdate() {
|
||||||
date '+%Y-%m-%d_%H.%M.%S'
|
date '+%Y-%m-%d_%H.%M.%S'
|
||||||
}
|
}
|
||||||
@@ -10,12 +23,8 @@ getactivemonitor() {
|
|||||||
hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name'
|
hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name'
|
||||||
}
|
}
|
||||||
|
|
||||||
xdgvideo="$(xdg-user-dir VIDEOS)"
|
mkdir -p "$RECORDING_DIR"
|
||||||
if [[ $xdgvideo = "$HOME" ]]; then
|
cd "$RECORDING_DIR" || exit
|
||||||
unset xdgvideo
|
|
||||||
fi
|
|
||||||
mkdir -p "${xdgvideo:-$HOME/Videos}"
|
|
||||||
cd "${xdgvideo:-$HOME/Videos}" || exit
|
|
||||||
|
|
||||||
# parse --region <value> without modifying $@ so other flags like --fullscreen still work
|
# parse --region <value> without modifying $@ so other flags like --fullscreen still work
|
||||||
ARGS=("$@")
|
ARGS=("$@")
|
||||||
|
|||||||
@@ -31,6 +31,25 @@ Singleton {
|
|||||||
property real timeToEmpty: UPower.displayDevice.timeToEmpty
|
property real timeToEmpty: UPower.displayDevice.timeToEmpty
|
||||||
property real timeToFull: UPower.displayDevice.timeToFull
|
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: {
|
onIsLowAndNotChargingChanged: {
|
||||||
if (!root.available || !isLowAndNotCharging) return;
|
if (!root.available || !isLowAndNotCharging) return;
|
||||||
Quickshell.execDetached([
|
Quickshell.execDetached([
|
||||||
|
|||||||
@@ -10,17 +10,55 @@ import Quickshell.Io
|
|||||||
* Simple polled resource usage service with RAM, Swap, and CPU usage.
|
* Simple polled resource usage service with RAM, Swap, and CPU usage.
|
||||||
*/
|
*/
|
||||||
Singleton {
|
Singleton {
|
||||||
property double memoryTotal: 1
|
id: root
|
||||||
property double memoryFree: 1
|
property real memoryTotal: 1
|
||||||
property double memoryUsed: memoryTotal - memoryFree
|
property real memoryFree: 0
|
||||||
property double memoryUsedPercentage: memoryUsed / memoryTotal
|
property real memoryUsed: memoryTotal - memoryFree
|
||||||
property double swapTotal: 1
|
property real memoryUsedPercentage: memoryUsed / memoryTotal
|
||||||
property double swapFree: 1
|
property real swapTotal: 1
|
||||||
property double swapUsed: swapTotal - swapFree
|
property real swapFree: 0
|
||||||
property double swapUsedPercentage: swapTotal > 0 ? (swapUsed / swapTotal) : 0
|
property real swapUsed: swapTotal - swapFree
|
||||||
property double cpuUsage: 0
|
property real swapUsedPercentage: swapTotal > 0 ? (swapUsed / swapTotal) : 0
|
||||||
|
property real cpuUsage: 0
|
||||||
property var previousCpuStats
|
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 {
|
Timer {
|
||||||
interval: 1
|
interval: 1
|
||||||
running: true
|
running: true
|
||||||
@@ -53,10 +91,24 @@ Singleton {
|
|||||||
|
|
||||||
previousCpuStats = { total, idle }
|
previousCpuStats = { total, idle }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
root.updateHistories()
|
||||||
interval = Config.options?.resources?.updateInterval ?? 3000
|
interval = Config.options?.resources?.updateInterval ?? 3000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FileView { id: fileMeminfo; path: "/proc/meminfo" }
|
FileView { id: fileMeminfo; path: "/proc/meminfo" }
|
||||||
FileView { id: fileStat; path: "/proc/stat" }
|
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