| 745 | } |
| 746 | |
| 747 | void PanadapterStack::dockPanadapter(const QString& panId) |
| 748 | { |
| 749 | PanFloatingWindow* fw = m_floatingWindows.take(panId); |
| 750 | if (!fw) return; |
| 751 | |
| 752 | fw->saveWindowGeometry(); |
| 753 | |
| 754 | // Hide the SpectrumWidget and release GPU resources *before* reparenting. |
| 755 | // On macOS with WA_NativeWindow, the native NSView must be torn down |
| 756 | // cleanly while still owned by a valid parent window. If we let |
| 757 | // takeApplet() orphan to nullptr first, the double NSView lifecycle |
| 758 | // (destroy → create top-level → destroy → embed) breaks the parent |
| 759 | // window's NSResponder chain on macOS Tahoe, freezing all input (#1344). |
| 760 | PanadapterApplet* applet = fw->applet(); |
| 761 | SpectrumWidget* sw = applet ? applet->spectrumWidget() : nullptr; |
| 762 | if (sw) { |
| 763 | sw->hide(); |
| 764 | sw->prepareForTopLevelChange(); |
| 765 | sw->resetGpuResources(); |
| 766 | } |
| 767 | |
| 768 | applet = fw->takeApplet(); |
| 769 | fw->hide(); |
| 770 | fw->deleteLater(); |
| 771 | saveFloatingState(); |
| 772 | |
| 773 | if (!applet) return; |
| 774 | |
| 775 | applet->setFloatingState(false); |
| 776 | applet->spectrumWidget()->setFloating(false); |
| 777 | // Update multi-pan mode on all applets |
| 778 | bool multi = m_pans.size() > 1; |
| 779 | for (auto* a : m_pans) |
| 780 | a->setMultiPanMode(multi); |
| 781 | |
| 782 | // Reparent directly into the splitter — addWidget() calls setParent() |
| 783 | // internally, so the widget goes straight from the floating window to |
| 784 | // the splitter without an intermediate top-level state. |
| 785 | m_splitter->addWidget(applet); |
| 786 | applet->show(); |
| 787 | rebuildDockedSplitter(); |
| 788 | |
| 789 | const int count = m_splitter->count(); |
| 790 | if (count > 1) { |
| 791 | int total = (m_splitter->orientation() == Qt::Horizontal) |
| 792 | ? m_splitter->width() : m_splitter->height(); |
| 793 | int each = total / count; |
| 794 | QList<int> sizes; |
| 795 | for (int i = 0; i < count; ++i) |
| 796 | sizes.append(each); |
| 797 | m_splitter->setSizes(sizes); |
| 798 | } |
| 799 | |
| 800 | // Force layout recalculation to prevent blank gaps on macOS |
| 801 | m_splitter->updateGeometry(); |
| 802 | if (auto* w = window()) { |
| 803 | if (auto* l = w->layout()) |
| 804 | l->invalidate(); |
no test coverage detected