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

window.py (130049B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\carbonui\control\window.py
      2 import math
      3 import sys
      4 import weakref
      5 import blue
      6 import eveicon
      7 import gametime
      8 import localization
      9 import log
     10 import signals
     11 import telemetry
     12 import types
     13 import uthread
     14 from carbon.common.script.sys.serviceConst import ROLE_PROGRAMMER
     15 from carbon.client.script.environment.AudioUtil import PlaySound
     16 from carbonui.text.settings import set_font_size_setting
     17 from carbonui.text.settings import FontSizeOption
     18 from carbonui import uiconst
     19 from carbonui.control.animatedsprite import AnimSprite
     20 from carbonui.control.button import Button
     21 from carbonui.button.const import ButtonVariant
     22 from carbonui.control.contextMenu.menuData import MenuData, MenuEntryData
     23 from carbonui.window.resizer import Resizer, Side
     24 from carbonui.window.snap import find_sibling_windows, SNAP_DISTANCE, WINDOW_SNAP_DISTANCE
     25 from carbonui.window.underlay import WindowUnderlay
     26 from carbonui.primitives.container import Container
     27 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     28 from carbonui.primitives.fill import Fill
     29 from carbonui.primitives.sprite import Sprite
     30 from carbonui.primitives.transform import Transform
     31 from carbonui.uianimations import animations
     32 from carbonui.uicore import uicore
     33 from carbonui.util.bunch import Bunch
     34 from carbonui.util.various_unsorted import GetWindowAbove, Transplant
     35 from carbonui.window.header.base import WindowHeaderBase
     36 from carbonui.window.settings import GetRegisteredState, RegisterState, WindowCompactModeSetting, WindowMarginMode, window_compact_mode_default_setting, window_margin_mode
     37 from carbonui.window.util import is_blocked
     38 from eve.client.script.ui import eveThemeColor
     39 from carbonui.button.group import ButtonGroup, ButtonSizeMode
     40 from carbonui.loggers.message_bus.windowMessenger import WindowMessenger
     41 from carbonui.window.control.default import DefaultWindowControls
     42 from carbonui.window.control.action import CloseWindowAction, CollapseWindowAction, CompactModeWindowAction, LightBackgroundWindowAction, LockWindowAction, MinimizeWindowAction, OverlayWindowAction
     43 from carbonui.window.header.default import DefaultWindowHeader
     44 from eveexceptions import EatsExceptions, UserError
     45 from menu import MenuLabel
     46 POSOVERLAPSHIFT = 11
     47 LEGACY_SIDE_NAME_BY_SIDE = {Side.LEFT: 'left',
     48  Side.TOP: 'top',
     49  Side.RIGHT: 'right',
     50  Side.BOTTOM: 'bottom'}
     51 
     52 class Window(Container):
     53     __guid__ = 'uicontrols.Window'
     54     isDragObject = True
     55     isTopLevelWindow = True
     56     default_width = 256
     57     default_height = 128
     58     default_minSize = (default_width, default_height)
     59     default_maxSize = (None, None)
     60     default_fixedHeight = None
     61     default_fixedWidth = None
     62     default_left = '__center__'
     63     default_top = '__center__'
     64     default_name = 'window'
     65     default_idx = 0
     66     default_state = uiconst.UI_HIDDEN
     67     default_isLightBackground = False
     68     default_isLightBackgroundConfigurable = True
     69     default_isOverlayed = False
     70     default_isOverlayable = True
     71     default_iconNum = 'res:/UI/Texture/WindowIcons/other.png'
     72     default_scope = uiconst.SCOPE_INGAME
     73     default_soundOpen = None
     74     default_extend_content_into_header = False
     75     default_apply_content_padding = True
     76     default_windowInstanceID = None
     77     default_stackID = None
     78     default_caption = None
     79     default_captionLabelPath = None
     80     default_descriptionLabelPath = None
     81     default_align = uiconst.RELATIVE
     82     default_openMinimized = False
     83     default_isKillable = True
     84     default_isMinimizable = True
     85     default_isStackable = True
     86     default_isCompactable = False
     87     default_isLockable = True
     88     default_useDefaultPos = False
     89     default_analyticID = None
     90     default_isCollapseable = True
     91     default_isCompact = None
     92     default_windowID = None
     93     WINDOW_NEVER_OFFSCREEN_BUFFER = 20
     94     COLLAPSE_AREA_HEIGHT = 25
     95     __active = False
     96     __active_snap_grid = None
     97     __caption = None
     98     __closing = False
     99     __collapsed = False
    100     __drag_started = False
    101     __focused_at_timestamp = None
    102     __header = None
    103     __header_has_sufficient_bottom_padding = False
    104     __header_inset = (0, 0)
    105     __hide_loading_indicator_requested = False
    106     __hovered = False
    107     __modal_result = None
    108     __on_active_changed = None
    109     __on_caption_changed = None
    110     __on_collapsed_changed = None
    111     __on_collapsible_changed = None
    112     __on_compact_mode_changed = None
    113     __on_content_padding_changed = None
    114     __on_end_scale = None
    115     __on_fixed_height_changed = None
    116     __on_fixed_width_changed = None
    117     __on_header_height_changed = None
    118     __on_header_inset_changed = None
    119     __on_icon_changed = None
    120     __on_killable_changed = None
    121     __on_light_background_changed = None
    122     __on_lockable_changed = None
    123     __on_locked_changed = None
    124     __on_margin_mode_changed = None
    125     __on_minimizable_changed = None
    126     __on_stacked_changed = None
    127     __on_start_scale = None
    128     __on_window_state_changed = None
    129     __opened_at_timestamp = None
    130     __previous_content_padding = None
    131     __previous_header_height = None
    132     __ready = False
    133     __resizer = None
    134     __scale_sides = None
    135     __stack = None
    136     __window_controls = None
    137     __window_controls_cont = None
    138 
    139     @staticmethod
    140     def __get_default_compact(window_ref):
    141         window = window_ref()
    142         if not window or window.closing:
    143             return False
    144         elif window.compactable and window.default_isCompact is None:
    145             return window_compact_mode_default_setting.get()
    146         else:
    147             return window.default_isCompact
    148 
    149     @classmethod
    150     def Reload(cls, instance):
    151         attributes = instance.attributesBunch
    152         state = instance.state
    153         if instance.isDialog:
    154             instance.Close()
    155             return
    156         attributes.openMinimized = instance.IsMinimized()
    157         instance.Close()
    158         wnd = cls.Open(**attributes)
    159         wnd.SetState(state)
    160 
    161     def __init__(self, show_window_controls = True, **kwargs):
    162         self.default_parent = uicore.layer.main
    163         self.__apply_content_padding = kwargs.get('apply_content_padding', self.default_apply_content_padding)
    164         self.__collapsible = self.default_isCollapseable
    165         self.__compact_mode_setting = WindowCompactModeSetting(window_id=kwargs.get('windowID', self.default_windowID), default_value=lambda _window_ref = weakref.ref(self): Window.__get_default_compact(_window_ref))
    166         self.__compactable = self.default_isCompactable
    167         self.__extend_content_into_header = kwargs.get('extend_content_into_header', self.default_extend_content_into_header)
    168         self.__icon = kwargs.get('iconNum', self.GetDefaultWndIcon())
    169         self.__killable = self.default_isKillable
    170         self.__lockable = self.default_isLockable
    171         self.__minimizable = kwargs.get('isMinimizable', self.default_isMinimizable)
    172         self.__scene_containers = set()
    173         self.__show_window_controls = show_window_controls
    174         self.__stackable = self.default_isStackable
    175         self._changing = False
    176         self._fixedHeight = self.default_fixedHeight
    177         self._fixedWidth = self.default_fixedWidth
    178         self._resizeable = True
    179         self._scaling = False
    180         self.preDragAbs = None
    181         self.preDragCursorPos = None
    182         self.stackID = kwargs.get('stackID', self.default_stackID)
    183         self.sortedScaleWindows = None
    184         self.minmaxScale = None
    185         self.dragMousePosition = None
    186         self.isBlinking = False
    187         self.isDialog = False
    188         self.isModal = False
    189         self.maxsize = kwargs.get('maxSize', self.default_maxSize)
    190         self.minsize = kwargs.get('minSize', self.default_minSize)
    191         self.scope = kwargs.get('scope', self.default_scope)
    192         self.windowID = kwargs.get('windowID', self.default_windowID)
    193         self.windowInstanceID = kwargs.get('windowInstanceID', self.default_windowInstanceID)
    194         super(Window, self).__init__(**kwargs)
    195         attributesBunch = Bunch(**kwargs)
    196         self.PostApplyAttributes(attributesBunch)
    197         window_margin_mode.on_changed.connect(self.__on_global_window_margin_mode_changed)
    198         notifyevents = getattr(self, '__notifyevents__', None)
    199         if not notifyevents:
    200             notifyevents = ['OnUIRefresh']
    201         elif 'OnUIRefresh' not in notifyevents:
    202             notifyevents.append('OnUIRefresh')
    203         self.__notifyevents__ = notifyevents
    204         sm.RegisterNotify(self)
    205 
    206     def ApplyAttributes(self, attributes):
    207         self.attributesBunch = attributes
    208         self.sr.tab = None
    209         self.sr.modalParent = None
    210         self.sr.loadingParent = None
    211         self.sr.underlay = None
    212         self.sr.headerParent = None
    213         self.sr.loadingIndicator = None
    214         self._CheckCallableDefaults()
    215         uicore.registry.RegisterWindow(self)
    216         left, top, _, _ = self.GetDefaultSizeAndPosition()
    217         attributes['left'] = left
    218         attributes['top'] = top
    219         if self.windowID:
    220             try:
    221                 attributes.name = str(self.windowID)
    222             except Exception:
    223                 attributes.name = repr(self.windowID)
    224 
    225         super(Window, self).ApplyAttributes(attributes)
    226         self.sr.maincontainer = Container(name='content', parent=self, align=uiconst.TOALL, state=uiconst.UI_PICKCHILDREN)
    227         self.cacheContents = True
    228         self.InitializeSize(useDefaultPos=attributes.get('useDefaultPos', self.default_useDefaultPos))
    229         self.Prepare_()
    230         self._UpdateHeaderInset(silent=True)
    231         self._UpdateBorderPadding()
    232         self.__compact_mode_setting.on_change.connect(self.__on_compact_mode_setting_changed)
    233         self._analyticID = attributes.Get('analyticID', self.default_analyticID)
    234         self.__opened_at_timestamp = gametime.GetWallclockTime()
    235         sm.ScatterEvent('OnWindowOpened', self)
    236         self._LogWindowEventOpened()
    237         soundOpen = attributes.get('soundOpen', self.default_soundOpen)
    238         if soundOpen:
    239             PlaySound(soundOpen)
    240 
    241     @property
    242     def active(self):
    243         return self.__active
    244 
    245     @property
    246     def on_active_changed(self):
    247         if self.__on_active_changed is None:
    248             self.__on_active_changed = signals.Signal('{}.on_active_changed'.format(self.__class__.__name__))
    249         return self.__on_active_changed
    250 
    251     def _emit_on_active_changed(self):
    252         if not self.closing and self.__on_active_changed is not None:
    253             self.__on_active_changed(self)
    254 
    255     @property
    256     def analyticID(self):
    257         return self._analyticID
    258 
    259     @property
    260     def caption(self):
    261         return self.__caption
    262 
    263     @caption.setter
    264     def caption(self, value):
    265         if self.__caption != value:
    266             self.__caption = value
    267             self._emit_on_caption_changed()
    268 
    269     @property
    270     def on_caption_changed(self):
    271         if self.__on_caption_changed is None:
    272             self.__on_caption_changed = signals.Signal('Window.on_caption_changed')
    273         return self.__on_caption_changed
    274 
    275     def _emit_on_caption_changed(self):
    276         if not self.closing:
    277             self._OnCaptionChanged()
    278             if self.__on_caption_changed is not None:
    279                 self.__on_caption_changed(self)
    280 
    281     @property
    282     def content(self):
    283         return self.sr.main
    284 
    285     @property
    286     def content_padding(self):
    287         if self.compact or self.margin_mode == WindowMarginMode.COMPACT:
    288             padding = 8
    289         else:
    290             padding = 16
    291         should_apply_top_padding = self.__extend_content_into_header or not self.__header_has_sufficient_bottom_padding
    292         top_padding = padding if should_apply_top_padding else 0
    293         return (padding,
    294          top_padding,
    295          padding,
    296          padding)
    297 
    298     @property
    299     def on_content_padding_changed(self):
    300         if self.__on_content_padding_changed is None:
    301             self.__on_content_padding_changed = signals.Signal('Window.on_content_padding_changed')
    302         return self.__on_content_padding_changed
    303 
    304     def _emit_on_content_padding_changed(self):
    305         if not self.closing and self.__on_content_padding_changed is not None:
    306             self.__on_content_padding_changed(self)
    307 
    308     @property
    309     def apply_content_padding(self):
    310         return self.__apply_content_padding
    311 
    312     @apply_content_padding.setter
    313     def apply_content_padding(self, value):
    314         value = bool(value)
    315         if value != self.__apply_content_padding:
    316             self.__apply_content_padding = value
    317             self._UpdateContentPadding()
    318 
    319     @property
    320     def closing(self):
    321         return self.__closing or self.stacked and self.stack.closing
    322 
    323     @property
    324     def collapsed(self):
    325         if self.stacked:
    326             return self.stack.collapsed
    327         return self.__collapsed
    328 
    329     @property
    330     def on_collapsed_changed(self):
    331         if self.__on_collapsed_changed is None:
    332             self.__on_collapsed_changed = signals.Signal('Window.on_collapsed_changed')
    333         return self.__on_collapsed_changed
    334 
    335     def _emit_on_collapsed_changed(self):
    336         if not self.closing and self.__on_collapsed_changed is not None:
    337             self.__on_collapsed_changed(self)
    338 
    339     @collapsed.setter
    340     def collapsed(self, value):
    341         if value:
    342             self.Collapse()
    343         else:
    344             self.Expand()
    345 
    346     @property
    347     def collapsible(self):
    348         return self.__collapsible
    349 
    350     @collapsible.setter
    351     def collapsible(self, value):
    352         if self.__collapsible != value:
    353             if value:
    354                 self.__collapsible = True
    355             else:
    356                 self.__collapsible = False
    357             self._emit_on_collapsible_changed()
    358 
    359     @property
    360     def on_collapsible_changed(self):
    361         if self.__on_collapsible_changed is None:
    362             self.__on_collapsible_changed = signals.Signal('{}.on_collapsible_changed'.format(self.__class__.__name__))
    363         return self.__on_collapsible_changed
    364 
    365     def _emit_on_collapsible_changed(self):
    366         if self.__on_collapsible_changed is not None:
    367             self.__on_collapsible_changed(self)
    368 
    369     @property
    370     def compact(self):
    371         if self.compactable:
    372             return self.__compact_mode_setting.get()
    373         else:
    374             return self.__compact_mode_setting.get_default()
    375 
    376     @compact.setter
    377     def compact(self, value):
    378         action = 'compact' if value else 'uncompact'
    379         if self.compactable and not is_blocked(action, self.windowID):
    380             self.__compact_mode_setting.set(value)
    381 
    382     @property
    383     def on_compact_mode_changed(self):
    384         if self.__on_compact_mode_changed is None:
    385             self.__on_compact_mode_changed = signals.Signal('Window.on_compact_mode_changed')
    386         return self.__on_compact_mode_changed
    387 
    388     def _emit_on_compact_mode_changed(self):
    389         if not self.closing:
    390             if self.compact:
    391                 self.OnCompactModeEnabled()
    392             else:
    393                 self.OnCompactModeDisabled()
    394             self._UpdateContentPadding()
    395             self._update_window_controls()
    396             if self.__on_compact_mode_changed is not None:
    397                 self.__on_compact_mode_changed(self)
    398 
    399     def __on_compact_mode_setting_changed(self, value):
    400         if not self.closing:
    401             self._emit_on_compact_mode_changed()
    402 
    403     @property
    404     def compactable(self):
    405         return self.__compactable
    406 
    407     @property
    408     def displayRect(self):
    409         return (self._displayX,
    410          self._displayY,
    411          self._displayWidth,
    412          self._displayHeight)
    413 
    414     @displayRect.setter
    415     def displayRect(self, value):
    416         displayX, displayY, displayWidth, displayHeight = value
    417         self._displayX = int(round(displayX))
    418         self._displayY = int(round(displayY))
    419         self._displayWidth = int(round(displayWidth))
    420         self._displayHeight = int(round(displayHeight))
    421         ro = self.renderObject
    422         if ro:
    423             ro.displayX = self._displayX
    424             ro.displayY = self._displayY
    425             ro.displayWidth = self._displayWidth
    426             ro.displayHeight = self._displayHeight
    427 
    428     @property
    429     def extend_content_into_header(self):
    430         return self.__extend_content_into_header
    431 
    432     @extend_content_into_header.setter
    433     def extend_content_into_header(self, value):
    434         if self.__extend_content_into_header != value:
    435             self.__extend_content_into_header = value
    436             self._UpdateHeaderAlignment()
    437             self._UpdateContentPadding()
    438 
    439     @property
    440     def fixed_height(self):
    441         return self._fixedHeight
    442 
    443     @fixed_height.setter
    444     def fixed_height(self, value):
    445         self.SetFixedHeight(value)
    446 
    447     @property
    448     def on_fixed_height_changed(self):
    449         if self.__on_fixed_height_changed is None:
    450             self.__on_fixed_height_changed = signals.Signal('{}.on_fixed_height_changed'.format(self.__class__.__name__))
    451         return self.__on_fixed_height_changed
    452 
    453     def _emit_fixed_height_changed(self):
    454         if not self.closing and self.__on_fixed_height_changed is not None:
    455             self.__on_fixed_height_changed(self)
    456 
    457     @property
    458     def fixed_width(self):
    459         return self._fixedWidth
    460 
    461     @fixed_width.setter
    462     def fixed_width(self, value):
    463         self.SetFixedWidth(value)
    464 
    465     @property
    466     def on_fixed_width_changed(self):
    467         if self.__on_fixed_width_changed is None:
    468             self.__on_fixed_width_changed = signals.Signal('{}.on_fixed_width_changed'.format(self.__class__.__name__))
    469         return self.__on_fixed_width_changed
    470 
    471     def _emit_fixed_width_changed(self):
    472         if not self.closing and self.__on_fixed_width_changed is not None:
    473             self.__on_fixed_width_changed(self)
    474 
    475     @property
    476     def header(self):
    477         if self.closing:
    478             raise RuntimeError('Attempting to manipulate a window header while the window is closing or already closed.')
    479         elif self.__header is not None and self.__header.destroyed:
    480             raise RuntimeError("The window's header has been destroyed, even though the window has not been closed yet")
    481         return self.__header
    482 
    483     @header.setter
    484     def header(self, value):
    485         if not self.closing:
    486             if value is None:
    487                 value = DefaultWindowHeader()
    488             self._SetHeader(value)
    489 
    490     @property
    491     def header_height(self):
    492         if self.__header is not None:
    493             _, height = self.__header.GetAbsoluteSize()
    494             return height + self.__header.padTop + self.__header.padBottom
    495         return 0
    496 
    497     @property
    498     def on_header_height_changed(self):
    499         if self.__on_header_height_changed is None:
    500             self.__on_header_height_changed = signals.Signal('{}.on_header_height_changed'.format(self.__class__.__name__))
    501         return self.__on_header_height_changed
    502 
    503     def _emit_on_header_height_changed(self):
    504         if not self.closing and self.__on_header_height_changed is not None:
    505             self.__on_header_height_changed(self)
    506 
    507     @property
    508     def header_inset(self):
    509         return self.__header_inset
    510 
    511     @property
    512     def on_header_inset_changed(self):
    513         if self.__on_header_inset_changed is None:
    514             self.__on_header_inset_changed = signals.Signal('Window.on_header_inset_changed')
    515         return self.__on_header_inset_changed
    516 
    517     def _emit_on_header_inset_changed(self):
    518         if not self.closing and self.__on_header_inset_changed is not None:
    519             self.__on_header_inset_changed(self)
    520 
    521     @property
    522     def icon(self):
    523         return self.__icon
    524 
    525     @icon.setter
    526     def icon(self, value):
    527         if self.__icon != value:
    528             self.__icon = value
    529             self._emit_on_icon_changed()
    530 
    531     @property
    532     def on_icon_changed(self):
    533         if self.__on_icon_changed is None:
    534             self.__on_icon_changed = signals.Signal('Window.on_icon_changed')
    535         return self.__on_icon_changed
    536 
    537     def _emit_on_icon_changed(self):
    538         if not self.closing and self.__on_icon_changed is not None:
    539             self.__on_icon_changed(self)
    540 
    541     @property
    542     def is_stack(self):
    543         return False
    544 
    545     @property
    546     def killable(self):
    547         return self.__killable
    548 
    549     @killable.setter
    550     def killable(self, value):
    551         if self.__killable != value:
    552             self.__killable = value
    553             self._emit_on_killable_changed()
    554 
    555     @property
    556     def on_killable_changed(self):
    557         if self.__on_killable_changed is None:
    558             self.__on_killable_changed = signals.Signal('Window.on_killable_changed')
    559         return self.__on_killable_changed
    560 
    561     def _emit_on_killable_changed(self):
    562         if not self.closing and self.__on_killable_changed is not None:
    563             self.__on_killable_changed(self)
    564 
    565     @property
    566     def light_background_enabled(self):
    567         return self.IsLightBackgroundEnabled()
    568 
    569     @light_background_enabled.setter
    570     def light_background_enabled(self, value):
    571         if value == self.light_background_enabled:
    572             return
    573         if value:
    574             self.EnableLightBackground()
    575         else:
    576             self.DisableLightBackground()
    577 
    578     @property
    579     def on_light_background_changed(self):
    580         if self.__on_light_background_changed is None:
    581             self.__on_light_background_changed = signals.Signal('Window.on_light_background_changed')
    582         return self.__on_light_background_changed
    583 
    584     def _emit_on_light_background_changed(self):
    585         if not self.closing:
    586             self.UpdateIntersectionBackground()
    587             if self.__on_light_background_changed is not None:
    588                 self.__on_light_background_changed(self)
    589 
    590     @property
    591     def locked(self):
    592         return self.GetRegisteredState('locked', False)
    593 
    594     @locked.setter
    595     def locked(self, value):
    596         action = 'lock' if value else 'unlock'
    597         if is_blocked(action, self.windowID):
    598             return
    599         if self.locked != value:
    600             self.RegisterState('locked', bool(value))
    601             self._emit_on_locked_changed()
    602 
    603     @property
    604     def on_locked_changed(self):
    605         if self.__on_locked_changed is None:
    606             self.__on_locked_changed = signals.Signal('Window.on_locked_changed')
    607         return self.__on_locked_changed
    608 
    609     def _emit_on_locked_changed(self):
    610         if not self.closing:
    611             self.Prepare_ScaleAreas_()
    612             if self.__on_locked_changed is not None:
    613                 self.__on_locked_changed(self)
    614 
    615     @property
    616     def lockable(self):
    617         return self.default_isLockable
    618 
    619     @lockable.setter
    620     def lockable(self, value):
    621         if self.__lockable != value:
    622             self.__lockable = value
    623             self._emit_on_lockable_changed()
    624 
    625     @property
    626     def on_lockable_changed(self):
    627         if self.__on_lockable_changed is None:
    628             self.__on_lockable_changed = signals.Signal('{}.on_lockable_changed'.format(self.__class__.__name__))
    629         return self.__on_lockable_changed
    630 
    631     def _emit_on_lockable_changed(self):
    632         if self.__on_lockable_changed is not None:
    633             self.__on_lockable_changed(self)
    634 
    635     @property
    636     def margin_mode(self):
    637         return window_margin_mode.value
    638 
    639     @property
    640     def on_margin_mode_changed(self):
    641         if self.__on_margin_mode_changed is None:
    642             self.__on_margin_mode_changed = signals.Signal('{}.on_margin_mode_changed'.format(self.__class__.__name__))
    643         return self.__on_margin_mode_changed
    644 
    645     @property
    646     def minimizable(self):
    647         return self.__minimizable
    648 
    649     @minimizable.setter
    650     def minimizable(self, value):
    651         if self.__minimizable != value:
    652             self.__minimizable = value
    653             self._emit_on_minimizable_changed()
    654 
    655     @property
    656     def on_minimizable_changed(self):
    657         if self.__on_minimizable_changed is None:
    658             self.__on_minimizable_changed = signals.Signal('{}.on_minimizable_changed'.format(self.__class__.__name__))
    659         return self.__on_minimizable_changed
    660 
    661     def _emit_on_minimizable_changed(self):
    662         if not self.closing and self.__on_minimizable_changed is not None:
    663             self.__on_minimizable_changed(self)
    664 
    665     @property
    666     def minimized(self):
    667         return self.IsMinimized()
    668 
    669     @minimized.setter
    670     def minimized(self, value):
    671         if value:
    672             self.Minimize()
    673         else:
    674             self.Maximize()
    675 
    676     @property
    677     def overlayable(self):
    678         return self.default_isOverlayable
    679 
    680     @property
    681     def show_window_controls(self):
    682         return self.__show_window_controls
    683 
    684     @show_window_controls.setter
    685     def show_window_controls(self, value):
    686         if self.__show_window_controls != value:
    687             self.__show_window_controls = value
    688             self._update_window_controls_visibility()
    689 
    690     @property
    691     def stack(self):
    692         return self.__stack
    693 
    694     @property
    695     def stacked(self):
    696         return self.__stack is not None
    697 
    698     @property
    699     def on_stacked_changed(self):
    700         if self.__on_stacked_changed is None:
    701             self.__on_stacked_changed = signals.Signal('Window.on_stacked_changed')
    702         return self.__on_stacked_changed
    703 
    704     def _emit_on_stacked_changed(self):
    705         if not self.closing and self.__on_stacked_changed is not None:
    706             self.__on_stacked_changed(self)
    707 
    708     @property
    709     def on_start_scale(self):
    710         if self.__on_start_scale is None:
    711             self.__on_start_scale = signals.Signal('Window.on_start_scale')
    712         return self.__on_start_scale
    713 
    714     def _emit_on_start_scale(self):
    715         if not self.closing and self.__on_start_scale is not None:
    716             self.__on_start_scale(self)
    717 
    718     @property
    719     def on_end_scale(self):
    720         if self.__on_end_scale is None:
    721             self.__on_end_scale = signals.Signal('Window.on_end_scale')
    722         return self.__on_end_scale
    723 
    724     def _emit_on_end_scale(self):
    725         if not self.closing and self.__on_end_scale is not None:
    726             self.__on_end_scale(self)
    727 
    728     def OnUIRefresh(self):
    729         self.Reload(self)
    730 
    731     @telemetry.ZONE_METHOD
    732     def PostApplyAttributes(self, attributes):
    733         self.RegisterSize()
    734         if attributes.get('ignoreStack', False):
    735             self.RegisterStackID(None)
    736         self.InitializeCaption(attributes)
    737         if attributes.get('openDragging', False) and uicore.uilib.leftbtn:
    738             self.RegisterStackID(None)
    739             self._SetOpen(True)
    740             uthread.new(self._OpenDraggingThread)
    741         else:
    742             self.InitializeStatesAndPosition(showIfInStack=attributes.get('showIfInStack', True), useDefaultPos=attributes.get('useDefaultPos', self.default_useDefaultPos))
    743         if attributes.get('openMinimized', self.default_openMinimized):
    744             self.Minimize(animate=False)
    745         uicore.registry.SetFocus(self)
    746         self.__ready = True
    747 
    748     def InitializeCaption(self, attributes):
    749         if self.__caption is not None:
    750             return
    751         caption = attributes.get('caption', self.default_caption)
    752         if caption is not None:
    753             self.SetCaption(caption)
    754         else:
    755             captionLabelPath = attributes.get('captionLabelPath', self.default_captionLabelPath)
    756             if captionLabelPath:
    757                 self.SetCaption(localization.GetByLabel(captionLabelPath))
    758 
    759     def Show(self, *args):
    760         self.Maximize(*args)
    761 
    762     def _OpenDraggingThread(self):
    763         self.state = uiconst.UI_NORMAL
    764         if self.IsLocked():
    765             return
    766         self.dragMousePosition = (uicore.uilib.x, uicore.uilib.y)
    767         self.left = uicore.uilib.x - 5
    768         self.top = uicore.uilib.y - 5
    769         uicore.uilib.SetMouseCapture(self)
    770         compact = self.GetRegisteredState('compact', False)
    771         if compact:
    772             self.Compact()
    773         self._BeginDrag()
    774 
    775     def GetAbsolutePosition(self):
    776         stack = self.stack
    777         if stack:
    778             return stack.contentCont.GetAbsolutePosition()
    779         return (self.left, self.top)
    780 
    781     def _CheckCallableDefaults(self):
    782         if callable(self.default_left):
    783             self.default_left = self.default_left()
    784         if callable(self.default_top):
    785             self.default_top = self.default_top()
    786         if callable(self.default_width):
    787             self.default_width = self.default_width()
    788         if callable(self.default_height):
    789             self.default_height = self.default_height()
    790 
    791     def Prepare_(self):
    792         self.Prepare_Layout()
    793         self._create_window_controls()
    794         self.Prepare_Header_()
    795         self.Prepare_LoadingIndicator_()
    796         self.Prepare_Background_()
    797         self.Prepare_ScaleAreas_()
    798 
    799     def Prepare_Layout(self):
    800         self.ConstructHeaderParent()
    801         self.sr.main = self.construct_content_cont()
    802 
    803     def construct_content_cont(self):
    804         return Container(parent=self.sr.maincontainer, name='main', align=uiconst.TOALL, padding=self.content_padding)
    805 
    806     def ConstructHeaderParent(self):
    807         self.sr.headerParent = ContainerAutoSize(parent=self.sr.maincontainer, name='headerParent', align=self._GetHeaderAlignment(), alignMode=uiconst.TOTOP, callback=self.__on_header_parent_size_changed, only_use_callback_when_size_changes=True)
    808         index = 0
    809         if self.__resizer is not None:
    810             index = self.__resizer.GetOrder()
    811         self.__window_controls_cont = Container(name='window_controls_cont', parent=self, align=uiconst.TOTOP_NOPUSH, padding=self._get_window_controls_cont_padding(), idx=index)
    812 
    813     def _get_window_controls_cont_padding(self):
    814         pad_left, pad_top, pad_right, pad_bottom = self.GetWindowBorderPadding()
    815         size_left, size_top, size_right, size_bottom = self.GetWindowBorderSize()
    816         return (pad_left + size_left,
    817          pad_top + size_top,
    818          pad_right + size_right,
    819          pad_bottom + size_bottom)
    820 
    821     def Prepare_Header_(self):
    822         self._SetHeader(DefaultWindowHeader())
    823 
    824     def _create_window_controls(self):
    825         actions = self._get_window_actions()
    826         self.__window_controls = DefaultWindowControls(window=self, parent=self.__window_controls_cont, actions=actions, get_menu=self.GetMenuMoreOptions, get_link_data=self.GetWindowLinkData, menu_unique_name=self.get_wnd_menu_unique_name(), on_reserved_width_changed=self._UpdateHeaderInset, display=self._get_window_controls_visible())
    827 
    828     def update_window_controls(self):
    829         if self.stacked:
    830             self.stack.__window_controls.update()
    831         else:
    832             self.__window_controls.update()
    833 
    834     def _get_window_actions(self):
    835         actions = [CloseWindowAction(self),
    836          MinimizeWindowAction(self),
    837          LockWindowAction(self),
    838          OverlayWindowAction(self),
    839          CompactModeWindowAction(self),
    840          LightBackgroundWindowAction(self),
    841          CollapseWindowAction(self)]
    842         actions.extend(self.GetCustomHeaderButtons())
    843         return actions
    844 
    845     def get_wnd_menu_unique_name(self):
    846         return None
    847 
    848     def _update_window_controls(self):
    849         self._destroy_window_controls()
    850         self._create_window_controls()
    851 
    852     def _destroy_window_controls(self):
    853         if self.__window_controls is not None:
    854             self.__window_controls.Close()
    855             self.__window_controls = None
    856 
    857     def _get_window_controls_visible(self):
    858         return self.__show_window_controls and not self.stacked
    859 
    860     def _update_window_controls_visibility(self):
    861         if self.__window_controls is not None:
    862             self.__window_controls.display = self._get_window_controls_visible()
    863 
    864     def GetCustomHeaderButtons(self):
    865         return []
    866 
    867     def SwapLanguage(self, lang):
    868         sm.GetService('gameui').SetLanguage(lang, doReload=True)
    869 
    870     def SetFontSize(self, size):
    871         set_font_size_setting(size)
    872 
    873     def GetToggleOption(self, label, callback, enabled, md):
    874         if enabled:
    875             icon = 'res:/UI/Texture/classes/UtilMenu/checkBoxActive.png'
    876         else:
    877             icon = 'res:/UI/Texture/classes/UtilMenu/checkBoxInactive.png'
    878         md.AddEntry(text=label, func=callback, texturePath=icon)
    879 
    880     def ToggleLabelDefaults(self):
    881         localization.SetFallBackMode(not localization.GetFallBackMode())
    882 
    883     def GetLabelMissingOption(self, md):
    884         enabled = localization.GetFallBackMode()
    885         label = 'Localization Label Defaults'
    886         return self.GetToggleOption(label, self.ToggleLabelDefaults, enabled, md)
    887 
    888     def AddDeveloperMenu(self, md):
    889         languages = ['English',
    890          'Korean',
    891          'German',
    892          'Russian',
    893          'Japanese',
    894          'Chinese',
    895          'Spanish']
    896         font_sizes = [('ExtraLarge', FontSizeOption.EXTRA_LARGE),
    897          ('Large', FontSizeOption.LARGE),
    898          ('Medium', FontSizeOption.MEDIUM),
    899          ('Small', FontSizeOption.SMALL),
    900          ('Tiny', FontSizeOption.EXTRA_SMALL)]
    901         md.AddLabel('Developer/Programmer')
    902         md.AddEntry(text='Reload Window', func=self.DebugReload)
    903         self.GetLabelMissingOption(md)
    904         language_submenus = []
    905         for lang in languages:
    906             language_submenus.append(MenuEntryData(text=lang, func=lambda l = lang: self.SwapLanguage(l), texturePath='res:/UI/Texture/classes/insider/localization_18.png'))
    907 
    908         md.AddEntry(text='Swap UI Language', subMenuData=language_submenus)
    909         font_size_submenus = []
    910         for font_size in font_sizes:
    911             font_size_submenus.append(MenuEntryData(text=font_size[0], func=lambda fs = font_size[1]: self.SetFontSize(fs), texturePath='res:/UI/Texture/classes/insider/text_styles_18.png'))
    912 
    913         md.AddEntry(text='Set Font Size', subMenuData=font_size_submenus)
    914         md.AddSeparator()
    915 
    916     def GetMenuMoreOptions(self):
    917         md = MenuData()
    918         if session.role & ROLE_PROGRAMMER:
    919             self.AddDeveloperMenu(md)
    920         return md
    921 
    922     def DebugReload(self, *args):
    923         self.Reload(self)
    924 
    925     def GetWindowLinkData(self):
    926         return None
    927 
    928     def Prepare_Background_(self):
    929         self.sr.underlay = WindowUnderlay(parent=self, padding=self.GetWindowBorderPadding())
    930 
    931     def GetDefaultWndIcon(self):
    932         return self.default_iconNum
    933 
    934     def GetNeocomGroupIcon(self):
    935         return self.icon
    936 
    937     def GetNeocomGroupLabel(self):
    938         return self.GetCaption()
    939 
    940     def EnableLightBackground(self):
    941         self.sr.underlay.EnableLightBackground()
    942         self._SetLightBackground(True)
    943 
    944     def DisableLightBackground(self):
    945         self.sr.underlay.DisableLightBackground()
    946         self._SetLightBackground(False)
    947 
    948     def IsLightBackgroundConfigurable(self, *args):
    949         return self.default_isLightBackgroundConfigurable
    950 
    951     def IsLightBackgroundEnabled(self):
    952         default = self.default_isLightBackground
    953         if callable(self.default_isLightBackground):
    954             default = default()
    955         return self.GetRegisteredState('isLightBackground', default)
    956 
    957     def _SetLightBackground(self, isLightBackground):
    958         self.RegisterState('isLightBackground', isLightBackground)
    959         self._emit_on_light_background_changed()
    960 
    961     def IsOverlayed(self):
    962         return self.GetRegisteredState('isOverlayed', self.default_isOverlayed)
    963 
    964     def SetOverlayed(self):
    965         self.RegisterState('isOverlayed', True)
    966 
    967     def SetNotOverlayed(self):
    968         self.RegisterState('isOverlayed', False)
    969 
    970     def IsBlinking(self):
    971         return self.isBlinking
    972 
    973     def SetBlinking(self):
    974         if uicore.registry.GetActive() == self:
    975             return
    976         self.isBlinking = True
    977         sm.ScatterEvent('OnWindowStartBlinking', self)
    978 
    979     def SetNotBlinking(self):
    980         self.isBlinking = False
    981         sm.ScatterEvent('OnWindowStopBlinking', self)
    982 
    983     def _handle_start_scale(self, sides):
    984         sides = [ LEGACY_SIDE_NAME_BY_SIDE[side] for side in sides ]
    985         self._StartScale(sides=sides)
    986 
    987     def _handle_end_scale(self, sides):
    988         self.EndScale(None)
    989 
    990     def _is_side_resizable(self, side):
    991         if not self.IsResizeable() or self.stacked:
    992             return False
    993         elif side in {Side.LEFT, Side.RIGHT}:
    994             return self.fixed_width is None
    995         elif side in {Side.TOP, Side.BOTTOM}:
    996             return self.fixed_height is None
    997         else:
    998             return True
    999 
   1000     def _get_resizer_padding(self):
   1001         return (0, 0, 0, 0)
   1002 
   1003     def _get_resizer_inner_padding(self):
   1004         margin_left, margin_top, margin_right, margin_bottom = self.GetWindowBorderPadding()
   1005         _, border_top, _, _ = self.GetWindowBorderSize()
   1006         pad_top = margin_top
   1007         if self.active:
   1008             pad_top += border_top
   1009         return (margin_left,
   1010          pad_top,
   1011          margin_right,
   1012          margin_bottom)
   1013 
   1014     def _update_resizer_padding(self):
   1015         self.__resizer.padding = self._get_resizer_padding()
   1016         self.__resizer.inner_padding = self._get_resizer_inner_padding()
   1017 
   1018     def Prepare_ScaleAreas_(self):
   1019         if self.__resizer is None:
   1020             index = 0
   1021             if self.__window_controls_cont is not None:
   1022                 index = self.__window_controls_cont.GetOrder() + 1
   1023             self.__resizer = Resizer(parent=self, idx=index, locked_sides=filter(lambda s: not self._is_side_resizable(s), Side.iter()), on_start_scale=self._handle_start_scale, on_end_scale=self._handle_end_scale, padding=self._get_resizer_padding(), inner_padding=self._get_resizer_inner_padding())
   1024         else:
   1025             self._update_resizer_padding()
   1026             self.__resizer.locked_sides = [ side for side in Side.iter() if not self._is_side_resizable(side) ]
   1027 
   1028     def Prepare_LoadingIndicator_(self):
   1029         if self.sr.loadingParent:
   1030             self.sr.loadingParent.Close()
   1031         self.sr.loadingParent = Container(name='__loadingParent', parent=self.sr.maincontainer, state=uiconst.UI_HIDDEN, idx=0)
   1032         self.sr.loadingIndicator = AnimSprite(parent=self.sr.loadingParent, state=uiconst.UI_HIDDEN, align=uiconst.TOPRIGHT, left=5)
   1033         self.sr.loadingIndicator.icons = [ 'ui_38_16_%s' % (210 + i) for i in xrange(8) ]
   1034 
   1035     def GetStackID(self):
   1036         window_id = self.windowID
   1037         if isinstance(window_id, tuple):
   1038             window_id, _ = window_id
   1039         if not window_id:
   1040             return None
   1041         stack_id_by_window_id = settings.char.windows.Get('stacksWindows', {})
   1042         return stack_id_by_window_id.get(window_id, self.stackID)
   1043 
   1044     def RegisterStackID(self, stack = None):
   1045         window_id = self.windowID
   1046         if isinstance(window_id, tuple):
   1047             window_id, _ = window_id
   1048         if not window_id:
   1049             return
   1050         stack_id = None
   1051         if stack is not None:
   1052             stack_id = stack.windowID
   1053         stack_id_by_window_id = settings.char.windows.Get('stacksWindows', {})
   1054         stack_id_by_window_id[window_id] = stack_id
   1055         settings.char.windows.Set('stacksWindows', stack_id_by_window_id)
   1056 
   1057     def OnStackInsert(self, stack):
   1058         self.__stack = stack
   1059         self.align = uiconst.TOALL
   1060         self.__ready = False
   1061         self.left = self.top = self.width = self.height = 0
   1062         self.__ready = True
   1063         self._stack_cacheContents = self.cacheContents
   1064         self.cacheContents = False
   1065         self.state = uiconst.UI_HIDDEN
   1066         self.sr.loadingIndicator.Stop()
   1067         self._update_window_controls_visibility()
   1068         self._UpdateContentPadding()
   1069         self._UpdateBorderPadding()
   1070         self.Prepare_ScaleAreas_()
   1071         self._emit_on_stacked_changed()
   1072 
   1073     def OnStackRemove(self, correctpos, dragging, grab):
   1074         self.__stack = None
   1075         self.sr.tab = None
   1076         self.align = uiconst.RELATIVE
   1077         self.state = uiconst.UI_NORMAL
   1078         self.grab = grab
   1079         self.dragMousePosition = (uicore.uilib.x, uicore.uilib.y)
   1080         if dragging:
   1081             uicore.uilib.SetMouseCapture(self)
   1082             uthread.new(self._BeginDrag)
   1083             if self.height < self.GetMinHeight():
   1084                 self.height = self.GetMinHeight()
   1085             if self.width < self.GetMinWidth():
   1086                 self.width = self.GetMinWidth()
   1087         self.ShowBackground()
   1088         self.Prepare_ScaleAreas_()
   1089         if correctpos:
   1090             self.left = uicore.uilib.x - grab[0]
   1091             self.top = uicore.uilib.y - grab[1]
   1092         self._update_window_controls_visibility()
   1093         self._UpdateContentPadding()
   1094         self._UpdateBorderPadding()
   1095         self._emit_on_stacked_changed()
   1096 
   1097     def _UpdateBorderPadding(self):
   1098         if self.stacked:
   1099             self.sr.maincontainer.padding = 0
   1100         else:
   1101             margin_left, margin_top, margin_right, margin_bottom = self.GetWindowBorderPadding()
   1102             border_left, border_top, border_right, border_bottom = self.GetWindowBorderSize()
   1103             self.sr.maincontainer.padding = (margin_left + border_left,
   1104              margin_top + border_top,
   1105              margin_right + border_right,
   1106              margin_bottom + border_bottom)
   1107         self._update_resizer_padding()
   1108 
   1109     def _UpdateHeaderAlignment(self):
   1110         if self.sr.headerParent is not None:
   1111             self.sr.headerParent.align = self._GetHeaderAlignment()
   1112 
   1113     def _GetHeaderAlignment(self):
   1114         if self.extend_content_into_header:
   1115             return uiconst.TOTOP_NOPUSH
   1116         return uiconst.TOTOP
   1117 
   1118     def InitializeSize(self, useDefaultPos = False):
   1119         d = uicore.desktop
   1120         if useDefaultPos:
   1121             left, top, width, height = self.GetDefaultSizeAndPosition()
   1122         else:
   1123             left, top, width, height, dw, dh = self.GetRegisteredPositionAndSize()
   1124         maxWidth, maxHeight = self.GetMaxWidth(), self.GetMaxHeight()
   1125         minWidth, minHeight = self.GetMinWidth(), self.GetMinHeight()
   1126         if self._fixedWidth:
   1127             self.width = self._fixedWidth
   1128         else:
   1129             self.width = min(maxWidth, max(minWidth, min(d.width, width)))
   1130         if self._fixedHeight:
   1131             self.height = self._fixedHeight
   1132         else:
   1133             self.height = min(maxHeight, max(minHeight, min(d.height, height)))
   1134 
   1135     def OnResetWindowSettings(self):
   1136         if self.__stack is not None:
   1137             sm.ChainEvent('ProcessWindowUnstacked', self, self.__stack)
   1138             self.OnStackRemove(0, 0, (0, 0))
   1139         if self.IsMinimized():
   1140             self.Maximize()
   1141         self.state = uiconst.UI_HIDDEN
   1142         self.align = uiconst.TOPLEFT
   1143         self.ShowBackground()
   1144         self.InitializeSize()
   1145         self.InitializeStatesAndPosition()
   1146 
   1147     def InitializeStatesAndPosition(self, showIfInStack = True, useDefaultPos = False, **kwds):
   1148         self.__ready = False
   1149         log.LogInfo('Window.InitializeStatesAndPosition', self.windowID)
   1150         if self.IsLightBackgroundConfigurable():
   1151             if self.IsLightBackgroundEnabled():
   1152                 self.EnableLightBackground()
   1153             else:
   1154                 self.DisableLightBackground()
   1155         if self.IsLocked():
   1156             self.Lock()
   1157         else:
   1158             self.Unlock()
   1159         if self.IsMinimized():
   1160             self._SetMaximized()
   1161         stack = None
   1162         stackID = self.GetStackID()
   1163         from carbonui.window.stack import WindowStack
   1164         if not isinstance(self, WindowStack):
   1165             if self.IsStackable() and stackID:
   1166                 stack = uicore.registry.GetWindow(stackID)
   1167                 if stack and not stack.closing:
   1168                     log.LogInfo('Window.InitializeStatesAndPosition, windowStack already created', self.windowID)
   1169                     stack.InsertWnd(self, False, showIfInStack, 1)
   1170         if not stack or stack.closing:
   1171             collapsed = self.GetRegisteredState('collapsed', False)
   1172             if collapsed:
   1173                 self.Collapse()
   1174             elif self.IsCollapsed():
   1175                 self.Expand()
   1176             self.UpdatePosition(useDefaultPos)
   1177             if stackID is not None and not isinstance(self, self.GetStackClass()) and self.__stackable:
   1178                 self.state = uiconst.UI_HIDDEN
   1179                 log.LogInfo('Window.InitializeStatesAndPosition creating new stack while initializing', self.windowID)
   1180                 stack = uicore.registry.GetStack(stackID, self.GetStackClass())
   1181                 if stack and not stack.closing:
   1182                     stack.InsertWnd(self, True, showIfInStack, 1)
   1183         if not self.InStack() and not self.IsMinimized():
   1184             self.state = uiconst.UI_NORMAL
   1185             self._SetMaximized()
   1186         self._SetOpen(True)
   1187         self.__ready = True
   1188 
   1189     def InitializeFocus(self):
   1190         focus = uicore.registry.GetFocus()
   1191         from carbonui.control.editPlainText import EditPlainTextCore
   1192         from carbonui.control.singlelineedits.baseSingleLineEdit import BaseSingleLineEdit
   1193         editFieldHasFocus = focus and isinstance(focus, (EditPlainTextCore, BaseSingleLineEdit))
   1194         if not editFieldHasFocus:
   1195             uthread.new(uicore.registry.SetFocus, self)
   1196 
   1197     def UpdatePosition(self, useDefaultPos = False):
   1198         if useDefaultPos:
   1199             left, top, width, height = self.GetDefaultSizeAndPosition()
   1200         else:
   1201             left, top, width, height, dw, dh = self.GetRegisteredPositionAndSize()
   1202         left = max(0, min(uicore.desktop.width - self.width, left))
   1203         top = max(0, min(uicore.desktop.height - self.height, top))
   1204         leftpush, rightpush = self.GetSideOffset()
   1205         if left in (0, SNAP_DISTANCE):
   1206             left += leftpush
   1207         elif left + self.width in (uicore.desktop.width, uicore.desktop.width - SNAP_DISTANCE):
   1208             left -= rightpush
   1209         self.left = left
   1210         self.top = top
   1211         self.CheckWndPos()
   1212 
   1213     @classmethod
   1214     def GetSideOffset(cls):
   1215         if uicore.layer.sidePanels:
   1216             return uicore.layer.sidePanels.GetSideOffset()
   1217         return (0, 0)
   1218 
   1219     def RegisterState(self, statename, value):
   1220         RegisterState(self.windowID, statename, value)
   1221 
   1222     def GetRegisteredState(self, statename, default = None):
   1223         return GetRegisteredState(self.windowID, statename, default)
   1224 
   1225     def IsRegisteringPositionAndSizeAllowed(self):
   1226         if self.windowID is None:
   1227             return False
   1228         if self._changing or self.IsMinimized():
   1229             return False
   1230         if not self.InStack():
   1231             if self.GetStackID():
   1232                 return False
   1233             if self.GetAlign() != uiconst.RELATIVE:
   1234                 return False
   1235         return True
   1236 
   1237     def RegisterPositionAndSize(self, key = None, windowID = None):
   1238         if not self.IsRegisteringPositionAndSizeAllowed():
   1239             return
   1240         l, t = self._GetPositionToRegister()
   1241         w, h = self._GetSizeToRegister()
   1242         self._RegisterWindowSizeAndPositionSetting(l, t, w, h, windowID)
   1243 
   1244     def RegisterSize(self):
   1245         if not self.IsRegisteringPositionAndSizeAllowed():
   1246             return
   1247         width, height = self._GetSizeToRegister()
   1248         self._RegisterWindowSizeAndPositionSetting(width=width, height=height)
   1249 
   1250     def _GetPositionToRegister(self):
   1251         if self.stacked:
   1252             l, t = self.stack.left, self.stack.top
   1253         else:
   1254             l, t = self.left, self.top
   1255         if l is not None:
   1256             l = self._AccountForSideAutoSnapDistance(l)
   1257         return (l, t)
   1258 
   1259     def _AccountForSideAutoSnapDistance(self, l):
   1260         dw, dh = uicore.desktop.width, uicore.desktop.height
   1261         leftOffset, rightOffset = self.GetSideOffset()
   1262         if l in (leftOffset, leftOffset + 16):
   1263             l -= leftOffset
   1264         elif l + self.width in (dw - rightOffset, dw - rightOffset - 16):
   1265             l += rightOffset
   1266         return l
   1267 
   1268     def _GetSizeToRegister(self):
   1269         if self.stacked:
   1270             w, h = self.stack.width, self.stack.height
   1271         else:
   1272             w, h = self.width, self.height
   1273         if self.IsCollapsed():
   1274             h = None
   1275         return (w, h)
   1276 
   1277     def _RegisterWindowSizeAndPositionSetting(self, left = None, top = None, width = None, height = None, windowID = None):
   1278         if windowID is None:
   1279             windowID = self._GetWindowID()
   1280         allWindows = settings.char.windows.Get('windowSizesAndPositions_1', {})
   1281         regLeft, regTop, regWidth, regHeight, _, _ = self.GetRegisteredPositionAndSizeByClass(windowID)
   1282         allWindows[windowID] = (left if left is not None else regLeft,
   1283          top if top is not None else regTop,
   1284          width if width is not None else regWidth,
   1285          height if height is not None else regHeight,
   1286          uicore.desktop.width,
   1287          uicore.desktop.height)
   1288         settings.char.windows.Set('windowSizesAndPositions_1', allWindows)
   1289 
   1290     def _GetWindowID(self):
   1291         if type(self.windowID) == tuple:
   1292             windowID, _ = self.windowID
   1293         else:
   1294             windowID = self.windowID
   1295         return windowID
   1296 
   1297     def SetFixedHeightFromContentHeight(self, height):
   1298         window_controls_height = self.__window_controls_cont.padTop + self.__window_controls_cont.padBottom
   1299         fixed_height = height + self.header_height + window_controls_height
   1300         self.SetFixedHeight(fixed_height, force=True)
   1301 
   1302     def SetFixedHeight(self, height = None, force = False):
   1303         if self._fixedHeight != height or force:
   1304             self._fixedHeight = height
   1305             if height is not None and not self.stacked:
   1306                 self.height = height
   1307             self.Prepare_ScaleAreas_()
   1308             self._emit_fixed_height_changed()
   1309 
   1310     def SetFixedWidth(self, width = None):
   1311         if self._fixedWidth != width:
   1312             self._fixedWidth = width
   1313             if width is not None and not self.stacked:
   1314                 self.width = width
   1315             self.Prepare_ScaleAreas_()
   1316             self._emit_fixed_width_changed()
   1317 
   1318     def GetMinSize(self, *args, **kw):
   1319         width, height = (0, 0)
   1320         if self.sr.headerParent and self.sr.headerParent.state != uiconst.UI_HIDDEN:
   1321             height += self.sr.headerParent.height
   1322         return (width, height)
   1323 
   1324     def IsCurrentDialog(self):
   1325         return getattr(self, 'isDialog', False) and (not getattr(self, 'isModal', False) or uicore.registry.GetModalWindow() == self)
   1326 
   1327     def Close(self, setClosed = False, *args, **kwds):
   1328         if self.__closing:
   1329             return
   1330         self.__closing = True
   1331         if setClosed:
   1332             self._SetOpen(False)
   1333         sm.ScatterEvent('OnWindowClosed', self.windowID, self.windowInstanceID, self.__class__)
   1334         if self.__modal_result:
   1335             self._SetModalResult(uiconst.ID_CLOSE)
   1336         self.state = uiconst.UI_HIDDEN
   1337         if self.__header is not None:
   1338             self.__header.unmount(self)
   1339             self.__header.Close()
   1340             self.__header = None
   1341         modalParent = self.sr.modalParent
   1342         stack = self.stack
   1343         super(Window, self).Close()
   1344         if uicore.registry.GetActive() == self:
   1345             if stack is not None:
   1346                 uicore.registry.SetFocus(stack)
   1347             else:
   1348                 uicore.registry.SetFocus(None)
   1349         if self.__modal_result:
   1350             self._SetModalResult(uiconst.ID_CLOSE)
   1351         if modalParent is not None and not modalParent.destroyed:
   1352             modalParent.Close()
   1353         uicore.registry.UnregisterWindow(self)
   1354         self.UpdateIntersectionBackground()
   1355         if stack is not None and not stack.destroyed:
   1356             stack.Check(checknone=1)
   1357 
   1358     def HasPendingModalResult(self):
   1359         return bool(self.__modal_result)
   1360 
   1361     def CloseByUser(self, *args):
   1362         if self.closing:
   1363             return
   1364         if not self.killable or is_blocked('close', self.windowID):
   1365             return
   1366         self._LogWindowClosed()
   1367         PlaySound(uiconst.SOUND_CLOSE)
   1368         self.Close(setClosed=True)
   1369 
   1370     def _OnResize(self, *args, **kw):
   1371         super(Window, self)._OnResize(*args)
   1372         self.UpdateIntersectionBackground()
   1373         if self.__ready:
   1374             self.RegisterPositionAndSize()
   1375         if self.OnResize_:
   1376             self.OnResize_(self)
   1377 
   1378     def UpdateAlignment(self, budgetLeft = 0, budgetTop = 0, budgetWidth = 0, budgetHeight = 0, updateChildrenOnly = False):
   1379         dirty = self._forceUpdateAlignment or self._alignmentDirty
   1380         ret = super(Window, self).UpdateAlignment(budgetLeft, budgetTop, budgetWidth, budgetHeight, updateChildrenOnly)
   1381         if dirty:
   1382             for sceneCont in self.__scene_containers:
   1383                 if not sceneCont.destroyed:
   1384                     sceneCont.UpdateViewPort()
   1385 
   1386         return ret
   1387 
   1388     def HideHeader(self):
   1389         self.collapsible = False
   1390         self.show_window_controls = False
   1391         if self.sr.headerParent:
   1392             self.sr.headerParent.state = uiconst.UI_HIDDEN
   1393 
   1394     def ShowHeader(self):
   1395         self.collapsible = True
   1396         self.show_window_controls = True
   1397         if self.sr.headerParent:
   1398             self.sr.headerParent.state = uiconst.UI_PICKCHILDREN
   1399 
   1400     def ShowBackground(self):
   1401         self.ShowUnderlay()
   1402 
   1403     def HideBackground(self):
   1404         self.HideUnderlay()
   1405 
   1406     def HideUnderlay(self):
   1407         if self.sr.underlay:
   1408             self.sr.underlay.state = uiconst.UI_HIDDEN
   1409 
   1410     def ShowUnderlay(self):
   1411         if self.sr.underlay:
   1412             self.sr.underlay.state = uiconst.UI_DISABLED
   1413 
   1414     def Blink(self):
   1415         if self.state == uiconst.UI_HIDDEN and self.sr.tab and hasattr(self.sr.tab, 'Blink'):
   1416             self.sr.tab.Blink(1)
   1417 
   1418     def GetMenu(self, *args):
   1419         entries = []
   1420         if self.killable:
   1421             text = MenuLabel('/Carbon/UI/Common/Close')
   1422             entry = MenuEntryData(text=text, func=self.CloseByUser)
   1423             entries.append(entry)
   1424         if not self.stacked and self.minimizable and not getattr(self, 'isModal', 0):
   1425             if self.IsMinimized():
   1426                 text = MenuLabel('/Carbon/UI/Controls/Window/Maximize')
   1427             else:
   1428                 text = MenuLabel('/Carbon/UI/Controls/Window/Minimize')
   1429             entry = MenuEntryData(text=text, func=self.ToggleMinimize)
   1430             entries.append(entry)
   1431         from carbonui.control.contextMenu.menuDataFactory import CreateMenuDataWithEntries
   1432         return CreateMenuDataWithEntries(entries)
   1433 
   1434     def ShowLoad(self, doBlock = True):
   1435         self.__hide_loading_indicator_requested = False
   1436         if self.sr.loadingParent:
   1437             if doBlock:
   1438                 self.sr.loadingParent.state = uiconst.UI_NORMAL
   1439             else:
   1440                 self.sr.loadingParent.state = uiconst.UI_DISABLED
   1441             if self.sr.loadingIndicator and hasattr(self.sr.loadingIndicator, 'Play'):
   1442                 if not (self.InStack() or self.GetAlign() != uiconst.RELATIVE):
   1443                     self.sr.loadingIndicator.sr.hint = localization.GetByLabel('/Carbon/UI/Controls/Window/Loading')
   1444                     self.sr.loadingIndicator.Play()
   1445         if not self.closing and self.__hide_loading_indicator_requested:
   1446             self.HideLoad()
   1447 
   1448     def HideLoad(self):
   1449         if self.sr.loadingIndicator and hasattr(self.sr.loadingIndicator, 'Stop'):
   1450             self.sr.loadingIndicator.Stop()
   1451         if self.sr.loadingParent:
   1452             self.sr.loadingParent.state = uiconst.UI_HIDDEN
   1453         self.__hide_loading_indicator_requested = True
   1454 
   1455     def ShowModal(self):
   1456         return self.ShowDialog(modal=True)
   1457 
   1458     def ShowDialog(self, modal = False, state = uiconst.UI_NORMAL, fillOpacity = uiconst.DEFAULT_MODAL_OPACITY, closeWhenClicked = False, sceneSaturation = uiconst.DEFAULT_MODAL_SCENE_SATURATION):
   1459         self.isDialog = True
   1460         self.isModal = modal
   1461         self.state = uiconst.UI_HIDDEN
   1462         if modal:
   1463             self.minimizable = False
   1464             if self.parent and not self.parent.destroyed and self.parent.name[:8] == 'l_modal_':
   1465                 self.parent.SetOrder(0)
   1466             else:
   1467                 layer = self._create_modal_layer(name='l_modal_%s' % self.name, close_when_clicked=closeWhenClicked)
   1468                 self.SetParent(layer)
   1469                 self.sr.modalParent = layer
   1470                 self.ModalPosition()
   1471             uicore.registry.AddModalWindow(self, fillOpacity=fillOpacity, sceneSaturation=sceneSaturation)
   1472         self.__modal_result = uthread.Channel()
   1473         self.state = state
   1474         if modal:
   1475             uicore.registry.SetFocus(self)
   1476         self.HideLoad()
   1477         ret = self.__modal_result.receive()
   1478         return ret
   1479 
   1480     def _create_modal_layer(self, name, close_when_clicked = False):
   1481         layer = Container(name=name, state=uiconst.UI_NORMAL, parent=uicore.layer.modal, idx=0)
   1482         if close_when_clicked:
   1483             layer.OnClick = self.CloseByUser
   1484         return layer
   1485 
   1486     def ModalPosition(self):
   1487         otherModal = uicore.registry.GetModalWindow()
   1488         if self.__ShouldAdjustNewModal(otherModal):
   1489             self.left = otherModal.left + 16
   1490             self.top = otherModal.top + 16
   1491             if self.left + self.width > uicore.desktop.width:
   1492                 self.left = 0
   1493             if self.top + self.height > uicore.desktop.height:
   1494                 self.top = 0
   1495         else:
   1496             cameraOffset = self.__class__.GetDefaultLeftOffset(self.width, align=uiconst.CENTER, left=0)
   1497             self.left = (uicore.desktop.width - self.width) / 2 + cameraOffset
   1498             self.top = (uicore.desktop.height - self.height) / 2
   1499 
   1500     def __ShouldAdjustNewModal(self, otherModal):
   1501         if not otherModal:
   1502             return False
   1503         if not getattr(otherModal, 'triggersModalAdjustment', True):
   1504             return False
   1505         if otherModal.state != uiconst.UI_NORMAL:
   1506             return False
   1507         if otherModal.parent.state != uiconst.UI_NORMAL:
   1508             return False
   1509         return True
   1510 
   1511     def ButtonResult(self, button, *args):
   1512         if self.IsCurrentDialog():
   1513             self.SetModalResult(button.btn_modalresult, 'ButtonResult')
   1514 
   1515     def DefineButtons(self, buttons, okLabel = None, okFunc = 'default', args = 'self', cancelLabel = None, cancelFunc = 'default', okModalResult = 'default', default = None):
   1516         if okLabel is None:
   1517             okLabel = localization.GetByLabel('UI/Common/Buttons/OK')
   1518         if cancelLabel is None:
   1519             cancelLabel = localization.GetByLabel('UI/Common/Buttons/Cancel')
   1520         buttonHeight = 32
   1521         if getattr(self.sr, 'bottom', None) is None:
   1522             self.sr.bottom = self.FindChild('bottom')
   1523             if not self.sr.bottom:
   1524                 self.sr.bottom = Container(name='bottom', parent=self.GetMainArea(), align=uiconst.TOBOTTOM, padTop=4, idx=0)
   1525         if self.sr.bottom is None:
   1526             return
   1527         self.sr.bottom.Flush()
   1528         if buttons is None:
   1529             self.sr.bottom.state = uiconst.UI_HIDDEN
   1530             return
   1531         self.sr.bottom.height = buttonHeight
   1532         if okModalResult == 'default':
   1533             okModalResult = uiconst.ID_OK
   1534         if okFunc == 'default':
   1535             okFunc = self.ConfirmFunction
   1536         if cancelFunc == 'default':
   1537             cancelFunc = self.ButtonResult
   1538 
   1539         def create_ok_button():
   1540             is_ok_default = okModalResult == default if default is not None else True
   1541             return Button(name='ok_dialog_button', label=okLabel, func=okFunc, args=args, btn_modalresult=okModalResult, btn_default=is_ok_default, variant=ButtonVariant.PRIMARY if is_ok_default else ButtonVariant.NORMAL)
   1542 
   1543         def create_cancel_button():
   1544             is_cancel_default = uiconst.ID_CANCEL == default if default is not None else False
   1545             return Button(name='cancel_dialog_button', label=cancelLabel, func=cancelFunc, args=args, btn_modalresult=uiconst.ID_CANCEL, btn_default=is_cancel_default, btn_cancel=True, variant=ButtonVariant.PRIMARY if is_cancel_default else ButtonVariant.NORMAL)
   1546 
   1547         def create_close_button():
   1548             is_close_default = uiconst.ID_CLOSE == default if default is not None else False
   1549             return Button(name='close_dialog_button', label=localization.GetByLabel('UI/Common/Buttons/Close'), func=self.CloseByUser, args=args, btn_modalresult=uiconst.ID_CLOSE, btn_default=is_close_default, btn_cancel=True, variant=ButtonVariant.PRIMARY if is_close_default else ButtonVariant.NORMAL)
   1550 
   1551         def create_yes_button():
   1552             is_yes_default = uiconst.ID_YES == default if default is not None else True
   1553             return Button(name='yes_dialog_button', label=localization.GetByLabel('UI/Common/Buttons/Yes'), func=self.ButtonResult, args=args, btn_modalresult=uiconst.ID_YES, btn_default=is_yes_default, variant=ButtonVariant.PRIMARY if is_yes_default else ButtonVariant.NORMAL)
   1554 
   1555         def create_no_button():
   1556             is_no_default = uiconst.ID_NO == default if default is not None else False
   1557             return Button(name='no_dialog_button', label=localization.GetByLabel('UI/Common/Buttons/No'), func=self.ButtonResult, args=args, btn_modalresult=uiconst.ID_NO, btn_default=is_no_default, variant=ButtonVariant.PRIMARY if is_no_default else ButtonVariant.NORMAL)
   1558 
   1559         if isinstance(buttons, (types.ListType, types.TupleType)):
   1560             button_data = []
   1561             for btn in buttons:
   1562                 is_default = btn.id == default if default is not None else False
   1563                 button_data.append(Button(label=btn.label, func=self.ButtonResult, args=None, btn_modalresult=btn.id, btn_default=is_default, btn_cancel=btn.id == uiconst.ID_CANCEL, variant=ButtonVariant.PRIMARY if is_default else ButtonVariant.NORMAL))
   1564 
   1565         elif buttons == uiconst.OK:
   1566             button_data = [create_ok_button()]
   1567         elif buttons == uiconst.OKCANCEL:
   1568             button_data = [create_ok_button(), create_cancel_button()]
   1569         elif buttons == uiconst.OKCLOSE:
   1570             button_data = [create_ok_button(), create_close_button()]
   1571         elif buttons == uiconst.YESNO:
   1572             button_data = [create_yes_button(), create_no_button()]
   1573         elif buttons == uiconst.YESNOCANCEL:
   1574             button_data = [create_yes_button(), create_no_button(), create_cancel_button()]
   1575         elif buttons == uiconst.CLOSE:
   1576             button_data = [create_close_button()]
   1577         elif isinstance(okLabel, (types.ListType, types.TupleType)):
   1578             button_data = []
   1579             for index in xrange(len(okLabel)):
   1580                 label = okLabel[index]
   1581                 additionalArguments = {'Function': okFunc,
   1582                  'Arguments': args,
   1583                  'Cancel Label': cancelLabel,
   1584                  'Cancel Function': cancelFunc,
   1585                  'Modal Result': okModalResult,
   1586                  'Default': default}
   1587                 for argName in additionalArguments:
   1588                     if isinstance(additionalArguments[argName], (types.ListType, types.TupleType)) and len(additionalArguments[argName]) > index:
   1589                         additionalArguments[argName] = additionalArguments[argName][index]
   1590 
   1591                 is_default = additionalArguments['Modal Result'] == default if default is not None else additionalArguments['Default']
   1592                 button_data.append(Button(label=label, func=additionalArguments['Function'], args=additionalArguments['Arguments'], btn_modalresult=additionalArguments['Modal Result'], btn_default=is_default, btn_cancel=additionalArguments['Modal Result'] == uiconst.ID_CANCEL, variant=ButtonVariant.PRIMARY if is_default else ButtonVariant.NORMAL))
   1593 
   1594         else:
   1595             button_data = [create_ok_button()]
   1596         buttonGroup = ButtonGroup(parent=self.sr.bottom, align=uiconst.TOBOTTOM, buttons=button_data, button_size_mode=ButtonSizeMode.STRETCH)
   1597         self.sr.bottom.height = max(buttonHeight, buttonGroup.height)
   1598         self.sr.bottom.state = uiconst.UI_PICKCHILDREN
   1599 
   1600     def ConfirmFunction(self, button, *args):
   1601         uicore.registry.Confirm(button)
   1602 
   1603     def SetScope(self, scope):
   1604         self.scope = scope
   1605 
   1606     def SetModalResult(self, result, caller = None):
   1607         if self.__modal_result:
   1608             self._SetModalResult(result)
   1609             self.Close()
   1610 
   1611     def _SetModalResult(self, result):
   1612         if self.__modal_result:
   1613             uicore.registry.RemoveModalWindow(self)
   1614             self.__modal_result.send(result)
   1615             self.__modal_result = None
   1616 
   1617     def MakeUnResizeable(self):
   1618         self._resizeable = False
   1619         self.Prepare_ScaleAreas_()
   1620         self.MakeUnstackable()
   1621 
   1622     def MakeUnstackable(self):
   1623         self.__stackable = False
   1624 
   1625     def MakeStackable(self):
   1626         self.__stackable = True
   1627 
   1628     def IsResizeable(self):
   1629         return self._resizeable and not self.IsLocked()
   1630 
   1631     def SetMinSize(self, size, refresh = 0):
   1632         self.minsize = size
   1633         if self.GetAlign() == uiconst.RELATIVE and not self.InStack():
   1634             if not self.IsCollapsed():
   1635                 if self.height < self.minsize[1]:
   1636                     self.height = self.minsize[1]
   1637                 if self.width < self.minsize[0]:
   1638                     self.width = self.minsize[0]
   1639                 if refresh:
   1640                     self.width, self.height = self.minsize
   1641         if self.stacked:
   1642             self.stack.SetMinWH()
   1643 
   1644     def SetMaxSize(self, size, refresh = 0):
   1645         self.maxsize = size
   1646         maxWidth, maxHeight = size
   1647         if self.GetAlign() == uiconst.RELATIVE:
   1648             if not self.IsCollapsed():
   1649                 if maxWidth is not None and self.width > maxWidth:
   1650                     self.width = maxWidth
   1651                 if maxHeight is not None and self.height > maxHeight:
   1652                     self.height = maxHeight
   1653                 if refresh:
   1654                     if maxWidth is not None:
   1655                         self.width = maxWidth
   1656                     if maxHeight is not None:
   1657                         self.height = maxHeight
   1658         if self.stacked:
   1659             self.stack.Check()
   1660 
   1661     def CheckMaxSize(self):
   1662         maxWidth, maxHeight = self.maxsize
   1663         if maxWidth and self.width > maxWidth:
   1664             self.width = maxWidth
   1665         if maxHeight and self.height > maxHeight:
   1666             self.height = maxHeight
   1667 
   1668     def _SetCaptionText(self, caption):
   1669         if localization.IsValidLabel(caption):
   1670             self.__caption = localization.GetByLabel(caption)
   1671         else:
   1672             self.__caption = caption
   1673 
   1674     def LockHeight(self, height):
   1675         self._fixedHeight = height
   1676         if not self.InStack():
   1677             self.height = height
   1678 
   1679     def UnlockHeight(self):
   1680         self._fixedHeight = None
   1681 
   1682     def LockWidth(self, width):
   1683         self._fixedWidth = width
   1684         if not self.stacked:
   1685             self.width = width
   1686 
   1687     def UnlockWidth(self):
   1688         self._fixedWidth = None
   1689 
   1690     def UpdateIntersectionBackground(self):
   1691         if not self.InStack():
   1692             sm.GetService('window').UpdateIntersectionBackground()
   1693 
   1694     def Maximize(self, retainOrder = False, *args, **kwds):
   1695         if self.closing or self._changing:
   1696             return
   1697         if is_blocked('restore', self.windowID):
   1698             return
   1699         self._changing = True
   1700         if self.stacked:
   1701             self._SetMaximized()
   1702             self.stack.ShowWnd(self)
   1703             self.stack.Maximize()
   1704             self._changing = False
   1705             return
   1706         self.OnStartMaximize_(self)
   1707         if self.IsCollapsed():
   1708             self.Expand(0)
   1709         wasMinimized = self.IsMinimized()
   1710         if not retainOrder:
   1711             uicore.registry.SetFocus(self)
   1712         self._SetMaximized()
   1713         self.state = uiconst.UI_NORMAL
   1714         self.InitializeSize()
   1715         self.InitializeStatesAndPosition()
   1716         kick = [ w for w in self.Find('trinity.Tr2Sprite2dContainer') + self.Find('trinity.Tr2Sprite2d') if hasattr(w, '_OnResize') ]
   1717         for each in kick:
   1718             if hasattr(each, '_OnResize'):
   1719                 each._OnResize()
   1720 
   1721         self.OnEndMaximize_(self)
   1722         self._changing = False
   1723         self._NotifyOfMaximized(wasMinimized)
   1724         self.UpdateIntersectionBackground()
   1725 
   1726     def _NotifyOfMaximized(self, wasMinimized):
   1727         sm.ScatterEvent('OnWindowMaximized', self, wasMinimized)
   1728 
   1729     def Minimize(self, animate = True):
   1730         if not self.minimizable:
   1731             return
   1732         if self.stacked:
   1733             return self.stack.Minimize(animate=animate)
   1734         PlaySound(uiconst.SOUND_COLLAPSE)
   1735         uicore.registry.SetFocus(None)
   1736         self._Minimize(animate=animate)
   1737         self.UpdateIntersectionBackground()
   1738 
   1739     def _Minimize(self, animate = True):
   1740         if self.closing or self.IsMinimized() or self._changing:
   1741             return
   1742         self.OnStartMinimize_(self)
   1743         self._changing = True
   1744         self._SetMinimized()
   1745         if animate:
   1746             self._AnimateMinimize()
   1747         self.state = uiconst.UI_HIDDEN
   1748         self.OnEndMinimize_(self)
   1749         self._changing = False
   1750         self._NotifyOfMinimized()
   1751 
   1752     def _NotifyOfMinimized(self):
   1753         sm.ScatterEvent('OnWindowMinimized', self)
   1754 
   1755     def _AnimateMinimize(self):
   1756         x, y = sm.GetService('neocom').GetMinimizeToPos(self)
   1757         x = float(x) / uicore.desktop.width
   1758         y = float(y) / uicore.desktop.height
   1759         t = Transform(parent=uicore.layer.main, state=uiconst.UI_DISABLED, align=uiconst.TOALL, scalingCenter=(x, y), idx=0)
   1760         wasCacheContent = self.cacheContents
   1761         self.cacheContents = False
   1762         self.SetParent(t)
   1763         animations.Tr2DFlipOut(t, duration=0.3)
   1764         animations.FadeOut(t, duration=0.25, sleep=True)
   1765         self.SetParent(uicore.layer.main)
   1766         self.cacheContents = wasCacheContent
   1767         t.Close()
   1768 
   1769     def IsMinimized(self):
   1770         if self.stacked:
   1771             return bool(self.stack.IsMinimized())
   1772         return bool(self.GetRegisteredState('minimized'))
   1773 
   1774     def _SetMinimized(self):
   1775         self.RegisterState('minimized', True)
   1776 
   1777     def _SetMaximized(self):
   1778         self.RegisterState('minimized', False)
   1779 
   1780     def _SetCollapsed(self, isCollapsed):
   1781         self.RegisterState('collapsed', isCollapsed)
   1782         self.__collapsed = bool(isCollapsed)
   1783         self._emit_on_collapsed_changed()
   1784 
   1785     def _SetOpen(self, isOpen):
   1786         self.RegisterState('open', isOpen)
   1787 
   1788     def _SetHovered(self, isHovered):
   1789         wasHovered = self.__hovered
   1790         self.__hovered = isHovered
   1791         if not wasHovered and isHovered:
   1792             sm.ScatterEvent('OnWindowMouseEnter', self.windowID)
   1793         elif wasHovered and not isHovered:
   1794             sm.ScatterEvent('OnWindowMouseExit', self.windowID)
   1795 
   1796     def IsHovered(self):
   1797         return self.__hovered
   1798 
   1799     def OnMouseEnter(self, *args):
   1800         self._SetHovered(isHovered=True)
   1801 
   1802     def OnMouseExit(self, *args):
   1803         self._SetHovered(isHovered=False)
   1804 
   1805     def OnMouseDown(self, btnNum, *args):
   1806         if btnNum != uiconst.MOUSELEFT:
   1807             return
   1808         self.OnMouseDown_(self)
   1809         self.dragMousePosition = (uicore.uilib.x, uicore.uilib.y)
   1810         if self.IsLocked():
   1811             self.DisableDrag()
   1812         else:
   1813             self.EnableDrag()
   1814         self.SetOrder(0)
   1815 
   1816     def GetDragClipRects(self, shiftGroup, ctrlGroup):
   1817         ml, mt, mw, mh = self.GetAbsolute()
   1818         sl, st, sw, sh = self.GetGroupAbsolute(shiftGroup)
   1819         cl, ct, cw, ch = self.GetGroupAbsolute(ctrlGroup)
   1820         pl, pt, pw, ph = self.parent.GetAbsolute()
   1821         x, y = self.dragMousePosition
   1822         return ((0,
   1823           0,
   1824           pw,
   1825           ph - (mt + self.GetCollapsedHeight() - y) + 1), (x - sl,
   1826           y - st,
   1827           pw - (sl + sw - x) + 1,
   1828           ph - (st + sh - y) + 1), (x - cl,
   1829           y - ct,
   1830           pw - (cl + cw - x) + 1,
   1831           ph - (ct + ch - y) + 1))
   1832 
   1833     def _BeginDrag(self):
   1834         self._dragging = True
   1835         ctrlDragWindows = self.FindConnectingWindows('bottom', includeLocked=False)
   1836         shiftDragWindows = self.FindConnectingWindows(includeLocked=False)
   1837         self.PrepareWindowsForMove(shiftDragWindows)
   1838         while not self.closing and self.IsBeingDragged():
   1839             if uicore.uilib.mouseTravel > 1:
   1840                 break
   1841             blue.pyos.synchro.Yield()
   1842 
   1843         if self.closing or not self.IsBeingDragged() or self.__drag_started:
   1844             return
   1845         if ctrlDragWindows is None:
   1846             ctrlDragWindows = self.FindConnectingWindows('bottom', includeLocked=False)
   1847         if shiftDragWindows is None:
   1848             shiftDragWindows = self.FindConnectingWindows(includeLocked=False)
   1849             self.PrepareWindowsForMove(shiftDragWindows)
   1850         snapGrid = None
   1851         snapGroup = None
   1852         snapIndicator = self.GetSnapIndicator()
   1853         allGrid, myGrid, shiftGrid, ctrlGrid = self.CreateSnapGrid(shiftDragWindows, ctrlDragWindows)
   1854         myRect, shiftRect, ctrlRect = self.GetDragClipRects(shiftDragWindows, ctrlDragWindows)
   1855         initMouseX, initMouseY = self.dragMousePosition
   1856         self.__drag_started = True
   1857         while not self.closing and self.IsBeingDragged() and uicore.uilib.leftbtn and self.GetAlign() == uiconst.RELATIVE:
   1858             self.state = uiconst.UI_DISABLED
   1859             shift = uicore.uilib.Key(uiconst.VK_SHIFT)
   1860             ctrl = uicore.uilib.Key(uiconst.VK_CONTROL)
   1861             self.left = uicore.uilib.x - initMouseX + self.preDragAbs[0]
   1862             self.top = uicore.uilib.y - initMouseY + self.preDragAbs[1]
   1863             for each in shiftDragWindows:
   1864                 if each is self:
   1865                     continue
   1866                 pl, pt, pw, ph = each.preDragAbs
   1867                 if shift or ctrl and each in ctrlDragWindows:
   1868                     each.left = uicore.uilib.x - initMouseX + pl
   1869                     each.top = uicore.uilib.y - initMouseY + pt
   1870                 else:
   1871                     each.left = pl
   1872                     each.top = pt
   1873 
   1874             self.SetOrder(0)
   1875             self.FindWindowToStackTo()
   1876             if shift:
   1877                 snapGrid = shiftGrid
   1878                 snapGroup = shiftDragWindows
   1879                 cursorRect = shiftRect
   1880             elif ctrl:
   1881                 cursorRect = ctrlRect
   1882                 snapGrid = ctrlGrid
   1883                 snapGroup = ctrlDragWindows
   1884             else:
   1885                 snapGrid = myGrid
   1886                 snapGroup = [self]
   1887                 cursorRect = myRect
   1888             uicore.uilib.ClipCursor(*cursorRect)
   1889             self.ShowSnapEdges_Moving(snapGroup, snapGrid, snapIndicator=snapIndicator)
   1890             self.OnDragTick()
   1891             blue.pyos.synchro.Yield()
   1892 
   1893         if not self.InStack() and self.IsStackable():
   1894             trystackto = self.FindWindowToStackTo()
   1895             self.ClearStackIndication()
   1896             if trystackto:
   1897                 if trystackto.stacked:
   1898                     trystackto.stack.CheckStackWith(self)
   1899                 else:
   1900                     trystackto.CheckStackWith(self)
   1901         uicore.uilib.UnclipCursor()
   1902         if self.closing:
   1903             return
   1904         if not self.InStack() and snapGrid and snapGroup:
   1905             self.ShowSnapEdges_Moving(snapGroup, snapGrid, doSnap=True)
   1906         self.CleanupParent('snapIndicator')
   1907         self.ClearStackIndication()
   1908         if self.InStack():
   1909             self.state = uiconst.UI_PICKCHILDREN
   1910         else:
   1911             self.state = uiconst.UI_NORMAL
   1912         self.AdjustWindowPositionToFitScreen()
   1913         if not self.closing:
   1914             self.__drag_started = False
   1915 
   1916     def AdjustWindowPositionToFitScreen(self):
   1917         if self.stacked:
   1918             return
   1919         maxLeftAllowed = uicore.desktop.width - self.WINDOW_NEVER_OFFSCREEN_BUFFER
   1920         if self.left > maxLeftAllowed:
   1921             self.left = maxLeftAllowed
   1922         minLeftAllowed = self.WINDOW_NEVER_OFFSCREEN_BUFFER - self.width
   1923         if self.left < minLeftAllowed:
   1924             self.left = minLeftAllowed
   1925         maxTopAllowed = uicore.desktop.height - self.WINDOW_NEVER_OFFSCREEN_BUFFER
   1926         if self.top > maxTopAllowed:
   1927             self.top = maxTopAllowed
   1928         minTopAllowed = self.WINDOW_NEVER_OFFSCREEN_BUFFER - self.height
   1929         if self.top < minTopAllowed:
   1930             self.top = minTopAllowed
   1931 
   1932     def CreateSnapGrid(self, shiftGroup = None, ctrlGroup = None):
   1933         leftOffset, rightOffset = self.GetSideOffset()
   1934         allWnds = find_sibling_windows(self)
   1935         desk = uicore.desktop
   1936         hAxes = [0,
   1937          desk.height,
   1938          16,
   1939          desk.height - 16]
   1940         vAxes = [0,
   1941          leftOffset,
   1942          leftOffset + 16,
   1943          desk.width - rightOffset,
   1944          desk.width - 16 - rightOffset]
   1945         if leftOffset < WINDOW_SNAP_DISTANCE:
   1946             vAxes.pop(0)
   1947         hAxesWithOutMe = hAxes[:]
   1948         vAxesWithOutMe = vAxes[:]
   1949         hAxesWithOutShiftGroup = hAxes[:]
   1950         vAxesWithOutShiftGroup = vAxes[:]
   1951         hAxesWithOutCtrlGroup = hAxes[:]
   1952         vAxesWithOutCtrlGroup = vAxes[:]
   1953         hLists = [hAxes,
   1954          hAxesWithOutMe,
   1955          hAxesWithOutShiftGroup,
   1956          hAxesWithOutCtrlGroup]
   1957         vLists = [vAxes,
   1958          vAxesWithOutMe,
   1959          vAxesWithOutShiftGroup,
   1960          vAxesWithOutCtrlGroup]
   1961         for wnd in allWnds:
   1962             l, t, w, h = wnd.GetAbsolute()
   1963             self.AddtoAxeList(wnd, vLists, l, shiftGroup, ctrlGroup)
   1964             self.AddtoAxeList(wnd, vLists, l + w, shiftGroup, ctrlGroup)
   1965             self.AddtoAxeList(wnd, hLists, t, shiftGroup, ctrlGroup)
   1966             self.AddtoAxeList(wnd, hLists, t + h, shiftGroup, ctrlGroup)
   1967 
   1968         self.__active_snap_grid = [(hAxes, vAxes),
   1969          (hAxesWithOutMe, vAxesWithOutMe),
   1970          (hAxesWithOutShiftGroup, vAxesWithOutShiftGroup),
   1971          (hAxesWithOutCtrlGroup, vAxesWithOutCtrlGroup)]
   1972         return self.__active_snap_grid
   1973 
   1974     def AddtoAxeList(self, wnd, lists, val, shiftgroup, ctrlgroup):
   1975         all, minusMe, minusShiftGroup, minusCtrlGroup = lists
   1976         if val not in all:
   1977             all.append(val)
   1978         if wnd != self and val not in minusMe:
   1979             minusMe.append(val)
   1980         if shiftgroup and wnd not in shiftgroup and val not in minusShiftGroup:
   1981             minusShiftGroup.append(val)
   1982         if ctrlgroup and wnd not in ctrlgroup and val not in minusCtrlGroup:
   1983             minusCtrlGroup.append(val)
   1984 
   1985     def ShowSnapEdges_Scaling(self, snapGrid, showSnap = True, doSnap = False):
   1986         if not self.__scale_sides:
   1987             return
   1988         horizontal, vertical = snapGrid
   1989         snapdist = WINDOW_SNAP_DISTANCE
   1990         match = {}
   1991         for side in self.__scale_sides:
   1992             wnd = self
   1993             minLDist = 1000
   1994             minRDist = 1000
   1995             minTDist = 1000
   1996             minBDist = 1000
   1997             wl, wt, ww, wh = wnd.GetAbsolute()
   1998             if showSnap:
   1999                 snapIndicator = wnd.GetSnapIndicator()
   2000                 snapIndicator.left = wl
   2001                 snapIndicator.top = wt
   2002                 snapIndicator.width = ww
   2003                 snapIndicator.height = wh
   2004                 for each in snapIndicator.children:
   2005                     each.state = uiconst.UI_HIDDEN
   2006 
   2007             else:
   2008                 wnd.HideSnapIndicator()
   2009             if side in ('top', 'bottom'):
   2010                 for hAxe in horizontal:
   2011                     tDist = abs(hAxe - wt)
   2012                     bDist = abs(hAxe - (wt + wh))
   2013                     if side == 'top' and tDist <= snapdist and tDist < minTDist:
   2014                         minTDist = tDist
   2015                         match[side, wnd] = hAxe
   2016                     elif side == 'bottom' and bDist <= snapdist and bDist < minBDist:
   2017                         minBDist = bDist
   2018                         match[side, wnd] = hAxe
   2019 
   2020             elif side in ('left', 'right'):
   2021                 for vAxe in vertical:
   2022                     lDist = abs(vAxe - wl)
   2023                     rDist = abs(vAxe - (wl + ww))
   2024                     if side == 'left' and lDist <= snapdist and lDist < minLDist:
   2025                         minLDist = lDist
   2026                         match[side, wnd] = vAxe
   2027                     elif side == 'right' and rDist <= snapdist and rDist < minRDist:
   2028                         minRDist = rDist
   2029                         match[side, wnd] = vAxe
   2030 
   2031         if showSnap or doSnap:
   2032             checkMultipleSideSnap = {}
   2033             for side, wnd in match.iterkeys():
   2034                 if wnd not in checkMultipleSideSnap:
   2035                     checkMultipleSideSnap[wnd] = []
   2036                 snapValue = match[side, wnd]
   2037                 wl, wt, ww, wh = wnd.GetAbsolute()
   2038                 minH = wnd.GetMinHeight()
   2039                 snapIndicator = wnd.GetSnapIndicator()
   2040                 snapIndicator.state = uiconst.UI_DISABLED
   2041                 snapIndicator.SetOrder(1)
   2042                 if side == 'left':
   2043                     if doSnap:
   2044                         wnd.left = snapValue
   2045                         if wnd.IsResizeable():
   2046                             wnd.width = wl - snapValue + ww
   2047                     if showSnap:
   2048                         snapIndicator.left = snapValue - 2
   2049                         snapIndicator.width = wl - snapValue + ww + 4
   2050                         snapIndicator.GetChild('sLeft').state = uiconst.UI_DISABLED
   2051                 if side == 'right':
   2052                     if doSnap:
   2053                         if wnd.IsResizeable():
   2054                             wnd.width = snapValue - wl
   2055                     if showSnap:
   2056                         snapIndicator.width = snapValue - wl + 4
   2057                         snapIndicator.GetChild('sRight').state = uiconst.UI_DISABLED
   2058                 if side == 'top':
   2059                     if doSnap:
   2060                         wnd.top = snapValue
   2061                         if wnd.IsResizeable():
   2062                             wnd.height = wnd._fixedHeight or max(minH, wt - snapValue + wh)
   2063                     if showSnap:
   2064                         snapIndicator.top = snapValue - 2
   2065                         snapIndicator.height = wt - snapValue + wh + 4
   2066                         snapIndicator.GetChild('sTop').state = uiconst.UI_DISABLED
   2067                 if side == 'bottom':
   2068                     if doSnap:
   2069                         if wnd.IsResizeable():
   2070                             wnd.height = wnd._fixedHeight or max(minH, snapValue - wt)
   2071                     if showSnap:
   2072                         snapIndicator.height = snapValue - wt + 4
   2073                         snapIndicator.GetChild('sBottom').state = uiconst.UI_DISABLED
   2074                 checkMultipleSideSnap[wnd].append(side)
   2075 
   2076     def SetActive(self, *args):
   2077         if self.stacked:
   2078             self.stack.SetActive()
   2079         self.ActivateUnderlay()
   2080         if self.display:
   2081             self.SetNotBlinking()
   2082         self.__active = True
   2083         self.Prepare_ScaleAreas_()
   2084         self.OnSetActive_(self)
   2085         self.OnWindowAboveSetActive()
   2086         if not self.is_stack:
   2087             self._LogWindowEventFocused()
   2088             self.__focused_at_timestamp = gametime.GetWallclockTime()
   2089 
   2090     def OnSetInactive(self, *args):
   2091         if self.stacked:
   2092             self.stack.OnSetInactive()
   2093         self.DeactivateUnderlay()
   2094         self.__active = False
   2095         self.Prepare_ScaleAreas_()
   2096         self.OnWindowAboveSetInactive()
   2097         if not self.is_stack:
   2098             self._LogWindowUnfocused()
   2099 
   2100     def ActivateUnderlay(self):
   2101         if self.sr.underlay:
   2102             self.sr.underlay.AnimEntry()
   2103 
   2104     def DeactivateUnderlay(self):
   2105         if self.sr.underlay:
   2106             self.sr.underlay.AnimExit()
   2107 
   2108     def HideSnapIndicator(self):
   2109         snapIndicator = self.parent.FindChild('snapIndicator')
   2110         if snapIndicator is not None:
   2111             snapIndicator.Close()
   2112 
   2113     def GetSnapIndicator(self):
   2114         snapIndicator = self.parent.FindChild('snapIndicator')
   2115         if snapIndicator is None:
   2116             snapIndicator = Container(parent=self.parent, color=(0.0, 1.0, 0.0, 1.0), align=uiconst.TOPLEFT, name='snapIndicator')
   2117             for label, align, iconPath in [('sLeftTop', uiconst.TOPLEFT, 'res:/UI/Texture/Icons/1_16_1.png'),
   2118              ('sRightTop', uiconst.TOPRIGHT, 'res:/UI/Texture/Icons/1_16_3.png'),
   2119              ('sRightBottom', uiconst.BOTTOMRIGHT, 'res:/UI/Texture/Icons/1_16_35.png'),
   2120              ('sLeftBottom', uiconst.BOTTOMLEFT, 'res:/UI/Texture/Icons/1_16_33.png'),
   2121              ('sLeft', uiconst.CENTERLEFT, 'res:/UI/Texture/Icons/1_16_17.png'),
   2122              ('sTop', uiconst.CENTERTOP, 'res:/UI/Texture/Icons/1_16_2.png'),
   2123              ('sRight', uiconst.CENTERRIGHT, 'res:/UI/Texture/Icons/1_16_19.png'),
   2124              ('sBottom', uiconst.CENTERBOTTOM, 'res:/UI/Texture/Icons/1_16_34.png')]:
   2125                 Sprite(parent=snapIndicator, name=label, align=align, state=uiconst.UI_HIDDEN, texturePath=iconPath, width=16, height=16)
   2126 
   2127         return snapIndicator
   2128 
   2129     def GetStackClass(self):
   2130         from carbonui.window.stack import WindowStack
   2131         return WindowStack
   2132 
   2133     def IndicateStackable(self, wnd = None):
   2134         if wnd is None:
   2135             if self.sr.snapIndicator:
   2136                 animations.FadeOut(self.sr.snapIndicator, duration=0.2, callback=self.sr.snapIndicator.Close)
   2137                 self.sr.snapIndicator = None
   2138             return
   2139         if not wnd.IsStackable() or not self.IsStackable():
   2140             return
   2141         if self.sr.snapIndicator is None:
   2142             margin_left, margin_top, margin_right, _ = wnd.GetWindowBorderPadding()
   2143             border_left, border_top, border_right, _ = wnd.GetWindowBorderSize()
   2144             padding = (margin_left + border_left,
   2145              margin_top + border_top,
   2146              margin_right + border_right,
   2147              0)
   2148             self.sr.snapIndicator = Fill(align=uiconst.TOTOP_NOPUSH, state=uiconst.UI_DISABLED, color=eveThemeColor.THEME_ACCENT, opacity=0.0, outputMode=uiconst.OUTPUT_COLOR_AND_GLOW, glowBrightness=0.3, height=wnd.header_height, padding=padding, idx=0)
   2149             animations.FadeTo(self.sr.snapIndicator, startVal=0.0, endVal=0.4, duration=0.2)
   2150         si = self.sr.snapIndicator
   2151         si.state = uiconst.UI_DISABLED
   2152         if si.parent != wnd:
   2153             Transplant(si, wnd, idx=0)
   2154         else:
   2155             si.SetOrder(0)
   2156 
   2157     def ClearStackIndication(self):
   2158         self.IndicateStackable(None)
   2159 
   2160     def ShowSnapEdges_Moving(self, snapGroup, snapGrid, snapIndicator = None, doSnap = False):
   2161         if snapGrid is None:
   2162             return
   2163         l, t, w, h = gl, gt, gw, gh = self.GetGroupAbsolute(snapGroup)
   2164         snapdist = WINDOW_SNAP_DISTANCE
   2165         lSnap = None
   2166         rSnap = None
   2167         tSnap = None
   2168         bSnap = None
   2169         minLDist = 1000
   2170         minRDist = 1000
   2171         minTDist = 1000
   2172         minBDist = 1000
   2173         horizontal, vertical = snapGrid
   2174         for hAxe in horizontal:
   2175             tDist = abs(hAxe - t)
   2176             bDist = abs(hAxe - (t + h))
   2177             if tDist <= snapdist and tDist < minTDist:
   2178                 tSnap = hAxe
   2179                 minTDist = tDist
   2180             elif bDist <= snapdist and bDist < minBDist:
   2181                 bSnap = hAxe
   2182                 minBDist = bDist
   2183 
   2184         for vAxe in vertical:
   2185             lDist = abs(vAxe - l)
   2186             rDist = abs(vAxe - (l + w))
   2187             if lDist <= snapdist and lDist < minLDist:
   2188                 lSnap = vAxe
   2189                 minLDist = lDist
   2190             elif rDist <= snapdist and rDist < minRDist:
   2191                 rSnap = vAxe
   2192                 minRDist = rDist
   2193 
   2194         if tSnap is not None:
   2195             t = tSnap
   2196             if bSnap is not None:
   2197                 h = bSnap - tSnap
   2198         elif bSnap is not None:
   2199             t = bSnap - h
   2200         if lSnap is not None:
   2201             l = lSnap
   2202             if rSnap is not None:
   2203                 w = rSnap - lSnap
   2204         elif rSnap is not None:
   2205             l = rSnap - w
   2206         if snapIndicator and not snapIndicator.destroyed:
   2207             snapIndicator.width = w + 6
   2208             snapIndicator.height = h + 6
   2209             snapIndicator.left = l - 2
   2210             snapIndicator.top = t - 2
   2211             for each in snapIndicator.children:
   2212                 each.state = uiconst.UI_HIDDEN
   2213 
   2214             snapIndicator.SetOrder(1)
   2215             if lSnap is not None:
   2216                 snapIndicator.GetChild('sLeft').state = uiconst.UI_DISABLED
   2217             if rSnap is not None:
   2218                 snapIndicator.GetChild('sRight').state = uiconst.UI_DISABLED
   2219             if tSnap is not None:
   2220                 snapIndicator.GetChild('sTop').state = uiconst.UI_DISABLED
   2221             if bSnap is not None:
   2222                 snapIndicator.GetChild('sBottom').state = uiconst.UI_DISABLED
   2223             snapIndicator.state = uiconst.UI_DISABLED
   2224         leftOffset, rightOffset = self.GetSideOffset()
   2225         cornerSnap = ''
   2226         if tSnap in (0, 16):
   2227             cornerSnap = 'top'
   2228         elif bSnap in (uicore.desktop.height, uicore.desktop.height - 16):
   2229             cornerSnap = 'bottom'
   2230         if cornerSnap:
   2231             if lSnap in (leftOffset, leftOffset + 16):
   2232                 cornerSnap = cornerSnap + 'left'
   2233             elif rSnap in (uicore.desktop.width, uicore.desktop.width - 16):
   2234                 cornerSnap = cornerSnap + 'right'
   2235         if doSnap:
   2236             scaleX = float(w) / gw
   2237             scaleY = float(h) / gh
   2238             diffX = l - gl
   2239             diffY = t - gt
   2240             for wnd in snapGroup:
   2241                 wnd.left += diffX
   2242                 wnd.top += diffY
   2243                 if wnd.IsResizeable():
   2244                     wnd.width = int(wnd.width * scaleX)
   2245                     wnd.height = int(wnd.height * scaleY)
   2246 
   2247             for wnd in snapGroup:
   2248                 if wnd._fixedHeight and wnd.height != wnd._fixedHeight:
   2249                     diff = wnd.height - wnd._fixedHeight
   2250                     bottomAlignedWindows = wnd.FindConnectingWindows('bottom')
   2251                     wnd.height -= diff
   2252                     for each in bottomAlignedWindows[1:]:
   2253                         each.top -= diff
   2254 
   2255                 if wnd._fixedWidth and wnd.width != wnd._fixedWidth:
   2256                     diff = wnd.width - wnd._fixedWidth
   2257                     rightAlignedWindows = wnd.FindConnectingWindows('right')
   2258                     wnd.width -= diff
   2259                     for each in rightAlignedWindows[1:]:
   2260                         each.left -= diff
   2261 
   2262     def FindWindowToStackTo(self):
   2263         over = uicore.uilib.mouseOver
   2264         if over is self:
   2265             return None
   2266         over = GetWindowAbove(over)
   2267         if not isinstance(over, Window) or not over.IsStackable():
   2268             self.ClearStackIndication()
   2269             return None
   2270         if over.stacked:
   2271             over = over.stack
   2272         l, t, w, h = over.GetAbsolute()
   2273         sl, st, sw, sh = self.GetAbsolute()
   2274         if (l < sl < l + w or l < sl + sw < l + w) and t <= st <= t + over.header_height:
   2275             self.IndicateStackable(over)
   2276             return over
   2277         self.ClearStackIndication()
   2278 
   2279     def IsStackable(self):
   2280         if self.__stackable and not self.IsLocked():
   2281             shiftonly = settings.user.ui.Get('stackwndsonshift', 0)
   2282             if shiftonly:
   2283                 return uicore.uilib.Key(uiconst.VK_SHIFT)
   2284             return 1
   2285         return 0
   2286 
   2287     def OnMouseUp(self, *args):
   2288         self.ClearStackIndication()
   2289         self.CleanupParent('snapIndicator')
   2290         uicore.uilib.UnclipCursor()
   2291         self._dragging = False
   2292         self._dragEnabled = False
   2293         self.__drag_started = False
   2294         self.RegisterPositionAndSize()
   2295 
   2296     def OnDblClick(self, *args):
   2297         if getattr(self, 'isDialog', False):
   2298             return
   2299         if uicore.uilib.y - self.top < self.header_height:
   2300             self.ToggleCollapse()
   2301 
   2302     def ToggleCollapse(self):
   2303         if not self.collapsible:
   2304             return
   2305         self.ResetToggleState()
   2306         if self.collapsed:
   2307             self.Expand()
   2308             PlaySound(uiconst.SOUND_EXPAND)
   2309         else:
   2310             self.Collapse()
   2311             PlaySound(uiconst.SOUND_COLLAPSE)
   2312 
   2313     def ToggleMinimize(self):
   2314         if self.IsMinimized():
   2315             self.Maximize()
   2316         elif self.minimizable:
   2317             parChildren = self.GetParentLayer().children
   2318             if self.InStack():
   2319                 if parChildren and parChildren[0] != self.stack:
   2320                     uicore.registry.SetFocus(self)
   2321                     self.stack.ShowWnd(self)
   2322                 elif self.stack.GetActiveWindow() != self:
   2323                     self.stack.ShowWnd(self)
   2324                 else:
   2325                     self.Minimize()
   2326             elif parChildren and parChildren[0] != self:
   2327                 uicore.registry.SetFocus(self)
   2328             else:
   2329                 self.Minimize()
   2330 
   2331     def GetParentLayer(self):
   2332         if self.InStack():
   2333             return self.stack.parent
   2334         else:
   2335             return self.parent
   2336 
   2337     def ResetToggleState(self):
   2338         uicore.registry.ResetToggleStateForWnd(getattr(self, 'windowID', None))
   2339 
   2340     def GetCollapsedHeight(self):
   2341         _, margin_top, _, margin_bottom = self.GetWindowBorderPadding()
   2342         _, border_top, _, border_bottom = self.GetWindowBorderSize()
   2343         return margin_top + border_top + WindowHeaderBase.COLLAPSED_HEIGHT + border_bottom + margin_bottom
   2344 
   2345     def Collapse(self, forceCollapse = False, checkchain = 1, *args):
   2346         if not self.collapsible or not forceCollapse and self.collapsed:
   2347             return
   2348         if self.sr.bottom:
   2349             self.sr.bottom.state = uiconst.UI_HIDDEN
   2350         if self.sr.main:
   2351             self.sr.main.state = uiconst.UI_HIDDEN
   2352         if not self.parent or not self.collapsible or self.collapsed:
   2353             return
   2354         if self.stacked:
   2355             return self.stack.Collapse()
   2356         bottomAlignedWindows = self.FindConnectingWindows('bottom')
   2357         allAlignedWindows = self.FindConnectingWindows()
   2358         gl, gt, gw, gh = self.GetGroupAbsolute(allAlignedWindows)
   2359         ch = self.GetCollapsedHeight()
   2360         heightDiff = self.height - ch
   2361         self._SetCollapsed(True)
   2362         self.LockHeight(ch)
   2363         alignedToBottom = False
   2364         alignedToTop = False
   2365         pl, pt, pw, ph = self.parent.GetAbsolute()
   2366         if gt in (0, 16):
   2367             alignedToTop = True
   2368         elif gt + gh in (ph, ph - 16):
   2369             alignedToBottom = True
   2370         if alignedToBottom:
   2371             topAlignedWindows = self.FindConnectingWindows('top')
   2372             for wnd in topAlignedWindows:
   2373                 wnd.top += heightDiff
   2374 
   2375         else:
   2376             for wnd in bottomAlignedWindows:
   2377                 if wnd == self:
   2378                     continue
   2379                 wnd.top -= heightDiff
   2380 
   2381         shift = uicore.uilib.Key(uiconst.VK_SHIFT)
   2382         if shift:
   2383             affected = allAlignedWindows
   2384             for each in affected:
   2385                 if each != self:
   2386                     each.Collapse()
   2387 
   2388         self.OnCollapsed(self)
   2389         self.Prepare_ScaleAreas_()
   2390 
   2391     def Expand(self, *args):
   2392         if not self.parent or not self.IsCollapsed():
   2393             return
   2394         if self.stacked:
   2395             return self.stack.Expand(*args)
   2396         self.UnlockHeight()
   2397         bottomAlignedWindows = self.FindConnectingWindows('bottom')
   2398         gl, gt, gw, gh = self.GetGroupAbsolute(bottomAlignedWindows)
   2399         pl, pt, pw, ph = self.parent.GetAbsolute()
   2400         alignedToBottom = False
   2401         if gt + gh in (ph, ph - 16):
   2402             alignedToBottom = True
   2403         left, top, width, height, dw, dh = self.GetRegisteredPositionAndSize()
   2404         height = max(height, self.GetMinHeight())
   2405         heightDiff = height - self.height
   2406         self._SetCollapsed(False)
   2407         self.height = height
   2408         if alignedToBottom:
   2409             topAlignedWindows = self.FindConnectingWindows('top')
   2410             for wnd in topAlignedWindows:
   2411                 wnd.top = max(0, wnd.top - heightDiff)
   2412 
   2413         else:
   2414             for wnd in bottomAlignedWindows:
   2415                 if wnd == self:
   2416                     continue
   2417                 wnd.top = min(wnd.top + heightDiff, uicore.desktop.height - wnd.height)
   2418 
   2419         shift = uicore.uilib.Key(uiconst.VK_SHIFT)
   2420         if shift:
   2421             affected = self.FindConnectingWindows()
   2422             for each in affected:
   2423                 if each != self:
   2424                     each.Expand()
   2425 
   2426         self.OnExpanded(self)
   2427         self.ValidateWindows()
   2428         self.Prepare_ScaleAreas_()
   2429         if self.sr.bottom:
   2430             self.sr.bottom.state = uiconst.UI_PICKCHILDREN
   2431         if self.sr.main:
   2432             self.sr.main.state = uiconst.UI_PICKCHILDREN
   2433 
   2434     def OnCompactModeEnabled(self):
   2435         pass
   2436 
   2437     def OnCompactModeDisabled(self):
   2438         pass
   2439 
   2440     def ValidateWindows(self):
   2441         d = uicore.desktop
   2442         all = uicore.registry.GetValidWindows(getModals=1, floatingOnly=True)
   2443         for wnd in all:
   2444             if wnd.GetAlign() != uiconst.RELATIVE:
   2445                 continue
   2446             wnd.left = max(-wnd.width + 64, min(d.width - 64, wnd.left))
   2447             wnd.top = max(0, min(d.height - wnd.GetCollapsedHeight(), wnd.top))
   2448 
   2449     def CheckStackWith(self, dropWnd):
   2450         if not self.IsStackable():
   2451             return
   2452         if self.sr.modalParent is not None or dropWnd.sr.modalParent is not None or dropWnd == self:
   2453             return
   2454         if self.state == uiconst.UI_HIDDEN:
   2455             return
   2456         if dropWnd.closing:
   2457             return
   2458         wnds = [(self, 0)]
   2459         kill = []
   2460         from carbonui.window.stack import WindowStack
   2461         if isinstance(dropWnd, WindowStack):
   2462             for wnd in dropWnd.GetWindows():
   2463                 wnds.append((wnd, wnd.state == uiconst.UI_NORMAL))
   2464 
   2465             kill.append(dropWnd)
   2466         else:
   2467             wnds.append((dropWnd, 1))
   2468         self._ConstructStack(wnds, kill)
   2469 
   2470     def CheckWndPos(self, i = 0):
   2471         if self.parent is None or self.parent.destroyed or i == 10:
   2472             return
   2473         if self.locked:
   2474             return
   2475         for each in self.parent.children:
   2476             if each != self and each.state == uiconst.UI_NORMAL:
   2477                 if each.left == self.left and each.top == self.top:
   2478                     self.left = self.left + POSOVERLAPSHIFT
   2479                     self.top = self.top + POSOVERLAPSHIFT
   2480                     if self.left + self.width > uicore.desktop.width:
   2481                         self.left = uicore.desktop.width - self.width
   2482                     if self.top + self.height > uicore.desktop.height:
   2483                         self.top = uicore.desktop.height - self.height
   2484                     self.CheckWndPos(i + 1)
   2485                     break
   2486 
   2487     def _ConstructStack(self, wnds, kill, group = None, groupidx = None):
   2488         stack = uicore.registry.GetStack(str(uthread.uniqueId()), self.GetStackClass())
   2489         if not stack or stack.closing:
   2490             return
   2491         for _wnd in wnds:
   2492             wnd, show = _wnd
   2493             stack.InsertWnd(wnd, 1, show)
   2494 
   2495         for each in kill:
   2496             if each is not None and not each.destroyed:
   2497                 each.Close()
   2498 
   2499         uicore.registry.SetFocus(stack)
   2500 
   2501     def SetHeight(self, height):
   2502         if self.GetAlign() == uiconst.RELATIVE and height != self.height:
   2503             self.height = height
   2504 
   2505     def SetHeight_PushOrPullWindowsBelow(self, newHeight):
   2506         if self.GetAlign() == uiconst.RELATIVE and newHeight != self.height:
   2507             bottomAlignedWindows = self.FindConnectingWindows('bottom')
   2508             heightDiff = newHeight - self.height
   2509             self.height = newHeight
   2510             for wnd in bottomAlignedWindows:
   2511                 if wnd == self:
   2512                     continue
   2513                 wnd.top += heightDiff
   2514 
   2515     def GetMinWidth(self):
   2516         minWidth = self._GetMinWidth()
   2517         if self._fixedWidth:
   2518             return min(self._fixedWidth, minWidth)
   2519         w, h = self.GetMinSize()
   2520         return max(w, minWidth)
   2521 
   2522     def _GetMinWidth(self):
   2523         return self.minsize[0]
   2524 
   2525     def GetMinHeight(self):
   2526         if self._fixedHeight:
   2527             return min(self._fixedHeight, self._GetMinHeight())
   2528         w, h = self.GetMinSize()
   2529         return max(h, self.minsize[1])
   2530 
   2531     def _GetMinHeight(self):
   2532         return self.minsize[1]
   2533 
   2534     def GetMaxWidth(self):
   2535         if self._fixedWidth:
   2536             return self._fixedWidth
   2537         return self.maxsize[0] or sys.maxint
   2538 
   2539     def GetMaxHeight(self):
   2540         if self._fixedHeight:
   2541             return self._fixedHeight
   2542         return self.maxsize[1] or sys.maxint
   2543 
   2544     def FindConnectingWindows(self, fromSide = None, wnds = None, validWnds = None, getParallelSides = 0, fullSideOnly = 0, includeLocked = True):
   2545         if validWnds is None:
   2546             validWnds = find_sibling_windows(self)
   2547             validWnds.insert(0, self)
   2548         l, t, w, h = self.GetAbsolute()
   2549         fromWndCorners = [(l, t),
   2550          (l + w, t),
   2551          (l + w, t + h),
   2552          (l, t + h)]
   2553         if fromSide == 'left':
   2554             validCornerPairs = [(3, 2), (0, 1)]
   2555             if getParallelSides:
   2556                 validCornerPairs += [(3, 0), (0, 3)]
   2557         elif fromSide == 'right':
   2558             validCornerPairs = [(1, 0), (2, 3)]
   2559             if getParallelSides:
   2560                 validCornerPairs += [(1, 2), (2, 1)]
   2561         elif fromSide == 'top':
   2562             validCornerPairs = [(0, 3), (1, 2)]
   2563             if getParallelSides:
   2564                 validCornerPairs += [(0, 1), (1, 0)]
   2565         elif fromSide == 'bottom':
   2566             validCornerPairs = [(2, 1), (3, 0)]
   2567             if getParallelSides:
   2568                 validCornerPairs += [(3, 2), (2, 3)]
   2569         else:
   2570             validCornerPairs = [(3, 2),
   2571              (0, 1),
   2572              (1, 0),
   2573              (2, 3),
   2574              (0, 3),
   2575              (1, 2),
   2576              (2, 1),
   2577              (3, 0)]
   2578         wnds = wnds or []
   2579         if self not in wnds:
   2580             wnds.append(self)
   2581         for wnd in validWnds:
   2582             if wnd in wnds:
   2583                 continue
   2584             wl, wt, ww, wh = wnd.GetAbsolute()
   2585             wndCorners = ((wl, wt),
   2586              (wl + ww, wt),
   2587              (wl + ww, wt + wh),
   2588              (wl, wt + wh))
   2589             m = 0
   2590             for c1, c2 in validCornerPairs:
   2591                 c1Pos = fromWndCorners[c1]
   2592                 c2Pos = wndCorners[c2]
   2593                 if c1Pos == c2Pos:
   2594                     if not fullSideOnly or m == 1:
   2595                         if getParallelSides:
   2596                             wnd.FindConnectingWindows(None, wnds, validWnds, getParallelSides, fullSideOnly)
   2597                         else:
   2598                             wnd.FindConnectingWindows(fromSide, wnds, validWnds, getParallelSides, fullSideOnly)
   2599                         break
   2600                     m += 1
   2601 
   2602         if not includeLocked:
   2603             wnds = [ wnd for wnd in wnds if not wnd.IsLocked() ]
   2604         return wnds
   2605 
   2606     def PrepareWindowsForMove(self, wnds):
   2607         for each in wnds:
   2608             each.preDragAbs = each.GetAbsolute()
   2609             each.preDragCursorPos = (uicore.uilib.x, uicore.uilib.y)
   2610             each.SetOrder(0)
   2611 
   2612     def CleanupParent(self, what):
   2613         for each in self.parent.children[:]:
   2614             if each.name == what:
   2615                 each.Close()
   2616 
   2617     def FindMinMaxScaling(self, sides):
   2618         scaleH, scaleV, scaleAbove, followL, followR, followT, followB, all = self.sortedScaleWindows
   2619         pl, pt, pw, ph = self.parent.GetAbsolute()
   2620         myMinX = minX = 0
   2621         myMaxX = maxX = pw
   2622         myMinY = minY = 0
   2623         myMaxY = maxY = ph
   2624         for wnd in scaleH + [self]:
   2625             spdl, spdt, spdw, spdh = wnd.preDragAbs
   2626             wndMinWidth = wnd.GetMinWidth()
   2627             wndMaxWidth = wnd.GetMaxWidth()
   2628             if 'left' in sides:
   2629                 minX = max(minX, spdl + spdw - wndMaxWidth)
   2630                 maxX = min(maxX, spdl + spdw - wndMinWidth)
   2631             elif 'right' in sides:
   2632                 minX = max(minX, spdl + wndMinWidth)
   2633                 maxX = min(maxX, spdl + wndMaxWidth)
   2634 
   2635         for wnd in scaleV + [self]:
   2636             spdl, spdt, spdw, spdh = wnd.preDragAbs
   2637             wndMinHeight = wnd.GetMinHeight()
   2638             wndMaxHeight = wnd.GetMaxHeight()
   2639             if 'top' in sides:
   2640                 minY = max(minY, spdt + spdh - wndMaxHeight)
   2641                 maxY = min(maxY, spdt + spdh - wndMinHeight)
   2642             elif 'bottom' in sides:
   2643                 minY = max(minY, spdt + wndMinHeight)
   2644                 maxY = min(maxY, spdt + wndMaxHeight)
   2645 
   2646         for wnd in scaleAbove:
   2647             spdl, spdt, spdw, spdh = wnd.preDragAbs
   2648             wndMinHeight = wnd.GetMinHeight()
   2649             wndMaxHeight = wnd.GetMaxHeight()
   2650             minY = max(minY, spdt + wndMinHeight)
   2651             maxY = min(maxY, spdt + wndMaxHeight)
   2652 
   2653         spdl, spdt, spdw, spdh = self.preDragAbs
   2654         myMinWidth = self.GetMinWidth()
   2655         myMinHeight = self.GetMinHeight()
   2656         myMaxWidth = self.GetMaxWidth()
   2657         myMaxHeight = self.GetMaxHeight()
   2658         if 'top' in sides:
   2659             myMaxY = min(myMaxY, spdt + spdh - myMinHeight)
   2660             myMinY = max(myMinY, spdt + spdh - myMaxHeight)
   2661             for wnd in followT:
   2662                 pdl, pdt, pdw, pdh = wnd.preDragAbs
   2663                 minY = max(minY, spdt - pdt)
   2664 
   2665         elif 'bottom' in sides:
   2666             myMinY = max(myMinY, spdt + myMinHeight)
   2667             myMaxY = min(myMaxY, spdt + myMaxHeight)
   2668             for wnd in followB:
   2669                 pdl, pdt, pdw, pdh = wnd.preDragAbs
   2670                 maxY = min(maxY, spdt + spdh + (ph - (pdt + pdh)))
   2671 
   2672         if 'left' in sides:
   2673             myMaxX = min(myMaxX, spdl + spdw - myMinWidth)
   2674             myMinX = max(myMinX, spdl + spdw - myMaxWidth)
   2675             for wnd in followL:
   2676                 pdl, pdt, pdw, pdh = wnd.preDragAbs
   2677                 minX = max(minX, spdl - pdl)
   2678 
   2679         elif 'right' in sides:
   2680             myMinX = max(myMinX, spdl + myMinWidth)
   2681             myMaxX = min(myMaxX, spdl + myMaxWidth)
   2682             for wnd in followR:
   2683                 pdl, pdt, pdw, pdh = wnd.preDragAbs
   2684                 maxX = min(maxX, spdl + spdw + (pw - (pdl + pdw)))
   2685 
   2686         return ((minX,
   2687           minY,
   2688           maxX,
   2689           maxY), (myMinX,
   2690           myMinY,
   2691           myMaxX,
   2692           myMaxY))
   2693 
   2694     def ModifyRect(self, sides):
   2695         l, t, w, h = self.GetAbsolute()
   2696         mx = uicore.uilib.x
   2697         my = uicore.uilib.y
   2698         rh = rv = 0
   2699         if 'right' in sides:
   2700             rh = l + w - mx
   2701         elif 'left' in sides:
   2702             rh = l - mx
   2703         if 'bottom' in sides:
   2704             rv = t + h - my
   2705         elif 'top' in sides:
   2706             rv = t - my
   2707         return (int(rh),
   2708          int(rv),
   2709          int(rh),
   2710          int(rv))
   2711 
   2712     @telemetry.ZONE_METHOD
   2713     def UpdateClipCursor(self, rect, mrect):
   2714         ml, mt, mr, mb = mrect
   2715         rl, rt, rr, rb = rect
   2716         uicore.uilib.ClipCursor(rl - ml, rt - mt, rr - mr, rb - mb)
   2717 
   2718     def GetSidesFromScalerName(self, sName):
   2719         ret = []
   2720         for s in ['Left',
   2721          'Top',
   2722          'Right',
   2723          'Bottom']:
   2724             if s in sName:
   2725                 ret.append(s.lower())
   2726 
   2727         return ret
   2728 
   2729     def StartScale(self, sender, btn, *args):
   2730         if btn == uiconst.MOUSELEFT:
   2731             sides = self.GetSidesFromScalerName(sender.name)
   2732             self._StartScale(sides=sides)
   2733 
   2734     def _StartScale(self, sides):
   2735         if not self.IsResizeable() or self.InStack():
   2736             return
   2737         self.__scale_sides = sides
   2738         self.sortedScaleWindows = self.SortScaleWindows(self.__scale_sides)
   2739         self.minmaxScale = self.FindMinMaxScaling(self.__scale_sides)
   2740         self.CreateSnapGrid(self.sortedScaleWindows[-1])
   2741         self._emit_on_start_scale()
   2742         self.OnStartScale_(self)
   2743         self._scaling = True
   2744         uthread.new(self.OnScale)
   2745 
   2746     def SortScaleWindows(self, sides):
   2747         wnds = []
   2748         for side in sides:
   2749             wnds += self.FindConnectingWindows(side, getParallelSides=1)
   2750 
   2751         self.PrepareWindowsForMove(wnds)
   2752         ml, mt, mw, mh = self.GetAbsolute()
   2753         scaleWidthMeH = []
   2754         scaleWidthMeV = []
   2755         onLeft = []
   2756         onRight = []
   2757         onBottom = []
   2758         onTop = []
   2759         all = [self]
   2760         scaleAbove = []
   2761         for wnd in wnds:
   2762             if wnd == self or wnd in all:
   2763                 continue
   2764             l, t, w, h = wnd.GetAbsolute()
   2765             if l == ml and w == mw:
   2766                 scaleWidthMeH.append(wnd)
   2767             elif l >= ml + mw:
   2768                 onRight.append(wnd)
   2769             elif l + w <= ml:
   2770                 onLeft.append(wnd)
   2771             elif 'right' in sides:
   2772                 onRight.append(wnd)
   2773             elif 'left' in sides:
   2774                 onLeft.append(wnd)
   2775             if t == mt and h == mh:
   2776                 scaleWidthMeV.append(wnd)
   2777             elif t >= mt + mh:
   2778                 onBottom.append(wnd)
   2779             elif t + h == mt and 'top' in sides:
   2780                 scaleAbove.append(wnd)
   2781             elif t == mt and 'top' in sides:
   2782                 onTop.append(wnd)
   2783             all.append(wnd)
   2784 
   2785         return (scaleWidthMeH,
   2786          scaleWidthMeV,
   2787          scaleAbove,
   2788          onLeft,
   2789          onRight,
   2790          onTop,
   2791          onBottom,
   2792          all)
   2793 
   2794     @telemetry.ZONE_METHOD
   2795     def OnScale(self, *args):
   2796         mRect = self.ModifyRect(self.__scale_sides)
   2797         while self._scaling and uicore.uilib.leftbtn and self and not self.closing:
   2798             diffx = uicore.uilib.x - self.preDragCursorPos[0]
   2799             diffy = uicore.uilib.y - self.preDragCursorPos[1]
   2800             shift = uicore.uilib.Key(uiconst.VK_SHIFT)
   2801             allMinMaxRect, myMinMaxRect = self.minmaxScale
   2802             if shift:
   2803                 snapGrid = self.__active_snap_grid[2]
   2804                 rect = allMinMaxRect
   2805             else:
   2806                 snapGrid = self.__active_snap_grid[1]
   2807                 rect = myMinMaxRect
   2808             self.UpdateClipCursor(rect, mRect)
   2809             if not shift:
   2810                 for wnd in self.sortedScaleWindows[-1]:
   2811                     wpdl, wpdt, wpdw, wpdh = wnd.preDragAbs
   2812                     wnd.left = wpdl
   2813                     wnd.top = wpdt
   2814                     wnd.width = wpdw
   2815                     wnd.height = wpdh
   2816 
   2817                 scaleH, scaleV, scaleAbove, followL, followR, followT, followB = ([],
   2818                  [],
   2819                  [],
   2820                  [],
   2821                  [],
   2822                  [],
   2823                  [])
   2824             else:
   2825                 scaleH, scaleV, scaleAbove, followL, followR, followT, followB, all = self.sortedScaleWindows
   2826             for side in self.__scale_sides:
   2827                 if side in ('left', 'right') and self.GetMinWidth() != self.GetMaxWidth():
   2828                     for wnd in scaleH + [self]:
   2829                         wpdl, wpdt, wpdw, wpdh = wnd.preDragAbs
   2830                         if side == 'left':
   2831                             wnd.left = wpdl + diffx
   2832                             if wnd.IsResizeable():
   2833                                 wnd.width = wnd._fixedWidth or wpdw - diffx
   2834                         elif side == 'right':
   2835                             if wnd.IsResizeable():
   2836                                 wnd.width = wnd._fixedWidth or wpdw + diffx
   2837 
   2838                     for wnd in [followR, followL][side == 'left']:
   2839                         wpdl, wpdt, wpdw, wpdh = wnd.preDragAbs
   2840                         wnd.left = wpdl + diffx
   2841 
   2842                 if side in ('top', 'bottom') and self.GetMinHeight() != self.GetMaxHeight():
   2843                     for wnd in scaleV + [self]:
   2844                         wpdl, wpdt, wpdw, wpdh = wnd.preDragAbs
   2845                         if side == 'top':
   2846                             if wnd.IsResizeable():
   2847                                 wnd.height = wnd._fixedHeight or wpdh - diffy
   2848                             wnd.top = wpdt + diffy
   2849                         elif side == 'bottom':
   2850                             if wnd.IsResizeable():
   2851                                 wnd.height = wnd._fixedHeight or wpdh + diffy
   2852 
   2853                     for wnd in [followB, followT][side == 'top']:
   2854                         wpdl, wpdt, wpdw, wpdh = wnd.preDragAbs
   2855                         wnd.top = wpdt + diffy
   2856 
   2857                     for wnd in scaleAbove:
   2858                         wpdl, wpdt, wpdw, wpdh = wnd.preDragAbs
   2859                         if wnd.IsResizeable():
   2860                             wnd.height = wnd._fixedHeight or wpdh + diffy
   2861 
   2862             self.ShowSnapEdges_Scaling(snapGrid)
   2863             self.OnScale_(self)
   2864             self._OnResize()
   2865             blue.pyos.synchro.SleepWallclock(1)
   2866 
   2867         self.ValidateWindows()
   2868 
   2869     def EndScale(self, sender, *args):
   2870         self.CleanupParent('snapIndicator')
   2871         if not self.IsResizeable() or self.InStack():
   2872             return
   2873         self._scaling = False
   2874         uicore.uilib.UnclipCursor()
   2875         if self.closing:
   2876             return
   2877         if self.__active_snap_grid:
   2878             snapGrid = self.__active_snap_grid[1]
   2879             self.ShowSnapEdges_Scaling(snapGrid, showSnap=False, doSnap=True)
   2880         self.RegisterPositionAndSize()
   2881         self._emit_on_end_scale()
   2882         self.OnEndScale_(self)
   2883 
   2884     def GetGroupAbsolute(self, group):
   2885         return self.GetGroupRect(group, 1)
   2886 
   2887     def OnResize_(self, *args):
   2888         self.OnResizeUpdate(self)
   2889 
   2890     def OnResizeUpdate(self, *args):
   2891         pass
   2892 
   2893     def OnScale_(self, wnd, *args):
   2894         pass
   2895 
   2896     def OnStartScale_(self, wnd, *args):
   2897         pass
   2898 
   2899     def OnEndScale_(self, wnd, *args):
   2900         pass
   2901 
   2902     def OnMouseDown_(self, what):
   2903         pass
   2904 
   2905     def OnStartMinimize_(self, *args):
   2906         pass
   2907 
   2908     def OnEndMinimize_(self, *args):
   2909         pass
   2910 
   2911     def OnStartMaximize_(self, *args):
   2912         pass
   2913 
   2914     def OnEndMaximize_(self, *args):
   2915         pass
   2916 
   2917     def OnSetActive_(self, *args):
   2918         pass
   2919 
   2920     def OnCollapsed(self, wnd, *args):
   2921         pass
   2922 
   2923     def OnExpanded(self, wnd, *args):
   2924         pass
   2925 
   2926     def OnDragTick(self, *args):
   2927         pass
   2928 
   2929     def GetIntersectingWindows(self):
   2930         all = uicore.registry.GetWindows()
   2931         ml, mt, mw, mh = self.GetAbsolute()
   2932         intersecting = []
   2933         for otherWindow in all:
   2934             if otherWindow is self:
   2935                 continue
   2936             if otherWindow.stacked:
   2937                 continue
   2938             if otherWindow.IsHidden():
   2939                 continue
   2940             ol, ot, ow, oh = otherWindow.GetAbsolute()
   2941             if not (ml <= ol < ml + mw or ml < ol + ow <= ml + mw or ml >= ol and ol + ow > ml + mw):
   2942                 continue
   2943             if not (mt <= ot < mt + mh or mt < ot + oh <= mt + mh or mt >= ot and ot + oh > mt + mh):
   2944                 continue
   2945             intersecting.append(otherWindow)
   2946 
   2947         return intersecting
   2948 
   2949     @classmethod
   2950     def GetTopRight_TopOffset(cls):
   2951         leftpush, rightpush = cls.GetSideOffset()
   2952         cornerWnd = None
   2953         for each in uicore.layer.main.children:
   2954             if not isinstance(each, Window) or each.state == uiconst.UI_HIDDEN:
   2955                 continue
   2956             if each.left + each.width in (uicore.desktop.width - rightpush, uicore.desktop.width - rightpush - 16):
   2957                 if each.top in (0, 16):
   2958                     cornerWnd = each
   2959                     break
   2960 
   2961         if cornerWnd:
   2962             bottomAlignedWindows = cornerWnd.FindConnectingWindows('bottom')
   2963             if bottomAlignedWindows:
   2964                 groupRect = cls.GetGroupRect(bottomAlignedWindows)
   2965                 return groupRect[3]
   2966 
   2967     @classmethod
   2968     def GetBottomLeft_TopOffset(cls):
   2969         leftpush, rightpush = cls.GetSideOffset()
   2970         cornerWnd = None
   2971         for each in uicore.layer.main.children:
   2972             if not isinstance(each, Window) or each.state == uiconst.UI_HIDDEN:
   2973                 continue
   2974             if each.left in (leftpush, leftpush + 16):
   2975                 if each.top + each.height in (uicore.desktop.height, uicore.desktop.height - 16):
   2976                     cornerWnd = each
   2977                     break
   2978 
   2979         if cornerWnd:
   2980             topAlignedWindows = cornerWnd.FindConnectingWindows('top')
   2981             if topAlignedWindows:
   2982                 groupRect = cls.GetGroupRect(topAlignedWindows)
   2983                 return groupRect[1]
   2984 
   2985     @classmethod
   2986     def _GetDefaultCaption(cls):
   2987         if cls.default_caption:
   2988             if localization.IsValidLabel(cls.default_caption):
   2989                 return localization.GetByLabel(cls.default_caption)
   2990             else:
   2991                 return cls.default_caption
   2992         if cls.default_captionLabelPath:
   2993             return localization.GetByLabel(cls.default_captionLabelPath)
   2994         return cls.__name__
   2995 
   2996     @classmethod
   2997     def GetIfOpen(cls, windowID = None, windowInstanceID = None):
   2998         windowID = windowID if windowID else cls.default_windowID
   2999         wnd = uicore.registry.GetWindow(windowID, windowInstanceID)
   3000         return wnd
   3001 
   3002     @classmethod
   3003     def CloseIfOpen(cls, windowID = None, windowInstanceID = None):
   3004         wnd = cls.GetIfOpen(windowID, windowInstanceID)
   3005         if wnd:
   3006             wnd.Close()
   3007 
   3008     @classmethod
   3009     def Open(cls, *args, **kwds):
   3010         windowID = kwds.get('windowID', None) or cls.default_windowID
   3011         windowInstanceID = kwds.get('windowInstanceID', None)
   3012         if is_blocked('open', windowID):
   3013             raise UserError('WindowBlockedFromOpening', {'window_name': cls._GetDefaultCaption()})
   3014         wnd = cls.GetIfOpen(windowID=windowID, windowInstanceID=windowInstanceID)
   3015         if wnd:
   3016             wnd.Maximize()
   3017             return wnd
   3018         newWindow = cls(**kwds)
   3019         return newWindow
   3020 
   3021     @classmethod
   3022     def OpenBehindFullscreenViews(cls, **kwds):
   3023         isSecondaryViewActive = sm.GetService('viewState').IsCurrentViewSecondary()
   3024         windowID = kwds.get('windowID', cls.default_windowID)
   3025         isOverlayed = GetRegisteredState(windowID, 'isOverlayed')
   3026         if isSecondaryViewActive and not isOverlayed:
   3027             wnd = cls.Open(openMinimized=True, idx=(-1), **kwds)
   3028             sm.GetService('window').RegisterAsMinimizedByFullscreenView(wnd)
   3029         else:
   3030             cls.Open(**kwds)
   3031 
   3032     @classmethod
   3033     def ToggleOpenClose(cls, *args, **kwds):
   3034         wnd = cls.GetIfOpen(windowID=kwds.get('windowID', None), windowInstanceID=kwds.get('windowInstanceID', None))
   3035         if wnd:
   3036             wasCollapsed = wnd.IsCollapsed()
   3037             if wasCollapsed:
   3038                 wnd.Expand()
   3039             if wnd.stacked:
   3040                 if wnd.stack.GetActiveWindow() != wnd:
   3041                     wnd.Maximize()
   3042                     return wnd
   3043                 obscured = wnd.stack.ObscuredByOtherWindows()
   3044             else:
   3045                 obscured = wnd.ObscuredByOtherWindows()
   3046             if wnd.IsMinimized():
   3047                 wnd.Maximize()
   3048                 uicore.registry.SetFocus(wnd)
   3049                 return wnd
   3050             if obscured:
   3051                 uicore.registry.SetFocus(wnd)
   3052                 return wnd
   3053             if not wasCollapsed:
   3054                 wnd.CloseByUser()
   3055         else:
   3056             return cls.Open(*args, **kwds)
   3057 
   3058     def ObscuredByOtherWindows(self):
   3059         intersecting = self.GetIntersectingWindows()
   3060         for wnd in intersecting:
   3061             if self not in uicore.layer.main.children:
   3062                 return False
   3063             if wnd not in uicore.layer.main.children:
   3064                 continue
   3065             if uicore.layer.main.children.index(self) > uicore.layer.main.children.index(wnd):
   3066                 return True
   3067 
   3068         return False
   3069 
   3070     @classmethod
   3071     def IsOpen(cls, windowID = None, windowInstanceID = None):
   3072         wnd = cls.GetIfOpen(windowID, windowInstanceID)
   3073         return bool(wnd)
   3074 
   3075     @classmethod
   3076     def IsOpenByWindowClass(cls):
   3077         wnd = uicore.registry.GetWindowByClass(cls)
   3078         return bool(wnd)
   3079 
   3080     @classmethod
   3081     def GetRegisteredOrDefaultStackID(cls, windowID = None):
   3082         windowID = windowID or cls.default_windowID
   3083         if windowID:
   3084             all = settings.char.windows.Get('stacksWindows', {})
   3085             if windowID in all:
   3086                 return all[windowID]
   3087             return all.get(windowID, cls.default_stackID)
   3088 
   3089     def GetRegisteredPositionAndSize(self):
   3090         return self.GetRegisteredPositionAndSizeByClass(self.windowID)
   3091 
   3092     @classmethod
   3093     def GetRegisteredPositionAndSizeByClass(cls, windowID = None):
   3094         windowID = windowID or cls.default_windowID
   3095         if type(windowID) == tuple:
   3096             windowID, subWindowID = windowID
   3097         all = settings.char.windows.Get('windowSizesAndPositions_1', {})
   3098         usingDefault = 1
   3099         if windowID and windowID in all:
   3100             left, top, width, height, cdw, cdh = all[windowID]
   3101             usingDefault = 0
   3102         else:
   3103             cdw, cdh = uicore.desktop.width, uicore.desktop.height
   3104         if usingDefault:
   3105             left, top, width, height = cls.GetDefaultSizeAndPosition()
   3106             pushleft = cls.GetDefaultLeftOffset(width=width, align=uiconst.CENTER, left=left)
   3107             if pushleft < 0:
   3108                 left = max(0, left + pushleft)
   3109             elif pushleft > 0:
   3110                 left = min(left + pushleft, uicore.desktop.width - width)
   3111         dw, dh = uicore.desktop.width, uicore.desktop.height
   3112         if cdw != dw:
   3113             wDiff = dw - cdw
   3114             if left + width in (cdw, cdw - 16):
   3115                 left += wDiff
   3116             elif left not in (0, 16):
   3117                 oldCenterX = (cdw - width) / 2
   3118                 xPortion = oldCenterX / float(cdw)
   3119                 newCenterX = int(xPortion * dw)
   3120                 cxDiff = newCenterX - oldCenterX
   3121                 left += cxDiff
   3122         if cdh != dh:
   3123             hDiff = dh - cdh
   3124             if top in (0, 16):
   3125                 pass
   3126             elif top + height in (cdh, cdh - 16):
   3127                 top += hDiff
   3128             else:
   3129                 oldCenterY = (cdh - height) / 2
   3130                 yPortion = oldCenterY / float(cdh)
   3131                 newCenterY = int(yPortion * dh)
   3132                 cyDiff = newCenterY - oldCenterY
   3133                 top += cyDiff
   3134         return (left,
   3135          top,
   3136          width,
   3137          height,
   3138          dw,
   3139          dh)
   3140 
   3141     @classmethod
   3142     def GetGroupRect(cls, group, getAbsolute = 0):
   3143         if not len(group):
   3144             return (0, 0, 0, 0)
   3145         l, t, w, h = group[0].GetAbsolute()
   3146         r = l + w
   3147         b = t + h
   3148         for wnd in group[1:]:
   3149             wl, wt, ww, wh = wnd.GetAbsolute()
   3150             l = min(l, wl)
   3151             t = min(t, wt)
   3152             r = max(r, wl + ww)
   3153             b = max(b, wt + wh)
   3154 
   3155         if getAbsolute:
   3156             return (l,
   3157              t,
   3158              r - l,
   3159              b - t)
   3160         return (l,
   3161          t,
   3162          r,
   3163          b)
   3164 
   3165     @classmethod
   3166     def GetDefaultSizeAndPosition(cls):
   3167         dw = uicore.desktop.width
   3168         dh = uicore.desktop.height
   3169         if cls.default_fixedWidth:
   3170             width = cls.default_fixedWidth
   3171         elif callable(cls.default_width):
   3172             width = cls.default_width()
   3173         else:
   3174             width = cls.default_width
   3175         if cls.default_fixedHeight:
   3176             height = cls.default_fixedHeight
   3177         elif callable(cls.default_height):
   3178             height = cls.default_height()
   3179         else:
   3180             height = cls.default_height
   3181         if cls.default_left == '__center__':
   3182             left = (dw - width) / 2
   3183         elif cls.default_left == '__right__':
   3184             left = dw - width
   3185         elif callable(cls.default_left):
   3186             left = cls.default_left()
   3187         else:
   3188             left = cls.default_left
   3189         if cls.default_top == '__center__':
   3190             top = (dh - height) / 2
   3191         elif cls.default_top == '__bottom__':
   3192             top = dh - height
   3193         elif callable(cls.default_top):
   3194             top = cls.default_top()
   3195         else:
   3196             top = cls.default_top
   3197         return (left,
   3198          top,
   3199          width,
   3200          height)
   3201 
   3202     @classmethod
   3203     def GetDefaultLeftOffset(cls, width, align = None, left = 0):
   3204         return sm.GetService('window').GetCameraLeftOffset(width, align, left)
   3205 
   3206     def RegisterSceneContainer(self, sceneCont):
   3207         self.__scene_containers.add(sceneCont)
   3208 
   3209     def UnregisterSceneContainer(self, sceneCont):
   3210         if sceneCont in self.__scene_containers:
   3211             self.__scene_containers.remove(sceneCont)
   3212 
   3213     def Flush(self):
   3214         mainArea = self.GetMainArea()
   3215         if mainArea is not None:
   3216             mainArea.Flush()
   3217 
   3218     @EatsExceptions('protoClientLogs')
   3219     def _LogWindowEventOpened(self):
   3220         if not self.analyticID:
   3221             return
   3222         message_bus = WindowMessenger(sm.GetService('publicGatewaySvc'))
   3223         message_bus.window_opened(self.analyticID)
   3224 
   3225     @EatsExceptions('protoClientLogs')
   3226     def _LogWindowClosed(self):
   3227         if not self.__opened_at_timestamp or not self.analyticID:
   3228             return
   3229         seconds, nanos = self.SplitBlueTimeIntoDecimalsAndFractions(self.__opened_at_timestamp)
   3230         message_bus = WindowMessenger(sm.GetService('publicGatewaySvc'))
   3231         message_bus.window_closed(self.analyticID, seconds, nanos)
   3232 
   3233     @EatsExceptions('protoClientLogs')
   3234     def _LogWindowEventFocused(self):
   3235         if not self.analyticID:
   3236             return
   3237         message_bus = WindowMessenger(sm.GetService('publicGatewaySvc'))
   3238         message_bus.window_focused(self.analyticID)
   3239 
   3240     @EatsExceptions('protoClientLogs')
   3241     def _LogWindowUnfocused(self):
   3242         if not self.__focused_at_timestamp or not self.analyticID:
   3243             return
   3244         seconds, nanos = self.SplitBlueTimeIntoDecimalsAndFractions(self.__focused_at_timestamp)
   3245         message_bus = WindowMessenger(sm.GetService('publicGatewaySvc'))
   3246         message_bus.window_unfocused(self.analyticID, seconds, nanos)
   3247 
   3248     @staticmethod
   3249     def SplitBlueTimeIntoDecimalsAndFractions(timestamp):
   3250         if timestamp is None:
   3251             return
   3252         full_duration = gametime.GetSecondsSinceWallclockTime(timestamp)
   3253         fractions, integer = math.modf(full_duration)
   3254         return (int(integer), int(fractions * 1000000000.0))
   3255 
   3256     def GetWindowSizeForContentSize(self, width = 0, height = 0):
   3257         left, top, right, bottom = self.GetContentToWindowEdgePadding()
   3258         if self.extend_content_into_header:
   3259             height = max(height, self.header_height)
   3260         return (width + left + right, height + top + bottom)
   3261 
   3262     def GetTotalContentPadding(self):
   3263         pad_top = self.content.padTop
   3264         if not self.extend_content_into_header:
   3265             pad_top += self.header_height
   3266         return (self.content.padLeft,
   3267          pad_top,
   3268          self.content.padRight,
   3269          self.content.padBottom)
   3270 
   3271     def GetContentToWindowEdgePadding(self):
   3272         left, top, right, bottom = self.GetTotalContentPadding()
   3273         margin_left, margin_top, margin_right, margin_bottom = self.GetWindowBorderPadding()
   3274         border_left, border_top, border_right, border_bottom = self.GetWindowBorderSize()
   3275         return (left + border_left + margin_left,
   3276          top + border_top + margin_top,
   3277          right + border_right + margin_right,
   3278          bottom + border_bottom + margin_bottom)
   3279 
   3280     def GetWindowBorderSize(self):
   3281         return (1, 1, 1, 1)
   3282 
   3283     def GetWindowBorderPadding(self):
   3284         return (1, 1, 1, 1)
   3285 
   3286     def _SetHeader(self, header):
   3287         if self.closing:
   3288             raise RuntimeError("Attempting to assign a header to a window that's in the process of closing, or already closed")
   3289         if self.__header is not None:
   3290             self.__header.SetParent(None)
   3291             self.__header.unmount(self)
   3292         self.__header = header
   3293         self.__header.align = uiconst.TOTOP
   3294         self.__header.SetParent(self.sr.headerParent)
   3295         self.__header.mount(self)
   3296         self._UpdateContentPadding()
   3297 
   3298     def _OnCaptionChanged(self):
   3299         if self.sr.tab and hasattr(self.sr.tab, 'SetLabel'):
   3300             self.sr.tab.SetLabel(self.caption)
   3301 
   3302     def _UpdateContentPadding(self):
   3303         if self.closing:
   3304             return
   3305         if self.stacked:
   3306             header = self.stack.header
   3307         else:
   3308             header = self.header
   3309         if header:
   3310             self.__header_has_sufficient_bottom_padding = header.HAS_SUFFICIENT_BOTTOM_PADDING
   3311         self._UpdateHeaderInset()
   3312         old_padding = self.__previous_content_padding
   3313         new_padding = self.content_padding
   3314         self.__previous_content_padding = new_padding
   3315         if self.__apply_content_padding:
   3316             self.content.padding = new_padding
   3317         else:
   3318             self.content.padding = 0
   3319         if old_padding is not None and old_padding != new_padding:
   3320             self._emit_on_content_padding_changed()
   3321 
   3322     def _UpdateHeaderInset(self, silent = False):
   3323         if not self.closing:
   3324             header_inset = self._ComputeHeaderInset()
   3325             if self.__header_inset != header_inset:
   3326                 self.__header_inset = header_inset
   3327                 if not silent:
   3328                     self._emit_on_header_inset_changed()
   3329 
   3330     def _ComputeHeaderInset(self):
   3331         inset_left, _, inset_right, _ = self.content_padding
   3332         if self.__window_controls is not None and self.__window_controls.display:
   3333             inset_right = max(inset_right, self.__window_controls.reserved_width)
   3334         return (inset_left, inset_right)
   3335 
   3336     def __on_global_window_margin_mode_changed(self, margin_mode):
   3337         if not self.closing:
   3338             self._UpdateContentPadding()
   3339             if self.__on_margin_mode_changed is not None:
   3340                 self.__on_margin_mode_changed(self)
   3341 
   3342     def __on_header_parent_size_changed(self):
   3343         if not self.closing:
   3344             height = self.sr.headerParent.height
   3345             if self.__previous_header_height != height:
   3346                 self.__previous_header_height = height
   3347                 self.__window_controls_cont.height = height
   3348                 self._emit_on_header_height_changed()
   3349 
   3350     @property
   3351     def iconNum(self):
   3352         return self.icon
   3353 
   3354     @iconNum.setter
   3355     def iconNum(self, value):
   3356         self.icon = value
   3357 
   3358     def GetMainArea(self):
   3359         return self.content
   3360 
   3361     def IsCollapsed(self):
   3362         return self.collapsed
   3363 
   3364     def IsCompact(self):
   3365         return self.compact
   3366 
   3367     def IsCompactable(self):
   3368         return self.compactable
   3369 
   3370     def Compact(self):
   3371         self.compact = True
   3372 
   3373     def UnCompact(self):
   3374         self.compact = False
   3375 
   3376     def GetCaption(self, update = 1):
   3377         return self.caption or ''
   3378 
   3379     def SetCaption(self, caption, *args, **kwds):
   3380         if localization.IsValidLabel(caption):
   3381             caption = localization.GetByLabel(caption)
   3382         self.caption = caption
   3383 
   3384     def InStack(self):
   3385         return self.stacked
   3386 
   3387     def IsKillable(self):
   3388         return self.killable
   3389 
   3390     def MakeKillable(self):
   3391         self.killable = True
   3392 
   3393     def MakeUnKillable(self):
   3394         self.killable = False
   3395 
   3396     def IsLocked(self):
   3397         return self.locked
   3398 
   3399     def IsLockable(self):
   3400         return self.lockable
   3401 
   3402     def Lock(self):
   3403         self.locked = True
   3404 
   3405     def Unlock(self):
   3406         self.locked = False
   3407 
   3408     def HideHeaderButtons(self):
   3409         self.show_window_controls = False
   3410 
   3411     def GetStack(self):
   3412         return self.stack
   3413 
   3414     def IsMinimizable(self):
   3415         if is_blocked('minimize', self.windowID):
   3416             return False
   3417         return self.minimizable
   3418 
   3419     def MakeUnMinimizable(self):
   3420         self.minimizable = False
   3421 
   3422     def MakeCollapseable(self):
   3423         self.collapsible = True
   3424 
   3425     def MakeUncollapseable(self):
   3426         self.collapsible = False
   3427 
   3428     def IsOverlayable(self):
   3429         return self.overlayable