diff --git a/CMakeLists.txt b/CMakeLists.txt index 45f54d7..3629c33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,13 +22,13 @@ qt_add_executable(tncat target_include_directories(tncat PRIVATE src) # QML files live in qml/ but register at the module root (TNCat.Main etc.) -set(TNCAT_QML Main Cat Chip Cup CatWindow ChipWindow CupWindow OverlayWindow SettingsWindow CatMoods) +set(TNCAT_QML Main Cat Chip Cup CatWindow ChipWindow CupWindow OverlayWindow SettingsWindow CatDefs) foreach(qmlfile IN LISTS TNCAT_QML) set_source_files_properties(qml/${qmlfile}.qml PROPERTIES QT_RESOURCE_ALIAS ${qmlfile}.qml) list(APPEND TNCAT_QML_FILES qml/${qmlfile}.qml) endforeach() -set_source_files_properties(qml/CatMoods.qml PROPERTIES QT_QML_SINGLETON_TYPE TRUE) +set_source_files_properties(qml/CatDefs.qml PROPERTIES QT_QML_SINGLETON_TYPE TRUE) qt_add_qml_module(tncat URI TNCat diff --git a/qml/Cat.qml b/qml/Cat.qml index 1a87a9c..454e12d 100644 --- a/qml/Cat.qml +++ b/qml/Cat.qml @@ -40,7 +40,15 @@ Item { readonly property bool sleeping: mood === "sleeping" readonly property bool happy: mood === "loving" || mood === "grooming" || mood === "stretching" || mood === "drinking" || petted - readonly property real unit: width / 100 + // The cat is drawn in a 100 x 90 box. The window box is larger so whiskers, + // tail and the glyphs above the head stay inside it in every pose. The slack + // has to be symmetric: facing left mirrors the art about the box centre. + // Widest reach is the right whiskers while stretching, art x 109. + readonly property real designWidth: 120 + readonly property real designHeight: 106 + readonly property real artX: 7 + readonly property real artY: 14 + readonly property real unit: width / designWidth // tail pose (design units); swish is added on top by the idle animation property real tailC1X: 14 @@ -57,8 +65,8 @@ Item { Behavior on tailX { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } } Behavior on tailY { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } } - implicitWidth: 100 - implicitHeight: 90 + implicitWidth: designWidth + implicitHeight: designHeight transform: Scale { origin.x: cat.width / 2 @@ -67,6 +75,8 @@ Item { Item { id: art + x: cat.artX * cat.unit + y: cat.artY * cat.unit width: 100 height: 90 transformOrigin: Item.TopLeft @@ -459,6 +469,46 @@ Item { } } + // Machine code tag, flashed in the headroom whenever the mood changes + Item { + id: codeTag + x: 16; y: -12 + width: codeBg.width + height: codeBg.height + opacity: 0 + visible: cat.showCodes && opacity > 0.01 + transform: Scale { xScale: cat.facingRight ? 1 : -1; origin.x: codeTag.width / 2 } + z: 10 + Accessible.ignored: true + + Rectangle { + id: codeBg + width: codeText.implicitWidth + 8 + height: codeText.implicitHeight + 4 + radius: 3 + color: "#242833" + opacity: 0.92 + antialiasing: true + + Text { + id: codeText + anchors.centerIn: parent + text: cat.moodCode + color: "#8fd6ff" + font.pixelSize: 10 + font.bold: true + font.family: "Menlo" + } + } + + SequentialAnimation { + id: codeFlash + NumberAnimation { target: codeTag; property: "opacity"; to: 1; duration: 160 } + PauseAnimation { duration: 1500 } + NumberAnimation { target: codeTag; property: "opacity"; to: 0; duration: 500 } + } + } + // speech bubble: "!" / "z" / "♥" Text { id: bubble @@ -490,47 +540,6 @@ Item { } } - // Machine code tag above the head, flashed whenever the mood changes - Item { - id: codeTag - x: 30; y: 2 - width: codeBg.width - height: codeBg.height - opacity: 0 - visible: cat.showCodes && opacity > 0.01 - transformOrigin: Item.Center - transform: Scale { xScale: cat.facingRight ? 1 : -1; origin.x: codeTag.width / 2 } - z: 10 - scale: cat.unit - Accessible.ignored: true - - Rectangle { - id: codeBg - width: codeText.implicitWidth + 10 - height: codeText.implicitHeight + 6 - radius: 4 - color: "#242833" - opacity: 0.92 - antialiasing: true - - Text { - id: codeText - anchors.centerIn: parent - text: cat.moodCode - color: "#8fd6ff" - font.pixelSize: 11 - font.bold: true - font.family: "Menlo" - } - } - - SequentialAnimation { - id: codeFlash - NumberAnimation { target: codeTag; property: "opacity"; to: 1; duration: 160 } - PauseAnimation { duration: 1500 } - NumberAnimation { target: codeTag; property: "opacity"; to: 0; duration: 500 } - } - } onMoodCodeChanged: if (showCodes && moodCode.length > 0) codeFlash.restart() diff --git a/qml/CatDefs.qml b/qml/CatDefs.qml new file mode 100644 index 0000000..9e0ab97 --- /dev/null +++ b/qml/CatDefs.qml @@ -0,0 +1,16 @@ +pragma Singleton +import QtQuick + +// Shared facts about the drawing, so every window sizes the cat the same way. +QtObject { + // Cat.qml draws in a 100 x 90 box inside a 120 x 106 window box. The slack + // holds the whiskers, the tail and the glyphs that float above the head. + readonly property real aspect: 106 / 120 + + readonly property var moodNames: ["walking", "running", "sitting", "grooming", "stretching", + "sleeping", "loving", "alert", "chasing", "batting", "drinking"] + + function mood(value) { + return moodNames[value] !== undefined ? moodNames[value] : "sitting" + } +} diff --git a/qml/CatMoods.qml b/qml/CatMoods.qml deleted file mode 100644 index 74e2f87..0000000 --- a/qml/CatMoods.qml +++ /dev/null @@ -1,13 +0,0 @@ -pragma Singleton -import QtQuick -import TNCat - -// Maps CatController::Mood onto the strings Cat.qml understands. -QtObject { - readonly property var names: ["walking", "running", "sitting", "grooming", "stretching", - "sleeping", "loving", "alert", "chasing", "batting", "drinking"] - - function name(mood) { - return names[mood] !== undefined ? names[mood] : "sitting" - } -} diff --git a/qml/CatWindow.qml b/qml/CatWindow.qml index cc960d6..33be2ce 100644 --- a/qml/CatWindow.qml +++ b/qml/CatWindow.qml @@ -13,7 +13,7 @@ Window { signal settingsRequested() width: settings.catSize - height: Math.round(settings.catSize * 0.9) + height: Math.round(settings.catSize * CatDefs.aspect) x: cat.x y: Screen.desktopAvailableHeight - height - settings.bottomMargin color: Platform.opaqueBackground ? settings.coat : "transparent" @@ -41,7 +41,7 @@ Window { anchors.fill: parent coat: win.settings.coat facingRight: win.cat.facingRight - mood: CatMoods.name(win.cat.mood) + mood: CatDefs.mood(win.cat.mood) lifted: win.cat.dragging petted: win.cat.hovered && !win.cat.dragging look: win.cat.look diff --git a/qml/OverlayWindow.qml b/qml/OverlayWindow.qml index a746434..d05012b 100644 --- a/qml/OverlayWindow.qml +++ b/qml/OverlayWindow.qml @@ -18,7 +18,7 @@ Window { property bool overlayActive: false readonly property int catW: settings.catSize - readonly property int catH: Math.round(settings.catSize * 0.9) + readonly property int catH: Math.round(settings.catSize * CatDefs.aspect) readonly property real groundY: height - catH - settings.bottomMargin // Only the cat itself should swallow clicks; everything else falls through. @@ -88,7 +88,7 @@ Window { anchors.fill: parent coat: win.settings.coat facingRight: win.cat.facingRight - mood: CatMoods.name(win.cat.mood) + mood: CatDefs.mood(win.cat.mood) lifted: win.cat.dragging petted: win.cat.hovered && !win.cat.dragging look: win.cat.look diff --git a/qml/SettingsWindow.qml b/qml/SettingsWindow.qml index c92905a..2277b40 100644 --- a/qml/SettingsWindow.qml +++ b/qml/SettingsWindow.qml @@ -37,7 +37,7 @@ Window { spacing: 16 Loader { Layout.preferredWidth: 110 - Layout.preferredHeight: 99 + Layout.preferredHeight: Math.round(110 * CatDefs.aspect) active: win.visible sourceComponent: Cat { coat: win.settings.coat diff --git a/src/catcontroller.cpp b/src/catcontroller.cpp index 6f131d1..184b3ae 100644 --- a/src/catcontroller.cpp +++ b/src/catcontroller.cpp @@ -50,7 +50,7 @@ CatController::CatController(AppSettings *settings, QObject *parent) qreal CatController::catNose() const { - return m_facingRight ? m_x + m_catWidth * 0.78 : m_x + m_catWidth * 0.22; + return m_facingRight ? m_x + m_catWidth * 0.86 : m_x + m_catWidth * 0.14; } void CatController::setX(qreal x) @@ -193,7 +193,7 @@ void CatController::coffeeBreak() return; endChase(); - const qreal reach = m_catWidth * 1.08; // just past the whiskers + const qreal reach = m_catWidth * 1.02; // just past the whiskers const qreal edge = m_catWidth * 0.5; // Put the mug on the side the cat faces, unless that runs off the screen.