feat(battery): circle battery option per/bar instance & theme/step colors

- New settings located in Bar > Widgets and Island > Appearance
- Theme & Step Colors located in Bar > Appearance
This commit is contained in:
purian23 committed 2026-09-02 23:14:02 -04:00
1 parent 99e2368b1c
commit 78e3308f40
12 files changed
+266 -82

No files matched your search

+2 -1
View File
@@ -815,7 +815,7 @@ Singleton {
property bool showBatteryTimeOnlyOnBattery: false
property bool showBatteryPowerCharging: false
property bool showBatteryPowerDischarging: false
property bool batteryPillStyle: false
property string batteryStyle: "icon"
property bool lockBeforeSuspend: false
property bool loginctlLockIntegration: true
property bool fadeToLockEnabled: true
@@ -1179,6 +1179,7 @@ Singleton {
"maximizeWidgetText": false,
"removeWidgetPadding": false,
"widgetPadding": 8,
"batteryColorMode": "theme",
"gothCornersEnabled": false,
"gothCornerRadiusOverride": false,
"gothCornerRadiusValue": 12,
+3 -3
View File
@@ -34,7 +34,7 @@ Singleton {
showBatteryTimeOnlyOnBattery: false,
showBatteryPowerCharging: false,
showBatteryPowerDischarging: false,
batteryPillStyle: false,
batteryStyle: "icon",
showPrinterIcon: false,
showScreenSharingIcon: true
};
@@ -102,8 +102,8 @@ Singleton {
item.showBatteryPowerCharging = order[i].showBatteryPowerCharging;
if (isObj && order[i].showBatteryPowerDischarging !== undefined)
item.showBatteryPowerDischarging = order[i].showBatteryPowerDischarging;
if (isObj && order[i].batteryPillStyle !== undefined)
item.batteryPillStyle = order[i].batteryPillStyle;
if (isObj && order[i].batteryStyle !== undefined)
item.batteryStyle = order[i].batteryStyle;
if (isObj && order[i].showPrinterIcon !== undefined)
item.showPrinterIcon = order[i].showPrinterIcon;
if (isObj && order[i].showScreenSharingIcon !== undefined)
+2 -1
View File
@@ -145,7 +145,7 @@ var SPEC = {
showBatteryTimeOnlyOnBattery: { def: false },
showBatteryPowerCharging: { def: false },
showBatteryPowerDischarging: { def: false },
batteryPillStyle: { def: false },
batteryStyle: { def: "icon" },
showControlCenterButton: { def: true },
showCapsLockIndicator: { def: true },
@@ -634,6 +634,7 @@ var SPEC = {
maximizeWidgetText: false,
removeWidgetPadding: false,
widgetPadding: 8,
batteryColorMode: "theme",
gothCornersEnabled: false,
gothCornerRadiusOverride: false,
gothCornerRadiusValue: 12,
+17 -1
View File
@@ -18,7 +18,9 @@ BasePill {
}
readonly property bool showTime: widgetData?.showBatteryTime !== undefined ? widgetData.showBatteryTime : SettingsData.showBatteryTime
readonly property bool showTimeOnlyOnBattery: widgetData?.showBatteryTimeOnlyOnBattery !== undefined ? widgetData.showBatteryTimeOnlyOnBattery : SettingsData.showBatteryTimeOnlyOnBattery
readonly property bool pillStyle: widgetData?.batteryPillStyle !== undefined ? widgetData.batteryPillStyle : SettingsData.batteryPillStyle
readonly property string batteryStyle: widgetData?.batteryStyle !== undefined ? widgetData.batteryStyle : SettingsData.batteryStyle
readonly property bool pillStyle: battery.batteryStyle !== "icon"
readonly property bool levelColors: (barConfig?.batteryColorMode ?? "theme") === "level"
readonly property bool showPowerCharging: widgetData?.showBatteryPowerCharging !== undefined ? widgetData.showBatteryPowerCharging : SettingsData.showBatteryPowerCharging
readonly property bool showPowerDischarging: widgetData?.showBatteryPowerDischarging !== undefined ? widgetData.showBatteryPowerDischarging : SettingsData.showBatteryPowerDischarging
readonly property bool showPower: BatteryService.isCharging ? showPowerCharging : showPowerDischarging
@@ -144,6 +146,10 @@ BasePill {
return Theme.widgetIconColor;
}
if (battery.levelColors) {
return BatteryService.levelColor;
}
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
return Theme.error;
}
@@ -161,6 +167,9 @@ BasePill {
visible: battery.pillStyle
vertical: true
showNumber: false
meterStyle: battery.batteryStyle
levelColors: battery.levelColors
maxDiameter: battery.widgetThickness - Theme.spacingXS
thickness: Theme.barIconSize(battery.barThickness, undefined, battery.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale)
anchors.horizontalCenter: parent.horizontalCenter
}
@@ -190,6 +199,10 @@ BasePill {
return Theme.widgetIconColor;
}
if (battery.levelColors) {
return BatteryService.levelColor;
}
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
return Theme.error;
}
@@ -206,6 +219,9 @@ BasePill {
BatteryMeter {
visible: battery.pillStyle
showNumber: battery.showPercent
meterStyle: battery.batteryStyle
levelColors: battery.levelColors
maxDiameter: battery.widgetThickness - Theme.spacingXS
thickness: Theme.barIconSize(battery.barThickness, -4, battery.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale)
fontSize: Theme.barTextSize(battery.barThickness, battery.barConfig?.fontScale, battery.barConfig?.maximizeWidgetText)
anchors.verticalCenter: parent.verticalCenter
@@ -301,7 +301,8 @@ Item {
thickness: root.statusIconSize
fontSize: root.textSize
hovered: groupArea.containsMouse
outlined: SettingsData.dankIslandBatteryStyle === "outline"
meterStyle: SettingsData.dankIslandBatteryStyle
levelColors: (SettingsData.islandBarConfig?.batteryColorMode ?? "theme") === "level"
}
DankIcon {
@@ -1483,6 +1483,29 @@ Item {
})
}
SettingsCard {
tab: "appearance"
iconName: "battery_horiz_075"
title: I18n.tr("Battery Colors", "bar appearance: battery indicator color card")
settingKey: "batteryColorMode"
visible: dankBarTab.appearanceOnly && selectedBarConfig?.enabled && BatteryService.batteryAvailable
SettingsButtonGroupRow {
tags: ["battery", "color", "level", "theme", "icon", "meter", "indicator"]
text: I18n.tr("Indicator Colors", "battery settings: battery indicator color mode")
description: I18n.tr("Follow the theme accent, or step from green to red as the battery drains.", "battery settings: indicator color mode description")
model: [I18n.tr("Theme", "battery settings: theme accent indicator colors"), I18n.tr("Level", "battery settings: charge level indicator colors")]
currentIndex: (dankBarTab.selectedBarConfig?.batteryColorMode ?? "theme") === "level" ? 1 : 0
onSelectionChanged: (index, selected) => {
if (selected) {
SettingsData.updateBarConfig(dankBarTab.selectedBarId, {
batteryColorMode: index === 1 ? "level" : "theme"
});
}
}
}
}
SettingsCard {
tab: "appearance"
iconName: "filter_b_and_w"
@@ -28,7 +28,7 @@ Item {
readonly property int instanceIndex: Math.max(0, root.instanceChoices.findIndex(row => row.id === SettingsData.dankIslandBarId))
readonly property var systemLevelDisplayValues: ["icon", "percentage", "both"]
readonly property var paletteValues: ["default", "bright", "dim"]
readonly property var batteryStyleValues: ["solid", "outline"]
readonly property var batteryStyleValues: ["solid", "outline", "ring"]
readonly property var satellitePositionValues: ["island", "edges"]
readonly property var interactionModeValues: ["click", "hybrid"]
@@ -54,7 +54,7 @@ Item {
SettingsCard {
width: parent.width
iconName: "view_in_ar"
title: I18n.tr("Dank Island - Beta", "island settings: page title")
title: I18n.tr("Dank Island", "island settings: page title")
settingKey: "dankIslandInstance"
tags: ["island", "layout", "standard", "frame", "mode", "bar"]
@@ -321,11 +321,11 @@ Item {
SettingsButtonGroupRow {
settingKey: "dankIslandBatteryStyle"
tags: ["island", "battery", "gauge", "solid", "outline", "appearance"]
text: I18n.tr("Material Battery Style", "island settings: battery meter style row")
description: I18n.tr("Solid material type or Outline", "island settings: battery style description")
tags: ["island", "battery", "gauge", "solid", "outline", "ring", "circle", "appearance"]
text: I18n.tr("Battery Style", "island settings: battery meter style row")
description: I18n.tr("Solid or outlined material meter, or a circular gauge", "island settings: battery style description")
visible: BatteryService.batteryAvailable
model: [I18n.tr("Solid", "island settings: filled battery meter style"), I18n.tr("Outline", "island settings: outlined battery meter style")]
model: [I18n.tr("Solid", "island settings: filled battery meter style"), I18n.tr("Outline", "island settings: outlined battery meter style"), I18n.tr("Circle", "island settings: circular battery meter style")]
currentIndex: root.valueIndex(root.batteryStyleValues, SettingsData.dankIslandBatteryStyle, "solid")
onSelectionChanged: (index, selected) => {
if (selected)
+3 -3
View File
@@ -510,7 +510,7 @@ Item {
widgetObj.showBatteryTimeOnlyOnBattery = SettingsData.showBatteryTimeOnlyOnBattery;
widgetObj.showBatteryPowerCharging = SettingsData.showBatteryPowerCharging;
widgetObj.showBatteryPowerDischarging = SettingsData.showBatteryPowerDischarging;
widgetObj.batteryPillStyle = SettingsData.batteryPillStyle;
widgetObj.batteryStyle = SettingsData.batteryStyle;
}
if (widgetId === "runningApps") {
widgetObj.runningAppsCompactMode = SettingsData.runningAppsCompactMode;
@@ -1053,8 +1053,8 @@ Item {
item.showBatteryPowerCharging = widget.showBatteryPowerCharging;
if (widget.showBatteryPowerDischarging !== undefined)
item.showBatteryPowerDischarging = widget.showBatteryPowerDischarging;
if (widget.batteryPillStyle !== undefined)
item.batteryPillStyle = widget.batteryPillStyle;
if (widget.batteryStyle !== undefined)
item.batteryStyle = widget.batteryStyle;
if (widget.showPrinterIcon !== undefined)
item.showPrinterIcon = widget.showPrinterIcon;
if (widget.showScreenSharingIcon !== undefined)
@@ -3455,18 +3455,18 @@ Column {
}
}
Rectangle {
Column {
id: batteryStyleBlock
width: parent.width
height: Math.max(18, Theme.fontSizeSmall) + Theme.spacingM * 2
radius: Theme.cornerRadius
color: batteryPillArea.containsMouse ? Theme.primaryHover : Theme.withAlpha(Theme.primaryHover, 0)
leftPadding: Theme.spacingS
rightPadding: Theme.spacingS
topPadding: Theme.spacingXS
bottomPadding: Theme.spacingXS
spacing: Theme.spacingXS
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.right: batteryPillToggle.left
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
width: batteryStyleBlock.width - Theme.spacingS * 2
spacing: Theme.spacingS
clip: true
@@ -3478,7 +3478,7 @@ Column {
}
StyledText {
text: I18n.tr("Material Battery Style")
text: I18n.tr("Battery Style")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
@@ -3489,27 +3489,24 @@ Column {
}
}
DankToggle {
id: batteryPillToggle
anchors.right: parent.right
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
width: 40
height: 20
checked: batteryContextMenu.currentWidgetData?.batteryPillStyle ?? SettingsData.batteryPillStyle
onToggled: {
root.overflowSettingChanged(batteryContextMenu.sectionId, batteryContextMenu.widgetIndex, "batteryPillStyle", toggled);
}
}
DankButtonGroup {
id: batteryStyleGroup
MouseArea {
id: batteryPillArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: {
batteryPillToggle.checked = !batteryPillToggle.checked;
root.overflowSettingChanged(batteryContextMenu.sectionId, batteryContextMenu.widgetIndex, "batteryPillStyle", batteryPillToggle.checked);
readonly property var values: ["icon", "solid", "outline", "ring"]
model: [I18n.tr("Icon", "battery widget: system battery glyph"), I18n.tr("Solid", "island settings: filled battery meter style"), I18n.tr("Outline", "island settings: outlined battery meter style"), I18n.tr("Circle", "island settings: circular battery meter style")]
buttonHeight: 24
minButtonWidth: 44
maximumWidth: batteryStyleBlock.width - Theme.spacingS * 2
buttonPadding: 6
checkIconSize: 10
textSize: 10
spacing: 2
currentIndex: Math.max(0, batteryStyleGroup.values.indexOf(batteryContextMenu.currentWidgetData?.batteryStyle ?? SettingsData.batteryStyle))
onSelectionChanged: (index, selected) => {
if (!selected)
return;
root.overflowSettingChanged(batteryContextMenu.sectionId, batteryContextMenu.widgetIndex, "batteryStyle", batteryStyleGroup.values[index] ?? "icon");
}
}
}
+13
View File
@@ -181,6 +181,19 @@ Singleton {
readonly property bool hasBatteryReading: batteryAvailable && batteryLevel > 0
readonly property bool isLowBattery: hasBatteryReading && batteryLevel <= SettingsData.batteryLowThreshold
readonly property bool isCriticalBattery: hasBatteryReading && batteryLevel <= SettingsData.batteryCriticalThreshold
readonly property color levelCautionColor: "#FFC107"
readonly property color levelCriticalColor: "#F44336"
readonly property color levelColor: {
if (isCharging)
return Theme.success;
if (isCriticalBattery)
return levelCriticalColor;
if (isLowBattery)
return Theme.warning;
if (batteryLevel <= SettingsData.batteryLowThreshold * 2)
return levelCautionColor;
return Theme.success;
}
property bool _hasNotifiedLowBattery: false
property bool _hasNotifiedCriticalBattery: false
+103 -12
View File
@@ -14,9 +14,13 @@ Item {
property bool vertical: false
property bool showNumber: true
property bool showBolt: true
property bool outlined: false
property string meterStyle: "solid"
property bool levelColors: false
property real maxDiameter: 0
property bool hovered: false
readonly property bool outlined: root.meterStyle === "outline"
readonly property bool ring: root.meterStyle === "ring"
readonly property real unit: root.thickness / 14
readonly property real level: Math.max(0, Math.min(100, BatteryService.batteryLevel))
readonly property bool charging: BatteryService.batteryAvailable && BatteryService.isCharging
@@ -24,14 +28,16 @@ Item {
readonly property color fillColor: {
if (!BatteryService.batteryAvailable)
return Theme.surfaceVariant;
if (root.charging)
return Theme.success;
if (root.levelColors)
return BatteryService.levelColor;
if (root.lowState)
return Theme.error;
return Theme.isLightColor(Theme.primary) === Theme.isLightColor(Theme.surface) ? Theme.surfaceText : Theme.primary;
}
readonly property color dimColor: Theme.withAlpha(root.fillColor, root.hovered ? 0.6 : 0.48)
readonly property color trackColor: {
if (root.ring)
return Theme.withAlpha(root.fillColor, root.hovered ? 0.4 : 0.26);
if (root.outlined)
return root.hovered ? Theme.withAlpha(Theme.surfaceVariant, 0.45) : "transparent";
return root.dimColor;
@@ -45,15 +51,27 @@ Item {
readonly property string numberText: Math.round(root.level).toString()
readonly property bool boltVisible: root.charging && root.showBolt
readonly property bool numberInside: !root.vertical && root.showNumber && BatteryService.batteryAvailable
readonly property bool ringNumberVisible: root.ring && root.showNumber && !root.boltVisible && BatteryService.batteryAvailable
readonly property real strokeWidth: root.outlined ? 1.5 * root.unit : 0
readonly property real textCanvasLeft: 1.5 * root.unit
readonly property real textCanvasWidth: root.bodyLength - root.textCanvasLeft - 1.5 * root.unit
readonly property real textNeed: fitMetrics.advanceWidth
property real fontSize: Theme.fontSizeSmall
readonly property real baseTextSize: root.fontSize
readonly property real textSize: root.baseTextSize
readonly property real ringDiameter: {
const natural = Math.round(19 * root.unit);
if (root.maxDiameter <= 0)
return natural;
return Math.min(natural, Math.round(root.maxDiameter));
}
readonly property real ringStroke: Math.max(1, Math.round(root.ringDiameter * 2 / 19))
readonly property real ringRadius: (root.ringDiameter - root.ringStroke) / 2
readonly property real ringInnerRadius: Math.max(1, (root.ringDiameter - 2 * root.ringStroke) / 2)
readonly property real ringChordWidth: 2 * Math.sqrt(Math.max(1, Math.pow(root.ringInnerRadius, 2) - Math.pow(fitMetrics.tightBoundingRect.height / 2, 2)))
readonly property real ringTextSize: root.textNeed > 0 ? Math.min(root.baseTextSize, root.baseTextSize * root.ringChordWidth / root.textNeed) : root.baseTextSize
readonly property real textSize: root.ring ? root.ringTextSize : root.baseTextSize
readonly property real textBaseline: root.height / 2 - digitInk.tightBoundingRect.y - digitInk.tightBoundingRect.height / 2
readonly property real boltBadgeSize: Math.round(9 * root.unit)
readonly property real boltBadgeSize: Math.round((root.ring ? 12 : 9) * root.unit)
readonly property real boltBadgeWidth: Math.round(root.boltBadgeSize * (8 / 13))
readonly property real bodyLength: Math.max(Math.round(25 * root.unit), Math.ceil(root.numberInside ? root.textNeed + root.textCanvasLeft + 1.5 * root.unit : 0))
readonly property real capGap: Math.max(1, Math.round(root.unit))
@@ -61,8 +79,8 @@ Item {
readonly property real capBreadth: Math.max(1, Math.round(1.25 * root.unit))
readonly property real capSpan: Math.round(6 * root.unit)
implicitWidth: root.vertical ? Math.round(14 * root.unit) : root.capOffset + (root.boltVisible ? root.boltBadgeWidth : root.capBreadth)
implicitHeight: root.vertical ? root.capOffset + root.capBreadth : Math.round(14 * root.unit)
implicitWidth: root.ring ? root.ringDiameter : root.vertical ? Math.round(14 * root.unit) : root.capOffset + (root.boltVisible ? root.boltBadgeWidth : root.capBreadth)
implicitHeight: root.ring ? root.ringDiameter : root.vertical ? root.capOffset + root.capBreadth : Math.round(14 * root.unit)
StyledTextMetrics {
id: fitMetrics
@@ -124,6 +142,77 @@ Item {
}
}
Component {
id: ringGauge
Shape {
id: gauge
property real sweep: root.level * 3.6
anchors.fill: parent
preferredRendererType: Shape.CurveRenderer
Behavior on sweep {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.standardEasing
}
}
ShapePath {
fillColor: "transparent"
strokeColor: root.trackColor
strokeWidth: root.ringStroke
capStyle: ShapePath.FlatCap
PathAngleArc {
centerX: root.ringDiameter / 2
centerY: root.ringDiameter / 2
radiusX: root.ringRadius
radiusY: root.ringRadius
startAngle: -90
sweepAngle: 360
}
}
ShapePath {
fillColor: "transparent"
strokeColor: root.level > 0 ? root.fillColor : "transparent"
strokeWidth: root.ringStroke
capStyle: ShapePath.RoundCap
PathAngleArc {
centerX: root.ringDiameter / 2
centerY: root.ringDiameter / 2
radiusX: root.ringRadius
radiusY: root.ringRadius
startAngle: -90
sweepAngle: gauge.sweep
}
}
}
}
Loader {
active: root.ring
anchors.centerIn: parent
width: root.ringDiameter
height: root.ringDiameter
sourceComponent: ringGauge
}
NumericText {
isMonospace: false
visible: root.ringNumberVisible
x: (root.width - implicitWidth) / 2
y: root.textBaseline - baselineOffset
text: root.numberText
color: Theme.surfaceText
font.weight: root.glyphWeight
font.pixelSize: Math.max(1, root.textSize)
}
Rectangle {
id: cap
@@ -132,7 +221,7 @@ Item {
width: root.vertical ? root.capSpan : root.capBreadth
height: root.vertical ? root.capBreadth : root.capSpan
radius: root.capBreadth / 2
visible: root.vertical || !root.boltVisible
visible: !root.ring && (root.vertical || !root.boltVisible)
color: root.outlined ? root.fillColor : root.dimColor
}
@@ -144,6 +233,7 @@ Item {
width: root.vertical ? root.width : root.bodyLength
height: root.vertical ? root.bodyLength : root.height
radius: 4 * root.unit
visible: !root.ring
color: root.trackColor
border.width: root.strokeWidth
border.color: root.fillColor
@@ -157,6 +247,7 @@ Item {
width: frame.width - root.strokeWidth * 2
height: frame.height - root.strokeWidth * 2
radius: Math.max(0, frame.radius - root.strokeWidth)
visible: !root.ring
color: "transparent"
Rectangle {
@@ -207,7 +298,7 @@ Item {
}
Glyphs {
visible: root.numberInside
visible: root.numberInside && !root.ring
ink: Theme.surfaceText
}
@@ -217,7 +308,7 @@ Item {
width: fill.width
height: fill.height
clip: true
visible: root.numberInside && !root.outlined
visible: root.numberInside && !root.outlined && !root.ring
Glyphs {
x: -parent.x
@@ -228,8 +319,8 @@ Item {
Bolt {
visible: root.boltVisible
x: root.vertical ? (root.width - width) / 2 : root.capOffset
y: root.vertical ? frame.y + (frame.height - height) / 2 : (root.height - height) / 2
x: root.ring || root.vertical ? (root.width - width) / 2 : root.capOffset
y: !root.ring && root.vertical ? frame.y + (frame.height - height) / 2 : (root.height - height) / 2
fillColor: Theme.surfaceText
}
}
@@ -2460,6 +2460,44 @@
],
"icon": "toolbar"
},
{
"section": "batteryColorMode",
"label": "Battery Colors",
"tabIndex": 6,
"category": "Bar",
"keywords": [
"accent",
"appearance",
"bar",
"battery",
"charge",
"charging",
"color",
"colors",
"colour",
"colours",
"drains",
"follow",
"green",
"hue",
"icon",
"indicator",
"level",
"look",
"meter",
"palette",
"panel",
"power",
"statusbar",
"step",
"style",
"theme",
"tint",
"topbar"
],
"icon": "battery_horiz_075",
"description": "Follow the theme accent, or step from green to red as the battery drains."
},
{
"section": "barBorder",
"label": "Border",
@@ -12264,6 +12302,32 @@
"description": "Draw an island-styled background behind satellite widgets",
"conditionKey": "islandEnabled"
},
{
"section": "dankIslandBatteryStyle",
"label": "Battery Style",
"tabIndex": 46,
"category": "Island",
"keywords": [
"appearance",
"battery",
"charge",
"charging",
"circle",
"circular",
"gauge",
"island",
"material",
"meter",
"outline",
"outlined",
"power",
"ring",
"solid",
"style"
],
"description": "Solid or outlined material meter, or a circular gauge",
"conditionKey": "islandEnabled"
},
{
"section": "dankIslandInteraction",
"label": "Behavior",
@@ -12398,12 +12462,11 @@
},
{
"section": "dankIslandInstance",
"label": "Dank Island - Beta",
"label": "Dank Island",
"tabIndex": 46,
"category": "Island",
"keywords": [
"bar",
"beta",
"dank",
"frame",
"instance",
@@ -12715,28 +12778,6 @@
"description": "Show a clickable clock beside compact media details",
"conditionKey": "islandEnabled"
},
{
"section": "dankIslandBatteryStyle",
"label": "Material Battery Style",
"tabIndex": 46,
"category": "Island",
"keywords": [
"appearance",
"battery",
"charge",
"charging",
"gauge",
"island",
"material",
"outline",
"power",
"solid",
"style",
"type"
],
"description": "Solid material type or Outline",
"conditionKey": "islandEnabled"
},
{
"section": "dankIslandMotion",
"label": "Motion & Accessibility",