I have no experience in user interface design whatsoever. Frankly, I should be reading some materials on how its problems are typically solved, but that's boring! Instead I'm going to come up with some ideas of my own, then see how they do after implementing them in a project.
For this project, I'm going with an interactive TUI (Textual User Interface) for use in terminal emulators, because I find them fun and they're easier to implement than non-textual equivalents.
What I do know about designing an interface is that scaling is the hardest part. Here's what we need to consider in relation to scaling:
Okay, that's actually a lot. Let's go over them one by one.
This one actually makes implementation much easier, as we don't need to worry about converting elements' sizes when working with transforms. Okay, next item.
This is also a convenience, but it does come with a catch: characters are taller than they are wide, so we need to be wary of how that may distort dimensions when trying to draw things which are equally wide and tall. It's not a huge deal though.
To clarify, I'm not referring to any technical limitation here. Rather, I mean that- with a small enough window- you will be unable to render anything useful. The easiest way to deal with this seems to be replacing the entire render with some text telling you the size requirements, as does btop.
While minimum size is pretty easy to test, it can be harder to tell when a greater width or height would cause unintended, unsightly scaling. This is especially problematic for me, as I use a single 1920x1080 monitor and that means I often have half or a quarter of my screenspace dedicated to testing. I can also move the test to a different workspace (bspwm <3), but that doesn't change the fact that others may be using 2k, 4k, or other monitors. Anyway, the simplest remedy for this is just to choose certain elements to scale up even when their contents don't need the room. Typically this works best when applied to the primary element: i.e. not a sidebar, toolbar, or similar.
Some non-essential elements may be okay to hide when the window is too small, and some should be forced to full size no matter what. Width and height might need to be independent for this; a column's list of elements can be shortened safely, but reducing the width of items makes them partially unreadable, and wrapping makes the list confusing to read.
Say you have a primary panel and a sidebar. You likely want those two to have the same height regardless of window size. That's pretty easy to handle, but what if you had two primary panels stacked vertically and wanted the sidebar to be as tall as both panels combined? There are a couple ways to do this. One is to partition space with percentages, like so (using pseudocode):
layout { sidebar { width 0.2, height 1.0 }, panels { width 0.8, height 1.0, bottom { width 1.0, height 0.5 }, top { width 1.0, height 0.5 } } }
In this example, each element has a width and a height which are portions of its parent's width and height. This has the benefit of ensuring correct groupings and alignment, but it's also pretty verbose and makes the actual coordinates harder to follow. I present an alternative:
They're really more like guiding lines, but I like pins better. Anyway, the idea here is to define some invisible horizontal and vertical lines for element borders to snap to. The difference between this and the partitioning method is that pins would be ordered instead of portioned; the state and priority flags of every element are stored, then traversed by priority > pin order until the entire layout can be resolved or a pause state (e.g. window too small) reached. Having just written this paragraph, I can now see this is probably a terrible idea. I'm going to do it anyway! Here's an example of how such a layout might be declared:
layout { pins { sidepane { c 10 }, # c x = column min_horizontal_offset stack { r 50 } # r y = row min_vertical_offset }, elements { sidebar { # each side defaults to $window_<side> right $sidepane }, low_panel { left $sidepane, top $stack }, high_panel { left $sidepane, bottom $stack } } }
I think this kind of definition could have two modes: an automatic one which traverses and corrects as needed (mentioned above), and a manual one which requires using variables in the coordinates of the pins to achieve correct scaling.
Read above- oops!
When gradually scaling a window in a single direction, the layout should not flicker between different scaling targets. In other words, each update should also keep a consistent direction where possible. I'm having a hard time visualizing how to solve this one, so testing is needed before any hypotheses can be made.
Most interactive TUIs have you to focus on a given element before interacting with its contents. This generally involves using tab or directional keys to navigate in a given order. This order should be preserved regardless of scaling so that the user doesn't get confused and their muscle memory for using the program isn't invalidated.
Now I'm off to program all this and see what works and what doesn't. I'll most likely end up using more traditional methods in the long run, but what good is development without experimentation? Results dissection to come (probably).ts dissection to come (probably).