diff --git a/linphone-desktop/resources.qrc b/linphone-desktop/resources.qrc index ceaaa203f..324a7aa96 100644 --- a/linphone-desktop/resources.qrc +++ b/linphone-desktop/resources.qrc @@ -325,13 +325,14 @@ ui/modules/Linphone/Styles/Notifications/NotificationReceivedFileMessageStyle.qml ui/modules/Linphone/Styles/Notifications/NotificationReceivedMessageStyle.qml ui/modules/Linphone/Styles/qmldir - ui/modules/Linphone/Styles/SmartSearchBar/SmartSearchBarStyle.qml ui/modules/Linphone/Styles/TelKeypad/TelKeypadStyle.qml ui/modules/Linphone/Styles/Timeline/TimelineStyle.qml + ui/modules/Linphone/Styles/View/SipAddressesViewStyle.qml ui/modules/Linphone/TelKeypad/TelKeypadButton.qml ui/modules/Linphone/TelKeypad/TelKeypad.qml ui/modules/Linphone/Timeline/Timeline.js ui/modules/Linphone/Timeline/Timeline.qml + ui/modules/Linphone/View/SipAddressesView.qml ui/scripts/LinphoneUtils/linphone-utils.js ui/scripts/LinphoneUtils/qmldir ui/scripts/Utils/port-tools.js diff --git a/linphone-desktop/ui/modules/Common/Form/+linux/SearchBox.qml b/linphone-desktop/ui/modules/Common/Form/+linux/SearchBox.qml index b88615528..2066328b1 100644 --- a/linphone-desktop/ui/modules/Common/Form/+linux/SearchBox.qml +++ b/linphone-desktop/ui/modules/Common/Form/+linux/SearchBox.qml @@ -14,14 +14,14 @@ Item { readonly property alias filter: searchField.text - property alias delegate: list.delegate - property alias header: list.header property alias entryHeight: menu.entryHeight property alias maxMenuHeight: menu.maxMenuHeight - - property alias model: list.model property alias placeholderText: searchField.placeholderText + default property alias _content: menu._content + + readonly property var view: _content[0] + property bool _isOpen: false // --------------------------------------------------------------------------- @@ -50,6 +50,7 @@ Item { } function _filter (text) { + var model = searchBox.view.model Utils.assert(model.setFilter != null, '`model.setFilter` must be defined.') model.setFilter(text) } @@ -97,13 +98,18 @@ Item { Keys.forwardTo: searchField onClosed: searchBox.closeMenu() + } - ScrollableListView { - id: list + Binding { + target: searchBox.view + property: 'width' + value: searchField.width + } - headerPositioning: header ? ListView.OverlayHeader : ListView.InlineFooter - width: searchField.width - } + Binding { + target: searchBox.view + property: 'headerPositioning' + value: searchBox.view.header ? ListView.OverlayHeader : ListView.InlineFooter } } diff --git a/linphone-desktop/ui/modules/Common/Menus/DropDownDynamicMenu.qml b/linphone-desktop/ui/modules/Common/Menus/DropDownDynamicMenu.qml index 7d9b333a1..952bcd009 100644 --- a/linphone-desktop/ui/modules/Common/Menus/DropDownDynamicMenu.qml +++ b/linphone-desktop/ui/modules/Common/Menus/DropDownDynamicMenu.qml @@ -42,10 +42,6 @@ Item { var list = _content[0] Utils.assert(list != null, 'No list found.') - Utils.assert( - Utils.qmlTypeof(list, 'QQuickListView') || Utils.qmlTypeof(list, 'ScrollableListView'), - 'No list view parameter.' - ) var height = list.count * entryHeight diff --git a/linphone-desktop/ui/modules/Linphone/SmartSearchBar/SmartSearchBar.qml b/linphone-desktop/ui/modules/Linphone/SmartSearchBar/SmartSearchBar.qml index b259968bc..c0acb55a7 100644 --- a/linphone-desktop/ui/modules/Linphone/SmartSearchBar/SmartSearchBar.qml +++ b/linphone-desktop/ui/modules/Linphone/SmartSearchBar/SmartSearchBar.qml @@ -1,8 +1,6 @@ -import QtQuick 2.7 -import QtQuick.Layouts 1.3 - import Common 1.0 import Linphone 1.0 + import Linphone.Styles 1.0 // ============================================================================= @@ -12,10 +10,6 @@ SearchBox { // --------------------------------------------------------------------------- - readonly property string interpretableSipAddress: SipAddressesModel.interpretUrl( - searchBox.filter - ) - readonly property alias isOpen: searchBox._isOpen // --------------------------------------------------------------------------- @@ -29,256 +23,49 @@ SearchBox { // --------------------------------------------------------------------------- - onEnterPressed: interpretableSipAddress.length > 0 && searchBox.launchCall(interpretableSipAddress) + entryHeight: SipAddressesViewStyle.entry.height - // --------------------------------------------------------------------------- - // Header. // --------------------------------------------------------------------------- - header: MouseArea { - height: { - var height = SmartSearchBarStyle.header.addButtonHeight - return defaultContact.visible ? height + searchBox.entryHeight : height - } - width: parent.width - - // Workaround to handle mouse. - // Without it, the mouse can be given to items list when mouse is hover header. - hoverEnabled: true - - Column { - anchors.fill: parent - - spacing: 0 - - // ----------------------------------------------------------------------- - // Default contact. - // ----------------------------------------------------------------------- - - Loader { - id: defaultContact - - height: searchBox.entryHeight - width: parent.width - - visible: interpretableSipAddress.length > 0 - - sourceComponent: Rectangle { - anchors.fill: parent - color: SmartSearchBarStyle.entry.color.normal - - RowLayout { - anchors { - fill: parent - rightMargin: SmartSearchBarStyle.entry.rightMargin - } - spacing: 0 - - Contact { - id: contact - - Layout.fillHeight: true - Layout.fillWidth: true - - entry: ({ - sipAddress: interpretableSipAddress - }) - } - - ActionBar { - iconSize: SmartSearchBarStyle.entry.iconSize - - ActionButton { - icon: 'video_call' - onClicked: { - searchBox.closeMenu() - searchBox.launchVideoCall(interpretableSipAddress) - } - } - - ActionButton { - icon: 'call' - onClicked: { - searchBox.closeMenu() - searchBox.launchCall(interpretableSipAddress) - } - } - - ActionButton { - icon: 'chat' - onClicked: { - searchBox.closeMenu() - searchBox.launchChat(interpretableSipAddress) - } - } - } - } - } - } - - // ----------------------------------------------------------------------- - // Add contact button. - // ----------------------------------------------------------------------- - - MouseArea { - id: addContactButton - - height: SmartSearchBarStyle.header.addButtonHeight - width: parent.width - - onClicked: { - searchBox.closeMenu() - searchBox.addContact(interpretableSipAddress) - } - - Rectangle { - anchors.fill: parent - color: parent.pressed - ? SmartSearchBarStyle.header.color.pressed - : SmartSearchBarStyle.header.color.normal - - Text { - anchors { - left: parent.left - leftMargin: SmartSearchBarStyle.header.leftMargin - verticalCenter: parent.verticalCenter - } - font { - bold: true - pointSize: SmartSearchBarStyle.header.text.fontSize - } - color: addContactButton.pressed - ? SmartSearchBarStyle.header.text.color.pressed - : SmartSearchBarStyle.header.text.color.normal - text: qsTr('addContact') - } - - Icon { - anchors { - right: parent.right - rightMargin: SmartSearchBarStyle.header.rightMargin - verticalCenter: parent.verticalCenter - } - icon: 'contact_add' - iconSize: SmartSearchBarStyle.header.iconSize - } - } - } - } - } + onEnterPressed: view.interpretableSipAddress.length > 0 && searchBox.launchCall(interpretableSipAddress) - // --------------------------------------------------------------------------- - // Entries. // --------------------------------------------------------------------------- - delegate: Rectangle { - id: sipAddressEntry + SipAddressesView { + id: view - color: SmartSearchBarStyle.entry.color.normal - height: searchBox.entryHeight - width: parent ? parent.width : 0 - - Rectangle { - id: indicator - - anchors.left: parent.left - color: 'transparent' - height: parent.height - width: SmartSearchBarStyle.entry.indicator.width - } - - MouseArea { - id: mouseArea - - anchors.fill: parent - hoverEnabled: true - - RowLayout { - anchors { - fill: parent - rightMargin: SmartSearchBarStyle.entry.rightMargin - } - spacing: 0 - - // ------------------------------------------------------------------- - // Contact or address info. - // ------------------------------------------------------------------- - - Contact { - Layout.fillHeight: true - Layout.fillWidth: true - entry: $sipAddress - - MouseArea { - anchors.fill: parent - - cursorShape: containsMouse - ? Qt.PointingHandCursor - : Qt.ArrowCursor - hoverEnabled: true - - onClicked: { - searchBox.closeMenu() - searchBox.entryClicked($sipAddress) - } - } - } - - // ------------------------------------------------------------------- - // Actions - // ------------------------------------------------------------------- - - ActionBar { - iconSize: SmartSearchBarStyle.entry.iconSize - - ActionButton { - icon: 'video_call' - onClicked: { - searchBox.closeMenu() - searchBox.launchVideoCall($sipAddress.sipAddress) - } - } - - ActionButton { - icon: 'call' - onClicked: { - searchBox.closeMenu() - searchBox.launchCall($sipAddress.sipAddress) - } - } - - ActionButton { - icon: 'chat' - onClicked: { - searchBox.closeMenu() - searchBox.launchChat($sipAddress.sipAddress) - } - } - } + actions: [{ + icon: 'video_call', + handler: function (entry) { + searchBox.closeMenu() + searchBox.launchVideoCall(entry.sipAddress) } - } - - // Separator. - Rectangle { - color: SmartSearchBarStyle.entry.separator.color - height: SmartSearchBarStyle.entry.separator.height - width: parent.width - } - - // ------------------------------------------------------------------------- - - states: State { - when: mouseArea.containsMouse - - PropertyChanges { - color: SmartSearchBarStyle.entry.color.hovered - target: sipAddressEntry + }, { + icon: 'call', + handler: function (entry) { + searchBox.closeMenu() + searchBox.launchCall(entry.sipAddress) } - - PropertyChanges { - color: SmartSearchBarStyle.entry.indicator.color - target: indicator + }, { + icon: 'chat', + handler: function (entry) { + searchBox.closeMenu() + searchBox.launchChat(entry.sipAddress) } + }] + + headerButtonDescription: qsTr('addContact') + headerButtonIcon: 'contact_add' + headerButtonAction: (function (sipAddress) { + searchBox.closeMenu() + searchBox.addContact(sipAddress) + }) + + genSipAddress: searchBox.filter + + onEntryClicked: { + searchBox.closeMenu() + searchBox.entryClicked(entry) } } } diff --git a/linphone-desktop/ui/modules/Linphone/Styles/SmartSearchBar/SmartSearchBarStyle.qml b/linphone-desktop/ui/modules/Linphone/Styles/View/SipAddressesViewStyle.qml similarity index 91% rename from linphone-desktop/ui/modules/Linphone/Styles/SmartSearchBar/SmartSearchBarStyle.qml rename to linphone-desktop/ui/modules/Linphone/Styles/View/SipAddressesViewStyle.qml index 9922f5435..d0387a077 100644 --- a/linphone-desktop/ui/modules/Linphone/Styles/SmartSearchBar/SmartSearchBarStyle.qml +++ b/linphone-desktop/ui/modules/Linphone/Styles/View/SipAddressesViewStyle.qml @@ -7,12 +7,13 @@ import Common 1.0 QtObject { property QtObject entry: QtObject { - property int rightMargin: 10 + property int height: 50 property int iconSize: 36 + property int rightMargin: 10 property QtObject color: QtObject { - property color normal: Colors.k property color hovered: Colors.y + property color normal: Colors.k } property QtObject indicator: QtObject { @@ -27,11 +28,14 @@ QtObject { } property QtObject header: QtObject { - property int addButtonHeight: 40 property int iconSize: 22 property int leftMargin: 20 property int rightMargin: 10 + property QtObject button: QtObject { + property int height: 40 + } + property QtObject color: QtObject { property color normal: Colors.j property color pressed: Colors.i diff --git a/linphone-desktop/ui/modules/Linphone/Styles/qmldir b/linphone-desktop/ui/modules/Linphone/Styles/qmldir index 69a04d187..3bbd06035 100644 --- a/linphone-desktop/ui/modules/Linphone/Styles/qmldir +++ b/linphone-desktop/ui/modules/Linphone/Styles/qmldir @@ -28,8 +28,8 @@ singleton NotificationReceivedCallStyle 1.0 Notifications/NotificationRec singleton NotificationReceivedMessageStyle 1.0 Notifications/NotificationReceivedMessageStyle.qml singleton NotificationReceivedFileMessageStyle 1.0 Notifications/NotificationReceivedFileMessageStyle.qml -singleton SmartSearchBarStyle 1.0 SmartSearchBar/SmartSearchBarStyle.qml - singleton TelKeypadStyle 1.0 TelKeypad/TelKeypadStyle.qml singleton TimelineStyle 1.0 Timeline/TimelineStyle.qml + +singleton SipAddressesViewStyle 1.0 View/SipAddressesViewStyle.qml diff --git a/linphone-desktop/ui/modules/Linphone/View/SipAddressesView.qml b/linphone-desktop/ui/modules/Linphone/View/SipAddressesView.qml new file mode 100644 index 000000000..4bf7f134b --- /dev/null +++ b/linphone-desktop/ui/modules/Linphone/View/SipAddressesView.qml @@ -0,0 +1,269 @@ +import QtQuick 2.7 +import QtQuick.Layouts 1.3 + +import Common 1.0 +import Linphone 1.0 +import Linphone.Styles 1.0 + +// ============================================================================= + +ScrollableListView { + id: sipAddressesView + + // --------------------------------------------------------------------------- + + // Contains a list of: { + // icon: 'string', + // handler: function () { ... } + // } + property var actions: [] + + property string genSipAddress + + // Optional parameters. + property string headerButtonDescription + property string headerButtonIcon + property var headerButtonAction + + readonly property string interpretableSipAddress: SipAddressesModel.interpretUrl( + genSipAddress + ) + + // --------------------------------------------------------------------------- + + signal entryClicked (var entry) + + // --------------------------------------------------------------------------- + // Header. + // --------------------------------------------------------------------------- + + header: MouseArea { + height: { + var height = headerButton.visible ? SipAddressesViewStyle.header.button.height : 0 + if (defaultContact.visible) { + height += SipAddressesViewStyle.entry.height + } + return height + } + width: parent.width + + // Workaround to handle mouse. + // Without it, the mouse can be given to items list when mouse is hover header. + hoverEnabled: true + + Column { + anchors.fill: parent + + spacing: 0 + + // ----------------------------------------------------------------------- + // Default contact. + // ----------------------------------------------------------------------- + + Loader { + id: defaultContact + + height: SipAddressesViewStyle.entry.height + width: parent.width + + visible: sipAddressesView.interpretableSipAddress.length > 0 + + sourceComponent: Rectangle { + anchors.fill: parent + color: SipAddressesViewStyle.entry.color.normal + + RowLayout { + anchors { + fill: parent + rightMargin: SipAddressesViewStyle.entry.rightMargin + } + spacing: 0 + + Contact { + id: contact + + Layout.fillHeight: true + Layout.fillWidth: true + + entry: ({ + sipAddress: sipAddressesView.interpretableSipAddress + }) + } + + ActionBar { + iconSize: SipAddressesViewStyle.entry.iconSize + + Repeater { + model: sipAddressesView.actions + + ActionButton { + icon: modelData.icon + onClicked: sipAddressesView.actions[index].handler({ + sipAddress: sipAddressesView.interpretableSipAddress + }) + } + } + } + } + } + } + + // ----------------------------------------------------------------------- + // Header button. + // ----------------------------------------------------------------------- + + MouseArea { + id: headerButton + + height: SipAddressesViewStyle.header.button.height + width: parent.width + + visible: !!sipAddressesView.headerButtonAction + + onClicked: sipAddressesView.headerButtonAction(sipAddressesView.interpretableSipAddress) + + Rectangle { + anchors.fill: parent + color: parent.pressed + ? SipAddressesViewStyle.header.color.pressed + : SipAddressesViewStyle.header.color.normal + + Text { + anchors { + left: parent.left + leftMargin: SipAddressesViewStyle.header.leftMargin + verticalCenter: parent.verticalCenter + } + + font { + bold: true + pointSize: SipAddressesViewStyle.header.text.fontSize + } + + color: headerButton.pressed + ? SipAddressesViewStyle.header.text.color.pressed + : SipAddressesViewStyle.header.text.color.normal + text: sipAddressesView.headerButtonDescription + } + + Icon { + anchors { + right: parent.right + rightMargin: SipAddressesViewStyle.header.rightMargin + verticalCenter: parent.verticalCenter + } + + icon: sipAddressesView.headerButtonIcon + iconSize: SipAddressesViewStyle.header.iconSize + + visible: icon.length > 0 + } + } + } + } + } + + // --------------------------------------------------------------------------- + // Entries. + // --------------------------------------------------------------------------- + + delegate: Rectangle { + id: sipAddressEntry + + color: SipAddressesViewStyle.entry.color.normal + height: SipAddressesViewStyle.entry.height + width: parent ? parent.width : 0 + + Rectangle { + id: indicator + + anchors.left: parent.left + color: 'transparent' + height: parent.height + width: SipAddressesViewStyle.entry.indicator.width + } + + MouseArea { + id: mouseArea + + anchors.fill: parent + hoverEnabled: true + + RowLayout { + anchors { + fill: parent + rightMargin: SipAddressesViewStyle.entry.rightMargin + } + spacing: 0 + + // --------------------------------------------------------------------- + // Contact or address info. + // --------------------------------------------------------------------- + + Contact { + Layout.fillHeight: true + Layout.fillWidth: true + + entry: $sipAddress + + MouseArea { + anchors.fill: parent + + cursorShape: containsMouse + ? Qt.PointingHandCursor + : Qt.ArrowCursor + hoverEnabled: true + + onClicked: sipAddressesView.entryClicked($sipAddress) + } + } + + // --------------------------------------------------------------------- + // Actions + // --------------------------------------------------------------------- + + ActionBar { + iconSize: SipAddressesViewStyle.entry.iconSize + + Repeater { + model: sipAddressesView.actions + + ActionButton { + icon: modelData.icon + onClicked: sipAddressesView.actions[index].handler($sipAddress) + } + } + } + } + } + + // Separator. + Rectangle { + color: SipAddressesViewStyle.entry.separator.color + height: SipAddressesViewStyle.entry.separator.height + width: parent.width + } + + // ------------------------------------------------------------------------- + + states: State { + when: mouseArea.containsMouse + + PropertyChanges { + color: SipAddressesViewStyle.entry.color.hovered + target: sipAddressEntry + } + + PropertyChanges { + color: SipAddressesViewStyle.entry.indicator.color + target: indicator + } + } + } + + // --------------------------------------------------------------------------- + // Model. + // --------------------------------------------------------------------------- + + model: SipAddressesProxyModel {} +} diff --git a/linphone-desktop/ui/views/App/Main/MainWindow.qml b/linphone-desktop/ui/views/App/Main/MainWindow.qml index 8c6f4f5cb..79bbcc899 100644 --- a/linphone-desktop/ui/views/App/Main/MainWindow.qml +++ b/linphone-desktop/ui/views/App/Main/MainWindow.qml @@ -150,12 +150,9 @@ ApplicationWindow { Layout.fillWidth: true - entryHeight: MainWindowStyle.searchBox.entryHeight maxMenuHeight: MainWindowStyle.searchBox.maxHeight placeholderText: qsTr('mainSearchBarPlaceholder') - model: SipAddressesProxyModel {} - onAddContact: window.setView('ContactEdit', { sipAddress: sipAddress }) @@ -210,7 +207,7 @@ ApplicationWindow { ApplicationMenu { id: menu - entryHeight: MainWindowStyle.menu.entryHeight + entryHeight: MainWindowStyle.menu.height entryWidth: MainWindowStyle.menu.width entries: [{ diff --git a/linphone-desktop/ui/views/App/Styles/Main/MainWindowStyle.qml b/linphone-desktop/ui/views/App/Styles/Main/MainWindowStyle.qml index 53cf92df9..3e1e1fc22 100644 --- a/linphone-desktop/ui/views/App/Styles/Main/MainWindowStyle.qml +++ b/linphone-desktop/ui/views/App/Styles/Main/MainWindowStyle.qml @@ -28,12 +28,11 @@ QtObject { } property QtObject menu: QtObject { - property int entryHeight: 50 + property int height: 50 property int width: 250 } property QtObject searchBox: QtObject { - property int entryHeight: 50 + SmartSearchBarStyle.entry.separator.height property int maxHeight: 300 // See Hick's law for good choice. } diff --git a/submodules/linphone b/submodules/linphone index 5e077c08b..2a3a23563 160000 --- a/submodules/linphone +++ b/submodules/linphone @@ -1 +1 @@ -Subproject commit 5e077c08b4c243e19d1432f89b2a69eebb69d5ee +Subproject commit 2a3a235634ce9fa7015a7ec6807679a361873ee6