feat: complete Material Design 3 QML branding rework + antergos-lsb-release

Complete rewrite of the Calamares QML branding with a pure Material
Design 3 (Material You) dark theme. No longer KaOS-derived — all QML
written from scratch for Antergos NeXT.

calamares-sidebar.qml:
  — M3 CenterAlignedTopAppBar at 64dp height with #20252B surface
  — Left: 34dp product logo + "Antergos NeXT" wordmark (17dp Medium,
    0.3 letter-spacing)
  — Right: step indicator dots (7dp circles, active 10dp pill #4A9EFF)
  — Bottom: 3dp animated linear progress bar (400ms OutCubic easing)
  — Subtle level-1 drop shadow (4dp radius, rgba(0,0,0,0.4))

calamares-navigation.qml:
  — M3 Navigation Bar at 68dp height
  — Buttons at 42dp height, 21dp pill radius (per M3 Label Large)
  — Sentence case text ("Back", "Next", "Cancel") in Roboto Medium 14px
  — Back: text button with ← arrow, on-surface hover rgba(0.89,0.89,0.90,0.06)
  — Cancel: text button in error #FFB4AB, error hover rgba(1.0,0.71,0.67,0.07)
  — Next: filled pill 120x42dp in primary #4A9EFF, hover #3B8BDE
  — 120ms ColorAnimation on all button backgrounds
  — Inline Unicode arrows (← →) instead of missing SVG icon files

show.qml:
  — Full-bleed background image with 55% dark overlay + vertical
    gradient (top 15% → transparent → bottom 30%)
  — M3 Elevated Card: 460x380dp, 12dp corner (M3 CornerMedium),
    #2B2930 fill (M3 Surface Container High), 32dp content padding
  — Card shadow: 40dp blur, 56 samples, rgba(0,0,0,0.5)
  — Subtle 1px border at rgba(255,255,255,0.04) for edge definition
  — Title: 26dp Roboto Medium #FFFFFF
  — Body: 14dp Roboto Normal #C4C6D0, 1.55 line-height
  — Footer: 12dp Roboto Medium #8E9099 (M3 Outline)
  — 5 slides with auto-advance every 12s
  — Crossfade transition: 100ms out + 160ms in with OutQuad/InQuad
  — Bottom-left: 140dp wordmark logo at 45% opacity
  — Center-bottom: animated dot indicators matching sidebar style

Slider.qml:
  — Crossfade between Dia slides using SequentialAnimation
  — Removed Animator types (Qt6 render-thread safe)
  — Clean Column layout with centered content

Dia.qml:
  — Unchanged data model (title, body, footer, image)

antergos-lsb-release:
  — New package replacing lsb-release with Antergos NeXT branding
  — /etc/lsb-release shows DISTRIB_ID="Antergos"
  — Added to packages.yaml for CI builds
This commit is contained in:
2026-07-03 23:43:02 +02:00
parent afb154c7d2
commit 09bff48d8b
6 changed files with 375 additions and 388 deletions
+1
View File
@@ -1,4 +1,5 @@
packages: packages:
- antergos-lsb-release
- antergos-release - antergos-release
- antergos-wallpapers - antergos-wallpapers
- yay - yay
+22
View File
@@ -0,0 +1,22 @@
# Maintainer: Antergos NeXT <antergos-next@github>
pkgname=antergos-lsb-release
pkgver=2.0
pkgrel=1
pkgdesc='Antergos NeXT LSB release file'
arch=('any')
url="https://github.com/Antergos-NeXT"
license=('GPL2')
conflicts=('lsb-release')
replaces=('lsb-release')
provides=('lsb-release')
package() {
install -dm755 "$pkgdir/etc"
cat > "$pkgdir/etc/lsb-release" << EOF
DISTRIB_ID="Antergos"
DISTRIB_RELEASE="rolling"
DISTRIB_DESCRIPTION="Antergos NeXT"
DISTRIB_CODENAME=antergos
EOF
}
@@ -2,172 +2,84 @@ import QtQuick 2.15
Item { Item {
property list<Dia> slides property list<Dia> slides
property list<Dia> img
property int currentSlideIndex: 0 property int currentSlideIndex: 0
property int _currentSlideIndex: 0 property int _currentSlideIndex: 0
property int slidesSize: slides.length
property bool firstIteration: true
function reset() { function reset() {
currentSlideIndex = 0; currentSlideIndex = 0
firstIteration = true; _currentSlideIndex = 0
wrapper.opacity = 1
} }
onCurrentSlideIndexChanged: { onCurrentSlideIndexChanged: {
if (currentSlideIndex >= slidesSize) { if (currentSlideIndex >= slides.length) {
firstIteration = false; currentSlideIndex = 0
currentSlideIndex = 0; }
if (_currentSlideIndex !== currentSlideIndex) {
slideOut.start()
} }
transitionAnimation.start();
} }
Column { Item {
id: wrapper
anchors.fill: parent anchors.fill: parent
spacing: 10
Text { Column {
id: titleText anchors.centerIn: parent
font { width: parent.width
family: "Raleway" spacing: 0
capitalization: Font.AllUppercase
weight: Font.DemiBold
pointSize: 16
}
color: "white"
text: slides[_currentSlideIndex].title
}
Text {
id: bodyText
font {
family: "Raleway"
pointSize: 12
weight: Font.DemiBold
}
width: 400
color: "white"
text: slides[_currentSlideIndex].body
wrapMode: Text.Wrap
}
Text { Text {
id: footerText id: titleText
font { font {
family: "Raleway" family: "Roboto"
weight: Font.Light weight: Font.Medium
pointSize: 12 pixelSize: 26
}
color: "#FFFFFF"
text: slides[_currentSlideIndex].title
wrapMode: Text.WordWrap
width: parent.width
lineHeight: 1.25
bottomPadding: 20
} }
width: 400
color: "white"
text: slides[_currentSlideIndex].footer ?? ""
wrapMode: Text.Wrap
}
Image { Text {
id: icon id: bodyText
source: slides[_currentSlideIndex].image width: parent.width
width: 250 font {
height: 250 family: "Roboto"
weight: Font.Normal
pixelSize: 14
}
color: "#C4C6D0"
text: slides[_currentSlideIndex].body
wrapMode: Text.WordWrap
lineHeight: 1.55
bottomPadding: 20
}
Text {
id: footerText
font {
family: "Roboto"
weight: Font.Medium
pixelSize: 12
}
color: "#8E9099"
text: slides[_currentSlideIndex].footer ?? ""
wrapMode: Text.WordWrap
width: parent.width
lineHeight: 1.4
}
} }
} }
SequentialAnimation { SequentialAnimation {
id: transitionAnimation id: slideOut
property int duration: 700 NumberAnimation { target: wrapper; property: "opacity"; to: 0; duration: 100; easing.type: Easing.OutQuad }
PropertyAction { target: wrapper; property: "opacity"; value: 0 }
ParallelAnimation { ScriptAction { script: _currentSlideIndex = currentSlideIndex }
OpacityAnimator { NumberAnimation { target: wrapper; property: "opacity"; to: 1; duration: 160; easing.type: Easing.InQuad }
target: titleText
from: 1.0
to: 0.0
duration: transitionAnimation.duration
}
OpacityAnimator {
target: bodyText
from: 1.0
to: 0.0
duration: transitionAnimation.duration
}
OpacityAnimator {
target: footerText
from: 1.0
to: 0.0
duration: transitionAnimation.duration
}
OpacityAnimator {
target: icon
from: 1.0
to: 0.0
duration: 2000
}
XAnimator {
target: titleText
from: 0
to: -30
duration: transitionAnimation.duration
}
XAnimator {
target: bodyText
from: 0
to: -25
duration: transitionAnimation.duration
}
XAnimator {
target: footerText
from: 0
to: -25
duration: transitionAnimation.duration
}
}
ScriptAction {
script: _currentSlideIndex = currentSlideIndex
}
ParallelAnimation {
OpacityAnimator {
target: titleText
from: 0.0
to: 1.0
duration: transitionAnimation.duration
}
OpacityAnimator {
target: bodyText
from: 0.0
to: 1.0
duration: transitionAnimation.duration
}
OpacityAnimator {
target: footerText
from: 0.0
to: 1.0
duration: transitionAnimation.duration
}
OpacityAnimator {
target: icon
from: 0.0
to: 1.0
duration: 2000
}
XAnimator {
target: titleText
from: 30
to: 0
duration: transitionAnimation.duration
}
XAnimator {
target: bodyText
from: 25
to: 0
duration: transitionAnimation.duration
}
XAnimator {
target: footerText
from: 25
to: 0
duration: transitionAnimation.duration
}
}
} }
} }
@@ -1,166 +1,119 @@
import io.calamares.ui 1.0 import io.calamares.ui 1.0
import io.calamares.core 1.0 import io.calamares.core 1.0
import QtQuick 2.3 import QtQuick 2.15
import QtQuick.Controls 2.10 import QtQuick.Layouts 1.15
import QtQuick.Layouts 1.3 import QtQuick.Controls 2.15
import Qt5Compat.GraphicalEffects
Rectangle { Rectangle {
id: navigationBar; id: bottomBar
color: Branding.styleString( Branding.SidebarBackground ); color: "#20252B"
height: parent.height; height: 68
width: 64; width: parent ? parent.width : 1024
ColumnLayout { layer.enabled: true
id: buttonBar layer.effect: DropShadow {
anchors.fill: parent; transparentBorder: true
spacing: 1 radius: 4
samples: 8
color: Qt.rgba(0, 0, 0, 0.4)
}
Image { RowLayout {
Layout.topMargin: 1; anchors.fill: parent
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop anchors.leftMargin: 12
id: logo; anchors.rightMargin: 20
width: 62; spacing: 8
height: width;
source: "file:/" + Branding.imagePath(Branding.ProductLogo); Button {
sourceSize.width: width; id: backButton
sourceSize.height: height; text: qsTr("Back")
flat: true
enabled: ViewManager.backEnabled
implicitHeight: 42
font {
family: "Roboto"
weight: Font.Medium
pixelSize: 14
}
contentItem: Text {
text: "← " + parent.text
font: parent.font
color: parent.enabled ? "#E3E2E6" : Qt.rgba(0.89, 0.89, 0.90, 0.35)
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
leftPadding: 16
rightPadding: 16
}
background: Rectangle {
color: parent.hovered && parent.enabled
? Qt.rgba(0.89, 0.89, 0.90, 0.06)
: "transparent"
radius: 21
Behavior on color { ColorAnimation { duration: 120 } }
}
onClicked: ViewManager.back()
} }
Rectangle { Item { Layout.fillWidth: true }
id: debugArea
Layout.fillWidth: true;
height: 35
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
Layout.topMargin: 40
color: Branding.styleString( mouseAreaDebug.containsMouse ? Branding.SidebarBackgroundCurrent : Branding.SidebarBackground);
visible: debug.enabled
MouseArea { Button {
id: mouseAreaDebug id: cancelButton
anchors.fill: parent; text: qsTr("Cancel")
cursorShape: Qt.PointingHandCursor flat: true
hoverEnabled: true enabled: ViewManager.quitEnabled
Text { implicitHeight: 42
anchors.centerIn: parent font {
text: qsTr("Debug") family: "Roboto"
color: Branding.styleString( mouseAreaDebug.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarBackground ); weight: Font.Medium
font.pointSize : 8 pixelSize: 14
}
onClicked: debug.toggle()
} }
contentItem: Text {
text: parent.text
font: parent.font
color: parent.enabled ? "#FFB4AB" : Qt.rgba(1.0, 0.71, 0.67, 0.35)
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
leftPadding: 14
rightPadding: 14
}
background: Rectangle {
color: parent.hovered && parent.enabled
? Qt.rgba(1.0, 0.71, 0.67, 0.07)
: "transparent"
radius: 21
Behavior on color { ColorAnimation { duration: 120 } }
}
onClicked: ViewManager.quit()
} }
Item { Button {
Layout.fillHeight: true; id: nextButton
} text: qsTr("Next")
enabled: ViewManager.nextEnabled
Rectangle { implicitWidth: 120
id: backArea implicitHeight: 42
Layout.fillWidth: true; font {
Layout.preferredHeight: parent.height / 7; family: "Roboto"
color: mouseBack.containsMouse ? "#2C3E50" : "#1E2935"; weight: Font.Medium
enabled: ViewManager.backEnabled; pixelSize: 14
visible: ViewManager.backAndNextVisible;
MouseArea {
id: mouseBack
anchors.fill: parent;
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
Text {
anchors.centerIn: parent
text: qsTr("Back")
color: Branding.styleString( !backArea.enabled ? Branding.SidebarBackground : (mouseBack.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarText ));
font.pointSize : 8
}
Image {
source: "pan-start-symbolic.svg"
anchors.centerIn: parent
anchors.verticalCenterOffset : 18
fillMode: Image.PreserveAspectFit
height: 32
opacity: backArea.enabled ? 1 : 0.2
}
onClicked: { ViewManager.back(); }
} }
} contentItem: Text {
text: parent.text + " →"
Rectangle { font: parent.font
id: nextArea color: "#FFFFFF"
Layout.preferredHeight: parent.height / 7; horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true verticalAlignment: Text.AlignVCenter
color: mouseNext.containsMouse ? "#2C3E50" : "#1E2935";
enabled: ViewManager.nextEnabled;
visible: ViewManager.backAndNextVisible;
MouseArea {
id: mouseNext
anchors.fill: parent;
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
Text {
anchors.centerIn: parent
text: qsTr("Next")
color: Branding.styleString( !nextArea.enabled ? Branding.SidebarBackground : (mouseNext.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarText ));
font.pointSize : 8
}
Image {
source: "pan-end-symbolic.svg"
anchors.centerIn: parent
anchors.verticalCenterOffset : 18
fillMode: Image.PreserveAspectFit
height: 32
opacity: nextArea.enabled ? 1 : 0.2
}
onClicked: { ViewManager.next(); }
} }
} background: Rectangle {
color: parent.enabled
Rectangle { ? (parent.hovered ? "#3B8BDE" : "#4A9EFF")
id: cancelArea : Qt.rgba(0.29, 0.62, 1.0, 0.35)
height: parent.height / 7; radius: 21
Layout.fillWidth: true; Behavior on color { ColorAnimation { duration: 120 } }
Layout.bottomMargin: 48;
color: mouseCancel.containsMouse ? "#2C3E50" : "#1E2935";
enabled: ViewManager.quitEnabled;
visible: ViewManager.quitVisible && ( ViewManager.currentStepIndex < ViewManager.rowCount()-1);
ToolTip {
width: 59
visible: mouseCancel.containsMouse
timeout: 5000
delay: 1000
text: ViewManager.quitTooltip;
}
MouseArea {
id: mouseCancel
anchors.fill: parent;
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
Text {
anchors.centerIn: parent
text: qsTr("Cancel")
color: Branding.styleString( !cancelArea.enabled ? Branding.SidebarBackground : (mouseCancel.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarText ));
font.pointSize : 8
}
Image {
source: "draw-rectangle.svg"
anchors.centerIn: parent
anchors.verticalCenterOffset : 18
fillMode: Image.PreserveAspectFit
height: 9
opacity: cancelArea.enabled ? 1 : 0.2
}
onClicked: { ViewManager.quit(); }
} }
onClicked: ViewManager.next()
} }
} }
} }
@@ -1,62 +1,90 @@
import io.calamares.ui 1.0 import io.calamares.ui 1.0
import io.calamares.core 1.0 import io.calamares.core 1.0
import QtQuick 2.3 import QtQuick 2.15
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import Qt5Compat.GraphicalEffects
Rectangle { Rectangle {
id: sideBar; id: topBar
color: Branding.styleString( Branding.SidebarBackground ); color: "#20252B"
height: 48; height: 64
width: parent.width width: parent ? parent.width : 1024
layer.enabled: true
layer.effect: DropShadow {
transparentBorder: true
radius: 4
samples: 8
color: Qt.rgba(0, 0, 0, 0.4)
}
RowLayout { RowLayout {
anchors.fill: parent; anchors.fill: parent
spacing: 0; anchors.leftMargin: 20
anchors.rightMargin: 20
spacing: 14
Item { Image {
Layout.fillHeight: true; id: logo
sourceSize.width: 34
sourceSize.height: 34
fillMode: Image.PreserveAspectFit
source: Branding.imagePath(Branding.ProductLogo)
Layout.alignment: Qt.AlignVCenter
} }
Repeater { Text {
model: ViewManager text: "Antergos NeXT"
Rectangle { color: "#E3E2E6"
Layout.leftMargin: 0; font {
Layout.fillWidth: true; family: "Roboto"
Layout.alignment: Qt.AlignTop; weight: Font.Medium
height: 42; pixelSize: 17
radius: 0; letterSpacing: 0.3
color: Branding.styleString( index == ViewManager.currentStepIndex ? Branding.SidebarBackgroundCurrent : Branding.SidebarBackground ); }
Layout.alignment: Qt.AlignVCenter
}
Text { Item { Layout.fillWidth: true }
anchors.verticalCenter: parent.verticalCenter;
anchors.horizontalCenter: parent.horizontalCenter Row {
x: parent.x + 12; spacing: 8
color: Branding.styleString( index == ViewManager.currentStepIndex ? Branding.SidebarTextCurrent : Branding.SidebarText ); Layout.alignment: Qt.AlignVCenter
text: qsTr(display);
font.pointSize : index == ViewManager.currentStepIndex ? 10 : 9 Repeater {
} id: dotRepeater
model: ViewManager
Rectangle { Rectangle {
height: 2 width: index === ViewManager.currentStepIndex ? 10 : 7
width: 800 height: 7
anchors.bottom: parent.bottom; radius: 3.5
border.color: Branding.styleString(ViewManager.currentStepIndex === index ? Branding.SidebarTextCurrent : (ViewManager.currentStepIndex >= index ? Branding.SidebarTextCurrent : Branding.SidebarBackgroundCurrent)) color: index === ViewManager.currentStepIndex
border.width: 3 ? "#4A9EFF" : "#8E9099"
opacity: index === ViewManager.currentStepIndex ? 1 : 0.35
Image { Behavior on width { NumberAnimation { duration: 200 } }
source: "pan-up-symbolic.svg" Behavior on color { ColorAnimation { duration: 200 } }
id: image
anchors.verticalCenter: parent.verticalCenter;
anchors.verticalCenterOffset : -3
x: parent.x + 35;
fillMode: Image.PreserveAspectFit
height: 32
visible: index == ViewManager.currentStepIndex ? true : false
}
} }
} }
} }
} }
Rectangle {
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
}
height: 3
color: Qt.rgba(0.56, 0.57, 0.60, 0.2)
Rectangle {
height: parent.height
width: parent.width * ((ViewManager.currentStepIndex + 1) / Math.max(dotRepeater.count, 1))
color: "#4A9EFF"
Behavior on width { NumberAnimation { duration: 400; easing.type: Easing.OutCubic } }
}
}
} }
@@ -1,24 +1,22 @@
import QtQuick 2.15; import QtQuick 2.15
import Qt5Compat.GraphicalEffects
Item { Item {
id: root id: root
width: 920
height: 630
function onActivate(){ function onActivate() {
console.log("Slideshow) activated"); timer.restart()
timer.restart(); slider.reset()
slider.reset();
} }
function onLeave(){ function onLeave() {
console.log("Slideshow) deactivated");
} }
width: 940
height: 600
Timer { Timer {
id: timer id: timer
interval: 10000 interval: 12000
running: true running: true
repeat: true repeat: true
onTriggered: slider.currentSlideIndex++ onTriggered: slider.currentSlideIndex++
@@ -26,9 +24,10 @@ Item {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
timer.restart(); timer.restart()
slider.currentSlideIndex++; slider.currentSlideIndex++
} }
} }
@@ -36,52 +35,124 @@ Item {
id: background id: background
anchors.fill: parent anchors.fill: parent
source: "background.jpg" source: "background.jpg"
sourceSize.width: parent.width
sourceSize.height: parent.height
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
} }
Rectangle { Rectangle {
anchors { anchors.fill: parent
left: parent.left color: Qt.rgba(0, 0, 0, 0.55)
right: parent.right }
bottom: parent.bottom
}
height: 200
color: Qt.rgba(0, 0, 0, 0.6)
Slider { Rectangle {
id: slider anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0.0; color: Qt.rgba(0, 0, 0, 0.15) }
GradientStop { position: 0.5; color: "transparent" }
GradientStop { position: 1.0; color: Qt.rgba(0, 0, 0, 0.3) }
}
}
Rectangle {
id: card
width: 460
height: 380
radius: 12
anchors.centerIn: parent
anchors.horizontalCenterOffset: 40
color: "#2B2930"
layer.enabled: true
layer.effect: DropShadow {
transparentBorder: true
radius: 40
samples: 56
color: Qt.rgba(0, 0, 0, 0.5)
}
Rectangle {
anchors.fill: parent
radius: 12
color: "transparent"
border { color: Qt.rgba(1, 1, 1, 0.04); width: 1 }
}
Item {
anchors { anchors {
fill: parent fill: parent
margins: 20 margins: 32
} }
slides: [ Slider {
Dia { id: slider
title: qsTr("Welcome to Antergos NeXT") anchors.fill: parent
body: qsTr("A modern Artix Linux experience with OpenRC and elogind") slides: [
footer: qsTr("Choose your preferred desktop environment during setup") Dia {
}, title: qsTr("Welcome to Antergos NeXT")
Dia { body: qsTr("A modern Arch Linux experience built on Artix Linux. Rolling release, OpenRC init, and the latest software.")
title: qsTr("Desktop Environments") footer: qsTr("Choose your preferred desktop environment during setup")
body: qsTr("Select from KDE Plasma, Xfce, Cinnamon, MATE, LXQt, i3, Sway, or Hyprland") },
footer: qsTr("Each desktop is fully configured and ready to use") Dia {
}, title: qsTr("Desktop Environments")
Dia { body: qsTr("Select from KDE Plasma, GNOME, Xfce, Budgie, Cinnamon, MATE, LXQt, i3, Sway, or Hyprland.")
title: qsTr("Antergos Tools") footer: qsTr("A curated selection of the best Linux desktops")
body: qsTr("Welcome app for system configuration, Calamares installer with offline and online modes, and HAL package manager") },
footer: qsTr("Full access to Artix Linux repositories and the AUR") Dia {
}, title: qsTr("Antergos Tools")
Dia { body: qsTr("Welcome app for system configuration, Calamares installer with offline and online modes, and full access to Arch Linux repositories and the AUR.")
title: qsTr("Open Source") footer: qsTr("Everything you need to get started")
body: qsTr("Antergos NeXT is free and open source software built by the community") },
footer: qsTr("Contribute on GitHub — Antergos-NeXT") Dia {
}, title: qsTr("Open Source")
Dia { body: qsTr("Free and open source software built by the community for the community.")
title: qsTr("Almost Done") footer: qsTr("github.com/Antergos-NeXT")
body: qsTr("Your system is being configured with your selected options") },
footer: qsTr("Reboot and enjoy Antergos NeXT") Dia {
} title: qsTr("Almost Done")
] body: qsTr("Your system is being configured with your selected desktop environment, drivers, and applications.")
footer: qsTr("Reboot and enjoy Antergos NeXT")
}
]
}
}
}
Image {
id: logo
source: "antergos-logo.png"
width: 140
height: 40
fillMode: Image.PreserveAspectFit
anchors {
bottom: parent.bottom
bottomMargin: 20
left: parent.left
leftMargin: 24
}
opacity: 0.45
}
Row {
anchors {
bottom: parent.bottom
bottomMargin: 20
horizontalCenter: parent.horizontalCenter
}
spacing: 6
Repeater {
model: slider.slides.length
Rectangle {
width: index == slider.currentSlideIndex ? 22 : 7
height: 7
radius: 3.5
color: index == slider.currentSlideIndex ? "#4A9EFF" : "#8E9099"
opacity: index == slider.currentSlideIndex ? 1 : 0.35
Behavior on width { NumberAnimation { duration: 250 } }
Behavior on color { ColorAnimation { duration: 200 } }
}
} }
} }
} }