Skip to content
Permalink
Browse files

Make overlay FPS and clock positionable

* Implement #1068, making the FPS and clock in the overlay positionable
like our user-block

* Introduce class OverlayPositionableItem to generalize our red
draggable anchor to set the position in the overlay settings
** Has a configuration and a display mode; with and without the anchor
** Receives a pointer to the position setting

The fps position was already set up to save, the timer not.
Use storage names according to the show flags ("time" rather than
"clock").

The anchor ("handle") and text-item are added to the scene as independant
objects to allow moving the anchor to the bottom/right, at which point
the item is prevented from bleeding over the edge (so a linked movement
of individual items with custom behavior at the right/bottom edge case).

Additional Notes:

Using relative values for positioning, the default values are best-effort
values. The space between the items (the top value of the FPS) will differ
between different screen/scene sizes.

The position is saved as a rectangle, even though a point would be enough.

Handling of visibility is sub-optimal, but works. (As we use two
independent, linked items, we have to implement our own setVisible
method, and currently only call in OverlayConfig (when using config
mode).

It looks like the overlaytext used to create a text pixmap could be
replaced by QGraphicsTextItem.
  • Loading branch information...
Kissaki committed Oct 3, 2015
1 parent f732ec4 commit 8bf9b0a1ed99d192946fed96cfbb206600f701dc
@@ -31,7 +31,7 @@
#include "mumble_pch.hpp"

#include "OverlayClient.h"

#include "OverlayPositionableItem.h"
#include "OverlayEditor.h"
#include "OverlayText.h"
#include "User.h"
@@ -80,13 +80,13 @@ OverlayClient::OverlayClient(QLocalSocket *socket, QObject *p)
qgs.addItem(&ougUsers);
ougUsers.show();

qgpiFPS = new QGraphicsPixmapItem();
qgpiFPS = new OverlayPositionableItem(&g.s.os.qrfFps);
qgs.addItem(qgpiFPS);
qgpiFPS->setPos(g.s.os.qrfFps.x(), g.s.os.qrfFps.y());
qgpiFPS->show();

// Time
qgpiTime = new QGraphicsPixmapItem();
qgpiTime = new OverlayPositionableItem(&g.s.os.qrfTime);
qgs.addItem(qgpiTime);
qgpiTime->setPos(g.s.os.qrfTime.x(), g.s.os.qrfTime.y());
qgpiTime->show();
@@ -130,6 +130,7 @@ void OverlayClient::updateFPS() {
// offset to use basepoint
//TODO: settings are providing a top left anchor, so shift down by ascent
qgpiFPS->setOffset(-pm.qpBasePoint + QPoint(0, pm.iAscent));
qgpiFPS->updateRender();
} else {
qgpiFPS->setPixmap(QPixmap());
}
@@ -140,6 +141,7 @@ void OverlayClient::updateTime() {
const BasepointPixmap &pm = OverlayTextLine(QString(QLatin1String("%1")).arg(QTime::currentTime().toString()), g.s.os.qfFps).createPixmap(g.s.os.qcFps);
qgpiTime->setPixmap(pm);
qgpiTime->setOffset(-pm.qpBasePoint + QPoint(0, pm.iAscent));
qgpiTime->updateRender();
} else {
qgpiTime->setPixmap(QPixmap());
}
@@ -43,6 +43,7 @@ class ClientUser;
class Overlay;
class QLibrary;
class QLocalServer;
class OverlayPositionableItem;

class OverlayClient : public QObject {
friend class Overlay;
@@ -60,8 +61,8 @@ class OverlayClient : public QObject {
int iOffsetX, iOffsetY;
QGraphicsPixmapItem *qgpiCursor;
QGraphicsPixmapItem *qgpiLogo;
QGraphicsPixmapItem *qgpiFPS;
QGraphicsPixmapItem *qgpiTime;
OverlayPositionableItem *qgpiFPS;
OverlayPositionableItem *qgpiTime;

/// The process ID of the process this OverlayClient is connected to.
quint64 uiPid;
@@ -34,6 +34,7 @@

#include "Overlay.h"
#include "OverlayUserGroup.h"
#include "OverlayPositionableItem.h"
#include "OverlayText.h"
#include "User.h"
#include "Channel.h"
@@ -58,7 +59,7 @@ static ConfigWidget *OverlayConfigDialogNew(Settings &st) {
static ConfigRegistrar registrar(6000, OverlayConfigDialogNew);
#endif

void OverlayConfig::initDisplay() {
void OverlayConfig::initDisplayFps() {
// set up FPS preview
qgsFpsPreview.clear();
qgsFpsPreview.setBackgroundBrush(qgvFpsPreview->backgroundBrush());
@@ -72,18 +73,26 @@ void OverlayConfig::initDisplay() {
qgvFpsPreview->setScene(&qgsFpsPreview);
qgvFpsPreview->centerOn(qgpiFpsDemo);

qgpiFpsLive = new OverlayPositionableItem(&s.os.qrfFps, true);
qgpiFpsLive->setZValue(-2.0f);
refreshFpsLive();
}

void OverlayConfig::initDisplayClock() {
qgpiTimeLive = new OverlayPositionableItem(&s.os.qrfTime, true);
qgpiTimeLive->setZValue(-2.0f);
refreshTimeLive();
}

void OverlayConfig::initDisplay() {
// set up overlay preview
qgpiScreen = new QGraphicsPixmapItem();
qgpiScreen->setPixmap(qpScreen);
qgpiScreen->setOpacity(0.5f);
qgpiScreen->setZValue(-10.0f);

qgpiFpsLive = new QGraphicsPixmapItem();
qgpiFpsLive->setZValue(-2.0f);
qgpiTimeLive = new QGraphicsPixmapItem();
qgpiTimeLive->setZValue(-2.0f);
refreshFpsLive();
refreshTimeLive();
initDisplayFps();
initDisplayClock();

qgtiInstructions = new QGraphicsTextItem();
qgtiInstructions->setHtml(QString::fromLatin1("<ul><li>%1</li><li>%2</li><li>%3</li></ul>").arg(
@@ -129,24 +138,23 @@ void OverlayConfig::refreshFpsDemo() {

void OverlayConfig::refreshFpsLive() {
if (s.os.bFps) {
qgpiFpsLive->setPos(s.os.qrfFps.topLeft() * fViewScale);
qgpiFpsLive->setPixmap(bpFpsDemo.scaled(bpFpsDemo.size() * fViewScale));
qgpiFpsLive->setOffset((-bpFpsDemo.qpBasePoint + QPoint(0, bpFpsDemo.iAscent)) * fViewScale);
} else {
qgpiFpsLive->setPixmap(QPixmap());
}
qgpiFpsLive->setItemVisible(s.os.bFps);
}

void OverlayConfig::refreshTimeLive() {
if (s.os.bTime) {
bpTimeDemo = OverlayTextLine(QString::fromLatin1("%1").arg(QTime::currentTime().toString()), s.os.qfFps).createPixmap(s.os.qcFps);
qgpiTimeLive->setPixmap(bpTimeDemo);
qgpiTimeLive->setPos(s.os.qrfTime.topLeft() * fViewScale);
qgpiTimeLive->setPixmap(bpTimeDemo.scaled(bpTimeDemo.size() * fViewScale));
qgpiTimeLive->setOffset((-bpTimeDemo.qpBasePoint + QPoint(0, bpTimeDemo.iAscent)) * fViewScale);
} else {
qgpiTimeLive->setPixmap(QPixmap());
}
qgpiTimeLive->setItemVisible(s.os.bTime);
}

OverlayConfig::OverlayConfig(Settings &st) :
@@ -429,6 +437,9 @@ void OverlayConfig::resizeScene(bool force) {
qgvView->fitInView(qgs.sceneRect(), Qt::KeepAspectRatio);
oug->updateLayout();
oug->updateUsers();

qgpiFpsLive->updateRender();
qgpiTimeLive->updateRender();
}

void OverlayConfig::on_qpbAdd_clicked() {
@@ -38,13 +38,16 @@

class OverlayUserGroup;
struct OverlayAppInfo;
class OverlayPositionableItem;

class OverlayConfig : public ConfigWidget, public Ui::OverlayConfig {
private:
Q_OBJECT
Q_DISABLE_COPY(OverlayConfig)

void initDisplay();
void initDisplayFps();
void initDisplayClock();
void refreshFpsDemo();
void refreshFpsLive();
void refreshTimeLive();
@@ -56,8 +59,8 @@ class OverlayConfig : public ConfigWidget, public Ui::OverlayConfig {
BasepointPixmap bpFpsDemo;
BasepointPixmap bpTimeDemo;
QGraphicsPixmapItem *qgpiFpsDemo;
QGraphicsPixmapItem *qgpiFpsLive;
QGraphicsPixmapItem *qgpiTimeLive;
OverlayPositionableItem *qgpiFpsLive;
OverlayPositionableItem *qgpiTimeLive;
OverlayUserGroup *oug;
QGraphicsTextItem *qgtiInstructions;

@@ -0,0 +1,81 @@

#include "mumble_pch.hpp"

#include "OverlayPositionableItem.h"

OverlayPositionableItem::OverlayPositionableItem(QRectF *posPtr, const bool isPositionable)
: m_position(posPtr)
, m_isPositionEditable(isPositionable)
, m_qgeiHandle(NULL) {
}

OverlayPositionableItem::~OverlayPositionableItem() {
delete m_qgeiHandle;
m_qgeiHandle = NULL;
}

void OverlayPositionableItem::createPositioningHandle() {
m_qgeiHandle = new QGraphicsEllipseItem(QRectF(-4.0f, -4.0f, 8.0f, 8.0f));
m_qgeiHandle->setPen(QPen(Qt::darkRed, 0.0f));
m_qgeiHandle->setBrush(Qt::red);
m_qgeiHandle->setZValue(0.5f);
m_qgeiHandle->setFlag(QGraphicsItem::ItemIsMovable);
m_qgeiHandle->setFlag(QGraphicsItem::ItemIsSelectable);
scene()->addItem(m_qgeiHandle);
m_qgeiHandle->installSceneEventFilter(this);
}

bool OverlayPositionableItem::sceneEventFilter(QGraphicsItem *watched, QEvent *event ) {
switch (event->type()) {
case QEvent::GraphicsSceneMouseMove:
case QEvent::GraphicsSceneMouseRelease:
QMetaObject::invokeMethod(this, "onMove", Qt::QueuedConnection);
break;
default:
break;
}
return QGraphicsItem::sceneEventFilter(watched, event);
}

void OverlayPositionableItem::onMove() {
if (m_qgeiHandle == NULL) {
return;
}

const QRectF &sr = scene()->sceneRect();
const QPointF &p = m_qgeiHandle->pos();

m_position->setX(qBound<qreal>(0.0f, p.x() / sr.width(), 1.0f));
m_position->setY(qBound<qreal>(0.0f, p.y() / sr.height(), 1.0f));

m_qgeiHandle->setPos(m_position->x() * sr.width(), m_position->y() * sr.height());

updateRender();
}

void OverlayPositionableItem::updateRender() {
const QRectF &sr = scene()->sceneRect();
// Translate the 0..1 float position to the real scene coordinates (relative to absolute position)
QPoint absPos(iroundf(sr.width() * m_position->x() + 0.5f), iroundf(sr.height() * m_position->y() + 0.5f));

if (m_isPositionEditable) {
if (m_qgeiHandle == NULL) {
createPositioningHandle();
}
m_qgeiHandle->setPos(absPos.x(), absPos.y());
}

QRectF br = boundingRect();
// Limit the position by the elements width (to make sure it is right-/bottom-bound rather than outside of the scene
QPoint maxPos(iroundf(sr.width() - br.width() + 0.5f), iroundf(sr.height() - br.height() + 0.5f));
int basex = qBound<int>(0, absPos.x(), maxPos.x());
int basey = qBound<int>(0, absPos.y(), maxPos.y());
setPos(basex, basey);
}

void OverlayPositionableItem::setItemVisible(const bool &visible) {
setVisible(visible);
if (m_qgeiHandle != NULL) {
m_qgeiHandle->setVisible(visible);
}
}
@@ -0,0 +1,29 @@
#ifndef MUMBLE_MUMBLE_OVERLAYPOSITIONABLEITEM_H
#define MUMBLE_MUMBLE_OVERLAYPOSITIONABLEITEM_H

#if QT_VERSION >= 0x050000
# include <QtWidgets/QGraphicsItem>
#else
# include <QtGui/QGraphicsItem>
#endif

class OverlayPositionableItem : public QObject, public QGraphicsPixmapItem {
Q_OBJECT
Q_DISABLE_COPY(OverlayPositionableItem);
public:
OverlayPositionableItem(QRectF *posPtr, const bool isPositionable=false);
virtual ~OverlayPositionableItem();
void updateRender();
void setItemVisible(const bool &visible);
private:
const bool m_isPositionEditable;
/// Float value between 0 and 1 where 0,0 is top left, and 1,1 is bottom right
QRectF *m_position;
QGraphicsEllipseItem *m_qgeiHandle;
void createPositioningHandle();
bool sceneEventFilter(QGraphicsItem *, QEvent *) Q_DECL_OVERRIDE;
private slots:
void onMove();
};

#endif
@@ -150,9 +150,9 @@ OverlaySettings::OverlaySettings() {
qcFps = Qt::white;
fFps = 0.75f;
qfFps = qfUserName;
qrfFps = QRectF(10, 50, -1, 0.023438f);
qrfFps = QRectF(0.0f, 0.05, -1, 0.023438f);
bFps = false;
qrfTime = QRectF(10, 10, -1, 0.023438f);
qrfTime = QRectF(0.0f, 0.0, -1, 0.023438f);
bTime = false;

bUseWhitelist = false;
@@ -549,6 +549,7 @@ void OverlaySettings::load(QSettings* settings_ptr) {
SAVELOAD(qrfMutedDeafened, "mutedrect");
SAVELOAD(qrfAvatar, "avatarrect");
SAVELOAD(qrfFps, "fpsrect");
SAVELOAD(qrfTime, "timerect");

LOADFLAG(qaUserName, "useralign");
LOADFLAG(qaChannel, "channelalign");
@@ -856,6 +857,7 @@ void OverlaySettings::save(QSettings* settings_ptr) {
SAVELOAD(qrfMutedDeafened, "mutedrect");
SAVELOAD(qrfAvatar, "avatarrect");
SAVELOAD(qrfFps, "fpsrect");
SAVELOAD(qrfTime, "timerect");

SAVEFLAG(qaUserName, "useralign");
SAVEFLAG(qaChannel, "channelalign");
@@ -147,7 +147,8 @@ HEADERS *= BanEditor.h \
MumbleApplication.h \
ApplicationPalette.h \
ThemeInfo.h \
Themes.h
Themes.h \
OverlayPositionableItem.h

SOURCES *= BanEditor.cpp \
ACLEditor.cpp \
@@ -209,7 +210,8 @@ SOURCES *= BanEditor.cpp \
MumbleApplication.cpp \
smallft.cpp \
ThemeInfo.cpp \
Themes.cpp
Themes.cpp \
OverlayPositionableItem.cpp

DIST *= ../../icons/mumble.ico licenses.h smallft.h ../../icons/mumble.xpm murmur_pch.h mumble.plist
RESOURCES *= mumble.qrc mumble_translations.qrc mumble_flags.qrc ../../themes/MumbleTheme.qrc

0 comments on commit 8bf9b0a

Please sign in to comment.
You can’t perform that action at this time.