Multiline button from script

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By PEIGNAULT Laurent

Hello,

As I said in title, I create my buttons from an array by script.
Button are inside a Scroll container and the buttons width = screen width (with littles margins)
Everything is good except that if text is lager than the screen, the overflow content is hidden.

There is not auto wrap or multilines option with buttons. as I know…

If someone have a solution for me

:bust_in_silhouette: Reply From: Lopy

You can use a Label or RichtextLable instead of buttons, and catch the click in your _gui_input(). If it does not work, verifiy the mouse_filter property of your Controls, and try putting a Panel behind your text that does the catching instead, to avoid miss clicks.

Thank you.
I’m going to try.
I think I’ll lose buttons states un that case.
Have à nice day

PEIGNAULT Laurent | 2021-01-26 05:36

The problem with this answer is that the theme for Labels and RichTextLabels will not match the theme for Buttons.

blurrred | 2021-10-03 19:12

:bust_in_silhouette: Reply From: domportera

In C# I wrote something that can do this. It seems pretty simple to translate to GDScript if that’s more your jam. You can use it like this:

MultiLineButton button = new MultiLineButton("This button has multiple lines of text!");
AddChild(button.Control);

This is the MultiLineButton class:

public class MultiLineButton 
{
    public MultiLineButton(string labelText, bool addVerticalPadding, bool autoWrap, bool maximizeAnchors)
    {
        Button = new Button();
        ConfigureButtonControl(Button);
        Label = new Label();
        
        ConfigureLabel(Label, labelText, autoWrap, addVerticalPadding);
        ConfigureLabelControl(label: Label, child: Button);
        
        if (maximizeAnchors)
        {
            Button.MaximizeAnchors();
            Label.MaximizeAnchors();
        }
    }

    void ConfigureButtonControl(Control button)
    {
        button.SizeFlagsHorizontal = (int)Control.SizeFlags.ExpandFill;
        button.SizeFlagsVertical = (int)Control.SizeFlags.ExpandFill;
        button.ShowBehindParent = true;
    }

    void ConfigureLabelControl(Control label, Control child)
    {
        label.SizeFlagsHorizontal = (int)Control.SizeFlags.ExpandFill;
        label.SizeFlagsVertical = (int)Control.SizeFlags.ExpandFill;
        label.MouseFilter = Control.MouseFilterEnum.Ignore;
        Label.AddChild(child);
    }

    protected static void ConfigureLabel(Label label, string text, bool autoWrap, bool addVerticalPadding)
    {
        label.Text = addVerticalPadding ? $"\n{text}\n" : text;
        label.Autowrap = autoWrap;
        label.Align = Godot.Label.AlignEnum.Center;
        label.Valign = Godot.Label.VAlign.Center;
    }

    public Control Control => Label;
    public Button Button { get; }
    public Label Label { get; }
}

That MaximizeAnchors function is just an extension method that does this:

public static void MaximizeAnchors(this Control control)
{
    control.AnchorLeft = 0;
    control.AnchorRight = 1;
    control.AnchorTop = 0;
    control.AnchorBottom = 1;
}

A generic version (that’s called in exactly the same way) that’s more future-proof to future child classes of Label is here. Not sure how this would be done in gdscript (some kind of duck typing stuff probably). but it provides an easy way to extend functionality and use your own Label classes or any that crop up in the future.

public class MultiLineButton : MultiLineButtonBase<Label>
{
    public MultiLineButton(string labelText, bool addVerticalPadding, bool autoWrap = true, bool maximizeAnchors = true) 
        : base(labelText, addVerticalPadding, autoWrap, maximizeAnchors){ }
}

public class MultiLineButtonBase<T> where T : Label, new()
{
    public MultiLineButtonBase(string labelText, bool addVerticalPadding, bool autoWrap, bool maximizeAnchors)
    {
        Button = new Button();
        ConfigureButtonControl(Button);
        Label = new T();
        
        ConfigureLabel(Label, labelText, autoWrap, addVerticalPadding);
        ConfigureLabelControl(label: Label, child: Button);
        
        if (maximizeAnchors)
        {
            Button.MaximizeAnchors();
            Label.MaximizeAnchors();
        }
    }

    void ConfigureButtonControl(Control button)
    {
        button.SizeFlagsHorizontal = (int)Control.SizeFlags.ExpandFill;
        button.SizeFlagsVertical = (int)Control.SizeFlags.ExpandFill;
        button.ShowBehindParent = true;
    }

    void ConfigureLabelControl(Control label, Control child)
    {
        label.SizeFlagsHorizontal = (int)Control.SizeFlags.ExpandFill;
        label.SizeFlagsVertical = (int)Control.SizeFlags.ExpandFill;
        label.MouseFilter = Control.MouseFilterEnum.Ignore;
        Label.AddChild(child);
    }

    protected static void ConfigureLabel(Label label, string text, bool autoWrap, bool addVerticalPadding)
    {
        label.Text = addVerticalPadding ? $"\n{text}\n" : text;
        label.Autowrap = autoWrap;
        label.Align = Godot.Label.AlignEnum.Center;
        label.Valign = Godot.Label.VAlign.Center;
    }

    public Control Control => Label;
    public Button Button { get; }
    public Label Label { get; }
}