profile: render profile on desktop via QtQuick

This breaks all dynamic features, including animations,
zooming tooltips, planner-handles, etc. They will have to be
converted one-by-one to QtQuick, which will be a major pain,
as the ProfileView is destroyed by Qt6 on reparenting.
This means that the view cannot store any persistent state.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2023-05-20 14:27:20 +02:00
parent 7eecc85464
commit 75886dd4d6
25 changed files with 322 additions and 76 deletions

View file

@ -24,6 +24,8 @@ set(SUBSURFACE_PROFILE_LIB_SRCS
divetooltipitem.h
profilescene.cpp
profilescene.h
profileview.cpp
profileview.h
tankitem.cpp
tankitem.h
)
@ -38,8 +40,6 @@ set(SUBSURFACE_PROFILE_LIB_SRCS
${SUBSURFACE_PROFILE_LIB_SRCS}
divehandler.cpp
divehandler.h
profilewidget2.cpp
profilewidget2.h
ruleritem.cpp
ruleritem.h
)

View file

@ -1,6 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
#include "divehandler.h"
#include "profilewidget2.h"
#include "profilescene.h"
#include "core/dive.h"
#include "core/gettextfromc.h"
@ -22,8 +21,9 @@ DiveHandler::DiveHandler(const struct dive *d, int currentDcNr) : dive(d), dcNr(
int DiveHandler::parentIndex()
{
ProfileWidget2 *view = qobject_cast<ProfileWidget2 *>(scene()->views().first());
return view->handleIndex(this);
//ProfileWidget2 *view = qobject_cast<ProfileWidget2 *>(scene()->views().first());
//return view->handleIndex(this);
return 0; // FIXME
}
void DiveHandler::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
@ -54,19 +54,17 @@ void DiveHandler::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
void DiveHandler::selfRemove()
{
#ifndef SUBSURFACE_MOBILE
setSelected(true);
ProfileWidget2 *view = qobject_cast<ProfileWidget2 *>(scene()->views().first());
view->keyDeleteAction();
#endif
//ProfileWidget2 *view = qobject_cast<ProfileWidget2 *>(scene()->views().first());
//view->keyDeleteAction();
}
void DiveHandler::changeGas()
{
ProfileWidget2 *view = qobject_cast<ProfileWidget2 *>(scene()->views().first());
QAction *action = qobject_cast<QAction *>(sender());
//ProfileWidget2 *view = qobject_cast<ProfileWidget2 *>(scene()->views().first());
//QAction *action = qobject_cast<QAction *>(sender());
view->changeGas(parentIndex(), action->data().toInt());
//view->changeGas(parentIndex(), action->data().toInt());
}
void DiveHandler::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
@ -75,9 +73,9 @@ void DiveHandler::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
return;
t.start();
ProfileWidget2 *view = qobject_cast<ProfileWidget2*>(scene()->views().first());
if(!view->profileScene->pointOnProfile(event->scenePos()))
return;
//ProfileWidget2 *view = qobject_cast<ProfileWidget2*>(scene()->views().first());
//if(!view->profileScene->pointOnProfile(event->scenePos()))
//return;
QGraphicsEllipseItem::mouseMoveEvent(event);
emit moved();

View file

@ -3,13 +3,15 @@
#include "profile-widget/divecartesianaxis.h"
#include "profile-widget/divetextitem.h"
#include "profile-widget/animationfunctions.h"
#include "core/dive.h"
#include "core/profile.h"
#include "qt-models/diveplannermodel.h"
#include "core/qthelper.h"
#include "core/settings/qPrefTechnicalDetails.h"
#include "core/settings/qPrefLog.h"
#include "libdivecomputer/parser.h"
#include "profile-widget/profilewidget2.h"
#include "qt-models/diveplannermodel.h"
#include <QPainter>
AbstractProfilePolygonItem::AbstractProfilePolygonItem(const plot_info &pInfo, const DiveCartesianAxis &horizontal,
const DiveCartesianAxis &vertical, DataAccessor accessor,

View file

@ -1,11 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
#include "divetextitem.h"
#include "profilewidget2.h"
#include "core/color.h"
#include "core/errorhelper.h"
#include <QBrush>
#include <cmath>
#include <QApplication>
#include <QBrush>
#include <QPainter>
static const double outlineSize = 3.0;

View file

@ -465,6 +465,10 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM
updateVisibility(hasHeartBeat, simplified);
updateAxes(hasHeartBeat, simplified);
// When we found that we don't have enough place to draw, the state was set to empty.
if (empty)
return;
int newMaxtime = get_maxtime(plotInfo);
if (calcMax || newMaxtime > maxtime)
maxtime = newMaxtime;

View file

@ -0,0 +1,134 @@
// SPDX-License-Identifier: GPL-2.0
#include "profileview.h"
#include "profilescene.h"
#include "zvalues.h"
#include "core/dive.h"
#include "core/errorhelper.h"
#include "core/pref.h"
#include "core/settings/qPrefTechnicalDetails.h"
#include "qt-quick/chartitem.h"
#include <QDebug>
#include <QElapsedTimer>
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),
zoomedPosition(0.0),
empty(true),
shouldCalculateMax(true),
profileScene(std::make_unique<ProfileScene>(1.0, false, false))
{
setBackgroundColor(Qt::black);
setFlag(ItemHasContents, true);
setAcceptHoverEvents(true);
setAcceptedMouseButtons(Qt::LeftButton);
}
ProfileView::ProfileView() : ProfileView(nullptr)
{
}
ProfileView::~ProfileView()
{
}
void ProfileView::resetPointers()
{
profileItem.reset();
}
void ProfileView::plotAreaChanged(const QSizeF &s)
{
if (!empty)
plotDive(d, dc, RenderFlags::Instant);
}
void ProfileView::clear()
{
//clearPictures();
//disconnectPlannerConnections();
profileScene->clear();
//handles.clear();
//gases.clear();
empty = true;
d = nullptr;
dc = 0;
}
void ProfileView::plotDive(const struct dive *dIn, int dcIn, int flags)
{
d = dIn;
dc = dcIn;
if (!d) {
clear();
return;
}
// If there was no previously displayed dive, turn off animations
if (empty)
flags |= RenderFlags::Instant;
empty = false;
// If Qt decided to destroy our canvas, recreate it
if (!profileItem)
profileItem = createChartItem<ChartGraphicsSceneItem>(ProfileZValue::Profile);
profileItem->setPos(QPointF(0.0, 0.0));
QElapsedTimer measureDuration; // let's measure how long this takes us (maybe we'll turn of TTL calculation later
measureDuration.start();
//DivePlannerPointsModel *model = currentState == EDIT || currentState == PLAN ? plannerModel : nullptr;
DivePlannerPointsModel *model = nullptr;
bool inPlanner = flags & RenderFlags::PlanMode;
QColor backgroundColor = inPlanner ? QColor("#D7E3EF")
: getColor(::BACKGROUND, false);
double zoom = calcZoom(zoomLevel);
profileScene->resize(size());
profileScene->plotDive(d, dc, model, inPlanner, true, //flags & RenderFlags::Instant,
flags & RenderFlags::DontRecalculatePlotInfo,
shouldCalculateMax, zoom, zoomedPosition);
profileItem->draw(size(), backgroundColor, *profileScene);
//rulerItem->setVisible(prefs.rulergraph && currentState != PLAN && currentState != EDIT);
//toolTipItem->setPlotInfo(profileScene->plotInfo);
//rulerItem->setPlotInfo(d, profileScene->plotInfo);
//if ((currentState == EDIT || currentState == PLAN) && plannerModel) {
//repositionDiveHandlers();
//plannerModel->deleteTemporaryPlan();
//}
// On zoom / pan don't recreate the picture thumbnails, only change their position.
//if (flags & RenderFlags::DontRecalculatePlotInfo)
//updateThumbnails();
//else
//plotPicturesInternal(d, flags & RenderFlags::Instant);
//toolTipItem->refresh(d, mapToScene(mapFromGlobal(QCursor::pos())), currentState == PLAN);
update();
// OK, how long did this take us? Anything above the second is way too long,
// so if we are calculation TTS / NDL then let's force that off.
qint64 elapsedTime = measureDuration.elapsed();
if (verbose)
qDebug() << "Profile calculation for dive " << d->number << "took" << elapsedTime << "ms" << " -- calculated ceiling preference is" << prefs.calcceiling;
if (elapsedTime > 1000 && prefs.calcndltts) {
qPrefTechnicalDetails::set_calcndltts(false);
report_error("%s", qPrintable(tr("Show NDL / TTS was disabled because of excessive processing time")));
}
}

View file

@ -0,0 +1,42 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef PROFILE_VIEW_H
#define PROFILE_VIEW_H
#include "qt-quick/chartview.h"
#include <memory>
class ChartGraphicsSceneItem;
class ProfileScene;
class ProfileView : public ChartView {
Q_OBJECT
public:
ProfileView();
ProfileView(QQuickItem *parent);
~ProfileView();
struct RenderFlags {
static constexpr int None = 0;
static constexpr int Instant = 1 << 0;
static constexpr int DontRecalculatePlotInfo = 1 << 1;
static constexpr int EditMode = 1 << 2;
static constexpr int PlanMode = 1 << 3;
};
void plotDive(const struct dive *d, int dc, int flags = RenderFlags::None);
void clear();
private:
const struct dive *d;
int dc;
int zoomLevel;
double zoomedPosition; // Position when zoomed: 0.0 = beginning, 1.0 = end.
bool empty; // No dive shown.
bool shouldCalculateMax; // Calculate maximum time and depth (default). False when dragging handles.
std::unique_ptr<ProfileScene> profileScene;
ChartItemPtr<ChartGraphicsSceneItem> profileItem;
void plotAreaChanged(const QSizeF &size) override;
void resetPointers() override;
};
#endif

View file

@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
#include "profile-widget/profilewidget2.h"
#include "profile-widget/profilescene.h"
#include "core/device.h"
#include "core/event.h"
@ -398,21 +397,6 @@ static void hideAll(const T &container)
item->setVisible(false);
}
void ProfileWidget2::clear()
{
currentState = INIT;
#ifndef SUBSURFACE_MOBILE
clearPictures();
#endif
disconnectPlannerModel();
profileScene->clear();
handles.clear();
gases.clear();
empty = true;
d = nullptr;
dc = 0;
}
void ProfileWidget2::setProfileState(const dive *dIn, int dcIn)
{
d = dIn;

View file

@ -60,7 +60,6 @@ public:
#ifndef SUBSURFACE_MOBILE
bool eventFilter(QObject *, QEvent *) override;
#endif
std::unique_ptr<ProfileScene> profileScene;
State currentState;
signals:

View file

@ -1,12 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
#include "profile-widget/ruleritem.h"
#include "profile-widget/profilewidget2.h"
#include "core/settings/qPrefTechnicalDetails.h"
#include <qgraphicssceneevent.h>
#include "core/profile.h"
#include <QFont>
#include <QFontMetrics>
#include <QGraphicsScene>
#include <QGraphicsSceneEvent>
#include <QGraphicsView>
RulerNodeItem2::RulerNodeItem2() :
pInfo(NULL),
idx(-1),
@ -77,18 +80,16 @@ RulerItem2::RulerItem2() : pInfo(NULL),
textItemBack->setPen(QColor(Qt::white));
textItemBack->setFlag(QGraphicsItem::ItemIgnoresTransformations);
setPen(QPen(QColor(Qt::black), 0.0));
#ifndef SUBSURFACE_MOBILE
connect(qPrefTechnicalDetails::instance(), &qPrefTechnicalDetails::rulergraphChanged, this, &RulerItem2::settingsChanged);
#endif
}
void RulerItem2::settingsChanged(bool value)
{
ProfileWidget2 *profWidget = NULL;
if (scene() && scene()->views().count())
profWidget = qobject_cast<ProfileWidget2 *>(scene()->views().first());
//ProfileWidget2 *profWidget = NULL;
//if (scene() && scene()->views().count())
//profWidget = qobject_cast<ProfileWidget2 *>(scene()->views().first());
setVisible( (profWidget && profWidget->currentState == ProfileWidget2::PROFILE) ? value : false);
//setVisible( (profWidget && profWidget->currentState == ProfileWidget2::PROFILE) ? value : false);
}
void RulerItem2::recalculate()

19
profile-widget/zvalues.h Normal file
View file

@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0
// Defines the z-values of features in the profile view.
// Objects with higher z-values are painted on top of objects
// with smaller z-values. For the same z-value objects are
// drawn in order of addition to the scene.
#ifndef PROFILE_ZVALUES_H
#define PROFILE_ZVALUES_H
// Encapsulating an enum in a struct is stupid, but allows us
// to not poison the namespace and yet autoconvert to int
// (in constrast to enum class). enum is so broken!
struct ProfileZValue {
enum ZValues {
Profile = 0,
Count
};
};
#endif