SessionMenuTab: add setting for large button layout

This commit is contained in:
Ly-sec
2025-12-16 20:10:28 +01:00
parent d4f39c6b75
commit 762e828382
16 changed files with 449 additions and 119 deletions
+284 -8
View File
@@ -15,8 +15,17 @@ import qs.Widgets
SmartPanel {
id: root
preferredWidth: Math.round(440 * Style.uiScaleRatio)
readonly property bool largeButtonsStyle: Settings.data.sessionMenu.largeButtonsStyle || false
// Make panel background transparent for large buttons style
panelBackgroundColor: largeButtonsStyle ? Color.transparent : Color.mSurface
preferredWidth: largeButtonsStyle ? 0 : Math.round(440 * Style.uiScaleRatio)
preferredWidthRatio: largeButtonsStyle ? 1.0 : 0
preferredHeight: {
if (largeButtonsStyle) {
return 0; // Use ratio instead
}
var headerHeight = Settings.data.sessionMenu.showHeader ? Style.baseWidgetSize * 0.6 : 0;
var dividerHeight = Settings.data.sessionMenu.showHeader ? Style.marginS : 0;
@@ -30,16 +39,17 @@ SmartPanel {
return Math.round(baseHeight + buttonsHeight);
}
preferredHeightRatio: largeButtonsStyle ? 1.0 : 0
// Positioning
// Positioning - large buttons style is always centered and fullscreen
readonly property string panelPosition: Settings.data.sessionMenu.position
panelAnchorHorizontalCenter: panelPosition === "center" || panelPosition.endsWith("_center")
panelAnchorVerticalCenter: panelPosition === "center"
panelAnchorLeft: panelPosition !== "center" && panelPosition.endsWith("_left")
panelAnchorRight: panelPosition !== "center" && panelPosition.endsWith("_right")
panelAnchorBottom: panelPosition.startsWith("bottom_")
panelAnchorTop: panelPosition.startsWith("top_")
panelAnchorHorizontalCenter: largeButtonsStyle || panelPosition === "center" || panelPosition.endsWith("_center")
panelAnchorVerticalCenter: largeButtonsStyle || panelPosition === "center"
panelAnchorLeft: !largeButtonsStyle && panelPosition !== "center" && panelPosition.endsWith("_left")
panelAnchorRight: !largeButtonsStyle && panelPosition !== "center" && panelPosition.endsWith("_right")
panelAnchorBottom: !largeButtonsStyle && panelPosition.startsWith("bottom_")
panelAnchorTop: !largeButtonsStyle && panelPosition.startsWith("top_")
// SessionMenu handle it's own closing logic
property bool closeWithEscape: false
@@ -339,6 +349,10 @@ SmartPanel {
id: ui
color: Color.transparent
// For large buttons style, use full screen dimensions
readonly property var contentPreferredWidth: largeButtonsStyle ? (root.screen?.width || root.width || 0) : undefined
readonly property var contentPreferredHeight: largeButtonsStyle ? (root.screen?.height || root.height || 0) : undefined
// Navigation functions
function selectFirst() {
root.selectFirst();
@@ -360,7 +374,72 @@ SmartPanel {
root.activate();
}
// Timer text for large buttons style (above buttons) - positioned absolutely with background
Rectangle {
id: timerTextContainer
visible: largeButtonsStyle && timerActive
anchors.bottom: largeButtonsContainer.top
anchors.horizontalCenter: largeButtonsContainer.horizontalCenter
anchors.bottomMargin: Style.marginM
width: timerText.width + Style.marginXL * 2
height: timerText.height + Style.marginL * 2
radius: Style.radiusM
color: Qt.alpha(Color.mSurface, 0.9)
border.color: Color.mOutline
border.width: Style.borderS
z: 1000
NText {
id: timerText
anchors.centerIn: parent
text: I18n.tr("session-menu.action-in-seconds", {
"action": I18n.tr("session-menu." + pendingAction),
"seconds": Math.ceil(timeRemaining / 1000)
})
font.weight: Style.fontWeightBold
pointSize: Style.fontSizeL
color: Color.mOnSurface
}
}
// Large buttons style layout container
ColumnLayout {
id: largeButtonsContainer
visible: largeButtonsStyle
anchors.centerIn: parent
// Large buttons style layout (grid)
GridLayout {
id: largeButtonsGrid
Layout.alignment: Qt.AlignHCenter
columns: Math.min(3, Math.ceil(Math.sqrt(powerOptions.length)))
rowSpacing: Style.marginXL
columnSpacing: Style.marginXL
width: columns * 200 * Style.uiScaleRatio + (columns - 1) * Style.marginXL
height: Math.ceil(powerOptions.length / columns) * 200 * Style.uiScaleRatio + (Math.ceil(powerOptions.length / columns) - 1) * Style.marginXL
Repeater {
model: powerOptions
delegate: LargeButton {
Layout.preferredWidth: 200 * Style.uiScaleRatio
Layout.preferredHeight: 200 * Style.uiScaleRatio
icon: modelData.icon
title: modelData.title
isShutdown: modelData.isShutdown || false
isSelected: index === selectedIndex
onClicked: {
selectedIndex = index;
startTimer(modelData.action);
}
pending: timerActive && pendingAction === modelData.action
}
}
}
}
// Normal style layout
NBox {
visible: !largeButtonsStyle
anchors.fill: parent
anchors.margins: Style.marginL
@@ -437,6 +516,24 @@ SmartPanel {
}
}
}
// Background MouseArea for large buttons style - closes panel when clicking outside buttons
MouseArea {
visible: largeButtonsStyle
anchors.fill: parent
z: -1
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onClicked: mouse => {
// Only close if not clicking on a button
// The buttons are above this MouseArea, so clicks on them won't reach here
if (timerActive) {
// Cancel countdown if active
cancelTimer();
} else {
root.close();
}
}
}
}
// Custom power button component
@@ -567,4 +664,183 @@ SmartPanel {
onClicked: buttonRoot.clicked()
}
}
// Large buttons style button component
component LargeButton: Rectangle {
id: largeButtonRoot
property string icon: ""
property string title: ""
property bool pending: false
property bool isShutdown: false
property bool isSelected: false
signal clicked
property real hoverScale: (isSelected || mouseArea.containsMouse) ? 1.05 : 1.0
radius: Style.radiusL
color: {
if (pending) {
return Qt.alpha(Color.mPrimary, 1.0);
}
if (isSelected || mouseArea.containsMouse) {
return Qt.alpha(Color.mPrimary, 1.0);
}
return Qt.alpha(Color.mSurfaceVariant, 0.65);
}
border.width: pending ? Style.borderM : (isSelected || mouseArea.containsMouse ? Style.borderM : 0)
border.color: pending ? Color.mPrimary : (isSelected || mouseArea.containsMouse ? Qt.alpha(Color.mPrimary, 0.5) : Color.transparent)
// Scale transform for hover effect
transform: Scale {
origin.x: largeButtonRoot.width / 2
origin.y: largeButtonRoot.height / 2
xScale: hoverScale
yScale: hoverScale
}
// Subtle shadow/glow effect
layer.enabled: isSelected || mouseArea.containsMouse || pending
layer.effect: MultiEffect {
shadowEnabled: true
shadowBlur: 20
shadowOpacity: 0.3
shadowColor: pending ? Color.mPrimary : (isShutdown ? Color.mError : Color.mPrimary)
shadowHorizontalOffset: 0
shadowVerticalOffset: 0
}
Behavior on color {
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.OutCirc
}
}
Behavior on border.width {
NumberAnimation {
duration: Style.animationFast
easing.type: Easing.OutCirc
}
}
Behavior on hoverScale {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutBack
easing.overshoot: 0.5
}
}
ColumnLayout {
anchors.centerIn: parent
anchors.margins: Style.marginL
spacing: Style.marginM
// Large icon with scale animation
NIcon {
id: iconElement
Layout.alignment: Qt.AlignHCenter
icon: largeButtonRoot.icon
color: {
if (largeButtonRoot.pending)
return Color.mOnPrimary;
if (largeButtonRoot.isShutdown && !largeButtonRoot.isSelected && !mouseArea.containsMouse)
return Color.mError;
if (largeButtonRoot.isSelected || mouseArea.containsMouse)
return Color.mOnPrimary;
return Color.mOnSurface;
}
pointSize: Style.fontSizeXXXL * 2
width: 80 * Style.uiScaleRatio
height: 80 * Style.uiScaleRatio
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
property real iconScale: (largeButtonRoot.isSelected || mouseArea.containsMouse) ? 1.1 : 1.0
transform: Scale {
origin.x: iconElement.width / 2
origin.y: iconElement.height / 2
xScale: iconElement.iconScale
yScale: iconElement.iconScale
}
Behavior on color {
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.OutCirc
}
}
Behavior on iconScale {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutBack
easing.overshoot: 0.6
}
}
}
// Title text
NText {
Layout.alignment: Qt.AlignHCenter
text: largeButtonRoot.title
font.weight: (largeButtonRoot.isSelected || mouseArea.containsMouse) ? Style.fontWeightBold : Style.fontWeightMedium
pointSize: Style.fontSizeL
color: {
if (largeButtonRoot.pending)
return Color.mOnPrimary;
if (largeButtonRoot.isShutdown && !largeButtonRoot.isSelected && !mouseArea.containsMouse)
return Color.mError;
if (largeButtonRoot.isSelected || mouseArea.containsMouse)
return Color.mOnPrimary;
return Color.mOnSurface;
}
Behavior on color {
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.OutCirc
}
}
Behavior on font.weight {
PropertyAnimation {
duration: Style.animationFast
}
}
}
// Pending indicator
Rectangle {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: -Style.marginM
width: 30
height: 30
radius: Math.min(Style.radiusL, width / 2)
color: Color.mPrimary
visible: largeButtonRoot.pending
NText {
anchors.centerIn: parent
text: Math.ceil(timeRemaining / 1000)
pointSize: Style.fontSizeM
font.weight: Style.fontWeightBold
color: Color.mOnPrimary
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: largeButtonRoot.clicked()
}
}
}