From baf4828a710b218b2f51fd7418f01818dea38d92 Mon Sep 17 00:00:00 2001
From: isXander <xandersmith2008@gmail.com>
Date: Fri, 26 May 2023 17:59:59 +0100
Subject: API layer for built-in controllers

---
 .../java/dev/isxander/yacl/api/ButtonOption.java   | 27 ++------
 .../java/dev/isxander/yacl/api/LabelOption.java    |  6 +-
 .../java/dev/isxander/yacl/api/ListOption.java     | 12 ----
 .../dev/isxander/yacl/api/ListOptionEntry.java     |  5 --
 .../main/java/dev/isxander/yacl/api/Option.java    | 46 +++----------
 .../dev/isxander/yacl/api/OptionDescription.java   |  6 +-
 .../java/dev/isxander/yacl/api/OptionGroup.java    | 10 ---
 .../api/controller/BooleanControllerBuilder.java   | 16 +++++
 .../api/controller/ColorControllerBuilder.java     | 14 ++++
 .../yacl/api/controller/ControllerBuilder.java     |  9 +++
 .../controller/CyclingListControllerBuilder.java   | 15 +++++
 .../controller/DoubleFieldControllerBuilder.java   | 10 +++
 .../controller/DoubleSliderControllerBuilder.java  |  4 ++
 .../yacl/api/controller/EnumControllerBuilder.java | 12 ++++
 .../controller/FloatFieldControllerBuilder.java    | 10 +++
 .../controller/FloatSliderControllerBuilder.java   | 10 +++
 .../controller/IntegerFieldControllerBuilder.java  | 10 +++
 .../controller/IntegerSliderControllerBuilder.java | 10 +++
 .../api/controller/LongFieldControllerBuilder.java | 10 +++
 .../controller/LongSliderControllerBuilder.java    | 10 +++
 .../controller/NumberFieldControllerBuilder.java   |  7 ++
 .../api/controller/SliderControllerBuilder.java    |  6 ++
 .../api/controller/StringControllerBuilder.java    | 10 +++
 .../api/controller/TickBoxControllerBuilder.java   | 10 +++
 .../api/controller/ValueFormattableController.java |  9 +++
 .../controllers/cycling/CyclingListController.java |  4 +-
 .../gui/controllers/cycling/EnumController.java    | 21 +-----
 .../dev/isxander/yacl/impl/ButtonOptionImpl.java   | 44 +++----------
 .../dev/isxander/yacl/impl/ConfigCategoryImpl.java |  3 +-
 .../dev/isxander/yacl/impl/LabelOptionImpl.java    |  5 --
 .../dev/isxander/yacl/impl/ListOptionImpl.java     | 36 +---------
 .../dev/isxander/yacl/impl/OptionGroupImpl.java    | 31 +--------
 .../java/dev/isxander/yacl/impl/OptionImpl.java    | 76 +++-------------------
 .../controller/AbstractControllerBuilderImpl.java  | 12 ++++
 .../controller/BooleanControllerBuilderImpl.java   | 56 ++++++++++++++++
 .../controller/ColorControllerBuilderImpl.java     | 27 ++++++++
 .../CyclingListControllerBuilderImpl.java          | 43 ++++++++++++
 .../DoubleFieldControllerBuilderImpl.java          | 50 ++++++++++++++
 .../DoubleSliderControllerBuilderImpl.java         | 43 ++++++++++++
 .../impl/controller/EnumControllerBuilderImpl.java | 35 ++++++++++
 .../FloatFieldControllerBuilderImpl.java           | 50 ++++++++++++++
 .../FloatSliderControllerBuilderImpl.java          | 43 ++++++++++++
 .../IntegerFieldControllerBuilderImpl.java         | 50 ++++++++++++++
 .../IntegerSliderControllerBuilderImpl.java        | 43 ++++++++++++
 .../controller/LongFieldControllerBuilderImpl.java | 50 ++++++++++++++
 .../LongSliderControllerBuilderImpl.java           | 43 ++++++++++++
 .../controller/StringControllerBuilderImpl.java    | 17 +++++
 .../controller/TickBoxControllerBuilderImpl.java   | 17 +++++
 48 files changed, 810 insertions(+), 283 deletions(-)
 create mode 100644 common/src/main/java/dev/isxander/yacl/api/controller/BooleanControllerBuilder.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/api/controller/ColorControllerBuilder.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/api/controller/ControllerBuilder.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/api/controller/CyclingListControllerBuilder.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/api/controller/DoubleFieldControllerBuilder.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/api/controller/DoubleSliderControllerBuilder.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/api/controller/EnumControllerBuilder.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/api/controller/FloatFieldControllerBuilder.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/api/controller/FloatSliderControllerBuilder.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/api/controller/IntegerFieldControllerBuilder.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/api/controller/IntegerSliderControllerBuilder.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/api/controller/LongFieldControllerBuilder.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/api/controller/LongSliderControllerBuilder.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/api/controller/NumberFieldControllerBuilder.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/api/controller/SliderControllerBuilder.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/api/controller/StringControllerBuilder.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/api/controller/TickBoxControllerBuilder.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/api/controller/ValueFormattableController.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/impl/controller/AbstractControllerBuilderImpl.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/impl/controller/BooleanControllerBuilderImpl.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/impl/controller/ColorControllerBuilderImpl.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/impl/controller/CyclingListControllerBuilderImpl.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/impl/controller/DoubleFieldControllerBuilderImpl.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/impl/controller/DoubleSliderControllerBuilderImpl.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/impl/controller/EnumControllerBuilderImpl.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/impl/controller/FloatFieldControllerBuilderImpl.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/impl/controller/FloatSliderControllerBuilderImpl.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/impl/controller/IntegerFieldControllerBuilderImpl.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/impl/controller/IntegerSliderControllerBuilderImpl.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/impl/controller/LongFieldControllerBuilderImpl.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/impl/controller/LongSliderControllerBuilderImpl.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/impl/controller/StringControllerBuilderImpl.java
 create mode 100644 common/src/main/java/dev/isxander/yacl/impl/controller/TickBoxControllerBuilderImpl.java

(limited to 'common/src')

diff --git a/common/src/main/java/dev/isxander/yacl/api/ButtonOption.java b/common/src/main/java/dev/isxander/yacl/api/ButtonOption.java
index 88e1c4b..4acbe0e 100644
--- a/common/src/main/java/dev/isxander/yacl/api/ButtonOption.java
+++ b/common/src/main/java/dev/isxander/yacl/api/ButtonOption.java
@@ -15,7 +15,7 @@ public interface ButtonOption extends Option<BiConsumer<YACLScreen, ButtonOption
      */
     BiConsumer<YACLScreen, ButtonOption> action();
 
-    static dev.isxander.yacl.api.ButtonOption.Builder createBuilder() {
+    static Builder createBuilder() {
         return new ButtonOptionImpl.BuilderImpl();
     }
 
@@ -25,18 +25,11 @@ public interface ButtonOption extends Option<BiConsumer<YACLScreen, ButtonOption
          *
          * @see Option#name()
          */
-        dev.isxander.yacl.api.ButtonOption.Builder name(@NotNull Component name);
+        Builder name(@NotNull Component name);
 
-        /**
-         * Sets the tooltip to be used by the option.
-         * Can be invoked twice to append more lines.
-         * No need to wrap the text yourself, the gui does this itself.
-         *
-         * @param tooltips text lines - merged with a new-line on {@link Option.Builder#build()}.
-         */
-        dev.isxander.yacl.api.ButtonOption.Builder tooltip(@NotNull Component... tooltips);
+        Builder description(@NotNull OptionDescription description);
 
-        dev.isxander.yacl.api.ButtonOption.Builder action(@NotNull BiConsumer<YACLScreen, ButtonOption> action);
+        Builder action(@NotNull BiConsumer<YACLScreen, ButtonOption> action);
 
         /**
          * Action to be executed upon button press
@@ -44,22 +37,14 @@ public interface ButtonOption extends Option<BiConsumer<YACLScreen, ButtonOption
          * @see ButtonOption#action()
          */
         @Deprecated
-        dev.isxander.yacl.api.ButtonOption.Builder action(@NotNull Consumer<YACLScreen> action);
+        Builder action(@NotNull Consumer<YACLScreen> action);
 
         /**
          * Sets if the option can be configured
          *
          * @see Option#available()
          */
-        dev.isxander.yacl.api.ButtonOption.Builder available(boolean available);
-
-        /**
-         * Sets the controller for the option.
-         * This is how you interact and change the options.
-         *
-         * @see dev.isxander.yacl.gui.controllers
-         */
-        dev.isxander.yacl.api.ButtonOption.Builder controller(@NotNull Function<ButtonOption, Controller<BiConsumer<YACLScreen, ButtonOption>>> control);
+        Builder available(boolean available);
 
         ButtonOption build();
     }
diff --git a/common/src/main/java/dev/isxander/yacl/api/LabelOption.java b/common/src/main/java/dev/isxander/yacl/api/LabelOption.java
index 05c7214..f646c55 100644
--- a/common/src/main/java/dev/isxander/yacl/api/LabelOption.java
+++ b/common/src/main/java/dev/isxander/yacl/api/LabelOption.java
@@ -21,7 +21,7 @@ public interface LabelOption extends Option<Component> {
         return new LabelOptionImpl(label);
     }
 
-    static dev.isxander.yacl.api.LabelOption.Builder createBuilder() {
+    static Builder createBuilder() {
         return new LabelOptionImpl.BuilderImpl();
     }
 
@@ -29,12 +29,12 @@ public interface LabelOption extends Option<Component> {
         /**
          * Appends a line to the label
          */
-        dev.isxander.yacl.api.LabelOption.Builder line(@NotNull Component line);
+        Builder line(@NotNull Component line);
 
         /**
          * Appends multiple lines to the label
          */
-        dev.isxander.yacl.api.LabelOption.Builder lines(@NotNull Collection<? extends Component> lines);
+        Builder lines(@NotNull Collection<? extends Component> lines);
 
         LabelOption build();
     }
diff --git a/common/src/main/java/dev/isxander/yacl/api/ListOption.java b/common/src/main/java/dev/isxander/yacl/api/ListOption.java
index c30f359..19a365d 100644
--- a/common/src/main/java/dev/isxander/yacl/api/ListOption.java
+++ b/common/src/main/java/dev/isxander/yacl/api/ListOption.java
@@ -63,18 +63,6 @@ public interface ListOption<T> extends OptionGroup, Option<List<T>> {
 
         Builder<T> description(@NotNull OptionDescription description);
 
-        /**
-         * Sets the tooltip to be used by the list. It is displayed like a normal
-         * group when you hover over the name. Entries do not allow a tooltip.
-         * <p>
-         * Can be invoked twice to append more lines.
-         * No need to wrap the text yourself, the gui does this itself.
-         *
-         * @param tooltips text lines - merged with a new-line on {@link dev.isxander.yacl.api.ListOption.Builder#build()}.
-         */
-        @Deprecated
-        Builder<T> tooltip(@NotNull Component... tooltips);
-
         /**
          * Sets the value that is used when creating new entries
          */
diff --git a/common/src/main/java/dev/isxander/yacl/api/ListOptionEntry.java b/common/src/main/java/dev/isxander/yacl/api/ListOptionEntry.java
index e0a3424..2679fa3 100644
--- a/common/src/main/java/dev/isxander/yacl/api/ListOptionEntry.java
+++ b/common/src/main/java/dev/isxander/yacl/api/ListOptionEntry.java
@@ -6,11 +6,6 @@ import org.jetbrains.annotations.NotNull;
 public interface ListOptionEntry<T> extends Option<T> {
     ListOption<T> parentGroup();
 
-    @Override
-    default @NotNull Class<T> typeClass() {
-        return parentGroup().elementTypeClass();
-    }
-
     @Override
     default @NotNull ImmutableSet<OptionFlag> flags() {
         return parentGroup().flags();
diff --git a/common/src/main/java/dev/isxander/yacl/api/Option.java b/common/src/main/java/dev/isxander/yacl/api/Option.java
index f0b595d..df48a62 100644
--- a/common/src/main/java/dev/isxander/yacl/api/Option.java
+++ b/common/src/main/java/dev/isxander/yacl/api/Option.java
@@ -1,6 +1,7 @@
 package dev.isxander.yacl.api;
 
 import com.google.common.collect.ImmutableSet;
+import dev.isxander.yacl.api.controller.ControllerBuilder;
 import dev.isxander.yacl.impl.OptionImpl;
 import net.minecraft.network.chat.Component;
 import org.jetbrains.annotations.NotNull;
@@ -53,12 +54,6 @@ public interface Option<T> {
      */
     void setAvailable(boolean available);
 
-    /**
-     * Class of the option type.
-     * Used by some controllers.
-     */
-    @NotNull Class<T> typeClass();
-
     /**
      * Tasks that needs to be executed upon applying changes.
      */
@@ -111,14 +106,19 @@ public interface Option<T> {
      */
     void addListener(BiConsumer<Option<T>, T> changedListener);
 
+    static <T> Builder<T> createBuilder() {
+        return new OptionImpl.BuilderImpl<>();
+    }
+
     /**
      * Creates a builder to construct an {@link Option}
      *
      * @param <T> type of the option's value
      * @param typeClass used to capture the type
      */
+    @Deprecated
     static <T> Builder<T> createBuilder(Class<T> typeClass) {
-        return new OptionImpl.BuilderImpl<>(typeClass);
+        return createBuilder();
     }
 
     interface Builder<T> {
@@ -146,33 +146,7 @@ public interface Option<T> {
          */
         Builder<T> description(@NotNull Function<T, OptionDescription> descriptionFunction);
 
-        /**
-         * Sets the tooltip to be used by the option.
-         * No need to wrap the text yourself, the gui does this itself.
-         *
-         * @param tooltipGetter function to get tooltip depending on value {@link Builder#build()}.
-         */
-        @Deprecated
-        Builder<T> tooltip(@NotNull Function<T, Component> tooltipGetter);
-
-        /**
-         * Sets the tooltip to be used by the option.
-         * No need to wrap the text yourself, the gui does this itself.
-         *
-         * @param tooltipGetter function to get tooltip depending on value {@link Builder#build()}.
-         */
-        @Deprecated
-        Builder<T> tooltip(@NotNull Function<T, Component>... tooltipGetter);
-
-        /**
-         * Sets the tooltip to be used by the option.
-         * Can be invoked twice to append more lines.
-         * No need to wrap the text yourself, the gui does this itself.
-         *
-         * @param tooltips text lines - merged with a new-line on {@link Builder#build()}.
-         */
-        @Deprecated
-        Builder<T> tooltip(@NotNull Component... tooltips);
+        Builder<T> controller(@NotNull Function<Option<T>, ControllerBuilder<T>> controllerBuilder);
 
         /**
          * Sets the controller for the option.
@@ -180,7 +154,7 @@ public interface Option<T> {
          *
          * @see dev.isxander.yacl.gui.controllers
          */
-        Builder<T> controller(@NotNull Function<Option<T>, Controller<T>> control);
+        Builder<T> customController(@NotNull Function<Option<T>, Controller<T>> control);
 
         /**
          * Sets the binding for the option.
@@ -220,7 +194,7 @@ public interface Option<T> {
          * Upon applying changes, all flags are executed.
          * {@link Option#flags()}
          */
-        Builder<T> flags(@NotNull Collection<OptionFlag> flags);
+        Builder<T> flags(@NotNull Collection<? extends OptionFlag> flags);
 
         /**
          * Instantly invokes the binder's setter when modified in the GUI.
diff --git a/common/src/main/java/dev/isxander/yacl/api/OptionDescription.java b/common/src/main/java/dev/isxander/yacl/api/OptionDescription.java
index 0b91d5a..c233309 100644
--- a/common/src/main/java/dev/isxander/yacl/api/OptionDescription.java
+++ b/common/src/main/java/dev/isxander/yacl/api/OptionDescription.java
@@ -40,10 +40,12 @@ public interface OptionDescription {
         return new OptionDescriptionImpl.BuilderImpl();
     }
 
-    static OptionDescription empty() {
-        return new OptionDescriptionImpl(CommonComponents.EMPTY, CompletableFuture.completedFuture(Optional.empty()));
+    static OptionDescription of(Component... description) {
+        return createBuilder().description(description).build();
     }
 
+    OptionDescription EMPTY = new OptionDescriptionImpl(CommonComponents.EMPTY, CompletableFuture.completedFuture(Optional.empty()));
+
     interface Builder {
         /**
          * Appends lines to the main description of the option. This can be called multiple times.
diff --git a/common/src/main/java/dev/isxander/yacl/api/OptionGroup.java b/common/src/main/java/dev/isxander/yacl/api/OptionGroup.java
index f1b2b5a..e4a0eeb 100644
--- a/common/src/main/java/dev/isxander/yacl/api/OptionGroup.java
+++ b/common/src/main/java/dev/isxander/yacl/api/OptionGroup.java
@@ -60,16 +60,6 @@ public interface OptionGroup {
 
         Builder description(@NotNull OptionDescription description);
 
-        /**
-         * Sets the tooltip to be used by the option group.
-         * Can be invoked twice to append more lines.
-         * No need to wrap the Component yourself, the gui does this itself.
-         *
-         * @param tooltips Component lines - merged with a new-line on {@link Builder#build()}.
-         */
-        @Deprecated
-        Builder tooltip(@NotNull Component... tooltips);
-
         /**
          * Adds an option to group.
          * To construct an option, use {@link Option#createBuilder(Class)}
diff --git a/common/src/main/java/dev/isxander/yacl/api/controller/BooleanControllerBuilder.java b/common/src/main/java/dev/isxander/yacl/api/controller/BooleanControllerBuilder.java
new file mode 100644
index 0000000..3af91f0
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/api/controller/BooleanControllerBuilder.java
@@ -0,0 +1,16 @@
+package dev.isxander.yacl.api.controller;
+
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.impl.controller.BooleanControllerBuilderImpl;
+
+public interface BooleanControllerBuilder extends ValueFormattableController<Boolean, BooleanControllerBuilder> {
+    BooleanControllerBuilder coloured(boolean coloured);
+
+    BooleanControllerBuilder onOffFormatter();
+    BooleanControllerBuilder yesNoFormatter();
+    BooleanControllerBuilder trueFalseFormatter();
+
+    static BooleanControllerBuilder create(Option<Boolean> option) {
+        return new BooleanControllerBuilderImpl(option);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/api/controller/ColorControllerBuilder.java b/common/src/main/java/dev/isxander/yacl/api/controller/ColorControllerBuilder.java
new file mode 100644
index 0000000..c4c8f9e
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/api/controller/ColorControllerBuilder.java
@@ -0,0 +1,14 @@
+package dev.isxander.yacl.api.controller;
+
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.impl.controller.ColorControllerBuilderImpl;
+
+import java.awt.Color;
+
+public interface ColorControllerBuilder extends ControllerBuilder<Color> {
+    ColorControllerBuilder allowAlpha(boolean allowAlpha);
+
+    static ColorControllerBuilder create(Option<Color> option) {
+        return new ColorControllerBuilderImpl(option);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/api/controller/ControllerBuilder.java b/common/src/main/java/dev/isxander/yacl/api/controller/ControllerBuilder.java
new file mode 100644
index 0000000..378de08
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/api/controller/ControllerBuilder.java
@@ -0,0 +1,9 @@
+package dev.isxander.yacl.api.controller;
+
+import dev.isxander.yacl.api.Controller;
+import org.jetbrains.annotations.ApiStatus;
+
+public interface ControllerBuilder<T> {
+    @ApiStatus.Internal
+    Controller<T> build();
+}
diff --git a/common/src/main/java/dev/isxander/yacl/api/controller/CyclingListControllerBuilder.java b/common/src/main/java/dev/isxander/yacl/api/controller/CyclingListControllerBuilder.java
new file mode 100644
index 0000000..d6d6c83
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/api/controller/CyclingListControllerBuilder.java
@@ -0,0 +1,15 @@
+package dev.isxander.yacl.api.controller;
+
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.impl.controller.CyclingListControllerBuilderImpl;
+
+public interface CyclingListControllerBuilder<T> extends ValueFormattableController<T, CyclingListControllerBuilder<T>> {
+    @SuppressWarnings("unchecked")
+    CyclingListControllerBuilder<T> values(T... values);
+
+    CyclingListControllerBuilder<T> values(Iterable<? extends T> values);
+
+    static <T> CyclingListControllerBuilder<T> create(Option<T> option) {
+        return new CyclingListControllerBuilderImpl<>(option);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/api/controller/DoubleFieldControllerBuilder.java b/common/src/main/java/dev/isxander/yacl/api/controller/DoubleFieldControllerBuilder.java
new file mode 100644
index 0000000..23f6270
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/api/controller/DoubleFieldControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl.api.controller;
+
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.impl.controller.DoubleFieldControllerBuilderImpl;
+
+public interface DoubleFieldControllerBuilder extends NumberFieldControllerBuilder<Double, DoubleFieldControllerBuilder> {
+    static DoubleFieldControllerBuilder create(Option<Double> option) {
+        return new DoubleFieldControllerBuilderImpl(option);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/api/controller/DoubleSliderControllerBuilder.java b/common/src/main/java/dev/isxander/yacl/api/controller/DoubleSliderControllerBuilder.java
new file mode 100644
index 0000000..2f08cfd
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/api/controller/DoubleSliderControllerBuilder.java
@@ -0,0 +1,4 @@
+package dev.isxander.yacl.api.controller;
+
+public interface DoubleSliderControllerBuilder extends SliderControllerBuilder<Double, DoubleSliderControllerBuilder> {
+}
diff --git a/common/src/main/java/dev/isxander/yacl/api/controller/EnumControllerBuilder.java b/common/src/main/java/dev/isxander/yacl/api/controller/EnumControllerBuilder.java
new file mode 100644
index 0000000..f545f9c
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/api/controller/EnumControllerBuilder.java
@@ -0,0 +1,12 @@
+package dev.isxander.yacl.api.controller;
+
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.impl.controller.EnumControllerBuilderImpl;
+
+public interface EnumControllerBuilder<T extends Enum<T>> extends ValueFormattableController<T, EnumControllerBuilder<T>> {
+    EnumControllerBuilder<T> enumClass(Class<T> enumClass);
+
+    static <T extends Enum<T>> EnumControllerBuilder<T> create(Option<T> option) {
+        return new EnumControllerBuilderImpl<>(option);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/api/controller/FloatFieldControllerBuilder.java b/common/src/main/java/dev/isxander/yacl/api/controller/FloatFieldControllerBuilder.java
new file mode 100644
index 0000000..202da79
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/api/controller/FloatFieldControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl.api.controller;
+
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.impl.controller.FloatFieldControllerBuilderImpl;
+
+public interface FloatFieldControllerBuilder extends NumberFieldControllerBuilder<Float, FloatFieldControllerBuilder> {
+    static FloatFieldControllerBuilder create(Option<Float> option) {
+        return new FloatFieldControllerBuilderImpl(option);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/api/controller/FloatSliderControllerBuilder.java b/common/src/main/java/dev/isxander/yacl/api/controller/FloatSliderControllerBuilder.java
new file mode 100644
index 0000000..e4600f6
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/api/controller/FloatSliderControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl.api.controller;
+
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.impl.controller.FloatSliderControllerBuilderImpl;
+
+public interface FloatSliderControllerBuilder extends SliderControllerBuilder<Float, FloatSliderControllerBuilder> {
+    static FloatSliderControllerBuilder create(Option<Float> option) {
+        return new FloatSliderControllerBuilderImpl(option);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/api/controller/IntegerFieldControllerBuilder.java b/common/src/main/java/dev/isxander/yacl/api/controller/IntegerFieldControllerBuilder.java
new file mode 100644
index 0000000..d256cd2
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/api/controller/IntegerFieldControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl.api.controller;
+
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.impl.controller.IntegerFieldControllerBuilderImpl;
+
+public interface IntegerFieldControllerBuilder extends NumberFieldControllerBuilder<Integer, IntegerFieldControllerBuilder> {
+    static IntegerFieldControllerBuilder create(Option<Integer> option) {
+        return new IntegerFieldControllerBuilderImpl(option);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/api/controller/IntegerSliderControllerBuilder.java b/common/src/main/java/dev/isxander/yacl/api/controller/IntegerSliderControllerBuilder.java
new file mode 100644
index 0000000..6733a6e
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/api/controller/IntegerSliderControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl.api.controller;
+
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.impl.controller.IntegerSliderControllerBuilderImpl;
+
+public interface IntegerSliderControllerBuilder extends SliderControllerBuilder<Integer, IntegerSliderControllerBuilder> {
+    static IntegerSliderControllerBuilder create(Option<Integer> option) {
+        return new IntegerSliderControllerBuilderImpl(option);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/api/controller/LongFieldControllerBuilder.java b/common/src/main/java/dev/isxander/yacl/api/controller/LongFieldControllerBuilder.java
new file mode 100644
index 0000000..8e162ae
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/api/controller/LongFieldControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl.api.controller;
+
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.impl.controller.LongFieldControllerBuilderImpl;
+
+public interface LongFieldControllerBuilder extends NumberFieldControllerBuilder<Long, LongFieldControllerBuilder> {
+    static LongFieldControllerBuilder create(Option<Long> option) {
+        return new LongFieldControllerBuilderImpl(option);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/api/controller/LongSliderControllerBuilder.java b/common/src/main/java/dev/isxander/yacl/api/controller/LongSliderControllerBuilder.java
new file mode 100644
index 0000000..9624cd2
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/api/controller/LongSliderControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl.api.controller;
+
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.impl.controller.LongSliderControllerBuilderImpl;
+
+public interface LongSliderControllerBuilder extends SliderControllerBuilder<Long, LongSliderControllerBuilder> {
+    static LongSliderControllerBuilder create(Option<Long> option) {
+        return new LongSliderControllerBuilderImpl(option);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/api/controller/NumberFieldControllerBuilder.java b/common/src/main/java/dev/isxander/yacl/api/controller/NumberFieldControllerBuilder.java
new file mode 100644
index 0000000..a8e8f4d
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/api/controller/NumberFieldControllerBuilder.java
@@ -0,0 +1,7 @@
+package dev.isxander.yacl.api.controller;
+
+public interface NumberFieldControllerBuilder<T extends Number, B extends NumberFieldControllerBuilder<T, B>> extends ValueFormattableController<T, B> {
+    B min(T min);
+    B max(T max);
+    B range(T min, T max);
+}
diff --git a/common/src/main/java/dev/isxander/yacl/api/controller/SliderControllerBuilder.java b/common/src/main/java/dev/isxander/yacl/api/controller/SliderControllerBuilder.java
new file mode 100644
index 0000000..b175014
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/api/controller/SliderControllerBuilder.java
@@ -0,0 +1,6 @@
+package dev.isxander.yacl.api.controller;
+
+public interface SliderControllerBuilder<T extends Number, B extends SliderControllerBuilder<T, B>> extends ValueFormattableController<T, B> {
+    B range(T min, T max);
+    B step(T step);
+}
diff --git a/common/src/main/java/dev/isxander/yacl/api/controller/StringControllerBuilder.java b/common/src/main/java/dev/isxander/yacl/api/controller/StringControllerBuilder.java
new file mode 100644
index 0000000..fcb3001
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/api/controller/StringControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl.api.controller;
+
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.impl.controller.StringControllerBuilderImpl;
+
+public interface StringControllerBuilder extends ControllerBuilder<String> {
+    static StringControllerBuilder create(Option<String> option) {
+        return new StringControllerBuilderImpl(option);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/api/controller/TickBoxControllerBuilder.java b/common/src/main/java/dev/isxander/yacl/api/controller/TickBoxControllerBuilder.java
new file mode 100644
index 0000000..6dd52b9
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/api/controller/TickBoxControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl.api.controller;
+
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.impl.controller.TickBoxControllerBuilderImpl;
+
+public interface TickBoxControllerBuilder extends ControllerBuilder<Boolean> {
+    static TickBoxControllerBuilder create(Option<Boolean> option) {
+        return new TickBoxControllerBuilderImpl(option);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/api/controller/ValueFormattableController.java b/common/src/main/java/dev/isxander/yacl/api/controller/ValueFormattableController.java
new file mode 100644
index 0000000..032b34e
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/api/controller/ValueFormattableController.java
@@ -0,0 +1,9 @@
+package dev.isxander.yacl.api.controller;
+
+import net.minecraft.network.chat.Component;
+
+import java.util.function.Function;
+
+public interface ValueFormattableController<T, B extends ValueFormattableController<T, B>> extends ControllerBuilder<T> {
+    B valueFormatter(Function<T, Component> formatter);
+}
diff --git a/common/src/main/java/dev/isxander/yacl/gui/controllers/cycling/CyclingListController.java b/common/src/main/java/dev/isxander/yacl/gui/controllers/cycling/CyclingListController.java
index 34f2cc9..43fa766 100644
--- a/common/src/main/java/dev/isxander/yacl/gui/controllers/cycling/CyclingListController.java
+++ b/common/src/main/java/dev/isxander/yacl/gui/controllers/cycling/CyclingListController.java
@@ -21,7 +21,7 @@ public class CyclingListController<T> implements ICyclingController<T> {
      * @param option option of which to bind the controller to
      * @param values the values to cycle through
      */
-    public CyclingListController(Option<T> option, Iterable<T> values) {
+    public CyclingListController(Option<T> option, Iterable<? extends T> values) {
         this(option, values, value -> Component.literal(value.toString()));
     }
 
@@ -31,7 +31,7 @@ public class CyclingListController<T> implements ICyclingController<T> {
      * @param values the values to cycle through
      * @param valueFormatter function of how to convert each value to a string to display
      */
-    public CyclingListController(Option<T> option, Iterable<T> values, Function<T, Component> valueFormatter) {
+    public CyclingListController(Option<T> option, Iterable<? extends T> values, Function<T, Component> valueFormatter) {
         this.option = option;
         this.valueFormatter = valueFormatter;
         this.values = ImmutableList.copyOf(values);
diff --git a/common/src/main/java/dev/isxander/yacl/gui/controllers/cycling/EnumController.java b/common/src/main/java/dev/isxander/yacl/gui/controllers/cycling/EnumController.java
index ebd2cb6..281f182 100644
--- a/common/src/main/java/dev/isxander/yacl/gui/controllers/cycling/EnumController.java
+++ b/common/src/main/java/dev/isxander/yacl/gui/controllers/cycling/EnumController.java
@@ -26,25 +26,8 @@ public class EnumController<T extends Enum<T>> extends CyclingListController<T>
         };
     }
 
-    /**
-     * Constructs a cycling enum controller with a default value formatter and all values being available.
-     * The default value formatter first searches if the
-     * enum is a {@link NameableEnum} or {@link OptionEnum} else, just uses {@link Enum#toString()}
-     *
-     * @param option bound option
-     */
-    public EnumController(Option<T> option) {
-        this(option, getDefaultFormatter());
-    }
-
-    /**
-     * Constructs a cycling enum controller with all values being available.
-     *
-     * @param option bound option
-     * @param valueFormatter format the enum into any {@link Component}
-     */
-    public EnumController(Option<T> option, Function<T, Component> valueFormatter) {
-        this(option, valueFormatter, option.typeClass().getEnumConstants());
+    public EnumController(Option<T> option, Class<T> enumClass) {
+        this(option, getDefaultFormatter(), enumClass.getEnumConstants());
     }
 
     /**
diff --git a/common/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java b/common/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java
index afb2cb5..c63dd99 100644
--- a/common/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java
+++ b/common/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java
@@ -3,6 +3,7 @@ package dev.isxander.yacl.impl;
 import com.google.common.collect.ImmutableSet;
 import dev.isxander.yacl.api.*;
 import dev.isxander.yacl.gui.YACLScreen;
+import dev.isxander.yacl.gui.controllers.ActionController;
 import net.minecraft.network.chat.Component;
 import net.minecraft.network.chat.MutableComponent;
 import org.apache.commons.lang3.Validate;
@@ -29,14 +30,13 @@ public final class ButtonOptionImpl implements ButtonOption {
             @NotNull Component name,
             @Nullable OptionDescription description,
             @NotNull BiConsumer<YACLScreen, ButtonOption> action,
-            boolean available,
-            @NotNull Function<ButtonOption, Controller<BiConsumer<YACLScreen, ButtonOption>>> controlGetter
+            boolean available
     ) {
         this.name = name;
         this.description = description;
         this.action = action;
         this.available = available;
-        this.controller = controlGetter.apply(this);
+        this.controller = new ActionController(this);
         this.binding = new EmptyBinderImpl();
     }
 
@@ -80,11 +80,6 @@ public final class ButtonOptionImpl implements ButtonOption {
         return binding;
     }
 
-    @Override
-    public @NotNull Class<BiConsumer<YACLScreen, ButtonOption>> typeClass() {
-        throw new UnsupportedOperationException();
-    }
-
     @Override
     public @NotNull ImmutableSet<OptionFlag> flags() {
         return ImmutableSet.of();
@@ -150,9 +145,8 @@ public final class ButtonOptionImpl implements ButtonOption {
     @ApiStatus.Internal
     public static final class BuilderImpl implements Builder {
         private Component name;
-        private final List<Component> tooltipLines = new ArrayList<>();
+        private OptionDescription description = OptionDescription.EMPTY;
         private boolean available = true;
-        private Function<ButtonOption, Controller<BiConsumer<YACLScreen, ButtonOption>>> controlGetter;
         private BiConsumer<YACLScreen, ButtonOption> action;
 
         @Override
@@ -164,10 +158,10 @@ public final class ButtonOptionImpl implements ButtonOption {
         }
 
         @Override
-        public Builder tooltip(@NotNull Component... tooltips) {
-            Validate.notNull(tooltips, "`tooltips` cannot be empty");
+        public Builder description(@NotNull OptionDescription description) {
+            Validate.notNull(description, "`description` cannot be null");
 
-            //tooltipLines.addAll(List.of(tooltips));
+            this.description = description;
             return this;
         }
 
@@ -194,30 +188,12 @@ public final class ButtonOptionImpl implements ButtonOption {
             return this;
         }
 
-        @Override
-        public Builder controller(@NotNull Function<ButtonOption, Controller<BiConsumer<YACLScreen, ButtonOption>>> control) {
-            Validate.notNull(control, "`control` cannot be null");
-
-            this.controlGetter = control;
-            return this;
-        }
-
         @Override
         public ButtonOption build() {
-            Validate.notNull(name, "`name` must not be null when building `Option`");
-            Validate.notNull(controlGetter, "`control` must not be null when building `Option`");
-            Validate.notNull(action, "`action` must not be null when building `Option`");
-
-            MutableComponent concatenatedTooltip = Component.empty();
-            boolean first = true;
-            for (Component line : tooltipLines) {
-                if (!first) concatenatedTooltip.append("\n");
-                first = false;
-
-                concatenatedTooltip.append(line);
-            }
+            Validate.notNull(name, "`name` must not be null when building `ButtonOption`");
+            Validate.notNull(action, "`action` must not be null when building `ButtonOption`");
 
-            return new ButtonOptionImpl(name, OptionDescription.createBuilder().description(concatenatedTooltip).build(), action, available, controlGetter);
+            return new ButtonOptionImpl(name, description, action, available);
         }
     }
 }
diff --git a/common/src/main/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java b/common/src/main/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java
index e593ce4..0690cda 100644
--- a/common/src/main/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java
+++ b/common/src/main/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java
@@ -3,6 +3,7 @@ package dev.isxander.yacl.impl;
 import com.google.common.collect.ImmutableList;
 import dev.isxander.yacl.api.*;
 import dev.isxander.yacl.impl.utils.YACLConstants;
+import net.minecraft.network.chat.CommonComponents;
 import net.minecraft.network.chat.Component;
 import net.minecraft.network.chat.ComponentContents;
 import net.minecraft.network.chat.MutableComponent;
@@ -111,7 +112,7 @@ public final class ConfigCategoryImpl implements ConfigCategory {
             Validate.notNull(name, "`name` must not be null to build `ConfigCategory`");
 
             List<OptionGroup> combinedGroups = new ArrayList<>();
-            combinedGroups.add(new OptionGroupImpl(Component.empty(), OptionDescription.empty(), ImmutableList.copyOf(rootOptions), false, true));
+            combinedGroups.add(new OptionGroupImpl(CommonComponents.EMPTY, OptionDescription.EMPTY, ImmutableList.copyOf(rootOptions), false, true));
             combinedGroups.addAll(groups);
 
             Validate.notEmpty(combinedGroups, "at least one option must be added to build `ConfigCategory`");
diff --git a/common/src/main/java/dev/isxander/yacl/impl/LabelOptionImpl.java b/common/src/main/java/dev/isxander/yacl/impl/LabelOptionImpl.java
index a32121d..c1a6772 100644
--- a/common/src/main/java/dev/isxander/yacl/impl/LabelOptionImpl.java
+++ b/common/src/main/java/dev/isxander/yacl/impl/LabelOptionImpl.java
@@ -73,11 +73,6 @@ public final class LabelOptionImpl implements LabelOption {
         throw new UnsupportedOperationException("Label options cannot be disabled.");
     }
 
-    @Override
-    public @NotNull Class<Component> typeClass() {
-        return Component.class;
-    }
-
     @Override
     public @NotNull ImmutableSet<OptionFlag> flags() {
         return ImmutableSet.of();
diff --git a/common/src/main/java/dev/isxander/yacl/impl/ListOptionImpl.java b/common/src/main/java/dev/isxander/yacl/impl/ListOptionImpl.java
index c21e733..4b37e9e 100644
--- a/common/src/main/java/dev/isxander/yacl/impl/ListOptionImpl.java
+++ b/common/src/main/java/dev/isxander/yacl/impl/ListOptionImpl.java
@@ -78,11 +78,6 @@ public final class ListOptionImpl<T> implements ListOption<T> {
         return binding;
     }
 
-    @Override
-    public @NotNull Class<List<T>> typeClass() {
-        throw new UnsupportedOperationException();
-    }
-
     @Override
     public @NotNull Class<T> elementTypeClass() {
         return typeClass;
@@ -218,8 +213,7 @@ public final class ListOptionImpl<T> implements ListOption<T> {
     @ApiStatus.Internal
     public static final class BuilderImpl<T> implements Builder<T> {
         private Component name = Component.empty();
-        private OptionDescription description = null;
-        private OptionDescription.Builder legacyBuilder = null;
+        private OptionDescription description = OptionDescription.EMPTY;
         private Function<ListOptionEntry<T>, Controller<T>> controllerFunction;
         private Binding<List<T>> binding = null;
         private final Set<OptionFlag> flags = new HashSet<>();
@@ -243,24 +237,12 @@ public final class ListOptionImpl<T> implements ListOption<T> {
 
         @Override
         public Builder<T> description(@NotNull OptionDescription description) {
-            Validate.isTrue(legacyBuilder == null, "Cannot set description when deprecated `tooltip` method is used");
             Validate.notNull(description, "`description` must not be null");
 
             this.description = description;
             return this;
         }
 
-        @Override
-        public Builder<T> tooltip(@NotNull Component... tooltips) {
-            Validate.isTrue(description == null, "Cannot use deprecated `tooltip` method when `description` in use.");
-            Validate.notEmpty(tooltips, "`tooltips` cannot be empty");
-
-            ensureLegacyDescriptionBuilder();
-
-            legacyBuilder.description(tooltips);
-            return this;
-        }
-
         @Override
         public Builder<T> initial(@NotNull T initialValue) {
             Validate.notNull(initialValue, "`initialValue` cannot be empty");
@@ -341,23 +323,7 @@ public final class ListOptionImpl<T> implements ListOption<T> {
             Validate.notNull(binding, "`binding` must not be null");
             Validate.notNull(initialValue, "`initialValue` must not be null");
 
-            if (description == null) {
-                if (ensureLegacyDescriptionBuilder())
-                    YACLConstants.LOGGER.warn("Using deprecated `tooltip` method in list option {}. Use `description` instead.", name.getString());
-
-                description = legacyBuilder.build();
-            }
-
             return new ListOptionImpl<>(name, description, binding, initialValue, typeClass, controllerFunction, ImmutableSet.copyOf(flags), collapsed, available, listeners);
         }
-
-        private boolean ensureLegacyDescriptionBuilder() {
-            if (legacyBuilder == null) {
-                legacyBuilder = OptionDescription.createBuilder();
-                return false;
-            } else {
-                return true;
-            }
-        }
     }
 }
diff --git a/common/src/main/java/dev/isxander/yacl/impl/OptionGroupImpl.java b/common/src/main/java/dev/isxander/yacl/impl/OptionGroupImpl.java
index 2db8acd..37eddfd 100644
--- a/common/src/main/java/dev/isxander/yacl/impl/OptionGroupImpl.java
+++ b/common/src/main/java/dev/isxander/yacl/impl/OptionGroupImpl.java
@@ -64,8 +64,7 @@ public final class OptionGroupImpl implements OptionGroup {
     @ApiStatus.Internal
     public static final class BuilderImpl implements Builder {
         private Component name = Component.empty();
-        private OptionDescription description = null;
-        private OptionDescription.Builder legacyBuilder = null;
+        private OptionDescription description = OptionDescription.EMPTY;
         private final List<Option<?>> options = new ArrayList<>();
         private boolean collapsed = false;
 
@@ -79,24 +78,12 @@ public final class OptionGroupImpl implements OptionGroup {
 
         @Override
         public Builder description(@NotNull OptionDescription description) {
-            Validate.isTrue(legacyBuilder == null, "Cannot set description when deprecated `tooltip` method is used");
             Validate.notNull(description, "`description` must not be null");
 
             this.description = description;
             return this;
         }
 
-        @Override
-        public Builder tooltip(@NotNull Component... tooltips) {
-            Validate.isTrue(description == null, "Cannot use deprecated `tooltip` method when `description` in use.");
-            Validate.notEmpty(tooltips, "`tooltips` cannot be empty");
-
-            ensureLegacyDescriptionBuilder();
-
-            legacyBuilder.description(tooltips);
-            return this;
-        }
-
         @Override
         public Builder option(@NotNull Option<?> option) {
             Validate.notNull(option, "`option` must not be null");
@@ -129,23 +116,7 @@ public final class OptionGroupImpl implements OptionGroup {
         public OptionGroup build() {
             Validate.notEmpty(options, "`options` must not be empty to build `OptionGroup`");
 
-            if (description == null) {
-                if (ensureLegacyDescriptionBuilder())
-                    YACLConstants.LOGGER.warn("Using deprecated `tooltip` method in option group '{}'. Use `description` instead.", name != null ? name.getString() : "unnamed group");
-
-                description = legacyBuilder.build();
-            }
-
             return new OptionGroupImpl(name, description, ImmutableList.copyOf(options), collapsed, false);
         }
-
-        private boolean ensureLegacyDescriptionBuilder() {
-            if (legacyBuilder == null) {
-                legacyBuilder = OptionDescription.createBuilder();
-                return false;
-            } else {
-                return true;
-            }
-        }
     }
 }
diff --git a/common/src/main/java/dev/isxander/yacl/impl/OptionImpl.java b/common/src/main/java/dev/isxander/yacl/impl/OptionImpl.java
index ebb1226..29ca48d 100644
--- a/common/src/main/java/dev/isxander/yacl/impl/OptionImpl.java
+++ b/common/src/main/java/dev/isxander/yacl/impl/OptionImpl.java
@@ -2,6 +2,7 @@ package dev.isxander.yacl.impl;
 
 import com.google.common.collect.ImmutableSet;
 import dev.isxander.yacl.api.*;
+import dev.isxander.yacl.api.controller.ControllerBuilder;
 import dev.isxander.yacl.impl.utils.YACLConstants;
 import net.minecraft.ChatFormatting;
 import net.minecraft.network.chat.Component;
@@ -28,8 +29,6 @@ public final class OptionImpl<T> implements Option<T> {
 
     private final ImmutableSet<OptionFlag> flags;
 
-    private final Class<T> typeClass;
-
     private T pendingValue;
 
     private final List<BiConsumer<Option<T>, T>> listeners;
@@ -41,14 +40,12 @@ public final class OptionImpl<T> implements Option<T> {
             @NotNull Binding<T> binding,
             boolean available,
             ImmutableSet<OptionFlag> flags,
-            @NotNull Class<T> typeClass,
             @NotNull Collection<BiConsumer<Option<T>, T>> listeners
     ) {
         this.name = name;
         this.binding = binding;
         this.available = available;
         this.flags = flags;
-        this.typeClass = typeClass;
         this.listeners = new ArrayList<>(listeners);
         this.controller = controlGetter.apply(this);
 
@@ -92,11 +89,6 @@ public final class OptionImpl<T> implements Option<T> {
         this.available = available;
     }
 
-    @Override
-    public @NotNull Class<T> typeClass() {
-        return typeClass;
-    }
-
     @Override
     public @NotNull ImmutableSet<OptionFlag> flags() {
         return flags;
@@ -151,8 +143,7 @@ public final class OptionImpl<T> implements Option<T> {
     public static class BuilderImpl<T> implements Builder<T> {
         private Component name = Component.literal("Name not specified!").withStyle(ChatFormatting.RED);
 
-        private Function<T, OptionDescription> descriptionFunction = null;
-        private final List<Function<T, Component>> tooltipGetters = new ArrayList<>();
+        private Function<T, OptionDescription> descriptionFunction = pending -> OptionDescription.EMPTY;
 
         private Function<Option<T>, Controller<T>> controlGetter;
 
@@ -164,14 +155,8 @@ public final class OptionImpl<T> implements Option<T> {
 
         private final Set<OptionFlag> flags = new HashSet<>();
 
-        private final Class<T> typeClass;
-
         private final List<BiConsumer<Option<T>, T>> listeners = new ArrayList<>();
 
-        public BuilderImpl(Class<T> typeClass) {
-            this.typeClass = typeClass;
-        }
-
         @Override
         public Builder<T> name(@NotNull Component name) {
             Validate.notNull(name, "`name` cannot be null");
@@ -192,35 +177,14 @@ public final class OptionImpl<T> implements Option<T> {
         }
 
         @Override
-        public Builder<T> tooltip(@NotNull Function<T, Component> tooltipGetter) {
-            Validate.notNull(tooltipGetter, "`tooltipGetter` cannot be null");
-
-            this.tooltipGetters.add(tooltipGetter);
-            return this;
-        }
-
-        @Override
-        @SafeVarargs
-        @Deprecated
-        public final Builder<T> tooltip(@NotNull Function<T, Component>... tooltipGetter) {
-            Validate.notNull(tooltipGetter, "`tooltipGetter` cannot be null");
+        public Builder<T> controller(@NotNull Function<Option<T>, ControllerBuilder<T>> controllerBuilder) {
+            Validate.notNull(controllerBuilder, "`controllerBuilder` cannot be null");
 
-            this.tooltipGetters.addAll(List.of(tooltipGetter));
-            return this;
-        }
-
-        @Override
-        public Builder<T> tooltip(@NotNull Component... tooltips) {
-            var tooltipFunctions = Arrays.stream(tooltips)
-                    .map(t -> (Function<T, Component>) opt -> t)
-                    .toList();
-
-            this.tooltipGetters.addAll(tooltipFunctions);
-            return this;
+            return customController(opt -> controllerBuilder.apply(opt).build());
         }
 
         @Override
-        public Builder<T> controller(@NotNull Function<Option<T>, Controller<T>> control) {
+        public Builder<T> customController(@NotNull Function<Option<T>, Controller<T>> control) {
             Validate.notNull(control, "`control` cannot be null");
 
             this.controlGetter = control;
@@ -260,7 +224,7 @@ public final class OptionImpl<T> implements Option<T> {
         }
 
         @Override
-        public Builder<T> flags(@NotNull Collection<OptionFlag> flags) {
+        public Builder<T> flags(@NotNull Collection<? extends OptionFlag> flags) {
             Validate.notNull(flags, "`flags` must not be null");
 
             this.flags.addAll(flags);
@@ -291,31 +255,7 @@ public final class OptionImpl<T> implements Option<T> {
             Validate.notNull(binding, "`binding` must not be null when building `Option`");
             Validate.isTrue(!instant || flags.isEmpty(), "instant application does not support option flags");
 
-            if (descriptionFunction == null) {
-                if (!tooltipGetters.isEmpty())
-                    YACLConstants.LOGGER.warn("Using deprecated `tooltip` method in option '{}'. Use `description` instead.", name.getString());
-
-                Function<T, Component> concatenatedTooltipGetter = value -> {
-                    MutableComponent concatenatedTooltip = Component.empty();
-                    boolean first = true;
-                    for (Function<T, Component> line : tooltipGetters) {
-                        Component lineComponent = line.apply(value);
-
-                        if (lineComponent.getContents() == ComponentContents.EMPTY)
-                            continue;
-
-                        if (!first) concatenatedTooltip.append("\n");
-                        first = false;
-
-                        concatenatedTooltip.append(lineComponent);
-                    }
-
-                    return concatenatedTooltip;
-                };
-                descriptionFunction = opt -> OptionDescription.createBuilder().description(concatenatedTooltipGetter.apply(opt)).build();
-            }
-
-            return new OptionImpl<>(name, descriptionFunction, controlGetter, binding, available, ImmutableSet.copyOf(flags), typeClass, listeners);
+            return new OptionImpl<>(name, descriptionFunction, controlGetter, binding, available, ImmutableSet.copyOf(flags), listeners);
         }
     }
 }
diff --git a/common/src/main/java/dev/isxander/yacl/impl/controller/AbstractControllerBuilderImpl.java b/common/src/main/java/dev/isxander/yacl/impl/controller/AbstractControllerBuilderImpl.java
new file mode 100644
index 0000000..206ab29
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/impl/controller/AbstractControllerBuilderImpl.java
@@ -0,0 +1,12 @@
+package dev.isxander.yacl.impl.controller;
+
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.api.controller.ControllerBuilder;
+
+public abstract class AbstractControllerBuilderImpl<T> implements ControllerBuilder<T> {
+    protected final Option<T> option;
+
+    protected AbstractControllerBuilderImpl(Option<T> option) {
+        this.option = option;
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/impl/controller/BooleanControllerBuilderImpl.java b/common/src/main/java/dev/isxander/yacl/impl/controller/BooleanControllerBuilderImpl.java
new file mode 100644
index 0000000..99c5ffc
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/impl/controller/BooleanControllerBuilderImpl.java
@@ -0,0 +1,56 @@
+package dev.isxander.yacl.impl.controller;
+
+import dev.isxander.yacl.api.Controller;
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.api.controller.BooleanControllerBuilder;
+import dev.isxander.yacl.gui.controllers.BooleanController;
+import net.minecraft.network.chat.Component;
+import org.apache.commons.lang3.Validate;
+
+import java.util.function.Function;
+
+public class BooleanControllerBuilderImpl extends AbstractControllerBuilderImpl<Boolean> implements BooleanControllerBuilder {
+    private boolean coloured = false;
+    private Function<Boolean, Component> formatter = BooleanController.ON_OFF_FORMATTER;
+
+    public BooleanControllerBuilderImpl(Option<Boolean> option) {
+        super(option);
+    }
+
+    @Override
+    public BooleanControllerBuilder coloured(boolean coloured) {
+        this.coloured = coloured;
+        return this;
+    }
+
+    @Override
+    public BooleanControllerBuilder valueFormatter(Function<Boolean, Component> formatter) {
+        Validate.notNull(formatter);
+
+        this.formatter = formatter;
+        return this;
+    }
+
+    @Override
+    public BooleanControllerBuilder onOffFormatter() {
+        this.formatter = BooleanController.ON_OFF_FORMATTER;
+        return this;
+    }
+
+    @Override
+    public BooleanControllerBuilder yesNoFormatter() {
+        this.formatter = BooleanController.YES_NO_FORMATTER;
+        return this;
+    }
+
+    @Override
+    public BooleanControllerBuilder trueFalseFormatter() {
+        this.formatter = BooleanController.TRUE_FALSE_FORMATTER;
+        return this;
+    }
+
+    @Override
+    public Controller<Boolean> build() {
+        return new BooleanController(option, formatter, coloured);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/impl/controller/ColorControllerBuilderImpl.java b/common/src/main/java/dev/isxander/yacl/impl/controller/ColorControllerBuilderImpl.java
new file mode 100644
index 0000000..8b7851b
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/impl/controller/ColorControllerBuilderImpl.java
@@ -0,0 +1,27 @@
+package dev.isxander.yacl.impl.controller;
+
+import dev.isxander.yacl.api.Controller;
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.api.controller.ColorControllerBuilder;
+import dev.isxander.yacl.gui.controllers.ColorController;
+
+import java.awt.Color;
+
+public class ColorControllerBuilderImpl extends AbstractControllerBuilderImpl<Color> implements ColorControllerBuilder {
+    private boolean allowAlpha = false;
+
+    public ColorControllerBuilderImpl(Option<Color> option) {
+        super(option);
+    }
+
+    @Override
+    public ColorControllerBuilder allowAlpha(boolean allowAlpha) {
+        this.allowAlpha = allowAlpha;
+        return this;
+    }
+
+    @Override
+    public Controller<Color> build() {
+        return new ColorController(option, allowAlpha);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/impl/controller/CyclingListControllerBuilderImpl.java b/common/src/main/java/dev/isxander/yacl/impl/controller/CyclingListControllerBuilderImpl.java
new file mode 100644
index 0000000..1af556c
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/impl/controller/CyclingListControllerBuilderImpl.java
@@ -0,0 +1,43 @@
+package dev.isxander.yacl.impl.controller;
+
+import com.google.common.collect.ImmutableList;
+import dev.isxander.yacl.api.Controller;
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.api.controller.CyclingListControllerBuilder;
+import dev.isxander.yacl.gui.controllers.cycling.CyclingListController;
+import net.minecraft.network.chat.Component;
+
+import java.util.function.Function;
+
+public final class CyclingListControllerBuilderImpl<T> extends AbstractControllerBuilderImpl<T> implements CyclingListControllerBuilder<T> {
+    private Iterable<? extends T> values;
+    private Function<T, Component> formatter = null;
+
+    public CyclingListControllerBuilderImpl(Option<T> option) {
+        super(option);
+    }
+
+    @Override
+    public CyclingListControllerBuilder<T> values(Iterable<? extends T> values) {
+        this.values = values;
+        return this;
+    }
+
+    @SafeVarargs
+    @Override
+    public final CyclingListControllerBuilder<T> values(T... values) {
+        this.values = ImmutableList.copyOf(values);
+        return this;
+    }
+
+    @Override
+    public CyclingListControllerBuilder<T> valueFormatter(Function<T, Component> formatter) {
+        this.formatter = formatter;
+        return this;
+    }
+
+    @Override
+    public Controller<T> build() {
+        return new CyclingListController<>(option, values, formatter);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/impl/controller/DoubleFieldControllerBuilderImpl.java b/common/src/main/java/dev/isxander/yacl/impl/controller/DoubleFieldControllerBuilderImpl.java
new file mode 100644
index 0000000..e22e08b
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/impl/controller/DoubleFieldControllerBuilderImpl.java
@@ -0,0 +1,50 @@
+package dev.isxander.yacl.impl.controller;
+
+import dev.isxander.yacl.api.Controller;
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.api.controller.DoubleFieldControllerBuilder;
+import dev.isxander.yacl.gui.controllers.slider.DoubleSliderController;
+import dev.isxander.yacl.gui.controllers.string.number.DoubleFieldController;
+import net.minecraft.network.chat.Component;
+
+import java.util.function.Function;
+
+public class DoubleFieldControllerBuilderImpl extends AbstractControllerBuilderImpl<Double> implements DoubleFieldControllerBuilder {
+    private double min = Double.MIN_VALUE;
+    private double max = Double.MAX_VALUE;
+    private Function<Double, Component> formatter = DoubleSliderController.DEFAULT_FORMATTER;
+
+    public DoubleFieldControllerBuilderImpl(Option<Double> option) {
+        super(option);
+    }
+
+    @Override
+    public DoubleFieldControllerBuilder min(Double min) {
+        this.min = min;
+        return this;
+    }
+
+    @Override
+    public DoubleFieldControllerBuilder max(Double max) {
+        this.max = max;
+        return this;
+    }
+
+    @Override
+    public DoubleFieldControllerBuilder range(Double min, Double max) {
+        this.min = min;
+        this.max = max;
+        return this;
+    }
+
+    @Override
+    public DoubleFieldControllerBuilder valueFormatter(Function<Double, Component> formatter) {
+        this.formatter = formatter;
+        return this;
+    }
+
+    @Override
+    public Controller<Double> build() {
+        return new DoubleFieldController(option, min, max, formatter);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/impl/controller/DoubleSliderControllerBuilderImpl.java b/common/src/main/java/dev/isxander/yacl/impl/controller/DoubleSliderControllerBuilderImpl.java
new file mode 100644
index 0000000..1c3a664
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/impl/controller/DoubleSliderControllerBuilderImpl.java
@@ -0,0 +1,43 @@
+package dev.isxander.yacl.impl.controller;
+
+import dev.isxander.yacl.api.Controller;
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.api.controller.DoubleSliderControllerBuilder;
+import dev.isxander.yacl.gui.controllers.slider.DoubleSliderController;
+import net.minecraft.network.chat.Component;
+
+import java.util.function.Function;
+
+public class DoubleSliderControllerBuilderImpl extends AbstractControllerBuilderImpl<Double> implements DoubleSliderControllerBuilder {
+    private double min, max;
+    private double step;
+    private Function<Double, Component> formatter = DoubleSliderController.DEFAULT_FORMATTER;
+
+    public DoubleSliderControllerBuilderImpl(Option<Double> option) {
+        super(option);
+    }
+
+    @Override
+    public DoubleSliderControllerBuilder range(Double min, Double max) {
+        this.min = min;
+        this.max = max;
+        return this;
+    }
+
+    @Override
+    public DoubleSliderControllerBuilder step(Double step) {
+        this.step = step;
+        return this;
+    }
+
+    @Override
+    public DoubleSliderControllerBuilder valueFormatter(Function<Double, Component> formatter) {
+        this.formatter = formatter;
+        return this;
+    }
+
+    @Override
+    public Controller<Double> build() {
+        return new DoubleSliderController(option, min, max, step, formatter);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/impl/controller/EnumControllerBuilderImpl.java b/common/src/main/java/dev/isxander/yacl/impl/controller/EnumControllerBuilderImpl.java
new file mode 100644
index 0000000..79016b5
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/impl/controller/EnumControllerBuilderImpl.java
@@ -0,0 +1,35 @@
+package dev.isxander.yacl.impl.controller;
+
+import dev.isxander.yacl.api.Controller;
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.api.controller.EnumControllerBuilder;
+import dev.isxander.yacl.gui.controllers.cycling.EnumController;
+import net.minecraft.network.chat.Component;
+
+import java.util.function.Function;
+
+public class EnumControllerBuilderImpl<T extends Enum<T>> extends AbstractControllerBuilderImpl<T> implements EnumControllerBuilder<T> {
+    private Class<T> enumClass;
+    private Function<T, Component> formatter = EnumController.getDefaultFormatter();
+
+    public EnumControllerBuilderImpl(Option<T> option) {
+        super(option);
+    }
+
+    @Override
+    public EnumControllerBuilder<T> enumClass(Class<T> enumClass) {
+        this.enumClass = enumClass;
+        return this;
+    }
+
+    @Override
+    public EnumControllerBuilder<T> valueFormatter(Function<T, Component> formatter) {
+        this.formatter = formatter;
+        return this;
+    }
+
+    @Override
+    public Controller<T> build() {
+        return new EnumController<>(option, formatter, enumClass.getEnumConstants());
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/impl/controller/FloatFieldControllerBuilderImpl.java b/common/src/main/java/dev/isxander/yacl/impl/controller/FloatFieldControllerBuilderImpl.java
new file mode 100644
index 0000000..22dbf1a
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/impl/controller/FloatFieldControllerBuilderImpl.java
@@ -0,0 +1,50 @@
+package dev.isxander.yacl.impl.controller;
+
+import dev.isxander.yacl.api.Controller;
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.api.controller.FloatFieldControllerBuilder;
+import dev.isxander.yacl.gui.controllers.slider.FloatSliderController;
+import dev.isxander.yacl.gui.controllers.string.number.FloatFieldController;
+import net.minecraft.network.chat.Component;
+
+import java.util.function.Function;
+
+public class FloatFieldControllerBuilderImpl extends AbstractControllerBuilderImpl<Float> implements FloatFieldControllerBuilder {
+    private float min = Float.MIN_VALUE;
+    private float max = Float.MAX_VALUE;
+    private Function<Float, Component> formatter = FloatSliderController.DEFAULT_FORMATTER;
+
+    public FloatFieldControllerBuilderImpl(Option<Float> option) {
+        super(option);
+    }
+
+    @Override
+    public FloatFieldControllerBuilder min(Float min) {
+        this.min = min;
+        return this;
+    }
+
+    @Override
+    public FloatFieldControllerBuilder max(Float max) {
+        this.max = max;
+        return this;
+    }
+
+    @Override
+    public FloatFieldControllerBuilder range(Float min, Float max) {
+        this.min = min;
+        this.max = max;
+        return this;
+    }
+
+    @Override
+    public FloatFieldControllerBuilder valueFormatter(Function<Float, Component> formatter) {
+        this.formatter = formatter;
+        return this;
+    }
+
+    @Override
+    public Controller<Float> build() {
+        return new FloatFieldController(option, min, max, formatter);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/impl/controller/FloatSliderControllerBuilderImpl.java b/common/src/main/java/dev/isxander/yacl/impl/controller/FloatSliderControllerBuilderImpl.java
new file mode 100644
index 0000000..c49eee7
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/impl/controller/FloatSliderControllerBuilderImpl.java
@@ -0,0 +1,43 @@
+package dev.isxander.yacl.impl.controller;
+
+import dev.isxander.yacl.api.Controller;
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.api.controller.FloatSliderControllerBuilder;
+import dev.isxander.yacl.gui.controllers.slider.FloatSliderController;
+import net.minecraft.network.chat.Component;
+
+import java.util.function.Function;
+
+public class FloatSliderControllerBuilderImpl extends AbstractControllerBuilderImpl<Float> implements FloatSliderControllerBuilder {
+    private float min, max;
+    private float step;
+    private Function<Float, Component> formatter = FloatSliderController.DEFAULT_FORMATTER;
+
+    public FloatSliderControllerBuilderImpl(Option<Float> option) {
+        super(option);
+    }
+
+    @Override
+    public FloatSliderControllerBuilder range(Float min, Float max) {
+        this.min = min;
+        this.max = max;
+        return this;
+    }
+
+    @Override
+    public FloatSliderControllerBuilder step(Float step) {
+        this.step = step;
+        return this;
+    }
+
+    @Override
+    public FloatSliderControllerBuilder valueFormatter(Function<Float, Component> formatter) {
+        this.formatter = formatter;
+        return this;
+    }
+
+    @Override
+    public Controller<Float> build() {
+        return new FloatSliderController(option, min, max, step, formatter);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/impl/controller/IntegerFieldControllerBuilderImpl.java b/common/src/main/java/dev/isxander/yacl/impl/controller/IntegerFieldControllerBuilderImpl.java
new file mode 100644
index 0000000..6514e81
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/impl/controller/IntegerFieldControllerBuilderImpl.java
@@ -0,0 +1,50 @@
+package dev.isxander.yacl.impl.controller;
+
+import dev.isxander.yacl.api.Controller;
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.api.controller.IntegerFieldControllerBuilder;
+import dev.isxander.yacl.gui.controllers.slider.IntegerSliderController;
+import dev.isxander.yacl.gui.controllers.string.number.IntegerFieldController;
+import net.minecraft.network.chat.Component;
+
+import java.util.function.Function;
+
+public class IntegerFieldControllerBuilderImpl extends AbstractControllerBuilderImpl<Integer> implements IntegerFieldControllerBuilder {
+    private int min = Integer.MIN_VALUE;
+    private int max = Integer.MAX_VALUE;
+    private Function<Integer, Component> formatter = IntegerSliderController.DEFAULT_FORMATTER;
+
+    public IntegerFieldControllerBuilderImpl(Option<Integer> option) {
+        super(option);
+    }
+
+    @Override
+    public IntegerFieldControllerBuilder min(Integer min) {
+        this.min = min;
+        return this;
+    }
+
+    @Override
+    public IntegerFieldControllerBuilder max(Integer max) {
+        this.max = max;
+        return this;
+    }
+
+    @Override
+    public IntegerFieldControllerBuilder range(Integer min, Integer max) {
+        this.min = min;
+        this.max = max;
+        return this;
+    }
+
+    @Override
+    public IntegerFieldControllerBuilder valueFormatter(Function<Integer, Component> formatter) {
+        this.formatter = formatter;
+        return this;
+    }
+
+    @Override
+    public Controller<Integer> build() {
+        return new IntegerFieldController(option, min, max, formatter);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/impl/controller/IntegerSliderControllerBuilderImpl.java b/common/src/main/java/dev/isxander/yacl/impl/controller/IntegerSliderControllerBuilderImpl.java
new file mode 100644
index 0000000..154adb1
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/impl/controller/IntegerSliderControllerBuilderImpl.java
@@ -0,0 +1,43 @@
+package dev.isxander.yacl.impl.controller;
+
+import dev.isxander.yacl.api.Controller;
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.api.controller.IntegerSliderControllerBuilder;
+import dev.isxander.yacl.gui.controllers.slider.IntegerSliderController;
+import net.minecraft.network.chat.Component;
+
+import java.util.function.Function;
+
+public class IntegerSliderControllerBuilderImpl extends AbstractControllerBuilderImpl<Integer> implements IntegerSliderControllerBuilder {
+    private int min, max;
+    private int step;
+    private Function<Integer, Component> formatter = IntegerSliderController.DEFAULT_FORMATTER;
+
+    public IntegerSliderControllerBuilderImpl(Option<Integer> option) {
+        super(option);
+    }
+
+    @Override
+    public IntegerSliderControllerBuilder range(Integer min, Integer max) {
+        this.min = min;
+        this.max = max;
+        return this;
+    }
+
+    @Override
+    public IntegerSliderControllerBuilder step(Integer step) {
+        this.step = step;
+        return this;
+    }
+
+    @Override
+    public IntegerSliderControllerBuilder valueFormatter(Function<Integer, Component> formatter) {
+        this.formatter = formatter;
+        return this;
+    }
+
+    @Override
+    public Controller<Integer> build() {
+        return new IntegerSliderController(option, min, max, step, formatter);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/impl/controller/LongFieldControllerBuilderImpl.java b/common/src/main/java/dev/isxander/yacl/impl/controller/LongFieldControllerBuilderImpl.java
new file mode 100644
index 0000000..0e76b3d
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/impl/controller/LongFieldControllerBuilderImpl.java
@@ -0,0 +1,50 @@
+package dev.isxander.yacl.impl.controller;
+
+import dev.isxander.yacl.api.Controller;
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.api.controller.LongFieldControllerBuilder;
+import dev.isxander.yacl.gui.controllers.slider.LongSliderController;
+import dev.isxander.yacl.gui.controllers.string.number.LongFieldController;
+import net.minecraft.network.chat.Component;
+
+import java.util.function.Function;
+
+public class LongFieldControllerBuilderImpl extends AbstractControllerBuilderImpl<Long> implements LongFieldControllerBuilder {
+    private long min = Long.MIN_VALUE;
+    private long max = Long.MAX_VALUE;
+    private Function<Long, Component> formatter = LongSliderController.DEFAULT_FORMATTER;
+
+    public LongFieldControllerBuilderImpl(Option<Long> option) {
+        super(option);
+    }
+
+    @Override
+    public LongFieldControllerBuilder min(Long min) {
+        this.min = min;
+        return this;
+    }
+
+    @Override
+    public LongFieldControllerBuilder max(Long max) {
+        this.max = max;
+        return this;
+    }
+
+    @Override
+    public LongFieldControllerBuilder range(Long min, Long max) {
+        this.min = min;
+        this.max = max;
+        return this;
+    }
+
+    @Override
+    public LongFieldControllerBuilder valueFormatter(Function<Long, Component> formatter) {
+        this.formatter = formatter;
+        return this;
+    }
+
+    @Override
+    public Controller<Long> build() {
+        return new LongFieldController(option, min, max, formatter);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/impl/controller/LongSliderControllerBuilderImpl.java b/common/src/main/java/dev/isxander/yacl/impl/controller/LongSliderControllerBuilderImpl.java
new file mode 100644
index 0000000..24926c4
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/impl/controller/LongSliderControllerBuilderImpl.java
@@ -0,0 +1,43 @@
+package dev.isxander.yacl.impl.controller;
+
+import dev.isxander.yacl.api.Controller;
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.api.controller.LongSliderControllerBuilder;
+import dev.isxander.yacl.gui.controllers.slider.LongSliderController;
+import net.minecraft.network.chat.Component;
+
+import java.util.function.Function;
+
+public class LongSliderControllerBuilderImpl extends AbstractControllerBuilderImpl<Long> implements LongSliderControllerBuilder {
+    private long min, max;
+    private long step;
+    private Function<Long, Component> formatter = LongSliderController.DEFAULT_FORMATTER;
+
+    public LongSliderControllerBuilderImpl(Option<Long> option) {
+        super(option);
+    }
+
+    @Override
+    public LongSliderControllerBuilder range(Long min, Long max) {
+        this.min = min;
+        this.max = max;
+        return this;
+    }
+
+    @Override
+    public LongSliderControllerBuilder step(Long step) {
+        this.step = step;
+        return this;
+    }
+
+    @Override
+    public LongSliderControllerBuilder valueFormatter(Function<Long, Component> formatter) {
+        this.formatter = formatter;
+        return this;
+    }
+
+    @Override
+    public Controller<Long> build() {
+        return new LongSliderController(option, min, max, step, formatter);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/impl/controller/StringControllerBuilderImpl.java b/common/src/main/java/dev/isxander/yacl/impl/controller/StringControllerBuilderImpl.java
new file mode 100644
index 0000000..17d9e92
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/impl/controller/StringControllerBuilderImpl.java
@@ -0,0 +1,17 @@
+package dev.isxander.yacl.impl.controller;
+
+import dev.isxander.yacl.api.Controller;
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.api.controller.StringControllerBuilder;
+import dev.isxander.yacl.gui.controllers.string.StringController;
+
+public class StringControllerBuilderImpl extends AbstractControllerBuilderImpl<String> implements StringControllerBuilder {
+    public StringControllerBuilderImpl(Option<String> option) {
+        super(option);
+    }
+
+    @Override
+    public Controller<String> build() {
+        return new StringController(option);
+    }
+}
diff --git a/common/src/main/java/dev/isxander/yacl/impl/controller/TickBoxControllerBuilderImpl.java b/common/src/main/java/dev/isxander/yacl/impl/controller/TickBoxControllerBuilderImpl.java
new file mode 100644
index 0000000..6d835cb
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/impl/controller/TickBoxControllerBuilderImpl.java
@@ -0,0 +1,17 @@
+package dev.isxander.yacl.impl.controller;
+
+import dev.isxander.yacl.api.Controller;
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.api.controller.TickBoxControllerBuilder;
+import dev.isxander.yacl.gui.controllers.TickBoxController;
+
+public class TickBoxControllerBuilderImpl extends AbstractControllerBuilderImpl<Boolean> implements TickBoxControllerBuilder {
+    public TickBoxControllerBuilderImpl(Option<Boolean> option) {
+        super(option);
+    }
+
+    @Override
+    public Controller<Boolean> build() {
+        return new TickBoxController(option);
+    }
+}
-- 
cgit