diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..f417ad0dfb51f5e10c0afa6d5c5953e947eec0ce
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,56 @@
+# C++ objects and libs
+*.slo
+*.lo
+*.o
+*.a
+*.la
+*.lai
+*.so
+*.so.*
+*.dll
+*.dylib
+
+# Qt-es
+object_script.*.Release
+object_script.*.Debug
+*_plugin_import.cpp
+/.qmake.cache
+/.qmake.stash
+*.pro.user
+*.pro.user.*
+*.qbs.user
+*.qbs.user.*
+*.moc
+moc_*.cpp
+moc_*.h
+qrc_*.cpp
+ui_*.h
+*.qmlc
+*.jsc
+Makefile*
+*build-*
+build/
+*.qm
+*.prl
+*.qmlls.ini
+
+# Qt unit tests
+target_wrapper.*
+
+# QtCreator
+*.autosave
+
+# QtCreator Qml
+*.qmlproject.user
+*.qmlproject.user.*
+
+# QtCreator CMake
+CMakeLists.txt.user*
+
+# QtCreator 4.8< compilation database
+compile_commands.json
+
+# QtCreator local machine specific files for imported projects
+*creator.user*
+
+*_qmlcache.qrc
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..dcfd1aa7d2ecf2dd78f1fa5fd239c06c48f0c945
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 Saint-Petersburg State University of Economics
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5831978941d2c41fede49f0f646dc7be3aa2e441 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,63 @@
+# Лабораторная работа № 3. Кнопка «лёд — вода — пар»
+
+**Студент:** Кривошеин Максим Константинович, группа ИС-2301
+**Дисциплина:** Разработка мобильных приложений, СПбГЭУ
+**Платформа:** ОС Аврора 5.2 (Aurora SDK 5.2.0.180), Qt 5.6, QML / Sailfish Silica
+
+## Задание
+
+Разработать компоненту в отдельном qml-файле. Компонента должна отображать кнопку,
+которая имеет три состояния: «лёд», «вода», «пар». Состояния должны быть оформлены
+в виде `States`. При желании добавить анимацию при переходе из состояния в состояние.
+
+## Реализация
+
+Компонента — [`qml/components/PhaseButton.qml`](qml/components/PhaseButton.qml).
+Каждое нажатие переводит кнопку в следующее состояние: лёд → вода → пар → лёд.
+
+| Состояние (`State.name`) | Надпись | Форма кнопки | Значок |
+|---|---|---|---|
+| `ice` | лёд, ниже 0 °C | голубой «кубик» со скруглёнными углами | снежинка |
+| `water` | вода, от 0 до 100 °C | синий круг, по воде расходятся круги | капля |
+| `steam` | пар, выше 100 °C | полупрозрачное серое «облако», вверх поднимаются клубы пара | волны пара |
+
+Все отличия состояний (цвет, радиус скругления, прозрачность, надписи, значок)
+заданы через `PropertyChanges` внутри `states`. Для переходов описаны `transitions`:
+
+- `ice → water` — плавление: цвет перетекает, углы скругляются с упругой анимацией `Easing.OutElastic`;
+- `water → steam` — кипение: кнопка «вздувается» и становится полупрозрачной;
+- `steam → ice` — кристаллизация: кнопка сжимается и доворачивается на 90°;
+- `* → *` — общий переход для прямого переключения в любое состояние.
+
+Во всех переходах надписи меняются через плавное затухание (`PropertyAction`
+между двумя `NumberAnimation`), значки состояний — перекрёстным затуханием.
+Значки — PNG-файлы из ресурсов `qrc:/images/`.
+
+На главном экране (`qml/pages/MainPage.qml`) под кнопкой есть три переключателя
+«лёд / вода / пар»: они показывают текущее состояние и позволяют выставить его
+напрямую. При каждой смене состояния в консоль пишется `Состояние кнопки: <имя>`.
+
+## Структура проекта
+
+```
+ru.krivoshein.PhaseButton.pro проект qmake
+resources.qrc значки состояний
+images/ice.png, water.png, steam.png
+src/main.cpp точка входа приложения
+qml/PhaseButton.qml ApplicationWindow
+qml/components/PhaseButton.qml компонента кнопки с тремя состояниями
+qml/pages/MainPage.qml экран с кнопкой
+qml/pages/AboutPage.qml «О приложении»
+qml/cover/DefaultCoverPage.qml обложка приложения
+rpm/ru.krivoshein.PhaseButton.spec описание RPM-пакета
+```
+
+## Скриншоты
+
+| Лёд | Вода | Пар |
+|---|---|---|
+|  |  |  |
+
+## Лицензия
+
+MIT, см. [LICENSE](LICENSE).
diff --git a/icons/108x108/ru.krivoshein.PhaseButton.png b/icons/108x108/ru.krivoshein.PhaseButton.png
new file mode 100644
index 0000000000000000000000000000000000000000..450186d7f1967d5c789c2b9152c90424b66b1c01
Binary files /dev/null and b/icons/108x108/ru.krivoshein.PhaseButton.png differ
diff --git a/icons/108x108/ru.template.untitled4.png b/icons/108x108/ru.template.untitled4.png
deleted file mode 100644
index 1f0537a46b3b3a6c3e372c464e074ae7f81242bc..0000000000000000000000000000000000000000
Binary files a/icons/108x108/ru.template.untitled4.png and /dev/null differ
diff --git a/icons/128x128/ru.krivoshein.PhaseButton.png b/icons/128x128/ru.krivoshein.PhaseButton.png
new file mode 100644
index 0000000000000000000000000000000000000000..eb4f64a78f290101ea4e2c776c2f76ef84c58b74
Binary files /dev/null and b/icons/128x128/ru.krivoshein.PhaseButton.png differ
diff --git a/icons/128x128/ru.template.untitled4.png b/icons/128x128/ru.template.untitled4.png
deleted file mode 100644
index c104cfe9ae71aab7247f28cb87a65995ce21b41e..0000000000000000000000000000000000000000
Binary files a/icons/128x128/ru.template.untitled4.png and /dev/null differ
diff --git a/icons/172x172/ru.krivoshein.PhaseButton.png b/icons/172x172/ru.krivoshein.PhaseButton.png
new file mode 100644
index 0000000000000000000000000000000000000000..0d584058ec27599a8aa319db715085f5657fcbbf
Binary files /dev/null and b/icons/172x172/ru.krivoshein.PhaseButton.png differ
diff --git a/icons/172x172/ru.template.untitled4.png b/icons/172x172/ru.template.untitled4.png
deleted file mode 100644
index c094ea65324a97c23d8803f91ea4ffdb9b8d30b5..0000000000000000000000000000000000000000
Binary files a/icons/172x172/ru.template.untitled4.png and /dev/null differ
diff --git a/icons/86x86/ru.krivoshein.PhaseButton.png b/icons/86x86/ru.krivoshein.PhaseButton.png
new file mode 100644
index 0000000000000000000000000000000000000000..e975e576392d48b2187503c121781f43743f43a9
Binary files /dev/null and b/icons/86x86/ru.krivoshein.PhaseButton.png differ
diff --git a/icons/86x86/ru.template.untitled4.png b/icons/86x86/ru.template.untitled4.png
deleted file mode 100644
index be528fd0e84537120961c5bee067268c6ad8f98e..0000000000000000000000000000000000000000
Binary files a/icons/86x86/ru.template.untitled4.png and /dev/null differ
diff --git a/images/ice.png b/images/ice.png
new file mode 100644
index 0000000000000000000000000000000000000000..3ae3e0a6788405dc425d4f80d7b3568763d54c46
Binary files /dev/null and b/images/ice.png differ
diff --git a/images/steam.png b/images/steam.png
new file mode 100644
index 0000000000000000000000000000000000000000..64f78ffe5a4f8464f71146037604bf5d7d66640b
Binary files /dev/null and b/images/steam.png differ
diff --git a/images/water.png b/images/water.png
new file mode 100644
index 0000000000000000000000000000000000000000..0096358632aab1f062b31662f5ca8fd0844a5cc8
Binary files /dev/null and b/images/water.png differ
diff --git a/qml/PhaseButton.qml b/qml/PhaseButton.qml
new file mode 100644
index 0000000000000000000000000000000000000000..dc4e670917a5b74f9d8541f4f0c396f5fc683cf3
--- /dev/null
+++ b/qml/PhaseButton.qml
@@ -0,0 +1,16 @@
+/**************************************************************************
+ * SPDX-License-Identifier: MIT
+ * SPDX-FileCopyrightText: Saint Petersburg State University of Economics
+ * SPDX-Author-list: Maxim Krivoshein 2026
+ * SPDX-Creation-date: 2026-10-09
+ **************************************************************************/
+
+import QtQuick 2.0
+import Sailfish.Silica 1.0
+
+ApplicationWindow {
+ objectName: "applicationWindow"
+ initialPage: Qt.resolvedUrl("pages/MainPage.qml")
+ cover: Qt.resolvedUrl("cover/DefaultCoverPage.qml")
+ allowedOrientations: defaultAllowedOrientations
+}
diff --git a/qml/components/PhaseButton.qml b/qml/components/PhaseButton.qml
new file mode 100644
index 0000000000000000000000000000000000000000..5ccc535adf35970055cd8d1925520ab0afab5cec
--- /dev/null
+++ b/qml/components/PhaseButton.qml
@@ -0,0 +1,326 @@
+/**************************************************************************
+ * SPDX-License-Identifier: MIT
+ * SPDX-FileCopyrightText: Saint Petersburg State University of Economics
+ * SPDX-Author-list: Maxim Krivoshein 2026
+ * SPDX-Creation-date: 2026-10-09
+ **************************************************************************/
+
+import QtQuick 2.0
+import Sailfish.Silica 1.0
+
+// Button with three states of water: "ice" -> "water" -> "steam" -> "ice".
+// Every click switches to the next state, each transition has its own animation.
+Item {
+ id: root
+
+ readonly property string title: titleLabel.text
+ property real _pulse: 1.0
+
+ signal clicked()
+
+ function next() {
+ if (state === "ice") {
+ state = "water"
+ } else if (state === "water") {
+ state = "steam"
+ } else {
+ state = "ice"
+ }
+ }
+
+ width: Theme.itemSizeHuge * 2
+ height: width
+ state: "ice"
+
+ Rectangle {
+ id: body
+
+ anchors.centerIn: parent
+ width: parent.width * 0.8
+ height: width
+ scale: (mouseArea.pressed ? 0.94 : 1.0) * root._pulse
+ border.width: Theme.paddingSmall / 2
+
+ Behavior on scale { NumberAnimation { duration: 90 } }
+
+ Rectangle {
+ id: ripple
+
+ anchors.centerIn: parent
+ width: parent.width * 0.4
+ height: width
+ radius: width / 2
+ color: "transparent"
+ border { width: 3; color: "#e3f2fd" }
+ opacity: 0
+ visible: root.state === "water"
+
+ SequentialAnimation {
+ running: ripple.visible
+ loops: Animation.Infinite
+
+ ParallelAnimation {
+ NumberAnimation {
+ target: ripple
+ property: "width"
+ from: body.width * 0.3
+ to: body.width * 0.95
+ duration: 1600
+ easing.type: Easing.OutQuad
+ }
+ NumberAnimation {
+ target: ripple
+ property: "opacity"
+ from: 0.8
+ to: 0
+ duration: 1600
+ }
+ }
+ PauseAnimation { duration: 300 }
+ }
+ }
+
+ Item {
+ id: glyph
+
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ top: parent.top
+ topMargin: parent.height * 0.12
+ }
+ width: parent.width * 0.42
+ height: width
+
+ Image {
+ id: iceGlyph
+
+ anchors.fill: parent
+ source: "qrc:/images/ice.png"
+ sourceSize { width: width; height: height }
+ opacity: 0
+ }
+
+ Image {
+ id: waterGlyph
+
+ anchors.fill: parent
+ source: "qrc:/images/water.png"
+ sourceSize { width: width; height: height }
+ opacity: 0
+ }
+
+ Image {
+ id: steamGlyph
+
+ anchors.fill: parent
+ source: "qrc:/images/steam.png"
+ sourceSize { width: width; height: height }
+ opacity: 0
+ }
+ }
+
+ Column {
+ id: textColumn
+
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ top: glyph.bottom
+ topMargin: Theme.paddingSmall
+ }
+
+ Label {
+ id: titleLabel
+
+ anchors.horizontalCenter: parent.horizontalCenter
+ font { pixelSize: Theme.fontSizeExtraLarge; bold: true }
+ }
+
+ Label {
+ id: temperatureLabel
+
+ anchors.horizontalCenter: parent.horizontalCenter
+ font.pixelSize: Theme.fontSizeSmall
+ }
+ }
+ }
+
+ Repeater {
+ id: steamPuffs
+
+ model: 5
+
+ Rectangle {
+ id: puff
+
+ readonly property real startX: body.x + body.width * (0.2 + 0.15 * index)
+
+ x: startX
+ y: body.y
+ width: Theme.paddingLarge * (1 + index % 3 * 0.5)
+ height: width
+ radius: width / 2
+ color: "#eceff1"
+ opacity: 0
+ visible: root.state === "steam"
+
+ SequentialAnimation {
+ running: puff.visible
+ loops: Animation.Infinite
+
+ PauseAnimation { duration: index * 280 }
+ ParallelAnimation {
+ NumberAnimation {
+ target: puff
+ property: "y"
+ from: body.y + body.height * 0.15
+ to: body.y - root.height * 0.25
+ duration: 1800
+ easing.type: Easing.OutQuad
+ }
+ NumberAnimation {
+ target: puff
+ property: "x"
+ from: puff.startX
+ to: puff.startX + (index % 2 === 0 ? 1 : -1) * Theme.paddingLarge
+ duration: 1800
+ }
+ SequentialAnimation {
+ NumberAnimation { target: puff; property: "opacity"; to: 0.85; duration: 400 }
+ NumberAnimation { target: puff; property: "opacity"; to: 0; duration: 1400 }
+ }
+ }
+ }
+ }
+ }
+
+ MouseArea {
+ id: mouseArea
+
+ anchors.fill: body
+ onClicked: {
+ root.next()
+ root.clicked()
+ }
+ }
+
+ states: [
+ State {
+ name: "ice"
+ PropertyChanges {
+ target: body
+ color: "#b3e5fc"
+ border.color: "#e1f5fe"
+ radius: Theme.paddingLarge
+ rotation: 0
+ opacity: 1.0
+ }
+ PropertyChanges { target: iceGlyph; opacity: 1 }
+ PropertyChanges { target: titleLabel; text: qsTr("лёд"); color: "#0d47a1" }
+ PropertyChanges { target: temperatureLabel; text: qsTr("ниже 0 °C"); color: "#1565c0" }
+ },
+ State {
+ name: "water"
+ PropertyChanges {
+ target: body
+ color: "#1e88e5"
+ border.color: "#64b5f6"
+ radius: body.width / 2
+ rotation: 0
+ opacity: 1.0
+ }
+ PropertyChanges { target: waterGlyph; opacity: 1 }
+ PropertyChanges { target: titleLabel; text: qsTr("вода"); color: "white" }
+ PropertyChanges { target: temperatureLabel; text: qsTr("от 0 до 100 °C"); color: "#e3f2fd" }
+ },
+ State {
+ name: "steam"
+ PropertyChanges {
+ target: body
+ color: "#cfd8dc"
+ border.color: "#eceff1"
+ radius: body.width * 0.3
+ rotation: 0
+ opacity: 0.88
+ }
+ PropertyChanges { target: steamGlyph; opacity: 1 }
+ PropertyChanges { target: titleLabel; text: qsTr("пар"); color: "#37474f" }
+ PropertyChanges { target: temperatureLabel; text: qsTr("выше 100 °C"); color: "#455a64" }
+ }
+ ]
+
+ transitions: [
+ // melting: the cube becomes a drop and wobbles
+ Transition {
+ from: "ice"
+ to: "water"
+
+ ParallelAnimation {
+ ColorAnimation { targets: [body, titleLabel, temperatureLabel]; duration: 700 }
+ NumberAnimation { targets: [iceGlyph, waterGlyph, steamGlyph]; property: "opacity"; duration: 500 }
+ NumberAnimation { target: body; property: "radius"; duration: 900; easing.type: Easing.OutElastic }
+ SequentialAnimation {
+ NumberAnimation { target: textColumn; property: "opacity"; to: 0; duration: 150 }
+ PropertyAction { targets: [titleLabel, temperatureLabel]; property: "text" }
+ NumberAnimation { target: textColumn; property: "opacity"; to: 1; duration: 250 }
+ }
+ }
+ },
+ // boiling: the drop inflates and becomes semi-transparent
+ Transition {
+ from: "water"
+ to: "steam"
+
+ ParallelAnimation {
+ ColorAnimation { targets: [body, titleLabel, temperatureLabel]; duration: 700 }
+ NumberAnimation { targets: [iceGlyph, waterGlyph, steamGlyph]; property: "opacity"; duration: 500 }
+ NumberAnimation { target: body; properties: "radius,opacity"; duration: 700 }
+ SequentialAnimation {
+ NumberAnimation { target: root; property: "_pulse"; to: 1.12; duration: 250; easing.type: Easing.OutQuad }
+ NumberAnimation { target: root; property: "_pulse"; to: 1.0; duration: 450; easing.type: Easing.InOutQuad }
+ }
+ SequentialAnimation {
+ NumberAnimation { target: textColumn; property: "opacity"; to: 0; duration: 150 }
+ PropertyAction { targets: [titleLabel, temperatureLabel]; property: "text" }
+ NumberAnimation { target: textColumn; property: "opacity"; to: 1; duration: 250 }
+ }
+ }
+ },
+ // freezing: the cloud shrinks and crystallises with a turn
+ Transition {
+ from: "steam"
+ to: "ice"
+
+ ParallelAnimation {
+ ColorAnimation { targets: [body, titleLabel, temperatureLabel]; duration: 700 }
+ NumberAnimation { targets: [iceGlyph, waterGlyph, steamGlyph]; property: "opacity"; duration: 500 }
+ NumberAnimation { target: body; properties: "radius,opacity"; duration: 700 }
+ RotationAnimation { target: body; from: -90; to: 0; duration: 700; easing.type: Easing.OutBack }
+ SequentialAnimation {
+ NumberAnimation { target: root; property: "_pulse"; to: 0.85; duration: 250 }
+ NumberAnimation { target: root; property: "_pulse"; to: 1.0; duration: 450; easing.type: Easing.OutBack }
+ }
+ SequentialAnimation {
+ NumberAnimation { target: textColumn; property: "opacity"; to: 0; duration: 150 }
+ PropertyAction { targets: [titleLabel, temperatureLabel]; property: "text" }
+ NumberAnimation { target: textColumn; property: "opacity"; to: 1; duration: 250 }
+ }
+ }
+ },
+ // direct switch between any two states (sublimation, condensation...)
+ Transition {
+ from: "*"
+ to: "*"
+
+ ParallelAnimation {
+ ColorAnimation { targets: [body, titleLabel, temperatureLabel]; duration: 600 }
+ NumberAnimation { targets: [iceGlyph, waterGlyph, steamGlyph]; property: "opacity"; duration: 500 }
+ NumberAnimation { target: body; properties: "radius,opacity"; duration: 600; easing.type: Easing.InOutQuad }
+ SequentialAnimation {
+ NumberAnimation { target: textColumn; property: "opacity"; to: 0; duration: 150 }
+ PropertyAction { targets: [titleLabel, temperatureLabel]; property: "text" }
+ NumberAnimation { target: textColumn; property: "opacity"; to: 1; duration: 250 }
+ }
+ }
+ }
+ ]
+}
diff --git a/qml/cover/DefaultCoverPage.qml b/qml/cover/DefaultCoverPage.qml
index 93f04c04f95c187c82c40c49d0103b26efb6dffe..1eb928302140be043b0596092b0fde45f4b59224 100644
--- a/qml/cover/DefaultCoverPage.qml
+++ b/qml/cover/DefaultCoverPage.qml
@@ -1,3 +1,10 @@
+/**************************************************************************
+ * SPDX-License-Identifier: MIT
+ * SPDX-FileCopyrightText: Saint Petersburg State University of Economics
+ * SPDX-Author-list: Maxim Krivoshein 2026
+ * SPDX-Creation-date: 2026-10-09
+ **************************************************************************/
+
import QtQuick 2.0
import Sailfish.Silica 1.0
@@ -6,10 +13,10 @@ CoverBackground {
CoverTemplate {
objectName: "applicationCover"
- primaryText: "App"
- secondaryText: qsTr("Template")
+ primaryText: qsTr("Лёд, вода, пар")
+ secondaryText: qsTr("Лабораторная №3")
icon {
- source: Qt.resolvedUrl("../icons/untitled4.svg")
+ source: Qt.resolvedUrl("../icons/PhaseButton.png")
sourceSize { width: icon.width; height: icon.height }
}
}
diff --git a/qml/icons/PhaseButton.png b/qml/icons/PhaseButton.png
new file mode 100644
index 0000000000000000000000000000000000000000..1905d0af6f3d12f06e2e329b8c413a17f82cf385
Binary files /dev/null and b/qml/icons/PhaseButton.png differ
diff --git a/qml/icons/untitled4.svg b/qml/icons/untitled4.svg
deleted file mode 100644
index 1ae97d5c8bb1c5c06f27b4852c49f470fbe4e89d..0000000000000000000000000000000000000000
Binary files a/qml/icons/untitled4.svg and /dev/null differ
diff --git a/qml/pages/AboutPage.qml b/qml/pages/AboutPage.qml
index e7d537a4979250c488e75c56886ebb35da82f5ad..80d541bd33eac024b5d81894d09bc636d07afe0e 100644
--- a/qml/pages/AboutPage.qml
+++ b/qml/pages/AboutPage.qml
@@ -1,3 +1,10 @@
+/**************************************************************************
+ * SPDX-License-Identifier: MIT
+ * SPDX-FileCopyrightText: Saint Petersburg State University of Economics
+ * SPDX-Author-list: Maxim Krivoshein 2026
+ * SPDX-Creation-date: 2026-10-09
+ **************************************************************************/
+
import QtQuick 2.0
import Sailfish.Silica 1.0
@@ -14,35 +21,56 @@ Page {
id: layout
objectName: "layout"
width: parent.width
+ spacing: Theme.paddingMedium
PageHeader {
objectName: "pageHeader"
- title: qsTr("About Application")
+ title: qsTr("О приложении")
}
Label {
objectName: "descriptionText"
- anchors { left: parent.left; right: parent.right; margins: Theme.horizontalPageMargin }
+ x: Theme.horizontalPageMargin
+ width: parent.width - 2 * Theme.horizontalPageMargin
color: palette.highlightColor
font.pixelSize: Theme.fontSizeSmall
textFormat: Text.RichText
wrapMode: Text.WordWrap
- text: qsTr("#descriptionText")
+ text: qsTr("Лабораторная работа №3. Компонент PhaseButton в отдельном QML-файле: кнопка с тремя состояниями «лёд», «вода» и «пар», оформленными через States, и анимацией каждого перехода через Transitions.")
}
SectionHeader {
- objectName: "licenseHeader"
- text: qsTr("3-Clause BSD License")
+ objectName: "authorHeader"
+ text: qsTr("Автор")
}
Label {
- objectName: "licenseText"
- anchors { left: parent.left; right: parent.right; margins: Theme.horizontalPageMargin }
+ objectName: "authorText"
+ x: Theme.horizontalPageMargin
+ width: parent.width - 2 * Theme.horizontalPageMargin
color: palette.highlightColor
font.pixelSize: Theme.fontSizeSmall
- textFormat: Text.RichText
wrapMode: Text.WordWrap
- text: qsTr("#licenseText")
+ text: qsTr("Кривошеин Максим Константинович, группа ИС-2301. СПбГЭУ, «Разработка мобильных приложений», 2026")
+ }
+
+ SectionHeader {
+ objectName: "licenseHeader"
+ text: qsTr("Лицензия MIT")
+ }
+
+ Label {
+ objectName: "licenseText"
+ x: Theme.horizontalPageMargin
+ width: parent.width - 2 * Theme.horizontalPageMargin
+ color: palette.secondaryHighlightColor
+ font.pixelSize: Theme.fontSizeExtraSmall
+ wrapMode: Text.WordWrap
+ text: "Copyright (c) 2026 Saint-Petersburg State University of Economics. "
+ + "Permission is hereby granted, free of charge, to any person obtaining a copy "
+ + "of this software and associated documentation files (the \"Software\"), to deal "
+ + "in the Software without restriction, subject to the conditions of the MIT License. "
+ + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND."
}
}
}
diff --git a/qml/pages/MainPage.qml b/qml/pages/MainPage.qml
index f1032faea6ffc979c059af39696cfe8045b21d7e..06fc46fac0c193a8260c25071bc268ebe57f8484 100644
--- a/qml/pages/MainPage.qml
+++ b/qml/pages/MainPage.qml
@@ -1,21 +1,103 @@
+/**************************************************************************
+ * SPDX-License-Identifier: MIT
+ * SPDX-FileCopyrightText: Saint Petersburg State University of Economics
+ * SPDX-Author-list: Maxim Krivoshein 2026
+ * SPDX-Creation-date: 2026-10-09
+ **************************************************************************/
+
import QtQuick 2.0
import Sailfish.Silica 1.0
+import "../components"
Page {
+ id: page
+
+ readonly property var phaseNames: ["ice", "water", "steam"]
+ readonly property var phaseTitles: [qsTr("лёд"), qsTr("вода"), qsTr("пар")]
+
objectName: "mainPage"
allowedOrientations: Orientation.All
- PageHeader {
- objectName: "pageHeader"
- title: qsTr("Template")
- extraContent.children: [
- IconButton {
- objectName: "aboutButton"
- icon.source: "image://theme/icon-m-about"
- anchors.verticalCenter: parent.verticalCenter
+ SilicaFlickable {
+ anchors.fill: parent
+ contentHeight: column.height + Theme.paddingLarge
+ PullDownMenu {
+ MenuItem {
+ text: qsTr("О приложении")
onClicked: pageStack.push(Qt.resolvedUrl("AboutPage.qml"))
}
- ]
+ }
+
+ Column {
+ id: column
+
+ width: parent.width
+ spacing: Theme.paddingLarge
+
+ PageHeader {
+ objectName: "pageHeader"
+ title: qsTr("Лёд, вода, пар")
+ }
+
+ Item {
+ width: parent.width
+ height: phaseButton.height + Theme.itemSizeMedium
+
+ PhaseButton {
+ id: phaseButton
+
+ objectName: "phaseButton"
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ bottom: parent.bottom
+ }
+ onStateChanged: console.log("Состояние кнопки: " + state)
+ }
+ }
+
+ Label {
+ x: Theme.horizontalPageMargin
+ width: parent.width - 2 * Theme.horizontalPageMargin
+ horizontalAlignment: Text.AlignHCenter
+ wrapMode: Text.WordWrap
+ color: Theme.secondaryHighlightColor
+ text: qsTr("Нажмите на кнопку, чтобы перевести воду в следующее агрегатное состояние")
+ }
+
+ SectionHeader {
+ text: qsTr("Текущее состояние")
+ }
+
+ Row {
+ anchors.horizontalCenter: parent.horizontalCenter
+ spacing: Theme.paddingMedium
+
+ Repeater {
+ model: page.phaseNames.length
+
+ BackgroundItem {
+ id: chip
+
+ readonly property bool current: phaseButton.state === page.phaseNames[index]
+
+ width: Theme.itemSizeExtraLarge
+ height: Theme.itemSizeSmall
+ highlighted: down || current
+
+ Label {
+ anchors.centerIn: parent
+ text: page.phaseTitles[index]
+ color: chip.current ? Theme.highlightColor : Theme.primaryColor
+ font.bold: chip.current
+ }
+
+ onClicked: phaseButton.state = page.phaseNames[index]
+ }
+ }
+ }
+ }
+
+ VerticalScrollDecorator {}
}
}
diff --git a/qml/untitled4.qml b/qml/untitled4.qml
deleted file mode 100644
index 94eb75a33ff4cb750e04e62592cac8ca28b17e57..0000000000000000000000000000000000000000
--- a/qml/untitled4.qml
+++ /dev/null
@@ -1,9 +0,0 @@
-import QtQuick 2.0
-import Sailfish.Silica 1.0
-
-ApplicationWindow {
- objectName: "applicationWindow"
- initialPage: Qt.resolvedUrl("pages/MainPage.qml")
- cover: Qt.resolvedUrl("cover/DefaultCoverPage.qml")
- allowedOrientations: defaultAllowedOrientations
-}
diff --git a/resources.qrc b/resources.qrc
new file mode 100644
index 0000000000000000000000000000000000000000..b6902309969e4e227c93bf49ce4e0609ed23d310
--- /dev/null
+++ b/resources.qrc
@@ -0,0 +1,7 @@
+
+
+ images/ice.png
+ images/water.png
+ images/steam.png
+
+
diff --git a/rpm/ru.template.untitled4.spec b/rpm/ru.krivoshein.PhaseButton.spec
similarity index 52%
rename from rpm/ru.template.untitled4.spec
rename to rpm/ru.krivoshein.PhaseButton.spec
index aa9a9ec77765471232eba512a58c1289c5e52608..ceed22db3bc98f62c0cc8df3e01e0cb9f2066e6f 100644
--- a/rpm/ru.template.untitled4.spec
+++ b/rpm/ru.krivoshein.PhaseButton.spec
@@ -1,9 +1,9 @@
-Name: ru.template.untitled4
-Summary: Моё приложения для ОС Аврора
-Version: 0.1
+Name: ru.krivoshein.PhaseButton
+Summary: Кнопка с тремя состояниями: лёд, вода, пар
+Version: 1.0
Release: 1
-License: BSD-3-Clause
-URL: https://auroraos.ru
+License: MIT
+URL: https://hub.mos.ru/unecon_mobiledevelopment_2026/is-2301/maximkrivoshein
Source0: %{name}-%{version}.tar.bz2
Requires: sailfishsilica-qt5 >= 0.10.9
@@ -13,7 +13,8 @@ BuildRequires: pkgconfig(Qt5Qml)
BuildRequires: pkgconfig(Qt5Quick)
%description
-Короткое описание моего приложения для ОС Аврора
+Кнопка с тремя состояниями: лёд, вода, пар.
+Лабораторная работа по дисциплине «Разработка мобильных приложений», СПбГЭУ.
%prep
%autosetup
diff --git a/rpm/ru.template.untitled4.changes.in b/rpm/ru.template.untitled4.changes.in
deleted file mode 100644
index 9d44a2eddd1fe8556feffd0cbee7870435b630ad..0000000000000000000000000000000000000000
--- a/rpm/ru.template.untitled4.changes.in
+++ /dev/null
@@ -1,15 +0,0 @@
-# Rename this file as ru.template.untitled4.changes to include changelog
-# entries in your RPM file.
-#
-# Add new changelog entries following the format below.
-# Add newest entries to the top of the list.
-# Separate entries from eachother with a blank line.
-#
-# Alternatively, if your changelog is automatically generated (e.g. with
-# the git-change-log command provided with Sailfish OS SDK), create a
-# ru.template.untitled4.changes.run script to let mb2 run the required commands for you.
-
-# * date Author's Name version-release
-# - Summary of changes
-
-
diff --git a/rpm/ru.template.untitled4.changes.run.in b/rpm/ru.template.untitled4.changes.run.in
deleted file mode 100755
index 8b3f6bd5bb7f4b4bbe1eb112a8d6f7c80f6ac522..0000000000000000000000000000000000000000
--- a/rpm/ru.template.untitled4.changes.run.in
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# Rename this file as ru.template.untitled4.changes.run to let mb2 automatically
-# generate changelog from well formatted Git commit messages and tag
-# annotations.
-
-git-change-log
-
-# Here are some basic examples how to change from the default behavior. Run
-# git-change-log --help inside the Aurora OS SDK chroot or build engine to
-# learn all the options git-change-log accepts.
-
-# Use a subset of tags
-#git-change-log --tags refs/tags/my-prefix/*
-
-# Group entries by minor revision, suppress headlines for patch-level revisions
-#git-change-log --dense '/[0-9]+.[0-9+$'
-
-# Trim very old changes
-#git-change-log --since 2014-04-01
-#echo '[ Some changelog entries trimmed for brevity ]'
-
-# Use the subjects (first lines) of tag annotations when no entry would be
-# included for a revision otherwise
-#git-change-log --auto-add-annotations
diff --git a/ru.krivoshein.PhaseButton.desktop b/ru.krivoshein.PhaseButton.desktop
new file mode 100644
index 0000000000000000000000000000000000000000..5dab69b244b71c62795362d8efb5b79d40173143
--- /dev/null
+++ b/ru.krivoshein.PhaseButton.desktop
@@ -0,0 +1,13 @@
+[Desktop Entry]
+Type=Application
+X-Nemo-Application-Type=silica-qt5
+Icon=ru.krivoshein.PhaseButton
+Exec=/usr/bin/ru.krivoshein.PhaseButton
+Name=Ice Water Steam
+Name[ru]=Лёд, вода, пар
+
+[X-Application]
+Permissions=
+OrganizationName=ru.krivoshein
+ApplicationName=PhaseButton
+ExecDBus=/usr/bin/ru.krivoshein.PhaseButton
diff --git a/ru.krivoshein.PhaseButton.pro b/ru.krivoshein.PhaseButton.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e7914adaccd47b2816a7c4eec0de741fd68c55ac
--- /dev/null
+++ b/ru.krivoshein.PhaseButton.pro
@@ -0,0 +1,32 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: Saint Petersburg State University of Economics
+# SPDX-Author-list: Maxim Krivoshein 2026
+# SPDX-Creation-date: 2026-10-09
+
+TARGET = ru.krivoshein.PhaseButton
+
+CONFIG += \
+ auroraapp
+
+PKGCONFIG += \
+
+SOURCES += \
+ src/main.cpp \
+
+HEADERS += \
+
+RESOURCES += \
+ resources.qrc \
+
+DISTFILES += \
+ rpm/ru.krivoshein.PhaseButton.spec \
+ README.md \
+ LICENSE \
+
+AURORAAPP_ICONS = 86x86 108x108 128x128 172x172
+
+CONFIG += auroraapp_i18n
+
+TRANSLATIONS += \
+ translations/ru.krivoshein.PhaseButton.ts \
+ translations/ru.krivoshein.PhaseButton-ru.ts \
diff --git a/ru.template.untitled4.desktop b/ru.template.untitled4.desktop
deleted file mode 100644
index 7a5e9c31fc31ee7c0934e24e9e7f36857acaf64f..0000000000000000000000000000000000000000
--- a/ru.template.untitled4.desktop
+++ /dev/null
@@ -1,13 +0,0 @@
-[Desktop Entry]
-Type=Application
-X-Nemo-Application-Type=silica-qt5
-Icon=ru.template.untitled4
-Exec=/usr/bin/ru.template.untitled4
-Name=Template
-Name[ru]=Шаблон
-
-[X-Application]
-Permissions=
-OrganizationName=ru.template
-ApplicationName=untitled4
-ExecDBus=/usr/bin/ru.template.untitled4
diff --git a/ru.template.untitled4.pro b/ru.template.untitled4.pro
deleted file mode 100644
index e22010076c928388b47b598f98734d9546008728..0000000000000000000000000000000000000000
--- a/ru.template.untitled4.pro
+++ /dev/null
@@ -1,22 +0,0 @@
-TARGET = ru.template.untitled4
-
-CONFIG += \
- auroraapp
-
-PKGCONFIG += \
-
-SOURCES += \
- src/main.cpp \
-
-HEADERS += \
-
-DISTFILES += \
- rpm/ru.template.untitled4.spec \
-
-AURORAAPP_ICONS = 86x86 108x108 128x128 172x172
-
-CONFIG += auroraapp_i18n
-
-TRANSLATIONS += \
- translations/ru.template.untitled4.ts \
- translations/ru.template.untitled4-ru.ts \
diff --git a/screenshots/01_ice.png b/screenshots/01_ice.png
new file mode 100644
index 0000000000000000000000000000000000000000..11969834e85d188a5b11968d33265b353a33f52c
Binary files /dev/null and b/screenshots/01_ice.png differ
diff --git a/screenshots/02_water.png b/screenshots/02_water.png
new file mode 100644
index 0000000000000000000000000000000000000000..276fa57ebd9821e1f04a9e7369e3b86b89071ed2
Binary files /dev/null and b/screenshots/02_water.png differ
diff --git a/screenshots/03_steam.png b/screenshots/03_steam.png
new file mode 100644
index 0000000000000000000000000000000000000000..fdd8df4f4fa15df586b79b4bf6cd541e2a4fa32b
Binary files /dev/null and b/screenshots/03_steam.png differ
diff --git a/src/main.cpp b/src/main.cpp
index a697cb65d62f7ce985ad91c2f6149296e9183af3..62596d5106bd7c79ef28f1a1f5f7c29618c8000e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,14 +1,21 @@
-#include
+/**************************************************************************
+ * SPDX-License-Identifier: MIT
+ * SPDX-FileCopyrightText: Saint Petersburg State University of Economics
+ * SPDX-Author-list: Maxim Krivoshein 2026
+ * SPDX-Creation-date: 2026-10-09
+ **************************************************************************/
+
#include
+#include
int main(int argc, char *argv[])
{
QScopedPointer application(Aurora::Application::application(argc, argv));
- application->setOrganizationName(QStringLiteral("ru.template"));
- application->setApplicationName(QStringLiteral("untitled4"));
+ application->setOrganizationName(QStringLiteral("ru.krivoshein"));
+ application->setApplicationName(QStringLiteral("PhaseButton"));
QScopedPointer view(Aurora::Application::createView());
- view->setSource(Aurora::Application::pathTo(QStringLiteral("qml/untitled4.qml")));
+ view->setSource(Aurora::Application::pathTo(QStringLiteral("qml/PhaseButton.qml")));
view->show();
return application->exec();
diff --git a/translations/ru.krivoshein.PhaseButton-ru.ts b/translations/ru.krivoshein.PhaseButton-ru.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0ecb9f308dbcdf3d95c423563d2836310c4dc639
--- /dev/null
+++ b/translations/ru.krivoshein.PhaseButton-ru.ts
@@ -0,0 +1,96 @@
+
+
+
+
+ AboutPage
+
+ О приложении
+
+
+
+ Лабораторная работа №3. Компонент PhaseButton в отдельном QML-файле: кнопка с тремя состояниями «лёд», «вода» и «пар», оформленными через States, и анимацией каждого перехода через Transitions.
+
+
+
+ Автор
+
+
+
+ Кривошеин Максим Константинович, группа ИС-2301. СПбГЭУ, «Разработка мобильных приложений», 2026
+
+
+
+ Лицензия MIT
+
+
+
+
+ DefaultCoverPage
+
+ Лёд, вода, пар
+
+
+
+ Лабораторная №3
+
+
+
+
+ MainPage
+
+ лёд
+
+
+
+ вода
+
+
+
+ пар
+
+
+
+ О приложении
+
+
+
+ Лёд, вода, пар
+
+
+
+ Нажмите на кнопку, чтобы перевести воду в следующее агрегатное состояние
+
+
+
+ Текущее состояние
+
+
+
+
+ PhaseButton
+
+ лёд
+
+
+
+ ниже 0 °C
+
+
+
+ вода
+
+
+
+ от 0 до 100 °C
+
+
+
+ пар
+
+
+
+ выше 100 °C
+
+
+
+
diff --git a/translations/ru.krivoshein.PhaseButton.ts b/translations/ru.krivoshein.PhaseButton.ts
new file mode 100644
index 0000000000000000000000000000000000000000..35527ba016df1a4719548c62467bbd21f531efda
--- /dev/null
+++ b/translations/ru.krivoshein.PhaseButton.ts
@@ -0,0 +1,96 @@
+
+
+
+
+ AboutPage
+
+ О приложении
+
+
+
+ Лабораторная работа №3. Компонент PhaseButton в отдельном QML-файле: кнопка с тремя состояниями «лёд», «вода» и «пар», оформленными через States, и анимацией каждого перехода через Transitions.
+
+
+
+ Автор
+
+
+
+ Кривошеин Максим Константинович, группа ИС-2301. СПбГЭУ, «Разработка мобильных приложений», 2026
+
+
+
+ Лицензия MIT
+
+
+
+
+ DefaultCoverPage
+
+ Лёд, вода, пар
+
+
+
+ Лабораторная №3
+
+
+
+
+ MainPage
+
+ лёд
+
+
+
+ вода
+
+
+
+ пар
+
+
+
+ О приложении
+
+
+
+ Лёд, вода, пар
+
+
+
+ Нажмите на кнопку, чтобы перевести воду в следующее агрегатное состояние
+
+
+
+ Текущее состояние
+
+
+
+
+ PhaseButton
+
+ лёд
+
+
+
+ ниже 0 °C
+
+
+
+ вода
+
+
+
+ от 0 до 100 °C
+
+
+
+ пар
+
+
+
+ выше 100 °C
+
+
+
+
diff --git a/translations/ru.template.untitled4-ru.ts b/translations/ru.template.untitled4-ru.ts
deleted file mode 100644
index 997e436e495e06993b3d1714508bb3116f3d8b22..0000000000000000000000000000000000000000
--- a/translations/ru.template.untitled4-ru.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
- AboutPage
-
-
- About Application
- О приложении
-
-
-
- #descriptionText
- <p>Короткое описание моего приложения для ОС Аврора</p>
-
-
-
- 3-Clause BSD License
- Лицензия 3-Clause BSD
-
-
-
- #licenseText
- <p><i>Copyright (C)2025 ru.template</i></p>
-<p>Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:</p>
-<ol>
- <li>Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.</li>
- <li>Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.</li>
- <li>Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.</li>
-</ol>
-<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</p>
-
-
-
- DefaultCoverPage
-
-
- Template
- Шаблон
-
-
-
- MainPage
-
-
- Template
- Шаблон
-
-
-
diff --git a/translations/ru.template.untitled4.ts b/translations/ru.template.untitled4.ts
deleted file mode 100644
index 268ad0f854b4f7ddccfac065b1dd3b2c54a9d9fd..0000000000000000000000000000000000000000
--- a/translations/ru.template.untitled4.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
- AboutPage
-
-
- About Application
- About Application
-
-
-
- #descriptionText
- <p>Короткое описание моего приложения для ОС Аврора</p>
-
-
-
- 3-Clause BSD License
- 3-Clause BSD License
-
-
-
- #licenseText
- <p><i>Copyright (C)2025 ru.template</i></p>
-<p>Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:</p>
-<ol>
- <li>Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.</li>
- <li>Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.</li>
- <li>Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.</li>
-</ol>
-<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</p>
-
-
-
- DefaultCoverPage
-
-
- Template
- Template
-
-
-
- MainPage
-
-
- Template
- Template
-
-
-