|
| 1 | +# API Improvement Proposals |
| 2 | + |
| 3 | +**Multi-AI Design Panel** (92% confidence) |
| 4 | +**Date:** 2026-06-12 |
| 5 | +**Issue:** #237 |
| 6 | + |
| 7 | +## Problem Statement |
| 8 | + |
| 9 | +Current API feels "old-school AWT" with excessive boilerplate: |
| 10 | +- Manual `setLocation(x, y)` and `setSize(w, h)` for every component |
| 11 | +- Coordinate math (`terminalWidth - 2`, `terminalHeight - 4`) |
| 12 | +- **Layout managers exist but are NEVER used** in any example |
| 13 | +- No `preferredSize` concept (layouts can't query component dimensions) |
| 14 | +- No constraint-aware `add(component, constraint)` method |
| 15 | +- Missing BoxLayout/VBoxLayout for simple vertical stacking |
| 16 | + |
| 17 | +## Three Alternative API Designs |
| 18 | + |
| 19 | +--- |
| 20 | + |
| 21 | +## Design 1: Builder Pattern (Fluent API) |
| 22 | + |
| 23 | +### Example Code |
| 24 | +```java |
| 25 | +Frame frame = Frame.builder("Simple Demo") |
| 26 | + .fillTerminal() |
| 27 | + .visible(true) |
| 28 | + .child( |
| 29 | + Panel.builder() |
| 30 | + .inset(1, 2, 2, 0) |
| 31 | + .bordered(true) |
| 32 | + .child(Label.builder("Interactive Demo").at(3, 4).size(40, 1)) |
| 33 | + .child( |
| 34 | + Button.builder("Click Me!") |
| 35 | + .at(3, 7) |
| 36 | + .size(15, 1) |
| 37 | + .onAction(() -> System.out.println("Clicked!")) |
| 38 | + ) |
| 39 | + ) |
| 40 | + .build(); |
| 41 | + |
| 42 | +RootPane.getInstance().add(frame); |
| 43 | +``` |
| 44 | + |
| 45 | +### Implementation |
| 46 | +Each component gets a static inner `Builder` class: |
| 47 | +```java |
| 48 | +public class Button extends Component { |
| 49 | + public static ButtonBuilder builder(String label) { |
| 50 | + return new ButtonBuilder(label); |
| 51 | + } |
| 52 | + |
| 53 | + public static class ButtonBuilder { |
| 54 | + private final String label; |
| 55 | + private int x, y, width, height; |
| 56 | + private Runnable action; |
| 57 | + |
| 58 | + ButtonBuilder(String label) { this.label = label; } |
| 59 | + |
| 60 | + public ButtonBuilder at(int x, int y) { |
| 61 | + this.x = x; this.y = y; |
| 62 | + return this; |
| 63 | + } |
| 64 | + |
| 65 | + public ButtonBuilder size(int w, int h) { |
| 66 | + this.width = w; this.height = h; |
| 67 | + return this; |
| 68 | + } |
| 69 | + |
| 70 | + public ButtonBuilder onAction(Runnable action) { |
| 71 | + this.action = action; |
| 72 | + return this; |
| 73 | + } |
| 74 | + |
| 75 | + public Button build() { |
| 76 | + Button btn = new Button(label); |
| 77 | + btn.setLocation(x, y); |
| 78 | + btn.setSize(width, height); |
| 79 | + if (action != null) btn.addActionListener(action); |
| 80 | + return btn; |
| 81 | + } |
| 82 | + } |
| 83 | +} |
| 84 | +``` |
| 85 | + |
| 86 | +### Pros |
| 87 | +- Reduces boilerplate (fluent chaining) |
| 88 | +- Better readability (nested structure mirrors UI hierarchy) |
| 89 | +- Type-safe (compile-time checking) |
| 90 | +- Backward compatible (existing API unchanged) |
| 91 | + |
| 92 | +### Cons |
| 93 | +- Still uses absolute positioning (doesn't encourage layouts) |
| 94 | +- Builder classes add ~50-100 LOC per component |
| 95 | +- Memory overhead (builder instances) |
| 96 | + |
| 97 | +### Migration Path |
| 98 | +1. Add builder classes to all components (phase 1) |
| 99 | +2. Update examples to use builders (phase 2) |
| 100 | +3. Mark setLocation/setSize as @Deprecated with warning (phase 3, optional) |
| 101 | + |
| 102 | +--- |
| 103 | + |
| 104 | +## Design 2: Layout-First API |
| 105 | + |
| 106 | +### Example Code |
| 107 | +```java |
| 108 | +Frame frame = new Frame("Simple Demo"); |
| 109 | +frame.setLayout(new VBoxLayout()); // NEW: vertical box layout |
| 110 | + |
| 111 | +Label title = new Label("Interactive Demo"); |
| 112 | +title.setPreferredSize(40, 1); // NEW: preferred size |
| 113 | + |
| 114 | +Button btn1 = new Button("Click Me!"); |
| 115 | +btn1.setPreferredSize(15, 1); |
| 116 | +btn1.addActionListener(() -> System.out.println("Clicked!")); |
| 117 | + |
| 118 | +frame.add(title); |
| 119 | +frame.add(btn1); |
| 120 | +frame.pack(); // NEW: size to preferred sizes |
| 121 | + |
| 122 | +RootPane.getInstance().add(frame); |
| 123 | +``` |
| 124 | + |
| 125 | +### NEW Classes Required |
| 126 | + |
| 127 | +**VBoxLayout.java:** |
| 128 | +```java |
| 129 | +public class VBoxLayout implements LayoutManager { |
| 130 | + private int spacing = 1; |
| 131 | + |
| 132 | + public VBoxLayout() {} |
| 133 | + public VBoxLayout(int spacing) { this.spacing = spacing; } |
| 134 | + |
| 135 | + @Override |
| 136 | + public void layoutContainer(Container parent) { |
| 137 | + int y = 0; |
| 138 | + for (Component child : parent.getChildren()) { |
| 139 | + Dimension pref = child.getPreferredSize(); |
| 140 | + child.setLocation(0, y); |
| 141 | + child.setSize(parent.getWidth(), pref.height); |
| 142 | + y += pref.height + spacing; |
| 143 | + } |
| 144 | + } |
| 145 | +} |
| 146 | +``` |
| 147 | + |
| 148 | +**Component.java additions:** |
| 149 | +```java |
| 150 | +public class Component { |
| 151 | + protected Dimension preferredSize = new Dimension(10, 1); // default |
| 152 | + |
| 153 | + public Dimension getPreferredSize() { |
| 154 | + return preferredSize; |
| 155 | + } |
| 156 | + |
| 157 | + public void setPreferredSize(int width, int height) { |
| 158 | + this.preferredSize = new Dimension(width, height); |
| 159 | + } |
| 160 | +} |
| 161 | +``` |
| 162 | + |
| 163 | +**Container.java additions:** |
| 164 | +```java |
| 165 | +public void add(Component child, Object constraint) { |
| 166 | + children.add(child); |
| 167 | + child.setParent(this); |
| 168 | + if (layoutManager != null) { |
| 169 | + layoutManager.addLayoutComponent(child, constraint); |
| 170 | + } |
| 171 | + invalidateLayout(); |
| 172 | +} |
| 173 | + |
| 174 | +public void pack() { |
| 175 | + // Size container to fit children's preferred sizes |
| 176 | + if (layoutManager != null) { |
| 177 | + Dimension pref = layoutManager.preferredLayoutSize(this); |
| 178 | + setSize(pref.width, pref.height); |
| 179 | + } |
| 180 | +} |
| 181 | +``` |
| 182 | + |
| 183 | +### Pros |
| 184 | +- **Fixes the root problem** (layout managers are invisible today) |
| 185 | +- Responsive to terminal resize |
| 186 | +- Familiar AWT pattern |
| 187 | +- Eliminates coordinate math |
| 188 | + |
| 189 | +### Cons |
| 190 | +- Breaking change (requires preferredSize concept) |
| 191 | +- Existing absolute-positioned code still works but discouraged |
| 192 | +- All widgets need sensible preferred sizes |
| 193 | +- More complex than builders |
| 194 | + |
| 195 | +### Migration Path |
| 196 | +1. Add preferredSize + pack() to Component/Container |
| 197 | +2. Implement VBoxLayout, HBoxLayout |
| 198 | +3. Update BorderLayout to use preferredSize |
| 199 | +4. Rewrite 1-2 examples using layouts |
| 200 | +5. Document layout-first approach in README |
| 201 | +6. Gradual migration (both APIs coexist) |
| 202 | + |
| 203 | +--- |
| 204 | + |
| 205 | +## Design 3: Declarative DSL (Record-Based) |
| 206 | + |
| 207 | +### Example Code |
| 208 | +```java |
| 209 | +UI ui = UI.create( |
| 210 | + frame("Simple Demo", |
| 211 | + panel(BORDERED, INSET(1, 2), |
| 212 | + label("Interactive Demo", AT(3, 4), SIZE(40, 1)), |
| 213 | + button("Click Me!", AT(3, 7), SIZE(15, 1), |
| 214 | + onClick(() -> System.out.println("Clicked!"))) |
| 215 | + ) |
| 216 | + ) |
| 217 | +); |
| 218 | + |
| 219 | +ui.show(); |
| 220 | +``` |
| 221 | + |
| 222 | +### Implementation |
| 223 | +```java |
| 224 | +public sealed interface ComponentSpec { |
| 225 | + record Frame(String title, ComponentSpec... children) implements ComponentSpec {} |
| 226 | + record Panel(Attribute[] attrs, ComponentSpec... children) implements ComponentSpec {} |
| 227 | + record Label(String text, Attribute... attrs) implements ComponentSpec {} |
| 228 | + record Button(String label, Attribute... attrs) implements ComponentSpec {} |
| 229 | +} |
| 230 | + |
| 231 | +public sealed interface Attribute { |
| 232 | + record At(int x, int y) implements Attribute {} |
| 233 | + record Size(int w, int h) implements Attribute {} |
| 234 | + record OnClick(Runnable action) implements Attribute {} |
| 235 | + record Bordered() implements Attribute {} |
| 236 | + record Inset(int top, int left, int bottom, int right) implements Attribute {} |
| 237 | +} |
| 238 | + |
| 239 | +public class UI { |
| 240 | + public static UI create(ComponentSpec spec) { |
| 241 | + return new UI(build(spec)); |
| 242 | + } |
| 243 | + |
| 244 | + private static Component build(ComponentSpec spec) { |
| 245 | + return switch (spec) { |
| 246 | + case Frame(String title, ComponentSpec[] children) -> { |
| 247 | + Frame f = new Frame(title); |
| 248 | + for (ComponentSpec child : children) { |
| 249 | + f.add(build(child)); |
| 250 | + } |
| 251 | + yield f; |
| 252 | + } |
| 253 | + case Button(String label, Attribute[] attrs) -> { |
| 254 | + Button b = new Button(label); |
| 255 | + applyAttributes(b, attrs); |
| 256 | + yield b; |
| 257 | + } |
| 258 | + // ... other cases |
| 259 | + }; |
| 260 | + } |
| 261 | +} |
| 262 | +``` |
| 263 | + |
| 264 | +### Pros |
| 265 | +- **Most concise** (minimal syntax) |
| 266 | +- Declarative (what, not how) |
| 267 | +- Java 21 pattern matching showcase |
| 268 | +- Immutable specifications |
| 269 | + |
| 270 | +### Cons |
| 271 | +- **Radical departure** from existing API |
| 272 | +- Sealed records = Java 17+ only |
| 273 | +- No IDE autocomplete for attributes |
| 274 | +- Harder to debug (stack traces through switch/recursion) |
| 275 | + |
| 276 | +### Migration Path |
| 277 | +- **NOT recommended for 1.0** (too disruptive) |
| 278 | +- Potential 2.0 "declarative module" as opt-in alternative |
| 279 | +- Keep imperative API as default |
| 280 | + |
| 281 | +--- |
| 282 | + |
| 283 | +## Multi-AI Recommendations |
| 284 | + |
| 285 | +### Consensus (all 3 models agreed): |
| 286 | +1. **Design 2 (Layout-First) is the best path forward** |
| 287 | + - Fixes root cause (layouts exist but unused) |
| 288 | + - Familiar to Java developers |
| 289 | + - Coexists with existing code |
| 290 | + |
| 291 | +2. **Quick wins to implement first:** |
| 292 | + - Add `VBoxLayout` and `HBoxLayout` |
| 293 | + - Add `Component.preferredSize` field |
| 294 | + - Add `Container.pack()` method |
| 295 | + - Add `Container.add(Component, Object)` overload |
| 296 | + - Rewrite one example (SimpleDemo.java) using layouts |
| 297 | + |
| 298 | +3. **Builder pattern is a good supplement:** |
| 299 | + - Can coexist with layouts |
| 300 | + - Reduces boilerplate even with absolute positioning |
| 301 | + - Low implementation cost |
| 302 | + |
| 303 | +### Suggested Roadmap |
| 304 | + |
| 305 | +**Phase 1 (v1.1 - 2 weeks):** |
| 306 | +- Implement `VBoxLayout` / `HBoxLayout` |
| 307 | +- Add `preferredSize` to Component |
| 308 | +- Add `pack()` to Container |
| 309 | +- Rewrite SimpleDemo to use layouts |
| 310 | + |
| 311 | +**Phase 2 (v1.2 - 4 weeks):** |
| 312 | +- Add builders to top 10 most-used widgets |
| 313 | +- Update 2 more examples |
| 314 | +- Document layout patterns |
| 315 | + |
| 316 | +**Phase 3 (v2.0 - future):** |
| 317 | +- Evaluate declarative DSL as experimental module |
| 318 | +- Benchmark layout performance |
| 319 | +- Consider constraint-based layouts (GridBagLayout equivalent) |
| 320 | + |
| 321 | +--- |
| 322 | + |
| 323 | +## Critical Codebase Findings |
| 324 | + |
| 325 | +From multi-AI analysis: |
| 326 | + |
| 327 | +1. **BorderLayout, FlowLayout, GridLayout ALREADY EXIST** but zero examples use them |
| 328 | +2. **No BoxLayout** (most common pattern) |
| 329 | +3. **Container.setLayout() exists** but is invisible to users |
| 330 | +4. **No preferredSize concept** (layouts can't query ideal dimensions) |
| 331 | + |
| 332 | +**The problem isn't missing features - it's discoverability and defaults.** |
| 333 | + |
| 334 | +--- |
| 335 | + |
| 336 | +## Action Items for Issue #237 |
| 337 | + |
| 338 | +1. ✅ Create VBoxLayout.java |
| 339 | +2. ✅ Create HBoxLayout.java |
| 340 | +3. ✅ Add preferredSize field to Component.java |
| 341 | +4. ✅ Add pack() method to Container.java |
| 342 | +5. ✅ Add add(Component, Object) to Container.java |
| 343 | +6. ✅ Rewrite SimpleDemo.java to use VBoxLayout |
| 344 | +7. ✅ Add "Layout Guide" section to docs/examples.md |
| 345 | +8. ✅ Update README to showcase layout-first approach |
| 346 | + |
| 347 | +**Estimated effort:** 3-5 days for phase 1 |
| 348 | + |
| 349 | +--- |
| 350 | + |
| 351 | +**Related Issues:** #237 (simplify API), #241 (real-world examples can demonstrate layouts) |
| 352 | +**Multi-AI Contributors:** Opus, Sonnet, Haiku (92% confidence) |
0 commit comments