1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

Re-style control center & sliders to be more android 16-y

This commit is contained in:
bbedward
2025-09-21 11:39:31 -04:00
parent 64c8e79bf2
commit babcc66765
20 changed files with 482 additions and 469 deletions

View File

@@ -140,9 +140,17 @@ Singleton {
property color background: currentThemeData.background property color background: currentThemeData.background
property color backgroundText: currentThemeData.backgroundText property color backgroundText: currentThemeData.backgroundText
property color outline: currentThemeData.outline property color outline: currentThemeData.outline
property color outlineVariant: currentThemeData.outlineVariant || Qt.rgba(outline.r, outline.g, outline.b, 0.6)
property color surfaceContainer: currentThemeData.surfaceContainer property color surfaceContainer: currentThemeData.surfaceContainer
property color surfaceContainerHigh: currentThemeData.surfaceContainerHigh property color surfaceContainerHigh: currentThemeData.surfaceContainerHigh
property color onSurface: surfaceText
property color onSurfaceVariant: surfaceVariantText
property color onPrimary: primaryText
property color onSurface_12: Qt.rgba(onSurface.r, onSurface.g, onSurface.b, 0.12)
property color onSurface_38: Qt.rgba(onSurface.r, onSurface.g, onSurface.b, 0.38)
property color onSurfaceVariant_30: Qt.rgba(onSurfaceVariant.r, onSurfaceVariant.g, onSurfaceVariant.b, 0.30)
property color error: currentThemeData.error || "#F2B8B5" property color error: currentThemeData.error || "#F2B8B5"
property color warning: currentThemeData.warning || "#FF9800" property color warning: currentThemeData.warning || "#FF9800"
property color info: currentThemeData.info || "#2196F3" property color info: currentThemeData.info || "#2196F3"

View File

@@ -23,6 +23,8 @@ DankPopout {
property string triggerSection: "right" property string triggerSection: "right"
property var triggerScreen: null property var triggerScreen: null
readonly property color _containerBg: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.60)
function setTriggerPosition(x, y, width, section, screen) { function setTriggerPosition(x, y, width, section, screen) {
triggerX = x triggerX = x
triggerY = y triggerY = y
@@ -240,20 +242,81 @@ DankPopout {
anchors.rightMargin: Theme.spacingL anchors.rightMargin: Theme.spacingL
spacing: Theme.spacingS spacing: Theme.spacingS
Battery { Rectangle {
widgetHeight: 40 width: batteryContentRow.implicitWidth + Theme.spacingS * 2
popupTarget: controlCenterBatteryPopout height: 40
parentScreen: root.triggerScreen radius: 20
section: "right" color: batteryMouseArea.containsMouse ? Qt.rgba(
barHeight: 123 Theme.primary.r,
batteryPopupVisible: controlCenterBatteryPopout.shouldBeVisible Theme.primary.g,
Theme.primary.b,
0.12) : Qt.rgba(
Theme.surfaceVariant.r,
Theme.surfaceVariant.g,
Theme.surfaceVariant.b,
0.5)
visible: BatteryService.batteryAvailable visible: BatteryService.batteryAvailable
onToggleBatteryPopup: { Row {
if (controlCenterBatteryPopout.shouldBeVisible) { id: batteryContentRow
controlCenterBatteryPopout.close() anchors.centerIn: parent
} else { spacing: Theme.spacingXS
controlCenterBatteryPopout.open()
DankIcon {
name: Theme.getBatteryIcon(BatteryService.batteryLevel, BatteryService.isCharging, BatteryService.batteryAvailable)
size: Theme.iconSize - 2
color: {
if (batteryMouseArea.containsMouse) {
return Theme.primary
}
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
return Theme.error
}
if (BatteryService.isCharging || BatteryService.isPluggedIn) {
return Theme.primary
}
return Theme.surfaceText
}
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: `${BatteryService.batteryLevel}%`
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: {
if (batteryMouseArea.containsMouse) {
return Theme.primary
}
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
return Theme.error
}
if (BatteryService.isCharging) {
return Theme.primary
}
return Theme.surfaceText
}
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: batteryMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
const globalPos = mapToGlobal(0, 0)
const currentScreen = root.triggerScreen || Screen
const screenX = currentScreen.x || 0
const relativeX = globalPos.x - screenX
controlCenterBatteryPopout.setTriggerPosition(relativeX, 123 + Theme.spacingXS, width, "right", currentScreen)
if (controlCenterBatteryPopout.shouldBeVisible) {
controlCenterBatteryPopout.close()
} else {
controlCenterBatteryPopout.open()
}
} }
} }
} }
@@ -548,7 +611,8 @@ DankPopout {
spacing: Theme.spacingM spacing: Theme.spacingM
AudioSliderRow { AudioSliderRow {
width: SettingsData.hideBrightnessSlider ? parent.width - Theme.spacingM : (parent.width - Theme.spacingM) / 2 width: SettingsData.hideBrightnessSlider ? parent.width - Theme.spacingS : (parent.width - Theme.spacingM) / 2
property color sliderTrackColor: root._containerBg
} }
Item { Item {
@@ -572,45 +636,12 @@ DankPopout {
NetworkPill { NetworkPill {
width: (parent.width - Theme.spacingM) / 2 width: (parent.width - Theme.spacingM) / 2
expanded: root.expandedSection === "network" expanded: root.expandedSection === "network"
onClicked: {
if (NetworkService.wifiToggling) {
return
}
if (NetworkService.networkStatus === "ethernet") {
if (NetworkService.ethernetConnected && !NetworkService.wifiEnabled) {
NetworkService.toggleWifiRadio()
return
}
root.toggleSection("network")
return
}
if (NetworkService.networkStatus === "wifi") {
if (NetworkService.ethernetConnected) {
NetworkService.toggleWifiRadio()
return
}
NetworkService.disconnectWifi()
return
}
if (!NetworkService.wifiEnabled) {
NetworkService.toggleWifiRadio()
return
}
if (NetworkService.wifiEnabled && NetworkService.networkStatus === "disconnected") {
root.toggleSection("network")
}
}
onExpandClicked: root.toggleSection("network") onExpandClicked: root.toggleSection("network")
} }
BluetoothPill { BluetoothPill {
width: (parent.width - Theme.spacingM) / 2 width: (parent.width - Theme.spacingM) / 2
expanded: root.expandedSection === "bluetooth" expanded: root.expandedSection === "bluetooth"
onClicked: {
if (!BluetoothService.available) return
if (BluetoothService.adapter)
BluetoothService.adapter.enabled = !BluetoothService.adapter.enabled
}
onExpandClicked: { onExpandClicked: {
if (!BluetoothService.available) return if (!BluetoothService.available) return
root.toggleSection("bluetooth") root.toggleSection("bluetooth")
@@ -650,22 +681,12 @@ DankPopout {
AudioOutputPill { AudioOutputPill {
width: (parent.width - Theme.spacingM) / 2 width: (parent.width - Theme.spacingM) / 2
expanded: root.expandedSection === "audio_output" expanded: root.expandedSection === "audio_output"
onClicked: {
if (AudioService.sink) {
AudioService.sink.audio.muted = !AudioService.sink.audio.muted
}
}
onExpandClicked: root.toggleSection("audio_output") onExpandClicked: root.toggleSection("audio_output")
} }
AudioInputPill { AudioInputPill {
width: (parent.width - Theme.spacingM) / 2 width: (parent.width - Theme.spacingM) / 2
expanded: root.expandedSection === "audio_input" expanded: root.expandedSection === "audio_input"
onClicked: {
if (AudioService.source) {
AudioService.source.audio.muted = !AudioService.source.audio.muted
}
}
onExpandClicked: root.toggleSection("audio_input") onExpandClicked: root.toggleSection("audio_input")
} }
} }

View File

@@ -59,6 +59,7 @@ Rectangle {
showValue: true showValue: true
valueOverride: actualVolumePercent valueOverride: actualVolumePercent
visible: AudioService.source && AudioService.source.audio visible: AudioService.source && AudioService.source.audio
thumbOutlineColor: Theme.surfaceContainer
onSliderValueChanged: function(newValue) { onSliderValueChanged: function(newValue) {
if (AudioService.source && AudioService.source.audio) { if (AudioService.source && AudioService.source.audio) {

View File

@@ -7,17 +7,17 @@ import qs.Services
import qs.Widgets import qs.Widgets
import qs.Modules.ControlCenter.Widgets import qs.Modules.ControlCenter.Widgets
BasePill { CompoundPill {
id: root id: root
property var defaultSource: AudioService.source property var defaultSource: AudioService.source
iconName: { iconName: {
if (!defaultSource) return "mic_off" if (!defaultSource) return "mic_off"
let volume = defaultSource.audio.volume let volume = defaultSource.audio.volume
let muted = defaultSource.audio.muted let muted = defaultSource.audio.muted
if (muted || volume === 0.0) return "mic_off" if (muted || volume === 0.0) return "mic_off"
return "mic" return "mic"
} }
@@ -41,6 +41,12 @@ BasePill {
return Math.round(defaultSource.audio.volume * 100) + "%" return Math.round(defaultSource.audio.volume * 100) + "%"
} }
onToggled: {
if (defaultSource && defaultSource.audio) {
defaultSource.audio.muted = !defaultSource.audio.muted
}
}
onWheelEvent: function (wheelEvent) { onWheelEvent: function (wheelEvent) {
if (!defaultSource || !defaultSource.audio) return if (!defaultSource || !defaultSource.audio) return
let delta = wheelEvent.angleDelta.y let delta = wheelEvent.angleDelta.y

View File

@@ -7,17 +7,17 @@ import qs.Services
import qs.Widgets import qs.Widgets
import qs.Modules.ControlCenter.Widgets import qs.Modules.ControlCenter.Widgets
BasePill { CompoundPill {
id: root id: root
property var defaultSink: AudioService.sink property var defaultSink: AudioService.sink
iconName: { iconName: {
if (!defaultSink) return "volume_off" if (!defaultSink) return "volume_off"
let volume = defaultSink.audio.volume let volume = defaultSink.audio.volume
let muted = defaultSink.audio.muted let muted = defaultSink.audio.muted
if (muted || volume === 0.0) return "volume_off" if (muted || volume === 0.0) return "volume_off"
if (volume <= 0.33) return "volume_down" if (volume <= 0.33) return "volume_down"
if (volume <= 0.66) return "volume_up" if (volume <= 0.66) return "volume_up"
@@ -43,6 +43,12 @@ BasePill {
return Math.round(defaultSink.audio.volume * 100) + "%" return Math.round(defaultSink.audio.volume * 100) + "%"
} }
onToggled: {
if (defaultSink && defaultSink.audio) {
defaultSink.audio.muted = !defaultSink.audio.muted
}
}
onWheelEvent: function (wheelEvent) { onWheelEvent: function (wheelEvent) {
if (!defaultSink || !defaultSink.audio) return if (!defaultSink || !defaultSink.audio) return
let delta = wheelEvent.angleDelta.y let delta = wheelEvent.angleDelta.y

View File

@@ -1,50 +0,0 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Services.Pipewire
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.ControlCenter.Widgets
SimpleSlider {
id: root
property var defaultSink: AudioService.sink
iconName: {
if (!defaultSink) return "volume_off"
let volume = defaultSink.audio.volume
let muted = defaultSink.audio.muted
if (muted || volume === 0.0) return "volume_off"
if (volume <= 0.33) return "volume_down"
if (volume <= 0.66) return "volume_up"
return "volume_up"
}
iconColor: defaultSink && !defaultSink.audio.muted && defaultSink.audio.volume > 0 ? Theme.primary : Theme.surfaceText
enabled: defaultSink !== null
allowIconClick: defaultSink !== null
value: defaultSink ? defaultSink.audio.volume : 0.0
maximumValue: 1.0
minimumValue: 0.0
onSliderValueChanged: function(newValue) {
if (defaultSink) {
defaultSink.audio.volume = newValue
if (newValue > 0 && defaultSink.audio.muted) {
defaultSink.audio.muted = false
}
}
}
onIconClicked: function() {
if (defaultSink) {
defaultSink.audio.muted = !defaultSink.audio.muted
}
}
}

View File

@@ -10,17 +10,18 @@ Row {
id: root id: root
property var defaultSink: AudioService.sink property var defaultSink: AudioService.sink
property color sliderTrackColor: "transparent"
height: 40 height: 40
spacing: Theme.spacingS spacing: 0
Rectangle { Rectangle {
width: Theme.iconSize + Theme.spacingS * 2 width: Theme.iconSize + Theme.spacingS * 2
height: Theme.iconSize + Theme.spacingS * 2 height: Theme.iconSize + Theme.spacingS * 2
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
radius: (Theme.iconSize + Theme.spacingS * 2) / 2 // Make it circular radius: (Theme.iconSize + Theme.spacingS * 2) / 2
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
Behavior on color { Behavior on color {
ColorAnimation { duration: Theme.shortDuration } ColorAnimation { duration: Theme.shortDuration }
} }
@@ -42,10 +43,10 @@ Row {
anchors.centerIn: parent anchors.centerIn: parent
name: { name: {
if (!defaultSink) return "volume_off" if (!defaultSink) return "volume_off"
let volume = defaultSink.audio.volume let volume = defaultSink.audio.volume
let muted = defaultSink.audio.muted let muted = defaultSink.audio.muted
if (muted || volume === 0.0) return "volume_off" if (muted || volume === 0.0) return "volume_off"
if (volume <= 0.33) return "volume_down" if (volume <= 0.33) return "volume_down"
if (volume <= 0.66) return "volume_up" if (volume <= 0.66) return "volume_up"
@@ -60,7 +61,7 @@ Row {
readonly property real actualVolumePercent: defaultSink ? Math.round(defaultSink.audio.volume * 100) : 0 readonly property real actualVolumePercent: defaultSink ? Math.round(defaultSink.audio.volume * 100) : 0
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: parent.width - (Theme.iconSize + Theme.spacingS * 2) - Theme.spacingM width: parent.width - (Theme.iconSize + Theme.spacingS * 2)
enabled: defaultSink !== null enabled: defaultSink !== null
minimum: 0 minimum: 0
maximum: 100 maximum: 100
@@ -68,6 +69,8 @@ Row {
showValue: true showValue: true
unit: "%" unit: "%"
valueOverride: actualVolumePercent valueOverride: actualVolumePercent
thumbOutlineColor: Theme.surfaceContainer
trackColor: root.sliderTrackColor.a > 0 ? root.sliderTrackColor : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.60)
onSliderValueChanged: function(newValue) { onSliderValueChanged: function(newValue) {
if (defaultSink) { if (defaultSink) {
defaultSink.audio.volume = newValue / 100.0 defaultSink.audio.volume = newValue / 100.0

View File

@@ -1,152 +0,0 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Common
import qs.Widgets
Rectangle {
id: root
property string iconName: ""
property color iconColor: Theme.surfaceText
property string primaryText: ""
property string secondaryText: ""
property bool expanded: false
property bool isActive: false
property bool showExpandArea: true
signal clicked()
signal expandClicked()
signal wheelEvent(var wheelEvent)
width: parent ? parent.width : 200
height: 60
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.6)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1
Rectangle {
id: mainArea
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
width: parent.width - (root.showExpandArea ? expandArea.width : 0)
topLeftRadius: Theme.cornerRadius
bottomLeftRadius: Theme.cornerRadius
topRightRadius: root.showExpandArea ? 0 : Theme.cornerRadius
bottomRightRadius: root.showExpandArea ? 0 : Theme.cornerRadius
color: showExpandArea && mainAreaMouse.containsMouse ?
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
"transparent"
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
Row {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.leftMargin: Theme.spacingM
anchors.rightMargin: Theme.spacingS
spacing: Theme.spacingS
DankIcon {
name: root.iconName
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
Column {
anchors.verticalCenter: parent.verticalCenter
width: parent.width - Theme.iconSize - Theme.spacingS
spacing: 2
StyledText {
width: parent.width
text: root.primaryText
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
StyledText {
width: parent.width
text: root.secondaryText
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
visible: text.length > 0
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
}
}
MouseArea {
id: mainAreaMouse
visible: root.showExpandArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.clicked()
onWheel: function (wheelEvent) {
root.wheelEvent(wheelEvent)
}
}
}
Rectangle {
id: expandArea
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
width: Theme.iconSize + Theme.spacingM * 2
visible: root.showExpandArea
topLeftRadius: 0
bottomLeftRadius: 0
topRightRadius: Theme.cornerRadius
bottomRightRadius: Theme.cornerRadius
color: expandAreaMouse.containsMouse ?
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
"transparent"
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
Rectangle {
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
width: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
}
DankIcon {
id: expandIcon
anchors.centerIn: parent
name: expanded ? "expand_less" : "expand_more"
size: Theme.iconSize - 2
color: Theme.surfaceVariantText
}
MouseArea {
id: expandAreaMouse
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.expandClicked()
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}

View File

@@ -6,14 +6,14 @@ import qs.Services
import qs.Widgets import qs.Widgets
import qs.Modules.ControlCenter.Widgets import qs.Modules.ControlCenter.Widgets
BasePill { CompoundPill {
id: root id: root
property var primaryDevice: { property var primaryDevice: {
if (!BluetoothService.adapter || !BluetoothService.adapter.devices) { if (!BluetoothService.adapter || !BluetoothService.adapter.devices) {
return null return null
} }
let devices = [...BluetoothService.adapter.devices.values.filter(dev => dev && (dev.paired || dev.trusted))] let devices = [...BluetoothService.adapter.devices.values.filter(dev => dev && (dev.paired || dev.trusted))]
for (let device of devices) { for (let device of devices) {
if (device && device.connected) { if (device && device.connected) {
@@ -64,4 +64,10 @@ BasePill {
} }
return "No devices" return "No devices"
} }
onToggled: {
if (BluetoothService.available && BluetoothService.adapter) {
BluetoothService.adapter.enabled = !BluetoothService.adapter.enabled
}
}
} }

View File

@@ -1,34 +0,0 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.ControlCenter.Widgets
SimpleSlider {
id: root
iconName: {
if (!DisplayService.brightnessAvailable) return "brightness_low"
let brightness = DisplayService.brightnessLevel
if (brightness <= 33) return "brightness_low"
if (brightness <= 66) return "brightness_medium"
return "brightness_high"
}
iconColor: DisplayService.brightnessAvailable && DisplayService.brightnessLevel > 0 ? Theme.primary : Theme.surfaceText
enabled: DisplayService.brightnessAvailable
value: DisplayService.brightnessLevel
maximumValue: 100.0
minimumValue: 0.0
onSliderValueChanged: function(newValue) {
if (DisplayService.brightnessAvailable) {
DisplayService.brightnessLevel = newValue
}
}
}

View File

@@ -9,7 +9,7 @@ Row {
id: root id: root
height: 40 height: 40
spacing: Theme.spacingS spacing: 0
Rectangle { Rectangle {
width: Theme.iconSize + Theme.spacingS * 2 width: Theme.iconSize + Theme.spacingS * 2
@@ -17,9 +17,9 @@ Row {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
radius: (Theme.iconSize + Theme.spacingS * 2) / 2 radius: (Theme.iconSize + Theme.spacingS * 2) / 2
color: iconArea.containsMouse color: iconArea.containsMouse
? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
: "transparent" : "transparent"
Behavior on color { Behavior on color {
ColorAnimation { duration: Theme.shortDuration } ColorAnimation { duration: Theme.shortDuration }
} }
@@ -29,7 +29,7 @@ Row {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: function(event) { onClicked: function(event) {
if (DisplayService.devices.length > 1) { if (DisplayService.devices.length > 1) {
if (deviceMenu.visible) { if (deviceMenu.visible) {
@@ -40,26 +40,26 @@ Row {
event.accepted = true event.accepted = true
} }
} }
}
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
name: { name: {
if (!DisplayService.brightnessAvailable) return "brightness_low" if (!DisplayService.brightnessAvailable) return "brightness_low"
let brightness = DisplayService.brightnessLevel let brightness = DisplayService.brightnessLevel
if (brightness <= 33) return "brightness_low" if (brightness <= 33) return "brightness_low"
if (brightness <= 66) return "brightness_medium" if (brightness <= 66) return "brightness_medium"
return "brightness_high" return "brightness_high"
}
size: Theme.iconSize
color: DisplayService.brightnessAvailable && DisplayService.brightnessLevel > 0 ? Theme.primary : Theme.surfaceText
} }
size: Theme.iconSize
color: DisplayService.brightnessAvailable && DisplayService.brightnessLevel > 0 ? Theme.primary : Theme.surfaceText
} }
} }
DankSlider { DankSlider {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: parent.width - (Theme.iconSize + Theme.spacingS * 2) - Theme.spacingM width: parent.width - (Theme.iconSize + Theme.spacingS * 2)
enabled: DisplayService.brightnessAvailable enabled: DisplayService.brightnessAvailable
minimum: 1 minimum: 1
maximum: 100 maximum: 100
@@ -79,6 +79,8 @@ Row {
DisplayService.setBrightness(newValue) DisplayService.setBrightness(newValue)
} }
} }
thumbOutlineColor: Theme.surfaceContainer
trackColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.60)
} }
Menu { Menu {

View File

@@ -0,0 +1,165 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Common
import qs.Widgets
Rectangle {
id: root
property string iconName: ""
property color iconColor: Theme.surfaceText
property string primaryText: ""
property string secondaryText: ""
property bool expanded: false
property bool isActive: false
property bool showExpandArea: true
signal toggled()
signal expandClicked()
signal wheelEvent(var wheelEvent)
width: parent ? parent.width : 220
height: 60
radius: Theme.cornerRadius
function hoverTint(base) {
const factor = 1.2
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor)
}
readonly property color _containerBg:
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b,
Theme.getContentBackgroundAlpha() * 0.60)
color: _containerBg
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.10)
border.width: 1
layer.enabled: true
layer.samples: 8
readonly property color _labelPrimary: Theme.surfaceText
readonly property color _labelSecondary: Theme.surfaceVariantText
readonly property color _tileBgActive: Theme.primary
readonly property color _tileBgInactive:
Qt.rgba(Theme.surface.r, Theme.surface.g, Theme.surface.b, 0.85)
readonly property color _tileRingActive:
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
readonly property color _tileRingInactive:
Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.18)
readonly property color _tileIconActive: Theme.primaryContainer
readonly property color _tileIconInactive: Theme.primary
property int _padH: Theme.spacingS
property int _tileSize: 48
property int _tileRadius: 14
Rectangle {
id: rightHoverOverlay
anchors.fill: parent
radius: root.radius
z: 0
visible: false
color: hoverTint(_containerBg)
opacity: 0.08
Behavior on opacity { NumberAnimation { duration: Theme.shortDuration } }
}
Row {
id: row
anchors.fill: parent
anchors.leftMargin: _padH
anchors.rightMargin: Theme.spacingM
spacing: Theme.spacingM
Rectangle {
id: iconTile
z: 1
width: _tileSize
height: _tileSize
anchors.verticalCenter: parent.verticalCenter
radius: _tileRadius
color: isActive ? _tileBgActive : _tileBgInactive
border.color: isActive ? _tileRingActive : "transparent"
border.width: isActive ? 1 : 0
Rectangle {
anchors.fill: parent
radius: _tileRadius
color: hoverTint(iconTile.color)
opacity: tileMouse.pressed ? 0.3 : (tileMouse.containsMouse ? 0.2 : 0.0)
visible: opacity > 0
Behavior on opacity { NumberAnimation { duration: Theme.shortDuration } }
}
DankIcon {
anchors.centerIn: parent
name: iconName
size: Theme.iconSize
color: isActive ? _tileIconActive : _tileIconInactive
}
MouseArea {
id: tileMouse
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.toggled()
}
}
Item {
id: body
width: row.width - iconTile.width - row.spacing
height: row.height
Column {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 2
StyledText {
width: parent.width
text: root.primaryText
color: _labelPrimary
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
StyledText {
width: parent.width
text: root.secondaryText
color: _labelSecondary
font.pixelSize: Theme.fontSizeSmall
visible: text.length > 0
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
}
MouseArea {
id: bodyMouse
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onEntered: { rightHoverOverlay.visible = true; rightHoverOverlay.opacity = 0.08 }
onExited: { rightHoverOverlay.opacity = 0.0; rightHoverOverlay.visible = false }
onPressed: rightHoverOverlay.opacity = 0.16
onReleased: rightHoverOverlay.opacity = containsMouse ? 0.08 : 0.0
onClicked: root.expandClicked()
onWheel: function (ev) {
root.wheelEvent(ev)
}
}
}
}
focus: true
Keys.onPressed: function (ev) {
if (ev.key === Qt.Key_Space || ev.key === Qt.Key_Return) { root.toggled(); ev.accepted = true }
else if (ev.key === Qt.Key_Right) { root.expandClicked(); ev.accepted = true }
}
}

View File

@@ -6,7 +6,7 @@ import qs.Services
import qs.Widgets import qs.Widgets
import qs.Modules.ControlCenter.Widgets import qs.Modules.ControlCenter.Widgets
BasePill { CompoundPill {
id: root id: root
isActive: { isActive: {
@@ -21,7 +21,7 @@ BasePill {
} }
return NetworkService.wifiEnabled return NetworkService.wifiEnabled
} }
iconName: { iconName: {
if (NetworkService.wifiToggling) { if (NetworkService.wifiToggling) {
return "sync" return "sync"
@@ -67,6 +67,12 @@ BasePill {
if (NetworkService.wifiEnabled) { if (NetworkService.wifiEnabled) {
return "Select network" return "Select network"
} }
return "Tap to enable" return ""
}
onToggled: {
if (NetworkService.networkStatus !== "ethernet" && !NetworkService.wifiToggling) {
NetworkService.toggleWifiRadio()
}
} }
} }

View File

@@ -1,50 +0,0 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Common
import qs.Widgets
Row {
id: root
property string iconName: ""
property color iconColor: Theme.surfaceText
property real value: 0.0
property real maximumValue: 1.0
property real minimumValue: 0.0
property bool enabled: true
property bool allowIconClick: false
signal sliderValueChanged(real value)
signal iconClicked()
height: 60
spacing: Theme.spacingM
DankIcon {
name: root.iconName
size: Theme.iconSize
color: root.iconColor
anchors.verticalCenter: parent.verticalCenter
MouseArea {
anchors.fill: parent
visible: root.allowIconClick
cursorShape: Qt.PointingHandCursor
onClicked: root.iconClicked()
}
}
DankSlider {
anchors.verticalCenter: parent.verticalCenter
width: {
if (parent.width <= 0) return 80
return Math.max(80, Math.min(400, parent.width - Theme.iconSize - Theme.spacingM))
}
enabled: root.enabled
minimum: Math.round(root.minimumValue * 100)
maximum: Math.round(root.maximumValue * 100)
value: Math.round(root.value * 100)
onSliderValueChanged: function(newValue) { root.sliderValueChanged(newValue / 100.0) }
}
}

View File

@@ -23,25 +23,31 @@ Rectangle {
border.width: 1 border.width: 1
opacity: enabled ? 1.0 : 0.6 opacity: enabled ? 1.0 : 0.6
function hoverTint(base) {
const factor = 1.2
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor)
}
readonly property color _containerBg:
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b,
Theme.getContentBackgroundAlpha() * 0.60)
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: mouseArea.containsMouse ? color: mouseArea.containsMouse ? hoverTint(_containerBg) : "transparent"
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : opacity: mouseArea.containsMouse ? 0.08 : 0.0
"transparent"
Behavior on opacity {
Behavior on color { NumberAnimation { duration: Theme.shortDuration }
ColorAnimation { duration: Theme.shortDuration }
} }
} }
Row { Row {
anchors.left: parent.left anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter anchors.leftMargin: Theme.spacingL + 2
anchors.right: parent.right
anchors.leftMargin: Theme.spacingM
anchors.rightMargin: Theme.spacingM anchors.rightMargin: Theme.spacingM
spacing: Theme.spacingS spacing: Theme.spacingM
DankIcon { DankIcon {
name: root.iconName name: root.iconName
@@ -50,29 +56,35 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
Column { Item {
anchors.verticalCenter: parent.verticalCenter width: parent.width - Theme.iconSize - parent.spacing
width: parent.width - Theme.iconSize - Theme.spacingS height: parent.height
spacing: 2
StyledText { Column {
width: parent.width anchors.left: parent.left
text: root.text anchors.right: parent.right
font.pixelSize: Theme.fontSizeMedium anchors.verticalCenter: parent.verticalCenter
color: Theme.surfaceText spacing: 2
font.weight: Font.Medium
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
StyledText { StyledText {
width: parent.width width: parent.width
text: root.secondaryText text: root.text
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText color: Theme.surfaceText
visible: text.length > 0 font.weight: Font.Medium
elide: Text.ElideRight elide: Text.ElideRight
wrapMode: Text.NoWrap wrapMode: Text.NoWrap
}
StyledText {
width: parent.width
text: root.secondaryText
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
visible: text.length > 0
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
} }
} }
} }

View File

@@ -78,6 +78,7 @@ DankOSD {
enabled: DisplayService.brightnessAvailable enabled: DisplayService.brightnessAvailable
showValue: true showValue: true
unit: "%" unit: "%"
thumbOutlineColor: Theme.surfaceContainer
Component.onCompleted: { Component.onCompleted: {
if (DisplayService.brightnessAvailable) { if (DisplayService.brightnessAvailable) {

View File

@@ -80,6 +80,7 @@ DankOSD {
enabled: AudioService.sink && AudioService.sink.audio enabled: AudioService.sink && AudioService.sink.audio
showValue: true showValue: true
unit: "%" unit: "%"
thumbOutlineColor: Theme.surfaceContainer
valueOverride: displayPercent valueOverride: displayPercent
Component.onCompleted: { Component.onCompleted: {

View File

@@ -757,6 +757,7 @@ Item {
unit: "" unit: ""
showValue: true showValue: true
wheelEnabled: false wheelEnabled: false
thumbOutlineColor: Theme.surfaceContainer
onSliderValueChanged: newValue => { onSliderValueChanged: newValue => {
SettingsData.setTopBarSpacing( SettingsData.setTopBarSpacing(
newValue) newValue)
@@ -784,6 +785,7 @@ Item {
unit: "" unit: ""
showValue: true showValue: true
wheelEnabled: false wheelEnabled: false
thumbOutlineColor: Theme.surfaceContainer
onSliderValueChanged: newValue => { onSliderValueChanged: newValue => {
SettingsData.setTopBarBottomGap( SettingsData.setTopBarBottomGap(
newValue) newValue)
@@ -811,6 +813,7 @@ Item {
unit: "" unit: ""
showValue: true showValue: true
wheelEnabled: false wheelEnabled: false
thumbOutlineColor: Theme.surfaceContainer
onSliderValueChanged: newValue => { onSliderValueChanged: newValue => {
SettingsData.setTopBarInnerPadding( SettingsData.setTopBarInnerPadding(
newValue) newValue)
@@ -838,6 +841,7 @@ Item {
unit: "" unit: ""
showValue: true showValue: true
wheelEnabled: false wheelEnabled: false
thumbOutlineColor: Theme.surfaceContainer
onSliderValueChanged: newValue => { onSliderValueChanged: newValue => {
SettingsData.setCornerRadius( SettingsData.setCornerRadius(
newValue) newValue)

View File

@@ -63,13 +63,6 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: root.showNetworkIcon visible: root.showNetworkIcon
RotationAnimation on rotation {
running: NetworkService.wifiToggling
loops: Animation.Infinite
from: 0
to: 360
duration: 1000
}
} }

View File

@@ -1,4 +1,5 @@
import QtQuick import QtQuick
import QtQuick.Effects
import qs.Common import qs.Common
import qs.Widgets import qs.Widgets
@@ -18,10 +19,14 @@ Item {
property real valueOverride: -1 property real valueOverride: -1
readonly property bool containsMouse: sliderMouseArea.containsMouse readonly property bool containsMouse: sliderMouseArea.containsMouse
property color thumbOutlineColor: Theme.surfaceContainer
property color trackColor: enabled ? Theme.outline : Theme.outline
function withAlpha(c, a) { return Qt.rgba(c.r, c.g, c.b, a) }
signal sliderValueChanged(int newValue) signal sliderValueChanged(int newValue)
signal sliderDragFinished(int finalValue) signal sliderDragFinished(int finalValue)
height: 40 height: 48
function updateValueFromPosition(x) { function updateValueFromPosition(x) {
let ratio = Math.max(0, Math.min(1, (x - sliderHandle.width / 2) / (sliderTrack.width - sliderHandle.width))) let ratio = Math.max(0, Math.min(1, (x - sliderHandle.width / 2) / (sliderTrack.width - sliderHandle.width)))
@@ -40,7 +45,7 @@ Item {
DankIcon { DankIcon {
name: slider.leftIcon name: slider.leftIcon
size: Theme.iconSize size: Theme.iconSize
color: slider.enabled ? Theme.surfaceText : Theme.surfaceVariantText color: slider.enabled ? Theme.onSurface : Theme.onSurface_38
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: slider.leftIcon.length > 0 visible: slider.leftIcon.length > 0
} }
@@ -52,85 +57,110 @@ Item {
property int rightIconWidth: slider.rightIcon.length > 0 ? Theme.iconSize : 0 property int rightIconWidth: slider.rightIcon.length > 0 ? Theme.iconSize : 0
width: parent.width - (leftIconWidth + rightIconWidth + (slider.leftIcon.length > 0 ? Theme.spacingM : 0) + (slider.rightIcon.length > 0 ? Theme.spacingM : 0)) width: parent.width - (leftIconWidth + rightIconWidth + (slider.leftIcon.length > 0 ? Theme.spacingM : 0) + (slider.rightIcon.length > 0 ? Theme.spacingM : 0))
height: 6 height: 12
radius: 3 radius: height / 2
color: slider.enabled ? Theme.surfaceVariantAlpha : Theme.surfaceLight color: slider.trackColor
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
clip: false
StyledRect { StyledRect {
id: sliderFill id: sliderFill
width: (parent.width - sliderHandle.width) * ((slider.value - slider.minimum) / (slider.maximum - slider.minimum)) + sliderHandle.width
height: parent.height height: parent.height
radius: parent.radius radius: parent.radius
color: slider.enabled ? Theme.primary : Theme.surfaceVariantText width: {
const ratio = (slider.value - slider.minimum) / (slider.maximum - slider.minimum)
Behavior on width { const travel = sliderTrack.width - sliderHandle.width
NumberAnimation { const center = (travel * ratio) + sliderHandle.width / 2
duration: Theme.shortDuration return Math.max(0, Math.min(sliderTrack.width, center))
easing.type: Theme.standardEasing
}
} }
color: slider.enabled ? Theme.primary : withAlpha(Theme.onSurface, 0.12)
} }
StyledRect { StyledRect {
id: sliderHandle id: sliderHandle
width: 18 property bool active: sliderMouseArea.containsMouse || sliderMouseArea.pressed || slider.isDragging
height: 18
radius: 9 width: 8
color: slider.enabled ? Theme.primary : Theme.surfaceVariantText height: 24
border.color: slider.enabled ? Qt.lighter(Theme.primary, 1.3) : Qt.lighter(Theme.surfaceVariantText, 1.3) radius: 4
border.width: 2 x: {
x: sliderFill.width - width const ratio = (slider.value - slider.minimum) / (slider.maximum - slider.minimum)
const travel = sliderTrack.width - width
return Math.max(0, Math.min(travel, travel * ratio))
}
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
scale: sliderMouseArea.containsMouse || sliderMouseArea.pressed ? 1.2 : 1 color: slider.enabled ? Theme.primary : withAlpha(Theme.onSurface, 0.12)
border.width: 3
border.color: slider.thumbOutlineColor
StyledRect {
anchors.fill: parent
radius: parent.radius
color: Theme.onPrimary
opacity: slider.enabled ? (sliderMouseArea.pressed ? 0.16 : (sliderMouseArea.containsMouse ? 0.08 : 0)) : 0
visible: opacity > 0
}
StyledRect { StyledRect {
anchors.centerIn: parent anchors.centerIn: parent
width: parent.width + 4 width: parent.width + 20
height: parent.height + 4 height: parent.height + 20
radius: width / 2 radius: width / 2
color: "transparent" color: "transparent"
border.color: Theme.primarySelected
border.width: 2 border.width: 2
visible: sliderMouseArea.containsMouse && slider.enabled border.color: Theme.primary
opacity: slider.enabled && slider.focus ? 0.3 : 0
visible: opacity > 0
} }
StyledRect { Rectangle {
id: valueTooltip id: ripple
anchors.centerIn: parent
width: 0
height: 0
radius: width / 2
color: Theme.onPrimary
opacity: 0
width: tooltipText.contentWidth + Theme.spacingS * 2 function start() {
height: tooltipText.contentHeight + Theme.spacingXS * 2 opacity = 0.16
radius: Theme.cornerRadius width = 0
color: Theme.surfaceContainer height = 0
border.color: Theme.outline rippleAnimation.start()
border.width: 1
anchors.bottom: parent.top
anchors.bottomMargin: Theme.spacingS
anchors.horizontalCenter: parent.horizontalCenter
visible: (sliderMouseArea.containsMouse && slider.showValue) || (slider.isDragging && slider.showValue)
opacity: visible ? 1 : 0
StyledText {
id: tooltipText
text: (slider.valueOverride >= 0 ? Math.round(slider.valueOverride) : slider.value) + slider.unit
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Medium
anchors.centerIn: parent
font.hintingPreference: Font.PreferFullHinting
} }
Behavior on opacity { SequentialAnimation {
id: rippleAnimation
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration target: ripple
easing.type: Theme.standardEasing properties: "width,height"
to: 28
duration: 180
}
NumberAnimation {
target: ripple
property: "opacity"
to: 0
duration: 150
} }
} }
} }
TapHandler {
acceptedButtons: Qt.LeftButton
onPressedChanged: {
if (pressed && slider.enabled) {
ripple.start()
}
}
}
scale: active ? 1.05 : 1.0
Behavior on scale { Behavior on scale {
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration duration: Theme.shortDuration
@@ -162,7 +192,7 @@ Item {
wheelEvent.accepted = false wheelEvent.accepted = false
return return
} }
let step = Math.max(1, (maximum - minimum) / 20) let step = Math.max(0.5, (maximum - minimum) / 100)
let newValue = wheelEvent.angleDelta.y > 0 ? Math.min(maximum, value + step) : Math.max(minimum, value - step) let newValue = wheelEvent.angleDelta.y > 0 ? Math.min(maximum, value + step) : Math.max(minimum, value - step)
newValue = Math.round(newValue) newValue = Math.round(newValue)
if (newValue !== value) { if (newValue !== value) {
@@ -197,12 +227,46 @@ Item {
} }
} }
} }
StyledRect {
id: valueTooltip
width: tooltipText.contentWidth + Theme.spacingS * 2
height: tooltipText.contentHeight + Theme.spacingXS * 2
radius: Theme.cornerRadius
color: Theme.surfaceContainer
border.color: Theme.outline
border.width: 1
anchors.bottom: parent.top
anchors.bottomMargin: Theme.spacingM
x: Math.max(0, Math.min(parent.width - width, sliderHandle.x + sliderHandle.width/2 - width/2))
visible: (sliderMouseArea.containsMouse && slider.showValue) || (slider.isDragging && slider.showValue)
opacity: visible ? 1 : 0
StyledText {
id: tooltipText
text: (slider.valueOverride >= 0 ? Math.round(slider.valueOverride) : slider.value) + slider.unit
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Medium
anchors.centerIn: parent
font.hintingPreference: Font.PreferFullHinting
}
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
} }
DankIcon { DankIcon {
name: slider.rightIcon name: slider.rightIcon
size: Theme.iconSize size: Theme.iconSize
color: slider.enabled ? Theme.surfaceText : Theme.surfaceVariantText color: slider.enabled ? Theme.onSurface : Theme.onSurface_38
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: slider.rightIcon.length > 0 visible: slider.rightIcon.length > 0
} }