Fix: ComboSetting support for QStringList models and multiple model types

- Add type detection for three different model types:
  - QStringList (video devices): plain strings
  - Audio devices: objects with {id, display_name}
  - Simple entries (language, color): objects with {text, value}
- Set textRole: "" for video devices ComboBox (QStringList)
- Fix ComboBox popup background color for dark mode
- Add window background color to CallsWindow for dark mode
- Update documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Eduard Wisch 2026-02-08 07:41:28 +01:00
parent ae94b325b1
commit 1c7e9271bf
5 changed files with 106 additions and 9 deletions

View file

@ -202,11 +202,72 @@ AbstractWindow {
}
```
## 4. ComboSetting unterstützt verschiedene Model-Typen
### Problem
Die ComboSetting-Komponente konnte nur mit einem Model-Typ umgehen. Es gibt aber drei verschiedene Typen:
1. **Audio-Geräte**: `QVariantList` mit Objekten `{id: "...", display_name: "..."}`
2. **Einfache Einträge**: `QVariantList` mit Objekten `{text: "...", value: "..."}`
3. **Video-Geräte**: `QStringList` mit einfachen Strings
### Symptome
- Audio-Geräte (Lautsprecher, Mikrofon, Klingel) wurden nach Neustart nicht korrekt angezeigt
- Kamera-Dropdown zeigte keine Gerätenamen an (nur leere Einträge)
### Lösung
**Datei:** `Linphone/view/Control/Button/Settings/ComboSetting.qml`
Die `currentIndex`-Berechnung, `onCurrentIndexChanged` und `Binding.value` wurden erweitert, um alle drei Typen zu erkennen und korrekt zu verarbeiten:
```qml
currentIndex: Utils.findIndex(model, function (entry) {
var currentVal = propertyOwnerGui
? propertyOwnerGui.core[mainItem.propertyName]
: propertyOwner[mainItem.propertyName]
// Handle different entry types
if (typeof entry === 'string') {
// QStringList (video devices): compare strings directly
return entry === currentVal
} else if (entry.id !== undefined) {
// Audio devices: compare by id
return currentVal && entry.id === currentVal.id
} else if (entry.value !== undefined) {
// Simple entries (language, color): compare by value
return entry.value === currentVal
}
return false
})
```
**Datei:** `Linphone/view/Control/Form/Settings/MultimediaSettings.qml`
Für die Kamera-ComboBox wurde `textRole: ""` gesetzt, da QStringList-Models keine Properties haben:
```qml
ComboSetting {
id: videoDevicesCbox
entries: SettingsCpp.videoDevices
propertyName: "videoDevice"
propertyOwner: SettingsCpp
textRole: "" // Empty for QStringList models
// ...
}
```
### Technischer Hintergrund
| Model-Typ | Beispiel | Vergleich | Rückgabewert |
|-----------|----------|-----------|--------------|
| QStringList | `"V4L2: /dev/video0"` | `entry === currentVal` | `entry` (String) |
| Audio-Geräte | `{id: "pulse:0", display_name: "Speaker"}` | `entry.id === currentVal.id` | `entry` (Objekt) |
| Einfache Einträge | `{text: "English", value: "en"}` | `entry.value === currentVal` | `entry.value` (String) |
## Zusammenfassung der geänderten Dateien
| Datei | Änderung |
|-------|----------|
| `ComboSetting.qml` | valueRole/textRole und korrekter Wertvergleich |
| `ComboSetting.qml` | Unterstützung für drei Model-Typen (QStringList, Audio-Geräte, einfache Einträge) |
| `MultimediaSettings.qml` | `textRole: ""` für Kamera-ComboBox |
| `TextField.qml` | Binding nach Windows-Workaround wiederherstellen |
| `ComboBox.qml` | Popup-Hintergrundfarbe für Dark Mode |
| `DisplaySettingsLayout.qml` | Tippfehler bei Farbname korrigiert |

View file

@ -204,6 +204,7 @@ Control.ComboBox {
Layout.leftMargin: delegateImg.visible ? 0 : Utils.getSizeWithScreenRatio(5)
Layout.alignment: Qt.AlignCenter
visible: mainItem.flagRole
color: DefaultStyle.main2_600
font {
family: DefaultStyle.flagFont
pixelSize: mainItem.pixelSize
@ -216,6 +217,7 @@ Control.ComboBox {
Layout.leftMargin: delegateImg.visible ? 0 : Utils.getSizeWithScreenRatio(flagItem.visble ? 5 : 25)
Layout.rightMargin: Utils.getSizeWithScreenRatio(20)
Layout.alignment: Qt.AlignCenter
color: DefaultStyle.main2_600
text: typeof (modelData) != "undefined" ? mainItem.textRole ? modelData[mainItem.textRole] : modelData.text ? modelData.text : modelData : $modelData ? mainItem.textRole ? $modelData[mainItem.textRole] : $modelData : ""
elide: Text.ElideRight
maximumLineCount: 1

View file

@ -49,27 +49,57 @@ RowLayout {
Layout.preferredWidth: titleText.length > 0 ? Utils.getSizeWithScreenRatio(200) : undefined
Layout.fillWidth: titleText.length === 0
oneLine: true
valueRole: "value"
textRole: "text"
currentIndex: Utils.findIndex(model, function (entry) {
var currentVal = propertyOwnerGui
? propertyOwnerGui.core[mainItem.propertyName]
: propertyOwner[mainItem.propertyName]
// Compare entry.value with the stored value (both are simple strings like "en", "orange")
return entry.value === currentVal
// Handle different entry types
if (typeof entry === 'string') {
// QStringList (video devices): compare strings directly
return entry === currentVal
} else if (entry.id !== undefined) {
// Audio devices: compare by id
return currentVal && entry.id === currentVal.id
} else if (entry.value !== undefined) {
// Simple entries (language, color): compare by value
return entry.value === currentVal
}
return false
})
onCurrentValueChanged: {
onCurrentIndexChanged: {
if (currentIndex < 0 || !model) return
var entry = model[currentIndex]
if (!entry) return
var storedVal = propertyOwnerGui
? propertyOwnerGui.core[mainItem.propertyName]
: propertyOwner[mainItem.propertyName]
// currentValue is now just the value string (e.g., "en", "orange")
binding.when = currentValue !== storedVal
// Determine if values differ based on entry type
if (typeof entry === 'string') {
// QStringList: compare strings directly
binding.when = entry !== storedVal
} else if (entry.id !== undefined) {
// Audio devices: compare by id
binding.when = !storedVal || entry.id !== storedVal.id
} else if (entry.value !== undefined) {
// Simple entries: compare by value
binding.when = entry.value !== storedVal
}
}
Binding {
id: binding
target: propertyOwnerGui ? propertyOwnerGui.core : propertyOwner
property: mainItem.propertyName
value: comboBox.currentValue
value: {
if (comboBox.currentIndex < 0 || !comboBox.model) return undefined
var entry = comboBox.model[comboBox.currentIndex]
if (!entry) return undefined
// Return based on entry type
if (typeof entry === 'string') return entry // QStringList
if (entry.id !== undefined) return entry // Audio devices
if (entry.value !== undefined) return entry.value // Simple entries
return entry
}
when: false
}
}

View file

@ -79,6 +79,7 @@ ColumnLayout {
//: "Haut-parleurs"
text: qsTr("multimedia_settings_speaker_title")
font: Typography.p2l
color: DefaultStyle.main2_600
Layout.fillWidth: true
}
}
@ -130,6 +131,7 @@ ColumnLayout {
//: "Microphone"
text: qsTr("multimedia_settings_microphone_title")
font: Typography.p2l
color: DefaultStyle.main2_600
Layout.fillWidth: true
}
}
@ -220,6 +222,7 @@ ColumnLayout {
//: "Caméra"
text: qsTr("multimedia_settings_camera_title")
font: Typography.p2l
color: DefaultStyle.main2_600
Layout.fillWidth: true
}
}
@ -231,6 +234,7 @@ ColumnLayout {
entries: SettingsCpp.videoDevices
propertyName: "videoDevice"
propertyOwner: SettingsCpp
textRole: ""
Connections {
enabled: mainItem.call
target: videoDevicesCbox

View file

@ -333,7 +333,7 @@ AbstractWindow {
/************************* CONTENT ********************************/
Rectangle {
anchors.fill: parent
color: DefaultStyle.grey_900
color: "#1a1a1a" // Always dark for call window (video display)
focus: true
ColumnLayout {