godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.16k stars 97 forks source link

Enhance corner and edge control with advanced slicing options in NinePatchRect #10463

Open nongvantinh opened 2 months ago

nongvantinh commented 2 months ago

Project Description

I am working on a project in Godot that requires creating responsive and scalable UI elements that retain their visual integrity across various screen sizes and resolutions. The NinePatchRect node is a key tool in this process, as it allows for creating UI elements with defined corners and edges that scale without distortion. However, the current limitations in defining corner and edge regions, combined with a lack of advanced control over texture behavior during scaling, have made it challenging to achieve the desired visual quality.


Problem or Limitation

The current implementation of NinePatchRect presents two main challenges:

  1. Corner and Edge Integrity: It's difficult to define and maintain the exact shapes and proportions of corners and edges during scaling. This often results in distorted corners or misaligned edges, which can compromise the visual consistency of UI elements.

  2. Limited Slicing Options: The options for controlling how the center and edges of the texture are stretched or tiled during scaling are limited. This makes it challenging to achieve the desired appearance, especially in complex UI designs that require more nuanced control over these areas.

https://github.com/user-attachments/assets/253b8a2b-a99b-418b-b28f-93201b480439


Feature / Enhancement Description

To address these issues, I propose an enhancement to the NinePatchRect node that combines more precise control over the corner and edge regions with advanced slicing options for the texture. This enhancement would include:

  1. Pixel-Perfect Corner and Edge Control:

    • Allow developers to define corner regions using exact pixel values, ensuring that corners maintain their original shape during scaling.
    • Provide an option to define edge regions as a percentage of the overall texture size, allowing for more flexible scaling and tiling of the edges.
  2. Advanced Slicing Options:

    • Introduce additional stretch modes for the center area, allowing for uniform, horizontal, or vertical stretching, or even custom tiling patterns.
    • Offer customizable stretch ratios for non-uniform stretching of the center area, as well as control over the tiling patterns and minimum/maximum tile sizes.
  3. Visual Guides in the Editor:

    • Implement visual guides within the editor to make it easier to see and adjust the corner and edge regions.
    • These guides would update in real-time as the node is resized, providing immediate feedback on how the texture will be sliced and scaled.

Proposal Implementation

The proposal would involve extending the existing NinePatchRect node with new properties and tools within the editor.

Properties:

Pseudo-Code:

class_name EnhancedNinePatchRect extends NinePatchRect

# Corner and edge properties
var corner_size_top_left: int = 10
var corner_size_top_right: int = 10
var corner_size_bottom_left: int = 10
var corner_size_bottom_right: int = 10
var edge_size_top: float = 0.1
var edge_size_bottom: float = 0.1
var edge_size_left: float = 0.1
var edge_size_right: float = 0.1

# Advanced slicing properties
var center_stretch_mode: int = CENTER_STRETCH_UNIFORM
var center_stretch_ratio_horizontal: float = 1.0
var center_stretch_ratio_vertical: float = 1.0
var center_tile_pattern: int = TILE_REPEAT
var center_tile_min_size: Vector2 = Vector2(0, 0)
var center_tile_max_size: Vector2 = Vector2(0, 0)

func _draw():
    # Drawing logic for corners and edges
    # Ensure corners maintain pixel-perfect sizes
    # Handle edges based on percentage sizes

    # Drawing logic for advanced slicing
    match center_stretch_mode:
        CENTER_STRETCH_UNIFORM:
            # Stretch uniformly
            draw_texture_rect_region(texture, center_rect, ..., center_stretch_ratio_horizontal, center_stretch_ratio_vertical)
        CENTER_STRETCH_HORIZONTAL:
            # Stretch horizontally only
            draw_texture_rect_region(texture, center_rect, ..., center_stretch_ratio_horizontal, 1.0)
        CENTER_STRETCH_VERTICAL:
            # Stretch vertically only
            draw_texture_rect_region(texture, center_rect, ..., 1.0, center_stretch_ratio_vertical)
        CENTER_STRETCH_NONE:
            # Tiling logic based on center_tile_pattern
            if center_tile_pattern == TILE_REPEAT:
                draw_tiled_texture(...)
            elif center_tile_pattern == TILE_MIRROR:
                draw_mirrored_texture(...)
            elif center_tile_pattern == TILE_STRETCH_TO_FIT:
                draw_stretch_to_fit_texture(...)

Workaround

Currently, achieving this functionality requires complex and time-consuming workarounds, such as manually adjusting margins and using custom scripts to handle advanced slicing. However, these methods are not only cumbersome but also prone to errors, particularly when dealing with dynamic resizing in-game.


Core Justification

This enhancement should be part of the core engine because it addresses a critical need in UI design: maintaining visual consistency and flexibility in scalable elements. Integrating these features directly into the NinePatchRect node will make it significantly easier for developers to create high-quality, responsive interfaces without relying on external plugins or custom scripts. This functionality is essential for any project that involves complex UI design, making it a valuable addition to the core engine.

nongvantinh commented 2 months ago

I'm designing a custom button, but when I use PanelContainer as its parent, I cannot set the anchor to Full Rect because the container controls its children.

I switched to using NinePatchRect as the parent, which allows me to set the anchor of the button. However, using Patch Margin also forces a minimum size for the NinePatchRect.

I fallback to the button, I used a theme override for the normal Button and used StyleBoxTexture just as with PanelContainer. However, this again imposes a minimum size.

If I only want to retain some corners and edges of the texture, I must set the minimum size for the node using it. I believe this is a flaw in the design.