Expand/Shrink

gList

Definition: include xpGUI.e

gdx id = gList(rtn get_data, [selected=NULL,] string attributes="", dword_seq args={})
Description: Creates a simple interface element that displays a non-sortable, header-less list of text-only items, with automatic scrollbars.
NB: scrollbars now almost done, but selection not even started [DEV]

get_data: (non-optional) a fetch function, see notes and DATA below.
selected: (optional) a procedure which is invoked when the selection changes.
For more information on the attributes and args parameters see gSetAttributes().
This is a paranormalised function. (see technicalia)

Returns: the identifier of the created element.
pwa/p2js: Almost, no scrollbars as yet, for one thing. [DEV]
See Also: gCanvas, gTable
Example:
-- demo\xpGUI\gList.exw
include xpGUI.e

sequence data = {"one","too","three"}
integer ldx = largest(apply(data,length),true)

function get_data(integer idx)
    if idx=0 then return length(data) end if
    if idx=-1 then return ldx end if -- note 1
    sequence res = data[idx]
    if res="too" then -- spelling mistake?
        -- see note 2 below
        res = {{"too",XPG_RED,XPG_ITALIC}}
    end if
    return res
end function

procedure selected(integer idx)
    ?{"selected",idx,data[idx]}
end procedure

gdx list = gList(get_data,selected,`FONT="Consolas, 12"`),
     dlg = gDialog(list,"gList",`SIZE=240x80`)
gSetAttribute(list,"BGCLR",XPG_LIGHT_PARCHMENT)
gShow(dlg)
gMainLoop()
gList
Notes: A gList is technically a read-only control, however there is nothing to prevent an application from handling keyboard/mouse input and modifying the data itself, and then presumably invoking gRedraw().

First-class performance is a significant concern in the design of this (deliberately simple) interface element.
1 Specifically when gList() asks data(-1), aka "what is the longest line?", it is entirely the application’s responsibility to figure out a clever way to answer that efficiently, and not the fault of gList() should an inefficient method be used, for instance apply(data,length) is fine when done once but not ideal should it repeat >99.99% of the exact same work on every single keystroke. For that reason, 0 is allowed to say "don’t care", with the obvious catch of no horizontal scrollbar to view the rest of any over-long lines.

Text is always left aligned, with everything in the same font face/pointsize, but optionally scrollable and each line can be specified as a list of coloured and/or normal/bold/italic text fragments. Line numbers (for instance) would be the responsibility of the get_data() function.

Should this control not meet your needs then perhaps you might consider a gTable() instead, which can handle multiple sortable columns, with headers, numeric entries, icons[??], etc. Also, a gList is just one half of the original IupList functionality which has been completely redesigned and split, so you may actually be looking for a gDropDown() instead. Should you be looking for a "dropped down with edit area" then what you probably want is actually a gVbox() with a pair of {gText,gList}) children instead, and performing the whole "add entry" thing explicitly yourself, along with setting the gText whenever selection changes in the gList. (Not really a fan of that kind of silly nonsense, be I, but at least whenever xpGUI starts to show signs of getting a bit highly opinionated, it should be relatively straightforward to change.)

Each element of data/returns from the DATA fetch function can be a plain string or a sequence of string or {string,colour[,style]} fragments. Of course you could pre-analyse (/syntax-colour) the whole of data in one go up-front, or perform that per-line/on demand as shown above, and the maxlen index might also change over time.
2 Note the double braces on the "too" above: a {"too",XPG_RED} would display "too" (in the current foreground colour), but then complain that XPG_RED is neither a string nor {string,colour[,style]}, where colour and style are as per gCanvasText() (including the -1 for "as is"). Obviously "two" would also be valid [albeit misleading/autocorrect], as would {"t",{"o",XPG_RED},"o"}.
Attributes:
?? MULTIPLE (creation only) Allows selecting several items simultaneously (multiple list). Default: "NO"/0/false.
To select multiple items, the Ctrl or Shift key must be held down.
Note this changes the required signature of any SELECTED handler.
Also note this is whole lines, inappropriate for and distinct from block selection in (say) an editor, which is much more of a syntax-colouring thing (see DATA). The latter can however co-exist quite happily with single line selection.
? ROWS (creation only) The number of lines to be displayed at any given time, in cases where no explict SIZE has been set. Of course using ROWS rather than SIZE helps to prevent it showing partial/vertically clipped lines, however that cannot be assured when it is resized or micro-scrolled. If (both are) undefined the length of data is/may be consulted and clamped to 1..50, that is unless constrained by some outer container size or suchlike, say perhaps being normalised to some other sibling control.
Typically much smaller than whatever get_data(0) returns, but can be a bit larger.
?? SELECTED (read only) When MULTIPLE is false, returns the index of the currently selected line, or 0 if none.
When MULTIPLE is true, returns a sequence of such, or {} if none.
This can be used as an independant (and on-demand) alternative to a SELECTED handler.
? SHOWITEM (write-only) Ensure the specified line (1..length(data)) is visible, in other words set the vertical scrollbar as needed.
Can also be {line,fromcol[,tocol[,toline]]} to set the horizontal scrollbar as well, to show as much of that as possible.
? TOPITEM Position the specified line (1..length(DATA)) at the top of the list.
also ?? CARET, CARETPOS,

ACTIVE, CANFOCUS, EXPAND (see note), FONT, MINSIZE, MAXSIZE, SIZE, TIP, VISIBLE.

Note that SCROLLSIZE and VIEWPORT are automatically handled on a gList, and you should not attempt to tamper with them.
Handlers:
DATA A fetch function for retrieving the gList entries/lines/rows.

function fetch_data([gdx id,] integer item)
id: (optional) identifier of the element that activated the event.
item: Index of the desired line, 0 to get the total length, -1 to get the index of the longest.

Naturally, specify a gdx id should you have some use for it, or just omit it otherwise.

When item is 0, as shown above, should simply return the outer/vertical length().
When item is -1, returning 0, or in fact the index of any line that fully fits, simply disables the horizontal scrollbar.
Aside: idx:=fetch_data(-1) is immediately followed by line:=fetch_data(idx) for all idx>0. Technically speaking, for perfect horizontal scrolling, fetch_data(-1) should effectively invoke gGetTextExtent() on every element of data, but in practice length() is usually good enough, and in fact when a fixed-width font is in use that perfectly identifies the correct line anyway.

Obviously your application may well perform much better by cacheing the results from such [getsize] calls, and you could also of course blatently lie, perform a full scan in the background/idle handler, then gRefresh() and tell the honest truth, iyswim. Obviously, you are perfectly at liberty to break up each data[i] on every fetch, rather than store it in pieces. Should some data[i] contain multiple fragments, it would be down to you to either sum the gGetTextExtent/length or apply said to a contatenation of them (gList does the former on the line:=fetch_data(idx) mentioned). Edita keeps tables of linelengths to minimise the effort required in that regard when things change, and therefore so will the new xpEditer, which will actually use a gList().

The fetch_data function can simulate a cuebanner by returning 1/1/"Enter xxx" when the internal application data is {}, of course with appropriate colour/style on the fake fragment if desired. [DEV: untested]

The function should return the integer length|idx, a text string, or a sequence of {text|{text,colour[,style]}} fragments.
Only those lines which are actually being displayed are ever requested, plus, optionally, the longest.
?SELECTED Action generated when an item in the list is selected. Note there is no "unselect" equivalent (as yet).

procedure selected([gdx id,] integer item) -- when MULTIPLE=NO
-- or --
procedure selected([gdx id,] sequence items) -- when MULTIPLE=YES

id: (optional) identifier of the element that activated the event.
item: Number of the changed item starting at 1.
items: Sequence of selected items, or {} if none.

Naturally, specify a gdx id should you have some use for it, and just omit it otherwise.

The MULTIPLE attribute determines whether the second parameter is an integer or a sequence, said can also be declared as object and tested within.
also CLICK, IDROP, KEY, MOUSEMOVE: All common handlers are supported.
Expand/Shrink