endless scroll, text scaling

master
dsc 2 years ago
parent debdd1fda7
commit dda2b21a9a

@ -5,12 +5,15 @@ op Maemo:
sudo apt install -y ccache cmake build-essential libx11-dev zlib1g-dev libpng-dev qtbase5-dev libqt5svg5-dev libqt5svg5-dev libqt5maemo5-dev libqt5x11extras5-dev gdb libqt5quickcontrols2-5 qtquickcontrols2-5-dev qml-module-qtquick-controls2 qml-module-qtquick2 qml-module-qtquick-layouts qml-module-qtquick-controls qml-module-qtquick-extras qml-module-qtquick-dialogs libqt5websockets5-dev libqt5x11extras5-dev qml-module-qtquick-controls qml-module-qtquick-layouts qtquickcontrols2-5-dev
```
Als `user`:
As `user`:
```bash
cmake -Bbuild .
make -Cbuild -j2
./build/bin/conversations
# mem usage
/usr/bin/time -v ./build/bin/conversations
```
### remote debug (dev)

@ -20,7 +20,13 @@ if(MAEMO)
find_package(Qt5 REQUIRED COMPONENTS Maemo5)
endif()
qt5_add_resources(RESOURCES assets.qrc assets/images/flags/flags.qrc qml/chat/chatty/chatty.qrc qml/chat/whatsthat/whatsthat.qrc)
qt5_add_resources(RESOURCES
assets.qrc
assets/images/flags/flags.qrc
qml/components/qml_components.qrc
qml/chat/chatty/chatty.qrc
qml/chat/whatsthat/whatsthat.qrc
)
if(MAEMO)
qt5_add_resources(RESOURCES assets_maemo.qrc)

@ -14,5 +14,6 @@
<file alias="general_sending_failed.png">assets/images/icons/general_sending_failed.png</file>
<file alias="general_contacts.png">assets/images/icons/general_contacts.png</file>
<file alias="general_missed.png">assets/images/icons/general_missed.png</file>
<file alias="rss_reader_move_down.png">assets/images/icons/rss_reader_move_down.png</file>
</qresource>
</RCC>

@ -14,5 +14,6 @@
<file alias="general_sending_failed.png">/usr/share/icons/hicolor/48x48/hildon/general_sending_failed.png</file>
<file alias="general_contacts.png">/usr/share/icons/hicolor/48x48/hildon/general_contacts.png</file>
<file alias="general_missed.png">/usr/share/icons/hicolor/48x48/hildon/general_missed.png</file>
<file alias="rss_reader_move_down.png">/usr/share/icons/hicolor/48x48/hildon/rss_reader_move_down.png</file>
</qresource>
</RCC>

@ -18,19 +18,27 @@
ChatWindow * ChatWindow::pChatWindow = nullptr;
ChatWindow::ChatWindow(Conversations *ctx, QWidget *parent) :
ChatWindow::ChatWindow(Conversations *ctx, const QString &group_uid, const QString &local_uid, const QString &remote_uid, QWidget *parent) :
QMainWindow(parent),
ui(new Ui::ChatWindow),
m_group_uid(group_uid),
m_local_uid(local_uid),
m_remote_uid(remote_uid),
m_ctx(ctx) {
pChatWindow = this;
ui->setupUi(this);
this->chatModel = new ChatModel(this);
this->chatModel->getMessages(remote_uid);
#ifdef MAEMO
setProperty("X-Maemo-StackedWindow", 1);
setProperty("X-Maemo-Orientation", 2);
#endif
auto *qctx = ui->quick->rootContext();
qctx->setContextProperty("chatModel", m_ctx->chatModel);
qctx->setContextProperty("chatModel", this->chatModel);
qctx->setContextProperty("ctx", m_ctx);
auto theme = config()->get(ConfigKeys::ChatTheme).toString();
if(theme == "chatty")
@ -56,7 +64,7 @@ Conversations *ChatWindow::getContext(){
}
void ChatWindow::closeEvent(QCloseEvent *event) {
m_ctx->chatModel->clear();
this->chatModel->clear();
QWidget::closeEvent(event);
}

@ -26,10 +26,12 @@ class ChatWindow : public QMainWindow {
Q_OBJECT
public:
explicit ChatWindow(Conversations *ctx, QWidget *parent = nullptr);
Ui::ChatWindow *ui;
explicit ChatWindow(Conversations *ctx, const QString &group_uid, const QString &local_uid, const QString &remote_uid, QWidget *parent = nullptr);
static Conversations *getContext();
~ChatWindow() override;
Ui::ChatWindow *ui;
ChatModel *chatModel;
private slots:
void onGatherMessage();
@ -40,6 +42,9 @@ signals:
private:
Conversations *m_ctx;
static ChatWindow *pChatWindow;
QString m_remote_uid;
QString m_local_uid;
QString m_group_uid;
void closeEvent(QCloseEvent *event) override;
};

@ -10,15 +10,6 @@
#include "lib/utils.h"
#include "lib/globals.h"
#define LOOKUP_INT(x) \
g_value_get_int((const GValue*)g_hash_table_lookup(values, x))
#define LOOKUP_BOOL(x) \
g_value_get_boolean((const GValue*)g_hash_table_lookup(values, x))
#define LOOKUP_STR(x) \
g_value_get_string((const GValue*)g_hash_table_lookup(values, x))
Conversations::Conversations(QCommandLineParser *cmdargs) {
this->cmdargs = cmdargs;
@ -35,133 +26,16 @@ Conversations::Conversations(QCommandLineParser *cmdargs) {
// flags, iso codes, etc
countries = new Countries();
m_textScaling = config()->get(ConfigKeys::TextScaling).toFloat();
if(this->isDebug) {
qDebug() << "configRoot: " << configRoot;
qDebug() << "homeDir: " << homeDir;
qDebug() << "configDirectory: " << configDirectory;
}
chatModel = new ChatModel();
chatOverviewModel = new ChatOverviewModel();
this->getOverviewMessages();
}
void Conversations::requestChat(const QString &group_uid, const QString &local_uid, const QString &remote_uid) {
this->getMessages(remote_uid, 10, 0);
emit showChat();
}
rtcom_query* Conversations::rtcomStartQuery(const int limit, const int offset, const RTComElQueryGroupBy group_by) {
RTComElQuery *query = NULL;
RTComElIter *it = NULL;
RTComEl *el = NULL;
el = rtcom_el_new();
query = rtcom_el_query_new(el);
if(group_by != RTCOM_EL_QUERY_GROUP_BY_NONE)
rtcom_el_query_set_group_by(query, group_by);
rtcom_el_query_set_limit(query, limit);
rtcom_el_query_set_offset(query, offset);
return new rtcom_query{query, it , el};
}
QList<ChatMessage*> Conversations::rtcomIterateResults(rtcom_query *query_struct) {
QList<ChatMessage*> results;
query_struct->it = rtcom_el_get_events(query_struct->el, query_struct->query);
if(query_struct->it && rtcom_el_iter_first(query_struct->it)) {
do {
GHashTable *values = NULL;
values = rtcom_el_iter_get_value_map(
query_struct->it,
"id",
"service",
"group-uid",
"local-uid",
"remote-uid",
"remote-name",
"remote-ebook-uid",
"content",
"icon-name",
"start-time",
"event-count",
"group-title",
"event-type",
"outgoing",
"flags",
NULL);
auto *item = new ChatMessage(
LOOKUP_INT("id"),
LOOKUP_STR("service"),
LOOKUP_STR("group-uid"),
LOOKUP_STR("local-uid"),
LOOKUP_STR("remote-uid"),
LOOKUP_STR("remote-name"),
LOOKUP_STR("remote-ebook-uid"),
LOOKUP_STR("content"),
LOOKUP_STR("icon-name"),
LOOKUP_INT("start-time"),
LOOKUP_INT("event-count"),
LOOKUP_STR("group-title"),
LOOKUP_STR("event-type"),
LOOKUP_BOOL("outgoing"),
LOOKUP_INT("flags"));
results << item;
} while (rtcom_el_iter_next(query_struct->it));
g_object_unref(query_struct->it);
} else {
qCritical() << "Failed to init iterator to start";
}
g_object_unref(query_struct->query);
return results;
}
void Conversations::getOverviewMessages(const int limit, const int offset) const {
rtcom_query* query_struct = Conversations::rtcomStartQuery(limit, offset, RTCOM_EL_QUERY_GROUP_BY_CONTACT);
bool query_prepared = FALSE;
query_prepared = rtcom_el_query_prepare(query_struct->query,
"service-id", m_rtcom_sms_service_id, RTCOM_EL_OP_EQUAL, NULL);
if(!query_prepared) {
qCritical() << "Couldn't prepare query";
g_object_unref(query_struct->query);
delete query_struct;
return;
}
auto results = Conversations::rtcomIterateResults(query_struct);
for (const auto &message: results)
this->chatOverviewModel->appendOverviewItem(message);
}
void Conversations::getMessages(const QString &remote_uid, const int limit, const int offset) const {
rtcom_query* query_struct = Conversations::rtcomStartQuery(limit, offset, RTCOM_EL_QUERY_GROUP_BY_NONE);
bool query_prepared = FALSE;
query_prepared = rtcom_el_query_prepare(query_struct->query,
"remote-uid", remote_uid.toStdString().c_str(), RTCOM_EL_OP_EQUAL,
"service-id", m_rtcom_sms_service_id, RTCOM_EL_OP_EQUAL,
NULL);
if(!query_prepared) {
qCritical() << "Couldn't prepare query";
g_object_unref(query_struct->query);
delete query_struct;
return;
}
auto results = Conversations::rtcomIterateResults(query_struct);
QList<ChatMessage*>::const_iterator rIt;
rIt = results.constEnd();
while(rIt != results.constBegin()) {
--rIt;
this->chatModel->appendMessage(*rIt);
}
chatOverviewModel = new ChatModel();
this->chatOverviewModel->getOverviewMessages();
}
void Conversations::onSendMessage(const QString &message) {
@ -176,6 +50,11 @@ void Conversations::setWindowTitle(const QString &title) {
emit setTitle(title);
}
void Conversations::onTextScalingChanged() {
m_textScaling = config()->get(ConfigKeys::TextScaling).toFloat();
emit textScalingChanged();
}
void Conversations::createConfigDirectory(const QString &dir) {
QStringList createDirs({dir});
for(const auto &d: createDirs) {

@ -6,22 +6,12 @@
#include <QNetworkAccessManager>
#include <QTimer>
#include <rtcom-eventlogger/eventlogger.h>
#include <glib.h>
#include <glib/gstdio.h>
#include "lib/http.h"
#include "lib/countries.h"
#include "lib/config.h"
#include "models/ChatModel.h"
#include "models/ChatOverviewModel.h"
#include "wsclient.h"
struct rtcom_query {
RTComElQuery *query;
RTComElIter *it;
RTComEl *el = NULL;
};
class Conversations : public QObject {
Q_OBJECT
Q_PROPERTY(QString configRootconfigDirectory MEMBER configDirectory);
@ -29,11 +19,15 @@ class Conversations : public QObject {
Q_PROPERTY(QString pathGenericData MEMBER pathGenericData);
Q_PROPERTY(QString homeDir MEMBER homeDir);
Q_PROPERTY(QString accountName MEMBER accountName);
Q_PROPERTY(bool isDebug MEMBER isDebug NOTIFY debugChanged);
Q_PROPERTY(float scaleFactor MEMBER m_textScaling NOTIFY textScalingChanged);
Q_PROPERTY(float isMaemo MEMBER isMaemo NOTIFY isMaemoChanged);
public:
explicit Conversations(QCommandLineParser *cmdargs);
~Conversations() override;
bool isDebug = false;
bool isMaemo = false;
QCommandLineParser *cmdargs;
@ -47,34 +41,28 @@ public:
static void createConfigDirectory(const QString &dir) ;
WSClient *ws;
Countries *countries;
ChatModel *chatModel;
ChatOverviewModel *chatOverviewModel;
ChatModel *chatOverviewModel;
void setWindowTitle(const QString &title);
static rtcom_query* rtcomStartQuery(int limit, int offset, RTComElQueryGroupBy group_by = RTCOM_EL_QUERY_GROUP_BY_NONE);
static QList<ChatMessage*> rtcomIterateResults(rtcom_query *query_struct);
void getMessages(const QString &remote_uid, int limit, int offset) const;
void getOverviewMessages(int limit = 20, int offset = 0) const;
signals:
void clockSkewDetected();
void setTitle(const QString &title); // set window title
void showChat();
void debugChanged();
void textScalingChanged();
void isMaemoChanged();
public slots:
void onSendMessage(const QString &message);
void requestChat(const QString &group_uid, const QString &local_uid, const QString &remote_uid);
void onTextScalingChanged();
private:
float m_textScaling = 1.0;
QTimer m_hibernateTimer;
std::chrono::seconds m_hibernateDetectInterval{300};
std::chrono::time_point<std::chrono::steady_clock> m_hibernatePreviousTime;
gint m_rtcom_sms_service_id = 3; // rtcom_el_get_service_id(el, "RTCOM_EL_SERVICE_SMS");
};
#endif // CONV_CONTEXT_H

@ -18,6 +18,7 @@ static const QHash<ConfigKeys::ConfigKey, ConfigDirective> configStrings = {
// General
{ConfigKeys::MaemoTest,{QS("MaemoTest"), ""}},
{ConfigKeys::ChatTheme,{QS("ChatTheme"), "whatsthat"}},
{ConfigKeys::TextScaling,{QS("TextScaling"), 1.0}},
};
QPointer<Config> Config::m_instance(nullptr);

@ -13,7 +13,7 @@ namespace ConfigKeys
enum ConfigKey {
MaemoTest,
ChatTheme,
FooBar
TextScaling
};
Q_ENUM_NS(ConfigKey)
}

@ -0,0 +1,88 @@
#include <rtcom-eventlogger/eventlogger.h>
#include <glib.h>
#include <glib/gstdio.h>
#include "models/ChatMessage.h"
struct rtcom_query {
RTComElQuery *query = NULL;
RTComElIter *it = NULL;
RTComEl *el = NULL;
};
#define LOOKUP_INT(x) \
g_value_get_int((const GValue*)g_hash_table_lookup(values, x))
#define LOOKUP_BOOL(x) \
g_value_get_boolean((const GValue*)g_hash_table_lookup(values, x))
#define LOOKUP_STR(x) \
g_value_get_string((const GValue*)g_hash_table_lookup(values, x))
rtcom_query* rtcomStartQuery(const int limit, const int offset, const RTComElQueryGroupBy group_by) {
RTComElQuery *query = NULL;
RTComElIter *it = NULL;
RTComEl *el = NULL;
el = rtcom_el_new();
query = rtcom_el_query_new(el);
if(group_by != RTCOM_EL_QUERY_GROUP_BY_NONE)
rtcom_el_query_set_group_by(query, group_by);
rtcom_el_query_set_limit(query, limit);
rtcom_el_query_set_offset(query, offset);
return new rtcom_query{query, it , el};
}
QList<ChatMessage*> rtcomIterateResults(rtcom_query *query_struct) {
QList<ChatMessage*> results;
query_struct->it = rtcom_el_get_events(query_struct->el, query_struct->query);
if(query_struct->it && rtcom_el_iter_first(query_struct->it)) {
do {
GHashTable *values = NULL;
values = rtcom_el_iter_get_value_map(
query_struct->it,
"id",
"service",
"group-uid",
"local-uid",
"remote-uid",
"remote-name",
"remote-ebook-uid",
"content",
"icon-name",
"start-time",
"event-count",
"group-title",
"event-type",
"outgoing",
"flags",
NULL);
auto *item = new ChatMessage(
LOOKUP_INT("id"),
LOOKUP_STR("service"),
LOOKUP_STR("group-uid"),
LOOKUP_STR("local-uid"),
LOOKUP_STR("remote-uid"),
LOOKUP_STR("remote-name"),
LOOKUP_STR("remote-ebook-uid"),
LOOKUP_STR("content"),
LOOKUP_STR("icon-name"),
LOOKUP_INT("start-time"),
LOOKUP_INT("event-count"),
LOOKUP_STR("group-title"),
LOOKUP_STR("event-type"),
LOOKUP_BOOL("outgoing"),
LOOKUP_INT("flags"));
results << item;
} while (rtcom_el_iter_next(query_struct->it));
g_object_unref(query_struct->it);
} else {
qCritical() << "Failed to init iterator to start";
}
g_object_unref(query_struct->query);
return results;
}

@ -67,6 +67,9 @@ int main(int argc, char *argv[]) {
auto *ctx = new Conversations(&parser);
ctx->applicationPath = argv_.at(0);
ctx->isDebug = debugMode;
#ifdef MAEMO
ctx->isMaemo = true;
#endif
auto *mainWindow = new MainWindow(ctx);
return QApplication::exec();
}

@ -25,6 +25,7 @@ MainWindow::MainWindow(Conversations *ctx, QWidget *parent) :
#ifdef MAEMO
setProperty("X-Maemo-StackedWindow", 1);
setProperty("X-Maemo-Orientation", 2);
#endif
this->screenDpiRef = 128;
@ -47,17 +48,16 @@ MainWindow::MainWindow(Conversations *ctx, QWidget *parent) :
auto *qctx = ui->quick->rootContext();
qctx->setContextProperty("cfg", config());
qctx->setContextProperty("ctx", m_ctx);
qctx->setContextProperty("chatOverviewModel", m_ctx->chatOverviewModel);
ui->quick->setSource(QUrl("qrc:/overview.qml"));
ui->menuBar->hide();
connect(ui->actionSettings, &QAction::triggered, this, &MainWindow::openSettingsWindow);
connect((QObject*)ui->quick->rootObject(), SIGNAL(rowClicked(QString, QString, QString)), ctx, SLOT(requestChat(QString, QString, QString)));
//connect(this, &MainWindow::requestChatWindow, this, &MainWindow::openChatWindow);
connect((QObject*)ui->quick->rootObject(), SIGNAL(rowClicked(QString, QString, QString)), this, SLOT(openChatWindow(QString, QString, QString)));
connect(m_ctx, &Conversations::setTitle, this, &QMainWindow::setWindowTitle);
connect(m_ctx, &Conversations::showChat, this, &MainWindow::openChatWindow);
this->show();
}
@ -65,10 +65,12 @@ MainWindow::MainWindow(Conversations *ctx, QWidget *parent) :
void MainWindow::openSettingsWindow() {
m_settings = new Settings(m_ctx, this);
m_settings->show();
connect(m_settings, &Settings::textScalingChanged, this->m_ctx, &Conversations::onTextScalingChanged);
}
void MainWindow::openChatWindow() {
m_chatWindow = new ChatWindow(m_ctx, this);
void MainWindow::openChatWindow(const QString &group_uid, const QString &local_uid, const QString &remote_uid) {
m_chatWindow = new ChatWindow(m_ctx, group_uid, local_uid, remote_uid, this);
m_chatWindow->show();
connect(m_chatWindow, &ChatWindow::sendMessage, this->m_ctx, &Conversations::onSendMessage);

@ -42,7 +42,7 @@ public:
qreal screenRatio;
private slots:
void openChatWindow();
void openChatWindow(const QString &group_uid, const QString &local_uid, const QString &remote_uid);
void openSettingsWindow();
private:

@ -48,6 +48,9 @@ QString ChatMessage::hourstr() const { return m_date.toString("hh:mm"); }
QString ChatMessage::datestr() const { return m_date.toString("dd/MM/yyyy"); }
bool ChatMessage::isHead() const {
if(previous == nullptr) return true;
if(m_text == "got in") {
int wegewg = 1;
}
if(previous->cid() == m_cid) return false;
return true;
}

@ -1,12 +1,26 @@
#include <QObject>
#include <QDebug>
#include "lib/rtcom.h"
#include "models/ChatModel.h"
ChatModel::ChatModel(QObject *parent)
: QAbstractListModel(parent) {
}
void ChatModel::prependMessage(ChatMessage *message) {
if(!chats.isEmpty()) {
auto *n = chats.at(0);
message->next = n;
n->previous = message;
}
beginInsertRows(QModelIndex(), 0, 0);
chats.prepend(message);
endInsertRows();
}
void ChatModel::appendMessage(ChatMessage *message) {
const int idx = rowCount();
if(idx != 0 && !chats.isEmpty()) {
@ -16,7 +30,7 @@ void ChatModel::appendMessage(ChatMessage *message) {
}
beginInsertRows(QModelIndex(), idx, rowCount());
chats << message;
chats.append(message);
endInsertRows();
}
@ -30,7 +44,13 @@ QVariant ChatModel::data(const QModelIndex &index, int role) const {
return QVariant();
const ChatMessage *message = chats[index.row()];
if (role == NameRole)
if (role == GroupUIDRole)
return message->group_uid();
else if (role == LocalUIDRole)
return message->local_uid();
else if (role == RemoteNameRole)
return message->remote_name();
else if (role == NameRole || role == RemoteUIDRole)
return message->remote_uid();
else if (role == DateRole)
return message->datestr();
@ -44,11 +64,19 @@ QVariant ChatModel::data(const QModelIndex &index, int role) const {
return message->isLast();
else if (role == OutgoingRole)
return message->outgoing();
else if (role == IconNameRole)
return message->icon_name();
else if (role == EventIDRole)
return message->event_id();
return QVariant();
}
QHash<int, QByteArray> ChatModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[GroupUIDRole] = "group_uid";
roles[LocalUIDRole] = "local_uid";
roles[RemoteUIDRole] = "remote_uid";
roles[RemoteNameRole] = "remote_name";
roles[NameRole] = "name";
roles[DateRole] = "datestr";
roles[HourRole] = "hourstr";
@ -56,6 +84,8 @@ QHash<int, QByteArray> ChatModel::roleNames() const {
roles[isHeadRole] = "isHead";
roles[isLastRole] = "isLast";
roles[OutgoingRole] = "outgoing";
roles[IconNameRole] = "icon_name";
roles[EventIDRole] = "event_id";
return roles;
}
@ -67,3 +97,79 @@ void ChatModel::clear() {
endResetModel();
}
void ChatModel::getOverviewMessages(const int limit, const int offset) {
rtcom_query* query_struct = rtcomStartQuery(limit, offset, RTCOM_EL_QUERY_GROUP_BY_CONTACT);
bool query_prepared = FALSE;
query_prepared = rtcom_el_query_prepare(query_struct->query,
"service-id", m_rtcom_sms_service_id, RTCOM_EL_OP_EQUAL, NULL);
if(!query_prepared) {
qCritical() << "Couldn't prepare query";
g_object_unref(query_struct->query);
delete query_struct;
return;
}
auto results = rtcomIterateResults(query_struct);
for (const auto &message: results)
this->appendMessage(message);
}
unsigned int ChatModel::getMessages(const QString &remote_uid) {
return this->getMessages(remote_uid, m_limit, m_offset);
}
unsigned int ChatModel::getMessages(const QString &remote_uid, const int limit, const int offset) {
m_remote_uid = remote_uid;
rtcom_query* query_struct = rtcomStartQuery(limit, offset, RTCOM_EL_QUERY_GROUP_BY_NONE);
bool query_prepared = FALSE;
query_prepared = rtcom_el_query_prepare(query_struct->query,
"remote-uid", remote_uid.toStdString().c_str(), RTCOM_EL_OP_EQUAL,
"service-id", m_rtcom_sms_service_id, RTCOM_EL_OP_EQUAL,
NULL);
if(!query_prepared) {
qCritical() << "Couldn't prepare query";
g_object_unref(query_struct->query);
delete query_struct;
return 0;
}
auto results = rtcomIterateResults(query_struct);
bool prepend = offset != 0;
if(prepend) {
for(auto const &message: results) {
this->prependMessage(message);
}
} else {
QList<ChatMessage *>::const_iterator rIt;
rIt = results.constEnd();
while (rIt != results.constBegin()) {
--rIt;
if (offset == 0)
this->appendMessage(*rIt);
}
}
return results.length();
}
unsigned int ChatModel::getPage() {
// called from QML for endless scroll, advances m_offset
m_page += 1;
m_offset = m_page * m_limit;
emit offsetChanged();
qDebug() << __FUNCTION__ << "limit:" << m_limit << " offset:" << m_offset;
auto count = this->getMessages(m_remote_uid, m_limit, m_offset);
if(count < m_limit) {
m_exhausted = true;
emit exhaustedChanged();
}
return count;
}

@ -5,6 +5,10 @@
#include <QDateTime>
#include <QStringList>
#include <rtcom-eventlogger/eventlogger.h>
#include <glib.h>
#include <glib/gstdio.h>
#include "models/ChatMessage.h"
@ -19,19 +23,56 @@ public:
MessageRole,
isHeadRole,
isLastRole,
OutgoingRole
OutgoingRole,
GroupUIDRole,
LocalUIDRole,
RemoteUIDRole,
RemoteNameRole,
IconNameRole,
EventIDRole
};
ChatModel(QObject *parent = nullptr);
explicit ChatModel(QObject *parent = nullptr);
void prependMessage(ChatMessage *message);
void appendMessage(ChatMessage *message);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
QList<ChatMessage*> chats;
// endless scroll exhausted
Q_PROPERTY(bool exhausted READ is_exhausted NOTIFY exhaustedChanged);
Q_PROPERTY(int limit READ limit NOTIFY limitChanged);
Q_PROPERTY(int offset READ offset NOTIFY offsetChanged);
int limit() const { return m_limit; }
int offset() const { return m_offset; }
bool is_exhausted() {
return m_exhausted;
}
Q_INVOKABLE unsigned int getPage();
unsigned int getMessages(const QString &remote_uid);
unsigned int getMessages(const QString &remote_uid, int limit, int offset);
void getOverviewMessages(int limit = 20, int offset = 0);
void clear();
signals:
void exhaustedChanged();
void limitChanged();
void offsetChanged();
protected:
QHash<int, QByteArray> roleNames() const;
private:
QString m_remote_uid;
int m_page = 0;
int m_limit = 4;
int m_offset = 0;
bool m_exhausted = false;
gint m_rtcom_sms_service_id = 3; // rtcom_el_get_service_id(el, "RTCOM_EL_SERVICE_SMS");
};
#endif

@ -1,61 +0,0 @@
#include <QObject>
#include <QDebug>
#include "models/ChatOverviewModel.h"
#include "models/ChatMessage.h"
ChatOverviewModel::ChatOverviewModel(QObject *parent)
: QAbstractListModel(parent) {
}
void ChatOverviewModel::appendOverviewItem(ChatMessage *message) {
const int idx = rowCount();
beginInsertRows(QModelIndex(), idx, rowCount());
items << message;
endInsertRows();
}
int ChatOverviewModel::rowCount(const QModelIndex & parent) const {
Q_UNUSED(parent);
return items.count();
}
QVariant ChatOverviewModel::data(const QModelIndex &index, int role) const {
if (index.row() < 0 || index.row() >= items.count())
return QVariant();
const ChatMessage *message = items[index.row()];
if (role == RemoteUIDRole)
return message->remote_uid();
else if (role == GroupUIDRole)
return message->group_uid();
else if (role == LocalUIDRole)
return message->local_uid();
else if (role == RemoteNameRole)
return message->remote_name();
else if (role == DateRole)
return message->datestr();
else if (role == HourRole)
return message->hourstr();
else if (role == MessageRole)
return message->text();
else if (role == OutgoingRole)
return message->outgoing();
else if (role == IconNameRole)
return message->icon_name();
return QVariant();
}
QHash<int, QByteArray> ChatOverviewModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[GroupUIDRole] = "group_uid";
roles[LocalUIDRole] = "local_uid";
roles[RemoteUIDRole] = "remote_uid";
roles[RemoteNameRole] = "remote_name";
roles[DateRole] = "datestr";
roles[HourRole] = "hourstr";
roles[MessageRole] = "message";
roles[OutgoingRole] = "outgoing";
roles[IconNameRole] = "icon_name";
return roles;
}

@ -1,39 +0,0 @@
#ifndef CHATOVERVIEWMODEL_H
#define CHATOVERVIEWMODEL_H
#include <QAbstractListModel>
#include <QDateTime>
#include <QStringList>
#include "models/ChatMessage.h"
class ChatOverviewModel : public QAbstractListModel
{
Q_OBJECT
public:
// Name, date, time, content, type?
enum ChatOverviewModelRoles {
RemoteUIDRole = Qt::UserRole + 1,
GroupUIDRole,
LocalUIDRole,
RemoteNameRole,
DateRole,
HourRole,
MessageRole,
OutgoingRole,
IconNameRole
};
ChatOverviewModel(QObject *parent = nullptr);
void appendOverviewItem(ChatMessage *item);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
QList<ChatMessage*> items;
protected:
QHash<int, QByteArray> roleNames() const;
};
#endif

@ -88,16 +88,12 @@ Rectangle {
model: xxx
delegate: RowLayout {
id: item
property int itemHeight: 32
property int itemHeight: textColumn.implicitHeight + 12
property bool isSelf: name == "dsc"
height: itemHeight + 12
width: parent.width
spacing: 0
Component.onCompleted: {
itemHeight = textColumn.implicitHeight + 12;
}
Item {
visible: isSelf
Layout.fillWidth: true

@ -2,10 +2,13 @@ import QtQuick 2.0
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.0
Rectangle {
import "../components" as Components
Components.ChatRoot {
id: root
visible: true
color: "black"
chatList: chatListView
property string avatarBorderColor: "#999999"
property string chatBorderColor: "#30302f"
property string nickColor: "#86d5fc"
@ -13,7 +16,7 @@ Rectangle {
property int itemHeightDefault: 68
property int itemHeightSmall: 32
ListView {
Components.ChatListView {
id: chatListView
property int nickWidth: 0
anchors.fill: parent
@ -21,11 +24,7 @@ Rectangle {
anchors.leftMargin: 20
anchors.rightMargin: 20
model: chatModel
onCountChanged: { // scroll to bottom
chatListView.currentIndex = count - 1
}
onScrollToBottom: root.scrollToBottom();
delegate: Rectangle {
property int itemHeight: isHead ? root.itemHeightDefault : root.itemHeightSmall;
@ -85,7 +84,7 @@ Rectangle {
return name
}
color: nickColor
font.pointSize: 18
font.pointSize: 18 * ctx.scaleFactor
Component.onCompleted: {
if(implicitWidth > chatListView.nickWidth)
@ -103,7 +102,7 @@ Rectangle {
text: message
color: "white"
wrapMode: Text.WordWrap
font.pointSize: 14
font.pointSize: 14 * ctx.scaleFactor
font.bold: name == "_self" ? true : false;
}
@ -120,7 +119,7 @@ Rectangle {
text: message
color: "white"
wrapMode: Text.WordWrap
font.pointSize: 14
font.pointSize: 14 * ctx.scaleFactor
font.bold: name == "_self" ? true : false;
}
@ -148,10 +147,20 @@ Rectangle {
textFormat: Text.PlainText
text: hourstr
color: "grey"
font.pointSize: 12
font.pointSize: 12 * ctx.scaleFactor
}
}
}
}
}
onFetchHistory: {
// Prepend new items to the model by calling `getPage()`.
// temp. disable visibility to 'break' the touch gesture,
// if we dont the list scrolling bugs out by "jumping"
chatListView.visible = false;
var count_results = chatModel.getPage();
chatListView.positionViewAtIndex(count_results, ListView.Visible)
chatListView.visible = true;
}
}

@ -2,10 +2,11 @@ import QtQuick 2.0
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.0
Rectangle {
import "../components" as Components
Components.ChatRoot {
id: root
visible: true
color: "grey"
chatList: chatListView
property string chatBackgroundSelf: "#056162"
property string chatBackgroundThem: "#262d31"
@ -13,27 +14,25 @@ Rectangle {
property int itemHeightSmall: 32
Image {
// background
source: "qrc:/whatsthat/bg.png"
anchors.fill: parent
fillMode: Image.Tile
}
ListView {
Components.ChatListView {
id: chatListView
anchors.fill: parent
anchors.topMargin: 10
anchors.leftMargin: 32
anchors.rightMargin: 32
model: chatModel
onCountChanged: { // scroll to bottom
chatListView.currentIndex = count - 1
}
onScrollToBottom: root.scrollToBottom();
delegate: RowLayout {
id: item
property int itemHeight: 32
property int itemHeight: textColumn.implicitHeight + 12
height: itemHeight + 12
width: parent.width
spacing: 0
@ -49,6 +48,7 @@ Rectangle {
radius: 4
clip: true
color: outgoing ? root.chatBackgroundSelf : root.chatBackgroundThem
Layout.preferredHeight: itemHeight
Layout.preferredWidth: {
var max_width = item.width / 6 * 4;
@ -82,7 +82,7 @@ Rectangle {
Text {
visible: !outgoing && isHead
font.pointSize: 12
font.pointSize: 12 * ctx.scaleFactor
color: "lightblue"
text: name
}
@ -93,7 +93,7 @@ Rectangle {
}
Text {
font.pointSize: 12
font.pointSize: 12 * ctx.scaleFactor
color: "#98ac90"
text: datestr + " " + hourstr
Layout.rightMargin: 0
@ -106,20 +106,8 @@ Rectangle {
text: message
wrapMode: Text.WordWrap
width: parent.width
font.pointSize: 14
font.pointSize: 14 * ctx.scaleFactor
Layout.preferredWidth: parent.width
// Dynamically set the height of `textRectangle`.
// Unfortunately this uses a timer, so that the text component has
// time to correctly wrap itself before starting to calculate the height.
Timer {
id: timer
running: false
repeat: false
interval: 50
onTriggered: itemHeight = textColumn.implicitHeight + 12;
}
Component.onCompleted: timer.start();
}
}
}
@ -131,4 +119,14 @@ Rectangle {
}
}
}
onFetchHistory: {
// Prepend new items to the model by calling `getPage()`.
// temp. disable visibility to 'break' the touch gesture,
// if we dont the list scrolling bugs out by "jumping"
chatListView.visible = false;
var count_results = chatModel.getPage();
chatListView.positionViewAtIndex(count_results, ListView.Visible)
chatListView.visible = true;
}
}

@ -0,0 +1,28 @@
import QtQuick 2.0
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.0
ListView {
id: root
model: chatModel
signal scrollToBottom()
boundsBehavior: Flickable.StopAtBounds
property var chatScroll: chatScroll
property bool scrollable: root.childrenRect.height > parent.height
property bool atBottom: (chatScroll.position + chatScroll.size) == 1
property bool atTop: chatScroll.position <= 0.01
property bool mayAutoScroll: atBottom && scrollable
onCountChanged: { // scroll to bottom
if(chatListView.mayAutoScroll)
scrollToBottom();
}
ScrollBar.vertical: ScrollBar {
id: chatScroll
visible: false
}
}

@ -0,0 +1,102 @@
import QtQuick 2.0
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.0
import "." as Components
Rectangle {
id: chatRoot
visible: true
color: "grey"
property var chatList
signal scrollToBottom()
signal fetchHistory()
onScrollToBottom: scrollBottomTimer.start()
Components.ChatScrollToBottomButton {
z: parent.z + 1
visible: !chatList.atBottom
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.rightMargin: 60
anchors.bottomMargin: 60
onClicked: scrollBottomTimer.start();
}
Item {
visible: chatList.atTop && !chatModel.exhausted
onVisibleChanged: {
if(!chatModel.exhausted && chatList.atTop && visible && chatListView.count >= chatModel.limit)
fetchHistory();
}
}
ColumnLayout {
// debugBar
z: parent.z + 1
visible: ctx.isDebug
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: 60
anchors.topMargin: 60
property int pointSize: 16
Text {
color: "lime"
text: "Messages: " + chatList.count + " (limit: " + chatModel.limit + " offset: " + chatModel.offset + " exhausted: " + chatModel.exhausted + ")"
font.pointSize: parent.pointSize
}
Text {
color: "lime"
text: "mayAutoScroll: " + chatList.mayAutoScroll
font.pointSize: parent.pointSize
}
Text {
color: "lime"
text: "atBottom: " + chatList.atBottom
font.pointSize: parent.pointSize
}
Text {
color: "lime"
text: "atTop: " + chatList.atTop
font.pointSize: parent.pointSize
}
Text {
color: "lime"
text: "rootHeight: " + chatRoot.height
font.pointSize: parent.pointSize
}
Text {
color: "lime"
text: "chatList.cRect.height: " + chatList.childrenRect.height
font.pointSize: parent.pointSize
}
Text {
color: "lime"
text: "scrollPosition: " + chatList.chatScroll.position.toFixed(4)
font.pointSize: parent.pointSize
}
Text {
color: "lime"
text: "scaling: " + ctx.scaleFactor
font.pointSize: parent.pointSize
}
}
Timer {
id: scrollBottomTimer
interval: 10
repeat: false
running: false
onTriggered: chatList.positionViewAtEnd();
}
}

@ -0,0 +1,34 @@
import QtQuick 2.0
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.0
Item {
id: root
width: 76
height: 76
signal clicked();
Rectangle { // lies: it is a circle
anchors.fill: parent
color: "white"
opacity: 0.3
radius: width * 0.5
}
Image {
width: 48
height: 48
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
source: "qrc:///rss_reader_move_down.png"
smooth: true
}
MouseArea {
anchors.fill: parent
onClicked: {
root.clicked();
}
}
}

@ -0,0 +1,7 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/">
<file alias="components/ChatScrollToBottomButton.qml">ChatScrollToBottomButton.qml</file>
<file alias="components/ChatRoot.qml">ChatRoot.qml</file>
<file alias="components/ChatListView.qml">ChatListView.qml</file>
</qresource>
</RCC>

@ -3,12 +3,14 @@ import QtQuick.Controls 2.3
import QtQuick.Layouts 1.0
import MaemoConfig 1.0
import "components" as Components
Rectangle {
id: appWindow
visible: true
color: "black"
property string highlight: "#00a2ff"
property int itemHeight: 76
property int itemHeight: 76 * ctx.scaleFactor
signal rowClicked(string group_uid, string local_uid, string remote_uid);
@ -18,8 +20,9 @@ Rectangle {
anchors.leftMargin: 4
anchors.rightMargin: 4
anchors.fill: parent
model: chatOverviewModel
boundsBehavior: Flickable.StopAtBounds
delegate: Rectangle {
height: itemHeight
width: parent.width
@ -31,13 +34,16 @@ Rectangle {
MouseArea {
anchors.fill: parent
hoverEnabled: true
hoverEnabled: !ctx.isMaemo
cursorShape: Qt.PointingHandCursor
onEntered: {
parent.parent.color = highlight;
if(!ctx.isMaemo)
parent.parent.color = highlight;
}
onExited: {
parent.parent.color = "black";
if(!ctx.isMaemo)
parent.parent.color = "black";
}
onClicked: {
appWindow.rowClicked(group_uid, local_uid, remote_uid);
@ -76,6 +82,7 @@ Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 0
clip: true
RowLayout {
Layout.fillWidth: true
@ -85,7 +92,7 @@ Rectangle {
text: remote_name
textFormat: Text.PlainText
color: "white"
font.pointSize: 18
font.pointSize: 18 * ctx.scaleFactor
Layout.alignment: Qt.AlignTop
}
@ -97,7 +104,7 @@ Rectangle {
}
color: "grey"
textFormat: Text.PlainText
font.pointSize: 12
font.pointSize: 12 * ctx.scaleFactor
Layout.alignment: Qt.AlignTop
}
@ -112,7 +119,7 @@ Rectangle {
Layout.preferredHeight: itemHeight / 2
textFormat: Text.PlainText
color: "grey"
font.pointSize: 14
font.pointSize: 14 * ctx.scaleFactor
text: {
if(message !== "") message;
else datestr;

@ -25,6 +25,7 @@ Settings::Settings(Conversations *ctx, QWidget *parent) :
#ifdef MAEMO
setProperty("X-Maemo-StackedWindow", 1);
setProperty("X-Maemo-Orientation", 2);
#endif
QPixmap p_theme_whatsthat(":whatsthat/whatsthat.png");
QPixmap p_theme_chatty(":chatty/chatty.png");
@ -47,9 +48,30 @@ Settings::Settings(Conversations *ctx, QWidget *parent) :
}
});
// text scaling
float textScaling = config()->get(ConfigKeys::TextScaling).toFloat();
if(textScaling < 1) textScaling = 1;
auto scaling = 100*(textScaling-1);
ui->label_textScalingValue->setText("x" + QString::number(textScaling));
ui->sliderTextScaling->setValue(scaling);
connect(ui->sliderTextScaling, &QSlider::valueChanged, this, &Settings::onTextScalingValueChanged);
emit textScalingChanged();
//connect(this->ui->btnSend, &QPushButton::clicked, this, &Settings::onGatherMessage);
}
void Settings::onTextScalingValueChanged(int val) {
float scaling;
if(val == 0) scaling = 1.0;
else if(val <= 25) scaling = 1.25;
else if(val <= 50) scaling = 1.50;
else if(val <= 75) scaling = 1.75;
else if(val <= 100) scaling = 2.0;
ui->label_textScalingValue->setText("x" + QString::number(scaling));
config()->set(ConfigKeys::TextScaling, scaling);
emit textScalingChanged();
}
Conversations *Settings::getContext(){
return pSettings->m_ctx;
}

@ -31,7 +31,10 @@ public:
Ui::Settings *ui;
signals:
void lol(const QString &message);
void textScalingChanged();
private slots:
void onTextScalingValueChanged(int val);
private:
Conversations *m_ctx;

@ -24,16 +24,7 @@
<normaloff>:/assets/images/appicons/64x64.png</normaloff>:/assets/images/appicons/64x64.png</iconset>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin">
<number>32</number>
</property>
<property name="topMargin">
<number>22</number>
</property>
<property name="rightMargin">
<number>32</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="label">
<property name="text">
@ -100,6 +91,86 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_textScaling">
<property name="text">
<string>Text scaling</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_textScalingValue">
<property name="text">
<string>1x</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QSlider" name="sliderTextScaling">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>25</number>
</property>
<property name="pageStep">
<number>25</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickInterval">
<number>25</number>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">

Loading…
Cancel
Save