NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

formFields.py (29800B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\carbonui\control\forms\formFields.py
      2 import datetime
      3 import time
      4 from datetime import date
      5 import eveicon
      6 import eveui
      7 import uthread2
      8 from dateutil import relativedelta
      9 from eveui import ItemTypeField
     10 from eveui.autocomplete import ItemCategorySuggestion, ItemGroupSuggestion, ItemTypeSuggestion, LocationSuggestion, ShipTypeSuggestion
     11 from eveui.autocomplete.npc.suggestion import FactionSuggestion, NpcCorporationSuggestion
     12 from eveui.autocomplete.player.suggestion import OwnerIdentitySuggestion
     13 from carbonui import Align, ButtonFrameType, ButtonVariant, Density, TextColor, uiconst
     14 from carbonui.text.styles import TextBody, TextDetail
     15 from carbonui.control.button import Button
     16 from carbonui.control.buttonIcon import ButtonIcon
     17 from carbonui.control.checkbox import Checkbox
     18 from carbonui.control.combo import Combo
     19 from carbonui.control.forms import formComponent
     20 from carbonui.control.forms.form import Form
     21 from carbonui.control.forms.formsUtil import FormatAsIncompleteInput
     22 from carbonui.control.singlelineedits.singleLineEditFloat import SingleLineEditFloat
     23 from carbonui.control.singlelineedits.singleLineEditInteger import SingleLineEditInteger
     24 from carbonui.control.singlelineedits.singleLineEditText import SingleLineEditText
     25 from carbonui.primitives.container import Container
     26 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     27 from carbonui.primitives.flowcontainer import FlowContainer
     28 from carbonui.primitives.line import Line
     29 from carbonui.primitives.sprite import Sprite
     30 from eve.client.script.ui.control.datepicker import DatePicker
     31 from eve.client.script.ui.control.eveEditPlainText import EditPlainText
     32 from eve.client.script.ui.control.infoIcon import InfoIcon
     33 from localization import GetByLabel
     34 from localization.formatters.timeIntervalFormatters import FormatSpecificDurationWritten
     35 
     36 class BaseField(ContainerAutoSize):
     37     default_align = Align.TOTOP
     38     default_alignMode = Align.TOTOP
     39     default_padTop = 8
     40     default_padBottom = 16
     41 
     42     def ApplyAttributes(self, attributes):
     43         super(BaseField, self).ApplyAttributes(attributes)
     44         self.label = None
     45         self.icon = None
     46         self.info = None
     47         self.component = attributes.component
     48         self.ConstructLayout()
     49         self.component.init_value(self.get_value())
     50         self.component.on_label_changed.connect(self.OnLabelChanged)
     51         self.component.on_icon_changed.connect(self.OnIconChanged)
     52         self.component.on_hint_changed.connect(self.OnHintChanged)
     53         self.component.on_validated.connect(self.OnValidated)
     54         self.component.on_value_set.connect(self.set_value)
     55         self.component.on_value_cleared.connect(self.clear_value)
     56         self.component.on_visibility_changed.connect(self.OnVisibilityChanged)
     57         self.display = self.component.is_visible
     58         if self.component.indent_level:
     59             self.padLeft = self.component.indent_level * 24
     60 
     61     def OnLabelChanged(self, component):
     62         if self.label:
     63             self.label.text = self._get_label_text()
     64 
     65     def OnIconChanged(self, component):
     66         if self.icon:
     67             self.icon.texturePath = component.icon
     68 
     69     def OnHintChanged(self, component):
     70         self.component.hint = component.hint
     71         self.ConstructHint(self.header_container)
     72 
     73     def OnVisibilityChanged(self, component):
     74         self.display = component.is_visible
     75 
     76     def OnValidated(self, component, is_valid):
     77         if not self.label:
     78             return
     79         text = self._get_label_text()
     80         if not is_valid:
     81             text = FormatAsIncompleteInput(text)
     82         self.label.text = text
     83 
     84     def ConstructLayout(self):
     85         self.ConstructHeader()
     86         self.ConstructBody()
     87 
     88     def Reconstruct(self):
     89         self.Flush()
     90         self.ConstructLayout()
     91 
     92     def ConstructHeader(self):
     93         self.header_container = ContainerAutoSize(name='header_container', parent=self, align=self.align, alignMode=self.alignMode, padBottom=4)
     94         self.ConstructIcon(self.header_container)
     95         self.ConstructHint(self.header_container)
     96         self.ConstructLabel(self.header_container)
     97 
     98     def ConstructIcon(self, parent):
     99         icon = self.component.icon
    100         if not icon:
    101             return
    102         icon_container = Container(name='icon_container', parent=parent, state=uiconst.UI_DISABLED, align=Align.TOLEFT, width=16)
    103         self.icon = Sprite(parent=icon_container, texturePath=icon, align=Align.CENTER, height=16, width=16, color=TextColor.SECONDARY)
    104 
    105     def ConstructLabel(self, parent):
    106         self.label = TextDetail(parent=parent, align=self.align, text=self._get_label_text(), color=TextColor.SECONDARY, padLeft=4)
    107 
    108     def ConstructHint(self, parent = None):
    109         if self.component.hint:
    110             self.info = InfoIcon(parent=parent or self, align=Align.TOPRIGHT, hint=self.component.hint)
    111         elif self.info:
    112             self.info.Close()
    113             self.info = None
    114 
    115     def ConstructBody(self):
    116         pass
    117 
    118     def get_value(self):
    119         raise NotImplementedError
    120 
    121     def _OnValueChanged(self, *args):
    122         self.component.on_field_value_changed(self.get_value())
    123 
    124     def set_value(self, value):
    125         raise NotImplementedError
    126 
    127     def clear_value(self):
    128         raise NotImplementedError
    129 
    130     def _get_label_text(self):
    131         return self.component.label
    132 
    133 
    134 class FormField(BaseField):
    135     default_padTop = 0
    136     default_padBottom = 0
    137 
    138     def ConstructLayout(self):
    139         self.ConstructHint()
    140         ConstructFields(self, self.component)
    141         self.component.on_components_changed.connect(self.OnComponentsChanged)
    142 
    143     def OnComponentsChanged(self, *args):
    144         self.Reconstruct()
    145 
    146     def get_value(self):
    147         return self.component.get_value()
    148 
    149     def set_value(self, value):
    150         pass
    151 
    152     def clear_value(self):
    153         pass
    154 
    155 
    156 class EditField(BaseField):
    157 
    158     def ConstructBody(self):
    159         max_chars = self.component.get_max_characters()
    160         self.textEdit = SingleLineEditText(name=self.component.get_id(), parent=self, setvalue=self.component.get_value(), hintText=self.component.placeholder, align=Align.TOTOP, OnChange=self._OnValueChanged, showLetterCounter=bool(max_chars), maxLength=max_chars)
    161 
    162     def get_value(self):
    163         return self.textEdit.GetText().strip()
    164 
    165     def set_value(self, value):
    166         self.textEdit.SetText(value)
    167 
    168 
    169 class EditFieldMultiLine(BaseField):
    170 
    171     def ConstructBody(self):
    172         max_chars = self.component.get_max_characters()
    173         self.textEdit = EditPlainText(name=self.component.get_id(), parent=self, setvalue=self.component.get_value(), hintText=self.component.placeholder, height=32 + self.component.num_lines * 16, align=Align.TOTOP, countWithTags=bool(max_chars), counterMax=max_chars, showattributepanel=self.component.show_formatting_panel)
    174         self.textEdit.OnChange = self._OnValueChanged
    175 
    176     def get_value(self):
    177         return self.textEdit.GetValue().strip()
    178 
    179     def set_value(self, value):
    180         self.textEdit.SetText(value)
    181 
    182 
    183 class Label(BaseField):
    184 
    185     def ConstructLayout(self):
    186         self.ConstructLabel(self)
    187 
    188     def get_value(self):
    189         return None
    190 
    191     def set_value(self, value):
    192         pass
    193 
    194 
    195 class IntegerEditField(BaseField):
    196 
    197     def ApplyAttributes(self, attributes):
    198         super(IntegerEditField, self).ApplyAttributes(attributes)
    199         self.component.min_value_changed_signal.connect(self.set_min_value)
    200         self.component.max_value_changed_signal.connect(self.set_max_value)
    201 
    202     def Close(self):
    203         super(IntegerEditField, self).Close()
    204         self.component.min_value_changed_signal.disconnect(self.set_min_value)
    205         self.component.max_value_changed_signal.disconnect(self.set_max_value)
    206 
    207     def ConstructBody(self):
    208         self.edit = SingleLineEditInteger(name=self.component.get_id(), parent=self, setvalue=self.component.get_value(), minValue=self.component.min_value, maxValue=self.component.max_value, align=self.align, hintText=self.component.placeholder, OnChange=self._OnValueChanged)
    209 
    210     def get_value(self):
    211         if not self.edit.text:
    212             return None
    213         return self.edit.GetValue()
    214 
    215     def set_value(self, value):
    216         self.edit.SetValue(value)
    217 
    218     def clear_value(self):
    219         self.edit.Clear()
    220 
    221     def get_min_value(self):
    222         return self.edit.minValue
    223 
    224     def get_max_value(self):
    225         return self.edit.maxValue
    226 
    227     def set_min_value(self, value):
    228         self.edit.SetMinValue(value)
    229         if self.get_value() < value:
    230             self.set_value(value)
    231 
    232     def set_max_value(self, value):
    233         self.edit.SetMaxValue(value)
    234         if self.get_value() > value:
    235             self.set_value(value)
    236 
    237 
    238 class HorizontalIntegerField(IntegerEditField):
    239     default_align = Align.TOPLEFT
    240     default_alignMode = None
    241     default_padTop = 0
    242     default_padBottom = 0
    243 
    244     def ConstructLabel(self, parent):
    245         self.label = TextDetail(parent=parent, align=self.align, text=self._get_label_text(), color=TextColor.SECONDARY)
    246 
    247     def ConstructBody(self):
    248         header_width = self.header_container.GetAutoSize()[0]
    249         self.edit = SingleLineEditInteger(name=self.component.get_id(), parent=self, setvalue=self.component.get_value(), minValue=self.component.min_value, maxValue=self.component.max_value, align=self.align, hintText=self.component.placeholder, OnChange=self._OnValueChanged, padLeft=header_width + 16, width=180)
    250         self.SetSizeAutomatically()
    251 
    252 
    253 class FormFieldWithHorizontalBody(FormField):
    254     default_padTop = 8
    255     default_padBottom = 16
    256 
    257     def ConstructLayout(self):
    258         BaseField.ConstructLayout(self)
    259 
    260     def ConstructBody(self):
    261         body_container = ContainerAutoSize(name='body_container', parent=self, align=Align.TOTOP)
    262         fields = []
    263         padLeft = 0
    264         for component in self.component.get_components():
    265             component_container = ContainerAutoSize(name='component_container', parent=body_container, align=Align.TOPLEFT, padLeft=padLeft)
    266             field = ConstructField(component_container, component)
    267             fields.append(field)
    268             component_container.SetSizeAutomatically()
    269             padLeft += component_container.width + 24
    270 
    271         self.component.on_components_changed.connect(self.OnComponentsChanged)
    272         body_container.SetSizeAutomatically()
    273         self.SetSizeAutomatically()
    274         total_height = body_container.height
    275         for field in fields:
    276             field.label.padTop = max(0, (total_height - field.label.height) / 2)
    277 
    278 
    279 class FloatEditField(BaseField):
    280 
    281     def ConstructBody(self):
    282         self.edit = SingleLineEditFloat(name=self.component.get_id(), parent=self, setvalue=self.component.get_value(), minValue=self.component.min_value, maxValue=self.component.max_value, align=Align.TOTOP, OnChange=self._OnValueChanged, decimalPlaces=2)
    283 
    284     def get_value(self):
    285         return self.edit.GetValue()
    286 
    287     def set_value(self, value):
    288         self.edit.SetValue(value)
    289 
    290 
    291 class CheckboxField(BaseField):
    292 
    293     def ConstructLayout(self):
    294         self.ConstructHint()
    295         self.checkbox = Checkbox(name=self.component.get_id(), parent=self, checked=self.component.get_value(), text=self.component.get_label(), callback=self._OnValueChanged)
    296 
    297     def get_value(self):
    298         return self.checkbox.GetValue()
    299 
    300     def set_value(self, value):
    301         self.checkbox.checked = value
    302 
    303     def ConstructHint(self, parent = None):
    304         if self.component.hint:
    305             InfoIcon(parent=self, align=Align.CENTERRIGHT, hint=self.component.hint)
    306 
    307 
    308 class CheckBoxFieldButton(BaseField):
    309 
    310     def ConstructLayout(self):
    311         self.ConstructHint()
    312         self.button = ButtonIcon(parent=self, align=Align.CENTERRIGHT, width=20, height=20, iconSize=16, padLeft=-20, padRight=16, color=TextColor.SECONDARY, texturePath=eveicon.open_window, func=self.component.open_window_func, hint=self.component.open_window_hint)
    313         self.checkbox = Checkbox(name=self.component.get_id(), parent=self, checked=self.component.get_value(), text=self.component.get_label(), callback=self._OnValueChanged)
    314 
    315     def get_value(self):
    316         return self.checkbox.GetValue()
    317 
    318     def set_value(self, value):
    319         self.checkbox.checked = value
    320 
    321     def ConstructHint(self, parent = None):
    322         if self.component.hint:
    323             InfoIcon(parent=self, align=Align.CENTERRIGHT, hint=self.component.hint)
    324 
    325 
    326 class ComboField(BaseField):
    327 
    328     def ConstructBody(self):
    329         self.combo = Combo(name=self.component.get_id(), parent=self, align=Align.TOTOP, callback=self._OnValueChanged, options=self.component.options, select=self.component.get_value(), nothingSelectedText=self.component.placeholder)
    330 
    331     def get_value(self):
    332         return self.combo.GetValue()
    333 
    334     def set_value(self, value):
    335         self.combo.SelectItemByValue(value)
    336         if value is None:
    337             self.combo.SetNothingSelected()
    338 
    339 
    340 class EveTypeField(BaseField):
    341 
    342     def ConstructBody(self):
    343         type_id = self.component.get_value()
    344         completed_suggestion = ItemTypeSuggestion(type_id=type_id) if type_id else None
    345         self.edit = ItemTypeField(parent=self, align=Align.TOTOP, placeholder=self.component.placeholder, show_suggestions_on_focus=True, completed_suggestion=completed_suggestion, type_filter=self.component.type_filter)
    346         self.edit.bind(completed_suggestion=self._OnValueChanged)
    347 
    348     def get_value(self):
    349         selected = self.edit.completed_suggestion
    350         if selected:
    351             return selected.type_id
    352 
    353     def set_value(self, value):
    354         pass
    355 
    356 
    357 class MixedHierarchyItemFormField(BaseField):
    358 
    359     def ConstructBody(self):
    360         completed_suggestion = self._get_completed_suggestion()
    361         self.edit = eveui.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, completed_suggestion=completed_suggestion, include_type=self.component.include_type, include_group=self.component.include_group, include_category=self.component.include_category)
    362         self.edit.bind(completed_suggestion=self._OnValueChanged)
    363 
    364     def _get_completed_suggestion(self):
    365         value = self.component.get_value()
    366         if not value:
    367             return
    368         try:
    369             item_type, item = value[0]
    370         except TypeError:
    371             return
    372 
    373         if item_type == 'item_type':
    374             return ItemTypeSuggestion(type_id=item)
    375         if item_type == 'item_group':
    376             return ItemGroupSuggestion(group_id=item)
    377         if item_type == 'item_category':
    378             return ItemCategorySuggestion(category_id=item)
    379 
    380     def get_value(self):
    381         suggestion = self.edit.completed_suggestion
    382         if isinstance(suggestion, ItemTypeSuggestion):
    383             return ('item_type', suggestion.type_id)
    384         if isinstance(suggestion, ItemGroupSuggestion):
    385             return ('item_group', suggestion.group_id)
    386         if isinstance(suggestion, ItemCategorySuggestion):
    387             return ('item_category', suggestion.category_id)
    388 
    389     def set_value(self, value):
    390         pass
    391 
    392 
    393 class SolarSystemField(BaseField):
    394 
    395     def ConstructBody(self):
    396         location_id = self.component.get_value()
    397         completed_suggestion = LocationSuggestion(location_id=location_id) if location_id else None
    398         self.edit = eveui.SolarSystemField(parent=self, align=Align.TOTOP, placeholder=self.component.placeholder, show_suggestions_on_focus=True, completed_suggestion=completed_suggestion, location_id=location_id)
    399         self.edit.bind(completed_suggestion=self._OnValueChanged)
    400 
    401     def get_value(self):
    402         selected = self.edit.completed_suggestion
    403         if selected:
    404             return selected.location_id
    405 
    406     def set_value(self, value):
    407         pass
    408 
    409 
    410 class NPCFactionField(BaseField):
    411 
    412     def ConstructBody(self):
    413         faction_id = self.component.get_value()
    414         completed_suggestion = FactionSuggestion(faction_id=faction_id) if faction_id else None
    415         self.edit = eveui.FactionField(parent=self, align=Align.TOTOP, placeholder=self.component.placeholder, show_suggestions_on_focus=True, completed_suggestion=completed_suggestion)
    416         self.edit.bind(completed_suggestion=self._OnValueChanged)
    417 
    418     def get_value(self):
    419         selected = self.edit.completed_suggestion
    420         if selected:
    421             return selected.faction_id
    422 
    423     def set_value(self, value):
    424         pass
    425 
    426 
    427 class PlayerCorporationField(BaseField):
    428 
    429     def ConstructBody(self):
    430         corp_id = self.component.get_value()
    431         completed_suggestion = OwnerIdentitySuggestion(owner_id=corp_id) if corp_id else None
    432         self.edit = eveui.PlayerCorporationField(parent=self, align=Align.TOTOP, placeholder=self.component.placeholder, show_suggestions_on_focus=True, completed_suggestion=completed_suggestion)
    433         self.edit.bind(completed_suggestion=self._OnValueChanged)
    434 
    435     def get_value(self):
    436         selected = self.edit.completed_suggestion
    437         if selected:
    438             return selected.corp_id
    439 
    440     def set_value(self, value):
    441         pass
    442 
    443 
    444 class NPCCorporationField(BaseField):
    445 
    446     def ConstructBody(self):
    447         corporation_id = self.component.get_value()
    448         completed_suggestion = NpcCorporationSuggestion(corporation_id=corporation_id) if corporation_id else None
    449         self.edit = eveui.NpcCorporationField(parent=self, align=Align.TOTOP, placeholder=self.component.placeholder, show_suggestions_on_focus=True, completed_suggestion=completed_suggestion, filter=self.component.filter)
    450         self.edit.bind(completed_suggestion=self._OnValueChanged)
    451 
    452     def get_value(self):
    453         selected = self.edit.completed_suggestion
    454         if selected:
    455             return selected.corporation_id
    456 
    457     def set_value(self, value):
    458         pass
    459 
    460 
    461 class OrganizationField(BaseField):
    462 
    463     def ConstructBody(self):
    464         owner_id = self.component.get_value()
    465         completed_suggestion = OwnerIdentitySuggestion(owner_id=owner_id) if owner_id else None
    466         self.edit = eveui.OrganizationField(parent=self, align=Align.TOTOP, placeholder=self.component.placeholder, show_suggestions_on_focus=True, completed_suggestion=completed_suggestion)
    467         self.edit.bind(completed_suggestion=self._OnValueChanged)
    468 
    469     def get_value(self):
    470         selected = self.edit.completed_suggestion
    471         if selected:
    472             return selected.owner_id
    473 
    474     def set_value(self, value):
    475         pass
    476 
    477 
    478 class PlayerOrPlayerOrganizationField(BaseField):
    479 
    480     def ConstructBody(self):
    481         owner_id = self.component.get_value()
    482         completed_suggestion = OwnerIdentitySuggestion(owner_id=owner_id) if owner_id else None
    483         self.edit = eveui.PlayerOrPlayerOrganizationField(parent=self, align=Align.TOTOP, placeholder=self.component.placeholder, show_suggestions_on_focus=True, completed_suggestion=completed_suggestion, include_character=self.component.include_character, include_corporation=self.component.include_corporation, include_alliance=self.component.include_alliance, include_faction=self.component.include_faction)
    484         self.edit.bind(completed_suggestion=self._OnValueChanged)
    485 
    486     def get_value(self):
    487         selected = self.edit.completed_suggestion
    488         if selected:
    489             return selected.owner_id
    490 
    491     def set_value(self, value):
    492         pass
    493 
    494 
    495 class ShipAndTreeGroupField(BaseField):
    496 
    497     def ConstructBody(self):
    498         type_id = self.component.get_value()
    499         completed_suggestion = ShipTypeSuggestion(type_id=type_id) if type_id else None
    500         self.edit = eveui.ShipField(parent=self, align=Align.TOTOP, placeholder=self.component.placeholder, show_suggestions_on_focus=True, completed_suggestion=completed_suggestion)
    501         self.edit.bind(completed_suggestion=self._OnValueChanged)
    502 
    503     def get_value(self):
    504         selected = self.edit.completed_suggestion
    505         if selected:
    506             return selected.owner_id
    507 
    508     def set_value(self, value):
    509         pass
    510 
    511 
    512 class DateTimeField(BaseField):
    513     default_clipChildren = True
    514     default_padTop = -8
    515 
    516     def _get_default_value(self):
    517         if self.component.duration_datetime:
    518             duration_in_seconds = self.component.duration_datetime.total_seconds()
    519             time_to_advance = relativedelta.relativedelta(seconds=duration_in_seconds)
    520         else:
    521             time_to_advance = relativedelta.relativedelta(weeks=1)
    522         now = datetime.datetime.utcnow()
    523         target_time = now + time_to_advance
    524         target_time_tuple = target_time.timetuple()
    525         return target_time_tuple
    526 
    527     def ConstructBody(self):
    528         if self.component.time_options is not None:
    529             self._buttons = []
    530             self._button_data = {}
    531             _flow = FlowContainer(parent=self, align=Align.TOTOP, height=24, contentSpacing=(8, 8))
    532             self._construct_buttons_from_options(_flow)
    533         date_container = ContainerAutoSize(name='date_container', parent=self, align=Align.TOTOP, alignMode=Align.TOTOP, top=32)
    534         set_picker_value = self._get_default_value()
    535         self.date_picker = DatePicker(name='datepicker', parent=date_container, align=Align.TOTOP, width=256, height=18)
    536         now = time.gmtime()
    537         self.date_picker.Startup(now=now, withTime=True, timeparts=self.component.timeparts, startYear=now[0], yearRange=self.component.year_range, set_value=set_picker_value)
    538         self.date_picker.OnDateChange = self._OnValueChanged
    539         self.duration_label = TextBody(name='duration_label', parent=self, align=Align.TOTOP, top=8)
    540         self._update_duration_label()
    541         uthread2.start_tasklet(self._time_remaining_routine)
    542 
    543     def _time_remaining_routine(self):
    544         while not self.destroyed:
    545             self._update_duration_label()
    546             uthread2.sleep(1)
    547 
    548     def _pre_set_from_timedelta(self):
    549         self.date_picker.SetValueFromNow(seconds=self.component.duration_datetime.total_seconds())
    550         self.component.on_field_value_changed(self.get_value())
    551 
    552     def _construct_buttons_from_options(self, parent):
    553         options = self.component.time_options
    554         for idx, option in enumerate(options):
    555             period, label, duration = self._parse_option(option)
    556             is_first = idx == 0
    557             is_last = idx == len(options) - 1
    558             self._add_button(parent, period, label, duration, is_first=is_first, is_last=is_last)
    559 
    560     def _parse_option(self, option):
    561         digits = ''
    562         period = ''
    563         for char in option:
    564             if char.isdigit():
    565                 digits = digits + char
    566             else:
    567                 char = char.lower()
    568                 if char not in ('h', 'd', 'w', 'm') or len(digits) == 0:
    569                     raise ValueError('Invalid time option %s', option)
    570                 period = char
    571                 break
    572 
    573         if len(digits) == 0 or len(period) == 0:
    574             raise ValueError('Invalid time option %s', option)
    575         duration = int(digits)
    576         label = ''
    577         if period == 'h':
    578             label = FormatSpecificDurationWritten(duration, type='hour')
    579         elif period == 'd':
    580             label = FormatSpecificDurationWritten(duration, type='day')
    581         elif period == 'w':
    582             label = FormatSpecificDurationWritten(duration, type='week')
    583         elif period == 'm':
    584             label = FormatSpecificDurationWritten(duration, type='month')
    585         return (period, label, duration)
    586 
    587     def _add_button(self, parent, period, label, duration, is_first, is_last):
    588         if is_first:
    589             frame_type = ButtonFrameType.CUT_BOTTOM_LEFT
    590         elif is_last:
    591             frame_type = ButtonFrameType.CUT_BOTTOM_RIGHT
    592         else:
    593             frame_type = ButtonFrameType.RECTANGLE
    594         button = Button(parent=parent, label=label, func=self._button_clicked, variant=ButtonVariant.GHOST, density=Density.COMPACT, frame_type=frame_type)
    595         self._buttons.append(button)
    596         self._button_data[button] = (period, duration)
    597 
    598     def _deselect_buttons(self):
    599         for button in self._buttons:
    600             if button.variant == ButtonVariant.PRIMARY:
    601                 button.variant = ButtonVariant.GHOST
    602 
    603     def _button_clicked(self, component):
    604         if component is None:
    605             return
    606         for button in self._buttons:
    607             if component == button:
    608                 button.variant = ButtonVariant.PRIMARY
    609             elif button.variant == ButtonVariant.PRIMARY:
    610                 button.variant = ButtonVariant.GHOST
    611 
    612         period, duration = self._button_data[component]
    613         if period == 'h':
    614             self.date_picker.SetValueFromNow(hours=duration)
    615         elif period == 'd':
    616             self.date_picker.SetValueFromNow(days=duration)
    617         elif period == 'w':
    618             self.date_picker.SetValueFromNow(weeks=duration)
    619         elif period == 'm':
    620             self.date_picker.SetValueFromNow(months=duration)
    621         self._update_duration_label()
    622         self.component.on_field_value_changed(self.get_value())
    623 
    624     def _is_valid_duration(self):
    625         if self.date_picker.HasDatePassed():
    626             return False
    627         delta = self.date_picker.GetDurationFromNow()
    628         if delta.months is 0 and delta.days is 0 and delta.hours is 0 and delta.minutes < 15:
    629             return False
    630         if delta.years is 0:
    631             return True
    632         if delta.years is 1 and delta.days is 0:
    633             date_now = date.today()
    634             date_in_picker = self.date_picker.GetValueAsDateTime()
    635             if date_now.day is date_in_picker.day and date_now.month is date_in_picker.month:
    636                 return True
    637         return False
    638 
    639     def _update_duration_label(self):
    640         written_form = self.date_picker.GetDurationFromNowString(show_from='day')
    641         if written_form:
    642             label = u'{} {}'.format(GetByLabel('UI/Corporations/Goals/ChosenDuration'), written_form)
    643         else:
    644             label = GetByLabel('UI/Corporations/Goals/DurationInPast')
    645         self.duration_label.text = label
    646         if self._is_valid_duration() and self.duration_label.color is not TextColor.NORMAL:
    647             self.duration_label.color = TextColor.NORMAL
    648         elif self.duration_label.color is not TextColor.DANGER:
    649             self.duration_label.color = TextColor.DANGER
    650 
    651     def _OnValueChanged(self, *args):
    652         super(DateTimeField, self)._OnValueChanged(*args)
    653         self._deselect_buttons()
    654         self._update_duration_label()
    655 
    656     def get_value(self):
    657         return self.date_picker.GetValue()
    658 
    659     def set_value(self, value):
    660         self.date_picker.SetValue(value)
    661 
    662 
    663 class Divider(BaseField):
    664 
    665     def ConstructLayout(self):
    666         Line(parent=self, align=Align.TOTOP, weight=1, opacity=0.2)
    667 
    668     def get_value(self):
    669         return None
    670 
    671     def set_value(self, value):
    672         pass
    673 
    674 
    675 class AccessListField(BaseField):
    676     default_padTop = -8
    677 
    678     def ConstructBody(self):
    679         self.combo = Combo(name=self.component.get_id(), parent=self, align=Align.TOTOP, callback=self._OnValueChanged, options=self.component.options, select=self.component.get_value(), nothingSelectedText=self.component.placeholder)
    680 
    681     def ConstructHeader(self):
    682         pass
    683 
    684     def ConstructIcon(self, parent):
    685         pass
    686 
    687     def ConstructLabel(self, parent):
    688         pass
    689 
    690     def ConstructHint(self, parent = None):
    691         pass
    692 
    693     def get_value(self):
    694         return self.combo.GetValue()
    695 
    696     def set_value(self, value):
    697         if not value:
    698             self.combo.SetNothingSelected()
    699         else:
    700             self.combo.SelectItemByValue(value)
    701 
    702 
    703 def ConstructFields(parent, form):
    704     for component in form.get_components():
    705         ConstructField(parent, component)
    706 
    707 
    708 def ConstructField(parent, component, **kwargs):
    709     field_cls = GetFieldClass(component)
    710     return field_cls(name='{}_{}'.format(field_cls.__name__, component.__class__.__name__), parent=parent, component=component, **kwargs)
    711 
    712 
    713 def GetFieldClass(component):
    714     if component.field_cls:
    715         return component.field_cls
    716     if isinstance(component, Form):
    717         return FormField
    718     if isinstance(component, formComponent.Boolean):
    719         return CheckboxField
    720     if isinstance(component, formComponent.Text):
    721         return EditField
    722     if isinstance(component, formComponent.TextMultiLine):
    723         return EditFieldMultiLine
    724     if isinstance(component, formComponent.Integer):
    725         return IntegerEditField
    726     if isinstance(component, formComponent.Float):
    727         return FloatEditField
    728     if isinstance(component, formComponent.Enum):
    729         return ComboField
    730     if isinstance(component, formComponent.EveType):
    731         return EveTypeField
    732     if isinstance(component, formComponent.SolarSystem):
    733         return SolarSystemField
    734     if isinstance(component, formComponent.NPCFaction):
    735         return NPCFactionField
    736     if isinstance(component, formComponent.PlayerCorporation):
    737         return PlayerCorporationField
    738     if isinstance(component, formComponent.NPCCorporation):
    739         return NPCCorporationField
    740     if isinstance(component, formComponent.PlayerOrPlayerOrganization):
    741         return PlayerOrPlayerOrganizationField
    742     if isinstance(component, formComponent.Label):
    743         return Label
    744     if isinstance(component, formComponent.Divider):
    745         return Divider
    746     if isinstance(component, formComponent.DateTime):
    747         return DateTimeField
    748     if isinstance(component, formComponent.MixedHierarchy):
    749         return MixedHierarchyItemFormField
    750     if isinstance(component, formComponent.AccessList):
    751         return AccessListField
    752     if isinstance(component, formComponent.BooleanButton):
    753         return CheckBoxFieldButton
    754     raise ValueError('Unknown component', repr(component))