oωo (owo-lib)

repository·braid-ui·Indexed 42 days ago

https://github.com/wisp-forest/owo-lib

A comprehensive utility, GUI, and configuration library for Fabric modding. It features owo-ui for declarative UI, owo-config for synchronized configuration, an automatic registration system, a networking layer with automatic serialization, and the Braid framework for building dynamic screens and widgets.

Tokens
6.7K
Snippets
32
Records
37
Agent score
54%

What's inside owo-lib

  1. Overview of oωo features

    braid-ui

    oωo is a general utility, GUI, and config library for Fabric modding designed to reduce code verbosity. Key features include:

    • owo-ui: A declarative UI library for building dynamic screens.
    • owo-config: A customizable configuration system built on owo-ui that supports server-client synchronization.
    • Registration System: A generic, automatic system for handling registries.
    • Item Group Extensions: Allows for sub-tabs, custom buttons, and textures within mod item groups.
    • Networking Layer: Features automatic serialization and handshaking for client compatibility.
    • Particle Helpers: Client-side tools for composing multi-particle effects.
    • Rich Text Translations: Enables using Minecraft's text component format directly in language files.
  2. Add oωo (owo-lib) to your Fabric project

    braid-ui

    To use oωo in your Fabric mod, add the Wisp Forest Maven repository and include the owo-lib dependency. If you plan to use the configuration system (owo-config), you must also include the annotationProcessor.

    Optionally, you can include owo-sentinel. This allows your mod to run without requiring users to manually install oωo; instead, sentinel will warn them and provide an option to download it automatically.

    // Groovy DSL
    repositories {
        maven { url 'https://maven.wispforest.io' }
    }
    
    dependencies {
        modImplementation "io.wispforest:owo-lib:${project.owo_version}"
        // only if you plan to use owo-config
        annotationProcessor "io.wispforest:owo-lib:${project.owo_version}"
        
        // include this if you don't want force your users to install owo
        include "io.wispforest:owo-sentinel:${project.owo_version}"
    }
  3. Add oωo (owo-lib) to your Fabric project (Kotlin DSL)

    braid-ui

    For projects using the Kotlin DSL, add the Wisp Forest Maven repository and the owo-lib dependency. Include the annotationProcessor if using owo-config, and owo-sentinel if you want to provide an automatic download option for users instead of forcing a hard dependency.

    // Kotlin DSL
    repositories {
        maven("https://maven.wispforest.io")
    }
        
    dependencies {
        modImplementation("io.wispforest:owo-lib:${properties["owo_version"]}")
        // only if you plan to use owo-config
        annotationProcessor("io.wispforest:owo-lib:${properties["owo_version"]}")
        
        // include this if you don't want force your users to install owo
        include("io.wispforest:owo-sentinel:${properties["owo_version"]}")
    }
  4. Access ambient UI layer context with LayerContextScope

    braid-ui

    In the Braid framework, LayerContextScope acts as an ambient provider for UI layer information. It is used to propagate the current Screen context down the widget tree. You can access this context within a BuildContext using the static helper methods provided by LayerContext.

    Note that attempting to access the context via of(context) (internally used by the helpers) when no LayerContextScope is present in the ancestor tree will throw an IllegalStateException.

  5. Manage UI navigation with Navigator

    braid-ui

    The Navigator widget manages a stack of routes within the Braid UI framework. It uses a SharedState containing a NavigationState to track which widgets are currently active.

    When a Navigator is built, it renders a Stack of the currently displayed routes. Routes can be added as standard pages or as overlays. Overlays are treated differently in the stack: the Navigator calculates the displayedRoutes by looking at the top of the stack and including all consecutive overlay routes until it hits a non-overlay route.

    // Initialize a Navigator with an initial starting widget
    Navigator navigator = new Navigator(new MyInitialWidget());
  6. Implement a custom Widget in the Braid framework

    braid-ui

    To create a UI component in the Braid framework, extend the Widget abstract class. Widgets are the base building blocks of the UI.

    Key characteristics of a Widget:

    • Mutability: By default, widgets are mutable. However, once a widget is frozen (via freeze()), any attempt to modify its state (like setting a key) will throw an ImmutableWidgetError.
    • Keys: You can assign a Key to a widget using the .key(Key key) method. Keys are used by the framework to determine if a widget can be updated in place.
    • Proxying: Every widget must implement the proxy() method, which returns a WidgetProxy instance used for interacting with the widget's state or lifecycle.
    • Update Logic: The framework uses Widget.canUpdate(oldWidget, newWidget) to decide if a new widget can replace an old one. This check passes if both widgets are of the same class and share the same key.
    import io.wispforest.owo.braid.framework.widget.Widget;
    import io.wispforest.owo.braid.framework.proxy.WidgetProxy;
    
    public class MyCustomWidget extends Widget {
        @Override
        public WidgetProxy proxy() {
            // Return your implementation of WidgetProxy
            return null; 
        }
    }
  7. Use Widget keys for identity and updates

    braid-ui

    Assigning a Key to a Widget allows the Braid framework to identify the widget across different render cycles.

    When the framework performs an update, it calls Widget.canUpdate(oldWidget, newWidget). An update is considered valid if:

    1. oldWidget.getClass() == newWidget.getClass()
    2. Objects.equals(oldWidget.key, newWidget.key)

    If the keys match and the classes match, the framework can reuse the existing widget instance instead of destroying and recreating it.

    // Assigning a key to a widget
    myWidget.key(someKeyInstance);
    
    // Retrieving the key
    Key currentKey = myWidget.key();
  8. Find a specific Minecraft AbstractWidget in the current layer

    braid-ui

    Use LayerContext.findWidget(BuildContext context, Predicate<AbstractWidget> predicate) to search for a specific Minecraft AbstractWidget within the current contextScreen. The method traverses the children of the screen (including those within Layout objects) and returns the first widget that satisfies the provided predicate.

    // Example: Find a widget by its class type
    AbstractWidget myWidget = LayerContext.findWidget(context, widget -> widget instanceof MyCustomWidget);
  9. Hide a specific toast using hideWithToken()

    braid-ui

    If you provided a token when calling BraidToast.show(), you can dismiss that specific toast before its timeout expires by calling BraidToast.hideWithToken(token). This searches the current Minecraft ToastManager for a BraidToast instance associated with that token and sets its visibility to HIDE.

    // To hide the toast created with 'myToken'
    BraidToast.hideWithToken(myToken);
  10. Create an EnumArgumentType for command arguments

    braid-ui

    Use EnumArgumentType.create(Class<T> enumClass) or EnumArgumentType.create(Class<T> enumClass, String noElementMessage) to create a new argument type that parses a specific Java enum.

    Important Requirements:

    • Dual Registration: You must call these methods on both the server and the client during mod initialization. This ensures the serializer is registered correctly in the type registry.
    • Initialization Timing: These calls must occur during mod initialization while the registries are still mutable.
    • Error Messages: If using the version with a custom message, you can use a {} placeholder to include the invalid value provided by the user (e.g., "Invalid value '{}'").

    Once created, use the same instance for both registering the argument in Commands#argument(String, ArgumentType) and for retrieving the value via .get(CommandContext, String).

    // 1. Create the instance (must be done on both client and server during init)
    EnumArgumentType<MyEnum> MY_ENUM_TYPE = EnumArgumentType.create(MyEnum.class, "Unknown option '{}'");
    
    // 2. Use it in command registration
    // commands.argument("option", MY_ENUM_TYPE).executes(...)
    
    // 3. Retrieve the value in the execution context
    // MyEnum value = MY_ENUM_TYPE.get(context, "option");