mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
profile: Unify desktop and mobile widgets
This breaks DPR handling, but it is a start. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
parent
413b236867
commit
e3767976a3
9 changed files with 129 additions and 316 deletions
|
|
@ -32,8 +32,6 @@ set(SUBSURFACE_PROFILE_LIB_SRCS
|
|||
if (SUBSURFACE_TARGET_EXECUTABLE MATCHES "MobileExecutable")
|
||||
set(SUBSURFACE_PROFILE_LIB_SRCS
|
||||
${SUBSURFACE_PROFILE_LIB_SRCS}
|
||||
qmlprofile.cpp
|
||||
qmlprofile.h
|
||||
)
|
||||
else ()
|
||||
set(SUBSURFACE_PROFILE_LIB_SRCS
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include "profilescene.h"
|
||||
#include "zvalues.h"
|
||||
#include "core/dive.h"
|
||||
#include "core/divelog.h"
|
||||
#include "core/errorhelper.h"
|
||||
#include "core/pref.h"
|
||||
#include "core/settings/qPrefDisplay.h"
|
||||
|
|
@ -42,17 +43,10 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
static double calcZoom(int zoomLevel)
|
||||
{
|
||||
// Base of exponential zoom function: one wheel-click will increase the zoom by 15%.
|
||||
constexpr double zoomFactor = 1.15;
|
||||
return zoomLevel == 0 ? 1.0 : pow(zoomFactor, zoomLevel);
|
||||
}
|
||||
|
||||
ProfileView::ProfileView(QQuickItem *parent) : ChartView(parent, ProfileZValue::Count),
|
||||
d(nullptr),
|
||||
dc(0),
|
||||
zoomLevel(0),
|
||||
zoomLevel(1.00),
|
||||
zoomedPosition(0.0),
|
||||
panning(false),
|
||||
empty(true),
|
||||
|
|
@ -141,7 +135,7 @@ void ProfileView::plotDive(const struct dive *dIn, int dcIn, int flags)
|
|||
|
||||
// We can't create the scene in the constructor, because we can't get the DPR property there. Oh joy!
|
||||
if (!profileScene) {
|
||||
double dpr = std::clamp(property("dpr").toReal(), 1.0, 100.0);
|
||||
double dpr = std::clamp(property("dpr").toReal(), 0.5, 100.0);
|
||||
profileScene = std::make_unique<ProfileScene>(dpr, false, false);
|
||||
}
|
||||
// If there was no previously displayed dive, turn off animations
|
||||
|
|
@ -162,14 +156,12 @@ void ProfileView::plotDive(const struct dive *dIn, int dcIn, int flags)
|
|||
DivePlannerPointsModel *model = nullptr;
|
||||
bool inPlanner = flags & RenderFlags::PlanMode;
|
||||
|
||||
double zoom = calcZoom(zoomLevel);
|
||||
|
||||
int animSpeed = flags & RenderFlags::Instant ? 0 : qPrefDisplay::animation_speed();
|
||||
|
||||
profileScene->resize(size());
|
||||
profileScene->plotDive(d, dc, animSpeed, model, inPlanner,
|
||||
flags & RenderFlags::DontRecalculatePlotInfo,
|
||||
shouldCalculateMax, zoom, zoomedPosition);
|
||||
shouldCalculateMax, zoomLevel, zoomedPosition);
|
||||
background = inPlanner ? QColor("#D7E3EF") : getColor(::BACKGROUND, false);
|
||||
profileItem->draw(size(), background, *profileScene);
|
||||
|
||||
|
|
@ -220,14 +212,17 @@ void ProfileView::anim(double fraction)
|
|||
|
||||
void ProfileView::resetZoom()
|
||||
{
|
||||
zoomLevel = 0;
|
||||
zoomLevel = 1.0;
|
||||
zoomedPosition = 0.0;
|
||||
}
|
||||
|
||||
void ProfileView::setZoom(int level)
|
||||
void ProfileView::setZoom(double level)
|
||||
{
|
||||
zoomLevel = level;
|
||||
plotDive(d, dc, RenderFlags::DontRecalculatePlotInfo);
|
||||
level = std::clamp(level, 1.0, 20.0);
|
||||
double old = std::exchange(zoomLevel, level);
|
||||
if (level != old)
|
||||
plotDive(d, dc, RenderFlags::DontRecalculatePlotInfo);
|
||||
emit zoomLevelChanged();
|
||||
}
|
||||
|
||||
void ProfileView::wheelEvent(QWheelEvent *event)
|
||||
|
|
@ -238,13 +233,13 @@ void ProfileView::wheelEvent(QWheelEvent *event)
|
|||
return; // No change in zoom level while panning.
|
||||
if (event->buttons() == Qt::LeftButton)
|
||||
return;
|
||||
if (event->angleDelta().y() > 0 && zoomLevel < 20)
|
||||
setZoom(++zoomLevel);
|
||||
else if (event->angleDelta().y() < 0 && zoomLevel > 0)
|
||||
setZoom(--zoomLevel);
|
||||
if (event->angleDelta().y() > 0)
|
||||
setZoom(zoomLevel * 1.15);
|
||||
else if (event->angleDelta().y() < 0)
|
||||
setZoom(zoomLevel / 1.15);
|
||||
else if (event->angleDelta().x() && zoomLevel > 0) {
|
||||
double oldPos = zoomedPosition;
|
||||
zoomedPosition = profileScene->calcZoomPosition(calcZoom(zoomLevel),
|
||||
zoomedPosition = profileScene->calcZoomPosition(zoomLevel,
|
||||
oldPos,
|
||||
oldPos - event->angleDelta().x());
|
||||
if (oldPos != zoomedPosition)
|
||||
|
|
@ -255,8 +250,8 @@ void ProfileView::wheelEvent(QWheelEvent *event)
|
|||
void ProfileView::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
panning = true;
|
||||
panningOriginalMousePosition = mapToScene(event->pos()).x();
|
||||
panningOriginalProfilePosition = zoomedPosition;
|
||||
QPointF pos = mapToScene(event->pos());
|
||||
panStart(pos.x(), pos.y());
|
||||
setCursor(Qt::ClosedHandCursor);
|
||||
event->accept();
|
||||
}
|
||||
|
|
@ -276,14 +271,8 @@ void ProfileView::mouseReleaseEvent(QMouseEvent *)
|
|||
void ProfileView::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
QPointF pos = mapToScene(event->pos());
|
||||
if (panning) {
|
||||
double oldPos = zoomedPosition;
|
||||
zoomedPosition = profileScene->calcZoomPosition(calcZoom(zoomLevel),
|
||||
panningOriginalProfilePosition,
|
||||
panningOriginalMousePosition - pos.x());
|
||||
if (oldPos != zoomedPosition)
|
||||
plotDive(d, dc, RenderFlags::Instant | RenderFlags::DontRecalculatePlotInfo); // TODO: animations don't work when scrolling
|
||||
}
|
||||
if (panning)
|
||||
pan(pos.x(), pos.y());
|
||||
|
||||
//toolTipItem->refresh(d, mapToScene(mapFromGlobal(QCursor::pos())), currentState == PLAN);
|
||||
|
||||
|
|
@ -296,3 +285,65 @@ void ProfileView::mouseMoveEvent(QMouseEvent *event)
|
|||
//mouseFollowerVertical->setLine(x, rect.top(), x, rect.bottom());
|
||||
//}
|
||||
}
|
||||
|
||||
int ProfileView::getDiveId() const
|
||||
{
|
||||
return d ? d->id : -1;
|
||||
}
|
||||
|
||||
void ProfileView::setDiveId(int id)
|
||||
{
|
||||
plotDive(divelog.dives.get_by_uniq_id(id), 0);
|
||||
}
|
||||
|
||||
int ProfileView::numDC() const
|
||||
{
|
||||
return d ? d->number_of_computers() : 0;
|
||||
}
|
||||
|
||||
void ProfileView::pinchStart()
|
||||
{
|
||||
zoomLevelPinchStart = zoomLevel;
|
||||
}
|
||||
|
||||
void ProfileView::pinch(double factor)
|
||||
{
|
||||
setZoom(zoomLevelPinchStart * factor);
|
||||
}
|
||||
|
||||
void ProfileView::nextDC()
|
||||
{
|
||||
rotateDC(1);
|
||||
}
|
||||
|
||||
void ProfileView::prevDC()
|
||||
{
|
||||
rotateDC(-1);
|
||||
}
|
||||
|
||||
void ProfileView::rotateDC(int dir)
|
||||
{
|
||||
int num = numDC();
|
||||
if (num <= 1)
|
||||
return;
|
||||
dc = (dc + dir) % num;
|
||||
if (dc < 0)
|
||||
dc += num;
|
||||
replot();
|
||||
}
|
||||
|
||||
void ProfileView::panStart(double x, double y)
|
||||
{
|
||||
panningOriginalMousePosition = x;
|
||||
panningOriginalProfilePosition = zoomedPosition;
|
||||
}
|
||||
|
||||
void ProfileView::pan(double x, double y)
|
||||
{
|
||||
double oldPos = zoomedPosition;
|
||||
zoomedPosition = profileScene->calcZoomPosition(zoomLevel,
|
||||
panningOriginalProfilePosition,
|
||||
panningOriginalMousePosition - x);
|
||||
if (oldPos != zoomedPosition)
|
||||
plotDive(d, dc, RenderFlags::Instant | RenderFlags::DontRecalculatePlotInfo); // TODO: animations don't work when scrolling
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@ class ProfileScene;
|
|||
|
||||
class ProfileView : public ChartView {
|
||||
Q_OBJECT
|
||||
|
||||
// Communication with the mobile interface is via properties. I hate it.
|
||||
Q_PROPERTY(int diveId READ getDiveId WRITE setDiveId)
|
||||
Q_PROPERTY(int numDC READ numDC NOTIFY numDCChanged)
|
||||
Q_PROPERTY(double zoomLevel MEMBER zoomLevel NOTIFY zoomLevelChanged)
|
||||
public:
|
||||
ProfileView();
|
||||
ProfileView(QQuickItem *parent);
|
||||
|
|
@ -28,10 +33,21 @@ public:
|
|||
void clear();
|
||||
void resetZoom();
|
||||
void anim(double fraction);
|
||||
|
||||
// For mobile
|
||||
Q_INVOKABLE void pinchStart();
|
||||
Q_INVOKABLE void pinch(double factor);
|
||||
Q_INVOKABLE void nextDC();
|
||||
Q_INVOKABLE void prevDC();
|
||||
Q_INVOKABLE void panStart(double x, double y);
|
||||
Q_INVOKABLE void pan(double x, double y);
|
||||
signals:
|
||||
void numDCChanged();
|
||||
void zoomLevelChanged();
|
||||
private:
|
||||
const struct dive *d;
|
||||
int dc;
|
||||
int zoomLevel;
|
||||
double zoomLevel, zoomLevelPinchStart;
|
||||
double zoomedPosition; // Position when zoomed: 0.0 = beginning, 1.0 = end.
|
||||
bool panning; // Currently panning.
|
||||
double panningOriginalMousePosition;
|
||||
|
|
@ -46,12 +62,18 @@ private:
|
|||
void plotAreaChanged(const QSizeF &size) override;
|
||||
void resetPointers() override;
|
||||
void replot();
|
||||
void setZoom(int level);
|
||||
void setZoom(double level);
|
||||
|
||||
void wheelEvent(QWheelEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
|
||||
// For mobile
|
||||
int getDiveId() const;
|
||||
void setDiveId(int id);
|
||||
int numDC() const;
|
||||
void rotateDC(int dir);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,163 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.
|
||||
#include "qmlprofile.h"
|
||||
#include "profilescene.h"
|
||||
#include "mobile-widgets/qmlmanager.h"
|
||||
#include "core/divelist.h"
|
||||
#include "core/errorhelper.h"
|
||||
#include "core/subsurface-float.h"
|
||||
#include "core/metrics.h"
|
||||
#include "core/subsurface-string.h"
|
||||
#include <QTransform>
|
||||
#include <QScreen>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
QMLProfile::QMLProfile(QQuickItem *parent) :
|
||||
QQuickPaintedItem(parent),
|
||||
m_diveId(0),
|
||||
m_dc(0),
|
||||
m_devicePixelRatio(1.0),
|
||||
m_margin(0),
|
||||
m_xOffset(0.0),
|
||||
m_yOffset(0.0)
|
||||
{
|
||||
createProfileView();
|
||||
setAntialiasing(true);
|
||||
setFlags(QQuickItem::ItemClipsChildrenToShape | QQuickItem::ItemHasContents );
|
||||
connect(QMLManager::instance(), &QMLManager::sendScreenChanged, this, &QMLProfile::screenChanged);
|
||||
connect(this, &QMLProfile::scaleChanged, this, &QMLProfile::triggerUpdate);
|
||||
connect(&diveListNotifier, &DiveListNotifier::divesChanged, this, &QMLProfile::divesChanged);
|
||||
setDevicePixelRatio(QMLManager::instance()->lastDevicePixelRatio());
|
||||
}
|
||||
|
||||
QMLProfile::~QMLProfile()
|
||||
{
|
||||
}
|
||||
|
||||
void QMLProfile::createProfileView()
|
||||
{
|
||||
m_profileWidget.reset(new ProfileScene(m_devicePixelRatio * 0.8, false, false));
|
||||
}
|
||||
|
||||
// we need this so we can connect update() to the scaleChanged() signal - which the connect above cannot do
|
||||
// directly as it chokes on the default parameter for update().
|
||||
// If the scale changes we may need to change our offsets to ensure that we still only show a subset of
|
||||
// the profile and not empty space around it, which the paint() method below will take care of, which will
|
||||
// eventually get called after we call update()
|
||||
void QMLProfile::triggerUpdate()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
void QMLProfile::paint(QPainter *painter)
|
||||
{
|
||||
QElapsedTimer timer;
|
||||
if (verbose)
|
||||
timer.start();
|
||||
|
||||
// let's look at the intended size of the content and scale our scene accordingly
|
||||
// for some odd reason the painter transformation is set up to scale by the dpr - which results
|
||||
// in applying that dpr scaling twice. So we hard-code it here to be the identity matrix
|
||||
QRect painterRect = painter->viewport();
|
||||
painter->resetTransform();
|
||||
if (m_diveId < 0)
|
||||
return;
|
||||
struct dive *d = divelog.dives.get_by_uniq_id(m_diveId);
|
||||
if (!d)
|
||||
return;
|
||||
m_profileWidget->draw(painter, painterRect, d, m_dc, nullptr, false);
|
||||
}
|
||||
|
||||
void QMLProfile::setMargin(int margin)
|
||||
{
|
||||
m_margin = margin;
|
||||
}
|
||||
|
||||
int QMLProfile::diveId() const
|
||||
{
|
||||
return m_diveId;
|
||||
}
|
||||
|
||||
void QMLProfile::setDiveId(int diveId)
|
||||
{
|
||||
m_diveId = diveId;
|
||||
emit numDCChanged();
|
||||
}
|
||||
|
||||
qreal QMLProfile::devicePixelRatio() const
|
||||
{
|
||||
return m_devicePixelRatio;
|
||||
}
|
||||
|
||||
void QMLProfile::setDevicePixelRatio(qreal dpr)
|
||||
{
|
||||
if (dpr != m_devicePixelRatio) {
|
||||
m_devicePixelRatio = dpr;
|
||||
// Recreate the view to redraw the text items with the new scale.
|
||||
createProfileView();
|
||||
emit devicePixelRatioChanged();
|
||||
}
|
||||
}
|
||||
|
||||
// don't update the profile here, have the user update x and y and then manually trigger an update
|
||||
void QMLProfile::setXOffset(qreal value)
|
||||
{
|
||||
if (nearly_equal(value, m_xOffset))
|
||||
return;
|
||||
m_xOffset = value;
|
||||
emit xOffsetChanged();
|
||||
}
|
||||
|
||||
// don't update the profile here, have the user update x and y and then manually trigger an update
|
||||
void QMLProfile::setYOffset(qreal value)
|
||||
{
|
||||
if (nearly_equal(value, m_yOffset))
|
||||
return;
|
||||
m_yOffset = value;
|
||||
emit yOffsetChanged();
|
||||
}
|
||||
|
||||
void QMLProfile::screenChanged(QScreen *screen)
|
||||
{
|
||||
setDevicePixelRatio(screen->devicePixelRatio());
|
||||
}
|
||||
|
||||
void QMLProfile::divesChanged(const QVector<dive *> &dives, DiveField)
|
||||
{
|
||||
for (struct dive *d: dives) {
|
||||
if (d->id == m_diveId) {
|
||||
report_info("dive #%d changed, trigger profile update", d->number);
|
||||
triggerUpdate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QMLProfile::nextDC()
|
||||
{
|
||||
rotateDC(1);
|
||||
}
|
||||
|
||||
void QMLProfile::prevDC()
|
||||
{
|
||||
rotateDC(-1);
|
||||
}
|
||||
|
||||
void QMLProfile::rotateDC(int dir)
|
||||
{
|
||||
struct dive *d = divelog.dives.get_by_uniq_id(m_diveId);
|
||||
if (!d)
|
||||
return;
|
||||
int numDC = d->number_of_computers();
|
||||
if (numDC == 1)
|
||||
return;
|
||||
m_dc = (m_dc + dir) % numDC;
|
||||
if (m_dc < 0)
|
||||
m_dc += numDC;
|
||||
triggerUpdate();
|
||||
}
|
||||
|
||||
int QMLProfile::numDC() const
|
||||
{
|
||||
struct dive *d = divelog.dives.get_by_uniq_id(m_diveId);
|
||||
return d ? d->number_of_computers() : 0;
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#ifndef QMLPROFILE_H
|
||||
#define QMLPROFILE_H
|
||||
|
||||
#include "core/subsurface-qt/divelistnotifier.h"
|
||||
#include <QQuickPaintedItem>
|
||||
#include <memory>
|
||||
|
||||
class ProfileScene;
|
||||
|
||||
class QMLProfile : public QQuickPaintedItem
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int diveId MEMBER m_diveId WRITE setDiveId)
|
||||
Q_PROPERTY(int numDC READ numDC NOTIFY numDCChanged)
|
||||
Q_PROPERTY(qreal devicePixelRatio READ devicePixelRatio WRITE setDevicePixelRatio NOTIFY devicePixelRatioChanged)
|
||||
Q_PROPERTY(qreal xOffset MEMBER m_xOffset WRITE setXOffset NOTIFY xOffsetChanged)
|
||||
Q_PROPERTY(qreal yOffset MEMBER m_yOffset WRITE setYOffset NOTIFY yOffsetChanged)
|
||||
|
||||
public:
|
||||
explicit QMLProfile(QQuickItem *parent = 0);
|
||||
~QMLProfile();
|
||||
|
||||
void paint(QPainter *painter);
|
||||
|
||||
int diveId() const;
|
||||
void setDiveId(int diveId);
|
||||
qreal devicePixelRatio() const;
|
||||
void setDevicePixelRatio(qreal dpr);
|
||||
void setXOffset(qreal value);
|
||||
void setYOffset(qreal value);
|
||||
Q_INVOKABLE void nextDC();
|
||||
Q_INVOKABLE void prevDC();
|
||||
|
||||
public slots:
|
||||
void setMargin(int margin);
|
||||
void screenChanged(QScreen *screen);
|
||||
void triggerUpdate();
|
||||
|
||||
private:
|
||||
int m_diveId;
|
||||
int m_dc;
|
||||
qreal m_devicePixelRatio;
|
||||
int m_margin;
|
||||
qreal m_xOffset, m_yOffset;
|
||||
std::unique_ptr<ProfileScene> m_profileWidget;
|
||||
void createProfileView();
|
||||
void rotateDC(int dir);
|
||||
int numDC() const;
|
||||
|
||||
private slots:
|
||||
void divesChanged(const QVector<dive *> &dives, DiveField);
|
||||
|
||||
signals:
|
||||
void rightAlignedChanged();
|
||||
void devicePixelRatioChanged();
|
||||
void xOffsetChanged();
|
||||
void yOffsetChanged();
|
||||
void numDCChanged();
|
||||
};
|
||||
|
||||
#endif // QMLPROFILE_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue