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:
parent
ae94b325b1
commit
1c7e9271bf
5 changed files with 106 additions and 9 deletions
|
|
@ -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
|
## Zusammenfassung der geänderten Dateien
|
||||||
|
|
||||||
| Datei | Änderung |
|
| 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 |
|
| `TextField.qml` | Binding nach Windows-Workaround wiederherstellen |
|
||||||
| `ComboBox.qml` | Popup-Hintergrundfarbe für Dark Mode |
|
| `ComboBox.qml` | Popup-Hintergrundfarbe für Dark Mode |
|
||||||
| `DisplaySettingsLayout.qml` | Tippfehler bei Farbname korrigiert |
|
| `DisplaySettingsLayout.qml` | Tippfehler bei Farbname korrigiert |
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,7 @@ Control.ComboBox {
|
||||||
Layout.leftMargin: delegateImg.visible ? 0 : Utils.getSizeWithScreenRatio(5)
|
Layout.leftMargin: delegateImg.visible ? 0 : Utils.getSizeWithScreenRatio(5)
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
visible: mainItem.flagRole
|
visible: mainItem.flagRole
|
||||||
|
color: DefaultStyle.main2_600
|
||||||
font {
|
font {
|
||||||
family: DefaultStyle.flagFont
|
family: DefaultStyle.flagFont
|
||||||
pixelSize: mainItem.pixelSize
|
pixelSize: mainItem.pixelSize
|
||||||
|
|
@ -216,6 +217,7 @@ Control.ComboBox {
|
||||||
Layout.leftMargin: delegateImg.visible ? 0 : Utils.getSizeWithScreenRatio(flagItem.visble ? 5 : 25)
|
Layout.leftMargin: delegateImg.visible ? 0 : Utils.getSizeWithScreenRatio(flagItem.visble ? 5 : 25)
|
||||||
Layout.rightMargin: Utils.getSizeWithScreenRatio(20)
|
Layout.rightMargin: Utils.getSizeWithScreenRatio(20)
|
||||||
Layout.alignment: Qt.AlignCenter
|
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 : ""
|
text: typeof (modelData) != "undefined" ? mainItem.textRole ? modelData[mainItem.textRole] : modelData.text ? modelData.text : modelData : $modelData ? mainItem.textRole ? $modelData[mainItem.textRole] : $modelData : ""
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
maximumLineCount: 1
|
maximumLineCount: 1
|
||||||
|
|
|
||||||
|
|
@ -49,27 +49,57 @@ RowLayout {
|
||||||
Layout.preferredWidth: titleText.length > 0 ? Utils.getSizeWithScreenRatio(200) : undefined
|
Layout.preferredWidth: titleText.length > 0 ? Utils.getSizeWithScreenRatio(200) : undefined
|
||||||
Layout.fillWidth: titleText.length === 0
|
Layout.fillWidth: titleText.length === 0
|
||||||
oneLine: true
|
oneLine: true
|
||||||
valueRole: "value"
|
|
||||||
textRole: "text"
|
textRole: "text"
|
||||||
currentIndex: Utils.findIndex(model, function (entry) {
|
currentIndex: Utils.findIndex(model, function (entry) {
|
||||||
var currentVal = propertyOwnerGui
|
var currentVal = propertyOwnerGui
|
||||||
? propertyOwnerGui.core[mainItem.propertyName]
|
? propertyOwnerGui.core[mainItem.propertyName]
|
||||||
: propertyOwner[mainItem.propertyName]
|
: propertyOwner[mainItem.propertyName]
|
||||||
// Compare entry.value with the stored value (both are simple strings like "en", "orange")
|
// Handle different entry types
|
||||||
return entry.value === currentVal
|
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
|
var storedVal = propertyOwnerGui
|
||||||
? propertyOwnerGui.core[mainItem.propertyName]
|
? propertyOwnerGui.core[mainItem.propertyName]
|
||||||
: propertyOwner[mainItem.propertyName]
|
: propertyOwner[mainItem.propertyName]
|
||||||
// currentValue is now just the value string (e.g., "en", "orange")
|
// Determine if values differ based on entry type
|
||||||
binding.when = currentValue !== storedVal
|
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 {
|
Binding {
|
||||||
id: binding
|
id: binding
|
||||||
target: propertyOwnerGui ? propertyOwnerGui.core : propertyOwner
|
target: propertyOwnerGui ? propertyOwnerGui.core : propertyOwner
|
||||||
property: mainItem.propertyName
|
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
|
when: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ ColumnLayout {
|
||||||
//: "Haut-parleurs"
|
//: "Haut-parleurs"
|
||||||
text: qsTr("multimedia_settings_speaker_title")
|
text: qsTr("multimedia_settings_speaker_title")
|
||||||
font: Typography.p2l
|
font: Typography.p2l
|
||||||
|
color: DefaultStyle.main2_600
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -130,6 +131,7 @@ ColumnLayout {
|
||||||
//: "Microphone"
|
//: "Microphone"
|
||||||
text: qsTr("multimedia_settings_microphone_title")
|
text: qsTr("multimedia_settings_microphone_title")
|
||||||
font: Typography.p2l
|
font: Typography.p2l
|
||||||
|
color: DefaultStyle.main2_600
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -220,6 +222,7 @@ ColumnLayout {
|
||||||
//: "Caméra"
|
//: "Caméra"
|
||||||
text: qsTr("multimedia_settings_camera_title")
|
text: qsTr("multimedia_settings_camera_title")
|
||||||
font: Typography.p2l
|
font: Typography.p2l
|
||||||
|
color: DefaultStyle.main2_600
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -231,6 +234,7 @@ ColumnLayout {
|
||||||
entries: SettingsCpp.videoDevices
|
entries: SettingsCpp.videoDevices
|
||||||
propertyName: "videoDevice"
|
propertyName: "videoDevice"
|
||||||
propertyOwner: SettingsCpp
|
propertyOwner: SettingsCpp
|
||||||
|
textRole: ""
|
||||||
Connections {
|
Connections {
|
||||||
enabled: mainItem.call
|
enabled: mainItem.call
|
||||||
target: videoDevicesCbox
|
target: videoDevicesCbox
|
||||||
|
|
|
||||||
|
|
@ -333,7 +333,7 @@ AbstractWindow {
|
||||||
/************************* CONTENT ********************************/
|
/************************* CONTENT ********************************/
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: DefaultStyle.grey_900
|
color: "#1a1a1a" // Always dark for call window (video display)
|
||||||
focus: true
|
focus: true
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue