studioDesignerPage.py (24673B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\cosmetics\ship\pages\studio\studioDesignerPage.py 2 import eveicon 3 from carbonui import Align, uiconst, AxisAlignment, Density, ButtonVariant, PickState 4 from carbonui.button.const import HEIGHT_EXPANDED 5 from carbonui.button.group import ButtonGroup, ButtonSizeMode, OverflowAlign 6 from carbonui.control.button import Button 7 from carbonui.primitives.container import Container 8 from carbonui.primitives.containerAutoSize import ContainerAutoSize 9 from carbonui.uianimations import animations 10 from cosmetics.client.shipSkinDesignSvc import get_ship_skin_design_svc 11 from cosmetics.client.shipSkinSequencingSvc import get_ship_skin_sequencing_svc 12 from cosmetics.client.ships.qa.settings import should_show_popup_if_skin_name_missing 13 from cosmetics.client.ships.skins.live_data import current_skin_design, current_skin_design_signals 14 from cosmetics.common.ships.skins.static_data.slot import SlotsDataLoader 15 from cosmetics.common.ships.skins.static_data.slot_configuration import SlotConfigurationsDataLoader, is_skinnable_ship 16 from cosmetics.common.ships.skins.static_data.slot_name import SlotID, PATTERN_RELATED_SLOT_IDS 17 from eve.client.script.ui.cosmetics.ship.const import ANIM_DURATION_LONG 18 from eve.client.script.ui.cosmetics.ship.controls.characterInfo import DesignOwnerCharacterInfo 19 from eve.client.script.ui.cosmetics.ship.controls.costInfo import StudioCostInfo 20 from eve.client.script.ui.cosmetics.ship.controls.sequencingTime import SequencingTime 21 from eve.client.script.ui.cosmetics.ship.pages.studio import studioSignals 22 from eve.client.script.ui.cosmetics.ship.pages.studio.componentPanel import ComponentPanel 23 from eve.client.script.ui.cosmetics.ship.pages.studio.cosmeticSlot import CosmeticSlot 24 from eve.client.script.ui.cosmetics.ship.pages.studio.gizmoControls import GizmoControls 25 from eve.client.script.ui.cosmetics.ship.pages.studio.hullSelectionPanel import HullSelectionPanel 26 from eve.client.script.ui.cosmetics.ship.pages.studio.nebulaSelector import NebulaSelector 27 from eve.client.script.ui.cosmetics.ship.pages.studio.nebulaBackground import SkinrBackgroundContainer 28 from eve.client.script.ui.cosmetics.ship.pages.studio.patternSlots import PatternSlots 29 from eve.client.script.ui.cosmetics.ship.pages.studio.radiallyAlignedMixin import RadiallyAlignedMixin 30 from eve.client.script.ui.cosmetics.ship.pages.studio.randomizeControls import RandomizeControls 31 from eve.client.script.ui.cosmetics.ship.pages.studio.saveSkinButton import SaveSkinButton, SaveAsSkinButton 32 from eve.client.script.ui.cosmetics.ship.pages.studio.skinDesignDragData import SkinDesignDragData 33 from eve.client.script.ui.cosmetics.ship.pages.studio.skinNameDialogue import SkinNameDialogue 34 from eve.client.script.ui.cosmetics.ship.pages.studio.studioUtil import get_circular_layout_radius 35 from eve.client.script.ui.shared.cloneGrade import ORIGIN_SHIP_SKINR 36 from eve.client.script.ui.shared.cloneGrade.omegaTooltipPanelCell import OmegaTooltipPanelCell 37 from characterskills.client import skill_signals 38 from itertoolsext.Enum import Enum 39 from localization import GetByLabel 40 from signals import Signal 41 PANEL_PAD_TOP = 160 42 PANEL_PAD_BOTTOM = 160 43 44 class StudioDesignerPage(Container): 45 SLOT_ANGLE_GAP = 14 46 SLOT_WINDOW_GAP = 40 47 SLOT_ANGLES = {SlotID.PRIMARY_NANOCOATING: 180 + SLOT_ANGLE_GAP * 1.5, 48 SlotID.SECONDARY_NANOCOATING: 180 + SLOT_ANGLE_GAP * 0.5, 49 SlotID.TERTIARY_NANOCOATING: 180 - SLOT_ANGLE_GAP * 0.5, 50 SlotID.TECH_AREA: 180 - SLOT_ANGLE_GAP * 1.5} 51 52 def __init__(self, *args, **kwargs): 53 super(StudioDesignerPage, self).__init__(*args, **kwargs) 54 self.slots_by_id = {} 55 self.panels_by_id = {} 56 self.hull_selection_panel = None 57 self.nebula_selection_panel = None 58 self.bg_fade_container = None 59 self.is_loaded = False 60 self.minimum_element_width = 246 61 self.circular_layout_pos_offset = 72 62 self.on_create_btn = Signal('on_create_btn') 63 64 def LoadPanel(self, animate = True): 65 if not self.is_loaded: 66 self.is_loaded = True 67 self.construct_layout() 68 self.connect_signals() 69 self.populate_from_design() 70 self.create_button.update() 71 self.state = uiconst.UI_PICKCHILDREN 72 animations.FadeTo(self, 0.0, 1.0, duration=0.6, timeOffset=ANIM_DURATION_LONG) 73 self.deselect_all_cosmetic_slots() 74 self.check_show_hull_selection_panel() 75 self.update_circular_layout(*self.GetAbsoluteSize()) 76 77 def check_show_hull_selection_panel(self): 78 active_ship_type_id = current_skin_design.get_active_ship_type_id() 79 if not is_skinnable_ship(active_ship_type_id): 80 self.show_hull_selection_panel() 81 else: 82 self.close_hull_selection_panel() 83 84 def UnloadPanel(self, animate = True): 85 self.Disable() 86 animations.FadeTo(self, self.opacity, 0.0, duration=0.2, callback=self.Hide) 87 88 def Close(self): 89 try: 90 self.disconnect_signals() 91 finally: 92 super(StudioDesignerPage, self).Close() 93 94 def connect_signals(self): 95 self.on_size_changed.connect(self.on_size_changed_signal) 96 studioSignals.on_scene_zoom.connect(self.on_scene_zoom) 97 current_skin_design_signals.on_slot_selected.connect(self.on_slot_selected) 98 current_skin_design_signals.on_slot_fitting_changed.connect(self.on_slot_fitting_changed) 99 current_skin_design_signals.on_design_reset.connect(self.on_design_reset) 100 current_skin_design_signals.on_existing_design_loaded.connect(self.on_existing_design_loaded) 101 current_skin_design_signals.on_ship_type_id_changed.connect(self.on_ship_type_id_changed) 102 skill_signals.on_skill_levels_trained.connect(self.on_skill_levels_trained) 103 104 def disconnect_signals(self): 105 self.on_size_changed.disconnect(self.on_size_changed_signal) 106 studioSignals.on_scene_zoom.disconnect(self.on_scene_zoom) 107 current_skin_design_signals.on_slot_selected.disconnect(self.on_slot_selected) 108 current_skin_design_signals.on_slot_fitting_changed.disconnect(self.on_slot_fitting_changed) 109 current_skin_design_signals.on_design_reset.disconnect(self.on_design_reset) 110 current_skin_design_signals.on_existing_design_loaded.disconnect(self.on_existing_design_loaded) 111 current_skin_design_signals.on_ship_type_id_changed.disconnect(self.on_ship_type_id_changed) 112 skill_signals.on_skill_levels_trained.disconnect(self.on_skill_levels_trained) 113 114 def on_ship_type_id_changed(self, type_id): 115 self.create_button.update() 116 self.update_slots() 117 118 def on_slot_fitting_changed(self, slot_id, component_instance): 119 self.create_button.update() 120 self.hide_all_slot_pulse_highlights() 121 if slot_id == SlotID.PATTERN and component_instance is None: 122 if current_skin_design.get_selected_slot_id() in (SlotID.PATTERN_MATERIAL, SlotID.SECONDARY_PATTERN, SlotID.SECONDARY_PATTERN_MATERIAL): 123 current_skin_design.set_selected_slot_id(None) 124 125 def hide_all_slot_pulse_highlights(self): 126 for slot_id in self.slots_by_id.keys(): 127 slot = self.slots_by_id[slot_id] 128 slot.hide_pulse_highlight() 129 130 def on_skill_levels_trained(self, levels_trained): 131 self.sequencing_time.update() 132 133 def construct_layout(self): 134 self.construct_base_containers() 135 self.construct_gizmo_controls() 136 self.construct_bottom_left_buttons() 137 self.construct_bottom_right_buttons() 138 self.reconstruct_slots_and_panels() 139 self.construct_randomize_controls() 140 self.construct_undo_buttons() 141 self.construct_pattern_slots() 142 self.construct_creator_character_info() 143 self.construct_background_ui() 144 145 def construct_undo_buttons(self): 146 self.undo_btns = UndoButtons(parent=self.center_right_cont) 147 148 def construct_randomize_controls(self): 149 self.randomize_controls = RandomizeControls(name='randomize_controls', parent=self.center_left_cont) 150 self.randomize_controls.angle_deg = 220 151 152 def construct_background_ui(self): 153 self.nebula_selection_panel = NebulaSelector(name='nebula_selector', parent=self, align=Align.TORIGHT_NOPUSH, padding=(0, 154 PANEL_PAD_TOP, 155 32, 156 PANEL_PAD_BOTTOM), clipChildren=True) 157 self.bg_fade_container = SkinrBackgroundContainer(name='nebula_fade_container', parent=self, align=Align.TOALL, pickState=uiconst.TR2_SPS_OFF) 158 159 def construct_bottom_left_buttons(self): 160 button_group = ButtonGroup(parent=self.button_overlay_cont, align=Align.TOLEFT, button_alignment=AxisAlignment.START, density=Density.EXPANDED, button_size_mode=ButtonSizeMode.DYNAMIC, ignore_overflow=True) 161 self.hull_selection_button = Button(parent=self, align=Align.CENTER, label=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/HullSelection'), func=self.on_hull_selection_button, texturePath=eveicon.spaceship_command) 162 button_group.add_button(self.hull_selection_button) 163 self.save_skin_button = SaveSkinButton(label=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/Save')) 164 button_group.add_button(self.save_skin_button) 165 self.save_as_skin_button = SaveAsSkinButton(label=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/SaveAs')) 166 button_group.add_button(self.save_as_skin_button) 167 168 def construct_creator_character_info(self): 169 self.made_by_character_info = DesignOwnerCharacterInfo(name='made_by_character_info', parent=self, align=Align.BOTTOMLEFT, left=32, top=116) 170 self._update_creator_character_info() 171 172 def _update_creator_character_info(self): 173 creator_char_id = current_skin_design.get().creator_character_id 174 self.made_by_character_info.character_id = creator_char_id 175 self.made_by_character_info.display = creator_char_id != session.charid 176 177 def get_share_button_drag_data(self, *args): 178 saved_design_id = current_skin_design.get().saved_skin_design_id 179 if saved_design_id: 180 saved_design_data = get_ship_skin_design_svc().get_saved_design(saved_design_id) 181 if saved_design_data: 182 return SkinDesignDragData(design_data=saved_design_data, saved_design_id=saved_design_id) 183 184 def construct_bottom_right_buttons(self): 185 self.bottom_right_container = Container(name='bottom_right_container', parent=self.button_overlay_cont, align=Align.TORIGHT_PROP, width=0.3) 186 button_group = ButtonGroup(parent=self.bottom_right_container, align=Align.BOTTOMRIGHT, button_alignment=AxisAlignment.END, button_size_mode=ButtonSizeMode.STRETCH, overflow_align=OverflowAlign.LEFT, density=Density.EXPANDED, width=220) 187 self.create_button = CreateSKINButton(label=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/FinalizeDesign'), func=self._on_create_btn, variant=ButtonVariant.PRIMARY) 188 info_container = ContainerAutoSize(name='info_container', parent=self, align=Align.BOTTOMRIGHT, width=button_group.width, left=64, top=108) 189 StudioCostInfo(name='cost_info', parent=info_container, align=Align.TOBOTTOM) 190 self.sequencing_time = SequencingTime(name='sequencing_time', parent=info_container, align=Align.TOBOTTOM, minWidth=button_group.width - 32, padBottom=4) 191 button_group.add_button(self.create_button) 192 193 def _on_create_btn(self, *args): 194 if should_show_popup_if_skin_name_missing.is_enabled() and not current_skin_design.get().name: 195 dialogue = SkinNameDialogue(window_name=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/SKINName')) 196 form_value = dialogue.ShowModal() 197 if form_value == uiconst.ID_CLOSE or not form_value[SkinNameDialogue.COMPONENT_NAME]: 198 return 199 self.on_create_btn() 200 201 def on_hull_selection_button(self, *args): 202 if self.hull_selection_panel: 203 self.close_hull_selection_panel() 204 else: 205 self.show_hull_selection_panel() 206 207 def show_hull_selection_panel(self): 208 self.deselect_all_cosmetic_slots() 209 self.hull_selection_panel = HullSelectionPanel(parent=self.overlay_cont, align=uiconst.TOLEFT, width=360, padding=(0, 210 PANEL_PAD_TOP, 211 0, 212 PANEL_PAD_BOTTOM)) 213 self.hull_selection_panel.on_close_btn.connect(self.on_hull_selection_close_btn) 214 animations.FadeTo(self.hull_selection_panel, 0.0, 1.0, duration=0.2) 215 216 def on_hull_selection_close_btn(self, *args): 217 self.close_hull_selection_panel() 218 219 def close_hull_selection_panel(self): 220 if self.hull_selection_panel: 221 self.hull_selection_panel.Close() 222 self.hull_selection_panel = None 223 224 def deselect_all_cosmetic_slots(self): 225 current_skin_design.set_selected_slot_id(None) 226 227 def close_all_overlay_panels(self): 228 self.close_hull_selection_panel() 229 self.deselect_all_cosmetic_slots() 230 231 def construct_base_containers(self): 232 self.button_overlay_cont = Container(name='button_overlay_cont', parent=self, padding=(32, 0, 64, 64), align=Align.TOBOTTOM_NOPUSH, height=HEIGHT_EXPANDED) 233 self.center_top_cont = Container(name='center_top_cont', parent=self) 234 self.center_left_cont = Container(name='center_left_cont', parent=self) 235 self.center_right_cont = Container(name='center_right_cont', parent=self) 236 self.slots_left_cont = Container(name='slots_left_cont', parent=self.center_left_cont) 237 self.slots_right_cont = Container(name='slots_right_cont', parent=self.center_right_cont) 238 self.overlay_cont = Container(name='overlay_cont', parent=self, padding=32) 239 240 def construct_gizmo_controls(self): 241 self.gizmo_controls = GizmoControls(name='gizmo_controls', parent=self.center_top_cont, align=Align.CENTERTOP) 242 243 def reconstruct_slots_and_panels(self): 244 self.reconstruct_slots() 245 self.reconstruct_panels() 246 247 def reconstruct_slots(self): 248 self.construct_non_pattern_slots() 249 250 def construct_non_pattern_slots(self): 251 for slot in self.slots_by_id.values(): 252 slot.Close() 253 254 self.slots_by_id.clear() 255 slot_config = self.get_current_skin_slot_config() 256 for slot_id in slot_config.slots: 257 if slot_id in PATTERN_RELATED_SLOT_IDS: 258 continue 259 slot_data = SlotsDataLoader.get_slot_data(slot_id) 260 slot = CosmeticSlot(name='slot_{name}'.format(name=slot_data.internal_name), parent=self.slots_left_cont, slot_data=slot_data) 261 slot.angle_deg = self.SLOT_ANGLES.get(slot_data.unique_id) 262 self.slots_by_id[slot_data.unique_id] = slot 263 if slot_id == SlotID.PRIMARY_NANOCOATING and not current_skin_design.get().has_fitted_components(): 264 slot.show_pulse_highlight() 265 266 def construct_pattern_slots(self): 267 self.pattern_slots = PatternSlots(name='pattern_slots', parent=self.slots_right_cont, align=Align.CENTER) 268 269 def reconstruct_panels(self): 270 for panel in self.panels_by_id.values(): 271 panel.on_close_btn.disconnect(self.on_panel_close_btn) 272 panel.Close() 273 274 self.panels_by_id.clear() 275 slot_config = self.get_current_skin_slot_config() 276 for slot_id in slot_config.slots: 277 slot_data = SlotsDataLoader.get_slot_data(slot_id) 278 self._construct_panel(slot_data) 279 280 def _construct_panel(self, slot_data): 281 align = Align.TORIGHT_NOPUSH if slot_data.unique_id in PATTERN_RELATED_SLOT_IDS else Align.TOLEFT_NOPUSH 282 panel = ComponentPanel(name='panel_{slot_name}'.format(slot_name=slot_data.internal_name), parent=self.overlay_cont, slot_data=slot_data, align=align, padding=(0, 283 PANEL_PAD_TOP, 284 0, 285 PANEL_PAD_BOTTOM)) 286 panel.on_close_btn.connect(self.on_panel_close_btn) 287 self.panels_by_id[slot_data.unique_id] = panel 288 289 def on_panel_close_btn(self, *args): 290 self.close_all_overlay_panels() 291 292 def on_design_reset(self, *args): 293 self.populate_from_design() 294 self.update_slots() 295 296 def on_existing_design_loaded(self, *args): 297 self.populate_from_design() 298 self.update_slots() 299 self._update_creator_character_info() 300 301 def populate_from_design(self): 302 self.populate_non_pattern_slots_from_design() 303 self.populate_pattern_slots_from_design() 304 self.save_skin_button.update_button_state() 305 self.save_as_skin_button.update_button_state() 306 307 def populate_non_pattern_slots_from_design(self): 308 for slot_id in self.slots_by_id.keys(): 309 component_instance = current_skin_design.get().slot_layout.fitted_slots.get(slot_id, None) 310 self.slots_by_id[slot_id].on_slot_fitting_changed(slot_id, component_instance) 311 self.panels_by_id[slot_id].select_component(component_instance) 312 313 def populate_pattern_slots_from_design(self): 314 self.pattern_slots.populate_from_design() 315 for slot_id in self.pattern_slots.slots_by_id.keys(): 316 component_instance = current_skin_design.get().slot_layout.fitted_slots.get(slot_id, None) 317 self.panels_by_id[slot_id].select_component(component_instance) 318 319 def on_slot_selected(self, slot_id): 320 self.close_hull_selection_panel() 321 for panel in self.panels_by_id.values(): 322 panel.on_slot_selected(slot_id) 323 324 def on_scene_zoom(self, is_zoomed): 325 if is_zoomed: 326 self.center_top_cont.state = uiconst.UI_DISABLED 327 self.center_left_cont.state = uiconst.UI_DISABLED 328 self.center_right_cont.state = uiconst.UI_DISABLED 329 else: 330 self.center_top_cont.state = uiconst.UI_PICKCHILDREN 331 self.center_left_cont.state = uiconst.UI_PICKCHILDREN 332 self.center_right_cont.state = uiconst.UI_PICKCHILDREN 333 if is_zoomed: 334 width_distance, height_distance = self.get_zoomed_width_height() 335 left_target = -width_distance 336 right_target = width_distance 337 top_target = -(min(width_distance, height_distance) + self.SLOT_WINDOW_GAP) 338 else: 339 left_target = 0.0 340 right_target = 0.0 341 top_target = 0.0 342 animations.MorphScalar(self.center_left_cont, 'left', self.center_left_cont.left, left_target, duration=0.3) 343 animations.MorphScalar(self.center_right_cont, 'left', self.center_right_cont.left, right_target, duration=0.3) 344 animations.MorphScalar(self.center_top_cont, 'top', self.center_top_cont.top, top_target, duration=0.3) 345 346 def get_zoomed_width_height(self): 347 diameter = self.get_circular_layout_radius() * 2.0 348 page_width, page_height = self.GetAbsoluteSize() 349 width_distance = page_width - diameter - self.SLOT_WINDOW_GAP * 2.0 350 height_distance = page_height - diameter - self.SLOT_WINDOW_GAP * 2.0 351 return (width_distance, height_distance) 352 353 def on_size_changed_signal(self, width, height): 354 self.update_circular_layout(width, height) 355 if width < 1100: 356 self.gizmo_controls.width = self.gizmo_controls.default_width + width - 1100 357 else: 358 self.gizmo_controls.width = self.gizmo_controls.default_width 359 360 def update_circular_layout(self, width, height): 361 radius = self.get_circular_layout_radius() 362 width_available = min(380, width / 2 - radius - self.circular_layout_pos_offset) 363 compact_mode = width_available < self.minimum_element_width 364 for slot in self.slots_by_id.values(): 365 slot.radius = radius 366 367 self.pattern_slots.update_radial_position(radius + 40) 368 for panel in self.panels_by_id.values(): 369 self.move_panel(compact_mode, width_available, panel) 370 371 self.move_panel(compact_mode, width_available, self.nebula_selection_panel) 372 width_available_no_min = width / 2 - radius - self.circular_layout_pos_offset 373 height_available = height / 2 - radius - self.circular_layout_pos_offset 374 self.bg_fade_container.adjust_size(width_available_no_min, height_available) 375 self.gizmo_controls.radius = radius 376 self.randomize_controls.radius = radius - 20 377 self.randomize_controls.angle_deg = 212 if compact_mode else 220 378 if compact_mode: 379 self.undo_btns.align = Align.BOTTOMRIGHT 380 self.undo_btns.left = 64 381 self.undo_btns.top = 210 382 else: 383 self.undo_btns.align = Align.CENTER 384 self.undo_btns.radius = radius 385 self.undo_btns.angle_deg = 45 386 387 def move_panel(self, compact_mode, width_available, panel): 388 if compact_mode: 389 offset = 128 if panel.align == Align.TOLEFT else 164 390 panel.left = width_available + offset 391 panel.width = self.minimum_element_width 392 panel.padTop = PANEL_PAD_TOP + 16 393 panel.padBottom = PANEL_PAD_BOTTOM + 8 394 else: 395 panel.left = 0 396 panel.width = width_available 397 panel.padTop = PANEL_PAD_TOP 398 panel.padBottom = PANEL_PAD_BOTTOM 399 400 def update_slots(self): 401 self.reconstruct_slots_and_panels() 402 self.populate_from_design() 403 self.update_circular_layout(*self.GetAbsoluteSize()) 404 405 def get_circular_layout_radius(self): 406 return get_circular_layout_radius(*self.GetAbsoluteSize()) 407 408 def get_current_skin_slot_config(self): 409 return SlotConfigurationsDataLoader.get_for_ship(current_skin_design.get().ship_type_id) 410 411 412 @Enum 413 414 class CreateButtonErrorState(object): 415 OMEGA_BLOCKED = 1 416 NO_COMPONENT_SELECTED = 2 417 SEQUENCING_FEATURE_DISABLED = 3 418 419 420 class CreateSKINButton(Button): 421 __notifyevents__ = ['OnSubscriptionChanged'] 422 error_state = None 423 424 def __init__(self, get_menu_entry_data_func = None, **kwargs): 425 super(CreateSKINButton, self).__init__(get_menu_entry_data_func, **kwargs) 426 sm.RegisterNotify(self) 427 428 def OnSubscriptionChanged(self): 429 self.update() 430 431 def LoadTooltipPanel(self, tooltipPanel, *args): 432 if self.error_state == CreateButtonErrorState.OMEGA_BLOCKED: 433 tooltipPanel.LoadStandardSpacing() 434 tooltipPanel.columns = 2 435 tooltipPanel.AddRow(rowClass=OmegaTooltipPanelCell, text=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/CannotSequenceForHullOmega'), origin=ORIGIN_SHIP_SKINR) 436 tooltipPanel.pickState = PickState.ON 437 elif self.error_state == CreateButtonErrorState.NO_COMPONENT_SELECTED: 438 tooltipPanel.LoadStandardSpacing() 439 tooltipPanel.columns = 1 440 tooltipPanel.AddLabelMedium(text=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/NoComponentsSelected'), wrapWidth=300) 441 elif self.error_state == CreateButtonErrorState.SEQUENCING_FEATURE_DISABLED: 442 tooltipPanel.LoadStandardSpacing() 443 tooltipPanel.columns = 1 444 tooltipPanel.AddLabelMedium(text=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/SequencingFeatureDisabled'), wrapWidth=300) 445 446 def update(self): 447 if not get_ship_skin_sequencing_svc().can_sequence_for_hull(current_skin_design.get().ship_type_id): 448 self.enabled = False 449 self.error_state = CreateButtonErrorState.OMEGA_BLOCKED 450 elif not current_skin_design.get().slot_layout.fitted_slots: 451 self.enabled = False 452 self.error_state = CreateButtonErrorState.NO_COMPONENT_SELECTED 453 else: 454 self.enabled = True 455 self.error_state = None 456 457 458 class UndoButtons(ButtonGroup, RadiallyAlignedMixin): 459 default_align = Align.CENTER 460 461 def __init__(self, **kw): 462 super(UndoButtons, self).__init__(**kw) 463 self.undo_btn = Button(parent=self, func=self.on_undo_btn, texturePath=eveicon.arrow_rotate_left, hint=GetByLabel('UI/Common/Undo'), variant=ButtonVariant.GHOST) 464 self.redo_btn = Button(parent=self, func=self.on_redo_btn, texturePath=eveicon.arrow_rotate_right, hint=GetByLabel('UI/Common/Redo'), variant=ButtonVariant.GHOST) 465 self.connnect_signals() 466 self.update() 467 468 def connnect_signals(self): 469 current_skin_design_signals.on_undo_history_change.connect(self.on_undo_history_change) 470 471 def on_undo_history_change(self): 472 self.update() 473 474 def update(self): 475 self.undo_btn.enabled = current_skin_design.is_undo_enabled() 476 self.redo_btn.enabled = current_skin_design.is_redo_enabled() 477 478 def on_undo_btn(self, *args): 479 current_skin_design.undo() 480 481 def on_redo_btn(self, *args): 482 current_skin_design.redo()