in lieu of a task tracker, here's some words explaining what i still need to do!
# immediate future
miscellaneous tasks i'm jotting down so i don't forget they need attention:
# pre-1.0
i want to release v1.0.0 by the end of 2025.
we'll see if i manage that, but the roadmap for it is pretty thin, so i'm hopeful:
## `#![no_std]`
some later work i really want to be able to support will require this,
so it's worth putting in the effort *now*
instead of waiting to port it until way later.
`alloc` is fine, though. not gonna be able to get around that in any case.
also some backends will require `std` -- but the main code shouldn't.
## high-level apis
there are a lot of higher-level apis that i want to provide as part of this.
currently i have two major groups planned:
- [ ]
`trait Splitter`, which divides `Frame`s into pieces
(in human-friendlier ways than the raw `split_v`/`split_h`)
- [ ]
`rows!` / `cols!`, using roughly the same dsl
to partition (respectively) horizontal and vertical stripes
- [ ]
`border!` to draw a border and get the inside region
- [ ]
`grid!` to divide things into a perfectly aligned grid
- [ ]
`trait Drawable`, which is the component system.
get passed a `Frame`, do whatever you want, return whatever you want.
(technically this is the same api as `Splitter`, yes. semantics.)
- [ ]
`tfmt!`/`Text` and, more generally, the formatted text api.
currently i'm using `owo-colors` as a bodge,
but it doesn't really fit this use case.
- [ ]
`button!`/`Button` returning `bool` for if it was just clicked
- [ ]
`TextBox` for text input
- [ ]
`Select` for `<select>`-style combobox input
## repaying tech debt
- [ ] reconsider how inputs and frames are handed out.
right now, every single input comes with a place to render.
this hypothetically makes implementation simpler --
rendering doesn't need to be conditional --
but in practice, it leads to more complexity to enable optimization,
since rendering can be *super* expensive and you want to be able to skip it if it won't be used!
at the same time, the point of an immediate-mode ui is storing *less* state,
and sending out special "time to draw" inputs would require that.
(how else could you render things like button clicks?)
so what i need to do is either track the state in the backend/frame itself,
e.g. always tracking an `InputState` and sending `StateEvent`s,
or better define how and when inputs are generated.
- [ ] align `Frame` and `Grid`
right now the two types have totally different apis.
ideally, `Frame` would expose the *same* api as `Grid`,
but doing the requisite translation to account for subsections and virtual cells.
- [ ] figure out if `Grid` can be indexed
this is challenging because indexing needs to return a reference,
but i should be able to at least support indexing on `Y` and returning a slice.
- [ ] unfuck the virtual cells concept.
right now frames "can" be grown past the bit of the screen they actually own;
this will be extremely needed to implement scrollable areas so i figured i'd do it now.
unfortunately i kinda beefed it!
- right now i have a `FrameArea` abstraction that covers like 50% of the relevant work
- inputs just. do not work. at all. with virtual cells, nor do i know how they should
- a lot of it is manually bodged and (because it's broken) untested
luckily none of the components *use* virtual cells yet,
so i *can* just leave that api private until it works --
but it'll eat at me if i don't fix it, and it might be hiding other conceptual issues,
so i should.
this will require some design work to figure out what virtual areas should look like
and how they should work in various cases,
and then hopefully some straightforward implementation in an abstraction layer
so `Frame` can stay relatively simple and dedicated to data access.
- [ ] stop using `owo-colors`.
great little crate for just writing colors to the screen --
and absolutely awful for this use-case. oops!
luckily i don't need most of what it does,
and what i do need, `crossterm` already handles.
so it should be a relatively easy replacement!
- [ ] code review.
a lot of this was written hastily, or in service of something else.
i'm gonna spend a couple days just... very boringly poring over it,
seeing if anything jumps out as a new abstraction
or cleaner implementation.
## open questions
there are a few design questions left on the **pre-1.0** roadmap,
since they'll have significant impacts on the external api
and i want that settled before i call 'er "done".
### **`Backend`**
should `Backend` be `trait Backend`, exposed directly?
or should there be a `struct FaTui` or something
to handle the common code?
i suspect the answer is yes,
but *what exactly* it should handle is very much up for debate.
i do have a trick to defer this question, at least:
i can seal `trait Backend` for now,
add a do-nothing wrapper struct as a placeholder,
and unseal the trait once the api is ready.
### **state tracking**
right now, every `Drawable`/`Splitter`/w/e needs external state.
that means you'd need a `let mut scroll_state` for every scrollable region,
a `let mut select_state` for every `Select`,
etc.
so how, if at all, should `fatui` alleviate that?
if there's a backend struct, then that can *store* data,
but how does it get indexed to match the sub-frame to the data?
what if the frame layout changes and the sub-frame no longer exists?
what if the inexistence was only temporary?
strictly speaking, this might not be pre-1.0:
odds are good i can leave space in the api so it's a minor update later down the line.
maybe.
### **null frames**
currently every frame sent out demands a full re-render.
on the surface of it this is... fine,
except a frame is sent with *every single input*.
there's some input compression to help lag be less impactful,
but still, that's a lot of pointless work
when it only gets re-rendered every 60th of a second!
the fix is some concept of "input without rendering".
the implication on the public api is:
what exactly does that look like, in api/api contract terms?
e.g. right now the `rng` example uses the screen buffer as its state;
should it instead keep separate state?
fair ask! but this needs to be defined.
unlike the other two, this really can't be deferred.
how backends are expected to serve frames
and how components are expected to render
is a contract i can't change without changing a lot more code.
# post-1.0
even after fatui is "done", it'll never really be *done*.
but it'll be more or less backwards compatible from this point forward,
at least until i come up with a wildly better api.
i'm dividing this into minor releases but don't take that too seriously.
who knows what order this will all be done in?
along the way you can expect some straightforward api additions,
like new splitters and components,
and maybe opening up some currently-closed apis, like `FrameInput`.
## scrolling
a new splitter, `scroll!` (or maybe just `Scroll`),
which uses the fancy "new" virtual frame api
to make components render they're in a very tall region
but only show a small fraction--
look, you know how scrolling works.
this implements a uniform api for scrollable regions.
'tain't that complex.
## themes
a core part of making guis look nice is reducing the number of variables to fiddle.
and an easy way to do that is splitting it into independent groups:
design the layout, then pick the color scheme, then tweak each until they look nice,
and then a final bit of tweaking to make them look nice together.
so to that end, it'd be real handy if we had some mechanism for sharing styles,
instead of everyone having to invent their own (or, worse, just not bothering!)
not sure exactly what this'll look like.
color schemes tend to be quite highly dependent on context
and there's not really a "universal set of variables" you can easily use.
but i think i can define a basic set, things like "text" and "emphasis",
and then leave room for further component-specific customization --
which is then at least *one control surface* for all that,
instead of having to wrangle it for half a dozen different things.
## guification
rather than needing the user to open and run your code in a terminal,
fatui will offer three distinct choices of backend:
- purely terminal-based, still. the default and the good.
- open-a-terminal. this opens your default system terminal --
if i bother to support windows, then the nice newer one --
and writes to that. i expect some jank so this is the bad.
- purely graphical. this opens a normal rendering window
and actually draws the graphics.
this allows for an enormous amount of customization,
and even custom tilesets via custom fonts,
but rendering things myself means it'll inevitably be pretty damn ugly.
much better for games, mostly irrelevant for terminal utilities,
but i'm making this to build games in it, so very relevant for me!