# pyright: reportGeneralTypeIssues=false from collections.abc import Generator from typing import cast import dearpygui.dearpygui as dpg import pytest import dpg_gauges as dpgg from dpg_gauges.state import clear_registry, get_gauge_state def _children(item: int | str) -> dict[int, list[int | str]]: return cast(dict[int, list[int | str]], dpg.get_item_children(item)) def _occupied_cell_counts(table: int | str) -> list[int]: counts: list[int] = [] for row in _children(table)[1]: occupied = 0 for cell in _children(row)[1]: if any(dpg.does_item_exist(child) for child in _children(cell)[1]): occupied += 1 counts.append(occupied) return counts @pytest.fixture def dpg_context() -> Generator[None]: clear_registry() dpgg.configure() dpg.create_context() try: yield finally: clear_registry() dpg.destroy_context() def test_dpg_gauge_shell_registers_before_yield(dpg_context: None) -> None: with ( dpg.window(label="Smoke"), dpgg.digital_gauge(tag="speed", label="Speed", value=42, width=180, height=80) as config, ): state = get_gauge_state("speed") assert config.tag == "speed" assert state.renderer is not None assert dpg.does_item_exist(state.container_tag) assert state.drawlist_tag is not None assert dpg.does_item_exist(state.drawlist_tag) def test_runtime_update_redraws_attached_gauge(dpg_context: None) -> None: with dpg.window(label="Smoke"): dpgg.digital_gauge(tag="rpm", label="RPM", value=1000, max_value=8000, width=200, height=90) dpgg.set_value("rpm", 2500) state = get_gauge_state("rpm") debug = dpgg.get_gauge_debug_state("rpm") assert state.displayed_value == 2500 assert state.dirty == 0 assert debug["renderer"]["drawlist_tag"] == "rpm__drawlist" assert "rpm__value" in debug["renderer"]["layers"]["dynamic"] def test_hidden_gauge_preserves_configured_size(dpg_context: None) -> None: with dpg.window(label="Hidden"): dpgg.digital_gauge(tag="hidden", value=1, width=160, height=70, show=False) state = get_gauge_state("hidden") assert state.measured_width == 160 assert state.measured_height == 70 assert state.last_nonzero_width == 160 assert state.last_nonzero_height == 70 dpgg.set_show("hidden", True) assert dpg.does_item_exist(state.container_tag) assert state.config.show is True def test_panel_and_grid_are_lightweight_containers(dpg_context: None) -> None: with ( dpg.window(label="Dashboard"), dpgg.gauge_panel(tag="engine", label="Engine", width=300, height=180) as panel, dpgg.gauge_grid(tag="engine_grid", columns=2) as grid, ): assert panel == "engine" assert grid == "engine_grid" dpgg.digital_gauge(tag="temp", label="Temp", value=90, width=120, height=60) assert dpg.does_item_exist("engine") assert dpg.does_item_exist("engine_grid") assert get_gauge_state("temp").renderer is not None def test_grid_columns_wrap_gauges_into_rows(dpg_context: None) -> None: with dpg.window(label="Dashboard", width=720), dpgg.gauge_grid( tag="wrap_grid", columns=3, min_column_width=1 ): for index in range(5): dpgg.digital_gauge(tag=f"metric_{index}", value=index, height=60) children = _children("wrap_grid") assert len(children[0]) == 3 assert len(children[1]) == 2 assert [len(_children(row)[1]) for row in children[1]] == [3, 2] def test_grid_auto_reduces_columns_to_parent_width(dpg_context: None) -> None: with dpg.window(label="Narrow", width=380), dpgg.gauge_grid( tag="narrow_grid", columns=3, min_column_width=180 ): for index in range(5): dpgg.digital_gauge(tag=f"narrow_metric_{index}", value=index, height=60) children = _children("narrow_grid") assert len(children[0]) == 2 assert len(children[1]) == 3 assert [len(_children(row)[1]) for row in children[1]] == [2, 2, 1] assert 0 < get_gauge_state("narrow_metric_0").config.width < 190 def test_grid_auto_columns_uses_available_width(dpg_context: None) -> None: with dpg.window(label="Auto", width=640), dpgg.gauge_grid( tag="auto_grid", columns="auto", min_column_width=180 ): for index in range(7): dpgg.digital_gauge(tag=f"auto_metric_{index}", value=index, height=60) children = _children("auto_grid") assert len(children[0]) == 3 assert len(children[1]) == 3 assert [len(_children(row)[1]) for row in children[1]] == [3, 3, 1] def test_grid_routes_panels_without_stealing_panel_children(dpg_context: None) -> None: with dpg.window(label="Nested", width=520), dpgg.gauge_grid(tag="nested_grid", columns=2): with dpgg.gauge_panel(tag="nested_panel", height=120): dpgg.digital_gauge(tag="inside_panel", value=1, height=60) dpgg.digital_gauge(tag="beside_panel", value=2, height=60) panel_cell = cast(int | str, dpg.get_item_parent("nested_panel")) assert dpg.get_item_type(panel_cell) == "mvAppItemType::mvTableCell" assert dpg.get_item_parent("inside_panel") == "nested_panel" beside_cell = cast(int | str, dpg.get_item_parent("beside_panel")) assert dpg.get_item_type(beside_cell) == "mvAppItemType::mvTableCell" def test_grid_accepts_gauges_added_after_context(dpg_context: None) -> None: with dpg.window(label="Late", width=520), dpgg.gauge_grid(tag="late_grid", columns=2): dpgg.digital_gauge(tag="late_initial", value=1, height=60) dpgg.digital_gauge(tag="late_second", parent="late_grid", value=2, height=60) dpgg.digital_gauge(tag="late_third", parent="late_grid", value=3, height=60) children = _children("late_grid") assert [len(_children(row)[1]) for row in children[1]] == [2, 1] for tag in ("late_initial", "late_second", "late_third"): cell = cast(int | str, dpg.get_item_parent(tag)) assert dpg.get_item_type(cell) == "mvAppItemType::mvTableCell" def test_auto_grid_in_autosize_panel_wraps_late_gauges(dpg_context: None) -> None: with ( dpg.window(label="Autosize", width=1200), dpgg.gauge_panel( tag="autosize_panel", width=1000, height=600, autosize_x=True, autosize_y=True ), ): dpgg.gauge_grid(tag="autosize_grid", columns="auto", min_column_width=180) for index in range(7): dpgg.digital_gauge( tag=f"autosize_metric_{index}", parent="autosize_grid", value=index, height=60 ) children = _children("autosize_grid") assert len(children[0]) == 5 assert [len(_children(row)[1]) for row in children[1]] == [5, 2] assert 0 < get_gauge_state("autosize_metric_0").config.width < 200 def test_auto_grid_refreshes_when_autosize_width_becomes_known(dpg_context: None) -> None: with ( dpg.window(label="Autosize"), dpgg.gauge_panel(tag="late_width_panel", autosize_x=True, autosize_y=True), ): grid = dpgg.gauge_grid(tag="late_width_grid", columns="auto", min_column_width=180) assert grid.columns == 1 dpg.configure_item("late_width_panel", width=1000) for index in range(6): dpgg.digital_gauge(tag=f"late_width_metric_{index}", parent="late_width_grid", value=index) children = _children("late_width_grid") assert len(children[0]) == 5 assert [len(_children(row)[1]) for row in children[1]] == [5, 1] def test_auto_grid_reflows_existing_gauges_when_panel_narrows(dpg_context: None) -> None: with dpg.window(label="Resize"), dpgg.gauge_panel( tag="resize_panel", width=1000, height=300 ): grid = dpgg.gauge_grid(tag="resize_grid", columns="auto", min_column_width=180) with grid: for index in range(6): dpgg.digital_gauge(tag=f"resize_metric_{index}", value=index, height=60) assert grid.columns == 5 children = _children("resize_grid") assert [len(_children(row)[1]) for row in children[1]] == [5, 1] dpg.configure_item("resize_panel", width=430) grid.refresh_columns() assert grid.columns == 2 children = _children("resize_grid") assert len(children[0]) == 2 assert [len(_children(row)[1]) for row in children[1]] == [2, 2, 2] for index in range(6): cell = cast(int | str, dpg.get_item_parent(f"resize_metric_{index}")) assert dpg.get_item_type(cell) == "mvAppItemType::mvTableCell" def test_grid_compacts_after_runtime_gauge_delete(dpg_context: None) -> None: from dpg_gauges.widget import _cleanup_grid_now with dpg.window(label="Reconnect", width=800): dpgg.gauge_grid(tag="connector_grid", columns=3, min_column_width=1) for cycle in range(3): tags = [f"connector_{cycle}_{index}" for index in range(3)] for index, tag in enumerate(tags): dpgg.digital_gauge(tag=tag, parent="connector_grid", value=index, height=60) children = _children("connector_grid") assert [len(_children(row)[1]) for row in children[1]] == [3] assert _occupied_cell_counts("connector_grid") == [3] for tag in tags: dpgg.delete_gauge(tag) _cleanup_grid_now("connector_grid") children = _children("connector_grid") assert children[1] == [] def test_grid_reuses_deleted_cell_before_appending(dpg_context: None) -> None: with dpg.window(label="Partial", width=800): dpgg.gauge_grid(tag="partial_grid", columns=3, min_column_width=1) for index in range(3): dpgg.digital_gauge(tag=f"partial_{index}", parent="partial_grid", value=index, height=60) dpgg.delete_gauge("partial_1") assert _occupied_cell_counts("partial_grid") == [2] dpgg.digital_gauge(tag="partial_new", parent="partial_grid", value=99, height=60) children = _children("partial_grid") assert [len(_children(row)[1]) for row in children[1]] == [3] assert _occupied_cell_counts("partial_grid") == [3]