This marks the beginnings of QML widgets in Quassel. The first widget to be ported is ChatView;
this commit does the following:
* Extend the build system for supporting QML
* Add a new qmlui module
* Add a (very) rudimentary ChatView component that connects to
* the new QmlMessageModel, which is intended for proxying between the client and the QuickUI
For now, -DWITH_QML=ON is required for cmake to build Quassel with QML. No, it's not worth trying out
at the moment.
# -DWITH_LIBINDICATE=OFF : Disable libindicate support (Ayatana notifications)
# -DWITH_KDE=ON : Enable KDE4 support
# -DWITH_CRYPT=OFF : Disable encryption support
+# -DWITH_QML=ON : Enable the QML-based UI (requires Qt 4.7+)
# -DWITH_OXYGEN=(ON|OFF) : Whether to install Oxygen icons (default: yes, unless KDE > 4.3.0 is present and enabled)
#
# -DEMBED_DATA=ON : Embed all data files in icons the binary, rather than installing them separately
option(WITH_LIBINDICATE "Enable Ayatana notification support" ON)
option(WITH_KDE "Enable KDE4 integration" OFF)
option(WITH_CRYPT "Enable encryption support if present on system" ON)
+option(WITH_QML "Enable the QML-based UI" OFF)
# We use icon paths from KDE 4.3.x, which are partially invalid on older and possibly
# even on newer KDE versions. Do not disable this unless you are sure that your Quassel will
# GUI stuff needs some new features
if(BUILD_GUI)
- set(QT_MIN_VERSION "4.6.0")
+ if(WITH_QML)
+ set(QT_MIN_VERSION "4.7.4")
+ else(WITH_QML)
+ set(QT_MIN_VERSION "4.6.0")
+ endif(WITH_QML)
else(BUILD_GUI)
set(QT_MIN_VERSION "4.4.0")
endif(BUILD_GUI)
message(STATUS "Not enabling WebKit support")
endif(WITH_WEBKIT)
+ # Setup QML support
+ if(WITH_QML)
+ if(QT_QTDECLARATIVE_FOUND)
+ message(STATUS "Found QtDeclarative, enabling the QML-based UI")
+ add_definitions(-DHAVE_QML)
+ set(CLIENT_QT4_VARS ${CLIENT_QT4_VARS} DECLARATIVE)
+ set(CLIENT_COMPILE_FLAGS "${CLIENT_COMPILE_FLAGS} -DQT_DECLARATIVE_LIB")
+ set(HAVE_QML true)
+ else(QT_QTDECLARATIVE_FOUND)
+ message(STATUS "QtDeclarative not found, disabling the QML-based UI")
+ endif(QT_QTDECLARATIVE_FOUND)
+ else(WITH_QML)
+ message(STATUS "Not enabling the QML-based UI")
+ endif(WITH_QML)
+
# Setup KDE4 support
if(WITH_KDE)
find_package(KDE4)
add_subdirectory(icons)
add_subdirectory(pics)
add_subdirectory(po)
+add_subdirectory(qml)
add_subdirectory(src)
--- /dev/null
+if(HAVE_QML)
+ set(CLIENT_RCS ${CLIENT_RCS} ../qml/qml.qrc PARENT_SCOPE)
+endif(HAVE_QML)
--- /dev/null
+import QtQuick 1.1
+// import Qt.components 1.0
+
+
+ListView {
+ id: flickable
+ anchors.fill: parent
+
+ Component {
+ id: msgDelegate
+ Item {
+ Row {
+ id: chatline
+ Text { text: timestamp; wrapMode: Text.NoWrap; width: 100 }
+ Text { text: sender; wrapMode: Text.NoWrap; width: 100 }
+ Text { text: contents; wrapMode: Text.Wrap; width: flickable.width-200}
+ }
+ height: chatline.height
+ ListView.onAdd: positionViewAtEnd()
+ }
+ }
+
+ model: msgModel
+
+ delegate: msgDelegate
+
+ Rectangle {
+ id: scrollbar
+ anchors.right: flickable.right
+ y: flickable.visibleArea.yPosition * flickable.height
+ width: 10
+ height: flickable.visibleArea.heightRatio * flickable.height
+ color: "black"
+ }
+}
--- /dev/null
+<!DOCTYPE RCC>
+<RCC version="1.0">
+ <qresource prefix="/qml" >
+ <file>ChatView.qml</file>
+ </qresource>
+</RCC>
if(BUILD_GUI)
add_subdirectory(client)
add_subdirectory(uisupport)
+ if(WITH_QML)
+ add_subdirectory(qmlui)
+ set(CLIENT_LIBS ${CLIENT_LIBS} mod_qmlui)
+ endif(WITH_QML)
add_subdirectory(qtui)
include_directories(BEFORE client)
include_directories(BEFORE uisupport)
--- /dev/null
+# Builds the qtui module
+
+set(QT_DONT_USE_QTGUI 0)
+set(QT_USE_QTDECLARATIVE 1)
+include(${QT_USE_FILE})
+
+
+set(SOURCES
+ qmlchatview.cpp
+ qmlmessagemodel.cpp
+ qmlmessagemodelitem.cpp
+)
+
+set(MOC_HDRS
+ qmlchatview.h
+ qmlmessagemodel.h
+)
+
+set(HEADERS
+ qmlmessagemodelitem.h
+)
+
+set(FORMS
+
+)
+
+foreach(FORM ${FORMS})
+ set(FORMPATH ${FORMPATH} ui/${FORM})
+endforeach(FORM ${FORMS})
+
+qt4_wrap_cpp(MOC ${MOC_HDRS} ${SPHDR})
+qt4_wrap_ui(UI ${FORMPATH} ${SPFRM})
+
+include_directories(${CMAKE_SOURCE_DIR}/src/common
+ ${CMAKE_SOURCE_DIR}/src/client
+ ${CMAKE_SOURCE_DIR}/src/uisupport
+ ${CMAKE_CURRENT_BINARY_DIR})
+
+add_library(mod_qmlui STATIC ${SOURCES} ${SPSRC} ${MOC} ${DBUS} ${UI} ${HEADERS})
+add_dependencies(mod_qmlui mod_common mod_client mod_uisupport)
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005-2010 by the Quassel Project *
+ * devel@quassel-irc.org *
+ * *
+ * 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 2 of the License, or *
+ * (at your option) version 3. *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QAbstractItemModel>
+#include <QDeclarativeContext>
+#include <QKeyEvent>
+#include <QScrollBar>
+
+//#include "bufferwidget.h"
+#include "client.h"
+#include "clientignorelistmanager.h"
+#include "messagefilter.h"
+#include "networkmodel.h"
+#include "qmlchatview.h"
+
+QmlChatView::QmlChatView(BufferId bufferId, QWidget *parent)
+ : QDeclarativeView(parent),
+ AbstractChatView()
+{
+ QList<BufferId> filterList;
+ filterList.append(bufferId);
+ MessageFilter *filter = new MessageFilter(Client::messageModel(), filterList, this);
+ init(filter);
+}
+
+QmlChatView::QmlChatView(MessageFilter *filter, QWidget *parent)
+ : QDeclarativeView(parent),
+ AbstractChatView()
+{
+ init(filter);
+}
+
+void QmlChatView::init(MessageFilter *filter) {
+ _bufferContainer = 0;
+ _invalidateFilter = false;
+
+ setResizeMode(SizeRootObjectToView);
+
+ QDeclarativeContext *ctxt = rootContext();
+ ctxt->setContextProperty("msgModel", filter);
+
+ setSource(QUrl("qrc:/qml/ChatView.qml"));
+
+ //connect(Client::networkModel(), SIGNAL(markerLineSet(BufferId,MsgId)), SLOT(markerLineSet(BufferId,MsgId)));
+
+ // only connect if client is synched with a core
+ if(Client::isConnected())
+ connect(Client::ignoreListManager(), SIGNAL(ignoreListChanged()), SLOT(invalidateFilter()));
+}
+
+bool QmlChatView::event(QEvent *event) {
+ if(event->type() == QEvent::KeyPress) {
+ QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
+ switch(keyEvent->key()) {
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ case Qt::Key_PageUp:
+ case Qt::Key_PageDown:
+ if(!verticalScrollBar()->isVisible()) {
+// scene()->requestBacklog();
+ return true;
+ }
+ default:
+ break;
+ }
+ }
+
+ if(event->type() == QEvent::Wheel) {
+ if(!verticalScrollBar()->isVisible()) {
+// scene()->requestBacklog();
+ return true;
+ }
+ }
+
+ if(event->type() == QEvent::Show) {
+ if(_invalidateFilter)
+ invalidateFilter();
+ }
+
+ return QGraphicsView::event(event);
+}
+
+MsgId QmlChatView::lastMsgId() const {
+/*
+ if(!scene())
+ return MsgId();
+
+ QAbstractItemModel *model = scene()->model();
+ if(!model || model->rowCount() == 0)
+ return MsgId();
+
+ return model->index(model->rowCount() - 1, 0).data(MessageModel::MsgIdRole).value<MsgId>();
+*/
+ return 0;
+}
+/*
+void QmlChatView::setMarkerLineVisible(bool visible) {
+ scene()->setMarkerLineVisible(visible);
+}
+
+void QmlChatView::setMarkerLine(MsgId msgId) {
+ if(!scene()->isSingleBufferScene())
+ return;
+
+ BufferId bufId = scene()->singleBufferId();
+ Client::setMarkerLine(bufId, msgId);
+}
+
+void QmlChatView::markerLineSet(BufferId buffer, MsgId msgId) {
+ if(!scene()->isSingleBufferScene() || scene()->singleBufferId() != buffer)
+ return;
+
+ scene()->setMarkerLine(msgId);
+ scene()->setMarkerLineVisible(true);
+}
+
+void QmlChatView::jumpToMarkerLine(bool requestBacklog) {
+ scene()->jumpToMarkerLine(requestBacklog);
+}
+*/
+void QmlChatView::addActionsToMenu(QMenu *menu, const QPointF &pos) {
+/*
+ // zoom actions
+ BufferWidget *bw = qobject_cast<BufferWidget *>(bufferContainer());
+ if(bw) {
+ bw->addActionsToMenu(menu, pos);
+ menu->addSeparator();
+ }
+*/
+}
+
+void QmlChatView::invalidateFilter() {
+ // if this is the currently selected QmlChatView
+ // invalidate immediately
+ if(isVisible()) {
+ //_scene->filter()->invalidateFilter();
+ _invalidateFilter = false;
+ }
+ // otherwise invalidate whenever the view is shown
+ else {
+ _invalidateFilter = true;
+ }
+}
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005-2010 by the Quassel Project *
+ * devel@quassel-irc.org *
+ * *
+ * 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 2 of the License, or *
+ * (at your option) version 3. *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef QMLCHATVIEW_H_
+#define QMLCHATVIEW_H_
+
+#include <QDeclarativeView>
+
+#include "abstractbuffercontainer.h"
+
+class AbstractBufferContainer;
+class AbstractUiMsg;
+class Buffer;
+class MessageFilter;
+class QMenu;
+
+class QmlChatView : public QDeclarativeView, public AbstractChatView {
+ Q_OBJECT
+
+public:
+ QmlChatView(MessageFilter *, QWidget *parent = 0);
+ QmlChatView(BufferId bufferId, QWidget *parent = 0);
+
+ virtual MsgId lastMsgId() const;
+// virtual MsgId lastVisibleMsgId() const;
+ inline AbstractBufferContainer *bufferContainer() const { return _bufferContainer; }
+ inline void setBufferContainer(AbstractBufferContainer *c) { _bufferContainer = c; }
+
+ virtual void addActionsToMenu(QMenu *, const QPointF &pos);
+
+public slots:
+ inline virtual void clear() {}
+
+// void setMarkerLineVisible(bool visible = true);
+// void setMarkerLine(MsgId msgId);
+// void jumpToMarkerLine(bool requestBacklog);
+
+protected:
+ virtual bool event(QEvent *event);
+// virtual void resizeEvent(QResizeEvent *event);
+// virtual void scrollContentsBy(int dx, int dy);
+
+protected slots:
+// virtual void verticalScrollbarChanged(int);
+
+private slots:
+// void lastLineChanged(QGraphicsItem *chatLine, qreal offset);
+// void adjustSceneRect();
+// void checkChatLineCaches();
+// void mouseMoveWhileSelecting(const QPointF &scenePos);
+// void scrollTimerTimeout();
+ void invalidateFilter();
+// void markerLineSet(BufferId buffer, MsgId msg);
+
+private:
+ void init(MessageFilter *filter);
+
+ AbstractBufferContainer *_bufferContainer;
+ bool _invalidateFilter;
+};
+
+
+#endif
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005-2011 by the Quassel Project *
+ * devel@quassel-irc.org *
+ * *
+ * 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 2 of the License, or *
+ * (at your option) version 3. *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "qmlmessagemodel.h"
+
+QmlMessageModel::QmlMessageModel(QObject *parent)
+ : MessageModel(parent)
+{
+ QHash<int, QByteArray> roles;
+ roles[TimestampRole] = "timestamp";
+ roles[SenderRole] = "sender";
+ roles[ContentsRole] = "contents";
+ setRoleNames(roles);
+
+}
+
+void QmlMessageModel::insertMessages__(int pos, const QList<Message> &messages) {
+ for(int i = 0; i < messages.count(); i++) {
+ _messageList.insert(pos, QmlMessageModelItem(messages[i]));
+ pos++;
+ }
+}
+
+Message QmlMessageModel::takeMessageAt(int i) {
+ Message msg = _messageList[i].message();
+ _messageList.removeAt(i);
+ return msg;
+}
+
+QVariant QmlMessageModel::data(const QModelIndex &index, int role) const {
+ int row = index.row();
+ if(row < 0 || row >= messageCount())
+ return QVariant();
+
+ return messageItemAt(row)->data(index.column(), role);
+}
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005-2011 by the Quassel Project *
+ * devel@quassel-irc.org *
+ * *
+ * 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 2 of the License, or *
+ * (at your option) version 3. *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef QMLMESSAGEMODEL_H_
+#define QMLMESSAGEMODEL_H_
+
+#include <QSortFilterProxyModel>
+
+#include "messagemodel.h"
+#include "qmlmessagemodelitem.h"
+
+class QmlMessageModel : public MessageModel {
+ Q_OBJECT
+
+public:
+ enum QmlMessageModelRole {
+ TimestampRole = MessageModel::UserRole,
+ SenderRole,
+ ContentsRole,
+ UserRole
+ };
+
+ QmlMessageModel(QObject *parent);
+ virtual QVariant data(const QModelIndex &index, int role) const;
+
+ virtual inline const MessageModelItem *messageItemAt(int i) const { return &_messageList[i]; }
+protected:
+
+ virtual inline int messageCount() const { return _messageList.count(); }
+ virtual inline bool messagesIsEmpty() const { return _messageList.isEmpty(); }
+ virtual inline MessageModelItem *messageItemAt(int i) { return &_messageList[i]; }
+ virtual inline const MessageModelItem *firstMessageItem() const { return &_messageList.first(); }
+ virtual inline MessageModelItem *firstMessageItem() { return &_messageList.first(); }
+ virtual inline const MessageModelItem *lastMessageItem() const { return &_messageList.last(); }
+ virtual inline MessageModelItem *lastMessageItem() { return &_messageList.last(); }
+ virtual inline void insertMessage__(int pos, const Message &msg) { _messageList.insert(pos, QmlMessageModelItem(msg)); }
+ virtual void insertMessages__(int pos, const QList<Message> &);
+ virtual inline void removeMessageAt(int i) { _messageList.removeAt(i); }
+ virtual inline void removeAllMessages() { _messageList.clear(); }
+ virtual Message takeMessageAt(int i);
+
+
+private:
+ QList<QmlMessageModelItem> _messageList;
+};
+
+#endif
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005-2010 by the Quassel Project *
+ * devel@quassel-irc.org *
+ * *
+ * 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 2 of the License, or *
+ * (at your option) version 3. *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "qmlmessagemodel.h"
+#include "qmlmessagemodelitem.h"
+
+QmlMessageModelItem::QmlMessageModelItem(const Message &msg)
+ : MessageModelItem(),
+ _styledMsg(msg)
+{
+ if(!msg.sender().contains('!'))
+ _styledMsg.setFlags(msg.flags() |= Message::ServerMsg);
+}
+
+bool QmlMessageModelItem::setData(int column, const QVariant &value, int role) {
+ switch(role) {
+ case MessageModel::FlagsRole:
+ _styledMsg.setFlags((Message::Flags)value.toUInt());
+ return true;
+ default:
+ return MessageModelItem::setData(column, value, role);
+ }
+}
+
+QVariant QmlMessageModelItem::data(int column, int role) const {
+ QVariant variant;
+ switch(role) {
+ case QmlMessageModel::TimestampRole:
+ variant = _styledMsg.timestamp();
+ break;
+ case QmlMessageModel::SenderRole:
+ variant = _styledMsg.sender();
+ break;
+ case QmlMessageModel::ContentsRole:
+ variant = _styledMsg.contents();
+ break;
+ default:
+ break;
+ }
+ if(!variant.isValid())
+ return MessageModelItem::data(column, role);
+ return variant;
+}
+
+QVariant QmlMessageModelItem::timestampData(int role) const {
+ switch(role) {
+ case MessageModel::DisplayRole:
+ return _styledMsg.timestamp();
+ }
+ return QVariant();
+}
+
+QVariant QmlMessageModelItem::senderData(int role) const {
+ switch(role) {
+ case MessageModel::DisplayRole:
+ return _styledMsg.sender();
+ }
+ return QVariant();
+}
+
+QVariant QmlMessageModelItem::contentsData(int role) const {
+ switch(role) {
+ case MessageModel::DisplayRole:
+ return _styledMsg.contents();
+ }
+ return QVariant();
+}
+
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005-2011 by the Quassel Project *
+ * devel@quassel-irc.org *
+ * *
+ * 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 2 of the License, or *
+ * (at your option) version 3. *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef QMLMESSAGEMODELITEM_H_
+#define QMLMESSAGEMODELITEM_H_
+
+#include "messagemodel.h"
+
+class QmlMessageModelItem : public MessageModelItem {
+public:
+ QmlMessageModelItem(const Message &msg);
+
+ virtual QVariant data(int column, int role) const;
+ virtual bool setData(int column, const QVariant &value, int role);
+
+ virtual inline const Message &message() const { return _styledMsg; }
+ virtual inline const QDateTime ×tamp() const { return _styledMsg.timestamp(); }
+ virtual inline const MsgId &msgId() const { return _styledMsg.msgId(); }
+ virtual inline const BufferId &bufferId() const { return _styledMsg.bufferId(); }
+ virtual inline void setBufferId(BufferId bufferId) { _styledMsg.setBufferId(bufferId); }
+ virtual inline Message::Type msgType() const { return _styledMsg.type(); }
+ virtual inline Message::Flags msgFlags() const { return _styledMsg.flags(); }
+
+private:
+ QVariant timestampData(int role) const;
+ QVariant senderData(int role) const;
+ QVariant contentsData(int role) const;
+
+ Message _styledMsg;
+};
+
+#endif
set(QT_USE_QTWEBKIT 1)
set(QT_USE_XMLPATTERNS 1)
endif(HAVE_WEBKIT)
+if(HAVE_QML)
+ set(QT_USE_QTDECLARATIVE 1)
+endif(HAVE_QML)
include(${QT_USE_FILE})
set(SOURCES
include_directories(${CMAKE_SOURCE_DIR}/src/common
${CMAKE_SOURCE_DIR}/src/client
+ ${CMAKE_SOURCE_DIR}/src/qmlui
${CMAKE_SOURCE_DIR}/src/qtui
${CMAKE_SOURCE_DIR}/src/qtui/settingspages
${CMAKE_SOURCE_DIR}/src/uisupport
${CMAKE_CURRENT_BINARY_DIR})
add_library(mod_qtui STATIC ${SOURCES} ${SPSRC} ${MOC} ${DBUS} ${UI} ${HEADERS})
-add_dependencies(mod_qtui mod_common mod_client mod_uisupport)
+add_dependencies(mod_qtui mod_common mod_client mod_qmlui mod_uisupport)