mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2026-06-07 20:31:31 +00:00
fix: Refine header layout and animations
This commit is contained in:
@@ -43,14 +43,20 @@ NPanel {
|
|||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: blueColumn
|
id: blueColumn
|
||||||
anchors.fill: parent
|
anchors.top: parent.top
|
||||||
anchors.margins: Style.marginM * scaling
|
anchors.left: parent.left
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.topMargin: Style.marginM * scaling
|
||||||
|
anchors.leftMargin: Style.marginM * scaling
|
||||||
|
anchors.bottomMargin: Style.marginM * scaling
|
||||||
|
anchors.rightMargin: clockItem.width + (Style.marginM * scaling * 2)
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
// Combined layout for weather icon, date, and weather text
|
// Combined layout for weather icon, date, and weather text
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: 60 * scaling
|
height: 60 * scaling
|
||||||
|
clip: true
|
||||||
spacing: Style.marginS * scaling
|
spacing: Style.marginS * scaling
|
||||||
|
|
||||||
// Weather icon and temperature
|
// Weather icon and temperature
|
||||||
@@ -70,14 +76,14 @@ NPanel {
|
|||||||
text: {
|
text: {
|
||||||
if (!weatherReady)
|
if (!weatherReady)
|
||||||
return ""
|
return ""
|
||||||
var temp = LocationService.data.weather.current_weather.temperature
|
var temp = LocationService.data.weather.current_weather.temperature
|
||||||
var suffix = "C"
|
var suffix = "C"
|
||||||
if (Settings.data.location.useFahrenheit) {
|
if (Settings.data.location.useFahrenheit) {
|
||||||
temp = LocationService.celsiusToFahrenheit(temp)
|
temp = LocationService.celsiusToFahrenheit(temp)
|
||||||
suffix = "F"
|
suffix = "F"
|
||||||
}
|
}
|
||||||
temp = Math.round(temp)
|
temp = Math.round(temp)
|
||||||
return `${temp}°${suffix}`
|
return `${temp}°${suffix}`
|
||||||
}
|
}
|
||||||
pointSize: Style.fontSizeM * scaling
|
pointSize: Style.fontSizeM * scaling
|
||||||
font.weight: Style.fontWeightBold
|
font.weight: Style.fontWeightBold
|
||||||
@@ -85,24 +91,27 @@ NPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Today day number
|
// Today day number - with simple, stable animation
|
||||||
NText {
|
NText {
|
||||||
opacity: content.isCurrentMonth ? 1.0 : 0.0
|
opacity: content.isCurrentMonth ? 1.0 : 0.0
|
||||||
Layout.preferredWidth: content.isCurrentMonth ? implicitWidth : 0
|
Layout.preferredWidth: content.isCurrentMonth ? implicitWidth : 0
|
||||||
|
elide: Text.ElideNone
|
||||||
|
clip: true
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||||
text: Time.date.getDate()
|
text: Time.date.getDate()
|
||||||
pointSize: Style.fontSizeXXXL * 1.5 * scaling
|
pointSize: Style.fontSizeXXXL * 1.5 * scaling
|
||||||
font.weight: Style.fontWeightBold
|
font.weight: Style.fontWeightBold
|
||||||
color: Color.mOnPrimary
|
color: Color.mOnPrimary
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity { NumberAnimation { duration: Style.animationFast } }
|
||||||
NumberAnimation { duration: Style.animationFast }
|
Behavior on Layout.preferredWidth { NumberAnimation { duration: Style.animationFast; easing.type: Easing.InOutQuad } }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Month, year, location
|
// Month, year, location
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.fillWidth: false
|
// Give the whole column a fixed width to stabilize the layout
|
||||||
|
Layout.preferredWidth: 170 * scaling
|
||||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||||
spacing: -Style.marginXS * scaling
|
spacing: -Style.marginXS * scaling
|
||||||
|
|
||||||
@@ -115,7 +124,6 @@ NPanel {
|
|||||||
font.weight: Style.fontWeightBold
|
font.weight: Style.fontWeightBold
|
||||||
color: Color.mOnPrimary
|
color: Color.mOnPrimary
|
||||||
Layout.alignment: Qt.AlignBaseline
|
Layout.alignment: Qt.AlignBaseline
|
||||||
Layout.maximumWidth: 150 * scaling
|
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,8 +143,8 @@ NPanel {
|
|||||||
text: {
|
text: {
|
||||||
if (!weatherReady)
|
if (!weatherReady)
|
||||||
return I18n.tr("calendar.weather.loading")
|
return I18n.tr("calendar.weather.loading")
|
||||||
const chunks = Settings.data.location.name.split(",")
|
const chunks = Settings.data.location.name.split(",")
|
||||||
return chunks[0]
|
return chunks[0]
|
||||||
}
|
}
|
||||||
pointSize: Style.fontSizeM * scaling
|
pointSize: Style.fontSizeM * scaling
|
||||||
font.weight: Style.fontWeightMedium
|
font.weight: Style.fontWeightMedium
|
||||||
@@ -154,96 +162,86 @@ NPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spacer between date and clock
|
// Spacer to push content left
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Digital clock with circular progress
|
// The Clock, anchored separately for stability
|
||||||
Item {
|
Item {
|
||||||
width: Style.fontSizeXXXL * 1.9 * scaling
|
id: clockItem
|
||||||
height: Style.fontSizeXXXL * 1.9 * scaling
|
anchors.right: parent.right
|
||||||
Layout.alignment: Qt.AlignVCenter
|
anchors.rightMargin: Style.marginM * scaling
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: Style.fontSizeXXXL * 1.9 * scaling
|
||||||
|
height: Style.fontSizeXXXL * 1.9 * scaling
|
||||||
|
|
||||||
// Seconds circular progress
|
Canvas {
|
||||||
Canvas {
|
id: secondsProgress
|
||||||
id: secondsProgress
|
anchors.fill: parent
|
||||||
anchors.fill: parent
|
property real progress: Time.date.getSeconds() / 60
|
||||||
|
onProgressChanged: requestPaint()
|
||||||
property real progress: Time.date.getSeconds() / 60
|
Connections {
|
||||||
onProgressChanged: requestPaint()
|
target: Time
|
||||||
|
function onDateChanged() {
|
||||||
Connections {
|
const total = Time.date.getSeconds() * 1000 + Time.date.getMilliseconds()
|
||||||
target: Time
|
secondsProgress.progress = total / 60000
|
||||||
function onDateChanged() {
|
|
||||||
const total = Time.date.getSeconds() * 1000 + Time.date.getMilliseconds()
|
|
||||||
secondsProgress.progress = total / 60000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onPaint: {
|
|
||||||
var ctx = getContext("2d")
|
|
||||||
var centerX = width / 2
|
|
||||||
var centerY = height / 2
|
|
||||||
var radius = Math.min(width, height) / 2 - 3 * scaling
|
|
||||||
|
|
||||||
ctx.reset()
|
|
||||||
|
|
||||||
// Background circle
|
|
||||||
ctx.beginPath()
|
|
||||||
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI)
|
|
||||||
ctx.lineWidth = 2.5 * scaling
|
|
||||||
ctx.strokeStyle = Qt.alpha(Color.mOnPrimary, 0.15)
|
|
||||||
ctx.stroke()
|
|
||||||
|
|
||||||
// Progress arc
|
|
||||||
ctx.beginPath()
|
|
||||||
ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + progress * 2 * Math.PI)
|
|
||||||
ctx.lineWidth = 2.5 * scaling
|
|
||||||
ctx.strokeStyle = Color.mOnPrimary
|
|
||||||
ctx.lineCap = "round"
|
|
||||||
ctx.stroke()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d")
|
||||||
|
var centerX = width / 2
|
||||||
|
var centerY = height / 2
|
||||||
|
var radius = Math.min(width, height) / 2 - 3 * scaling
|
||||||
|
ctx.reset()
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI)
|
||||||
|
ctx.lineWidth = 2.5 * scaling
|
||||||
|
ctx.strokeStyle = Qt.alpha(Color.mOnPrimary, 0.15)
|
||||||
|
ctx.stroke()
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + progress * 2 * Math.PI)
|
||||||
|
ctx.lineWidth = 2.5 * scaling
|
||||||
|
ctx.strokeStyle = Color.mOnPrimary
|
||||||
|
ctx.lineCap = "round"
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Digital clock
|
ColumnLayout {
|
||||||
ColumnLayout {
|
anchors.centerIn: parent
|
||||||
anchors.centerIn: parent
|
spacing: -Style.marginXXS * scaling
|
||||||
spacing: -Style.marginXXS * scaling
|
NText {
|
||||||
|
text: {
|
||||||
NText {
|
var t = Settings.data.location.use12hourFormat ? Qt.locale().toString(new Date(), "hh AP") : Qt.locale().toString(new Date(), "HH")
|
||||||
text: {
|
return t.split(" ")[0]
|
||||||
var t = Settings.data.location.use12hourFormat ? Qt.locale().toString(new Date(), "hh AP") : Qt.locale().toString(new Date(), "HH")
|
|
||||||
return t.split(" ")[0]
|
|
||||||
}
|
|
||||||
pointSize: Style.fontSizeXS * scaling
|
|
||||||
font.weight: Style.fontWeightBold
|
|
||||||
color: Color.mOnPrimary
|
|
||||||
family: Settings.data.ui.fontFixed
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
NText {
|
|
||||||
text: Qt.formatTime(Time.date, "mm")
|
|
||||||
pointSize: Style.fontSizeXXS * scaling
|
|
||||||
font.weight: Style.fontWeightBold
|
|
||||||
color: Color.mOnPrimary
|
|
||||||
family: Settings.data.ui.fontFixed
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
pointSize: Style.fontSizeXS * scaling
|
||||||
|
font.weight: Style.fontWeightBold
|
||||||
|
color: Color.mOnPrimary
|
||||||
|
family: Settings.data.ui.fontFixed
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
}
|
||||||
|
NText {
|
||||||
|
text: Qt.formatTime(Time.date, "mm")
|
||||||
|
pointSize: Style.fontSizeXXS * scaling
|
||||||
|
font.weight: Style.fontWeightBold
|
||||||
|
color: Color.mOnPrimary
|
||||||
|
family: Settings.data.ui.fontFixed
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6-day forecast (outside blue banner)
|
// ... (rest of the file is unchanged) ...
|
||||||
RowLayout {
|
RowLayout {
|
||||||
visible: weatherReady
|
visible: weatherReady
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
spacing: Style.marginL * scaling
|
spacing: Style.marginL * scaling
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: weatherReady ? Math.min(6, LocationService.data.weather.daily.time.length) : 0
|
model: weatherReady ? Math.min(6, LocationService.data.weather.daily.time.length) : 0
|
||||||
delegate: ColumnLayout {
|
delegate: ColumnLayout {
|
||||||
@@ -251,7 +249,6 @@ NPanel {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
spacing: Style.marginS * scaling
|
spacing: Style.marginS * scaling
|
||||||
|
|
||||||
NText {
|
NText {
|
||||||
text: {
|
text: {
|
||||||
var weatherDate = new Date(LocationService.data.weather.daily.time[index].replace(/-/g, "/"))
|
var weatherDate = new Date(LocationService.data.weather.daily.time[index].replace(/-/g, "/"))
|
||||||
@@ -262,14 +259,12 @@ NPanel {
|
|||||||
font.weight: Style.fontWeightMedium
|
font.weight: Style.fontWeightMedium
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||||
icon: LocationService.weatherSymbolFromCode(LocationService.data.weather.daily.weathercode[index])
|
icon: LocationService.weatherSymbolFromCode(LocationService.data.weather.daily.weathercode[index])
|
||||||
pointSize: Style.fontSizeXXL * 1.5 * scaling
|
pointSize: Style.fontSizeXXL * 1.5 * scaling
|
||||||
color: Color.mPrimary
|
color: Color.mPrimary
|
||||||
}
|
}
|
||||||
|
|
||||||
NText {
|
NText {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: {
|
text: {
|
||||||
@@ -290,27 +285,19 @@ NPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loading indicator for weather
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
visible: !weatherReady
|
visible: !weatherReady
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
NBusyIndicator {}
|
NBusyIndicator {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spacer
|
|
||||||
Item {}
|
Item {}
|
||||||
|
|
||||||
// Navigation and divider
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: Style.marginS * scaling
|
spacing: Style.marginS * scaling
|
||||||
|
|
||||||
NDivider {
|
NDivider {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "chevron-left"
|
icon: "chevron-left"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
@@ -320,7 +307,6 @@ NPanel {
|
|||||||
content.isCurrentMonth = content.checkIsCurrentMonth()
|
content.isCurrentMonth = content.checkIsCurrentMonth()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "calendar"
|
icon: "calendar"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
@@ -329,7 +315,6 @@ NPanel {
|
|||||||
content.isCurrentMonth = true
|
content.isCurrentMonth = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "chevron-right"
|
icon: "chevron-right"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
@@ -340,31 +325,24 @@ NPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Names of days of the week
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
visible: Settings.data.location.showWeekNumberInCalendar
|
visible: Settings.data.location.showWeekNumberInCalendar
|
||||||
Layout.preferredWidth: visible ? Style.baseWidgetSize * 0.7 * scaling : 0
|
Layout.preferredWidth: visible ? Style.baseWidgetSize * 0.7 * scaling : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
columns: 7
|
columns: 7
|
||||||
rows: 1
|
rows: 1
|
||||||
columnSpacing: 0
|
columnSpacing: 0
|
||||||
rowSpacing: 0
|
rowSpacing: 0
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: 7
|
model: 7
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: Style.baseWidgetSize * 0.6 * scaling
|
Layout.preferredHeight: Style.baseWidgetSize * 0.6 * scaling
|
||||||
|
|
||||||
NText {
|
NText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: {
|
text: {
|
||||||
@@ -381,27 +359,20 @@ NPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grid with weeks and days
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
// Column of week numbers
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
visible: Settings.data.location.showWeekNumberInCalendar
|
visible: Settings.data.location.showWeekNumberInCalendar
|
||||||
Layout.preferredWidth: visible ? Style.baseWidgetSize * 0.7 * scaling : 0
|
Layout.preferredWidth: visible ? Style.baseWidgetSize * 0.7 * scaling : 0
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: 6
|
model: 6
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
||||||
NText {
|
NText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
color: Color.mOutline
|
color: Color.mOutline
|
||||||
@@ -433,42 +404,35 @@ NPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Days Grid
|
|
||||||
MonthGrid {
|
MonthGrid {
|
||||||
id: grid
|
id: grid
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
spacing: Style.marginXXS * scaling
|
spacing: Style.marginXXS * scaling
|
||||||
month: Time.date.getMonth()
|
month: Time.date.getMonth()
|
||||||
year: Time.date.getFullYear()
|
year: Time.date.getFullYear()
|
||||||
locale: Qt.locale()
|
locale: Qt.locale()
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: Style.baseWidgetSize * 0.9 * scaling
|
width: Style.baseWidgetSize * 0.9 * scaling
|
||||||
height: Style.baseWidgetSize * 0.9 * scaling
|
height: Style.baseWidgetSize * 0.9 * scaling
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
radius: Style.radiusM * scaling
|
radius: Style.radiusM * scaling
|
||||||
|
|
||||||
color: model.today ? Color.mSecondary : Color.transparent
|
color: model.today ? Color.mSecondary : Color.transparent
|
||||||
|
|
||||||
NText {
|
NText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: model.day
|
text: model.day
|
||||||
color: {
|
color: {
|
||||||
if (model.today)
|
if (model.today)
|
||||||
return Color.mOnSecondary
|
return Color.mOnSecondary
|
||||||
if (model.month === grid.month)
|
if (model.month === grid.month)
|
||||||
return Color.mOnSurface
|
return Color.mOnSurface
|
||||||
return Color.mOnSurfaceVariant
|
return Color.mOnSurfaceVariant
|
||||||
}
|
}
|
||||||
opacity: model.month === grid.month ? 1.0 : 0.4
|
opacity: model.month === grid.month ? 1.0 : 0.4
|
||||||
pointSize: Style.fontSizeM * scaling
|
pointSize: Style.fontSizeM * scaling
|
||||||
font.weight: model.today ? Style.fontWeightBold : Style.fontWeightMedium
|
font.weight: model.today ? Style.fontWeightBold : Style.fontWeightMedium
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
ColorAnimation {
|
||||||
duration: Style.animationFast
|
duration: Style.animationFast
|
||||||
|
|||||||
Reference in New Issue
Block a user