1
0
Fork 0

Launcher: location page in QtQuick

This moves the last page into QQ2 implementation, in preparation
for making the top-level UI be a QQuickWindow.
This commit is contained in:
James Turner 2018-05-07 16:41:10 +01:00
parent 7e8b1b67e7
commit b2f90fbfa4
34 changed files with 3168 additions and 2034 deletions

View file

@ -16,8 +16,10 @@ using namespace flightgear;
* FGTaxiNode
*************************************************************************/
FGTaxiNode::FGTaxiNode(int index, const SGGeod& pos, bool aOnRunway, int aHoldType) :
FGPositioned(TRANSIENT_ID, FGPositioned::PARKING, "", pos),
FGTaxiNode::FGTaxiNode(int index, const SGGeod& pos,
bool aOnRunway, int aHoldType,
const std::string& ident) :
FGPositioned(TRANSIENT_ID, FGPositioned::PARKING, ident, pos),
m_index(index),
isOnRunway(aOnRunway),
holdType(aHoldType),

View file

@ -31,7 +31,8 @@ protected:
bool m_isPushback;
public:
FGTaxiNode(int index, const SGGeod& pos, bool aOnRunway, int aHoldType);
FGTaxiNode(int index, const SGGeod& pos, bool aOnRunway, int aHoldType,
const std::string& ident = {});
virtual ~FGTaxiNode();
void setElevation(double val);

View file

@ -42,10 +42,9 @@ FGParking::FGParking(int index,
const std::string& name,
const std::string& aType,
const std::string& codes) :
FGTaxiNode(index, pos, false, 0),
FGTaxiNode(index, pos, false, 0, name),
heading(aHeading),
radius(aRadius),
parkingName(name),
type(aType),
airlineCodes(codes)
{

View file

@ -42,7 +42,6 @@ class FGParking : public FGTaxiNode
private:
const double heading;
const double radius;
const std::string parkingName;
const std::string type;
const std::string airlineCodes;
FGTaxiNodeRef pushBackPoint;
@ -64,10 +63,7 @@ public:
std::string getType () const { return type; };
std::string getCodes () const { return airlineCodes;};
std::string getName () const { return parkingName; };
// TODO do parkings have different name and ident?
virtual const std::string& name() const { return parkingName; }
std::string getName () const { return ident(); };
void setPushBackPoint(const FGTaxiNodeRef& node);
FGTaxiNodeRef getPushBackPoint () { return pushBackPoint; };

View file

@ -33,8 +33,12 @@
#include <Airports/runways.hxx>
#include <Airports/parking.hxx>
#include <Airports/pavement.hxx>
#include <Airports/groundnetwork.hxx>
#include <Navaids/navrecord.hxx>
#include <Navaids/NavDataCache.hxx>
#include "QmlPositioned.hxx"
static double distanceToLineSegment(const QVector2D& p, const QVector2D& a,
const QVector2D& b, double* outT = NULL)
@ -86,7 +90,7 @@ static double unitLengthAfterMapping(const QTransform& t)
return QVector2D(tVec).length();
}
AirportDiagram::AirportDiagram(QWidget* pr) :
AirportDiagram::AirportDiagram(QQuickItem* pr) :
BaseDiagram(pr),
m_approachDistanceNm(-1.0)
{
@ -133,10 +137,29 @@ void AirportDiagram::setAirport(FGAirportRef apt)
m_airport = apt;
m_projectionCenter = apt ? apt->geod() : SGGeod();
m_runways.clear();
m_approachDistanceNm = -1.0; m_parking.clear();
m_parking.clear();
m_helipads.clear();
if (apt) {
if (apt->type() == FGPositioned::HELIPORT) {
for (unsigned int r=0; r<apt->numHelipads(); ++r) {
FGHelipadRef pad = apt->getHelipadByIndex(r);
// add pad with index as data role
addHelipad(pad);
}
} else {
for (unsigned int r=0; r<apt->numRunways(); ++r) {
addRunway(apt->getRunwayByIndex(r));
}
}
FGGroundNetwork* ground = apt->groundNetwork();
if (ground && ground->exists()) {
for (auto park : ground->allParkings()) {
addParking(park);
}
} // of was able to get ground-network
buildTaxiways();
buildPavements();
}
@ -148,48 +171,23 @@ void AirportDiagram::setAirport(FGAirportRef apt)
update();
}
FGRunwayRef AirportDiagram::selectedRunway() const
void AirportDiagram::setSelection(QmlPositioned* pos)
{
return m_selectedRunway;
}
void AirportDiagram::setSelectedRunway(FGRunwayRef r)
{
if (r == m_selectedRunway) {
if (pos && (m_selection == pos->inner())) {
return;
}
m_selectedParking.clear();
m_selectedHelipad.clear();
m_selectedRunway = r;
update();
}
void AirportDiagram::setSelectedHelipad(FGHelipadRef pad)
{
if (pad == m_selectedHelipad) {
return;
if (!pos) {
m_selection.clear();
} else {
m_selection = pos->inner();
}
m_selectedParking.clear();
m_selectedRunway.clear();
m_selectedHelipad = pad;
emit selectionChanged();
recomputeBounds(true);
update();
}
void AirportDiagram::setSelectedParking(FGParkingRef park)
{
if (m_selectedParking == park) {
return;
}
m_selectedRunway.clear();
m_selectedHelipad.clear();
m_selectedParking = park;
update();
}
void AirportDiagram::setApproachExtensionDistance(double distanceNm)
void AirportDiagram::setApproachExtensionNm(double distanceNm)
{
if (m_approachDistanceNm == distanceNm) {
return;
@ -198,6 +196,38 @@ void AirportDiagram::setApproachExtensionDistance(double distanceNm)
m_approachDistanceNm = distanceNm;
recomputeBounds(true);
update();
emit approachExtensionChanged();
}
double AirportDiagram::approachExtensionNm() const
{
return m_approachDistanceNm;
}
QmlPositioned* AirportDiagram::selection() const
{
if (!m_selection)
return nullptr;
return new QmlPositioned{m_selection};
}
qlonglong AirportDiagram::airportGuid() const
{
if (!m_airport)
return 0;
return m_airport->guid();
}
void AirportDiagram::setAirportGuid(qlonglong guid)
{
if (guid == -1) {
m_airport.clear();
} else {
m_airport = fgpositioned_cast<FGAirport>(flightgear::NavDataCache::instance()->loadById(guid));
}
setAirport(m_airport);
emit airportChanged();
}
void AirportDiagram::addRunway(FGRunwayRef rwy)
@ -233,16 +263,17 @@ void AirportDiagram::doComputeBounds()
}
Q_FOREACH(const ParkingData& p, m_parking) {
extendBounds(p.pt);
extendBounds(p.pt, 10.0);
}
Q_FOREACH(const HelipadData& p, m_helipads) {
extendBounds(p.pt);
extendBounds(p.pt, 20.0);
}
if (m_selectedRunway && (m_approachDistanceNm > 0.0)) {
FGRunway* runwaySelection = fgpositioned_cast<FGRunway>(m_selection);
if (runwaySelection && (m_approachDistanceNm > 0.0)) {
double d = SG_NM_TO_METER * m_approachDistanceNm;
QPointF pt = project(m_selectedRunway->pointOnCenterline(-d));
QPointF pt = project(runwaySelection->pointOnCenterline(-d));
extendBounds(pt);
}
}
@ -263,11 +294,9 @@ void AirportDiagram::addHelipad(FGHelipadRef pad)
update();
}
void AirportDiagram::paintContents(QPainter* p)
{
QTransform t = p->transform();
// pavements
QBrush brush(QColor(0x9f, 0x9f, 0x9f));
Q_FOREACH(const QPainterPath& path, m_pavements) {
@ -305,11 +334,13 @@ void AirportDiagram::paintContents(QPainter* p)
SGGeod aircraftPos;
int headingDeg;
FGRunway* runwaySelection = fgpositioned_cast<FGRunway>(m_selection);
// now draw the runways for real
Q_FOREACH(const RunwayData& r, m_runways) {
QColor color(Qt::magenta);
if ((r.runway == m_selectedRunway) || (r.runway->reciprocalRunway() == m_selectedRunway)) {
if ((r.runway == runwaySelection) || (r.runway->reciprocalRunway() == runwaySelection)) {
color = Qt::yellow;
}
@ -329,7 +360,7 @@ void AirportDiagram::paintContents(QPainter* p)
// invert scaling factor so we can use screen pixel sizes here
p->scale(1.0 / m_scale, 1.0/ m_scale);
p->setPen((r.runway == m_selectedRunway) ? Qt::yellow : Qt::magenta);
p->setPen((r.runway == runwaySelection) ? Qt::yellow : Qt::magenta);
p->drawText(QRect(-100, 5, 200, 200), ident, Qt::AlignHCenter | Qt::AlignTop);
FGRunway* recip = r.runway->reciprocalRunway();
@ -340,28 +371,28 @@ void AirportDiagram::paintContents(QPainter* p)
p->rotate(recip->headingDeg());
p->scale(1.0 / m_scale, 1.0/ m_scale);
p->setPen((r.runway->reciprocalRunway() == m_selectedRunway) ? Qt::yellow : Qt::magenta);
p->setPen((r.runway->reciprocalRunway() == runwaySelection) ? Qt::yellow : Qt::magenta);
p->drawText(QRect(-100, 5, 200, 200), recipIdent, Qt::AlignHCenter | Qt::AlignTop);
}
if (m_selectedRunway) {
if (runwaySelection) {
drawAircraft = true;
aircraftPos = m_selectedRunway->geod();
headingDeg = m_selectedRunway->headingDeg();
aircraftPos = runwaySelection->geod();
headingDeg = runwaySelection->headingDeg();
}
if (m_selectedRunway && (m_approachDistanceNm > 0.0)) {
if (runwaySelection && (m_approachDistanceNm > 0.0)) {
p->setTransform(t);
// draw approach extension point
double d = SG_NM_TO_METER * m_approachDistanceNm;
QPointF pt = project(m_selectedRunway->pointOnCenterline(-d));
QPointF pt2 = project(m_selectedRunway->geod());
QPointF pt = project(runwaySelection->pointOnCenterline(-d));
QPointF pt2 = project(runwaySelection->geod());
QPen pen(Qt::yellow);
pen.setWidth(2.0 / m_scale);
p->setPen(pen);
p->drawLine(pt, pt2);
aircraftPos = m_selectedRunway->pointOnCenterline(-d);
aircraftPos = runwaySelection->pointOnCenterline(-d);
}
if (drawAircraft) {
@ -388,23 +419,26 @@ void AirportDiagram::paintContents(QPainter* p)
void AirportDiagram::drawHelipads(QPainter* painter)
{
QTransform t = painter->transform();
FGHelipad* selectedHelipad = fgpositioned_cast<FGHelipad>(m_selection);
Q_FOREACH(const HelipadData& p, m_helipads) {
painter->setTransform(t);
painter->save();
painter->translate(p.pt);
if (p.helipad == m_selectedHelipad) {
if (p.helipad == selectedHelipad) {
painter->setBrush(Qt::yellow);
} else {
painter->setBrush(Qt::magenta);
}
painter->drawPath(m_helipadIconPath);
painter->restore();
}
}
void AirportDiagram::drawParking(QPainter* painter, const ParkingData& p) const
{
painter->save();
painter->translate(p.pt);
double hdg = p.parking->getHeading();
@ -420,8 +454,10 @@ void AirportDiagram::drawParking(QPainter* painter, const ParkingData& p) const
}
painter->rotate(hdg);
painter->setPen(Qt::NoPen);
if (p.parking == m_selectedParking) {
FGParking* selectedParking = fgpositioned_cast<FGParking>(m_selection);
if (p.parking == selectedParking) {
painter->setBrush(Qt::yellow);
} else {
painter->setBrush(QColor(255, 196, 196)); // kind of pink
@ -431,7 +467,7 @@ void AirportDiagram::drawParking(QPainter* painter, const ParkingData& p) const
// ensure the selection colour is quite visible, by not filling
// with white when selected
if (p.parking != m_selectedParking) {
if (p.parking != selectedParking) {
painter->fillRect(labelRect, Qt::white);
}
@ -450,39 +486,39 @@ void AirportDiagram::drawParking(QPainter* painter, const ParkingData& p) const
// draw text
painter->setPen(Qt::black);
painter->drawText(labelRect, textFlags, parkingName);
painter->restore();
}
AirportDiagram::ParkingData AirportDiagram::findParkingData(const FGParkingRef &pk) const
{
FGParking* selectedParking = fgpositioned_cast<FGParking>(m_selection);
if (!selectedParking)
return {};
Q_FOREACH(const ParkingData& p, m_parking) {
if (p.parking == m_selectedParking) {
if (p.parking == selectedParking) {
return p;
}
}
return ParkingData();
return {};
}
void AirportDiagram::drawParkings(QPainter* painter) const
{
painter->save();
QTransform t = painter->transform();
FGParking* selectedParking = fgpositioned_cast<FGParking>(m_selection);
Q_FOREACH(const ParkingData& p, m_parking) {
if (p.parking == m_selectedParking) {
if (p.parking == selectedParking) {
continue; // skip and draw last
}
painter->setTransform(t);
drawParking(painter, p);
}
if (m_selectedParking) {
painter->setTransform(t);
drawParking(painter, findParkingData(m_selectedParking));
if (selectedParking) {
drawParking(painter, findParkingData(selectedParking));
}
painter->restore();
}
void AirportDiagram::drawILS(QPainter* painter, FGRunwayRef runway) const
@ -514,12 +550,14 @@ void AirportDiagram::drawILS(QPainter* painter, FGRunwayRef runway) const
void AirportDiagram::mouseReleaseEvent(QMouseEvent* me)
{
if (me->button() != Qt::LeftButton) {
return;
}
me->accept();
QTransform t(transform());
double minWidth = 8.0 * unitLengthAfterMapping(t.inverted());
#if 0
QImage img(width(), height(), QImage::Format_ARGB32);
QPainter imgPaint(&img);
imgPaint.setPen(QPen(Qt::cyan, 1));
#endif
Q_FOREACH(const RunwayData& r, m_runways) {
QPainterPath pp = pathForRunway(r, t, minWidth);
@ -528,11 +566,8 @@ void AirportDiagram::mouseReleaseEvent(QMouseEvent* me)
QPointF p1(t.map(r.p1)), p2(t.map(r.p2));
double param;
distanceToLineSegment(QVector2D(me->pos()), QVector2D(p1), QVector2D(p2), &param);
if (param > 0.5) {
emit clickedRunway(r.runway->reciprocalRunway());
} else {
emit clickedRunway(r.runway);
}
const FGRunwayRef clickedRunway = (param > 0.5) ? FGRunwayRef{r.runway->reciprocalRunway()} : r.runway;
emit clicked(new QmlPositioned{clickedRunway});
return;
}
} // of runways iteration
@ -540,18 +575,22 @@ void AirportDiagram::mouseReleaseEvent(QMouseEvent* me)
Q_FOREACH(const ParkingData& parking, m_parking) {
QPainterPath pp = pathForParking(parking, t);
if (pp.contains(me->pos())) {
emit clickedParking(parking.parking);
emit clicked(new QmlPositioned{parking.parking});
return;
}
}
Q_FOREACH(const HelipadData& pad, m_helipads) {
QPainterPath pp = pathForHelipad(pad, t);
//imgPaint.drawPath(pp);
if (pp.contains(me->pos())) {
emit clickedHelipad(pad.helipad);
emit clicked(new QmlPositioned{pad.helipad});
return;
}
}
#if 0
img.save("/Users/jmt/Desktop/img.png");
#endif
}
QPainterPath AirportDiagram::pathForRunway(const RunwayData& r, const QTransform& t,

View file

@ -23,15 +23,25 @@
#include "BaseDiagram.hxx"
#include <QPixmap>
#include <Airports/parking.hxx>
#include <Airports/runways.hxx>
#include <simgear/math/sg_geodesy.hxx>
// forward decls
class QmlPositioned;
class AirportDiagram : public BaseDiagram
{
Q_OBJECT
Q_PROPERTY(QmlPositioned* selection READ selection WRITE setSelection NOTIFY selectionChanged)
Q_PROPERTY(qlonglong airport READ airportGuid WRITE setAirportGuid NOTIFY airportChanged)
Q_PROPERTY(double approachExtensionNm READ approachExtensionNm WRITE setApproachExtensionNm NOTIFY approachExtensionChanged)
public:
AirportDiagram(QWidget* pr);
AirportDiagram(QQuickItem* pr = nullptr);
virtual ~AirportDiagram();
void setAirport(FGAirportRef apt);
@ -40,24 +50,30 @@ public:
void addParking(FGParkingRef park);
void addHelipad(FGHelipadRef pad);
FGRunwayRef selectedRunway() const;
void setSelectedRunway(FGRunwayRef r);
QmlPositioned* selection() const;
void setSelectedHelipad(FGHelipadRef pad);
void setSelectedParking(FGParkingRef park);
void setSelection(QmlPositioned* pos);
void setApproachExtensionNm(double distanceNm);
double approachExtensionNm() const;
qlonglong airportGuid() const;
void setAirportGuid(qlonglong guid);
void setApproachExtensionDistance(double distanceNm);
Q_SIGNALS:
void clickedRunway(FGRunwayRef rwy);
void clickedHelipad(FGHelipadRef pad);
void clickedParking(FGParkingRef park);
void clicked(QmlPositioned* pos);
void selectionChanged();
void airportChanged();
void approachExtensionChanged();
protected:
virtual void mouseReleaseEvent(QMouseEvent* me);
void mouseReleaseEvent(QMouseEvent* me) override;
void paintContents(QPainter*) Q_DECL_OVERRIDE;
void paintContents(QPainter*) override;
void doComputeBounds() Q_DECL_OVERRIDE;
void doComputeBounds() override;
private:
struct RunwayData {
QPointF p1, p2;
@ -115,11 +131,9 @@ private:
QPainterPath m_parkingIconPath, // arrow points right
m_parkingIconLeftPath; // arrow points left
double m_approachDistanceNm;
FGRunwayRef m_selectedRunway;
FGParkingRef m_selectedParking;
FGHelipadRef m_selectedHelipad;
QPainterPath m_helipadIconPath;
FGPositionedRef m_selection;
};
#endif // of GUI_AIRPORT_DIAGRAM_HXX

View file

@ -26,6 +26,7 @@
#include <QDebug>
#include <QVector2D>
#include <QMouseEvent>
#include <QPaintDevice>
#include <Navaids/navrecord.hxx>
#include <Navaids/positioned.hxx>
@ -39,6 +40,7 @@ const double rec = 6378137; // earth radius, equator (?)
const double rpol = 6356752.314; // earth radius, polar (?)
const double MINIMUM_SCALE = 0.002;
const double MAXIMUM_SCALE = 2.0;
//Returns Earth radius at a given latitude (Ellipsoide equation with two equal axis)
static double earth_radius_lat( double lat )
@ -48,14 +50,15 @@ static double earth_radius_lat( double lat )
return 1.0 / sqrt( a * a + b * b );
}
BaseDiagram::BaseDiagram(QWidget* pr) :
QWidget(pr),
BaseDiagram::BaseDiagram(QQuickItem* pr) :
QQuickPaintedItem(pr),
m_autoScalePan(true),
m_wheelAngleDeltaAccumulator(0)
{
setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::MinimumExpanding);
setMinimumSize(100, 100);
setAcceptedMouseButtons(Qt::LeftButton);
setFlag(ItemHasContents);
setOpaquePainting(true);
setAntialiasing(true);
}
QTransform BaseDiagram::transform() const
@ -98,11 +101,15 @@ void BaseDiagram::extendRect(QRectF &r, const QPointF &p)
}
}
void BaseDiagram::paintEvent(QPaintEvent*)
QRect BaseDiagram::rect() const
{
QPainter p(this);
p.setRenderHints(QPainter::Antialiasing);
p.fillRect(rect(), QColor(0x3f, 0x3f, 0x3f));
return QRect(0, 0, width(), height());
}
void BaseDiagram::paint(QPainter* p)
{
//p->setRenderHints(QPainter::Antialiasing);
p->fillRect(rect(), QColor(0x3f, 0x3f, 0x3f));
if (m_autoScalePan) {
// fit bounds within our available space, allowing for a margin
@ -110,24 +117,26 @@ void BaseDiagram::paintEvent(QPaintEvent*)
double ratioInX = (width() - MARGIN * 2) / m_bounds.width();
double ratioInY = (height() - MARGIN * 2) / m_bounds.height();
m_scale = std::min(ratioInX, ratioInY);
SG_CLAMP_RANGE(m_scale, MINIMUM_SCALE, MAXIMUM_SCALE);
}
QTransform t(transform());
p.setTransform(t);
m_baseDeviceTransform = p->deviceTransform();
m_viewportTransform = transform();
p->setWorldTransform(m_viewportTransform * m_baseDeviceTransform);
paintPolygonData(&p);
paintNavaids(&p);
paintContents(&p);
paintPolygonData(p);
paintNavaids(p);
paintContents(p);
}
void BaseDiagram::paintAirplaneIcon(QPainter* painter, const SGGeod& geod, int headingDeg)
{
QPointF pos = project(geod);
QPixmap pix(":/airplane-icon");
pos = painter->transform().map(pos);
painter->resetTransform();
pos = m_viewportTransform.map(pos);
painter->save();
painter->setWorldTransform(m_baseDeviceTransform);
painter->translate(pos.x(), pos.y());
painter->rotate(headingDeg);
@ -135,16 +144,17 @@ void BaseDiagram::paintAirplaneIcon(QPainter* painter, const SGGeod& geod, int h
QRect airplaneIconRect = pix.rect();
airplaneIconRect.moveCenter(QPoint(0,0));
painter->drawPixmap(airplaneIconRect, pix);
painter->restore();
}
void BaseDiagram::paintPolygonData(QPainter* painter)
{
QTransform xf = painter->transform();
QTransform invT = xf.inverted();
SGGeod topLeft = unproject(invT.map(rect().topLeft()), m_projectionCenter);
SGGeod viewCenter = unproject(invT.map(rect().center()), m_projectionCenter);
SGGeod bottomRight = unproject(invT.map(rect().bottomRight()), m_projectionCenter);
QTransform invT = m_viewportTransform.inverted();
const auto geom = rect();
SGGeod topLeft = unproject(invT.map(geom.topLeft()), m_projectionCenter);
SGGeod viewCenter = unproject(invT.map(geom.center()), m_projectionCenter);
SGGeod bottomRight = unproject(invT.map(geom.bottomRight()), m_projectionCenter);
double drawRangeNm = std::max(SGGeodesy::distanceNm(viewCenter, topLeft),
SGGeodesy::distanceNm(viewCenter, bottomRight));
@ -155,35 +165,31 @@ void BaseDiagram::paintPolygonData(QPainter* painter)
QPen waterPen(QColor(64, 64, 255), 1);
waterPen.setCosmetic(true);
painter->setPen(waterPen);
flightgear::PolyLineList::const_iterator it;
for (it=lines.begin(); it != lines.end(); ++it) {
paintGeodVec(painter, (*it)->points());
for (auto line : lines) {
paintGeodVec(painter, line->points());
}
lines = flightgear::PolyLine::linesNearPos(viewCenter, drawRangeNm,
flightgear::PolyLine::URBAN);
for (it=lines.begin(); it != lines.end(); ++it) {
fillClosedGeodVec(painter, QColor(192, 192, 96), (*it)->points());
for (auto line : lines) {
fillClosedGeodVec(painter, QColor(192, 192, 96), line->points());
}
lines = flightgear::PolyLine::linesNearPos(viewCenter, drawRangeNm,
flightgear::PolyLine::RIVER);
painter->setPen(waterPen);
for (it=lines.begin(); it != lines.end(); ++it) {
paintGeodVec(painter, (*it)->points());
for (auto line : lines) {
paintGeodVec(painter, line->points());
}
lines = flightgear::PolyLine::linesNearPos(viewCenter, drawRangeNm,
flightgear::PolyLine::LAKE);
for (it=lines.begin(); it != lines.end(); ++it) {
fillClosedGeodVec(painter, QColor(128, 128, 255),
(*it)->points());
for (auto line : lines) {
fillClosedGeodVec(painter, QColor(128, 128, 255), line->points());
}
}
void BaseDiagram::paintGeodVec(QPainter* painter, const flightgear::SGGeodVec& vec)
@ -216,17 +222,17 @@ class MapFilter : public FGPositioned::TypeFilter
{
public:
MapFilter(LauncherAircraftType aircraft)
MapFilter(LauncherController::AircraftType aircraft)
{
// addType(FGPositioned::FIX);
addType(FGPositioned::NDB);
addType(FGPositioned::VOR);
if (aircraft == Helicopter) {
if (aircraft == LauncherController::Helicopter) {
addType(FGPositioned::HELIPAD);
}
if (aircraft == Seaplane) {
if (aircraft == LauncherController::Seaplane) {
addType(FGPositioned::SEAPORT);
} else {
addType(FGPositioned::AIRPORT);
@ -252,12 +258,11 @@ public:
void BaseDiagram::splitItems(const FGPositionedList& in, FGPositionedList& navaids,
FGPositionedList& ports)
{
FGPositionedList::const_iterator it = in.begin();
for (; it != in.end(); ++it) {
if (FGPositioned::isAirportType(it->ptr())) {
ports.push_back(*it);
for (auto p : in) {
if (FGPositioned::isAirportType(p)) {
ports.push_back(p);
} else {
navaids.push_back(*it);
navaids.push_back(p);
}
}
}
@ -273,10 +278,8 @@ bool orderAirportsByRunwayLength(const FGPositionedRef& a,
void BaseDiagram::paintNavaids(QPainter* painter)
{
QTransform xf = painter->transform();
painter->setTransform(QTransform()); // reset to identity
QTransform invT = xf.inverted();
painter->save();
QTransform invT = m_viewportTransform.inverted();
SGGeod topLeft = unproject(invT.map(rect().topLeft()), m_projectionCenter);
SGGeod viewCenter = unproject(invT.map(rect().center()), m_projectionCenter);
@ -300,19 +303,17 @@ void BaseDiagram::paintNavaids(QPainter* painter)
m_labelRects.clear();
m_labelRects.reserve(items.size());
painter->setTransform(m_baseDeviceTransform);
FGPositionedList::const_iterator it;
for (it = ports.begin(); it != ports.end(); ++it) {
paintNavaid(painter, xf, *it);
for (auto port : ports) {
paintNavaid(painter, port);
}
for (it = navaids.begin(); it != navaids.end(); ++it) {
paintNavaid(painter, xf, *it);
for (auto nav : navaids) {
paintNavaid(painter, nav);
}
// restore transform
painter->setTransform(xf);
painter->restore();
}
QRect boundsOfLines(const QVector<QLineF>& lines)
@ -325,7 +326,7 @@ QRect boundsOfLines(const QVector<QLineF>& lines)
return r;
}
void BaseDiagram::paintNavaid(QPainter* painter, const QTransform& t, const FGPositionedRef &pos)
void BaseDiagram::paintNavaid(QPainter* painter, const FGPositionedRef &pos)
{
if (isNavaidIgnored(pos))
return;
@ -341,9 +342,11 @@ void BaseDiagram::paintNavaid(QPainter* painter, const QTransform& t, const FGPo
if (apt->hasHardRunwayOfLengthFt(minRunwayLengthFt)) {
drawAsIcon = false;
painter->setTransform(t);
QVector<QLineF> lines = projectAirportRuwaysWithCenter(apt, m_projectionCenter);
painter->save();
painter->setTransform(m_viewportTransform * m_baseDeviceTransform);
QPen pen(QColor(0x03, 0x83, 0xbf), 8);
pen.setCosmetic(true);
painter->setPen(pen);
@ -354,15 +357,15 @@ void BaseDiagram::paintNavaid(QPainter* painter, const QTransform& t, const FGPo
painter->setPen(linePen);
painter->drawLines(lines);
painter->resetTransform();
iconRect = m_viewportTransform.mapRect(boundsOfLines(lines));
iconRect = t.mapRect(boundsOfLines(lines));
painter->restore();
}
}
if (drawAsIcon) {
QPixmap pm = iconForPositioned(pos);
QPointF loc = t.map(project(pos->geod()));
QPointF loc = m_viewportTransform.map(project(pos->geod()));
iconRect = pm.rect();
iconRect.moveCenter(loc.toPoint());
painter->drawPixmap(iconRect, pm);
@ -510,8 +513,13 @@ QRect BaseDiagram::labelPositioned(const QRect& itemRect,
void BaseDiagram::mousePressEvent(QMouseEvent *me)
{
if (!hasActiveFocus()) {
forceActiveFocus(Qt::MouseFocusReason);
}
m_lastMousePos = me->pos();
m_didPan = false;
me->accept();
}
void BaseDiagram::mouseMoveEvent(QMouseEvent *me)
@ -558,7 +566,7 @@ void BaseDiagram::wheelEvent(QWheelEvent *we)
m_scale *= 0.75;
}
SG_CLAMP_RANGE(m_scale, MINIMUM_SCALE, 1.0);
SG_CLAMP_RANGE(m_scale, MINIMUM_SCALE, MAXIMUM_SCALE);
update();
}
@ -585,7 +593,7 @@ void BaseDiagram::doComputeBounds()
// no-op in the base class
}
void BaseDiagram::extendBounds(const QPointF& p)
void BaseDiagram::extendBounds(const QPointF& p, double radiusM)
{
// this check added after a bug where apt.dat reports SCSL as
// https://airportguide.com/airport/info/AG0003 (British Columbia)
@ -598,7 +606,12 @@ void BaseDiagram::extendBounds(const QPointF& p)
return;
}
if (radiusM > 0.0) {
extendRect(m_bounds, p - QPointF(radiusM, radiusM));
extendRect(m_bounds, p + QPointF(radiusM, radiusM));
} else {
extendRect(m_bounds, p);
}
}
QPointF BaseDiagram::project(const SGGeod& geod, const SGGeod& center)
@ -787,7 +800,7 @@ QVector<QLineF> BaseDiagram::projectAirportRuwaysWithCenter(FGAirportRef apt, co
return r;
}
void BaseDiagram::setAircraftType(LauncherAircraftType type)
void BaseDiagram::setAircraftType(LauncherController::AircraftType type)
{
m_aircraftType = type;
update();

View file

@ -21,23 +21,23 @@
#ifndef GUI_BASEDIAGRAM_HXX
#define GUI_BASEDIAGRAM_HXX
#include <QWidget>
#include <QPainterPath>
#include <QHash>
#include <QQuickPaintedItem>
#include <QTransform>
#include <simgear/math/sg_geodesy.hxx>
#include <Navaids/positioned.hxx>
#include <Airports/airport.hxx>
#include <Navaids/PolyLine.hxx>
#include "LauncherController.hxx"
#include "QtLauncher_fwd.hxx"
class BaseDiagram : public QWidget
class BaseDiagram : public QQuickPaintedItem
{
Q_OBJECT
public:
BaseDiagram(QWidget* pr);
BaseDiagram(QQuickItem* pr = nullptr);
enum IconOption
{
@ -54,14 +54,16 @@ public:
static QVector<QLineF> projectAirportRuwaysIntoRect(FGAirportRef apt, const QRectF& bounds);
static QVector<QLineF> projectAirportRuwaysWithCenter(FGAirportRef apt, const SGGeod &c);
void setAircraftType(LauncherAircraftType type);
void setAircraftType(LauncherController::AircraftType type);
QRect rect() const;
protected:
virtual void paintEvent(QPaintEvent* pe);
void paint(QPainter* p) override;
virtual void mousePressEvent(QMouseEvent* me);
virtual void mouseMoveEvent(QMouseEvent* me);
void mousePressEvent(QMouseEvent* me) override;
void mouseMoveEvent(QMouseEvent* me) override;
virtual void wheelEvent(QWheelEvent* we);
void wheelEvent(QWheelEvent* we) override;
virtual void paintContents(QPainter*);
@ -71,7 +73,7 @@ protected:
virtual void doComputeBounds();
void extendBounds(const QPointF& p);
void extendBounds(const QPointF& p, double radiusM = 1.0);
QPointF project(const SGGeod& geod) const;
QTransform transform() const;
@ -85,7 +87,7 @@ protected:
QPointF m_panOffset, m_lastMousePos;
int m_wheelAngleDeltaAccumulator;
bool m_didPan;
LauncherAircraftType m_aircraftType;
LauncherController::AircraftType m_aircraftType = LauncherController::Airplane;
static void extendRect(QRectF& r, const QPointF& p);
@ -118,6 +120,8 @@ private:
int & flags /* out parameter */) const;
QRect labelPositioned(const QRect &itemRect, const QSize &bounds, LabelPosition lp) const;
QTransform m_baseDeviceTransform;
QTransform m_viewportTransform;
QVector<FGPositionedRef> m_ignored;
mutable QHash<PositionedID, LabelPosition> m_labelPositions;
@ -126,9 +130,7 @@ private:
static int textFlagsForLabelPosition(LabelPosition pos);
void splitItems(const FGPositionedList &in, FGPositionedList &navaids, FGPositionedList &ports);
void paintNavaid(QPainter *painter,
const QTransform& t,
const FGPositionedRef &pos);
void paintNavaid(QPainter *painter, const FGPositionedRef &pos);
void paintPolygonData(QPainter *painter);
void paintGeodVec(QPainter *painter, const flightgear::SGGeodVec &vec);
void fillClosedGeodVec(QPainter *painter, const QColor &color, const flightgear::SGGeodVec &vec);

View file

@ -69,7 +69,6 @@ endif()
if (HAVE_QT)
qt5_wrap_ui(uic_sources Launcher.ui
SetupRootDialog.ui
LocationWidget.ui
InstallSceneryDialog.ui
)
qt5_add_resources(qrc_sources resources.qrc)
@ -90,14 +89,14 @@ if (HAVE_QT)
AircraftModel.cxx
CatalogListModel.cxx
CatalogListModel.hxx
LocationWidget.cxx
LocationWidget.hxx
QtMessageBox.cxx
QtMessageBox.hxx
QtFileDialog.cxx
QtFileDialog.hxx
InstallSceneryDialog.hxx
InstallSceneryDialog.cxx
LocationController.cxx
LocationController.hxx
ToolboxButton.cpp
ToolboxButton.h
LauncherArgumentTokenizer.cxx
@ -122,6 +121,8 @@ if (HAVE_QT)
LauncherController.hxx
AddOnsController.cxx
AddOnsController.hxx
PixmapImageItem.cxx
PixmapImageItem.hxx
${uic_sources}
${qrc_sources}
${qml_sources})
@ -150,6 +151,12 @@ if (HAVE_QT)
ThumbnailImageItem.hxx
PopupWindowTracker.cxx
PopupWindowTracker.hxx
QmlPositioned.hxx
QmlPositioned.cxx
QmlNavCacheWrapper.hxx
QmlNavCacheWrapper.cxx
QmlRadioButtonHelper.cxx
QmlRadioButtonHelper.hxx
)
set_property(TARGET fgqmlui PROPERTY AUTOMOC ON)

View file

@ -299,7 +299,9 @@
</widget>
<widget class="LocationWidget" name="location"/>
<widget class="QQuickWidget" name="location">
</widget>
<widget class="QQuickWidget" name="environmentPage">
</widget>
@ -360,12 +362,6 @@
<extends>QWidget</extends>
<header>QtQuickWidgets/QQuickWidget</header>
</customwidget>
<customwidget>
<class>LocationWidget</class>
<extends>QWidget</extends>
<header location="global">GUI/LocationWidget.hxx</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ToolboxButton</class>
<extends>QPushButton</extends>

View file

@ -9,6 +9,7 @@
#include <QMessageBox>
#include <QSettings>
#include <QQuickWindow>
#include <QQmlComponent>
// simgear headers
#include <simgear/package/Install.hxx>
@ -38,26 +39,29 @@
#include "DefaultAircraftLocator.hxx"
#include "LaunchConfig.hxx"
#include "AircraftModel.hxx"
// remove me once location widget is ported to Quick
#include "LocationWidget.hxx"
#include "LocationController.hxx"
#include "QmlPositioned.hxx"
#include "PixmapImageItem.hxx"
#include "AirportDiagram.hxx"
#include "NavaidDiagram.hxx"
#include "QmlRadioButtonHelper.hxx"
using namespace simgear::pkg;
LauncherController::LauncherController(QObject *parent,
LocationWidget* loc) :
QObject(parent),
m_locationWidget_FIXME(loc)
LauncherController::LauncherController(QObject *parent) :
QObject(parent)
{
m_serversModel = new MPServersModel(this);
m_location = new LocationController(this);
m_locationHistory = new RecentLocationsModel(this);
m_selectedAircraftInfo = new QmlAircraftInfo(this);
m_config = new LaunchConfig(this);
connect(m_config, &LaunchConfig::collect, this, &LauncherController::collectAircraftArgs);
connect(m_locationWidget_FIXME, &LocationWidget::descriptionChanged,
m_location->setLaunchConfig(m_config);
connect(m_location, &LocationController::descriptionChanged,
this, &LauncherController::summaryChanged);
initQML();
@ -91,6 +95,9 @@ LauncherController::LauncherController(QObject *parent,
void LauncherController::initQML()
{
qmlRegisterUncreatableType<LauncherController>("FlightGear.Launcher", 1, 0, "LauncherController", "no");
qmlRegisterUncreatableType<LocationController>("FlightGear.Launcher", 1, 0, "LocationController", "no");
qmlRegisterType<LauncherArgumentTokenizer>("FlightGear.Launcher", 1, 0, "ArgumentTokenizer");
qmlRegisterUncreatableType<QAbstractItemModel>("FlightGear.Launcher", 1, 0, "QAIM", "no");
qmlRegisterUncreatableType<AircraftProxyModel>("FlightGear.Launcher", 1, 0, "AircraftProxyModel", "no");
@ -108,6 +115,15 @@ void LauncherController::initQML()
qmlRegisterType<ThumbnailImageItem>("FlightGear.Launcher", 1, 0, "ThumbnailImage");
qmlRegisterType<PreviewImageItem>("FlightGear.Launcher", 1, 0, "PreviewImage");
qmlRegisterType<QmlPositioned>("FlightGear", 1, 0, "Positioned");
// this is a Q_GADGET, but we need to register it for use in return types, etc
qRegisterMetaType<QmlGeod>();
qmlRegisterType<PixmapImageItem>("FlightGear", 1, 0, "PixmapImage");
qmlRegisterType<AirportDiagram>("FlightGear", 1, 0, "AirportDiagram");
qmlRegisterType<NavaidDiagram>("FlightGear", 1, 0, "NavaidDiagram");
qmlRegisterType<QmlRadioButtonGroup>("FlightGear", 1, 0, "RadioButtonGroup");
QNetworkDiskCache* diskCache = new QNetworkDiskCache(this);
SGPath cachePath = globals->get_fg_home() / "PreviewsCache";
diskCache->setCacheDirectory(QString::fromStdString(cachePath.utf8Str()));
@ -132,7 +148,7 @@ void LauncherController::restoreSettings()
}
m_locationWidget_FIXME->restoreSettings();
m_location->restoreSettings();
QVariantMap currentLocation = m_locationHistory->mostRecent();
if (currentLocation.isEmpty()) {
// use the default
@ -143,7 +159,7 @@ void LauncherController::restoreSettings()
currentLocation["location-apt-runway"] = "active";
} // otherwise we failed to find the default airport in the nav-db :(
}
m_locationWidget_FIXME->restoreLocation(currentLocation);
m_location->restoreLocation(currentLocation);
updateSelectedAircraft();
m_serversModel->requestRestore();
@ -199,7 +215,7 @@ void LauncherController::doRun()
m_aircraftHistory->insert(m_selectedAircraft);
QVariant locSet = m_locationWidget_FIXME->saveLocation();
QVariant locSet = m_location->saveLocation();
m_locationHistory->insert(locSet);
// aircraft paths
@ -257,8 +273,7 @@ void LauncherController::doApply()
globals->get_props()->setStringValue("/sim/aircraft-dir", aircraftDir);
}
// location
m_locationWidget_FIXME->setLocationProperties();
m_location->setLocationProperties();
saveSettings();
}
@ -268,13 +283,12 @@ QString LauncherController::selectAircraftStateAutomatically()
if (!m_selectedAircraftInfo)
return {};
if (m_locationWidget_FIXME->isAirborneLocation()) {
if (m_selectedAircraftInfo->hasState("approach"))
if (m_location->isAirborneLocation() && m_selectedAircraftInfo->hasState("approach"))
{
return "approach";
}
if (m_locationWidget_FIXME->isParkedLocation()) {
if (m_selectedAircraftInfo->hasState("parked"))
if (m_location->isParkedLocation() && m_selectedAircraftInfo->hasState("parked")) {
return "parked";
} else {
// also try 'engines-running'?
@ -299,14 +313,12 @@ void LauncherController::updateSelectedAircraft()
m_selectedAircraftInfo->setUri(m_selectedAircraft);
QModelIndex index = m_aircraftModel->indexOfAircraftURI(m_selectedAircraft);
if (index.isValid()) {
LauncherAircraftType aircraftType = Airplane;
m_aircraftType = Airplane;
if (index.data(AircraftIsHelicopterRole).toBool()) {
aircraftType = Helicopter;
m_aircraftType = Helicopter;
} else if (index.data(AircraftIsSeaplaneRole).toBool()) {
aircraftType = Seaplane;
m_aircraftType = Seaplane;
}
m_locationWidget_FIXME->setAircraftType(aircraftType);
}
emit canFlyChanged();
@ -380,10 +392,9 @@ QmlAircraftInfo *LauncherController::selectedAircraftInfo() const
void LauncherController::restoreLocation(QVariant var)
{
m_locationWidget_FIXME->restoreLocation(var.toMap());
m_location->restoreLocation(var.toMap());
}
QUrl LauncherController::selectedAircraft() const
{
return m_selectedAircraft;
@ -458,11 +469,6 @@ QStringList LauncherController::combinedSummary() const
return m_settingsSummary + m_environmentSummary;
}
QString LauncherController::locationDescription() const
{
return m_locationWidget_FIXME->locationDescription();
}
simgear::pkg::PackageRef LauncherController::packageForAircraftURI(QUrl uri) const
{
if (uri.scheme() != "package") {

View file

@ -35,9 +35,9 @@ class RecentAircraftModel;
class RecentLocationsModel;
class MPServersModel;
class AircraftItemModel;
class LocationWidget;
class QQuickItem;
class LaunchConfig;
class LocationController;
class LauncherController : public QObject
{
@ -49,6 +49,8 @@ class LauncherController : public QObject
Q_PROPERTY(AircraftItemModel* baseAircraftModel MEMBER m_aircraftModel CONSTANT)
Q_PROPERTY(LocationController* location MEMBER m_location CONSTANT)
Q_PROPERTY(MPServersModel* mpServersModel MEMBER m_serversModel CONSTANT)
Q_PROPERTY(QUrl selectedAircraft READ selectedAircraft WRITE setSelectedAircraft NOTIFY selectedAircraftChanged)
@ -61,7 +63,6 @@ class LauncherController : public QObject
Q_PROPERTY(QStringList settingsSummary READ settingsSummary WRITE setSettingsSummary NOTIFY summaryChanged)
Q_PROPERTY(QStringList environmentSummary READ environmentSummary WRITE setEnvironmentSummary NOTIFY summaryChanged)
Q_PROPERTY(QString locationDescription READ locationDescription NOTIFY summaryChanged)
Q_PROPERTY(QStringList combinedSummary READ combinedSummary NOTIFY summaryChanged)
Q_PROPERTY(QString versionString READ versionString CONSTANT)
@ -70,9 +71,10 @@ class LauncherController : public QObject
Q_PROPERTY(RecentLocationsModel* locationHistory READ locationHistory CONSTANT)
Q_PROPERTY(bool canFly READ canFly NOTIFY canFlyChanged)
Q_PROPERTY(AircraftType aircraftType READ aircraftType NOTIFY selectedAircraftChanged)
public:
explicit LauncherController(QObject *parent,
LocationWidget* loc);
explicit LauncherController(QObject *parent);
void initQML();
@ -114,8 +116,6 @@ public:
QStringList combinedSummary() const;
QString locationDescription() const;
QString versionString() const;
RecentAircraftModel* aircraftHistory();
@ -144,6 +144,22 @@ public:
void restoreSettings();
void saveSettings();
LocationController* location() const
{ return m_location; }
enum AircraftType
{
Airplane = 0,
Seaplane,
Helicopter,
Airship
};
Q_ENUM(AircraftType)
AircraftType aircraftType() const
{ return m_aircraftType; }
signals:
void selectedAircraftChanged(QUrl selectedAircraft);
@ -195,8 +211,10 @@ private:
AircraftProxyModel* m_aircraftSearchModel;
AircraftProxyModel* m_browseAircraftModel;
MPServersModel* m_serversModel = nullptr;
LocationController* m_location = nullptr;
QUrl m_selectedAircraft;
AircraftType m_aircraftType = Airplane;
int m_ratingFilters[4] = {3, 3, 3, 3};
LaunchConfig* m_config = nullptr;
QmlAircraftInfo* m_selectedAircraftInfo = nullptr;
@ -204,8 +222,6 @@ private:
QStringList m_settingsSummary, m_environmentSummary;
RecentAircraftModel* m_aircraftHistory = nullptr;
RecentLocationsModel* m_locationHistory = nullptr;
LocationWidget* m_locationWidget_FIXME = nullptr;
};
#endif // LAUNCHERCONTROLLER_HXX

View file

@ -44,6 +44,7 @@
#include "DefaultAircraftLocator.hxx"
#include "AddOnsController.hxx"
#include "CatalogListModel.hxx"
#include "LocationController.hxx"
#include "ui_Launcher.h"
@ -69,14 +70,12 @@ LauncherMainWindow::LauncherMainWindow() :
#endif
m_controller = new LauncherController(this, m_ui->location);
m_controller = new LauncherController(this);
m_controller->initQML();
connect(m_controller, &LauncherController::canFlyChanged,
this, &LauncherMainWindow::onCanFlyChanged);
m_ui->location->setLaunchConfig(m_controller->config());
QMenu* toolsMenu = mb->addMenu(tr("Tools"));
QAction* restoreDefaultsAction = toolsMenu->addAction(tr("Restore defaults..."));
connect(restoreDefaultsAction, &QAction::triggered,
@ -143,6 +142,18 @@ LauncherMainWindow::LauncherMainWindow() :
this, &LauncherMainWindow::onQuickStatusChanged);
m_ui->aircraftList->setSource(QUrl("qrc:///qml/AircraftList.qml"));
/////////////
// location
m_ui->location->setResizeMode(QQuickWidget::SizeRootObjectToView);
m_ui->location->engine()->addImportPath("qrc:///");
m_ui->location->engine()->rootContext()->setContextProperty("_launcher", m_controller);
m_ui->location->engine()->rootContext()->setContextProperty("_config", m_controller->config());
m_ui->location->engine()->rootContext()->setContextProperty("_location", m_controller->location());
connect( m_ui->location, &QQuickWidget::statusChanged,
this, &LauncherMainWindow::onQuickStatusChanged);
m_ui->location->setSource(QUrl("qrc:///qml/Location.qml"));
/////////////
// settings
m_ui->settings->engine()->addImportPath("qrc:///");
QQmlContext* settingsContext = m_ui->settings->engine()->rootContext();

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,203 @@
// LocationController.hxx - GUI launcher dialog using Qt5
//
// Written by James Turner, started October 2015.
//
// Copyright (C) 2015 James Turner <zakalawe@mac.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef LOCATION_CONTROLLER_HXX
#define LOCATION_CONTROLLER_HXX
#include <QObjectList>
#include <Navaids/positioned.hxx>
#include <Airports/airports_fwd.hxx>
#include "QtLauncher_fwd.hxx"
#include "LaunchConfig.hxx"
#include "QmlPositioned.hxx"
class NavSearchModel;
class LocationController : public QObject
{
Q_OBJECT
Q_PROPERTY(QString description READ description NOTIFY descriptionChanged)
Q_PROPERTY(NavSearchModel* searchModel MEMBER m_searchModel CONSTANT)
Q_PROPERTY(QList<QObject*> airportRunways READ airportRunways NOTIFY baseLocationChanged)
Q_PROPERTY(QList<QObject*> airportParkings READ airportParkings NOTIFY baseLocationChanged)
Q_PROPERTY(bool offsetEnabled READ offsetEnabled WRITE setOffsetEnabled NOTIFY offsetChanged)
Q_PROPERTY(int offsetRadial READ offsetRadial WRITE setOffsetRadial NOTIFY offsetChanged)
Q_PROPERTY(bool offsetBearingIsTrue MEMBER m_offsetBearingIsTrue NOTIFY offsetChanged)
Q_PROPERTY(double offsetNm READ offsetNm WRITE setOffsetNm NOTIFY offsetChanged)
Q_PROPERTY(int headingDeg MEMBER m_headingDeg NOTIFY configChanged)
Q_PROPERTY(int altitudeFt MEMBER m_altitudeFt NOTIFY configChanged)
Q_PROPERTY(int airspeedKnots MEMBER m_airspeedKnots NOTIFY configChanged)
Q_PROPERTY(bool onFinal READ onFinal WRITE setOnFinal NOTIFY configChanged)
Q_PROPERTY(bool isAirportLocation READ isAirportLocation NOTIFY baseLocationChanged)
Q_PROPERTY(bool useActiveRunway READ useActiveRunway WRITE setUseActiveRunway NOTIFY configChanged)
Q_PROPERTY(bool useAvailableParking READ useAvailableParking WRITE setUseAvailableParking NOTIFY configChanged)
Q_PROPERTY(bool tuneNAV1 READ tuneNAV1 WRITE setTuneNAV1 NOTIFY configChanged)
Q_PROPERTY(QmlGeod baseGeod READ baseGeod WRITE setBaseGeod NOTIFY baseLocationChanged)
Q_PROPERTY(QmlPositioned* detail READ detail CONSTANT)
public:
explicit LocationController(QObject *parent = nullptr);
~LocationController();
void setLaunchConfig(LaunchConfig* config);
QString description() const;
void setBaseLocation(FGPositionedRef ref);
bool shouldStartPaused() const;
void setLocationProperties();
void restoreLocation(QVariantMap l);
QVariantMap saveLocation() const;
void restoreSettings();
/// used to automatically select aircraft state
bool isParkedLocation() const;
/// used to automatically select aircraft state
bool isAirborneLocation() const;
int offsetRadial() const;
double offsetNm() const
{
return m_offsetNm;
}
Q_INVOKABLE void setBaseLocation(QmlPositioned* pos);
Q_INVOKABLE void setDetailLocation(QmlPositioned* pos);
QmlGeod baseGeod() const;
void setBaseGeod(QmlGeod geod);
bool isAirportLocation() const;
bool offsetEnabled() const
{
return m_offsetEnabled;
}
bool onFinal() const
{
return m_onFinal;
}
void setUseActiveRunway(bool b);
bool useActiveRunway() const
{
return m_useActiveRunway;
}
Q_INVOKABLE void addToRecent(QmlPositioned* pos);
QObjectList airportRunways() const;
QObjectList airportParkings() const;
Q_INVOKABLE void showHistoryInSearchModel();
Q_INVOKABLE QmlGeod parseStringAsGeod(QString string) const;
bool tuneNAV1() const
{
return m_tuneNAV1;
}
QmlPositioned* detail() const;
bool useAvailableParking() const
{
return m_useAvailableParking;
}
public slots:
void setOffsetRadial(int offsetRadial);
void setOffsetNm(double offsetNm);
void setOffsetEnabled(bool offsetEnabled);
void setOnFinal(bool onFinal);
void setTuneNAV1(bool tuneNAV1);
void setUseAvailableParking(bool useAvailableParking);
Q_SIGNALS:
void descriptionChanged();
void offsetChanged();
void baseLocationChanged();
void configChanged();
private Q_SLOTS:
void onCollectConfig();
private:
void onSearchComplete();
void onAirportRunwayClicked(FGRunwayRef rwy);
void onAirportParkingClicked(FGParkingRef park);
void addToRecent(FGPositionedRef pos);
void setNavRadioOption();
void applyPositionOffset();
NavSearchModel* m_searchModel = nullptr;
FGPositionedRef m_location;
FGAirportRef m_airportLocation; // valid if m_location is an FGAirport
FGPositionedRef m_detailLocation; // parking stand or runway detail
bool m_locationIsLatLon = false;
SGGeod m_geodLocation;
FGPositionedList m_recentLocations;
LaunchConfig* m_config = nullptr;
QmlPositioned* m_detailQml = nullptr;
bool m_offsetEnabled = false;
int m_offsetRadial = 0;
double m_offsetNm = 0.0;
bool m_offsetBearingIsTrue = false;
int m_headingDeg = 0;
int m_altitudeFt= -9999;
int m_airspeedKnots = 120;
bool m_onFinal = false;
bool m_useActiveRunway = true;
bool m_tuneNAV1;
bool m_useAvailableParking;
};
#endif // LOCATION_CONTROLLER_HXX

File diff suppressed because it is too large Load diff

View file

@ -1,117 +0,0 @@
// LocationWidget.hxx - GUI launcher dialog using Qt5
//
// Written by James Turner, started October 2015.
//
// Copyright (C) 2015 James Turner <zakalawe@mac.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef LOCATIONWIDGET_H
#define LOCATIONWIDGET_H
#include <QWidget>
#include <QToolButton>
#include <Navaids/positioned.hxx>
#include <Airports/airports_fwd.hxx>
#include "LaunchConfig.hxx"
#include "QtLauncher_fwd.hxx"
namespace Ui {
class LocationWidget;
}
class NavSearchModel;
class LocationWidget : public QWidget
{
Q_OBJECT
public:
explicit LocationWidget(QWidget *parent = 0);
~LocationWidget();
void setLaunchConfig(LaunchConfig* config);
QString locationDescription() const;
void setBaseLocation(FGPositionedRef ref);
void setAircraftType(LauncherAircraftType ty);
bool shouldStartPaused() const;
void setLocationProperties();
void restoreLocation(QVariantMap l);
QVariantMap saveLocation() const;
void restoreSettings();
/// used to automatically select aircraft state
bool isParkedLocation() const;
/// used to automatically select aircraft state
bool isAirborneLocation() const;
Q_SIGNALS:
void descriptionChanged(QString t);
private Q_SLOTS:
void updateDescription();
void onLocationChanged();
void onOffsetDataChanged();
void onHeadingChanged();
void onCollectConfig();
private:
void onSearch();
void onSearchResultSelected(const QModelIndex& index);
void onSearchComplete();
void onAirportRunwayClicked(FGRunwayRef rwy);
void onAirportParkingClicked(FGParkingRef park);
void onAirportHelipadClicked(FGHelipadRef pad);
void onOffsetBearingTrueChanged(bool on);
void addToRecent(FGPositionedRef pos);
void onOffsetEnabledToggled(bool on);
void onBackToSearch();
void setNavRadioOption();
void onShowHistory();
void applyPositionOffset();
Ui::LocationWidget *m_ui;
NavSearchModel* m_searchModel;
FGPositionedRef m_location;
bool m_locationIsLatLon;
SGGeod m_geodLocation;
QToolButton* m_backButton;
FGPositionedList m_recentLocations;
LauncherAircraftType m_aircraftType;
LaunchConfig* m_config = nullptr;
};
#endif // LOCATIONWIDGET_H

View file

@ -1,442 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LocationWidget</class>
<widget class="QWidget" name="LocationWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>864</width>
<height>683</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QStackedWidget" name="stack">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="airportPage">
<layout class="QGridLayout" name="gridLayout" columnstretch="1,0,0,0">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="5" column="2">
<widget class="QSpinBox" name="approachDistanceSpin">
<property name="suffix">
<string>nm</string>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QRadioButton" name="parkingRadio">
<property name="text">
<string>Parking:</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QRadioButton" name="runwayRadio">
<property name="text">
<string>Runway:</string>
</property>
</widget>
</item>
<item row="6" column="1" colspan="3">
<widget class="QComboBox" name="parkingCombo"/>
</item>
<item row="5" column="3">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="1" colspan="3">
<widget class="QComboBox" name="runwayCombo"/>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="onFinalCheckbox">
<property name="text">
<string>On final approach at distance:</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="4">
<widget class="QLabel" name="titleLabel">
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="3" column="0" colspan="4">
<widget class="AirportDiagram" name="airportDiagram" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="navaidPage">
<layout class="QGridLayout" name="gridLayout_3" rowstretch="0,1,0,0,0,0" columnstretch="1,0,0,1,0,1,0,0,0,0">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="4" column="6" colspan="2">
<widget class="QSpinBox" name="altitudeSpinbox">
<property name="suffix">
<string>ft</string>
</property>
<property name="minimum">
<number>-1000</number>
</property>
<property name="maximum">
<number>120000</number>
</property>
<property name="singleStep">
<number>50</number>
</property>
<property name="value">
<number>5000</number>
</property>
</widget>
</item>
<item row="4" column="8" colspan="2">
<widget class="QComboBox" name="altitudeModeCombo">
<item>
<property name="text">
<string>Barometric altitude (ASL)</string>
</property>
</item>
<item>
<property name="text">
<string>Above ground (AGL)</string>
</property>
</item>
<item>
<property name="text">
<string>Flight Level (FL)</string>
</property>
</item>
</widget>
</item>
<item row="4" column="5">
<widget class="QLabel" name="altitudeLabel">
<property name="text">
<string>Altitude:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="5" column="0" colspan="10">
<widget class="QGroupBox" name="offsetGroup">
<property name="title">
<string>Offset</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,0,0,1,0">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<widget class="QLabel" name="offsetBearingLabel">
<property name="text">
<string>Bearing:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="offsetBearingSpinbox">
<property name="wrapping">
<bool>true</bool>
</property>
<property name="maximum">
<number>359</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="trueBearing">
<property name="text">
<string>True</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="offsetDistanceLabel">
<property name="text">
<string>Distance:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="offsetNmSpinbox">
<property name="suffix">
<string>nm</string>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="value">
<double>10.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="aispeedLabel">
<property name="text">
<string>Airspeed:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QLabel" name="label">
<property name="text">
<string>Heading:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="4" column="4">
<widget class="QSpinBox" name="headingSpinbox">
<property name="wrapping">
<bool>true</bool>
</property>
<property name="maximum">
<number>359</number>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2">
<widget class="QSpinBox" name="airspeedSpinbox">
<property name="suffix">
<string>kts</string>
</property>
<property name="maximum">
<number>9999</number>
</property>
<property name="value">
<number>120</number>
</property>
</widget>
</item>
<item row="0" column="0" colspan="10">
<widget class="QLabel" name="navTitleLabel">
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="0" colspan="10">
<widget class="NavaidDiagram" name="navaidDiagram" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>200</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="searchPage">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="searchLabel">
<property name="text">
<string>Search:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="locationSearchEdit">
<property name="placeholderText">
<string>Enter an ICAO code, navaid or name, then press Enter</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="searchHistory">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
<item>
<widget class="QLabel" name="searchStatusText">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="searchIcon">
<property name="minimumSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignHCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QListView" name="searchResultsList"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AirportDiagram</class>
<extends>QWidget</extends>
<header location="global">GUI/AirportDiagram.hxx</header>
<container>1</container>
</customwidget>
<customwidget>
<class>NavaidDiagram</class>
<extends>QWidget</extends>
<header location="global">GUI/NavaidDiagram.hxx</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View file

@ -27,7 +27,9 @@
#include <QVector2D>
#include <QMouseEvent>
NavaidDiagram::NavaidDiagram(QWidget* pr) :
#include <Navaids/NavDataCache.hxx>
NavaidDiagram::NavaidDiagram(QQuickItem* pr) :
BaseDiagram(pr),
m_offsetEnabled(false),
m_offsetDistanceNm(5.0),
@ -37,12 +39,28 @@ NavaidDiagram::NavaidDiagram(QWidget* pr) :
}
void NavaidDiagram::setNavaid(FGPositionedRef nav)
void NavaidDiagram::setNavaid(qlonglong nav)
{
m_navaid = nav;
m_projectionCenter = nav ? nav->geod() : SGGeod();
m_geod = nav->geod();
m_navaid = fgpositioned_cast<FGNavRecord>(flightgear::NavDataCache::instance()->loadById(nav));
m_projectionCenter = m_navaid ? m_navaid->geod() : SGGeod();
m_geod = m_projectionCenter;
recomputeBounds(true);
emit locationChanged();
}
qlonglong NavaidDiagram::navaid() const
{
return m_navaid->guid();
}
void NavaidDiagram::setGeod(QmlGeod geod)
{
setGeod(geod.geod());
}
QmlGeod NavaidDiagram::geod() const
{
return QmlGeod(m_geod);
}
void NavaidDiagram::setGeod(const SGGeod &geod)
@ -51,6 +69,7 @@ void NavaidDiagram::setGeod(const SGGeod &geod)
m_geod = geod;
m_projectionCenter = m_geod;
recomputeBounds(true);
emit locationChanged();
}
void NavaidDiagram::setOffsetEnabled(bool offset)
@ -59,24 +78,28 @@ void NavaidDiagram::setOffsetEnabled(bool offset)
return;
m_offsetEnabled = offset;
recomputeBounds(true);
emit offsetChanged();
}
void NavaidDiagram::setOffsetDistanceNm(double distanceNm)
{
m_offsetDistanceNm = distanceNm;
update();
emit offsetChanged();
}
void NavaidDiagram::setOffsetBearingDeg(int bearingDeg)
{
m_offsetBearingDeg = bearingDeg;
update();
emit offsetChanged();
}
void NavaidDiagram::setHeadingDeg(int headingDeg)
{
m_headingDeg = headingDeg;
update();
emit offsetChanged();
}
void NavaidDiagram::paintContents(QPainter *painter)

View file

@ -22,7 +22,7 @@
#define GUI_NAVAID_DIAGRAM_HXX
#include "BaseDiagram.hxx"
#include <QPainterPath>
#include "QmlPositioned.hxx"
#include <Navaids/navrecord.hxx>
#include <simgear/math/sg_geodesy.hxx>
@ -30,28 +30,50 @@
class NavaidDiagram : public BaseDiagram
{
Q_OBJECT
public:
NavaidDiagram(QWidget* pr);
void setNavaid(FGPositionedRef nav);
Q_PROPERTY(qlonglong navaid READ navaid WRITE setNavaid NOTIFY locationChanged)
Q_PROPERTY(QmlGeod geod READ geod WRITE setGeod NOTIFY locationChanged)
Q_PROPERTY(int headingDeg READ headingDeg WRITE setHeadingDeg NOTIFY offsetChanged)
Q_PROPERTY(int offsetBearingDeg READ offsetBearingDeg WRITE setOffsetBearingDeg NOTIFY offsetChanged)
Q_PROPERTY(bool offsetEnabled READ isOffsetEnabled WRITE setOffsetEnabled NOTIFY offsetChanged)
Q_PROPERTY(double offsetDistanceNm READ offsetDistanceNm WRITE setOffsetDistanceNm NOTIFY offsetChanged)
public:
NavaidDiagram(QQuickItem* pr = nullptr);
void setNavaid(qlonglong nav);
qlonglong navaid() const;
void setGeod(QmlGeod geod);
QmlGeod geod() const;
void setGeod(const SGGeod& geod);
bool isOffsetEnabled() const;
bool isOffsetEnabled() const
{ return m_offsetEnabled; }
void setOffsetEnabled(bool offset);
void setOffsetDistanceNm(double distanceNm);
double offsetDistanceNm() const;
double offsetDistanceNm() const
{ return m_offsetDistanceNm; }
void setOffsetBearingDeg(int bearingDeg);
int offsetBearingDeg() const;
int offsetBearingDeg() const
{ return m_offsetBearingDeg; }
void setHeadingDeg(int headingDeg);
void headingDeg() const;
protected:
void paintContents(QPainter *) Q_DECL_OVERRIDE;
int headingDeg() const
{ return m_headingDeg; }
void doComputeBounds() Q_DECL_OVERRIDE;
signals:
void locationChanged();
void offsetChanged();
protected:
void paintContents(QPainter *) override;
void doComputeBounds() override;
private:
FGPositionedRef m_navaid;
SGGeod m_geod;

View file

@ -0,0 +1,50 @@
// PixmapImageItem.cxx - display a QPixmap/QImage directly
//
// Written by James Turner, started April 2018.
//
// Copyright (C) 2018 James Turner <james@flightgear.org>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "PixmapImageItem.hxx"
#include <QPainter>
PixmapImageItem::PixmapImageItem(QQuickItem* parent) :
QQuickPaintedItem(parent)
{
}
void PixmapImageItem::paint(QPainter *painter)
{
if (_image.isNull())
return;
QRect rect(0, 0, width(), height());
painter->drawImage(rect, _image);
}
void PixmapImageItem::setImage(QImage img)
{
if (img == _image)
return;
_image = img;
update();
const auto sz = img.size();
setImplicitSize(sz.width(), sz.height());
emit imageChanged();
}

View file

@ -0,0 +1,50 @@
// PixmapImageItem.hxx - display a QPixmap/QImage directly
//
// Written by James Turner, started April 2018.
//
// Copyright (C) 2018 James Turner <james@flightgear.org>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef PIXMAPIMAGEITEM_HXX
#define PIXMAPIMAGEITEM_HXX
#include <QQuickPaintedItem>
#include <QImage>
class PixmapImageItem : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QImage image READ image WRITE setImage NOTIFY imageChanged)
public:
PixmapImageItem(QQuickItem* parent = nullptr);
void paint(QPainter* painter) override;
QImage image() const
{ return _image; }
void setImage(QImage img);
signals:
void imageChanged();
private:
QImage _image;
};
#endif // PIXMAPIMAGEITEM_HXX

View file

@ -0,0 +1,26 @@
// QmlNavCacheWrapper.cxx - Expose NavData to Qml
//
// Written by James Turner, started April 2018.
//
// Copyright (C) 2018 James Turner <james@flightgear.org>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "QmlNavCacheWrapper.hxx"
QmlNavCacheWrapper::QmlNavCacheWrapper()
{
}

View file

@ -0,0 +1,38 @@
// QmlNavCacheWrapper.hxx - Expose NavData to Qml
//
// Written by James Turner, started April 2018.
//
// Copyright (C) 2018 James Turner <james@flightgear.org>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef QMLNAVCACHEWRAPPER_HXX
#define QMLNAVCACHEWRAPPER_HXX
#include <QObject>
/**
* @brief QmlNavCacheWrapper wraps and exposes NavData to Qml as singleton
* service
*/
class QmlNavCacheWrapper : public QObject
{
Q_OBJECT
public:
QmlNavCacheWrapper();
};
#endif // QMLNAVCACHEWRAPPER_HXX

317
src/GUI/QmlPositioned.cxx Normal file
View file

@ -0,0 +1,317 @@
// QmlPositioned.cxx - Expose NavData to Qml
//
// Written by James Turner, started April 2018.
//
// Copyright (C) 2018 James Turner <james@flightgear.org>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "QmlPositioned.hxx"
#include <QDebug>
#include <Navaids/NavDataCache.hxx>
#include <Navaids/navrecord.hxx>
#include <Airports/airport.hxx>
#include <Airports/groundnetwork.hxx>
#include <Airports/runways.hxx>
using namespace flightgear;
QmlGeod::QmlGeod() :
m_data(SGGeod::fromDeg(-9999.0, -9999.0))
{
}
QmlGeod::QmlGeod(const SGGeod &geod) :
m_data(geod)
{
}
double QmlGeod::latitudeDeg() const
{
return m_data.getLatitudeDeg();
}
double QmlGeod::longitudeDeg() const
{
return m_data.getLongitudeDeg();
}
double QmlGeod::latitudeRad() const
{
return m_data.getLatitudeRad();
}
double QmlGeod::longitudeRad() const
{
return m_data.getLongitudeRad();
}
double QmlGeod::elevationFt() const
{
return m_data.getElevationFt();
}
bool QmlGeod::valid() const
{
return m_data.isValid();
}
double QmlGeod::elevationM() const
{
return m_data.getElevationM();
}
void QmlGeod::setLatitudeDeg(double latitudeDeg)
{
if (qFuzzyCompare(m_data.getLatitudeDeg(), latitudeDeg))
return;
m_data.setLatitudeDeg(latitudeDeg);
// emit latitudeChanged();
}
void QmlGeod::setLongitudeDeg(double longitudeDeg)
{
if (qFuzzyCompare(m_data.getLongitudeDeg(), longitudeDeg))
return;
m_data.setLongitudeDeg(longitudeDeg);
// emit longitudeChanged();
}
void QmlGeod::setLatitudeRad(double latitudeRad)
{
if (qFuzzyCompare(m_data.getLatitudeRad(), latitudeRad))
return;
m_data.setLatitudeRad(latitudeRad);
// emit latitudeChanged();
}
void QmlGeod::setLongitudeRad(double longitudeRad)
{
if (qFuzzyCompare(m_data.getLongitudeRad(), longitudeRad))
return;
m_data.setLongitudeRad(longitudeRad);
// emit longitudeChanged();
}
void QmlGeod::setElevationM(double elevationM)
{
if (qFuzzyCompare(m_data.getElevationM(), elevationM))
return;
m_data.setElevationM(elevationM);
// emit elevationChanged();
}
void QmlGeod::setElevationFt(double elevationFt)
{
if (qFuzzyCompare(m_data.getElevationFt(), elevationFt))
return;
m_data.setElevationFt(elevationFt);
// emit elevationChanged();
}
/////////////////////////////////////////////////////////////////////////////////
QmlPositioned::QmlPositioned(QObject *parent) : QObject(parent)
{
}
QmlPositioned::QmlPositioned(FGPositionedRef p) :
m_pos(p)
{
}
void QmlPositioned::setInner(FGPositionedRef p)
{
if (!p) {
m_pos.clear();
} else {
m_pos = p;
}
}
FGPositionedRef QmlPositioned::inner() const
{
return m_pos;
}
bool QmlPositioned::valid() const
{
return m_pos.valid();
}
QString QmlPositioned::ident() const
{
if (!m_pos)
return {};
return QString::fromStdString(m_pos->ident());
}
QString QmlPositioned::name() const
{
if (!m_pos)
return {};
return QString::fromStdString(m_pos->name());
}
qlonglong QmlPositioned::guid() const
{
if (!m_pos)
return 0;
return m_pos->guid();
}
QmlPositioned::Type QmlPositioned::type() const
{
if (!m_pos)
return Invalid;
return static_cast<Type>(m_pos->type());
}
void QmlPositioned::setGuid(qlonglong guid)
{
if (m_pos && (m_pos->guid()) == guid)
return;
m_pos = NavDataCache::instance()->loadById(guid);
emit guidChanged();
emit infoChanged();
}
bool QmlPositioned::isAirportType() const
{
return FGPositioned::isAirportType(m_pos.get());
}
bool QmlPositioned::isRunwayType() const
{
return FGPositioned::isRunwayType(m_pos.get());
}
bool QmlPositioned::isNavaidType() const
{
return FGPositioned::isNavaidType(m_pos.get());
}
QmlGeod* QmlPositioned::geod() const
{
if (!m_pos)
return nullptr;
return new QmlGeod(m_pos->geod());
}
double QmlPositioned::navaidFrequencyMHz() const
{
FGNavRecord* nav = fgpositioned_cast<FGNavRecord>(m_pos);
if (!nav)
return 0.0;
qWarning() << Q_FUNC_INFO << "check me!";
return nav->get_freq() / 1000.0;
}
double QmlPositioned::navaidRangeNm() const
{
FGNavRecord* nav = fgpositioned_cast<FGNavRecord>(m_pos);
if (!nav)
return 0.0;
return nav->get_range();
}
QmlPositioned *QmlPositioned::navaidRunway() const
{
FGNavRecord* nav = fgpositioned_cast<FGNavRecord>(m_pos);
if (!nav || !nav->runway())
return nullptr;
return new QmlPositioned(nav->runway());
}
QmlPositioned *QmlPositioned::colocatedDME() const
{
FGNavRecord* nav = fgpositioned_cast<FGNavRecord>(m_pos);
if (!nav || !nav->hasDME())
return nullptr;
return new QmlPositioned(flightgear::NavDataCache::instance()->loadById(nav->colocatedDME()));
}
QmlPositioned *QmlPositioned::owningAirport() const
{
FGRunway* runway = fgpositioned_cast<FGRunway>(m_pos);
if (runway) {
return new QmlPositioned(runway->airport());
}
return nullptr;
}
double QmlPositioned::runwayHeadingDeg() const
{
FGRunway* runway = fgpositioned_cast<FGRunway>(m_pos);
if (runway) {
return runway->headingDeg();
}
return 0.0;
}
double QmlPositioned::runwayLengthFt() const
{
FGRunway* runway = fgpositioned_cast<FGRunway>(m_pos);
if (runway) {
return runway->lengthFt();
}
return 0.0;
}
bool QmlPositioned::equals(QmlPositioned *other) const
{
return (other && (other->inner() == inner()));
}
bool QmlPositioned::airportHasParkings() const
{
if (!isAirportType())
return false;
FGAirport* apt = fgpositioned_cast<FGAirport>(m_pos);
if (!apt)
return false;
return !apt->groundNetwork()->allParkings().empty();
}
bool operator==(const QmlPositioned& p1, const QmlPositioned& p2)
{
return p1.inner() == p2.inner();
}

186
src/GUI/QmlPositioned.hxx Normal file
View file

@ -0,0 +1,186 @@
// QmlPositioned.hxx - Expose NavData to Qml
//
// Written by James Turner, started April 2018.
//
// Copyright (C) 2018 James Turner <james@flightgear.org>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef QMLPOSITIONED_HXX
#define QMLPOSITIONED_HXX
#include <QObject>
#include <Navaids/positioned.hxx>
/**
* @brief Expose an SGGeod as Qml-friendly class
*/
class QmlGeod
{
Q_GADGET
Q_PROPERTY(double latitudeDeg READ latitudeDeg WRITE setLatitudeDeg)
Q_PROPERTY(double longitudeDeg READ longitudeDeg WRITE setLongitudeDeg)
Q_PROPERTY(double latitudeRad READ latitudeRad WRITE setLatitudeRad)
Q_PROPERTY(double longitudeRad READ longitudeRad WRITE setLongitudeRad)
Q_PROPERTY(double elevationM READ elevationM WRITE setElevationM)
Q_PROPERTY(double elevationFt READ elevationFt WRITE setElevationFt)
Q_PROPERTY(bool valid READ valid)
public:
QmlGeod();
QmlGeod(const SGGeod& geod);
SGGeod geod() const
{ return m_data; }
double latitudeDeg() const;
double longitudeDeg() const;
double latitudeRad() const;
double longitudeRad() const;
double elevationM() const;
double elevationFt() const;
bool valid() const;
public slots:
void setLatitudeDeg(double latitudeDeg);
void setLongitudeDeg(double longitudeDeg);
void setLatitudeRad(double latitudeRad);
void setLongitudeRad(double longitudeRad);
void setElevationM(double elevationM);
void setElevationFt(double elevationFt);
private:
SGGeod m_data;
};
Q_DECLARE_METATYPE(QmlGeod)
class QmlPositioned : public QObject
{
Q_OBJECT
public:
explicit QmlPositioned(QObject *parent = nullptr);
explicit QmlPositioned(FGPositionedRef p);
// proxy FGPositioned type values
enum Type {
Invalid = FGPositioned::INVALID,
Airport = FGPositioned::AIRPORT,
Heliport = FGPositioned::HELIPORT,
Seaport = FGPositioned::SEAPORT,
Runway = FGPositioned::RUNWAY,
Helipad = FGPositioned::HELIPAD,
Taxiway = FGPositioned::TAXIWAY,
Pavement = FGPositioned::PAVEMENT,
Waypoint = FGPositioned::WAYPOINT,
Fix = FGPositioned::FIX,
NDB = FGPositioned::NDB,
VOR = FGPositioned::VOR,
ILS = FGPositioned::ILS,
Localizer = FGPositioned::LOC,
Glideslope = FGPositioned::GS,
OuterMarker = FGPositioned::OM,
MiddleMarker = FGPositioned::MM,
InnerMarker = FGPositioned::IM,
DME = FGPositioned::DME,
TACAN = FGPositioned::TACAN,
MobileTACAN = FGPositioned::MOBILE_TACAN,
Tower = FGPositioned::TOWER,
Parking = FGPositioned::PARKING,
Country = FGPositioned::COUNTRY,
City = FGPositioned::CITY,
Town = FGPositioned::TOWN,
Village = FGPositioned::VILLAGE
};
Q_ENUM(Type)
Q_PROPERTY(QString ident READ ident NOTIFY infoChanged)
Q_PROPERTY(QString name READ name NOTIFY infoChanged)
Q_PROPERTY(Type type READ type NOTIFY infoChanged)
Q_PROPERTY(bool valid READ valid NOTIFY infoChanged)
Q_PROPERTY(QmlGeod* geod READ geod NOTIFY infoChanged)
Q_PROPERTY(qlonglong guid READ guid WRITE setGuid NOTIFY guidChanged)
Q_PROPERTY(bool isAirportType READ isAirportType NOTIFY infoChanged)
Q_PROPERTY(bool isRunwayType READ isRunwayType NOTIFY infoChanged)
Q_PROPERTY(bool isNavaidType READ isNavaidType NOTIFY infoChanged)
Q_PROPERTY(double navaidFrequencyMHz READ navaidFrequencyMHz NOTIFY infoChanged)
Q_PROPERTY(double navaidRangeNm READ navaidRangeNm NOTIFY infoChanged)
Q_PROPERTY(QmlPositioned* colocatedDME READ colocatedDME NOTIFY infoChanged)
Q_PROPERTY(QmlPositioned* navaidRunway READ navaidRunway NOTIFY infoChanged)
Q_PROPERTY(QmlPositioned* owningAirport READ owningAirport NOTIFY infoChanged)
Q_PROPERTY(double runwayHeadingDeg READ runwayHeadingDeg NOTIFY infoChanged)
Q_PROPERTY(double runwayLengthFt READ runwayLengthFt NOTIFY infoChanged)
Q_PROPERTY(bool airportHasParkings READ airportHasParkings NOTIFY infoChanged)
void setInner(FGPositionedRef p);
FGPositionedRef inner() const;
bool valid() const;
QString ident() const;
QString name() const;
qlonglong guid() const;
Type type() const;
bool isAirportType() const;
bool isRunwayType() const;
bool isNavaidType() const;
QmlGeod* geod() const;
double navaidFrequencyMHz() const;
double navaidRangeNm() const;
QmlPositioned* navaidRunway() const;
QmlPositioned* colocatedDME() const;
// owning airport if one exists
QmlPositioned* owningAirport() const;
double runwayHeadingDeg() const;
double runwayLengthFt() const;
Q_INVOKABLE bool equals(QmlPositioned* other) const;
bool airportHasParkings() const;
public slots:
void setGuid(qlonglong guid);
signals:
void guidChanged();
void infoChanged();
private:
FGPositionedRef m_pos;
};
bool operator==(const QmlPositioned& p1, const QmlPositioned& p2);
#endif // QMLPOSITIONED_HXX

View file

@ -1,15 +1,11 @@
#ifndef QTGUI_FWD_H
#define QTGUI_FWD_H
enum LauncherAircraftType
{
Airplane = 0,
Seaplane,
Helicopter,
Airship
};
#include <QString>
extern QString fixNavaidName(QString s);
#endif // QTGUI_FWD_H

View file

@ -15,6 +15,7 @@ FocusScope {
property alias prefix: prefix.text
property alias maxDigits: edit.maximumLength
property int step: 1
property bool live: false
implicitHeight: editFrame.height
// we have a margin between the frame and the label, and on each
@ -25,8 +26,11 @@ FocusScope {
function incrementValue()
{
if (edit.activeFocus) {
value = Math.min(parseFloat(edit.text) + root.step, root.max)
edit.text = value
var newValue = Math.min(parseFloat(edit.text) + root.step, root.max)
edit.text = newValue
if (live) {
commit(newValue);
}
} else {
commit(Math.min(value + root.step, root.max))
}
@ -35,8 +39,11 @@ FocusScope {
function decrementValue()
{
if (edit.activeFocus) {
value = Math.max(parseFloat(edit.text) - root.step, root.min)
edit.text = value
var newValue = Math.max(parseFloat(edit.text) - root.step, root.min)
edit.text = newValue
if (live) {
commit(newValue);
}
} else {
commit(Math.max(value - root.step, root.min))
}
@ -75,6 +82,17 @@ FocusScope {
}
}
// timer to commit the value when in live mode
Timer {
id: liveEditTimer
interval: 800
onTriggered: {
if (edit.activeFocus) {
commit(parseInt(edit.text));
}
}
}
Binding {
when: !edit.activeFocus
target: edit
@ -133,6 +151,13 @@ FocusScope {
selectAll();
} else {
commit(parseFloat(text))
liveEditTimer.stop();
}
}
onTextChanged: {
if (activeFocus && root.live) {
liveEditTimer.restart();
}
}
}

View file

@ -13,6 +13,7 @@ FocusScope {
property alias prefix: prefix.text
property alias maxDigits: edit.maximumLength
property int step: 1
property bool live: false
implicitHeight: editFrame.height
// we have a margin between the frame and the label, and on each
@ -23,8 +24,11 @@ FocusScope {
function incrementValue()
{
if (edit.activeFocus) {
value = Math.min(parseInt(edit.text) + root.step, root.max)
edit.text = value
var newValue = Math.min(parseInt(edit.text) + root.step, root.max)
edit.text = newValue
if (live) {
commit(newValue);
}
} else {
commit(Math.min(value + root.step, root.max))
}
@ -33,8 +37,11 @@ FocusScope {
function decrementValue()
{
if (edit.activeFocus) {
value = Math.max(parseInt(edit.text) - root.step, root.min)
edit.text = value
var newValue = Math.max(parseInt(edit.text) - root.step, root.min)
edit.text = newValue
if (live) {
commit(newValue);
}
} else {
commit(Math.max(value - root.step, root.min))
}
@ -73,6 +80,17 @@ FocusScope {
}
}
// timer to commit the value when in live mode
Timer {
id: liveEditTimer
interval: 800
onTriggered: {
if (edit.activeFocus) {
commit(parseInt(edit.text));
}
}
}
Binding {
when: !edit.activeFocus
target: edit
@ -131,6 +149,13 @@ FocusScope {
selectAll();
} else {
commit(parseInt(text))
liveEditTimer.stop();
}
}
onTextChanged: {
if (activeFocus && root.live) {
liveEditTimer.restart();
}
}
}

248
src/GUI/qml/Location.qml Normal file
View file

@ -0,0 +1,248 @@
import QtQuick 2.4
import FlightGear 1.0
import FlightGear.Launcher 1.0
import "."
Item {
id: root
property bool __searchActive: false
property string lastSearch
function backToSearch()
{
detailLoader.sourceComponent = null
}
function selectLocation(guid, type)
{
selectedLocation.guid = guid;
_location.setBaseLocation(selectedLocation)
_location.addToRecent(selectedLocation);
if (selectedLocation.isAirportType) {
detailLoader.sourceComponent = airportDetails
} else {
detailLoader.sourceComponent = navaidDetails
}
}
Component.onCompleted: {
_location.showHistoryInSearchModel()
}
Positioned {
id: selectedLocation
}
Component {
id: airportDetails
LocationAirportView {
id: airportView
}
}
Component {
id: navaidDetails
LocationNavaidView {
id: navaidView
}
}
Rectangle {
anchors.fill: parent
color: "white"
}
Component {
id: locationSearchDelegate
Rectangle {
id: delegateRoot
height: delegateContent.height + Style.margin
width: searchView.width
function itemDescription()
{
if (model.type === Positioned.Fix) return model.ident
if (model.type === Positioned.VOR) {
var freq = (model.frequency / 100).toFixed(3);
return "%1 - %2 (%3 MHz)".arg(model.ident).arg(model.name).arg(freq);
}
// general case
return "%1 - %2".arg(model.ident).arg(model.name);
}
Item {
id: delegateContent
height: Math.max(delegateIcon.height, delegateText.height)
width: parent.width
Rectangle {
visible: delegateMouse.containsMouse
color: "#cfcfcf"
}
PixmapImage {
id: delegateIcon
anchors.left: parent.left
anchors.leftMargin: Style.margin
anchors.verticalCenter: parent.verticalCenter
image: model.icon
}
Text {
id: delegateText
anchors.right: parent.right
anchors.left: delegateIcon.right
anchors.rightMargin: Style.margin
anchors.leftMargin: Style.margin
anchors.verticalCenter: parent.verticalCenter
text: delegateRoot.itemDescription();
wrapMode: Text.WordWrap
}
MouseArea {
id: delegateMouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
root.selectLocation(model.guid, model.type);
}
}
}
Item {
id: footer
height: Style.margin
width: parent.width
anchors.bottom: parent.bottom
Rectangle {
color: Style.frameColor
height: 1
width: parent.width - Style.strutSize
anchors.centerIn: parent
}
}
}
}
SearchButton {
id: searchButton
anchors.right: parent.right
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: Style.margin
autoSubmit: false
placeholder: qsTr("Enter a navaid or airport ID, name or a latitude & longitude");
onSearch: {
// when th search term is cleared, show the history
if (term == "") {
_location.showHistoryInSearchModel();
return;
}
var geod = _location.parseStringAsGeod(term)
if (geod.valid) {
console.info("REMOVE-ME: Setting lat-lon location")
_location.baseGeod = geod
selectedLocation.guid = 0;
detailLoader.sourceComponent = navaidDetails
return;
}
root.lastSearch = term;
_location.searchModel.setSearch(term, _launcher.aircraftType)
}
}
Rectangle {
id: headerSplit
color: Style.frameColor
height: 1
width: parent.width - Style.inset
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: searchButton.bottom
anchors.topMargin: Style.margin
}
ListView {
id: searchView
anchors.top: headerSplit.bottom
anchors.topMargin: Style.margin
width: parent.width
anchors.bottom: parent.bottom
model: _location.searchModel
delegate: locationSearchDelegate
clip: true
header: Item {
visible: _location.searchModel.isSearchActive
width: parent.width
height: visible ? 50 : 0
Text {
text: qsTr("Searching")
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.horizontalCenter
}
AnimatedImage {
source: "qrc://linear-spinner"
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.horizontalCenter
}
}
footer: Item {
width: parent.width
height: noResultsText.height
visible: (parent.count === 0) && !_location.searchModel.isSearchActive
Text {
id: noResultsText
width: parent.width
text: qsTr("No results for found search '%1'").arg(root.lastSearch)
wrapMode: Text.WordWrap
}
}
}
// scrollbar
Loader {
id: detailLoader
anchors.fill: parent
visible: sourceComponent != null
onStatusChanged: {
if (status == Loader.Ready) {
if (selectedLocation.valid) {
item.location = selectedLocation.guid
} else {
// lon-lat
item.geod = _location.baseGeod
}
}
}
}
Button {
anchors { left: parent.left; top: parent.top; margins: Style.margin }
width: Style.strutSize
visible: detailLoader.visible
id: backButton
text: "< Back"
onClicked: {
root.backToSearch();
}
}
}

View file

@ -0,0 +1,278 @@
import QtQuick 2.4
import FlightGear 1.0
import FlightGear.Launcher 1.0
import "."
Item {
id: root
property alias location: airportData.guid
Positioned {
id: airportData
onGuidChanged: _location.setBaseLocation(this)
}
readonly property bool isHeliport: airportData.type === Positioned.Heliport
readonly property bool haveParking: airportData.airportHasParkings
AirportDiagram {
id: diagram
anchors.fill: parent
airport: airportData.guid
onClicked: {
if (pos === null)
return;
_location.setDetailLocation(pos)
diagram.selection = pos
syncUIFromController();
}
approachExtensionNm: _location.onFinal ? _location.offsetNm : -1.0
}
// not very declarative, try to remove this over time
function syncUIFromController()
{
runwayRadio.selected = (_location.detail.isRunwayType);
parkingRadio.selected = (_location.detail.type == Positioned.Parking);
if (_location.detail.isRunwayType) {
runwayChoice.syncCurrentIndex();
} else if (_location.detail.type == Positioned.Parking) {
parkingChoice.syncCurrentIndex();
}
}
RadioButtonGroup {
id: radioGroup
}
Component.onCompleted: {
syncUIFromController();
}
Rectangle {
id: panel
color: "transparent"
border.width: 1
border.color: Style.frameColor
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
margins: Style.strutSize
}
height: selectionGrid.height + Style.margin * 2
// set opacity here only, so we don't make the whole summary pannel translucent
Rectangle {
id: background
anchors.fill: parent
z: -1
opacity: Style.panelOpacity
color: "white"
}
Column {
id: selectionGrid
spacing: Style.margin
width: parent.width
Text { // heading text
id: airportHeading
width: parent.width
text: isHeliport ? qsTr("Heliport: ") + airportData.ident + " / " + airportData.name
: qsTr("Airport: ") + airportData.ident + " / " + airportData.name
}
Row {
width: parent.width
spacing: Style.margin
RadioButton {
id: runwayRadio
anchors.verticalCenter: parent.verticalCenter
group: radioGroup
onClicked: {
if (selected) runwayChoice.setLocation();
}
}
Text {
text: isHeliport ? qsTr("Pad") : qsTr("Runway")
anchors.verticalCenter: parent.verticalCenter
}
PopupChoice {
id: runwayChoice
model: _location.airportRunways
displayRole: "ident"
width: parent.width * 0.5
anchors.verticalCenter: parent.verticalCenter
headerText: qsTr("Active")
enabled: runwayRadio.selected
onCurrentIndexChanged: {
setLocation();
}
function setLocation()
{
if (currentIndex == -1) {
_location.useActiveRunway = true;
diagram.selection = null;
} else {
_location.setDetailLocation(_location.airportRunways[currentIndex])
diagram.selection = _location.airportRunways[currentIndex]
}
}
function syncCurrentIndex()
{
if (_location.useActiveRunway) {
currentIndex = -1;
return;
}
for (var i=0; i < _location.airportRunways.length; ++i) {
if (_location.airportRunways[i].equals(_location.detail)) {
currentIndex = i;
return;
}
}
// not found, default to active
currentIndex = -1;
}
}
}
// runway offset row
Row {
x: Style.strutSize
// no offset for helipads
visible: !isHeliport
ToggleSwitch {
id: onFinalToggle
label: qsTr("On final approach at ")
anchors.verticalCenter: parent.verticalCenter
checked: _location.onFinal
enabled:runwayRadio.selected
onCheckedChanged: _location.onFinal = checked
}
DoubleSpinbox {
id: offsetNmEdit
value: _location.offsetNm
onCommit: _location.offsetNm = newValue;
suffix: "Nm"
min: 0.0
max: 40.0
decimals: 1
maxDigits: 5
live: true
anchors.verticalCenter: parent.verticalCenter
enabled: runwayRadio.selected && onFinalToggle.checked
}
Text {
text: qsTr(" from the threshold")
anchors.verticalCenter: parent.verticalCenter
}
}
ToggleSwitch {
x: Style.strutSize
// no localizer for helipads
visible: !isHeliport
// enable if selected runway has ILS
label: qsTr("Tune navigation radio (NAV1) to runway localizer")
checked: _location.tuneNAV1
onCheckedChanged: {
_location.tuneNAV1 = checked
}
}
// parking row
Row {
width: parent.width
spacing: Style.margin
// hide if there's no parking locations defined for this airport
visible: haveParking
RadioButton {
id: parkingRadio
anchors.verticalCenter: parent.verticalCenter
group: radioGroup
onClicked: {
if (selected) parkingChoice.setLocation();
}
}
Text {
text: qsTr("Parking")
anchors.verticalCenter: parent.verticalCenter
}
PopupChoice {
id: parkingChoice
model: _location.airportParkings
displayRole: "name"
width: parent.width * 0.5
anchors.verticalCenter: parent.verticalCenter
headerText: qsTr("Available")
enabled: parkingRadio.selected
onCurrentIndexChanged: {
setLocation();
}
function syncCurrentIndex()
{
if (_location.useAvailableParking) {
currentIndex = -1;
return;
}
for (var i=0; i < _location.airportParkings.length; ++i) {
if (_location.airportParkings[i].equals(_location.detail)) {
currentIndex = i;
return;
}
}
// not found, default to available
currentIndex = -1;
}
function setLocation()
{
if (currentIndex == -1) {
_location.useAvailableParking = true;
diagram.selection = null;
} else {
_location.setDetailLocation(_location.airportParkings[currentIndex])
diagram.selection = _location.airportParkings[currentIndex]
}
}
}
}
} // main layout column
} // main panel rectangle
}

View file

@ -0,0 +1,182 @@
import QtQuick 2.4
import FlightGear 1.0
import FlightGear.Launcher 1.0
import "."
Item {
property alias location: navaidData.guid
property alias geod: diagram.geod
Positioned {
id: navaidData
onGuidChanged: {
if (guid > 0) {
diagram.navaid = guid
_location.setBaseLocation(this)
}
}
}
NavaidDiagram {
id: diagram
anchors.fill: parent
offsetEnabled: _location.offsetEnabled
offsetBearingDeg: _location.offsetRadial
offsetDistanceNm: _location.offsetNm
headingDeg: _location.headingDeg
}
Rectangle {
id: panel
color: "transparent"
border.width: 1
border.color: Style.frameColor
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
margins: Style.strutSize
}
height: selectionGrid.height + Style.margin * 2
// set opacity here only, so we don't make the whole summary pannel translucent
Rectangle {
id: background
anchors.fill: parent
z: -1
opacity: Style.panelOpacity
color: "white"
}
Column {
id: selectionGrid
spacing: Style.margin
width: parent.width
Text { // heading text
visible: navaidData.valid
id: heading
width: parent.width
text: "Navaid: " + navaidData.ident + " / " + navaidData.name
}
Row {
height: childrenRect.height
width: parent.width
spacing: Style.margin
IntegerSpinbox {
label: qsTr("Airspeed:")
suffix: "kts"
min: 0
max: 10000 // more for spaceships?
step: 5
maxDigits: 5
value: _location.airspeedKnots
onCommit: _location.airspeedKnots = newValue
}
IntegerSpinbox {
label: qsTr("Heading:")
suffix: "deg" // FIXME use Unicode degree symbol
min: 0
max: 359
live: true
maxDigits: 3
value: _location.headingDeg
onCommit: _location.headingDeg = newValue
}
}
Row {
height: childrenRect.height
width: parent.width
spacing: Style.margin
IntegerSpinbox {
label: qsTr("Altitude:")
suffix: "ft"
min: -1000 // Dead Sea, Schiphol
max: 200000
step: 100
maxDigits: 6
visible: !altitudeTypeChoice.isFlightLevel
value: _location.altitudeFt
onCommit: _location.altitudeFt = newValue
}
IntegerSpinbox {
label: qsTr("Altitude:")
prefix: "FL"
min: 0
max: 1000
step: 10
maxDigits: 3
visible: altitudeTypeChoice.isFlightLevel
}
PopupChoice {
id: altitudeTypeChoice
readonly property bool isFlightLevel: (currentIndex == 2)
model: [qsTr("Above mean sea-level (MSL)"),
qsTr("Above ground (AGL)"),
qsTr("Flight-level")]
}
}
// offset row
Row {
x: Style.strutSize
ToggleSwitch {
id: offsetToggle
label: qsTr("Offset ")
anchors.verticalCenter: parent.verticalCenter
checked: _location.offsetEnabled
onCheckedChanged: {
_location.offsetEnabled = checked
}
}
DoubleSpinbox {
id: offsetNmEdit
value: _location.offsetNm
onCommit: _location.offsetNm = newValue
min: 0.0
max: 40.0
suffix: "Nm"
maxDigits: 5
decimals: 1
live: true
anchors.verticalCenter: parent.verticalCenter
enabled: offsetToggle.checked
}
Text {
text: qsTr(" on bearing ")
anchors.verticalCenter: parent.verticalCenter
}
IntegerSpinbox {
id: offsetBearingEdit
suffix: "deg" // FIXME use Unicode degree symbol
min: 0
max: 359
maxDigits: 3
live: true
anchors.verticalCenter: parent.verticalCenter
enabled: offsetToggle.checked
value: _location.offsetRadial
onCommit: _location.offsetRadial = newValue
}
}
} // main layout column
} // main panel rectangle
}

View file

@ -18,6 +18,13 @@ Item {
color: "magenta"
}
Connections {
target: _launcher
onAircraftTypeChanged: {
console.info("Aircraft type is now:" + _launcher.aircraftType)
}
}
PreviewImage {
id: preview
anchors.centerIn: parent
@ -151,7 +158,8 @@ Item {
// TODO - make clickable, jump to to the aircraft in the installed
// aircraft list
Text {
text: _launcher.selectedAircraftInfo.name
text: _launcher.selectedAircraftInfo.name === "" ?
qsTr("No aircraft selected") : _launcher.selectedAircraftInfo.name
font.pixelSize: Style.headingFontPixelSize
}
@ -265,8 +273,9 @@ Item {
// TODO - make clickable, jump to the location page
Text {
text: _launcher.locationDescription
text: _launcher.location.description
font.pixelSize: Style.headingFontPixelSize
width: summaryGrid.middleColumnWidth
}
HistoryPopup {

View file

@ -72,6 +72,7 @@
<file>qml/DateTimeValueEdit.qml</file>
<file>qml/SettingsDateTimePicker.qml</file>
<file>qml/Summary.qml</file>
<file>qml/Location.qml</file>
<file>qml/HistoryPopup.qml</file>
<file>qml/AddOns.qml</file>
<file>qml/DragToReorderButton.qml</file>
@ -86,6 +87,8 @@
<file>qml/PathListDelegate.qml</file>
<file>qml/AddCatalogPanel.qml</file>
<file>qml/LineEdit.qml</file>
<file>qml/LocationAirportView.qml</file>
<file>qml/LocationNavaidView.qml</file>
<file alias="linear-spinner">qml/icons8-linear-spinner.gif</file>
<file>qml/RadioButton.qml</file>
<file>qml/IntegerSpinbox.qml</file>