Commit e601e3aa authored by Ronan Abhamon's avatar Ronan Abhamon

feat(ui): create a generic `SipAddressesView` component

parent f18b3f40
...@@ -325,13 +325,14 @@ ...@@ -325,13 +325,14 @@
<file>ui/modules/Linphone/Styles/Notifications/NotificationReceivedFileMessageStyle.qml</file> <file>ui/modules/Linphone/Styles/Notifications/NotificationReceivedFileMessageStyle.qml</file>
<file>ui/modules/Linphone/Styles/Notifications/NotificationReceivedMessageStyle.qml</file> <file>ui/modules/Linphone/Styles/Notifications/NotificationReceivedMessageStyle.qml</file>
<file>ui/modules/Linphone/Styles/qmldir</file> <file>ui/modules/Linphone/Styles/qmldir</file>
<file>ui/modules/Linphone/Styles/SmartSearchBar/SmartSearchBarStyle.qml</file>
<file>ui/modules/Linphone/Styles/TelKeypad/TelKeypadStyle.qml</file> <file>ui/modules/Linphone/Styles/TelKeypad/TelKeypadStyle.qml</file>
<file>ui/modules/Linphone/Styles/Timeline/TimelineStyle.qml</file> <file>ui/modules/Linphone/Styles/Timeline/TimelineStyle.qml</file>
<file>ui/modules/Linphone/Styles/View/SipAddressesViewStyle.qml</file>
<file>ui/modules/Linphone/TelKeypad/TelKeypadButton.qml</file> <file>ui/modules/Linphone/TelKeypad/TelKeypadButton.qml</file>
<file>ui/modules/Linphone/TelKeypad/TelKeypad.qml</file> <file>ui/modules/Linphone/TelKeypad/TelKeypad.qml</file>
<file>ui/modules/Linphone/Timeline/Timeline.js</file> <file>ui/modules/Linphone/Timeline/Timeline.js</file>
<file>ui/modules/Linphone/Timeline/Timeline.qml</file> <file>ui/modules/Linphone/Timeline/Timeline.qml</file>
<file>ui/modules/Linphone/View/SipAddressesView.qml</file>
<file>ui/scripts/LinphoneUtils/linphone-utils.js</file> <file>ui/scripts/LinphoneUtils/linphone-utils.js</file>
<file>ui/scripts/LinphoneUtils/qmldir</file> <file>ui/scripts/LinphoneUtils/qmldir</file>
<file>ui/scripts/Utils/port-tools.js</file> <file>ui/scripts/Utils/port-tools.js</file>
......
...@@ -14,14 +14,14 @@ Item { ...@@ -14,14 +14,14 @@ Item {
readonly property alias filter: searchField.text readonly property alias filter: searchField.text
property alias delegate: list.delegate
property alias header: list.header
property alias entryHeight: menu.entryHeight property alias entryHeight: menu.entryHeight
property alias maxMenuHeight: menu.maxMenuHeight property alias maxMenuHeight: menu.maxMenuHeight
property alias model: list.model
property alias placeholderText: searchField.placeholderText property alias placeholderText: searchField.placeholderText
default property alias _content: menu._content
readonly property var view: _content[0]
property bool _isOpen: false property bool _isOpen: false
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
...@@ -50,6 +50,7 @@ Item { ...@@ -50,6 +50,7 @@ Item {
} }
function _filter (text) { function _filter (text) {
var model = searchBox.view.model
Utils.assert(model.setFilter != null, '`model.setFilter` must be defined.') Utils.assert(model.setFilter != null, '`model.setFilter` must be defined.')
model.setFilter(text) model.setFilter(text)
} }
...@@ -97,13 +98,18 @@ Item { ...@@ -97,13 +98,18 @@ Item {
Keys.forwardTo: searchField Keys.forwardTo: searchField
onClosed: searchBox.closeMenu() onClosed: searchBox.closeMenu()
}
ScrollableListView { Binding {
id: list target: searchBox.view
property: 'width'
value: searchField.width
}
headerPositioning: header ? ListView.OverlayHeader : ListView.InlineFooter Binding {
width: searchField.width target: searchBox.view
} property: 'headerPositioning'
value: searchBox.view.header ? ListView.OverlayHeader : ListView.InlineFooter
} }
} }
......
...@@ -42,10 +42,6 @@ Item { ...@@ -42,10 +42,6 @@ Item {
var list = _content[0] var list = _content[0]
Utils.assert(list != null, 'No list found.') 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 var height = list.count * entryHeight
......
import QtQuick 2.7
import QtQuick.Layouts 1.3
import Common 1.0 import Common 1.0
import Linphone 1.0 import Linphone 1.0
import Linphone.Styles 1.0 import Linphone.Styles 1.0
// ============================================================================= // =============================================================================
...@@ -12,10 +10,6 @@ SearchBox { ...@@ -12,10 +10,6 @@ SearchBox {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
readonly property string interpretableSipAddress: SipAddressesModel.interpretUrl(
searchBox.filter
)
readonly property alias isOpen: searchBox._isOpen readonly property alias isOpen: searchBox._isOpen
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
...@@ -29,256 +23,49 @@ SearchBox { ...@@ -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 { onEnterPressed: view.interpretableSipAddress.length > 0 && searchBox.launchCall(interpretableSipAddress)
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 { SipAddressesView {
icon: 'call' id: view
onClicked: {
searchBox.closeMenu()
searchBox.launchCall(interpretableSipAddress)
}
}
ActionButton { actions: [{
icon: 'chat' icon: 'video_call',
onClicked: { handler: function (entry) {
searchBox.closeMenu() searchBox.closeMenu()
searchBox.launchChat(interpretableSipAddress) searchBox.launchVideoCall(entry.sipAddress)
}
}
}
}
}
} }
}, {
// ----------------------------------------------------------------------- icon: 'call',
// Add contact button. handler: function (entry) {
// ----------------------------------------------------------------------- searchBox.closeMenu()
searchBox.launchCall(entry.sipAddress)
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
}
}
} }
} }, {
} icon: 'chat',
handler: function (entry) {
// --------------------------------------------------------------------------- searchBox.closeMenu()
// Entries. searchBox.launchChat(entry.sipAddress)
// ---------------------------------------------------------------------------
delegate: Rectangle {
id: sipAddressEntry
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)
}
}
}
} }
} }]
// Separator.
Rectangle {
color: SmartSearchBarStyle.entry.separator.color
height: SmartSearchBarStyle.entry.separator.height
width: parent.width
}
// ------------------------------------------------------------------------- headerButtonDescription: qsTr('addContact')
headerButtonIcon: 'contact_add'
headerButtonAction: (function (sipAddress) {
searchBox.closeMenu()
searchBox.addContact(sipAddress)
})
states: State { genSipAddress: searchBox.filter
when: mouseArea.containsMouse
PropertyChanges { onEntryClicked: {
color: SmartSearchBarStyle.entry.color.hovered searchBox.closeMenu()
target: sipAddressEntry searchBox.entryClicked(entry)
}
PropertyChanges {
color: SmartSearchBarStyle.entry.indicator.color
target: indicator
}
} }
} }
} }
...@@ -7,12 +7,13 @@ import Common 1.0 ...@@ -7,12 +7,13 @@ import Common 1.0
QtObject { QtObject {
property QtObject entry: QtObject { property QtObject entry: QtObject {
property int rightMargin: 10 property int height: 50
property int iconSize: 36 property int iconSize: 36
property int rightMargin: 10
property QtObject color: QtObject { property QtObject color: QtObject {
property color normal: Colors.k
property color hovered: Colors.y property color hovered: Colors.y
property color normal: Colors.k
} }
property QtObject indicator: QtObject { property QtObject indicator: QtObject {
...@@ -27,11 +28,14 @@ QtObject { ...@@ -27,11 +28,14 @@ QtObject {
} }
property QtObject header: QtObject { property QtObject header: QtObject {
property int addButtonHeight: 40
property int iconSize: 22 property int iconSize: 22
property int leftMargin: 20 property int leftMargin: 20
property int rightMargin: 10 property int rightMargin: 10
property QtObject button: QtObject {
property int height: 40
}
property QtObject color: QtObject { property QtObject color: QtObject {
property color normal: Colors.j property color normal: Colors.j
property color pressed: Colors.i property color pressed: Colors.i
......
...@@ -28,8 +28,8 @@ singleton NotificationReceivedCallStyle 1.0 Notifications/NotificationRec ...@@ -28,8 +28,8 @@ singleton NotificationReceivedCallStyle 1.0 Notifications/NotificationRec
singleton NotificationReceivedMessageStyle 1.0 Notifications/NotificationReceivedMessageStyle.qml singleton NotificationReceivedMessageStyle 1.0 Notifications/NotificationReceivedMessageStyle.qml
singleton NotificationReceivedFileMessageStyle 1.0 Notifications/NotificationReceivedFileMessageStyle.qml singleton NotificationReceivedFileMessageStyle 1.0 Notifications/NotificationReceivedFileMessageStyle.qml
singleton SmartSearchBarStyle 1.0 SmartSearchBar/SmartSearchBarStyle.qml
singleton TelKeypadStyle 1.0 TelKeypad/TelKeypadStyle.qml singleton TelKeypadStyle 1.0 TelKeypad/TelKeypadStyle.qml
singleton TimelineStyle 1.0 Timeline/TimelineStyle.qml singleton TimelineStyle 1.0 Timeline/TimelineStyle.qml
singleton SipAddressesViewStyle 1.0 View/SipAddressesViewStyle.qml
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 {}
}
...@@ -150,12 +150,9 @@ ApplicationWindow { ...@@ -150,12 +150,9 @@ ApplicationWindow {
Layout.fillWidth: true Layout.fillWidth: true
entryHeight: MainWindowStyle.searchBox.entryHeight
maxMenuHeight: MainWindowStyle.searchBox.maxHeight maxMenuHeight: MainWindowStyle.searchBox.maxHeight
placeholderText: qsTr('mainSearchBarPlaceholder') placeholderText: qsTr('mainSearchBarPlaceholder')
model: SipAddressesProxyModel {}
onAddContact: window.setView('ContactEdit', { onAddContact: window.setView('ContactEdit', {
sipAddress: sipAddress sipAddress: sipAddress
}) })
...@@ -210,7 +207,7 @@ ApplicationWindow { ...@@ -210,7 +207,7 @@ ApplicationWindow {
ApplicationMenu { ApplicationMenu {
id: menu id: menu
entryHeight: MainWindowStyle.menu.entryHeight entryHeight: MainWindowStyle.menu.height
entryWidth: MainWindowStyle.menu.width entryWidth: MainWindowStyle.menu.width
entries: [{ entries: [{
......
...@@ -28,12 +28,11 @@ QtObject { ...@@ -28,12 +28,11 @@ QtObject {
} }
property QtObject menu: QtObject { property QtObject menu: QtObject {
property int entryHeight: 50 property int height: 50
property int width: 250 property int width: 250
} }
property QtObject searchBox: QtObject { property QtObject searchBox: QtObject {
property int entryHeight: 50 + SmartSearchBarStyle.entry.separator.height
property int maxHeight: 300 // See Hick's law for good choice. property int maxHeight: 300 // See Hick's law for good choice.
} }
......
linphone @ 2a3a2356
Subproject commit 5e077c08b4c243e19d1432f89b2a69eebb69d5ee Subproject commit 2a3a235634ce9fa7015a7ec6807679a361873ee6
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment