aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/companion/Figure.qml
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-29 10:12:17 +0100
committerDanilo M. <danix@danix.xyz>2026-10-29 10:12:17 +0100
commit0a7db3b48cce5f064c20f280e345a8bf1feb5ecb (patch)
treeeaf9c4be1a832893acf327019ef71a352d9d6647 /desktop/companion/Figure.qml
parent28beb8a9600b6e74cab454401cc94c664d90bfe5 (diff)
downloadquickshell-0a7db3b48cce5f064c20f280e345a8bf1feb5ecb.tar.gz
quickshell-0a7db3b48cce5f064c20f280e345a8bf1feb5ecb.zip
companion: remove from the tree, archived as tag archive/companion
The chibi 2B companion is retired for now. Its last state is kept in the signed tag archive/companion (the commit before this one), so it can be revisited with `git checkout archive/companion`; hypr-theme carries a tag of the same name for its compositor hooks (typing/delete events and the SUPER+\ bind). Removed with it, since nothing else used them: the Companion, phrases and artists settings pages, the five companion prefs in Prefs.qml and Prefs.ready, the wallpaper heart (Admired.qml's list), the assistant's replied() signal and cardTop, and the keybinds reminder's name for the code:49 bind. The design specs and plans under docs/ stay as history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop/companion/Figure.qml')
-rw-r--r--desktop/companion/Figure.qml173
1 files changed, 0 insertions, 173 deletions
diff --git a/desktop/companion/Figure.qml b/desktop/companion/Figure.qml
deleted file mode 100644
index 285e2cb..0000000
--- a/desktop/companion/Figure.qml
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License version 2 as
-// published by the Free Software Foundation.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-import QtQuick
-import ".."
-
-// One frame of the companion, drawn at half its pixel size and standing on
-// its bottom edge: a crossfade between frames, a slow bob, a tremble, the
-// drop out of view and back, and the self-destruct flash. Which frame is Mood.js's
-// business; this only draws it.
-Item {
- id: s
-
- // The frame, and what to show when it is missing: the same outfit's
- // base, never the other outfit's, so a gap in the art cannot put the
- // skirt back mid-scene.
- property string source
- property string fallback
- // Dropped below the bottom edge and faded out.
- property bool out: false
- signal settled(bool out)
-
- // Two images: the front one stays until the back one has loaded, then
- // they swap, so a frame change never flashes empty.
- property bool flip: false
- readonly property Image front: s.flip ? b : a
- width: s.front.implicitWidth / 2
- height: s.front.implicitHeight / 2
-
- function load(url) {
- const front = s.flip ? b : a, back = s.flip ? a : b;
- if (String(front.source) === url) { back.source = ""; return; }
- if (String(back.source) === url && back.status === Image.Ready) { s.flip = !s.flip; return; }
- back.source = url;
- }
- onSourceChanged: s.load(s.source)
- Component.onCompleted: s.load(s.source)
-
- // The incoming frame fades in on top while the outgoing one stays
- // opaque under it. Fading both at once left each near half opacity
- // mid-swap, and she blinked see-through on every turn of the dj loop.
- property real fade: 1
- NumberAnimation { id: fadeIn; target: s; property: "fade"; from: 0; to: 1; duration: 120 }
- onFlipChanged: fadeIn.restart()
-
- function ready(img) {
- if (img.status === Image.Error && String(img.source) !== s.fallback) {
- img.source = s.fallback;
- return;
- }
- if (img.status === Image.Ready && img !== s.front) s.flip = !s.flip;
- }
-
- property real drop: 0
- property real bob: 0
- NumberAnimation {
- id: dropAnim
- target: s; property: "drop"; duration: 320
- onFinished: s.settled(s.out)
- }
- // Out from creation (a shell started with her hidden) never ran the drop,
- // since a starting value fires no onOutChanged, and she was drawn in place
- // with her clock stopped, frozen on whatever frame came next. The body
- // below hides her until she is shown, and the first entrance starts from
- // below like any other.
- onOutChanged: {
- dropAnim.stop();
- if (!s.out && s.drop === 0) s.drop = s.height + 24;
- dropAnim.to = s.out ? s.height + 24 : 0;
- dropAnim.easing.type = s.out ? Easing.InCubic : Easing.OutBack;
- dropAnim.start();
- }
- // A quick side to side shiver, for the wallpaper blush. Stopping can
- // leave it mid-swing, so it is put back to rest.
- property bool trembling: false
- property real shake: 0
- onTremblingChanged: if (!s.trembling) s.shake = 0
- SequentialAnimation on shake {
- running: s.trembling && !s.out
- loops: Animation.Infinite
- NumberAnimation { to: 1.5; duration: 40 }
- NumberAnimation { to: -1.5; duration: 80 }
- NumberAnimation { to: 0; duration: 40 }
- }
- SequentialAnimation on bob {
- running: !s.out
- loops: Animation.Infinite
- NumberAnimation { to: 2; duration: 1900; easing.type: Easing.InOutSine }
- NumberAnimation { to: 0; duration: 2300; easing.type: Easing.InOutSine }
- }
-
- Item {
- anchors.fill: parent
- visible: !s.out || dropAnim.running
- opacity: 1 - s.drop / (s.height + 24)
- transform: Translate { x: s.shake; y: s.drop - s.bob }
-
- Image {
- id: a
- width: implicitWidth / 2; height: implicitHeight / 2
- anchors { bottom: parent.bottom; horizontalCenter: parent.horizontalCenter }
- smooth: true; mipmap: true; asynchronous: true
- z: s.front === a ? 1 : 0
- opacity: s.front === a ? s.fade : s.fade < 1 ? 1 : 0
- onStatusChanged: s.ready(a)
- }
- Image {
- id: b
- width: implicitWidth / 2; height: implicitHeight / 2
- anchors { bottom: parent.bottom; horizontalCenter: parent.horizontalCenter }
- smooth: true; mipmap: true; asynchronous: true
- z: s.front === b ? 1 : 0
- opacity: s.front === b ? s.fade : s.fade < 1 ? 1 : 0
- onStatusChanged: s.ready(b)
- }
- }
-
- // --- self-destruct: the glow swells into a flash, then a ring and
- // sparks burst out. About 1.1 s, matching Mood.T.destruct, during which
- // the frame swaps to the leotard under the flash.
-
- function explode() { boom.restart(); }
-
- property real burst: 0
- Rectangle {
- id: flash
- anchors.centerIn: parent
- width: s.height * 1.6; height: width; radius: width / 2
- color: "white"; opacity: 0; scale: 0
- }
- Rectangle {
- id: ring
- anchors.centerIn: parent
- width: s.height * 2; height: width; radius: width / 2
- color: "transparent"
- border { width: 3; color: Theme.peach }
- opacity: 0; scale: 0
- }
- Repeater {
- model: 8
- Rectangle {
- required property int index
- readonly property real angle: index * Math.PI / 4
- width: 6; height: 6; radius: 3
- color: index % 2 ? Theme.peach : Theme.yellow
- x: s.width / 2 - 3 + Math.cos(angle) * s.burst * s.height
- y: s.height / 2 - 3 + Math.sin(angle) * s.burst * s.height
- opacity: s.burst > 0 && s.burst < 1 ? 1 - s.burst : 0
- }
- }
- SequentialAnimation {
- id: boom
- ParallelAnimation {
- NumberAnimation { target: flash; property: "scale"; from: 0; to: 1; duration: 600; easing.type: Easing.InQuad }
- NumberAnimation { target: flash; property: "opacity"; from: 0; to: 0.95; duration: 600 }
- }
- ParallelAnimation {
- NumberAnimation { target: flash; property: "opacity"; to: 0; duration: 400 }
- NumberAnimation { target: ring; property: "scale"; from: 0.2; to: 1.4; duration: 400; easing.type: Easing.OutCubic }
- NumberAnimation { target: ring; property: "opacity"; from: 1; to: 0; duration: 400 }
- NumberAnimation { target: s; property: "burst"; from: 0; to: 1; duration: 500; easing.type: Easing.OutCubic }
- }
- PropertyAction { target: s; property: "burst"; value: 0 }
- }
-}