mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
profile: convert the "ruler item" to qt-quick
Code is mostly based on the "tooltip item". The dragging code was slightly reworked to be more logical. A "disk item" was added for the handles. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
parent
b167e130a4
commit
ea0085fef6
14 changed files with 313 additions and 228 deletions
|
@ -182,7 +182,8 @@ SOURCES += subsurface-mobile-main.cpp \
|
||||||
profile-widget/tooltipitem.cpp \
|
profile-widget/tooltipitem.cpp \
|
||||||
profile-widget/divelineitem.cpp \
|
profile-widget/divelineitem.cpp \
|
||||||
profile-widget/divetextitem.cpp \
|
profile-widget/divetextitem.cpp \
|
||||||
profile-widget/profileview.cpp
|
profile-widget/profileview.cpp \
|
||||||
|
profile-widget/ruleritem.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
commands/command_base.h \
|
commands/command_base.h \
|
||||||
|
@ -345,7 +346,9 @@ HEADERS += \
|
||||||
profile-widget/divelineitem.h \
|
profile-widget/divelineitem.h \
|
||||||
profile-widget/divepixmapcache.h \
|
profile-widget/divepixmapcache.h \
|
||||||
profile-widget/divetextitem.h \
|
profile-widget/divetextitem.h \
|
||||||
profile-widget/profileview.h
|
profile-widget/profileview.h \
|
||||||
|
profile-widget/ruleritem.h \
|
||||||
|
profile-widget/profiletranslations.h
|
||||||
|
|
||||||
RESOURCES += mobile-widgets/qml/mobile-resources.qrc \
|
RESOURCES += mobile-widgets/qml/mobile-resources.qrc \
|
||||||
mobile-widgets/3rdparty/icons.qrc \
|
mobile-widgets/3rdparty/icons.qrc \
|
||||||
|
|
|
@ -23,6 +23,8 @@ set(SUBSURFACE_PROFILE_LIB_SRCS
|
||||||
profiletranslations.h
|
profiletranslations.h
|
||||||
profileview.cpp
|
profileview.cpp
|
||||||
profileview.h
|
profileview.h
|
||||||
|
ruleritem.cpp
|
||||||
|
ruleritem.h
|
||||||
tankitem.cpp
|
tankitem.cpp
|
||||||
tankitem.h
|
tankitem.h
|
||||||
tooltipitem.h
|
tooltipitem.h
|
||||||
|
@ -37,8 +39,6 @@ set(SUBSURFACE_PROFILE_LIB_SRCS
|
||||||
${SUBSURFACE_PROFILE_LIB_SRCS}
|
${SUBSURFACE_PROFILE_LIB_SRCS}
|
||||||
divehandler.cpp
|
divehandler.cpp
|
||||||
divehandler.h
|
divehandler.h
|
||||||
ruleritem.cpp
|
|
||||||
ruleritem.h
|
|
||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
source_group("Subsurface Profile" FILES ${SUBSURFACE_PROFILE_LIB_SRCS})
|
source_group("Subsurface Profile" FILES ${SUBSURFACE_PROFILE_LIB_SRCS})
|
||||||
|
|
|
@ -612,22 +612,37 @@ int ProfileScene::timeAt(QPointF pos) const
|
||||||
return lrint(timeAxis->valueAt(pos));
|
return lrint(timeAxis->valueAt(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<double, double> ProfileScene::minMaxTime()
|
std::pair<double, double> ProfileScene::minMaxTime() const
|
||||||
{
|
{
|
||||||
return { timeAxis->minimum(), timeAxis->maximum() };
|
return { timeAxis->minimum(), timeAxis->maximum() };
|
||||||
}
|
}
|
||||||
|
|
||||||
double ProfileScene::yToScreen(double y)
|
std::pair<double, double> ProfileScene::minMaxX() const
|
||||||
|
{
|
||||||
|
return timeAxis->screenMinMax();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<double, double> ProfileScene::minMaxY() const
|
||||||
|
{
|
||||||
|
return profileYAxis->screenMinMax();
|
||||||
|
}
|
||||||
|
|
||||||
|
double ProfileScene::yToScreen(double y) const
|
||||||
{
|
{
|
||||||
auto [min, max] = profileYAxis->screenMinMax();
|
auto [min, max] = profileYAxis->screenMinMax();
|
||||||
return min + (max - min) * y;
|
return min + (max - min) * y;
|
||||||
}
|
}
|
||||||
|
|
||||||
double ProfileScene::posAtTime(double time)
|
double ProfileScene::posAtTime(double time) const
|
||||||
{
|
{
|
||||||
return timeAxis->posAtValue(time);
|
return timeAxis->posAtValue(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double ProfileScene::posAtDepth(double depth) const
|
||||||
|
{
|
||||||
|
return profileYAxis->posAtValue(depth);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::pair<QString, QPixmap>> ProfileScene::eventsAt(QPointF pos) const
|
std::vector<std::pair<QString, QPixmap>> ProfileScene::eventsAt(QPointF pos) const
|
||||||
{
|
{
|
||||||
std::vector<std::pair<QString, QPixmap>> res;
|
std::vector<std::pair<QString, QPixmap>> res;
|
||||||
|
|
|
@ -51,9 +51,12 @@ public:
|
||||||
double calcZoomPosition(double zoom, double originalPos, double delta);
|
double calcZoomPosition(double zoom, double originalPos, double delta);
|
||||||
const plot_info &getPlotInfo() const;
|
const plot_info &getPlotInfo() const;
|
||||||
int timeAt(QPointF pos) const; // time in seconds
|
int timeAt(QPointF pos) const; // time in seconds
|
||||||
std::pair<double, double> minMaxTime(); // time in seconds
|
std::pair<double, double> minMaxTime() const; // time in seconds
|
||||||
double posAtTime(double time); // time in seconds
|
std::pair<double, double> minMaxX() const; // minimum and maximum x positions of canvas
|
||||||
double yToScreen(double y); // For pictures: depth given in fration of displayed range.
|
std::pair<double, double> minMaxY() const; // minimum and maximum y positions of canvas
|
||||||
|
double posAtTime(double time) const; // time in seconds
|
||||||
|
double posAtDepth(double depth) const;
|
||||||
|
double yToScreen(double y) const; // For pictures: depth given in fration of displayed range.
|
||||||
std::vector<std::pair<QString, QPixmap>> eventsAt(QPointF pos) const;
|
std::vector<std::pair<QString, QPixmap>> eventsAt(QPointF pos) const;
|
||||||
|
|
||||||
const struct dive *d;
|
const struct dive *d;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "profileview.h"
|
#include "profileview.h"
|
||||||
#include "pictureitem.h"
|
#include "pictureitem.h"
|
||||||
#include "profilescene.h"
|
#include "profilescene.h"
|
||||||
|
#include "ruleritem.h"
|
||||||
#include "tooltipitem.h"
|
#include "tooltipitem.h"
|
||||||
#include "zvalues.h"
|
#include "zvalues.h"
|
||||||
#include "core/dive.h"
|
#include "core/dive.h"
|
||||||
|
@ -142,6 +143,7 @@ void ProfileView::resetPointers()
|
||||||
{
|
{
|
||||||
profileItem.reset();
|
profileItem.reset();
|
||||||
tooltip.reset();
|
tooltip.reset();
|
||||||
|
ruler.reset();
|
||||||
pictures.clear();
|
pictures.clear();
|
||||||
highlightedPicture = nullptr;
|
highlightedPicture = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -170,6 +172,8 @@ void ProfileView::clear()
|
||||||
//gases.clear();
|
//gases.clear();
|
||||||
if (tooltip)
|
if (tooltip)
|
||||||
tooltip->setVisible(false);
|
tooltip->setVisible(false);
|
||||||
|
if (ruler)
|
||||||
|
ruler->setVisible(false);
|
||||||
empty = true;
|
empty = true;
|
||||||
d = nullptr;
|
d = nullptr;
|
||||||
dc = 0;
|
dc = 0;
|
||||||
|
@ -217,9 +221,6 @@ void ProfileView::plotDive(const struct dive *dIn, int dcIn, int flags)
|
||||||
background = inPlanner ? QColor("#D7E3EF") : getColor(::BACKGROUND, false);
|
background = inPlanner ? QColor("#D7E3EF") : getColor(::BACKGROUND, false);
|
||||||
profileItem->draw(size(), background, *profileScene);
|
profileItem->draw(size(), background, *profileScene);
|
||||||
|
|
||||||
//rulerItem->setVisible(prefs.rulergraph && currentState != PLAN && currentState != EDIT);
|
|
||||||
//rulerItem->setPlotInfo(d, profileScene->plotInfo);
|
|
||||||
|
|
||||||
//if ((currentState == EDIT || currentState == PLAN) && plannerModel) {
|
//if ((currentState == EDIT || currentState == PLAN) && plannerModel) {
|
||||||
//repositionDiveHandlers();
|
//repositionDiveHandlers();
|
||||||
//plannerModel->deleteTemporaryPlan();
|
//plannerModel->deleteTemporaryPlan();
|
||||||
|
@ -257,6 +258,15 @@ void ProfileView::plotDive(const struct dive *dIn, int dcIn, int flags)
|
||||||
tooltip->setVisible(false);
|
tooltip->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ruler)
|
||||||
|
ruler = std::make_unique<RulerItem>(*this, dpr);
|
||||||
|
if (prefs.rulergraph && !(flags & RenderFlags::PlanMode) && !(flags & RenderFlags::EditMode)) {
|
||||||
|
ruler->setVisible(true);
|
||||||
|
updateRuler(animSpeed);
|
||||||
|
} else {
|
||||||
|
ruler->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
// Reset animation.
|
// Reset animation.
|
||||||
animation = make_anim([this](double progress) { anim(progress); }, animSpeed);
|
animation = make_anim([this](double progress) { anim(progress); }, animSpeed);
|
||||||
}
|
}
|
||||||
|
@ -549,6 +559,20 @@ void ProfileView::updateTooltip(QPointF pos, bool plannerMode, int animSpeed)
|
||||||
{ if (tooltip) tooltip->anim(progress); update(); }, animSpeed);
|
{ if (tooltip) tooltip->anim(progress); update(); }, animSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProfileView::rulerDragged()
|
||||||
|
{
|
||||||
|
updateRuler(qPrefDisplay::animation_speed());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileView::updateRuler(int animSpeed)
|
||||||
|
{
|
||||||
|
ruler->update(d, dpr, *profileScene, profileScene->getPlotInfo(), animSpeed);
|
||||||
|
|
||||||
|
// Reset animation.
|
||||||
|
ruler_animation = make_anim([this](double progress)
|
||||||
|
{ if (ruler) ruler->anim(progress); update(); }, animSpeed);
|
||||||
|
}
|
||||||
|
|
||||||
// Create a PictureEntry object and add its thumbnail to the scene if profile pictures are shown.
|
// Create a PictureEntry object and add its thumbnail to the scene if profile pictures are shown.
|
||||||
ProfileView::PictureEntry::PictureEntry(offset_t offset, const QString &filename, ChartItemPtr<PictureItem> thumbnail, double dpr, bool synchronous) : offset(offset),
|
ProfileView::PictureEntry::PictureEntry(offset_t offset, const QString &filename, ChartItemPtr<PictureItem> thumbnail, double dpr, bool synchronous) : offset(offset),
|
||||||
filename(filename),
|
filename(filename),
|
||||||
|
|
|
@ -13,6 +13,7 @@ class ProfileAnimation;
|
||||||
class ProfileScene;
|
class ProfileScene;
|
||||||
class ToolTipItem;
|
class ToolTipItem;
|
||||||
struct picture;
|
struct picture;
|
||||||
|
class RulerItem;
|
||||||
|
|
||||||
class ProfileView : public ChartView {
|
class ProfileView : public ChartView {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -40,6 +41,7 @@ public:
|
||||||
void clear();
|
void clear();
|
||||||
void resetZoom();
|
void resetZoom();
|
||||||
void anim(double fraction);
|
void anim(double fraction);
|
||||||
|
void rulerDragged(); // Called by the RulterItem when a handle was dragged.
|
||||||
|
|
||||||
// For mobile
|
// For mobile
|
||||||
Q_INVOKABLE void pinchStart();
|
Q_INVOKABLE void pinchStart();
|
||||||
|
@ -84,6 +86,10 @@ private:
|
||||||
void updateTooltip(QPointF pos, bool plannerMode, int animSpeed);
|
void updateTooltip(QPointF pos, bool plannerMode, int animSpeed);
|
||||||
std::unique_ptr<ProfileAnimation> tooltip_animation;
|
std::unique_ptr<ProfileAnimation> tooltip_animation;
|
||||||
|
|
||||||
|
std::unique_ptr<RulerItem> ruler;
|
||||||
|
void updateRuler(int animSpeed);
|
||||||
|
std::unique_ptr<ProfileAnimation> ruler_animation;
|
||||||
|
|
||||||
QPointF previousHoverMovePosition;
|
QPointF previousHoverMovePosition;
|
||||||
|
|
||||||
// The list of pictures in this plot. The pictures are sorted by offset in seconds.
|
// The list of pictures in this plot. The pictures are sorted by offset in seconds.
|
||||||
|
|
|
@ -1,182 +1,184 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
#include "profile-widget/ruleritem.h"
|
#include "ruleritem.h"
|
||||||
|
#include "profilescene.h"
|
||||||
|
#include "profileview.h"
|
||||||
|
#include "zvalues.h"
|
||||||
#include "core/settings/qPrefTechnicalDetails.h"
|
#include "core/settings/qPrefTechnicalDetails.h"
|
||||||
|
|
||||||
#include "core/profile.h"
|
#include "core/profile.h"
|
||||||
|
|
||||||
#include <QFont>
|
#include <QApplication>
|
||||||
#include <QFontMetrics>
|
|
||||||
#include <QGraphicsScene>
|
|
||||||
#include <QGraphicsSceneEvent>
|
|
||||||
#include <QGraphicsView>
|
|
||||||
|
|
||||||
RulerNodeItem2::RulerNodeItem2() :
|
static QColor handleBorderColor(Qt::red);
|
||||||
pInfo(NULL),
|
static QColor handleColor(0xff, 0, 0, 0x80);
|
||||||
idx(-1),
|
static constexpr double handleRadius = 8.0;
|
||||||
ruler(NULL),
|
|
||||||
timeAxis(NULL),
|
static QColor lineColor(Qt::black);
|
||||||
depthAxis(NULL)
|
static constexpr double lineWidth = 1.0;
|
||||||
|
|
||||||
|
static constexpr int tooltipBorder = 1;
|
||||||
|
static constexpr double tooltipBorderRadius = 2.0; // Radius of rounded corners
|
||||||
|
static QColor tooltipBorderColor(Qt::black);
|
||||||
|
static QColor tooltipColor(0xff, 0xff, 0xff, 190);
|
||||||
|
static QColor tooltipFontColor(Qt::black);
|
||||||
|
|
||||||
|
class RulerItemHandle : public ChartDiskItem
|
||||||
{
|
{
|
||||||
setRect(-8, -8, 16, 16);
|
public:
|
||||||
setBrush(QColor(0xff, 0, 0, 127));
|
ProfileView &profileView;
|
||||||
setPen(QColor(Qt::red));
|
double xpos;
|
||||||
setFlag(ItemIsMovable);
|
// The first argument is passed twice, to avoid an downcast. Yes, that's silly.
|
||||||
setFlag(ItemSendsGeometryChanges);
|
RulerItemHandle(ChartView &view, ProfileView &profileView, double dpr) :
|
||||||
setFlag(ItemIgnoresTransformations);
|
ChartDiskItem(view, ProfileZValue::RulerItem,
|
||||||
|
QPen(handleBorderColor, dpr),
|
||||||
|
QBrush(handleColor),
|
||||||
|
true),
|
||||||
|
profileView(profileView),
|
||||||
|
xpos(0.0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
// The call chain here is weird: this calls into the ProfileScene, which then calls
|
||||||
|
// back into the RulerItem. The reason is that the ProfileScene knows the current
|
||||||
|
// dive etc. This seems more robust than storing the current dive in the subobject.
|
||||||
|
void drag(QPointF pos) override
|
||||||
|
{
|
||||||
|
xpos = pos.x();
|
||||||
|
profileView.rulerDragged();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// duplicate code in tooltipitem.cpp
|
||||||
|
static QFont makeFont(double dpr)
|
||||||
|
{
|
||||||
|
QFont font(qApp->font());
|
||||||
|
if (dpr != 1.0) {
|
||||||
|
int pixelSize = font.pixelSize();
|
||||||
|
if (pixelSize > 0) {
|
||||||
|
pixelSize = lrint(static_cast<double>(pixelSize) * dpr);
|
||||||
|
font.setPixelSize(pixelSize);
|
||||||
|
} else {
|
||||||
|
font.setPointSizeF(font.pointSizeF() * dpr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RulerNodeItem2::setPlotInfo(const plot_info &info)
|
static ChartItemPtr<RulerItemHandle> makeHandle(ProfileView &view, double dpr)
|
||||||
{
|
{
|
||||||
pInfo = &info;
|
auto res = view.createChartItem<RulerItemHandle>(view, dpr);
|
||||||
idx = 0;
|
res->resize(handleRadius * dpr);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RulerNodeItem2::setRuler(RulerItem2 *r)
|
RulerItem::RulerItem(ProfileView &view, double dpr) :
|
||||||
|
line(view.createChartItem<ChartLineItem>(ProfileZValue::RulerItem,
|
||||||
|
lineColor, lineWidth * dpr)),
|
||||||
|
handle1(makeHandle(view, dpr)),
|
||||||
|
handle2(makeHandle(view, dpr)),
|
||||||
|
tooltip(view.createChartItem<AnimatedChartRectItem>(ProfileZValue::RulerItem,
|
||||||
|
QPen(tooltipBorderColor, lrint(tooltipBorder * dpr)),
|
||||||
|
QBrush(tooltipColor), tooltipBorderRadius * dpr,
|
||||||
|
false)),
|
||||||
|
font(makeFont(dpr)),
|
||||||
|
fm(font),
|
||||||
|
fontHeight(fm.height())
|
||||||
{
|
{
|
||||||
ruler = r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RulerNodeItem2::recalculate()
|
void RulerItem::setVisible(bool visible)
|
||||||
{
|
{
|
||||||
if (!pInfo || pInfo->nr <= 0)
|
line->setVisible(visible);
|
||||||
|
handle1->setVisible(visible);
|
||||||
|
handle2->setVisible(visible);
|
||||||
|
tooltip->setVisible(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary search to find index at time stamp
|
||||||
|
static int get_idx_at_time(const plot_info &info, int time)
|
||||||
|
{
|
||||||
|
auto entry = std::lower_bound(info.entry.begin(), info.entry.end(), time,
|
||||||
|
[](const plot_data &d, int time)
|
||||||
|
{ return d.sec < time; });
|
||||||
|
return entry != info.entry.end() ? entry - info.entry.begin()
|
||||||
|
: info.entry.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RulerItem::update(const dive *d, double dpr, const ProfileScene &scene, const plot_info &info, int animspeed)
|
||||||
|
{
|
||||||
|
if (info.nr == 0)
|
||||||
|
return; // Nothing to display
|
||||||
|
|
||||||
|
auto [minX, maxX] = scene.minMaxX();
|
||||||
|
auto [minY, maxY] = scene.minMaxY();
|
||||||
|
double x1 = std::clamp(handle1->xpos, minX, maxX);
|
||||||
|
double x2 = std::clamp(handle2->xpos, minX, maxX);
|
||||||
|
|
||||||
|
int time1 = lrint(scene.timeAt(QPointF(x1, 0.0)));
|
||||||
|
int time2 = lrint(scene.timeAt(QPointF(x2, 0.0)));
|
||||||
|
|
||||||
|
int idx1 = get_idx_at_time(info, time1);
|
||||||
|
int idx2 = get_idx_at_time(info, time2);
|
||||||
|
|
||||||
|
double y1 = scene.posAtDepth(info.entry[idx1].depth);
|
||||||
|
double y2 = scene.posAtDepth(info.entry[idx2].depth);
|
||||||
|
|
||||||
|
QPointF pos1(x1, y1);
|
||||||
|
QPointF pos2(x2, y2);
|
||||||
|
line->setLine(pos1, pos2);
|
||||||
|
handle1->setPos(pos1);
|
||||||
|
handle2->setPos(pos2);
|
||||||
|
|
||||||
|
if (idx1 == idx2) {
|
||||||
|
tooltip->setVisible(false);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const struct plot_data &last = pInfo->entry[pInfo->nr - 1];
|
|
||||||
if (x() < 0) {
|
|
||||||
setPos(0, y());
|
|
||||||
} else if (x() > timeAxis->posAtValue(last.sec)) {
|
|
||||||
setPos(timeAxis->posAtValue(last.sec), depthAxis->posAtValue(last.depth));
|
|
||||||
} else {
|
|
||||||
idx = 0;
|
|
||||||
while (idx < pInfo->nr && timeAxis->posAtValue(pInfo->entry[idx].sec) < x())
|
|
||||||
++idx;
|
|
||||||
const struct plot_data &data = pInfo->entry[idx];
|
|
||||||
setPos(timeAxis->posAtValue(data.sec), depthAxis->posAtValue(data.depth));
|
|
||||||
}
|
}
|
||||||
}
|
tooltip->setVisible(true);
|
||||||
|
|
||||||
void RulerNodeItem2::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
auto lines = compare_samples(d, info, idx1, idx2, 1);
|
||||||
{
|
|
||||||
qreal x = event->scenePos().x();
|
|
||||||
if (x < 0.0)
|
|
||||||
x = 0.0;
|
|
||||||
setPos(x, event->scenePos().y());
|
|
||||||
recalculate();
|
|
||||||
ruler->recalculate();
|
|
||||||
}
|
|
||||||
|
|
||||||
RulerItem2::RulerItem2() : pInfo(NULL),
|
double width = 0;
|
||||||
source(new RulerNodeItem2()),
|
// Turn strings into QString/width pairs and increase width if needed
|
||||||
dest(new RulerNodeItem2()),
|
std::vector<std::pair<QString, int>> strings;
|
||||||
timeAxis(NULL),
|
strings.reserve(lines.size());
|
||||||
depthAxis(NULL),
|
for (auto &s_std: lines) {
|
||||||
textItemBack(new QGraphicsRectItem(this)),
|
auto s = QString::fromStdString(s_std);
|
||||||
textItem(new QGraphicsSimpleTextItem(this))
|
int w = fm.size(Qt::TextSingleLine, s).width();
|
||||||
{
|
width = std::max(width, static_cast<double>(w));
|
||||||
source->setRuler(this);
|
strings.push_back(std::make_pair(s, w));
|
||||||
dest->setRuler(this);
|
|
||||||
textItem->setFlag(QGraphicsItem::ItemIgnoresTransformations);
|
|
||||||
textItemBack->setBrush(QColor(0xff, 0xff, 0xff, 190));
|
|
||||||
textItemBack->setPen(QColor(Qt::white));
|
|
||||||
textItemBack->setFlag(QGraphicsItem::ItemIgnoresTransformations);
|
|
||||||
setPen(QPen(QColor(Qt::black), 0.0));
|
|
||||||
connect(qPrefTechnicalDetails::instance(), &qPrefTechnicalDetails::rulergraphChanged, this, &RulerItem2::settingsChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RulerItem2::settingsChanged(bool value)
|
|
||||||
{
|
|
||||||
//ProfileWidget2 *profWidget = NULL;
|
|
||||||
//if (scene() && scene()->views().count())
|
|
||||||
//profWidget = qobject_cast<ProfileWidget2 *>(scene()->views().first());
|
|
||||||
|
|
||||||
//setVisible( (profWidget && profWidget->currentState == ProfileWidget2::PROFILE) ? value : false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RulerItem2::recalculate()
|
|
||||||
{
|
|
||||||
QPointF tmp;
|
|
||||||
QFont font;
|
|
||||||
QFontMetrics fm(font);
|
|
||||||
|
|
||||||
if (timeAxis == NULL || depthAxis == NULL || !pInfo || pInfo->nr == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
prepareGeometryChange();
|
|
||||||
startPoint = mapFromItem(source, 0, 0);
|
|
||||||
endPoint = mapFromItem(dest, 0, 0);
|
|
||||||
|
|
||||||
if (startPoint.x() > endPoint.x()) {
|
|
||||||
tmp = endPoint;
|
|
||||||
endPoint = startPoint;
|
|
||||||
startPoint = tmp;
|
|
||||||
}
|
}
|
||||||
QLineF line(startPoint, endPoint);
|
width += 6.0 * tooltipBorder * dpr;
|
||||||
setLine(line);
|
|
||||||
|
|
||||||
QString text;
|
double height = static_cast<double>(strings.size()) * fontHeight +
|
||||||
for (const std::string &s: compare_samples(dive, *pInfo, source->idx, dest->idx, 1)) {
|
4.0 * tooltipBorder * dpr;
|
||||||
if (!text.isEmpty())
|
|
||||||
text += '\n';
|
QPixmap pixmap(lrint(width), lrint(height));
|
||||||
text += QString::fromStdString(s);
|
pixmap.fill(Qt::transparent);
|
||||||
|
QPainter painter(&pixmap);
|
||||||
|
|
||||||
|
painter.setFont(font);
|
||||||
|
painter.setPen(QPen(tooltipFontColor)); // QPainter uses QPen to set text color!
|
||||||
|
double x = 4.0 * tooltipBorder * dpr;
|
||||||
|
double y = 2.0 * tooltipBorder * dpr;
|
||||||
|
for (auto &[s,w]: strings) {
|
||||||
|
QRectF rect(x, y, w, fontHeight);
|
||||||
|
painter.drawText(rect, s);
|
||||||
|
y += fontHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw text
|
tooltip->setPixmap(pixmap, animspeed);
|
||||||
QGraphicsView *view = scene()->views().first();
|
|
||||||
QPoint begin = view->mapFromScene(mapToScene(startPoint));
|
|
||||||
textItem->setText(text);
|
|
||||||
qreal tgtX = startPoint.x();
|
|
||||||
const qreal diff = begin.x() + textItem->boundingRect().width();
|
|
||||||
// clamp so that the text doesn't go out of the screen to the right
|
|
||||||
if (diff > view->width()) {
|
|
||||||
begin.setX(lrint(begin.x() - (diff - view->width())));
|
|
||||||
tgtX = mapFromScene(view->mapToScene(begin)).x();
|
|
||||||
}
|
|
||||||
// always show the text bellow the lowest of the start and end points
|
|
||||||
qreal tgtY = (startPoint.y() >= endPoint.y()) ? startPoint.y() : endPoint.y();
|
|
||||||
// this isn't exactly optimal, since we want to scale the 1.0, 4.0 distances as well
|
|
||||||
textItem->setPos(tgtX - 1.0, tgtY + 4.0);
|
|
||||||
|
|
||||||
// setup the text background
|
if (pos2.x() < pos1.x())
|
||||||
textItemBack->setVisible(startPoint.x() != endPoint.x());
|
std::swap(pos1, pos2);
|
||||||
textItemBack->setPos(textItem->x(), textItem->y());
|
double xpos = width < maxX - minX ?
|
||||||
textItemBack->setRect(0, 0, textItem->boundingRect().width(), textItem->boundingRect().height());
|
std::clamp(pos1.x() + handleRadius * dpr, 0.0, maxX - width) : 0.0;
|
||||||
|
|
||||||
|
double ypos = height < maxY - minY ?
|
||||||
|
std::clamp(pos1.y() + handleRadius * dpr, 0.0, maxY - height) : 0.0;
|
||||||
|
|
||||||
|
tooltip->setPos(QPointF(xpos, ypos));
|
||||||
}
|
}
|
||||||
|
|
||||||
RulerNodeItem2 *RulerItem2::sourceNode() const
|
void RulerItem::anim(double progress)
|
||||||
{
|
{
|
||||||
return source;
|
tooltip->anim(progress);
|
||||||
}
|
|
||||||
|
|
||||||
RulerNodeItem2 *RulerItem2::destNode() const
|
|
||||||
{
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RulerItem2::setPlotInfo(const struct dive *d, const plot_info &info)
|
|
||||||
{
|
|
||||||
dive = d;
|
|
||||||
pInfo = &info;
|
|
||||||
dest->setPlotInfo(info);
|
|
||||||
source->setPlotInfo(info);
|
|
||||||
dest->recalculate();
|
|
||||||
source->recalculate();
|
|
||||||
recalculate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RulerItem2::setAxis(DiveCartesianAxis *time, DiveCartesianAxis *depth)
|
|
||||||
{
|
|
||||||
timeAxis = time;
|
|
||||||
depthAxis = depth;
|
|
||||||
dest->depthAxis = depth;
|
|
||||||
dest->timeAxis = time;
|
|
||||||
source->depthAxis = depth;
|
|
||||||
source->timeAxis = time;
|
|
||||||
recalculate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RulerItem2::setVisible(bool visible)
|
|
||||||
{
|
|
||||||
QGraphicsLineItem::setVisible(visible);
|
|
||||||
source->setVisible(visible);
|
|
||||||
dest->setVisible(visible);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,59 +2,31 @@
|
||||||
#ifndef RULERITEM_H
|
#ifndef RULERITEM_H
|
||||||
#define RULERITEM_H
|
#define RULERITEM_H
|
||||||
|
|
||||||
#include <QObject>
|
#include "qt-quick/chartitem.h"
|
||||||
#include <QGraphicsEllipseItem>
|
|
||||||
#include <QGraphicsObject>
|
|
||||||
#include "profile-widget/divecartesianaxis.h"
|
|
||||||
|
|
||||||
struct plot_data;
|
#include <QFont>
|
||||||
|
#include <QFontMetrics>
|
||||||
|
|
||||||
|
class ProfileView;
|
||||||
|
class ProfileScene;
|
||||||
|
class RulerItemHandle;
|
||||||
struct plot_info;
|
struct plot_info;
|
||||||
class RulerItem2;
|
struct dive;
|
||||||
|
|
||||||
class RulerNodeItem2 : public QObject, public QGraphicsEllipseItem {
|
class RulerItem {
|
||||||
Q_OBJECT
|
ChartItemPtr<ChartLineItem> line;
|
||||||
friend class RulerItem2;
|
ChartItemPtr<RulerItemHandle> handle1, handle2;
|
||||||
|
ChartItemPtr<AnimatedChartRectItem> tooltip;
|
||||||
|
|
||||||
|
QFont font;
|
||||||
|
QFontMetrics fm;
|
||||||
|
double fontHeight;
|
||||||
|
QPixmap title;
|
||||||
public:
|
public:
|
||||||
explicit RulerNodeItem2();
|
RulerItem(ProfileView &view, double dpr);
|
||||||
void setRuler(RulerItem2 *r);
|
|
||||||
void setPlotInfo(const struct plot_info &info);
|
|
||||||
void recalculate();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
|
|
||||||
const struct plot_info *pInfo;
|
|
||||||
int idx;
|
|
||||||
RulerItem2 *ruler;
|
|
||||||
DiveCartesianAxis *timeAxis;
|
|
||||||
DiveCartesianAxis *depthAxis;
|
|
||||||
};
|
|
||||||
|
|
||||||
class RulerItem2 : public QObject, public QGraphicsLineItem {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit RulerItem2();
|
|
||||||
void recalculate();
|
|
||||||
|
|
||||||
void setPlotInfo(const struct dive *d, const struct plot_info &pInfo);
|
|
||||||
RulerNodeItem2 *sourceNode() const;
|
|
||||||
RulerNodeItem2 *destNode() const;
|
|
||||||
void setAxis(DiveCartesianAxis *time, DiveCartesianAxis *depth);
|
|
||||||
void setVisible(bool visible);
|
void setVisible(bool visible);
|
||||||
|
void update(const dive *d, double dpr, const ProfileScene &scene, const plot_info &info, int animspeed);
|
||||||
public
|
void anim(double progress);
|
||||||
slots:
|
|
||||||
void settingsChanged(bool toggled);
|
|
||||||
|
|
||||||
private:
|
|
||||||
const struct dive *dive;
|
|
||||||
const struct plot_info *pInfo;
|
|
||||||
QPointF startPoint, endPoint;
|
|
||||||
RulerNodeItem2 *source, *dest;
|
|
||||||
QString text;
|
|
||||||
DiveCartesianAxis *timeAxis;
|
|
||||||
DiveCartesianAxis *depthAxis;
|
|
||||||
QGraphicsRectItem *textItemBack;
|
|
||||||
QGraphicsSimpleTextItem *textItem;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QFontMetrics>
|
|
||||||
|
|
||||||
static const int tooltipBorder = 2;
|
static const int tooltipBorder = 2;
|
||||||
static const double tooltipBorderRadius = 4.0; // Radius of rounded corners
|
static const double tooltipBorderRadius = 4.0; // Radius of rounded corners
|
||||||
|
@ -37,8 +36,8 @@ static QFont makeFont(double dpr)
|
||||||
|
|
||||||
ToolTipItem::ToolTipItem(ChartView &view, double dpr) :
|
ToolTipItem::ToolTipItem(ChartView &view, double dpr) :
|
||||||
AnimatedChartRectItem(view, ProfileZValue::ToolTipItem,
|
AnimatedChartRectItem(view, ProfileZValue::ToolTipItem,
|
||||||
QPen(tooltipBorderColor, tooltipBorder),
|
QPen(tooltipBorderColor, lrint(tooltipBorder * dpr)),
|
||||||
QBrush(tooltipColor), tooltipBorderRadius,
|
QBrush(tooltipColor), tooltipBorderRadius * dpr,
|
||||||
true),
|
true),
|
||||||
font(makeFont(dpr)),
|
font(makeFont(dpr)),
|
||||||
fm(font),
|
fm(font),
|
||||||
|
@ -131,9 +130,9 @@ void ToolTipItem::update(const dive *d, double dpr, int time, const plot_info &p
|
||||||
}
|
}
|
||||||
|
|
||||||
width += tissues.width();
|
width += tissues.width();
|
||||||
width += 6.0 * tooltipBorder;
|
width += 6.0 * tooltipBorder * dpr;
|
||||||
height = std::max(height, static_cast<double>(tissues.height()));
|
height = std::max(height, static_cast<double>(tissues.height()));
|
||||||
height += 4.0 * tooltipBorder + title.height();
|
height += 4.0 * tooltipBorder * dpr + title.height();
|
||||||
|
|
||||||
QPixmap pixmap(lrint(width), lrint(height));
|
QPixmap pixmap(lrint(width), lrint(height));
|
||||||
pixmap.fill(Qt::transparent);
|
pixmap.fill(Qt::transparent);
|
||||||
|
@ -141,12 +140,12 @@ void ToolTipItem::update(const dive *d, double dpr, int time, const plot_info &p
|
||||||
|
|
||||||
painter.setFont(font);
|
painter.setFont(font);
|
||||||
painter.setPen(QPen(tooltipFontColor)); // QPainter uses QPen to set text color!
|
painter.setPen(QPen(tooltipFontColor)); // QPainter uses QPen to set text color!
|
||||||
double x = 4.0 * tooltipBorder + tissues.width();
|
double x = 4.0 * tooltipBorder * dpr + tissues.width();
|
||||||
double y = 2.0 * tooltipBorder;
|
double y = 2.0 * tooltipBorder * dpr;
|
||||||
double titleOffset = (width - title.width()) / 2.0;
|
double titleOffset = (width - title.width()) / 2.0;
|
||||||
painter.drawPixmap(lrint(titleOffset), lrint(y), title, 0, 0, title.width(), title.height());
|
painter.drawPixmap(lrint(titleOffset), lrint(y), title, 0, 0, title.width(), title.height());
|
||||||
y += round(fontHeight);
|
y += round(fontHeight);
|
||||||
painter.drawPixmap(lrint(2.0 * tooltipBorder), lrint(y), tissues, 0, 0, tissues.width(), tissues.height());
|
painter.drawPixmap(lrint(2.0 * tooltipBorder * dpr), lrint(y), tissues, 0, 0, tissues.width(), tissues.height());
|
||||||
y += round(fontHeight);
|
y += round(fontHeight);
|
||||||
for (auto &[s,w]: strings) {
|
for (auto &[s,w]: strings) {
|
||||||
QRectF rect(x, y, w, fontHeight);
|
QRectF rect(x, y, w, fontHeight);
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "qt-quick/chartitem.h"
|
#include "qt-quick/chartitem.h"
|
||||||
|
|
||||||
#include <QFont>
|
#include <QFont>
|
||||||
|
#include <QFontMetrics>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
|
|
||||||
struct dive;
|
struct dive;
|
||||||
|
|
|
@ -13,6 +13,7 @@ struct ProfileZValue {
|
||||||
enum ZValues {
|
enum ZValues {
|
||||||
Profile = 0,
|
Profile = 0,
|
||||||
Pictures,
|
Pictures,
|
||||||
|
RulerItem,
|
||||||
ToolTipItem,
|
ToolTipItem,
|
||||||
Count
|
Count
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,7 +37,7 @@ QRectF ChartItem::getRect() const
|
||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChartItem::setPos(QPointF)
|
void ChartItem::drag(QPointF)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +119,11 @@ void ChartPixmapItem::resize(QSizeF size)
|
||||||
setTextureDirty();
|
setTextureDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChartPixmapItem::drag(QPointF pos)
|
||||||
|
{
|
||||||
|
setPos(pos);
|
||||||
|
}
|
||||||
|
|
||||||
void ChartPixmapItem::setPos(QPointF pos)
|
void ChartPixmapItem::setPos(QPointF pos)
|
||||||
{
|
{
|
||||||
rect.moveTopLeft(pos);
|
rect.moveTopLeft(pos);
|
||||||
|
@ -158,6 +163,8 @@ ChartRectItem::~ChartRectItem()
|
||||||
void ChartRectItem::resize(QSizeF size)
|
void ChartRectItem::resize(QSizeF size)
|
||||||
{
|
{
|
||||||
ChartPixmapItem::resize(size);
|
ChartPixmapItem::resize(size);
|
||||||
|
if (!painter)
|
||||||
|
return;
|
||||||
img->fill(Qt::transparent);
|
img->fill(Qt::transparent);
|
||||||
painter->setPen(pen);
|
painter->setPen(pen);
|
||||||
painter->setBrush(brush);
|
painter->setBrush(brush);
|
||||||
|
@ -175,7 +182,8 @@ void AnimatedChartRectItem::setPixmap(const QPixmap &p, int animSpeed)
|
||||||
{
|
{
|
||||||
if (animSpeed <= 0) {
|
if (animSpeed <= 0) {
|
||||||
resize(p.size());
|
resize(p.size());
|
||||||
painter->drawPixmap(0, 0, p, 0, 0, p.width(), p.height());
|
if (painter)
|
||||||
|
painter->drawPixmap(0, 0, p, 0, 0, p.width(), p.height());
|
||||||
setTextureDirty();
|
setTextureDirty();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -194,10 +202,47 @@ void AnimatedChartRectItem::anim(double fraction)
|
||||||
QSize s(mid(originalSize.width(), pixmap.width(), fraction),
|
QSize s(mid(originalSize.width(), pixmap.width(), fraction),
|
||||||
mid(originalSize.height(), pixmap.height(), fraction));
|
mid(originalSize.height(), pixmap.height(), fraction));
|
||||||
resize(s);
|
resize(s);
|
||||||
painter->drawPixmap(0, 0, pixmap, 0, 0, s.width(), s.height());
|
if (painter)
|
||||||
|
painter->drawPixmap(0, 0, pixmap, 0, 0, s.width(), s.height());
|
||||||
setTextureDirty();
|
setTextureDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChartDiskItem::ChartDiskItem(ChartView &v, size_t z, const QPen &pen, const QBrush &brush, bool dragable) :
|
||||||
|
ChartPixmapItem(v, z, dragable),
|
||||||
|
pen(pen), brush(brush)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ChartDiskItem::~ChartDiskItem()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChartDiskItem::resize(double radius)
|
||||||
|
{
|
||||||
|
ChartPixmapItem::resize(QSizeF(2.0 * radius, 2.0 * radius));
|
||||||
|
if (!painter)
|
||||||
|
return;
|
||||||
|
img->fill(Qt::transparent);
|
||||||
|
painter->setPen(pen);
|
||||||
|
painter->setBrush(brush);
|
||||||
|
QSize imgSize = img->size();
|
||||||
|
int width = pen.width();
|
||||||
|
QRect rect(width / 2, width / 2, imgSize.width() - width, imgSize.height() - width);
|
||||||
|
painter->drawEllipse(rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Moves the _center_ of the disk to given position.
|
||||||
|
void ChartDiskItem::setPos(QPointF pos)
|
||||||
|
{
|
||||||
|
rect.moveCenter(pos);
|
||||||
|
setPositionDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF ChartDiskItem::getPos() const
|
||||||
|
{
|
||||||
|
return rect.center();
|
||||||
|
}
|
||||||
|
|
||||||
ChartTextItem::ChartTextItem(ChartView &v, size_t z, const QFont &f, const std::vector<QString> &text, bool center) :
|
ChartTextItem::ChartTextItem(ChartView &v, size_t z, const QFont &f, const std::vector<QString> &text, bool center) :
|
||||||
ChartPixmapItem(v, z), f(f), center(center)
|
ChartPixmapItem(v, z), f(f), center(center)
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,7 +30,7 @@ public:
|
||||||
const bool dragable; // Item can be dragged with the mouse. Must be set in constructor.
|
const bool dragable; // Item can be dragged with the mouse. Must be set in constructor.
|
||||||
virtual ~ChartItem(); // Attention: must only be called by render thread.
|
virtual ~ChartItem(); // Attention: must only be called by render thread.
|
||||||
QRectF getRect() const;
|
QRectF getRect() const;
|
||||||
virtual void setPos(QPointF pos); // Called when dragging the item
|
virtual void drag(QPointF pos); // Called when dragging the item
|
||||||
virtual void stopDrag(QPointF pos); // Called when dragging the item finished
|
virtual void stopDrag(QPointF pos); // Called when dragging the item finished
|
||||||
protected:
|
protected:
|
||||||
ChartItem(ChartView &v, size_t z, bool dragable = false);
|
ChartItem(ChartView &v, size_t z, bool dragable = false);
|
||||||
|
@ -81,7 +81,8 @@ public:
|
||||||
ChartPixmapItem(ChartView &v, size_t z, bool dragable = false);
|
ChartPixmapItem(ChartView &v, size_t z, bool dragable = false);
|
||||||
~ChartPixmapItem();
|
~ChartPixmapItem();
|
||||||
|
|
||||||
void setPos(QPointF pos) override;
|
virtual void setPos(QPointF pos);
|
||||||
|
void drag(QPointF pos) override; // calls setPos() by default
|
||||||
void setScale(double scale);
|
void setScale(double scale);
|
||||||
void render() override;
|
void render() override;
|
||||||
protected:
|
protected:
|
||||||
|
@ -131,6 +132,19 @@ private:
|
||||||
QSize originalSize;
|
QSize originalSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// A solid disk, potentially with border.
|
||||||
|
class ChartDiskItem : public ChartPixmapItem {
|
||||||
|
public:
|
||||||
|
ChartDiskItem(ChartView &v, size_t z, const QPen &pen, const QBrush &brush, bool dragable = false);
|
||||||
|
~ChartDiskItem();
|
||||||
|
void resize(double radius);
|
||||||
|
void setPos(QPointF pos) override;
|
||||||
|
QPointF getPos() const;
|
||||||
|
private:
|
||||||
|
QPen pen;
|
||||||
|
QBrush brush;
|
||||||
|
};
|
||||||
|
|
||||||
// Attention: text is only drawn after calling setColor()!
|
// Attention: text is only drawn after calling setColor()!
|
||||||
class ChartTextItem : public ChartPixmapItem {
|
class ChartTextItem : public ChartPixmapItem {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -342,7 +342,7 @@ void ChartView::mouseMoveEvent(QMouseEvent *event)
|
||||||
QSizeF sceneSize = size();
|
QSizeF sceneSize = size();
|
||||||
if (sceneSize.width() <= 1.0 || sceneSize.height() <= 1.0)
|
if (sceneSize.width() <= 1.0 || sceneSize.height() <= 1.0)
|
||||||
return;
|
return;
|
||||||
draggedItem->setPos(event->pos() - dragStartMouse + dragStartItem);
|
draggedItem->drag(event->pos() - dragStartMouse + dragStartItem);
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue