Match Plasma panel edge areas for clicks and scrolling

This commit is contained in:
ajp_anton
2026-09-11 10:55:27 +00:00
parent 8719402d97
commit ccd0161398
8 changed files with 117 additions and 94 deletions
@@ -17,7 +17,7 @@ public:
bool event(QEvent *event) override
{
if (event->type() == QEvent::Wheel || event->type() == QEvent::MouseButtonPress
|| event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseMove) {
|| event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseButtonDblClick || event->type() == QEvent::MouseMove) {
++delivered;
return true;
}
@@ -31,6 +31,7 @@ class PanelControllerTest : public QObject
private:
static void setup(PanelController &controller, PanelWindow &window, QJSEngine &engine)
{
window.resize(400, 40);
controller.setParentItem(window.contentItem());
controller.setProperty("active", true);
controller.setProperty("locateArea", QVariant::fromValue(engine.evaluate(
@@ -52,15 +53,15 @@ private:
QCoreApplication::sendEvent(&window, &event);
}
private Q_SLOTS:
void scrollHitTestingDoesNotExpandClickTargets()
void clicksAndScrollsShareWidgetAreas()
{
QJSEngine engine;
PanelWindow window;
PanelController controller;
setup(controller, window, engine);
controller.setLocateArea(engine.evaluate(u"(function(x,y,scrolling) { return scrolling ? 'launcher' : 'empty'; })"_s));
controller.setLocateArea(engine.evaluate(u"(function(x,y) { return 'launcher'; })"_s));
controller.setProperty("scrollActions", QVariantMap{{u"launcher"_s, u"brightness"_s}, {u"empty"_s, u"volume"_s}});
controller.setProperty("clickActions", QVariantMap{{u"leftClick"_s, u"mute"_s}});
controller.setProperty("clickActions", QVariantMap{{u"leftClick"_s, u"mute"_s}, {u"leftDoubleClick"_s, u"overview"_s}});
QSignalSpy scrolling(&controller, &PanelController::actionRequested);
QSignalSpy clicking(&controller, &PanelController::clickRequested);
wheel(window, 120);
@@ -68,8 +69,11 @@ private Q_SLOTS:
QCOMPARE(scrolling.first(), QVariantList({u"brightness"_s, 1}));
mouse(window, QEvent::MouseButtonPress, 20);
mouse(window, QEvent::MouseButtonRelease, 20);
QCOMPARE(clicking.count(), 1);
QCOMPARE(clicking.first(), QVariantList({u"leftClick"_s}));
mouse(window, QEvent::MouseButtonPress, 20);
mouse(window, QEvent::MouseButtonDblClick, 20);
mouse(window, QEvent::MouseButtonRelease, 20);
QCOMPARE(clicking.count(), 0);
QCOMPARE(window.delivered, 5); // Plasma handles both clicks, including margin forwarding.
}
void wheelOverridesAndNormalBehaviour()
{
@@ -145,11 +149,24 @@ private Q_SLOTS:
mouse(window, QEvent::MouseButtonRelease, 20);
QCOMPARE(actions.count(), 0);
}
void releaseOutsidePanelDoesNotClick()
{
QJSEngine engine;
PanelWindow window;
PanelController controller;
setup(controller, window, engine);
controller.setProperty("clickActions", QVariantMap{{u"leftClick"_s, u"mute"_s}});
QSignalSpy actions(&controller, &PanelController::clickRequested);
mouse(window, QEvent::MouseButtonPress, 1);
mouse(window, QEvent::MouseButtonRelease, -1);
QCOMPARE(actions.count(), 0);
}
void disabledAndOtherWindowsAreUntouched()
{
QJSEngine engine;
PanelWindow window;
PanelWindow popup;
popup.resize(400, 40);
PanelController controller;
setup(controller, window, engine);
QSignalSpy actions(&controller, &PanelController::actionRequested);
+47 -37
View File
@@ -4,6 +4,12 @@ import QtTest
import "../controller/contents/ui/areas.js" as Areas
Item {
id: scene
property Item containment: edges
property int leftPadding: edges.vertical ? 8 : 12
property int rightPadding: edges.vertical ? 8 : 14
property int topPadding: edges.vertical ? 12 : 8
property int bottomPadding: edges.vertical ? 14 : 8
width: 600
height: 100
Item {
@@ -94,68 +100,72 @@ Item {
function init() {
edges.vertical = false;
edges.reversed = false;
edges.scale = 1;
edges.x = 40;
launcher.visible = true;
}
function scrollAt(along, across) {
function areaAt(along, across) {
const p = edges.mapToItem(null, edges.vertical ? across : along, edges.vertical ? along : across);
return Areas.scrollAreaAt(edges, p.x, p.y, edges.vertical);
return Areas.areaAt(edges, p.x, p.y, scene);
}
function test_scrollMargins_data() {
function test_panelMargins_data() {
return [
{tag: "horizontal", vertical: false, scale: 1},
{tag: "vertical", vertical: true, scale: 1},
{tag: "scaled", vertical: false, scale: 1.5},
{tag: "vertical-scaled", vertical: true, scale: 1.5}
{tag: "horizontal", vertical: false, x: 40},
{tag: "vertical", vertical: true, x: 40},
{tag: "fractional-origin", vertical: false, x: 40.5},
{tag: "vertical-fractional-origin", vertical: true, x: 40.5}
];
}
function test_scrollMargins(data) {
function test_panelMargins(data) {
edges.vertical = data.vertical;
edges.scale = data.scale;
compare(scrollAt(-20, 58), "launcher"); // leading outer corner
compare(scrollAt(30, 2), "launcher"); // thickness padding above/beside icon
compare(scrollAt(420, 58), "clock"); // trailing outer corner
compare(scrollAt(65, 58), "empty"); // internal gap
compare(scrollAt(120, 58), "empty"); // explicit spacer
compare(scrollAt(200, 58), "empty");
edges.x = data.x;
compare(areaAt(-20, 58), "launcher"); // leading outer corner
compare(areaAt(30, 2), "launcher"); // thickness padding above/beside icon
compare(areaAt(420, 58), "clock"); // trailing outer corner
compare(areaAt(386, 52), "clock"); // exclusive bottom/right boundaries
compare(areaAt(385.75, 51.75), "clock"); // fractional positions just inside
compare(areaAt(65, 58), "empty"); // internal gap
compare(areaAt(120, 58), "empty"); // explicit spacer
compare(areaAt(200, 58), "empty");
edges.reversed = true; // physical order, not QObject child order
compare(scrollAt(-20, 58), "clock");
compare(scrollAt(420, 58), "launcher");
compare(areaAt(-20, 58), "clock");
compare(areaAt(420, 58), "launcher");
}
function test_clickMarginsStayEmpty() {
const p = edges.mapToItem(null, -20, 58);
compare(Areas.areaAt(edges, p.x, p.y), "empty");
compare(Areas.scrollAreaAt(edges, p.x, p.y, false), "launcher");
function test_panelViewDiscovery() {
compare(Areas.panelViewFor(launcher), scene);
const inside = launcher.mapToItem(null, 10, 10);
compare(Areas.areaAt(edges, inside.x, inside.y), "launcher");
compare(Areas.areaAt(edges, inside.x, inside.y, scene), "launcher");
compare(Areas.areaAt(edges, inside.x, inside.y, null), "");
}
function test_hiddenAndZeroSizeDoNotOwnPadding() {
launcher.visible = false;
compare(scrollAt(-20, 58), "empty"); // spacer becomes first
compare(Areas.scrollAreaAt(null, 0, 0, false), "");
compare(areaAt(-20, 58), "empty"); // don't stretch the next widget into a real gap
compare(Areas.areaAt(null, 0, 0, scene), "");
}
function taskArea(x, y) {
return Areas.areaAt(panel, x, y, {containment: panel,
leftPadding: 0, rightPadding: 0, topPadding: 0, bottomPadding: 0});
}
function test_taskThicknessPaddingKeepsUnusedSpaceEmpty() {
compare(Areas.scrollAreaAt(panel, 40, 65, false), "tasks");
compare(Areas.scrollAreaAt(panel, 150, 65, false), "empty");
compare(Areas.areaAt(panel, 40, 65), "empty");
compare(Areas.scrollAreaAt(panel, 0, 65, false), "tasks");
compare(taskArea(40, 65), "tasks");
compare(taskArea(150, 65), "empty");
compare(taskArea(0, 65), "tasks");
}
function test_taskButtonsVersusUnusedSpace() {
compare(Areas.areaAt(panel, 40, 30), "tasks");
compare(Areas.areaAt(panel, 150, 30), "empty");
compare(taskArea(40, 30), "tasks");
compare(taskArea(150, 30), "empty");
button.isWindow = false; // pinned launchers still count as buttons
compare(Areas.areaAt(panel, 40, 30), "tasks");
compare(taskArea(40, 30), "tasks");
button.visible = false;
compare(Areas.areaAt(panel, 40, 30), "empty");
compare(taskArea(40, 30), "empty");
button.visible = true;
}
function test_trayAndUnrecognizedWidgetsKeepClicks() {
compare(Areas.areaAt(panel, 240, 30), "tray");
compare(Areas.areaAt(panel, 300, 30), "other");
compare(taskArea(240, 30), "tray");
compare(taskArea(300, 30), "other");
arbitraryWidget.visible = false;
compare(Areas.areaAt(panel, 300, 30), "empty");
compare(taskArea(300, 30), "empty");
arbitraryWidget.visible = true;
compare(Areas.areaAt(null, 40, 30), "");
compare(Areas.areaAt(null, 40, 30, scene), "");
}
}
}