{"record":{"id":"b2b3fdd151394ee0","repo":"a-b-street/abstreet","slug":"to-geom-called-on-a-widget-tree-that-has-something","errorCode":null,"errorMessage":"to_geom called on a widget tree that has something interactive","messagePattern":"to_geom called on a widget tree that has something interactive","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"widgetry/src/widgets/mod.rs","lineNumber":774,"sourceCode":"        }\n    }\n\n    fn consume_geometry(mut self, batch: &mut GeomBatch) {\n        if let Some(bg) = self.bg_batch.take() {\n            batch.append(bg.translate(self.rect.x1, self.rect.y1));\n        }\n\n        if self.widget.is::<Container>() {\n            // downcast() consumes, so we have to do the is() check first\n            if let Ok(container) = self.widget.downcast::<Container>() {\n                for w in container.members {\n                    w.consume_geometry(batch);\n                }\n            }\n        } else if let Ok(defer) = self.widget.downcast::<DeferDraw>() {\n            batch.append(defer.batch.translate(defer.top_left.x, defer.top_left.y));\n        } else {\n            panic!(\"to_geom called on a widget tree that has something interactive\");\n        }\n    }\n\n    fn find(&self, name: &str) -> Option<&Widget> {\n        if self.id == Some(name.to_string()) {\n            return Some(self);\n        }\n\n        if let Some(container) = self.widget.downcast_ref::<Container>() {\n            for widget in &container.members {\n                if let Some(w) = widget.find(name) {\n                    return Some(w);\n                }\n            }\n        }\n\n        None\n    }","sourceCodeStart":756,"sourceCodeEnd":792,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/widgetry/src/widgets/mod.rs#L756-L792","documentation":"`to_geom`/`consume_geometry` converts a widget tree into a plain GeomBatch for rendering; it only handles non-interactive widgets. Reaching any interactive widget (Button, Slider, etc.) in the tree is unsupported and panics.","triggerScenarios":"Calling `Panel::consume_geometry`/to_geom on a tree containing interactive widgets such as Buttons, Sliders, or TextBoxes.","commonSituations":"Trying to snapshot or export a Panel that still has controls on it; reusing an interactive Panel's contents as static drawing; forgetting to replace controls with a rendering of them first.","solutions":["Remove/replace interactive widgets before calling to_geom","Only call to_geom on purely decorative trees (Labels, Lines, DeferDraw)","Render interactive widgets normally via draw instead of extracting geometry","Build a separate static tree for the geometry extraction"],"exampleFix":"// before\npanel.consume_geometry(&mut batch); // panel contains buttons\n// after\nlet static_tree = Container::vertical(vec![Widget::draw_batch(label_batch)]);\nstatic_tree.consume_geometry(&mut batch);","handlingStrategy":"validation","validationCode":"// ensure tree is non-interactive before extracting geometry\nassert!(!panel.has_widget(\"save_btn\"), \"remove interactive widgets before to_geom\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Call to_geom only on decorative trees","Build a separate static snapshot tree for exports","Replace controls with their drawn geometry first"],"tags":["rust","widgetry","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"0964f29315820c91b171b585eb51e300164e9197","analyzedAt":"2026-09-13T18:02:03.421Z","contentChangedAt":"2026-09-13T18:02:03.421Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}