diff --git a/evidence/native-width-dependency-supply-20261007/README.md b/evidence/native-width-dependency-supply-20261007/README.md new file mode 100644 index 0000000000..ac7ab478e8 --- /dev/null +++ b/evidence/native-width-dependency-supply-20261007/README.md @@ -0,0 +1,35 @@ +# Native width declaration supply + +Layout.get_content_width arranges at zero height: incoming height is unused, +but arrangement styles still matter. Its original HeightDependency declaration +now owns both answers through NativeOptimalWidth. The separate consumer override +in NativeWidgetWidth is deleted; shared NativeWidgetMeasurementHeight selects +container/leaf once and consumes the original compiled layout declaration. + +IndependentHeight remains conservative about styles. Undeclared arrangers and +custom width methods still receive ContextHeight from Layout.__init_subclass__; +explicit custom declarations retain their own policies. Box and relative-height +resources, epochs, descendants, placement and raw style publication are unchanged. + +Four affected checks passed in 0.46 seconds: one container acquisition for a +live custom getter, original native paint/raw writes, height-independent custom +layout hooks retaining style inputs, and unknown layout/scalar behavior. +Before/after AST covers 724/725 modules with zero omissions and all width +supply/style/measurement consumers. There is no competing runtime writer of +Layout._content_width_dependency; it remains class-declaration-owned. + +The original loaded source Toad App passed with ten tabs, 508 widgets, zero +inputs/providers and empty stderr. Pointer first-display medians were 33.9 ms +on each side (maxima 80.2/64.0 ms). Original cProfile native is_container calls +were 453/270, versus 506/303 in retained prior source App profiles. These are +observed calls, not compressed Chrome transitions or a controlled timing trial. +app.json preserves raw profile rows/hashes; stdout/stderr are retained here; +original profiles remain at +/home/ts/.cache/agent-scratch/native-width-supply-app-20261007. + +Production is unchanged from a2056caa4. Private declaration-table replacement +outside Layout.__init_subclass__ is not certified; normal custom methods and +C3 subclass declarations still bind their original policy or conservative +ContextHeight. This does not change box/relative source validity or cache reuse. +No package/public operation or reliable overall latency gain. The actual +agent-switch and ACP costs remain separate unresolved workflows. diff --git a/evidence/native-width-dependency-supply-20261007/app-stderr.log b/evidence/native-width-dependency-supply-20261007/app-stderr.log new file mode 100644 index 0000000000..e69de29bb2 diff --git a/evidence/native-width-dependency-supply-20261007/app-stdout.log b/evidence/native-width-dependency-supply-20261007/app-stdout.log new file mode 100644 index 0000000000..bf9276507e --- /dev/null +++ b/evidence/native-width-dependency-supply-20261007/app-stdout.log @@ -0,0 +1,302 @@ +{ + "boundary": "completed headless _display; not terminal pixels", + "tabs": 10, + "pointer": true, + "active_widgets": 508, + "toggle_first_paint": { + "#channels-sidebar": { + "median_ms": 33.9, + "p95_ms": 36.2, + "max_ms": 80.2 + }, + "#thread-sidebar": { + "median_ms": 33.9, + "p95_ms": 45.5, + "max_ms": 64.0 + } + } +} +{ + "publication_trace": [ + { + "elapsed_ms": 34.9, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": true, + "right_region": [ + 117, + 3, + 3, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 80.1, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": true, + "right_region": [ + 117, + 3, + 3, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 35.7, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": true, + "right_region": [ + 117, + 3, + 3, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 36.2, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": true, + "right_region": [ + 117, + 3, + 3, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 34.6, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": true, + "right_region": [ + 117, + 3, + 3, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 32.7, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": true, + "right_region": [ + 117, + 3, + 3, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 33.0, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": true, + "right_region": [ + 117, + 3, + 3, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 33.1, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": true, + "right_region": [ + 117, + 3, + 3, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 29.4, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": true, + "right_region": [ + 117, + 3, + 3, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 32.5, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": true, + "right_region": [ + 117, + 3, + 3, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 31.2, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": false, + "right_region": [ + 80, + 3, + 40, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 63.8, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": true, + "right_region": [ + 117, + 3, + 3, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 44.1, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": false, + "right_region": [ + 80, + 3, + 40, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 30.9, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": true, + "right_region": [ + 117, + 3, + 3, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 36.8, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": false, + "right_region": [ + 80, + 3, + 40, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 23.2, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": true, + "right_region": [ + 117, + 3, + 3, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 45.5, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": false, + "right_region": [ + 80, + 3, + 40, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 26.5, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": true, + "right_region": [ + 117, + 3, + 3, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 36.6, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": false, + "right_region": [ + 80, + 3, + 40, + 38 + ], + "exclusions": [] + }, + { + "elapsed_ms": 25.8, + "renderable": "ChopsUpdate", + "batch_count": 0, + "current": true, + "right_collapsed": true, + "right_region": [ + 117, + 3, + 3, + 38 + ], + "exclusions": [] + } + ] +} diff --git a/evidence/native-width-dependency-supply-20261007/app.json b/evidence/native-width-dependency-supply-20261007/app.json new file mode 100644 index 0000000000..126961a721 --- /dev/null +++ b/evidence/native-width-dependency-supply-20261007/app.json @@ -0,0 +1,196 @@ +{ + "boundary": "completed headless _display; not terminal pixels", + "tabs": 10, + "pointer": true, + "active_widgets": 508, + "toggle_first_paint": { + "#channels-sidebar": { + "median_ms": 33.9, + "p95_ms": 36.2, + "max_ms": 80.2 + }, + "#thread-sidebar": { + "median_ms": 33.9, + "p95_ms": 45.5, + "max_ms": 64.0 + } + }, + "production_head": "a2056caa4", + "terminal_exit": 0, + "stderr_bytes": 0, + "inputs": 0, + "providers": 0, + "profile_rows": { + "toggle-channels.pstats": { + "sha256": "56b272e1483fa92584dd75a8d08a995d0716bc13cc077cf48d2dc426eb824f60", + "rows": [ + { + "source": "/home/ts/wt/textual-frame-publication-20261006/src/textual/widget.py", + "function": "_relative_children_height", + "primitive_calls": 27, + "total_calls": 83, + "self_seconds": 0.00038224300000000005, + "cumulative_seconds": 0.00114021 + }, + { + "source": "/home/ts/wt/toad-right-sidebar-latency-20261006/src/toad/widgets/viewport_body.py", + "function": "is_container", + "primitive_calls": 34, + "total_calls": 34, + "self_seconds": 5.1044e-05, + "cumulative_seconds": 9.6062e-05 + }, + { + "source": "/home/ts/wt/textual-frame-publication-20261006/src/textual/scroll_view.py", + "function": "is_container", + "primitive_calls": 4, + "total_calls": 4, + "self_seconds": 7.510000000000001e-07, + "cumulative_seconds": 7.510000000000001e-07 + }, + { + "source": "/home/ts/wt/textual-frame-publication-20261006/src/textual/widget.py", + "function": "is_container", + "primitive_calls": 453, + "total_calls": 453, + "self_seconds": 0.000406363, + "cumulative_seconds": 0.0009258370000000001 + }, + { + "source": "/home/ts/wt/textual-frame-publication-20261006/src/textual/_measurement.py", + "function": "styles_sensitive", + "primitive_calls": 19, + "total_calls": 19, + "self_seconds": 2.133e-06, + "cumulative_seconds": 2.133e-06 + }, + { + "source": "/home/ts/wt/textual-frame-publication-20261006/src/textual/_measurement.py", + "function": "styles_sensitive", + "primitive_calls": 16, + "total_calls": 16, + "self_seconds": 5.363e-06, + "cumulative_seconds": 5.1880000000000005e-05 + }, + { + "source": "/home/ts/wt/textual-frame-publication-20261006/src/textual/widget.py", + "function": "_box_depends_on_available_height", + "primitive_calls": 79, + "total_calls": 86, + "self_seconds": 8.0399e-05, + "cumulative_seconds": 0.0011897350000000001 + }, + { + "source": "/home/ts/wt/textual-frame-publication-20261006/src/textual/_measurement.py", + "function": "styles_sensitive", + "primitive_calls": 32, + "total_calls": 32, + "self_seconds": 2.36e-05, + "cumulative_seconds": 0.000103424 + }, + { + "source": "/home/ts/wt/textual-frame-publication-20261006/src/textual/_measurement.py", + "function": "styles_sensitive", + "primitive_calls": 32, + "total_calls": 32, + "self_seconds": 3.3986e-05, + "cumulative_seconds": 3.3986e-05 + }, + { + "source": "/home/ts/wt/textual-frame-publication-20261006/src/textual/_measurement.py", + "function": "styles_sensitive", + "primitive_calls": 155, + "total_calls": 155, + "self_seconds": 0.000126756, + "cumulative_seconds": 0.001159951 + } + ] + }, + "toggle-thread.pstats": { + "sha256": "227a42995c84bddaa557ac73614252ea1a3a1e4364d1ae692c6361fd4a2030de", + "rows": [ + { + "source": "/home/ts/wt/textual-frame-publication-20261006/src/textual/widget.py", + "function": "_relative_children_height", + "primitive_calls": 8, + "total_calls": 8, + "self_seconds": 4.8086e-05, + "cumulative_seconds": 0.000158789 + }, + { + "source": "/home/ts/wt/toad-right-sidebar-latency-20261006/src/toad/widgets/viewport_body.py", + "function": "is_container", + "primitive_calls": 14, + "total_calls": 14, + "self_seconds": 2.4686e-05, + "cumulative_seconds": 4.5266e-05 + }, + { + "source": "/home/ts/wt/textual-frame-publication-20261006/src/textual/scroll_view.py", + "function": "is_container", + "primitive_calls": 1, + "total_calls": 1, + "self_seconds": 7.22e-07, + "cumulative_seconds": 7.22e-07 + }, + { + "source": "/home/ts/wt/textual-frame-publication-20261006/src/textual/widget.py", + "function": "is_container", + "primitive_calls": 270, + "total_calls": 270, + "self_seconds": 0.00024313, + "cumulative_seconds": 0.000561431 + }, + { + "source": "/home/ts/wt/textual-frame-publication-20261006/src/textual/_measurement.py", + "function": "styles_sensitive", + "primitive_calls": 18, + "total_calls": 18, + "self_seconds": 1.9150000000000003e-06, + "cumulative_seconds": 1.9150000000000003e-06 + }, + { + "source": "/home/ts/wt/textual-frame-publication-20261006/src/textual/_measurement.py", + "function": "styles_sensitive", + "primitive_calls": 18, + "total_calls": 18, + "self_seconds": 7.0420000000000005e-06, + "cumulative_seconds": 6.2078e-05 + }, + { + "source": "/home/ts/wt/textual-frame-publication-20261006/src/textual/widget.py", + "function": "_box_depends_on_available_height", + "primitive_calls": 46, + "total_calls": 47, + "self_seconds": 5.2324e-05, + "cumulative_seconds": 0.0007099350000000001 + }, + { + "source": "/home/ts/wt/textual-frame-publication-20261006/src/textual/_measurement.py", + "function": "styles_sensitive", + "primitive_calls": 36, + "total_calls": 36, + "self_seconds": 2.8826e-05, + "cumulative_seconds": 0.000125239 + }, + { + "source": "/home/ts/wt/textual-frame-publication-20261006/src/textual/_measurement.py", + "function": "styles_sensitive", + "primitive_calls": 36, + "total_calls": 36, + "self_seconds": 4.1107000000000005e-05, + "cumulative_seconds": 4.1107000000000005e-05 + }, + { + "source": "/home/ts/wt/textual-frame-publication-20261006/src/textual/_measurement.py", + "function": "styles_sensitive", + "primitive_calls": 117, + "total_calls": 117, + "self_seconds": 0.00010346400000000001, + "cumulative_seconds": 0.0008999370000000001 + } + ] + } + }, + "limits": "Headless source App first completed display, not pixels or an agent-switch/ACP comparison. Prior profile counts are observed runs, not a controlled timing trial." +} diff --git a/evidence/native-width-dependency-supply-20261007/checks.log b/evidence/native-width-dependency-supply-20261007/checks.log new file mode 100644 index 0000000000..665826cc38 --- /dev/null +++ b/evidence/native-width-dependency-supply-20261007/checks.log @@ -0,0 +1,2 @@ +.... [100%] +4 passed in 0.46s diff --git a/evidence/native-width-dependency-supply-20261007/source.json b/evidence/native-width-dependency-supply-20261007/source.json new file mode 100644 index 0000000000..212519dadb --- /dev/null +++ b/evidence/native-width-dependency-supply-20261007/source.json @@ -0,0 +1,424 @@ +{ + "before": { + "revision": "480f824c03c875aa3003d32c2c61166737e2c646", + "modules": 724, + "omissions": [], + "sites": [ + [ + "src/textual/_measurement.py", + 22, + "declaration", + "styles_sensitive" + ], + [ + "src/textual/_measurement.py", + 62, + "declaration", + "layout_dependency" + ], + [ + "src/textual/_measurement.py", + 80, + "declaration", + "styles_sensitive" + ], + [ + "src/textual/_measurement.py", + 89, + "declaration", + "layout_dependency" + ], + [ + "src/textual/_measurement.py", + 94, + "declaration", + "layout_dependency" + ], + [ + "src/textual/_measurement.py", + 97, + "declaration", + "styles_sensitive" + ], + [ + "src/textual/_measurement.py", + 119, + "declaration", + "styles_sensitive" + ], + [ + "src/textual/_measurement.py", + 126, + "declaration", + "styles_sensitive" + ], + [ + "src/textual/_measurement.py", + 95, + "Load", + "widget.layout._content_width_dependency" + ], + [ + "src/textual/_measurement.py", + 275, + "Load", + "widget._content_width_dependency" + ], + [ + "src/textual/_measurement.py", + 199, + "Load", + "child._content_width_dependency" + ], + [ + "src/textual/layout.py", + 280, + "declaration", + "get_content_width" + ], + [ + "src/textual/layout.py", + 245, + "Store", + "cls._content_width_dependency" + ], + [ + "src/textual/layout.py", + 253, + "Store", + "cls._content_width_dependency" + ], + [ + "src/textual/layouts/stream.py", + 124, + "declaration", + "get_content_width" + ], + [ + "src/textual/scroll_view.py", + 63, + "declaration", + "get_content_width" + ], + [ + "src/textual/widget.py", + 2104, + "declaration", + "get_content_width" + ], + [ + "src/textual/widget.py", + 4163, + "Store", + "cls._content_width_dependency" + ], + [ + "src/textual/widget.py", + 326, + "Load", + "self._content_width_dependency" + ], + [ + "src/textual/widgets/_button.py", + 376, + "declaration", + "get_content_width" + ], + [ + "src/textual/widgets/_digits.py", + 89, + "declaration", + "get_content_width" + ], + [ + "src/textual/widgets/_input.py", + 701, + "declaration", + "get_content_width" + ], + [ + "src/textual/widgets/_option_list.py", + 840, + "declaration", + "get_content_width" + ], + [ + "src/textual/widgets/_rich_log.py", + 141, + "declaration", + "get_content_width" + ], + [ + "src/textual/widgets/_rule.py", + 200, + "declaration", + "get_content_width" + ], + [ + "src/textual/widgets/_switch.py", + 196, + "declaration", + "get_content_width" + ], + [ + "src/textual/widgets/_toggle_button.py", + 231, + "declaration", + "get_content_width" + ], + [ + "tests/test_box_model.py", + 14, + "declaration", + "get_content_width" + ], + [ + "tests/test_box_model.py", + 53, + "declaration", + "get_content_width" + ], + [ + "tests/test_box_model.py", + 102, + "declaration", + "get_content_width" + ], + [ + "tests/test_box_model.py", + 151, + "declaration", + "get_content_width" + ], + [ + "tests/test_box_model.py", + 175, + "declaration", + "get_content_width" + ], + [ + "tests/test_height_independent_measurement.py", + 160, + "declaration", + "get_content_width" + ], + [ + "tests/test_line_surface_style_measurement.py", + 64, + "Load", + "widget._content_width_dependency" + ] + ] + }, + "after": { + "revision": "66921a083f01f1bf438a242707b14b1feb2a77e8", + "modules": 725, + "omissions": [], + "sites": [ + [ + "src/textual/_measurement.py", + 22, + "declaration", + "styles_sensitive" + ], + [ + "src/textual/_measurement.py", + 62, + "declaration", + "layout_dependency" + ], + [ + "src/textual/_measurement.py", + 80, + "declaration", + "styles_sensitive" + ], + [ + "src/textual/_measurement.py", + 89, + "declaration", + "layout_dependency" + ], + [ + "src/textual/_measurement.py", + 94, + "declaration", + "layout_dependency" + ], + [ + "src/textual/_measurement.py", + 109, + "declaration", + "styles_sensitive" + ], + [ + "src/textual/_measurement.py", + 118, + "declaration", + "styles_sensitive" + ], + [ + "src/textual/_measurement.py", + 123, + "declaration", + "styles_sensitive" + ], + [ + "src/textual/_measurement.py", + 95, + "Load", + "widget.layout._content_width_dependency" + ], + [ + "src/textual/_measurement.py", + 273, + "Load", + "widget._content_width_dependency" + ], + [ + "src/textual/_measurement.py", + 196, + "Load", + "child._content_width_dependency" + ], + [ + "src/textual/layout.py", + 281, + "declaration", + "get_content_width" + ], + [ + "src/textual/layout.py", + 246, + "Store", + "cls._content_width_dependency" + ], + [ + "src/textual/layout.py", + 254, + "Store", + "cls._content_width_dependency" + ], + [ + "src/textual/layouts/stream.py", + 124, + "declaration", + "get_content_width" + ], + [ + "src/textual/scroll_view.py", + 63, + "declaration", + "get_content_width" + ], + [ + "src/textual/widget.py", + 2104, + "declaration", + "get_content_width" + ], + [ + "src/textual/widget.py", + 4163, + "Store", + "cls._content_width_dependency" + ], + [ + "src/textual/widget.py", + 326, + "Load", + "self._content_width_dependency" + ], + [ + "src/textual/widgets/_button.py", + 376, + "declaration", + "get_content_width" + ], + [ + "src/textual/widgets/_digits.py", + 89, + "declaration", + "get_content_width" + ], + [ + "src/textual/widgets/_input.py", + 701, + "declaration", + "get_content_width" + ], + [ + "src/textual/widgets/_option_list.py", + 840, + "declaration", + "get_content_width" + ], + [ + "src/textual/widgets/_rich_log.py", + 141, + "declaration", + "get_content_width" + ], + [ + "src/textual/widgets/_rule.py", + 200, + "declaration", + "get_content_width" + ], + [ + "src/textual/widgets/_switch.py", + 196, + "declaration", + "get_content_width" + ], + [ + "src/textual/widgets/_toggle_button.py", + 231, + "declaration", + "get_content_width" + ], + [ + "tests/test_box_model.py", + 14, + "declaration", + "get_content_width" + ], + [ + "tests/test_box_model.py", + 53, + "declaration", + "get_content_width" + ], + [ + "tests/test_box_model.py", + 102, + "declaration", + "get_content_width" + ], + [ + "tests/test_box_model.py", + 151, + "declaration", + "get_content_width" + ], + [ + "tests/test_box_model.py", + 175, + "declaration", + "get_content_width" + ], + [ + "tests/test_height_independent_measurement.py", + 160, + "declaration", + "get_content_width" + ], + [ + "tests/test_line_surface_style_measurement.py", + 64, + "Load", + "widget._content_width_dependency" + ] + ] + } +} diff --git a/src/textual/_measurement.py b/src/textual/_measurement.py index a0e7347ad7..583690b50c 100644 --- a/src/textual/_measurement.py +++ b/src/textual/_measurement.py @@ -94,16 +94,6 @@ class NativeWidgetWidth(NativeWidgetMeasurementHeight): def layout_dependency(self, widget: Widget) -> HeightDependency: return widget.layout._content_width_dependency - def styles_sensitive(self, widget: Widget) -> bool: - if widget.is_container and widget._native_measurement_layout_hooks: - from textual.layout import Layout - - # Native optimal-width measurement selects the same arrangement. - # A custom width method retains its independently declared inputs. - if type(widget.layout).get_content_width is Layout.get_content_width: - return NATIVE_LAYOUT_HEIGHT.styles_sensitive(widget) - return super().styles_sensitive(widget) - class NativeLayoutHeight(HeightDependency): def depends(self, widget: Widget) -> bool: @@ -122,6 +112,13 @@ def styles_sensitive(self, widget: Widget) -> bool: return widget.layout._arrangement_height_dependency.styles_sensitive(widget) +class NativeOptimalWidth(IndependentHeight): + """Native width arranges at zero height but reads arrangement styles.""" + + def styles_sensitive(self, widget: Widget) -> bool: + return NATIVE_LAYOUT_HEIGHT.styles_sensitive(widget) + + class FlowHeight(HeightDependency): def styles_sensitive(self, widget: Widget) -> bool: # Native placement consumes declared geometry rules and child boxes. @@ -212,6 +209,7 @@ def depends(self, widget: Widget) -> bool: NATIVE_WIDGET_HEIGHT = NativeWidgetHeight() NATIVE_WIDGET_WIDTH = NativeWidgetWidth() NATIVE_LAYOUT_HEIGHT = NativeLayoutHeight() +NATIVE_OPTIMAL_WIDTH = NativeOptimalWidth() FLOW_HEIGHT = FlowHeight() STREAM_HEIGHT = StreamHeight() GRID_HEIGHT = GridHeight() diff --git a/src/textual/layout.py b/src/textual/layout.py index efb50a1403..8fb208c2c9 100644 --- a/src/textual/layout.py +++ b/src/textual/layout.py @@ -6,7 +6,8 @@ from textual._spatial_map import SpatialMap from textual._measurement import ( - INDEPENDENT_HEIGHT, NATIVE_LAYOUT_HEIGHT, HeightDependency, height_dependency, + NATIVE_LAYOUT_HEIGHT, NATIVE_OPTIMAL_WIDTH, + HeightDependency, height_dependency, ) from textual.canvas import Canvas, Rectangle from textual.geometry import Offset, Region, Size, Spacing @@ -276,7 +277,7 @@ def arrange( An iterable of widget location """ - @height_dependency(INDEPENDENT_HEIGHT) + @height_dependency(NATIVE_OPTIMAL_WIDTH) def get_content_width(self, widget: Widget, container: Size, viewport: Size) -> int: """Get the optimal content width by arranging children. diff --git a/tests/test_native_width_dependency_supply.py b/tests/test_native_width_dependency_supply.py new file mode 100644 index 0000000000..afdb71c9c8 --- /dev/null +++ b/tests/test_native_width_dependency_supply.py @@ -0,0 +1,18 @@ +"""Width style decisions consume the original class-bound measurement supply.""" +from textual._measurement import NATIVE_WIDGET_WIDTH +from textual.widget import Widget + + +def test_leaf_width_style_decision_reads_container_once(): + class Counted(Widget): + reads = 0 + + @property + def is_container(self): + self.reads += 1 + return super().is_container + + leaf = Counted() + leaf.reads = 0 + assert NATIVE_WIDGET_WIDTH.styles_sensitive(leaf) + assert leaf.reads == 1