nodebpy 520.14.0__tar.gz → 520.15.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nodebpy-520.14.0 → nodebpy-520.15.0}/PKG-INFO +1 -1
- {nodebpy-520.14.0 → nodebpy-520.15.0}/pyproject.toml +1 -1
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/builder/__init__.py +2 -1
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/builder/items.py +25 -2
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/export/codegen.py +9 -1
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/manual.py +8 -8
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/manual.py +113 -48
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/manual.py +10 -10
- {nodebpy-520.14.0 → nodebpy-520.15.0}/README.md +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/__init__.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/assets/__init__.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/assets/__main__.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/assets/_codegen.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/builder/_registry.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/builder/_utils.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/builder/accessor.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/builder/arrange.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/builder/asset.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/builder/mixins.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/builder/node.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/builder/socket.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/builder/tree.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/export/__init__.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/export/diagram.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/export/web_render.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/__init__.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/__init__.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/graph.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/ordering.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/ranking.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/realize.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/stacking.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/structs.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/sugiyama.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/x_coords.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/y_coords.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/config.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/utils.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/__init__.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/_mixins.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/__init__.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/assets.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/color.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/converter.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/distort.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/filter.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/group.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/input.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/interface.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/matte.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/output.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/vector.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/__init__.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/assets.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/attribute.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/color.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/converter.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/geometry.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/grid.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/group.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/groups.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/input.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/interface.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/output.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/texture.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/utilities.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/vector.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/zone.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/__init__.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/assets.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/color.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/converter.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/grid.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/group.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/input.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/output.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/script.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/shader.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/texture.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/vector.py +0 -0
- {nodebpy-520.14.0 → nodebpy-520.15.0}/src/nodebpy/types.py +0 -0
|
@@ -9,7 +9,7 @@ from .asset import (
|
|
|
9
9
|
PackageLibrary,
|
|
10
10
|
asset_group_base,
|
|
11
11
|
)
|
|
12
|
-
from .items import Item, ItemsMixin
|
|
12
|
+
from .items import Item, ItemsMixin, MenuItem
|
|
13
13
|
from .mixins import LinkingMixin, OperatorMixin
|
|
14
14
|
from .node import (
|
|
15
15
|
BaseNode,
|
|
@@ -118,6 +118,7 @@ __all__ = [
|
|
|
118
118
|
"MaterialSocketList",
|
|
119
119
|
"MatrixSocket",
|
|
120
120
|
"MatrixSocketList",
|
|
121
|
+
"MenuItem",
|
|
121
122
|
"MenuSocket",
|
|
122
123
|
"MenuSocketList",
|
|
123
124
|
"NodeGroupBuilder",
|
|
@@ -10,7 +10,7 @@ from ..types import _is_default_value
|
|
|
10
10
|
from ._registry import _wrap_socket
|
|
11
11
|
from ._utils import _SocketLike
|
|
12
12
|
from .node import DynamicInputsMixin
|
|
13
|
-
from .socket import Socket
|
|
13
|
+
from .socket import BaseSocket, Socket
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
16
|
from ..types import (
|
|
@@ -96,7 +96,7 @@ def _infer_value_type(value: Any) -> str | None:
|
|
|
96
96
|
return None
|
|
97
97
|
|
|
98
98
|
|
|
99
|
-
_SocketT = TypeVar("_SocketT", bound=
|
|
99
|
+
_SocketT = TypeVar("_SocketT", bound=BaseSocket, default=Socket)
|
|
100
100
|
|
|
101
101
|
|
|
102
102
|
class Item(Generic[_SocketT]):
|
|
@@ -152,6 +152,29 @@ class Item(Generic[_SocketT]):
|
|
|
152
152
|
)
|
|
153
153
|
|
|
154
154
|
|
|
155
|
+
class MenuItem(Item[_SocketT]):
|
|
156
|
+
"""Handle for one Menu Switch enum item.
|
|
157
|
+
|
|
158
|
+
``input`` is the item's input socket (typed to the switch's data
|
|
159
|
+
type), ``is_selected`` the item's boolean output socket, and
|
|
160
|
+
``description`` the tooltip Blender shows for the item in the menu.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def is_selected(self) -> BooleanSocket:
|
|
165
|
+
"""Boolean output socket — True when this item is selected."""
|
|
166
|
+
return cast("BooleanSocket", self.output)
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def description(self) -> str:
|
|
170
|
+
"""Tooltip shown for this item in the drop-down menu."""
|
|
171
|
+
return self._item.description
|
|
172
|
+
|
|
173
|
+
@description.setter
|
|
174
|
+
def description(self, value: str) -> None:
|
|
175
|
+
self._item.description = value
|
|
176
|
+
|
|
177
|
+
|
|
155
178
|
class ItemsMixin(DynamicInputsMixin):
|
|
156
179
|
"""Socket machinery for nodes whose sockets are driven by a bpy item
|
|
157
180
|
collection (``capture_items``, ``bake_items``, ``format_items``, ...).
|
|
@@ -3904,6 +3904,7 @@ def _emit_menu_switch(node, ctx: EmitContext) -> Expr | _Val | None:
|
|
|
3904
3904
|
"""MenuSwitch emits the factory dict form
|
|
3905
3905
|
``g.MenuSwitch.geometry(menu, {"Name": value, ...})`` — the plain
|
|
3906
3906
|
constructor's per-socket kwargs cannot recreate the enum item names.
|
|
3907
|
+
An item with a tooltip emits the ``(value, description)`` pair form.
|
|
3907
3908
|
Each tree type has its own MenuSwitch class (with tree-specific factories
|
|
3908
3909
|
such as ``shader``), so the alias follows the tree being exported."""
|
|
3909
3910
|
factory = _SWITCH_FACTORY_NAMES.get(node.data_type)
|
|
@@ -3911,7 +3912,14 @@ def _emit_menu_switch(node, ctx: EmitContext) -> Expr | _Val | None:
|
|
|
3911
3912
|
return None
|
|
3912
3913
|
alias = _TREE_ALIAS.get(ctx.node_tree.bl_idname, "g")
|
|
3913
3914
|
ctx.used_aliases.add(alias)
|
|
3914
|
-
|
|
3915
|
+
item_exprs: dict[str, Expr] = {}
|
|
3916
|
+
for (name, expr), item in zip(
|
|
3917
|
+
_switch_item_exprs(node, ctx, "Menu"), node.enum_items
|
|
3918
|
+
):
|
|
3919
|
+
if item.description:
|
|
3920
|
+
expr = TupleExpr([expr, Lit(item.description)])
|
|
3921
|
+
item_exprs[name] = expr
|
|
3922
|
+
items = DictExpr(item_exprs)
|
|
3915
3923
|
menu_link = ctx.input_link(node, "Menu")
|
|
3916
3924
|
if menu_link is not None:
|
|
3917
3925
|
return Call(
|
|
@@ -30,7 +30,7 @@ from ...types import (
|
|
|
30
30
|
InputString,
|
|
31
31
|
InputVector,
|
|
32
32
|
)
|
|
33
|
-
from ..geometry.manual import Float, Frame, _MenuSwitchBase
|
|
33
|
+
from ..geometry.manual import Float, Frame, MenuItems, _MenuSwitchBase
|
|
34
34
|
|
|
35
35
|
__all__ = ["Float", "Frame", "MenuSwitch", "tree"]
|
|
36
36
|
|
|
@@ -52,43 +52,43 @@ class MenuSwitch[T: BaseSocket](_MenuSwitchBase[T]):
|
|
|
52
52
|
|
|
53
53
|
@classmethod
|
|
54
54
|
def float(
|
|
55
|
-
cls, menu: InputMenu = None, items:
|
|
55
|
+
cls, menu: InputMenu = None, items: MenuItems[InputFloat] | None = None
|
|
56
56
|
) -> "MenuSwitch[FloatSocket]":
|
|
57
57
|
return MenuSwitch(menu, items, data_type="FLOAT")
|
|
58
58
|
|
|
59
59
|
@classmethod
|
|
60
60
|
def integer(
|
|
61
|
-
cls, menu: InputMenu = None, items:
|
|
61
|
+
cls, menu: InputMenu = None, items: MenuItems[InputInteger] | None = None
|
|
62
62
|
) -> "MenuSwitch[IntegerSocket]":
|
|
63
63
|
return MenuSwitch(menu, items, data_type="INT")
|
|
64
64
|
|
|
65
65
|
@classmethod
|
|
66
66
|
def boolean(
|
|
67
|
-
cls, menu: InputMenu = None, items:
|
|
67
|
+
cls, menu: InputMenu = None, items: MenuItems[InputBoolean] | None = None
|
|
68
68
|
) -> "MenuSwitch[BooleanSocket]":
|
|
69
69
|
return MenuSwitch(menu, items, data_type="BOOLEAN")
|
|
70
70
|
|
|
71
71
|
@classmethod
|
|
72
72
|
def vector(
|
|
73
|
-
cls, menu: InputMenu = None, items:
|
|
73
|
+
cls, menu: InputMenu = None, items: MenuItems[InputVector] | None = None
|
|
74
74
|
) -> "MenuSwitch[VectorSocket]":
|
|
75
75
|
return MenuSwitch(menu, items, data_type="VECTOR")
|
|
76
76
|
|
|
77
77
|
@classmethod
|
|
78
78
|
def color(
|
|
79
|
-
cls, menu: InputMenu = None, items:
|
|
79
|
+
cls, menu: InputMenu = None, items: MenuItems[InputColor] | None = None
|
|
80
80
|
) -> "MenuSwitch[ColorSocket]":
|
|
81
81
|
return MenuSwitch(menu, items, data_type="RGBA")
|
|
82
82
|
|
|
83
83
|
@classmethod
|
|
84
84
|
def string(
|
|
85
|
-
cls, menu: InputMenu = None, items:
|
|
85
|
+
cls, menu: InputMenu = None, items: MenuItems[InputString] | None = None
|
|
86
86
|
) -> "MenuSwitch[StringSocket]":
|
|
87
87
|
return MenuSwitch(menu, items, data_type="STRING")
|
|
88
88
|
|
|
89
89
|
@classmethod
|
|
90
90
|
def menu(
|
|
91
|
-
cls, menu: InputMenu = None, items:
|
|
91
|
+
cls, menu: InputMenu = None, items: MenuItems[InputMenu] | None = None
|
|
92
92
|
) -> "MenuSwitch[MenuSocket]":
|
|
93
93
|
return MenuSwitch(menu, items, data_type="MENU")
|
|
94
94
|
|
|
@@ -51,6 +51,7 @@ from ...builder import Socket as SocketLinker
|
|
|
51
51
|
from ...builder._registry import _wrap_socket
|
|
52
52
|
from ...builder.items import (
|
|
53
53
|
GridItem,
|
|
54
|
+
MenuItem,
|
|
54
55
|
_apply_item_value,
|
|
55
56
|
_FieldItemFactory,
|
|
56
57
|
_socket_for_item,
|
|
@@ -128,6 +129,9 @@ __all__ = (
|
|
|
128
129
|
"IndexSwitch",
|
|
129
130
|
"JoinGeometry",
|
|
130
131
|
"JoinStrings",
|
|
132
|
+
# "MenuItems" is deliberately not exported: quartodoc cannot document a
|
|
133
|
+
# PEP 695 `type` alias (griffe Kind.TYPE_ALIAS); import it from this
|
|
134
|
+
# module directly for annotations.
|
|
131
135
|
"MenuSwitch",
|
|
132
136
|
"MeshBoolean",
|
|
133
137
|
"RepeatInput",
|
|
@@ -1501,6 +1505,22 @@ class IndexSwitch[T: BaseSocket](ItemsMixin, BaseNode):
|
|
|
1501
1505
|
self.node.data_type = value
|
|
1502
1506
|
|
|
1503
1507
|
|
|
1508
|
+
type MenuItems[V] = Mapping[str, None | V | tuple[V | None, str]]
|
|
1509
|
+
"""Menu Switch ``items`` mapping: item name → value, where the value may be
|
|
1510
|
+
a ``(value, description)`` pair to also set the item's tooltip."""
|
|
1511
|
+
|
|
1512
|
+
|
|
1513
|
+
def _split_menu_item(
|
|
1514
|
+
value: InputAny | tuple[InputAny, str],
|
|
1515
|
+
) -> tuple[InputAny, str | None]:
|
|
1516
|
+
"""Split a ``(value, description)`` menu item pair. A 2-tuple whose
|
|
1517
|
+
second element is a string can never be a socket default value, so the
|
|
1518
|
+
form is unambiguous."""
|
|
1519
|
+
if isinstance(value, tuple) and len(value) == 2 and isinstance(value[1], str):
|
|
1520
|
+
return value[0], value[1]
|
|
1521
|
+
return cast("InputAny", value), None
|
|
1522
|
+
|
|
1523
|
+
|
|
1504
1524
|
class _MenuSwitchBase[T: BaseSocket](ItemsMixin, BaseNode):
|
|
1505
1525
|
"""Base class for MenuSwitch nodes across all tree types."""
|
|
1506
1526
|
|
|
@@ -1525,30 +1545,42 @@ class _MenuSwitchBase[T: BaseSocket](ItemsMixin, BaseNode):
|
|
|
1525
1545
|
def __init__(
|
|
1526
1546
|
self,
|
|
1527
1547
|
menu: InputMenu = None,
|
|
1528
|
-
items:
|
|
1548
|
+
items: MenuItems[InputAny] | None = None,
|
|
1529
1549
|
*,
|
|
1530
1550
|
data_type: SOCKET_TYPES = "FLOAT",
|
|
1531
1551
|
):
|
|
1532
1552
|
super().__init__()
|
|
1533
1553
|
self.data_type = data_type
|
|
1534
1554
|
self.node.enum_items.clear()
|
|
1535
|
-
|
|
1555
|
+
# a plain string `menu` is an explicit selection; otherwise the
|
|
1556
|
+
# selection defaults to the first item
|
|
1557
|
+
self._explicit_selection = isinstance(menu, str)
|
|
1536
1558
|
self._link_args(**(items or {}))
|
|
1537
|
-
self.
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
if self.node.enum_items and not isinstance(menu, str):
|
|
1559
|
+
if isinstance(menu, str) and not self.node.enum_items:
|
|
1560
|
+
# a selection named before any items exist (they will be added via
|
|
1561
|
+
# item()) can't be set yet; defer it to context exit
|
|
1542
1562
|
assert self.node.inputs is not None
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1563
|
+
self.tree._menu_defaults.append(
|
|
1564
|
+
_MenuDefault(self.node.inputs["Menu"], menu)
|
|
1565
|
+
)
|
|
1566
|
+
else:
|
|
1567
|
+
self._establish_links(Menu=menu)
|
|
1568
|
+
|
|
1569
|
+
if self.node.enum_items and not self._explicit_selection:
|
|
1570
|
+
self._default_selection_to_first()
|
|
1571
|
+
|
|
1572
|
+
def _default_selection_to_first(self) -> None:
|
|
1573
|
+
"""Default the menu selection to the first enum item."""
|
|
1574
|
+
assert self.node.inputs is not None
|
|
1575
|
+
try:
|
|
1576
|
+
menu_socket = self.node.inputs["Menu"]
|
|
1577
|
+
menu_socket.default_value = self.node.enum_items[0].name
|
|
1578
|
+
except TypeError: # pragma: no cover - rare Blender enum-refresh quirk
|
|
1579
|
+
# the socket is a NodeSocketMenu whose enum hasn't refreshed yet, so
|
|
1580
|
+
# the default_value isn't settable here; defer it to context exit.
|
|
1581
|
+
self.tree._menu_defaults.append(
|
|
1582
|
+
_MenuDefault(self.node.inputs["Menu"], self.node.enum_items[0].name)
|
|
1583
|
+
)
|
|
1552
1584
|
|
|
1553
1585
|
@property
|
|
1554
1586
|
def _socket_data_types(self) -> tuple[str, ...]:
|
|
@@ -1560,24 +1592,57 @@ class _MenuSwitchBase[T: BaseSocket](ItemsMixin, BaseNode):
|
|
|
1560
1592
|
# menu items are untyped; .new() takes only a name
|
|
1561
1593
|
return self._items.new(name)
|
|
1562
1594
|
|
|
1563
|
-
def _link_args(self, **kwargs: InputAny):
|
|
1595
|
+
def _link_args(self, **kwargs: InputAny | tuple[InputAny, str]):
|
|
1564
1596
|
for key, value in kwargs.items():
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1597
|
+
value, description = _split_menu_item(value)
|
|
1598
|
+
item = self._new_item(key, self.data_type)
|
|
1599
|
+
if description:
|
|
1600
|
+
item.description = description
|
|
1601
|
+
self._apply_item_input(self._item_socket(item), value)
|
|
1602
|
+
|
|
1603
|
+
def _apply_item_input(self, socket: NodeSocket, value: InputAny) -> None:
|
|
1604
|
+
if value is None:
|
|
1605
|
+
return # item declared but left unlinked
|
|
1606
|
+
if _is_default_value(value):
|
|
1607
|
+
if isinstance(socket, bpy.types.NodeSocketMenu) and isinstance(value, str):
|
|
1608
|
+
# the socket is a NodeSocketMenu, but the default_value is not settable
|
|
1609
|
+
# until the full tree is built and menu items are known. We need to defer
|
|
1610
|
+
# the setting of the default values until after tree construction.
|
|
1611
|
+
self.tree._menu_defaults.append(_MenuDefault(socket, value))
|
|
1578
1612
|
else:
|
|
1579
|
-
|
|
1580
|
-
|
|
1613
|
+
socket.default_value = value # ty: ignore[unresolved-attribute]
|
|
1614
|
+
else:
|
|
1615
|
+
source = self._source_socket(value) # type: ignore
|
|
1616
|
+
self._link(source, socket)
|
|
1617
|
+
|
|
1618
|
+
def item(
|
|
1619
|
+
self,
|
|
1620
|
+
name: str,
|
|
1621
|
+
value: InputAny | tuple[InputAny, str] = None,
|
|
1622
|
+
*,
|
|
1623
|
+
description: str | None = None,
|
|
1624
|
+
) -> MenuItem[T]:
|
|
1625
|
+
"""Declare a menu item and return its handle.
|
|
1626
|
+
|
|
1627
|
+
``value`` may be a linkable (linked into the item's input socket), a
|
|
1628
|
+
plain default value, or a ``(value, description)`` pair; omit it to
|
|
1629
|
+
declare the item unlinked. ``description`` sets the tooltip Blender
|
|
1630
|
+
shows for the item in the menu.
|
|
1631
|
+
|
|
1632
|
+
Unless the menu selection was set explicitly, declaring the first
|
|
1633
|
+
item also defaults the selection to it (as the constructor does).
|
|
1634
|
+
"""
|
|
1635
|
+
value, pair_description = _split_menu_item(value)
|
|
1636
|
+
if description is None:
|
|
1637
|
+
description = pair_description
|
|
1638
|
+
bpy_item = self._new_item(name, self.data_type)
|
|
1639
|
+
if description:
|
|
1640
|
+
bpy_item.description = description
|
|
1641
|
+
handle = cast("MenuItem[T]", MenuItem(self, bpy_item))
|
|
1642
|
+
self._apply_item_input(self._item_socket(bpy_item), value)
|
|
1643
|
+
if len(self.node.enum_items) == 1 and not self._explicit_selection:
|
|
1644
|
+
self._default_selection_to_first()
|
|
1645
|
+
return handle
|
|
1581
1646
|
|
|
1582
1647
|
def is_selected(self, name: str) -> BooleanSocket:
|
|
1583
1648
|
"""Gets the boolean output socket that is True when the named menu item is selected.
|
|
@@ -1616,7 +1681,7 @@ class MenuSwitch[T: BaseSocket](_MenuSwitchBase[T]):
|
|
|
1616
1681
|
def float(
|
|
1617
1682
|
cls,
|
|
1618
1683
|
menu: InputMenu = None,
|
|
1619
|
-
items:
|
|
1684
|
+
items: MenuItems[InputFloat] | None = None,
|
|
1620
1685
|
) -> "MenuSwitch[FloatSocket]":
|
|
1621
1686
|
return MenuSwitch(menu, items, data_type="FLOAT")
|
|
1622
1687
|
|
|
@@ -1624,7 +1689,7 @@ class MenuSwitch[T: BaseSocket](_MenuSwitchBase[T]):
|
|
|
1624
1689
|
def integer(
|
|
1625
1690
|
cls,
|
|
1626
1691
|
menu: InputMenu = None,
|
|
1627
|
-
items:
|
|
1692
|
+
items: MenuItems[InputInteger] | None = None,
|
|
1628
1693
|
) -> "MenuSwitch[IntegerSocket]":
|
|
1629
1694
|
return MenuSwitch(menu, items, data_type="INT")
|
|
1630
1695
|
|
|
@@ -1632,7 +1697,7 @@ class MenuSwitch[T: BaseSocket](_MenuSwitchBase[T]):
|
|
|
1632
1697
|
def boolean(
|
|
1633
1698
|
cls,
|
|
1634
1699
|
menu: InputMenu = None,
|
|
1635
|
-
items:
|
|
1700
|
+
items: MenuItems[InputBoolean] | None = None,
|
|
1636
1701
|
) -> "MenuSwitch[BooleanSocket]":
|
|
1637
1702
|
return MenuSwitch(menu, items, data_type="BOOLEAN")
|
|
1638
1703
|
|
|
@@ -1640,7 +1705,7 @@ class MenuSwitch[T: BaseSocket](_MenuSwitchBase[T]):
|
|
|
1640
1705
|
def vector(
|
|
1641
1706
|
cls,
|
|
1642
1707
|
menu: InputMenu = None,
|
|
1643
|
-
items:
|
|
1708
|
+
items: MenuItems[InputVector] | None = None,
|
|
1644
1709
|
) -> "MenuSwitch[VectorSocket]":
|
|
1645
1710
|
return MenuSwitch(menu, items, data_type="VECTOR")
|
|
1646
1711
|
|
|
@@ -1648,7 +1713,7 @@ class MenuSwitch[T: BaseSocket](_MenuSwitchBase[T]):
|
|
|
1648
1713
|
def color(
|
|
1649
1714
|
cls,
|
|
1650
1715
|
menu: InputMenu = None,
|
|
1651
|
-
items:
|
|
1716
|
+
items: MenuItems[InputColor] | None = None,
|
|
1652
1717
|
) -> "MenuSwitch[ColorSocket]":
|
|
1653
1718
|
return MenuSwitch(menu, items, data_type="RGBA")
|
|
1654
1719
|
|
|
@@ -1656,7 +1721,7 @@ class MenuSwitch[T: BaseSocket](_MenuSwitchBase[T]):
|
|
|
1656
1721
|
def rotation(
|
|
1657
1722
|
cls,
|
|
1658
1723
|
menu: InputMenu = None,
|
|
1659
|
-
items:
|
|
1724
|
+
items: MenuItems[InputRotation] | None = None,
|
|
1660
1725
|
) -> "MenuSwitch[RotationSocket]":
|
|
1661
1726
|
return MenuSwitch(menu, items, data_type="ROTATION")
|
|
1662
1727
|
|
|
@@ -1664,7 +1729,7 @@ class MenuSwitch[T: BaseSocket](_MenuSwitchBase[T]):
|
|
|
1664
1729
|
def matrix(
|
|
1665
1730
|
cls,
|
|
1666
1731
|
menu: InputMenu = None,
|
|
1667
|
-
items:
|
|
1732
|
+
items: MenuItems[InputMatrix] | None = None,
|
|
1668
1733
|
) -> "MenuSwitch[MatrixSocket]":
|
|
1669
1734
|
return MenuSwitch(menu, items, data_type="MATRIX")
|
|
1670
1735
|
|
|
@@ -1672,7 +1737,7 @@ class MenuSwitch[T: BaseSocket](_MenuSwitchBase[T]):
|
|
|
1672
1737
|
def string(
|
|
1673
1738
|
cls,
|
|
1674
1739
|
menu: InputMenu = None,
|
|
1675
|
-
items:
|
|
1740
|
+
items: MenuItems[InputString] | None = None,
|
|
1676
1741
|
) -> "MenuSwitch[StringSocket]":
|
|
1677
1742
|
return MenuSwitch(menu, items, data_type="STRING")
|
|
1678
1743
|
|
|
@@ -1680,7 +1745,7 @@ class MenuSwitch[T: BaseSocket](_MenuSwitchBase[T]):
|
|
|
1680
1745
|
def menu(
|
|
1681
1746
|
cls,
|
|
1682
1747
|
menu: InputMenu = None,
|
|
1683
|
-
items:
|
|
1748
|
+
items: MenuItems[InputMenu] | None = None,
|
|
1684
1749
|
) -> "MenuSwitch[MenuSocket]":
|
|
1685
1750
|
return MenuSwitch(menu, items, data_type="MENU")
|
|
1686
1751
|
|
|
@@ -1688,7 +1753,7 @@ class MenuSwitch[T: BaseSocket](_MenuSwitchBase[T]):
|
|
|
1688
1753
|
def object(
|
|
1689
1754
|
cls,
|
|
1690
1755
|
menu: InputMenu = None,
|
|
1691
|
-
items:
|
|
1756
|
+
items: MenuItems[InputObject] | None = None,
|
|
1692
1757
|
) -> "MenuSwitch[ObjectSocket]":
|
|
1693
1758
|
return MenuSwitch(menu, items, data_type="OBJECT")
|
|
1694
1759
|
|
|
@@ -1696,7 +1761,7 @@ class MenuSwitch[T: BaseSocket](_MenuSwitchBase[T]):
|
|
|
1696
1761
|
def geometry(
|
|
1697
1762
|
cls,
|
|
1698
1763
|
menu: InputMenu = None,
|
|
1699
|
-
items:
|
|
1764
|
+
items: MenuItems[InputGeometry] | None = None,
|
|
1700
1765
|
) -> "MenuSwitch[GeometrySocket]":
|
|
1701
1766
|
return MenuSwitch(menu, items, data_type="GEOMETRY")
|
|
1702
1767
|
|
|
@@ -1704,7 +1769,7 @@ class MenuSwitch[T: BaseSocket](_MenuSwitchBase[T]):
|
|
|
1704
1769
|
def collection(
|
|
1705
1770
|
cls,
|
|
1706
1771
|
menu: InputMenu = None,
|
|
1707
|
-
items:
|
|
1772
|
+
items: MenuItems[InputCollection] | None = None,
|
|
1708
1773
|
) -> "MenuSwitch[CollectionSocket]":
|
|
1709
1774
|
return MenuSwitch(menu, items, data_type="COLLECTION")
|
|
1710
1775
|
|
|
@@ -1712,7 +1777,7 @@ class MenuSwitch[T: BaseSocket](_MenuSwitchBase[T]):
|
|
|
1712
1777
|
def image(
|
|
1713
1778
|
cls,
|
|
1714
1779
|
menu: InputMenu = None,
|
|
1715
|
-
items:
|
|
1780
|
+
items: MenuItems[InputImage] | None = None,
|
|
1716
1781
|
) -> "MenuSwitch[ImageSocket]":
|
|
1717
1782
|
return MenuSwitch(menu, items, data_type="IMAGE")
|
|
1718
1783
|
|
|
@@ -1720,7 +1785,7 @@ class MenuSwitch[T: BaseSocket](_MenuSwitchBase[T]):
|
|
|
1720
1785
|
def material(
|
|
1721
1786
|
cls,
|
|
1722
1787
|
menu: InputMenu = None,
|
|
1723
|
-
items:
|
|
1788
|
+
items: MenuItems[InputMaterial] | None = None,
|
|
1724
1789
|
) -> "MenuSwitch[MaterialSocket]":
|
|
1725
1790
|
return MenuSwitch(menu, items, data_type="MATERIAL")
|
|
1726
1791
|
|
|
@@ -1728,7 +1793,7 @@ class MenuSwitch[T: BaseSocket](_MenuSwitchBase[T]):
|
|
|
1728
1793
|
def bundle(
|
|
1729
1794
|
cls,
|
|
1730
1795
|
menu: InputMenu = None,
|
|
1731
|
-
items:
|
|
1796
|
+
items: MenuItems[InputBundle] | None = None,
|
|
1732
1797
|
) -> "MenuSwitch[BundleSocket]":
|
|
1733
1798
|
return MenuSwitch(menu, items, data_type="BUNDLE")
|
|
1734
1799
|
|
|
@@ -1736,7 +1801,7 @@ class MenuSwitch[T: BaseSocket](_MenuSwitchBase[T]):
|
|
|
1736
1801
|
def closure(
|
|
1737
1802
|
cls,
|
|
1738
1803
|
menu: InputMenu = None,
|
|
1739
|
-
items:
|
|
1804
|
+
items: MenuItems[InputClosure] | None = None,
|
|
1740
1805
|
) -> "MenuSwitch[ClosureSocket]":
|
|
1741
1806
|
return MenuSwitch(menu, items, data_type="CLOSURE")
|
|
1742
1807
|
|
|
@@ -30,7 +30,7 @@ from ...types import (
|
|
|
30
30
|
InputVector,
|
|
31
31
|
)
|
|
32
32
|
from ..geometry import Frame, RepeatInput, RepeatOutput, RepeatZone
|
|
33
|
-
from ..geometry.manual import Float, _MenuSwitchBase
|
|
33
|
+
from ..geometry.manual import Float, MenuItems, _MenuSwitchBase
|
|
34
34
|
|
|
35
35
|
__all__ = [
|
|
36
36
|
"Attribute",
|
|
@@ -74,55 +74,55 @@ class MenuSwitch[T: BaseSocket](_MenuSwitchBase[T]):
|
|
|
74
74
|
|
|
75
75
|
@classmethod
|
|
76
76
|
def float(
|
|
77
|
-
cls, menu: InputMenu = None, items:
|
|
77
|
+
cls, menu: InputMenu = None, items: MenuItems[InputFloat] | None = None
|
|
78
78
|
) -> "MenuSwitch[FloatSocket]":
|
|
79
79
|
return MenuSwitch(menu, items, data_type="FLOAT")
|
|
80
80
|
|
|
81
81
|
@classmethod
|
|
82
82
|
def integer(
|
|
83
|
-
cls, menu: InputMenu = None, items:
|
|
83
|
+
cls, menu: InputMenu = None, items: MenuItems[InputInteger] | None = None
|
|
84
84
|
) -> "MenuSwitch[IntegerSocket]":
|
|
85
85
|
return MenuSwitch(menu, items, data_type="INT")
|
|
86
86
|
|
|
87
87
|
@classmethod
|
|
88
88
|
def boolean(
|
|
89
|
-
cls, menu: InputMenu = None, items:
|
|
89
|
+
cls, menu: InputMenu = None, items: MenuItems[InputBoolean] | None = None
|
|
90
90
|
) -> "MenuSwitch[BooleanSocket]":
|
|
91
91
|
return MenuSwitch(menu, items, data_type="BOOLEAN")
|
|
92
92
|
|
|
93
93
|
@classmethod
|
|
94
94
|
def vector(
|
|
95
|
-
cls, menu: InputMenu = None, items:
|
|
95
|
+
cls, menu: InputMenu = None, items: MenuItems[InputVector] | None = None
|
|
96
96
|
) -> "MenuSwitch[VectorSocket]":
|
|
97
97
|
return MenuSwitch(menu, items, data_type="VECTOR")
|
|
98
98
|
|
|
99
99
|
@classmethod
|
|
100
100
|
def color(
|
|
101
|
-
cls, menu: InputMenu = None, items:
|
|
101
|
+
cls, menu: InputMenu = None, items: MenuItems[InputColor] | None = None
|
|
102
102
|
) -> "MenuSwitch[ColorSocket]":
|
|
103
103
|
return MenuSwitch(menu, items, data_type="RGBA")
|
|
104
104
|
|
|
105
105
|
@classmethod
|
|
106
106
|
def menu(
|
|
107
|
-
cls, menu: InputMenu = None, items:
|
|
107
|
+
cls, menu: InputMenu = None, items: MenuItems[InputMenu] | None = None
|
|
108
108
|
) -> "MenuSwitch[MenuSocket]":
|
|
109
109
|
return MenuSwitch(menu, items, data_type="MENU")
|
|
110
110
|
|
|
111
111
|
@classmethod
|
|
112
112
|
def closure(
|
|
113
|
-
cls, menu: InputMenu = None, items:
|
|
113
|
+
cls, menu: InputMenu = None, items: MenuItems[InputClosure] | None = None
|
|
114
114
|
) -> "MenuSwitch[ClosureSocket]":
|
|
115
115
|
return MenuSwitch(menu, items, data_type="CLOSURE")
|
|
116
116
|
|
|
117
117
|
@classmethod
|
|
118
118
|
def bundle(
|
|
119
|
-
cls, menu: InputMenu = None, items:
|
|
119
|
+
cls, menu: InputMenu = None, items: MenuItems[InputBundle] | None = None
|
|
120
120
|
) -> "MenuSwitch[BundleSocket]":
|
|
121
121
|
return MenuSwitch(menu, items, data_type="BUNDLE")
|
|
122
122
|
|
|
123
123
|
@classmethod
|
|
124
124
|
def shader(
|
|
125
|
-
cls, menu: InputMenu = None, items:
|
|
125
|
+
cls, menu: InputMenu = None, items: MenuItems[InputShader] | None = None
|
|
126
126
|
) -> "MenuSwitch[ShaderSocket]":
|
|
127
127
|
return MenuSwitch(menu, items, data_type="SHADER")
|
|
128
128
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|