Re: [GITGRUB] New menu interface (implementation)

From:

Michal Suchanek

Subject:

Re: [GITGRUB] New menu interface (implementation)

Date:

Sat, 10 Oct 2009 13:22:35 +0200

2009/10/10 Bean <address@hidden>:
> On Sat, Oct 10, 2009 at 4:41 AM, Michal Suchanek <address@hidden> wrote:
>> 2009/10/9 Bean <address@hidden>:
>>> On Sat, Oct 10, 2009 at 1:27 AM, Michal Suchanek <address@hidden> wrote:
>>>> 2009/10/9 Bean <address@hidden>:
>>>>> On Sat, Oct 10, 2009 at 12:29 AM, Michal Suchanek <address@hidden> wrote:
>>>>>> 2009/10/9 Bean <address@hidden>:
>>>>>>> On Fri, Oct 9, 2009 at 11:57 PM, Michal Suchanek <address@hidden> wrote:
>>>>>>>> I am suggesting an interface that allows style commands like
>>>>>>>>
>>>>>>>> style {
>>>>>>>>
>>>>>>>> (class==button).(text==OK) { <style> }
>>>>>>>>
>>>>>>>> (class==dialog).<nothing here>.(class=button) { <style> }
>>>>>>>>
>>>>>>>> (class==buttonbar) { direction = right_to_left }
>>>>>>>>
>>>>>>>> (class==button) {
>>>>>>>> áborder_top = button_top
>>>>>>>> áborder_left = button_left
>>>>>>>> á...
>>>>>>>> }
>>>>>>>>
>>>>>>>> }
>>>>>>>>
>>>>>>>> for
>>>>>>>>
>>>>>>>> panel { class = dialog ; direction = top_to_bottom
>>>>>>>> ápanel {
>>>>>>>> á scroll = auto
>>>>>>>> á átext { Blah blah blah... }
>>>>>>>> á}
>>>>>>>> ápanel { class = buttonbar ;
>>>>>>>> ápanel { class = button ; img { check.png } ;text { OK } ; command =
>>>>>>>> <something> }
>>>>>>>> ápanel { class = button ; img { cross.png } ;text { Cancel } ;
>>>>>>>> command = <something>}
>>>>>>>> á}
>>>>>>>> }
>>>>>>>>
>>>>>>>> The exact syntax and semantic of the selectors it to be defined.
>>>>>>>>
>>>>>>>> They may be imperative commands that are applied immediately to all
>>>>>>>> widgets currently in existence or they may be stored in a style
>>>>>>>> database that widgets consult each time they are drawn or some
>>>>>>>> combination of the above (for example the style commands affect a
>>>>>>>> style database in order of appearance but do not affect widgets
>>>>>>>> directly).
>>>>>>>>
>>>>>>>> I guess the styles that appear in the main config (grub.cfg or loaded
>>>>>>>> by loadcfg) should be added together so that scripts that generate
>>>>>>>> different parts of the file can add style bits for their widgets.
>>>>>>>>
>>>>>>>> loadstyle command should reset all widget style properties to default
>>>>>>>> (either widget default or the state after loading config) and then
>>>>>>>> interpret the content of the file as if it was enclosed in style {}.
>>>>>>>>
>>>>>>>> When loadstyle is repeated the widgets should be reset again so that
>>>>>>>> previous style cannot affect the newly loaded style.
>>>>>>>
>>>>>>> Hi,
>>>>>>>
>>>>>>> Perhaps it can be written like this:
>>>>>>>
>>>>>>> class {
>>>>>>
>>>>>> perhaps this should be
>>>>>>
>>>>>> style {
>>>>>>
>>>>>>> ábutton.text_OK { <style>}
>>>>>>
>>>>>> text_OK is quite ugly for a selector that specifies that the property
>>>>>> text should be equal OK.
>>>>>>
>>>>>> What if the text contains a space or underscore or the property
>>>>>> contains underscore?
>>>>>>
>>>>>>> ádialog.*button { <style> }
>>>>>>
>>>>>> perhaps this should be
>>>>>>
>>>>>> dialog.*.button
>>>>>>
>>>>>> meaning there is one element in between or
>>>>>>
>>>>>> dialog.**.button
>>>>>>
>>>>>> meaning there can be zero or more elements in between {which is sorely
>>>>>> missing from CSS}
>>>>>
>>>>> Hi,
>>>>>
>>>>> Do we need to distinguish the situation that exactly one element is in
>>>>> between ?
>>>>>
>>>>>>
>>>>>>> ábuttonbar { direction = right_to_left }
>>>>>>> ábutton { áborder_top = button_top áborder_left = button_left }
>>>>>>> }
>>>>>>>
>>>>>>> panel { class = dialog ; direction = top_to_bottom
>>>>>>> ápanel {
>>>>>>> á scroll = auto
>>>>>>> á átext { Blah blah blah... }
>>>>>>> á}
>>>>>>> ápanel { class = buttonbar ;
>>>>>>> ápanel { class = button ; img { check.png } ;text { class=text_OK } ;
>>>>>>> command =<something> }
>>>>>>> ápanel { class = button ; img { cross.png } ;text { class=text_Cancel
>>>>>>> } ; command = <something>}
>>>>>>> á}
>>>>>>> á}
>>>>>>>
>>>>>>
>>>>>> This resolves the syntax issue somewhat but there is still problem
>>>>>> with the order in which the rules are applied.
>>>>>
>>>>> We can first try parent*class format, from near to far, then class
>>>>> itself. If class is not set, use the widget name, for example, in the
>>>>> following config:
>>>>>
>>>>> panel
>>>>> {
>>>>> áclass = aa
>>>>> ápanel {
>>>>> áclass = bb
>>>>> ápanel {
>>>>> á átext { class=cc id=text1 }
>>>>> á}
>>>>> átext { id=text2}
>>>>> }
>>>>>
>>>>> We scan the following section in order:
>>>>>
>>>>> text1:
>>>>> bb.cc
>>>>> bb.**.cc:
>>>>> aa.*.cc
>>>>> aa.**.cc
>>>>> cc
>>>>
>>>> I would guess it should be
>>>>
>>>> * /* everything */
>>>> text /* = **.text */
>>>> cc /* class specified */
>>>> aa.**.text
>>>> aa.**.cc
>>>> bb.**.text /* nearer start*/
>>>> bb.**.cc
>>>> aa.*.text /* less starry */
>>>> aa.*,cc
>>>> bb.text
>>>> bb.cc
>>>> aa.bb..text /* more elements specified in the path */
>>>> aa.bb.cc
>>>>
>>>> meaning that a property can be set by any of the above but the later
>>>> styles (lower in the list) can override properties set earlier.
>>>>
>>>> This is similar to what X props or CSS would do - the resulting
>>>> properties do not depend on the order of styling in text but on how
>>>> "specific" the selector is.
>>>>
>>>> This requires a style store separate from the widgets but likely
>>>> results in somewhat tidier and better predicatble behaviour than
>>>> setting the properties directly on the widgets. The separate style
>>>> store also means that you can save the state before loading a style
>>>> and reset it later quite easily.
>>>>
>>>> Overriding styles in this system is problematic, that's why the
>>>> properties should be reset when a different style is loaded.
>>>>
>>>> The properties set directly on the widgets should be probably
>>>> consulted first, before the * style, and it should be possible to
>>>> restore them when the style is changed.
>>>
>>> Hi,
>>>
>>> Currently, the menu system don't store class property in widget, this
>>> make it easier to change theme, we just reload the class tree and
>>> reinit the screen. It checks local properties first, if not found,
>>> check the class tree for default value, so the scan order should be
>>> reversed, it quits as soon as one match is found.
>>>
>>> For starters, we can just implement two level pattern parent*class, as
>>> each property would need to scan the tree for matches, deep level
>>> would mean a lot of iteration and could have a impact on performance.
>>
>> I don't think this is too much of a problem. How deep a typical layout
>> will be? Ten levels? Do ten pointer dereferences slow down grub? There
>> are components in the current system that are much slower (ie fonts
>> and drawing) so we should not really care.
>>
>> Even if this becomes problem in the future given a reasonable
>> interface to the style resolver the matching can be optimized later.
>>
>> It seems that the paths are in fact very simple:
>>
>> - there is a list of zero or more classes. A class may be either one
>> that is explicitly specified on a panel or "" (anonymous panel)
>> - at the end there is an element type (panel, edit, text, image, ...)
>> and an optional class
>>
>> So the interface to the style resolver should accept this list of
>> classes in some form and information on the final element and the
>> resolver should return back the properties it knows about for this
>> path (return a structure holding these or set them directly on the
>> final element if passed to it or something).
>
> Hi,
>
> To support full path, except the last element, each element can appear
> or not independent of others, for N level, this is 2^(N-1), for
> example, four class aa, bb, cc, dd, we need to scan:
>
> aa.bb.cc.dd
> aa.bb.dd
> aa.cc.dd
> aa.dd
> bb.cc.dd
> bb.dd
> cc.dd
> dd
>
> This is for every property. For every widget, the layout manger would
> read at least margin_*, padding_*, attach_*, width, height, that's 16
> property, widget like panel would need to read border_*, left/top/.. ,
> an extra of 15 property, the number can go out of control rapidly.
> Anyway, how often do we need patterns like aa.bb.cc.dd ?
I would put it differently: is single level selector enough? Are you
sure you will never want to select an element on which somebody forgot
to put a class?
For me the answer is no. So I am not sure how many levels I need. I am
sure one is insufficient and I doubt a fixed number like two levels
would do.
Note however that you need not check combinations that do not exist in
the style.
The "specificity" rule gives ordering independent of the actual
element matched against the style (with the exception of a.**.b which
may designate a shorter or longer path depending on the actual
element).
Let's ignore the ** issue for now and sort the list of style rules by
specificity.
Let's assume that the style resolver gets a pointer to the element
matched and this element contains the class name, element type, and
parent pointer in its common data.
As a first step you can count the number of parent elements and skip
any style rules that specify too many elements.
Now you create a zeroed structure that contains *all* style properties
that an element can have, and a bitmap which says which are set.
You walk the list of rules (which can have similar structures
attached) and add the properties from the rules. When the structure is
full or there are no more rules you are done.
You can save this structure in the element until style changes.
It would be nice to have this structure extensible but since there are
no bindings for a scripting language there is no point. Any extension
requires recompiling grub anyway. The structure should perhaps include
version (or size or something) if multiple modules use it so that a
module that expects more fields does not write outside of the
structure.
When there are bindings to a scripting language this structure should
be replaced by a hash implemented in the runtime of that language (any
structure of key-value pairs where values are easily retrieved by key
and it is also easily determined if a value for a particular key
exists).
The ** issue can be ignored (= the behavior is undefined in that case)
or a special check can be included for this case.
Thanks
Michal