mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
SessionMenuTab: add setting for large button layout
This commit is contained in:
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user