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
|
@ -171,7 +171,6 @@ SOURCES += subsurface-mobile-main.cpp \
|
|||
qt-models/weightsysteminfomodel.cpp \
|
||||
qt-models/filterconstraintmodel.cpp \
|
||||
qt-models/filterpresetmodel.cpp \
|
||||
profile-widget/qmlprofile.cpp \
|
||||
profile-widget/divecartesianaxis.cpp \
|
||||
profile-widget/diveeventitem.cpp \
|
||||
profile-widget/divepercentageitem.cpp \
|
||||
|
@ -337,7 +336,6 @@ HEADERS += \
|
|||
qt-models/weightsysteminfomodel.h \
|
||||
qt-models/filterconstraintmodel.h \
|
||||
qt-models/filterpresetmodel.h \
|
||||
profile-widget/qmlprofile.h \
|
||||
profile-widget/divepercentageitem.h \
|
||||
profile-widget/diveprofileitem.h \
|
||||
profile-widget/profilescene.h \
|
||||
|
|
|
@ -231,13 +231,13 @@ Item {
|
|||
Layout.columnSpan: 3
|
||||
clip: true
|
||||
|
||||
QMLProfile {
|
||||
ProfileView {
|
||||
id: qmlProfile
|
||||
visible: !noDive
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
property real lastScale: 1.0 // final scale at the end of previous pinch
|
||||
diveId: detailsView.myId
|
||||
property real dpr: 0.8 // TODO: make this dynamic
|
||||
Rectangle {
|
||||
color: "transparent"
|
||||
opacity: 0.6
|
||||
|
@ -253,36 +253,20 @@ Item {
|
|||
// before realizing that this is actually a pinch/zoom. So let's reset this
|
||||
// just in case
|
||||
qmlProfile.opacity = 1.0
|
||||
if (manager.verboseEnabled)
|
||||
manager.appendTextToLog("pinch started w/ previousScale " + qmlProfile.lastScale)
|
||||
qmlProfile.pinchStart()
|
||||
}
|
||||
onPinchUpdated: {
|
||||
if (pinch.scale * qmlProfile.lastScale < 1.0)
|
||||
qmlProfile.lastScale = 1.0 / pinch.scale // this way we never shrink and the changes stay smooth
|
||||
// the underlying widget deals with the scaling, no need to send an update request
|
||||
qmlProfile.scale = pinch.scale * qmlProfile.lastScale
|
||||
if (manager.verboseEnabled)
|
||||
manager.appendTextToLog("pinch updated to scale " + qmlProfile.scale);
|
||||
}
|
||||
onPinchFinished: {
|
||||
// remember the final scale value so we can continue from there next time the user pinches
|
||||
qmlProfile.lastScale = pinch.scale * qmlProfile.lastScale
|
||||
qmlProfile.pinch(pinch.scale)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
// we want to pan the profile if we are zoomed in, but we want to immediately
|
||||
// pass the mouse events through to the ListView if we are not. That way you
|
||||
// can swipe through the dive list, even if you happen to swipe the profile
|
||||
property bool isZoomed: qmlProfile.scale - 1.0 > 0.02
|
||||
property bool isZoomed: qmlProfile.zoomLevel > 1.02
|
||||
|
||||
// this indicates that we are actually dragging
|
||||
property bool dragging: false
|
||||
// cursor/finger position as we start dragging
|
||||
property real initialX
|
||||
property real initialY
|
||||
// the offset previously used to show the profile
|
||||
property real oldXOffset
|
||||
property real oldYOffset
|
||||
|
||||
// if the profile is not scaled in, don't start panning
|
||||
// but if the profile is scaled in, then start almost immediately
|
||||
|
@ -291,16 +275,6 @@ Item {
|
|||
// pass events through to the parent and eventually into the ListView
|
||||
propagateComposedEvents: true
|
||||
|
||||
// for testing / debugging on a desktop
|
||||
scrollGestureEnabled: true
|
||||
onWheel: {
|
||||
manager.appendTextToLog("wheel " + wheel.angleDelta)
|
||||
if (wheel.angleDelta.y > 0)
|
||||
qmlProfile.scale += 0.2
|
||||
if (wheel.angleDelta.y < 0 && qmlProfile.scale > 1.1)
|
||||
qmlProfile.scale -= 0.2
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
drag.target: qmlProfile
|
||||
drag.axis: Drag.XAndYAxis
|
||||
|
@ -311,10 +285,7 @@ Item {
|
|||
}
|
||||
onPressAndHold: {
|
||||
dragging = true;
|
||||
oldXOffset = qmlProfile.xOffset
|
||||
oldYOffset = qmlProfile.yOffset
|
||||
initialX = mouse.x
|
||||
initialY = mouse.y
|
||||
qmlProfile.panStart(mouse.x, mouse.y)
|
||||
if (manager.verboseEnabled)
|
||||
manager.appendTextToLog("press and hold at mouse" + Math.round(10 * mouse.x) / 10 + " / " + Math.round(10 * mouse.y) / 10)
|
||||
// give visual feedback to the user that they now can drag
|
||||
|
@ -322,13 +293,9 @@ Item {
|
|||
}
|
||||
onPositionChanged: {
|
||||
if (dragging) {
|
||||
var x = (mouse.x - initialX) / qmlProfile.scale
|
||||
var y = (mouse.y - initialY) / qmlProfile.scale
|
||||
if (manager.verboseEnabled)
|
||||
manager.appendTextToLog("drag mouse " + Math.round(10 * mouse.x) / 10 + " / " + Math.round(10 * mouse.y) / 10 + " delta " + Math.round(x) + " / " + Math.round(y))
|
||||
qmlProfile.xOffset = oldXOffset + x
|
||||
qmlProfile.yOffset = oldYOffset + y
|
||||
qmlProfile.update()
|
||||
qmlProfile.pan(mouse.x, mouse.y)
|
||||
} else {
|
||||
mouse.accepted = false
|
||||
}
|
||||
|
@ -344,7 +311,6 @@ Item {
|
|||
onClicked: {
|
||||
// reset the position if not zoomed in
|
||||
if (!isZoomed) {
|
||||
qmlProfile.xOffset = qmlProfile.yOffset = oldXOffset = oldYOffset = 0
|
||||
mouse.accepted = false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
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
|
|
@ -13,10 +13,6 @@ ChartView::ChartView(QQuickItem *parent, size_t maxZ) : QQuickItem(parent),
|
|||
setFlag(ItemHasContents, true);
|
||||
}
|
||||
|
||||
ChartView::~ChartView()
|
||||
{
|
||||
}
|
||||
|
||||
// Define a hideable dummy QSG node that is used as a parent node to make
|
||||
// all objects of a z-level visible / invisible.
|
||||
using ZNode = HideableQSGNode<QSGNode>;
|
||||
|
@ -24,22 +20,22 @@ using ZNode = HideableQSGNode<QSGNode>;
|
|||
class RootNode : public QSGNode
|
||||
{
|
||||
public:
|
||||
RootNode(ChartView &view, QColor backgroundColor, size_t maxZ);
|
||||
RootNode(ChartView *view, QColor backgroundColor, size_t maxZ);
|
||||
~RootNode();
|
||||
ChartView &view;
|
||||
ChartView *view;
|
||||
std::unique_ptr<QSGRectangleNode> backgroundNode; // solid background
|
||||
// We entertain one node per Z-level.
|
||||
std::vector<std::unique_ptr<ZNode>> zNodes;
|
||||
};
|
||||
|
||||
RootNode::RootNode(ChartView &view, QColor backgroundColor, size_t maxZ) : view(view)
|
||||
RootNode::RootNode(ChartView *view, QColor backgroundColor, size_t maxZ) : view(view)
|
||||
{
|
||||
zNodes.resize(maxZ);
|
||||
|
||||
// Add a background rectangle with a solid color. This could
|
||||
// also be done on the widget level, but would have to be done
|
||||
// separately for desktop and mobile, so do it here.
|
||||
backgroundNode.reset(view.w()->createRectangleNode());
|
||||
backgroundNode.reset(view->w()->createRectangleNode());
|
||||
appendChildNode(backgroundNode.get());
|
||||
|
||||
for (auto &zNode: zNodes) {
|
||||
|
@ -50,7 +46,16 @@ RootNode::RootNode(ChartView &view, QColor backgroundColor, size_t maxZ) : view(
|
|||
|
||||
RootNode::~RootNode()
|
||||
{
|
||||
view.emergencyShutdown();
|
||||
if (view)
|
||||
view->emergencyShutdown();
|
||||
}
|
||||
|
||||
ChartView::~ChartView()
|
||||
{
|
||||
// Sometimes the rootNode is destructed before the view,
|
||||
// sometimes the other way around. QtQuick is a mess!
|
||||
if (rootNode)
|
||||
rootNode->view = nullptr;
|
||||
}
|
||||
|
||||
void ChartView::freeDeletedChartItems()
|
||||
|
@ -69,7 +74,7 @@ QSGNode *ChartView::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNod
|
|||
// This is just a copy of what is found in Qt's documentation.
|
||||
RootNode *n = static_cast<RootNode *>(oldNode);
|
||||
if (!n)
|
||||
n = rootNode = new RootNode(*this, backgroundColor, maxZ);
|
||||
n = rootNode = new RootNode(this, backgroundColor, maxZ);
|
||||
|
||||
// Delete all chart items that are marked for deletion.
|
||||
freeDeletedChartItems();
|
||||
|
@ -93,7 +98,7 @@ QSGNode *ChartView::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNod
|
|||
// permission to do so! If the widget is reused, we try to delete the
|
||||
// stale items, whose nodes have already been deleted by QtQuick, leading
|
||||
// to a double-free(). Instead of searching for the cause of this behavior,
|
||||
// let's just hook into the rootNodes destructor and delete the objects
|
||||
// let's just hook into the rootNode's destructor and delete the objects
|
||||
// in a controlled manner, so that QtQuick has no more access to them.
|
||||
void ChartView::emergencyShutdown()
|
||||
{
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include "qt-models/divesummarymodel.h"
|
||||
#include "qt-models/messagehandlermodel.h"
|
||||
#include "qt-models/mobilelistmodel.h"
|
||||
#include "profile-widget/qmlprofile.h"
|
||||
#include "core/downloadfromdcthread.h"
|
||||
#include "core/subsurfacestartup.h" // for testqml
|
||||
#include "core/metrics.h"
|
||||
|
@ -220,7 +219,6 @@ static void register_qml_types(QQmlEngine *engine)
|
|||
#ifdef SUBSURFACE_MOBILE
|
||||
register_qml_type<QMLManager>("QMLManager");
|
||||
register_qml_type<StatsManager>("StatsManager");
|
||||
register_qml_type<QMLProfile>("QMLProfile");
|
||||
register_qml_type<DiveImportedModel>("DCImportModel");
|
||||
register_qml_type<DiveSummaryModel>("DiveSummaryModel");
|
||||
register_qml_type<ChartListModel>("ChartListModel");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue