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

scrollbar.py (18913B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\carbonui\control\scrollbar.py
      2 import datetime
      3 import gametime
      4 import mathext
      5 import signals
      6 import uthread2
      7 from carbon.client.script.environment.AudioUtil import PlaySound
      8 from carbonui import Axis, uiconst
      9 from carbonui.primitives.container import Container
     10 from carbonui.primitives.fill import Fill
     11 from carbonui.text.color import TextColor
     12 from carbonui.uianimations import animations
     13 from carbonui.uicore import uicore
     14 from carbonui.util.dpi import reverse_scale_dpi
     15 from carbonui.util.various_unsorted import GetWindowAbove
     16 from chroma import Color
     17 from eve.client.script.ui import eveColor, eveThemeColor
     18 
     19 class Scrollbar(Container):
     20     TRACK_COLOR = (0.0, 0.0, 0.0)
     21     TRACK_SIZE = 8
     22     TRACK_OPACITY_INACTIVE = 0.0
     23     TRACK_OPACITY_ACTIVE = 0.3
     24     HANDLE_THICKNESS_IDLE = 2
     25     HANDLE_THICKNESS_HOVER = 4
     26     HANDLE_SIZE_MIN = 16
     27     HANDLE_HIGHLIGHT_DURATION = datetime.timedelta(milliseconds=300)
     28     PAGE_SCROLLING_INITIAL_DELAY = 0.3
     29     PAGE_SCROLLING_INTERVAL = 0.05
     30     _active = False
     31     _handle = None
     32     _handle_color_override = None
     33     _handle_highlight_tasklet = None
     34     _handle_highlight_timestamp = None
     35     _hovered = False
     36     _on_scroll_fraction_changed = None
     37     _page_scrolling_tasklet = None
     38     _track = None
     39 
     40     def __init__(self, axis, scroll_fraction = 0.0, handle_size_fraction = 0.0, on_scroll_fraction_changed = None, state = uiconst.UI_NORMAL, **kwargs):
     41         self._axis = axis
     42         self._scroll_fraction = scroll_fraction
     43         self._handle_size_fraction = handle_size_fraction
     44         super(Scrollbar, self).__init__(state=state, **kwargs)
     45         window_above = GetWindowAbove(self)
     46         if window_above:
     47             self._active = window_above.active
     48         self._layout()
     49         if on_scroll_fraction_changed:
     50             self.on_scroll_fraction_changed.connect(on_scroll_fraction_changed)
     51 
     52     @property
     53     def axis(self):
     54         return self._axis
     55 
     56     @property
     57     def scrolling(self):
     58         is_page_scrolling = self._page_scrolling_tasklet is not None
     59         if self._handle:
     60             return self._handle.scrolling or is_page_scrolling
     61         else:
     62             return is_page_scrolling
     63 
     64     @property
     65     def handle_size_fraction(self):
     66         return self._handle_size_fraction
     67 
     68     @handle_size_fraction.setter
     69     def handle_size_fraction(self, value):
     70         value = mathext.clamp(value, 0.0, 1.0)
     71         if self._handle_size_fraction != value:
     72             self._handle_size_fraction = value
     73             self._update_handle_size_and_position()
     74 
     75     @property
     76     def scroll_fraction(self):
     77         return self._scroll_fraction
     78 
     79     @scroll_fraction.setter
     80     def scroll_fraction(self, value):
     81         value = mathext.clamp(value, 0.0, 1.0)
     82         if self._scroll_fraction != value:
     83             self._scroll_fraction = value
     84             self._update_handle_size_and_position()
     85             if self._on_scroll_fraction_changed is not None:
     86                 self._on_scroll_fraction_changed(self)
     87 
     88     @property
     89     def on_scroll_fraction_changed(self):
     90         if self._on_scroll_fraction_changed is None:
     91             self._on_scroll_fraction_changed = signals.Signal('{}.on_scroll_fraction_changed'.format(self.__class__.__name__))
     92         return self._on_scroll_fraction_changed
     93 
     94     def scroll_by_page(self, page_count = 1):
     95         scrollable_portion = 1.0 - self._handle_size_fraction
     96         scroll_amount = page_count * self._handle_size_fraction
     97         scroll_fraction = self.scroll_fraction + scroll_amount / scrollable_portion
     98         self._set_scroll_fraction_with_highlight(scroll_fraction)
     99 
    100     def scroll_to_fraction(self, scroll_fraction):
    101         self._set_scroll_fraction_with_highlight(scroll_fraction)
    102 
    103     def _set_scroll_fraction_with_highlight(self, scroll_fraction):
    104         old_scroll_fraction = self.scroll_fraction
    105         self.scroll_fraction = scroll_fraction
    106         if not mathext.is_close(old_scroll_fraction, scroll_fraction):
    107             self._highlight_scroll_handle()
    108 
    109     def _layout(self):
    110         handle_width_min, handle_height_min = self._get_handle_size_min()
    111         self._handle = ScrollHandle(parent=self, align=self._get_handle_align(), axis=self._axis, expanded=self._get_handle_expanded(), color=self._get_handle_color(), on_scroll_move=self._on_handle_scroll_move, on_hovered_changed=self._on_handle_hovered_changed, minWidth=handle_width_min, minHeight=handle_height_min)
    112         self._track = Fill(bgParent=self, align=uiconst.TOALL, color=self.TRACK_COLOR, opacity=self._get_track_opacity())
    113 
    114     def _get_dimensions(self):
    115         if self.align == uiconst.TOALL:
    116             return (0, 0)
    117         if self._axis == Axis.HORIZONTAL:
    118             height = self.TRACK_SIZE
    119             if self.align in uiconst.ALIGNMENTS_WITH_INCLUDED_VERTICAL_PADDING:
    120                 height += self.padTop + self.padBottom
    121             return (self.width, height)
    122         if self._axis == Axis.VERTICAL:
    123             width = self.TRACK_SIZE
    124             if self.align in uiconst.ALIGNMENTS_WITH_INCLUDED_HORIZONTAL_PADDING:
    125                 width += self.padLeft + self.padRight
    126             return (width, self.height)
    127 
    128     def _update_dimensions(self):
    129         self.width, self.height = self._get_dimensions()
    130 
    131     def _get_handle_align(self):
    132         if self._axis == Axis.HORIZONTAL:
    133             return uiconst.TOLEFT_PROP
    134         if self._axis == Axis.VERTICAL:
    135             return uiconst.TOTOP_PROP
    136 
    137     def _get_handle_color(self):
    138         if self._handle_color_override is not None:
    139             return self._handle_color_override
    140         elif self._handle and self._handle.hovered:
    141             return self._get_handle_highlight_color()
    142         elif self._hovered:
    143             return Color.from_rgba(*eveThemeColor.THEME_FOCUSDARK).with_alpha(0.6)
    144         else:
    145             return TextColor.DISABLED
    146 
    147     @staticmethod
    148     def _get_handle_highlight_color():
    149         return eveThemeColor.THEME_FOCUS
    150 
    151     def _update_handle_color(self, duration = 0.3):
    152         if self._handle:
    153             animations.SpColorMorphTo(self._handle, startColor=self._handle.color, endColor=self._get_handle_color(), duration=duration)
    154 
    155     def _highlight_scroll_handle(self):
    156         self._handle_highlight_timestamp = gametime.now()
    157         if self._handle_highlight_tasklet is None:
    158             self._handle_highlight_tasklet = uthread2.start_tasklet(self._do_highlight_handle)
    159 
    160     def _do_highlight_handle(self):
    161         try:
    162             self._handle_color_override = self._get_handle_highlight_color()
    163             self._update_handle_color(duration=0.1)
    164             while True:
    165                 remaining = (self._handle_highlight_timestamp + self.HANDLE_HIGHLIGHT_DURATION - gametime.now()).total_seconds()
    166                 if remaining > 0.0:
    167                     uthread2.sleep(remaining)
    168                 else:
    169                     self._handle_color_override = None
    170                     self._update_handle_color(duration=0.5)
    171                     break
    172 
    173         finally:
    174             self._handle_highlight_tasklet = None
    175 
    176     def _get_handle_size_min(self):
    177         if self._axis == Axis.HORIZONTAL:
    178             return (self.HANDLE_SIZE_MIN, None)
    179         if self._axis == Axis.VERTICAL:
    180             return (None, self.HANDLE_SIZE_MIN)
    181 
    182     def _update_handle_size_min(self):
    183         if self._handle:
    184             self._handle.minWidth, self._handle.minHeight = self._get_handle_size_min()
    185 
    186     def _get_current_handle_length(self):
    187         if self._axis == Axis.HORIZONTAL:
    188             return reverse_scale_dpi(self._handle.displayWidth)
    189         if self._axis == Axis.VERTICAL:
    190             return reverse_scale_dpi(self._handle.displayHeight)
    191 
    192     def _get_current_track_length(self):
    193         if self._axis == Axis.HORIZONTAL:
    194             return reverse_scale_dpi(self.displayWidth)
    195         if self._axis == Axis.VERTICAL:
    196             return reverse_scale_dpi(self.displayHeight)
    197 
    198     def _get_handle_size_and_position(self):
    199         if self._handle:
    200             track_length = self._get_current_track_length()
    201             handle_length = self._get_current_handle_length()
    202             if mathext.is_almost_zero(track_length):
    203                 handle_size_fraction = 1.0
    204             else:
    205                 handle_size_fraction = handle_length / float(track_length)
    206             position = mathext.clamp(self._scroll_fraction * (1.0 - handle_size_fraction), low=0.0, high=1.0)
    207             if self._axis == Axis.HORIZONTAL:
    208                 return (self._handle_size_fraction, 0, position)
    209             if self._axis == Axis.VERTICAL:
    210                 return (0, self._handle_size_fraction, position)
    211         else:
    212             return (0, 0, 0)
    213 
    214     def _update_handle_size_and_position(self):
    215         if self._handle:
    216             self._handle.width, self._handle.height, position = self._get_handle_size_and_position()
    217             if self._axis == Axis.HORIZONTAL:
    218                 self._handle.left = position
    219             elif self._axis == Axis.VERTICAL:
    220                 self._handle.top = position
    221 
    222     def _get_handle_expanded(self):
    223         return self._hovered
    224 
    225     def _update_handle_expanded(self):
    226         if self._handle:
    227             self._handle.expanded = self._get_handle_expanded()
    228 
    229     def _on_handle_scroll_move(self, delta):
    230         scrollable_length = self._get_scrollable_track_length()
    231         if mathext.is_almost_zero(scrollable_length):
    232             scroll_fraction = 0.0
    233         else:
    234             scroll_fraction = delta / float(scrollable_length)
    235         self.scroll_fraction += scroll_fraction
    236 
    237     def _get_scrollable_track_length(self):
    238         if self._handle:
    239             return self._get_current_track_length() - self._get_current_handle_length()
    240         else:
    241             return 0
    242 
    243     def _on_handle_hovered_changed(self, handle):
    244         self._update_handle_color(duration=0.1)
    245 
    246     def _get_track_opacity(self):
    247         if self._active or self._hovered:
    248             return self.TRACK_OPACITY_ACTIVE
    249         else:
    250             return self.TRACK_OPACITY_INACTIVE
    251 
    252     def _update_track_opacity(self):
    253         if self._track:
    254             animations.FadeTo(self._track, startVal=self._track.opacity, endVal=self._get_track_opacity(), duration=0.3)
    255 
    256     def _get_track_fraction_at_position(self, x, y):
    257         track_length = self._get_current_track_length()
    258         if self._axis == Axis.VERTICAL:
    259             mouse_position = y
    260             _, track_start = self.GetCurrentAbsolutePosition()
    261         else:
    262             mouse_position = x
    263             track_start, _ = self.GetCurrentAbsolutePosition()
    264         return mathext.clamp((mouse_position - track_start) / float(track_length), low=0.0, high=1.0)
    265 
    266     def _get_scroll_fraction_at_position(self, x, y):
    267         track_length = self._get_current_track_length()
    268         if self._axis == Axis.VERTICAL:
    269             mouse_position = y
    270             _, track_start = self.GetCurrentAbsolutePosition()
    271         else:
    272             mouse_position = x
    273             track_start, _ = self.GetCurrentAbsolutePosition()
    274         handle_length = self._get_current_handle_length()
    275         return mathext.clamp((mouse_position - track_start) / float(track_length - handle_length), low=0.0, high=1.0)
    276 
    277     def _start_page_scrolling(self, target_fraction):
    278         if target_fraction >= self.scroll_fraction:
    279             page_count = 1
    280         else:
    281             page_count = -1
    282         self.scroll_by_page(page_count)
    283         self._start_page_scrolling_tasklet(page_count)
    284 
    285     def _start_page_scrolling_tasklet(self, page_count):
    286         self._cancel_page_scrolling_maybe()
    287         self._page_scrolling_tasklet = uthread2.start_tasklet(self._page_scrolling_loop, page_count)
    288 
    289     def _cancel_page_scrolling_maybe(self):
    290         if self._page_scrolling_tasklet is not None:
    291             self._page_scrolling_tasklet.kill()
    292             self._page_scrolling_tasklet = None
    293 
    294     def _page_scrolling_loop(self, page_count):
    295         try:
    296             uthread2.sleep(self.PAGE_SCROLLING_INITIAL_DELAY)
    297             while True:
    298                 if not uicore.uilib.leftbtn:
    299                     break
    300                 if self._axis == Axis.VERTICAL:
    301                     handle_start = self._handle.top
    302                 else:
    303                     handle_start = self._handle.left
    304                 handle_end = handle_start + self._handle_size_fraction
    305                 target_fraction = self._get_track_fraction_at_position(x=uicore.uilib.x, y=uicore.uilib.y)
    306                 if page_count > 0 and target_fraction >= handle_end or page_count < 0 and target_fraction <= handle_start:
    307                     self.scroll_by_page(page_count)
    308                 uthread2.sleep(self.PAGE_SCROLLING_INTERVAL)
    309 
    310         finally:
    311             self._page_scrolling_tasklet = None
    312 
    313     def UpdateAlignment(self, budgetLeft = 0, budgetTop = 0, budgetWidth = 0, budgetHeight = 0, updateChildrenOnly = False):
    314         self._update_dimensions()
    315         result = super(Scrollbar, self).UpdateAlignment(budgetLeft, budgetTop, budgetWidth, budgetHeight, updateChildrenOnly)
    316         self._update_handle_size_and_position()
    317         if self._handle._alignmentDirty:
    318             super(Scrollbar, self).UpdateAlignment(budgetLeft, budgetTop, budgetWidth, budgetHeight, updateChildrenOnly)
    319         return result
    320 
    321     def OnWindowAboveSetActive(self):
    322         self._active = True
    323         self._update_track_opacity()
    324         self._update_handle_color()
    325 
    326     def OnWindowAboveSetInactive(self):
    327         self._active = False
    328         self._update_track_opacity()
    329         self._update_handle_color()
    330 
    331     def OnMouseEnter(self, *args):
    332         self._hovered = True
    333         PlaySound(uiconst.SOUND_BUTTON_HOVER)
    334         self._update_handle_color(duration=0.1)
    335         self._update_handle_expanded()
    336         self._update_track_opacity()
    337 
    338     def OnMouseExit(self, *args):
    339         self._hovered = False
    340         self._update_handle_color()
    341         self._update_handle_expanded()
    342         self._update_track_opacity()
    343 
    344     def OnMouseDown(self, button, *args):
    345         if button == uiconst.MOUSELEFT:
    346             self._start_page_scrolling(target_fraction=self._get_scroll_fraction_at_position(x=uicore.uilib.x, y=uicore.uilib.y))
    347 
    348     def OnMouseUp(self, button, *args):
    349         if button == uiconst.MOUSELEFT:
    350             self._cancel_page_scrolling_maybe()
    351 
    352 
    353 class ScrollHandle(Container):
    354     _fill = None
    355     _fill_expanded = None
    356     _hovered = False
    357     _on_hovered_changed = None
    358     _scrolling = False
    359     _last_mouse_position = None
    360     _on_scroll_move = None
    361 
    362     def __init__(self, axis, color = None, expanded = False, on_hovered_changed = None, on_scroll_move = None, state = uiconst.UI_NORMAL, **kwargs):
    363         if color is None:
    364             color = eveColor.WHITE
    365         color = Color.from_any(color)
    366         self._axis = axis
    367         self._expanded = expanded
    368         self._color = color
    369         super(ScrollHandle, self).__init__(state=state, **kwargs)
    370         self.opacity = color.opacity
    371         self._layout()
    372         if on_hovered_changed is not None:
    373             self.on_hovered_changed.connect(on_hovered_changed)
    374         if on_scroll_move is not None:
    375             self.on_scroll_move.connect(on_scroll_move)
    376 
    377     @property
    378     def color(self):
    379         return self._color.with_alpha(self.opacity)
    380 
    381     @color.setter
    382     def color(self, value):
    383         self._color = Color.from_any(value)
    384         self.opacity = self._color.opacity
    385         if self._fill:
    386             self._fill.color = self._color.rgb
    387         if self._fill_expanded:
    388             self._fill_expanded.color = self._color.rgb
    389 
    390     @property
    391     def expanded(self):
    392         return self._expanded
    393 
    394     @expanded.setter
    395     def expanded(self, value):
    396         if self._expanded != value:
    397             self._expanded = value
    398             self._update_expanded()
    399 
    400     @property
    401     def hovered(self):
    402         return self._hovered
    403 
    404     @property
    405     def on_hovered_changed(self):
    406         if self._on_hovered_changed is None:
    407             self._on_hovered_changed = signals.Signal('{}.on_hovered_changed'.format(self.__class__.__name__))
    408         return self._on_hovered_changed
    409 
    410     @property
    411     def scrolling(self):
    412         return self._scrolling
    413 
    414     @property
    415     def on_scroll_move(self):
    416         if self._on_scroll_move is None:
    417             self._on_scroll_move = signals.Signal('{}.on_scroll_move'.format(self.__class__.__name__))
    418         return self._on_scroll_move
    419 
    420     def _layout(self):
    421         self._fill = Fill(parent=self, align=uiconst.TOALL, padding=self._get_fill_padding(expanded=False), color=self._color.rgb)
    422         self._fill_expanded = Fill(parent=self, align=uiconst.TOALL, padding=self._get_fill_padding(expanded=True), color=self._color.rgb, opacity=1.0 if self._expanded else 0.0, outputMode=uiconst.OUTPUT_COLOR_AND_GLOW, glowBrightness=0.3)
    423 
    424     @staticmethod
    425     def _get_fill_padding(expanded):
    426         if expanded:
    427             handle_thickness = Scrollbar.HANDLE_THICKNESS_HOVER
    428         else:
    429             handle_thickness = Scrollbar.HANDLE_THICKNESS_IDLE
    430         pad = int(round((Scrollbar.TRACK_SIZE - handle_thickness) / 2.0))
    431         return (pad,
    432          pad,
    433          pad,
    434          pad)
    435 
    436     def _update_expanded(self):
    437         if self._fill_expanded:
    438             if self._expanded:
    439                 animations.FadeIn(self._fill_expanded, duration=0.2)
    440             else:
    441                 animations.FadeOut(self._fill_expanded, duration=0.2)
    442 
    443     def _start_scrolling(self):
    444         if not self._scrolling:
    445             self._scrolling = True
    446             if self._axis == Axis.HORIZONTAL:
    447                 self._last_mouse_position = uicore.uilib.x
    448             elif self._axis == Axis.VERTICAL:
    449                 self._last_mouse_position = uicore.uilib.y
    450 
    451     def _update_scrolling(self):
    452         current_position = 0
    453         if self._axis == Axis.HORIZONTAL:
    454             current_position = uicore.uilib.x
    455         elif self._axis == Axis.VERTICAL:
    456             current_position = uicore.uilib.y
    457         delta = current_position - self._last_mouse_position
    458         if delta != 0 and self._on_scroll_move is not None:
    459             self._on_scroll_move(delta)
    460         self._last_mouse_position = current_position
    461 
    462     def _stop_scrolling(self):
    463         if self._scrolling:
    464             self._scrolling = False
    465 
    466     def OnMouseEnter(self, *args):
    467         self._hovered = True
    468         if self._on_hovered_changed:
    469             self._on_hovered_changed(self)
    470 
    471     def OnMouseExit(self, *args):
    472         self._hovered = False
    473         if self._on_hovered_changed:
    474             self._on_hovered_changed(self)
    475 
    476     def OnMouseDown(self, button, *args):
    477         if button == uiconst.MOUSELEFT:
    478             self._start_scrolling()
    479 
    480     def OnMouseMove(self, *args):
    481         if self._scrolling:
    482             self._update_scrolling()
    483 
    484     def OnMouseUp(self, button, *args):
    485         if button == uiconst.MOUSELEFT:
    486             self._stop_scrolling()