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