panel.py (10229B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\dynamicresources\client\ess\bracket\panel.py 2 import abc 3 import datetime 4 import eveui 5 import gametime 6 import signals 7 import threadutils 8 from carbonui import uiconst 9 from carbonui.uicore import uicore 10 from eveui import Rect 11 from dynamicresources.client.ess.bracket.tracker import TransformTracker 12 13 class BracketPanel(object): 14 __metaclass__ = abc.ABCMeta 15 16 def __init__(self, transform, layer, camera = None, clipping_dead_zone = 0, collapse_at_camera_distance = None, name = None, on_focus = None, collapsed = False): 17 self._transform = transform 18 self._layer = layer 19 self._camera = camera 20 self._clipping_dead_zone = clipping_dead_zone 21 self._collapse_at_camera_distance = collapse_at_camera_distance 22 self._is_forced_expanded = False 23 self._is_collapsed = collapsed 24 self.is_locked = False 25 self._is_closed = False 26 self._focused = False 27 self._tracker = TransformTracker(name=name, parent=layer, transform=transform, curve_set=uicore.uilib.bracketCurveSet, tracker_parent=uicore.layer.inflight) 28 self._state_requests = {} 29 self._next_state_request_token_id = 1 30 self.on_focus = signals.Signal() 31 if on_focus: 32 self.on_focus.connect(on_focus) 33 self._clipped_state_token = None 34 self._zoom_state_token = None 35 self._interaction_state_token = None 36 self._focus_state_token = None 37 self._was_clipped = None 38 self._start_frame_update_loop() 39 40 @property 41 def is_closed(self): 42 return self._is_closed 43 44 @property 45 def tracker(self): 46 return self._tracker 47 48 @property 49 def transform(self): 50 return self._transform 51 52 @property 53 def layer(self): 54 return self._layer 55 56 @property 57 def clipping_bounds(self): 58 return get_absolute_bounds(self._layer) 59 60 @property 61 def is_collapsed(self): 62 return self._is_collapsed 63 64 @property 65 def is_expanded(self): 66 return not self._is_collapsed 67 68 @property 69 def can_expand(self): 70 return self._is_collapsed and self._focus_state_token is None and not self._was_clipped 71 72 @property 73 def focused(self): 74 return self._focused 75 76 @focused.setter 77 def focused(self, focused): 78 if focused != self._focused: 79 self._focused = focused 80 self.on_focus(focused) 81 82 def close(self): 83 self._tracker.close() 84 self._is_closed = True 85 86 def focus(self): 87 self.focused = True 88 89 def unfocus(self): 90 self.focused = False 91 92 @threadutils.threaded 93 def _start_frame_update_loop(self): 94 collapse_at_cursor_distance = 150.0 95 cursor_distance_grace_period = datetime.timedelta(seconds=1.0) 96 cursor_last_within_distance_at = None 97 while not self._is_closed: 98 clipping_bounds = self.clipping_bounds 99 content_bounds = self.content_bounds 100 if self._is_forced_expanded: 101 cursor_distance = content_bounds.distance_from_point(*eveui.Mouse.position()) 102 if cursor_distance < collapse_at_cursor_distance: 103 cursor_last_within_distance_at = gametime.now() 104 else: 105 if cursor_last_within_distance_at is None: 106 cursor_last_within_distance_at = gametime.now() 107 dt = gametime.now() - cursor_last_within_distance_at 108 if dt >= cursor_distance_grace_period: 109 self._is_forced_expanded = False 110 if not self._is_forced_expanded and self._interaction_state_token is not None: 111 self._interaction_state_token.clear() 112 self._interaction_state_token = None 113 if self._was_clipped is not None: 114 dead_zone_sign = 1 if self._was_clipped else -1 115 dead_zone_delta = dead_zone_sign * (self._clipping_dead_zone / 2.0) 116 content_bounds = content_bounds.inflate(dead_zone_delta) 117 if content_bounds.is_empty: 118 is_enveloped = clipping_bounds.contains(*content_bounds.center) 119 else: 120 is_enveloped = clipping_bounds.envelops(content_bounds) 121 if is_enveloped and (self._was_clipped is None or self._was_clipped): 122 self._was_clipped = False 123 if self._clipped_state_token is not None: 124 self._clipped_state_token.clear() 125 self._clipped_state_token = None 126 elif not is_enveloped and not self._was_clipped: 127 self._was_clipped = True 128 if self._clipped_state_token is None: 129 self._clipped_state_token = self.request_collapse(Priority.clipped) 130 if self._collapse_at_camera_distance is not None and self._camera.is_valid: 131 camera_distance = self._camera.distance_from_transform(self._transform) 132 out_of_range = camera_distance > self._collapse_at_camera_distance 133 if out_of_range and self._zoom_state_token is None: 134 self._zoom_state_token = self.request_collapse(Priority.zoomed_out) 135 elif not out_of_range and self._zoom_state_token is not None: 136 self._zoom_state_token.clear() 137 self._zoom_state_token = None 138 should_expand, should_lock = self._should_be_expanded() 139 if self._is_collapsed and should_expand: 140 self.expand() 141 self.is_locked = False 142 self._is_collapsed = False 143 elif not self._is_collapsed and not should_expand: 144 self.collapse(lock=should_lock) 145 self.is_locked |= should_lock 146 self._is_collapsed = True 147 elif self._is_collapsed and not should_expand and not self.is_locked and should_lock: 148 self.collapse(lock=should_lock) 149 self._is_collapsed = True 150 self.is_locked = True 151 elif self._is_collapsed and not should_expand and self.is_locked and not should_lock: 152 self.collapse(lock=should_lock) 153 self._is_collapsed = True 154 self.is_locked = False 155 self._update_interaction_point_in_frame() 156 eveui.wait_for_next_frame() 157 158 def request_collapse(self, priority = 0, lock = False): 159 return self._allocate_state_request(priority, expand=False, lock=lock) 160 161 def request_expand(self, priority = 0): 162 return self._allocate_state_request(priority, expand=True) 163 164 def _allocate_state_request(self, priority, expand, lock = False): 165 token_id = self._next_state_request_token_id 166 self._next_state_request_token_id += 1 167 self._state_requests[token_id] = StateRequest(priority, expand, lock) 168 return StateRequestToken(token_id, on_clear=self._clear_state_request) 169 170 def _clear_state_request(self, token_id): 171 try: 172 del self._state_requests[token_id] 173 except KeyError: 174 pass 175 176 def _should_be_expanded(self): 177 if not self._state_requests: 178 return (True, False) 179 highest_priority = 0 180 expand = False 181 lock = False 182 for request in self._state_requests.values(): 183 lock |= request.lock 184 if request.priority > highest_priority: 185 highest_priority = request.priority 186 expand = request.expand 187 elif request.priority == highest_priority: 188 expand |= request.expand 189 190 return (expand, lock) 191 192 def force_expand(self): 193 if not self._is_forced_expanded and self._is_collapsed: 194 self._is_forced_expanded = True 195 self._interaction_state_token = self.request_expand(Priority.player) 196 uicore.uilib.RegisterForTriuiEvents([uiconst.UI_MOUSEDOWN], self._on_global_mouse_down) 197 198 def force_collapse(self): 199 if self._focus_state_token is None: 200 self._focus_state_token = self.request_collapse(Priority.player) 201 202 def clear_force_collapse(self): 203 if self._focus_state_token is not None: 204 self._focus_state_token.clear() 205 self._focus_state_token = None 206 207 def _update_interaction_point_in_frame(self): 208 pass 209 210 def _on_global_mouse_down(self, target, *args, **kwargs): 211 if target is None: 212 return True 213 if not self._is_forced_expanded: 214 return False 215 click_captured = self.content_bounds.contains(*eveui.Mouse.position()) 216 if self._is_forced_expanded and not click_captured: 217 self._is_forced_expanded = False 218 return False 219 return True 220 221 @abc.abstractproperty 222 def content_bounds(self): 223 pass 224 225 @abc.abstractmethod 226 def expand(self): 227 pass 228 229 @abc.abstractmethod 230 def collapse(self, lock = False): 231 pass 232 233 234 class Priority(object): 235 control_point = 5 236 player = 4 237 no_content = 3 238 zoomed_out = 2 239 clipped = 1 240 241 242 class StateRequest(object): 243 244 def __init__(self, priority, expand, lock): 245 self.priority = priority 246 self.expand = expand 247 self.lock = lock 248 249 @property 250 def collapse(self): 251 return not self.expand 252 253 254 class StateRequestToken(object): 255 256 def __init__(self, token_id, on_clear): 257 self._token_id = token_id 258 self._cleared = False 259 self.on_clear = signals.Signal() 260 if on_clear: 261 self.on_clear.connect(on_clear) 262 263 def clear(self): 264 if not self._cleared: 265 self._cleared = True 266 self.on_clear(self._token_id) 267 268 def __del__(self): 269 self.clear() 270 271 272 def get_absolute_bounds(ui_object, allow_alignment = False): 273 if ui_object.destroyed: 274 return Rect.zero() 275 if allow_alignment: 276 left, top, width, height = ui_object.GetAbsolute() 277 else: 278 left, top = ui_object.GetCurrentAbsolutePosition() 279 width, height = ui_object.GetCurrentAbsoluteSize() 280 return Rect(left, top, width, height) 281 282 283 from dynamicresources.client.ess.bracket.debug import __reload_update__