It would be easier to create a custom Control
that has the same functionality as a ProgressBar
and do everything yourself.
If you want to go low level you could also use a ProgressBar
and create a custom StyleBox
class that uses the RenderingServer
directly to draw a new canvas item with the Material
you want.
Like:
@tool
class_name StyleBoxShader extends StyleBox
@export var material:Material
@export var texture:Texture2D
# the canvas item we are going to use
var canvas_item_rid:RID
func _init() -> void:
# create the canvas item
canvas_item_rid = RenderingServer.canvas_item_create()
func _notification(what: int) -> void:
if what == NOTIFICATION_PREDELETE:
# don't forget to free it
RenderingServer.free_rid(canvas_item_rid)
func _draw(to_canvas_item: RID, rect: Rect2) -> void:
# clear the canvas item
RenderingServer.canvas_item_clear(canvas_item_rid)
# set its parent to the canvas item from the object we want to draw
# this will inherit transform, modulation and visibility from it
RenderingServer.canvas_item_set_parent(canvas_item_rid, to_canvas_item)
# set our material to the canvas item
RenderingServer.canvas_item_set_material(canvas_item_rid, material.get_rid())
if is_instance_valid(texture):
# if texture is valid, use it
RenderingServer.canvas_item_add_texture_rect(canvas_item_rid, rect, texture.get_rid())
else:
# if texture is not valid, draw a white rectangle
RenderingServer.canvas_item_add_rect(canvas_item_rid, rect, Color.WHITE)
func _get_draw_rect(rect: Rect2) -> Rect2:
return rect
func _get_minimum_size() -> Vector2:
return Vector2.ZERO
Result:
(I enabled CanvasItem.clip_children
in the ProgressBar
to get the rounded corners)
This “trick” could also work with a custom Texture2D
but the TextureProgressBar
class uses the RenderingServer
directly when drawing the textures using nine patch so the Texture2D._draw*()
callbacks are never called.