mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-06-11 00:47:22 +00:00
Harden quick settings and volume OSD models
Clamp quick settings page math to valid bounds and guard volume OSD\ncontrols when PulseAudio objects are absent. Remove unused delegate\nrequired properties tied to enabled state.
This commit is contained in:
parent
9a3db42f74
commit
dd3e366e17
8 changed files with 40 additions and 24 deletions
|
|
@ -29,12 +29,12 @@ Item {
|
|||
|
||||
required property QS.QuickSettingsModel quickSettingsModel
|
||||
|
||||
readonly property real columns: Math.round(Math.min(6, Math.max(ShellSettings.Settings.quickSettingsColumns, width / intendedColumnWidth)))
|
||||
readonly property real columnWidth: Math.floor(width / columns)
|
||||
readonly property int minimizedColumns: Math.round(Math.min(8, Math.max(5, width / intendedMinimizedColumnWidth)))
|
||||
readonly property real minimizedColumnWidth: Math.floor(width / minimizedColumns)
|
||||
readonly property int columns: Math.max(1, Math.round(Math.min(6, Math.max(ShellSettings.Settings.quickSettingsColumns, width / intendedColumnWidth))))
|
||||
readonly property real columnWidth: Math.max(1, Math.floor(width / columns))
|
||||
readonly property int minimizedColumns: Math.max(1, Math.round(Math.min(8, Math.max(5, width / intendedMinimizedColumnWidth))))
|
||||
readonly property real minimizedColumnWidth: Math.max(1, Math.floor(width / minimizedColumns))
|
||||
|
||||
readonly property real rowHeight: columnWidth * 0.7
|
||||
readonly property real rowHeight: Math.max(1, columnWidth * 0.7)
|
||||
readonly property real fullHeight: fullView.implicitHeight
|
||||
|
||||
readonly property real intendedColumnWidth: Kirigami.Units.gridUnit * 7
|
||||
|
|
@ -43,16 +43,17 @@ Item {
|
|||
|
||||
property real fullViewProgress: 1
|
||||
|
||||
readonly property int columnCount: Math.floor(width/columnWidth)
|
||||
readonly property int columnCount: Math.max(1, Math.floor(width / columnWidth))
|
||||
readonly property int rowCount: {
|
||||
let totalRows = Math.ceil(quickSettingsCount / columnCount);
|
||||
let maxRows = root.isConvergence ? 3 : 5; // more than 5 is just disorienting
|
||||
let targetRows = Math.floor(Window.height * (root.isConvergence ? 0.42 : 0.65) / rowHeight);
|
||||
let targetRows = Math.max(1, Math.floor(Window.height * (root.isConvergence ? 0.42 : 0.65) / rowHeight));
|
||||
return Math.max(1, Math.min(maxRows, Math.min(totalRows, targetRows)));
|
||||
}
|
||||
|
||||
readonly property int pageSize: rowCount * columnCount
|
||||
readonly property int quickSettingsCount: quickSettingsModel.count
|
||||
readonly property int pageSize: Math.max(1, rowCount * columnCount)
|
||||
readonly property int quickSettingsCount: Math.max(0, quickSettingsModel.count)
|
||||
readonly property int pageCount: Math.max(1, Math.ceil(quickSettingsCount / pageSize))
|
||||
|
||||
// Management tiles — promoted to full-width status rows in convergence.
|
||||
readonly property var __managementCommands: ({
|
||||
|
|
@ -279,7 +280,7 @@ Item {
|
|||
Layout.maximumHeight: visible ? rowCount * rowHeight : 0
|
||||
|
||||
Repeater {
|
||||
model: Math.ceil(quickSettingsCount / pageSize)
|
||||
model: root.isConvergence ? 0 : root.pageCount
|
||||
delegate: Flow {
|
||||
id: flow
|
||||
spacing: 0
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ MobileShell.BaseItem {
|
|||
required property string text
|
||||
required property string status
|
||||
required property string icon
|
||||
required property bool enabled
|
||||
required property string settingsCommand
|
||||
required property var toggleFunction
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ Item {
|
|||
required property string text
|
||||
required property string status
|
||||
required property string icon
|
||||
required property bool enabled
|
||||
required property var toggleFunction
|
||||
property bool compact: false
|
||||
|
||||
|
|
|
|||
|
|
@ -32,12 +32,19 @@ Controls.AbstractButton {
|
|||
property string type // sink, source, source-output
|
||||
|
||||
property bool onlyOne: false
|
||||
property bool useVolumeObject: true
|
||||
|
||||
// Whether this item is selected
|
||||
readonly property bool supportsSelection: (baseItem.type == "sink" || baseItem.type == "source")
|
||||
readonly property bool selected: supportsSelection && (model.PulseObject.hasOwnProperty("default") ? model.PulseObject.default : false)
|
||||
readonly property bool hasPulseObject: model.PulseObject !== null && model.PulseObject !== undefined
|
||||
readonly property bool hasVolumeObject: useVolumeObject && hasPulseObject
|
||||
readonly property bool selected: supportsSelection && hasPulseObject && (model.PulseObject.hasOwnProperty("default") ? model.PulseObject.default : false)
|
||||
|
||||
onClicked: {
|
||||
if (!hasPulseObject || !model.PulseObject.hasOwnProperty("default")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set as the default audio device
|
||||
model.PulseObject.default = true
|
||||
}
|
||||
|
|
@ -175,7 +182,7 @@ Controls.AbstractButton {
|
|||
visible: model.HasVolume !== false // Devices always have volume but Streams don't necessarily
|
||||
enabled: model.VolumeWritable
|
||||
muted: model.Muted
|
||||
volumeObject: model.PulseObject
|
||||
volumeObject: baseItem.hasVolumeObject ? model.PulseObject : null
|
||||
activeFocusOnTab: false // access from delegate
|
||||
|
||||
value: to, model.Volume
|
||||
|
|
@ -215,7 +222,7 @@ Controls.AbstractButton {
|
|||
}
|
||||
PlasmaComponents.Label {
|
||||
id: percentText
|
||||
readonly property real value: model.PulseObject.volume > slider.to ? model.PulseObject.volume : slider.value
|
||||
readonly property real value: baseItem.hasVolumeObject && model.PulseObject.volume > slider.to ? model.PulseObject.volume : slider.value
|
||||
readonly property real displayValue: Math.round(value / PulseAudio.NormalVolume * 100.0)
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.minimumWidth: percentMetrics.advanceWidth
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ ListItemBase {
|
|||
|
||||
property QtObject devicesModel
|
||||
readonly property bool isEventStream: Name == "sink-input-by-media-role:event"
|
||||
useVolumeObject: !isEventStream
|
||||
|
||||
label: {
|
||||
if (isEventStream) {
|
||||
|
|
|
|||
|
|
@ -157,11 +157,13 @@ Window {
|
|||
anchors.leftMargin: Kirigami.Units.smallSpacing * 2
|
||||
anchors.rightMargin: Kirigami.Units.smallSpacing
|
||||
|
||||
property int volumePercent: PreferredDevice.sink.volume / PulseAudio.NormalVolume * 100.0
|
||||
readonly property bool hasSink: PreferredDevice.sink !== null
|
||||
property int volumePercent: hasSink ? PreferredDevice.sink.volume / PulseAudio.NormalVolume * 100.0 : 0
|
||||
|
||||
PlasmaComponents.ToolButton {
|
||||
icon.name: !PreferredDevice.sink || (PreferredDevice.sink.muted ? "audio-volume-muted" : MobileShell.AudioInfo.icon)
|
||||
text: !PreferredDevice.sink || (PreferredDevice.sink.muted ? i18n("Unmute") : i18n("Mute"))
|
||||
enabled: containerLayout.hasSink
|
||||
icon.name: containerLayout.hasSink ? (PreferredDevice.sink.muted ? "audio-volume-muted" : MobileShell.AudioInfo.icon) : "audio-volume-muted"
|
||||
text: containerLayout.hasSink && PreferredDevice.sink.muted ? i18n("Unmute") : i18n("Mute")
|
||||
display: Controls.AbstractButton.IconOnly
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
|
||||
|
|
@ -169,6 +171,9 @@ Window {
|
|||
Layout.rightMargin: Kirigami.Units.smallSpacing
|
||||
|
||||
onClicked: {
|
||||
if (!containerLayout.hasSink) {
|
||||
return;
|
||||
}
|
||||
hideTimer.restart();
|
||||
PreferredDevice.sink.muted = !PreferredDevice.sink.muted;
|
||||
}
|
||||
|
|
@ -185,11 +190,15 @@ Window {
|
|||
to: PulseAudio.NormalVolume
|
||||
stepSize: to / (to / PulseAudio.NormalVolume * 100.0)
|
||||
|
||||
volumeObject: PreferredDevice.sink
|
||||
muted: PreferredDevice.sink.muted
|
||||
value: PreferredDevice.sink.volume
|
||||
enabled: containerLayout.hasSink
|
||||
volumeObject: containerLayout.hasSink ? PreferredDevice.sink : null
|
||||
muted: containerLayout.hasSink ? PreferredDevice.sink.muted : false
|
||||
value: containerLayout.hasSink ? PreferredDevice.sink.volume : PulseAudio.MinimalVolume
|
||||
|
||||
onMoved: {
|
||||
if (!containerLayout.hasSink) {
|
||||
return;
|
||||
}
|
||||
PreferredDevice.sink.volume = value;
|
||||
PreferredDevice.sink.muted = value === 0;
|
||||
}
|
||||
|
|
@ -202,7 +211,7 @@ Window {
|
|||
// Otherwise it might be that the slider is at v10
|
||||
// whereas PA rejected the volume change and is
|
||||
// still at v15 (e.g.).
|
||||
value = Qt.binding(() => PreferredDevice.sink.volume);
|
||||
value = Qt.binding(() => containerLayout.hasSink ? PreferredDevice.sink.volume : PulseAudio.MinimalVolume);
|
||||
hideTimer.restart();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ Window {
|
|||
}
|
||||
PlasmaComponents.Label {
|
||||
text: i18n("Open audio settings")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ QHash<int, QByteArray> PaginateModel::roleNames() const
|
|||
|
||||
int PaginateModel::rowsByPageSize(int size) const
|
||||
{
|
||||
return d->m_hasStaticRowCount ? size : !d->m_sourceModel ? 0 : qMin(d->m_sourceModel->rowCount() - d->m_firstItem, size);
|
||||
return d->m_hasStaticRowCount ? qMax(size, 0) : !d->m_sourceModel ? 0 : qMax(qMin(d->m_sourceModel->rowCount() - d->m_firstItem, size), 0);
|
||||
}
|
||||
|
||||
int PaginateModel::rowCount(const QModelIndex &parent) const
|
||||
|
|
|
|||
Loading…
Reference in a new issue