| 2742 | } |
| 2743 | |
| 2744 | float ImGuiMenu::drawTransform_() |
| 2745 | { |
| 2746 | // Use topmost-selected objects so that when a parent and its child are both selected, |
| 2747 | // only the parent is edited — otherwise applying the same xf to both would move the |
| 2748 | // child twice (once through its own xf, once through the parent's cascade). |
| 2749 | const auto& selected = SceneCache::getAllTopmostObjects<Object, ObjectSelectivityType::Selected>(); |
| 2750 | |
| 2751 | // Hide the panel if nothing is selected or any selected object is locked — |
| 2752 | // a generalisation of the original single-object `!selected[0]->isLocked()` gate. |
| 2753 | const bool canShow = !selected.empty() |
| 2754 | && std::none_of( selected.begin(), selected.end(), |
| 2755 | []( const auto& o ){ return o->isLocked(); } ); |
| 2756 | if ( !canShow ) |
| 2757 | { |
| 2758 | transformBlockShown_ = false; |
| 2759 | return 0.f; |
| 2760 | } |
| 2761 | |
| 2762 | auto& style = ImGui::GetStyle(); |
| 2763 | |
| 2764 | // Fix up the scene-list scroll on the frame the transform block first appears, so the |
| 2765 | // selected row stays in view as the properties panel grows by the block's height. |
| 2766 | if ( !transformBlockShown_ ) |
| 2767 | { |
| 2768 | transformBlockShown_ = true; |
| 2769 | sceneObjectsList_->setNextFrameFixScroll(); |
| 2770 | } |
| 2771 | |
| 2772 | // When transforms differ across the selection, we display "—" in the fields, do not |
| 2773 | // enter the editing path, and do not open the context menu — its Copy/Paste/Reset |
| 2774 | // operations assume a single shared xf to read from and write back to. |
| 2775 | const AffineXf3f& xf0 = selected.front()->xf(); |
| 2776 | const bool allSame = std::all_of( selected.begin() + 1, selected.end(), |
| 2777 | [&]( const auto& o ){ return o->xf() == xf0; } ); |
| 2778 | |
| 2779 | float resultHeight_ = ImGui::GetTextLineHeight() + style.FramePadding.y * 2 + style.ItemSpacing.y; |
| 2780 | bool openedContext = false; |
| 2781 | if ( drawCollapsingHeaderTransform_() ) |
| 2782 | { |
| 2783 | openedContext = drawTransformContextMenu_( selected ); |
| 2784 | const float transformHeight = ( ImGui::GetTextLineHeight() + style.FramePadding.y * 2 ) * 3 + style.ItemSpacing.y * 2; |
| 2785 | resultHeight_ += transformHeight + style.ItemSpacing.y; |
| 2786 | ImGui::BeginChild( "SceneTransform", ImVec2( 0, transformHeight ) ); |
| 2787 | |
| 2788 | auto xf = xf0; |
| 2789 | Matrix3f q, r; |
| 2790 | Vector3f euler; |
| 2791 | Vector3f scale; |
| 2792 | if ( allSame ) |
| 2793 | { |
| 2794 | decomposeMatrix3( xf.A, q, r ); |
| 2795 | euler = ( 180 / PI_F ) * q.toEulerAngles(); |
| 2796 | scale = Vector3f{ r.x.x, r.y.y, r.z.z }; |
| 2797 | } |
| 2798 | |
| 2799 | bool inputDeactivated = false; |
| 2800 | bool inputChanged = false; |
| 2801 |
nothing calls this directly
no test coverage detected