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 # show = False 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 - 200, 354 position=(10, item_y), 355 size=(self._scroll_width - 40, 50), 356 on_value_change_call=bui.Call( 357 self._check_value_changed, plugspec 358 ), 359 textcolor=( 360 (0.8, 0.3, 0.3) 361 if (plugspec.attempted_load and plugspec.plugin is None) 362 else ( 363 (0.6, 0.6, 0.6) 364 if plugspec.plugin is None 365 else (0, 1, 0) 366 ) 367 ), 368 ) 369 # noinspection PyUnresolvedReferences 370 if plugin is not None and plugin.has_settings_ui(): 371 button = bui.buttonwidget( 372 parent=self._subcontainer, 373 label=bui.Lstr(resource='mainMenu.settingsText'), 374 autoselect=True, 375 size=(100, 40), 376 position=(sub_width - 130, item_y + 6), 377 ) 378 # noinspection PyUnresolvedReferences 379 bui.buttonwidget( 380 edit=button, 381 on_activate_call=bui.Call(plugin.show_settings_ui, button), 382 ) 383 else: 384 button = None 385 386 # Allow getting back to back button. 387 if num_shown == 0: 388 bui.widget( 389 edit=check, 390 up_widget=self._back_button, 391 left_widget=self._back_button, 392 right_widget=self._settings_button, 393 ) 394 if button is not None: 395 bui.widget(edit=button, up_widget=self._back_button) 396 397 # Make sure we scroll all the way to the end when using 398 # keyboard/button nav. 399 bui.widget(edit=check, show_buffer_top=40, show_buffer_bottom=40) 400 num_shown += 1 401 402 bui.textwidget( 403 edit=self._num_plugins_text, 404 text=str(num_shown), 405 ) 406 407 if num_shown == 0: 408 bui.textwidget( 409 edit=self._no_plugins_installed_text, 410 text=bui.Lstr(resource='noPluginsInstalledText'), 411 ) 412 413 def _save_state(self) -> None: 414 try: 415 sel = self._root_widget.get_selected_child() 416 if sel == self._category_button: 417 sel_name = 'Category' 418 elif sel == self._settings_button: 419 sel_name = 'Settings' 420 elif sel == self._back_button: 421 sel_name = 'Back' 422 elif sel == self._scrollwidget: 423 sel_name = 'Scroll' 424 else: 425 raise ValueError(f'unrecognized selection \'{sel}\'') 426 assert bui.app.classic is not None 427 bui.app.ui_v1.window_states[type(self)] = sel_name 428 except Exception: 429 logging.exception('Error saving state for %s.', self) 430 431 def _restore_state(self) -> None: 432 try: 433 assert bui.app.classic is not None 434 sel_name = bui.app.ui_v1.window_states.get(type(self)) 435 sel: bui.Widget | None 436 if sel_name == 'Category': 437 sel = self._category_button 438 elif sel_name == 'Settings': 439 sel = self._settings_button 440 elif sel_name == 'Back': 441 sel = self._back_button 442 else: 443 sel = self._scrollwidget 444 if sel: 445 bui.containerwidget(edit=self._root_widget, selected_child=sel) 446 except Exception: 447 logging.exception('Error restoring state for %s.', self) 448 449 def _do_back(self) -> None: 450 # pylint: disable=cyclic-import 451 from bauiv1lib.settings.advanced import AdvancedSettingsWindow 452 453 # no-op if our underlying widget is dead or on its way out. 454 if not self._root_widget or self._root_widget.transitioning_out: 455 return 456 457 self._save_state() 458 bui.containerwidget( 459 edit=self._root_widget, transition=self._transition_out 460 ) 461 assert bui.app.classic is not None 462 bui.app.ui_v1.set_main_menu_window( 463 AdvancedSettingsWindow(transition='in_left').get_root_widget(), 464 from_window=self._root_widget, 465 )
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 # show = False 344 345 if not show: 346 continue 347 348 item_y = sub_height - (num_shown + 1) * plug_line_height 349 check = bui.checkboxwidget( 350 parent=self._subcontainer, 351 text=bui.Lstr(value=classpath), 352 autoselect=True, 353 value=enabled, 354 maxwidth=self._scroll_width - 200, 355 position=(10, item_y), 356 size=(self._scroll_width - 40, 50), 357 on_value_change_call=bui.Call( 358 self._check_value_changed, plugspec 359 ), 360 textcolor=( 361 (0.8, 0.3, 0.3) 362 if (plugspec.attempted_load and plugspec.plugin is None) 363 else ( 364 (0.6, 0.6, 0.6) 365 if plugspec.plugin is None 366 else (0, 1, 0) 367 ) 368 ), 369 ) 370 # noinspection PyUnresolvedReferences 371 if plugin is not None and plugin.has_settings_ui(): 372 button = bui.buttonwidget( 373 parent=self._subcontainer, 374 label=bui.Lstr(resource='mainMenu.settingsText'), 375 autoselect=True, 376 size=(100, 40), 377 position=(sub_width - 130, item_y + 6), 378 ) 379 # noinspection PyUnresolvedReferences 380 bui.buttonwidget( 381 edit=button, 382 on_activate_call=bui.Call(plugin.show_settings_ui, button), 383 ) 384 else: 385 button = None 386 387 # Allow getting back to back button. 388 if num_shown == 0: 389 bui.widget( 390 edit=check, 391 up_widget=self._back_button, 392 left_widget=self._back_button, 393 right_widget=self._settings_button, 394 ) 395 if button is not None: 396 bui.widget(edit=button, up_widget=self._back_button) 397 398 # Make sure we scroll all the way to the end when using 399 # keyboard/button nav. 400 bui.widget(edit=check, show_buffer_top=40, show_buffer_bottom=40) 401 num_shown += 1 402 403 bui.textwidget( 404 edit=self._num_plugins_text, 405 text=str(num_shown), 406 ) 407 408 if num_shown == 0: 409 bui.textwidget( 410 edit=self._no_plugins_installed_text, 411 text=bui.Lstr(resource='noPluginsInstalledText'), 412 ) 413 414 def _save_state(self) -> None: 415 try: 416 sel = self._root_widget.get_selected_child() 417 if sel == self._category_button: 418 sel_name = 'Category' 419 elif sel == self._settings_button: 420 sel_name = 'Settings' 421 elif sel == self._back_button: 422 sel_name = 'Back' 423 elif sel == self._scrollwidget: 424 sel_name = 'Scroll' 425 else: 426 raise ValueError(f'unrecognized selection \'{sel}\'') 427 assert bui.app.classic is not None 428 bui.app.ui_v1.window_states[type(self)] = sel_name 429 except Exception: 430 logging.exception('Error saving state for %s.', self) 431 432 def _restore_state(self) -> None: 433 try: 434 assert bui.app.classic is not None 435 sel_name = bui.app.ui_v1.window_states.get(type(self)) 436 sel: bui.Widget | None 437 if sel_name == 'Category': 438 sel = self._category_button 439 elif sel_name == 'Settings': 440 sel = self._settings_button 441 elif sel_name == 'Back': 442 sel = self._back_button 443 else: 444 sel = self._scrollwidget 445 if sel: 446 bui.containerwidget(edit=self._root_widget, selected_child=sel) 447 except Exception: 448 logging.exception('Error restoring state for %s.', self) 449 450 def _do_back(self) -> None: 451 # pylint: disable=cyclic-import 452 from bauiv1lib.settings.advanced import AdvancedSettingsWindow 453 454 # no-op if our underlying widget is dead or on its way out. 455 if not self._root_widget or self._root_widget.transitioning_out: 456 return 457 458 self._save_state() 459 bui.containerwidget( 460 edit=self._root_widget, transition=self._transition_out 461 ) 462 assert bui.app.classic is not None 463 bui.app.ui_v1.set_main_menu_window( 464 AdvancedSettingsWindow(transition='in_left').get_root_widget(), 465 from_window=self._root_widget, 466 )
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