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>
112 lines
3.6 KiB
QML
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()
|
|
}
|
|
}
|