edit_window.py (16579B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\systemMenu\theme\edit_window.py 2 import carbonui 3 import carbonui.text.styles 4 import eveicon 5 import localization 6 import signals 7 import trinity 8 from carbonui import Align, ButtonVariant, uiconst 9 from carbonui.button.group import ButtonGroup, ButtonSizeMode 10 from carbonui.control.button import Button 11 from carbonui.control.singlelineedits.singleLineEditText import SingleLineEditText 12 from carbonui.control.window import Window 13 from carbonui.primitives.container import Container 14 from carbonui.primitives.containerAutoSize import ContainerAutoSize 15 from carbonui.primitives.frame import Frame 16 from carbonui.primitives.layoutGrid import LayoutGrid 17 from carbonui.primitives.sprite import Sprite 18 from carbonui.uianimations import animations 19 from carbonui.uicore import uicore 20 from eve.client.script.ui import eveColor 21 from eve.client.script.ui.control import eveLabel 22 from eve.client.script.ui.control.infoIcon import InfoGlyphIcon 23 from eve.client.script.ui.shared.colorThemes import ColorTheme 24 from eve.client.script.ui.shared.systemMenu.theme import theme_const 25 from carbonui.control.color_picker import ColorField 26 from eve.client.script.ui.shared.systemMenu.theme.confirm import confirm_discard_unsaved_color_theme_changes 27 from eve.client.script.ui.shared.systemMenu.theme.model import focus_dark_from_focus 28 from eve.client.script.ui.shared.systemMenu.theme.share import copy_theme_slug_to_clipboard, format_theme_slug, InvalidThemeSlug, parse_theme_slug 29 30 class ThemeEditState(object): 31 32 def __init__(self, theme): 33 self._initial_theme = theme 34 self._theme_id = theme.id 35 self._name = theme.name 36 self._focus = theme.focus 37 self._accent = theme.accent 38 self._alert = theme.alert 39 self._tint = theme.tint 40 self.on_theme_changed = signals.Signal() 41 self.on_name_changed = signals.Signal() 42 self.on_focus_color_changed = signals.Signal() 43 self.on_focus_dark_color_changed = signals.Signal() 44 self.on_accent_color_changed = signals.Signal() 45 self.on_tint_color_changed = signals.Signal() 46 self.on_alert_color_changed = signals.Signal() 47 self.on_theme_slug_changed = signals.Signal() 48 49 @property 50 def has_unsaved_changes(self): 51 return self._initial_theme != self.theme 52 53 @property 54 def theme(self): 55 return ColorTheme(theme_id=self._theme_id, name=self._name, focus=self._focus, focus_dark=self.focus_dark_color, accent=self._accent, alert=self._alert, tint=self._tint) 56 57 @property 58 def name(self): 59 return self._name 60 61 @name.setter 62 def name(self, value): 63 if self._name != value: 64 self._name = value 65 self.on_name_changed(self) 66 67 @property 68 def focus_color(self): 69 return self._focus 70 71 @focus_color.setter 72 def focus_color(self, value): 73 if self._focus != value: 74 self._focus = value 75 self.on_focus_color_changed(self) 76 self.on_focus_dark_color_changed(self) 77 self.on_theme_slug_changed(self) 78 self.on_theme_changed(self) 79 80 @property 81 def focus_dark_color(self): 82 return focus_dark_from_focus(self._focus) 83 84 @property 85 def accent_color(self): 86 return self._accent 87 88 @accent_color.setter 89 def accent_color(self, value): 90 if self._accent != value: 91 self._accent = value 92 self.on_accent_color_changed(self) 93 self.on_theme_slug_changed(self) 94 self.on_theme_changed(self) 95 96 @property 97 def tint_color(self): 98 return self._tint 99 100 @tint_color.setter 101 def tint_color(self, value): 102 if self._tint != value: 103 self._tint = value 104 self.on_tint_color_changed(self) 105 self.on_theme_slug_changed(self) 106 self.on_theme_changed(self) 107 108 @property 109 def alert_color(self): 110 return self._alert 111 112 @alert_color.setter 113 def alert_color(self, value): 114 if self._alert != value: 115 self._alert = value 116 self.on_alert_color_changed(self) 117 self.on_theme_slug_changed(self) 118 self.on_theme_changed(self) 119 120 @property 121 def theme_slug(self): 122 return format_theme_slug(focus=self.focus_color, accent=self.accent_color, tint=self.tint_color, alert=self.alert_color) 123 124 def apply_theme_slug(self, slug): 125 focus, accent, tint, alert = parse_theme_slug(slug) 126 self.focus_color = focus 127 self.accent_color = accent 128 self.tint_color = tint 129 self.alert_color = alert 130 131 132 class ThemeEditWindow(Window): 133 default_windowID = 'theme_edit_window' 134 default_isLightBackgroundConfigurable = False 135 default_isLockable = False 136 default_isMinimizable = False 137 default_isOverlayable = False 138 default_isCollapseable = False 139 default_captionLabelPath = 'UI/SystemMenu/GeneralSettings/ColorTheme/ThemeEditWindowCaption' 140 default_iconNum = 'res:/UI/Texture/WindowIcons/color_picker.png' 141 _content_wrap = None 142 _theme_slug_input = None 143 144 def __init__(self, theme, on_theme_changed = None, **kwargs): 145 self.on_theme_changed = signals.Signal() 146 self._theme_edit_state = ThemeEditState(theme) 147 super(ThemeEditWindow, self).__init__(**kwargs) 148 self.MakeUnResizeable() 149 self._layout() 150 self._theme_edit_state.on_focus_color_changed.connect(self._on_focus_color_changed) 151 self._theme_edit_state.on_accent_color_changed.connect(self._on_accent_color_changed) 152 self._theme_edit_state.on_tint_color_changed.connect(self._on_tint_color_changed) 153 self._theme_edit_state.on_alert_color_changed.connect(self._on_alert_color_changed) 154 self._theme_edit_state.on_theme_changed.connect(self._on_theme_changed) 155 self._theme_edit_state.on_theme_slug_changed.connect(self._on_theme_slug_changed) 156 if on_theme_changed is not None: 157 self.on_theme_changed.connect(on_theme_changed) 158 159 @property 160 def theme(self): 161 return self._theme_edit_state.theme 162 163 def _on_theme_changed(self, theme_edit_state): 164 self.on_theme_changed(self) 165 166 def _layout(self): 167 self._content_wrap = ContainerAutoSize(parent=self.content, callback=self._on_content_wrap_size_changed, only_use_callback_when_size_changes=True) 168 self._content_wrap.DisableAutoSize() 169 grid = LayoutGrid(parent=self._content_wrap, align=Align.TOPLEFT, columns=2, cellSpacing=(16, 16)) 170 name_cont = ContainerAutoSize(align=Align.TOTOP) 171 grid.AddCell(name_cont, colSpan=2) 172 eveLabel.EveLabelMedium(parent=name_cont, align=Align.TOTOP, text=localization.GetByLabel('UI/SystemMenu/GeneralSettings/ColorTheme/ThemeNameFieldLabel')) 173 SingleLineEditText(parent=name_cont, align=Align.TOTOP, top=4, maxLength=64, setvalue=self._theme_edit_state.name, OnChange=self._on_name_input_changed) 174 primary_grid = LayoutGrid(parent=grid, align=Align.TOPLEFT, columns=1, cellSpacing=(0, 4)) 175 eveLabel.EveLabelMedium(parent=primary_grid, align=Align.TOPLEFT, text=localization.GetByLabel('UI/SystemMenu/GeneralSettings/ColorTheme/PrimaryColorFieldLabel')) 176 self._focus_color_picker = ColorField(parent=primary_grid, align=Align.TOPLEFT, color=self._theme_edit_state.focus_color, on_color_changed=self._on_focus_color_picked, brightness_range=(theme_const.FOREGROUND_MIN_BRIGHTNESS, 1.0)) 177 accent_grid = LayoutGrid(parent=grid, align=Align.TOPLEFT, columns=1, cellSpacing=(0, 4)) 178 eveLabel.EveLabelMedium(parent=accent_grid, align=Align.TOPLEFT, text=localization.GetByLabel('UI/SystemMenu/GeneralSettings/ColorTheme/AccentColorFieldLabel')) 179 self._accent_color_picker = ColorField(parent=accent_grid, align=Align.TOPLEFT, color=self._theme_edit_state.accent_color, on_color_changed=self._on_accent_color_picked, brightness_range=(theme_const.FOREGROUND_MIN_BRIGHTNESS, 1.0)) 180 tint_grid = LayoutGrid(parent=grid, align=Align.TOPLEFT, columns=1, cellSpacing=(0, 4)) 181 eveLabel.EveLabelMedium(parent=tint_grid, align=Align.TOPLEFT, text=localization.GetByLabel('UI/SystemMenu/GeneralSettings/ColorTheme/TintColorFieldLabel')) 182 self._tint_color_picker = ColorField(parent=tint_grid, align=Align.TOPLEFT, color=self._theme_edit_state.tint_color, on_color_changed=self._on_tint_color_picked, brightness_range=(0.0, theme_const.BACKGROUND_MAX_BRIGHTNESS), ignore_color_blind_mode=True) 183 alert_grid = LayoutGrid(parent=grid, align=Align.TOPLEFT, columns=1, cellSpacing=(0, 4)) 184 eveLabel.EveLabelMedium(parent=alert_grid, align=Align.TOPLEFT, text=localization.GetByLabel('UI/SystemMenu/GeneralSettings/ColorTheme/AlertColorFieldLabel')) 185 self._alert_color_picker = ColorField(parent=alert_grid, align=Align.TOPLEFT, color=self._theme_edit_state.alert_color, on_color_changed=self._on_alert_color_picked, brightness_range=(theme_const.FOREGROUND_MIN_BRIGHTNESS, 1.0)) 186 share_cont = ContainerAutoSize(align=Align.TOTOP, alignMode=Align.TOTOP, top=16) 187 grid.AddCell(share_cont, colSpan=2) 188 Button(parent=ContainerAutoSize(parent=share_cont, align=Align.TORIGHT, padLeft=8), align=Align.TOPLEFT, hint=localization.GetByLabel('UI/Common/Copy'), texturePath=eveicon.copy, func=self._on_copy_theme_slug, args=()) 189 self._theme_slug_input = SingleLineEditText(parent=share_cont, align=Align.TOTOP, setvalue=self._theme_edit_state.theme_slug, autoselect=True, OnChange=self._on_theme_slug_edited, OnFocusLost=self._on_theme_slug_focus_lost) 190 self._theme_slug_input.SetHistoryVisibility(False) 191 InfoGlyphIcon(parent=ContainerAutoSize(parent=self._theme_slug_input, align=Align.TORIGHT, left=8, idx=0), align=Align.CENTER, state=uiconst.UI_NORMAL, hint=localization.GetByLabel('UI/SystemMenu/GeneralSettings/ColorTheme/ShareHint')) 192 group = ButtonGroup(align=Align.TOTOP, button_size_mode=ButtonSizeMode.STRETCH, top=16) 193 grid.AddCell(group, colSpan=2) 194 Button(parent=group, label=localization.GetByLabel('UI/Common/Buttons/Apply'), func=self._on_apply, args=(), variant=ButtonVariant.PRIMARY) 195 Button(parent=group, label=localization.GetByLabel('UI/SystemMenu/GeneralSettings/ColorTheme/Save'), func=self._on_save, args=()) 196 Button(parent=group, label=localization.GetByLabel('UI/Common/Cancel'), func=self._on_cancel, args=()) 197 self._content_wrap.EnableAutoSize() 198 199 def _on_apply(self): 200 self.SetModalResult(uiconst.ID_APPLY) 201 202 def _on_save(self): 203 self.SetModalResult(uiconst.ID_OK) 204 205 def _on_cancel(self): 206 if self._confirm_discard_changes_if_any(): 207 self.SetModalResult(uiconst.ID_CANCEL) 208 209 def _on_copy_theme_slug(self): 210 copy_theme_slug_to_clipboard(self._theme_edit_state.theme_slug) 211 212 def CloseByUser(self, *args): 213 if self._confirm_discard_changes_if_any(): 214 super(ThemeEditWindow, self).CloseByUser() 215 216 def DeactivateUnderlay(self): 217 pass 218 219 def OnWindowAboveSetInactive(self): 220 pass 221 222 def _confirm_discard_changes_if_any(self): 223 if self._theme_edit_state.has_unsaved_changes: 224 return confirm_discard_unsaved_color_theme_changes() 225 else: 226 return True 227 228 def _on_name_input_changed(self, text): 229 self._theme_edit_state.name = text 230 231 def _on_focus_color_changed(self, theme_edit_state): 232 self._focus_color_picker.color = theme_edit_state.focus_color 233 234 def _on_focus_color_picked(self, color_picker): 235 self._theme_edit_state.focus_color = color_picker.color 236 237 def _on_accent_color_changed(self, theme_edit_state): 238 self._accent_color_picker.color = theme_edit_state.accent_color 239 240 def _on_accent_color_picked(self, color_picker): 241 self._theme_edit_state.accent_color = color_picker.color 242 243 def _on_tint_color_changed(self, theme_edit_state): 244 self._tint_color_picker.color = theme_edit_state.tint_color 245 246 def _on_tint_color_picked(self, color_picker): 247 self._theme_edit_state.tint_color = color_picker.color 248 249 def _on_alert_color_changed(self, theme_edit_state): 250 self._alert_color_picker.color = theme_edit_state.alert_color 251 252 def _on_alert_color_picked(self, color_picker): 253 self._theme_edit_state.alert_color = color_picker.color 254 255 def _on_theme_slug_changed(self, theme_edit_state): 256 focused = uicore.registry.GetFocus() == self._theme_slug_input 257 if not focused: 258 self._theme_slug_input.SetText(theme_edit_state.theme_slug) 259 260 def _on_theme_slug_edited(self, text): 261 try: 262 self._theme_edit_state.apply_theme_slug(text) 263 except InvalidThemeSlug: 264 pass 265 266 def _on_theme_slug_focus_lost(self, input): 267 try: 268 self._theme_edit_state.apply_theme_slug(input.GetValue()) 269 except InvalidThemeSlug: 270 pass 271 272 self._theme_slug_input.SetText(self._theme_edit_state.theme_slug) 273 274 def _on_content_wrap_size_changed(self): 275 self.width, self.height = self.GetWindowSizeForContentSize(width=self._content_wrap.width, height=self._content_wrap.height) 276 277 def _create_modal_layer(self, name, close_when_clicked = False): 278 return ModalLayer(name=name, parent=uicore.layer.modal) 279 280 281 def edit_custom_color_theme(theme, ui_color_service): 282 if theme.is_preset: 283 raise ValueError("Can't edit preset themes") 284 285 def on_theme_changed(theme_edit_window): 286 ui_color_service.SetThemeOverride(theme_edit_window.theme) 287 288 ui_color_service.SetThemeOverride(theme) 289 try: 290 window = ThemeEditWindow.Open(theme=theme, on_theme_changed=on_theme_changed) 291 result = window.ShowDialog(modal=True, fillOpacity=0.0, sceneSaturation=1.0) 292 if result == uiconst.ID_APPLY: 293 ui_color_service.SaveCustomTheme(window.theme) 294 ui_color_service.SetThemeID(window.theme.id) 295 elif result == uiconst.ID_OK: 296 ui_color_service.SaveCustomTheme(window.theme) 297 finally: 298 ui_color_service.ClearThemeOverride() 299 300 301 class ModalLayer(Container): 302 _frame = None 303 _header = None 304 _hatch = None 305 HATCH_SIZE = 264 306 HATCH_SIZE_HALF = HATCH_SIZE / 2.0 307 308 def __init__(self, name, parent): 309 super(ModalLayer, self).__init__(name=name, parent=parent, align=Align.TOALL, state=uiconst.UI_NORMAL, idx=0) 310 self._frame = Frame(parent=self, align=Align.TOALL, frameConst=uiconst.FRAME_BORDER2_CORNER0, color=eveColor.WARNING_ORANGE) 311 self._header = carbonui.text.styles.TextHeadline(parent=self, align=Align.CENTERTOP, top=16, text=localization.GetByLabel('UI/SystemMenu/GeneralSettings/ColorTheme/ColorThemePreviewModeCaption'), color=eveColor.WARNING_ORANGE) 312 self._hatch = Sprite(parent=self, align=Align.TOPLEFT, state=uiconst.UI_DISABLED, width=264, height=264, texturePath='res:/UI/Texture/classes/ColorPicker/hatch.png', textureSecondaryPath='res:/UI/Texture/classes/ColorPicker/hatch_mask.png', spriteEffect=trinity.TR2_SFX_MODULATE, outputMode=uiconst.OUTPUT_COLOR_AND_GLOW, color=eveColor.WARNING_ORANGE, opacity=0.15, glowBrightness=0.1) 313 self._update_hatch_position() 314 uicore.uilib.RegisterForTriuiEvents(uiconst.UI_MOUSEMOVE, self._on_global_mouse_move) 315 316 def _on_global_mouse_move(self, *args): 317 if not self.destroyed and self._hatch: 318 self._update_hatch_position() 319 return True 320 else: 321 return False 322 323 def _update_hatch_position(self): 324 self._hatch.left = uicore.uilib.x - self.HATCH_SIZE_HALF 325 self._hatch.top = uicore.uilib.y - self.HATCH_SIZE_HALF 326 self._hatch.translationPrimary = (uicore.uilib.x % 11 / float(self.HATCH_SIZE), uicore.uilib.y % 11 / float(self.HATCH_SIZE)) 327 328 def _AppendChildRO(self, child): 329 super(ModalLayer, self)._AppendChildRO(child) 330 if self._hatch and child is not self._hatch: 331 self._hatch.SetOrder(-1) 332 333 def _InsertChildRO(self, idx, child): 334 super(ModalLayer, self)._InsertChildRO(idx, child) 335 if self._hatch and child is not self._hatch: 336 self._hatch.SetOrder(-1) 337 338 def OnMouseDown(self, *args): 339 for element in (self._frame, self._header): 340 if element: 341 animations.SpColorMorphTo(element, startColor=eveColor.WARNING_ORANGE, endColor=eveColor.WHITE, duration=0.4, loops=3, curveType=uiconst.ANIM_WAVE) 342 343 if self._hatch: 344 animations.FadeTo(self._hatch, startVal=0.3, endVal=0.15, duration=0.3)