cpjreynolds / rustty

A terminal UI library
https://docs.rs/rustty
MIT License
153 stars 14 forks source link

UI Redesign: Widget based API #23

Closed Syntaf closed 8 years ago

Syntaf commented 8 years ago

Over the past couple of weeks I've worked on redesigning the API. It's not nearly finished, but I think it's in a state that's ready to be shared for anyone else to help collaborate on. Check the examples for how the new API would work!

Philosophy

The new UI design is a widget based API, meaning core functionality of the UI is abstracted from the user, and a number of containers and frames are used to create the desired UI. There are a couple of key traits in this redesign currently: Widget, Button, and Layout.

Widgets are the core of the API, all frontend structs offered to the user inherit from Widget. This generalizes the way structs interact with each other and makes encapsulation and specialized widgets easier to develop. Button and Layout are specialized Widgets that offer new functionality that is distinguishable.

Frontend structs all offer a similar API (see Implementation section for a list). This makes developing new widgets easier and interactions less buggy (we don't need to worry about a specific widget interacting with another specific widget, because they are generalized by their traits).

Implementation

Core UI functionality , mostly built by @hsoft , is within the rustty::ui::core:: namespace. Sadly a bug in rust doesn't allow me to expose it correctly ( see mod.rs within core ) but this keeps the frontend a separate concept from the core, so when designing new widgets for users like CheckButton , or TextLabel for example, these are kept separate from the core UI implementation. Here's a rundown of the new UI files:

Core

frame and frame_mut should be considered unstable, I don't know if I should expose the Frame to the user (allowing them to call all functions above through their traits manually and more). However it offers a lot more creative power to have access to the actual frame containing text to be drawn.

Ui

A lot , I'm probably missing a number of things in this list but here's what could be worked on

Labels

Any thoughts? Suggestions?

ghost commented 8 years ago

First of all: thanks for working on this! As this is quite a big PR, I haven't have had time yet to review the whole of it, but I do have early questions:

Pack

Where did you take your inspiration for pack(), Tkinter? If yes, it would seem that it's a misnomer since our pack() here doesn't do the same thing.

In Tkinter, if you have button a and b and you do a.pack(left); b.pack(left);, a and b will end up side by side.

Unless I misunderstood the code we have here, if we do a.pack(left); b.pack(left); with the same margins in rustty, our buttons will end up overlapping. In your examples, you compensate by adjusting margins, but it doesn't seem very convenient to me.

Is this something you're planning to improve?

Layering

I see that you took the former Widget and added a layer on top of it. My early reaction is to think that we're starting to have a lot of layers here and I'm wondering if some of it is spurious.

At the first look, I fail to see what this new layer allows. In your PR message, I see a description of the new API, but no justification. I'd be curious to know what new feature made you say "ok, we need a new layer".

Layout

Why are layout button-specific? Wouldn't we want to be able to layout any kind of widgets?

Syntaf commented 8 years ago

Thanks for the constructive criticism, one of the biggest reasons I wanted to start this PR so early is to hear some opinions on the new API.

The API is unfinished so if you find some really wonky stuff, so we should definitely discuss some stuff that you guys may not understand/agree with so it can be refactored into a direction everyone agrees with

Pack

I decided to call it pack because it's calling structure is similar to that of Tkinter's, however you are right in that it does not pack elements in respect to currently drawn widgets. I didn't want to name it align again simply because I didn't want to having naming conflicts between the Widget trait and Alignable trait

It's very possible we could implement what pack in Tkinter does, but I didn't think of using pack like that. With having all of our widgets being text based, overlapping widgets is a frontend problem that won't cause any internal backend issues (like crashes or something).

Maybe it's best that we don't call it pack then? What would be a better name for what is does currently? Just align?

Layering

I'm not specifically sure what you mean, but i'll answer what I think you're asking. I created an additional layer with the traits Widget , Button , and Layout because I wanted to abstract backend UI implementation from frontend UI implementation. I felt creating this layer makes adding future UI elements much easier and straight forward for any future collaborators, or even people writing programs that need to create their own specialized widget. The general idea is that:

and that's the only requirement, how someone goes about doing that is up to them, but as long as a UI elements implements the functions defined by widget it will be able to be packed and drawn the screen.

Say you have a case in your project where StdButton just doesn't do it for you, you want all of your buttons to be red. In that case you can create your own specialized button for your project that fits your needs. If you wanted to do this currently, you would need to rewrite the entire dialog module and re-implement the add_button feature. With this PR you can instead create your own special button within your project

struct RedButton {
    // ....
 }

impl Widget for RedButton {
    //...
}

impl Button for RedButton {
    //...
} 

and now you can add your redbutton to anything that accepts Buttons within the UI

let mut b1 = RedButton::new("red", 'r', ButtonResult::Ok);
b1.pack(&maindlg, HorizontalAlign::Left, VerticalAlign::Middle, (1,1));
maindlg.add_button(b1); 

The same can be said for Layouts and Widgets, does that give you a better idea? I'm hoping to provide an easier API for not only using the UI but for developing it as well. This layering will allow us to generalize categories of traits and operate on them regardless of their implementation. Doesn't matter if we have a RedButton, StdButton, and any other kind of button; those UI elements are generalized as Buttons which can be added to dialogs without the need of specialized functions for each button. Widget implementations will be completely independent from other widgets allowing us to build a much cleaner frontend UI (as well as writing them will be much easier if we have an expressive and strong backed UI that defines the interactions that Widget, Button etc.. offer)

A lot of my ideas stem from Tkinter, Tkinter has a similar way of creating special widgets and such that I found really useful in my work.

Layout

I'm going to preface this in that the Layout is a horrible implementation in general, I'd love to hear some thoughts on how to make this better.

The original plan was to make this for Widgets, but the problem is that buttons within a layout need to forward their keys and results to the dialog when a layout is added. I haven't found a way to parse a container of widgets and only grab elements of that container that implement a Button trait to forward their keys and results, if I had a container of widgets I would attempting to be calling accel() and result() on widgets that may not implement that

Syntaf commented 8 years ago

Anyone know why lazy_static is all of a sudden failing again in the nightly build?

Syntaf commented 8 years ago

Here's an example of how this PR can be used to make the creation of terminal graphics and UI design easier. My repository is currently using my fork of rustty that has the most recent changes within it. Some things to note:

I still have a lot to add to the repo I linked, it's very unfinished, but I hope you get the idea! It's also made me realize I need add a couple more standard widgets.

Oh, and VerticalLayout and HorizontalLayout are still awful , I need to fix those soon. What this comment was for is to hopefully convince you that this is a worthwhile addition to rustty.