@@ -146,13 +146,17 @@ class _UpdateHandleStub:
146146 def __init__ (self ):
147147 self .request_update_count = 0
148148 self .dirty_all_count = 0
149+ self .records = {}
149150
150151 def request_update (self ):
151152 self .request_update_count += 1
152153
153154 def dirty_all (self ):
154155 self .dirty_all_count += 1
155156
157+ def update_record_list (self , name , items ):
158+ self .records [name ] = list (items )
159+
156160
157161def _translation_matrix (tx : float , ty : float , tz : float ) -> list [list [float ]]:
158162 return [
@@ -734,7 +738,7 @@ def get_action_for_scroll(mode, modifiers):
734738
735739 # A refresh would re-bin against the new custom range; mirror that so the view the
736740 # next scroll reads from tracks the committed range.
737- def sync_view_from_custom_range ():
741+ def sync_view_from_custom_range (view_only = False ):
738742 panel ._primary_histogram_min = (
739743 panel ._auto_histogram_min
740744 if panel ._custom_range_min_value is None
@@ -1197,3 +1201,102 @@ def _set_panel_space(panel_id, space):
11971201 finally :
11981202 lf .ui .get_panel = original_get_panel
11991203 lf .ui .set_panel_space = original_set_panel_space
1204+
1205+
1206+ # --- Snappiness / elite-UX regressions -------------------------------------------------
1207+
1208+ def test_wheel_zoom_magnitude_scales_with_delta (histogram_panel_module ):
1209+ f = histogram_panel_module .HistogramPanel ._wheel_zoom_magnitude
1210+ assert f (1.0 ) == 1.0
1211+ assert f (- 1.0 ) == 1.0
1212+ assert f (120.0 ) == 1.0 # one HID notch
1213+ assert f (5.0 ) == 1.0 # small per-notch systems stay single-step
1214+ assert f (600.0 ) == pytest .approx (5.0 ) # 5-notch flick zooms 5x further
1215+ assert f (- 100000.0 ) == 8.0 # clamped
1216+
1217+
1218+ def test_approx_equal_uses_relative_tolerance (histogram_panel_module ):
1219+ f = histogram_panel_module .HistogramPanel ._approx_equal
1220+ assert f (1.0 , 1.0 )
1221+ assert f (1_000_000.0 , 1_000_000.5 ) # within 1e-6 relative
1222+ assert not f (1_000_000.0 , 1_000_100.0 ) # outside relative tolerance
1223+ assert f (0.0 , 0.0 )
1224+ assert not f (0.0 , 1e-3 )
1225+
1226+
1227+ def test_cursor_zoom_magnitude_zooms_further (histogram_panel_module ):
1228+ f = histogram_panel_module .HistogramPanel ._cursor_zoom_bounds
1229+ one = f (0.0 , 1.0 , 0.5 , 0.0 , 1.0 , zoom_in = True , magnitude = 1.0 )
1230+ five = f (0.0 , 1.0 , 0.5 , 0.0 , 1.0 , zoom_in = True , magnitude = 5.0 )
1231+ assert one is not None and five is not None
1232+ assert (one [1 ] - one [0 ]) == pytest .approx (0.8 )
1233+ assert (five [1 ] - five [0 ]) == pytest .approx (0.8 ** 5 )
1234+ assert (five [1 ] - five [0 ]) < (one [1 ] - one [0 ])
1235+
1236+
1237+ def test_refresh_view_only_reuses_cache_without_extracting (histogram_panel_module , lf , numpy , monkeypatch ):
1238+ panel = histogram_panel_module .HistogramPanel ()
1239+ panel ._handle = _UpdateHandleStub ()
1240+ data = numpy .array ([0.1 , 0.5 , 0.9 ], dtype = numpy .float32 )
1241+ panel ._primary_valid_values = lf .Tensor .from_numpy (data )
1242+ panel ._primary_finite_values_cpu = lf .Tensor .from_numpy (data )
1243+
1244+ calls = {"extract" : 0 , "rebind" : 0 }
1245+ monkeypatch .setattr (panel , "_extract_metric_values" ,
1246+ lambda * a , ** k : calls .__setitem__ ("extract" , calls ["extract" ] + 1 ))
1247+ monkeypatch .setattr (panel , "_rebind_view_from_cache" ,
1248+ lambda : calls .__setitem__ ("rebind" , calls ["rebind" ] + 1 ))
1249+
1250+ panel ._refresh (view_only = True )
1251+
1252+ assert calls ["rebind" ] == 1 # re-binned from cache
1253+ assert calls ["extract" ] == 0 # never re-extracted the scene
1254+ assert panel ._handle .dirty_all_count == 1 # exactly one repaint
1255+
1256+
1257+ def test_refresh_view_only_falls_back_when_cache_empty (histogram_panel_module , monkeypatch ):
1258+ panel = histogram_panel_module .HistogramPanel ()
1259+ panel ._handle = _UpdateHandleStub ()
1260+ panel ._primary_finite_values_cpu = None
1261+ rebind = []
1262+ monkeypatch .setattr (panel , "_rebind_view_from_cache" , lambda : rebind .append (1 ))
1263+ monkeypatch .setattr (histogram_panel_module .lf , "get_scene" , lambda : None , raising = False )
1264+
1265+ panel ._refresh (view_only = True )
1266+
1267+ assert rebind == [] # no cache -> falls through to the full (here: no-scene) path
1268+
1269+
1270+ def test_chart_dblclick_fits_and_clears_custom_range (histogram_panel_module , monkeypatch ):
1271+ panel = histogram_panel_module .HistogramPanel ()
1272+ panel ._handle = _UpdateHandleStub ()
1273+ panel ._show_chart = True
1274+ panel ._custom_range_min_value = 0.2
1275+ panel ._custom_range_max_value = 0.8
1276+ refreshed = []
1277+ monkeypatch .setattr (panel , "_refresh_range_preserving_mark" , lambda : refreshed .append (1 ))
1278+
1279+ event = _MouseEventStub (mouse_x = 0.0 )
1280+ panel ._on_chart_dblclick (event )
1281+
1282+ assert panel ._custom_range_min_value is None
1283+ assert panel ._custom_range_max_value is None
1284+ assert refreshed == [1 ]
1285+ assert event .stopped is True
1286+
1287+
1288+ def test_mouseup_aborts_drag_when_scene_invalid (histogram_panel_module , monkeypatch ):
1289+ panel = histogram_panel_module .HistogramPanel ()
1290+ panel ._handle = _UpdateHandleStub ()
1291+ panel ._dragging_mark = True
1292+ monkeypatch .setattr (histogram_panel_module .lf , "get_scene" ,
1293+ lambda : SimpleNamespace (is_valid = lambda : False ), raising = False )
1294+ reset = []
1295+ monkeypatch .setattr (panel , "_reset_marked_state" , lambda clear_scene = False : reset .append (clear_scene ))
1296+
1297+ event = _MouseEventStub (mouse_x = 0.0 )
1298+ panel ._on_document_mouseup (event )
1299+
1300+ assert panel ._dragging_mark is False
1301+ assert reset == [False ]
1302+ assert event .stopped is True
0 commit comments