@@ -310,6 +310,10 @@ def init_pyvista_render_objects(self):
310310 )
311311 self .state .vtu_path = fourc_geometry .vtu_file_path
312312
313+ # render window initialization: to be done only once while starting the webviewer, otherwise no proper binding within the current setup!
314+ if "render_window" not in self ._server_vars :
315+ self ._server_vars ["render_window" ] = pv .Plotter ()
316+
313317 if self .state .vtu_path == "" :
314318 self .state .read_in_status = self .state .all_read_in_statuses [
315319 "vtu_conversion_error"
@@ -491,6 +495,7 @@ def init_general_sections_state_and_server_vars(self):
491495
492496 # loop through input file sections
493497 self .state .general_sections = {}
498+ self .state .add_section = ""
494499 self .state .add_key = "" # key for the add property row
495500 self .state .add_value = "" # value for the add property row
496501 for section_name , section_data in self ._server_vars [
@@ -1243,6 +1248,97 @@ def change_export_fourc_yaml_path(self, export_fourc_yaml_path, **kwargs):
12431248 #################################################
12441249 # SELECTION CHANGES #################################
12451250 ################################################
1251+ @controller .set ("click_delete_section_button" )
1252+ def click_delete_section_button (self , ** kwargs ):
1253+ """Deletes the currently selected section; if it was the last
1254+ subsection, delete the main too."""
1255+ if self .state .selected_section_name in self .state .json_schema .get (
1256+ "required" , []
1257+ ):
1258+ return
1259+
1260+ cur_main = self .state .selected_main_section_name
1261+ cur_section = self .state .selected_section_name
1262+ if not cur_main or not cur_section :
1263+ return
1264+
1265+ general_sections = copy .deepcopy (self .state .general_sections ) or {}
1266+ sections_names = copy .deepcopy (self .state .section_names ) or {}
1267+
1268+ # delete the subsection's data
1269+ del general_sections [cur_main ][cur_section ]
1270+ self .state .general_sections = general_sections
1271+
1272+ # rebuild subsections list (new list ref -> reactive)
1273+ subs_before = sections_names [cur_main ]["subsections" ]
1274+ new_subs = [s for s in subs_before if s != cur_section ]
1275+ sections_names [cur_main ] = {** sections_names [cur_main ], "subsections" : new_subs }
1276+
1277+ if cur_section == cur_main :
1278+ # last one -> delete the main group immediately
1279+ sections_names .pop (cur_main , None )
1280+ general_sections .pop (cur_main , None )
1281+ self .state .section_names = sections_names
1282+ self .state .general_sections = general_sections
1283+
1284+ # choose a new valid selection
1285+ new_main = next (iter (sections_names .keys ()), "" )
1286+ self .state .selected_main_section_name = new_main
1287+ self .state .selected_section_name = (
1288+ sections_names [new_main ]["subsections" ][0 ]
1289+ if new_main and sections_names [new_main ]["subsections" ]
1290+ else ""
1291+ )
1292+ return
1293+
1294+ self .state .section_names = sections_names
1295+ self .state .selected_main_section_name = cur_main
1296+ self .state .selected_section_name = new_subs [0 ] if new_subs else ""
1297+
1298+ @change ("add_section" )
1299+ def change_add_section (self , ** kwargs ):
1300+ """Reaction to section selection."""
1301+ add_section = self .state .add_section
1302+ main_section_name = add_section .split ("/" )[0 ] or ""
1303+
1304+ if add_section not in self .state .json_schema .get ("properties" , {}):
1305+ return
1306+
1307+ general_sections = copy .deepcopy (self .state .general_sections ) or {}
1308+ section_names = copy .deepcopy (self .state .section_names ) or {}
1309+
1310+ # Ensure main buckets exist
1311+ if main_section_name not in section_names :
1312+ section_names [main_section_name ] = {
1313+ "subsections" : [main_section_name ],
1314+ "content_mode" : self .state .all_content_modes ["general_section" ],
1315+ }
1316+ if main_section_name not in general_sections :
1317+ general_sections [main_section_name ] = {main_section_name : {}}
1318+
1319+ # Store data under main -> sub
1320+ if add_section not in general_sections [main_section_name ]:
1321+ general_sections [main_section_name ][add_section ] = {}
1322+
1323+ # Replace subsections list with a NEW list object
1324+ subs = section_names [main_section_name ]["subsections" ]
1325+ if add_section not in subs :
1326+ subs = subs + [add_section ] # new list ref
1327+ section_names [main_section_name ] = {
1328+ ** section_names [main_section_name ], # keep content_mode
1329+ "subsections" : subs , # new list ref
1330+ }
1331+
1332+ # Commit (new references -> reactive)
1333+ self .state .general_sections = general_sections
1334+ self .state .section_names = section_names
1335+
1336+ # Set a valid selection so VSelect updates
1337+ self .state .selected_main_section_name = main_section_name
1338+ self .state .selected_section_name = add_section
1339+
1340+ self .state .add_section = ""
1341+
12461342 @change ("selected_main_section_name" )
12471343 def change_selected_main_section_name (self , selected_main_section_name , ** kwargs ):
12481344 """Reaction to change of state.selected_main_section_name."""
0 commit comments