From 79ec2c5bfcf4684e0e07b6a3bb989d653e1db140 Mon Sep 17 00:00:00 2001 From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> Date: Fri, 2 Aug 2019 14:45:58 -0400 Subject: [PATCH] ui: add contact picker widget - this widget can be used to display a list of contacts for selecting a conferencee or transferee Change-Id: Id65bf294bbf4d0e88721d3924a39b7dff69b0c64 --- callwidget.cpp | 1 + contactdialog.ui | 94 ------ contactpicker.cpp | 145 +++++++++ contactpicker.h | 98 ++++++ contactpicker.ui | 319 +++++++++++++++++++ contactpickeritemdelegate.cpp | 191 +++++++++++ contactpickeritemdelegate.h | 47 +++ images/icons/ic_call_transfer_white_24px.png | Bin 703 -> 1324 bytes images/spike.png | Bin 0 -> 355 bytes images/spikeMask.png | Bin 257 -> 0 bytes jami-qt.pro | 3 +- ressources.qrc | 169 +++++----- ring-client-windows.vcxproj | 13 +- ring-client-windows.vcxproj.filters | 15 + smartlistmodel.cpp | 19 +- smartlistmodel.h | 4 +- stylesheet.css | 12 +- videooverlay.cpp | 44 ++- videooverlay.h | 5 +- videooverlay.ui | 26 ++ videoview.cpp | 4 + 21 files changed, 997 insertions(+), 212 deletions(-) delete mode 100644 contactdialog.ui create mode 100644 contactpicker.cpp create mode 100644 contactpicker.h create mode 100644 contactpicker.ui create mode 100644 contactpickeritemdelegate.cpp create mode 100644 contactpickeritemdelegate.h create mode 100644 images/spike.png delete mode 100644 images/spikeMask.png diff --git a/callwidget.cpp b/callwidget.cpp index 7051f26..17b83ab 100644 --- a/callwidget.cpp +++ b/callwidget.cpp @@ -644,6 +644,7 @@ void CallWidget::slotShowCallView(const std::string& accountId, ui->callStackWidget->setCurrentWidget(ui->videoPage); ui->videoWidget->showChatviewIfToggled(); hideMiniSpinner(); + ui->videoWidget->pushRenderer(convInfo.callId); } void CallWidget::slotShowIncomingCallView(const std::string& accountId, diff --git a/contactdialog.ui b/contactdialog.ui deleted file mode 100644 index 4d4e80f..0000000 --- a/contactdialog.ui +++ /dev/null @@ -1,94 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>ContactDialog</class> - <widget class="QDialog" name="ContactDialog"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>398</width> - <height>154</height> - </rect> - </property> - <property name="windowTitle"> - <string>Dialog</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QLabel" name="label"> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="text"> - <string>New Contact</string> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="nameLineEdit"> - <property name="placeholderText"> - <string>Enter a name...</string> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="numberLineEdit"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="alignment"> - <set>Qt::AlignCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> - </property> - </widget> - </item> - </layout> - </widget> - <resources/> - <connections> - <connection> - <sender>buttonBox</sender> - <signal>accepted()</signal> - <receiver>ContactDialog</receiver> - <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>248</x> - <y>254</y> - </hint> - <hint type="destinationlabel"> - <x>157</x> - <y>274</y> - </hint> - </hints> - </connection> - <connection> - <sender>buttonBox</sender> - <signal>rejected()</signal> - <receiver>ContactDialog</receiver> - <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>316</x> - <y>260</y> - </hint> - <hint type="destinationlabel"> - <x>286</x> - <y>274</y> - </hint> - </hints> - </connection> - </connections> -</ui> diff --git a/contactpicker.cpp b/contactpicker.cpp new file mode 100644 index 0000000..70ccd7f --- /dev/null +++ b/contactpicker.cpp @@ -0,0 +1,145 @@ +/*************************************************************************** + * Copyright (C) 2015-2019 by Savoir-faire Linux * + * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>* + * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + **************************************************************************/ + +#include "contactpicker.h" +#include "ui_contactpicker.h" + +#include <QMouseEvent> + +#include "contactpickeritemdelegate.h" + +ContactPicker::ContactPicker(QWidget *parent) : + QDialog(parent), + ui(new Ui::ContactPicker), + type_(Type::CONFERENCE) +{ + ui->setupUi(this); + + setWindowFlags(Qt::CustomizeWindowHint); + setWindowFlags(Qt::Popup | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint); + setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_TranslucentBackground, true); + + ui->smartList->setItemDelegate(new ContactPickerItemDelegate()); + + selectableProxyModel_ = new SelectableProxyModel(smartListModel_.get()); + ui->smartList->setModel(selectableProxyModel_); + +} + +ContactPicker::~ContactPicker() +{ + delete ui; +} + +void +ContactPicker::on_smartList_clicked(const QModelIndex &index) +{ + Q_UNUSED(index); + this->accept(); +} + +void +ContactPicker::accept() +{ + auto idx = ui->smartList->currentIndex(); + + if (idx.isValid()) { + // get current call id and peer uri + auto selectedConvUid = LRCInstance::getSelectedConvUid(); + auto convModel = LRCInstance::getCurrentConversationModel(); + auto conversation = Utils::getConversationFromUid(selectedConvUid, *convModel); + auto thisCallId = conversation->callId; + auto contactUri = idx.data(static_cast<int>(SmartListModel::Role::URI)).value<QString>().toStdString(); + + // let parent deal with this as this dialog will be destroyed + switch (type_) { + case Type::CONFERENCE: + emit contactWillJoinConference(thisCallId, contactUri); + break; + case Type::BLIND_TRANSFER: + case Type::ATTENDED_TRANSFER: + emit contactWillDoBlindTransfer(thisCallId, contactUri); + break; + default: + break; + } + } + + QDialog::accept(); +} + +void +ContactPicker::on_ringContactLineEdit_textChanged(const QString &arg1) +{ + selectableProxyModel_->setFilterRegExp(QRegExp(arg1, Qt::CaseInsensitive, QRegExp::FixedString)); +} + +void +ContactPicker::mousePressEvent(QMouseEvent *event) +{ + auto contactPickerWidgetRect = ui->contactPickerWidget->rect(); + if (!contactPickerWidgetRect.contains(event->pos())) { + //close(); + emit willClose(event); + } +} + +void +ContactPicker::setTitle(const std::string& title) +{ + ui->title->setText(QString::fromStdString(title)); +} + +void +ContactPicker::setType(const Type& type) +{ + type_ = type; + smartListModel_.reset(new SmartListModel(LRCInstance::getCurrAccId(), this, true)); + selectableProxyModel_->setSourceModel(smartListModel_.get()); + // adjust filter + switch (type_) { + case Type::CONFERENCE: + selectableProxyModel_->setPredicate( + [this](const QModelIndex& index, const QRegExp& regexp) { + bool match = regexp.indexIn(index.data(Qt::DisplayRole).toString()) != -1; + auto convUid = index.data(static_cast<int>(SmartListModel::Role::UID)).value<QString>().toStdString(); + auto convModel = LRCInstance::getCurrentConversationModel(); + auto conversation = Utils::getConversationFromUid(convUid, *convModel); + if (conversation == convModel->allFilteredConversations().end()) { + return false; + } + auto callModel = LRCInstance::getCurrentCallModel(); + return match && + !(callModel->hasCall(conversation->callId) || callModel->hasCall(conversation->confId)) && + !index.parent().isValid(); + }); + break; + case Type::BLIND_TRANSFER: + case Type::ATTENDED_TRANSFER: + selectableProxyModel_->setPredicate( + [this](const QModelIndex& index, const QRegExp& regexp) { + return true; + }); + break; + default: + break; + } + selectableProxyModel_->invalidate(); +} diff --git a/contactpicker.h b/contactpicker.h new file mode 100644 index 0000000..b9e9b4a --- /dev/null +++ b/contactpicker.h @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (C) 2015-2019 by Savoir-faire Linux * + * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>* + * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + **************************************************************************/ + +#pragma once + +#include <QDialog> +#include <QSortFilterProxyModel> + +#include "smartlistmodel.h" +#include "utils.h" +#include "lrcinstance.h" + +namespace Ui { +class ContactPicker; +} + +class SelectableProxyModel : public QSortFilterProxyModel +{ +public: + using FilterPredicate = std::function<bool(const QModelIndex&, const QRegExp&)>; + + explicit SelectableProxyModel(QAbstractItemModel* parent) : QSortFilterProxyModel(parent) { + setSourceModel(parent); + } + + void setPredicate(FilterPredicate filterPredicate) { + filterPredicate_ = filterPredicate; + } + + virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const { + // Accept all contacts in conversation list filtered with account type, except those in a call + auto index = sourceModel()->index(source_row, 0, source_parent); + if (filterPredicate_) { + return filterPredicate_(index, filterRegExp()); + } + } + +private: + std::function<bool(const QModelIndex&, const QRegExp&)> filterPredicate_; + +}; + +class ContactPicker : public QDialog +{ + Q_OBJECT; + +public: + enum class Type { + CONFERENCE, + BLIND_TRANSFER, + ATTENDED_TRANSFER, + COUNT__ + }; + + explicit ContactPicker(QWidget *parent = 0); + ~ContactPicker(); + void setTitle(const std::string& title); + void setType(const Type& type); + +protected: + void mousePressEvent(QMouseEvent *event); + +signals: + void contactWillJoinConference(const std::string& callId, const std::string& contactUri); + void contactWillDoBlindTransfer(const std::string& callId, const std::string& contactUri); + void willClose(QMouseEvent *event); + +protected slots: + void accept(); + +private slots: + void on_smartList_clicked(const QModelIndex &index); + void on_ringContactLineEdit_textChanged(const QString &arg1); + +private: + Ui::ContactPicker *ui; + + std::unique_ptr<SmartListModel> smartListModel_; + SelectableProxyModel* selectableProxyModel_; + Type type_; + +}; diff --git a/contactpicker.ui b/contactpicker.ui new file mode 100644 index 0000000..a943292 --- /dev/null +++ b/contactpicker.ui @@ -0,0 +1,319 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ContactPicker</class> + <widget class="QDialog" name="ContactPicker"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>256</width> + <height>343</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string/> + </property> + <property name="styleSheet"> + <string notr="true"/> + </property> + <layout class="QGridLayout" name="gridLayout"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <item row="0" column="0"> + <widget class="QWidget" name="contactPickerWidget" native="true"> + <layout class="QGridLayout" name="gridLayout_2"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>5</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>10</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <item row="1" column="0"> + <widget class="QWidget" name="smartListOuterWidget" native="true"> + <property name="styleSheet"> + <string notr="true"/> + </property> + <layout class="QVBoxLayout" name="smartListOuterWidgetLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QWidget" name="searchBarLayoutWidget" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>60</height> + </size> + </property> + <layout class="QGridLayout" name="gridLayout_4"> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <item row="2" column="1"> + <spacer name="verticalSpacer_8"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>15</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="2"> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="0"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1"> + <widget class="RingContactLineEdit" name="ringContactLineEdit"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>100</width> + <height>30</height> + </size> + </property> + <property name="baseSize"> + <size> + <width>244</width> + <height>30</height> + </size> + </property> + <property name="font"> + <font> + <pointsize>9</pointsize> + </font> + </property> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="toolTip"> + <string>Search contact text input</string> + </property> + <property name="maxLength"> + <number>100</number> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + <property name="placeholderText"> + <string>Search contacts</string> + </property> + <property name="clearButtonEnabled"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="1"> + <spacer name="verticalSpacer_7"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>5</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <widget class="SmartListView" name="smartList"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="autoScrollMargin"> + <number>16</number> + </property> + <property name="indentation"> + <number>0</number> + </property> + <property name="rootIsDecorated"> + <bool>false</bool> + </property> + <property name="itemsExpandable"> + <bool>false</bool> + </property> + <property name="expandsOnDoubleClick"> + <bool>false</bool> + </property> + <attribute name="headerVisible"> + <bool>false</bool> + </attribute> + <attribute name="headerDefaultSectionSize"> + <number>0</number> + </attribute> + <attribute name="headerMinimumSectionSize"> + <number>0</number> + </attribute> + </widget> + </item> + </layout> + </widget> + </item> + <item row="0" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <property name="topMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="title"> + <property name="font"> + <font> + <family>Microsoft Tai Le</family> + <pointsize>10</pointsize> + </font> + </property> + <property name="text"> + <string>Add contact to conference</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="spikeLabel"> + <property name="text"> + <string/> + </property> + <property name="pixmap"> + <pixmap resource="ressources.qrc">:/images/spike.png</pixmap> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>RingContactLineEdit</class> + <extends>QLineEdit</extends> + <header>ringcontactlineedit.h</header> + </customwidget> + <customwidget> + <class>SmartListView</class> + <extends>QTreeView</extends> + <header>smartlistview.h</header> + </customwidget> + </customwidgets> + <resources> + <include location="ressources.qrc"/> + </resources> + <connections/> +</ui> diff --git a/contactpickeritemdelegate.cpp b/contactpickeritemdelegate.cpp new file mode 100644 index 0000000..476ebf6 --- /dev/null +++ b/contactpickeritemdelegate.cpp @@ -0,0 +1,191 @@ +/*************************************************************************** + * Copyright (C) 2019 by Savoir-faire Linux * + * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + **************************************************************************/ + +#include "contactpickeritemdelegate.h" + +#include <QApplication> +#include <QPainter> +#include <QPixmap> + + // Client +#include "smartlistmodel.h" +#include "ringthemeutils.h" +#include "utils.h" +#include "lrcinstance.h" +#include "mainwindow.h" + +#include <ciso646> + +ContactPickerItemDelegate::ContactPickerItemDelegate(QObject* parent) + : QItemDelegate(parent) +{ +} + +void +ContactPickerItemDelegate::paint(QPainter* painter + , const QStyleOptionViewItem& option + , const QModelIndex& index +) const +{ + QStyleOptionViewItem opt(option); + painter->setRenderHint(QPainter::Antialiasing, true); + + // Not having focus removes dotted lines around the item + if (opt.state & QStyle::State_HasFocus) + opt.state ^= QStyle::State_HasFocus; + + bool selected = false; + if (option.state & QStyle::State_Selected) { + selected = true; + opt.state ^= QStyle::State_Selected; + } else { + highlightMap_[index.row()] = option.state & QStyle::State_MouseOver; + } + + QColor presenceBorderColor = Qt::white; + auto rowHighlight = highlightMap_.find(index.row()); + if (selected) { + painter->fillRect(option.rect, RingTheme::smartlistSelection_); + presenceBorderColor = RingTheme::smartlistSelection_; + } else if (rowHighlight != highlightMap_.end() && (*rowHighlight).second) { + painter->fillRect(option.rect, RingTheme::smartlistHighlight_); + presenceBorderColor = RingTheme::smartlistHighlight_; + } + + QRect &rect = opt.rect; + + // Avatar drawing + opt.decorationSize = QSize(sizeImage_, sizeImage_); + opt.decorationPosition = QStyleOptionViewItem::Left; + opt.decorationAlignment = Qt::AlignCenter; + + QRect rectAvatar(dx_ + rect.left(), rect.top() + dy_, sizeImage_, sizeImage_); + drawDecoration(painter, opt, rectAvatar, + QPixmap::fromImage(index.data(Qt::DecorationRole).value<QImage>()) + .scaled(sizeImage_, sizeImage_, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + + QFont font(painter->font()); + + // Presence indicator + if (index.data(static_cast<int>(SmartListModel::Role::Presence)).value<bool>()) { + qreal radius = sizeImage_ / 6; + QPainterPath outerCircle, innerCircle; + QPointF center(rectAvatar.right() - radius + 2, (rectAvatar.bottom() - radius) + 1 + 2); + qreal outerCRadius = radius; + qreal innerCRadius = outerCRadius * 0.75; + outerCircle.addEllipse(center, outerCRadius, outerCRadius); + innerCircle.addEllipse(center, innerCRadius, innerCRadius); + painter->fillPath(outerCircle, presenceBorderColor); + painter->fillPath(innerCircle, RingTheme::presenceGreen_); + } + + using namespace lrc::api; + auto type = Utils::toEnum<profile::Type>( + index.data(static_cast<int>(SmartListModel::Role::ContactType)).value<int>() + ); + switch (type) { + case profile::Type::RING: + case profile::Type::TEMPORARY: + paintRingContactItem(painter, option, rect, index); + break; + case profile::Type::SIP: + break; + default: + paintRingContactItem(painter, option, rect, index); + break; + } +} + +QSize +ContactPickerItemDelegate::sizeHint(const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + Q_UNUSED(option); + Q_UNUSED(index); + return QSize(0, cellHeight_); +} + +void +ContactPickerItemDelegate::paintRingContactItem(QPainter* painter, + const QStyleOptionViewItem& option, + const QRect& rect, + const QModelIndex& index) const +{ + Q_UNUSED(option); + QFont font(painter->font()); + QPen pen(painter->pen()); + painter->setPen(pen); + + auto scalingRatio = MainWindow::instance().getCurrentScalingRatio(); + if (scalingRatio > 1.0) { + font.setPointSize(fontSize_ - 2); + } else { + font.setPointSize(fontSize_); + } + + auto leftMargin = dx_ + sizeImage_ + dx_; + auto rightMargin = dx_; + auto topMargin = 4; + auto bottomMargin = 8; + + QRect rectName1(rect.left() + leftMargin, + rect.top() + topMargin, + rect.width() - leftMargin * 2, + rect.height() / 2 - 2); + + QRect rectName2(rectName1.left(), + rectName1.top() + rectName1.height(), + rectName1.width(), + rectName1.height() - bottomMargin); + + QFontMetrics fontMetrics(font); + + // The name is displayed at the avatar's right + QString nameStr = index.data(static_cast<int>(SmartListModel::Role::DisplayName)).value<QString>(); + if (!nameStr.isNull()) { + font.setItalic(false); + font.setBold(false); + pen.setColor(RingTheme::lightBlack_); + painter->setPen(pen); + painter->setFont(font); + QString elidedNameStr = fontMetrics.elidedText(nameStr, Qt::ElideRight, rectName1.width()); + painter->drawText(rectName1, Qt::AlignVCenter | Qt::AlignLeft, elidedNameStr); + } + + // Display the ID under the name + QString idStr = index.data(static_cast<int>(SmartListModel::Role::DisplayID)).value<QString>(); + if (idStr != nameStr && !idStr.isNull()) { + font.setItalic(false); + font.setBold(false); + pen.setColor(RingTheme::grey_); + painter->setPen(pen); + painter->setFont(font); + idStr = fontMetrics.elidedText(idStr, Qt::ElideRight, rectName2.width()); + painter->drawText(rectName2, Qt::AlignVCenter | Qt::AlignLeft, idStr); + } +} + +void +ContactPickerItemDelegate::paintSIPContactItem(QPainter* painter, + const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + Q_UNUSED(painter); + Q_UNUSED(option); + Q_UNUSED(index); +} diff --git a/contactpickeritemdelegate.h b/contactpickeritemdelegate.h new file mode 100644 index 0000000..6cdc225 --- /dev/null +++ b/contactpickeritemdelegate.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright (C) 2019 by Savoir-faire Linux * + * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + **************************************************************************/ + +#pragma once + +#include <QObject> +#include <QItemDelegate> + +class QPainter; + +class ContactPickerItemDelegate : public QItemDelegate +{ + Q_OBJECT +public: + explicit ContactPickerItemDelegate(QObject* parent = 0); + +protected: + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; + +private: + void paintRingContactItem(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, const QModelIndex& index) const; + void paintSIPContactItem(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + + constexpr static int sizeImage_ = 48; + constexpr static int cellHeight_ = 60; + constexpr static int dy_ = 6; + constexpr static int dx_ = 12; + constexpr static int fontSize_ = 11; + + mutable std::map<int, bool> highlightMap_; +}; diff --git a/images/icons/ic_call_transfer_white_24px.png b/images/icons/ic_call_transfer_white_24px.png index ed68542c24cf6e16df683c78feaa1e39af1939ac..a43dbb4be9d54a6be42b89d03488487b86ed2613 100644 GIT binary patch literal 1324 zcmeAS@N?(olHy`uVBq!ia0y~yVDJWE4mJh`1`EHcR}2gcEX7WqAsieW95oy%9SjT% zoCO|{#S9F3${@^GvDChdfq_A?#5JNMI6tkVJh3R1p}f3YFEcN@I61K(RWH9NefB#W zDFz0Xm7Xq+Ar*7p&h}j)nkv&ie?Qly?U5}3QY@+ST~@k$QLW9G&=JU)yF@#3ZPDd* zcU-!aG&)YW&R59QimK>bz_QqES;f++wYnxv8XozP33ax*=PXNi&NKc#xB8x?@ll%s z^WyY>pLd`CxxYC5p0WOxEnBvPbpPtGTK-PvKj)RXucFs(VA-`Pr&ZO!HB384ZQ04J zR?&;@*VeS2)h@jB^MFJ_Z0F-X=IX{=!NNs3JGdm7L(E-|tXwno>d#lxYwZ?>l&vV< z+~#%u-ulf|duG;ptn%kkTy^}{nuV7(i=Wg|)hV@zwRgEEce?R=o8|&NMmfF88<QmK z_HT@?KBhQROZ)iOXM1=|^_b_K4@kF?ZVZsu6`E!h*Pp^slGY{_YQ(lD&_u;u^YtAb z(^Y9a6}1yKghbY-aBQ-0?hV{9kx|y{)VW<I&b@&bw;v8l{X0YPfYa9|o>I4?ZV#5O zlnzR*ove9alTWGJL6r~huJEQ>X&*1mu56Q9oFEh-Dp<WN&u5aTAk+PHj>76TsnE;e zB1_7}4qTUPS`%{qT)=WIwyvh4P5Vv>q}dg?w^@Z2aO}vQprN6j{rp2E!w#+BxpQQg z-gS0#>Ri22`6^#BO<wMVkcRDdpUr&xSD7!n;`MILyC-*_Z}z+UkUQb`1k<9RRf|vg z)zpSro!jrj@LK874*}C6BhEXsb6@zn@2#5VoWFh7f=L0!>F>;WCb!?)png9(=t|43 z4WAO4Kl@EDjfgdz9kqF8{k^cH`?Uw|3KnLFJzv>$==X{@E5uhG7qZHVpDSysn!kQ? z+TFmzY!dzgS34xz)~%^>DP>-xDNwqBQ}(L!-wS~;XSkVMwpafujM=+sTfyawBkar9 z>AzU_ao1;4uF}3;`yFn~Eltl6dDX@g`TMHzt8+IbcHP~&+TlhwSk&n25r#<htI@B| z-O#wQmi2C_!`=<=dU_pg<Nxk-xUu}&ms5etf$~}tmi~&lD=FRjE2?o#@T+CJjLq1r z^Et$PAI{mZZ2B#!&Z?cisv9>fe)Vcsu^HRfI`;d1Us`Nuyo~w$)~3pK>AmIirY?Tx z5h$-e;i~i`=h(wnT<86Bs1Nz;QX3VUdu-*R6QL{QUx_`qxhS?lydhobQOe0%-7jjU z&onw*C}F|zhvARsbJ11G2jm_!Z+gCt=j&BTt224(D&-E_7k9}$I=#W!H;z9*vuvJx zaEq+L!;2c5SI8wVE|}P}OI7iU=hxJ&?@#X%?F_nWl_hVS^n6kLm-tP`x4&AIc|1(j z>+9QHb3_)cGyXfbv;S(gme^9?sjUC+?C?|hr>K5q{;JPQ)K0BRy&LON!+wWn&!i2% zV-f<lc3xVMJ2A=SkEr16#;*tTOP^{uzty%<epH*FIae?H*x82+S<_hs1Sj9si)MVY z%KE&5l;Uc4mp9CLjAjhKv-^srdMB|82rgGjk}lBQFzwvuXyuGKYvf`xA0O~NAeAuZ z+^pBPGS;k-6HIie5fRL8>~2&)c|Z5K(e-SxS3#wlcg4<?`LyiI#wo>NR`X4@-qf7k z#uLGLhau+TU(d+18>M&XDy6+y_rG<G<BIN=9=lmAxGmTt9NZttSgm%u=P7vg5*%>+ aV-tL>!Vtom$IrmPz~JfX=d#Wzp$Pz9(qr%d literal 703 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?F<YIY)RhkE)4%caKYZ?lNlHo zI14-?iy0VruY)k7lg8`{1_lQ95>H=O_6JPdLTrql>soyo7?^xKT^vI)?!BG4H(SI} z;@Et_&V&3MjyyLy=FAhj<-+RPX{NcQ$CKsaHK$7&D|KAn9I<y;bHu5sw|7B$x|ZvP zaF;-SLDt^Rq@K=yI(H5jo~b)icjxUPyMn8C?SB6$dsdsC=D#X*b=cY<E(^vjong`z zabXQRKCG^~e|nA@Q-WtuywiN%V>9OOWrz^|c$c4l{#iNYYOV;iowG6-_lYwm^z77r z*|TKoBKL%Ihtp4F-+RU($ok>d;pYwIAD^-avVOR9I9(-_{U?9nUX2EEIlJ}-yZf`5 z+f{u<SwCDkoW6I-Tg^lEQ?{%WU`Rfr?fJIFu6xqHiWYmNl}|b9{V&b834eT>^Vb(6 zgNL7=G_tmze>$Ob(M;9^CC48p_Z({1DVxdgt8wRq584MZ{rBJ8@j-UKt@NG6GfSoJ zo>8uOV9H_B&z<jRvWe+UIfGC72Wf_RM_bQVXa7CHR59n``u(@ln+1<+^n|``xweVH zj-fe}=?3SYO$zTqaz)lzExYtni*e27TM7q+3buF&@IIK$YRLa)wa^iZ57(Q1@bFas z%3=J*kX_AKcrlRw^8v&D+G|M#cbzUg)ni<IATA+$mT{Z@5mWXf`3YLjc6s)j3+Fdn zT*TnR!E&G{doIVkJ?FDU8@9XlA7OR(WvF2}X57ZUK-#qM(#5%9rrbWudGCuhFeFKT zXkohY_UJn=_x6=y4(uoR9yq2lt||WTcY>0>y#xCRg97FsQJxH|-HtUxAK1O7e$fo0 z96nXqDO;j1>3Q;cG9OG|yu^RfImzTGa1391DerP#s3=)*jsyb(1B0ilpUXO@geCy% CX)JsI diff --git a/images/spike.png b/images/spike.png new file mode 100644 index 0000000000000000000000000000000000000000..1f0392703b91ddcc49f5fd47707ee5f82eb51021 GIT binary patch literal 355 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU=ZM7V_;xd_PkP)fq{W7$=lt9L587%VaH+a z!>bq=7&r?&B8wRq^pruEv0|xx83O|Ydx@v7EBj+683qpd%3J@<7#J8NOI#yLobz*Y zQ}ap~oQqNuOHxx5$}>wc6x=<11Hv2m#2FYEPJ6mIhD02GdLh<}DN&;BVg9s)teu9} z6-A<^aAw^|x)qvX`na-@J#KD=L5%{(ISYHc5A*ha7d<5D%&xp<>UP_*AE)o?+Zj6B zhgjZuASXTjw%B`#7Au~++)7up?VKt!Bon(s-#$`qG2G|XaXw3SzVnO3AOoj1^E<6- zZyG*C2smwi#O-!sLrzRh%ZHZttGn%m7s`ILY_Xf??{Z>8hHp*Fho<?fyZZ%}xeHth zJMPZ0UeKwl^7Z3>g%GLvAsq$MP7-o;KRhO>tbNp9&wr^|ZpFr>o(v2O44$rjF6*2U FngED9fl>ef literal 0 HcmV?d00001 diff --git a/images/spikeMask.png b/images/spikeMask.png deleted file mode 100644 index c0e42b65822d8b2548ffefcfd8538a4810c5ff96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmeAS@N?(olHy`uVBq!ia0y~yV6b6eV36ZrVPIgmHF4_^1_lPEByV>YhX3vTXZABN zFmM)lL>4nJa0`PlBg3pY5)2Fs>?NMQuIvw(xrMDQ&7YaQVqjpH<muuV;&J@wIZM6< z0|DoY=a=rNm%RAY$)U9+;M&QajoGg&l{ao)c=jmc1k0T*wkOXstNu-z`lUVeB5Rmq z_>GKR2i-2onsvz~D8F#LHhW%z?Mu&V-DVr)woHs&Dt<$>YU0*Qd}~_ElF~EX%i7A) z++Q-3weTfXmnhs`)O=G)H^F;L$KOj%-+a4ow*PBn^-%f!gKgT1sN1>wIm;Os7#KWV L{an^LB{Ts5eNtj+ diff --git a/jami-qt.pro b/jami-qt.pro index 4d7ea6a..2ef752f 100644 --- a/jami-qt.pro +++ b/jami-qt.pro @@ -136,5 +136,6 @@ FORMS += ./aboutdialog.ui \ ./videooverlay.ui \ ./videoview.ui \ ./downloadbar.ui \ - ./updateconfirmdialog.ui + ./updateconfirmdialog.ui \ + ./contactpicker.ui RESOURCES += ressources.qrc diff --git a/ressources.qrc b/ressources.qrc index fe80f96..9ca1fcb 100644 --- a/ressources.qrc +++ b/ressources.qrc @@ -1,86 +1,87 @@ <RCC> - <qresource prefix="/"> - <file>images/icons/outline-info-24px.svg</file> - <file>images/icons/baseline-camera_alt-24px.svg</file> - <file>images/icons/baseline-refresh-24px.svg</file> - <file>images/jami_rolling_spinner.gif</file> - <file>images/icons/baseline-close-24px.svg</file> - <file>images/icons/baseline-done-24px.svg</file> - <file>images/icons/baseline-error_outline-24px.svg</file> - <file>stylesheet.css</file> - <file>images/ajax-loader.gif</file> - <file>images/default_avatar_overlay.svg</file> - <file>images/FontAwesome.otf</file> - <file>images/logo-jami-standard-coul.png</file> - <file>images/qrcode.png</file> - <file>images/jami.ico</file> - <file>images/jami.png</file> - <file>images/waiting.gif</file> - <file>images/icons/ic_add_black_18dp_2x.png</file> - <file>images/icons/ic_arrow_back_24px.svg</file> - <file>images/icons/ic_arrow_back_white_24dp.png</file> - <file>images/icons/ic_arrow_drop_down_black_9dp_2x.png</file> - <file>images/icons/ic_arrow_drop_down_black_18dp_2x.png</file> - <file>images/icons/ic_arrow_drop_up_black_9dp_2x.png</file> - <file>images/icons/ic_arrow_drop_up_black_18dp_2x.png</file> - <file>images/icons/ic_arrow_forward_white_48dp_2x.png</file> - <file>images/icons/ic_arrow_tab_next_black_9dp_2x.png</file> - <file>images/icons/ic_arrow_tab_previous_black_9dp_2x.png</file> - <file>images/icons/ic_block_24px.svg</file> - <file>images/icons/ic_call_transfer_white_24px.png</file> - <file>images/icons/ic_chat_black_24dp_2x.png</file> - <file>images/icons/ic_chat_white_24dp.png</file> - <file>images/icons/ic_check_white_18dp_2x.png</file> - <file>images/icons/ic_clear_24px.svg</file> - <file>images/icons/ic_close_white_24dp.png</file> - <file>images/icons/ic_content_copy_white_24dp.png</file> - <file>images/icons/ic_delete_black_18dp_2x.png</file> - <file>images/icons/ic_done_white_24dp.png</file> - <file>images/icons/ic_folder_black_18dp_2x.png</file> - <file>images/icons/ic_group_add_white_24dp.png</file> - <file>images/icons/ic_high_quality_white_24dp.png</file> - <file>images/icons/ic_mic_off_white_24dp.png</file> - <file>images/icons/ic_pause_white_24dp.png</file> - <file>images/icons/ic_pause_white_100px.png</file> - <file>images/icons/ic_person_add_black_24dp_2x.png</file> - <file>images/icons/ic_person_add_white_24dp.png</file> - <file>images/icons/ic_phone_24px.svg</file> - <file>images/icons/ic_photo_camera_white_24dp_2x.png</file> - <file>images/icons/ic_search_black_18dp_2x.png</file> - <file>images/icons/ic_send_24px.svg</file> - <file>images/icons/ic_send_white_24dp.png</file> - <file>images/icons/ic_settings_white_48dp_2x.png</file> - <file>images/icons/ic_share_black_48dp_2x.png</file> - <file>images/icons/ic_video_call_24px.svg</file> - <file>images/icons/ic_videocam_off_white_24dp.png</file> - <file>images/icons/ic_videocam_white.png</file> - <file>images/icons/ic_voicemail_white_24dp_2x.png</file> - <file>images/icons/round-add-24px.svg</file> - <file>images/icons/round-arrow_drop_down-24px.svg</file> - <file>images/icons/round-arrow_drop_up-24px.svg</file> - <file>images/icons/round-arrow_right-24px.svg</file> - <file>images/icons/round-close-24px.svg</file> - <file>images/icons/round-edit-24px.svg</file> - <file>images/icons/round-folder-24px.svg</file> - <file>images/icons/round-remove_circle-24px.svg</file> - <file>images/icons/round-settings-24px.svg</file> - <file>images/icons/round-undo-24px.svg</file> - <file>web/chatview.css</file> - <file>web/chatview.html</file> - <file>web/chatview.js</file> - <file>web/linkify.js</file> - <file>web/linkify-html.js</file> - <file>web/linkify-string.js</file> - <file>web/qwebchannel.js</file> - <file>images/icons/round-check_circle-24px.svg</file> - <file>images/icons/round-error-24px.svg</file> - <file>images/icons/round-save_alt-24px.svg</file> - <file>images/jami_eclipse_spinner.gif</file> - <file>images/icons/ic_hide_password.png</file> - <file>images/icons/ic_show_password.png</file> - <file>images/icons/baseline-desktop_windows-24px.svg</file> - <file>images/icons/baseline-people-24px.svg</file> - <file>images/icons/round-add_a_photo-24px.svg</file> - <file>images/icons/ic_mic_white_24dp.png</file> - </qresource> + <qresource prefix="/"> + <file>images/icons/outline-info-24px.svg</file> + <file>images/icons/baseline-camera_alt-24px.svg</file> + <file>images/icons/baseline-refresh-24px.svg</file> + <file>images/jami_rolling_spinner.gif</file> + <file>images/icons/baseline-close-24px.svg</file> + <file>images/icons/baseline-done-24px.svg</file> + <file>images/icons/baseline-error_outline-24px.svg</file> + <file>stylesheet.css</file> + <file>images/ajax-loader.gif</file> + <file>images/default_avatar_overlay.svg</file> + <file>images/FontAwesome.otf</file> + <file>images/logo-jami-standard-coul.png</file> + <file>images/qrcode.png</file> + <file>images/jami.ico</file> + <file>images/jami.png</file> + <file>images/spike.png</file> + <file>images/waiting.gif</file> + <file>images/icons/ic_add_black_18dp_2x.png</file> + <file>images/icons/ic_arrow_back_24px.svg</file> + <file>images/icons/ic_arrow_back_white_24dp.png</file> + <file>images/icons/ic_arrow_drop_down_black_9dp_2x.png</file> + <file>images/icons/ic_arrow_drop_down_black_18dp_2x.png</file> + <file>images/icons/ic_arrow_drop_up_black_9dp_2x.png</file> + <file>images/icons/ic_arrow_drop_up_black_18dp_2x.png</file> + <file>images/icons/ic_arrow_forward_white_48dp_2x.png</file> + <file>images/icons/ic_arrow_tab_next_black_9dp_2x.png</file> + <file>images/icons/ic_arrow_tab_previous_black_9dp_2x.png</file> + <file>images/icons/ic_block_24px.svg</file> + <file>images/icons/ic_call_transfer_white_24px.png</file> + <file>images/icons/ic_chat_black_24dp_2x.png</file> + <file>images/icons/ic_chat_white_24dp.png</file> + <file>images/icons/ic_check_white_18dp_2x.png</file> + <file>images/icons/ic_clear_24px.svg</file> + <file>images/icons/ic_close_white_24dp.png</file> + <file>images/icons/ic_content_copy_white_24dp.png</file> + <file>images/icons/ic_delete_black_18dp_2x.png</file> + <file>images/icons/ic_done_white_24dp.png</file> + <file>images/icons/ic_folder_black_18dp_2x.png</file> + <file>images/icons/ic_group_add_white_24dp.png</file> + <file>images/icons/ic_high_quality_white_24dp.png</file> + <file>images/icons/ic_mic_off_white_24dp.png</file> + <file>images/icons/ic_pause_white_24dp.png</file> + <file>images/icons/ic_pause_white_100px.png</file> + <file>images/icons/ic_person_add_black_24dp_2x.png</file> + <file>images/icons/ic_person_add_white_24dp.png</file> + <file>images/icons/ic_phone_24px.svg</file> + <file>images/icons/ic_photo_camera_white_24dp_2x.png</file> + <file>images/icons/ic_search_black_18dp_2x.png</file> + <file>images/icons/ic_send_24px.svg</file> + <file>images/icons/ic_send_white_24dp.png</file> + <file>images/icons/ic_settings_white_48dp_2x.png</file> + <file>images/icons/ic_share_black_48dp_2x.png</file> + <file>images/icons/ic_video_call_24px.svg</file> + <file>images/icons/ic_videocam_off_white_24dp.png</file> + <file>images/icons/ic_videocam_white.png</file> + <file>images/icons/ic_voicemail_white_24dp_2x.png</file> + <file>images/icons/round-add-24px.svg</file> + <file>images/icons/round-arrow_drop_down-24px.svg</file> + <file>images/icons/round-arrow_drop_up-24px.svg</file> + <file>images/icons/round-arrow_right-24px.svg</file> + <file>images/icons/round-close-24px.svg</file> + <file>images/icons/round-edit-24px.svg</file> + <file>images/icons/round-folder-24px.svg</file> + <file>images/icons/round-remove_circle-24px.svg</file> + <file>images/icons/round-settings-24px.svg</file> + <file>images/icons/round-undo-24px.svg</file> + <file>web/chatview.css</file> + <file>web/chatview.html</file> + <file>web/chatview.js</file> + <file>web/linkify.js</file> + <file>web/linkify-html.js</file> + <file>web/linkify-string.js</file> + <file>web/qwebchannel.js</file> + <file>images/icons/round-check_circle-24px.svg</file> + <file>images/icons/round-error-24px.svg</file> + <file>images/icons/round-save_alt-24px.svg</file> + <file>images/jami_eclipse_spinner.gif</file> + <file>images/icons/ic_hide_password.png</file> + <file>images/icons/ic_show_password.png</file> + <file>images/icons/baseline-desktop_windows-24px.svg</file> + <file>images/icons/baseline-people-24px.svg</file> + <file>images/icons/round-add_a_photo-24px.svg</file> + <file>images/icons/ic_mic_white_24dp.png</file> + </qresource> </RCC> diff --git a/ring-client-windows.vcxproj b/ring-client-windows.vcxproj index 21fbdf4..3036e4e 100644 --- a/ring-client-windows.vcxproj +++ b/ring-client-windows.vcxproj @@ -230,6 +230,8 @@ <ClCompile Include="banneditemwidget.cpp" /> <ClCompile Include="bannedlistmodel.cpp" /> <ClCompile Include="animationhelpers.cpp" /> + <ClCompile Include="contactpicker.cpp" /> + <ClCompile Include="contactpickeritemdelegate.cpp" /> <ClCompile Include="currentaccountcombobox.cpp" /> <ClCompile Include="aboutdialog.cpp" /> <ClCompile Include="updatedownloaddialog.cpp" /> @@ -430,6 +432,14 @@ <IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;$(ProjectDir)..\ring-daemon\contrib\msvc\include;$(ProjectDir)..\daemon\contrib\msvc\include;$(ProjectDir)..\ring-lrc\src;$(ProjectDir)..\lrc\src;$(ProjectDir)winsparkle\include;$(ProjectDir)qrencode-win32\qrencode-win32;$(QTDIR)\include;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtXml;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtWebEngineWidgets;$(QTDIR)\include\QtWebChannel;$(QTDIR)\include\QtCore;$(QTDIR)\mkspecs\win32-msvc;.\release</IncludePath> <IncludePath Condition="'$(Configuration)|$(Platform)'=='ReleaseCompile|x64'">.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;$(ProjectDir)..\ring-daemon\contrib\msvc\include;$(ProjectDir)..\daemon\contrib\msvc\include;$(ProjectDir)..\ring-lrc\src;$(ProjectDir)..\lrc\src;$(ProjectDir)winsparkle\include;$(ProjectDir)qrencode-win32\qrencode-win32;$(QTDIR)\include;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtXml;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtWebEngineWidgets;$(QTDIR)\include\QtWebChannel;$(QTDIR)\include\QtCore;$(QTDIR)\mkspecs\win32-msvc;.\release</IncludePath> </QtMoc> + <QtMoc Include="contactpicker.h"> + <IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;$(ProjectDir)..\ring-daemon\contrib\msvc\include;$(ProjectDir)..\daemon\contrib\msvc\include;$(ProjectDir)..\ring-lrc\src;$(ProjectDir)..\lrc\src;$(ProjectDir)winsparkle\include;$(ProjectDir)qrencode-win32\qrencode-win32;$(QTDIR)\include;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtXml;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtWebEngineWidgets;$(QTDIR)\include\QtWebChannel;$(QTDIR)\include\QtCore;$(QTDIR)\mkspecs\win32-msvc;.\release</IncludePath> + <IncludePath Condition="'$(Configuration)|$(Platform)'=='ReleaseCompile|x64'">.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;$(ProjectDir)..\ring-daemon\contrib\msvc\include;$(ProjectDir)..\daemon\contrib\msvc\include;$(ProjectDir)..\ring-lrc\src;$(ProjectDir)..\lrc\src;$(ProjectDir)winsparkle\include;$(ProjectDir)qrencode-win32\qrencode-win32;$(QTDIR)\include;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtXml;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtWebEngineWidgets;$(QTDIR)\include\QtWebChannel;$(QTDIR)\include\QtCore;$(QTDIR)\mkspecs\win32-msvc;.\release</IncludePath> + </QtMoc> + <QtMoc Include="contactpickeritemdelegate.h"> + <IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;$(ProjectDir)..\ring-daemon\contrib\msvc\include;$(ProjectDir)..\daemon\contrib\msvc\include;$(ProjectDir)..\ring-lrc\src;$(ProjectDir)..\lrc\src;$(ProjectDir)winsparkle\include;$(ProjectDir)qrencode-win32\qrencode-win32;$(QTDIR)\include;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtXml;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtWebEngineWidgets;$(QTDIR)\include\QtWebChannel;$(QTDIR)\include\QtCore;$(QTDIR)\mkspecs\win32-msvc;.\release</IncludePath> + <IncludePath Condition="'$(Configuration)|$(Platform)'=='ReleaseCompile|x64'">.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;$(ProjectDir)..\ring-daemon\contrib\msvc\include;$(ProjectDir)..\daemon\contrib\msvc\include;$(ProjectDir)..\ring-lrc\src;$(ProjectDir)..\lrc\src;$(ProjectDir)winsparkle\include;$(ProjectDir)qrencode-win32\qrencode-win32;$(QTDIR)\include;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtXml;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtWebEngineWidgets;$(QTDIR)\include\QtWebChannel;$(QTDIR)\include\QtCore;$(QTDIR)\mkspecs\win32-msvc;.\release</IncludePath> + </QtMoc> <ClInclude Include="pixbufmanipulator.h" /> <QtMoc Include="ringbutton.h"> </QtMoc> @@ -607,6 +617,7 @@ <SubType>Designer</SubType> </QtUic> <QtUic Include="updatedownloaddialog.ui" /> + <QtUic Include="contactpicker.ui" /> <QtUic Include="deleteaccountdialog.ui"> <SubType>Designer</SubType> </QtUic> @@ -701,4 +712,4 @@ <UserProperties UicDir=".\GeneratedFiles" RccDir=".\GeneratedFiles" Qt5Version_x0020_x64="$(DefaultQtVersion)" /> </VisualStudio> </ProjectExtensions> -</Project> \ No newline at end of file +</Project> diff --git a/ring-client-windows.vcxproj.filters b/ring-client-windows.vcxproj.filters index f46f7a8..57f4527 100644 --- a/ring-client-windows.vcxproj.filters +++ b/ring-client-windows.vcxproj.filters @@ -207,6 +207,12 @@ <ClCompile Include="downloadmanager.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="contactpicker.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="contactpickeritemdelegate.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <QtMoc Include="aboutdialog.h"> @@ -356,6 +362,12 @@ <QtMoc Include="runguard.h"> <Filter>Header Files</Filter> </QtMoc> + <QtMoc Include="contactpicker.h"> + <Filter>Header Files</Filter> + </QtMoc> + <QtMoc Include="contactpickeritemdelegate.h"> + <Filter>Header Files</Filter> + </QtMoc> </ItemGroup> <ItemGroup> <CustomBuild Include="debug\moc_predefs.h.cbt"> @@ -638,6 +650,9 @@ <QtUic Include="updatedownloaddialog.ui"> <Filter>Form Files</Filter> </QtUic> + <QtUic Include="contactpicker.ui"> + <Filter>Form Files</Filter> + </QtUic> </ItemGroup> <ItemGroup> <None Include="images\FontAwesome.otf"> diff --git a/smartlistmodel.cpp b/smartlistmodel.cpp index db26c06..ef898db 100644 --- a/smartlistmodel.cpp +++ b/smartlistmodel.cpp @@ -32,9 +32,10 @@ #include "utils.h" #include "lrcinstance.h" -SmartListModel::SmartListModel(const std::string& accId, QObject *parent) +SmartListModel::SmartListModel(const std::string& accId, QObject *parent, bool contactList) : QAbstractItemModel(parent), - accId_(accId) + accId_(accId), + contactList_(contactList) { } @@ -42,6 +43,10 @@ int SmartListModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) { auto& accInfo = LRCInstance::accountModel().getAccountInfo(accId_); + if (contactList_) { + auto filterType = accInfo.profileInfo.type; + return accInfo.conversationModel->getFilteredConversations(filterType).size(); + } return accInfo.conversationModel->allFilteredConversations().size(); } return 0; // A valid QModelIndex returns 0 as no entry has sub-elements @@ -60,7 +65,15 @@ QVariant SmartListModel::data(const QModelIndex &index, int role) const } auto& accInfo = LRCInstance::accountModel().getAccountInfo(accId_); - const auto& item = accInfo.conversationModel->filteredConversation(index.row()); + + lrc::api::conversation::Info item; + if (contactList_) { + auto filterType = accInfo.profileInfo.type; + item = accInfo.conversationModel->getFilteredConversations(filterType).at(index.row()); + } else { + item = accInfo.conversationModel->filteredConversation(index.row()); + } + if (item.participants.size() > 0) { try { switch (role) { diff --git a/smartlistmodel.h b/smartlistmodel.h index 08e36a1..6320af3 100644 --- a/smartlistmodel.h +++ b/smartlistmodel.h @@ -35,7 +35,6 @@ public: using ConversationInfo = lrc::api::conversation::Info; using ContactInfo = lrc::api::contact::Info; - enum Role { DisplayName = Qt::UserRole + 1, DisplayID, @@ -51,7 +50,7 @@ public: ContextMenuOpen }; - explicit SmartListModel(const std::string& accId, QObject *parent = 0); + explicit SmartListModel(const std::string& accId, QObject *parent = 0, bool contactList = false); // QAbstractItemModel int rowCount(const QModelIndex &parent = QModelIndex()) const override; @@ -68,4 +67,5 @@ public: private: std::string accId_; + bool contactList_; }; diff --git a/stylesheet.css b/stylesheet.css index 12ca44d..f80075c 100644 --- a/stylesheet.css +++ b/stylesheet.css @@ -186,14 +186,14 @@ QPushButton#qualityButton, QPushButton#recButton { QPushButton#holdButton:hover, QPushButton#chatButton:hover, QPushButton#noMicButton:hover, QPushButton#noVideoButton:hover, QPushButton#transferButton:hover, QPushButton#addPersonButton:hover, QPushButton#joinButton:hover, QPushButton#qualityButton:hover, QPushButton#addToContactButton:hover, -QPushButton#recButton:hover{ +QPushButton#recButton:hover { background-color: rgba(0, 192, 213, 0.6); } QPushButton#holdButton:pressed, QPushButton#chatButton:pressed, QPushButton#noMicButton:pressed, QPushButton#noVideoButton:pressed, QPushButton#transferButton:pressed, QPushButton#addPersonButton:pressed, QPushButton#joinButton:pressed, QPushButton#qualityButton:pressed, QPushButton#addToContactButton:pressed, -QPushButton#recButton:pressed{ +QPushButton#recButton:pressed { background-color: rgba(0, 192, 213, 0.8); } @@ -722,4 +722,10 @@ SettingsWidget QListView { background-color: transparent; } -/* } SettingsWidget */ \ No newline at end of file +/* } SettingsWidget */ + +QWidget#contactPickerWidget { + border-radius: 10px; + border:solid 1px transparent; + background-color: white; +} diff --git a/videooverlay.cpp b/videooverlay.cpp index 656522a..c04965f 100644 --- a/videooverlay.cpp +++ b/videooverlay.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2015-2017 by Savoir-faire Linux * + * Copyright (C) 2015-2019 by Savoir-faire Linux * * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>* * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> * * * @@ -21,25 +21,28 @@ #include "ui_videooverlay.h" #include <QTime> +#include <QMouseEvent> #include "lrcinstance.h" +#include "contactpicker.h" #include "utils.h" VideoOverlay::VideoOverlay(QWidget* parent) : QWidget(parent), ui(new Ui::VideoOverlay), - oneSecondTimer_(new QTimer(this)) + oneSecondTimer_(new QTimer(this)), + contactPicker_(new ContactPicker(this)) { ui->setupUi(this); + setAttribute(Qt::WA_NoSystemBackground); + ui->bottomButtons->setMouseTracking(true); ui->chatButton->setCheckable(true); ui->onHoldLabel->setVisible(false); - setAttribute(Qt::WA_NoSystemBackground); - ui->recButton->setVisible(false); } @@ -90,9 +93,14 @@ VideoOverlay::setVideoMuteVisibility(bool visible) bool VideoOverlay::shouldShowOverlay() { + if (!LRCInstance::getCurrentCallModel()->hasCall(callId_)) { + return false; + } auto callInfo = LRCInstance::getCurrentCallModel()->getCall(callId_); - auto callPaused = callInfo.status == lrc::api::call::Status::PAUSED; - return ui->bottomButtons->underMouse() || ui->topInfoBar->underMouse() || callPaused; + return ui->bottomButtons->underMouse() || + ui->topInfoBar->underMouse() || + (callInfo.status == lrc::api::call::Status::PAUSED) || + contactPicker_->isActiveWindow(); } void @@ -130,50 +138,40 @@ VideoOverlay::on_chatButton_toggled(bool checked) void VideoOverlay::on_holdButton_clicked() { - auto selectedConvUid = LRCInstance::getSelectedConvUid(); - auto conversation = Utils::getConversationFromUid(selectedConvUid, - *LRCInstance::getCurrentConversationModel()); - auto& callId = conversation->callId; auto callModel = LRCInstance::getCurrentCallModel(); - if (callModel->hasCall(callId)) { - auto onHold = callModel->getCall(callId).status == lrc::api::call::Status::PAUSED; + if (callModel->hasCall(callId_)) { + callModel->togglePause(callId_); + auto onHold = callModel->getCall(callId_).status == lrc::api::call::Status::PAUSED; ui->holdButton->setChecked(!onHold); ui->onHoldLabel->setVisible(!onHold); - callModel->togglePause(callId); } } void VideoOverlay::on_noMicButton_toggled(bool checked) { - bool btn_status = false; + Q_UNUSED(checked); auto callModel = LRCInstance::getCurrentCallModel(); if (callModel->hasCall(callId_)) { callModel->toggleMedia(callId_, lrc::api::NewCallModel::Media::AUDIO); - btn_status = callModel->getCall(callId_).audioMuted; } } void VideoOverlay::on_noVideoButton_toggled(bool checked) { - bool btn_status = false; + Q_UNUSED(checked); auto callModel = LRCInstance::getCurrentCallModel(); if (callModel->hasCall(callId_)) { callModel->toggleMedia(callId_, lrc::api::NewCallModel::Media::VIDEO); - btn_status = callModel->getCall(callId_).videoMuted; } } void VideoOverlay::on_recButton_clicked() { - auto selectedConvUid = LRCInstance::getSelectedConvUid(); - auto conversation = Utils::getConversationFromUid(selectedConvUid, - *LRCInstance::getCurrentConversationModel()); - auto& callId = conversation->callId; auto callModel = LRCInstance::getCurrentCallModel(); - if (callModel->hasCall(callId)) { - callModel->toggleAudioRecord(callId); + if (callModel->hasCall(callId_)) { + callModel->toggleAudioRecord(callId_); } } diff --git a/videooverlay.h b/videooverlay.h index c59475d..28cef61 100644 --- a/videooverlay.h +++ b/videooverlay.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2015-2017 by Savoir-faire Linux * + * Copyright (C) 2015-2019 by Savoir-faire Linux * * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>* * * * This program is free software; you can redistribute it and/or modify * @@ -22,6 +22,8 @@ #include <QMenu> #include <QTimer> +class ContactPicker; + namespace Ui { class VideoOverlay; } @@ -56,6 +58,7 @@ private slots: private: Ui::VideoOverlay* ui; + ContactPicker* contactPicker_; bool dialogVisible_ = false; QTimer* oneSecondTimer_; std::string callId_; diff --git a/videooverlay.ui b/videooverlay.ui index 984bbad..34aaed7 100644 --- a/videooverlay.ui +++ b/videooverlay.ui @@ -106,6 +106,32 @@ </property> </widget> </item> + <item> + <widget class="QPushButton" name="transferCallButton"> + <property name="minimumSize"> + <size> + <width>36</width> + <height>36</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>36</width> + <height>36</height> + </size> + </property> + <property name="toolTip"> + <string>Transfer the call</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset> + <normaloff>images/icons/ic_call_transfer_white_24px.png</normaloff>images/icons/ic_call_transfer_white_24px.png</iconset> + </property> + </widget> + </item> <item> <widget class="QPushButton" name="chatButton"> <property name="minimumSize"> diff --git a/videoview.cpp b/videoview.cpp index 38d5001..ad2aa53 100644 --- a/videoview.cpp +++ b/videoview.cpp @@ -351,6 +351,10 @@ VideoView::pushRenderer(const std::string& callId) { auto call = callModel->getCall(callId); + if (call.isAudioOnly) { + return; + } + this->overlay_->callStarted(callId); this->overlay_->setVideoMuteVisibility(!LRCInstance::getCurrentCallModel()->getCall(callId).isAudioOnly); -- GitLab