diff --git a/src/GUI/qml/AirportEntry.qml b/src/GUI/qml/AirportEntry.qml
index 527235a36..c5d9f43ab 100644
--- a/src/GUI/qml/AirportEntry.qml
+++ b/src/GUI/qml/AirportEntry.qml
@@ -1,5 +1,4 @@
import QtQuick 2.4
-import QtQuick.Window 2.0
import FlightGear 1.0
import FlightGear.Launcher 1.0
import "."
@@ -66,7 +65,9 @@ FocusScope
onActiveFocusChanged: {
if (activeFocus) {
- OverlayShared.globalOverlay.showOverlayAtItemOffset(overlay, root, Qt.point(xOffsetForEditFrame, root.height + Style.margin))
+ OverlayShared.globalOverlay.showOverlayAtItemOffset(overlay, root,
+ Qt.point(xOffsetForEditFrame, root.height + Style.margin),
+ false /* don't offset */);
} else {
OverlayShared.globalOverlay.dismissOverlay()
searchCompleter.clear();
diff --git a/src/GUI/qml/Launcher.qml b/src/GUI/qml/Launcher.qml
index 644924dbb..61c60d3bd 100644
--- a/src/GUI/qml/Launcher.qml
+++ b/src/GUI/qml/Launcher.qml
@@ -167,6 +167,4 @@ Item {
OverlayShared.globalOverlay = this
}
}
-
-
}
diff --git a/src/GUI/qml/Menu.qml b/src/GUI/qml/Menu.qml
index 783223489..dc0e26b2a 100644
--- a/src/GUI/qml/Menu.qml
+++ b/src/GUI/qml/Menu.qml
@@ -62,7 +62,7 @@ Item {
width: parent.width - 2
// helper function called by BaseMenuItem on its parent, i.e,
-// us, to requestc closing the menu
+// us, to request closing the menu
function requestClose()
{
root.close();
@@ -75,4 +75,4 @@ Item {
// menu items get inserted here
}
}
-}
\ No newline at end of file
+}
diff --git a/src/GUI/qml/Overlay.qml b/src/GUI/qml/Overlay.qml
index 537c2246c..611559406 100644
--- a/src/GUI/qml/Overlay.qml
+++ b/src/GUI/qml/Overlay.qml
@@ -1,6 +1,10 @@
import QtQuick 2.4
Item {
+ id: root
+
+ readonly property bool dismissOnClickOutside: activeOverlayLoader.item && activeOverlayLoader.item.hasOwnProperty("dismissOnClickOutside") ?
+ activeOverlayLoader.item.dismissOnClickOutside : false
function showOverlay(comp)
{
@@ -23,9 +27,40 @@ Item {
activeOverlayLoader.visible = false;
}
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ visible: activeOverlayLoader.visible && root.dismissOnClickOutside
+ onClicked: root.dismissOverlay()
+ }
+
Loader
{
id: activeOverlayLoader
// no size, size to the component
+
+ onStatusChanged: {
+ if (status == Loader.Ready) {
+ if (item.hasOwnProperty("adjustYPosition")) {
+ // setting position here doesn't work, so we use a 0-interval
+ // timer to do it shortly afterwards
+ adjustPosition.start();
+ }
+ }
+ }
+
+ function adjustY()
+ {
+ var overflowHeight = (y + item.height) - root.height;
+ if (overflowHeight > 0) {
+ activeOverlayLoader.y = Math.max(0, y - overflowHeight)
+ }
+ }
+
+ Timer {
+ id: adjustPosition
+ interval: 0
+ onTriggered: activeOverlayLoader.adjustY();
+ }
}
}
diff --git a/src/GUI/qml/OverlayMenu.qml b/src/GUI/qml/OverlayMenu.qml
new file mode 100644
index 000000000..fbe293631
--- /dev/null
+++ b/src/GUI/qml/OverlayMenu.qml
@@ -0,0 +1,100 @@
+import QtQuick 2.4
+import FlightGear 1.0
+import "."
+
+Rectangle {
+ id: root
+
+ property var model: undefined
+ property int currentIndex: 0
+ property string headerText: ""
+ property int maximumPermittedHeight: 450
+
+ color: "white"
+ border.width: 1
+ border.color: Style.minorFrameColor
+ width: flick.width + Style.margin
+ height: flick.height + Style.margin
+ clip: true
+
+ // Overlay control properties
+ property bool dismissOnClickOutside: true
+ property bool adjustYPosition: true
+
+ signal select(var index);
+
+ Component.onCompleted: {
+ computeMenuWidth();
+ }
+
+ function close()
+ {
+ OverlayShared.globalOverlay.dismissOverlay()
+ }
+
+ function doSelect(index)
+ {
+ select(index);
+ close();
+ }
+
+ function computeMenuWidth()
+ {
+ var minWidth = 0;
+ for (var i = 0; i < choicesRepeater.count; i++) {
+ minWidth = Math.max(minWidth, choicesRepeater.itemAt(i).implicitWidth);
+ }
+ if (root.haveHeader())
+ minWidth = Math.max(minWidth, header.width);
+ flick.width = minWidth + scroller.width
+ }
+
+ function haveHeader()
+ {
+ return headerText !== "";
+ }
+
+ Flickable {
+ id: flick
+ anchors.centerIn: parent
+
+ height: Math.min(root.maximumPermittedHeight, contentHeight);
+ contentHeight: itemsColumn.childrenRect.height
+
+ Column {
+ id: itemsColumn
+ width: flick.width
+ spacing: Style.margin
+
+ PopupChoiceItem {
+ id: header
+ text: root.headerText
+ visible: root.haveHeader()
+ onSelect: root.doSelect(-1);
+ }
+
+ // main item repeater
+ Repeater {
+ id: choicesRepeater
+ model: root.model
+ delegate: PopupChoiceItem {
+ isCurrentIndex: root.currentIndex === model.index
+ onSelect: root.doSelect(index)
+ text: model && model.hasOwnProperty(displayRole) ? model[displayRole] // Qml ListModel and QAbstractItemModel
+ : modelData && modelData.hasOwnProperty(displayRole) ? modelData[displayRole] // QObjectList / QObject
+ : modelData != undefined ? modelData : "" // Models without role
+ }
+ } // menu item repeater
+ } // of menu contents column
+ }
+
+ Scrollbar {
+ id: scroller
+ flickable: flick
+ visible: flick.contentHeight > flick.height
+ anchors.right: root.right
+ anchors.rightMargin: 1
+ anchors.verticalCenter: parent.verticalCenter
+ height: flick.height
+ }
+}
diff --git a/src/GUI/qml/PopupChoice.qml b/src/GUI/qml/PopupChoice.qml
index fadc2b67e..4e596dfaf 100644
--- a/src/GUI/qml/PopupChoice.qml
+++ b/src/GUI/qml/PopupChoice.qml
@@ -1,7 +1,6 @@
import QtQuick 2.4
-import QtQuick.Window 2.0
import FlightGear.Launcher 1.0
-
+import FlightGear 1.0
import "."
Item {
@@ -123,109 +122,20 @@ Item {
hoverEnabled: root.enabled
enabled: root.enabled
onClicked: {
- var screenPos = _launcher.mapToGlobal(currentChoiceText, Qt.point(0, -currentChoiceText.height * currentIndex))
- if (screenPos.y < 0) {
- // if the popup would appear off the screen, use the first item
- // position instead
- screenPos = _launcher.mapToGlobal(currentChoiceText, Qt.point(0, 0))
- }
-
- popupFrame.x = screenPos.x;
- popupFrame.y = screenPos.y;
- popupFrame.visible = true
- tracker.window = popupFrame
+ OverlayShared.globalOverlay.showOverlayAtItemOffset(menu, root,
+ Qt.point(currentChoiceFrame.x, root.height))
}
}
- PopupWindowTracker {
- id: tracker
+ Component {
+ id: menu
+ OverlayMenu {
+ currentIndex: root.currentIndex
+ model: root.model
+ headerText: root.headerText
+ onSelect: root.select(index)
+ }
}
- Window {
- id: popupFrame
- flags: Qt.Popup
- height: choicesColumn.childrenRect.height + Style.margin * 2
- width: choicesColumn.width + Style.margin * 2
- visible: false
- color: "white"
-
- Rectangle {
- border.width: 1
- border.color: Style.minorFrameColor
- anchors.fill: parent
- }
-
- // choice layout column
- Column {
- id: choicesColumn
- spacing: Style.margin
- x: Style.margin
- y: Style.margin
- width: menuWidth
-
- // optional header component:
- StyledText {
- id: header
- text: root.headerText
- visible: root.haveHeader();
- height: implicitHeight + Style.margin
-
- // 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: {
- popupFrame.visible = false
- root.select(-1);
- }
- }
- }
-
- function calculateMenuWidth()
- {
- var minWidth = 0;
- for (var i = 0; i < choicesRepeater.count; i++) {
- minWidth = Math.max(minWidth, choicesRepeater.itemAt(i).implicitWidth);
- }
- if (root.haveHeader())
- minWidth = Math.max(minWidth, header.width);
- return minWidth;
- }
-
- readonly property int menuWidth: calculateMenuWidth()
-
- // main item repeater
- Repeater {
- id: choicesRepeater
- model: root.model
- delegate:
- Text {
- id: choiceText
- readonly property bool selected: root.currentIndex === model.index
-
- // Taken from TableViewItemDelegateLoader.qml to follow QML role conventions
- text: model && model.hasOwnProperty(displayRole) ? model[displayRole] // Qml ListModel and QAbstractItemModel
- : modelData && modelData.hasOwnProperty(displayRole) ? modelData[displayRole] // QObjectList / QObject
- : modelData != undefined ? modelData : "" // Models without role
- height: implicitHeight + Style.margin
- font.pixelSize: Style.baseFontPixelSize
- color: choiceArea.containsMouse ? Style.themeColor : Style.baseTextColor
-
- MouseArea {
- id: choiceArea
- width: popupFrame.width // full width of the popup
- height: parent.height
- hoverEnabled: true
-
- onClicked: {
- popupFrame.visible = false
- root.select(model.index)
- }
- }
- } // of Text delegate
- } // text repeater
- } // text column
- } // of popup Window
}
diff --git a/src/GUI/qml/PopupChoiceItem.qml b/src/GUI/qml/PopupChoiceItem.qml
new file mode 100644
index 000000000..d783b616e
--- /dev/null
+++ b/src/GUI/qml/PopupChoiceItem.qml
@@ -0,0 +1,21 @@
+import QtQuick 2.4
+import "."
+
+Text {
+ id: choiceText
+ property bool isCurrentIndex: false
+
+ signal select(var index);
+
+ height: implicitHeight + Style.margin
+ font.pixelSize: Style.baseFontPixelSize
+ color: choiceArea.containsMouse ? Style.themeColor : Style.baseTextColor
+
+ MouseArea {
+ id: choiceArea
+ width: flick.width // full width of the popup
+ height: parent.height
+ hoverEnabled: true
+ onClicked: choiceText.select(model.index)
+ }
+} // of Text delegate
diff --git a/src/GUI/resources.qrc b/src/GUI/resources.qrc
index 42c97fb5a..4be553cbc 100644
--- a/src/GUI/resources.qrc
+++ b/src/GUI/resources.qrc
@@ -121,6 +121,8 @@
qml/PlanAirportView.qml
qml/PlanRouteDetails.qml
qml/RouteLegsView.qml
+ qml/OverlayMenu.qml
+ qml/PopupChoiceItem.qml
preview-close.png