godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
91.6k stars 21.28k forks source link

PopupMenu generated by OptionButton is not inheriting parent's material when use_parent_material is true #66587

Open Sunfire-Interactive opened 2 years ago

Sunfire-Interactive commented 2 years ago

Godot version

v3.5.1.stable.official [6fed1ffa3]

System information

Windows 10

Issue description

When using an OptionButton node, the internal PopupMenu node generated at runtime does not inherit the material from its parent when the popup's use_parent_material value is set to true. As far as I can tell, this is not intended behaviour, as the generated PopupMenu is a direct child node of the OptionButton, and this does not occur with manually created PopupMenu nodes.

Steps to reproduce

  1. Create an OptionButton node.
  2. Add a CanvasMaterial to the OptionButton.
  3. Add a script to the OptionButton with the following line in the _ready() function: get_popup().use_parent_material = true
  4. Run the scene. The materials on the OptionButton and PopupMenu do not match, despite the value being set, as seen in the Remote scene tree. image image

Minimal reproduction project

OptionButton Material Bug.zip

Zeerus commented 2 months ago

Ran into this as well today on my project (Godot 4.3). My use case being that I instantiate several sub-scenes in my GUI to keep things separate. I use the main root of this hierarchy that everything is instantiated under to use a generalized canvas shader material I put on all its children.

It seems that this behavior is due to PopupMenu having a different ancestry. It doesn't inherit from CanvasItem from what I can tell (granted my C++ is pretty rusty), and is basically a special case of a Window. Open to any suggestions on a workaround if someone happens across this.

Edit: This works

extends OptionButton

var mat = load("res://Materials/UI/ShaderMaterial.tres")

func _ready() -> void:
    set_material_scroll_cont()

func set_material_scroll_cont():
    var scroll_cont = get_popup().get_child(0, true)    
    scroll_cont.material = mat
    for child in scroll_cont.get_children(true):
        child.use_parent_material = true

OR This, though I can't speak to how efficient it is:

extends OptionButton

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
    if use_parent_material == true:
        set_material_scroll_cont()

func set_material_scroll_cont() -> void:
    var mat: Material = null
    var test_parent: Node = get_parent()
    while mat == null:
        if test_parent is CanvasItem:
            mat = (test_parent as CanvasItem).material
            if mat == null:
                test_parent = test_parent.get_parent()
        else:
            break

    if mat != null:
        var scroll_cont: ScrollContainer = get_popup().get_child(0, true) as ScrollContainer
        scroll_cont.material = mat
        for child: Control in (scroll_cont.get_children(true) as Array[Control]):
            child.use_parent_material = true