UI tweaks for the launcher
This commit is contained in:
parent
8eb4d76411
commit
71a1348037
16 changed files with 749 additions and 23 deletions
|
@ -59,7 +59,7 @@ bool PopupWindowTracker::eventFilter(QObject *watched, QEvent *event)
|
||||||
} else {
|
} else {
|
||||||
m_window->close();
|
m_window->close();
|
||||||
setWindow(nullptr);
|
setWindow(nullptr);
|
||||||
return true;
|
// still fall through
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
93
src/GUI/QmlRadioButtonHelper.cxx
Normal file
93
src/GUI/QmlRadioButtonHelper.cxx
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
// QmlRadioButtonHelper.hxx - helper for QtQuick radio button impl
|
||||||
|
//
|
||||||
|
// Written by James Turner, started April 2018.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 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 "QmlRadioButtonHelper.hxx"
|
||||||
|
|
||||||
|
#include <QMetaObject>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
QmlRadioButtonGroup::QmlRadioButtonGroup(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QmlRadioButtonGroupAttached* QmlRadioButtonGroup::qmlAttachedProperties(QObject *object)
|
||||||
|
{
|
||||||
|
return new QmlRadioButtonGroupAttached(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject *QmlRadioButtonGroup::selected() const
|
||||||
|
{
|
||||||
|
return m_selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlRadioButtonGroup::setSelected(QObject *selected)
|
||||||
|
{
|
||||||
|
if (m_selected == selected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_selected = selected;
|
||||||
|
emit selectedChanged(m_selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
QmlRadioButtonGroupAttached::QmlRadioButtonGroupAttached(QObject *pr) :
|
||||||
|
QObject(pr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QmlRadioButtonGroup *QmlRadioButtonGroupAttached::group() const
|
||||||
|
{
|
||||||
|
return m_group;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QmlRadioButtonGroupAttached::isSelected() const
|
||||||
|
{
|
||||||
|
if (!m_group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return (m_group->selected() == this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlRadioButtonGroupAttached::setGroup(QmlRadioButtonGroup *group)
|
||||||
|
{
|
||||||
|
if (m_group == group)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_group) {
|
||||||
|
disconnect(m_group, &QmlRadioButtonGroup::selectedChanged,
|
||||||
|
this, &QmlRadioButtonGroupAttached::onGroupSelectionChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_group = group;
|
||||||
|
|
||||||
|
if (m_group) {
|
||||||
|
connect(m_group, &QmlRadioButtonGroup::selectedChanged,
|
||||||
|
this, &QmlRadioButtonGroupAttached::onGroupSelectionChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit groupChanged(m_group);
|
||||||
|
emit isSelectedChanged(isSelected());
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlRadioButtonGroupAttached::onGroupSelectionChanged()
|
||||||
|
{
|
||||||
|
emit isSelectedChanged(isSelected());
|
||||||
|
}
|
78
src/GUI/QmlRadioButtonHelper.hxx
Normal file
78
src/GUI/QmlRadioButtonHelper.hxx
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
// QmlRadioButtonHelper.hxx - helper for QtQuick radio button impl
|
||||||
|
//
|
||||||
|
// Written by James Turner, started April 2018.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 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 QMLRADIOBUTTONHELPER_HXX
|
||||||
|
#define QMLRADIOBUTTONHELPER_HXX
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QQmlEngine> // for QML_DECLARE_TYPEINFO
|
||||||
|
|
||||||
|
class QmlRadioButtonGroup;
|
||||||
|
|
||||||
|
class QmlRadioButtonGroupAttached : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QmlRadioButtonGroup* group READ group WRITE setGroup NOTIFY groupChanged)
|
||||||
|
Q_PROPERTY(bool isSelected READ isSelected NOTIFY isSelectedChanged)
|
||||||
|
public:
|
||||||
|
QmlRadioButtonGroupAttached(QObject* pr = nullptr);
|
||||||
|
|
||||||
|
QmlRadioButtonGroup* group() const;
|
||||||
|
bool isSelected() const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setGroup(QmlRadioButtonGroup* group);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void groupChanged(QmlRadioButtonGroup* group);
|
||||||
|
void isSelectedChanged(bool isSelected);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onGroupSelectionChanged();
|
||||||
|
|
||||||
|
QmlRadioButtonGroup* m_group = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QmlRadioButtonGroup : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QObject* selected READ selected WRITE setSelected NOTIFY selectedChanged)
|
||||||
|
public:
|
||||||
|
explicit QmlRadioButtonGroup(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
static QmlRadioButtonGroupAttached *qmlAttachedProperties(QObject *);
|
||||||
|
|
||||||
|
QObject* selected() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void selectedChanged(QObject* selected);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setSelected(QObject* selected);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QObject* m_selected = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
QML_DECLARE_TYPEINFO(QmlRadioButtonGroup, QML_HAS_ATTACHED_PROPERTIES)
|
||||||
|
|
||||||
|
#endif // QMLRADIOBUTTONHELPER_HXX
|
|
@ -12,7 +12,7 @@ Item {
|
||||||
footer.height
|
footer.height
|
||||||
implicitWidth: ListView.view.width
|
implicitWidth: ListView.view.width
|
||||||
|
|
||||||
readonly property bool __isSelected: (_launcher.selectedAircraft == model.uri)
|
readonly property bool __isSelected: (_launcher.selectedAircraft === model.uri)
|
||||||
|
|
||||||
property bool __showAlternateText: false
|
property bool __showAlternateText: false
|
||||||
|
|
||||||
|
@ -100,13 +100,13 @@ Item {
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
height: implicitHeight
|
height: implicitHeight
|
||||||
visible: (model.description != "") || root.__showAlternateText
|
visible: (model.description !== "") || root.__showAlternateText
|
||||||
}
|
}
|
||||||
|
|
||||||
AircraftDownloadPanel
|
AircraftDownloadPanel
|
||||||
{
|
{
|
||||||
id: downloadPanel
|
id: downloadPanel
|
||||||
visible: (model.package != undefined)
|
visible: (model.package !== undefined)
|
||||||
packageSize: model.packageSizeBytes
|
packageSize: model.packageSizeBytes
|
||||||
installStatus: model.packageStatus
|
installStatus: model.packageStatus
|
||||||
downloadedBytes: model.downloadedBytes
|
downloadedBytes: model.downloadedBytes
|
||||||
|
|
|
@ -93,7 +93,7 @@ FocusScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
if ((event.key == Qt.Key_Colon) || (event.key == Qt.Key_Slash)) {
|
if ((event.key === Qt.Key_Colon) || (event.key === Qt.Key_Slash)) {
|
||||||
nextToFocus.focus = true;
|
nextToFocus.focus = true;
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
|
@ -136,9 +136,10 @@ FocusScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
onWheel: {
|
onWheel: {
|
||||||
if (wheel.angleDelta > 0) {
|
var delta = wheel.angleDelta.y
|
||||||
|
if (delta > 0) {
|
||||||
root.incrementValue()
|
root.incrementValue()
|
||||||
} else if (wheel.angleDelta < 0) {
|
} else if (delta < 0) {
|
||||||
root.decrementValue()
|
root.decrementValue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
214
src/GUI/qml/DoubleSpinbox.qml
Normal file
214
src/GUI/qml/DoubleSpinbox.qml
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
import QtQuick 2.4
|
||||||
|
import "."
|
||||||
|
|
||||||
|
FocusScope {
|
||||||
|
id: root
|
||||||
|
property string label
|
||||||
|
property bool enabled: true
|
||||||
|
property int value: 0
|
||||||
|
property alias min: validator.bottom
|
||||||
|
property int max: validator.top
|
||||||
|
property alias decimals: validator.decimals
|
||||||
|
|
||||||
|
property bool wrap: false
|
||||||
|
property alias suffix: suffix.text
|
||||||
|
property alias prefix: prefix.text
|
||||||
|
property alias maxDigits: edit.maximumLength
|
||||||
|
property int step: 1
|
||||||
|
|
||||||
|
implicitHeight: editFrame.height
|
||||||
|
// we have a margin between the frame and the label, and on each
|
||||||
|
implicitWidth: label.width + editFrame.width + Style.margin
|
||||||
|
|
||||||
|
signal commit(var newValue);
|
||||||
|
|
||||||
|
function incrementValue()
|
||||||
|
{
|
||||||
|
if (edit.activeFocus) {
|
||||||
|
value = Math.min(parseFloat(edit.text) + root.step, root.max)
|
||||||
|
edit.text = value
|
||||||
|
} else {
|
||||||
|
commit(Math.min(value + root.step, root.max))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function decrementValue()
|
||||||
|
{
|
||||||
|
if (edit.activeFocus) {
|
||||||
|
value = Math.max(parseFloat(edit.text) - root.step, root.min)
|
||||||
|
edit.text = value
|
||||||
|
} else {
|
||||||
|
commit(Math.max(value - root.step, root.min))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextMetrics {
|
||||||
|
id: metrics
|
||||||
|
text: root.max // use maximum value
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: label
|
||||||
|
text: root.label
|
||||||
|
anchors.verticalCenter: editFrame.verticalCenter
|
||||||
|
color: editFrame.activeFocus ? Style.themeColor :
|
||||||
|
(root.enabled ? "black" : Style.inactiveThemeColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
height: root.height
|
||||||
|
width: root.width
|
||||||
|
enabled: root.enabled
|
||||||
|
|
||||||
|
// use wheel events to adjust up/dowm
|
||||||
|
onClicked: {
|
||||||
|
edit.forceActiveFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
onWheel: {
|
||||||
|
var delta = wheel.angleDelta.y
|
||||||
|
if (delta > 0) {
|
||||||
|
root.incrementValue()
|
||||||
|
} else if (delta < 0) {
|
||||||
|
root.decrementValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
when: !edit.activeFocus
|
||||||
|
target: edit
|
||||||
|
property: "text"
|
||||||
|
value: root.value
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: editFrame
|
||||||
|
clip: true
|
||||||
|
anchors.left: label.right
|
||||||
|
anchors.margins: Style.margin
|
||||||
|
|
||||||
|
height: edit.implicitHeight + Style.margin
|
||||||
|
width: edit.width + prefix.width + suffix.width + upDownArea.width + Style.margin * 2
|
||||||
|
radius: Style.roundRadius
|
||||||
|
border.color: edit.activeFocus ? Style.frameColor : Style.minorFrameColor
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: prefix
|
||||||
|
visible: root.prefix !== ""
|
||||||
|
color: Style.baseTextColor
|
||||||
|
anchors.baseline: edit.baseline
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Style.margin
|
||||||
|
}
|
||||||
|
|
||||||
|
TextInput {
|
||||||
|
id: edit
|
||||||
|
enabled: root.enabled
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: prefix.right
|
||||||
|
selectByMouse: true
|
||||||
|
width: metrics.width
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
|
||||||
|
focus: true
|
||||||
|
color: enabled && activeFocus ? Style.themeColor : Style.baseTextColor
|
||||||
|
|
||||||
|
validator: DoubleValidator {
|
||||||
|
id: validator
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onUpPressed: {
|
||||||
|
root.incrementValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onDownPressed: {
|
||||||
|
root.decrementValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
onActiveFocusChanged: {
|
||||||
|
if (activeFocus) {
|
||||||
|
selectAll();
|
||||||
|
} else {
|
||||||
|
commit(parseFloat(text))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: suffix
|
||||||
|
visible: root.suffix !== ""
|
||||||
|
color: Style.baseTextColor
|
||||||
|
anchors.baseline: edit.baseline
|
||||||
|
anchors.right: upDownArea.left
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: upDownArea
|
||||||
|
// color: "white"
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Style.margin
|
||||||
|
anchors.verticalCenter: editFrame.verticalCenter
|
||||||
|
height: upDownIcon.implicitHeight
|
||||||
|
visible: edit.activeFocus
|
||||||
|
width: upDownIcon.implicitWidth
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: upDownIcon
|
||||||
|
// show up/down arrows
|
||||||
|
source: "qrc:///up-down-arrow"
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height / 2
|
||||||
|
onPressed: {
|
||||||
|
root.incrementValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
opacity: 0.5
|
||||||
|
color: Style.themeColor
|
||||||
|
visible: parent.pressed
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: upRepeat
|
||||||
|
interval: 250
|
||||||
|
running: parent.pressed
|
||||||
|
repeat: true
|
||||||
|
onTriggered: root.incrementValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height / 2
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
onPressed: {
|
||||||
|
root.decrementValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
opacity: 0.5
|
||||||
|
color: Style.themeColor
|
||||||
|
visible: parent.pressed
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: downRepeat
|
||||||
|
interval: 250
|
||||||
|
running: parent.pressed
|
||||||
|
repeat: true
|
||||||
|
onTriggered: root.decrementValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // of frame rectangle
|
||||||
|
|
||||||
|
|
||||||
|
}
|
212
src/GUI/qml/IntegerSpinbox.qml
Normal file
212
src/GUI/qml/IntegerSpinbox.qml
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
import QtQuick 2.4
|
||||||
|
import "."
|
||||||
|
|
||||||
|
FocusScope {
|
||||||
|
id: root
|
||||||
|
property string label
|
||||||
|
property bool enabled: true
|
||||||
|
property int value: 0
|
||||||
|
property alias min: validator.bottom
|
||||||
|
property int max: validator.top
|
||||||
|
property bool wrap: false
|
||||||
|
property alias suffix: suffix.text
|
||||||
|
property alias prefix: prefix.text
|
||||||
|
property alias maxDigits: edit.maximumLength
|
||||||
|
property int step: 1
|
||||||
|
|
||||||
|
implicitHeight: editFrame.height
|
||||||
|
// we have a margin between the frame and the label, and on each
|
||||||
|
implicitWidth: label.width + editFrame.width + Style.margin
|
||||||
|
|
||||||
|
signal commit(var newValue);
|
||||||
|
|
||||||
|
function incrementValue()
|
||||||
|
{
|
||||||
|
if (edit.activeFocus) {
|
||||||
|
value = Math.min(parseInt(edit.text) + root.step, root.max)
|
||||||
|
edit.text = value
|
||||||
|
} else {
|
||||||
|
commit(Math.min(value + root.step, root.max))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function decrementValue()
|
||||||
|
{
|
||||||
|
if (edit.activeFocus) {
|
||||||
|
value = Math.max(parseInt(edit.text) - root.step, root.min)
|
||||||
|
edit.text = value
|
||||||
|
} else {
|
||||||
|
commit(Math.max(value - root.step, root.min))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextMetrics {
|
||||||
|
id: metrics
|
||||||
|
text: root.max // use maximum value
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: label
|
||||||
|
text: root.label
|
||||||
|
anchors.verticalCenter: editFrame.verticalCenter
|
||||||
|
color: editFrame.activeFocus ? Style.themeColor :
|
||||||
|
(root.enabled ? "black" : Style.inactiveThemeColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
height: root.height
|
||||||
|
width: root.width
|
||||||
|
enabled: root.enabled
|
||||||
|
|
||||||
|
// use wheel events to adjust up/dowm
|
||||||
|
onClicked: {
|
||||||
|
edit.forceActiveFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
onWheel: {
|
||||||
|
var delta = wheel.angleDelta.y
|
||||||
|
if (delta > 0) {
|
||||||
|
root.incrementValue()
|
||||||
|
} else if (delta < 0) {
|
||||||
|
root.decrementValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
when: !edit.activeFocus
|
||||||
|
target: edit
|
||||||
|
property: "text"
|
||||||
|
value: root.value
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: editFrame
|
||||||
|
clip: true
|
||||||
|
anchors.left: label.right
|
||||||
|
anchors.margins: Style.margin
|
||||||
|
|
||||||
|
height: edit.implicitHeight + Style.margin
|
||||||
|
width: edit.width + prefix.width + suffix.width + upDownArea.width + Style.margin * 2
|
||||||
|
radius: Style.roundRadius
|
||||||
|
border.color: edit.activeFocus ? Style.frameColor : Style.minorFrameColor
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: prefix
|
||||||
|
visible: root.prefix !== ""
|
||||||
|
color: Style.baseTextColor
|
||||||
|
anchors.baseline: edit.baseline
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Style.margin
|
||||||
|
}
|
||||||
|
|
||||||
|
TextInput {
|
||||||
|
id: edit
|
||||||
|
enabled: root.enabled
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: prefix.right
|
||||||
|
selectByMouse: true
|
||||||
|
width: metrics.width
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
|
||||||
|
focus: true
|
||||||
|
color: enabled && activeFocus ? Style.themeColor : Style.baseTextColor
|
||||||
|
|
||||||
|
validator: IntValidator {
|
||||||
|
id: validator
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onUpPressed: {
|
||||||
|
root.incrementValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onDownPressed: {
|
||||||
|
root.decrementValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
onActiveFocusChanged: {
|
||||||
|
if (activeFocus) {
|
||||||
|
selectAll();
|
||||||
|
} else {
|
||||||
|
commit(parseInt(text))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: suffix
|
||||||
|
visible: root.suffix !== ""
|
||||||
|
color: Style.baseTextColor
|
||||||
|
anchors.baseline: edit.baseline
|
||||||
|
anchors.right: upDownArea.left
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: upDownArea
|
||||||
|
// color: "white"
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Style.margin
|
||||||
|
anchors.verticalCenter: editFrame.verticalCenter
|
||||||
|
height: upDownIcon.implicitHeight
|
||||||
|
visible: edit.activeFocus
|
||||||
|
width: upDownIcon.implicitWidth
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: upDownIcon
|
||||||
|
// show up/down arrows
|
||||||
|
source: "qrc:///up-down-arrow"
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height / 2
|
||||||
|
onPressed: {
|
||||||
|
root.incrementValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
opacity: 0.5
|
||||||
|
color: Style.themeColor
|
||||||
|
visible: parent.pressed
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: upRepeat
|
||||||
|
interval: 250
|
||||||
|
running: parent.pressed
|
||||||
|
repeat: true
|
||||||
|
onTriggered: root.incrementValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height / 2
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
onPressed: {
|
||||||
|
root.decrementValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
opacity: 0.5
|
||||||
|
color: Style.themeColor
|
||||||
|
visible: parent.pressed
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: downRepeat
|
||||||
|
interval: 250
|
||||||
|
running: parent.pressed
|
||||||
|
repeat: true
|
||||||
|
onTriggered: root.decrementValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // of frame rectangle
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ FocusScope {
|
||||||
property string placeholder: ""
|
property string placeholder: ""
|
||||||
property alias validator: edit.validator
|
property alias validator: edit.validator
|
||||||
property alias text: edit.text
|
property alias text: edit.text
|
||||||
|
property bool enabled: true
|
||||||
|
|
||||||
property alias suggestedWidthString: metrics.text
|
property alias suggestedWidthString: metrics.text
|
||||||
readonly property int suggestedWidth: useFullWidth ? root.width
|
readonly property int suggestedWidth: useFullWidth ? root.width
|
||||||
|
@ -16,6 +17,7 @@ FocusScope {
|
||||||
property bool useFullWidth: false
|
property bool useFullWidth: false
|
||||||
|
|
||||||
implicitHeight: editFrame.height
|
implicitHeight: editFrame.height
|
||||||
|
implicitWidth: suggestedWidth + label.implicitWidth + (Style.margin * 3)
|
||||||
|
|
||||||
TextMetrics {
|
TextMetrics {
|
||||||
id: metrics
|
id: metrics
|
||||||
|
@ -35,10 +37,8 @@ FocusScope {
|
||||||
anchors.left: label.right
|
anchors.left: label.right
|
||||||
anchors.margins: Style.margin
|
anchors.margins: Style.margin
|
||||||
|
|
||||||
|
|
||||||
height: edit.implicitHeight + Style.margin
|
height: edit.implicitHeight + Style.margin
|
||||||
|
width: Math.min(root.width - (label.width + Style.margin), Math.max(suggestedWidth, edit.implicitWidth) + Style.margin * 2);
|
||||||
width: Math.min(root.width - (label.width + Style.margin * 2), Math.max(suggestedWidth, edit.implicitWidth));
|
|
||||||
|
|
||||||
radius: Style.roundRadius
|
radius: Style.roundRadius
|
||||||
border.color: edit.activeFocus ? Style.frameColor : Style.minorFrameColor
|
border.color: edit.activeFocus ? Style.frameColor : Style.minorFrameColor
|
||||||
|
@ -54,6 +54,7 @@ FocusScope {
|
||||||
anchors.margins: Style.margin
|
anchors.margins: Style.margin
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
focus: true
|
focus: true
|
||||||
|
color: enabled && activeFocus ? Style.themeColor : Style.baseTextColor
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: placeholder
|
id: placeholder
|
||||||
|
|
|
@ -14,7 +14,11 @@ Item {
|
||||||
property int currentIndex: 0
|
property int currentIndex: 0
|
||||||
property bool __dummy: false
|
property bool __dummy: false
|
||||||
|
|
||||||
|
property alias header: choicesHeader.sourceComponent
|
||||||
|
property string headerText: ""
|
||||||
|
|
||||||
implicitHeight: Math.max(label.implicitHeight, currentChoiceFrame.height)
|
implicitHeight: Math.max(label.implicitHeight, currentChoiceFrame.height)
|
||||||
|
implicitWidth: label.implicitWidth + Style.margin + currentChoiceFrame.__naturalWidth
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Repeater {
|
Repeater {
|
||||||
|
@ -44,8 +48,16 @@ Item {
|
||||||
__dummy = !__dummy // force update of currentText
|
__dummy = !__dummy // force update of currentText
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function haveHeader()
|
||||||
|
{
|
||||||
|
return headerText !== "";
|
||||||
|
}
|
||||||
|
|
||||||
function currentText()
|
function currentText()
|
||||||
{
|
{
|
||||||
|
if ((currentIndex == -1) && haveHeader())
|
||||||
|
return headerText;
|
||||||
|
|
||||||
var foo = __dummy; // fake propery dependency to update this
|
var foo = __dummy; // fake propery dependency to update this
|
||||||
var item = internalModel.itemAt(currentIndex);
|
var item = internalModel.itemAt(currentIndex);
|
||||||
if (!item) return "";
|
if (!item) return "";
|
||||||
|
@ -65,17 +77,19 @@ Item {
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: currentChoiceFrame
|
id: currentChoiceFrame
|
||||||
radius: Style.roundRadius
|
radius: Style.roundRadius
|
||||||
border.color: mouseArea.containsMouse ? Style.themeColor : Style.minorFrameColor
|
border.color: root.enabled ? (mouseArea.containsMouse ? Style.themeColor : Style.minorFrameColor)
|
||||||
|
: Style.inactiveThemeColor
|
||||||
border.width: 1
|
border.width: 1
|
||||||
height: currentChoiceText.implicitHeight + Style.margin
|
height: currentChoiceText.implicitHeight + Style.margin
|
||||||
|
clip: true
|
||||||
|
|
||||||
anchors.left: label.right
|
anchors.left: label.right
|
||||||
anchors.leftMargin: Style.margin
|
anchors.leftMargin: Style.margin
|
||||||
|
|
||||||
// width of current item, or available space after the label
|
// width of current item, or available space after the label
|
||||||
width: Math.min(root.width - (label.width + Style.margin),
|
width: Math.min(root.width - (label.width + Style.margin), __naturalWidth);
|
||||||
currentChoiceText.implicitWidth + (Style.margin * 2) + upDownIcon.width);
|
|
||||||
|
|
||||||
|
readonly property int __naturalWidth: currentChoiceText.implicitWidth + (Style.margin * 3) + upDownIcon.width
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
|
@ -102,7 +116,7 @@ Item {
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
hoverEnabled: true
|
hoverEnabled: root.enabled
|
||||||
enabled: root.enabled
|
enabled: root.enabled
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var screenPos = _launcher.mapToGlobal(currentChoiceText, Qt.point(0, -currentChoiceText.height * currentIndex))
|
var screenPos = _launcher.mapToGlobal(currentChoiceText, Qt.point(0, -currentChoiceText.height * currentIndex))
|
||||||
|
@ -138,13 +152,42 @@ Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
}
|
}
|
||||||
|
|
||||||
// text repeater
|
// choice layout column
|
||||||
Column {
|
Column {
|
||||||
id: choicesColumn
|
id: choicesColumn
|
||||||
spacing: Style.margin
|
spacing: Style.margin
|
||||||
x: Style.margin
|
x: Style.margin
|
||||||
y: Style.margin
|
y: Style.margin
|
||||||
|
|
||||||
|
// optional header component:
|
||||||
|
Loader {
|
||||||
|
id: choicesHeader
|
||||||
|
active: root.haveHeader()
|
||||||
|
|
||||||
|
// default component is just a plain text element, same as
|
||||||
|
// normal items
|
||||||
|
sourceComponent: Text {
|
||||||
|
text: root.headerText
|
||||||
|
height: implicitHeight + Style.margin
|
||||||
|
width: popupFrame.width
|
||||||
|
}
|
||||||
|
|
||||||
|
height: item ? item.height : 0
|
||||||
|
width: item ? item.width : 0
|
||||||
|
|
||||||
|
// essentially the same mouse area as normal items
|
||||||
|
MouseArea {
|
||||||
|
width: popupFrame.width // full width of the popup
|
||||||
|
height: parent.height
|
||||||
|
z: -1 // so header can do other mouse behaviours
|
||||||
|
onClicked: {
|
||||||
|
root.currentIndex = -1;
|
||||||
|
popupFrame.visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // of header loader
|
||||||
|
|
||||||
|
// main item repeater
|
||||||
Repeater {
|
Repeater {
|
||||||
id: choicesRepeater
|
id: choicesRepeater
|
||||||
model: root.model
|
model: root.model
|
||||||
|
|
68
src/GUI/qml/RadioButton.qml
Normal file
68
src/GUI/qml/RadioButton.qml
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import QtQuick 2.4
|
||||||
|
import FlightGear 1.0
|
||||||
|
import "."
|
||||||
|
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
property bool selected
|
||||||
|
property RadioButtonGroup group // nil by default
|
||||||
|
|
||||||
|
signal clicked()
|
||||||
|
|
||||||
|
implicitHeight: outerRing.height + Style.margin
|
||||||
|
implicitWidth: outerRing.width + Style.margin
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
when: root.group != null
|
||||||
|
target: root
|
||||||
|
property: "selected"
|
||||||
|
value: (root.group.selected === root)
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: innerRing
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: radius * 2
|
||||||
|
height: radius * 2
|
||||||
|
radius: Style.roundRadius
|
||||||
|
color: Style.themeColor
|
||||||
|
visible: selected || mouse.containsMouse
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: outerRing
|
||||||
|
anchors.centerIn: parent
|
||||||
|
border.color: Style.themeColor
|
||||||
|
border.width: 2
|
||||||
|
color: "transparent"
|
||||||
|
radius: Style.roundRadius + 4
|
||||||
|
width: radius * 2
|
||||||
|
height: radius * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: pressRing
|
||||||
|
opacity: 0.3
|
||||||
|
width: outerRing.width * 2
|
||||||
|
height: outerRing.height * 2
|
||||||
|
visible: mouse.pressed
|
||||||
|
radius: width / 2
|
||||||
|
color: "#7f7f7f"
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouse
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: {
|
||||||
|
if (root.group) {
|
||||||
|
root.group.selected = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.clicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,14 +5,15 @@ FocusScope
|
||||||
{
|
{
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
width:frame.width
|
width: Style.strutSize * 3
|
||||||
height: frame.height
|
height: frame.height
|
||||||
|
|
||||||
// property string text
|
property string placeholder: qsTr("Search")
|
||||||
property bool active: false
|
property bool active: false
|
||||||
|
|
||||||
signal search(string term)
|
signal search(string term)
|
||||||
|
|
||||||
|
property bool autoSubmit: true
|
||||||
property alias autoSubmitTimeout: searchTimer.interval
|
property alias autoSubmitTimeout: searchTimer.interval
|
||||||
|
|
||||||
onActiveChanged: {
|
onActiveChanged: {
|
||||||
|
@ -23,7 +24,7 @@ FocusScope
|
||||||
|
|
||||||
function clear()
|
function clear()
|
||||||
{
|
{
|
||||||
buttonText.text = "Search"
|
buttonText.text = ""
|
||||||
root.focus = false
|
root.focus = false
|
||||||
searchTimer.stop();
|
searchTimer.stop();
|
||||||
root.search("");
|
root.search("");
|
||||||
|
@ -34,12 +35,14 @@ FocusScope
|
||||||
id: frame
|
id: frame
|
||||||
radius: Style.roundRadius
|
radius: Style.roundRadius
|
||||||
|
|
||||||
width: Style.strutSize * 3
|
width: root.width
|
||||||
height: Math.max(searchIcon.height, buttonText.height) + (Style.roundRadius)
|
height: Math.max(searchIcon.height, buttonText.height) + (Style.roundRadius)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
border.color: (mouse.containsMouse | active) ? Style.themeColor: Style.minorFrameColor
|
border.color: (mouse.containsMouse | active) ? Style.themeColor: Style.minorFrameColor
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
TextInput {
|
TextInput {
|
||||||
id: buttonText
|
id: buttonText
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
@ -51,7 +54,9 @@ FocusScope
|
||||||
focus: true
|
focus: true
|
||||||
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
searchTimer.restart();
|
if (root.autoSubmit) {
|
||||||
|
searchTimer.restart();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onEditingFinished: {
|
onEditingFinished: {
|
||||||
|
@ -63,7 +68,14 @@ FocusScope
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
text: "Search"
|
text: ""
|
||||||
|
|
||||||
|
// placeholder text, hides itself whenever parent has non-empty text
|
||||||
|
Text {
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: parent.text == ""
|
||||||
|
text: root.placeholder
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
|
|
|
@ -121,7 +121,7 @@ Item {
|
||||||
label: qsTr("Show debugging console")
|
label: qsTr("Show debugging console")
|
||||||
description: qsTr("Open a console window showing debug output from the application.")
|
description: qsTr("Open a console window showing debug output from the application.")
|
||||||
advanced: true
|
advanced: true
|
||||||
hidden: _osName != "win"
|
hidden: _osName !== "win"
|
||||||
keywords: ["console", "terminal", "log", "debug"]
|
keywords: ["console", "terminal", "log", "debug"]
|
||||||
setting: "console"
|
setting: "console"
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ Item {
|
||||||
property bool enabled: true
|
property bool enabled: true
|
||||||
|
|
||||||
implicitWidth: track.width + label.width + 16
|
implicitWidth: track.width + label.width + 16
|
||||||
implicitHeight: label.height
|
implicitHeight: Math.max(label.height, thumb.height)
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: track
|
id: track
|
||||||
|
|
BIN
src/GUI/qml/icons8-linear-spinner.gif
Normal file
BIN
src/GUI/qml/icons8-linear-spinner.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
|
@ -86,6 +86,10 @@
|
||||||
<file>qml/PathListDelegate.qml</file>
|
<file>qml/PathListDelegate.qml</file>
|
||||||
<file>qml/AddCatalogPanel.qml</file>
|
<file>qml/AddCatalogPanel.qml</file>
|
||||||
<file>qml/LineEdit.qml</file>
|
<file>qml/LineEdit.qml</file>
|
||||||
|
<file alias="linear-spinner">qml/icons8-linear-spinner.gif</file>
|
||||||
|
<file>qml/RadioButton.qml</file>
|
||||||
|
<file>qml/IntegerSpinbox.qml</file>
|
||||||
|
<file>qml/DoubleSpinbox.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/preview">
|
<qresource prefix="/preview">
|
||||||
<file alias="close-icon">preview-close.png</file>
|
<file alias="close-icon">preview-close.png</file>
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 768 B After Width: | Height: | Size: 802 B |
Loading…
Add table
Reference in a new issue