{
  "version": 1,
  "name": "Widgets",
  "icon_size": 32,
  "tab_height": 0,
  "layout": "wrap",
  "align": "left",
  "tabs": [
    {
      "id": "tab_46a5ff45",
      "name": "Widgets",
      "items": [
        {
          "kind": "section",
          "id": "item_e35c8fef",
          "label": "Playback",
          "label_align": "center",
          "text_style": "heading"
        },
        {
          "kind": "widget",
          "id": "item_8d654294",
          "label": "Evaluation",
          "icon": "",
          "tooltip": "DG / Serial / Parallel, Cached Playback and GPU Override: the current state lit, click to change",
          "command": "\"\"\"Evaluation: DG / Serial / Parallel with the current mode lit, a Cached\nPlayback light (lit while on, green once the whole playback range is cached,\namber when safe mode has switched it off) and a GPU Override light. Click any\nto change it. The \"why is my rig slow\" row.\"\"\"\nimport os\n\nfrom maya import cmds\nimport maya.api.OpenMaya as om\nfrom tdtoolkit import maya_utils\nfrom tdtoolkit.qt import Qt, QtCore, QtGui, QtWidgets, is_valid, exec_\nfrom tdtoolkit.widgets import resolve_color\n\n\ndef palette_of(widget):\n    \"\"\"The applied theme's colors, from the shelf that holds the widget.\"\"\"\n    while widget is not None:\n        fn = getattr(widget, \"theme_palette\", None)\n        if callable(fn):\n            try:\n                return fn()\n            except Exception:\n                return {}\n        widget = widget.parentWidget()\n    return {}\n\n\ndef row_height_of(widget):\n    while widget is not None:\n        fn = getattr(widget, \"row_height\", None)\n        if callable(fn):\n            try:\n                return int(fn())\n            except Exception:\n                break\n        widget = widget.parentWidget()\n    return 40\n\n\nclass Light(QtWidgets.QToolButton):\n    \"\"\"A button lit in a color while its state is on. Draws an icon\n    (``icon(painter, box, on, ink, faint)``) or a word.\"\"\"\n\n    def __init__(self, parent, size, palette, icon=None, text=\"\", width=None,\n                 lit=\"@colors.accent\", lit_fallback=\"#5285a6\"):\n        super().__init__(parent)\n        self.setObjectName(\"tdShelfLight\")\n        self.setCheckable(True)\n        self.setCursor(Qt.CursorShape.PointingHandCursor)\n        self.setFocusPolicy(Qt.FocusPolicy.NoFocus)\n        self._icon = icon\n        self._text = text\n        self._on_fill = resolve_color(lit, palette, lit_fallback)\n        self._off_fill = resolve_color(\"@colors.base\", palette, \"#2b2b2b\")\n        self._ink_on = resolve_color(\"@colors.text\", palette, \"#eeeeee\").lighter(115)\n        self._ink_off = resolve_color(\"@colors.text_disabled\", palette, \"#8a8a8a\")\n        self._border = resolve_color(\"@colors.border\", palette, \"#444444\")\n        self._hover = False\n        font = QtGui.QFont(self.font())\n        font.setBold(True)\n        font.setPixelSize(max(9, int(size * 0.34)))\n        self.setFont(font)\n        if width is None:\n            width = size if not text else max(size, QtGui.QFontMetrics(font).horizontalAdvance(text) + 16)\n        self.setFixedSize(int(width), int(size))\n\n    def set_lit_color(self, color):\n        self._on_fill = QtGui.QColor(color)\n        self.update()\n\n    def set_text(self, text):\n        self._text = text\n        self.update()\n\n    def set_state(self, on):\n        \"\"\"The state as read from Maya: shown, never announced.\"\"\"\n        self.blockSignals(True)\n        self.setChecked(bool(on))\n        self.blockSignals(False)\n        self.update()\n\n    def enterEvent(self, event):\n        self._hover = True\n        self.update()\n        super().enterEvent(event)\n\n    def leaveEvent(self, event):\n        self._hover = False\n        self.update()\n        super().leaveEvent(event)\n\n    def paintEvent(self, event):\n        on = self.isChecked()\n        painter = QtGui.QPainter(self)\n        painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)\n        if not self.isEnabled():\n            painter.setOpacity(0.45)\n        rect = QtCore.QRectF(self.rect()).adjusted(1.5, 1.5, -1.5, -1.5)\n        fill = QtGui.QColor(self._on_fill if on else self._off_fill)\n        if self._hover:\n            fill = fill.lighter(118)\n        if self.isDown():\n            fill = fill.darker(115)\n        painter.setPen(QtGui.QPen(self._on_fill.lighter(125) if on else self._border, 1))\n        painter.setBrush(fill)\n        painter.drawRoundedRect(rect, 5, 5)\n        if on:\n            # A lit button carries a light bar along its foot as well, so the\n            # state reads at a glance even where the accent is close to the base.\n            bar = QtCore.QRectF(rect.left() + 6, rect.bottom() - 4, rect.width() - 12, 2.5)\n            painter.setPen(Qt.PenStyle.NoPen)\n            painter.setBrush(self._ink_on)\n            painter.drawRoundedRect(bar, 1, 1)\n        ink = self._ink_on if on else self._ink_off\n        faint = QtGui.QColor(ink)\n        faint.setAlpha(150 if on else 110)\n        if self._text:\n            painter.setPen(ink)\n            painter.setFont(self.font())\n            painter.drawText(rect.adjusted(0, 0, 0, -3), Qt.AlignmentFlag.AlignCenter, self._text)\n        elif self._icon is not None:\n            inset = max(6.0, rect.height() * 0.2)\n            box = rect.adjusted(inset, inset - 1, -inset, -inset - 2)\n            self._icon(painter, box, on, ink, faint)\n        painter.end()\n\n\nclass Row(QtWidgets.QWidget):\n    \"\"\"Lights side by side, as one shelf widget.\"\"\"\n\n    def __init__(self, parent, name):\n        super().__init__(parent)\n        self.setObjectName(name)\n        self._layout = QtWidgets.QHBoxLayout(self)\n        self._layout.setContentsMargins(0, 0, 0, 0)\n        self._layout.setSpacing(3)\n\n    def add(self, widget):\n        self._layout.addWidget(widget)\n        return widget\n\n\ndef listen(widget, refresh, events=(), scene=(), poll_ms=0):\n    \"\"\"Call ``refresh`` after any of Maya's ``events`` (names for\n    ``MEventMessage``; one this Maya lacks is skipped) or ``scene`` messages\n    (``MSceneMessage`` kinds), coalesced through a 0 ms timer, and every\n    ``poll_ms`` for a state Maya announces nowhere. Everything is taken off\n    again with the widget: a shelf rebuilds its widgets often.\"\"\"\n    timer = QtCore.QTimer(widget)\n    timer.setSingleShot(True)\n    timer.setInterval(0)\n\n    def safe():\n        if is_valid(widget):\n            refresh()\n\n    timer.timeout.connect(safe)\n    handles, heard = [], []\n    for event in events:\n        try:\n            handles.append(om.MEventMessage.addEventCallback(event, lambda *_: timer.start()))\n            heard.append(event)\n        except Exception:\n            pass\n    for kind in scene:\n        handles.append(om.MSceneMessage.addCallback(kind, lambda *_: timer.start()))\n    if poll_ms:\n        poll = QtCore.QTimer(widget)\n        poll.setInterval(int(poll_ms))\n        poll.timeout.connect(safe)\n        poll.start()\n    widget.destroyed.connect(lambda *_: maya_utils.remove_callbacks(handles))\n    widget.heard = heard\n    return timer\n\n\n\nMODES = (\n    (\"DG\", \"off\", \"DG: the classic dependency graph, one node at a time\"),\n    (\"Serial\", \"serial\", \"Serial: the evaluation graph, one node at a time (for finding cycles)\"),\n    (\"Parallel\", \"parallel\", \"Parallel: the evaluation graph across every core\"),\n)\nGREEN = \"#4c9a63\"           # a cache that holds the whole playback range\nGPU_GREEN = \"#55b46a\"       # GPU Override, lit\n# GPU Override, exactly as Preferences > Animation drives it (read out of\n# Maya's own createPrefWndUI.mel and initHUDScriptsEvaluationManager.mel):\n# the checkbox writes the ``gpuOverride`` option var and calls\n# ``turnOnOpenCLEvaluatorActive`` / ``turnOffOpenCLEvaluatorActive``, which\n# load the ``GPUBuiltInDeformer`` plug-in and toggle the *active* state of the\n# ``deformer`` evaluator -- ``evaluator -q -name deformer``, which is not the\n# same as ``evaluator -q -enable``. Driving ``-enable`` instead leaves the\n# preference untouched, which is what this widget used to do.\nGPU_EVALUATOR = \"deformer\"\nGPU_OPTION_VAR = \"gpuOverride\"\nGPU_PROCS = \"initHUDScriptsEvaluationManager.mel\"\n\n\ndef mode():\n    return (cmds.evaluationManager(query=True, mode=True) or [\"off\"])[0]\n\n\ndef cache_on():\n    return bool(cmds.evaluator(name=\"cache\", query=True, enable=True))\n\n\ndef gpu_supported():\n    return GPU_EVALUATOR in (cmds.evaluator(query=True) or [])\n\n\ndef _flag(value):\n    \"\"\"``evaluator -q`` answers with a bare value in some Mayas and a one-item\n    list in others.\"\"\"\n    if isinstance(value, (list, tuple)):\n        value = value[0] if value else False\n    return bool(value)\n\n\ndef gpu_on():\n    \"\"\"Whether GPU Override is running, as Maya's own ``isEvaluatorActive`` asks.\"\"\"\n    if not gpu_supported():\n        return False\n    try:\n        return _flag(cmds.evaluator(query=True, name=GPU_EVALUATOR))\n    except Exception:\n        return False\n\n\ndef gpu_preference():\n    \"\"\"What the Preferences checkbox is showing (it may lead the live state).\"\"\"\n    try:\n        return bool(cmds.optionVar(query=GPU_OPTION_VAR))\n    except Exception:\n        return False\n\n\ndef set_gpu(on):\n    \"\"\"Turn GPU Override on or off the way the preference checkbox does: the\n    option var it reads, then Maya's own proc, which loads the plug-in on\n    first use and rebuilds the graph.\"\"\"\n    try:\n        if not maya_utils.mel.eval(\"exists turnOnOpenCLEvaluatorActive\"):\n            maya_utils.mel.eval('source \"%s\"' % GPU_PROCS)\n    except Exception:\n        pass\n    cmds.optionVar(intValue=(GPU_OPTION_VAR, int(bool(on))))\n    maya_utils.mel.eval(\"turnOnOpenCLEvaluatorActive()\" if on else \"turnOffOpenCLEvaluatorActive()\")\n\n\ndef cache_fill():\n    \"\"\"How much of the playback range is cached, 0..1; None when Maya cannot say.\"\"\"\n    try:\n        frames = cmds.cacheEvaluator(query=True, cachedFrames=True) or []\n    except Exception:\n        return None\n    # Maya 2025 answers ``[[1, 5, 30]]``: one entry per cached range, its\n    # start and end the last two numbers. A flat ``[5, 30]`` is read too.\n    ranges = []\n    if frames and not isinstance(frames[0], (list, tuple)):\n        frames = [frames]\n    for entry in frames:\n        entry = list(entry)\n        if len(entry) >= 2:\n            ranges.append((float(entry[-2]), float(entry[-1])))\n    lo, hi = cmds.playbackOptions(query=True, minTime=True), cmds.playbackOptions(query=True, maxTime=True)\n    span = hi - lo + 1\n    if span <= 0:\n        return None\n    covered = 0.0\n    for start, end in ranges:\n        a, b = max(start, lo), min(end, hi)\n        if b >= a:\n            covered += b - a + 1\n    return min(1.0, covered / span)\n\n\ndef safe_mode_tripped():\n    try:\n        return bool(cmds.cacheEvaluator(query=True, safeModeMessages=True))\n    except Exception:\n        return False\n\n\ndef paint_cache(painter, box, on, ink, faint, fill):\n    x, y, w, h = box.left(), box.top(), box.width(), box.height()\n    # A drum (a cache), filling from the bottom as the range fills.\n    top = QtCore.QRectF(x + w * 0.15, y, w * 0.7, h * 0.3)\n    painter.setPen(QtGui.QPen(ink, 1.5, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap))\n    painter.setBrush(Qt.BrushStyle.NoBrush)\n    painter.drawLine(QtCore.QPointF(x + w * 0.15, y + h * 0.15), QtCore.QPointF(x + w * 0.15, y + h * 0.85))\n    painter.drawLine(QtCore.QPointF(x + w * 0.85, y + h * 0.15), QtCore.QPointF(x + w * 0.85, y + h * 0.85))\n    painter.drawArc(QtCore.QRectF(x + w * 0.15, y + h * 0.7, w * 0.7, h * 0.3), 180 * 16, 180 * 16)\n    if fill:\n        level = y + h * 0.85 - (h * 0.6) * fill\n        painter.setPen(Qt.PenStyle.NoPen)\n        painter.setBrush(faint)\n        painter.drawRect(QtCore.QRectF(x + w * 0.15, level, w * 0.7, y + h * 0.85 - level))\n    painter.setPen(QtGui.QPen(ink, 1.5))\n    painter.setBrush(faint if on else Qt.BrushStyle.NoBrush)\n    painter.drawEllipse(top)\n\n\ndef build(parent, item):\n    palette = palette_of(parent)\n    size = row_height_of(parent)\n    row = Row(parent, \"tdShelfEvaluation\")\n    state = {\"fill\": 0.0}\n    modes = {}\n    for label, value, what in MODES:\n        light = row.add(Light(row, size, palette, text=label))\n        light.setProperty(\"tdMode\", value)\n        light.setToolTip(what + \"\\nClick to switch to it.\")\n        light.clicked.connect(lambda _checked=False, v=value: set_mode(v))\n        modes[value] = light\n    cache = row.add(Light(row, size, palette, icon=lambda p, b, on, ink, faint: paint_cache(p, b, on, ink, faint, state[\"fill\"])))\n    cache.setObjectName(\"tdShelfCacheLight\")\n    gpu = row.add(Light(row, size, palette, text=\"GPU\", lit=GPU_GREEN, lit_fallback=GPU_GREEN))\n    gpu.setObjectName(\"tdShelfGpuLight\")\n    accent = resolve_color(\"@colors.accent\", palette, \"#5285a6\")\n    amber = resolve_color(\"@colors.warning\", palette, \"#c8a24b\")\n    row.modes, row.cache, row.gpu = modes, cache, gpu\n\n    def refresh():\n        current = mode()\n        for value, light in modes.items():\n            light.set_state(value == current)\n        on = cache_on()\n        fill = cache_fill() if on else None\n        state[\"fill\"] = fill or 0.0\n        tripped = on and safe_mode_tripped()\n        cache.set_lit_color(amber if tripped else (GREEN if fill is not None and fill >= 0.999 else accent))\n        cache.set_state(on)\n        if not on:\n            cache.setToolTip(\"Cached Playback: off.\\nClick to turn it on.\")\n        elif tripped:\n            cache.setToolTip(\"Cached Playback: on, but safe mode has switched it off for this scene.\\nClick to turn it off.\")\n        elif fill is None:\n            cache.setToolTip(\"Cached Playback: on.\\nClick to turn it off.\")\n        else:\n            cache.setToolTip(\"Cached Playback: on, %d%% of the playback range cached.\\nClick to turn it off.\" % round(fill * 100))\n        # Maya greys its own GPU Override checkbox out in DG mode, where the\n        # evaluator has nothing to run in; this follows.\n        supported, running = gpu_supported(), gpu_on()\n        gpu.setEnabled(supported and current != \"off\")\n        gpu.set_state(running)\n        if not supported:\n            gpu.setToolTip(\"GPU Override: not available in this Maya (no deformer evaluator is registered).\")\n        elif current == \"off\":\n            gpu.setToolTip(\"GPU Override: %s, but DG mode does not use it.\\nSwitch to Serial or Parallel first.\"\n                           % (\"on\" if running else \"off\"))\n        else:\n            tip = \"GPU Override: %s.\\nDeformers run on the GPU. Click to turn it %s (the same as Preferences > Animation).\" \\\n                  % (\"on\" if running else \"off\", \"off\" if running else \"on\")\n            if gpu_preference() != running:\n                tip += \"\\nThe preference says %s: the evaluator has not caught up.\" % (\"on\" if gpu_preference() else \"off\")\n            gpu.setToolTip(tip)\n\n    def set_mode(value):\n        with maya_utils.undo_paused():\n            cmds.evaluationManager(mode=value)\n        refresh()\n\n    def toggle_cache():\n        with maya_utils.undo_paused():\n            cmds.evaluator(name=\"cache\", enable=not cache_on())\n        refresh()\n\n    def toggle_gpu():\n        # Maya's proc re-triggers an evaluation (``currentTime``), which is an\n        # unnamed undo step, so the whole thing is kept off the queue.\n        if gpu_supported():\n            with maya_utils.undo_paused():\n                set_gpu(not gpu_on())\n        refresh()\n\n    cache.clicked.connect(lambda *_: toggle_cache())\n    gpu.clicked.connect(lambda *_: toggle_gpu())\n    listen(row, refresh, events=(\"customEvaluatorChanged\", \"cachingEvaluationModeChanged\", \"cachingPreferencesChanged\",\n                                 \"cachingSafeModeChanged\", \"playbackRangeChanged\", \"SceneOpened\", \"NewSceneOpened\"),\n           poll_ms=1000)                          # the cache fills during playback, and says nothing when it does\n    refresh()\n    return row\n",
          "language": "python",
          "extra": {
            "entry": "build"
          }
        },
        {
          "kind": "widget",
          "id": "item_e84e94ff",
          "label": "Frame Range Presets",
          "icon": "",
          "tooltip": "Full, Render, Keys and \u00b110: click to set the playback range; the one it equals is lit",
          "command": "\"\"\"Frame range presets: Full, Render, Keys and +-10, the one matching the\nplayback range lit. Follows the Range Slider, the current frame and the\nselection, so a range set by hand lights the preset it happens to equal.\"\"\"\nimport os\n\nfrom maya import cmds\nimport maya.api.OpenMaya as om\nfrom tdtoolkit import maya_utils\nfrom tdtoolkit.qt import Qt, QtCore, QtGui, QtWidgets, is_valid, exec_\nfrom tdtoolkit.widgets import resolve_color\n\n\ndef palette_of(widget):\n    \"\"\"The applied theme's colors, from the shelf that holds the widget.\"\"\"\n    while widget is not None:\n        fn = getattr(widget, \"theme_palette\", None)\n        if callable(fn):\n            try:\n                return fn()\n            except Exception:\n                return {}\n        widget = widget.parentWidget()\n    return {}\n\n\ndef row_height_of(widget):\n    while widget is not None:\n        fn = getattr(widget, \"row_height\", None)\n        if callable(fn):\n            try:\n                return int(fn())\n            except Exception:\n                break\n        widget = widget.parentWidget()\n    return 40\n\n\nclass Light(QtWidgets.QToolButton):\n    \"\"\"A button lit in a color while its state is on. Draws an icon\n    (``icon(painter, box, on, ink, faint)``) or a word.\"\"\"\n\n    def __init__(self, parent, size, palette, icon=None, text=\"\", width=None,\n                 lit=\"@colors.accent\", lit_fallback=\"#5285a6\"):\n        super().__init__(parent)\n        self.setObjectName(\"tdShelfLight\")\n        self.setCheckable(True)\n        self.setCursor(Qt.CursorShape.PointingHandCursor)\n        self.setFocusPolicy(Qt.FocusPolicy.NoFocus)\n        self._icon = icon\n        self._text = text\n        self._on_fill = resolve_color(lit, palette, lit_fallback)\n        self._off_fill = resolve_color(\"@colors.base\", palette, \"#2b2b2b\")\n        self._ink_on = resolve_color(\"@colors.text\", palette, \"#eeeeee\").lighter(115)\n        self._ink_off = resolve_color(\"@colors.text_disabled\", palette, \"#8a8a8a\")\n        self._border = resolve_color(\"@colors.border\", palette, \"#444444\")\n        self._hover = False\n        font = QtGui.QFont(self.font())\n        font.setBold(True)\n        font.setPixelSize(max(9, int(size * 0.34)))\n        self.setFont(font)\n        if width is None:\n            width = size if not text else max(size, QtGui.QFontMetrics(font).horizontalAdvance(text) + 16)\n        self.setFixedSize(int(width), int(size))\n\n    def set_lit_color(self, color):\n        self._on_fill = QtGui.QColor(color)\n        self.update()\n\n    def set_text(self, text):\n        self._text = text\n        self.update()\n\n    def set_state(self, on):\n        \"\"\"The state as read from Maya: shown, never announced.\"\"\"\n        self.blockSignals(True)\n        self.setChecked(bool(on))\n        self.blockSignals(False)\n        self.update()\n\n    def enterEvent(self, event):\n        self._hover = True\n        self.update()\n        super().enterEvent(event)\n\n    def leaveEvent(self, event):\n        self._hover = False\n        self.update()\n        super().leaveEvent(event)\n\n    def paintEvent(self, event):\n        on = self.isChecked()\n        painter = QtGui.QPainter(self)\n        painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)\n        if not self.isEnabled():\n            painter.setOpacity(0.45)\n        rect = QtCore.QRectF(self.rect()).adjusted(1.5, 1.5, -1.5, -1.5)\n        fill = QtGui.QColor(self._on_fill if on else self._off_fill)\n        if self._hover:\n            fill = fill.lighter(118)\n        if self.isDown():\n            fill = fill.darker(115)\n        painter.setPen(QtGui.QPen(self._on_fill.lighter(125) if on else self._border, 1))\n        painter.setBrush(fill)\n        painter.drawRoundedRect(rect, 5, 5)\n        if on:\n            # A lit button carries a light bar along its foot as well, so the\n            # state reads at a glance even where the accent is close to the base.\n            bar = QtCore.QRectF(rect.left() + 6, rect.bottom() - 4, rect.width() - 12, 2.5)\n            painter.setPen(Qt.PenStyle.NoPen)\n            painter.setBrush(self._ink_on)\n            painter.drawRoundedRect(bar, 1, 1)\n        ink = self._ink_on if on else self._ink_off\n        faint = QtGui.QColor(ink)\n        faint.setAlpha(150 if on else 110)\n        if self._text:\n            painter.setPen(ink)\n            painter.setFont(self.font())\n            painter.drawText(rect.adjusted(0, 0, 0, -3), Qt.AlignmentFlag.AlignCenter, self._text)\n        elif self._icon is not None:\n            inset = max(6.0, rect.height() * 0.2)\n            box = rect.adjusted(inset, inset - 1, -inset, -inset - 2)\n            self._icon(painter, box, on, ink, faint)\n        painter.end()\n\n\nclass Row(QtWidgets.QWidget):\n    \"\"\"Lights side by side, as one shelf widget.\"\"\"\n\n    def __init__(self, parent, name):\n        super().__init__(parent)\n        self.setObjectName(name)\n        self._layout = QtWidgets.QHBoxLayout(self)\n        self._layout.setContentsMargins(0, 0, 0, 0)\n        self._layout.setSpacing(3)\n\n    def add(self, widget):\n        self._layout.addWidget(widget)\n        return widget\n\n\ndef listen(widget, refresh, events=(), scene=(), poll_ms=0):\n    \"\"\"Call ``refresh`` after any of Maya's ``events`` (names for\n    ``MEventMessage``; one this Maya lacks is skipped) or ``scene`` messages\n    (``MSceneMessage`` kinds), coalesced through a 0 ms timer, and every\n    ``poll_ms`` for a state Maya announces nowhere. Everything is taken off\n    again with the widget: a shelf rebuilds its widgets often.\"\"\"\n    timer = QtCore.QTimer(widget)\n    timer.setSingleShot(True)\n    timer.setInterval(0)\n\n    def safe():\n        if is_valid(widget):\n            refresh()\n\n    timer.timeout.connect(safe)\n    handles, heard = [], []\n    for event in events:\n        try:\n            handles.append(om.MEventMessage.addEventCallback(event, lambda *_: timer.start()))\n            heard.append(event)\n        except Exception:\n            pass\n    for kind in scene:\n        handles.append(om.MSceneMessage.addCallback(kind, lambda *_: timer.start()))\n    if poll_ms:\n        poll = QtCore.QTimer(widget)\n        poll.setInterval(int(poll_ms))\n        poll.timeout.connect(safe)\n        poll.start()\n    widget.destroyed.connect(lambda *_: maya_utils.remove_callbacks(handles))\n    widget.heard = heard\n    return timer\n\n\n\nPRESETS = (\n    (\"Full\", None, \"the whole animation (the Range Slider's outer ends)\"),\n    (\"Render\", None, \"the render range (Render Settings)\"),\n    (\"Keys\", None, \"first to last key on the selection (every key with nothing selected)\"),\n    (\"\\u00b110\", 10, \"ten frames either side of the current frame\"),\n    (\"\\u00b120\", 20, \"twenty frames either side of the current frame\"),\n    (\"\\u00b150\", 50, \"fifty frames either side of the current frame\"),\n    (\"\\u00b1100\", 100, \"a hundred frames either side of the current frame\"),\n)\nSPANS = {name: span for name, span, _what in PRESETS if span}\n\n\ndef preset_range(name):\n    \"\"\"The (start, end) a preset stands for now, or None when it has none.\"\"\"\n    if name == \"Full\":\n        return cmds.playbackOptions(query=True, animationStartTime=True), cmds.playbackOptions(query=True, animationEndTime=True)\n    if name == \"Render\":\n        return cmds.getAttr(\"defaultRenderGlobals.startFrame\"), cmds.getAttr(\"defaultRenderGlobals.endFrame\")\n    if name == \"Keys\":\n        nodes = cmds.ls(selection=True) or cmds.ls(type=\"animCurve\") or []\n        times = cmds.keyframe(nodes, query=True, timeChange=True) if nodes else None\n        if not times:\n            return None\n        return min(times), max(times)\n    span = SPANS.get(name)\n    if not span:\n        return None\n    now = cmds.currentTime(query=True)\n    return now - span, now + span\n\n\ndef current_range():\n    return cmds.playbackOptions(query=True, minTime=True), cmds.playbackOptions(query=True, maxTime=True)\n\n\ndef apply_range(lo, hi):\n    with maya_utils.undo_paused():                # playbackOptions is an unnamed undo step\n        if lo < cmds.playbackOptions(query=True, animationStartTime=True):\n            cmds.playbackOptions(animationStartTime=lo)\n        if hi > cmds.playbackOptions(query=True, animationEndTime=True):\n            cmds.playbackOptions(animationEndTime=hi)\n        cmds.playbackOptions(minTime=lo, maxTime=hi)\n\n\ndef build(parent, item):\n    palette = palette_of(parent)\n    size = row_height_of(parent)\n    row = Row(parent, \"tdShelfRangePresets\")\n    lights = {}\n    for name, _span, what in PRESETS:\n        light = row.add(Light(row, size, palette, text=name))\n        light.setProperty(\"tdPreset\", name)\n        light.clicked.connect(lambda _checked=False, n=name: choose(n))\n        lights[name] = (light, what)\n\n    def refresh():\n        lo, hi = current_range()\n        for name, (light, what) in lights.items():\n            rng = preset_range(name)\n            on = rng is not None and abs(rng[0] - lo) < 1e-6 and abs(rng[1] - hi) < 1e-6\n            light.setEnabled(rng is not None)\n            light.set_state(on)\n            if rng is None:\n                light.setToolTip(\"%s: %s\\nNo keys to take a range from.\" % (name, what))\n            else:\n                light.setToolTip(\"%s: %s\\n%g to %g%s\" % (name, what, rng[0], rng[1],\n                                 \" (the playback range now)\" if on else \"\\nClick to make it the playback range.\"))\n\n    def choose(name):\n        rng = preset_range(name)\n        if rng is not None:\n            apply_range(*rng)\n        refresh()\n\n    listen(row, refresh, events=(\"playbackRangeChanged\", \"timeChanged\", \"SelectionChanged\",\n                                 \"SceneOpened\", \"NewSceneOpened\", \"Undo\", \"Redo\"))\n    refresh()\n    return row\n",
          "language": "python",
          "extra": {
            "entry": "build"
          }
        },
        {
          "kind": "widget",
          "id": "item_d88cb009",
          "label": "Scrub",
          "icon": "",
          "tooltip": "Drag to change the current frame",
          "command": "\"\"\"A mini time slider: drag to scrub the playback range.\n\nScrubbing is kept off the undo queue, as Maya's own time slider is. The\nslider follows the current frame and the playback range however they change\n(the time slider, playback, a scene opening): it listens to Maya's\n``timeChanged``, ``playbackRangeChanged`` and scene-opened events, and takes\nthose callbacks off again with the widget.\n\nLaid out like every row on this shelf: its caption (``extra[\"caption\"]``) in\none column, then the slider, as wide as the other rows' controls.\n\"\"\"\nimport maya.api.OpenMaya as om\nfrom tdtoolkit.qt import Qt, QtCore, QtWidgets, is_valid\nfrom tdtoolkit.widgets import resolve_color\nfrom tdtoolkit import maya_utils\nfrom maya import cmds\n\nCAPTIONS = (\"Select\", \"Range\", \"Camera\", \"Time\")   # every captioned row on this shelf: the column fits the widest\nBODY_WIDTH = 190                                   # every row's controls, so the buttons after them line up\n\n\ndef palette_of(widget):\n    \"\"\"The applied theme's colors, from the shelf that holds the widget.\"\"\"\n    while widget is not None:\n        fn = getattr(widget, \"theme_palette\", None)\n        if callable(fn):\n            try:\n                return fn()\n            except Exception:\n                return {}\n        widget = widget.parentWidget()\n    return {}\n\n\ndef row_height_of(widget):\n    while widget is not None:\n        fn = getattr(widget, \"row_height\", None)\n        if callable(fn):\n            try:\n                return int(fn())\n            except Exception:\n                break\n        widget = widget.parentWidget()\n    return 40\n\n\ndef captioned(parent, item, default):\n    \"\"\"A row laid out like every other on this shelf: the caption in a column\n    as wide as the widest of ``CAPTIONS`` (drawn as the shelf draws an inline\n    label), then ``BODY_WIDTH`` of controls. Answers the row, the layout to\n    put the controls in, and how tall a control should be.\"\"\"\n    palette = palette_of(parent)\n    height = max(20, row_height_of(parent) - 14)\n    row = QtWidgets.QWidget(parent)\n    layout = QtWidgets.QHBoxLayout(row)\n    layout.setContentsMargins(3, 0, 0, 0)\n    layout.setSpacing(0)\n    caption = QtWidgets.QLabel(str((getattr(item, \"extra\", None) or {}).get(\"caption\") or default), row)\n    color = resolve_color(\"@tools.shelf.heading\", palette)\n    if not color.isValid():\n        color = resolve_color(\"@colors.text\", palette, \"#dddddd\")\n    caption.setStyleSheet(\"color: %s; background: transparent;\" % color.name())\n    caption.setFixedWidth(max(caption.fontMetrics().horizontalAdvance(c) for c in CAPTIONS) + 8)\n    layout.addWidget(caption)\n    body = QtWidgets.QHBoxLayout()\n    body.setContentsMargins(0, 0, 0, 0)\n    body.setSpacing(4)\n    holder = QtWidgets.QWidget(row)\n    holder.setLayout(body)\n    holder.setFixedSize(BODY_WIDTH, height)\n    layout.addWidget(holder)\n    row.caption = caption\n    return row, holder, body, height\n\n\ndef listen(widget, refresh, events=(), scene=(), poll_ms=0):\n    \"\"\"Call ``refresh`` after any of Maya's ``events`` (names for\n    ``MEventMessage``; one this Maya lacks is skipped) or ``scene`` messages\n    (``MSceneMessage`` kinds), coalesced through a 0 ms timer, and every\n    ``poll_ms`` for a state Maya announces nowhere. Everything is taken off\n    again with the widget: a shelf rebuilds its widgets often.\"\"\"\n    timer = QtCore.QTimer(widget)\n    timer.setSingleShot(True)\n    timer.setInterval(0)\n\n    def safe():\n        if is_valid(widget):\n            refresh()\n\n    timer.timeout.connect(safe)\n    handles, heard = [], []\n    for event in events:\n        try:\n            handles.append(om.MEventMessage.addEventCallback(event, lambda *_: timer.start()))\n            heard.append(event)\n        except Exception:\n            pass\n    for kind in scene:\n        handles.append(om.MSceneMessage.addCallback(kind, lambda *_: timer.start()))\n    if poll_ms:\n        poll = QtCore.QTimer(widget)\n        poll.setInterval(int(poll_ms))\n        poll.timeout.connect(safe)\n        poll.start()\n    widget.destroyed.connect(lambda *_: maya_utils.remove_callbacks(handles))\n    widget.heard, widget.handles = heard, handles\n    return timer\n\n\ndef build(parent, item):\n    row, holder, body, height = captioned(parent, item, \"Time\")\n    slider = QtWidgets.QSlider(Qt.Orientation.Horizontal, holder)\n    slider.setFixedHeight(height)\n    slider.setFocusPolicy(Qt.FocusPolicy.NoFocus)\n\n    def refresh():\n        lo = int(cmds.playbackOptions(query=True, minTime=True))\n        hi = int(cmds.playbackOptions(query=True, maxTime=True))\n        now = cmds.currentTime(query=True)\n        slider.blockSignals(True)\n        slider.setRange(lo, max(hi, lo + 1))\n        slider.setValue(int(now))\n        slider.blockSignals(False)\n        slider.setToolTip(\"Frame %g of %d to %d. Drag to scrub the playback range.\" % (now, lo, hi))\n\n    def scrub(value):\n        # currentTime from a script is an unnamed undo step: keep it off the queue\n        with maya_utils.undo_paused():\n            cmds.currentTime(value)\n\n    slider.valueChanged.connect(scrub)\n    body.addWidget(slider)\n    listen(row, refresh, events=(\"timeChanged\", \"playbackRangeChanged\", \"SceneOpened\", \"NewSceneOpened\"))\n    refresh()\n    return row\n",
          "language": "python",
          "extra": {
            "entry": "build",
            "caption": "Time"
          }
        },
        {
          "kind": "section",
          "id": "item_3355a96c",
          "label": "Scene",
          "label_align": "center",
          "text_style": "heading"
        },
        {
          "kind": "widget",
          "id": "item_3c314828",
          "label": "References",
          "icon": "",
          "tooltip": "The scene's references by state; load / unload the chosen one, right-click for more",
          "command": "\"\"\"References: the scene's references in a drop-down colored by state (loaded,\nunloaded, file missing), a light beside it that loads or unloads the chosen\none, and a right-click menu to reload it or select everything in its\nnamespace. Follows references being created, removed, loaded and unloaded.\"\"\"\nimport os\n\nfrom maya import cmds\nimport maya.api.OpenMaya as om\nfrom tdtoolkit import maya_utils\nfrom tdtoolkit.qt import Qt, QtCore, QtGui, QtWidgets, is_valid, exec_\nfrom tdtoolkit.widgets import resolve_color\n\n\ndef palette_of(widget):\n    \"\"\"The applied theme's colors, from the shelf that holds the widget.\"\"\"\n    while widget is not None:\n        fn = getattr(widget, \"theme_palette\", None)\n        if callable(fn):\n            try:\n                return fn()\n            except Exception:\n                return {}\n        widget = widget.parentWidget()\n    return {}\n\n\ndef row_height_of(widget):\n    while widget is not None:\n        fn = getattr(widget, \"row_height\", None)\n        if callable(fn):\n            try:\n                return int(fn())\n            except Exception:\n                break\n        widget = widget.parentWidget()\n    return 40\n\n\nclass Light(QtWidgets.QToolButton):\n    \"\"\"A button lit in a color while its state is on. Draws an icon\n    (``icon(painter, box, on, ink, faint)``) or a word.\"\"\"\n\n    def __init__(self, parent, size, palette, icon=None, text=\"\", width=None,\n                 lit=\"@colors.accent\", lit_fallback=\"#5285a6\"):\n        super().__init__(parent)\n        self.setObjectName(\"tdShelfLight\")\n        self.setCheckable(True)\n        self.setCursor(Qt.CursorShape.PointingHandCursor)\n        self.setFocusPolicy(Qt.FocusPolicy.NoFocus)\n        self._icon = icon\n        self._text = text\n        self._on_fill = resolve_color(lit, palette, lit_fallback)\n        self._off_fill = resolve_color(\"@colors.base\", palette, \"#2b2b2b\")\n        self._ink_on = resolve_color(\"@colors.text\", palette, \"#eeeeee\").lighter(115)\n        self._ink_off = resolve_color(\"@colors.text_disabled\", palette, \"#8a8a8a\")\n        self._border = resolve_color(\"@colors.border\", palette, \"#444444\")\n        self._hover = False\n        font = QtGui.QFont(self.font())\n        font.setBold(True)\n        font.setPixelSize(max(9, int(size * 0.34)))\n        self.setFont(font)\n        if width is None:\n            width = size if not text else max(size, QtGui.QFontMetrics(font).horizontalAdvance(text) + 16)\n        self.setFixedSize(int(width), int(size))\n\n    def set_lit_color(self, color):\n        self._on_fill = QtGui.QColor(color)\n        self.update()\n\n    def set_text(self, text):\n        self._text = text\n        self.update()\n\n    def set_state(self, on):\n        \"\"\"The state as read from Maya: shown, never announced.\"\"\"\n        self.blockSignals(True)\n        self.setChecked(bool(on))\n        self.blockSignals(False)\n        self.update()\n\n    def enterEvent(self, event):\n        self._hover = True\n        self.update()\n        super().enterEvent(event)\n\n    def leaveEvent(self, event):\n        self._hover = False\n        self.update()\n        super().leaveEvent(event)\n\n    def paintEvent(self, event):\n        on = self.isChecked()\n        painter = QtGui.QPainter(self)\n        painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)\n        if not self.isEnabled():\n            painter.setOpacity(0.45)\n        rect = QtCore.QRectF(self.rect()).adjusted(1.5, 1.5, -1.5, -1.5)\n        fill = QtGui.QColor(self._on_fill if on else self._off_fill)\n        if self._hover:\n            fill = fill.lighter(118)\n        if self.isDown():\n            fill = fill.darker(115)\n        painter.setPen(QtGui.QPen(self._on_fill.lighter(125) if on else self._border, 1))\n        painter.setBrush(fill)\n        painter.drawRoundedRect(rect, 5, 5)\n        if on:\n            # A lit button carries a light bar along its foot as well, so the\n            # state reads at a glance even where the accent is close to the base.\n            bar = QtCore.QRectF(rect.left() + 6, rect.bottom() - 4, rect.width() - 12, 2.5)\n            painter.setPen(Qt.PenStyle.NoPen)\n            painter.setBrush(self._ink_on)\n            painter.drawRoundedRect(bar, 1, 1)\n        ink = self._ink_on if on else self._ink_off\n        faint = QtGui.QColor(ink)\n        faint.setAlpha(150 if on else 110)\n        if self._text:\n            painter.setPen(ink)\n            painter.setFont(self.font())\n            painter.drawText(rect.adjusted(0, 0, 0, -3), Qt.AlignmentFlag.AlignCenter, self._text)\n        elif self._icon is not None:\n            inset = max(6.0, rect.height() * 0.2)\n            box = rect.adjusted(inset, inset - 1, -inset, -inset - 2)\n            self._icon(painter, box, on, ink, faint)\n        painter.end()\n\n\nclass Row(QtWidgets.QWidget):\n    \"\"\"Lights side by side, as one shelf widget.\"\"\"\n\n    def __init__(self, parent, name):\n        super().__init__(parent)\n        self.setObjectName(name)\n        self._layout = QtWidgets.QHBoxLayout(self)\n        self._layout.setContentsMargins(0, 0, 0, 0)\n        self._layout.setSpacing(3)\n\n    def add(self, widget):\n        self._layout.addWidget(widget)\n        return widget\n\n\ndef listen(widget, refresh, events=(), scene=(), poll_ms=0):\n    \"\"\"Call ``refresh`` after any of Maya's ``events`` (names for\n    ``MEventMessage``; one this Maya lacks is skipped) or ``scene`` messages\n    (``MSceneMessage`` kinds), coalesced through a 0 ms timer, and every\n    ``poll_ms`` for a state Maya announces nowhere. Everything is taken off\n    again with the widget: a shelf rebuilds its widgets often.\"\"\"\n    timer = QtCore.QTimer(widget)\n    timer.setSingleShot(True)\n    timer.setInterval(0)\n\n    def safe():\n        if is_valid(widget):\n            refresh()\n\n    timer.timeout.connect(safe)\n    handles, heard = [], []\n    for event in events:\n        try:\n            handles.append(om.MEventMessage.addEventCallback(event, lambda *_: timer.start()))\n            heard.append(event)\n        except Exception:\n            pass\n    for kind in scene:\n        handles.append(om.MSceneMessage.addCallback(kind, lambda *_: timer.start()))\n    if poll_ms:\n        poll = QtCore.QTimer(widget)\n        poll.setInterval(int(poll_ms))\n        poll.timeout.connect(safe)\n        poll.start()\n    widget.destroyed.connect(lambda *_: maya_utils.remove_callbacks(handles))\n    widget.heard = heard\n    return timer\n\n\n\n\ndef references():\n    rows = []\n    for node in cmds.ls(type=\"reference\") or []:\n        if \"sharedReferenceNode\" in node:\n            continue\n        try:\n            path = cmds.referenceQuery(node, filename=True, withoutCopyNumber=True)\n        except Exception:\n            continue                          # a reference node with no file behind it\n        try:\n            namespace = cmds.referenceQuery(node, namespace=True).lstrip(\":\")\n        except Exception:\n            namespace = \"\"\n        try:\n            loaded = bool(cmds.referenceQuery(node, isLoaded=True))\n        except Exception:\n            loaded = False\n        rows.append({\"node\": node, \"path\": path, \"namespace\": namespace or node,\n                     \"loaded\": loaded, \"missing\": not os.path.isfile(path)})\n    return rows\n\n\ndef paint_reload(painter, box, on, ink, faint):\n    x, y, w, h = box.left(), box.top(), box.width(), box.height()\n    # A circular arrow: most of a ring, with an arrow head where it ends.\n    ring = QtCore.QRectF(x + w * 0.1, y + h * 0.1, w * 0.8, h * 0.8)\n    painter.setPen(QtGui.QPen(ink, 2.0, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap))\n    painter.setBrush(Qt.BrushStyle.NoBrush)\n    painter.drawArc(ring, 40 * 16, 280 * 16)\n    tip = QtCore.QPointF(x + w * 0.9, y + h * 0.28)\n    painter.setPen(Qt.PenStyle.NoPen)\n    painter.setBrush(ink)\n    painter.drawPolygon(QtGui.QPolygonF([tip, QtCore.QPointF(tip.x() - w * 0.3, tip.y() - h * 0.02),\n                                         QtCore.QPointF(tip.x() - w * 0.02, tip.y() + h * 0.3)]))\n\n\nclass RefCombo(QtWidgets.QComboBox):\n    def __init__(self, parent, on_menu):\n        super().__init__(parent)\n        self._on_menu = on_menu\n        self.setFocusPolicy(Qt.FocusPolicy.NoFocus)\n\n    def contextMenuEvent(self, event):\n        self._on_menu(event.globalPos())\n\n    def wheelEvent(self, event):\n        event.ignore()                        # the wheel scrolls the shelf, never switches a reference\n\n\ndef build(parent, item):\n    palette = palette_of(parent)\n    size = row_height_of(parent)\n    row = Row(parent, \"tdShelfReferences\")\n    colors = {\n        \"loaded\": resolve_color(\"@colors.text\", palette, \"#dddddd\"),\n        \"unloaded\": resolve_color(\"@colors.text_dim\", palette, \"#a0a0a0\"),\n        \"missing\": resolve_color(\"@colors.error\", palette, \"#c85a4b\"),\n    }\n    combo = row.add(RefCombo(row, lambda pos: menu_at(pos)))\n    combo.setFixedHeight(size)\n    combo.setMinimumWidth(int(size * 4.5))\n    combo.setSizeAdjustPolicy(QtWidgets.QComboBox.SizeAdjustPolicy.AdjustToContents)\n    toggle = row.add(Light(row, size, palette, icon=paint_reload))\n    toggle.setObjectName(\"tdShelfReferenceLoad\")\n    row.combo, row.toggle = combo, toggle\n\n    def chosen():\n        return combo.itemData(combo.currentIndex()) if combo.count() else None\n\n    def refresh():\n        keep = chosen()\n        rows = references()\n        combo.blockSignals(True)\n        combo.clear()\n        for ref in rows:\n            state = \"missing\" if ref[\"missing\"] else (\"loaded\" if ref[\"loaded\"] else \"unloaded\")\n            combo.addItem(ref[\"namespace\"], ref)\n            index = combo.count() - 1\n            combo.setItemData(index, QtGui.QBrush(colors[state]), Qt.ItemDataRole.ForegroundRole)\n            combo.setItemData(index, \"%s\\n%s%s\" % (ref[\"path\"], state, \", file not found\" if ref[\"missing\"] else \"\"),\n                              Qt.ItemDataRole.ToolTipRole)\n            if keep and keep.get(\"node\") == ref[\"node\"]:\n                combo.setCurrentIndex(index)\n        if not rows:\n            combo.addItem(\"(no references)\", None)\n        combo.setEnabled(bool(rows))\n        combo.blockSignals(False)\n        sync_toggle()\n\n    def sync_toggle():\n        ref = chosen()\n        toggle.setEnabled(ref is not None and not ref[\"missing\"])\n        toggle.set_state(bool(ref and ref[\"loaded\"]))\n        if ref is None:\n            toggle.setToolTip(\"No reference chosen.\")\n        elif ref[\"missing\"]:\n            toggle.setToolTip(\"%s: its file is missing\\n%s\" % (ref[\"namespace\"], ref[\"path\"]))\n        else:\n            toggle.setToolTip(\"%s: %s\\n%s\\nClick to %s it.\" % (\n                ref[\"namespace\"], \"loaded\" if ref[\"loaded\"] else \"unloaded\", ref[\"path\"],\n                \"unload\" if ref[\"loaded\"] else \"load\"))\n        combo.setToolTip(combo.itemData(combo.currentIndex(), Qt.ItemDataRole.ToolTipRole) or \"The scene's references\")\n\n    def load_or_unload():\n        ref = chosen()\n        if ref is None:\n            return\n        if ref[\"loaded\"]:\n            cmds.file(unloadReference=ref[\"node\"])\n        else:\n            cmds.file(loadReference=ref[\"node\"])\n        refresh()\n\n    def menu_at(pos):\n        ref = chosen()\n        if ref is None:\n            return\n        menu = QtWidgets.QMenu(combo)\n        menu.addAction(\"Select All Nodes in %s\" % ref[\"namespace\"], lambda: select_all(ref))\n        menu.addAction(\"Reload\", lambda: (cmds.file(loadReference=ref[\"node\"]), refresh()))\n        menu.addAction(\"Unload\" if ref[\"loaded\"] else \"Load\", load_or_unload)\n        exec_(menu, pos)\n        menu.deleteLater()\n\n    def select_all(ref):\n        nodes = cmds.referenceQuery(ref[\"node\"], nodes=True, dagPath=True) or []\n        cmds.select(nodes, replace=True)\n\n    toggle.clicked.connect(lambda *_: load_or_unload())\n    combo.currentIndexChanged.connect(lambda *_: sync_toggle())\n    kinds = om.MSceneMessage\n    listen(row, refresh, events=(\"Undo\", \"Redo\"),\n           scene=(kinds.kAfterOpen, kinds.kAfterNew, kinds.kAfterImport, kinds.kAfterCreateReference,\n                  kinds.kAfterRemoveReference, kinds.kAfterLoadReference, kinds.kAfterUnloadReference))\n    refresh()\n    return row\n",
          "language": "python",
          "extra": {
            "entry": "build"
          }
        },
        {
          "kind": "widget",
          "id": "item_7fd1119b",
          "label": "Snaps and Live",
          "icon": "",
          "tooltip": "Grid, curve and point snapping, and the live surface",
          "command": "\"\"\"Snaps: grid, curve and point snapping as one light each, lit while on,\nand a Live light that shows when a surface is live (its name in the tooltip)\nand clears it on click; with nothing live, a click makes the selection live.\"\"\"\nimport os\n\nfrom maya import cmds\nimport maya.api.OpenMaya as om\nfrom tdtoolkit import maya_utils\nfrom tdtoolkit.qt import Qt, QtCore, QtGui, QtWidgets, is_valid, exec_\nfrom tdtoolkit.widgets import resolve_color\n\n\ndef palette_of(widget):\n    \"\"\"The applied theme's colors, from the shelf that holds the widget.\"\"\"\n    while widget is not None:\n        fn = getattr(widget, \"theme_palette\", None)\n        if callable(fn):\n            try:\n                return fn()\n            except Exception:\n                return {}\n        widget = widget.parentWidget()\n    return {}\n\n\ndef row_height_of(widget):\n    while widget is not None:\n        fn = getattr(widget, \"row_height\", None)\n        if callable(fn):\n            try:\n                return int(fn())\n            except Exception:\n                break\n        widget = widget.parentWidget()\n    return 40\n\n\nclass Light(QtWidgets.QToolButton):\n    \"\"\"A button lit in a color while its state is on. Draws an icon\n    (``icon(painter, box, on, ink, faint)``) or a word.\"\"\"\n\n    def __init__(self, parent, size, palette, icon=None, text=\"\", width=None,\n                 lit=\"@colors.accent\", lit_fallback=\"#5285a6\"):\n        super().__init__(parent)\n        self.setObjectName(\"tdShelfLight\")\n        self.setCheckable(True)\n        self.setCursor(Qt.CursorShape.PointingHandCursor)\n        self.setFocusPolicy(Qt.FocusPolicy.NoFocus)\n        self._icon = icon\n        self._text = text\n        self._on_fill = resolve_color(lit, palette, lit_fallback)\n        self._off_fill = resolve_color(\"@colors.base\", palette, \"#2b2b2b\")\n        self._ink_on = resolve_color(\"@colors.text\", palette, \"#eeeeee\").lighter(115)\n        self._ink_off = resolve_color(\"@colors.text_disabled\", palette, \"#8a8a8a\")\n        self._border = resolve_color(\"@colors.border\", palette, \"#444444\")\n        self._hover = False\n        font = QtGui.QFont(self.font())\n        font.setBold(True)\n        font.setPixelSize(max(9, int(size * 0.34)))\n        self.setFont(font)\n        if width is None:\n            width = size if not text else max(size, QtGui.QFontMetrics(font).horizontalAdvance(text) + 16)\n        self.setFixedSize(int(width), int(size))\n\n    def set_lit_color(self, color):\n        self._on_fill = QtGui.QColor(color)\n        self.update()\n\n    def set_text(self, text):\n        self._text = text\n        self.update()\n\n    def set_state(self, on):\n        \"\"\"The state as read from Maya: shown, never announced.\"\"\"\n        self.blockSignals(True)\n        self.setChecked(bool(on))\n        self.blockSignals(False)\n        self.update()\n\n    def enterEvent(self, event):\n        self._hover = True\n        self.update()\n        super().enterEvent(event)\n\n    def leaveEvent(self, event):\n        self._hover = False\n        self.update()\n        super().leaveEvent(event)\n\n    def paintEvent(self, event):\n        on = self.isChecked()\n        painter = QtGui.QPainter(self)\n        painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)\n        if not self.isEnabled():\n            painter.setOpacity(0.45)\n        rect = QtCore.QRectF(self.rect()).adjusted(1.5, 1.5, -1.5, -1.5)\n        fill = QtGui.QColor(self._on_fill if on else self._off_fill)\n        if self._hover:\n            fill = fill.lighter(118)\n        if self.isDown():\n            fill = fill.darker(115)\n        painter.setPen(QtGui.QPen(self._on_fill.lighter(125) if on else self._border, 1))\n        painter.setBrush(fill)\n        painter.drawRoundedRect(rect, 5, 5)\n        if on:\n            # A lit button carries a light bar along its foot as well, so the\n            # state reads at a glance even where the accent is close to the base.\n            bar = QtCore.QRectF(rect.left() + 6, rect.bottom() - 4, rect.width() - 12, 2.5)\n            painter.setPen(Qt.PenStyle.NoPen)\n            painter.setBrush(self._ink_on)\n            painter.drawRoundedRect(bar, 1, 1)\n        ink = self._ink_on if on else self._ink_off\n        faint = QtGui.QColor(ink)\n        faint.setAlpha(150 if on else 110)\n        if self._text:\n            painter.setPen(ink)\n            painter.setFont(self.font())\n            painter.drawText(rect.adjusted(0, 0, 0, -3), Qt.AlignmentFlag.AlignCenter, self._text)\n        elif self._icon is not None:\n            inset = max(6.0, rect.height() * 0.2)\n            box = rect.adjusted(inset, inset - 1, -inset, -inset - 2)\n            self._icon(painter, box, on, ink, faint)\n        painter.end()\n\n\nclass Row(QtWidgets.QWidget):\n    \"\"\"Lights side by side, as one shelf widget.\"\"\"\n\n    def __init__(self, parent, name):\n        super().__init__(parent)\n        self.setObjectName(name)\n        self._layout = QtWidgets.QHBoxLayout(self)\n        self._layout.setContentsMargins(0, 0, 0, 0)\n        self._layout.setSpacing(3)\n\n    def add(self, widget):\n        self._layout.addWidget(widget)\n        return widget\n\n\ndef listen(widget, refresh, events=(), scene=(), poll_ms=0):\n    \"\"\"Call ``refresh`` after any of Maya's ``events`` (names for\n    ``MEventMessage``; one this Maya lacks is skipped) or ``scene`` messages\n    (``MSceneMessage`` kinds), coalesced through a 0 ms timer, and every\n    ``poll_ms`` for a state Maya announces nowhere. Everything is taken off\n    again with the widget: a shelf rebuilds its widgets often.\"\"\"\n    timer = QtCore.QTimer(widget)\n    timer.setSingleShot(True)\n    timer.setInterval(0)\n\n    def safe():\n        if is_valid(widget):\n            refresh()\n\n    timer.timeout.connect(safe)\n    handles, heard = [], []\n    for event in events:\n        try:\n            handles.append(om.MEventMessage.addEventCallback(event, lambda *_: timer.start()))\n            heard.append(event)\n        except Exception:\n            pass\n    for kind in scene:\n        handles.append(om.MSceneMessage.addCallback(kind, lambda *_: timer.start()))\n    if poll_ms:\n        poll = QtCore.QTimer(widget)\n        poll.setInterval(int(poll_ms))\n        poll.timeout.connect(safe)\n        poll.start()\n    widget.destroyed.connect(lambda *_: maya_utils.remove_callbacks(handles))\n    widget.heard = heard\n    return timer\n\n\n\n\ndef paint_grid(painter, box, on, ink, faint):\n    x, y, w, h = box.left(), box.top(), box.width(), box.height()\n    painter.setPen(QtGui.QPen(faint, 1.2))\n    for t in (0.0, 0.5, 1.0):\n        painter.drawLine(QtCore.QPointF(x, y + h * t), QtCore.QPointF(x + w, y + h * t))\n        painter.drawLine(QtCore.QPointF(x + w * t, y), QtCore.QPointF(x + w * t, y + h))\n    painter.setPen(Qt.PenStyle.NoPen)\n    painter.setBrush(ink)\n    painter.drawEllipse(QtCore.QPointF(x + w * 0.5, y + h * 0.5), 2.4, 2.4)\n\n\ndef paint_curve(painter, box, on, ink, faint):\n    x, y, w, h = box.left(), box.top(), box.width(), box.height()\n    path = QtGui.QPainterPath(QtCore.QPointF(x, y + h * 0.9))\n    path.cubicTo(QtCore.QPointF(x + w * 0.2, y - h * 0.2), QtCore.QPointF(x + w * 0.8, y + h * 1.2),\n                 QtCore.QPointF(x + w, y + h * 0.1))\n    painter.setPen(QtGui.QPen(faint, 1.8, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap))\n    painter.setBrush(Qt.BrushStyle.NoBrush)\n    painter.drawPath(path)\n    painter.setPen(Qt.PenStyle.NoPen)\n    painter.setBrush(ink)\n    painter.drawEllipse(path.pointAtPercent(0.5), 2.4, 2.4)\n\n\ndef paint_point(painter, box, on, ink, faint):\n    x, y, w, h = box.left(), box.top(), box.width(), box.height()\n    c = box.center()\n    diamond = QtGui.QPolygonF([QtCore.QPointF(c.x(), y), QtCore.QPointF(x + w, c.y()),\n                               QtCore.QPointF(c.x(), y + h), QtCore.QPointF(x, c.y())])\n    painter.setPen(QtGui.QPen(faint, 1.4, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap, Qt.PenJoinStyle.RoundJoin))\n    painter.setBrush(Qt.BrushStyle.NoBrush)\n    painter.drawPolygon(diamond)\n    painter.setPen(Qt.PenStyle.NoPen)\n    painter.setBrush(ink)\n    painter.drawEllipse(c, 2.4, 2.4)\n\n\ndef paint_live(painter, box, on, ink, faint):\n    x, y, w, h = box.left(), box.top(), box.width(), box.height()\n    # A surface seen edge-on with a pin standing on it: what \"live\" means.\n    sheet = QtGui.QPainterPath(QtCore.QPointF(x, y + h * 0.85))\n    sheet.quadTo(QtCore.QPointF(x + w * 0.5, y + h * 0.5), QtCore.QPointF(x + w, y + h * 0.85))\n    painter.setPen(QtGui.QPen(ink, 1.6, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap))\n    painter.setBrush(Qt.BrushStyle.NoBrush)\n    painter.drawPath(sheet)\n    painter.setPen(QtGui.QPen(ink, 1.8, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap))\n    painter.drawLine(QtCore.QPointF(x + w * 0.5, y + h * 0.66), QtCore.QPointF(x + w * 0.5, y + h * 0.22))\n    painter.setPen(Qt.PenStyle.NoPen)\n    painter.setBrush(ink if on else faint)\n    painter.drawEllipse(QtCore.QPointF(x + w * 0.5, y + h * 0.16), w * 0.16, w * 0.16)\n\n\nSNAPS = ((\"grid\", \"Snap to grids\", paint_grid), (\"curve\", \"Snap to curves\", paint_curve),\n         (\"point\", \"Snap to points\", paint_point))\n\n\ndef live_objects():\n    return cmds.ls(live=True) or []\n\n\ndef build(parent, item):\n    palette = palette_of(parent)\n    size = row_height_of(parent)\n    row = Row(parent, \"tdShelfSnaps\")\n    snaps = {}\n    for kind, what, icon in SNAPS:\n        light = row.add(Light(row, size, palette, icon=icon))\n        light.setProperty(\"tdSnap\", kind)\n        light.clicked.connect(lambda _checked=False, k=kind: toggle_snap(k))\n        snaps[kind] = (light, what)\n    live = row.add(Light(row, size, palette, icon=paint_live, lit=\"@colors.warning\", lit_fallback=\"#c8a24b\"))\n    live.setObjectName(\"tdShelfLiveLight\")\n    row.snaps, row.live = snaps, live\n\n    def refresh():\n        for kind, (light, what) in snaps.items():\n            on = bool(cmds.snapMode(query=True, **{kind: True}))\n            light.set_state(on)\n            light.setToolTip(\"%s: %s.\\nClick to turn it %s.\" % (what, \"on\" if on else \"off\", \"off\" if on else \"on\"))\n        names = live_objects()\n        live.set_state(bool(names))\n        if names:\n            live.setToolTip(\"Live surface: %s\\nClick to make nothing live.\" % \", \".join(names))\n        else:\n            live.setToolTip(\"Live surface: none.\\nClick to make the selection live.\")\n\n    def toggle_snap(kind):\n        with maya_utils.undo_paused():\n            cmds.snapMode(**{kind: not bool(cmds.snapMode(query=True, **{kind: True}))})\n        refresh()\n\n    def toggle_live():\n        if live_objects():\n            cmds.makeLive(none=True)\n        elif cmds.ls(selection=True):\n            cmds.makeLive()\n        refresh()\n\n    live.clicked.connect(lambda *_: toggle_live())\n    listen(row, refresh, events=(\"snapModeChanged\", \"SelectionChanged\", \"Undo\", \"Redo\", \"SceneOpened\", \"NewSceneOpened\"),\n           poll_ms=1500)                            # nothing announces makeLive\n    refresh()\n    return row\n",
          "language": "python",
          "extra": {
            "entry": "build"
          }
        },
        {
          "kind": "section",
          "id": "item_af9e566c",
          "label": "Viewport",
          "label_align": "center",
          "text_style": "heading"
        },
        {
          "kind": "widget",
          "id": "item_552eca78",
          "label": "NURBS Curves",
          "icon": "",
          "tooltip": "NURBS Curves: show or hide NURBS curves in every visible viewport (lit while shown)",
          "command": "\"\"\"NURBS Curves: show or hide NURBS curves in the viewports. Lit while shown.\n\nOne of the tool box's viewport toggles (shelf 09_viewport). The button reads\nthe focused viewport (else the first visible one), so it always says what you\nsee; a click sets every visible viewport, so they agree. It listens to Maya's\n``modelEditorChanged`` and ``ModelPanelSetFocus`` events, and takes those\ncallbacks off again with the widget (a shelf rebuilds its widgets often).\n\"\"\"\nfrom maya import cmds\nimport maya.api.OpenMaya as om\nfrom tdtoolkit import maya_utils\nfrom tdtoolkit.qt import Qt, QtCore, QtGui, QtWidgets, is_valid\nfrom tdtoolkit.widgets import resolve_color\n\nFLAGS = (\"nurbsCurves\",)                      # the modelEditor flag(s) this button drives\nNAME = \"NURBS Curves\"\n\n\ndef panels():\n    \"\"\"The visible model panels, the focused one first.\"\"\"\n    visible = [p for p in (cmds.getPanel(visiblePanels=True) or []) if cmds.getPanel(typeOf=p) == \"modelPanel\"]\n    focus = cmds.getPanel(withFocus=True)\n    if focus in visible:\n        visible.remove(focus)\n        visible.insert(0, focus)\n    return visible\n\n\ndef shown():\n    \"\"\"Whether the viewport you are looking at shows NURBS curves.\"\"\"\n    found = panels()\n    if not found:\n        return False\n    return any(cmds.modelEditor(found[0], query=True, **{flag: True}) for flag in FLAGS)\n\n\ndef show(on):\n    for panel in panels():\n        cmds.modelEditor(panel, edit=True, nurbsCurves=on)\n\n\ndef palette_of(widget):\n    \"\"\"The applied theme's colors, from the shelf that holds the widget.\"\"\"\n    while widget is not None:\n        fn = getattr(widget, \"theme_palette\", None)\n        if callable(fn):\n            try:\n                return fn()\n            except Exception:\n                return {}\n        widget = widget.parentWidget()\n    return {}\n\n\ndef row_height_of(widget):\n    while widget is not None:\n        fn = getattr(widget, \"row_height\", None)\n        if callable(fn):\n            try:\n                return int(fn())\n            except Exception:\n                break\n        widget = widget.parentWidget()\n    return 40\n\n\ndef paint_icon(painter, box, on, ink, faint):\n    \"\"\"NURBS curves, drawn in ``box`` (a QRectF). ``ink`` draws, ``faint`` is the dimmer line.\"\"\"\n    x, y, w, h = box.left(), box.top(), box.width(), box.height()\n    # An S-curve, the way a NURBS curve reads in a viewport: one smooth\n    # stroke, no hull and no CVs, with a dot at each end.\n    start, end = QtCore.QPointF(x, y + h * 0.85), QtCore.QPointF(x + w, y + h * 0.15)\n    path = QtGui.QPainterPath(start)\n    path.cubicTo(QtCore.QPointF(x + w * 0.9, y + h * 1.05), QtCore.QPointF(x + w * 0.1, y - h * 0.05), end)\n    painter.setPen(QtGui.QPen(ink, 2.2, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap))\n    painter.setBrush(Qt.BrushStyle.NoBrush)\n    painter.drawPath(path)\n    painter.setPen(Qt.PenStyle.NoPen)\n    painter.setBrush(ink)\n    for point in (start, end):\n        painter.drawEllipse(point, 2.2, 2.2)\n\n\nclass Toggle(QtWidgets.QToolButton):\n    \"\"\"A square button lit in the theme's accent while NURBS curves show.\"\"\"\n\n    def __init__(self, parent, size, palette):\n        super().__init__(parent)\n        self.setObjectName(\"tdShelfViewportToggle\")\n        self.setCheckable(True)\n        self.setFixedSize(size, size)\n        self.setCursor(Qt.CursorShape.PointingHandCursor)\n        self.setFocusPolicy(Qt.FocusPolicy.NoFocus)\n        self._on_fill = resolve_color(\"@colors.accent\", palette, \"#5285a6\")\n        self._off_fill = resolve_color(\"@colors.base\", palette, \"#2b2b2b\")\n        self._ink_on = resolve_color(\"@colors.text\", palette, \"#eeeeee\").lighter(115)\n        self._ink_off = resolve_color(\"@colors.text_disabled\", palette, \"#8a8a8a\")\n        self._border = resolve_color(\"@colors.border\", palette, \"#444444\")\n        self._hover = False\n\n    def enterEvent(self, event):\n        self._hover = True\n        self.update()\n        super().enterEvent(event)\n\n    def leaveEvent(self, event):\n        self._hover = False\n        self.update()\n        super().leaveEvent(event)\n\n    def paintEvent(self, event):\n        on = self.isChecked()\n        painter = QtGui.QPainter(self)\n        painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)\n        rect = QtCore.QRectF(self.rect()).adjusted(1.5, 1.5, -1.5, -1.5)\n        fill = QtGui.QColor(self._on_fill if on else self._off_fill)\n        if self._hover:\n            fill = fill.lighter(118)\n        if self.isDown():\n            fill = fill.darker(115)\n        painter.setPen(QtGui.QPen(self._on_fill.lighter(125) if on else self._border, 1))\n        painter.setBrush(fill)\n        painter.drawRoundedRect(rect, 5, 5)\n        # A lit button carries a light bar along its foot as well, so the\n        # state reads at a glance even where the accent is close to the base.\n        if on:\n            bar = QtCore.QRectF(rect.left() + 6, rect.bottom() - 4, rect.width() - 12, 2.5)\n            painter.setPen(Qt.PenStyle.NoPen)\n            painter.setBrush(self._ink_on)\n            painter.drawRoundedRect(bar, 1, 1)\n        ink = self._ink_on if on else self._ink_off\n        faint = QtGui.QColor(ink)\n        faint.setAlpha(150 if on else 110)\n        inset = max(6.0, rect.width() * 0.2)\n        box = rect.adjusted(inset, inset - 1, -inset, -inset - 2)\n        paint_icon(painter, box, on, ink, faint)\n        painter.end()\n\n\ndef build(parent, item):\n    palette = palette_of(parent)\n    button = Toggle(parent, row_height_of(parent), palette)\n\n    def refresh():\n        if not is_valid(button):                  # the shelf rebuilt; a late event found a dead widget\n            return\n        on = shown()\n        button.blockSignals(True)\n        button.setChecked(on)\n        button.blockSignals(False)\n        button.setToolTip(\"%s: %s\\nClick to %s them in every visible viewport.\"\n                          % (NAME, \"shown\" if on else \"hidden\", \"hide\" if on else \"show\"))\n        button.update()\n\n    def toggled(on):\n        show(on)\n        refresh()\n\n    button.toggled.connect(toggled)\n    # Follow Maya's own Show menu and the focus moving between viewports.\n    timer = QtCore.QTimer(button)\n    timer.setSingleShot(True)\n    timer.setInterval(0)\n    timer.timeout.connect(refresh)\n    handles = [om.MEventMessage.addEventCallback(event, lambda *_: timer.start())\n               for event in (\"modelEditorChanged\", \"ModelPanelSetFocus\")]\n    button.destroyed.connect(lambda *_: maya_utils.remove_callbacks(handles))\n    refresh()\n    return button\n",
          "language": "python",
          "extra": {
            "entry": "build"
          }
        },
        {
          "kind": "widget",
          "id": "item_e1d3fb10",
          "label": "NURBS Surfaces",
          "icon": "",
          "tooltip": "NURBS Surfaces: show or hide NURBS surfaces in every visible viewport (lit while shown)",
          "command": "\"\"\"NURBS Surfaces: show or hide NURBS surfaces in the viewports. Lit while shown.\n\nOne of the tool box's viewport toggles (shelf 09_viewport). The button reads\nthe focused viewport (else the first visible one), so it always says what you\nsee; a click sets every visible viewport, so they agree. It listens to Maya's\n``modelEditorChanged`` and ``ModelPanelSetFocus`` events, and takes those\ncallbacks off again with the widget (a shelf rebuilds its widgets often).\n\"\"\"\nfrom maya import cmds\nimport maya.api.OpenMaya as om\nfrom tdtoolkit import maya_utils\nfrom tdtoolkit.qt import Qt, QtCore, QtGui, QtWidgets, is_valid\nfrom tdtoolkit.widgets import resolve_color\n\nFLAGS = (\"nurbsSurfaces\",)                      # the modelEditor flag(s) this button drives\nNAME = \"NURBS Surfaces\"\n\n\ndef panels():\n    \"\"\"The visible model panels, the focused one first.\"\"\"\n    visible = [p for p in (cmds.getPanel(visiblePanels=True) or []) if cmds.getPanel(typeOf=p) == \"modelPanel\"]\n    focus = cmds.getPanel(withFocus=True)\n    if focus in visible:\n        visible.remove(focus)\n        visible.insert(0, focus)\n    return visible\n\n\ndef shown():\n    \"\"\"Whether the viewport you are looking at shows NURBS surfaces.\"\"\"\n    found = panels()\n    if not found:\n        return False\n    return any(cmds.modelEditor(found[0], query=True, **{flag: True}) for flag in FLAGS)\n\n\ndef show(on):\n    for panel in panels():\n        cmds.modelEditor(panel, edit=True, nurbsSurfaces=on)\n\n\ndef palette_of(widget):\n    \"\"\"The applied theme's colors, from the shelf that holds the widget.\"\"\"\n    while widget is not None:\n        fn = getattr(widget, \"theme_palette\", None)\n        if callable(fn):\n            try:\n                return fn()\n            except Exception:\n                return {}\n        widget = widget.parentWidget()\n    return {}\n\n\ndef row_height_of(widget):\n    while widget is not None:\n        fn = getattr(widget, \"row_height\", None)\n        if callable(fn):\n            try:\n                return int(fn())\n            except Exception:\n                break\n        widget = widget.parentWidget()\n    return 40\n\n\ndef paint_icon(painter, box, on, ink, faint):\n    \"\"\"NURBS surfaces, drawn in ``box`` (a QRectF). ``ink`` draws, ``faint`` is the dimmer line.\"\"\"\n    x, y, w, h = box.left(), box.top(), box.width(), box.height()\n    # A NURBS patch: a curved sheet seen in perspective, with its isoparms.\n    def arc(t):\n        # a cross-curve at 0..1 down the patch: bows down in the middle\n        left = QtCore.QPointF(x + w * (0.05 + 0.15 * t), y + h * (0.15 + 0.7 * t))\n        right = QtCore.QPointF(x + w * (0.8 + 0.15 * t), y + h * (0.05 + 0.7 * t))\n        control = QtCore.QPointF((left.x() + right.x()) / 2, (left.y() + right.y()) / 2 + h * 0.28)\n        return left, control, right\n    sheet = QtGui.QPainterPath()\n    l0, c0, r0 = arc(0.0)\n    l1, c1, r1 = arc(1.0)\n    sheet.moveTo(l0)\n    sheet.quadTo(c0, r0)\n    sheet.lineTo(r1)\n    sheet.quadTo(c1, l1)\n    sheet.closeSubpath()\n    painter.setPen(QtGui.QPen(ink, 1.6, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap, Qt.PenJoinStyle.RoundJoin))\n    painter.setBrush(faint)\n    painter.drawPath(sheet)\n    painter.setBrush(Qt.BrushStyle.NoBrush)\n    painter.setPen(QtGui.QPen(ink, 1.1))\n    for t in (0.33, 0.66):\n        left, control, right = arc(t)\n        iso = QtGui.QPainterPath(left)\n        iso.quadTo(control, right)\n        painter.drawPath(iso)\n\n\nclass Toggle(QtWidgets.QToolButton):\n    \"\"\"A square button lit in the theme's accent while NURBS surfaces show.\"\"\"\n\n    def __init__(self, parent, size, palette):\n        super().__init__(parent)\n        self.setObjectName(\"tdShelfViewportToggle\")\n        self.setCheckable(True)\n        self.setFixedSize(size, size)\n        self.setCursor(Qt.CursorShape.PointingHandCursor)\n        self.setFocusPolicy(Qt.FocusPolicy.NoFocus)\n        self._on_fill = resolve_color(\"@colors.accent\", palette, \"#5285a6\")\n        self._off_fill = resolve_color(\"@colors.base\", palette, \"#2b2b2b\")\n        self._ink_on = resolve_color(\"@colors.text\", palette, \"#eeeeee\").lighter(115)\n        self._ink_off = resolve_color(\"@colors.text_disabled\", palette, \"#8a8a8a\")\n        self._border = resolve_color(\"@colors.border\", palette, \"#444444\")\n        self._hover = False\n\n    def enterEvent(self, event):\n        self._hover = True\n        self.update()\n        super().enterEvent(event)\n\n    def leaveEvent(self, event):\n        self._hover = False\n        self.update()\n        super().leaveEvent(event)\n\n    def paintEvent(self, event):\n        on = self.isChecked()\n        painter = QtGui.QPainter(self)\n        painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)\n        rect = QtCore.QRectF(self.rect()).adjusted(1.5, 1.5, -1.5, -1.5)\n        fill = QtGui.QColor(self._on_fill if on else self._off_fill)\n        if self._hover:\n            fill = fill.lighter(118)\n        if self.isDown():\n            fill = fill.darker(115)\n        painter.setPen(QtGui.QPen(self._on_fill.lighter(125) if on else self._border, 1))\n        painter.setBrush(fill)\n        painter.drawRoundedRect(rect, 5, 5)\n        # A lit button carries a light bar along its foot as well, so the\n        # state reads at a glance even where the accent is close to the base.\n        if on:\n            bar = QtCore.QRectF(rect.left() + 6, rect.bottom() - 4, rect.width() - 12, 2.5)\n            painter.setPen(Qt.PenStyle.NoPen)\n            painter.setBrush(self._ink_on)\n            painter.drawRoundedRect(bar, 1, 1)\n        ink = self._ink_on if on else self._ink_off\n        faint = QtGui.QColor(ink)\n        faint.setAlpha(150 if on else 110)\n        inset = max(6.0, rect.width() * 0.2)\n        box = rect.adjusted(inset, inset - 1, -inset, -inset - 2)\n        paint_icon(painter, box, on, ink, faint)\n        painter.end()\n\n\ndef build(parent, item):\n    palette = palette_of(parent)\n    button = Toggle(parent, row_height_of(parent), palette)\n\n    def refresh():\n        if not is_valid(button):                  # the shelf rebuilt; a late event found a dead widget\n            return\n        on = shown()\n        button.blockSignals(True)\n        button.setChecked(on)\n        button.blockSignals(False)\n        button.setToolTip(\"%s: %s\\nClick to %s them in every visible viewport.\"\n                          % (NAME, \"shown\" if on else \"hidden\", \"hide\" if on else \"show\"))\n        button.update()\n\n    def toggled(on):\n        show(on)\n        refresh()\n\n    button.toggled.connect(toggled)\n    # Follow Maya's own Show menu and the focus moving between viewports.\n    timer = QtCore.QTimer(button)\n    timer.setSingleShot(True)\n    timer.setInterval(0)\n    timer.timeout.connect(refresh)\n    handles = [om.MEventMessage.addEventCallback(event, lambda *_: timer.start())\n               for event in (\"modelEditorChanged\", \"ModelPanelSetFocus\")]\n    button.destroyed.connect(lambda *_: maya_utils.remove_callbacks(handles))\n    refresh()\n    return button\n",
          "language": "python",
          "extra": {
            "entry": "build"
          }
        },
        {
          "kind": "widget",
          "id": "item_01d90890",
          "label": "Polygons",
          "icon": "",
          "tooltip": "Polygons: show or hide polygon meshes in every visible viewport (lit while shown)",
          "command": "\"\"\"Polygons: show or hide polygon meshes in the viewports. Lit while shown.\n\nOne of the tool box's viewport toggles (shelf 09_viewport). The button reads\nthe focused viewport (else the first visible one), so it always says what you\nsee; a click sets every visible viewport, so they agree. It listens to Maya's\n``modelEditorChanged`` and ``ModelPanelSetFocus`` events, and takes those\ncallbacks off again with the widget (a shelf rebuilds its widgets often).\n\"\"\"\nfrom maya import cmds\nimport maya.api.OpenMaya as om\nfrom tdtoolkit import maya_utils\nfrom tdtoolkit.qt import Qt, QtCore, QtGui, QtWidgets, is_valid\nfrom tdtoolkit.widgets import resolve_color\n\nFLAGS = (\"polymeshes\",)                      # the modelEditor flag(s) this button drives\nNAME = \"Polygons\"\n\n\ndef panels():\n    \"\"\"The visible model panels, the focused one first.\"\"\"\n    visible = [p for p in (cmds.getPanel(visiblePanels=True) or []) if cmds.getPanel(typeOf=p) == \"modelPanel\"]\n    focus = cmds.getPanel(withFocus=True)\n    if focus in visible:\n        visible.remove(focus)\n        visible.insert(0, focus)\n    return visible\n\n\ndef shown():\n    \"\"\"Whether the viewport you are looking at shows polygon meshes.\"\"\"\n    found = panels()\n    if not found:\n        return False\n    return any(cmds.modelEditor(found[0], query=True, **{flag: True}) for flag in FLAGS)\n\n\ndef show(on):\n    for panel in panels():\n        cmds.modelEditor(panel, edit=True, polymeshes=on)\n\n\ndef palette_of(widget):\n    \"\"\"The applied theme's colors, from the shelf that holds the widget.\"\"\"\n    while widget is not None:\n        fn = getattr(widget, \"theme_palette\", None)\n        if callable(fn):\n            try:\n                return fn()\n            except Exception:\n                return {}\n        widget = widget.parentWidget()\n    return {}\n\n\ndef row_height_of(widget):\n    while widget is not None:\n        fn = getattr(widget, \"row_height\", None)\n        if callable(fn):\n            try:\n                return int(fn())\n            except Exception:\n                break\n        widget = widget.parentWidget()\n    return 40\n\n\ndef paint_icon(painter, box, on, ink, faint):\n    \"\"\"polygon meshes, drawn in ``box`` (a QRectF). ``ink`` draws, ``faint`` is the dimmer line.\"\"\"\n    x, y, w, h = box.left(), box.top(), box.width(), box.height()\n    # An isometric cube: top face lit, two sides outlined.\n    top = QtGui.QPolygonF([QtCore.QPointF(x + w / 2, y), QtCore.QPointF(x + w, y + h * 0.25),\n                           QtCore.QPointF(x + w / 2, y + h * 0.5), QtCore.QPointF(x, y + h * 0.25)])\n    left = QtGui.QPolygonF([QtCore.QPointF(x, y + h * 0.25), QtCore.QPointF(x + w / 2, y + h * 0.5),\n                            QtCore.QPointF(x + w / 2, y + h), QtCore.QPointF(x, y + h * 0.75)])\n    right = QtGui.QPolygonF([QtCore.QPointF(x + w, y + h * 0.25), QtCore.QPointF(x + w / 2, y + h * 0.5),\n                             QtCore.QPointF(x + w / 2, y + h), QtCore.QPointF(x + w, y + h * 0.75)])\n    painter.setPen(QtGui.QPen(ink, 1.6, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap, Qt.PenJoinStyle.RoundJoin))\n    painter.setBrush(faint)\n    painter.drawPolygon(top)\n    painter.setBrush(Qt.BrushStyle.NoBrush)\n    painter.drawPolygon(left)\n    painter.drawPolygon(right)\n\n\nclass Toggle(QtWidgets.QToolButton):\n    \"\"\"A square button lit in the theme's accent while polygon meshes show.\"\"\"\n\n    def __init__(self, parent, size, palette):\n        super().__init__(parent)\n        self.setObjectName(\"tdShelfViewportToggle\")\n        self.setCheckable(True)\n        self.setFixedSize(size, size)\n        self.setCursor(Qt.CursorShape.PointingHandCursor)\n        self.setFocusPolicy(Qt.FocusPolicy.NoFocus)\n        self._on_fill = resolve_color(\"@colors.accent\", palette, \"#5285a6\")\n        self._off_fill = resolve_color(\"@colors.base\", palette, \"#2b2b2b\")\n        self._ink_on = resolve_color(\"@colors.text\", palette, \"#eeeeee\").lighter(115)\n        self._ink_off = resolve_color(\"@colors.text_disabled\", palette, \"#8a8a8a\")\n        self._border = resolve_color(\"@colors.border\", palette, \"#444444\")\n        self._hover = False\n\n    def enterEvent(self, event):\n        self._hover = True\n        self.update()\n        super().enterEvent(event)\n\n    def leaveEvent(self, event):\n        self._hover = False\n        self.update()\n        super().leaveEvent(event)\n\n    def paintEvent(self, event):\n        on = self.isChecked()\n        painter = QtGui.QPainter(self)\n        painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)\n        rect = QtCore.QRectF(self.rect()).adjusted(1.5, 1.5, -1.5, -1.5)\n        fill = QtGui.QColor(self._on_fill if on else self._off_fill)\n        if self._hover:\n            fill = fill.lighter(118)\n        if self.isDown():\n            fill = fill.darker(115)\n        painter.setPen(QtGui.QPen(self._on_fill.lighter(125) if on else self._border, 1))\n        painter.setBrush(fill)\n        painter.drawRoundedRect(rect, 5, 5)\n        # A lit button carries a light bar along its foot as well, so the\n        # state reads at a glance even where the accent is close to the base.\n        if on:\n            bar = QtCore.QRectF(rect.left() + 6, rect.bottom() - 4, rect.width() - 12, 2.5)\n            painter.setPen(Qt.PenStyle.NoPen)\n            painter.setBrush(self._ink_on)\n            painter.drawRoundedRect(bar, 1, 1)\n        ink = self._ink_on if on else self._ink_off\n        faint = QtGui.QColor(ink)\n        faint.setAlpha(150 if on else 110)\n        inset = max(6.0, rect.width() * 0.2)\n        box = rect.adjusted(inset, inset - 1, -inset, -inset - 2)\n        paint_icon(painter, box, on, ink, faint)\n        painter.end()\n\n\ndef build(parent, item):\n    palette = palette_of(parent)\n    button = Toggle(parent, row_height_of(parent), palette)\n\n    def refresh():\n        if not is_valid(button):                  # the shelf rebuilt; a late event found a dead widget\n            return\n        on = shown()\n        button.blockSignals(True)\n        button.setChecked(on)\n        button.blockSignals(False)\n        button.setToolTip(\"%s: %s\\nClick to %s them in every visible viewport.\"\n                          % (NAME, \"shown\" if on else \"hidden\", \"hide\" if on else \"show\"))\n        button.update()\n\n    def toggled(on):\n        show(on)\n        refresh()\n\n    button.toggled.connect(toggled)\n    # Follow Maya's own Show menu and the focus moving between viewports.\n    timer = QtCore.QTimer(button)\n    timer.setSingleShot(True)\n    timer.setInterval(0)\n    timer.timeout.connect(refresh)\n    handles = [om.MEventMessage.addEventCallback(event, lambda *_: timer.start())\n               for event in (\"modelEditorChanged\", \"ModelPanelSetFocus\")]\n    button.destroyed.connect(lambda *_: maya_utils.remove_callbacks(handles))\n    refresh()\n    return button\n",
          "language": "python",
          "extra": {
            "entry": "build"
          }
        },
        {
          "kind": "widget",
          "id": "item_db97c769",
          "label": "Joints",
          "icon": "",
          "tooltip": "Joints: show or hide joints in every visible viewport (lit while shown)",
          "command": "\"\"\"Joints: show or hide joints in the viewports. Lit while shown.\n\nOne of the tool box's viewport toggles (shelf 09_viewport). The button reads\nthe focused viewport (else the first visible one), so it always says what you\nsee; a click sets every visible viewport, so they agree. It listens to Maya's\n``modelEditorChanged`` and ``ModelPanelSetFocus`` events, and takes those\ncallbacks off again with the widget (a shelf rebuilds its widgets often).\n\"\"\"\nfrom maya import cmds\nimport maya.api.OpenMaya as om\nfrom tdtoolkit import maya_utils\nfrom tdtoolkit.qt import Qt, QtCore, QtGui, QtWidgets, is_valid\nfrom tdtoolkit.widgets import resolve_color\n\nFLAGS = (\"joints\",)                      # the modelEditor flag(s) this button drives\nNAME = \"Joints\"\n\n\ndef panels():\n    \"\"\"The visible model panels, the focused one first.\"\"\"\n    visible = [p for p in (cmds.getPanel(visiblePanels=True) or []) if cmds.getPanel(typeOf=p) == \"modelPanel\"]\n    focus = cmds.getPanel(withFocus=True)\n    if focus in visible:\n        visible.remove(focus)\n        visible.insert(0, focus)\n    return visible\n\n\ndef shown():\n    \"\"\"Whether the viewport you are looking at shows joints.\"\"\"\n    found = panels()\n    if not found:\n        return False\n    return any(cmds.modelEditor(found[0], query=True, **{flag: True}) for flag in FLAGS)\n\n\ndef show(on):\n    for panel in panels():\n        cmds.modelEditor(panel, edit=True, joints=on)\n\n\ndef palette_of(widget):\n    \"\"\"The applied theme's colors, from the shelf that holds the widget.\"\"\"\n    while widget is not None:\n        fn = getattr(widget, \"theme_palette\", None)\n        if callable(fn):\n            try:\n                return fn()\n            except Exception:\n                return {}\n        widget = widget.parentWidget()\n    return {}\n\n\ndef row_height_of(widget):\n    while widget is not None:\n        fn = getattr(widget, \"row_height\", None)\n        if callable(fn):\n            try:\n                return int(fn())\n            except Exception:\n                break\n        widget = widget.parentWidget()\n    return 40\n\n\ndef paint_icon(painter, box, on, ink, faint):\n    \"\"\"joints, drawn in ``box`` (a QRectF). ``ink`` draws, ``faint`` is the dimmer line.\"\"\"\n    x, y, w, h = box.left(), box.top(), box.width(), box.height()\n    # Two joints and the bone between them, as Maya draws a chain.\n    a = QtCore.QPointF(x + w * 0.22, y + h * 0.78)\n    b = QtCore.QPointF(x + w * 0.78, y + h * 0.22)\n    r = max(2.5, w * 0.14)\n    d = b - a\n    n = QtCore.QPointF(-d.y(), d.x())\n    length = (n.x() ** 2 + n.y() ** 2) ** 0.5 or 1.0\n    n = QtCore.QPointF(n.x() / length * r * 0.9, n.y() / length * r * 0.9)\n    knee = a + d * 0.25\n    bone = QtGui.QPolygonF([a, knee + n, b, knee - n])\n    painter.setPen(QtGui.QPen(ink, 1.4, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap, Qt.PenJoinStyle.RoundJoin))\n    painter.setBrush(faint)\n    painter.drawPolygon(bone)\n    painter.setBrush(Qt.BrushStyle.NoBrush)\n    painter.setPen(QtGui.QPen(ink, 1.6))\n    painter.drawEllipse(a, r, r)\n    painter.drawEllipse(b, r, r)\n\n\nclass Toggle(QtWidgets.QToolButton):\n    \"\"\"A square button lit in the theme's accent while joints show.\"\"\"\n\n    def __init__(self, parent, size, palette):\n        super().__init__(parent)\n        self.setObjectName(\"tdShelfViewportToggle\")\n        self.setCheckable(True)\n        self.setFixedSize(size, size)\n        self.setCursor(Qt.CursorShape.PointingHandCursor)\n        self.setFocusPolicy(Qt.FocusPolicy.NoFocus)\n        self._on_fill = resolve_color(\"@colors.accent\", palette, \"#5285a6\")\n        self._off_fill = resolve_color(\"@colors.base\", palette, \"#2b2b2b\")\n        self._ink_on = resolve_color(\"@colors.text\", palette, \"#eeeeee\").lighter(115)\n        self._ink_off = resolve_color(\"@colors.text_disabled\", palette, \"#8a8a8a\")\n        self._border = resolve_color(\"@colors.border\", palette, \"#444444\")\n        self._hover = False\n\n    def enterEvent(self, event):\n        self._hover = True\n        self.update()\n        super().enterEvent(event)\n\n    def leaveEvent(self, event):\n        self._hover = False\n        self.update()\n        super().leaveEvent(event)\n\n    def paintEvent(self, event):\n        on = self.isChecked()\n        painter = QtGui.QPainter(self)\n        painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)\n        rect = QtCore.QRectF(self.rect()).adjusted(1.5, 1.5, -1.5, -1.5)\n        fill = QtGui.QColor(self._on_fill if on else self._off_fill)\n        if self._hover:\n            fill = fill.lighter(118)\n        if self.isDown():\n            fill = fill.darker(115)\n        painter.setPen(QtGui.QPen(self._on_fill.lighter(125) if on else self._border, 1))\n        painter.setBrush(fill)\n        painter.drawRoundedRect(rect, 5, 5)\n        # A lit button carries a light bar along its foot as well, so the\n        # state reads at a glance even where the accent is close to the base.\n        if on:\n            bar = QtCore.QRectF(rect.left() + 6, rect.bottom() - 4, rect.width() - 12, 2.5)\n            painter.setPen(Qt.PenStyle.NoPen)\n            painter.setBrush(self._ink_on)\n            painter.drawRoundedRect(bar, 1, 1)\n        ink = self._ink_on if on else self._ink_off\n        faint = QtGui.QColor(ink)\n        faint.setAlpha(150 if on else 110)\n        inset = max(6.0, rect.width() * 0.2)\n        box = rect.adjusted(inset, inset - 1, -inset, -inset - 2)\n        paint_icon(painter, box, on, ink, faint)\n        painter.end()\n\n\ndef build(parent, item):\n    palette = palette_of(parent)\n    button = Toggle(parent, row_height_of(parent), palette)\n\n    def refresh():\n        if not is_valid(button):                  # the shelf rebuilt; a late event found a dead widget\n            return\n        on = shown()\n        button.blockSignals(True)\n        button.setChecked(on)\n        button.blockSignals(False)\n        button.setToolTip(\"%s: %s\\nClick to %s them in every visible viewport.\"\n                          % (NAME, \"shown\" if on else \"hidden\", \"hide\" if on else \"show\"))\n        button.update()\n\n    def toggled(on):\n        show(on)\n        refresh()\n\n    button.toggled.connect(toggled)\n    # Follow Maya's own Show menu and the focus moving between viewports.\n    timer = QtCore.QTimer(button)\n    timer.setSingleShot(True)\n    timer.setInterval(0)\n    timer.timeout.connect(refresh)\n    handles = [om.MEventMessage.addEventCallback(event, lambda *_: timer.start())\n               for event in (\"modelEditorChanged\", \"ModelPanelSetFocus\")]\n    button.destroyed.connect(lambda *_: maya_utils.remove_callbacks(handles))\n    refresh()\n    return button\n",
          "language": "python",
          "extra": {
            "entry": "build"
          }
        },
        {
          "kind": "widget",
          "id": "item_48e74a87",
          "label": "Locators",
          "icon": "",
          "tooltip": "Locators: show or hide locators in every visible viewport (lit while shown)",
          "command": "\"\"\"Locators: show or hide locators in the viewports. Lit while shown.\n\nOne of the tool box's viewport toggles (shelf 09_viewport). The button reads\nthe focused viewport (else the first visible one), so it always says what you\nsee; a click sets every visible viewport, so they agree. It listens to Maya's\n``modelEditorChanged`` and ``ModelPanelSetFocus`` events, and takes those\ncallbacks off again with the widget (a shelf rebuilds its widgets often).\n\"\"\"\nfrom maya import cmds\nimport maya.api.OpenMaya as om\nfrom tdtoolkit import maya_utils\nfrom tdtoolkit.qt import Qt, QtCore, QtGui, QtWidgets, is_valid\nfrom tdtoolkit.widgets import resolve_color\n\nFLAGS = (\"locators\",)                      # the modelEditor flag(s) this button drives\nNAME = \"Locators\"\n\n\ndef panels():\n    \"\"\"The visible model panels, the focused one first.\"\"\"\n    visible = [p for p in (cmds.getPanel(visiblePanels=True) or []) if cmds.getPanel(typeOf=p) == \"modelPanel\"]\n    focus = cmds.getPanel(withFocus=True)\n    if focus in visible:\n        visible.remove(focus)\n        visible.insert(0, focus)\n    return visible\n\n\ndef shown():\n    \"\"\"Whether the viewport you are looking at shows locators.\"\"\"\n    found = panels()\n    if not found:\n        return False\n    return any(cmds.modelEditor(found[0], query=True, **{flag: True}) for flag in FLAGS)\n\n\ndef show(on):\n    for panel in panels():\n        cmds.modelEditor(panel, edit=True, locators=on)\n\n\ndef palette_of(widget):\n    \"\"\"The applied theme's colors, from the shelf that holds the widget.\"\"\"\n    while widget is not None:\n        fn = getattr(widget, \"theme_palette\", None)\n        if callable(fn):\n            try:\n                return fn()\n            except Exception:\n                return {}\n        widget = widget.parentWidget()\n    return {}\n\n\ndef row_height_of(widget):\n    while widget is not None:\n        fn = getattr(widget, \"row_height\", None)\n        if callable(fn):\n            try:\n                return int(fn())\n            except Exception:\n                break\n        widget = widget.parentWidget()\n    return 40\n\n\ndef paint_icon(painter, box, on, ink, faint):\n    \"\"\"locators, drawn in ``box`` (a QRectF). ``ink`` draws, ``faint`` is the dimmer line.\"\"\"\n    x, y, w, h = box.left(), box.top(), box.width(), box.height()\n    c = box.center()\n    # Maya's locator: three axes crossing at a point, the third foreshortened.\n    painter.setPen(QtGui.QPen(ink, 2, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap))\n    painter.drawLine(QtCore.QPointF(x, c.y()), QtCore.QPointF(x + w, c.y()))\n    painter.drawLine(QtCore.QPointF(c.x(), y), QtCore.QPointF(c.x(), y + h))\n    painter.setPen(QtGui.QPen(faint, 2, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap))\n    painter.drawLine(QtCore.QPointF(c.x() - w * 0.28, c.y() + h * 0.28),\n                     QtCore.QPointF(c.x() + w * 0.28, c.y() - h * 0.28))\n    painter.setPen(Qt.PenStyle.NoPen)\n    painter.setBrush(ink)\n    painter.drawEllipse(c, 2.2, 2.2)\n\n\nclass Toggle(QtWidgets.QToolButton):\n    \"\"\"A square button lit in the theme's accent while locators show.\"\"\"\n\n    def __init__(self, parent, size, palette):\n        super().__init__(parent)\n        self.setObjectName(\"tdShelfViewportToggle\")\n        self.setCheckable(True)\n        self.setFixedSize(size, size)\n        self.setCursor(Qt.CursorShape.PointingHandCursor)\n        self.setFocusPolicy(Qt.FocusPolicy.NoFocus)\n        self._on_fill = resolve_color(\"@colors.accent\", palette, \"#5285a6\")\n        self._off_fill = resolve_color(\"@colors.base\", palette, \"#2b2b2b\")\n        self._ink_on = resolve_color(\"@colors.text\", palette, \"#eeeeee\").lighter(115)\n        self._ink_off = resolve_color(\"@colors.text_disabled\", palette, \"#8a8a8a\")\n        self._border = resolve_color(\"@colors.border\", palette, \"#444444\")\n        self._hover = False\n\n    def enterEvent(self, event):\n        self._hover = True\n        self.update()\n        super().enterEvent(event)\n\n    def leaveEvent(self, event):\n        self._hover = False\n        self.update()\n        super().leaveEvent(event)\n\n    def paintEvent(self, event):\n        on = self.isChecked()\n        painter = QtGui.QPainter(self)\n        painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)\n        rect = QtCore.QRectF(self.rect()).adjusted(1.5, 1.5, -1.5, -1.5)\n        fill = QtGui.QColor(self._on_fill if on else self._off_fill)\n        if self._hover:\n            fill = fill.lighter(118)\n        if self.isDown():\n            fill = fill.darker(115)\n        painter.setPen(QtGui.QPen(self._on_fill.lighter(125) if on else self._border, 1))\n        painter.setBrush(fill)\n        painter.drawRoundedRect(rect, 5, 5)\n        # A lit button carries a light bar along its foot as well, so the\n        # state reads at a glance even where the accent is close to the base.\n        if on:\n            bar = QtCore.QRectF(rect.left() + 6, rect.bottom() - 4, rect.width() - 12, 2.5)\n            painter.setPen(Qt.PenStyle.NoPen)\n            painter.setBrush(self._ink_on)\n            painter.drawRoundedRect(bar, 1, 1)\n        ink = self._ink_on if on else self._ink_off\n        faint = QtGui.QColor(ink)\n        faint.setAlpha(150 if on else 110)\n        inset = max(6.0, rect.width() * 0.2)\n        box = rect.adjusted(inset, inset - 1, -inset, -inset - 2)\n        paint_icon(painter, box, on, ink, faint)\n        painter.end()\n\n\ndef build(parent, item):\n    palette = palette_of(parent)\n    button = Toggle(parent, row_height_of(parent), palette)\n\n    def refresh():\n        if not is_valid(button):                  # the shelf rebuilt; a late event found a dead widget\n            return\n        on = shown()\n        button.blockSignals(True)\n        button.setChecked(on)\n        button.blockSignals(False)\n        button.setToolTip(\"%s: %s\\nClick to %s them in every visible viewport.\"\n                          % (NAME, \"shown\" if on else \"hidden\", \"hide\" if on else \"show\"))\n        button.update()\n\n    def toggled(on):\n        show(on)\n        refresh()\n\n    button.toggled.connect(toggled)\n    # Follow Maya's own Show menu and the focus moving between viewports.\n    timer = QtCore.QTimer(button)\n    timer.setSingleShot(True)\n    timer.setInterval(0)\n    timer.timeout.connect(refresh)\n    handles = [om.MEventMessage.addEventCallback(event, lambda *_: timer.start())\n               for event in (\"modelEditorChanged\", \"ModelPanelSetFocus\")]\n    button.destroyed.connect(lambda *_: maya_utils.remove_callbacks(handles))\n    refresh()\n    return button\n",
          "language": "python",
          "extra": {
            "entry": "build"
          }
        },
        {
          "kind": "widget",
          "id": "item_52cf9c62",
          "label": "All Displays",
          "icon": "",
          "tooltip": "All Displays: show or hide everything (every Show entry) in every visible viewport (lit while shown)",
          "command": "\"\"\"All Displays: show or hide everything (every Show entry) in the viewports. Lit while shown.\n\nOne of the tool box's viewport toggles (shelf 09_viewport). The button reads\nthe focused viewport (else the first visible one), so it always says what you\nsee; a click sets every visible viewport, so they agree. It listens to Maya's\n``modelEditorChanged`` and ``ModelPanelSetFocus`` events, and takes those\ncallbacks off again with the widget (a shelf rebuilds its widgets often).\n\"\"\"\nfrom maya import cmds\nimport maya.api.OpenMaya as om\nfrom tdtoolkit import maya_utils\nfrom tdtoolkit.qt import Qt, QtCore, QtGui, QtWidgets, is_valid\nfrom tdtoolkit.widgets import resolve_color\n\nFLAGS = (\"polymeshes\", \"nurbsSurfaces\", \"nurbsCurves\", \"joints\", \"locators\", \"lights\", \"cameras\")                      # the modelEditor flag(s) this button drives\nNAME = \"All Displays\"\n\n\ndef panels():\n    \"\"\"The visible model panels, the focused one first.\"\"\"\n    visible = [p for p in (cmds.getPanel(visiblePanels=True) or []) if cmds.getPanel(typeOf=p) == \"modelPanel\"]\n    focus = cmds.getPanel(withFocus=True)\n    if focus in visible:\n        visible.remove(focus)\n        visible.insert(0, focus)\n    return visible\n\n\ndef shown():\n    \"\"\"Whether the viewport you are looking at shows everything (every Show entry).\"\"\"\n    found = panels()\n    if not found:\n        return False\n    return any(cmds.modelEditor(found[0], query=True, **{flag: True}) for flag in FLAGS)\n\n\ndef show(on):\n    for panel in panels():\n        cmds.modelEditor(panel, edit=True, allObjects=on)\n\n\ndef palette_of(widget):\n    \"\"\"The applied theme's colors, from the shelf that holds the widget.\"\"\"\n    while widget is not None:\n        fn = getattr(widget, \"theme_palette\", None)\n        if callable(fn):\n            try:\n                return fn()\n            except Exception:\n                return {}\n        widget = widget.parentWidget()\n    return {}\n\n\ndef row_height_of(widget):\n    while widget is not None:\n        fn = getattr(widget, \"row_height\", None)\n        if callable(fn):\n            try:\n                return int(fn())\n            except Exception:\n                break\n        widget = widget.parentWidget()\n    return 40\n\n\ndef paint_icon(painter, box, on, ink, faint):\n    \"\"\"everything (every Show entry), drawn in ``box`` (a QRectF). ``ink`` draws, ``faint`` is the dimmer line.\"\"\"\n    x, y, w, h = box.left(), box.top(), box.width(), box.height()\n    c = box.center()\n    pen = QtGui.QPen(ink, 1.8, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap, Qt.PenJoinStyle.RoundJoin)\n    painter.setPen(pen)\n    painter.setBrush(Qt.BrushStyle.NoBrush)\n    if on:\n        # An open eye: two arcs, the iris between them.\n        lid = QtGui.QPainterPath(QtCore.QPointF(x, c.y()))\n        lid.quadTo(QtCore.QPointF(c.x(), y - h * 0.15), QtCore.QPointF(x + w, c.y()))\n        lid.quadTo(QtCore.QPointF(c.x(), y + h * 1.15), QtCore.QPointF(x, c.y()))\n        painter.drawPath(lid)\n        painter.setBrush(ink)\n        painter.drawEllipse(c, w * 0.16, w * 0.16)\n    else:\n        # A closed eye: the lower lid and three lashes.\n        lid = QtGui.QPainterPath(QtCore.QPointF(x, c.y() - h * 0.1))\n        lid.quadTo(QtCore.QPointF(c.x(), y + h * 0.9), QtCore.QPointF(x + w, c.y() - h * 0.1))\n        painter.drawPath(lid)\n        for fx, dx in ((0.25, -0.12), (0.5, 0.0), (0.75, 0.12)):\n            start = QtCore.QPointF(x + w * fx, c.y() + h * (0.28 if fx == 0.5 else 0.2))\n            painter.drawLine(start, QtCore.QPointF(start.x() + w * dx, start.y() + h * 0.28))\n\n\nclass Toggle(QtWidgets.QToolButton):\n    \"\"\"A square button lit in the theme's accent while everything (every Show entry) show.\"\"\"\n\n    def __init__(self, parent, size, palette):\n        super().__init__(parent)\n        self.setObjectName(\"tdShelfViewportToggle\")\n        self.setCheckable(True)\n        self.setFixedSize(size, size)\n        self.setCursor(Qt.CursorShape.PointingHandCursor)\n        self.setFocusPolicy(Qt.FocusPolicy.NoFocus)\n        self._on_fill = resolve_color(\"@colors.accent\", palette, \"#5285a6\")\n        self._off_fill = resolve_color(\"@colors.base\", palette, \"#2b2b2b\")\n        self._ink_on = resolve_color(\"@colors.text\", palette, \"#eeeeee\").lighter(115)\n        self._ink_off = resolve_color(\"@colors.text_disabled\", palette, \"#8a8a8a\")\n        self._border = resolve_color(\"@colors.border\", palette, \"#444444\")\n        self._hover = False\n\n    def enterEvent(self, event):\n        self._hover = True\n        self.update()\n        super().enterEvent(event)\n\n    def leaveEvent(self, event):\n        self._hover = False\n        self.update()\n        super().leaveEvent(event)\n\n    def paintEvent(self, event):\n        on = self.isChecked()\n        painter = QtGui.QPainter(self)\n        painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)\n        rect = QtCore.QRectF(self.rect()).adjusted(1.5, 1.5, -1.5, -1.5)\n        fill = QtGui.QColor(self._on_fill if on else self._off_fill)\n        if self._hover:\n            fill = fill.lighter(118)\n        if self.isDown():\n            fill = fill.darker(115)\n        painter.setPen(QtGui.QPen(self._on_fill.lighter(125) if on else self._border, 1))\n        painter.setBrush(fill)\n        painter.drawRoundedRect(rect, 5, 5)\n        # A lit button carries a light bar along its foot as well, so the\n        # state reads at a glance even where the accent is close to the base.\n        if on:\n            bar = QtCore.QRectF(rect.left() + 6, rect.bottom() - 4, rect.width() - 12, 2.5)\n            painter.setPen(Qt.PenStyle.NoPen)\n            painter.setBrush(self._ink_on)\n            painter.drawRoundedRect(bar, 1, 1)\n        ink = self._ink_on if on else self._ink_off\n        faint = QtGui.QColor(ink)\n        faint.setAlpha(150 if on else 110)\n        inset = max(6.0, rect.width() * 0.2)\n        box = rect.adjusted(inset, inset - 1, -inset, -inset - 2)\n        paint_icon(painter, box, on, ink, faint)\n        painter.end()\n\n\ndef build(parent, item):\n    palette = palette_of(parent)\n    button = Toggle(parent, row_height_of(parent), palette)\n\n    def refresh():\n        if not is_valid(button):                  # the shelf rebuilt; a late event found a dead widget\n            return\n        on = shown()\n        button.blockSignals(True)\n        button.setChecked(on)\n        button.blockSignals(False)\n        button.setToolTip(\"%s: %s\\nClick to %s them in every visible viewport.\"\n                          % (NAME, \"shown\" if on else \"hidden\", \"hide\" if on else \"show\"))\n        button.update()\n\n    def toggled(on):\n        show(on)\n        refresh()\n\n    button.toggled.connect(toggled)\n    # Follow Maya's own Show menu and the focus moving between viewports.\n    timer = QtCore.QTimer(button)\n    timer.setSingleShot(True)\n    timer.setInterval(0)\n    timer.timeout.connect(refresh)\n    handles = [om.MEventMessage.addEventCallback(event, lambda *_: timer.start())\n               for event in (\"modelEditorChanged\", \"ModelPanelSetFocus\")]\n    button.destroyed.connect(lambda *_: maya_utils.remove_callbacks(handles))\n    refresh()\n    return button\n",
          "language": "python",
          "extra": {
            "entry": "build"
          }
        }
      ]
    }
  ]
}
