bauiv1lib.settings.plugins
Plugin Window UI.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Plugin Window UI.""" 4 5from __future__ import annotations 6 7import logging 8from enum import Enum 9from typing import TYPE_CHECKING, assert_never, override 10 11import bauiv1 as bui 12from bauiv1lib import popup 13 14if TYPE_CHECKING: 15 pass 16 17 18class Category(Enum): 19 """Categories we can display.""" 20 21 ALL = 'all' 22 ENABLED = 'enabled' 23 DISABLED = 'disabled' 24 25 @property 26 def resource(self) -> str: 27 """Resource name for us.""" 28 return f'{self.value}Text' 29 30 31class PluginWindow(bui.MainWindow): 32 """Window for configuring plugins.""" 33 34 def __init__( 35 self, 36 transition: str | None = 'in_right', 37 origin_widget: bui.Widget | None = None, 38 ): 39 app = bui.app 40 41 self._category = Category.ALL 42 43 assert bui.app.classic is not None 44 uiscale = bui.app.ui_v1.uiscale 45 self._width = 870.0 if uiscale is bui.UIScale.SMALL else 670.0 46 x_inset = 100 if uiscale is bui.UIScale.SMALL else 0 47 self._height = ( 48 370.0 49 if uiscale is bui.UIScale.SMALL 50 else 450.0 if uiscale is bui.UIScale.MEDIUM else 520.0 51 ) 52 top_extra = 10 if uiscale is bui.UIScale.SMALL else 0 53 super().__init__( 54 root_widget=bui.containerwidget( 55 size=(self._width, self._height + top_extra), 56 toolbar_visibility=( 57 'menu_minimal' 58 if uiscale is bui.UIScale.SMALL 59 else 'menu_full' 60 ), 61 scale=( 62 1.9 63 if uiscale is bui.UIScale.SMALL 64 else 1.4 if uiscale is bui.UIScale.MEDIUM else 1.0 65 ), 66 stack_offset=( 67 (0, -25) if uiscale is bui.UIScale.SMALL else (0, 0) 68 ), 69 ), 70 transition=transition, 71 origin_widget=origin_widget, 72 ) 73 74 self._scroll_width = self._width - (100 + 2 * x_inset) 75 self._scroll_height = self._height - 115.0 76 self._sub_width = self._scroll_width * 0.95 77 self._sub_height = 724.0 78 79 assert app.classic is not None 80 if uiscale is bui.UIScale.SMALL: 81 bui.containerwidget( 82 edit=self._root_widget, on_cancel_call=self.main_window_back 83 ) 84 self._back_button = None 85 else: 86 self._back_button = bui.buttonwidget( 87 parent=self._root_widget, 88 position=(53 + x_inset, self._height - 60), 89 size=(140, 60), 90 scale=0.8, 91 autoselect=True, 92 label=bui.Lstr(resource='backText'), 93 button_type='back', 94 on_activate_call=self.main_window_back, 95 ) 96 bui.containerwidget( 97 edit=self._root_widget, cancel_button=self._back_button 98 ) 99 100 self._title_text = bui.textwidget( 101 parent=self._root_widget, 102 position=(self._width * 0.5, self._height - 41), 103 size=(0, 0), 104 text=bui.Lstr(resource='pluginsText'), 105 color=app.ui_v1.title_color, 106 maxwidth=170, 107 h_align='center', 108 v_align='center', 109 ) 110 111 if self._back_button is not None: 112 bui.buttonwidget( 113 edit=self._back_button, 114 button_type='backSmall', 115 size=(60, 60), 116 label=bui.charstr(bui.SpecialChar.BACK), 117 ) 118 119 settings_button_x = 670 if uiscale is bui.UIScale.SMALL else 570 120 121 self._num_plugins_text = bui.textwidget( 122 parent=self._root_widget, 123 position=(settings_button_x - 130, self._height - 41), 124 size=(0, 0), 125 text='', 126 h_align='center', 127 v_align='center', 128 ) 129 130 self._category_button = bui.buttonwidget( 131 parent=self._root_widget, 132 scale=0.7, 133 position=(settings_button_x - 105, self._height - 60), 134 size=(130, 60), 135 label=bui.Lstr(resource='allText'), 136 autoselect=True, 137 on_activate_call=bui.WeakCall(self._show_category_options), 138 color=(0.55, 0.73, 0.25), 139 iconscale=1.2, 140 ) 141 142 self._settings_button = bui.buttonwidget( 143 parent=self._root_widget, 144 position=(settings_button_x, self._height - 58), 145 size=(40, 40), 146 label='', 147 on_activate_call=self._open_settings, 148 ) 149 150 bui.imagewidget( 151 parent=self._root_widget, 152 position=(settings_button_x + 3, self._height - 57), 153 draw_controller=self._settings_button, 154 size=(35, 35), 155 texture=bui.gettexture('settingsIcon'), 156 ) 157 158 bui.widget( 159 edit=self._settings_button, 160 up_widget=self._settings_button, 161 right_widget=self._settings_button, 162 ) 163 164 self._scrollwidget = bui.scrollwidget( 165 parent=self._root_widget, 166 position=(50 + x_inset, 50), 167 simple_culling_v=20.0, 168 highlight=False, 169 size=(self._scroll_width, self._scroll_height), 170 selection_loops_to_parent=True, 171 claims_left_right=True, 172 ) 173 bui.widget(edit=self._scrollwidget, right_widget=self._scrollwidget) 174 175 self._no_plugins_installed_text = bui.textwidget( 176 parent=self._root_widget, 177 position=(self._width * 0.5, self._height * 0.5), 178 size=(0, 0), 179 text='', 180 color=(0.6, 0.6, 0.6), 181 scale=0.8, 182 h_align='center', 183 v_align='center', 184 ) 185 186 if bui.app.meta.scanresults is None: 187 bui.screenmessage( 188 'Still scanning plugins; please try again.', color=(1, 0, 0) 189 ) 190 bui.getsound('error').play() 191 plugspecs = bui.app.plugins.plugin_specs 192 plugstates: dict[str, dict] = bui.app.config.get('Plugins', {}) 193 assert isinstance(plugstates, dict) 194 195 plug_line_height = 50 196 sub_width = self._scroll_width 197 sub_height = len(plugspecs) * plug_line_height 198 self._subcontainer = bui.containerwidget( 199 parent=self._scrollwidget, 200 size=(sub_width, sub_height), 201 background=False, 202 ) 203 self._show_plugins() 204 bui.containerwidget( 205 edit=self._root_widget, selected_child=self._scrollwidget 206 ) 207 self._restore_state() 208 209 @override 210 def get_main_window_state(self) -> bui.MainWindowState: 211 # Support recreating our window for back/refresh purposes. 212 cls = type(self) 213 return bui.BasicMainWindowState( 214 create_call=lambda transition, origin_widget: cls( 215 transition=transition, origin_widget=origin_widget 216 ) 217 ) 218 219 @override 220 def on_main_window_close(self) -> None: 221 self._save_state() 222 223 def _check_value_changed(self, plug: bui.PluginSpec, value: bool) -> None: 224 bui.screenmessage( 225 bui.Lstr(resource='settingsWindowAdvanced.mustRestartText'), 226 color=(1.0, 0.5, 0.0), 227 ) 228 plugstates: dict[str, dict] = bui.app.config.setdefault('Plugins', {}) 229 assert isinstance(plugstates, dict) 230 plugstate = plugstates.setdefault(plug.class_path, {}) 231 plugstate['enabled'] = value 232 bui.app.config.commit() 233 234 def _open_settings(self) -> None: 235 # pylint: disable=cyclic-import 236 from bauiv1lib.settings.pluginsettings import PluginSettingsWindow 237 238 # no-op if our underlying widget is dead or on its way out. 239 if not self._root_widget or self._root_widget.transitioning_out: 240 return 241 242 self._save_state() 243 bui.containerwidget(edit=self._root_widget, transition='out_left') 244 assert bui.app.classic is not None 245 bui.app.ui_v1.set_main_window( 246 PluginSettingsWindow(transition='in_right'), from_window=self 247 ) 248 249 def _show_category_options(self) -> None: 250 uiscale = bui.app.ui_v1.uiscale 251 252 popup.PopupMenuWindow( 253 position=self._category_button.get_screen_space_center(), 254 scale=( 255 2.3 256 if uiscale is bui.UIScale.SMALL 257 else 1.65 if uiscale is bui.UIScale.MEDIUM else 1.23 258 ), 259 choices=[c.value for c in Category], 260 choices_display=[bui.Lstr(resource=c.resource) for c in Category], 261 current_choice=self._category.value, 262 delegate=self, 263 ) 264 265 def popup_menu_selected_choice( 266 self, popup_window: popup.PopupMenuWindow, choice: str 267 ) -> None: 268 """Called when a choice is selected in the popup.""" 269 del popup_window # unused 270 self._category = Category(choice) 271 self._clear_scroll_widget() 272 self._show_plugins() 273 274 bui.buttonwidget( 275 edit=self._category_button, 276 label=bui.Lstr(resource=self._category.resource), 277 ) 278 279 def popup_menu_closing(self, popup_window: popup.PopupWindow) -> None: 280 """Called when the popup is closing.""" 281 282 def _clear_scroll_widget(self) -> None: 283 existing_widgets = self._subcontainer.get_children() 284 if existing_widgets: 285 for i in existing_widgets: 286 i.delete() 287 288 def _show_plugins(self) -> None: 289 # pylint: disable=too-many-locals 290 # pylint: disable=too-many-branches 291 # pylint: disable=too-many-statements 292 plugspecs = bui.app.plugins.plugin_specs 293 plugstates: dict[str, dict] = bui.app.config.setdefault('Plugins', {}) 294 assert isinstance(plugstates, dict) 295 296 plug_line_height = 50 297 sub_width = self._scroll_width 298 num_enabled = 0 299 num_disabled = 0 300 301 plugspecs_sorted = sorted(plugspecs.items()) 302 303 bui.textwidget( 304 edit=self._no_plugins_installed_text, 305 text='', 306 ) 307 308 for _classpath, plugspec in plugspecs_sorted: 309 # counting number of enabled and disabled plugins 310 # plugstate = plugstates.setdefault(plugspec[0], {}) 311 if plugspec.enabled: 312 num_enabled += 1 313 else: 314 num_disabled += 1 315 316 if self._category is Category.ALL: 317 sub_height = len(plugspecs) * plug_line_height 318 bui.containerwidget( 319 edit=self._subcontainer, size=(self._scroll_width, sub_height) 320 ) 321 elif self._category is Category.ENABLED: 322 sub_height = num_enabled * plug_line_height 323 bui.containerwidget( 324 edit=self._subcontainer, size=(self._scroll_width, sub_height) 325 ) 326 elif self._category is Category.DISABLED: 327 sub_height = num_disabled * plug_line_height 328 bui.containerwidget( 329 edit=self._subcontainer, size=(self._scroll_width, sub_height) 330 ) 331 else: 332 # Make sure we handle all cases. 333 assert_never(self._category) 334 335 num_shown = 0 336 for classpath, plugspec in plugspecs_sorted: 337 plugin = plugspec.plugin 338 enabled = plugspec.enabled 339 340 if self._category is Category.ALL: 341 show = True 342 elif self._category is Category.ENABLED: 343 show = enabled 344 elif self._category is Category.DISABLED: 345 show = not enabled 346 else: 347 assert_never(self._category) 348 349 if not show: 350 continue 351 352 item_y = sub_height - (num_shown + 1) * plug_line_height 353 check = bui.checkboxwidget( 354 parent=self._subcontainer, 355 text=bui.Lstr(value=classpath), 356 autoselect=True, 357 value=enabled, 358 maxwidth=self._scroll_width 359 - ( 360 200 361 if plugin is not None and plugin.has_settings_ui() 362 else 80 363 ), 364 position=(10, item_y), 365 size=(self._scroll_width - 40, 50), 366 on_value_change_call=bui.Call( 367 self._check_value_changed, plugspec 368 ), 369 textcolor=( 370 (0.8, 0.3, 0.3) 371 if (plugspec.attempted_load and plugspec.plugin is None) 372 else ( 373 (0.6, 0.6, 0.6) 374 if plugspec.plugin is None 375 else (0, 1, 0) 376 ) 377 ), 378 ) 379 # noinspection PyUnresolvedReferences 380 if plugin is not None and plugin.has_settings_ui(): 381 button = bui.buttonwidget( 382 parent=self._subcontainer, 383 label=bui.Lstr(resource='mainMenu.settingsText'), 384 autoselect=True, 385 size=(100, 40), 386 position=(sub_width - 130, item_y + 6), 387 ) 388 # noinspection PyUnresolvedReferences 389 bui.buttonwidget( 390 edit=button, 391 on_activate_call=bui.Call(plugin.show_settings_ui, button), 392 ) 393 else: 394 button = None 395 396 # Allow getting back to back button. 397 if num_shown == 0: 398 bui.widget( 399 edit=check, 400 up_widget=self._back_button, 401 left_widget=self._back_button, 402 right_widget=( 403 self._settings_button if button is None else button 404 ), 405 ) 406 if button is not None: 407 bui.widget(edit=button, up_widget=self._back_button) 408 409 # Make sure we scroll all the way to the end when using 410 # keyboard/button nav. 411 bui.widget(edit=check, show_buffer_top=40, show_buffer_bottom=40) 412 num_shown += 1 413 414 bui.textwidget( 415 edit=self._num_plugins_text, 416 text=str(num_shown), 417 ) 418 419 if num_shown == 0: 420 bui.textwidget( 421 edit=self._no_plugins_installed_text, 422 text=bui.Lstr(resource='noPluginsInstalledText'), 423 ) 424 425 def _save_state(self) -> None: 426 try: 427 sel = self._root_widget.get_selected_child() 428 if sel == self._category_button: 429 sel_name = 'Category' 430 elif sel == self._settings_button: 431 sel_name = 'Settings' 432 elif sel == self._back_button: 433 sel_name = 'Back' 434 elif sel == self._scrollwidget: 435 sel_name = 'Scroll' 436 else: 437 raise ValueError(f'unrecognized selection \'{sel}\'') 438 assert bui.app.classic is not None 439 bui.app.ui_v1.window_states[type(self)] = sel_name 440 except Exception: 441 logging.exception('Error saving state for %s.', self) 442 443 def _restore_state(self) -> None: 444 try: 445 assert bui.app.classic is not None 446 sel_name = bui.app.ui_v1.window_states.get(type(self)) 447 sel: bui.Widget | None 448 if sel_name == 'Category': 449 sel = self._category_button 450 elif sel_name == 'Settings': 451 sel = self._settings_button 452 elif sel_name == 'Back': 453 sel = self._back_button 454 else: 455 sel = self._scrollwidget 456 if sel: 457 bui.containerwidget(edit=self._root_widget, selected_child=sel) 458 except Exception: 459 logging.exception('Error restoring state for %s.', self)
class
Category(enum.Enum):
19class Category(Enum): 20 """Categories we can display.""" 21 22 ALL = 'all' 23 ENABLED = 'enabled' 24 DISABLED = 'disabled' 25 26 @property 27 def resource(self) -> str: 28 """Resource name for us.""" 29 return f'{self.value}Text'
Categories we can display.
ALL =
<Category.ALL: 'all'>
ENABLED =
<Category.ENABLED: 'enabled'>
DISABLED =
<Category.DISABLED: 'disabled'>
resource: str
26 @property 27 def resource(self) -> str: 28 """Resource name for us.""" 29 return f'{self.value}Text'
Resource name for us.
Inherited Members
- enum.Enum
- name
- value
class
PluginWindow(bauiv1._uitypes.MainWindow):
32class PluginWindow(bui.MainWindow): 33 """Window for configuring plugins.""" 34 35 def __init__( 36 self, 37 transition: str | None = 'in_right', 38 origin_widget: bui.Widget | None = None, 39 ): 40 app = bui.app 41 42 self._category = Category.ALL 43 44 assert bui.app.classic is not None 45 uiscale = bui.app.ui_v1.uiscale 46 self._width = 870.0 if uiscale is bui.UIScale.SMALL else 670.0 47 x_inset = 100 if uiscale is bui.UIScale.SMALL else 0 48 self._height = ( 49 370.0 50 if uiscale is bui.UIScale.SMALL 51 else 450.0 if uiscale is bui.UIScale.MEDIUM else 520.0 52 ) 53 top_extra = 10 if uiscale is bui.UIScale.SMALL else 0 54 super().__init__( 55 root_widget=bui.containerwidget( 56 size=(self._width, self._height + top_extra), 57 toolbar_visibility=( 58 'menu_minimal' 59 if uiscale is bui.UIScale.SMALL 60 else 'menu_full' 61 ), 62 scale=( 63 1.9 64 if uiscale is bui.UIScale.SMALL 65 else 1.4 if uiscale is bui.UIScale.MEDIUM else 1.0 66 ), 67 stack_offset=( 68 (0, -25) if uiscale is bui.UIScale.SMALL else (0, 0) 69 ), 70 ), 71 transition=transition, 72 origin_widget=origin_widget, 73 ) 74 75 self._scroll_width = self._width - (100 + 2 * x_inset) 76 self._scroll_height = self._height - 115.0 77 self._sub_width = self._scroll_width * 0.95 78 self._sub_height = 724.0 79 80 assert app.classic is not None 81 if uiscale is bui.UIScale.SMALL: 82 bui.containerwidget( 83 edit=self._root_widget, on_cancel_call=self.main_window_back 84 ) 85 self._back_button = None 86 else: 87 self._back_button = bui.buttonwidget( 88 parent=self._root_widget, 89 position=(53 + x_inset, self._height - 60), 90 size=(140, 60), 91 scale=0.8, 92 autoselect=True, 93 label=bui.Lstr(resource='backText'), 94 button_type='back', 95 on_activate_call=self.main_window_back, 96 ) 97 bui.containerwidget( 98 edit=self._root_widget, cancel_button=self._back_button 99 ) 100 101 self._title_text = bui.textwidget( 102 parent=self._root_widget, 103 position=(self._width * 0.5, self._height - 41), 104 size=(0, 0), 105 text=bui.Lstr(resource='pluginsText'), 106 color=app.ui_v1.title_color, 107 maxwidth=170, 108 h_align='center', 109 v_align='center', 110 ) 111 112 if self._back_button is not None: 113 bui.buttonwidget( 114 edit=self._back_button, 115 button_type='backSmall', 116 size=(60, 60), 117 label=bui.charstr(bui.SpecialChar.BACK), 118 ) 119 120 settings_button_x = 670 if uiscale is bui.UIScale.SMALL else 570 121 122 self._num_plugins_text = bui.textwidget( 123 parent=self._root_widget, 124 position=(settings_button_x - 130, self._height - 41), 125 size=(0, 0), 126 text='', 127 h_align='center', 128 v_align='center', 129 ) 130 131 self._category_button = bui.buttonwidget( 132 parent=self._root_widget, 133 scale=0.7, 134 position=(settings_button_x - 105, self._height - 60), 135 size=(130, 60), 136 label=bui.Lstr(resource='allText'), 137 autoselect=True, 138 on_activate_call=bui.WeakCall(self._show_category_options), 139 color=(0.55, 0.73, 0.25), 140 iconscale=1.2, 141 ) 142 143 self._settings_button = bui.buttonwidget( 144 parent=self._root_widget, 145 position=(settings_button_x, self._height - 58), 146 size=(40, 40), 147 label='', 148 on_activate_call=self._open_settings, 149 ) 150 151 bui.imagewidget( 152 parent=self._root_widget, 153 position=(settings_button_x + 3, self._height - 57), 154 draw_controller=self._settings_button, 155 size=(35, 35), 156 texture=bui.gettexture('settingsIcon'), 157 ) 158 159 bui.widget( 160 edit=self._settings_button, 161 up_widget=self._settings_button, 162 right_widget=self._settings_button, 163 ) 164 165 self._scrollwidget = bui.scrollwidget( 166 parent=self._root_widget, 167 position=(50 + x_inset, 50), 168 simple_culling_v=20.0, 169 highlight=False, 170 size=(self._scroll_width, self._scroll_height), 171 selection_loops_to_parent=True, 172 claims_left_right=True, 173 ) 174 bui.widget(edit=self._scrollwidget, right_widget=self._scrollwidget) 175 176 self._no_plugins_installed_text = bui.textwidget( 177 parent=self._root_widget, 178 position=(self._width * 0.5, self._height * 0.5), 179 size=(0, 0), 180 text='', 181 color=(0.6, 0.6, 0.6), 182 scale=0.8, 183 h_align='center', 184 v_align='center', 185 ) 186 187 if bui.app.meta.scanresults is None: 188 bui.screenmessage( 189 'Still scanning plugins; please try again.', color=(1, 0, 0) 190 ) 191 bui.getsound('error').play() 192 plugspecs = bui.app.plugins.plugin_specs 193 plugstates: dict[str, dict] = bui.app.config.get('Plugins', {}) 194 assert isinstance(plugstates, dict) 195 196 plug_line_height = 50 197 sub_width = self._scroll_width 198 sub_height = len(plugspecs) * plug_line_height 199 self._subcontainer = bui.containerwidget( 200 parent=self._scrollwidget, 201 size=(sub_width, sub_height), 202 background=False, 203 ) 204 self._show_plugins() 205 bui.containerwidget( 206 edit=self._root_widget, selected_child=self._scrollwidget 207 ) 208 self._restore_state() 209 210 @override 211 def get_main_window_state(self) -> bui.MainWindowState: 212 # Support recreating our window for back/refresh purposes. 213 cls = type(self) 214 return bui.BasicMainWindowState( 215 create_call=lambda transition, origin_widget: cls( 216 transition=transition, origin_widget=origin_widget 217 ) 218 ) 219 220 @override 221 def on_main_window_close(self) -> None: 222 self._save_state() 223 224 def _check_value_changed(self, plug: bui.PluginSpec, value: bool) -> None: 225 bui.screenmessage( 226 bui.Lstr(resource='settingsWindowAdvanced.mustRestartText'), 227 color=(1.0, 0.5, 0.0), 228 ) 229 plugstates: dict[str, dict] = bui.app.config.setdefault('Plugins', {}) 230 assert isinstance(plugstates, dict) 231 plugstate = plugstates.setdefault(plug.class_path, {}) 232 plugstate['enabled'] = value 233 bui.app.config.commit() 234 235 def _open_settings(self) -> None: 236 # pylint: disable=cyclic-import 237 from bauiv1lib.settings.pluginsettings import PluginSettingsWindow 238 239 # no-op if our underlying widget is dead or on its way out. 240 if not self._root_widget or self._root_widget.transitioning_out: 241 return 242 243 self._save_state() 244 bui.containerwidget(edit=self._root_widget, transition='out_left') 245 assert bui.app.classic is not None 246 bui.app.ui_v1.set_main_window( 247 PluginSettingsWindow(transition='in_right'), from_window=self 248 ) 249 250 def _show_category_options(self) -> None: 251 uiscale = bui.app.ui_v1.uiscale 252 253 popup.PopupMenuWindow( 254 position=self._category_button.get_screen_space_center(), 255 scale=( 256 2.3 257 if uiscale is bui.UIScale.SMALL 258 else 1.65 if uiscale is bui.UIScale.MEDIUM else 1.23 259 ), 260 choices=[c.value for c in Category], 261 choices_display=[bui.Lstr(resource=c.resource) for c in Category], 262 current_choice=self._category.value, 263 delegate=self, 264 ) 265 266 def popup_menu_selected_choice( 267 self, popup_window: popup.PopupMenuWindow, choice: str 268 ) -> None: 269 """Called when a choice is selected in the popup.""" 270 del popup_window # unused 271 self._category = Category(choice) 272 self._clear_scroll_widget() 273 self._show_plugins() 274 275 bui.buttonwidget( 276 edit=self._category_button, 277 label=bui.Lstr(resource=self._category.resource), 278 ) 279 280 def popup_menu_closing(self, popup_window: popup.PopupWindow) -> None: 281 """Called when the popup is closing.""" 282 283 def _clear_scroll_widget(self) -> None: 284 existing_widgets = self._subcontainer.get_children() 285 if existing_widgets: 286 for i in existing_widgets: 287 i.delete() 288 289 def _show_plugins(self) -> None: 290 # pylint: disable=too-many-locals 291 # pylint: disable=too-many-branches 292 # pylint: disable=too-many-statements 293 plugspecs = bui.app.plugins.plugin_specs 294 plugstates: dict[str, dict] = bui.app.config.setdefault('Plugins', {}) 295 assert isinstance(plugstates, dict) 296 297 plug_line_height = 50 298 sub_width = self._scroll_width 299 num_enabled = 0 300 num_disabled = 0 301 302 plugspecs_sorted = sorted(plugspecs.items()) 303 304 bui.textwidget( 305 edit=self._no_plugins_installed_text, 306 text='', 307 ) 308 309 for _classpath, plugspec in plugspecs_sorted: 310 # counting number of enabled and disabled plugins 311 # plugstate = plugstates.setdefault(plugspec[0], {}) 312 if plugspec.enabled: 313 num_enabled += 1 314 else: 315 num_disabled += 1 316 317 if self._category is Category.ALL: 318 sub_height = len(plugspecs) * plug_line_height 319 bui.containerwidget( 320 edit=self._subcontainer, size=(self._scroll_width, sub_height) 321 ) 322 elif self._category is Category.ENABLED: 323 sub_height = num_enabled * plug_line_height 324 bui.containerwidget( 325 edit=self._subcontainer, size=(self._scroll_width, sub_height) 326 ) 327 elif self._category is Category.DISABLED: 328 sub_height = num_disabled * plug_line_height 329 bui.containerwidget( 330 edit=self._subcontainer, size=(self._scroll_width, sub_height) 331 ) 332 else: 333 # Make sure we handle all cases. 334 assert_never(self._category) 335 336 num_shown = 0 337 for classpath, plugspec in plugspecs_sorted: 338 plugin = plugspec.plugin 339 enabled = plugspec.enabled 340 341 if self._category is Category.ALL: 342 show = True 343 elif self._category is Category.ENABLED: 344 show = enabled 345 elif self._category is Category.DISABLED: 346 show = not enabled 347 else: 348 assert_never(self._category) 349 350 if not show: 351 continue 352 353 item_y = sub_height - (num_shown + 1) * plug_line_height 354 check = bui.checkboxwidget( 355 parent=self._subcontainer, 356 text=bui.Lstr(value=classpath), 357 autoselect=True, 358 value=enabled, 359 maxwidth=self._scroll_width 360 - ( 361 200 362 if plugin is not None and plugin.has_settings_ui() 363 else 80 364 ), 365 position=(10, item_y), 366 size=(self._scroll_width - 40, 50), 367 on_value_change_call=bui.Call( 368 self._check_value_changed, plugspec 369 ), 370 textcolor=( 371 (0.8, 0.3, 0.3) 372 if (plugspec.attempted_load and plugspec.plugin is None) 373 else ( 374 (0.6, 0.6, 0.6) 375 if plugspec.plugin is None 376 else (0, 1, 0) 377 ) 378 ), 379 ) 380 # noinspection PyUnresolvedReferences 381 if plugin is not None and plugin.has_settings_ui(): 382 button = bui.buttonwidget( 383 parent=self._subcontainer, 384 label=bui.Lstr(resource='mainMenu.settingsText'), 385 autoselect=True, 386 size=(100, 40), 387 position=(sub_width - 130, item_y + 6), 388 ) 389 # noinspection PyUnresolvedReferences 390 bui.buttonwidget( 391 edit=button, 392 on_activate_call=bui.Call(plugin.show_settings_ui, button), 393 ) 394 else: 395 button = None 396 397 # Allow getting back to back button. 398 if num_shown == 0: 399 bui.widget( 400 edit=check, 401 up_widget=self._back_button, 402 left_widget=self._back_button, 403 right_widget=( 404 self._settings_button if button is None else button 405 ), 406 ) 407 if button is not None: 408 bui.widget(edit=button, up_widget=self._back_button) 409 410 # Make sure we scroll all the way to the end when using 411 # keyboard/button nav. 412 bui.widget(edit=check, show_buffer_top=40, show_buffer_bottom=40) 413 num_shown += 1 414 415 bui.textwidget( 416 edit=self._num_plugins_text, 417 text=str(num_shown), 418 ) 419 420 if num_shown == 0: 421 bui.textwidget( 422 edit=self._no_plugins_installed_text, 423 text=bui.Lstr(resource='noPluginsInstalledText'), 424 ) 425 426 def _save_state(self) -> None: 427 try: 428 sel = self._root_widget.get_selected_child() 429 if sel == self._category_button: 430 sel_name = 'Category' 431 elif sel == self._settings_button: 432 sel_name = 'Settings' 433 elif sel == self._back_button: 434 sel_name = 'Back' 435 elif sel == self._scrollwidget: 436 sel_name = 'Scroll' 437 else: 438 raise ValueError(f'unrecognized selection \'{sel}\'') 439 assert bui.app.classic is not None 440 bui.app.ui_v1.window_states[type(self)] = sel_name 441 except Exception: 442 logging.exception('Error saving state for %s.', self) 443 444 def _restore_state(self) -> None: 445 try: 446 assert bui.app.classic is not None 447 sel_name = bui.app.ui_v1.window_states.get(type(self)) 448 sel: bui.Widget | None 449 if sel_name == 'Category': 450 sel = self._category_button 451 elif sel_name == 'Settings': 452 sel = self._settings_button 453 elif sel_name == 'Back': 454 sel = self._back_button 455 else: 456 sel = self._scrollwidget 457 if sel: 458 bui.containerwidget(edit=self._root_widget, selected_child=sel) 459 except Exception: 460 logging.exception('Error restoring state for %s.', self)
Window for configuring plugins.
PluginWindow( transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None)
35 def __init__( 36 self, 37 transition: str | None = 'in_right', 38 origin_widget: bui.Widget | None = None, 39 ): 40 app = bui.app 41 42 self._category = Category.ALL 43 44 assert bui.app.classic is not None 45 uiscale = bui.app.ui_v1.uiscale 46 self._width = 870.0 if uiscale is bui.UIScale.SMALL else 670.0 47 x_inset = 100 if uiscale is bui.UIScale.SMALL else 0 48 self._height = ( 49 370.0 50 if uiscale is bui.UIScale.SMALL 51 else 450.0 if uiscale is bui.UIScale.MEDIUM else 520.0 52 ) 53 top_extra = 10 if uiscale is bui.UIScale.SMALL else 0 54 super().__init__( 55 root_widget=bui.containerwidget( 56 size=(self._width, self._height + top_extra), 57 toolbar_visibility=( 58 'menu_minimal' 59 if uiscale is bui.UIScale.SMALL 60 else 'menu_full' 61 ), 62 scale=( 63 1.9 64 if uiscale is bui.UIScale.SMALL 65 else 1.4 if uiscale is bui.UIScale.MEDIUM else 1.0 66 ), 67 stack_offset=( 68 (0, -25) if uiscale is bui.UIScale.SMALL else (0, 0) 69 ), 70 ), 71 transition=transition, 72 origin_widget=origin_widget, 73 ) 74 75 self._scroll_width = self._width - (100 + 2 * x_inset) 76 self._scroll_height = self._height - 115.0 77 self._sub_width = self._scroll_width * 0.95 78 self._sub_height = 724.0 79 80 assert app.classic is not None 81 if uiscale is bui.UIScale.SMALL: 82 bui.containerwidget( 83 edit=self._root_widget, on_cancel_call=self.main_window_back 84 ) 85 self._back_button = None 86 else: 87 self._back_button = bui.buttonwidget( 88 parent=self._root_widget, 89 position=(53 + x_inset, self._height - 60), 90 size=(140, 60), 91 scale=0.8, 92 autoselect=True, 93 label=bui.Lstr(resource='backText'), 94 button_type='back', 95 on_activate_call=self.main_window_back, 96 ) 97 bui.containerwidget( 98 edit=self._root_widget, cancel_button=self._back_button 99 ) 100 101 self._title_text = bui.textwidget( 102 parent=self._root_widget, 103 position=(self._width * 0.5, self._height - 41), 104 size=(0, 0), 105 text=bui.Lstr(resource='pluginsText'), 106 color=app.ui_v1.title_color, 107 maxwidth=170, 108 h_align='center', 109 v_align='center', 110 ) 111 112 if self._back_button is not None: 113 bui.buttonwidget( 114 edit=self._back_button, 115 button_type='backSmall', 116 size=(60, 60), 117 label=bui.charstr(bui.SpecialChar.BACK), 118 ) 119 120 settings_button_x = 670 if uiscale is bui.UIScale.SMALL else 570 121 122 self._num_plugins_text = bui.textwidget( 123 parent=self._root_widget, 124 position=(settings_button_x - 130, self._height - 41), 125 size=(0, 0), 126 text='', 127 h_align='center', 128 v_align='center', 129 ) 130 131 self._category_button = bui.buttonwidget( 132 parent=self._root_widget, 133 scale=0.7, 134 position=(settings_button_x - 105, self._height - 60), 135 size=(130, 60), 136 label=bui.Lstr(resource='allText'), 137 autoselect=True, 138 on_activate_call=bui.WeakCall(self._show_category_options), 139 color=(0.55, 0.73, 0.25), 140 iconscale=1.2, 141 ) 142 143 self._settings_button = bui.buttonwidget( 144 parent=self._root_widget, 145 position=(settings_button_x, self._height - 58), 146 size=(40, 40), 147 label='', 148 on_activate_call=self._open_settings, 149 ) 150 151 bui.imagewidget( 152 parent=self._root_widget, 153 position=(settings_button_x + 3, self._height - 57), 154 draw_controller=self._settings_button, 155 size=(35, 35), 156 texture=bui.gettexture('settingsIcon'), 157 ) 158 159 bui.widget( 160 edit=self._settings_button, 161 up_widget=self._settings_button, 162 right_widget=self._settings_button, 163 ) 164 165 self._scrollwidget = bui.scrollwidget( 166 parent=self._root_widget, 167 position=(50 + x_inset, 50), 168 simple_culling_v=20.0, 169 highlight=False, 170 size=(self._scroll_width, self._scroll_height), 171 selection_loops_to_parent=True, 172 claims_left_right=True, 173 ) 174 bui.widget(edit=self._scrollwidget, right_widget=self._scrollwidget) 175 176 self._no_plugins_installed_text = bui.textwidget( 177 parent=self._root_widget, 178 position=(self._width * 0.5, self._height * 0.5), 179 size=(0, 0), 180 text='', 181 color=(0.6, 0.6, 0.6), 182 scale=0.8, 183 h_align='center', 184 v_align='center', 185 ) 186 187 if bui.app.meta.scanresults is None: 188 bui.screenmessage( 189 'Still scanning plugins; please try again.', color=(1, 0, 0) 190 ) 191 bui.getsound('error').play() 192 plugspecs = bui.app.plugins.plugin_specs 193 plugstates: dict[str, dict] = bui.app.config.get('Plugins', {}) 194 assert isinstance(plugstates, dict) 195 196 plug_line_height = 50 197 sub_width = self._scroll_width 198 sub_height = len(plugspecs) * plug_line_height 199 self._subcontainer = bui.containerwidget( 200 parent=self._scrollwidget, 201 size=(sub_width, sub_height), 202 background=False, 203 ) 204 self._show_plugins() 205 bui.containerwidget( 206 edit=self._root_widget, selected_child=self._scrollwidget 207 ) 208 self._restore_state()
Create a MainWindow given a root widget and transition info.
Automatically handles in and out transitions on the provided widget, so there is no need to set transitions when creating it.
210 @override 211 def get_main_window_state(self) -> bui.MainWindowState: 212 # Support recreating our window for back/refresh purposes. 213 cls = type(self) 214 return bui.BasicMainWindowState( 215 create_call=lambda transition, origin_widget: cls( 216 transition=transition, origin_widget=origin_widget 217 ) 218 )
Return a WindowState to recreate this window, if supported.
@override
def
on_main_window_close(self) -> None:
Called before transitioning out a main window.
A good opportunity to save window state/etc.
Inherited Members
- bauiv1._uitypes.MainWindow
- main_window_back_state
- main_window_close
- can_change_main_window
- main_window_back
- main_window_replace
- bauiv1._uitypes.Window
- get_root_widget