From 669f47858b39ce69224645c7543827c15cbab4e0 Mon Sep 17 00:00:00 2001 From: Julien Wadel Date: Fri, 3 Mar 2023 17:16:11 +0100 Subject: [PATCH] Fix Contact edit on messing mouse focus (regression from Qt 5.15.12) --- CHANGELOG.md | 8 +- .../ui/modules/Common/Form/ListForm.js | 1 + .../Common/Form/TransparentTextInput.qml | 213 ++++++++++-------- .../Common/Helpers/InvertedMouseArea.qml | 208 ++++++++--------- 4 files changed, 226 insertions(+), 204 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e07a0a005..36b535c87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Mute option for each chatrooms. - New Chat Layout. -## 5.0.11 - undefined + +## 5.0.12 - undefined + +## Fixed +- Unusable Contact sheet. + +## 5.0.11 - 2023-02-24 ### Fixed - Display of non-Ascii avatar diff --git a/linphone-app/ui/modules/Common/Form/ListForm.js b/linphone-app/ui/modules/Common/Form/ListForm.js index 0d87e3872..dd23558de 100644 --- a/linphone-app/ui/modules/Common/Form/ListForm.js +++ b/linphone-app/ui/modules/Common/Form/ListForm.js @@ -67,6 +67,7 @@ function addValue (value) { } function handleEditionFinished (index, text) { + if(index<0) return var model = values.model var oldValue = model.get(index).$value diff --git a/linphone-app/ui/modules/Common/Form/TransparentTextInput.qml b/linphone-app/ui/modules/Common/Form/TransparentTextInput.qml index 8ffd455f0..f9f9903c8 100644 --- a/linphone-app/ui/modules/Common/Form/TransparentTextInput.qml +++ b/linphone-app/ui/modules/Common/Form/TransparentTextInput.qml @@ -8,104 +8,117 @@ import Common.Styles 1.0 // ============================================================================= Item { - property alias color: textInput.color - property alias font: textInput.font - property alias inputMethodHints: textInput.inputMethodHints - property alias placeholder: placeholder.text - property alias readOnly: textInput.readOnly - property alias text: textInput.text - property bool forceFocus: false - property bool isInvalid: false - property int padding: TransparentTextInputStyle.padding - - // --------------------------------------------------------------------------- - - signal editingFinished - - // --------------------------------------------------------------------------- - - onActiveFocusChanged: { - if (activeFocus) { - textInput.focus = true - } - } - - Rectangle { - id: background - - color: (textInput.activeFocus || parent.forceFocus) && !textInput.readOnly - ? TransparentTextInputStyle.backgroundColor.color - : // No Style constant, see component name. - // It's a `transparent` TextInput. - 'transparent' - height: parent.height - width: { - var width = textInput.contentWidth + parent.padding * 2 - return width < parent.width ? width : parent.width - } - - InvertedMouseArea { - anchors.fill: parent - enabled: textInput.activeFocus - - onPressed: textInput.focus = false - } - } - - Icon { - id: icon - - anchors.left: background.right - height: background.height - icon: 'generic_error' - iconSize: TransparentTextInputStyle.iconSize - visible: parent.isInvalid - } - - Text { - id: placeholder - - anchors.centerIn: parent - color: TransparentTextInputStyle.placeholder.colorModel.color - elide: Text.ElideRight - - font { - italic: true - pointSize: TransparentTextInputStyle.placeholder.pointSize - } - - height: textInput.height - width: textInput.width - - verticalAlignment: Text.AlignVCenter - visible: textInput.text.length === 0 && (!textInput.activeFocus || textInput.readOnly) - } - - TextInput { - id: textInput - - anchors.centerIn: parent - height: parent.height - width: parent.width - parent.padding * 2 - - clip: true - color: activeFocus && !readOnly - ? TransparentTextInputStyle.text.color.focused.color - : TransparentTextInputStyle.text.color.normal.color - font.pointSize: TransparentTextInputStyle.text.pointSize - selectByMouse: true - verticalAlignment: TextInput.AlignVCenter - - Keys.onEscapePressed: focus = false - Keys.onEnterPressed: focus = false - Keys.onReturnPressed: focus = false - - onEditingFinished: { - cursorPosition = 0 - - if (!parent.readOnly) { - parent.editingFinished() - } - } - } + property alias color: textInput.color + property alias font: textInput.font + property alias inputMethodHints: textInput.inputMethodHints + property alias placeholder: placeholder.text + property alias readOnly: textInput.readOnly + property alias text: textInput.oldText + property bool forceFocus: false + property bool isInvalid: false + property int padding: TransparentTextInputStyle.padding + + // --------------------------------------------------------------------------- + + signal editingFinished + + // --------------------------------------------------------------------------- + + onActiveFocusChanged: { + if (activeFocus) { + textInput.forceActiveFocus() + } + } + + Rectangle { + id: background + property bool displayBackground: (textInput.activeFocus || parent.forceFocus) && !textInput.readOnly + color: displayBackground + ? TransparentTextInputStyle.backgroundColor.color + : // No Style constant, see component name. + // It's a `transparent` TextInput. + 'transparent' + height: parent.height + width: { + var width = textInput.contentWidth + parent.padding * 2 + return width < parent.width ? width : parent.width + } + + InvertedMouseArea { + anchors.fill: parent + enabled: textInput.activeFocus + + onPressed: textInput.updateText() + } + } + + Icon { + id: icon + + anchors.left: background.right + height: background.height + icon: 'generic_error' + iconSize: TransparentTextInputStyle.iconSize + visible: parent.isInvalid + } + + Text { + id: placeholder + + anchors.centerIn: parent + color: TransparentTextInputStyle.placeholder.colorModel.color + elide: Text.ElideRight + + font { + italic: true + pointSize: TransparentTextInputStyle.placeholder.pointSize + } + + height: textInput.height + width: textInput.width + + verticalAlignment: Text.AlignVCenter + visible: textInput.text.length === 0 && (!textInput.activeFocus || textInput.readOnly) + } + + TextInput { + id: textInput + + anchors.centerIn: parent + height: parent.height + width: parent.width - parent.padding * 2 + + clip: true + color: activeFocus && !readOnly + ? TransparentTextInputStyle.text.color.focused.color + : TransparentTextInputStyle.text.color.normal.color + font.pointSize: TransparentTextInputStyle.text.pointSize + selectByMouse: true + verticalAlignment: TextInput.AlignVCenter + + Keys.onEscapePressed: cancelText() + Keys.onEnterPressed: textInput.updateText() + Keys.onReturnPressed: textInput.updateText() + + property string oldText + onOldTextChanged: if(text!=oldText) text = oldText + function cancelText(){ + text = oldText + focus = false + } + function updateText(){ + cursorPosition = 0 + oldText = text + + if (!parent.readOnly) { + parent.editingFinished() + } + focus = false + } + onEditingFinished: { + if(focus){ + updateText() + } + } + } } diff --git a/linphone-app/ui/modules/Common/Helpers/InvertedMouseArea.qml b/linphone-app/ui/modules/Common/Helpers/InvertedMouseArea.qml index 33d9c0fca..5abaa3158 100644 --- a/linphone-app/ui/modules/Common/Helpers/InvertedMouseArea.qml +++ b/linphone-app/ui/modules/Common/Helpers/InvertedMouseArea.qml @@ -8,107 +8,109 @@ import Utils 1.0 // ============================================================================= Item { - id: item - - // --------------------------------------------------------------------------- - - property bool _mouseAlwaysOutside - property var _mouseArea: null - - // --------------------------------------------------------------------------- - - // When emitted, returns a function to test if the click - // is on a specific item. It takes only a item parameter. - signal pressed (var pointIsInItem) - - // --------------------------------------------------------------------------- - - function _createMouseArea () { - var parent = Utils.getTopParent(item, true) - - if (_mouseArea == null) { - _mouseArea = builder.createObject() - } - - _mouseArea.parent = parent - - // Must be true if a fake parent is used and if `mouseArea` - // is not in the same window that `item`. - _mouseAlwaysOutside = - _mouseArea.parent !== Utils.getTopParent(item) - } - - function _deleteMouseArea () { - if (_mouseArea != null) { - _mouseArea.destroy() - _mouseArea = null - } - } - - // --------------------------------------------------------------------------- - - // It's necessary to use a `enabled` variable. - // See: http://doc.qt.io/qt-5/qml-qtqml-component.html#completed-signal - // - // The creation order of components in a view is undefined, - // so the mouse area must be created only when the target component - // was completed. - Component.onCompleted: enabled && _createMouseArea() - Component.onDestruction: _deleteMouseArea() - - onEnabledChanged: { - _deleteMouseArea() - - if (enabled) { - _createMouseArea() - } - } - - Component { - id: builder - - MouseArea { - cursorShape: Qt.ArrowCursor - property var _timeout - - function _checkPosition (positionEvent) { - // Propagate event. - positionEvent.accepted = false - - // Click is outside or not. - if ( - _mouseAlwaysOutside || - !Utils.pointIsInItem(this, item, positionEvent) - ) { - if (_timeout != null) { - // Remove existing timeout to avoid the creation of - // many children. - Utils.clearTimeout(_timeout) - } - - // Use a asynchronous call that is executed - // after the propagated event. - // - // It's useful to ensure the window's context is not - // modified with the positionEvent before the `onPressed` - // call. - // - // The timeout is destroyed with the `MouseArea` component. - _timeout = Utils.setTimeout(this, 0, item.pressed.bind( - this, - (function (point, item) { - return Utils.pointIsInItem(this, item, point) - }).bind(this, { x: positionEvent.x, y: positionEvent.y }) - )) - } - } - - anchors.fill: parent - propagateComposedEvents: true - z: Constants.zMax - - onPressed: _checkPosition.call(this, mouse) - onWheel: _checkPosition.call(this, wheel) - } - } + id: mainItem + + // --------------------------------------------------------------------------- + + property bool _mouseAlwaysOutside + property var _mouseArea: null + + // --------------------------------------------------------------------------- + + // When emitted, returns a function to test if the click + // is on a specific item. It takes only a item parameter. + signal pressed (var pointIsInItem) + + // --------------------------------------------------------------------------- + + function _createMouseArea () { + var parent = Utils.getTopParent(mainItem, true) + + if (_mouseArea == null) { + _mouseArea = builder.createObject() + } + + _mouseArea.parent = parent + + // Must be true if a fake parent is used and if `mouseArea` + // is not in the same window that `item`. + _mouseAlwaysOutside = + _mouseArea.parent !== Utils.getTopParent(mainItem) + } + + function _deleteMouseArea () { + if (_mouseArea != null) { + _mouseArea.destroy() + _mouseArea = null + } + } + + // --------------------------------------------------------------------------- + + // It's necessary to use a `enabled` variable. + // See: http://doc.qt.io/qt-5/qml-qtqml-component.html#completed-signal + // + // The creation order of components in a view is undefined, + // so the mouse area must be created only when the target component + // was completed. + Component.onCompleted: enabled && _createMouseArea() + Component.onDestruction: _deleteMouseArea() + + onEnabledChanged: { + _deleteMouseArea() + + if (enabled) { + _createMouseArea() + } + } + + Component { + id: builder + + MouseArea { + id: area + cursorShape: Qt.ArrowCursor + preventStealing: true + property var _timeout + + function _checkPosition (positionEvent) { + // Propagate event. + positionEvent.accepted = false + + // Click is outside or not. + if ( + _mouseAlwaysOutside || + !Utils.pointIsInItem(area, mainItem, positionEvent) + ) { + if (_timeout != null) { + // Remove existing timeout to avoid the creation of + // many children. + Utils.clearTimeout(_timeout) + } + + // Use a asynchronous call that is executed + // after the propagated event. + // + // It's useful to ensure the window's context is not + // modified with the positionEvent before the `onPressed` + // call. + // + // The timeout is destroyed with the `MouseArea` component. + _timeout = Utils.setTimeout(area, 0, mainItem.pressed.bind( + this, + (function (point, item) { + return Utils.pointIsInItem(area, item, point) + }).bind(this, { x: positionEvent.x, y: positionEvent.y }) + )) + } + } + + anchors.fill: parent + propagateComposedEvents: true + z: Constants.zMax + + onPressed: _checkPosition.call(area, mouse) + onWheel: _checkPosition.call(area, wheel) + } + } }