:date: 2016-05-30 ==================== Monday, May 30, 2016 ==================== This week-end Lino had a "heart surgery": I wrote a new module :mod:`lino.core.widgets` with the "general" functionality of what I used to call "user interface elements". The `widgets` branch ==================== (01:00 am) Before explaining what that heart surgery was about, here is what I had to learn once more this morning because I don't use it often enough: how to create a branch after having done modifications in my working copy which is still on origin/master (but before checkin). :: $ git status On branch master Changes not staged for commit: (use "git add ..." to update what will be committed) (use "git checkout -- ..." to discard changes in working directory) modified: lino/core/actions.py modified: lino/core/kernel.py modified: lino/core/requests.py modified: lino/core/site.py modified: lino/core/tablerequest.py modified: lino/modlib/extjs/__init__.py modified: lino/modlib/extjs/elems.py modified: lino/modlib/extjs/ext_renderer.py modified: lino/modlib/jinja/__init__.py modified: lino/utils/jsgen.py Untracked files: (use "git add ..." to include in what will be committed) lino/core/widgets.py no changes added to commit (use "git add" and/or "git commit -a") Above changes are almost ready to go into master, but only *almost*. There seems to be some difference in the generated :file:`lino_900_en.js` file. So I'd like to compare that file with the same file as is was generated by Lino before my work. IOW I want to switch back to my last published version for a short time before publishing my work. I *create and select* a new local branch "widgets":: $ git checkout -b widgets Note: `git checkout widgets` without -b would have created the branch but not selected it. To make sure that I am now on branch "widgets", I can run:: $ git status On branch master ... I add my modifications and check them in:: $ git add lino/ $ git checkin -m "almost done" Now I check out master:: $ git checkout master M lino/core/actions.py M lino/core/kernel.py M lino/core/requests.py M lino/core/site.py M lino/core/tablerequest.py M lino/modlib/extjs/__init__.py M lino/modlib/extjs/elems.py M lino/modlib/extjs/ext_renderer.py M lino/modlib/jinja/__init__.py M lino/utils/jsgen.py Switched to branch 'master' Your branch is up-to-date with 'origin/master'. So I am now back to the old version. I let it create a :file:`lino_900_en.js` file, then switch back to widgets before continuing to work on it:: $ git checkout widgets Switched to branch 'widgets' After working on it some more time, I can say:: $ git ci -a -m "widgets branch now seems ok (tests are running)" And before going to sleep I'd actually would like to publish the widgets branch. I tried to publish them as follows:: $ git push Everything up-to-date That didn't work. Because "Your local branches aren’t automatically synchronized to the remotes you write to – you have to explicitly push the branches you want to share." (as explained `here `_). So I must do:: $ git push origin widgets Counting objects: 101, done. Delta compression using up to 8 threads. Compressing objects: 100% (30/30), done. Writing objects: 100% (30/30), 12.50 KiB | 0 bytes/s, done. Total 30 (delta 25), reused 0 (delta 0) To git@github.com:lsaffre/lino.git * [new branch] widgets -> widgets $ Voilà. Now I can go to sleep... A heart surgery for Lino ======================== Until today, :mod:`lino.modlib.extjs.elems` contained two very different things: - Lino's "widgeting system" which is generally valid and applies also for user interfaces that have nothing to do with ExtJS (e.g. :mod:`lino.modlib.bootstrap3`). - The "layout elements" are a pythonic representation of the many JavaScript components used by the ExtJS user interface. Their main purpose is to generate the :xfile:`linoweb.js` file. The "widgeting system" is now in :mod:`lino.core.widgets`, and only the ExtJS specific stuff remains in :mod:`lino.modlib.extjs.elems`. This splitting was a rather difficult and dangerous enterprise. Although the test suites of all projects now pass, I expect more problems to appear in the near future because we are far from having 100% of test coverage. Why did I do this? It was a design problem which I had been feeling for quite some time: people who want to write a user interface for Lino but who don't want to know about ExtJS, were discouraged when they saw lots of ExtJS-specific stuff when looking at the :mod:`lino.modlib.extjs.elems` module. Why just now? It started "by itself", probably because I had a few problems to get the http://bugs.lino-framework.org/ website back to work after playing around on that server for :ticket:`920` last Friday, and I decided to repair it by doing it finally "the way it should be done". I underestimated somewhat how deep this would go. Application code requires no changes at all, and :mod:`lino_extjs6` required only a few changes. Especially I removed :mod:`lino_extjs.extjs6.elems`, which had been a copy of :mod:`lino.modlib.extjs.elems` where Hamza (fortunately) did not yet need to change a single line. About layouts ============= Once more I try to explain what this "heart surgery" was about and why I wanted to separate :mod:`lino.core.widgets` from :mod:`lino.modlib.extjs.elems`. Lino's "layouts" are an abstract pythonic description of the forms and grids of an application. Layouts are an integral part of the application and should not depend on the choice of user interface. A Layout is the object defined by the application programmer. Lino creates (for every user interface) one LayoutHandle per Layout. A LayoutHandle is a "compiled" version of a Layout: it contains "widgets" which have been created by the WidgetFactory of the plugin which acts as user interface. HtmlRenderer and TextRenderer use the plain widgets, but ExtJS is much more complex and therefore ExtRenderer has its own WidgetFactory which doesn't creates extended widgets instead of the plain widgets. They are called "layout elements", but actually they are extended widgets. About multiple inheritance ========================== One worry is that I have now created a "double class hierarchy". Here is a simplified inheritance diagram for `TextFieldElement`:: (widgets) (elems) Widget --------> LayoutElement | | V V FieldElement ------> FieldWidget | | V V TextFieldWidget ----> TextFieldElement This is not just simple *diamond inheritance*, it is a "carpet of diamonds". This carpet causes lots of subtle pitfalls due to multiple inheritance. One possibility to get rid of this might be to use composition instead of inheritance. That is: the elements would not inherit from the widgets, they would just point to "their" widget. But I won't do that right now, because (a) I am glad that Lino seems to be well after that big operation and (b) I am not yet sure whether we *want* to get rid of it. Maybe this is a case where MI is needed ("Multiple inheritance is one of those things that is not used often, and can be misused, but is sometimes needed.", `KeithB `__)