Printed Amstrad Addict magazine announced, check it out here!

Main Menu

gedeihen Generic Declarative GUI Engine.

Started by madram, 07:48, 02 July 16

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.


As suggested by Roudoudou, although AYAY Kaeppttn would be the primary beneficent, a generic GUI engine should be an independent project.

So, here it is! I've updated the draft, with a complete use example, which would only require NumericWidget to be coded: UiSpecifications - ORGAMS

Four ways to contribute:
* propose a better architecture.
* complete specification, from the eye of the GUI designer who would like to use the widgets.
* code one particular widget.

Each of these could take from several minutes to a couple of hours.

NB: I've taken a look at Symbios's way to define GUI, but I haven't found the way to bind user inputs with specific actions (i.e. the 'controller' part of MVC).

Anyway, it doesn't cover same requirements. For maximal efficiency, this new GUI should be text based. By that I mean: no proportional font or arbitrary location for editable fields, but it might support sprites.


As we may change set_cursor,write_char,write_text during gui_init, i think we need to set a font bitmap (char width, char height, 1bit pixels for the 96 first characters, excluding 0-31 ascii char?).

I propose to not use the system font cause there will be ASCII differences between differents officials system ROM

In addition to that, with a another font, disregarding the screen offset and dimensions, we are able to change the text resolution (because people want more lines, more characters with the same screen resolution)

Another problem may be the need of graphical characters -> how many, which?

My pronouns are RASM and ACE


You're right ! I was mentioning firmware's own BB5D to make it concrete and give required interface.

Yep for custom resolution, that's the power of genericity! Actually it could be a good idea to be able to override set_cursor and write_char on a widget basis (for sub-window with custom resolution).
In that case we must think set_cursor's interface again. For instance it should be passed the offset, since it's engine's responsibility to know where the window would begin.

As said in AYAY's thread, Orgams already provide custom font and fast display routines. Nonetheless we may use firmware for WIP version.
Actually, for code and testing purpose, I would recommend to plug mock routines, allowing automatized unit tests.

Oh by the way, for Orgams I intend to use 01-1f for internationalization (merging french and german charmap, mostly) and some special chars (e.g. ellipsis).

Also agree on the need of graphical characters. Semi-graphics interface is what I have in mind, i.e. we should be able to almost reproduce Soundtrakker/Starkos GUI. At least two solutions for displaying these characters :

  • Using chars >= #80
  • Using dedicated widget (i.e. non TextWidget), with settable sprites bank.
I prefer the second idea, since:

  • Separate concerns keep isolated (we may have several fonts, but sharing the same graphical elements).
  • Even for screen refresh, we typically display text and graphical elements (e.g. table separators) separatly.


Quote from: madram on 10:18, 03 July 16
Actually, for code and testing purpose, I would recommend to plug mock routines, allowing automatized unit tests.
Yes !  :)

For handling UI we can have:
- callback functions

Each widget would need it's own data for registering a function to be called when an action is done (e.g. button pressed).

- messages

Each widget sends a message (with it's id) to a function. I think less data is needed for this.

I use wxwidgets and it's xrc. I define the gui using wxformbuilder and this writes out an xml file with the description of the gui. Parts of the gui may be loaded and created at runtime. There are message handlers (so wxwidgets is message based). I define functions that respond to the messages. This is nice because I don't need to write code to describe the ui. I would say this is a nice idea to consider. wxwidgets is similar to windows resources but it is better.

Another really great thing of wxwidgets is the layout and I really think this should be considered. wxwidgets have containers (row, column, grid). I define my gui using this, I don't use pixel positions. The gui can resize *automatically* and it handles any size fonts. I think this should be considered for this gui.

I also use a games development kit which uses callbacks. This also has a "language" for describing ui but it is really c++ templates so it is short-hand for writing C++ code. The callbacks are executed when the user performs an interaction. The callback method is ok because any class can register a callback for the same "button press" but I find it a bit more messy.

So consider a tool that can make the gui and please don't use pixel positions.

My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource


Thanks for your comments!

Quote from: arnoldemu on 10:45, 03 July 16
For handling UI we can have:
- callback functions
- messages
The callback functions would be simple Z80 routines, and the 'messages' the parameters passed to these routines (and the simple fact of calling the routine!).  See complete usage example:
I'm not sure why would we need any scheme more complex than that. But I keep the good idea of passing ID of emittor widget. And maybe some meta-information about the passed value (its type?).

Quote from: arnoldemu on 10:45, 03 July 16
wxwidgets have containers (row, column, grid).
The gui can resize *automatically* and it handles any size fonts. I think this should be considered for this gui.
please don't use pixel positions

Yes sure for containers (cf table example in draft UI) and stacking operators instead of absolution positionning.
Automatic resize and arbitrary fonts would be going some bits too far, not meeting the requirements ( anymore.
On CPC, resolution is known and fixed, so I'm not willing to sacrifice render speed for too much flexiblity which would give questionnable look anyway.
In other words, I think gedeihen requirements gives best of both world :
   * no need to compute positions.
   * ability to tweak the interface pixel-wise via sprites. No cluncky layout.

Quote from: arnoldemu on 10:45, 03 July 16
So consider a tool that can make the gui.
The goal is to make the design so neat that a tool would be both unneeded and easy to code!


Table widget interface proposal.

In the draft, horizontal separator are defined between each column.
I think that's a bad idea! All separators should be defined as optionnal parameters of the TableWidget itself.
Here is what it would look like:

++ TableWidget

All the following parameters allow to define separators.
Names are formed from the type of 4 cells surrounding the separator, in this order: 
    up-left, up-right, down-left, down-right.
The type can be :
* o (outside table)
* h (header cell)
* c (normal cell)
* _ (when no separation between up/down or down/left)
E.g.  hh__ is a vertical separator between 2 header cells.
      c_c_ is an horizontal separator between 2 normal cells.

By default, header and normal cells aren't descriminated. I.e., if you only define cc__, c_c_ and the likes, the separators between headers would be the same.

All these parameters takes a widget as value. If this is NoneWidget, the cells are concatenated.
The separators are localised in figure 1 below.

* oooh (a) Default=oohh
* oohh (=) Default=oocc
* oocc     Default=NoneWidget
* ooho (b) Default=oohh
* oh__ (U) Default=oc__
* oc__     Default=NoneWidget
* ohhc (c) Default=occc
* occc     Default=NoneWidget
* h_c_ (~) Default=c_c_
* h_cc (d) Default=c_cc
* hoco (e) Default=coco
* hc__ (V) Default=cc__
* cc__ (|) Default=Nonewidget
* c_cc     Default=c_c_
* c_c_ (-) Default=NoneWidget
* coco (h) Default=NoneWidget
* ohoo (i) Default=ocoo
* ocoo     Default=NoneWidget
* h_oo (_) Default=c_oo
* hcoo (j) Default=ccoo
* c_oo (.) Default=NoneWidget
* ccoo (k) Default=c_oo
* cooo (l) Default=c_oo

              U     header1        U
U   header3   V         |          |
U   header4   V         |          |
U   header5   V         |          |

     Figure 1. Separators labels.


Introducing InputManager

Just as well writing byte on screen is delegated to external routines, scanning keyboard/mouse would be taken care of by a so called InputManager. gedeihen musn't have to deal with things such as dead-keys or auto-repetition delay.

Now, requiring 'read_key' isn't enough; sometimes we want the char (e.g. 'a' when entering text), and other times the key position (e.g. A/Q in the keyboard to play C note). Anyway we can do better at isolating responsibility: let the InputManager do all key mapping, and convert to an event number.
So, gedeihen would simply have to call a non blocking 'get_event' routine.
This should return either the absence of event, or an event id along with a parameter (since we don't want a distinct event by char for e.g. text input).

On another side, keymaps are associated to widget, since certains event only makes sense when a widget has focus. We must be able to set a particular keymap in widget definition. gedeihen would call set_keymap when entering the widget, and reset_keymap when exiting.

To summarize, InputManager would come with 3 routines :

Enrich/override current keymap.

in: hl= map table.
out: N/A

Remove last added keymap.

in: N/A
out: N/A

Query event.

in: N/A
out: Carry if event occured. Then,
        A= event ID
        C= event parameter.
     NC otherwise.


Refining DisplayManager

Instead of introducing "graphical characters", I think it is more flexible to allow arbitrary sprite, via a SpriteWidget. I find it more convenient to directly provide the address of sprite, instead of the address of a sprite bank and then the index in the bank.

So, in addition to write_char, DisplayManager should expose draw_sprite, with the following interface:

  hl = sprite address
  bc = size to draw (in grid-unit)
nb: size might be greater than actual sprite's size (auto-repetition) or less (croping).

Also, the following routines might come handy:

Fill a window from current cursor position

  bc = size of window to fill  (in grid-unit)
  a  = paper

Scroll window from current cursor postion, by 'a' grid-units.
  bc = size of window to scroll (in grid-unit)
  a  = scroll amplitude         (in grid-unit)

To summarize, the DisplayManager would come with the following routines:

get_offset (needed for sub-windows with different DisplayManager)


Yet another way to contribute!

  • Make a working version of the pattern example (UI case study, or a subset of it (e.g. notes and instrument) in the declarative GUI framework of your liking. No code should we written, except to query/update a dummy model.
Thus we can gather best designs and check whether we're missing something.


Can I ask what this is for? Dont we have Symbos and FutureOs doing this already


Quote from: Trebmint on 18:29, 05 July 16
Can I ask what this is for? Dont we have Symbos and FutureOs doing this already

SymbOS's GUI is very comprehensive, and btw. it's source is available. Why start from scratch? Why to invent the wheel again? Just my thoughts.  :)
TFM of FutureSoft
Also visit the CPC and Plus users favorite OS: FutureOS - The Revolution on CPC6128 and 6128Plus


Quote from: Trebmint on 18:29, 05 July 16
Can I ask what this is for? Dont we have Symbos and FutureOs doing this already

SymbOS and FutureOS are... OS !
I think madram want to do something doing only UI and not in a WIMP paradigm...


Hmm..... good point, so he want's it more light-weight. Biggest problem is always what's really needed and what to implement (or better what not to save space if needed).  :)
TFM of FutureSoft
Also visit the CPC and Plus users favorite OS: FutureOS - The Revolution on CPC6128 and 6128Plus


Quote from: CloudStrife on 18:39, 05 July 16
SymbOS and FutureOS are... OS !
I think madram want to do something doing only UI and not in a WIMP paradigm...
Okay, Im all for anything development wise on the CPC, just as long as we're not reinventing the wheel


Hi MadRam, that's another interesting project!
I have a few questions:
- at the end will it be pixel or fixed textchar (like 8x8) based? Or will it support two different modi? So if it's a library, you can choose what you want for your single tasking app? (in a multitasking GUI it's probably impossible to mix it, when you want to display different apps/forms)
- what is a "sprite"? is it a bitmap control/widget ("picture box" etc.) or can it move like a sprite in games?
- is it desgined to support (overlapping) forms ("windows")? as you were already talking about controls which have focus and send messages, it sounds like a form-based environment or is it just based on controls/widgets within a non-window area like a classic sound tracker?
- will it be combined with your dynamic memory management project you talked about some time ago?



Non technical question: Why does a French person give their software a German name? And a strange one at that. (gedeihen is the German word verb "to thrive",  "fleurir" in French).



Hello Trebmint!

SymbOS's crossplatform GUI is quite impressive, but I don't think it makes it possible to reproduce a soundtrakker-like interface with the following requirements:

   * No code needed to encode UI logic (except some thin wrappers to query/update the model). Having to write code means reinventing the wheel for *each* tool.
   * maximum speed possible, for reactive pattern scrolling.
   * split-screen allowed.
   * full-screen allowed.

Also, I might be wrong, but it doesn't seems easy to code for SymbOS natively on CPC.

Last thing: by delegating input handling and raw display, gedeihen is actually cross-OS by design!

Hi Prodatron! Glad you came by!

   * It will be grid-based. Grid size (typically 1 byte * 8 lines) can be customized for each application, or even for a particular window. So we get best of both worlds, text-like for full speed, higher resolution if needed. See "grid based layout"  for more precisions.
      * Multitasking access isn't in requirements, but I see no incompatibility. 
   * What I called sprite is an arbritrary bitmap to fill a round number of cells. No move planned.
   * I'm not fully aware on forms/windows distinctions, maybe we should set a nomenclature everybody could agree on.
      * It's primarly thought for static tiling windows rather than resizeable floating ones.
      * But overlapping windows could be added in a second version via a dedicated operator. The design is open for new widgets and operators, so we don't have to code them all at once.
   * Yeah ideally it should use a memory manager. It's not strictly needed, but then client and engine will have to figure out how to reconnect their respective work bank/page.

Bonjour Bryce !

I think it's a beautiful word, and can mean GEneric DEclarative Interface Horse ENgine.
Plus, too much English already!


Hey Madram

That sounds like a cool aim, and I would admit thats not what Symbos is at present. Its an interesting time with new frameworks such as yours' and CPCTelera.

CPCTelera is particuarly impressive as it was unheralded and super useful for games coders. All development is good.


Quote from: madram on 11:06, 06 July 16What I called sprite is an arbritrary bitmap to fill a round number of cells. No move planned.
More like a tile/pattern so?
"You make one mistake in your life and the internet will never let you live it down" (Keith Goodyer)


Quote from: TotO on 13:20, 06 July 16
More like a tile/pattern so?

You're right, tile is surely less misleading than sprite.
Note: each would come with its own arbitrary size (in grid-unit), for possibly unique usage (e.g. title/logo).


Hi Madram,
Quote from: madram on 07:48, 02 July 16I've taken a look at Symbios's way to define GUI, but I haven't found the way to bind user inputs with specific actions (i.e. the 'controller' part of MVC).
Quote from: madram on 11:06, 06 July 16No code needed to encode UI logic (except some thin wrappers to query/update the model).
The SymbOS GUI is sending messages to the owner of a form each time something happend (control with ID xyz of form abc has been clicked, control has been modified, control got/lost focus, form has been moved etc. etc.). The app then decides if and how to react on these events. As an example, if you have a settings dialogue the app wouldn't take care about anything the users does in the form until he clicks "Ok" or "Cancel". Then you may have to implement some "controller" code, if the modified data have to be transformed into another format or whatever (what you described as the "thin wrapper"), but the "model" may also directly be linked to the "viewer".

I am not sure how you want to integrate the "controller" part in Gedeihen? I couldn't find something in the draft, or maybe I missed it.



View / Controller

Yes there is not yet emphasis on this matter, it's a draft draft!
Each updatable widget (e.g. numeric or text field) must set:
* access (2 words): addresses of getter and setter routines.

The getter accounts for the 'view' part, and the setter for the 'controller' one.
It's necessary since we must query/update the model at some point.
I think it's also sufficient, together with some optional routines associated to widgets:

* on_enter (1 word)
* on_exit   (1 word)
* on_enter_row  (1 word) ; for TableWidget
* on_enter_col   (1 word) ; for TableWidget
Each of these options must be followed by the address of the perticular routine to call.

gedeihen wouldn't do any data housekeeping per se. But a particular widget can. For instance TextFieldWidget will only call the setter once the text is fully entered and validated.

To reproduce your form+validation example, the setters could update a temporary data structure, and validation would copy it to a persistent one.
Reusing data-structures would amount to reuse getters/setters.


Widget reuse via additional parameter

We might customize a widget, e.g. adding keyboard shortcuts to dec/inc a numeric field. Or create a composite widget alltogether, e.g. a field surrounded by button to act on it.
Such enriched widgets can be reused in the UI. The problem is that associated setter and getter will point to the same data.
To circonvent this issue, we allow to define a parameter, which will be passed by setter/getter.

The setter/getter interface would resemble:

; In:
;    HL= external parameter (0 if not set)
;    C=  emmitor widget ID
; In for setter, out for getter:
;    A or DE (depending on widget type): value

Concrete example, self explanatory I hope:

; UI Definition, vertical list of 3 numeric fields ---------------------

mainUI        WORD VerticalStack
              WORD numField1
              WORD numField2
              WORD numField3
              WORD endOfStack

numField1     WORD CustomizeWidget, myNumeric
              BYTE setExtParam:WORD data1
              BYTE endOfParam

numField2     WORD CustomizeWidget, myNumeric
              BYTE setExtParam:WORD data2
              BYTE endOfParam

numField3     WORD CustomizeWidget, myNumeric
              BYTE setExtParam:WORD data3
              BYTE endOfParam

; Warning: extParam must be set to use this Widget
              WORD NumericWidget
              BYTE minRange, 1
              BYTE maxRange, 9
              BYTE access: WORD genericGetter, genericSetter
              BYTE keyMap: WORD numericKeyMap
              BYTE endOfParameter

numericKeyMap BYTE keyPlus:  WORD genericInc
              BYTE keyMinus: WORD genericDec
              BYTE endOfKeyMap

; Routine for model view update

genericGetter   ld a,(hl):RET
genericSetter   ld (hl),a:RET
genericInc      inc (hl):SCF:RET   ; carry to trigger refresh
genericDec      dec (hl):SCF:RET 

data1    BYTE 1
data2    BYTE 1
data3    BYTE 1

There is a flaw in this picture. NumericWidget in accountable for entering figure within the defined range. But genericInc/Dec don't make any check. Adding them manually would be ugly. A better solution is to let NumericWidget do Increment/Decrement.
The question now is how to activate these 'widget actions' beasts.
Maybe afterall we need messages for inter-widgets communication.


[Keyboard Management]

In AyAy context, there are two ways a key is bound to note playing.
In pattern editor, pressing R will lead to enter one "F" note and process to next line, whereas elsewhere we want to sustain the note as long the key is pressed.

For the latter case, we may test if the key is still pressed (e.g. &BB1E KM Test Key), but it would defeat the nice decoupling of key handling vs event management. Put in another way, if an action is triggered by an event, either it's a one shot, or it should be stopped by another event. Viva symmetry.

That's why I think a model based on "key_pressed & key_released" events would be a better match for Gedeihen.

Con: have to rewrite the whole keyboard manager (e.g. the auto-repetition part)
Pro: allow finer handling (dead-keys, multiple keys, ...).

@BSC: no news since your PM the 31st of July. Should I Worry?


Quote from: madram on 15:52, 19 August 16@BSC: no news since your PM the 31st of July. Should I Worry?
Here I am! What's happenin'? 
** My SID player/tracker AYAY Kaeppttn! on github **  Some CPC music and experiments ** Other music ** More music on scenestream (former nectarine) ** Some shaders ** Some Soundtrakker tunes ** Some tunes in Javascript

My hardware: ** Schneider CPC 464 with colour screen, 64k extension, 3" and 5,25 drives and more ** Amstrad CPC 6128 with M4 board, GreaseWeazle.

Powered by SMFPacks Menu Editor Mod