mirror of
https://github.com/end-4/dots-hyprland.git
synced 2026-06-05 23:09:26 -05:00
make dropdown options like a overlay
This commit is contained in:
@@ -0,0 +1,264 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import qs.modules.common.functions
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
implicitWidth: dropdownButton.implicitWidth
|
||||||
|
implicitHeight: dropdownButton.implicitHeight
|
||||||
|
|
||||||
|
property list<var> options: []
|
||||||
|
property var currentValue: null
|
||||||
|
|
||||||
|
property string buttonText: ""
|
||||||
|
property string buttonIcon: ""
|
||||||
|
property string placeholder: "Select..."
|
||||||
|
property Component buttonContent: null
|
||||||
|
|
||||||
|
property Component itemDelegate: null
|
||||||
|
property real maxPopupHeight: 300
|
||||||
|
property real popupWidth: dropdownButton.width
|
||||||
|
|
||||||
|
property real buttonRadius: dropdownButton.height / 2
|
||||||
|
property color buttonBackground: Appearance.colors.colSecondaryContainer
|
||||||
|
property color buttonBackgroundHover: Appearance.colors.colSecondaryContainerHover
|
||||||
|
property color buttonBackgroundActive: Appearance.colors.colSecondaryContainerActive
|
||||||
|
|
||||||
|
property bool popupVisible: false
|
||||||
|
|
||||||
|
signal selected(var newValue)
|
||||||
|
|
||||||
|
function findWindowRoot() {
|
||||||
|
var p = root.parent;
|
||||||
|
while (p && p.parent) {
|
||||||
|
p = p.parent;
|
||||||
|
}
|
||||||
|
return p || root.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGlobalPosition() {
|
||||||
|
var windowRoot = findWindowRoot();
|
||||||
|
if (windowRoot) {
|
||||||
|
return root.mapToItem(windowRoot, 0, 0);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentDisplayName() {
|
||||||
|
for (let i = 0; i < options.length; i++) {
|
||||||
|
if (options[i].value === currentValue) {
|
||||||
|
return options[i].displayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return placeholder;
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupButton {
|
||||||
|
id: dropdownButton
|
||||||
|
anchors.fill: parent
|
||||||
|
buttonRadius: root.buttonRadius
|
||||||
|
buttonRadiusPressed: root.buttonRadius
|
||||||
|
leftRadius: root.buttonRadius
|
||||||
|
rightRadius: root.buttonRadius
|
||||||
|
horizontalPadding: 16
|
||||||
|
verticalPadding: 10
|
||||||
|
colBackground: root.buttonBackground
|
||||||
|
colBackgroundHover: root.buttonBackgroundHover
|
||||||
|
colBackgroundActive: root.buttonBackgroundActive
|
||||||
|
|
||||||
|
contentItem: Loader {
|
||||||
|
sourceComponent: root.buttonContent || defaultButtonContent
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.popupVisible = !root.popupVisible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: defaultButtonContent
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
active: root.buttonIcon.length > 0
|
||||||
|
visible: active
|
||||||
|
sourceComponent: MaterialSymbol {
|
||||||
|
text: root.buttonIcon
|
||||||
|
iconSize: Appearance.font.pixelSize.larger
|
||||||
|
color: Appearance.colors.colOnSecondaryContainer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
color: Appearance.colors.colOnSecondaryContainer
|
||||||
|
text: root.buttonText || root.getCurrentDisplayName()
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialSymbol {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
text: root.popupVisible ? "arrow_drop_up" : "arrow_drop_down"
|
||||||
|
iconSize: Appearance.font.pixelSize.larger
|
||||||
|
color: Appearance.colors.colOnSecondaryContainer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: scrim
|
||||||
|
visible: root.popupVisible
|
||||||
|
parent: root.findWindowRoot()
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "transparent"
|
||||||
|
z: 999
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
root.popupVisible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: popupContent
|
||||||
|
visible: root.popupVisible
|
||||||
|
z: 1000
|
||||||
|
|
||||||
|
parent: root.findWindowRoot()
|
||||||
|
|
||||||
|
width: root.popupWidth
|
||||||
|
height: Math.min(optionColumn.implicitHeight + 16, root.maxPopupHeight)
|
||||||
|
|
||||||
|
radius: Appearance.rounding.normal
|
||||||
|
color: Appearance.colors.colSurfaceContainerHigh
|
||||||
|
border.width: 1
|
||||||
|
border.color: Appearance.colors.colOutline
|
||||||
|
|
||||||
|
Flickable {
|
||||||
|
id: scrollView
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 8
|
||||||
|
contentHeight: optionColumn.implicitHeight
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: optionColumn
|
||||||
|
width: scrollView.width
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.options
|
||||||
|
|
||||||
|
delegate: Loader {
|
||||||
|
id: itemLoader
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
sourceComponent: root.itemDelegate || defaultItemDelegate
|
||||||
|
|
||||||
|
onLoaded: {
|
||||||
|
if (item) {
|
||||||
|
item.modelData = Qt.binding(() => modelData);
|
||||||
|
item.index = Qt.binding(() => index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opacity: visible ? 1 : 0
|
||||||
|
scale: visible ? 1 : 0.95
|
||||||
|
transformOrigin: Item.Top
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: defaultItemDelegate
|
||||||
|
|
||||||
|
GroupButton {
|
||||||
|
id: optionButton
|
||||||
|
property var modelData
|
||||||
|
property int index
|
||||||
|
|
||||||
|
buttonText: modelData?.displayName || ""
|
||||||
|
toggled: modelData?.value === root.currentValue
|
||||||
|
buttonRadius: Appearance.rounding.small
|
||||||
|
buttonRadiusPressed: Appearance.rounding.small
|
||||||
|
leftRadius: Appearance.rounding.small
|
||||||
|
rightRadius: Appearance.rounding.small
|
||||||
|
horizontalPadding: 12
|
||||||
|
verticalPadding: 8
|
||||||
|
colBackground: "transparent"
|
||||||
|
colBackgroundHover: Appearance.colors.colSecondaryContainerHover
|
||||||
|
colBackgroundActive: Appearance.colors.colSecondaryContainerActive
|
||||||
|
colBackgroundToggled: Appearance.colors.colPrimary
|
||||||
|
colBackgroundToggledHover: Appearance.colors.colPrimaryHover
|
||||||
|
colBackgroundToggledActive: Appearance.colors.colPrimaryActive
|
||||||
|
|
||||||
|
contentItem: RowLayout {
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
MaterialSymbol {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
text: "check"
|
||||||
|
iconSize: Appearance.font.pixelSize.normal
|
||||||
|
color: optionButton.toggled ? Appearance.colors.colOnPrimary : Appearance.colors.colOnSecondaryContainer
|
||||||
|
opacity: optionButton.toggled ? 1 : 0
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
color: optionButton.toggled ? Appearance.colors.colOnPrimary : Appearance.colors.colOnSecondaryContainer
|
||||||
|
text: optionButton.buttonText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.selected(modelData.value);
|
||||||
|
root.popupVisible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.modules.common.functions
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: root
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
property list<var> options: []
|
|
||||||
property var currentValue: null
|
|
||||||
|
|
||||||
signal selected(var newValue)
|
|
||||||
|
|
||||||
function getCurrentDisplayName() {
|
|
||||||
for (let i = 0; i < options.length; i++) {
|
|
||||||
if (options[i].value === currentValue) {
|
|
||||||
return options[i].displayName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "Select language";
|
|
||||||
}
|
|
||||||
|
|
||||||
GroupButton {
|
|
||||||
id: dropdownButton
|
|
||||||
Layout.fillWidth: true
|
|
||||||
buttonRadius: height / 2
|
|
||||||
buttonRadiusPressed: height / 2
|
|
||||||
leftRadius: height / 2
|
|
||||||
rightRadius: height / 2
|
|
||||||
horizontalPadding: 16
|
|
||||||
verticalPadding: 10
|
|
||||||
colBackground: Appearance.colors.colSecondaryContainer
|
|
||||||
colBackgroundHover: Appearance.colors.colSecondaryContainerHover
|
|
||||||
colBackgroundActive: Appearance.colors.colSecondaryContainerActive
|
|
||||||
|
|
||||||
contentItem: RowLayout {
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
MaterialSymbol {
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
text: "language"
|
|
||||||
iconSize: Appearance.font.pixelSize.larger
|
|
||||||
color: Appearance.colors.colOnSecondaryContainer
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
color: Appearance.colors.colOnSecondaryContainer
|
|
||||||
text: root.getCurrentDisplayName()
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialSymbol {
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
text: dropdownPopup.visible ? "arrow_drop_up" : "arrow_drop_down"
|
|
||||||
iconSize: Appearance.font.pixelSize.larger
|
|
||||||
color: Appearance.colors.colOnSecondaryContainer
|
|
||||||
|
|
||||||
Behavior on text {
|
|
||||||
enabled: Appearance.animation.elementMoveFast.numberAnimation !== undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
dropdownPopup.visible = !dropdownPopup.visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: dropdownPopup
|
|
||||||
visible: false
|
|
||||||
Layout.fillWidth: true
|
|
||||||
implicitHeight: visible ? Math.min(optionColumn.implicitHeight + 16, 300) : 0
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: Appearance.rounding.normal
|
|
||||||
color: Appearance.colors.colSurfaceContainerHigh
|
|
||||||
border.width: 1
|
|
||||||
border.color: Appearance.colors.colOutline
|
|
||||||
|
|
||||||
Flickable {
|
|
||||||
id: scrollView
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: 8
|
|
||||||
contentHeight: optionColumn.implicitHeight
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: optionColumn
|
|
||||||
width: parent.width
|
|
||||||
spacing: 2
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: root.options
|
|
||||||
|
|
||||||
delegate: GroupButton {
|
|
||||||
id: optionButton
|
|
||||||
required property var modelData
|
|
||||||
required property int index
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
buttonText: modelData.displayName
|
|
||||||
toggled: modelData.value === root.currentValue
|
|
||||||
buttonRadius: Appearance.rounding.small
|
|
||||||
buttonRadiusPressed: Appearance.rounding.small
|
|
||||||
leftRadius: Appearance.rounding.small
|
|
||||||
rightRadius: Appearance.rounding.small
|
|
||||||
horizontalPadding: 12
|
|
||||||
verticalPadding: 8
|
|
||||||
colBackground: "transparent"
|
|
||||||
colBackgroundHover: Appearance.colors.colSecondaryContainerHover
|
|
||||||
colBackgroundActive: Appearance.colors.colSecondaryContainerActive
|
|
||||||
colBackgroundToggled: Appearance.colors.colPrimary
|
|
||||||
colBackgroundToggledHover: Appearance.colors.colPrimaryHover
|
|
||||||
colBackgroundToggledActive: Appearance.colors.colPrimaryActive
|
|
||||||
|
|
||||||
contentItem: RowLayout {
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
MaterialSymbol {
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
text: "check"
|
|
||||||
iconSize: Appearance.font.pixelSize.normal
|
|
||||||
color: optionButton.toggled ? Appearance.colors.colOnPrimary : Appearance.colors.colOnSecondaryContainer
|
|
||||||
opacity: optionButton.toggled ? 1 : 0
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
enabled: Appearance.animation.elementMoveFast.numberAnimation !== undefined
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 200
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
color: optionButton.toggled ? Appearance.colors.colOnPrimary : Appearance.colors.colOnSecondaryContainer
|
|
||||||
text: optionButton.buttonText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
root.selected(modelData.value);
|
|
||||||
dropdownPopup.visible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on implicitHeight {
|
|
||||||
enabled: Appearance.animation.elementMoveFast.numberAnimation !== undefined
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 200
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
opacity: visible ? 1 : 0
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
enabled: Appearance.animation.elementMoveFast.numberAnimation !== undefined
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 200
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -137,8 +137,9 @@ ContentPage {
|
|||||||
title: Translation.tr("Interface Language")
|
title: Translation.tr("Interface Language")
|
||||||
tooltip: Translation.tr("Select the language for the user interface.\n\"Auto\" will use your system's locale.")
|
tooltip: Translation.tr("Select the language for the user interface.\n\"Auto\" will use your system's locale.")
|
||||||
|
|
||||||
LanguageDropdownButton {
|
DropdownButton {
|
||||||
id: languageSelector
|
id: languageSelector
|
||||||
|
buttonIcon: "language"
|
||||||
currentValue: Config.options.language.ui
|
currentValue: Config.options.language.ui
|
||||||
onSelected: newValue => {
|
onSelected: newValue => {
|
||||||
Config.options.language.ui = newValue;
|
Config.options.language.ui = newValue;
|
||||||
|
|||||||
Reference in New Issue
Block a user