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
0764e25a58
commit
6e10959e40
|
@ -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
|
||||
onClicked: page.sourceComponent = statusInputPageComponent
|
||||
}
|
||||
StatusNavigationListItem {
|
||||
title: "StatusSelect"
|
||||
selected: page.sourceComponent == statusSelectPageComponent
|
||||
onClicked: page.sourceComponent = statusSelectPageComponent
|
||||
}
|
||||
StatusListSectionHeadline { text: "StatusQ.Components" }
|
||||
StatusNavigationListItem {
|
||||
title: "List Items"
|
||||
|
@ -256,6 +261,11 @@ StatusWindow {
|
|||
StatusInputPage {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: statusSelectPageComponent
|
||||
StatusSelectPage {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: listItemsComponent
|
||||
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
|
||||
StatusCheckBox 0.1 StatusCheckBox.qml
|
||||
StatusSlider 0.1 StatusSlider.qml
|
||||
StatusSelect 0.1 StatusSelect.qml
|
||||
StatusBaseInput 0.1 StatusBaseInput.qml
|
||||
StatusInput 0.1 StatusInput.qml
|
||||
StatusPickerButton 0.1 StatusPickerButton.qml
|
||||
|
|
|
@ -205,5 +205,10 @@ ThemePalette {
|
|||
property QtObject statusSwitchTab: QtObject {
|
||||
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 color backgroundColor: white
|
||||
}
|
||||
|
||||
property QtObject statusSelect: QtObject {
|
||||
property color menuItemBackgroundColor: white
|
||||
property color menuItemHoverBackgroundColor: baseColor2
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -150,6 +150,11 @@ QtObject {
|
|||
property color backgroundColor
|
||||
}
|
||||
|
||||
property QtObject statusSelect: QtObject {
|
||||
property color menuItemBackgroundColor
|
||||
property color menuItemHoeverBackgroundColor
|
||||
}
|
||||
|
||||
function alphaColor(color, alpha) {
|
||||
let actualColor = Qt.darker(color, 1)
|
||||
actualColor.a = alpha
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
<file>src/StatusQ/Controls/StatusSwitch.qml</file>
|
||||
<file>src/StatusQ/Controls/StatusCheckBox.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/Validators/StatusValidator.qml</file>
|
||||
<file>src/StatusQ/Controls/Validators/StatusMinLengthValidator.qml</file>
|
||||
|
|
Loading…
Reference in New Issue