Files
TNCcat/qml/OverlayWindow.qml
T
JulianandClaude Opus 5 6b9553f6d4 Stop clipping the cat at the window edge
The cat was drawn in a 100 x 90 box that was exactly the window, so anything
reaching past it was cut off: the right whiskers always, and more of them in
poses that push the head forward, up to art x 109 while stretching.

The window box is now 120 x 106 with the drawing inset at (7, 14). The slack is
symmetric on purpose, because facing left mirrors the art about the box centre,
so asymmetric slack would clip the other side instead.

The mood-name mapper grew the shared aspect ratio and became CatDefs, so every
window sizes the cat the same way. Nose and mug offsets follow the new inset.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 03:10:04 +02:00

128 lines
3.9 KiB
QML

pragma ComponentBehavior: Bound
import QtQuick
import TNCat
// One screen-filling, click-through window that holds the cat and the chip.
// Wayland does not let a client place its own window, so there the cat moves
// inside this overlay instead of the window moving across the desktop.
Window {
id: win
required property CatController cat
required property AppSettings settings
signal settingsRequested()
// Fullscreen is a visibility, not a visible flag, so the mode switch has to
// go through visibility or the two fight each other.
property bool overlayActive: false
readonly property int catW: settings.catSize
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.
function refreshInputRegion() {
Platform.setInputRegion(win, Qt.rect(Math.round(cat.x), Math.round(groundY), catW, catH))
}
visibility: overlayActive ? Window.FullScreen : Window.Hidden
color: "transparent"
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool
| Qt.NoDropShadowWindowHint | Qt.WindowDoesNotAcceptFocus
title: qsTr("TNCat")
onOverlayActiveChanged: {
if (!overlayActive)
return
cat.stageWidth = width
cat.catWidth = catW
refreshInputRegion()
}
onWidthChanged: cat.stageWidth = width
onCatWChanged: cat.catWidth = catW
Binding {
target: win.cat
property: "catCenter"
value: Qt.point(win.x + win.cat.x + win.catW / 2, win.y + win.groundY + win.catH / 2)
}
// Coalesce mask updates: one X11 round trip per frame of walking is plenty.
Timer {
interval: 33
running: win.overlayActive
repeat: true
onTriggered: win.refreshInputRegion()
}
Chip {
x: win.cat.chipX - width / 2
y: win.height - height - win.settings.bottomMargin
width: Math.round(win.settings.catSize * 0.45)
height: Math.round(width * 0.73)
visible: win.cat.chipVisible
kick: win.cat.chipKick
Behavior on x {
NumberAnimation { duration: 320; easing.type: Easing.OutQuad }
}
}
Cup {
x: win.cat.cupX - width / 2
y: win.height - height - win.settings.bottomMargin
width: Math.round(win.settings.catSize * 0.38)
height: Math.round(width * 1.18)
visible: win.cat.cupVisible
}
Item {
id: catItem
x: win.cat.x
y: win.groundY
width: win.catW
height: win.catH
Cat {
anchors.fill: parent
coat: win.settings.coat
facingRight: win.cat.facingRight
mood: CatDefs.mood(win.cat.mood)
lifted: win.cat.dragging
petted: win.cat.hovered && !win.cat.dragging
look: win.cat.look
showCodes: win.settings.showCodes
Accessible.role: Accessible.Button
Accessible.name: qsTr("TNCat")
}
HoverHandler {
onHoveredChanged: win.cat.hovered = hovered
}
DragHandler {
target: null
onActiveChanged: win.cat.dragging = active
onCentroidChanged: {
if (active)
win.cat.x = Math.max(0, Math.min(win.cat.stageWidth - win.catW,
catItem.x + centroid.position.x - centroid.pressPosition.x))
}
}
TapHandler {
acceptedButtons: Qt.LeftButton
onTapped: win.cat.pet()
onDoubleTapped: win.settingsRequested()
onLongPressed: win.settingsRequested()
}
TapHandler {
acceptedButtons: Qt.RightButton
onTapped: win.settingsRequested()
}
}
}