feat(StatusQ.Controls): introduce `StatusSelect`
This introduces a new `StatusSelect` component which is a select form control. The `model` property can be used to apply a `ListModel` for dynamic data. To give users full control over what the menu items look like, `StatusSelect` exposes a `selectMenu.delegate` property. Most of the time this should be a `StatusMenuItemDelegate` to get access to the comple `MenuItem` component (remember that `StatusMenuItem` is merely an `Action` type). `StatusMenuItemDelegate` derives most of its behaviour by its applied `action`, so the easiest way to construct a dynamic select with StatusQ menu item look and feel is a combination of `StatusMenuItemDelegate` and `StatusMenuItem` as shown below. Further more, because `StatusSelect` can't know what the `delegate` is going to look like it also can't decide what data goes into a `selectedItem`. Therefore, it offers another API, the `selectedItemComponent` which can be any component. This component can then be accessed by menu item actions to set corresponding properties. Usage: ```qml import StatusQ.Controls 0.1 StatusSelect { label: "Some label" model: ListModel { ListElement { name: "Pascal" } ListElement { name: "Khushboo" } ListElement { name: "Alexandra" } ListElement { name: "Eric" } } selectMenu.delegate: StatusMenuItemDelegate { statusPopupMenu: select action: StatusMenuItem { iconSettings.name: "filled-account" text: name onTriggered: { selectedItem.text = name } } } selectedItemComponent: Item { id: selectedItem anchors.fill: parent property string text: "" StatusBaseText { text: selectedItem.text anchors.centerIn: parent color: Theme.palette.directColor1 } } } ``` Closes #436
This commit is contained in:
parent
ec2aeffc55
commit
03c6da6e9f
|
@ -0,0 +1,54 @@
|
||||||
|
import QtQuick 2.14
|
||||||
|
import QtQuick.Controls 2.14
|
||||||
|
import QtQuick.Layouts 1.14
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Popups 0.1
|
||||||
|
|
||||||
|
import Sandbox 0.1
|
||||||
|
Column {
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
StatusSelect {
|
||||||
|
id: select
|
||||||
|
label: "Some label"
|
||||||
|
model: ListModel {
|
||||||
|
ListElement {
|
||||||
|
name: "Pascal"
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
name: "Khushboo"
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
name: "Alexandra"
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
name: "Eric"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectMenu.delegate: StatusMenuItemDelegate {
|
||||||
|
statusPopupMenu: select
|
||||||
|
action: StatusMenuItem {
|
||||||
|
iconSettings.name: "filled-account"
|
||||||
|
text: name
|
||||||
|
onTriggered: {
|
||||||
|
selectedItem.text = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectedItemComponent: Item {
|
||||||
|
id: selectedItem
|
||||||
|
anchors.fill: parent
|
||||||
|
property string text: ""
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
text: selectedItem.text
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: Theme.palette.directColor1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -153,6 +153,11 @@ StatusWindow {
|
||||||
selected: page.sourceComponent == statusInputPageComponent
|
selected: page.sourceComponent == statusInputPageComponent
|
||||||
onClicked: page.sourceComponent = statusInputPageComponent
|
onClicked: page.sourceComponent = statusInputPageComponent
|
||||||
}
|
}
|
||||||
|
StatusNavigationListItem {
|
||||||
|
title: "StatusSelect"
|
||||||
|
selected: page.sourceComponent == statusSelectPageComponent
|
||||||
|
onClicked: page.sourceComponent = statusSelectPageComponent
|
||||||
|
}
|
||||||
StatusListSectionHeadline { text: "StatusQ.Components" }
|
StatusListSectionHeadline { text: "StatusQ.Components" }
|
||||||
StatusNavigationListItem {
|
StatusNavigationListItem {
|
||||||
title: "List Items"
|
title: "List Items"
|
||||||
|
@ -256,6 +261,11 @@ StatusWindow {
|
||||||
StatusInputPage {}
|
StatusInputPage {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: statusSelectPageComponent
|
||||||
|
StatusSelectPage {}
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: listItemsComponent
|
id: listItemsComponent
|
||||||
ListItems {}
|
ListItems {}
|
||||||
|
|
|
@ -0,0 +1,175 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import QtQuick.Controls 2.13
|
||||||
|
import QtQuick.Layouts 1.13
|
||||||
|
import QtGraphicalEffects 1.13
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Popups 0.1
|
||||||
|
|
||||||
|
Item {
|
||||||
|
enum MenuAlignment {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Center
|
||||||
|
}
|
||||||
|
property string label: ""
|
||||||
|
readonly property bool hasLabel: label !== ""
|
||||||
|
property color bgColor: Theme.palette.baseColor2
|
||||||
|
readonly property int labelMargin: 7
|
||||||
|
property var model
|
||||||
|
property alias selectMenu: selectMenu
|
||||||
|
property color bgColorHover: bgColor
|
||||||
|
property alias selectedItemComponent: selectedItemContainer.children
|
||||||
|
property int caretRightMargin: 16
|
||||||
|
property alias select: inputRectangle
|
||||||
|
property int menuAlignment: StatusSelect.MenuAlignment.Right
|
||||||
|
property Item zeroItemsView: Item {}
|
||||||
|
property string validationError: ""
|
||||||
|
property alias validationErrorAlignment: validationErrorText.horizontalAlignment
|
||||||
|
property int validationErrorTopMargin: 11
|
||||||
|
implicitWidth: 448
|
||||||
|
|
||||||
|
id: root
|
||||||
|
height: inputRectangle.height + (hasLabel ? inputLabel.height + labelMargin : 0) + (!!validationError ? (validationErrorText.height + validationErrorTopMargin) : 0)
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: inputLabel
|
||||||
|
visible: hasLabel
|
||||||
|
text: root.label
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 0
|
||||||
|
font.pixelSize: 15
|
||||||
|
color: root.enabled ? Theme.palette.directColor1 : Theme.palette.baseColor1
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
property bool hovered: false
|
||||||
|
id: inputRectangle
|
||||||
|
height: 56
|
||||||
|
color: hovered ? bgColorHover : bgColor
|
||||||
|
radius: 8
|
||||||
|
anchors.top: root.hasLabel ? inputLabel.bottom : parent.top
|
||||||
|
anchors.topMargin: root.hasLabel ? root.labelMargin : 0
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.left: parent.left
|
||||||
|
border.width: !!validationError ? 1 : 0
|
||||||
|
border.color: Theme.palette.dangerColor1
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: selectedItemContainer
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusIcon {
|
||||||
|
id: caret
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: caretRightMargin
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
icon: "chevron-down"
|
||||||
|
color: Theme.palette.baseColor1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: selectMenu.width
|
||||||
|
height: selectMenu.height
|
||||||
|
x: selectMenu.x
|
||||||
|
y: selectMenu.y
|
||||||
|
visible: selectMenu.opened
|
||||||
|
color: Theme.palette.statusSelect.menuItemBackgroundColor
|
||||||
|
radius: 8
|
||||||
|
border.color: Theme.palette.baseColor2
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
verticalOffset: 3
|
||||||
|
radius: 8
|
||||||
|
samples: 15
|
||||||
|
fast: true
|
||||||
|
cached: true
|
||||||
|
color: Theme.palette.dropShadow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusPopupMenu {
|
||||||
|
id: selectMenu
|
||||||
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
||||||
|
width: parent.width
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: menuItems
|
||||||
|
model: root.model
|
||||||
|
property int zeroItemsViewHeight
|
||||||
|
delegate: selectMenu.delegate
|
||||||
|
onItemAdded: {
|
||||||
|
root.zeroItemsView.visible = false
|
||||||
|
root.zeroItemsView.height = 0
|
||||||
|
}
|
||||||
|
onItemRemoved: {
|
||||||
|
if (count === 0) {
|
||||||
|
root.zeroItemsView.visible = true
|
||||||
|
root.zeroItemsView.height = zeroItemsViewHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Component.onCompleted: {
|
||||||
|
zeroItemsViewHeight = root.zeroItemsView.height
|
||||||
|
root.zeroItemsView.visible = count === 0
|
||||||
|
root.zeroItemsView.height = count !== 0 ? 0 : root.zeroItemsView.height
|
||||||
|
selectMenu.insertItem(0, root.zeroItemsView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: validationErrorText
|
||||||
|
visible: !!validationError
|
||||||
|
text: validationError
|
||||||
|
anchors.top: inputRectangle.bottom
|
||||||
|
anchors.topMargin: validationErrorTopMargin
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.left: parent.left
|
||||||
|
font.pixelSize: 12
|
||||||
|
height: visible ? implicitHeight : 0
|
||||||
|
color: Theme.palette.dangerColor1
|
||||||
|
horizontalAlignment: TextEdit.AlignRight
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: inputRectangle
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
hoverEnabled: true
|
||||||
|
onEntered: {
|
||||||
|
inputRectangle.hovered = true
|
||||||
|
}
|
||||||
|
onExited: {
|
||||||
|
inputRectangle.hovered = false
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
if (selectMenu.opened) {
|
||||||
|
selectMenu.close()
|
||||||
|
} else {
|
||||||
|
const rightOffset = inputRectangle.width - selectMenu.width
|
||||||
|
let offset = rightOffset
|
||||||
|
switch (root.menuAlignment) {
|
||||||
|
case StatusSelect.MenuAlignment.Left:
|
||||||
|
offset = 0
|
||||||
|
break
|
||||||
|
case StatusSelect.MenuAlignment.Right:
|
||||||
|
offset = rightOffset
|
||||||
|
break
|
||||||
|
case StatusSelect.MenuAlignment.Center:
|
||||||
|
offset = rightOffset / 2
|
||||||
|
}
|
||||||
|
selectMenu.popup(inputRectangle.x + offset, inputRectangle.y + inputRectangle.height + 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ StatusSwitch 0.1 StatusSwitch.qml
|
||||||
StatusRadioButton 0.1 StatusRadioButton.qml
|
StatusRadioButton 0.1 StatusRadioButton.qml
|
||||||
StatusCheckBox 0.1 StatusCheckBox.qml
|
StatusCheckBox 0.1 StatusCheckBox.qml
|
||||||
StatusSlider 0.1 StatusSlider.qml
|
StatusSlider 0.1 StatusSlider.qml
|
||||||
|
StatusSelect 0.1 StatusSelect.qml
|
||||||
StatusBaseInput 0.1 StatusBaseInput.qml
|
StatusBaseInput 0.1 StatusBaseInput.qml
|
||||||
StatusInput 0.1 StatusInput.qml
|
StatusInput 0.1 StatusInput.qml
|
||||||
StatusPickerButton 0.1 StatusPickerButton.qml
|
StatusPickerButton 0.1 StatusPickerButton.qml
|
||||||
|
|
|
@ -205,5 +205,10 @@ ThemePalette {
|
||||||
property QtObject statusSwitchTab: QtObject {
|
property QtObject statusSwitchTab: QtObject {
|
||||||
property color backgroundColor: baseColor3
|
property color backgroundColor: baseColor3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property QtObject statusSelect: QtObject {
|
||||||
|
property color menuItemBackgroundColor: baseColor2
|
||||||
|
property color menuItemHoverBackgroundColor: directColor7
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -203,5 +203,10 @@ ThemePalette {
|
||||||
property QtObject statusSwitchTab: QtObject {
|
property QtObject statusSwitchTab: QtObject {
|
||||||
property color backgroundColor: white
|
property color backgroundColor: white
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property QtObject statusSelect: QtObject {
|
||||||
|
property color menuItemBackgroundColor: white
|
||||||
|
property color menuItemHoverBackgroundColor: baseColor2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -150,6 +150,11 @@ QtObject {
|
||||||
property color backgroundColor
|
property color backgroundColor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property QtObject statusSelect: QtObject {
|
||||||
|
property color menuItemBackgroundColor
|
||||||
|
property color menuItemHoeverBackgroundColor
|
||||||
|
}
|
||||||
|
|
||||||
function alphaColor(color, alpha) {
|
function alphaColor(color, alpha) {
|
||||||
let actualColor = Qt.darker(color, 1)
|
let actualColor = Qt.darker(color, 1)
|
||||||
actualColor.a = alpha
|
actualColor.a = alpha
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
<file>src/StatusQ/Controls/StatusSwitch.qml</file>
|
<file>src/StatusQ/Controls/StatusSwitch.qml</file>
|
||||||
<file>src/StatusQ/Controls/StatusCheckBox.qml</file>
|
<file>src/StatusQ/Controls/StatusCheckBox.qml</file>
|
||||||
<file>src/StatusQ/Controls/StatusButton.qml</file>
|
<file>src/StatusQ/Controls/StatusButton.qml</file>
|
||||||
|
<file>src/StatusQ/Controls/StatusSelect.qml</file>
|
||||||
<file>src/StatusQ/Controls/StatusToolTip.qml</file>
|
<file>src/StatusQ/Controls/StatusToolTip.qml</file>
|
||||||
<file>src/StatusQ/Controls/Validators/StatusValidator.qml</file>
|
<file>src/StatusQ/Controls/Validators/StatusValidator.qml</file>
|
||||||
<file>src/StatusQ/Controls/Validators/StatusMinLengthValidator.qml</file>
|
<file>src/StatusQ/Controls/Validators/StatusMinLengthValidator.qml</file>
|
||||||
|
|
Loading…
Reference in New Issue