/* * Copyright 2018 by Marco Martin * Copyright 2018 David Edmundson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import QtQuick 2.9 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.5 as Kirigami import Mycroft 1.0 as Mycroft Item { id: root implicitWidth: Kirigami.Units.gridUnit * 5 implicitHeight: width property var circleBackgroundColor: "#F5F5F5" property var circleInnerColor: "#CD5C5C" property bool hasShadow: true state: "idle" states: [ State { name: "idle" PropertyChanges { target: innerCircle graphicsColor: circleInnerColor backgroundColor: circleBackgroundColor } PropertyChanges { target: root opacity: 0 } StateChangeScript { script: { innerCircleRotation.running = false; innerCircleRotation.to = 0; innerCircleRotation.loops = 1; innerCircleRotation.running = true; outerCircleRotation.loops = 1; outerCircleRotation.restart(); fadeTimer.running = false; } } }, State { name: "waiting" PropertyChanges { target: innerCircle graphicsColor: circleInnerColor backgroundColor: circleBackgroundColor } PropertyChanges { target: root opacity: 1 } StateChangeScript { script: { innerCircleRotation.running = false; innerCircleRotation.to = -360; innerCircleRotation.loops = 1; innerCircleRotation.running = true; outerCircleRotation.loops = 1; outerCircleRotation.restart(); fadeTimer.running = false; } } }, State { name: "loading" PropertyChanges { target: innerCircle targetRotation: 0 graphicsColor: circleInnerColor backgroundColor: circleBackgroundColor } PropertyChanges { target: root opacity: 1 } StateChangeScript { script: { innerCircleRotation.running = false; innerCircleRotation.to = innerCircle.rotation - 360; innerCircleRotation.loops = Animation.Infinite; innerCircleRotation.running = true; outerCircleRotation.loops = Animation.Infinite; outerCircleRotation.restart(); fadeTimer.running = false; } } }, State { name: "ok" PropertyChanges { target: innerCircle explicit: true targetRotation: -90 graphicsColor: Kirigami.Theme.positiveTextColor backgroundColor: Qt.tint(Kirigami.Theme.backgroundColor, Qt.rgba(Kirigami.Theme.positiveTextColor.r, Kirigami.Theme.positiveTextColor.g, Kirigami.Theme.positiveTextColor.b, 0.4)) } PropertyChanges { target: root opacity: 1 } StateChangeScript { script: { innerCircleRotation.running = false; innerCircleRotation.to = -90; innerCircleRotation.loops = 1; innerCircleRotation.running = true; outerCircleRotation.loops = 1; outerCircleRotation.restart(); fadeTimer.restart(); } } }, State { name: "error" PropertyChanges { target: innerCircle explicit: true graphicsColor: "white" backgroundColor: Qt.tint(Kirigami.Theme.backgroundColor, Qt.rgba(Kirigami.Theme.negativeTextColor.r, Kirigami.Theme.negativeTextColor.g, Kirigami.Theme.negativeTextColor.b, 0.4)) } PropertyChanges { target: root opacity: 1 } StateChangeScript { script: { innerCircleRotation.running = false; innerCircleRotation.to = 90; innerCircleRotation.loops = 1; innerCircleRotation.running = true; outerCircleRotation.loops = 1; outerCircleRotation.restart(); fadeTimer.restart(); } } } ] Connections { target: Mycroft.MycroftController onListeningChanged: { if (Mycroft.MycroftController.listening) { root.state = "waiting"; } else { fadeTimer.restart(); } } onNotUnderstood: { root.state = "idle" root.state = "error"; } onFallbackTextRecieved: { if (skill.length > 0) { root.state = "ok"; } } onServerReadyChanged: { if (Mycroft.MycroftController.serverReady) { root.state = "ok"; } } onStatusChanged: { switch (Mycroft.MycroftController.status) { case Mycroft.MycroftController.Open: root.state = Mycroft.MycroftController.serverReady ? "ok" : "loading"; break; case Mycroft.MycroftController.Connecting: root.state = "loading"; break; case Mycroft.MycroftController.Error: default: root.state = "error"; break; } } onCurrentIntentChanged: { if (Mycroft.MycroftController.currentIntent.length == 0) { if (root.state == "loading") { root.state = "idle"; } } else { root.state = "loading"; } } } Rectangle { id: background anchors.centerIn: parent width: Math.min(parent.width, parent.height) height: width color: innerCircle.backgroundColor radius: height layer.enabled: hasShadow layer.effect: DropShadow { cached: true transparentBorder: true horizontalOffset: 0 verticalOffset: 2 } } Behavior on opacity { OpacityAnimator { duration: innerCircle.animationLength easing.type: Easing.InOutCubic } } Rectangle { id: innerCircleGraphics anchors { fill: outerCircle margins: innerCircle.unit * 5 } visible: false color: innerCircle.graphicsColor radius: width } Item { id: innerCircleMask visible: false anchors.fill: innerCircleGraphics Rectangle { anchors { left: parent.left right: parent.horizontalCenter top: parent.top bottom: parent.bottom } color: "white" } } OpacityMask { id: innerCircle property int unit: Math.max(1, background.width/20) property color graphicsColor property color backgroundColor property int animationLength: 1000 property int targetRotation: 0 Behavior on graphicsColor { ColorAnimation { duration: innerCircle.animationLength easing.type: Easing.InOutCubic } } Behavior on backgroundColor { ColorAnimation { duration: innerCircle.animationLength easing.type: Easing.InOutCubic } } anchors.fill: innerCircleGraphics source: innerCircleGraphics maskSource: innerCircleMask RotationAnimator { id: innerCircleRotation target: innerCircle from: innerCircle.rotation to: 0 direction: RotationAnimator.Counterclockwise duration: innerCircle.animationLength easing.type: Easing.InOutCubic } } Item { id: outerCircle anchors { fill: background margins: innerCircle.unit * 3 } // the little dot Rectangle { width: innerCircle.unit * 2 height: width radius: width color: innerCircle.graphicsColor anchors.horizontalCenter : parent.horizontalCenter } //the circle Rectangle { anchors { fill: parent margins: innerCircle.unit * 3 } radius: width color: "transparent" border.width: innerCircle.unit border.color: innerCircle.graphicsColor } RotationAnimator { id: outerCircleRotation target: outerCircle from: outerCircle.rotation to: outerCircle.rotation + 360 - (outerCircle.rotation + 360) % 360 direction: RotationAnimator.Clockwise duration: innerCircle.animationLength easing.type: Easing.InOutCubic } } Timer { id: fadeTimer interval: 3000 repeat: false onTriggered: root.state = "idle" } }