formFieldsMultivalue.py (17266B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\carbonui\control\forms\formFieldsMultivalue.py 2 import eveicon 3 import uthread 4 from carbonui import Align, uiconst 5 from carbonui import TextColor 6 from carbonui.text.styles import TextBody 7 from carbonui.control.buttonIcon import ButtonIcon 8 from carbonui.control.combo import Combo 9 from carbonui.control.comboEntryData import ComboEntryData 10 from carbonui.control.forms.formFields import BaseField 11 from carbonui.primitives.container import Sprite, Container 12 from carbonui.primitives.containerAutoSize import ContainerAutoSize 13 from carbonui.primitives.frame import Frame 14 from eveformat.client import solar_system_with_security 15 from eveui import LocationField, fade, fade_in, ItemField, NpcCorporationField, ShipField, PlayerOrPlayerOrganizationField, fade_out 16 from eveui.audio import Sound 17 from eveui.autocomplete import LocationSuggestion, ItemTypeSuggestion, ItemGroupSuggestion, ItemCategorySuggestion, ShipTypeSuggestion, ShipClassSuggestion, OwnerIdentitySuggestion, NpcCorporationSuggestion 18 from localization import GetByLabel 19 20 class MultiValueFormField(BaseField): 21 22 def ApplyAttributes(self, attributes): 23 self.edit = None 24 self._result = list() 25 self._results_entry = {} 26 super(MultiValueFormField, self).ApplyAttributes(attributes) 27 28 def ConstructBody(self): 29 self.ConstructSearchField() 30 self._results_container = ContainerAutoSize(name='multivalue_results_container', parent=self, align=Align.TOTOP) 31 for entry in self.component.get_value() or []: 32 entry = self._parse_entry(entry) 33 if entry is not None: 34 self._initialize_entry(entry) 35 36 def ConstructSearchField(self): 37 pass 38 39 def _parse_entry(self, entry): 40 if isinstance(entry, tuple): 41 _entry_type, entry_value = entry 42 parsed_entry = entry_value 43 else: 44 parsed_entry = entry 45 return parsed_entry 46 47 def _initialize_entry(self, entry): 48 self._add_new_entry(entry) 49 50 def _add_entry_chip(self, entry): 51 self._result.append(entry) 52 self._sort_results() 53 chip = MultiValueListItem(idx=self._result.index(entry), parent=self._results_container, entry=entry, get_text=self._get_text, render_icon=self._render_icon, get_menu=self._get_menu, get_drag_data=self._get_drag_data, on_click_remove=self._on_remove_chip) 54 self._results_entry[entry] = chip 55 56 def _get_label_text(self): 57 return u'{} <color={}>{}/{}</color>'.format(self.component.label, TextColor.DISABLED, len(self._result), self.component.max_entries) 58 59 def _get_text(self, entry): 60 return str(entry) 61 62 def _get_subtext(self, entry): 63 return '' 64 65 def _render_icon(self, entry, size): 66 return None 67 68 def _get_menu(self, entry): 69 return None 70 71 def _get_drag_data(self, entry): 72 return None 73 74 def _on_remove_chip(self, entry): 75 self._result.remove(entry) 76 chip = self._results_entry[entry] 77 chip.Close() 78 del self._results_entry[entry] 79 self._values_changed() 80 81 def _cant_add_new_entry(self, entry): 82 return self._result is None or entry in self._result or self._at_max_entries() 83 84 def _add_new_entry(self, entry): 85 if self._cant_add_new_entry(entry): 86 self._clear_edit() 87 return 88 self._add_entry_chip(entry) 89 self._values_changed() 90 self._clear_edit() 91 92 def _at_max_entries(self): 93 return len(self._result) >= self.component.max_entries 94 95 def _values_changed(self): 96 self.component.on_field_value_changed(self.get_value()) 97 self.label.text = self._get_label_text() 98 self._update_input_field() 99 100 def _update_input_field(self): 101 pass 102 103 def _clear_edit(self): 104 return NotImplementedError() 105 106 def get_value(self): 107 return self._result 108 109 def set_value(self, value): 110 pass 111 112 def _sort_results(self): 113 pass 114 115 116 class _AutoCompleteMultiField(MultiValueFormField): 117 118 def get_value(self): 119 return [ self._get_suggestion_value(suggestion) for suggestion in self._result ] 120 121 def _get_suggestion_value(self, suggestion): 122 return suggestion 123 124 def _value_changed(self, *args): 125 new_value = self.edit.completed_suggestion 126 if new_value is None: 127 return 128 self._add_new_entry(new_value) 129 130 def _get_text(self, entry): 131 return entry.text 132 133 def _get_menu(self, entry): 134 return entry.get_menu() 135 136 def _get_drag_data(self, entry): 137 return entry.get_drag_data() 138 139 def _update_input_field(self): 140 if self._at_max_entries(): 141 self.edit.placeholder = GetByLabel('UI/Corporations/Goals/MultiValueLimitReached') 142 else: 143 self.edit.placeholder = self.component.placeholder 144 145 def _clear_edit(self): 146 uthread.pool('_AutoCompleteMultiField::_clear_edit', self.edit.clear) 147 148 def _sort_results(self): 149 self._result.sort(key=lambda entry: self._sort_key(entry)) 150 151 def _sort_key(self, entry): 152 return entry.text.lower() 153 154 155 class MultiValueLocationFormField(_AutoCompleteMultiField): 156 157 def ConstructSearchField(self): 158 self.edit = LocationField(parent=self, align=Align.TOTOP, placeholder=self.component.placeholder, show_suggestions_on_focus=True, location_id=None, include_region=self.component.include_region, include_constellation=self.component.include_constellation, include_solar_system=self.component.include_solar_system, include_station=False) 159 self.edit.bind(completed_suggestion=self._value_changed) 160 161 def _initialize_entry(self, entry): 162 self._add_new_entry(LocationSuggestion(entry)) 163 164 def _get_suggestion_value(self, suggestion): 165 return suggestion.location_id 166 167 def _get_text(self, entry): 168 if entry.is_solar_system: 169 return solar_system_with_security(entry.location_id) 170 subtext = None 171 if entry.is_constellation: 172 subtext = GetByLabel('UI/Common/LocationTypes/Constellation') 173 elif entry.is_region: 174 subtext = GetByLabel('UI/Common/LocationTypes/Region') 175 return _get_text_with_subtext(entry.text, subtext) 176 177 def _render_icon(self, entry, size): 178 if entry.is_solar_system: 179 icon = eveicon.solar_system 180 elif entry.is_constellation: 181 icon = eveicon.constellation 182 elif entry.is_region: 183 icon = eveicon.region 184 else: 185 return None 186 return _render_basic_icon(icon, size) 187 188 def _get_menu(self, entry): 189 return entry.get_menu() 190 191 def _sort_key(self, entry): 192 if entry.is_solar_system: 193 priority = 1 194 elif entry.is_constellation: 195 priority = 2 196 elif entry.is_region: 197 priority = 3 198 else: 199 priority = 4 200 return (priority, entry.text.lower()) 201 202 203 class MultiValueItemFormField(_AutoCompleteMultiField): 204 205 def ConstructSearchField(self): 206 self.edit = ItemField(parent=self, align=Align.TOTOP, placeholder=self.component.placeholder, show_suggestions_on_focus=True, type_filter=self.component.type_filter, group_filter=self.component.group_filter, category_filter=self.component.category_filter, include_type=self.component.include_type, include_group=self.component.include_group, include_category=self.component.include_category) 207 self.edit.bind(completed_suggestion=self._value_changed) 208 209 def _parse_entry(self, entry): 210 if isinstance(entry, tuple): 211 parsed_entry = entry 212 else: 213 parsed_entry = ('item_type', entry) 214 return parsed_entry 215 216 def _initialize_entry(self, entry): 217 entry_type, entry_id = entry 218 suggestion = None 219 if entry_type == 'item_type': 220 suggestion = ItemTypeSuggestion(entry_id) 221 elif entry_type == 'item_group': 222 suggestion = ItemGroupSuggestion(entry_id) 223 elif entry_type == 'item_category': 224 suggestion = ItemCategorySuggestion(entry_id) 225 if suggestion: 226 self._add_new_entry(suggestion) 227 228 def _get_suggestion_value(self, suggestion): 229 if isinstance(suggestion, ItemTypeSuggestion): 230 return ('item_type', suggestion.type_id) 231 if isinstance(suggestion, ItemGroupSuggestion): 232 return ('item_group', suggestion.group_id) 233 if isinstance(suggestion, ItemCategorySuggestion): 234 return ('item_category', suggestion.category_id) 235 236 def _render_icon(self, entry, size): 237 if isinstance(entry, ItemTypeSuggestion): 238 size = max(size, 24) 239 return entry.render_icon(size) 240 241 def _get_text(self, entry): 242 if isinstance(entry, ItemTypeSuggestion): 243 return entry.text 244 return _get_text_with_subtext(entry.text, entry.subtext) 245 246 def _sort_key(self, entry): 247 if isinstance(entry, ItemTypeSuggestion): 248 priority = 1 249 elif isinstance(entry, ItemGroupSuggestion): 250 priority = 2 251 elif isinstance(entry, ItemCategorySuggestion): 252 priority = 3 253 else: 254 priority = 4 255 return (priority, entry.text.lower()) 256 257 258 class MultiValueNPCCorpFormField(_AutoCompleteMultiField): 259 260 def ConstructSearchField(self): 261 self.edit = NpcCorporationField(parent=self, align=Align.TOTOP, placeholder=self.component.placeholder, show_suggestions_on_focus=True, filter=self.component.filter) 262 self.edit.bind(completed_suggestion=self._value_changed) 263 264 def _initialize_entry(self, entry): 265 self._add_new_entry(NpcCorporationSuggestion(entry)) 266 267 def _get_suggestion_value(self, suggestion): 268 return suggestion.corporation_id 269 270 def _render_icon(self, entry, size): 271 size = max(size, 24) 272 return entry.render_icon(size) 273 274 275 class MultiValuePlayerCorpOrOrgFormField(_AutoCompleteMultiField): 276 277 def ConstructSearchField(self): 278 self.edit = PlayerOrPlayerOrganizationField(parent=self, align=Align.TOTOP, placeholder=self.component.placeholder, include_character=self.component.include_character, include_corporation=self.component.include_corporation, include_alliance=self.component.include_alliance, include_faction=self.component.include_faction, show_suggestions_on_focus=True) 279 self.edit.bind(completed_suggestion=self._value_changed) 280 281 def _initialize_entry(self, entry): 282 self._add_new_entry(OwnerIdentitySuggestion(entry)) 283 284 def _get_suggestion_value(self, suggestion): 285 return suggestion.owner_id 286 287 def _render_icon(self, entry, size): 288 size = max(size, 24) 289 return entry.render_icon(size) 290 291 def _get_text(self, entry): 292 return _get_text_with_subtext(entry.text, entry.subtext) 293 294 def _sort_key(self, entry): 295 if entry.is_character: 296 priority = 1 297 elif entry.is_corporation: 298 priority = 2 299 elif entry.is_alliance: 300 priority = 3 301 elif entry.is_faction: 302 priority = 4 303 else: 304 priority = 5 305 return (priority, entry.text.lower()) 306 307 308 class MultiValueShipAndShipTreeFormField(_AutoCompleteMultiField): 309 310 def ConstructSearchField(self): 311 self.edit = ShipField(parent=self, align=Align.TOTOP, placeholder=self.component.placeholder, show_suggestions_on_focus=True, include_type=self.component.include_type, include_class=self.component.include_class, type_filter=self.component.type_filter) 312 self.edit.bind(completed_suggestion=self._value_changed) 313 314 def _parse_entry(self, entry): 315 if isinstance(entry, tuple): 316 parsed_entry = entry 317 else: 318 parsed_entry = ('ship_type', entry) 319 return parsed_entry 320 321 def _initialize_entry(self, entry): 322 entry_type, entry_id = entry 323 suggestion = None 324 if entry_type == 'ship_type': 325 suggestion = ShipTypeSuggestion(entry_id) 326 elif entry_type == 'ship_class': 327 suggestion = ShipClassSuggestion(entry_id) 328 if suggestion: 329 self._add_new_entry(suggestion) 330 331 def _get_suggestion_value(self, suggestion): 332 if isinstance(suggestion, ShipTypeSuggestion): 333 return ('ship_type', suggestion.type_id) 334 if isinstance(suggestion, ShipClassSuggestion): 335 return ('ship_class', suggestion.ship_class_id) 336 337 def _render_icon(self, entry, size): 338 if isinstance(entry, ShipTypeSuggestion): 339 size = max(size, 24) 340 return entry.render_icon(size) 341 342 def _sort_key(self, entry): 343 if isinstance(entry, ShipTypeSuggestion): 344 priority = 1 345 elif isinstance(entry, ShipClassSuggestion): 346 priority = 2 347 else: 348 priority = 3 349 return (priority, entry.text.lower()) 350 351 352 class MultiValueEnum(MultiValueFormField): 353 354 def ConstructSearchField(self): 355 self.placeholder = self.component.placeholder or GetByLabel('UI/Common/PleaseSelect') 356 self.edit = Combo(name=self.component.get_id(), parent=self, align=Align.TOTOP, callback=self._value_changed, options=self.component.options, nothingSelectedText=self.placeholder) 357 358 def _parse_entry(self, entry): 359 entry = super(MultiValueEnum, self)._parse_entry(entry) 360 if not isinstance(entry, ComboEntryData): 361 for option in self.component.options: 362 if option.returnValue == entry: 363 return option 364 365 return None 366 return entry 367 368 def _value_changed(self, *args): 369 entry = self.edit.GetSelectedEntry() 370 if entry is None: 371 return 372 self._add_new_entry(entry) 373 374 def _on_remove_chip(self, entry): 375 if entry not in self._result: 376 return 377 super(MultiValueEnum, self)._on_remove_chip(entry) 378 self.edit.ShowEntry(entry.returnValue) 379 380 def _add_new_entry(self, entry): 381 if self._cant_add_new_entry(entry): 382 self._clear_edit() 383 return 384 self.edit.HideEntry(entry.returnValue) 385 super(MultiValueEnum, self)._add_new_entry(entry) 386 387 def _get_text(self, entry): 388 return entry.label 389 390 def _render_icon(self, entry, size): 391 return _render_basic_icon(entry.icon, size) 392 393 def _clear_edit(self): 394 self.edit.SetNothingSelected() 395 396 def _update_input_field(self): 397 if self._at_max_entries(): 398 self.edit.nothingSelectedText = GetByLabel('UI/Corporations/Goals/MultiValueLimitReached') 399 else: 400 self.edit.nothingSelectedText = self.placeholder 401 402 403 class MultiValueListItem(Container): 404 default_align = Align.TOTOP 405 default_state = uiconst.UI_NORMAL 406 default_alignMode = Align.TOLEFT 407 default_height = 28 408 default_padTop = 8 409 isDragObject = True 410 411 def __init__(self, entry, get_text, render_icon, get_menu, get_drag_data, on_click_remove, *args, **kwargs): 412 super(MultiValueListItem, self).__init__(*args, **kwargs) 413 self._entry = entry 414 self._get_menu = get_menu 415 self._get_drag_data = get_drag_data 416 self._on_click_remove = on_click_remove 417 contents_container = Container(parent=self, align=Align.TOALL, padLeft=8, padRight=8) 418 self._close_container = Container(parent=contents_container, align=Align.TORIGHT, width=16, padLeft=8, opacity=0) 419 ButtonIcon(parent=self._close_container, texturePath=eveicon.close, align=Align.CENTER, height=16, width=16, color=TextColor.SECONDARY, func=self._close_icon_clicked) 420 icon = render_icon(entry, size=16) 421 if icon: 422 icon_container = Container(parent=contents_container, state=uiconst.UI_DISABLED, align=Align.TOLEFT, width=16, padRight=8) 423 icon.align = Align.CENTER 424 icon.SetParent(icon_container) 425 label_container = Container(parent=contents_container, align=Align.TOALL) 426 self._label = TextBody(parent=label_container, align=Align.CENTERLEFT, text=get_text(entry)) 427 self._bg_frame = Frame(bgParent=self, cornerSize=9, texturePath='res:/UI/Texture/Shared/DarkStyle/panel1Corner_Solid.png', color=(1, 1, 1, 0.05)) 428 429 def _close_icon_clicked(self, *args, **kwargs): 430 self._on_click_remove(self._entry) 431 432 def GetMenu(self): 433 return self._get_menu(self._entry) 434 435 def GetDragData(self): 436 drag_data = self._get_drag_data(self._entry) 437 if drag_data: 438 return [drag_data] 439 440 def OnMouseEnter(self, *args): 441 super(MultiValueListItem, self).OnMouseEnter(*args) 442 Sound.button_hover.play() 443 fade(self._bg_frame, end_value=0.1, duration=0.2) 444 fade_in(self._close_container, duration=0.2) 445 446 def OnMouseExit(self, *args): 447 super(MultiValueListItem, self).OnMouseExit(*args) 448 fade(self._bg_frame, end_value=0.05, duration=0.2) 449 fade_out(self._close_container, duration=0.2) 450 451 452 def _render_basic_icon(icon, size): 453 return Sprite(texturePath=icon, align=Align.CENTER, height=size, width=size, color=TextColor.NORMAL) 454 455 456 def _get_text_with_subtext(text, subtext): 457 if subtext: 458 return u'<color={}>[{}]</color> {}'.format(TextColor.SECONDARY, subtext, text) 459 return text