Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GUI Proposal #132

Open
Spartan322 opened this issue Oct 26, 2021 · 4 comments
Open

GUI Proposal #132

Spartan322 opened this issue Oct 26, 2021 · 4 comments
Labels
Enhancement Feature Bloat ew, why WIP Work In Progress

Comments

@Spartan322
Copy link
Collaborator

Spartan322 commented Oct 26, 2021

Currently the only GUI in Pathfinder is PFButtons which are basically just a class wrapper over Hacknet's C-function vanilla buttons.

This proposal should cover all common usable GUI objects for the game and define them in a manner for which they are automatically handled. Behavior should also be intrinsically extendable. Feel free to comment desired additions or changes.

All definitions will be added to the Pathfinder.GUI namespace unless otherwise specified.

Current Interfaces:

interface IDrawObject : IEnumerable<IGuiElement>
{
    GameScreen Target { get; }
    IDrawObject Root { get; }
    
    SpriteFont DefaultFont { get; }
    bool TrySetDefaultFont(SpritFont font);

    void DrawCircle(Vector2 position, float radius, Color fillColor);
    void DrawLine(Vector2 from, Vector2 to, Color color, float width = 1.0f, bool antialiased = false);
    void DrawRect(Rectangle rect, Color color, bool filled = true, float width = 1.0f, bool antialiased = false);
    void PushTransform(Vector2 position = default, float rotation = default, Vector2 scale = default);
    Tuple<Vector2, float, Vector2> PeekTransformStack();
    Tuple<Vector2, float, Vector2> GetLastPushedTransform();
    Tuple<Vector2, float, Vector2> GetCombinedTransform();
    void PopTransform();
    void ClearAllTransforms();
    void DrawTexture(Texture2D texture, Vector2 position, Color modulate = Colors.White);
    void DrawVertices(IEnumerable<Tuple<Vector2, Color, Vector2>> vertsAndColorAndUvs, Texture2D texture = default, float width = 1f);
    void DrawArc(Vector2 center, float radius, float startAngle, float endAngle, int points, Color color, float width = 1f, bool antialiased = false);
    float DrawChar(Vector2 position, char current, char next, Color modulate = Colors.White, SprintFont font = default);
    void DrawString(Vector2 position, string text, Color modulate = Color.White, int clipWidth = int32.MaxValue, SpriteFont font = default);
    
    void Initialize();
    void Draw(float t);
    void Update(float t);
    void Input(InputEvent evt);
    void UnhandledInput(InputEvent evt);

    void ConsumeInput();

    event GuiAction OnInitialize;
    event GuiAction<IGuiElement> OnElementAdd;
    event GuiAction<IGuiElement> OnElementRemove;
    event GuiAction<float> OnUpdate;
    event GuiAction<float> OnDraw;
    event GuiAction<InputEvent> OnInput;

    void AddElement(IGuiElement element, int index = int.MaxValue);
    void AddElementAfter(IGuiElement addElement, IGuiElement before);
    void RemoveElement(IGuiElement element);
    void RemoveElement(string identifier);
    void RemoveElementAt(int index, int count = 1);
    void RemoveElementIf(Predicate<IGuiElement> evaluate, int count = int.MaxValue);
    bool HasElement(IGuiElement element);
    bool HasElement(string identifier);
    IGuiElement GetElement(string identifier);
    ElementT GetElement<ElementT>(string identifier) where ElementT : IGuiElement;
    IGuiElement FindElement(string path);
    ElementT FindElement<ElementT>(string path) where ElementT : IGuiElement;
    int ElementCount { get; }
    IGuiElement this[string identifier] { get; }
    IList<IGuiElement> GetElements();
    IList<ElementT> GetElementsOf<ElementT>() where ElementT : IGuiElement;
}
interface IGuiElement : IDrawObject
{
    IDrawObject Parent { get; }

    string Identifier { get; }
    bool TrySetIdentifier(string identifier);
    int Index { get; }
    bool TrySetIndex(int index);
    Color Color { get; }
    bool TrySetColor(Color color);

    void LoadTo(IDrawObject parent);
    void Unload();

    event GuiAction OnLoadTo;
    event GuiAction OnUnload;

    string GetPath();
}

Current Classes:

delegate void GuiAction(IGuiElement element);
delegate void GuiAction<T>(IGuiElement element, T arg1);

enum HorizontalAlign
{
    Left,
    Center,
    Right,
    Fill
}
enum VerticalAlign
{
    Top,
    Center,
    Bottom,
    Fill
}

class Theme
{
    public SpriteFont DefaultFont { get; set; }
    public Dictionary<string, ThemeType> ThemeTypes { get; }
}

class ThemeType
{
    public Theme Theme { get; }
    public SpriteFont DefaultFont { get; set; }
    public Dictionary<string, Color> Colors { get; }
    public Dictionary<string int> Integers { get; }
    public Dictionary<string, float> Floats { get; }
    public Dictionary<string, SpriteFont> Fonts { get; }
    public Dictionary<string, Texture2D> Textures { get; }
}

class InputEvent {}
class GameScreenWrapper : IDrawObject
{
    GameScreenWrapper(GameScreen toWrap, SpriteFont defaultFont = default)
    {
        Target = toWrap;
        Root = this;
        DefaultFont = defaultFont ?? GuiData.font;
    }
}
class BaseGuiElement : IGuiElement
{
    readonly Dictionary<string, IGuiElemt> Elements;
}
class BaseRectElement : BaseGuiElement
{
    virtual Rectangle Rect { get; }
    virtual bool TrySetRect(Rectangle rect);
}
class TextLabel : BaseRectElement
{
    virtual string Text { get; }
    virtual bool TrySetText(string text);
    virtual HorizontalAlign HorizontalAlign { get; }
    virtual bool TrySetHorizontalAlign(HorizontalAlign align);
    virtual VerticalAlign VerticalAlign { get; }
    virtual TrySetVerticalAlign(VerticalAlign align);
    virtual bool ClipText { get; }
    virtual bool TrySetClipText(bool clip);
    virtual bool WrapHorizontal { get; }
    virtual bool TrySetWrapHorizontal(bool wrap);
    virtual int VisibleTextRatio { get; }
    virtual bool TrySetVisibleTextRatio(int ratio);
}
class BaseRectClickable : BaseRectElement
{
    enum ActiveTypeEnum
    {
        Down,
        Up,
        Toggle
    }
    [Flags]
    enum MouseButtonMask
    {
        Left        = 0b10000,
        Right       = 0b01000,
        Middle      = 0b00100,
        XButton1    = 0b00010,
        XButton2    = 0b00001,
        Both        = Left | Right,
        Any         = Left | Right | Middle | XButton1 | XButton2
    }

    virtual float HeldTimeActivation { get; } = 0;
    virtual bool TrySetHeldTimeActivation(float heldTime);
    virtual ActiveTypeEnum ActiveType { get; }
    virtual bool TrySetActiveType(ActiveTypeEnum type);
    virtual MouseButtonMask MouseButton { get; }
    virtual bool TrySetActiveMouseButton(MouseButtonMask mask);
    virtual Color? ClickedColor { get; }
    virtual bool TrySetClickedColor(Color? color);
    virtual Color? HoverColor { get; }
    virtual bool TrySetHoverColor(Color? color);
    virtual bool IsDisabled { get; }
    virtual bool TrySetIsDisabled(bool disabled);
    virtual Color? DisabledColor { get; }
    virtual bool TrySetDisabledColor(Color? color);
    virtual bool IsActive { get; }
    virtual bool SetIsActive(bool active);

    virtual bool IsMouseOvertop(Vector2 relativeMousePosition);

    delegate void ClickableAction(BaseRectClickable sender);
    delegate void ClickableAction<T>(BaseRectClickable sender, T arg1);
    event ClickableAction<MouseButtonMask> OnDown;
    event ClickableAction<MouseButtonMask> OnUp;
    event ClickableAction OnActivated;
}
class TextButton : BaseRectClickable
{
    virtual Texture2D BackgroundTexture { get; }
    virtual bool TrySetBackgroundTexture(Texture2D texture);
    virtual Color? TextColor { get; }
    virtual bool TrySetTextColor(Color? color);
    virtual string Text { get; }
    virtual bool TrySetText(string text);
    virtual HorizontalAlign HorizontalAlign { get; }
    virtual bool TrySetHorizontalAlign(HorizontalAlign align);
    virtual VerticalAlign VerticalAlign { get; }
    virtual TrySetVerticalAlign(VerticalAlign align);
    virtual bool ClipText { get; }
    virtual bool TrySetClipText(bool clip);
}
class VanillaButton : TextButton
{
    bool DrawOutlineOnly { get; set; }
    Color? OutlineColor { get; set; }
    Color? TagColor { get; set; }
}
class CheckboxButton : TextButton
{
    virtual TextLabel TooltipLabel { get; }
    virtual bool TrySetTooltipLabel(TextLabel label);
}
class DraggableRectangle : BaseRectClickable
{
    virtual Vector2? XLimit { get; }
    virtual bool TrySetXLimit(Vector2? limit);
    virtual Vector2? YLimit { get; }
    virtual bool TrySetYLimit(Vector2? limit);
    virtual Rectangle? SelectableBorder { get; }
    virtual bool TrySetSelectableBorder(Rectangle? border);
    virtual bool TrySetSelectableBorder(Vector2? border);
    virtual bool TrySetSelectableBorder(float? border);
}
class RangeElement : BaseRectClickable
{
    enum RangeDirection
    {
        Vertical,
        Horizontal
    }
    virtual RangeDirection Direction { get; }
    virtual bool TrySetDirection(RangeDirection direction);
    virtual float Value { get; }
    virtual bool TrySetValue(float value);
    virtual float MaxValue { get; }
    virtual bool TrySetMaxValue(float max);
    virtual float MinValue { get; }
    virtual bool TrySetMinValue(float min);
    virtual float Step { get; }
    virtual bool TrySetStep(float step);
}
class BaseScrollbar : RangeElement
{
    DraggableRectangle ScrollbarElement { get; }
    virtual bool DrawScrollbarOnly { get; }
    virtual bool TrySetDrawScrollbarOnly(bool draw);
}
class ScrollPanel : BaseRectElement
{
    BaseScrollbar Scrollbar { get; }
    virtual bool OnlyScrollOnMouseover { get; }
    virtual bool TrySetOnlyScrollOnMouseover(bool scroll);
    virtual float? MaxScroll { get; }
    virtual bool TrySetMaxScroll(float? max);
}
class SliderBar : RangeElement
{
}
class TextBoxCaret : BaseRectElement
{
    virtual float? BlinkSpeed { get; }
    virtual bool TrySetBlinkSpeed(float? speed);
    virtual int CaretPosition { get; }
    virtual bool TrySetCaretPosition(int position);
}
class TextBox : TextButton
{
    TextBoxCaret Caret { get; }
    virtual bool CanExpandToContent { get; }
    virtual bool TrySetCanExpandToContent(bool expand);
    virtual int MaxLength { get; }
    virtual bool TrySetMaxLength(int maxLength);
    virtual string SelectedText { get; }
    virtual bool TrySetSelectedText(string text);
    virtual int SelectionPosition { get; }
    virtual bool TrySetSelectionPosition(int position);
    virtual string PlaceholderText { get; }
    virtual bool TrySetPlaceholderText(string text);
    virtual Color? PlaceholderColor { get; }
    virtual bool TrySetPlaceholderColor(Color? color);
    virtual SpriteFont PlaceholderFont { get; }
    virtual bool TrySetPlaceholderFont(SpriteFont font);
    virtual string SecretCharacter { get; }
    virtual bool TrySetSecretCharacter(string character);
    virtual int MaxLines { get; }
    virtual bool TrySetMaxLines(int lines);
}

The TrySet pattern is to enable implementations to determine their own management of the objects, so its easy to know when a successful set has taken place.

InputEvent is a considered system for dealing with input events, enabling propagation and consumption of events by elements. It is likely to be flushed out after this is finalized.

Gui Elements which are expected to manage their contents, like say a panel or window, can reference them via the Element related methods.

This is unfinished, but design additions are still welcomed.

@SoundOfScooting
Copy link
Contributor

Maybe IClickableElement should be split or extended to support also detecting right-clicks and allowing different behavior for left-clicks and right-clicks.

@Fayti1703
Copy link
Contributor

IDisablableElement should be IDisableableElement.
The Modulate properties should also be renamed imo -- that's a verb, not a noun and they're Colors.
BaseRectButton also implements IClicableElement, not IClickableElement (the definition is spelled correctly)

I'm also not sure if all the properties should have both a getter and a setter -- one might want to build elements that dynamically adjust, for example, their disabledness depending on other state, with no way to override it. The current interfaces would require a throwing setter for that case.

@Spartan322
Copy link
Collaborator Author

I'm also not sure if all the properties should have both a getter and a setter -- one might want to build elements that dynamically adjust, for example, their disabledness depending on other state, with no way to override it. The current interfaces would require a throwing setter for that case.

There's nothing stopping the element from just ignoring the setter, if its directly implementing the interface it can even hide the setter when the class is referenced, in every other case it can simply create an empty setter and ignore it. With something this automatic I don't think anything should be built with the expectation that changes need be reflected and it costs less than an exception to check even in that case.

@Spartan322
Copy link
Collaborator Author

The GUI Proposal has been redone, the old one can still be found here

@Spartan322 Spartan322 added the WIP Work In Progress label Apr 29, 2022
@Spartan322 Spartan322 changed the title [WIP] GUI Proposal GUI Proposal Apr 29, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement Feature Bloat ew, why WIP Work In Progress
Projects
None yet
Development

No branches or pull requests

3 participants