From bef29c54235e692204ef97947af007f2ccb58932 Mon Sep 17 00:00:00 2001 From: Ralph Martin Fischer Date: Thu, 5 Nov 2026 10:26:48 +0100 Subject: [PATCH] Ein Panel in einem fremden Gehaeuse (Longpaths Combined-Feld) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Betreiber 2026-11-05: "im combined sollten auch alle contest optionen moeglich sein". Longpath legt ein abgeloestes Panel in sein Combined- Feld: das Feld nimmt das Widget selbst auf (Elternwechsel) und bringt seine eigene Kopfzeile mit. PanelContainerWidget::setzeFremdesGehaeuse(bool): solange an, sind Schloss, ↙, ✕ und der Anfasser rechts unten verborgen, und setFloating(false) tut nichts -- ein Andocken aus dem Logbuch heraus (Fenster-Menue, Profil) risse das Widget aus dem Feld, das es noch fuehrt. Zurueck geht es nur ueber das Gehaeuse. Fuer den PanelLayoutManager bleibt das Panel "abgeloest". test_einbettung::imFremdenGehaeuseIstDasPanelNackt -- gegen eine wirkungslose Fassung rot gefahren. Co-Authored-By: Claude Fable 5.1 --- TESTLISTE.md | 4 ++ src/ui/PanelContainerWidget.cpp | 23 ++++++++++ src/ui/PanelContainerWidget.h | 13 ++++++ tests/test_einbettung.cpp | 77 +++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+) diff --git a/TESTLISTE.md b/TESTLISTE.md index e6247b0..8a10f48 100644 --- a/TESTLISTE.md +++ b/TESTLISTE.md @@ -45,6 +45,10 @@ bewusst aufgeschoben. Contestprogramms im Fenster. Prüfstand `test_einbettung::eingebettetBleibtDieMenueleisteImFenster` (nur unter Cocoa aussagekräftig; offscreen gibt es keine Programmleiste). + Dazu (2026-11-05): ein Panel im Combined-Feld von Longpath hat + keine eigenen Knöpfe (Schloss, ↙, ✕) und keinen Anfasser, und + Fenster › Panels im Logbuch dockt es NICHT an — zurück geht es nur + über das Feld. Prüfstand `test_einbettung::imFremdenGehaeuseIstDasPanelNackt`. - [ ] **Wasserfall gegen das echte Longpath — Ursache gefunden, am Gerät noch nicht nachgefahren.** Gefahren am 2026-11-02: Longpath protokolliert „Spektrum abonniert rx 0, 718 Punkte" und sendet diff --git a/src/ui/PanelContainerWidget.cpp b/src/ui/PanelContainerWidget.cpp index a8dc15c..791ed28 100644 --- a/src/ui/PanelContainerWidget.cpp +++ b/src/ui/PanelContainerWidget.cpp @@ -198,11 +198,34 @@ bool PanelContainerWidget::trySetGeometry(const QRect& rect) return true; } +void PanelContainerWidget::setzeFremdesGehaeuse(bool an) +{ + if (m_fremdesGehaeuse == an) { return; } + m_fremdesGehaeuse = an; + if (an) { + // Keine eigenen Knoepfe: das Gehaeuse hat seine. + zeigeRueckwegAffordanz(false); + if (m_overlayLockButton) { m_overlayLockButton->hide(); } + if (m_resizeGrip) { m_resizeGrip->hide(); } + } else { + if (m_overlayLockButton) { m_overlayLockButton->show(); } + if (m_resizeGrip) { m_resizeGrip->show(); } + zeigeRueckwegAffordanz(m_floating && s_rahmenlosSchwebend); + layoutOverlayWidgets(); + } +} + void PanelContainerWidget::setFloating(bool floating) { if (m_floating == floating) { return; } + // Im fremden Gehaeuse entscheidet das Gehaeuse (siehe + // setzeFremdesGehaeuse): ein Andocken von hier aus (Fenster-Menue, + // Profil) risse das Widget aus dem Feld, das es noch fuehrt. + if (m_fremdesGehaeuse && !floating) { + return; + } if (floating) { // Die Lage in der Fläche merken, damit der Rückweg dort landet, // wo das Panel herkam. diff --git a/src/ui/PanelContainerWidget.h b/src/ui/PanelContainerWidget.h index a814a4e..cfce124 100644 --- a/src/ui/PanelContainerWidget.h +++ b/src/ui/PanelContainerWidget.h @@ -122,6 +122,18 @@ class PanelContainerWidget : public QWidget { static void setzeRahmenlosSchwebend(bool an); static bool rahmenlosSchwebend(); bool isFloating() const { return m_floating; } + // ── In einem fremden Gehaeuse (2026-11-05) ──────────────────────── + // + // Longpath legt ein Panel in sein Combined-Feld (Betreiber: "im + // combined sollten auch alle contest optionen moeglich sein"). Das + // Gehaeuse nimmt das Widget selbst auf (Elternwechsel) und bringt + // seine eigene Kopfzeile mit: Schloss, ↙, ✕ und der Anfasser rechts + // unten des Panels verschwinden solange, und setFloating(false) tut + // nichts -- zurueck in die Flaeche geht es nur ueber das Gehaeuse, + // das das Panel erst freigibt. Vorbedingung: das Panel ist abgeloest + // (isFloating()); so bleibt es fuer den PanelLayoutManager "weg". + void setzeFremdesGehaeuse(bool an); + bool fremdesGehaeuse() const { return m_fremdesGehaeuse; } // Die Fläche, in die zurückgedockt wird -- der Elternteil, den das // Panel beim Herauslösen hatte. void setDockTarget(QWidget* canvas) { m_dockTarget = canvas; } @@ -204,6 +216,7 @@ class PanelContainerWidget : public QWidget { QString m_title; bool m_locked = false; bool m_floating = false; + bool m_fremdesGehaeuse = false; QWidget* m_dockTarget = nullptr; QRect m_dockedGeometry; // Lage in der Fläche, für den Rückweg bool m_contentHasOwnChrome; diff --git a/tests/test_einbettung.cpp b/tests/test_einbettung.cpp index 001bacd..c005b82 100644 --- a/tests/test_einbettung.cpp +++ b/tests/test_einbettung.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,7 @@ private slots: void rahmenlosSchwebendIstEckigMitRueckweg(); void geschwisterDesOberstenFenstersUndDasKreuz(); void eingebettetBleibtDieMenueleisteImFenster(); + void imFremdenGehaeuseIstDasPanelNackt(); }; void TestEinbettung::dasDatenverzeichnisHaengtNichtAmProzessnamen() @@ -330,3 +332,78 @@ void TestEinbettung::eingebettetBleibtDieMenueleisteImFenster() QMenuBar vorgabe; QCOMPARE(eigenstaendig.menuBar()->isNativeMenuBar(), vorgabe.isNativeMenuBar()); } + +// 2026-11-05, Betreiber: "im combined sollten auch alle contest optionen +// moeglich sein". Ein abgeloestes Panel geht in ein fremdes Gehaeuse: +// ohne eigene Knoepfe und Anfasser, und setFloating(false) greift dort +// nicht -- zurueck geht es nur ueber das Gehaeuse. +void TestEinbettung::imFremdenGehaeuseIstDasPanelNackt() +{ + QTemporaryDir ordner; + QVERIFY(ordner.isValid()); + AppController controller; + QString fehler; + QVERIFY2(controller.openDatabase(datenbankPfad(ordner.path()), &fehler), + qPrintable(fehler)); + ContestSettings s = controller.settings(); + s.ownCallsign = QStringLiteral("OE5SOS"); + s.ownGrid = QStringLiteral("JN67UT"); + s.rigctldHost.clear(); + s.rotor1Enabled = false; + s.rotor2Enabled = false; + s.esmEnabled = false; + controller.setSettings(s); + PanelContainerWidget::setzeRahmenlosSchwebend(true); + QMainWindow oberstes; + oberstes.resize(800, 600); + oberstes.show(); + QVERIFY(QTest::qWaitForWindowExposed(&oberstes, 10000)); + auto* fenster = new MainWindow(controller, &oberstes); + + fenster->setzePanelSchwebend(QStringLiteral("map"), true); + auto* karte = qobject_cast(fenster->panelFenster(QStringLiteral("map"))); + QVERIFY(karte && karte->isWindow() && karte->isFloating()); + QVERIFY(QTest::qWaitForWindowExposed(karte, 10000)); + QTest::qWait(100); + // Die Karte hat eine Kopfzeile (Header), andere Panels sind chromlos + // mit Overlay-Knoepfen -- beide Namen suchen. + auto knopf = [karte](const char* kopf, const char* overlay) -> QPushButton* { + for (QPushButton* b : karte->findChildren()) { + if (b->objectName() == QLatin1String(kopf) + || b->objectName() == QLatin1String(overlay)) { return b; } + } + return nullptr; + }; + QPushButton* rueck = knopf("panelHeaderDockButton", "panelOverlayDockButton"); + QPushButton* kreuz = knopf("panelHeaderCloseButton", "panelOverlayCloseButton"); + QVERIFY(rueck && rueck->isVisible()); + QVERIFY(kreuz && kreuz->isVisible()); + + // Ins fremde Gehaeuse: ein gewoehnliches Widget mit Layout. + QWidget gehaeuse(&oberstes, Qt::Window); + auto* lay = new QVBoxLayout(&gehaeuse); + karte->setzeFremdesGehaeuse(true); + lay->addWidget(karte); + gehaeuse.show(); + QVERIFY(!karte->isWindow()); + QVERIFY2(karte->isFloating(), "fuer das Logbuch bleibt es abgeloest"); + QVERIFY(karte->fremdesGehaeuse()); + QVERIFY2(!rueck->isVisible(), "kein ↙ im fremden Gehaeuse"); + QVERIFY2(!kreuz->isVisible(), "kein ✕ im fremden Gehaeuse"); + // Andocken von innen greift nicht. + fenster->setzePanelSchwebend(QStringLiteral("map"), false); + QVERIFY2(karte->isFloating() && karte->parentWidget() == &gehaeuse, + "im fremden Gehaeuse dockt das Logbuch nicht selbst an"); + + // Zurueck: das Gehaeuse gibt frei, dann dockt es wieder. + lay->removeWidget(karte); + karte->setParent(nullptr); + karte->setzeFremdesGehaeuse(false); + fenster->setzePanelSchwebend(QStringLiteral("map"), false); + QVERIFY(!karte->isFloating() && !karte->fremdesGehaeuse()); + fenster->setzePanelSchwebend(QStringLiteral("map"), true); + QVERIFY(karte->isWindow()); + QVERIFY(QTest::qWaitForWindowExposed(karte, 10000)); + QVERIFY2(rueck->isVisible(), "↙ ist wieder da"); + PanelContainerWidget::setzeRahmenlosSchwebend(false); +}