Files
TNCcat/qml/CatWindow.qml
T
JulianandClaude Opus 5 d685bf492a Shape the window when there is no compositor
On Xfce with compositing off, X11 has no alpha to blend against, so the
transparent background came out as a black box around the cat.

When a window comes up without an alpha channel, TNCat now cuts the window down
to the pixels the drawing covers, using the X11 shape extension, and refreshes
that 15 times a second so it follows the animation. A sitting cat in a 120 x 106
window comes out as 96 rectangles over 29 percent of the window, so most of the
box is gone. Edges are harder than with a compositor, one bit per pixel, and the
log says so and names the Xfce setting that gives smooth ones instead.

The image-to-rectangles step is platform independent and folds every device row
into its window row, which both halves the rectangle count on a scaled display
and keeps a pixel that is opaque in only one of them.

xcb and xcb-shape are picked up through pkg-config on Unix and the build still
works without them. Opaque mode now paints a neutral card rather than the coat
colour.

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

112 lines
3.6 KiB
QML

pragma ComponentBehavior: Bound
import QtQuick
import TNCat
// One small window that is exactly the cat. Used wherever a client may place
// its own window, which is everything except Wayland.
Window {
id: win
required property CatController cat
required property AppSettings settings
signal settingsRequested()
width: settings.catSize
// The drawing knows its own proportions; no constant to keep in sync.
height: Math.round(width * sprite.implicitHeight / sprite.implicitWidth)
x: cat.x
y: Screen.desktopAvailableHeight - height - settings.bottomMargin
// A neutral card reads better than the coat colour when the background
// cannot be transparent at all.
color: Platform.opaqueBackground ? "#f2f0ec" : "transparent"
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool
| Qt.NoDropShadowWindowHint | Qt.WindowDoesNotAcceptFocus
| (Platform.bypassWindowManager ? Qt.X11BypassWindowManagerHint : 0)
visible: true
title: qsTr("TNCat")
Component.onCompleted: {
cat.stageWidth = Screen.desktopAvailableWidth
cat.catWidth = width
Platform.checkTranslucency(win)
if (Platform.debug)
win.logGeometry("created")
}
function logGeometry(what) {
console.log("TNCat cat window", what,
"x", win.x, "y", win.y, "w", win.width, "h", win.height,
"| screen", Screen.width + "x" + Screen.height,
"available", Screen.desktopAvailableWidth + "x" + Screen.desktopAvailableHeight,
"| platform", Platform.name, "overlay", Platform.overlayMode)
}
onHeightChanged: if (Platform.debug) logGeometry("height changed")
onWidthChanged: {
cat.catWidth = width
if (Platform.debug)
logGeometry("width changed")
}
Screen.onDesktopAvailableWidthChanged: cat.stageWidth = Screen.desktopAvailableWidth
// Without a compositor the window is cut to the cat's own outline. The
// drawing animates, so the shape has to be refreshed; 15 Hz is enough for
// a swinging leg and cheap enough for a window this small.
Timer {
interval: 66
repeat: true
running: Platform.shapeWindows && win.visible
triggeredOnStart: true
onTriggered: Platform.updateWindowShape(win)
}
// The eyes need to know where the face sits on the desktop.
Binding {
target: win.cat
property: "catCenter"
value: Qt.point(win.x + win.width / 2, win.y + win.height / 2)
}
Cat {
id: sprite
anchors.fill: parent
coat: win.settings.coat
facingRight: win.cat.facingRight
mood: win.cat.moodName
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.width,
win.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()
}
}