There are still a few places that are more complicated where the replacement
to `IDP_New` isn't obvious, but this commit replaces most uses of the ugly
`IDPropertyTemplate` usage.
When creating popover with `UI_popover_begin` the popover block
is created just once without a region and since `UI_popover_begin` blocks
are not support refreshing, layout panels may not be compatible
in this kind of popover since open/close state cannot be refreshed either.
Only `UILayout.popover(...)` popovers are supported to have layout panels.
Pull Request: https://projects.blender.org/blender/blender/pulls/119796
"Own" (the adjective) cannot be used on its own. It should be combined
with something like "its own", "our own", "her own", or "the object's own".
It also isn't used separately to mean something like "separate".
Also, "its own" is correct instead of "it's own" which is a misues of the verb.
Reverting aa328e898e as this causes unintended changes elsewhere. Even
if made more conservative with just allowing UI_LAYOUT_PANEL and
force_menu. Will need further investigation for a better solution.
Pull Request: https://projects.blender.org/blender/blender/pulls/119187
Currently UI_BTYPE_POPOVER with icon and without text will get enough
extra padding to fit a downward arrow, only if in a header region. This
PR allows it to get the same padding in other areas when "force_menu"
is true.
Pull Request: https://projects.blender.org/blender/blender/pulls/119181
Over the last couple years (!) UI buttons have moved to derived classes,
meaning we don't need to use the same "a1" and "a2" variables to store
different information. At this point, that information is set specifically
by internal UI code, or functions like `UI_but_*_set`.
These values are only set to their default 0 values now (or -1 in some
non-meaningful cases). This commit removes the values from buttons
and removes the remaining a1 and a2 arguments from the UI API.
The line separator needs to know vertical or horizontal orientation
at draw time, and is independent of button rect. This PR removes the
use of uiBut->a1 for this in favor of a derived struct.
Pull Request: https://projects.blender.org/blender/blender/pulls/118969
The main simplification is using return values rather than return
arguments, and the additional semantic clarity from std::optional.
Also use `fmt` for formatting and use lambdas instead of macros
as helpers in a few modal keymap formatting functions.
Similar commits:
- a1792e98a4
- f04bc75f8c
- 6abf43cef5
- 7ca4dcac5a
Pull Request: https://projects.blender.org/blender/blender/pulls/117785
This simplifies the C++ API for making layout panels. Now it is also more similar to the Python API.
`uiLayoutPanel` is like `layout.panel` and `uiLayoutPanelProp` is like `layout.panel_prop`.
Both, `uiLayoutPanel` and `uiLayoutPanelProp`, now exist in two variants. One that takes a label
parameter and one that does not. If the label is passed in, only the panel body layout is returned.
Otherwise, the header and body are returned for more customization.
Pull Request: https://projects.blender.org/blender/blender/pulls/117670
For some reason the "get" function actually allocates the button's
operator properties container. This may or may not make sense to
do, but while it happens, the function name might as well make
that clear.
Removal of "confirm" operator callback for confirmation customization,
in favor of new method that shares existing operator dialog code and
allows python configuration.
Pull Request: https://projects.blender.org/blender/blender/pulls/117564
There are many instances where customizing the header row is desired so
return both the header row layout and the main body layout.
For consistency and correctness, the arrow symbol and the is_open check
are always performed so callers only need to perform layout tasks.
The C++ side now receives a `PanelLayout` structure containing both:
```cpp
PanelLayout panel_layout = uiLayoutPanelWithHeader( ... );
uiItemL(panel_layout.header, ... );
if (panel_layout.body) {
...
}
```
And the Python side receives a tuple:
```python
header, body = layout.panel( ... )
header.label(...)
if body:
...
```
```python
import bpy
from bpy.props import BoolProperty
class LayoutDemoPanel(bpy.types.Panel):
bl_label = "Layout Panel Demo"
bl_idname = "SCENE_PT_layout_panel"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "scene"
def draw(self, context):
layout = self.layout
scene = context.scene
layout.label(text="Basic Panels")
header, panel = layout.panel("my_panel_id", default_closed=True)
header.label(text="Hello World")
if panel:
panel.label(text="Success")
header, panel = layout.panel_prop(scene, "show_demo_panel")
header.label(text="My Panel")
if panel:
panel.prop(scene, "frame_start")
panel.prop(scene, "frame_end")
layout.label(text="Customized headers")
# Add a checkbox to the Panel header. text must be None for this panel
header, panel = layout.panel("my_panel_id-2", default_closed=True)
header.prop(scene, "use_checkmark", text="") # text must be empty for the checkbox
header.label(text="Checkmark at beginning")
if panel:
panel.label(text="Success")
header, panel = layout.panel("my_panel_id-3", default_closed=True)
header.label(text="Buttons at the end")
header.operator("mesh.primitive_cube_add", text="", icon='EXPORT')
header.operator("mesh.primitive_cube_add", text="", icon='X')
if panel:
panel.label(text="Success")
header, panel = layout.panel("my_panel_id-4", default_closed=True)
header.prop(scene, "use_checkmark2", text="")
header.label(text="Both")
header.operator("mesh.primitive_cube_add", text="", icon='EXPORT')
header.operator("mesh.primitive_cube_add", text="", icon='X')
if panel:
panel.label(text="Success")
bpy.utils.register_class(LayoutDemoPanel)
bpy.types.Scene.show_demo_panel = BoolProperty(default=False)
bpy.types.Scene.use_checkmark = BoolProperty(default=False)
bpy.types.Scene.use_checkmark2 = BoolProperty(default=False)
```
Pull Request: https://projects.blender.org/blender/blender/pulls/117248
This (likely copy-pasted) check to report either an unknown operator
type, or a known operator type missing its RNA struct definition, had a
reversed logic. It would report a 'missing srna' for unknown operator,
and vice-versa.
Instead of a single function with variadic arguments, a special enum
type containing which string to request, and a special struct to
contain the request and the result, just use separate functions for
each request, and return a std::string by value. Also change the enum
item string access to just give access to the enum item itself and add
const in a few places as necessary.
The callers of the API function get much clearer this way, and it's
much easier to see which information is used to create certain tooltip
strings.
The previous commit introduced a new `RPT_()` macro to translate
strings which are not tooltips or regular interface elements, but
longer reports or statuses.
This commit uses the new macro to translate many strings all over the
UI.
Most of it is a simple replace from `TIP_()` or `IFACE_()` to
`RPT_()`, but there are some additional changes:
- A few translations inside `BKE_report()` are removed altogether
because they are already handled by the translation system.
- Messages inside `UI_but_disable()` are no longer translated
manually, but they are handled by a new regex in the translation
system.
Pull Request: https://projects.blender.org/blender/blender/pulls/116804
Pull Request: https://projects.blender.org/blender/blender/pulls/116804
This adds a Python API for layout panels that have been introduced in #113584.
Two new methods on `UILayout` are added:
* `.panel(idname, text="...", default_closed=False) -> Optional[UILayout]`
* `.panel_prop(owner, prop_name, text="...") -> Optional[UILayout]`
Both create a panel and return `None` if the panel is collapsed. The difference lies
in how the open-close-state is stored. The first method internally manages the
open-close-state based on the provided identifier. The second one allows for
providing a boolean property that stores whether the panel is open. This is useful
when creating a dynamic of panels and when it is difficult to create a unique idname.
For the `.panel(...)` method, a new internal map on `Panel` is created which keeps
track of all the panel states based on the idname. Currently, there is no mechanism
for freeing any elements once they have been added to the map. This is unlikely to
cause a problem anytime soon, but we might need some kind of garbage collection
in the future.
```python
import bpy
from bpy.props import BoolProperty
class LayoutDemoPanel(bpy.types.Panel):
bl_label = "Layout Panel Demo"
bl_idname = "SCENE_PT_layout_panel"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "scene"
def draw(self, context):
layout = self.layout
scene = context.scene
layout.label(text="Before")
if panel := layout.panel("my_panel_id", text="Hello World", default_closed=False):
panel.label(text="Success")
if panel := layout.panel_prop(scene, "show_demo_panel", text="My Panel"):
panel.prop(scene, "frame_start")
panel.prop(scene, "frame_end")
layout.label(text="After")
bpy.utils.register_class(LayoutDemoPanel)
bpy.types.Scene.show_demo_panel = BoolProperty(default=False)
```
Pull Request: https://projects.blender.org/blender/blender/pulls/116949
Along with the 4.1 libraries upgrade, we are bumping the clang-format
version from 8-12 to 17. This affects quite a few files.
If not already the case, you may consider pointing your IDE to the
clang-format binary bundled with the Blender precompiled libraries.
This adds support for so called "layout panels" which are panels that are created as part
of `uiLayout`. The goal is to make it easier to have expandable sections as part of a UI.
The initial use case for this are panels in the geometry nodes modifier. This patch provides
a better solution compared to what was attempted in #108565.
### Problems with Existing Approaches
Currently, there are two ways to create these expandable sections:
* One can define a new `Panel` type for each expandable section and use the `parent_id`
to make this a subpanel of another panel. This has a few problems:
* `uiLayout` drawing code is more scattered, because one can't just use a single function
that creates the layout for an entire panel including its subpanels.
* It does not work so well for dynamic amounts of panels (e.g. like what we need for
the geometry nodes modifier to organize the inputs).
* Typically, Blender uses a immediate-ui approach, but subpanels break that currently
and need extra handling.
* The order of panels is not very explicit.
* One can't interleave subpanels and other ui elements, subpanels always come at the
end of the parent panel.
* Custom solution using existing `uiLayout`. This is done in the material properties. It
also has a few problems:
* Custom solutions tend to work slightly different in different places. So the UI is less unified.
* Can't drag open/close multiple panels.
* The background color for subpanels does not change.
### Solution
A possible solution to all of these problems is to add support for panels to `uiLayout` directly:
```cpp
/* Add elements before subpanel. */
if (uiLayout *panel_layout = uiLayoutPanel(layout, ...)) {
/* Add elements in subpanel, but only of the panel is open. */
}
/* Add elements after subpanel. */
```
Integrating subpanels with `uiLayout` has some benefits:
* Subpanels are treated like any other sub-layout and don't have unnecessary boilerplate.
* It becomes trivial to have a dynamic number of potentially nested subpanels.
* Resolves all mentioned problems of custom subpanel solutions.
### Open/Close State
The most tricky thing is to decide where to store the open/close state. Ideally, it should
be stored in the `region` because then the same layout panel can be opened and closed
in every region independently. Unfortunately, storing the state in the region is fairly
complex in some cases.
For example, for modifier subpanels the region would have to store an open/close state
for each panel in each modifier in each object. So a map with
`object pointer + modifier id + panel id` as key would be required. Obviously, this map
could become quite big. Also storing that many ID pointers in UI data is not great and
we don't even have stable modifier ids yet. There also isn't an obvious way for how to
clear unused elements from the map which could become necessary when it becomes big.
In practice, it's rare that the same modifier list is shown in two editors. So the benefit of
storing the open/close state in the region is negligible. Therefor, a much simpler solution
is possible: the open/close state can be stored in the modifier directly. This is actually
how it was implemented before already (see `ui_expand_flag`).
The implementation of layout panels in this patch is *agnostic* to how the open/close
state is stored exactly, as long as it can be referenced as a boolean rna property. This
allows us to store the state in the modifier directly but also allows us to store the state
in the region for other layout panels in the future. We could consider adding an API that
makes it easy to store the state in the region for cases where the key is simpler.
For example: `uiLayoutPanel(layout, TIP_("Mesh Settings"), PanelRegionKey("mesh_settings"))`.
### Python API (not included)
Adding a Python API is fairly straight forward. However, it is **not** included in this patch
so that we can mature the internal API a bit more if necessary, before addon developers
start to depend on it. It would probably work like so:
```python
if panel := layout.panel("Mesh Settings", ...):
# Add layout elements in the panel if it's open.
```
Pull Request: https://projects.blender.org/blender/blender/pulls/113584