HeinrichApfelmus / threepenny-gui

GUI framework that uses the web browser as a display.
https://heinrichapfelmus.github.io/threepenny-gui/
Other
439 stars 77 forks source link

Widgets API #53

Closed Daniel-Diaz closed 2 years ago

Daniel-Diaz commented 11 years ago

I submit this as a pull request, but please note that this is actually an API proposal.

The attached code shows an example of how it can be implemented in a particular simple case, and explains the general idea. Below I copy of the text that can be found at the source code. Module and function names and other details are provisional and I do not expect to keep them like they are right now. My only intention is to propose an idea.

If the proposal looks reasonable, I will continue working on it.


The idea is to organize elements as widgets. Instead of having every element with the same type, each kind of widget would have a type of its own. In the example above, a simple checkbox widget is implemented. It consists in the following parts:

  1. Widget Type. It contains all the necessary elements that the given widget needs to work properly. It may contain several elements, or even other widgets. The type must be kept abstract.
  2. Widget Class Instance. To fit in the picture, a widget must be converted to a single element, so it can be included in the layout of the program and interact with other elements.
  3. Widget Attributes. Attribute setters and/or getters of the widget. In the example above, checkboxes have the 'checked' and 'text' attributes.

About attributes, I think plenty of widgets are going to share some of them. In the example above, properties like 'checked' and 'text' may apply to other widgets as well. Type classes can solve this problem in an elegant way. A great consequence of implementing it this way is that users will be only allowed to apply attributes only to those type that makes sense to apply them to. Currently, attributes can be applied freely to any element, which might make the user wonder in each execution if the attribute will work or not, and then go back to code and try another one.

Note that elements will still be used, but certain tasks (like using lists, checkboxes, buttons, etc) can be achieved in a better way.

HeinrichApfelmus commented 11 years ago

Hello Daniel, (wow, there are at least three different Daniels on this project)

sounds like a good idea! I think what we want is a collection of widget types and associated type classes similar to what wxHaskell offers. For instance, there is a Checkable class for widgets that can be checked or unchecked.

I very much appreciate widget suggestions and pull requests. However, before we proceed, I would like to establish a design guide for making widgets, as there are a few gotchas with point 3 when it comes to avoiding spaghetti code in the long run. I have put down some thoughts in a blog post of mine, but the post is already outdated and not entirely accurate.

I intend to write said design guide and put it in the repo soon, but that will take some time. Can you bear with me until then?

Daniel-Diaz commented 11 years ago

Hello Daniel, (wow, there are at least three different Daniels on this project)

Yes, next proposal is to rename the project to "The Apfelmus-Daniel GUI"!

sounds like a good idea! I think what we want is a collection of widget types and associated type classes similar to what wxHaskell offers. For instance, there is a Checkable class for widgets that can be checked or unchecked.

I am glad we are reaching an agreement here.

I very much appreciate widget suggestions and pull requests. However, before we proceed, I would like to establish a design guide for making widgets, as there are a few gotchas with point 3 when it comes to avoiding spaghetti code in the long run. I have put down some thoughts in a blog post of mine, but the post is already outdated and not entirely accurate.

Could you elaborate these "gotchas" of point 3? I have read the post you mentioned, but I think that, even when the issues may be related, they are not about the same thing.

I intend to write said design guide and put it in the repo soon, but that will take some time. Can you bear with me until then?

Sure. As soon as you get down with the design guide, we will be able to start coding right away, following the design principles. In the meanwhile, I will be using my provisional implementation of some widgets for my personal projects.

duplode commented 11 years ago

One of the other Daniels here :smiley:

Could you elaborate these "gotchas" of point 3? I have read the post you mentioned, but I think that, even when the issues may be related, they are not about the same thing.

As we wait for Heinrich, my three cents: at face value, your proposal puts two things on the table: (a) the suggestion to have standard types and classes for common widgets, which is clearly a good idea; and (b) the question about what the actual interfaces and implementations of these widgets will look like, which is where the gotchas, the blog post and the planned design guide meet. For instance, I am currently trying to use Reactive.Threepenny to implement widgets that follow the three principles stated in the post. While later today I intend to open another discussion issue motivated by these experiments, it will strictly concern point (b); the desirability of (a) is indeed quite independent from it.

HeinrichApfelmus commented 10 years ago

Concerning the widget class, I think it's a good idea to change the current element function and introduce the following class instead

class Widget w where
    getElement :: w -> Element

element :: Widget w => w -> IO Element
element = return . getElement

widget  :: Widget w => w -> IO w
widget  = return

instance Widget Element where
    getElement = id

When employing the #-style, we use the function element when we want to modify the element and the function widget when we want to modify the widget. Example:

element w #. "fancy"
widget  w # set awesome True

Concerning the attribute setters and getters, i.e. point 3, we could certainly follow traditional GUI libraries and just list a few getters and setters, like WriteAttr, ReadAttr and Event.

However, I think we can do better than that by connecting it to functional reactive programming (FRP). To give an example, I would like the value property of an input widget to have "change notification" built-in. At the moment, we have

value        :: Attr Input String
valueChanged :: Input -> Event String

but I would to see these combined into one thing, for instance a Behavior.

In fact, trying something new along these lines was my primary motivation for departing from wxHaskell and starting my own GUI framework. I have no idea how the new thing will look like, though. But I would like to get a better understanding of it before churning out lots of widgets.

To give you a more detailed overview of what I have in mind, I have now committed the first part of the widget design guide. The "Concepts" section is done, but of course, the "Implementation" sections is conspiciously unfinished.


To proceed, I think it's best that I commit the Widget class to the development branch right away, so that you get at least a small convenience class for your own widgets, @Daniel-Diaz .