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": {
|
||||
"mPrimary": "#ebbcba",
|
||||
"mOnPrimary": "#1f1d2e",
|
||||
"mOnPrimary": "#191724",
|
||||
"mSecondary": "#9ccfd8",
|
||||
"mOnSecondary": "#1f1d2e",
|
||||
"mOnSecondary": "#191724",
|
||||
"mTertiary": "#f6c177",
|
||||
"mOnTertiary": "#1f1d2e",
|
||||
"mOnTertiary": "#191724",
|
||||
"mError": "#eb6f92",
|
||||
"mOnError": "#1f1d2e",
|
||||
"mSurface": "#1f1d2e",
|
||||
"mOnError": "#191724",
|
||||
"mSurface": "#191724",
|
||||
"mOnSurface": "#e0def4",
|
||||
"mSurfaceVariant": "#26233a",
|
||||
"mOnSurfaceVariant": "#908caa",
|
||||
"mOutline": "#403d52",
|
||||
"mShadow": "#1f1d2e"
|
||||
"mShadow": "#191724"
|
||||
},
|
||||
"light": {
|
||||
"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
|
||||
|
||||
NIcon {
|
||||
text: "system_update_alt"
|
||||
icon: "box"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: Color.mPrimary
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ NPanel {
|
|||
// Reset button (only show if update failed)
|
||||
NIconButton {
|
||||
visible: ArchUpdaterService.updateFailed
|
||||
icon: "refresh"
|
||||
icon: "arrow-repeat"
|
||||
tooltipText: "Reset update state"
|
||||
sizeRatio: 0.8
|
||||
colorBg: Color.mError
|
||||
|
|
@ -55,7 +55,7 @@ NPanel {
|
|||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
icon: "x-lg"
|
||||
tooltipText: "Close"
|
||||
sizeRatio: 0.8
|
||||
onClicked: root.close()
|
||||
|
|
@ -103,7 +103,7 @@ NPanel {
|
|||
} // Spacer
|
||||
|
||||
NIcon {
|
||||
text: "hourglass_empty"
|
||||
icon: "hourglass"
|
||||
font.pointSize: Style.fontSizeXXXL * scaling
|
||||
color: Color.mPrimary
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
|
@ -143,7 +143,7 @@ NPanel {
|
|||
spacing: Style.marginM * scaling
|
||||
|
||||
NIcon {
|
||||
text: "terminal"
|
||||
icon: "terminal"
|
||||
font.pointSize: Style.fontSizeXXXL * scaling
|
||||
color: Color.mError
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
|
@ -181,7 +181,7 @@ NPanel {
|
|||
spacing: Style.marginM * scaling
|
||||
|
||||
NIcon {
|
||||
text: "package"
|
||||
icon: "box"
|
||||
font.pointSize: Style.fontSizeXXXL * scaling
|
||||
color: Color.mError
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
|
@ -219,7 +219,7 @@ NPanel {
|
|||
spacing: Style.marginM * scaling
|
||||
|
||||
NIcon {
|
||||
text: "error"
|
||||
icon: "exclamation-triangle"
|
||||
font.pointSize: Style.fontSizeXXXL * scaling
|
||||
color: Color.mError
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
|
@ -245,7 +245,7 @@ NPanel {
|
|||
|
||||
// Prominent refresh button
|
||||
NIconButton {
|
||||
icon: "refresh"
|
||||
icon: "arrow-repeat"
|
||||
tooltipText: "Try checking again"
|
||||
sizeRatio: 1.2
|
||||
colorBg: Color.mPrimary
|
||||
|
|
@ -270,7 +270,7 @@ NPanel {
|
|||
spacing: Style.marginM * scaling
|
||||
|
||||
NIcon {
|
||||
text: "error_outline"
|
||||
icon: "exclamation-triangle"
|
||||
font.pointSize: Style.fontSizeXXXL * scaling
|
||||
color: Color.mError
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
|
@ -295,7 +295,7 @@ NPanel {
|
|||
|
||||
// Prominent refresh button
|
||||
NIconButton {
|
||||
icon: "refresh"
|
||||
icon: "arrow-repeat"
|
||||
tooltipText: "Refresh and try again"
|
||||
sizeRatio: 1.2
|
||||
colorBg: Color.mPrimary
|
||||
|
|
@ -323,7 +323,7 @@ NPanel {
|
|||
spacing: Style.marginM * scaling
|
||||
|
||||
NIcon {
|
||||
text: "check_circle"
|
||||
icon: "check-lg"
|
||||
font.pointSize: Style.fontSizeXXXL * scaling
|
||||
color: Color.mPrimary
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
|
@ -483,7 +483,7 @@ NPanel {
|
|||
spacing: Style.marginL * scaling
|
||||
|
||||
NIconButton {
|
||||
icon: "refresh"
|
||||
icon: "arrow-repeat"
|
||||
tooltipText: ArchUpdaterService.aurBusy ? "Checking for updates..." : (!ArchUpdaterService.canPoll ? "Refresh available soon" : "Refresh package lists")
|
||||
onClicked: {
|
||||
ArchUpdaterService.forceRefresh()
|
||||
|
|
@ -495,7 +495,7 @@ NPanel {
|
|||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "system_update_alt"
|
||||
icon: "box-fill"
|
||||
tooltipText: "Update all packages"
|
||||
enabled: ArchUpdaterService.totalUpdates > 0
|
||||
onClicked: {
|
||||
|
|
@ -508,7 +508,7 @@ NPanel {
|
|||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "check_box"
|
||||
icon: "box"
|
||||
tooltipText: "Update selected packages"
|
||||
enabled: ArchUpdaterService.selectedPackagesCount > 0
|
||||
onClicked: {
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ Variants {
|
|||
property real screenWidth: width
|
||||
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
|
||||
|
|
@ -164,7 +164,7 @@ Variants {
|
|||
property real screenWidth: width
|
||||
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
|
||||
|
|
@ -191,7 +191,7 @@ Variants {
|
|||
property real screenWidth: width
|
||||
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
|
||||
|
|
@ -218,7 +218,7 @@ Variants {
|
|||
property real screenWidth: width
|
||||
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
|
||||
|
|
|
|||
|
|
@ -33,28 +33,34 @@ RowLayout {
|
|||
|
||||
readonly property bool showIcon: (widgetSettings.showIcon !== undefined) ? widgetSettings.showIcon : widgetMetadata.showIcon
|
||||
|
||||
readonly property real minWidth: 160
|
||||
readonly property real maxWidth: 400
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: Style.marginS * scaling
|
||||
visible: getTitle() !== ""
|
||||
// 6% of total width
|
||||
readonly property real minWidth: Math.max(1, screen.width * 0.06)
|
||||
readonly property real maxWidth: minWidth * 2
|
||||
|
||||
function getTitle() {
|
||||
return CompositorService.focusedWindowTitle !== "(No active window)" ? CompositorService.focusedWindowTitle : ""
|
||||
}
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: Style.marginS * scaling
|
||||
visible: getTitle() !== ""
|
||||
|
||||
function getAppIcon() {
|
||||
// Try CompositorService first
|
||||
const focusedWindow = CompositorService.getFocusedWindow()
|
||||
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
|
||||
if (ToplevelManager && ToplevelManager.activeToplevel) {
|
||||
const activeToplevel = ToplevelManager.activeToplevel
|
||||
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
|
||||
elide: mouseArea.containsMouse ? Text.ElideNone : Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: Color.mSecondary
|
||||
color: Color.mPrimary
|
||||
clip: true
|
||||
|
||||
Behavior on Layout.preferredWidth {
|
||||
|
|
|
|||
|
|
@ -29,15 +29,15 @@ NIconButton {
|
|||
return "terminal"
|
||||
}
|
||||
if (!ArchUpdaterService.aurHelperAvailable) {
|
||||
return "package"
|
||||
return "box"
|
||||
}
|
||||
if (ArchUpdaterService.aurBusy) {
|
||||
return "sync"
|
||||
return "arrow-repeat"
|
||||
}
|
||||
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
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ Item {
|
|||
// Test mode
|
||||
readonly property bool testMode: false
|
||||
readonly property int testPercent: 50
|
||||
readonly property bool testCharging: true
|
||||
readonly property bool testCharging: false
|
||||
|
||||
// Main properties
|
||||
readonly property var battery: UPower.displayDevice
|
||||
|
|
@ -57,9 +57,7 @@ Item {
|
|||
// Only notify once we are a below threshold
|
||||
if (!charging && !root.hasNotifiedLowBattery && percent <= warningThreshold) {
|
||||
root.hasNotifiedLowBattery = true
|
||||
// Maybe go with toast ?
|
||||
Quickshell.execDetached(
|
||||
["notify-send", "-u", "critical", "-i", "battery-caution", "Low Battery", `Battery is at ${p}%. Please connect charger.`])
|
||||
ToastService.showWarning("Low Battery", `Battery is at ${Math.round(percent)}%. Please connect the charger.`)
|
||||
} else if (root.hasNotifiedLowBattery && (charging || percent > warningThreshold + 5)) {
|
||||
// Reset when charging starts or when battery recovers 5% above threshold
|
||||
root.hasNotifiedLowBattery = false
|
||||
|
|
@ -87,11 +85,7 @@ Item {
|
|||
rightOpen: BarWidgetRegistry.getNPillDirection(root)
|
||||
icon: testMode ? BatteryService.getIcon(testPercent, testCharging, true) : BatteryService.getIcon(percent,
|
||||
charging, isReady)
|
||||
iconRotation: -90
|
||||
text: ((isReady && battery.isLaptopBattery) || testMode) ? Math.round(percent) + "%" : "-"
|
||||
textColor: charging ? Color.mPrimary : Color.mOnSurface
|
||||
iconCircleColor: Color.mPrimary
|
||||
collapsedIconColor: Color.mOnSurface
|
||||
text: (isReady || testMode) ? Math.round(percent) + "%" : "-"
|
||||
autoHide: false
|
||||
forceOpen: isReady && (testMode || battery.isLaptopBattery) && alwaysShowPercentage
|
||||
disableOpen: (!isReady || (!testMode && !battery.isLaptopBattery))
|
||||
|
|
|
|||
|
|
@ -46,8 +46,7 @@ Item {
|
|||
function getIcon() {
|
||||
var monitor = getMonitor()
|
||||
var brightness = monitor ? monitor.brightness : 0
|
||||
return brightness <= 0 ? "brightness_1" : brightness < 0.33 ? "brightness_low" : brightness
|
||||
< 0.66 ? "brightness_medium" : "brightness_high"
|
||||
return brightness <= 0.5 ? "brightness-low" : "brightness-high"
|
||||
}
|
||||
|
||||
// Connection used to open the pill when brightness changes
|
||||
|
|
@ -80,8 +79,6 @@ Item {
|
|||
|
||||
rightOpen: BarWidgetRegistry.getNPillDirection(root)
|
||||
icon: getIcon()
|
||||
iconCircleColor: Color.mPrimary
|
||||
collapsedIconColor: Color.mOnSurface
|
||||
autoHide: false // Important to be false so we can hover as long as we want
|
||||
text: {
|
||||
var monitor = getMonitor()
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ Rectangle {
|
|||
anchors.centerIn: parent
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mPrimary
|
||||
}
|
||||
|
||||
NTooltip {
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@ NIconButton {
|
|||
property ShellScreen screen
|
||||
property real scaling: 1.0
|
||||
|
||||
icon: "contrast"
|
||||
icon: "transparency"
|
||||
tooltipText: "Toggle light/dark mode"
|
||||
sizeRatio: 0.8
|
||||
|
||||
colorBg: Color.mSurfaceVariant
|
||||
colorFg: Color.mOnSurface
|
||||
colorBg: Settings.data.colorSchemes.darkMode ? Color.mSurfaceVariant : Color.mPrimary
|
||||
colorFg: Settings.data.colorSchemes.darkMode ? Color.mOnSurface : Color.mOnPrimary
|
||||
colorBorder: Color.transparent
|
||||
colorBorderHover: Color.transparent
|
||||
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ NIconButton {
|
|||
|
||||
sizeRatio: 0.8
|
||||
|
||||
icon: "coffee"
|
||||
icon: "cup"
|
||||
tooltipText: IdleInhibitorService.isInhibited ? "Disable keep awake" : "Enable keep awake"
|
||||
colorBg: Color.mSurfaceVariant
|
||||
colorFg: IdleInhibitorService.isInhibited ? Color.mPrimary : Color.mOnSurface
|
||||
colorBg: IdleInhibitorService.isInhibited ? Color.mPrimary : Color.mSurfaceVariant
|
||||
colorFg: IdleInhibitorService.isInhibited ? Color.mOnPrimary : Color.mOnSurface
|
||||
colorBorder: Color.transparent
|
||||
onClicked: {
|
||||
IdleInhibitorService.manualToggle()
|
||||
|
|
|
|||
|
|
@ -24,9 +24,7 @@ Item {
|
|||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
rightOpen: BarWidgetRegistry.getNPillDirection(root)
|
||||
icon: "keyboard_alt"
|
||||
iconCircleColor: Color.mPrimary
|
||||
collapsedIconColor: Color.mOnSurface
|
||||
icon: "keyboard"
|
||||
autoHide: false // Important to be false so we can hover as long as we want
|
||||
text: currentLayout
|
||||
tooltipText: "Keyboard layout: " + currentLayout
|
||||
|
|
|
|||
|
|
@ -38,8 +38,9 @@ RowLayout {
|
|||
readonly property string visualizerType: (widgetSettings.visualizerType !== undefined && widgetSettings.visualizerType
|
||||
!== "") ? widgetSettings.visualizerType : widgetMetadata.visualizerType
|
||||
|
||||
readonly property real minWidth: 160
|
||||
readonly property real maxWidth: 400
|
||||
// 6% of total width
|
||||
readonly property real minWidth: Math.max(1, screen.width * 0.06)
|
||||
readonly property real maxWidth: minWidth * 2
|
||||
|
||||
function getTitle() {
|
||||
return MediaService.trackTitle + (MediaService.trackArtist !== "" ? ` - ${MediaService.trackArtist}` : "")
|
||||
|
|
@ -134,7 +135,7 @@ RowLayout {
|
|||
|
||||
NIcon {
|
||||
id: windowIcon
|
||||
text: MediaService.isPlaying ? "pause" : "play_arrow"
|
||||
text: MediaService.isPlaying ? "pause" : "play"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
|
@ -154,7 +155,8 @@ RowLayout {
|
|||
id: trackArt
|
||||
anchors.fill: parent
|
||||
imagePath: MediaService.trackArtUrl
|
||||
fallbackIcon: MediaService.isPlaying ? "pause" : "play_arrow"
|
||||
fallbackIcon: MediaService.isPlaying ? "pause" : "play"
|
||||
fallbackIconSize: 10 * scaling
|
||||
borderWidth: 0
|
||||
border.color: Color.transparent
|
||||
}
|
||||
|
|
@ -178,7 +180,7 @@ RowLayout {
|
|||
font.weight: Style.fontWeightMedium
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: Color.mTertiary
|
||||
color: Color.mSecondary
|
||||
|
||||
Behavior on Layout.preferredWidth {
|
||||
NumberAnimation {
|
||||
|
|
|
|||
|
|
@ -43,9 +43,9 @@ Item {
|
|||
|
||||
function getIcon() {
|
||||
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
|
||||
|
|
@ -92,8 +92,6 @@ Item {
|
|||
|
||||
rightOpen: BarWidgetRegistry.getNPillDirection(root)
|
||||
icon: getIcon()
|
||||
iconCircleColor: Color.mPrimary
|
||||
collapsedIconColor: Color.mOnSurface
|
||||
autoHide: false // Important to be false so we can hover as long as we want
|
||||
text: Math.floor(AudioService.inputVolume * 100) + "%"
|
||||
forceOpen: alwaysShowPercentage
|
||||
|
|
|
|||
|
|
@ -15,12 +15,12 @@ NIconButton {
|
|||
property real scaling: 1.0
|
||||
|
||||
sizeRatio: 0.8
|
||||
colorBg: Color.mSurfaceVariant
|
||||
colorFg: Color.mOnSurface
|
||||
colorBg: Settings.data.nightLight.enabled ? Color.mPrimary : Color.mSurfaceVariant
|
||||
colorFg: Settings.data.nightLight.enabled ? Color.mOnPrimary : Color.mOnSurface
|
||||
colorBorder: 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.`
|
||||
onClicked: Settings.data.nightLight.enabled = !Settings.data.nightLight.enabled
|
||||
|
||||
|
|
|
|||
|
|
@ -53,10 +53,10 @@ NIconButton {
|
|||
}
|
||||
|
||||
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'."
|
||||
colorBg: Color.mSurfaceVariant
|
||||
colorFg: Settings.data.notifications.doNotDisturb ? Color.mError : Color.mOnSurface
|
||||
colorFg: Color.mOnSurface
|
||||
colorBorder: Color.transparent
|
||||
colorBorderHover: Color.transparent
|
||||
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ NIconButton {
|
|||
|
||||
function profileIcon() {
|
||||
if (!hasPP)
|
||||
return "balance"
|
||||
return "yin-yang"
|
||||
if (powerProfiles.profile === PowerProfile.Performance)
|
||||
return "speed"
|
||||
return "speedometer2"
|
||||
if (powerProfiles.profile === PowerProfile.Balanced)
|
||||
return "balance"
|
||||
return "yin-yang"
|
||||
if (powerProfiles.profile === PowerProfile.PowerSaver)
|
||||
return "eco"
|
||||
return "leaf"
|
||||
}
|
||||
|
||||
function profileName() {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ NIconButton {
|
|||
|
||||
sizeRatio: 0.8
|
||||
|
||||
icon: "power_settings_new"
|
||||
icon: "power"
|
||||
tooltipText: "Power Settings"
|
||||
colorBg: Color.mSurfaceVariant
|
||||
colorFg: Color.mError
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ NIconButton {
|
|||
property real scaling: 1.0
|
||||
|
||||
visible: ScreenRecorderService.isRecording
|
||||
icon: "videocam"
|
||||
icon: "camera-video"
|
||||
tooltipText: "Screen recording is active\nClick to stop recording"
|
||||
sizeRatio: 0.8
|
||||
colorBg: Color.mPrimary
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ NIconButton {
|
|||
readonly property bool useDistroLogo: (widgetSettings.useDistroLogo
|
||||
!== undefined) ? widgetSettings.useDistroLogo : widgetMetadata.useDistroLogo
|
||||
|
||||
icon: useDistroLogo ? "" : "widgets"
|
||||
icon: useDistroLogo ? "" : "layout-sidebar-inset-reverse"
|
||||
tooltipText: "Open side panel."
|
||||
sizeRatio: 0.8
|
||||
|
||||
|
|
|
|||
|
|
@ -38,12 +38,4 @@ Item {
|
|||
implicitHeight: Style.barHeight * scaling
|
||||
width: implicitWidth
|
||||
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
|
||||
readonly property bool showNetworkStats: (widgetSettings.showNetworkStats
|
||||
!== undefined) ? widgetSettings.showNetworkStats : widgetMetadata.showNetworkStats
|
||||
readonly property bool showDiskUsage: (widgetSettings.showDiskUsage
|
||||
!== undefined) ? widgetSettings.showDiskUsage : widgetMetadata.showDiskUsage
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: Style.marginS * scaling
|
||||
|
|
@ -52,126 +54,188 @@ RowLayout {
|
|||
|
||||
RowLayout {
|
||||
id: mainLayout
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Style.marginS * scaling
|
||||
anchors.rightMargin: Style.marginS * scaling
|
||||
anchors.centerIn: parent // Better centering than margins
|
||||
width: parent.width - Style.marginM * scaling * 2
|
||||
spacing: Style.marginS * scaling
|
||||
|
||||
// CPU Usage Component
|
||||
RowLayout {
|
||||
id: cpuUsageLayout
|
||||
spacing: Style.marginXS * scaling
|
||||
Item {
|
||||
Layout.preferredWidth: cpuUsageRow.implicitWidth
|
||||
Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
visible: showCpuUsage
|
||||
|
||||
NIcon {
|
||||
id: cpuUsageIcon
|
||||
text: "speed"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
RowLayout {
|
||||
id: cpuUsageRow
|
||||
anchors.centerIn: parent
|
||||
spacing: Style.marginXS * scaling
|
||||
|
||||
NText {
|
||||
id: cpuUsageText
|
||||
text: `${SystemStatService.cpuUsage}%`
|
||||
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
|
||||
NIcon {
|
||||
icon: "speedometer2"
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
text: `${SystemStatService.cpuUsage}%`
|
||||
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
|
||||
RowLayout {
|
||||
id: cpuTempLayout
|
||||
// spacing is thin here to compensate for the vertical thermometer icon
|
||||
spacing: Style.marginXXS * scaling
|
||||
Item {
|
||||
Layout.preferredWidth: cpuTempRow.implicitWidth
|
||||
Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
visible: showCpuTemp
|
||||
|
||||
NIcon {
|
||||
text: "thermometer"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
RowLayout {
|
||||
id: cpuTempRow
|
||||
anchors.centerIn: parent
|
||||
spacing: Style.marginXS * scaling
|
||||
|
||||
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
|
||||
NIcon {
|
||||
icon: "fire"
|
||||
// Fire is so tall, we need to make it smaller
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
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
|
||||
RowLayout {
|
||||
id: memoryUsageLayout
|
||||
spacing: Style.marginXS * scaling
|
||||
Item {
|
||||
Layout.preferredWidth: memoryUsageRow.implicitWidth
|
||||
Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
visible: showMemoryUsage
|
||||
|
||||
NIcon {
|
||||
text: "memory"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
RowLayout {
|
||||
id: memoryUsageRow
|
||||
anchors.centerIn: parent
|
||||
spacing: Style.marginXS * scaling
|
||||
|
||||
NText {
|
||||
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
|
||||
NIcon {
|
||||
icon: "cpu"
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
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
|
||||
RowLayout {
|
||||
id: networkDownloadLayout
|
||||
spacing: Style.marginXS * scaling
|
||||
Item {
|
||||
Layout.preferredWidth: networkDownloadRow.implicitWidth
|
||||
Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
visible: showNetworkStats
|
||||
|
||||
NIcon {
|
||||
text: "download"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
RowLayout {
|
||||
id: networkDownloadRow
|
||||
anchors.centerIn: parent
|
||||
spacing: Style.marginXS * scaling
|
||||
|
||||
NText {
|
||||
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
|
||||
NIcon {
|
||||
icon: "cloud-arrow-down"
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
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
|
||||
RowLayout {
|
||||
id: networkUploadLayout
|
||||
spacing: Style.marginXS * scaling
|
||||
Item {
|
||||
Layout.preferredWidth: networkUploadRow.implicitWidth
|
||||
Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
visible: showNetworkStats
|
||||
|
||||
NIcon {
|
||||
text: "upload"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
RowLayout {
|
||||
id: networkUploadRow
|
||||
anchors.centerIn: parent
|
||||
spacing: Style.marginXS * scaling
|
||||
|
||||
NText {
|
||||
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
|
||||
NIcon {
|
||||
icon: "cloud-arrow-up"
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
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() {
|
||||
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
|
||||
|
|
@ -77,8 +77,6 @@ Item {
|
|||
|
||||
rightOpen: BarWidgetRegistry.getNPillDirection(root)
|
||||
icon: getIcon()
|
||||
iconCircleColor: Color.mPrimary
|
||||
collapsedIconColor: Color.mOnSurface
|
||||
autoHide: false // Important to be false so we can hover as long as we want
|
||||
text: Math.floor(AudioService.volume * 100) + "%"
|
||||
forceOpen: alwaysShowPercentage
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ NIconButton {
|
|||
icon: {
|
||||
try {
|
||||
if (NetworkService.ethernetConnected) {
|
||||
return "lan"
|
||||
return "ethernet"
|
||||
}
|
||||
let connected = false
|
||||
let signalStrength = 0
|
||||
|
|
@ -34,7 +34,7 @@ NIconButton {
|
|||
break
|
||||
}
|
||||
}
|
||||
return connected ? NetworkService.signalIcon(signalStrength) : "wifi_find"
|
||||
return connected ? NetworkService.signalIcon(signalStrength) : "wifi-off"
|
||||
} catch (error) {
|
||||
Logger.error("Wi-Fi", "Error getting icon:", error)
|
||||
return "signal_wifi_bad"
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ ColumnLayout {
|
|||
|
||||
// One device BT icon
|
||||
NIcon {
|
||||
text: BluetoothService.getDeviceIcon(modelData)
|
||||
icon: BluetoothService.getDeviceIcon(modelData)
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: getContentColor(Color.mOnSurface)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
|
@ -164,7 +164,7 @@ ColumnLayout {
|
|||
}
|
||||
return "Connect"
|
||||
}
|
||||
icon: (isBusy ? "hourglass_full" : null)
|
||||
icon: (isBusy ? "hourglass-split" : null)
|
||||
onClicked: {
|
||||
if (modelData.connected) {
|
||||
BluetoothService.disconnectDevice(modelData)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ NPanel {
|
|||
spacing: Style.marginM * scaling
|
||||
|
||||
NIcon {
|
||||
text: "bluetooth"
|
||||
icon: "bluetooth"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: Color.mPrimary
|
||||
}
|
||||
|
|
@ -42,7 +42,7 @@ NPanel {
|
|||
}
|
||||
|
||||
NIconButton {
|
||||
icon: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop_circle" : "refresh"
|
||||
icon: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop" : "arrow-repeat"
|
||||
tooltipText: "Refresh Devices"
|
||||
sizeRatio: 0.8
|
||||
onClicked: {
|
||||
|
|
@ -53,7 +53,7 @@ NPanel {
|
|||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
icon: "x-lg"
|
||||
tooltipText: "Close"
|
||||
sizeRatio: 0.8
|
||||
onClicked: {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@ Item {
|
|||
IpcHandler {
|
||||
target: "screenRecorder"
|
||||
function toggle() {
|
||||
ScreenRecorderService.toggleRecording()
|
||||
if (ScreenRecorderService.isAvailable) {
|
||||
ScreenRecorderService.toggleRecording()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -418,7 +418,7 @@ Loader {
|
|||
font.weight: Style.fontWeightBold
|
||||
}
|
||||
NIcon {
|
||||
text: "keyboard_alt"
|
||||
icon: "keyboard"
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
|
|
@ -428,7 +428,7 @@ Loader {
|
|||
spacing: Style.marginS * scaling
|
||||
visible: batteryIndicator.batteryVisible
|
||||
NIcon {
|
||||
text: BatteryService.getIcon(batteryIndicator.percent, batteryIndicator.charging,
|
||||
icon: BatteryService.getIcon(batteryIndicator.percent, batteryIndicator.charging,
|
||||
batteryIndicator.isReady)
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
color: batteryIndicator.charging ? Color.mPrimary : Color.mOnSurface
|
||||
|
|
@ -718,21 +718,47 @@ Loader {
|
|||
anchors.margins: 50 * scaling
|
||||
spacing: 20 * scaling
|
||||
|
||||
// Shutdown
|
||||
Rectangle {
|
||||
Layout.preferredWidth: 60 * scaling
|
||||
Layout.preferredHeight: 60 * scaling
|
||||
Layout.preferredWidth: iconPower.implicitWidth + Style.marginXL * scaling
|
||||
Layout.preferredHeight: Layout.preferredWidth
|
||||
radius: width * 0.5
|
||||
color: powerButtonArea.containsMouse ? Color.mError : Qt.alpha(Color.mError, 0.2)
|
||||
border.color: Color.mError
|
||||
border.width: Math.max(1, Style.borderM * scaling)
|
||||
|
||||
NIcon {
|
||||
id: iconPower
|
||||
anchors.centerIn: parent
|
||||
text: "power_settings_new"
|
||||
font.pointSize: Style.fontSizeXL * scaling
|
||||
icon: "power"
|
||||
font.pointSize: Style.fontSizeXXXL * scaling
|
||||
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 {
|
||||
id: powerButtonArea
|
||||
anchors.fill: parent
|
||||
|
|
@ -743,21 +769,47 @@ Loader {
|
|||
}
|
||||
}
|
||||
|
||||
// Reboot
|
||||
Rectangle {
|
||||
Layout.preferredWidth: 60 * scaling
|
||||
Layout.preferredHeight: 60 * scaling
|
||||
Layout.preferredWidth: iconReboot.implicitWidth + Style.marginXL * scaling
|
||||
Layout.preferredHeight: Layout.preferredWidth
|
||||
radius: width * 0.5
|
||||
color: restartButtonArea.containsMouse ? Color.mPrimary : Qt.alpha(Color.mPrimary, Style.opacityLight)
|
||||
border.color: Color.mPrimary
|
||||
border.width: Math.max(1, Style.borderM * scaling)
|
||||
|
||||
NIcon {
|
||||
id: iconReboot
|
||||
anchors.centerIn: parent
|
||||
text: "restart_alt"
|
||||
font.pointSize: Style.fontSizeXL * scaling
|
||||
icon: "arrow-repeat"
|
||||
font.pointSize: Style.fontSizeXXXL * scaling
|
||||
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 {
|
||||
id: restartButtonArea
|
||||
anchors.fill: parent
|
||||
|
|
@ -765,24 +817,51 @@ Loader {
|
|||
onClicked: {
|
||||
CompositorService.reboot()
|
||||
}
|
||||
// Tooltip handled via inline rectangle visibility
|
||||
}
|
||||
}
|
||||
|
||||
// Suspend
|
||||
Rectangle {
|
||||
Layout.preferredWidth: 60 * scaling
|
||||
Layout.preferredHeight: 60 * scaling
|
||||
Layout.preferredWidth: iconSuspend.implicitWidth + Style.marginXL * scaling
|
||||
Layout.preferredHeight: Layout.preferredWidth
|
||||
radius: width * 0.5
|
||||
color: suspendButtonArea.containsMouse ? Color.mSecondary : Qt.alpha(Color.mSecondary, 0.2)
|
||||
border.color: Color.mSecondary
|
||||
border.width: Math.max(1, Style.borderM * scaling)
|
||||
|
||||
NIcon {
|
||||
id: iconSuspend
|
||||
anchors.centerIn: parent
|
||||
text: "bedtime"
|
||||
font.pointSize: Style.fontSizeXL * scaling
|
||||
icon: "pause-fill"
|
||||
font.pointSize: Style.fontSizeXXXL * scaling
|
||||
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 {
|
||||
id: suspendButtonArea
|
||||
anchors.fill: parent
|
||||
|
|
@ -790,6 +869,7 @@ Loader {
|
|||
onClicked: {
|
||||
CompositorService.suspend()
|
||||
}
|
||||
// Tooltip handled via inline rectangle visibility
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -205,14 +205,12 @@ Variants {
|
|||
Layout.fillWidth: true
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
// Avatar
|
||||
// Image
|
||||
NImageCircled {
|
||||
id: appAvatar
|
||||
Layout.preferredWidth: 40 * scaling
|
||||
Layout.preferredHeight: 40 * scaling
|
||||
Layout.alignment: Qt.AlignTop
|
||||
imagePath: model.image && model.image !== "" ? model.image : ""
|
||||
fallbackIcon: ""
|
||||
borderColor: Color.transparent
|
||||
borderWidth: 0
|
||||
visible: (model.image && model.image !== "")
|
||||
|
|
@ -294,7 +292,7 @@ Variants {
|
|||
|
||||
// Close button positioned absolutely
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
icon: "x-lg"
|
||||
tooltipText: "Close"
|
||||
sizeRatio: 0.6
|
||||
anchors.top: parent.top
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ NPanel {
|
|||
spacing: Style.marginM * scaling
|
||||
|
||||
NIcon {
|
||||
text: "notifications"
|
||||
icon: "bell"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: Color.mPrimary
|
||||
}
|
||||
|
|
@ -45,21 +45,21 @@ NPanel {
|
|||
}
|
||||
|
||||
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."
|
||||
sizeRatio: 0.8
|
||||
onClicked: Settings.data.notifications.doNotDisturb = !Settings.data.notifications.doNotDisturb
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "delete"
|
||||
icon: "trash"
|
||||
tooltipText: "Clear history"
|
||||
sizeRatio: 0.8
|
||||
onClicked: NotificationService.clearHistory()
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
icon: "x-lg"
|
||||
tooltipText: "Close"
|
||||
sizeRatio: 0.8
|
||||
onClicked: {
|
||||
|
|
@ -85,7 +85,7 @@ NPanel {
|
|||
}
|
||||
|
||||
NIcon {
|
||||
text: "notifications_off"
|
||||
icon: "bell-slash"
|
||||
font.pointSize: 64 * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
|
@ -175,7 +175,7 @@ NPanel {
|
|||
|
||||
// Delete button
|
||||
NIconButton {
|
||||
icon: "delete"
|
||||
icon: "trash"
|
||||
tooltipText: "Delete notification"
|
||||
sizeRatio: 0.7
|
||||
Layout.alignment: Qt.AlignTop
|
||||
|
|
|
|||
|
|
@ -29,27 +29,27 @@ NPanel {
|
|||
property int selectedIndex: 0
|
||||
readonly property var powerOptions: [{
|
||||
"action": "lock",
|
||||
"icon": "lock_outline",
|
||||
"icon": "lock",
|
||||
"title": "Lock",
|
||||
"subtitle": "Lock your session"
|
||||
}, {
|
||||
"action": "suspend",
|
||||
"icon": "bedtime",
|
||||
"icon": "pause-circle",
|
||||
"title": "Suspend",
|
||||
"subtitle": "Put the system to sleep"
|
||||
}, {
|
||||
"action": "reboot",
|
||||
"icon": "refresh",
|
||||
"icon": "arrow-repeat",
|
||||
"title": "Reboot",
|
||||
"subtitle": "Restart the system"
|
||||
}, {
|
||||
"action": "logout",
|
||||
"icon": "exit_to_app",
|
||||
"icon": "escape",
|
||||
"title": "Logout",
|
||||
"subtitle": "End your session"
|
||||
}, {
|
||||
"action": "shutdown",
|
||||
"icon": "power_settings_new",
|
||||
"icon": "power",
|
||||
"title": "Shutdown",
|
||||
"subtitle": "Turn off the system",
|
||||
"isShutdown": true
|
||||
|
|
@ -276,7 +276,7 @@ NPanel {
|
|||
}
|
||||
|
||||
NIconButton {
|
||||
icon: timerActive ? "back_hand" : "close"
|
||||
icon: timerActive ? "x-square" : "x-lg"
|
||||
tooltipText: timerActive ? "Cancel Timer" : "Close"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
colorBg: timerActive ? Qt.alpha(Color.mError, 0.08) : Color.transparent
|
||||
|
|
@ -360,7 +360,7 @@ NPanel {
|
|||
id: iconElement
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: buttonRoot.icon
|
||||
icon: buttonRoot.icon
|
||||
color: {
|
||||
if (buttonRoot.pending)
|
||||
return Color.mPrimary
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ NBox {
|
|||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "add"
|
||||
icon: "plus-lg"
|
||||
|
||||
colorBg: Color.mPrimary
|
||||
colorFg: Color.mOnPrimary
|
||||
|
|
@ -170,7 +170,7 @@ NBox {
|
|||
Loader {
|
||||
active: BarWidgetRegistry.widgetHasUserSettings(modelData.id)
|
||||
sourceComponent: NIconButton {
|
||||
icon: "settings"
|
||||
icon: "gear"
|
||||
sizeRatio: 0.6
|
||||
colorBorder: Qt.alpha(Color.mOutline, Style.opacityLight)
|
||||
colorBg: Color.mOnSurface
|
||||
|
|
@ -210,7 +210,7 @@ NBox {
|
|||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
icon: "x-lg"
|
||||
sizeRatio: 0.6
|
||||
colorBorder: Qt.alpha(Color.mOutline, Style.opacityLight)
|
||||
colorBg: Color.mOnSurface
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ Popup {
|
|||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
icon: "x-lg"
|
||||
onClicked: settingsPopup.close()
|
||||
}
|
||||
}
|
||||
|
|
@ -107,6 +107,7 @@ Popup {
|
|||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginM * scaling
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
|
|
@ -120,7 +121,7 @@ Popup {
|
|||
|
||||
NButton {
|
||||
text: "Apply"
|
||||
icon: "check"
|
||||
icon: "check-lg"
|
||||
onClicked: {
|
||||
if (settingsLoader.item && settingsLoader.item.saveSettings) {
|
||||
var newSettings = settingsLoader.item.saveSettings()
|
||||
|
|
|
|||
|
|
@ -16,10 +16,13 @@ ColumnLayout {
|
|||
// Local state
|
||||
property bool valueAlwaysShowPercentage: widgetData.alwaysShowPercentage
|
||||
!== undefined ? widgetData.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
|
||||
property int valueWarningThreshold: widgetData.warningThreshold
|
||||
!== undefined ? widgetData.warningThreshold : widgetMetadata.warningThreshold
|
||||
|
||||
function saveSettings() {
|
||||
var settings = Object.assign({}, widgetData || {})
|
||||
settings.alwaysShowPercentage = valueAlwaysShowPercentage
|
||||
settings.warningThreshold = valueWarningThreshold
|
||||
return settings
|
||||
}
|
||||
|
||||
|
|
@ -28,4 +31,14 @@ ColumnLayout {
|
|||
checked: root.valueAlwaysShowPercentage
|
||||
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.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
import qs.Commons
|
||||
import qs.Widgets
|
||||
import qs.Services
|
||||
|
|
@ -9,7 +10,6 @@ ColumnLayout {
|
|||
id: root
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
// Properties to receive data from parent
|
||||
property var widgetData: null
|
||||
property var widgetMetadata: null
|
||||
|
||||
|
|
@ -22,16 +22,190 @@ ColumnLayout {
|
|||
return settings
|
||||
}
|
||||
|
||||
// Icon setting
|
||||
NTextInput {
|
||||
id: iconInput
|
||||
Layout.fillWidth: true
|
||||
label: "Icon Name"
|
||||
description: "Choose a name from the Material Icon set."
|
||||
placeholderText: "Enter icon name (e.g., favorite, home, settings)"
|
||||
description: "Pick from Bootstrap Icons or type a name."
|
||||
placeholderText: "Enter icon name (e.g., speedometer2, gear, house)"
|
||||
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 {
|
||||
id: leftClickExecInput
|
||||
Layout.fillWidth: true
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ ColumnLayout {
|
|||
!== undefined ? widgetData.showMemoryAsPercent : widgetMetadata.showMemoryAsPercent
|
||||
property bool valueShowNetworkStats: widgetData.showNetworkStats
|
||||
!== undefined ? widgetData.showNetworkStats : widgetMetadata.showNetworkStats
|
||||
property bool valueShowDiskUsage: widgetData.showDiskUsage !== undefined ? widgetData.showDiskUsage : widgetMetadata.showDiskUsage
|
||||
|
||||
function saveSettings() {
|
||||
var settings = Object.assign({}, widgetData || {})
|
||||
|
|
@ -29,6 +30,7 @@ ColumnLayout {
|
|||
settings.showMemoryUsage = valueShowMemoryUsage
|
||||
settings.showMemoryAsPercent = valueShowMemoryAsPercent
|
||||
settings.showNetworkStats = valueShowNetworkStats
|
||||
settings.showDiskUsage = valueShowDiskUsage
|
||||
return settings
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +61,7 @@ ColumnLayout {
|
|||
NToggle {
|
||||
id: showMemoryAsPercent
|
||||
Layout.fillWidth: true
|
||||
label: "Show memory as percentage"
|
||||
label: "Memory as percentage"
|
||||
checked: valueShowMemoryAsPercent
|
||||
onToggled: checked => valueShowMemoryAsPercent = checked
|
||||
}
|
||||
|
|
@ -71,4 +73,12 @@ ColumnLayout {
|
|||
checked: valueShowNetworkStats
|
||||
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 = [{
|
||||
"id": SettingsPanel.Tab.General,
|
||||
"label": "General",
|
||||
"icon": "tune",
|
||||
"icon": "box",
|
||||
"source": generalTab
|
||||
}, {
|
||||
"id": SettingsPanel.Tab.Bar,
|
||||
"label": "Bar",
|
||||
"icon": "web_asset",
|
||||
"icon": "segmented-nav",
|
||||
"source": barTab
|
||||
}, {
|
||||
"id": SettingsPanel.Tab.Launcher,
|
||||
"label": "Launcher",
|
||||
"icon": "apps",
|
||||
"icon": "rocket",
|
||||
"source": launcherTab
|
||||
}, {
|
||||
"id": SettingsPanel.Tab.Audio,
|
||||
"label": "Audio",
|
||||
"icon": "volume_up",
|
||||
"icon": "speaker",
|
||||
"source": audioTab
|
||||
}, {
|
||||
"id": SettingsPanel.Tab.Display,
|
||||
"label": "Display",
|
||||
"icon": "monitor",
|
||||
"icon": "display",
|
||||
"source": displayTab
|
||||
}, {
|
||||
"id": SettingsPanel.Tab.Network,
|
||||
"label": "Network",
|
||||
"icon": "lan",
|
||||
"icon": "ethernet",
|
||||
"source": networkTab
|
||||
}, {
|
||||
"id": SettingsPanel.Tab.Brightness,
|
||||
"label": "Brightness",
|
||||
"icon": "brightness_6",
|
||||
"icon": "brightness-high",
|
||||
"source": brightnessTab
|
||||
}, {
|
||||
"id": SettingsPanel.Tab.Weather,
|
||||
"label": "Weather",
|
||||
"icon": "partly_cloudy_day",
|
||||
"icon": "cloud-sun",
|
||||
"source": weatherTab
|
||||
}, {
|
||||
"id": SettingsPanel.Tab.ColorScheme,
|
||||
|
|
@ -168,7 +168,7 @@ NPanel {
|
|||
}, {
|
||||
"id": SettingsPanel.Tab.Wallpaper,
|
||||
"label": "Wallpaper",
|
||||
"icon": "image",
|
||||
"icon": "easel",
|
||||
"source": wallpaperTab
|
||||
}]
|
||||
|
||||
|
|
@ -177,7 +177,7 @@ NPanel {
|
|||
newTabs.push({
|
||||
"id": SettingsPanel.Tab.WallpaperSelector,
|
||||
"label": "Wallpaper Selector",
|
||||
"icon": "wallpaper_slideshow",
|
||||
"icon": "image",
|
||||
"source": wallpaperSelectorTab
|
||||
})
|
||||
}
|
||||
|
|
@ -185,17 +185,17 @@ NPanel {
|
|||
newTabs.push({
|
||||
"id": SettingsPanel.Tab.ScreenRecorder,
|
||||
"label": "Screen Recorder",
|
||||
"icon": "videocam",
|
||||
"icon": "camera-video",
|
||||
"source": screenRecorderTab
|
||||
}, {
|
||||
"id": SettingsPanel.Tab.Hooks,
|
||||
"label": "Hooks",
|
||||
"icon": "cable",
|
||||
"icon": "link-45deg",
|
||||
"source": hooksTab
|
||||
}, {
|
||||
"id": SettingsPanel.Tab.About,
|
||||
"label": "About",
|
||||
"icon": "info",
|
||||
"icon": "info-circle",
|
||||
"source": aboutTab
|
||||
})
|
||||
|
||||
|
|
@ -400,15 +400,13 @@ NPanel {
|
|||
anchors.fill: parent
|
||||
anchors.leftMargin: Style.marginS * scaling
|
||||
anchors.rightMargin: Style.marginS * scaling
|
||||
spacing: Style.marginS * scaling
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
// Tab icon
|
||||
NIcon {
|
||||
text: modelData.icon
|
||||
text: Bootstrap.icons[modelData.icon]
|
||||
color: tabTextColor
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
}
|
||||
|
||||
// Tab label
|
||||
NText {
|
||||
text: modelData.label
|
||||
|
|
@ -416,6 +414,7 @@ NPanel {
|
|||
font.pointSize: Style.fontSizeM * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -473,7 +472,7 @@ NPanel {
|
|||
|
||||
// Close button
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
icon: "x-lg"
|
||||
tooltipText: "Close"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
onClicked: root.close()
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ ColumnLayout {
|
|||
|
||||
// Button aligned to the center of the actual input field
|
||||
NIconButton {
|
||||
icon: "add"
|
||||
icon: "plus-lg"
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
Layout.bottomMargin: blacklistInput.description ? Style.marginS * scaling : 0
|
||||
onClicked: {
|
||||
|
|
@ -322,7 +322,7 @@ ColumnLayout {
|
|||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
icon: "x-lg"
|
||||
sizeRatio: 0.8
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.rightMargin: Style.marginXS * scaling
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ ColumnLayout {
|
|||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "refresh"
|
||||
icon: "arrow-repeat"
|
||||
tooltipText: "Reset scaling"
|
||||
onClicked: ScalingService.setScreenScale(modelData, 1.0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ ColumnLayout {
|
|||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "refresh"
|
||||
icon: "arrow-repeat"
|
||||
tooltipText: "Refresh wallpaper list"
|
||||
onClicked: {
|
||||
WallpaperService.refreshWallpapersList()
|
||||
|
|
@ -181,7 +181,7 @@ ColumnLayout {
|
|||
visible: isSelected
|
||||
|
||||
NIcon {
|
||||
text: "check"
|
||||
icon: "check-lg"
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSecondary
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ NBox {
|
|||
}
|
||||
|
||||
NIcon {
|
||||
text: "album"
|
||||
icon: "disc"
|
||||
font.pointSize: Style.fontSizeXXXL * 2.5 * scaling
|
||||
color: Color.mPrimary
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
|
@ -162,14 +162,14 @@ NBox {
|
|||
anchors.fill: parent
|
||||
anchors.margins: Style.marginXS * scaling
|
||||
imagePath: MediaService.trackArtUrl
|
||||
fallbackIcon: "music_note"
|
||||
fallbackIcon: "disc"
|
||||
borderColor: Color.mOutline
|
||||
borderWidth: Math.max(1, Style.borderS * scaling)
|
||||
}
|
||||
|
||||
// Fallback icon when no album art available
|
||||
NIcon {
|
||||
text: "album"
|
||||
icon: "disc"
|
||||
color: Color.mPrimary
|
||||
font.pointSize: Style.fontSizeL * 12 * scaling
|
||||
visible: !trackArt.visible
|
||||
|
|
@ -307,7 +307,7 @@ NBox {
|
|||
|
||||
// Previous button
|
||||
NIconButton {
|
||||
icon: "skip_previous"
|
||||
icon: "skip-start"
|
||||
tooltipText: "Previous Media"
|
||||
visible: MediaService.canGoPrevious
|
||||
onClicked: MediaService.canGoPrevious ? MediaService.previous() : {}
|
||||
|
|
@ -315,7 +315,7 @@ NBox {
|
|||
|
||||
// Play/Pause button
|
||||
NIconButton {
|
||||
icon: MediaService.isPlaying ? "pause" : "play_arrow"
|
||||
icon: MediaService.isPlaying ? "pause" : "play"
|
||||
tooltipText: MediaService.isPlaying ? "Pause" : "Play"
|
||||
visible: (MediaService.canPlay || MediaService.canPause)
|
||||
onClicked: (MediaService.canPlay || MediaService.canPause) ? MediaService.playPause() : {}
|
||||
|
|
@ -323,7 +323,7 @@ NBox {
|
|||
|
||||
// Next button
|
||||
NIconButton {
|
||||
icon: "skip_next"
|
||||
icon: "skip-end"
|
||||
tooltipText: "Next media"
|
||||
visible: MediaService.canGoNext
|
||||
onClicked: MediaService.canGoNext ? MediaService.next() : {}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ NBox {
|
|||
}
|
||||
// Performance
|
||||
NIconButton {
|
||||
icon: "speed"
|
||||
icon: "speedometer2"
|
||||
tooltipText: "Set performance power profile."
|
||||
enabled: hasPP
|
||||
opacity: enabled ? Style.opacityFull : Style.opacityMedium
|
||||
|
|
@ -37,12 +37,13 @@ NBox {
|
|||
onClicked: {
|
||||
if (enabled) {
|
||||
powerProfiles.profile = PowerProfile.Performance
|
||||
ToastService.showNotice("Power Profile", "Performance")
|
||||
}
|
||||
}
|
||||
}
|
||||
// Balanced
|
||||
NIconButton {
|
||||
icon: "balance"
|
||||
icon: "yin-yang"
|
||||
tooltipText: "Set balanced power profile."
|
||||
enabled: hasPP
|
||||
opacity: enabled ? Style.opacityFull : Style.opacityMedium
|
||||
|
|
@ -51,12 +52,13 @@ NBox {
|
|||
onClicked: {
|
||||
if (enabled) {
|
||||
powerProfiles.profile = PowerProfile.Balanced
|
||||
ToastService.showNotice("Power Profile", "Balanced")
|
||||
}
|
||||
}
|
||||
}
|
||||
// Eco
|
||||
NIconButton {
|
||||
icon: "eco"
|
||||
icon: "leaf"
|
||||
tooltipText: "Set eco power profile."
|
||||
enabled: hasPP
|
||||
opacity: enabled ? Style.opacityFull : Style.opacityMedium
|
||||
|
|
@ -65,6 +67,7 @@ NBox {
|
|||
onClicked: {
|
||||
if (enabled) {
|
||||
powerProfiles.profile = PowerProfile.PowerSaver
|
||||
ToastService.showNotice("Power Profile", "Power Saver")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,8 @@ NBox {
|
|||
}
|
||||
NText {
|
||||
text: `System uptime: ${uptimeText}`
|
||||
color: Color.mOnSurface
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +59,7 @@ NBox {
|
|||
Layout.fillWidth: true
|
||||
}
|
||||
NIconButton {
|
||||
icon: "settings"
|
||||
icon: "gear"
|
||||
tooltipText: "Open settings."
|
||||
onClicked: {
|
||||
settingsPanel.requestedTab = SettingsPanel.Tab.General
|
||||
|
|
@ -68,7 +69,7 @@ NBox {
|
|||
|
||||
NIconButton {
|
||||
id: powerButton
|
||||
icon: "power_settings_new"
|
||||
icon: "power"
|
||||
tooltipText: "Power menu."
|
||||
onClicked: {
|
||||
powerPanel.open(screen)
|
||||
|
|
@ -78,7 +79,7 @@ NBox {
|
|||
|
||||
NIconButton {
|
||||
id: closeButton
|
||||
icon: "close"
|
||||
icon: "x-lg"
|
||||
tooltipText: "Close side panel."
|
||||
onClicked: {
|
||||
sidePanel.close()
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ NBox {
|
|||
|
||||
NCircleStat {
|
||||
value: SystemStatService.cpuUsage
|
||||
icon: "speed"
|
||||
icon: "speedometer2"
|
||||
flat: true
|
||||
contentScale: 0.8
|
||||
width: 72 * scaling
|
||||
|
|
@ -33,7 +33,7 @@ NBox {
|
|||
NCircleStat {
|
||||
value: SystemStatService.cpuTemp
|
||||
suffix: "°C"
|
||||
icon: "device_thermostat"
|
||||
icon: "fire"
|
||||
flat: true
|
||||
contentScale: 0.8
|
||||
width: 72 * scaling
|
||||
|
|
@ -41,7 +41,7 @@ NBox {
|
|||
}
|
||||
NCircleStat {
|
||||
value: SystemStatService.memPercent
|
||||
icon: "memory"
|
||||
icon: "cpu"
|
||||
flat: true
|
||||
contentScale: 0.8
|
||||
width: 72 * scaling
|
||||
|
|
@ -49,7 +49,7 @@ NBox {
|
|||
}
|
||||
NCircleStat {
|
||||
value: SystemStatService.diskPercent
|
||||
icon: "hard_drive"
|
||||
icon: "hdd"
|
||||
flat: true
|
||||
contentScale: 0.8
|
||||
width: 72 * scaling
|
||||
|
|
|
|||
|
|
@ -25,11 +25,14 @@ NBox {
|
|||
}
|
||||
// Screen Recorder
|
||||
NIconButton {
|
||||
icon: "videocam"
|
||||
tooltipText: ScreenRecorderService.isRecording ? "Stop screen recording." : "Start screen recording."
|
||||
icon: "camera-video"
|
||||
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
|
||||
colorFg: ScreenRecorderService.isRecording ? Color.mOnPrimary : Color.mPrimary
|
||||
onClicked: {
|
||||
if (!ScreenRecorderService.isAvailable)
|
||||
return
|
||||
ScreenRecorderService.toggleRecording()
|
||||
// If we were not recording and we just initiated a start, close the panel
|
||||
if (!ScreenRecorderService.isRecording) {
|
||||
|
|
@ -41,7 +44,7 @@ NBox {
|
|||
|
||||
// Idle Inhibitor
|
||||
NIconButton {
|
||||
icon: "coffee"
|
||||
icon: "cup"
|
||||
tooltipText: IdleInhibitorService.isInhibited ? "Disable keep awake." : "Enable keep awake."
|
||||
colorBg: IdleInhibitorService.isInhibited ? Color.mPrimary : Color.mSurfaceVariant
|
||||
colorFg: IdleInhibitorService.isInhibited ? Color.mOnPrimary : Color.mPrimary
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ NBox {
|
|||
RowLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
NIcon {
|
||||
text: weatherReady ? LocationService.weatherSymbolFromCode(
|
||||
icon: weatherReady ? LocationService.weatherSymbolFromCode(
|
||||
LocationService.data.weather.current_weather.weathercode) : ""
|
||||
font.pointSize: Style.fontSizeXXXL * 1.75 * scaling
|
||||
color: Color.mPrimary
|
||||
|
|
@ -98,7 +98,7 @@ NBox {
|
|||
color: Color.mOnSurface
|
||||
}
|
||||
NIcon {
|
||||
text: LocationService.weatherSymbolFromCode(LocationService.data.weather.daily.weathercode[index])
|
||||
icon: LocationService.weatherSymbolFromCode(LocationService.data.weather.daily.weathercode[index])
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: Color.mPrimary
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ NPanel {
|
|||
spacing: Style.marginM * scaling
|
||||
|
||||
NIcon {
|
||||
text: Settings.data.network.wifiEnabled ? "wifi" : "wifi_off"
|
||||
icon: Settings.data.network.wifiEnabled ? "wifi" : "wifi-off"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: Settings.data.network.wifiEnabled ? Color.mPrimary : Color.mOnSurfaceVariant
|
||||
}
|
||||
|
|
@ -55,7 +55,7 @@ NPanel {
|
|||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "refresh"
|
||||
icon: "arrow-repeat"
|
||||
tooltipText: "Refresh"
|
||||
sizeRatio: 0.8
|
||||
enabled: Settings.data.network.wifiEnabled && !NetworkService.scanning
|
||||
|
|
@ -63,7 +63,7 @@ NPanel {
|
|||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
icon: "x-lg"
|
||||
tooltipText: "Close"
|
||||
sizeRatio: 0.8
|
||||
onClicked: root.close()
|
||||
|
|
@ -91,7 +91,7 @@ NPanel {
|
|||
spacing: Style.marginS * scaling
|
||||
|
||||
NIcon {
|
||||
text: "error"
|
||||
icon: "exclamation-triangle"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
color: Color.mError
|
||||
}
|
||||
|
|
@ -105,7 +105,7 @@ NPanel {
|
|||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
icon: "x-lg"
|
||||
sizeRatio: 0.6
|
||||
onClicked: NetworkService.lastError = ""
|
||||
}
|
||||
|
|
@ -129,7 +129,7 @@ NPanel {
|
|||
}
|
||||
|
||||
NIcon {
|
||||
text: "wifi_off"
|
||||
icon: "wifi-off"
|
||||
font.pointSize: 64 * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
|
@ -245,7 +245,7 @@ NPanel {
|
|||
spacing: Style.marginS * scaling
|
||||
|
||||
NIcon {
|
||||
text: NetworkService.signalIcon(modelData.signal)
|
||||
icon: NetworkService.signalIcon(modelData.signal)
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: modelData.connected ? Color.mPrimary : Color.mOnSurface
|
||||
}
|
||||
|
|
@ -377,7 +377,7 @@ NPanel {
|
|||
&& NetworkService.connectingTo !== modelData.ssid
|
||||
&& NetworkService.forgettingNetwork !== modelData.ssid
|
||||
&& NetworkService.disconnectingFrom !== modelData.ssid
|
||||
icon: "delete"
|
||||
icon: "trash"
|
||||
tooltipText: "Forget network"
|
||||
sizeRatio: 0.7
|
||||
onClicked: expandedSsid = expandedSsid === modelData.ssid ? "" : modelData.ssid
|
||||
|
|
@ -492,7 +492,7 @@ NPanel {
|
|||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
icon: "x-lg"
|
||||
sizeRatio: 0.8
|
||||
onClicked: {
|
||||
passwordSsid = ""
|
||||
|
|
@ -547,7 +547,7 @@ NPanel {
|
|||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
icon: "x-lg"
|
||||
sizeRatio: 0.8
|
||||
onClicked: expandedSsid = ""
|
||||
}
|
||||
|
|
@ -571,7 +571,7 @@ NPanel {
|
|||
}
|
||||
|
||||
NIcon {
|
||||
text: "wifi_find"
|
||||
icon: "search"
|
||||
font.pointSize: 64 * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
|
@ -586,7 +586,7 @@ NPanel {
|
|||
|
||||
NButton {
|
||||
text: "Scan again"
|
||||
icon: "refresh"
|
||||
icon: "arrow-repeat"
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
onClicked: NetworkService.scan()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,11 @@
|
|||
</a>
|
||||
</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.
|
||||
|
||||
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
|
||||
- `ttf-roboto` - The default font used for most of the UI
|
||||
- `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
|
||||
- `brightnessctl` - For internal/laptop monitor brightness
|
||||
- `ddcutil` - For desktop monitor brightness (might introduce some system instability with certain monitors)
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@ Singleton {
|
|||
function onMutedChanged() {
|
||||
root._muted = (sink?.audio.muted ?? true)
|
||||
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() {
|
||||
root._inputMuted = (source?.audio.muted ?? true)
|
||||
Logger.log("AudioService", "OnInputMuteChanged:", root._inputMuted)
|
||||
// Toast: microphone mute toggle
|
||||
ToastService.showNotice("Microphone", root._inputMuted ? "Muted" : "Unmuted")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,8 @@ Singleton {
|
|||
"showCpuTemp": true,
|
||||
"showMemoryUsage": true,
|
||||
"showMemoryAsPercent": false,
|
||||
"showNetworkStats": false
|
||||
"showNetworkStats": false,
|
||||
"showDiskUsage": false
|
||||
},
|
||||
"Workspace": {
|
||||
"allowUserSettings": true,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ pragma Singleton
|
|||
|
||||
import Quickshell
|
||||
import Quickshell.Services.UPower
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
|
@ -9,41 +11,20 @@ Singleton {
|
|||
// Choose icon based on charge and charging state
|
||||
function getIcon(percent, charging, isReady) {
|
||||
if (!isReady) {
|
||||
return "battery_error"
|
||||
return "exclamation-triangle"
|
||||
}
|
||||
|
||||
if (charging) {
|
||||
if (percent >= 95)
|
||||
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"
|
||||
return "battery-charging"
|
||||
} else {
|
||||
if (percent >= 95)
|
||||
return "battery_full"
|
||||
if (percent >= 85)
|
||||
return "battery_6_bar"
|
||||
if (percent >= 70)
|
||||
return "battery_5_bar"
|
||||
if (percent >= 55)
|
||||
return "battery_4_bar"
|
||||
if (percent >= 40)
|
||||
return "battery_3_bar"
|
||||
return "battery-full"
|
||||
if (percent >= 45)
|
||||
return "battery-half"
|
||||
if (percent >= 25)
|
||||
return "battery_2_bar"
|
||||
if (percent >= 10)
|
||||
return "battery_1_bar"
|
||||
return "battery-low"
|
||||
if (percent >= 0)
|
||||
return "battery_0_bar"
|
||||
return "battery"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,17 +62,17 @@ Singleton {
|
|||
}
|
||||
|
||||
if (icon.includes("mouse") || name.includes("mouse")) {
|
||||
return "mouse"
|
||||
return "mouse-2"
|
||||
}
|
||||
if (icon.includes("keyboard") || name.includes("keyboard")) {
|
||||
return "keyboard"
|
||||
}
|
||||
if (icon.includes("phone") || name.includes("phone") || name.includes("iphone") || name.includes("android")
|
||||
|| name.includes("samsung")) {
|
||||
return "smartphone"
|
||||
return "phone"
|
||||
}
|
||||
if (icon.includes("watch") || name.includes("watch")) {
|
||||
return "watch"
|
||||
return "smartwatch"
|
||||
}
|
||||
if (icon.includes("speaker") || name.includes("speaker")) {
|
||||
return "speaker"
|
||||
|
|
|
|||
|
|
@ -23,6 +23,11 @@ Singleton {
|
|||
// Re-apply current scheme to pick the right variant
|
||||
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({
|
||||
"id": toplevel.address || "",
|
||||
"title": toplevel.title || "",
|
||||
"appId": appId,
|
||||
"id": (toplevel.address !== undefined
|
||||
&& toplevel.address !== null) ? String(toplevel.address) : "",
|
||||
"title": (toplevel.title !== undefined && toplevel.title !== null) ? String(
|
||||
toplevel.title) : "",
|
||||
"appId": (appId !== undefined && appId !== null) ? String(appId) : "",
|
||||
"workspaceId": toplevel.workspace?.id || null,
|
||||
"isFocused": toplevel.activated === true
|
||||
})
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ Singleton {
|
|||
property ListModel displayFonts: ListModel {}
|
||||
property bool fontsLoaded: false
|
||||
|
||||
// -------------------------------------------
|
||||
function init() {
|
||||
Logger.log("Font", "Service started")
|
||||
loadSystemFonts()
|
||||
|
|
|
|||
|
|
@ -231,21 +231,23 @@ Singleton {
|
|||
// --------------------------------
|
||||
function weatherSymbolFromCode(code) {
|
||||
if (code === 0)
|
||||
return "sunny"
|
||||
return "sun"
|
||||
if (code === 1 || code === 2)
|
||||
return "partly_cloudy_day"
|
||||
return "cloud-sun"
|
||||
if (code === 3)
|
||||
return "cloud"
|
||||
if (code >= 45 && code <= 48)
|
||||
return "foggy"
|
||||
return "cloud-haze"
|
||||
if (code >= 51 && code <= 67)
|
||||
return "rainy"
|
||||
return "cloud-rain"
|
||||
if (code >= 71 && code <= 77)
|
||||
return "weather_snowy"
|
||||
if (code >= 80 && code <= 82)
|
||||
return "rainy"
|
||||
return "cloud-snow"
|
||||
if (code >= 71 && code <= 77)
|
||||
return "cloud-snow"
|
||||
if (code >= 85 && code <= 86)
|
||||
return "cloud-snow"
|
||||
if (code >= 95 && code <= 99)
|
||||
return "thunderstorm"
|
||||
return "cloud-lightning"
|
||||
return "cloud"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -202,14 +202,12 @@ Singleton {
|
|||
// Helper functions
|
||||
function signalIcon(signal) {
|
||||
if (signal >= 80)
|
||||
return "network_wifi"
|
||||
if (signal >= 60)
|
||||
return "network_wifi_3_bar"
|
||||
if (signal >= 40)
|
||||
return "network_wifi_2_bar"
|
||||
return "wifi"
|
||||
if (signal >= 50)
|
||||
return "wifi-2"
|
||||
if (signal >= 20)
|
||||
return "network_wifi_1_bar"
|
||||
return "signal_wifi_0_bar"
|
||||
return "wifi-1"
|
||||
return "dot"
|
||||
}
|
||||
|
||||
function isSecured(security) {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@ Singleton {
|
|||
target: Settings.data.nightLight
|
||||
function onEnabledChanged() {
|
||||
apply()
|
||||
// Toast: night light toggled
|
||||
const enabled = !!Settings.data.nightLight.enabled
|
||||
ToastService.showNotice("Night Light", enabled ? "Enabled" : "Disabled")
|
||||
}
|
||||
function onNightTempChanged() {
|
||||
apply()
|
||||
|
|
|
|||
|
|
@ -13,6 +13,17 @@ Singleton {
|
|||
property bool isRecording: false
|
||||
property bool isPending: false
|
||||
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
|
||||
function toggleRecording() {
|
||||
|
|
@ -21,6 +32,9 @@ Singleton {
|
|||
|
||||
// Start screen recording using Quickshell.execDetached
|
||||
function startRecording() {
|
||||
if (!isAvailable) {
|
||||
return
|
||||
}
|
||||
if (isRecording || isPending) {
|
||||
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 {
|
||||
id: pendingTimer
|
||||
interval: 2000 // Wait 2 seconds to see if process stays alive
|
||||
|
|
|
|||
|
|
@ -321,10 +321,8 @@ Singleton {
|
|||
// -------------------------------------------------------
|
||||
// Helper function to format network speeds
|
||||
function formatSpeed(bytesPerSecond) {
|
||||
if (bytesPerSecond < 1024) {
|
||||
return bytesPerSecond.toFixed(0) + "B/s"
|
||||
} else if (bytesPerSecond < 1024 * 1024) {
|
||||
return (bytesPerSecond / 1024).toFixed(0) + "KB/s"
|
||||
if (bytesPerSecond < 1024 * 1024) {
|
||||
return (bytesPerSecond / 1024).toFixed(1) + "KB/s"
|
||||
} else if (bytesPerSecond < 1024 * 1024 * 1024) {
|
||||
return (bytesPerSecond / (1024 * 1024)).toFixed(1) + "MB/s"
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -185,6 +185,11 @@ Singleton {
|
|||
// Process the message queue
|
||||
function processQueue() {
|
||||
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
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,27 +80,29 @@ Rectangle {
|
|||
spacing: Style.marginXS * scaling
|
||||
|
||||
// Icon (optional)
|
||||
NIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
layoutTopMargin: 1 * scaling
|
||||
visible: root.icon !== ""
|
||||
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
|
||||
}
|
||||
Loader {
|
||||
active: root.icon !== ""
|
||||
sourceComponent: NIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
easing.type: Easing.OutCubic
|
||||
icon: 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 {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ RowLayout {
|
|||
NIcon {
|
||||
visible: root.checked
|
||||
anchors.centerIn: parent
|
||||
text: "check"
|
||||
icon: "check-lg"
|
||||
color: root.activeOnColor
|
||||
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
|
||||
Rectangle {
|
||||
id: iconBadge
|
||||
width: 28 * scaling * contentScale
|
||||
width: iconText.implicitWidth + Style.marginXS * scaling
|
||||
height: width
|
||||
radius: width / 2
|
||||
color: Color.mSurface
|
||||
color: Color.mPrimary
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.rightMargin: -6 * scaling * contentScale
|
||||
anchors.topMargin: Style.marginXXS * scaling * contentScale
|
||||
anchors.rightMargin: Style.marginXXS * scaling * contentScale
|
||||
anchors.topMargin: Style.marginXS * scaling * contentScale
|
||||
|
||||
NIcon {
|
||||
id: iconText
|
||||
anchors.centerIn: parent
|
||||
text: root.icon
|
||||
font.pointSize: Style.fontSizeLargeXL * scaling * contentScale
|
||||
color: Color.mOnSurface
|
||||
icon: root.icon
|
||||
font.pointSize: Style.fontSizeS * scaling * contentScale
|
||||
color: Color.mOnPrimary
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import QtQuick
|
|||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
|
|
@ -58,7 +59,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
NIcon {
|
||||
text: "palette"
|
||||
icon: "paint-bucket"
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import QtQuick
|
|||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Popup {
|
||||
|
|
@ -129,7 +130,7 @@ Popup {
|
|||
spacing: Style.marginS * scaling
|
||||
|
||||
NIcon {
|
||||
text: "palette"
|
||||
icon: "eyedropper"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: Color.mPrimary
|
||||
}
|
||||
|
|
@ -147,7 +148,7 @@ Popup {
|
|||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
icon: "x-lg"
|
||||
onClicked: root.close()
|
||||
}
|
||||
}
|
||||
|
|
@ -491,7 +492,7 @@ Popup {
|
|||
NButton {
|
||||
id: cancelButton
|
||||
text: "Cancel"
|
||||
icon: "close"
|
||||
icon: "x-lg"
|
||||
outlined: cancelButton.hovered ? false : true
|
||||
customHeight: 36 * scaling
|
||||
customWidth: 100 * scaling
|
||||
|
|
@ -502,7 +503,7 @@ Popup {
|
|||
|
||||
NButton {
|
||||
text: "Apply"
|
||||
icon: "check"
|
||||
icon: "check-lg"
|
||||
customHeight: 36 * scaling
|
||||
customWidth: 100 * scaling
|
||||
onClicked: {
|
||||
|
|
|
|||
|
|
@ -85,8 +85,8 @@ RowLayout {
|
|||
indicator: NIcon {
|
||||
x: combo.width - width - Style.marginM * scaling
|
||||
y: combo.topPadding + (combo.availableHeight - height) / 2
|
||||
text: "arrow_drop_down"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
icon: "chevron-down"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
}
|
||||
|
||||
popup: Popup {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,22 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
import qs.Widgets
|
||||
import QtQuick.Layouts
|
||||
|
||||
Text {
|
||||
// Optional layout nudge for optical alignment when used inside Layouts
|
||||
property real layoutTopMargin: 0
|
||||
text: "question_mark"
|
||||
font.family: "Material Symbols Rounded"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
font.variableAxes: {
|
||||
"wght"// slightly bold to ensure all lines looks good
|
||||
: (Font.Normal + Font.Bold) / 2.5
|
||||
readonly property string defaultIcon: "balloon"
|
||||
property string icon: defaultIcon
|
||||
|
||||
text: {
|
||||
if (icon === undefined || Bootstrap.icons[icon] === undefined) {
|
||||
Logger.warn("Icon", `"${icon}"`, "doesn't exist in the bootstrap font")
|
||||
Logger.callStack()
|
||||
return Bootstrap.icons[defaultIcon]
|
||||
}
|
||||
return Bootstrap.icons[icon]
|
||||
}
|
||||
font.family: "bootstrap-icons"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
color: Color.mOnSurface
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
Layout.topMargin: layoutTopMargin
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,17 +35,31 @@ Rectangle {
|
|||
opacity: root.enabled ? Style.opacityFull : Style.opacityMedium
|
||||
color: root.enabled && root.hovering ? colorBgHover : colorBg
|
||||
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)
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
|
||||
NIcon {
|
||||
text: root.icon
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
color: root.hovering ? colorFgHover : colorFg
|
||||
icon: root.icon
|
||||
font.pointSize: Math.max(1, root.width * 0.4)
|
||||
color: root.enabled && root.hovering ? colorFgHover : colorFg
|
||||
// Center horizontally
|
||||
x: (root.width - width) / 2
|
||||
// Center vertically accounting for font metrics
|
||||
y: (root.height - height) / 2 + (height - contentHeight) / 2
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NTooltip {
|
||||
|
|
@ -56,13 +70,14 @@ Rectangle {
|
|||
}
|
||||
|
||||
MouseArea {
|
||||
enabled: root.enabled
|
||||
// Always enabled to allow hover/tooltip even when the button is disabled
|
||||
enabled: true
|
||||
anchors.fill: parent
|
||||
cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
hovering = true
|
||||
hovering = root.enabled ? true : false
|
||||
if (tooltipText) {
|
||||
tooltip.show()
|
||||
}
|
||||
|
|
@ -79,6 +94,9 @@ Rectangle {
|
|||
if (tooltipText) {
|
||||
tooltip.hide()
|
||||
}
|
||||
if (!root.enabled) {
|
||||
return
|
||||
}
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
root.clicked()
|
||||
} else if (mouse.button === Qt.RightButton) {
|
||||
|
|
|
|||
|
|
@ -9,9 +9,10 @@ Rectangle {
|
|||
id: root
|
||||
|
||||
property string imagePath: ""
|
||||
property string fallbackIcon: ""
|
||||
property color borderColor: Color.transparent
|
||||
property real borderWidth: 0
|
||||
property string fallbackIcon: ""
|
||||
property real fallbackIconSize: Style.fontSizeXXL * scaling
|
||||
|
||||
color: Color.transparent
|
||||
radius: parent.width * 0.5
|
||||
|
|
@ -45,18 +46,20 @@ Rectangle {
|
|||
}
|
||||
|
||||
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
|
||||
blending: true
|
||||
}
|
||||
|
||||
// Fallback icon
|
||||
NIcon {
|
||||
anchors.centerIn: parent
|
||||
text: fallbackIcon
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
visible: fallbackIcon !== undefined && fallbackIcon !== "" && (imagePath === undefined || imagePath === "")
|
||||
z: 0
|
||||
Loader {
|
||||
active: fallbackIcon !== undefined && fallbackIcon !== "" && (imagePath === undefined || imagePath === "")
|
||||
sourceComponent: NIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: fallbackIcon
|
||||
font.pointSize: fallbackIconSize
|
||||
z: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,11 @@ Rectangle {
|
|||
id: root
|
||||
|
||||
property string imagePath: ""
|
||||
property string fallbackIcon: ""
|
||||
property color borderColor: Color.transparent
|
||||
property real borderWidth: 0
|
||||
property real imageRadius: width * 0.5
|
||||
property string fallbackIcon: ""
|
||||
property real fallbackIconSize: Style.fontSizeXXL * scaling
|
||||
|
||||
property real scaledRadius: imageRadius * Settings.data.general.radiusRatio
|
||||
|
||||
|
|
@ -56,7 +57,7 @@ Rectangle {
|
|||
property real itemHeight: root.height
|
||||
property real cornerRadius: root.radius
|
||||
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
|
||||
supportsAtlasTextures: false
|
||||
|
|
@ -71,12 +72,14 @@ Rectangle {
|
|||
}
|
||||
|
||||
// Fallback icon
|
||||
NIcon {
|
||||
anchors.centerIn: parent
|
||||
text: fallbackIcon
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
visible: fallbackIcon !== undefined && fallbackIcon !== "" && (imagePath === undefined || imagePath === "")
|
||||
z: 0
|
||||
Loader {
|
||||
active: fallbackIcon !== undefined && fallbackIcon !== "" && (imagePath === undefined || imagePath === "")
|
||||
sourceComponent: NIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: fallbackIcon
|
||||
font.pointSize: fallbackIconSize
|
||||
z: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import QtQuick
|
|||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
import qs.Widgets
|
||||
import qs.Services
|
||||
|
||||
// Input and button row
|
||||
RowLayout {
|
||||
|
|
@ -13,7 +14,7 @@ RowLayout {
|
|||
property string placeholderText: ""
|
||||
property string text: ""
|
||||
property string actionButtonText: "Test"
|
||||
property string actionButtonIcon: "play_arrow"
|
||||
property string actionButtonIcon: "play"
|
||||
property bool actionButtonEnabled: text !== ""
|
||||
|
||||
// Signals
|
||||
|
|
|
|||
|
|
@ -9,21 +9,15 @@ Item {
|
|||
property string icon: ""
|
||||
property string text: ""
|
||||
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 bool autoHide: false
|
||||
property bool forceOpen: false
|
||||
property bool disableOpen: false
|
||||
property bool rightOpen: false
|
||||
property bool hovered: false
|
||||
|
||||
// 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 hidden
|
||||
|
|
@ -50,14 +44,14 @@ Item {
|
|||
|
||||
Rectangle {
|
||||
id: pill
|
||||
width: effectiveShown ? maxPillWidth : 1
|
||||
width: revealed ? maxPillWidth : 1
|
||||
height: pillHeight
|
||||
|
||||
x: rightOpen ? (iconCircle.x + iconCircle.width / 2) : // Opens right
|
||||
(iconCircle.x + iconCircle.width / 2) - width // Opens left
|
||||
|
||||
opacity: effectiveShown ? Style.opacityFull : Style.opacityNone
|
||||
color: pillColor
|
||||
opacity: revealed ? Style.opacityFull : Style.opacityNone
|
||||
color: Color.mSurfaceVariant
|
||||
|
||||
topLeftRadius: rightOpen ? 0 : pillHeight * 0.5
|
||||
bottomLeftRadius: rightOpen ? 0 : pillHeight * 0.5
|
||||
|
|
@ -77,8 +71,8 @@ Item {
|
|||
text: root.text
|
||||
font.pointSize: Style.fontSizeXS * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: textColor
|
||||
visible: effectiveShown
|
||||
color: Color.mPrimary
|
||||
visible: revealed
|
||||
}
|
||||
|
||||
Behavior on width {
|
||||
|
|
@ -102,11 +96,8 @@ Item {
|
|||
width: iconSize
|
||||
height: iconSize
|
||||
radius: width * 0.5
|
||||
// When forced shown, match pill background; otherwise use accent when hovered
|
||||
color: forceOpen ? pillColor : (showPill ? iconCircleColor : Color.mSurfaceVariant)
|
||||
color: hovered && !forceOpen ? Color.mPrimary : Color.mSurfaceVariant
|
||||
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)
|
||||
|
||||
|
|
@ -118,11 +109,9 @@ Item {
|
|||
}
|
||||
|
||||
NIcon {
|
||||
text: root.icon
|
||||
rotation: root.iconRotation
|
||||
icon: root.icon
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
// When forced shown, use pill text color; otherwise accent color when hovered
|
||||
color: forceOpen ? textColor : (showPill ? iconTextColor : Color.mOnSurface)
|
||||
color: hovered && !forceOpen ? Color.mOnPrimary : Color.mOnSurface
|
||||
// Center horizontally
|
||||
x: (iconCircle.width - width) / 2
|
||||
// Center vertically accounting for font metrics
|
||||
|
|
@ -220,6 +209,7 @@ Item {
|
|||
hoverEnabled: true
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||
onEntered: {
|
||||
hovered = true
|
||||
root.entered()
|
||||
tooltip.show()
|
||||
if (disableOpen) {
|
||||
|
|
@ -230,6 +220,7 @@ Item {
|
|||
}
|
||||
}
|
||||
onExited: {
|
||||
hovered = false
|
||||
root.exited()
|
||||
if (!forceOpen) {
|
||||
hide()
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ RowLayout {
|
|||
|
||||
NIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "remove"
|
||||
icon: "chevron-left"
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
color: decreaseArea.containsMouse ? Color.mOnPrimary : Color.mPrimary
|
||||
}
|
||||
|
|
@ -130,7 +130,7 @@ RowLayout {
|
|||
|
||||
NIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "add"
|
||||
icon: "chevron-right"
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
color: increaseArea.containsMouse ? Color.mOnPrimary : Color.mPrimary
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,23 +112,13 @@ Item {
|
|||
RowLayout {
|
||||
id: contentLayout
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginM * scaling
|
||||
spacing: Style.marginS * scaling
|
||||
anchors.margins: Style.marginL * scaling
|
||||
spacing: Style.marginL * scaling
|
||||
|
||||
// Icon
|
||||
NIcon {
|
||||
id: icon
|
||||
text: {
|
||||
switch (root.type) {
|
||||
case "warning":
|
||||
return "warning"
|
||||
case "notice":
|
||||
return "info"
|
||||
default:
|
||||
return "info"
|
||||
}
|
||||
}
|
||||
|
||||
icon: (root.type == "warning") ? "exclamation-triangle" : "check-circle"
|
||||
color: {
|
||||
switch (root.type) {
|
||||
case "warning":
|
||||
|
|
@ -172,7 +162,7 @@ Item {
|
|||
|
||||
// Close button (only if persistent or manual dismiss needed)
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
icon: "x-lg"
|
||||
visible: root.persistent || root.duration === 0
|
||||
|
||||
colorBg: Color.mSurfaceVariant
|
||||
|
|
|
|||
44
flake.nix
44
flake.nix
|
|
@ -1,16 +1,13 @@
|
|||
{
|
||||
description = "Noctalia shell - a Wayland desktop shell built with Quickshell";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
systems.url = "github:nix-systems/default";
|
||||
|
||||
quickshell = {
|
||||
url = "git+https://git.outfoxxed.me/outfoxxed/quickshell";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
|
|
@ -24,7 +21,6 @@
|
|||
system:
|
||||
nixpkgs.legacyPackages.${system}.alejandra
|
||||
);
|
||||
|
||||
packages = eachSystem (
|
||||
system: let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
|
|
@ -32,7 +28,33 @@
|
|||
withX11 = 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; [
|
||||
bash
|
||||
bluez
|
||||
|
|
@ -49,12 +71,12 @@
|
|||
networkmanager
|
||||
wl-clipboard
|
||||
];
|
||||
|
||||
fontconfig = pkgs.makeFontsConf {
|
||||
fontDirectories = [
|
||||
pkgs.material-symbols
|
||||
pkgs.roboto
|
||||
pkgs.inter-nerdfont
|
||||
ttf-bootstrap-icons # Add the custom font package here
|
||||
];
|
||||
};
|
||||
in {
|
||||
|
|
@ -62,21 +84,17 @@
|
|||
pname = "noctalia-shell";
|
||||
version = self.rev or self.dirtyRev or "dirty";
|
||||
src = ./.;
|
||||
|
||||
nativeBuildInputs = [pkgs.gcc pkgs.makeWrapper pkgs.qt6.wrapQtAppsHook];
|
||||
buildInputs = [qs pkgs.xkeyboard-config pkgs.qt6.qtbase];
|
||||
propagatedBuildInputs = runtimeDeps;
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/share/noctalia-shell
|
||||
cp -r ./* $out/share/noctalia-shell
|
||||
|
||||
makeWrapper ${qs}/bin/qs $out/bin/noctalia-shell \
|
||||
--prefix PATH : "${pkgs.lib.makeBinPath runtimeDeps}" \
|
||||
--set FONTCONFIG_FILE "${fontconfig}" \
|
||||
--add-flags "-p $out/share/noctalia-shell"
|
||||
'';
|
||||
|
||||
meta = {
|
||||
description = "A sleek and minimal desktop shell thoughtfully crafted for Wayland, built with Quickshell.";
|
||||
homepage = "https://github.com/noctalia-dev/noctalia-shell";
|
||||
|
|
@ -84,9 +102,11 @@
|
|||
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);
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue