Merge branch 'bootstrap-icons'
This commit is contained in:
commit
a4534b1611
77 changed files with 2939 additions and 720 deletions
|
|
@ -1,19 +1,19 @@
|
||||||
{
|
{
|
||||||
"dark": {
|
"dark": {
|
||||||
"mPrimary": "#ebbcba",
|
"mPrimary": "#ebbcba",
|
||||||
"mOnPrimary": "#1f1d2e",
|
"mOnPrimary": "#191724",
|
||||||
"mSecondary": "#9ccfd8",
|
"mSecondary": "#9ccfd8",
|
||||||
"mOnSecondary": "#1f1d2e",
|
"mOnSecondary": "#191724",
|
||||||
"mTertiary": "#f6c177",
|
"mTertiary": "#f6c177",
|
||||||
"mOnTertiary": "#1f1d2e",
|
"mOnTertiary": "#191724",
|
||||||
"mError": "#eb6f92",
|
"mError": "#eb6f92",
|
||||||
"mOnError": "#1f1d2e",
|
"mOnError": "#191724",
|
||||||
"mSurface": "#1f1d2e",
|
"mSurface": "#191724",
|
||||||
"mOnSurface": "#e0def4",
|
"mOnSurface": "#e0def4",
|
||||||
"mSurfaceVariant": "#26233a",
|
"mSurfaceVariant": "#26233a",
|
||||||
"mOnSurfaceVariant": "#908caa",
|
"mOnSurfaceVariant": "#908caa",
|
||||||
"mOutline": "#403d52",
|
"mOutline": "#403d52",
|
||||||
"mShadow": "#1f1d2e"
|
"mShadow": "#191724"
|
||||||
},
|
},
|
||||||
"light": {
|
"light": {
|
||||||
"mPrimary": "#d46e6b",
|
"mPrimary": "#d46e6b",
|
||||||
|
|
|
||||||
|
|
@ -1,270 +0,0 @@
|
||||||
#!/usr/bin/env -S bash
|
|
||||||
|
|
||||||
# A Bash script to monitor system stats and output them in JSON format.
|
|
||||||
|
|
||||||
# --- Configuration ---
|
|
||||||
# Default sleep duration in seconds. Can be overridden by the first argument.
|
|
||||||
SLEEP_DURATION=3
|
|
||||||
|
|
||||||
# --- Argument Parsing ---
|
|
||||||
# Check if a command-line argument is provided for the sleep duration.
|
|
||||||
if [[ -n "$1" ]]; then
|
|
||||||
# Basic validation to ensure the argument is a number (integer or float).
|
|
||||||
if [[ "$1" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
|
|
||||||
SLEEP_DURATION=$1
|
|
||||||
else
|
|
||||||
# Output to stderr if the format is invalid.
|
|
||||||
echo "Warning: Invalid duration format '$1'. Using default of ${SLEEP_DURATION}s." >&2
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Global Cache Variables ---
|
|
||||||
# These variables will store the discovered CPU temperature sensor path and type
|
|
||||||
# to avoid searching for it on every loop iteration.
|
|
||||||
TEMP_SENSOR_PATH=""
|
|
||||||
TEMP_SENSOR_TYPE=""
|
|
||||||
|
|
||||||
# Network speed monitoring variables
|
|
||||||
PREV_RX_BYTES=0
|
|
||||||
PREV_TX_BYTES=0
|
|
||||||
PREV_TIME=0
|
|
||||||
|
|
||||||
# --- Data Collection Functions ---
|
|
||||||
|
|
||||||
#
|
|
||||||
# Gets memory usage in GB, MB, and as a percentage.
|
|
||||||
#
|
|
||||||
get_memory_info() {
|
|
||||||
awk '
|
|
||||||
/MemTotal/ {total=$2}
|
|
||||||
/MemAvailable/ {available=$2}
|
|
||||||
END {
|
|
||||||
if (total > 0) {
|
|
||||||
usage_kb = total - available
|
|
||||||
usage_gb = usage_kb / 1000000
|
|
||||||
usage_percent = (usage_kb / total) * 100
|
|
||||||
printf "%.1f %.0f\n", usage_gb, usage_percent
|
|
||||||
} else {
|
|
||||||
# Fallback if /proc/meminfo is unreadable or empty.
|
|
||||||
print "0.0 0 0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
' /proc/meminfo
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Gets the usage percentage of the root filesystem ("/").
|
|
||||||
#
|
|
||||||
get_disk_usage() {
|
|
||||||
# df gets disk usage. --output=pcent shows only the percentage for the root path.
|
|
||||||
# tail -1 gets the data line, and tr removes the '%' sign and whitespace.
|
|
||||||
df --output=pcent / | tail -1 | tr -d ' %'
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Calculates current CPU usage over a short interval.
|
|
||||||
#
|
|
||||||
get_cpu_usage() {
|
|
||||||
# Read all 10 CPU time fields to prevent errors on newer kernels.
|
|
||||||
read -r cpu prev_user prev_nice prev_system prev_idle prev_iowait prev_irq prev_softirq prev_steal prev_guest prev_guest_nice < /proc/stat
|
|
||||||
|
|
||||||
# Calculate previous total and idle times.
|
|
||||||
local prev_total_idle=$((prev_idle + prev_iowait))
|
|
||||||
local prev_total=$((prev_user + prev_nice + prev_system + prev_idle + prev_iowait + prev_irq + prev_softirq + prev_steal + prev_guest + prev_guest_nice))
|
|
||||||
|
|
||||||
# Wait for a short period.
|
|
||||||
sleep 0.05
|
|
||||||
|
|
||||||
# Read all 10 CPU time fields again for the second measurement.
|
|
||||||
read -r cpu user nice system idle iowait irq softirq steal guest guest_nice < /proc/stat
|
|
||||||
|
|
||||||
# Calculate new total and idle times.
|
|
||||||
local total_idle=$((idle + iowait))
|
|
||||||
local total=$((user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice))
|
|
||||||
|
|
||||||
# Add a check to prevent division by zero if total hasn't changed.
|
|
||||||
if (( total <= prev_total )); then
|
|
||||||
echo "0.0"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Calculate the difference over the interval.
|
|
||||||
local diff_total=$((total - prev_total))
|
|
||||||
local diff_idle=$((total_idle - prev_total_idle))
|
|
||||||
|
|
||||||
# Use awk for floating-point calculation and print the percentage.
|
|
||||||
awk -v total="$diff_total" -v idle="$diff_idle" '
|
|
||||||
BEGIN {
|
|
||||||
if (total > 0) {
|
|
||||||
# Formula: 100 * (Total - Idle) / Total
|
|
||||||
usage = 100 * (total - idle) / total
|
|
||||||
printf "%.1f\n", usage
|
|
||||||
} else {
|
|
||||||
print "0.0"
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Finds and returns the CPU temperature in degrees Celsius.
|
|
||||||
# Caches the sensor path for efficiency.
|
|
||||||
#
|
|
||||||
get_cpu_temp() {
|
|
||||||
# If the sensor path hasn't been found yet, search for it.
|
|
||||||
if [[ -z "$TEMP_SENSOR_PATH" ]]; then
|
|
||||||
for dir in /sys/class/hwmon/hwmon*; do
|
|
||||||
# Check if the 'name' file exists and read it.
|
|
||||||
if [[ -f "$dir/name" ]]; then
|
|
||||||
local name
|
|
||||||
name=$(<"$dir/name")
|
|
||||||
# Check for supported sensor types.
|
|
||||||
if [[ "$name" == "coretemp" || "$name" == "k10temp" || "$name" == "zenpower" ]]; then
|
|
||||||
TEMP_SENSOR_PATH=$dir
|
|
||||||
TEMP_SENSOR_TYPE=$name
|
|
||||||
break # Found it, no need to keep searching.
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If after searching no sensor was found, return 0.
|
|
||||||
if [[ -z "$TEMP_SENSOR_PATH" ]]; then
|
|
||||||
echo 0
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Get temp based on sensor type ---
|
|
||||||
if [[ "$TEMP_SENSOR_TYPE" == "coretemp" ]]; then
|
|
||||||
# For Intel 'coretemp', average all available temperature sensors.
|
|
||||||
local total_temp=0
|
|
||||||
local sensor_count=0
|
|
||||||
|
|
||||||
# Use a for loop with a glob to iterate over all temp input files.
|
|
||||||
# This is more efficient than 'find' for this simple case.
|
|
||||||
for temp_file in "$TEMP_SENSOR_PATH"/temp*_input; do
|
|
||||||
# The glob returns the pattern itself if no files match,
|
|
||||||
# so we must check if the file actually exists.
|
|
||||||
if [[ -f "$temp_file" ]]; then
|
|
||||||
total_temp=$((total_temp + $(<"$temp_file")))
|
|
||||||
sensor_count=$((sensor_count + 1))
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if (( sensor_count > 0 )); then
|
|
||||||
# Use awk for the final division to handle potential floating point numbers
|
|
||||||
# and convert from millidegrees to integer degrees Celsius.
|
|
||||||
awk -v total="$total_temp" -v count="$sensor_count" 'BEGIN { print int(total / count / 1000) }'
|
|
||||||
else
|
|
||||||
# If no sensor files were found, return 0.
|
|
||||||
echo 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
elif [[ "$TEMP_SENSOR_TYPE" == "k10temp" ]]; then
|
|
||||||
# For AMD 'k10temp', find the 'Tctl' sensor, which is the control temperature.
|
|
||||||
local tctl_input=""
|
|
||||||
for label_file in "$TEMP_SENSOR_PATH"/temp*_label; do
|
|
||||||
if [[ -f "$label_file" ]] && [[ $(<"$label_file") == "Tctl" ]]; then
|
|
||||||
# The input file has the same name but with '_input' instead of '_label'.
|
|
||||||
tctl_input="${label_file%_label}_input"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ -f "$tctl_input" ]]; then
|
|
||||||
# Read the temperature and convert from millidegrees to degrees.
|
|
||||||
echo "$(( $(<"$tctl_input") / 1000 ))"
|
|
||||||
else
|
|
||||||
echo 0 # Fallback
|
|
||||||
fi
|
|
||||||
elif [[ "$TEMP_SENSOR_TYPE" == "zenpower" ]]; then
|
|
||||||
# For zenpower, read the first available temp sensor
|
|
||||||
for temp_file in "$TEMP_SENSOR_PATH"/temp*_input; do
|
|
||||||
if [[ -f "$temp_file" ]]; then
|
|
||||||
local temp_value
|
|
||||||
temp_value=$(cat "$temp_file" | tr -d '\n\r') # Remove any newlines
|
|
||||||
echo "$((temp_value / 1000))"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
echo 0
|
|
||||||
|
|
||||||
if [[ -f "$tctl_input" ]]; then
|
|
||||||
# Read the temperature and convert from millidegrees to degrees.
|
|
||||||
echo "$(($(<"$tctl_input") / 1000))"
|
|
||||||
else
|
|
||||||
echo 0 # Fallback
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo 0 # Should not happen if cache logic is correct.
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# --- Main Loop ---
|
|
||||||
# This loop runs indefinitely, gathering and printing stats.
|
|
||||||
while true; do
|
|
||||||
# Call the functions to gather all the data.
|
|
||||||
# get_memory_info
|
|
||||||
read -r mem_gb mem_per <<< "$(get_memory_info)"
|
|
||||||
|
|
||||||
# Command substitution captures the single output from the other functions.
|
|
||||||
disk_per=$(get_disk_usage)
|
|
||||||
cpu_usage=$(get_cpu_usage)
|
|
||||||
cpu_temp=$(get_cpu_temp)
|
|
||||||
|
|
||||||
# Get network speeds
|
|
||||||
current_time=$(date +%s.%N)
|
|
||||||
total_rx=0
|
|
||||||
total_tx=0
|
|
||||||
|
|
||||||
# Read total bytes from /proc/net/dev for all interfaces
|
|
||||||
while IFS=: read -r interface stats; do
|
|
||||||
# Skip only loopback interface, allow other interfaces
|
|
||||||
if [[ "$interface" =~ ^lo[[:space:]]*$ ]]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Extract rx and tx bytes (fields 1 and 9 in the stats part)
|
|
||||||
rx_bytes=$(echo "$stats" | awk '{print $1}')
|
|
||||||
tx_bytes=$(echo "$stats" | awk '{print $9}')
|
|
||||||
|
|
||||||
# Add to totals if they are valid numbers
|
|
||||||
if [[ "$rx_bytes" =~ ^[0-9]+$ ]] && [[ "$tx_bytes" =~ ^[0-9]+$ ]]; then
|
|
||||||
total_rx=$((total_rx + rx_bytes))
|
|
||||||
total_tx=$((total_tx + tx_bytes))
|
|
||||||
fi
|
|
||||||
done < <(tail -n +3 /proc/net/dev)
|
|
||||||
|
|
||||||
# Calculate speeds if we have previous data
|
|
||||||
rx_speed=0
|
|
||||||
tx_speed=0
|
|
||||||
|
|
||||||
if [[ "$PREV_TIME" != "0" ]]; then
|
|
||||||
time_diff=$(awk -v current="$current_time" -v prev="$PREV_TIME" 'BEGIN { printf "%.3f", current - prev }')
|
|
||||||
rx_diff=$((total_rx - PREV_RX_BYTES))
|
|
||||||
tx_diff=$((total_tx - PREV_TX_BYTES))
|
|
||||||
|
|
||||||
# Calculate speeds in bytes per second using awk
|
|
||||||
rx_speed=$(awk -v rx="$rx_diff" -v time="$time_diff" 'BEGIN { printf "%.0f", rx / time }')
|
|
||||||
tx_speed=$(awk -v tx="$tx_diff" -v time="$time_diff" 'BEGIN { printf "%.0f", tx / time }')
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Update previous values for next iteration
|
|
||||||
PREV_RX_BYTES=$total_rx
|
|
||||||
PREV_TX_BYTES=$total_tx
|
|
||||||
PREV_TIME=$current_time
|
|
||||||
|
|
||||||
# Use printf to format the final JSON output string, adding the mem_mb key.
|
|
||||||
printf '{"cpu": "%s", "cputemp": "%s", "memgb":"%s", "memper": "%s", "diskper": "%s", "rx_speed": "%s", "tx_speed": "%s"}\n' \
|
|
||||||
"$cpu_usage" \
|
|
||||||
"$cpu_temp" \
|
|
||||||
"$mem_gb" \
|
|
||||||
"$mem_per" \
|
|
||||||
"$disk_per" \
|
|
||||||
"$rx_speed" \
|
|
||||||
"$tx_speed"
|
|
||||||
|
|
||||||
# Wait for the specified duration before the next update.
|
|
||||||
sleep "$SLEEP_DURATION"
|
|
||||||
done
|
|
||||||
2090
Commons/Bootstrap.qml
Normal file
2090
Commons/Bootstrap.qml
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -28,7 +28,7 @@ NPanel {
|
||||||
spacing: Style.marginM * scaling
|
spacing: Style.marginM * scaling
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "system_update_alt"
|
icon: "box"
|
||||||
font.pointSize: Style.fontSizeXXL * scaling
|
font.pointSize: Style.fontSizeXXL * scaling
|
||||||
color: Color.mPrimary
|
color: Color.mPrimary
|
||||||
}
|
}
|
||||||
|
|
@ -44,7 +44,7 @@ NPanel {
|
||||||
// Reset button (only show if update failed)
|
// Reset button (only show if update failed)
|
||||||
NIconButton {
|
NIconButton {
|
||||||
visible: ArchUpdaterService.updateFailed
|
visible: ArchUpdaterService.updateFailed
|
||||||
icon: "refresh"
|
icon: "arrow-repeat"
|
||||||
tooltipText: "Reset update state"
|
tooltipText: "Reset update state"
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
colorBg: Color.mError
|
colorBg: Color.mError
|
||||||
|
|
@ -55,7 +55,7 @@ NPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "close"
|
icon: "x-lg"
|
||||||
tooltipText: "Close"
|
tooltipText: "Close"
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
onClicked: root.close()
|
onClicked: root.close()
|
||||||
|
|
@ -103,7 +103,7 @@ NPanel {
|
||||||
} // Spacer
|
} // Spacer
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "hourglass_empty"
|
icon: "hourglass"
|
||||||
font.pointSize: Style.fontSizeXXXL * scaling
|
font.pointSize: Style.fontSizeXXXL * scaling
|
||||||
color: Color.mPrimary
|
color: Color.mPrimary
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
@ -143,7 +143,7 @@ NPanel {
|
||||||
spacing: Style.marginM * scaling
|
spacing: Style.marginM * scaling
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "terminal"
|
icon: "terminal"
|
||||||
font.pointSize: Style.fontSizeXXXL * scaling
|
font.pointSize: Style.fontSizeXXXL * scaling
|
||||||
color: Color.mError
|
color: Color.mError
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
@ -181,7 +181,7 @@ NPanel {
|
||||||
spacing: Style.marginM * scaling
|
spacing: Style.marginM * scaling
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "package"
|
icon: "box"
|
||||||
font.pointSize: Style.fontSizeXXXL * scaling
|
font.pointSize: Style.fontSizeXXXL * scaling
|
||||||
color: Color.mError
|
color: Color.mError
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
@ -219,7 +219,7 @@ NPanel {
|
||||||
spacing: Style.marginM * scaling
|
spacing: Style.marginM * scaling
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "error"
|
icon: "exclamation-triangle"
|
||||||
font.pointSize: Style.fontSizeXXXL * scaling
|
font.pointSize: Style.fontSizeXXXL * scaling
|
||||||
color: Color.mError
|
color: Color.mError
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
@ -245,7 +245,7 @@ NPanel {
|
||||||
|
|
||||||
// Prominent refresh button
|
// Prominent refresh button
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "refresh"
|
icon: "arrow-repeat"
|
||||||
tooltipText: "Try checking again"
|
tooltipText: "Try checking again"
|
||||||
sizeRatio: 1.2
|
sizeRatio: 1.2
|
||||||
colorBg: Color.mPrimary
|
colorBg: Color.mPrimary
|
||||||
|
|
@ -270,7 +270,7 @@ NPanel {
|
||||||
spacing: Style.marginM * scaling
|
spacing: Style.marginM * scaling
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "error_outline"
|
icon: "exclamation-triangle"
|
||||||
font.pointSize: Style.fontSizeXXXL * scaling
|
font.pointSize: Style.fontSizeXXXL * scaling
|
||||||
color: Color.mError
|
color: Color.mError
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
@ -295,7 +295,7 @@ NPanel {
|
||||||
|
|
||||||
// Prominent refresh button
|
// Prominent refresh button
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "refresh"
|
icon: "arrow-repeat"
|
||||||
tooltipText: "Refresh and try again"
|
tooltipText: "Refresh and try again"
|
||||||
sizeRatio: 1.2
|
sizeRatio: 1.2
|
||||||
colorBg: Color.mPrimary
|
colorBg: Color.mPrimary
|
||||||
|
|
@ -323,7 +323,7 @@ NPanel {
|
||||||
spacing: Style.marginM * scaling
|
spacing: Style.marginM * scaling
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "check_circle"
|
icon: "check-lg"
|
||||||
font.pointSize: Style.fontSizeXXXL * scaling
|
font.pointSize: Style.fontSizeXXXL * scaling
|
||||||
color: Color.mPrimary
|
color: Color.mPrimary
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
@ -483,7 +483,7 @@ NPanel {
|
||||||
spacing: Style.marginL * scaling
|
spacing: Style.marginL * scaling
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "refresh"
|
icon: "arrow-repeat"
|
||||||
tooltipText: ArchUpdaterService.aurBusy ? "Checking for updates..." : (!ArchUpdaterService.canPoll ? "Refresh available soon" : "Refresh package lists")
|
tooltipText: ArchUpdaterService.aurBusy ? "Checking for updates..." : (!ArchUpdaterService.canPoll ? "Refresh available soon" : "Refresh package lists")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
ArchUpdaterService.forceRefresh()
|
ArchUpdaterService.forceRefresh()
|
||||||
|
|
@ -495,7 +495,7 @@ NPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "system_update_alt"
|
icon: "box-fill"
|
||||||
tooltipText: "Update all packages"
|
tooltipText: "Update all packages"
|
||||||
enabled: ArchUpdaterService.totalUpdates > 0
|
enabled: ArchUpdaterService.totalUpdates > 0
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|
@ -508,7 +508,7 @@ NPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "check_box"
|
icon: "box"
|
||||||
tooltipText: "Update selected packages"
|
tooltipText: "Update selected packages"
|
||||||
enabled: ArchUpdaterService.selectedPackagesCount > 0
|
enabled: ArchUpdaterService.selectedPackagesCount > 0
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ Variants {
|
||||||
property real screenWidth: width
|
property real screenWidth: width
|
||||||
property real screenHeight: height
|
property real screenHeight: height
|
||||||
|
|
||||||
fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_fade.frag.qsb")
|
fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/wp_fade.frag.qsb")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wipe transition shader
|
// Wipe transition shader
|
||||||
|
|
@ -164,7 +164,7 @@ Variants {
|
||||||
property real screenWidth: width
|
property real screenWidth: width
|
||||||
property real screenHeight: height
|
property real screenHeight: height
|
||||||
|
|
||||||
fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_wipe.frag.qsb")
|
fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/wp_wipe.frag.qsb")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disc reveal transition shader
|
// Disc reveal transition shader
|
||||||
|
|
@ -191,7 +191,7 @@ Variants {
|
||||||
property real screenWidth: width
|
property real screenWidth: width
|
||||||
property real screenHeight: height
|
property real screenHeight: height
|
||||||
|
|
||||||
fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_disc.frag.qsb")
|
fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/wp_disc.frag.qsb")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Diagonal stripes transition shader
|
// Diagonal stripes transition shader
|
||||||
|
|
@ -218,7 +218,7 @@ Variants {
|
||||||
property real screenWidth: width
|
property real screenWidth: width
|
||||||
property real screenHeight: height
|
property real screenHeight: height
|
||||||
|
|
||||||
fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_stripes.frag.qsb")
|
fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/wp_stripes.frag.qsb")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Animation for the transition progress
|
// Animation for the transition progress
|
||||||
|
|
|
||||||
|
|
@ -33,28 +33,34 @@ RowLayout {
|
||||||
|
|
||||||
readonly property bool showIcon: (widgetSettings.showIcon !== undefined) ? widgetSettings.showIcon : widgetMetadata.showIcon
|
readonly property bool showIcon: (widgetSettings.showIcon !== undefined) ? widgetSettings.showIcon : widgetMetadata.showIcon
|
||||||
|
|
||||||
readonly property real minWidth: 160
|
// 6% of total width
|
||||||
readonly property real maxWidth: 400
|
readonly property real minWidth: Math.max(1, screen.width * 0.06)
|
||||||
Layout.alignment: Qt.AlignVCenter
|
readonly property real maxWidth: minWidth * 2
|
||||||
spacing: Style.marginS * scaling
|
|
||||||
visible: getTitle() !== ""
|
|
||||||
|
|
||||||
function getTitle() {
|
function getTitle() {
|
||||||
return CompositorService.focusedWindowTitle !== "(No active window)" ? CompositorService.focusedWindowTitle : ""
|
return CompositorService.focusedWindowTitle !== "(No active window)" ? CompositorService.focusedWindowTitle : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
spacing: Style.marginS * scaling
|
||||||
|
visible: getTitle() !== ""
|
||||||
|
|
||||||
function getAppIcon() {
|
function getAppIcon() {
|
||||||
// Try CompositorService first
|
// Try CompositorService first
|
||||||
const focusedWindow = CompositorService.getFocusedWindow()
|
const focusedWindow = CompositorService.getFocusedWindow()
|
||||||
if (focusedWindow && focusedWindow.appId) {
|
if (focusedWindow && focusedWindow.appId) {
|
||||||
return Icons.iconForAppId(focusedWindow.appId.toLowerCase())
|
const idValue = focusedWindow.appId
|
||||||
|
const normalizedId = (typeof idValue === 'string') ? idValue : String(idValue)
|
||||||
|
return Icons.iconForAppId(normalizedId.toLowerCase())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to ToplevelManager
|
// Fallback to ToplevelManager
|
||||||
if (ToplevelManager && ToplevelManager.activeToplevel) {
|
if (ToplevelManager && ToplevelManager.activeToplevel) {
|
||||||
const activeToplevel = ToplevelManager.activeToplevel
|
const activeToplevel = ToplevelManager.activeToplevel
|
||||||
if (activeToplevel.appId) {
|
if (activeToplevel.appId) {
|
||||||
return Icons.iconForAppId(activeToplevel.appId.toLowerCase())
|
const idValue2 = activeToplevel.appId
|
||||||
|
const normalizedId2 = (typeof idValue2 === 'string') ? idValue2 : String(idValue2)
|
||||||
|
return Icons.iconForAppId(normalizedId2.toLowerCase())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,7 +129,7 @@ RowLayout {
|
||||||
font.weight: Style.fontWeightMedium
|
font.weight: Style.fontWeightMedium
|
||||||
elide: mouseArea.containsMouse ? Text.ElideNone : Text.ElideRight
|
elide: mouseArea.containsMouse ? Text.ElideNone : Text.ElideRight
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
color: Color.mSecondary
|
color: Color.mPrimary
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
Behavior on Layout.preferredWidth {
|
Behavior on Layout.preferredWidth {
|
||||||
|
|
|
||||||
|
|
@ -29,15 +29,15 @@ NIconButton {
|
||||||
return "terminal"
|
return "terminal"
|
||||||
}
|
}
|
||||||
if (!ArchUpdaterService.aurHelperAvailable) {
|
if (!ArchUpdaterService.aurHelperAvailable) {
|
||||||
return "package"
|
return "box"
|
||||||
}
|
}
|
||||||
if (ArchUpdaterService.aurBusy) {
|
if (ArchUpdaterService.aurBusy) {
|
||||||
return "sync"
|
return "arrow-repeat"
|
||||||
}
|
}
|
||||||
if (ArchUpdaterService.totalUpdates > 0) {
|
if (ArchUpdaterService.totalUpdates > 0) {
|
||||||
return "system_update_alt"
|
return "box-fill"
|
||||||
}
|
}
|
||||||
return "task_alt"
|
return "box"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tooltip with repo vs AUR breakdown and sample lists
|
// Tooltip with repo vs AUR breakdown and sample lists
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ Item {
|
||||||
// Test mode
|
// Test mode
|
||||||
readonly property bool testMode: false
|
readonly property bool testMode: false
|
||||||
readonly property int testPercent: 50
|
readonly property int testPercent: 50
|
||||||
readonly property bool testCharging: true
|
readonly property bool testCharging: false
|
||||||
|
|
||||||
// Main properties
|
// Main properties
|
||||||
readonly property var battery: UPower.displayDevice
|
readonly property var battery: UPower.displayDevice
|
||||||
|
|
@ -57,9 +57,7 @@ Item {
|
||||||
// Only notify once we are a below threshold
|
// Only notify once we are a below threshold
|
||||||
if (!charging && !root.hasNotifiedLowBattery && percent <= warningThreshold) {
|
if (!charging && !root.hasNotifiedLowBattery && percent <= warningThreshold) {
|
||||||
root.hasNotifiedLowBattery = true
|
root.hasNotifiedLowBattery = true
|
||||||
// Maybe go with toast ?
|
ToastService.showWarning("Low Battery", `Battery is at ${Math.round(percent)}%. Please connect the charger.`)
|
||||||
Quickshell.execDetached(
|
|
||||||
["notify-send", "-u", "critical", "-i", "battery-caution", "Low Battery", `Battery is at ${p}%. Please connect charger.`])
|
|
||||||
} else if (root.hasNotifiedLowBattery && (charging || percent > warningThreshold + 5)) {
|
} else if (root.hasNotifiedLowBattery && (charging || percent > warningThreshold + 5)) {
|
||||||
// Reset when charging starts or when battery recovers 5% above threshold
|
// Reset when charging starts or when battery recovers 5% above threshold
|
||||||
root.hasNotifiedLowBattery = false
|
root.hasNotifiedLowBattery = false
|
||||||
|
|
@ -87,11 +85,7 @@ Item {
|
||||||
rightOpen: BarWidgetRegistry.getNPillDirection(root)
|
rightOpen: BarWidgetRegistry.getNPillDirection(root)
|
||||||
icon: testMode ? BatteryService.getIcon(testPercent, testCharging, true) : BatteryService.getIcon(percent,
|
icon: testMode ? BatteryService.getIcon(testPercent, testCharging, true) : BatteryService.getIcon(percent,
|
||||||
charging, isReady)
|
charging, isReady)
|
||||||
iconRotation: -90
|
text: (isReady || testMode) ? Math.round(percent) + "%" : "-"
|
||||||
text: ((isReady && battery.isLaptopBattery) || testMode) ? Math.round(percent) + "%" : "-"
|
|
||||||
textColor: charging ? Color.mPrimary : Color.mOnSurface
|
|
||||||
iconCircleColor: Color.mPrimary
|
|
||||||
collapsedIconColor: Color.mOnSurface
|
|
||||||
autoHide: false
|
autoHide: false
|
||||||
forceOpen: isReady && (testMode || battery.isLaptopBattery) && alwaysShowPercentage
|
forceOpen: isReady && (testMode || battery.isLaptopBattery) && alwaysShowPercentage
|
||||||
disableOpen: (!isReady || (!testMode && !battery.isLaptopBattery))
|
disableOpen: (!isReady || (!testMode && !battery.isLaptopBattery))
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,7 @@ Item {
|
||||||
function getIcon() {
|
function getIcon() {
|
||||||
var monitor = getMonitor()
|
var monitor = getMonitor()
|
||||||
var brightness = monitor ? monitor.brightness : 0
|
var brightness = monitor ? monitor.brightness : 0
|
||||||
return brightness <= 0 ? "brightness_1" : brightness < 0.33 ? "brightness_low" : brightness
|
return brightness <= 0.5 ? "brightness-low" : "brightness-high"
|
||||||
< 0.66 ? "brightness_medium" : "brightness_high"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connection used to open the pill when brightness changes
|
// Connection used to open the pill when brightness changes
|
||||||
|
|
@ -80,8 +79,6 @@ Item {
|
||||||
|
|
||||||
rightOpen: BarWidgetRegistry.getNPillDirection(root)
|
rightOpen: BarWidgetRegistry.getNPillDirection(root)
|
||||||
icon: getIcon()
|
icon: getIcon()
|
||||||
iconCircleColor: Color.mPrimary
|
|
||||||
collapsedIconColor: Color.mOnSurface
|
|
||||||
autoHide: false // Important to be false so we can hover as long as we want
|
autoHide: false // Important to be false so we can hover as long as we want
|
||||||
text: {
|
text: {
|
||||||
var monitor = getMonitor()
|
var monitor = getMonitor()
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ Rectangle {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
font.pointSize: Style.fontSizeS * scaling
|
font.pointSize: Style.fontSizeS * scaling
|
||||||
font.weight: Style.fontWeightBold
|
font.weight: Style.fontWeightBold
|
||||||
|
color: Color.mPrimary
|
||||||
}
|
}
|
||||||
|
|
||||||
NTooltip {
|
NTooltip {
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,12 @@ NIconButton {
|
||||||
property ShellScreen screen
|
property ShellScreen screen
|
||||||
property real scaling: 1.0
|
property real scaling: 1.0
|
||||||
|
|
||||||
icon: "contrast"
|
icon: "transparency"
|
||||||
tooltipText: "Toggle light/dark mode"
|
tooltipText: "Toggle light/dark mode"
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
|
|
||||||
colorBg: Color.mSurfaceVariant
|
colorBg: Settings.data.colorSchemes.darkMode ? Color.mSurfaceVariant : Color.mPrimary
|
||||||
colorFg: Color.mOnSurface
|
colorFg: Settings.data.colorSchemes.darkMode ? Color.mOnSurface : Color.mOnPrimary
|
||||||
colorBorder: Color.transparent
|
colorBorder: Color.transparent
|
||||||
colorBorderHover: Color.transparent
|
colorBorderHover: Color.transparent
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@ NIconButton {
|
||||||
|
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
|
|
||||||
icon: "coffee"
|
icon: "cup"
|
||||||
tooltipText: IdleInhibitorService.isInhibited ? "Disable keep awake" : "Enable keep awake"
|
tooltipText: IdleInhibitorService.isInhibited ? "Disable keep awake" : "Enable keep awake"
|
||||||
colorBg: Color.mSurfaceVariant
|
colorBg: IdleInhibitorService.isInhibited ? Color.mPrimary : Color.mSurfaceVariant
|
||||||
colorFg: IdleInhibitorService.isInhibited ? Color.mPrimary : Color.mOnSurface
|
colorFg: IdleInhibitorService.isInhibited ? Color.mOnPrimary : Color.mOnSurface
|
||||||
colorBorder: Color.transparent
|
colorBorder: Color.transparent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
IdleInhibitorService.manualToggle()
|
IdleInhibitorService.manualToggle()
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,7 @@ Item {
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
rightOpen: BarWidgetRegistry.getNPillDirection(root)
|
rightOpen: BarWidgetRegistry.getNPillDirection(root)
|
||||||
icon: "keyboard_alt"
|
icon: "keyboard"
|
||||||
iconCircleColor: Color.mPrimary
|
|
||||||
collapsedIconColor: Color.mOnSurface
|
|
||||||
autoHide: false // Important to be false so we can hover as long as we want
|
autoHide: false // Important to be false so we can hover as long as we want
|
||||||
text: currentLayout
|
text: currentLayout
|
||||||
tooltipText: "Keyboard layout: " + currentLayout
|
tooltipText: "Keyboard layout: " + currentLayout
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,9 @@ RowLayout {
|
||||||
readonly property string visualizerType: (widgetSettings.visualizerType !== undefined && widgetSettings.visualizerType
|
readonly property string visualizerType: (widgetSettings.visualizerType !== undefined && widgetSettings.visualizerType
|
||||||
!== "") ? widgetSettings.visualizerType : widgetMetadata.visualizerType
|
!== "") ? widgetSettings.visualizerType : widgetMetadata.visualizerType
|
||||||
|
|
||||||
readonly property real minWidth: 160
|
// 6% of total width
|
||||||
readonly property real maxWidth: 400
|
readonly property real minWidth: Math.max(1, screen.width * 0.06)
|
||||||
|
readonly property real maxWidth: minWidth * 2
|
||||||
|
|
||||||
function getTitle() {
|
function getTitle() {
|
||||||
return MediaService.trackTitle + (MediaService.trackArtist !== "" ? ` - ${MediaService.trackArtist}` : "")
|
return MediaService.trackTitle + (MediaService.trackArtist !== "" ? ` - ${MediaService.trackArtist}` : "")
|
||||||
|
|
@ -134,7 +135,7 @@ RowLayout {
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
id: windowIcon
|
id: windowIcon
|
||||||
text: MediaService.isPlaying ? "pause" : "play_arrow"
|
text: MediaService.isPlaying ? "pause" : "play"
|
||||||
font.pointSize: Style.fontSizeL * scaling
|
font.pointSize: Style.fontSizeL * scaling
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
@ -154,7 +155,8 @@ RowLayout {
|
||||||
id: trackArt
|
id: trackArt
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
imagePath: MediaService.trackArtUrl
|
imagePath: MediaService.trackArtUrl
|
||||||
fallbackIcon: MediaService.isPlaying ? "pause" : "play_arrow"
|
fallbackIcon: MediaService.isPlaying ? "pause" : "play"
|
||||||
|
fallbackIconSize: 10 * scaling
|
||||||
borderWidth: 0
|
borderWidth: 0
|
||||||
border.color: Color.transparent
|
border.color: Color.transparent
|
||||||
}
|
}
|
||||||
|
|
@ -178,7 +180,7 @@ RowLayout {
|
||||||
font.weight: Style.fontWeightMedium
|
font.weight: Style.fontWeightMedium
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
color: Color.mTertiary
|
color: Color.mSecondary
|
||||||
|
|
||||||
Behavior on Layout.preferredWidth {
|
Behavior on Layout.preferredWidth {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
|
|
|
||||||
|
|
@ -43,9 +43,9 @@ Item {
|
||||||
|
|
||||||
function getIcon() {
|
function getIcon() {
|
||||||
if (AudioService.inputMuted) {
|
if (AudioService.inputMuted) {
|
||||||
return "mic_off"
|
return "mic-mute"
|
||||||
}
|
}
|
||||||
return AudioService.inputVolume <= Number.EPSILON ? "mic_off" : (AudioService.inputVolume < 0.33 ? "mic" : "mic")
|
return AudioService.inputVolume <= Number.EPSILON ? "mic-mute" : (AudioService.inputVolume < 0.33 ? "mic" : "mic")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connection used to open the pill when input volume changes
|
// Connection used to open the pill when input volume changes
|
||||||
|
|
@ -92,8 +92,6 @@ Item {
|
||||||
|
|
||||||
rightOpen: BarWidgetRegistry.getNPillDirection(root)
|
rightOpen: BarWidgetRegistry.getNPillDirection(root)
|
||||||
icon: getIcon()
|
icon: getIcon()
|
||||||
iconCircleColor: Color.mPrimary
|
|
||||||
collapsedIconColor: Color.mOnSurface
|
|
||||||
autoHide: false // Important to be false so we can hover as long as we want
|
autoHide: false // Important to be false so we can hover as long as we want
|
||||||
text: Math.floor(AudioService.inputVolume * 100) + "%"
|
text: Math.floor(AudioService.inputVolume * 100) + "%"
|
||||||
forceOpen: alwaysShowPercentage
|
forceOpen: alwaysShowPercentage
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,12 @@ NIconButton {
|
||||||
property real scaling: 1.0
|
property real scaling: 1.0
|
||||||
|
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
colorBg: Color.mSurfaceVariant
|
colorBg: Settings.data.nightLight.enabled ? Color.mPrimary : Color.mSurfaceVariant
|
||||||
colorFg: Color.mOnSurface
|
colorFg: Settings.data.nightLight.enabled ? Color.mOnPrimary : Color.mOnSurface
|
||||||
colorBorder: Color.transparent
|
colorBorder: Color.transparent
|
||||||
colorBorderHover: Color.transparent
|
colorBorderHover: Color.transparent
|
||||||
|
|
||||||
icon: Settings.data.nightLight.enabled ? "bedtime" : "bedtime_off"
|
icon: "moon-stars"
|
||||||
tooltipText: `Night light: ${Settings.data.nightLight.enabled ? "enabled." : "disabled."}\nLeft click to toggle.\nRight click to access settings.`
|
tooltipText: `Night light: ${Settings.data.nightLight.enabled ? "enabled." : "disabled."}\nLeft click to toggle.\nRight click to access settings.`
|
||||||
onClicked: Settings.data.nightLight.enabled = !Settings.data.nightLight.enabled
|
onClicked: Settings.data.nightLight.enabled = !Settings.data.nightLight.enabled
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,10 +53,10 @@ NIconButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
icon: Settings.data.notifications.doNotDisturb ? "notifications_off" : "notifications"
|
icon: Settings.data.notifications.doNotDisturb ? "bell-slash" : "bell"
|
||||||
tooltipText: Settings.data.notifications.doNotDisturb ? "Notification history.\nRight-click to disable 'Do Not Disturb'." : "Notification history.\nRight-click to enable 'Do Not Disturb'."
|
tooltipText: Settings.data.notifications.doNotDisturb ? "Notification history.\nRight-click to disable 'Do Not Disturb'." : "Notification history.\nRight-click to enable 'Do Not Disturb'."
|
||||||
colorBg: Color.mSurfaceVariant
|
colorBg: Color.mSurfaceVariant
|
||||||
colorFg: Settings.data.notifications.doNotDisturb ? Color.mError : Color.mOnSurface
|
colorFg: Color.mOnSurface
|
||||||
colorBorder: Color.transparent
|
colorBorder: Color.transparent
|
||||||
colorBorderHover: Color.transparent
|
colorBorderHover: Color.transparent
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,13 @@ NIconButton {
|
||||||
|
|
||||||
function profileIcon() {
|
function profileIcon() {
|
||||||
if (!hasPP)
|
if (!hasPP)
|
||||||
return "balance"
|
return "yin-yang"
|
||||||
if (powerProfiles.profile === PowerProfile.Performance)
|
if (powerProfiles.profile === PowerProfile.Performance)
|
||||||
return "speed"
|
return "speedometer2"
|
||||||
if (powerProfiles.profile === PowerProfile.Balanced)
|
if (powerProfiles.profile === PowerProfile.Balanced)
|
||||||
return "balance"
|
return "yin-yang"
|
||||||
if (powerProfiles.profile === PowerProfile.PowerSaver)
|
if (powerProfiles.profile === PowerProfile.PowerSaver)
|
||||||
return "eco"
|
return "leaf"
|
||||||
}
|
}
|
||||||
|
|
||||||
function profileName() {
|
function profileName() {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ NIconButton {
|
||||||
|
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
|
|
||||||
icon: "power_settings_new"
|
icon: "power"
|
||||||
tooltipText: "Power Settings"
|
tooltipText: "Power Settings"
|
||||||
colorBg: Color.mSurfaceVariant
|
colorBg: Color.mSurfaceVariant
|
||||||
colorFg: Color.mError
|
colorFg: Color.mError
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ NIconButton {
|
||||||
property real scaling: 1.0
|
property real scaling: 1.0
|
||||||
|
|
||||||
visible: ScreenRecorderService.isRecording
|
visible: ScreenRecorderService.isRecording
|
||||||
icon: "videocam"
|
icon: "camera-video"
|
||||||
tooltipText: "Screen recording is active\nClick to stop recording"
|
tooltipText: "Screen recording is active\nClick to stop recording"
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
colorBg: Color.mPrimary
|
colorBg: Color.mPrimary
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ NIconButton {
|
||||||
readonly property bool useDistroLogo: (widgetSettings.useDistroLogo
|
readonly property bool useDistroLogo: (widgetSettings.useDistroLogo
|
||||||
!== undefined) ? widgetSettings.useDistroLogo : widgetMetadata.useDistroLogo
|
!== undefined) ? widgetSettings.useDistroLogo : widgetMetadata.useDistroLogo
|
||||||
|
|
||||||
icon: useDistroLogo ? "" : "widgets"
|
icon: useDistroLogo ? "" : "layout-sidebar-inset-reverse"
|
||||||
tooltipText: "Open side panel."
|
tooltipText: "Open side panel."
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,12 +38,4 @@ Item {
|
||||||
implicitHeight: Style.barHeight * scaling
|
implicitHeight: Style.barHeight * scaling
|
||||||
width: implicitWidth
|
width: implicitWidth
|
||||||
height: implicitHeight
|
height: implicitHeight
|
||||||
|
|
||||||
// Optional: Add a subtle visual indicator in debug mode
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: Qt.rgba(1, 0, 0, 0.1) // Very subtle red tint
|
|
||||||
visible: Settings.data.general.debugMode || false
|
|
||||||
radius: Style.radiusXXS * scaling
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ RowLayout {
|
||||||
!== undefined) ? widgetSettings.showMemoryAsPercent : widgetMetadata.showMemoryAsPercent
|
!== undefined) ? widgetSettings.showMemoryAsPercent : widgetMetadata.showMemoryAsPercent
|
||||||
readonly property bool showNetworkStats: (widgetSettings.showNetworkStats
|
readonly property bool showNetworkStats: (widgetSettings.showNetworkStats
|
||||||
!== undefined) ? widgetSettings.showNetworkStats : widgetMetadata.showNetworkStats
|
!== undefined) ? widgetSettings.showNetworkStats : widgetMetadata.showNetworkStats
|
||||||
|
readonly property bool showDiskUsage: (widgetSettings.showDiskUsage
|
||||||
|
!== undefined) ? widgetSettings.showDiskUsage : widgetMetadata.showDiskUsage
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
spacing: Style.marginS * scaling
|
spacing: Style.marginS * scaling
|
||||||
|
|
@ -52,126 +54,188 @@ RowLayout {
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: mainLayout
|
id: mainLayout
|
||||||
anchors.fill: parent
|
anchors.centerIn: parent // Better centering than margins
|
||||||
anchors.leftMargin: Style.marginS * scaling
|
width: parent.width - Style.marginM * scaling * 2
|
||||||
anchors.rightMargin: Style.marginS * scaling
|
|
||||||
spacing: Style.marginS * scaling
|
spacing: Style.marginS * scaling
|
||||||
|
|
||||||
// CPU Usage Component
|
// CPU Usage Component
|
||||||
RowLayout {
|
Item {
|
||||||
id: cpuUsageLayout
|
Layout.preferredWidth: cpuUsageRow.implicitWidth
|
||||||
spacing: Style.marginXS * scaling
|
Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling)
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
visible: showCpuUsage
|
visible: showCpuUsage
|
||||||
|
|
||||||
NIcon {
|
RowLayout {
|
||||||
id: cpuUsageIcon
|
id: cpuUsageRow
|
||||||
text: "speed"
|
anchors.centerIn: parent
|
||||||
Layout.alignment: Qt.AlignVCenter
|
spacing: Style.marginXS * scaling
|
||||||
}
|
|
||||||
|
|
||||||
NText {
|
NIcon {
|
||||||
id: cpuUsageText
|
icon: "speedometer2"
|
||||||
text: `${SystemStatService.cpuUsage}%`
|
font.pointSize: Style.fontSizeM * scaling
|
||||||
font.family: Settings.data.ui.fontFixed
|
Layout.alignment: Qt.AlignVCenter
|
||||||
font.pointSize: Style.fontSizeS * scaling
|
}
|
||||||
font.weight: Style.fontWeightMedium
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
NText {
|
||||||
verticalAlignment: Text.AlignVCenter
|
text: `${SystemStatService.cpuUsage}%`
|
||||||
color: Color.mPrimary
|
font.family: Settings.data.ui.fontFixed
|
||||||
|
font.pointSize: Style.fontSizeS * scaling
|
||||||
|
font.weight: Style.fontWeightMedium
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: Color.mPrimary
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CPU Temperature Component
|
// CPU Temperature Component
|
||||||
RowLayout {
|
Item {
|
||||||
id: cpuTempLayout
|
Layout.preferredWidth: cpuTempRow.implicitWidth
|
||||||
// spacing is thin here to compensate for the vertical thermometer icon
|
Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling)
|
||||||
spacing: Style.marginXXS * scaling
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
visible: showCpuTemp
|
visible: showCpuTemp
|
||||||
|
|
||||||
NIcon {
|
RowLayout {
|
||||||
text: "thermometer"
|
id: cpuTempRow
|
||||||
Layout.alignment: Qt.AlignVCenter
|
anchors.centerIn: parent
|
||||||
}
|
spacing: Style.marginXS * scaling
|
||||||
|
|
||||||
NText {
|
NIcon {
|
||||||
text: `${SystemStatService.cpuTemp}°C`
|
icon: "fire"
|
||||||
font.family: Settings.data.ui.fontFixed
|
// Fire is so tall, we need to make it smaller
|
||||||
font.pointSize: Style.fontSizeS * scaling
|
font.pointSize: Style.fontSizeS * scaling
|
||||||
font.weight: Style.fontWeightMedium
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.alignment: Qt.AlignVCenter
|
}
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
color: Color.mPrimary
|
NText {
|
||||||
|
text: `${SystemStatService.cpuTemp}°C`
|
||||||
|
font.family: Settings.data.ui.fontFixed
|
||||||
|
font.pointSize: Style.fontSizeS * scaling
|
||||||
|
font.weight: Style.fontWeightMedium
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: Color.mPrimary
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Memory Usage Component
|
// Memory Usage Component
|
||||||
RowLayout {
|
Item {
|
||||||
id: memoryUsageLayout
|
Layout.preferredWidth: memoryUsageRow.implicitWidth
|
||||||
spacing: Style.marginXS * scaling
|
Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling)
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
visible: showMemoryUsage
|
visible: showMemoryUsage
|
||||||
|
|
||||||
NIcon {
|
RowLayout {
|
||||||
text: "memory"
|
id: memoryUsageRow
|
||||||
Layout.alignment: Qt.AlignVCenter
|
anchors.centerIn: parent
|
||||||
}
|
spacing: Style.marginXS * scaling
|
||||||
|
|
||||||
NText {
|
NIcon {
|
||||||
text: showMemoryAsPercent ? `${SystemStatService.memPercent}%` : `${SystemStatService.memGb}G`
|
icon: "cpu"
|
||||||
font.family: Settings.data.ui.fontFixed
|
font.pointSize: Style.fontSizeM * scaling
|
||||||
font.pointSize: Style.fontSizeS * scaling
|
Layout.alignment: Qt.AlignVCenter
|
||||||
font.weight: Style.fontWeightMedium
|
}
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
NText {
|
||||||
color: Color.mPrimary
|
text: showMemoryAsPercent ? `${SystemStatService.memPercent}%` : `${SystemStatService.memGb}G`
|
||||||
|
font.family: Settings.data.ui.fontFixed
|
||||||
|
font.pointSize: Style.fontSizeS * scaling
|
||||||
|
font.weight: Style.fontWeightMedium
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: Color.mPrimary
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Network Download Speed Component
|
// Network Download Speed Component
|
||||||
RowLayout {
|
Item {
|
||||||
id: networkDownloadLayout
|
Layout.preferredWidth: networkDownloadRow.implicitWidth
|
||||||
spacing: Style.marginXS * scaling
|
Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling)
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
visible: showNetworkStats
|
visible: showNetworkStats
|
||||||
|
|
||||||
NIcon {
|
RowLayout {
|
||||||
text: "download"
|
id: networkDownloadRow
|
||||||
Layout.alignment: Qt.AlignVCenter
|
anchors.centerIn: parent
|
||||||
}
|
spacing: Style.marginXS * scaling
|
||||||
|
|
||||||
NText {
|
NIcon {
|
||||||
text: SystemStatService.formatSpeed(SystemStatService.rxSpeed)
|
icon: "cloud-arrow-down"
|
||||||
font.family: Settings.data.ui.fontFixed
|
font.pointSize: Style.fontSizeM * scaling
|
||||||
font.pointSize: Style.fontSizeS * scaling
|
Layout.alignment: Qt.AlignVCenter
|
||||||
font.weight: Style.fontWeightMedium
|
}
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
NText {
|
||||||
color: Color.mPrimary
|
text: SystemStatService.formatSpeed(SystemStatService.rxSpeed)
|
||||||
|
font.family: Settings.data.ui.fontFixed
|
||||||
|
font.pointSize: Style.fontSizeS * scaling
|
||||||
|
font.weight: Style.fontWeightMedium
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: Color.mPrimary
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Network Upload Speed Component
|
// Network Upload Speed Component
|
||||||
RowLayout {
|
Item {
|
||||||
id: networkUploadLayout
|
Layout.preferredWidth: networkUploadRow.implicitWidth
|
||||||
spacing: Style.marginXS * scaling
|
Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling)
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
visible: showNetworkStats
|
visible: showNetworkStats
|
||||||
|
|
||||||
NIcon {
|
RowLayout {
|
||||||
text: "upload"
|
id: networkUploadRow
|
||||||
Layout.alignment: Qt.AlignVCenter
|
anchors.centerIn: parent
|
||||||
}
|
spacing: Style.marginXS * scaling
|
||||||
|
|
||||||
NText {
|
NIcon {
|
||||||
text: SystemStatService.formatSpeed(SystemStatService.txSpeed)
|
icon: "cloud-arrow-up"
|
||||||
font.family: Settings.data.ui.fontFixed
|
font.pointSize: Style.fontSizeM * scaling
|
||||||
font.pointSize: Style.fontSizeS * scaling
|
Layout.alignment: Qt.AlignVCenter
|
||||||
font.weight: Style.fontWeightMedium
|
}
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
NText {
|
||||||
color: Color.mPrimary
|
text: SystemStatService.formatSpeed(SystemStatService.txSpeed)
|
||||||
|
font.family: Settings.data.ui.fontFixed
|
||||||
|
font.pointSize: Style.fontSizeS * scaling
|
||||||
|
font.weight: Style.fontWeightMedium
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: Color.mPrimary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disk Usage Component (primary drive)
|
||||||
|
Item {
|
||||||
|
Layout.preferredWidth: diskUsageRow.implicitWidth
|
||||||
|
Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling)
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
visible: showDiskUsage
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: diskUsageRow
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Style.marginXS * scaling
|
||||||
|
|
||||||
|
NIcon {
|
||||||
|
icon: "hdd"
|
||||||
|
font.pointSize: Style.fontSizeM * scaling
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
NText {
|
||||||
|
text: `${SystemStatService.diskPercent}%`
|
||||||
|
font.family: Settings.data.ui.fontFixed
|
||||||
|
font.pointSize: Style.fontSizeS * scaling
|
||||||
|
font.weight: Style.fontWeightMedium
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: Color.mPrimary
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,9 +43,9 @@ Item {
|
||||||
|
|
||||||
function getIcon() {
|
function getIcon() {
|
||||||
if (AudioService.muted) {
|
if (AudioService.muted) {
|
||||||
return "volume_off"
|
return "volume-mute"
|
||||||
}
|
}
|
||||||
return AudioService.volume <= Number.EPSILON ? "volume_off" : (AudioService.volume < 0.33 ? "volume_down" : "volume_up")
|
return AudioService.volume <= 0.2 ? "volume-off" : (AudioService.volume < 0.6 ? "volume-down" : "volume-up")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connection used to open the pill when volume changes
|
// Connection used to open the pill when volume changes
|
||||||
|
|
@ -77,8 +77,6 @@ Item {
|
||||||
|
|
||||||
rightOpen: BarWidgetRegistry.getNPillDirection(root)
|
rightOpen: BarWidgetRegistry.getNPillDirection(root)
|
||||||
icon: getIcon()
|
icon: getIcon()
|
||||||
iconCircleColor: Color.mPrimary
|
|
||||||
collapsedIconColor: Color.mOnSurface
|
|
||||||
autoHide: false // Important to be false so we can hover as long as we want
|
autoHide: false // Important to be false so we can hover as long as we want
|
||||||
text: Math.floor(AudioService.volume * 100) + "%"
|
text: Math.floor(AudioService.volume * 100) + "%"
|
||||||
forceOpen: alwaysShowPercentage
|
forceOpen: alwaysShowPercentage
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ NIconButton {
|
||||||
icon: {
|
icon: {
|
||||||
try {
|
try {
|
||||||
if (NetworkService.ethernetConnected) {
|
if (NetworkService.ethernetConnected) {
|
||||||
return "lan"
|
return "ethernet"
|
||||||
}
|
}
|
||||||
let connected = false
|
let connected = false
|
||||||
let signalStrength = 0
|
let signalStrength = 0
|
||||||
|
|
@ -34,7 +34,7 @@ NIconButton {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return connected ? NetworkService.signalIcon(signalStrength) : "wifi_find"
|
return connected ? NetworkService.signalIcon(signalStrength) : "wifi-off"
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.error("Wi-Fi", "Error getting icon:", error)
|
Logger.error("Wi-Fi", "Error getting icon:", error)
|
||||||
return "signal_wifi_bad"
|
return "signal_wifi_bad"
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ ColumnLayout {
|
||||||
|
|
||||||
// One device BT icon
|
// One device BT icon
|
||||||
NIcon {
|
NIcon {
|
||||||
text: BluetoothService.getDeviceIcon(modelData)
|
icon: BluetoothService.getDeviceIcon(modelData)
|
||||||
font.pointSize: Style.fontSizeXXL * scaling
|
font.pointSize: Style.fontSizeXXL * scaling
|
||||||
color: getContentColor(Color.mOnSurface)
|
color: getContentColor(Color.mOnSurface)
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
@ -164,7 +164,7 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
return "Connect"
|
return "Connect"
|
||||||
}
|
}
|
||||||
icon: (isBusy ? "hourglass_full" : null)
|
icon: (isBusy ? "hourglass-split" : null)
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (modelData.connected) {
|
if (modelData.connected) {
|
||||||
BluetoothService.disconnectDevice(modelData)
|
BluetoothService.disconnectDevice(modelData)
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ NPanel {
|
||||||
spacing: Style.marginM * scaling
|
spacing: Style.marginM * scaling
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "bluetooth"
|
icon: "bluetooth"
|
||||||
font.pointSize: Style.fontSizeXXL * scaling
|
font.pointSize: Style.fontSizeXXL * scaling
|
||||||
color: Color.mPrimary
|
color: Color.mPrimary
|
||||||
}
|
}
|
||||||
|
|
@ -42,7 +42,7 @@ NPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop_circle" : "refresh"
|
icon: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop" : "arrow-repeat"
|
||||||
tooltipText: "Refresh Devices"
|
tooltipText: "Refresh Devices"
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|
@ -53,7 +53,7 @@ NPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "close"
|
icon: "x-lg"
|
||||||
tooltipText: "Close"
|
tooltipText: "Close"
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,9 @@ Item {
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
target: "screenRecorder"
|
target: "screenRecorder"
|
||||||
function toggle() {
|
function toggle() {
|
||||||
ScreenRecorderService.toggleRecording()
|
if (ScreenRecorderService.isAvailable) {
|
||||||
|
ScreenRecorderService.toggleRecording()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -418,7 +418,7 @@ Loader {
|
||||||
font.weight: Style.fontWeightBold
|
font.weight: Style.fontWeightBold
|
||||||
}
|
}
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "keyboard_alt"
|
icon: "keyboard"
|
||||||
font.pointSize: Style.fontSizeM * scaling
|
font.pointSize: Style.fontSizeM * scaling
|
||||||
color: Color.mOnSurface
|
color: Color.mOnSurface
|
||||||
}
|
}
|
||||||
|
|
@ -428,7 +428,7 @@ Loader {
|
||||||
spacing: Style.marginS * scaling
|
spacing: Style.marginS * scaling
|
||||||
visible: batteryIndicator.batteryVisible
|
visible: batteryIndicator.batteryVisible
|
||||||
NIcon {
|
NIcon {
|
||||||
text: BatteryService.getIcon(batteryIndicator.percent, batteryIndicator.charging,
|
icon: BatteryService.getIcon(batteryIndicator.percent, batteryIndicator.charging,
|
||||||
batteryIndicator.isReady)
|
batteryIndicator.isReady)
|
||||||
font.pointSize: Style.fontSizeM * scaling
|
font.pointSize: Style.fontSizeM * scaling
|
||||||
color: batteryIndicator.charging ? Color.mPrimary : Color.mOnSurface
|
color: batteryIndicator.charging ? Color.mPrimary : Color.mOnSurface
|
||||||
|
|
@ -718,21 +718,47 @@ Loader {
|
||||||
anchors.margins: 50 * scaling
|
anchors.margins: 50 * scaling
|
||||||
spacing: 20 * scaling
|
spacing: 20 * scaling
|
||||||
|
|
||||||
|
// Shutdown
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.preferredWidth: 60 * scaling
|
Layout.preferredWidth: iconPower.implicitWidth + Style.marginXL * scaling
|
||||||
Layout.preferredHeight: 60 * scaling
|
Layout.preferredHeight: Layout.preferredWidth
|
||||||
radius: width * 0.5
|
radius: width * 0.5
|
||||||
color: powerButtonArea.containsMouse ? Color.mError : Qt.alpha(Color.mError, 0.2)
|
color: powerButtonArea.containsMouse ? Color.mError : Qt.alpha(Color.mError, 0.2)
|
||||||
border.color: Color.mError
|
border.color: Color.mError
|
||||||
border.width: Math.max(1, Style.borderM * scaling)
|
border.width: Math.max(1, Style.borderM * scaling)
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
|
id: iconPower
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: "power_settings_new"
|
icon: "power"
|
||||||
font.pointSize: Style.fontSizeXL * scaling
|
font.pointSize: Style.fontSizeXXXL * scaling
|
||||||
color: powerButtonArea.containsMouse ? Color.mOnError : Color.mError
|
color: powerButtonArea.containsMouse ? Color.mOnError : Color.mError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tooltip (inline rectangle to avoid separate Window during lock)
|
||||||
|
Rectangle {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.bottom: parent.top
|
||||||
|
anchors.bottomMargin: 12 * scaling
|
||||||
|
radius: Style.radiusM * scaling
|
||||||
|
color: Color.mSurface
|
||||||
|
border.color: Color.mOutline
|
||||||
|
border.width: Math.max(1, Style.borderS * scaling)
|
||||||
|
visible: powerButtonArea.containsMouse
|
||||||
|
z: 1
|
||||||
|
NText {
|
||||||
|
id: shutdownTooltipText
|
||||||
|
anchors.margins: Style.marginM * scaling
|
||||||
|
anchors.fill: parent
|
||||||
|
text: "Shut down the computer."
|
||||||
|
font.pointSize: Style.fontSizeM * scaling
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
implicitWidth: shutdownTooltipText.implicitWidth + Style.marginM * 2 * scaling
|
||||||
|
implicitHeight: shutdownTooltipText.implicitHeight + Style.marginM * 2 * scaling
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: powerButtonArea
|
id: powerButtonArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
@ -743,21 +769,47 @@ Loader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reboot
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.preferredWidth: 60 * scaling
|
Layout.preferredWidth: iconReboot.implicitWidth + Style.marginXL * scaling
|
||||||
Layout.preferredHeight: 60 * scaling
|
Layout.preferredHeight: Layout.preferredWidth
|
||||||
radius: width * 0.5
|
radius: width * 0.5
|
||||||
color: restartButtonArea.containsMouse ? Color.mPrimary : Qt.alpha(Color.mPrimary, Style.opacityLight)
|
color: restartButtonArea.containsMouse ? Color.mPrimary : Qt.alpha(Color.mPrimary, Style.opacityLight)
|
||||||
border.color: Color.mPrimary
|
border.color: Color.mPrimary
|
||||||
border.width: Math.max(1, Style.borderM * scaling)
|
border.width: Math.max(1, Style.borderM * scaling)
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
|
id: iconReboot
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: "restart_alt"
|
icon: "arrow-repeat"
|
||||||
font.pointSize: Style.fontSizeXL * scaling
|
font.pointSize: Style.fontSizeXXXL * scaling
|
||||||
color: restartButtonArea.containsMouse ? Color.mOnPrimary : Color.mPrimary
|
color: restartButtonArea.containsMouse ? Color.mOnPrimary : Color.mPrimary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tooltip
|
||||||
|
Rectangle {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.bottom: parent.top
|
||||||
|
anchors.bottomMargin: 12 * scaling
|
||||||
|
radius: Style.radiusM * scaling
|
||||||
|
color: Color.mSurface
|
||||||
|
border.color: Color.mOutline
|
||||||
|
border.width: Math.max(1, Style.borderS * scaling)
|
||||||
|
visible: restartButtonArea.containsMouse
|
||||||
|
z: 1
|
||||||
|
NText {
|
||||||
|
id: restartTooltipText
|
||||||
|
anchors.margins: Style.marginM * scaling
|
||||||
|
anchors.fill: parent
|
||||||
|
text: "Restart the computer."
|
||||||
|
font.pointSize: Style.fontSizeM * scaling
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
implicitWidth: restartTooltipText.implicitWidth + Style.marginM * 2 * scaling
|
||||||
|
implicitHeight: restartTooltipText.implicitHeight + Style.marginM * 2 * scaling
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: restartButtonArea
|
id: restartButtonArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
@ -765,24 +817,51 @@ Loader {
|
||||||
onClicked: {
|
onClicked: {
|
||||||
CompositorService.reboot()
|
CompositorService.reboot()
|
||||||
}
|
}
|
||||||
|
// Tooltip handled via inline rectangle visibility
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Suspend
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.preferredWidth: 60 * scaling
|
Layout.preferredWidth: iconSuspend.implicitWidth + Style.marginXL * scaling
|
||||||
Layout.preferredHeight: 60 * scaling
|
Layout.preferredHeight: Layout.preferredWidth
|
||||||
radius: width * 0.5
|
radius: width * 0.5
|
||||||
color: suspendButtonArea.containsMouse ? Color.mSecondary : Qt.alpha(Color.mSecondary, 0.2)
|
color: suspendButtonArea.containsMouse ? Color.mSecondary : Qt.alpha(Color.mSecondary, 0.2)
|
||||||
border.color: Color.mSecondary
|
border.color: Color.mSecondary
|
||||||
border.width: Math.max(1, Style.borderM * scaling)
|
border.width: Math.max(1, Style.borderM * scaling)
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
|
id: iconSuspend
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: "bedtime"
|
icon: "pause-fill"
|
||||||
font.pointSize: Style.fontSizeXL * scaling
|
font.pointSize: Style.fontSizeXXXL * scaling
|
||||||
color: suspendButtonArea.containsMouse ? Color.mOnSecondary : Color.mSecondary
|
color: suspendButtonArea.containsMouse ? Color.mOnSecondary : Color.mSecondary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tooltip
|
||||||
|
Rectangle {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.bottom: parent.top
|
||||||
|
anchors.bottomMargin: 12 * scaling
|
||||||
|
radius: Style.radiusM * scaling
|
||||||
|
color: Color.mSurface
|
||||||
|
border.color: Color.mOutline
|
||||||
|
border.width: Math.max(1, Style.borderS * scaling)
|
||||||
|
visible: suspendButtonArea.containsMouse
|
||||||
|
z: 1
|
||||||
|
NText {
|
||||||
|
id: suspendTooltipText
|
||||||
|
anchors.margins: Style.marginM * scaling
|
||||||
|
anchors.fill: parent
|
||||||
|
text: "Suspend the system."
|
||||||
|
font.pointSize: Style.fontSizeM * scaling
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
implicitWidth: suspendTooltipText.implicitWidth + Style.marginM * 2 * scaling
|
||||||
|
implicitHeight: suspendTooltipText.implicitHeight + Style.marginM * 2 * scaling
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: suspendButtonArea
|
id: suspendButtonArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
@ -790,6 +869,7 @@ Loader {
|
||||||
onClicked: {
|
onClicked: {
|
||||||
CompositorService.suspend()
|
CompositorService.suspend()
|
||||||
}
|
}
|
||||||
|
// Tooltip handled via inline rectangle visibility
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -205,14 +205,12 @@ Variants {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: Style.marginM * scaling
|
spacing: Style.marginM * scaling
|
||||||
|
|
||||||
// Avatar
|
// Image
|
||||||
NImageCircled {
|
NImageCircled {
|
||||||
id: appAvatar
|
|
||||||
Layout.preferredWidth: 40 * scaling
|
Layout.preferredWidth: 40 * scaling
|
||||||
Layout.preferredHeight: 40 * scaling
|
Layout.preferredHeight: 40 * scaling
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
imagePath: model.image && model.image !== "" ? model.image : ""
|
imagePath: model.image && model.image !== "" ? model.image : ""
|
||||||
fallbackIcon: ""
|
|
||||||
borderColor: Color.transparent
|
borderColor: Color.transparent
|
||||||
borderWidth: 0
|
borderWidth: 0
|
||||||
visible: (model.image && model.image !== "")
|
visible: (model.image && model.image !== "")
|
||||||
|
|
@ -294,7 +292,7 @@ Variants {
|
||||||
|
|
||||||
// Close button positioned absolutely
|
// Close button positioned absolutely
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "close"
|
icon: "x-lg"
|
||||||
tooltipText: "Close"
|
tooltipText: "Close"
|
||||||
sizeRatio: 0.6
|
sizeRatio: 0.6
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ NPanel {
|
||||||
spacing: Style.marginM * scaling
|
spacing: Style.marginM * scaling
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "notifications"
|
icon: "bell"
|
||||||
font.pointSize: Style.fontSizeXXL * scaling
|
font.pointSize: Style.fontSizeXXL * scaling
|
||||||
color: Color.mPrimary
|
color: Color.mPrimary
|
||||||
}
|
}
|
||||||
|
|
@ -45,21 +45,21 @@ NPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: Settings.data.notifications.doNotDisturb ? "notifications_off" : "notifications_active"
|
icon: Settings.data.notifications.doNotDisturb ? "bell-slash" : "bell"
|
||||||
tooltipText: Settings.data.notifications.doNotDisturb ? "'Do Not Disturb' is enabled." : "'Do Not Disturb' is disabled."
|
tooltipText: Settings.data.notifications.doNotDisturb ? "'Do Not Disturb' is enabled." : "'Do Not Disturb' is disabled."
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
onClicked: Settings.data.notifications.doNotDisturb = !Settings.data.notifications.doNotDisturb
|
onClicked: Settings.data.notifications.doNotDisturb = !Settings.data.notifications.doNotDisturb
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "delete"
|
icon: "trash"
|
||||||
tooltipText: "Clear history"
|
tooltipText: "Clear history"
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
onClicked: NotificationService.clearHistory()
|
onClicked: NotificationService.clearHistory()
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "close"
|
icon: "x-lg"
|
||||||
tooltipText: "Close"
|
tooltipText: "Close"
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|
@ -85,7 +85,7 @@ NPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "notifications_off"
|
icon: "bell-slash"
|
||||||
font.pointSize: 64 * scaling
|
font.pointSize: 64 * scaling
|
||||||
color: Color.mOnSurfaceVariant
|
color: Color.mOnSurfaceVariant
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
@ -175,7 +175,7 @@ NPanel {
|
||||||
|
|
||||||
// Delete button
|
// Delete button
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "delete"
|
icon: "trash"
|
||||||
tooltipText: "Delete notification"
|
tooltipText: "Delete notification"
|
||||||
sizeRatio: 0.7
|
sizeRatio: 0.7
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
|
|
|
||||||
|
|
@ -29,27 +29,27 @@ NPanel {
|
||||||
property int selectedIndex: 0
|
property int selectedIndex: 0
|
||||||
readonly property var powerOptions: [{
|
readonly property var powerOptions: [{
|
||||||
"action": "lock",
|
"action": "lock",
|
||||||
"icon": "lock_outline",
|
"icon": "lock",
|
||||||
"title": "Lock",
|
"title": "Lock",
|
||||||
"subtitle": "Lock your session"
|
"subtitle": "Lock your session"
|
||||||
}, {
|
}, {
|
||||||
"action": "suspend",
|
"action": "suspend",
|
||||||
"icon": "bedtime",
|
"icon": "pause-circle",
|
||||||
"title": "Suspend",
|
"title": "Suspend",
|
||||||
"subtitle": "Put the system to sleep"
|
"subtitle": "Put the system to sleep"
|
||||||
}, {
|
}, {
|
||||||
"action": "reboot",
|
"action": "reboot",
|
||||||
"icon": "refresh",
|
"icon": "arrow-repeat",
|
||||||
"title": "Reboot",
|
"title": "Reboot",
|
||||||
"subtitle": "Restart the system"
|
"subtitle": "Restart the system"
|
||||||
}, {
|
}, {
|
||||||
"action": "logout",
|
"action": "logout",
|
||||||
"icon": "exit_to_app",
|
"icon": "escape",
|
||||||
"title": "Logout",
|
"title": "Logout",
|
||||||
"subtitle": "End your session"
|
"subtitle": "End your session"
|
||||||
}, {
|
}, {
|
||||||
"action": "shutdown",
|
"action": "shutdown",
|
||||||
"icon": "power_settings_new",
|
"icon": "power",
|
||||||
"title": "Shutdown",
|
"title": "Shutdown",
|
||||||
"subtitle": "Turn off the system",
|
"subtitle": "Turn off the system",
|
||||||
"isShutdown": true
|
"isShutdown": true
|
||||||
|
|
@ -276,7 +276,7 @@ NPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: timerActive ? "back_hand" : "close"
|
icon: timerActive ? "x-square" : "x-lg"
|
||||||
tooltipText: timerActive ? "Cancel Timer" : "Close"
|
tooltipText: timerActive ? "Cancel Timer" : "Close"
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
colorBg: timerActive ? Qt.alpha(Color.mError, 0.08) : Color.transparent
|
colorBg: timerActive ? Qt.alpha(Color.mError, 0.08) : Color.transparent
|
||||||
|
|
@ -360,7 +360,7 @@ NPanel {
|
||||||
id: iconElement
|
id: iconElement
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: buttonRoot.icon
|
icon: buttonRoot.icon
|
||||||
color: {
|
color: {
|
||||||
if (buttonRoot.pending)
|
if (buttonRoot.pending)
|
||||||
return Color.mPrimary
|
return Color.mPrimary
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ NBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "add"
|
icon: "plus-lg"
|
||||||
|
|
||||||
colorBg: Color.mPrimary
|
colorBg: Color.mPrimary
|
||||||
colorFg: Color.mOnPrimary
|
colorFg: Color.mOnPrimary
|
||||||
|
|
@ -170,7 +170,7 @@ NBox {
|
||||||
Loader {
|
Loader {
|
||||||
active: BarWidgetRegistry.widgetHasUserSettings(modelData.id)
|
active: BarWidgetRegistry.widgetHasUserSettings(modelData.id)
|
||||||
sourceComponent: NIconButton {
|
sourceComponent: NIconButton {
|
||||||
icon: "settings"
|
icon: "gear"
|
||||||
sizeRatio: 0.6
|
sizeRatio: 0.6
|
||||||
colorBorder: Qt.alpha(Color.mOutline, Style.opacityLight)
|
colorBorder: Qt.alpha(Color.mOutline, Style.opacityLight)
|
||||||
colorBg: Color.mOnSurface
|
colorBg: Color.mOnSurface
|
||||||
|
|
@ -210,7 +210,7 @@ NBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "close"
|
icon: "x-lg"
|
||||||
sizeRatio: 0.6
|
sizeRatio: 0.6
|
||||||
colorBorder: Qt.alpha(Color.mOutline, Style.opacityLight)
|
colorBorder: Qt.alpha(Color.mOutline, Style.opacityLight)
|
||||||
colorBg: Color.mOnSurface
|
colorBg: Color.mOnSurface
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ Popup {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "close"
|
icon: "x-lg"
|
||||||
onClicked: settingsPopup.close()
|
onClicked: settingsPopup.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -107,6 +107,7 @@ Popup {
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: Style.marginM * scaling
|
Layout.topMargin: Style.marginM * scaling
|
||||||
|
spacing: Style.marginM * scaling
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
@ -120,7 +121,7 @@ Popup {
|
||||||
|
|
||||||
NButton {
|
NButton {
|
||||||
text: "Apply"
|
text: "Apply"
|
||||||
icon: "check"
|
icon: "check-lg"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (settingsLoader.item && settingsLoader.item.saveSettings) {
|
if (settingsLoader.item && settingsLoader.item.saveSettings) {
|
||||||
var newSettings = settingsLoader.item.saveSettings()
|
var newSettings = settingsLoader.item.saveSettings()
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,13 @@ ColumnLayout {
|
||||||
// Local state
|
// Local state
|
||||||
property bool valueAlwaysShowPercentage: widgetData.alwaysShowPercentage
|
property bool valueAlwaysShowPercentage: widgetData.alwaysShowPercentage
|
||||||
!== undefined ? widgetData.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
|
!== undefined ? widgetData.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
|
||||||
|
property int valueWarningThreshold: widgetData.warningThreshold
|
||||||
|
!== undefined ? widgetData.warningThreshold : widgetMetadata.warningThreshold
|
||||||
|
|
||||||
function saveSettings() {
|
function saveSettings() {
|
||||||
var settings = Object.assign({}, widgetData || {})
|
var settings = Object.assign({}, widgetData || {})
|
||||||
settings.alwaysShowPercentage = valueAlwaysShowPercentage
|
settings.alwaysShowPercentage = valueAlwaysShowPercentage
|
||||||
|
settings.warningThreshold = valueWarningThreshold
|
||||||
return settings
|
return settings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,4 +31,14 @@ ColumnLayout {
|
||||||
checked: root.valueAlwaysShowPercentage
|
checked: root.valueAlwaysShowPercentage
|
||||||
onToggled: checked => root.valueAlwaysShowPercentage = checked
|
onToggled: checked => root.valueAlwaysShowPercentage = checked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NSpinBox {
|
||||||
|
label: "Low battery warning threshold"
|
||||||
|
description: "Show a warning when battery falls below this percentage."
|
||||||
|
value: valueWarningThreshold
|
||||||
|
suffix: "%"
|
||||||
|
minimum: 5
|
||||||
|
maximum: 50
|
||||||
|
onValueChanged: valueWarningThreshold = value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Window
|
||||||
import qs.Commons
|
import qs.Commons
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
@ -9,7 +10,6 @@ ColumnLayout {
|
||||||
id: root
|
id: root
|
||||||
spacing: Style.marginM * scaling
|
spacing: Style.marginM * scaling
|
||||||
|
|
||||||
// Properties to receive data from parent
|
|
||||||
property var widgetData: null
|
property var widgetData: null
|
||||||
property var widgetMetadata: null
|
property var widgetMetadata: null
|
||||||
|
|
||||||
|
|
@ -22,16 +22,190 @@ ColumnLayout {
|
||||||
return settings
|
return settings
|
||||||
}
|
}
|
||||||
|
|
||||||
// Icon setting
|
|
||||||
NTextInput {
|
NTextInput {
|
||||||
id: iconInput
|
id: iconInput
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
label: "Icon Name"
|
label: "Icon Name"
|
||||||
description: "Choose a name from the Material Icon set."
|
description: "Pick from Bootstrap Icons or type a name."
|
||||||
placeholderText: "Enter icon name (e.g., favorite, home, settings)"
|
placeholderText: "Enter icon name (e.g., speedometer2, gear, house)"
|
||||||
text: widgetData?.icon || widgetMetadata.icon
|
text: widgetData?.icon || widgetMetadata.icon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: Style.marginS * scaling
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
NIcon {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
icon: iconInput.text
|
||||||
|
visible: iconInput.text !== ""
|
||||||
|
}
|
||||||
|
NButton {
|
||||||
|
text: "Browse"
|
||||||
|
onClicked: iconPicker.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
id: iconPicker
|
||||||
|
modal: true
|
||||||
|
property real panelWidth: {
|
||||||
|
var w = Math.round(Math.max(Screen.width * 0.35, 900) * scaling)
|
||||||
|
w = Math.min(w, Screen.width - Style.marginL * 2)
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
property real panelHeight: {
|
||||||
|
var h = Math.round(Math.max(Screen.height * 0.65, 700) * scaling)
|
||||||
|
h = Math.min(h, Screen.height - Style.barHeight * scaling - Style.marginL * 2)
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
width: panelWidth
|
||||||
|
height: panelHeight
|
||||||
|
anchors.centerIn: Overlay.overlay
|
||||||
|
padding: Style.marginXL * scaling
|
||||||
|
|
||||||
|
property string query: ""
|
||||||
|
property string selectedIcon: ""
|
||||||
|
property var allIcons: Object.keys(Bootstrap.icons)
|
||||||
|
property var filteredIcons: allIcons.filter(function (name) {
|
||||||
|
return query === "" || name.toLowerCase().indexOf(query.toLowerCase()) !== -1
|
||||||
|
})
|
||||||
|
readonly property int tileBase: Math.round(112 * scaling)
|
||||||
|
readonly property int columns: Math.max(3, Math.floor(grid.width / (tileBase + Style.marginS * 2)))
|
||||||
|
readonly property int cellW: Math.floor(grid.width / columns)
|
||||||
|
readonly property int cellH: Math.round(cellW * 0.7 + 36 * scaling)
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: Color.mSurface
|
||||||
|
radius: Style.radiusL * scaling
|
||||||
|
border.color: Color.mPrimary
|
||||||
|
border.width: Style.borderM * scaling
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: Style.marginM * scaling
|
||||||
|
|
||||||
|
// Title row
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
NText {
|
||||||
|
text: "Icon Picker"
|
||||||
|
font.pointSize: Style.fontSizeL * scaling
|
||||||
|
font.weight: Style.fontWeightBold
|
||||||
|
color: Color.mPrimary
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
NIconButton {
|
||||||
|
icon: "x-lg"
|
||||||
|
onClicked: iconPicker.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NDivider {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Style.marginS * scaling
|
||||||
|
NTextInput {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
label: "Search"
|
||||||
|
placeholderText: "Search (e.g., arrow, battery, cloud)"
|
||||||
|
text: iconPicker.query
|
||||||
|
onTextChanged: iconPicker.query = text.trim().toLowerCase()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Icon grid
|
||||||
|
ScrollView {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
GridView {
|
||||||
|
id: grid
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Style.marginM * scaling
|
||||||
|
cellWidth: iconPicker.cellW
|
||||||
|
cellHeight: iconPicker.cellH
|
||||||
|
model: iconPicker.filteredIcons
|
||||||
|
delegate: Rectangle {
|
||||||
|
width: grid.cellWidth
|
||||||
|
height: grid.cellHeight
|
||||||
|
radius: Style.radiusS * scaling
|
||||||
|
clip: true
|
||||||
|
color: (iconPicker.selectedIcon === modelData) ? Qt.alpha(Color.mPrimary, 0.15) : "transparent"
|
||||||
|
border.color: (iconPicker.selectedIcon === modelData) ? Color.mPrimary : Qt.rgba(0, 0, 0, 0)
|
||||||
|
border.width: (iconPicker.selectedIcon === modelData) ? Style.borderS * scaling : 0
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: iconPicker.selectedIcon = modelData
|
||||||
|
onDoubleClicked: {
|
||||||
|
iconPicker.selectedIcon = modelData
|
||||||
|
iconInput.text = iconPicker.selectedIcon
|
||||||
|
iconPicker.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Style.marginS * scaling
|
||||||
|
spacing: Style.marginS * scaling
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.preferredHeight: 4 * scaling
|
||||||
|
}
|
||||||
|
NIcon {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
icon: modelData
|
||||||
|
font.pointSize: Style.fontSizeXXXL * scaling
|
||||||
|
}
|
||||||
|
NText {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: Style.marginXS * scaling
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
maximumLineCount: 1
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
color: Color.mOnSurfaceVariant
|
||||||
|
font.pointSize: Style.fontSizeXS * scaling
|
||||||
|
text: modelData
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Style.marginM * scaling
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
NButton {
|
||||||
|
text: "Cancel"
|
||||||
|
outlined: true
|
||||||
|
onClicked: iconPicker.close()
|
||||||
|
}
|
||||||
|
NButton {
|
||||||
|
text: "Apply"
|
||||||
|
icon: "check-lg"
|
||||||
|
enabled: iconPicker.selectedIcon !== ""
|
||||||
|
onClicked: {
|
||||||
|
iconInput.text = iconPicker.selectedIcon
|
||||||
|
iconPicker.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NTextInput {
|
NTextInput {
|
||||||
id: leftClickExecInput
|
id: leftClickExecInput
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ ColumnLayout {
|
||||||
!== undefined ? widgetData.showMemoryAsPercent : widgetMetadata.showMemoryAsPercent
|
!== undefined ? widgetData.showMemoryAsPercent : widgetMetadata.showMemoryAsPercent
|
||||||
property bool valueShowNetworkStats: widgetData.showNetworkStats
|
property bool valueShowNetworkStats: widgetData.showNetworkStats
|
||||||
!== undefined ? widgetData.showNetworkStats : widgetMetadata.showNetworkStats
|
!== undefined ? widgetData.showNetworkStats : widgetMetadata.showNetworkStats
|
||||||
|
property bool valueShowDiskUsage: widgetData.showDiskUsage !== undefined ? widgetData.showDiskUsage : widgetMetadata.showDiskUsage
|
||||||
|
|
||||||
function saveSettings() {
|
function saveSettings() {
|
||||||
var settings = Object.assign({}, widgetData || {})
|
var settings = Object.assign({}, widgetData || {})
|
||||||
|
|
@ -29,6 +30,7 @@ ColumnLayout {
|
||||||
settings.showMemoryUsage = valueShowMemoryUsage
|
settings.showMemoryUsage = valueShowMemoryUsage
|
||||||
settings.showMemoryAsPercent = valueShowMemoryAsPercent
|
settings.showMemoryAsPercent = valueShowMemoryAsPercent
|
||||||
settings.showNetworkStats = valueShowNetworkStats
|
settings.showNetworkStats = valueShowNetworkStats
|
||||||
|
settings.showDiskUsage = valueShowDiskUsage
|
||||||
return settings
|
return settings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,7 +61,7 @@ ColumnLayout {
|
||||||
NToggle {
|
NToggle {
|
||||||
id: showMemoryAsPercent
|
id: showMemoryAsPercent
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
label: "Show memory as percentage"
|
label: "Memory as percentage"
|
||||||
checked: valueShowMemoryAsPercent
|
checked: valueShowMemoryAsPercent
|
||||||
onToggled: checked => valueShowMemoryAsPercent = checked
|
onToggled: checked => valueShowMemoryAsPercent = checked
|
||||||
}
|
}
|
||||||
|
|
@ -71,4 +73,12 @@ ColumnLayout {
|
||||||
checked: valueShowNetworkStats
|
checked: valueShowNetworkStats
|
||||||
onToggled: checked => valueShowNetworkStats = checked
|
onToggled: checked => valueShowNetworkStats = checked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NToggle {
|
||||||
|
id: showDiskUsage
|
||||||
|
Layout.fillWidth: true
|
||||||
|
label: "Storage usage"
|
||||||
|
checked: valueShowDiskUsage
|
||||||
|
onToggled: checked => valueShowDiskUsage = checked
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,42 +123,42 @@ NPanel {
|
||||||
let newTabs = [{
|
let newTabs = [{
|
||||||
"id": SettingsPanel.Tab.General,
|
"id": SettingsPanel.Tab.General,
|
||||||
"label": "General",
|
"label": "General",
|
||||||
"icon": "tune",
|
"icon": "box",
|
||||||
"source": generalTab
|
"source": generalTab
|
||||||
}, {
|
}, {
|
||||||
"id": SettingsPanel.Tab.Bar,
|
"id": SettingsPanel.Tab.Bar,
|
||||||
"label": "Bar",
|
"label": "Bar",
|
||||||
"icon": "web_asset",
|
"icon": "segmented-nav",
|
||||||
"source": barTab
|
"source": barTab
|
||||||
}, {
|
}, {
|
||||||
"id": SettingsPanel.Tab.Launcher,
|
"id": SettingsPanel.Tab.Launcher,
|
||||||
"label": "Launcher",
|
"label": "Launcher",
|
||||||
"icon": "apps",
|
"icon": "rocket",
|
||||||
"source": launcherTab
|
"source": launcherTab
|
||||||
}, {
|
}, {
|
||||||
"id": SettingsPanel.Tab.Audio,
|
"id": SettingsPanel.Tab.Audio,
|
||||||
"label": "Audio",
|
"label": "Audio",
|
||||||
"icon": "volume_up",
|
"icon": "speaker",
|
||||||
"source": audioTab
|
"source": audioTab
|
||||||
}, {
|
}, {
|
||||||
"id": SettingsPanel.Tab.Display,
|
"id": SettingsPanel.Tab.Display,
|
||||||
"label": "Display",
|
"label": "Display",
|
||||||
"icon": "monitor",
|
"icon": "display",
|
||||||
"source": displayTab
|
"source": displayTab
|
||||||
}, {
|
}, {
|
||||||
"id": SettingsPanel.Tab.Network,
|
"id": SettingsPanel.Tab.Network,
|
||||||
"label": "Network",
|
"label": "Network",
|
||||||
"icon": "lan",
|
"icon": "ethernet",
|
||||||
"source": networkTab
|
"source": networkTab
|
||||||
}, {
|
}, {
|
||||||
"id": SettingsPanel.Tab.Brightness,
|
"id": SettingsPanel.Tab.Brightness,
|
||||||
"label": "Brightness",
|
"label": "Brightness",
|
||||||
"icon": "brightness_6",
|
"icon": "brightness-high",
|
||||||
"source": brightnessTab
|
"source": brightnessTab
|
||||||
}, {
|
}, {
|
||||||
"id": SettingsPanel.Tab.Weather,
|
"id": SettingsPanel.Tab.Weather,
|
||||||
"label": "Weather",
|
"label": "Weather",
|
||||||
"icon": "partly_cloudy_day",
|
"icon": "cloud-sun",
|
||||||
"source": weatherTab
|
"source": weatherTab
|
||||||
}, {
|
}, {
|
||||||
"id": SettingsPanel.Tab.ColorScheme,
|
"id": SettingsPanel.Tab.ColorScheme,
|
||||||
|
|
@ -168,7 +168,7 @@ NPanel {
|
||||||
}, {
|
}, {
|
||||||
"id": SettingsPanel.Tab.Wallpaper,
|
"id": SettingsPanel.Tab.Wallpaper,
|
||||||
"label": "Wallpaper",
|
"label": "Wallpaper",
|
||||||
"icon": "image",
|
"icon": "easel",
|
||||||
"source": wallpaperTab
|
"source": wallpaperTab
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
|
@ -177,7 +177,7 @@ NPanel {
|
||||||
newTabs.push({
|
newTabs.push({
|
||||||
"id": SettingsPanel.Tab.WallpaperSelector,
|
"id": SettingsPanel.Tab.WallpaperSelector,
|
||||||
"label": "Wallpaper Selector",
|
"label": "Wallpaper Selector",
|
||||||
"icon": "wallpaper_slideshow",
|
"icon": "image",
|
||||||
"source": wallpaperSelectorTab
|
"source": wallpaperSelectorTab
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -185,17 +185,17 @@ NPanel {
|
||||||
newTabs.push({
|
newTabs.push({
|
||||||
"id": SettingsPanel.Tab.ScreenRecorder,
|
"id": SettingsPanel.Tab.ScreenRecorder,
|
||||||
"label": "Screen Recorder",
|
"label": "Screen Recorder",
|
||||||
"icon": "videocam",
|
"icon": "camera-video",
|
||||||
"source": screenRecorderTab
|
"source": screenRecorderTab
|
||||||
}, {
|
}, {
|
||||||
"id": SettingsPanel.Tab.Hooks,
|
"id": SettingsPanel.Tab.Hooks,
|
||||||
"label": "Hooks",
|
"label": "Hooks",
|
||||||
"icon": "cable",
|
"icon": "link-45deg",
|
||||||
"source": hooksTab
|
"source": hooksTab
|
||||||
}, {
|
}, {
|
||||||
"id": SettingsPanel.Tab.About,
|
"id": SettingsPanel.Tab.About,
|
||||||
"label": "About",
|
"label": "About",
|
||||||
"icon": "info",
|
"icon": "info-circle",
|
||||||
"source": aboutTab
|
"source": aboutTab
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -400,15 +400,13 @@ NPanel {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.leftMargin: Style.marginS * scaling
|
anchors.leftMargin: Style.marginS * scaling
|
||||||
anchors.rightMargin: Style.marginS * scaling
|
anchors.rightMargin: Style.marginS * scaling
|
||||||
spacing: Style.marginS * scaling
|
spacing: Style.marginM * scaling
|
||||||
|
|
||||||
// Tab icon
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: modelData.icon
|
text: Bootstrap.icons[modelData.icon]
|
||||||
color: tabTextColor
|
color: tabTextColor
|
||||||
font.pointSize: Style.fontSizeL * scaling
|
font.pointSize: Style.fontSizeL * scaling
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tab label
|
// Tab label
|
||||||
NText {
|
NText {
|
||||||
text: modelData.label
|
text: modelData.label
|
||||||
|
|
@ -416,6 +414,7 @@ NPanel {
|
||||||
font.pointSize: Style.fontSizeM * scaling
|
font.pointSize: Style.fontSizeM * scaling
|
||||||
font.weight: Style.fontWeightBold
|
font.weight: Style.fontWeightBold
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -473,7 +472,7 @@ NPanel {
|
||||||
|
|
||||||
// Close button
|
// Close button
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "close"
|
icon: "x-lg"
|
||||||
tooltipText: "Close"
|
tooltipText: "Close"
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
onClicked: root.close()
|
onClicked: root.close()
|
||||||
|
|
|
||||||
|
|
@ -272,7 +272,7 @@ ColumnLayout {
|
||||||
|
|
||||||
// Button aligned to the center of the actual input field
|
// Button aligned to the center of the actual input field
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "add"
|
icon: "plus-lg"
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
Layout.bottomMargin: blacklistInput.description ? Style.marginS * scaling : 0
|
Layout.bottomMargin: blacklistInput.description ? Style.marginS * scaling : 0
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|
@ -322,7 +322,7 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "close"
|
icon: "x-lg"
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.rightMargin: Style.marginXS * scaling
|
Layout.rightMargin: Style.marginXS * scaling
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "refresh"
|
icon: "arrow-repeat"
|
||||||
tooltipText: "Reset scaling"
|
tooltipText: "Reset scaling"
|
||||||
onClicked: ScalingService.setScreenScale(modelData, 1.0)
|
onClicked: ScalingService.setScreenScale(modelData, 1.0)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "refresh"
|
icon: "arrow-repeat"
|
||||||
tooltipText: "Refresh wallpaper list"
|
tooltipText: "Refresh wallpaper list"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
WallpaperService.refreshWallpapersList()
|
WallpaperService.refreshWallpapersList()
|
||||||
|
|
@ -181,7 +181,7 @@ ColumnLayout {
|
||||||
visible: isSelected
|
visible: isSelected
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "check"
|
icon: "check-lg"
|
||||||
font.pointSize: Style.fontSizeM * scaling
|
font.pointSize: Style.fontSizeM * scaling
|
||||||
font.weight: Style.fontWeightBold
|
font.weight: Style.fontWeightBold
|
||||||
color: Color.mOnSecondary
|
color: Color.mOnSecondary
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ NBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "album"
|
icon: "disc"
|
||||||
font.pointSize: Style.fontSizeXXXL * 2.5 * scaling
|
font.pointSize: Style.fontSizeXXXL * 2.5 * scaling
|
||||||
color: Color.mPrimary
|
color: Color.mPrimary
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
@ -162,14 +162,14 @@ NBox {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Style.marginXS * scaling
|
anchors.margins: Style.marginXS * scaling
|
||||||
imagePath: MediaService.trackArtUrl
|
imagePath: MediaService.trackArtUrl
|
||||||
fallbackIcon: "music_note"
|
fallbackIcon: "disc"
|
||||||
borderColor: Color.mOutline
|
borderColor: Color.mOutline
|
||||||
borderWidth: Math.max(1, Style.borderS * scaling)
|
borderWidth: Math.max(1, Style.borderS * scaling)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback icon when no album art available
|
// Fallback icon when no album art available
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "album"
|
icon: "disc"
|
||||||
color: Color.mPrimary
|
color: Color.mPrimary
|
||||||
font.pointSize: Style.fontSizeL * 12 * scaling
|
font.pointSize: Style.fontSizeL * 12 * scaling
|
||||||
visible: !trackArt.visible
|
visible: !trackArt.visible
|
||||||
|
|
@ -307,7 +307,7 @@ NBox {
|
||||||
|
|
||||||
// Previous button
|
// Previous button
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "skip_previous"
|
icon: "skip-start"
|
||||||
tooltipText: "Previous Media"
|
tooltipText: "Previous Media"
|
||||||
visible: MediaService.canGoPrevious
|
visible: MediaService.canGoPrevious
|
||||||
onClicked: MediaService.canGoPrevious ? MediaService.previous() : {}
|
onClicked: MediaService.canGoPrevious ? MediaService.previous() : {}
|
||||||
|
|
@ -315,7 +315,7 @@ NBox {
|
||||||
|
|
||||||
// Play/Pause button
|
// Play/Pause button
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: MediaService.isPlaying ? "pause" : "play_arrow"
|
icon: MediaService.isPlaying ? "pause" : "play"
|
||||||
tooltipText: MediaService.isPlaying ? "Pause" : "Play"
|
tooltipText: MediaService.isPlaying ? "Pause" : "Play"
|
||||||
visible: (MediaService.canPlay || MediaService.canPause)
|
visible: (MediaService.canPlay || MediaService.canPause)
|
||||||
onClicked: (MediaService.canPlay || MediaService.canPause) ? MediaService.playPause() : {}
|
onClicked: (MediaService.canPlay || MediaService.canPause) ? MediaService.playPause() : {}
|
||||||
|
|
@ -323,7 +323,7 @@ NBox {
|
||||||
|
|
||||||
// Next button
|
// Next button
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "skip_next"
|
icon: "skip-end"
|
||||||
tooltipText: "Next media"
|
tooltipText: "Next media"
|
||||||
visible: MediaService.canGoNext
|
visible: MediaService.canGoNext
|
||||||
onClicked: MediaService.canGoNext ? MediaService.next() : {}
|
onClicked: MediaService.canGoNext ? MediaService.next() : {}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ NBox {
|
||||||
}
|
}
|
||||||
// Performance
|
// Performance
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "speed"
|
icon: "speedometer2"
|
||||||
tooltipText: "Set performance power profile."
|
tooltipText: "Set performance power profile."
|
||||||
enabled: hasPP
|
enabled: hasPP
|
||||||
opacity: enabled ? Style.opacityFull : Style.opacityMedium
|
opacity: enabled ? Style.opacityFull : Style.opacityMedium
|
||||||
|
|
@ -37,12 +37,13 @@ NBox {
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
powerProfiles.profile = PowerProfile.Performance
|
powerProfiles.profile = PowerProfile.Performance
|
||||||
|
ToastService.showNotice("Power Profile", "Performance")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Balanced
|
// Balanced
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "balance"
|
icon: "yin-yang"
|
||||||
tooltipText: "Set balanced power profile."
|
tooltipText: "Set balanced power profile."
|
||||||
enabled: hasPP
|
enabled: hasPP
|
||||||
opacity: enabled ? Style.opacityFull : Style.opacityMedium
|
opacity: enabled ? Style.opacityFull : Style.opacityMedium
|
||||||
|
|
@ -51,12 +52,13 @@ NBox {
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
powerProfiles.profile = PowerProfile.Balanced
|
powerProfiles.profile = PowerProfile.Balanced
|
||||||
|
ToastService.showNotice("Power Profile", "Balanced")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Eco
|
// Eco
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "eco"
|
icon: "leaf"
|
||||||
tooltipText: "Set eco power profile."
|
tooltipText: "Set eco power profile."
|
||||||
enabled: hasPP
|
enabled: hasPP
|
||||||
opacity: enabled ? Style.opacityFull : Style.opacityMedium
|
opacity: enabled ? Style.opacityFull : Style.opacityMedium
|
||||||
|
|
@ -65,6 +67,7 @@ NBox {
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
powerProfiles.profile = PowerProfile.PowerSaver
|
powerProfiles.profile = PowerProfile.PowerSaver
|
||||||
|
ToastService.showNotice("Power Profile", "Power Saver")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,8 @@ NBox {
|
||||||
}
|
}
|
||||||
NText {
|
NText {
|
||||||
text: `System uptime: ${uptimeText}`
|
text: `System uptime: ${uptimeText}`
|
||||||
color: Color.mOnSurface
|
font.pointSize: Style.fontSizeS * scaling
|
||||||
|
color: Color.mOnSurfaceVariant
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,7 +59,7 @@ NBox {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "settings"
|
icon: "gear"
|
||||||
tooltipText: "Open settings."
|
tooltipText: "Open settings."
|
||||||
onClicked: {
|
onClicked: {
|
||||||
settingsPanel.requestedTab = SettingsPanel.Tab.General
|
settingsPanel.requestedTab = SettingsPanel.Tab.General
|
||||||
|
|
@ -68,7 +69,7 @@ NBox {
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
id: powerButton
|
id: powerButton
|
||||||
icon: "power_settings_new"
|
icon: "power"
|
||||||
tooltipText: "Power menu."
|
tooltipText: "Power menu."
|
||||||
onClicked: {
|
onClicked: {
|
||||||
powerPanel.open(screen)
|
powerPanel.open(screen)
|
||||||
|
|
@ -78,7 +79,7 @@ NBox {
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
id: closeButton
|
id: closeButton
|
||||||
icon: "close"
|
icon: "x-lg"
|
||||||
tooltipText: "Close side panel."
|
tooltipText: "Close side panel."
|
||||||
onClicked: {
|
onClicked: {
|
||||||
sidePanel.close()
|
sidePanel.close()
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ NBox {
|
||||||
|
|
||||||
NCircleStat {
|
NCircleStat {
|
||||||
value: SystemStatService.cpuUsage
|
value: SystemStatService.cpuUsage
|
||||||
icon: "speed"
|
icon: "speedometer2"
|
||||||
flat: true
|
flat: true
|
||||||
contentScale: 0.8
|
contentScale: 0.8
|
||||||
width: 72 * scaling
|
width: 72 * scaling
|
||||||
|
|
@ -33,7 +33,7 @@ NBox {
|
||||||
NCircleStat {
|
NCircleStat {
|
||||||
value: SystemStatService.cpuTemp
|
value: SystemStatService.cpuTemp
|
||||||
suffix: "°C"
|
suffix: "°C"
|
||||||
icon: "device_thermostat"
|
icon: "fire"
|
||||||
flat: true
|
flat: true
|
||||||
contentScale: 0.8
|
contentScale: 0.8
|
||||||
width: 72 * scaling
|
width: 72 * scaling
|
||||||
|
|
@ -41,7 +41,7 @@ NBox {
|
||||||
}
|
}
|
||||||
NCircleStat {
|
NCircleStat {
|
||||||
value: SystemStatService.memPercent
|
value: SystemStatService.memPercent
|
||||||
icon: "memory"
|
icon: "cpu"
|
||||||
flat: true
|
flat: true
|
||||||
contentScale: 0.8
|
contentScale: 0.8
|
||||||
width: 72 * scaling
|
width: 72 * scaling
|
||||||
|
|
@ -49,7 +49,7 @@ NBox {
|
||||||
}
|
}
|
||||||
NCircleStat {
|
NCircleStat {
|
||||||
value: SystemStatService.diskPercent
|
value: SystemStatService.diskPercent
|
||||||
icon: "hard_drive"
|
icon: "hdd"
|
||||||
flat: true
|
flat: true
|
||||||
contentScale: 0.8
|
contentScale: 0.8
|
||||||
width: 72 * scaling
|
width: 72 * scaling
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,14 @@ NBox {
|
||||||
}
|
}
|
||||||
// Screen Recorder
|
// Screen Recorder
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "videocam"
|
icon: "camera-video"
|
||||||
tooltipText: ScreenRecorderService.isRecording ? "Stop screen recording." : "Start screen recording."
|
enabled: ScreenRecorderService.isAvailable
|
||||||
|
tooltipText: ScreenRecorderService.isAvailable ? (ScreenRecorderService.isRecording ? "Stop screen recording." : "Start screen recording.") : "Screen recorder not installed."
|
||||||
colorBg: ScreenRecorderService.isRecording ? Color.mPrimary : Color.mSurfaceVariant
|
colorBg: ScreenRecorderService.isRecording ? Color.mPrimary : Color.mSurfaceVariant
|
||||||
colorFg: ScreenRecorderService.isRecording ? Color.mOnPrimary : Color.mPrimary
|
colorFg: ScreenRecorderService.isRecording ? Color.mOnPrimary : Color.mPrimary
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
if (!ScreenRecorderService.isAvailable)
|
||||||
|
return
|
||||||
ScreenRecorderService.toggleRecording()
|
ScreenRecorderService.toggleRecording()
|
||||||
// If we were not recording and we just initiated a start, close the panel
|
// If we were not recording and we just initiated a start, close the panel
|
||||||
if (!ScreenRecorderService.isRecording) {
|
if (!ScreenRecorderService.isRecording) {
|
||||||
|
|
@ -41,7 +44,7 @@ NBox {
|
||||||
|
|
||||||
// Idle Inhibitor
|
// Idle Inhibitor
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "coffee"
|
icon: "cup"
|
||||||
tooltipText: IdleInhibitorService.isInhibited ? "Disable keep awake." : "Enable keep awake."
|
tooltipText: IdleInhibitorService.isInhibited ? "Disable keep awake." : "Enable keep awake."
|
||||||
colorBg: IdleInhibitorService.isInhibited ? Color.mPrimary : Color.mSurfaceVariant
|
colorBg: IdleInhibitorService.isInhibited ? Color.mPrimary : Color.mSurfaceVariant
|
||||||
colorFg: IdleInhibitorService.isInhibited ? Color.mOnPrimary : Color.mPrimary
|
colorFg: IdleInhibitorService.isInhibited ? Color.mOnPrimary : Color.mPrimary
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ NBox {
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: Style.marginS * scaling
|
spacing: Style.marginS * scaling
|
||||||
NIcon {
|
NIcon {
|
||||||
text: weatherReady ? LocationService.weatherSymbolFromCode(
|
icon: weatherReady ? LocationService.weatherSymbolFromCode(
|
||||||
LocationService.data.weather.current_weather.weathercode) : ""
|
LocationService.data.weather.current_weather.weathercode) : ""
|
||||||
font.pointSize: Style.fontSizeXXXL * 1.75 * scaling
|
font.pointSize: Style.fontSizeXXXL * 1.75 * scaling
|
||||||
color: Color.mPrimary
|
color: Color.mPrimary
|
||||||
|
|
@ -98,7 +98,7 @@ NBox {
|
||||||
color: Color.mOnSurface
|
color: Color.mOnSurface
|
||||||
}
|
}
|
||||||
NIcon {
|
NIcon {
|
||||||
text: LocationService.weatherSymbolFromCode(LocationService.data.weather.daily.weathercode[index])
|
icon: LocationService.weatherSymbolFromCode(LocationService.data.weather.daily.weathercode[index])
|
||||||
font.pointSize: Style.fontSizeXXL * scaling
|
font.pointSize: Style.fontSizeXXL * scaling
|
||||||
color: Color.mPrimary
|
color: Color.mPrimary
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ NPanel {
|
||||||
spacing: Style.marginM * scaling
|
spacing: Style.marginM * scaling
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: Settings.data.network.wifiEnabled ? "wifi" : "wifi_off"
|
icon: Settings.data.network.wifiEnabled ? "wifi" : "wifi-off"
|
||||||
font.pointSize: Style.fontSizeXXL * scaling
|
font.pointSize: Style.fontSizeXXL * scaling
|
||||||
color: Settings.data.network.wifiEnabled ? Color.mPrimary : Color.mOnSurfaceVariant
|
color: Settings.data.network.wifiEnabled ? Color.mPrimary : Color.mOnSurfaceVariant
|
||||||
}
|
}
|
||||||
|
|
@ -55,7 +55,7 @@ NPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "refresh"
|
icon: "arrow-repeat"
|
||||||
tooltipText: "Refresh"
|
tooltipText: "Refresh"
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
enabled: Settings.data.network.wifiEnabled && !NetworkService.scanning
|
enabled: Settings.data.network.wifiEnabled && !NetworkService.scanning
|
||||||
|
|
@ -63,7 +63,7 @@ NPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "close"
|
icon: "x-lg"
|
||||||
tooltipText: "Close"
|
tooltipText: "Close"
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
onClicked: root.close()
|
onClicked: root.close()
|
||||||
|
|
@ -91,7 +91,7 @@ NPanel {
|
||||||
spacing: Style.marginS * scaling
|
spacing: Style.marginS * scaling
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "error"
|
icon: "exclamation-triangle"
|
||||||
font.pointSize: Style.fontSizeL * scaling
|
font.pointSize: Style.fontSizeL * scaling
|
||||||
color: Color.mError
|
color: Color.mError
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +105,7 @@ NPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "close"
|
icon: "x-lg"
|
||||||
sizeRatio: 0.6
|
sizeRatio: 0.6
|
||||||
onClicked: NetworkService.lastError = ""
|
onClicked: NetworkService.lastError = ""
|
||||||
}
|
}
|
||||||
|
|
@ -129,7 +129,7 @@ NPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "wifi_off"
|
icon: "wifi-off"
|
||||||
font.pointSize: 64 * scaling
|
font.pointSize: 64 * scaling
|
||||||
color: Color.mOnSurfaceVariant
|
color: Color.mOnSurfaceVariant
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
@ -245,7 +245,7 @@ NPanel {
|
||||||
spacing: Style.marginS * scaling
|
spacing: Style.marginS * scaling
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: NetworkService.signalIcon(modelData.signal)
|
icon: NetworkService.signalIcon(modelData.signal)
|
||||||
font.pointSize: Style.fontSizeXXL * scaling
|
font.pointSize: Style.fontSizeXXL * scaling
|
||||||
color: modelData.connected ? Color.mPrimary : Color.mOnSurface
|
color: modelData.connected ? Color.mPrimary : Color.mOnSurface
|
||||||
}
|
}
|
||||||
|
|
@ -377,7 +377,7 @@ NPanel {
|
||||||
&& NetworkService.connectingTo !== modelData.ssid
|
&& NetworkService.connectingTo !== modelData.ssid
|
||||||
&& NetworkService.forgettingNetwork !== modelData.ssid
|
&& NetworkService.forgettingNetwork !== modelData.ssid
|
||||||
&& NetworkService.disconnectingFrom !== modelData.ssid
|
&& NetworkService.disconnectingFrom !== modelData.ssid
|
||||||
icon: "delete"
|
icon: "trash"
|
||||||
tooltipText: "Forget network"
|
tooltipText: "Forget network"
|
||||||
sizeRatio: 0.7
|
sizeRatio: 0.7
|
||||||
onClicked: expandedSsid = expandedSsid === modelData.ssid ? "" : modelData.ssid
|
onClicked: expandedSsid = expandedSsid === modelData.ssid ? "" : modelData.ssid
|
||||||
|
|
@ -492,7 +492,7 @@ NPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "close"
|
icon: "x-lg"
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
onClicked: {
|
onClicked: {
|
||||||
passwordSsid = ""
|
passwordSsid = ""
|
||||||
|
|
@ -547,7 +547,7 @@ NPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "close"
|
icon: "x-lg"
|
||||||
sizeRatio: 0.8
|
sizeRatio: 0.8
|
||||||
onClicked: expandedSsid = ""
|
onClicked: expandedSsid = ""
|
||||||
}
|
}
|
||||||
|
|
@ -571,7 +571,7 @@ NPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "wifi_find"
|
icon: "search"
|
||||||
font.pointSize: 64 * scaling
|
font.pointSize: 64 * scaling
|
||||||
color: Color.mOnSurfaceVariant
|
color: Color.mOnSurfaceVariant
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
@ -586,7 +586,7 @@ NPanel {
|
||||||
|
|
||||||
NButton {
|
NButton {
|
||||||
text: "Scan again"
|
text: "Scan again"
|
||||||
icon: "refresh"
|
icon: "arrow-repeat"
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
onClicked: NetworkService.scan()
|
onClicked: NetworkService.scan()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,11 @@
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
> ⚠️ **BREAKING CHANGE:**
|
||||||
|
> We transitioned from using Material Symbols to Bootstrap Icons, that means you will have to install the new font.
|
||||||
|
---
|
||||||
|
|
||||||
A sleek and minimal desktop shell thoughtfully crafted for Wayland, built with Quickshell.
|
A sleek and minimal desktop shell thoughtfully crafted for Wayland, built with Quickshell.
|
||||||
|
|
||||||
Features a modern modular architecture with a status bar, notification system, control panel, comprehensive system integration, and more — all styled with a warm lavender palette, or your favorite color scheme!
|
Features a modern modular architecture with a status bar, notification system, control panel, comprehensive system integration, and more — all styled with a warm lavender palette, or your favorite color scheme!
|
||||||
|
|
@ -66,7 +71,7 @@ Features a modern modular architecture with a status bar, notification system, c
|
||||||
- `quickshell-git` - Core shell framework
|
- `quickshell-git` - Core shell framework
|
||||||
- `ttf-roboto` - The default font used for most of the UI
|
- `ttf-roboto` - The default font used for most of the UI
|
||||||
- `inter-font` - The default font used for Headers (ex: clock on the LockScreen)
|
- `inter-font` - The default font used for Headers (ex: clock on the LockScreen)
|
||||||
- `ttf-material-symbols-variable-git` - Icon font for UI elements
|
- `ttf-bootstrap-icons` - Icon font for UI elements
|
||||||
- `gpu-screen-recorder` - Screen recording functionality
|
- `gpu-screen-recorder` - Screen recording functionality
|
||||||
- `brightnessctl` - For internal/laptop monitor brightness
|
- `brightnessctl` - For internal/laptop monitor brightness
|
||||||
- `ddcutil` - For desktop monitor brightness (might introduce some system instability with certain monitors)
|
- `ddcutil` - For desktop monitor brightness (might introduce some system instability with certain monitors)
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,8 @@ Singleton {
|
||||||
function onMutedChanged() {
|
function onMutedChanged() {
|
||||||
root._muted = (sink?.audio.muted ?? true)
|
root._muted = (sink?.audio.muted ?? true)
|
||||||
Logger.log("AudioService", "OnMuteChanged:", root._muted)
|
Logger.log("AudioService", "OnMuteChanged:", root._muted)
|
||||||
|
// Toast: audio output mute toggle
|
||||||
|
ToastService.showNotice("Audio Output", root._muted ? "Muted" : "Unmuted")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,6 +81,8 @@ Singleton {
|
||||||
function onMutedChanged() {
|
function onMutedChanged() {
|
||||||
root._inputMuted = (source?.audio.muted ?? true)
|
root._inputMuted = (source?.audio.muted ?? true)
|
||||||
Logger.log("AudioService", "OnInputMuteChanged:", root._inputMuted)
|
Logger.log("AudioService", "OnInputMuteChanged:", root._inputMuted)
|
||||||
|
// Toast: microphone mute toggle
|
||||||
|
ToastService.showNotice("Microphone", root._inputMuted ? "Muted" : "Unmuted")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,8 @@ Singleton {
|
||||||
"showCpuTemp": true,
|
"showCpuTemp": true,
|
||||||
"showMemoryUsage": true,
|
"showMemoryUsage": true,
|
||||||
"showMemoryAsPercent": false,
|
"showMemoryAsPercent": false,
|
||||||
"showNetworkStats": false
|
"showNetworkStats": false,
|
||||||
|
"showDiskUsage": false
|
||||||
},
|
},
|
||||||
"Workspace": {
|
"Workspace": {
|
||||||
"allowUserSettings": true,
|
"allowUserSettings": true,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ pragma Singleton
|
||||||
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Services.UPower
|
import Quickshell.Services.UPower
|
||||||
|
import qs.Commons
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
@ -9,41 +11,20 @@ Singleton {
|
||||||
// Choose icon based on charge and charging state
|
// Choose icon based on charge and charging state
|
||||||
function getIcon(percent, charging, isReady) {
|
function getIcon(percent, charging, isReady) {
|
||||||
if (!isReady) {
|
if (!isReady) {
|
||||||
return "battery_error"
|
return "exclamation-triangle"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (charging) {
|
if (charging) {
|
||||||
if (percent >= 95)
|
return "battery-charging"
|
||||||
return "battery_full"
|
|
||||||
if (percent >= 85)
|
|
||||||
return "battery_charging_90"
|
|
||||||
if (percent >= 65)
|
|
||||||
return "battery_charging_80"
|
|
||||||
if (percent >= 55)
|
|
||||||
return "battery_charging_60"
|
|
||||||
if (percent >= 45)
|
|
||||||
return "battery_charging_50"
|
|
||||||
if (percent >= 25)
|
|
||||||
return "battery_charging_30"
|
|
||||||
if (percent >= 0)
|
|
||||||
return "battery_charging_20"
|
|
||||||
} else {
|
} else {
|
||||||
if (percent >= 95)
|
|
||||||
return "battery_full"
|
|
||||||
if (percent >= 85)
|
if (percent >= 85)
|
||||||
return "battery_6_bar"
|
return "battery-full"
|
||||||
if (percent >= 70)
|
if (percent >= 45)
|
||||||
return "battery_5_bar"
|
return "battery-half"
|
||||||
if (percent >= 55)
|
|
||||||
return "battery_4_bar"
|
|
||||||
if (percent >= 40)
|
|
||||||
return "battery_3_bar"
|
|
||||||
if (percent >= 25)
|
if (percent >= 25)
|
||||||
return "battery_2_bar"
|
return "battery-low"
|
||||||
if (percent >= 10)
|
|
||||||
return "battery_1_bar"
|
|
||||||
if (percent >= 0)
|
if (percent >= 0)
|
||||||
return "battery_0_bar"
|
return "battery"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,17 +62,17 @@ Singleton {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (icon.includes("mouse") || name.includes("mouse")) {
|
if (icon.includes("mouse") || name.includes("mouse")) {
|
||||||
return "mouse"
|
return "mouse-2"
|
||||||
}
|
}
|
||||||
if (icon.includes("keyboard") || name.includes("keyboard")) {
|
if (icon.includes("keyboard") || name.includes("keyboard")) {
|
||||||
return "keyboard"
|
return "keyboard"
|
||||||
}
|
}
|
||||||
if (icon.includes("phone") || name.includes("phone") || name.includes("iphone") || name.includes("android")
|
if (icon.includes("phone") || name.includes("phone") || name.includes("iphone") || name.includes("android")
|
||||||
|| name.includes("samsung")) {
|
|| name.includes("samsung")) {
|
||||||
return "smartphone"
|
return "phone"
|
||||||
}
|
}
|
||||||
if (icon.includes("watch") || name.includes("watch")) {
|
if (icon.includes("watch") || name.includes("watch")) {
|
||||||
return "watch"
|
return "smartwatch"
|
||||||
}
|
}
|
||||||
if (icon.includes("speaker") || name.includes("speaker")) {
|
if (icon.includes("speaker") || name.includes("speaker")) {
|
||||||
return "speaker"
|
return "speaker"
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,11 @@ Singleton {
|
||||||
// Re-apply current scheme to pick the right variant
|
// Re-apply current scheme to pick the right variant
|
||||||
applyScheme(Settings.data.colorSchemes.predefinedScheme)
|
applyScheme(Settings.data.colorSchemes.predefinedScheme)
|
||||||
}
|
}
|
||||||
|
// Toast: dark/light mode switched
|
||||||
|
const enabled = !!Settings.data.colorSchemes.darkMode
|
||||||
|
const label = enabled ? "Dark Mode" : "Light Mode"
|
||||||
|
const description = enabled ? "Enabled" : "Enabled"
|
||||||
|
ToastService.showNotice(label, description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -192,9 +192,11 @@ Singleton {
|
||||||
}
|
}
|
||||||
|
|
||||||
windowsList.push({
|
windowsList.push({
|
||||||
"id": toplevel.address || "",
|
"id": (toplevel.address !== undefined
|
||||||
"title": toplevel.title || "",
|
&& toplevel.address !== null) ? String(toplevel.address) : "",
|
||||||
"appId": appId,
|
"title": (toplevel.title !== undefined && toplevel.title !== null) ? String(
|
||||||
|
toplevel.title) : "",
|
||||||
|
"appId": (appId !== undefined && appId !== null) ? String(appId) : "",
|
||||||
"workspaceId": toplevel.workspace?.id || null,
|
"workspaceId": toplevel.workspace?.id || null,
|
||||||
"isFocused": toplevel.activated === true
|
"isFocused": toplevel.activated === true
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ Singleton {
|
||||||
property ListModel displayFonts: ListModel {}
|
property ListModel displayFonts: ListModel {}
|
||||||
property bool fontsLoaded: false
|
property bool fontsLoaded: false
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
function init() {
|
function init() {
|
||||||
Logger.log("Font", "Service started")
|
Logger.log("Font", "Service started")
|
||||||
loadSystemFonts()
|
loadSystemFonts()
|
||||||
|
|
|
||||||
|
|
@ -231,21 +231,23 @@ Singleton {
|
||||||
// --------------------------------
|
// --------------------------------
|
||||||
function weatherSymbolFromCode(code) {
|
function weatherSymbolFromCode(code) {
|
||||||
if (code === 0)
|
if (code === 0)
|
||||||
return "sunny"
|
return "sun"
|
||||||
if (code === 1 || code === 2)
|
if (code === 1 || code === 2)
|
||||||
return "partly_cloudy_day"
|
return "cloud-sun"
|
||||||
if (code === 3)
|
if (code === 3)
|
||||||
return "cloud"
|
return "cloud"
|
||||||
if (code >= 45 && code <= 48)
|
if (code >= 45 && code <= 48)
|
||||||
return "foggy"
|
return "cloud-haze"
|
||||||
if (code >= 51 && code <= 67)
|
if (code >= 51 && code <= 67)
|
||||||
return "rainy"
|
return "cloud-rain"
|
||||||
if (code >= 71 && code <= 77)
|
if (code >= 71 && code <= 77)
|
||||||
return "weather_snowy"
|
return "cloud-snow"
|
||||||
if (code >= 80 && code <= 82)
|
if (code >= 71 && code <= 77)
|
||||||
return "rainy"
|
return "cloud-snow"
|
||||||
|
if (code >= 85 && code <= 86)
|
||||||
|
return "cloud-snow"
|
||||||
if (code >= 95 && code <= 99)
|
if (code >= 95 && code <= 99)
|
||||||
return "thunderstorm"
|
return "cloud-lightning"
|
||||||
return "cloud"
|
return "cloud"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -202,14 +202,12 @@ Singleton {
|
||||||
// Helper functions
|
// Helper functions
|
||||||
function signalIcon(signal) {
|
function signalIcon(signal) {
|
||||||
if (signal >= 80)
|
if (signal >= 80)
|
||||||
return "network_wifi"
|
return "wifi"
|
||||||
if (signal >= 60)
|
if (signal >= 50)
|
||||||
return "network_wifi_3_bar"
|
return "wifi-2"
|
||||||
if (signal >= 40)
|
|
||||||
return "network_wifi_2_bar"
|
|
||||||
if (signal >= 20)
|
if (signal >= 20)
|
||||||
return "network_wifi_1_bar"
|
return "wifi-1"
|
||||||
return "signal_wifi_0_bar"
|
return "dot"
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSecured(security) {
|
function isSecured(security) {
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,9 @@ Singleton {
|
||||||
target: Settings.data.nightLight
|
target: Settings.data.nightLight
|
||||||
function onEnabledChanged() {
|
function onEnabledChanged() {
|
||||||
apply()
|
apply()
|
||||||
|
// Toast: night light toggled
|
||||||
|
const enabled = !!Settings.data.nightLight.enabled
|
||||||
|
ToastService.showNotice("Night Light", enabled ? "Enabled" : "Disabled")
|
||||||
}
|
}
|
||||||
function onNightTempChanged() {
|
function onNightTempChanged() {
|
||||||
apply()
|
apply()
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,17 @@ Singleton {
|
||||||
property bool isRecording: false
|
property bool isRecording: false
|
||||||
property bool isPending: false
|
property bool isPending: false
|
||||||
property string outputPath: ""
|
property string outputPath: ""
|
||||||
|
property bool isAvailable: false
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
checkAvailability()
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAvailability() {
|
||||||
|
// Detect native or Flatpak gpu-screen-recorder
|
||||||
|
availabilityCheckProcess.command = ["sh", "-c", "command -v gpu-screen-recorder >/dev/null 2>&1 || (command -v flatpak >/dev/null 2>&1 && flatpak list --app | grep -q 'com.dec05eba.gpu_screen_recorder')"]
|
||||||
|
availabilityCheckProcess.running = true
|
||||||
|
}
|
||||||
|
|
||||||
// Start or Stop recording
|
// Start or Stop recording
|
||||||
function toggleRecording() {
|
function toggleRecording() {
|
||||||
|
|
@ -21,6 +32,9 @@ Singleton {
|
||||||
|
|
||||||
// Start screen recording using Quickshell.execDetached
|
// Start screen recording using Quickshell.execDetached
|
||||||
function startRecording() {
|
function startRecording() {
|
||||||
|
if (!isAvailable) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (isRecording || isPending) {
|
if (isRecording || isPending) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -88,6 +102,18 @@ Singleton {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Availability check process
|
||||||
|
Process {
|
||||||
|
id: availabilityCheckProcess
|
||||||
|
command: ["sh", "-c", "true"]
|
||||||
|
onExited: function (exitCode, exitStatus) {
|
||||||
|
// exitCode 0 means available, non-zero means unavailable
|
||||||
|
root.isAvailable = (exitCode === 0)
|
||||||
|
}
|
||||||
|
stdout: StdioCollector {}
|
||||||
|
stderr: StdioCollector {}
|
||||||
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: pendingTimer
|
id: pendingTimer
|
||||||
interval: 2000 // Wait 2 seconds to see if process stays alive
|
interval: 2000 // Wait 2 seconds to see if process stays alive
|
||||||
|
|
|
||||||
|
|
@ -321,10 +321,8 @@ Singleton {
|
||||||
// -------------------------------------------------------
|
// -------------------------------------------------------
|
||||||
// Helper function to format network speeds
|
// Helper function to format network speeds
|
||||||
function formatSpeed(bytesPerSecond) {
|
function formatSpeed(bytesPerSecond) {
|
||||||
if (bytesPerSecond < 1024) {
|
if (bytesPerSecond < 1024 * 1024) {
|
||||||
return bytesPerSecond.toFixed(0) + "B/s"
|
return (bytesPerSecond / 1024).toFixed(1) + "KB/s"
|
||||||
} else if (bytesPerSecond < 1024 * 1024) {
|
|
||||||
return (bytesPerSecond / 1024).toFixed(0) + "KB/s"
|
|
||||||
} else if (bytesPerSecond < 1024 * 1024 * 1024) {
|
} else if (bytesPerSecond < 1024 * 1024 * 1024) {
|
||||||
return (bytesPerSecond / (1024 * 1024)).toFixed(1) + "MB/s"
|
return (bytesPerSecond / (1024 * 1024)).toFixed(1) + "MB/s"
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,11 @@ Singleton {
|
||||||
// Process the message queue
|
// Process the message queue
|
||||||
function processQueue() {
|
function processQueue() {
|
||||||
if (messageQueue.length === 0 || allToasts.length === 0) {
|
if (messageQueue.length === 0 || allToasts.length === 0) {
|
||||||
|
// Added this so we don't accidentally get duplicate toasts
|
||||||
|
// if it causes issues, remove it and we'll find a different solution
|
||||||
|
if (allToasts.length === 0 && messageQueue.length > 0) {
|
||||||
|
messageQueue = []
|
||||||
|
}
|
||||||
isShowingToast = false
|
isShowingToast = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,27 +80,29 @@ Rectangle {
|
||||||
spacing: Style.marginXS * scaling
|
spacing: Style.marginXS * scaling
|
||||||
|
|
||||||
// Icon (optional)
|
// Icon (optional)
|
||||||
NIcon {
|
Loader {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
active: root.icon !== ""
|
||||||
layoutTopMargin: 1 * scaling
|
sourceComponent: NIcon {
|
||||||
visible: root.icon !== ""
|
Layout.alignment: Qt.AlignVCenter
|
||||||
text: root.icon
|
|
||||||
font.pointSize: root.iconSize
|
|
||||||
color: {
|
|
||||||
if (!root.enabled)
|
|
||||||
return Color.mOnSurfaceVariant
|
|
||||||
if (root.outlined) {
|
|
||||||
if (root.pressed || root.hovered)
|
|
||||||
return root.backgroundColor
|
|
||||||
return root.backgroundColor
|
|
||||||
}
|
|
||||||
return root.textColor
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
icon: root.icon
|
||||||
ColorAnimation {
|
font.pointSize: root.iconSize
|
||||||
duration: Style.animationFast
|
color: {
|
||||||
easing.type: Easing.OutCubic
|
if (!root.enabled)
|
||||||
|
return Color.mOnSurfaceVariant
|
||||||
|
if (root.outlined) {
|
||||||
|
if (root.pressed || root.hovered)
|
||||||
|
return root.backgroundColor
|
||||||
|
return root.backgroundColor
|
||||||
|
}
|
||||||
|
return root.textColor
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Style.animationFast
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ RowLayout {
|
||||||
NIcon {
|
NIcon {
|
||||||
visible: root.checked
|
visible: root.checked
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: "check"
|
icon: "check-lg"
|
||||||
color: root.activeOnColor
|
color: root.activeOnColor
|
||||||
font.pointSize: Math.max(Style.fontSizeS, root.baseSize * 0.7) * scaling
|
font.pointSize: Math.max(Style.fontSizeS, root.baseSize * 0.7) * scaling
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,20 +88,21 @@ Rectangle {
|
||||||
// Tiny circular badge for the icon, positioned using anchors within the gauge
|
// Tiny circular badge for the icon, positioned using anchors within the gauge
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: iconBadge
|
id: iconBadge
|
||||||
width: 28 * scaling * contentScale
|
width: iconText.implicitWidth + Style.marginXS * scaling
|
||||||
height: width
|
height: width
|
||||||
radius: width / 2
|
radius: width / 2
|
||||||
color: Color.mSurface
|
color: Color.mPrimary
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.rightMargin: -6 * scaling * contentScale
|
anchors.rightMargin: Style.marginXXS * scaling * contentScale
|
||||||
anchors.topMargin: Style.marginXXS * scaling * contentScale
|
anchors.topMargin: Style.marginXS * scaling * contentScale
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
|
id: iconText
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: root.icon
|
icon: root.icon
|
||||||
font.pointSize: Style.fontSizeLargeXL * scaling * contentScale
|
font.pointSize: Style.fontSizeS * scaling * contentScale
|
||||||
color: Color.mOnSurface
|
color: Color.mOnPrimary
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import qs.Commons
|
import qs.Commons
|
||||||
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
|
@ -58,7 +59,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "palette"
|
icon: "paint-bucket"
|
||||||
color: Color.mOnSurfaceVariant
|
color: Color.mOnSurfaceVariant
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import qs.Commons
|
import qs.Commons
|
||||||
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
|
|
@ -129,7 +130,7 @@ Popup {
|
||||||
spacing: Style.marginS * scaling
|
spacing: Style.marginS * scaling
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: "palette"
|
icon: "eyedropper"
|
||||||
font.pointSize: Style.fontSizeXXL * scaling
|
font.pointSize: Style.fontSizeXXL * scaling
|
||||||
color: Color.mPrimary
|
color: Color.mPrimary
|
||||||
}
|
}
|
||||||
|
|
@ -147,7 +148,7 @@ Popup {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "close"
|
icon: "x-lg"
|
||||||
onClicked: root.close()
|
onClicked: root.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -491,7 +492,7 @@ Popup {
|
||||||
NButton {
|
NButton {
|
||||||
id: cancelButton
|
id: cancelButton
|
||||||
text: "Cancel"
|
text: "Cancel"
|
||||||
icon: "close"
|
icon: "x-lg"
|
||||||
outlined: cancelButton.hovered ? false : true
|
outlined: cancelButton.hovered ? false : true
|
||||||
customHeight: 36 * scaling
|
customHeight: 36 * scaling
|
||||||
customWidth: 100 * scaling
|
customWidth: 100 * scaling
|
||||||
|
|
@ -502,7 +503,7 @@ Popup {
|
||||||
|
|
||||||
NButton {
|
NButton {
|
||||||
text: "Apply"
|
text: "Apply"
|
||||||
icon: "check"
|
icon: "check-lg"
|
||||||
customHeight: 36 * scaling
|
customHeight: 36 * scaling
|
||||||
customWidth: 100 * scaling
|
customWidth: 100 * scaling
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,8 @@ RowLayout {
|
||||||
indicator: NIcon {
|
indicator: NIcon {
|
||||||
x: combo.width - width - Style.marginM * scaling
|
x: combo.width - width - Style.marginM * scaling
|
||||||
y: combo.topPadding + (combo.availableHeight - height) / 2
|
y: combo.topPadding + (combo.availableHeight - height) / 2
|
||||||
text: "arrow_drop_down"
|
icon: "chevron-down"
|
||||||
font.pointSize: Style.fontSizeXXL * scaling
|
font.pointSize: Style.fontSizeL * scaling
|
||||||
}
|
}
|
||||||
|
|
||||||
popup: Popup {
|
popup: Popup {
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,22 @@
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
import qs.Commons
|
import qs.Commons
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
// Optional layout nudge for optical alignment when used inside Layouts
|
readonly property string defaultIcon: "balloon"
|
||||||
property real layoutTopMargin: 0
|
property string icon: defaultIcon
|
||||||
text: "question_mark"
|
|
||||||
font.family: "Material Symbols Rounded"
|
text: {
|
||||||
font.pointSize: Style.fontSizeL * scaling
|
if (icon === undefined || Bootstrap.icons[icon] === undefined) {
|
||||||
font.variableAxes: {
|
Logger.warn("Icon", `"${icon}"`, "doesn't exist in the bootstrap font")
|
||||||
"wght"// slightly bold to ensure all lines looks good
|
Logger.callStack()
|
||||||
: (Font.Normal + Font.Bold) / 2.5
|
return Bootstrap.icons[defaultIcon]
|
||||||
|
}
|
||||||
|
return Bootstrap.icons[icon]
|
||||||
}
|
}
|
||||||
|
font.family: "bootstrap-icons"
|
||||||
|
font.pointSize: Style.fontSizeL * scaling
|
||||||
color: Color.mOnSurface
|
color: Color.mOnSurface
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
Layout.topMargin: layoutTopMargin
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,17 +35,31 @@ Rectangle {
|
||||||
opacity: root.enabled ? Style.opacityFull : Style.opacityMedium
|
opacity: root.enabled ? Style.opacityFull : Style.opacityMedium
|
||||||
color: root.enabled && root.hovering ? colorBgHover : colorBg
|
color: root.enabled && root.hovering ? colorBgHover : colorBg
|
||||||
radius: width * 0.5
|
radius: width * 0.5
|
||||||
border.color: root.hovering ? colorBorderHover : colorBorder
|
border.color: root.enabled && root.hovering ? colorBorderHover : colorBorder
|
||||||
border.width: Math.max(1, Style.borderS * scaling)
|
border.width: Math.max(1, Style.borderS * scaling)
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Style.animationNormal
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: root.icon
|
icon: root.icon
|
||||||
font.pointSize: Style.fontSizeM * scaling
|
font.pointSize: Math.max(1, root.width * 0.4)
|
||||||
color: root.hovering ? colorFgHover : colorFg
|
color: root.enabled && root.hovering ? colorFgHover : colorFg
|
||||||
// Center horizontally
|
// Center horizontally
|
||||||
x: (root.width - width) / 2
|
x: (root.width - width) / 2
|
||||||
// Center vertically accounting for font metrics
|
// Center vertically accounting for font metrics
|
||||||
y: (root.height - height) / 2 + (height - contentHeight) / 2
|
y: (root.height - height) / 2 + (height - contentHeight) / 2
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Style.animationFast
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NTooltip {
|
NTooltip {
|
||||||
|
|
@ -56,13 +70,14 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
enabled: root.enabled
|
// Always enabled to allow hover/tooltip even when the button is disabled
|
||||||
|
enabled: true
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
onEntered: {
|
onEntered: {
|
||||||
hovering = true
|
hovering = root.enabled ? true : false
|
||||||
if (tooltipText) {
|
if (tooltipText) {
|
||||||
tooltip.show()
|
tooltip.show()
|
||||||
}
|
}
|
||||||
|
|
@ -79,6 +94,9 @@ Rectangle {
|
||||||
if (tooltipText) {
|
if (tooltipText) {
|
||||||
tooltip.hide()
|
tooltip.hide()
|
||||||
}
|
}
|
||||||
|
if (!root.enabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (mouse.button === Qt.LeftButton) {
|
if (mouse.button === Qt.LeftButton) {
|
||||||
root.clicked()
|
root.clicked()
|
||||||
} else if (mouse.button === Qt.RightButton) {
|
} else if (mouse.button === Qt.RightButton) {
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,10 @@ Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string imagePath: ""
|
property string imagePath: ""
|
||||||
property string fallbackIcon: ""
|
|
||||||
property color borderColor: Color.transparent
|
property color borderColor: Color.transparent
|
||||||
property real borderWidth: 0
|
property real borderWidth: 0
|
||||||
|
property string fallbackIcon: ""
|
||||||
|
property real fallbackIconSize: Style.fontSizeXXL * scaling
|
||||||
|
|
||||||
color: Color.transparent
|
color: Color.transparent
|
||||||
radius: parent.width * 0.5
|
radius: parent.width * 0.5
|
||||||
|
|
@ -45,18 +46,20 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
property real imageOpacity: root.opacity
|
property real imageOpacity: root.opacity
|
||||||
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/circled_image.frag.qsb")
|
fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/circled_image.frag.qsb")
|
||||||
supportsAtlasTextures: false
|
supportsAtlasTextures: false
|
||||||
blending: true
|
blending: true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback icon
|
// Fallback icon
|
||||||
NIcon {
|
Loader {
|
||||||
anchors.centerIn: parent
|
active: fallbackIcon !== undefined && fallbackIcon !== "" && (imagePath === undefined || imagePath === "")
|
||||||
text: fallbackIcon
|
sourceComponent: NIcon {
|
||||||
font.pointSize: Style.fontSizeXXL * scaling
|
anchors.centerIn: parent
|
||||||
visible: fallbackIcon !== undefined && fallbackIcon !== "" && (imagePath === undefined || imagePath === "")
|
icon: fallbackIcon
|
||||||
z: 0
|
font.pointSize: fallbackIconSize
|
||||||
|
z: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,11 @@ Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string imagePath: ""
|
property string imagePath: ""
|
||||||
property string fallbackIcon: ""
|
|
||||||
property color borderColor: Color.transparent
|
property color borderColor: Color.transparent
|
||||||
property real borderWidth: 0
|
property real borderWidth: 0
|
||||||
property real imageRadius: width * 0.5
|
property real imageRadius: width * 0.5
|
||||||
|
property string fallbackIcon: ""
|
||||||
|
property real fallbackIconSize: Style.fontSizeXXL * scaling
|
||||||
|
|
||||||
property real scaledRadius: imageRadius * Settings.data.general.radiusRatio
|
property real scaledRadius: imageRadius * Settings.data.general.radiusRatio
|
||||||
|
|
||||||
|
|
@ -56,7 +57,7 @@ Rectangle {
|
||||||
property real itemHeight: root.height
|
property real itemHeight: root.height
|
||||||
property real cornerRadius: root.radius
|
property real cornerRadius: root.radius
|
||||||
property real imageOpacity: root.opacity
|
property real imageOpacity: root.opacity
|
||||||
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/rounded_image.frag.qsb")
|
fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/rounded_image.frag.qsb")
|
||||||
|
|
||||||
// Qt6 specific properties - ensure proper blending
|
// Qt6 specific properties - ensure proper blending
|
||||||
supportsAtlasTextures: false
|
supportsAtlasTextures: false
|
||||||
|
|
@ -71,12 +72,14 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback icon
|
// Fallback icon
|
||||||
NIcon {
|
Loader {
|
||||||
anchors.centerIn: parent
|
active: fallbackIcon !== undefined && fallbackIcon !== "" && (imagePath === undefined || imagePath === "")
|
||||||
text: fallbackIcon
|
sourceComponent: NIcon {
|
||||||
font.pointSize: Style.fontSizeXXL * scaling
|
anchors.centerIn: parent
|
||||||
visible: fallbackIcon !== undefined && fallbackIcon !== "" && (imagePath === undefined || imagePath === "")
|
icon: fallbackIcon
|
||||||
z: 0
|
font.pointSize: fallbackIconSize
|
||||||
|
z: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import qs.Commons
|
import qs.Commons
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
// Input and button row
|
// Input and button row
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
|
@ -13,7 +14,7 @@ RowLayout {
|
||||||
property string placeholderText: ""
|
property string placeholderText: ""
|
||||||
property string text: ""
|
property string text: ""
|
||||||
property string actionButtonText: "Test"
|
property string actionButtonText: "Test"
|
||||||
property string actionButtonIcon: "play_arrow"
|
property string actionButtonIcon: "play"
|
||||||
property bool actionButtonEnabled: text !== ""
|
property bool actionButtonEnabled: text !== ""
|
||||||
|
|
||||||
// Signals
|
// Signals
|
||||||
|
|
|
||||||
|
|
@ -9,21 +9,15 @@ Item {
|
||||||
property string icon: ""
|
property string icon: ""
|
||||||
property string text: ""
|
property string text: ""
|
||||||
property string tooltipText: ""
|
property string tooltipText: ""
|
||||||
property color pillColor: Color.mSurfaceVariant
|
|
||||||
property color textColor: Color.mOnSurface
|
|
||||||
property color iconCircleColor: Color.mPrimary
|
|
||||||
property color iconTextColor: Color.mSurface
|
|
||||||
property color collapsedIconColor: Color.mOnSurface
|
|
||||||
|
|
||||||
property real iconRotation: 0
|
|
||||||
property real sizeRatio: 0.8
|
property real sizeRatio: 0.8
|
||||||
property bool autoHide: false
|
property bool autoHide: false
|
||||||
property bool forceOpen: false
|
property bool forceOpen: false
|
||||||
property bool disableOpen: false
|
property bool disableOpen: false
|
||||||
property bool rightOpen: false
|
property bool rightOpen: false
|
||||||
|
property bool hovered: false
|
||||||
|
|
||||||
// Effective shown state (true if hovered/animated open or forced)
|
// Effective shown state (true if hovered/animated open or forced)
|
||||||
readonly property bool effectiveShown: forceOpen || showPill
|
readonly property bool revealed: forceOpen || showPill
|
||||||
|
|
||||||
signal shown
|
signal shown
|
||||||
signal hidden
|
signal hidden
|
||||||
|
|
@ -50,14 +44,14 @@ Item {
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: pill
|
id: pill
|
||||||
width: effectiveShown ? maxPillWidth : 1
|
width: revealed ? maxPillWidth : 1
|
||||||
height: pillHeight
|
height: pillHeight
|
||||||
|
|
||||||
x: rightOpen ? (iconCircle.x + iconCircle.width / 2) : // Opens right
|
x: rightOpen ? (iconCircle.x + iconCircle.width / 2) : // Opens right
|
||||||
(iconCircle.x + iconCircle.width / 2) - width // Opens left
|
(iconCircle.x + iconCircle.width / 2) - width // Opens left
|
||||||
|
|
||||||
opacity: effectiveShown ? Style.opacityFull : Style.opacityNone
|
opacity: revealed ? Style.opacityFull : Style.opacityNone
|
||||||
color: pillColor
|
color: Color.mSurfaceVariant
|
||||||
|
|
||||||
topLeftRadius: rightOpen ? 0 : pillHeight * 0.5
|
topLeftRadius: rightOpen ? 0 : pillHeight * 0.5
|
||||||
bottomLeftRadius: rightOpen ? 0 : pillHeight * 0.5
|
bottomLeftRadius: rightOpen ? 0 : pillHeight * 0.5
|
||||||
|
|
@ -77,8 +71,8 @@ Item {
|
||||||
text: root.text
|
text: root.text
|
||||||
font.pointSize: Style.fontSizeXS * scaling
|
font.pointSize: Style.fontSizeXS * scaling
|
||||||
font.weight: Style.fontWeightBold
|
font.weight: Style.fontWeightBold
|
||||||
color: textColor
|
color: Color.mPrimary
|
||||||
visible: effectiveShown
|
visible: revealed
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on width {
|
Behavior on width {
|
||||||
|
|
@ -102,11 +96,8 @@ Item {
|
||||||
width: iconSize
|
width: iconSize
|
||||||
height: iconSize
|
height: iconSize
|
||||||
radius: width * 0.5
|
radius: width * 0.5
|
||||||
// When forced shown, match pill background; otherwise use accent when hovered
|
color: hovered && !forceOpen ? Color.mPrimary : Color.mSurfaceVariant
|
||||||
color: forceOpen ? pillColor : (showPill ? iconCircleColor : Color.mSurfaceVariant)
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
border.width: Math.max(1, Style.borderS * scaling)
|
|
||||||
border.color: forceOpen ? Qt.alpha(Color.mOutline, 0.5) : Color.transparent
|
|
||||||
|
|
||||||
x: rightOpen ? 0 : (parent.width - width)
|
x: rightOpen ? 0 : (parent.width - width)
|
||||||
|
|
||||||
|
|
@ -118,11 +109,9 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
text: root.icon
|
icon: root.icon
|
||||||
rotation: root.iconRotation
|
|
||||||
font.pointSize: Style.fontSizeM * scaling
|
font.pointSize: Style.fontSizeM * scaling
|
||||||
// When forced shown, use pill text color; otherwise accent color when hovered
|
color: hovered && !forceOpen ? Color.mOnPrimary : Color.mOnSurface
|
||||||
color: forceOpen ? textColor : (showPill ? iconTextColor : Color.mOnSurface)
|
|
||||||
// Center horizontally
|
// Center horizontally
|
||||||
x: (iconCircle.width - width) / 2
|
x: (iconCircle.width - width) / 2
|
||||||
// Center vertically accounting for font metrics
|
// Center vertically accounting for font metrics
|
||||||
|
|
@ -220,6 +209,7 @@ Item {
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||||
onEntered: {
|
onEntered: {
|
||||||
|
hovered = true
|
||||||
root.entered()
|
root.entered()
|
||||||
tooltip.show()
|
tooltip.show()
|
||||||
if (disableOpen) {
|
if (disableOpen) {
|
||||||
|
|
@ -230,6 +220,7 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onExited: {
|
onExited: {
|
||||||
|
hovered = false
|
||||||
root.exited()
|
root.exited()
|
||||||
if (!forceOpen) {
|
if (!forceOpen) {
|
||||||
hide()
|
hide()
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ RowLayout {
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: "remove"
|
icon: "chevron-left"
|
||||||
font.pointSize: Style.fontSizeS * scaling
|
font.pointSize: Style.fontSizeS * scaling
|
||||||
color: decreaseArea.containsMouse ? Color.mOnPrimary : Color.mPrimary
|
color: decreaseArea.containsMouse ? Color.mOnPrimary : Color.mPrimary
|
||||||
}
|
}
|
||||||
|
|
@ -130,7 +130,7 @@ RowLayout {
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: "add"
|
icon: "chevron-right"
|
||||||
font.pointSize: Style.fontSizeS * scaling
|
font.pointSize: Style.fontSizeS * scaling
|
||||||
color: increaseArea.containsMouse ? Color.mOnPrimary : Color.mPrimary
|
color: increaseArea.containsMouse ? Color.mOnPrimary : Color.mPrimary
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,23 +112,13 @@ Item {
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: contentLayout
|
id: contentLayout
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Style.marginM * scaling
|
anchors.margins: Style.marginL * scaling
|
||||||
spacing: Style.marginS * scaling
|
spacing: Style.marginL * scaling
|
||||||
|
|
||||||
// Icon
|
// Icon
|
||||||
NIcon {
|
NIcon {
|
||||||
id: icon
|
id: icon
|
||||||
text: {
|
icon: (root.type == "warning") ? "exclamation-triangle" : "check-circle"
|
||||||
switch (root.type) {
|
|
||||||
case "warning":
|
|
||||||
return "warning"
|
|
||||||
case "notice":
|
|
||||||
return "info"
|
|
||||||
default:
|
|
||||||
return "info"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
color: {
|
color: {
|
||||||
switch (root.type) {
|
switch (root.type) {
|
||||||
case "warning":
|
case "warning":
|
||||||
|
|
@ -172,7 +162,7 @@ Item {
|
||||||
|
|
||||||
// Close button (only if persistent or manual dismiss needed)
|
// Close button (only if persistent or manual dismiss needed)
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "close"
|
icon: "x-lg"
|
||||||
visible: root.persistent || root.duration === 0
|
visible: root.persistent || root.duration === 0
|
||||||
|
|
||||||
colorBg: Color.mSurfaceVariant
|
colorBg: Color.mSurfaceVariant
|
||||||
|
|
|
||||||
44
flake.nix
44
flake.nix
|
|
@ -1,16 +1,13 @@
|
||||||
{
|
{
|
||||||
description = "Noctalia shell - a Wayland desktop shell built with Quickshell";
|
description = "Noctalia shell - a Wayland desktop shell built with Quickshell";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
systems.url = "github:nix-systems/default";
|
systems.url = "github:nix-systems/default";
|
||||||
|
|
||||||
quickshell = {
|
quickshell = {
|
||||||
url = "git+https://git.outfoxxed.me/outfoxxed/quickshell";
|
url = "git+https://git.outfoxxed.me/outfoxxed/quickshell";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs = {
|
||||||
self,
|
self,
|
||||||
nixpkgs,
|
nixpkgs,
|
||||||
|
|
@ -24,7 +21,6 @@
|
||||||
system:
|
system:
|
||||||
nixpkgs.legacyPackages.${system}.alejandra
|
nixpkgs.legacyPackages.${system}.alejandra
|
||||||
);
|
);
|
||||||
|
|
||||||
packages = eachSystem (
|
packages = eachSystem (
|
||||||
system: let
|
system: let
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
|
@ -32,7 +28,33 @@
|
||||||
withX11 = false;
|
withX11 = false;
|
||||||
withI3 = false;
|
withI3 = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Custom ttf-bootstrap-icons package
|
||||||
|
ttf-bootstrap-icons = pkgs.stdenvNoCC.mkDerivation rec {
|
||||||
|
pname = "ttf-bootstrap-icons";
|
||||||
|
version = "1.13.1";
|
||||||
|
|
||||||
|
src = pkgs.fetchzip {
|
||||||
|
url = "https://github.com/twbs/icons/releases/download/v${version}/bootstrap-icons-${version}.zip";
|
||||||
|
sha256 = "999021e12fab5c9ede5e4e7072eb176122be798b2f99195acf5dda47aef8fc93";
|
||||||
|
stripRoot = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
install -Dm644 fonts/bootstrap-icons.ttf $out/share/fonts/truetype/bootstrap-icons.ttf
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = with pkgs.lib; {
|
||||||
|
description = "Official open source SVG icon library for Bootstrap";
|
||||||
|
homepage = "https://icons.getbootstrap.com/";
|
||||||
|
license = licenses.mit;
|
||||||
|
platforms = platforms.all;
|
||||||
|
maintainers = [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
runtimeDeps = with pkgs; [
|
runtimeDeps = with pkgs; [
|
||||||
bash
|
bash
|
||||||
bluez
|
bluez
|
||||||
|
|
@ -49,12 +71,12 @@
|
||||||
networkmanager
|
networkmanager
|
||||||
wl-clipboard
|
wl-clipboard
|
||||||
];
|
];
|
||||||
|
|
||||||
fontconfig = pkgs.makeFontsConf {
|
fontconfig = pkgs.makeFontsConf {
|
||||||
fontDirectories = [
|
fontDirectories = [
|
||||||
pkgs.material-symbols
|
pkgs.material-symbols
|
||||||
pkgs.roboto
|
pkgs.roboto
|
||||||
pkgs.inter-nerdfont
|
pkgs.inter-nerdfont
|
||||||
|
ttf-bootstrap-icons # Add the custom font package here
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
|
|
@ -62,21 +84,17 @@
|
||||||
pname = "noctalia-shell";
|
pname = "noctalia-shell";
|
||||||
version = self.rev or self.dirtyRev or "dirty";
|
version = self.rev or self.dirtyRev or "dirty";
|
||||||
src = ./.;
|
src = ./.;
|
||||||
|
|
||||||
nativeBuildInputs = [pkgs.gcc pkgs.makeWrapper pkgs.qt6.wrapQtAppsHook];
|
nativeBuildInputs = [pkgs.gcc pkgs.makeWrapper pkgs.qt6.wrapQtAppsHook];
|
||||||
buildInputs = [qs pkgs.xkeyboard-config pkgs.qt6.qtbase];
|
buildInputs = [qs pkgs.xkeyboard-config pkgs.qt6.qtbase];
|
||||||
propagatedBuildInputs = runtimeDeps;
|
propagatedBuildInputs = runtimeDeps;
|
||||||
|
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
mkdir -p $out/share/noctalia-shell
|
mkdir -p $out/share/noctalia-shell
|
||||||
cp -r ./* $out/share/noctalia-shell
|
cp -r ./* $out/share/noctalia-shell
|
||||||
|
|
||||||
makeWrapper ${qs}/bin/qs $out/bin/noctalia-shell \
|
makeWrapper ${qs}/bin/qs $out/bin/noctalia-shell \
|
||||||
--prefix PATH : "${pkgs.lib.makeBinPath runtimeDeps}" \
|
--prefix PATH : "${pkgs.lib.makeBinPath runtimeDeps}" \
|
||||||
--set FONTCONFIG_FILE "${fontconfig}" \
|
--set FONTCONFIG_FILE "${fontconfig}" \
|
||||||
--add-flags "-p $out/share/noctalia-shell"
|
--add-flags "-p $out/share/noctalia-shell"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
description = "A sleek and minimal desktop shell thoughtfully crafted for Wayland, built with Quickshell.";
|
description = "A sleek and minimal desktop shell thoughtfully crafted for Wayland, built with Quickshell.";
|
||||||
homepage = "https://github.com/noctalia-dev/noctalia-shell";
|
homepage = "https://github.com/noctalia-dev/noctalia-shell";
|
||||||
|
|
@ -84,9 +102,11 @@
|
||||||
mainProgram = "noctalia-shell";
|
mainProgram = "noctalia-shell";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Expose the custom font as a separate package (optional)
|
||||||
|
ttf-bootstrap-icons = ttf-bootstrap-icons;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
defaultPackage = eachSystem (system: self.packages.${system}.default);
|
defaultPackage = eachSystem (system: self.packages.${system}.default);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue