@@ -54,10 +54,150 @@ namespace rs2
5454
5555 id_and_model.second .draw_option ( update_read_only_options, is_streaming, error_message, *viewer.not_model );
5656 }
57+
58+ // Composite options have no generic per-field editing UI (see embedded-filter-model.h) -
59+ // RS2_COMPOSITE_OPTION_HKR_MINZ_CONTROL gets a hardcoded editor below; everything else
60+ // just shows read-only metadata (description, byte size).
61+ for ( auto id : _composite_option_ids )
62+ {
63+ try
64+ {
65+ if ( id == RS2_COMPOSITE_OPTION_HKR_MINZ_CONTROL )
66+ {
67+ // No always-visible description line here - draw_minz_control_editor shows
68+ // it as a tooltip on hovering the framed control instead, to save vertical
69+ // space in this narrow side panel.
70+ draw_minz_control_editor ( error_message );
71+ continue ;
72+ }
73+
74+ // TextWrapped, not TextDisabled - this side panel is narrow enough that a
75+ // one-line description reliably clips instead of just looking dim.
76+ ImGui::PushStyleColor ( ImGuiCol_Text, ImGui::GetStyle ().Colors [ImGuiCol_TextDisabled] );
77+ ImGui::TextWrapped ( " %s" , _embedded_filter->get_composite_option_description ( id ) );
78+ ImGui::PopStyleColor ();
79+
80+ auto bytes = _embedded_filter->get_composite_option ( id );
81+ ImGui::TextDisabled ( " (%zu bytes, composite option - no generic editor yet)" , bytes.size () );
82+ }
83+ catch ( const std::exception& e )
84+ {
85+ error_message = e.what ();
86+ }
87+ }
88+ }
89+
90+ void embedded_filter_model::draw_minz_control_editor ( std::string & error_message )
91+ {
92+ const auto id = RS2_COMPOSITE_OPTION_HKR_MINZ_CONTROL ;
93+
94+ if ( ! _minz_editor_initialized )
95+ {
96+ try
97+ {
98+ _minz_editor_value = _embedded_filter->get_composite_option_as < rs2_minz_control >( id );
99+ _minz_editor_initialized = true ;
100+ }
101+ catch ( const std::exception & e )
102+ {
103+ error_message = e.what ();
104+ return ;
105+ }
106+ }
107+
108+ // Minimal indent - just enough padding that widget text doesn't sit flush on the frame
109+ // border - rather than the tree's full default indent, so the group sits as far left as
110+ // the panel allows.
111+ ImGui::Indent ( 4 .0f );
112+ ImGui::Dummy ( ImVec2 ( 0 , 2 ) );
113+
114+ float frame_left = ImGui::GetCursorScreenPos ().x - 4 .0f ;
115+ float frame_top = ImGui::GetCursorScreenPos ().y - 4 .0f ;
116+ float frame_width = ImGui::GetContentRegionAvail ().x + 4 .0f ;
117+
118+ bool enable = _minz_editor_value.enable != 0 ;
119+ if ( ImGui::Checkbox ( " Enable##minz" , &enable ) )
120+ _minz_editor_value.enable = enable ? 1 : 0 ;
121+
122+ // Every label below sits on its own line, with the interactive widget using a "##"-only
123+ // (invisible) label - this side panel is too narrow for "label: [====slider====] value"
124+ // all on one line; ImGui doesn't wrap inline widget labels, it just clips them.
125+ // Downscale ratio only ever takes {1,2,4} - radio buttons, not a slider, so the user
126+ // can't land on an invalid value like 3 (mirrors the field validation tool's own UI).
127+ ImGui::Text ( " Downscale Ratio:" );
128+ for ( int v : { 1 , 2 , 4 } )
129+ {
130+ ImGui::SameLine ();
131+ if ( ImGui::RadioButton ( ( std::to_string ( v ) + " ##minz_ratio" ).c_str (), _minz_editor_value.downscale_ratio == v ) )
132+ _minz_editor_value.downscale_ratio = v;
133+ }
134+
135+ ImGui::Text ( " Disparity Shift:" );
136+ int shift = _minz_editor_value.disparity_shift ;
137+ if ( ImGui::SliderInt ( " ##minz_shift" , &shift, 0 , 512 ) )
138+ _minz_editor_value.disparity_shift = shift;
139+
140+ ImGui::Text ( " Threshold (mm):" );
141+ int threshold = _minz_editor_value.threshold ;
142+ if ( ImGui::SliderInt ( " ##minz_threshold" , &threshold, 0 , 65535 ) )
143+ _minz_editor_value.threshold = threshold;
144+
145+ bool manual = _minz_editor_value.threshold_mode != 0 ;
146+ if ( ImGui::Checkbox ( " Manual Threshold##minz" , &manual ) )
147+ _minz_editor_value.threshold_mode = manual ? 1 : 0 ;
148+ if ( ImGui::IsItemHovered () )
149+ ImGui::SetTooltip ( " Unchecked = Auto (firmware-computed threshold)" );
150+
151+ // Send-only: writes the edited struct atomically and leaves the displayed fields as the
152+ // user set them. Does not read back - if the device clamps or overrides a value (e.g.
153+ // Auto-mode threshold), the fields won't reflect that until the panel is next reopened
154+ // or re-initialized, which is an accepted tradeoff for a single-purpose button.
155+ if ( ImGui::Button ( " Send##minz" ) )
156+ {
157+ try
158+ {
159+ _embedded_filter->set_composite_option_from ( id, _minz_editor_value );
160+ }
161+ catch ( const std::exception & e )
162+ {
163+ error_message = e.what ();
164+ }
165+ }
166+
167+ ImGui::Dummy ( ImVec2 ( 0 , 2 ) );
168+ float frame_bottom = ImGui::GetCursorScreenPos ().y ;
169+ ImVec2 frame_min ( frame_left, frame_top );
170+ ImVec2 frame_max ( frame_left + frame_width, frame_bottom );
171+ ImGui::GetWindowDrawList ()->AddRect (
172+ frame_min, frame_max,
173+ ImGui::GetColorU32 ( ImGuiCol_Border ),
174+ 3 .0f , // rounding
175+ 0 , // flags
176+ 2 .0f ); // thickness - visually ties every MinZ widget to one control
177+
178+ // The description (formerly an always-visible wrapped line above the frame) is now a
179+ // hover tooltip on the frame itself, to save vertical space in this narrow side panel.
180+ if ( ImGui::IsMouseHoveringRect ( frame_min, frame_max ) )
181+ {
182+ try
183+ {
184+ ImGui::SetTooltip ( " %s" , _embedded_filter->get_composite_option_description ( id ) );
185+ }
186+ catch ( const std::exception & )
187+ {
188+ // Best-effort tooltip only - a failure here shouldn't disrupt the editor itself.
189+ }
190+ }
191+
192+ ImGui::Unindent ( 4 .0f );
57193 }
58194
59195 void embedded_filter_model::embedded_filter_enable_disable (bool actual)
60196 {
197+ // Composite-only embedded filters (e.g. HKR MinZ Control) register no scalar options at
198+ // all, including this one - nothing to toggle here for them.
199+ if ( ! _embedded_filter->supports ( RS2_OPTION_EMBEDDED_FILTER_ENABLED ) )
200+ return ;
61201 _embedded_filter->set_option (RS2_OPTION_EMBEDDED_FILTER_ENABLED , actual ? 1 .0f : 0 .0f );
62202 _enabled = _embedded_filter->get_option (RS2_OPTION_EMBEDDED_FILTER_ENABLED );
63203 }
@@ -76,7 +216,17 @@ namespace rs2
76216 model ? &model->_options_invalidated : nullptr ,
77217 error_message );
78218 }
79- _enabled = _embedded_filter->get_option (RS2_OPTION_EMBEDDED_FILTER_ENABLED );
219+
220+ // Composite options are enumerated separately - completely different registry from
221+ // scalar rs2_option (see rs_composite_option.h) - so this is its own loop, not folded
222+ // into the one above.
223+ _composite_option_ids = _embedded_filter->get_supported_composite_options ();
224+
225+ // Composite-only embedded filters (e.g. HKR MinZ Control) don't register this scalar
226+ // option at all - _enabled keeps its default (true, see embedded-filter-model.h) instead
227+ // of throwing here.
228+ if ( _embedded_filter->supports ( RS2_OPTION_EMBEDDED_FILTER_ENABLED ) )
229+ _enabled = _embedded_filter->get_option (RS2_OPTION_EMBEDDED_FILTER_ENABLED );
80230
81231 try
82232 {
0 commit comments