prompt-toolkit / python-prompt-toolkit

Library for building powerful interactive command line applications in Python
https://python-prompt-toolkit.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
9.37k stars 716 forks source link

Explain layout better #674

Open techtonik opened 6 years ago

techtonik commented 6 years ago

Is it possible to color references to different layout component classes on this page: http://python-prompt-toolkit.readthedocs.io/en/stable/pages/full_screen_apps.html#the-layout

It seems that there are three groups of layout components:

  1. Container, HSplit, VSplit, FloatContainer - (light-blue lines) specify layout only (no flow direction)
  2. Window - (red) flow and scrolling
  3. UIControl, BufferControl, FormattedTextControl - (green) object that is being placed into Window

Then all three levels can be grouped into single widget - TextArea, Button, Frame (what's the difference with Window/Container), VerticalLine. Would be nice to see decomposition of VerticalLine into basic layout components.

jonathanslenders commented 6 years ago

Hi @techtonik,

I'm willing to accept pull requests for these pages of documentation. I've no idea what color options Sphinx offers to make it more clear. Also, I hope to find more time in the coming year(s) to make the full-screen documentation better. There is a load of functionality in these classes, and I could probably write a book about it right now. :-)

These are very good suggestions that you have there. This is correct indeed.

fish-face commented 6 years ago

This is quite necessary. If you try to do anything at all that isn't in the default examples it seems that one immediately runs into problems when trying to focus elements: the layout will claim that whatever you give it is either not in the layout or is an "invalid value" (read: it cannot see the child you really care about).

I think this has something to do with the fact that while you can try and focus a Container, a Window, a UIControl, a Widget or even a Buffer, internally the layout consists of Containers and must somehow not contain all of them. Better documentation on when you need which kind of object might help.

Would it help matters if Widgets were changed to be subclasses of Container? Would it help if Windows disappeared and Controls, Widgets and Containers were all one class, as in a GUI toolkit? There are a lot of isinstance-checks. It certainly looks suspicious that there are isinstance checks everywhere but then Widgets are not a subclass of any of these things, yet still somehow magically work, at least some of the time - I suspect that my problems stem from trying to use a TextArea widget where I really need a Container, or a Window, or a Control.

jonathanslenders commented 6 years ago

That's fair. Like I said, any contribution to the documentation is welcome, and if you have certain questions in particular (maybe with code samples that don't work), I'm willing to have a look at that. I the meantime, the documentation will probably improve.

Anything passed to the focus() function should be something that appears in the layout.

Widgets will not be subclassed of Container. I started that way, but that didn't work. The reason is that Container is an interface, not an implementation of an interface, and not all widgets would implement all of these methods. In practice, it would probably mean that some widgets would inherit from HSplit, other from VSplit, other from Window, and so on. Each of these implementations of Container defines a different set of methods, so this means that as a user you have to know all of these methods in order for inheritance to be safe to use. There is a wisdom that composition is better then inheritance, and this is actually what these widgets are doing. They compose lower level UI components together, and expose the root container through one method __pt_container__. This is very similar to how an integer in Python implements the __str__ interface. An integer doesn't have to inherit from str in order to be used in places that only expect __str__.

Normally, if we have a TextArea, then there should not really be a need to think about Window or Control``. The layout is composed of containers and widgets can be placed in the layout like if they were a container. I'm curious to see examples where that isn't true, because that could be a bug.

fish-face commented 6 years ago

So I think the problem I was experiencing was fixed in ea93753, so maybe the documentation was not really so bad :P At least at the moment I am not familiar enough to really be confident trying to make improvements myself, but maybe in the future.

As regards architecture, the way GTK does it for example is that everything is a subclass of some generic class (called 'Widget' which is not what it is here) which just renders its contents at its location at whatever size they request, I believe. If your Widgets were subclassed from such a class then their first child would be the HSplit, for example, and you wouldn't have to reimplement HSplit-specific things. I have heard the "wisdom" about composition but I think it might add complexity here given all the isinstancing. This is rather the opposite of interface/duck-typing!

But the GTK-style architecture is quite different from what you have. I think it would end up simpler but it would take a lot of effort to get there. Aside from documentation, in the medium term perhaps one idea would be to experiment with getting rid of the Window class. Maybe it wouldn't work on its own but it seems to me that you could make the ability to display a UIControl part of the Container interface, and then you could attach UIControls directly to a Container rather than having to wrap them in a Window every time.

techtonik commented 6 years ago

Looks like I will never get to Inkscape to draw, so maybe put some conceptcode for somebody who is more skilled or have experience with other layout engines.

layout = Layout()

What is the effect of default empty layout? Does it cover the whole screen or contracts to zero-pixel?