diff --git a/README.md b/README.md index 1b546e1..2da3792 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,11 @@ with dpg.window(label="Engine"): dpgg.status_light(tag="ecu", label="ECU", state=True) ``` -`gauge_grid` is a wrapping, table-backed layout. `columns` is a maximum; if the parent is too -narrow for `columns * min_column_width`, the grid uses fewer columns and continues on the next row -instead of creating a horizontal scrollbar. Gauges with `width=0` or `width=-1` fill their grid cell. +`gauge_grid` is a wrapping, table-backed layout. An integer `columns` value is a maximum; if the +parent is too narrow for `columns * min_column_width`, the grid uses fewer columns and continues on +the next row instead of creating a horizontal scrollbar. Use `columns="auto"` for autobreak mode, +where the grid derives the column count from the available width. Gauges with `width=0` or +`width=-1` fill their grid cell. Gauges also accept Dear PyGui's usual `parent` argument for adding a gauge to an existing container after that container has already been created: diff --git a/docs/API_REFERENCE.md b/docs/API_REFERENCE.md index 7a4ab89..bdd3168 100644 --- a/docs/API_REFERENCE.md +++ b/docs/API_REFERENCE.md @@ -24,10 +24,14 @@ Creation functions are context managers and accept common options such as `tag`, ## Layout Helpers - `gauge_panel(**config)`: child-window panel for grouped gauges. -- `gauge_grid(columns=1, min_column_width=180, fit_gauges=True, **config)`: table-backed - wrapping gauge grid. `columns` is the maximum number of columns. If the parent is narrower than - `columns * min_column_width`, the grid uses fewer columns and wraps gauges onto later rows to - avoid horizontal scrolling. Gauges without an explicit positive width fill their grid cell. +- `gauge_grid(columns=1, min_column_width=180, fit_gauges=True, cell_padding=12, **config)`: + table-backed + wrapping gauge grid. Pass an integer `columns` to set the maximum column count; if the parent is + narrower than `columns * min_column_width`, the grid uses fewer columns and wraps gauges onto + later rows to avoid horizontal scrolling. Pass `columns="auto"` for autobreak mode, where the + column count is derived entirely from the available width and `min_column_width`. Gauges without + an explicit positive width fill their grid cell. Increase `cell_padding` if a custom Dear PyGui + theme uses wider table padding or borders. ## Runtime API diff --git a/examples/layout_grid.py b/examples/layout_grid.py index 43cfaba..35ece49 100644 --- a/examples/layout_grid.py +++ b/examples/layout_grid.py @@ -9,7 +9,7 @@ def main() -> None: dpg.create_context() try: with dpg.window(label="Grid layout", width=820, height=620): - dpg.add_text("Three-column grid: fixed column cap, automatic rows") + dpg.add_text("Max columns: columns=3 wraps onto new rows after every third gauge") with dpgg.gauge_panel(label="Wide panel", width=-1, height=270), dpgg.gauge_grid( columns=3, min_column_width=190 ): @@ -26,9 +26,10 @@ def main() -> None: dpgg.battery_gauge(tag="grid_battery", label="Battery", value=74, height=160) dpg.add_spacer(height=8) - dpg.add_text("Narrow panel: columns=3 auto-reduces to avoid horizontal scrolling") - with dpgg.gauge_panel(label="Narrow panel", width=390, height=250), dpgg.gauge_grid( - columns=3, min_column_width=180 + dpg.add_text("Autobreak: columns='auto' chooses columns from available width") + with ( + dpgg.gauge_panel(label="Autobreak narrow panel", width=430, height=285), + dpgg.gauge_grid(columns="auto", min_column_width=180), ): for index, value in enumerate((18, 42, 67, 91, 55), start=1): dpgg.digital_gauge( diff --git a/src/dpg_gauges/widget.py b/src/dpg_gauges/widget.py index 51ee1fc..e920512 100644 --- a/src/dpg_gauges/widget.py +++ b/src/dpg_gauges/widget.py @@ -134,6 +134,7 @@ class GridLayoutContext: columns: int min_column_width: int fit_gauges: bool + cell_padding: int = 12 row_height: int = 0 available_width: int = 0 _next_index: int = 0 @@ -169,7 +170,7 @@ class GridLayoutContext: def cell_width(self) -> int | None: if self.available_width <= 0: return None - return max(int(self.available_width / self.columns), 1) + return max(int(self.available_width / self.columns) - self.cell_padding, 1) def create_gauge_context(gauge_type: str, config: ConfigT) -> GaugeContext[ConfigT]: @@ -257,9 +258,15 @@ def _available_layout_width(parent: Tag | None, width: int) -> int: return 0 -def _resolved_columns(columns: int, min_column_width: int, available_width: int) -> int: - columns = max(int(columns), 1) +def _resolved_columns( + columns: int | Literal["auto"], min_column_width: int, available_width: int +) -> int: min_column_width = max(int(min_column_width), 0) + if columns == "auto": + if available_width > 0 and min_column_width > 0: + return max(1, available_width // min_column_width) + return 1 + columns = max(int(columns), 1) if available_width > 0 and min_column_width > 0: columns = min(columns, max(1, available_width // min_column_width)) return columns @@ -305,9 +312,10 @@ def gauge_panel( def gauge_grid( *, - columns: int = 1, + columns: int | Literal["auto"] = 1, min_column_width: int = 180, fit_gauges: bool = True, + cell_padding: int = 12, row_height: int = 0, tag: Tag | None = None, parent: Tag | None = None, @@ -318,7 +326,7 @@ def gauge_grid( ) -> GridLayoutContext: """Create a lightweight wrapping gauge layout container.""" if not _dpg_context_active: - return GridLayoutContext(None, 1, min_column_width, fit_gauges) + return GridLayoutContext(None, 1, min_column_width, fit_gauges, cell_padding) layout_parent, layout_width = _next_layout_cell(parent) if layout_parent is not None: parent = layout_parent @@ -340,7 +348,13 @@ def gauge_grid( for _ in range(columns): dpg.add_table_column(parent=container, width_stretch=True, init_width_or_weight=1.0) return GridLayoutContext( - container, columns, min_column_width, fit_gauges, row_height, available_width + container, + columns, + min_column_width, + fit_gauges, + cell_padding, + row_height, + available_width, ) diff --git a/tests/test_step3_widget_renderer.py b/tests/test_step3_widget_renderer.py index 9a2f037..6a72bf8 100644 --- a/tests/test_step3_widget_renderer.py +++ b/tests/test_step3_widget_renderer.py @@ -107,6 +107,20 @@ def test_grid_auto_reduces_columns_to_parent_width(dpg_context: None) -> None: 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: