Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions TESTLISTE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions src/ui/PanelContainerWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 13 additions & 0 deletions src/ui/PanelContainerWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down Expand Up @@ -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;
Expand Down
77 changes: 77 additions & 0 deletions tests/test_einbettung.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <QPushButton>
#include <QMainWindow>
#include <QMenuBar>
#include <QVBoxLayout>
#include <QSignalSpy>

#include <QApplication>
Expand All @@ -44,6 +45,7 @@ private slots:
void rahmenlosSchwebendIstEckigMitRueckweg();
void geschwisterDesOberstenFenstersUndDasKreuz();
void eingebettetBleibtDieMenueleisteImFenster();
void imFremdenGehaeuseIstDasPanelNackt();
};

void TestEinbettung::dasDatenverzeichnisHaengtNichtAmProzessnamen()
Expand Down Expand Up @@ -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<PanelContainerWidget*>(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<QPushButton*>()) {
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);
}
Loading